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