QEMUã®ãªãã¿(QEMU internals) part2
åå(part1)rkx1209.hatenablog.com
ã®ç¶ãã§ãã
part2ã§ã¯ä»®æ³IRQ,ãããã»ãã,ä»®æ³IO,TCGãè¦ã¦ããã¾ãã
å¤åpart2ã§çµããã§ãã(æéãããã°ã¾ããã¤ãpart3ã¨ãæ¸ãã¦ã¿ããã§ãã...)
ã§ã¯ã¾ãä»®æ³IRQããè¦ã¦ããã¾ãã
6.ä»®æ³IRQ
QEMUã«ããã¦IRQã¯IRQStateæ§é ä½ã§è¡¨ããã¾ãã
(hw/core/irq.c)
struct IRQState { Object parent_obj; qemu_irq_handler handler; void *opaque; int n; };
nãIRQçªå·ã§handlerãIRQ#nã«å¯¾å¿ãããã³ãã©ã§ããã¾ãIRQStateã¯(include/hw/irq.h)ã§qemu_irqã«typedefããã¦ãããã以éã¯qemu_irqã¨å¼ã³ã¾ãã
ã§ã¯ååè¦ã¦ãããã·ã³åæåã®ã¡ã¤ã³é¨åã§ããpc_init1ã®ç¶ãããè¦ã¦è¡ãã¾ãããã
(hw/i386/pc_piix.c: pc_init1)
static void pc_init1(MachineState *machine, const char *host_type, const char *pci_type) { ... ISABus *isa_bus; ... qemu_irq *gsi; qemu_irq *i8259; qemu_irq smi_irq; GSIState *gsi_state; ... gsi_state = g_malloc0(sizeof(*gsi_state)); [*1] ... else { gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); [*2] } isa_bus_irqs(isa_bus, gsi); [*6] ... else { i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());[*7] } ... for (i = 0; i < ISA_NUM_IRQS; i++) { gsi_state->i8259_irq[i] = i8259[i]; [*8] } ... }
[*1]ã§GSIStateã確ä¿ãã¦ãã¾ããGSIã¯Global System Interruptsã®ç¥ã§qemu_irqãç½®ãã¦ããã°ãã¼ãã«ãªã³ã³ããã¹ãã§ãã
[*2]ã§#0 ~ #GSI_NUM_PINSã¾ã§ã®IRQãåæåãã¦ããã¾ãã
(hw/core/irq.c)
qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler, void *opaque, int n) { qemu_irq *s; int i; if (!old) { n_old = 0; } s = old ? g_renew(qemu_irq, old, n + n_old) : g_new(qemu_irq, n); for (i = n_old; i < n + n_old; i++) { s[i] = qemu_allocate_irq(handler, opaque, i); [*3] } return s; } qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n) { return qemu_extend_irqs(NULL, 0, handler, opaque, n); } qemu_irq qemu_allocate_irq(qemu_irq_handler handler, void *opaque, int n) { struct IRQState *irq; irq = IRQ(object_new(TYPE_IRQ)); [*4] irq->handler = handler; irq->opaque = opaque; irq->n = n; return irq; }
[*3]ã§ããããã®IRQãåæåãã¦ãã¾ããå ·ä½çã«ã¯[*4]ã§TYPE_IRQåã®ãªãã¸ã§ã¯ããnewãã¦ãã¾ãããã§ã¯qemu_allocate_irqsã«æ¸¡ããã¦ãããã³ãã©ãgsi_handlerãè¦ã¦ã¿ã¾ãã
(hw/i386/pc.c)
void gsi_handler(void *opaque, int n, int level) { GSIState *s = opaque; DPRINTF("pc: %s GSI %d\n", level ? "raising" : "lowering", n); if (n < ISA_NUM_IRQS) { qemu_set_irq(s->i8259_irq[n], level);[*5] } qemu_set_irq(s->ioapic_irq[n], level); }
IRQãã³ãã¼ãISAã®ç¯å²ã§æ±ãããªã[*5]ã§å²ãè¾¼ã¿ãçºçããã¦ãã¾ããæ±ããªãç¯å²ã¯APICã§å²ãè¾¼ãã§ãã¾ãã
(hw/core/irq.c)
void qemu_set_irq(qemu_irq irq, int level) { if (!irq) return; irq->handler(irq->opaque, irq->n, level); }
qemu_set_irqã¯æå®ãããirqã®ãã³ãã©ãå¼ã³åºãã ãã§ããããã§å²ãè¾¼ã¿ãçºçãã¾ããç°¡åã§ããã
ã§ã¯pc_init1ã«æ»ãã¾ãããã
[*6]ã®isa_bus_irqs(hw/isa/isa-bus.c)ã§ISAãã¹ã«gsiãè¨å®ãã¦ãã¾ããPCIãã¹ã®ããã«4ãã³ããã³ã³ãã£ã°ã¬ã¼ã·ã§ã³ãéãã¦IRQã«å¯¾å¿ãããå¿
è¦ã¯ç¡ãã®ã§ãã®ããã«ç´ ç´ãªä»£å
¥ã§æ¸ãã§ãã¾ãã[*7]ã§isa_busã«æ¥ç¶ãããi8259ãåæåãã¦ãã¾ãã(isa_busã«ã¤ãã¦ã¯æ¬¡ã®ç« ã§èª¬æãã¾ã)
(hw/intc/i8259.c: i8259_init)
qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq) { qemu_irq *irq_set; DeviceState *dev; ISADevice *isadev; int i; irq_set = g_new0(qemu_irq, ISA_NUM_IRQS); /* Init i8259(master) */ isadev = i8259_init_chip(TYPE_I8259, bus, true); [*9] dev = DEVICE(isadev); qdev_connect_gpio_out(dev, 0, parent_irq); for (i = 0 ; i < 8; i++) { irq_set[i] = qdev_get_gpio_in(dev, i); } isa_pic = dev; /* Init i8259(slave) */ isadev = i8259_init_chip(TYPE_I8259, bus, false); [*10] dev = DEVICE(isadev); qdev_connect_gpio_out(dev, 0, irq_set[2]); [*11] for (i = 0 ; i < 8; i++) { irq_set[i + 8] = qdev_get_gpio_in(dev, i); } slave_pic = PIC_COMMON(dev); /* irq_set is GPIO of i8259 */ return irq_set; }
[*9][*10]ã§ããããi8259ã®ãã¹ã¿ã¼ãã¹ã¬ã¼ããåæåãã[*11]ã§ã¯devã®unnamed-gpio-out[0]ããããã£ãããªãã¡IRQã®åºåããã¹ã¿ã¼ã®irq_set[2]ã«è¨å®ãã¦ãã¾ããããã§ã«ã¹ã±ã¼ãæ¥ç¶ãå®äºãã¾ãããã¾ãããã§irq_setã«è¨å®ããããã³ãã©ã¯pc_allocate_cpu_irq(hw/i386/pc.c)ã§æå®ããã¦ããpic_irq_requestã§ãã
(hw/i386/pc.c: pic_irq_request)
static void pic_irq_request(void *opaque, int irq, int level) { CPUState *cs = first_cpu; X86CPU *cpu = X86_CPU(cs); DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq); ... else { if (level) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } }
(translate-all.c: cpu_interrupt)
void cpu_interrupt(CPUState *cpu, int mask) { cpu->interrupt_request |= mask; cpu->tcg_exit_req = 1; }
cpu_interruptã¯CPUStateã«ãã¼ãå²ãè¾¼ã¿ãçºçããäºãéç¥ãTCGã®å®è¡ãä¸æ¢ããããè¦æ±ãã¾ãã(TCGã«ã¤ãã¦ã¯å¾ã»ã©èª¬æãã¾ã)
æå¾ã«[*8]ã«ã¦gsi_stateã®qemu_irqã«ä¸ã§è¨å®ããç©ã代å
¥ãã¦ããã¾ããããã§qemu_set_irqãå®è¡ãããå ´åãgsi_stateãéãã¦pic_irq_requestãå¼ã³åºãããäºã«ãªãã¾ãã
7.ä»®æ³ãããã»ãã
ãã¦æ¬¡ã¯ä»®æ³ãããã»ããã§ãã念ã®çºãã¼ãã¦ã§ã¢ã®å
¨ä½åpart1ã¨ã¯éããã¼ã¸ã§ã³ã§è¼ãã¦ããã¾ãã
INTEL 440FX PCISET 82441FX PCI AND MEMORY CONTROLLER (PMC) AND 82442FX DATA BUS
ACCELERATOR (DBX)ãã
http://download.intel.com/design/chipsets/datashts/29054901.pdf
ã§ã¯pc_init1ã«æ»ã£ã¦ä»åº¦ã¯i440fxãåæåãã¦ããé¨åãè¦ã¦è¡ãã¾ãããã
(hw/i386/pc_piix.c: pc_init1)
if (pci_enabled) { /* hosttype: TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost" pci_type: TYPE_I440FX_PCI_DEVICE "i440FX" gsi: IRQ system_memory: main memory */ pci_bus = i440fx_init(host_type, pci_type, &i440fx_state, &piix3_devfn, &isa_bus, gsi, system_memory, system_io, machine->ram_size, pcms->below_4g_mem_size, pcms->above_4g_mem_size, pci_memory, ram_memory);[*1] }
host_typeã¨ããã®ã¯ä¸å³ã«ãããããã«CPUã®ãã¹ã¨PCIãã¹ãã¤ãªãããã®PCI Host Bridgeã表ãã¦ãã¾ããã¾ãpci_typeã¯ã¤ãªãå ã®PCIã表ãã¦ãã¾ãã
(hw/pci-host/piix.c)
PCIBus *i440fx_init(const char *host_type, const char *pci_type, PCII440FXState **pi440fx_state, int *piix3_devfn, ISABus **isa_bus, qemu_irq *pic, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, ram_addr_t ram_size, ram_addr_t below_4g_mem_size, ram_addr_t above_4g_mem_size, MemoryRegion *pci_address_space, MemoryRegion *ram_memory) { DeviceState *dev; PCIBus *b; PCIDevice *d; PCIHostState *s; PIIX3State *piix3; PCII440FXState *f; unsigned i; I440FXState *i440fx; dev = qdev_create(NULL, host_type); [*2] s = PCI_HOST_BRIDGE(dev);[*3] b = pci_bus_new(dev, NULL, pci_address_space, address_space_io, 0, TYPE_PCI_BUS); [*4] s->bus = b; [*5] object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); [*6] qdev_init_nofail(dev); [*7] d = pci_create_simple(b, 0, pci_type); [*8] *pi440fx_state = I440FX_PCI_DEVICE(d); f = *pi440fx_state; f->system_memory = address_space_mem; f->pci_address_space = pci_address_space; f->ram_memory = ram_memory; i440fx = I440FX_PCI_HOST_BRIDGE(dev); i440fx->pci_info.w32.begin = below_4g_mem_size; /* setup pci memory mapping */ pc_pci_as_mapping_init(OBJECT(f), f->system_memory, f->pci_address_space); [*9] ... else { PCIDevice *pci_dev = pci_create_simple_multifunction(b, -1, true, "PIIX3"); [*10] piix3 = PIIX3_PCI_DEVICE(pci_dev); pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, PIIX_NUM_PIRQS); [*11] pci_bus_set_route_irq_fn(b, piix3_route_intx_pin_to_irq); [*12] } piix3->pic = pic; [*13] *isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0")); [*14] *piix3_devfn = piix3->dev.devfn; ... return b; }
[*2]qdev_createã§ã¯æ°ããDeviceStateã確ä¿ãã¾ããDeviceStateã¯èªåãæ¥ç¶ããã¦ãããã¹ã®æ
å ±ãparentã¨ãã¦æã¡ã¾ãã第1å¼æ°ã«ã¯ãã®parentã«æå®ããBusStateã渡ãã¾ãããã®å ´åNULLãªã®ã§ããã©ã«ãã®ã«ã¼ããã¹ãparentã«ãªãã¾ãã
ã¾ã第2å¼æ°ã«ã¯ä½æããQdevã®ã¿ã¤ãåãæå®ãã¾ãã(ããã§ã¯i440FX-pcihost)
[*3]ã§PCIHostBridgeClassã®ã¤ã³ã¹ã¿ã³ã¹ã§ããPCIHostStateã«è§£éãã¦ãã¾ãã(part1ã§èª¬æããQOMã®ããªã¢ã¼ãã£ãºã ã§ã) PCIHostBridgeClass(include/hw/pci/pci_host.h)ã¯SysBusDeviceClass(include/hw/sysbus.h)ãç¶æ¿ãã¦ãã¾ãã
[*4]ã®pci_bus_newã§PCIBusClass(include/hw/pci/pci_bus.h)ã®ã¤ã³ã¹ã¿ã³ã¹ã§ããPCIBusãåæåãã¦ãã¾ãã
PCIBus *pci_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min, const char *typename) { PCIBus *bus; bus = PCI_BUS(qbus_create(typename, parent, name)); pci_bus_init(bus, parent, name, address_space_mem, address_space_io, devfn_min); return bus; }
parentã¯ãã¹ãçãã¦ãã親ããã¤ã¹(DeviceState)ã§ããBusStateã¯è¦ªã«DeviceStateãæã¡ãDeviceStateã親ã«BusStateãæã£ã¦ãã¾ããã¤ã¾ããããã¯äº¤äºã«ã¤ãªãã£ã¦ããè¨è¨ã«ãªã£ã¦ãã¾ãã(ã¾ããã¼ãã®åè·¯å³ãèããã°ãããªãã®ãèªç¶ã§ãã)
[*5]ã§ãã¹ãããªãã¸ã«PCIBusãã¤ãªãã§ãã¾ãã
[*6]ã§ã¯object_property_add_childã§"i440fx"ããããã£ã追å ãã³ã³ããã«ã¤ãªãã§ãã¾ãã
ããããã°part1ã®2ç« QOMã®æã§ã³ã³ããã®èª¬æããã¦ããªãã£ãã®ã§ããã«ã¤ãã¦ã¯å°ã詳ããè¦ã¦è¡ãã¾ããã
(qom/object.c)
void object_property_add_child(Object *obj, const char *name, Object *child, Error **errp) { Error *local_err = NULL; gchar *type; ObjectProperty *op; ... type = g_strdup_printf("child<%s>", object_get_typename(OBJECT(child))); op = object_property_add(obj, name, type, object_get_child_property, NULL, object_finalize_child_property, child, &local_err); ... child->parent = obj; out: g_free(type); }
ã¾ãã»ã¨ãã©object_property_addã®wrapperãªã®ã§ãããæå¾ã«childãobjã®åã¨ãã¦ç¹ãç´ãã¦ãã¾ãã
ããObjectã£ã¦è¦ªåé¢ä¿ãªãã¦ãã£ããã¨ãªãã¨æãã¾ãããããã¯QOMã®ã³ã³ãã(ã¨å¼ãã§è¯ãã®ããª)ã§ä½¿ç¨ããæã®ã¿ä½¿ãããé¢ä¿ã§ããã³ã³ããã¯éè¦ãªã¤ã³ã¹ã¿ã³ã¹ãæ¨æ§é ã§ã¤ãªãã§ãããã®ã§ã«ã¼ãã¯object_get_root()ã§åå¾ã§ãã¾ãã
qdev_get_machineãè¦ã¦ã¿ãã¨åããã¾ãã
(hw/core/qdev.c)
Object *qdev_get_machine(void) { static Object *dev; if (dev == NULL) { dev = container_get(object_get_root(), "/machine"); } return dev; }
ã³ã³ããã®æ¨æ§é ã¯ãã¡ã¤ã«ãã¹ã®ããã«ã¢ã¯ã»ã¹ãã¾ããããã§ã¯root/machineãªãã¸ã§ã¯ããåå¾ãã¦ãã訳ã§ãã
ã¤ã¾ã[*6]ã¯root/machineãªãã¸ã§ã¯ã(-machineã§æå®ããMachineClassã§ã)ã®"i440fx"ããããã£ã«devãªãã¸ã§ã¯ããã¤ãªãroot/machine/i440fxãä½æãã¦ãã¾ãã
ãã¦[*7]ã®qdev_init_nofail(hw/core/qdev.c)ã§devã®realizedããããã£ãtrueã«è¨å®ãã¦ãã¾ããrealizedããããã£ã¯part1ã§ã説æãã¾ãããããã¤ã¹ãèµ·åãããétrueã«è¨å®ããã¾ãã
ã¡ãªã¿ã«initã®ãããªååã§ãªãreazliedã¨ããååãã¤ãã¦ããã®ã¯æããQMP(QEMU Machine Protocol)ãªã©ãå©ç¨ããããã¤ã¹ã®ããããã©ã°æã«å¼ã³åºãããããã ã¨æããã¾ãã(ãã©ã°ã«æ°ä»ãã¨ããæå³åãã§ããããã)
i440fxããªãã¸ã®realizedã³ã¼ã«ããã¯é¢æ°ã«ã¤ãã¦ã¯å¾ã§è¦ã¦ããã¾ãã
[*8]ã§ã¯i440fxãããæ¬ä½ã®åæåãè¡ã£ã¦ãã¾ããã¾ã[*9]ã®pc_pci_as_mapping_init(hw/i386/pc.c)ã§ã¯pci_address_spaceãsystem_memoryã®åã¨ãã¦ã¤ãªãã§ãã¾ãã(part1ã®ä»®æ³ã¡ã¢ãªã®èª¬æãè¦ã¦ãã ãã)
[*10]ã§ã¯PIIX3Stateãåæåãã¦ãã¾ããä¸å³ã§ã¯piix4ã¨ãªã£ã¦ãã¾ããæ£ç¢ºã«ã¯piix3ã«è¿ãã³ã¼ãä¸ã§ã¯3ã®æ±ãã«ãªã£ã¦ãã¾ãã
[*11]ã®pci_bus_irqs(hw/pci/pci.c)ã§PCIBusã®set_irqã«piix3_set_irqãè¨å®ãã¦ãã¾ããPICã¯PCIBusãéãã¦CPUã¸ã¨ã¤ãªãã£ã¦ãããããã¹ã¸å²ãè¾¼ã¿ãã³ãã©ãè¨å®ãã¦ããã®ã ã¨æããã¾ãã
[*12]ã§ã¯ãã¹ã«piix3_route_intx_pin_to_irqã¨ããé¢æ°ãè¨å®ãã¦ãã¾ãããããã¯ä¸ä½ä½ãªã®ã§ããããã
(hw/pci-host/piix.c: piix3_route_intx_pin_to_irq)
static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) { PIIX3State *piix3 = opaque; int irq = piix3->dev.config[PIIX_PIRQC + pin]; PCIINTxRoute route; if (irq < PIIX_NUM_PIC_IRQS) { route.mode = PCI_INTX_ENABLED; route.irq = irq; } else { route.mode = PCI_INTX_DISABLED; route.irq = -1; } return route; }
piix3->dev.configã¯åPCIããã¤ã¹ã®PCIã³ã³ãã£ã°ã¬ã¼ã·ã§ã³ç©ºéã§ãã念ã®çºä»æ§å³ãè¼ãã¦ããã¾ãã
Wikipedia:PCI configuration spaceãã
https://en.wikipedia.org/wiki/PCI_configuration_space
config[PIIX_PIRQC + pin]ã«ããä¸å³ã®Interrupt Lineãã対å¿ããIRQçªå·ãåãåºãã¦ããããã§ãã
[*13]ã§ã¯6ç« ã§gsi_handlerãè¨å®ããqemu_irqã®é åãè¨å®ãã[*14]ã§ISAãã¹ãåæåãã¦ãã¾ããISAãã¹ã¯piix3ã®childãã¹ã¨ãªã£ã¦ãã¾ãã
8.ä»®æ³IO
ã§ã¯æ¬¡ã«ä»®æ³IOã«ã¤ãã¦è¦ã¦ããã¾ããããã§ã¯ä»®æ³ããã¤ã¹ã¨ãã¦ããããã¼ãã£ã¹ã¯ãã©ã¤ããè¦ã¦ããã¾ãã(VENOMèå¼±æ§ã§æåã«ãªã£ãããã§ã)
(hw/block/fdc.c)
static const MemoryRegionOps fdctrl_mem_ops = { .read = fdctrl_read_mem, [*3] .write = fdctrl_write_mem, .endianness = DEVICE_NATIVE_ENDIAN, }; static void sysbus_fdc_initfn(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); FDCtrlSysBus *sys = SYSBUS_FDC(obj); FDCtrl *fdctrl = &sys->state; memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_ops, fdctrl, "fdc", 0x08); [*2] sysbus_init_mmio(sbd, &fdctrl->iomem); } ... static const TypeInfo sysbus_fdc_info = { .name = "sysbus-fdc", .parent = TYPE_SYSBUS_FDC, .instance_init = sysbus_fdc_initfn, [*1] .class_init = sysbus_fdc_class_init, }; static void fdc_register_types(void) { ... type_register_static(&sysbus_fdc_info); ... } type_init(fdc_register_types)
ãã¤ãã®type_initãããããã§ããããã¼ã¯ã©ã¹ãåæåãã¦ãã¾ãã[*1]ã§ã³ã³ã¹ãã©ã¯ã¿ã¨ãã¦sysbus_fdc_initfnãè¨å®ãã¦ãã¾ããã³ã³ã¹ãã©ã¯ã¿å
ã§ã¯[*2]ã«ããã¦MMIOãåæåãã¦ãã¾ããMMIOã¯AddressSpaceã¨ãã¦address_space_io(exec.c)ãæã¡ãã®ä¸ã«ãã¼ãçªå·ãã¢ãã¬ã¹ã¨ããMemoryRegionãã¤ãªãã£ã¦ããã¾ãã(ãã®è¾ºãã®ãã¼ã¿æ§é ã¯part1ã®5.SoftMMUãããä¸åº¦è¦ãªããã¦ãã ãã)
ãã¦ãã®MemoryRegionãMMIOã§ããå ´åãreadããã³writeã®ããã¯é¢æ°ãå¼ã³åºããã¾ãã
part1ã§ã¿ãaddress_space_lduw_internalãããä¸åº¦è¦ã¦ã¿ã¾ãããã
ååã¯çç¥ãã¦ãã¾ãããMMIOãå¦çããé¨åãããã¾ãã
(exec.c: address_space_lduw_internal)
static inline uint32_t address_space_lduw_internal(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, MemTxResult *result, enum device_endian endian) { uint8_t *ptr; uint64_t val; MemoryRegion *mr; hwaddr l = 2; hwaddr addr1; MemTxResult r; bool release_lock = false; rcu_read_lock(); mr = address_space_translate(as, addr, &addr1, &l, false); if (l < 2 || !memory_access_is_direct(mr, false)) { .... /* I/O case */ r = memory_region_dispatch_read(mr, addr1, &val, 2, attrs); [*4] .... } }
MMIOã®å ´å[*4]ã§ããã¯é¢æ°ãå¼ã³åºãã¦ãã¾ãã
ããããã¼ãã£ã¹ã¯ã®å ´åãã®ããã¯é¢æ°readã¯[*3]ã®fdctrl_read_memã«ãªãã¾ãã
(hw/block/fdc.c)
static uint32_t fdctrl_read (void *opaque, uint32_t reg) { FDCtrl *fdctrl = opaque; uint32_t retval; reg &= 7; switch (reg) { ... case FD_REG_FIFO: retval = fdctrl_read_data(fdctrl); [*5] break; } return retval; } static uint64_t fdctrl_read_mem (void *opaque, hwaddr reg, unsigned ize) { return fdctrl_read(opaque, (uint32_t)reg); }
[*5]ã§ãã¼ã¿ã¬ã¸ã¹ã¿ãããã¼ã¿ãèªã¿è¾¼ãã§ãã¾ãã
(ããããã¼ãã£ã¹ã¯ã³ã³ããã¼ã©ã®ä»æ§ã¯wikiãªã©ãåç
§ãã¦ãã ããã)
(hw/block/fdc.c)
static uint32_t fdctrl_read_data(FDCtrl *fdctrl) { FDrive *cur_drv; uint32_t retval = 0; uint32_t pos; ... pos = fdctrl->data_pos; pos %= FD_SECTOR_LEN; switch (fdctrl->phase) { case FD_PHASE_EXECUTION: if (pos == 0) { ... if (blk_read(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { [*6] FLOPPY_DPRINTF("error getting sector %d\n", fd_sector(cur_drv)); /* Sure, image size is too small... */ memset(fdctrl->fifo, 0, FD_SECTOR_LEN); } } } retval = fdctrl->fifo[pos]; FLOPPY_DPRINTF("data register: 0x%02x\n", retval); return retval; }
[*6]ã§ã»ã¯ã¿çªå·ããã³BlockBackendãæå®ãã¦ãããã¯èªã¿è¾¼ã¿ãè¡ã£ã¦ãã¾ãã
ããããã¯ããããã¼ã®ãã¼ã¿è»¢éé¨åãæ±ããããã¯ããã¤ã¹ãã©ã¤ãã®å¦çãè¦ã¦ããã¾ãã
(block/block-backend.c: blk_read)
int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int nb_sectors) { ... return bdrv_read(blk->bs, sector_num, buf, nb_sectors); }
(block/io.c)
static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors, bool is_write, BdrvRequestFlags flags) { QEMUIOVector qiov; struct iovec iov = { .iov_base = (void *)buf, .iov_len = nb_sectors * BDRV_SECTOR_SIZE, }; if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { return -EINVAL; } qemu_iovec_init_external(&qiov, &iov, 1); return bdrv_prwv_co(bs, sector_num << BDRV_SECTOR_BITS, &qiov, is_write, flags); [*7] } int bdrv_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false, 0); }
QEMUIOVectorã«IOãªã¯ã¨ã¹ããè¨å®ãã[*7]ã§bdrv_prwv_coãå¼ã³åºãã¦ãã¾ãã
(block/io.c)
static int bdrv_prwv_co(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov, bool is_write, BdrvRequestFlags flags) { Coroutine *co; RwCo rwco = { .bs = bs, .offset = offset, .qiov = qiov, .is_write = is_write, .ret = NOT_DONE, .flags = flags, }; ... else { AioContext *aio_context = bdrv_get_aio_context(bs); co = qemu_coroutine_create(bdrv_rw_co_entry); [*8] qemu_coroutine_enter(co, &rwco); [*9] while (rwco.ret == NOT_DONE) { aio_poll(aio_context, true); } } return rwco.ret; }
[*8]ã§ã³ã«ã¼ãã³ãä½æãã¦ãã¾ããQEMUã¯callback hellãåé¿ããããã«ç¬èªã«ã³ã«ã¼ãã³ãå®è£ ãã¦ããã[*9]ã®enterã§bdrv_rw_co_entryã³ã«ã¼ãã³ã«é£ã³ãyieldãå¼ã°ããã¨æ»ã£ã¦ãã¾ãã
(block/io.c)
static void coroutine_fn bdrv_rw_co_entry(void *opaque) { RwCo *rwco = opaque; if (!rwco->is_write) { rwco->ret = bdrv_co_do_preadv(rwco->bs, rwco->offset, rwco->qiov->size, rwco->qiov, rwco->flags); } ... } ... static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { BlockDriver *drv = bs->drv; BdrvTrackedRequest req; ... ret = bdrv_aligned_preadv(bs, &req, offset, bytes, align, use_local_qiov ? &local_qiov : qiov, flags); [*10] ... return ret; }
bdrv_co_do_preadvã¯ä¸ããããããã¯IOè¦æ±ã®ã¢ãã¬ã¹ã®ã¢ã©ã¤ã¡ã³ãã調æ´ããå¾[*10]ã®bdrv_aligned_preadvãå¼ã³åºãã ãã§ãã
static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, int64_t align, QEMUIOVector *qiov, int flags) { BlockDriver *drv = bs->drv; int ret; int64_t sector_num = offset >> BDRV_SECTOR_BITS; unsigned int nb_sectors = bytes >> BDRV_SECTOR_BITS; ... /* Forward the request to the BlockDriver */ if (!bs->zero_beyond_eof) { ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); } else { /* Read zeros after EOF */ int64_t total_sectors, max_nb_sectors; total_sectors = bdrv_nb_sectors(bs); if (total_sectors < 0) { ret = total_sectors; goto out; } max_nb_sectors = ROUND_UP(MAX(0, total_sectors - sector_num), align >> BDRV_SECTOR_BITS); ... else if (max_nb_sectors > 0) { ret = drv->bdrv_co_readv(bs, sector_num, max_nb_sectors, &local_qiov); [*11] } ... } }
[*11]ã®BlockDriver->bdrv_co_readvã«ã¯-driverãªãã·ã§ã³ã§æå®ããqemuã®imgå½¢å¼ã«ãã£ã¦æå®ãããé¢æ°ãéãã¾ããä¾ãã°qcow2(block/qcow2.c)ã§ã¯qcow2_co_readvãªã©ãæå®ããã¦ãã¾ããqcow2ã®ç´°ããä»æ§ã«é¢ãã¦ã¯çç¥ãã¾ãã
9.TCG
ã§ã¯ããããQEMUã®ä¸å¿å¦çã§ããTCG(Tiny Code Generator)ã®å¦çãè¦ã¦ããã¾ããååã®4.ä»®æ³CPUã®[*15]ãè¦ã¦ãã ããããã®tcg_exec_all()ã¨ããã®ãTCGã®å®è¡éå§é¢æ°ã§ããã§ã¯ãã®tcg_exec_allé¢æ°ããè¦ã¦è¡ãã¾ãããã
(cpus.c)
static int tcg_cpu_exec(CPUState *cpu) { ... ret = cpu_exec(cpu);[*2] ... return ret; } static void tcg_exec_all(void) { ... for (; next_cpu != NULL && !exit_request; next_cpu = CPU_NEXT(next_cpu)) { CPUState *cpu = next_cpu; qemu_clock_enable(QEMU_CLOCK_VIRTUAL, (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); if (cpu_can_run(cpu)) { r = tcg_cpu_exec(cpu); [*1] ... } else if (cpu->stop || cpu->stopped) { break; } } }
[*1]ã§åvcpuãã¨ã«tcg_cpu_execãå®è¡ãã¦ãã¾ããtcg_cpu_execã§ã¯[*2]ã§cpu_execãå¼ã³åºããã¦ããããããããã¡ã¤ã³ã®å¦çã¨ãªãã¾ãã
(cpu-exec.c)
int cpu_exec(CPUState *cpu) { CPUClass *cc = CPU_GET_CLASS(cpu); #ifdef TARGET_I386 X86CPU *x86_cpu = X86_CPU(cpu); CPUArchState *env = &x86_cpu->env; #endif int ret, interrupt_request; TranslationBlock *tb; uint8_t *tc_ptr; uintptr_t next_tb; SyncClocks sc; ... current_cpu = cpu; ... for(;;) { if (sigsetjmp(cpu->jmp_env, 0) == 0) { ... next_tb = 0; for(;;) { interrupt_request = cpu->interrupt_request; if (unlikely(interrupt_request)) { if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { [*3] next_tb = 0; } ... } ... tb_lock(); tb = tb_find_fast(cpu); [*4] ... if (next_tb != 0 && tb->page_addr[1] == -1) { tb_add_jump((TranslationBlock *)(next_tb & ~TB_EXIT_MASK), next_tb & TB_EXIT_MASK, tb); [*5] } tb_unlock(); if (likely(!cpu->exit_request)) { trace_exec_tb(tb, tb->pc); tc_ptr = tb->tc_ptr; cpu->current_tb = tb; next_tb = cpu_tb_exec(cpu, tc_ptr); [*6] cpu->current_tb = NULL; ... } /* for(;;) */ ... } } /* for(;;) */ return ret; } ||< [*3]ã§å²ãè¾¼ã¿ãªã¯ã¨ã¹ããæ¥ã¦ããå ´åcpu_exec_interruptãå®è¡ãã¾ããã²ã¹ããx86ã®å ´åããã¯(target-i386/cpu.c)ã®type_initãéãã¦x86_cpu_exec_interruptãè¨å®ããã¦ãã¾ãã (target-i386/seg_helper.c: x86_cpu_exec_interrupt) >|c| bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; bool ret = false; ... else if ((interrupt_request & CPU_INTERRUPT_HARD) && (((env->hflags2 & HF2_VINTR_MASK) && (env->hflags2 & HF2_HIF_MASK)) || (!(env->hflags2 & HF2_VINTR_MASK) && (env->eflags & IF_MASK && !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { int intno; ... intno = cpu_get_pic_interrupt(env); [*7] ... do_interrupt_x86_hardirq(env, intno, 1); [*8] /* ensure that no TB jump will be modified as the program flow was changed */ ret = true; } ... } return ret; }
[*7]ã§PICCommonState(include/hw/isa/i8259_internal.h)ã®irq_baseã使ã£ã¦IRQçªå·ããINTçªå·ã¸å¤æãã¦ãã¾ãã[*8]ã§å®éã«å²ãè¾¼ã¿ãå®è¡ãã¾ãã
(target-i386/seg_helper.c)
static void do_interrupt_all(X86CPU *cpu, int intno, int is_int, int error_code, target_ulong next_eip, int is_hw) { CPUX86State *env = &cpu->env; if (env->cr[0] & CR0_PE_MASK) { { do_interrupt_protected(env, intno, is_int, error_code, next_eip, is_hw); [*9] } } ... } .. void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw) { do_interrupt_all(x86_env_get_cpu(env), intno, 0, 0, 0, is_hw); }
ãããã¯ãã¢ã¼ããæå¹ãªå ´å[*9]ã§do_interrupt_protectedãå¼ã³åºãã¾ãã
(target-i386/seg_helper.c: do_interrupt_protected)
static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, int error_code, unsigned int next_eip, int is_hw) { SegmentCache *dt; target_ulong ptr, ssp; int type, dpl, selector, ss_dpl, cpl; int has_error_code, new_stack, shift; uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0; uint32_t old_eip, sp_mask; int vm86 = env->eflags & VM_MASK; ... dt = &env->idt; ptr = dt->base + intno * 8; e1 = cpu_ldl_kernel(env, ptr); [*10] e2 = cpu_ldl_kernel(env, ptr + 4); /* check gate type */ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; ... dpl = (e2 >> DESC_DPL_SHIFT) & 3; [*11] cpl = env->hflags & HF_CPL_MASK; ... selector = e1 >> 16; offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); ... if (!(e2 & DESC_C_MASK) && dpl < cpl) { /* to inner privilege */ get_ss_esp_from_tss(env, &ss, &esp, dpl, 0); [*12] ... new_stack = 1; sp_mask = get_sp_mask(ss_e2); ssp = get_seg_base(ss_e1, ss_e2); } ... shift = type >> 3; else { PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env)); [*13] PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector); PUSHW(ssp, esp, sp_mask, old_eip); if (has_error_code) { PUSHW(ssp, esp, sp_mask, error_code); } } ... if (new_stack) { ... ss = (ss & ~3) | dpl; cpu_x86_load_seg_cache(env, R_SS, ss, ssp, get_seg_limit(ss_e1, ss_e2), ss_e2); } SET_ESP(esp, sp_mask); selector = (selector & ~3) | dpl; cpu_x86_load_seg_cache(env, R_CS, selector, get_seg_base(e1, e2), get_seg_limit(e1, e2), e2); [*16] env->eip = offset; [*17] }
[*10]ã§IDTã®ç¤ºãã²ã¼ããã£ã¹ã¯ãªãã¿ã®ä¸ä½4byteããã³ä¸ä½4byteãèªã¿è¾¼ãã§ãã¾ãã[*11]ã§ã¯ãã£ã¹ã¯ãªãã¿ã§ç¤ºãããDPL(Descriptor Privilege Level)ããã³ç¾ç¹æ¨©ã¬ãã«ã®CPL(Current Privilege Level)ãè¨å®ããDPL < CPLãªå ´åãã¤ã¾ãå²ãè¾¼ã¿ã«ãã£ã¦æ¨©éãä¸ããå ´å[*12]ã§TSSããæ°ããã¹ã¿ãã¯ã®æ
å ±ãèªã¿ã ãã¦ãã¾ããããã¦[*13]ã§ç¾å¨ã®eflags,cs,eipãéé¿ãå²ãè¾¼ã¿ã®æºåãå®äºãã¾ãã
(ãã®è¾ºãã®ä»æ§ã¯intelã®SDMãèªããã詳解linuxã«ã¼ãã«ãæã£ã¦ããæ¹ã¯p.152ãããã«è¼ã£ã¦ãã¾ãã®ã§é©æåç
§ãã¦ãã ãã)
[*13]ã®PUSHWãå°ã詳ããè¦ã¦ã¿ã¾ãããã
(target-i386/seg_helper.c)
#define PUSHW_RA(ssp, sp, sp_mask, val, ra) \ { \ sp -= 2; \ cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ } #define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0)
å®è³ªçãªå¦çã¯cpu_stw_kernel_raã§ããã¯ãããããä¸ç©ãªååã®é¢æ°ã§ãããå®ç¾©ã¯ãã¡ãã«ãªãã¾ãã
(include/exec/cpu_ldst_template.h)
static inline void glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, target_ulong ptr, RES_TYPE v, uintptr_t retaddr) { int page_index; target_ulong addr; int mmu_idx; TCGMemOpIdx oi; addr = ptr; page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = CPU_MMU_INDEX; if (unlikely(env->tlb_table[mmu_idx][page_index].addr_write != (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { oi = make_memop_idx(SHIFT, mmu_idx); glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, v, oi, retaddr); [*14] } else { uintptr_t hostaddr = addr + env->tlb_table[mmu_idx][page_index].addend; glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v); [*15] } }
[*14]ã§ã¯tlb_tableã«ãã£ãã·ã¥ãç¡ãå ´åhelper_be_st_name(softmmu_tmeplate.h)ãå©ç¨ãã¦HVAã«å¤æããå¤ãæ¸ãè¾¼ãã§ãã¾ãã(ããã¯part1ã®SoftMMUã®æã§è¦ã¾ãã)
ãããã§ã«ãã£ãã·ã¥ãããå ´åã¯addendã使ã£ã¦HVAã«å¤æããå¾ã[*15]ã®stw_pãå¼ã³åºãã¾ããstw_pã¯(include/exec/cpu-all.h)ã«ããã¦stw_le_pã®å¥åã¨ãªã£ã¦ãã¾ãã
(stw_le_pã¨ããã®ã¯store word little endian pointerã®ç¥ãæå³ãã¦ããã¨æããã¾ã)
(include/qemu/bswap.h)
static inline void stw_he_p(void *ptr, uint16_t v) { memcpy(ptr, &v, sizeof(v)); } static inline void stw_le_p(void *ptr, uint16_t v) { stw_he_p(ptr, le_bswap(v, 16)); }
çµå±memcpyã§ãããããã§HVAã«å¤ãæ¸ãè¾¼ãã§ãã¾ãã
do_interrupt_protectedã«æ»ã[*16]ã®cpu_x86_load_seg_cache(target-i386/cpu.h)ã§ã¯CSã¬ã¸ã¹ã¿(ããã¯SegmentCacheã§è¡¨ããã¾ã)ãæ´æ°ãã¦ãã¾ããããã¦[*17]ã§eipãã»ã°ã¡ã³ãã¬ã¸ã¹ã¿å
ã®ãªãã»ããã«æ´æ°ãå²ãè¾¼ã¿ã¯çµäºã§ããããã§å²ãè¾¼ã¿ãã³ãã©ã«å¶å¾¡ã移ãã¾ãã
ãã¦å²ãè¾¼ã¿å¦çãçµãã£ãã®ã§TCGã«æ»ãã¾ãããã[*4]ã®tb_find_fastã¯TranslationBlockããã£ãã·ã¥ããæ¢ãããªãå ´åã¯ããã«tb_find_slowãå¼ã³åºãã¾ãã
(cpu-exec.c)
static inline TranslationBlock *tb_find_fast(CPUState *cpu) { CPUArchState *env = (CPUArchState *)cpu->env_ptr; TranslationBlock *tb; target_ulong cs_base, pc; int flags; cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); tb = cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]; if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base || tb->flags != flags)) { tb = tb_find_slow(cpu, pc, cs_base, flags); } return tb; }
ããããTranslationBlockã¨ã¯ã²ã¹ãã®å½ä»¤åãTCGãéãã¦ãã¹ãã®å½ä»¤åã«å¤æããéãããããåå²å½ä»¤ãã¨ã«åããç©ã§ãå½ä»¤åã®åºæ¬åä½ãããã¯ã®ãããªç©ã§ãã
ããããããã¯ã¯ãããããããã¼ã°ããã³ã¨ããã¼ã°ã§æã¾ããå½¢ã§å®è¡ããã¾ãããåå²å½ä»¤ãå®è¡ãã¦ãããã³ã«ãå®è¡ãã¹ã«ãã£ã¦æ¥ç¶ããã¦ããã¾ãããããBlock Chainingã¨è¨ãã¾ãã
QEMU: Architecture and Internals Lecture for the Embedded Systems Course CSD, University of Creteãã
http://www.csd.uoc.gr/~hy428/reading/qemu-internals-slides-may6-2014.pdf
ãã¦ã§ã¯tb_find_slowã®å®è£
ãè¦ã¦è¡ãã¾ãããã
(cpu-exec.c)
static TranslationBlock *tb_find_slow(CPUState *cpu, target_ulong pc, target_ulong cs_base, uint64_t flags) { TranslationBlock *tb; ... /* if no translated code available, then translate it now */ tb = tb_gen_code(cpu, pc, cs_base, flags, 0); [*18] /* we add the TB in the virtual pc hash table */ cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; [*19] return tb; }
[*18]ã§TranslationBlock(以ä¸TBã¨å¼ã³ã¾ã)ãä½æãã[*19]ã§ãã£ãã·ã¥ã«ããã¾ããã§ã¯ãã®tb_gen_codeãè¦ã¦ã¿ã¾ãã
(translate-all.c)
TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc, target_ulong cs_base, int flags, int cflags) { CPUArchState *env = cpu->env_ptr; TranslationBlock *tb; tb_page_addr_t phys_pc, phys_page2; target_ulong virt_page2; tcg_insn_unit *gen_code_buf; int gen_code_size, search_size; ... phys_pc = get_page_addr_code(env, pc);[*20] if (use_icount) { cflags |= CF_USE_ICOUNT; } tb = tb_alloc(pc); [*21] ... gen_code_buf = tcg_ctx.code_gen_ptr; tb->tc_ptr = gen_code_buf; tb->cs_base = cs_base; tb->flags = flags; tb->cflags = cflags; gen_intermediate_code(env, tb); [*22] ... tcg_ctx.tb_jmp_offset = NULL; tcg_ctx.tb_next = tb->tb_next; ... tcg_ctx.code_gen_ptr = (void *) ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, CODE_GEN_ALIGN); ... gen_code_size = tcg_gen_code(&tcg_ctx, gen_code_buf); [*34] .... /* check next page if needed */ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; phys_page2 = -1; if ((pc & TARGET_PAGE_MASK) != virt_page2) { phys_page2 = get_page_addr_code(env, virt_page2); } tb_link_page(tb, phys_pc, phys_page2); return tb; }
[*20]ã§ã¯ååã®SoftMMUã§è¦ãget_page_addr_codeã使ã£ã¦pcãHVAã«å¤æãã¦ãã¾ãã
[*21]ã§æ°ããTBã確ä¿ããå¾ã[*22]ã®gen_intermediate_codeã§å®éã«ãã¤ã¯ãã³ã¼ããä½æãã¦TBã«æ ¼ç´ãã¦ãã¾ãã
(target-i386/translate.c: gen_intermediate_code)
void gen_intermediate_code(CPUX86State *env, TranslationBlock *tb) { X86CPU *cpu = x86_env_get_cpu(env); CPUState *cs = CPU(cpu); DisasContext dc1, *dc = &dc1; target_ulong pc_ptr; uint64_t flags; target_ulong pc_start; target_ulong cs_base; int num_insns; int max_insns; ... cpu_T[0] = tcg_temp_new(); [*23] cpu_T[1] = tcg_temp_new(); cpu_A0 = tcg_temp_new(); ... for(;;) { pc_ptr = disas_insn(env, dc, pc_ptr); [*24] /* stop translation if indicated */ if (dc->is_jmp) break; ... } }
[*23]ã§ã¯ãã¤ã¯ãã³ã¼ãä¸ã§ä½¿ç¨ããTCGç¨ã®æ±ç¨ã¬ã¸ã¹ã¿ãäºã¤ãã¢ãã¬ã¹æ ¼ç´ç¨ã®ã¬ã¸ã¹ã¿ãä¸ã¤ä½æãã¦ãã¾ãã
[*24]ã§ããããpc_ptrçªå°ããå§ã¾ãã²ã¹ãã®ãã·ã³èªããã¤ã¯ãã³ã¼ãã«å¤æãã¦ããã¾ãã
(target-i386/translate.c: disas_insn)
/* convert one instruction. s->is_jmp is set if the translation must be stopped. Return the next pc value */ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, target_ulong pc_start) { int b, prefixes; int shift; TCGMemOp ot, aflag, dflag; int modrm, reg, rm, mod, op, opreg, val; target_ulong next_eip, tval; int rex_w, rex_r; s->pc = pc_start; ... b = cpu_ldub_code(env, s->pc); s->pc++; /* now check op code */ reswitch: switch(b) { ... case 0x8b: /* mov Ev, Gv */ ot = mo_b_d(b, dflag); modrm = cpu_ldub_code(env, s->pc++); [*25] reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); [*26] gen_op_mov_reg_v(ot, reg, cpu_T[0]); [*31] break; ... /* port I/O */ case 0xe4: case 0xe5: ot = mo_b_d32(b, dflag); val = cpu_ldub_code(env, s->pc++); tcg_gen_movi_tl(cpu_T[0], val); gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); tcg_gen_movi_i32(cpu_tmp2_i32, val); [*32] gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); [*33] gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); [*34] break; ... } return s->pc; }
ã¨ãã¥ã¬ã¼ã¿ããããã®ã¨ã¦ã¤ããªãé·ãswitchæã§ãããã¾ãã«ãé·ãã®ã§MOVå½ä»¤ã¨portI/Oã§ä½¿ç¨ããINå½ä»¤ã®ã¿ããã¯ã¢ãããã¾ãã
ã¾ãMOV r16/32 r/m16/32å½ä»¤ããè¦ã¦ããã¾ãããã[*25]ã§ModR/Mãã¤ããåãåºãã¦ãã¾ããããã«ãªãã©ã³ãã®æ
å ±ãå
¥ã£ã¦ãã¾ãã(ãã¤ããªã¢ã³ãªäººãªãæ¥å¸¸çã«è¦ã¦ããããããã¾ããã)
次ã«[*26]ã«ããã¦tcgã®loadå½ä»¤ãä½æãã¦ãã¾ãã
(target-i386/translate.c: gen_ldst_modrm)
static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, TCGMemOp ot, int reg, int is_store) { int mod, rm; mod = (modrm >> 6) & 3; rm = (modrm & 7) | REX_B(s); ... else { gen_lea_modrm(env, s, modrm); [*27] ... else { gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); ... } } }
is_storeã¯falseã§ãããã[*27]ã§cpu_A0ã«ãã£ã¹ãã¬ã¼ã¹ã¡ã³ããªã©ãè¨ç®ããã¡ã¢ãªã®ã¢ãã¬ã¹ãè¨å®ããgen_op_ld_vãå¼ã³åºããã¾ãã
(target-i386/translate.c: gen_op_ld_v)
static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) { tcg_gen_qemu_ld_tl(t0, a0, s->mem_index, idx | MO_LE); }
ã¾ãtcg_gen_qemu_ld_tlã¯(tcg/tcg-op.h)ã«ããã¦tcg_gen_qemu_ld_i32ã®å¥åã¨ãã¦defineããã¦ãã¾ãã
static void gen_ldst_i32(TCGOpcode opc, TCGv_i32 val, TCGv addr, TCGMemOp memop, TCGArg idx) { TCGMemOpIdx oi = make_memop_idx(memop, idx); ... if (TCG_TARGET_REG_BITS == 32) { tcg_gen_op4i_i32(opc, val, TCGV_LOW(addr), TCGV_HIGH(addr), oi); } else { tcg_gen_op3(&tcg_ctx, opc, GET_TCGV_I32(val), GET_TCGV_I64(addr), oi); } #endif } ... void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) { memop = tcg_canonicalize_memop(memop, 0, 0); gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx); }
tcg_gen_op4i_i32ã¯(tcg/tcg-op.h)ã§tcg_genop4ãå¼ã³åºãã¦ãã¾ãã
static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGv_i32 a4) { tcg_gen_op4(&tcg_ctx, opc, GET_TCGV_I32(a1), GET_TCGV_I32(a2), GET_TCGV_I32(a3), GET_TCGV_I32(a4)); }
void tcg_gen_op4(TCGContext *ctx, TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4) { int pi = ctx->gen_next_parm_idx; tcg_debug_assert(pi + 4 <= OPPARAM_BUF_SIZE); ctx->gen_next_parm_idx = pi + 4; ctx->gen_opparam_buf[pi + 0] = a1; [*28] ctx->gen_opparam_buf[pi + 1] = a2; ctx->gen_opparam_buf[pi + 2] = a3; ctx->gen_opparam_buf[pi + 3] = a4; tcg_emit_op(ctx, opc, pi); [*29] }
[*28]ã§4ã¤ã®å¼æ°ãTCGContextã«æ ¼ç´ããå¾[*29]ã§tcg_emit_opãå¼ã³åºãgen_op_bufã«TCGOpã¨ãã¦è¿½å ãã¦ãã¾ãã[*30]
static void tcg_emit_op(TCGContext *ctx, TCGOpcode opc, int args) { int oi = ctx->gen_next_op_idx; int ni = oi + 1; int pi = oi - 1; ctx->gen_last_op_idx = oi; ctx->gen_next_op_idx = ni; ctx->gen_op_buf[oi] = (TCGOp){ [*30] .opc = opc, .args = args, .prev = pi, .next = ni }; }
追å ãããTCGOpãã©ã®ããã«ãã¹ãã®ã³ã¼ãã«å¤æããããã«ã¤ãã¦ã¯å¾ã»ã©è¦ã¦ããã¾ããã§ã¯disas_insnã¾ã§æ»ãã[*31]ã®gen_op_mov_reg_vã§ã¯cpu_T[0]ãå®å ã®regã«movããå½ä»¤ãçæãã¦ãã¾ãããããå ç¨ã¨åãããã«å é¨ã§tcg_emit_opã使ãå½ä»¤ãããã¡ã«ããã¦ãã¾ãã
ãã¦ã§ã¯æ¬¡ã«port IOã®INå½ä»¤ãè¦ã¦è¡ãã¾ãããã
[*32]ã§cpu_T[0]ã«ãã¼ãçªå·ãmovãã¦ãã¾ããããã¦[*33]ã®gen_helper_in_funcããgen_helper_inlãå¼ã³åºãã¦ãã¾ãã
static void gen_helper_in_func(TCGMemOp ot, TCGv v, TCGv_i32 n) { switch (ot) { case MO_8: gen_helper_inb(v, cpu_env, n); break; case MO_16: gen_helper_inw(v, cpu_env, n); break; case MO_32: gen_helper_inl(v, cpu_env, n); break; default: tcg_abort(); } }
ãã®gen_helper_inlã¯DEF_HELPER_2(include/exec/helper-head.h)ã«ãã£ã¦DEF_HELPER_2(inl, tl, env, i32);(target-i386/helper.h)ã¨å®ç¾©ããã¦ãã¾ãã
DEF_HELPER_2ã¯å®è³ªçã«ã¯(include/exec/helper-gen.h)ã®DEF_HELPER_FLAGS_2ã§ãããtcg_gen_callNãå¼ã³åºãã¦ãã¾ãã
(include/exec/helper-gen.h: DEF_HELPER_FLAGS_2)
#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ { \ TCGArg args[2] = { dh_arg(t1, 1), dh_arg(t2, 2) }; \ tcg_gen_callN(&tcg_ctx, HELPER(name), dh_retvar(ret), 2, args); \ }
ãã®tcg_gen_callN(tcg/tcg.c)ã¯è©³ç´°ã¯çãã¾ãã第äºå¼æ°ã§ä¸ããããé¢æ°ãcallãããã¤ã¯ãã³ã¼ããçæãã¾ããããã§ã¯HELPERãã¯ã(include/exec/helper-head.h)ãç¨ããHELPER(name)ããªãã¡helper_inlé¢æ°ã«ãªãã¾ããã§ã¯ãã®helper_inlé¢æ°ã¯ä½ããã¦ãããã¨ããã¨
(target-i386/misc_helper.c: helper_inl)
target_ulong helper_inl(CPUX86State *env, uint32_t port) { ... return address_space_ldl(&address_space_io, port, cpu_get_mem_attrs(env), NULL); }
address_space_ioã®portçªå°ã®å¤ãèªã¿ã ãã¦ãã¾ããããã¯8ç« ã®ä»®æ³IOã§ã説æããããã«Memory Mapped IOã使ã£ã¦å¤é¨ããã¤ã¹ããã®ãã¼ã¿ãèªãã§ããããã§ããx86ã«ãããin/outå½ä»¤ã¯å é¨çã«MMIOã¨ãã¦æ±ããã¦ããã®ã§ãã
ãã¦ã§ã¯tb_code_genã«æ»ã£ã¦[*34]ã®tcg_gen_codeãè¦ã¦è¡ãã¾ãããããã®é¢æ°ãä¸ã§ä½æãããã¤ã¯ãã³ã¼ããå®éã®ãã¹ãã®ãã·ã³èªã«å¤æãã¾ãã
int tcg_gen_code(TCGContext *s, tcg_insn_unit *gen_code_buf) { ... for (oi = s->gen_first_op_idx; oi >= 0; oi = oi_next) { TCGOp * const op = &s->gen_op_buf[oi]; TCGArg * const args = &s->gen_opparam_buf[op->args]; TCGOpcode opc = op->opc; /* tcg_op_defs is defition of TCG operations(see tcg/tcg-opc.h) */ const TCGOpDef *def = &tcg_op_defs[opc]; uint16_t dead_args = s->op_dead_args[oi]; uint8_t sync_args = s->op_sync_args[oi]; switch (opc) { case INDEX_op_mov_i32: case INDEX_op_mov_i64: tcg_reg_alloc_mov(s, def, args, dead_args, sync_args); break; case INDEX_op_movi_i32: case INDEX_op_movi_i64: tcg_reg_alloc_movi(s, args, dead_args, sync_args); break; ... default: tcg_reg_alloc_op(s, def, opc, args, dead_args, sync_args); [*35] break; } ... } return tcg_current_code_size(s); }
[*35]ã§TCGã®ãªãã³ã¼ãããå®éã®ãã¹ãã®å½ä»¤åãçæãã¦ãã¾ãã
static void tcg_reg_alloc_op(TCGContext *s, const TCGOpDef *def, TCGOpcode opc, const TCGArg *args, uint16_t dead_args, uint8_t sync_args) { for(k = 0; k < nb_iargs; k++) { i = def->sorted_args[nb_oargs + k]; arg = args[i]; arg_ct = &def->args_ct[i]; ts = &s->temps[arg]; ... else if (ts->val_type == TEMP_VAL_CONST) { else { reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); tcg_out_movi(s, ts->type, reg, ts->val); [*36] ts->val_type = TEMP_VAL_REG; ts->reg = reg; ts->mem_coherent = 0; s->reg_to_temp[reg] = arg; } } } }
[*36]ã®tcg_out_moviã§åãã¹ããã¨ã®movå½ä»¤ãtcg_outé¢æ°(tcg/tcg.c)ã使ã£ã¦æ¸ãåºãã¦ãã¾ãã(tcg/i386/tcg-targe.c)
static __attribute__((unused)) inline void tcg_out32(TCGContext *s, uint32_t v) { if (TCG_TARGET_INSN_UNIT_SIZE == 4) { *s->code_ptr++ = v; } else { tcg_insn_unit *p = s->code_ptr; memcpy(p, &v, sizeof(v)); s->code_ptr = p + (4 / TCG_TARGET_INSN_UNIT_SIZE); } }
ãããæ¸ãã ãããã¹ãå½ä»¤åã¯[*6]ã®cpu_tb_execã«ãã£ã¦å®è¡ãéå§ããã¾ãã
cpu_tb_exec(cpu-exec.c)ã¯tcg_qemu_tb_exec(tcg/tcg.h)ãå¼ã³åºãã¾ãã
# define tcg_qemu_tb_exec(env, tb_ptr) \ ((uintptr_t (*)(void *, void *))tcg_ctx.code_gen_prologue)(env, tb_ptr)
ããã¯TCGContextã®code_gen_prologueãé¢æ°ãã¤ã³ã¿ã¨è§£éãå¼ã³åºãã¦ãã¾ãã
ãã®code_gen_prologueã¯TCGã«ããããããã¼ã°ã表ãã¦ããã(accel.c)ã®tcg_initã«ãã£ã¦åæåããã¦ãã¾ããtcg_initã¯tcg_exec_initãå¼ã³åºãã¾ãã
(translate-all.c: tcg_exec_init)
void tcg_exec_init(unsigned long tb_size) { cpu_gen_init(); page_init(); code_gen_alloc(tb_size); #if defined(CONFIG_SOFTMMU) tcg_prologue_init(&tcg_ctx); [*37] #endif }
[*37]ãcode_gen_prologueã®åæåé¢æ°ã§ãã
void tcg_prologue_init(TCGContext *s) { size_t prologue_size, total_size; void *buf0, *buf1; /* Put the prologue at the beginning of code_gen_buffer. */ buf0 = s->code_gen_buffer; s->code_ptr = buf0;ã[*38] s->code_buf = buf0; s->code_gen_prologue = buf0; /* Generate the prologue. */ tcg_target_qemu_prologue(s); [*39] ... }
[*38]ã§code_ptrããªãã¡ç¾å¨å¦çä¸ã®å½ä»¤åã®ãã¤ã³ã¿ããããã¼ã°ã«è¨å®ãã¦ãã¾ãã
[*39]ã®tcg_target_qemu_prologue(s)(tcg/tcg.c)ã§ãããã¼ã°,call TB,ã¨ããã¼ã°ãcode_gen_prologueã«æ¸ãè¾¼ãã§ãããããã§TBã«ã¸ã£ã³ãããå½ä»¤ãã§ãã¾ããã
ããã§ã¨ãã¥ã¬ã¼ã·ã§ã³ã«ãããåºæ¬çãªè¦ç´ ã¯æ®ã©å ¨ã¦èª¬æãã¾ããã
10.ãããã«
part1ããç¶ãTCG,SoftMMU,IO,IRQ,QOMã¨è¦ã¦ãã¾ããã
ãåç¥ã®æ¹ã¯å¤ãã¨æãã¾ãããQEMUã«ã¯ä»ã«ãQEMUTimer,BH,vfio,ã©ã¤ããã¤ã°ã¬ã¼ã·ã§ã³,virtio,QMP,TCI,JITã³ã³ãã¤ã©ãªã©ã¾ã ã¾ã æ°ããããªãç¨ã®æ©è½ãããã¾ãã
ã§ããæ¬è¨äºã§è¦ã¦ããåºæ¬çãªæãæãã¦ããã°ãã®è¾ºããèªãã®ãé£ããã¯ãªãã¨æãã¾ããèå³ã®ããæ¹ã¯ã©ãã©ãhackãã¦ã¿ã¦ãã ããã
QEMU-internalã¯ããã¾ã§ã§ã(å¤å)
èªãã§ãã ãã£ã¦æé£ããããã¾ããï¼