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) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <sys/asm_linkage.h> 26 #include <sys/asm_misc.h> 27 #include <sys/regset.h> 28 #include <sys/privregs.h> 29 #include <sys/x86_archext.h> 30 #include <sys/cpr_wakecode.h> 31 32 #if !defined(__lint) 33 #include <sys/segments.h> 34 #include "assym.h" 35 #endif 36 37 #ifdef DEBUG 38 #define LED 1 39 #define SERIAL 1 40 #endif /* DEBUG */ 41 42 #ifdef DEBUG 43 #define COM1 0x3f8 44 #define COM2 0x2f8 45 #define WC_COM COM2 /* either COM1 or COM2 */ 46 #define WC_LED 0x80 /* diagnostic led port ON motherboard */ 47 48 /* 49 * defined as offsets from the data register 50 */ 51 #define DLL 0 /* divisor latch (lsb) */ 52 #define DLH 1 /* divisor latch (msb) */ 53 #define LCR 3 /* line control register */ 54 #define MCR 4 /* modem control register */ 55 56 57 #define DLAB 0x80 /* divisor latch access bit */ 58 #define B9600L 0X0c /* lsb bit pattern for 9600 baud */ 59 #define B9600H 0X0 /* hsb bit pattern for 9600 baud */ 60 #define DTR 0x01 /* Data Terminal Ready */ 61 #define RTS 0x02 /* Request To Send */ 62 #define STOP1 0x00 /* 1 stop bit */ 63 #define BITS8 0x03 /* 8 bits per char */ 64 65 #endif /* DEBUG */ 66 67 /* 68 * This file contains the low level routines involved in getting 69 * into and out of ACPI S3, including those needed for restarting 70 * the non-boot cpus. 71 * 72 * Our assumptions: 73 * 74 * Our actions: 75 * 76 */ 77 78 #if defined(lint) || defined(__lint) 79 80 /*ARGSUSED*/ 81 int 82 wc_save_context(wc_cpu_t *pcpu) 83 { return 0; } 84 85 #else /* lint */ 86 87 #if defined(__amd64) 88 89 ENTRY_NP(wc_save_context) 90 91 movq (%rsp), %rdx / return address 92 movq %rdx, WC_RETADDR(%rdi) 93 pushq %rbp 94 movq %rsp,%rbp 95 96 movq %rdi, WC_VIRTADDR(%rdi) 97 movq %rdi, WC_RDI(%rdi) 98 99 movq %rdx, WC_RDX(%rdi) 100 101 / stash everything else we need 102 sgdt WC_GDT(%rdi) 103 sidt WC_IDT(%rdi) 104 sldt WC_LDT(%rdi) 105 str WC_TR(%rdi) 106 107 movq %cr0, %rdx 108 movq %rdx, WC_CR0(%rdi) 109 movq %cr3, %rdx 110 movq %rdx, WC_CR3(%rdi) 111 movq %cr4, %rdx 112 movq %rdx, WC_CR4(%rdi) 113 movq %cr8, %rdx 114 movq %rdx, WC_CR8(%rdi) 115 116 movq %r8, WC_R8(%rdi) 117 movq %r9, WC_R9(%rdi) 118 movq %r10, WC_R10(%rdi) 119 movq %r11, WC_R11(%rdi) 120 movq %r12, WC_R12(%rdi) 121 movq %r13, WC_R13(%rdi) 122 movq %r14, WC_R14(%rdi) 123 movq %r15, WC_R15(%rdi) 124 movq %rax, WC_RAX(%rdi) 125 movq %rbp, WC_RBP(%rdi) 126 movq %rbx, WC_RBX(%rdi) 127 movq %rcx, WC_RCX(%rdi) 128 movq %rsi, WC_RSI(%rdi) 129 movq %rsp, WC_RSP(%rdi) 130 131 movw %ss, WC_SS(%rdi) 132 movw %cs, WC_CS(%rdi) 133 movw %ds, WC_DS(%rdi) 134 movw %es, WC_ES(%rdi) 135 136 movq $0, %rcx / save %fs register 137 movw %fs, %cx 138 movq %rcx, WC_FS(%rdi) 139 140 movl $MSR_AMD_FSBASE, %ecx 141 rdmsr 142 movl %eax, WC_FSBASE(%rdi) 143 movl %edx, WC_FSBASE+4(%rdi) 144 145 movq $0, %rcx / save %gs register 146 movw %gs, %cx 147 movq %rcx, WC_GS(%rdi) 148 149 movl $MSR_AMD_GSBASE, %ecx / save gsbase msr 150 rdmsr 151 movl %eax, WC_GSBASE(%rdi) 152 movl %edx, WC_GSBASE+4(%rdi) 153 154 movl $MSR_AMD_KGSBASE, %ecx / save kgsbase msr 155 rdmsr 156 movl %eax, WC_KGSBASE(%rdi) 157 movl %edx, WC_KGSBASE+4(%rdi) 158 159 movq %gs:CPU_ID, %rax / save current cpu id 160 movq %rax, WC_CPU_ID(%rdi) 161 162 pushfq 163 popq WC_EFLAGS(%rdi) 164 165 wbinvd / flush the cache 166 mfence 167 168 movq $1, %rax / at suspend return 1 169 170 leave 171 172 ret 173 174 SET_SIZE(wc_save_context) 175 176 #elif defined(__i386) 177 178 ENTRY_NP(wc_save_context) 179 180 movl 4(%esp), %eax / wc_cpu_t * 181 movl %eax, WC_VIRTADDR(%eax) 182 183 movl (%esp), %edx / return address 184 movl %edx, WC_RETADDR(%eax) 185 186 str WC_TR(%eax) / stash everything else we need 187 sgdt WC_GDT(%eax) 188 sldt WC_LDT(%eax) 189 sidt WC_IDT(%eax) 190 191 movl %cr0, %edx 192 movl %edx, WC_CR0(%eax) 193 movl %cr3, %edx 194 movl %edx, WC_CR3(%eax) 195 movl %cr4, %edx 196 movl %edx, WC_CR4(%eax) 197 198 movl %ebx, WC_EBX(%eax) 199 movl %edi, WC_EDI(%eax) 200 movl %esi, WC_ESI(%eax) 201 movl %ebp, WC_EBP(%eax) 202 movl %esp, WC_ESP(%eax) 203 204 movw %ss, WC_SS(%eax) 205 movw %cs, WC_CS(%eax) 206 movw %ds, WC_DS(%eax) 207 movw %es, WC_ES(%eax) 208 movw %fs, WC_FS(%eax) 209 movw %gs, WC_GS(%eax) 210 211 pushfl 212 popl WC_EFLAGS(%eax) 213 214 pushl %gs:CPU_ID / save current cpu id 215 popl WC_CPU_ID(%eax) 216 217 wbinvd / flush the cache 218 mfence 219 220 movl $1, %eax / at suspend return 1 221 ret 222 223 SET_SIZE(wc_save_context) 224 225 #endif /* __amd64 */ 226 227 #endif /* lint */ 228 229 230 /* 231 * Our assumptions: 232 * - We are running in real mode. 233 * - Interrupts are disabled. 234 * 235 * Our actions: 236 * - We start using our GDT by loading correct values in the 237 * selector registers (cs=KCS_SEL, ds=es=ss=KDS_SEL, fs=KFS_SEL, 238 * gs=KGS_SEL). 239 * - We change over to using our IDT. 240 * - We load the default LDT into the hardware LDT register. 241 * - We load the default TSS into the hardware task register. 242 * - We restore registers 243 * - We return to original caller (a la setjmp) 244 */ 245 246 #if defined(lint) || defined(__lint) 247 248 void 249 wc_rm_start(void) 250 {} 251 252 void 253 wc_rm_end(void) 254 {} 255 256 #else /* lint */ 257 258 #if defined(__amd64) 259 260 ENTRY_NP(wc_rm_start) 261 262 /* 263 * For the Sun Studio 10 assembler we needed to do a .code32 and 264 * mentally invert the meaning of the addr16 and data16 prefixes to 265 * get 32-bit access when generating code to be executed in 16-bit 266 * mode (sigh...) 267 * 268 * This code, despite always being built with GNU as, has inherited 269 * the conceptual damage. 270 */ 271 272 .code32 273 274 cli 275 movw %cs, %ax 276 movw %ax, %ds / establish ds ... 277 movw %ax, %ss / ... and ss:esp 278 D16 movl $WC_STKSTART, %esp 279 / using the following value blows up machines! - DO NOT USE 280 / D16 movl 0xffc, %esp 281 282 283 #if LED 284 D16 movl $WC_LED, %edx 285 D16 movb $0xd1, %al 286 outb (%dx) 287 #endif 288 289 #if SERIAL 290 D16 movl $WC_COM, %edx 291 D16 movb $0x61, %al 292 outb (%dx) 293 #endif 294 295 D16 call cominit 296 297 /* 298 * Enable protected-mode, write protect, and alignment mask 299 * %cr0 has already been initialsed to zero 300 */ 301 movl %cr0, %eax 302 D16 orl $_CONST(CR0_PE|CR0_WP|CR0_AM), %eax 303 movl %eax, %cr0 304 305 /* 306 * Do a jmp immediately after writing to cr0 when enabling protected 307 * mode to clear the real mode prefetch queue (per Intel's docs) 308 */ 309 jmp pestart 310 pestart: 311 312 #if LED 313 D16 movl $WC_LED, %edx 314 D16 movb $0xd2, %al 315 outb (%dx) 316 #endif 317 318 #if SERIAL 319 D16 movl $WC_COM, %edx 320 D16 movb $0x62, %al 321 outb (%dx) 322 #endif 323 324 /* 325 * 16-bit protected mode is now active, so prepare to turn on long 326 * mode 327 */ 328 329 #if LED 330 D16 movl $WC_LED, %edx 331 D16 movb $0xd3, %al 332 outb (%dx) 333 #endif 334 335 #if SERIAL 336 D16 movl $WC_COM, %edx 337 D16 movb $0x63, %al 338 outb (%dx) 339 #endif 340 341 /* 342 * Add any initial cr4 bits 343 */ 344 movl %cr4, %eax 345 A16 D16 orl CR4OFF, %eax 346 347 /* 348 * Enable PAE mode (CR4.PAE) 349 */ 350 D16 orl $CR4_PAE, %eax 351 movl %eax, %cr4 352 353 #if LED 354 D16 movl $WC_LED, %edx 355 D16 movb $0xd4, %al 356 outb (%dx) 357 #endif 358 359 #if SERIAL 360 D16 movl $WC_COM, %edx 361 D16 movb $0x64, %al 362 outb (%dx) 363 #endif 364 365 /* 366 * Point cr3 to the 64-bit long mode page tables. 367 * 368 * Note that these MUST exist in 32-bit space, as we don't have 369 * a way to load %cr3 with a 64-bit base address for the page tables 370 * until the CPU is actually executing in 64-bit long mode. 371 */ 372 A16 D16 movl CR3OFF, %eax 373 movl %eax, %cr3 374 375 /* 376 * Set long mode enable in EFER (EFER.LME = 1) 377 */ 378 D16 movl $MSR_AMD_EFER, %ecx 379 rdmsr 380 381 D16 orl $AMD_EFER_LME, %eax 382 wrmsr 383 384 #if LED 385 D16 movl $WC_LED, %edx 386 D16 movb $0xd5, %al 387 outb (%dx) 388 #endif 389 390 #if SERIAL 391 D16 movl $WC_COM, %edx 392 D16 movb $0x65, %al 393 outb (%dx) 394 #endif 395 396 /* 397 * Finally, turn on paging (CR0.PG = 1) to activate long mode. 398 */ 399 movl %cr0, %eax 400 D16 orl $CR0_PG, %eax 401 movl %eax, %cr0 402 403 /* 404 * The instruction after enabling paging in CR0 MUST be a branch. 405 */ 406 jmp long_mode_active 407 408 long_mode_active: 409 410 #if LED 411 D16 movl $WC_LED, %edx 412 D16 movb $0xd6, %al 413 outb (%dx) 414 #endif 415 416 #if SERIAL 417 D16 movl $WC_COM, %edx 418 D16 movb $0x66, %al 419 outb (%dx) 420 #endif 421 422 /* 423 * Long mode is now active but since we're still running with the 424 * original 16-bit CS we're actually in 16-bit compatability mode. 425 * 426 * We have to load an intermediate GDT and IDT here that we know are 427 * in 32-bit space before we can use the kernel's GDT and IDT, which 428 * may be in the 64-bit address space, and since we're in compatability 429 * mode, we only have access to 16 and 32-bit instructions at the 430 * moment. 431 */ 432 A16 D16 lgdt TEMPGDTOFF /* load temporary GDT */ 433 A16 D16 lidt TEMPIDTOFF /* load temporary IDT */ 434 435 436 /* 437 * Do a far transfer to 64-bit mode. Set the CS selector to a 64-bit 438 * long mode selector (CS.L=1) in the temporary 32-bit GDT and jump 439 * to the real mode platter address of wc_long_mode_64 as until the 440 * 64-bit CS is in place we don't have access to 64-bit instructions 441 * and thus can't reference a 64-bit %rip. 442 */ 443 444 #if LED 445 D16 movl $WC_LED, %edx 446 D16 movb $0xd7, %al 447 outb (%dx) 448 #endif 449 450 #if SERIAL 451 D16 movl $WC_COM, %edx 452 D16 movb $0x67, %al 453 outb (%dx) 454 #endif 455 456 D16 pushl $TEMP_CS64_SEL 457 A16 D16 pushl LM64OFF 458 459 D16 lret 460 461 462 /* 463 * Support routine to re-initialize VGA subsystem 464 */ 465 vgainit: 466 D16 ret 467 468 /* 469 * Support routine to re-initialize keyboard (which is USB - help!) 470 */ 471 kbdinit: 472 D16 ret 473 474 /* 475 * Support routine to re-initialize COM ports to something sane 476 */ 477 cominit: 478 / init COM1 & COM2 479 480 #if DEBUG 481 /* 482 * on debug kernels we need to initialize COM1 & COM2 here, so that 483 * we can get debug output before the asy driver has resumed 484 */ 485 486 / select COM1 487 D16 movl $_CONST(COM1+LCR), %edx 488 D16 movb $DLAB, %al / divisor latch 489 outb (%dx) 490 491 D16 movl $_CONST(COM1+DLL), %edx / divisor latch lsb 492 D16 movb $B9600L, %al / divisor latch 493 outb (%dx) 494 495 D16 movl $_CONST(COM1+DLH), %edx / divisor latch hsb 496 D16 movb $B9600H, %al / divisor latch 497 outb (%dx) 498 499 D16 movl $_CONST(COM1+LCR), %edx / select COM1 500 D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len 501 outb (%dx) 502 503 D16 movl $_CONST(COM1+MCR), %edx / select COM1 504 D16 movb $_CONST(RTS|DTR), %al / data term ready & req to send 505 outb (%dx) 506 507 / select COM2 508 D16 movl $_CONST(COM2+LCR), %edx 509 D16 movb $DLAB, %al / divisor latch 510 outb (%dx) 511 512 D16 movl $_CONST(COM2+DLL), %edx / divisor latch lsb 513 D16 movb $B9600L, %al / divisor latch 514 outb (%dx) 515 516 D16 movl $_CONST(COM2+DLH), %edx / divisor latch hsb 517 D16 movb $B9600H, %al / divisor latch 518 outb (%dx) 519 520 D16 movl $_CONST(COM2+LCR), %edx / select COM1 521 D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len 522 outb (%dx) 523 524 D16 movl $_CONST(COM2+MCR), %edx / select COM1 525 D16 movb $_CONST(RTS|DTR), %al / data term ready & req to send 526 outb (%dx) 527 #endif /* DEBUG */ 528 529 D16 ret 530 531 .code64 532 533 .globl wc_long_mode_64 534 wc_long_mode_64: 535 536 #if LED 537 movw $WC_LED, %dx 538 movb $0xd8, %al 539 outb (%dx) 540 #endif 541 542 #if SERIAL 543 movw $WC_COM, %dx 544 movb $0x68, %al 545 outb (%dx) 546 #endif 547 548 /* 549 * We are now running in long mode with a 64-bit CS (EFER.LMA=1, 550 * CS.L=1) so we now have access to 64-bit instructions. 551 * 552 * First, set the 64-bit GDT base. 553 */ 554 .globl rm_platter_pa 555 movl rm_platter_pa, %eax 556 557 lgdtq GDTROFF(%rax) /* load 64-bit GDT */ 558 559 /* 560 * Save the CPU number in %r11; get the value here since it's saved in 561 * the real mode platter. 562 */ 563 / JAN 564 / the following is wrong! need to figure out MP systems 565 / movl CPUNOFF(%rax), %r11d 566 567 /* 568 * Add rm_platter_pa to %rsp to point it to the same location as seen 569 * from 64-bit mode. 570 */ 571 addq %rax, %rsp 572 573 /* 574 * Now do an lretq to load CS with the appropriate selector for the 575 * kernel's 64-bit GDT and to start executing 64-bit setup code at the 576 * virtual address where boot originally loaded this code rather than 577 * the copy in the real mode platter's rm_code array as we've been 578 * doing so far. 579 */ 580 581 #if LED 582 movw $WC_LED, %dx 583 movb $0xd9, %al 584 outb (%dx) 585 #endif 586 587 / JAN this should produce 'i' but we get 'g' instead ??? 588 #if SERIAL 589 movw $WC_COM, %dx 590 movb $0x69, %al 591 outb (%dx) 592 #endif 593 594 pushq $KCS_SEL 595 pushq $kernel_wc_code 596 lretq 597 598 .globl kernel_wc_code 599 kernel_wc_code: 600 601 #if LED 602 movw $WC_LED, %dx 603 movb $0xda, %al 604 outb (%dx) 605 #endif 606 607 / JAN this should produce 'j' but we get 'g' instead ??? 608 #if SERIAL 609 movw $WC_COM, %dx 610 movb $0x6a, %al 611 outb (%dx) 612 #endif 613 614 /* 615 * Complete the balance of the setup we need to before executing 616 * 64-bit kernel code (namely init rsp, TSS, LGDT, FS and GS). 617 */ 618 .globl rm_platter_va 619 movq rm_platter_va, %rbx 620 addq $WC_CPU, %rbx 621 622 #if LED 623 movw $WC_LED, %dx 624 movb $0xdb, %al 625 outb (%dx) 626 #endif 627 628 #if SERIAL 629 movw $WC_COM, %dx 630 movw $0x6b, %ax 631 outb (%dx) 632 #endif 633 634 /* 635 * restore the rest of the registers 636 */ 637 638 lidtq WC_IDT(%rbx) 639 640 #if LED 641 movw $WC_LED, %dx 642 movb $0xdc, %al 643 outb (%dx) 644 #endif 645 646 #if SERIAL 647 movw $WC_COM, %dx 648 movw $0x6c, %ax 649 outb (%dx) 650 #endif 651 652 /* 653 * restore the rest of the registers 654 */ 655 656 movw $KDS_SEL, %ax 657 movw %ax, %ds 658 movw %ax, %es 659 movw %ax, %ss 660 661 /* 662 * Before proceeding, enable usage of the page table NX bit if 663 * that's how the page tables are set up. 664 */ 665 bt $X86FSET_NX, x86_featureset(%rip) 666 jnc 1f 667 movl $MSR_AMD_EFER, %ecx 668 rdmsr 669 orl $AMD_EFER_NXE, %eax 670 wrmsr 671 1: 672 673 movq WC_CR4(%rbx), %rax / restore full cr4 (with Global Enable) 674 movq %rax, %cr4 675 676 lldt WC_LDT(%rbx) 677 movzwq WC_TR(%rbx), %rax / clear TSS busy bit 678 addq WC_GDT+2(%rbx), %rax 679 andl $0xfffffdff, 4(%rax) 680 movq 4(%rax), %rcx 681 ltr WC_TR(%rbx) 682 683 #if LED 684 movw $WC_LED, %dx 685 movb $0xdd, %al 686 outb (%dx) 687 #endif 688 689 #if SERIAL 690 movw $WC_COM, %dx 691 movw $0x6d, %ax 692 outb (%dx) 693 #endif 694 695 / restore %fsbase %gsbase %kgbase registers using wrmsr instruction 696 697 movq WC_FS(%rbx), %rcx / restore fs register 698 movw %cx, %fs 699 700 movl $MSR_AMD_FSBASE, %ecx 701 movl WC_FSBASE(%rbx), %eax 702 movl WC_FSBASE+4(%rbx), %edx 703 wrmsr 704 705 movq WC_GS(%rbx), %rcx / restore gs register 706 movw %cx, %gs 707 708 movl $MSR_AMD_GSBASE, %ecx / restore gsbase msr 709 movl WC_GSBASE(%rbx), %eax 710 movl WC_GSBASE+4(%rbx), %edx 711 wrmsr 712 713 movl $MSR_AMD_KGSBASE, %ecx / restore kgsbase msr 714 movl WC_KGSBASE(%rbx), %eax 715 movl WC_KGSBASE+4(%rbx), %edx 716 wrmsr 717 718 movq WC_CR0(%rbx), %rdx 719 movq %rdx, %cr0 720 movq WC_CR3(%rbx), %rdx 721 movq %rdx, %cr3 722 movq WC_CR8(%rbx), %rdx 723 movq %rdx, %cr8 724 725 #if LED 726 movw $WC_LED, %dx 727 movb $0xde, %al 728 outb (%dx) 729 #endif 730 731 #if SERIAL 732 movw $WC_COM, %dx 733 movb $0x6e, %al 734 outb (%dx) 735 #endif 736 737 /* 738 * if we are not running on the boot CPU restore stack contents by 739 * calling i_cpr_restore_stack(curthread, save_stack); 740 */ 741 movq %rsp, %rbp 742 call i_cpr_bootcpuid 743 cmpl %eax, WC_CPU_ID(%rbx) 744 je 2f 745 746 movq %gs:CPU_THREAD, %rdi 747 movq WC_SAVED_STACK(%rbx), %rsi 748 call i_cpr_restore_stack 749 2: 750 751 movq WC_RSP(%rbx), %rsp / restore stack pointer 752 753 /* 754 * APIC initialization 755 */ 756 movq %rsp, %rbp 757 758 /* 759 * skip iff function pointer is NULL 760 */ 761 cmpq $0, ap_mlsetup 762 je 3f 763 call *ap_mlsetup 764 3: 765 766 call *cpr_start_cpu_func 767 768 / restore %rbx to the value it ahd before we called the functions above 769 movq rm_platter_va, %rbx 770 addq $WC_CPU, %rbx 771 772 movq WC_R8(%rbx), %r8 773 movq WC_R9(%rbx), %r9 774 movq WC_R10(%rbx), %r10 775 movq WC_R11(%rbx), %r11 776 movq WC_R12(%rbx), %r12 777 movq WC_R13(%rbx), %r13 778 movq WC_R14(%rbx), %r14 779 movq WC_R15(%rbx), %r15 780 / movq WC_RAX(%rbx), %rax 781 movq WC_RBP(%rbx), %rbp 782 movq WC_RCX(%rbx), %rcx 783 / movq WC_RDX(%rbx), %rdx 784 movq WC_RDI(%rbx), %rdi 785 movq WC_RSI(%rbx), %rsi 786 787 788 / assume that %cs does not need to be restored 789 / %ds, %es & %ss are ignored in 64bit mode 790 movw WC_SS(%rbx), %ss 791 movw WC_DS(%rbx), %ds 792 movw WC_ES(%rbx), %es 793 794 #if LED 795 movw $WC_LED, %dx 796 movb $0xdf, %al 797 outb (%dx) 798 #endif 799 800 #if SERIAL 801 movw $WC_COM, %dx 802 movb $0x6f, %al 803 outb (%dx) 804 #endif 805 806 807 movq WC_RBP(%rbx), %rbp 808 movq WC_RSP(%rbx), %rsp 809 810 #if LED 811 movw $WC_LED, %dx 812 movb $0xe0, %al 813 outb (%dx) 814 #endif 815 816 #if SERIAL 817 movw $WC_COM, %dx 818 movb $0x70, %al 819 outb (%dx) 820 #endif 821 822 823 movq WC_RCX(%rbx), %rcx 824 825 pushq WC_EFLAGS(%rbx) / restore flags 826 popfq 827 828 #if LED 829 movw $WC_LED, %dx 830 movb $0xe1, %al 831 outb (%dx) 832 #endif 833 834 #if SERIAL 835 movw $WC_COM, %dx 836 movb $0x71, %al 837 outb (%dx) 838 #endif 839 840 /* 841 * can not use outb after this point, because doing so would mean using 842 * %dx which would modify %rdx which is restored here 843 */ 844 845 movq %rbx, %rax 846 movq WC_RDX(%rax), %rdx 847 movq WC_RBX(%rax), %rbx 848 849 leave 850 851 movq WC_RETADDR(%rax), %rax 852 movq %rax, (%rsp) / return to caller of wc_save_context 853 854 xorl %eax, %eax / at wakeup return 0 855 ret 856 857 858 SET_SIZE(wc_rm_start) 859 860 ENTRY_NP(asmspin) 861 862 movl %edi, %ecx 863 A1: 864 loop A1 865 866 SET_SIZE(asmspin) 867 868 .globl wc_rm_end 869 wc_rm_end: 870 nop 871 872 #elif defined(__i386) 873 874 ENTRY_NP(wc_rm_start) 875 876 /entry: jmp entry / stop here for HDT 877 878 cli 879 movw %cs, %ax 880 movw %ax, %ds / establish ds ... 881 movw %ax, %ss / ... and ss:esp 882 D16 movl $WC_STKSTART, %esp 883 884 #if LED 885 D16 movl $WC_LED, %edx 886 D16 movb $0xd1, %al 887 outb (%dx) 888 #endif 889 890 #if SERIAL 891 D16 movl $WC_COM, %edx 892 D16 movb $0x61, %al 893 outb (%dx) 894 #endif 895 896 897 D16 call vgainit 898 D16 call kbdinit 899 D16 call cominit 900 901 #if LED 902 D16 movl $WC_LED, %edx 903 D16 movb $0xd2, %al 904 outb (%dx) 905 #endif 906 907 #if SERIAL 908 D16 movl $WC_COM, %edx 909 D16 movb $0x62, %al 910 outb (%dx) 911 #endif 912 913 D16 A16 movl $WC_CPU, %ebx / base add of wc_cpu_t 914 915 #if LED 916 D16 movb $0xd3, %al 917 outb $WC_LED 918 #endif 919 920 #if SERIAL 921 D16 movl $WC_COM, %edx 922 D16 movb $0x63, %al 923 outb (%dx) 924 #endif 925 926 D16 A16 movl %cs:WC_DS(%ebx), %edx / %ds post prot/paging transit 927 928 #if LED 929 D16 movb $0xd4, %al 930 outb $WC_LED 931 #endif 932 933 D16 A16 lgdt %cs:WC_GDT(%ebx) / restore gdt and idtr 934 D16 A16 lidt %cs:WC_IDT(%ebx) 935 936 #if LED 937 D16 movb $0xd5, %al 938 outb $WC_LED 939 #endif 940 941 D16 A16 movl %cs:WC_CR4(%ebx), %eax / restore cr4 942 D16 andl $_BITNOT(CR4_PGE), %eax / don't set Global Enable yet 943 movl %eax, %cr4 944 945 #if LED 946 D16 movb $0xd6, %al 947 outb $WC_LED 948 #endif 949 950 D16 A16 movl %cs:WC_CR3(%ebx), %eax / set PDPT 951 movl %eax, %cr3 952 953 #if LED 954 D16 movb $0xd7, %al 955 outb $WC_LED 956 #endif 957 958 D16 A16 movl %cs:WC_CR0(%ebx), %eax / enable prot/paging, etc. 959 movl %eax, %cr0 960 961 #if LED 962 D16 movb $0xd8, %al 963 outb $WC_LED 964 #endif 965 966 D16 A16 movl %cs:WC_VIRTADDR(%ebx), %ebx / virtaddr of wc_cpu_t 967 968 #if LED 969 D16 movb $0xd9, %al 970 outb $WC_LED 971 #endif 972 973 #if LED 974 D16 movb $0xda, %al 975 outb $WC_LED 976 #endif 977 978 jmp flush / flush prefetch queue 979 flush: 980 D16 pushl $KCS_SEL 981 D16 pushl $kernel_wc_code 982 D16 lret / re-appear at kernel_wc_code 983 984 985 /* 986 * Support routine to re-initialize VGA subsystem 987 */ 988 vgainit: 989 D16 ret 990 991 /* 992 * Support routine to re-initialize keyboard (which is USB - help!) 993 */ 994 kbdinit: 995 D16 ret 996 997 /* 998 * Support routine to re-initialize COM ports to something sane for debug output 999 */ 1000 cominit: 1001 #if DEBUG 1002 /* 1003 * on debug kernels we need to initialize COM1 & COM2 here, so that 1004 * we can get debug output before the asy driver has resumed 1005 */ 1006 1007 / select COM1 1008 D16 movl $_CONST(COM1+LCR), %edx 1009 D16 movb $DLAB, %al / divisor latch 1010 outb (%dx) 1011 1012 D16 movl $_CONST(COM1+DLL), %edx / divisor latch lsb 1013 D16 movb $B9600L, %al / divisor latch 1014 outb (%dx) 1015 1016 D16 movl $_CONST(COM1+DLH), %edx / divisor latch hsb 1017 D16 movb $B9600H, %al / divisor latch 1018 outb (%dx) 1019 1020 D16 movl $_CONST(COM1+LCR), %edx / select COM1 1021 D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len 1022 outb (%dx) 1023 1024 D16 movl $_CONST(COM1+MCR), %edx / select COM1 1025 D16 movb $_CONST(RTS|DTR), %al / 1 stop bit, 8bit word len 1026 outb (%dx) 1027 1028 / select COM2 1029 D16 movl $_CONST(COM2+LCR), %edx 1030 D16 movb $DLAB, %al / divisor latch 1031 outb (%dx) 1032 1033 D16 movl $_CONST(COM2+DLL), %edx / divisor latch lsb 1034 D16 movb $B9600L, %al / divisor latch 1035 outb (%dx) 1036 1037 D16 movl $_CONST(COM2+DLH), %edx / divisor latch hsb 1038 D16 movb $B9600H, %al / divisor latch 1039 outb (%dx) 1040 1041 D16 movl $_CONST(COM2+LCR), %edx / select COM1 1042 D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len 1043 outb (%dx) 1044 1045 D16 movl $_CONST(COM2+MCR), %edx / select COM1 1046 D16 movb $_CONST(RTS|DTR), %al / 1 stop bit, 8bit word len 1047 outb (%dx) 1048 #endif /* DEBUG */ 1049 1050 D16 ret 1051 1052 .globl wc_rm_end 1053 wc_rm_end: 1054 nop 1055 1056 .globl kernel_wc_code 1057 kernel_wc_code: 1058 / At this point we are with kernel's cs and proper eip. 1059 / We will be executing not from the copy in real mode platter, 1060 / but from the original code where boot loaded us. 1061 / By this time GDT and IDT are loaded as is cr0, cr3 and cr4. 1062 / %ebx is wc_cpu 1063 / %dx is our ds 1064 1065 #if LED 1066 D16 movb $0xdb, %al 1067 outb $WC_LED 1068 #endif 1069 1070 / got here OK 1071 1072 movw %dx, %ds / $KDS_SEL 1073 1074 #if LED 1075 movb $0xdc, %al 1076 outb $WC_LED 1077 #endif 1078 1079 /* 1080 * Before proceeding, enable usage of the page table NX bit if 1081 * that's how the page tables are set up. 1082 */ 1083 bt $X86FSET_NX, x86_featureset 1084 jnc 1f 1085 movl $MSR_AMD_EFER, %ecx 1086 rdmsr 1087 orl $AMD_EFER_NXE, %eax 1088 wrmsr 1089 1: 1090 1091 movl WC_CR4(%ebx), %eax / restore full cr4 (with Global Enable) 1092 movl %eax, %cr4 1093 1094 1095 lldt WC_LDT(%ebx) / $LDT_SEL 1096 1097 movzwl WC_TR(%ebx), %eax / clear TSS busy bit 1098 addl WC_GDT+2(%ebx), %eax 1099 andl $_BITNOT(0x200), 4(%eax) 1100 ltr WC_TR(%ebx) / $UTSS_SEL 1101 1102 movw WC_SS(%ebx), %ss / restore segment registers 1103 movw WC_ES(%ebx), %es 1104 movw WC_FS(%ebx), %fs 1105 movw WC_GS(%ebx), %gs 1106 1107 /* 1108 * set the stack pointer to point into the identity mapped page 1109 * temporarily, so we can make function calls 1110 */ 1111 .globl rm_platter_va 1112 movl rm_platter_va, %eax 1113 movl $WC_STKSTART, %esp 1114 addl %eax, %esp 1115 movl %esp, %ebp 1116 1117 /* 1118 * if we are not running on the boot CPU restore stack contents by 1119 * calling i_cpr_restore_stack(curthread, save_stack); 1120 */ 1121 call i_cpr_bootcpuid 1122 cmpl %eax, WC_CPU_ID(%ebx) 1123 je 2f 1124 1125 pushl WC_SAVED_STACK(%ebx) 1126 pushl %gs:CPU_THREAD 1127 call i_cpr_restore_stack 1128 addl $0x10, %esp 1129 2: 1130 1131 movl WC_ESP(%ebx), %esp 1132 movl %esp, %ebp 1133 1134 movl WC_RETADDR(%ebx), %eax / return to caller of wc_save_context 1135 movl %eax, (%esp) 1136 1137 /* 1138 * APIC initialization, skip iff function pointer is NULL 1139 */ 1140 cmpl $0, ap_mlsetup 1141 je 3f 1142 call *ap_mlsetup 1143 3: 1144 1145 call *cpr_start_cpu_func 1146 1147 pushl WC_EFLAGS(%ebx) / restore flags 1148 popfl 1149 1150 movl WC_EDI(%ebx), %edi / restore general registers 1151 movl WC_ESI(%ebx), %esi 1152 movl WC_EBP(%ebx), %ebp 1153 movl WC_EBX(%ebx), %ebx 1154 1155 /exit: jmp exit / stop here for HDT 1156 1157 xorl %eax, %eax / at wakeup return 0 1158 ret 1159 1160 SET_SIZE(wc_rm_start) 1161 1162 1163 #endif /* defined(__amd64) */ 1164 1165 #endif /* lint */ 1166