summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/acpi/dsdt-override.txt15
-rwxr-xr-xDocumentation/acpi/initramfs-add-dsdt.sh43
-rw-r--r--Documentation/kernel-parameters.txt3
-rw-r--r--drivers/acpi/Kconfig17
-rw-r--r--drivers/acpi/osl.c97
-rw-r--r--include/linux/kernel.h1
-rw-r--r--init/initramfs.c8
-rw-r--r--init/main.c7
-rw-r--r--kernel/panic.c5
9 files changed, 187 insertions, 9 deletions
diff --git a/Documentation/acpi/dsdt-override.txt b/Documentation/acpi/dsdt-override.txt
new file mode 100644
index 00000000000..5008f256a2d
--- /dev/null
+++ b/Documentation/acpi/dsdt-override.txt
@@ -0,0 +1,15 @@
+Linux supports two methods of overriding the BIOS DSDT:
+
+CONFIG_ACPI_CUSTOM_DSDT builds the image into the kernel.
+
+CONFIG_ACPI_CUSTOM_DSDT_INITRD adds the image to the initrd.
+
+When to use these methods is described in detail on the
+Linux/ACPI home page:
+http://www.lesswatts.org/projects/acpi/overridingDSDT.php
+
+Note that if both options are used, the DSDT supplied
+by the INITRD method takes precedence.
+
+Documentation/initramfs-add-dsdt.sh is provided for convenience
+for use with the CONFIG_ACPI_CUSTOM_DSDT_INITRD method.
diff --git a/Documentation/acpi/initramfs-add-dsdt.sh b/Documentation/acpi/initramfs-add-dsdt.sh
new file mode 100755
index 00000000000..17ef6e838e1
--- /dev/null
+++ b/Documentation/acpi/initramfs-add-dsdt.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# Adds a DSDT file to the initrd (if it's an initramfs)
+# first argument is the name of archive
+# second argument is the name of the file to add
+# The file will be copied as /DSDT.aml
+
+# 20060126: fix "Premature end of file" with some old cpio (Roland Robic)
+# 20060205: this time it should really work
+
+# check the arguments
+if [ $# -ne 2 ]; then
+ program_name=$(basename $0)
+ echo "\
+$program_name: too few arguments
+Usage: $program_name initrd-name.img DSDT-to-add.aml
+Adds a DSDT file to an initrd (in initramfs format)
+
+ initrd-name.img: filename of the initrd in initramfs format
+ DSDT-to-add.aml: filename of the DSDT file to add
+ " 1>&2
+ exit 1
+fi
+
+# we should check it's an initramfs
+
+tempcpio=$(mktemp -d)
+# cleanup on exit, hangup, interrupt, quit, termination
+trap 'rm -rf $tempcpio' 0 1 2 3 15
+
+# extract the archive
+gunzip -c "$1" > "$tempcpio"/initramfs.cpio || exit 1
+
+# copy the DSDT file at the root of the directory so that we can call it "/DSDT.aml"
+cp -f "$2" "$tempcpio"/DSDT.aml
+
+# add the file
+cd "$tempcpio"
+(echo DSDT.aml | cpio --quiet -H newc -o -A -O "$tempcpio"/initramfs.cpio) || exit 1
+cd "$OLDPWD"
+
+# re-compress the archive
+gzip -c "$tempcpio"/initramfs.cpio > "$1"
+
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 8ea41b6e6a8..0dcbd266b44 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -177,6 +177,9 @@ and is between 256 and 4096 characters. It is defined in the file
acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT
+ acpi_no_initrd_override [KNL,ACPI]
+ Disable loading custom ACPI tables from the initramfs
+
acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
Format: To spoof as Windows 98: ="Microsoft Windows"
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index d141bdb3a7d..7ef172c2a1d 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -276,8 +276,10 @@ config ACPI_CUSTOM_DSDT
depends on !STANDALONE
default n
help
- This option is to load a custom ACPI DSDT
- If you don't know what that is, say N.
+ This option supports a custom DSDT by linking it into the kernel.
+ See Documentation/acpi/dsdt-override.txt
+
+ If unsure, say N.
config ACPI_CUSTOM_DSDT_FILE
string "Custom DSDT Table file to include"
@@ -287,6 +289,17 @@ 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 n
+ help
+ This option supports a custom DSDT by optionally loading it from initrd.
+ See Documentation/acpi/dsdt-override.txt
+
+ If you are not using this feature now, but may use it 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
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index c2c585366fa..27ccd68b8f4 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -91,6 +91,10 @@ static DEFINE_SPINLOCK(acpi_res_lock);
#define OSI_STRING_LENGTH_MAX 64 /* arbitrary */
static char osi_additional_string[OSI_STRING_LENGTH_MAX];
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+static int acpi_no_initrd_override;
+#endif
+
/*
* "Ode to _OSI(Linux)"
*
@@ -329,6 +333,67 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
return AE_OK;
}
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+struct acpi_table_header *acpi_find_dsdt_initrd(void)
+{
+ struct file *firmware_file;
+ mm_segment_t oldfs;
+ unsigned long len, len2;
+ struct acpi_table_header *dsdt_buffer, *ret = NULL;
+ struct kstat stat;
+ char *ramfs_dsdt_name = "/DSDT.aml";
+
+ printk(KERN_INFO PREFIX "Checking initramfs for custom DSDT");
+
+ /*
+ * 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.
+ * A static/init firmware infrastructure doesn't exist yet...
+ */
+ if (vfs_stat(ramfs_dsdt_name, &stat) < 0)
+ return ret;
+
+ len = stat.size;
+ /* check especially against empty files */
+ if (len <= 4) {
+ printk(KERN_ERR PREFIX "Failed: DSDT only %lu bytes.\n", len);
+ return ret;
+ }
+
+ firmware_file = filp_open(ramfs_dsdt_name, O_RDONLY, 0);
+ if (IS_ERR(firmware_file)) {
+ printk(KERN_ERR PREFIX "Failed to open %s.\n", ramfs_dsdt_name);
+ return ret;
+ }
+
+ dsdt_buffer = kmalloc(len, GFP_ATOMIC);
+ if (!dsdt_buffer) {
+ printk(KERN_ERR PREFIX "Failed to allocate %lu bytes.\n", len);
+ goto err;
+ }
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ len2 = vfs_read(firmware_file, (char __user *)dsdt_buffer, len,
+ &firmware_file->f_pos);
+ set_fs(oldfs);
+ if (len2 < len) {
+ printk(KERN_ERR PREFIX "Failed to read %lu bytes from %s.\n",
+ len, ramfs_dsdt_name);
+ ACPI_FREE(dsdt_buffer);
+ goto err;
+ }
+
+ printk(KERN_INFO PREFIX "Found %lu byte DSDT in %s.\n",
+ len, ramfs_dsdt_name);
+ ret = dsdt_buffer;
+err:
+ filp_close(firmware_file, NULL);
+ return ret;
+}
+#endif
+
acpi_status
acpi_os_table_override(struct acpi_table_header * existing_table,
struct acpi_table_header ** new_table)
@@ -336,17 +401,41 @@ acpi_os_table_override(struct acpi_table_header * existing_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 ((strncmp(existing_table->signature, "DSDT", 4) == 0) &&
+ !acpi_no_initrd_override) {
+ struct acpi_table_header *initrd_table;
+
+ initrd_table = acpi_find_dsdt_initrd();
+ if (initrd_table)
+ *new_table = initrd_table;
+ }
+#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);
+ }
return AE_OK;
}
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+int __init acpi_no_initrd_override_setup(char *s)
+{
+ acpi_no_initrd_override = 1;
+ return 1;
+}
+__setup("acpi_no_initrd_override", acpi_no_initrd_override_setup);
+#endif
+
static irqreturn_t acpi_irq(int irq, void *dev_id)
{
u32 handled;
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 18222f267bc..9e01f376840 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -243,6 +243,7 @@ extern enum system_states {
#define TAINT_BAD_PAGE (1<<5)
#define TAINT_USER (1<<6)
#define TAINT_DIE (1<<7)
+#define TAINT_OVERRIDDEN_ACPI_TABLE (1<<8)
extern void dump_stack(void) __cold;
diff --git a/init/initramfs.c b/init/initramfs.c
index d53fee8d860..c0b1e0533d8 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -538,7 +538,7 @@ skip:
initrd_end = 0;
}
-static int __init populate_rootfs(void)
+int __init populate_rootfs(void)
{
char *err = unpack_to_rootfs(__initramfs_start,
__initramfs_end - __initramfs_start, 0);
@@ -577,4 +577,10 @@ static int __init populate_rootfs(void)
}
return 0;
}
+#ifndef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+/*
+ * if this option is enabled, populate_rootfs() is called _earlier_ in the
+ * boot sequence. This insures that the ACPI initialisation can find the file.
+ */
rootfs_initcall(populate_rootfs);
+#endif
diff --git a/init/main.c b/init/main.c
index c691f5f7fc2..2a78932f6c0 100644
--- a/init/main.c
+++ b/init/main.c
@@ -102,6 +102,12 @@ static inline void mark_rodata_ro(void) { }
extern void tc_init(void);
#endif
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
+extern int populate_rootfs(void);
+#else
+static inline void populate_rootfs(void) {}
+#endif
+
enum system_states system_state;
EXPORT_SYMBOL(system_state);
@@ -648,6 +654,7 @@ asmlinkage void __init start_kernel(void)
check_bugs();
+ populate_rootfs(); /* For DSDT override from initramfs */
acpi_early_init(); /* before LAPIC and SMP init */
/* Do the rest non-__init'ed, we're now alive */
diff --git a/kernel/panic.c b/kernel/panic.c
index d9e90cfe329..24af9f8bac9 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -161,7 +161,7 @@ const char *print_tainted(void)
{
static char buf[20];
if (tainted) {
- snprintf(buf, sizeof(buf), "Tainted: %c%c%c%c%c%c%c%c",
+ snprintf(buf, sizeof(buf), "Tainted: %c%c%c%c%c%c%c%c%c",
tainted & TAINT_PROPRIETARY_MODULE ? 'P' : 'G',
tainted & TAINT_FORCED_MODULE ? 'F' : ' ',
tainted & TAINT_UNSAFE_SMP ? 'S' : ' ',
@@ -169,7 +169,8 @@ const char *print_tainted(void)
tainted & TAINT_MACHINE_CHECK ? 'M' : ' ',
tainted & TAINT_BAD_PAGE ? 'B' : ' ',
tainted & TAINT_USER ? 'U' : ' ',
- tainted & TAINT_DIE ? 'D' : ' ');
+ tainted & TAINT_DIE ? 'D' : ' ',
+ tainted & TAINT_OVERRIDDEN_ACPI_TABLE ? 'A' : ' ');
}
else
snprintf(buf, sizeof(buf), "Not tainted");