diff options
Diffstat (limited to 'drivers/acpi/osl.c')
-rw-r--r-- | drivers/acpi/osl.c | 97 |
1 files changed, 93 insertions, 4 deletions
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; |