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 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #if defined(__lint)
  30 
  31 int silence_lint = 0;
  32 
  33 #else
  34 
  35 #include <sys/segments.h>
  36 #include <sys/controlregs.h>
  37 
  38 /*
  39  * Do a call into BIOS.  This goes down to 16 bit real mode and back again.
  40  */
  41 
  42 /*
  43  * instruction prefix to change operand size in instruction
  44  */
  45 #define DATASZ  .byte 0x66;
  46 
  47 #if defined(__amd64)
  48 #define MOVCR(x, y)     movq  x,%rax; movq  %rax, y
  49 #define LOAD_XAX(sym)   leaq    sym, %rax
  50 #elif defined(__i386)
  51 #define MOVCR(x, y)     movl  x,%eax; movl  %eax, y
  52 #define LOAD_XAX(sym)   leal    sym, %eax
  53 #endif
  54 
  55         .globl  _start
  56 _start:
  57 
  58 #if defined(__i386)
  59 
  60         /*
  61          * Save caller registers
  62          */
  63         movl    %ebp, save_ebp
  64         movl    %esp, save_esp
  65         movl    %ebx, save_ebx
  66         movl    %esi, save_esi
  67         movl    %edi, save_edi
  68 
  69         /* get registers argument into esi */
  70         movl    8(%esp), %esi
  71 
  72         /* put interrupt number in %bl */
  73         movl    4(%esp), %ebx
  74 
  75         /* Switch to a low memory stack */
  76         movl    $_start, %esp
  77 
  78         /* allocate space for args on stack */
  79         subl    $18, %esp
  80         movl    %esp, %edi
  81 
  82 #elif defined(__amd64)
  83 
  84         /*
  85          * Save caller registers
  86          */
  87         movq    %rbp, save_rbp
  88         movq    %rsp, save_rsp
  89         movq    %rbx, save_rbx
  90         movq    %rsi, save_rsi
  91         movq    %r12, save_r12
  92         movq    %r13, save_r13
  93         movq    %r14, save_r14
  94         movq    %r15, save_r15
  95 
  96         /* Switch to a low memory stack */
  97         movq    $_start, %rsp
  98 
  99         /* put interrupt number in %bl */
 100         movq    %rdi, %rbx
 101 
 102         /* allocate space for args on stack */
 103         subq    $18, %rsp
 104         movq    %rsp, %rdi
 105 
 106 #endif
 107 
 108         /* copy args from high memory to stack in low memory */
 109         cld
 110         movl    $18, %ecx
 111         rep
 112         movsb
 113 
 114         /*
 115          * Save system registers
 116          */
 117         sidt    save_idt
 118         sgdt    save_gdt
 119         str     save_tr
 120         movw    %cs, save_cs
 121         movw    %ds, save_ds
 122         movw    %ss, save_ss
 123         movw    %es, save_es
 124         movw    %fs, save_fs
 125         movw    %gs, save_gs
 126         MOVCR(  %cr4, save_cr4)
 127         MOVCR(  %cr3, save_cr3)
 128         MOVCR(  %cr0, save_cr0)
 129 
 130 #if defined(__amd64)
 131         /*
 132          * save/clear the extension parts of the fs/gs base registers and cr8
 133          */
 134         movl    $MSR_AMD_FSBASE, %ecx
 135         rdmsr
 136         movl    %eax, save_fsbase
 137         movl    %edx, save_fsbase + 4
 138         xorl    %eax, %eax
 139         xorl    %edx, %edx
 140         wrmsr
 141 
 142         movl    $MSR_AMD_GSBASE, %ecx
 143         rdmsr
 144         movl    %eax, save_gsbase
 145         movl    %edx, save_gsbase + 4
 146         xorl    %eax, %eax
 147         xorl    %edx, %edx
 148         wrmsr
 149 
 150         movl    $MSR_AMD_KGSBASE, %ecx
 151         rdmsr
 152         movl    %eax, save_kgsbase
 153         movl    %edx, save_kgsbase + 4
 154         xorl    %eax, %eax
 155         xorl    %edx, %edx
 156         wrmsr
 157 
 158         movq    %cr8, %rax
 159         movq    %rax, save_cr8
 160 #endif
 161 
 162         /*
 163          * set offsets in 16 bit ljmp instructions below
 164          */
 165         LOAD_XAX(enter_real)
 166         movw    %ax, enter_real_ljmp
 167 
 168         LOAD_XAX(enter_protected)
 169         movw    %ax, enter_protected_ljmp
 170 
 171         LOAD_XAX(gdt_info)
 172         movw    %ax, gdt_info_load
 173 
 174         /*
 175          * insert BIOS interrupt number into later instruction
 176          */
 177         movb    %bl, int_instr+1
 178         jmp     1f
 179 1:
 180 
 181         /*
 182          * zero out all the registers to make sure they're 16 bit clean
 183          */
 184 #if defined(__amd64)
 185         xorq    %r8, %r8
 186         xorq    %r9, %r9
 187         xorq    %r10, %r10
 188         xorq    %r11, %r11
 189         xorq    %r12, %r12
 190         xorq    %r13, %r13
 191         xorq    %r14, %r14
 192         xorq    %r15, %r15
 193 #endif
 194         xorl    %eax, %eax
 195         xorl    %ebx, %ebx
 196         xorl    %ecx, %ecx
 197         xorl    %edx, %edx
 198         xorl    %ebp, %ebp
 199         xorl    %esi, %esi
 200         xorl    %edi, %edi
 201 
 202         /*
 203          * Load our own GDT/IDT
 204          */
 205         lgdt    gdt_info
 206         lidt    idt_info
 207 
 208 #if defined(__amd64)
 209         /*
 210          * Shut down 64 bit mode. First get into compatiblity mode.
 211          */
 212         movq    %rsp, %rax
 213         pushq   $B32DATA_SEL
 214         pushq   %rax
 215         pushf
 216         pushq   $B32CODE_SEL
 217         pushq   $1f
 218         iretq
 219 1:
 220         .code32
 221 
 222         /*
 223          * disable long mode by:
 224          * - shutting down paging (bit 31 of cr0)
 225          * - flushing the TLB
 226          * - disabling LME (long made enable) in EFER (extended feature reg)
 227          */
 228         movl    %cr0, %eax
 229         btcl    $31, %eax               /* disable paging */
 230         movl    %eax, %cr0
 231         ljmp    $B32CODE_SEL, $1f
 232 1:
 233 
 234         xorl    %eax, %eax
 235         movl    %eax, %cr3              /* flushes TLB */
 236 
 237         movl    $MSR_AMD_EFER, %ecx     /* Extended Feature Enable */
 238         rdmsr
 239         btcl    $8, %eax                /* bit 8 Long Mode Enable bit */
 240         wrmsr
 241 #endif
 242 
 243         /*
 244          * ok.. now enter 16 bit mode, so we can shut down protected mode
 245          *
 246          * We'll have to act like we're still in a 32 bit section.
 247          * So the code from this point has DATASZ in front of it to get 32 bit
 248          * operands. If DATASZ is missing the operands will be 16 bit.
 249          *
 250          * Now shut down paging and protected (ie. segmentation) modes.
 251          */
 252         ljmp    $B16CODE_SEL, $enter_16_bit
 253 enter_16_bit:
 254 
 255         /*
 256          * Make sure hidden parts of segment registers are 16 bit clean
 257          */
 258         DATASZ  movl    $B16DATA_SEL, %eax
 259                 movw    %ax, %ss
 260                 movw    %ax, %ds
 261                 movw    %ax, %es
 262                 movw    %ax, %fs
 263                 movw    %ax, %gs
 264 
 265 
 266         DATASZ  movl    $0x0, %eax      /* put us in real mode */
 267         DATASZ  movl    %eax, %cr0
 268         .byte   0xea                    /* ljmp */
 269 enter_real_ljmp:
 270         .value  0                       /* addr (16 bit) */
 271         .value  0x0                     /* value for %cs */
 272 enter_real:
 273 
 274         /*
 275          * zero out the remaining segment registers
 276          */
 277         DATASZ  xorl    %eax, %eax
 278                 movw    %ax, %ss
 279                 movw    %ax, %ds
 280                 movw    %ax, %es
 281                 movw    %ax, %fs
 282                 movw    %ax, %gs
 283 
 284         /*
 285          * load the arguments to the BIOS call from the stack
 286          */
 287         popl    %eax    /* really executes a 16 bit pop */
 288         popl    %ebx
 289         popl    %ecx
 290         popl    %edx
 291         popl    %esi
 292         popl    %edi
 293         popl    %ebp
 294         pop     %es
 295         pop     %ds
 296 
 297         /*
 298          * do the actual BIOS call
 299          */
 300         sti
 301 int_instr:
 302         int     $0x10           /* this int number is overwritten */
 303         cli                     /* ensure interrupts remain disabled */
 304 
 305         /*
 306          * save results of the BIOS call
 307          */
 308         pushf
 309         push    %ds
 310         push    %es
 311         pushl   %ebp            /* still executes as 16 bit */
 312         pushl   %edi
 313         pushl   %esi
 314         pushl   %edx
 315         pushl   %ecx
 316         pushl   %ebx
 317         pushl   %eax
 318 
 319         /*
 320          * Restore protected mode and 32 bit execution
 321          */
 322         push    $0                      /* make sure %ds is zero before lgdt */
 323         pop     %ds
 324         .byte   0x0f, 0x01, 0x16        /* lgdt */
 325 gdt_info_load:
 326         .value  0       /* temp GDT in currently addressible mem */
 327 
 328         DATASZ  movl    $0x1, %eax
 329         DATASZ  movl    %eax, %cr0
 330 
 331         .byte   0xea                    /* ljmp */
 332 enter_protected_ljmp:
 333         .value  0                       /* addr (still in 16 bit) */
 334         .value  B32CODE_SEL             /* %cs value */
 335 enter_protected:
 336 
 337         /*
 338          * We are now back in a 32 bit code section, fix data/stack segments
 339          */
 340         .code32
 341         movw    $B32DATA_SEL, %ax
 342         movw    %ax, %ds
 343         movw    %ax, %ss
 344 
 345         /*
 346          * Re-enable paging. Note we only use 32 bit mov's to restore these
 347          * control registers. That's OK as the upper 32 bits are always zero.
 348          */
 349         movl    save_cr4, %eax
 350         movl    %eax, %cr4
 351         movl    save_cr3, %eax
 352         movl    %eax, %cr3
 353 
 354 #if defined(__amd64)
 355         /*
 356          * re-enable long mode
 357          */
 358         movl    $MSR_AMD_EFER, %ecx
 359         rdmsr
 360         btsl    $8, %eax
 361         wrmsr
 362 #endif
 363 
 364         movl    save_cr0, %eax
 365         movl    %eax, %cr0
 366         jmp     enter_paging
 367 enter_paging:
 368 
 369 
 370 #if defined(__amd64)
 371         /*
 372          * transition back to 64 bit mode
 373          */
 374         pushl   $B64CODE_SEL
 375         pushl   $longmode
 376         lret
 377 longmode:
 378         .code64
 379 #endif
 380         /*
 381          * restore caller frame pointer and segment registers
 382          */
 383         lgdt    save_gdt
 384         lidt    save_idt
 385 
 386         /*
 387          * Before loading the task register we need to reset the busy bit
 388          * in its corresponding GDT selector. The busy bit is the 2nd bit in
 389          * the 5th byte of the selector.
 390          */
 391 #if defined(__i386)
 392         movzwl  save_tr, %eax
 393         addl    save_gdt+2, %eax
 394         btcl    $1, 5(%eax)
 395 #elif defined(__amd64)
 396         movzwq  save_tr, %rax
 397         addq    save_gdt+2, %rax
 398         btcl    $1, 5(%rax)
 399 #endif
 400         ltr     save_tr
 401         movw    save_ds, %ds
 402         movw    save_ss, %ss
 403         movw    save_es, %es
 404         movw    save_fs, %fs
 405         movw    save_gs, %gs
 406 
 407 #if defined(__i386)
 408         pushl   save_cs
 409         pushl   $.newcs
 410         lret
 411 #elif defined(__amd64)
 412         pushq   save_cs
 413         pushq   $.newcs
 414         lretq
 415 #endif
 416 .newcs:
 417 
 418 #if defined(__amd64)
 419         /*
 420          * restore the hidden kernel segment base register values
 421          */
 422         movl    save_fsbase, %eax
 423         movl    save_fsbase + 4, %edx
 424         movl    $MSR_AMD_FSBASE, %ecx
 425         wrmsr
 426 
 427         movl    save_gsbase, %eax
 428         movl    save_gsbase + 4, %edx
 429         movl    $MSR_AMD_GSBASE, %ecx
 430         wrmsr
 431 
 432         movl    save_kgsbase, %eax
 433         movl    save_kgsbase + 4, %edx
 434         movl    $MSR_AMD_KGSBASE, %ecx
 435         wrmsr
 436 
 437         movq    save_cr8, %rax
 438         cmpq    $0, %rax
 439         je      1f
 440         movq    %rax, %cr8
 441 1:
 442 #endif
 443 
 444         /*
 445          * copy results to caller's location, then restore remaining registers
 446          */
 447 #if defined(__i386)
 448         movl    save_esp, %edi
 449         movl    8(%edi), %edi
 450         movl    %esp, %esi
 451         movl    $18, %ecx
 452         rep
 453         movsb
 454         movw    18(%esp), %ax
 455         andl    $0xffff, %eax
 456         movl    save_ebx, %ebx
 457         movl    save_esi, %esi
 458         movl    save_edi, %edi
 459         movl    save_esp, %esp
 460         movl    save_ebp, %ebp
 461         movl    save_esp, %esp
 462         ret
 463 
 464 #elif defined(__amd64)
 465         movq    save_rsi, %rdi
 466         movq    %rsp, %rsi
 467         movq    $18, %rcx
 468         rep
 469         movsb
 470         movw    18(%rsp), %ax
 471         andq    $0xffff, %rax
 472         movq    save_r12, %r12
 473         movq    save_r13, %r13
 474         movq    save_r14, %r14
 475         movq    save_r15, %r15
 476         movq    save_rbx, %rbx
 477         movq    save_rbp, %rbp
 478         movq    save_rsp, %rsp
 479         ret
 480 
 481 #endif
 482 
 483 
 484 /*
 485  * Caller's registers to restore
 486  */
 487         .align 4
 488 save_esi:
 489         .long   0
 490 save_edi:
 491         .long   0
 492 save_ebx:
 493         .long   0
 494 save_ebp:
 495         .long   0
 496 save_esp:
 497         .long   0
 498 
 499         .align 8
 500 #if defined(__amd64)
 501 save_rsi:
 502         .quad   0
 503 save_rbx:
 504         .quad   0
 505 save_rbp:
 506         .quad   0
 507 save_rsp:
 508         .quad   0
 509 save_r12:
 510         .quad   0
 511 save_r13:
 512         .quad   0
 513 save_r14:
 514         .quad   0
 515 save_r15:
 516         .quad   0
 517 save_kgsbase:
 518         .quad   0
 519 save_gsbase:
 520         .quad   0
 521 save_fsbase:
 522         .quad   0
 523 save_cr8:
 524         .quad   0
 525 #endif  /* __amd64 */
 526 
 527 save_idt:
 528         .quad   0
 529         .quad   0
 530 
 531 save_gdt:
 532         .quad   0
 533         .quad   0
 534 
 535 save_cr0:
 536         .quad   0
 537 save_cr3:
 538         .quad   0
 539 save_cr4:
 540         .quad   0
 541 save_cs:
 542         .quad   0
 543 save_ss:
 544         .value  0
 545 save_ds:
 546         .value  0
 547 save_es:
 548         .value  0
 549 save_fs:
 550         .value  0
 551 save_gs:
 552         .value  0
 553 save_tr:
 554         .value  0
 555 
 556 idt_info:
 557         .value 0x3ff
 558         .quad 0
 559 
 560 
 561 /*
 562  * We need to trampoline thru a gdt we have in low memory.
 563  */
 564 #include "../boot/boot_gdt.s"
 565 #endif /* __lint */