]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Eric Piel <piel@localhost.(none)> |
2 | Subject: [PATCH 1/1] ACPI: initramfs DSDT override support | |
3 | Patch-mainline: not yet | |
4 | ||
5 | ||
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 | |
11 | copied into memory. | |
12 | ||
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 | |
17 | infrastructure. | |
18 | ||
19 | It also contains a fix for compilation on non-ACPI platforms provided by Rene Rebe. | |
20 | ||
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> | |
24 | --- | |
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 | |
34 | ||
35 | --- a/Documentation/acpi/dsdt-override.txt | |
36 | +++ b/Documentation/acpi/dsdt-override.txt | |
37 | @@ -1,7 +1,15 @@ | |
38 | -Linux supports a method of overriding the BIOS DSDT: | |
39 | +Linux supports two methods of overriding the BIOS DSDT: | |
40 | ||
41 | CONFIG_ACPI_CUSTOM_DSDT builds the image into the kernel. | |
42 | ||
43 | -When to use this method is described in detail on the | |
44 | +CONFIG_ACPI_CUSTOM_DSDT_INITRD adds the image to the initrd. | |
45 | + | |
46 | +When to use these methods is described in detail on the | |
47 | Linux/ACPI home page: | |
48 | http://www.lesswatts.org/projects/acpi/overridingDSDT.php | |
49 | + | |
50 | +Note that if both options are used, the DSDT supplied | |
51 | +by the INITRD method takes precedence. | |
52 | + | |
53 | +Documentation/initramfs-add-dsdt.sh is provided for convenience | |
54 | +for use with the CONFIG_ACPI_CUSTOM_DSDT_INITRD method. | |
55 | --- /dev/null | |
56 | +++ b/Documentation/acpi/initramfs-add-dsdt.sh | |
57 | @@ -0,0 +1,43 @@ | |
58 | +#!/bin/bash | |
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 | |
63 | + | |
64 | +# 20060126: fix "Premature end of file" with some old cpio (Roland Robic) | |
65 | +# 20060205: this time it should really work | |
66 | + | |
67 | +# check the arguments | |
68 | +if [ $# -ne 2 ]; then | |
69 | + program_name=$(basename $0) | |
70 | + echo "\ | |
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) | |
74 | + | |
75 | + initrd-name.img: filename of the initrd in initramfs format | |
76 | + DSDT-to-add.aml: filename of the DSDT file to add | |
77 | + " 1>&2 | |
78 | + exit 1 | |
79 | +fi | |
80 | + | |
81 | +# we should check it's an initramfs | |
82 | + | |
83 | +tempcpio=$(mktemp -d) | |
84 | +# cleanup on exit, hangup, interrupt, quit, termination | |
85 | +trap 'rm -rf $tempcpio' 0 1 2 3 15 | |
86 | + | |
87 | +# extract the archive | |
88 | +gunzip -c "$1" > "$tempcpio"/initramfs.cpio || exit 1 | |
89 | + | |
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 | |
92 | + | |
93 | +# add the file | |
94 | +cd "$tempcpio" | |
95 | +(echo DSDT.aml | cpio --quiet -H newc -o -A -O "$tempcpio"/initramfs.cpio) || exit 1 | |
96 | +cd "$OLDPWD" | |
97 | + | |
98 | +# re-compress the archive | |
99 | +gzip -c "$tempcpio"/initramfs.cpio > "$1" | |
100 | + | |
101 | --- a/Documentation/kernel-parameters.txt | |
102 | +++ b/Documentation/kernel-parameters.txt | |
103 | @@ -179,6 +179,9 @@ and is between 256 and 4096 characters. | |
104 | ||
105 | acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT | |
106 | ||
107 | + acpi_no_initrd_override [KNL,ACPI] | |
108 | + Disable loading custom ACPI tables from the initramfs | |
109 | + | |
110 | acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS | |
111 | Format: To spoof as Windows 98: ="Microsoft Windows" | |
112 | ||
113 | --- a/drivers/acpi/Kconfig | |
114 | +++ b/drivers/acpi/Kconfig | |
115 | @@ -301,6 +301,17 @@ config ACPI_CUSTOM_DSDT | |
116 | bool | |
117 | default ACPI_CUSTOM_DSDT_FILE != "" | |
118 | ||
119 | +config ACPI_CUSTOM_DSDT_INITRD | |
120 | + bool "Read Custom DSDT from initramfs" | |
121 | + depends on BLK_DEV_INITRD | |
122 | + default n | |
123 | + help | |
124 | + This option supports a custom DSDT by optionally loading it from initrd. | |
125 | + See Documentation/acpi/dsdt-override.txt | |
126 | + | |
127 | + If you are not using this feature now, but may use it later, | |
128 | + it is safe to say Y here. | |
129 | + | |
130 | config ACPI_BLACKLIST_YEAR | |
131 | int "Disable ACPI for systems before Jan 1st this year" if X86_32 | |
132 | default 0 | |
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]; | |
138 | ||
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); | |
142 | +#endif | |
143 | + | |
144 | /* | |
145 | * "Ode to _OSI(Linux)" | |
146 | * | |
147 | @@ -325,7 +330,7 @@ acpi_os_predefined_override(const struct | |
148 | return AE_OK; | |
149 | } | |
150 | ||
151 | -acpi_status | |
152 | +acpi_status __init | |
153 | acpi_os_table_override(struct acpi_table_header * existing_table, | |
154 | struct acpi_table_header ** new_table) | |
155 | { | |
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; | |
159 | #endif | |
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; | |
164 | + | |
165 | + initrd_table = acpi_find_dsdt_initrd(); | |
166 | + if (initrd_table) | |
167 | + *new_table = initrd_table; | |
168 | + } | |
169 | +#endif | |
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 | |
174 | return AE_OK; | |
175 | } | |
176 | ||
177 | +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD | |
178 | +static int __init acpi_no_initrd_override_setup(char *s) | |
179 | +{ | |
180 | + acpi_no_initrd_override = 1; | |
181 | + return 1; | |
182 | +} | |
183 | +__setup("acpi_no_initrd_override", acpi_no_initrd_override_setup); | |
184 | +#endif | |
185 | + | |
186 | static irqreturn_t acpi_irq(int irq, void *dev_id) | |
187 | { | |
188 | u32 handled; | |
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 | |
192 | * the RSDT/XSDT. | |
193 | * | |
194 | ******************************************************************************/ | |
195 | -static acpi_status acpi_tb_load_namespace(void) | |
196 | +static acpi_status __init acpi_tb_load_namespace(void) | |
197 | { | |
198 | acpi_status status; | |
199 | struct acpi_table_header *table; | |
200 | @@ -612,7 +612,7 @@ static acpi_status acpi_tb_load_namespac | |
201 | * | |
202 | ******************************************************************************/ | |
203 | ||
204 | -acpi_status acpi_load_tables(void) | |
205 | +acpi_status __init acpi_load_tables(void) | |
206 | { | |
207 | acpi_status status; | |
208 | ||
209 | @@ -630,9 +630,6 @@ acpi_status acpi_load_tables(void) | |
210 | return_ACPI_STATUS(status); | |
211 | } | |
212 | ||
213 | -ACPI_EXPORT_SYMBOL(acpi_load_tables) | |
214 | - | |
215 | - | |
216 | /******************************************************************************* | |
217 | * | |
218 | * FUNCTION: acpi_install_table_handler | |
219 | --- a/init/initramfs.c | |
220 | +++ b/init/initramfs.c | |
221 | @@ -6,6 +6,9 @@ | |
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> | |
227 | +#endif | |
228 | ||
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; | |
238 | +#else | |
239 | +const char *file_looked_for = NULL; | |
240 | +#endif | |
241 | ||
242 | static void __init parse_header(char *s) | |
243 | { | |
244 | @@ -113,6 +122,7 @@ static __initdata enum state { | |
245 | SkipIt, | |
246 | GotName, | |
247 | CopyFile, | |
248 | + CopyFileMem, | |
249 | GotSymlink, | |
250 | Reset | |
251 | } state, next_state; | |
252 | @@ -257,6 +267,9 @@ static int __init do_name(void) | |
253 | free_hash(); | |
254 | return 0; | |
255 | } | |
256 | + if (file_looked_for && S_ISREG(mode) && | |
257 | + (strcmp(collected, file_looked_for) == 0)) | |
258 | + state = CopyFileMem; | |
259 | if (dry_run) | |
260 | return 0; | |
261 | clean_path(collected, mode); | |
262 | @@ -289,6 +302,40 @@ static int __init do_name(void) | |
263 | return 0; | |
264 | } | |
265 | ||
266 | +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD | |
267 | +static int __init do_copy_mem(void) | |
268 | +{ | |
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"); | |
273 | + return 1; | |
274 | + } | |
275 | + file_mem = kmalloc(body_len, GFP_ATOMIC); | |
276 | + if (!file_mem) { | |
277 | + error("failed to allocate enough memory"); | |
278 | + return 1; | |
279 | + } | |
280 | + file_current = file_mem; | |
281 | + } | |
282 | + if (count >= body_len) { | |
283 | + memcpy(file_current, victim, body_len); | |
284 | + eat(body_len); | |
285 | + file_looked_for = NULL; /* don't find files with same name */ | |
286 | + state = SkipIt; | |
287 | + return 0; | |
288 | + } else { | |
289 | + memcpy(file_current, victim, count); | |
290 | + file_current += count; | |
291 | + body_len -= count; | |
292 | + eat(count); | |
293 | + return 1; | |
294 | + } | |
295 | +} | |
296 | +#else | |
297 | +#define do_copy_mem NULL | |
298 | +#endif | |
299 | + | |
300 | static int __init do_copy(void) | |
301 | { | |
302 | if (count >= body_len) { | |
303 | @@ -323,6 +370,7 @@ static __initdata int (*actions[])(void) | |
304 | [SkipIt] = do_skip, | |
305 | [GotName] = do_name, | |
306 | [CopyFile] = do_copy, | |
307 | + [CopyFileMem] = do_copy_mem, | |
308 | [GotSymlink] = do_symlink, | |
309 | [Reset] = do_reset, | |
310 | }; | |
311 | @@ -560,3 +608,31 @@ static int __init populate_rootfs(void) | |
312 | return 0; | |
313 | } | |
314 | rootfs_initcall(populate_rootfs); | |
315 | + | |
316 | +#ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD | |
317 | +struct acpi_table_header * __init acpi_find_dsdt_initrd(void) | |
318 | +{ | |
319 | + char *err, *ramfs_dsdt_name = "DSDT.aml"; | |
320 | + | |
321 | + printk(KERN_INFO "ACPI: Checking initramfs for custom DSDT\n"); | |
322 | + file_mem = NULL; | |
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; | |
327 | + | |
328 | + if (err) { | |
329 | + /* | |
330 | + * Even if reading the DSDT file was successful, | |
331 | + * we give up if the initramfs cannot be entirely read. | |
332 | + */ | |
333 | + kfree(file_mem); | |
334 | + printk(KERN_ERR "ACPI: Aborded because %s.\n", err); | |
335 | + return NULL; | |
336 | + } | |
337 | + if (file_mem) | |
338 | + printk(KERN_INFO "ACPI: Found DSDT in %s.\n", ramfs_dsdt_name); | |
339 | + | |
340 | + return file_mem; | |
341 | +} | |
342 | +#endif |