lkml.org 
[lkml]   [1999]   [Feb]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH] APM power off

Hi,

I made a patch to the APM power off routines which switches to real mode
before attempting to power off the computer. This makes APM power off work
on my computer (Gigabyte motherboard & AWARD BIOS).

Please test the patch if you experience problems with APM power off.
(Please make sure that APM power off is enabled in the kernel
configuration and that you use "halt -p" in your shutdown script, e.g.
/sbin/init.d/halt. You also need a recent /bin/halt binary.)

Walter
--- linux/arch/i386/kernel/process.c~ Sun Feb 28 23:09:29 1999
+++ linux/arch/i386/kernel/process.c Mon Mar 1 00:04:01 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
+ *
*/

/*
@@ -380,10 +384,106 @@
{
}

+#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)
{
#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
- apm_power_off();
+ apm_realmode_poweroff();
#endif
}
\
 
 \ /
  Last update: 2005-03-22 13:50    [W:0.070 / U:0.040 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site