1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* -*- linux-c -*- ------------------------------------------------------- * |
3 | * |
4 | * Copyright (C) 1991, 1992 Linus Torvalds |
5 | * Copyright 2007 rPath, Inc. - All Rights Reserved |
6 | * Copyright 2009 Intel Corporation; author H. Peter Anvin |
7 | * |
8 | * ----------------------------------------------------------------------- */ |
9 | |
10 | /* |
11 | * Main module for the real-mode kernel code |
12 | */ |
13 | #include <linux/build_bug.h> |
14 | |
15 | #include "boot.h" |
16 | #include "string.h" |
17 | |
18 | struct boot_params boot_params __attribute__((aligned(16))); |
19 | |
20 | struct port_io_ops pio_ops; |
21 | |
22 | char *HEAP = _end; |
23 | char *heap_end = _end; /* Default end of heap = no heap */ |
24 | |
25 | /* |
26 | * Copy the header into the boot parameter block. Since this |
27 | * screws up the old-style command line protocol, adjust by |
28 | * filling in the new-style command line pointer instead. |
29 | */ |
30 | |
31 | static void copy_boot_params(void) |
32 | { |
33 | struct old_cmdline { |
34 | u16 cl_magic; |
35 | u16 cl_offset; |
36 | }; |
37 | const struct old_cmdline * const oldcmd = |
38 | absolute_pointer(OLD_CL_ADDRESS); |
39 | |
40 | BUILD_BUG_ON(sizeof(boot_params) != 4096); |
41 | memcpy(&boot_params.hdr, &hdr, sizeof(hdr)); |
42 | |
43 | if (!boot_params.hdr.cmd_line_ptr && |
44 | oldcmd->cl_magic == OLD_CL_MAGIC) { |
45 | /* Old-style command line protocol. */ |
46 | u16 cmdline_seg; |
47 | |
48 | /* Figure out if the command line falls in the region |
49 | of memory that an old kernel would have copied up |
50 | to 0x90000... */ |
51 | if (oldcmd->cl_offset < boot_params.hdr.setup_move_size) |
52 | cmdline_seg = ds(); |
53 | else |
54 | cmdline_seg = 0x9000; |
55 | |
56 | boot_params.hdr.cmd_line_ptr = |
57 | (cmdline_seg << 4) + oldcmd->cl_offset; |
58 | } |
59 | } |
60 | |
61 | /* |
62 | * Query the keyboard lock status as given by the BIOS, and |
63 | * set the keyboard repeat rate to maximum. Unclear why the latter |
64 | * is done here; this might be possible to kill off as stale code. |
65 | */ |
66 | static void keyboard_init(void) |
67 | { |
68 | struct biosregs ireg, oreg; |
69 | initregs(regs: &ireg); |
70 | |
71 | ireg.ah = 0x02; /* Get keyboard status */ |
72 | intcall(int_no: 0x16, ireg: &ireg, oreg: &oreg); |
73 | boot_params.kbd_status = oreg.al; |
74 | |
75 | ireg.ax = 0x0305; /* Set keyboard repeat rate */ |
76 | intcall(int_no: 0x16, ireg: &ireg, NULL); |
77 | } |
78 | |
79 | /* |
80 | * Get Intel SpeedStep (IST) information. |
81 | */ |
82 | static void query_ist(void) |
83 | { |
84 | struct biosregs ireg, oreg; |
85 | |
86 | /* Some older BIOSes apparently crash on this call, so filter |
87 | it from machines too old to have SpeedStep at all. */ |
88 | if (cpu.level < 6) |
89 | return; |
90 | |
91 | initregs(regs: &ireg); |
92 | ireg.ax = 0xe980; /* IST Support */ |
93 | ireg.edx = 0x47534943; /* Request value */ |
94 | intcall(int_no: 0x15, ireg: &ireg, oreg: &oreg); |
95 | |
96 | boot_params.ist_info.signature = oreg.eax; |
97 | boot_params.ist_info.command = oreg.ebx; |
98 | boot_params.ist_info.event = oreg.ecx; |
99 | boot_params.ist_info.perf_level = oreg.edx; |
100 | } |
101 | |
102 | /* |
103 | * Tell the BIOS what CPU mode we intend to run in. |
104 | */ |
105 | static void set_bios_mode(void) |
106 | { |
107 | #ifdef CONFIG_X86_64 |
108 | struct biosregs ireg; |
109 | |
110 | initregs(regs: &ireg); |
111 | ireg.ax = 0xec00; |
112 | ireg.bx = 2; |
113 | intcall(int_no: 0x15, ireg: &ireg, NULL); |
114 | #endif |
115 | } |
116 | |
117 | static void init_heap(void) |
118 | { |
119 | char *stack_end; |
120 | |
121 | if (boot_params.hdr.loadflags & CAN_USE_HEAP) { |
122 | asm("leal %P1(%%esp),%0" |
123 | : "=r" (stack_end) : "i" (-STACK_SIZE)); |
124 | |
125 | heap_end = (char *) |
126 | ((size_t)boot_params.hdr.heap_end_ptr + 0x200); |
127 | if (heap_end > stack_end) |
128 | heap_end = stack_end; |
129 | } else { |
130 | /* Boot protocol 2.00 only, no heap available */ |
131 | puts("WARNING: Ancient bootloader, some functionality " |
132 | "may be limited!\n" ); |
133 | } |
134 | } |
135 | |
136 | void main(void) |
137 | { |
138 | init_default_io_ops(); |
139 | |
140 | /* First, copy the boot header into the "zeropage" */ |
141 | copy_boot_params(); |
142 | |
143 | /* Initialize the early-boot console */ |
144 | console_init(); |
145 | if (cmdline_find_option_bool(option: "debug" )) |
146 | puts("early console in setup code\n" ); |
147 | |
148 | /* End of heap check */ |
149 | init_heap(); |
150 | |
151 | /* Make sure we have all the proper CPU support */ |
152 | if (validate_cpu()) { |
153 | puts("Unable to boot - please use a kernel appropriate " |
154 | "for your CPU.\n" ); |
155 | die(); |
156 | } |
157 | |
158 | /* Tell the BIOS what CPU mode we intend to run in. */ |
159 | set_bios_mode(); |
160 | |
161 | /* Detect memory layout */ |
162 | detect_memory(); |
163 | |
164 | /* Set keyboard repeat rate (why?) and query the lock flags */ |
165 | keyboard_init(); |
166 | |
167 | /* Query Intel SpeedStep (IST) information */ |
168 | query_ist(); |
169 | |
170 | /* Query APM information */ |
171 | #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) |
172 | query_apm_bios(); |
173 | #endif |
174 | |
175 | /* Query EDD information */ |
176 | #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) |
177 | query_edd(); |
178 | #endif |
179 | |
180 | /* Set the video mode */ |
181 | set_video(); |
182 | |
183 | /* Do the last things and invoke protected mode */ |
184 | go_to_protected_mode(); |
185 | } |
186 | |