162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* -*- linux-c -*- ------------------------------------------------------- * 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 562306a36Sopenharmony_ci * Copyright 2007 rPath, Inc. - All Rights Reserved 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * ----------------------------------------------------------------------- */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Prepare the machine for transition to protected mode. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "boot.h" 1462306a36Sopenharmony_ci#include <asm/segment.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * Invoke the realmode switch hook if present; otherwise 1862306a36Sopenharmony_ci * disable all interrupts. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistatic void realmode_switch_hook(void) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci if (boot_params.hdr.realmode_swtch) { 2362306a36Sopenharmony_ci asm volatile("lcallw *%0" 2462306a36Sopenharmony_ci : : "m" (boot_params.hdr.realmode_swtch) 2562306a36Sopenharmony_ci : "eax", "ebx", "ecx", "edx"); 2662306a36Sopenharmony_ci } else { 2762306a36Sopenharmony_ci asm volatile("cli"); 2862306a36Sopenharmony_ci outb(0x80, 0x70); /* Disable NMI */ 2962306a36Sopenharmony_ci io_delay(); 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Disable all interrupts at the legacy PIC. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic void mask_all_interrupts(void) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci outb(0xff, 0xa1); /* Mask all interrupts on the secondary PIC */ 3962306a36Sopenharmony_ci io_delay(); 4062306a36Sopenharmony_ci outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */ 4162306a36Sopenharmony_ci io_delay(); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * Reset IGNNE# if asserted in the FPU. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic void reset_coprocessor(void) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci outb(0, 0xf0); 5062306a36Sopenharmony_ci io_delay(); 5162306a36Sopenharmony_ci outb(0, 0xf1); 5262306a36Sopenharmony_ci io_delay(); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * Set up the GDT 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct gdt_ptr { 6062306a36Sopenharmony_ci u16 len; 6162306a36Sopenharmony_ci u32 ptr; 6262306a36Sopenharmony_ci} __attribute__((packed)); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void setup_gdt(void) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci /* There are machines which are known to not boot with the GDT 6762306a36Sopenharmony_ci being 8-byte unaligned. Intel recommends 16 byte alignment. */ 6862306a36Sopenharmony_ci static const u64 boot_gdt[] __attribute__((aligned(16))) = { 6962306a36Sopenharmony_ci /* CS: code, read/execute, 4 GB, base 0 */ 7062306a36Sopenharmony_ci [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff), 7162306a36Sopenharmony_ci /* DS: data, read/write, 4 GB, base 0 */ 7262306a36Sopenharmony_ci [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff), 7362306a36Sopenharmony_ci /* TSS: 32-bit tss, 104 bytes, base 4096 */ 7462306a36Sopenharmony_ci /* We only have a TSS here to keep Intel VT happy; 7562306a36Sopenharmony_ci we don't actually use it for anything. */ 7662306a36Sopenharmony_ci [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103), 7762306a36Sopenharmony_ci }; 7862306a36Sopenharmony_ci /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead 7962306a36Sopenharmony_ci of the gdt_ptr contents. Thus, make it static so it will 8062306a36Sopenharmony_ci stay in memory, at least long enough that we switch to the 8162306a36Sopenharmony_ci proper kernel GDT. */ 8262306a36Sopenharmony_ci static struct gdt_ptr gdt; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci gdt.len = sizeof(boot_gdt)-1; 8562306a36Sopenharmony_ci gdt.ptr = (u32)&boot_gdt + (ds() << 4); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci asm volatile("lgdtl %0" : : "m" (gdt)); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* 9162306a36Sopenharmony_ci * Set up the IDT 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_cistatic void setup_idt(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci static const struct gdt_ptr null_idt = {0, 0}; 9662306a36Sopenharmony_ci asm volatile("lidtl %0" : : "m" (null_idt)); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * Actual invocation sequence 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_civoid go_to_protected_mode(void) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci /* Hook before leaving real mode, also disables interrupts */ 10562306a36Sopenharmony_ci realmode_switch_hook(); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Enable the A20 gate */ 10862306a36Sopenharmony_ci if (enable_a20()) { 10962306a36Sopenharmony_ci puts("A20 gate not responding, unable to boot...\n"); 11062306a36Sopenharmony_ci die(); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Reset coprocessor (IGNNE#) */ 11462306a36Sopenharmony_ci reset_coprocessor(); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Mask all interrupts in the PIC */ 11762306a36Sopenharmony_ci mask_all_interrupts(); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Actual transition to protected mode... */ 12062306a36Sopenharmony_ci setup_idt(); 12162306a36Sopenharmony_ci setup_gdt(); 12262306a36Sopenharmony_ci protected_mode_jump(boot_params.hdr.code32_start, 12362306a36Sopenharmony_ci (u32)&boot_params + (ds() << 4)); 12462306a36Sopenharmony_ci} 125