diff options
author | Markus Gaugusch <dsdt@gaugusch.at> | 2008-02-04 18:04:06 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-02-06 22:07:41 -0500 |
commit | 71fc47a9adf8ee89e5c96a47222915c5485ac437 (patch) | |
tree | a2eaefbb703dde933a9726eae7e6399761d40136 | |
parent | 488b5ec871191359b9b79262a3d48456dae7ea5f (diff) |
ACPI: basic initramfs DSDT override support
The basics of DSDT from initramfs. In case this option is selected,
populate_rootfs() is called a bit earlier to have the initramfs content
available during ACPI initialization.
This is a very similar path to the one available at
http://gaugusch.at/kernel.shtml but with some update in the
documentation, default set to No and the change of populate_rootfs() the
"Jeff Mahony way" (which avoids reading the initramfs twice).
Signed-off-by: Thomas Renninger <trenn@suse.de>
Signed-off-by: Eric Piel <eric.piel@tremplin-utc.net>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | Documentation/acpi/dsdt-initrd.txt | 99 | ||||
-rw-r--r-- | drivers/acpi/Kconfig | 17 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 73 | ||||
-rw-r--r-- | init/initramfs.c | 8 | ||||
-rw-r--r-- | init/main.c | 7 |
5 files changed, 199 insertions, 5 deletions
diff --git a/Documentation/acpi/dsdt-initrd.txt b/Documentation/acpi/dsdt-initrd.txt new file mode 100644 index 000000000000..736043359dfb --- /dev/null +++ b/Documentation/acpi/dsdt-initrd.txt | |||
@@ -0,0 +1,99 @@ | |||
1 | ACPI Custom DSDT read from initramfs | ||
2 | |||
3 | 2003 by Markus Gaugusch < dsdt at gaugusch dot at > | ||
4 | Special thanks go to Thomas Renninger from SuSE, who updated the patch for | ||
5 | 2.6.0 and later modified it to read inside initramfs | ||
6 | 2004 - 2008 maintained by Eric Piel < eric dot piel at tremplin-utc dot net > | ||
7 | |||
8 | This option is intended for people who would like to hack their DSDT and don't | ||
9 | want to recompile their kernel after every change. It can also be useful to | ||
10 | distros which offers pre-compiled kernels and want to allow their users to use | ||
11 | a modified DSDT. In the Kernel config, enable the initial RAM filesystem | ||
12 | support (in General Setup) and enable ACPI_CUSTOM_DSDT_INITRD at the ACPI | ||
13 | options (General Setup|ACPI Support|Read Custom DSDT from initramfs). | ||
14 | |||
15 | A custom DSDT (Differentiated System Description Table) is useful when your | ||
16 | computer uses ACPI but problems occur due to broken implementation. Typically, | ||
17 | your computer works but there are some troubles with the hardware detection or | ||
18 | the power management. You can check that troubles come from errors in the DSDT by | ||
19 | activating the ACPI debug option and reading the logs. This table is provided | ||
20 | by the BIOS, therefore it might be a good idea to check for BIOS update on your | ||
21 | vendor website before going any further. Errors are often caused by vendors | ||
22 | testing their hardware only with Windows or because there is code which is | ||
23 | executed only on a specific OS with a specific version and Linux hasn't been | ||
24 | considered during the development. | ||
25 | |||
26 | Before you run away from customising your DSDT, you should note that already | ||
27 | corrected tables are available for a fair amount of computers on this web-page: | ||
28 | http://acpi.sf.net/dsdt . Be careful though, to work correctly a DSDT has to | ||
29 | match closely the hardware, including the amount of RAM, the frequency of the | ||
30 | processor and the PCI cards present! If you are part of the unluckies who | ||
31 | cannot find their hardware in this database, you can modify your DSDT by | ||
32 | yourself. This process is less painful than it sounds. Download the Intel ASL | ||
33 | compiler/decompiler at http://www.intel.com/technology/IAPC/acpi/downloads.htm . | ||
34 | As root, you then have to dump your DSDT and decompile it. By using the | ||
35 | compiler messages as well as the kernel ACPI debug messages and the reference | ||
36 | book (available at the Intel website and also at http://www.acpi.info), it is | ||
37 | quite easy to obtain a fully working table. | ||
38 | |||
39 | Once your new DSDT is ready you'll have to add it to an initramfs so that the | ||
40 | kernel can read the table at the very beginning of the boot. As the file has to | ||
41 | be accessed very early during the boot process the initramfs has to be an | ||
42 | initramfs. The file is contained into the initramfs under the name /DSDT.aml . | ||
43 | To obtain such an initramfs, you might have to modify your initramfs script or | ||
44 | you can add it later to the initramfs with the script appended to this | ||
45 | document. The command will look like: | ||
46 | initramfs-add-dsdt initramfs.img my-dsdt.aml | ||
47 | |||
48 | In case you don't use any initramfs, the possibilities you have are to either | ||
49 | start using one (try mkinitrd or yaird), or use the "Include Custom DSDT" | ||
50 | configure option to directly include your DSDT inside the kernel. | ||
51 | |||
52 | The message "Looking for DSDT in initramfs..." will tell you if the DSDT was | ||
53 | found or not. If you need to update your DSDT, generate a new initramfs and | ||
54 | perform the steps above. Don't forget that with Lilo, you'll have to re-run it. | ||
55 | |||
56 | |||
57 | ====================== Here starts initramfs-add-dsdt ========================== | ||
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" | ||
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ccf6ea95f68c..0442ae153a24 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -274,6 +274,23 @@ config ACPI_CUSTOM_DSDT_FILE | |||
274 | Enter the full path name to the file which includes the AmlCode | 274 | Enter the full path name to the file which includes the AmlCode |
275 | declaration. | 275 | declaration. |
276 | 276 | ||
277 | config ACPI_CUSTOM_DSDT_INITRD | ||
278 | bool "Read Custom DSDT from initramfs" | ||
279 | depends on BLK_DEV_INITRD | ||
280 | default n | ||
281 | help | ||
282 | The DSDT (Differentiated System Description Table) often needs to be | ||
283 | overridden because of broken BIOS implementations. If this feature is | ||
284 | activated you will be able to provide a customized DSDT by adding it | ||
285 | to your initramfs. If your mkinitrd tool does not support this feature | ||
286 | a script is provided in the documentation. For more details see | ||
287 | <file:Documentation/dsdt-initrd.txt> or <http://gaugusch.at/kernel.shtml>. | ||
288 | If there is no table found, it will fall-back to the custom DSDT | ||
289 | in-kernel (if activated) or to the DSDT from the BIOS. | ||
290 | |||
291 | Even if you do not need a new one at the moment, you may want to use a | ||
292 | better DSDT later. It is safe to say Y here. | ||
293 | |||
277 | config ACPI_BLACKLIST_YEAR | 294 | config ACPI_BLACKLIST_YEAR |
278 | int "Disable ACPI for systems before Jan 1st this year" if X86_32 | 295 | int "Disable ACPI for systems before Jan 1st this year" if X86_32 |
279 | default 0 | 296 | default 0 |
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index e53fb516f9d4..131936e7ff17 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -312,6 +312,66 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val, | |||
312 | return AE_OK; | 312 | return AE_OK; |
313 | } | 313 | } |
314 | 314 | ||
315 | #ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD | ||
316 | struct acpi_table_header *acpi_find_dsdt_initrd(void) | ||
317 | { | ||
318 | struct file *firmware_file; | ||
319 | mm_segment_t oldfs; | ||
320 | unsigned long len, len2; | ||
321 | struct acpi_table_header *dsdt_buffer, *ret = NULL; | ||
322 | struct kstat stat; | ||
323 | char *ramfs_dsdt_name = "/DSDT.aml"; | ||
324 | |||
325 | printk(KERN_INFO PREFIX "Looking for DSDT in initramfs... "); | ||
326 | |||
327 | /* | ||
328 | * Never do this at home, only the user-space is allowed to open a file. | ||
329 | * The clean way would be to use the firmware loader. But this code must be run | ||
330 | * before there is any userspace available. So we need a static/init firmware | ||
331 | * infrastructure, which doesn't exist yet... | ||
332 | */ | ||
333 | if (vfs_stat(ramfs_dsdt_name, &stat) < 0) { | ||
334 | printk("not found.\n"); | ||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | len = stat.size; | ||
339 | /* check especially against empty files */ | ||
340 | if (len <= 4) { | ||
341 | printk("error, file is too small: only %lu bytes.\n", len); | ||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | firmware_file = filp_open(ramfs_dsdt_name, O_RDONLY, 0); | ||
346 | if (IS_ERR(firmware_file)) { | ||
347 | printk("error, could not open file %s.\n", ramfs_dsdt_name); | ||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | dsdt_buffer = ACPI_ALLOCATE(len); | ||
352 | if (!dsdt_buffer) { | ||
353 | printk("error when allocating %lu bytes of memory.\n", len); | ||
354 | goto err; | ||
355 | } | ||
356 | |||
357 | oldfs = get_fs(); | ||
358 | set_fs(KERNEL_DS); | ||
359 | len2 = vfs_read(firmware_file, (char __user *)dsdt_buffer, len, &firmware_file->f_pos); | ||
360 | set_fs(oldfs); | ||
361 | if (len2 < len) { | ||
362 | printk("error trying to read %lu bytes from %s.\n", len, ramfs_dsdt_name); | ||
363 | ACPI_FREE(dsdt_buffer); | ||
364 | goto err; | ||
365 | } | ||
366 | |||
367 | printk("successfully read %lu bytes from %s.\n", len, ramfs_dsdt_name); | ||
368 | ret = dsdt_buffer; | ||
369 | err: | ||
370 | filp_close(firmware_file, NULL); | ||
371 | return ret; | ||
372 | } | ||
373 | #endif | ||
374 | |||
315 | acpi_status | 375 | acpi_status |
316 | acpi_os_table_override(struct acpi_table_header * existing_table, | 376 | acpi_os_table_override(struct acpi_table_header * existing_table, |
317 | struct acpi_table_header ** new_table) | 377 | struct acpi_table_header ** new_table) |
@@ -319,13 +379,18 @@ acpi_os_table_override(struct acpi_table_header * existing_table, | |||
319 | if (!existing_table || !new_table) | 379 | if (!existing_table || !new_table) |
320 | return AE_BAD_PARAMETER; | 380 | return AE_BAD_PARAMETER; |
321 | 381 | ||
382 | *new_table = NULL; | ||
383 | |||
322 | #ifdef CONFIG_ACPI_CUSTOM_DSDT | 384 | #ifdef CONFIG_ACPI_CUSTOM_DSDT |
323 | if (strncmp(existing_table->signature, "DSDT", 4) == 0) | 385 | if (strncmp(existing_table->signature, "DSDT", 4) == 0) |
324 | *new_table = (struct acpi_table_header *)AmlCode; | 386 | *new_table = (struct acpi_table_header *)AmlCode; |
325 | else | 387 | #endif |
326 | *new_table = NULL; | 388 | #ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD |
327 | #else | 389 | if (strncmp(existing_table->signature, "DSDT", 4) == 0) { |
328 | *new_table = NULL; | 390 | struct acpi_table_header *initrd_table = acpi_find_dsdt_initrd(); |
391 | if (initrd_table) | ||
392 | *new_table = initrd_table; | ||
393 | } | ||
329 | #endif | 394 | #endif |
330 | return AE_OK; | 395 | return AE_OK; |
331 | } | 396 | } |
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 @@ skip: | |||
538 | initrd_end = 0; | 538 | initrd_end = 0; |
539 | } | 539 | } |
540 | 540 | ||
541 | static int __init populate_rootfs(void) | 541 | int __init populate_rootfs(void) |
542 | { | 542 | { |
543 | char *err = unpack_to_rootfs(__initramfs_start, | 543 | char *err = unpack_to_rootfs(__initramfs_start, |
544 | __initramfs_end - __initramfs_start, 0); | 544 | __initramfs_end - __initramfs_start, 0); |
@@ -577,4 +577,10 @@ static int __init populate_rootfs(void) | |||
577 | } | 577 | } |
578 | return 0; | 578 | return 0; |
579 | } | 579 | } |
580 | #ifndef CONFIG_ACPI_CUSTOM_DSDT_INITRD | ||
581 | /* | ||
582 | * if this option is enabled, populate_rootfs() is called _earlier_ in the | ||
583 | * boot sequence. This insures that the ACPI initialisation can find the file. | ||
584 | */ | ||
580 | rootfs_initcall(populate_rootfs); | 585 | rootfs_initcall(populate_rootfs); |
586 | #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) { } | |||
102 | extern void tc_init(void); | 102 | extern void tc_init(void); |
103 | #endif | 103 | #endif |
104 | 104 | ||
105 | #ifdef CONFIG_ACPI_CUSTOM_DSDT_INITRD | ||
106 | extern int populate_rootfs(void); | ||
107 | #else | ||
108 | static inline void populate_rootfs(void) {} | ||
109 | #endif | ||
110 | |||
105 | enum system_states system_state; | 111 | enum system_states system_state; |
106 | EXPORT_SYMBOL(system_state); | 112 | EXPORT_SYMBOL(system_state); |
107 | 113 | ||
@@ -648,6 +654,7 @@ asmlinkage void __init start_kernel(void) | |||
648 | 654 | ||
649 | check_bugs(); | 655 | check_bugs(); |
650 | 656 | ||
657 | populate_rootfs(); /* For DSDT override from initramfs */ | ||
651 | acpi_early_init(); /* before LAPIC and SMP init */ | 658 | acpi_early_init(); /* before LAPIC and SMP init */ |
652 | 659 | ||
653 | /* Do the rest non-__init'ed, we're now alive */ | 660 | /* Do the rest non-__init'ed, we're now alive */ |