xref: /openbmc/linux/arch/x86/boot/main.c (revision 6c71a0574249f5e5a45fe055ab5f837023d5eeca)
197873a3dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
296ae6ea0SThomas Gleixner /* -*- linux-c -*- ------------------------------------------------------- *
396ae6ea0SThomas Gleixner  *
496ae6ea0SThomas Gleixner  *   Copyright (C) 1991, 1992 Linus Torvalds
596ae6ea0SThomas Gleixner  *   Copyright 2007 rPath, Inc. - All Rights Reserved
6df7699c5SH. Peter Anvin  *   Copyright 2009 Intel Corporation; author H. Peter Anvin
796ae6ea0SThomas Gleixner  *
896ae6ea0SThomas Gleixner  * ----------------------------------------------------------------------- */
996ae6ea0SThomas Gleixner 
1096ae6ea0SThomas Gleixner /*
1196ae6ea0SThomas Gleixner  * Main module for the real-mode kernel code
1296ae6ea0SThomas Gleixner  */
13d5a1baddSRikard Falkeborn #include <linux/build_bug.h>
1496ae6ea0SThomas Gleixner 
1596ae6ea0SThomas Gleixner #include "boot.h"
16c041b5adSVivek Goyal #include "string.h"
1796ae6ea0SThomas Gleixner 
1896ae6ea0SThomas Gleixner struct boot_params boot_params __attribute__((aligned(16)));
1996ae6ea0SThomas Gleixner 
20eb4ea1aeSKirill A. Shutemov struct port_io_ops pio_ops;
21eb4ea1aeSKirill A. Shutemov 
2296ae6ea0SThomas Gleixner char *HEAP = _end;
2396ae6ea0SThomas Gleixner char *heap_end = _end;		/* Default end of heap = no heap */
2496ae6ea0SThomas Gleixner 
2596ae6ea0SThomas Gleixner /*
2696ae6ea0SThomas Gleixner  * Copy the header into the boot parameter block.  Since this
2796ae6ea0SThomas Gleixner  * screws up the old-style command line protocol, adjust by
2896ae6ea0SThomas Gleixner  * filling in the new-style command line pointer instead.
2996ae6ea0SThomas Gleixner  */
3096ae6ea0SThomas Gleixner 
copy_boot_params(void)3196ae6ea0SThomas Gleixner static void copy_boot_params(void)
3296ae6ea0SThomas Gleixner {
3396ae6ea0SThomas Gleixner 	struct old_cmdline {
3496ae6ea0SThomas Gleixner 		u16 cl_magic;
3596ae6ea0SThomas Gleixner 		u16 cl_offset;
3696ae6ea0SThomas Gleixner 	};
3796ae6ea0SThomas Gleixner 	const struct old_cmdline * const oldcmd =
38aeb84412SKees Cook 		absolute_pointer(OLD_CL_ADDRESS);
3996ae6ea0SThomas Gleixner 
400e96f31eSJordan Borgner 	BUILD_BUG_ON(sizeof(boot_params) != 4096);
410e96f31eSJordan Borgner 	memcpy(&boot_params.hdr, &hdr, sizeof(hdr));
4296ae6ea0SThomas Gleixner 
4396ae6ea0SThomas Gleixner 	if (!boot_params.hdr.cmd_line_ptr &&
4496ae6ea0SThomas Gleixner 	    oldcmd->cl_magic == OLD_CL_MAGIC) {
4596ae6ea0SThomas Gleixner 		/* Old-style command line protocol. */
4696ae6ea0SThomas Gleixner 		u16 cmdline_seg;
4796ae6ea0SThomas Gleixner 
4896ae6ea0SThomas Gleixner 		/* Figure out if the command line falls in the region
4996ae6ea0SThomas Gleixner 		   of memory that an old kernel would have copied up
5096ae6ea0SThomas Gleixner 		   to 0x90000... */
5196ae6ea0SThomas Gleixner 		if (oldcmd->cl_offset < boot_params.hdr.setup_move_size)
5296ae6ea0SThomas Gleixner 			cmdline_seg = ds();
5396ae6ea0SThomas Gleixner 		else
5496ae6ea0SThomas Gleixner 			cmdline_seg = 0x9000;
5596ae6ea0SThomas Gleixner 
5696ae6ea0SThomas Gleixner 		boot_params.hdr.cmd_line_ptr =
5796ae6ea0SThomas Gleixner 			(cmdline_seg << 4) + oldcmd->cl_offset;
5896ae6ea0SThomas Gleixner 	}
5996ae6ea0SThomas Gleixner }
6096ae6ea0SThomas Gleixner 
6196ae6ea0SThomas Gleixner /*
62b2d0b7a0SJoshua Cov  * Query the keyboard lock status as given by the BIOS, and
63b2d0b7a0SJoshua Cov  * set the keyboard repeat rate to maximum.  Unclear why the latter
6496ae6ea0SThomas Gleixner  * is done here; this might be possible to kill off as stale code.
6596ae6ea0SThomas Gleixner  */
keyboard_init(void)66b2d0b7a0SJoshua Cov static void keyboard_init(void)
6796ae6ea0SThomas Gleixner {
68b2d0b7a0SJoshua Cov 	struct biosregs ireg, oreg;
69df7699c5SH. Peter Anvin 	initregs(&ireg);
70b2d0b7a0SJoshua Cov 
71b2d0b7a0SJoshua Cov 	ireg.ah = 0x02;		/* Get keyboard status */
72b2d0b7a0SJoshua Cov 	intcall(0x16, &ireg, &oreg);
73b2d0b7a0SJoshua Cov 	boot_params.kbd_status = oreg.al;
74b2d0b7a0SJoshua Cov 
75b2d0b7a0SJoshua Cov 	ireg.ax = 0x0305;	/* Set keyboard repeat rate */
76df7699c5SH. Peter Anvin 	intcall(0x16, &ireg, NULL);
7796ae6ea0SThomas Gleixner }
7896ae6ea0SThomas Gleixner 
7996ae6ea0SThomas Gleixner /*
8096ae6ea0SThomas Gleixner  * Get Intel SpeedStep (IST) information.
8196ae6ea0SThomas Gleixner  */
query_ist(void)8296ae6ea0SThomas Gleixner static void query_ist(void)
8396ae6ea0SThomas Gleixner {
84df7699c5SH. Peter Anvin 	struct biosregs ireg, oreg;
85df7699c5SH. Peter Anvin 
86c2dcfde8SH. Peter Anvin 	/* Some older BIOSes apparently crash on this call, so filter
87c2dcfde8SH. Peter Anvin 	   it from machines too old to have SpeedStep at all. */
887b27718bSJoerg Roedel 	if (cpu.level < 6)
897b27718bSJoerg Roedel 		return;
907b27718bSJoerg Roedel 
91df7699c5SH. Peter Anvin 	initregs(&ireg);
92df7699c5SH. Peter Anvin 	ireg.ax  = 0xe980;	 /* IST Support */
93df7699c5SH. Peter Anvin 	ireg.edx = 0x47534943;	 /* Request value */
94df7699c5SH. Peter Anvin 	intcall(0x15, &ireg, &oreg);
95df7699c5SH. Peter Anvin 
96df7699c5SH. Peter Anvin 	boot_params.ist_info.signature  = oreg.eax;
97df7699c5SH. Peter Anvin 	boot_params.ist_info.command    = oreg.ebx;
98df7699c5SH. Peter Anvin 	boot_params.ist_info.event      = oreg.ecx;
99df7699c5SH. Peter Anvin 	boot_params.ist_info.perf_level = oreg.edx;
10096ae6ea0SThomas Gleixner }
10196ae6ea0SThomas Gleixner 
10296ae6ea0SThomas Gleixner /*
10396ae6ea0SThomas Gleixner  * Tell the BIOS what CPU mode we intend to run in.
10496ae6ea0SThomas Gleixner  */
set_bios_mode(void)10596ae6ea0SThomas Gleixner static void set_bios_mode(void)
10696ae6ea0SThomas Gleixner {
10796ae6ea0SThomas Gleixner #ifdef CONFIG_X86_64
108df7699c5SH. Peter Anvin 	struct biosregs ireg;
10996ae6ea0SThomas Gleixner 
110df7699c5SH. Peter Anvin 	initregs(&ireg);
111df7699c5SH. Peter Anvin 	ireg.ax = 0xec00;
112df7699c5SH. Peter Anvin 	ireg.bx = 2;
113df7699c5SH. Peter Anvin 	intcall(0x15, &ireg, NULL);
11496ae6ea0SThomas Gleixner #endif
11596ae6ea0SThomas Gleixner }
11696ae6ea0SThomas Gleixner 
init_heap(void)117acd644bbSH. Peter Anvin static void init_heap(void)
118acd644bbSH. Peter Anvin {
119acd644bbSH. Peter Anvin 	char *stack_end;
120acd644bbSH. Peter Anvin 
121acd644bbSH. Peter Anvin 	if (boot_params.hdr.loadflags & CAN_USE_HEAP) {
122*5396ce9aSUros Bizjak 		asm("leal %n1(%%esp),%0"
123*5396ce9aSUros Bizjak 		    : "=r" (stack_end) : "i" (STACK_SIZE));
124acd644bbSH. Peter Anvin 
125acd644bbSH. Peter Anvin 		heap_end = (char *)
126acd644bbSH. Peter Anvin 			((size_t)boot_params.hdr.heap_end_ptr + 0x200);
127acd644bbSH. Peter Anvin 		if (heap_end > stack_end)
128acd644bbSH. Peter Anvin 			heap_end = stack_end;
129acd644bbSH. Peter Anvin 	} else {
130acd644bbSH. Peter Anvin 		/* Boot protocol 2.00 only, no heap available */
131acd644bbSH. Peter Anvin 		puts("WARNING: Ancient bootloader, some functionality "
132acd644bbSH. Peter Anvin 		     "may be limited!\n");
133acd644bbSH. Peter Anvin 	}
134acd644bbSH. Peter Anvin }
135acd644bbSH. Peter Anvin 
main(void)13696ae6ea0SThomas Gleixner void main(void)
13796ae6ea0SThomas Gleixner {
138eb4ea1aeSKirill A. Shutemov 	init_default_io_ops();
139eb4ea1aeSKirill A. Shutemov 
14096ae6ea0SThomas Gleixner 	/* First, copy the boot header into the "zeropage" */
14196ae6ea0SThomas Gleixner 	copy_boot_params();
14296ae6ea0SThomas Gleixner 
143fa97bdf9SPekka Enberg 	/* Initialize the early-boot console */
144fa97bdf9SPekka Enberg 	console_init();
1458fee13a4SYinghai Lu 	if (cmdline_find_option_bool("debug"))
1468fee13a4SYinghai Lu 		puts("early console in setup code\n");
147fa97bdf9SPekka Enberg 
14896ae6ea0SThomas Gleixner 	/* End of heap check */
149acd644bbSH. Peter Anvin 	init_heap();
15096ae6ea0SThomas Gleixner 
15196ae6ea0SThomas Gleixner 	/* Make sure we have all the proper CPU support */
15296ae6ea0SThomas Gleixner 	if (validate_cpu()) {
15396ae6ea0SThomas Gleixner 		puts("Unable to boot - please use a kernel appropriate "
15496ae6ea0SThomas Gleixner 		     "for your CPU.\n");
15596ae6ea0SThomas Gleixner 		die();
15696ae6ea0SThomas Gleixner 	}
15796ae6ea0SThomas Gleixner 
15896ae6ea0SThomas Gleixner 	/* Tell the BIOS what CPU mode we intend to run in. */
15996ae6ea0SThomas Gleixner 	set_bios_mode();
16096ae6ea0SThomas Gleixner 
16196ae6ea0SThomas Gleixner 	/* Detect memory layout */
16296ae6ea0SThomas Gleixner 	detect_memory();
16396ae6ea0SThomas Gleixner 
164b2d0b7a0SJoshua Cov 	/* Set keyboard repeat rate (why?) and query the lock flags */
165b2d0b7a0SJoshua Cov 	keyboard_init();
16696ae6ea0SThomas Gleixner 
16796ae6ea0SThomas Gleixner 	/* Query Intel SpeedStep (IST) information */
16896ae6ea0SThomas Gleixner 	query_ist();
16996ae6ea0SThomas Gleixner 
17096ae6ea0SThomas Gleixner 	/* Query APM information */
17196ae6ea0SThomas Gleixner #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
17296ae6ea0SThomas Gleixner 	query_apm_bios();
17396ae6ea0SThomas Gleixner #endif
17496ae6ea0SThomas Gleixner 
17596ae6ea0SThomas Gleixner 	/* Query EDD information */
17696ae6ea0SThomas Gleixner #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
17796ae6ea0SThomas Gleixner 	query_edd();
17896ae6ea0SThomas Gleixner #endif
1791a8514e0SH. Peter Anvin 
1801a8514e0SH. Peter Anvin 	/* Set the video mode */
1811a8514e0SH. Peter Anvin 	set_video();
1821a8514e0SH. Peter Anvin 
18396ae6ea0SThomas Gleixner 	/* Do the last things and invoke protected mode */
18496ae6ea0SThomas Gleixner 	go_to_protected_mode();
18596ae6ea0SThomas Gleixner }
186