Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 #include <linux/linkage.h>
0003 #include <asm/segment.h>
0004 #include <asm/page_types.h>
0005 #include <asm/processor-flags.h>
0006 #include <asm/msr-index.h>
0007 #include "realmode.h"
0008 
0009 /*
0010  * The following code and data reboots the machine by switching to real
0011  * mode and jumping to the BIOS reset entry point, as if the CPU has
0012  * really been reset.  The previous version asked the keyboard
0013  * controller to pulse the CPU reset line, which is more thorough, but
0014  * doesn't work with at least one type of 486 motherboard.  It is easy
0015  * to stop this code working; hence the copious comments.
0016  *
0017  * This code is called with the restart type (0 = BIOS, 1 = APM) in
0018  * the primary argument register (%eax for 32 bit, %edi for 64 bit).
0019  */
0020     .section ".text32", "ax"
0021     .code32
0022 SYM_CODE_START(machine_real_restart_asm)
0023 
0024 #ifdef CONFIG_X86_64
0025     /* Switch to trampoline GDT as it is guaranteed < 4 GiB */
0026     movl    $__KERNEL_DS, %eax
0027     movl    %eax, %ds
0028     lgdtl   pa_tr_gdt
0029 
0030     /* Disable paging to drop us out of long mode */
0031     movl    %cr0, %eax
0032     andl    $~X86_CR0_PG, %eax
0033     movl    %eax, %cr0
0034     ljmpl   $__KERNEL32_CS, $pa_machine_real_restart_paging_off
0035 
0036 SYM_INNER_LABEL(machine_real_restart_paging_off, SYM_L_GLOBAL)
0037     xorl    %eax, %eax
0038     xorl    %edx, %edx
0039     movl    $MSR_EFER, %ecx
0040     wrmsr
0041 
0042     movl    %edi, %eax
0043     
0044 #endif /* CONFIG_X86_64 */
0045     
0046     /* Set up the IDT for real mode. */
0047     lidtl   pa_machine_real_restart_idt
0048 
0049     /*
0050      * Set up a GDT from which we can load segment descriptors for real
0051      * mode.  The GDT is not used in real mode; it is just needed here to
0052      * prepare the descriptors.
0053      */
0054     lgdtl   pa_machine_real_restart_gdt
0055 
0056     /*
0057      * Load the data segment registers with 16-bit compatible values
0058      */
0059     movl    $16, %ecx
0060     movl    %ecx, %ds
0061     movl    %ecx, %es
0062     movl    %ecx, %fs
0063     movl    %ecx, %gs
0064     movl    %ecx, %ss
0065     ljmpw   $8, $1f
0066 SYM_CODE_END(machine_real_restart_asm)
0067 
0068 /*
0069  * This is 16-bit protected mode code to disable paging and the cache,
0070  * switch to real mode and jump to the BIOS reset code.
0071  *
0072  * The instruction that switches to real mode by writing to CR0 must be
0073  * followed immediately by a far jump instruction, which set CS to a
0074  * valid value for real mode, and flushes the prefetch queue to avoid
0075  * running instructions that have already been decoded in protected
0076  * mode.
0077  *
0078  * Clears all the flags except ET, especially PG (paging), PE
0079  * (protected-mode enable) and TS (task switch for coprocessor state
0080  * save).  Flushes the TLB after paging has been disabled.  Sets CD and
0081  * NW, to disable the cache on a 486, and invalidates the cache.  This
0082  * is more like the state of a 486 after reset.  I don't know if
0083  * something else should be done for other chips.
0084  *
0085  * More could be done here to set up the registers as if a CPU reset had
0086  * occurred; hopefully real BIOSs don't assume much.  This is not the
0087  * actual BIOS entry point, anyway (that is at 0xfffffff0).
0088  *
0089  * Most of this work is probably excessive, but it is what is tested.
0090  */
0091     .text
0092     .code16
0093 
0094     .balign 16
0095 machine_real_restart_asm16:
0096 1:
0097     xorl    %ecx, %ecx
0098     movl    %cr0, %edx
0099     andl    $0x00000011, %edx
0100     orl $0x60000000, %edx
0101     movl    %edx, %cr0
0102     movl    %ecx, %cr3
0103     movl    %cr0, %edx
0104     testl   $0x60000000, %edx   /* If no cache bits -> no wbinvd */
0105     jz  2f
0106     wbinvd
0107 2:
0108     andb    $0x10, %dl
0109     movl    %edx, %cr0
0110     LJMPW_RM(3f)
0111 3:
0112     andw    %ax, %ax
0113     jz  bios
0114 
0115 apm:
0116     movw    $0x1000, %ax
0117     movw    %ax, %ss
0118     movw    $0xf000, %sp
0119     movw    $0x5307, %ax
0120     movw    $0x0001, %bx
0121     movw    $0x0003, %cx
0122     int $0x15
0123     /* This should never return... */
0124 
0125 bios:
0126     ljmpw   $0xf000, $0xfff0
0127 
0128     .section ".rodata", "a"
0129 
0130     .balign 16
0131 SYM_DATA_START(machine_real_restart_idt)
0132     .word   0xffff      /* Length - real mode default value */
0133     .long   0       /* Base - real mode default value */
0134 SYM_DATA_END(machine_real_restart_idt)
0135 
0136     .balign 16
0137 SYM_DATA_START(machine_real_restart_gdt)
0138     /* Self-pointer */
0139     .word   0xffff      /* Length - real mode default value */
0140     .long   pa_machine_real_restart_gdt
0141     .word   0
0142 
0143     /*
0144      * 16-bit code segment pointing to real_mode_seg
0145      * Selector value 8
0146      */
0147     .word   0xffff      /* Limit */
0148     .long   0x9b000000 + pa_real_mode_base
0149     .word   0
0150 
0151     /*
0152      * 16-bit data segment with the selector value 16 = 0x10 and
0153      * base value 0x100; since this is consistent with real mode
0154      * semantics we don't have to reload the segments once CR0.PE = 0.
0155      */
0156     .quad   GDT_ENTRY(0x0093, 0x100, 0xffff)
0157 SYM_DATA_END(machine_real_restart_gdt)