Last active
December 15, 2021 04:15
-
-
Save Risca/5c52f5cd17c4e6e91785d7ba9ef8adc5 to your computer and use it in GitHub Desktop.
This adds support to qemu for bifferboard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: host-qemu-5.2.0/default-configs/devices/i386-softmmu.mak | |
=================================================================== | |
--- host-qemu-5.2.0.orig/default-configs/devices/i386-softmmu.mak | |
+++ host-qemu-5.2.0/default-configs/devices/i386-softmmu.mak | |
@@ -27,5 +27,6 @@ | |
# | |
CONFIG_ISAPC=y | |
CONFIG_I440FX=y | |
+CONFIG_RDC3210=y | |
CONFIG_Q35=y | |
CONFIG_MICROVM=y | |
Index: host-qemu-5.2.0/hw/block/pflash_cfi02.c | |
=================================================================== | |
--- host-qemu-5.2.0.orig/hw/block/pflash_cfi02.c | |
+++ host-qemu-5.2.0/hw/block/pflash_cfi02.c | |
@@ -97,7 +97,7 @@ struct PFlashCFI02 { | |
uint16_t ident3; | |
uint16_t unlock_addr0; | |
uint16_t unlock_addr1; | |
- uint8_t cfi_table[0x4d]; | |
+ uint8_t cfi_table[0x50]; | |
QEMUTimer timer; | |
/* The device replicates the flash memory across its memory space. Emulate | |
* that by having a container (.mem) filled with an array of aliases | |
@@ -315,12 +315,14 @@ static uint64_t pflash_read(void *opaque | |
pflash_register_memory(pfl, 1); | |
} | |
offset &= pfl->chip_len - 1; | |
- boff = offset & 0xFF; | |
- if (pfl->width == 2) { | |
+ boff = offset; | |
+ if (width == 2) { | |
boff = boff >> 1; | |
- } else if (pfl->width == 4) { | |
+ } else if (width == 4) { | |
boff = boff >> 2; | |
} | |
+ /* Only the least-significant 11 bits are used in most cases. */ | |
+ boff &= 0x7FF; | |
switch (pfl->cmd) { | |
default: | |
/* This should never happen : reset state & treat it as a read*/ | |
@@ -350,10 +352,18 @@ static uint64_t pflash_read(void *opaque | |
ret = boff & 0x01 ? pfl->ident1 : pfl->ident0; | |
break; | |
case 0x02: | |
- ret = 0x00; /* Pretend all sectors are unprotected */ | |
+ // TODO: check if offset == sector address instead of this | |
+ if (width == pfl->width) { | |
+ ret = 0x00; /* Pretend all sectors are unprotected */ | |
+ } | |
+ else { | |
+ ret = pfl->ident1 & ((1 << (width * 8)) - 1); | |
+ } | |
break; | |
case 0x0E: | |
case 0x0F: | |
+ case 0x100: | |
+ case 0x200: | |
ret = boff & 0x01 ? pfl->ident3 : pfl->ident2; | |
if (ret != (uint8_t)-1) { | |
break; | |
@@ -831,6 +841,7 @@ static void pflash_cfi02_realize(DeviceS | |
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); | |
timer_init_ns(&pfl->timer, QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); | |
+ pfl->bypass = 0; | |
pfl->wcycle = 0; | |
pfl->cmd = 0; | |
pfl->status = 0; | |
@@ -926,7 +937,9 @@ static void pflash_cfi02_realize(DeviceS | |
pfl->cfi_table[0x0b + pri_ofs] = 0x00; | |
/* Page mode not supported. */ | |
pfl->cfi_table[0x0c + pri_ofs] = 0x00; | |
- assert(0x0c + pri_ofs < ARRAY_SIZE(pfl->cfi_table)); | |
+ /* Top/bottom boot sector */ | |
+ pfl->cfi_table[0x0f + pri_ofs] = 0x02; | |
+ assert(0x0f + pri_ofs < ARRAY_SIZE(pfl->cfi_table)); | |
} | |
static Property pflash_cfi02_properties[] = { | |
Index: host-qemu-5.2.0/hw/i386/Kconfig | |
=================================================================== | |
--- host-qemu-5.2.0.orig/hw/i386/Kconfig | |
+++ host-qemu-5.2.0/hw/i386/Kconfig | |
@@ -119,6 +119,12 @@ config AMD_IOMMU | |
bool | |
select X86_IOMMU | |
+config RDC3210 | |
+ bool | |
+ imply R6040_PCI | |
+ select I440FX | |
+ select PFLASH_CFI02 | |
+ | |
config VMPORT | |
bool | |
Index: host-qemu-5.2.0/hw/i386/meson.build | |
=================================================================== | |
--- host-qemu-5.2.0.orig/hw/i386/meson.build | |
+++ host-qemu-5.2.0/hw/i386/meson.build | |
@@ -13,6 +13,7 @@ i386_ss.add(when: 'CONFIG_AMD_IOMMU', if | |
i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) | |
i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c')) | |
i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) | |
+i386_ss.add(when: 'CONFIG_RDC3210', if_true: files('rdc3210.c')) | |
i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) | |
i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) | |
i386_ss.add(when: 'CONFIG_VTD', if_true: files('intel_iommu.c')) | |
Index: host-qemu-5.2.0/hw/i386/rdc3210.c | |
=================================================================== | |
--- /dev/null | |
+++ host-qemu-5.2.0/hw/i386/rdc3210.c | |
@@ -0,0 +1,411 @@ | |
+/* | |
+ * QEMU RDC3210 PC System Emulator | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a copy | |
+ * of this software and associated documentation files (the "Software"), to deal | |
+ * in the Software without restriction, including without limitation the rights | |
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
+ * copies of the Software, and to permit persons to whom the Software is | |
+ * furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included in | |
+ * all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
+ * THE SOFTWARE. | |
+ */ | |
+ | |
+#include "qemu/osdep.h" | |
+#include CONFIG_DEVICES | |
+ | |
+#include "qemu/units.h" | |
+#include "hw/loader.h" | |
+#include "hw/i386/x86.h" | |
+#include "hw/i386/pc.h" | |
+#include "hw/i386/apic.h" | |
+#include "hw/pci-host/i440fx.h" | |
+#include "hw/southbridge/piix.h" | |
+#include "hw/display/ramfb.h" | |
+#include "hw/firmware/smbios.h" | |
+#include "hw/pci/pci.h" | |
+#include "hw/pci/pci_ids.h" | |
+#include "hw/usb.h" | |
+#include "hw/ide/pci.h" | |
+#include "hw/irq.h" | |
+#include "hw/qdev-properties.h" | |
+#include "net/net.h" | |
+#include "sysemu/kvm.h" | |
+#include "sysemu/block-backend.h" | |
+#include "hw/kvm/clock.h" | |
+#include "sysemu/sysemu.h" | |
+#include "hw/sysbus.h" | |
+#include "sysemu/arch_init.h" | |
+#include "hw/i2c/smbus_eeprom.h" | |
+#include "hw/xen/xen-x86.h" | |
+#include "exec/memory.h" | |
+#include "exec/address-spaces.h" | |
+#include "e820_memory_layout.h" | |
+#include "hw/acpi/acpi.h" | |
+#include "hw/acpi/pc-hotplug.h" | |
+#include "cpu.h" | |
+#include "qapi/error.h" | |
+#include "qemu/error-report.h" | |
+#include "sysemu/xen.h" | |
+#include "migration/global_state.h" | |
+#include "migration/misc.h" | |
+#include "sysemu/numa.h" | |
+#include "hw/hyperv/vmbus-bridge.h" | |
+#include "hw/mem/nvdimm.h" | |
+#include "hw/i386/acpi-build.h" | |
+ | |
+#define MAX_IDE_BUS 2 | |
+ | |
+#define FLASH_EN29LV640_BASE (0xff800000) | |
+#define FLASH_EN29LV640_SIZE (0x800000) | |
+static void bifferboard_flash_init(MemoryRegion *rom_memory) | |
+{ | |
+ DriveInfo *dinfo; | |
+ BlockBackend *blk; | |
+ DeviceState *dev; | |
+ | |
+ dev = qdev_new(TYPE_PFLASH_CFI02); | |
+ | |
+ dinfo = drive_get(IF_PFLASH, 0, 0); | |
+ blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; | |
+ if (blk) { | |
+ char devpath[100]; | |
+ hwaddr addr = (hwaddr)(FLASH_EN29LV640_BASE + FLASH_EN29LV640_SIZE - 64*1024); | |
+ | |
+ snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr); | |
+ add_boot_device_path(-1, NULL, devpath); | |
+ | |
+ /* Set blk as backing file */ | |
+ qdev_prop_set_drive(dev, "drive", blk); | |
+ } | |
+ else { | |
+ warn_report("No firmware backing file set! Set one with '%s'", | |
+ "-drive file=firmware.img,if=pflash,format=raw"); | |
+ /* Try to load biffboot from "-bios" command line */ | |
+ x86_bios_rom_init(rom_memory, false); | |
+ } | |
+ | |
+ // Last 64K is organized as 8 * 8K | |
+ qdev_prop_set_uint32(dev, "num-blocks0", (FLASH_EN29LV640_SIZE - 0x10000) / 0x10000); | |
+ qdev_prop_set_uint32(dev, "sector-length0", 0x10000); | |
+ qdev_prop_set_uint32(dev, "num-blocks1", 8); | |
+ qdev_prop_set_uint32(dev, "sector-length1", 0x2000); | |
+ qdev_prop_set_uint8(dev, "width", 2); // 16 bit | |
+ qdev_prop_set_uint8(dev, "mappings", 1); | |
+ qdev_prop_set_uint8(dev, "big-endian", 0); | |
+ qdev_prop_set_uint16(dev, "id0", 0x007F); | |
+ qdev_prop_set_uint16(dev, "id1", 0x22CB); | |
+ qdev_prop_set_uint16(dev, "id2", 0x001C); | |
+ qdev_prop_set_uint16(dev, "id3", 0x0000); | |
+ qdev_prop_set_uint16(dev, "unlock-addr0", 0x0555); | |
+ qdev_prop_set_uint16(dev, "unlock-addr1", 0x02aa); | |
+ qdev_prop_set_string(dev, "name", "en29lv640.0"); | |
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); | |
+ | |
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, FLASH_EN29LV640_BASE); | |
+} | |
+ | |
+static void rdc_memory_init(PCMachineState *pcms, | |
+ MemoryRegion *system_memory, | |
+ MemoryRegion *rom_memory, | |
+ MemoryRegion **ram_memory) | |
+{ | |
+ MemoryRegion *ram_below_4g, *ram_above_4g; | |
+ MachineState *machine = MACHINE(pcms); | |
+ MachineClass *mc = MACHINE_GET_CLASS(machine); | |
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); | |
+ X86MachineState *x86ms = X86_MACHINE(pcms); | |
+ | |
+ assert(machine->ram_size == x86ms->below_4g_mem_size + | |
+ x86ms->above_4g_mem_size); | |
+ | |
+ /* | |
+ * Split single memory region and use aliases to address portions of it, | |
+ * done for backwards compatibility with older qemus. | |
+ */ | |
+ *ram_memory = machine->ram; | |
+ ram_below_4g = g_malloc(sizeof(*ram_below_4g)); | |
+ memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", machine->ram, | |
+ 0, x86ms->below_4g_mem_size); | |
+ memory_region_add_subregion(system_memory, 0, ram_below_4g); | |
+ e820_add_entry(0, x86ms->below_4g_mem_size, E820_RAM); | |
+ if (x86ms->above_4g_mem_size > 0) { | |
+ ram_above_4g = g_malloc(sizeof(*ram_above_4g)); | |
+ memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", | |
+ machine->ram, | |
+ x86ms->below_4g_mem_size, | |
+ x86ms->above_4g_mem_size); | |
+ memory_region_add_subregion(system_memory, 0x100000000ULL, | |
+ ram_above_4g); | |
+ e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM); | |
+ } | |
+ | |
+ if (!pcmc->has_reserved_memory && | |
+ (machine->ram_slots || | |
+ (machine->maxram_size > machine->ram_size))) { | |
+ | |
+ error_report("\"-memory 'slots|maxmem'\" is not supported by: %s", | |
+ mc->name); | |
+ exit(EXIT_FAILURE); | |
+ } | |
+ | |
+ /* always allocate the device memory information */ | |
+ machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); | |
+ | |
+ /* initialize device memory address space */ | |
+ if (pcmc->has_reserved_memory && | |
+ (machine->ram_size < machine->maxram_size)) { | |
+ ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; | |
+ | |
+ if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { | |
+ error_report("unsupported amount of memory slots: %"PRIu64, | |
+ machine->ram_slots); | |
+ exit(EXIT_FAILURE); | |
+ } | |
+ | |
+ if (QEMU_ALIGN_UP(machine->maxram_size, | |
+ TARGET_PAGE_SIZE) != machine->maxram_size) { | |
+ error_report("maximum memory size must by aligned to multiple of " | |
+ "%d bytes", TARGET_PAGE_SIZE); | |
+ exit(EXIT_FAILURE); | |
+ } | |
+ | |
+ machine->device_memory->base = | |
+ ROUND_UP(0x100000000ULL + x86ms->above_4g_mem_size, 1 * GiB); | |
+ | |
+ if (pcmc->enforce_aligned_dimm) { | |
+ /* size device region assuming 1G page max alignment per slot */ | |
+ device_mem_size += (1 * GiB) * machine->ram_slots; | |
+ } | |
+ | |
+ if ((machine->device_memory->base + device_mem_size) < | |
+ device_mem_size) { | |
+ error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, | |
+ machine->maxram_size); | |
+ exit(EXIT_FAILURE); | |
+ } | |
+ | |
+ memory_region_init(&machine->device_memory->mr, OBJECT(pcms), | |
+ "device-memory", device_mem_size); | |
+ memory_region_add_subregion(system_memory, machine->device_memory->base, | |
+ &machine->device_memory->mr); | |
+ } | |
+ | |
+ bifferboard_flash_init(rom_memory); | |
+ pc_system_flash_cleanup_unused(pcms); | |
+ | |
+ /* Init default IOAPIC address space */ | |
+ x86ms->ioapic_as = &address_space_memory; | |
+ | |
+ /* Init ACPI memory hotplug IO base address */ | |
+ pcms->memhp_io_base = ACPI_MEMORY_HOTPLUG_BASE; | |
+} | |
+ | |
+/* PC hardware initialisation */ | |
+static void pc_init1(MachineState *machine, | |
+ const char *host_type, const char *pci_type) | |
+{ | |
+ PCMachineState *pcms = PC_MACHINE(machine); | |
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); | |
+ X86MachineState *x86ms = X86_MACHINE(machine); | |
+ MemoryRegion *system_memory = get_system_memory(); | |
+ MemoryRegion *system_io = get_system_io(); | |
+ PCIBus *pci_bus; | |
+ PIIX3State *piix3; | |
+ ISABus *isa_bus; | |
+ PCII440FXState *i440fx_state; | |
+ int piix3_devfn = -1; | |
+ GSIState *gsi_state; | |
+ ISADevice *rtc_state; | |
+ MemoryRegion *ram_memory; | |
+ MemoryRegion *pci_memory; | |
+ MemoryRegion *rom_memory; | |
+ ram_addr_t lowmem; | |
+ | |
+ /* | |
+ * Calculate ram split, for memory below and above 4G. It's a bit | |
+ * complicated for backward compatibility reasons ... | |
+ * | |
+ * - Traditional split is 3.5G (lowmem = 0xe0000000). This is the | |
+ * default value for max_ram_below_4g now. | |
+ * | |
+ * - Then, to gigabyte align the memory, we move the split to 3G | |
+ * (lowmem = 0xc0000000). But only in case we have to split in | |
+ * the first place, i.e. ram_size is larger than (traditional) | |
+ * lowmem. And for new machine types (gigabyte_align = true) | |
+ * only, for live migration compatibility reasons. | |
+ * | |
+ * - Next the max-ram-below-4g option was added, which allowed to | |
+ * reduce lowmem to a smaller value, to allow a larger PCI I/O | |
+ * window below 4G. qemu doesn't enforce gigabyte alignment here, | |
+ * but prints a warning. | |
+ * | |
+ * - Finally max-ram-below-4g got updated to also allow raising lowmem, | |
+ * so legacy non-PAE guests can get as much memory as possible in | |
+ * the 32bit address space below 4G. | |
+ * | |
+ * - Note that Xen has its own ram setup code in xen_ram_init(), | |
+ * called via xen_hvm_init_pc(). | |
+ * | |
+ * Examples: | |
+ * qemu -M pc-1.7 -m 4G (old default) -> 3584M low, 512M high | |
+ * qemu -M pc -m 4G (new default) -> 3072M low, 1024M high | |
+ * qemu -M pc,max-ram-below-4g=2G -m 4G -> 2048M low, 2048M high | |
+ * qemu -M pc,max-ram-below-4g=4G -m 3968M -> 3968M low (=4G-128M) | |
+ */ | |
+ if (!pcms->max_ram_below_4g) { | |
+ pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ | |
+ } | |
+ lowmem = pcms->max_ram_below_4g; | |
+ if (machine->ram_size >= pcms->max_ram_below_4g) { | |
+ if (pcmc->gigabyte_align) { | |
+ if (lowmem > 0xc0000000) { | |
+ lowmem = 0xc0000000; | |
+ } | |
+ if (lowmem & (1 * GiB - 1)) { | |
+ warn_report("Large machine and max_ram_below_4g " | |
+ "(%" PRIu64 ") not a multiple of 1G; " | |
+ "possible bad performance.", | |
+ pcms->max_ram_below_4g); | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (machine->ram_size >= lowmem) { | |
+ x86ms->above_4g_mem_size = machine->ram_size - lowmem; | |
+ x86ms->below_4g_mem_size = lowmem; | |
+ } else { | |
+ x86ms->above_4g_mem_size = 0; | |
+ x86ms->below_4g_mem_size = machine->ram_size; | |
+ } | |
+ | |
+ x86_cpus_init(x86ms, pcmc->default_cpu_version); | |
+ | |
+ if (pcmc->kvmclock_enabled) { | |
+ kvmclock_create(pcmc->kvmclock_create_always); | |
+ } | |
+ | |
+ pci_memory = g_new(MemoryRegion, 1); | |
+ memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); | |
+ rom_memory = pci_memory; | |
+ | |
+ pc_guest_info_init(pcms); | |
+ | |
+ if (pcmc->smbios_defaults) { | |
+ MachineClass *mc = MACHINE_GET_CLASS(machine); | |
+ /* These values are guest ABI, do not change */ | |
+ smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", | |
+ mc->name, pcmc->smbios_legacy_mode, | |
+ pcmc->smbios_uuid_encoded, | |
+ SMBIOS_ENTRY_POINT_21); | |
+ } | |
+ | |
+ /* allocate ram and load rom/bios */ | |
+ rdc_memory_init(pcms, system_memory, rom_memory, &ram_memory); | |
+ | |
+ gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); | |
+ | |
+ pci_bus = i440fx_init(host_type, | |
+ pci_type, | |
+ &i440fx_state, | |
+ system_memory, system_io, machine->ram_size, | |
+ x86ms->below_4g_mem_size, | |
+ x86ms->above_4g_mem_size, | |
+ pci_memory, ram_memory); | |
+ pcms->bus = pci_bus; | |
+ | |
+ piix3 = piix3_create(pci_bus, &isa_bus); | |
+ piix3->pic = x86ms->gsi; | |
+ piix3_devfn = piix3->dev.devfn; | |
+ | |
+ isa_bus_irqs(isa_bus, x86ms->gsi); | |
+ | |
+ pc_i8259_create(isa_bus, gsi_state->i8259_irq); | |
+ | |
+ ioapic_init_gsi(gsi_state, "i440fx"); | |
+ | |
+ if (tcg_enabled()) { | |
+ x86_register_ferr_irq(x86ms->gsi[13]); | |
+ } | |
+ | |
+ assert(pcms->vmport != ON_OFF_AUTO__MAX); | |
+ if (pcms->vmport == ON_OFF_AUTO_AUTO) { | |
+ pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; | |
+ } | |
+ | |
+ /* init basic PC hardware */ | |
+ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, &rtc_state, true, | |
+ 0x4); | |
+ | |
+ pc_nic_init(pcmc, isa_bus, pci_bus); | |
+ | |
+ if (pcmc->pci_enabled) { | |
+ PCIDevice *dev; | |
+ BusState *idebus[MAX_IDE_BUS]; | |
+ | |
+ dev = pci_create_simple(pci_bus, piix3_devfn + 1, | |
+ xen_enabled() ? "piix3-ide-xen" : "piix3-ide"); | |
+ pci_ide_create_devs(dev); | |
+ idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); | |
+ idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); | |
+ pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); | |
+ } | |
+ | |
+ if (pcmc->pci_enabled && machine_usb(machine)) { | |
+ pci_create_simple(pci_bus, piix3_devfn + 2, "pci-ohci"); | |
+ } | |
+ | |
+ if (pcmc->pci_enabled && x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) { | |
+ DeviceState *piix4_pm; | |
+ qemu_irq smi_irq; | |
+ | |
+ smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); | |
+ /* TODO: Populate SPD eeprom data. */ | |
+ pcms->smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, | |
+ x86ms->gsi[9], smi_irq, | |
+ x86_machine_is_smm_enabled(x86ms), | |
+ &piix4_pm); | |
+ smbus_eeprom_init(pcms->smbus, 8, NULL, 0); | |
+ | |
+ object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, | |
+ TYPE_HOTPLUG_HANDLER, | |
+ (Object **)&x86ms->acpi_dev, | |
+ object_property_allow_set_link, | |
+ OBJ_PROP_LINK_STRONG); | |
+ object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, | |
+ OBJECT(piix4_pm), &error_abort); | |
+ } | |
+} | |
+ | |
+static void rdc3210_machine_options(MachineClass *m) | |
+{ | |
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m); | |
+ pcmc->default_nic_model = "e1000"; | |
+ m->family = "pc_piix"; | |
+ m->desc = "RDC3210"; | |
+ m->default_machine_opts = "firmware=bios-256k.bin"; | |
+ m->default_display = NULL; | |
+ machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); | |
+ machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); | |
+ m->alias = "rdc3210"; | |
+ m->is_default = false; | |
+ pcmc->default_cpu_version = 1; | |
+} | |
+ | |
+static void pc_init_rdc3210(MachineState *machine) \ | |
+{ | |
+ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, | |
+ TYPE_I440FX_PCI_DEVICE); | |
+} | |
+ | |
+DEFINE_PC_MACHINE(rdc3210, "rdc-3210", pc_init_rdc3210, | |
+ rdc3210_machine_options); | |
Index: host-qemu-5.2.0/hw/net/Kconfig | |
=================================================================== | |
--- host-qemu-5.2.0.orig/hw/net/Kconfig | |
+++ host-qemu-5.2.0/hw/net/Kconfig | |
@@ -49,6 +49,11 @@ config RTL8139_PCI | |
default y if PCI_DEVICES | |
depends on PCI | |
+config R6040_PCI | |
+ bool | |
+ default y if PCI_DEVICES | |
+ depends on PCI | |
+ | |
config VMXNET3_PCI | |
bool | |
default y if PCI_DEVICES | |
Index: host-qemu-5.2.0/hw/net/meson.build | |
=================================================================== | |
--- host-qemu-5.2.0.orig/hw/net/meson.build | |
+++ host-qemu-5.2.0/hw/net/meson.build | |
@@ -11,6 +11,7 @@ softmmu_ss.add(when: 'CONFIG_E1000_PCI', | |
softmmu_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) | |
softmmu_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('e1000e.c', 'e1000e_core.c', 'e1000x_common.c')) | |
softmmu_ss.add(when: 'CONFIG_RTL8139_PCI', if_true: files('rtl8139.c')) | |
+softmmu_ss.add(when: 'CONFIG_R6040_PCI', if_true: files('r6040.c')) | |
softmmu_ss.add(when: 'CONFIG_TULIP', if_true: files('tulip.c')) | |
softmmu_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) | |
softmmu_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('vmxnet3.c')) | |
Index: host-qemu-5.2.0/hw/net/r6040.c | |
=================================================================== | |
--- /dev/null | |
+++ host-qemu-5.2.0/hw/net/r6040.c | |
@@ -0,0 +1,685 @@ | |
+/* | |
+ * Emulation of r6040 ethernet controller found in a number of SoCs. | |
+ * Copyright (c) 2011 Mark Kelly, mark@bifferos.com | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a | |
+ * copy of this software and associated documentation files (the "Software"), | |
+ * to deal in the Software without restriction, including without limitation | |
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
+ * and/or sell copies of the Software, and to permit persons to whom the | |
+ * Software is furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included | |
+ * in all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR | |
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
+ * | |
+ * This has been written using the R8610[1] and ip101a[2] datasheets. | |
+ * | |
+ * ICs with the embedded controller include R8610, R3210, AMRISC20000 | |
+ * and Vortex86SX | |
+ * | |
+ * The emulation seems good enough to fool Linux 2.6.37.6. It is | |
+ * not perfect, but has proven useful. | |
+ * | |
+ * [1] http://www.sima.com.tw/download/R8610_D06_20051003.pdf | |
+ * [2] http://www.icplus.com.tw/pp-IP101A.html | |
+ */ | |
+ | |
+#include "qemu/osdep.h" | |
+ | |
+#include "hw/pci/pci.h" | |
+#include "hw/qdev-properties.h" | |
+#include "migration/vmstate.h" | |
+#include "net/net.h" | |
+#include "sysemu/sysemu.h" | |
+#include "qemu/timer.h" | |
+ | |
+/* #define DEBUG_R6040 1 */ | |
+ | |
+ | |
+#if defined DEBUG_R6040 | |
+#define DPRINTF(fmt, ...) \ | |
+ do { fprintf(stderr, "R6040: " fmt, ## __VA_ARGS__); } while (0) | |
+#else | |
+static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...) | |
+{ | |
+ return 0; | |
+} | |
+#endif | |
+ | |
+#define TYPE_R6040 "r6040" | |
+ | |
+OBJECT_DECLARE_SIMPLE_TYPE(R6040State, R6040) | |
+ | |
+/* Cast in order of appearance. _W suffix means it's used to index the | |
+ register word array (regs_w) | |
+ */ | |
+ | |
+#define MPSCCR_W (0x88 / 2) | |
+ | |
+#define MAC0_W (0x68 / 2) | |
+#define MAC1_W (0x6a / 2) | |
+#define MAC2_W (0x6c / 2) | |
+ | |
+ | |
+#define RX_START_LOW_W (0x34 / 2) | |
+#define RX_START_HIGH_W (0x38 / 2) | |
+#define TX_PKT_COUNT_W (0x5a / 2) | |
+#define RX_PKT_COUNT_W (0x50 / 2) | |
+ | |
+ | |
+#define MCR0_W (0x00 / 2) | |
+#define MCR1_W (0x04 / 2) | |
+#define BIT_MRST (1 << 0) | |
+ | |
+#define MTPR_W (0x14 / 2) | |
+#define MRBSR_W (0x18 / 2) | |
+#define MISR_W (0x3c / 2) | |
+#define MIER_W (0x40 / 2) | |
+ | |
+#define MMDIO_W (0x20 / 2) | |
+#define MDIO_READ_W (0x24 / 2) | |
+#define MDIO_WRITE_W (0x28 / 2) | |
+ | |
+#define MRCNT_W (0x50 / 2) | |
+#define MTCNT_W (0x5c / 2) | |
+ | |
+ | |
+#define MDIO_WRITE 0x4000 | |
+#define MDIO_READ 0x2000 | |
+ | |
+ | |
+typedef struct R6040State { | |
+ PCIDevice dev; | |
+ NICState *nic; | |
+ NICConf conf; | |
+ | |
+ /* PHY related register sets */ | |
+ uint16_t mid0[3]; | |
+ uint16_t phy_regs[32]; | |
+ uint32_t phy_op_in_progress; | |
+ | |
+ /* Primary IO address space */ | |
+ union { | |
+ uint8_t regs_b[0x100]; /* Byte access */ | |
+ uint16_t regs_w[0x100/2]; /* word access */ | |
+ uint32_t regs_l[0x100/4]; /* DWORD access */ | |
+ }; | |
+ QEMUTimer *timer; | |
+ | |
+ MemoryRegion bar_io; | |
+ MemoryRegion bar_mem; | |
+} R6040State; | |
+ | |
+ | |
+/* some inlines to help access above structure */ | |
+static inline uint32_t TX_START(R6040State *s) | |
+{ | |
+ uint32_t tmp = s->regs_w[0x2c/2]; | |
+ return tmp | (s->regs_w[0x30/2] << 16); | |
+} | |
+ | |
+static inline void TX_START_SET(R6040State *s, uint32_t start) | |
+{ | |
+ s->regs_w[0x2c/2] = start & 0xffff; | |
+ s->regs_w[0x30/2] = (start >> 16) & 0xffff; | |
+} | |
+ | |
+static inline uint32_t RX_START(R6040State *s) | |
+{ | |
+ uint32_t tmp = s->regs_w[0x34/2]; | |
+ return tmp | (s->regs_w[0x38/2] << 16); | |
+} | |
+ | |
+static inline void RX_START_SET(R6040State *s, uint32_t start) | |
+{ | |
+ s->regs_w[0x34/2] = start & 0xffff; | |
+ s->regs_w[0x38/2] = (start >> 16) & 0xffff; | |
+} | |
+ | |
+ | |
+static void r6040_update_irq(R6040State *s) | |
+{ | |
+ PCIDevice *d = PCI_DEVICE(s); | |
+ uint16_t isr = s->regs_w[MISR_W] & s->regs_w[MIER_W]; | |
+ | |
+ pci_set_irq(d, (isr != 0)); | |
+} | |
+ | |
+ | |
+/* Mark auto-neg complete, NIC up. */ | |
+static void PhysicalLinkUp(void *opaque) | |
+{ | |
+ R6040State *s = opaque; | |
+ s->phy_regs[1] |= (1 << 2); | |
+ timer_del(s->timer); | |
+ timer_free(s->timer); | |
+} | |
+ | |
+ | |
+/* Transmit and receive descriptors are doubled up | |
+ One is a subset of the other anyhow | |
+ */ | |
+typedef struct Descriptor { | |
+ uint16_t dst; | |
+ uint16_t dlen; | |
+ uint32_t dbp; | |
+ uint32_t dnx; | |
+ uint16_t hidx; | |
+ uint16_t reserved_1; | |
+ uint16_t reserved_2; | |
+} Descriptor; | |
+ | |
+ | |
+/* Some debugging functions */ | |
+ | |
+#ifdef DEBUG_R6040 | |
+static void addr_dump16(const char *name, uint16_t val) | |
+{ | |
+ DPRINTF("%s: 0x%04x ", name, val); | |
+} | |
+ | |
+static void addr_dump32(const char *name, uint32_t val) | |
+{ | |
+ DPRINTF("%s: 0x%x ", name, val); | |
+} | |
+ | |
+static void hex_dump(const uint8_t *data, uint32_t len) | |
+{ | |
+ uint8_t i; | |
+ DPRINTF("hex: "); | |
+ for (i = 0; i < len; i++) { | |
+ fprintf(stderr, "%02x ", *data); | |
+ if (i && !(i % 0x20)) { | |
+ fprintf(stderr, "\n"); | |
+ } | |
+ data++; | |
+ } | |
+ fprintf(stderr, "\n"); | |
+} | |
+ | |
+static void desc_dump(Descriptor *d, uint32_t addr) | |
+{ | |
+ DPRINTF("\nDumping: 0x%x\n", addr); | |
+ addr_dump16("DST", d->dst); | |
+ addr_dump16("DLEN", d->dlen); | |
+ addr_dump32("DBP", (unsigned long)d->dbp); | |
+ addr_dump32("DNX", (unsigned long)d->dnx); | |
+ addr_dump16("HIDX", d->hidx); | |
+ printf("\n"); | |
+} | |
+ | |
+static void dump_phys_mem(uint32_t addr, int len) | |
+{ | |
+ uint8_t buffer[1024]; | |
+ cpu_physical_memory_read(addr, buffer, len); | |
+ hex_dump(buffer, len); | |
+} | |
+ | |
+static void dump_pci(uint8_t *pci_conf) | |
+{ | |
+ uint32_t *p = (uint32_t *)pci_conf; | |
+ int i = 0; | |
+ for (i = 0; i < 0x40; i += 4) { | |
+ DPRINTF("Addr: 0x%08x, Data: 0x%08x\n", i, *p); | |
+ p++; | |
+ } | |
+} | |
+#endif | |
+ | |
+ | |
+static const VMStateDescription vmstate_r6040 = { | |
+ .name = "r6040", | |
+ .version_id = 3, | |
+ .minimum_version_id = 2, | |
+ .minimum_version_id_old = 2, | |
+ .fields = (VMStateField[]) { | |
+ VMSTATE_PCI_DEVICE(dev, R6040State), | |
+ VMSTATE_BUFFER(regs_b, R6040State), | |
+ VMSTATE_UINT16_ARRAY(mid0, R6040State, 3), | |
+ VMSTATE_UINT16_ARRAY(phy_regs, R6040State, 32), | |
+ VMSTATE_UINT32(phy_op_in_progress, R6040State), | |
+ VMSTATE_MACADDR(conf.macaddr, R6040State), | |
+ VMSTATE_END_OF_LIST() | |
+ } | |
+}; | |
+ | |
+ | |
+static int TryToSendOnePacket(void *opaque) | |
+{ | |
+ R6040State *s = opaque; | |
+ Descriptor d; | |
+ uint8_t pkt_buffer[2000]; | |
+ uint32_t tocopy; | |
+ | |
+ cpu_physical_memory_read(TX_START(s), (uint8_t *)&d, sizeof(d)); | |
+ | |
+ if (d.dst & 0x8000) { /* MAC owns it? */ | |
+ tocopy = d.dlen; | |
+ if (tocopy > sizeof(pkt_buffer)) { | |
+ tocopy = sizeof(pkt_buffer); | |
+ } | |
+ /* copy the packet to send it */ | |
+ cpu_physical_memory_read(d.dbp, pkt_buffer, tocopy); | |
+ | |
+ qemu_send_packet(qemu_get_queue(s->nic), pkt_buffer, tocopy); | |
+ s->regs_w[TX_PKT_COUNT_W]++; | |
+ | |
+ /* relinquish ownership, we're done with it */ | |
+ d.dst &= ~0x8000; | |
+ | |
+ /* Copy the new version of the descriptor back */ | |
+ cpu_physical_memory_write(TX_START(s), (uint8_t *)&d, sizeof(d)); | |
+ | |
+ /* Advance to the next buffer if packet processed */ | |
+ TX_START_SET(s, d.dnx); | |
+ | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static void r6040_transmit(void *opaque) | |
+{ | |
+ R6040State *s = opaque; | |
+ int count = 0; | |
+ | |
+ while (TryToSendOnePacket(s)) { | |
+ ++count; | |
+ } | |
+ | |
+ if (count) { | |
+ s->regs_w[MISR_W] |= 0x10; | |
+ r6040_update_irq(s); | |
+ } | |
+} | |
+ | |
+ | |
+/* Whether to allow callback returning 1 for yes, can receive */ | |
+static bool r6040_can_receive(NetClientState *nc) | |
+{ | |
+ R6040State *s = qemu_get_nic_opaque(nc); | |
+ return s->regs_w[0] & 0x0002; | |
+} | |
+ | |
+ | |
+static int ReceiveOnePacket(void *opaque, const uint8_t *buf, size_t len) | |
+{ | |
+ R6040State *s = opaque; | |
+ uint32_t tocopy = len+4; /* include checksum */ | |
+ Descriptor d; | |
+ | |
+ cpu_physical_memory_read(RX_START(s), (uint8_t *)&d, sizeof(Descriptor)); | |
+ /*desc_dump(&d, 0);*/ | |
+ | |
+ if (d.dst & 0x8000) { /* MAC owned? */ | |
+ | |
+ uint16_t max_buffer = s->regs_w[MRBSR_W] & 0x07fc; | |
+ if (tocopy > max_buffer) { | |
+ tocopy = max_buffer; | |
+ } | |
+ | |
+ cpu_physical_memory_write(d.dbp, buf, tocopy-4); | |
+ | |
+ /* indicate received OK */ | |
+ d.dst |= (1 << 14); | |
+ d.dlen = tocopy; | |
+ /* relinquish ownership */ | |
+ d.dst &= ~0x8000; | |
+ | |
+ /* Copy the descriptor back */ | |
+ cpu_physical_memory_write(RX_START(s), (uint8_t *)&d, | |
+ sizeof(Descriptor)); | |
+ | |
+ s->regs_w[RX_PKT_COUNT_W]++; | |
+ | |
+ s->regs_w[MISR_W] |= 1; /* received pkt interrupt */ | |
+ | |
+ r6040_update_irq(s); | |
+ | |
+ RX_START_SET(s, d.dnx); /* advance */ | |
+ | |
+ return 0; | |
+ } | |
+ return -1; | |
+} | |
+ | |
+ | |
+/* called on incoming packets */ | |
+static ssize_t r6040_receive(NetClientState *nc, const uint8_t *buf, | |
+ size_t len) | |
+{ | |
+ R6040State *s = qemu_get_nic_opaque(nc); | |
+ DPRINTF("Received incoming packet of len %ld\n", len); | |
+ | |
+ if (0 == ReceiveOnePacket(s, buf, len)) { | |
+ return len; /* copied OK */ | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static inline int BIT_SET(uint16_t old, uint16_t new, uint16_t bit) | |
+{ | |
+ uint16_t before = (old & (1 << bit)); | |
+ uint16_t after = (new & (1 << bit)); | |
+ if (!before && after) { | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+ | |
+static void r6040_ioport_writew(void *opaque, uint32_t addr, uint32_t val) | |
+{ | |
+ R6040State *s = opaque; | |
+ uint16_t old; | |
+ addr &= 0xff; /* get relative to base address */ | |
+ addr /= 2; /* Get the offset into the word-array */ | |
+ old = s->regs_w[addr]; /* store the old value for future use */ | |
+ | |
+ switch (addr) { | |
+ case MCR0_W: /* 0x00 */ | |
+ if (BIT_SET(old, val, 12)) { | |
+ r6040_transmit(opaque); | |
+ } | |
+ break; | |
+ case MCR1_W: /* 0x04 */ | |
+ if (val & BIT_MRST) { /* reset request incoming */ | |
+ /* reset requested, complete it immediately, set this value to | |
+ default */ | |
+ val = 0x0010; | |
+ } | |
+ break; | |
+ case MTPR_W: /* TX command reg, 0x14 */ | |
+ if (val & 1) { | |
+ r6040_transmit(opaque); | |
+ val &= ~1; | |
+ } | |
+ break; | |
+ case MMDIO_W: /* MDIO control, 0x20 */ | |
+ { | |
+ int phy_exists = ((val & 0x1f00) == 0x100) ? 1 : 0; | |
+ uint16_t *phy = s->phy_regs; | |
+ phy += (val & 0x1f); | |
+ | |
+ if (val & (1 << 13)) { /* read data */ | |
+ if (phy_exists) { | |
+ s->regs_w[MDIO_READ_W] = *phy; | |
+ } else { | |
+ s->regs_w[MDIO_READ_W] = 0xffff; | |
+ } | |
+ } else if (val & (1 << 14)) { /* write data */ | |
+ if (phy_exists) { | |
+ *phy = s->regs_w[MDIO_WRITE_W]; | |
+ } | |
+ } | |
+ | |
+ /* Whether you request to read or write, both bits go high while | |
+ the operation is in progress, e.g. tell it to read, and the | |
+ write-in-progress flag also goes high */ | |
+ val |= 0x6000; /* signal operation has started */ | |
+ s->phy_op_in_progress = 1; | |
+ | |
+ break; | |
+ } | |
+ case MISR_W: /* interrupt status reg (read to clear), 0x3c */ | |
+ return; | |
+ | |
+ case MIER_W: /* interrupt enable register, 0x40 */ | |
+ s->regs_w[MIER_W] = val; | |
+ r6040_update_irq(s); | |
+ return; | |
+ | |
+ case MRCNT_W: /* 0x50 */ | |
+ case MTCNT_W: /* 0x5c */ | |
+ return; /* Can't write to pkt count registers, skip */ | |
+ | |
+ } | |
+ s->regs_w[addr] = val & 0xFFFF; | |
+} | |
+ | |
+ | |
+static uint32_t r6040_ioport_readw(void *opaque, uint32_t addr) | |
+{ | |
+ R6040State *s = opaque; | |
+ addr &= 0xff; /* get relative to base address */ | |
+ addr /= 2; /* Get the offset into the word-array */ | |
+ uint32_t tmp = s->regs_w[addr]; /* get the value */ | |
+ | |
+ switch (addr) { | |
+ | |
+ case MMDIO_W: /* MDIO control, 0x20 */ | |
+ { | |
+ /* Clear any in-progress MDIO activity for the next read | |
+ This simulates the polling of the MDIO operation status, | |
+ so the driver code always has to read the register twice | |
+ before it thinks the operation is complete. */ | |
+ if (s->phy_op_in_progress) { | |
+ s->regs_w[addr] &= ~0x6000; | |
+ s->phy_op_in_progress = 0; | |
+ } | |
+ break; | |
+ } | |
+ case MISR_W: /* interrupt status reg (read to clear) 0x3c */ | |
+ s->regs_w[addr] = 0; | |
+ break; | |
+ case MIER_W: /* interrupt enable reg 0x40 */ | |
+ break; | |
+ case MRCNT_W: /* 0x50 */ | |
+ case MTCNT_W: /* 0x5c */ | |
+ s->regs_w[addr] = 0; /* read to clear */ | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+ return tmp; | |
+} | |
+ | |
+ | |
+/* byte and long access are routed via the word operation handlers */ | |
+static void r6040_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) | |
+{ | |
+ R6040State *s = opaque; | |
+ addr &= 0xFF; | |
+ val &= 0xFF; | |
+ uint16_t old = s->regs_w[addr/2]; /* get the current value */ | |
+ if (addr & 1) { | |
+ old &= 0xff; | |
+ old |= (val << 8); | |
+ } else { | |
+ old &= 0xff00; | |
+ old |= val; | |
+ } | |
+ | |
+ r6040_ioport_writew(opaque, addr, old); /* call the word-based version */ | |
+} | |
+ | |
+static void r6040_ioport_writel(void *opaque, uint32_t addr, uint32_t val) | |
+{ | |
+ /* Set the low value */ | |
+ r6040_ioport_writew(opaque, addr, val & 0xffff); | |
+ /* Set the high value */ | |
+ r6040_ioport_writew(opaque, addr+2, (val >> 16) & 0xffff); | |
+} | |
+ | |
+static uint32_t r6040_ioport_readb(void *opaque, uint32_t addr) | |
+{ | |
+ uint32_t tmp = r6040_ioport_readw(opaque, addr & ~1); | |
+ if (addr & 1) { | |
+ return (tmp & 0xff00) >> 8; | |
+ } | |
+ return tmp & 0xff; | |
+} | |
+ | |
+static uint32_t r6040_ioport_readl(void *opaque, uint32_t addr) | |
+{ | |
+ uint32_t tmp = r6040_ioport_readw(opaque, addr); | |
+ return tmp | (r6040_ioport_readw(opaque, addr+2) << 16); | |
+} | |
+ | |
+static void r6040_ioport_write(void *opaque, hwaddr addr, | |
+ uint64_t val, unsigned size) | |
+{ | |
+ switch (size) { | |
+ case 1: | |
+ r6040_ioport_writeb(opaque, addr, val); | |
+ break; | |
+ case 2: | |
+ r6040_ioport_writew(opaque, addr, val); | |
+ break; | |
+ case 4: | |
+ r6040_ioport_writel(opaque, addr, val); | |
+ break; | |
+ } | |
+} | |
+ | |
+static uint64_t r6040_ioport_read(void *opaque, hwaddr addr, | |
+ unsigned size) | |
+{ | |
+ switch (size) { | |
+ case 1: | |
+ return r6040_ioport_readb(opaque, addr); | |
+ case 2: | |
+ return r6040_ioport_readw(opaque, addr); | |
+ case 4: | |
+ return r6040_ioport_readl(opaque, addr); | |
+ } | |
+ | |
+ return -1; | |
+} | |
+ | |
+static const MemoryRegionOps rtl8139_io_ops = { | |
+ .read = r6040_ioport_read, | |
+ .write = r6040_ioport_write, | |
+ .impl = { | |
+ .min_access_size = 1, | |
+ .max_access_size = 4, | |
+ }, | |
+ .endianness = DEVICE_LITTLE_ENDIAN, | |
+}; | |
+ | |
+ | |
+static NetClientInfo net_r6040_info = { | |
+ .type = NET_CLIENT_DRIVER_NIC, | |
+ .size = sizeof(NICState), | |
+ .can_receive = r6040_can_receive, | |
+ .receive = r6040_receive, | |
+}; | |
+ | |
+ | |
+static void r6040_realize(PCIDevice *dev, Error **errp) | |
+{ | |
+ R6040State *s = R6040(dev); | |
+ uint8_t *pci_conf; | |
+ | |
+ /* MAC PHYS status change register. Linux driver expects something | |
+ sensible as default and if not will try to set it */ | |
+ s->regs_w[MPSCCR_W] = 0x9f07; | |
+ | |
+ /* Default value for maximum packet size */ | |
+ s->regs_w[MRBSR_W] = 0x600; | |
+ | |
+ /* set the MAC, linux driver reads this when it loads, it is | |
+ normally set by the BIOS, but obviously Qemu BIOS isn't going | |
+ to do that */ | |
+ s->regs_w[MAC0_W] = 0x5452; | |
+ s->regs_w[MAC1_W] = 0x1200; | |
+ s->regs_w[MAC2_W] = 0x5734; | |
+ | |
+ /* Tell Qemu the same thing */ | |
+ s->conf.macaddr.a[0] = s->regs_w[MAC0_W] & 0xff; | |
+ s->conf.macaddr.a[1] = (s->regs_w[MAC0_W] >> 8) & 0xff; | |
+ s->conf.macaddr.a[2] = s->regs_w[MAC1_W] & 0xff; | |
+ s->conf.macaddr.a[3] = (s->regs_w[MAC1_W] >> 8) & 0xff; | |
+ s->conf.macaddr.a[4] = s->regs_w[MAC2_W] & 0xff; | |
+ s->conf.macaddr.a[5] = (s->regs_w[MAC2_W] >> 8) & 0xff; | |
+ | |
+ /* no commands running */ | |
+ s->phy_op_in_progress = 0; | |
+ | |
+ /* PHY auto-neg in progress */ | |
+ s->phy_regs[1] = 0x786d & ~(1 << 2); | |
+ s->phy_regs[2] = 0x0243; | |
+ s->phy_regs[3] = 0x0c54; | |
+ | |
+ pci_conf = (uint8_t *)s->dev.config; | |
+ | |
+ pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; /* header_type */ | |
+ pci_conf[PCI_INTERRUPT_LINE] = 0xa; /* interrupt line */ | |
+ pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */ | |
+ | |
+ memory_region_init_io(&s->bar_io, OBJECT(s), &rtl8139_io_ops, s, | |
+ "r6040", 0x100); | |
+ memory_region_init_alias(&s->bar_mem, OBJECT(s), "r6040-mem", &s->bar_io, | |
+ 0, 0x100); | |
+ | |
+ pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io); | |
+ pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar_mem); | |
+ | |
+ s->nic = qemu_new_nic(&net_r6040_info, &s->conf, | |
+ object_get_typename(OBJECT(dev)), DEVICE(dev)->id, s); | |
+ | |
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); | |
+ | |
+ /* Simulate a delay of a couple of seconds for the link to come up. | |
+ Not required for Linux but very handy for developing BIOS code. | |
+ */ | |
+ s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, PhysicalLinkUp, s); | |
+ timer_mod(s->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1500); | |
+} | |
+ | |
+static Property r6040_properties[] = { | |
+ DEFINE_NIC_PROPERTIES(R6040State, conf), | |
+ DEFINE_PROP_END_OF_LIST(), | |
+}; | |
+ | |
+static void r6040_class_init(ObjectClass *klass, void *data) | |
+{ | |
+ DeviceClass *dc = DEVICE_CLASS(klass); | |
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); | |
+ | |
+ k->realize = r6040_realize; | |
+ k->exit = NULL; | |
+ k->romfile = NULL; | |
+ k->vendor_id = PCI_VENDOR_ID_RDC; | |
+ k->device_id = PCI_DEVICE_ID_RDC_R6040; | |
+ k->revision = 0; | |
+ k->class_id = PCI_CLASS_NETWORK_ETHERNET; | |
+ dc->reset = NULL; | |
+ dc->vmsd = &vmstate_r6040; | |
+ device_class_set_props(dc, r6040_properties); | |
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); | |
+} | |
+ | |
+ | |
+static const TypeInfo r6040_info = { | |
+ .name = TYPE_R6040, | |
+ .parent = TYPE_PCI_DEVICE, | |
+ .instance_size = sizeof(R6040State), | |
+ .class_init = r6040_class_init, | |
+ .instance_init = NULL, | |
+ .interfaces = (InterfaceInfo[]) { | |
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE }, | |
+ { }, | |
+ }, | |
+}; | |
+ | |
+ | |
+static void r6040_register_types(void) | |
+{ | |
+ type_register_static(&r6040_info); | |
+} | |
+ | |
+ | |
+type_init(r6040_register_types) | |
Index: host-qemu-5.2.0/hw/pci-host/i440fx.c | |
=================================================================== | |
--- host-qemu-5.2.0.orig/hw/pci-host/i440fx.c | |
+++ host-qemu-5.2.0/hw/pci-host/i440fx.c | |
@@ -309,6 +309,11 @@ PCIBus *i440fx_init(const char *host_typ | |
} | |
d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size; | |
+ d->config[0x90] = 0x00; | |
+ d->config[0x91] = 0x10; | |
+ d->config[0x92] = 0x32; | |
+ d->config[0x93] = 0x00; | |
+ | |
i440fx_update_memory_mappings(f); | |
return b; | |
@@ -329,8 +334,10 @@ static void i440fx_class_init(ObjectClas | |
k->realize = i440fx_realize; | |
k->config_write = i440fx_write_config; | |
- k->vendor_id = PCI_VENDOR_ID_INTEL; | |
- k->device_id = PCI_DEVICE_ID_INTEL_82441; | |
+// k->vendor_id = PCI_VENDOR_ID_INTEL; | |
+// k->device_id = PCI_DEVICE_ID_INTEL_82441; | |
+ k->vendor_id = PCI_VENDOR_ID_RDC; | |
+ k->device_id = PCI_DEVICE_ID_RDC_R6020; | |
k->revision = 0x02; | |
k->class_id = PCI_CLASS_BRIDGE_HOST; | |
dc->desc = "Host bridge"; | |
Index: host-qemu-5.2.0/include/hw/pci/pci_ids.h | |
=================================================================== | |
--- host-qemu-5.2.0.orig/include/hw/pci/pci_ids.h | |
+++ host-qemu-5.2.0/include/hw/pci/pci_ids.h | |
@@ -258,6 +258,11 @@ | |
#define PCI_DEVICE_ID_INTEL_P35_MCH 0x29c0 | |
+#define PCI_VENDOR_ID_RDC 0x17f3 | |
+#define PCI_DEVICE_ID_RDC_R6020 0x6020 | |
+#define PCI_DEVICE_ID_RDC_R6030 0x6030 | |
+#define PCI_DEVICE_ID_RDC_R6040 0x6040 | |
+ | |
#define PCI_VENDOR_ID_XEN 0x5853 | |
#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 | |
Index: host-qemu-5.2.0/target/i386/cpu.h | |
=================================================================== | |
--- host-qemu-5.2.0.orig/target/i386/cpu.h | |
+++ host-qemu-5.2.0/target/i386/cpu.h | |
@@ -135,7 +135,10 @@ typedef enum X86Seg { | |
#define AC_MASK 0x00040000 | |
#define VIF_MASK 0x00080000 | |
#define VIP_MASK 0x00100000 | |
-#define ID_MASK 0x00200000 | |
+// | |
+// Prevent EFLAGS ID toggle, so linux doesn't think we have CPU ID. | |
+//#define ID_MASK 0x00200000 | |
+#define ID_MASK 0x00000000 | |
/* hidden flags - used internally by qemu to represent additional cpu | |
states. Only the INHIBIT_IRQ, SMM and SVMI are not redundant. We |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment