mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-27 13:30:52 +00:00
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJUNo9yAAoJEBRUblpOawnXQDkH/1M5DxmVwUv+SZtHEdpsT7Eq UGjRzfYXsYP/WkEqxVzYJmN0HJn9z8uJZin/dqwDPQLjCy8gf/xuaNCfoZqMuxHw iNaTgKpi9Uy0G0VWxjlZpRu8f5JjqHbJEP6aTT0hooBHaqQoBSm1fQh/pnCUvnpB qDQeHcOjrzIMkQJ3Ji8z2s+CapHaiIa63hJqRJztS5vbonPjngjj87dA54eIqDtQ RwXy58y+C8YMKqfpOG6lA+RxogESyyCfDBVUA1wwRDad1mOFKUMtEd4jAL39HUgR qINSKybG12V2bx8E+vNbaKB+68+NLdxcyR39JR2aun+a8yw+yDcF5BBiMXlqV0U= =4bqy -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
fcb2cd928f
@ -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
|
||||
|
157
accel.c
Normal file
157
accel.c
Normal file
@ -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);
|
@ -1337,11 +1337,6 @@ void cpudef_init(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
int tcg_available(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kvm_available(void)
|
||||
{
|
||||
#ifdef CONFIG_KVM
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
62
include/sysemu/accel.h
Normal file
62
include/sysemu/accel.h
Normal file
@ -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
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
40
kvm-all.c
40
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);
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Binary file not shown.
@ -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
|
||||
|
@ -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); \
|
||||
|
@ -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:
|
||||
|
359
qemu-char.c
359
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);
|
||||
TCPCharDriver *s = chr->opaque;
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
@ -3820,6 +3935,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,
|
||||
|
@ -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.
|
||||
|
27
qtest.c
27
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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
83
vl.c
83
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;
|
||||
|
@ -11,9 +11,3 @@
|
||||
void xenstore_store_pv_console_info(int i, CharDriverState *chr)
|
||||
{
|
||||
}
|
||||
|
||||
int xen_init(MachineClass *mc)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
|
25
xen-common.c
25
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);
|
||||
|
Loading…
Reference in New Issue
Block a user