lkml.org 
[lkml]   [1999]   [Aug]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectPATCH: real mode POWER OFF with buggy BIOS
Hi,

the patch i get from Walter Hofmann is really great :-) First run, first success ! I patched 2.2.12 and everything works as expected, my (buggy) BIOS switch the box off, really crazy...

One improvement could be a option within config/menuconfig to switch the realmode-poweroff workarround on/off.

This mail contains the patch as attachment so please merge this stuff with the new linux release !

--
Raphal Bossek <raphael.bossek@solutions4linux.de> [ICQ #40047651]
PGP fingerprint: DF 6F 2C 76 46 1E B4 1C 5B A5 2E 1B FC E0 D3 F6
PGP public key: http://www.solutions4linux.de/private/pgpkey.asc

diff -u -r linux-2.2.9-isdn.vanilla/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- linux-2.2.9-isdn.vanilla/arch/i386/kernel/apm.c Fri Jan 15 07:57:25 1999
+++ linux/arch/i386/kernel/apm.c Sat Jun 12 23:45:05 1999
@@ -554,7 +554,7 @@
* kernel option.
*/
if (apm_enabled || (smp_hack == 2))
- (void) apm_set_power_state(APM_STATE_OFF);
+ apm_realmode_poweroff();
}

#ifdef CONFIG_APM_DISPLAY_BLANK
diff -u -r linux-2.2.9-isdn.vanilla/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- linux-2.2.9-isdn.vanilla/arch/i386/kernel/process.c Fri Apr 30 17:13:37 1999
+++ linux/arch/i386/kernel/process.c Sat Jun 12 23:45:16 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, March 1999
+ *
*/

/*
@@ -383,6 +387,102 @@
void machine_halt(void)
{
}
+
+#if defined(CONFIG_APM) && defined(CONFIG_APM_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 poweroff code. */
+
+unsigned char apm_real_mode_poweroff [] =
+{
+ 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_realmode_poweroff(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_poweroff)),
+ apm_real_mode_poweroff, sizeof (apm_real_mode_poweroff));
+
+ /* 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_poweroff))));
+
+}
+#endif

void machine_power_off(void)
{
diff -u -r linux-2.2.9-isdn.vanilla/include/linux/reboot.h linux/include/linux/reboot.h
--- linux-2.2.9-isdn.vanilla/include/linux/reboot.h Fri Aug 28 04:37:33 1998
+++ linux/include/linux/reboot.h Sat Jun 12 23:48:49 1999
@@ -47,6 +47,10 @@
extern void machine_halt(void);
extern void machine_power_off(void);

+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
+extern void apm_realmode_poweroff(void);
+#endif
+
#endif

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