aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS5
-rw-r--r--Makefile.target28
-rw-r--r--arch_init.c4
-rw-r--r--block/curl.c8
-rw-r--r--block/sheepdog.c14
-rw-r--r--block/vmdk.c34
-rw-r--r--bsd-user/main.c6
-rwxr-xr-xconfigure60
-rw-r--r--default-configs/pci.mak1
-rw-r--r--default-configs/ppc-softmmu.mak2
-rw-r--r--default-configs/ppc64-softmmu.mak4
-rw-r--r--default-configs/ppcemb-softmmu.mak2
-rw-r--r--docs/tracing.txt2
-rw-r--r--hw/arm/boot.c7
-rw-r--r--hw/block/Makefile.objs1
-rw-r--r--hw/block/nvme.c885
-rw-r--r--hw/block/nvme.h711
-rw-r--r--hw/char/serial.c2
-rw-r--r--hw/microblaze/boot.c12
-rw-r--r--hw/ppc/ppc440_bamboo.c2
-rw-r--r--hw/ppc/spapr_vio.c6
-rw-r--r--hw/ppc/virtex_ml507.c18
-rw-r--r--include/hw/pci/pci_ids.h1
-rw-r--r--include/libfdt_env.h36
-rw-r--r--linux-user/main.c6
-rw-r--r--qapi-schema.json18
-rwxr-xr-xscripts/create_config15
-rwxr-xr-xscripts/tracetool.py18
-rw-r--r--tcg/ppc64/tcg-target.c29
-rw-r--r--ui/gtk.c4
-rw-r--r--vl.c2
31 files changed, 1747 insertions, 196 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 3412b07c59..a015f68123 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -609,6 +609,11 @@ S: Supported
F: hw/char/virtio-serial-bus.c
F: hw/char/virtio-console.c
+nvme
+M: Keith Busch <keith.busch@intel.com>
+S: Supported
+F: hw/block/nvme*
+
Xilinx EDK
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
diff --git a/Makefile.target b/Makefile.target
index b0be124d29..9a4985213b 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -15,14 +15,14 @@ QEMU_CFLAGS+=-I$(SRC_PATH)/include
ifdef CONFIG_USER_ONLY
# user emulator name
-QEMU_PROG=qemu-$(TARGET_ARCH2)
+QEMU_PROG=qemu-$(TARGET_NAME)
else
# system emulator name
ifneq (,$(findstring -mwindows,$(libs_softmmu)))
# Terminate program name with a 'w' because the linker builds a windows executable.
-QEMU_PROGW=qemu-system-$(TARGET_ARCH2)w$(EXESUF)
+QEMU_PROGW=qemu-system-$(TARGET_NAME)w$(EXESUF)
endif # windows executable
-QEMU_PROG=qemu-system-$(TARGET_ARCH2)$(EXESUF)
+QEMU_PROG=qemu-system-$(TARGET_NAME)$(EXESUF)
endif
PROGS=$(QEMU_PROG)
@@ -35,7 +35,7 @@ config-target.h: config-target.h-timestamp
config-target.h-timestamp: config-target.mak
ifdef CONFIG_TRACE_SYSTEMTAP
-stap: $(QEMU_PROG).stp
+stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp
ifdef CONFIG_USER_ONLY
TARGET_TYPE=user
@@ -43,14 +43,24 @@ else
TARGET_TYPE=system
endif
-$(QEMU_PROG).stp: $(SRC_PATH)/trace-events
+$(QEMU_PROG).stp-installed: $(SRC_PATH)/trace-events
$(call quiet-command,$(TRACETOOL) \
--format=stap \
--backend=$(TRACE_BACKEND) \
--binary=$(bindir)/$(QEMU_PROG) \
- --target-arch=$(TARGET_ARCH) \
+ --target-name=$(TARGET_NAME) \
+ --target-type=$(TARGET_TYPE) \
+ < $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG).stp-installed")
+
+$(QEMU_PROG).stp: $(SRC_PATH)/trace-events
+ $(call quiet-command,$(TRACETOOL) \
+ --format=stap \
+ --backend=$(TRACE_BACKEND) \
+ --binary=$(realpath .)/$(QEMU_PROG) \
+ --target-name=$(TARGET_NAME) \
--target-type=$(TARGET_TYPE) \
< $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG).stp")
+
else
stap:
endif
@@ -93,7 +103,7 @@ endif #CONFIG_LINUX_USER
ifdef CONFIG_BSD_USER
-QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH)
+QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ABI_DIR)
obj-y += bsd-user/
obj-y += gdbstub.o user-exec.o
@@ -118,7 +128,7 @@ obj-$(CONFIG_XEN) += xen-all.o xen-mapcache.o
obj-$(CONFIG_NO_XEN) += xen-stub.o
# Hardware support
-ifeq ($(TARGET_ARCH), sparc64)
+ifeq ($(TARGET_NAME), sparc64)
obj-y += hw/sparc64/
else
obj-y += hw/$(TARGET_BASE_ARCH)/
@@ -182,7 +192,7 @@ endif
endif
ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
- $(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
+ $(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
endif
GENERATED_HEADERS += config-target.h
diff --git a/arch_init.c b/arch_init.c
index 872020e062..a8b91eed7a 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -123,7 +123,7 @@ static struct defconfig_file {
bool userconfig;
} default_config_files[] = {
{ CONFIG_QEMU_CONFDIR "/qemu.conf", true },
- { CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf", true },
+ { CONFIG_QEMU_CONFDIR "/target-" TARGET_NAME ".conf", true },
{ NULL }, /* end of list */
};
@@ -1093,7 +1093,7 @@ TargetInfo *qmp_query_target(Error **errp)
{
TargetInfo *info = g_malloc0(sizeof(*info));
- info->arch = TARGET_TYPE;
+ info->arch = g_strdup(TARGET_NAME);
return info;
}
diff --git a/block/curl.c b/block/curl.c
index 4dc3b4b71b..6af8cb7577 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -406,6 +406,12 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags)
static int inited = 0;
+ if (flags & BDRV_O_RDWR) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR,
+ "curl block device does not support writes");
+ return -EROFS;
+ }
+
opts = qemu_opts_create_nofail(&runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
@@ -446,8 +452,6 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags)
if (curl_easy_perform(state->curl))
goto out;
curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
- curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
- curl_easy_setopt(state->curl, CURLOPT_NOBODY, 0);
if (d)
s->len = (size_t)d;
else if(!s->len)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 21a4edf15b..1b7c3f19b0 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -2063,7 +2063,7 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
if (snapid) {
tag[0] = 0;
} else {
- pstrcpy(tag, sizeof(tag), s->name);
+ pstrcpy(tag, sizeof(tag), snapshot_id);
}
ret = reload_inode(s, snapid, tag);
@@ -2071,14 +2071,11 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
goto out;
}
- if (!s->inode.vm_state_size) {
- error_report("Invalid snapshot");
- ret = -ENOENT;
+ ret = sd_create_branch(s);
+ if (ret) {
goto out;
}
- s->is_snapshot = true;
-
g_free(old_s);
return 0;
@@ -2196,8 +2193,9 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
int fd, ret = 0, remaining = size;
unsigned int data_len;
uint64_t vmstate_oid;
- uint32_t vdi_index;
uint64_t offset;
+ uint32_t vdi_index;
+ uint32_t vdi_id = load ? s->inode.parent_vdi_id : s->inode.vdi_id;
fd = connect_to_sdog(s);
if (fd < 0) {
@@ -2210,7 +2208,7 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
data_len = MIN(remaining, SD_DATA_OBJ_SIZE - offset);
- vmstate_oid = vid_to_vmstate_oid(s->inode.vdi_id, vdi_index);
+ vmstate_oid = vid_to_vmstate_oid(vdi_id, vdi_index);
create = (offset == 0);
if (load) {
diff --git a/block/vmdk.c b/block/vmdk.c
index 608daaf930..65ae0119c4 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -507,8 +507,11 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
if (ret < 0) {
return ret;
}
- if (header.capacity == 0 && header.desc_offset) {
- return vmdk_open_desc_file(bs, flags, header.desc_offset << 9);
+ if (header.capacity == 0) {
+ int64_t desc_offset = le64_to_cpu(header.desc_offset);
+ if (desc_offset) {
+ return vmdk_open_desc_file(bs, flags, desc_offset << 9);
+ }
}
if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) {
@@ -719,27 +722,40 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
int64_t desc_offset)
{
int ret;
- char buf[2048];
+ char *buf = NULL;
char ct[128];
BDRVVmdkState *s = bs->opaque;
+ int64_t size;
+
+ size = bdrv_getlength(bs->file);
+ if (size < 0) {
+ return -EINVAL;
+ }
+
+ size = MIN(size, 1 << 20); /* avoid unbounded allocation */
+ buf = g_malloc0(size + 1);
- ret = bdrv_pread(bs->file, desc_offset, buf, sizeof(buf));
+ ret = bdrv_pread(bs->file, desc_offset, buf, size);
if (ret < 0) {
- return ret;
+ goto exit;
}
- buf[2047] = '\0';
if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
- return -EMEDIUMTYPE;
+ ret = -EMEDIUMTYPE;
+ goto exit;
}
if (strcmp(ct, "monolithicFlat") &&
strcmp(ct, "twoGbMaxExtentSparse") &&
strcmp(ct, "twoGbMaxExtentFlat")) {
fprintf(stderr,
"VMDK: Not supported image type \"%s\""".\n", ct);
- return -ENOTSUP;
+ ret = -ENOTSUP;
+ goto exit;
}
s->desc_offset = 0;
- return vmdk_parse_extents(buf, bs, bs->file->filename);
+ ret = vmdk_parse_extents(buf, bs, bs->file->filename);
+exit:
+ g_free(buf);
+ return ret;
}
static int vmdk_open(BlockDriverState *bs, QDict *options, int flags)
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 0da3ab9e21..572f13afe4 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -670,8 +670,8 @@ void cpu_loop(CPUSPARCState *env)
static void usage(void)
{
- printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
- "usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
+ printf("qemu-" TARGET_NAME " version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
+ "usage: qemu-" TARGET_NAME " [options] program [arguments...]\n"
"BSD CPU emulator (compiled for %s emulation)\n"
"\n"
"Standard options:\n"
@@ -706,7 +706,7 @@ static void usage(void)
"Note that if you provide several changes to single variable\n"
"last change will stay in effect.\n"
,
- TARGET_ARCH,
+ TARGET_NAME,
interp_prefix,
x86_stack_size);
exit(1);
diff --git a/configure b/configure
index 60b0811706..ad32f87b8e 100755
--- a/configure
+++ b/configure
@@ -2486,9 +2486,31 @@ fi
##########################################
# fdt probe
+# fdt support is mandatory for at least some target architectures,
+# so insist on it if we're building those system emulators.
+fdt_required=no
+for target in $target_list; do
+ case $target in
+ arm*-softmmu|ppc*-softmmu|microblaze*-softmmu)
+ fdt_required=yes
+ ;;
+ esac
+done
+
+if test "$fdt_required" = "yes"; then
+ if test "$fdt" = "no"; then
+ error_exit "fdt disabled but some requested targets require it." \
+ "You can turn off fdt only if you also disable all the system emulation" \
+ "targets which need it (by specifying a cut down --target-list)."
+ fi
+ fdt=yes
+fi
+
if test "$fdt" != "no" ; then
fdt_libs="-lfdt"
+ # explicitly check for libfdt_env.h as it is missing in some stable installs
cat > $TMPC << EOF
+#include <libfdt_env.h>
int main(void) { return 0; }
EOF
if compile_prog "" "$fdt_libs" ; then
@@ -4083,10 +4105,10 @@ fi
for target in $target_list; do
target_dir="$target"
config_target_mak=$target_dir/config-target.mak
-target_arch2=`echo $target | cut -d '-' -f 1`
+target_name=`echo $target | cut -d '-' -f 1`
target_bigendian="no"
-case "$target_arch2" in
+case "$target_name" in
armeb|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or32|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb)
target_bigendian=yes
;;
@@ -4096,17 +4118,17 @@ target_user_only="no"
target_linux_user="no"
target_bsd_user="no"
case "$target" in
- ${target_arch2}-softmmu)
+ ${target_name}-softmmu)
target_softmmu="yes"
;;
- ${target_arch2}-linux-user)
+ ${target_name}-linux-user)
if test "$linux" != "yes" ; then
error_exit "Target '$target' is only available on a Linux host"
fi
target_user_only="yes"
target_linux_user="yes"
;;
- ${target_arch2}-bsd-user)
+ ${target_name}-bsd-user)
if test "$bsd" != "yes" ; then
error_exit "Target '$target' is only available on a BSD host"
fi
@@ -4124,14 +4146,14 @@ echo "# Automatically generated by configure - do not modify" > $config_target_m
bflt="no"
target_nptl="no"
-interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_arch2/g"`
+interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_name/g"`
gdb_xml_files=""
-TARGET_ARCH="$target_arch2"
+TARGET_ARCH="$target_name"
TARGET_BASE_ARCH=""
TARGET_ABI_DIR=""
-case "$target_arch2" in
+case "$target_name" in
i386)
;;
x86_64)
@@ -4243,17 +4265,15 @@ upper() {
echo "$@"| LC_ALL=C tr '[a-z]' '[A-Z]'
}
-echo "TARGET_ARCH=$TARGET_ARCH" >> $config_target_mak
target_arch_name="`upper $TARGET_ARCH`"
echo "TARGET_$target_arch_name=y" >> $config_target_mak
-echo "TARGET_ARCH2=$target_arch2" >> $config_target_mak
-echo "TARGET_TYPE=TARGET_TYPE_`upper $target_arch2`" >> $config_target_mak
+echo "TARGET_NAME=$target_name" >> $config_target_mak
echo "TARGET_BASE_ARCH=$TARGET_BASE_ARCH" >> $config_target_mak
if [ "$TARGET_ABI_DIR" = "" ]; then
TARGET_ABI_DIR=$TARGET_ARCH
fi
echo "TARGET_ABI_DIR=$TARGET_ABI_DIR" >> $config_target_mak
-case "$target_arch2" in
+case "$target_name" in
i386|x86_64)
if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then
echo "CONFIG_XEN=y" >> $config_target_mak
@@ -4264,17 +4284,17 @@ case "$target_arch2" in
;;
*)
esac
-case "$target_arch2" in
+case "$target_name" in
arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
# Make sure the target and host cpus are compatible
if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
- \( "$target_arch2" = "$cpu" -o \
- \( "$target_arch2" = "ppcemb" -a "$cpu" = "ppc" \) -o \
- \( "$target_arch2" = "ppc64" -a "$cpu" = "ppc" \) -o \
- \( "$target_arch2" = "ppc" -a "$cpu" = "ppc64" \) -o \
- \( "$target_arch2" = "ppcemb" -a "$cpu" = "ppc64" \) -o \
- \( "$target_arch2" = "x86_64" -a "$cpu" = "i386" \) -o \
- \( "$target_arch2" = "i386" -a "$cpu" = "x86_64" \) \) ; then
+ \( "$target_name" = "$cpu" -o \
+ \( "$target_name" = "ppcemb" -a "$cpu" = "ppc" \) -o \
+ \( "$target_name" = "ppc64" -a "$cpu" = "ppc" \) -o \
+ \( "$target_name" = "ppc" -a "$cpu" = "ppc64" \) -o \
+ \( "$target_name" = "ppcemb" -a "$cpu" = "ppc64" \) -o \
+ \( "$target_name" = "x86_64" -a "$cpu" = "i386" \) -o \
+ \( "$target_name" = "i386" -a "$cpu" = "x86_64" \) \) ; then
echo "CONFIG_KVM=y" >> $config_target_mak
if test "$vhost_net" = "yes" ; then
echo "CONFIG_VHOST_NET=y" >> $config_target_mak
diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index d557eabfb9..91b1e92da5 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -29,3 +29,4 @@ CONFIG_SERIAL_PCI=y
CONFIG_IPACK=y
CONFIG_WDT_IB6300ESB=y
CONFIG_PCI_TESTDEV=y
+CONFIG_NVME_PCI=y
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index cc3587f721..bcaa52fb10 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -42,6 +42,6 @@ CONFIG_I8259=y
CONFIG_XILINX=y
CONFIG_XILINX_ETHLITE=y
CONFIG_OPENPIC=y
-CONFIG_E500=$(CONFIG_FDT)
+CONFIG_E500=y
# For PReP
CONFIG_MC146818RTC=y
diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
index 884ea8a1cd..8b7874ea6a 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -42,8 +42,8 @@ CONFIG_I8259=y
CONFIG_XILINX=y
CONFIG_XILINX_ETHLITE=y
CONFIG_OPENPIC=y
-CONFIG_PSERIES=$(CONFIG_FDT)
-CONFIG_E500=$(CONFIG_FDT)
+CONFIG_PSERIES=y
+CONFIG_E500=y
# For pSeries
CONFIG_PCI_HOTPLUG=y
# For PReP
diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak
index be93e03ce2..61920ffe63 100644
--- a/default-configs/ppcemb-softmmu.mak
+++ b/default-configs/ppcemb-softmmu.mak
@@ -37,6 +37,6 @@ CONFIG_I8259=y
CONFIG_XILINX=y
CONFIG_XILINX_ETHLITE=y
CONFIG_OPENPIC=y
-CONFIG_E500=$(CONFIG_FDT)
+CONFIG_E500=y
# For PReP
CONFIG_MC146818RTC=y
diff --git a/docs/tracing.txt b/docs/tracing.txt
index 60ff9c5e6e..bfc261bcaf 100644
--- a/docs/tracing.txt
+++ b/docs/tracing.txt
@@ -225,7 +225,7 @@ probes:
scripts/tracetool --dtrace --stap \
--binary path/to/qemu-binary \
--target-type system \
- --target-arch x86_64 \
+ --target-name x86_64 \
<trace-events >qemu.stp
== Trace event properties ==
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index f451529fce..defcf15097 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -227,7 +227,6 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
{
-#ifdef CONFIG_FDT
uint32_t *mem_reg_property;
uint32_t mem_reg_propsize;
void *fdt = NULL;
@@ -308,12 +307,6 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
cpu_physical_memory_write(addr, fdt, size);
return 0;
-
-#else
- fprintf(stderr, "Device tree requested, "
- "but qemu was compiled without fdt support\n");
- return -1;
-#endif
}
static void do_cpu_reset(void *opaque)
diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs
index e4329a08a9..25acc6722d 100644
--- a/hw/block/Makefile.objs
+++ b/hw/block/Makefile.objs
@@ -8,6 +8,7 @@ common-obj-$(CONFIG_XEN_BACKEND) += xen_disk.o
common-obj-$(CONFIG_ECC) += ecc.o
common-obj-$(CONFIG_ONENAND) += onenand.o
common-obj-$(CONFIG_PC_SYSFW) += pc_sysfw.o
+common-obj-$(CONFIG_NVME_PCI) += nvme.o
obj-$(CONFIG_SH4) += tc58128.o
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
new file mode 100644
index 0000000000..5db941cc68
--- /dev/null
+++ b/hw/block/nvme.c
@@ -0,0 +1,885 @@
+/*
+ * QEMU NVM Express Controller
+ *
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Written by Keith Busch <keith.busch@intel.com>
+ *
+ * This code is licensed under the GNU GPL v2 or later.
+ */
+
+/**
+ * Reference Specs: http://www.nvmexpress.org, 1.1, 1.0e
+ *
+ * http://www.nvmexpress.org/resources/
+ */
+
+/**
+ * Usage: add options:
+ * -drive file=<file>,if=none,id=<drive_id>
+ * -device nvme,drive=<drive_id>,serial=<serial>,id=<id[optional]>
+ */
+
+#include <hw/block/block.h>
+#include <hw/hw.h>
+#include <hw/pci/msix.h>
+#include <hw/pci/pci.h>
+
+#include "nvme.h"
+
+static void nvme_process_sq(void *opaque);
+
+static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid)
+{
+ return sqid < n->num_queues && n->sq[sqid] != NULL ? 0 : -1;
+}
+
+static int nvme_check_cqid(NvmeCtrl *n, uint16_t cqid)
+{
+ return cqid < n->num_queues && n->cq[cqid] != NULL ? 0 : -1;
+}
+
+static void nvme_inc_cq_tail(NvmeCQueue *cq)
+{
+ cq->tail++;
+ if (cq->tail >= cq->size) {
+ cq->tail = 0;
+ cq->phase = !cq->phase;
+ }
+}
+
+static void nvme_inc_sq_head(NvmeSQueue *sq)
+{
+ sq->head = (sq->head + 1) % sq->size;
+}
+
+static uint8_t nvme_cq_full(NvmeCQueue *cq)
+{
+ return (cq->tail + 1) % cq->size == cq->head;
+}
+
+static uint8_t nvme_sq_empty(NvmeSQueue *sq)
+{
+ return sq->head == sq->tail;
+}
+
+static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
+{
+ if (cq->irq_enabled) {
+ if (msix_enabled(&(n->parent_obj))) {
+ msix_notify(&(n->parent_obj), cq->vector);
+ } else {
+ qemu_irq_pulse(n->parent_obj.irq[0]);
+ }
+ }
+}
+
+static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2,
+ uint32_t len, NvmeCtrl *n)
+{
+ hwaddr trans_len = n->page_size - (prp1 % n->page_size);
+ trans_len = MIN(len, trans_len);
+ int num_prps = (len >> n->page_bits) + 1;
+
+ if (!prp1) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ qemu_sglist_init(qsg, num_prps, pci_dma_context(&n->parent_obj));
+ qemu_sglist_add(qsg, prp1, trans_len);
+ len -= trans_len;
+ if (len) {
+ if (!prp2) {
+ goto unmap;
+ }
+ if (len > n->page_size) {
+ uint64_t prp_list[n->max_prp_ents];
+ uint32_t nents, prp_trans;
+ int i = 0;
+
+ nents = (len + n->page_size - 1) >> n->page_bits;
+ prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
+ pci_dma_read(&n->parent_obj, prp2, (void *)prp_list, prp_trans);
+ while (len != 0) {
+ uint64_t prp_ent = le64_to_cpu(prp_list[i]);
+
+ if (i == n->max_prp_ents - 1 && len > n->page_size) {
+ if (!prp_ent || prp_ent & (n->page_size - 1)) {
+ goto unmap;
+ }
+
+ i = 0;
+ nents = (len + n->page_size - 1) >> n->page_bits;
+ prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
+ pci_dma_read(&n->parent_obj, prp_ent, (void *)prp_list,
+ prp_trans);
+ prp_ent = le64_to_cpu(prp_list[i]);
+ }
+
+ if (!prp_ent || prp_ent & (n->page_size - 1)) {
+ goto unmap;
+ }
+
+ trans_len = MIN(len, n->page_size);
+ qemu_sglist_add(qsg, prp_ent, trans_len);
+ len -= trans_len;
+ i++;
+ }
+ } else {
+ if (prp2 & (n->page_size - 1)) {
+ goto unmap;
+ }
+ qemu_sglist_add(qsg, prp2, len);
+ }
+ }
+ return NVME_SUCCESS;
+
+ unmap:
+ qemu_sglist_destroy(qsg);
+ return NVME_INVALID_FIELD | NVME_DNR;
+}
+
+static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
+ uint64_t prp1, uint64_t prp2)
+{
+ QEMUSGList qsg;
+
+ if (nvme_map_prp(&qsg, prp1, prp2, len, n)) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ if (dma_buf_read(ptr, len, &qsg)) {
+ qemu_sglist_destroy(&qsg);
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ return NVME_SUCCESS;
+}
+
+static void nvme_post_cqes(void *opaque)
+{
+ NvmeCQueue *cq = opaque;
+ NvmeCtrl *n = cq->ctrl;
+ NvmeRequest *req, *next;
+
+ QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) {
+ NvmeSQueue *sq;
+ hwaddr addr;
+
+ if (nvme_cq_full(cq)) {
+ break;
+ }
+
+ QTAILQ_REMOVE(&cq->req_list, req, entry);
+ sq = req->sq;
+ req->cqe.status = cpu_to_le16((req->status << 1) | cq->phase);
+ req->cqe.sq_id = cpu_to_le16(sq->sqid);
+ req->cqe.sq_head = cpu_to_le16(sq->head);
+ addr = cq->dma_addr + cq->tail * n->cqe_size;
+ nvme_inc_cq_tail(cq);
+ pci_dma_write(&n->parent_obj, addr, (void *)&req->cqe,
+ sizeof(req->cqe));
+ QTAILQ_INSERT_TAIL(&sq->req_list, req, entry);
+ }
+ nvme_isr_notify(n, cq);
+}
+
+static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req)
+{
+ assert(cq->cqid == req->sq->cqid);
+ QTAILQ_REMOVE(&req->sq->out_req_list, req, entry);
+ QTAILQ_INSERT_TAIL(&cq->req_list, req, entry);
+ qemu_mod_timer(cq->timer, qemu_get_clock_ns(vm_clock) + 500);
+}
+
+static void nvme_rw_cb(void *opaque, int ret)
+{
+ NvmeRequest *req = opaque;
+ NvmeSQueue *sq = req->sq;
+ NvmeCtrl *n = sq->ctrl;
+ NvmeCQueue *cq = n->cq[sq->cqid];
+
+ bdrv_acct_done(n->conf.bs, &req->acct);
+ if (!ret) {
+ req->status = NVME_SUCCESS;
+ } else {
+ req->status = NVME_INTERNAL_DEV_ERROR;
+ }
+
+ qemu_sglist_destroy(&req->qsg);
+ nvme_enqueue_req_completion(cq, req);
+}
+
+static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
+ NvmeRequest *req)
+{
+ NvmeRwCmd *rw = (NvmeRwCmd *)cmd;
+ uint32_t nlb = le32_to_cpu(rw->nlb) + 1;
+ uint64_t slba = le64_to_cpu(rw->slba);
+ uint64_t prp1 = le64_to_cpu(rw->prp1);
+ uint64_t prp2 = le64_to_cpu(rw->prp2);
+
+ uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
+ uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds;
+ uint64_t data_size = nlb << data_shift;
+ uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
+ int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
+
+ if ((slba + nlb) > ns->id_ns.nsze) {
+ return NVME_LBA_RANGE | NVME_DNR;
+ }
+ if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ assert((nlb << data_shift) == req->qsg.size);
+
+ dma_acct_start(n->conf.bs, &req->acct, &req->qsg, is_write ?
+ BDRV_ACCT_WRITE : BDRV_ACCT_READ);
+ req->aiocb = is_write ?
+ dma_bdrv_write(n->conf.bs, &req->qsg, aio_slba, nvme_rw_cb, req) :
+ dma_bdrv_read(n->conf.bs, &req->qsg, aio_slba, nvme_rw_cb, req);
+
+ return NVME_NO_COMPLETE;
+}
+
+static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
+{
+ NvmeNamespace *ns;
+ uint32_t nsid = le32_to_cpu(cmd->nsid);
+
+ if (nsid == 0 || nsid > n->num_namespaces) {
+ return NVME_INVALID_NSID | NVME_DNR;
+ }
+
+ ns = &n->namespaces[nsid - 1];
+ switch (cmd->opcode) {
+ case NVME_CMD_FLUSH:
+ return NVME_SUCCESS;
+ case NVME_CMD_WRITE:
+ case NVME_CMD_READ:
+ return nvme_rw(n, ns, cmd, req);
+ default:
+ return NVME_INVALID_OPCODE | NVME_DNR;
+ }
+}
+
+static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n)
+{
+ n->sq[sq->sqid] = NULL;
+ qemu_del_timer(sq->timer);
+ qemu_free_timer(sq->timer);
+ g_free(sq->io_req);
+ if (sq->sqid) {
+ g_free(sq);
+ }
+}
+
+static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
+{
+ NvmeDeleteQ *c = (NvmeDeleteQ *)cmd;
+ NvmeRequest *req, *next;
+ NvmeSQueue *sq;
+ NvmeCQueue *cq;
+ uint16_t qid = le16_to_cpu(c->qid);
+
+ if (!qid || nvme_check_sqid(n, qid)) {
+ return NVME_INVALID_QID | NVME_DNR;
+ }
+
+ sq = n->sq[qid];
+ while (!QTAILQ_EMPTY(&sq->out_req_list)) {
+ req = QTAILQ_FIRST(&sq->out_req_list);
+ assert(req->aiocb);
+ bdrv_aio_cancel(req->aiocb);
+ }
+ if (!nvme_check_cqid(n, sq->cqid)) {
+ cq = n->cq[sq->cqid];
+ QTAILQ_REMOVE(&cq->sq_list, sq, entry);
+
+ nvme_post_cqes(cq);
+ QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) {
+ if (req->sq == sq) {
+ QTAILQ_REMOVE(&cq->req_list, req, entry);
+ QTAILQ_INSERT_TAIL(&sq->req_list, req, entry);
+ }
+ }
+ }
+
+ nvme_free_sq(sq, n);
+ return NVME_SUCCESS;
+}
+
+static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr,
+ uint16_t sqid, uint16_t cqid, uint16_t size)
+{
+ int i;
+ NvmeCQueue *cq;
+
+ sq->ctrl = n;
+ sq->dma_addr = dma_addr;
+ sq->sqid = sqid;
+ sq->size = size;
+ sq->cqid = cqid;
+ sq->head = sq->tail = 0;
+ sq->io_req = g_malloc(sq->size * sizeof(*sq->io_req));
+
+ QTAILQ_INIT(&sq->req_list);
+ QTAILQ_INIT(&sq->out_req_list);
+ for (i = 0; i < sq->size; i++) {
+ sq->io_req[i].sq = sq;
+ QTAILQ_INSERT_TAIL(&(sq->req_list), &sq->io_req[i], entry);
+ }
+ sq->timer = qemu_new_timer_ns(vm_clock, nvme_process_sq, sq);
+
+ assert(n->cq[cqid]);
+ cq = n->cq[cqid];
+ QTAILQ_INSERT_TAIL(&(cq->sq_list), sq, entry);
+ n->sq[sqid] = sq;
+}
+
+static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
+{
+ NvmeSQueue *sq;
+ NvmeCreateSq *c = (NvmeCreateSq *)cmd;
+
+ uint16_t cqid = le16_to_cpu(c->cqid);
+ uint16_t sqid = le16_to_cpu(c->sqid);
+ uint16_t qsize = le16_to_cpu(c->qsize);
+ uint16_t qflags = le16_to_cpu(c->sq_flags);
+ uint64_t prp1 = le64_to_cpu(c->prp1);
+
+ if (!cqid || nvme_check_cqid(n, cqid)) {
+ return NVME_INVALID_CQID | NVME_DNR;
+ }
+ if (!sqid || (sqid && !nvme_check_sqid(n, sqid))) {
+ return NVME_INVALID_QID | NVME_DNR;
+ }
+ if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
+ return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
+ }
+ if (!prp1 || prp1 & (n->page_size - 1)) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ if (!(NVME_SQ_FLAGS_PC(qflags))) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ sq = g_malloc0(sizeof(*sq));
+ nvme_init_sq(sq, n, prp1, sqid, cqid, qsize + 1);
+ return NVME_SUCCESS;
+}
+
+static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n)
+{
+ n->cq[cq->cqid] = NULL;
+ qemu_del_timer(cq->timer);
+ qemu_free_timer(cq->timer);
+ msix_vector_unuse(&n->parent_obj, cq->vector);
+ if (cq->cqid) {
+ g_free(cq);
+ }
+}
+
+static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
+{
+ NvmeDeleteQ *c = (NvmeDeleteQ *)cmd;
+ NvmeCQueue *cq;
+ uint16_t qid = le16_to_cpu(c->qid);
+
+ if (!qid || nvme_check_cqid(n, qid)) {
+ return NVME_INVALID_CQID | NVME_DNR;
+ }
+
+ cq = n->cq[qid];
+ if (!QTAILQ_EMPTY(&cq->sq_list)) {
+ return NVME_INVALID_QUEUE_DEL;
+ }
+ nvme_free_cq(cq, n);
+ return NVME_SUCCESS;
+}
+
+static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr,
+ uint16_t cqid, uint16_t vector, uint16_t size, uint16_t irq_enabled)
+{
+ cq->ctrl = n;
+ cq->cqid = cqid;
+ cq->size = size;
+ cq->dma_addr = dma_addr;
+ cq->phase = 1;
+ cq->irq_enabled = irq_enabled;
+ cq->vector = vector;
+ cq->head = cq->tail = 0;
+ QTAILQ_INIT(&cq->req_list);
+ QTAILQ_INIT(&cq->sq_list);
+ msix_vector_use(&n->parent_obj, cq->vector);
+ n->cq[cqid] = cq;
+ cq->timer = qemu_new_timer_ns(vm_clock, nvme_post_cqes, cq);
+}
+
+static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
+{
+ NvmeCQueue *cq;
+ NvmeCreateCq *c = (NvmeCreateCq *)cmd;
+ uint16_t cqid = le16_to_cpu(c->cqid);
+ uint16_t vector = le16_to_cpu(c->irq_vector);
+ uint16_t qsize = le16_to_cpu(c->qsize);
+ uint16_t qflags = le16_to_cpu(c->cq_flags);
+ uint64_t prp1 = le64_to_cpu(c->prp1);
+
+ if (!cqid || (cqid && !nvme_check_cqid(n, cqid))) {
+ return NVME_INVALID_CQID | NVME_DNR;
+ }
+ if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
+ return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
+ }
+ if (!prp1) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ if (vector > n->num_queues) {
+ return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
+ }
+ if (!(NVME_CQ_FLAGS_PC(qflags))) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ cq = g_malloc0(sizeof(*cq));
+ nvme_init_cq(cq, n, prp1, cqid, vector, qsize + 1,
+ NVME_CQ_FLAGS_IEN(qflags));
+ return NVME_SUCCESS;
+}
+
+static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
+{
+ NvmeNamespace *ns;
+ NvmeIdentify *c = (NvmeIdentify *)cmd;
+ uint32_t cns = le32_to_cpu(c->cns);
+ uint32_t nsid = le32_to_cpu(c->nsid);
+ uint64_t prp1 = le64_to_cpu(c->prp1);
+ uint64_t prp2 = le64_to_cpu(c->prp2);
+
+ if (cns) {
+ return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
+ prp1, prp2);
+ }
+ if (nsid == 0 || nsid > n->num_namespaces) {
+ return NVME_INVALID_NSID | NVME_DNR;
+ }
+
+ ns = &n->namespaces[nsid - 1];
+ return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
+ prp1, prp2);
+}
+
+static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
+{
+ uint32_t dw10 = le32_to_cpu(cmd->cdw10);
+
+ switch (dw10) {
+ case NVME_NUMBER_OF_QUEUES:
+ req->cqe.result = cpu_to_le32(n->num_queues);
+ break;
+ default:
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ return NVME_SUCCESS;
+}
+
+static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
+{
+ uint32_t dw10 = le32_to_cpu(cmd->cdw10);
+
+ switch (dw10) {
+ case NVME_NUMBER_OF_QUEUES:
+ req->cqe.result = cpu_to_le32(n->num_queues);
+ break;
+ default:
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+ return NVME_SUCCESS;
+}
+
+static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
+{
+ switch (cmd->opcode) {
+ case NVME_ADM_CMD_DELETE_SQ:
+ return nvme_del_sq(n, cmd);
+ case NVME_ADM_CMD_CREATE_SQ:
+ return nvme_create_sq(n, cmd);
+ case NVME_ADM_CMD_DELETE_CQ:
+ return nvme_del_cq(n, cmd);
+ case NVME_ADM_CMD_CREATE_CQ:
+ return nvme_create_cq(n, cmd);
+ case NVME_ADM_CMD_IDENTIFY:
+ return nvme_identify(n, cmd);
+ case NVME_ADM_CMD_SET_FEATURES:
+ return nvme_set_feature(n, cmd, req);
+ case NVME_ADM_CMD_GET_FEATURES:
+ return nvme_get_feature(n, cmd, req);
+ default:
+ return NVME_INVALID_OPCODE | NVME_DNR;
+ }
+}
+
+static void nvme_process_sq(void *opaque)
+{
+ NvmeSQueue *sq = opaque;
+ NvmeCtrl *n = sq->ctrl;
+ NvmeCQueue *cq = n->cq[sq->cqid];
+
+ uint16_t status;
+ hwaddr addr;
+ NvmeCmd cmd;
+ NvmeRequest *req;
+
+ while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) {
+ addr = sq->dma_addr + sq->head * n->sqe_size;
+ pci_dma_read(&n->parent_obj, addr, (void *)&cmd, sizeof(cmd));
+ nvme_inc_sq_head(sq);
+
+ req = QTAILQ_FIRST(&sq->req_list);
+ QTAILQ_REMOVE(&sq->req_list, req, entry);
+ QTAILQ_INSERT_TAIL(&sq->out_req_list, req, entry);
+ memset(&req->cqe, 0, sizeof(req->cqe));
+ req->cqe.cid = cmd.cid;
+
+ status = sq->sqid ? nvme_io_cmd(n, &cmd, req) :
+ nvme_admin_cmd(n, &cmd, req);
+ if (status != NVME_NO_COMPLETE) {
+ req->status = status;
+ nvme_enqueue_req_completion(cq, req);
+ }
+ }
+}
+
+static void nvme_clear_ctrl(NvmeCtrl *n)
+{
+ int i;
+
+ for (i = 0; i < n->num_queues; i++) {
+ if (n->sq[i] != NULL) {
+ nvme_free_sq(n->sq[i], n);
+ }
+ }
+ for (i = 0; i < n->num_queues; i++) {
+ if (n->cq[i] != NULL) {
+ nvme_free_cq(n->cq[i], n);
+ }
+ }
+
+ bdrv_flush(n->conf.bs);
+ n->bar.cc = 0;
+}
+
+static int nvme_start_ctrl(NvmeCtrl *n)
+{
+ uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
+ uint32_t page_size = 1 << page_bits;
+
+ if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
+ n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
+ NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
+ NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
+ NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
+ NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
+ NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
+ NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
+ !NVME_AQA_ASQS(n->bar.aqa) || NVME_AQA_ASQS(n->bar.aqa) > 4095 ||
+ !NVME_AQA_ACQS(n->bar.aqa) || NVME_AQA_ACQS(n->bar.aqa) > 4095) {
+ return -1;
+ }
+
+ n->page_bits = page_bits;
+ n->page_size = page_size;
+ n->max_prp_ents = n->page_size / sizeof(uint64_t);
+ n->cqe_size = 1 << NVME_CC_IOCQES(n->bar.cc);
+ n->sqe_size = 1 << NVME_CC_IOSQES(n->bar.cc);
+ nvme_init_cq(&n->admin_cq, n, n->bar.acq, 0, 0,
+ NVME_AQA_ACQS(n->bar.aqa) + 1, 1);
+ nvme_init_sq(&n->admin_sq, n, n->bar.asq, 0, 0,
+ NVME_AQA_ASQS(n->bar.aqa) + 1);
+
+ return 0;
+}
+
+static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
+ unsigned size)
+{
+ switch (offset) {
+ case 0xc:
+ n->bar.intms |= data & 0xffffffff;
+ n->bar.intmc = n->bar.intms;
+ break;
+ case 0x10:
+ n->bar.intms &= ~(data & 0xffffffff);
+ n->bar.intmc = n->bar.intms;
+ break;
+ case 0x14:
+ if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
+ n->bar.cc = data;
+ if (nvme_start_ctrl(n)) {
+ n->bar.csts = NVME_CSTS_FAILED;
+ } else {
+ n->bar.csts = NVME_CSTS_READY;
+ }
+ } else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
+ nvme_clear_ctrl(n);
+ n->bar.csts &= ~NVME_CSTS_READY;
+ }
+ if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
+ nvme_clear_ctrl(n);
+ n->bar.cc = data;
+ n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
+ } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
+ n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
+ n->bar.cc = data;
+ }
+ break;
+ case 0x24:
+ n->bar.aqa = data & 0xffffffff;
+ break;
+ case 0x28:
+ n->bar.asq = data;
+ break;
+ case 0x2c:
+ n->bar.asq |= data << 32;
+ break;
+ case 0x30:
+ n->bar.acq = data;
+ break;
+ case 0x34:
+ n->bar.acq |= data << 32;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+ NvmeCtrl *n = (NvmeCtrl *)opaque;
+ uint8_t *ptr = (uint8_t *)&n->bar;
+ uint64_t val = 0;
+
+ if (addr < sizeof(n->bar)) {
+ memcpy(&val, ptr + addr, size);
+ }
+ return val;
+}
+
+static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
+{
+ uint32_t qid;
+
+ if (addr & ((1 << 2) - 1)) {
+ return;
+ }
+
+ if (((addr - 0x1000) >> 2) & 1) {
+ uint16_t new_head = val & 0xffff;
+ int start_sqs;
+ NvmeCQueue *cq;
+
+ qid = (addr - (0x1000 + (1 << 2))) >> 3;
+ if (nvme_check_cqid(n, qid)) {
+ return;
+ }
+
+ cq = n->cq[qid];
+ if (new_head >= cq->size) {
+ return;
+ }
+
+ start_sqs = nvme_cq_full(cq) ? 1 : 0;
+ cq->head = new_head;
+ if (start_sqs) {
+ NvmeSQueue *sq;
+ QTAILQ_FOREACH(sq, &cq->sq_list, entry) {
+ qemu_mod_timer(sq->timer, qemu_get_clock_ns(vm_clock) + 500);
+ }
+ qemu_mod_timer(cq->timer, qemu_get_clock_ns(vm_clock) + 500);
+ }
+
+ if (cq->tail != cq->head) {
+ nvme_isr_notify(n, cq);
+ }
+ } else {
+ uint16_t new_tail = val & 0xffff;
+ NvmeSQueue *sq;
+
+ qid = (addr - 0x1000) >> 3;
+ if (nvme_check_sqid(n, qid)) {
+ return;
+ }
+
+ sq = n->sq[qid];
+ if (new_tail >= sq->size) {
+ return;
+ }
+
+ sq->tail = new_tail;
+ qemu_mod_timer(sq->timer, qemu_get_clock_ns(vm_clock) + 500);
+ }
+}
+
+static void nvme_mmio_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ NvmeCtrl *n = (NvmeCtrl *)opaque;
+ if (addr < sizeof(n->bar)) {
+ nvme_write_bar(n, addr, data, size);
+ } else if (addr >= 0x1000) {
+ nvme_process_db(n, addr, data);
+ }
+}
+
+static const MemoryRegionOps nvme_mmio_ops = {
+ .read = nvme_mmio_read,
+ .write = nvme_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 2,
+ .max_access_size = 8,
+ },
+};
+
+static int nvme_init(PCIDevice *pci_dev)
+{
+ NvmeCtrl *n = NVME(pci_dev);
+ NvmeIdCtrl *id = &n->id_ctrl;
+
+ int i;
+ int64_t bs_size;
+ uint8_t *pci_conf;
+
+ if (!(n->conf.bs)) {
+ return -1;
+ }
+
+ bs_size = bdrv_getlength(n->conf.bs);
+ if (bs_size <= 0) {
+ return -1;
+ }
+
+ blkconf_serial(&n->conf, &n->serial);
+ if (!n->serial) {
+ return -1;
+ }
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 1;
+ pci_config_set_prog_interface(pci_dev->config, 0x2);
+ pci_config_set_class(pci_dev->config, PCI_CLASS_STORAGE_EXPRESS);
+ pcie_endpoint_cap_init(&n->parent_obj, 0x80);
+
+ n->num_namespaces = 1;
+ n->num_queues = 64;
+ n->reg_size = 1 << qemu_fls(0x1004 + 2 * (n->num_queues + 1) * 4);
+ n->ns_size = bs_size / (uint64_t)n->num_namespaces;
+
+ n->namespaces = g_malloc0(sizeof(*n->namespaces)*n->num_namespaces);
+ n->sq = g_malloc0(sizeof(*n->sq)*n->num_queues);
+ n->cq = g_malloc0(sizeof(*n->cq)*n->num_queues);
+
+ memory_region_init_io(&n->iomem, &nvme_mmio_ops, n, "nvme", n->reg_size);
+ pci_register_bar(&n->parent_obj, 0,
+ PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64,
+ &n->iomem);
+ msix_init_exclusive_bar(&n->parent_obj, n->num_queues, 4);
+
+ id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID));
+ id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID));
+ strpadcpy((char *)id->mn, sizeof(id->mn), "QEMU NVMe Ctrl", ' ');
+ strpadcpy((char *)id->fr, sizeof(id->fr), "1.0", ' ');
+ strpadcpy((char *)id->sn, sizeof(id->sn), n->serial, ' ');
+ id->rab = 6;
+ id->ieee[0] = 0x00;
+ id->ieee[1] = 0x02;
+ id->ieee[2] = 0xb3;
+ id->oacs = cpu_to_le16(0);
+ id->frmw = 7 << 1;
+ id->lpa = 1 << 0;
+ id->sqes = (0x6 << 4) | 0x6;
+ id->cqes = (0x4 << 4) | 0x4;
+ id->nn = cpu_to_le32(n->num_namespaces);
+ id->psd[0].mp = cpu_to_le16(0x9c4);
+ id->psd[0].enlat = cpu_to_le32(0x10);
+ id->psd[0].exlat = cpu_to_le32(0x4);
+
+ n->bar.cap = 0;
+ NVME_CAP_SET_MQES(n->bar.cap, 0x7ff);
+ NVME_CAP_SET_CQR(n->bar.cap, 1);
+ NVME_CAP_SET_AMS(n->bar.cap, 1);
+ NVME_CAP_SET_TO(n->bar.cap, 0xf);
+ NVME_CAP_SET_CSS(n->bar.cap, 1);
+
+ n->bar.vs = 0x00010001;
+ n->bar.intmc = n->bar.intms = 0;
+
+ for (i = 0; i < n->num_namespaces; i++) {
+ NvmeNamespace *ns = &n->namespaces[i];
+ NvmeIdNs *id_ns = &ns->id_ns;
+ id_ns->nsfeat = 0;
+ id_ns->nlbaf = 0;
+ id_ns->flbas = 0;
+ id_ns->mc = 0;
+ id_ns->dpc = 0;
+ id_ns->dps = 0;
+ id_ns->lbaf[0].ds = BDRV_SECTOR_BITS;
+ id_ns->ncap = id_ns->nuse = id_ns->nsze =
+ cpu_to_le64(n->ns_size >>
+ id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds);
+ }
+ return 0;
+}
+
+static void nvme_exit(PCIDevice *pci_dev)
+{
+ NvmeCtrl *n = NVME(pci_dev);
+
+ nvme_clear_ctrl(n);
+ g_free(n->namespaces);
+ g_free(n->cq);
+ g_free(n->sq);
+ msix_uninit_exclusive_bar(pci_dev);
+ memory_region_destroy(&n->iomem);
+}
+
+static Property nvme_props[] = {
+ DEFINE_BLOCK_PROPERTIES(NvmeCtrl, conf),
+ DEFINE_PROP_STRING("serial", NvmeCtrl, serial),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription nvme_vmstate = {
+ .name = "nvme",
+ .unmigratable = 1,
+};
+
+static void nvme_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+
+ pc->init = nvme_init;
+ pc->exit = nvme_exit;
+ pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
+ pc->vendor_id = PCI_VENDOR_ID_INTEL;
+ pc->device_id = 0x5845;
+ pc->revision = 1;
+ pc->is_express = 1;
+
+ dc->desc = "Non-Volatile Memory Express";
+ dc->props = nvme_props;
+ dc->vmsd = &nvme_vmstate;
+}
+
+static const TypeInfo nvme_info = {
+ .name = "nvme",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(NvmeCtrl),
+ .class_init = nvme_class_init,
+};
+
+static void nvme_register_types(void)
+{
+ type_register_static(&nvme_info);
+}
+
+type_init(nvme_register_types)
diff --git a/hw/block/nvme.h b/hw/block/nvme.h
new file mode 100644
index 0000000000..bd8fc3e4bb
--- /dev/null
+++ b/hw/block/nvme.h
@@ -0,0 +1,711 @@
+#ifndef HW_NVME_H
+#define HW_NVME_H
+
+typedef struct NvmeBar {
+ uint64_t cap;
+ uint32_t vs;
+ uint32_t intms;
+ uint32_t intmc;
+ uint32_t cc;
+ uint32_t rsvd1;
+ uint32_t csts;
+ uint32_t nssrc;
+ uint32_t aqa;
+ uint64_t asq;
+ uint64_t acq;
+} NvmeBar;
+
+enum NvmeCapShift {
+ CAP_MQES_SHIFT = 0,
+ CAP_CQR_SHIFT = 16,
+ CAP_AMS_SHIFT = 17,
+ CAP_TO_SHIFT = 24,
+ CAP_DSTRD_SHIFT = 32,
+ CAP_NSSRS_SHIFT = 33,
+ CAP_CSS_SHIFT = 37,
+ CAP_MPSMIN_SHIFT = 48,
+ CAP_MPSMAX_SHIFT = 52,
+};
+
+enum NvmeCapMask {
+ CAP_MQES_MASK = 0xffff,
+ CAP_CQR_MASK = 0x1,
+ CAP_AMS_MASK = 0x3,
+ CAP_TO_MASK = 0xff,
+ CAP_DSTRD_MASK = 0xf,
+ CAP_NSSRS_MASK = 0x1,
+ CAP_CSS_MASK = 0xff,
+ CAP_MPSMIN_MASK = 0xf,
+ CAP_MPSMAX_MASK = 0xf,
+};
+
+#define NVME_CAP_MQES(cap) (((cap) >> CAP_MQES_SHIFT) & CAP_MQES_MASK)
+#define NVME_CAP_CQR(cap) (((cap) >> CAP_CQR_SHIFT) & CAP_CQR_MASK)
+#define NVME_CAP_AMS(cap) (((cap) >> CAP_AMS_SHIFT) & CAP_AMS_MASK)
+#define NVME_CAP_TO(cap) (((cap) >> CAP_TO_SHIFT) & CAP_TO_MASK)
+#define NVME_CAP_DSTRD(cap) (((cap) >> CAP_DSTRD_SHIFT) & CAP_DSTRD_MASK)
+#define NVME_CAP_NSSRS(cap) (((cap) >> CAP_NSSRS_SHIFT) & CAP_NSSRS_MASK)
+#define NVME_CAP_CSS(cap) (((cap) >> CAP_CSS_SHIFT) & CAP_CSS_MASK)
+#define NVME_CAP_MPSMIN(cap)(((cap) >> CAP_MPSMIN_SHIFT) & CAP_MPSMIN_MASK)
+#define NVME_CAP_MPSMAX(cap)(((cap) >> CAP_MPSMAX_SHIFT) & CAP_MPSMAX_MASK)
+
+#define NVME_CAP_SET_MQES(cap, val) (cap |= (uint64_t)(val & CAP_MQES_MASK) \
+ << CAP_MQES_SHIFT)
+#define NVME_CAP_SET_CQR(cap, val) (cap |= (uint64_t)(val & CAP_CQR_MASK) \
+ << CAP_CQR_SHIFT)
+#define NVME_CAP_SET_AMS(cap, val) (cap |= (uint64_t)(val & CAP_AMS_MASK) \
+ << CAP_AMS_SHIFT)
+#define NVME_CAP_SET_TO(cap, val) (cap |= (uint64_t)(val & CAP_TO_MASK) \
+ << CAP_TO_SHIFT)
+#define NVME_CAP_SET_DSTRD(cap, val) (cap |= (uint64_t)(val & CAP_DSTRD_MASK) \
+ << CAP_DSTRD_SHIFT)
+#define NVME_CAP_SET_NSSRS(cap, val) (cap |= (uint64_t)(val & CAP_NSSRS_MASK) \
+ << CAP_NSSRS_SHIFT)
+#define NVME_CAP_SET_CSS(cap, val) (cap |= (uint64_t)(val & CAP_CSS_MASK) \
+ << CAP_CSS_SHIFT)
+#define NVME_CAP_SET_MPSMIN(cap, val) (cap |= (uint64_t)(val & CAP_MPSMIN_MASK)\
+ << CAP_MPSMIN_SHIFT)
+#define NVME_CAP_SET_MPSMAX(cap, val) (cap |= (uint64_t)(val & CAP_MPSMAX_MASK)\
+ << CAP_MPSMAX_SHIFT)
+
+enum NvmeCcShift {
+ CC_EN_SHIFT = 0,
+ CC_CSS_SHIFT = 4,
+ CC_MPS_SHIFT = 7,
+ CC_AMS_SHIFT = 11,
+ CC_SHN_SHIFT = 14,
+ CC_IOSQES_SHIFT = 16,
+ CC_IOCQES_SHIFT = 20,
+};
+
+enum NvmeCcMask {
+ CC_EN_MASK = 0x1,
+ CC_CSS_MASK = 0x7,
+ CC_MPS_MASK = 0xf,
+ CC_AMS_MASK = 0x7,
+ CC_SHN_MASK = 0x3,
+ CC_IOSQES_MASK = 0xf,
+ CC_IOCQES_MASK = 0xf,
+};
+
+#define NVME_CC_EN(cc) ((cc >> CC_EN_SHIFT) & CC_EN_MASK)
+#define NVME_CC_CSS(cc) ((cc >> CC_CSS_SHIFT) & CC_CSS_MASK)
+#define NVME_CC_MPS(cc) ((cc >> CC_MPS_SHIFT) & CC_MPS_MASK)
+#define NVME_CC_AMS(cc) ((cc >> CC_AMS_SHIFT) & CC_AMS_MASK)
+#define NVME_CC_SHN(cc) ((cc >> CC_SHN_SHIFT) & CC_SHN_MASK)
+#define NVME_CC_IOSQES(cc) ((cc >> CC_IOSQES_SHIFT) & CC_IOSQES_MASK)
+#define NVME_CC_IOCQES(cc) ((cc >> CC_IOCQES_SHIFT) & CC_IOCQES_MASK)
+
+enum NvmeCstsShift {
+ CSTS_RDY_SHIFT = 0,
+ CSTS_CFS_SHIFT = 1,
+ CSTS_SHST_SHIFT = 2,
+ CSTS_NSSRO_SHIFT = 4,
+};
+
+enum NvmeCstsMask {
+ CSTS_RDY_MASK = 0x1,
+ CSTS_CFS_MASK = 0x1,
+ CSTS_SHST_MASK = 0x3,
+ CSTS_NSSRO_MASK = 0x1,
+};
+
+enum NvmeCsts {
+ NVME_CSTS_READY = 1 << CSTS_RDY_SHIFT,
+ NVME_CSTS_FAILED = 1 << CSTS_CFS_SHIFT,
+ NVME_CSTS_SHST_NORMAL = 0 << CSTS_SHST_SHIFT,
+ NVME_CSTS_SHST_PROGRESS = 1 << CSTS_SHST_SHIFT,
+ NVME_CSTS_SHST_COMPLETE = 2 << CSTS_SHST_SHIFT,
+ NVME_CSTS_NSSRO = 1 << CSTS_NSSRO_SHIFT,
+};
+
+#define NVME_CSTS_RDY(csts) ((csts >> CSTS_RDY_SHIFT) & CSTS_RDY_MASK)
+#define NVME_CSTS_CFS(csts) ((csts >> CSTS_CFS_SHIFT) & CSTS_CFS_MASK)
+#define NVME_CSTS_SHST(csts) ((csts >> CSTS_SHST_SHIFT) & CSTS_SHST_MASK)
+#define NVME_CSTS_NSSRO(csts) ((csts >> CSTS_NSSRO_SHIFT) & CSTS_NSSRO_MASK)
+
+enum NvmeAqaShift {
+ AQA_ASQS_SHIFT = 0,
+ AQA_ACQS_SHIFT = 16,
+};
+
+enum NvmeAqaMask {
+ AQA_ASQS_MASK = 0xfff,
+ AQA_ACQS_MASK = 0xfff,
+};
+
+#define NVME_AQA_ASQS(aqa) ((aqa >> AQA_ASQS_SHIFT) & AQA_ASQS_MASK)
+#define NVME_AQA_ACQS(aqa) ((aqa >> AQA_ACQS_SHIFT) & AQA_ACQS_MASK)
+
+typedef struct NvmeCmd {
+ uint8_t opcode;
+ uint8_t fuse;
+ uint16_t cid;
+ uint32_t nsid;
+ uint64_t res1;
+ uint64_t mptr;
+ uint64_t prp1;
+ uint64_t prp2;
+ uint32_t cdw10;
+ uint32_t cdw11;
+ uint32_t cdw12;
+ uint32_t cdw13;
+ uint32_t cdw14;
+ uint32_t cdw15;
+} NvmeCmd;
+
+enum NvmeAdminCommands {
+ NVME_ADM_CMD_DELETE_SQ = 0x00,
+ NVME_ADM_CMD_CREATE_SQ = 0x01,
+ NVME_ADM_CMD_GET_LOG_PAGE = 0x02,
+ NVME_ADM_CMD_DELETE_CQ = 0x04,
+ NVME_ADM_CMD_CREATE_CQ = 0x05,
+ NVME_ADM_CMD_IDENTIFY = 0x06,
+ NVME_ADM_CMD_ABORT = 0x08,
+ NVME_ADM_CMD_SET_FEATURES = 0x09,
+ NVME_ADM_CMD_GET_FEATURES = 0x0a,
+ NVME_ADM_CMD_ASYNC_EV_REQ = 0x0c,
+ NVME_ADM_CMD_ACTIVATE_FW = 0x10,
+ NVME_ADM_CMD_DOWNLOAD_FW = 0x11,
+ NVME_ADM_CMD_FORMAT_NVM = 0x80,
+ NVME_ADM_CMD_SECURITY_SEND = 0x81,
+ NVME_ADM_CMD_SECURITY_RECV = 0x82,
+};
+
+enum NvmeIoCommands {
+ NVME_CMD_FLUSH = 0x00,
+ NVME_CMD_WRITE = 0x01,
+ NVME_CMD_READ = 0x02,
+ NVME_CMD_WRITE_UNCOR = 0x04,
+ NVME_CMD_COMPARE = 0x05,
+ NVME_CMD_DSM = 0x09,
+};
+
+typedef struct NvmeDeleteQ {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t rsvd1[9];
+ uint16_t qid;
+ uint16_t rsvd10;
+ uint32_t rsvd11[5];
+} NvmeDeleteQ;
+
+typedef struct NvmeCreateCq {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t rsvd1[5];
+ uint64_t prp1;
+ uint64_t rsvd8;
+ uint16_t cqid;
+ uint16_t qsize;
+ uint16_t cq_flags;
+ uint16_t irq_vector;
+ uint32_t rsvd12[4];
+} NvmeCreateCq;
+
+#define NVME_CQ_FLAGS_PC(cq_flags) (cq_flags & 0x1)
+#define NVME_CQ_FLAGS_IEN(cq_flags) ((cq_flags >> 1) & 0x1)
+
+typedef struct NvmeCreateSq {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t rsvd1[5];
+ uint64_t prp1;
+ uint64_t rsvd8;
+ uint16_t sqid;
+ uint16_t qsize;
+ uint16_t sq_flags;
+ uint16_t cqid;
+ uint32_t rsvd12[4];
+} NvmeCreateSq;
+
+#define NVME_SQ_FLAGS_PC(sq_flags) (sq_flags & 0x1)
+#define NVME_SQ_FLAGS_QPRIO(sq_flags) ((sq_flags >> 1) & 0x3)
+
+enum NvmeQueueFlags {
+ NVME_Q_PC = 1,
+ NVME_Q_PRIO_URGENT = 0,
+ NVME_Q_PRIO_HIGH = 1,
+ NVME_Q_PRIO_NORMAL = 2,
+ NVME_Q_PRIO_LOW = 3,
+};
+
+typedef struct NvmeIdentify {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t nsid;
+ uint64_t rsvd2[2];
+ uint64_t prp1;
+ uint64_t prp2;
+ uint32_t cns;
+ uint32_t rsvd11[5];
+} NvmeIdentify;
+
+typedef struct NvmeRwCmd {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t nsid;
+ uint64_t rsvd2;
+ uint64_t mptr;
+ uint64_t prp1;
+ uint64_t prp2;
+ uint64_t slba;
+ uint16_t nlb;
+ uint16_t control;
+ uint32_t dsmgmt;
+ uint32_t reftag;
+ uint16_t apptag;
+ uint16_t appmask;
+} NvmeRwCmd;
+
+enum {
+ NVME_RW_LR = 1 << 15,
+ NVME_RW_FUA = 1 << 14,
+ NVME_RW_DSM_FREQ_UNSPEC = 0,
+ NVME_RW_DSM_FREQ_TYPICAL = 1,
+ NVME_RW_DSM_FREQ_RARE = 2,
+ NVME_RW_DSM_FREQ_READS = 3,
+ NVME_RW_DSM_FREQ_WRITES = 4,
+ NVME_RW_DSM_FREQ_RW = 5,
+ NVME_RW_DSM_FREQ_ONCE = 6,
+ NVME_RW_DSM_FREQ_PREFETCH = 7,
+ NVME_RW_DSM_FREQ_TEMP = 8,
+ NVME_RW_DSM_LATENCY_NONE = 0 << 4,
+ NVME_RW_DSM_LATENCY_IDLE = 1 << 4,
+ NVME_RW_DSM_LATENCY_NORM = 2 << 4,
+ NVME_RW_DSM_LATENCY_LOW = 3 << 4,
+ NVME_RW_DSM_SEQ_REQ = 1 << 6,
+ NVME_RW_DSM_COMPRESSED = 1 << 7,
+ NVME_RW_PRINFO_PRACT = 1 << 13,
+ NVME_RW_PRINFO_PRCHK_GUARD = 1 << 12,
+ NVME_RW_PRINFO_PRCHK_APP = 1 << 11,
+ NVME_RW_PRINFO_PRCHK_REF = 1 << 10,
+};
+
+typedef struct NvmeDsmCmd {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t nsid;
+ uint64_t rsvd2[2];
+ uint64_t prp1;
+ uint64_t prp2;
+ uint32_t nr;
+ uint32_t attributes;
+ uint32_t rsvd12[4];
+} NvmeDsmCmd;
+
+enum {
+ NVME_DSMGMT_IDR = 1 << 0,
+ NVME_DSMGMT_IDW = 1 << 1,
+ NVME_DSMGMT_AD = 1 << 2,
+};
+
+typedef struct NvmeDsmRange {
+ uint32_t cattr;
+ uint32_t nlb;
+ uint64_t slba;
+} NvmeDsmRange;
+
+enum NvmeAsyncEventRequest {
+ NVME_AER_TYPE_ERROR = 0,
+ NVME_AER_TYPE_SMART = 1,
+ NVME_AER_TYPE_IO_SPECIFIC = 6,
+ NVME_AER_TYPE_VENDOR_SPECIFIC = 7,
+ NVME_AER_INFO_ERR_INVALID_SQ = 0,
+ NVME_AER_INFO_ERR_INVALID_DB = 1,
+ NVME_AER_INFO_ERR_DIAG_FAIL = 2,
+ NVME_AER_INFO_ERR_PERS_INTERNAL_ERR = 3,
+ NVME_AER_INFO_ERR_TRANS_INTERNAL_ERR = 4,
+ NVME_AER_INFO_ERR_FW_IMG_LOAD_ERR = 5,
+ NVME_AER_INFO_SMART_RELIABILITY = 0,
+ NVME_AER_INFO_SMART_TEMP_THRESH = 1,
+ NVME_AER_INFO_SMART_SPARE_THRESH = 2,
+};
+
+typedef struct NvmeAerResult {
+ uint8_t event_type;
+ uint8_t event_info;
+ uint8_t log_page;
+ uint8_t resv;
+} NvmeAerResult;
+
+typedef struct NvmeCqe {
+ uint32_t result;
+ uint32_t rsvd;
+ uint16_t sq_head;
+ uint16_t sq_id;
+ uint16_t cid;
+ uint16_t status;
+} NvmeCqe;
+
+enum NvmeStatusCodes {
+ NVME_SUCCESS = 0x0000,
+ NVME_INVALID_OPCODE = 0x0001,
+ NVME_INVALID_FIELD = 0x0002,
+ NVME_CID_CONFLICT = 0x0003,
+ NVME_DATA_TRAS_ERROR = 0x0004,
+ NVME_POWER_LOSS_ABORT = 0x0005,
+ NVME_INTERNAL_DEV_ERROR = 0x0006,
+ NVME_CMD_ABORT_REQ = 0x0007,
+ NVME_CMD_ABORT_SQ_DEL = 0x0008,
+ NVME_CMD_ABORT_FAILED_FUSE = 0x0009,
+ NVME_CMD_ABORT_MISSING_FUSE = 0x000a,
+ NVME_INVALID_NSID = 0x000b,
+ NVME_CMD_SEQ_ERROR = 0x000c,
+ NVME_LBA_RANGE = 0x0080,
+ NVME_CAP_EXCEEDED = 0x0081,
+ NVME_NS_NOT_READY = 0x0082,
+ NVME_NS_RESV_CONFLICT = 0x0083,
+ NVME_INVALID_CQID = 0x0100,
+ NVME_INVALID_QID = 0x0101,
+ NVME_MAX_QSIZE_EXCEEDED = 0x0102,
+ NVME_ACL_EXCEEDED = 0x0103,
+ NVME_RESERVED = 0x0104,
+ NVME_AER_LIMIT_EXCEEDED = 0x0105,
+ NVME_INVALID_FW_SLOT = 0x0106,
+ NVME_INVALID_FW_IMAGE = 0x0107,
+ NVME_INVALID_IRQ_VECTOR = 0x0108,
+ NVME_INVALID_LOG_ID = 0x0109,
+ NVME_INVALID_FORMAT = 0x010a,
+ NVME_FW_REQ_RESET = 0x010b,
+ NVME_INVALID_QUEUE_DEL = 0x010c,
+ NVME_FID_NOT_SAVEABLE = 0x010d,
+ NVME_FID_NOT_NSID_SPEC = 0x010f,
+ NVME_FW_REQ_SUSYSTEM_RESET = 0x0110,
+ NVME_CONFLICTING_ATTRS = 0x0180,
+ NVME_INVALID_PROT_INFO = 0x0181,
+ NVME_WRITE_TO_RO = 0x0182,
+ NVME_WRITE_FAULT = 0x0280,
+ NVME_UNRECOVERED_READ = 0x0281,
+ NVME_E2E_GUARD_ERROR = 0x0282,
+ NVME_E2E_APP_ERROR = 0x0283,
+ NVME_E2E_REF_ERROR = 0x0284,
+ NVME_CMP_FAILURE = 0x0285,
+ NVME_ACCESS_DENIED = 0x0286,
+ NVME_MORE = 0x2000,
+ NVME_DNR = 0x4000,
+ NVME_NO_COMPLETE = 0xffff,
+};
+
+typedef struct NvmeFwSlotInfoLog {
+ uint8_t afi;
+ uint8_t reserved1[7];
+ uint8_t frs1[8];
+ uint8_t frs2[8];
+ uint8_t frs3[8];
+ uint8_t frs4[8];
+ uint8_t frs5[8];
+ uint8_t frs6[8];
+ uint8_t frs7[8];
+ uint8_t reserved2[448];
+} NvmeFwSlotInfoLog;
+
+typedef struct NvmeErrorLog {
+ uint64_t error_count;
+ uint16_t sqid;
+ uint16_t cid;
+ uint16_t status_field;
+ uint16_t param_error_location;
+ uint64_t lba;
+ uint32_t nsid;
+ uint8_t vs;
+ uint8_t resv[35];
+} NvmeErrorLog;
+
+typedef struct NvmeSmartLog {
+ uint8_t critical_warning;
+ uint8_t temperature[2];
+ uint8_t available_spare;
+ uint8_t available_spare_threshold;
+ uint8_t percentage_used;
+ uint8_t reserved1[26];
+ uint64_t data_units_read[2];
+ uint64_t data_units_written[2];
+ uint64_t host_read_commands[2];
+ uint64_t host_write_commands[2];
+ uint64_t controller_busy_time[2];
+ uint64_t power_cycles[2];
+ uint64_t power_on_hours[2];
+ uint64_t unsafe_shutdowns[2];
+ uint64_t media_errors[2];
+ uint64_t number_of_error_log_entries[2];
+ uint8_t reserved2[320];
+} NvmeSmartLog;
+
+enum NvmeSmartWarn {
+ NVME_SMART_SPARE = 1 << 0,
+ NVME_SMART_TEMPERATURE = 1 << 1,
+ NVME_SMART_RELIABILITY = 1 << 2,
+ NVME_SMART_MEDIA_READ_ONLY = 1 << 3,
+ NVME_SMART_FAILED_VOLATILE_MEDIA = 1 << 4,
+};
+
+enum LogIdentifier {
+ NVME_LOG_ERROR_INFO = 0x01,
+ NVME_LOG_SMART_INFO = 0x02,
+ NVME_LOG_FW_SLOT_INFO = 0x03,
+};
+
+typedef struct NvmePSD {
+ uint16_t mp;
+ uint16_t reserved;
+ uint32_t enlat;
+ uint32_t exlat;
+ uint8_t rrt;
+ uint8_t rrl;
+ uint8_t rwt;
+ uint8_t rwl;
+ uint8_t resv[16];
+} NvmePSD;
+
+typedef struct NvmeIdCtrl {
+ uint16_t vid;
+ uint16_t ssvid;
+ uint8_t sn[20];
+ uint8_t mn[40];
+ uint8_t fr[8];
+ uint8_t rab;
+ uint8_t ieee[3];
+ uint8_t cmic;
+ uint8_t mdts;
+ uint8_t rsvd255[178];
+ uint16_t oacs;
+ uint8_t acl;
+ uint8_t aerl;
+ uint8_t frmw;
+ uint8_t lpa;
+ uint8_t elpe;
+ uint8_t npss;
+ uint8_t rsvd511[248];
+ uint8_t sqes;
+ uint8_t cqes;
+ uint16_t rsvd515;
+ uint32_t nn;
+ uint16_t oncs;
+ uint16_t fuses;
+ uint8_t fna;
+ uint8_t vwc;
+ uint16_t awun;
+ uint16_t awupf;
+ uint8_t rsvd703[174];
+ uint8_t rsvd2047[1344];
+ NvmePSD psd[32];
+ uint8_t vs[1024];
+} NvmeIdCtrl;
+
+enum NvmeIdCtrlOacs {
+ NVME_OACS_SECURITY = 1 << 0,
+ NVME_OACS_FORMAT = 1 << 1,
+ NVME_OACS_FW = 1 << 2,
+};
+
+enum NvmeIdCtrlOncs {
+ NVME_ONCS_COMPARE = 1 << 0,
+ NVME_ONCS_WRITE_UNCORR = 1 << 1,
+ NVME_ONCS_DSM = 1 << 2,
+ NVME_ONCS_WRITE_ZEROS = 1 << 3,
+ NVME_ONCS_FEATURES = 1 << 4,
+ NVME_ONCS_RESRVATIONS = 1 << 5,
+};
+
+#define NVME_CTRL_SQES_MIN(sqes) ((sqes) & 0xf)
+#define NVME_CTRL_SQES_MAX(sqes) (((sqes) >> 4) & 0xf)
+#define NVME_CTRL_CQES_MIN(cqes) ((cqes) & 0xf)
+#define NVME_CTRL_CQES_MAX(cqes) (((cqes) >> 4) & 0xf)
+
+typedef struct NvmeFeatureVal {
+ uint32_t arbitration;
+ uint32_t power_mgmt;
+ uint32_t temp_thresh;
+ uint32_t err_rec;
+ uint32_t volatile_wc;
+ uint32_t num_queues;
+ uint32_t int_coalescing;
+ uint32_t *int_vector_config;
+ uint32_t write_atomicity;
+ uint32_t async_config;
+ uint32_t sw_prog_marker;
+} NvmeFeatureVal;
+
+#define NVME_ARB_AB(arb) (arb & 0x7)
+#define NVME_ARB_LPW(arb) ((arb >> 8) & 0xff)
+#define NVME_ARB_MPW(arb) ((arb >> 16) & 0xff)
+#define NVME_ARB_HPW(arb) ((arb >> 24) & 0xff)
+
+#define NVME_INTC_THR(intc) (intc & 0xff)
+#define NVME_INTC_TIME(intc) ((intc >> 8) & 0xff)
+
+enum NvmeFeatureIds {
+ NVME_ARBITRATION = 0x1,
+ NVME_POWER_MANAGEMENT = 0x2,
+ NVME_LBA_RANGE_TYPE = 0x3,
+ NVME_TEMPERATURE_THRESHOLD = 0x4,
+ NVME_ERROR_RECOVERY = 0x5,
+ NVME_VOLATILE_WRITE_CACHE = 0x6,
+ NVME_NUMBER_OF_QUEUES = 0x7,
+ NVME_INTERRUPT_COALESCING = 0x8,
+ NVME_INTERRUPT_VECTOR_CONF = 0x9,
+ NVME_WRITE_ATOMICITY = 0xa,
+ NVME_ASYNCHRONOUS_EVENT_CONF = 0xb,
+ NVME_SOFTWARE_PROGRESS_MARKER = 0x80
+};
+
+typedef struct NvmeRangeType {
+ uint8_t type;
+ uint8_t attributes;
+ uint8_t rsvd2[14];
+ uint64_t slba;
+ uint64_t nlb;
+ uint8_t guid[16];
+ uint8_t rsvd48[16];
+} NvmeRangeType;
+
+typedef struct NvmeLBAF {
+ uint16_t ms;
+ uint8_t ds;
+ uint8_t rp;
+} NvmeLBAF;
+
+typedef struct NvmeIdNs {
+ uint64_t nsze;
+ uint64_t ncap;
+ uint64_t nuse;
+ uint8_t nsfeat;
+ uint8_t nlbaf;
+ uint8_t flbas;
+ uint8_t mc;
+ uint8_t dpc;
+ uint8_t dps;
+ uint8_t res30[98];
+ NvmeLBAF lbaf[16];
+ uint8_t res192[192];
+ uint8_t vs[3712];
+} NvmeIdNs;
+
+#define NVME_ID_NS_NSFEAT_THIN(nsfeat) ((nsfeat & 0x1))
+#define NVME_ID_NS_FLBAS_EXTENDED(flbas) ((flbas >> 4) & 0x1)
+#define NVME_ID_NS_FLBAS_INDEX(flbas) ((flbas & 0xf))
+#define NVME_ID_NS_MC_SEPARATE(mc) ((mc >> 1) & 0x1)
+#define NVME_ID_NS_MC_EXTENDED(mc) ((mc & 0x1))
+#define NVME_ID_NS_DPC_LAST_EIGHT(dpc) ((dpc >> 4) & 0x1)
+#define NVME_ID_NS_DPC_FIRST_EIGHT(dpc) ((dpc >> 3) & 0x1)
+#define NVME_ID_NS_DPC_TYPE_3(dpc) ((dpc >> 2) & 0x1)
+#define NVME_ID_NS_DPC_TYPE_2(dpc) ((dpc >> 1) & 0x1)
+#define NVME_ID_NS_DPC_TYPE_1(dpc) ((dpc & 0x1))
+#define NVME_ID_NS_DPC_TYPE_MASK 0x7
+
+enum NvmeIdNsDps {
+ DPS_TYPE_NONE = 0,
+ DPS_TYPE_1 = 1,
+ DPS_TYPE_2 = 2,
+ DPS_TYPE_3 = 3,
+ DPS_TYPE_MASK = 0x7,
+ DPS_FIRST_EIGHT = 8,
+};
+
+static inline void _nvme_check_size(void)
+{
+ QEMU_BUILD_BUG_ON(sizeof(NvmeAerResult) != 4);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeCqe) != 16);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeDsmRange) != 16);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeCmd) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeDeleteQ) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeCreateCq) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeCreateSq) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeIdentify) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeRwCmd) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeDsmCmd) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeRangeType) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) != 512);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrl) != 4096);
+ QEMU_BUILD_BUG_ON(sizeof(NvmeIdNs) != 4096);
+}
+
+typedef struct NvmeAsyncEvent {
+ QSIMPLEQ_ENTRY(NvmeAsyncEvent) entry;
+ NvmeAerResult result;
+} NvmeAsyncEvent;
+
+typedef struct NvmeRequest {
+ struct NvmeSQueue *sq;
+ BlockDriverAIOCB *aiocb;
+ uint16_t status;
+ NvmeCqe cqe;
+ BlockAcctCookie acct;
+ QEMUSGList qsg;
+ QTAILQ_ENTRY(NvmeRequest)entry;
+} NvmeRequest;
+
+typedef struct NvmeSQueue {
+ struct NvmeCtrl *ctrl;
+ uint16_t sqid;
+ uint16_t cqid;
+ uint32_t head;
+ uint32_t tail;
+ uint32_t size;
+ uint64_t dma_addr;
+ QEMUTimer *timer;
+ NvmeRequest *io_req;
+ QTAILQ_HEAD(sq_req_list, NvmeRequest) req_list;
+ QTAILQ_HEAD(out_req_list, NvmeRequest) out_req_list;
+ QTAILQ_ENTRY(NvmeSQueue) entry;
+} NvmeSQueue;
+
+typedef struct NvmeCQueue {
+ struct NvmeCtrl *ctrl;
+ uint8_t phase;
+ uint16_t cqid;
+ uint16_t irq_enabled;
+ uint32_t head;
+ uint32_t tail;
+ uint32_t vector;
+ uint32_t size;
+ uint64_t dma_addr;
+ QEMUTimer *timer;
+ QTAILQ_HEAD(sq_list, NvmeSQueue) sq_list;
+ QTAILQ_HEAD(cq_req_list, NvmeRequest) req_list;
+} NvmeCQueue;
+
+typedef struct NvmeNamespace {
+ NvmeIdNs id_ns;
+} NvmeNamespace;
+
+#define TYPE_NVME "nvme"
+#define NVME(obj) \
+ OBJECT_CHECK(NvmeCtrl, (obj), TYPE_NVME)
+
+typedef struct NvmeCtrl {
+ PCIDevice parent_obj;
+ MemoryRegion iomem;
+ NvmeBar bar;
+ BlockConf conf;
+
+ uint16_t page_size;
+ uint16_t page_bits;
+ uint16_t max_prp_ents;
+ uint16_t cqe_size;
+ uint16_t sqe_size;
+ uint32_t reg_size;
+ uint32_t num_namespaces;
+ uint32_t num_queues;
+ uint32_t max_q_ents;
+ uint64_t ns_size;
+
+ char *serial;
+ NvmeNamespace *namespaces;
+ NvmeSQueue **sq;
+ NvmeCQueue **cq;
+ NvmeSQueue admin_sq;
+ NvmeCQueue admin_cq;
+ NvmeIdCtrl id_ctrl;
+} NvmeCtrl;
+
+#endif /* HW_NVME_H */
diff --git a/hw/char/serial.c b/hw/char/serial.c
index b537e426b0..6382f98389 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -424,7 +424,7 @@ static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size)
ret = s->divider & 0xff;
} else {
if(s->fcr & UART_FCR_FE) {
- ret = fifo8_is_full(&s->recv_fifo) ?
+ ret = fifo8_is_empty(&s->recv_fifo) ?
0 : fifo8_pop(&s->recv_fifo);
if (s->recv_fifo.num == 0) {
s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c
index e543d886b8..3f1d70ef9b 100644
--- a/hw/microblaze/boot.c
+++ b/hw/microblaze/boot.c
@@ -61,7 +61,6 @@ static int microblaze_load_dtb(hwaddr addr,
const char *dtb_filename)
{
int fdt_size;
-#ifdef CONFIG_FDT
void *fdt = NULL;
int r;
@@ -81,17 +80,6 @@ static int microblaze_load_dtb(hwaddr addr,
}
cpu_physical_memory_write(addr, fdt, fdt_size);
-#else
- /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
- to the kernel. */
- if (dtb_filename) {
- fdt_size = load_image_targphys(dtb_filename, addr, 0x10000);
- }
- if (kernel_cmdline) {
- fprintf(stderr,
- "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
- }
-#endif
return fdt_size;
}
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index a55e7170cc..b0c1c027d4 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -58,7 +58,6 @@ static int bamboo_load_device_tree(hwaddr addr,
const char *kernel_cmdline)
{
int ret = -1;
-#ifdef CONFIG_FDT
uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) };
char *filename;
int fdt_size;
@@ -115,7 +114,6 @@ static int bamboo_load_device_tree(hwaddr addr,
g_free(fdt);
out:
-#endif
return ret;
}
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index 1405c3202c..304f3168f7 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -34,9 +34,7 @@
#include "hw/ppc/spapr_vio.h"
#include "hw/ppc/xics.h"
-#ifdef CONFIG_FDT
#include <libfdt.h>
-#endif /* CONFIG_FDT */
/* #define DEBUG_SPAPR */
@@ -94,7 +92,6 @@ VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
return NULL;
}
-#ifdef CONFIG_FDT
static int vio_make_devnode(VIOsPAPRDevice *dev,
void *fdt)
{
@@ -159,7 +156,6 @@ static int vio_make_devnode(VIOsPAPRDevice *dev,
return node_off;
}
-#endif /* CONFIG_FDT */
/*
* CRQ handling
@@ -570,7 +566,6 @@ static void spapr_vio_register_types(void)
type_init(spapr_vio_register_types)
-#ifdef CONFIG_FDT
static int compare_reg(const void *p1, const void *p2)
{
VIOsPAPRDevice const *dev1, *dev2;
@@ -655,4 +650,3 @@ int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus)
return ret;
}
-#endif /* CONFIG_FDT */
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index 1b4ce760e6..709a707243 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -141,7 +141,6 @@ static int xilinx_load_device_tree(hwaddr addr,
{
char *path;
int fdt_size;
-#ifdef CONFIG_FDT
void *fdt;
int r;
@@ -162,23 +161,6 @@ static int xilinx_load_device_tree(hwaddr addr,
if (r < 0)
fprintf(stderr, "couldn't set /chosen/bootargs\n");
cpu_physical_memory_write(addr, fdt, fdt_size);
-#else
- /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
- to the kernel. */
- fdt_size = load_image_targphys("ppc.dtb", addr, 0x10000);
- if (fdt_size < 0) {
- path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
- if (path) {
- fdt_size = load_image_targphys(path, addr, 0x10000);
- g_free(path);
- }
- }
-
- if (kernel_cmdline) {
- fprintf(stderr,
- "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
- }
-#endif
return fdt_size;
}
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index d8dc2f1bf7..08f8161524 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -19,6 +19,7 @@
#define PCI_CLASS_STORAGE_IDE 0x0101
#define PCI_CLASS_STORAGE_RAID 0x0104
#define PCI_CLASS_STORAGE_SATA 0x0106
+#define PCI_CLASS_STORAGE_EXPRESS 0x0108
#define PCI_CLASS_STORAGE_OTHER 0x0180
#define PCI_CLASS_NETWORK_ETHERNET 0x0200
diff --git a/include/libfdt_env.h b/include/libfdt_env.h
deleted file mode 100644
index 3667d4cb3a..0000000000
--- a/include/libfdt_env.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * 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/>.
- *
- * Copyright IBM Corp. 2008
- * Authors: Hollis Blanchard <hollisb@us.ibm.com>
- *
- */
-
-#ifndef _LIBFDT_ENV_H
-#define _LIBFDT_ENV_H
-
-#include "qemu/bswap.h"
-
-#ifdef HOST_WORDS_BIGENDIAN
-#define fdt32_to_cpu(x) (x)
-#define cpu_to_fdt32(x) (x)
-#define fdt64_to_cpu(x) (x)
-#define cpu_to_fdt64(x) (x)
-#else
-#define fdt32_to_cpu(x) bswap32(x)
-#define cpu_to_fdt32(x) bswap32(x)
-#define fdt64_to_cpu(x) bswap64(x)
-#define cpu_to_fdt64(x) bswap64(x)
-#endif
-
-#endif /* _LIBFDT_ENV_H */
diff --git a/linux-user/main.c b/linux-user/main.c
index b97b8cfd33..21725a4971 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -3339,7 +3339,7 @@ static void handle_arg_strace(const char *arg)
static void handle_arg_version(const char *arg)
{
- printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION
+ printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION
", Copyright (c) 2003-2008 Fabrice Bellard\n");
exit(0);
}
@@ -3400,8 +3400,8 @@ static void usage(void)
int maxarglen;
int maxenvlen;
- printf("usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
- "Linux CPU emulator (compiled for " TARGET_ARCH " emulation)\n"
+ printf("usage: qemu-" TARGET_NAME " [options] program [arguments...]\n"
+ "Linux CPU emulator (compiled for " TARGET_NAME " emulation)\n"
"\n"
"Options and associated environment variables:\n"
"\n");
diff --git a/qapi-schema.json b/qapi-schema.json
index 5ad6894738..a80ee405d1 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3013,22 +3013,6 @@
{ 'command': 'query-fdsets', 'returns': ['FdsetInfo'] }
##
-# @TargetType
-#
-# Target CPU emulation type
-#
-# These parameters correspond to the softmmu binary CPU name that is currently
-# running.
-#
-# Since: 1.2.0
-##
-{ 'enum': 'TargetType',
- 'data': [ 'alpha', 'arm', 'cris', 'i386', 'lm32', 'm68k', 'microblazeel',
- 'microblaze', 'mips64el', 'mips64', 'mipsel', 'mips', 'moxie',
- 'or32', 'ppc64', 'ppcemb', 'ppc', 's390x', 'sh4eb', 'sh4',
- 'sparc64', 'sparc', 'unicore32', 'x86_64', 'xtensaeb', 'xtensa' ] }
-
-##
# @TargetInfo:
#
# Information describing the QEMU target.
@@ -3038,7 +3022,7 @@
# Since: 1.2.0
##
{ 'type': 'TargetInfo',
- 'data': { 'arch': 'TargetType' } }
+ 'data': { 'arch': 'str' } }
##
# @query-target:
diff --git a/scripts/create_config b/scripts/create_config
index 258513a887..b1adbf5897 100755
--- a/scripts/create_config
+++ b/scripts/create_config
@@ -77,16 +77,10 @@ case $line in
value=${line#*=}
echo "#define $name $value"
;;
- TARGET_ARCH=*) # configuration
- target_arch=${line#*=}
- echo "#define TARGET_ARCH \"$target_arch\""
- ;;
TARGET_BASE_ARCH=*) # configuration
target_base_arch=${line#*=}
- if [ "$target_base_arch" != "$target_arch" ]; then
- base_arch_name=`echo $target_base_arch | LC_ALL=C tr '[a-z]' '[A-Z]'`
- echo "#define TARGET_$base_arch_name 1"
- fi
+ base_arch_name=`echo $target_base_arch | LC_ALL=C tr '[a-z]' '[A-Z]'`
+ echo "#define TARGET_$base_arch_name 1"
;;
TARGET_XML_FILES=*)
# do nothing
@@ -94,8 +88,9 @@ case $line in
TARGET_ABI_DIR=*)
# do nothing
;;
- TARGET_ARCH2=*)
- # do nothing
+ TARGET_NAME=*)
+ target_name=${line#*=}
+ echo "#define TARGET_NAME \"$target_name\""
;;
TARGET_DIRS=*)
# do nothing
diff --git a/scripts/tracetool.py b/scripts/tracetool.py
index a79ec0f096..5f4890f3ab 100755
--- a/scripts/tracetool.py
+++ b/scripts/tracetool.py
@@ -46,9 +46,9 @@ Options:
--check-backend Check if the given backend is valid.
--binary <path> Full path to QEMU binary.
--target-type <type> QEMU emulator target type ('system' or 'user').
- --target-arch <arch> QEMU emulator target arch.
+ --target-name <name> QEMU emulator target name.
--probe-prefix <prefix> Prefix for dtrace probe names
- (default: qemu-<target-type>-<target-arch>).\
+ (default: qemu-<target-type>-<target-name>).\
""" % {
"script" : _SCRIPT,
"backends" : backend_descr,
@@ -66,7 +66,7 @@ def main(args):
_SCRIPT = args[0]
long_opts = [ "backend=", "format=", "help", "list-backends", "check-backend" ]
- long_opts += [ "binary=", "target-type=", "target-arch=", "probe-prefix=" ]
+ long_opts += [ "binary=", "target-type=", "target-name=", "probe-prefix=" ]
try:
opts, args = getopt.getopt(args[1:], "", long_opts)
@@ -78,7 +78,7 @@ def main(args):
arg_format = ""
binary = None
target_type = None
- target_arch = None
+ target_name = None
probe_prefix = None
for opt, arg in opts:
if opt == "--help":
@@ -100,8 +100,8 @@ def main(args):
binary = arg
elif opt == '--target-type':
target_type = arg
- elif opt == '--target-arch':
- target_arch = arg
+ elif opt == '--target-name':
+ target_name = arg
elif opt == '--probe-prefix':
probe_prefix = arg
@@ -122,11 +122,11 @@ def main(args):
error_opt("--binary is required for SystemTAP tapset generator")
if probe_prefix is None and target_type is None:
error_opt("--target-type is required for SystemTAP tapset generator")
- if probe_prefix is None and target_arch is None:
- error_opt("--target-arch is required for SystemTAP tapset generator")
+ if probe_prefix is None and target_name is None:
+ error_opt("--target-name is required for SystemTAP tapset generator")
if probe_prefix is None:
- probe_prefix = ".".join([ "qemu", target_type, target_arch ])
+ probe_prefix = ".".join([ "qemu", target_type, target_name ])
try:
tracetool.generate(sys.stdin, arg_format, arg_backend,
diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c
index 0fcf2b5daa..606b73defc 100644
--- a/tcg/ppc64/tcg-target.c
+++ b/tcg/ppc64/tcg-target.c
@@ -308,7 +308,8 @@ static int tcg_target_const_match (tcg_target_long val,
#define OPCD(opc) ((opc)<<26)
#define XO19(opc) (OPCD(19)|((opc)<<1))
-#define XO30(opc) (OPCD(30)|((opc)<<2))
+#define MD30(opc) (OPCD(30)|((opc)<<2))
+#define MDS30(opc) (OPCD(30)|((opc)<<1))
#define XO31(opc) (OPCD(31)|((opc)<<1))
#define XO58(opc) (OPCD(58)|(opc))
#define XO62(opc) (OPCD(62)|(opc))
@@ -354,10 +355,10 @@ static int tcg_target_const_match (tcg_target_long val,
#define RLWINM OPCD( 21)
#define RLWNM OPCD( 23)
-#define RLDICL XO30( 0)
-#define RLDICR XO30( 1)
-#define RLDIMI XO30( 3)
-#define RLDCL XO30( 8)
+#define RLDICL MD30( 0)
+#define RLDICR MD30( 1)
+#define RLDIMI MD30( 3)
+#define RLDCL MDS30( 8)
#define BCLR XO19( 16)
#define BCCTR XO19(528)
@@ -1661,7 +1662,7 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args,
tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31);
} else {
tcg_out32(s, SUBFIC | TAI(0, args[2], 32));
- tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2])
+ tcg_out32(s, RLWNM | SAB(args[1], args[0], 0)
| MB(0) | ME(31));
}
break;
@@ -1922,8 +1923,6 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args,
if (a0 == 0) {
tcg_out_mov(s, TCG_TYPE_I64, args[0], a0);
- /* Revert the source rotate that we performed above. */
- tcg_out_rld(s, RLDICL, a1, a1, 32, 0);
}
break;
@@ -1960,18 +1959,18 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args,
environment. So in 64-bit mode it's always carry-out of bit 63.
The fallback code using deposit works just as well for 32-bit. */
a0 = args[0], a1 = args[1];
- if (a0 == args[4] || (!const_args[5] && a0 == args[5])) {
+ if (a0 == args[3] || (!const_args[5] && a0 == args[5])) {
a0 = TCG_REG_R0;
}
- if (const_args[3]) {
- tcg_out32(s, ADDIC | TAI(a0, args[2], args[3]));
+ if (const_args[4]) {
+ tcg_out32(s, ADDIC | TAI(a0, args[2], args[4]));
} else {
- tcg_out32(s, ADDC | TAB(a0, args[2], args[3]));
+ tcg_out32(s, ADDC | TAB(a0, args[2], args[4]));
}
if (const_args[5]) {
- tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[4]));
+ tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[3]));
} else {
- tcg_out32(s, ADDE | TAB(a1, args[4], args[5]));
+ tcg_out32(s, ADDE | TAB(a1, args[3], args[5]));
}
if (a0 != args[0]) {
tcg_out_mov(s, TCG_TYPE_I64, args[0], a0);
@@ -2149,7 +2148,7 @@ static const TCGTargetOpDef ppc_op_defs[] = {
{ INDEX_op_deposit_i32, { "r", "0", "rZ" } },
{ INDEX_op_deposit_i64, { "r", "0", "rZ" } },
- { INDEX_op_add2_i64, { "r", "r", "r", "rI", "r", "rZM" } },
+ { INDEX_op_add2_i64, { "r", "r", "r", "r", "rI", "rZM" } },
{ INDEX_op_sub2_i64, { "r", "r", "rI", "r", "rZM", "r" } },
{ INDEX_op_muls2_i64, { "r", "r", "r", "r" } },
{ INDEX_op_mulu2_i64, { "r", "r", "r", "r" } },
diff --git a/ui/gtk.c b/ui/gtk.c
index 50a6993627..7310e200cc 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -377,7 +377,11 @@ static void gd_cursor_define(DisplayChangeListener *dcl,
pixbuf, c->hot_x, c->hot_y);
gdk_window_set_cursor(gtk_widget_get_window(s->drawing_area), cursor);
g_object_unref(pixbuf);
+#if !GTK_CHECK_VERSION(3, 0, 0)
gdk_cursor_unref(cursor);
+#else
+ g_object_unref(cursor);
+#endif
}
static void gd_switch(DisplayChangeListener *dcl,
diff --git a/vl.c b/vl.c
index 9f8fd6e377..f94ec9ca17 100644
--- a/vl.c
+++ b/vl.c
@@ -199,9 +199,7 @@ static int rtc_date_offset = -1; /* -1 means no change */
QEMUClock *rtc_clock;
int vga_interface_type = VGA_NONE;
static int full_screen = 0;
-#ifdef CONFIG_SDL
static int no_frame = 0;
-#endif
int no_quit = 0;
CharDriverState *serial_hds[MAX_SERIAL_PORTS];
CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];