Skip to content

Commit

Permalink
Support UEFI and macOSX (#31)
Browse files Browse the repository at this point in the history
* fix vga shared buffer

* restrict isr bios image size

* fix ahci atapi without dma

* implement virtio legacy interface

* custom cpuid vendor

* add usb keyboard

* add vga mmio bar

* fix usb keyboard arrow keys

* return zero if out of pci config space

* add apple smc device

* vga mmio range

* add memory description to smbios

* q35 ddr3

* prevent lots of duplicated pointerevents

* cpuid model

* add required smbios types

* move files

* set isa bios size 256k to prevent reboot failue

* add reserved area to fix qxl

* support macosx

* i440fx is not working now, fix it later

* use std::string as a output buffer

* disable mcfg table if not Q35

* fix i440fx

* add fwcfg signature dma read

* improve panic and mutex

* better osk buffer

* fix usb copy string error

* add kvm_irqchip device

* create thread to reset machine

* add mtrr msr indices

* fix uefi reboot

* flexible acpi builder

* fix rsdt size

* memory region supports overlapping

* use a lock guard object

* fix load snapshot

* add SoftReset to PCI devices

* no pci controller default command

* vcpu PreRun and PostRun code

* add OVMF and macOS Sonoma support

* notify memory change to vfio

* add pvpanic acpi ssdt

* remove useless code

* add memory region class

* vga dirty memory

* add mutex lock in dirty bitmap code
  • Loading branch information
78 authored Jul 5, 2024
1 parent e2d4354 commit 939afd9
Show file tree
Hide file tree
Showing 112 changed files with 4,389 additions and 3,778 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ config/default.yaml
.clang-format
.vscode/
.DS_Store
config/
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@
"config.h": "c",
"__hash_table": "cpp",
"__tree": "cpp",
"ios": "cpp"
"ios": "cpp",
"__locale": "cpp",
"__string": "cpp",
"*.hex": "cpp",
"__mutex_base": "cpp"
},
"C_Cpp.default.cppStandard": "c++17",
"C_Cpp.default.includePath": [
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ What's supported now:
### Basic functions

1. 440FX ✅ / Q35 Chipset ✅
2. SeaBIOS ✅
2. SeaBIOS ✅ OVMF ✅
3. Memory Region Management ✅
4. IOPort Management ✅
5. Devices Management ✅
Expand All @@ -47,7 +47,7 @@ What's supported now:
14. VGA / VBE ✅
15. Option Roms ✅ / SMBIOS ✅ / ACPI Table ✅
16. Boot DOS ✅
17. Boot OS (Win98 to Win11 / DOS / Ubuntu) ✅
17. Boot OS (Win98 to Win11 / DOS / Ubuntu / macOS Sonoma) ✅
18. QCOW2 ✅

### Multimedia & Networking
Expand All @@ -61,7 +61,7 @@ What's supported now:
7. User network ✅
8. VFIO (mdev & passthrough) ✅
9. Samba
10. USB 1.0 UHCI ✅ / USB 3.0 XHCI ✅ / USB Tablet ✅ / USB Midi ✅ / USB Wacom ✅
10. USB 1.0 UHCI ✅ / USB 3.0 XHCI ✅ / USB Tablet ✅ / USB Keyboard ✅ / USB Midi ✅ / USB Wacom ✅

### Hyper-V & Migration

Expand All @@ -76,7 +76,7 @@ What's supported now:
### For RockyLinux 9.3,

```bash
dnf install epel-release gdb cmake gcc-c++
dnf install epel-release gdb cmake gcc-c++ acpica-tools
dnf --enablerepo=devel install -y protobuf-compiler protobuf-devel glib2-devel yaml-cpp-devel pixman-devel libzstd-devel zlib-devel

# If SDL enabled
Expand All @@ -86,7 +86,7 @@ dnf --enablerepo=devel install -y SDL2-devel alsa-lib-devel
### For Debian 12,

```bash
apt install meson gdb cmake build-essential g++
apt install meson gdb cmake build-essential g++ acpica-tools
apt install protobuf-compiler libprotobuf-dev libglib2.0-dev libyaml-cpp-dev libpixman-1-dev libzstd-dev zlib1g-dev

# If SDL enabled
Expand Down
10 changes: 5 additions & 5 deletions config/i440fx.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ machine:
objects:
# Use the old name pci-host instead of i440fx-host for compatibility
- class: i440fx-host
- class: kvm-irqchip
- class: kvm-clock
- class: firmware-config
- class: debug-console
- class: dummy-device

# PCI devices connected to PCI Host.
# The PIIX3 must be put first because it must use PCI slot 1
Expand All @@ -19,11 +24,6 @@ objects:

# I/O devices connected to PIIX3
- class: cmos
- class: debug-console
- class: dummy-device
- class: firmware-config
- class: kvm-clock
- class: ps2
- class: i8257-dma
- class: i82078-fdc

9 changes: 5 additions & 4 deletions config/q35.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ machine:
objects:
# Use the old name pci-host instead of q35-host for compatibility
- class: q35-host
- class: kvm-irqchip
- class: kvm-clock
- class: firmware-config
- class: debug-console
- class: dummy-device

# PCI devices connected to PCI Host
- class: ich9-hda
Expand All @@ -16,10 +21,6 @@ objects:

# I/O devices connected to ICH9 LPC
- class: cmos
- class: debug-console
- class: dummy-device
- class: firmware-config
- class: kvm-clock
- class: ps2

# codec device connected to ICH9 HDA
Expand Down
22 changes: 20 additions & 2 deletions core/configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ Configuration::Configuration(Machine* machine) : machine_(machine) {
void Configuration::InitializePaths() {
char temp[1024];
bzero(temp, sizeof(temp));
getcwd(temp, sizeof(temp) - 1);
directories_.insert(temp);
if (getcwd(temp, sizeof(temp) - 1) != nullptr) {
directories_.insert(temp);
}

bzero(temp, sizeof(temp));
if (readlink("/proc/self/exe", temp, sizeof(temp) - 1) > 0) {
Expand Down Expand Up @@ -220,6 +221,23 @@ void Configuration::LoadMachine(const YAML::Node& node) {
}
if (node["vcpu"]) {
machine_->num_vcpus_ = node["vcpu"].as<uint64_t>();
if (machine_->num_vcpus_ == 1) {
machine_->num_cores_ = 1;
machine_->num_threads_ = 1;
} else {
MV_ASSERT(machine_->num_vcpus_ % 2 == 0);
machine_->num_threads_ = 2;
machine_->num_cores_ = machine_->num_vcpus_ / machine_->num_threads_;
}
}
if (node["cpuid"]) {
auto &cpuid = node["cpuid"];
if (cpuid["vendor"]) {
machine_->vcpu_vendor_ = cpuid["vendor"].as<std::string>();
}
if (cpuid["model"]) {
machine_->vcpu_model_ = cpuid["model"].as<std::string>();
}
}
if (node["priority"]) {
machine_->vcpu_priority_ = node["priority"].as<uint64_t>();
Expand Down
55 changes: 27 additions & 28 deletions core/device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ void Device::Disconnect() {
manager_->UnregisterDevice(this);
}

void Device::AddIoResource(IoResourceType type, uint64_t base, uint64_t length, const char* name) {
AddIoResource(type, base, length, name, nullptr, kIoResourceFlagNone);
IoResource* Device::AddIoResource(IoResourceType type, uint64_t base, uint64_t length, const char* name) {
return AddIoResource(type, base, length, name, nullptr, kIoResourceFlagNone);
}

void Device::AddIoResource(IoResourceType type, uint64_t base, uint64_t length, const char* name, void* host_memory, IoResourceFlag flags) {
IoResource* Device::AddIoResource(IoResourceType type, uint64_t base, uint64_t length, const char* name, void* host_memory, IoResourceFlag flags) {
MV_ASSERT(length > 0);
auto resource = new IoResource {
.type = type,
Expand All @@ -100,42 +100,41 @@ void Device::AddIoResource(IoResourceType type, uint64_t base, uint64_t length,
.mapped_region = nullptr,
.flags = flags
};
io_resources_.push_back(resource);
io_resources_.insert(resource);
if (connected_) {
SetIoResourceEnabled(resource, true);
}
return resource;
}

void Device::RemoveIoResource(IoResource* resource) {
if (connected_) {
SetIoResourceEnabled(resource, false);
}
io_resources_.erase(resource);
delete resource;
}

void Device::RemoveIoResource(IoResourceType type, const char* name) {
for (auto it = io_resources_.begin(); it != io_resources_.end(); it++) {
auto resource = *it;
if (resource->type == type &&
(resource->name == name || (name && resource->name && strcmp(resource->name, name) == 0))
) {
if (connected_) {
SetIoResourceEnabled(resource, false);
}
io_resources_.erase(it);
delete resource;
return;
}
auto it = std::find_if(io_resources_.begin(), io_resources_.end(), [type, name](IoResource* resource) {
return resource->type == type && (resource->name == name || (name && resource->name && strcmp(resource->name, name) == 0));
});
if (it == io_resources_.end()) {
MV_PANIC("%s not found type=%d name=%s", name_, type, name);
return;
}
RemoveIoResource(*it);
}

void Device::RemoveIoResource(IoResourceType type, uint64_t base) {
for (auto it = io_resources_.begin(); it != io_resources_.end(); it++) {
auto resource = *it;
if (resource->type == type && resource->base == base) {
if (connected_) {
SetIoResourceEnabled(resource, false);
}
MV_ASSERT(!resource->enabled);
io_resources_.erase(it);
delete resource;
return;
}
auto it = std::find_if(io_resources_.begin(), io_resources_.end(), [type, base](IoResource* resource) {
return resource->type == type && resource->base == base;
});
if (it == io_resources_.end()) {
MV_PANIC("%s not found type=%d base=0x%lx", name_, type, base);
return;
}
MV_PANIC("%s not found type=%d base=0x%lx", name_, type, base);
RemoveIoResource(*it);
}

void Device::SetIoResourceEnabled(IoResource* resource, bool enabled) {
Expand Down
61 changes: 13 additions & 48 deletions core/device_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ DeviceManager::DeviceManager(Machine* machine, Device* root) :
SetupIrqChip();

/* Initialize GSI routing table */
SetupGsiRoutingTable();
ResetGsiRoutingTable();

/* Call Connect() on all devices and do the initialization
* 1. reset device status
Expand All @@ -76,6 +76,8 @@ DeviceManager::~DeviceManager() {

/* Called when system start or reset */
void DeviceManager::ResetDevices() {
ResetGsiRoutingTable();

for (auto device : registered_devices_) {
device->Reset();
}
Expand Down Expand Up @@ -196,7 +198,7 @@ void DeviceManager::RegisterIoHandler(Device* device, const IoResource* resource
});
} else if (resource->type == kIoResourceTypeMmio) {
// Map the memory to type Device. Accessing these regions will cause MMIO access fault
const MemoryRegion* region = machine_->memory_manager()->Map(resource->base, resource->length,
auto region = machine_->memory_manager()->Map(resource->base, resource->length,
nullptr, kMemoryTypeDevice, resource->name);

mmio_handlers_.push_back(new IoHandler {
Expand Down Expand Up @@ -277,7 +279,7 @@ IoEvent* DeviceManager::RegisterIoEvent(Device* device, IoResourceType type, uin
};
int ret = ioctl(machine_->vm_fd_, KVM_IOEVENTFD, &kvm_ioevent);
if (ret < 0) {
MV_PANIC("failed to register io event, ret=%d", ret);
MV_PANIC("%s failed to register io event, ret=%d", device->name(), ret);
}
if (machine_->debug_) {
MV_LOG("%s register IO event address=0x%lx length=%lu fd=%d", device->name(), address, length, event->fd);
Expand Down Expand Up @@ -494,9 +496,12 @@ void DeviceManager::HandleMmio(uint64_t addr, uint8_t* data, uint16_t size, int
}
}

// Read from unhandled MMIO always returns 0
if (!is_write) {
bzero(data, size);
}
if (machine_->debug()) {
machine_->memory_manager()->PrintMemoryScope();
MV_PANIC("unhandled mmio %s base: 0x%016lx size: %x data: %016lx",
MV_ERROR("unhandled mmio %s base: 0x%016lx size: %x data: %016lx",
is_write ? "write" : "read", addr, size, *(uint64_t*)data);
}
}
Expand Down Expand Up @@ -623,7 +628,8 @@ void DeviceManager::SetupIrqChip() {
}

/* Although KVM has initialized GSI routing table, we still need to do it again */
void DeviceManager::SetupGsiRoutingTable() {
void DeviceManager::ResetGsiRoutingTable() {
gsi_routing_table_.clear();
auto add_irq_routing = [this](uint gsi, uint chip, uint pin) {
kvm_irq_routing_entry entry = {
.gsi = gsi,
Expand Down Expand Up @@ -728,27 +734,7 @@ void DeviceManager::RemoveMsiNotifier(int gsi, int trigger_fd) {
UpdateGsiRoutingTable();
}


bool DeviceManager::SaveState(MigrationWriter* writer) {
writer->SetPrefix("kvm-irqchip");
/* Save irq chip */
kvm_irqchip chip;
chip.chip_id = KVM_IRQCHIP_PIC_MASTER;
MV_ASSERT(ioctl(machine_->vm_fd_, KVM_GET_IRQCHIP, &chip) == 0);
writer->WriteRaw("PIC_MASTER", &chip.chip.pic, sizeof(chip.chip.pic));

chip.chip_id = KVM_IRQCHIP_PIC_SLAVE;
MV_ASSERT(ioctl(machine_->vm_fd_, KVM_GET_IRQCHIP, &chip) == 0);
writer->WriteRaw("PIC_SLAVE", &chip.chip.pic, sizeof(chip.chip.pic));

chip.chip_id = KVM_IRQCHIP_IOAPIC;
MV_ASSERT(ioctl(machine_->vm_fd_, KVM_GET_IRQCHIP, &chip) == 0);
writer->WriteRaw("IOAPIC", &chip.chip.ioapic, sizeof(chip.chip.ioapic));

kvm_pit_state2 pit2;
MV_ASSERT(ioctl(machine_->vm_fd_, KVM_GET_PIT2, &pit2) == 0);
writer->WriteRaw("PIT2", &pit2, sizeof(pit2));

/* Save states of devices */
for (auto device : registered_devices_) {
writer->SetPrefix(device->name());
Expand All @@ -761,28 +747,7 @@ bool DeviceManager::SaveState(MigrationWriter* writer) {
}

bool DeviceManager::LoadState(MigrationReader* reader) {
reader->SetPrefix("kvm-irqchip");
/* Load irq chip */
kvm_irqchip chip;
chip.chip_id = KVM_IRQCHIP_PIC_MASTER;
if (!reader->ReadRaw("PIC_MASTER", &chip.chip.pic, sizeof(chip.chip.pic)))
return false;
MV_ASSERT(ioctl(machine_->vm_fd_, KVM_SET_IRQCHIP, &chip) == 0);

chip.chip_id = KVM_IRQCHIP_PIC_SLAVE;
if (!reader->ReadRaw("PIC_SLAVE", &chip.chip.pic, sizeof(chip.chip.pic)))
return false;
MV_ASSERT(ioctl(machine_->vm_fd_, KVM_SET_IRQCHIP, &chip) == 0);

chip.chip_id = KVM_IRQCHIP_IOAPIC;
if (!reader->ReadRaw("IOAPIC", &chip.chip.ioapic, sizeof(chip.chip.ioapic)))
return false;
MV_ASSERT(ioctl(machine_->vm_fd_, KVM_SET_IRQCHIP, &chip) == 0);

kvm_pit_state2 pit2;
if (!reader->ReadRaw("PIT2", &pit2, sizeof(pit2)))
return false;
MV_ASSERT(ioctl(machine_->vm_fd_, KVM_SET_PIT2, &pit2) == 0);
ResetGsiRoutingTable();

/* Load device states */
for (auto device : registered_devices_) {
Expand Down
Loading

0 comments on commit 939afd9

Please sign in to comment.