linux-acpi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Early initrd file overwrite and ACPI table override making use of it
@ 2012-07-18 10:36 Thomas Renninger
  2012-07-18 10:36 ` [PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing Thomas Renninger
  2012-07-18 10:36 ` [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
  0 siblings, 2 replies; 19+ messages in thread
From: Thomas Renninger @ 2012-07-18 10:36 UTC (permalink / raw)
  To: linux-kernel, hpa, lenb; +Cc: linux-acpi, initramfs, bigeasy

Hi Peter,
another attempt, this time making use of cpio encapsulation
of early files as discussed.
Would be great if this can get committed to the branch you created
and get queued for 3.6-rc1 mainline inclusion.

It is based on Linus' master branch on top of:
commit 8c84bf4166a4698296342841a549bbee03860ac0
Merge: bd0a521 5db9a4d
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Sun Jul 8 09:09:27 2012 -0700

    Merge branch 'for-3.5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup

Thanks,

   Thomas


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

* [PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing
  2012-07-18 10:36 Early initrd file overwrite and ACPI table override making use of it Thomas Renninger
@ 2012-07-18 10:36 ` Thomas Renninger
       [not found]   ` <1342607764-66747-2-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
  2012-07-18 10:36 ` [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
  1 sibling, 1 reply; 19+ messages in thread
From: Thomas Renninger @ 2012-07-18 10:36 UTC (permalink / raw)
  To: linux-kernel, hpa, lenb; +Cc: linux-acpi, initramfs, bigeasy, Thomas Renninger

cpio parsing code comes from  H. Peter Anvin.
The CONFIG_EARLY_INITRD feature is architecture independent, but
for now only enabled/called for X86.
The problem is that initrd_start must be valid, but there is no
architecture independent reserve_initrd() call in init/main.c or
similiar.

Signed-off-by: Thomas Renninger <trenn@suse.de>
CC: hpa@zytor.com
---
 Documentation/initrd.txt |   22 ++++++++
 arch/x86/kernel/setup.c  |    2 +
 include/linux/initrd.h   |   12 ++++
 init/Makefile            |    1 +
 init/initrd_early.c      |  131 ++++++++++++++++++++++++++++++++++++++++++++++
 usr/Kconfig              |   12 ++++
 6 files changed, 180 insertions(+), 0 deletions(-)
 create mode 100644 init/initrd_early.c

diff --git a/Documentation/initrd.txt b/Documentation/initrd.txt
index 4e1839c..e515e83 100644
--- a/Documentation/initrd.txt
+++ b/Documentation/initrd.txt
@@ -93,6 +93,28 @@ mkdir /tmp/imagefile
 cd /tmp/imagefile
 gzip -cd /boot/imagefile.img | cpio -imd --quiet
 
+
+Multiple cpio images glued together
+-----------------------------------
+
+Several cpio images, compressed or uncompressed can be concatenated.
+There is especially one use-case for this: see kernel accessing
+initrd data early section below.
+
+
+Accessing initrd data early
+---------------------------
+
+There is a mechanism to access data passed from the initrd much earlier.
+This only works if the data needed early is encapsulated in an uncompressed
+cpio image is passed. It must be the first cpio archive if multiple
+cpio archives are concatenated and passed as initrd.
+Typically if you want to pass data which is supposed to be consumed by
+the kernel really early, one would pass two cpio images glued together:
+   - One compressed, holding the big data which is needed by userspace
+   - One uncompressed cpio image holding files for early kernel initialization
+For further details look out for the CONFIG_EARLY_INITRD option in the sources.
+
 Installation
 ------------
 
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 16be6dc..9e039f6 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -941,6 +941,8 @@ void __init setup_arch(char **cmdline_p)
 
 	reserve_initrd();
 
+	early_initrd_find_cpio_data((void *)initrd_start, initrd_end - initrd_start);
+
 	reserve_crashkernel();
 
 	vsmp_init();
diff --git a/include/linux/initrd.h b/include/linux/initrd.h
index 55289d2..3fe262e 100644
--- a/include/linux/initrd.h
+++ b/include/linux/initrd.h
@@ -18,3 +18,15 @@ extern unsigned long initrd_start, initrd_end;
 extern void free_initrd_mem(unsigned long, unsigned long);
 
 extern unsigned int real_root_dev;
+
+
+#define MAX_EARLY_INITRD_CB 16
+
+#ifdef CONFIG_EARLY_INITRD
+extern int early_initrd_find_cpio_data(const char *data, size_t len);
+#else
+static int early_initrd_find_cpio_data(const char *data, size_t len)
+{
+	return 0;
+}
+#endif
diff --git a/init/Makefile b/init/Makefile
index 7bc47ee..c8408ec 100644
--- a/init/Makefile
+++ b/init/Makefile
@@ -9,6 +9,7 @@ else
 obj-$(CONFIG_BLK_DEV_INITRD)   += initramfs.o
 endif
 obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
+obj-$(CONFIG_EARLY_INITRD)     += initrd_early.o
 
 ifneq ($(CONFIG_ARCH_INIT_TASK),y)
 obj-y                          += init_task.o
diff --git a/init/initrd_early.c b/init/initrd_early.c
new file mode 100644
index 0000000..c657a4b
--- /dev/null
+++ b/init/initrd_early.c
@@ -0,0 +1,131 @@
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/initrd.h>
+
+struct initrd_early_data {
+	/* Path where relevant files can be found in uncompressed cpio */
+ 	char *namesp;
+	/* Callback called for each found file in above path */
+	int (*cb)(void *data, int size, const char *name);
+	/* Finalize callback: Called if file scanning finished and above
+	   callback has been called one or more times successfully */
+	void (*final)(void);
+};
+
+/*
+ * Add here new callback functions and the path relevant files show up in an
+ * uncompressed cpio
+ */
+static __initdata struct initrd_early_data initrd_early_callbacks[] =
+{
+	{
+		.namesp = NULL,
+	}
+};
+
+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
+};
+
+#define ALIGN4(p) ((void *)(((size_t)p + 3) & ~3))
+
+void __init early_initrd_find_cpio_data(const char *data, size_t len)
+{
+	const size_t cpio_header_len = 8*C_NFIELDS - 2;
+	const char *p, *dptr, *nptr;
+	unsigned int ch[C_NFIELDS], *chp, v;
+	unsigned char c, x;
+	int i, j;
+	struct initrd_early_data *ied;
+	int str_len[MAX_EARLY_INITRD_CB];
+	int match[MAX_EARLY_INITRD_CB];
+
+	for(i = 0, ied = initrd_early_callbacks; ied->namesp; ied++, i++) {
+		str_len[i] = strlen(ied->namesp);
+		match[i] = 0;
+	}
+
+	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 = ALIGN4(p + ch[C_NAMESIZE]);
+		nptr = ALIGN4(dptr + ch[C_FILESIZE]);
+
+		if (nptr > p + len || dptr < p || nptr < dptr)
+			goto quit; /* Buffer overrun */
+
+		if ((ch[C_MODE] & 0170000) == 0100000) {
+			for(i = 0, ied = initrd_early_callbacks; ied->namesp;
+			    ied++, i++) {
+				int min_len = (str_len[i] < ch[C_NAMESIZE])
+					? str_len[i] : ch[C_NAMESIZE];
+				if (!memcmp(p, ied->namesp, min_len)) {
+					if (!ied->cb((void *)dptr,
+					     ch[C_FILESIZE], p + min_len))
+						match[i]++;
+				}
+			}
+		}
+
+		len -= (nptr - p);
+		p = nptr;
+	}
+
+quit:
+	for(i = 0, ied = initrd_early_callbacks; ied->namesp; ied++, i++) {
+		if (match[i])
+			ied->final();
+	}
+}
diff --git a/usr/Kconfig b/usr/Kconfig
index 085872b..3b832ed 100644
--- a/usr/Kconfig
+++ b/usr/Kconfig
@@ -166,3 +166,15 @@ config INITRAMFS_COMPRESSION_LZO
 	  (both compression and decompression) is the fastest.
 
 endchoice
+
+config EARLY_INITRD
+	bool "Ability to pass data to the kernel which is needed really early"
+	default y
+	depends on BLK_DEV_INITRD && X86
+	help
+	 CPU microcode updates could be loaded before CPU initialization.
+	 BIOS data can be overridden via initrd for debugging purposes.
+	 If you are unsure whether your Hardware or kernel makes use of this,
+	 it is safe to say yes here. As long as no data is passed through an
+	 uncompressed cpio via initrd the kernel could make use of, nothing
+	 will happen.
-- 
1.7.6.1


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

* [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  2012-07-18 10:36 Early initrd file overwrite and ACPI table override making use of it Thomas Renninger
  2012-07-18 10:36 ` [PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing Thomas Renninger
@ 2012-07-18 10:36 ` Thomas Renninger
  1 sibling, 0 replies; 19+ messages in thread
From: Thomas Renninger @ 2012-07-18 10:36 UTC (permalink / raw)
  To: linux-kernel, hpa, lenb
  Cc: linux-acpi, initramfs, bigeasy, Thomas Renninger, eric.piel,
	vojcek, Lin Ming, robert.moore

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
---
 Documentation/acpi/initrd_table_override.txt |  119 ++++++++++++++++
 drivers/acpi/Kconfig                         |   10 ++
 drivers/acpi/osl.c                           |  193 ++++++++++++++++++++++++--
 include/linux/acpi.h                         |    6 +
 include/linux/initrd.h                       |    4 +-
 init/initramfs.c                             |   23 +++-
 init/initrd_early.c                          |   10 ++
 7 files changed, 349 insertions(+), 16 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..e985dea
--- /dev/null
+++ b/Documentation/acpi/initrd_table_override.txt
@@ -0,0 +1,119 @@
+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:
+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 *.dsl
+# Add the raw ACPI tables to an uncompressed cpio archive.
+# They must be put into /kernel/firmware/acpi directory inside the cpio
+# archive.
+# If you want to override other firmware files early (for example CPU
+# microcode), you must use only one uncompressed cpio archive and it must
+# be the first. Other, typically compressed cpio archives, must be
+# concatenated on top of the uncompressed one.
+# For further info read the "Accessing initrd data early" chapter in
+# Documtenation/initrd.txt.
+mkdir -p /tmp/early_cpio/kernel/firmware/acpi
+cp TBL1.dat /tmp/early_cpio/kernel/firmware/acpi
+cat TBL2.dat /tmp/early_cpio/kernel/firmware/acpi
+cat TBL3.dat /tmp/early_cpio/kernel/firmware/acpi
+cd /tmp/early_cpio
+find . | 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/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 8099895..9d49efb 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -261,6 +261,16 @@ config ACPI_CUSTOM_DSDT
 	bool
 	default ACPI_CUSTOM_DSDT_FILE != ""
 
+config ACPI_INITRD_TABLE_OVERRIDE
+	bool
+	depends on EARLY_INITRD
+	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 c3881b2..059be34 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -45,6 +45,7 @@
 #include <linux/list.h>
 #include <linux/jiffies.h>
 #include <linux/semaphore.h>
+#include <linux/memblock.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -534,6 +535,126 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
 	return AE_OK;
 }
 
+#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+#include <asm/e820.h>
+
+#define ACPI_OVERRIDE_TABLES 10
+
+__initdata static struct{
+	void *data;
+	int size;
+} early_initrd_files[ACPI_OVERRIDE_TABLES];
+static __initdata int table_nr;
+static int all_tables_size;
+static u64 acpi_tables_addr;
+
+/* 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 *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) \
+	{ printk(KERN_ERR "ACPI OVERRIDE: " x " [%s]\n", name); return 1; }
+
+#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
+
+int __init acpi_initrd_table_override(void *data, int size, const char *name)
+{
+	int sig;
+	struct acpi_table_header *table;
+
+	if (table_nr >= ACPI_OVERRIDE_TABLES)
+		INVALID_TABLE("Too much early tables - ignoring", name);
+
+	if (size < sizeof(struct acpi_table_header))
+		INVALID_TABLE("Table smaller than ACPI header", name);
+
+	table = 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", name);
+
+	if (size != table->length)
+		INVALID_TABLE("File length does not match table length", name);
+
+	if (acpi_table_checksum(data, table->length))
+		INVALID_TABLE("Bad table checksum", name);
+
+	printk(KERN_INFO "%4.4s ACPI table found in initrd [%s][%d]\n",
+	       table->signature, name, table->length);
+
+	all_tables_size += table->length;
+	early_initrd_files[table_nr].data =  data;
+	early_initrd_files[table_nr].size =  size;
+	table_nr++;
+	return 0;
+}
+
+void __init acpi_initrd_finalize(void)
+{
+	int i, offset = 0;
+	char *p;
+
+	acpi_tables_addr =
+		memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT,
+				       all_tables_size, PAGE_SIZE);
+	if (!acpi_tables_addr)
+		panic("Cannot find place for ACPI override tables\n");
+
+	/*
+	 * 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 (i = 0; i < table_nr; i++) {
+		memcpy(p + offset, early_initrd_files[i].data,
+		       early_initrd_files[i].size);
+		offset += early_initrd_files[i].size;
+	}
+	early_iounmap(p, all_tables_size);
+}
+#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */
+
+static void acpi_table_taint(struct acpi_table_header *table)
+{
+	printk(KERN_WARNING 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 +668,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 f421dd8..9fb292c 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -76,6 +76,12 @@ 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
+int __init acpi_initrd_table_override(void *data,
+			      int size, const char *name);
+void __init acpi_initrd_finalize(void);
+#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);
diff --git a/include/linux/initrd.h b/include/linux/initrd.h
index 3fe262e..8b26e4d 100644
--- a/include/linux/initrd.h
+++ b/include/linux/initrd.h
@@ -23,9 +23,9 @@ extern unsigned int real_root_dev;
 #define MAX_EARLY_INITRD_CB 16
 
 #ifdef CONFIG_EARLY_INITRD
-extern int early_initrd_find_cpio_data(const char *data, size_t len);
+extern void early_initrd_find_cpio_data(const char *data, size_t len);
 #else
-static int early_initrd_find_cpio_data(const char *data, size_t len)
+static void early_initrd_find_cpio_data(const char *data, size_t len)
 {
 	return 0;
 }
diff --git a/init/initramfs.c b/init/initramfs.c
index 84c6bf1..70a1972 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -411,8 +411,10 @@ static int __init flush_buffer(void *bufv, unsigned len)
 			buf += written;
 			len -= written;
 			state = Reset;
-		} else
+		} else {
+			pr_info("junk in compressed archive 3 %u", len);
 			error("junk in compressed archive");
+		}
 	}
 	return origLen;
 }
@@ -427,6 +429,10 @@ static char * __init unpack_to_rootfs(char *buf, unsigned len)
 	decompress_fn decompress;
 	const char *compress_name;
 	static __initdata char msg_buf[64];
+	int skipped = 0;
+	unsigned long tot_written = 0;
+
+	pr_info("%s: 0x%p len: %u\n", __FUNCTION__, buf, len);
 
 	header_buf = kmalloc(110, GFP_KERNEL);
 	symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
@@ -441,13 +447,17 @@ static char * __init unpack_to_rootfs(char *buf, unsigned len)
 	while (!message && len) {
 		loff_t saved_offset = this_header;
 		if (*buf == '0' && !(this_header & 3)) {
+			pr_info("Starting...\n");
 			state = Start;
 			written = write_buffer(buf, len);
 			buf += written;
 			len -= written;
+			pr_info("... %u written, remaining: %u\n",
+				written, len);
 			continue;
 		}
 		if (!*buf) {
+			skipped++;
 			buf++;
 			len--;
 			this_header++;
@@ -456,21 +466,28 @@ static char * __init unpack_to_rootfs(char *buf, unsigned len)
 		this_header = 0;
 		decompress = decompress_method(buf, len, &compress_name);
 		if (decompress) {
+			pr_info("Decompress: %u - buf[0]: 0x%x - buf[1]: 0x%x", len, *buf, *(buf + 1));
+			pr_info("Decompressing via %s\n", compress_name);
 			res = decompress(buf, len, NULL, flush_buffer, NULL,
 				   &my_inptr, error);
 			if (res)
 				error("decompressor failed");
 		} else if (compress_name) {
+			pr_info("Not decompressing via %s\n", compress_name);
 			if (!message) {
 				snprintf(msg_buf, sizeof msg_buf,
 					 "compression method %s not configured",
 					 compress_name);
 				message = msg_buf;
 			}
-		} else
+		} else {
+			pr_info("junk in compressed archive 1 %u - buf[0]: 0x%x - buf[1]: 0x%x", len, *buf, *(buf + 1));
 			error("junk in compressed archive");
-		if (state != Reset)
+		}
+		if (state != Reset) {
+			pr_info("junk in compressed archive 2 %u", len);
 			error("junk in compressed archive");
+		}
 		this_header = saved_offset + my_inptr;
 		buf += my_inptr;
 		len -= my_inptr;
diff --git a/init/initrd_early.c b/init/initrd_early.c
index c657a4b..35d8480 100644
--- a/init/initrd_early.c
+++ b/init/initrd_early.c
@@ -1,6 +1,9 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/initrd.h>
+#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+#include <linux/acpi.h>
+#endif
 
 struct initrd_early_data {
 	/* Path where relevant files can be found in uncompressed cpio */
@@ -18,6 +21,13 @@ struct initrd_early_data {
  */
 static __initdata struct initrd_early_data initrd_early_callbacks[] =
 {
+#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+	{
+		.namesp = "kernel/firmware/acpi/",
+		.cb     = acpi_initrd_table_override,
+		.final  = acpi_initrd_finalize,
+	},
+#endif
 	{
 		.namesp = NULL,
 	}
-- 
1.7.6.1


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

* Re: [PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing
       [not found]   ` <1342607764-66747-2-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
@ 2012-07-21 15:21     ` H. Peter Anvin
       [not found]       ` <500AC8F6.4010802-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: H. Peter Anvin @ 2012-07-21 15:21 UTC (permalink / raw)
  To: Thomas Renninger
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, lenb-DgEjT+Ai2ygdnm+yROfE0A,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	initramfs-u79uwXL29TY76Z2rM5mHXA, bigeasy-hfZtesqFncYOwBW4kG4KsQ,
	Fenghua Yu

On 07/18/2012 03:36 AM, Thomas Renninger wrote:
> cpio parsing code comes from  H. Peter Anvin.
> The CONFIG_EARLY_INITRD feature is architecture independent, but
> for now only enabled/called for X86.
> The problem is that initrd_start must be valid, but there is no
> architecture independent reserve_initrd() call in init/main.c or
> similiar.
> + * Add here new callback functions and the path relevant files show up in an
> + * uncompressed cpio
> + */
> +static __initdata struct initrd_early_data initrd_early_callbacks[] =
> +{
> +	{
> +		.namesp = NULL,
> +	}
> +};
> +

I don't like your callback interface at all.  In fact, it is actively 
broken, because it assumes that all early users are runnable at the same 
time, which is trivially shown false -- the microcode work that Fenghua 
Yu is working on needs access to its early data much, much earlier than 
your ACPI code.

So big NAK on this change.  Instead we should stick to the imperative 
interface that I had in my original code (call the search function with 
a filename and let it return a pointer if found.)

	-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] 19+ messages in thread

* Re: [PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing
       [not found]       ` <500AC8F6.4010802-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org>
@ 2012-07-23 14:40         ` Thomas Renninger
  2012-07-23 15:09           ` H. Peter Anvin
  0 siblings, 1 reply; 19+ messages in thread
From: Thomas Renninger @ 2012-07-23 14:40 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, lenb-DgEjT+Ai2ygdnm+yROfE0A,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	initramfs-u79uwXL29TY76Z2rM5mHXA, bigeasy-hfZtesqFncYOwBW4kG4KsQ,
	Fenghua Yu

On Saturday, July 21, 2012 05:21:26 PM H. Peter Anvin wrote:
> On 07/18/2012 03:36 AM, Thomas Renninger wrote:
> > cpio parsing code comes from  H. Peter Anvin.
> > The CONFIG_EARLY_INITRD feature is architecture independent, but
> > for now only enabled/called for X86.
> > The problem is that initrd_start must be valid, but there is no
> > architecture independent reserve_initrd() call in init/main.c or
> > similiar.
> > + * Add here new callback functions and the path relevant files show up in an
> > + * uncompressed cpio
> > + */
> > +static __initdata struct initrd_early_data initrd_early_callbacks[] =
> > +{
> > +	{
> > +		.namesp = NULL,
> > +	}
> > +};
> > +
> 
> I don't like your callback interface at all.  In fact, it is actively 
> broken, because it assumes that all early users are runnable at the same 
> time,
That's wrong.
If you have a closer look at the "ACPI table override" solution, it's
splitted up:
The callback collects all initrd provided tables:
    __init acpi_initrd_table_override() and __init acpi_initrd_finalize()
the actual usage of the files can happen any time later on when
the ACPICA subsystems grabs another table.

This is the most flexible solution and I cannot see why others
cannot do the same.

> which is trivially shown false -- the microcode work that Fenghua 
> Yu is working on needs access to its early data much, much earlier than 
> your ACPI code.
This is another problem and I expect I call:
early_initrd_find_cpio_data()
early enough for Fenghua's needs.
If not, how early exactly is this needed?

I hook in shortly after initrd_start gets valid.
This is necessary so that early_initrd_find_cpio_data can make
use of the arch independent initrd_start variable and the whole
feature is kept arch independent:

@@ -941,6 +941,8 @@ void __init setup_arch(char **cmdline_p)
 
        reserve_initrd();
 
+       early_initrd_find_cpio_data((void *)initrd_start, initrd_end - initrd_start);
+

I have heard about a possible use case on ARM for this (pass device tree
via initrd). I will ask...

> So big NAK on this change.  Instead we should stick to the imperative 
> interface that I had in my original code (call the search function with 
> a filename and let it return a pointer if found.)
This does not work with what I like to achive with APCI table overriding:
Pass an arbitrary amount of firmware files.
It could work with pre-defining the files, but that is not nice:
.../acpi0.aml
..
.../acpi9.aml

Another advantage:
If (just an example) CPU microcode files get passed via "early initrd",
the same path could be provided than needed by request_fw().
For Intel CPU microcode that would be:
/lib/firmware/intel-ucode
and files are split up there into family-model-stepping as done
already by microcode_intel.c
This would allow (with my approach):
   1) One can build a CPU family-model-stepping independent,
      generic initrd putting in all available Intel CPU microcodes.
   2) The path in cpio accessed via "uncompressed early initrd"
      can be the same as in the later unpacked rootfs accessed
      by initrd userspace tools.
      No need to add them twice, to the compressed and uncompressed
      cpio if the files should be available as "early initrd" data
      *and* in initramfs.      
      So above CPU microcode files could be used via "early initrd"
      mechanism to flash the boot CPU. And the same file(s) can be used
      later in unpacked intramfs via request_fw() to flash other, later
      brought up CPUs.
   3) ...


-> I'd prefer to go with this more flexible callback approach, instead
of spreading initrd_findcpio(..) calls all over the kernel when this gets
used more often.

   Thomas

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

* Re: [PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing
  2012-07-23 14:40         ` Thomas Renninger
@ 2012-07-23 15:09           ` H. Peter Anvin
  2012-07-24  9:16             ` Thomas Renninger
  0 siblings, 1 reply; 19+ messages in thread
From: H. Peter Anvin @ 2012-07-23 15:09 UTC (permalink / raw)
  To: Thomas Renninger
  Cc: linux-kernel, lenb, linux-acpi, initramfs, bigeasy, Fenghua Yu

On 07/23/2012 07:40 AM, Thomas Renninger wrote:
> This is another problem and I expect I call:
> early_initrd_find_cpio_data()
> early enough for Fenghua's needs.
> If not, how early exactly is this needed?

We're calling that from arch-specific code before even turning paging 
on.  This has a couple of consequences:

1. ALL STATIC POINTERS ARE FORBIDDEN.  Period.  The code must be able to 
be executed from a nonstandard linear address, and any static pointer 
(like a function pointer) breaks that.

2. Any ideas of doing everything at the same time, or uniform 
architecture, is clearly out the window... we're just barely capable of 
using C at this point at all.

Now, you definitely do have a valid point about being able to iterate 
over multiple files with a common prefix.  We could do that with either 
a callback (where the callback is passed in as an argument), but I think 
it might be nicer to do that as an iterator interface... let me ketch on 
this.

> If (just an example) CPU microcode files get passed via "early initrd",
> the same path could be provided than needed by request_fw().

This will all be obsolete.  request_fw is available way, way, way too late.


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


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

* Re: [PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing
  2012-07-23 15:09           ` H. Peter Anvin
@ 2012-07-24  9:16             ` Thomas Renninger
  0 siblings, 0 replies; 19+ messages in thread
From: Thomas Renninger @ 2012-07-24  9:16 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: linux-kernel, lenb, linux-acpi, initramfs, bigeasy, Fenghua Yu

On Monday, July 23, 2012 05:09:16 PM H. Peter Anvin wrote:
> On 07/23/2012 07:40 AM, Thomas Renninger wrote:
> > This is another problem and I expect I call:
> > early_initrd_find_cpio_data()
> > early enough for Fenghua's needs.
> > If not, how early exactly is this needed?
> 
> We're calling that from arch-specific code before even turning paging 
> on.
Why?
If you would shed more light into what you (or Fenghua)
try to achieve that would help.
What kind of platform/CPU is this?
What happens if firmware is not provided (that early,..)?

...

> Now, you definitely do have a valid point about being able to iterate 
> over multiple files with a common prefix.  We could do that with either 
> a callback (where the callback is passed in as an argument), but I think 
> it might be nicer to do that as an iterator interface... let me ketch on 
> this.
Please do.

I finally would like to have the ACPI table via initird overriding.
ACPICA people added the physical table override stuff only for this.
With the cpio encapsulation (using initrd_start) this is a nice,
arch independent approach.
I can imagine the one or other arch picks this up. One example
could be flattened device tree passing via intird for ARM.

Maybe it could be kept arch independent with a "weird archs need things
even earlier" specific interface/hook if really needed.
Then others could pass initrd_start as now done, but X86 or
specific archs do some nasty HW specific stuff.

...
 
Hm, I could imagine this early fiddling will take some time.
If the cpio encapsulation as shown is acceptable, these patches
could get pushed already and the "very very early" additions can
be pushed on top in one of the next kernel rounds.

Thanks,

   Thomas

^ permalink raw reply	[flat|nested] 19+ 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 ` Thomas Renninger
       [not found]   ` <1346318957-5831-3-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
  0 siblings, 1 reply; 19+ 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] 19+ messages in thread

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
       [not found]   ` <1346318957-5831-3-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
@ 2012-08-30  9:34     ` Thomas Renninger
  0 siblings, 0 replies; 19+ messages in thread
From: Thomas Renninger @ 2012-08-30  9:34 UTC (permalink / raw)
  To: hpa-YMNOUZJC4hwAvxtiuMwx3w, initramfs-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, lenb-DgEjT+Ai2ygdnm+yROfE0A,
	robert.moore-ral2JQCrhuEAvxtiuMwx3w,
	bigeasy-hfZtesqFncYOwBW4kG4KsQ, vojcek-wYtBgQxc//8,
	eric.piel-VkQ1JFuSMpfAbQlEx87xDw,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA, yinghai-DgEjT+Ai2ygdnm+yROfE0A

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-l3A5Bk7waGM@public.gmane.org>
> CC: eric.piel-VkQ1JFuSMpfAbQlEx87xDw@public.gmane.org
> CC: vojcek-wYtBgQxc//8@public.gmane.org
> CC: Lin Ming <ming.m.lin-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Lin Ming's address is not valid anymore?:
<ming.m.lin-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>: 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] 19+ messages in thread

* [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  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
       [not found]   ` <1348234085-39220-3-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
  2012-09-22 15:16   ` Len Brown
  0 siblings, 2 replies; 19+ 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, vojcek, Lin Ming

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                           |  203 ++++++++++++++++++++++++--
 include/linux/acpi.h                         |    4 +
 5 files changed, 331 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..b550831
--- /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 a /kernel/firmware/acpi directory inside the
+# cpio archive.
+# The uncompressed cpio archive 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
+# A maximum of: #define ACPI_OVERRIDE_TABLES 10
+# tables are  currently allowed (see osl.c):
+iasl -sa facp.dsl
+iasl -sa ssdt1.dsl
+cp facp.aml kernel/firmware/acpi
+cp ssdt1.aml kernel/firmware/acpi
+# 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..ba7560f 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -534,6 +534,137 @@ 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, path, name)					\
+	{ pr_err("ACPI OVERRIDE: " x " [%s%s]\n", path, 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, total_offset = 0;
+	long offset = 0;
+	struct acpi_table_header *table;
+	char cpio_path[32] = "kernel/firmware/acpi/";
+	struct cpio_data file;
+	struct cpio_data early_initrd_files[ACPI_OVERRIDE_TABLES];
+	char *p;
+
+	if (data == NULL || size == 0)
+		return;
+
+	for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) {
+		file = find_cpio_data(cpio_path, data, size, &offset);
+		if (!file.data)
+			break;
+
+		data += offset;
+		size -= offset;
+
+		if (file.size < sizeof(struct acpi_table_header))
+			INVALID_TABLE("Table smaller than ACPI header",
+				      cpio_path, file.name);
+
+		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, file.name);
+		if (file.size != table->length)
+			INVALID_TABLE("File length does not match table length",
+				      cpio_path, file.name);
+		if (acpi_table_checksum(file.data, table->length))
+			INVALID_TABLE("Bad table checksum",
+				      cpio_path, file.name);
+
+		pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n",
+			table->signature, cpio_path, file.name, 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 + total_offset, early_initrd_files[no].data,
+		       early_initrd_files[no].size);
+		total_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 +678,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] 19+ messages in thread

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
       [not found]   ` <1348234085-39220-3-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
@ 2012-09-21 20:56     ` Yinghai Lu
       [not found]       ` <CAE9FiQX64iRo9QjhEPYtEOhbEweKSmTssQ50VfPznWtHA345CQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: Yinghai Lu @ 2012-09-21 20:56 UTC (permalink / raw)
  To: Thomas Renninger
  Cc: hpa-YMNOUZJC4hwAvxtiuMwx3w, initramfs-u79uwXL29TY76Z2rM5mHXA,
	robert.moore-ral2JQCrhuEAvxtiuMwx3w, lenb-DgEjT+Ai2ygdnm+yROfE0A,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	eric.piel-VkQ1JFuSMpfAbQlEx87xDw, vojcek-wYtBgQxc//8, Lin Ming

On Fri, Sep 21, 2012 at 6:28 AM, Thomas Renninger <trenn-l3A5Bk7waGM@public.gmane.org> wrote:
> Details can be found in:
> Documentation/acpi/initrd_table_override.txt
>
> Signed-off-by: Thomas Renninger <trenn-l3A5Bk7waGM@public.gmane.org>
> CC: eric.piel-VkQ1JFuSMpfAbQlEx87xDw@public.gmane.org
> CC: vojcek-wYtBgQxc//8@public.gmane.org
> CC: Lin Ming <ming.m.lin-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> CC: lenb-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
> CC: robert.moore-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org
> CC: hpa-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org
> CC: yinghai-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
> ---
>  Documentation/acpi/initrd_table_override.txt |  122 +++++++++++++++
>  arch/x86/kernel/setup.c                      |    4 +
>  drivers/acpi/Kconfig                         |    9 +
>  drivers/acpi/osl.c                           |  203 ++++++++++++++++++++++++--
>  include/linux/acpi.h                         |    4 +
>  5 files changed, 331 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..b550831
> --- /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 a /kernel/firmware/acpi directory inside the
> +# cpio archive.
> +# The uncompressed cpio archive 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
> +# A maximum of: #define ACPI_OVERRIDE_TABLES 10
> +# tables are  currently allowed (see osl.c):
> +iasl -sa facp.dsl
> +iasl -sa ssdt1.dsl
> +cp facp.aml kernel/firmware/acpi
> +cp ssdt1.aml kernel/firmware/acpi
> +# 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
> +

could use
#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
void acpi_initrd_override(void *data, size_t size);
#else
static inline void acpi_initrd_override(void *data, size_t size)
{
}
#endif

in one header file to avoid MACRO in setup.c

>         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..ba7560f 100644
> --- a/drivers/acpi/osl.c
> +++ b/drivers/acpi/osl.c
> @@ -534,6 +534,137 @@ 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, path, name)                                   \
> +       { pr_err("ACPI OVERRIDE: " x " [%s%s]\n", path, 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, total_offset = 0;
> +       long offset = 0;
> +       struct acpi_table_header *table;
> +       char cpio_path[32] = "kernel/firmware/acpi/";
> +       struct cpio_data file;
> +       struct cpio_data early_initrd_files[ACPI_OVERRIDE_TABLES];
> +       char *p;
> +
> +       if (data == NULL || size == 0)
> +               return;
> +
> +       for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) {
> +               file = find_cpio_data(cpio_path, data, size, &offset);
> +               if (!file.data)
> +                       break;
> +
> +               data += offset;
> +               size -= offset;
> +
> +               if (file.size < sizeof(struct acpi_table_header))
> +                       INVALID_TABLE("Table smaller than ACPI header",
> +                                     cpio_path, file.name);
> +
> +               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, file.name);
> +               if (file.size != table->length)
> +                       INVALID_TABLE("File length does not match table length",
> +                                     cpio_path, file.name);
> +               if (acpi_table_checksum(file.data, table->length))
> +                       INVALID_TABLE("Bad table checksum",
> +                                     cpio_path, file.name);
> +
> +               pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n",
> +                       table->signature, cpio_path, file.name, 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();

need to move those arch related to arch/x86

> +       p = early_ioremap(acpi_tables_addr, all_tables_size);
> +
> +       for (no = 0; no < table_nr; no++) {
> +               memcpy(p + total_offset, early_initrd_files[no].data,
> +                      early_initrd_files[no].size);
> +               total_offset += early_initrd_files[no].size;
> +       }

You may use one loop function, and it could take one call back.
callback 1 will get item and size.
callback 2 will do the copy...

so you can remove hard limit of ACPI_OVERRIDE_TABLES.

> +       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);
> +}
> +

can you split acpi_table_taint split change to another patch?

>  acpi_status
>  acpi_os_table_override(struct acpi_table_header * existing_table,
>                        struct acpi_table_header ** new_table)
> @@ -547,24 +678,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

also could hide macro in header file.

> +       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
> +}

could be split to another patch too.


>
>  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);

could drop __init declaration in header file.

> +#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	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  2012-09-21 13:28 ` [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
       [not found]   ` <1348234085-39220-3-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
@ 2012-09-22 15:16   ` Len Brown
       [not found]     ` <505DD65F.9080203-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
  2012-09-24 20:27     ` H. Peter Anvin
  1 sibling, 2 replies; 19+ messages in thread
From: Len Brown @ 2012-09-22 15:16 UTC (permalink / raw)
  To: Thomas Renninger
  Cc: hpa, initramfs, robert.moore, linux-kernel, linux-acpi, yinghai,
	eric.piel, vojcek, Lin Ming

This isn't a NAK, but I'm at best, "like warm" on this.

I'm not convinced it is a good thing to have - enabled by default -
the ability for users to easily over-ride ACPI tables.

I am concerned this will result in users hacking their BIOS, and making themselves
unsupportable by both their HW and SW suppliers.

"But it is just a _debug_ capability", one counters, "we'd never have
to support somebody who actually uses it."

Even if you toss support (and security) out the window, there is a more insidious
problem.  When such a user latches onto a workaround that tickles their
itch, they are satisfied, and they have zero incentive to get
either their BIOS or Linux fixed for the benefit of other users.

Today we have 2 methods to override AML:
ACPI_CUSTOM_METHOD (default n) allows you to scribble on AML
on the current running system.
ACPI_CUSTOM_DSDT (default n) allows you to override the entire DSDT/SSDT,
but requires you to build that DSDT into the custom kernel itself.

Developers running on their own systems are not complaining about these.

But what if you want to debug something on a remote system
with a distro binary kernel, you say?  The user doesn't know how
to build a kernel, and the distro is too busy to do so.
Some distros ship the initrd hack to  address this problem,
even though it has been repeatedly rejected upstream.
But curiously, even larger distros do NOT ship that hack
and somehow they have survived the last decade of Linux/ACPI
deployment without it.  How is that possible?

Yes, convenience sounds like an improvement over inconvenience.
Yes, generality to override any table sounds like a good thing
over the limitation to override just AML tables.
But does that make it a good idea?

specific comments in-line below...


On 09/21/2012 09:28 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>
> 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                           |  203 ++++++++++++++++++++++++--
>  include/linux/acpi.h                         |    4 +
>  5 files changed, 331 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..b550831
> --- /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

If "the" ACPI_...

> +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

"bug, which is so severe"

> +that a workaround is not accepted in the Linus kernel.

"Linux 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...

The above 6 paragraphs are appropriate for the mailing list,
but don't belong in the README file.  Presumably this capability
is present in the kernel being run, so you don't have to sell it
over alternative debug methods.

> +
> +
> +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 a /kernel/firmware/acpi directory inside the
> +# cpio archive.
> +# The uncompressed cpio archive 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
> +# A maximum of: #define ACPI_OVERRIDE_TABLES 10
> +# tables are  currently allowed (see osl.c):
> +iasl -sa facp.dsl
> +iasl -sa ssdt1.dsl
> +cp facp.aml kernel/firmware/acpi
> +cp ssdt1.aml kernel/firmware/acpi
> +# Create the uncompressed cpio archive and concatenate the orginal initrd

"original"

> +# 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..ba7560f 100644
> --- a/drivers/acpi/osl.c
> +++ b/drivers/acpi/osl.c
> @@ -534,6 +534,137 @@ 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, path, name)					\
> +	{ pr_err("ACPI OVERRIDE: " x " [%s%s]\n", path, name); continue; }
> +
> +#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
> +
> +/* Must not increase 10 or needs code modifcation below */

"modification"



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

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
       [not found]     ` <505DD65F.9080203-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
@ 2012-09-23  1:17       ` Thomas Renninger
       [not found]         ` <201209230317.04050.trenn-l3A5Bk7waGM@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: Thomas Renninger @ 2012-09-23  1:17 UTC (permalink / raw)
  To: Len Brown
  Cc: hpa-YMNOUZJC4hwAvxtiuMwx3w, initramfs-u79uwXL29TY76Z2rM5mHXA,
	robert.moore-ral2JQCrhuEAvxtiuMwx3w,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA, yinghai-DgEjT+Ai2ygdnm+yROfE0A,
	eric.piel-VkQ1JFuSMpfAbQlEx87xDw, vojcek-wYtBgQxc//8

Hi Len,

On Saturday 22 September 2012 17:16:47 Len Brown wrote:
> This isn't a NAK, but I'm at best, "like warm" on this.
We had this discussion already...
And you already had similar patches Signed-off and put in your queue
for Linus in 2008:
http://www.mail-archive.com/linux-acpi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org/msg11907.html

Iirc they have not been accepted because populate_rootfs was too
late or the way the tables have been passed has been (correctly)
considered a bad approach.

This is the clean re-write.
 
> I'm not convinced it is a good thing to have - enabled by default -
> the ability for users to easily over-ride ACPI tables.
> 
> I am concerned this will result in users hacking their BIOS, and making themselves
> unsupportable by both their HW and SW suppliers.
They can do that already by overriding via re-compiling the kernel.
Making it as hard as possible, also means making debugging ACPI problems
on Linux as hard as possible.
There are quite some bugs that got stuck, because people could not compile
a kernel.
And quite some people who have the knowledge and had overridden their DSDTs
via recompiling the kernel wasted some hours/days (compiling a kernel on the
private laptop at home takes time) and they could have invested their time
better.

> "But it is just a _debug_ capability", one counters, "we'd never have
> to support somebody who actually uses it."
Both is right and these are the answers to most of your concerns.
 
> Even if you toss support (and security) out the window, there is a more insidious
> problem.  When such a user latches onto a workaround that tickles their
> itch, they are satisfied, and they have zero incentive to get
> either their BIOS or Linux fixed for the benefit of other users.
I expect it exactly the other way round:
It has been made clear to report issues.
Currently these guys are more or less on their own, not finding a workaround
or any solution at all.
Now with some help they will find workarounds, post them on the list and
developers will already get a concrete idea of what is going wrong.
 
> Today we have 2 methods to override AML:
> ACPI_CUSTOM_METHOD (default n) allows you to scribble on AML
> on the current running system.
> ACPI_CUSTOM_DSDT (default n) allows you to override the entire DSDT/SSDT,
> but requires you to build that DSDT into the custom kernel itself.
I know, what is so bad to make developing this stuff on Linux "even easier"?

> Developers running on their own systems are not complaining about these.
> 
> But what if you want to debug something on a remote system
> with a distro binary kernel, you say?  The user doesn't know how
> to build a kernel, and the distro is too busy to do so.
> Some distros ship the initrd hack to  address this problem,
> even though it has been repeatedly rejected upstream.
> But curiously, even larger distros do NOT ship that hack
> and somehow they have survived the last decade of Linux/ACPI
> deployment without it.  How is that possible?
>  
> Yes, convenience sounds like an improvement over inconvenience.
> Yes, generality to override any table sounds like a good thing
> over the limitation to override just AML tables.
> But does that make it a good idea?

I do not want to re-discuss the topic all over again if possible,
here my major points:
  - Even on "Linux supported by vendor" systems it can be convenient
    for example for a distribution to be able to prove a bug to be
    a BIOS and not a kernel bug, by simply booting the fixed BIOS
    and verify the issue to be fixed.
  - Quite some BIOS tables, even from vendors supporting Linux, have
    dozens of warnings and errors in their ACPI tables.
    If it is easier to develop and debug ACPI code on Linux, there
    might be more vendors doing that and providing more robust BIOSes.
  - Unfortunately a lot or most laptop/desktop vendors still do not
    care that much about Linux. Quite some systems are sold and nobody
    even tried to boot Linux on them. The BIOS did only get verified
    on Windows. On such systems it's essential that people can find
    out as easy as possible how and why their BIOS behaves like the
    way it does and then contribute that information to the list to get
    things solved.
  - ...
    
> specific comments in-line below...

Thanks a lot for going through it!
I'll address yours (and Yinghai's) findings and try to get an update
sent out on Monday.

    Thomas

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

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
       [not found]         ` <201209230317.04050.trenn-l3A5Bk7waGM@public.gmane.org>
@ 2012-09-23  4:25           ` Len Brown
  2012-09-24  6:40             ` Thomas Renninger
  2012-09-24 18:26             ` Matthew Garrett
  0 siblings, 2 replies; 19+ messages in thread
From: Len Brown @ 2012-09-23  4:25 UTC (permalink / raw)
  To: Thomas Renninger
  Cc: hpa-YMNOUZJC4hwAvxtiuMwx3w, initramfs-u79uwXL29TY76Z2rM5mHXA,
	robert.moore-ral2JQCrhuEAvxtiuMwx3w,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA, yinghai-DgEjT+Ai2ygdnm+yROfE0A,
	eric.piel-VkQ1JFuSMpfAbQlEx87xDw, vojcek-wYtBgQxc//8

> +config ACPI_INITRD_TABLE_OVERRIDE
> +       bool
> +       default y

Do distros in addition to SuSE concur they want to ship this way?

The last time we tried to make debugging easier we added
ACPI_CUSTOM_METHOD, which allowed root to over-ride an AML method
on a running system.  Distro security-minded people were not amused.

thanks,
-Len Brown, Intel Open Source Technology Center

ps I noticed your reference to acpidump in the README.
That reminded me to push it to the kernel source tree.
Its new home will be tools/power/acpi/

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

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  2012-09-23  4:25           ` Len Brown
@ 2012-09-24  6:40             ` Thomas Renninger
  2012-09-24  9:21               ` Alan Cox
  2012-09-24 18:26             ` Matthew Garrett
  1 sibling, 1 reply; 19+ messages in thread
From: Thomas Renninger @ 2012-09-24  6:40 UTC (permalink / raw)
  To: Len Brown
  Cc: hpa, initramfs, robert.moore, linux-kernel, linux-acpi, yinghai,
	eric.piel, vojcek

On Sunday 23 September 2012 06:25:40 Len Brown wrote:
> > +config ACPI_INITRD_TABLE_OVERRIDE
> > +       bool
> > +       default y
> 
> Do distros in addition to SuSE concur they want to ship this way?
Whether distros ship this in their enterprise, community or just in
a -debug kernel flavor is up to them.
I cannot see why this cannot be enabled by default on all.
That is what the TAINT flag is for...
 
> The last time we tried to make debugging easier we added
> ACPI_CUSTOM_METHOD, which allowed root to over-ride an AML method
> on a running system.  Distro security-minded people were not amused.
Yep and therefore you have to remove this one from the tools for
ACPI debugging you listed.
The issue is/was, that root can inject code at runtime which is then
executed in kernel environment.
Afaik there are "security" provisions or say setups, which do
hide modprobe/insmod and do not allow root to load any kernel drivers 
or similar.
If one can write the kernel or initrd which gets booted, I guess there
are not much security restrictions anymore you could put on this user...
But thanks for the pointer, I'll go and double check with some
security guys.

> thanks,
> -Len Brown, Intel Open Source Technology Center
> 
> ps I noticed your reference to acpidump in the README.
> That reminded me to push it to the kernel source tree.
> Its new home will be tools/power/acpi
This is the one which I tried to/did adjust to acpica headers?
This sounds like a very good idea. I'll adjust the docs.

pss: Can this tool live there as well:
ftp://ftp.suse.com/pub/people/trenn/sources/ec/ec_access.c
It's the userspace tool for examining EC values (and changes) via
ec_sys debug driver and a corresponding /sys/kernel/debug/.. file.
It's more ore less doing the same what the old thinkpad_acpi driver
could, but offers this to all machines with an EC device.

    Thomas

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

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  2012-09-24  6:40             ` Thomas Renninger
@ 2012-09-24  9:21               ` Alan Cox
  0 siblings, 0 replies; 19+ messages in thread
From: Alan Cox @ 2012-09-24  9:21 UTC (permalink / raw)
  To: Thomas Renninger
  Cc: Len Brown, hpa, initramfs, robert.moore, linux-kernel, linux-acpi,
	yinghai, eric.piel, vojcek

> The issue is/was, that root can inject code at runtime which is then
> executed in kernel environment.

Yes there are lots of other ways to do this too. The constraint we use
for it is CAP_SYS_RAWIO. With that capability you can totally do raw
hardware access and the like so requiring it for runtime ACPI updating
and execution is consistent with the security model.

> Afaik there are "security" provisions or say setups, which do
> hide modprobe/insmod and do not allow root to load any kernel drivers 
> or similar.

To do this you have to revoke CAP_SYS_RAWIO.

Alan

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

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  2012-09-23  4:25           ` Len Brown
  2012-09-24  6:40             ` Thomas Renninger
@ 2012-09-24 18:26             ` Matthew Garrett
  1 sibling, 0 replies; 19+ messages in thread
From: Matthew Garrett @ 2012-09-24 18:26 UTC (permalink / raw)
  To: Len Brown
  Cc: Thomas Renninger, hpa, initramfs, robert.moore, linux-kernel,
	linux-acpi, yinghai, eric.piel, vojcek

On Sun, Sep 23, 2012 at 12:25:40AM -0400, Len Brown wrote:
> > +config ACPI_INITRD_TABLE_OVERRIDE
> > +       bool
> > +       default y
> 
> Do distros in addition to SuSE concur they want to ship this way?

We certainly don't.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  2012-09-22 15:16   ` Len Brown
       [not found]     ` <505DD65F.9080203-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
@ 2012-09-24 20:27     ` H. Peter Anvin
  1 sibling, 0 replies; 19+ messages in thread
From: H. Peter Anvin @ 2012-09-24 20:27 UTC (permalink / raw)
  To: Len Brown
  Cc: Thomas Renninger, initramfs, robert.moore, linux-kernel,
	linux-acpi, yinghai, eric.piel, vojcek, Lin Ming

On 09/22/2012 08:16 AM, Len Brown wrote:
> This isn't a NAK, but I'm at best, "like warm" on this.
> 
> I'm not convinced it is a good thing to have - enabled by default -
> the ability for users to easily over-ride ACPI tables.

For the record: I'm fine with the implementation from a technical
standpoint now; I'm completely agnostic on the policy.  Since Len is the
ACPI maintainer this is up to him.

	-hpa


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

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
       [not found]       ` <CAE9FiQX64iRo9QjhEPYtEOhbEweKSmTssQ50VfPznWtHA345CQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2012-09-25 14:17         ` Thomas Renninger
  0 siblings, 0 replies; 19+ messages in thread
From: Thomas Renninger @ 2012-09-25 14:17 UTC (permalink / raw)
  To: Yinghai Lu
  Cc: hpa-YMNOUZJC4hwAvxtiuMwx3w, initramfs-u79uwXL29TY76Z2rM5mHXA,
	robert.moore-ral2JQCrhuEAvxtiuMwx3w, lenb-DgEjT+Ai2ygdnm+yROfE0A,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	eric.piel-VkQ1JFuSMpfAbQlEx87xDw, vojcek-wYtBgQxc//8

...
> > +       /*
> > +        * 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();
> 
> need to move those arch related to arch/x86

I agree it should get moved out.
I have split this into a separate patch.

> > +       p = early_ioremap(acpi_tables_addr, all_tables_size);
> > +
> > +       for (no = 0; no < table_nr; no++) {
> > +               memcpy(p + total_offset, early_initrd_files[no].data,
> > +                      early_initrd_files[no].size);
> > +               total_offset += early_initrd_files[no].size;
> > +       }
> 
> You may use one loop function, and it could take one call back.
> callback 1 will get item and size.
> callback 2 will do the copy...
> 
> so you can remove hard limit of ACPI_OVERRIDE_TABLES.
I do not fully understand this one.
Currently I have 3 steps:
  1) iterate over all tables and
      - remember address and size of each
      - sum up total size
  2) memblock reserve total size
  3) copy each table into the memblock reserved area

I cannot see how I could get around the limit easily.
Also the restriction is not a big deal, 10 tables is
a lot. It can also be increased without much bad side-effects,
because all xy[ACPI_OVERRIDE_TABLES] arrays are not global.

I'll sent a split up patchset...

> > +       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);
> > +}
> > +
> 
> can you split acpi_table_taint split change to another patch?
Yep.

> >  acpi_status
> >  acpi_os_table_override(struct acpi_table_header * existing_table,
> >                        struct acpi_table_header ** new_table)
> > @@ -547,24 +678,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);

One taint too much the one below is enough...

> > -       }
> > +       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
> 
> also could hide macro in header file.
No, that's not possible.
This would be acpica, multi OS headers, I doubt they want
to have Linux specific macros in there...
 
I addressed all the rest and will sent out split up patches.

Thanks for your review!

   Thomas

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

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

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-18 10:36 Early initrd file overwrite and ACPI table override making use of it Thomas Renninger
2012-07-18 10:36 ` [PATCH 1/2] init: Introduce early initrd files through uncompressed cpio passing Thomas Renninger
     [not found]   ` <1342607764-66747-2-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
2012-07-21 15:21     ` H. Peter Anvin
     [not found]       ` <500AC8F6.4010802-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org>
2012-07-23 14:40         ` Thomas Renninger
2012-07-23 15:09           ` H. Peter Anvin
2012-07-24  9:16             ` Thomas Renninger
2012-07-18 10:36 ` [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
  -- strict thread matches above, loose matches on Subject: below --
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 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
     [not found]   ` <1346318957-5831-3-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
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 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
     [not found]   ` <1348234085-39220-3-git-send-email-trenn-l3A5Bk7waGM@public.gmane.org>
2012-09-21 20:56     ` Yinghai Lu
     [not found]       ` <CAE9FiQX64iRo9QjhEPYtEOhbEweKSmTssQ50VfPznWtHA345CQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-09-25 14:17         ` Thomas Renninger
2012-09-22 15:16   ` Len Brown
     [not found]     ` <505DD65F.9080203-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2012-09-23  1:17       ` Thomas Renninger
     [not found]         ` <201209230317.04050.trenn-l3A5Bk7waGM@public.gmane.org>
2012-09-23  4:25           ` Len Brown
2012-09-24  6:40             ` Thomas Renninger
2012-09-24  9:21               ` Alan Cox
2012-09-24 18:26             ` Matthew Garrett
2012-09-24 20:27     ` H. Peter Anvin

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).