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