1 From: Eric Piel <piel@localhost.(none)>
2 Subject: [PATCH 1/1] ACPI: initramfs DSDT override support
3 Patch-mainline: not yet
6 Permits to load of DSDT (the main ACPI table) from initramfs. In case this
7 option is selected, the initramfs is parsed at ACPI initialization (very early
8 boot time) to look for a file DSDT.aml . This aims at allowing users to
9 override the DSDT without recompiling the kernel. This is done by adding a new
10 feature to the initramfs parser so that one specific file can be directly
13 This is derived from the patch v0.8 from http://gaugusch.at/kernel.shtml but
14 with kernel inclusion in mind: some clean-up's in the documentation, default
15 set to No, a kernel parameter to disable it at runtime, and most important, a
16 different approach for reading the initramfs which avoids using the filesystem
19 It also contains a fix for compilation on non-ACPI platforms provided by Rene Rebe.
21 Signed-off-by: Eric Piel <eric.piel@tremplin-utc.net>
22 Signed-off-by: Thomas Renninger <trenn@suse.de>
23 Signed-off-by: Len Brown <len.brown@intel.com>
25 Documentation/acpi/dsdt-override.txt | 12 ++++
26 Documentation/acpi/initramfs-add-dsdt.sh | 43 +++++++++++++++++
27 Documentation/kernel-parameters.txt | 3 +
28 drivers/acpi/Kconfig | 11 ++++
29 drivers/acpi/osl.c | 26 ++++++++++
30 drivers/acpi/tables/tbxface.c | 7 --
31 init/initramfs.c | 76 +++++++++++++++++++++++++++++++
32 7 files changed, 170 insertions(+), 8 deletions(-)
33 create mode 100644 Documentation/acpi/initramfs-add-dsdt.sh
35 --- a/Documentation/acpi/dsdt-override.txt
36 +++ b/Documentation/acpi/dsdt-override.txt
38 -Linux supports a method of overriding the BIOS DSDT:
39 +Linux supports two methods of overriding the BIOS DSDT:
41 CONFIG_ACPI_CUSTOM_DSDT builds the image into the kernel.
43 -When to use this method is described in detail on the
44 +CONFIG_ACPI_CUSTOM_DSDT_INITRD adds the image to the initrd.
46 +When to use these methods is described in detail on the
48 http://www.lesswatts.org/projects/acpi/overridingDSDT.php
50 +Note that if both options are used, the DSDT supplied
51 +by the INITRD method takes precedence.
53 +Documentation/initramfs-add-dsdt.sh is provided for convenience
54 +for use with the CONFIG_ACPI_CUSTOM_DSDT_INITRD method.
56 +++ b/Documentation/acpi/initramfs-add-dsdt.sh
59 +# Adds a DSDT file to the initrd (if it's an initramfs)
60 +# first argument is the name of archive
61 +# second argument is the name of the file to add
62 +# The file will be copied as /DSDT.aml
64 +# 20060126: fix "Premature end of file" with some old cpio (Roland Robic)
65 +# 20060205: this time it should really work
67 +# check the arguments
68 +if [ $# -ne 2 ]; then
69 + program_name=$(basename $0)
71 +$program_name: too few arguments
72 +Usage: $program_name initrd-name.img DSDT-to-add.aml
73 +Adds a DSDT file to an initrd (in initramfs format)
75 + initrd-name.img: filename of the initrd in initramfs format
76 + DSDT-to-add.aml: filename of the DSDT file to add
81 +# we should check it's an initramfs
83 +tempcpio=$(mktemp -d)
84 +# cleanup on exit, hangup, interrupt, quit, termination
85 +trap 'rm -rf $tempcpio' 0 1 2 3 15
87 +# extract the archive
88 +gunzip -c "$1" > "$tempcpio"/initramfs.cpio || exit 1
90 +# copy the DSDT file at the root of the directory so that we can call it "/DSDT.aml"
91 +cp -f "$2" "$tempcpio"/DSDT.aml
95 +(echo DSDT.aml | cpio --quiet -H newc -o -A -O "$tempcpio"/initramfs.cpio) || exit 1
98 +# re-compress the archive
99 +gzip -c "$tempcpio"/initramfs.cpio > "$1"
101 --- a/Documentation/kernel-parameters.txt
102 +++ b/Documentation/kernel-parameters.txt
103 @@ -179,6 +179,9 @@ and is between 256 and 4096 characters.
105 acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT
107 + acpi_no_initrd_override [KNL,ACPI]
108 + Disable loading custom ACPI tables from the initramfs
110 acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
111 Format: To spoof as Windows 98: ="Microsoft Windows"
113 --- a/drivers/acpi/Kconfig
114 +++ b/drivers/acpi/Kconfig
115 @@ -301,6 +301,17 @@ config ACPI_CUSTOM_DSDT
117 default ACPI_CUSTOM_DSDT_FILE != ""
119 +config ACPI_CUSTOM_DSDT_INITRD
120 + bool "Read Custom DSDT from initramfs"
121 + depends on BLK_DEV_INITRD
124 + This option supports a custom DSDT by optionally loading it from initrd.
125 + See Documentation/acpi/dsdt-override.txt
127 + If you are not using this feature now, but may use it later,
128 + it is safe to say Y here.
130 config ACPI_BLACKLIST_YEAR
131 int "Disable ACPI for systems before Jan 1st this year" if X86_32
133 --- a/drivers/acpi/osl.c
134 +++ b/drivers/acpi/osl.c
135 @@ -96,6 +96,11 @@ static DEFINE_SPINLOCK(acpi_res_lock);
136 #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */
137 static char osi_additional_string[OSI_STRING_LENGTH_MAX];
139 +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
140 +static int acpi_no_initrd_override;
141 +extern struct acpi_table_header *acpi_find_dsdt_initrd(void);
145 * "Ode to _OSI(Linux)"
147 @@ -325,7 +330,7 @@ acpi_os_predefined_override(const struct
153 acpi_os_table_override(struct acpi_table_header * existing_table,
154 struct acpi_table_header ** new_table)
156 @@ -338,6 +343,16 @@ acpi_os_table_override(struct acpi_table
157 if (strncmp(existing_table->signature, "DSDT", 4) == 0)
158 *new_table = (struct acpi_table_header *)AmlCode;
160 +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
161 + if ((strncmp(existing_table->signature, "DSDT", 4) == 0) &&
162 + !acpi_no_initrd_override) {
163 + struct acpi_table_header *initrd_table;
165 + initrd_table = acpi_find_dsdt_initrd();
167 + *new_table = initrd_table;
170 if (*new_table != NULL) {
171 printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], "
172 "this is unsafe: tainting kernel\n",
173 @@ -348,6 +363,15 @@ acpi_os_table_override(struct acpi_table
177 +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
178 +static int __init acpi_no_initrd_override_setup(char *s)
180 + acpi_no_initrd_override = 1;
183 +__setup("acpi_no_initrd_override", acpi_no_initrd_override_setup);
186 static irqreturn_t acpi_irq(int irq, void *dev_id)
189 --- a/drivers/acpi/tables/tbxface.c
190 +++ b/drivers/acpi/tables/tbxface.c
191 @@ -487,7 +487,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_by_ind
194 ******************************************************************************/
195 -static acpi_status acpi_tb_load_namespace(void)
196 +static acpi_status __init acpi_tb_load_namespace(void)
199 struct acpi_table_header *table;
200 @@ -612,7 +612,7 @@ static acpi_status acpi_tb_load_namespac
202 ******************************************************************************/
204 -acpi_status acpi_load_tables(void)
205 +acpi_status __init acpi_load_tables(void)
209 @@ -630,9 +630,6 @@ acpi_status acpi_load_tables(void)
210 return_ACPI_STATUS(status);
213 -ACPI_EXPORT_SYMBOL(acpi_load_tables)
216 /*******************************************************************************
218 * FUNCTION: acpi_install_table_handler
219 --- a/init/initramfs.c
220 +++ b/init/initramfs.c
222 #include <linux/delay.h>
223 #include <linux/string.h>
224 #include <linux/syscalls.h>
225 +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
226 +#include <acpi/acpi.h>
229 static __initdata char *message;
230 static void __init error(char *x)
231 @@ -80,6 +83,12 @@ static __initdata unsigned long body_len
232 static __initdata uid_t uid;
233 static __initdata gid_t gid;
234 static __initdata unsigned rdev;
235 +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
236 +static __initdata char *file_looked_for;
237 +static __initdata struct acpi_table_header *file_mem;
239 +const char *file_looked_for = NULL;
242 static void __init parse_header(char *s)
244 @@ -113,6 +122,7 @@ static __initdata enum state {
252 @@ -257,6 +267,9 @@ static int __init do_name(void)
256 + if (file_looked_for && S_ISREG(mode) &&
257 + (strcmp(collected, file_looked_for) == 0))
258 + state = CopyFileMem;
261 clean_path(collected, mode);
262 @@ -289,6 +302,40 @@ static int __init do_name(void)
266 +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
267 +static int __init do_copy_mem(void)
269 + static void *file_current; /* current position in the memory */
270 + if (file_mem == NULL) {
271 + if (body_len < 4) { /* check especially against empty files */
272 + error("file is less than 4 bytes");
275 + file_mem = kmalloc(body_len, GFP_ATOMIC);
277 + error("failed to allocate enough memory");
280 + file_current = file_mem;
282 + if (count >= body_len) {
283 + memcpy(file_current, victim, body_len);
285 + file_looked_for = NULL; /* don't find files with same name */
289 + memcpy(file_current, victim, count);
290 + file_current += count;
297 +#define do_copy_mem NULL
300 static int __init do_copy(void)
302 if (count >= body_len) {
303 @@ -323,6 +370,7 @@ static __initdata int (*actions[])(void)
306 [CopyFile] = do_copy,
307 + [CopyFileMem] = do_copy_mem,
308 [GotSymlink] = do_symlink,
311 @@ -560,3 +608,31 @@ static int __init populate_rootfs(void)
314 rootfs_initcall(populate_rootfs);
316 +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD
317 +struct acpi_table_header * __init acpi_find_dsdt_initrd(void)
319 + char *err, *ramfs_dsdt_name = "DSDT.aml";
321 + printk(KERN_INFO "ACPI: Checking initramfs for custom DSDT\n");
323 + file_looked_for = ramfs_dsdt_name;
324 + err = unpack_to_rootfs((char *)initrd_start,
325 + initrd_end - initrd_start, 1);
326 + file_looked_for = NULL;
330 + * Even if reading the DSDT file was successful,
331 + * we give up if the initramfs cannot be entirely read.
334 + printk(KERN_ERR "ACPI: Aborded because %s.\n", err);
338 + printk(KERN_INFO "ACPI: Found DSDT in %s.\n", ramfs_dsdt_name);