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