1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <sys/multiboot.h>
28#include <sys/multiboot2.h>
29#include <sys/asm_linkage.h>
30#include <sys/segments.h>
31#include <sys/controlregs.h>
32
33#include "dboot_xboot.h"
34
35	.text
36	.globl _start
37_start:
38	jmp	code_start
39
40	/*
41	 * The multiboot header has to be at the start of the file
42	 *
43	 * The 32 bit kernel is ELF32, so the MB header is mostly ignored.
44	 *
45	 * The 64 bit kernel is ELF64, so we get grub to load the entire
46	 * ELF file into memory and trick it into jumping into this code.
47	 * The trick is done by a binary utility run after unix is linked,
48	 * that rewrites the mb_header.
49	 */
50	.align 4
51	.globl	mb_header
52mb_header:
53	.long	MB_HEADER_MAGIC	/* magic number */
54#if defined(_BOOT_TARGET_i386)
55	.long	MB_HEADER_FLAGS_32	/* flags */
56	.long	MB_HEADER_CHECKSUM_32	/* checksum */
57#elif defined (_BOOT_TARGET_amd64)
58	.long	MB_HEADER_FLAGS_64	/* flags */
59	.long	MB_HEADER_CHECKSUM_64	/* checksum */
60#else
61#error No architecture defined
62#endif
63	.long	0x11111111	/* header_addr: patched by mbh_patch */
64	.long	0x100000	/* load_addr: patched by mbh_patch */
65	.long	0		/* load_end_addr - 0 means entire file */
66	.long	0		/* bss_end_addr */
67	.long	0x2222222	/* entry_addr: patched by mbh_patch */
68	.long	0		/* video mode.. */
69	.long	0		/* width 0 == don't care */
70	.long	0		/* height 0 == don't care */
71	.long	0		/* depth 0 == don't care */
72
73#if defined(_BOOT_TARGET_i386)
74	/*
75	 * The MB2 header must be 8 byte aligned relative to the beginning of
76	 * the in-memory ELF object. The 32-bit kernel ELF file has sections
77	 * which are 4-byte aligned, and as .align family directives only do
78	 * control the alignment inside the section, we need to construct the
79	 * image manually, by inserting the padding where needed. The alignment
80	 * setup here depends on the first PT_LOAD section of the ELF file, if
81	 * this section offset will change, this code must be reviewed.
82	 * Similarily, if we add extra tag types into the information request
83	 * or add tags into the tag list.
84	 */
85	.long	0		/* padding */
86#else
87	.balign	MULTIBOOT_HEADER_ALIGN
88#endif
89mb2_header:
90	.long	MULTIBOOT2_HEADER_MAGIC
91	.long	MULTIBOOT_ARCHITECTURE_I386
92	.long	mb2_header_end - mb2_header
93	.long	-(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + (mb2_header_end - mb2_header))
94
95	/*
96	 * Multiboot 2 tags follow. Note, the first tag immediately follows
97	 * the header. Subsequent tags must be aligned by MULTIBOOT_TAG_ALIGN.
98	 *
99	 * MB information request tag.
100	 */
101information_request_tag_start:
102	.word	MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST
103	.word	0
104	.long	information_request_tag_end - information_request_tag_start
105	.long	MULTIBOOT_TAG_TYPE_CMDLINE
106	.long	MULTIBOOT_TAG_TYPE_MODULE
107	.long	MULTIBOOT_TAG_TYPE_BOOTDEV
108	.long	MULTIBOOT_TAG_TYPE_MMAP
109	.long	MULTIBOOT_TAG_TYPE_FRAMEBUFFER
110	.long	MULTIBOOT_TAG_TYPE_BASIC_MEMINFO
111information_request_tag_end:
112
113#if defined (_BOOT_TARGET_amd64)
114	/*
115	 * The following values are patched by mbh_patch for the 64-bit kernel,
116	 * so we only provide this tag for the 64-bit kernel.
117	 */
118	.balign	MULTIBOOT_TAG_ALIGN
119address_tag_start:
120	.word	MULTIBOOT_HEADER_TAG_ADDRESS
121	.word	0
122	.long	address_tag_end - address_tag_start
123	.long	mb2_header
124	.globl	mb2_load_addr
125mb2_load_addr:
126	.long	0		/* load addr */
127	.long	0		/* load_end_addr */
128	.long	0		/* bss_end_addr */
129address_tag_end:
130	/*
131	 * entry address tag
132	 */
133	.balign	MULTIBOOT_TAG_ALIGN
134entry_address_tag_start:
135	.word	MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS
136	.word	0
137	.long	entry_address_tag_end - entry_address_tag_start
138	.long	0		/* entry addr */
139entry_address_tag_end:
140
141	.balign	MULTIBOOT_TAG_ALIGN	/* Alignment for the next tag */
142#endif
143	/*
144	 * MB console flags tag
145	 */
146console_tag_start:
147	.word	MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS
148	.word	0
149	.long	console_tag_end - console_tag_start
150	.long	MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED
151console_tag_end:
152	.long	0		/* padding */
153
154	/*
155	 * MB header framebuffer tag
156	 */
157framebuffer_tag_start:
158	.word	MULTIBOOT_HEADER_TAG_FRAMEBUFFER
159	.word	0
160	.long	framebuffer_tag_end - framebuffer_tag_start
161	.long	0		/* width - no preference */
162	.long	0		/* height - no preference */
163	.long	0		/* depth - no preference */
164framebuffer_tag_end:
165	.long	0		/* padding */
166
167	/*
168	 * Tell the bootloader to load the modules page aligned to
169	 * the specified alignment.
170	 */
171	.word	MULTIBOOT_HEADER_TAG_MODULE_ALIGN
172	.word	0
173	.long	8
174
175	/*
176	 * Termination tag.
177	 */
178	.word	MULTIBOOT_HEADER_TAG_END
179	.word	0
180	.long	8
181mb2_header_end:
182
183	/*
184	 * At entry we are in protected mode, 32 bit execution, paging and
185	 * interrupts are disabled.
186	 *
187	 * EAX == MB_BOOTLOADER_MAGIC
188	 * EBX points to multiboot information
189	 * segment registers all have segments with base 0, limit == 0xffffffff
190	 */
191code_start:
192	movl	%eax, mb_magic
193	movl	%ebx, mb_addr
194
195	movl	$stack_space, %esp	/* load my stack pointer */
196	addl	$STACK_SIZE, %esp
197
198	pushl	$0x0			/* push a dead-end frame */
199	pushl	$0x0
200	movl	%esp, %ebp
201
202	pushl	$0x0			/* clear all processor flags */
203	popf
204
205	/*
206	 * setup a global descriptor table with known contents
207	 */
208	lgdt	gdt_info
209	movw	$B32DATA_SEL, %ax
210	movw    %ax, %ds
211	movw    %ax, %es
212	movw    %ax, %fs
213	movw    %ax, %gs
214	movw    %ax, %ss
215	ljmp    $B32CODE_SEL, $newgdt
216newgdt:
217	nop
218
219	/*
220	 * go off and determine memory config, build page tables, etc.
221	 */
222	call	startup_kernel
223
224
225	/*
226	 * On amd64 we'll want the stack pointer to be 16 byte aligned.
227	 */
228	andl	$0xfffffff0, %esp
229
230	/*
231	 * Enable PGE, PAE and large pages
232	 */
233	movl	%cr4, %eax
234	testl	$1, pge_support
235	jz	1f
236	orl	$CR4_PGE, %eax
2371:
238	testl	$1, pae_support
239	jz	1f
240	orl	$CR4_PAE, %eax
2411:
242	testl	$1, largepage_support
243	jz	1f
244	orl	$CR4_PSE, %eax
2451:
246	movl	%eax, %cr4
247
248	/*
249	 * enable NX protection if processor supports it
250	 */
251	testl   $1, NX_support
252	jz      1f
253	movl    $MSR_AMD_EFER, %ecx
254	rdmsr
255	orl     $AMD_EFER_NXE, %eax
256	wrmsr
2571:
258
259
260	/*
261	 * load the pagetable base address into cr3
262	 */
263	movl	top_page_table, %eax
264	movl	%eax, %cr3
265
266#if defined(_BOOT_TARGET_amd64)
267	/*
268	 * enable long mode
269	 */
270	movl	$MSR_AMD_EFER, %ecx
271	rdmsr
272	orl	$AMD_EFER_LME, %eax
273	wrmsr
274#endif
275
276	/*
277	 * enable paging, write protection, alignment masking, but disable
278	 * the cache disable and write through only bits.
279	 */
280	movl	%cr0, %eax
281	orl	$_CONST(CR0_PG | CR0_WP | CR0_AM), %eax
282	andl	$_BITNOT(CR0_NW | CR0_CD), %eax
283	movl	%eax, %cr0
284	jmp	paging_on
285paging_on:
286
287	/*
288	 * The xboot_info ptr gets passed to the kernel as its argument
289	 */
290	movl	bi, %edi
291	movl	entry_addr_low, %esi
292
293#if defined(_BOOT_TARGET_i386)
294
295	pushl	%edi
296	call	*%esi
297
298#elif defined(_BOOT_TARGET_amd64)
299
300	/*
301	 * We're still in compatibility mode with 32 bit execution.
302	 * Switch to 64 bit mode now by switching to a 64 bit code segment.
303	 * then set up and do a lret to get into 64 bit execution.
304	 */
305	pushl	$B64CODE_SEL
306	pushl	$longmode
307	lret
308longmode:
309	.code64
310	movq	$0xffffffff00000000,%rdx
311	orq	%rdx, %rsi		/* set upper bits of entry addr */
312	notq	%rdx
313	andq	%rdx, %rdi		/* clean %rdi for passing arg */
314	call	*%rsi
315
316#else
317#error	"undefined target"
318#endif
319
320	.code32
321
322	/*
323	 * if reset fails halt the system
324	 */
325	ENTRY_NP(dboot_halt)
326	hlt
327	SET_SIZE(dboot_halt)
328
329	/*
330	 * flush the TLB
331	 */
332	ENTRY_NP(reload_cr3)
333	movl	%cr3, %eax
334	movl	%eax, %cr3
335	ret
336	SET_SIZE(reload_cr3)
337
338	/*
339	 * Detect if we can do cpuid, see if we can change bit 21 of eflags.
340	 * Note we don't do the bizarre tests for Cyrix CPUs in ml/locore.s.
341	 * If you're on such a CPU, you're stuck with non-PAE 32 bit kernels.
342	 */
343	ENTRY_NP(have_cpuid)
344	pushf
345	pushf
346	xorl	%eax, %eax
347	popl	%ecx
348	movl	%ecx, %edx
349	xorl	$0x200000, %ecx
350	pushl	%ecx
351	popf
352	pushf
353	popl	%ecx
354	cmpl	%ecx, %edx
355	setne	%al
356	popf
357	ret
358	SET_SIZE(have_cpuid)
359
360	/*
361	 * We want the GDT to be on its own page for better performance
362	 * running under hypervisors.
363	 */
364	.skip 4096
365#include "../boot/boot_gdt.s"
366	.skip 4096
367	.long	0
368
369