lkml.org 
[lkml]   [1999]   [Jun]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subjectupdated apm patch
-----BEGIN PGP SIGNED MESSAGE-----


I have updated my apm patch. It switches the processor to real mode before
it attempts to turn off the computer. This should solve the problem seen
by a number or people.

Walter Hofmann


Changes:
* moved call so that power off via magic SysReq works
* patch is against 2.2.9, but should work against any 2.2.x and 2.3.x

-----BEGIN PGP SIGNATURE-----
Version: PGPfreeware 5.0i for non-commercial use
Charset: noconv

iQCVAwUBN2L4BvzeA3/eVHOFAQFdDQP6Ammx0Nv4nCPBYFGTkxOCXtrfFM/ct65g
FUblS8k32X3iD7xPtovr8R9GWK3YAWJlyFi6gdw5IgqBV3wkbGWEIcIB0Ohy7aMQ
5FMGlAbj2/20vP3XeXYmOv4889E4hzkuUaEOuTjsEqk2u2Tpww1rCS6siVXLCu4j
aSiicq34QLc=
=Ntmq
-----END PGP SIGNATURE-----
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:52    [W:1.095 / U:0.376 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site