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