 drivers/acpi/Kconfig        |   17 +++++
 drivers/acpi/osl.c          |  127 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/acpi/tables/tbget.c |    8 ++
 include/acpi/acpiosxf.h     |    4 +
 include/linux/kernel.h      |    1 
 init/main.c                 |   16 ++---
 kernel/panic.c              |    5 +
 7 files changed, 164 insertions(+), 14 deletions(-)

Index: linux-2.6.18/drivers/acpi/Kconfig
===================================================================
--- linux-2.6.18.orig/drivers/acpi/Kconfig
+++ linux-2.6.18/drivers/acpi/Kconfig
@@ -264,6 +264,23 @@ config ACPI_CUSTOM_DSDT_FILE
 	  Enter the full path name to the file which includes the AmlCode
 	  declaration.
 
+config ACPI_CUSTOM_DSDT_INITRD
+	bool "Read Custom DSDT from initramfs"
+	depends on BLK_DEV_INITRD
+	default y
+	help
+	  The DSDT (Differentiated System Description Table) often needs to be
+	  overridden because of broken BIOS implementations. If this feature is
+	  activated you will be able to provide a customized DSDT by adding it
+	  to your initramfs.  For now you need to use a special mkinitrd tool.
+	  For more details see <file:Documentation/dsdt-initrd.txt> or
+	  <http://gaugusch.at/kernel.shtml>. If there is no table found, it
+	  will fallback to the custom DSDT in-kernel (if activated) or to the
+	  DSDT from the BIOS.
+
+	  Even if you do not need a new one at the moment, you may want to use a
+	  better implemented DSDT later. It is safe to say Y here.
+
 config ACPI_BLACKLIST_YEAR
 	int "Disable ACPI for systems before Jan 1st this year" if X86_32
 	default 0
Index: linux-2.6.18/drivers/acpi/osl.c
===================================================================
--- linux-2.6.18.orig/drivers/acpi/osl.c
+++ linux-2.6.18/drivers/acpi/osl.c
@@ -69,6 +69,10 @@ extern char line_buf[80];
 int acpi_specific_hotkey_enabled = TRUE;
 EXPORT_SYMBOL(acpi_specific_hotkey_enabled);
 
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+int acpi_must_unregister_table = FALSE;
+#endif
+
 static unsigned int acpi_irq_irq;
 static acpi_osd_handler acpi_irq_handler;
 static void *acpi_irq_context;
@@ -219,6 +223,113 @@ acpi_os_predefined_override(const struct
 	return AE_OK;
 }
 
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+#define MAX_DS_SDTS 10
+static struct acpi_table_header *ds_sdt_buffers[MAX_DS_SDTS];
+static unsigned int tables_loaded = 0;
+
+void acpi_load_override_tables(void){
+	struct file         *firmware_file;
+	mm_segment_t        oldfs;
+	unsigned long       len, len2;
+	struct kstat        stat;
+	unsigned int        x, y;
+	char *ramfs_ds_sdt_names[MAX_DS_SDTS] = {
+		"/DSDT.aml",
+		"/SSDT1.aml",
+		"/SSDT2.aml",
+		"/SSDT3.aml",
+		"/SSDT4.aml",
+		"/SSDT5.aml",
+		"/SSDT6.aml",
+		"/SSDT7.aml",
+		"/SSDT8.aml",
+		"/SSDT9.aml",
+	};
+	/*
+	 * Never do this at home, only the user-space is allowed to open a file.
+	 * The clean way would be to use the firmware loader. But this code must be run
+	 * before there is any userspace available. So we need a static/init firmware
+	 * infrastructure, which doesn't exist yet...
+	 */
+	for (x = 0; x < MAX_DS_SDTS; x++){
+		if (vfs_stat(ramfs_ds_sdt_names[x], &stat) < 0) {
+			continue;
+		}
+		len = stat.size;
+		/* check especially against empty files */
+		if (len <= 4) {
+			printk("error file %s is too small, only %lu bytes.\n",
+			       ramfs_ds_sdt_names[x], len);
+			continue;
+		}
+
+		ds_sdt_buffers[x] = kmalloc(len, GFP_KERNEL);
+		if (!ds_sdt_buffers[x]) {
+			printk("error when allocating %lu bytes of memory.\n",
+			       len);
+			/* better free all tables again */
+			for (y = 0; y < x; y++){
+				if (ds_sdt_buffers[y])
+					kfree(ds_sdt_buffers[x]);
+			}
+			acpi_must_unregister_table = FALSE;
+			return;
+		}
+
+		firmware_file = filp_open(ramfs_ds_sdt_names[x], O_RDONLY, 0);
+		if (IS_ERR(firmware_file)) {
+			printk("error, could not open file %s.\n",
+			       ramfs_ds_sdt_names[x]);
+			kfree(ds_sdt_buffers[x]);
+			continue;
+		}
+
+		oldfs = get_fs();
+		set_fs(KERNEL_DS);
+		len2 = vfs_read(firmware_file,
+				(char __user *)ds_sdt_buffers[x],
+				len,
+				&firmware_file->f_pos);
+		set_fs(oldfs);
+		filp_close(firmware_file, NULL);
+		if (len2 < len) {
+			printk("error trying to read %lu bytes from %s.\n",
+			       len, ramfs_ds_sdt_names[x]);
+			kfree(ds_sdt_buffers[x]);
+			continue;
+		}
+		printk(PREFIX "successfully read %lu bytes from file %s\n",
+		       len, ramfs_ds_sdt_names[x]);
+	}
+}
+
+struct acpi_table_header * acpi_find_dsdt_initrd(struct acpi_table_header * t)
+{
+	struct acpi_table_header	*ret = NULL;
+	unsigned int                     x;
+	for (x = 0; x < MAX_DS_SDTS; x++){
+		if (ds_sdt_buffers[x]){
+			if (!memcmp(ds_sdt_buffers[x]->signature,
+				    t->signature, 4) &&
+			    !memcmp(ds_sdt_buffers[x]->oem_table_id,
+				    t->oem_table_id, 8)){
+				ret = ds_sdt_buffers[x];
+				printk(PREFIX "Override [%4.4s-%8.8s]"
+				       " from initramfs -"
+				       " tainting kernel\n",
+				       t->signature,
+				       t->oem_table_id);
+				add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
+				acpi_must_unregister_table = TRUE;
+				break;
+			}
+		}
+	}
+	return ret;
+}
+#endif
+
 acpi_status
 acpi_os_table_override(struct acpi_table_header * existing_table,
 		       struct acpi_table_header ** new_table)
@@ -226,13 +337,21 @@ acpi_os_table_override(struct acpi_table
 	if (!existing_table || !new_table)
 		return AE_BAD_PARAMETER;
 
+	*new_table = NULL;
+
 #ifdef CONFIG_ACPI_CUSTOM_DSDT
 	if (strncmp(existing_table->signature, "DSDT", 4) == 0)
 		*new_table = (struct acpi_table_header *)AmlCode;
-	else
-		*new_table = NULL;
-#else
-	*new_table = NULL;
+#endif
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+	if (!tables_loaded){
+		acpi_load_override_tables();
+		tables_loaded = 1;
+	}
+	if (!strncmp(existing_table->signature, "DSDT", 4) ||
+	    !strncmp(existing_table->signature, "SSDT", 4)){
+		*new_table = acpi_find_dsdt_initrd(existing_table);
+	}
 #endif
 	return AE_OK;
 }
Index: linux-2.6.18/drivers/acpi/tables/tbget.c
===================================================================
--- linux-2.6.18.orig/drivers/acpi/tables/tbget.c
+++ linux-2.6.18/drivers/acpi/tables/tbget.c
@@ -278,6 +278,14 @@ acpi_tb_table_override(struct acpi_table
 	address.pointer.logical = new_table;
 
 	status = acpi_tb_get_this_table(&address, new_table, table_info);
+
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+	if (acpi_must_unregister_table) {
+		kfree(new_table);
+		acpi_must_unregister_table = FALSE;
+	}
+#endif
+
 	if (ACPI_FAILURE(status)) {
 		ACPI_EXCEPTION((AE_INFO, status, "Could not copy ACPI table"));
 		return_ACPI_STATUS(status);
Index: linux-2.6.18/include/acpi/acpiosxf.h
===================================================================
--- linux-2.6.18.orig/include/acpi/acpiosxf.h
+++ linux-2.6.18/include/acpi/acpiosxf.h
@@ -95,6 +95,10 @@ acpi_status
 acpi_os_table_override(struct acpi_table_header *existing_table,
 		       struct acpi_table_header **new_table);
 
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+extern int acpi_must_unregister_table;
+#endif
+
 /*
  * Spinlock primitives
  */
Index: linux-2.6.18/init/main.c
===================================================================
--- linux-2.6.18.orig/init/main.c
+++ linux-2.6.18/init/main.c
@@ -581,8 +581,6 @@ asmlinkage void __init start_kernel(void
 
 	check_bugs();
 
-	acpi_early_init(); /* before LAPIC and SMP init */
-
 	/* Do the rest non-__init'ed, we're now alive */
 	rest_init();
 }
@@ -699,6 +697,14 @@ static int init(void * unused)
 	 */
 	child_reaper = current;
 
+ 	/*
+ 	 * Do this before initcalls, because some drivers want to access
+ 	 * firmware files.
+ 	 */
+ 	populate_rootfs();
+
+ 	acpi_early_init(); /* before LAPIC and SMP init */
+
 	smp_prepare_cpus(max_cpus);
 
 	do_pre_smp_initcalls();
@@ -708,12 +714,6 @@ static int init(void * unused)
 
 	cpuset_init_smp();
 
-	/*
-	 * Do this before initcalls, because some drivers want to access
-	 * firmware files.
-	 */
-	populate_rootfs();
-
 	do_basic_setup();
 
 	/*
Index: linux-2.6.18/include/linux/kernel.h
===================================================================
--- linux-2.6.18.orig/include/linux/kernel.h
+++ linux-2.6.18/include/linux/kernel.h
@@ -206,6 +206,7 @@ extern enum system_states {
 #define TAINT_FORCED_RMMOD		(1<<3)
 #define TAINT_MACHINE_CHECK		(1<<4)
 #define TAINT_BAD_PAGE			(1<<5)
+#define TAINT_OVERRIDDEN_ACPI_TABLE	(1<<6)
 
 extern void dump_stack(void);
 
Index: linux-2.6.18/kernel/panic.c
===================================================================
--- linux-2.6.18.orig/kernel/panic.c
+++ linux-2.6.18/kernel/panic.c
@@ -158,13 +158,14 @@ const char *print_tainted(void)
 {
 	static char buf[20];
 	if (tainted) {
-		snprintf(buf, sizeof(buf), "Tainted: %c%c%c%c%c%c",
+		snprintf(buf, sizeof(buf), "Tainted: %c%c%c%c%c%c%c",
 			tainted & TAINT_PROPRIETARY_MODULE ? 'P' : 'G',
 			tainted & TAINT_FORCED_MODULE ? 'F' : ' ',
 			tainted & TAINT_UNSAFE_SMP ? 'S' : ' ',
 			tainted & TAINT_FORCED_RMMOD ? 'R' : ' ',
  			tainted & TAINT_MACHINE_CHECK ? 'M' : ' ',
-			tainted & TAINT_BAD_PAGE ? 'B' : ' ');
+			tainted & TAINT_BAD_PAGE ? 'B' : ' ',
+			tainted & TAINT_OVERRIDDEN_ACPI_TABLE ? 'A' : ' ');
 	}
 	else
 		snprintf(buf, sizeof(buf), "Not tainted");
