aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/apm.c
diff options
context:
space:
mode:
authorZachary Amsden <zach@vmware.com>2006-01-06 03:11:53 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-06 11:33:34 -0500
commit3012d2d209580c78b5927d55c60a10891be8befd (patch)
tree5da305197c9e117b9207395de57d5c0a0ed432c6 /arch/i386/kernel/apm.c
parent5702d0f742b2f462267bca147334f77a255bcc74 (diff)
[PATCH] x86: Always relax segments
APM BIOSes have many bugs regarding proper representation of the appropriate segment limits for calling the BIOS. By default, APM_RELAX_SEGMENTS is always turned on to support running the APM BIOS on these buggy machines. Keeping 64k limits poses very little danger to the kernel, because the pages where the APM BIOS is located will always be in low physical memory BIOS areas, which should already be marked reserved, and only buggy BIOSes would possibly overstep the segment bounds with writes to data anyway. Since forcing stricter limits breaks many machines and is not default behavior, it seems reasonable to deprecate the older code which may cause APM BIOS to fault. If you really have a badly enough broken APM BIOS that you have to turn off APM_RELAX_SEGMENTS, seems like the best recourse here would be to disable the APM BIOS and / or not compile it into your kernel to begin with, and / or add your system to the known bad list. The reason I want to deprecate this code is there is underlying brokenness with the set_limit macros, and getting rid of many of the call sites rather than rewriting them seems to be the simplest and most correct course of action. Signed-off-by: Zachary Amsden <zach@vmware.com> Acked-by: "Seth, Rohit" <rohit.seth@intel.com> Cc: Stephen Rothwell <sfr@canb.auug.org.au> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/apm.c')
-rw-r--r--arch/i386/kernel/apm.c55
1 files changed, 17 insertions, 38 deletions
diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c
index 6c8e483ce9e4..0d2981120cd6 100644
--- a/arch/i386/kernel/apm.c
+++ b/arch/i386/kernel/apm.c
@@ -303,17 +303,6 @@ extern int (*console_blank_hook)(int);
303#include "apm.h" 303#include "apm.h"
304 304
305/* 305/*
306 * Define to make all _set_limit calls use 64k limits. The APM 1.1 BIOS is
307 * supposed to provide limit information that it recognizes. Many machines
308 * do this correctly, but many others do not restrict themselves to their
309 * claimed limit. When this happens, they will cause a segmentation
310 * violation in the kernel at boot time. Most BIOS's, however, will
311 * respect a 64k limit, so we use that. If you want to be pedantic and
312 * hold your BIOS to its claims, then undefine this.
313 */
314#define APM_RELAX_SEGMENTS
315
316/*
317 * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend. 306 * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
318 * This patched by Chad Miller <cmiller@surfsouth.com>, original code by 307 * This patched by Chad Miller <cmiller@surfsouth.com>, original code by
319 * David Chen <chen@ctpa04.mit.edu> 308 * David Chen <chen@ctpa04.mit.edu>
@@ -2312,9 +2301,20 @@ static int __init apm_init(void)
2312 set_base(bad_bios_desc, __va((unsigned long)0x40 << 4)); 2301 set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
2313 _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4)); 2302 _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
2314 2303
2304 /*
2305 * Set up the long jump entry point to the APM BIOS, which is called
2306 * from inline assembly.
2307 */
2315 apm_bios_entry.offset = apm_info.bios.offset; 2308 apm_bios_entry.offset = apm_info.bios.offset;
2316 apm_bios_entry.segment = APM_CS; 2309 apm_bios_entry.segment = APM_CS;
2317 2310
2311 /*
2312 * The APM 1.1 BIOS is supposed to provide limit information that it
2313 * recognizes. Many machines do this correctly, but many others do
2314 * not restrict themselves to their claimed limit. When this happens,
2315 * they will cause a segmentation violation in the kernel at boot time.
2316 * Most BIOS's, however, will respect a 64k limit, so we use that.
2317 */
2318 for (i = 0; i < NR_CPUS; i++) { 2318 for (i = 0; i < NR_CPUS; i++) {
2319 struct desc_struct *gdt = get_cpu_gdt_table(i); 2319 struct desc_struct *gdt = get_cpu_gdt_table(i);
2320 if (!gdt) 2320 if (!gdt)
@@ -2325,33 +2325,12 @@ static int __init apm_init(void)
2325 __va((unsigned long)apm_info.bios.cseg_16 << 4)); 2325 __va((unsigned long)apm_info.bios.cseg_16 << 4));
2326 set_base(gdt[APM_DS >> 3], 2326 set_base(gdt[APM_DS >> 3],
2327 __va((unsigned long)apm_info.bios.dseg << 4)); 2327 __va((unsigned long)apm_info.bios.dseg << 4));
2328#ifndef APM_RELAX_SEGMENTS 2328 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
2329 if (apm_info.bios.version == 0x100) { 2329 _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
2330#endif 2330 /* For some unknown machine. */
2331 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ 2331 _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
2332 _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1); 2332 /* For the DEC Hinote Ultra CT475 (and others?) */
2333 /* For some unknown machine. */ 2333 _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
2334 _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
2335 /* For the DEC Hinote Ultra CT475 (and others?) */
2336 _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
2337#ifndef APM_RELAX_SEGMENTS
2338 } else {
2339 _set_limit((char *)&gdt[APM_CS >> 3],
2340 (apm_info.bios.cseg_len - 1) & 0xffff);
2341 _set_limit((char *)&gdt[APM_CS_16 >> 3],
2342 (apm_info.bios.cseg_16_len - 1) & 0xffff);
2343 _set_limit((char *)&gdt[APM_DS >> 3],
2344 (apm_info.bios.dseg_len - 1) & 0xffff);
2345 /* workaround for broken BIOSes */
2346 if (apm_info.bios.cseg_len <= apm_info.bios.offset)
2347 _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 -1);
2348 if (apm_info.bios.dseg_len <= 0x40) { /* 0x40 * 4kB == 64kB */
2349 /* for the BIOS that assumes granularity = 1 */
2350 gdt[APM_DS >> 3].b |= 0x800000;
2351 printk(KERN_NOTICE "apm: we set the granularity of dseg.\n");
2352 }
2353 }
2354#endif
2355 } 2334 }
2356 2335
2357 apm_proc = create_proc_info_entry("apm", 0, NULL, apm_get_info); 2336 apm_proc = create_proc_info_entry("apm", 0, NULL, apm_get_info);