[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <AANLkTikxqk=HEw9R-Du=v-1ti1HDGAY9vaNUep2XARaz@mail.gmail.com>
Date: Fri, 18 Feb 2011 07:31:16 -0500
From: Brian Gerst <brgerst@...il.com>
To: mingo@...hat.com, hpa@...or.com, linux-kernel@...r.kernel.org,
castet.matthieu@...e.fr, sfr@...b.auug.org.au, tglx@...utronix.de,
rjw@...k.pl, hpa@...ux.intel.com
Subject: Re: [tip:x86/trampoline] x86, reboot: Move the real-mode reboot code
to an assembly file
On Fri, Feb 18, 2011 at 12:20 AM, tip-bot for H. Peter Anvin
<hpa@...ux.intel.com> wrote:
> Commit-ID: 3d35ac346e981162eeba391e496faceed4753e7b
> Gitweb: http://git.kernel.org/tip/3d35ac346e981162eeba391e496faceed4753e7b
> Author: H. Peter Anvin <hpa@...ux.intel.com>
> AuthorDate: Mon, 14 Feb 2011 18:36:03 -0800
> Committer: H. Peter Anvin <hpa@...ux.intel.com>
> CommitDate: Thu, 17 Feb 2011 21:05:34 -0800
>
> x86, reboot: Move the real-mode reboot code to an assembly file
>
> Move the real-mode reboot code out to an assembly file (reboot_32.S)
> which is allocated using the common lowmem trampoline allocator.
>
> Signed-off-by: H. Peter Anvin <hpa@...ux.intel.com>
> LKML-Reference: <4D5DFBE4.7090104@...el.com>
> Cc: Stephen Rothwell <sfr@...b.auug.org.au>
> Cc: Rafael J. Wysocki <rjw@...k.pl>
> Cc: Matthieu Castet <castet.matthieu@...e.fr>
> ---
> arch/x86/include/asm/reboot.h | 5 +-
> arch/x86/kernel/Makefile | 1 +
> arch/x86/kernel/apm_32.c | 12 +----
> arch/x86/kernel/reboot.c | 120 ++++++++-----------------------------
> arch/x86/kernel/reboot_32.S | 131 +++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 162 insertions(+), 107 deletions(-)
>
> diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h
> index 562d4fd..3250e3d 100644
> --- a/arch/x86/include/asm/reboot.h
> +++ b/arch/x86/include/asm/reboot.h
> @@ -18,7 +18,10 @@ extern struct machine_ops machine_ops;
>
> void native_machine_crash_shutdown(struct pt_regs *regs);
> void native_machine_shutdown(void);
> -void machine_real_restart(const unsigned char *code, int length);
> +void machine_real_restart(unsigned int type);
> +/* These must match dispatch_table in reboot_32.S */
> +#define MRR_BIOS 0
> +#define MRR_APM 1
>
> typedef void (*nmi_shootdown_cb)(int, struct die_args*);
> void nmi_shootdown_cpus(nmi_shootdown_cb callback);
> diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
> index 2e8ce0d..778c5b93 100644
> --- a/arch/x86/kernel/Makefile
> +++ b/arch/x86/kernel/Makefile
> @@ -59,6 +59,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o
> obj-y += cpu/
> obj-y += acpi/
> obj-y += reboot.o
> +obj-$(CONFIG_X86_32) += reboot_32.o
> obj-$(CONFIG_MCA) += mca_32.o
> obj-$(CONFIG_X86_MSR) += msr.o
> obj-$(CONFIG_X86_CPUID) += cpuid.o
> diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c
> index 0e4f24c..b929108 100644
> --- a/arch/x86/kernel/apm_32.c
> +++ b/arch/x86/kernel/apm_32.c
> @@ -975,20 +975,10 @@ recalc:
>
> static void apm_power_off(void)
> {
> - unsigned char po_bios_call[] = {
> - 0xb8, 0x00, 0x10, /* 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 */
> - };
> -
> /* Some bioses don't like being called from CPU != 0 */
> if (apm_info.realmode_power_off) {
> set_cpus_allowed_ptr(current, cpumask_of(0));
> - machine_real_restart(po_bios_call, sizeof(po_bios_call));
> + machine_real_restart(MRR_APM);
> } else {
> (void)set_system_power_state(APM_STATE_OFF);
> }
> diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
> index fc7aae1..10c6619 100644
> --- a/arch/x86/kernel/reboot.c
> +++ b/arch/x86/kernel/reboot.c
> @@ -295,68 +295,16 @@ static int __init reboot_init(void)
> }
> core_initcall(reboot_init);
>
> -/* The following code and data reboots the machine by switching to real
> - mode and jumping to the BIOS reset entry point, as if the CPU has
> - really been reset. The previous version asked the keyboard
> - controller to pulse the CPU reset line, which is more thorough, but
> - doesn't work with at least one type of 486 motherboard. It is easy
> - to stop this code working; hence the copious comments. */
> -static const unsigned long long
> -real_mode_gdt_entries [3] =
> -{
> - 0x0000000000000000ULL, /* Null descriptor */
> - 0x00009b000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
> - 0x000093000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
> -};
> +extern const unsigned char machine_real_restart_asm[];
> +extern const u64 machine_real_restart_gdt[3];
>
> -static const struct desc_ptr
> -real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
> -real_mode_idt = { 0x3ff, 0 };
> -
> -/* This is 16-bit protected mode code to disable paging and the cache,
> - switch to real mode and jump to the BIOS reset code.
> -
> - The instruction that switches to real mode by writing to CR0 must be
> - followed immediately by a far jump instruction, which set CS to a
> - valid value for real mode, and flushes the prefetch queue to avoid
> - running instructions that have already been decoded in protected
> - mode.
> -
> - Clears all the flags except ET, especially PG (paging), PE
> - (protected-mode enable) and TS (task switch for coprocessor state
> - save). Flushes the TLB after paging has been disabled. Sets CD and
> - NW, to disable the cache on a 486, and invalidates the cache. This
> - is more like the state of a 486 after reset. I don't know if
> - something else should be done for other chips.
> -
> - More could be done here to set up the registers as if a CPU reset had
> - occurred; hopefully real BIOSs don't assume much. */
> -static const unsigned char real_mode_switch [] =
> -{
> - 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, 0x09, /* wbinvd */
> - 0x24, 0x10, /* f: andb $0x10,al */
> - 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
> -};
> -static const unsigned char jump_to_bios [] =
> +void machine_real_restart(unsigned int type)
> {
> - 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
> -};
> + void *restart_va;
> + unsigned long restart_pa;
> + void (*restart_lowmem)(unsigned int);
> + u64 *lowmem_gdt;
>
> -/*
> - * Switch to real mode and then execute the code
> - * specified by the code and length parameters.
> - * We assume that length will aways be less that 100!
> - */
> -void machine_real_restart(const unsigned char *code, int length)
> -{
> local_irq_disable();
>
> /* Write zero to CMOS register number 0x0f, which the BIOS POST
> @@ -384,41 +332,23 @@ void machine_real_restart(const unsigned char *code, int length)
> too. */
> *((unsigned short *)0x472) = reboot_mode;
>
> - /* 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(real_mode_switch) - 100),
> - real_mode_switch, sizeof (real_mode_switch));
> - memcpy((void *)(0x1000 - 100), code, length);
> -
> - /* Set up the IDT for real mode. */
> - load_idt(&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. */
> - load_gdt(&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__ ("movl $0x0010,%%eax\n"
> - "\tmovl %%eax,%%ds\n"
> - "\tmovl %%eax,%%es\n"
> - "\tmovl %%eax,%%fs\n"
> - "\tmovl %%eax,%%gs\n"
> - "\tmovl %%eax,%%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 reset
> - entry point. */
> - __asm__ __volatile__ ("ljmp $0x0008,%0"
> - :
> - : "i" ((void *)(0x1000 - sizeof (real_mode_switch) - 100)));
> + /* Patch the GDT in the low memory trampoline */
> + lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt);
> +
> + restart_va = TRAMPOLINE_SYM(machine_real_restart_asm);
> + restart_pa = virt_to_phys(restart_va);
> + restart_lowmem = (void (*)(unsigned int))restart_pa;
> +
> + /* GDT[0]: GDT self-pointer */
> + lowmem_gdt[0] =
> + (u64)(sizeof(machine_real_restart_gdt) - 1) +
> + ((u64)virt_to_phys(lowmem_gdt) << 16);
> + /* GDT[1]: 64K real mode code segment */
> + lowmem_gdt[1] =
> + GDT_ENTRY(0x009b, restart_pa, 0xffff);
> +
> + /* Jump to the identity-mapped low memory code */
> + restart_lowmem(type);
> }
> #ifdef CONFIG_APM_MODULE
> EXPORT_SYMBOL(machine_real_restart);
> @@ -573,7 +503,7 @@ static void native_machine_emergency_restart(void)
>
> #ifdef CONFIG_X86_32
> case BOOT_BIOS:
> - machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
> + machine_real_restart(MRR_BIOS);
>
> reboot_type = BOOT_KBD;
> break;
> diff --git a/arch/x86/kernel/reboot_32.S b/arch/x86/kernel/reboot_32.S
> new file mode 100644
> index 0000000..f242356
> --- /dev/null
> +++ b/arch/x86/kernel/reboot_32.S
> @@ -0,0 +1,131 @@
> +#include <linux/linkage.h>
> +#include <linux/init.h>
> +#include <asm/segment.h>
> +#include <asm/page_types.h>
> +
> +/*
> + * The following code and data reboots the machine by switching to real
> + * mode and jumping to the BIOS reset entry point, as if the CPU has
> + * really been reset. The previous version asked the keyboard
> + * controller to pulse the CPU reset line, which is more thorough, but
> + * doesn't work with at least one type of 486 motherboard. It is easy
> + * to stop this code working; hence the copious comments.
> + *
> + * This code is called with the restart type (0 = BIOS, 1 = APM) in %eax.
> + */
> + .section ".x86_trampoline","a"
> + .balign 16
> + .code32
> +ENTRY(machine_real_restart_asm)
> +r_base = .
> + /* Get our own relocated address */
> + call 1f
> +1: popl %ebx
> + subl $1b, %ebx
> +
> + /* Patch post-real-mode segment jump */
> + movw dispatch_table(%ebx,%ecx,2),%cx
> + movw %cx, 101f(%ebx)
> + movw %ax, 102f(%ebx)
This looks wrong. The type parameter is in %eax, not %ecx, and this
code is expecting %eax to be a segment.
--
Brian Gerst
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists