diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2016-03-30 17:32:11 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2016-03-30 17:32:11 +0100 |
commit | 4468d4e0f383f09c7a4df2480a2bf87fafcbe20b (patch) | |
tree | 033500f975fe01bd5277631202d0730cb1db0521 | |
parent | 489ef4c810033e63af570c8a430af8b9858bfa5f (diff) | |
parent | db31e49a565fc4c165fd98201721b313c3412c1f (diff) |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20160330-1' into staging
target-arm queue:
* virt: fix the virtual power button by adding a modelled
"key press for 100ms" device
* various improvements to m25p80 flash devices
* implement new QMP query-gic-capability command to let the
management layer know what versions of GIC we support
# gpg: Signature made Wed 30 Mar 2016 17:30:51 BST using RSA key ID 14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg: aka "Peter Maydell <pmaydell@gmail.com>"
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
* remotes/pmaydell/tags/pull-target-arm-20160330-1:
arm: implement query-gic-capabilities
kvm: add kvm_device_supported() helper function
arm: enhance kvm_arm_create_scratch_host_vcpu
arm: qmp: add query-gic-capabilities interface
block: m25p80: at25128a/at25256a models
block: m25p80: n25q256a/n25q512a models
block: m25p80: Implemented FSR register
block: m25p80: Fast read and 4bytes commands
block: m25p80: Dummy cycles for N25Q256/512
block: m25p80: Add configuration registers
block: m25p80: 4byte address mode
block: m25p80: Extend address mode
block: m25p80: Widen flags variable
block: m25p80: RESET_ENABLE and RESET_MEMORY commands
block: m25p80: Removed unused variable
ARM: Virt: Use gpio_key for power button
hw/gpio: Add the emulation of gpio_key
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | default-configs/arm-softmmu.mak | 1 | ||||
-rw-r--r-- | hw/arm/virt.c | 7 | ||||
-rw-r--r-- | hw/block/m25p80.c | 330 | ||||
-rw-r--r-- | hw/gpio/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/gpio/gpio_key.c | 104 | ||||
-rw-r--r-- | include/sysemu/kvm.h | 9 | ||||
-rw-r--r-- | kvm-all.c | 15 | ||||
-rw-r--r-- | monitor.c | 8 | ||||
-rw-r--r-- | qapi-schema.json | 36 | ||||
-rw-r--r-- | qmp-commands.hx | 27 | ||||
-rw-r--r-- | target-arm/Makefile.objs | 2 | ||||
-rw-r--r-- | target-arm/kvm.c | 14 | ||||
-rw-r--r-- | target-arm/kvm_arm.h | 7 | ||||
-rw-r--r-- | target-arm/monitor.c | 84 |
14 files changed, 620 insertions, 25 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 2bcd23656b..c63cdd073d 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -111,3 +111,4 @@ CONFIG_I82801B11=y CONFIG_ACPI=y CONFIG_SMBIOS=y CONFIG_ASPEED_SOC=y +CONFIG_GPIO_KEY=y diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a5e787dec6..56d35c7716 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -582,11 +582,11 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic) g_free(nodename); } -static DeviceState *pl061_dev; +static DeviceState *gpio_key_dev; static void virt_powerdown_req(Notifier *n, void *opaque) { /* use gpio Pin 3 for power button event */ - qemu_set_irq(qdev_get_gpio_in(pl061_dev, 3), 1); + qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); } static Notifier virt_system_powerdown_notifier = { @@ -596,6 +596,7 @@ static Notifier virt_system_powerdown_notifier = { static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic) { char *nodename; + DeviceState *pl061_dev; hwaddr base = vbi->memmap[VIRT_GPIO].base; hwaddr size = vbi->memmap[VIRT_GPIO].size; int irq = vbi->irqmap[VIRT_GPIO]; @@ -618,6 +619,8 @@ static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic) qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk"); qemu_fdt_setprop_cell(vbi->fdt, nodename, "phandle", phandle); + gpio_key_dev = sysbus_create_simple("gpio-key", -1, + qdev_get_gpio_in(pl061_dev, 3)); qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys"); qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys", "compatible", "gpio-keys"); qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#size-cells", 0); diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index de24f427dc..906b71257e 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -26,6 +26,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/ssi/ssi.h" +#include "qemu/bitops.h" #ifndef M25P80_ERR_DEBUG #define M25P80_ERR_DEBUG 0 @@ -46,7 +47,10 @@ /* set to allow the page program command to write 0s back to 1. Useful for * modelling EEPROM with SPI flash command set */ -#define WR_1 0x100 +#define EEPROM 0x100 + +/* 16 MiB max in 3 byte address mode */ +#define MAX_3BYTES_SIZE 0x1000000 typedef struct FlashPartInfo { const char *part_name; @@ -61,7 +65,7 @@ typedef struct FlashPartInfo { uint32_t sector_size; uint32_t n_sectors; uint32_t page_size; - uint8_t flags; + uint16_t flags; } FlashPartInfo; /* adapted from linux */ @@ -79,6 +83,30 @@ typedef struct FlashPartInfo { #define JEDEC_WINBOND 0xEF #define JEDEC_SPANSION 0x01 +/* Numonyx (Micron) Configuration register macros */ +#define VCFG_DUMMY 0x1 +#define VCFG_WRAP_SEQUENTIAL 0x2 +#define NVCFG_XIP_MODE_DISABLED (7 << 9) +#define NVCFG_XIP_MODE_MASK (7 << 9) +#define VCFG_XIP_MODE_ENABLED (1 << 3) +#define CFG_DUMMY_CLK_LEN 4 +#define NVCFG_DUMMY_CLK_POS 12 +#define VCFG_DUMMY_CLK_POS 4 +#define EVCFG_OUT_DRIVER_STRENGHT_DEF 7 +#define EVCFG_VPP_ACCELERATOR (1 << 3) +#define EVCFG_RESET_HOLD_ENABLED (1 << 4) +#define NVCFG_DUAL_IO_MASK (1 << 2) +#define EVCFG_DUAL_IO_ENABLED (1 << 6) +#define NVCFG_QUAD_IO_MASK (1 << 3) +#define EVCFG_QUAD_IO_ENABLED (1 << 7) +#define NVCFG_4BYTE_ADDR_MASK (1 << 0) +#define NVCFG_LOWER_SEGMENT_MASK (1 << 1) +#define CFG_UPPER_128MB_SEG_ENABLED 0x3 + +/* Numonyx (Micron) Flag Status Register macros */ +#define FSR_4BYTE_ADDR_MODE_ENABLED 0x1 +#define FSR_FLASH_READY (1 << 7) + static const FlashPartInfo known_devices[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ { INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) }, @@ -95,6 +123,12 @@ static const FlashPartInfo known_devices[] = { { INFO("at45db081d", 0x1f2500, 0, 64 << 10, 16, ER_4K) }, + /* Atmel EEPROMS - it is assumed, that don't care bit in command + * is set to 0. Block protection is not supported. + */ + { INFO("at25128a-nonjedec", 0x0, 0, 1, 131072, EEPROM) }, + { INFO("at25256a-nonjedec", 0x0, 0, 1, 262144, EEPROM) }, + /* EON -- en25xxx */ { INFO("en25f32", 0x1c3116, 0, 64 << 10, 64, ER_4K) }, { INFO("en25p32", 0x1c2016, 0, 64 << 10, 64, 0) }, @@ -206,8 +240,9 @@ static const FlashPartInfo known_devices[] = { { INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) }, { INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) }, - /* Numonyx -- n25q128 */ { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) }, + { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, + { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, }; typedef enum { @@ -218,21 +253,49 @@ typedef enum { WREN = 0x6, JEDEC_READ = 0x9f, BULK_ERASE = 0xc7, + READ_FSR = 0x70, - READ = 0x3, - FAST_READ = 0xb, + READ = 0x03, + READ4 = 0x13, + FAST_READ = 0x0b, + FAST_READ4 = 0x0c, DOR = 0x3b, + DOR4 = 0x3c, QOR = 0x6b, + QOR4 = 0x6c, DIOR = 0xbb, + DIOR4 = 0xbc, QIOR = 0xeb, + QIOR4 = 0xec, - PP = 0x2, + PP = 0x02, + PP4 = 0x12, DPP = 0xa2, QPP = 0x32, ERASE_4K = 0x20, + ERASE4_4K = 0x21, ERASE_32K = 0x52, ERASE_SECTOR = 0xd8, + ERASE4_SECTOR = 0xdc, + + EN_4BYTE_ADDR = 0xB7, + EX_4BYTE_ADDR = 0xE9, + + EXTEND_ADDR_READ = 0xC8, + EXTEND_ADDR_WRITE = 0xC5, + + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, + + RNVCR = 0xB5, + WNVCR = 0xB1, + + RVCR = 0x85, + WVCR = 0x81, + + REVCR = 0x65, + WEVCR = 0x61, } FlashCMD; typedef enum { @@ -246,8 +309,6 @@ typedef enum { typedef struct Flash { SSISlave parent_obj; - uint32_t r; - BlockBackend *blk; uint8_t *storage; @@ -261,7 +322,13 @@ typedef struct Flash { uint8_t needed_bytes; uint8_t cmd_in_progress; uint64_t cur_addr; + uint32_t nonvolatile_cfg; + uint32_t volatile_cfg; + uint32_t enh_volatile_cfg; bool write_enable; + bool four_bytes_address_mode; + bool reset_enable; + uint8_t ear; int64_t dirty_page; @@ -333,6 +400,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) switch (cmd) { case ERASE_4K: + case ERASE4_4K: len = 4 << 10; capa_to_assert = ER_4K; break; @@ -341,6 +409,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) capa_to_assert = ER_32K; break; case ERASE_SECTOR: + case ERASE4_SECTOR: len = s->pi->sector_size; break; case BULK_ERASE: @@ -387,7 +456,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data) " -> %" PRIx8 "\n", addr, prev, data); } - if (s->pi->flags & WR_1) { + if (s->pi->flags & EEPROM) { s->storage[s->cur_addr] = data; } else { s->storage[s->cur_addr] &= data; @@ -397,11 +466,43 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data) s->dirty_page = page; } +static inline int get_addr_length(Flash *s) +{ + /* check if eeprom is in use */ + if (s->pi->flags == EEPROM) { + return 2; + } + + switch (s->cmd_in_progress) { + case PP4: + case READ4: + case QIOR4: + case ERASE4_4K: + case ERASE4_SECTOR: + case FAST_READ4: + case DOR4: + case QOR4: + case DIOR4: + return 4; + default: + return s->four_bytes_address_mode ? 4 : 3; + } +} + static void complete_collecting_data(Flash *s) { - s->cur_addr = s->data[0] << 16; - s->cur_addr |= s->data[1] << 8; - s->cur_addr |= s->data[2]; + int i; + + s->cur_addr = 0; + + for (i = 0; i < get_addr_length(s); ++i) { + s->cur_addr <<= 8; + s->cur_addr |= s->data[i]; + } + + if (get_addr_length(s) == 3) { + s->cur_addr += (s->ear & 0x3) * MAX_3BYTES_SIZE; + } s->state = STATE_IDLE; @@ -409,19 +510,28 @@ static void complete_collecting_data(Flash *s) case DPP: case QPP: case PP: + case PP4: s->state = STATE_PAGE_PROGRAM; break; case READ: + case READ4: case FAST_READ: + case FAST_READ4: case DOR: + case DOR4: case QOR: + case QOR4: case DIOR: + case DIOR4: case QIOR: + case QIOR4: s->state = STATE_READ; break; case ERASE_4K: + case ERASE4_4K: case ERASE_32K: case ERASE_SECTOR: + case ERASE4_SECTOR: flash_erase(s, s->cur_addr, s->cmd_in_progress); break; case WRSR: @@ -429,49 +539,128 @@ static void complete_collecting_data(Flash *s) s->write_enable = false; } break; + case EXTEND_ADDR_WRITE: + s->ear = s->data[0]; + break; + case WNVCR: + s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8); + break; + case WVCR: + s->volatile_cfg = s->data[0]; + break; + case WEVCR: + s->enh_volatile_cfg = s->data[0]; + break; default: break; } } +static void reset_memory(Flash *s) +{ + s->cmd_in_progress = NOP; + s->cur_addr = 0; + s->ear = 0; + s->four_bytes_address_mode = false; + s->len = 0; + s->needed_bytes = 0; + s->pos = 0; + s->state = STATE_IDLE; + s->write_enable = false; + s->reset_enable = false; + + if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) { + s->volatile_cfg = 0; + s->volatile_cfg |= VCFG_DUMMY; + s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL; + if ((s->nonvolatile_cfg & NVCFG_XIP_MODE_MASK) + != NVCFG_XIP_MODE_DISABLED) { + s->volatile_cfg |= VCFG_XIP_MODE_ENABLED; + } + s->volatile_cfg |= deposit32(s->volatile_cfg, + VCFG_DUMMY_CLK_POS, + CFG_DUMMY_CLK_LEN, + extract32(s->nonvolatile_cfg, + NVCFG_DUMMY_CLK_POS, + CFG_DUMMY_CLK_LEN) + ); + + s->enh_volatile_cfg = 0; + s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGHT_DEF; + s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR; + s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED; + if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) { + s->enh_volatile_cfg |= EVCFG_DUAL_IO_ENABLED; + } + if (s->nonvolatile_cfg & NVCFG_QUAD_IO_MASK) { + s->enh_volatile_cfg |= EVCFG_QUAD_IO_ENABLED; + } + if (!(s->nonvolatile_cfg & NVCFG_4BYTE_ADDR_MASK)) { + s->four_bytes_address_mode = true; + } + if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) { + s->ear = CFG_UPPER_128MB_SEG_ENABLED; + } + } + + DB_PRINT_L(0, "Reset done.\n"); +} + static void decode_new_cmd(Flash *s, uint32_t value) { s->cmd_in_progress = value; DB_PRINT_L(0, "decoded new command:%x\n", value); + if (value != RESET_MEMORY) { + s->reset_enable = false; + } + switch (value) { case ERASE_4K: + case ERASE4_4K: case ERASE_32K: case ERASE_SECTOR: + case ERASE4_SECTOR: case READ: + case READ4: case DPP: case QPP: case PP: - s->needed_bytes = 3; + case PP4: + s->needed_bytes = get_addr_length(s); s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case FAST_READ: + case FAST_READ4: case DOR: + case DOR4: case QOR: - s->needed_bytes = 4; + case QOR4: + s->needed_bytes = get_addr_length(s); + if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) { + /* Dummy cycles modeled with bytes writes instead of bits */ + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); + } s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case DIOR: + case DIOR4: switch ((s->pi->jedec >> 16) & 0xFF) { case JEDEC_WINBOND: case JEDEC_SPANSION: s->needed_bytes = 4; break; - case JEDEC_NUMONYX: default: - s->needed_bytes = 5; + s->needed_bytes = get_addr_length(s); + /* Dummy cycles modeled with bytes writes instead of bits */ + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); } s->pos = 0; s->len = 0; @@ -479,14 +668,16 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case QIOR: + case QIOR4: switch ((s->pi->jedec >> 16) & 0xFF) { case JEDEC_WINBOND: case JEDEC_SPANSION: s->needed_bytes = 6; break; - case JEDEC_NUMONYX: default: - s->needed_bytes = 8; + s->needed_bytes = get_addr_length(s); + /* Dummy cycles modeled with bytes writes instead of bits */ + s->needed_bytes += extract32(s->volatile_cfg, 4, 4); } s->pos = 0; s->len = 0; @@ -516,6 +707,16 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->state = STATE_READING_DATA; break; + case READ_FSR: + s->data[0] = FSR_FLASH_READY; + if (s->four_bytes_address_mode) { + s->data[0] |= FSR_4BYTE_ADDR_MODE_ENABLED; + } + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case JEDEC_READ: DB_PRINT_L(0, "populated jedec code\n"); s->data[0] = (s->pi->jedec >> 16) & 0xff; @@ -543,6 +744,77 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case NOP: break; + case EN_4BYTE_ADDR: + s->four_bytes_address_mode = true; + break; + case EX_4BYTE_ADDR: + s->four_bytes_address_mode = false; + break; + case EXTEND_ADDR_READ: + s->data[0] = s->ear; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case EXTEND_ADDR_WRITE: + if (s->write_enable) { + s->needed_bytes = 1; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case RNVCR: + s->data[0] = s->nonvolatile_cfg & 0xFF; + s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF; + s->pos = 0; + s->len = 2; + s->state = STATE_READING_DATA; + break; + case WNVCR: + if (s->write_enable) { + s->needed_bytes = 2; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case RVCR: + s->data[0] = s->volatile_cfg & 0xFF; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case WVCR: + if (s->write_enable) { + s->needed_bytes = 1; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case REVCR: + s->data[0] = s->enh_volatile_cfg & 0xFF; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case WEVCR: + if (s->write_enable) { + s->needed_bytes = 1; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case RESET_ENABLE: + s->reset_enable = true; + break; + case RESET_MEMORY: + if (s->reset_enable) { + reset_memory(s); + } + break; default: qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; @@ -649,14 +921,26 @@ static int m25p80_init(SSISlave *ss) return 0; } +static void m25p80_reset(DeviceState *d) +{ + Flash *s = M25P80(d); + + reset_memory(s); +} + static void m25p80_pre_save(void *opaque) { flash_sync_dirty((Flash *)opaque, -1); } +static Property m25p80_properties[] = { + DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF), + DEFINE_PROP_END_OF_LIST(), +}; + static const VMStateDescription vmstate_m25p80 = { .name = "xilinx_spi", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .pre_save = m25p80_pre_save, .fields = (VMStateField[]) { @@ -668,6 +952,12 @@ static const VMStateDescription vmstate_m25p80 = { VMSTATE_UINT8(cmd_in_progress, Flash), VMSTATE_UINT64(cur_addr, Flash), VMSTATE_BOOL(write_enable, Flash), + VMSTATE_BOOL_V(reset_enable, Flash, 2), + VMSTATE_UINT8_V(ear, Flash, 2), + VMSTATE_BOOL_V(four_bytes_address_mode, Flash, 2), + VMSTATE_UINT32_V(nonvolatile_cfg, Flash, 2), + VMSTATE_UINT32_V(volatile_cfg, Flash, 2), + VMSTATE_UINT32_V(enh_volatile_cfg, Flash, 2), VMSTATE_END_OF_LIST() } }; @@ -683,6 +973,8 @@ static void m25p80_class_init(ObjectClass *klass, void *data) k->set_cs = m25p80_cs; k->cs_polarity = SSI_CS_LOW; dc->vmsd = &vmstate_m25p80; + dc->props = m25p80_properties; + dc->reset = m25p80_reset; mc->pi = data; } diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index 52233f7e2f..a43c7cf442 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -3,6 +3,7 @@ common-obj-$(CONFIG_PL061) += pl061.o common-obj-$(CONFIG_PUV3) += puv3_gpio.o common-obj-$(CONFIG_ZAURUS) += zaurus.o common-obj-$(CONFIG_E500) += mpc8xxx.o +common-obj-$(CONFIG_GPIO_KEY) += gpio_key.o obj-$(CONFIG_OMAP) += omap_gpio.o obj-$(CONFIG_IMX) += imx_gpio.o diff --git a/hw/gpio/gpio_key.c b/hw/gpio/gpio_key.c new file mode 100644 index 0000000000..ef287727b6 --- /dev/null +++ b/hw/gpio/gpio_key.c @@ -0,0 +1,104 @@ +/* + * GPIO key + * + * Copyright (c) 2016 Linaro Limited + * + * Author: Shannon Zhao <shannon.zhao@linaro.org> + * + * Emulate a (human) keypress -- when the key is triggered by + * setting the incoming gpio line, the outbound irq line is + * raised for 100ms before being dropped again. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" + +#define TYPE_GPIOKEY "gpio-key" +#define GPIOKEY(obj) OBJECT_CHECK(GPIOKEYState, (obj), TYPE_GPIOKEY) +#define GPIO_KEY_LATENCY 100 /* 100ms */ + +typedef struct GPIOKEYState { + SysBusDevice parent_obj; + + QEMUTimer *timer; + qemu_irq irq; +} GPIOKEYState; + +static const VMStateDescription vmstate_gpio_key = { + .name = "gpio-key", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(timer, GPIOKEYState), + VMSTATE_END_OF_LIST() + } +}; + +static void gpio_key_reset(DeviceState *dev) +{ + GPIOKEYState *s = GPIOKEY(dev); + + timer_del(s->timer); +} + +static void gpio_key_timer_expired(void *opaque) +{ + GPIOKEYState *s = (GPIOKEYState *)opaque; + + qemu_set_irq(s->irq, 0); + timer_del(s->timer); +} + +static void gpio_key_set_irq(void *opaque, int irq, int level) +{ + GPIOKEYState *s = (GPIOKEYState *)opaque; + + qemu_set_irq(s->irq, 1); + timer_mod(s->timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + GPIO_KEY_LATENCY); +} + +static void gpio_key_realize(DeviceState *dev, Error **errp) +{ + GPIOKEYState *s = GPIOKEY(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(dev, gpio_key_set_irq, 1); + s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, gpio_key_timer_expired, s); +} + +static void gpio_key_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = gpio_key_realize; + dc->vmsd = &vmstate_gpio_key; + dc->reset = &gpio_key_reset; +} + +static const TypeInfo gpio_key_info = { + .name = TYPE_GPIOKEY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GPIOKEYState), + .class_init = gpio_key_class_init, +}; + +static void gpio_key_register_types(void) +{ + type_register_static(&gpio_key_info); +} + +type_init(gpio_key_register_types) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 6695fa7cfd..0e18f15c94 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -306,6 +306,15 @@ void kvm_device_access(int fd, int group, uint64_t attr, */ int kvm_create_device(KVMState *s, uint64_t type, bool test); +/** + * kvm_device_supported - probe whether KVM supports specific device + * + * @vmfd: The fd handler for VM + * @type: type of device + * + * @return: true if supported, otherwise false. + */ +bool kvm_device_supported(int vmfd, uint64_t type); /* Arch specific hooks */ @@ -2339,6 +2339,21 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test) return test ? 0 : create_dev.fd; } +bool kvm_device_supported(int vmfd, uint64_t type) +{ + struct kvm_create_device create_dev = { + .type = type, + .fd = -1, + .flags = KVM_CREATE_DEVICE_TEST, + }; + + if (ioctl(vmfd, KVM_CHECK_EXTENSION, KVM_CAP_DEVICE_CTRL) <= 0) { + return false; + } + + return (ioctl(vmfd, KVM_CREATE_DEVICE, &create_dev) >= 0); +} + int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source) { struct kvm_one_reg reg; @@ -4259,3 +4259,11 @@ void qmp_dump_skeys(const char *filename, Error **errp) error_setg(errp, QERR_FEATURE_DISABLED, "dump-skeys"); } #endif + +#ifndef TARGET_ARM +GICCapabilityList *qmp_query_gic_capabilities(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "query-gic-capabilities"); + return NULL; +} +#endif diff --git a/qapi-schema.json b/qapi-schema.json index 8907790daa..e58f6a9a12 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -4134,3 +4134,39 @@ ## { 'enum': 'ReplayMode', 'data': [ 'none', 'record', 'play' ] } + +## +# @GICCapability: +# +# The struct describes capability for a specific GIC (Generic +# Interrupt Controller) version. These bits are not only decided by +# QEMU/KVM software version, but also decided by the hardware that +# the program is running upon. +# +# @version: version of GIC to be described. Currently, only 2 and 3 +# are supported. +# +# @emulated: whether current QEMU/hardware supports emulated GIC +# device in user space. +# +# @kernel: whether current QEMU/hardware supports hardware +# accelerated GIC device in kernel. +# +# Since: 2.6 +## +{ 'struct': 'GICCapability', + 'data': { 'version': 'int', + 'emulated': 'bool', + 'kernel': 'bool' } } + +## +# @query-gic-capabilities: +# +# This command is ARM-only. It will return a list of GICCapability +# objects that describe its capability bits. +# +# Returns: a list of GICCapability objects. +# +# Since: 2.6 +## +{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] } diff --git a/qmp-commands.hx b/qmp-commands.hx index 9e05365ccf..de896a5a31 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -4853,3 +4853,30 @@ Example: {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840, "pop-vlan": 1, "id": 251658240} ]} + +EQMP + +#if defined TARGET_ARM + { + .name = "query-gic-capabilities", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_query_gic_capabilities, + }, +#endif + +SQMP +query-gic-capabilities +--------------- + +Return a list of GICCapability objects, describing supported GIC +(Generic Interrupt Controller) versions. + +Arguments: None + +Example: + +-> { "execute": "query-gic-capabilities" } +<- { "return": [{ "version": 2, "emulated": true, "kernel": false }, + { "version": 3, "emulated": false, "kernel": true } ] } + +EQMP diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index a80eb39743..82cbe6bbad 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -1,5 +1,5 @@ obj-y += arm-semi.o -obj-$(CONFIG_SOFTMMU) += machine.o psci.o arch_dump.o +obj-$(CONFIG_SOFTMMU) += machine.o psci.o arch_dump.o monitor.o obj-$(CONFIG_KVM) += kvm.o obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 969ab0bab5..36710320f0 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -62,13 +62,18 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, goto err; } + if (!init) { + /* Caller doesn't want the VCPU to be initialized, so skip it */ + goto finish; + } + ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, init); if (ret >= 0) { ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init); if (ret < 0) { goto err; } - } else { + } else if (cpus_to_try) { /* Old kernel which doesn't know about the * PREFERRED_TARGET ioctl: we know it will only support * creating one kind of guest CPU which is its preferred @@ -85,8 +90,15 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, if (ret < 0) { goto err; } + } else { + /* Treat a NULL cpus_to_try argument the same as an empty + * list, which means we will fail the call since this must + * be an old kernel which doesn't support PREFERRED_TARGET. + */ + goto err; } +finish: fdarray[0] = kvmfd; fdarray[1] = vmfd; fdarray[2] = cpufd; diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index 07f0c72199..345233c18b 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -124,9 +124,12 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu); * kvm_arm_create_scratch_host_vcpu: * @cpus_to_try: array of QEMU_KVM_ARM_TARGET_* values (terminated with * QEMU_KVM_ARM_TARGET_NONE) to try as fallback if the kernel does not - * know the PREFERRED_TARGET ioctl + * know the PREFERRED_TARGET ioctl. Passing NULL is the same as passing + * an empty array. * @fdarray: filled in with kvmfd, vmfd, cpufd file descriptors in that order - * @init: filled in with the necessary values for creating a host vcpu + * @init: filled in with the necessary values for creating a host + * vcpu. If NULL is provided, will not init the vCPU (though the cpufd + * will still be set up). * * Create a scratch vcpu in its own VM of the type preferred by the host * kernel (as would be used for '-cpu host'), for purposes of probing it diff --git a/target-arm/monitor.c b/target-arm/monitor.c new file mode 100644 index 0000000000..1ee59a2e45 --- /dev/null +++ b/target-arm/monitor.c @@ -0,0 +1,84 @@ +/* + * QEMU monitor.c for ARM. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qmp-commands.h" +#include "hw/boards.h" +#include "kvm_arm.h" + +static GICCapability *gic_cap_new(int version) +{ + GICCapability *cap = g_new0(GICCapability, 1); + cap->version = version; + /* by default, support none */ + cap->emulated = false; + cap->kernel = false; + return cap; +} + +static GICCapabilityList *gic_cap_list_add(GICCapabilityList *head, + GICCapability *cap) +{ + GICCapabilityList *item = g_new0(GICCapabilityList, 1); + item->value = cap; + item->next = head; + return item; +} + +static inline void gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3) +{ +#ifdef CONFIG_KVM + int fdarray[3]; + + if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, NULL)) { + return; + } + + /* Test KVM GICv2 */ + if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V2)) { + v2->kernel = true; + } + + /* Test KVM GICv3 */ + if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V3)) { + v3->kernel = true; + } + + kvm_arm_destroy_scratch_host_vcpu(fdarray); +#endif +} + +GICCapabilityList *qmp_query_gic_capabilities(Error **errp) +{ + GICCapabilityList *head = NULL; + GICCapability *v2 = gic_cap_new(2), *v3 = gic_cap_new(3); + + v2->emulated = true; + /* TODO: we'd change to true after we get emulated GICv3. */ + v3->emulated = false; + + gic_cap_kvm_probe(v2, v3); + + head = gic_cap_list_add(head, v2); + head = gic_cap_list_add(head, v3); + + return head; +} |