Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
68af6c8
cp -r examples/virtio_pci examples/windows
dreamliner787-9 Jul 16, 2026
bc8b61e
examples/windows: renamed and purged stuff from virtio_pci
dreamliner787-9 Jul 16, 2026
2656df3
examples/windows: +QEMU RAM, fix virtio regs moving, +bochs-display
dreamliner787-9 Jul 17, 2026
897df0c
examples/windows: rm serial subsystem + boot UEFI
dreamliner787-9 Jul 17, 2026
1b29e85
x86_64: implement QEMU bochs-display passthrough
dreamliner787-9 Jul 17, 2026
1637976
examples/windows: add bochs-display passthrough
dreamliner787-9 Jul 17, 2026
433122d
guset_ram: bump max to 3 regions
dreamliner787-9 Jul 17, 2026
b039fa3
examples/windows: bump guest RAM to 11G
dreamliner787-9 Jul 17, 2026
e04c560
examples/windows: passthrough kb + mouse
dreamliner787-9 Jul 17, 2026
7f2308c
examples/windows: handle all IRQ passthrough
dreamliner787-9 Jul 18, 2026
a590623
examples/windows: fix BSOD inaccessible boot device
dreamliner787-9 Jul 23, 2026
6ada6ab
examples/windows: purge linux
dreamliner787-9 Jul 23, 2026
1e7fe3c
examples/windows: fix PS/2 KB+M not working on Windows
dreamliner787-9 Jul 24, 2026
b222034
HACK: enable Intel APICv operation
dreamliner787-9 Jul 27, 2026
af2f3be
checkpoint windows 10+11 working on QEMU, just need APICv kernel patch
dreamliner787-9 Jul 28, 2026
96fb2dd
x86_64: implement VM Exit counting
dreamliner787-9 Jul 28, 2026
71c8f71
x86_64/apic: minor typo fix
dreamliner787-9 Jul 30, 2026
6fe2378
examples/windows: metaprogram clean ups
dreamliner787-9 Jul 30, 2026
6c51313
x86_64/msr: handle IA32_TSC_ADJUST read
dreamliner787-9 Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions examples/windows/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#
# Copyright 2024, UNSW
#
# SPDX-License-Identifier: BSD-2-Clause
#

BUILD_DIR ?= build
export MICROKIT_CONFIG ?= debug

ifeq ($(strip $(MICROKIT_SDK)),)
$(error MICROKIT_SDK must be specified)
endif
override MICROKIT_SDK := $(abspath $(MICROKIT_SDK))

# create a virtual disk image for QEMU targets
export BLK_NUM_PART = 1
export BLK_SIZE = 512
# 16MiB of disk space
export BLK_MEM ?= 16777216

# Allow to user to specify a custom partition
PARTITION :=
ifdef PARTITION
export PARTITION_ARG := --partition $(PARTITION)
endif

BUILD_DIR ?= build
export BUILD_DIR := $(abspath ${BUILD_DIR})
export WINDOWS_EXAMPLE := $(abspath .)

export CC_USERLEVEL := clang
export MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit
export SDDF := $(abspath ../../dep/sddf)
export LIBVMM := $(abspath ../../)

IMAGE_FILE := $(BUILD_DIR)/loader.img
REPORT_FILE := $(BUILD_DIR)/report.txt

all: $(IMAGE_FILE)

qemu $(IMAGE_FILE) $(REPORT_FILE) clean clobber: $(BUILD_DIR)/Makefile FORCE
$(MAKE) -C $(BUILD_DIR) MICROKIT_SDK=$(MICROKIT_SDK) $(notdir $@)

$(BUILD_DIR)/Makefile: windows.mk
mkdir -p $(BUILD_DIR)
cp windows.mk $@

FORCE:
101 changes: 101 additions & 0 deletions examples/windows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!--
Copyright 2026, UNSW
SPDX-License-Identifier: CC-BY-SA-4.0
-->

# Windows VM example

@billn fill in

## x86_64 Hardware Requirements

QEMU's Tiny Code Generator (TCG) does not emulate Intel's
virtualisation extension on x86_64. Whereas for ARM it does.
Hence to run this example on QEMU, you will need an x86_64 Intel CPU
with virtualisation (VT-x) turned on in your BIOS, a Linux install
and KVM enabled.

## QEMU x86_64 troubleshooting

We use native sDDF virtIO Block and Network drivers for storage
and networking. However, these drivers temporarily rely on
hardcoded physical addresses for their device registers. Because
sDDF does not yet have merged ACPI and PCI drivers to handle
dynamic mapping, the drivers will crash if your QEMU instance
places the registers at unexpected addresses.

(Note: This limitation will be resolved once the ACPI and PCI
drivers are merged into sDDF.)

If you encounter this crash, you must manually retrieve the correct
physical addresses from QEMU and update your configuration.

1. Access the QEMU Monitor
While your QEMU instance is running, press Ctrl + a, release
both keys, and then press c. You will be dropped into the QEMU
monitor prompt:

```
QEMU 11.0.2 monitor - type 'help' for more information
(qemu)
```

2. Retrieve PCI Information
At the prompt, type info pci to list the connected devices
and their memory allocations. You will see an output similar
to this:

```
(qemu) info pci
Bus 0, device 0, function 0:
Host bridge: PCI device 8086:1237
PCI subsystem 1af4:1100
id ""
Bus 0, device 1, function 0:
ISA bridge: PCI device 8086:7000
PCI subsystem 1af4:1100
id ""
Bus 0, device 1, function 1:
IDE controller: PCI device 8086:7010
PCI subsystem 1af4:1100
BAR4: I/O at 0xc0a0 [0xc0af]
id ""
Bus 0, device 1, function 3:
Bridge: PCI device 8086:7113
PCI subsystem 1af4:1100
IRQ 9, pin A
id ""
Bus 0, device 2, function 0:
Ethernet controller: PCI device 1af4:1000
PCI subsystem 1af4:0001
IRQ 10, pin A
BAR0: I/O at 0xc080 [0xc09f]
BAR1: 32 bit memory at 0xfebc8000 [0xfebc8fff]
BAR4: 64 bit prefetchable memory at 0xe0000000000 [0xe0000003fff]
BAR6: 32 bit memory (not mapped)
id ""
Bus 0, device 3, function 0:
SCSI controller: PCI device 1af4:1001
PCI subsystem 1af4:0002
IRQ 11, pin A
BAR0: I/O at 0xc000 [0xc07f]
BAR1: 32 bit memory at 0xfebc9000 [0xfebc9fff]
BAR4: 64 bit prefetchable memory at 0xe0000004000 [0xe0000007fff]
id ""
Bus 0, device 4, function 0:
Display controller: PCI device 1234:1111
PCI subsystem 1af4:1100
BAR0: 32 bit prefetchable memory at 0xfd000000 [0xfdffffff]
BAR2: 32 bit memory at 0xfebca000 [0xfebcafff]
BAR6: 32 bit memory (not mapped)
id ""
(qemu)
```

3. Update meta.py
Locate the Ethernet controller and SCSI controller sections in the
output. For both devices, find the line that reads BAR4: 64 bit
prefetchable memory at....

Take these memory addresses, replace the existing values at the
top of meta.py, and everything will function normally.
192 changes: 192 additions & 0 deletions examples/windows/client_vm/x86_64/guest_arch_init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright 2026, UNSW
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <stddef.h>
#include <stdint.h>
#include <microkit.h>
#include <libvmm/libvmm.h>
#include <sddf/serial/queue.h>
#include <sddf/serial/config.h>
#include <sddf/blk/queue.h>
#include <sddf/blk/config.h>
#include <sddf/network/queue.h>
#include <sddf/network/config.h>
#include <sddf/timer/config.h>

#include <guest_arch_init.h>

/* Device slot of the virtio console device on bus 0.
* Host bridge = 0, ISA bridge = 1 so we must avoid these.
* Then on Intel, the integrated graphics is conventionally on slot 2 as well...
* Make sure these two matches what is written in DSDT
*/
#define DISPLAY_PCI_DEVICE_SLOT 3

#define VIRTIO_NET_PCI_DEVICE_SLOT 4
#define VIRTIO_NET_PCI_IOAPIC_PIN 16

#define VIRTIO_BLK_PCI_DEVICE_SLOT 5
#define VIRTIO_BLK_PCI_IOAPIC_PIN 17

/* Data from sdfgen */
__attribute__((__section__(".timer_client_config"))) timer_client_config_t timer_config;
extern blk_client_config_t blk_config;
extern net_client_config_t net_config;
extern vmm_config_t vmm_config;

/* Data for the guest's ACPI Differentiated System Description Table (DSDT). */
extern char _guest_dsdt_aml[];
extern char _guest_dsdt_aml_end[];
/* Data for the guest's UEFI firmware. */
extern char _guest_firmware[];
extern char _guest_firmware_end[];

/* Bookkeeping structures for virtio devices */
extern struct virtio_blk_device virtio_blk;
extern struct virtio_net_device virtio_net;

/* sDDF data */
extern blk_queue_handle_t blk_queue;

extern net_queue_handle_t net_rx_queue;
extern net_queue_handle_t net_tx_queue;

#define GUEST_CMDLINE "earlyprintk=serial,0x3f8,115200 debug console=ttyS0,115200 earlycon=serial,0x3f8,115200 loglevel=8"
#define GUEST_LOW_RAM_SIZE 0xD0000000
#define GUEST_LOW_RAM_HVA 0x20000000
#define GUEST_HIGH_RAM_SIZE 0x200000000
#define GUEST_HIGH_RAM_HVA 0x100000000
#define GUEST_HIGH_RAM_GPA 0x100000000
#define GUEST_FLASH_GPA 0xFFA00000
#define GUEST_FLASH_SIZE 0x600000
#define GUEST_FLASH_HVA 0x10000000

#define COM1_IOAPIC_CHIP 0
#define COM1_IOAPIC_PIN 4
#define COM1_IO_PORT_ID 50
#define COM1_IO_PORT_ADDR 0x3F8
#define COM1_IO_PORT_SIZE 8

#define PS2_DATA_IO_PORT_ID 40
#define PS2_DATA_IO_PORT_ADDR 0x60
#define PS2_DATA_IO_PORT_SIZE 1
#define PS2_STS_IO_PORT_ID 41
#define PS2_STS_IO_PORT_ADDR 0x64
#define PS2_STS_IO_PORT_SIZE 1
#define PS2_FIRST_IRQ_IOAPIC_CHIP 0
#define PS2_FIRST_IRQ_IOAPIC_PIN 1
#define PS2_FIRST_IRQ_ID 42
#define PS2_SECOND_IRQ_IOAPIC_CHIP 0
#define PS2_SECOND_IRQ_IOAPIC_PIN 12
#define PS2_SECOND_IRQ_ID 43

/* Matches DSDT */
#define PCI_MMIO_APERATURE_GPA 0xE0000000
#define PCI_MMIO_APERATURE_SIZE 0x10000000

bool guest_arch_init(void)
{
assert(timer_config_check_magic(&timer_config));

arch_guest_init_t args = {
.bsp = true,
.timer_ch = timer_config.driver_id,
.apicv_hva = 0x3000000000,
.num_guest_ram_regions = 3,
.guest_ram_regions = {
(struct guest_ram_region) {
.gpa_start = LOW_RAM_START_GPA, .size = GUEST_LOW_RAM_SIZE, .vmm_vaddr = (void *)GUEST_LOW_RAM_HVA
},
(struct guest_ram_region) {
.gpa_start = GUEST_HIGH_RAM_GPA, .size = GUEST_HIGH_RAM_SIZE, .vmm_vaddr = (void *)GUEST_HIGH_RAM_HVA
},
(struct guest_ram_region) {
.gpa_start = GUEST_FLASH_GPA, .size = GUEST_FLASH_SIZE, .vmm_vaddr = (void *)GUEST_FLASH_HVA
},
},
.pci_init = (struct guest_pci_init) {
.ecam_gpa = 0,
.ecam_size = 0,
.mmio_aperature_gpa = PCI_MMIO_APERATURE_GPA,
.mmio_aperature_size = PCI_MMIO_APERATURE_SIZE,
},
};

if (!guest_init(args)) {
LOG_VMM_ERR("Failed to initialise VMM\n");
return false;
}

/* Place all the binaries in the right locations before starting the guest */
size_t dsdt_aml_size = _guest_dsdt_aml_end - _guest_dsdt_aml;
size_t firmware_size = _guest_firmware_end - _guest_firmware;

if (!dsdt_aml_size) {
LOG_VMM_ERR("DSDT AML image is empty\n");
return false;
}
if (!firmware_size) {
LOG_VMM_ERR("Firmware image is empty\n");
return false;
}

if (!uefi_setup_images((uintptr_t)_guest_firmware, firmware_size, (uintptr_t)_guest_dsdt_aml, dsdt_aml_size,
GUEST_FLASH_GPA, GUEST_FLASH_SIZE)) {
LOG_VMM_ERR("Failed to initialise UEFI firmware\n");
return false;
}


/* Pass through COM1 serial port */
microkit_vcpu_x86_enable_ioport(GUEST_BOOT_VCPU_ID, COM1_IO_PORT_ID, COM1_IO_PORT_ADDR, COM1_IO_PORT_SIZE);

/* Pass through serial IRQs */
assert(virq_register_passthrough(X86_IOAPIC_IRQ_ROUTE(COM1_IOAPIC_CHIP, COM1_IOAPIC_PIN), SERIAL_IRQ_CH));
microkit_irq_ack(SERIAL_IRQ_CH);

/* Pass through QEMU Bochs display */
assert(register_qemu_bochs_display_on_pci_bus(0, DISPLAY_PCI_DEVICE_SLOT, 0));

/* Pass through PS2 keyboard and mouse */
microkit_vcpu_x86_enable_ioport(GUEST_BOOT_VCPU_ID, PS2_DATA_IO_PORT_ID, PS2_DATA_IO_PORT_ADDR,
PS2_DATA_IO_PORT_SIZE);
microkit_vcpu_x86_enable_ioport(GUEST_BOOT_VCPU_ID, PS2_STS_IO_PORT_ID, PS2_STS_IO_PORT_ADDR, PS2_STS_IO_PORT_SIZE);
assert(virq_register_passthrough(X86_IOAPIC_IRQ_ROUTE(PS2_FIRST_IRQ_IOAPIC_CHIP, PS2_FIRST_IRQ_IOAPIC_PIN),
PS2_FIRST_IRQ_ID));
microkit_irq_ack(PS2_FIRST_IRQ_ID);
assert(virq_register_passthrough(X86_IOAPIC_IRQ_ROUTE(PS2_SECOND_IRQ_IOAPIC_CHIP, PS2_SECOND_IRQ_IOAPIC_PIN),
PS2_SECOND_IRQ_ID));
microkit_irq_ack(PS2_SECOND_IRQ_ID);

return true;
}

bool virtio_arch_init(void)
{
blk_storage_info_t *storage_info = blk_config.virt.storage_info.vaddr;
if (!virtio_pci_blk_init(&virtio_blk, 0, VIRTIO_BLK_PCI_DEVICE_SLOT,
X86_IOAPIC_IRQ_ROUTE(0, VIRTIO_BLK_PCI_IOAPIC_PIN), (uintptr_t)blk_config.data.vaddr,
blk_config.data.size, storage_info, &blk_queue, blk_config.virt.num_buffers,
blk_config.virt.id)) {
LOG_VMM_ERR("Failed to initialise virtIO PCI Block device\n");
return false;
}

if (!virtio_pci_net_init(&virtio_net, 0, VIRTIO_NET_PCI_DEVICE_SLOT,
X86_IOAPIC_IRQ_ROUTE(0, VIRTIO_NET_PCI_IOAPIC_PIN), &net_rx_queue, &net_tx_queue,
(uintptr_t)net_config.rx_data.vaddr, (uintptr_t)net_config.tx_data.vaddr, net_config.rx.id,
net_config.tx.id, net_config.mac_addr.addr)) {
LOG_VMM_ERR("Failed to initialise virtIO PCI Network device\n");
return false;
}

return true;
}

bool guest_arch_start(void)
{
return guest_start_reset_state();
}
Loading
Loading