target-arm queue:
* support variable (runtime-determined) page sizes, for a nearly-20% speedup of TCG for ARMv7 and v8 CPUs with 4K pages * ptimer: add tests, support more flexible behaviour around what happens on the "zero" tick, use ptimer for a9gtimer * virt: ACPI: Add IORT Structure definition * i2c: Fix SMBus read transactions to avoid double events * timer: stm32f2xx_timer: add check for prescaler value * QOMify musicpal, pxa2xx_gpio, strongarm, pl110 * target-arm: Implement new HLT trap for semihosting * i2c: Add asserts for second smbus i2c_start_transfer() -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJYDkPBAAoJEDwlJe0UNgzeUgYP/1coSoKGgxsKkLp8KRZSinoP k8J/GQCvPi7Ha1ntonPQ2vSrs11EdGlNaQWQo8Iq1KVnWx0K+fJJfhtqNXnzk7gN 6xOhR+c3vZhCCoPkTJgY5yMF7LsMwOgYLmuk5hzxGHb7IRteHCaUCG5mCQBOOK39 kIuXu0UlYo/cPYcXLZkHpRIQAHWWwXvfthAWHoYvdVFMmBUnneFZgPlFOYCAG7X6 L9sEbpH5ZG2ttUXTYGtrBw1WfkVrKY9rwG2xZqu3yqQfGMDQFwHP1Q4pB7Z+SpoE H6mI7DBUnvHjcscg/H6/LUmMfJ3pL8qS7NtGz1AP9ArU2/Zk+MO7YJEcRBiYwtXY Z/LbYTyU7Ellrd5t2hAe1zxDiGh3TcRRCVuFZdyRhisyloKbq+/WmAcsTkuO5NRd p1hHBoPvwYEhEHXcoeq5uOGtwxrr4dl736wQ4vEhNyzbCfpcytEePjbxNdE44iVM VTcmsI0S6aAZxDv8a07dPn4BlFVXt/nzuhXFFDnealjVqOD5Pe4aG9oFTgHsJkdR bt1Z3gmjH6jCmUTTaMVqY696u8NoRxJdk7OMBLMHYmKuLzGJ70pqy2kinYOZXqxW yLziS++qTlZwzHqVxDNsnLiRoRn2Jo1kb/hoBIkvJ+TgoxqfDQjGBvVQLHHjbu/Z FKOCN6lnBNL41R4JigR7 =nDCf -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20161024' into staging target-arm queue: * support variable (runtime-determined) page sizes, for a nearly-20% speedup of TCG for ARMv7 and v8 CPUs with 4K pages * ptimer: add tests, support more flexible behaviour around what happens on the "zero" tick, use ptimer for a9gtimer * virt: ACPI: Add IORT Structure definition * i2c: Fix SMBus read transactions to avoid double events * timer: stm32f2xx_timer: add check for prescaler value * QOMify musicpal, pxa2xx_gpio, strongarm, pl110 * target-arm: Implement new HLT trap for semihosting * i2c: Add asserts for second smbus i2c_start_transfer() # gpg: Signature made Mon 24 Oct 2016 18:24:17 BST # gpg: using RSA key 0x3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20161024: (32 commits) i2c: Add asserts for second smbus i2c_start_transfer() target-arm: Implement new HLT trap for semihosting hw/display: QOM'ify pl110.c hw/arm: QOM'ify strongarm.c hw/arm: QOM'ify pxa2xx_gpio.c hw/arm: QOM'ify musicpal.c timer: stm32f2xx_timer: add check for prescaler value i2c: Fix SMBus read transactions to avoid double events timer: a9gtimer: remove loop to auto-increment comparator ARM: Virt: ACPI: Build an IORT table with RC and ITS nodes ACPI: Add IORT Structure definition tests: Add tests for the ARM MPTimer arm_mptimer: Convert to use ptimer tests: ptimer: Replace 10000 with 1 tests: ptimer: Change the copyright comment tests: ptimer: Add tests for "no counter round down" policy hw/ptimer: Add "no counter round down" policy tests: ptimer: Add tests for "no immediate reload" policy hw/ptimer: Add "no immediate reload" policy tests: ptimer: Add tests for "no immediate trigger" policy ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
fe4c04071f
47
exec.c
47
exec.c
@ -93,6 +93,11 @@ static MemoryRegion io_mem_unassigned;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_PAGE_BITS_VARY
|
||||
int target_page_bits;
|
||||
bool target_page_bits_decided;
|
||||
#endif
|
||||
|
||||
struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
|
||||
/* current CPU in the current thread. It is only valid inside
|
||||
cpu_exec() */
|
||||
@ -102,8 +107,37 @@ __thread CPUState *current_cpu;
|
||||
2 = Adaptive rate instruction counting. */
|
||||
int use_icount;
|
||||
|
||||
bool set_preferred_target_page_bits(int bits)
|
||||
{
|
||||
/* The target page size is the lowest common denominator for all
|
||||
* the CPUs in the system, so we can only make it smaller, never
|
||||
* larger. And we can't make it smaller once we've committed to
|
||||
* a particular size.
|
||||
*/
|
||||
#ifdef TARGET_PAGE_BITS_VARY
|
||||
assert(bits >= TARGET_PAGE_BITS_MIN);
|
||||
if (target_page_bits == 0 || target_page_bits > bits) {
|
||||
if (target_page_bits_decided) {
|
||||
return false;
|
||||
}
|
||||
target_page_bits = bits;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
static void finalize_target_page_bits(void)
|
||||
{
|
||||
#ifdef TARGET_PAGE_BITS_VARY
|
||||
if (target_page_bits == 0) {
|
||||
target_page_bits = TARGET_PAGE_BITS_MIN;
|
||||
}
|
||||
target_page_bits_decided = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef struct PhysPageEntry PhysPageEntry;
|
||||
|
||||
struct PhysPageEntry {
|
||||
@ -153,7 +187,7 @@ typedef struct subpage_t {
|
||||
MemoryRegion iomem;
|
||||
AddressSpace *as;
|
||||
hwaddr base;
|
||||
uint16_t sub_section[TARGET_PAGE_SIZE];
|
||||
uint16_t sub_section[];
|
||||
} subpage_t;
|
||||
|
||||
#define PHYS_SECTION_UNASSIGNED 0
|
||||
@ -2215,8 +2249,7 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base)
|
||||
{
|
||||
subpage_t *mmio;
|
||||
|
||||
mmio = g_malloc0(sizeof(subpage_t));
|
||||
|
||||
mmio = g_malloc0(sizeof(subpage_t) + TARGET_PAGE_SIZE * sizeof(uint16_t));
|
||||
mmio->as = as;
|
||||
mmio->base = base;
|
||||
memory_region_init_io(&mmio->iomem, NULL, &subpage_ops, mmio,
|
||||
@ -2808,6 +2841,14 @@ void cpu_register_map_client(QEMUBH *bh)
|
||||
void cpu_exec_init_all(void)
|
||||
{
|
||||
qemu_mutex_init(&ram_list.mutex);
|
||||
/* The data structures we set up here depend on knowing the page size,
|
||||
* so no more changes can be made after this point.
|
||||
* In an ideal world, nothing we did before we had finished the
|
||||
* machine setup would care about the target page size, and we could
|
||||
* do this much later, rather than requiring board models to state
|
||||
* up front what their requirements are.
|
||||
*/
|
||||
finalize_target_page_bits();
|
||||
io_mem_init();
|
||||
memory_map_init();
|
||||
qemu_mutex_init(&map_client_list_lock);
|
||||
|
@ -384,18 +384,24 @@ static NetClientInfo net_mv88w8618_info = {
|
||||
.cleanup = eth_cleanup,
|
||||
};
|
||||
|
||||
static int mv88w8618_eth_init(SysBusDevice *sbd)
|
||||
static void mv88w8618_eth_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
|
||||
object_get_typename(OBJECT(dev)), dev->id, s);
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s,
|
||||
memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
|
||||
"mv88w8618-eth", MP_ETH_SIZE);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
|
||||
|
||||
s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
|
||||
object_get_typename(OBJECT(dev)), dev->id, s);
|
||||
}
|
||||
|
||||
static const VMStateDescription mv88w8618_eth_vmsd = {
|
||||
@ -423,17 +429,17 @@ static Property mv88w8618_eth_properties[] = {
|
||||
static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = mv88w8618_eth_init;
|
||||
dc->vmsd = &mv88w8618_eth_vmsd;
|
||||
dc->props = mv88w8618_eth_properties;
|
||||
dc->realize = mv88w8618_eth_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo mv88w8618_eth_info = {
|
||||
.name = TYPE_MV88W8618_ETH,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(mv88w8618_eth_state),
|
||||
.instance_init = mv88w8618_eth_init,
|
||||
.class_init = mv88w8618_eth_class_init,
|
||||
};
|
||||
|
||||
@ -615,23 +621,26 @@ static const GraphicHwOps musicpal_gfx_ops = {
|
||||
.gfx_update = lcd_refresh,
|
||||
};
|
||||
|
||||
static int musicpal_lcd_init(SysBusDevice *sbd)
|
||||
static void musicpal_lcd_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
musicpal_lcd_state *s = MUSICPAL_LCD(dev);
|
||||
s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
|
||||
qemu_console_resize(s->con, 128 * 3, 64 * 3);
|
||||
}
|
||||
|
||||
static void musicpal_lcd_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
musicpal_lcd_state *s = MUSICPAL_LCD(dev);
|
||||
|
||||
s->brightness = 7;
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s,
|
||||
memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s,
|
||||
"musicpal-lcd", MP_LCD_SIZE);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
|
||||
qemu_console_resize(s->con, 128*3, 64*3);
|
||||
|
||||
qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription musicpal_lcd_vmsd = {
|
||||
@ -652,16 +661,16 @@ static const VMStateDescription musicpal_lcd_vmsd = {
|
||||
static void musicpal_lcd_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = musicpal_lcd_init;
|
||||
dc->vmsd = &musicpal_lcd_vmsd;
|
||||
dc->realize = musicpal_lcd_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo musicpal_lcd_info = {
|
||||
.name = TYPE_MUSICPAL_LCD,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(musicpal_lcd_state),
|
||||
.instance_init = musicpal_lcd_init,
|
||||
.class_init = musicpal_lcd_class_init,
|
||||
};
|
||||
|
||||
@ -748,16 +757,16 @@ static const MemoryRegionOps mv88w8618_pic_ops = {
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int mv88w8618_pic_init(SysBusDevice *dev)
|
||||
static void mv88w8618_pic_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
mv88w8618_pic_state *s = MV88W8618_PIC(dev);
|
||||
|
||||
qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32);
|
||||
sysbus_init_irq(dev, &s->parent_irq);
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s,
|
||||
memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s,
|
||||
"musicpal-pic", MP_PIC_SIZE);
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription mv88w8618_pic_vmsd = {
|
||||
@ -774,9 +783,7 @@ static const VMStateDescription mv88w8618_pic_vmsd = {
|
||||
static void mv88w8618_pic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = mv88w8618_pic_init;
|
||||
dc->reset = mv88w8618_pic_reset;
|
||||
dc->vmsd = &mv88w8618_pic_vmsd;
|
||||
}
|
||||
@ -785,6 +792,7 @@ static const TypeInfo mv88w8618_pic_info = {
|
||||
.name = TYPE_MV88W8618_PIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(mv88w8618_pic_state),
|
||||
.instance_init = mv88w8618_pic_init,
|
||||
.class_init = mv88w8618_pic_class_init,
|
||||
};
|
||||
|
||||
@ -913,8 +921,9 @@ static const MemoryRegionOps mv88w8618_pit_ops = {
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int mv88w8618_pit_init(SysBusDevice *dev)
|
||||
static void mv88w8618_pit_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
mv88w8618_pit_state *s = MV88W8618_PIT(dev);
|
||||
int i;
|
||||
|
||||
@ -924,10 +933,9 @@ static int mv88w8618_pit_init(SysBusDevice *dev)
|
||||
mv88w8618_timer_init(dev, &s->timer[i], 1000000);
|
||||
}
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s,
|
||||
memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s,
|
||||
"musicpal-pit", MP_PIT_SIZE);
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription mv88w8618_timer_vmsd = {
|
||||
@ -955,9 +963,7 @@ static const VMStateDescription mv88w8618_pit_vmsd = {
|
||||
static void mv88w8618_pit_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = mv88w8618_pit_init;
|
||||
dc->reset = mv88w8618_pit_reset;
|
||||
dc->vmsd = &mv88w8618_pit_vmsd;
|
||||
}
|
||||
@ -966,6 +972,7 @@ static const TypeInfo mv88w8618_pit_info = {
|
||||
.name = TYPE_MV88W8618_PIT,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(mv88w8618_pit_state),
|
||||
.instance_init = mv88w8618_pit_init,
|
||||
.class_init = mv88w8618_pit_class_init,
|
||||
};
|
||||
|
||||
@ -1018,15 +1025,15 @@ static const MemoryRegionOps mv88w8618_flashcfg_ops = {
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int mv88w8618_flashcfg_init(SysBusDevice *dev)
|
||||
static void mv88w8618_flashcfg_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev);
|
||||
|
||||
s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s,
|
||||
memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s,
|
||||
"musicpal-flashcfg", MP_FLASHCFG_SIZE);
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription mv88w8618_flashcfg_vmsd = {
|
||||
@ -1042,9 +1049,7 @@ static const VMStateDescription mv88w8618_flashcfg_vmsd = {
|
||||
static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = mv88w8618_flashcfg_init;
|
||||
dc->vmsd = &mv88w8618_flashcfg_vmsd;
|
||||
}
|
||||
|
||||
@ -1052,6 +1057,7 @@ static const TypeInfo mv88w8618_flashcfg_info = {
|
||||
.name = TYPE_MV88W8618_FLASHCFG,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(mv88w8618_flashcfg_state),
|
||||
.instance_init = mv88w8618_flashcfg_init,
|
||||
.class_init = mv88w8618_flashcfg_class_init,
|
||||
};
|
||||
|
||||
@ -1350,22 +1356,21 @@ static void musicpal_gpio_reset(DeviceState *d)
|
||||
s->isr = 0;
|
||||
}
|
||||
|
||||
static int musicpal_gpio_init(SysBusDevice *sbd)
|
||||
static void musicpal_gpio_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
musicpal_gpio_state *s = MUSICPAL_GPIO(dev);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s,
|
||||
memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s,
|
||||
"musicpal-gpio", MP_GPIO_SIZE);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
|
||||
|
||||
qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription musicpal_gpio_vmsd = {
|
||||
@ -1386,9 +1391,7 @@ static const VMStateDescription musicpal_gpio_vmsd = {
|
||||
static void musicpal_gpio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = musicpal_gpio_init;
|
||||
dc->reset = musicpal_gpio_reset;
|
||||
dc->vmsd = &musicpal_gpio_vmsd;
|
||||
}
|
||||
@ -1397,6 +1400,7 @@ static const TypeInfo musicpal_gpio_info = {
|
||||
.name = TYPE_MUSICPAL_GPIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(musicpal_gpio_state),
|
||||
.instance_init = musicpal_gpio_init,
|
||||
.class_init = musicpal_gpio_class_init,
|
||||
};
|
||||
|
||||
@ -1516,12 +1520,13 @@ static void musicpal_key_event(void *opaque, int keycode)
|
||||
s->kbd_extended = 0;
|
||||
}
|
||||
|
||||
static int musicpal_key_init(SysBusDevice *sbd)
|
||||
static void musicpal_key_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
musicpal_key_state *s = MUSICPAL_KEY(dev);
|
||||
|
||||
memory_region_init(&s->iomem, OBJECT(s), "dummy", 0);
|
||||
memory_region_init(&s->iomem, obj, "dummy", 0);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
s->kbd_extended = 0;
|
||||
@ -1530,8 +1535,6 @@ static int musicpal_key_init(SysBusDevice *sbd)
|
||||
qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
|
||||
|
||||
qemu_add_kbd_event_handler(musicpal_key_event, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription musicpal_key_vmsd = {
|
||||
@ -1548,9 +1551,7 @@ static const VMStateDescription musicpal_key_vmsd = {
|
||||
static void musicpal_key_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = musicpal_key_init;
|
||||
dc->vmsd = &musicpal_key_vmsd;
|
||||
}
|
||||
|
||||
@ -1558,6 +1559,7 @@ static const TypeInfo musicpal_key_info = {
|
||||
.name = TYPE_MUSICPAL_KEY,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(musicpal_key_state),
|
||||
.instance_init = musicpal_key_init,
|
||||
.class_init = musicpal_key_class_init,
|
||||
};
|
||||
|
||||
|
@ -280,23 +280,28 @@ DeviceState *pxa2xx_gpio_init(hwaddr base,
|
||||
return dev;
|
||||
}
|
||||
|
||||
static int pxa2xx_gpio_initfn(SysBusDevice *sbd)
|
||||
static void pxa2xx_gpio_initfn(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &pxa_gpio_ops,
|
||||
s, "pxa2xx-gpio", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq0);
|
||||
sysbus_init_irq(sbd, &s->irq1);
|
||||
sysbus_init_irq(sbd, &s->irqX);
|
||||
}
|
||||
|
||||
static void pxa2xx_gpio_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev);
|
||||
|
||||
s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu));
|
||||
|
||||
qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines);
|
||||
qdev_init_gpio_out(dev, s->handler, s->lines);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &pxa_gpio_ops, s, "pxa2xx-gpio", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq0);
|
||||
sysbus_init_irq(sbd, &s->irq1);
|
||||
sysbus_init_irq(sbd, &s->irqX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -336,18 +341,18 @@ static Property pxa2xx_gpio_properties[] = {
|
||||
static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = pxa2xx_gpio_initfn;
|
||||
dc->desc = "PXA2xx GPIO controller";
|
||||
dc->props = pxa2xx_gpio_properties;
|
||||
dc->vmsd = &vmstate_pxa2xx_gpio_regs;
|
||||
dc->realize = pxa2xx_gpio_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo pxa2xx_gpio_info = {
|
||||
.name = TYPE_PXA2XX_GPIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(PXA2xxGPIOInfo),
|
||||
.instance_init = pxa2xx_gpio_initfn,
|
||||
.class_init = pxa2xx_gpio_class_init,
|
||||
};
|
||||
|
||||
|
@ -1236,6 +1236,11 @@ static void strongarm_uart_init(Object *obj)
|
||||
|
||||
s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s);
|
||||
s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s);
|
||||
}
|
||||
|
||||
static void strongarm_uart_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
StrongARMUARTState *s = STRONGARM_UART(dev);
|
||||
|
||||
qemu_chr_fe_set_handlers(&s->chr,
|
||||
strongarm_uart_can_receive,
|
||||
@ -1316,6 +1321,7 @@ static void strongarm_uart_class_init(ObjectClass *klass, void *data)
|
||||
dc->reset = strongarm_uart_reset;
|
||||
dc->vmsd = &vmstate_strongarm_uart_regs;
|
||||
dc->props = strongarm_uart_properties;
|
||||
dc->realize = strongarm_uart_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo strongarm_uart_info = {
|
||||
@ -1516,19 +1522,19 @@ static int strongarm_ssp_post_load(void *opaque, int version_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int strongarm_ssp_init(SysBusDevice *sbd)
|
||||
static void strongarm_ssp_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
StrongARMSSPState *s = STRONGARM_SSP(dev);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ssp_ops, s,
|
||||
memory_region_init_io(&s->iomem, obj, &strongarm_ssp_ops, s,
|
||||
"ssp", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
s->bus = ssi_create_bus(dev, "ssi");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void strongarm_ssp_reset(DeviceState *dev)
|
||||
@ -1558,9 +1564,7 @@ static const VMStateDescription vmstate_strongarm_ssp_regs = {
|
||||
static void strongarm_ssp_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = strongarm_ssp_init;
|
||||
dc->desc = "StrongARM SSP controller";
|
||||
dc->reset = strongarm_ssp_reset;
|
||||
dc->vmsd = &vmstate_strongarm_ssp_regs;
|
||||
@ -1570,6 +1574,7 @@ static const TypeInfo strongarm_ssp_info = {
|
||||
.name = TYPE_STRONGARM_SSP,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(StrongARMSSPState),
|
||||
.instance_init = strongarm_ssp_init,
|
||||
.class_init = strongarm_ssp_class_init,
|
||||
};
|
||||
|
||||
|
@ -383,6 +383,61 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
|
||||
return rsdp_table;
|
||||
}
|
||||
|
||||
static void
|
||||
build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
|
||||
{
|
||||
int iort_start = table_data->len;
|
||||
AcpiIortIdMapping *idmap;
|
||||
AcpiIortItsGroup *its;
|
||||
AcpiIortTable *iort;
|
||||
size_t node_size, iort_length;
|
||||
AcpiIortRC *rc;
|
||||
|
||||
iort = acpi_data_push(table_data, sizeof(*iort));
|
||||
|
||||
iort_length = sizeof(*iort);
|
||||
iort->node_count = cpu_to_le32(2); /* RC and ITS nodes */
|
||||
iort->node_offset = cpu_to_le32(sizeof(*iort));
|
||||
|
||||
/* ITS group node */
|
||||
node_size = sizeof(*its) + sizeof(uint32_t);
|
||||
iort_length += node_size;
|
||||
its = acpi_data_push(table_data, node_size);
|
||||
|
||||
its->type = ACPI_IORT_NODE_ITS_GROUP;
|
||||
its->length = cpu_to_le16(node_size);
|
||||
its->its_count = cpu_to_le32(1);
|
||||
its->identifiers[0] = 0; /* MADT translation_id */
|
||||
|
||||
/* Root Complex Node */
|
||||
node_size = sizeof(*rc) + sizeof(*idmap);
|
||||
iort_length += node_size;
|
||||
rc = acpi_data_push(table_data, node_size);
|
||||
|
||||
rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
|
||||
rc->length = cpu_to_le16(node_size);
|
||||
rc->mapping_count = cpu_to_le32(1);
|
||||
rc->mapping_offset = cpu_to_le32(sizeof(*rc));
|
||||
|
||||
/* fully coherent device */
|
||||
rc->memory_properties.cache_coherency = cpu_to_le32(1);
|
||||
rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
|
||||
rc->pci_segment_number = 0; /* MCFG pci_segment */
|
||||
|
||||
/* Identity RID mapping covering the whole input RID range */
|
||||
idmap = &rc->id_mapping_array[0];
|
||||
idmap->input_base = 0;
|
||||
idmap->id_count = cpu_to_le32(0xFFFF);
|
||||
idmap->output_base = 0;
|
||||
/* output IORT node is the ITS group node (the first node) */
|
||||
idmap->output_reference = cpu_to_le32(iort->node_offset);
|
||||
|
||||
iort->length = cpu_to_le32(iort_length);
|
||||
|
||||
build_header(linker, table_data, (void *)(table_data->data + iort_start),
|
||||
"IORT", table_data->len - iort_start, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
|
||||
{
|
||||
@ -667,17 +722,6 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
|
||||
ACPI_BUILD_TABLE_FILE, tables_blob,
|
||||
64, false /* high memory */);
|
||||
|
||||
/*
|
||||
* The ACPI v5.1 tables for Hardware-reduced ACPI platform are:
|
||||
* RSDP
|
||||
* RSDT
|
||||
* FADT
|
||||
* GTDT
|
||||
* MADT
|
||||
* MCFG
|
||||
* DSDT
|
||||
*/
|
||||
|
||||
/* DSDT is pointed to by FADT */
|
||||
dsdt = tables_blob->len;
|
||||
build_dsdt(tables_blob, tables->linker, guest_info);
|
||||
@ -703,6 +747,11 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
|
||||
build_srat(tables_blob, tables->linker, guest_info);
|
||||
}
|
||||
|
||||
if (its_class_name() && !guest_info->no_its) {
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_iort(tables_blob, tables->linker, guest_info);
|
||||
}
|
||||
|
||||
/* RSDT is pointed to by RSDP */
|
||||
rsdt = tables_blob->len;
|
||||
build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
|
||||
|
@ -1499,6 +1499,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||
mc->block_default_type = IF_VIRTIO;
|
||||
mc->no_cdrom = 1;
|
||||
mc->pci_allow_0_address = true;
|
||||
/* We know we will never create a pre-ARMv7 CPU which needs 1K pages */
|
||||
mc->minimum_page_bits = 12;
|
||||
}
|
||||
|
||||
static const TypeInfo virt_machine_info = {
|
||||
@ -1570,6 +1572,8 @@ static void virt_machine_2_7_options(MachineClass *mc)
|
||||
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_7);
|
||||
/* ITS was introduced with 2.8 */
|
||||
vmc->no_its = true;
|
||||
/* Stick with 1K pages for migration compatibility */
|
||||
mc->minimum_page_bits = 0;
|
||||
}
|
||||
DEFINE_VIRT_MACHINE(2, 7)
|
||||
|
||||
|
130
hw/core/ptimer.c
130
hw/core/ptimer.c
@ -13,6 +13,9 @@
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
#define DELTA_ADJUST 1
|
||||
#define DELTA_NO_ADJUST -1
|
||||
|
||||
struct ptimer_state
|
||||
{
|
||||
uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */
|
||||
@ -35,16 +38,21 @@ static void ptimer_trigger(ptimer_state *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void ptimer_reload(ptimer_state *s)
|
||||
static void ptimer_reload(ptimer_state *s, int delta_adjust)
|
||||
{
|
||||
uint32_t period_frac = s->period_frac;
|
||||
uint64_t period = s->period;
|
||||
uint64_t delta = s->delta;
|
||||
|
||||
if (s->delta == 0) {
|
||||
if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
|
||||
ptimer_trigger(s);
|
||||
s->delta = s->limit;
|
||||
}
|
||||
if (s->delta == 0 || s->period == 0) {
|
||||
|
||||
if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
|
||||
delta = s->delta = s->limit;
|
||||
}
|
||||
|
||||
if (s->period == 0) {
|
||||
if (!qtest_enabled()) {
|
||||
fprintf(stderr, "Timer with period zero, disabling\n");
|
||||
}
|
||||
@ -53,6 +61,39 @@ static void ptimer_reload(ptimer_state *s)
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
|
||||
if (delta_adjust != DELTA_NO_ADJUST) {
|
||||
delta += delta_adjust;
|
||||
}
|
||||
}
|
||||
|
||||
if (delta == 0 && (s->policy_mask & PTIMER_POLICY_CONTINUOUS_TRIGGER)) {
|
||||
if (s->enabled == 1 && s->limit == 0) {
|
||||
delta = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
|
||||
if (delta_adjust != DELTA_NO_ADJUST) {
|
||||
delta = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
|
||||
if (s->enabled == 1 && s->limit != 0) {
|
||||
delta = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (delta == 0) {
|
||||
if (!qtest_enabled()) {
|
||||
fprintf(stderr, "Timer with delta zero, disabling\n");
|
||||
}
|
||||
timer_del(s->timer);
|
||||
s->enabled = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Artificially limit timeout rate to something
|
||||
* achievable under QEMU. Otherwise, QEMU spends all
|
||||
@ -62,15 +103,15 @@ static void ptimer_reload(ptimer_state *s)
|
||||
* on the current generation of host machines.
|
||||
*/
|
||||
|
||||
if (s->enabled == 1 && (s->delta * period < 10000) && !use_icount) {
|
||||
period = 10000 / s->delta;
|
||||
if (s->enabled == 1 && (delta * period < 10000) && !use_icount) {
|
||||
period = 10000 / delta;
|
||||
period_frac = 0;
|
||||
}
|
||||
|
||||
s->last_event = s->next_event;
|
||||
s->next_event = s->last_event + s->delta * period;
|
||||
s->next_event = s->last_event + delta * period;
|
||||
if (period_frac) {
|
||||
s->next_event += ((int64_t)period_frac * s->delta) >> 32;
|
||||
s->next_event += ((int64_t)period_frac * delta) >> 32;
|
||||
}
|
||||
timer_mod(s->timer, s->next_event);
|
||||
}
|
||||
@ -78,12 +119,35 @@ static void ptimer_reload(ptimer_state *s)
|
||||
static void ptimer_tick(void *opaque)
|
||||
{
|
||||
ptimer_state *s = (ptimer_state *)opaque;
|
||||
ptimer_trigger(s);
|
||||
s->delta = 0;
|
||||
bool trigger = true;
|
||||
|
||||
if (s->enabled == 2) {
|
||||
s->delta = 0;
|
||||
s->enabled = 0;
|
||||
} else {
|
||||
ptimer_reload(s);
|
||||
int delta_adjust = DELTA_ADJUST;
|
||||
|
||||
if (s->delta == 0 || s->limit == 0) {
|
||||
/* If a "continuous trigger" policy is not used and limit == 0,
|
||||
we should error out. delta == 0 means that this tick is
|
||||
caused by a "no immediate reload" policy, so it shouldn't
|
||||
be adjusted. */
|
||||
delta_adjust = DELTA_NO_ADJUST;
|
||||
}
|
||||
|
||||
if (!(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
|
||||
/* Avoid re-trigger on deferred reload if "no immediate trigger"
|
||||
policy isn't used. */
|
||||
trigger = (delta_adjust == DELTA_ADJUST);
|
||||
}
|
||||
|
||||
s->delta = s->limit;
|
||||
|
||||
ptimer_reload(s, delta_adjust);
|
||||
}
|
||||
|
||||
if (trigger) {
|
||||
ptimer_trigger(s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,9 +155,10 @@ uint64_t ptimer_get_count(ptimer_state *s)
|
||||
{
|
||||
uint64_t counter;
|
||||
|
||||
if (s->enabled) {
|
||||
if (s->enabled && s->delta != 0) {
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t next = s->next_event;
|
||||
int64_t last = s->last_event;
|
||||
bool expired = (now - next >= 0);
|
||||
bool oneshot = (s->enabled == 2);
|
||||
|
||||
@ -118,7 +183,7 @@ uint64_t ptimer_get_count(ptimer_state *s)
|
||||
/* We need to divide time by period, where time is stored in
|
||||
rem (64-bit integer) and period is stored in period/period_frac
|
||||
(64.32 fixed point).
|
||||
|
||||
|
||||
Doing full precision division is hard, so scale values and
|
||||
do a 64-bit division. The result should be rounded down,
|
||||
so that the rounding error never causes the timer to go
|
||||
@ -145,6 +210,35 @@ uint64_t ptimer_get_count(ptimer_state *s)
|
||||
div += 1;
|
||||
}
|
||||
counter = rem / div;
|
||||
|
||||
if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
|
||||
/* Before wrapping around, timer should stay with counter = 0
|
||||
for a one period. */
|
||||
if (!oneshot && s->delta == s->limit) {
|
||||
if (now == last) {
|
||||
/* Counter == delta here, check whether it was
|
||||
adjusted and if it was, then right now it is
|
||||
that "one period". */
|
||||
if (counter == s->limit + DELTA_ADJUST) {
|
||||
return 0;
|
||||
}
|
||||
} else if (counter == s->limit) {
|
||||
/* Since the counter is rounded down and now != last,
|
||||
the counter == limit means that delta was adjusted
|
||||
by +1 and right now it is that adjusted period. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s->policy_mask & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) {
|
||||
/* If now == last then delta == limit, i.e. the counter already
|
||||
represents the correct value. It would be rounded down a 1ns
|
||||
later. */
|
||||
if (now != last) {
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
counter = s->delta;
|
||||
@ -157,7 +251,7 @@ void ptimer_set_count(ptimer_state *s, uint64_t count)
|
||||
s->delta = count;
|
||||
if (s->enabled) {
|
||||
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ptimer_reload(s);
|
||||
ptimer_reload(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +268,7 @@ void ptimer_run(ptimer_state *s, int oneshot)
|
||||
s->enabled = oneshot ? 2 : 1;
|
||||
if (was_disabled) {
|
||||
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ptimer_reload(s);
|
||||
ptimer_reload(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +292,7 @@ void ptimer_set_period(ptimer_state *s, int64_t period)
|
||||
s->period_frac = 0;
|
||||
if (s->enabled) {
|
||||
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ptimer_reload(s);
|
||||
ptimer_reload(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,7 +304,7 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq)
|
||||
s->period_frac = (1000000000ll << 32) / freq;
|
||||
if (s->enabled) {
|
||||
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ptimer_reload(s);
|
||||
ptimer_reload(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,7 +317,7 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
|
||||
s->delta = limit;
|
||||
if (s->enabled && reload) {
|
||||
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ptimer_reload(s);
|
||||
ptimer_reload(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,17 +466,16 @@ static const GraphicHwOps pl110_gfx_ops = {
|
||||
.gfx_update = pl110_update_display,
|
||||
};
|
||||
|
||||
static int pl110_initfn(SysBusDevice *sbd)
|
||||
static void pl110_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
PL110State *s = PL110(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
|
||||
s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pl110_init(Object *obj)
|
||||
@ -503,11 +502,10 @@ static void pl111_init(Object *obj)
|
||||
static void pl110_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = pl110_initfn;
|
||||
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
|
||||
dc->vmsd = &vmstate_pl110;
|
||||
dc->realize = pl110_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo pl110_info = {
|
||||
|
@ -88,7 +88,12 @@ int i2c_bus_busy(I2CBus *bus)
|
||||
return !QLIST_EMPTY(&bus->current_devs);
|
||||
}
|
||||
|
||||
/* Returns non-zero if the address is not valid. */
|
||||
/*
|
||||
* Returns non-zero if the address is not valid. If this is called
|
||||
* again without an intervening i2c_end_transfer(), like in the SMBus
|
||||
* case where the operation is switched from write to read, this
|
||||
* function will not rescan the bus and thus cannot fail.
|
||||
*/
|
||||
/* TODO: Make this handle multiple masters. */
|
||||
int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
|
||||
{
|
||||
@ -104,15 +109,25 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
|
||||
bus->broadcast = true;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
|
||||
DeviceState *qdev = kid->child;
|
||||
I2CSlave *candidate = I2C_SLAVE(qdev);
|
||||
if ((candidate->address == address) || (bus->broadcast)) {
|
||||
node = g_malloc(sizeof(struct I2CNode));
|
||||
node->elt = candidate;
|
||||
QLIST_INSERT_HEAD(&bus->current_devs, node, next);
|
||||
if (!bus->broadcast) {
|
||||
break;
|
||||
/*
|
||||
* If there are already devices in the list, that means we are in
|
||||
* the middle of a transaction and we shouldn't rescan the bus.
|
||||
*
|
||||
* This happens with any SMBus transaction, even on a pure I2C
|
||||
* device. The interface does a transaction start without
|
||||
* terminating the previous transaction.
|
||||
*/
|
||||
if (QLIST_EMPTY(&bus->current_devs)) {
|
||||
QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
|
||||
DeviceState *qdev = kid->child;
|
||||
I2CSlave *candidate = I2C_SLAVE(qdev);
|
||||
if ((candidate->address == address) || (bus->broadcast)) {
|
||||
node = g_malloc(sizeof(struct I2CNode));
|
||||
node->elt = candidate;
|
||||
QLIST_INSERT_HEAD(&bus->current_devs, node, next);
|
||||
if (!bus->broadcast) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,10 +152,6 @@ void i2c_end_transfer(I2CBus *bus)
|
||||
I2CSlaveClass *sc;
|
||||
I2CNode *node, *next;
|
||||
|
||||
if (QLIST_EMPTY(&bus->current_devs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {
|
||||
sc = I2C_SLAVE_GET_CLASS(node->elt);
|
||||
if (sc->event) {
|
||||
|
@ -248,7 +248,9 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
i2c_start_transfer(bus, addr, 1);
|
||||
if (i2c_start_transfer(bus, addr, 1)) {
|
||||
assert(0);
|
||||
}
|
||||
data = i2c_recv(bus);
|
||||
i2c_nack(bus);
|
||||
i2c_end_transfer(bus);
|
||||
@ -273,7 +275,9 @@ int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
i2c_start_transfer(bus, addr, 1);
|
||||
if (i2c_start_transfer(bus, addr, 1)) {
|
||||
assert(0);
|
||||
}
|
||||
data = i2c_recv(bus);
|
||||
data |= i2c_recv(bus) << 8;
|
||||
i2c_nack(bus);
|
||||
@ -302,7 +306,9 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data)
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
i2c_start_transfer(bus, addr, 1);
|
||||
if (i2c_start_transfer(bus, addr, 1)) {
|
||||
assert(0);
|
||||
}
|
||||
len = i2c_recv(bus);
|
||||
if (len > 32) {
|
||||
len = 0;
|
||||
|
@ -82,15 +82,15 @@ static void a9_gtimer_update(A9GTimerState *s, bool sync)
|
||||
if ((s->control & R_CONTROL_TIMER_ENABLE) &&
|
||||
(gtb->control & R_CONTROL_COMP_ENABLE)) {
|
||||
/* R2p0+, where the compare function is >= */
|
||||
while (gtb->compare < update.new) {
|
||||
if (gtb->compare < update.new) {
|
||||
DB_PRINT("Compare event happened for CPU %d\n", i);
|
||||
gtb->status = 1;
|
||||
if (gtb->control & R_CONTROL_AUTO_INCREMENT) {
|
||||
DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n",
|
||||
gtb->inc);
|
||||
gtb->compare += gtb->inc;
|
||||
} else {
|
||||
break;
|
||||
if (gtb->control & R_CONTROL_AUTO_INCREMENT && gtb->inc) {
|
||||
uint64_t inc =
|
||||
QEMU_ALIGN_UP(update.new - gtb->compare, gtb->inc);
|
||||
DB_PRINT("Auto incrementing timer compare by %"
|
||||
PRId64 "\n", inc);
|
||||
gtb->compare += inc;
|
||||
}
|
||||
}
|
||||
cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1;
|
||||
|
@ -20,22 +20,33 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/ptimer.h"
|
||||
#include "hw/timer/arm_mptimer.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qom/cpu.h"
|
||||
|
||||
#define PTIMER_POLICY \
|
||||
(PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | \
|
||||
PTIMER_POLICY_CONTINUOUS_TRIGGER | \
|
||||
PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | \
|
||||
PTIMER_POLICY_NO_IMMEDIATE_RELOAD | \
|
||||
PTIMER_POLICY_NO_COUNTER_ROUND_DOWN)
|
||||
|
||||
/* This device implements the per-cpu private timer and watchdog block
|
||||
* which is used in both the ARM11MPCore and Cortex-A9MP.
|
||||
*/
|
||||
|
||||
static inline int get_current_cpu(ARMMPTimerState *s)
|
||||
{
|
||||
if (current_cpu->cpu_index >= s->num_cpu) {
|
||||
int cpu_id = current_cpu ? current_cpu->cpu_index : 0;
|
||||
|
||||
if (cpu_id >= s->num_cpu) {
|
||||
hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
|
||||
s->num_cpu, current_cpu->cpu_index);
|
||||
s->num_cpu, cpu_id);
|
||||
}
|
||||
return current_cpu->cpu_index;
|
||||
|
||||
return cpu_id;
|
||||
}
|
||||
|
||||
static inline void timerblock_update_irq(TimerBlock *tb)
|
||||
@ -44,33 +55,42 @@ static inline void timerblock_update_irq(TimerBlock *tb)
|
||||
}
|
||||
|
||||
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
|
||||
static inline uint32_t timerblock_scale(TimerBlock *tb)
|
||||
static inline uint32_t timerblock_scale(uint32_t control)
|
||||
{
|
||||
return (((tb->control >> 8) & 0xff) + 1) * 10;
|
||||
return (((control >> 8) & 0xff) + 1) * 10;
|
||||
}
|
||||
|
||||
static void timerblock_reload(TimerBlock *tb, int restart)
|
||||
static inline void timerblock_set_count(struct ptimer_state *timer,
|
||||
uint32_t control, uint64_t *count)
|
||||
{
|
||||
if (tb->count == 0) {
|
||||
return;
|
||||
/* PTimer would trigger interrupt for periodic timer when counter set
|
||||
* to 0, MPtimer under certain condition only.
|
||||
*/
|
||||
if ((control & 3) == 3 && (control & 0xff00) == 0 && *count == 0) {
|
||||
*count = ptimer_get_limit(timer);
|
||||
}
|
||||
if (restart) {
|
||||
tb->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ptimer_set_count(timer, *count);
|
||||
}
|
||||
|
||||
static inline void timerblock_run(struct ptimer_state *timer,
|
||||
uint32_t control, uint32_t load)
|
||||
{
|
||||
if ((control & 1) && ((control & 0xff00) || load != 0)) {
|
||||
ptimer_run(timer, !(control & 2));
|
||||
}
|
||||
tb->tick += (int64_t)tb->count * timerblock_scale(tb);
|
||||
timer_mod(tb->timer, tb->tick);
|
||||
}
|
||||
|
||||
static void timerblock_tick(void *opaque)
|
||||
{
|
||||
TimerBlock *tb = (TimerBlock *)opaque;
|
||||
tb->status = 1;
|
||||
if (tb->control & 2) {
|
||||
tb->count = tb->load;
|
||||
timerblock_reload(tb, 0);
|
||||
} else {
|
||||
tb->count = 0;
|
||||
/* Periodic timer with load = 0 and prescaler != 0 would re-trigger
|
||||
* IRQ after one period, otherwise it either stops or wraps around.
|
||||
*/
|
||||
if ((tb->control & 2) && (tb->control & 0xff00) == 0 &&
|
||||
ptimer_get_limit(tb->timer) == 0) {
|
||||
ptimer_stop(tb->timer);
|
||||
}
|
||||
tb->status = 1;
|
||||
timerblock_update_irq(tb);
|
||||
}
|
||||
|
||||
@ -78,21 +98,11 @@ static uint64_t timerblock_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
TimerBlock *tb = (TimerBlock *)opaque;
|
||||
int64_t val;
|
||||
switch (addr) {
|
||||
case 0: /* Load */
|
||||
return tb->load;
|
||||
return ptimer_get_limit(tb->timer);
|
||||
case 4: /* Counter. */
|
||||
if (((tb->control & 1) == 0) || (tb->count == 0)) {
|
||||
return 0;
|
||||
}
|
||||
/* Slow and ugly, but hopefully won't happen too often. */
|
||||
val = tb->tick - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
val /= timerblock_scale(tb);
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
}
|
||||
return val;
|
||||
return ptimer_get_count(tb->timer);
|
||||
case 8: /* Control. */
|
||||
return tb->control;
|
||||
case 12: /* Interrupt status. */
|
||||
@ -106,37 +116,45 @@ static void timerblock_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
TimerBlock *tb = (TimerBlock *)opaque;
|
||||
int64_t old;
|
||||
uint32_t control = tb->control;
|
||||
switch (addr) {
|
||||
case 0: /* Load */
|
||||
tb->load = value;
|
||||
/* Fall through. */
|
||||
/* Setting load to 0 stops the timer without doing the tick if
|
||||
* prescaler = 0.
|
||||
*/
|
||||
if ((control & 1) && (control & 0xff00) == 0 && value == 0) {
|
||||
ptimer_stop(tb->timer);
|
||||
}
|
||||
ptimer_set_limit(tb->timer, value, 1);
|
||||
timerblock_run(tb->timer, control, value);
|
||||
break;
|
||||
case 4: /* Counter. */
|
||||
if ((tb->control & 1) && tb->count) {
|
||||
/* Cancel the previous timer. */
|
||||
timer_del(tb->timer);
|
||||
}
|
||||
tb->count = value;
|
||||
if (tb->control & 1) {
|
||||
timerblock_reload(tb, 1);
|
||||
/* Setting counter to 0 stops the one-shot timer, or periodic with
|
||||
* load = 0, without doing the tick if prescaler = 0.
|
||||
*/
|
||||
if ((control & 1) && (control & 0xff00) == 0 && value == 0 &&
|
||||
(!(control & 2) || ptimer_get_limit(tb->timer) == 0)) {
|
||||
ptimer_stop(tb->timer);
|
||||
}
|
||||
timerblock_set_count(tb->timer, control, &value);
|
||||
timerblock_run(tb->timer, control, value);
|
||||
break;
|
||||
case 8: /* Control. */
|
||||
old = tb->control;
|
||||
tb->control = value;
|
||||
if (value & 1) {
|
||||
if ((old & 1) && (tb->count != 0)) {
|
||||
/* Do nothing if timer is ticking right now. */
|
||||
break;
|
||||
}
|
||||
if (tb->control & 2) {
|
||||
tb->count = tb->load;
|
||||
}
|
||||
timerblock_reload(tb, 1);
|
||||
} else if (old & 1) {
|
||||
/* Shutdown the timer. */
|
||||
timer_del(tb->timer);
|
||||
if ((control & 3) != (value & 3)) {
|
||||
ptimer_stop(tb->timer);
|
||||
}
|
||||
if ((control & 0xff00) != (value & 0xff00)) {
|
||||
ptimer_set_period(tb->timer, timerblock_scale(value));
|
||||
}
|
||||
if (value & 1) {
|
||||
uint64_t count = ptimer_get_count(tb->timer);
|
||||
/* Re-load periodic timer counter if needed. */
|
||||
if ((value & 2) && count == 0) {
|
||||
timerblock_set_count(tb->timer, value, &count);
|
||||
}
|
||||
timerblock_run(tb->timer, value, count);
|
||||
}
|
||||
tb->control = value;
|
||||
break;
|
||||
case 12: /* Interrupt status. */
|
||||
tb->status &= ~value;
|
||||
@ -186,13 +204,12 @@ static const MemoryRegionOps timerblock_ops = {
|
||||
|
||||
static void timerblock_reset(TimerBlock *tb)
|
||||
{
|
||||
tb->count = 0;
|
||||
tb->load = 0;
|
||||
tb->control = 0;
|
||||
tb->status = 0;
|
||||
tb->tick = 0;
|
||||
if (tb->timer) {
|
||||
timer_del(tb->timer);
|
||||
ptimer_stop(tb->timer);
|
||||
ptimer_set_limit(tb->timer, 0, 1);
|
||||
ptimer_set_period(tb->timer, timerblock_scale(0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +255,8 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp)
|
||||
*/
|
||||
for (i = 0; i < s->num_cpu; i++) {
|
||||
TimerBlock *tb = &s->timerblock[i];
|
||||
tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb);
|
||||
QEMUBH *bh = qemu_bh_new(timerblock_tick, tb);
|
||||
tb->timer = ptimer_init(bh, PTIMER_POLICY);
|
||||
sysbus_init_irq(sbd, &tb->irq);
|
||||
memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb,
|
||||
"arm_mptimer_timerblock", 0x20);
|
||||
@ -248,26 +266,23 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
static const VMStateDescription vmstate_timerblock = {
|
||||
.name = "arm_mptimer_timerblock",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 3,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(count, TimerBlock),
|
||||
VMSTATE_UINT32(load, TimerBlock),
|
||||
VMSTATE_UINT32(control, TimerBlock),
|
||||
VMSTATE_UINT32(status, TimerBlock),
|
||||
VMSTATE_INT64(tick, TimerBlock),
|
||||
VMSTATE_TIMER_PTR(timer, TimerBlock),
|
||||
VMSTATE_PTIMER(timer, TimerBlock),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_arm_mptimer = {
|
||||
.name = "arm_mptimer",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 3,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu,
|
||||
2, vmstate_timerblock, TimerBlock),
|
||||
3, vmstate_timerblock, TimerBlock),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
@ -217,7 +217,7 @@ static void stm32f2xx_timer_write(void *opaque, hwaddr offset,
|
||||
return;
|
||||
case TIM_PSC:
|
||||
timer_val = stm32f2xx_ns_to_ticks(s, now) - s->tick_offset;
|
||||
s->tim_psc = value;
|
||||
s->tim_psc = value & 0xFFFF;
|
||||
value = timer_val;
|
||||
break;
|
||||
case TIM_CNT:
|
||||
|
@ -189,6 +189,15 @@ void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val,
|
||||
|
||||
/* page related stuff */
|
||||
|
||||
#ifdef TARGET_PAGE_BITS_VARY
|
||||
extern bool target_page_bits_decided;
|
||||
extern int target_page_bits;
|
||||
#define TARGET_PAGE_BITS ({ assert(target_page_bits_decided); \
|
||||
target_page_bits; })
|
||||
#else
|
||||
#define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS
|
||||
#endif
|
||||
|
||||
#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
|
||||
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
|
||||
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
|
||||
|
@ -609,4 +609,72 @@ typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit;
|
||||
/* Masks for Flags field above */
|
||||
#define ACPI_DMAR_INCLUDE_PCI_ALL 1
|
||||
|
||||
/*
|
||||
* Input Output Remapping Table (IORT)
|
||||
* Conforms to "IO Remapping Table System Software on ARM Platforms",
|
||||
* Document number: ARM DEN 0049B, October 2015
|
||||
*/
|
||||
|
||||
struct AcpiIortTable {
|
||||
ACPI_TABLE_HEADER_DEF /* ACPI common table header */
|
||||
uint32_t node_count;
|
||||
uint32_t node_offset;
|
||||
uint32_t reserved;
|
||||
} QEMU_PACKED;
|
||||
typedef struct AcpiIortTable AcpiIortTable;
|
||||
|
||||
/*
|
||||
* IORT node types
|
||||
*/
|
||||
|
||||
#define ACPI_IORT_NODE_HEADER_DEF /* Node format common fields */ \
|
||||
uint8_t type; \
|
||||
uint16_t length; \
|
||||
uint8_t revision; \
|
||||
uint32_t reserved; \
|
||||
uint32_t mapping_count; \
|
||||
uint32_t mapping_offset;
|
||||
|
||||
/* Values for node Type above */
|
||||
enum {
|
||||
ACPI_IORT_NODE_ITS_GROUP = 0x00,
|
||||
ACPI_IORT_NODE_NAMED_COMPONENT = 0x01,
|
||||
ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02,
|
||||
ACPI_IORT_NODE_SMMU = 0x03,
|
||||
ACPI_IORT_NODE_SMMU_V3 = 0x04
|
||||
};
|
||||
|
||||
struct AcpiIortIdMapping {
|
||||
uint32_t input_base;
|
||||
uint32_t id_count;
|
||||
uint32_t output_base;
|
||||
uint32_t output_reference;
|
||||
uint32_t flags;
|
||||
} QEMU_PACKED;
|
||||
typedef struct AcpiIortIdMapping AcpiIortIdMapping;
|
||||
|
||||
struct AcpiIortMemoryAccess {
|
||||
uint32_t cache_coherency;
|
||||
uint8_t hints;
|
||||
uint16_t reserved;
|
||||
uint8_t memory_flags;
|
||||
} QEMU_PACKED;
|
||||
typedef struct AcpiIortMemoryAccess AcpiIortMemoryAccess;
|
||||
|
||||
struct AcpiIortItsGroup {
|
||||
ACPI_IORT_NODE_HEADER_DEF
|
||||
uint32_t its_count;
|
||||
uint32_t identifiers[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct AcpiIortItsGroup AcpiIortItsGroup;
|
||||
|
||||
struct AcpiIortRC {
|
||||
ACPI_IORT_NODE_HEADER_DEF
|
||||
AcpiIortMemoryAccess memory_properties;
|
||||
uint32_t ats_attribute;
|
||||
uint32_t pci_segment_number;
|
||||
AcpiIortIdMapping id_mapping_array[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct AcpiIortRC AcpiIortRC;
|
||||
|
||||
#endif
|
||||
|
@ -86,6 +86,12 @@ typedef struct {
|
||||
* Returns a @HotpluggableCPUList, which describes CPUs objects which
|
||||
* could be added with -device/device_add.
|
||||
* Caller is responsible for freeing returned list.
|
||||
* @minimum_page_bits:
|
||||
* If non-zero, the board promises never to create a CPU with a page size
|
||||
* smaller than this, so QEMU can use a more efficient larger page
|
||||
* size than the target architecture's minimum. (Attempting to create
|
||||
* such a CPU will fail.) Note that changing this is a migration
|
||||
* compatibility break for the machine.
|
||||
*/
|
||||
struct MachineClass {
|
||||
/*< private >*/
|
||||
@ -124,6 +130,7 @@ struct MachineClass {
|
||||
ram_addr_t default_ram_size;
|
||||
bool option_rom_has_mr;
|
||||
bool rom_file_has_mr;
|
||||
int minimum_page_bits;
|
||||
|
||||
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
|
||||
DeviceState *dev);
|
||||
|
@ -35,6 +35,26 @@
|
||||
*/
|
||||
#define PTIMER_POLICY_DEFAULT 0
|
||||
|
||||
/* Periodic timer counter stays with "0" for a one period before wrapping
|
||||
* around. */
|
||||
#define PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD (1 << 0)
|
||||
|
||||
/* Running periodic timer that has counter = limit = 0 would continuously
|
||||
* re-trigger every period. */
|
||||
#define PTIMER_POLICY_CONTINUOUS_TRIGGER (1 << 1)
|
||||
|
||||
/* Starting to run with/setting counter to "0" won't trigger immediately,
|
||||
* but after a one period for both oneshot and periodic modes. */
|
||||
#define PTIMER_POLICY_NO_IMMEDIATE_TRIGGER (1 << 2)
|
||||
|
||||
/* Starting to run with/setting counter to "0" won't re-load counter
|
||||
* immediately, but after a one period. */
|
||||
#define PTIMER_POLICY_NO_IMMEDIATE_RELOAD (1 << 3)
|
||||
|
||||
/* Make counter value of the running timer represent the actual value and
|
||||
* not the one less. */
|
||||
#define PTIMER_POLICY_NO_COUNTER_ROUND_DOWN (1 << 4)
|
||||
|
||||
/* ptimer.c */
|
||||
typedef struct ptimer_state ptimer_state;
|
||||
typedef void (*ptimer_cb)(void *opaque);
|
||||
|
@ -27,12 +27,9 @@
|
||||
|
||||
/* State of a single timer or watchdog block */
|
||||
typedef struct {
|
||||
uint32_t count;
|
||||
uint32_t load;
|
||||
uint32_t control;
|
||||
uint32_t status;
|
||||
int64_t tick;
|
||||
QEMUTimer *timer;
|
||||
struct ptimer_state *timer;
|
||||
qemu_irq irq;
|
||||
MemoryRegion iomem;
|
||||
} TimerBlock;
|
||||
|
@ -81,6 +81,18 @@ bool tcg_enabled(void);
|
||||
|
||||
void cpu_exec_init_all(void);
|
||||
|
||||
/**
|
||||
* set_preferred_target_page_bits:
|
||||
* @bits: number of bits needed to represent an address within the page
|
||||
*
|
||||
* Set the preferred target page size (the actual target page
|
||||
* size may be smaller than any given CPU's preference).
|
||||
* Returns true on success, false on failure (which can only happen
|
||||
* if this is called after the system has already finalized its
|
||||
* choice of page size and the requested page size is smaller than that).
|
||||
*/
|
||||
bool set_preferred_target_page_bits(int bits);
|
||||
|
||||
/**
|
||||
* Sends a (part of) iovec down a socket, yielding when the socket is full, or
|
||||
* Receives data into a (part of) iovec from a socket,
|
||||
|
@ -806,6 +806,9 @@ void cpu_loop(CPUARMState *env)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_SEMIHOST:
|
||||
env->regs[0] = do_arm_semihosting(env);
|
||||
break;
|
||||
case EXCP_INTERRUPT:
|
||||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
|
@ -69,7 +69,7 @@ static uint64_t bitmap_sync_count;
|
||||
/* 0x80 is reserved in migration.h start with 0x100 next */
|
||||
#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100
|
||||
|
||||
static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE];
|
||||
static uint8_t *ZERO_TARGET_PAGE;
|
||||
|
||||
static inline bool is_zero_range(uint8_t *p, uint64_t size)
|
||||
{
|
||||
@ -1431,6 +1431,7 @@ static void ram_migration_cleanup(void *opaque)
|
||||
cache_fini(XBZRLE.cache);
|
||||
g_free(XBZRLE.encoded_buf);
|
||||
g_free(XBZRLE.current_buf);
|
||||
g_free(ZERO_TARGET_PAGE);
|
||||
XBZRLE.cache = NULL;
|
||||
XBZRLE.encoded_buf = NULL;
|
||||
XBZRLE.current_buf = NULL;
|
||||
@ -1889,6 +1890,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
||||
|
||||
if (migrate_use_xbzrle()) {
|
||||
XBZRLE_cache_lock();
|
||||
ZERO_TARGET_PAGE = g_malloc0(TARGET_PAGE_SIZE);
|
||||
XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
|
||||
TARGET_PAGE_SIZE,
|
||||
TARGET_PAGE_SIZE);
|
||||
|
@ -265,6 +265,7 @@ typedef struct SaveState {
|
||||
bool skip_configuration;
|
||||
uint32_t len;
|
||||
const char *name;
|
||||
uint32_t target_page_bits;
|
||||
} SaveState;
|
||||
|
||||
static SaveState savevm_state = {
|
||||
@ -286,6 +287,19 @@ static void configuration_pre_save(void *opaque)
|
||||
|
||||
state->len = strlen(current_name);
|
||||
state->name = current_name;
|
||||
state->target_page_bits = TARGET_PAGE_BITS;
|
||||
}
|
||||
|
||||
static int configuration_pre_load(void *opaque)
|
||||
{
|
||||
SaveState *state = opaque;
|
||||
|
||||
/* If there is no target-page-bits subsection it means the source
|
||||
* predates the variable-target-page-bits support and is using the
|
||||
* minimum possible value for this CPU.
|
||||
*/
|
||||
state->target_page_bits = TARGET_PAGE_BITS_MIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int configuration_post_load(void *opaque, int version_id)
|
||||
@ -298,12 +312,43 @@ static int configuration_post_load(void *opaque, int version_id)
|
||||
(int) state->len, state->name, current_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state->target_page_bits != TARGET_PAGE_BITS) {
|
||||
error_report("Received TARGET_PAGE_BITS is %d but local is %d",
|
||||
state->target_page_bits, TARGET_PAGE_BITS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The target-page-bits subsection is present only if the
|
||||
* target page size is not the same as the default (ie the
|
||||
* minimum page size for a variable-page-size guest CPU).
|
||||
* If it is present then it contains the actual target page
|
||||
* bits for the machine, and migration will fail if the
|
||||
* two ends don't agree about it.
|
||||
*/
|
||||
static bool vmstate_target_page_bits_needed(void *opaque)
|
||||
{
|
||||
return TARGET_PAGE_BITS > TARGET_PAGE_BITS_MIN;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_target_page_bits = {
|
||||
.name = "configuration/target-page-bits",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = vmstate_target_page_bits_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(target_page_bits, SaveState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_configuration = {
|
||||
.name = "configuration",
|
||||
.version_id = 1,
|
||||
.pre_load = configuration_pre_load,
|
||||
.post_load = configuration_post_load,
|
||||
.pre_save = configuration_pre_save,
|
||||
.fields = (VMStateField[]) {
|
||||
@ -311,6 +356,10 @@ static const VMStateDescription vmstate_configuration = {
|
||||
VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, 0, len),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_target_page_bits,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
static void dump_vmstate_vmsd(FILE *out_file,
|
||||
|
@ -576,6 +576,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
ARMCPU *cpu = ARM_CPU(dev);
|
||||
ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev);
|
||||
CPUARMState *env = &cpu->env;
|
||||
int pagebits;
|
||||
|
||||
/* Some features automatically imply others: */
|
||||
if (arm_feature(env, ARM_FEATURE_V8)) {
|
||||
@ -631,6 +632,29 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
set_feature(env, ARM_FEATURE_THUMB_DSP);
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_V7) &&
|
||||
!arm_feature(env, ARM_FEATURE_M) &&
|
||||
!arm_feature(env, ARM_FEATURE_MPU)) {
|
||||
/* v7VMSA drops support for the old ARMv5 tiny pages, so we
|
||||
* can use 4K pages.
|
||||
*/
|
||||
pagebits = 12;
|
||||
} else {
|
||||
/* For CPUs which might have tiny 1K pages, or which have an
|
||||
* MPU and might have small region sizes, stick with 1K pages.
|
||||
*/
|
||||
pagebits = 10;
|
||||
}
|
||||
if (!set_preferred_target_page_bits(pagebits)) {
|
||||
/* This can only ever happen for hotplugging a CPU, or if
|
||||
* the board code incorrectly creates a CPU which it has
|
||||
* promised via minimum_page_size that it will not.
|
||||
*/
|
||||
error_setg(errp, "This CPU requires a smaller page size than the "
|
||||
"system is using");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->reset_hivecs) {
|
||||
cpu->reset_sctlr |= (1 << 13);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@
|
||||
#define EXCP_SMC 13 /* Secure Monitor Call */
|
||||
#define EXCP_VIRQ 14
|
||||
#define EXCP_VFIQ 15
|
||||
#define EXCP_SEMIHOST 16 /* semihosting call (A64 only) */
|
||||
#define EXCP_SEMIHOST 16 /* semihosting call */
|
||||
|
||||
#define ARMV7M_EXCP_RESET 1
|
||||
#define ARMV7M_EXCP_NMI 2
|
||||
@ -1766,10 +1766,11 @@ bool write_cpustate_to_list(ARMCPU *cpu);
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#define TARGET_PAGE_BITS 12
|
||||
#else
|
||||
/* The ARM MMU allows 1k pages. */
|
||||
/* ??? Linux doesn't actually use these, and they're deprecated in recent
|
||||
architecture revisions. Maybe a configure option to disable them. */
|
||||
#define TARGET_PAGE_BITS 10
|
||||
/* ARMv7 and later CPUs have 4K pages minimum, but ARMv5 and v6
|
||||
* have to support 1K tiny pages.
|
||||
*/
|
||||
#define TARGET_PAGE_BITS_VARY
|
||||
#define TARGET_PAGE_BITS_MIN 10
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_AARCH64)
|
||||
|
@ -6573,12 +6573,19 @@ static inline bool check_for_semihosting(CPUState *cs)
|
||||
/* Only intercept calls from privileged modes, to provide some
|
||||
* semblance of security.
|
||||
*/
|
||||
if (!semihosting_enabled() ||
|
||||
((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)) {
|
||||
if (cs->exception_index != EXCP_SEMIHOST &&
|
||||
(!semihosting_enabled() ||
|
||||
((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (cs->exception_index) {
|
||||
case EXCP_SEMIHOST:
|
||||
/* This is always a semihosting call; the "is this usermode"
|
||||
* and "is semihosting enabled" checks have been done at
|
||||
* translate time.
|
||||
*/
|
||||
break;
|
||||
case EXCP_SWI:
|
||||
/* Check for semihosting interrupt. */
|
||||
if (env->thumb) {
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "arm_ldst.h"
|
||||
#include "exec/semihost.h"
|
||||
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/helper-gen.h"
|
||||
@ -1144,6 +1145,33 @@ static inline void gen_lookup_tb(DisasContext *s)
|
||||
s->is_jmp = DISAS_JUMP;
|
||||
}
|
||||
|
||||
static inline void gen_hlt(DisasContext *s, int imm)
|
||||
{
|
||||
/* HLT. This has two purposes.
|
||||
* Architecturally, it is an external halting debug instruction.
|
||||
* Since QEMU doesn't implement external debug, we treat this as
|
||||
* it is required for halting debug disabled: it will UNDEF.
|
||||
* Secondly, "HLT 0x3C" is a T32 semihosting trap instruction,
|
||||
* and "HLT 0xF000" is an A32 semihosting syscall. These traps
|
||||
* must trigger semihosting even for ARMv7 and earlier, where
|
||||
* HLT was an undefined encoding.
|
||||
* In system mode, we don't allow userspace access to
|
||||
* semihosting, to provide some semblance of security
|
||||
* (and for consistency with our 32-bit semihosting).
|
||||
*/
|
||||
if (semihosting_enabled() &&
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
s->current_el != 0 &&
|
||||
#endif
|
||||
(imm == (s->thumb ? 0x3c : 0xf000))) {
|
||||
gen_exception_internal_insn(s, 0, EXCP_SEMIHOST);
|
||||
return;
|
||||
}
|
||||
|
||||
gen_exception_insn(s, s->thumb ? 2 : 4, EXCP_UDEF, syn_uncategorized(),
|
||||
default_exception_el(s));
|
||||
}
|
||||
|
||||
static inline void gen_add_data_offset(DisasContext *s, unsigned int insn,
|
||||
TCGv_i32 var)
|
||||
{
|
||||
@ -8395,6 +8423,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
||||
{
|
||||
int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4);
|
||||
switch (op1) {
|
||||
case 0:
|
||||
/* HLT */
|
||||
gen_hlt(s, imm16);
|
||||
break;
|
||||
case 1:
|
||||
/* bkpt */
|
||||
ARCH(5);
|
||||
@ -8419,7 +8451,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
||||
gen_smc(s);
|
||||
break;
|
||||
default:
|
||||
goto illegal_op;
|
||||
g_assert_not_reached();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -11451,19 +11483,33 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xa: /* rev */
|
||||
case 0xa: /* rev, and hlt */
|
||||
{
|
||||
int op1 = extract32(insn, 6, 2);
|
||||
|
||||
if (op1 == 2) {
|
||||
/* HLT */
|
||||
int imm6 = extract32(insn, 0, 6);
|
||||
|
||||
gen_hlt(s, imm6);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Otherwise this is rev */
|
||||
ARCH(6);
|
||||
rn = (insn >> 3) & 0x7;
|
||||
rd = insn & 0x7;
|
||||
tmp = load_reg(s, rn);
|
||||
switch ((insn >> 6) & 3) {
|
||||
switch (op1) {
|
||||
case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
|
||||
case 1: gen_rev16(tmp); break;
|
||||
case 3: gen_revsh(tmp); break;
|
||||
default: goto illegal_op;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
store_reg(s, rd, tmp);
|
||||
break;
|
||||
}
|
||||
|
||||
case 6:
|
||||
switch ((insn >> 5) & 7) {
|
||||
|
@ -303,6 +303,8 @@ check-qtest-arm-y += tests/m25p80-test$(EXESUF)
|
||||
gcov-files-arm-y += hw/misc/tmp105.c
|
||||
check-qtest-arm-y += tests/virtio-blk-test$(EXESUF)
|
||||
gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c
|
||||
check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF)
|
||||
gcov-files-arm-y += hw/timer/arm_mptimer.c
|
||||
|
||||
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
|
||||
|
||||
@ -684,6 +686,7 @@ tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-
|
||||
tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y)
|
||||
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o
|
||||
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
|
||||
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
|
||||
|
||||
tests/migration/stress$(EXESUF): tests/migration/stress.o
|
||||
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Stubs for the ptimer-test
|
||||
*
|
||||
* Author: Dmitry Osipenko <digetx@gmail.com>
|
||||
* Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* QTest testcase for the ptimer
|
||||
*
|
||||
* Author: Dmitry Osipenko <digetx@gmail.com>
|
||||
* Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
@ -99,6 +99,7 @@ static void check_oneshot(gconstpointer arg)
|
||||
const uint8_t *policy = arg;
|
||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
||||
|
||||
triggered = false;
|
||||
|
||||
@ -106,34 +107,46 @@ static void check_oneshot(gconstpointer arg)
|
||||
ptimer_set_count(ptimer, 10);
|
||||
ptimer_run(ptimer, 1);
|
||||
|
||||
qemu_clock_step(2000000 * 2 + 100000);
|
||||
qemu_clock_step(2000000 * 2 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_stop(ptimer);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 11);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_run(ptimer, 1);
|
||||
|
||||
qemu_clock_step(2000000 * 7 + 100000);
|
||||
qemu_clock_step(2000000 * 7 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_true(triggered);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
|
||||
|
||||
triggered = false;
|
||||
if (no_round_down) {
|
||||
g_assert_false(triggered);
|
||||
} else {
|
||||
g_assert_true(triggered);
|
||||
|
||||
triggered = false;
|
||||
}
|
||||
|
||||
qemu_clock_step(2000000);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
if (no_round_down) {
|
||||
g_assert_true(triggered);
|
||||
|
||||
triggered = false;
|
||||
} else {
|
||||
g_assert_false(triggered);
|
||||
}
|
||||
|
||||
qemu_clock_step(4000000);
|
||||
|
||||
@ -142,30 +155,30 @@ static void check_oneshot(gconstpointer arg)
|
||||
|
||||
ptimer_set_count(ptimer, 10);
|
||||
|
||||
qemu_clock_step(20000000 + 100000);
|
||||
qemu_clock_step(20000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10);
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_set_limit(ptimer, 9, 1);
|
||||
|
||||
qemu_clock_step(20000000 + 100000);
|
||||
qemu_clock_step(20000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_run(ptimer, 1);
|
||||
|
||||
qemu_clock_step(2000000 + 100000);
|
||||
qemu_clock_step(2000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7);
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_set_count(ptimer, 20);
|
||||
|
||||
qemu_clock_step(2000000 * 19 + 100000);
|
||||
qemu_clock_step(2000000 * 19 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000);
|
||||
@ -177,7 +190,7 @@ static void check_oneshot(gconstpointer arg)
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 * 12 + 100000);
|
||||
qemu_clock_step(2000000 * 12 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_false(triggered);
|
||||
@ -188,6 +201,10 @@ static void check_periodic(gconstpointer arg)
|
||||
const uint8_t *policy = arg;
|
||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||
bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD);
|
||||
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
||||
bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD);
|
||||
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
||||
|
||||
triggered = false;
|
||||
|
||||
@ -195,28 +212,70 @@ static void check_periodic(gconstpointer arg)
|
||||
ptimer_set_limit(ptimer, 10, 1);
|
||||
ptimer_run(ptimer, 0);
|
||||
|
||||
qemu_clock_step(2000000 * 10 + 100000);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10);
|
||||
g_assert_false(triggered);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
|
||||
qemu_clock_step(1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 10 - 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, wrap_policy ? 0 : 10);
|
||||
g_assert_true(triggered);
|
||||
|
||||
qemu_clock_step(1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
wrap_policy ? 0 : (no_round_down ? 10 : 9));
|
||||
g_assert_true(triggered);
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_set_count(ptimer, 20);
|
||||
|
||||
qemu_clock_step(2000000 * 11 + 100000);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 20);
|
||||
g_assert_false(triggered);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
|
||||
qemu_clock_step(1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 20 : 19);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 11 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 9 : 8);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 10);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
|
||||
g_assert_true(triggered);
|
||||
|
||||
triggered = false;
|
||||
|
||||
ptimer_set_count(ptimer, 3);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 4);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
|
||||
g_assert_true(triggered);
|
||||
|
||||
ptimer_stop(ptimer);
|
||||
@ -224,50 +283,82 @@ static void check_periodic(gconstpointer arg)
|
||||
|
||||
qemu_clock_step(2000000);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_set_count(ptimer, 3);
|
||||
ptimer_run(ptimer, 0);
|
||||
|
||||
qemu_clock_step(2000000 * 3 + 100000);
|
||||
qemu_clock_step(2000000 * 3 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
wrap_policy ? 0 : (no_round_down ? 10 : 9));
|
||||
g_assert_true(triggered);
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0));
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_set_count(ptimer, 0);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10);
|
||||
g_assert_true(triggered);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
no_immediate_reload ? 0 : 10);
|
||||
|
||||
if (no_immediate_trigger) {
|
||||
g_assert_false(triggered);
|
||||
} else {
|
||||
g_assert_true(triggered);
|
||||
}
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 * 12 + 100000);
|
||||
qemu_clock_step(1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
|
||||
if (no_immediate_reload) {
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000);
|
||||
|
||||
if (no_immediate_trigger) {
|
||||
g_assert_true(triggered);
|
||||
} else {
|
||||
g_assert_false(triggered);
|
||||
}
|
||||
|
||||
triggered = false;
|
||||
}
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 12);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0));
|
||||
g_assert_true(triggered);
|
||||
|
||||
ptimer_stop(ptimer);
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 * 12 + 100000);
|
||||
qemu_clock_step(2000000 * 10);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0));
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_run(ptimer, 0);
|
||||
ptimer_set_period(ptimer, 0);
|
||||
|
||||
qemu_clock_step(2000000 + 100000);
|
||||
qemu_clock_step(2000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0));
|
||||
g_assert_false(triggered);
|
||||
}
|
||||
|
||||
@ -276,6 +367,8 @@ static void check_on_the_fly_mode_change(gconstpointer arg)
|
||||
const uint8_t *policy = arg;
|
||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||
bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD);
|
||||
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
||||
|
||||
triggered = false;
|
||||
|
||||
@ -283,16 +376,20 @@ static void check_on_the_fly_mode_change(gconstpointer arg)
|
||||
ptimer_set_limit(ptimer, 10, 1);
|
||||
ptimer_run(ptimer, 1);
|
||||
|
||||
qemu_clock_step(2000000 * 9 + 100000);
|
||||
qemu_clock_step(2000000 * 9 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_run(ptimer, 0);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
wrap_policy ? 0 : (no_round_down ? 10 : 9));
|
||||
g_assert_true(triggered);
|
||||
|
||||
triggered = false;
|
||||
@ -301,7 +398,8 @@ static void check_on_the_fly_mode_change(gconstpointer arg)
|
||||
|
||||
ptimer_run(ptimer, 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
(no_round_down ? 1 : 0) + (wrap_policy ? 1 : 0));
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 3);
|
||||
@ -315,6 +413,7 @@ static void check_on_the_fly_period_change(gconstpointer arg)
|
||||
const uint8_t *policy = arg;
|
||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
||||
|
||||
triggered = false;
|
||||
|
||||
@ -322,17 +421,17 @@ static void check_on_the_fly_period_change(gconstpointer arg)
|
||||
ptimer_set_limit(ptimer, 8, 1);
|
||||
ptimer_run(ptimer, 1);
|
||||
|
||||
qemu_clock_step(2000000 * 4 + 100000);
|
||||
qemu_clock_step(2000000 * 4 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_set_period(ptimer, 4000000);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
|
||||
|
||||
qemu_clock_step(4000000 * 2 + 100000);
|
||||
qemu_clock_step(4000000 * 2 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(4000000 * 2);
|
||||
@ -346,6 +445,7 @@ static void check_on_the_fly_freq_change(gconstpointer arg)
|
||||
const uint8_t *policy = arg;
|
||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
||||
|
||||
triggered = false;
|
||||
|
||||
@ -353,17 +453,17 @@ static void check_on_the_fly_freq_change(gconstpointer arg)
|
||||
ptimer_set_limit(ptimer, 8, 1);
|
||||
ptimer_run(ptimer, 1);
|
||||
|
||||
qemu_clock_step(2000000 * 4 + 100000);
|
||||
qemu_clock_step(2000000 * 4 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
|
||||
g_assert_false(triggered);
|
||||
|
||||
ptimer_set_freq(ptimer, 250);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3);
|
||||
|
||||
qemu_clock_step(2000000 * 4 + 100000);
|
||||
qemu_clock_step(2000000 * 4 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 4);
|
||||
@ -394,25 +494,53 @@ static void check_run_with_delta_0(gconstpointer arg)
|
||||
const uint8_t *policy = arg;
|
||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||
bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD);
|
||||
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
||||
bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD);
|
||||
bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
|
||||
|
||||
triggered = false;
|
||||
|
||||
ptimer_set_period(ptimer, 2000000);
|
||||
ptimer_set_limit(ptimer, 99, 0);
|
||||
ptimer_run(ptimer, 1);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99);
|
||||
g_assert_true(triggered);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
no_immediate_reload ? 0 : 99);
|
||||
|
||||
if (no_immediate_trigger) {
|
||||
g_assert_false(triggered);
|
||||
} else {
|
||||
g_assert_true(triggered);
|
||||
}
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 + 100000);
|
||||
if (no_immediate_trigger || no_immediate_reload) {
|
||||
qemu_clock_step(2000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 97);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
no_immediate_reload ? 0 : (no_round_down ? 98 : 97));
|
||||
|
||||
if (no_immediate_trigger && no_immediate_reload) {
|
||||
g_assert_true(triggered);
|
||||
|
||||
triggered = false;
|
||||
} else {
|
||||
g_assert_false(triggered);
|
||||
}
|
||||
|
||||
ptimer_set_count(ptimer, 99);
|
||||
ptimer_run(ptimer, 1);
|
||||
}
|
||||
|
||||
qemu_clock_step(2000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 97);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 2);
|
||||
@ -424,19 +552,42 @@ static void check_run_with_delta_0(gconstpointer arg)
|
||||
|
||||
ptimer_set_count(ptimer, 0);
|
||||
ptimer_run(ptimer, 0);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99);
|
||||
g_assert_true(triggered);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
no_immediate_reload ? 0 : 99);
|
||||
|
||||
if (no_immediate_trigger) {
|
||||
g_assert_false(triggered);
|
||||
} else {
|
||||
g_assert_true(triggered);
|
||||
}
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 + 100000);
|
||||
qemu_clock_step(1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 97);
|
||||
if (no_immediate_reload) {
|
||||
qemu_clock_step(2000000);
|
||||
}
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 99 : 98);
|
||||
|
||||
if (no_immediate_reload && no_immediate_trigger) {
|
||||
g_assert_true(triggered);
|
||||
} else {
|
||||
g_assert_false(triggered);
|
||||
}
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97);
|
||||
g_assert_false(triggered);
|
||||
|
||||
qemu_clock_step(2000000 * 98);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 98);
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==,
|
||||
wrap_policy ? 0 : (no_round_down ? 99 : 98));
|
||||
g_assert_true(triggered);
|
||||
|
||||
ptimer_stop(ptimer);
|
||||
@ -447,21 +598,55 @@ static void check_periodic_with_load_0(gconstpointer arg)
|
||||
const uint8_t *policy = arg;
|
||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||
bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER);
|
||||
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
||||
|
||||
triggered = false;
|
||||
|
||||
ptimer_set_period(ptimer, 2000000);
|
||||
ptimer_run(ptimer, 0);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
|
||||
if (no_immediate_trigger) {
|
||||
g_assert_false(triggered);
|
||||
} else {
|
||||
g_assert_true(triggered);
|
||||
}
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
|
||||
if (continuous_trigger || no_immediate_trigger) {
|
||||
g_assert_true(triggered);
|
||||
} else {
|
||||
g_assert_false(triggered);
|
||||
}
|
||||
|
||||
triggered = false;
|
||||
|
||||
ptimer_set_count(ptimer, 10);
|
||||
ptimer_run(ptimer, 0);
|
||||
|
||||
qemu_clock_step(2000000 * 10 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_true(triggered);
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 + 100000);
|
||||
qemu_clock_step(2000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
if (continuous_trigger) {
|
||||
g_assert_true(triggered);
|
||||
} else {
|
||||
g_assert_false(triggered);
|
||||
}
|
||||
|
||||
ptimer_stop(ptimer);
|
||||
}
|
||||
@ -471,6 +656,7 @@ static void check_oneshot_with_load_0(gconstpointer arg)
|
||||
const uint8_t *policy = arg;
|
||||
QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL);
|
||||
ptimer_state *ptimer = ptimer_init(bh, *policy);
|
||||
bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER);
|
||||
|
||||
triggered = false;
|
||||
|
||||
@ -478,26 +664,30 @@ static void check_oneshot_with_load_0(gconstpointer arg)
|
||||
ptimer_run(ptimer, 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_true(triggered);
|
||||
|
||||
if (no_immediate_trigger) {
|
||||
g_assert_false(triggered);
|
||||
} else {
|
||||
g_assert_true(triggered);
|
||||
}
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 + 100000);
|
||||
qemu_clock_step(2000000 + 1);
|
||||
|
||||
g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0);
|
||||
g_assert_false(triggered);
|
||||
|
||||
triggered = false;
|
||||
|
||||
qemu_clock_step(2000000 + 100000);
|
||||
|
||||
g_assert_false(triggered);
|
||||
if (no_immediate_trigger) {
|
||||
g_assert_true(triggered);
|
||||
} else {
|
||||
g_assert_false(triggered);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_ptimer_tests(uint8_t policy)
|
||||
{
|
||||
uint8_t *ppolicy = g_malloc(1);
|
||||
char *policy_name = g_malloc(64);
|
||||
char *policy_name = g_malloc0(256);
|
||||
|
||||
*ppolicy = policy;
|
||||
|
||||
@ -505,6 +695,26 @@ static void add_ptimer_tests(uint8_t policy)
|
||||
g_sprintf(policy_name, "default");
|
||||
}
|
||||
|
||||
if (policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
|
||||
g_strlcat(policy_name, "wrap_after_one_period,", 256);
|
||||
}
|
||||
|
||||
if (policy & PTIMER_POLICY_CONTINUOUS_TRIGGER) {
|
||||
g_strlcat(policy_name, "continuous_trigger,", 256);
|
||||
}
|
||||
|
||||
if (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) {
|
||||
g_strlcat(policy_name, "no_immediate_trigger,", 256);
|
||||
}
|
||||
|
||||
if (policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD) {
|
||||
g_strlcat(policy_name, "no_immediate_reload,", 256);
|
||||
}
|
||||
|
||||
if (policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) {
|
||||
g_strlcat(policy_name, "no_counter_rounddown,", 256);
|
||||
}
|
||||
|
||||
g_test_add_data_func(
|
||||
g_strdup_printf("/ptimer/set_count policy=%s", policy_name),
|
||||
ppolicy, check_set_count);
|
||||
@ -550,6 +760,16 @@ static void add_ptimer_tests(uint8_t policy)
|
||||
ppolicy, check_oneshot_with_load_0);
|
||||
}
|
||||
|
||||
static void add_all_ptimer_policies_comb_tests(void)
|
||||
{
|
||||
int last_policy = PTIMER_POLICY_NO_COUNTER_ROUND_DOWN;
|
||||
int policy = PTIMER_POLICY_DEFAULT;
|
||||
|
||||
for (; policy < (last_policy << 1); policy++) {
|
||||
add_ptimer_tests(policy);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
@ -560,7 +780,7 @@ int main(int argc, char **argv)
|
||||
main_loop_tlg.tl[i] = g_new0(QEMUTimerList, 1);
|
||||
}
|
||||
|
||||
add_ptimer_tests(PTIMER_POLICY_DEFAULT);
|
||||
add_all_ptimer_policies_comb_tests();
|
||||
|
||||
qtest_allowed = true;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* QTest testcase for the ptimer
|
||||
*
|
||||
* Author: Dmitry Osipenko <digetx@gmail.com>
|
||||
* Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
|
1105
tests/test-arm-mptimer.c
Normal file
1105
tests/test-arm-mptimer.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -97,25 +97,24 @@ typedef struct PageDesc {
|
||||
#define V_L2_BITS 10
|
||||
#define V_L2_SIZE (1 << V_L2_BITS)
|
||||
|
||||
/* The bits remaining after N lower levels of page tables. */
|
||||
#define V_L1_BITS_REM \
|
||||
((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS)
|
||||
|
||||
#if V_L1_BITS_REM < 4
|
||||
#define V_L1_BITS (V_L1_BITS_REM + V_L2_BITS)
|
||||
#else
|
||||
#define V_L1_BITS V_L1_BITS_REM
|
||||
#endif
|
||||
|
||||
#define V_L1_SIZE ((target_ulong)1 << V_L1_BITS)
|
||||
|
||||
#define V_L1_SHIFT (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - V_L1_BITS)
|
||||
|
||||
uintptr_t qemu_host_page_size;
|
||||
intptr_t qemu_host_page_mask;
|
||||
|
||||
/* The bottom level has pointers to PageDesc */
|
||||
static void *l1_map[V_L1_SIZE];
|
||||
/*
|
||||
* L1 Mapping properties
|
||||
*/
|
||||
static int v_l1_size;
|
||||
static int v_l1_shift;
|
||||
static int v_l2_levels;
|
||||
|
||||
/* The bottom level has pointers to PageDesc, and is indexed by
|
||||
* anything from 4 to (V_L2_BITS + 3) bits, depending on target page size.
|
||||
*/
|
||||
#define V_L1_MIN_BITS 4
|
||||
#define V_L1_MAX_BITS (V_L2_BITS + 3)
|
||||
#define V_L1_MAX_SIZE (1 << V_L1_MAX_BITS)
|
||||
|
||||
static void *l1_map[V_L1_MAX_SIZE];
|
||||
|
||||
/* code generation context */
|
||||
TCGContext tcg_ctx;
|
||||
@ -125,6 +124,26 @@ TCGContext tcg_ctx;
|
||||
__thread int have_tb_lock;
|
||||
#endif
|
||||
|
||||
static void page_table_config_init(void)
|
||||
{
|
||||
uint32_t v_l1_bits;
|
||||
|
||||
assert(TARGET_PAGE_BITS);
|
||||
/* The bits remaining after N lower levels of page tables. */
|
||||
v_l1_bits = (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS;
|
||||
if (v_l1_bits < V_L1_MIN_BITS) {
|
||||
v_l1_bits += V_L2_BITS;
|
||||
}
|
||||
|
||||
v_l1_size = 1 << v_l1_bits;
|
||||
v_l1_shift = L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - v_l1_bits;
|
||||
v_l2_levels = v_l1_shift / V_L2_BITS - 1;
|
||||
|
||||
assert(v_l1_bits <= V_L1_MAX_BITS);
|
||||
assert(v_l1_shift % V_L2_BITS == 0);
|
||||
assert(v_l2_levels >= 0);
|
||||
}
|
||||
|
||||
void tb_lock(void)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
@ -332,6 +351,8 @@ void page_size_init(void)
|
||||
static void page_init(void)
|
||||
{
|
||||
page_size_init();
|
||||
page_table_config_init();
|
||||
|
||||
#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY)
|
||||
{
|
||||
#ifdef HAVE_KINFO_GETVMMAP
|
||||
@ -408,10 +429,10 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
|
||||
int i;
|
||||
|
||||
/* Level 1. Always allocated. */
|
||||
lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1));
|
||||
lp = l1_map + ((index >> v_l1_shift) & (v_l1_size - 1));
|
||||
|
||||
/* Level 2..N-1. */
|
||||
for (i = V_L1_SHIFT / V_L2_BITS - 1; i > 0; i--) {
|
||||
for (i = v_l2_levels; i > 0; i--) {
|
||||
void **p = atomic_rcu_read(lp);
|
||||
|
||||
if (p == NULL) {
|
||||
@ -826,10 +847,10 @@ static void page_flush_tb_1(int level, void **lp)
|
||||
|
||||
static void page_flush_tb(void)
|
||||
{
|
||||
int i;
|
||||
int i, l1_sz = v_l1_size;
|
||||
|
||||
for (i = 0; i < V_L1_SIZE; i++) {
|
||||
page_flush_tb_1(V_L1_SHIFT / V_L2_BITS - 1, l1_map + i);
|
||||
for (i = 0; i < l1_sz; i++) {
|
||||
page_flush_tb_1(v_l2_levels, l1_map + i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1883,16 +1904,16 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data,
|
||||
int walk_memory_regions(void *priv, walk_memory_regions_fn fn)
|
||||
{
|
||||
struct walk_memory_regions_data data;
|
||||
uintptr_t i;
|
||||
uintptr_t i, l1_sz = v_l1_size;
|
||||
|
||||
data.fn = fn;
|
||||
data.priv = priv;
|
||||
data.start = -1u;
|
||||
data.prot = 0;
|
||||
|
||||
for (i = 0; i < V_L1_SIZE; i++) {
|
||||
int rc = walk_memory_regions_1(&data, (target_ulong)i << (V_L1_SHIFT + TARGET_PAGE_BITS),
|
||||
V_L1_SHIFT / V_L2_BITS - 1, l1_map + i);
|
||||
for (i = 0; i < l1_sz; i++) {
|
||||
target_ulong base = i << (v_l1_shift + TARGET_PAGE_BITS);
|
||||
int rc = walk_memory_regions_1(&data, base, v_l2_levels, l1_map + i);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
10
vl.c
10
vl.c
@ -4088,6 +4088,16 @@ int main(int argc, char **argv, char **envp)
|
||||
}
|
||||
object_property_add_child(object_get_root(), "machine",
|
||||
OBJECT(current_machine), &error_abort);
|
||||
|
||||
if (machine_class->minimum_page_bits) {
|
||||
if (!set_preferred_target_page_bits(machine_class->minimum_page_bits)) {
|
||||
/* This would be a board error: specifying a minimum smaller than
|
||||
* a target's compile-time fixed setting.
|
||||
*/
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
cpu_exec_init_all();
|
||||
|
||||
if (machine_class->hw_version) {
|
||||
|
Loading…
Reference in New Issue
Block a user