An overview,

The Android version used in this paper is 5.1.0_R1, while the Goldfish kernel version is 3.4. The Android image is based on x86 architecture. This article takes battery as an example to completely introduce the implementation and use of virtual devices.


Why does android Emulator need virtual devices? To put it simply, the Android system needs to use virtual devices, but the host system does not, such as GPS, Bluetooth, battery, GSM, etc. In addition, the virtual device also provides a way to communicate between Android Emulator and guest OS. For example, the power of the battery can be set in the Emulator control panel and whether the battery is being charged, as shown in Figure 1. You can also set the current GPS coordinates and so on; More importantly, the operation of drawing in the Guest OS can be executed in the host, so the Android Emulator can run the Guest OS smoothly.



Figure 1


The frame of the entire virtual appliance is shown in Figure 2, with the guest OS in the upper left corner; The lower left corner includes both the driver of the virtual device in kernel and the simulation of the virtual device in emulator. In the lower right corner is the Android Emulator.

1. The guest OS uses the virtual device driver provided by Hal or the kernel directly (generally, the virtual device driver provides some character device files and property files, which can be read and written).

2-1. From the perspective of kernel, there is no need to care about whether the device is real or virtual, but only the resources provided by the device, such as IO resources, interrupt number, and how to read and write the device registers, which are similar to ordinary drivers. It should be noted that the virtual devices are all attached to the Platform bus to facilitate dynamic allocation of IO memory space and interrupt number. Of course, the IO memory and interrupt number of the platform bus itself are fixed write dead, corresponding to the fixed write dead in the emulator.

2-2. From the perspective of emulator, the first is the simulation of Platform Bus, which needs to use fixed write-dead IO memory and interrupt number, which is corresponding to kernel. Other virtual devices then dynamically register IO memory and interrupt numbers onto the platform bus. When kernel reads and writes IO memory, emulator can obviously know which virtual physical address is being read and written to, and then obtain the virtual page. Virtual page has corresponding information, you can obtain a variable io_index, using this io_index, you can know which virtual device the page is IO memory, and the virtual device’s own read and write function, use the corresponding device read and write function, Read and write virtual device registers (each virtual device register is placed in a structure) to receive/return data according to the function of the agreed register. This is a lot of knowledge, and involves a lot of hardware knowledge, for pure software developers, too complex, more on this later.

3. Emulator provides an abstract virtual device called pipe. The corresponding device file is /dev/qemu_PIPE, which provides a common data sending and receiving method for both guest OS and Emulator. Based on the common data sending and receiving method of this layer, there are many Qemud services registered in emulator. Guest OS can communicate with these Qemud services by reading /dev/qemu_pipe.

PS:

1. There is a Qemud process in Guest OS. Virtual device ttyS1 is used to provide the communication mode between Guest OS and Emulator.

2. For the platform model, check out this resource :www.wowotech.net/device_mode…

When a new device is registered, the device is used as a parameter and probe is given to each matching driver to see which driver can process the new device.

When a new driver is registered, the driver is used as a parameter to probe each unprocessed matched device to see which unprocessed device the new driver can process.

Matches by driver and device name.




The driver of the virtual device in the kernel

2.1. Driver of battery

First, the study of virtual device battery documents:

VII. Goldfish battery:
======================

Relevant files:
  $QEMU/hw/android/goldfish/battery.c
  $QEMU/hw/power_supply.h
  $KERNEL/drivers/power/goldfish_battery.c

Device properties:
  Name: goldfish_battery
  Id: -1
  IrqCount: 1
  I/O Registers:
    0x00 INT_STATUS   R: Read battery and A/C status change bits.
    0x04 INT_ENABLE   W: Enable or disable IRQ on status change.
    0x08 AC_ONLINE    R: Read 0 if AC power disconnected, 1 otherwise.
    0x0c STATUS       R: Read battery status (charging/full/... see below).
    0x10 HEALTH       R: Read battery health (good/overheat/... see below).
    0x14 PRESENT      R: Read 1 if battery is present, 0 otherwise.
    0x18 CAPACITY     R: Read battery charge percentage in [0..100] range.

A simple device used to report the state of the virtual device's battery, and
whether the device is powered through a USB or A/C adapter.

The device uses a single IRQ to notify the kernel that the battery or A/C status
changed. When this happens, the kernel should perform an IO_READ(INT_STATUS)
which returns a 2-bit value containing flags:

  bit 0: Set to 1 to indicate a change in battery status.
  bit 1: Set to 1 to indicate a change in A/C status.

Note that reading this register also lowers the IRQ level.

The A/C status can be read with IO_READ(AC_ONLINE), which returns 1 if the
device is powered, or 0 otherwise.

The battery status is spread over multiple I/O registers:

  IO_READ(PRESENT) returns 1 if the battery is present in the virtual device,
  or 0 otherwise.

  IO_READ(CAPACITY) returns the battery's charge percentage, as an integer
  between 0 and 100, inclusive. NOTE: This register is probably misnamed since
  it does not represent the battery's capacity, but it's current charge level.

  IO_READ(STATUS) returns one of the following values:

    0x00  UNKNOWN      Battery state is unknown.
    0x01  CHARGING     Battery is charging.
    0x02  DISCHARGING  Battery is discharging.
    0x03  NOT_CHARGING Battery is not charging (e.g. full or dead).

  IO_READ(HEALTH) returns one of the following values:

    0x00  UNKNOWN         Battery health unknown.
    0x01  GOOD            Battery is in good condition.
    0x02  OVERHEATING     Battery is over-heating.
    0x03  DEAD            Battery is dead.
    0x04  OVERVOLTAGE     Battery generates too much voltage.
    0x05  UNSPEC_FAILURE  Battery has unspecified failure.

The kernel can use IO_WRITE(INT_ENABLE, <flags>) to select which condition
changes should trigger an IRQ. <flags> is a 2-bit value using the same format
as INT_STATUS.Copy the code

You can think of the device as a function, and registers are some of its input data, some of its return data, and some of its states. Interrupt is a bit like Linux programming of signal (signal), when equipment have data to be read, can receive data, status changes, and so on, can be (and, of course, also can not) to generate an interrupt, interrupt execution of the kernel (CPU hardware interrupt, not operating system scheduler), jump to the interrupt handler (similar to the signal processing function, Signal corresponds to signal handler, interrupt number corresponds to interrupt handler). The specific redirect methods are as follows: When requesting an interrupt using the kernel function request_IRq, enter the interrupt number and the interrupt function. There is a table (array) in memory, called the interrupt vector table, with the interrupt number as the key, with the interrupt function address as value, records the information of the interrupt function. When an interrupt occurs, the CPU can know the interrupt number, and then find the corresponding interrupt handler function through the interrupt direction table, and then jump to the execution. Int irq; void *dev_id; void *dev_id; void *dev_id; void *dev_id; The address of the virtual device register is very small, which can be understood as an offset. Platform Bus is used to obtain the virtual physical address of THE I/O memory, and ioreMap is used to map the virtual physical address to the kernel virtual address, which can be used in the kernel. Note that it can not be used as normal memory, you need to use special readB, writeb, readW, writew, readL, writel, because the hardware register, each time read, return data can be different; If you want to send an array through a register, write to the same register through a loop. The register address is not ++. In addition, there are strict requirements on the order of read and write and the width of operation (8bit, 16bit or 32bit), not random. If you treat it as a normal memory access, the compiler may use the cache, the CPU may execute instructions out of order, and the width may not be correct, so you cannot use it as a normal memory pointer.


‘driver code in the catalogue of goldfish drivers/misc/qemupipe qemu_pipe. C, for the sake of simplicity, cancellation, closed, clean code is not detail.

The driver initialization function is:

static struct platform_driver goldfish_battery_device = {
    .probe      = goldfish_battery_probe,
    .remove     = goldfish_battery_remove,
    .driver = {
        .name = "goldfish-battery"
    }
};

static int __init goldfish_battery_init(void)
{
    return platform_driver_register(&goldfish_battery_device);
}Copy the code



Goldfish_battery_probe initializes the Goldfish_battery_data structure, then uses platform_get_resource to obtain THE I/O memory resources of the device, and ioremap the IORESOURCE_MEM resources. Then save base to data->reg_base; We then use platform_get_irq to get the interrupt number and save it to data->irq and use request_irq to register the interrupt function goldfish_battery_interrupt.

Data ->battery and data-> AC are struct power_supply, such as battery:

    data->battery.properties = goldfish_battery_props;
    data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
    data->battery.get_property = goldfish_battery_get_property;
    data->battery.name = "battery";
    data->battery.type = POWER_SUPPLY_TYPE_BATTERY;Copy the code

After power_supply_register, there are files in /sys/class/power_supply/battery of the guest OS. For example, capacity. Health, status, etc., the reading function is goldfish_battery_get_property, the writing function is not. Guest OS user-space programs read these properties files directly, and the contents of the properties files come from reading registers, for example

 static int goldfish_battery_get_property(struct power_supply *psy,
              enum power_supply_property psp,
              union power_supply_propval *val)
{
 struct goldfish_battery_data *data = container_of(psy,
     struct goldfish_battery_data, battery);
 int ret = 0;

 switch (psp) {
 case POWER_SUPPLY_PROP_STATUS:
     val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
     break;
 case POWER_SUPPLY_PROP_HEALTH:
     val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
        break;
    case POWER_SUPPLY_PROP_PRESENT:
        val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
        break;
    case POWER_SUPPLY_PROP_TECHNOLOGY:
        val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
        break;
    case POWER_SUPPLY_PROP_CAPACITY:
        val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
        break;
    default:
        ret = -EINVAL;
        break;
    }

    return ret;
}Copy the code

In this way, information about the virtual device battery can be obtained.

Finally, GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK) writes BATTERY_INT_MASK to the register BATTERY_INT_ENABLE enables an interrupt. When the status of the battery and ac changes, the virtual device interrupts (this code is in emulator) and our interrupt function goldfish_battery_interrupt is invoked.

The complete Goldfish_battery_probe code is as follows:

static int goldfish_battery_probe(struct platform_device *pdev)
{
    int ret;
    struct resource *r;
    struct goldfish_battery_data *data;

    data = kzalloc(sizeof(*data), GFP_KERNEL);
    if (data == NULL) {
        ret = -ENOMEM;
        goto err_data_alloc_failed;
    }
    spin_lock_init(&data->lock);

    data->battery.properties = goldfish_battery_props;
    data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
    data->battery.get_property = goldfish_battery_get_property;
    data->battery.name = "battery";
    data->battery.type = POWER_SUPPLY_TYPE_BATTERY;

    data->ac.properties = goldfish_ac_props;
    data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props);
    data->ac.get_property = goldfish_ac_get_property;
    data->ac.name = "ac";
    data->ac.type = POWER_SUPPLY_TYPE_MAINS;

    r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (r == NULL) {
        printk(KERN_ERR "%s: platform_get_resource failed\n", pdev->name);
        ret = -ENODEV;
        goto err_no_io_base;
    }
#if defined(CONFIG_ARM)
    data->reg_base = (void __iomem *)IO_ADDRESS(r->start - IO_START);
#elif defined(CONFIG_X86) || defined(CONFIG_MIPS)
    data->reg_base = ioremap(r->start, r->end - r->start + 1);
#else
#error NOT SUPPORTED
#endif

    data->irq = platform_get_irq(pdev, 0);
    if (data->irq < 0) {
        printk(KERN_ERR "%s: platform_get_irq failed\n", pdev->name);
        ret = -ENODEV;
        goto err_no_irq;
    }

    ret = request_irq(data->irq, goldfish_battery_interrupt, IRQF_SHARED, pdev->name, data);
    if (ret)
        goto err_request_irq_failed;

    ret = power_supply_register(&pdev->dev, &data->ac);
    if (ret)
        goto err_ac_failed;

    ret = power_supply_register(&pdev->dev, &data->battery);
    if (ret)
        goto err_battery_failed;

    platform_set_drvdata(pdev, data);
    battery_data = data;

    GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
    return 0;

err_battery_failed:
    power_supply_unregister(&data->ac);
err_ac_failed:
    free_irq(data->irq, data);
err_request_irq_failed:
err_no_irq:
#if defined(CONFIG_ARM)
#elif defined(CONFIG_X86) || defined(CONFIG_MIPS)
    iounmap(data->reg_base);
#else
#error NOT SUPPORTED
#endif
err_no_io_base:
    kfree(data);
err_data_alloc_failed:
    return ret;
}Copy the code





The interrupt function goldfish_battery_interrupt reads the STATUS register to determine whether it is an interrupt event from the battery or ac

    /* read status flags, which will clear the interrupt */
    status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
    status &= BATTERY_INT_MASK;Copy the code

Then call power_supply_CHANGED to notify the kernel.

The full goldfish_battery_interrupt looks like this:

static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
{
    unsigned long irq_flags;
    struct goldfish_battery_data *data = dev_id;
    uint32_t status;

    spin_lock_irqsave(&data->lock, irq_flags);

    /* read status flags, which will clear the interrupt */
    status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
    status &= BATTERY_INT_MASK;

    if (status & BATTERY_STATUS_CHANGED)
        power_supply_changed(&data->battery);
    if (status & AC_STATUS_CHANGED)
        power_supply_changed(&data->ac);

    spin_unlock_irqrestore(&data->lock, irq_flags);
    return status ? IRQ_HANDLED : IRQ_NONE;
}Copy the code


Notice how struct Goldfish_battery_data is passed to the interrupt function and platform_device.

2.2 Driver of Platform Bus

Arch /x86/ Mach-goldfish /pdev_bus.c is a good idea to look at platform bus drivers before looking at virtual devices

Platform Bus documentation:

I. Goldfish platform bus:
=========================

The 'platform bus', in Linux kernel speak, is a special device that is capable
of enumerating other platform devices found on the system to the kernel. This
flexibility allows to customize which virtual devices are available when running
a given emulated system configuration.

Relevant files:
  $QEMU/hw/android/goldfish/device.c
  $KERNEL/arch/arm/mach-goldfish/pdev_bus.c
  $KERNEL/arch/x86/mach-goldfish/pdev_bus.c
  $KERNEL/arch/mips/goldfish/pdev_bus.c

Device properties:
  Name: goldfish_device_bus
  Id:   -1
  IrqCount: 1

  32-bit I/O registers (offset, name, abstract)

    0x00 BUS_OP      R: Iterate to next device in enumeration.
                     W: Start device enumeration.

    0x04 GET_NAME    W: Copy device name to kernel memory.
    0x08 NAME_LEN    R: Read length of current device's name.
    0x0c ID          R: Read id of current device.
    0x10 IO_BASE     R: Read I/O base address of current device.
    0x14 IO_SIZE     R: Read I/O base size of current device.
    0x18 IRQ_BASE    R: Read base IRQ of current device.
    0x1c IRQ_COUNT   R: Read IRQ count of current device.

    # For 64-bit guest architectures only:
    0x20 NAME_ADDR_HIGH  W: Write high 32-bit of kernel address of name
                            buffer used by GET_NAME. Must be written to
                            before the GET_NAME write.

The kernel iterates over the list of current devices with something like:

   IO_WRITE(BUS_OP, 0);    // Start iteration, any value other than 0 is invalid.
   for (;;) {
     int ret = IO_READ(BUS_OP);
     if (ret == 0 /* OP_DONE */) {
       // no more devices.
       break;
     }
     else if (ret == 8 /* OP_ADD_DEV */) {
       // Read device properties.
       Device dev;
       dev.name_len  = IO_READ(NAME_LEN);
       dev.id        = IO_READ(ID);
       dev.io_base   = IO_READ(IO_BASE);
       dev.io_size   = IO_READ(IO_SIZE);
       dev.irq_base  = IO_READ(IRQ_BASE);
       dev.irq_count = IO_READ(IRQ_COUNT);

       dev.name = kalloc(dev.name_len + 1);  // allocate room for device name.
    #if 64BIT_GUEST_CPU
       IO_WRITE(NAME_ADDR_HIGH, (uint32_t)(dev.name >> 32));
    #endif
       IO_WRITE(GET_NAME, (uint32_t)dev.name);  // copy to kernel memory.
       dev.name[dev.name_len] = 0;

       .. add device to kernel's list.
     }
     else {
       // Not returned by current goldfish implementation.
     }
   }

The device also uses a single IRQ, which it will raise to indicate to the kernel
that new devices are available, or that some of them have been removed. The
kernel will then start a new enumeration. The IRQ is lowered by the device only
when a IO_READ(BUS_OP) returns 0 (OP_DONE).

NOTE: The kernel hard-codes a platform_device definition with the name
      "goldfish_pdev_bus" for the platform bus (e.g. see
      $KERNEL/arch/arm/mach-goldfish/board-goldfish.c), however, the bus itself
      will appear during enumeration as a device named "goldfish_device_bus"

      The kernel driver for the platform bus only matches the "goldfish_pdev_bus"
      name, and will ignore any device named "goldfish_device_bus".Copy the code


The first step is to provide IO memory and interrupt number information agreed with emulator to the kernel:

static struct resource goldfish_pdev_bus_resources[] = {
    {
        .start  = GOLDFISH_PDEV_BUS_BASE,
        .end    = GOLDFISH_PDEV_BUS_BASE + GOLDFISH_PDEV_BUS_END - 1,
        .flags  = IORESOURCE_IO,
    },
    {
        .start  = IRQ_PDEV_BUS,
        .end    = IRQ_PDEV_BUS,
        .flags  = IORESOURCE_IRQ,
    }
};

struct platform_device goldfish_pdev_bus_device = {
    .name = "goldfish_pdev_bus",
    .id = -1,
    .num_resources = ARRAY_SIZE(goldfish_pdev_bus_resources),
    .resource = goldfish_pdev_bus_resources
};
static int __init goldfish_init(void)
{
    return platform_device_register(&goldfish_pdev_bus_device);
}
device_initcall(goldfish_init);Copy the code


static struct platform_driver goldfish_pdev_bus_driver = {
    .probe = goldfish_pdev_bus_probe,
    .remove = __devexit_p(goldfish_pdev_bus_remove),
    .driver = {
        .name = "goldfish_pdev_bus"
    }
};

static int __init goldfish_pdev_bus_init(void)
{
    return platform_driver_register(&goldfish_pdev_bus_driver);
}

static void __exit goldfish_pdev_bus_exit(void)
{
    platform_driver_unregister(&goldfish_pdev_bus_driver);
}

module_init(goldfish_pdev_bus_init);
module_exit(goldfish_pdev_bus_exit);Copy the code


static int __devinit goldfish_pdev_bus_probe(struct platform_device *pdev)
{
    int ret;
    struct resource *r;
    r = platform_get_resource(pdev, IORESOURCE_IO, 0);
    if(r == NULL)
        return -EINVAL;
    pdev_bus_base = ioremap(GOLDFISH_IO_START + r->start, GOLDFISH_IO_SIZE);

    r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if(r == NULL)
        return -EINVAL;
    pdev_bus_irq = r->start;

    ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt, IRQF_SHARED, "goldfish_pdev_bus", );
    if(ret)
        goto err_request_irq_failed;

    writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP);

err_request_irq_failed:
    return ret;
}Copy the code


static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id) { irqreturn_t ret = IRQ_NONE; while(1) { uint32_t op = readl(pdev_bus_base + PDEV_BUS_OP); switch(op) { case PDEV_BUS_OP_DONE: return IRQ_NONE; case PDEV_BUS_OP_REMOVE_DEV: goldfish_pdev_remove(); break; case PDEV_BUS_OP_ADD_DEV: goldfish_new_pdev(); break; } ret = IRQ_HANDLED; }}Copy the code

Finally, the schedule_work(&pdev_bus_worker) function is called. Goldfish_pdev_worker is a worker, similar to a tasklet, that is registered to run at a later point in time without consuming the interrupt context. This function is mainly used to update three linked lists: newly added devices, deleted devices, registered devices.

static int goldfish_new_pdev(void)
{
    struct pdev_bus_dev *dev;
    uint32_t name_len;
    uint32_t irq = -1, irq_count;
    int resource_count = 2;
    uint32_t base;
    char *name;

    base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);

    irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT);
    name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN);
    if(irq_count)
        resource_count++;

    dev = kzalloc(sizeof(*dev) + sizeof(struct resource) * resource_count + name_len + 1, GFP_ATOMIC);
    if(dev == NULL)
        return -ENOMEM;

    dev->pdev.num_resources = resource_count;
    dev->pdev.resource = (struct resource *)(dev + 1);
    dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count);
    dev->pdev.dev.coherent_dma_mask = ~0;

    writel((unsigned long)name, pdev_bus_base + PDEV_BUS_GET_NAME);
    name[name_len] = '\0';
    dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID);
    dev->pdev.resource[0].start = base;
    dev->pdev.resource[0].end = base + readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1;
    dev->pdev.resource[0].flags = IORESOURCE_MEM;
    if(irq_count) {
        irq = readl(pdev_bus_base + PDEV_BUS_IRQ);
        dev->pdev.resource[1].start = irq;
        dev->pdev.resource[1].end = irq + irq_count - 1;
        dev->pdev.resource[1].flags = IORESOURCE_IRQ;
    }

    printk("goldfish_new_pdev %s at %x irq %d\n", name, base, irq);
    list_add_tail(&dev->list, &pdev_bus_new_devices);
    schedule_work(&pdev_bus_worker);

    return 0;
}Copy the code
static void goldfish_pdev_worker(struct work_struct *work) { int ret; struct pdev_bus_dev *pos, *n; list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) { list_del(&pos->list); platform_device_unregister(&pos->pdev); kfree(pos); } list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) { list_del(&pos->list); ret = platform_device_register(&pos->pdev); if(ret) { printk("goldfish_pdev_worker failed to register device, %s\n", pos->pdev.name); } else { printk("goldfish_pdev_worker registered %s\n", pos->pdev.name); } list_add_tail(&pos->list, &pdev_bus_registered_devices); }}Copy the code








Virtual devices in emulator

3.1 Battery Virtual device

The code of the battery virtual device is androidxref.com/5.1.0_r1/xr…

First, the register of the virtual device defines the address of the register. Then, the structure goldfish_battery_state is used to store the register information. When reading or writing to this structure, it is the read/write register and is used to simulate the register.

enum { /* status register */ BATTERY_INT_STATUS = 0x00, /* set this to enable IRQ */ BATTERY_INT_ENABLE = 0x04, BATTERY_AC_ONLINE = 0x08, BATTERY_STATUS = 0x0C, BATTERY_HEALTH = 0x10, BATTERY_PRESENT = 0x14, BATTERY_CAPACITY = 0x18, BATTERY_STATUS_CHANGED = 1U << 0, AC_STATUS_CHANGED = 1U << 1, BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, }; struct goldfish_battery_state { struct goldfish_device dev; // IRQs uint32_t int_status; // irq enable mask for int_status uint32_t int_enable; int ac_online; int status; int health; int present; int capacity; // the fields below are part of the device configuration // and don't need to be saved to / restored from snapshots. int  hw_has_battery; };Copy the code



/* update this each time you update the battery_state struct */ #define BATTERY_STATE_SAVE_VERSION 1 #define QFIELD_STRUCT struct goldfish_battery_state QFIELD_BEGIN(goldfish_battery_fields) QFIELD_INT32(int_status), QFIELD_INT32(int_enable), QFIELD_INT32(ac_online), QFIELD_INT32(status), QFIELD_INT32(health), QFIELD_INT32(present), QFIELD_INT32(capacity), QFIELD_END static void goldfish_battery_save(QEMUFile* f, void* opaque) { struct goldfish_battery_state* s = opaque; qemu_put_struct(f, goldfish_battery_fields, s); } static int goldfish_battery_load(QEMUFile* f, void* opaque, int version_id) { struct goldfish_battery_state* s = opaque; if (version_id ! = BATTERY_STATE_SAVE_VERSION) return -1; return qemu_get_struct(f, goldfish_battery_fields, s); }Copy the code

void goldfish_battery_init(int has_battery)
{
    struct goldfish_battery_state *s;

    s = (struct goldfish_battery_state *)g_malloc0(sizeof(*s));
    s->dev.name = "goldfish-battery";
    s->dev.base = 0;    // will be allocated dynamically
    s->dev.size = 0x1000;
    s->dev.irq_count = 1;

    // default values for the battery
    s->ac_online = 1;
    s->hw_has_battery = has_battery;
    if (has_battery) {
        s->status = POWER_SUPPLY_STATUS_CHARGING;
        s->health = POWER_SUPPLY_HEALTH_GOOD;
        s->present = 1;     // battery is present
        s->capacity = 50;   // 50% charged
    } else {
        s->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
        s->health = POWER_SUPPLY_HEALTH_DEAD;
        s->present = 0;
        s->capacity = 0;
    }

    battery_state = s;

    goldfish_device_add(&s->dev, goldfish_battery_readfn, goldfish_battery_writefn, s);

    register_savevm(NULL,
                    "battery_state",
                    0,
                    BATTERY_STATE_SAVE_VERSION,
                    goldfish_battery_save,
                    goldfish_battery_load,
                    s);
}Copy the code

Note that after reading BATTERY_INT_STATUS, if there is an interrupt flag, it is cleared, because the program has already read that there is a new interrupt event and there is no need to trigger another interrupt.

static uint32_t goldfish_battery_read(void *opaque, hwaddr offset) { uint32_t ret; struct goldfish_battery_state *s = opaque; switch(offset) { case BATTERY_INT_STATUS: // return current buffer status flags ret = s->int_status & s->int_enable; if (ret) { goldfish_device_set_irq(&s->dev, 0, 0); s->int_status = 0; } return ret; case BATTERY_INT_ENABLE: return s->int_enable; case BATTERY_AC_ONLINE: return s->ac_online; case BATTERY_STATUS: return s->status; case BATTERY_HEALTH: return s->health; case BATTERY_PRESENT: return s->present; case BATTERY_CAPACITY: return s->capacity; default: cpu_abort (cpu_single_env, "goldfish_battery_read: Bad offset %x\n", offset); return 0; } } static void goldfish_battery_write(void *opaque, hwaddr offset, uint32_t val) { struct goldfish_battery_state *s = opaque; switch(offset) { case BATTERY_INT_ENABLE: /* enable interrupts */ s->int_enable = val; // s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY); // goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable)); break; default: cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset); }}Copy the code

static CPUReadMemoryFunc *goldfish_battery_readfn[] = {
    goldfish_battery_read,
    goldfish_battery_read,
    goldfish_battery_read
};


static CPUWriteMemoryFunc *goldfish_battery_writefn[] = {
    goldfish_battery_write,
    goldfish_battery_write,
    goldfish_battery_write
};Copy the code


3.2 platform Bus Virtual devices

The code of the platform Bus virtual device is androidxref.com/5.1.0_r1/xr…

Note that the Platform Bus is itself a device and is also in the device list.

Base, size, irq, and irq_count specified in goldfish_device_init and goldfish_device_bus_init are fixed, corresponding to code in the kernel.

static struct bus_state bus_state = {
    .dev = {
        .name = "goldfish_device_bus",
        .id = -1,
        .base = 0x10001000,
        .size = 0x1000,
        .irq = 1,
        .irq_count = 1,
    }
};

void goldfish_device_init(qemu_irq *pic, uint32_t base, uint32_t size, uint32_t irq, uint32_t irq_count)
{
    goldfish_pic = pic;
    goldfish_free_base = base;
    goldfish_free_irq = irq;
}

int goldfish_device_bus_init(uint32_t base, uint32_t irq)
{
    bus_state.dev.base = base;
    bus_state.dev.irq = irq;

    return goldfish_device_add(&bus_state.dev, goldfish_bus_readfn, goldfish_bus_writefn, &bus_state);
}
Copy the code

static void goldfish_bus_write(void *opaque, hwaddr offset, uint32_t value) { struct bus_state *s = (struct bus_state *)opaque; switch(offset) { case PDEV_BUS_OP: switch(value) { case PDEV_BUS_OP_INIT: goldfish_bus_op_init(s); break; default: cpu_abort (cpu_single_env, "goldfish_bus_write: Bad PDEV_BUS_OP value %x\n", value); }; break; case PDEV_BUS_GET_NAME: if(s->current) { target_ulong name = (target_ulong)(s->name_addr_high | value); safe_memory_rw_debug(current_cpu, name, (void*)s->current->name, strlen(s->current->name), 1); } break; case PDEV_BUS_NAME_ADDR_HIGH: s->name_addr_high = ((uint64_t)value << 32); goldfish_64bit_guest = 1; break; default: cpu_abort (cpu_single_env, "goldfish_bus_write: Bad offset %x\n", offset); } } static void goldfish_bus_op_init(struct bus_state *s) { struct goldfish_device *dev = first_device; while(dev) { dev->reported_state = 0; dev = dev->next; } s->current = NULL; goldfish_device_set_irq(&s->dev, 0, first_device ! = NULL); }Copy the code


static uint32_t goldfish_bus_read(void *opaque, hwaddr offset) { struct bus_state *s = (struct bus_state *)opaque; switch (offset) { case PDEV_BUS_OP: if(s->current) { s->current->reported_state = 1; s->current = s->current->next; } else { s->current = first_device; } while(s->current && s->current->reported_state == 1) s->current = s->current->next; if(s->current) return PDEV_BUS_OP_ADD_DEV; else { goldfish_device_set_irq(&s->dev, 0, 0); return PDEV_BUS_OP_DONE; } case PDEV_BUS_NAME_LEN: return s->current ? strlen(s->current->name) : 0; case PDEV_BUS_ID: return s->current ? s->current->id : 0; case PDEV_BUS_IO_BASE: return s->current ? s->current->base : 0; case PDEV_BUS_IO_SIZE: return s->current ? s->current->size : 0; case PDEV_BUS_IRQ: return s->current ? s->current->irq : 0; case PDEV_BUS_IRQ_COUNT: return s->current ? s->current->irq_count : 0; default: cpu_abort (cpu_single_env, "goldfish_bus_read: Bad offset %x\n", offset); return 0; }}Copy the code

The classic interrupt controller used by x86 is 8258A(document). In the emulator, a virtual 8259A(code) is used instead of the 8259A on the computer because of the hardware 8259A, the Emulator cannot trigger its interrupt request. The interrupt related initialization code is: androidxref.com/5.1.0_r1/xr… A maximum of 15 virtual interrupts, two 8259A connections, are connected from the chip to the main chip’s IRQ2 (IRQ from 0 to 7 for every chip). Dev is a concrete virtual device; Irq is the interrupt number of each virtual device. If the virtual device has only one interrupt, the IRQ here is 0. If the virtual device has two interrupts, the IRQ here can be 0 or 1. A level of 1 generates interrupts and a level of 0 cancels interrupts (not disallows interrupts, just cancels interrupt requests). Goldfish_device_set_irq calls the qemu_set_irq function, which eventually sets the bit in the IRR(interrupt Request register) register in virtual 8259A that corresponds to the interrupt number for setting the virtual device (androidxref.com/5.1.0_r1/xr…). To fire the interrupt event, and then the interrupt function in the kernel code will be executed (after firing the interrupt, the CPU gets the interrupt number, looks up the interrupt direction table, and jumps to the interrupt handler function to execute).

void goldfish_device_set_irq(struct goldfish_device *dev, int irq, int level)
{
    if(irq >= dev->irq_count)
        cpu_abort (cpu_single_env, "goldfish_device_set_irq: Bad irq %d >= %d\n", irq, dev->irq_count);
    else
        qemu_set_irq(goldfish_pic[dev->irq + irq], level);
}Copy the code

3.3. The soul of virtual devices goldfish_device_add

This important function, of course, only a few lines long, calls other functions. Goldfish_add_device_no_io allocates I/O memory and interrupt number to the new device based on the current idle I/O memory address and interrupt number. Cpu_register_io_memory maintains three arrays of read functions, three arrays of write functions, and an array of virtual device register structures. The array subscript is IO_index, which is dynamically allocated. Note that several IO_indexes are reserved. Cpu_register_physical_memory distributed virtual physical memory page and io_index < < 3 | subwidth phys_offset PhysPageDesc structure information stored in the page.

int goldfish_device_add(struct goldfish_device *dev,
                       CPUReadMemoryFunc **mem_read,
                       CPUWriteMemoryFunc **mem_write,
                       void *opaque)
{
    int iomemtype;
    goldfish_add_device_no_io(dev);
    iomemtype = cpu_register_io_memory(mem_read, mem_write, opaque);
    cpu_register_physical_memory(dev->base, dev->size, iomemtype);
    return 0;
}Copy the code



The function that dynamically allocates IO memory and interrupt numbers for virtual devices is goldfish_add_device_no_io. Note that several interrupt numbers are reserved on x86.

int goldfish_add_device_no_io(struct goldfish_device *dev)
{
    if(dev->base == 0) {
        dev->base = goldfish_free_base;
        goldfish_free_base += dev->size;
    }
    if(dev->irq == 0 && dev->irq_count > 0) {
        dev->irq = goldfish_free_irq;
        goldfish_free_irq += dev->irq_count;
#ifdef TARGET_I386
        /* Make sure that we pass by the reserved IRQs. */
        while (goldfish_free_irq == GFD_KBD_IRQ ||
               goldfish_free_irq == GFD_RTC_IRQ ||
               goldfish_free_irq == GFD_MOUSE_IRQ ||
               goldfish_free_irq == GFD_ERR_IRQ) {
            goldfish_free_irq++;
        }
#endif
        if (goldfish_free_irq >= GFD_MAX_IRQ) {
            derror("Goldfish device has exceeded available IRQ number.");
            exit(1);
        }
    }
    //printf("goldfish_add_device: %s, base %x %x, irq %d %d\n",
    //       dev->name, dev->base, dev->size, dev->irq, dev->irq_count);
    dev->next = NULL;
    if(last_device) {
        last_device->next = dev;
    }
    else {
        first_device = dev;
    }
    last_device = dev;
    return 0;
}Copy the code



The cpu_register_io_memory function is used to manipulate the three arrays. Note that io_index is allocated dynamically. Each virtual device has an IO_index. Note that the maximum value of io_index is IO_MEM_NB_ENTRIES:

/* MMIO pages are identified by a combination of an IO device index and
   3 flags.  The ROMD code stores the page ram offset in iotlb entry,
   so only a limited number of ids are avaiable.  */

#define IO_MEM_NB_ENTRIES  (1 << (TARGET_PAGE_BITS  - IO_MEM_SHIFT))Copy the code

The return value of a function is io_index < < 3 | subwidth, subwidth mark three read and write functions to see if there is NULL.

When the io_index and register (offset) are known, the virtual device’s own read-write function can be called to read and write the register structure and simulate the device. Io_index = io_index = io_index = io_index = io_index = io_index

/* mem_read and mem_write are arrays of functions containing the function to access byte (index 0), word (index 1) and dword (index 2). Functions can be omitted with a NULL function pointer. If io_index is non zero, the corresponding io zone is modified. If it is zero, a new io zone is allocated. The return value can be used with cpu_register_physical_memory(). (-1) is returned if error.  */ static int cpu_register_io_memory_fixed(int io_index, CPUReadMemoryFunc * const *mem_read, CPUWriteMemoryFunc * const *mem_write, void *opaque) { int i, subwidth = 0; if (io_index <= 0) { io_index = get_free_io_mem_idx(); if (io_index == -1) return io_index; } else { io_index >>= IO_MEM_SHIFT; if (io_index >= IO_MEM_NB_ENTRIES) return -1; } for(i = 0; i < 3; i++) { if (! mem_read[i] || ! mem_write[i]) subwidth = IO_MEM_SUBWIDTH; _io_mem_read[io_index][i] = mem_read[i]; _io_mem_write[io_index][i] = mem_write[i]; } io_mem_opaque[io_index] = opaque; return (io_index << IO_MEM_SHIFT) | subwidth; } int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read, CPUWriteMemoryFunc * const *mem_write, void *opaque) { return cpu_register_io_memory_fixed(0, mem_read, mem_write, opaque); }Copy the code








The third function cpu_register_physical_memory distribution virtual physical memory, and will be io_index < < 3 | subwidth stored in the phys_offset PhysPageDesc structure.

The code for physical memory management is complex, just understand that ordinary RAM is allocated per page, and phys_offset = 0, which means ordinary RAM; IO memory is allocated according to the page, and phys_offset is just io_index < < 3 | subwidth, if IO memory accounted for more than one page, then each page phys_offset is the same (region_offset different), You can find the same IO_index.

Here are some macro definitions. Note that IO_MEM_ROM, IO_MEM_UNASSIGNED, and IO_MEM_NOTDIRTY are the io_indexes reserved for get_free_IO_mem_IDx.

#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) #define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1) #define IO_MEM_SHIFT 3  #define IO_MEM_RAM (0 << IO_MEM_SHIFT) /* hardcoded offset */ #define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */ #define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT) #define IO_MEM_NOTDIRTY (3 << IO_MEM_SHIFT) /* Acts like a ROM when read and like a device when written. */ #define IO_MEM_ROMD (1) #define IO_MEM_SUBPAGE (2) #define IO_MEM_SUBWIDTH (4)Copy the code

static inline void cpu_register_physical_memory(hwaddr start_addr, ram_addr_t size, ram_addr_t phys_offset) { cpu_register_physical_memory_offset(start_addr, size, phys_offset, 0); } static inline void cpu_register_physical_memory_offset(hwaddr start_addr, ram_addr_t size, ram_addr_t phys_offset, ram_addr_t region_offset) { cpu_register_physical_memory_log(start_addr, size, phys_offset, region_offset, false); } void cpu_register_physical_memory_log(hwaddr start_addr, ram_addr_t size, ram_addr_t phys_offset, ram_addr_t region_offset, bool log_dirty) { hwaddr addr, end_addr; PhysPageDesc *p; CPUState *cpu; ram_addr_t orig_size = size; subpage_t *subpage; if (kvm_enabled()) kvm_set_phys_mem(start_addr, size, phys_offset); #ifdef CONFIG_HAX if (hax_enabled()) hax_set_phys_mem(start_addr, size, phys_offset); #endif if (phys_offset == IO_MEM_UNASSIGNED) { region_offset = start_addr; } region_offset &= TARGET_PAGE_MASK; size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; end_addr = start_addr + (hwaddr)size; addr = start_addr; do { p = phys_page_find(addr >> TARGET_PAGE_BITS); if (p && p->phys_offset ! = IO_MEM_UNASSIGNED) { ram_addr_t orig_memory = p->phys_offset; hwaddr start_addr2, end_addr2; int need_subpage = 0; CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, need_subpage); if (need_subpage) { if (! (orig_memory & IO_MEM_SUBPAGE)) { subpage = subpage_init((addr & TARGET_PAGE_MASK), &p->phys_offset, orig_memory, p->region_offset); } else { subpage = io_mem_opaque[(orig_memory & ~TARGET_PAGE_MASK) >> IO_MEM_SHIFT]; } subpage_register(subpage, start_addr2, end_addr2, phys_offset, region_offset); p->region_offset = 0; } else { p->phys_offset = phys_offset; if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || (phys_offset & IO_MEM_ROMD)) phys_offset += TARGET_PAGE_SIZE; } } else { p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); p->phys_offset = phys_offset; p->region_offset = region_offset; if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || (phys_offset & IO_MEM_ROMD)) { phys_offset += TARGET_PAGE_SIZE; } else { hwaddr start_addr2, end_addr2; int need_subpage = 0; CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, need_subpage); if (need_subpage) { subpage = subpage_init((addr & TARGET_PAGE_MASK), &p->phys_offset, IO_MEM_UNASSIGNED, addr & TARGET_PAGE_MASK); subpage_register(subpage, start_addr2, end_addr2, phys_offset, region_offset); p->region_offset = 0; } } } region_offset += TARGET_PAGE_SIZE; addr += TARGET_PAGE_SIZE; } while (addr ! = end_addr); /* since each CPU stores ram addresses in its TLB cache, we must reset the modified entries */ /* XXX: slow ! */ CPU_FOREACH(cpu) { tlb_flush(cpu->env_ptr, 1); }}Copy the code





If you use KVM acceleration, when reading or writing MMIO, exit:

         case KVM_EXIT_MMIO:
             dprintf("handle_mmio\n");
             cpu_physical_memory_rw(run->mmio.phys_addr,
                                    run->mmio.data,
                                    run->mmio.len,
                                    run->mmio.is_write);
             ret = 1;
             break;Copy the code

The cpu_physical_memory_rw function will be called, check whether it is MMIO, if so, get io_index, The io_mem_write(io_index, addr1, val, XXX) and io_mem_read(io_index, addr1, XXX) functions are then called according to different access widths (8bit, 16bit, 32bit). These two functions are wrappers around the three arrays maintained by CPU_register_io_memory. In this way, the read/write function of the virtual device corresponding to the register and the register structure and offset can be used to simulate the read/write of the register.

Haxm and TCG work in a similar way.

void cpu_physical_memory_rw(hwaddr addr, void *buf, int len, int is_write) { int l, io_index; uint8_t *ptr; uint32_t val; hwaddr page; ram_addr_t pd; uint8_t* buf8 = (uint8_t*)buf; PhysPageDesc *p; while (len > 0) { page = addr & TARGET_PAGE_MASK; l = (page + TARGET_PAGE_SIZE) - addr; if (l > len) l = len; p = phys_page_find(page >> TARGET_PAGE_BITS); if (! p) { pd = IO_MEM_UNASSIGNED; } else { pd = p->phys_offset; } if (is_write) { if ((pd & ~TARGET_PAGE_MASK) ! = IO_MEM_RAM) { hwaddr addr1 = addr; io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (p) addr1 = (addr & ~TARGET_PAGE_MASK) + p->region_offset; /* XXX: could force cpu_single_env to NULL to avoid potential bugs */ if (l >= 4 && ((addr1 & 3) == 0)) { /* 32 bit write access  */ val = ldl_p(buf8); io_mem_write(io_index, addr1, val, 4); l = 4; } else if (l >= 2 && ((addr1 & 1) == 0)) { /* 16 bit write access */ val = lduw_p(buf8); io_mem_write(io_index, addr1, val, 2); l = 2; } else { /* 8 bit write access */ val = ldub_p(buf8); io_mem_write(io_index, addr1, val, 1); l = 1; } } else { ram_addr_t addr1; addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); /* RAM case */ ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf8, l); invalidate_and_set_dirty(addr1, l); } } else { if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && ! (pd & IO_MEM_ROMD)) { hwaddr addr1 = addr; /* I/O case */ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (p) addr1 = (addr & ~TARGET_PAGE_MASK) + p->region_offset; if (l >= 4 && ((addr1 & 3) == 0)) { /* 32 bit read access */ val = io_mem_read(io_index, addr1, 4); stl_p(buf8, val); l = 4; } else if (l >= 2 && ((addr1 & 1) == 0)) { /* 16 bit read access */ val = io_mem_read(io_index, addr1, 2); stw_p(buf8, val); l = 2; } else { /* 8 bit read access */ val = io_mem_read(io_index, addr1, 1); stb_p(buf8, val); l = 1; } } else { /* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); memcpy(buf8, ptr, l); } } len -= l; buf8 += l; addr += l; }}Copy the code





References:

Driver preparation can be seen: LINUX device driver (version 3)

Hardware knowledge, you can see guo Tianxiang 51 MCU video

Don’t look at Tan Ho-keung