summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2014-10-09 15:09:05 +0100
committerPeter Maydell <peter.maydell@linaro.org>2014-10-09 15:09:05 +0100
commitfcb2cd928f5519b8241e22411a696be1a7272b1c (patch)
tree394f049d19f8abb12c1b11f4dcc03b9036da604d
parentb6011bd8a57c1eda81a857d21adeb9b66e58b1b0 (diff)
parent5008e5b7b817b5ea2b788203122cd50e7c16e599 (diff)
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
Four changes here. Polling for reconnection of character devices, the QOMification of accelerators, a fix for -kernel support on x86, and one for a recently-introduced virtio-scsi optimization. # gpg: Signature made Thu 09 Oct 2014 14:36:50 BST using RSA key ID 4E6B09D7 # gpg: Good signature from "Paolo Bonzini <pbonzini@redhat.com>" # gpg: aka "Paolo Bonzini <bonzini@gnu.org>" * remotes/bonzini/tags/for-upstream: (28 commits) qemu-char: Fix reconnect socket error reporting qemu-sockets: Add error to non-blocking connect handler qemu-error: Add error_vreport() virtio-scsi: fix use-after-free of VirtIOSCSIReq linuxboot: compute initrd loading address kvm: Make KVMState be the TYPE_KVM_ACCEL instance struct accel: Create accel object when initializing machine accel: Pass MachineState object to accel init functions accel: Rename 'init' method to 'init_machine' accel: Move accel init/allowed code to separate function accel: Remove tcg_available() function accel: Move qtest accel registration to qtest.c accel: Move Xen registration code to xen-common.c accel: Move KVM accel registration to kvm-all.c accel: Report unknown accelerator as "not found" instead of "does not exist" accel: Make AccelClass.available() optional accel: Use QOM classes for accel types accel: Move accel name lookup to separate function accel: Simplify configure_accelerator() using AccelType *acc variable accel: Create AccelType typedef ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--Makefile.objs1
-rw-r--r--accel.c157
-rw-r--r--arch_init.c5
-rw-r--r--hw/scsi/virtio-scsi.c9
-rw-r--r--include/hw/boards.h4
-rw-r--r--include/hw/xen/xen.h1
-rw-r--r--include/qemu/error-report.h1
-rw-r--r--include/qemu/sockets.h2
-rw-r--r--include/qemu/typedefs.h1
-rw-r--r--include/sysemu/accel.h62
-rw-r--r--include/sysemu/arch_init.h1
-rw-r--r--include/sysemu/kvm.h2
-rw-r--r--include/sysemu/qtest.h1
-rw-r--r--kvm-all.c40
-rw-r--r--kvm-stub.c5
-rw-r--r--migration-tcp.c4
-rw-r--r--migration-unix.c4
-rw-r--r--pc-bios/linuxboot.binbin1024 -> 1024 bytes
-rw-r--r--pc-bios/optionrom/linuxboot.S47
-rw-r--r--pc-bios/optionrom/optionrom.h21
-rw-r--r--qapi-schema.json15
-rw-r--r--qemu-char.c359
-rw-r--r--qemu-options.hx20
-rw-r--r--qtest.c27
-rw-r--r--util/qemu-error.c23
-rw-r--r--util/qemu-sockets.c20
-rw-r--r--vl.c83
-rw-r--r--xen-common-stub.c6
-rw-r--r--xen-common.c25
29 files changed, 711 insertions, 235 deletions
diff --git a/Makefile.objs b/Makefile.objs
index 97db978d16..add83755be 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -62,6 +62,7 @@ common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
common-obj-y += audio/
common-obj-y += hw/
+common-obj-y += accel.o
common-obj-y += ui/
common-obj-y += bt-host.o bt-vhci.o
diff --git a/accel.c b/accel.c
new file mode 100644
index 0000000000..74e41daaa5
--- /dev/null
+++ b/accel.c
@@ -0,0 +1,157 @@
+/*
+ * QEMU System Emulator, accelerator interfaces
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2014 Red Hat Inc.
+ *
+ * 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 "sysemu/accel.h"
+#include "hw/boards.h"
+#include "qemu-common.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "sysemu/qtest.h"
+#include "hw/xen/xen.h"
+#include "qom/object.h"
+#include "hw/boards.h"
+
+int tcg_tb_size;
+static bool tcg_allowed = true;
+
+static int tcg_init(MachineState *ms)
+{
+ tcg_exec_init(tcg_tb_size * 1024 * 1024);
+ return 0;
+}
+
+static const TypeInfo accel_type = {
+ .name = TYPE_ACCEL,
+ .parent = TYPE_OBJECT,
+ .class_size = sizeof(AccelClass),
+ .instance_size = sizeof(AccelState),
+};
+
+/* Lookup AccelClass from opt_name. Returns NULL if not found */
+static AccelClass *accel_find(const char *opt_name)
+{
+ char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
+ AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name));
+ g_free(class_name);
+ return ac;
+}
+
+static int accel_init_machine(AccelClass *acc, MachineState *ms)
+{
+ ObjectClass *oc = OBJECT_CLASS(acc);
+ const char *cname = object_class_get_name(oc);
+ AccelState *accel = ACCEL(object_new(cname));
+ int ret;
+ ms->accelerator = accel;
+ *(acc->allowed) = true;
+ ret = acc->init_machine(ms);
+ if (ret < 0) {
+ ms->accelerator = NULL;
+ *(acc->allowed) = false;
+ object_unref(OBJECT(accel));
+ }
+ return ret;
+}
+
+int configure_accelerator(MachineState *ms)
+{
+ const char *p;
+ char buf[10];
+ int ret;
+ bool accel_initialised = false;
+ bool init_failed = false;
+ AccelClass *acc = NULL;
+
+ p = qemu_opt_get(qemu_get_machine_opts(), "accel");
+ if (p == NULL) {
+ /* Use the default "accelerator", tcg */
+ p = "tcg";
+ }
+
+ while (!accel_initialised && *p != '\0') {
+ if (*p == ':') {
+ p++;
+ }
+ p = get_opt_name(buf, sizeof(buf), p, ':');
+ acc = accel_find(buf);
+ if (!acc) {
+ fprintf(stderr, "\"%s\" accelerator not found.\n", buf);
+ continue;
+ }
+ if (acc->available && !acc->available()) {
+ printf("%s not supported for this target\n",
+ acc->name);
+ continue;
+ }
+ ret = accel_init_machine(acc, ms);
+ if (ret < 0) {
+ init_failed = true;
+ fprintf(stderr, "failed to initialize %s: %s\n",
+ acc->name,
+ strerror(-ret));
+ } else {
+ accel_initialised = true;
+ }
+ }
+
+ if (!accel_initialised) {
+ if (!init_failed) {
+ fprintf(stderr, "No accelerator found!\n");
+ }
+ exit(1);
+ }
+
+ if (init_failed) {
+ fprintf(stderr, "Back to %s accelerator.\n", acc->name);
+ }
+
+ return !accel_initialised;
+}
+
+
+static void tcg_accel_class_init(ObjectClass *oc, void *data)
+{
+ AccelClass *ac = ACCEL_CLASS(oc);
+ ac->name = "tcg";
+ ac->init_machine = tcg_init;
+ ac->allowed = &tcg_allowed;
+}
+
+#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
+
+static const TypeInfo tcg_accel_type = {
+ .name = TYPE_TCG_ACCEL,
+ .parent = TYPE_ACCEL,
+ .class_init = tcg_accel_class_init,
+};
+
+static void register_accel_types(void)
+{
+ type_register_static(&accel_type);
+ type_register_static(&tcg_accel_type);
+}
+
+type_init(register_accel_types);
diff --git a/arch_init.c b/arch_init.c
index c974f3fea9..9b3e25d805 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -1337,11 +1337,6 @@ void cpudef_init(void)
#endif
}
-int tcg_available(void)
-{
- return 1;
-}
-
int kvm_available(void)
{
#ifdef CONFIG_KVM
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 203e62449a..6c02fe2b9a 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -545,11 +545,12 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
- if (scsi_req_enqueue(req->sreq)) {
- scsi_req_continue(req->sreq);
+ SCSIRequest *sreq = req->sreq;
+ if (scsi_req_enqueue(sreq)) {
+ scsi_req_continue(sreq);
}
- bdrv_io_unplug(req->sreq->dev->conf.bs);
- scsi_req_unref(req->sreq);
+ bdrv_io_unplug(sreq->dev->conf.bs);
+ scsi_req_unref(sreq);
}
static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 663f16acdd..e07c03f846 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -5,12 +5,11 @@
#include "qemu/typedefs.h"
#include "sysemu/blockdev.h"
+#include "sysemu/accel.h"
#include "hw/qdev.h"
#include "qom/object.h"
-typedef struct MachineState MachineState;
-
typedef void QEMUMachineInitFunc(MachineState *ms);
typedef void QEMUMachineResetFunc(void);
@@ -135,6 +134,7 @@ struct MachineState {
char *kernel_cmdline;
char *initrd_filename;
const char *cpu_model;
+ AccelState *accelerator;
};
#endif
diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h
index f71f2d8963..b0ed04caa9 100644
--- a/include/hw/xen/xen.h
+++ b/include/hw/xen/xen.h
@@ -36,7 +36,6 @@ void xen_cmos_set_s3_resume(void *opaque, int irq, int level);
qemu_irq *xen_interrupt_controller_init(void);
-int xen_init(MachineClass *mc);
void xenstore_store_pv_console_info(int i, struct CharDriverState *chr);
#if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY)
diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h
index 000eae3957..7ab235590e 100644
--- a/include/qemu/error-report.h
+++ b/include/qemu/error-report.h
@@ -38,6 +38,7 @@ void error_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0);
void error_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
void error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
void error_set_progname(const char *argv0);
+void error_vreport(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0);
void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
const char *error_get_progname(void);
extern bool enable_timestamp_msg;
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index fdbb196787..f47dae614a 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -47,7 +47,7 @@ int recv_all(int fd, void *buf, int len1, bool single_read);
/* callback function for nonblocking connect
* valid fd on success, negative error code on failure
*/
-typedef void NonBlockingConnectHandler(int fd, void *opaque);
+typedef void NonBlockingConnectHandler(int fd, Error *errp, void *opaque);
InetSocketAddress *inet_parse(const char *str, Error **errp);
int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp);
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 5f20b0e263..04df51b6fc 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -32,6 +32,7 @@ typedef struct MemoryMappingList MemoryMappingList;
typedef struct QEMUMachine QEMUMachine;
typedef struct MachineClass MachineClass;
+typedef struct MachineState MachineState;
typedef struct NICInfo NICInfo;
typedef struct HCIInfo HCIInfo;
typedef struct AudioState AudioState;
diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h
new file mode 100644
index 0000000000..997720f36c
--- /dev/null
+++ b/include/sysemu/accel.h
@@ -0,0 +1,62 @@
+/* QEMU accelerator interfaces
+ *
+ * Copyright (c) 2014 Red Hat Inc
+ *
+ * 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.
+ */
+#ifndef HW_ACCEL_H
+#define HW_ACCEL_H
+
+#include "qemu/typedefs.h"
+#include "qom/object.h"
+
+typedef struct AccelState {
+ /*< private >*/
+ Object parent_obj;
+} AccelState;
+
+typedef struct AccelClass {
+ /*< private >*/
+ ObjectClass parent_class;
+ /*< public >*/
+
+ const char *opt_name;
+ const char *name;
+ int (*available)(void);
+ int (*init_machine)(MachineState *ms);
+ bool *allowed;
+} AccelClass;
+
+#define TYPE_ACCEL "accel"
+
+#define ACCEL_CLASS_SUFFIX "-" TYPE_ACCEL
+#define ACCEL_CLASS_NAME(a) (a ACCEL_CLASS_SUFFIX)
+
+#define ACCEL_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AccelClass, (klass), TYPE_ACCEL)
+#define ACCEL(obj) \
+ OBJECT_CHECK(AccelState, (obj), TYPE_ACCEL)
+#define ACCEL_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AccelClass, (obj), TYPE_ACCEL)
+
+extern int tcg_tb_size;
+
+int configure_accelerator(MachineState *ms);
+
+#endif
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index 769ec069b7..54b36c16c4 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -33,7 +33,6 @@ void do_smbios_option(QemuOpts *opts);
void ram_mig_init(void);
void cpudef_init(void);
void audio_init(void);
-int tcg_available(void);
int kvm_available(void);
int xen_available(void);
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index 77ee240875..b0cd657f77 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -163,8 +163,6 @@ extern KVMState *kvm_state;
/* external API */
-int kvm_init(MachineClass *mc);
-
int kvm_has_sync_mmu(void);
int kvm_has_vcpu_events(void);
int kvm_has_robust_singlestep(void);
diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index 95c9ade778..05473b75a5 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -26,7 +26,6 @@ static inline bool qtest_enabled(void)
bool qtest_driver(void);
-int qtest_init_accel(MachineClass *mc);
void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp);
static inline int qtest_available(void)
diff --git a/kvm-all.c b/kvm-all.c
index 4afcd0551b..e98a7c74a7 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -25,6 +25,7 @@
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "sysemu/sysemu.h"
+#include "sysemu/accel.h"
#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/s390x/adapter.h"
@@ -70,8 +71,10 @@ typedef struct KVMSlot
typedef struct kvm_dirty_log KVMDirtyLog;
-struct KVMState
+typedef struct KVMState
{
+ AccelState parent_obj;
+
KVMSlot *slots;
int nr_slots;
int fd;
@@ -104,7 +107,12 @@ struct KVMState
QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
bool direct_msi;
#endif
-};
+} KVMState;
+
+#define TYPE_KVM_ACCEL ACCEL_CLASS_NAME("kvm")
+
+#define KVM_STATE(obj) \
+ OBJECT_CHECK(KVMState, (obj), TYPE_KVM_ACCEL)
KVMState *kvm_state;
bool kvm_kernel_irqchip;
@@ -1377,8 +1385,9 @@ static int kvm_max_vcpus(KVMState *s)
return (ret) ? ret : kvm_recommended_vcpus(s);
}
-int kvm_init(MachineClass *mc)
+static int kvm_init(MachineState *ms)
{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
static const char upgrade_note[] =
"Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
"(see http://sourceforge.net/projects/kvm).\n";
@@ -1397,7 +1406,7 @@ int kvm_init(MachineClass *mc)
int i, type = 0;
const char *kvm_type;
- s = g_malloc0(sizeof(KVMState));
+ s = KVM_STATE(ms->accelerator);
/*
* On systems where the kernel can support different base page
@@ -1586,7 +1595,6 @@ err:
close(s->fd);
}
g_free(s->slots);
- g_free(s);
return ret;
}
@@ -2225,3 +2233,25 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target)
}
return r;
}
+
+static void kvm_accel_class_init(ObjectClass *oc, void *data)
+{
+ AccelClass *ac = ACCEL_CLASS(oc);
+ ac->name = "KVM";
+ ac->init_machine = kvm_init;
+ ac->allowed = &kvm_allowed;
+}
+
+static const TypeInfo kvm_accel_type = {
+ .name = TYPE_KVM_ACCEL,
+ .parent = TYPE_ACCEL,
+ .class_init = kvm_accel_class_init,
+ .instance_size = sizeof(KVMState),
+};
+
+static void kvm_type_init(void)
+{
+ type_register_static(&kvm_accel_type);
+}
+
+type_init(kvm_type_init);
diff --git a/kvm-stub.c b/kvm-stub.c
index 8e7737caa9..43fc0dd01e 100644
--- a/kvm-stub.c
+++ b/kvm-stub.c
@@ -35,11 +35,6 @@ int kvm_init_vcpu(CPUState *cpu)
return -ENOSYS;
}
-int kvm_init(MachineClass *mc)
-{
- return -ENOSYS;
-}
-
void kvm_flush_coalesced_mmio_buffer(void)
{
}
diff --git a/migration-tcp.c b/migration-tcp.c
index 2e34517bb9..91c9cf381e 100644
--- a/migration-tcp.c
+++ b/migration-tcp.c
@@ -33,12 +33,12 @@
do { } while (0)
#endif
-static void tcp_wait_for_connect(int fd, void *opaque)
+static void tcp_wait_for_connect(int fd, Error *err, void *opaque)
{
MigrationState *s = opaque;
if (fd < 0) {
- DPRINTF("migrate connect error\n");
+ DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
s->file = NULL;
migrate_fd_error(s);
} else {
diff --git a/migration-unix.c b/migration-unix.c
index 0a5f8a1332..1cdadfbc83 100644
--- a/migration-unix.c
+++ b/migration-unix.c
@@ -33,12 +33,12 @@
do { } while (0)
#endif
-static void unix_wait_for_connect(int fd, void *opaque)
+static void unix_wait_for_connect(int fd, Error *err, void *opaque)
{
MigrationState *s = opaque;
if (fd < 0) {
- DPRINTF("migrate connect error\n");
+ DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
s->file = NULL;
migrate_fd_error(s);
} else {
diff --git a/pc-bios/linuxboot.bin b/pc-bios/linuxboot.bin
index e7c36694f9..130103fb73 100644
--- a/pc-bios/linuxboot.bin
+++ b/pc-bios/linuxboot.bin
Binary files differ
diff --git a/pc-bios/optionrom/linuxboot.S b/pc-bios/optionrom/linuxboot.S
index 748c831160..5bc0af08e0 100644
--- a/pc-bios/optionrom/linuxboot.S
+++ b/pc-bios/optionrom/linuxboot.S
@@ -76,14 +76,45 @@ boot_kernel:
copy_kernel:
+ /* Compute initrd address */
+ mov $0xe801, %ax
+ xor %cx, %cx
+ xor %dx, %dx
+ int $0x15
+
+ /* Output could be in AX/BX or CX/DX */
+ or %cx, %cx
+ jnz 1f
+ or %dx, %dx
+ jnz 1f
+ mov %ax, %cx
+ mov %bx, %dx
+1:
+
+ or %dx, %dx
+ jnz 2f
+ addw $1024, %cx /* add 1 MB */
+ movzwl %cx, %edi
+ shll $10, %edi /* convert to bytes */
+ jmp 3f
+
+2:
+ addw $16777216 >> 16, %dx /* add 16 MB */
+ movzwl %dx, %edi
+ shll $16, %edi /* convert to bytes */
+
+3:
+ read_fw FW_CFG_INITRD_SIZE
+ subl %eax, %edi
+ andl $-4096, %edi /* EDI = start of initrd */
/* We need to load the kernel into memory we can't access in 16 bit
mode, so let's get into 32 bit mode, write the kernel and jump
back again. */
/* Reserve space on the stack for our GDT descriptor. */
- mov %esp, %ebp
- sub $16, %esp
+ mov %esp, %ebp
+ sub $16, %esp
/* Now create the GDT descriptor */
movw $((3 * 8) - 1), -16(%bp)
@@ -108,10 +139,18 @@ copy_kernel:
/* We're now running in 16-bit CS, but 32-bit ES! */
/* Load kernel and initrd */
+ pushl %edi
+ read_fw_blob_addr32_edi(FW_CFG_INITRD)
read_fw_blob_addr32(FW_CFG_KERNEL)
- read_fw_blob_addr32(FW_CFG_INITRD)
read_fw_blob_addr32(FW_CFG_CMDLINE)
- read_fw_blob_addr32(FW_CFG_SETUP)
+
+ read_fw FW_CFG_SETUP_ADDR
+ mov %eax, %edi
+ mov %eax, %ebx
+ read_fw_blob_addr32_edi(FW_CFG_SETUP)
+
+ /* Update the header with the initrd address we chose above */
+ popl %es:0x218(%ebx)
/* And now jump into Linux! */
mov $0, %eax
diff --git a/pc-bios/optionrom/optionrom.h b/pc-bios/optionrom/optionrom.h
index ce436085d9..f1a9021ec1 100644
--- a/pc-bios/optionrom/optionrom.h
+++ b/pc-bios/optionrom/optionrom.h
@@ -51,8 +51,6 @@
.endm
#define read_fw_blob_pre(var) \
- read_fw var ## _ADDR; \
- mov %eax, %edi; \
read_fw var ## _SIZE; \
mov %eax, %ecx; \
mov $var ## _DATA, %ax; \
@@ -68,6 +66,8 @@
* Clobbers: %eax, %edx, %es, %ecx, %edi
*/
#define read_fw_blob(var) \
+ read_fw var ## _ADDR; \
+ mov %eax, %edi; \
read_fw_blob_pre(var); \
/* old as(1) doesn't like this insn so emit the bytes instead: \
rep insb (%dx), %es:(%edi); \
@@ -80,7 +80,22 @@
*
* Clobbers: %eax, %edx, %es, %ecx, %edi
*/
-#define read_fw_blob_addr32(var) \
+#define read_fw_blob_addr32(var) \
+ read_fw var ## _ADDR; \
+ mov %eax, %edi; \
+ read_fw_blob_pre(var); \
+ /* old as(1) doesn't like this insn so emit the bytes instead: \
+ addr32 rep insb (%dx), %es:(%edi); \
+ */ \
+ .dc.b 0x67,0xf3,0x6c
+
+/*
+ * Read a blob from the fw_cfg device in forced addr32 mode, address is in %edi.
+ * Requires _SIZE and _DATA values for the parameter.
+ *
+ * Clobbers: %eax, %edx, %edi, %es, %ecx
+ */
+#define read_fw_blob_addr32_edi(var) \
read_fw_blob_pre(var); \
/* old as(1) doesn't like this insn so emit the bytes instead: \
addr32 rep insb (%dx), %es:(%edi); \
diff --git a/qapi-schema.json b/qapi-schema.json
index 2e9e261e0c..4f0d7e3250 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2651,14 +2651,19 @@
# @nodelay: #optional set TCP_NODELAY socket option (default: false)
# @telnet: #optional enable telnet protocol on server
# sockets (default: false)
+# @reconnect: #optional For a client socket, if a socket is disconnected,
+# then attempt a reconnect after the given number of seconds.
+# Setting this to zero disables this function. (default: 0)
+# (Since: 2.2)
#
# Since: 1.4
##
-{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress',
- '*server' : 'bool',
- '*wait' : 'bool',
- '*nodelay' : 'bool',
- '*telnet' : 'bool' } }
+{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress',
+ '*server' : 'bool',
+ '*wait' : 'bool',
+ '*nodelay' : 'bool',
+ '*telnet' : 'bool',
+ '*reconnect' : 'int' } }
##
# @ChardevUdp:
diff --git a/qemu-char.c b/qemu-char.c
index 8623c70964..bd0709bab5 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -28,6 +28,9 @@
#include "sysemu/char.h"
#include "hw/usb.h"
#include "qmp-commands.h"
+#include "qapi/qmp-input-visitor.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi-visit.h"
#include <unistd.h>
#include <fcntl.h>
@@ -84,6 +87,94 @@
#define READ_BUF_LEN 4096
#define READ_RETRIES 10
+#define CHR_MAX_FILENAME_SIZE 256
+
+/***********************************************************/
+/* Socket address helpers */
+static void qapi_copy_SocketAddress(SocketAddress **p_dest,
+ SocketAddress *src)
+{
+ QmpOutputVisitor *qov;
+ QmpInputVisitor *qiv;
+ Visitor *ov, *iv;
+ QObject *obj;
+
+ *p_dest = NULL;
+
+ qov = qmp_output_visitor_new();
+ ov = qmp_output_get_visitor(qov);
+ visit_type_SocketAddress(ov, &src, NULL, &error_abort);
+ obj = qmp_output_get_qobject(qov);
+ qmp_output_visitor_cleanup(qov);
+ if (!obj) {
+ return;
+ }
+
+ qiv = qmp_input_visitor_new(obj);
+ iv = qmp_input_get_visitor(qiv);
+ visit_type_SocketAddress(iv, p_dest, NULL, &error_abort);
+ qmp_input_visitor_cleanup(qiv);
+ qobject_decref(obj);
+}
+
+static int SocketAddress_to_str(char *dest, int max_len,
+ const char *prefix, SocketAddress *addr,
+ bool is_listen, bool is_telnet)
+{
+ switch (addr->kind) {
+ case SOCKET_ADDRESS_KIND_INET:
+ return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix,
+ is_telnet ? "telnet" : "tcp", addr->inet->host,
+ addr->inet->port, is_listen ? ",server" : "");
+ break;
+ case SOCKET_ADDRESS_KIND_UNIX:
+ return snprintf(dest, max_len, "%sunix:%s%s", prefix,
+ addr->q_unix->path, is_listen ? ",server" : "");
+ break;
+ case SOCKET_ADDRESS_KIND_FD:
+ return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->fd->str,
+ is_listen ? ",server" : "");
+ break;
+ default:
+ abort();
+ }
+}
+
+static int sockaddr_to_str(char *dest, int max_len,
+ struct sockaddr_storage *ss, socklen_t ss_len,
+ struct sockaddr_storage *ps, socklen_t ps_len,
+ bool is_listen, bool is_telnet)
+{
+ char shost[NI_MAXHOST], sserv[NI_MAXSERV];
+ char phost[NI_MAXHOST], pserv[NI_MAXSERV];
+ const char *left = "", *right = "";
+
+ switch (ss->ss_family) {
+#ifndef _WIN32
+ case AF_UNIX:
+ return snprintf(dest, max_len, "unix:%s%s",
+ ((struct sockaddr_un *)(ss))->sun_path,
+ is_listen ? ",server" : "");
+#endif
+ case AF_INET6:
+ left = "[";
+ right = "]";
+ /* fall through */
+ case AF_INET:
+ getnameinfo((struct sockaddr *) ss, ss_len, shost, sizeof(shost),
+ sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV);
+ getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
+ pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
+ return snprintf(dest, max_len, "%s:%s%s%s:%s%s <-> %s%s%s:%s",
+ is_telnet ? "telnet" : "tcp",
+ left, shost, right, sserv,
+ is_listen ? ",server" : "",
+ left, phost, right, pserv);
+
+ default:
+ return snprintf(dest, max_len, "unknown");
+ }
+}
/***********************************************************/
/* character device */
@@ -989,7 +1080,8 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
{
int fd_in, fd_out;
- char filename_in[256], filename_out[256];
+ char filename_in[CHR_MAX_FILENAME_SIZE];
+ char filename_out[CHR_MAX_FILENAME_SIZE];
const char *filename = opts->device;
if (filename == NULL) {
@@ -997,8 +1089,8 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
return NULL;
}
- snprintf(filename_in, 256, "%s.in", filename);
- snprintf(filename_out, 256, "%s.out", filename);
+ snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename);
+ snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename);
TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
if (fd_in < 0 || fd_out < 0) {
@@ -1976,7 +2068,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
OVERLAPPED ov;
int ret;
DWORD size;
- char openname[256];
+ char openname[CHR_MAX_FILENAME_SIZE];
s->fpipe = TRUE;
@@ -2410,8 +2502,39 @@ typedef struct {
int read_msgfds_num;
int *write_msgfds;
int write_msgfds_num;
+
+ SocketAddress *addr;
+ bool is_listen;
+ bool is_telnet;
+
+ guint reconnect_timer;
+ int64_t reconnect_time;
+ bool connect_err_reported;
} TCPCharDriver;
+static gboolean socket_reconnect_timeout(gpointer opaque);
+
+static void qemu_chr_socket_restart_timer(CharDriverState *chr)
+{
+ TCPCharDriver *s = chr->opaque;
+ assert(s->connected == 0);
+ s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
+ socket_reconnect_timeout, chr);
+}
+
+static void check_report_connect_error(CharDriverState *chr,
+ Error *err)
+{
+ TCPCharDriver *s = chr->opaque;
+
+ if (!s->connect_err_reported) {
+ error_report("Unable to connect character device %s: %s",
+ chr->label, error_get_pretty(err));
+ s->connect_err_reported = true;
+ }
+ qemu_chr_socket_restart_timer(chr);
+}
+
static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
#ifndef _WIN32
@@ -2690,7 +2813,12 @@ static void tcp_chr_disconnect(CharDriverState *chr)
s->chan = NULL;
closesocket(s->fd);
s->fd = -1;
+ SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
+ "disconnected:", s->addr, s->is_listen, s->is_telnet);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+ if (s->reconnect_time) {
+ qemu_chr_socket_restart_timer(chr);
+ }
}
static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
@@ -2761,6 +2889,21 @@ static void tcp_chr_connect(void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
+ struct sockaddr_storage ss, ps;
+ socklen_t ss_len = sizeof(ss), ps_len = sizeof(ps);
+
+ memset(&ss, 0, ss_len);
+ if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) {
+ snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
+ "Error in getsockname: %s\n", strerror(errno));
+ } else if (getpeername(s->fd, (struct sockaddr *) &ps, &ps_len) != 0) {
+ snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
+ "Error in getpeername: %s\n", strerror(errno));
+ } else {
+ sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
+ &ss, ss_len, &ps, ps_len,
+ s->is_listen, s->is_telnet);
+ }
s->connected = 1;
if (s->chan) {
@@ -2859,6 +3002,12 @@ static void tcp_chr_close(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
int i;
+
+ if (s->reconnect_timer) {
+ g_source_remove(s->reconnect_timer);
+ s->reconnect_timer = 0;
+ }
+ qapi_free_SocketAddress(s->addr);
if (s->fd >= 0) {
remove_fd_in_watch(chr);
if (s->chan) {
@@ -2889,79 +3038,15 @@ static void tcp_chr_close(CharDriverState *chr)
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
- bool is_listen, bool is_telnet,
- bool is_waitconnect,
- Error **errp)
+static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd)
{
- CharDriverState *chr = NULL;
- TCPCharDriver *s = NULL;
- char host[NI_MAXHOST], serv[NI_MAXSERV];
- const char *left = "", *right = "";
- struct sockaddr_storage ss;
- socklen_t ss_len = sizeof(ss);
-
- memset(&ss, 0, ss_len);
- if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) {
- error_setg_errno(errp, errno, "getsockname");
- return NULL;
- }
-
- chr = qemu_chr_alloc();
- s = g_malloc0(sizeof(TCPCharDriver));
-
- s->connected = 0;
- s->fd = -1;
- s->listen_fd = -1;
- s->read_msgfds = 0;
- s->read_msgfds_num = 0;
- s->write_msgfds = 0;
- s->write_msgfds_num = 0;
-
- chr->filename = g_malloc(256);
- switch (ss.ss_family) {
-#ifndef _WIN32
- case AF_UNIX:
- s->is_unix = 1;
- snprintf(chr->filename, 256, "unix:%s%s",
- ((struct sockaddr_un *)(&ss))->sun_path,
- is_listen ? ",server" : "");
- break;
-#endif
- case AF_INET6:
- left = "[";
- right = "]";
- /* fall through */
- case AF_INET:
- s->do_nodelay = do_nodelay;
- getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host),
- serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);
- snprintf(chr->filename, 256, "%s:%s%s%s:%s%s",
- is_telnet ? "telnet" : "tcp",
- left, host, right, serv,
- is_listen ? ",server" : "");
- break;
- }
-
- chr->opaque = s;
- chr->chr_write = tcp_chr_write;
- chr->chr_sync_read = tcp_chr_sync_read;
- chr->chr_close = tcp_chr_close;
- chr->get_msgfds = tcp_get_msgfds;
- chr->set_msgfds = tcp_set_msgfds;
- chr->chr_add_client = tcp_chr_add_client;
- chr->chr_add_watch = tcp_chr_add_watch;
- chr->chr_update_read_handler = tcp_chr_update_read_handler;
- /* be isn't opened until we get a connection */
- chr->explicit_be_open = true;
+ TCPCharDriver *s = chr->opaque;
- if (is_listen) {
+ if (s->is_listen) {
s->listen_fd = fd;
s->listen_chan = io_channel_from_socket(s->listen_fd);
- s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
- if (is_telnet) {
- s->do_telnetopt = 1;
- }
+ s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN,
+ tcp_chr_accept, chr);
} else {
s->connected = 1;
s->fd = fd;
@@ -2969,14 +3054,41 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
s->chan = io_channel_from_socket(s->fd);
tcp_chr_connect(chr);
}
+}
- if (is_listen && is_waitconnect) {
- fprintf(stderr, "QEMU waiting for connection on: %s\n",
- chr->filename);
- tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
- qemu_set_nonblock(s->listen_fd);
+static void qemu_chr_socket_connected(int fd, Error *err, void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+
+ if (fd < 0) {
+ check_report_connect_error(chr, err);
+ return;
}
- return chr;
+
+ s->connect_err_reported = false;
+ qemu_chr_finish_socket_connection(chr, fd);
+}
+
+static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp)
+{
+ TCPCharDriver *s = chr->opaque;
+ int fd;
+
+ if (s->is_listen) {
+ fd = socket_listen(s->addr, errp);
+ } else if (s->reconnect_time) {
+ fd = socket_connect(s->addr, errp, qemu_chr_socket_connected, chr);
+ return fd >= 0;
+ } else {
+ fd = socket_connect(s->addr, errp, NULL, NULL);
+ }
+ if (fd < 0) {
+ return false;
+ }
+
+ qemu_chr_finish_socket_connection(chr, fd);
+ return true;
}
/*********************************************************/
@@ -3396,6 +3508,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
+ int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
const char *path = qemu_opt_get(opts, "path");
const char *host = qemu_opt_get(opts, "host");
const char *port = qemu_opt_get(opts, "port");
@@ -3422,6 +3535,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
backend->socket->telnet = is_telnet;
backend->socket->has_wait = true;
backend->socket->wait = is_waitconnect;
+ backend->socket->has_reconnect = true;
+ backend->socket->reconnect = reconnect;
addr = g_new0(SocketAddress, 1);
if (path) {
@@ -3821,6 +3936,9 @@ QemuOptsList qemu_chardev_opts = {
.name = "delay",
.type = QEMU_OPT_BOOL,
},{
+ .name = "reconnect",
+ .type = QEMU_OPT_NUMBER,
+ },{
.name = "telnet",
.type = QEMU_OPT_BOOL,
},{
@@ -3964,26 +4082,95 @@ static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
#endif /* WIN32 */
+static void socket_try_connect(CharDriverState *chr)
+{
+ Error *err = NULL;
+
+ if (!qemu_chr_open_socket_fd(chr, &err)) {
+ check_report_connect_error(chr, err);
+ }
+}
+
+static gboolean socket_reconnect_timeout(gpointer opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+
+ s->reconnect_timer = 0;
+
+ if (chr->be_open) {
+ return false;
+ }
+
+ socket_try_connect(chr);
+
+ return false;
+}
+
static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
Error **errp)
{
+ CharDriverState *chr;
+ TCPCharDriver *s;
SocketAddress *addr = sock->addr;
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
bool is_listen = sock->has_server ? sock->server : true;
bool is_telnet = sock->has_telnet ? sock->telnet : false;
bool is_waitconnect = sock->has_wait ? sock->wait : false;
- int fd;
+ int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
+
+ chr = qemu_chr_alloc();
+ s = g_malloc0(sizeof(TCPCharDriver));
+
+ s->fd = -1;
+ s->listen_fd = -1;
+ s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX;
+ s->is_listen = is_listen;
+ s->is_telnet = is_telnet;
+ s->do_nodelay = do_nodelay;
+ qapi_copy_SocketAddress(&s->addr, sock->addr);
+
+ chr->opaque = s;
+ chr->chr_write = tcp_chr_write;
+ chr->chr_sync_read = tcp_chr_sync_read;
+ chr->chr_close = tcp_chr_close;
+ chr->get_msgfds = tcp_get_msgfds;
+ chr->set_msgfds = tcp_set_msgfds;
+ chr->chr_add_client = tcp_chr_add_client;
+ chr->chr_add_watch = tcp_chr_add_watch;
+ chr->chr_update_read_handler = tcp_chr_update_read_handler;
+ /* be isn't opened until we get a connection */
+ chr->explicit_be_open = true;
+
+ chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE);
+ SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, "disconnected:",
+ addr, is_listen, is_telnet);
if (is_listen) {
- fd = socket_listen(addr, errp);
- } else {
- fd = socket_connect(addr, errp, NULL, NULL);
+ if (is_telnet) {
+ s->do_telnetopt = 1;
+ }
+ } else if (reconnect > 0) {
+ s->reconnect_time = reconnect;
}
- if (fd < 0) {
+
+ if (s->reconnect_time) {
+ socket_try_connect(chr);
+ } else if (!qemu_chr_open_socket_fd(chr, errp)) {
+ g_free(s);
+ g_free(chr->filename);
+ g_free(chr);
return NULL;
}
- return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen,
- is_telnet, is_waitconnect, errp);
+
+ if (is_listen && is_waitconnect) {
+ fprintf(stderr, "QEMU waiting for connection on: %s\n",
+ chr->filename);
+ tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
+ qemu_set_nonblock(s->listen_fd);
+ }
+
+ return chr;
}
static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
diff --git a/qemu-options.hx b/qemu-options.hx
index 365b56c424..22cf3b94ce 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1930,9 +1930,9 @@ ETEXI
DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
"-chardev null,id=id[,mux=on|off]\n"
- "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay]\n"
- " [,server][,nowait][,telnet][,mux=on|off] (tcp)\n"
- "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] (unix)\n"
+ "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n"
+ " [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n"
+ "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n"
"-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
" [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
"-chardev msmouse,id=id[,mux=on|off]\n"
@@ -2004,7 +2004,7 @@ Options to each backend are described below.
A void device. This device will not emit any data, and will drop any data it
receives. The null backend does not take any options.
-@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet]
+@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}]
Create a two-way stream socket, which can be either a TCP or a unix socket. A
unix socket will be created if @option{path} is specified. Behaviour is
@@ -2018,6 +2018,10 @@ connect to a listening socket.
@option{telnet} specifies that traffic on the socket should interpret telnet
escape sequences.
+@option{reconnect} sets the timeout for reconnecting on non-server sockets when
+the remote end goes away. qemu will delay this many seconds and then attempt
+to reconnect. Zero disables reconnecting, and is the default.
+
TCP and unix socket options are given below:
@table @option
@@ -2687,14 +2691,16 @@ telnet on port 5555 to access the QEMU port.
localhost 5555
@end table
-@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay]
+@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay][,reconnect=@var{seconds}]
The TCP Net Console has two modes of operation. It can send the serial
I/O to a location or wait for a connection from a location. By default
the TCP Net Console is sent to @var{host} at the @var{port}. If you use
the @var{server} option QEMU will wait for a client socket application
to connect to the port before continuing, unless the @code{nowait}
option was specified. The @code{nodelay} option disables the Nagle buffering
-algorithm. If @var{host} is omitted, 0.0.0.0 is assumed. Only
+algorithm. The @code{reconnect} option only applies if @var{noserver} is
+set, if the connection goes down it will attempt to reconnect at the
+given interval. If @var{host} is omitted, 0.0.0.0 is assumed. Only
one TCP connection at a time is accepted. You can use @code{telnet} to
connect to the corresponding character device.
@table @code
@@ -2715,7 +2721,7 @@ MAGIC_SYSRQ sequence if you use a telnet that supports sending the break
sequence. Typically in unix telnet you do it with Control-] and then
type "send break" followed by pressing the enter key.
-@item unix:@var{path}[,server][,nowait]
+@item unix:@var{path}[,server][,nowait][,reconnect=@var{seconds}]
A unix domain socket is used instead of a tcp socket. The option works the
same as if you had specified @code{-serial tcp} except the unix domain socket
@var{path} is used for connections.
diff --git a/qtest.c b/qtest.c
index ef0d99191c..4b85995de0 100644
--- a/qtest.c
+++ b/qtest.c
@@ -17,6 +17,7 @@
#include "exec/ioport.h"
#include "exec/memory.h"
#include "hw/irq.h"
+#include "sysemu/accel.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
#include "qemu/config-file.h"
@@ -519,7 +520,7 @@ static void configure_qtest_icount(const char *options)
qemu_opts_del(opts);
}
-int qtest_init_accel(MachineClass *mc)
+static int qtest_init_accel(MachineState *ms)
{
configure_qtest_icount("0");
return 0;
@@ -557,3 +558,27 @@ bool qtest_driver(void)
{
return qtest_chr;
}
+
+static void qtest_accel_class_init(ObjectClass *oc, void *data)
+{
+ AccelClass *ac = ACCEL_CLASS(oc);
+ ac->name = "QTest";
+ ac->available = qtest_available;
+ ac->init_machine = qtest_init_accel;
+ ac->allowed = &qtest_allowed;
+}
+
+#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest")
+
+static const TypeInfo qtest_accel_type = {
+ .name = TYPE_QTEST_ACCEL,
+ .parent = TYPE_ACCEL,
+ .class_init = qtest_accel_class_init,
+};
+
+static void qtest_type_init(void)
+{
+ type_register_static(&qtest_accel_type);
+}
+
+type_init(qtest_type_init);
diff --git a/util/qemu-error.c b/util/qemu-error.c
index 7b167fd06b..9bba5f53d8 100644
--- a/util/qemu-error.c
+++ b/util/qemu-error.c
@@ -199,14 +199,13 @@ static void error_print_loc(void)
bool enable_timestamp_msg;
/*
* Print an error message to current monitor if we have one, else to stderr.
- * Format arguments like sprintf(). The result should not contain
+ * Format arguments like vsprintf(). The result should not contain
* newlines.
* Prepend the current location and append a newline.
* It's wrong to call this in a QMP monitor. Use qerror_report() there.
*/
-void error_report(const char *fmt, ...)
+void error_vreport(const char *fmt, va_list ap)
{
- va_list ap;
GTimeVal tv;
gchar *timestr;
@@ -218,8 +217,22 @@ void error_report(const char *fmt, ...)
}
error_print_loc();
- va_start(ap, fmt);
error_vprintf(fmt, ap);
- va_end(ap);
error_printf("\n");
}
+
+/*
+ * Print an error message to current monitor if we have one, else to stderr.
+ * Format arguments like sprintf(). The result should not contain
+ * newlines.
+ * Prepend the current location and append a newline.
+ * It's wrong to call this in a QMP monitor. Use qerror_report() there.
+ */
+void error_report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ error_vreport(fmt, ap);
+ va_end(ap);
+}
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 1eef590af5..a76bb3c913 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -234,6 +234,7 @@ static void wait_for_connect(void *opaque)
int val = 0, rc = 0;
socklen_t valsize = sizeof(val);
bool in_progress;
+ Error *err = NULL;
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
@@ -244,10 +245,12 @@ static void wait_for_connect(void *opaque)
/* update rc to contain error */
if (!rc && val) {
rc = -1;
+ errno = val;
}
/* connect error */
if (rc < 0) {
+ error_setg_errno(&err, errno, "Error connecting to socket");
closesocket(s->fd);
s->fd = rc;
}
@@ -257,9 +260,14 @@ static void wait_for_connect(void *opaque)
while (s->current_addr->ai_next != NULL && s->fd < 0) {
s->current_addr = s->current_addr->ai_next;
s->fd = inet_connect_addr(s->current_addr, &in_progress, s, NULL);
+ if (s->fd < 0) {
+ error_free(err);
+ err = NULL;
+ error_setg_errno(&err, errno, "Unable to start socket connect");
+ }
/* connect in progress */
if (in_progress) {
- return;
+ goto out;
}
}
@@ -267,9 +275,11 @@ static void wait_for_connect(void *opaque)
}
if (s->callback) {
- s->callback(s->fd, s->opaque);
+ s->callback(s->fd, err, s->opaque);
}
g_free(s);
+out:
+ error_free(err);
}
static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
@@ -401,7 +411,7 @@ int inet_connect_opts(QemuOpts *opts, Error **errp,
return sock;
} else {
if (callback) {
- callback(sock, opaque);
+ callback(sock, NULL, opaque);
}
}
g_free(connect_state);
@@ -769,7 +779,7 @@ int unix_connect_opts(QemuOpts *opts, Error **errp,
} else if (rc >= 0) {
/* non blocking socket immediate success, call callback */
if (callback != NULL) {
- callback(sock, opaque);
+ callback(sock, NULL, opaque);
}
}
@@ -919,7 +929,7 @@ int socket_connect(SocketAddress *addr, Error **errp,
fd = monitor_get_fd(cur_mon, addr->fd->str, errp);
if (fd >= 0 && callback) {
qemu_set_nonblock(fd);
- callback(fd, opaque);
+ callback(fd, NULL, opaque);
}
break;
diff --git a/vl.c b/vl.c
index debcbcceea..964d63403c 100644
--- a/vl.c
+++ b/vl.c
@@ -61,6 +61,7 @@ int main(int argc, char **argv)
#include "qemu/sockets.h"
#include "hw/hw.h"
#include "hw/boards.h"
+#include "sysemu/accel.h"
#include "hw/usb.h"
#include "hw/pcmcia.h"
#include "hw/i386/pc.h"
@@ -213,11 +214,9 @@ static NotifierList exit_notifiers =
static NotifierList machine_init_done_notifiers =
NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers);
-static bool tcg_allowed = true;
bool xen_allowed;
uint32_t xen_domid;
enum xen_mode xen_mode = XEN_EMULATE;
-static int tcg_tb_size;
static int has_defaults = 1;
static int default_serial = 1;
@@ -2681,84 +2680,6 @@ static MachineClass *machine_parse(const char *name)
exit(!name || !is_help_option(name));
}
-static int tcg_init(MachineClass *mc)
-{
- tcg_exec_init(tcg_tb_size * 1024 * 1024);
- return 0;
-}
-
-static struct {
- const char *opt_name;
- const char *name;
- int (*available)(void);
- int (*init)(MachineClass *mc);
- bool *allowed;
-} accel_list[] = {
- { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed },
- { "xen", "Xen", xen_available, xen_init, &xen_allowed },
- { "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed },
- { "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed },
-};
-
-static int configure_accelerator(MachineClass *mc)
-{
- const char *p;
- char buf[10];
- int i, ret;
- bool accel_initialised = false;
- bool init_failed = false;
-
- p = qemu_opt_get(qemu_get_machine_opts(), "accel");
- if (p == NULL) {
- /* Use the default "accelerator", tcg */
- p = "tcg";
- }
-
- while (!accel_initialised && *p != '\0') {
- if (*p == ':') {
- p++;
- }
- p = get_opt_name(buf, sizeof (buf), p, ':');
- for (i = 0; i < ARRAY_SIZE(accel_list); i++) {
- if (strcmp(accel_list[i].opt_name, buf) == 0) {
- if (!accel_list[i].available()) {
- printf("%s not supported for this target\n",
- accel_list[i].name);
- break;
- }
- *(accel_list[i].allowed) = true;
- ret = accel_list[i].init(mc);
- if (ret < 0) {
- init_failed = true;
- fprintf(stderr, "failed to initialize %s: %s\n",
- accel_list[i].name,
- strerror(-ret));
- *(accel_list[i].allowed) = false;
- } else {
- accel_initialised = true;
- }
- break;
- }
- }
- if (i == ARRAY_SIZE(accel_list)) {
- fprintf(stderr, "\"%s\" accelerator does not exist.\n", buf);
- }
- }
-
- if (!accel_initialised) {
- if (!init_failed) {
- fprintf(stderr, "No accelerator found!\n");
- }
- exit(1);
- }
-
- if (init_failed) {
- fprintf(stderr, "Back to %s accelerator.\n", accel_list[i].name);
- }
-
- return !accel_initialised;
-}
-
void qemu_add_exit_notifier(Notifier *notify)
{
notifier_list_add(&exit_notifiers, notify);
@@ -4264,7 +4185,7 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
- configure_accelerator(machine_class);
+ configure_accelerator(current_machine);
if (qtest_chrdev) {
Error *local_err = NULL;
diff --git a/xen-common-stub.c b/xen-common-stub.c
index bd56ca2ce5..906f991f1e 100644
--- a/xen-common-stub.c
+++ b/xen-common-stub.c
@@ -11,9 +11,3 @@
void xenstore_store_pv_console_info(int i, CharDriverState *chr)
{
}
-
-int xen_init(MachineClass *mc)
-{
- return -ENOSYS;
-}
-
diff --git a/xen-common.c b/xen-common.c
index f07b35e471..56359ca725 100644
--- a/xen-common.c
+++ b/xen-common.c
@@ -11,6 +11,7 @@
#include "hw/xen/xen_backend.h"
#include "qmp-commands.h"
#include "sysemu/char.h"
+#include "sysemu/accel.h"
//#define DEBUG_XEN
@@ -109,7 +110,7 @@ static void xen_change_state_handler(void *opaque, int running,
}
}
-int xen_init(MachineClass *mc)
+static int xen_init(MachineState *ms)
{
xen_xc = xen_xc_interface_open(0, 0, 0);
if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
@@ -121,3 +122,25 @@ int xen_init(MachineClass *mc)
return 0;
}
+static void xen_accel_class_init(ObjectClass *oc, void *data)
+{
+ AccelClass *ac = ACCEL_CLASS(oc);
+ ac->name = "Xen";
+ ac->init_machine = xen_init;
+ ac->allowed = &xen_allowed;
+}
+
+#define TYPE_XEN_ACCEL ACCEL_CLASS_NAME("xen")
+
+static const TypeInfo xen_accel_type = {
+ .name = TYPE_XEN_ACCEL,
+ .parent = TYPE_ACCEL,
+ .class_init = xen_accel_class_init,
+};
+
+static void xen_type_init(void)
+{
+ type_register_static(&xen_accel_type);
+}
+
+type_init(xen_type_init);