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 *   Copyright 2009 Intel Corporation; author H. Peter Anvin
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * ----------------------------------------------------------------------- */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * Main module for the real-mode kernel code
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/build_bug.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "boot.h"
1662306a36Sopenharmony_ci#include "string.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct boot_params boot_params __attribute__((aligned(16)));
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct port_io_ops pio_ops;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cichar *HEAP = _end;
2362306a36Sopenharmony_cichar *heap_end = _end;		/* Default end of heap = no heap */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Copy the header into the boot parameter block.  Since this
2762306a36Sopenharmony_ci * screws up the old-style command line protocol, adjust by
2862306a36Sopenharmony_ci * filling in the new-style command line pointer instead.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void copy_boot_params(void)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct old_cmdline {
3462306a36Sopenharmony_ci		u16 cl_magic;
3562306a36Sopenharmony_ci		u16 cl_offset;
3662306a36Sopenharmony_ci	};
3762306a36Sopenharmony_ci	const struct old_cmdline * const oldcmd =
3862306a36Sopenharmony_ci		absolute_pointer(OLD_CL_ADDRESS);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(boot_params) != 4096);
4162306a36Sopenharmony_ci	memcpy(&boot_params.hdr, &hdr, sizeof(hdr));
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!boot_params.hdr.cmd_line_ptr &&
4462306a36Sopenharmony_ci	    oldcmd->cl_magic == OLD_CL_MAGIC) {
4562306a36Sopenharmony_ci		/* Old-style command line protocol. */
4662306a36Sopenharmony_ci		u16 cmdline_seg;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		/* Figure out if the command line falls in the region
4962306a36Sopenharmony_ci		   of memory that an old kernel would have copied up
5062306a36Sopenharmony_ci		   to 0x90000... */
5162306a36Sopenharmony_ci		if (oldcmd->cl_offset < boot_params.hdr.setup_move_size)
5262306a36Sopenharmony_ci			cmdline_seg = ds();
5362306a36Sopenharmony_ci		else
5462306a36Sopenharmony_ci			cmdline_seg = 0x9000;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		boot_params.hdr.cmd_line_ptr =
5762306a36Sopenharmony_ci			(cmdline_seg << 4) + oldcmd->cl_offset;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * Query the keyboard lock status as given by the BIOS, and
6362306a36Sopenharmony_ci * set the keyboard repeat rate to maximum.  Unclear why the latter
6462306a36Sopenharmony_ci * is done here; this might be possible to kill off as stale code.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic void keyboard_init(void)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct biosregs ireg, oreg;
6962306a36Sopenharmony_ci	initregs(&ireg);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	ireg.ah = 0x02;		/* Get keyboard status */
7262306a36Sopenharmony_ci	intcall(0x16, &ireg, &oreg);
7362306a36Sopenharmony_ci	boot_params.kbd_status = oreg.al;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	ireg.ax = 0x0305;	/* Set keyboard repeat rate */
7662306a36Sopenharmony_ci	intcall(0x16, &ireg, NULL);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * Get Intel SpeedStep (IST) information.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cistatic void query_ist(void)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct biosregs ireg, oreg;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Some older BIOSes apparently crash on this call, so filter
8762306a36Sopenharmony_ci	   it from machines too old to have SpeedStep at all. */
8862306a36Sopenharmony_ci	if (cpu.level < 6)
8962306a36Sopenharmony_ci		return;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	initregs(&ireg);
9262306a36Sopenharmony_ci	ireg.ax  = 0xe980;	 /* IST Support */
9362306a36Sopenharmony_ci	ireg.edx = 0x47534943;	 /* Request value */
9462306a36Sopenharmony_ci	intcall(0x15, &ireg, &oreg);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	boot_params.ist_info.signature  = oreg.eax;
9762306a36Sopenharmony_ci	boot_params.ist_info.command    = oreg.ebx;
9862306a36Sopenharmony_ci	boot_params.ist_info.event      = oreg.ecx;
9962306a36Sopenharmony_ci	boot_params.ist_info.perf_level = oreg.edx;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/*
10362306a36Sopenharmony_ci * Tell the BIOS what CPU mode we intend to run in.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_cistatic void set_bios_mode(void)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci#ifdef CONFIG_X86_64
10862306a36Sopenharmony_ci	struct biosregs ireg;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	initregs(&ireg);
11162306a36Sopenharmony_ci	ireg.ax = 0xec00;
11262306a36Sopenharmony_ci	ireg.bx = 2;
11362306a36Sopenharmony_ci	intcall(0x15, &ireg, NULL);
11462306a36Sopenharmony_ci#endif
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void init_heap(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	char *stack_end;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (boot_params.hdr.loadflags & CAN_USE_HEAP) {
12262306a36Sopenharmony_ci		asm("leal %P1(%%esp),%0"
12362306a36Sopenharmony_ci		    : "=r" (stack_end) : "i" (-STACK_SIZE));
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		heap_end = (char *)
12662306a36Sopenharmony_ci			((size_t)boot_params.hdr.heap_end_ptr + 0x200);
12762306a36Sopenharmony_ci		if (heap_end > stack_end)
12862306a36Sopenharmony_ci			heap_end = stack_end;
12962306a36Sopenharmony_ci	} else {
13062306a36Sopenharmony_ci		/* Boot protocol 2.00 only, no heap available */
13162306a36Sopenharmony_ci		puts("WARNING: Ancient bootloader, some functionality "
13262306a36Sopenharmony_ci		     "may be limited!\n");
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_civoid main(void)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	init_default_io_ops();
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* First, copy the boot header into the "zeropage" */
14162306a36Sopenharmony_ci	copy_boot_params();
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* Initialize the early-boot console */
14462306a36Sopenharmony_ci	console_init();
14562306a36Sopenharmony_ci	if (cmdline_find_option_bool("debug"))
14662306a36Sopenharmony_ci		puts("early console in setup code\n");
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* End of heap check */
14962306a36Sopenharmony_ci	init_heap();
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* Make sure we have all the proper CPU support */
15262306a36Sopenharmony_ci	if (validate_cpu()) {
15362306a36Sopenharmony_ci		puts("Unable to boot - please use a kernel appropriate "
15462306a36Sopenharmony_ci		     "for your CPU.\n");
15562306a36Sopenharmony_ci		die();
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* Tell the BIOS what CPU mode we intend to run in. */
15962306a36Sopenharmony_ci	set_bios_mode();
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Detect memory layout */
16262306a36Sopenharmony_ci	detect_memory();
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Set keyboard repeat rate (why?) and query the lock flags */
16562306a36Sopenharmony_ci	keyboard_init();
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Query Intel SpeedStep (IST) information */
16862306a36Sopenharmony_ci	query_ist();
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* Query APM information */
17162306a36Sopenharmony_ci#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
17262306a36Sopenharmony_ci	query_apm_bios();
17362306a36Sopenharmony_ci#endif
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* Query EDD information */
17662306a36Sopenharmony_ci#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
17762306a36Sopenharmony_ci	query_edd();
17862306a36Sopenharmony_ci#endif
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Set the video mode */
18162306a36Sopenharmony_ci	set_video();
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Do the last things and invoke protected mode */
18462306a36Sopenharmony_ci	go_to_protected_mode();
18562306a36Sopenharmony_ci}
186