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