[PATCH] x86: Always relax segments
authorZachary Amsden <zach@vmware.com>
Fri, 6 Jan 2006 08:11:53 +0000 (00:11 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Fri, 6 Jan 2006 16:33:34 +0000 (08:33 -0800)
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>
arch/i386/kernel/apm.c

index 6c8e483..0d29811 100644 (file)
@@ -302,17 +302,6 @@ extern int (*console_blank_hook)(int);
 
 #include "apm.h"
 
-/*
- * Define to make all _set_limit calls use 64k limits.  The APM 1.1 BIOS is
- * supposed to provide limit information that it recognizes.  Many machines
- * do this correctly, but many others do not restrict themselves to their
- * claimed limit.  When this happens, they will cause a segmentation
- * violation in the kernel at boot time.  Most BIOS's, however, will
- * respect a 64k limit, so we use that.  If you want to be pedantic and
- * hold your BIOS to its claims, then undefine this.
- */
-#define APM_RELAX_SEGMENTS
-
 /*
  * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
  * This patched by Chad Miller <cmiller@surfsouth.com>, original code by
@@ -2312,9 +2301,20 @@ static int __init apm_init(void)
        set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
        _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
 
+       /*
+        * Set up the long jump entry point to the APM BIOS, which is called
+        * from inline assembly.
+        */
        apm_bios_entry.offset = apm_info.bios.offset;
        apm_bios_entry.segment = APM_CS;
 
+       /*
+        * The APM 1.1 BIOS is supposed to provide limit information that it
+        * recognizes.  Many machines do this correctly, but many others do
+        * not restrict themselves to their claimed limit.  When this happens,
+        * they will cause a segmentation violation in the kernel at boot time.
+        * Most BIOS's, however, will respect a 64k limit, so we use that.
+        */
        for (i = 0; i < NR_CPUS; i++) {
                struct desc_struct *gdt = get_cpu_gdt_table(i);
                if (!gdt)
@@ -2325,33 +2325,12 @@ static int __init apm_init(void)
                         __va((unsigned long)apm_info.bios.cseg_16 << 4));
                set_base(gdt[APM_DS >> 3],
                         __va((unsigned long)apm_info.bios.dseg << 4));
-#ifndef APM_RELAX_SEGMENTS
-               if (apm_info.bios.version == 0x100) {
-#endif
-                       /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
-                       _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
-                       /* For some unknown machine. */
-                       _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
-                       /* For the DEC Hinote Ultra CT475 (and others?) */
-                       _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
-#ifndef APM_RELAX_SEGMENTS
-               } else {
-                       _set_limit((char *)&gdt[APM_CS >> 3],
-                               (apm_info.bios.cseg_len - 1) & 0xffff);
-                       _set_limit((char *)&gdt[APM_CS_16 >> 3],
-                               (apm_info.bios.cseg_16_len - 1) & 0xffff);
-                       _set_limit((char *)&gdt[APM_DS >> 3],
-                               (apm_info.bios.dseg_len - 1) & 0xffff);
-                     /* workaround for broken BIOSes */
-                       if (apm_info.bios.cseg_len <= apm_info.bios.offset)
-                               _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 -1);
-                       if (apm_info.bios.dseg_len <= 0x40) { /* 0x40 * 4kB == 64kB */
-                               /* for the BIOS that assumes granularity = 1 */
-                               gdt[APM_DS >> 3].b |= 0x800000;
-                               printk(KERN_NOTICE "apm: we set the granularity of dseg.\n");
-                       }
-               }
-#endif
+               /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
+               _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
+               /* For some unknown machine. */
+               _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
+               /* For the DEC Hinote Ultra CT475 (and others?) */
+               _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
        }
 
        apm_proc = create_proc_info_entry("apm", 0, NULL, apm_get_info);