From 314e298735903035ba2b7b0f3cf39981f4171546 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sun, 11 Sep 2011 20:22:05 +0000 Subject: [PATCH 01/87] memory: simple memory tree printer Add a monitor command 'info mtree' to show the memory hierarchy much like /proc/iomem in Linux. Signed-off-by: Blue Swirl Signed-off-by: Avi Kivity --- memory.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ memory.h | 2 ++ monitor.c | 13 ++++++++ 3 files changed, 106 insertions(+) diff --git a/memory.c b/memory.c index 71e769e8b8..a85d118890 100644 --- a/memory.c +++ b/memory.c @@ -1271,3 +1271,94 @@ void set_system_io_map(MemoryRegion *mr) address_space_io.root = mr; memory_region_update_topology(); } + +typedef struct MemoryRegionList MemoryRegionList; + +struct MemoryRegionList { + const MemoryRegion *mr; + bool printed; + QTAILQ_ENTRY(MemoryRegionList) queue; +}; + +typedef QTAILQ_HEAD(queue, MemoryRegionList) MemoryRegionListHead; + +static void mtree_print_mr(fprintf_function mon_printf, void *f, + const MemoryRegion *mr, unsigned int level, + target_phys_addr_t base, + MemoryRegionListHead *print_queue) +{ + const MemoryRegion *submr; + unsigned int i; + + + if (!mr) { + return; + } + + for (i = 0; i < level; i++) { + mon_printf(f, " "); + } + + if (mr->alias) { + MemoryRegionList *ml; + bool found = false; + + /* check if the alias is already in the queue */ + QTAILQ_FOREACH(ml, print_queue, queue) { + if (ml->mr == mr->alias && !ml->printed) { + found = true; + } + } + + if (!found) { + ml = g_new(MemoryRegionList, 1); + ml->mr = mr->alias; + ml->printed = false; + QTAILQ_INSERT_TAIL(print_queue, ml, queue); + } + mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " : alias %s @%s " + TARGET_FMT_plx "-" TARGET_FMT_plx "\n", + base + mr->addr, + base + mr->addr + (target_phys_addr_t)mr->size - 1, + mr->name, + mr->alias->name, + mr->alias_offset, + mr->alias_offset + (target_phys_addr_t)mr->size - 1); + } else { + mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " : %s\n", + base + mr->addr, + base + mr->addr + (target_phys_addr_t)mr->size - 1, + mr->name); + } + QTAILQ_FOREACH(submr, &mr->subregions, subregions_link) { + mtree_print_mr(mon_printf, f, submr, level + 1, base + mr->addr, + print_queue); + } +} + +void mtree_info(fprintf_function mon_printf, void *f) +{ + MemoryRegionListHead ml_head; + MemoryRegionList *ml, *ml2; + + QTAILQ_INIT(&ml_head); + + mon_printf(f, "memory\n"); + mtree_print_mr(mon_printf, f, address_space_memory.root, 0, 0, &ml_head); + + /* print aliased regions */ + QTAILQ_FOREACH(ml, &ml_head, queue) { + if (!ml->printed) { + mon_printf(f, "%s\n", ml->mr->name); + mtree_print_mr(mon_printf, f, ml->mr, 0, 0, &ml_head); + } + } + + QTAILQ_FOREACH_SAFE(ml, &ml_head, queue, ml2) { + g_free(ml2); + } + + QTAILQ_INIT(&ml_head); + mon_printf(f, "I/O\n"); + mtree_print_mr(mon_printf, f, address_space_io.root, 0, 0, &ml_head); +} diff --git a/memory.h b/memory.h index e93e65a4f2..d5b47da839 100644 --- a/memory.h +++ b/memory.h @@ -501,6 +501,8 @@ void memory_region_transaction_begin(void); */ void memory_region_transaction_commit(void); +void mtree_info(fprintf_function mon_printf, void *f); + #endif #endif diff --git a/monitor.c b/monitor.c index 8ec2c5efea..d323ea5851 100644 --- a/monitor.c +++ b/monitor.c @@ -63,6 +63,7 @@ #endif #include "trace/control.h" #include "ui/qemu-spice.h" +#include "memory.h" //#define DEBUG //#define DEBUG_COMPLETION @@ -2470,6 +2471,11 @@ static void tlb_info(Monitor *mon) } #endif +static void do_info_mtree(Monitor *mon) +{ + mtree_info((fprintf_function)monitor_printf, mon); +} + static void do_info_kvm_print(Monitor *mon, const QObject *data) { QDict *qdict; @@ -2977,6 +2983,13 @@ static const mon_cmd_t info_cmds[] = { .mhandler.info = mem_info, }, #endif + { + .name = "mtree", + .args_type = "", + .params = "", + .help = "show memory tree", + .mhandler.info = do_info_mtree, + }, { .name = "jit", .args_type = "", From 4b474ba7a1a96aabcf3482eb5b23ad5361a38750 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 27 Sep 2011 15:00:31 +0200 Subject: [PATCH 02/87] memory: Print region priority Useful to discover eclipses. Signed-off-by: Jan Kiszka Signed-off-by: Avi Kivity --- memory.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/memory.c b/memory.c index a85d118890..eae67be453 100644 --- a/memory.c +++ b/memory.c @@ -1316,18 +1316,20 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, ml->printed = false; QTAILQ_INSERT_TAIL(print_queue, ml, queue); } - mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " : alias %s @%s " + mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d): alias %s @%s " TARGET_FMT_plx "-" TARGET_FMT_plx "\n", base + mr->addr, base + mr->addr + (target_phys_addr_t)mr->size - 1, + mr->priority, mr->name, mr->alias->name, mr->alias_offset, mr->alias_offset + (target_phys_addr_t)mr->size - 1); } else { - mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " : %s\n", + mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d): %s\n", base + mr->addr, base + mr->addr + (target_phys_addr_t)mr->size - 1, + mr->priority, mr->name); } QTAILQ_FOREACH(submr, &mr->subregions, subregions_link) { From 066318109fa2f377e5097f115a23f85c53f1a674 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 27 Sep 2011 15:00:38 +0200 Subject: [PATCH 03/87] memory: Do not print empty PIO root Signed-off-by: Jan Kiszka Signed-off-by: Avi Kivity --- memory.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/memory.c b/memory.c index eae67be453..19f1d36bd4 100644 --- a/memory.c +++ b/memory.c @@ -1360,7 +1360,10 @@ void mtree_info(fprintf_function mon_printf, void *f) g_free(ml2); } - QTAILQ_INIT(&ml_head); - mon_printf(f, "I/O\n"); - mtree_print_mr(mon_printf, f, address_space_io.root, 0, 0, &ml_head); + if (address_space_io.root && + !QTAILQ_EMPTY(&address_space_io.root->subregions)) { + QTAILQ_INIT(&ml_head); + mon_printf(f, "I/O\n"); + mtree_print_mr(mon_printf, f, address_space_io.root, 0, 0, &ml_head); + } } From 9479c57a8c1e6c33f171ca7c93fea84567045833 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 27 Sep 2011 15:00:41 +0200 Subject: [PATCH 04/87] memory: Print regions in ascending order Makes reading the output more user friendly. Signed-off-by: Jan Kiszka Signed-off-by: Avi Kivity --- memory.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/memory.c b/memory.c index 19f1d36bd4..f46e6268c2 100644 --- a/memory.c +++ b/memory.c @@ -1285,12 +1285,13 @@ typedef QTAILQ_HEAD(queue, MemoryRegionList) MemoryRegionListHead; static void mtree_print_mr(fprintf_function mon_printf, void *f, const MemoryRegion *mr, unsigned int level, target_phys_addr_t base, - MemoryRegionListHead *print_queue) + MemoryRegionListHead *alias_print_queue) { + MemoryRegionList *new_ml, *ml, *next_ml; + MemoryRegionListHead submr_print_queue; const MemoryRegion *submr; unsigned int i; - if (!mr) { return; } @@ -1304,7 +1305,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, bool found = false; /* check if the alias is already in the queue */ - QTAILQ_FOREACH(ml, print_queue, queue) { + QTAILQ_FOREACH(ml, alias_print_queue, queue) { if (ml->mr == mr->alias && !ml->printed) { found = true; } @@ -1314,7 +1315,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, ml = g_new(MemoryRegionList, 1); ml->mr = mr->alias; ml->printed = false; - QTAILQ_INSERT_TAIL(print_queue, ml, queue); + QTAILQ_INSERT_TAIL(alias_print_queue, ml, queue); } mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d): alias %s @%s " TARGET_FMT_plx "-" TARGET_FMT_plx "\n", @@ -1332,9 +1333,33 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, mr->priority, mr->name); } + + QTAILQ_INIT(&submr_print_queue); + QTAILQ_FOREACH(submr, &mr->subregions, subregions_link) { - mtree_print_mr(mon_printf, f, submr, level + 1, base + mr->addr, - print_queue); + new_ml = g_new(MemoryRegionList, 1); + new_ml->mr = submr; + QTAILQ_FOREACH(ml, &submr_print_queue, queue) { + if (new_ml->mr->addr < ml->mr->addr || + (new_ml->mr->addr == ml->mr->addr && + new_ml->mr->priority > ml->mr->priority)) { + QTAILQ_INSERT_BEFORE(ml, new_ml, queue); + new_ml = NULL; + break; + } + } + if (new_ml) { + QTAILQ_INSERT_TAIL(&submr_print_queue, new_ml, queue); + } + } + + QTAILQ_FOREACH(ml, &submr_print_queue, queue) { + mtree_print_mr(mon_printf, f, ml->mr, level + 1, base + mr->addr, + alias_print_queue); + } + + QTAILQ_FOREACH_SAFE(next_ml, &submr_print_queue, queue, ml) { + g_free(ml); } } From d11cf8cc80d946dfc9a23597cd9a0bb1c487cfa7 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Mon, 3 Oct 2011 10:20:13 +0200 Subject: [PATCH 05/87] etrax-dma: Remove bogus if statement Reported-by: Stefan Weil Signed-off-by: Edgar E. Iglesias --- hw/etraxfs_dma.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/etraxfs_dma.c b/hw/etraxfs_dma.c index d3082acc8f..5ca8253ae2 100644 --- a/hw/etraxfs_dma.c +++ b/hw/etraxfs_dma.c @@ -599,12 +599,10 @@ dma_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value) static void dma_update_state(struct fs_dma_ctrl *ctrl, int c) { - if ((ctrl->channels[c].regs[RW_CFG] & 1) != 3) { - if (ctrl->channels[c].regs[RW_CFG] & 2) - ctrl->channels[c].state = STOPPED; - if (!(ctrl->channels[c].regs[RW_CFG] & 1)) - ctrl->channels[c].state = RST; - } + if (ctrl->channels[c].regs[RW_CFG] & 2) + ctrl->channels[c].state = STOPPED; + if (!(ctrl->channels[c].regs[RW_CFG] & 1)) + ctrl->channels[c].state = RST; } static void From c45a81682d6d15b7ec82ed90c34a537de66dea55 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Sun, 2 Oct 2011 08:44:37 -0500 Subject: [PATCH 06/87] hmp: re-enable trace-file command Commit 31965ae27bc11e90674be12584bb201b83df5aef reverted a previous renaming of CONFIG_SIMPLE_TRACE->CONFIG_TRACE_SIMPLE in a couple spots, leading to trace-file currently being unavailable. Signed-off-by: Michael Roth Signed-off-by: Stefan Hajnoczi --- hmp-commands.hx | 2 +- monitor.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 9e1cca8e3d..844e6c91e7 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -194,7 +194,7 @@ STEXI changes status of a trace event ETEXI -#if defined(CONFIG_SIMPLE_TRACE) +#if defined(CONFIG_TRACE_SIMPLE) { .name = "trace-file", .args_type = "op:s?,arg:F?", diff --git a/monitor.c b/monitor.c index d323ea5851..08f6197863 100644 --- a/monitor.c +++ b/monitor.c @@ -606,7 +606,7 @@ static void do_trace_event_set_state(Monitor *mon, const QDict *qdict) } } -#ifdef CONFIG_SIMPLE_TRACE +#ifdef CONFIG_TRACE_SIMPLE static void do_trace_file(Monitor *mon, const QDict *qdict) { const char *op = qdict_get_try_str(qdict, "op"); From 28dcee10c54984449259a13c6f27bf98c4770f3c Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 22 Sep 2011 20:14:12 +0100 Subject: [PATCH 07/87] trace: trace bdrv_open_common() bdrv_open_common() is a useful point to trace since it reveals the filename and block driver for a given BlockDriverState. Signed-off-by: Stefan Hajnoczi --- block.c | 2 ++ trace-events | 1 + 2 files changed, 3 insertions(+) diff --git a/block.c b/block.c index e3fe97f275..1ae22d5d2a 100644 --- a/block.c +++ b/block.c @@ -475,6 +475,8 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename, assert(drv != NULL); + trace_bdrv_open_common(bs, filename, flags, drv->format_name); + bs->file = NULL; bs->total_sectors = 0; bs->encrypted = 0; diff --git a/trace-events b/trace-events index b7ddf14bd8..f408564775 100644 --- a/trace-events +++ b/trace-events @@ -56,6 +56,7 @@ virtio_console_chr_read(unsigned int port, int size) "port %u, size %d" virtio_console_chr_event(unsigned int port, int event) "port %u, event %d" # block.c +bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags %#x format_name \"%s\"" multiwrite_cb(void *mcb, int ret) "mcb %p ret %d" bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d" bdrv_aio_multiwrite_earlyfail(void *mcb) "mcb %p" From 89bd820acb477b2c41f13977a3ceb4538fadcd02 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 23 Sep 2011 08:23:06 +0100 Subject: [PATCH 08/87] trace: trace monitor qmp dispatch/completion Add trace events for handle_qmp_command(), which dispatches qmp commands, and monitor_protocol_emitter(), which produces the reply to a qmp command. Also remove duplicate #include "trace/control.h". Signed-off-by: Stefan Hajnoczi --- monitor.c | 5 ++++- trace-events | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/monitor.c b/monitor.c index 08f6197863..cf59442754 100644 --- a/monitor.c +++ b/monitor.c @@ -57,11 +57,11 @@ #include "json-parser.h" #include "osdep.h" #include "cpu.h" +#include "trace.h" #include "trace/control.h" #ifdef CONFIG_TRACE_SIMPLE #include "trace/simple.h" #endif -#include "trace/control.h" #include "ui/qemu-spice.h" #include "memory.h" @@ -370,6 +370,8 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) { QDict *qmp; + trace_monitor_protocol_emitter(mon); + qmp = qdict_new(); if (!monitor_has_error(mon)) { @@ -5102,6 +5104,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) qobject_incref(mon->mc->id); cmd_name = qdict_get_str(input, "execute"); + trace_handle_qmp_command(mon, cmd_name); if (invalid_qmp_mode(mon, cmd_name)) { qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); goto err_out; diff --git a/trace-events b/trace-events index f408564775..c9fd8e5ad5 100644 --- a/trace-events +++ b/trace-events @@ -534,3 +534,7 @@ esp_mem_writeb_cmd_sel(uint32_t val) "Select without ATN (%2.2x)" esp_mem_writeb_cmd_selatn(uint32_t val) "Select with ATN (%2.2x)" esp_mem_writeb_cmd_selatns(uint32_t val) "Select with ATN & stop (%2.2x)" esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (%2.2x)" + +# monitor.c +handle_qmp_command(void *mon, const char *cmd_name) "mon %p cmd_name \"%s\"" +monitor_protocol_emitter(void *mon) "mon %p" From 59370aaa5693d57c350074e537f2627d5b9d446c Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 30 Sep 2011 17:34:58 +0100 Subject: [PATCH 09/87] trace: add arguments to bdrv_co_io_em() trace event It is useful to know the BlockDriverState as well as the sector_num/nb_sectors of an emulated .bdrv_co_*() request. Signed-off-by: Stefan Hajnoczi --- block.c | 2 +- trace-events | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index 1ae22d5d2a..e865fab27e 100644 --- a/block.c +++ b/block.c @@ -2999,7 +2999,7 @@ static int coroutine_fn bdrv_co_io_em(BlockDriverState *bs, int64_t sector_num, bdrv_co_io_em_complete, &co); } - trace_bdrv_co_io(is_write, acb); + trace_bdrv_co_io_em(bs, sector_num, nb_sectors, is_write, acb); if (!acb) { return -EIO; } diff --git a/trace-events b/trace-events index c9fd8e5ad5..9528c04fed 100644 --- a/trace-events +++ b/trace-events @@ -67,7 +67,7 @@ bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" -bdrv_co_io(int is_write, void *acb) "is_write %d acb %p" +bdrv_co_io_em(void *bs, int64_t sector_num, int nb_sectors, int is_write, void *acb) "bs %p sector_num %"PRId64" nb_sectors %d is_write %d acb %p" # hw/virtio-blk.c virtio_blk_req_complete(void *req, int status) "req %p status %d" From 27f930c5f0c26fbb845d8071e3bca5f127891ecd Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 16 Sep 2011 00:26:05 +0200 Subject: [PATCH 10/87] lm32: add missing qemu_init_vcpu() call Signed-off-by: Michael Walle --- target-lm32/helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target-lm32/helper.c b/target-lm32/helper.c index 014fd8de06..fc0b444d81 100644 --- a/target-lm32/helper.c +++ b/target-lm32/helper.c @@ -218,6 +218,7 @@ CPUState *cpu_lm32_init(const char *cpu_model) cpu_exec_init(env); cpu_reset(env); + qemu_init_vcpu(env); if (!tcg_initialized) { tcg_initialized = 1; From fcfa339778a624dd67bb2def664ec6aabb497ba8 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 11 Aug 2011 00:13:23 +0200 Subject: [PATCH 11/87] milkymist_uart: support new core version The new version of the uart core introduces status and control bits. Signed-off-by: Michael Walle --- hw/milkymist-hw.h | 5 ++-- hw/milkymist-uart.c | 72 +++++++++++++++++++++++++++++++++++++++------ hw/milkymist.c | 2 +- trace-events | 4 +-- 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/hw/milkymist-hw.h b/hw/milkymist-hw.h index 20de68ecce..9f358a7d69 100644 --- a/hw/milkymist-hw.h +++ b/hw/milkymist-hw.h @@ -5,15 +5,14 @@ #include "qdev-addr.h" static inline DeviceState *milkymist_uart_create(target_phys_addr_t base, - qemu_irq rx_irq, qemu_irq tx_irq) + qemu_irq irq) { DeviceState *dev; dev = qdev_create(NULL, "milkymist-uart"); qdev_init_nofail(dev); sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); return dev; } diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c index e8e309de7c..d836462e4b 100644 --- a/hw/milkymist-uart.c +++ b/hw/milkymist-uart.c @@ -30,19 +30,53 @@ enum { R_RXTX = 0, R_DIV, + R_STAT, + R_CTRL, + R_DBG, R_MAX }; +enum { + STAT_THRE = (1<<0), + STAT_RX_EVT = (1<<1), + STAT_TX_EVT = (1<<2), +}; + +enum { + CTRL_RX_IRQ_EN = (1<<0), + CTRL_TX_IRQ_EN = (1<<1), + CTRL_THRU_EN = (1<<2), +}; + +enum { + DBG_BREAK_EN = (1<<0), +}; + struct MilkymistUartState { SysBusDevice busdev; CharDriverState *chr; - qemu_irq rx_irq; - qemu_irq tx_irq; + qemu_irq irq; uint32_t regs[R_MAX]; }; typedef struct MilkymistUartState MilkymistUartState; +static void uart_update_irq(MilkymistUartState *s) +{ + int rx_event = s->regs[R_STAT] & STAT_RX_EVT; + int tx_event = s->regs[R_STAT] & STAT_TX_EVT; + int rx_irq_en = s->regs[R_CTRL] & CTRL_RX_IRQ_EN; + int tx_irq_en = s->regs[R_CTRL] & CTRL_TX_IRQ_EN; + + if ((rx_irq_en && rx_event) || (tx_irq_en && tx_event)) { + trace_milkymist_uart_raise_irq(); + qemu_irq_raise(s->irq); + } else { + trace_milkymist_uart_lower_irq(); + qemu_irq_lower(s->irq); + } +} + static uint32_t uart_read(void *opaque, target_phys_addr_t addr) { MilkymistUartState *s = opaque; @@ -51,7 +85,12 @@ static uint32_t uart_read(void *opaque, target_phys_addr_t addr) addr >>= 2; switch (addr) { case R_RXTX: + r = s->regs[addr]; + break; case R_DIV: + case R_STAT: + case R_CTRL: + case R_DBG: r = s->regs[addr]; break; @@ -79,18 +118,26 @@ static void uart_write(void *opaque, target_phys_addr_t addr, uint32_t value) if (s->chr) { qemu_chr_fe_write(s->chr, &ch, 1); } - trace_milkymist_uart_pulse_irq_tx(); - qemu_irq_pulse(s->tx_irq); + s->regs[R_STAT] |= STAT_TX_EVT; break; case R_DIV: + case R_CTRL: + case R_DBG: s->regs[addr] = value; break; + case R_STAT: + /* write one to clear bits */ + s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT)); + break; + default: error_report("milkymist_uart: write access to unknown register 0x" TARGET_FMT_plx, addr << 2); break; } + + uart_update_irq(s); } static CPUReadMemoryFunc * const uart_read_fn[] = { @@ -109,14 +156,19 @@ static void uart_rx(void *opaque, const uint8_t *buf, int size) { MilkymistUartState *s = opaque; + assert(!(s->regs[R_STAT] & STAT_RX_EVT)); + + s->regs[R_STAT] |= STAT_RX_EVT; s->regs[R_RXTX] = *buf; - trace_milkymist_uart_pulse_irq_rx(); - qemu_irq_pulse(s->rx_irq); + + uart_update_irq(s); } static int uart_can_rx(void *opaque) { - return 1; + MilkymistUartState *s = opaque; + + return !(s->regs[R_STAT] & STAT_RX_EVT); } static void uart_event(void *opaque, int event) @@ -131,6 +183,9 @@ static void milkymist_uart_reset(DeviceState *d) for (i = 0; i < R_MAX; i++) { s->regs[i] = 0; } + + /* THRE is always set */ + s->regs[R_STAT] = STAT_THRE; } static int milkymist_uart_init(SysBusDevice *dev) @@ -138,8 +193,7 @@ static int milkymist_uart_init(SysBusDevice *dev) MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev); int uart_regs; - sysbus_init_irq(dev, &s->rx_irq); - sysbus_init_irq(dev, &s->tx_irq); + sysbus_init_irq(dev, &s->irq); uart_regs = cpu_register_io_memory(uart_read_fn, uart_write_fn, s, DEVICE_NATIVE_ENDIAN); diff --git a/hw/milkymist.c b/hw/milkymist.c index bca0a58d8c..6d99260b20 100644 --- a/hw/milkymist.c +++ b/hw/milkymist.c @@ -146,7 +146,7 @@ milkymist_init(ram_addr_t ram_size_not_used, exit(1); } - milkymist_uart_create(0x60000000, irq[0], irq[1]); + milkymist_uart_create(0x60000000, irq[0]); milkymist_sysctl_create(0x60001000, irq[2], irq[3], irq[4], 80000000, 0x10014d31, 0x0000041f, 0x00000001); milkymist_hpdmc_create(0x60002000); diff --git a/trace-events b/trace-events index b7ddf14bd8..1fc1858ec0 100644 --- a/trace-events +++ b/trace-events @@ -444,8 +444,8 @@ milkymist_tmu2_pulse_irq(void) "Pulse IRQ" # hw/milkymist-uart.c milkymist_uart_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" milkymist_uart_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" -milkymist_uart_pulse_irq_rx(void) "Pulse IRQ RX" -milkymist_uart_pulse_irq_tx(void) "Pulse IRQ TX" +milkymist_uart_raise_irq(void) "Raise IRQ" +milkymist_uart_lower_irq(void) "Lower IRQ" # hw/milkymist-vgafb.c milkymist_vgafb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" From 779277cab4ab748dea3dfc8c8e035c7a40523e1b Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 16 Sep 2011 00:48:34 +0200 Subject: [PATCH 12/87] milkymist: new interrupt map Due to the new uart core version the interrupt mapping has changed. Signed-off-by: Michael Walle --- hw/milkymist.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/milkymist.c b/hw/milkymist.c index 6d99260b20..b7a8c1c256 100644 --- a/hw/milkymist.c +++ b/hw/milkymist.c @@ -147,16 +147,16 @@ milkymist_init(ram_addr_t ram_size_not_used, } milkymist_uart_create(0x60000000, irq[0]); - milkymist_sysctl_create(0x60001000, irq[2], irq[3], irq[4], + milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3], 80000000, 0x10014d31, 0x0000041f, 0x00000001); milkymist_hpdmc_create(0x60002000); milkymist_vgafb_create(0x60003000, 0x40000000, 0x0fffffff); milkymist_memcard_create(0x60004000); - milkymist_ac97_create(0x60005000, irq[5], irq[6], irq[7], irq[8]); - milkymist_pfpu_create(0x60006000, irq[9]); - milkymist_tmu2_create(0x60007000, irq[10]); - milkymist_minimac2_create(0x60008000, 0x30000000, irq[11], irq[12]); - milkymist_softusb_create(0x6000f000, irq[17], + milkymist_ac97_create(0x60005000, irq[4], irq[5], irq[6], irq[7]); + milkymist_pfpu_create(0x60006000, irq[8]); + milkymist_tmu2_create(0x60007000, irq[9]); + milkymist_minimac2_create(0x60008000, 0x30000000, irq[10], irq[11]); + milkymist_softusb_create(0x6000f000, irq[15], 0x20000000, 0x1000, 0x20020000, 0x2000); /* make sure juart isn't the first chardev */ From e4fc8781db7c49b0c5ac5d24762e17c59dfe0871 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 30 Sep 2011 11:39:11 +0100 Subject: [PATCH 13/87] qed: fix use-after-free during l2 cache commit QED's metadata caching strategy allows two parallel requests to race for metadata lookup. The first one to complete will populate the metadata cache and the second one will drop the data it just read in favor of the cached data. There is a use-after-free in qed_read_l2_table_cb() and qed_commit_l2_update() where l2_table->offset was used after the l2_table may have been freed due to a metadata lookup race. Fix this by keeping the l2_offset in a local variable and not reaching into the possibly freed l2_table. Reported-by: Amit Shah Signed-off-by: Stefan Hajnoczi Signed-off-by: Anthony Liguori --- block/qed-table.c | 6 +++--- block/qed.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/block/qed-table.c b/block/qed-table.c index d96afa81d7..f31f9ff069 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -222,21 +222,21 @@ static void qed_read_l2_table_cb(void *opaque, int ret) QEDRequest *request = read_l2_table_cb->request; BDRVQEDState *s = read_l2_table_cb->s; CachedL2Table *l2_table = request->l2_table; + uint64_t l2_offset = read_l2_table_cb->l2_offset; if (ret) { /* can't trust loaded L2 table anymore */ qed_unref_l2_cache_entry(l2_table); request->l2_table = NULL; } else { - l2_table->offset = read_l2_table_cb->l2_offset; + l2_table->offset = l2_offset; qed_commit_l2_cache_entry(&s->l2_cache, l2_table); /* This is guaranteed to succeed because we just committed the entry * to the cache. */ - request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, - l2_table->offset); + request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); assert(request->l2_table != NULL); } diff --git a/block/qed.c b/block/qed.c index 624e261b35..e87dc4decf 100644 --- a/block/qed.c +++ b/block/qed.c @@ -911,14 +911,14 @@ static void qed_commit_l2_update(void *opaque, int ret) QEDAIOCB *acb = opaque; BDRVQEDState *s = acb_to_s(acb); CachedL2Table *l2_table = acb->request.l2_table; + uint64_t l2_offset = l2_table->offset; qed_commit_l2_cache_entry(&s->l2_cache, l2_table); /* This is guaranteed to succeed because we just committed the entry to the * cache. */ - acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, - l2_table->offset); + acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); assert(acb->request.l2_table != NULL); qed_aio_next_io(opaque, ret); From 277f9acf79bbf3affb98200c92a4aedaa3234083 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 26 May 2011 11:52:44 +0200 Subject: [PATCH 14/87] spapr: proper qdevification Right now the spapr devices cannot be instantiated with -device, because the IRQs need to be passed to the spapr_*_create functions. Do this instead in the bus's init wrapper. This is particularly important with the conversion from scsi-disk to scsi-{cd,hd} that Markus made. After his patches, if you specify a scsi-cd device attached to an if=none drive, the default VSCSI controller will not be created and, without qdevification, you will not be able to add yours. NOTE from agraf: added small compile fix Signed-off-by: Paolo Bonzini Cc: Alexander Graf Cc: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 15 +++++---------- hw/spapr.h | 8 ++++++++ hw/spapr_llan.c | 7 +------ hw/spapr_vio.c | 5 +++++ hw/spapr_vio.h | 13 ++++--------- hw/spapr_vscsi.c | 8 +------- hw/spapr_vty.c | 8 +------- 7 files changed, 25 insertions(+), 39 deletions(-) diff --git a/hw/spapr.c b/hw/spapr.c index 1265cee6d9..8cf93fe51e 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -298,7 +298,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, long kernel_size, initrd_size, fw_size; long pteg_shift = 17; char *filename; - int irq = 16; spapr = g_malloc(sizeof(*spapr)); cpu_ppc_hypercall = emulate_spapr_hypercall; @@ -360,15 +359,14 @@ static void ppc_spapr_init(ram_addr_t ram_size, /* Set up VIO bus */ spapr->vio_bus = spapr_vio_bus_init(); - for (i = 0; i < MAX_SERIAL_PORTS; i++, irq++) { + for (i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { spapr_vty_create(spapr->vio_bus, SPAPR_VTY_BASE_ADDRESS + i, - serial_hds[i], xics_find_qirq(spapr->icp, irq), - irq); + serial_hds[i]); } } - for (i = 0; i < nb_nics; i++, irq++) { + for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; if (!nd->model) { @@ -376,8 +374,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, } if (strcmp(nd->model, "ibmveth") == 0) { - spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd, - xics_find_qirq(spapr->icp, irq), irq); + spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd); } else { fprintf(stderr, "pSeries (sPAPR) platform does not support " "NIC model '%s' (only ibmveth is supported)\n", @@ -387,9 +384,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, } for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) { - spapr_vscsi_create(spapr->vio_bus, 0x2000 + i, - xics_find_qirq(spapr->icp, irq), irq); - irq++; + spapr_vscsi_create(spapr->vio_bus, 0x2000 + i); } if (kernel_filename) { diff --git a/hw/spapr.h b/hw/spapr.h index 263691b6fb..009c459a14 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -1,6 +1,8 @@ #if !defined(__HW_SPAPR_H__) #define __HW_SPAPR_H__ +#include "hw/xics.h" + struct VIOsPAPRBus; struct icp_state; @@ -278,6 +280,12 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, target_ulong *args); +static inline qemu_irq spapr_find_qirq(sPAPREnvironment *spapr, + int irq_num) +{ + return xics_find_qirq(spapr->icp, irq_num); +} + static inline uint32_t rtas_ld(target_ulong phys, int n) { return ldl_be_phys(phys + 4*n); diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index c18efc7ee6..2597748882 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -195,11 +195,9 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev) return 0; } -void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd, - qemu_irq qirq, uint32_t vio_irq_num) +void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd) { DeviceState *dev; - VIOsPAPRDevice *sdev; dev = qdev_create(&bus->bus, "spapr-vlan"); qdev_prop_set_uint32(dev, "reg", reg); @@ -207,9 +205,6 @@ void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd, qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - sdev = (VIOsPAPRDevice *)dev; - sdev->qirq = qirq; - sdev->vio_irq_num = vio_irq_num; } static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index ce6558bb7e..ba2e1c178b 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -32,6 +32,7 @@ #include "hw/spapr.h" #include "hw/spapr_vio.h" +#include "hw/xics.h" #ifdef CONFIG_FDT #include @@ -595,6 +596,7 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo) { VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo; VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; + VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus); char *id; if (asprintf(&id, "%s@%x", info->dt_name, dev->reg) < 0) { @@ -602,6 +604,8 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo) } dev->qdev.id = id; + dev->vio_irq_num = bus->irq++; + dev->qirq = spapr_find_qirq(spapr, dev->vio_irq_num); rtce_init(dev); @@ -656,6 +660,7 @@ VIOsPAPRBus *spapr_vio_bus_init(void) qbus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio"); bus = DO_UPCAST(VIOsPAPRBus, bus, qbus); + bus->irq = 16; /* hcall-vio */ spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index 603a8c43a3..faa5d94d7a 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -62,6 +62,7 @@ typedef struct VIOsPAPRDevice { typedef struct VIOsPAPRBus { BusState bus; + int irq; } VIOsPAPRBus; typedef struct { @@ -98,15 +99,9 @@ uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr); int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq); void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len); -void spapr_vty_create(VIOsPAPRBus *bus, - uint32_t reg, CharDriverState *chardev, - qemu_irq qirq, uint32_t vio_irq_num); - -void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd, - qemu_irq qirq, uint32_t vio_irq_num); - -void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg, - qemu_irq qirq, uint32_t vio_irq_num); +void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev); +void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd); +void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg); int spapr_tce_set_bypass(uint32_t unit, uint32_t enable); void spapr_vio_quiesce(void); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index fc9ac6ab50..d2d041522f 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -893,20 +893,14 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev) return 0; } -void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg, - qemu_irq qirq, uint32_t vio_irq_num) +void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg) { DeviceState *dev; - VIOsPAPRDevice *sdev; dev = qdev_create(&bus->bus, "spapr-vscsi"); qdev_prop_set_uint32(dev, "reg", reg); qdev_init_nofail(dev); - - sdev = (VIOsPAPRDevice *)dev; - sdev->qirq = qirq; - sdev->vio_irq_num = vio_irq_num; } static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index f5046d9183..607b81b533 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -115,20 +115,14 @@ static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -void spapr_vty_create(VIOsPAPRBus *bus, - uint32_t reg, CharDriverState *chardev, - qemu_irq qirq, uint32_t vio_irq_num) +void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev) { DeviceState *dev; - VIOsPAPRDevice *sdev; dev = qdev_create(&bus->bus, "spapr-vty"); qdev_prop_set_uint32(dev, "reg", reg); qdev_prop_set_chr(dev, "chardev", chardev); qdev_init_nofail(dev); - sdev = (VIOsPAPRDevice *)dev; - sdev->qirq = qirq; - sdev->vio_irq_num = vio_irq_num; } static void vty_hcalls(VIOsPAPRBus *bus) From 77c7ea5ebbcf494f36f243d786e5f8409d7a4b85 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 26 May 2011 11:52:45 +0200 Subject: [PATCH 15/87] spapr: prepare for qdevification of irq Restructure common properties for sPAPR devices so that IRQ definitions can be added in one place. Signed-off-by: Paolo Bonzini Cc: Alexander Graf Cc: David Gibson Signed-off-by: Alexander Graf --- hw/spapr_llan.c | 4 +--- hw/spapr_vio.h | 5 +++++ hw/spapr_vscsi.c | 4 +--- hw/spapr_vty.c | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index 2597748882..abe12973e6 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -495,9 +495,7 @@ static VIOsPAPRDeviceInfo spapr_vlan = { .qdev.name = "spapr-vlan", .qdev.size = sizeof(VIOsPAPRVLANDevice), .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x1000), - DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, rtce_window_size, - 0x10000000), + DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev, 0x1000, 0x10000000), DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), DEFINE_PROP_END_OF_LIST(), }, diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index faa5d94d7a..7eb5367653 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -60,6 +60,11 @@ typedef struct VIOsPAPRDevice { VIOsPAPR_CRQ crq; } VIOsPAPRDevice; +#define DEFINE_SPAPR_PROPERTIES(type, field, default_reg, default_dma_window) \ + DEFINE_PROP_UINT32("reg", type, field.reg, default_reg), \ + DEFINE_PROP_UINT32("dma-window", type, field.rtce_window_size, \ + default_dma_window) + typedef struct VIOsPAPRBus { BusState bus; int irq; diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index d2d041522f..6fc82f6bd7 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -930,9 +930,7 @@ static VIOsPAPRDeviceInfo spapr_vscsi = { .qdev.name = "spapr-vscsi", .qdev.size = sizeof(VSCSIState), .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000), - DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, - rtce_window_size, 0x10000000), + DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev, 0x2000, 0x10000000), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index 607b81b533..a9d4b035e2 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -140,7 +140,7 @@ static VIOsPAPRDeviceInfo spapr_vty = { .qdev.name = "spapr-vty", .qdev.size = sizeof(VIOsPAPRVTYDevice), .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0), + DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev, 0, 0), DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), DEFINE_PROP_END_OF_LIST(), }, From 416343b144cb6a5d73b381e56f452fdbd5992522 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 26 May 2011 11:52:46 +0200 Subject: [PATCH 16/87] spapr: make irq customizable via qdev This also lets the user see the irq in "info qtree". Signed-off-by: Paolo Bonzini Cc: Alexander Graf Cc: David Gibson Signed-off-by: Alexander Graf --- hw/spapr_vio.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index ba2e1c178b..0546ccb043 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -52,6 +52,10 @@ static struct BusInfo spapr_vio_bus_info = { .name = "spapr-vio", .size = sizeof(VIOsPAPRBus), + .props = (Property[]) { + DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, vio_irq_num, 0), \ + DEFINE_PROP_END_OF_LIST(), + }, }; VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg) @@ -604,7 +608,9 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo) } dev->qdev.id = id; - dev->vio_irq_num = bus->irq++; + if (!dev->vio_irq_num) { + dev->vio_irq_num = bus->irq++; + } dev->qirq = spapr_find_qirq(spapr, dev->vio_irq_num); rtce_init(dev); From d751dfb313d3fd4feca60e305dd9904fec189d3f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 00:49:45 +0200 Subject: [PATCH 17/87] PPC: Move openpic to target specific code compilation The MPIC has some funny feature where it maps different registers to an MMIO region depending which CPU accesses them. To be able to reflect that, we need to make OpenPIC be compiled in the target code, so it can access cpu_single_env. Signed-off-by: Alexander Graf --- Makefile.objs | 1 - Makefile.target | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.objs b/Makefile.objs index 8d23fbbbee..c849e51122 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -222,7 +222,6 @@ hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o # PPC devices -hw-obj-$(CONFIG_OPENPIC) += openpic.o hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o # Mac shared devices hw-obj-$(CONFIG_MACIO) += macio.o diff --git a/Makefile.target b/Makefile.target index 88d2f1fb7e..8db9f37c70 100644 --- a/Makefile.target +++ b/Makefile.target @@ -252,6 +252,8 @@ obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o obj-ppc-y += virtex_ml507.o obj-ppc-$(CONFIG_KVM) += kvm_ppc.o obj-ppc-$(CONFIG_FDT) += device_tree.o +# PowerPC OpenPIC +obj-ppc-y += openpic.o # Xilinx PPC peripherals obj-ppc-y += xilinx_intc.o From 704c7e5d0f473bf6f268b18fdaada40e4b7ca964 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 01:33:29 +0200 Subject: [PATCH 18/87] PPC: Add CPU local MMIO regions to MPIC The MPIC exports a register set for each CPU connected to it. They can all be accessed through specific registers or using a shadow page that is mapped differently depending on which CPU accesses it. This patch implements the shadow map, making it possible for guests to access the CPU local registers using the same address on each CPU. Signed-off-by: Alexander Graf --- hw/openpic.c | 110 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 38 deletions(-) diff --git a/hw/openpic.c b/hw/openpic.c index 26c96e20f9..cf89f23192 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -2,6 +2,7 @@ * OpenPIC emulation * * Copyright (c) 2004 Jocelyn Mayer + * 2011 Alexander Graf * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -161,6 +162,16 @@ static inline int test_bit (uint32_t *field, int bit) return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0; } +static int get_current_cpu(void) +{ + return cpu_single_env->cpu_index; +} + +static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr, + int idx); +static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr, + uint32_t val, int idx); + enum { IRQ_EXTERNAL = 0x01, IRQ_INTERNAL = 0x02, @@ -590,18 +601,27 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val); if (addr & 0xF) return; - addr &= 0xFF; switch (addr) { - case 0x00: /* FREP */ + case 0x40: + case 0x50: + case 0x60: + case 0x70: + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + openpic_cpu_write_internal(opp, addr, val, get_current_cpu()); break; - case 0x20: /* GLBC */ + case 0x1000: /* FREP */ + break; + case 0x1020: /* GLBC */ if (val & 0x80000000 && opp->reset) opp->reset(opp); opp->glbc = val & ~0x80000000; break; - case 0x80: /* VENI */ + case 0x1080: /* VENI */ break; - case 0x90: /* PINT */ + case 0x1090: /* PINT */ for (idx = 0; idx < opp->nb_cpus; idx++) { if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) { DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx); @@ -615,22 +635,20 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v } opp->pint = val; break; -#if MAX_IPI > 0 - case 0xA0: /* IPI_IPVP */ - case 0xB0: - case 0xC0: - case 0xD0: + case 0x10A0: /* IPI_IPVP */ + case 0x10B0: + case 0x10C0: + case 0x10D0: { int idx; - idx = (addr - 0xA0) >> 4; + idx = (addr - 0x10A0) >> 4; write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP, val); } break; -#endif - case 0xE0: /* SPVE */ + case 0x10E0: /* SPVE */ opp->spve = val & 0x000000FF; break; - case 0xF0: /* TIFR */ + case 0x10F0: /* TIFR */ opp->tifr = val; break; default: @@ -647,36 +665,43 @@ static uint32_t openpic_gbl_read (void *opaque, target_phys_addr_t addr) retval = 0xFFFFFFFF; if (addr & 0xF) return retval; - addr &= 0xFF; switch (addr) { - case 0x00: /* FREP */ + case 0x1000: /* FREP */ retval = opp->frep; break; - case 0x20: /* GLBC */ + case 0x1020: /* GLBC */ retval = opp->glbc; break; - case 0x80: /* VENI */ + case 0x1080: /* VENI */ retval = opp->veni; break; - case 0x90: /* PINT */ + case 0x1090: /* PINT */ retval = 0x00000000; break; -#if MAX_IPI > 0 - case 0xA0: /* IPI_IPVP */ + case 0x40: + case 0x50: + case 0x60: + case 0x70: + case 0x80: + case 0x90: + case 0xA0: case 0xB0: - case 0xC0: - case 0xD0: + retval = openpic_cpu_read_internal(opp, addr, get_current_cpu()); + break; + case 0x10A0: /* IPI_IPVP */ + case 0x10B0: + case 0x10C0: + case 0x10D0: { int idx; - idx = (addr - 0xA0) >> 4; + idx = (addr - 0x10A0) >> 4; retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP); } break; -#endif - case 0xE0: /* SPVE */ + case 0x10E0: /* SPVE */ retval = opp->spve; break; - case 0xF0: /* TIFR */ + case 0x10F0: /* TIFR */ retval = opp->tifr; break; default: @@ -794,23 +819,23 @@ static uint32_t openpic_src_read (void *opaque, uint32_t addr) return retval; } -static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t val) +static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr, + uint32_t val, int idx) { openpic_t *opp = opaque; IRQ_src_t *src; IRQ_dst_t *dst; - int idx, s_IRQ, n_IRQ; + int s_IRQ, n_IRQ; - DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val); + DPRINTF("%s: cpu %d addr " TARGET_FMT_plx " <= %08x\n", __func__, idx, + addr, val); if (addr & 0xF) return; - addr &= 0x1FFF0; - idx = addr / 0x1000; dst = &opp->dst[idx]; addr &= 0xFF0; switch (addr) { #if MAX_IPI > 0 - case 0x40: /* PIPD */ + case 0x40: /* IPIDR */ case 0x50: case 0x60: case 0x70: @@ -852,20 +877,24 @@ static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t v } } -static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr) +static void openpic_cpu_write(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12); +} + +static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr, + int idx) { openpic_t *opp = opaque; IRQ_src_t *src; IRQ_dst_t *dst; uint32_t retval; - int idx, n_IRQ; + int n_IRQ; - DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr); + DPRINTF("%s: cpu %d addr " TARGET_FMT_plx "\n", __func__, idx, addr); retval = 0xFFFFFFFF; if (addr & 0xF) return retval; - addr &= 0x1FFF0; - idx = addr / 0x1000; dst = &opp->dst[idx]; addr &= 0xFF0; switch (addr) { @@ -925,6 +954,11 @@ static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr) return retval; } +static uint32_t openpic_cpu_read(void *opaque, target_phys_addr_t addr) +{ + return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12); +} + static void openpic_buggy_write (void *opaque, target_phys_addr_t addr, uint32_t val) { From bc59d9c9163abbe3646a70bca5ff59e73e07904a Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 01:35:15 +0200 Subject: [PATCH 19/87] PPC: Extend MPIC MMIO range The MPIC exports a page for each CPU that it controls. To support more than one CPU, we need to also reserve the MMIO space according to the amount of CPUs we want to support. Signed-off-by: Alexander Graf --- hw/openpic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/openpic.c b/hw/openpic.c index cf89f23192..f7d5583da5 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -128,7 +128,7 @@ enum { #define MPIC_MSI_REG_START 0x11C00 #define MPIC_MSI_REG_SIZE 0x100 #define MPIC_CPU_REG_START 0x20000 -#define MPIC_CPU_REG_SIZE 0x100 +#define MPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) enum mpic_ide_bits { IDR_EP = 0, From a675155e2d78b083b47774c118990041333308b5 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 01:36:44 +0200 Subject: [PATCH 20/87] PPC: Fix IPI support in MPIC The current IPI support in the MPIC code is incomplete and doesn't work. This code adds proper support for IPIs in MPIC by using the IDE register to remember which CPUs IPIs are still outstanding to. New triggers through the IPI trigger register only add to the list of CPUs we want to IPI. Signed-off-by: Alexander Graf --- v1 -> v2: - Use MAX_IPI instead of hardcoded 4 Signed-off-by: Alexander Graf --- hw/openpic.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/hw/openpic.c b/hw/openpic.c index f7d5583da5..9710ac0b30 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -57,7 +57,7 @@ #define MAX_MBX 4 #define MAX_TMR 4 #define VECTOR_BITS 8 -#define MAX_IPI 0 +#define MAX_IPI 4 #define VID (0x00000000) @@ -840,7 +840,9 @@ static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr, case 0x60: case 0x70: idx = (addr - 0x40) >> 4; - write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE, val); + /* we use IDE as mask which CPUs to deliver the IPI to still. */ + write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE, + opp->src[opp->irq_ipi0 + idx].ide | val); openpic_set_irq(opp, opp->irq_ipi0 + idx, 1); openpic_set_irq(opp, opp->irq_ipi0 + idx, 0); break; @@ -934,6 +936,17 @@ static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr, reset_bit(&src->ipvp, IPVP_ACTIVITY); src->pending = 0; } + + if ((n_IRQ >= opp->irq_ipi0) && (n_IRQ < (opp->irq_ipi0 + MAX_IPI))) { + src->ide &= ~(1 << idx); + if (src->ide && !test_bit(&src->ipvp, IPVP_SENSE)) { + /* trigger on CPUs that didn't know about it yet */ + openpic_set_irq(opp, n_IRQ, 1); + openpic_set_irq(opp, n_IRQ, 0); + /* if all CPUs knew about it, set active bit again */ + set_bit(&src->ipvp, IPVP_ACTIVITY); + } + } } break; case 0xB0: /* PEOI */ From 9250fd24a98af75f196480a75aacf99291ef46a9 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 23 Jul 2011 11:05:35 +0200 Subject: [PATCH 21/87] PPC: Set MPIC IDE for IPI to 0 We use the IDE register with IPIs as a mask to keep track which processors have already acknowledged the respective interrupt. So we need to initialize it to 0 to make sure that it doesn't accidently fire an IPI on CPU0 when the first IPI is triggered. Reported-by: Elie Richa Signed-off-by: Alexander Graf --- v2 -> v3: - fix IDE IPI reset --- hw/openpic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/openpic.c b/hw/openpic.c index 9710ac0b30..31ad1751fe 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -1299,6 +1299,10 @@ static void mpic_reset (void *opaque) mpp->src[i].ipvp = 0x80800000; mpp->src[i].ide = 0x00000001; } + /* Set IDE for IPIs to 0 so we don't get spurious interrupts */ + for (i = mpp->irq_ipi0; i < (mpp->irq_ipi0 + MAX_IPI); i++) { + mpp->src[i].ide = 0; + } /* Initialise IRQ destinations */ for (i = 0; i < MAX_CPU; i++) { mpp->dst[i].pctp = 0x0000000F; From 3ee82442c53e342883eb6f05b1776b05892e336f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 23 Jul 2011 11:09:23 +0200 Subject: [PATCH 22/87] PPC: MPIC: Remove read functionality for WO registers The IPI dispatch registers are write only according to every MPIC spec I have found. So instead of pretending you could read back something from them, better not handle them at all. Reported-by: Elie Richa Signed-off-by: Alexander Graf --- hw/openpic.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hw/openpic.c b/hw/openpic.c index 31ad1751fe..dfec52e2e2 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -952,13 +952,6 @@ static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr, case 0xB0: /* PEOI */ retval = 0; break; -#if MAX_IPI > 0 - case 0x40: /* IDE */ - case 0x50: - idx = (addr - 0x40) >> 4; - retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE); - break; -#endif default: break; } From 0d33defbe3e75db75599c52607da6fc3b4ce0f25 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 23 Jul 2011 11:27:53 +0200 Subject: [PATCH 23/87] PPC: MPIC: Fix CI bit definitions The bit definitions for critical interrupt routing are in PowerPC order (most significant bit is 0), while we end up shifting it with normal bit order. Turn the numbers around so we actually end up fetching the right ones. Signed-off-by: Alexander Graf --- hw/openpic.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/openpic.c b/hw/openpic.c index dfec52e2e2..109c1bc6f4 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -131,11 +131,11 @@ enum { #define MPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) enum mpic_ide_bits { - IDR_EP = 0, - IDR_CI0 = 1, - IDR_CI1 = 2, - IDR_P1 = 30, - IDR_P0 = 31, + IDR_EP = 31, + IDR_CI0 = 30, + IDR_CI1 = 29, + IDR_P1 = 1, + IDR_P0 = 0, }; #else From bbc5842211cdd90103cfe52f2ca24afac880694f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 01:39:46 +0200 Subject: [PATCH 24/87] PPC: Bump MPIC up to 32 supported CPUs The MPIC emulation is now capable of handling up to 32 CPUs. Reflect that in the code exporting the numbers out and fix an integer overflow while at it. Signed-off-by: Alexander Graf --- v1 -> v2: - Max cpus is 15 due to cINT routing - Report nb_cpus not MAX_CPUS in MPIC capabilities --- hw/openpic.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/openpic.c b/hw/openpic.c index 109c1bc6f4..03e442b1f5 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -63,7 +63,7 @@ #elif defined(USE_MPCxxx) -#define MAX_CPU 2 +#define MAX_CPU 15 #define MAX_IRQ 128 #define MAX_DBL 0 #define MAX_MBX 0 @@ -507,7 +507,7 @@ static inline void write_IRQreg (openpic_t *opp, int n_IRQ, break; case IRQ_IDE: tmp = val & 0xC0000000; - tmp |= val & ((1 << MAX_CPU) - 1); + tmp |= val & ((1ULL << MAX_CPU) - 1); opp->src[n_IRQ].ide = tmp; DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); break; @@ -1283,7 +1283,7 @@ static void mpic_reset (void *opaque) mpp->glbc = 0x80000000; /* Initialise controller registers */ - mpp->frep = 0x004f0002; + mpp->frep = 0x004f0002 | ((mpp->nb_cpus - 1) << 8); mpp->veni = VENI; mpp->pint = 0x00000000; mpp->spve = 0x0000FFFF; @@ -1684,10 +1684,6 @@ qemu_irq *mpic_init (target_phys_addr_t base, int nb_cpus, {mpic_cpu_read, mpic_cpu_write, MPIC_CPU_REG_START, MPIC_CPU_REG_SIZE}, }; - /* XXX: for now, only one CPU is supported */ - if (nb_cpus != 1) - return NULL; - mpp = g_malloc0(sizeof(openpic_t)); for (i = 0; i < sizeof(list)/sizeof(list[0]); i++) { From e61c36d58d31d0ce7e1e7674bc8f1d1fc8efadd1 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 01:41:16 +0200 Subject: [PATCH 25/87] PPC: E500: create multiple envs When creating a VM, we should go through smp_cpus and create a virtual CPU for every CPU the user requested. This patch adds support for that and moves some code around to make that more convenient. Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 44 ++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 1274a3e1eb..8d05587514 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -226,7 +226,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, const char *cpu_model) { PCIBus *pci_bus; - CPUState *env; + CPUState *env = NULL; uint64_t elf_entry; uint64_t elf_lowaddr; target_phys_addr_t entry=0; @@ -240,24 +240,40 @@ static void mpc8544ds_init(ram_addr_t ram_size, qemu_irq *irqs, *mpic; DeviceState *dev; struct boot_info *boot_info; + CPUState *firstenv = NULL; - /* Setup CPU */ + /* Setup CPUs */ if (cpu_model == NULL) { cpu_model = "e500v2_v30"; } - env = cpu_ppc_init(cpu_model); - if (!env) { - fprintf(stderr, "Unable to initialize CPU!\n"); - exit(1); + for (i = 0; i < smp_cpus; i++) { + qemu_irq *input; + env = cpu_ppc_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to initialize CPU!\n"); + exit(1); + } + + if (!firstenv) { + firstenv = env; + } + + env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; + + /* XXX register timer? */ + ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR); + ppc_dcr_init(env, NULL, NULL); + /* XXX Enable DEC interrupts - probably wrong in the backend */ + env->spr[SPR_40x_TCR] = 1 << 26; + + /* Register reset handler */ + boot_info = g_malloc0(sizeof(struct boot_info)); + qemu_register_reset(mpc8544ds_cpu_reset, env); + env->load_info = boot_info; } - /* XXX register timer? */ - ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR); - ppc_dcr_init(env, NULL, NULL); - - /* Register reset handler */ - qemu_register_reset(mpc8544ds_cpu_reset, env); + env = firstenv; /* Fixup Memory size on a alignment boundary */ ram_size &= ~(RAM_SIZES_ALIGN - 1); @@ -336,8 +352,6 @@ static void mpc8544ds_init(ram_addr_t ram_size, } } - boot_info = g_malloc0(sizeof(struct boot_info)); - /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { #ifndef CONFIG_FDT @@ -350,10 +364,10 @@ static void mpc8544ds_init(ram_addr_t ram_size, exit(1); } + boot_info = env->load_info; boot_info->entry = entry; boot_info->dt_base = dt_base; } - env->load_info = boot_info; if (kvm_enabled()) { kvmppc_init(); From a915249fa1e0a59e9a893cd335fd31af39b01bde Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 01:42:58 +0200 Subject: [PATCH 26/87] PPC: E500: Generate IRQ lines for many CPUs Now that we can generate multiple envs for all our virtual CPUs, we also need to tell the MPIC that we have multiple CPUs connected and connect them all to the respective virtual interrupt lines. Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 8d05587514..9cb01f3e7e 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -237,7 +237,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, target_long initrd_size=0; int i=0; unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; - qemu_irq *irqs, *mpic; + qemu_irq **irqs, *mpic; DeviceState *dev; struct boot_info *boot_info; CPUState *firstenv = NULL; @@ -247,6 +247,8 @@ static void mpc8544ds_init(ram_addr_t ram_size, cpu_model = "e500v2_v30"; } + irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); + irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); for (i = 0; i < smp_cpus; i++) { qemu_irq *input; env = cpu_ppc_init(cpu_model); @@ -259,6 +261,10 @@ static void mpc8544ds_init(ram_addr_t ram_size, firstenv = env; } + irqs[i] = irqs[0] + (i * OPENPIC_OUTPUT_NB); + input = (qemu_irq *)env->irq_inputs; + irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; + irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; /* XXX register timer? */ @@ -283,10 +289,11 @@ static void mpc8544ds_init(ram_addr_t ram_size, "mpc8544ds.ram", ram_size)); /* MPIC */ - irqs = g_malloc0(sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); - irqs[OPENPIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_INT]; - irqs[OPENPIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_CINT]; - mpic = mpic_init(MPC8544_MPIC_REGS_BASE, 1, &irqs, NULL); + mpic = mpic_init(MPC8544_MPIC_REGS_BASE, smp_cpus, irqs, NULL); + + if (!mpic) { + cpu_abort(env, "MPIC failed to initialize\n"); + } /* Serial */ if (serial_hds[0]) { From d69a8e6387adcd79c29666930bc64fffd2a3f456 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 01:52:57 +0200 Subject: [PATCH 27/87] device tree: add nop_node We have a qemu internal abstraction layer on FDT. While I'm not fully convinced we need it at all, it's missing the nop_node functionality that we now need on e500. So let's add it and think about the general future of that API later. Signed-off-by: Alexander Graf --- device_tree.c | 11 +++++++++++ device_tree.h | 1 + 2 files changed, 12 insertions(+) diff --git a/device_tree.c b/device_tree.c index 3a224d1e0f..23e89e3b90 100644 --- a/device_tree.c +++ b/device_tree.c @@ -107,3 +107,14 @@ int qemu_devtree_setprop_string(void *fdt, const char *node_path, return fdt_setprop_string(fdt, offset, property, string); } + +int qemu_devtree_nop_node(void *fdt, const char *node_path) +{ + int offset; + + offset = fdt_path_offset(fdt, node_path); + if (offset < 0) + return offset; + + return fdt_nop_node(fdt, offset); +} diff --git a/device_tree.h b/device_tree.h index cecd98f042..76fce5ffb2 100644 --- a/device_tree.h +++ b/device_tree.h @@ -22,5 +22,6 @@ int qemu_devtree_setprop_cell(void *fdt, const char *node_path, const char *property, uint32_t val); int qemu_devtree_setprop_string(void *fdt, const char *node_path, const char *property, const char *string); +int qemu_devtree_nop_node(void *fdt, const char *node_path); #endif /* __DEVICE_TREE_H__ */ From a489f7f7111fb2e8aaf6a152731a5f1fe7e0c4b3 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 02:08:10 +0200 Subject: [PATCH 28/87] PPC: bamboo: Move host fdt copy to target We have some code in generic kvm_ppc.c that is only used by 440. Move to the 440 specific device code. Signed-off-by: Alexander Graf --- hw/ppc440_bamboo.c | 37 +++++++++++++++++++++++++++++++++++-- target-ppc/kvm_ppc.c | 30 ------------------------------ target-ppc/kvm_ppc.h | 1 - 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c index 1addb68327..65d4f0fbd4 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -31,6 +31,38 @@ #define FDT_ADDR 0x1800000 #define RAMDISK_ADDR 0x1900000 +#ifdef CONFIG_FDT +static int bamboo_copy_host_cell(void *fdt, const char *node, const char *prop) +{ + uint32_t cell; + int ret; + + ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell)); + if (ret < 0) { + fprintf(stderr, "couldn't read host %s/%s\n", node, prop); + goto out; + } + + ret = qemu_devtree_setprop_cell(fdt, node, prop, cell); + if (ret < 0) { + fprintf(stderr, "couldn't set guest %s/%s\n", node, prop); + goto out; + } + +out: + return ret; +} + +static void bamboo_fdt_update(void *fdt) +{ + /* Copy data from the host device tree into the guest. Since the guest can + * directly access the timebase without host involvement, we must expose + * the correct frequencies. */ + bamboo_copy_host_cell(fdt, "/cpus/cpu@0", "clock-frequency"); + bamboo_copy_host_cell(fdt, "/cpus/cpu@0", "timebase-frequency"); +} +#endif + static int bamboo_load_device_tree(target_phys_addr_t addr, uint32_t ramsize, target_phys_addr_t initrd_base, @@ -76,8 +108,9 @@ static int bamboo_load_device_tree(target_phys_addr_t addr, if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); - if (kvm_enabled()) - kvmppc_fdt_update(fdt); + if (kvm_enabled()) { + bamboo_fdt_update(fdt); + } ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); diff --git a/target-ppc/kvm_ppc.c b/target-ppc/kvm_ppc.c index c031fcb75a..26ecc9d25f 100644 --- a/target-ppc/kvm_ppc.c +++ b/target-ppc/kvm_ppc.c @@ -54,36 +54,6 @@ free: free(path); return ret; } - -static int kvmppc_copy_host_cell(void *fdt, const char *node, const char *prop) -{ - uint32_t cell; - int ret; - - ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell)); - if (ret < 0) { - fprintf(stderr, "couldn't read host %s/%s\n", node, prop); - goto out; - } - - ret = qemu_devtree_setprop_cell(fdt, node, prop, cell); - if (ret < 0) { - fprintf(stderr, "couldn't set guest %s/%s\n", node, prop); - goto out; - } - -out: - return ret; -} - -void kvmppc_fdt_update(void *fdt) -{ - /* Copy data from the host device tree into the guest. Since the guest can - * directly access the timebase without host involvement, we must expose - * the correct frequencies. */ - kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "clock-frequency"); - kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "timebase-frequency"); -} #endif static void kvmppc_timer_hack(void *opaque) diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 45a1373b28..2f32249e6d 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -10,7 +10,6 @@ #define __KVM_PPC_H__ void kvmppc_init(void); -void kvmppc_fdt_update(void *fdt); #ifndef CONFIG_KVM static inline int kvmppc_read_host_property(const char *node_path, const char *prop, void *val, size_t len) From eadaada1ceb74f152dc9fe196bdda1b79c86275c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 02:29:15 +0200 Subject: [PATCH 29/87] PPC: KVM: Add generic function to read host clockfreq We need to find out the host's clock-frequency when running on KVM, so let's export a respective function. Signed-off-by: Alexander Graf --- v1 -> v2: - enable 64bit values --- target-ppc/kvm.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ target-ppc/kvm_ppc.h | 1 + 2 files changed, 68 insertions(+) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 21f35af762..77b98c4d73 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -14,6 +14,7 @@ * */ +#include #include #include #include @@ -38,6 +39,8 @@ do { } while (0) #endif +#define PROC_DEVTREE_CPU "/proc/device-tree/cpus/" + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; @@ -509,6 +512,70 @@ uint32_t kvmppc_get_tbfreq(void) return retval; } +/* Try to find a device tree node for a CPU with clock-frequency property */ +static int kvmppc_find_cpu_dt(char *buf, int buf_len) +{ + struct dirent *dirp; + DIR *dp; + + if ((dp = opendir(PROC_DEVTREE_CPU)) == NULL) { + printf("Can't open directory " PROC_DEVTREE_CPU "\n"); + return -1; + } + + buf[0] = '\0'; + while ((dirp = readdir(dp)) != NULL) { + FILE *f; + snprintf(buf, buf_len, "%s%s/clock-frequency", PROC_DEVTREE_CPU, + dirp->d_name); + f = fopen(buf, "r"); + if (f) { + snprintf(buf, buf_len, "%s%s", PROC_DEVTREE_CPU, dirp->d_name); + fclose(f); + break; + } + buf[0] = '\0'; + } + closedir(dp); + if (buf[0] == '\0') { + printf("Unknown host!\n"); + return -1; + } + + return 0; +} + +uint64_t kvmppc_get_clockfreq(void) +{ + char buf[512]; + uint32_t tb[2]; + FILE *f; + int len; + + if (kvmppc_find_cpu_dt(buf, sizeof(buf))) { + return 0; + } + + strncat(buf, "/clock-frequency", sizeof(buf) - strlen(buf)); + + f = fopen(buf, "rb"); + if (!f) { + return -1; + } + + len = fread(tb, sizeof(tb[0]), 2, f); + fclose(f); + switch (len) { + case 1: + /* freq is only a single cell */ + return tb[0]; + case 2: + return *(uint64_t*)tb; + } + + return 0; +} + int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len) { uint32_t *hc = (uint32_t*)buf; diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 2f32249e6d..7c08c0f4eb 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -23,6 +23,7 @@ int kvmppc_read_host_property(const char *node_path, const char *prop, #endif uint32_t kvmppc_get_tbfreq(void); +uint64_t kvmppc_get_clockfreq(void); int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len); int kvmppc_set_interrupt(CPUState *env, int irq, int level); From 911d6e7ad12514652601ee579b2e1201c1545fa1 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 02:34:11 +0200 Subject: [PATCH 30/87] PPC: E500: Use generic kvm function for freq Now that we have generic KVM functions to read out the host tb and clock frequencies, let's use them in the e500 code! Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 44 +++++++++--------------------------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 9cb01f3e7e..8748531809 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -14,8 +14,6 @@ * (at your option) any later version. */ -#include - #include "config.h" #include "qemu-common.h" #include "net.h" @@ -96,6 +94,9 @@ static int mpc8544_load_device_tree(CPUState *env, int fdt_size; void *fdt; uint8_t hypercall[16]; + char cpu_name[128] = "/cpus/PowerPC,8544@0"; + uint32_t clock_freq = 400000000; + uint32_t tb_freq = 400000000; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -133,32 +134,9 @@ static int mpc8544_load_device_tree(CPUState *env, fprintf(stderr, "couldn't set /chosen/bootargs\n"); if (kvm_enabled()) { - struct dirent *dirp; - DIR *dp; - char buf[128]; - - if ((dp = opendir("/proc/device-tree/cpus/")) == NULL) { - printf("Can't open directory /proc/device-tree/cpus/\n"); - ret = -1; - goto out; - } - - buf[0] = '\0'; - while ((dirp = readdir(dp)) != NULL) { - if (strncmp(dirp->d_name, "PowerPC", 7) == 0) { - snprintf(buf, 128, "/cpus/%s", dirp->d_name); - break; - } - } - closedir(dp); - if (buf[0] == '\0') { - printf("Unknow host!\n"); - ret = -1; - goto out; - } - - mpc8544_copy_soc_cell(fdt, buf, "clock-frequency"); - mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency"); + /* Read out host's frequencies */ + clock_freq = kvmppc_get_clockfreq(); + tb_freq = kvmppc_get_tbfreq(); /* indicate KVM hypercall interface */ qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", @@ -166,15 +144,11 @@ static int mpc8544_load_device_tree(CPUState *env, kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", hypercall, sizeof(hypercall)); - } else { - const uint32_t freq = 400000000; - - qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", - "clock-frequency", freq); - qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", - "timebase-frequency", freq); } + qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); + qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); + ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); From 66ae790247f1f6c19cc651b64272bbb9cbfc328d Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 02:35:28 +0200 Subject: [PATCH 31/87] PPC: E500: Remove mpc8544_copy_soc_cell We don't need mpc8544_copy_soc_cell anymore, since we're explicitly reading host values and writing guest values respectively. Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 8748531809..2c7c6774f2 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -56,30 +56,6 @@ struct boot_info uint32_t entry; }; -#ifdef CONFIG_FDT -static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop) -{ - uint32_t cell; - int ret; - - ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell)); - if (ret < 0) { - fprintf(stderr, "couldn't read host %s/%s\n", node, prop); - goto out; - } - - ret = qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", - prop, cell); - if (ret < 0) { - fprintf(stderr, "couldn't set guest /cpus/PowerPC,8544@0/%s\n", prop); - goto out; - } - -out: - return ret; -} -#endif - static int mpc8544_load_device_tree(CPUState *env, target_phys_addr_t addr, uint32_t ramsize, From 7dadd40c89837a9c61d6bb24164a6c6b660d70a4 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 02:44:53 +0200 Subject: [PATCH 32/87] PPC: bamboo: Use kvm api for freq and clock frequencies Now that we have nice and shiny APIs to read out the host's clock and timebase frequencies, let's use them in the bamboo code as well! Signed-off-by: Alexander Graf --- hw/ppc440_bamboo.c | 45 ++++++++++++--------------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c index 65d4f0fbd4..1523764c3a 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -31,38 +31,6 @@ #define FDT_ADDR 0x1800000 #define RAMDISK_ADDR 0x1900000 -#ifdef CONFIG_FDT -static int bamboo_copy_host_cell(void *fdt, const char *node, const char *prop) -{ - uint32_t cell; - int ret; - - ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell)); - if (ret < 0) { - fprintf(stderr, "couldn't read host %s/%s\n", node, prop); - goto out; - } - - ret = qemu_devtree_setprop_cell(fdt, node, prop, cell); - if (ret < 0) { - fprintf(stderr, "couldn't set guest %s/%s\n", node, prop); - goto out; - } - -out: - return ret; -} - -static void bamboo_fdt_update(void *fdt) -{ - /* Copy data from the host device tree into the guest. Since the guest can - * directly access the timebase without host involvement, we must expose - * the correct frequencies. */ - bamboo_copy_host_cell(fdt, "/cpus/cpu@0", "clock-frequency"); - bamboo_copy_host_cell(fdt, "/cpus/cpu@0", "timebase-frequency"); -} -#endif - static int bamboo_load_device_tree(target_phys_addr_t addr, uint32_t ramsize, target_phys_addr_t initrd_base, @@ -75,6 +43,8 @@ static int bamboo_load_device_tree(target_phys_addr_t addr, char *filename; int fdt_size; void *fdt; + uint32_t tb_freq = 400000000; + uint32_t clock_freq = 400000000; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -108,10 +78,19 @@ static int bamboo_load_device_tree(target_phys_addr_t addr, if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); + /* Copy data from the host device tree into the guest. Since the guest can + * directly access the timebase without host involvement, we must expose + * the correct frequencies. */ if (kvm_enabled()) { - bamboo_fdt_update(fdt); + tb_freq = kvmppc_get_tbfreq(); + clock_freq = kvmppc_get_clockfreq(); } + qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", + clock_freq); + qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", + tb_freq); + ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); From e95a69cb6dce91c971bdf34a84a4026ff6b7821f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 02:46:11 +0200 Subject: [PATCH 33/87] PPC: KVM: Remove kvmppc_read_host_property We just got rid of the last user of kvmppc_read_host_property, so we can now safely remove it. Signed-off-by: Alexander Graf --- target-ppc/kvm_ppc.c | 35 ----------------------------------- target-ppc/kvm_ppc.h | 11 ----------- 2 files changed, 46 deletions(-) diff --git a/target-ppc/kvm_ppc.c b/target-ppc/kvm_ppc.c index 26ecc9d25f..24fc6bce3b 100644 --- a/target-ppc/kvm_ppc.c +++ b/target-ppc/kvm_ppc.c @@ -21,41 +21,6 @@ static QEMUTimer *kvmppc_timer; static unsigned int kvmppc_timer_rate; -#ifdef CONFIG_FDT -int kvmppc_read_host_property(const char *node_path, const char *prop, - void *val, size_t len) -{ - char *path; - FILE *f; - int ret = 0; - int pathlen; - - pathlen = snprintf(NULL, 0, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop) - + 1; - path = g_malloc(pathlen); - - snprintf(path, pathlen, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop); - - f = fopen(path, "rb"); - if (f == NULL) { - ret = errno; - goto free; - } - - len = fread(val, len, 1, f); - if (len != 1) { - ret = ferror(f); - goto close; - } - -close: - fclose(f); -free: - free(path); - return ret; -} -#endif - static void kvmppc_timer_hack(void *opaque) { qemu_notify_event(); diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 7c08c0f4eb..0c659c8d75 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -10,17 +10,6 @@ #define __KVM_PPC_H__ void kvmppc_init(void); -#ifndef CONFIG_KVM -static inline int kvmppc_read_host_property(const char *node_path, const char *prop, - void *val, size_t len) -{ - assert(0); - return -ENOSYS; -} -#else -int kvmppc_read_host_property(const char *node_path, const char *prop, - void *val, size_t len); -#endif uint32_t kvmppc_get_tbfreq(void); uint64_t kvmppc_get_clockfreq(void); From 921e28db8dac945af8ae065b862c46393b259374 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 02:54:51 +0200 Subject: [PATCH 34/87] PPC: KVM: Add stubs for kvm helper functions We have a bunch of helper functions that don't have any stubs for them in case we don't have CONFIG_KVM enabled. That didn't bite us so far, because gcc can optimize them out pretty well, but we should really provide them. Signed-off-by: Alexander Graf --- v1 -> v2: - use uint64_t for clockfreq --- target-ppc/kvm_ppc.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 0c659c8d75..76f98d9ab2 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -11,11 +11,37 @@ void kvmppc_init(void); +#ifdef CONFIG_KVM + uint32_t kvmppc_get_tbfreq(void); uint64_t kvmppc_get_clockfreq(void); int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len); int kvmppc_set_interrupt(CPUState *env, int irq, int level); +#else + +static inline uint32_t kvmppc_get_tbfreq(void) +{ + return 0; +} + +static inline uint64_t kvmppc_get_clockfreq(void) +{ + return 0; +} + +static inline int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len) +{ + return -1; +} + +static inline int kvmppc_set_interrupt(CPUState *env, int irq, int level) +{ + return -1; +} + +#endif + #ifndef CONFIG_KVM #define kvmppc_eieio() do { } while (0) #else From 621d05e3011bb369a6d48881ceaabb4ecf1a8790 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 03:01:11 +0200 Subject: [PATCH 35/87] PPC: E500: Update freqs for all CPUs Now that we can so nicely find out the host's frequencies, we should also make sure that we get them into all virtual CPUs' device tree nodes. Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 2c7c6774f2..0791e27462 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -70,9 +70,9 @@ static int mpc8544_load_device_tree(CPUState *env, int fdt_size; void *fdt; uint8_t hypercall[16]; - char cpu_name[128] = "/cpus/PowerPC,8544@0"; uint32_t clock_freq = 400000000; uint32_t tb_freq = 400000000; + int i; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -122,8 +122,12 @@ static int mpc8544_load_device_tree(CPUState *env, hypercall, sizeof(hypercall)); } - qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); - qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); + for (i = 0; i < smp_cpus; i++) { + char cpu_name[128]; + snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", i); + qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); + qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); + } ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); From 66bc7e00404a2a6ce92b0f4ee2ecc5df416650b3 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 03:02:31 +0200 Subject: [PATCH 36/87] PPC: E500: Remove unneeded CPU nodes We should only keep CPU nodes in the device tree around that we really have virtual CPUs for. So remove all superfluous entries that we just keep there in case someone wants to create a lot of vCPUs. Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 0791e27462..9379624e37 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -129,6 +129,12 @@ static int mpc8544_load_device_tree(CPUState *env, qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); } + for (i = smp_cpus; i < 32; i++) { + char cpu_name[128]; + snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", i); + qemu_devtree_nop_node(fdt, cpu_name); + } + ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); From 5c145dacacad04f751ca5c3fb6467e590e7f3c46 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 22 Jul 2011 13:32:29 +0200 Subject: [PATCH 37/87] PPC: E500: Add PV spinning code CPUs that are not the boot CPU need to run in spinning code to check if they should run off to execute and if so where to jump to. This usually happens by leaving secondary CPUs looping and checking if some variable in memory changed. In an environment like Qemu however we can be more clever. We can just export the spin table the primary CPU modifies as MMIO region that would event based wake up the respective secondary CPUs. That saves us quite some cycles while the secondary CPUs are not up yet. So this patch adds a PV device that simply exports the spinning table into the guest and thus allows the primary CPU to wake up secondary ones. Signed-off-by: Alexander Graf --- v1 -> v2: - change into MMIO scheme - map the secondary NIP instead of 0 1:1 - only map 64MB for TLB, same as u-boot - prepare code for 64-bit spinnings v2 -> v3: - remove r6 - set MAS2_M - map EA 0 - use second TLB1 entry v3 -> v4: - change to memoryops v4 -> v5: - fix endianness bugs v5 -> v6: - add header --- Makefile.target | 2 +- hw/ppce500_mpc8544ds.c | 33 ++++++- hw/ppce500_spin.c | 215 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 hw/ppce500_spin.c diff --git a/Makefile.target b/Makefile.target index 8db9f37c70..ff3efa430c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -247,7 +247,7 @@ endif obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-ppc-y += ppc440.o ppc440_bamboo.o # PowerPC E500 boards -obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o +obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o # PowerPC 440 Xilinx ML507 reference board. obj-ppc-y += virtex_ml507.o obj-ppc-$(CONFIG_KVM) += kvm_ppc.o diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 9379624e37..3b8b449a56 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -49,6 +49,7 @@ #define MPC8544_PCI_IO 0xE1000000 #define MPC8544_PCI_IOLEN 0x10000 #define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000) +#define MPC8544_SPIN_BASE 0xEF000000 struct boot_info { @@ -164,6 +165,18 @@ static void mmubooke_create_initial_mapping(CPUState *env, tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; } +static void mpc8544ds_cpu_reset_sec(void *opaque) +{ + CPUState *env = opaque; + + cpu_reset(env); + + /* Secondary CPU starts in halted state for now. Needs to change when + implementing non-kernel boot. */ + env->halted = 1; + env->exception_index = EXCP_HLT; +} + static void mpc8544ds_cpu_reset(void *opaque) { CPUState *env = opaque; @@ -172,6 +185,7 @@ static void mpc8544ds_cpu_reset(void *opaque) cpu_reset(env); /* Set initial guest state. */ + env->halted = 0; env->gpr[1] = (16<<20) - 8; env->gpr[3] = bi->dt_base; env->nip = bi->entry; @@ -199,7 +213,6 @@ static void mpc8544ds_init(ram_addr_t ram_size, unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; qemu_irq **irqs, *mpic; DeviceState *dev; - struct boot_info *boot_info; CPUState *firstenv = NULL; /* Setup CPUs */ @@ -234,9 +247,16 @@ static void mpc8544ds_init(ram_addr_t ram_size, env->spr[SPR_40x_TCR] = 1 << 26; /* Register reset handler */ - boot_info = g_malloc0(sizeof(struct boot_info)); - qemu_register_reset(mpc8544ds_cpu_reset, env); - env->load_info = boot_info; + if (!i) { + /* Primary CPU */ + struct boot_info *boot_info; + boot_info = g_malloc0(sizeof(struct boot_info)); + qemu_register_reset(mpc8544ds_cpu_reset, env); + env->load_info = boot_info; + } else { + /* Secondary CPUs */ + qemu_register_reset(mpc8544ds_cpu_reset_sec, env); + } } env = firstenv; @@ -289,6 +309,9 @@ static void mpc8544ds_init(ram_addr_t ram_size, } } + /* Register spinning region */ + sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); + /* Load kernel. */ if (kernel_filename) { kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); @@ -321,6 +344,8 @@ static void mpc8544ds_init(ram_addr_t ram_size, /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { + struct boot_info *boot_info; + #ifndef CONFIG_FDT cpu_abort(env, "Compiled without FDT support - can't load kernel\n"); #endif diff --git a/hw/ppce500_spin.c b/hw/ppce500_spin.c new file mode 100644 index 0000000000..cccd94073a --- /dev/null +++ b/hw/ppce500_spin.c @@ -0,0 +1,215 @@ +/* + * QEMU PowerPC e500v2 ePAPR spinning code + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * This code is not really a device, but models an interface that usually + * firmware takes care of. It's used when QEMU plays the role of firmware. + * + * Specification: + * + * https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf + * + */ + +#include "hw.h" +#include "sysemu.h" +#include "sysbus.h" +#include "kvm.h" + +#define MAX_CPUS 32 + +typedef struct spin_info { + uint64_t addr; + uint64_t r3; + uint32_t resv; + uint32_t pir; + uint64_t reserved; +} __attribute__ ((packed)) SpinInfo; + +typedef struct spin_state { + SysBusDevice busdev; + MemoryRegion iomem; + SpinInfo spin[MAX_CPUS]; +} SpinState; + +typedef struct spin_kick { + CPUState *env; + SpinInfo *spin; +} SpinKick; + +static void spin_reset(void *opaque) +{ + SpinState *s = opaque; + int i; + + for (i = 0; i < MAX_CPUS; i++) { + SpinInfo *info = &s->spin[i]; + + info->pir = i; + info->r3 = i; + info->addr = 1; + } +} + +/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ +static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) +{ + return (ffs(size >> 10) - 1) >> 1; +} + +static void mmubooke_create_initial_mapping(CPUState *env, + target_ulong va, + target_phys_addr_t pa, + target_phys_addr_t len) +{ + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1); + target_phys_addr_t size; + + size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT); + tlb->mas1 = MAS1_VALID | size; + tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M; + tlb->mas7_3 = pa & TARGET_PAGE_MASK; + tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; +} + +static void spin_kick(void *data) +{ + SpinKick *kick = data; + CPUState *env = kick->env; + SpinInfo *curspin = kick->spin; + target_phys_addr_t map_size = 64 * 1024 * 1024; + target_phys_addr_t map_start; + + cpu_synchronize_state(env); + stl_p(&curspin->pir, env->spr[SPR_PIR]); + env->nip = ldq_p(&curspin->addr) & (map_size - 1); + env->gpr[3] = ldq_p(&curspin->r3); + env->gpr[4] = 0; + env->gpr[5] = 0; + env->gpr[6] = 0; + env->gpr[7] = map_size; + env->gpr[8] = 0; + env->gpr[9] = 0; + + map_start = ldq_p(&curspin->addr) & ~(map_size - 1); + mmubooke_create_initial_mapping(env, 0, map_start, map_size); + + env->halted = 0; + env->exception_index = -1; + qemu_cpu_kick(env); +} + +static void spin_write(void *opaque, target_phys_addr_t addr, uint64_t value, + unsigned len) +{ + SpinState *s = opaque; + int env_idx = addr / sizeof(SpinInfo); + CPUState *env; + SpinInfo *curspin = &s->spin[env_idx]; + uint8_t *curspin_p = (uint8_t*)curspin; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->cpu_index == env_idx) { + break; + } + } + + if (!env) { + /* Unknown CPU */ + return; + } + + if (!env->cpu_index) { + /* primary CPU doesn't spin */ + return; + } + + curspin_p = &curspin_p[addr % sizeof(SpinInfo)]; + switch (len) { + case 1: + stb_p(curspin_p, value); + break; + case 2: + stw_p(curspin_p, value); + break; + case 4: + stl_p(curspin_p, value); + break; + } + + if (!(ldq_p(&curspin->addr) & 1)) { + /* run CPU */ + SpinKick kick = { + .env = env, + .spin = curspin, + }; + + run_on_cpu(env, spin_kick, &kick); + } +} + +static uint64_t spin_read(void *opaque, target_phys_addr_t addr, unsigned len) +{ + SpinState *s = opaque; + uint8_t *spin_p = &((uint8_t*)s->spin)[addr]; + + switch (len) { + case 1: + return ldub_p(spin_p); + case 2: + return lduw_p(spin_p); + case 4: + return ldl_p(spin_p); + default: + assert(0); + } +} + +const MemoryRegionOps spin_rw_ops = { + .read = spin_read, + .write = spin_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int ppce500_spin_initfn(SysBusDevice *dev) +{ + SpinState *s; + + s = FROM_SYSBUS(SpinState, sysbus_from_qdev(dev)); + + memory_region_init_io(&s->iomem, &spin_rw_ops, s, "e500 spin pv device", + sizeof(SpinInfo) * MAX_CPUS); + sysbus_init_mmio_region(dev, &s->iomem); + + qemu_register_reset(spin_reset, s); + + return 0; +} + +static SysBusDeviceInfo ppce500_spin_info = { + .init = ppce500_spin_initfn, + .qdev.name = "e500-spin", + .qdev.size = sizeof(SpinState), +}; + +static void ppce500_spin_register(void) +{ + sysbus_register_withprop(&ppce500_spin_info); +} +device_init(ppce500_spin_register); From 10f25a46c5202329cc028bcd79a0ca5f277261a4 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 03:06:12 +0200 Subject: [PATCH 38/87] PPC: E500: Update cpu-release-addr property in cpu nodes The guest OS wants to know where the guest spins, so let's tell him while updating the CPU nodes with the frequencies anyways. Signed-off-by: Alexander Graf --- v1 -> v2: - use new spin table address --- hw/ppce500_mpc8544ds.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 3b8b449a56..a3e1ce481e 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -125,9 +125,15 @@ static int mpc8544_load_device_tree(CPUState *env, for (i = 0; i < smp_cpus; i++) { char cpu_name[128]; + uint64_t cpu_release_addr[] = { + cpu_to_be64(MPC8544_SPIN_BASE + (i * 0x20)) + }; + snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", i); qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); + qemu_devtree_setprop(fdt, cpu_name, "cpu-release-addr", + cpu_release_addr, sizeof(cpu_release_addr)); } for (i = smp_cpus; i < 32; i++) { From 80ad781643b68c91316687051965e12021abeab6 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 22 Jul 2011 13:55:37 +0200 Subject: [PATCH 39/87] device tree: add add_subnode command We want to be able to create subnodes in our device tree, so export it through the qemu device tree abstraction framework. Signed-off-by: Alexander Graf --- device_tree.c | 24 ++++++++++++++++++++++++ device_tree.h | 1 + 2 files changed, 25 insertions(+) diff --git a/device_tree.c b/device_tree.c index 23e89e3b90..f4a78c8217 100644 --- a/device_tree.c +++ b/device_tree.c @@ -118,3 +118,27 @@ int qemu_devtree_nop_node(void *fdt, const char *node_path) return fdt_nop_node(fdt, offset); } + +int qemu_devtree_add_subnode(void *fdt, const char *name) +{ + int offset; + char *dupname = g_strdup(name); + char *basename = strrchr(dupname, '/'); + int retval; + + if (!basename) { + return -1; + } + + basename[0] = '\0'; + basename++; + + offset = fdt_path_offset(fdt, dupname); + if (offset < 0) { + return offset; + } + + retval = fdt_add_subnode(fdt, offset, basename); + g_free(dupname); + return retval; +} diff --git a/device_tree.h b/device_tree.h index 76fce5ffb2..4378685b7a 100644 --- a/device_tree.h +++ b/device_tree.h @@ -23,5 +23,6 @@ int qemu_devtree_setprop_cell(void *fdt, const char *node_path, int qemu_devtree_setprop_string(void *fdt, const char *node_path, const char *property, const char *string); int qemu_devtree_nop_node(void *fdt, const char *node_path); +int qemu_devtree_add_subnode(void *fdt, const char *name); #endif /* __DEVICE_TREE_H__ */ From ccbcfedd17fd2d13521fcee66810d0df464ec1cc Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 23 Jul 2011 10:52:00 +0200 Subject: [PATCH 40/87] device tree: dont fail operations When we screw up and issue an FDT command that doesn't work, we really need to know immediately and usually can't continue to create the machine. To make sure we don't need to add error checking in all device tree modification code users, we can just add the fail checks to the qemu abstract functions. Signed-off-by: Alexander Graf --- device_tree.c | 76 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/device_tree.c b/device_tree.c index f4a78c8217..751538ec1d 100644 --- a/device_tree.c +++ b/device_tree.c @@ -72,56 +72,81 @@ fail: return NULL; } -int qemu_devtree_setprop(void *fdt, const char *node_path, - const char *property, void *val_array, int size) +static int findnode_nofail(void *fdt, const char *node_path) { int offset; offset = fdt_path_offset(fdt, node_path); - if (offset < 0) - return offset; + if (offset < 0) { + fprintf(stderr, "%s Couldn't find node %s: %s\n", __func__, node_path, + fdt_strerror(offset)); + exit(1); + } - return fdt_setprop(fdt, offset, property, val_array, size); + return offset; +} + +int qemu_devtree_setprop(void *fdt, const char *node_path, + const char *property, void *val_array, int size) +{ + int r; + + r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val_array, size); + if (r < 0) { + fprintf(stderr, "%s: Couldn't set %s/%s: %s\n", __func__, node_path, + property, fdt_strerror(r)); + exit(1); + } + + return r; } int qemu_devtree_setprop_cell(void *fdt, const char *node_path, const char *property, uint32_t val) { - int offset; + int r; - offset = fdt_path_offset(fdt, node_path); - if (offset < 0) - return offset; + r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val); + if (r < 0) { + fprintf(stderr, "%s: Couldn't set %s/%s = %#08x: %s\n", __func__, + node_path, property, val, fdt_strerror(r)); + exit(1); + } - return fdt_setprop_cell(fdt, offset, property, val); + return r; } int qemu_devtree_setprop_string(void *fdt, const char *node_path, const char *property, const char *string) { - int offset; + int r; - offset = fdt_path_offset(fdt, node_path); - if (offset < 0) - return offset; + r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string); + if (r < 0) { + fprintf(stderr, "%s: Couldn't set %s/%s = %s: %s\n", __func__, + node_path, property, string, fdt_strerror(r)); + exit(1); + } - return fdt_setprop_string(fdt, offset, property, string); + return r; } int qemu_devtree_nop_node(void *fdt, const char *node_path) { - int offset; + int r; - offset = fdt_path_offset(fdt, node_path); - if (offset < 0) - return offset; + r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path)); + if (r < 0) { + fprintf(stderr, "%s: Couldn't nop node %s: %s\n", __func__, node_path, + fdt_strerror(r)); + exit(1); + } - return fdt_nop_node(fdt, offset); + return r; } int qemu_devtree_add_subnode(void *fdt, const char *name) { - int offset; char *dupname = g_strdup(name); char *basename = strrchr(dupname, '/'); int retval; @@ -133,12 +158,13 @@ int qemu_devtree_add_subnode(void *fdt, const char *name) basename[0] = '\0'; basename++; - offset = fdt_path_offset(fdt, dupname); - if (offset < 0) { - return offset; + retval = fdt_add_subnode(fdt, findnode_nofail(fdt, dupname), basename); + if (retval < 0) { + fprintf(stderr, "FDT: Failed to create subnode %s: %s\n", name, + fdt_strerror(retval)); + exit(1); } - retval = fdt_add_subnode(fdt, offset, basename); g_free(dupname); return retval; } From ded57c5f3a2dd49728b1cb51f17194e880514ff6 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 23 Jul 2011 10:54:11 +0200 Subject: [PATCH 41/87] device tree: give dt more size We currently load a device tree blob and then just take its size x2 to account for modifications we do inside. While this is nice and great, it fails when we have a small device tree as blob and lots of nodes added in machine init code. So for now, just make it 20k bigger than it was before. We maybe want to be more clever about this later. Signed-off-by: Alexander Graf --- device_tree.c | 1 + 1 file changed, 1 insertion(+) diff --git a/device_tree.c b/device_tree.c index 751538ec1d..dc69232f10 100644 --- a/device_tree.c +++ b/device_tree.c @@ -41,6 +41,7 @@ void *load_device_tree(const char *filename_path, int *sizep) } /* Expand to 2x size to give enough room for manipulation. */ + dt_size += 10000; dt_size *= 2; /* First allocate space in qemu for device tree */ fdt = g_malloc0(dt_size); From be637f74d4991089170ddb89a49400b1fb8feff8 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 23 Jul 2011 10:55:50 +0200 Subject: [PATCH 42/87] MPC8544DS: Remove CPU nodes We want to generate the CPU nodes in machine init code, so remove them from the device tree definition that we precompile. Signed-off-by: Alexander Graf --- pc-bios/mpc8544ds.dtb | Bin 2277 -> 2028 bytes pc-bios/mpc8544ds.dts | 12 ------------ 2 files changed, 12 deletions(-) diff --git a/pc-bios/mpc8544ds.dtb b/pc-bios/mpc8544ds.dtb index ae318b1fe83846cc2e133951a3666fcfcdf87f79..c6d302153c7407d5d0127be29b0c35f80e47f8fb 100644 GIT binary patch delta 424 zcmaDV_=aEO0`I@K3=HgV7#J8V7#P?t0BH>%76f7eAO-?P8KC%#jT*{~lRq;qVGNu+ zgGpO80wTx2Se#mvnV92XVrpOj5@H5o79dUoaVFO=n@yHu7E~<+@qhp%%K^lVK&%DC zOh63N(K9)OS(!0yas{(Dk?LOn)z6*G!y?7RuxYXeOPCPDVW4@8NM@d#Jb@*NiQ(ep zFD&Xnqh(mFTTP3F~Xi9v};53e2?3j(nK5CZ{YE>PTIqlPkLJ!3$Ad1_IB zvyO$SiHU;&Seh9~vH-DTazQCb0LJ$Paex5E4+OFmkod`He4yqApb%Vr6B@stfk6!< z4_B}V%tP=uK>19QJs6iW9+>=rQJZnmWEm!T#^aN1n7mbC@*oFs0P!Ut)&gQCAci^e z?&LL0%0TrOh*s~wtStKu$plcqfdJG*M&`*4%wa-|B0wQVBw?w^FPM{<7?mdbu&4v= zD`Bx>Vl; #size-cells = <0>; - - PowerPC,8544@0 { - device_type = "cpu"; - reg = <0x0>; - d-cache-line-size = <32>; // 32 bytes - i-cache-line-size = <32>; // 32 bytes - d-cache-size = <0x8000>; // L1, 32K - i-cache-size = <0x8000>; // L1, 32K - timebase-frequency = <0>; - bus-frequency = <0>; - clock-frequency = <0>; - }; }; memory { From 1e3debf098acfc222344b6afcc45d04f1c7c4185 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 23 Jul 2011 10:56:40 +0200 Subject: [PATCH 43/87] MPC8544DS: Generate CPU nodes on init With this patch, we generate CPU nodes in the machine initialization, giving us the freedom to generate as many nodes as we want and as the machine supports, but only those. This is a first step towards a much cleaner device tree generation infrastructure, where we would not require precompiled dtb blobs anymore. Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 46 ++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index a3e1ce481e..dfa8034a8e 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -123,23 +123,43 @@ static int mpc8544_load_device_tree(CPUState *env, hypercall, sizeof(hypercall)); } - for (i = 0; i < smp_cpus; i++) { + /* We need to generate the cpu nodes in reverse order, so Linux can pick + the first node as boot node and be happy */ + for (i = smp_cpus - 1; i >= 0; i--) { char cpu_name[128]; - uint64_t cpu_release_addr[] = { - cpu_to_be64(MPC8544_SPIN_BASE + (i * 0x20)) - }; + uint64_t cpu_release_addr = cpu_to_be64(MPC8544_SPIN_BASE + (i * 0x20)); - snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", i); + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->cpu_index == i) { + break; + } + } + + if (!env) { + continue; + } + + snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", env->cpu_index); + qemu_devtree_add_subnode(fdt, cpu_name); qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); - qemu_devtree_setprop(fdt, cpu_name, "cpu-release-addr", - cpu_release_addr, sizeof(cpu_release_addr)); - } - - for (i = smp_cpus; i < 32; i++) { - char cpu_name[128]; - snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", i); - qemu_devtree_nop_node(fdt, cpu_name); + qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu"); + qemu_devtree_setprop_cell(fdt, cpu_name, "reg", env->cpu_index); + qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size", + env->dcache_line_size); + qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size", + env->icache_line_size); + qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); + qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); + qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0); + if (env->cpu_index) { + qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled"); + qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); + qemu_devtree_setprop(fdt, cpu_name, "cpu-release-addr", + &cpu_release_addr, sizeof(cpu_release_addr)); + } else { + qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay"); + } } ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); From a2a674204b51ec58c9bbc1bc9de62f68e1fc36f5 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 21 Jul 2011 01:45:37 +0200 Subject: [PATCH 44/87] PPC: E500: Bump CPU count to 15 Now that we have everything in place, make the machine description aware of the fact that we can now handle 15 virtual CPUs! Signed-off-by: Alexander Graf --- v1 -> v2: - Max cpus is 15 because of MPIC --- hw/ppce500_mpc8544ds.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index dfa8034a8e..b86a008c9c 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -396,6 +396,7 @@ static QEMUMachine mpc8544ds_machine = { .name = "mpc8544ds", .desc = "mpc8544ds", .init = mpc8544ds_init, + .max_cpus = 15, }; static void mpc8544ds_machine_init(void) From 0a6b8dde6523a93d263631ddb9a726b2790a3cf8 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 10 Aug 2011 14:21:41 +0200 Subject: [PATCH 45/87] PPC: Add new target config for pseries We only support -M pseries when certain prerequisites are met, such as a PPC64 guest and libfdt. To only gather these requirements in a single place, this patch introduces a new CONFIG_PSERIES variable that gets set when all prerequisites are met. Signed-off-by: Alexander Graf --- Makefile.target | 6 ++---- configure | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile.target b/Makefile.target index ff3efa430c..fc3dfaabe5 100644 --- a/Makefile.target +++ b/Makefile.target @@ -239,10 +239,8 @@ obj-ppc-y += ppc_oldworld.o # NewWorld PowerMac obj-ppc-y += ppc_newworld.o # IBM pSeries (sPAPR) -ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy) -obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o -obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o -endif +obj-ppc-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o +obj-ppc-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o # PowerPC 4xx boards obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-ppc-y += ppc440.o ppc440_bamboo.o diff --git a/configure b/configure index 59b14947ed..29cdd752a9 100755 --- a/configure +++ b/configure @@ -3389,6 +3389,9 @@ case "$target_arch2" in fi fi esac +if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then + echo "CONFIG_PSERIES=y" >> $config_target_mak +fi if test "$target_bigendian" = "yes" ; then echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak fi From 7c6da3deb8b8670043f2ea5b21da939436ce41c3 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 9 Aug 2011 14:02:19 +0200 Subject: [PATCH 46/87] KVM: update kernel headers This patch updates the kvm kernel headers to the latest version. Signed-off-by: Alexander Graf --- linux-headers/asm-powerpc/kvm.h | 23 +++++++++++++++++++++++ linux-headers/asm-x86/kvm_para.h | 14 ++++++++++++++ linux-headers/linux/kvm.h | 25 +++++++++++++++++-------- linux-headers/linux/kvm_para.h | 1 + 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 777d307c71..579e219d69 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -22,6 +22,10 @@ #include +/* Select powerpc specific features in */ +#define __KVM_HAVE_SPAPR_TCE +#define __KVM_HAVE_PPC_SMT + struct kvm_regs { __u64 pc; __u64 cr; @@ -144,6 +148,12 @@ struct kvm_regs { #define KVM_SREGS_E_UPDATE_DEC (1 << 2) #define KVM_SREGS_E_UPDATE_DBSR (1 << 3) +/* + * Book3S special bits to indicate contents in the struct by maintaining + * backwards compatibility with older structs. If adding a new field, + * please make sure to add a flag for that new field */ +#define KVM_SREGS_S_HIOR (1 << 0) + /* * In KVM_SET_SREGS, reserved/pad fields must be left untouched from a * previous KVM_GET_REGS. @@ -169,6 +179,8 @@ struct kvm_sregs { __u64 ibat[8]; __u64 dbat[8]; } ppc32; + __u64 flags; /* KVM_SREGS_S_ */ + __u64 hior; } s; struct { union { @@ -272,4 +284,15 @@ struct kvm_guest_debug_arch { #define KVM_INTERRUPT_UNSET -2U #define KVM_INTERRUPT_SET_LEVEL -3U +/* for KVM_CAP_SPAPR_TCE */ +struct kvm_create_spapr_tce { + __u64 liobn; + __u32 window_size; +}; + +/* for KVM_ALLOCATE_RMA */ +struct kvm_allocate_rma { + __u64 rma_size; +}; + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h index 834d71ea1f..f2ac46a2a2 100644 --- a/linux-headers/asm-x86/kvm_para.h +++ b/linux-headers/asm-x86/kvm_para.h @@ -21,6 +21,7 @@ */ #define KVM_FEATURE_CLOCKSOURCE2 3 #define KVM_FEATURE_ASYNC_PF 4 +#define KVM_FEATURE_STEAL_TIME 5 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. @@ -30,10 +31,23 @@ #define MSR_KVM_WALL_CLOCK 0x11 #define MSR_KVM_SYSTEM_TIME 0x12 +#define KVM_MSR_ENABLED 1 /* Custom MSRs falls in the range 0x4b564d00-0x4b564dff */ #define MSR_KVM_WALL_CLOCK_NEW 0x4b564d00 #define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02 +#define MSR_KVM_STEAL_TIME 0x4b564d03 + +struct kvm_steal_time { + __u64 steal; + __u32 version; + __u32 flags; + __u32 pad[12]; +}; + +#define KVM_STEAL_ALIGNMENT_BITS 5 +#define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1))) +#define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1) #define KVM_MAX_MMU_OP_BATCH 32 diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index fc63b73996..2062375854 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -161,6 +161,7 @@ struct kvm_pit_config { #define KVM_EXIT_NMI 16 #define KVM_EXIT_INTERNAL_ERROR 17 #define KVM_EXIT_OSI 18 +#define KVM_EXIT_PAPR_HCALL 19 /* For KVM_EXIT_INTERNAL_ERROR */ #define KVM_INTERNAL_ERROR_EMULATION 1 @@ -264,6 +265,11 @@ struct kvm_run { struct { __u64 gprs[32]; } osi; + struct { + __u64 nr; + __u64 ret; + __u64 args[9]; + } papr_hcall; /* Fix the size of the union. */ char padding[256]; }; @@ -457,7 +463,7 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_VAPIC 6 #define KVM_CAP_EXT_CPUID 7 #define KVM_CAP_CLOCKSOURCE 8 -#define KVM_CAP_NR_VCPUS 9 /* returns max vcpus per vm */ +#define KVM_CAP_NR_VCPUS 9 /* returns recommended max vcpus per vm */ #define KVM_CAP_NR_MEMSLOTS 10 /* returns max memory slots per vm */ #define KVM_CAP_PIT 11 #define KVM_CAP_NOP_IO_DELAY 12 @@ -544,6 +550,12 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_TSC_CONTROL 60 #define KVM_CAP_GET_TSC_KHZ 61 #define KVM_CAP_PPC_BOOKE_SREGS 62 +#define KVM_CAP_SPAPR_TCE 63 +#define KVM_CAP_PPC_SMT 64 +#define KVM_CAP_PPC_RMA 65 +#define KVM_CAP_MAX_VCPUS 66 /* returns max vcpus per vm */ +#define KVM_CAP_PPC_HIOR 67 +#define KVM_CAP_PPC_PAPR 68 #ifdef KVM_CAP_IRQ_ROUTING @@ -746,6 +758,9 @@ struct kvm_clock_data { /* Available with KVM_CAP_XCRS */ #define KVM_GET_XCRS _IOR(KVMIO, 0xa6, struct kvm_xcrs) #define KVM_SET_XCRS _IOW(KVMIO, 0xa7, struct kvm_xcrs) +#define KVM_CREATE_SPAPR_TCE _IOW(KVMIO, 0xa8, struct kvm_create_spapr_tce) +/* Available with KVM_CAP_RMA */ +#define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) @@ -773,20 +788,14 @@ struct kvm_assigned_pci_dev { struct kvm_assigned_irq { __u32 assigned_dev_id; - __u32 host_irq; + __u32 host_irq; /* ignored (legacy field) */ __u32 guest_irq; __u32 flags; union { - struct { - __u32 addr_lo; - __u32 addr_hi; - __u32 data; - } guest_msi; __u32 reserved[12]; }; }; - struct kvm_assigned_msix_nr { __u32 assigned_dev_id; __u16 entry_nr; diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h index 7bdcf93c1d..b315e27a9c 100644 --- a/linux-headers/linux/kvm_para.h +++ b/linux-headers/linux/kvm_para.h @@ -26,3 +26,4 @@ #include #endif /* __LINUX_KVM_PARA_H */ + From f61b4bedaf3508b0dffa2e4b277af1f6646c3418 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 9 Aug 2011 17:57:37 +0200 Subject: [PATCH 47/87] PPC: Enable to use PAPR with PR style KVM When running PR style KVM, we need to tell the kernel that we want to run in PAPR mode now. This means that we need to pass some more register information down and enable papr mode. We also need to align the HTAB to htab_size boundary. Using this patch, -M pseries works with kvm even on non-hv kvm implementations, as long as the preceding kernel patches are in. Signed-off-by: Alexander Graf --- v1 -> v2: - match on CONFIG_PSERIES v2 -> v3: - remove HIOR pieces from PAPR patch (ABI breakage) --- hw/spapr.c | 14 +++++++++++++- target-ppc/kvm.c | 40 ++++++++++++++++++++++++++++++++++++++++ target-ppc/kvm_ppc.h | 5 +++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/hw/spapr.c b/hw/spapr.c index 8cf93fe51e..c5c9a95197 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -38,6 +38,9 @@ #include "hw/spapr_vio.h" #include "hw/xics.h" +#include "kvm.h" +#include "kvm_ppc.h" + #include #define KERNEL_LOAD_ADDR 0x00000000 @@ -336,12 +339,21 @@ static void ppc_spapr_init(ram_addr_t ram_size, * later we should probably make it scale to the size of guest * RAM */ spapr->htab_size = 1ULL << (pteg_shift + 7); - spapr->htab = g_malloc(spapr->htab_size); + spapr->htab = qemu_memalign(spapr->htab_size, spapr->htab_size); for (env = first_cpu; env != NULL; env = env->next_cpu) { env->external_htab = spapr->htab; env->htab_base = -1; env->htab_mask = spapr->htab_size - 1; + + /* Tell KVM that we're in PAPR mode */ + env->spr[SPR_SDR1] = (unsigned long)spapr->htab | + ((pteg_shift + 7) - 18); + env->spr[SPR_HIOR] = 0; + + if (kvm_enabled()) { + kvmppc_set_papr(env); + } } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin"); diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 77b98c4d73..f65b6e1f4a 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -29,6 +29,10 @@ #include "cpu.h" #include "device_tree.h" +#include "hw/sysbus.h" +#include "hw/spapr.h" +#include "hw/spapr_vio.h" + //#define DEBUG_KVM #ifdef DEBUG_KVM @@ -455,6 +459,14 @@ int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run) dprintf("handle halt\n"); ret = kvmppc_handle_halt(env); break; +#ifdef CONFIG_PSERIES + case KVM_EXIT_PAPR_HCALL: + dprintf("handle PAPR hypercall\n"); + run->papr_hcall.ret = spapr_hypercall(env, run->papr_hcall.nr, + run->papr_hcall.args); + ret = 1; + break; +#endif default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -606,6 +618,34 @@ int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len) return 0; } +void kvmppc_set_papr(CPUState *env) +{ + struct kvm_enable_cap cap; + int ret; + + memset(&cap, 0, sizeof(cap)); + cap.cap = KVM_CAP_PPC_PAPR; + ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &cap); + + if (ret) { + goto fail; + } + + /* + * XXX We set HIOR here. It really should be a qdev property of + * the CPU node, but we don't have CPUs converted to qdev yet. + * + * Once we have qdev CPUs, move HIOR to a qdev property and + * remove this chunk. + */ + /* XXX Set HIOR using new ioctl */ + + return; + +fail: + cpu_abort(env, "This KVM version does not support PAPR\n"); +} + bool kvm_arch_stop_on_emulation_error(CPUState *env) { return true; diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 76f98d9ab2..c484e60bcb 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -17,6 +17,7 @@ uint32_t kvmppc_get_tbfreq(void); uint64_t kvmppc_get_clockfreq(void); int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len); int kvmppc_set_interrupt(CPUState *env, int irq, int level); +void kvmppc_set_papr(CPUState *env); #else @@ -40,6 +41,10 @@ static inline int kvmppc_set_interrupt(CPUState *env, int irq, int level) return -1; } +static inline void kvmppc_set_papr(CPUState *env) +{ +} + #endif #ifndef CONFIG_KVM From 0a8b293893051f5c170ae7a2209c12fb92666027 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 9 Aug 2011 18:07:13 +0200 Subject: [PATCH 48/87] PPC: SPAPR: Use KVM function for time info One of the things we can't fake on PPC is the timer speed. So we need to extract the frequency information from the host and put it back into the guest device tree. Luckily, we already have functions for that from the non-pseries targets, so all we need to do is to connect the dots and the guest suddenly gets to know its real timer speeds. Signed-off-by: Alexander Graf --- hw/spapr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/spapr.c b/hw/spapr.c index c5c9a95197..760e3231d1 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -140,6 +140,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, char *nodename; uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), 0xffffffff, 0xffffffff}; + uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ; + uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; if (asprintf(&nodename, "%s@%x", modelname, index) < 0) { fprintf(stderr, "Allocation failure\n"); @@ -158,10 +160,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, env->dcache_line_size))); _FDT((fdt_property_cell(fdt, "icache-block-size", env->icache_line_size))); - _FDT((fdt_property_cell(fdt, "timebase-frequency", TIMEBASE_FREQ))); - /* Hardcode CPU frequency for now. It's kind of arbitrary on - * full emu, for kvm we should copy it from the host */ - _FDT((fdt_property_cell(fdt, "clock-frequency", 1000000000))); + _FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq))); + _FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq))); _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr))); _FDT((fdt_property(fdt, "ibm,pft-size", pft_size_prop, sizeof(pft_size_prop)))); From cc67b9c89907d752f9970b7078afe69afcf10b7a Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 3 Aug 2011 21:02:14 +0000 Subject: [PATCH 49/87] pseries: Bugfixes for interrupt numbering in XICS code The implementation of the XICS interrupt controller contains several (difficult to trigger) bugs due to the fact that we were not 100% consistent with which irq numbering we used. In most places, global numbers were used as handled by the presentation layer, however a few functions took "local" numberings, that is the source number within the interrupt source controller which is offset from the global number. In most cases the function and its caller agreed on this, but in a few cases it didn't. This patch cleans this up by always using global numbering. Translation to the local number is now always and only done when we look up the individual interrupt source state structure. This should remove the existing bugs and with luck reduce the chances of re-introducing such bugs. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/xics.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/xics.c b/hw/xics.c index 80e064eaa3..1c5eaa4135 100644 --- a/hw/xics.c +++ b/hw/xics.c @@ -185,17 +185,17 @@ static int ics_valid_irq(struct ics_state *ics, uint32_t nr) && (nr < (ics->offset + ics->nr_irqs)); } -static void ics_set_irq_msi(void *opaque, int nr, int val) +static void ics_set_irq_msi(void *opaque, int srcno, int val) { struct ics_state *ics = (struct ics_state *)opaque; - struct ics_irq_state *irq = ics->irqs + nr; + struct ics_irq_state *irq = ics->irqs + srcno; if (val) { if (irq->priority == 0xff) { irq->masked_pending = 1; /* masked pending */ ; } else { - icp_irq(ics->icp, irq->server, nr + ics->offset, irq->priority); + icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); } } } @@ -227,7 +227,7 @@ static void ics_resend_msi(struct ics_state *ics) static void ics_write_xive_msi(struct ics_state *ics, int nr, int server, uint8_t priority) { - struct ics_irq_state *irq = ics->irqs + nr; + struct ics_irq_state *irq = ics->irqs + nr - ics->offset; irq->server = server; irq->priority = priority; @@ -237,7 +237,7 @@ static void ics_write_xive_msi(struct ics_state *ics, int nr, int server, } irq->masked_pending = 0; - icp_irq(ics->icp, server, nr + ics->offset, priority); + icp_irq(ics->icp, server, nr, priority); } static void ics_reject(struct ics_state *ics, int nr) @@ -332,7 +332,7 @@ static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token, return; } - ics_write_xive_msi(ics, nr - ics->offset, server, priority); + ics_write_xive_msi(ics, nr, server, priority); rtas_st(rets, 0, 0); /* Success */ } @@ -386,7 +386,7 @@ static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token, struct ics_irq_state *irq = xics->irqs + (nr - xics->offset); irq->saved_priority = irq->priority; - ics_write_xive_msi(xics, nr - xics->offset, irq->server, 0xff); + ics_write_xive_msi(xics, nr, irq->server, 0xff); #endif rtas_st(rets, 0, 0); /* Success */ @@ -416,8 +416,7 @@ static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token, #if 0 struct ics_irq_state *irq = xics->irqs + (nr - xics->offset); - ics_write_xive_msi(xics, nr - xics->offset, - irq->server, irq->saved_priority); + ics_write_xive_msi(xics, nr, irq->server, irq->saved_priority); #endif rtas_st(rets, 0, 0); /* Success */ From 0c103f8e6999889e974246533bf7bf9c433a0471 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 3 Aug 2011 21:02:17 +0000 Subject: [PATCH 50/87] pseries: Add a phandle to the xicp interrupt controller device tree node Future devices we will be adding to the pseries machine (e.g. PCI) will need nodes in the device tree which explicitly reference the top-level interrupt controller via interrupt-parent or interrupt-map properties. In order to do this, the interrupt controller node needs an assigned phandle. This patch adds the appropriate property, in preparation. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/spapr.c b/hw/spapr.c index 760e3231d1..bb00ae6a6f 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -57,6 +57,8 @@ #define MAX_CPUS 256 #define XICS_IRQS 1024 +#define PHANDLE_XICP 0x00001111 + sPAPREnvironment *spapr; static void *spapr_create_fdt_skel(const char *cpu_model, @@ -202,6 +204,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", interrupt_server_ranges_prop, sizeof(interrupt_server_ranges_prop)))); + _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); + _FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP))); + _FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP))); _FDT((fdt_end_node(fdt))); From 9dfef5aae422d479d8e561889da05cf31d850d5c Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 3 Aug 2011 21:02:18 +0000 Subject: [PATCH 51/87] pseries: interrupt controller should not have a 'reg' property The interrupt controller presented in the device tree for the pseries machine is manipulated by the guest only through hypervisor calls. It has no real or emulated registers for the guest to access. However, it currently has a bogus 'reg' property advertising a register window. Moreover, this property has an invalid format, being a 32-bit zero, when the #address-cells property on the root bus indicates that it needs a 64-bit address. Since the guest never attempts to manipulate the node directly, it works, but it is ugly and can cause warnings when manipulating the device tree in other tools (such as future firmware versions). This patch, therefore, corrects the problem by entirely removing the interrupt-controller node's 'reg' property. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/spapr.c b/hw/spapr.c index bb00ae6a6f..9eefef9015 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -194,12 +194,11 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_end_node(fdt))); /* interrupt controller */ - _FDT((fdt_begin_node(fdt, "interrupt-controller@0"))); + _FDT((fdt_begin_node(fdt, "interrupt-controller"))); _FDT((fdt_property_string(fdt, "device_type", "PowerPC-External-Interrupt-Presentation"))); _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp"))); - _FDT((fdt_property_cell(fdt, "reg", 0))); _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", interrupt_server_ranges_prop, From f73a2575a3bce8a3c487331c918d2c0f9b2e489d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 3 Aug 2011 21:02:19 +0000 Subject: [PATCH 52/87] pseries: More complete WIMG validation in H_ENTER code Currently our implementation of the H_ENTER hypercall, which inserts a mapping in the hash page table assumes that only ordinary memory is ever mapped, and only permits mapping attribute bits accordingly (WIMG==0010). However, we intend to start adding emulated IO to the pseries platform (and real IO with PCI passthrough on kvm) which means this simple test will no longer suffice. This patch extends the h_enter validation code to check if the given address is a RAM address. If it is it enforces WIMG==0010, otherwise it assumes that it is an IO mapping and instead enforces WIMG=010x. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 3 ++- hw/spapr.h | 1 + hw/spapr_hcall.c | 22 ++++++++++++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/hw/spapr.c b/hw/spapr.c index 9eefef9015..00aed62272 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -336,7 +336,8 @@ static void ppc_spapr_init(ram_addr_t ram_size, } /* allocate RAM */ - ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size); + spapr->ram_limit = ram_size; + ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", spapr->ram_limit); cpu_register_physical_memory(0, ram_size, ram_offset); /* allocate hash page table. For now we always make this 16mb, diff --git a/hw/spapr.h b/hw/spapr.h index 009c459a14..3d21b7a1cc 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -10,6 +10,7 @@ typedef struct sPAPREnvironment { struct VIOsPAPRBus *vio_bus; struct icp_state *icp; + target_phys_addr_t ram_limit; void *htab; long htab_size; target_phys_addr_t fdt_addr, rtas_addr; diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index f7ead04a96..70f853cb1b 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -99,6 +99,8 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, target_ulong pte_index = args[1]; target_ulong pteh = args[2]; target_ulong ptel = args[3]; + target_ulong page_shift = 12; + target_ulong raddr; target_ulong i; uint8_t *hpte; @@ -111,6 +113,7 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, #endif if ((ptel & 0xff000) == 0) { /* 16M page */ + page_shift = 24; /* lowest AVA bit must be 0 for 16M pages */ if (pteh & 0x80) { return H_PARAMETER; @@ -120,12 +123,23 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, } } - /* FIXME: bounds check the pa? */ + raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1); - /* Check WIMG */ - if ((ptel & HPTE_R_WIMG) != HPTE_R_M) { - return H_PARAMETER; + if (raddr < spapr->ram_limit) { + /* Regular RAM - should have WIMG=0010 */ + if ((ptel & HPTE_R_WIMG) != HPTE_R_M) { + return H_PARAMETER; + } + } else { + /* Looks like an IO address */ + /* FIXME: What WIMG combinations could be sensible for IO? + * For now we allow WIMG=010x, but are there others? */ + /* FIXME: Should we check against registered IO addresses? */ + if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) { + return H_PARAMETER; + } } + pteh &= ~0x60ULL; if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { From 4e85f82c857550c5864b27ea96571d722bd80b61 Mon Sep 17 00:00:00 2001 From: Elie Richa Date: Fri, 22 Jul 2011 05:58:39 +0000 Subject: [PATCH 53/87] PPC: Fix sync instructions problem in SMP In the current emulation of the load-and-reserve (lwarx) and store-conditional (stwcx.) instructions, the internal reservation mechanism is taken into account, however each CPU has its own reservation information and this information is not synchronized between CPUs to perform proper synchronization. The following test case with 2 CPUs shows that the semantics of the "lwarx" and "stwcx." instructions are not preserved by the emulation. The test case does the following : - CPU0: reserve a memory location - CPU1: reserve the same memory location - CPU0: perform stwcx. on the location The last store-conditional operation succeeds while it is supposed to fail since the reservation was supposed to be lost at the second reserve operation. This (one line) patch fixes this problem in a very simple manner by removing the reservation of a CPU every time it is scheduled (in cpu_exec()). While this is a harsh workaround, it does not affect the guest code much because reservations are usually held for a very short time, that is an lwarx is almost always followed by an stwcx. a few instructions below. Therefore, in most cases, the reservation will be taken and consumed before a CPU switch occurs. However in the rare case where a CPU switch does occur between the lwarx and its corresponding stwcx. this patch solves a potential erroneous behavior of the synchronization instructions. Signed-off-by: Elie Richa Signed-off-by: Alexander Graf --- cpu-exec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cpu-exec.c b/cpu-exec.c index aef66f290c..a9fa608cff 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -217,6 +217,7 @@ int cpu_exec(CPUState *env) #elif defined(TARGET_ARM) #elif defined(TARGET_UNICORE32) #elif defined(TARGET_PPC) + env->reserve_addr = -1; #elif defined(TARGET_LM32) #elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) From 827200a2df044bbedd55c0894d94391263b7654c Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 10 Aug 2011 14:44:20 +0000 Subject: [PATCH 54/87] pseries: Add real mode debugging hcalls PAPR systems support several hypercalls intended for use in real mode debugging tools. These implement reads and writes to arbitrary guest physical addresses. This is useful for real mode software because it allows access to IO addresses and memory outside the RMA without going through the somewhat involved process of setting up the hash page table and enabling translation. We want these so that when we add real IO devices, the SLOF firmware can boot from them without having to enter virtual mode. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr_hcall.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 70f853cb1b..0c61c10bc7 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -463,6 +463,67 @@ static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr, nret, rtas_r3 + 12 + 4*nargs); } +static target_ulong h_logical_load(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong size = args[0]; + target_ulong addr = args[1]; + + switch (size) { + case 1: + args[0] = ldub_phys(addr); + return H_SUCCESS; + case 2: + args[0] = lduw_phys(addr); + return H_SUCCESS; + case 4: + args[0] = ldl_phys(addr); + return H_SUCCESS; + case 8: + args[0] = ldq_phys(addr); + return H_SUCCESS; + } + return H_PARAMETER; +} + +static target_ulong h_logical_store(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong size = args[0]; + target_ulong addr = args[1]; + target_ulong val = args[2]; + + switch (size) { + case 1: + stb_phys(addr, val); + return H_SUCCESS; + case 2: + stw_phys(addr, val); + return H_SUCCESS; + case 4: + stl_phys(addr, val); + return H_SUCCESS; + case 8: + stq_phys(addr, val); + return H_SUCCESS; + } + return H_PARAMETER; +} + +static target_ulong h_logical_icbi(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + /* Nothing to do on emulation, KVM will trap this in the kernel */ + return H_SUCCESS; +} + +static target_ulong h_logical_dcbf(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + /* Nothing to do on emulation, KVM will trap this in the kernel */ + return H_SUCCESS; +} + static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; @@ -527,6 +588,18 @@ static void hypercall_init(void) spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate + * here between the "CI" and the "CACHE" variants, they will use whatever + * mapping attributes qemu is using. When using KVM, the kernel will + * enforce the attributes more strongly + */ + spapr_register_hypercall(H_LOGICAL_CI_LOAD, h_logical_load); + spapr_register_hypercall(H_LOGICAL_CI_STORE, h_logical_store); + spapr_register_hypercall(H_LOGICAL_CACHE_LOAD, h_logical_load); + spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store); + spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi); + spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf); + /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); } From 68722054035304ec84912fadf185483298d82a8d Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 10 Aug 2011 16:36:27 +0000 Subject: [PATCH 55/87] pseries: use macro for firmware filename For some time we've had a nicely defined macro with the filename for our firmware image. However we didn't actually use it in the place we're supposed to. This patch fixes it. Signed-off-by: Nishanth Aravamudan Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/spapr.c b/hw/spapr.c index 00aed62272..91953cfd18 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -442,7 +442,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, "%ldM guest RAM\n", MIN_RAM_SLOF); exit(1); } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin"); + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, FW_FILE_NAME); fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); if (fw_size < 0) { hw_error("qemu: could not load LPAR rtas '%s'\n", filename); From a54fc0800e420df862acf089e638e0fa2d8c3585 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 31 Aug 2011 15:13:41 +0200 Subject: [PATCH 56/87] KVM: Update kernel headers Another round of KVM features, another round of kernel header updates :) Signed-off-by: Alexander Graf --- linux-headers/asm-powerpc/kvm.h | 40 +++++++++++++++++++++++++++++++++ linux-headers/linux/kvm.h | 18 +++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 579e219d69..28eecf04a3 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -284,6 +284,11 @@ struct kvm_guest_debug_arch { #define KVM_INTERRUPT_UNSET -2U #define KVM_INTERRUPT_SET_LEVEL -3U +#define KVM_CPU_440 1 +#define KVM_CPU_E500V2 2 +#define KVM_CPU_3S_32 3 +#define KVM_CPU_3S_64 4 + /* for KVM_CAP_SPAPR_TCE */ struct kvm_create_spapr_tce { __u64 liobn; @@ -295,4 +300,39 @@ struct kvm_allocate_rma { __u64 rma_size; }; +struct kvm_book3e_206_tlb_entry { + __u32 mas8; + __u32 mas1; + __u64 mas2; + __u64 mas7_3; +}; + +struct kvm_book3e_206_tlb_params { + /* + * For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV: + * + * - The number of ways of TLB0 must be a power of two between 2 and + * 16. + * - TLB1 must be fully associative. + * - The size of TLB0 must be a multiple of the number of ways, and + * the number of sets must be a power of two. + * - The size of TLB1 may not exceed 64 entries. + * - TLB0 supports 4 KiB pages. + * - The page sizes supported by TLB1 are as indicated by + * TLB1CFG (if MMUCFG[MAVN] = 0) or TLB1PS (if MMUCFG[MAVN] = 1) + * as returned by KVM_GET_SREGS. + * - TLB2 and TLB3 are reserved, and their entries in tlb_sizes[] + * and tlb_ways[] must be zero. + * + * tlb_ways[n] = tlb_sizes[n] means the array is fully associative. + * + * KVM will adjust TLBnCFG based on the sizes configured here, + * though arrays greater than 2048 entries will have TLBnCFG[NENTRY] + * set to zero. + */ + __u32 tlb_sizes[4]; + __u32 tlb_ways[4]; + __u32 reserved[8]; +}; + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 2062375854..8bb6cde856 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -556,6 +556,7 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_MAX_VCPUS 66 /* returns max vcpus per vm */ #define KVM_CAP_PPC_HIOR 67 #define KVM_CAP_PPC_PAPR 68 +#define KVM_CAP_SW_TLB 69 #ifdef KVM_CAP_IRQ_ROUTING @@ -635,6 +636,21 @@ struct kvm_clock_data { __u32 pad[9]; }; +#define KVM_MMU_FSL_BOOKE_NOHV 0 +#define KVM_MMU_FSL_BOOKE_HV 1 + +struct kvm_config_tlb { + __u64 params; + __u64 array; + __u32 mmu_type; + __u32 array_len; +}; + +struct kvm_dirty_tlb { + __u64 bitmap; + __u32 num_dirty; +}; + /* * ioctls for VM fds */ @@ -761,6 +777,8 @@ struct kvm_clock_data { #define KVM_CREATE_SPAPR_TCE _IOW(KVMIO, 0xa8, struct kvm_create_spapr_tce) /* Available with KVM_CAP_RMA */ #define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma) +/* Available with KVM_CAP_SW_TLB */ +#define KVM_DIRTY_TLB _IOW(KVMIO, 0xaa, struct kvm_dirty_tlb) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) From 93dd5e852c043f7b86595e1bfa0b4f9a39a6acfb Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 31 Aug 2011 11:26:56 +0000 Subject: [PATCH 57/87] kvm: ppc: booke206: use MMU API Share the TLB array with KVM. This allows us to set the initial TLB both on initial boot and reset, is useful for debugging, and could eventually be used to support migration. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 2 + target-ppc/cpu.h | 2 + target-ppc/kvm.c | 85 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index b86a008c9c..61151d886c 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -189,6 +189,8 @@ static void mmubooke_create_initial_mapping(CPUState *env, tlb->mas2 = va & TARGET_PAGE_MASK; tlb->mas7_3 = pa & TARGET_PAGE_MASK; tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; + + env->tlb_dirty = true; } static void mpc8544ds_cpu_reset_sec(void *opaque) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index b8d42e0b2c..3e7f797e0b 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -934,6 +934,8 @@ struct CPUPPCState { ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ /* 403 dedicated access protection registers */ target_ulong pb[4]; + bool tlb_dirty; /* Set to non-zero when modifying TLB */ + bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */ #endif /* Other registers */ diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index f65b6e1f4a..35a6f1086e 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -112,6 +112,52 @@ static int kvm_arch_sync_sregs(CPUState *cenv) return kvm_vcpu_ioctl(cenv, KVM_SET_SREGS, &sregs); } +/* Set up a shared TLB array with KVM */ +static int kvm_booke206_tlb_init(CPUState *env) +{ + struct kvm_book3e_206_tlb_params params = {}; + struct kvm_config_tlb cfg = {}; + struct kvm_enable_cap encap = {}; + unsigned int entries = 0; + int ret, i; + + if (!kvm_enabled() || + !kvm_check_extension(env->kvm_state, KVM_CAP_SW_TLB)) { + return 0; + } + + assert(ARRAY_SIZE(params.tlb_sizes) == BOOKE206_MAX_TLBN); + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + params.tlb_sizes[i] = booke206_tlb_size(env, i); + params.tlb_ways[i] = booke206_tlb_ways(env, i); + entries += params.tlb_sizes[i]; + } + + assert(entries == env->nb_tlb); + assert(sizeof(struct kvm_book3e_206_tlb_entry) == sizeof(ppcmas_tlb_t)); + + env->tlb_dirty = true; + + cfg.array = (uintptr_t)env->tlb.tlbm; + cfg.array_len = sizeof(ppcmas_tlb_t) * entries; + cfg.params = (uintptr_t)¶ms; + cfg.mmu_type = KVM_MMU_FSL_BOOKE_NOHV; + + encap.cap = KVM_CAP_SW_TLB; + encap.args[0] = (uintptr_t)&cfg; + + ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &encap); + if (ret < 0) { + fprintf(stderr, "%s: couldn't enable KVM_CAP_SW_TLB: %s\n", + __func__, strerror(-ret)); + return ret; + } + + env->kvm_sw_tlb = true; + return 0; +} + int kvm_arch_init_vcpu(CPUState *cenv) { int ret; @@ -123,6 +169,15 @@ int kvm_arch_init_vcpu(CPUState *cenv) idle_timer = qemu_new_timer_ns(vm_clock, kvm_kick_env, cenv); + /* Some targets support access to KVM's guest TLB. */ + switch (cenv->mmu_model) { + case POWERPC_MMU_BOOKE206: + ret = kvm_booke206_tlb_init(cenv); + break; + default: + break; + } + return ret; } @@ -130,6 +185,31 @@ void kvm_arch_reset_vcpu(CPUState *env) { } +static void kvm_sw_tlb_put(CPUState *env) +{ + struct kvm_dirty_tlb dirty_tlb; + unsigned char *bitmap; + int ret; + + if (!env->kvm_sw_tlb) { + return; + } + + bitmap = g_malloc((env->nb_tlb + 7) / 8); + memset(bitmap, 0xFF, (env->nb_tlb + 7) / 8); + + dirty_tlb.bitmap = (uintptr_t)bitmap; + dirty_tlb.num_dirty = env->nb_tlb; + + ret = kvm_vcpu_ioctl(env, KVM_DIRTY_TLB, &dirty_tlb); + if (ret) { + fprintf(stderr, "%s: KVM_DIRTY_TLB: %s\n", + __func__, strerror(-ret)); + } + + g_free(bitmap); +} + int kvm_arch_put_registers(CPUState *env, int level) { struct kvm_regs regs; @@ -167,6 +247,11 @@ int kvm_arch_put_registers(CPUState *env, int level) if (ret < 0) return ret; + if (env->tlb_dirty) { + kvm_sw_tlb_put(env); + env->tlb_dirty = false; + } + return ret; } From bebabbc7aa7d1c5a45ee325838adf58a19f1b1ee Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Thu, 18 Aug 2011 10:38:42 +0000 Subject: [PATCH 58/87] ppc: booke206: add "info tlb" support Signed-off-by: Scott Wood Signed-off-by: Alexander Graf --- hmp-commands.hx | 2 +- monitor.c | 5 +-- target-ppc/cpu.h | 2 ++ target-ppc/helper.c | 88 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 9e1cca8e3d..506014c13f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1306,7 +1306,7 @@ show i8259 (PIC) state @item info pci show emulated PCI device info @item info tlb -show virtual to physical memory mappings (i386, SH4 and SPARC only) +show virtual to physical memory mappings (i386, SH4, SPARC, and PPC only) @item info mem show the active virtual memory mappings (i386 only) @item info jit diff --git a/monitor.c b/monitor.c index d323ea5851..58e149fe0c 100644 --- a/monitor.c +++ b/monitor.c @@ -2462,7 +2462,7 @@ static void tlb_info(Monitor *mon) #endif -#if defined(TARGET_SPARC) +#if defined(TARGET_SPARC) || defined(TARGET_PPC) static void tlb_info(Monitor *mon) { CPUState *env1 = mon_get_cpu(); @@ -2965,7 +2965,8 @@ static const mon_cmd_t info_cmds[] = { .user_print = do_pci_info_print, .mhandler.info_new = do_pci_info, }, -#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) +#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \ + defined(TARGET_PPC) { .name = "tlb", .args_type = "", diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 3e7f797e0b..5200e6e8c6 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -2045,4 +2045,6 @@ static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) env->nip = tb->pc; } +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env); + #endif /* !defined (__CPU_PPC_H__) */ diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 96ea46494a..4b3731e88a 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -1465,6 +1465,94 @@ found_tlb: return ret; } +static const char *book3e_tsize_to_str[32] = { + "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", + "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", + "1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G", + "1T", "2T" +}; + +static void mmubooke206_dump_one_tlb(FILE *f, fprintf_function cpu_fprintf, + CPUState *env, int tlbn, int offset, + int tlbsize) +{ + ppcmas_tlb_t *entry; + int i; + + cpu_fprintf(f, "\nTLB%d:\n", tlbn); + cpu_fprintf(f, "Effective Physical Size TID TS SRWX URWX WIMGE U0123\n"); + + entry = &env->tlb.tlbm[offset]; + for (i = 0; i < tlbsize; i++, entry++) { + target_phys_addr_t ea, pa, size; + int tsize; + + if (!(entry->mas1 & MAS1_VALID)) { + continue; + } + + tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + size = 1024ULL << tsize; + ea = entry->mas2 & ~(size - 1); + pa = entry->mas7_3 & ~(size - 1); + + cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c U%c%c%c %c%c%c%c%c U%c%c%c%c\n", + (uint64_t)ea, (uint64_t)pa, + book3e_tsize_to_str[tsize], + (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT, + (entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT, + entry->mas7_3 & MAS3_SR ? 'R' : '-', + entry->mas7_3 & MAS3_SW ? 'W' : '-', + entry->mas7_3 & MAS3_SX ? 'X' : '-', + entry->mas7_3 & MAS3_UR ? 'R' : '-', + entry->mas7_3 & MAS3_UW ? 'W' : '-', + entry->mas7_3 & MAS3_UX ? 'X' : '-', + entry->mas2 & MAS2_W ? 'W' : '-', + entry->mas2 & MAS2_I ? 'I' : '-', + entry->mas2 & MAS2_M ? 'M' : '-', + entry->mas2 & MAS2_G ? 'G' : '-', + entry->mas2 & MAS2_E ? 'E' : '-', + entry->mas7_3 & MAS3_U0 ? '0' : '-', + entry->mas7_3 & MAS3_U1 ? '1' : '-', + entry->mas7_3 & MAS3_U2 ? '2' : '-', + entry->mas7_3 & MAS3_U3 ? '3' : '-'); + } +} + +static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf, + CPUState *env) +{ + int offset = 0; + int i; + + if (kvm_enabled() && !env->kvm_sw_tlb) { + cpu_fprintf(f, "Cannot access KVM TLB\n"); + return; + } + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int size = booke206_tlb_size(env, i); + + if (size == 0) { + continue; + } + + mmubooke206_dump_one_tlb(f, cpu_fprintf, env, i, offset, size); + offset += size; + } +} + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE206: + mmubooke206_dump_mmu(f, cpu_fprintf, env); + break; + default: + cpu_fprintf(f, "%s: unimplemented\n", __func__); + } +} + static inline int check_physical(CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw) { From 2bd9543cd303d9f6cbd37b7466bb03543035156b Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Thu, 18 Aug 2011 10:38:40 +0000 Subject: [PATCH 59/87] ppc: booke206: use MAV=2.0 TSIZE definition, fix 4G pages This definition is backward compatible with MAV=1.0 as long as the guest does not set reserved bits in MAS1/MAS4. Also, fix the shift in booke206_tlb_to_page_size -- it's the base that should be able to hold a 4G page size, not the shift count. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 2 +- target-ppc/cpu.h | 4 ++-- target-ppc/helper.c | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 61151d886c..8095516f43 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -174,7 +174,7 @@ out: /* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) { - return (ffs(size >> 10) - 1) >> 1; + return ffs(size >> 10) - 1; } static void mmubooke_create_initial_mapping(CPUState *env, diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 5200e6e8c6..32706dff7a 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -667,8 +667,8 @@ enum { #define MAS0_ATSEL_TLB 0 #define MAS0_ATSEL_LRAT MAS0_ATSEL -#define MAS1_TSIZE_SHIFT 8 -#define MAS1_TSIZE_MASK (0xf << MAS1_TSIZE_SHIFT) +#define MAS1_TSIZE_SHIFT 7 +#define MAS1_TSIZE_MASK (0x1f << MAS1_TSIZE_SHIFT) #define MAS1_TS_SHIFT 12 #define MAS1_TS (1 << MAS1_TS_SHIFT) diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 4b3731e88a..6339be3a75 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -1293,7 +1293,7 @@ target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb) { uint32_t tlbncfg; int tlbn = booke206_tlbm_to_tlbn(env, tlb); - target_phys_addr_t tlbm_size; + int tlbm_size; tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; @@ -1301,9 +1301,10 @@ target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb) tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; } else { tlbm_size = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT; + tlbm_size <<= 1; } - return (1 << (tlbm_size << 1)) << 10; + return 1024ULL << tlbm_size; } /* TLB check function for MAS based SoftTLBs */ From 697ab892786d47008807a49f57b2fd86adfcd098 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 31 Aug 2011 15:45:10 +0000 Subject: [PATCH 60/87] Implement POWER7's CFAR in TCG This patch implements support for the CFAR SPR on POWER7 (Come From Address Register), which snapshots the PC value at the time of a branch or an rfid. The latest powerpc-next kernel also catches it and can show it in xmon or in the signal frames. This works well enough to let recent kernels boot (which otherwise oops on the CFAR access). It hasn't been tested enough to be confident that the CFAR values are actually accurate, but one thing at a time. Signed-off-by: Ben Herrenschmidt Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 8 ++++++++ target-ppc/translate.c | 28 ++++++++++++++++++++++++++++ target-ppc/translate_init.c | 23 ++++++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 32706dff7a..3f4af22397 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -555,6 +555,8 @@ enum { /* Decrementer clock: RTC clock (POWER, 601) or bus clock */ POWERPC_FLAG_RTC_CLK = 0x00010000, POWERPC_FLAG_BUS_CLK = 0x00020000, + /* Has CFAR */ + POWERPC_FLAG_CFAR = 0x00040000, }; /*****************************************************************************/ @@ -872,6 +874,10 @@ struct CPUPPCState { target_ulong ctr; /* condition register */ uint32_t crf[8]; +#if defined(TARGET_PPC64) + /* CFAR */ + target_ulong cfar; +#endif /* XER */ target_ulong xer; /* Reservation address */ @@ -1204,6 +1210,7 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) #define SPR_601_UDECR (0x006) #define SPR_LR (0x008) #define SPR_CTR (0x009) +#define SPR_DSCR (0x011) #define SPR_DSISR (0x012) #define SPR_DAR (0x013) /* DAE for PowerPC 601 */ #define SPR_601_RTCU (0x014) @@ -1212,6 +1219,7 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) #define SPR_SDR1 (0x019) #define SPR_SRR0 (0x01A) #define SPR_SRR1 (0x01B) +#define SPR_CFAR (0x01C) #define SPR_AMR (0x01D) #define SPR_BOOKE_PID (0x030) #define SPR_BOOKE_DECAR (0x036) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 4277460692..1e362fc238 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -69,6 +69,9 @@ static TCGv cpu_nip; static TCGv cpu_msr; static TCGv cpu_ctr; static TCGv cpu_lr; +#if defined(TARGET_PPC64) +static TCGv cpu_cfar; +#endif static TCGv cpu_xer; static TCGv cpu_reserve; static TCGv_i32 cpu_fpscr; @@ -154,6 +157,11 @@ void ppc_translate_init(void) cpu_lr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, lr), "lr"); +#if defined(TARGET_PPC64) + cpu_cfar = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cfar), "cfar"); +#endif + cpu_xer = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, xer), "xer"); @@ -187,6 +195,7 @@ typedef struct DisasContext { int le_mode; #if defined(TARGET_PPC64) int sf_mode; + int has_cfar; #endif int fpu_enabled; int altivec_enabled; @@ -3345,6 +3354,14 @@ static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) /* stfiwx */ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX); +static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip) +{ +#if defined(TARGET_PPC64) + if (ctx->has_cfar) + tcg_gen_movi_tl(cpu_cfar, nip); +#endif +} + /*** Branch ***/ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { @@ -3407,6 +3424,7 @@ static void gen_b(DisasContext *ctx) target = li; if (LK(ctx->opcode)) gen_setlr(ctx, ctx->nip); + gen_update_cfar(ctx, ctx->nip); gen_goto_tb(ctx, 0, target); } @@ -3469,6 +3487,7 @@ static inline void gen_bcond(DisasContext *ctx, int type) } tcg_temp_free_i32(temp); } + gen_update_cfar(ctx, ctx->nip); if (type == BCOND_IM) { target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); if (likely(AA(ctx->opcode) == 0)) { @@ -3580,6 +3599,7 @@ static void gen_rfi(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } + gen_update_cfar(ctx, ctx->nip); gen_helper_rfi(); gen_sync_exception(ctx); #endif @@ -3596,6 +3616,7 @@ static void gen_rfid(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } + gen_update_cfar(ctx, ctx->nip); gen_helper_rfid(); gen_sync_exception(ctx); #endif @@ -9263,6 +9284,12 @@ void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf, */ } +#if defined(TARGET_PPC64) + if (env->flags & POWERPC_FLAG_CFAR) { + cpu_fprintf(f, " CFAR " TARGET_FMT_lx"\n", env->cfar); + } +#endif + switch (env->mmu_model) { case POWERPC_MMU_32B: case POWERPC_MMU_601: @@ -9371,6 +9398,7 @@ static inline void gen_intermediate_code_internal(CPUState *env, ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0; #if defined(TARGET_PPC64) ctx.sf_mode = msr_sf; + ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); #endif ctx.fpu_enabled = msr_fp; if ((env->flags & POWERPC_FLAG_SPE) && msr_spe) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 9ea193dcf6..211f3bd0c1 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -129,6 +129,19 @@ static void spr_write_lr (void *opaque, int sprn, int gprn) tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]); } +/* CFAR */ +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +static void spr_read_cfar (void *opaque, int gprn, int sprn) +{ + tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar); +} + +static void spr_write_cfar (void *opaque, int sprn, int gprn) +{ + tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]); +} +#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ + /* CTR */ static void spr_read_ctr (void *opaque, int gprn, int sprn) { @@ -6489,7 +6502,7 @@ static void init_proc_970MP (CPUPPCState *env) #define POWERPC_BFDM_POWER7 (bfd_mach_ppc64) #define POWERPC_FLAG_POWER7 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) + POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR) #define check_pow_POWER7 check_pow_nocheck static void init_proc_POWER7 (CPUPPCState *env) @@ -6508,6 +6521,14 @@ static void init_proc_POWER7 (CPUPPCState *env) &spr_read_purr, SPR_NOACCESS, &spr_read_purr, SPR_NOACCESS, 0x00000000); + spr_register(env, SPR_CFAR, "SPR_CFAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_cfar, &spr_write_cfar, + 0x00000000); + spr_register(env, SPR_DSCR, "SPR_DSCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); #endif /* !CONFIG_USER_ONLY */ /* Memory management */ /* XXX : not implemented */ From a3d0abaecaa2981ad8c0036b6caf0ff1324fa57d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 31 Aug 2011 15:50:50 +0000 Subject: [PATCH 61/87] pseries: Implement hcall-bulk hypervisor interface This patch adds support for the H_REMOVE_BULK hypercall on the pseries machine. Strictly speaking this isn't necessarym since the kernel will only attempt to use this if hcall-bulk is advertised in the device tree, which previously it was not. Adding this support may give a marginal performance increase, but more importantly it reduces the differences between the emulated machine and an existing PowerVM or kvm system, both of which already implement hcall-bulk. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 2 +- hw/spapr_hcall.c | 125 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 114 insertions(+), 13 deletions(-) diff --git a/hw/spapr.c b/hw/spapr.c index 91953cfd18..deb4ae5417 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -75,7 +75,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)}; char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt" - "\0hcall-tce\0hcall-vio\0hcall-splpar"; + "\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk"; uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; int i; char *modelname; diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 0c61c10bc7..84281be9e2 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -174,20 +174,26 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) +enum { + REMOVE_SUCCESS = 0, + REMOVE_NOT_FOUND = 1, + REMOVE_PARM = 2, + REMOVE_HW = 3, +}; + +static target_ulong remove_hpte(CPUState *env, target_ulong ptex, + target_ulong avpn, + target_ulong flags, + target_ulong *vp, target_ulong *rp) { - target_ulong flags = args[0]; - target_ulong pte_index = args[1]; - target_ulong avpn = args[2]; uint8_t *hpte; target_ulong v, r, rb; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { - return H_PARAMETER; + if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { + return REMOVE_PARM; } - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64); while (!lock_hpte(hpte, HPTE_V_HVLOCK)) { /* We have no real concurrency in qemu soft-emulation, so we * will never actually have a contested lock */ @@ -202,14 +208,106 @@ static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, ((flags & H_ANDCOND) && (v & avpn) != 0)) { stq_p(hpte, v & ~HPTE_V_HVLOCK); assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); - return H_NOT_FOUND; + return REMOVE_NOT_FOUND; } - args[0] = v & ~HPTE_V_HVLOCK; - args[1] = r; + *vp = v & ~HPTE_V_HVLOCK; + *rp = r; stq_p(hpte, 0); - rb = compute_tlbie_rb(v, r, pte_index); + rb = compute_tlbie_rb(v, r, ptex); ppc_tlb_invalidate_one(env, rb); assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); + return REMOVE_SUCCESS; +} + +static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong pte_index = args[1]; + target_ulong avpn = args[2]; + int ret; + + ret = remove_hpte(env, pte_index, avpn, flags, + &args[0], &args[1]); + + switch (ret) { + case REMOVE_SUCCESS: + return H_SUCCESS; + + case REMOVE_NOT_FOUND: + return H_NOT_FOUND; + + case REMOVE_PARM: + return H_PARAMETER; + + case REMOVE_HW: + return H_HARDWARE; + } + + assert(0); +} + +#define H_BULK_REMOVE_TYPE 0xc000000000000000ULL +#define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL +#define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL +#define H_BULK_REMOVE_END 0xc000000000000000ULL +#define H_BULK_REMOVE_CODE 0x3000000000000000ULL +#define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL +#define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL +#define H_BULK_REMOVE_PARM 0x2000000000000000ULL +#define H_BULK_REMOVE_HW 0x3000000000000000ULL +#define H_BULK_REMOVE_RC 0x0c00000000000000ULL +#define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL +#define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL +#define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL +#define H_BULK_REMOVE_AVPN 0x0200000000000000ULL +#define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL + +#define H_BULK_REMOVE_MAX_BATCH 4 + +static target_ulong h_bulk_remove(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + int i; + + for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { + target_ulong *tsh = &args[i*2]; + target_ulong tsl = args[i*2 + 1]; + target_ulong v, r, ret; + + if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) { + break; + } else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) { + return H_PARAMETER; + } + + *tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS; + *tsh |= H_BULK_REMOVE_RESPONSE; + + if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) { + *tsh |= H_BULK_REMOVE_PARM; + return H_PARAMETER; + } + + ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl, + (*tsh & H_BULK_REMOVE_FLAGS) >> 26, + &v, &r); + + *tsh |= ret << 60; + + switch (ret) { + case REMOVE_SUCCESS: + *tsh |= (r & (HPTE_R_C | HPTE_R_R)) << 43; + break; + + case REMOVE_PARM: + return H_PARAMETER; + + case REMOVE_HW: + return H_HARDWARE; + } + } + return H_SUCCESS; } @@ -581,6 +679,9 @@ static void hypercall_init(void) spapr_register_hypercall(H_REMOVE, h_remove); spapr_register_hypercall(H_PROTECT, h_protect); + /* hcall-bulk */ + spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); + /* hcall-dabr */ spapr_register_hypercall(H_SET_DABR, h_set_dabr); From f5b6ffcf2a94337df31e801dd11b34896bd4fe2b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Aug 2011 05:28:52 +0000 Subject: [PATCH 62/87] vscsi: send the CHECK_CONDITION status down together with autosense data I introduced this bug in commit 05751d3 (vscsi: always use get_sense, 2011-08-03) because at the time there was no way to expose a sense condition to SLOF and Linux manages to work around the bug. However, the bug becomes evident now that SCSI devices also report unit attention on reset. SLOF also has problems dealing with unit attention conditions, so it still will not boot even with this fix (just like OpenBIOS). IBM folks are aware of their part of the bug. :-) Reported-by: Thomas Huth Signed-off-by: Paolo Bonzini Acked-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr_vscsi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 6fc82f6bd7..e8426d7c5e 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -483,7 +483,6 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) if (status == CHECK_CONDITION) { req->senselen = scsi_req_get_sense(req->sreq, req->sense, sizeof(req->sense)); - status = 0; dprintf("VSCSI: Sense data, %d bytes:\n", len); dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n", req->sense[0], req->sense[1], req->sense[2], req->sense[3], From 5a576fb3e20c3087a6d30be5a94550ace003c6d7 Mon Sep 17 00:00:00 2001 From: Fabien Chouteau Date: Thu, 1 Sep 2011 04:56:00 +0000 Subject: [PATCH 63/87] Gdbstub: handle read of fpscr Signed-off-by: Fabien Chouteau Signed-off-by: Alexander Graf --- gdbstub.c | 2 +- target-ppc/translate_init.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 12dd100af4..1d99e19258 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -733,7 +733,7 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) { if (gdb_has_xml) return 0; - GET_REG32(0); /* fpscr */ + GET_REG32(env->fpscr); } } } diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 211f3bd0c1..d09c7ca98b 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -9700,8 +9700,7 @@ static int gdb_get_float_reg(CPUState *env, uint8_t *mem_buf, int n) return 8; } if (n == 32) { - /* FPSCR not implemented */ - memset(mem_buf, 0, 4); + stl_p(mem_buf, env->fpscr); return 4; } return 0; From e5697f20a262e1fbfd8288950d043f359dc19f60 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 5 Sep 2011 03:02:29 +0000 Subject: [PATCH 64/87] ppc405: use RAM_ADDR_FMT instead of %08lx The RAM_ADDR_FMT macro hides the type of ram_addr_t so that format strings can be safely used. Make sure to use RAM_ADDR_FMT so that the build works on 32-bit hosts with Xen enabled. Whether Xen should affect ppc TCG targets is questionable but a separate issue. Signed-off-by: Stefan Hajnoczi Signed-off-by: Alexander Graf --- hw/ppc405_boards.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/ppc405_boards.c b/hw/ppc405_boards.c index e6c8ac67d9..712a6bebe3 100644 --- a/hw/ppc405_boards.c +++ b/hw/ppc405_boards.c @@ -213,7 +213,8 @@ static void ref405ep_init (ram_addr_t ram_size, sram_size = 512 * 1024; sram_offset = qemu_ram_alloc(NULL, "ef405ep.sram", sram_size); #ifdef DEBUG_BOARD_INIT - printf("%s: register SRAM at offset %08lx\n", __func__, sram_offset); + printf("%s: register SRAM at offset " RAM_ADDR_FMT "\n", + __func__, sram_offset); #endif cpu_register_physical_memory(0xFFF00000, sram_size, sram_offset | IO_MEM_RAM); @@ -357,7 +358,7 @@ static void ref405ep_init (ram_addr_t ram_size, #ifdef DEBUG_BOARD_INIT printf("%s: Done\n", __func__); #endif - printf("bdloc %016lx\n", (unsigned long)bdloc); + printf("bdloc " RAM_ADDR_FMT "\n", bdloc); } static QEMUMachine ref405ep_machine = { From 8d3a8c1e77b0485d4335aa3733c550dc606ac819 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 7 Sep 2011 13:41:54 +0200 Subject: [PATCH 65/87] openpic: Unfold read_IRQreg The helper function read_IRQreg was always called with a specific argument on the type of register to access. Inside the function we were simply doing a switch on that constant argument again. It's a lot easier to just unfold this into two separate functions and call each individually. Reported-by: Blue Swirl Signed-off-by: Alexander Graf --- hw/openpic.c | 56 +++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/hw/openpic.c b/hw/openpic.c index 03e442b1f5..fbd8837d97 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -472,20 +472,14 @@ static void openpic_reset (void *opaque) opp->glbc = 0x00000000; } -static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg) +static inline uint32_t read_IRQreg_ide(openpic_t *opp, int n_IRQ) { - uint32_t retval; + return opp->src[n_IRQ].ide; +} - switch (reg) { - case IRQ_IPVP: - retval = opp->src[n_IRQ].ipvp; - break; - case IRQ_IDE: - retval = opp->src[n_IRQ].ide; - break; - } - - return retval; +static inline uint32_t read_IRQreg_ipvp(openpic_t *opp, int n_IRQ) +{ + return opp->src[n_IRQ].ipvp; } static inline void write_IRQreg (openpic_t *opp, int n_IRQ, @@ -523,10 +517,10 @@ static uint32_t read_doorbell_register (openpic_t *opp, switch (offset) { case DBL_IPVP_OFFSET: - retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, IRQ_DBL0 + n_dbl); break; case DBL_IDE_OFFSET: - retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE); + retval = read_IRQreg_ide(opp, IRQ_DBL0 + n_dbl); break; case DBL_DMR_OFFSET: retval = opp->doorbells[n_dbl].dmr; @@ -564,10 +558,10 @@ static uint32_t read_mailbox_register (openpic_t *opp, retval = opp->mailboxes[n_mbx].mbr; break; case MBX_IVPR_OFFSET: - retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, IRQ_MBX0 + n_mbx); break; case MBX_DMR_OFFSET: - retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE); + retval = read_IRQreg_ide(opp, IRQ_MBX0 + n_mbx); break; } @@ -695,7 +689,7 @@ static uint32_t openpic_gbl_read (void *opaque, target_phys_addr_t addr) { int idx; idx = (addr - 0x10A0) >> 4; - retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, opp->irq_ipi0 + idx); } break; case 0x10E0: /* SPVE */ @@ -765,10 +759,10 @@ static uint32_t openpic_timer_read (void *opaque, uint32_t addr) retval = opp->timers[idx].tibc; break; case 0x20: /* TIPV */ - retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, opp->irq_tim0 + idx); break; case 0x30: /* TIDE */ - retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE); + retval = read_IRQreg_ide(opp, opp->irq_tim0 + idx); break; } DPRINTF("%s: => %08x\n", __func__, retval); @@ -809,10 +803,10 @@ static uint32_t openpic_src_read (void *opaque, uint32_t addr) idx = addr >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(opp, idx, IRQ_IDE); + retval = read_IRQreg_ide(opp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(opp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); @@ -1368,13 +1362,13 @@ static uint32_t mpic_timer_read (void *opaque, target_phys_addr_t addr) retval = mpp->timers[idx].tibc; break; case 0x20: /* TIPV */ - retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, MPIC_TMR_IRQ + idx); break; case 0x30: /* TIDR */ if ((addr &0xF0) == 0XF0) retval = mpp->dst[cpu].tfrr; else - retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, MPIC_TMR_IRQ + idx); break; } DPRINTF("%s: => %08x\n", __func__, retval); @@ -1421,10 +1415,10 @@ static uint32_t mpic_src_ext_read (void *opaque, target_phys_addr_t addr) idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(mpp, idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(mpp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); } @@ -1471,10 +1465,10 @@ static uint32_t mpic_src_int_read (void *opaque, target_phys_addr_t addr) idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(mpp, idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(mpp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); } @@ -1521,10 +1515,10 @@ static uint32_t mpic_src_msg_read (void *opaque, target_phys_addr_t addr) idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(mpp, idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(mpp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); } @@ -1570,10 +1564,10 @@ static uint32_t mpic_src_msi_read (void *opaque, target_phys_addr_t addr) idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(mpp, idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(mpp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); } From 11de8b716665f1f6d5e586c62411f47ae539c0dc Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 7 Sep 2011 13:47:22 +0200 Subject: [PATCH 66/87] openpic: Unfold write_IRQreg The helper function write_IRQreg was always called with a specific argument on the type of register to access. Inside the function we were simply doing a switch on that constant argument again. It's a lot easier to just unfold this into two separate functions and call each individually. Reported-by: Blue Swirl Signed-off-by: Alexander Graf --- hw/openpic.c | 79 ++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/hw/openpic.c b/hw/openpic.c index fbd8837d97..43b8f275d6 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -482,30 +482,25 @@ static inline uint32_t read_IRQreg_ipvp(openpic_t *opp, int n_IRQ) return opp->src[n_IRQ].ipvp; } -static inline void write_IRQreg (openpic_t *opp, int n_IRQ, - uint32_t reg, uint32_t val) +static inline void write_IRQreg_ide(openpic_t *opp, int n_IRQ, uint32_t val) { uint32_t tmp; - switch (reg) { - case IRQ_IPVP: - /* NOTE: not fully accurate for special IRQs, but simple and - sufficient */ - /* ACTIVITY bit is read-only */ - opp->src[n_IRQ].ipvp = - (opp->src[n_IRQ].ipvp & 0x40000000) | - (val & 0x800F00FF); - openpic_update_irq(opp, n_IRQ); - DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", - n_IRQ, val, opp->src[n_IRQ].ipvp); - break; - case IRQ_IDE: - tmp = val & 0xC0000000; - tmp |= val & ((1ULL << MAX_CPU) - 1); - opp->src[n_IRQ].ide = tmp; - DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); - break; - } + tmp = val & 0xC0000000; + tmp |= val & ((1ULL << MAX_CPU) - 1); + opp->src[n_IRQ].ide = tmp; + DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); +} + +static inline void write_IRQreg_ipvp(openpic_t *opp, int n_IRQ, uint32_t val) +{ + /* NOTE: not fully accurate for special IRQs, but simple and sufficient */ + /* ACTIVITY bit is read-only */ + opp->src[n_IRQ].ipvp = (opp->src[n_IRQ].ipvp & 0x40000000) + | (val & 0x800F00FF); + openpic_update_irq(opp, n_IRQ); + DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", n_IRQ, val, + opp->src[n_IRQ].ipvp); } #if 0 // Code provision for Intel model @@ -535,10 +530,10 @@ static void write_doorbell_register (penpic_t *opp, int n_dbl, { switch (offset) { case DBL_IVPR_OFFSET: - write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value); + write_IRQreg_ipvp(opp, IRQ_DBL0 + n_dbl, value); break; case DBL_IDE_OFFSET: - write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value); + write_IRQreg_ide(opp, IRQ_DBL0 + n_dbl, value); break; case DBL_DMR_OFFSET: opp->doorbells[n_dbl].dmr = value; @@ -576,10 +571,10 @@ static void write_mailbox_register (openpic_t *opp, int n_mbx, opp->mailboxes[n_mbx].mbr = value; break; case MBX_IVPR_OFFSET: - write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value); + write_IRQreg_ipvp(opp, IRQ_MBX0 + n_mbx, value); break; case MBX_DMR_OFFSET: - write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value); + write_IRQreg_ide(opp, IRQ_MBX0 + n_mbx, value); break; } } @@ -636,7 +631,7 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v { int idx; idx = (addr - 0x10A0) >> 4; - write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP, val); + write_IRQreg_ipvp(opp, opp->irq_ipi0 + idx, val); } break; case 0x10E0: /* SPVE */ @@ -729,10 +724,10 @@ static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val) opp->timers[idx].tibc = val; break; case 0x20: /* TIVP */ - write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP, val); + write_IRQreg_ipvp(opp, opp->irq_tim0 + idx, val); break; case 0x30: /* TIDE */ - write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE, val); + write_IRQreg_ide(opp, opp->irq_tim0 + idx, val); break; } } @@ -782,10 +777,10 @@ static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val) idx = addr >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(opp, idx, IRQ_IDE, val); + write_IRQreg_ide(opp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(opp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(opp, idx, val); } } @@ -835,8 +830,8 @@ static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr, case 0x70: idx = (addr - 0x40) >> 4; /* we use IDE as mask which CPUs to deliver the IPI to still. */ - write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE, - opp->src[opp->irq_ipi0 + idx].ide | val); + write_IRQreg_ide(opp, opp->irq_ipi0 + idx, + opp->src[opp->irq_ipi0 + idx].ide | val); openpic_set_irq(opp, opp->irq_ipi0 + idx, 1); openpic_set_irq(opp, opp->irq_ipi0 + idx, 0); break; @@ -1330,13 +1325,13 @@ static void mpic_timer_write (void *opaque, target_phys_addr_t addr, uint32_t va mpp->timers[idx].tibc = val; break; case 0x20: /* GTIVPR */ - write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, MPIC_TMR_IRQ + idx, val); break; case 0x30: /* GTIDR & TFRR */ if ((addr & 0xF0) == 0xF0) mpp->dst[cpu].tfrr = val; else - write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, MPIC_TMR_IRQ + idx, val); break; } } @@ -1391,10 +1386,10 @@ static void mpic_src_ext_write (void *opaque, target_phys_addr_t addr, idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(mpp, idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(mpp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, idx, val); } } } @@ -1441,10 +1436,10 @@ static void mpic_src_int_write (void *opaque, target_phys_addr_t addr, idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(mpp, idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(mpp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, idx, val); } } } @@ -1491,10 +1486,10 @@ static void mpic_src_msg_write (void *opaque, target_phys_addr_t addr, idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(mpp, idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(mpp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, idx, val); } } } @@ -1541,10 +1536,10 @@ static void mpic_src_msi_write (void *opaque, target_phys_addr_t addr, idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(mpp, idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(mpp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, idx, val); } } } From 7a880d9352575c70bae6fb246436c513ff5fe418 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Sun, 4 Sep 2011 08:41:15 +0000 Subject: [PATCH 67/87] ppc: move ADB stuff from ppc_mac.h to adb.h Allow to use ADB in non-ppc macintosh Signed-off-by: Laurent Vivier Signed-off-by: Alexander Graf --- hw/adb.c | 2 +- hw/adb.h | 67 +++++++++++++++++++++++++++++++++++++++++++++++ hw/cuda.c | 1 + hw/ppc_mac.h | 42 ----------------------------- hw/ppc_newworld.c | 1 + hw/ppc_oldworld.c | 1 + 6 files changed, 71 insertions(+), 43 deletions(-) create mode 100644 hw/adb.h diff --git a/hw/adb.c b/hw/adb.c index 8dedbf819f..aa15f55dc9 100644 --- a/hw/adb.c +++ b/hw/adb.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ #include "hw.h" -#include "ppc_mac.h" +#include "adb.h" #include "console.h" /* debug ADB */ diff --git a/hw/adb.h b/hw/adb.h new file mode 100644 index 0000000000..b2a591c546 --- /dev/null +++ b/hw/adb.h @@ -0,0 +1,67 @@ +/* + * QEMU ADB emulation shared definitions and prototypes + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if !defined(__ADB_H__) +#define __ADB_H__ + +#define MAX_ADB_DEVICES 16 + +#define ADB_MAX_OUT_LEN 16 + +typedef struct ADBDevice ADBDevice; + +/* buf = NULL means polling */ +typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, + const uint8_t *buf, int len); +typedef int ADBDeviceReset(ADBDevice *d); + +struct ADBDevice { + struct ADBBusState *bus; + int devaddr; + int handler; + ADBDeviceRequest *devreq; + ADBDeviceReset *devreset; + void *opaque; +}; + +typedef struct ADBBusState { + ADBDevice devices[MAX_ADB_DEVICES]; + int nb_devices; + int poll_index; +} ADBBusState; + +int adb_request(ADBBusState *s, uint8_t *buf_out, + const uint8_t *buf, int len); +int adb_poll(ADBBusState *s, uint8_t *buf_out); + +ADBDevice *adb_register_device(ADBBusState *s, int devaddr, + ADBDeviceRequest *devreq, + ADBDeviceReset *devreset, + void *opaque); +void adb_kbd_init(ADBBusState *bus); +void adb_mouse_init(ADBBusState *bus); + +extern ADBBusState adb_bus; +#endif /* !defined(__ADB_H__) */ diff --git a/hw/cuda.c b/hw/cuda.c index 5c92d81b4b..6f059751d8 100644 --- a/hw/cuda.c +++ b/hw/cuda.c @@ -24,6 +24,7 @@ */ #include "hw.h" #include "ppc_mac.h" +#include "adb.h" #include "qemu-timer.h" #include "sysemu.h" diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h index 7351bb6d37..af75e45cc2 100644 --- a/hw/ppc_mac.h +++ b/hw/ppc_mac.h @@ -77,46 +77,4 @@ void macio_nvram_setup_bar(MacIONVRAMState *s, MemoryRegion *bar, void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); uint32_t macio_nvram_read (void *opaque, uint32_t addr); void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val); - -/* adb.c */ - -#define MAX_ADB_DEVICES 16 - -#define ADB_MAX_OUT_LEN 16 - -typedef struct ADBDevice ADBDevice; - -/* buf = NULL means polling */ -typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, - const uint8_t *buf, int len); -typedef int ADBDeviceReset(ADBDevice *d); - -struct ADBDevice { - struct ADBBusState *bus; - int devaddr; - int handler; - ADBDeviceRequest *devreq; - ADBDeviceReset *devreset; - void *opaque; -}; - -typedef struct ADBBusState { - ADBDevice devices[MAX_ADB_DEVICES]; - int nb_devices; - int poll_index; -} ADBBusState; - -int adb_request(ADBBusState *s, uint8_t *buf_out, - const uint8_t *buf, int len); -int adb_poll(ADBBusState *s, uint8_t *buf_out); - -ADBDevice *adb_register_device(ADBBusState *s, int devaddr, - ADBDeviceRequest *devreq, - ADBDeviceReset *devreset, - void *opaque); -void adb_kbd_init(ADBBusState *bus); -void adb_mouse_init(ADBBusState *bus); - -extern ADBBusState adb_bus; - #endif /* !defined(__PPC_MAC_H__) */ diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index b1cc3d70a7..b9a50db3fa 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -49,6 +49,7 @@ #include "hw.h" #include "ppc.h" #include "ppc_mac.h" +#include "adb.h" #include "mac_dbdma.h" #include "nvram.h" #include "pc.h" diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index fa2c1e7768..ebcaafa641 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -26,6 +26,7 @@ #include "hw.h" #include "ppc.h" #include "ppc_mac.h" +#include "adb.h" #include "mac_dbdma.h" #include "nvram.h" #include "pc.h" From ea0a7eb4603d5929d25ac32e27d2e318b5cea910 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 8 Sep 2011 18:51:17 +0200 Subject: [PATCH 68/87] PPC: Fix via-cuda memory registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 23c5e4ca (convert to memory API) broke the VIA Cuda emulation layer by not registering the IO structs. This patch registers them properly and thus makes -M g3beige and -M mac99 work again. Tested-by: Andreas Färber Signed-off-by: Alexander Graf --- hw/cuda.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/hw/cuda.c b/hw/cuda.c index 6f059751d8..40774360df 100644 --- a/hw/cuda.c +++ b/hw/cuda.c @@ -634,16 +634,20 @@ static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr) return 0; } -static CPUWriteMemoryFunc * const cuda_write[] = { - &cuda_writeb, - &cuda_writew, - &cuda_writel, -}; - -static CPUReadMemoryFunc * const cuda_read[] = { - &cuda_readb, - &cuda_readw, - &cuda_readl, +static MemoryRegionOps cuda_ops = { + .old_mmio = { + .write = { + cuda_writeb, + cuda_writew, + cuda_writel, + }, + .read = { + cuda_readb, + cuda_readw, + cuda_readl, + }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, }; static bool cuda_timer_exist(void *opaque, int version_id) @@ -740,8 +744,8 @@ void cuda_init (MemoryRegion **cuda_mem, qemu_irq irq) s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, s); - cpu_register_io_memory(cuda_read, cuda_write, s, - DEVICE_NATIVE_ENDIAN); + memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000); + *cuda_mem = &s->mem; vmstate_register(NULL, -1, &vmstate_cuda, s); qemu_register_reset(cuda_reset, s); From 0157644c7b0bce8f3169bb17ca588ad1e8039fe2 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 13 Sep 2011 10:41:23 +0200 Subject: [PATCH 69/87] PPC: Fix heathrow PIC to use little endian MMIO During the memory API conversion, the indication on little endianness of MMIO for the heathrow PIC got dropped. This patch adds it back again. Signed-off-by: Alexander Graf --- hw/heathrow_pic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/heathrow_pic.c b/hw/heathrow_pic.c index 51996ab793..16f48d12e1 100644 --- a/hw/heathrow_pic.c +++ b/hw/heathrow_pic.c @@ -126,7 +126,7 @@ static uint64_t pic_read(void *opaque, target_phys_addr_t addr, static const MemoryRegionOps heathrow_pic_ops = { .read = pic_read, .write = pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void heathrow_pic_set_irq(void *opaque, int num, int level) From d38f674c446ca55cc8a62a993ba00c99be8742e7 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Sep 2011 10:26:26 +0200 Subject: [PATCH 70/87] KVM: Update kernel headers Removes ABI-breaking HIOR parts - KVM patch to follow. Signed-off-by: Alexander Graf --- linux-headers/asm-powerpc/kvm.h | 8 -------- linux-headers/linux/kvm.h | 1 - 2 files changed, 9 deletions(-) diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 28eecf04a3..25964ee107 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -148,12 +148,6 @@ struct kvm_regs { #define KVM_SREGS_E_UPDATE_DEC (1 << 2) #define KVM_SREGS_E_UPDATE_DBSR (1 << 3) -/* - * Book3S special bits to indicate contents in the struct by maintaining - * backwards compatibility with older structs. If adding a new field, - * please make sure to add a flag for that new field */ -#define KVM_SREGS_S_HIOR (1 << 0) - /* * In KVM_SET_SREGS, reserved/pad fields must be left untouched from a * previous KVM_GET_REGS. @@ -179,8 +173,6 @@ struct kvm_sregs { __u64 ibat[8]; __u64 dbat[8]; } ppc32; - __u64 flags; /* KVM_SREGS_S_ */ - __u64 hior; } s; struct { union { diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 8bb6cde856..6f5095c8a4 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -554,7 +554,6 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_PPC_SMT 64 #define KVM_CAP_PPC_RMA 65 #define KVM_CAP_MAX_VCPUS 66 /* returns max vcpus per vm */ -#define KVM_CAP_PPC_HIOR 67 #define KVM_CAP_PPC_PAPR 68 #define KVM_CAP_SW_TLB 69 From 9d4e4f8cbc29a4d684268f16542c8e5431530113 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Sep 2011 10:51:29 +0200 Subject: [PATCH 71/87] KVM: Update kernel headers Update HIOR and generic register get/set. Signed-off-by: Alexander Graf --- linux-headers/asm-powerpc/kvm.h | 2 ++ linux-headers/linux/kvm.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 25964ee107..fb3fddcd87 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -327,4 +327,6 @@ struct kvm_book3e_206_tlb_params { __u32 reserved[8]; }; +#define KVM_ONE_REG_PPC_HIOR KVM_ONE_REG_PPC | 0x100 + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 6f5095c8a4..a8761d3be6 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -554,8 +554,10 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_PPC_SMT 64 #define KVM_CAP_PPC_RMA 65 #define KVM_CAP_MAX_VCPUS 66 /* returns max vcpus per vm */ +#define KVM_CAP_PPC_HIOR 67 #define KVM_CAP_PPC_PAPR 68 #define KVM_CAP_SW_TLB 69 +#define KVM_CAP_ONE_REG 70 #ifdef KVM_CAP_IRQ_ROUTING @@ -650,6 +652,34 @@ struct kvm_dirty_tlb { __u32 num_dirty; }; +/* Available with KVM_CAP_ONE_REG */ + +#define KVM_ONE_REG_GENERIC 0x0000000000000000ULL + +/* + * Architecture specific registers are to be defined in arch headers and + * ORed with the arch identifier. + */ +#define KVM_ONE_REG_PPC 0x1000000000000000ULL +#define KVM_ONE_REG_X86 0x2000000000000000ULL +#define KVM_ONE_REG_IA64 0x3000000000000000ULL +#define KVM_ONE_REG_ARM 0x4000000000000000ULL +#define KVM_ONE_REG_S390 0x5000000000000000ULL + +struct kvm_one_reg { + __u64 id; + union { + __u8 reg8; + __u16 reg16; + __u32 reg32; + __u64 reg64; + __u8 reg128[16]; + __u8 reg256[32]; + __u8 reg512[64]; + __u8 reg1024[128]; + } u; +}; + /* * ioctls for VM fds */ @@ -778,6 +808,9 @@ struct kvm_dirty_tlb { #define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma) /* Available with KVM_CAP_SW_TLB */ #define KVM_DIRTY_TLB _IOW(KVMIO, 0xaa, struct kvm_dirty_tlb) +/* Available with KVM_CAP_ONE_REG */ +#define KVM_GET_ONE_REG _IOWR(KVMIO, 0xab, struct kvm_one_reg) +#define KVM_SET_ONE_REG _IOW(KVMIO, 0xac, struct kvm_one_reg) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) From 94135e813c14bac3f967e6b5aa35b9d617737e68 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Sep 2011 21:38:45 +0200 Subject: [PATCH 72/87] KVM: PPC: Use HIOR setting for -M pseries with PR KVM When running with PR KVM, we need to set HIOR directly. Thankfully there is now a new interface to set registers individually so we can just use that and poke HIOR into the guest vcpu's HIOR register. While at it, this also sets SDR1 because -M pseries requires it to run. With this patch, -M pseries works properly with PR KVM. Signed-off-by: Alexander Graf --- target-ppc/kvm.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 35a6f1086e..75832d83b8 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -705,10 +705,11 @@ int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len) void kvmppc_set_papr(CPUState *env) { - struct kvm_enable_cap cap; + struct kvm_enable_cap cap = {}; + struct kvm_one_reg reg = {}; + struct kvm_sregs sregs = {}; int ret; - memset(&cap, 0, sizeof(cap)); cap.cap = KVM_CAP_PPC_PAPR; ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &cap); @@ -723,7 +724,25 @@ void kvmppc_set_papr(CPUState *env) * Once we have qdev CPUs, move HIOR to a qdev property and * remove this chunk. */ - /* XXX Set HIOR using new ioctl */ + reg.id = KVM_ONE_REG_PPC_HIOR; + reg.u.reg64 = env->spr[SPR_HIOR]; + ret = kvm_vcpu_ioctl(env, KVM_SET_ONE_REG, ®); + if (ret) { + goto fail; + } + + /* Set SDR1 so kernel space finds the HTAB */ + ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + if (ret) { + goto fail; + } + + sregs.u.s.sdr1 = env->spr[SPR_SDR1]; + + ret = kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs); + if (ret) { + goto fail; + } return; From ddd1055b07fdfe488a22c2275adaca75f4206d30 Mon Sep 17 00:00:00 2001 From: Fabien Chouteau Date: Tue, 13 Sep 2011 04:00:32 +0000 Subject: [PATCH 73/87] PPC: booke timers While working on the emulation of the freescale p2010 (e500v2) I realized that there's no implementation of booke's timers features. Currently mpc8544 uses ppc_emb (ppc_emb_timers_init) which is close but not exactly like booke (for example booke uses different SPR). Signed-off-by: Fabien Chouteau Signed-off-by: Alexander Graf --- Makefile.target | 2 +- hw/ppc.c | 138 ++++++++----------- hw/ppc.h | 37 ++++- hw/ppc4xx_devs.c | 2 +- hw/ppc_booke.c | 266 ++++++++++++++++++++++++++++++++++++ hw/ppce500_mpc8544ds.c | 6 +- hw/virtex_ml507.c | 11 +- target-ppc/cpu.h | 27 ++++ target-ppc/translate_init.c | 39 ++++++ 9 files changed, 427 insertions(+), 101 deletions(-) create mode 100644 hw/ppc_booke.c diff --git a/Makefile.target b/Makefile.target index fc3dfaabe5..1e9815cb2d 100644 --- a/Makefile.target +++ b/Makefile.target @@ -229,7 +229,7 @@ obj-i386-$(CONFIG_KVM) += kvmclock.o obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o # shared objects -obj-ppc-y = ppc.o +obj-ppc-y = ppc.o ppc_booke.o obj-ppc-y += vga.o # PREP target obj-ppc-y += i8259.o mc146818rtc.o diff --git a/hw/ppc.c b/hw/ppc.c index 887074822b..25b59dddaa 100644 --- a/hw/ppc.c +++ b/hw/ppc.c @@ -50,7 +50,7 @@ static void cpu_ppc_tb_stop (CPUState *env); static void cpu_ppc_tb_start (CPUState *env); -static void ppc_set_irq (CPUState *env, int n_IRQ, int level) +void ppc_set_irq(CPUState *env, int n_IRQ, int level) { unsigned int old_pending = env->pending_interrupts; @@ -423,25 +423,8 @@ void ppce500_irq_init (CPUState *env) } /*****************************************************************************/ /* PowerPC time base and decrementer emulation */ -struct ppc_tb_t { - /* Time base management */ - int64_t tb_offset; /* Compensation */ - int64_t atb_offset; /* Compensation */ - uint32_t tb_freq; /* TB frequency */ - /* Decrementer management */ - uint64_t decr_next; /* Tick for next decr interrupt */ - uint32_t decr_freq; /* decrementer frequency */ - struct QEMUTimer *decr_timer; - /* Hypervisor decrementer management */ - uint64_t hdecr_next; /* Tick for next hdecr interrupt */ - struct QEMUTimer *hdecr_timer; - uint64_t purr_load; - uint64_t purr_start; - void *opaque; -}; -static inline uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, - int64_t tb_offset) +uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) { /* TB time in tb periods */ return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset; @@ -611,10 +594,13 @@ static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next) int64_t diff; diff = next - qemu_get_clock_ns(vm_clock); - if (diff >= 0) + if (diff >= 0) { decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec()); - else + } else if (tb_env->flags & PPC_TIMER_BOOKE) { + decr = 0; + } else { decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec()); + } LOG_TB("%s: %08" PRIx32 "\n", __func__, decr); return decr; @@ -678,18 +664,24 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp, decr, value); now = qemu_get_clock_ns(vm_clock); next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); - if (is_excp) + if (is_excp) { next += *nextp - now; - if (next == now) + } + if (next == now) { next++; + } *nextp = next; /* Adjust timer */ qemu_mod_timer(timer, next); - /* If we set a negative value and the decrementer was positive, - * raise an exception. + + /* If we set a negative value and the decrementer was positive, raise an + * exception. */ - if ((value & 0x80000000) && !(decr & 0x80000000)) + if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) + && (value & 0x80000000) + && !(decr & 0x80000000)) { (*raise_excp)(env); + } } static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr, @@ -763,6 +755,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq) tb_env = g_malloc0(sizeof(ppc_tb_t)); env->tb_env = tb_env; + tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; /* Create new timer */ tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, env); if (0) { @@ -806,11 +799,11 @@ uint32_t cpu_ppc601_load_rtcl (CPUState *env) } /*****************************************************************************/ -/* Embedded PowerPC timers */ +/* PowerPC 40x timers */ /* PIT, FIT & WDT */ -typedef struct ppcemb_timer_t ppcemb_timer_t; -struct ppcemb_timer_t { +typedef struct ppc40x_timer_t ppc40x_timer_t; +struct ppc40x_timer_t { uint64_t pit_reload; /* PIT auto-reload value */ uint64_t fit_next; /* Tick for next FIT interrupt */ struct QEMUTimer *fit_timer; @@ -826,12 +819,12 @@ static void cpu_4xx_fit_cb (void *opaque) { CPUState *env; ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; uint64_t now, next; env = opaque; tb_env = env->tb_env; - ppcemb_timer = tb_env->opaque; + ppc40x_timer = tb_env->opaque; now = qemu_get_clock_ns(vm_clock); switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { case 0: @@ -853,7 +846,7 @@ static void cpu_4xx_fit_cb (void *opaque) next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq); if (next == now) next++; - qemu_mod_timer(ppcemb_timer->fit_timer, next); + qemu_mod_timer(ppc40x_timer->fit_timer, next); env->spr[SPR_40x_TSR] |= 1 << 26; if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) ppc_set_irq(env, PPC_INTERRUPT_FIT, 1); @@ -865,11 +858,11 @@ static void cpu_4xx_fit_cb (void *opaque) /* Programmable interval timer */ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) { - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; uint64_t now, next; - ppcemb_timer = tb_env->opaque; - if (ppcemb_timer->pit_reload <= 1 || + ppc40x_timer = tb_env->opaque; + if (ppc40x_timer->pit_reload <= 1 || !((env->spr[SPR_40x_TCR] >> 26) & 0x1) || (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { /* Stop PIT */ @@ -877,9 +870,9 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) qemu_del_timer(tb_env->decr_timer); } else { LOG_TB("%s: start PIT %016" PRIx64 "\n", - __func__, ppcemb_timer->pit_reload); + __func__, ppc40x_timer->pit_reload); now = qemu_get_clock_ns(vm_clock); - next = now + muldiv64(ppcemb_timer->pit_reload, + next = now + muldiv64(ppc40x_timer->pit_reload, get_ticks_per_sec(), tb_env->decr_freq); if (is_excp) next += tb_env->decr_next - now; @@ -894,21 +887,21 @@ static void cpu_4xx_pit_cb (void *opaque) { CPUState *env; ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; env = opaque; tb_env = env->tb_env; - ppcemb_timer = tb_env->opaque; + ppc40x_timer = tb_env->opaque; env->spr[SPR_40x_TSR] |= 1 << 27; if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) - ppc_set_irq(env, ppcemb_timer->decr_excp, 1); + ppc_set_irq(env, ppc40x_timer->decr_excp, 1); start_stop_pit(env, tb_env, 1); LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " "%016" PRIx64 "\n", __func__, (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1), env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], - ppcemb_timer->pit_reload); + ppc40x_timer->pit_reload); } /* Watchdog timer */ @@ -916,12 +909,12 @@ static void cpu_4xx_wdt_cb (void *opaque) { CPUState *env; ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; uint64_t now, next; env = opaque; tb_env = env->tb_env; - ppcemb_timer = tb_env->opaque; + ppc40x_timer = tb_env->opaque; now = qemu_get_clock_ns(vm_clock); switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { case 0: @@ -948,13 +941,13 @@ static void cpu_4xx_wdt_cb (void *opaque) switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { case 0x0: case 0x1: - qemu_mod_timer(ppcemb_timer->wdt_timer, next); - ppcemb_timer->wdt_next = next; + qemu_mod_timer(ppc40x_timer->wdt_timer, next); + ppc40x_timer->wdt_next = next; env->spr[SPR_40x_TSR] |= 1 << 31; break; case 0x2: - qemu_mod_timer(ppcemb_timer->wdt_timer, next); - ppcemb_timer->wdt_next = next; + qemu_mod_timer(ppc40x_timer->wdt_timer, next); + ppc40x_timer->wdt_next = next; env->spr[SPR_40x_TSR] |= 1 << 30; if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) ppc_set_irq(env, PPC_INTERRUPT_WDT, 1); @@ -982,12 +975,12 @@ static void cpu_4xx_wdt_cb (void *opaque) void store_40x_pit (CPUState *env, target_ulong val) { ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; tb_env = env->tb_env; - ppcemb_timer = tb_env->opaque; + ppc40x_timer = tb_env->opaque; LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val); - ppcemb_timer->pit_reload = val; + ppc40x_timer->pit_reload = val; start_stop_pit(env, tb_env, 0); } @@ -996,31 +989,7 @@ target_ulong load_40x_pit (CPUState *env) return cpu_ppc_load_decr(env); } -void store_booke_tsr (CPUState *env, target_ulong val) -{ - ppc_tb_t *tb_env = env->tb_env; - ppcemb_timer_t *ppcemb_timer; - - ppcemb_timer = tb_env->opaque; - - LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val); - env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000); - if (val & 0x80000000) - ppc_set_irq(env, ppcemb_timer->decr_excp, 0); -} - -void store_booke_tcr (CPUState *env, target_ulong val) -{ - ppc_tb_t *tb_env; - - tb_env = env->tb_env; - LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val); - env->spr[SPR_40x_TCR] = val & 0xFFC00000; - start_stop_pit(env, tb_env, 1); - cpu_4xx_wdt_cb(env); -} - -static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq) +static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) { CPUState *env = opaque; ppc_tb_t *tb_env = env->tb_env; @@ -1032,30 +1001,31 @@ static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq) /* XXX: we should also update all timers */ } -clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq, +clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq, unsigned int decr_excp) { ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; tb_env = g_malloc0(sizeof(ppc_tb_t)); env->tb_env = tb_env; - ppcemb_timer = g_malloc0(sizeof(ppcemb_timer_t)); + tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; + ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t)); tb_env->tb_freq = freq; tb_env->decr_freq = freq; - tb_env->opaque = ppcemb_timer; + tb_env->opaque = ppc40x_timer; LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); - if (ppcemb_timer != NULL) { + if (ppc40x_timer != NULL) { /* We use decr timer for PIT */ tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env); - ppcemb_timer->fit_timer = + ppc40x_timer->fit_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env); - ppcemb_timer->wdt_timer = + ppc40x_timer->wdt_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env); - ppcemb_timer->decr_excp = decr_excp; + ppc40x_timer->decr_excp = decr_excp; } - return &ppc_emb_set_tb_clk; + return &ppc_40x_set_tb_clk; } /*****************************************************************************/ diff --git a/hw/ppc.h b/hw/ppc.h index 3ccf13479b..9f911704af 100644 --- a/hw/ppc.h +++ b/hw/ppc.h @@ -1,3 +1,5 @@ +void ppc_set_irq (CPUState *env, int n_IRQ, int level); + /* PowerPC hardware exceptions management helpers */ typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); typedef struct clk_setup_t clk_setup_t; @@ -11,6 +13,36 @@ static inline void clk_setup (clk_setup_t *clk, uint32_t freq) (*clk->cb)(clk->opaque, freq); } +struct ppc_tb_t { + /* Time base management */ + int64_t tb_offset; /* Compensation */ + int64_t atb_offset; /* Compensation */ + uint32_t tb_freq; /* TB frequency */ + /* Decrementer management */ + uint64_t decr_next; /* Tick for next decr interrupt */ + uint32_t decr_freq; /* decrementer frequency */ + struct QEMUTimer *decr_timer; + /* Hypervisor decrementer management */ + uint64_t hdecr_next; /* Tick for next hdecr interrupt */ + struct QEMUTimer *hdecr_timer; + uint64_t purr_load; + uint64_t purr_start; + void *opaque; + uint32_t flags; +}; + +/* PPC Timers flags */ +#define PPC_TIMER_BOOKE (1 << 0) /* Enable Booke support */ +#define PPC_TIMER_E500 (1 << 1) /* Enable e500 support */ +#define PPC_DECR_UNDERFLOW_TRIGGERED (1 << 2) /* Decr interrupt triggered when + * the most significant bit + * changes from 0 to 1. + */ +#define PPC_DECR_ZERO_TRIGGERED (1 << 3) /* Decr interrupt triggered when + * the decrementer reaches zero. + */ + +uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset); clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq); /* Embedded PowerPC DCR management */ typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn); @@ -19,7 +51,7 @@ int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn), int (*dcr_write_error)(int dcrn)); int ppc_dcr_register (CPUState *env, int dcrn, void *opaque, dcr_read_cb drc_read, dcr_write_cb dcr_write); -clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq, +clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq, unsigned int decr_excp); /* Embedded PowerPC reset */ @@ -55,3 +87,6 @@ enum { #define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) #define PPC_SERIAL_MM_BAUDBASE 399193 + +/* ppc_booke.c */ +void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags); diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c index 349f046b2f..d18caa4192 100644 --- a/hw/ppc4xx_devs.c +++ b/hw/ppc4xx_devs.c @@ -56,7 +56,7 @@ CPUState *ppc4xx_init (const char *cpu_model, cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ cpu_clk->opaque = env; /* Set time-base frequency to sysclk */ - tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT); + tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT); tb_clk->opaque = env; ppc_dcr_init(env, NULL, NULL); /* Register qemu callbacks */ diff --git a/hw/ppc_booke.c b/hw/ppc_booke.c new file mode 100644 index 0000000000..3e2fff588a --- /dev/null +++ b/hw/ppc_booke.c @@ -0,0 +1,266 @@ +/* + * QEMU PowerPC Booke hardware System Emulator + * + * Copyright (c) 2011 AdaCore + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "ppc.h" +#include "qemu-timer.h" +#include "sysemu.h" +#include "nvram.h" +#include "qemu-log.h" +#include "loader.h" + + +/* Timer Control Register */ + +#define TCR_WP_SHIFT 30 /* Watchdog Timer Period */ +#define TCR_WP_MASK (0x3 << TCR_WP_SHIFT) +#define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */ +#define TCR_WRC_MASK (0x3 << TCR_WRC_SHIFT) +#define TCR_WIE (1 << 27) /* Watchdog Timer Interrupt Enable */ +#define TCR_DIE (1 << 26) /* Decrementer Interrupt Enable */ +#define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */ +#define TCR_FP_MASK (0x3 << TCR_FP_SHIFT) +#define TCR_FIE (1 << 23) /* Fixed-Interval Timer Interrupt Enable */ +#define TCR_ARE (1 << 22) /* Auto-Reload Enable */ + +/* Timer Control Register (e500 specific fields) */ + +#define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */ +#define TCR_E500_FPEXT_MASK (0xf << TCR_E500_FPEXT_SHIFT) +#define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */ +#define TCR_E500_WPEXT_MASK (0xf << TCR_E500_WPEXT_SHIFT) + +/* Timer Status Register */ + +#define TSR_FIS (1 << 26) /* Fixed-Interval Timer Interrupt Status */ +#define TSR_DIS (1 << 27) /* Decrementer Interrupt Status */ +#define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */ +#define TSR_WRS_MASK (0x3 << TSR_WRS_SHIFT) +#define TSR_WIS (1 << 30) /* Watchdog Timer Interrupt Status */ +#define TSR_ENW (1 << 31) /* Enable Next Watchdog Timer */ + +typedef struct booke_timer_t booke_timer_t; +struct booke_timer_t { + + uint64_t fit_next; + struct QEMUTimer *fit_timer; + + uint64_t wdt_next; + struct QEMUTimer *wdt_timer; + + uint32_t flags; +}; + +static void booke_update_irq(CPUState *env) +{ + ppc_set_irq(env, PPC_INTERRUPT_DECR, + (env->spr[SPR_BOOKE_TSR] & TSR_DIS + && env->spr[SPR_BOOKE_TCR] & TCR_DIE)); + + ppc_set_irq(env, PPC_INTERRUPT_WDT, + (env->spr[SPR_BOOKE_TSR] & TSR_WIS + && env->spr[SPR_BOOKE_TCR] & TCR_WIE)); + + ppc_set_irq(env, PPC_INTERRUPT_FIT, + (env->spr[SPR_BOOKE_TSR] & TSR_FIS + && env->spr[SPR_BOOKE_TCR] & TCR_FIE)); +} + +/* Return the location of the bit of time base at which the FIT will raise an + interrupt */ +static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env) +{ + uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT; + + if (tb_env->flags & PPC_TIMER_E500) { + /* e500 Fixed-interval timer period extension */ + uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK) + >> TCR_E500_FPEXT_SHIFT; + fp = 63 - (fp | fpext << 2); + } else { + fp = env->fit_period[fp]; + } + + return fp; +} + +/* Return the location of the bit of time base at which the WDT will raise an + interrupt */ +static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env) +{ + uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT; + + if (tb_env->flags & PPC_TIMER_E500) { + /* e500 Watchdog timer period extension */ + uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK) + >> TCR_E500_WPEXT_SHIFT; + wp = 63 - (wp | wpext << 2); + } else { + wp = env->wdt_period[wp]; + } + + return wp; +} + +static void booke_update_fixed_timer(CPUState *env, + uint8_t target_bit, + uint64_t *next, + struct QEMUTimer *timer) +{ + ppc_tb_t *tb_env = env->tb_env; + uint64_t lapse; + uint64_t tb; + uint64_t period = 1 << (target_bit + 1); + uint64_t now; + + now = qemu_get_clock_ns(vm_clock); + tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset); + + lapse = period - ((tb - (1 << target_bit)) & (period - 1)); + + *next = now + muldiv64(lapse, get_ticks_per_sec(), tb_env->tb_freq); + + /* XXX: If expire time is now. We can't run the callback because we don't + * have access to it. So we just set the timer one nanosecond later. + */ + + if (*next == now) { + (*next)++; + } + + qemu_mod_timer(timer, *next); +} + +static void booke_decr_cb(void *opaque) +{ + CPUState *env; + ppc_tb_t *tb_env; + booke_timer_t *booke_timer; + + env = opaque; + tb_env = env->tb_env; + booke_timer = tb_env->opaque; + env->spr[SPR_BOOKE_TSR] |= TSR_DIS; + + booke_update_irq(env); + + if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) { + /* Auto Reload */ + cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]); + } +} + +static void booke_fit_cb(void *opaque) +{ + CPUState *env; + ppc_tb_t *tb_env; + booke_timer_t *booke_timer; + + env = opaque; + tb_env = env->tb_env; + booke_timer = tb_env->opaque; + env->spr[SPR_BOOKE_TSR] |= TSR_FIS; + + booke_update_irq(env); + + booke_update_fixed_timer(env, + booke_get_fit_target(env, tb_env), + &booke_timer->fit_next, + booke_timer->fit_timer); +} + +static void booke_wdt_cb(void *opaque) +{ + CPUState *env; + ppc_tb_t *tb_env; + booke_timer_t *booke_timer; + + env = opaque; + tb_env = env->tb_env; + booke_timer = tb_env->opaque; + + /* TODO: There's lots of complicated stuff to do here */ + + booke_update_irq(env); + + booke_update_fixed_timer(env, + booke_get_wdt_target(env, tb_env), + &booke_timer->wdt_next, + booke_timer->wdt_timer); +} + +void store_booke_tsr(CPUState *env, target_ulong val) +{ + ppc_tb_t *tb_env = env->tb_env; + booke_timer_t *booke_timer; + + booke_timer = tb_env->opaque; + + env->spr[SPR_BOOKE_TSR] &= ~val; + + booke_update_irq(env); +} + +void store_booke_tcr(CPUState *env, target_ulong val) +{ + ppc_tb_t *tb_env = env->tb_env; + booke_timer_t *booke_timer = tb_env->opaque; + + tb_env = env->tb_env; + env->spr[SPR_BOOKE_TCR] = val; + + booke_update_irq(env); + + booke_update_fixed_timer(env, + booke_get_fit_target(env, tb_env), + &booke_timer->fit_next, + booke_timer->fit_timer); + + booke_update_fixed_timer(env, + booke_get_wdt_target(env, tb_env), + &booke_timer->wdt_next, + booke_timer->wdt_timer); + +} + +void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags) +{ + ppc_tb_t *tb_env; + booke_timer_t *booke_timer; + + tb_env = g_malloc0(sizeof(ppc_tb_t)); + booke_timer = g_malloc0(sizeof(booke_timer_t)); + + env->tb_env = tb_env; + tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; + + tb_env->tb_freq = freq; + tb_env->decr_freq = freq; + tb_env->opaque = booke_timer; + tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, env); + + booke_timer->fit_timer = + qemu_new_timer_ns(vm_clock, &booke_fit_cb, env); + booke_timer->wdt_timer = + qemu_new_timer_ns(vm_clock, &booke_wdt_cb, env); +} diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 8095516f43..f00367ed7b 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -268,11 +268,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; - /* XXX register timer? */ - ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR); - ppc_dcr_init(env, NULL, NULL); - /* XXX Enable DEC interrupts - probably wrong in the backend */ - env->spr[SPR_40x_TCR] = 1 << 26; + ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); /* Register reset handler */ if (!i) { diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c index 7459b0bbe9..66df27a551 100644 --- a/hw/virtex_ml507.c +++ b/hw/virtex_ml507.c @@ -81,7 +81,6 @@ static void mmubooke_create_initial_mapping(CPUState *env, static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, int do_init, const char *cpu_model, - clk_setup_t *cpu_clk, clk_setup_t *tb_clk, uint32_t sysclk) { CPUState *env; @@ -93,11 +92,7 @@ static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, exit(1); } - cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ - cpu_clk->opaque = env; - /* Set time-base frequency to sysclk */ - tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR); - tb_clk->opaque = env; + ppc_booke_timers_init(env, sysclk, 0/* no flags */); ppc_dcr_init(env, NULL, NULL); @@ -197,7 +192,6 @@ static void virtex_init(ram_addr_t ram_size, DriveInfo *dinfo; ram_addr_t phys_ram; qemu_irq irq[32], *cpu_irq; - clk_setup_t clk_setup[7]; int kernel_size; int i; @@ -207,8 +201,7 @@ static void virtex_init(ram_addr_t ram_size, } memset(clk_setup, 0, sizeof(clk_setup)); - env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0], - &clk_setup[1], 400000000); + env = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000); qemu_register_reset(main_cpu_reset, env); phys_ram = qemu_ram_alloc(NULL, "ram", ram_size); diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 3f4af22397..3f77e308a6 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1018,8 +1018,35 @@ struct CPUPPCState { #if !defined(CONFIG_USER_ONLY) void *load_info; /* Holds boot loading state. */ #endif + + /* booke timers */ + + /* Specifies bit locations of the Time Base used to signal a fixed timer + * exception on a transition from 0 to 1. (watchdog or fixed-interval timer) + * + * 0 selects the least significant bit. + * 63 selects the most significant bit. + */ + uint8_t fit_period[4]; + uint8_t wdt_period[4]; }; +#define SET_FIT_PERIOD(a_, b_, c_, d_) \ +do { \ + env->fit_period[0] = (a_); \ + env->fit_period[1] = (b_); \ + env->fit_period[2] = (c_); \ + env->fit_period[3] = (d_); \ + } while (0) + +#define SET_WDT_PERIOD(a_, b_, c_, d_) \ +do { \ + env->wdt_period[0] = (a_); \ + env->wdt_period[1] = (b_); \ + env->wdt_period[2] = (c_); \ + env->wdt_period[3] = (d_); \ + } while (0) + #if !defined(CONFIG_USER_ONLY) /* Context used internally during MMU translations */ typedef struct mmu_ctx_t mmu_ctx_t; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index d09c7ca98b..ca0d8525c8 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -3266,6 +3266,9 @@ static void init_proc_401 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 401x2 */ @@ -3304,6 +3307,9 @@ static void init_proc_401x2 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 401x3 */ @@ -3337,6 +3343,9 @@ static void init_proc_401x3 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); } /* IOP480 */ @@ -3375,6 +3384,9 @@ static void init_proc_IOP480 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 403 */ @@ -3405,6 +3417,9 @@ static void init_proc_403 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 403 GCX */ @@ -3455,6 +3470,9 @@ static void init_proc_403GCX (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 405 */ @@ -3504,6 +3522,9 @@ static void init_proc_405 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 440 EP */ @@ -3586,6 +3607,9 @@ static void init_proc_440EP (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 440 GP */ @@ -3650,6 +3674,9 @@ static void init_proc_440GP (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 440x4 */ @@ -3714,6 +3741,9 @@ static void init_proc_440x4 (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 440x5 */ @@ -3795,6 +3825,9 @@ static void init_proc_440x5 (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; ppc40x_irq_init(env); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 460 (guessed) */ @@ -3883,6 +3916,9 @@ static void init_proc_460 (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 460F (guessed) */ @@ -3974,6 +4010,9 @@ static void init_proc_460F (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* Freescale 5xx cores (aka RCPU) */ From 44427c401f7c0053db31f40a58a427e1ae3cd8c3 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 19 Sep 2011 15:17:47 +0200 Subject: [PATCH 74/87] PPC: Clean up BookE timer code The BookE timer code had some written-but-not-read variables. Get rid of them. Signed-off-by: Alexander Graf --- hw/ppc_booke.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/hw/ppc_booke.c b/hw/ppc_booke.c index 3e2fff588a..88719458b0 100644 --- a/hw/ppc_booke.c +++ b/hw/ppc_booke.c @@ -153,15 +153,9 @@ static void booke_update_fixed_timer(CPUState *env, static void booke_decr_cb(void *opaque) { - CPUState *env; - ppc_tb_t *tb_env; - booke_timer_t *booke_timer; + CPUState *env = opaque; - env = opaque; - tb_env = env->tb_env; - booke_timer = tb_env->opaque; env->spr[SPR_BOOKE_TSR] |= TSR_DIS; - booke_update_irq(env); if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) { @@ -211,13 +205,7 @@ static void booke_wdt_cb(void *opaque) void store_booke_tsr(CPUState *env, target_ulong val) { - ppc_tb_t *tb_env = env->tb_env; - booke_timer_t *booke_timer; - - booke_timer = tb_env->opaque; - env->spr[SPR_BOOKE_TSR] &= ~val; - booke_update_irq(env); } From e6c866d417d1a09536f489e66c21cf49b7ec60b6 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 15 Sep 2011 20:49:49 +0000 Subject: [PATCH 75/87] pseries: Refactor spapr irq allocation Paulo Bonzini changed the original spapr code, which manually assigned irq numbers for each virtual device, to allocate them automatically from the device initialization. That allowed spapr virtual devices to be constructed with -device, which is a good start. However, the way that patch worked doesn't extend nicely for the future when we want to support devices other than sPAPR VIO devices (e.g. virtio and PCI). This patch rearranges the irq allocation to be global across the sPAPR environment, so it can be used by other bus types as well. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 25 +++++++++++++++++++++++++ hw/spapr.h | 7 ++----- hw/spapr_vio.c | 9 ++++----- hw/spapr_vio.h | 1 - 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/hw/spapr.c b/hw/spapr.c index deb4ae5417..b1189755d3 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -61,6 +61,30 @@ sPAPREnvironment *spapr; +qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num) +{ + uint32_t irq; + qemu_irq qirq; + + if (hint) { + irq = hint; + /* FIXME: we should probably check for collisions somehow */ + } else { + irq = spapr->next_irq++; + } + + qirq = xics_find_qirq(spapr->icp, irq); + if (!qirq) { + return NULL; + } + + if (irq_num) { + *irq_num = irq; + } + + return qirq; +} + static void *spapr_create_fdt_skel(const char *cpu_model, target_phys_addr_t initrd_base, target_phys_addr_t initrd_size, @@ -372,6 +396,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, /* Set up Interrupt Controller */ spapr->icp = xics_system_init(XICS_IRQS); + spapr->next_irq = 16; /* Set up VIO bus */ spapr->vio_bus = spapr_vio_bus_init(); diff --git a/hw/spapr.h b/hw/spapr.h index 3d21b7a1cc..ec910de5b0 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -17,6 +17,7 @@ typedef struct sPAPREnvironment { long rtas_size; void *fdt_skel; target_ulong entry_point; + int next_irq; } sPAPREnvironment; #define H_SUCCESS 0 @@ -281,11 +282,7 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, target_ulong *args); -static inline qemu_irq spapr_find_qirq(sPAPREnvironment *spapr, - int irq_num) -{ - return xics_find_qirq(spapr->icp, irq_num); -} +qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num); static inline uint32_t rtas_ld(target_ulong phys, int n) { diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index 0546ccb043..35818e18f1 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -600,7 +600,6 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo) { VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo; VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; - VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus); char *id; if (asprintf(&id, "%s@%x", info->dt_name, dev->reg) < 0) { @@ -608,10 +607,11 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo) } dev->qdev.id = id; - if (!dev->vio_irq_num) { - dev->vio_irq_num = bus->irq++; + + dev->qirq = spapr_allocate_irq(dev->vio_irq_num, &dev->vio_irq_num); + if (!dev->qirq) { + return -1; } - dev->qirq = spapr_find_qirq(spapr, dev->vio_irq_num); rtce_init(dev); @@ -666,7 +666,6 @@ VIOsPAPRBus *spapr_vio_bus_init(void) qbus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio"); bus = DO_UPCAST(VIOsPAPRBus, bus, qbus); - bus->irq = 16; /* hcall-vio */ spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index 7eb5367653..4fe5f742c2 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -67,7 +67,6 @@ typedef struct VIOsPAPRDevice { typedef struct VIOsPAPRBus { BusState bus; - int irq; } VIOsPAPRBus; typedef struct { From ac26f8c3891dde7a3fc7f24a3a87905df843d905 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 28 Sep 2011 16:53:16 +0000 Subject: [PATCH 76/87] pseries: Implement set-time-of-day RTAS function Currently there is no implementation for set-time-of-day rtas function, which causes the following warning "setting the clock failed (-1)" on the guest. This patch just creates this function, get the timedate diff and store in the papr environment, so that the correct value will be returned by get-time-of-day. In order to try it, just adjust the hardware time, run hwclock --systohc, so that, on when the system runs hwclock --hctosys, the value is correctly adjusted, i.e. the host time plus the timediff. Signed-off-by: Breno Leitao Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.h | 1 + hw/spapr_rtas.c | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/hw/spapr.h b/hw/spapr.h index ec910de5b0..6657c336f6 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -18,6 +18,7 @@ typedef struct sPAPREnvironment { void *fdt_skel; target_ulong entry_point; int next_irq; + int rtc_offset; } sPAPREnvironment; #define H_SUCCESS 0 diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c index 00c8ce5a15..d1ac74cfbd 100644 --- a/hw/spapr_rtas.c +++ b/hw/spapr_rtas.c @@ -67,7 +67,7 @@ static void rtas_get_time_of_day(sPAPREnvironment *spapr, return; } - qemu_get_timedate(&tm, 0); + qemu_get_timedate(&tm, spapr->rtc_offset); rtas_st(rets, 0, 0); /* Success */ rtas_st(rets, 1, tm.tm_year + 1900); @@ -79,6 +79,27 @@ static void rtas_get_time_of_day(sPAPREnvironment *spapr, rtas_st(rets, 7, 0); /* we don't do nanoseconds */ } +static void rtas_set_time_of_day(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct tm tm; + + tm.tm_year = rtas_ld(args, 0) - 1900; + tm.tm_mon = rtas_ld(args, 1) - 1; + tm.tm_mday = rtas_ld(args, 2); + tm.tm_hour = rtas_ld(args, 3); + tm.tm_min = rtas_ld(args, 4); + tm.tm_sec = rtas_ld(args, 5); + + /* Just generate a monitor event for the change */ + rtc_change_mon_event(&tm); + spapr->rtc_offset = qemu_timedate_diff(&tm); + + rtas_st(rets, 0, 0); /* Success */ +} + static void rtas_power_off(sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -271,6 +292,7 @@ static void register_core_rtas(void) { spapr_rtas_register("display-character", rtas_display_character); spapr_rtas_register("get-time-of-day", rtas_get_time_of_day); + spapr_rtas_register("set-time-of-day", rtas_set_time_of_day); spapr_rtas_register("power-off", rtas_power_off); spapr_rtas_register("query-cpu-stopped-state", rtas_query_cpu_stopped_state); From 7c0a3409627604c111d5c5e1ce4e0224c2b56315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Tue, 4 Oct 2011 05:14:52 +0000 Subject: [PATCH 77/87] ppc64: Fix linker script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 8733f609 (Fix linker scripts) linking on Linux/ppc64 fails: LINK ppc64-linux-user/qemu-ppc64 /usr/lib64/gcc/powerpc64-suse-linux/4.3/../../../../powerpc64-suse-linux/bin/ld:/home/afaerber/qemu/ppc64.ld:84: syntax error collect2: ld gab 1 als Ende-Status zurück make[1]: *** [qemu-ppc64] Fehler 1 make: *** [subdir-ppc64-linux-user] Fehler 2 Fix by removing a leftover line in the ppc64 linker script. Cc: Gerd Hoffmann Cc: Blue Swirl Signed-off-by: Andreas Färber Signed-off-by: Alexander Graf --- ppc64.ld | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ppc64.ld b/ppc64.ld index 0059ee53ac..0a7c0dd0c8 100644 --- a/ppc64.ld +++ b/ppc64.ld @@ -81,8 +81,8 @@ SECTIONS .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) } .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } .eh_frame_hdr : { *(.eh_frame_hdr) } -*(.gcc_except_table.*) } /* Adjust the address for the data segment. We want to -adjust up to + the same address within the page on the next page up. */ + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ . = ALIGN (0x10000) - ((0x10000 - .) & (0x10000 - 1)); . = DATA_SEGMENT_ALIGN (0x10000, 0x1000); /* Exception handling */ .eh_frame : { KEEP (*(.eh_frame)) } From 6e19a1379d3d12c2d8f934aa8154037a609f4507 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 8 Oct 2011 10:00:02 +0000 Subject: [PATCH 78/87] ARM: fix segfault Fix a bug in bccd9ec5f098668576342c83d90d6d6833d61d33, target-arm/op_helper.c missed a change unlike all other targets. This lead to a NULL pointer dereferences. Reviewed-by: Peter Maydell Signed-off-by: Blue Swirl --- target-arm/op_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index ab9c9239d3..1892b35ecc 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -84,6 +84,7 @@ void tlb_fill(CPUState *env1, target_ulong addr, int is_write, int mmu_idx, int ret; saved_env = env; + env = env1; ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx); if (unlikely(ret)) { if (retaddr) { From 498562926d798abf9b2fa2533a108f73a914fd3a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Oct 2011 13:56:23 +0100 Subject: [PATCH 79/87] tcg/ia64: Remove unused tcg_out_addi() Remove the unused function tcg_out_addi() from the ia64 TCG backend; this brings it into line with other backends. Signed-off-by: Peter Maydell Signed-off-by: Blue Swirl --- tcg/ia64/tcg-target.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tcg/ia64/tcg-target.c b/tcg/ia64/tcg-target.c index 3803ab6bfa..bb22b9abec 100644 --- a/tcg/ia64/tcg-target.c +++ b/tcg/ia64/tcg-target.c @@ -847,25 +847,6 @@ static inline void tcg_out_movi(TCGContext *s, TCGType type, tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg)); } -static inline void tcg_out_addi(TCGContext *s, TCGArg reg, tcg_target_long val) -{ - if (val == ((int32_t)val << 10) >> 10) { - tcg_out_bundle(s, MmI, - tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, - TCG_REG_R2, val, TCG_REG_R0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg, - reg, TCG_REG_R2)); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, val); - tcg_out_bundle(s, mmI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg, - reg, TCG_REG_R2)); - } -} - static void tcg_out_br(TCGContext *s, int label_index) { TCGLabel *l = &s->labels[label_index]; From dc439de7f96dc1a17e137273366123df24837876 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 1 Oct 2011 13:56:29 +0100 Subject: [PATCH 80/87] tcg/s390: Remove unused tcg_out_addi() Remove the unused function tcg_out_addi() from the s390 TCG backend; this brings it into line with other backends. Signed-off-by: Peter Maydell Acked-by: Richard Henderson Signed-off-by: Blue Swirl --- tcg/s390/tcg-target.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c index b58df719a6..9317fe88ef 100644 --- a/tcg/s390/tcg-target.c +++ b/tcg/s390/tcg-target.c @@ -2322,8 +2322,3 @@ static void tcg_target_qemu_prologue(TCGContext *s) /* br %r14 (return) */ tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14); } - -static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) -{ - tcg_abort(); -} From a9406ea127b0561cf95e5a4e8ce0c5e06c21e644 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 22 Sep 2011 08:11:18 -0700 Subject: [PATCH 81/87] target-alpha: Honor icount for RPCC instruction. Signed-off-by: Richard Henderson --- target-alpha/translate.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target-alpha/translate.c b/target-alpha/translate.c index 1e224a2152..fb2e9e5f60 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -2721,8 +2721,16 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) break; case 0xC000: /* RPCC */ - if (ra != 31) - gen_helper_load_pcc(cpu_ir[ra]); + if (ra != 31) { + if (use_icount) { + gen_io_start(); + gen_helper_load_pcc(cpu_ir[ra]); + gen_io_end(); + ret = EXIT_PC_STALE; + } else { + gen_helper_load_pcc(cpu_ir[ra]); + } + } break; case 0xE000: /* RC */ From 753d11f2238663990191ba8285bfe9301f7e4020 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 24 Jun 2011 11:58:37 -0700 Subject: [PATCH 82/87] target-alpha: Add custom PALcode image for CLIPPER emulation. Signed-off-by: Richard Henderson --- .gitmodules | 3 +++ Makefile | 3 ++- configure | 8 +++++++- pc-bios/README | 3 +++ pc-bios/palcode-clipper | Bin 0 -> 185703 bytes roms/qemu-palcode | 1 + 6 files changed, 16 insertions(+), 2 deletions(-) create mode 100755 pc-bios/palcode-clipper create mode 160000 roms/qemu-palcode diff --git a/.gitmodules b/.gitmodules index c3faa38790..2a43dbcd7c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "roms/openbios"] path = roms/openbios url = git://git.qemu.org/openbios.git +[submodule "roms/qemu-palcode"] + path = roms/qemu-palcode + url = git://repo.or.cz/qemu-palcode.git diff --git a/Makefile b/Makefile index 6ed3194fd7..8b6c19a0a5 100644 --- a/Makefile +++ b/Makefile @@ -251,7 +251,8 @@ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ mpc8544ds.dtb \ multiboot.bin linuxboot.bin \ s390-zipl.rom \ -spapr-rtas.bin slof.bin +spapr-rtas.bin slof.bin \ +palcode-clipper else BLOBS= endif diff --git a/configure b/configure index 29cdd752a9..9b4fe34fce 100755 --- a/configure +++ b/configure @@ -3618,7 +3618,13 @@ FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" FILES="$FILES pc-bios/spapr-rtas/Makefile" FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" -for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.rom $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do +for bios_file in \ + $source_path/pc-bios/*.bin \ + $source_path/pc-bios/*.rom \ + $source_path/pc-bios/*.dtb \ + $source_path/pc-bios/openbios-* \ + $source_path/pc-bios/palcode-* +do FILES="$FILES pc-bios/`basename $bios_file`" done mkdir -p $DIRS diff --git a/pc-bios/README b/pc-bios/README index 02651fe224..4d1d816fdb 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -32,3 +32,6 @@ - The S390 zipl loader is an addition to the official IBM s390-tools package. That fork is maintained in its own git repository at: git://repo.or.cz/s390-tools.git + +- The sources for the Alpha palcode image is available from: + git://repo.or.cz/qemu-palcode.git diff --git a/pc-bios/palcode-clipper b/pc-bios/palcode-clipper new file mode 100755 index 0000000000000000000000000000000000000000..a92372c107af72071e265e1ca94b9ae5573bd317 GIT binary patch literal 185703 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 literal 0 HcmV?d00001 diff --git a/roms/qemu-palcode b/roms/qemu-palcode new file mode 160000 index 0000000000..7abb12f60e --- /dev/null +++ b/roms/qemu-palcode @@ -0,0 +1 @@ +Subproject commit 7abb12f60eb3069019e9497e193733d77d8f0722 From 80bb2ff770e3e6ff2922a7b60978394571ec64cb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 25 Aug 2011 11:38:59 -1000 Subject: [PATCH 83/87] target-alpha: Add CLIPPER emulation. This is a DP264 variant, SMP capable, no unusual hardware present. The emulation does not currently include any PCI IOMMU code. Hopefully the generic support for that can be merged to HEAD soon. Signed-off-by: Richard Henderson --- Makefile.target | 1 + default-configs/alpha-softmmu.mak | 2 + hw/alpha_dp264.c | 177 +++++++ hw/alpha_pci.c | 134 +++++ hw/alpha_sys.h | 24 + hw/alpha_typhoon.c | 794 ++++++++++++++++++++++++++++++ 6 files changed, 1132 insertions(+) create mode 100644 hw/alpha_dp264.c create mode 100644 hw/alpha_pci.c create mode 100644 hw/alpha_sys.h create mode 100644 hw/alpha_typhoon.c diff --git a/Makefile.target b/Makefile.target index 1e9815cb2d..1aa6fce690 100644 --- a/Makefile.target +++ b/Makefile.target @@ -367,6 +367,7 @@ obj-s390x-y = s390-virtio-bus.o s390-virtio.o obj-alpha-y = i8259.o mc146818rtc.o obj-alpha-y += vga.o cirrus_vga.o +obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o obj-xtensa-y += xtensa_pic.o obj-xtensa-y += xtensa_sample.o diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak index abadcffec9..be86d0c4e6 100644 --- a/default-configs/alpha-softmmu.mak +++ b/default-configs/alpha-softmmu.mak @@ -3,7 +3,9 @@ include pci.mak CONFIG_SERIAL=y CONFIG_I8254=y +CONFIG_PCKBD=y CONFIG_VGA_PCI=y CONFIG_IDE_CORE=y CONFIG_IDE_QDEV=y CONFIG_VMWARE_VGA=y +CONFIG_IDE_CMD646=y diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c new file mode 100644 index 0000000000..7c36b21f0c --- /dev/null +++ b/hw/alpha_dp264.c @@ -0,0 +1,177 @@ +/* + * QEMU Alpha DP264/CLIPPER hardware system emulator. + * + * Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK + * variants because CLIPPER doesn't have an SMC669 SuperIO controler + * that we need to emulate as well. + */ + +#include "hw.h" +#include "elf.h" +#include "loader.h" +#include "boards.h" +#include "alpha_sys.h" +#include "sysemu.h" +#include "mc146818rtc.h" +#include "ide.h" + +#define MAX_IDE_BUS 2 + +static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr) +{ + if (((addr >> 41) & 3) == 2) { + addr &= 0xffffffffffull; + } + return addr; +} + +/* Note that there are at least 3 viewpoints of IRQ numbers on Alpha systems. + (0) The dev_irq_n lines into the cpu, which we totally ignore, + (1) The DRIR lines in the typhoon chipset, + (2) The "vector" aka mangled interrupt number reported by SRM PALcode, + (3) The interrupt number assigned by the kernel. + The following function is concerned with (1) only. */ + +static int clipper_pci_map_irq(PCIDevice *d, int irq_num) +{ + int slot = d->devfn >> 3; + + assert(irq_num >= 0 && irq_num <= 3); + + return (slot + 1) * 4 + irq_num; +} + +static void clipper_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *cpus[4]; + PCIBus *pci_bus; + qemu_irq rtc_irq; + long size, i; + const char *palcode_filename; + uint64_t palcode_entry, palcode_low, palcode_high; + uint64_t kernel_entry, kernel_low, kernel_high; + + /* Create up to 4 cpus. */ + memset(cpus, 0, sizeof(cpus)); + for (i = 0; i < smp_cpus; ++i) { + cpus[i] = cpu_init(cpu_model ? cpu_model : "ev67"); + } + + cpus[0]->trap_arg0 = ram_size; + cpus[0]->trap_arg1 = 0; + cpus[0]->trap_arg2 = smp_cpus; + + /* Init the chipset. */ + pci_bus = typhoon_init(ram_size, &rtc_irq, cpus, clipper_pci_map_irq); + + rtc_init(1980, rtc_irq); + pit_init(0x40, 0); + isa_create_simple("i8042"); + + /* VGA setup. Don't bother loading the bios. */ + alpha_pci_vga_setup(pci_bus); + + /* Serial code setup. */ + for (i = 0; i < MAX_SERIAL_PORTS; ++i) { + if (serial_hds[i]) { + serial_isa_init(i, serial_hds[i]); + } + } + + /* Network setup. e1000 is good enough, failing Tulip support. */ + for (i = 0; i < nb_nics; i++) { + pci_nic_init_nofail(&nd_table[i], "e1000", NULL); + } + + /* IDE disk setup. */ + { + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + ide_drive_get(hd, MAX_IDE_BUS); + + pci_cmd646_ide_init(pci_bus, hd, 0); + } + + /* Load PALcode. Given that this is not "real" cpu palcode, + but one explicitly written for the emulation, we might as + well load it directly from and ELF image. */ + palcode_filename = (bios_name ? bios_name : "palcode-clipper"); + palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename); + if (palcode_filename == NULL) { + hw_error("no palcode provided\n"); + exit(1); + } + size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys, + NULL, &palcode_entry, &palcode_low, &palcode_high, + 0, EM_ALPHA, 0); + if (size < 0) { + hw_error("could not load palcode '%s'\n", palcode_filename); + exit(1); + } + + /* Start all cpus at the PALcode RESET entry point. */ + for (i = 0; i < smp_cpus; ++i) { + cpus[i]->pal_mode = 1; + cpus[i]->pc = palcode_entry; + cpus[i]->palbr = palcode_entry; + } + + /* Load a kernel. */ + if (kernel_filename) { + uint64_t param_offset; + + size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys, + NULL, &kernel_entry, &kernel_low, &kernel_high, + 0, EM_ALPHA, 0); + if (size < 0) { + hw_error("could not load kernel '%s'\n", kernel_filename); + exit(1); + } + + cpus[0]->trap_arg1 = kernel_entry; + + param_offset = kernel_low - 0x6000; + + if (kernel_cmdline) { + pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline); + } + + if (initrd_filename) { + long initrd_base, initrd_size; + + initrd_size = get_image_size(initrd_filename); + if (initrd_size < 0) { + hw_error("could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + + /* Put the initrd image as high in memory as possible. */ + initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; + load_image_targphys(initrd_filename, initrd_base, + ram_size - initrd_base); + + stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000UL); + stq_phys(param_offset + 0x108, initrd_size); + } + } +} + +static QEMUMachine clipper_machine = { + .name = "clipper", + .desc = "Alpha DP264/CLIPPER", + .init = clipper_init, + .max_cpus = 4, + .is_default = 1, +}; + +static void clipper_machine_init(void) +{ + qemu_register_machine(&clipper_machine); +} + +machine_init(clipper_machine_init); diff --git a/hw/alpha_pci.c b/hw/alpha_pci.c new file mode 100644 index 0000000000..e9757028af --- /dev/null +++ b/hw/alpha_pci.c @@ -0,0 +1,134 @@ +/* + * QEMU Alpha PCI support functions. + * + * Some of this isn't very Alpha specific at all. + * + * ??? Sparse memory access not implemented. + */ + +#include "config.h" +#include "alpha_sys.h" +#include "qemu-log.h" +#include "sysemu.h" +#include "vmware_vga.h" + + +/* PCI IO reads/writes, to byte-word addressable memory. */ +/* ??? Doesn't handle multiple PCI busses. */ + +static uint64_t bw_io_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + } + abort(); +} + +static void bw_io_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + switch (size) { + case 1: + cpu_outb(addr, val); + break; + case 2: + cpu_outw(addr, val); + break; + case 4: + cpu_outl(addr, val); + break; + default: + abort(); + } +} + +const MemoryRegionOps alpha_pci_bw_io_ops = { + .read = bw_io_read, + .write = bw_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +/* PCI config space reads/writes, to byte-word addressable memory. */ +static uint64_t bw_conf1_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + PCIBus *b = opaque; + return pci_data_read(b, addr, size); +} + +static void bw_conf1_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + PCIBus *b = opaque; + pci_data_write(b, addr, val, size); +} + +const MemoryRegionOps alpha_pci_conf1_ops = { + .read = bw_conf1_read, + .write = bw_conf1_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +/* PCI/EISA Interrupt Acknowledge Cycle. */ + +static uint64_t iack_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + return pic_read_irq(isa_pic); +} + +static void special_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + qemu_log("pci: special write cycle"); +} + +const MemoryRegionOps alpha_pci_iack_ops = { + .read = iack_read, + .write = special_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +void alpha_pci_vga_setup(PCIBus *pci_bus) +{ + switch (vga_interface_type) { +#ifdef CONFIG_SPICE + case VGA_QXL: + pci_create_simple(pci_bus, -1, "qxl-vga"); + return; +#endif + case VGA_CIRRUS: + pci_cirrus_vga_init(pci_bus); + return; + case VGA_VMWARE: + if (pci_vmsvga_init(pci_bus)) { + return; + } + break; + } + /* If VGA is enabled at all, and one of the above didn't work, then + fallback to Standard VGA. */ + if (vga_interface_type != VGA_NONE) { + pci_vga_init(pci_bus); + } +} diff --git a/hw/alpha_sys.h b/hw/alpha_sys.h new file mode 100644 index 0000000000..13f017733b --- /dev/null +++ b/hw/alpha_sys.h @@ -0,0 +1,24 @@ +/* Alpha cores and system support chips. */ + +#ifndef HW_ALPHA_H +#define HW_ALPHA_H 1 + +#include "pci.h" +#include "pci_host.h" +#include "ide.h" +#include "net.h" +#include "pc.h" +#include "usb-ohci.h" +#include "irq.h" + + +PCIBus *typhoon_init(ram_addr_t, qemu_irq *, CPUState *[4], pci_map_irq_fn); + +/* alpha_pci.c. */ +extern const MemoryRegionOps alpha_pci_bw_io_ops; +extern const MemoryRegionOps alpha_pci_conf1_ops; +extern const MemoryRegionOps alpha_pci_iack_ops; + +void alpha_pci_vga_setup(PCIBus *pci_bus); + +#endif diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c new file mode 100644 index 0000000000..4c97219f8c --- /dev/null +++ b/hw/alpha_typhoon.c @@ -0,0 +1,794 @@ +/* + * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation. + * + * Written by Richard Henderson. + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "cpu.h" +#include "exec-all.h" +#include "hw.h" +#include "devices.h" +#include "sysemu.h" +#include "alpha_sys.h" +#include "exec-memory.h" + + +typedef struct TyphoonCchip { + MemoryRegion region; + uint64_t misc; + uint64_t drir; + uint64_t dim[4]; + uint32_t iic[4]; + CPUState *cpu[4]; +} TyphoonCchip; + +typedef struct TyphoonWindow { + uint32_t base_addr; + uint32_t mask; + uint32_t translated_base_pfn; +} TyphoonWindow; + +typedef struct TyphoonPchip { + MemoryRegion region; + MemoryRegion reg_iack; + MemoryRegion reg_mem; + MemoryRegion reg_io; + MemoryRegion reg_conf; + uint64_t ctl; + TyphoonWindow win[4]; +} TyphoonPchip; + +typedef struct TyphoonState { + PCIHostState host; + TyphoonCchip cchip; + TyphoonPchip pchip; + MemoryRegion dchip_region; + MemoryRegion ram_region; + + /* QEMU emulation state. */ + uint32_t latch_tmp; +} TyphoonState; + +/* Called when one of DRIR or DIM changes. */ +static void cpu_irq_change(CPUState *env, uint64_t req) +{ + /* If there are any non-masked interrupts, tell the cpu. */ + if (env) { + if (req) { + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } + } +} + +static uint64_t cchip_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + CPUState *env = cpu_single_env; + TyphoonState *s = opaque; + uint64_t ret = 0; + + if (addr & 4) { + return s->latch_tmp; + } + + switch (addr) { + case 0x0000: + /* CSC: Cchip System Configuration Register. */ + /* All sorts of data here; probably the only thing relevant is + PIP<14> Pchip 1 Present = 0. */ + break; + + case 0x0040: + /* MTR: Memory Timing Register. */ + /* All sorts of stuff related to real DRAM. */ + break; + + case 0x0080: + /* MISC: Miscellaneous Register. */ + ret = s->cchip.misc | (env->cpu_index & 3); + break; + + case 0x00c0: + /* MPD: Memory Presence Detect Register. */ + break; + + case 0x0100: /* AAR0 */ + case 0x0140: /* AAR1 */ + case 0x0180: /* AAR2 */ + case 0x01c0: /* AAR3 */ + /* AAR: Array Address Register. */ + /* All sorts of information about DRAM. */ + break; + + case 0x0200: + /* DIM0: Device Interrupt Mask Register, CPU0. */ + ret = s->cchip.dim[0]; + break; + case 0x0240: + /* DIM1: Device Interrupt Mask Register, CPU1. */ + ret = s->cchip.dim[1]; + break; + case 0x0280: + /* DIR0: Device Interrupt Request Register, CPU0. */ + ret = s->cchip.dim[0] & s->cchip.drir; + break; + case 0x02c0: + /* DIR1: Device Interrupt Request Register, CPU1. */ + ret = s->cchip.dim[1] & s->cchip.drir; + break; + case 0x0300: + /* DRIR: Device Raw Interrupt Request Register. */ + ret = s->cchip.drir; + break; + + case 0x0340: + /* PRBEN: Probe Enable Register. */ + break; + + case 0x0380: + /* IIC0: Interval Ignore Count Register, CPU0. */ + ret = s->cchip.iic[0]; + break; + case 0x03c0: + /* IIC1: Interval Ignore Count Register, CPU1. */ + ret = s->cchip.iic[1]; + break; + + case 0x0400: /* MPR0 */ + case 0x0440: /* MPR1 */ + case 0x0480: /* MPR2 */ + case 0x04c0: /* MPR3 */ + /* MPR: Memory Programming Register. */ + break; + + case 0x0580: + /* TTR: TIGbus Timing Register. */ + /* All sorts of stuff related to interrupt delivery timings. */ + break; + case 0x05c0: + /* TDR: TIGbug Device Timing Register. */ + break; + + case 0x0600: + /* DIM2: Device Interrupt Mask Register, CPU2. */ + ret = s->cchip.dim[2]; + break; + case 0x0640: + /* DIM3: Device Interrupt Mask Register, CPU3. */ + ret = s->cchip.dim[3]; + break; + case 0x0680: + /* DIR2: Device Interrupt Request Register, CPU2. */ + ret = s->cchip.dim[2] & s->cchip.drir; + break; + case 0x06c0: + /* DIR3: Device Interrupt Request Register, CPU3. */ + ret = s->cchip.dim[3] & s->cchip.drir; + break; + + case 0x0700: + /* IIC2: Interval Ignore Count Register, CPU2. */ + ret = s->cchip.iic[2]; + break; + case 0x0740: + /* IIC3: Interval Ignore Count Register, CPU3. */ + ret = s->cchip.iic[3]; + break; + + case 0x0780: + /* PWR: Power Management Control. */ + break; + + case 0x0c00: /* CMONCTLA */ + case 0x0c40: /* CMONCTLB */ + case 0x0c80: /* CMONCNT01 */ + case 0x0cc0: /* CMONCNT23 */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); + return -1; + } + + s->latch_tmp = ret >> 32; + return ret; +} + +static uint64_t dchip_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + /* Skip this. It's all related to DRAM timing and setup. */ + return 0; +} + +static uint64_t pchip_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t ret = 0; + + if (addr & 4) { + return s->latch_tmp; + } + + switch (addr) { + case 0x0000: + /* WSBA0: Window Space Base Address Register. */ + ret = s->pchip.win[0].base_addr; + break; + case 0x0040: + /* WSBA1 */ + ret = s->pchip.win[1].base_addr; + break; + case 0x0080: + /* WSBA2 */ + ret = s->pchip.win[2].base_addr; + break; + case 0x00c0: + /* WSBA3 */ + ret = s->pchip.win[3].base_addr; + break; + + case 0x0100: + /* WSM0: Window Space Mask Register. */ + ret = s->pchip.win[0].mask; + break; + case 0x0140: + /* WSM1 */ + ret = s->pchip.win[1].mask; + break; + case 0x0180: + /* WSM2 */ + ret = s->pchip.win[2].mask; + break; + case 0x01c0: + /* WSM3 */ + ret = s->pchip.win[3].mask; + break; + + case 0x0200: + /* TBA0: Translated Base Address Register. */ + ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10; + break; + case 0x0240: + /* TBA1 */ + ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10; + break; + case 0x0280: + /* TBA2 */ + ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10; + break; + case 0x02c0: + /* TBA3 */ + ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10; + break; + + case 0x0300: + /* PCTL: Pchip Control Register. */ + ret = s->pchip.ctl; + break; + case 0x0340: + /* PLAT: Pchip Master Latency Register. */ + break; + case 0x03c0: + /* PERROR: Pchip Error Register. */ + break; + case 0x0400: + /* PERRMASK: Pchip Error Mask Register. */ + break; + case 0x0440: + /* PERRSET: Pchip Error Set Register. */ + break; + case 0x0480: + /* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */ + break; + case 0x04c0: + /* TLBIA: Translation Buffer Invalidate All Register (WO). */ + break; + case 0x0500: /* PMONCTL */ + case 0x0540: /* PMONCNT */ + case 0x0800: /* SPRST */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); + return -1; + } + + s->latch_tmp = ret >> 32; + return ret; +} + +static void cchip_write(void *opaque, target_phys_addr_t addr, + uint64_t v32, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t val, oldval, newval; + + if (addr & 4) { + val = v32 << 32 | s->latch_tmp; + addr ^= 4; + } else { + s->latch_tmp = v32; + return; + } + + switch (addr) { + case 0x0000: + /* CSC: Cchip System Configuration Register. */ + /* All sorts of data here; nothing relevant RW. */ + break; + + case 0x0040: + /* MTR: Memory Timing Register. */ + /* All sorts of stuff related to real DRAM. */ + break; + + case 0x0080: + /* MISC: Miscellaneous Register. */ + newval = oldval = s->cchip.misc; + newval &= ~(val & 0x10000ff0); /* W1C fields */ + if (val & 0x100000) { + newval &= ~0xff0000ull; /* ACL clears ABT and ABW */ + } else { + newval |= val & 0x00f00000; /* ABT field is W1S */ + if ((newval & 0xf0000) == 0) { + newval |= val & 0xf0000; /* ABW field is W1S iff zero */ + } + } + newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */ + + newval &= ~0xf0000000000ull; /* WO and RW fields */ + newval |= val & 0xf0000000000ull; + s->cchip.misc = newval; + + /* Pass on changes to IPI and ITI state. */ + if ((newval ^ oldval) & 0xff0) { + int i; + for (i = 0; i < 4; ++i) { + CPUState *env = s->cchip.cpu[i]; + if (env) { + /* IPI can be either cleared or set by the write. */ + if (newval & (1 << (i + 8))) { + cpu_interrupt(env, CPU_INTERRUPT_SMP); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_SMP); + } + + /* ITI can only be cleared by the write. */ + if ((newval & (1 << (i + 4))) == 0) { + cpu_reset_interrupt(env, CPU_INTERRUPT_TIMER); + } + } + } + } + break; + + case 0x00c0: + /* MPD: Memory Presence Detect Register. */ + break; + + case 0x0100: /* AAR0 */ + case 0x0140: /* AAR1 */ + case 0x0180: /* AAR2 */ + case 0x01c0: /* AAR3 */ + /* AAR: Array Address Register. */ + /* All sorts of information about DRAM. */ + break; + + case 0x0200: /* DIM0 */ + /* DIM: Device Interrupt Mask Register, CPU0. */ + s->cchip.dim[0] = val; + cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir); + break; + case 0x0240: /* DIM1 */ + /* DIM: Device Interrupt Mask Register, CPU1. */ + s->cchip.dim[0] = val; + cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir); + break; + + case 0x0280: /* DIR0 (RO) */ + case 0x02c0: /* DIR1 (RO) */ + case 0x0300: /* DRIR (RO) */ + break; + + case 0x0340: + /* PRBEN: Probe Enable Register. */ + break; + + case 0x0380: /* IIC0 */ + s->cchip.iic[0] = val & 0xffffff; + break; + case 0x03c0: /* IIC1 */ + s->cchip.iic[1] = val & 0xffffff; + break; + + case 0x0400: /* MPR0 */ + case 0x0440: /* MPR1 */ + case 0x0480: /* MPR2 */ + case 0x04c0: /* MPR3 */ + /* MPR: Memory Programming Register. */ + break; + + case 0x0580: + /* TTR: TIGbus Timing Register. */ + /* All sorts of stuff related to interrupt delivery timings. */ + break; + case 0x05c0: + /* TDR: TIGbug Device Timing Register. */ + break; + + case 0x0600: + /* DIM2: Device Interrupt Mask Register, CPU2. */ + s->cchip.dim[2] = val; + cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir); + break; + case 0x0640: + /* DIM3: Device Interrupt Mask Register, CPU3. */ + s->cchip.dim[3] = val; + cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir); + break; + + case 0x0680: /* DIR2 (RO) */ + case 0x06c0: /* DIR3 (RO) */ + break; + + case 0x0700: /* IIC2 */ + s->cchip.iic[2] = val & 0xffffff; + break; + case 0x0740: /* IIC3 */ + s->cchip.iic[3] = val & 0xffffff; + break; + + case 0x0780: + /* PWR: Power Management Control. */ + break; + + case 0x0c00: /* CMONCTLA */ + case 0x0c40: /* CMONCTLB */ + case 0x0c80: /* CMONCNT01 */ + case 0x0cc0: /* CMONCNT23 */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); + return; + } +} + +static void dchip_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + /* Skip this. It's all related to DRAM timing and setup. */ +} + +static void pchip_write(void *opaque, target_phys_addr_t addr, + uint64_t v32, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t val, oldval; + + if (addr & 4) { + val = v32 << 32 | s->latch_tmp; + addr ^= 4; + } else { + s->latch_tmp = v32; + return; + } + + switch (addr) { + case 0x0000: + /* WSBA0: Window Space Base Address Register. */ + s->pchip.win[0].base_addr = val; + break; + case 0x0040: + /* WSBA1 */ + s->pchip.win[1].base_addr = val; + break; + case 0x0080: + /* WSBA2 */ + s->pchip.win[2].base_addr = val; + break; + case 0x00c0: + /* WSBA3 */ + s->pchip.win[3].base_addr = val; + break; + + case 0x0100: + /* WSM0: Window Space Mask Register. */ + s->pchip.win[0].mask = val; + break; + case 0x0140: + /* WSM1 */ + s->pchip.win[1].mask = val; + break; + case 0x0180: + /* WSM2 */ + s->pchip.win[2].mask = val; + break; + case 0x01c0: + /* WSM3 */ + s->pchip.win[3].mask = val; + break; + + case 0x0200: + /* TBA0: Translated Base Address Register. */ + s->pchip.win[0].translated_base_pfn = val >> 10; + break; + case 0x0240: + /* TBA1 */ + s->pchip.win[1].translated_base_pfn = val >> 10; + break; + case 0x0280: + /* TBA2 */ + s->pchip.win[2].translated_base_pfn = val >> 10; + break; + case 0x02c0: + /* TBA3 */ + s->pchip.win[3].translated_base_pfn = val >> 10; + break; + + case 0x0300: + /* PCTL: Pchip Control Register. */ + oldval = s->pchip.ctl; + oldval &= ~0x00001cff0fc7ffull; /* RW fields */ + oldval |= val & 0x00001cff0fc7ffull; + + s->pchip.ctl = oldval; + break; + + case 0x0340: + /* PLAT: Pchip Master Latency Register. */ + break; + case 0x03c0: + /* PERROR: Pchip Error Register. */ + break; + case 0x0400: + /* PERRMASK: Pchip Error Mask Register. */ + break; + case 0x0440: + /* PERRSET: Pchip Error Set Register. */ + break; + + case 0x0480: + /* TLBIV: Translation Buffer Invalidate Virtual Register. */ + break; + + case 0x04c0: + /* TLBIA: Translation Buffer Invalidate All Register (WO). */ + break; + + case 0x0500: + /* PMONCTL */ + case 0x0540: + /* PMONCNT */ + case 0x0800: + /* SPRST */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); + return; + } +} + +static const MemoryRegionOps cchip_ops = { + .read = cchip_read, + .write = cchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps dchip_ops = { + .read = dchip_read, + .write = dchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static const MemoryRegionOps pchip_ops = { + .read = pchip_read, + .write = pchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void typhoon_set_irq(void *opaque, int irq, int level) +{ + TyphoonState *s = opaque; + uint64_t drir; + int i; + + /* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */ + drir = s->cchip.drir; + if (level) { + drir |= 1ull << irq; + } else { + drir &= ~(1ull << irq); + } + s->cchip.drir = drir; + + for (i = 0; i < 4; ++i) { + cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir); + } +} + +static void typhoon_set_isa_irq(void *opaque, int irq, int level) +{ + typhoon_set_irq(opaque, 55, level); +} + +static void typhoon_set_timer_irq(void *opaque, int irq, int level) +{ + TyphoonState *s = opaque; + int i; + + /* Thankfully, the mc146818rtc code doesn't track the IRQ state, + and so we don't have to worry about missing interrupts just + because we never actually ACK the interrupt. Just ignore any + case of the interrupt level going low. */ + if (level == 0) { + return; + } + + /* Deliver the interrupt to each CPU, considering each CPU's IIC. */ + for (i = 0; i < 4; ++i) { + CPUState *env = s->cchip.cpu[i]; + if (env) { + uint32_t iic = s->cchip.iic[i]; + + /* ??? The verbage in Section 10.2.2.10 isn't 100% clear. + Bit 24 is the OverFlow bit, RO, and set when the count + decrements past 0. When is OF cleared? My guess is that + OF is actually cleared when the IIC is written, and that + the ICNT field always decrements. At least, that's an + interpretation that makes sense, and "allows the CPU to + determine exactly how mant interval timer ticks were + skipped". At least within the next 4M ticks... */ + + iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000); + s->cchip.iic[i] = iic; + + if (iic & 0x1000000) { + /* Set the ITI bit for this cpu. */ + s->cchip.misc |= 1 << (i + 4); + /* And signal the interrupt. */ + cpu_interrupt(env, CPU_INTERRUPT_TIMER); + } + } + } +} + +PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq, + CPUState *cpus[4], pci_map_irq_fn sys_map_irq) +{ + const uint64_t MB = 1024 * 1024; + const uint64_t GB = 1024 * MB; + MemoryRegion *addr_space = get_system_memory(); + MemoryRegion *addr_space_io = get_system_io(); + DeviceState *dev; + PCIHostState *p; + TyphoonState *s; + PCIBus *b; + + dev = qdev_create(NULL, "typhoon-pcihost"); + qdev_init_nofail(dev); + + p = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); + s = container_of(p, TyphoonState, host); + + /* Remember the CPUs so that we can deliver interrupts to them. */ + memcpy(s->cchip.cpu, cpus, 4 * sizeof(CPUState *)); + + *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1); + + /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB, + but the address space hole reserved at this point is 8TB. */ + memory_region_init_ram(&s->ram_region, NULL, "ram", ram_size); + memory_region_add_subregion(addr_space, 0, &s->ram_region); + + /* TIGbus, 0x801.0000.0000, 1GB. */ + /* ??? The TIGbus is used for delivering interrupts, and access to + the flash ROM. I'm not sure that we need to implement it at all. */ + + /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */ + memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x80180000000, &s->pchip.region); + + /* Cchip CSRs, 0x801.A000.0000, 256MB. */ + memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x801a0000000, &s->cchip.region); + + /* Dchip CSRs, 0x801.B000.0000, 256MB. */ + memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x801b0000000, &s->dchip_region); + + /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */ + memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB); + memory_region_add_subregion(addr_space, 0x80000000000, &s->pchip.reg_mem); + + /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */ + /* ??? Ideally we drop the "system" i/o space on the floor and give the + PCI subsystem the full address space reserved by the chipset. + We can't do that until the MEM and IO paths in memory.c are unified. */ + memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL, + "pci0-io", 32*MB); + memory_region_add_subregion(addr_space, 0x801fc000000, &s->pchip.reg_io); + + b = pci_register_bus(&s->host.busdev.qdev, "pci", + typhoon_set_irq, sys_map_irq, s, + &s->pchip.reg_mem, addr_space_io, 0, 64); + s->host.bus = b; + + /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ + memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b, + "pci0-iack", 64*MB); + memory_region_add_subregion(addr_space, 0x801f8000000, &s->pchip.reg_iack); + + /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */ + memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b, + "pci0-conf", 16*MB); + memory_region_add_subregion(addr_space, 0x801fe000000, &s->pchip.reg_conf); + + /* For the record, these are the mappings for the second PCI bus. + We can get away with not implementing them because we indicate + via the Cchip.CSC bit that Pchip1 is not present. */ + /* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */ + /* Pchip1 CSRs, 0x802.8000.0000, 256MB. */ + /* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */ + /* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */ + /* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */ + + /* Init the ISA bus. */ + /* ??? Technically there should be a cy82c693ub pci-isa bridge. */ + { + qemu_irq isa_pci_irq, *isa_irqs; + + isa_bus_new(NULL, addr_space_io); + isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1); + isa_irqs = i8259_init(isa_pci_irq); + isa_bus_irqs(isa_irqs); + } + + return b; +} + +static int typhoon_pcihost_init(SysBusDevice *dev) +{ + return 0; +} + +static SysBusDeviceInfo typhoon_pcihost_info = { + .init = typhoon_pcihost_init, + .qdev.name = "typhoon-pcihost", + .qdev.size = sizeof(TyphoonState), + .qdev.no_user = 1 +}; + +static void typhoon_register(void) +{ + sysbus_register_withprop(&typhoon_pcihost_info); +} +device_init(typhoon_register); From bc24270e6471fb916a3816de9e63c31474392739 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Apr 2011 18:58:09 -0700 Subject: [PATCH 84/87] target-alpha: Implement WAIT IPR. Signed-off-by: Richard Henderson --- target-alpha/translate.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/target-alpha/translate.c b/target-alpha/translate.c index fb2e9e5f60..c1ef465679 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -1617,9 +1617,10 @@ static void gen_mfpr(int ra, int regno) } } -static void gen_mtpr(int rb, int regno) +static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) { TCGv tmp; + int data; if (rb == 31) { tmp = tcg_const_i64(0); @@ -1627,19 +1628,27 @@ static void gen_mtpr(int rb, int regno) tmp = cpu_ir[rb]; } - /* These two register numbers perform a TLB cache flush. Thankfully we - can only do this inside PALmode, which means that the current basic - block cannot be affected by the change in mappings. */ - if (regno == 255) { + switch (regno) { + case 255: /* TBIA */ gen_helper_tbia(); - } else if (regno == 254) { + break; + + case 254: /* TBIS */ gen_helper_tbis(tmp); - } else { + break; + + case 253: + /* WAIT */ + tmp = tcg_const_i64(1); + tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUState, halted)); + return gen_excp(ctx, EXCP_HLT, 0); + + default: /* The basic registers are data only, and unknown registers are read-zero, write-ignore. */ - int data = cpu_pr_data(regno); + data = cpu_pr_data(regno); if (data != 0) { if (data & PR_BYTE) { tcg_gen_st8_i64(tmp, cpu_env, data & ~PR_BYTE); @@ -1649,11 +1658,14 @@ static void gen_mtpr(int rb, int regno) tcg_gen_st_i64(tmp, cpu_env, data); } } + break; } if (rb == 31) { tcg_temp_free(tmp); } + + return NO_EXIT; } #endif /* !USER_ONLY*/ @@ -3061,8 +3073,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) /* HW_MTPR (PALcode) */ #ifndef CONFIG_USER_ONLY if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { - gen_mtpr(rb, insn & 0xffff); - break; + return gen_mtpr(ctx, rb, insn & 0xffff); } #endif goto invalid_opc; From 034ebc2753e7d16879a91e4407c4e0706f63604e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Apr 2011 09:22:52 -0700 Subject: [PATCH 85/87] target-alpha: Implement HALT IPR. Signed-off-by: Richard Henderson --- target-alpha/helper.h | 1 + target-alpha/op_helper.c | 10 ++++++++++ target-alpha/translate.c | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/target-alpha/helper.h b/target-alpha/helper.h index 2dec57e44b..c352c2493e 100644 --- a/target-alpha/helper.h +++ b/target-alpha/helper.h @@ -113,6 +113,7 @@ DEF_HELPER_2(stq_c_phys, i64, i64, i64) DEF_HELPER_FLAGS_0(tbia, TCG_CALL_CONST, void) DEF_HELPER_FLAGS_1(tbis, TCG_CALL_CONST, void, i64) +DEF_HELPER_1(halt, void, i64); #endif #include "def-helper.h" diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c index d8945dcbca..1896ab8b00 100644 --- a/target-alpha/op_helper.c +++ b/target-alpha/op_helper.c @@ -22,6 +22,7 @@ #include "host-utils.h" #include "softfloat.h" #include "helper.h" +#include "sysemu.h" #include "qemu-timer.h" #define FP_STATUS (env->fp_status) @@ -1218,6 +1219,15 @@ void helper_tbis(uint64_t p) { tlb_flush_page(env, p); } + +void helper_halt(uint64_t restart) +{ + if (restart) { + qemu_system_reset_request(); + } else { + qemu_system_shutdown_request(); + } +} #endif /*****************************************************************************/ diff --git a/target-alpha/translate.c b/target-alpha/translate.c index c1ef465679..0acbd682d3 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -1645,6 +1645,11 @@ static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUState, halted)); return gen_excp(ctx, EXCP_HLT, 0); + case 252: + /* HALT */ + gen_helper_halt(tmp); + return EXIT_PC_STALE; + default: /* The basic registers are data only, and unknown registers are read-zero, write-ignore. */ From c781cf96e298b9134b05ed1e7ca981a929e08e77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Apr 2011 10:40:08 -0700 Subject: [PATCH 86/87] target-alpha: Add high-resolution access to wall clock and an alarm. The alarm is a fully general one-shot time comparator, which will be usable under Linux as a hrtimer source. It's much more flexible than the RTC source available on real hardware. The wall clock allows the guest access to the host timekeeping. Much like the KVM wall clock source for other guests. Both are accessed via the PALcode Cserve entry point. Signed-off-by: Richard Henderson --- hw/alpha_typhoon.c | 21 ++++++++++++++++++++- target-alpha/cpu.h | 4 ++++ target-alpha/helper.h | 4 ++++ target-alpha/op_helper.c | 15 +++++++++++++++ target-alpha/translate.c | 29 +++++++++++++++++++++++++---- 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c index 4c97219f8c..c769fcc138 100644 --- a/hw/alpha_typhoon.c +++ b/hw/alpha_typhoon.c @@ -681,6 +681,16 @@ static void typhoon_set_timer_irq(void *opaque, int irq, int level) } } +static void typhoon_alarm_timer(void *opaque) +{ + TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3); + int cpu = (uintptr_t)opaque & 3; + + /* Set the ITI bit for this cpu. */ + s->cchip.misc |= 1 << (cpu + 4); + cpu_interrupt(s->cchip.cpu[cpu], CPU_INTERRUPT_TIMER); +} + PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq, CPUState *cpus[4], pci_map_irq_fn sys_map_irq) { @@ -692,6 +702,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq, PCIHostState *p; TyphoonState *s; PCIBus *b; + int i; dev = qdev_create(NULL, "typhoon-pcihost"); qdev_init_nofail(dev); @@ -700,7 +711,15 @@ PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq, s = container_of(p, TyphoonState, host); /* Remember the CPUs so that we can deliver interrupts to them. */ - memcpy(s->cchip.cpu, cpus, 4 * sizeof(CPUState *)); + for (i = 0; i < 4; i++) { + CPUState *env = cpus[i]; + s->cchip.cpu[i] = env; + if (env) { + env->alarm_timer = qemu_new_timer_ns(rtc_clock, + typhoon_alarm_timer, + (void *)((uintptr_t)s + i)); + } + } *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1); diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index c2e7bb31ef..9d61d45ab6 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -265,6 +265,10 @@ struct CPUAlphaState { uint64_t scratch[24]; #endif + /* This alarm doesn't exist in real hardware; we wish it did. */ + struct QEMUTimer *alarm_timer; + uint64_t alarm_expire; + #if TARGET_LONG_BITS > HOST_LONG_BITS /* temporary fixed-point registers * used to emulate 64 bits target on 32 bits hosts diff --git a/target-alpha/helper.h b/target-alpha/helper.h index c352c2493e..b693ceea97 100644 --- a/target-alpha/helper.h +++ b/target-alpha/helper.h @@ -113,7 +113,11 @@ DEF_HELPER_2(stq_c_phys, i64, i64, i64) DEF_HELPER_FLAGS_0(tbia, TCG_CALL_CONST, void) DEF_HELPER_FLAGS_1(tbis, TCG_CALL_CONST, void, i64) + DEF_HELPER_1(halt, void, i64); + +DEF_HELPER_FLAGS_0(get_time, TCG_CALL_CONST, i64) +DEF_HELPER_FLAGS_1(set_alarm, TCG_CALL_CONST, void, i64) #endif #include "def-helper.h" diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c index 1896ab8b00..cc102dbd63 100644 --- a/target-alpha/op_helper.c +++ b/target-alpha/op_helper.c @@ -1228,6 +1228,21 @@ void helper_halt(uint64_t restart) qemu_system_shutdown_request(); } } + +uint64_t helper_get_time(void) +{ + return qemu_get_clock_ns(rtc_clock); +} + +void helper_set_alarm(uint64_t expire) +{ + if (expire) { + env->alarm_expire = expire; + qemu_mod_timer(env->alarm_timer, expire); + } else { + qemu_del_timer(env->alarm_timer); + } +} #endif /*****************************************************************************/ diff --git a/target-alpha/translate.c b/target-alpha/translate.c index 0acbd682d3..a961159d5d 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -1590,18 +1590,34 @@ static int cpu_pr_data(int pr) return offsetof(CPUAlphaState, shadow[pr - 32]); case 40 ... 63: return offsetof(CPUAlphaState, scratch[pr - 40]); + + case 251: + return offsetof(CPUAlphaState, alarm_expire); } return 0; } -static void gen_mfpr(int ra, int regno) +static ExitStatus gen_mfpr(int ra, int regno) { int data = cpu_pr_data(regno); /* In our emulated PALcode, these processor registers have no side effects from reading. */ if (ra == 31) { - return; + return NO_EXIT; + } + + if (regno == 250) { + /* WALL_TIME */ + if (use_icount) { + gen_io_start(); + gen_helper_get_time(cpu_ir[ra]); + gen_io_end(); + return EXIT_PC_STALE; + } else { + gen_helper_get_time(cpu_ir[ra]); + return NO_EXIT; + } } /* The basic registers are data only, and unknown registers @@ -1615,6 +1631,7 @@ static void gen_mfpr(int ra, int regno) } else { tcg_gen_ld_i64(cpu_ir[ra], cpu_env, data); } + return NO_EXIT; } static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) @@ -1650,6 +1667,11 @@ static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) gen_helper_halt(tmp); return EXIT_PC_STALE; + case 251: + /* ALARM */ + gen_helper_set_alarm(tmp); + break; + default: /* The basic registers are data only, and unknown registers are read-zero, write-ignore. */ @@ -2772,8 +2794,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) /* HW_MFPR (PALcode) */ #ifndef CONFIG_USER_ONLY if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { - gen_mfpr(ra, insn & 0xffff); - break; + return gen_mfpr(ra, insn & 0xffff); } #endif goto invalid_opc; From 02d6516c8ba00bdd6d96b622f000cb28c3449f43 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Sun, 9 Oct 2011 08:50:50 +0200 Subject: [PATCH 87/87] target-alpha: Fix compilation errors for 32 bit hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On i386, these errors were reported: qemu/hw/alpha_dp264.c: In function ‘clipper_init’: qemu/hw/alpha_dp264.c:158: error: integer constant is too large for ‘unsigned long’ type qemu/hw/alpha_typhoon.c: In function ‘typhoon_init’: qemu/hw/alpha_typhoon.c:737: error: integer constant is too large for ‘long’ type qemu/hw/alpha_typhoon.c:741: error: integer constant is too large for ‘long’ type qemu/hw/alpha_typhoon.c:745: error: integer constant is too large for ‘long’ type qemu/hw/alpha_typhoon.c:749: error: integer constant is too large for ‘long’ type qemu/hw/alpha_typhoon.c:757: error: integer constant is too large for ‘long’ type qemu/hw/alpha_typhoon.c:767: error: integer constant is too large for ‘long’ type qemu/hw/alpha_typhoon.c:772: error: integer constant is too large for ‘long’ type Signed-off-by: Stefan Weil Signed-off-by: Blue Swirl --- hw/alpha_dp264.c | 2 +- hw/alpha_typhoon.c | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c index 7c36b21f0c..fcc20e973d 100644 --- a/hw/alpha_dp264.c +++ b/hw/alpha_dp264.c @@ -155,7 +155,7 @@ static void clipper_init(ram_addr_t ram_size, load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); - stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000UL); + stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000ULL); stq_phys(param_offset + 0x108, initrd_size); } } diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c index c769fcc138..c7608bbabd 100644 --- a/hw/alpha_typhoon.c +++ b/hw/alpha_typhoon.c @@ -734,19 +734,23 @@ PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq, /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */ memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB); - memory_region_add_subregion(addr_space, 0x80180000000, &s->pchip.region); + memory_region_add_subregion(addr_space, 0x80180000000ULL, + &s->pchip.region); /* Cchip CSRs, 0x801.A000.0000, 256MB. */ memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB); - memory_region_add_subregion(addr_space, 0x801a0000000, &s->cchip.region); + memory_region_add_subregion(addr_space, 0x801a0000000ULL, + &s->cchip.region); /* Dchip CSRs, 0x801.B000.0000, 256MB. */ memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB); - memory_region_add_subregion(addr_space, 0x801b0000000, &s->dchip_region); + memory_region_add_subregion(addr_space, 0x801b0000000ULL, + &s->dchip_region); /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */ memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB); - memory_region_add_subregion(addr_space, 0x80000000000, &s->pchip.reg_mem); + memory_region_add_subregion(addr_space, 0x80000000000ULL, + &s->pchip.reg_mem); /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */ /* ??? Ideally we drop the "system" i/o space on the floor and give the @@ -754,7 +758,8 @@ PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq, We can't do that until the MEM and IO paths in memory.c are unified. */ memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL, "pci0-io", 32*MB); - memory_region_add_subregion(addr_space, 0x801fc000000, &s->pchip.reg_io); + memory_region_add_subregion(addr_space, 0x801fc000000ULL, + &s->pchip.reg_io); b = pci_register_bus(&s->host.busdev.qdev, "pci", typhoon_set_irq, sys_map_irq, s, @@ -764,12 +769,14 @@ PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq, /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b, "pci0-iack", 64*MB); - memory_region_add_subregion(addr_space, 0x801f8000000, &s->pchip.reg_iack); + memory_region_add_subregion(addr_space, 0x801f8000000ULL, + &s->pchip.reg_iack); /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */ memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b, "pci0-conf", 16*MB); - memory_region_add_subregion(addr_space, 0x801fe000000, &s->pchip.reg_conf); + memory_region_add_subregion(addr_space, 0x801fe000000ULL, + &s->pchip.reg_conf); /* For the record, these are the mappings for the second PCI bus. We can get away with not implementing them because we indicate