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