├── .gitignore ├── DOSKernel.cpp ├── DOSKernel.h ├── LICENSE.txt ├── Makefile ├── README.md ├── hvdos.c ├── interface.h └── vmcs.h /.gitignore: -------------------------------------------------------------------------------- 1 | hvdos 2 | -------------------------------------------------------------------------------- /DOSKernel.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009-present, the libcpu developers. All Rights Reserved. 2 | // Read LICENSE.txt for licensing information. 3 | 4 | #include "DOSKernel.h" 5 | #include "interface.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | //#define DEBUG 1 14 | 15 | #ifndef O_BINARY 16 | #define O_BINARY 0 17 | #endif 18 | 19 | #define MK_FP(SEG, OFF) (((SEG) << 4) + (OFF)) 20 | 21 | 22 | // TODO Make this list 23 | #define DOS_EBADF EBADF 24 | #define DOS_ENFILE ENFILE 25 | 26 | namespace { 27 | 28 | enum { 29 | ATTR_ARCHIVE = (1 << 5), 30 | ATTR_DIRECTORY = (1 << 4), 31 | ATTR_VOLUME_LABEL = (1 << 3), 32 | ATTR_SYSTEM = (1 << 2), 33 | ATTR_HIDDEN = (1 << 1), 34 | ATTR_READONLY = (1 << 0) 35 | }; 36 | 37 | #pragma pack(push, 1) 38 | 39 | struct FindData { 40 | uint8_t Unknown[21]; 41 | uint8_t Attributes; 42 | uint16_t FileTime; 43 | uint16_t FileDate; 44 | uint32_t FileSize; 45 | char FileName[13]; 46 | }; 47 | 48 | #pragma pack(pop) 49 | 50 | 51 | #pragma pack(push, 1) 52 | struct PSP { 53 | uint8_t CPMExit[2]; 54 | uint16_t FirstFreeSegment; 55 | uint8_t Reserved1; 56 | uint8_t CPMCall5Compat[5]; 57 | uint32_t OldTSRAddress; 58 | uint32_t OldBreakAddress; 59 | uint32_t CriticalErrorHandlerAddress; 60 | uint16_t CallerPSPSegment; 61 | uint8_t JobFileTable[20]; 62 | uint16_t EnvironmentSegment; 63 | uint32_t INT21SSSP; 64 | uint16_t JobFileTableSize; 65 | uint32_t JobFileTablePointer; 66 | uint32_t PreviousPSP; 67 | uint32_t Reserved2; 68 | uint16_t DOSVersion; 69 | uint8_t Reserved3[14]; 70 | uint8_t DOSFarCall[3]; 71 | uint16_t Reserved4; 72 | uint8_t ExtendedFCB1[7]; 73 | uint8_t FCB1[16]; 74 | uint8_t FCB2[20]; 75 | uint8_t CommandLineLength; 76 | char CommandLine[127]; 77 | }; 78 | #pragma pack(pop) 79 | 80 | // convert backslashes to slashes 81 | static inline void 82 | ConvertSlashes(std::string &S) 83 | { 84 | std::transform(S.begin(), S.begin(), S.end(), 85 | [](char C) { return (C == '\\') ? '/' : C; }); 86 | } 87 | 88 | static inline uint8_t 89 | ModeToAttribute(uint16_t Mode) 90 | { 91 | uint8_t Attribute = 0; 92 | 93 | if (Mode & S_IFDIR) { 94 | Attribute |= ATTR_DIRECTORY; 95 | } 96 | if ((Mode & S_IRUSR) == 0) { 97 | Attribute |= ATTR_READONLY; 98 | } 99 | 100 | return Attribute; 101 | } 102 | 103 | } 104 | 105 | DOSKernel::DOSKernel(char *memory, hv_vcpuid_t vcpu, int argc, char **argv) : 106 | _memory (memory), 107 | _vcpu (vcpu), 108 | _dta (0), 109 | _exitStatus(0) 110 | { 111 | _fdbits.resize(256); 112 | 113 | _fdtable[0] = 0, _fdbits[0] = true; 114 | _fdtable[1] = 1, _fdbits[1] = true; 115 | _fdtable[2] = 2, _fdbits[2] = true; 116 | 117 | // Initialize PSP 118 | makePSP(0, argc, argv); 119 | } 120 | 121 | DOSKernel::~DOSKernel() 122 | { 123 | } 124 | 125 | int DOSKernel:: 126 | dispatch(uint8_t IntNo) 127 | { 128 | switch (IntNo) { 129 | case 0x20: return int20(); 130 | case 0x21: return int21(); 131 | default: break; 132 | } 133 | return STATUS_UNHANDLED; 134 | } 135 | 136 | int DOSKernel:: 137 | int20() 138 | { 139 | _exitStatus = 0; 140 | return STATUS_STOP; 141 | } 142 | 143 | int DOSKernel:: 144 | int21() 145 | { 146 | #ifdef DEBUG 147 | std::fprintf(stderr, "\n[%04x] INT 21/AH=%02Xh\n", pc, AH); 148 | #endif 149 | 150 | switch (AH) { 151 | case 0x02: return int21Func02(); 152 | case 0x08: return int21Func08(); 153 | case 0x09: return int21Func09(); 154 | case 0x0A: return int21Func0A(); 155 | case 0x0C: return int21Func0C(); 156 | case 0x0E: return int21Func0E(); 157 | case 0x19: return int21Func19(); 158 | case 0x1A: return int21Func1A(); 159 | case 0x25: return int21Func25(); 160 | case 0x26: return int21Func26(); 161 | case 0x30: return int21Func30(); 162 | case 0x33: return int21Func33(); 163 | case 0x35: return int21Func35(); 164 | case 0x3C: return int21Func3C(); 165 | case 0x3D: return int21Func3D(); 166 | case 0x3E: return int21Func3E(); 167 | case 0x3F: return int21Func3F(); 168 | case 0x40: return int21Func40(); 169 | case 0x41: return int21Func41(); 170 | case 0x42: return int21Func42(); 171 | case 0x43: return int21Func43(); 172 | case 0x4C: return int21Func4C(); 173 | case 0x4E: return int21Func4E(); 174 | case 0x4F: return int21Func4F(); 175 | case 0x57: return int21Func57(); 176 | default: break; 177 | } 178 | 179 | std::fprintf(stderr, "Unknown interrupt 0x21/0x%02X at %s:%d\n", 180 | AH, __FILE__, __LINE__); 181 | return STATUS_UNSUPPORTED; 182 | } 183 | 184 | // DOS 1+ - WRITE CHARACTER TO STANDARD OUTPUT 185 | int DOSKernel:: 186 | int21Func02() 187 | { 188 | putchar(DL); 189 | SET_AL(DL); 190 | return STATUS_HANDLED; 191 | } 192 | 193 | // DOS 1+ - CHARACTER INPUT WITHOUT ECHO 194 | int DOSKernel:: 195 | int21Func08() 196 | { 197 | SET_AL(internalGetChar(0)); 198 | return STATUS_HANDLED; 199 | } 200 | 201 | // DOS 1+ - WRITE STRING TO STANDARD OUTPUT 202 | int DOSKernel:: 203 | int21Func09() 204 | { 205 | std::string S(readCString(MK_FP(DS, DX), '$')); 206 | fwrite(S.data(), 1, S.size(), stdout); 207 | 208 | SET_AL('$'); 209 | 210 | return STATUS_HANDLED; 211 | } 212 | 213 | // DOS 1+ - BUFFERED INPUT 214 | int DOSKernel:: 215 | int21Func0A() 216 | { 217 | uint32_t abs = MK_FP(DS, DX); 218 | char *addr = &_memory[abs]; 219 | getline(&addr, NULL, stdin); 220 | 221 | return STATUS_HANDLED; 222 | } 223 | 224 | // DOS 1+ - FLUSH BUFFER AND READ STANDARD INPUT 225 | int DOSKernel:: 226 | int21Func0C() 227 | { 228 | flushConsoleInput(); 229 | 230 | switch (AL) { 231 | case 0x01: 232 | case 0x06: 233 | case 0x07: 234 | case 0x08: 235 | case 0x0a: 236 | SET_AH(AL); 237 | int21(); 238 | SET_AH(0x0c); 239 | break; 240 | 241 | default: 242 | break; 243 | } 244 | 245 | return STATUS_HANDLED; 246 | } 247 | 248 | // DOS 1+ - SELECT DEFAULT DRIVE 249 | int DOSKernel:: 250 | int21Func0E() 251 | { 252 | SET_AL(DL + 'A'); 253 | return STATUS_HANDLED; 254 | } 255 | 256 | // DOS 1+ - GET CURRENT DEFAULT DRIVE 257 | int DOSKernel:: 258 | int21Func19() 259 | { 260 | SET_AL(0); 261 | return STATUS_HANDLED; 262 | } 263 | 264 | // DOS 1+ - SET DISK TRANSFER AREA ADDRESS 265 | int DOSKernel:: 266 | int21Func1A() 267 | { 268 | _dta = DX; 269 | return STATUS_HANDLED; 270 | } 271 | 272 | void DOSKernel:: 273 | makePSP(uint16_t seg, int argc, char **argv) 274 | { 275 | uint32_t abs = MK_FP(seg, 0); 276 | 277 | struct PSP *PSP = (struct PSP *)(&_memory[abs]); 278 | 279 | // CPMExit: INT 20h 280 | PSP->CPMExit[0] = 0xcd; 281 | PSP->CPMExit[1] = 0x20; 282 | 283 | // DOS Far Call: INT 21h + RETF 284 | PSP->DOSFarCall[0] = 0xcd; 285 | PSP->DOSFarCall[1] = 0x21; 286 | PSP->DOSFarCall[2] = 0xcb; 287 | 288 | // first FSB = empty file name 289 | PSP->FCB1[0] = 0x01; 290 | PSP->FCB1[1] = 0x20; 291 | 292 | uint8_t c = 0; 293 | int j = 0; 294 | for (int i = 2; i < argc && c < 0x7E; i++) { 295 | j = 0; 296 | PSP->CommandLine[c++] = ' '; 297 | while (argv[i][j] && c < 0x7E) 298 | PSP->CommandLine[c++] = argv[i][j++]; 299 | } 300 | PSP->CommandLine[c] = 0x0D; 301 | PSP->CommandLineLength = c; 302 | } 303 | 304 | // DOS 1+ - SET INTERRUPT VECTOR 305 | int DOSKernel:: 306 | int21Func25() 307 | { 308 | #ifdef DEBUG 309 | std::fprintf(stderr, "[%04x] SET INTERRUPT VECTOR: 0x%02x to 0x%04x:0x%04x\n", pc, AL, DS, DX); 310 | #endif 311 | return STATUS_HANDLED; 312 | } 313 | 314 | // DOS 1+ - CREATE NEW PROGRAM SEGMENT PREFIX 315 | int DOSKernel:: 316 | int21Func26() 317 | { 318 | makePSP(DS, 0, 0); 319 | return STATUS_HANDLED; 320 | } 321 | 322 | // DOS 2+ - GET DOS VERSION 323 | int DOSKernel:: 324 | int21Func30() 325 | { 326 | SET_AL(7); 327 | SET_AH(0); 328 | return STATUS_HANDLED; 329 | } 330 | 331 | // DOS 2+ - EXTENDED BREAK CHECKING 332 | int DOSKernel:: 333 | int21Func33() 334 | { 335 | static bool extended_break_checking = false; 336 | switch (AL) { 337 | case 0: 338 | SET_DL(extended_break_checking); 339 | break; 340 | case 1: 341 | extended_break_checking = DL; 342 | break; 343 | default: 344 | std::fprintf(stderr, "Unknown subfunction 0x21/0x33/0x%02X at %s:%d\n", 345 | AL, __FILE__, __LINE__); 346 | } 347 | return STATUS_HANDLED; 348 | } 349 | 350 | // DOS 2+ - GET INTERRUPT VECTOR 351 | int DOSKernel:: 352 | int21Func35() 353 | { 354 | #ifdef DEBUG 355 | std::fprintf(stderr, "\nGET INTERRUPT VECTOR: 0x%02x\n", AL); 356 | #endif 357 | SET_ES(0); 358 | SET_BX(0); 359 | return STATUS_HANDLED; 360 | } 361 | 362 | // DOS 2+ - CREAT - CREATE OR TRUNCATE FILE 363 | int DOSKernel:: 364 | int21Func3C() 365 | { 366 | std::string FN(readCString(MK_FP(DS, DX))); 367 | 368 | #if DEBUG 369 | std::fprintf(stderr, "\ncreat: %s\n", FN.c_str()); 370 | #endif 371 | 372 | // TODO we ignore attributes 373 | int HostFD = ::open(FN.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_BINARY, 0777); 374 | if (HostFD < 0) { 375 | SETC(1); 376 | SET_AX(getDOSError()); 377 | } else { 378 | int FD = allocFD(HostFD); 379 | if (FD < 0) { 380 | ::close(HostFD); 381 | SETC(1); 382 | SET_AX(DOS_ENFILE); 383 | } else { 384 | SETC(0); 385 | SET_AX(FD); 386 | } 387 | } 388 | 389 | return STATUS_HANDLED; 390 | } 391 | 392 | // DOS 2+ - OPEN - OPEN EXISTING FILE 393 | int DOSKernel:: 394 | int21Func3D() 395 | { 396 | std::string FN(readCString(MK_FP(DS, DX))); 397 | 398 | #if DEBUG 399 | std::fprintf(stderr, "\nopen: %s\n", FN.c_str()); 400 | #endif 401 | 402 | // oflag is compatible! 403 | int HostFD = ::open(FN.c_str(), (AL & 3) | O_BINARY); 404 | if (HostFD < 0) { 405 | SETC(1); 406 | SET_AX(getDOSError()); 407 | } else { 408 | int FD = allocFD(HostFD); 409 | if (FD < 0) { 410 | ::close(HostFD); 411 | SETC(1); 412 | SET_AX(DOS_ENFILE); 413 | } else { 414 | SETC(0); 415 | SET_AX(FD); 416 | } 417 | } 418 | 419 | return STATUS_HANDLED; 420 | } 421 | 422 | // DOS 2+ - CLOSE - CLOSE FILE 423 | int DOSKernel:: 424 | int21Func3E() 425 | { 426 | int FD = BX; 427 | int HostFD = findFD(FD); 428 | if (HostFD < 0) { 429 | SETC(1); 430 | SET_AX(DOS_EBADF); 431 | } else { 432 | deallocFD(FD); 433 | if (::close(HostFD) < 0) { 434 | SETC(1); 435 | SET_AX(getDOSError()); 436 | } else { 437 | SETC(0); 438 | } 439 | } 440 | 441 | return STATUS_HANDLED; 442 | } 443 | 444 | // DOS 2+ - READ - READ FROM FILE OR DEVICE 445 | int DOSKernel:: 446 | int21Func3F() 447 | { 448 | int FD = findFD(BX); 449 | if (FD < 0) { 450 | SETC(1); 451 | SET_AX(DOS_EBADF); 452 | return STATUS_HANDLED; 453 | } 454 | 455 | char Buffer[64 * 1024]; 456 | ssize_t ReadCount = ::read(FD, Buffer, CX); 457 | if (ReadCount < 0) { 458 | SETC(1); 459 | SET_AX(getDOSError()); 460 | } else { 461 | writeMem(MK_FP(DS, DX), Buffer, ReadCount); 462 | SETC(0); 463 | SET_AX(ReadCount); 464 | } 465 | 466 | return STATUS_HANDLED; 467 | } 468 | 469 | // DOS 2+ - WRITE - WRITE TO FILE OR DEVICE 470 | int DOSKernel:: 471 | int21Func40() 472 | { 473 | int FD = findFD(BX); 474 | if (FD < 0) { 475 | SETC(1); 476 | SET_AX(DOS_EBADF); 477 | return STATUS_HANDLED; 478 | } 479 | 480 | std::string B(readString(MK_FP(DS, DX), CX)); 481 | 482 | ssize_t WriteCount = ::write(FD, B.data(), B.size()); 483 | if (WriteCount < 0) { 484 | SETC(1); 485 | SET_AX(getDOSError()); 486 | } else { 487 | SETC(0); 488 | SET_AX(WriteCount); 489 | } 490 | 491 | return STATUS_HANDLED; 492 | } 493 | 494 | // DOS 2+ - UNLINK - DELETE FILE 495 | int DOSKernel:: 496 | int21Func41() 497 | { 498 | std::string FN(readCString(MK_FP(DS, DX))); 499 | 500 | // TODO 501 | #if DEBUG 502 | std::fprintf(stderr, "\nUNIMPL del: %s\n", FN.c_str()); 503 | #endif 504 | 505 | return STATUS_HANDLED; 506 | } 507 | 508 | // DOS 2+ - LSEEK - SET CURRENT FILE POSITION 509 | int DOSKernel:: 510 | int21Func42() 511 | { 512 | int FD = findFD(BX); 513 | if (FD < 0) { 514 | SETC(1); 515 | SET_AX(DOS_EBADF); 516 | return STATUS_HANDLED; 517 | } 518 | 519 | off_t NewOffset = lseek(FD, (CX << 16) | DX, AL); 520 | 521 | #if 0 522 | std::fprintf(stderr, "\n%" PRIx64 ": lseek(%d, 0x%x, %d) = %" PRId64 "\n", 523 | pc.value(), BX, (CX << 16) | DX, AL, 524 | static_cast (NewOffset)); 525 | #endif 526 | 527 | if (NewOffset < 0) { 528 | SETC(1); 529 | SET_AX(getDOSError()); 530 | } else { 531 | SETC(0); 532 | SET_DX(NewOffset >> 16); 533 | SET_AX(NewOffset & 0xffff); 534 | } 535 | return STATUS_HANDLED; 536 | } 537 | 538 | // DOS 2+ - GET/SET FILE ATTRIBUTES 539 | int DOSKernel:: 540 | int21Func43() 541 | { 542 | std::string FN; 543 | struct stat ST; 544 | 545 | switch (AL) { 546 | case 0x00: // GET FILE ATTRIBUTES 547 | FN = readCString(MK_FP(DS, DX)); 548 | ConvertSlashes(FN); 549 | 550 | if (::stat(FN.c_str(), &ST) != 0) { 551 | SETC(1); 552 | SET_AX(getDOSError()); 553 | return STATUS_HANDLED; 554 | } 555 | 556 | SETC(0); 557 | SET_CX(ModeToAttribute(ST.st_mode)); 558 | break; 559 | 560 | case 0x01: // SET FILE ATTRIBUTES 561 | #if DEBUG 562 | std::fprintf(stderr, "\nUNIMPL SetFileAttributes: 0x%02X, %s\n", 563 | CX, readCString(MK_FP(DS, DX)).c_str()); 564 | #endif 565 | SETC(0); 566 | break; 567 | 568 | default: 569 | std::fprintf(stderr, "Unknown GetSetFileAttributes " 570 | "subfunction: 0x%02X\n", AL); 571 | return STATUS_UNSUPPORTED; 572 | } 573 | 574 | return STATUS_HANDLED; 575 | } 576 | 577 | // DOS 2+ - EXIT - TERMINATE WITH RETURN CODE 578 | int DOSKernel:: 579 | int21Func4C() 580 | { 581 | _exitStatus = AL; 582 | return STATUS_STOP; 583 | } 584 | 585 | // DOS 2+ - FINDFIRST - FIND FIRST MATCHING FILE 586 | int DOSKernel:: 587 | int21Func4E() 588 | { 589 | std::string FileSpec(readCString(MK_FP(DS, DX))); 590 | ConvertSlashes(FileSpec); 591 | 592 | if (CX & ATTR_VOLUME_LABEL) { 593 | #if DEBUG 594 | std::fprintf(stderr, "\nUNIMPL findfirst volume label: %s\n", 595 | FileSpec.c_str()); 596 | #endif 597 | SETC(1); 598 | SET_AX(0x12); // no more files 599 | return STATUS_HANDLED; 600 | } 601 | 602 | if (FileSpec.find_first_of("?*") != std::string::npos) { 603 | #if DEBUG 604 | std::fprintf(stderr, "\nUNIMPL findfirst wildcards: %s\n", 605 | FileSpec.c_str()); 606 | #endif 607 | SETC(1); 608 | SET_AX(0x12); // no more files 609 | return STATUS_HANDLED; 610 | } 611 | 612 | struct stat ST; 613 | if (::stat(FileSpec.c_str(), &ST)) { 614 | SETC(1); 615 | SET_AX(getDOSError()); 616 | return STATUS_HANDLED; 617 | } 618 | 619 | if ((ST.st_mode) == S_IFDIR && !(CX & ATTR_DIRECTORY)) { 620 | // found a directory but program hasn't requested directories 621 | SETC(1); 622 | SET_AX(0x12); // no more files 623 | return STATUS_HANDLED; 624 | } 625 | 626 | 627 | FindData FD; 628 | 629 | std::memset(&FD, 0, sizeof(FD)); 630 | 631 | FD.Attributes = ModeToAttribute(ST.st_mode); 632 | FD.FileTime = 0; // TODO 633 | FD.FileDate = 0; // TODO 634 | FD.FileSize = ST.st_size; 635 | 636 | size_t FNPos = FileSpec.rfind('/'); 637 | if (FNPos != std::string::npos) { 638 | FNPos++; 639 | } else { 640 | FNPos = 0; 641 | } 642 | std::strncpy(FD.FileName, &FileSpec[FNPos], sizeof(FD.FileName) - 1); 643 | 644 | writeMem(MK_FP(DS, _dta), &FD, sizeof(FD)); 645 | 646 | SETC(0); 647 | return STATUS_HANDLED; 648 | } 649 | 650 | // DOS 2+ - FINDNEXT - FIND NEXT MATCHING FILE 651 | int DOSKernel:: 652 | int21Func4F() 653 | { 654 | // TODO 655 | #if DEBUG 656 | std::fprintf(stderr, "\nUNIMPL FindNext\n"); 657 | #endif 658 | SET_AX(0x12); // no more files 659 | SETC(1); 660 | 661 | return STATUS_HANDLED; 662 | } 663 | 664 | // DOS 2+ - GET FILE'S LAST-WRITTEN DATE AND TIME 665 | int DOSKernel:: 666 | int21Func57() 667 | { 668 | // TODO 669 | #if DEBUG 670 | std::fprintf(stderr, "\nUNIMPL datetime\n"); 671 | #endif 672 | SETC(0); 673 | 674 | return STATUS_HANDLED; 675 | } 676 | 677 | int DOSKernel:: 678 | getDOSError() const 679 | { 680 | // TODO translate 681 | return errno; 682 | } 683 | 684 | void DOSKernel:: 685 | flushConsoleInput() 686 | { 687 | fflush(stdout); 688 | } 689 | 690 | int DOSKernel:: 691 | internalGetChar(bool Echo) 692 | { 693 | return getchar(); 694 | } 695 | 696 | int DOSKernel:: 697 | allocFD(int HostFD) 698 | { 699 | auto I = std::find(_fdbits.begin(), _fdbits.end(), false); 700 | if (I == _fdbits.end()) 701 | return -1; 702 | 703 | int FD = I - _fdbits.begin(); 704 | _fdbits[FD] = true; 705 | _fdtable.insert(std::make_pair(FD, HostFD)); 706 | return FD; 707 | } 708 | 709 | void DOSKernel:: 710 | deallocFD(int FD) 711 | { 712 | if (FD < 3) 713 | return; 714 | 715 | _fdbits[FD] = false; 716 | _fdtable.erase(FD); 717 | } 718 | 719 | int DOSKernel:: 720 | findFD(int FD) 721 | { 722 | if (FD < 0) 723 | return -1; 724 | 725 | auto I = _fdtable.find(FD); 726 | return (I != _fdtable.end()) ? I->second : -1; 727 | } 728 | 729 | 730 | void DOSKernel:: 731 | writeMem(uint16_t const &Address, void const *Bytes, size_t Length) 732 | { 733 | auto D = Address; 734 | uint8_t const *B = reinterpret_cast (Bytes); 735 | 736 | while (Length-- != 0) { 737 | writeMem8(D, *B); 738 | D++, B++; 739 | } 740 | } 741 | 742 | std::string DOSKernel:: 743 | readString(uint16_t const &Address, size_t Length) 744 | { 745 | std::string Result; 746 | auto S = Address; 747 | 748 | while (Length-- != 0) { 749 | Result += static_cast (readMem8(S)); 750 | S++; 751 | } 752 | 753 | return Result; 754 | } 755 | 756 | std::string DOSKernel:: 757 | readCString(uint16_t const &Address, char Terminator) 758 | { 759 | std::string Result; 760 | auto S = Address; 761 | 762 | for (;;) { 763 | char C = readMem8(S); 764 | if (C == Terminator) 765 | break; 766 | 767 | Result += C, S++; 768 | } 769 | 770 | return Result; 771 | } 772 | -------------------------------------------------------------------------------- /DOSKernel.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009-present, the libcpu developers. All Rights Reserved. 2 | // Read LICENSE.txt for licensing information. 3 | 4 | #ifndef __DOSKernel_h 5 | #define __DOSKernel_h 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class DOSKernel { 13 | public: 14 | enum { 15 | STATUS_HANDLED, 16 | STATUS_STOP, 17 | STATUS_UNHANDLED, 18 | STATUS_UNSUPPORTED, 19 | STATUS_NORETURN 20 | }; 21 | 22 | private: 23 | char *_memory; 24 | hv_vcpuid_t _vcpu; 25 | std::map _fdtable; 26 | std::vector _fdbits; 27 | uint16_t _dta; 28 | int _exitStatus; 29 | 30 | public: 31 | DOSKernel(char *memory, hv_vcpuid_t vcpu, int argc, char **argv); 32 | ~DOSKernel(); 33 | 34 | public: 35 | int dispatch(uint8_t IntNo); 36 | 37 | private: 38 | int int20(); 39 | int int21(); 40 | 41 | private: 42 | int int21Func02(); 43 | int int21Func08(); 44 | int int21Func09(); 45 | int int21Func0A(); 46 | int int21Func0C(); 47 | int int21Func0E(); 48 | int int21Func19(); 49 | int int21Func1A(); 50 | int int21Func25(); 51 | int int21Func26(); 52 | int int21Func30(); 53 | int int21Func33(); 54 | int int21Func35(); 55 | int int21Func3C(); 56 | int int21Func3D(); 57 | int int21Func3E(); 58 | int int21Func3F(); 59 | int int21Func40(); 60 | int int21Func41(); 61 | int int21Func42(); 62 | int int21Func43(); 63 | int int21Func4C(); 64 | int int21Func4E(); 65 | int int21Func4F(); 66 | int int21Func57(); 67 | 68 | private: 69 | int getDOSError() const; 70 | void makePSP(uint16_t seg, int argc, char **argv); 71 | 72 | private: 73 | void flushConsoleInput(); 74 | int internalGetChar(bool Echo); 75 | 76 | private: 77 | int allocFD(int HostFD); 78 | void deallocFD(int FD); 79 | 80 | int findFD(int FD); 81 | 82 | private: 83 | void writeMem(uint16_t const &Address, void const *Bytes, 84 | size_t Length); 85 | std::string readString(uint16_t const &Address, size_t Length); 86 | std::string readCString(uint16_t const &Address, 87 | char Terminator = '\0'); 88 | 89 | }; 90 | 91 | #endif // !__DOSKernel_h 92 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2010, the hvdos developers 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or 5 | without modification, are permitted provided that the following 6 | conditions are met: 7 | 8 | * Redistributions of source code must retain the above 9 | copyright notice, this list of conditions and the 10 | following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials 15 | provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | clang++ -std=c++11 -framework Hypervisor -o hvdos DOSKernel.cpp hvdos.c 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hvdos 2 | 3 | *hvdos* is a simple DOS emulator based on the OS X 10.10 Yosemite Hypervisor.framework. 4 | 5 | ## Purpose 6 | 7 | Consider this project more of an example or a template to create solutions based on Hypervisor.framework than a useful application on its own. 8 | 9 | See [pagetable.com/?p=764](http://www.pagetable.com/?p=764) for details. 10 | 11 | ## Status 12 | 13 | *hvdos* can run some simple DOS programs in .COM format. Try [PKUNZJR.COM](https://github.com/libcpu/libcpu/blob/2fa4a9574a3320bd3953d1b238c36f55090405fb/test/bin/x86/pkunzjr.com?raw=true) for example. 14 | 15 | ## License 16 | 17 | See [LICENSE.txt](LICENSE.txt) (2-clause-BSD). 18 | 19 | In order to simplify use of this code as a template, you can consider any parts from "hvdos.c" and "interface.h" as being in the public domain. 20 | 21 | ## Author 22 | 23 | Michael Steil 24 | -------------------------------------------------------------------------------- /hvdos.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009-present, the hvdos developers. All Rights Reserved. 2 | // Read LICENSE.txt for licensing information. 3 | // 4 | // hvdos - a simple DOS emulator based on the OS X 10.10 Hypervisor.framework 5 | 6 | #include 7 | #include 8 | #include 9 | #include "vmcs.h" 10 | #include "interface.h" 11 | #include "DOSKernel.h" 12 | 13 | //#define DEBUG 1 14 | 15 | /* read GPR */ 16 | uint64_t 17 | rreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg) 18 | { 19 | uint64_t v; 20 | 21 | if (hv_vcpu_read_register(vcpu, reg, &v)) { 22 | abort(); 23 | } 24 | 25 | return v; 26 | } 27 | 28 | /* write GPR */ 29 | void 30 | wreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg, uint64_t v) 31 | { 32 | if (hv_vcpu_write_register(vcpu, reg, v)) { 33 | abort(); 34 | } 35 | } 36 | 37 | /* read VMCS field */ 38 | static uint64_t 39 | rvmcs(hv_vcpuid_t vcpu, uint32_t field) 40 | { 41 | uint64_t v; 42 | 43 | if (hv_vmx_vcpu_read_vmcs(vcpu, field, &v)) { 44 | abort(); 45 | } 46 | 47 | return v; 48 | } 49 | 50 | /* write VMCS field */ 51 | static void 52 | wvmcs(hv_vcpuid_t vcpu, uint32_t field, uint64_t v) 53 | { 54 | if (hv_vmx_vcpu_write_vmcs(vcpu, field, v)) { 55 | abort(); 56 | } 57 | } 58 | 59 | /* desired control word constrained by hardware/hypervisor capabilities */ 60 | static uint64_t 61 | cap2ctrl(uint64_t cap, uint64_t ctrl) 62 | { 63 | return (ctrl | (cap & 0xffffffff)) & (cap >> 32); 64 | } 65 | 66 | int 67 | main(int argc, char **argv) 68 | { 69 | if (argc < 2) { 70 | fprintf(stderr, "Usage: hvdos [com file]\n"); 71 | exit(1); 72 | } 73 | 74 | /* create a VM instance for the current task */ 75 | if (hv_vm_create(HV_VM_DEFAULT)) { 76 | abort(); 77 | } 78 | 79 | /* get hypervisor enforced capabilities of the machine, (see Intel docs) */ 80 | uint64_t vmx_cap_pinbased, vmx_cap_procbased, vmx_cap_procbased2, vmx_cap_entry; 81 | if (hv_vmx_read_capability(HV_VMX_CAP_PINBASED, &vmx_cap_pinbased)) { 82 | abort(); 83 | } 84 | if (hv_vmx_read_capability(HV_VMX_CAP_PROCBASED, &vmx_cap_procbased)) { 85 | abort(); 86 | } 87 | if (hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &vmx_cap_procbased2)) { 88 | abort(); 89 | } 90 | if (hv_vmx_read_capability(HV_VMX_CAP_ENTRY, &vmx_cap_entry)) { 91 | abort(); 92 | } 93 | 94 | /* allocate some guest physical memory */ 95 | #define VM_MEM_SIZE (1 * 1024 * 1024) 96 | void *vm_mem; 97 | if (!(vm_mem = valloc(VM_MEM_SIZE))) { 98 | abort(); 99 | } 100 | /* map a segment of guest physical memory into the guest physical address 101 | * space of the vm (at address 0) */ 102 | if (hv_vm_map(vm_mem, 0, VM_MEM_SIZE, HV_MEMORY_READ | HV_MEMORY_WRITE 103 | | HV_MEMORY_EXEC)) 104 | { 105 | abort(); 106 | } 107 | 108 | /* create a vCPU instance for this thread */ 109 | hv_vcpuid_t vcpu; 110 | if (hv_vcpu_create(&vcpu, HV_VCPU_DEFAULT)) { 111 | abort(); 112 | } 113 | 114 | /* vCPU setup */ 115 | #define VMCS_PRI_PROC_BASED_CTLS_HLT (1 << 7) 116 | #define VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD (1 << 19) 117 | #define VMCS_PRI_PROC_BASED_CTLS_CR8_STORE (1 << 20) 118 | 119 | /* set VMCS control fields */ 120 | wvmcs(vcpu, VMCS_PIN_BASED_CTLS, cap2ctrl(vmx_cap_pinbased, 0)); 121 | wvmcs(vcpu, VMCS_PRI_PROC_BASED_CTLS, cap2ctrl(vmx_cap_procbased, 122 | VMCS_PRI_PROC_BASED_CTLS_HLT | 123 | VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD | 124 | VMCS_PRI_PROC_BASED_CTLS_CR8_STORE)); 125 | wvmcs(vcpu, VMCS_SEC_PROC_BASED_CTLS, cap2ctrl(vmx_cap_procbased2, 0)); 126 | wvmcs(vcpu, VMCS_ENTRY_CTLS, cap2ctrl(vmx_cap_entry, 0)); 127 | wvmcs(vcpu, VMCS_EXCEPTION_BITMAP, 0xffffffff); 128 | wvmcs(vcpu, VMCS_CR0_MASK, 0x60000000); 129 | wvmcs(vcpu, VMCS_CR0_SHADOW, 0); 130 | wvmcs(vcpu, VMCS_CR4_MASK, 0); 131 | wvmcs(vcpu, VMCS_CR4_SHADOW, 0); 132 | /* set VMCS guest state fields */ 133 | wvmcs(vcpu, VMCS_GUEST_CS_SELECTOR, 0); 134 | wvmcs(vcpu, VMCS_GUEST_CS_LIMIT, 0xffff); 135 | wvmcs(vcpu, VMCS_GUEST_CS_ACCESS_RIGHTS, 0x9b); 136 | wvmcs(vcpu, VMCS_GUEST_CS_BASE, 0); 137 | 138 | wvmcs(vcpu, VMCS_GUEST_DS_SELECTOR, 0); 139 | wvmcs(vcpu, VMCS_GUEST_DS_LIMIT, 0xffff); 140 | wvmcs(vcpu, VMCS_GUEST_DS_ACCESS_RIGHTS, 0x93); 141 | wvmcs(vcpu, VMCS_GUEST_DS_BASE, 0); 142 | 143 | wvmcs(vcpu, VMCS_GUEST_ES_SELECTOR, 0); 144 | wvmcs(vcpu, VMCS_GUEST_ES_LIMIT, 0xffff); 145 | wvmcs(vcpu, VMCS_GUEST_ES_ACCESS_RIGHTS, 0x93); 146 | wvmcs(vcpu, VMCS_GUEST_ES_BASE, 0); 147 | 148 | wvmcs(vcpu, VMCS_GUEST_FS_SELECTOR, 0); 149 | wvmcs(vcpu, VMCS_GUEST_FS_LIMIT, 0xffff); 150 | wvmcs(vcpu, VMCS_GUEST_FS_ACCESS_RIGHTS, 0x93); 151 | wvmcs(vcpu, VMCS_GUEST_FS_BASE, 0); 152 | 153 | wvmcs(vcpu, VMCS_GUEST_GS_SELECTOR, 0); 154 | wvmcs(vcpu, VMCS_GUEST_GS_LIMIT, 0xffff); 155 | wvmcs(vcpu, VMCS_GUEST_GS_ACCESS_RIGHTS, 0x93); 156 | wvmcs(vcpu, VMCS_GUEST_GS_BASE, 0); 157 | 158 | wvmcs(vcpu, VMCS_GUEST_SS_SELECTOR, 0); 159 | wvmcs(vcpu, VMCS_GUEST_SS_LIMIT, 0xffff); 160 | wvmcs(vcpu, VMCS_GUEST_SS_ACCESS_RIGHTS, 0x93); 161 | wvmcs(vcpu, VMCS_GUEST_SS_BASE, 0); 162 | 163 | wvmcs(vcpu, VMCS_GUEST_LDTR_SELECTOR, 0); 164 | wvmcs(vcpu, VMCS_GUEST_LDTR_LIMIT, 0); 165 | wvmcs(vcpu, VMCS_GUEST_LDTR_ACCESS_RIGHTS, 0x10000); 166 | wvmcs(vcpu, VMCS_GUEST_LDTR_BASE, 0); 167 | 168 | wvmcs(vcpu, VMCS_GUEST_TR_SELECTOR, 0); 169 | wvmcs(vcpu, VMCS_GUEST_TR_LIMIT, 0); 170 | wvmcs(vcpu, VMCS_GUEST_TR_ACCESS_RIGHTS, 0x83); 171 | wvmcs(vcpu, VMCS_GUEST_TR_BASE, 0); 172 | 173 | wvmcs(vcpu, VMCS_GUEST_GDTR_LIMIT, 0); 174 | wvmcs(vcpu, VMCS_GUEST_GDTR_BASE, 0); 175 | 176 | wvmcs(vcpu, VMCS_GUEST_IDTR_LIMIT, 0); 177 | wvmcs(vcpu, VMCS_GUEST_IDTR_BASE, 0); 178 | 179 | wvmcs(vcpu, VMCS_GUEST_CR0, 0x20); 180 | wvmcs(vcpu, VMCS_GUEST_CR3, 0x0); 181 | wvmcs(vcpu, VMCS_GUEST_CR4, 0x2000); 182 | 183 | /* initialize DOS emulation */ 184 | DOSKernel Kernel((char *)vm_mem, vcpu, argc, argv); 185 | 186 | /* read COM file at 0x100 */ 187 | FILE *f = fopen(argv[1], "r"); 188 | fread((char *)vm_mem + 0x100, 1, 64 * 1024, f); 189 | fclose(f); 190 | 191 | /* set up GPRs, start at COM file entry point */ 192 | wreg(vcpu, HV_X86_RIP, 0x100); 193 | wreg(vcpu, HV_X86_RFLAGS, 0x2); 194 | wreg(vcpu, HV_X86_RSP, 0x0); 195 | 196 | /* vCPU run loop */ 197 | int stop = 0; 198 | do { 199 | if (hv_vcpu_run(vcpu)) { 200 | abort(); 201 | } 202 | /* handle VMEXIT */ 203 | uint64_t exit_reason = rvmcs(vcpu, VMCS_EXIT_REASON); 204 | 205 | switch (exit_reason) { 206 | case EXIT_REASON_EXCEPTION: { 207 | uint8_t interrupt_number = rvmcs(vcpu, VMCS_IDT_VECTORING_INFO) & 0xFF; 208 | int Status = Kernel.dispatch(interrupt_number); 209 | switch (Status) { 210 | case DOSKernel::STATUS_HANDLED: 211 | wreg(vcpu, HV_X86_RIP, rreg(vcpu, HV_X86_RIP) + 2); 212 | break; 213 | case DOSKernel::STATUS_UNSUPPORTED: 214 | case DOSKernel::STATUS_STOP: 215 | stop = 1; 216 | break; 217 | case DOSKernel::STATUS_NORETURN: 218 | // The kernel changed the PC. 219 | break; 220 | default: 221 | break; 222 | } 223 | break; 224 | } 225 | case EXIT_REASON_EXT_INTR: 226 | /* VMEXIT due to host interrupt, nothing to do */ 227 | #if DEBUG 228 | printf("IRQ\n"); 229 | #endif 230 | break; 231 | case EXIT_REASON_HLT: 232 | /* guest executed HLT */ 233 | #if DEBUG 234 | printf("HLT\n"); 235 | #endif 236 | stop = 1; 237 | break; 238 | case EXIT_REASON_EPT_FAULT: 239 | /* disambiguate between EPT cold misses and MMIO */ 240 | /* ... handle MMIO ... */ 241 | break; 242 | /* ... many more exit reasons go here ... */ 243 | default: 244 | printf("unhandled VMEXIT (%llu)\n", exit_reason); 245 | 246 | stop = 1; 247 | } 248 | } while (!stop); 249 | 250 | /* 251 | * optional clean-up 252 | */ 253 | 254 | /* destroy vCPU */ 255 | if (hv_vcpu_destroy(vcpu)) { 256 | abort(); 257 | } 258 | 259 | /* unmap memory segment at address 0 */ 260 | if (hv_vm_unmap(0, VM_MEM_SIZE)) { 261 | abort(); 262 | } 263 | /* destroy VM instance of this task */ 264 | if (hv_vm_destroy()) { 265 | abort(); 266 | } 267 | 268 | free(vm_mem); 269 | 270 | return 0; 271 | } 272 | -------------------------------------------------------------------------------- /interface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009-present, the hvdos developers. All Rights Reserved. 2 | // Read LICENSE.txt for licensing information. 3 | // 4 | // hvdos - a simple DOS emulator based on the OS X 10.10 Hypervisor.framework 5 | 6 | extern uint64_t rreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg); 7 | extern void wreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg, uint64_t v); 8 | 9 | #define readMem8(a) _memory[a] 10 | #define writeMem8(a, v) do { _memory[a] = v; } while (0) 11 | 12 | #define AX ((uint16_t)rreg(_vcpu, HV_X86_RAX)) 13 | #define BX ((uint16_t)rreg(_vcpu, HV_X86_RBX)) 14 | #define CX ((uint16_t)rreg(_vcpu, HV_X86_RCX)) 15 | #define DX ((uint16_t)rreg(_vcpu, HV_X86_RDX)) 16 | 17 | #define pc ((uint16_t)rreg(_vcpu, HV_X86_RIP)) 18 | #define DS rreg(_vcpu, HV_X86_DS) 19 | #define ES rreg(_vcpu, HV_X86_ES) 20 | 21 | #define FLAGS ((uint16_t)rreg(_vcpu, HV_X86_RFLAGS)) 22 | 23 | #define AL ((uint16_t)rreg(_vcpu, HV_X86_RAX) & 0xFF) 24 | #define AH ((uint16_t)rreg(_vcpu, HV_X86_RAX) >> 8) 25 | #define BL ((uint16_t)rreg(_vcpu, HV_X86_RBX) & 0xFF) 26 | #define BH ((uint16_t)rreg(_vcpu, HV_X86_RBX) >> 8) 27 | #define CL ((uint16_t)rreg(_vcpu, HV_X86_RCX) & 0xFF) 28 | #define CH ((uint16_t)rreg(_vcpu, HV_X86_RCX) >> 8) 29 | #define DL ((uint16_t)rreg(_vcpu, HV_X86_RDX) & 0xFF) 30 | #define DH ((uint16_t)rreg(_vcpu, HV_X86_RDX) >> 8) 31 | 32 | #define SET_AX(v) wreg(_vcpu, HV_X86_RAX, v) 33 | #define SET_BX(v) wreg(_vcpu, HV_X86_RBX, v) 34 | #define SET_CX(v) wreg(_vcpu, HV_X86_RCX, v) 35 | #define SET_DX(v) wreg(_vcpu, HV_X86_RDX, v) 36 | 37 | #define SET_DS(v) wreg(_vcpu, HV_X86_DS, v) 38 | #define SET_ES(v) wreg(_vcpu, HV_X86_ES, v) 39 | 40 | #define SET_AL(v) wreg(_vcpu, HV_X86_RAX, (AX & 0xFF00) | v) 41 | #define SET_AH(v) wreg(_vcpu, HV_X86_RAX, (AX & 0xFF) | (v << 8)) 42 | #define SET_BL(v) wreg(_vcpu, HV_X86_RBX, (BX & 0xFF00) | v) 43 | #define SET_BH(v) wreg(_vcpu, HV_X86_RBX, (BX & 0xFF) | (v << 8)) 44 | #define SET_CL(v) wreg(_vcpu, HV_X86_RCX, (CX & 0xFF00) | v) 45 | #define SET_CH(v) wreg(_vcpu, HV_X86_RCX, (CX & 0xFF) | (v << 8)) 46 | #define SET_DL(v) wreg(_vcpu, HV_X86_RDX, (DX & 0xFF00) | v) 47 | #define SET_DH(v) wreg(_vcpu, HV_X86_RDX, (DX & 0xFF) | (v << 8)) 48 | 49 | #define SETC(v) wreg(_vcpu, HV_X86_RFLAGS, (FLAGS & 0xFFFE) | v) 50 | -------------------------------------------------------------------------------- /vmcs.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2011 NetApp, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * $FreeBSD$ 27 | */ 28 | 29 | #ifndef _VMCS_H_ 30 | #define _VMCS_H_ 31 | 32 | #define VMCS_INITIAL 0xffffffffffffffff 33 | 34 | #define VMCS_IDENT(encoding) ((encoding) | 0x80000000) 35 | /* 36 | * VMCS field encodings from Appendix H, Intel Architecture Manual Vol3B. 37 | */ 38 | #define VMCS_INVALID_ENCODING 0xffffffff 39 | 40 | /* 16-bit control fields */ 41 | #define VMCS_VPID 0x00000000 42 | #define VMCS_PIR_VECTOR 0x00000002 43 | 44 | /* 16-bit guest-state fields */ 45 | #define VMCS_GUEST_ES_SELECTOR 0x00000800 46 | #define VMCS_GUEST_CS_SELECTOR 0x00000802 47 | #define VMCS_GUEST_SS_SELECTOR 0x00000804 48 | #define VMCS_GUEST_DS_SELECTOR 0x00000806 49 | #define VMCS_GUEST_FS_SELECTOR 0x00000808 50 | #define VMCS_GUEST_GS_SELECTOR 0x0000080A 51 | #define VMCS_GUEST_LDTR_SELECTOR 0x0000080C 52 | #define VMCS_GUEST_TR_SELECTOR 0x0000080E 53 | #define VMCS_GUEST_INTR_STATUS 0x00000810 54 | 55 | /* 16-bit host-state fields */ 56 | #define VMCS_HOST_ES_SELECTOR 0x00000C00 57 | #define VMCS_HOST_CS_SELECTOR 0x00000C02 58 | #define VMCS_HOST_SS_SELECTOR 0x00000C04 59 | #define VMCS_HOST_DS_SELECTOR 0x00000C06 60 | #define VMCS_HOST_FS_SELECTOR 0x00000C08 61 | #define VMCS_HOST_GS_SELECTOR 0x00000C0A 62 | #define VMCS_HOST_TR_SELECTOR 0x00000C0C 63 | 64 | /* 64-bit control fields */ 65 | #define VMCS_IO_BITMAP_A 0x00002000 66 | #define VMCS_IO_BITMAP_B 0x00002002 67 | #define VMCS_MSR_BITMAP 0x00002004 68 | #define VMCS_EXIT_MSR_STORE 0x00002006 69 | #define VMCS_EXIT_MSR_LOAD 0x00002008 70 | #define VMCS_ENTRY_MSR_LOAD 0x0000200A 71 | #define VMCS_EXECUTIVE_VMCS 0x0000200C 72 | #define VMCS_TSC_OFFSET 0x00002010 73 | #define VMCS_VIRTUAL_APIC 0x00002012 74 | #define VMCS_APIC_ACCESS 0x00002014 75 | #define VMCS_PIR_DESC 0x00002016 76 | #define VMCS_EPTP 0x0000201A 77 | #define VMCS_EOI_EXIT0 0x0000201C 78 | #define VMCS_EOI_EXIT1 0x0000201E 79 | #define VMCS_EOI_EXIT2 0x00002020 80 | #define VMCS_EOI_EXIT3 0x00002022 81 | #define VMCS_EOI_EXIT(vector) (VMCS_EOI_EXIT0 + ((vector) / 64) * 2) 82 | 83 | /* 64-bit read-only fields */ 84 | #define VMCS_GUEST_PHYSICAL_ADDRESS 0x00002400 85 | 86 | /* 64-bit guest-state fields */ 87 | #define VMCS_LINK_POINTER 0x00002800 88 | #define VMCS_GUEST_IA32_DEBUGCTL 0x00002802 89 | #define VMCS_GUEST_IA32_PAT 0x00002804 90 | #define VMCS_GUEST_IA32_EFER 0x00002806 91 | #define VMCS_GUEST_IA32_PERF_GLOBAL_CTRL 0x00002808 92 | #define VMCS_GUEST_PDPTE0 0x0000280A 93 | #define VMCS_GUEST_PDPTE1 0x0000280C 94 | #define VMCS_GUEST_PDPTE2 0x0000280E 95 | #define VMCS_GUEST_PDPTE3 0x00002810 96 | 97 | /* 64-bit host-state fields */ 98 | #define VMCS_HOST_IA32_PAT 0x00002C00 99 | #define VMCS_HOST_IA32_EFER 0x00002C02 100 | #define VMCS_HOST_IA32_PERF_GLOBAL_CTRL 0x00002C04 101 | 102 | /* 32-bit control fields */ 103 | #define VMCS_PIN_BASED_CTLS 0x00004000 104 | #define VMCS_PRI_PROC_BASED_CTLS 0x00004002 105 | #define VMCS_EXCEPTION_BITMAP 0x00004004 106 | #define VMCS_PF_ERROR_MASK 0x00004006 107 | #define VMCS_PF_ERROR_MATCH 0x00004008 108 | #define VMCS_CR3_TARGET_COUNT 0x0000400A 109 | #define VMCS_EXIT_CTLS 0x0000400C 110 | #define VMCS_EXIT_MSR_STORE_COUNT 0x0000400E 111 | #define VMCS_EXIT_MSR_LOAD_COUNT 0x00004010 112 | #define VMCS_ENTRY_CTLS 0x00004012 113 | #define VMCS_ENTRY_MSR_LOAD_COUNT 0x00004014 114 | #define VMCS_ENTRY_INTR_INFO 0x00004016 115 | #define VMCS_ENTRY_EXCEPTION_ERROR 0x00004018 116 | #define VMCS_ENTRY_INST_LENGTH 0x0000401A 117 | #define VMCS_TPR_THRESHOLD 0x0000401C 118 | #define VMCS_SEC_PROC_BASED_CTLS 0x0000401E 119 | #define VMCS_PLE_GAP 0x00004020 120 | #define VMCS_PLE_WINDOW 0x00004022 121 | 122 | /* 32-bit read-only data fields */ 123 | #define VMCS_INSTRUCTION_ERROR 0x00004400 124 | #define VMCS_EXIT_REASON 0x00004402 125 | #define VMCS_EXIT_INTR_INFO 0x00004404 126 | #define VMCS_EXIT_INTR_ERRCODE 0x00004406 127 | #define VMCS_IDT_VECTORING_INFO 0x00004408 128 | #define VMCS_IDT_VECTORING_ERROR 0x0000440A 129 | #define VMCS_EXIT_INSTRUCTION_LENGTH 0x0000440C 130 | #define VMCS_EXIT_INSTRUCTION_INFO 0x0000440E 131 | 132 | /* 32-bit guest-state fields */ 133 | #define VMCS_GUEST_ES_LIMIT 0x00004800 134 | #define VMCS_GUEST_CS_LIMIT 0x00004802 135 | #define VMCS_GUEST_SS_LIMIT 0x00004804 136 | #define VMCS_GUEST_DS_LIMIT 0x00004806 137 | #define VMCS_GUEST_FS_LIMIT 0x00004808 138 | #define VMCS_GUEST_GS_LIMIT 0x0000480A 139 | #define VMCS_GUEST_LDTR_LIMIT 0x0000480C 140 | #define VMCS_GUEST_TR_LIMIT 0x0000480E 141 | #define VMCS_GUEST_GDTR_LIMIT 0x00004810 142 | #define VMCS_GUEST_IDTR_LIMIT 0x00004812 143 | #define VMCS_GUEST_ES_ACCESS_RIGHTS 0x00004814 144 | #define VMCS_GUEST_CS_ACCESS_RIGHTS 0x00004816 145 | #define VMCS_GUEST_SS_ACCESS_RIGHTS 0x00004818 146 | #define VMCS_GUEST_DS_ACCESS_RIGHTS 0x0000481A 147 | #define VMCS_GUEST_FS_ACCESS_RIGHTS 0x0000481C 148 | #define VMCS_GUEST_GS_ACCESS_RIGHTS 0x0000481E 149 | #define VMCS_GUEST_LDTR_ACCESS_RIGHTS 0x00004820 150 | #define VMCS_GUEST_TR_ACCESS_RIGHTS 0x00004822 151 | #define VMCS_GUEST_INTERRUPTIBILITY 0x00004824 152 | #define VMCS_GUEST_ACTIVITY 0x00004826 153 | #define VMCS_GUEST_SMBASE 0x00004828 154 | #define VMCS_GUEST_IA32_SYSENTER_CS 0x0000482A 155 | #define VMCS_PREEMPTION_TIMER_VALUE 0x0000482E 156 | 157 | /* 32-bit host state fields */ 158 | #define VMCS_HOST_IA32_SYSENTER_CS 0x00004C00 159 | 160 | /* Natural Width control fields */ 161 | #define VMCS_CR0_MASK 0x00006000 162 | #define VMCS_CR4_MASK 0x00006002 163 | #define VMCS_CR0_SHADOW 0x00006004 164 | #define VMCS_CR4_SHADOW 0x00006006 165 | #define VMCS_CR3_TARGET0 0x00006008 166 | #define VMCS_CR3_TARGET1 0x0000600A 167 | #define VMCS_CR3_TARGET2 0x0000600C 168 | #define VMCS_CR3_TARGET3 0x0000600E 169 | 170 | /* Natural Width read-only fields */ 171 | #define VMCS_EXIT_QUALIFICATION 0x00006400 172 | #define VMCS_IO_RCX 0x00006402 173 | #define VMCS_IO_RSI 0x00006404 174 | #define VMCS_IO_RDI 0x00006406 175 | #define VMCS_IO_RIP 0x00006408 176 | #define VMCS_GUEST_LINEAR_ADDRESS 0x0000640A 177 | 178 | /* Natural Width guest-state fields */ 179 | #define VMCS_GUEST_CR0 0x00006800 180 | #define VMCS_GUEST_CR3 0x00006802 181 | #define VMCS_GUEST_CR4 0x00006804 182 | #define VMCS_GUEST_ES_BASE 0x00006806 183 | #define VMCS_GUEST_CS_BASE 0x00006808 184 | #define VMCS_GUEST_SS_BASE 0x0000680A 185 | #define VMCS_GUEST_DS_BASE 0x0000680C 186 | #define VMCS_GUEST_FS_BASE 0x0000680E 187 | #define VMCS_GUEST_GS_BASE 0x00006810 188 | #define VMCS_GUEST_LDTR_BASE 0x00006812 189 | #define VMCS_GUEST_TR_BASE 0x00006814 190 | #define VMCS_GUEST_GDTR_BASE 0x00006816 191 | #define VMCS_GUEST_IDTR_BASE 0x00006818 192 | #define VMCS_GUEST_DR7 0x0000681A 193 | #define VMCS_GUEST_RSP 0x0000681C 194 | #define VMCS_GUEST_RIP 0x0000681E 195 | #define VMCS_GUEST_RFLAGS 0x00006820 196 | #define VMCS_GUEST_PENDING_DBG_EXCEPTIONS 0x00006822 197 | #define VMCS_GUEST_IA32_SYSENTER_ESP 0x00006824 198 | #define VMCS_GUEST_IA32_SYSENTER_EIP 0x00006826 199 | 200 | /* Natural Width host-state fields */ 201 | #define VMCS_HOST_CR0 0x00006C00 202 | #define VMCS_HOST_CR3 0x00006C02 203 | #define VMCS_HOST_CR4 0x00006C04 204 | #define VMCS_HOST_FS_BASE 0x00006C06 205 | #define VMCS_HOST_GS_BASE 0x00006C08 206 | #define VMCS_HOST_TR_BASE 0x00006C0A 207 | #define VMCS_HOST_GDTR_BASE 0x00006C0C 208 | #define VMCS_HOST_IDTR_BASE 0x00006C0E 209 | #define VMCS_HOST_IA32_SYSENTER_ESP 0x00006C10 210 | #define VMCS_HOST_IA32_SYSENTER_EIP 0x00006C12 211 | #define VMCS_HOST_RSP 0x00006C14 212 | #define VMCS_HOST_RIP 0x00006c16 213 | 214 | /* 215 | * VM instruction error numbers 216 | */ 217 | #define VMRESUME_WITH_NON_LAUNCHED_VMCS 5 218 | 219 | /* 220 | * VMCS exit reasons 221 | */ 222 | #define EXIT_REASON_EXCEPTION 0 223 | #define EXIT_REASON_EXT_INTR 1 224 | #define EXIT_REASON_TRIPLE_FAULT 2 225 | #define EXIT_REASON_INIT 3 226 | #define EXIT_REASON_SIPI 4 227 | #define EXIT_REASON_IO_SMI 5 228 | #define EXIT_REASON_SMI 6 229 | #define EXIT_REASON_INTR_WINDOW 7 230 | #define EXIT_REASON_NMI_WINDOW 8 231 | #define EXIT_REASON_TASK_SWITCH 9 232 | #define EXIT_REASON_CPUID 10 233 | #define EXIT_REASON_GETSEC 11 234 | #define EXIT_REASON_HLT 12 235 | #define EXIT_REASON_INVD 13 236 | #define EXIT_REASON_INVLPG 14 237 | #define EXIT_REASON_RDPMC 15 238 | #define EXIT_REASON_RDTSC 16 239 | #define EXIT_REASON_RSM 17 240 | #define EXIT_REASON_VMCALL 18 241 | #define EXIT_REASON_VMCLEAR 19 242 | #define EXIT_REASON_VMLAUNCH 20 243 | #define EXIT_REASON_VMPTRLD 21 244 | #define EXIT_REASON_VMPTRST 22 245 | #define EXIT_REASON_VMREAD 23 246 | #define EXIT_REASON_VMRESUME 24 247 | #define EXIT_REASON_VMWRITE 25 248 | #define EXIT_REASON_VMXOFF 26 249 | #define EXIT_REASON_VMXON 27 250 | #define EXIT_REASON_CR_ACCESS 28 251 | #define EXIT_REASON_DR_ACCESS 29 252 | #define EXIT_REASON_INOUT 30 253 | #define EXIT_REASON_RDMSR 31 254 | #define EXIT_REASON_WRMSR 32 255 | #define EXIT_REASON_INVAL_VMCS 33 256 | #define EXIT_REASON_INVAL_MSR 34 257 | #define EXIT_REASON_MWAIT 36 258 | #define EXIT_REASON_MTF 37 259 | #define EXIT_REASON_MONITOR 39 260 | #define EXIT_REASON_PAUSE 40 261 | #define EXIT_REASON_MCE_DURING_ENTRY 41 262 | #define EXIT_REASON_TPR 43 263 | #define EXIT_REASON_APIC_ACCESS 44 264 | #define EXIT_REASON_VIRTUALIZED_EOI 45 265 | #define EXIT_REASON_GDTR_IDTR 46 266 | #define EXIT_REASON_LDTR_TR 47 267 | #define EXIT_REASON_EPT_FAULT 48 268 | #define EXIT_REASON_EPT_MISCONFIG 49 269 | #define EXIT_REASON_INVEPT 50 270 | #define EXIT_REASON_RDTSCP 51 271 | #define EXIT_REASON_VMX_PREEMPT 52 272 | #define EXIT_REASON_INVVPID 53 273 | #define EXIT_REASON_WBINVD 54 274 | #define EXIT_REASON_XSETBV 55 275 | #define EXIT_REASON_APIC_WRITE 56 276 | 277 | /* 278 | * NMI unblocking due to IRET. 279 | * 280 | * Applies to VM-exits due to hardware exception or EPT fault. 281 | */ 282 | #define EXIT_QUAL_NMIUDTI (1 << 12) 283 | /* 284 | * VMCS interrupt information fields 285 | */ 286 | #define VMCS_INTR_VALID (1U << 31) 287 | #define VMCS_INTR_T_MASK 0x700 /* Interruption-info type */ 288 | #define VMCS_INTR_T_HWINTR (0 << 8) 289 | #define VMCS_INTR_T_NMI (2 << 8) 290 | #define VMCS_INTR_T_HWEXCEPTION (3 << 8) 291 | #define VMCS_INTR_T_SWINTR (4 << 8) 292 | #define VMCS_INTR_T_PRIV_SWEXCEPTION (5 << 8) 293 | #define VMCS_INTR_T_SWEXCEPTION (6 << 8) 294 | #define VMCS_INTR_DEL_ERRCODE (1 << 11) 295 | 296 | /* 297 | * VMCS IDT-Vectoring information fields 298 | */ 299 | #define VMCS_IDT_VEC_VALID (1U << 31) 300 | #define VMCS_IDT_VEC_ERRCODE_VALID (1 << 11) 301 | 302 | /* 303 | * VMCS Guest interruptibility field 304 | */ 305 | #define VMCS_INTERRUPTIBILITY_STI_BLOCKING (1 << 0) 306 | #define VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING (1 << 1) 307 | #define VMCS_INTERRUPTIBILITY_SMI_BLOCKING (1 << 2) 308 | #define VMCS_INTERRUPTIBILITY_NMI_BLOCKING (1 << 3) 309 | 310 | /* 311 | * Exit qualification for EXIT_REASON_INVAL_VMCS 312 | */ 313 | #define EXIT_QUAL_NMI_WHILE_STI_BLOCKING 3 314 | 315 | /* 316 | * Exit qualification for EPT violation 317 | */ 318 | #define EPT_VIOLATION_DATA_READ (1UL << 0) 319 | #define EPT_VIOLATION_DATA_WRITE (1UL << 1) 320 | #define EPT_VIOLATION_INST_FETCH (1UL << 2) 321 | #define EPT_VIOLATION_GPA_READABLE (1UL << 3) 322 | #define EPT_VIOLATION_GPA_WRITEABLE (1UL << 4) 323 | #define EPT_VIOLATION_GPA_EXECUTABLE (1UL << 5) 324 | #define EPT_VIOLATION_GLA_VALID (1UL << 7) 325 | #define EPT_VIOLATION_XLAT_VALID (1UL << 8) 326 | 327 | /* 328 | * Exit qualification for APIC-access VM exit 329 | */ 330 | #define APIC_ACCESS_OFFSET(qual) ((qual) & 0xFFF) 331 | #define APIC_ACCESS_TYPE(qual) (((qual) >> 12) & 0xF) 332 | 333 | /* 334 | * Exit qualification for APIC-write VM exit 335 | */ 336 | #define APIC_WRITE_OFFSET(qual) ((qual) & 0xFFF) 337 | 338 | #endif 339 | --------------------------------------------------------------------------------