From 59136514da5a6359da17495ba1e69050936e8696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Thu, 1 Oct 2015 00:09:24 +0000 Subject: [PATCH] [NTVDM] - Add a basic boot sequence functionality (read from CMOS); will be improved in the future. - Print a "FATAL BOOT FAILURE" error message when INT 18h is called. - Fail startup if we cannot mount the available hard disk images. - Improve some diagnostic error messages. svn path=/trunk/; revision=69421 --- .../mvdm/ntvdm/bios/bios32/bios32.c | 172 ++++++++++++------ reactos/subsystems/mvdm/ntvdm/dos/dem.c | 77 -------- reactos/subsystems/mvdm/ntvdm/dos/dem.h | 11 +- reactos/subsystems/mvdm/ntvdm/emulator.c | 12 +- reactos/subsystems/mvdm/ntvdm/hardware/cmos.h | 1 + reactos/subsystems/mvdm/ntvdm/hardware/disk.c | 12 +- reactos/subsystems/mvdm/ntvdm/ntvdm.c | 82 ++++++++- reactos/subsystems/mvdm/ntvdm/ntvdm.h | 3 + 8 files changed, 216 insertions(+), 154 deletions(-) diff --git a/reactos/subsystems/mvdm/ntvdm/bios/bios32/bios32.c b/reactos/subsystems/mvdm/ntvdm/bios/bios32/bios32.c index 89eee5138f9..9e0d529dcce 100644 --- a/reactos/subsystems/mvdm/ntvdm/bios/bios32/bios32.c +++ b/reactos/subsystems/mvdm/ntvdm/bios/bios32/bios32.c @@ -161,6 +161,31 @@ static const BYTE PostCode[] = /* PRIVATE FUNCTIONS **********************************************************/ +static VOID BiosCharPrint(CHAR Character) +{ + /* Save AX and BX */ + USHORT AX = getAX(); + USHORT BX = getBX(); + + /* + * Set the parameters: + * AL contains the character to print, + * BL contains the character attribute, + * BH contains the video page to use. + */ + setAL(Character); + setBL(DEFAULT_ATTRIBUTE); + setBH(Bda->VideoPage); + + /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */ + setAH(0x0E); + Int32Call(&BiosContext, BIOS_VIDEO_INTERRUPT); + + /* Restore AX and BX */ + setBX(BX); + setAX(AX); +} + static VOID WINAPI BiosException(LPWORD Stack) { /* Get the exception number and call the emulator API */ @@ -505,6 +530,8 @@ static VOID WINAPI BiosMiscService(LPWORD Stack) static VOID WINAPI BiosRomBasic(LPWORD Stack) { + PrintMessageAnsi(BiosCharPrint, "FATAL: INT18: BOOT FAILURE."); + /* ROM Basic is unsupported, display a message to the user */ DisplayMessage(L"NTVDM doesn't support ROM Basic. The VDM is closing."); @@ -518,79 +545,110 @@ extern VOID WINAPI BiosDiskService(LPWORD Stack); static VOID WINAPI BiosBootstrapLoader(LPWORD Stack) { + USHORT BootOrder; + + USHORT AX, BX, CX, DX, ES; + AX = getAX(); + BX = getBX(); + CX = getCX(); + DX = getDX(); + ES = getES(); + /* - * In real BIOSes one loads the bootsector read from a diskette - * or from a disk, copy it to 0000:7C00 and then boot it. - * Since we are 32-bit VM and we hardcode our DOS at the moment, - * just call the DOS 32-bit initialization code. + * Read the boot sequence order from the CMOS, old behaviour AMI-style. + * + * For more information, see: + * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/orgs.asm + * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/PC/BIOS/boot.c + * http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/cmos.cc + * https://web.archive.org/web/20111209041013/http://www-ivs.cs.uni-magdeburg.de/~zbrog/asm/cmos.html + * http://www.bioscentral.com/misc/cmosmap.htm + */ + IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_SYSOP); + BootOrder = (IOReadB(CMOS_DATA_PORT) & 0x20) >> 5; + + /* + * BootOrder = + * 0: Hard Disk, then Floppy Disk + * 1: Floppy Disk, then Hard Disk + * In all cases, if booting from those devices failed, + * ROM DOS-32 is started. If it fails, INT 18h is called. */ - DPRINT("BiosBootstrapLoader -->\n"); + DPRINT("BiosBootstrapLoader (BootOrder = 0x%02X) -->\n", BootOrder); + /* + * Format of the BootOrder command: + * 2 bytes. Each half-byte contains the ID of the drive to boot. + * Currently defined: + * 0x0: 1st Floppy disk + * 0x1: 1st Hard disk + * Other, or 0xF: Stop boot sequence. + */ + BootOrder = 0xFF00 | ((1 << (4 * BootOrder)) & 0xFF); + +Retry: + switch (BootOrder & 0x0F) { - USHORT i; - - USHORT AX, BX, CX, DX, ES; - AX = getAX(); - BX = getBX(); - CX = getCX(); - DX = getDX(); - ES = getES(); - - // i = 0; - i = 1; - do + /* Boot from 1st floppy drive */ + case 0: { - if (i == 0) - { - /* Boot from 1st floppy drive */ + setAH(0x02); // Read sectors + setAL(0x01); // Number of sectors + setDH(0x00); // Head 0 + setCH(0x00); // Cylinder 0 + setCL(0x01); // Sector 1 + setDL(0x00); // First diskette drive (used by loader code, so should not be cleared) + setES(0x0000); // Write data in 0000:7C00 + setBX(0x7C00); + BiosDiskService(Stack); + if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit; + DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH()); - setAH(0x02); // Read sectors - setAL(0x01); // Number of sectors - setDH(0x00); // First head - setCH(0x00); // First cylinder - setCL(0x01); // First sector - setDL(0x00); // First diskette drive (used by loader code, so should not be cleared) - setES(0x0000); // Place data in 0000:7C00 - setBX(0x7C00); - BiosDiskService(Stack); - if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit; - DPRINT1("An error happened while loading the bootsector from floppy 0, error = %d\n", getAH()); - } - else if (i == 1) - { - /* Boot from 1st HDD drive */ + break; + } - setAH(0x02); // Read sectors - setAL(0x01); // Number of sectors - setDH(0x00); // First head - setCH(0x00); // First cylinder - setCL(0x01); // First sector - setDL(0x80); // First HDD drive (used by loader code, so should not be cleared) - setES(0x0000); // Place data in 0000:7C00 - setBX(0x7C00); - BiosDiskService(Stack); - if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit; - DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH()); - } - // } while (i++ < 1); - } while (i-- > 0); + /* Boot from 1st HDD drive */ + case 1: + { + setAH(0x02); // Read sectors + setAL(0x01); // Number of sectors + setDH(0x00); // Head 0 + setCH(0x00); // Cylinder 0 + setCL(0x01); // Sector 1 + setDL(0x80); // First HDD drive (used by loader code, so should not be cleared) + setES(0x0000); // Write data in 0000:7C00 + setBX(0x7C00); + BiosDiskService(Stack); + if (!(Stack[STACK_FLAGS] & EMULATOR_FLAG_CF)) goto Quit; + DPRINT1("An error happened while loading the bootsector from HDD 0, error = %d\n", getAH()); - /* Clear everything, we are going to load DOS32 */ - setAX(AX); - setBX(BX); - setCX(CX); - setDX(DX); - setES(ES); - Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + break; + } + + default: + goto StartDos; } + /* Go to next drive and invalidate the last half-byte. */ + BootOrder = (BootOrder >> 4) | 0xF000; + goto Retry; + +StartDos: + /* Clear everything, we are going to load DOS32 */ + setAX(AX); + setBX(BX); + setCX(CX); + setDX(DX); + setES(ES); + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + /* Load our DOS */ DosBootsectorInitialize(); Quit: /* - * Position CPU to 0000:7C00 to boot the OS. + * Jump to 0000:7C00 to boot the OS. * * Since we are called via the INT32 mechanism, we need to correctly set * CS:IP, not by changing the current one (otherwise the interrupt could diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dem.c b/reactos/subsystems/mvdm/ntvdm/dos/dem.c index b3642215c23..1af10697aef 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dem.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dem.c @@ -82,83 +82,6 @@ VOID DosCharPrint(CHAR Character) DosPrintCharacter(DOS_OUTPUT_HANDLE, Character); } -/* - * This function, derived from ntvdm.c!DisplayMessage, is used by the BIOS and - * the DOS to display messages to an output device. A printer function is given - * for printing the characters. - */ -static VOID -DisplayMessageAnsiV(IN CHAR_PRINT CharPrint, - IN LPCSTR Format, - IN va_list args) -{ - static CHAR CurChar = 0; - LPSTR str; - -#ifndef WIN2K_COMPLIANT - CHAR StaticBuffer[256]; - LPSTR Buffer = StaticBuffer; // Use the static buffer by default. -#else - CHAR Buffer[2048]; // Large enough. If not, increase it by hand. -#endif - size_t MsgLen; - -#ifndef WIN2K_COMPLIANT - /* - * Retrieve the message length and if it is too long, allocate - * an auxiliary buffer; otherwise use the static buffer. - * The string is built to be NULL-terminated. - */ - MsgLen = _vscprintf(Format, args); - if (MsgLen >= ARRAYSIZE(StaticBuffer)) - { - Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(CHAR)); - if (Buffer == NULL) - { - /* Allocation failed, use the static buffer and display a suitable error message */ - Buffer = StaticBuffer; - Format = "DisplayMessageAnsi()\nOriginal message is too long and allocating an auxiliary buffer failed."; - MsgLen = strlen(Format); - } - } -#else - MsgLen = ARRAYSIZE(Buffer) - 1; -#endif - - RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(CHAR)); - _vsnprintf(Buffer, MsgLen, Format, args); - - /* Display the message */ - DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer); - - MsgLen = strlen(Buffer); - str = Buffer; - while (MsgLen--) - { - if (*str == '\n' && CurChar != '\r') - CharPrint('\r'); - - CurChar = *str++; - CharPrint(CurChar); - } - -#ifndef WIN2K_COMPLIANT - /* Free the buffer if needed */ - if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); -#endif -} - -VOID -DemDisplayMessage(IN CHAR_PRINT CharPrint, - IN LPCSTR Format, ...) -{ - va_list Parameters; - - va_start(Parameters, Format); - DisplayMessageAnsiV(CharPrint, Format, Parameters); - va_end(Parameters); -} - static VOID DemLoadNTDOSKernel(VOID) { diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dem.h b/reactos/subsystems/mvdm/ntvdm/dos/dem.h index 9ee27a59926..befbe80dc61 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dem.h +++ b/reactos/subsystems/mvdm/ntvdm/dos/dem.h @@ -29,18 +29,13 @@ /* FUNCTIONS ******************************************************************/ -typedef VOID (*CHAR_PRINT)(IN CHAR Character); VOID BiosCharPrint(CHAR Character); -VOID DosCharPrint(CHAR Character); - -VOID DemDisplayMessage(IN CHAR_PRINT CharPrint, - IN LPCSTR Format, ...); - #define BiosDisplayMessage(Format, ...) \ - DemDisplayMessage(BiosCharPrint, (Format), ##__VA_ARGS__) + PrintMessageAnsi(BiosCharPrint, (Format), ##__VA_ARGS__) +VOID DosCharPrint(CHAR Character); #define DosDisplayMessage(Format, ...) \ - DemDisplayMessage(DosCharPrint, (Format), ##__VA_ARGS__) + PrintMessageAnsi(DosCharPrint, (Format), ##__VA_ARGS__) BOOLEAN DosShutdown(BOOLEAN Immediate); diff --git a/reactos/subsystems/mvdm/ntvdm/emulator.c b/reactos/subsystems/mvdm/ntvdm/emulator.c index 344f64e5af0..fa55abb5d40 100644 --- a/reactos/subsystems/mvdm/ntvdm/emulator.c +++ b/reactos/subsystems/mvdm/ntvdm/emulator.c @@ -564,14 +564,22 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) } } - /* Mount the available hard disks */ + /* + * Mount the available hard disks. Contrary to floppies, failing + * mounting a hard disk is considered as an unrecoverable error. + */ for (i = 0; i < ARRAYSIZE(GlobalSettings.HardDisks); ++i) { if (GlobalSettings.HardDisks[i].Length != 0 && GlobalSettings.HardDisks[i].Buffer && GlobalSettings.HardDisks[i].Buffer != '\0') { - MountDisk(HARD_DISK, i, GlobalSettings.HardDisks[i].Buffer, FALSE); + if (!MountDisk(HARD_DISK, i, GlobalSettings.HardDisks[i].Buffer, FALSE)) + { + wprintf(L"FATAL: Failed to mount hard disk file '%Z'.\n", &GlobalSettings); + EmulatorCleanup(); + return FALSE; + } } } diff --git a/reactos/subsystems/mvdm/ntvdm/hardware/cmos.h b/reactos/subsystems/mvdm/ntvdm/hardware/cmos.h index 45a222fbe33..3420f57d0ef 100644 --- a/reactos/subsystems/mvdm/ntvdm/hardware/cmos.h +++ b/reactos/subsystems/mvdm/ntvdm/hardware/cmos.h @@ -70,6 +70,7 @@ typedef enum _CMOS_REGISTERS CMOS_REG_BASE_MEMORY_HIGH = 0x16, CMOS_REG_EXT_MEMORY_LOW = 0x17, CMOS_REG_EXT_MEMORY_HIGH = 0x18, + CMOS_REG_SYSOP = 0x2D, CMOS_REG_ACTUAL_EXT_MEMORY_LOW = 0x30, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH = 0x31, CMOS_REG_CENTURY = 0x32, diff --git a/reactos/subsystems/mvdm/ntvdm/hardware/disk.c b/reactos/subsystems/mvdm/ntvdm/hardware/disk.c index 6c67312bda5..581efae88fb 100644 --- a/reactos/subsystems/mvdm/ntvdm/hardware/disk.c +++ b/reactos/subsystems/mvdm/ntvdm/hardware/disk.c @@ -199,7 +199,7 @@ MountFDI(IN PDISK_IMAGE DiskImage, if (FileSize == INVALID_FILE_SIZE && GetLastError() != ERROR_SUCCESS) { /* We failed, bail out */ - DisplayMessage(L"Error when retrieving file size, or size too large (%d).", FileSize); + DisplayMessage(L"MountFDI: Error when retrieving file size, or size too large (%d).", FileSize); return FALSE; } @@ -547,7 +547,11 @@ MountDisk(IN DISK_TYPE DiskType, FileName, hFile != INVALID_HANDLE_VALUE ? "succeeded" : "failed", GetLastError()); /* If we failed, bail out */ - if (hFile == INVALID_HANDLE_VALUE) return FALSE; + if (hFile == INVALID_HANDLE_VALUE) + { + DisplayMessage(L"MountDisk: Error when opening disk file '%S' (Error: %u).", FileName, GetLastError()); + return FALSE; + } /* OK, we have a handle to the file */ @@ -563,7 +567,7 @@ MountDisk(IN DISK_TYPE DiskType, GetLastError() == ERROR_INVALID_FUNCTION) { /* Objects other than real files are not supported */ - DisplayMessage(L"'%S' is not a valid disk file.", FileName); + DisplayMessage(L"MountDisk: '%S' is not a valid disk file.", FileName); goto Quit; } SetLastError(0); @@ -571,7 +575,7 @@ MountDisk(IN DISK_TYPE DiskType, GetLastError() == ERROR_INVALID_FUNCTION) { /* Objects other than real files are not supported */ - DisplayMessage(L"'%S' is not a valid disk file.", FileName); + DisplayMessage(L"MountDisk: '%S' is not a valid disk file.", FileName); goto Quit; } diff --git a/reactos/subsystems/mvdm/ntvdm/ntvdm.c b/reactos/subsystems/mvdm/ntvdm/ntvdm.c index 70948070dd5..29ecf73a2e0 100644 --- a/reactos/subsystems/mvdm/ntvdm/ntvdm.c +++ b/reactos/subsystems/mvdm/ntvdm/ntvdm.c @@ -562,9 +562,9 @@ DisplayMessage(IN LPCWSTR Format, ...) WCHAR Buffer[2048]; // Large enough. If not, increase it by hand. #endif size_t MsgLen; - va_list Parameters; + va_list args; - va_start(Parameters, Format); + va_start(args, Format); #ifndef WIN2K_COMPLIANT /* @@ -572,7 +572,7 @@ DisplayMessage(IN LPCWSTR Format, ...) * an auxiliary buffer; otherwise use the static buffer. * The string is built to be NULL-terminated. */ - MsgLen = _vscwprintf(Format, Parameters); + MsgLen = _vscwprintf(Format, args); if (MsgLen >= ARRAYSIZE(StaticBuffer)) { Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR)); @@ -589,9 +589,9 @@ DisplayMessage(IN LPCWSTR Format, ...) #endif RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(WCHAR)); - _vsnwprintf(Buffer, MsgLen, Format, Parameters); + _vsnwprintf(Buffer, MsgLen, Format, args); - va_end(Parameters); + va_end(args); /* Display the message */ DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer); @@ -603,6 +603,76 @@ DisplayMessage(IN LPCWSTR Format, ...) #endif } +/* + * This function, derived from DisplayMessage, is used by the BIOS and + * the DOS to display messages to an output device. A printer function + * is given for printing the characters. + */ +VOID +PrintMessageAnsi(IN CHAR_PRINT CharPrint, + IN LPCSTR Format, ...) +{ + static CHAR CurChar = 0; + LPSTR str; + +#ifndef WIN2K_COMPLIANT + CHAR StaticBuffer[256]; + LPSTR Buffer = StaticBuffer; // Use the static buffer by default. +#else + CHAR Buffer[2048]; // Large enough. If not, increase it by hand. +#endif + size_t MsgLen; + va_list args; + + va_start(args, Format); + +#ifndef WIN2K_COMPLIANT + /* + * Retrieve the message length and if it is too long, allocate + * an auxiliary buffer; otherwise use the static buffer. + * The string is built to be NULL-terminated. + */ + MsgLen = _vscprintf(Format, args); + if (MsgLen >= ARRAYSIZE(StaticBuffer)) + { + Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(CHAR)); + if (Buffer == NULL) + { + /* Allocation failed, use the static buffer and display a suitable error message */ + Buffer = StaticBuffer; + Format = "DisplayMessageAnsi()\nOriginal message is too long and allocating an auxiliary buffer failed."; + MsgLen = strlen(Format); + } + } +#else + MsgLen = ARRAYSIZE(Buffer) - 1; +#endif + + RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(CHAR)); + _vsnprintf(Buffer, MsgLen, Format, args); + + va_end(args); + + /* Display the message */ + // DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer); + + MsgLen = strlen(Buffer); + str = Buffer; + while (MsgLen--) + { + if (*str == '\n' && CurChar != '\r') + CharPrint('\r'); + + CurChar = *str++; + CharPrint(CurChar); + } + +#ifndef WIN2K_COMPLIANT + /* Free the buffer if needed */ + if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); +#endif +} + static VOID ConsoleCleanup(VOID); @@ -877,7 +947,7 @@ wmain(INT argc, WCHAR *argv[]) } #endif - /* Load global VDM settings */ + /* Load the global VDM settings */ LoadGlobalSettings(&GlobalSettings); DPRINT1("\n\n\nNTVDM - Starting...\n\n\n"); diff --git a/reactos/subsystems/mvdm/ntvdm/ntvdm.h b/reactos/subsystems/mvdm/ntvdm/ntvdm.h index d43a26e8914..e1d48e46f34 100644 --- a/reactos/subsystems/mvdm/ntvdm/ntvdm.h +++ b/reactos/subsystems/mvdm/ntvdm/ntvdm.h @@ -94,7 +94,10 @@ extern HWND hConsoleWnd; /* * Interface functions */ +typedef VOID (*CHAR_PRINT)(IN CHAR Character); VOID DisplayMessage(IN LPCWSTR Format, ...); +VOID PrintMessageAnsi(IN CHAR_PRINT CharPrint, + IN LPCSTR Format, ...); /*static*/ VOID CreateVdmMenu(HANDLE ConOutHandle);