diff --git a/Documentation/acpi/dsdt-override.txt b/Documentation/acpi/dsdt-override.txt new file mode 100644 index 000000000000..5008f256a2db --- /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 000000000000..17ef6e838e14 --- /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 8ea41b6e6a85..0dcbd266b442 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 d141bdb3a7d4..7ef172c2a1d6 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 c2c585366fa6..27ccd68b8f46 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 18222f267bc4..9e01f376840a 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 d53fee8d8604..c0b1e0533d80 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -538,7 +538,7 @@ static void __init free_initrd(void) 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 c691f5f7fc27..2a78932f6c07 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 d9e90cfe3298..24af9f8bac99 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");