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 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 /* 25 * Copyright (c) 2010, Intel Corporation. 26 * All rights reserved. 27 */ 28 29 #include <sys/asm_linkage.h> 30 #include <sys/asm_misc.h> 31 #include <sys/regset.h> 32 #include <sys/privregs.h> 33 #include <sys/x86_archext.h> 34 35 #if !defined(__lint) 36 #include <sys/segments.h> 37 #include "assym.h" 38 #endif 39 40 /* 41 * Our assumptions: 42 * - We are running in real mode. 43 * - Interrupts are disabled. 44 * - Selectors are equal (cs == ds == ss) for all real mode code 45 * - The GDT, IDT, ktss and page directory has been built for us 46 * 47 * Our actions: 48 * Start CPU: 49 * - We start using our GDT by loading correct values in the 50 * selector registers (cs=KCS_SEL, ds=es=ss=KDS_SEL, fs=KFS_SEL, 51 * gs=KGS_SEL). 52 * - We change over to using our IDT. 53 * - We load the default LDT into the hardware LDT register. 54 * - We load the default TSS into the hardware task register. 55 * - call mp_startup(void) indirectly through the T_PC 56 * Stop CPU: 57 * - Put CPU into halted state with interrupts disabled 58 * 59 */ 60 61 #if defined(__lint) 62 63 void 64 real_mode_start_cpu(void) 65 {} 66 67 void 68 real_mode_stop_cpu_stage1(void) 69 {} 70 71 void 72 real_mode_stop_cpu_stage2(void) 73 {} 74 75 #else /* __lint */ 76 77 #if defined(__amd64) 78 79 ENTRY_NP(real_mode_start_cpu) 80 81 /* 82 * NOTE: The GNU assembler automatically does the right thing to 83 * generate data size operand prefixes based on the code size 84 * generation mode (e.g. .code16, .code32, .code64) and as such 85 * prefixes need not be used on instructions EXCEPT in the case 86 * of address prefixes for code for which the reference is not 87 * automatically of the default operand size. 88 */ 89 .code16 90 cli 91 movw %cs, %ax 92 movw %ax, %ds /* load cs into ds */ 93 movw %ax, %ss /* and into ss */ 94 95 /* 96 * Helps in debugging by giving us the fault address. 97 * 98 * Remember to patch a hlt (0xf4) at cmntrap to get a good stack. 99 */ 100 movl $0xffc, %esp 101 movl %cr0, %eax 102 103 /* 104 * Enable protected-mode, write protect, and alignment mask 105 */ 106 orl $(CR0_PE|CR0_WP|CR0_AM), %eax 107 movl %eax, %cr0 108 109 /* 110 * Do a jmp immediately after writing to cr0 when enabling protected 111 * mode to clear the real mode prefetch queue (per Intel's docs) 112 */ 113 jmp pestart 114 115 pestart: 116 /* 117 * 16-bit protected mode is now active, so prepare to turn on long 118 * mode. 119 * 120 * Note that we currently assume that if we're attempting to run a 121 * kernel compiled with (__amd64) #defined, the target CPU has long 122 * mode support. 123 */ 124 125 #if 0 126 /* 127 * If there's a chance this might not be true, the following test should 128 * be done, with the no_long_mode branch then doing something 129 * appropriate: 130 */ 131 132 movl $0x80000000, %eax /* get largest extended CPUID */ 133 cpuid 134 cmpl $0x80000000, %eax /* check if > 0x80000000 */ 135 jbe no_long_mode /* nope, no long mode */ 136 movl $0x80000001, %eax 137 cpuid /* get extended feature flags */ 138 btl $29, %edx /* check for long mode */ 139 jnc no_long_mode /* long mode not supported */ 140 #endif 141 142 /* 143 * Add any initial cr4 bits 144 */ 145 movl %cr4, %eax 146 addr32 orl CR4OFF, %eax 147 148 /* 149 * Enable PAE mode (CR4.PAE) 150 */ 151 orl $CR4_PAE, %eax 152 movl %eax, %cr4 153 154 /* 155 * Point cr3 to the 64-bit long mode page tables. 156 * 157 * Note that these MUST exist in 32-bit space, as we don't have 158 * a way to load %cr3 with a 64-bit base address for the page tables 159 * until the CPU is actually executing in 64-bit long mode. 160 */ 161 addr32 movl CR3OFF, %eax 162 movl %eax, %cr3 163 164 /* 165 * Set long mode enable in EFER (EFER.LME = 1) 166 */ 167 movl $MSR_AMD_EFER, %ecx 168 rdmsr 169 orl $AMD_EFER_LME, %eax 170 wrmsr 171 172 /* 173 * Finally, turn on paging (CR0.PG = 1) to activate long mode. 174 */ 175 movl %cr0, %eax 176 orl $CR0_PG, %eax 177 movl %eax, %cr0 178 179 /* 180 * The instruction after enabling paging in CR0 MUST be a branch. 181 */ 182 jmp long_mode_active 183 184 long_mode_active: 185 /* 186 * Long mode is now active but since we're still running with the 187 * original 16-bit CS we're actually in 16-bit compatability mode. 188 * 189 * We have to load an intermediate GDT and IDT here that we know are 190 * in 32-bit space before we can use the kernel's GDT and IDT, which 191 * may be in the 64-bit address space, and since we're in compatability 192 * mode, we only have access to 16 and 32-bit instructions at the 193 * moment. 194 */ 195 addr32 lgdtl TEMPGDTOFF /* load temporary GDT */ 196 addr32 lidtl TEMPIDTOFF /* load temporary IDT */ 197 198 /* 199 * Do a far transfer to 64-bit mode. Set the CS selector to a 64-bit 200 * long mode selector (CS.L=1) in the temporary 32-bit GDT and jump 201 * to the real mode platter address of long_mode 64 as until the 64-bit 202 * CS is in place we don't have access to 64-bit instructions and thus 203 * can't reference a 64-bit %rip. 204 */ 205 pushl $TEMP_CS64_SEL 206 addr32 pushl LM64OFF 207 lretl 208 209 .globl long_mode_64 210 long_mode_64: 211 .code64 212 /* 213 * We are now running in long mode with a 64-bit CS (EFER.LMA=1, 214 * CS.L=1) so we now have access to 64-bit instructions. 215 * 216 * First, set the 64-bit GDT base. 217 */ 218 .globl rm_platter_pa 219 movl rm_platter_pa, %eax 220 lgdtq GDTROFF(%rax) /* load 64-bit GDT */ 221 222 /* 223 * Save the CPU number in %r11; get the value here since it's saved in 224 * the real mode platter. 225 */ 226 movl CPUNOFF(%rax), %r11d 227 228 /* 229 * Add rm_platter_pa to %rsp to point it to the same location as seen 230 * from 64-bit mode. 231 */ 232 addq %rax, %rsp 233 234 /* 235 * Now do an lretq to load CS with the appropriate selector for the 236 * kernel's 64-bit GDT and to start executing 64-bit setup code at the 237 * virtual address where boot originally loaded this code rather than 238 * the copy in the real mode platter's rm_code array as we've been 239 * doing so far. 240 */ 241 pushq $KCS_SEL 242 pushq $kernel_cs_code 243 lretq 244 .globl real_mode_start_cpu_end 245 real_mode_start_cpu_end: 246 nop 247 248 kernel_cs_code: 249 /* 250 * Complete the balance of the setup we need to before executing 251 * 64-bit kernel code (namely init rsp, TSS, LGDT, FS and GS). 252 */ 253 .globl rm_platter_va 254 movq rm_platter_va, %rax 255 lidtq IDTROFF(%rax) 256 257 movw $KDS_SEL, %ax 258 movw %ax, %ds 259 movw %ax, %es 260 movw %ax, %ss 261 262 movw $KTSS_SEL, %ax /* setup kernel TSS */ 263 ltr %ax 264 265 xorw %ax, %ax /* clear LDTR */ 266 lldt %ax 267 268 /* 269 * Set GS to the address of the per-cpu structure as contained in 270 * cpu[cpu_number]. 271 * 272 * Unfortunately there's no way to set the 64-bit gsbase with a mov, 273 * so we have to stuff the low 32 bits in %eax and the high 32 bits in 274 * %edx, then call wrmsr. 275 */ 276 leaq cpu(%rip), %rdi 277 movl (%rdi, %r11, 8), %eax 278 movl 4(%rdi, %r11, 8), %edx 279 movl $MSR_AMD_GSBASE, %ecx 280 wrmsr 281 282 /* 283 * Init FS and KernelGSBase. 284 * 285 * Based on code in mlsetup(), set them both to 8G (which shouldn't be 286 * valid until some 64-bit processes run); this will then cause an 287 * exception in any code that tries to index off them before they are 288 * properly setup. 289 */ 290 xorl %eax, %eax /* low 32 bits = 0 */ 291 movl $2, %edx /* high 32 bits = 2 */ 292 movl $MSR_AMD_FSBASE, %ecx 293 wrmsr 294 295 movl $MSR_AMD_KGSBASE, %ecx 296 wrmsr 297 298 /* 299 * Init %rsp to the exception stack set in tss_ist1 and create a legal 300 * AMD64 ABI stack frame 301 */ 302 movq %gs:CPU_TSS, %rax 303 movq TSS_IST1(%rax), %rsp 304 pushq $0 /* null return address */ 305 pushq $0 /* null frame pointer terminates stack trace */ 306 movq %rsp, %rbp /* stack aligned on 16-byte boundary */ 307 308 movq %cr0, %rax 309 andq $~(CR0_TS|CR0_EM), %rax /* clear emulate math chip bit */ 310 orq $(CR0_MP|CR0_NE), %rax 311 movq %rax, %cr0 /* set machine status word */ 312 313 /* 314 * Before going any further, enable usage of page table NX bit if 315 * that's how our page tables are set up. 316 */ 317 bt $X86FSET_NX, x86_featureset(%rip) 318 jnc 1f 319 movl $MSR_AMD_EFER, %ecx 320 rdmsr 321 orl $AMD_EFER_NXE, %eax 322 wrmsr 323 1: 324 325 /* 326 * Complete the rest of the setup and call mp_startup(). 327 */ 328 movq %gs:CPU_THREAD, %rax /* get thread ptr */ 329 call *T_PC(%rax) /* call mp_startup */ 330 /* not reached */ 331 int $20 /* whoops, returned somehow! */ 332 333 SET_SIZE(real_mode_start_cpu) 334 335 #elif defined(__i386) 336 337 ENTRY_NP(real_mode_start_cpu) 338 339 #if !defined(__GNUC_AS__) 340 341 cli 342 D16 movw %cs, %eax 343 movw %eax, %ds /* load cs into ds */ 344 movw %eax, %ss /* and into ss */ 345 346 /* 347 * Helps in debugging by giving us the fault address. 348 * 349 * Remember to patch a hlt (0xf4) at cmntrap to get a good stack. 350 */ 351 D16 movl $0xffc, %esp 352 353 D16 A16 lgdt %cs:GDTROFF 354 D16 A16 lidt %cs:IDTROFF 355 D16 A16 movl %cs:CR4OFF, %eax /* set up CR4, if desired */ 356 D16 andl %eax, %eax 357 D16 A16 je no_cr4 358 359 D16 movl %eax, %ecx 360 D16 movl %cr4, %eax 361 D16 orl %ecx, %eax 362 D16 movl %eax, %cr4 363 no_cr4: 364 D16 A16 movl %cs:CR3OFF, %eax 365 A16 movl %eax, %cr3 366 movl %cr0, %eax 367 368 /* 369 * Enable protected-mode, paging, write protect, and alignment mask 370 */ 371 D16 orl $[CR0_PG|CR0_PE|CR0_WP|CR0_AM], %eax 372 movl %eax, %cr0 373 jmp pestart 374 375 pestart: 376 D16 pushl $KCS_SEL 377 D16 pushl $kernel_cs_code 378 D16 lret 379 .globl real_mode_start_cpu_end 380 real_mode_start_cpu_end: 381 nop 382 383 .globl kernel_cs_code 384 kernel_cs_code: 385 /* 386 * At this point we are with kernel's cs and proper eip. 387 * 388 * We will be executing not from the copy in real mode platter, 389 * but from the original code where boot loaded us. 390 * 391 * By this time GDT and IDT are loaded as is cr3. 392 */ 393 movw $KFS_SEL,%eax 394 movw %eax,%fs 395 movw $KGS_SEL,%eax 396 movw %eax,%gs 397 movw $KDS_SEL,%eax 398 movw %eax,%ds 399 movw %eax,%es 400 movl %gs:CPU_TSS,%esi 401 movw %eax,%ss 402 movl TSS_ESP0(%esi),%esp 403 movw $KTSS_SEL,%ax 404 ltr %ax 405 xorw %ax, %ax /* clear LDTR */ 406 lldt %ax 407 movl %cr0,%edx 408 andl $-1![CR0_TS|CR0_EM],%edx /* clear emulate math chip bit */ 409 orl $[CR0_MP|CR0_NE],%edx 410 movl %edx,%cr0 /* set machine status word */ 411 412 /* 413 * Before going any further, enable usage of page table NX bit if 414 * that's how our page tables are set up. 415 */ 416 bt $X86FSET_NX, x86_featureset 417 jnc 1f 418 movl %cr4, %ecx 419 andl $CR4_PAE, %ecx 420 jz 1f 421 movl $MSR_AMD_EFER, %ecx 422 rdmsr 423 orl $AMD_EFER_NXE, %eax 424 wrmsr 425 1: 426 movl %gs:CPU_THREAD, %eax /* get thread ptr */ 427 call *T_PC(%eax) /* call mp_startup */ 428 /* not reached */ 429 int $20 /* whoops, returned somehow! */ 430 431 #else 432 433 cli 434 mov %cs, %ax 435 mov %eax, %ds /* load cs into ds */ 436 mov %eax, %ss /* and into ss */ 437 438 /* 439 * Helps in debugging by giving us the fault address. 440 * 441 * Remember to patch a hlt (0xf4) at cmntrap to get a good stack. 442 */ 443 D16 mov $0xffc, %esp 444 445 D16 A16 lgdtl %cs:GDTROFF 446 D16 A16 lidtl %cs:IDTROFF 447 D16 A16 mov %cs:CR4OFF, %eax /* set up CR4, if desired */ 448 D16 and %eax, %eax 449 D16 A16 je no_cr4 450 451 D16 mov %eax, %ecx 452 D16 mov %cr4, %eax 453 D16 or %ecx, %eax 454 D16 mov %eax, %cr4 455 no_cr4: 456 D16 A16 mov %cs:CR3OFF, %eax 457 A16 mov %eax, %cr3 458 mov %cr0, %eax 459 460 /* 461 * Enable protected-mode, paging, write protect, and alignment mask 462 */ 463 D16 or $(CR0_PG|CR0_PE|CR0_WP|CR0_AM), %eax 464 mov %eax, %cr0 465 jmp pestart 466 467 pestart: 468 D16 pushl $KCS_SEL 469 D16 pushl $kernel_cs_code 470 D16 lret 471 .globl real_mode_start_cpu_end 472 real_mode_start_cpu_end: 473 nop 474 .globl kernel_cs_code 475 kernel_cs_code: 476 /* 477 * At this point we are with kernel's cs and proper eip. 478 * 479 * We will be executing not from the copy in real mode platter, 480 * but from the original code where boot loaded us. 481 * 482 * By this time GDT and IDT are loaded as is cr3. 483 */ 484 mov $KFS_SEL, %ax 485 mov %eax, %fs 486 mov $KGS_SEL, %ax 487 mov %eax, %gs 488 mov $KDS_SEL, %ax 489 mov %eax, %ds 490 mov %eax, %es 491 mov %gs:CPU_TSS, %esi 492 mov %eax, %ss 493 mov TSS_ESP0(%esi), %esp 494 mov $(KTSS_SEL), %ax 495 ltr %ax 496 xorw %ax, %ax /* clear LDTR */ 497 lldt %ax 498 mov %cr0, %edx 499 and $~(CR0_TS|CR0_EM), %edx /* clear emulate math chip bit */ 500 or $(CR0_MP|CR0_NE), %edx 501 mov %edx, %cr0 /* set machine status word */ 502 503 /* 504 * Before going any farther, enable usage of page table NX bit if 505 * that's how our page tables are set up. 506 */ 507 bt $X86FSET_NX, x86_featureset 508 jnc 1f 509 movl %cr4, %ecx 510 andl $CR4_PAE, %ecx 511 jz 1f 512 movl $MSR_AMD_EFER, %ecx 513 rdmsr 514 orl $AMD_EFER_NXE, %eax 515 wrmsr 516 1: 517 mov %gs:CPU_THREAD, %eax /* get thread ptr */ 518 call *T_PC(%eax) /* call mp_startup */ 519 /* not reached */ 520 int $20 /* whoops, returned somehow! */ 521 #endif 522 523 SET_SIZE(real_mode_start_cpu) 524 525 #endif /* __amd64 */ 526 527 #if defined(__amd64) 528 529 ENTRY_NP(real_mode_stop_cpu_stage1) 530 531 #if !defined(__GNUC_AS__) 532 533 /* 534 * For vulcan as we need to do a .code32 and mentally invert the 535 * meaning of the addr16 and data16 prefixes to get 32-bit access when 536 * generating code to be executed in 16-bit mode (sigh...) 537 */ 538 .code32 539 cli 540 movw %cs, %ax 541 movw %ax, %ds /* load cs into ds */ 542 movw %ax, %ss /* and into ss */ 543 544 /* 545 * Jump to the stage 2 code in the rm_platter_va->rm_cpu_halt_code 546 */ 547 movw $CPUHALTCODEOFF, %ax 548 .byte 0xff, 0xe0 /* jmp *%ax */ 549 550 #else /* __GNUC_AS__ */ 551 552 /* 553 * NOTE: The GNU assembler automatically does the right thing to 554 * generate data size operand prefixes based on the code size 555 * generation mode (e.g. .code16, .code32, .code64) and as such 556 * prefixes need not be used on instructions EXCEPT in the case 557 * of address prefixes for code for which the reference is not 558 * automatically of the default operand size. 559 */ 560 .code16 561 cli 562 movw %cs, %ax 563 movw %ax, %ds /* load cs into ds */ 564 movw %ax, %ss /* and into ss */ 565 566 /* 567 * Jump to the stage 2 code in the rm_platter_va->rm_cpu_halt_code 568 */ 569 movw $CPUHALTCODEOFF, %ax 570 jmp *%ax 571 572 #endif /* !__GNUC_AS__ */ 573 574 .globl real_mode_stop_cpu_stage1_end 575 real_mode_stop_cpu_stage1_end: 576 nop 577 578 SET_SIZE(real_mode_stop_cpu_stage1) 579 580 #elif defined(__i386) 581 582 ENTRY_NP(real_mode_stop_cpu_stage1) 583 584 #if !defined(__GNUC_AS__) 585 586 cli 587 D16 movw %cs, %eax 588 movw %eax, %ds /* load cs into ds */ 589 movw %eax, %ss /* and into ss */ 590 591 /* 592 * Jump to the stage 2 code in the rm_platter_va->rm_cpu_halt_code 593 */ 594 movw $CPUHALTCODEOFF, %ax 595 .byte 0xff, 0xe0 /* jmp *%ax */ 596 597 #else /* __GNUC_AS__ */ 598 599 cli 600 mov %cs, %ax 601 mov %eax, %ds /* load cs into ds */ 602 mov %eax, %ss /* and into ss */ 603 604 /* 605 * Jump to the stage 2 code in the rm_platter_va->rm_cpu_halt_code 606 */ 607 movw $CPUHALTCODEOFF, %ax 608 jmp *%ax 609 610 #endif /* !__GNUC_AS__ */ 611 612 .globl real_mode_stop_cpu_stage1_end 613 real_mode_stop_cpu_stage1_end: 614 nop 615 616 SET_SIZE(real_mode_stop_cpu_stage1) 617 618 #endif /* __amd64 */ 619 620 ENTRY_NP(real_mode_stop_cpu_stage2) 621 622 movw $0xdead, %ax 623 movw %ax, CPUHALTEDOFF 624 625 real_mode_stop_cpu_loop: 626 /* 627 * Put CPU into halted state. 628 * Only INIT, SMI, NMI could break the loop. 629 */ 630 hlt 631 jmp real_mode_stop_cpu_loop 632 633 .globl real_mode_stop_cpu_stage2_end 634 real_mode_stop_cpu_stage2_end: 635 nop 636 637 SET_SIZE(real_mode_stop_cpu_stage2) 638 639 #endif /* __lint */