lkml.org 
[lkml]   [1999]   [Dec]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: APM poweroff
On Sat, 25 Dec 1999, James Mulcahy wrote:

> I'm using a 2.2.13 kernel, compiled with the poweroff on shutdown option.
> But when I execute halt -p (or poweroff) the kernel panics at the point
> where I expect it to powerdown. I copied the oops message by hand and
> then re-typed it once I had rebooted. I've included the ksymoops output,
> but I'm not sure where to go from here, is this a known problem, should I
> be able to find the problem using the ksymoops output ?

I have attached my patch. It will hopefully solve your problem (please
report success or failure). You need to turn on the
CONFIG_APM_REAL_MODE_POWER_OFF option.

> ps. My processor is a MAD k6-2 400, and I'm fairly sure the apm code in
> the bios isn't faulty, because the machine switches itself off when it
> gets shutdown from windows...

Award BIOS? It is most certainly the BIOS' fault.

> EIP: 0050:[<0000885>]

0050 means it is BIOS code that faulted.

Walter
diff -u -r linux-2.2.12-int/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.2.12-int/Documentation/Configure.help Thu Aug 26 19:40:29 1999
+++ linux/Documentation/Configure.help Sun Aug 29 17:14:27 1999
@@ -9078,7 +9078,14 @@
anonymous) contain support for this ("halt -p" shuts down Linux and
powers off the computer, if executed from runlevel 0). As with the
other APM options, this option may not work reliably with some APM
- BIOS implementations.
+ BIOS implementations. You should try to switch on the next option if
+ your computer crashes in this case.
+
+Use real mode APM BIOS call to power off
+CONFIG_APM_REAL_MODE_POWER_OFF
+ Use real mode APM BIOS calls to switch off the computer. This is
+ a work-around for a number of buggy BIOSes. Switch this option on if
+ your computer crashes instead of powering off properly.

Ignore multiple suspend/standby events
CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
diff -u -r linux-2.2.12-int/arch/i386/config.in linux/arch/i386/config.in
--- linux-2.2.12-int/arch/i386/config.in Thu Aug 26 19:40:29 1999
+++ linux/arch/i386/config.in Sun Aug 29 17:14:27 1999
@@ -110,6 +110,9 @@
bool ' Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE
bool ' Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
bool ' Power off on shutdown' CONFIG_APM_POWER_OFF
+ if [ "$CONFIG_APM_POWER_OFF" = "y" ]; then
+ bool ' Use real mode APM BIOS call to power off' CONFIG_APM_REAL_MODE_POWER_OFF
+ fi
bool ' Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
bool ' Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE
bool ' RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT
diff -u -r linux-2.2.12-int/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- linux-2.2.12-int/arch/i386/kernel/apm.c Sun Aug 29 16:23:35 1999
+++ linux/arch/i386/kernel/apm.c Sun Aug 29 17:23:01 1999
@@ -128,6 +128,7 @@
#include <linux/miscdevice.h>
#include <linux/apm_bios.h>
#include <linux/init.h>
+#include <linux/reboot.h>

#include <asm/system.h>
#include <asm/uaccess.h>
@@ -554,7 +555,11 @@
* kernel option.
*/
if (apm_enabled || (smp_hack == 2))
+#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
+ apm_real_mode_power_off();
+#else
(void) apm_set_power_state(APM_STATE_OFF);
+#endif /* CONFIG_APM_REAL_MODE_POWER_OFF */
}

#ifdef CONFIG_APM_DISPLAY_BLANK
diff -u -r linux-2.2.12-int/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- linux-2.2.12-int/arch/i386/kernel/process.c Sun Aug 29 16:23:35 1999
+++ linux/arch/i386/kernel/process.c Sun Aug 29 17:24:22 1999
@@ -2,6 +2,10 @@
* linux/arch/i386/kernel/process.c
*
* Copyright (C) 1995 Linus Torvalds
+ *
+ * Change back to real mode for APM power off
+ * Walter Hofmann, August 1999
+ *
*/

/*
@@ -383,6 +387,104 @@
void machine_halt(void)
{
}
+
+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) \
+ && defined(CONFIG_APM_REAL_MODE_POWER_OFF)
+
+/* This is 16-bit protected mode code to disable paging and the cache,
+ switch to real mode and jump to the BIOS APM power off code. */
+
+unsigned char apm_real_mode_power_off_code [] =
+{
+ 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
+ 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */
+ 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
+ 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
+ 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */
+ 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */
+ 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */
+ 0x74, 0x02, /* jz f */
+ 0x0f, 0x08, /* invd */
+ 0x24, 0x10, /* f: andb $0x10,al */
+ 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
+ 0xea, 0xed, 0x0f, 0x00, 0x00, /* ljmp g */
+ 0xb8, 0x00, 0x10, /* g: movw $0x1000,ax */
+ 0x8e, 0xd0, /* movw ax,ss */
+ 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */
+ 0xb8, 0x07, 0x53, /* movw $0x5307,ax */
+ 0xbb, 0x01, 0x00, /* movw $0x0001,bx */
+ 0xb9, 0x03, 0x00, /* movw $0x0003,cx */
+ 0xcd, 0x15 /* int $0x15 */
+};
+
+void apm_real_mode_power_off(void)
+{
+ cli ();
+
+ /* Remap the kernel at virtual address zero, as well as offset zero
+ from the kernel segment. This assumes the kernel segment starts at
+ virtual address 0xc0000000. */
+
+ memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
+ sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
+
+ /* Make sure the first page is mapped to the start of physical memory.
+ It is normally not mapped, to trap kernel NULL pointer dereferences. */
+
+ pg0 [0] = 7;
+
+ /*
+ * Use `swapper_pg_dir' as our page directory. We bother with
+ * `SET_PAGE_DIR' because although might be rebooting, but if we change
+ * the way we set root page dir in the future, then we wont break a
+ * seldom used feature ;)
+ */
+
+ SET_PAGE_DIR(current,swapper_pg_dir);
+
+ /* For the switch to real mode, copy some code to low memory. It has
+ to be in the first 64k because it is running in 16-bit mode, and it
+ has to have the same physical and virtual address, because it turns
+ off paging. Copy it near the end of the first page, out of the way
+ of BIOS variables. */
+
+ memcpy ((void *) (0x1000 - sizeof (apm_real_mode_power_off_code)),
+ apm_real_mode_power_off_code, sizeof (apm_real_mode_power_off_code));
+
+ /* Set up the IDT for real mode. */
+
+ __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
+
+ /* Set up a GDT from which we can load segment descriptors for real
+ mode. The GDT is not used in real mode; it is just needed here to
+ prepare the descriptors. */
+
+ __asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt));
+
+ /* Load the data segment registers, and thus the descriptors ready for
+ real mode. The base address of each segment is 0x100, 16 times the
+ selector value being loaded here. This is so that the segment
+ registers don't have to be reloaded after switching to real mode:
+ the values are consistent for real mode operation already. */
+
+ __asm__ __volatile__ ("movw $0x0010,%%ax\n"
+ "\tmovw %%ax,%%ds\n"
+ "\tmovw %%ax,%%es\n"
+ "\tmovw %%ax,%%fs\n"
+ "\tmovw %%ax,%%gs\n"
+ "\tmovw %%ax,%%ss" : : : "eax");
+
+ /* Jump to the 16-bit code that we copied earlier. It disables paging
+ and the cache, switches to real mode, and jumps to the BIOS APM
+ entry point. */
+
+ __asm__ __volatile__ ("ljmp $0x0008,%0"
+ :
+ : "i" ((void *) (0x1000 - sizeof
+ (apm_real_mode_power_off_code))));
+
+}
+#endif

void machine_power_off(void)
{
diff -u -r linux-2.2.12-int/include/linux/reboot.h linux/include/linux/reboot.h
--- linux-2.2.12-int/include/linux/reboot.h Sun Aug 29 16:23:35 1999
+++ linux/include/linux/reboot.h Sun Aug 29 17:34:46 1999
@@ -47,6 +47,11 @@
extern void machine_halt(void);
extern void machine_power_off(void);

+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) \
+ && defined(CONFIG_APM_REAL_MODE_POWER_OFF)
+extern void apm_real_mode_power_off(void);
+#endif
+
#endif

#endif /* _LINUX_REBOOT_H */
\
 
 \ /
  Last update: 2005-03-22 13:55    [W:0.051 / U:0.356 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site