Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Vga Text Mode: To Hell and Back (Part II)

5.00/5 (4 votes)
19 May 2018CPOL2 min read 9.7K  
This article shows how to enter vga text mode and return from it on Windows

Introduction

On Windows Vista and Windows 7, display driver can adhere to either XPDM or WDDM.

The previous article covered XPDM on Windows 7 case. On Windows 8+, the only possible choice for display driver is to adhere to WDDM. In WDDM, there are no longer separate drivers for video and display, we have only display port and display miniport.

According to this MSDN article, there are three types of display miniport drivers introduced:

  1. Full Graphics Driver
  2. Display Only Driver
  3. Render Only Driver

On Windows 8 (did not check on Windows 10), we have default BasicDisplay.sys and BasicRender.sys drivers. VirtualBox supplies full graphics driver VBoxVideoW8.sys that can replace default pair.

All this means that we can no longer call video driver because there is no EngDeviceIoControl involved. So we have to get video mode and set it manually. To figure out how to do this, I stepped into IOCTL_VIDEO_SET_CURRENT_MODE call:

C++
VgaDeviceIoControl(pDeviceObject, IOCTL_VIDEO_SET_CURRENT_MODE, 
                                   &VideoMode, sizeof(VideoMode), NULL, 0, &BytesReturned, FALSE);

and found out that video driver uses x86BiosCall function to set video mode. With this in mind, I went to Windows 8 and set breakpoint on x86BiosCall. I found two x86BiosCall calls inside BasicDisplay!BiosSetDisplayMode function. The first call sets specified video mode and the second call checks current video mode (whether mode was successfully set).

Using the Code

Let's see the code. User mode part remains the same, so I won't duplicate it here. We need to disable gpus, run driver, refresh desktop, enable gpus. With this in mind, let's see the driver itself.

C++
#include <wdm.h>

#define DELAY_SECOND                        -10000000

struct X86_BIOS_REGISTERS
{
    ULONG Eax;
    ULONG Ecx;
    ULONG Edx;
    ULONG Ebx;
    ULONG Ebp;
    ULONG Esi;
    ULONG Edi;
    USHORT SegDs;
    USHORT SegEs;
};

extern "C"
{
    NTKERNELAPI BOOLEAN x86BiosCall
        (ULONG InterruptNumber, X86_BIOS_REGISTERS *pRegisters);        // import from HAL.DLL

    NTKERNELAPI VOID VidResetDisplay(BOOLEAN HalReset);                 // import from BOOTVID.DLL

    NTKERNELAPI VOID VidDisplayString(PUCHAR String);                   // import from BOOTVID.DLL
}

NTSTATUS VgaGetMode(ULONG *pMode)
{
    BOOLEAN b;
    NTSTATUS Status;
    X86_BIOS_REGISTERS Registers;

    memset(&Registers, 0, sizeof(Registers));

    Registers.Eax = 0x4F03;

    b = x86BiosCall(0x10, &Registers);

    if ((b) && (Registers.Eax == 0x4F))
    {
        *pMode = Registers.Ebx;
        Status = STATUS_SUCCESS;
    }
    else Status = STATUS_UNSUCCESSFUL;

    return Status;
}

NTSTATUS VgaSetMode(ULONG Mode)
{
    BOOLEAN b;
    NTSTATUS Status;
    X86_BIOS_REGISTERS Registers;

    memset(&Registers, 0, sizeof(Registers));

    Registers.Eax = 0x4F02;
    Registers.Ebx = 0x4000 | Mode;

    b = x86BiosCall(0x10, &Registers);

    if ((b) && (Registers.Eax == 0x4F))
    {
        Status = STATUS_SUCCESS;
    }
    else Status = STATUS_UNSUCCESSFUL;

    return Status;
}

void DriverUnload(PDRIVER_OBJECT pDriverObject)
{
}

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
    ULONG Mode;
    NTSTATUS Status;
    LARGE_INTEGER Interval;

    Status = VgaGetMode(&Mode);

    if (NT_SUCCESS(Status))
    {
        VidResetDisplay(TRUE);
        VidDisplayString((PUCHAR)"HELLO\n");

        Interval.QuadPart = DELAY_SECOND * 10;
        KeDelayExecutionThread(KernelMode, FALSE, &Interval);

        VgaSetMode(Mode);
    }

    pDriverObject->DriverUnload = DriverUnload;

    return STATUS_SUCCESS;
}

You can see this link for VESA BIOS Extensions commands.

See ReactOS source repository for boot video functions prototypes.

If we will try to compile our driver, we will get the following linker errors:

x86 build:

1>Source.obj : error LNK2019: 
unresolved external symbol __imp__VidResetDisplay@4 referenced in function _DriverEntry@8
1>Source.obj : error LNK2019: 
unresolved external symbol __imp__VidDisplayString@4 referenced in function _DriverEntry@8

x64 build:

1>Source.obj : error LNK2019: 
unresolved external symbol __imp_VidResetDisplay referenced in function DriverEntry
1>Source.obj : error LNK2019: 
unresolved external symbol __imp_VidDisplayString referenced in function DriverEntry

To import from BOOTVID.DLL, we need to create import library and add it to the linker input for our driver project. To do this, we need dumpbin and lib tools. On my system, they are located in:

x86:

D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin

x64:

D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\amd64

Copy BOOTVID.DLL from C:\Windows\System32 to appropriate directory (depending on whether your Windows is 64 bit or not) and run the following cmd command:

dumpbin /exports BOOTVID.DLL

It will give you exported function names and their ordinals:

Image 1

Create module definition file and copy it to the appropriate directory (depending on whether you compile driver for x86 or x64). Module definition file BOOTVID.def content:

x86:

C++
EXPORTS

VidDisplayString@4 @5

VidResetDisplay@4 @8

x64:

C++
EXPORTS

VidDisplayString

VidResetDisplay

Note that for x86 build, we need to suffix function name and specify ordinal, whereas for x64 build, we can go with pure function name.

Run the following cmd command:

C++
lib /def:BOOTVID.def /out:BOOTVID.lib

Import library will be generated:

Image 2

Now copy import library to your project directory and add it to linker input (Project Properties -> Linker -> Input -> Additional Dependencies).

The reason to directly import from BOOTVID.DLL: Inbv* functions do not work the same way as they work on Windows 7.

After this, the driver will compile fine.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)