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