411 lines
12 KiB
Diff
411 lines
12 KiB
Diff
|
From: Eric Piel <piel@localhost.(none)>
|
||
|
Subject: [PATCH 1/1] ACPI: initramfs DSDT override support
|
||
|
Patch-mainline: not yet
|
||
|
|
||
|
|
||
|
Permits to load of DSDT (the main ACPI table) from initramfs. In case this
|
||
|
option is selected, the initramfs is parsed at ACPI initialization (very early
|
||
|
boot time) to look for a file DSDT.aml . This aims at allowing users to
|
||
|
override the DSDT without recompiling the kernel. This is done by adding a new
|
||
|
feature to the initramfs parser so that one specific file can be directly
|
||
|
copied into memory.
|
||
|
|
||
|
This is derived from the patch v0.8 from http://gaugusch.at/kernel.shtml but
|
||
|
with kernel inclusion in mind: some clean-up's in the documentation, default
|
||
|
set to No, a kernel parameter to disable it at runtime, and most important, a
|
||
|
different approach for reading the initramfs which avoids using the filesystem
|
||
|
infrastructure.
|
||
|
|
||
|
It also contains a fix for compilation on non-ACPI platforms provided by Rene Rebe.
|
||
|
|
||
|
Update 17 Sep 2009 jeffm@suse.com:
|
||
|
- 2.6.30 (or so) introduced very early ACPI initialization for proper SMP
|
||
|
detection. This caused crashes since things like the mm caches weren't
|
||
|
set up yet, so kmalloc would crash. This update delays overriding the DSDT
|
||
|
until the acpi_early_init() call that used to override it. Since there is
|
||
|
a DSDT already loaded, it is necessarily a bit hacky.
|
||
|
|
||
|
Signed-off-by: Eric Piel <eric.piel@tremplin-utc.net>
|
||
|
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||
|
Signed-off-by: Len Brown <len.brown@intel.com>
|
||
|
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
|
||
|
---
|
||
|
Documentation/acpi/dsdt-override.txt | 12 +++-
|
||
|
Documentation/acpi/initramfs-add-dsdt.sh | 43 +++++++++++++++
|
||
|
Documentation/kernel-parameters.txt | 3 +
|
||
|
drivers/acpi/Kconfig | 11 ++++
|
||
|
drivers/acpi/acpica/tbxface.c | 36 +++++++++++--
|
||
|
drivers/acpi/osl.c | 28 +++++++++-
|
||
|
init/initramfs.c | 84 +++++++++++++++++++++++++++++++
|
||
|
7 files changed, 209 insertions(+), 8 deletions(-)
|
||
|
create mode 100644 Documentation/acpi/initramfs-add-dsdt.sh
|
||
|
|
||
|
--- a/Documentation/acpi/dsdt-override.txt
|
||
|
+++ b/Documentation/acpi/dsdt-override.txt
|
||
|
@@ -1,7 +1,15 @@
|
||
|
-Linux supports a method of overriding the BIOS DSDT:
|
||
|
+Linux supports two methods of overriding the BIOS DSDT:
|
||
|
|
||
|
CONFIG_ACPI_CUSTOM_DSDT builds the image into the kernel.
|
||
|
|
||
|
-When to use this method is described in detail on the
|
||
|
+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.
|
||
|
--- /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"
|
||
|
+
|
||
|
--- a/Documentation/kernel-parameters.txt
|
||
|
+++ b/Documentation/kernel-parameters.txt
|
||
|
@@ -217,6 +217,9 @@ and is between 256 and 4096 characters.
|
||
|
|
||
|
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"
|
||
|
|
||
|
--- a/drivers/acpi/Kconfig
|
||
|
+++ b/drivers/acpi/Kconfig
|
||
|
@@ -248,6 +248,17 @@ config ACPI_CUSTOM_DSDT
|
||
|
bool
|
||
|
default ACPI_CUSTOM_DSDT_FILE != ""
|
||
|
|
||
|
+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
|
||
|
--- a/drivers/acpi/acpica/tbxface.c
|
||
|
+++ b/drivers/acpi/acpica/tbxface.c
|
||
|
@@ -484,6 +484,33 @@ acpi_get_table_by_index(u32 table_index,
|
||
|
|
||
|
ACPI_EXPORT_SYMBOL(acpi_get_table_by_index)
|
||
|
|
||
|
+static void
|
||
|
+acpi_dsdt_initrd_override(void)
|
||
|
+{
|
||
|
+#if defined(CONFIG_ACPI_CUSTOM_DSDT_INITRD)
|
||
|
+ struct acpi_table_header *new = NULL;
|
||
|
+ struct acpi_table_desc *table;
|
||
|
+ acpi_status status;
|
||
|
+
|
||
|
+ table = &acpi_gbl_root_table_list.tables[ACPI_TABLE_INDEX_DSDT];
|
||
|
+ status = acpi_os_table_override(table->pointer, &new);
|
||
|
+ if (ACPI_SUCCESS(status) && new) {
|
||
|
+ acpi_tb_delete_table(table);
|
||
|
+
|
||
|
+ /* This is the top part of acpi_table_load */
|
||
|
+ memset(table, 0, sizeof(*table));
|
||
|
+ table->address = ACPI_PTR_TO_PHYSADDR(new);
|
||
|
+ table->pointer = new;
|
||
|
+ table->length = new->length;
|
||
|
+ table->flags |= ACPI_TABLE_ORIGIN_OVERRIDE;
|
||
|
+ table->flags |= ACPI_TABLE_ORIGIN_ALLOCATED;
|
||
|
+ memcpy(table->signature.ascii, new->signature, ACPI_NAME_SIZE);
|
||
|
+ acpi_tb_print_table_header(table->address, new);
|
||
|
+ }
|
||
|
+#endif
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
/*******************************************************************************
|
||
|
*
|
||
|
* FUNCTION: acpi_tb_load_namespace
|
||
|
@@ -496,7 +523,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_by_ind
|
||
|
* the RSDT/XSDT.
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
-static acpi_status acpi_tb_load_namespace(void)
|
||
|
+static acpi_status __init acpi_tb_load_namespace(void)
|
||
|
{
|
||
|
acpi_status status;
|
||
|
u32 i;
|
||
|
@@ -522,6 +549,8 @@ static acpi_status acpi_tb_load_namespac
|
||
|
goto unlock_and_exit;
|
||
|
}
|
||
|
|
||
|
+ acpi_dsdt_initrd_override();
|
||
|
+
|
||
|
/* A valid DSDT is required */
|
||
|
|
||
|
status =
|
||
|
@@ -590,7 +619,7 @@ static acpi_status acpi_tb_load_namespac
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
-acpi_status acpi_load_tables(void)
|
||
|
+acpi_status __init acpi_load_tables(void)
|
||
|
{
|
||
|
acpi_status status;
|
||
|
|
||
|
@@ -607,9 +636,6 @@ acpi_status acpi_load_tables(void)
|
||
|
return_ACPI_STATUS(status);
|
||
|
}
|
||
|
|
||
|
-ACPI_EXPORT_SYMBOL(acpi_load_tables)
|
||
|
-
|
||
|
-
|
||
|
/*******************************************************************************
|
||
|
*
|
||
|
* FUNCTION: acpi_install_table_handler
|
||
|
--- a/drivers/acpi/osl.c
|
||
|
+++ b/drivers/acpi/osl.c
|
||
|
@@ -98,6 +98,11 @@ 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;
|
||
|
+extern struct acpi_table_header *acpi_find_dsdt_initrd(void);
|
||
|
+#endif
|
||
|
+
|
||
|
/*
|
||
|
* The story of _OSI(Linux)
|
||
|
*
|
||
|
@@ -352,7 +357,7 @@ acpi_os_predefined_override(const struct
|
||
|
return AE_OK;
|
||
|
}
|
||
|
|
||
|
-acpi_status
|
||
|
+acpi_status __init
|
||
|
acpi_os_table_override(struct acpi_table_header * existing_table,
|
||
|
struct acpi_table_header ** new_table)
|
||
|
{
|
||
|
@@ -365,6 +370,18 @@ acpi_os_table_override(struct acpi_table
|
||
|
if (strncmp(existing_table->signature, "DSDT", 4) == 0)
|
||
|
*new_table = (struct acpi_table_header *)AmlCode;
|
||
|
#endif
|
||
|
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
|
||
|
+ if ((strncmp(existing_table->signature, "DSDT", 4) == 0) &&
|
||
|
+ !acpi_no_initrd_override && acpi_gbl_permanent_mmap) {
|
||
|
+ /* JDM: acpi_gbl_permanent_mmap means acpi_early_init() has
|
||
|
+ * been called so things like kmalloc are ok. */
|
||
|
+ 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",
|
||
|
@@ -375,6 +392,15 @@ acpi_os_table_override(struct acpi_table
|
||
|
return AE_OK;
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
|
||
|
+static 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;
|
||
|
--- a/init/initramfs.c
|
||
|
+++ b/init/initramfs.c
|
||
|
@@ -8,6 +8,9 @@
|
||
|
#include <linux/dirent.h>
|
||
|
#include <linux/syscalls.h>
|
||
|
#include <linux/utime.h>
|
||
|
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
|
||
|
+#include <acpi/acpi.h>
|
||
|
+#endif
|
||
|
|
||
|
static __initdata char *message;
|
||
|
static void __init error(char *x)
|
||
|
@@ -125,6 +128,12 @@ static __initdata unsigned long body_len
|
||
|
static __initdata uid_t uid;
|
||
|
static __initdata gid_t gid;
|
||
|
static __initdata unsigned rdev;
|
||
|
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
|
||
|
+static __initdata char *file_looked_for;
|
||
|
+static __initdata struct acpi_table_header *file_mem;
|
||
|
+#else
|
||
|
+const char *file_looked_for = NULL;
|
||
|
+#endif
|
||
|
|
||
|
static void __init parse_header(char *s)
|
||
|
{
|
||
|
@@ -159,6 +168,7 @@ static __initdata enum state {
|
||
|
SkipIt,
|
||
|
GotName,
|
||
|
CopyFile,
|
||
|
+ CopyFileMem,
|
||
|
GotSymlink,
|
||
|
Reset
|
||
|
} state, next_state;
|
||
|
@@ -228,6 +238,11 @@ static int __init do_header(void)
|
||
|
parse_header(collected);
|
||
|
next_header = this_header + N_ALIGN(name_len) + body_len;
|
||
|
next_header = (next_header + 3) & ~3;
|
||
|
+ if (file_looked_for) {
|
||
|
+ read_into(name_buf, N_ALIGN(name_len), GotName);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
state = SkipIt;
|
||
|
if (name_len <= 0 || name_len > PATH_MAX)
|
||
|
return 0;
|
||
|
@@ -298,6 +313,12 @@ static int __init do_name(void)
|
||
|
free_hash();
|
||
|
return 0;
|
||
|
}
|
||
|
+ if (file_looked_for) {
|
||
|
+ if (S_ISREG(mode) &&
|
||
|
+ (strcmp(collected, file_looked_for) == 0))
|
||
|
+ state = CopyFileMem;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
clean_path(collected, mode);
|
||
|
if (S_ISREG(mode)) {
|
||
|
int ml = maybe_link();
|
||
|
@@ -333,6 +354,40 @@ static int __init do_name(void)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
|
||
|
+static int __init do_copy_mem(void)
|
||
|
+{
|
||
|
+ static void *file_current; /* current position in the memory */
|
||
|
+ if (file_mem == NULL) {
|
||
|
+ if (body_len < 4) { /* check especially against empty files */
|
||
|
+ error("file is less than 4 bytes");
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+ file_mem = kmalloc(body_len, GFP_ATOMIC);
|
||
|
+ if (!file_mem) {
|
||
|
+ error("failed to allocate enough memory");
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+ file_current = file_mem;
|
||
|
+ }
|
||
|
+ if (count >= body_len) {
|
||
|
+ memcpy(file_current, victim, body_len);
|
||
|
+ eat(body_len);
|
||
|
+ file_looked_for = NULL; /* don't find files with same name */
|
||
|
+ state = SkipIt;
|
||
|
+ return 0;
|
||
|
+ } else {
|
||
|
+ memcpy(file_current, victim, count);
|
||
|
+ file_current += count;
|
||
|
+ body_len -= count;
|
||
|
+ eat(count);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+}
|
||
|
+#else
|
||
|
+#define do_copy_mem NULL
|
||
|
+#endif
|
||
|
+
|
||
|
static int __init do_copy(void)
|
||
|
{
|
||
|
if (count >= body_len) {
|
||
|
@@ -370,6 +425,7 @@ static __initdata int (*actions[])(void)
|
||
|
[SkipIt] = do_skip,
|
||
|
[GotName] = do_name,
|
||
|
[CopyFile] = do_copy,
|
||
|
+ [CopyFileMem] = do_copy_mem,
|
||
|
[GotSymlink] = do_symlink,
|
||
|
[Reset] = do_reset,
|
||
|
};
|
||
|
@@ -606,3 +662,31 @@ static int __init populate_rootfs(void)
|
||
|
return 0;
|
||
|
}
|
||
|
rootfs_initcall(populate_rootfs);
|
||
|
+
|
||
|
+#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
|
||
|
+struct acpi_table_header * __init acpi_find_dsdt_initrd(void)
|
||
|
+{
|
||
|
+ char *err, *ramfs_dsdt_name = "DSDT.aml";
|
||
|
+
|
||
|
+ printk(KERN_INFO "ACPI: Checking initramfs for custom DSDT\n");
|
||
|
+ file_mem = NULL;
|
||
|
+ file_looked_for = ramfs_dsdt_name;
|
||
|
+ err = unpack_to_rootfs((char *)initrd_start,
|
||
|
+ initrd_end - initrd_start);
|
||
|
+ file_looked_for = NULL;
|
||
|
+
|
||
|
+ if (err) {
|
||
|
+ /*
|
||
|
+ * Even if reading the DSDT file was successful,
|
||
|
+ * we give up if the initramfs cannot be entirely read.
|
||
|
+ */
|
||
|
+ kfree(file_mem);
|
||
|
+ printk(KERN_ERR "ACPI: Aborted because %s.\n", err);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ if (file_mem)
|
||
|
+ printk(KERN_INFO "ACPI: Found DSDT in %s.\n", ramfs_dsdt_name);
|
||
|
+
|
||
|
+ return file_mem;
|
||
|
+}
|
||
|
+#endif
|