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