summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fsdev/virtfs-proxy-helper.c3
-rw-r--r--hw/9pfs/9p.c194
-rw-r--r--hw/9pfs/9p.h2
-rw-r--r--hw/m68k/bootinfo.h48
-rw-r--r--hw/m68k/q800.c76
-rw-r--r--hw/m68k/virt.c57
-rw-r--r--include/standard-headers/asm-m68k/bootinfo-virt.h4
-rw-r--r--include/standard-headers/asm-m68k/bootinfo.h8
-rw-r--r--linux-user/cpu_loop-common.h15
-rw-r--r--linux-user/i386/cpu_loop.c6
-rw-r--r--linux-user/ioctls.h24
-rw-r--r--linux-user/strace.c6
-rw-r--r--linux-user/strace.list3
-rw-r--r--linux-user/syscall.c100
-rw-r--r--target/i386/cpu.c8
-rw-r--r--target/i386/cpu.h3
-rw-r--r--target/i386/ops_sse.h116
-rw-r--r--target/i386/ops_sse_header.h17
-rw-r--r--target/i386/tcg/decode-new.c.inc48
-rw-r--r--target/i386/tcg/decode-new.h3
-rw-r--r--target/i386/tcg/emit.c.inc68
-rw-r--r--target/i386/tcg/fpu_helper.c60
-rw-r--r--target/i386/tcg/translate.c1
-rw-r--r--tests/qtest/libqos/meson.build1
-rw-r--r--tests/qtest/libqos/virtio-9p-client.c1049
-rw-r--r--tests/qtest/libqos/virtio-9p-client.h494
-rw-r--r--tests/qtest/virtio-9p-test.c1301
-rw-r--r--tests/tcg/i386/test-avx.c17
-rwxr-xr-xtests/tcg/i386/test-avx.py8
29 files changed, 2340 insertions, 1400 deletions
diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
index 2dde27922f..5cafcd7703 100644
--- a/fsdev/virtfs-proxy-helper.c
+++ b/fsdev/virtfs-proxy-helper.c
@@ -10,6 +10,7 @@
*/
#include "qemu/osdep.h"
+#include <glib/gstdio.h>
#include <sys/resource.h>
#include <getopt.h>
#include <syslog.h>
@@ -639,7 +640,7 @@ static int do_create_others(int type, struct iovec *iovec)
if (retval < 0) {
goto err_out;
}
- retval = mkdir(path.data, mode);
+ retval = g_mkdir(path.data, mode);
break;
case T_SYMLINK:
retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path);
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 76c591a01b..072cf67956 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -256,7 +256,8 @@ static size_t v9fs_string_size(V9fsString *str)
}
/*
- * returns 0 if fid got re-opened, 1 if not, < 0 on error */
+ * returns 0 if fid got re-opened, 1 if not, < 0 on error
+ */
static int coroutine_fn v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f)
{
int err = 1;
@@ -282,33 +283,32 @@ static V9fsFidState *coroutine_fn get_fid(V9fsPDU *pdu, int32_t fid)
V9fsFidState *f;
V9fsState *s = pdu->s;
- QSIMPLEQ_FOREACH(f, &s->fid_list, next) {
+ f = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid));
+ if (f) {
BUG_ON(f->clunked);
- if (f->fid == fid) {
- /*
- * Update the fid ref upfront so that
- * we don't get reclaimed when we yield
- * in open later.
- */
- f->ref++;
- /*
- * check whether we need to reopen the
- * file. We might have closed the fd
- * while trying to free up some file
- * descriptors.
- */
- err = v9fs_reopen_fid(pdu, f);
- if (err < 0) {
- f->ref--;
- return NULL;
- }
- /*
- * Mark the fid as referenced so that the LRU
- * reclaim won't close the file descriptor
- */
- f->flags |= FID_REFERENCED;
- return f;
+ /*
+ * Update the fid ref upfront so that
+ * we don't get reclaimed when we yield
+ * in open later.
+ */
+ f->ref++;
+ /*
+ * check whether we need to reopen the
+ * file. We might have closed the fd
+ * while trying to free up some file
+ * descriptors.
+ */
+ err = v9fs_reopen_fid(pdu, f);
+ if (err < 0) {
+ f->ref--;
+ return NULL;
}
+ /*
+ * Mark the fid as referenced so that the LRU
+ * reclaim won't close the file descriptor
+ */
+ f->flags |= FID_REFERENCED;
+ return f;
}
return NULL;
}
@@ -317,12 +317,11 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
{
V9fsFidState *f;
- QSIMPLEQ_FOREACH(f, &s->fid_list, next) {
+ f = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid));
+ if (f) {
/* If fid is already there return NULL */
BUG_ON(f->clunked);
- if (f->fid == fid) {
- return NULL;
- }
+ return NULL;
}
f = g_new0(V9fsFidState, 1);
f->fid = fid;
@@ -333,7 +332,7 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
* reclaim won't close the file descriptor
*/
f->flags |= FID_REFERENCED;
- QSIMPLEQ_INSERT_TAIL(&s->fid_list, f, next);
+ g_hash_table_insert(s->fids, GINT_TO_POINTER(fid), f);
v9fs_readdir_init(s->proto_version, &f->fs.dir);
v9fs_readdir_init(s->proto_version, &f->fs_reclaim.dir);
@@ -424,12 +423,12 @@ static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid)
{
V9fsFidState *fidp;
- QSIMPLEQ_FOREACH(fidp, &s->fid_list, next) {
- if (fidp->fid == fid) {
- QSIMPLEQ_REMOVE(&s->fid_list, fidp, V9fsFidState, next);
- fidp->clunked = true;
- return fidp;
- }
+ /* TODO: Use g_hash_table_steal_extended() instead? */
+ fidp = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid));
+ if (fidp) {
+ g_hash_table_remove(s->fids, GINT_TO_POINTER(fid));
+ fidp->clunked = true;
+ return fidp;
}
return NULL;
}
@@ -439,10 +438,15 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu)
int reclaim_count = 0;
V9fsState *s = pdu->s;
V9fsFidState *f;
+ GHashTableIter iter;
+ gpointer fid;
+
+ g_hash_table_iter_init(&iter, s->fids);
+
QSLIST_HEAD(, V9fsFidState) reclaim_list =
QSLIST_HEAD_INITIALIZER(reclaim_list);
- QSIMPLEQ_FOREACH(f, &s->fid_list, next) {
+ while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &f)) {
/*
* Unlink fids cannot be reclaimed. Check
* for them and skip them. Also skip fids
@@ -514,72 +518,85 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu)
}
}
+/*
+ * This is used when a path is removed from the directory tree. Any
+ * fids that still reference it must not be closed from then on, since
+ * they cannot be reopened.
+ */
static int coroutine_fn v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path)
{
- int err;
+ int err = 0;
V9fsState *s = pdu->s;
- V9fsFidState *fidp, *fidp_next;
+ V9fsFidState *fidp;
+ gpointer fid;
+ GHashTableIter iter;
+ /*
+ * The most common case is probably that we have exactly one
+ * fid for the given path, so preallocate exactly one.
+ */
+ g_autoptr(GArray) to_reopen = g_array_sized_new(FALSE, FALSE,
+ sizeof(V9fsFidState *), 1);
+ gint i;
- fidp = QSIMPLEQ_FIRST(&s->fid_list);
- if (!fidp) {
- return 0;
- }
+ g_hash_table_iter_init(&iter, s->fids);
/*
- * v9fs_reopen_fid() can yield : a reference on the fid must be held
- * to ensure its pointer remains valid and we can safely pass it to
- * QSIMPLEQ_NEXT(). The corresponding put_fid() can also yield so
- * we must keep a reference on the next fid as well. So the logic here
- * is to get a reference on a fid and only put it back during the next
- * iteration after we could get a reference on the next fid. Start with
- * the first one.
+ * We iterate over the fid table looking for the entries we need
+ * to reopen, and store them in to_reopen. This is because
+ * v9fs_reopen_fid() and put_fid() yield. This allows the fid table
+ * to be modified in the meantime, invalidating our iterator.
*/
- for (fidp->ref++; fidp; fidp = fidp_next) {
+ while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &fidp)) {
if (fidp->path.size == path->size &&
!memcmp(fidp->path.data, path->data, path->size)) {
- /* Mark the fid non reclaimable. */
- fidp->flags |= FID_NON_RECLAIMABLE;
-
- /* reopen the file/dir if already closed */
- err = v9fs_reopen_fid(pdu, fidp);
- if (err < 0) {
- put_fid(pdu, fidp);
- return err;
- }
- }
-
- fidp_next = QSIMPLEQ_NEXT(fidp, next);
-
- if (fidp_next) {
/*
- * Ensure the next fid survives a potential clunk request during
- * put_fid() below and v9fs_reopen_fid() in the next iteration.
+ * Ensure the fid survives a potential clunk request during
+ * v9fs_reopen_fid or put_fid.
*/
- fidp_next->ref++;
+ fidp->ref++;
+ fidp->flags |= FID_NON_RECLAIMABLE;
+ g_array_append_val(to_reopen, fidp);
}
+ }
- /* We're done with this fid */
- put_fid(pdu, fidp);
+ for (i = 0; i < to_reopen->len; i++) {
+ fidp = g_array_index(to_reopen, V9fsFidState*, i);
+ /* reopen the file/dir if already closed */
+ err = v9fs_reopen_fid(pdu, fidp);
+ if (err < 0) {
+ break;
+ }
}
- return 0;
+ for (i = 0; i < to_reopen->len; i++) {
+ put_fid(pdu, g_array_index(to_reopen, V9fsFidState*, i));
+ }
+ return err;
}
static void coroutine_fn virtfs_reset(V9fsPDU *pdu)
{
V9fsState *s = pdu->s;
V9fsFidState *fidp;
+ GList *freeing;
+ /*
+ * Get a list of all the values (fid states) in the table, which
+ * we then...
+ */
+ g_autoptr(GList) fids = g_hash_table_get_values(s->fids);
- /* Free all fids */
- while (!QSIMPLEQ_EMPTY(&s->fid_list)) {
- /* Get fid */
- fidp = QSIMPLEQ_FIRST(&s->fid_list);
- fidp->ref++;
+ /* ... remove from the table, taking over ownership. */
+ g_hash_table_steal_all(s->fids);
- /* Clunk fid */
- QSIMPLEQ_REMOVE(&s->fid_list, fidp, V9fsFidState, next);
+ /*
+ * This allows us to release our references to them asynchronously without
+ * iterating over the hash table and risking iterator invalidation
+ * through concurrent modifications.
+ */
+ for (freeing = fids; freeing; freeing = freeing->next) {
+ fidp = freeing->data;
+ fidp->ref++;
fidp->clunked = true;
-
put_fid(pdu, fidp);
}
}
@@ -3205,6 +3222,8 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
V9fsFidState *tfidp;
V9fsState *s = pdu->s;
V9fsFidState *dirfidp = NULL;
+ GHashTableIter iter;
+ gpointer fid;
v9fs_path_init(&new_path);
if (newdirfid != -1) {
@@ -3238,11 +3257,13 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
if (err < 0) {
goto out;
}
+
/*
* Fixup fid's pointing to the old name to
* start pointing to the new name
*/
- QSIMPLEQ_FOREACH(tfidp, &s->fid_list, next) {
+ g_hash_table_iter_init(&iter, s->fids);
+ while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &tfidp)) {
if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) {
/* replace the name */
v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data));
@@ -3320,6 +3341,8 @@ static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
V9fsPath oldpath, newpath;
V9fsState *s = pdu->s;
int err;
+ GHashTableIter iter;
+ gpointer fid;
v9fs_path_init(&oldpath);
v9fs_path_init(&newpath);
@@ -3336,7 +3359,8 @@ static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
* Fixup fid's pointing to the old name to
* start pointing to the new name
*/
- QSIMPLEQ_FOREACH(tfidp, &s->fid_list, next) {
+ g_hash_table_iter_init(&iter, s->fids);
+ while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &tfidp)) {
if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) {
/* replace the name */
v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data));
@@ -4226,7 +4250,7 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
s->ctx.fmode = fse->fmode;
s->ctx.dmode = fse->dmode;
- QSIMPLEQ_INIT(&s->fid_list);
+ s->fids = g_hash_table_new(NULL, NULL);
qemu_co_rwlock_init(&s->rename_lock);
if (s->ops->init(&s->ctx, errp) < 0) {
@@ -4286,6 +4310,10 @@ void v9fs_device_unrealize_common(V9fsState *s)
if (s->ctx.fst) {
fsdev_throttle_cleanup(s->ctx.fst);
}
+ if (s->fids) {
+ g_hash_table_destroy(s->fids);
+ s->fids = NULL;
+ }
g_free(s->tag);
qp_table_destroy(&s->qpd_table);
qp_table_destroy(&s->qpp_table);
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index a523ac34a9..2fce4140d1 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -339,7 +339,7 @@ typedef struct {
struct V9fsState {
QLIST_HEAD(, V9fsPDU) free_list;
QLIST_HEAD(, V9fsPDU) active_list;
- QSIMPLEQ_HEAD(, V9fsFidState) fid_list;
+ GHashTable *fids;
FileOperations *ops;
FsContext ctx;
char *tag;
diff --git a/hw/m68k/bootinfo.h b/hw/m68k/bootinfo.h
index 897162b818..a3d37e3c80 100644
--- a/hw/m68k/bootinfo.h
+++ b/hw/m68k/bootinfo.h
@@ -12,66 +12,66 @@
#ifndef HW_M68K_BOOTINFO_H
#define HW_M68K_BOOTINFO_H
-#define BOOTINFO0(as, base, id) \
+#define BOOTINFO0(base, id) \
do { \
- stw_phys(as, base, id); \
+ stw_p(base, id); \
base += 2; \
- stw_phys(as, base, sizeof(struct bi_record)); \
+ stw_p(base, sizeof(struct bi_record)); \
base += 2; \
} while (0)
-#define BOOTINFO1(as, base, id, value) \
+#define BOOTINFO1(base, id, value) \
do { \
- stw_phys(as, base, id); \
+ stw_p(base, id); \
base += 2; \
- stw_phys(as, base, sizeof(struct bi_record) + 4); \
+ stw_p(base, sizeof(struct bi_record) + 4); \
base += 2; \
- stl_phys(as, base, value); \
+ stl_p(base, value); \
base += 4; \
} while (0)
-#define BOOTINFO2(as, base, id, value1, value2) \
+#define BOOTINFO2(base, id, value1, value2) \
do { \
- stw_phys(as, base, id); \
+ stw_p(base, id); \
base += 2; \
- stw_phys(as, base, sizeof(struct bi_record) + 8); \
+ stw_p(base, sizeof(struct bi_record) + 8); \
base += 2; \
- stl_phys(as, base, value1); \
+ stl_p(base, value1); \
base += 4; \
- stl_phys(as, base, value2); \
+ stl_p(base, value2); \
base += 4; \
} while (0)
-#define BOOTINFOSTR(as, base, id, string) \
+#define BOOTINFOSTR(base, id, string) \
do { \
int i; \
- stw_phys(as, base, id); \
+ stw_p(base, id); \
base += 2; \
- stw_phys(as, base, \
+ stw_p(base, \
(sizeof(struct bi_record) + strlen(string) + \
1 /* null termination */ + 3 /* padding */) & ~3); \
base += 2; \
for (i = 0; string[i]; i++) { \
- stb_phys(as, base++, string[i]); \
+ stb_p(base++, string[i]); \
} \
- stb_phys(as, base++, 0); \
- base = (base + 3) & ~3; \
+ stb_p(base++, 0); \
+ base = QEMU_ALIGN_PTR_UP(base, 4); \
} while (0)
-#define BOOTINFODATA(as, base, id, data, len) \
+#define BOOTINFODATA(base, id, data, len) \
do { \
int i; \
- stw_phys(as, base, id); \
+ stw_p(base, id); \
base += 2; \
- stw_phys(as, base, \
+ stw_p(base, \
(sizeof(struct bi_record) + len + \
2 /* length field */ + 3 /* padding */) & ~3); \
base += 2; \
- stw_phys(as, base, len); \
+ stw_p(base, len); \
base += 2; \
for (i = 0; i < len; ++i) { \
- stb_phys(as, base++, data[i]); \
+ stb_p(base++, data[i]); \
} \
- base = (base + 3) & ~3; \
+ base = QEMU_ALIGN_PTR_UP(base, 4); \
} while (0)
#endif
diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c
index 101ab0f803..e09e244ddc 100644
--- a/hw/m68k/q800.c
+++ b/hw/m68k/q800.c
@@ -23,6 +23,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/datadir.h"
+#include "qemu/guest-random.h"
#include "sysemu/sysemu.h"
#include "cpu.h"
#include "hw/boards.h"
@@ -320,11 +321,22 @@ static const TypeInfo glue_info = {
},
};
+typedef struct {
+ M68kCPU *cpu;
+ struct bi_record *rng_seed;
+} ResetInfo;
+
static void main_cpu_reset(void *opaque)
{
- M68kCPU *cpu = opaque;
+ ResetInfo *reset_info = opaque;
+ M68kCPU *cpu = reset_info->cpu;
CPUState *cs = CPU(cpu);
+ if (reset_info->rng_seed) {
+ qemu_guest_getrandom_nofail((void *)reset_info->rng_seed->data + 2,
+ be16_to_cpu(*(uint16_t *)reset_info->rng_seed->data));
+ }
+
cpu_reset(cs);
cpu->env.aregs[7] = ldl_phys(cs->as, 0);
cpu->env.pc = ldl_phys(cs->as, 4);
@@ -385,6 +397,8 @@ static void q800_init(MachineState *machine)
NubusBus *nubus;
DeviceState *glue;
DriveInfo *dinfo;
+ ResetInfo *reset_info;
+ uint8_t rng_seed[32];
linux_boot = (kernel_filename != NULL);
@@ -394,9 +408,12 @@ static void q800_init(MachineState *machine)
exit(1);
}
+ reset_info = g_new0(ResetInfo, 1);
+
/* init CPUs */
cpu = M68K_CPU(cpu_create(machine->cpu_type));
- qemu_register_reset(main_cpu_reset, cpu);
+ reset_info->cpu = cpu;
+ qemu_register_reset(main_cpu_reset, reset_info);
/* RAM */
memory_region_add_subregion(get_system_memory(), 0, machine->ram);
@@ -596,6 +613,14 @@ static void q800_init(MachineState *machine)
cs = CPU(cpu);
if (linux_boot) {
uint64_t high;
+ void *param_blob, *param_ptr, *param_rng_seed;
+
+ if (kernel_cmdline) {
+ param_blob = g_malloc(strlen(kernel_cmdline) + 1024);
+ } else {
+ param_blob = g_malloc(1024);
+ }
+
kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
&elf_entry, NULL, &high, NULL, 1,
EM_68K, 0, 0);
@@ -605,23 +630,24 @@ static void q800_init(MachineState *machine)
}
stl_phys(cs->as, 4, elf_entry); /* reset initial PC */
parameters_base = (high + 1) & ~1;
-
- BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_MAC);
- BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040);
- BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040);
- BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040);
- BOOTINFO1(cs->as, parameters_base, BI_MAC_CPUID, CPUB_68040);
- BOOTINFO1(cs->as, parameters_base, BI_MAC_MODEL, MAC_MODEL_Q800);
- BOOTINFO1(cs->as, parameters_base,
+ param_ptr = param_blob;
+
+ BOOTINFO1(param_ptr, BI_MACHTYPE, MACH_MAC);
+ BOOTINFO1(param_ptr, BI_FPUTYPE, FPU_68040);
+ BOOTINFO1(param_ptr, BI_MMUTYPE, MMU_68040);
+ BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68040);
+ BOOTINFO1(param_ptr, BI_MAC_CPUID, CPUB_68040);
+ BOOTINFO1(param_ptr, BI_MAC_MODEL, MAC_MODEL_Q800);
+ BOOTINFO1(param_ptr,
BI_MAC_MEMSIZE, ram_size >> 20); /* in MB */
- BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size);
- BOOTINFO1(cs->as, parameters_base, BI_MAC_VADDR,
+ BOOTINFO2(param_ptr, BI_MEMCHUNK, 0, ram_size);
+ BOOTINFO1(param_ptr, BI_MAC_VADDR,
VIDEO_BASE + macfb_mode->offset);
- BOOTINFO1(cs->as, parameters_base, BI_MAC_VDEPTH, graphic_depth);
- BOOTINFO1(cs->as, parameters_base, BI_MAC_VDIM,
+ BOOTINFO1(param_ptr, BI_MAC_VDEPTH, graphic_depth);
+ BOOTINFO1(param_ptr, BI_MAC_VDIM,
(graphic_height << 16) | graphic_width);
- BOOTINFO1(cs->as, parameters_base, BI_MAC_VROW, macfb_mode->stride);
- BOOTINFO1(cs->as, parameters_base, BI_MAC_SCCBASE, SCC_BASE);
+ BOOTINFO1(param_ptr, BI_MAC_VROW, macfb_mode->stride);
+ BOOTINFO1(param_ptr, BI_MAC_SCCBASE, SCC_BASE);
rom = g_malloc(sizeof(*rom));
memory_region_init_ram_ptr(rom, NULL, "m68k_fake_mac.rom",
@@ -630,10 +656,16 @@ static void q800_init(MachineState *machine)
memory_region_add_subregion(get_system_memory(), MACROM_ADDR, rom);
if (kernel_cmdline) {
- BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE,
+ BOOTINFOSTR(param_ptr, BI_COMMAND_LINE,
kernel_cmdline);
}
+ /* Pass seed to RNG. */
+ param_rng_seed = param_ptr;
+ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
+ BOOTINFODATA(param_ptr, BI_RNG_SEED,
+ rng_seed, sizeof(rng_seed));
+
/* load initrd */
if (initrd_filename) {
initrd_size = get_image_size(initrd_filename);
@@ -646,13 +678,19 @@ static void q800_init(MachineState *machine)
initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
load_image_targphys(initrd_filename, initrd_base,
ram_size - initrd_base);
- BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base,
+ BOOTINFO2(param_ptr, BI_RAMDISK, initrd_base,
initrd_size);
} else {
initrd_base = 0;
initrd_size = 0;
}
- BOOTINFO0(cs->as, parameters_base, BI_LAST);
+ BOOTINFO0(param_ptr, BI_LAST);
+ rom_add_blob_fixed_as("bootinfo", param_blob, param_ptr - param_blob,
+ parameters_base, cs->as);
+ reset_info->rng_seed = rom_ptr_for_as(cs->as, parameters_base,
+ param_ptr - param_blob) +
+ (param_rng_seed - param_blob);
+ g_free(param_blob);
} else {
uint8_t *ptr;
/* allocate and load BIOS */
diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c
index 2f3ffc0de6..89c4108eb5 100644
--- a/hw/m68k/virt.c
+++ b/hw/m68k/virt.c
@@ -89,6 +89,7 @@ typedef struct {
M68kCPU *cpu;
hwaddr initial_pc;
hwaddr initial_stack;
+ struct bi_record *rng_seed;
} ResetInfo;
static void main_cpu_reset(void *opaque)
@@ -97,6 +98,11 @@ static void main_cpu_reset(void *opaque)
M68kCPU *cpu = reset_info->cpu;
CPUState *cs = CPU(cpu);
+ if (reset_info->rng_seed) {
+ qemu_guest_getrandom_nofail((void *)reset_info->rng_seed->data + 2,
+ be16_to_cpu(*(uint16_t *)reset_info->rng_seed->data));
+ }
+
cpu_reset(cs);
cpu->env.aregs[7] = reset_info->initial_stack;
cpu->env.pc = reset_info->initial_pc;
@@ -212,6 +218,13 @@ static void virt_init(MachineState *machine)
if (kernel_filename) {
CPUState *cs = CPU(cpu);
uint64_t high;
+ void *param_blob, *param_ptr, *param_rng_seed;
+
+ if (kernel_cmdline) {
+ param_blob = g_malloc(strlen(kernel_cmdline) + 1024);
+ } else {
+ param_blob = g_malloc(1024);
+ }
kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
&elf_entry, NULL, &high, NULL, 1,
@@ -222,36 +235,38 @@ static void virt_init(MachineState *machine)
}
reset_info->initial_pc = elf_entry;
parameters_base = (high + 1) & ~1;
+ param_ptr = param_blob;
- BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_VIRT);
- BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040);
- BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040);
- BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040);
- BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size);
+ BOOTINFO1(param_ptr, BI_MACHTYPE, MACH_VIRT);
+ BOOTINFO1(param_ptr, BI_FPUTYPE, FPU_68040);
+ BOOTINFO1(param_ptr, BI_MMUTYPE, MMU_68040);
+ BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68040);
+ BOOTINFO2(param_ptr, BI_MEMCHUNK, 0, ram_size);
- BOOTINFO1(cs->as, parameters_base, BI_VIRT_QEMU_VERSION,
+ BOOTINFO1(param_ptr, BI_VIRT_QEMU_VERSION,
((QEMU_VERSION_MAJOR << 24) | (QEMU_VERSION_MINOR << 16) |
(QEMU_VERSION_MICRO << 8)));
- BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_PIC_BASE,
+ BOOTINFO2(param_ptr, BI_VIRT_GF_PIC_BASE,
VIRT_GF_PIC_MMIO_BASE, VIRT_GF_PIC_IRQ_BASE);
- BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_RTC_BASE,
+ BOOTINFO2(param_ptr, BI_VIRT_GF_RTC_BASE,
VIRT_GF_RTC_MMIO_BASE, VIRT_GF_RTC_IRQ_BASE);
- BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_TTY_BASE,
+ BOOTINFO2(param_ptr, BI_VIRT_GF_TTY_BASE,
VIRT_GF_TTY_MMIO_BASE, VIRT_GF_TTY_IRQ_BASE);
- BOOTINFO2(cs->as, parameters_base, BI_VIRT_CTRL_BASE,
+ BOOTINFO2(param_ptr, BI_VIRT_CTRL_BASE,
VIRT_CTRL_MMIO_BASE, VIRT_CTRL_IRQ_BASE);
- BOOTINFO2(cs->as, parameters_base, BI_VIRT_VIRTIO_BASE,
+ BOOTINFO2(param_ptr, BI_VIRT_VIRTIO_BASE,
VIRT_VIRTIO_MMIO_BASE, VIRT_VIRTIO_IRQ_BASE);
if (kernel_cmdline) {
- BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE,
+ BOOTINFOSTR(param_ptr, BI_COMMAND_LINE,
kernel_cmdline);
}
- /* Pass seed to RNG. */
- qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
- BOOTINFODATA(cs->as, parameters_base, BI_VIRT_RNG_SEED,
- rng_seed, sizeof(rng_seed));
+ /* Pass seed to RNG. */
+ param_rng_seed = param_ptr;
+ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
+ BOOTINFODATA(param_ptr, BI_RNG_SEED,
+ rng_seed, sizeof(rng_seed));
/* load initrd */
if (initrd_filename) {
@@ -265,13 +280,19 @@ static void virt_init(MachineState *machine)
initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
load_image_targphys(initrd_filename, initrd_base,
ram_size - initrd_base);
- BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base,
+ BOOTINFO2(param_ptr, BI_RAMDISK, initrd_base,
initrd_size);
} else {
initrd_base = 0;
initrd_size = 0;
}
- BOOTINFO0(cs->as, parameters_base, BI_LAST);
+ BOOTINFO0(param_ptr, BI_LAST);
+ rom_add_blob_fixed_as("bootinfo", param_blob, param_ptr - param_blob,
+ parameters_base, cs->as);
+ reset_info->rng_seed = rom_ptr_for_as(cs->as, parameters_base,
+ param_ptr - param_blob) +
+ (param_rng_seed - param_blob);
+ g_free(param_blob);
}
}
diff --git a/include/standard-headers/asm-m68k/bootinfo-virt.h b/include/standard-headers/asm-m68k/bootinfo-virt.h
index 1b1ffd4705..75ac6bbd7d 100644
--- a/include/standard-headers/asm-m68k/bootinfo-virt.h
+++ b/include/standard-headers/asm-m68k/bootinfo-virt.h
@@ -12,7 +12,9 @@
#define BI_VIRT_GF_TTY_BASE 0x8003
#define BI_VIRT_VIRTIO_BASE 0x8004
#define BI_VIRT_CTRL_BASE 0x8005
-#define BI_VIRT_RNG_SEED 0x8006
+
+/* No longer used -- replaced with BI_RNG_SEED -- but don't reuse this index:
+ * #define BI_VIRT_RNG_SEED 0x8006 */
#define VIRT_BOOTI_VERSION MK_BI_VERSION(2, 0)
diff --git a/include/standard-headers/asm-m68k/bootinfo.h b/include/standard-headers/asm-m68k/bootinfo.h
index 7b790e8ec8..b7a8dd2514 100644
--- a/include/standard-headers/asm-m68k/bootinfo.h
+++ b/include/standard-headers/asm-m68k/bootinfo.h
@@ -57,7 +57,13 @@ struct mem_info {
/* (struct mem_info) */
#define BI_COMMAND_LINE 0x0007 /* kernel command line parameters */
/* (string) */
-
+/*
+ * A random seed used to initialize the RNG. Record format:
+ *
+ * - length [ 2 bytes, 16-bit big endian ]
+ * - seed data [ `length` bytes, padded to preserve 4-byte struct alignment ]
+ */
+#define BI_RNG_SEED 0x0008
/*
* Linux/m68k Architectures (BI_MACHTYPE)
diff --git a/linux-user/cpu_loop-common.h b/linux-user/cpu_loop-common.h
index 36ff5b14f2..e644d2ef90 100644
--- a/linux-user/cpu_loop-common.h
+++ b/linux-user/cpu_loop-common.h
@@ -23,18 +23,9 @@
#include "exec/log.h"
#include "special-errno.h"
-#define EXCP_DUMP(env, fmt, ...) \
-do { \
- CPUState *cs = env_cpu(env); \
- fprintf(stderr, fmt , ## __VA_ARGS__); \
- fprintf(stderr, "Failing executable: %s\n", exec_path); \
- cpu_dump_state(cs, stderr, 0); \
- if (qemu_log_separate()) { \
- qemu_log(fmt, ## __VA_ARGS__); \
- qemu_log("Failing executable: %s\n", exec_path); \
- log_cpu_state(cs, 0); \
- } \
-} while (0)
+void target_exception_dump(CPUArchState *env, const char *fmt, int code);
+#define EXCP_DUMP(env, fmt, code) \
+ target_exception_dump(env, fmt, code)
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs);
#endif
diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c
index 42837399bc..865413c08f 100644
--- a/linux-user/i386/cpu_loop.c
+++ b/linux-user/i386/cpu_loop.c
@@ -201,7 +201,6 @@ void cpu_loop(CPUX86State *env)
{
CPUState *cs = env_cpu(env);
int trapnr;
- abi_ulong pc;
abi_ulong ret;
for(;;) {
@@ -307,9 +306,8 @@ void cpu_loop(CPUX86State *env)
cpu_exec_step_atomic(cs);
break;
default:
- pc = env->segs[R_CS].base + env->eip;
- EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
- (long)pc, trapnr);
+ EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n",
+ trapnr);
abort();
}
process_pending_signals(env);
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index f182d40190..071f7ca253 100644
--- a/linux-user/ioctls.h
+++ b/linux-user/ioctls.h
@@ -96,9 +96,7 @@
IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT))
IOCTL(BLKRRPART, 0, TYPE_NULL)
IOCTL(BLKGETSIZE, IOC_R, MK_PTR(TYPE_ULONG))
-#ifdef BLKGETSIZE64
IOCTL(BLKGETSIZE64, IOC_R, MK_PTR(TYPE_ULONGLONG))
-#endif
IOCTL(BLKFLSBUF, 0, TYPE_NULL)
IOCTL(BLKRASET, 0, TYPE_INT)
IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG))
@@ -107,33 +105,15 @@
IOCTL_SPECIAL(BLKPG, IOC_W, do_ioctl_blkpg,
MK_PTR(MK_STRUCT(STRUCT_blkpg_ioctl_arg)))
-#ifdef BLKDISCARD
IOCTL(BLKDISCARD, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2)))
-#endif
-#ifdef BLKIOMIN
IOCTL(BLKIOMIN, IOC_R, MK_PTR(TYPE_INT))
-#endif
-#ifdef BLKIOOPT
IOCTL(BLKIOOPT, IOC_R, MK_PTR(TYPE_INT))
-#endif
-#ifdef BLKALIGNOFF
IOCTL(BLKALIGNOFF, IOC_R, MK_PTR(TYPE_INT))
-#endif
-#ifdef BLKPBSZGET
IOCTL(BLKPBSZGET, IOC_R, MK_PTR(TYPE_INT))
-#endif
-#ifdef BLKDISCARDZEROES
IOCTL(BLKDISCARDZEROES, IOC_R, MK_PTR(TYPE_INT))
-#endif
-#ifdef BLKSECDISCARD
IOCTL(BLKSECDISCARD, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2)))
-#endif
-#ifdef BLKROTATIONAL
IOCTL(BLKROTATIONAL, IOC_R, MK_PTR(TYPE_SHORT))
-#endif
-#ifdef BLKZEROOUT
IOCTL(BLKZEROOUT, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2)))
-#endif
IOCTL(FDMSGON, 0, TYPE_NULL)
IOCTL(FDMSGOFF, 0, TYPE_NULL)
@@ -149,17 +129,13 @@
IOCTL(FDTWADDLE, 0, TYPE_NULL)
IOCTL(FDEJECT, 0, TYPE_NULL)
-#ifdef FIBMAP
IOCTL(FIBMAP, IOC_W | IOC_R, MK_PTR(TYPE_LONG))
-#endif
#ifdef FICLONE
IOCTL(FICLONE, IOC_W, TYPE_INT)
IOCTL(FICLONERANGE, IOC_W, MK_PTR(MK_STRUCT(STRUCT_file_clone_range)))
#endif
-#ifdef FIGETBSZ
IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG))
-#endif
#ifdef CONFIG_FIEMAP
IOCTL_SPECIAL(FS_IOC_FIEMAP, IOC_W | IOC_R, do_ioctl_fs_ioc_fiemap,
MK_PTR(MK_STRUCT(STRUCT_fiemap)))
diff --git a/linux-user/strace.c b/linux-user/strace.c
index 37bc96df9b..9ae5a812cd 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -1969,7 +1969,7 @@ print_execv(CPUArchState *cpu_env, const struct syscallname *name,
}
#endif
-#ifdef TARGET_NR_faccessat
+#if defined(TARGET_NR_faccessat) || defined(TARGET_NR_faccessat2)
static void
print_faccessat(CPUArchState *cpu_env, const struct syscallname *name,
abi_long arg0, abi_long arg1, abi_long arg2,
@@ -3383,10 +3383,10 @@ print_pidfd_send_signal(CPUArchState *cpu_env, const struct syscallname *name,
unlock_user(p, arg2, 0);
} else {
- print_pointer(arg2, 1);
+ print_pointer(arg2, 0);
}
- print_raw_param("%u", arg3, 0);
+ print_raw_param("%u", arg3, 1);
print_syscall_epilogue(name);
}
#endif
diff --git a/linux-user/strace.list b/linux-user/strace.list
index a87415bf3d..3df2184580 100644
--- a/linux-user/strace.list
+++ b/linux-user/strace.list
@@ -178,6 +178,9 @@
#ifdef TARGET_NR_faccessat
{ TARGET_NR_faccessat, "faccessat" , NULL, print_faccessat, NULL },
#endif
+#ifdef TARGET_NR_faccessat2
+{ TARGET_NR_faccessat2, "faccessat2" , NULL, print_faccessat, NULL },
+#endif
#ifdef TARGET_NR_fadvise64
{ TARGET_NR_fadvise64, "fadvise64" , NULL, NULL, NULL },
#endif
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 2e954d8dbd..8402c1399d 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -111,6 +111,31 @@
#define FS_IOC32_SETFLAGS _IOW('f', 2, int)
#define FS_IOC32_GETVERSION _IOR('v', 1, int)
#define FS_IOC32_SETVERSION _IOW('v', 2, int)
+
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#define BLKDISCARD _IO(0x12,119)
+#define BLKIOMIN _IO(0x12,120)
+#define BLKIOOPT _IO(0x12,121)
+#define BLKALIGNOFF _IO(0x12,122)
+#define BLKPBSZGET _IO(0x12,123)
+#define BLKDISCARDZEROES _IO(0x12,124)
+#define BLKSECDISCARD _IO(0x12,125)
+#define BLKROTATIONAL _IO(0x12,126)
+#define BLKZEROOUT _IO(0x12,127)
+
+#define FIBMAP _IO(0x00,1)
+#define FIGETBSZ _IO(0x00,2)
+
+struct file_clone_range {
+ __s64 src_fd;
+ __u64 src_offset;
+ __u64 src_length;
+ __u64 dest_offset;
+};
+
+#define FICLONE _IOW(0x94, 9, int)
+#define FICLONERANGE _IOW(0x94, 13, struct file_clone_range)
+
#else
#include <linux/fs.h>
#endif
@@ -158,6 +183,7 @@
#include "qapi/error.h"
#include "fd-trans.h"
#include "tcg/tcg.h"
+#include "cpu_loop-common.h"
#ifndef CLONE_IO
#define CLONE_IO 0x80000000 /* Clone io context */
@@ -8144,6 +8170,33 @@ static int is_proc_myself(const char *filename, const char *entry)
return 0;
}
+static void excp_dump_file(FILE *logfile, CPUArchState *env,
+ const char *fmt, int code)
+{
+ if (logfile) {
+ CPUState *cs = env_cpu(env);
+
+ fprintf(logfile, fmt, code);
+ fprintf(logfile, "Failing executable: %s\n", exec_path);
+ cpu_dump_state(cs, logfile, 0);
+ open_self_maps(env, fileno(logfile));
+ }
+}
+
+void target_exception_dump(CPUArchState *env, const char *fmt, int code)
+{
+ /* dump to console */
+ excp_dump_file(stderr, env, fmt, code);
+
+ /* dump to log file */
+ if (qemu_log_separate()) {
+ FILE *logfile = qemu_log_trylock();
+
+ excp_dump_file(logfile, env, fmt, code);
+ qemu_log_unlock(logfile);
+ }
+}
+
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN || \
defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA)
static int is_proc(const char *filename, const char *entry)
@@ -8251,8 +8304,7 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int
};
if (is_proc_myself(pathname, "exe")) {
- int execfd = qemu_getauxval(AT_EXECFD);
- return execfd ? execfd : safe_openat(dirfd, exec_path, flags, mode);
+ return safe_openat(dirfd, exec_path, flags, mode);
}
for (fake_open = fakes; fake_open->filename; fake_open++) {
@@ -8679,16 +8731,21 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
#if defined(__NR_pidfd_send_signal) && defined(TARGET_NR_pidfd_send_signal)
case TARGET_NR_pidfd_send_signal:
{
- siginfo_t uinfo;
+ siginfo_t uinfo, *puinfo;
- p = lock_user(VERIFY_READ, arg3, sizeof(target_siginfo_t), 1);
- if (!p) {
- return -TARGET_EFAULT;
+ if (arg3) {
+ p = lock_user(VERIFY_READ, arg3, sizeof(target_siginfo_t), 1);
+ if (!p) {
+ return -TARGET_EFAULT;
+ }
+ target_to_host_siginfo(&uinfo, p);
+ unlock_user(p, arg3, 0);
+ puinfo = &uinfo;
+ } else {
+ puinfo = NULL;
}
- target_to_host_siginfo(&uinfo, p);
- unlock_user(p, arg3, 0);
ret = get_errno(pidfd_send_signal(arg1, target_to_host_signal(arg2),
- &uinfo, arg4));
+ puinfo, arg4));
}
return ret;
#endif
@@ -8855,7 +8912,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
* before the execve completes and makes it the other
* program's problem.
*/
- ret = get_errno(safe_execve(p, argp, envp));
+ if (is_proc_myself(p, "exe")) {
+ ret = get_errno(safe_execve(exec_path, argp, envp));
+ } else {
+ ret = get_errno(safe_execve(p, argp, envp));
+ }
unlock_user(p, arg1, 0);
goto execve_end;
@@ -9110,6 +9171,15 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
unlock_user(p, arg2, 0);
return ret;
#endif
+#if defined(TARGET_NR_faccessat2)
+ case TARGET_NR_faccessat2:
+ if (!(p = lock_user_string(arg2))) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(faccessat(arg1, p, arg3, arg4));
+ unlock_user(p, arg2, 0);
+ return ret;
+#endif
#ifdef TARGET_NR_nice /* not on alpha */
case TARGET_NR_nice:
return get_errno(nice(arg1));
@@ -11793,7 +11863,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
return -host_to_target_errno(ret);
#endif
-#if TARGET_ABI_BITS == 32
+#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32)
#ifdef TARGET_NR_fadvise64_64
case TARGET_NR_fadvise64_64:
@@ -11920,7 +11990,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
return get_errno(sys_gettid());
#ifdef TARGET_NR_readahead
case TARGET_NR_readahead:
-#if TARGET_ABI_BITS == 32
+#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32)
if (regpairs_aligned(cpu_env, num)) {
arg2 = arg3;
arg3 = arg4;
@@ -12612,7 +12682,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
#endif /* CONFIG_EVENTFD */
#if defined(CONFIG_FALLOCATE) && defined(TARGET_NR_fallocate)
case TARGET_NR_fallocate:
-#if TARGET_ABI_BITS == 32
+#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32)
ret = get_errno(fallocate(arg1, arg2, target_offset64(arg3, arg4),
target_offset64(arg5, arg6)));
#else
@@ -12623,7 +12693,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
#if defined(CONFIG_SYNC_FILE_RANGE)
#if defined(TARGET_NR_sync_file_range)
case TARGET_NR_sync_file_range:
-#if TARGET_ABI_BITS == 32
+#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32)
#if defined(TARGET_MIPS)
ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4),
target_offset64(arg5, arg6), arg7));
@@ -12645,7 +12715,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
case TARGET_NR_arm_sync_file_range:
#endif
/* This is like sync_file_range but the arguments are reordered */
-#if TARGET_ABI_BITS == 32
+#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32)
ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4),
target_offset64(arg5, arg6), arg2));
#else
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 0ebd610faa..22b681ca37 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -625,13 +625,13 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_POPCNT | \
CPUID_EXT_XSAVE | /* CPUID_EXT_OSXSAVE is dynamic */ \
CPUID_EXT_MOVBE | CPUID_EXT_AES | CPUID_EXT_HYPERVISOR | \
- CPUID_EXT_RDRAND | CPUID_EXT_AVX)
+ CPUID_EXT_RDRAND | CPUID_EXT_AVX | CPUID_EXT_F16C | \
+ CPUID_EXT_FMA)
/* missing:
CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_SMX,
- CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_CID, CPUID_EXT_FMA,
+ CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_CID,
CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_PCID, CPUID_EXT_DCA,
- CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER,
- CPUID_EXT_F16C */
+ CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER */
#ifdef TARGET_X86_64
#define TCG_EXT2_X86_64_FEATURES (CPUID_EXT2_SYSCALL | CPUID_EXT2_LM)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index dad2b2db8d..d4bc19577a 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1258,6 +1258,7 @@ typedef union ZMMReg {
uint16_t _w_ZMMReg[512 / 16];
uint32_t _l_ZMMReg[512 / 32];
uint64_t _q_ZMMReg[512 / 64];
+ float16 _h_ZMMReg[512 / 16];
float32 _s_ZMMReg[512 / 32];
float64 _d_ZMMReg[512 / 64];
XMMReg _x_ZMMReg[512 / 128];
@@ -1282,6 +1283,7 @@ typedef struct BNDCSReg {
#define ZMM_B(n) _b_ZMMReg[63 - (n)]
#define ZMM_W(n) _w_ZMMReg[31 - (n)]
#define ZMM_L(n) _l_ZMMReg[15 - (n)]
+#define ZMM_H(n) _h_ZMMReg[31 - (n)]
#define ZMM_S(n) _s_ZMMReg[15 - (n)]
#define ZMM_Q(n) _q_ZMMReg[7 - (n)]
#define ZMM_D(n) _d_ZMMReg[7 - (n)]
@@ -1301,6 +1303,7 @@ typedef struct BNDCSReg {
#define ZMM_B(n) _b_ZMMReg[n]
#define ZMM_W(n) _w_ZMMReg[n]
#define ZMM_L(n) _l_ZMMReg[n]
+#define ZMM_H(n) _h_ZMMReg[n]
#define ZMM_S(n) _s_ZMMReg[n]
#define ZMM_Q(n) _q_ZMMReg[n]
#define ZMM_D(n) _d_ZMMReg[n]
diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h
index d35fc15c65..3cbc36a59d 100644
--- a/target/i386/ops_sse.h
+++ b/target/i386/ops_sse.h
@@ -586,6 +586,35 @@ void glue(helper_cvtpd2ps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)
}
}
+#if SHIFT >= 1
+void glue(helper_cvtph2ps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)
+{
+ int i;
+
+ for (i = 2 << SHIFT; --i >= 0; ) {
+ d->ZMM_S(i) = float16_to_float32(s->ZMM_H(i), true, &env->sse_status);
+ }
+}
+
+void glue(helper_cvtps2ph, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, int mode)
+{
+ int i;
+ FloatRoundMode prev_rounding_mode = env->sse_status.float_rounding_mode;
+ if (!(mode & (1 << 2))) {
+ set_x86_rounding_mode(mode & 3, &env->sse_status);
+ }
+
+ for (i = 0; i < 2 << SHIFT; i++) {
+ d->ZMM_H(i) = float32_to_float16(s->ZMM_S(i), true, &env->sse_status);
+ }
+ for (i >>= 2; i < 1 << SHIFT; i++) {
+ d->Q(i) = 0;
+ }
+
+ env->sse_status.float_rounding_mode = prev_rounding_mode;
+}
+#endif
+
#if SHIFT == 1
void helper_cvtss2sd(CPUX86State *env, Reg *d, Reg *v, Reg *s)
{
@@ -1684,20 +1713,7 @@ void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
prev_rounding_mode = env->sse_status.float_rounding_mode;
if (!(mode & (1 << 2))) {
- switch (mode & 3) {
- case 0:
- set_float_rounding_mode(float_round_nearest_even, &env->sse_status);
- break;
- case 1:
- set_float_rounding_mode(float_round_down, &env->sse_status);
- break;
- case 2:
- set_float_rounding_mode(float_round_up, &env->sse_status);
- break;
- case 3:
- set_float_rounding_mode(float_round_to_zero, &env->sse_status);
- break;
- }
+ set_x86_rounding_mode(mode & 3, &env->sse_status);
}
for (i = 0; i < 2 << SHIFT; i++) {
@@ -1721,20 +1737,7 @@ void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
prev_rounding_mode = env->sse_status.float_rounding_mode;
if (!(mode & (1 << 2))) {
- switch (mode & 3) {
- case 0:
- set_float_rounding_mode(float_round_nearest_even, &env->sse_status);
- break;
- case 1:
- set_float_rounding_mode(float_round_down, &env->sse_status);
- break;
- case 2:
- set_float_rounding_mode(float_round_up, &env->sse_status);
- break;
- case 3:
- set_float_rounding_mode(float_round_to_zero, &env->sse_status);
- break;
- }
+ set_x86_rounding_mode(mode & 3, &env->sse_status);
}
for (i = 0; i < 1 << SHIFT; i++) {
@@ -1759,20 +1762,7 @@ void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s,
prev_rounding_mode = env->sse_status.float_rounding_mode;
if (!(mode & (1 << 2))) {
- switch (mode & 3) {
- case 0:
- set_float_rounding_mode(float_round_nearest_even, &env->sse_status);
- break;
- case 1:
- set_float_rounding_mode(float_round_down, &env->sse_status);
- break;
- case 2:
- set_float_rounding_mode(float_round_up, &env->sse_status);
- break;
- case 3:
- set_float_rounding_mode(float_round_to_zero, &env->sse_status);
- break;
- }
+ set_x86_rounding_mode(mode & 3, &env->sse_status);
}
d->ZMM_S(0) = float32_round_to_int(s->ZMM_S(0), &env->sse_status);
@@ -1797,20 +1787,7 @@ void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s,
prev_rounding_mode = env->sse_status.float_rounding_mode;
if (!(mode & (1 << 2))) {
- switch (mode & 3) {
- case 0:
- set_float_rounding_mode(float_round_nearest_even, &env->sse_status);
- break;
- case 1:
- set_float_rounding_mode(float_round_down, &env->sse_status);
- break;
- case 2:
- set_float_rounding_mode(float_round_up, &env->sse_status);
- break;
- case 3:
- set_float_rounding_mode(float_round_to_zero, &env->sse_status);
- break;
- }
+ set_x86_rounding_mode(mode & 3, &env->sse_status);
}
d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status);
@@ -2545,6 +2522,33 @@ void helper_vpermd_ymm(Reg *d, Reg *v, Reg *s)
}
#endif
+/* FMA3 op helpers */
+#if SHIFT == 1
+#define SSE_HELPER_FMAS(name, elem, F) \
+ void name(CPUX86State *env, Reg *d, Reg *a, Reg *b, Reg *c, int flags) \
+ { \
+ d->elem(0) = F(a->elem(0), b->elem(0), c->elem(0), flags, &env->sse_status); \
+ }
+#define SSE_HELPER_FMAP(name, elem, num, F) \
+ void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *a, Reg *b, Reg *c, \
+ int flags, int flip) \
+ { \
+ int i; \
+ for (i = 0; i < num; i++) { \
+ d->elem(i) = F(a->elem(i), b->elem(i), c->elem(i), flags, &env->sse_status); \
+ flags ^= flip; \
+ } \
+ }
+
+SSE_HELPER_FMAS(helper_fma4ss, ZMM_S, float32_muladd)
+SSE_HELPER_FMAS(helper_fma4sd, ZMM_D, float64_muladd)
+#endif
+
+#if SHIFT >= 1
+SSE_HELPER_FMAP(helper_fma4ps, ZMM_S, 2 << SHIFT, float32_muladd)
+SSE_HELPER_FMAP(helper_fma4pd, ZMM_D, 1 << SHIFT, float64_muladd)
+#endif
+
#undef SSE_HELPER_S
#undef LANE_WIDTH
diff --git a/target/i386/ops_sse_header.h b/target/i386/ops_sse_header.h
index 2f1f811f9f..8a7b2f4e2f 100644
--- a/target/i386/ops_sse_header.h
+++ b/target/i386/ops_sse_header.h
@@ -353,6 +353,23 @@ DEF_HELPER_4(glue(aeskeygenassist, SUFFIX), void, env, Reg, Reg, i32)
DEF_HELPER_5(glue(pclmulqdq, SUFFIX), void, env, Reg, Reg, Reg, i32)
#endif
+/* F16C helpers */
+#if SHIFT >= 1
+DEF_HELPER_3(glue(cvtph2ps, SUFFIX), void, env, Reg, Reg)
+DEF_HELPER_4(glue(cvtps2ph, SUFFIX), void, env, Reg, Reg, int)
+#endif
+
+/* FMA3 helpers */
+#if SHIFT == 1
+DEF_HELPER_6(fma4ss, void, env, Reg, Reg, Reg, Reg, int)
+DEF_HELPER_6(fma4sd, void, env, Reg, Reg, Reg, Reg, int)
+#endif
+
+#if SHIFT >= 1
+DEF_HELPER_7(glue(fma4ps, SUFFIX), void, env, Reg, Reg, Reg, Reg, int, int)
+DEF_HELPER_7(glue(fma4pd, SUFFIX), void, env, Reg, Reg, Reg, Reg, int, int)
+#endif
+
/* AVX helpers */
#if SHIFT >= 1
DEF_HELPER_4(glue(vpermilpd, SUFFIX), void, env, Reg, Reg, Reg)
diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc
index 8e1eb9db42..e4878b967f 100644
--- a/target/i386/tcg/decode-new.c.inc
+++ b/target/i386/tcg/decode-new.c.inc
@@ -336,6 +336,7 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = {
[0x07] = X86_OP_ENTRY3(PHSUBSW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66),
[0x10] = X86_OP_ENTRY2(PBLENDVB, V,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66),
+ [0x13] = X86_OP_ENTRY2(VCVTPH2PS, V,x, W,ph, vex11 cpuid(F16C) p_66),
[0x14] = X86_OP_ENTRY2(BLENDVPS, V,x, W,x, vex4 cpuid(SSE41) p_66),
[0x15] = X86_OP_ENTRY2(BLENDVPD, V,x, W,x, vex4 cpuid(SSE41) p_66),
/* Listed incorrectly as type 4 */
@@ -375,6 +376,16 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = {
[0x92] = X86_OP_ENTRY3(VPGATHERD, V,x, H,x, M,d, vex12 cpuid(AVX2) p_66), /* vgatherdps/d */
[0x93] = X86_OP_ENTRY3(VPGATHERQ, V,x, H,x, M,q, vex12 cpuid(AVX2) p_66), /* vgatherqps/d */
+ /* Should be exception type 2 but they do not have legacy SSE equivalents? */
+ [0x96] = X86_OP_ENTRY3(VFMADDSUB132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0x97] = X86_OP_ENTRY3(VFMSUBADD132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+
+ [0xa6] = X86_OP_ENTRY3(VFMADDSUB213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xa7] = X86_OP_ENTRY3(VFMSUBADD213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+
+ [0xb6] = X86_OP_ENTRY3(VFMADDSUB231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xb7] = X86_OP_ENTRY3(VFMSUBADD231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+
[0x08] = X86_OP_ENTRY3(PSIGNB, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66),
[0x09] = X86_OP_ENTRY3(PSIGNW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66),
[0x0a] = X86_OP_ENTRY3(PSIGND, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66),
@@ -420,6 +431,34 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = {
[0x8c] = X86_OP_ENTRY3(VPMASKMOV, V,x, H,x, WM,x, vex6 cpuid(AVX2) p_66),
[0x8e] = X86_OP_ENTRY3(VPMASKMOV_st, M,x, V,x, H,x, vex6 cpuid(AVX2) p_66),
+ /* Should be exception type 2 or 3 but they do not have legacy SSE equivalents? */
+ [0x98] = X86_OP_ENTRY3(VFMADD132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0x99] = X86_OP_ENTRY3(VFMADD132Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0x9a] = X86_OP_ENTRY3(VFMSUB132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0x9b] = X86_OP_ENTRY3(VFMSUB132Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0x9c] = X86_OP_ENTRY3(VFNMADD132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0x9d] = X86_OP_ENTRY3(VFNMADD132Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0x9e] = X86_OP_ENTRY3(VFNMSUB132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0x9f] = X86_OP_ENTRY3(VFNMSUB132Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+
+ [0xa8] = X86_OP_ENTRY3(VFMADD213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xa9] = X86_OP_ENTRY3(VFMADD213Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xaa] = X86_OP_ENTRY3(VFMSUB213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xab] = X86_OP_ENTRY3(VFMSUB213Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xac] = X86_OP_ENTRY3(VFNMADD213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xad] = X86_OP_ENTRY3(VFNMADD213Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xae] = X86_OP_ENTRY3(VFNMSUB213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xaf] = X86_OP_ENTRY3(VFNMSUB213Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+
+ [0xb8] = X86_OP_ENTRY3(VFMADD231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xb9] = X86_OP_ENTRY3(VFMADD231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xba] = X86_OP_ENTRY3(VFMSUB231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xbb] = X86_OP_ENTRY3(VFMSUB231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xbc] = X86_OP_ENTRY3(VFNMADD231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xbd] = X86_OP_ENTRY3(VFNMADD231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xbe] = X86_OP_ENTRY3(VFNMSUB231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+ [0xbf] = X86_OP_ENTRY3(VFNMSUB231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66),
+
[0xdb] = X86_OP_ENTRY3(VAESIMC, V,dq, None,None, W,dq, vex4 cpuid(AES) p_66),
[0xdc] = X86_OP_ENTRY3(VAESENC, V,x, H,x, W,x, vex4 cpuid(AES) p_66),
[0xdd] = X86_OP_ENTRY3(VAESENCLAST, V,x, H,x, W,x, vex4 cpuid(AES) p_66),
@@ -525,6 +564,7 @@ static const X86OpEntry opcodes_0F3A[256] = {
[0x15] = X86_OP_ENTRY3(PEXTRW, E,w, V,dq, I,b, vex5 cpuid(SSE41) zext0 p_66),
[0x16] = X86_OP_ENTRY3(PEXTR, E,y, V,dq, I,b, vex5 cpuid(SSE41) p_66),
[0x17] = X86_OP_ENTRY3(VEXTRACTPS, E,d, V,dq, I,b, vex5 cpuid(SSE41) p_66),
+ [0x1d] = X86_OP_ENTRY3(VCVTPS2PH, W,ph, V,x, I,b, vex11 cpuid(F16C) p_66),
[0x20] = X86_OP_ENTRY4(PINSRB, V,dq, H,dq, E,b, vex5 cpuid(SSE41) zext2 p_66),
[0x21] = X86_OP_GROUP0(VINSERTPS),
@@ -1051,6 +1091,10 @@ static bool decode_op_size(DisasContext *s, X86OpEntry *e, X86OpSize size, MemOp
*ot = s->vex_l ? MO_256 : MO_128;
return true;
+ case X86_SIZE_ph: /* SSE/AVX packed half precision */
+ *ot = s->vex_l ? MO_128 : MO_64;
+ return true;
+
case X86_SIZE_d64: /* Default to 64-bit in 64-bit mode */
*ot = CODE64(s) && s->dflag == MO_32 ? MO_64 : s->dflag;
return true;
@@ -1342,6 +1386,10 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid)
switch (cpuid) {
case X86_FEAT_None:
return true;
+ case X86_FEAT_F16C:
+ return (s->cpuid_ext_features & CPUID_EXT_F16C);
+ case X86_FEAT_FMA:
+ return (s->cpuid_ext_features & CPUID_EXT_FMA);
case X86_FEAT_MOVBE:
return (s->cpuid_ext_features & CPUID_EXT_MOVBE);
case X86_FEAT_PCLMULQDQ:
diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h
index f159c26850..cb6b8bcf67 100644
--- a/target/i386/tcg/decode-new.h
+++ b/target/i386/tcg/decode-new.h
@@ -92,6 +92,7 @@ typedef enum X86OpSize {
/* Custom */
X86_SIZE_d64,
X86_SIZE_f64,
+ X86_SIZE_ph, /* SSE/AVX packed half precision */
} X86OpSize;
typedef enum X86CPUIDFeature {
@@ -103,6 +104,8 @@ typedef enum X86CPUIDFeature {
X86_FEAT_AVX2,
X86_FEAT_BMI1,
X86_FEAT_BMI2,
+ X86_FEAT_F16C,
+ X86_FEAT_FMA,
X86_FEAT_MOVBE,
X86_FEAT_PCLMULQDQ,
X86_FEAT_SSE,
diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc
index 27eca591a9..7037ff91c6 100644
--- a/target/i386/tcg/emit.c.inc
+++ b/target/i386/tcg/emit.c.inc
@@ -39,6 +39,11 @@ typedef void (*SSEFunc_0_eppt)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b,
TCGv val);
typedef void (*SSEFunc_0_epppti)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b,
TCGv_ptr reg_c, TCGv a0, TCGv_i32 scale);
+typedef void (*SSEFunc_0_eppppi)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b,
+ TCGv_ptr reg_c, TCGv_ptr reg_d, TCGv_i32 flags);
+typedef void (*SSEFunc_0_eppppii)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b,
+ TCGv_ptr reg_c, TCGv_ptr reg_d, TCGv_i32 even,
+ TCGv_i32 odd);
static inline TCGv_i32 tcg_constant8u_i32(uint8_t val)
{
@@ -296,7 +301,7 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv
case X86_OP_MMX:
break;
case X86_OP_SSE:
- if ((s->prefix & PREFIX_VEX) && op->ot == MO_128) {
+ if (!op->has_ea && (s->prefix & PREFIX_VEX) && op->ot <= MO_128) {
tcg_gen_gvec_dup_imm(MO_64,
offsetof(CPUX86State, xmm_regs[op->n].ZMM_X(1)),
16, 16, 0);
@@ -491,6 +496,52 @@ FP_SSE(VMIN, min)
FP_SSE(VDIV, div)
FP_SSE(VMAX, max)
+#define FMA_SSE_PACKED(uname, ptr0, ptr1, ptr2, even, odd) \
+static void gen_##uname##Px(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \
+{ \
+ SSEFunc_0_eppppii xmm = s->vex_w ? gen_helper_fma4pd_xmm : gen_helper_fma4ps_xmm; \
+ SSEFunc_0_eppppii ymm = s->vex_w ? gen_helper_fma4pd_ymm : gen_helper_fma4ps_ymm; \
+ SSEFunc_0_eppppii fn = s->vex_l ? ymm : xmm; \
+ \
+ fn(cpu_env, OP_PTR0, ptr0, ptr1, ptr2, \
+ tcg_constant_i32(even), \
+ tcg_constant_i32((even) ^ (odd))); \
+}
+
+#define FMA_SSE(uname, ptr0, ptr1, ptr2, flags) \
+FMA_SSE_PACKED(uname, ptr0, ptr1, ptr2, flags, flags) \
+static void gen_##uname##Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \
+{ \
+ SSEFunc_0_eppppi fn = s->vex_w ? gen_helper_fma4sd : gen_helper_fma4ss; \
+ \
+ fn(cpu_env, OP_PTR0, ptr0, ptr1, ptr2, \
+ tcg_constant_i32(flags)); \
+} \
+
+FMA_SSE(VFMADD231, OP_PTR1, OP_PTR2, OP_PTR0, 0)
+FMA_SSE(VFMADD213, OP_PTR1, OP_PTR0, OP_PTR2, 0)
+FMA_SSE(VFMADD132, OP_PTR0, OP_PTR2, OP_PTR1, 0)
+
+FMA_SSE(VFNMADD231, OP_PTR1, OP_PTR2, OP_PTR0, float_muladd_negate_product)
+FMA_SSE(VFNMADD213, OP_PTR1, OP_PTR0, OP_PTR2, float_muladd_negate_product)
+FMA_SSE(VFNMADD132, OP_PTR0, OP_PTR2, OP_PTR1, float_muladd_negate_product)
+
+FMA_SSE(VFMSUB231, OP_PTR1, OP_PTR2, OP_PTR0, float_muladd_negate_c)
+FMA_SSE(VFMSUB213, OP_PTR1, OP_PTR0, OP_PTR2, float_muladd_negate_c)
+FMA_SSE(VFMSUB132, OP_PTR0, OP_PTR2, OP_PTR1, float_muladd_negate_c)
+
+FMA_SSE(VFNMSUB231, OP_PTR1, OP_PTR2, OP_PTR0, float_muladd_negate_c|float_muladd_negate_product)
+FMA_SSE(VFNMSUB213, OP_PTR1, OP_PTR0, OP_PTR2, float_muladd_negate_c|float_muladd_negate_product)
+FMA_SSE(VFNMSUB132, OP_PTR0, OP_PTR2, OP_PTR1, float_muladd_negate_c|float_muladd_negate_product)
+
+FMA_SSE_PACKED(VFMADDSUB231, OP_PTR1, OP_PTR2, OP_PTR0, float_muladd_negate_c, 0)
+FMA_SSE_PACKED(VFMADDSUB213, OP_PTR1, OP_PTR0, OP_PTR2, float_muladd_negate_c, 0)
+FMA_SSE_PACKED(VFMADDSUB132, OP_PTR0, OP_PTR2, OP_PTR1, float_muladd_negate_c, 0)
+
+FMA_SSE_PACKED(VFMSUBADD231, OP_PTR1, OP_PTR2, OP_PTR0, 0, float_muladd_negate_c)
+FMA_SSE_PACKED(VFMSUBADD213, OP_PTR1, OP_PTR0, OP_PTR2, 0, float_muladd_negate_c)
+FMA_SSE_PACKED(VFMSUBADD132, OP_PTR0, OP_PTR2, OP_PTR1, 0, float_muladd_negate_c)
+
#define FP_UNPACK_SSE(uname, lname) \
static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \
{ \
@@ -852,6 +903,7 @@ UNARY_INT_SSE(VCVTTPD2DQ, cvttpd2dq)
UNARY_INT_SSE(VCVTDQ2PS, cvtdq2ps)
UNARY_INT_SSE(VCVTPS2DQ, cvtps2dq)
UNARY_INT_SSE(VCVTTPS2DQ, cvttps2dq)
+UNARY_INT_SSE(VCVTPH2PS, cvtph2ps)
static inline void gen_unary_imm_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode,
@@ -1868,6 +1920,20 @@ static void gen_VCVTfp2fp(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec
gen_helper_cvtsd2ss, gen_helper_cvtss2sd);
}
+static void gen_VCVTPS2PH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_unary_imm_fp_sse(s, env, decode,
+ gen_helper_cvtps2ph_xmm,
+ gen_helper_cvtps2ph_ymm);
+ /*
+ * VCVTPS2PH is the only instruction that performs an operation on a
+ * register source and then *stores* into memory.
+ */
+ if (decode->op[0].has_ea) {
+ gen_store_sse(s, decode, decode->op[0].offset);
+ }
+}
+
static void gen_VCVTSI2Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
int vec_len = vector_len(s, decode);
diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c
index a6a90a1817..6f3741b635 100644
--- a/target/i386/tcg/fpu_helper.c
+++ b/target/i386/tcg/fpu_helper.c
@@ -32,7 +32,8 @@
#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7].d)
#define ST1 ST(1)
-#define FPU_RC_MASK 0xc00
+#define FPU_RC_SHIFT 10
+#define FPU_RC_MASK (3 << FPU_RC_SHIFT)
#define FPU_RC_NEAR 0x000
#define FPU_RC_DOWN 0x400
#define FPU_RC_UP 0x800
@@ -685,28 +686,26 @@ uint32_t helper_fnstcw(CPUX86State *env)
return env->fpuc;
}
+static void set_x86_rounding_mode(unsigned mode, float_status *status)
+{
+ static FloatRoundMode x86_round_mode[4] = {
+ float_round_nearest_even,
+ float_round_down,
+ float_round_up,
+ float_round_to_zero
+ };
+ assert(mode < ARRAY_SIZE(x86_round_mode));
+ set_float_rounding_mode(x86_round_mode[mode], status);
+}
+
void update_fp_status(CPUX86State *env)
{
- FloatRoundMode rnd_mode;
+ int rnd_mode;
FloatX80RoundPrec rnd_prec;
/* set rounding mode */
- switch (env->fpuc & FPU_RC_MASK) {
- default:
- case FPU_RC_NEAR:
- rnd_mode = float_round_nearest_even;
- break;
- case FPU_RC_DOWN:
- rnd_mode = float_round_down;
- break;
- case FPU_RC_UP:
- rnd_mode = float_round_up;
- break;
- case FPU_RC_CHOP:
- rnd_mode = float_round_to_zero;
- break;
- }
- set_float_rounding_mode(rnd_mode, &env->fp_status);
+ rnd_mode = (env->fpuc & FPU_RC_MASK) >> FPU_RC_SHIFT;
+ set_x86_rounding_mode(rnd_mode, &env->fp_status);
switch ((env->fpuc >> 8) & 3) {
case 0:
@@ -3038,11 +3037,8 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask)
/* XXX: optimize by storing fptt and fptags in the static cpu state */
#define SSE_DAZ 0x0040
-#define SSE_RC_MASK 0x6000
-#define SSE_RC_NEAR 0x0000
-#define SSE_RC_DOWN 0x2000
-#define SSE_RC_UP 0x4000
-#define SSE_RC_CHOP 0x6000
+#define SSE_RC_SHIFT 13
+#define SSE_RC_MASK (3 << SSE_RC_SHIFT)
#define SSE_FZ 0x8000
void update_mxcsr_status(CPUX86State *env)
@@ -3051,22 +3047,8 @@ void update_mxcsr_status(CPUX86State *env)
int rnd_type;
/* set rounding mode */
- switch (mxcsr & SSE_RC_MASK) {
- default:
- case SSE_RC_NEAR:
- rnd_type = float_round_nearest_even;
- break;
- case SSE_RC_DOWN:
- rnd_type = float_round_down;
- break;
- case SSE_RC_UP:
- rnd_type = float_round_up;
- break;
- case SSE_RC_CHOP:
- rnd_type = float_round_to_zero;
- break;
- }
- set_float_rounding_mode(rnd_type, &env->sse_status);
+ rnd_type = (mxcsr & SSE_RC_MASK) >> SSE_RC_SHIFT;
+ set_x86_rounding_mode(rnd_type, &env->sse_status);
/* Set exception flags. */
set_float_exception_flags((mxcsr & FPUS_IE ? float_flag_invalid : 0) |
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index e19d5c1c64..85be2e58c2 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -26,6 +26,7 @@
#include "tcg/tcg-op-gvec.h"
#include "exec/cpu_ldst.h"
#include "exec/translator.h"
+#include "fpu/softfloat.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build
index a5b6d5197a..113c80b4e4 100644
--- a/tests/qtest/libqos/meson.build
+++ b/tests/qtest/libqos/meson.build
@@ -34,6 +34,7 @@ libqos_srcs = files(
'tpci200.c',
'virtio.c',
'virtio-9p.c',
+ 'virtio-9p-client.c',
'virtio-balloon.c',
'virtio-blk.c',
'vhost-user-blk.c',
diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c
new file mode 100644
index 0000000000..e4a368e036
--- /dev/null
+++ b/tests/qtest/libqos/virtio-9p-client.c
@@ -0,0 +1,1049 @@
+/*
+ * 9P network client for VirtIO 9P test cases (based on QTest)
+ *
+ * Copyright (c) 2014 SUSE LINUX Products GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * Not so fast! You might want to read the 9p developer docs first:
+ * https://wiki.qemu.org/Documentation/9p
+ */
+
+#include "qemu/osdep.h"
+#include "virtio-9p-client.h"
+
+#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000)
+static QGuestAllocator *alloc;
+
+void v9fs_set_allocator(QGuestAllocator *t_alloc)
+{
+ alloc = t_alloc;
+}
+
+/*
+ * Used to auto generate new fids. Start with arbitrary high value to avoid
+ * collision with hard coded fids in basic test code.
+ */
+static uint32_t fid_generator = 1000;
+
+static uint32_t genfid(void)
+{
+ return fid_generator++;
+}
+
+/**
+ * Splits the @a in string by @a delim into individual (non empty) strings
+ * and outputs them to @a out. The output array @a out is NULL terminated.
+ *
+ * Output array @a out must be freed by calling split_free().
+ *
+ * @returns number of individual elements in output array @a out (without the
+ * final NULL terminating element)
+ */
+static int split(const char *in, const char *delim, char ***out)
+{
+ int n = 0, i = 0;
+ char *tmp, *p;
+
+ tmp = g_strdup(in);
+ for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) {
+ if (strlen(p) > 0) {
+ ++n;
+ }
+ }
+ g_free(tmp);
+
+ *out = g_new0(char *, n + 1); /* last element NULL delimiter */
+
+ tmp = g_strdup(in);
+ for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) {
+ if (strlen(p) > 0) {
+ (*out)[i++] = g_strdup(p);
+ }
+ }
+ g_free(tmp);
+
+ return n;
+}
+
+static void split_free(char ***out)
+{
+ int i;
+ if (!*out) {
+ return;
+ }
+ for (i = 0; (*out)[i]; ++i) {
+ g_free((*out)[i]);
+ }
+ g_free(*out);
+ *out = NULL;
+}
+
+void v9fs_memwrite(P9Req *req, const void *addr, size_t len)
+{
+ qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len);
+ req->t_off += len;
+}
+
+void v9fs_memskip(P9Req *req, size_t len)
+{
+ req->r_off += len;
+}
+
+void v9fs_memread(P9Req *req, void *addr, size_t len)
+{
+ qtest_memread(req->qts, req->r_msg + req->r_off, addr, len);
+ req->r_off += len;
+}
+
+void v9fs_uint8_read(P9Req *req, uint8_t *val)
+{
+ v9fs_memread(req, val, 1);
+}
+
+void v9fs_uint16_write(P9Req *req, uint16_t val)
+{
+ uint16_t le_val = cpu_to_le16(val);
+
+ v9fs_memwrite(req, &le_val, 2);
+}
+
+void v9fs_uint16_read(P9Req *req, uint16_t *val)
+{
+ v9fs_memread(req, val, 2);
+ le16_to_cpus(val);
+}
+
+void v9fs_uint32_write(P9Req *req, uint32_t val)
+{
+ uint32_t le_val = cpu_to_le32(val);
+
+ v9fs_memwrite(req, &le_val, 4);
+}
+
+void v9fs_uint64_write(P9Req *req, uint64_t val)
+{
+ uint64_t le_val = cpu_to_le64(val);
+
+ v9fs_memwrite(req, &le_val, 8);
+}
+
+void v9fs_uint32_read(P9Req *req, uint32_t *val)
+{
+ v9fs_memread(req, val, 4);
+ le32_to_cpus(val);
+}
+
+void v9fs_uint64_read(P9Req *req, uint64_t *val)
+{
+ v9fs_memread(req, val, 8);
+ le64_to_cpus(val);
+}
+
+/* len[2] string[len] */
+uint16_t v9fs_string_size(const char *string)
+{
+ size_t len = strlen(string);
+
+ g_assert_cmpint(len, <=, UINT16_MAX - 2);
+
+ return 2 + len;
+}
+
+void v9fs_string_write(P9Req *req, const char *string)
+{
+ int len = strlen(string);
+
+ g_assert_cmpint(len, <=, UINT16_MAX);
+
+ v9fs_uint16_write(req, (uint16_t) len);
+ v9fs_memwrite(req, string, len);
+}
+
+void v9fs_string_read(P9Req *req, uint16_t *len, char **string)
+{
+ uint16_t local_len;
+
+ v9fs_uint16_read(req, &local_len);
+ if (len) {
+ *len = local_len;
+ }
+ if (string) {
+ *string = g_malloc(local_len + 1);
+ v9fs_memread(req, *string, local_len);
+ (*string)[local_len] = 0;
+ } else {
+ v9fs_memskip(req, local_len);
+ }
+}
+
+typedef struct {
+ uint32_t size;
+ uint8_t id;
+ uint16_t tag;
+} QEMU_PACKED P9Hdr;
+
+P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id,
+ uint16_t tag)
+{
+ P9Req *req = g_new0(P9Req, 1);
+ uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */
+ P9Hdr hdr = {
+ .id = id,
+ .tag = cpu_to_le16(tag)
+ };
+
+ g_assert_cmpint(total_size, <=, UINT32_MAX - size);
+ total_size += size;
+ hdr.size = cpu_to_le32(total_size);
+
+ g_assert_cmpint(total_size, <=, P9_MAX_SIZE);
+
+ req->qts = global_qtest;
+ req->v9p = v9p;
+ req->t_size = total_size;
+ req->t_msg = guest_alloc(alloc, req->t_size);
+ v9fs_memwrite(req, &hdr, 7);
+ req->tag = tag;
+ return req;
+}
+
+void v9fs_req_send(P9Req *req)
+{
+ QVirtio9P *v9p = req->v9p;
+
+ req->r_msg = guest_alloc(alloc, P9_MAX_SIZE);
+ req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size,
+ false, true);
+ qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false);
+ qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head);
+ req->t_off = 0;
+}
+
+static const char *rmessage_name(uint8_t id)
+{
+ return
+ id == P9_RLERROR ? "RLERROR" :
+ id == P9_RVERSION ? "RVERSION" :
+ id == P9_RATTACH ? "RATTACH" :
+ id == P9_RWALK ? "RWALK" :
+ id == P9_RLOPEN ? "RLOPEN" :
+ id == P9_RWRITE ? "RWRITE" :
+ id == P9_RMKDIR ? "RMKDIR" :
+ id == P9_RLCREATE ? "RLCREATE" :
+ id == P9_RSYMLINK ? "RSYMLINK" :
+ id == P9_RLINK ? "RLINK" :
+ id == P9_RUNLINKAT ? "RUNLINKAT" :
+ id == P9_RFLUSH ? "RFLUSH" :
+ id == P9_RREADDIR ? "READDIR" :
+ "<unknown>";
+}
+
+void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len)
+{
+ QVirtio9P *v9p = req->v9p;
+
+ qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len,
+ QVIRTIO_9P_TIMEOUT_US);
+}
+
+void v9fs_req_recv(P9Req *req, uint8_t id)
+{
+ P9Hdr hdr;
+
+ v9fs_memread(req, &hdr, 7);
+ hdr.size = ldl_le_p(&hdr.size);
+ hdr.tag = lduw_le_p(&hdr.tag);
+
+ g_assert_cmpint(hdr.size, >=, 7);
+ g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE);
+ g_assert_cmpint(hdr.tag, ==, req->tag);
+
+ if (hdr.id != id) {
+ g_printerr("Received response %d (%s) instead of %d (%s)\n",
+ hdr.id, rmessage_name(hdr.id), id, rmessage_name(id));
+
+ if (hdr.id == P9_RLERROR) {
+ uint32_t err;
+ v9fs_uint32_read(req, &err);
+ g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err));
+ }
+ }
+ g_assert_cmpint(hdr.id, ==, id);
+}
+
+void v9fs_req_free(P9Req *req)
+{
+ guest_free(alloc, req->t_msg);
+ guest_free(alloc, req->r_msg);
+ g_free(req);
+}
+
+/* size[4] Rlerror tag[2] ecode[4] */
+void v9fs_rlerror(P9Req *req, uint32_t *err)
+{
+ v9fs_req_recv(req, P9_RLERROR);
+ v9fs_uint32_read(req, err);
+ v9fs_req_free(req);
+}
+
+/* size[4] Tversion tag[2] msize[4] version[s] */
+TVersionRes v9fs_tversion(TVersionOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+ uint32_t body_size = 4;
+ uint16_t string_size;
+ uint16_t server_len;
+ g_autofree char *server_version = NULL;
+
+ g_assert(opt.client);
+
+ if (!opt.msize) {
+ opt.msize = P9_MAX_SIZE;
+ }
+
+ if (!opt.tag) {
+ opt.tag = P9_NOTAG;
+ }
+
+ if (!opt.version) {
+ opt.version = "9P2000.L";
+ }
+
+ string_size = v9fs_string_size(opt.version);
+ g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
+ body_size += string_size;
+ req = v9fs_req_init(opt.client, body_size, P9_TVERSION, opt.tag);
+
+ v9fs_uint32_write(req, opt.msize);
+ v9fs_string_write(req, opt.version);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rversion(req, &server_len, &server_version);
+ g_assert_cmpmem(server_version, server_len,
+ opt.version, strlen(opt.version));
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TVersionRes) {
+ .req = req,
+ };
+}
+
+/* size[4] Rversion tag[2] msize[4] version[s] */
+void v9fs_rversion(P9Req *req, uint16_t *len, char **version)
+{
+ uint32_t msize;
+
+ v9fs_req_recv(req, P9_RVERSION);
+ v9fs_uint32_read(req, &msize);
+
+ g_assert_cmpint(msize, ==, P9_MAX_SIZE);
+
+ if (len || version) {
+ v9fs_string_read(req, len, version);
+ }
+
+ v9fs_req_free(req);
+}
+
+/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */
+TAttachRes v9fs_tattach(TAttachOpt opt)
+{
+ uint32_t err;
+ const char *uname = ""; /* ignored by QEMU */
+ const char *aname = ""; /* ignored by QEMU */
+
+ g_assert(opt.client);
+ /* expecting either Rattach or Rlerror, but obviously not both */
+ g_assert(!opt.expectErr || !opt.rattach.qid);
+
+ if (!opt.requestOnly) {
+ v9fs_tversion((TVersionOpt) { .client = opt.client });
+ }
+
+ if (!opt.n_uname) {
+ opt.n_uname = getuid();
+ }
+
+ P9Req *req = v9fs_req_init(opt.client, 4 + 4 + 2 + 2 + 4, P9_TATTACH,
+ opt.tag);
+
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_uint32_write(req, P9_NOFID);
+ v9fs_string_write(req, uname);
+ v9fs_string_write(req, aname);
+ v9fs_uint32_write(req, opt.n_uname);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rattach(req, opt.rattach.qid);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TAttachRes) {
+ .req = req,
+ };
+}
+
+/* size[4] Rattach tag[2] qid[13] */
+void v9fs_rattach(P9Req *req, v9fs_qid *qid)
+{
+ v9fs_req_recv(req, P9_RATTACH);
+ if (qid) {
+ v9fs_memread(req, qid, 13);
+ }
+ v9fs_req_free(req);
+}
+
+/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
+TWalkRes v9fs_twalk(TWalkOpt opt)
+{
+ P9Req *req;
+ int i;
+ uint32_t body_size = 4 + 4 + 2;
+ uint32_t err;
+ char **wnames = NULL;
+
+ g_assert(opt.client);
+ /* expecting either high- or low-level path, both not both */
+ g_assert(!opt.path || !(opt.nwname || opt.wnames));
+ /* expecting either Rwalk or Rlerror, but obviously not both */
+ g_assert(!opt.expectErr || !(opt.rwalk.nwqid || opt.rwalk.wqid));
+
+ if (!opt.newfid) {
+ opt.newfid = genfid();
+ }
+
+ if (opt.path) {
+ opt.nwname = split(opt.path, "/", &wnames);
+ opt.wnames = wnames;
+ }
+
+ for (i = 0; i < opt.nwname; i++) {
+ uint16_t wname_size = v9fs_string_size(opt.wnames[i]);
+
+ g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size);
+ body_size += wname_size;
+ }
+ req = v9fs_req_init(opt.client, body_size, P9_TWALK, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_uint32_write(req, opt.newfid);
+ v9fs_uint16_write(req, opt.nwname);
+ for (i = 0; i < opt.nwname; i++) {
+ v9fs_string_write(req, opt.wnames[i]);
+ }
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rwalk(req, opt.rwalk.nwqid, opt.rwalk.wqid);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ split_free(&wnames);
+
+ return (TWalkRes) {
+ .newfid = opt.newfid,
+ .req = req,
+ };
+}
+
+/* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */
+void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid)
+{
+ uint16_t local_nwqid;
+
+ v9fs_req_recv(req, P9_RWALK);
+ v9fs_uint16_read(req, &local_nwqid);
+ if (nwqid) {
+ *nwqid = local_nwqid;
+ }
+ if (wqid) {
+ *wqid = g_malloc(local_nwqid * 13);
+ v9fs_memread(req, *wqid, local_nwqid * 13);
+ }
+ v9fs_req_free(req);
+}
+
+/* size[4] Tgetattr tag[2] fid[4] request_mask[8] */
+TGetAttrRes v9fs_tgetattr(TGetAttrOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ /* expecting either Rgetattr or Rlerror, but obviously not both */
+ g_assert(!opt.expectErr || !opt.rgetattr.attr);
+
+ if (!opt.request_mask) {
+ opt.request_mask = P9_GETATTR_ALL;
+ }
+
+ req = v9fs_req_init(opt.client, 4 + 8, P9_TGETATTR, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_uint64_write(req, opt.request_mask);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rgetattr(req, opt.rgetattr.attr);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TGetAttrRes) { .req = req };
+}
+
+/*
+ * size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8]
+ * rdev[8] size[8] blksize[8] blocks[8]
+ * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]
+ * ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8]
+ * gen[8] data_version[8]
+ */
+void v9fs_rgetattr(P9Req *req, v9fs_attr *attr)
+{
+ v9fs_req_recv(req, P9_RGETATTR);
+
+ v9fs_uint64_read(req, &attr->valid);
+ v9fs_memread(req, &attr->qid, 13);
+ v9fs_uint32_read(req, &attr->mode);
+ v9fs_uint32_read(req, &attr->uid);
+ v9fs_uint32_read(req, &attr->gid);
+ v9fs_uint64_read(req, &attr->nlink);
+ v9fs_uint64_read(req, &attr->rdev);
+ v9fs_uint64_read(req, &attr->size);
+ v9fs_uint64_read(req, &attr->blksize);
+ v9fs_uint64_read(req, &attr->blocks);
+ v9fs_uint64_read(req, &attr->atime_sec);
+ v9fs_uint64_read(req, &attr->atime_nsec);
+ v9fs_uint64_read(req, &attr->mtime_sec);
+ v9fs_uint64_read(req, &attr->mtime_nsec);
+ v9fs_uint64_read(req, &attr->ctime_sec);
+ v9fs_uint64_read(req, &attr->ctime_nsec);
+ v9fs_uint64_read(req, &attr->btime_sec);
+ v9fs_uint64_read(req, &attr->btime_nsec);
+ v9fs_uint64_read(req, &attr->gen);
+ v9fs_uint64_read(req, &attr->data_version);
+
+ v9fs_req_free(req);
+}
+
+/* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */
+TReadDirRes v9fs_treaddir(TReadDirOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ /* expecting either Rreaddir or Rlerror, but obviously not both */
+ g_assert(!opt.expectErr || !(opt.rreaddir.count ||
+ opt.rreaddir.nentries || opt.rreaddir.entries));
+
+ req = v9fs_req_init(opt.client, 4 + 8 + 4, P9_TREADDIR, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_uint64_write(req, opt.offset);
+ v9fs_uint32_write(req, opt.count);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rreaddir(req, opt.rreaddir.count, opt.rreaddir.nentries,
+ opt.rreaddir.entries);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TReadDirRes) { .req = req };
+}
+
+/* size[4] Rreaddir tag[2] count[4] data[count] */
+void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries,
+ struct V9fsDirent **entries)
+{
+ uint32_t local_count;
+ struct V9fsDirent *e = NULL;
+ uint16_t slen;
+ uint32_t n = 0;
+
+ v9fs_req_recv(req, P9_RREADDIR);
+ v9fs_uint32_read(req, &local_count);
+
+ if (count) {
+ *count = local_count;
+ }
+
+ for (int32_t togo = (int32_t)local_count;
+ togo >= 13 + 8 + 1 + 2;
+ togo -= 13 + 8 + 1 + 2 + slen, ++n)
+ {
+ if (!e) {
+ e = g_new(struct V9fsDirent, 1);
+ if (entries) {
+ *entries = e;
+ }
+ } else {
+ e = e->next = g_new(struct V9fsDirent, 1);
+ }
+ e->next = NULL;
+ /* qid[13] offset[8] type[1] name[s] */
+ v9fs_memread(req, &e->qid, 13);
+ v9fs_uint64_read(req, &e->offset);
+ v9fs_uint8_read(req, &e->type);
+ v9fs_string_read(req, &slen, &e->name);
+ }
+
+ if (nentries) {
+ *nentries = n;
+ }
+
+ v9fs_req_free(req);
+}
+
+void v9fs_free_dirents(struct V9fsDirent *e)
+{
+ struct V9fsDirent *next = NULL;
+
+ for (; e; e = next) {
+ next = e->next;
+ g_free(e->name);
+ g_free(e);
+ }
+}
+
+/* size[4] Tlopen tag[2] fid[4] flags[4] */
+TLOpenRes v9fs_tlopen(TLOpenOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ /* expecting either Rlopen or Rlerror, but obviously not both */
+ g_assert(!opt.expectErr || !(opt.rlopen.qid || opt.rlopen.iounit));
+
+ req = v9fs_req_init(opt.client, 4 + 4, P9_TLOPEN, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_uint32_write(req, opt.flags);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rlopen(req, opt.rlopen.qid, opt.rlopen.iounit);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TLOpenRes) { .req = req };
+}
+
+/* size[4] Rlopen tag[2] qid[13] iounit[4] */
+void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit)
+{
+ v9fs_req_recv(req, P9_RLOPEN);
+ if (qid) {
+ v9fs_memread(req, qid, 13);
+ } else {
+ v9fs_memskip(req, 13);
+ }
+ if (iounit) {
+ v9fs_uint32_read(req, iounit);
+ }
+ v9fs_req_free(req);
+}
+
+/* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */
+TWriteRes v9fs_twrite(TWriteOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+ uint32_t body_size = 4 + 8 + 4;
+ uint32_t written = 0;
+
+ g_assert(opt.client);
+
+ g_assert_cmpint(body_size, <=, UINT32_MAX - opt.count);
+ body_size += opt.count;
+ req = v9fs_req_init(opt.client, body_size, P9_TWRITE, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_uint64_write(req, opt.offset);
+ v9fs_uint32_write(req, opt.count);
+ v9fs_memwrite(req, opt.data, opt.count);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rwrite(req, &written);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TWriteRes) {
+ .req = req,
+ .count = written
+ };
+}
+
+/* size[4] Rwrite tag[2] count[4] */
+void v9fs_rwrite(P9Req *req, uint32_t *count)
+{
+ v9fs_req_recv(req, P9_RWRITE);
+ if (count) {
+ v9fs_uint32_read(req, count);
+ }
+ v9fs_req_free(req);
+}
+
+/* size[4] Tflush tag[2] oldtag[2] */
+TFlushRes v9fs_tflush(TFlushOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+
+ req = v9fs_req_init(opt.client, 2, P9_TFLUSH, opt.tag);
+ v9fs_uint32_write(req, opt.oldtag);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rflush(req);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TFlushRes) { .req = req };
+}
+
+/* size[4] Rflush tag[2] */
+void v9fs_rflush(P9Req *req)
+{
+ v9fs_req_recv(req, P9_RFLUSH);
+ v9fs_req_free(req);
+}
+
+/* size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4] */
+TMkdirRes v9fs_tmkdir(TMkdirOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ /* expecting either hi-level atPath or low-level dfid, but not both */
+ g_assert(!opt.atPath || !opt.dfid);
+ /* expecting either Rmkdir or Rlerror, but obviously not both */
+ g_assert(!opt.expectErr || !opt.rmkdir.qid);
+
+ if (opt.atPath) {
+ opt.dfid = v9fs_twalk((TWalkOpt) { .client = opt.client,
+ .path = opt.atPath }).newfid;
+ }
+
+ if (!opt.mode) {
+ opt.mode = 0750;
+ }
+
+ uint32_t body_size = 4 + 4 + 4;
+ uint16_t string_size = v9fs_string_size(opt.name);
+
+ g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
+ body_size += string_size;
+
+ req = v9fs_req_init(opt.client, body_size, P9_TMKDIR, opt.tag);
+ v9fs_uint32_write(req, opt.dfid);
+ v9fs_string_write(req, opt.name);
+ v9fs_uint32_write(req, opt.mode);
+ v9fs_uint32_write(req, opt.gid);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rmkdir(req, opt.rmkdir.qid);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TMkdirRes) { .req = req };
+}
+
+/* size[4] Rmkdir tag[2] qid[13] */
+void v9fs_rmkdir(P9Req *req, v9fs_qid *qid)
+{
+ v9fs_req_recv(req, P9_RMKDIR);
+ if (qid) {
+ v9fs_memread(req, qid, 13);
+ } else {
+ v9fs_memskip(req, 13);
+ }
+ v9fs_req_free(req);
+}
+
+/* size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4] */
+TlcreateRes v9fs_tlcreate(TlcreateOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ /* expecting either hi-level atPath or low-level fid, but not both */
+ g_assert(!opt.atPath || !opt.fid);
+ /* expecting either Rlcreate or Rlerror, but obviously not both */
+ g_assert(!opt.expectErr || !(opt.rlcreate.qid || opt.rlcreate.iounit));
+
+ if (opt.atPath) {
+ opt.fid = v9fs_twalk((TWalkOpt) { .client = opt.client,
+ .path = opt.atPath }).newfid;
+ }
+
+ if (!opt.mode) {
+ opt.mode = 0750;
+ }
+
+ uint32_t body_size = 4 + 4 + 4 + 4;
+ uint16_t string_size = v9fs_string_size(opt.name);
+
+ g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
+ body_size += string_size;
+
+ req = v9fs_req_init(opt.client, body_size, P9_TLCREATE, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_string_write(req, opt.name);
+ v9fs_uint32_write(req, opt.flags);
+ v9fs_uint32_write(req, opt.mode);
+ v9fs_uint32_write(req, opt.gid);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rlcreate(req, opt.rlcreate.qid, opt.rlcreate.iounit);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TlcreateRes) { .req = req };
+}
+
+/* size[4] Rlcreate tag[2] qid[13] iounit[4] */
+void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit)
+{
+ v9fs_req_recv(req, P9_RLCREATE);
+ if (qid) {
+ v9fs_memread(req, qid, 13);
+ } else {
+ v9fs_memskip(req, 13);
+ }
+ if (iounit) {
+ v9fs_uint32_read(req, iounit);
+ }
+ v9fs_req_free(req);
+}
+
+/* size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4] */
+TsymlinkRes v9fs_tsymlink(TsymlinkOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ /* expecting either hi-level atPath or low-level fid, but not both */
+ g_assert(!opt.atPath || !opt.fid);
+ /* expecting either Rsymlink or Rlerror, but obviously not both */
+ g_assert(!opt.expectErr || !opt.rsymlink.qid);
+
+ if (opt.atPath) {
+ opt.fid = v9fs_twalk((TWalkOpt) { .client = opt.client,
+ .path = opt.atPath }).newfid;
+ }
+
+ uint32_t body_size = 4 + 4;
+ uint16_t string_size = v9fs_string_size(opt.name) +
+ v9fs_string_size(opt.symtgt);
+
+ g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
+ body_size += string_size;
+
+ req = v9fs_req_init(opt.client, body_size, P9_TSYMLINK, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_string_write(req, opt.name);
+ v9fs_string_write(req, opt.symtgt);
+ v9fs_uint32_write(req, opt.gid);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rsymlink(req, opt.rsymlink.qid);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TsymlinkRes) { .req = req };
+}
+
+/* size[4] Rsymlink tag[2] qid[13] */
+void v9fs_rsymlink(P9Req *req, v9fs_qid *qid)
+{
+ v9fs_req_recv(req, P9_RSYMLINK);
+ if (qid) {
+ v9fs_memread(req, qid, 13);
+ } else {
+ v9fs_memskip(req, 13);
+ }
+ v9fs_req_free(req);
+}
+
+/* size[4] Tlink tag[2] dfid[4] fid[4] name[s] */
+TlinkRes v9fs_tlink(TlinkOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ /* expecting either hi-level atPath or low-level dfid, but not both */
+ g_assert(!opt.atPath || !opt.dfid);
+ /* expecting either hi-level toPath or low-level fid, but not both */
+ g_assert(!opt.toPath || !opt.fid);
+
+ if (opt.atPath) {
+ opt.dfid = v9fs_twalk((TWalkOpt) { .client = opt.client,
+ .path = opt.atPath }).newfid;
+ }
+ if (opt.toPath) {
+ opt.fid = v9fs_twalk((TWalkOpt) { .client = opt.client,
+ .path = opt.toPath }).newfid;
+ }
+
+ uint32_t body_size = 4 + 4;
+ uint16_t string_size = v9fs_string_size(opt.name);
+
+ g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
+ body_size += string_size;
+
+ req = v9fs_req_init(opt.client, body_size, P9_TLINK, opt.tag);
+ v9fs_uint32_write(req, opt.dfid);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_string_write(req, opt.name);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rlink(req);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TlinkRes) { .req = req };
+}
+
+/* size[4] Rlink tag[2] */
+void v9fs_rlink(P9Req *req)
+{
+ v9fs_req_recv(req, P9_RLINK);
+ v9fs_req_free(req);
+}
+
+/* size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4] */
+TunlinkatRes v9fs_tunlinkat(TunlinkatOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ /* expecting either hi-level atPath or low-level dirfd, but not both */
+ g_assert(!opt.atPath || !opt.dirfd);
+
+ if (opt.atPath) {
+ opt.dirfd = v9fs_twalk((TWalkOpt) { .client = opt.client,
+ .path = opt.atPath }).newfid;
+ }
+
+ uint32_t body_size = 4 + 4;
+ uint16_t string_size = v9fs_string_size(opt.name);
+
+ g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
+ body_size += string_size;
+
+ req = v9fs_req_init(opt.client, body_size, P9_TUNLINKAT, opt.tag);
+ v9fs_uint32_write(req, opt.dirfd);
+ v9fs_string_write(req, opt.name);
+ v9fs_uint32_write(req, opt.flags);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_runlinkat(req);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TunlinkatRes) { .req = req };
+}
+
+/* size[4] Runlinkat tag[2] */
+void v9fs_runlinkat(P9Req *req)
+{
+ v9fs_req_recv(req, P9_RUNLINKAT);
+ v9fs_req_free(req);
+}
diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h
new file mode 100644
index 0000000000..78228eb97d
--- /dev/null
+++ b/tests/qtest/libqos/virtio-9p-client.h
@@ -0,0 +1,494 @@
+/*
+ * 9P network client for VirtIO 9P test cases (based on QTest)
+ *
+ * Copyright (c) 2014 SUSE LINUX Products GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * Not so fast! You might want to read the 9p developer docs first:
+ * https://wiki.qemu.org/Documentation/9p
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_9P_CLIENT_H
+#define TESTS_LIBQOS_VIRTIO_9P_CLIENT_H
+
+#include "hw/9pfs/9p.h"
+#include "hw/9pfs/9p-synth.h"
+#include "virtio-9p.h"
+#include "qgraph.h"
+#include "tests/qtest/libqtest-single.h"
+
+#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */
+
+typedef struct {
+ QTestState *qts;
+ QVirtio9P *v9p;
+ uint16_t tag;
+ uint64_t t_msg;
+ uint32_t t_size;
+ uint64_t r_msg;
+ /* No r_size, it is hardcoded to P9_MAX_SIZE */
+ size_t t_off;
+ size_t r_off;
+ uint32_t free_head;
+} P9Req;
+
+/* type[1] version[4] path[8] */
+typedef char v9fs_qid[13];
+
+typedef struct v9fs_attr {
+ uint64_t valid;
+ v9fs_qid qid;
+ uint32_t mode;
+ uint32_t uid;
+ uint32_t gid;
+ uint64_t nlink;
+ uint64_t rdev;
+ uint64_t size;
+ uint64_t blksize;
+ uint64_t blocks;
+ uint64_t atime_sec;
+ uint64_t atime_nsec;
+ uint64_t mtime_sec;
+ uint64_t mtime_nsec;
+ uint64_t ctime_sec;
+ uint64_t ctime_nsec;
+ uint64_t btime_sec;
+ uint64_t btime_nsec;
+ uint64_t gen;
+ uint64_t data_version;
+} v9fs_attr;
+
+#define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
+#define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */
+
+struct V9fsDirent {
+ v9fs_qid qid;
+ uint64_t offset;
+ uint8_t type;
+ char *name;
+ struct V9fsDirent *next;
+};
+
+/* options for 'Twalk' 9p request */
+typedef struct TWalkOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID of directory from where walk should start (optional) */
+ uint32_t fid;
+ /* file ID for target directory being walked to (optional) */
+ uint32_t newfid;
+ /* low level variant of path to walk to (optional) */
+ uint16_t nwname;
+ char **wnames;
+ /* high level variant of path to walk to (optional) */
+ const char *path;
+ /* data being received from 9p server as 'Rwalk' response (optional) */
+ struct {
+ uint16_t *nwqid;
+ v9fs_qid **wqid;
+ } rwalk;
+ /* only send Twalk request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TWalkOpt;
+
+/* result of 'Twalk' 9p request */
+typedef struct TWalkRes {
+ /* file ID of target directory been walked to */
+ uint32_t newfid;
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TWalkRes;
+
+/* options for 'Tversion' 9p request */
+typedef struct TVersionOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* maximum message size that can be handled by client (optional) */
+ uint32_t msize;
+ /* protocol version (optional) */
+ const char *version;
+ /* only send Tversion request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TVersionOpt;
+
+/* result of 'Tversion' 9p request */
+typedef struct TVersionRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TVersionRes;
+
+/* options for 'Tattach' 9p request */
+typedef struct TAttachOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID to be associated with root of file tree (optional) */
+ uint32_t fid;
+ /* numerical uid of user being introduced to server (optional) */
+ uint32_t n_uname;
+ /* data being received from 9p server as 'Rattach' response (optional) */
+ struct {
+ /* server's idea of the root of the file tree */
+ v9fs_qid *qid;
+ } rattach;
+ /* only send Tattach request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TAttachOpt;
+
+/* result of 'Tattach' 9p request */
+typedef struct TAttachRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TAttachRes;
+
+/* options for 'Tgetattr' 9p request */
+typedef struct TGetAttrOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID of file/dir whose attributes shall be retrieved (required) */
+ uint32_t fid;
+ /* bitmask indicating attribute fields to be retrieved (optional) */
+ uint64_t request_mask;
+ /* data being received from 9p server as 'Rgetattr' response (optional) */
+ struct {
+ v9fs_attr *attr;
+ } rgetattr;
+ /* only send Tgetattr request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TGetAttrOpt;
+
+/* result of 'Tgetattr' 9p request */
+typedef struct TGetAttrRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TGetAttrRes;
+
+/* options for 'Treaddir' 9p request */
+typedef struct TReadDirOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID of directory whose entries shall be retrieved (required) */
+ uint32_t fid;
+ /* offset in entries stream, i.e. for multiple requests (optional) */
+ uint64_t offset;
+ /* maximum bytes to be returned by server (required) */
+ uint32_t count;
+ /* data being received from 9p server as 'Rreaddir' response (optional) */
+ struct {
+ uint32_t *count;
+ uint32_t *nentries;
+ struct V9fsDirent **entries;
+ } rreaddir;
+ /* only send Treaddir request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TReadDirOpt;
+
+/* result of 'Treaddir' 9p request */
+typedef struct TReadDirRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TReadDirRes;
+
+/* options for 'Tlopen' 9p request */
+typedef struct TLOpenOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID of file / directory to be opened (required) */
+ uint32_t fid;
+ /* Linux open(2) flags such as O_RDONLY, O_RDWR, O_WRONLY (optional) */
+ uint32_t flags;
+ /* data being received from 9p server as 'Rlopen' response (optional) */
+ struct {
+ v9fs_qid *qid;
+ uint32_t *iounit;
+ } rlopen;
+ /* only send Tlopen request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TLOpenOpt;
+
+/* result of 'Tlopen' 9p request */
+typedef struct TLOpenRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TLOpenRes;
+
+/* options for 'Twrite' 9p request */
+typedef struct TWriteOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID of file to write to (required) */
+ uint32_t fid;
+ /* start position of write from beginning of file (optional) */
+ uint64_t offset;
+ /* how many bytes to write */
+ uint32_t count;
+ /* data to be written */
+ const void *data;
+ /* only send Twrite request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TWriteOpt;
+
+/* result of 'Twrite' 9p request */
+typedef struct TWriteRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+ /* amount of bytes written */
+ uint32_t count;
+} TWriteRes;
+
+/* options for 'Tflush' 9p request */
+typedef struct TFlushOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* message to flush (required) */
+ uint16_t oldtag;
+ /* only send Tflush request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TFlushOpt;
+
+/* result of 'Tflush' 9p request */
+typedef struct TFlushRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TFlushRes;
+
+/* options for 'Tmkdir' 9p request */
+typedef struct TMkdirOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* low level variant of directory where new one shall be created */
+ uint32_t dfid;
+ /* high-level variant of directory where new one shall be created */
+ const char *atPath;
+ /* New directory's name (required) */
+ const char *name;
+ /* Linux mkdir(2) mode bits (optional) */
+ uint32_t mode;
+ /* effective group ID of caller */
+ uint32_t gid;
+ /* data being received from 9p server as 'Rmkdir' response (optional) */
+ struct {
+ /* QID of newly created directory */
+ v9fs_qid *qid;
+ } rmkdir;
+ /* only send Tmkdir request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TMkdirOpt;
+
+/* result of 'TMkdir' 9p request */
+typedef struct TMkdirRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TMkdirRes;
+
+/* options for 'Tlcreate' 9p request */
+typedef struct TlcreateOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* low-level variant of directory where new file shall be created */
+ uint32_t fid;
+ /* high-level variant of directory where new file shall be created */
+ const char *atPath;
+ /* name of new file (required) */
+ const char *name;
+ /* Linux kernel intent bits */
+ uint32_t flags;
+ /* Linux create(2) mode bits */
+ uint32_t mode;
+ /* effective group ID of caller */
+ uint32_t gid;
+ /* data being received from 9p server as 'Rlcreate' response (optional) */
+ struct {
+ v9fs_qid *qid;
+ uint32_t *iounit;
+ } rlcreate;
+ /* only send Tlcreate request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TlcreateOpt;
+
+/* result of 'Tlcreate' 9p request */
+typedef struct TlcreateRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TlcreateRes;
+
+/* options for 'Tsymlink' 9p request */
+typedef struct TsymlinkOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* low-level variant of directory where symlink shall be created */
+ uint32_t fid;
+ /* high-level variant of directory where symlink shall be created */
+ const char *atPath;
+ /* name of symlink (required) */
+ const char *name;
+ /* where symlink will point to (required) */
+ const char *symtgt;
+ /* effective group ID of caller */
+ uint32_t gid;
+ /* data being received from 9p server as 'Rsymlink' response (optional) */
+ struct {
+ v9fs_qid *qid;
+ } rsymlink;
+ /* only send Tsymlink request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TsymlinkOpt;
+
+/* result of 'Tsymlink' 9p request */
+typedef struct TsymlinkRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TsymlinkRes;
+
+/* options for 'Tlink' 9p request */
+typedef struct TlinkOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* low-level variant of directory where hard link shall be created */
+ uint32_t dfid;
+ /* high-level variant of directory where hard link shall be created */
+ const char *atPath;
+ /* low-level variant of target referenced by new hard link */
+ uint32_t fid;
+ /* high-level variant of target referenced by new hard link */
+ const char *toPath;
+ /* name of hard link (required) */
+ const char *name;
+ /* only send Tlink request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TlinkOpt;
+
+/* result of 'Tlink' 9p request */
+typedef struct TlinkRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TlinkRes;
+
+/* options for 'Tunlinkat' 9p request */
+typedef struct TunlinkatOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* low-level variant of directory where name shall be unlinked */
+ uint32_t dirfd;
+ /* high-level variant of directory where name shall be unlinked */
+ const char *atPath;
+ /* name of directory entry to be unlinked (required) */
+ const char *name;
+ /* Linux unlinkat(2) flags */
+ uint32_t flags;
+ /* only send Tunlinkat request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TunlinkatOpt;
+
+/* result of 'Tunlinkat' 9p request */
+typedef struct TunlinkatRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TunlinkatRes;
+
+void v9fs_set_allocator(QGuestAllocator *t_alloc);
+void v9fs_memwrite(P9Req *req, const void *addr, size_t len);
+void v9fs_memskip(P9Req *req, size_t len);
+void v9fs_memread(P9Req *req, void *addr, size_t len);
+void v9fs_uint8_read(P9Req *req, uint8_t *val);
+void v9fs_uint16_write(P9Req *req, uint16_t val);
+void v9fs_uint16_read(P9Req *req, uint16_t *val);
+void v9fs_uint32_write(P9Req *req, uint32_t val);
+void v9fs_uint64_write(P9Req *req, uint64_t val);
+void v9fs_uint32_read(P9Req *req, uint32_t *val);
+void v9fs_uint64_read(P9Req *req, uint64_t *val);
+uint16_t v9fs_string_size(const char *string);
+void v9fs_string_write(P9Req *req, const char *string);
+void v9fs_string_read(P9Req *req, uint16_t *len, char **string);
+P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id,
+ uint16_t tag);
+void v9fs_req_send(P9Req *req);
+void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len);
+void v9fs_req_recv(P9Req *req, uint8_t id);
+void v9fs_req_free(P9Req *req);
+void v9fs_rlerror(P9Req *req, uint32_t *err);
+TVersionRes v9fs_tversion(TVersionOpt);
+void v9fs_rversion(P9Req *req, uint16_t *len, char **version);
+TAttachRes v9fs_tattach(TAttachOpt);
+void v9fs_rattach(P9Req *req, v9fs_qid *qid);
+TWalkRes v9fs_twalk(TWalkOpt opt);
+void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid);
+TGetAttrRes v9fs_tgetattr(TGetAttrOpt);
+void v9fs_rgetattr(P9Req *req, v9fs_attr *attr);
+TReadDirRes v9fs_treaddir(TReadDirOpt);
+void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries,
+ struct V9fsDirent **entries);
+void v9fs_free_dirents(struct V9fsDirent *e);
+TLOpenRes v9fs_tlopen(TLOpenOpt);
+void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit);
+TWriteRes v9fs_twrite(TWriteOpt);
+void v9fs_rwrite(P9Req *req, uint32_t *count);
+TFlushRes v9fs_tflush(TFlushOpt);
+void v9fs_rflush(P9Req *req);
+TMkdirRes v9fs_tmkdir(TMkdirOpt);
+void v9fs_rmkdir(P9Req *req, v9fs_qid *qid);
+TlcreateRes v9fs_tlcreate(TlcreateOpt);
+void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit);
+TsymlinkRes v9fs_tsymlink(TsymlinkOpt);
+void v9fs_rsymlink(P9Req *req, v9fs_qid *qid);
+TlinkRes v9fs_tlink(TlinkOpt);
+void v9fs_rlink(P9Req *req);
+TunlinkatRes v9fs_tunlinkat(TunlinkatOpt);
+void v9fs_runlinkat(P9Req *req);
+
+#endif
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 25305a4cf7..65e69491e5 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -13,76 +13,27 @@
*/
#include "qemu/osdep.h"
-#include "libqtest-single.h"
#include "qemu/module.h"
-#include "hw/9pfs/9p.h"
-#include "hw/9pfs/9p-synth.h"
-#include "libqos/virtio-9p.h"
-#include "libqos/qgraph.h"
-
-#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000)
-static QGuestAllocator *alloc;
-
-/*
- * Used to auto generate new fids. Start with arbitrary high value to avoid
- * collision with hard coded fids in basic test code.
- */
-static uint32_t fid_generator = 1000;
-
-static uint32_t genfid(void)
-{
- return fid_generator++;
-}
-
-/**
- * Splits the @a in string by @a delim into individual (non empty) strings
- * and outputs them to @a out. The output array @a out is NULL terminated.
- *
- * Output array @a out must be freed by calling split_free().
- *
- * @returns number of individual elements in output array @a out (without the
- * final NULL terminating element)
- */
-static int split(const char *in, const char *delim, char ***out)
-{
- int n = 0, i = 0;
- char *tmp, *p;
-
- tmp = g_strdup(in);
- for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) {
- if (strlen(p) > 0) {
- ++n;
- }
- }
- g_free(tmp);
-
- *out = g_new0(char *, n + 1); /* last element NULL delimiter */
-
- tmp = g_strdup(in);
- for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) {
- if (strlen(p) > 0) {
- (*out)[i++] = g_strdup(p);
- }
- }
- g_free(tmp);
-
- return n;
-}
-
-static void split_free(char ***out)
-{
- int i;
- for (i = 0; (*out)[i]; ++i) {
- g_free((*out)[i]);
- }
- g_free(*out);
- *out = NULL;
-}
+#include "libqos/virtio-9p-client.h"
+
+#define twalk(...) v9fs_twalk((TWalkOpt) __VA_ARGS__)
+#define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__)
+#define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__)
+#define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__)
+#define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__)
+#define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__)
+#define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__)
+#define tflush(...) v9fs_tflush((TFlushOpt) __VA_ARGS__)
+#define tmkdir(...) v9fs_tmkdir((TMkdirOpt) __VA_ARGS__)
+#define tlcreate(...) v9fs_tlcreate((TlcreateOpt) __VA_ARGS__)
+#define tsymlink(...) v9fs_tsymlink((TsymlinkOpt) __VA_ARGS__)
+#define tlink(...) v9fs_tlink((TlinkOpt) __VA_ARGS__)
+#define tunlinkat(...) v9fs_tunlinkat((TunlinkatOpt) __VA_ARGS__)
static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
size_t tag_len = qvirtio_config_readw(v9p->vdev, 0);
g_autofree char *tag = NULL;
int i;
@@ -96,670 +47,43 @@ static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len);
}
-#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */
-
-typedef struct {
- QTestState *qts;
- QVirtio9P *v9p;
- uint16_t tag;
- uint64_t t_msg;
- uint32_t t_size;
- uint64_t r_msg;
- /* No r_size, it is hardcoded to P9_MAX_SIZE */
- size_t t_off;
- size_t r_off;
- uint32_t free_head;
-} P9Req;
-
-static void v9fs_memwrite(P9Req *req, const void *addr, size_t len)
-{
- qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len);
- req->t_off += len;
-}
-
-static void v9fs_memskip(P9Req *req, size_t len)
-{
- req->r_off += len;
-}
-
-static void v9fs_memread(P9Req *req, void *addr, size_t len)
-{
- qtest_memread(req->qts, req->r_msg + req->r_off, addr, len);
- req->r_off += len;
-}
-
-static void v9fs_uint8_read(P9Req *req, uint8_t *val)
-{
- v9fs_memread(req, val, 1);
-}
-
-static void v9fs_uint16_write(P9Req *req, uint16_t val)
-{
- uint16_t le_val = cpu_to_le16(val);
-
- v9fs_memwrite(req, &le_val, 2);
-}
-
-static void v9fs_uint16_read(P9Req *req, uint16_t *val)
-{
- v9fs_memread(req, val, 2);
- le16_to_cpus(val);
-}
-
-static void v9fs_uint32_write(P9Req *req, uint32_t val)
-{
- uint32_t le_val = cpu_to_le32(val);
-
- v9fs_memwrite(req, &le_val, 4);
-}
-
-static void v9fs_uint64_write(P9Req *req, uint64_t val)
-{
- uint64_t le_val = cpu_to_le64(val);
-
- v9fs_memwrite(req, &le_val, 8);
-}
-
-static void v9fs_uint32_read(P9Req *req, uint32_t *val)
-{
- v9fs_memread(req, val, 4);
- le32_to_cpus(val);
-}
-
-static void v9fs_uint64_read(P9Req *req, uint64_t *val)
-{
- v9fs_memread(req, val, 8);
- le64_to_cpus(val);
-}
-
-/* len[2] string[len] */
-static uint16_t v9fs_string_size(const char *string)
-{
- size_t len = strlen(string);
-
- g_assert_cmpint(len, <=, UINT16_MAX - 2);
-
- return 2 + len;
-}
-
-static void v9fs_string_write(P9Req *req, const char *string)
-{
- int len = strlen(string);
-
- g_assert_cmpint(len, <=, UINT16_MAX);
-
- v9fs_uint16_write(req, (uint16_t) len);
- v9fs_memwrite(req, string, len);
-}
-
-static void v9fs_string_read(P9Req *req, uint16_t *len, char **string)
-{
- uint16_t local_len;
-
- v9fs_uint16_read(req, &local_len);
- if (len) {
- *len = local_len;
- }
- if (string) {
- *string = g_malloc(local_len + 1);
- v9fs_memread(req, *string, local_len);
- (*string)[local_len] = 0;
- } else {
- v9fs_memskip(req, local_len);
- }
-}
-
- typedef struct {
- uint32_t size;
- uint8_t id;
- uint16_t tag;
-} QEMU_PACKED P9Hdr;
-
-static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id,
- uint16_t tag)
-{
- P9Req *req = g_new0(P9Req, 1);
- uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */
- P9Hdr hdr = {
- .id = id,
- .tag = cpu_to_le16(tag)
- };
-
- g_assert_cmpint(total_size, <=, UINT32_MAX - size);
- total_size += size;
- hdr.size = cpu_to_le32(total_size);
-
- g_assert_cmpint(total_size, <=, P9_MAX_SIZE);
-
- req->qts = global_qtest;
- req->v9p = v9p;
- req->t_size = total_size;
- req->t_msg = guest_alloc(alloc, req->t_size);
- v9fs_memwrite(req, &hdr, 7);
- req->tag = tag;
- return req;
-}
-
-static void v9fs_req_send(P9Req *req)
-{
- QVirtio9P *v9p = req->v9p;
-
- req->r_msg = guest_alloc(alloc, P9_MAX_SIZE);
- req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size,
- false, true);
- qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false);
- qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head);
- req->t_off = 0;
-}
-
-static const char *rmessage_name(uint8_t id)
-{
- return
- id == P9_RLERROR ? "RLERROR" :
- id == P9_RVERSION ? "RVERSION" :
- id == P9_RATTACH ? "RATTACH" :
- id == P9_RWALK ? "RWALK" :
- id == P9_RLOPEN ? "RLOPEN" :
- id == P9_RWRITE ? "RWRITE" :
- id == P9_RMKDIR ? "RMKDIR" :
- id == P9_RLCREATE ? "RLCREATE" :
- id == P9_RSYMLINK ? "RSYMLINK" :
- id == P9_RLINK ? "RLINK" :
- id == P9_RUNLINKAT ? "RUNLINKAT" :
- id == P9_RFLUSH ? "RFLUSH" :
- id == P9_RREADDIR ? "READDIR" :
- "<unknown>";
-}
-
-static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len)
-{
- QVirtio9P *v9p = req->v9p;
-
- qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len,
- QVIRTIO_9P_TIMEOUT_US);
-}
-
-static void v9fs_req_recv(P9Req *req, uint8_t id)
-{
- P9Hdr hdr;
-
- v9fs_memread(req, &hdr, 7);
- hdr.size = ldl_le_p(&hdr.size);
- hdr.tag = lduw_le_p(&hdr.tag);
-
- g_assert_cmpint(hdr.size, >=, 7);
- g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE);
- g_assert_cmpint(hdr.tag, ==, req->tag);
-
- if (hdr.id != id) {
- g_printerr("Received response %d (%s) instead of %d (%s)\n",
- hdr.id, rmessage_name(hdr.id), id, rmessage_name(id));
-
- if (hdr.id == P9_RLERROR) {
- uint32_t err;
- v9fs_uint32_read(req, &err);
- g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err));
- }
- }
- g_assert_cmpint(hdr.id, ==, id);
-}
-
-static void v9fs_req_free(P9Req *req)
-{
- guest_free(alloc, req->t_msg);
- guest_free(alloc, req->r_msg);
- g_free(req);
-}
-
-/* size[4] Rlerror tag[2] ecode[4] */
-static void v9fs_rlerror(P9Req *req, uint32_t *err)
-{
- v9fs_req_recv(req, P9_RLERROR);
- v9fs_uint32_read(req, err);
- v9fs_req_free(req);
-}
-
-/* size[4] Tversion tag[2] msize[4] version[s] */
-static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version,
- uint16_t tag)
-{
- P9Req *req;
- uint32_t body_size = 4;
- uint16_t string_size = v9fs_string_size(version);
-
- g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
- body_size += string_size;
- req = v9fs_req_init(v9p, body_size, P9_TVERSION, tag);
-
- v9fs_uint32_write(req, msize);
- v9fs_string_write(req, version);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rversion tag[2] msize[4] version[s] */
-static void v9fs_rversion(P9Req *req, uint16_t *len, char **version)
-{
- uint32_t msize;
-
- v9fs_req_recv(req, P9_RVERSION);
- v9fs_uint32_read(req, &msize);
-
- g_assert_cmpint(msize, ==, P9_MAX_SIZE);
-
- if (len || version) {
- v9fs_string_read(req, len, version);
- }
-
- v9fs_req_free(req);
-}
-
-/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */
-static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname,
- uint16_t tag)
-{
- const char *uname = ""; /* ignored by QEMU */
- const char *aname = ""; /* ignored by QEMU */
- P9Req *req = v9fs_req_init(v9p, 4 + 4 + 2 + 2 + 4, P9_TATTACH, tag);
-
- v9fs_uint32_write(req, fid);
- v9fs_uint32_write(req, P9_NOFID);
- v9fs_string_write(req, uname);
- v9fs_string_write(req, aname);
- v9fs_uint32_write(req, n_uname);
- v9fs_req_send(req);
- return req;
-}
-
-/* type[1] version[4] path[8] */
-typedef char v9fs_qid[13];
-
static inline bool is_same_qid(v9fs_qid a, v9fs_qid b)
{
/* don't compare QID version for checking for file ID equalness */
return a[0] == b[0] && memcmp(&a[5], &b[5], 8) == 0;
}
-/* size[4] Rattach tag[2] qid[13] */
-static void v9fs_rattach(P9Req *req, v9fs_qid *qid)
-{
- v9fs_req_recv(req, P9_RATTACH);
- if (qid) {
- v9fs_memread(req, qid, 13);
- }
- v9fs_req_free(req);
-}
-
-/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
-static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid,
- uint16_t nwname, char *const wnames[], uint16_t tag)
-{
- P9Req *req;
- int i;
- uint32_t body_size = 4 + 4 + 2;
-
- for (i = 0; i < nwname; i++) {
- uint16_t wname_size = v9fs_string_size(wnames[i]);
-
- g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size);
- body_size += wname_size;
- }
- req = v9fs_req_init(v9p, body_size, P9_TWALK, tag);
- v9fs_uint32_write(req, fid);
- v9fs_uint32_write(req, newfid);
- v9fs_uint16_write(req, nwname);
- for (i = 0; i < nwname; i++) {
- v9fs_string_write(req, wnames[i]);
- }
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */
-static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid)
-{
- uint16_t local_nwqid;
-
- v9fs_req_recv(req, P9_RWALK);
- v9fs_uint16_read(req, &local_nwqid);
- if (nwqid) {
- *nwqid = local_nwqid;
- }
- if (wqid) {
- *wqid = g_malloc(local_nwqid * 13);
- v9fs_memread(req, *wqid, local_nwqid * 13);
- }
- v9fs_req_free(req);
-}
-
-/* size[4] Tgetattr tag[2] fid[4] request_mask[8] */
-static P9Req *v9fs_tgetattr(QVirtio9P *v9p, uint32_t fid, uint64_t request_mask,
- uint16_t tag)
-{
- P9Req *req;
-
- req = v9fs_req_init(v9p, 4 + 8, P9_TGETATTR, tag);
- v9fs_uint32_write(req, fid);
- v9fs_uint64_write(req, request_mask);
- v9fs_req_send(req);
- return req;
-}
-
-typedef struct v9fs_attr {
- uint64_t valid;
- v9fs_qid qid;
- uint32_t mode;
- uint32_t uid;
- uint32_t gid;
- uint64_t nlink;
- uint64_t rdev;
- uint64_t size;
- uint64_t blksize;
- uint64_t blocks;
- uint64_t atime_sec;
- uint64_t atime_nsec;
- uint64_t mtime_sec;
- uint64_t mtime_nsec;
- uint64_t ctime_sec;
- uint64_t ctime_nsec;
- uint64_t btime_sec;
- uint64_t btime_nsec;
- uint64_t gen;
- uint64_t data_version;
-} v9fs_attr;
-
-#define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
-
-/*
- * size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8]
- * rdev[8] size[8] blksize[8] blocks[8]
- * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]
- * ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8]
- * gen[8] data_version[8]
- */
-static void v9fs_rgetattr(P9Req *req, v9fs_attr *attr)
-{
- v9fs_req_recv(req, P9_RGETATTR);
-
- v9fs_uint64_read(req, &attr->valid);
- v9fs_memread(req, &attr->qid, 13);
- v9fs_uint32_read(req, &attr->mode);
- v9fs_uint32_read(req, &attr->uid);
- v9fs_uint32_read(req, &attr->gid);
- v9fs_uint64_read(req, &attr->nlink);
- v9fs_uint64_read(req, &attr->rdev);
- v9fs_uint64_read(req, &attr->size);
- v9fs_uint64_read(req, &attr->blksize);
- v9fs_uint64_read(req, &attr->blocks);
- v9fs_uint64_read(req, &attr->atime_sec);
- v9fs_uint64_read(req, &attr->atime_nsec);
- v9fs_uint64_read(req, &attr->mtime_sec);
- v9fs_uint64_read(req, &attr->mtime_nsec);
- v9fs_uint64_read(req, &attr->ctime_sec);
- v9fs_uint64_read(req, &attr->ctime_nsec);
- v9fs_uint64_read(req, &attr->btime_sec);
- v9fs_uint64_read(req, &attr->btime_nsec);
- v9fs_uint64_read(req, &attr->gen);
- v9fs_uint64_read(req, &attr->data_version);
-
- v9fs_req_free(req);
-}
-
-/* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */
-static P9Req *v9fs_treaddir(QVirtio9P *v9p, uint32_t fid, uint64_t offset,
- uint32_t count, uint16_t tag)
-{
- P9Req *req;
-
- req = v9fs_req_init(v9p, 4 + 8 + 4, P9_TREADDIR, tag);
- v9fs_uint32_write(req, fid);
- v9fs_uint64_write(req, offset);
- v9fs_uint32_write(req, count);
- v9fs_req_send(req);
- return req;
-}
-
-struct V9fsDirent {
- v9fs_qid qid;
- uint64_t offset;
- uint8_t type;
- char *name;
- struct V9fsDirent *next;
-};
-
-/* size[4] Rreaddir tag[2] count[4] data[count] */
-static void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries,
- struct V9fsDirent **entries)
-{
- uint32_t local_count;
- struct V9fsDirent *e = NULL;
- uint16_t slen;
- uint32_t n = 0;
-
- v9fs_req_recv(req, P9_RREADDIR);
- v9fs_uint32_read(req, &local_count);
-
- if (count) {
- *count = local_count;
- }
-
- for (int32_t togo = (int32_t)local_count;
- togo >= 13 + 8 + 1 + 2;
- togo -= 13 + 8 + 1 + 2 + slen, ++n)
- {
- if (!e) {
- e = g_new(struct V9fsDirent, 1);
- if (entries) {
- *entries = e;
- }
- } else {
- e = e->next = g_new(struct V9fsDirent, 1);
- }
- e->next = NULL;
- /* qid[13] offset[8] type[1] name[s] */
- v9fs_memread(req, &e->qid, 13);
- v9fs_uint64_read(req, &e->offset);
- v9fs_uint8_read(req, &e->type);
- v9fs_string_read(req, &slen, &e->name);
- }
-
- if (nentries) {
- *nentries = n;
- }
-
- v9fs_req_free(req);
-}
-
-static void v9fs_free_dirents(struct V9fsDirent *e)
-{
- struct V9fsDirent *next = NULL;
-
- for (; e; e = next) {
- next = e->next;
- g_free(e->name);
- g_free(e);
- }
-}
-
-/* size[4] Tlopen tag[2] fid[4] flags[4] */
-static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags,
- uint16_t tag)
-{
- P9Req *req;
-
- req = v9fs_req_init(v9p, 4 + 4, P9_TLOPEN, tag);
- v9fs_uint32_write(req, fid);
- v9fs_uint32_write(req, flags);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rlopen tag[2] qid[13] iounit[4] */
-static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit)
-{
- v9fs_req_recv(req, P9_RLOPEN);
- if (qid) {
- v9fs_memread(req, qid, 13);
- } else {
- v9fs_memskip(req, 13);
- }
- if (iounit) {
- v9fs_uint32_read(req, iounit);
- }
- v9fs_req_free(req);
-}
-
-/* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */
-static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset,
- uint32_t count, const void *data, uint16_t tag)
-{
- P9Req *req;
- uint32_t body_size = 4 + 8 + 4;
-
- g_assert_cmpint(body_size, <=, UINT32_MAX - count);
- body_size += count;
- req = v9fs_req_init(v9p, body_size, P9_TWRITE, tag);
- v9fs_uint32_write(req, fid);
- v9fs_uint64_write(req, offset);
- v9fs_uint32_write(req, count);
- v9fs_memwrite(req, data, count);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rwrite tag[2] count[4] */
-static void v9fs_rwrite(P9Req *req, uint32_t *count)
-{
- v9fs_req_recv(req, P9_RWRITE);
- if (count) {
- v9fs_uint32_read(req, count);
- }
- v9fs_req_free(req);
-}
-
-/* size[4] Tflush tag[2] oldtag[2] */
-static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag)
-{
- P9Req *req;
-
- req = v9fs_req_init(v9p, 2, P9_TFLUSH, tag);
- v9fs_uint32_write(req, oldtag);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rflush tag[2] */
-static void v9fs_rflush(P9Req *req)
-{
- v9fs_req_recv(req, P9_RFLUSH);
- v9fs_req_free(req);
-}
-
-static void do_version(QVirtio9P *v9p)
-{
- const char *version = "9P2000.L";
- uint16_t server_len;
- g_autofree char *server_version = NULL;
- P9Req *req;
-
- req = v9fs_tversion(v9p, P9_MAX_SIZE, version, P9_NOTAG);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rversion(req, &server_len, &server_version);
-
- g_assert_cmpmem(server_version, server_len, version, strlen(version));
-}
-
-/*
- * utility function: walk to requested dir and return fid for that dir and
- * the QIDs of server response
- */
-static uint32_t do_walk_rqids(QVirtio9P *v9p, const char *path, uint16_t *nwqid,
- v9fs_qid **wqid)
-{
- char **wnames;
- P9Req *req;
- const uint32_t fid = genfid();
-
- int nwnames = split(path, "/", &wnames);
-
- req = v9fs_twalk(v9p, 0, fid, nwnames, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, nwqid, wqid);
-
- split_free(&wnames);
- return fid;
-}
-
-/* utility function: walk to requested dir and return fid for that dir */
-static uint32_t do_walk(QVirtio9P *v9p, const char *path)
-{
- return do_walk_rqids(v9p, path, NULL, NULL);
-}
-
-/* utility function: walk to requested dir and expect passed error response */
-static void do_walk_expect_error(QVirtio9P *v9p, const char *path, uint32_t err)
-{
- char **wnames;
- P9Req *req;
- uint32_t _err;
- const uint32_t fid = genfid();
-
- int nwnames = split(path, "/", &wnames);
-
- req = v9fs_twalk(v9p, 0, fid, nwnames, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlerror(req, &_err);
-
- g_assert_cmpint(_err, ==, err);
-
- split_free(&wnames);
-}
-
static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc)
{
- alloc = t_alloc;
- do_version(obj);
-}
-
-static void do_attach_rqid(QVirtio9P *v9p, v9fs_qid *qid)
-{
- P9Req *req;
-
- do_version(v9p);
- req = v9fs_tattach(v9p, 0, getuid(), 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rattach(req, qid);
-}
-
-static void do_attach(QVirtio9P *v9p)
-{
- do_attach_rqid(v9p, NULL);
+ v9fs_set_allocator(t_alloc);
+ tversion({ .client = obj });
}
static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc)
{
- alloc = t_alloc;
- do_attach(obj);
+ v9fs_set_allocator(t_alloc);
+ tattach({ .client = obj });
}
static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
char *wnames[P9_MAXWELEM];
uint16_t nwqid;
g_autofree v9fs_qid *wqid = NULL;
int i;
- P9Req *req;
for (i = 0; i < P9_MAXWELEM; i++) {
wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i);
}
- do_attach(v9p);
- req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, &nwqid, &wqid);
+ tattach({ .client = v9p });
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1,
+ .nwname = P9_MAXWELEM, .wnames = wnames,
+ .rwalk = { .nwqid = &nwqid, .wqid = &wqid }
+ });
g_assert_cmpint(nwqid, ==, P9_MAXWELEM);
@@ -778,192 +102,37 @@ static bool fs_dirents_contain_name(struct V9fsDirent *e, const char* name)
return false;
}
-/* size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4] */
-static P9Req *v9fs_tmkdir(QVirtio9P *v9p, uint32_t dfid, const char *name,
- uint32_t mode, uint32_t gid, uint16_t tag)
-{
- P9Req *req;
-
- uint32_t body_size = 4 + 4 + 4;
- uint16_t string_size = v9fs_string_size(name);
-
- g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
- body_size += string_size;
-
- req = v9fs_req_init(v9p, body_size, P9_TMKDIR, tag);
- v9fs_uint32_write(req, dfid);
- v9fs_string_write(req, name);
- v9fs_uint32_write(req, mode);
- v9fs_uint32_write(req, gid);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rmkdir tag[2] qid[13] */
-static void v9fs_rmkdir(P9Req *req, v9fs_qid *qid)
-{
- v9fs_req_recv(req, P9_RMKDIR);
- if (qid) {
- v9fs_memread(req, qid, 13);
- } else {
- v9fs_memskip(req, 13);
- }
- v9fs_req_free(req);
-}
-
-/* size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4] */
-static P9Req *v9fs_tlcreate(QVirtio9P *v9p, uint32_t fid, const char *name,
- uint32_t flags, uint32_t mode, uint32_t gid,
- uint16_t tag)
-{
- P9Req *req;
-
- uint32_t body_size = 4 + 4 + 4 + 4;
- uint16_t string_size = v9fs_string_size(name);
-
- g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
- body_size += string_size;
-
- req = v9fs_req_init(v9p, body_size, P9_TLCREATE, tag);
- v9fs_uint32_write(req, fid);
- v9fs_string_write(req, name);
- v9fs_uint32_write(req, flags);
- v9fs_uint32_write(req, mode);
- v9fs_uint32_write(req, gid);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rlcreate tag[2] qid[13] iounit[4] */
-static void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit)
-{
- v9fs_req_recv(req, P9_RLCREATE);
- if (qid) {
- v9fs_memread(req, qid, 13);
- } else {
- v9fs_memskip(req, 13);
- }
- if (iounit) {
- v9fs_uint32_read(req, iounit);
- }
- v9fs_req_free(req);
-}
-
-/* size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4] */
-static P9Req *v9fs_tsymlink(QVirtio9P *v9p, uint32_t fid, const char *name,
- const char *symtgt, uint32_t gid, uint16_t tag)
-{
- P9Req *req;
-
- uint32_t body_size = 4 + 4;
- uint16_t string_size = v9fs_string_size(name) + v9fs_string_size(symtgt);
-
- g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
- body_size += string_size;
-
- req = v9fs_req_init(v9p, body_size, P9_TSYMLINK, tag);
- v9fs_uint32_write(req, fid);
- v9fs_string_write(req, name);
- v9fs_string_write(req, symtgt);
- v9fs_uint32_write(req, gid);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rsymlink tag[2] qid[13] */
-static void v9fs_rsymlink(P9Req *req, v9fs_qid *qid)
-{
- v9fs_req_recv(req, P9_RSYMLINK);
- if (qid) {
- v9fs_memread(req, qid, 13);
- } else {
- v9fs_memskip(req, 13);
- }
- v9fs_req_free(req);
-}
-
-/* size[4] Tlink tag[2] dfid[4] fid[4] name[s] */
-static P9Req *v9fs_tlink(QVirtio9P *v9p, uint32_t dfid, uint32_t fid,
- const char *name, uint16_t tag)
-{
- P9Req *req;
-
- uint32_t body_size = 4 + 4;
- uint16_t string_size = v9fs_string_size(name);
-
- g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
- body_size += string_size;
-
- req = v9fs_req_init(v9p, body_size, P9_TLINK, tag);
- v9fs_uint32_write(req, dfid);
- v9fs_uint32_write(req, fid);
- v9fs_string_write(req, name);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Rlink tag[2] */
-static void v9fs_rlink(P9Req *req)
-{
- v9fs_req_recv(req, P9_RLINK);
- v9fs_req_free(req);
-}
-
-/* size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4] */
-static P9Req *v9fs_tunlinkat(QVirtio9P *v9p, uint32_t dirfd, const char *name,
- uint32_t flags, uint16_t tag)
-{
- P9Req *req;
-
- uint32_t body_size = 4 + 4;
- uint16_t string_size = v9fs_string_size(name);
-
- g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
- body_size += string_size;
-
- req = v9fs_req_init(v9p, body_size, P9_TUNLINKAT, tag);
- v9fs_uint32_write(req, dirfd);
- v9fs_string_write(req, name);
- v9fs_uint32_write(req, flags);
- v9fs_req_send(req);
- return req;
-}
-
-/* size[4] Runlinkat tag[2] */
-static void v9fs_runlinkat(P9Req *req)
-{
- v9fs_req_recv(req, P9_RUNLINKAT);
- v9fs_req_free(req);
-}
-
/* basic readdir test where reply fits into a single response message */
static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
- char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
+ v9fs_set_allocator(t_alloc);
+ char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
uint16_t nqid;
v9fs_qid qid;
uint32_t count, nentries;
struct V9fsDirent *entries = NULL;
- P9Req *req;
- do_attach(v9p);
- req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, &nqid, NULL);
+ tattach({ .client = v9p });
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1,
+ .nwname = 1, .wnames = wnames, .rwalk.nwqid = &nqid
+ });
g_assert_cmpint(nqid, ==, 1);
- req = v9fs_tlopen(v9p, 1, O_DIRECTORY, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlopen(req, &qid, NULL);
+ tlopen({
+ .client = v9p, .fid = 1, .flags = O_DIRECTORY, .rlopen.qid = &qid
+ });
/*
* submit count = msize - 11, because 11 is the header size of Rreaddir
*/
- req = v9fs_treaddir(v9p, 1, 0, P9_MAX_SIZE - 11, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rreaddir(req, &count, &nentries, &entries);
+ treaddir({
+ .client = v9p, .fid = 1, .offset = 0, .count = P9_MAX_SIZE - 11,
+ .rreaddir = {
+ .count = &count, .nentries = &nentries, .entries = &entries
+ }
+ });
/*
* Assuming msize (P9_MAX_SIZE) is large enough so we can retrieve all
@@ -993,16 +162,15 @@ static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc)
/* readdir test where overall request is split over several messages */
static void do_readdir_split(QVirtio9P *v9p, uint32_t count)
{
- char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
+ char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) };
uint16_t nqid;
v9fs_qid qid;
uint32_t nentries, npartialentries;
struct V9fsDirent *entries, *tail, *partialentries;
- P9Req *req;
int fid;
uint64_t offset;
- do_attach(v9p);
+ tattach({ .client = v9p });
fid = 1;
offset = 0;
@@ -1010,14 +178,15 @@ static void do_readdir_split(QVirtio9P *v9p, uint32_t count)
nentries = 0;
tail = NULL;
- req = v9fs_twalk(v9p, 0, fid, 1, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, &nqid, NULL);
+ twalk({
+ .client = v9p, .fid = 0, .newfid = fid,
+ .nwname = 1, .wnames = wnames, .rwalk.nwqid = &nqid
+ });
g_assert_cmpint(nqid, ==, 1);
- req = v9fs_tlopen(v9p, fid, O_DIRECTORY, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlopen(req, &qid, NULL);
+ tlopen({
+ .client = v9p, .fid = fid, .flags = O_DIRECTORY, .rlopen.qid = &qid
+ });
/*
* send as many Treaddir requests as required to get all directory
@@ -1027,9 +196,13 @@ static void do_readdir_split(QVirtio9P *v9p, uint32_t count)
npartialentries = 0;
partialentries = NULL;
- req = v9fs_treaddir(v9p, fid, offset, count, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rreaddir(req, &count, &npartialentries, &partialentries);
+ treaddir({
+ .client = v9p, .fid = fid, .offset = offset, .count = count,
+ .rreaddir = {
+ .count = &count, .nentries = &npartialentries,
+ .entries = &partialentries
+ }
+ });
if (npartialentries > 0 && partialentries) {
if (!entries) {
entries = partialentries;
@@ -1073,17 +246,14 @@ static void do_readdir_split(QVirtio9P *v9p, uint32_t count)
static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
- char *const wnames[] = { g_strdup(" /") };
- P9Req *req;
- uint32_t err;
+ v9fs_set_allocator(t_alloc);
+ char *wnames[] = { g_strdup(" /") };
- do_attach(v9p);
- req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlerror(req, &err);
-
- g_assert_cmpint(err, ==, ENOENT);
+ tattach({ .client = v9p });
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames,
+ .expectErr = ENOENT
+ });
g_free(wnames[0]);
}
@@ -1091,32 +261,34 @@ static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_walk_nonexistent(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
- do_attach(v9p);
+ tattach({ .client = v9p });
/*
* The 9p2000 protocol spec says: "If the first element cannot be walked
* for any reason, Rerror is returned."
*/
- do_walk_expect_error(v9p, "non-existent", ENOENT);
+ twalk({ .client = v9p, .path = "non-existent", .expectErr = ENOENT });
}
static void fs_walk_2nd_nonexistent(void *obj, void *data,
QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
v9fs_qid root_qid;
uint16_t nwqid;
- uint32_t fid, err;
- P9Req *req;
+ uint32_t fid;
g_autofree v9fs_qid *wqid = NULL;
g_autofree char *path = g_strdup_printf(
QTEST_V9FS_SYNTH_WALK_FILE "/non-existent", 0
);
- do_attach_rqid(v9p, &root_qid);
- fid = do_walk_rqids(v9p, path, &nwqid, &wqid);
+ tattach({ .client = v9p, .rattach.qid = &root_qid });
+ fid = twalk({
+ .client = v9p, .path = path,
+ .rwalk = { .nwqid = &nwqid, .wqid = &wqid }
+ }).newfid;
/*
* The 9p2000 protocol spec says: "nwqid is therefore either nwname or the
* index of the first elementwise walk that failed."
@@ -1127,37 +299,38 @@ static void fs_walk_2nd_nonexistent(void *obj, void *data,
g_assert(wqid && wqid[0] && !is_same_qid(root_qid, wqid[0]));
/* expect fid being unaffected by walk above */
- req = v9fs_tgetattr(v9p, fid, P9_GETATTR_BASIC, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlerror(req, &err);
-
- g_assert_cmpint(err, ==, ENOENT);
+ tgetattr({
+ .client = v9p, .fid = fid, .request_mask = P9_GETATTR_BASIC,
+ .expectErr = ENOENT
+ });
}
static void fs_walk_none(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
v9fs_qid root_qid;
g_autofree v9fs_qid *wqid = NULL;
- P9Req *req;
struct v9fs_attr attr;
- do_version(v9p);
- req = v9fs_tattach(v9p, 0, getuid(), 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rattach(req, &root_qid);
+ tversion({ .client = v9p });
+ tattach({
+ .client = v9p, .fid = 0, .n_uname = getuid(),
+ .rattach.qid = &root_qid
+ });
- req = v9fs_twalk(v9p, 0, 1, 0, NULL, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, NULL, &wqid);
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1, .nwname = 0, .wnames = NULL,
+ .rwalk.wqid = &wqid
+ });
/* special case: no QID is returned if nwname=0 was sent */
g_assert(wqid == NULL);
- req = v9fs_tgetattr(v9p, 1, P9_GETATTR_BASIC, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rgetattr(req, &attr);
+ tgetattr({
+ .client = v9p, .fid = 1, .request_mask = P9_GETATTR_BASIC,
+ .rgetattr.attr = &attr
+ });
g_assert(is_same_qid(root_qid, attr.qid));
}
@@ -1165,20 +338,21 @@ static void fs_walk_none(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
- char *const wnames[] = { g_strdup("..") };
+ v9fs_set_allocator(t_alloc);
+ char *wnames[] = { g_strdup("..") };
v9fs_qid root_qid;
g_autofree v9fs_qid *wqid = NULL;
- P9Req *req;
- do_version(v9p);
- req = v9fs_tattach(v9p, 0, getuid(), 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rattach(req, &root_qid);
+ tversion({ .client = v9p });
+ tattach({
+ .client = v9p, .fid = 0, .n_uname = getuid(),
+ .rattach.qid = &root_qid
+ });
- req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, NULL, &wqid); /* We now we'll get one qid */
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames,
+ .rwalk.wqid = &wqid /* We now we'll get one qid */
+ });
g_assert_cmpmem(&root_qid, 13, wqid[0], 13);
@@ -1188,18 +362,15 @@ static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
- char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) };
- P9Req *req;
+ v9fs_set_allocator(t_alloc);
+ char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) };
- do_attach(v9p);
- req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, NULL, NULL);
+ tattach({ .client = v9p });
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
+ });
- req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlopen(req, NULL, NULL);
+ tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY });
g_free(wnames[0]);
}
@@ -1207,25 +378,23 @@ static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
static const uint32_t write_count = P9_MAX_SIZE / 2;
- char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) };
+ char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) };
g_autofree char *buf = g_malloc0(write_count);
uint32_t count;
- P9Req *req;
- do_attach(v9p);
- req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, NULL, NULL);
+ tattach({ .client = v9p });
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
+ });
- req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlopen(req, NULL, NULL);
+ tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY });
- req = v9fs_twrite(v9p, 1, 0, write_count, buf, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwrite(req, &count);
+ count = twrite({
+ .client = v9p, .fid = 1, .offset = 0, .count = write_count,
+ .data = buf
+ }).count;
g_assert_cmpint(count, ==, write_count);
g_free(wnames[0]);
@@ -1234,28 +403,32 @@ static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
- char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
+ v9fs_set_allocator(t_alloc);
+ char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
P9Req *req, *flush_req;
uint32_t reply_len;
uint8_t should_block;
- do_attach(v9p);
- req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, NULL, NULL);
+ tattach({ .client = v9p });
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
+ });
- req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlopen(req, NULL, NULL);
+ tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY });
/* This will cause the 9p server to try to write data to the backend,
* until the write request gets cancelled.
*/
should_block = 1;
- req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0);
+ req = twrite({
+ .client = v9p, .fid = 1, .offset = 0,
+ .count = sizeof(should_block), .data = &should_block,
+ .requestOnly = true
+ }).req;
- flush_req = v9fs_tflush(v9p, req->tag, 1);
+ flush_req = tflush({
+ .client = v9p, .oldtag = req->tag, .tag = 1, .requestOnly = true
+ }).req;
/* The write request is supposed to be flushed: the server should just
* mark the write request as used and reply to the flush request.
@@ -1271,28 +444,32 @@ static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
- char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
+ v9fs_set_allocator(t_alloc);
+ char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
P9Req *req, *flush_req;
uint32_t count;
uint8_t should_block;
- do_attach(v9p);
- req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rwalk(req, NULL, NULL);
+ tattach({ .client = v9p });
+ twalk({
+ .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames
+ });
- req = v9fs_tlopen(v9p, 1, O_WRONLY, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlopen(req, NULL, NULL);
+ tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY });
/* This will cause the write request to complete right away, before it
* could be actually cancelled.
*/
should_block = 0;
- req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0);
+ req = twrite({
+ .client = v9p, .fid = 1, .offset = 0,
+ .count = sizeof(should_block), .data = &should_block,
+ .requestOnly = true
+ }).req;
- flush_req = v9fs_tflush(v9p, req->tag, 1);
+ flush_req = tflush({
+ .client = v9p, .oldtag = req->tag, .tag = 1, .requestOnly = true
+ }).req;
/* The write request is supposed to complete. The server should
* reply to the write request and the flush request.
@@ -1305,99 +482,24 @@ static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc)
g_free(wnames[0]);
}
-static void do_mkdir(QVirtio9P *v9p, const char *path, const char *cname)
-{
- g_autofree char *name = g_strdup(cname);
- uint32_t fid;
- P9Req *req;
-
- fid = do_walk(v9p, path);
-
- req = v9fs_tmkdir(v9p, fid, name, 0750, 0, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rmkdir(req, NULL);
-}
-
-/* create a regular file with Tlcreate and return file's fid */
-static uint32_t do_lcreate(QVirtio9P *v9p, const char *path,
- const char *cname)
-{
- g_autofree char *name = g_strdup(cname);
- uint32_t fid;
- P9Req *req;
-
- fid = do_walk(v9p, path);
-
- req = v9fs_tlcreate(v9p, fid, name, 0, 0750, 0, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlcreate(req, NULL, NULL);
-
- return fid;
-}
-
-/* create symlink named @a clink in directory @a path pointing to @a to */
-static void do_symlink(QVirtio9P *v9p, const char *path, const char *clink,
- const char *to)
-{
- g_autofree char *name = g_strdup(clink);
- g_autofree char *dst = g_strdup(to);
- uint32_t fid;
- P9Req *req;
-
- fid = do_walk(v9p, path);
-
- req = v9fs_tsymlink(v9p, fid, name, dst, 0, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rsymlink(req, NULL);
-}
-
-/* create a hard link named @a clink in directory @a path pointing to @a to */
-static void do_hardlink(QVirtio9P *v9p, const char *path, const char *clink,
- const char *to)
-{
- uint32_t dfid, fid;
- P9Req *req;
-
- dfid = do_walk(v9p, path);
- fid = do_walk(v9p, to);
-
- req = v9fs_tlink(v9p, dfid, fid, clink, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_rlink(req);
-}
-
-static void do_unlinkat(QVirtio9P *v9p, const char *atpath, const char *rpath,
- uint32_t flags)
-{
- g_autofree char *name = g_strdup(rpath);
- uint32_t fid;
- P9Req *req;
-
- fid = do_walk(v9p, atpath);
-
- req = v9fs_tunlinkat(v9p, fid, name, flags, 0);
- v9fs_req_wait_for_reply(req, NULL);
- v9fs_runlinkat(req);
-}
-
static void fs_readdir_split_128(void *obj, void *data,
QGuestAllocator *t_alloc)
{
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
do_readdir_split(obj, 128);
}
static void fs_readdir_split_256(void *obj, void *data,
QGuestAllocator *t_alloc)
{
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
do_readdir_split(obj, 256);
}
static void fs_readdir_split_512(void *obj, void *data,
QGuestAllocator *t_alloc)
{
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
do_readdir_split(obj, 512);
}
@@ -1407,15 +509,15 @@ static void fs_readdir_split_512(void *obj, void *data,
static void fs_create_dir(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
struct stat st;
g_autofree char *root_path = virtio_9p_test_path("");
g_autofree char *new_dir = virtio_9p_test_path("01");
g_assert(root_path != NULL);
- do_attach(v9p);
- do_mkdir(v9p, "/", "01");
+ tattach({ .client = v9p });
+ tmkdir({ .client = v9p, .atPath = "/", .name = "01" });
/* check if created directory really exists now ... */
g_assert(stat(new_dir, &st) == 0);
@@ -1426,22 +528,25 @@ static void fs_create_dir(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_unlinkat_dir(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
struct stat st;
g_autofree char *root_path = virtio_9p_test_path("");
g_autofree char *new_dir = virtio_9p_test_path("02");
g_assert(root_path != NULL);
- do_attach(v9p);
- do_mkdir(v9p, "/", "02");
+ tattach({ .client = v9p });
+ tmkdir({ .client = v9p, .atPath = "/", .name = "02" });
/* check if created directory really exists now ... */
g_assert(stat(new_dir, &st) == 0);
/* ... and is actually a directory */
g_assert((st.st_mode & S_IFMT) == S_IFDIR);
- do_unlinkat(v9p, "/", "02", P9_DOTL_AT_REMOVEDIR);
+ tunlinkat({
+ .client = v9p, .atPath = "/", .name = "02",
+ .flags = P9_DOTL_AT_REMOVEDIR
+ });
/* directory should be gone now */
g_assert(stat(new_dir, &st) != 0);
}
@@ -1449,13 +554,13 @@ static void fs_unlinkat_dir(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_create_file(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
struct stat st;
g_autofree char *new_file = virtio_9p_test_path("03/1st_file");
- do_attach(v9p);
- do_mkdir(v9p, "/", "03");
- do_lcreate(v9p, "03", "1st_file");
+ tattach({ .client = v9p });
+ tmkdir({ .client = v9p, .atPath = "/", .name = "03" });
+ tlcreate({ .client = v9p, .atPath = "03", .name = "1st_file" });
/* check if created file exists now ... */
g_assert(stat(new_file, &st) == 0);
@@ -1466,20 +571,20 @@ static void fs_create_file(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_unlinkat_file(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
struct stat st;
g_autofree char *new_file = virtio_9p_test_path("04/doa_file");
- do_attach(v9p);
- do_mkdir(v9p, "/", "04");
- do_lcreate(v9p, "04", "doa_file");
+ tattach({ .client = v9p });
+ tmkdir({ .client = v9p, .atPath = "/", .name = "04" });
+ tlcreate({ .client = v9p, .atPath = "04", .name = "doa_file" });
/* check if created file exists now ... */
g_assert(stat(new_file, &st) == 0);
/* ... and is a regular file */
g_assert((st.st_mode & S_IFMT) == S_IFREG);
- do_unlinkat(v9p, "04", "doa_file", 0);
+ tunlinkat({ .client = v9p, .atPath = "04", .name = "doa_file" });
/* file should be gone now */
g_assert(stat(new_file, &st) != 0);
}
@@ -1487,18 +592,21 @@ static void fs_unlinkat_file(void *obj, void *data, QGuestAllocator *t_alloc)
static void fs_symlink_file(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
struct stat st;
g_autofree char *real_file = virtio_9p_test_path("05/real_file");
g_autofree char *symlink_file = virtio_9p_test_path("05/symlink_file");
- do_attach(v9p);
- do_mkdir(v9p, "/", "05");
- do_lcreate(v9p, "05", "real_file");
+ tattach({ .client = v9p });
+ tmkdir({ .client = v9p, .atPath = "/", .name = "05" });
+ tlcreate({ .client = v9p, .atPath = "05", .name = "real_file" });
g_assert(stat(real_file, &st) == 0);
g_assert((st.st_mode & S_IFMT) == S_IFREG);
- do_symlink(v9p, "05", "symlink_file", "real_file");
+ tsymlink({
+ .client = v9p, .atPath = "05", .name = "symlink_file",
+ .symtgt = "real_file"
+ });
/* check if created link exists now */
g_assert(stat(symlink_file, &st) == 0);
@@ -1508,21 +616,24 @@ static void fs_unlinkat_symlink(void *obj, void *data,
QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
struct stat st;
g_autofree char *real_file = virtio_9p_test_path("06/real_file");
g_autofree char *symlink_file = virtio_9p_test_path("06/symlink_file");
- do_attach(v9p);
- do_mkdir(v9p, "/", "06");
- do_lcreate(v9p, "06", "real_file");
+ tattach({ .client = v9p });
+ tmkdir({ .client = v9p, .atPath = "/", .name = "06" });
+ tlcreate({ .client = v9p, .atPath = "06", .name = "real_file" });
g_assert(stat(real_file, &st) == 0);
g_assert((st.st_mode & S_IFMT) == S_IFREG);
- do_symlink(v9p, "06", "symlink_file", "real_file");
+ tsymlink({
+ .client = v9p, .atPath = "06", .name = "symlink_file",
+ .symtgt = "real_file"
+ });
g_assert(stat(symlink_file, &st) == 0);
- do_unlinkat(v9p, "06", "symlink_file", 0);
+ tunlinkat({ .client = v9p, .atPath = "06", .name = "symlink_file" });
/* symlink should be gone now */
g_assert(stat(symlink_file, &st) != 0);
}
@@ -1530,18 +641,21 @@ static void fs_unlinkat_symlink(void *obj, void *data,
static void fs_hardlink_file(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
struct stat st_real, st_link;
g_autofree char *real_file = virtio_9p_test_path("07/real_file");
g_autofree char *hardlink_file = virtio_9p_test_path("07/hardlink_file");
- do_attach(v9p);
- do_mkdir(v9p, "/", "07");
- do_lcreate(v9p, "07", "real_file");
+ tattach({ .client = v9p });
+ tmkdir({ .client = v9p, .atPath = "/", .name = "07" });
+ tlcreate({ .client = v9p, .atPath = "07", .name = "real_file" });
g_assert(stat(real_file, &st_real) == 0);
g_assert((st_real.st_mode & S_IFMT) == S_IFREG);
- do_hardlink(v9p, "07", "hardlink_file", "07/real_file");
+ tlink({
+ .client = v9p, .atPath = "07", .name = "hardlink_file",
+ .toPath = "07/real_file"
+ });
/* check if link exists now ... */
g_assert(stat(hardlink_file, &st_link) == 0);
@@ -1555,21 +669,24 @@ static void fs_unlinkat_hardlink(void *obj, void *data,
QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
- alloc = t_alloc;
+ v9fs_set_allocator(t_alloc);
struct stat st_real, st_link;
g_autofree char *real_file = virtio_9p_test_path("08/real_file");
g_autofree char *hardlink_file = virtio_9p_test_path("08/hardlink_file");
- do_attach(v9p);
- do_mkdir(v9p, "/", "08");
- do_lcreate(v9p, "08", "real_file");
+ tattach({ .client = v9p });
+ tmkdir({ .client = v9p, .atPath = "/", .name = "08" });
+ tlcreate({ .client = v9p, .atPath = "08", .name = "real_file" });
g_assert(stat(real_file, &st_real) == 0);
g_assert((st_real.st_mode & S_IFMT) == S_IFREG);
- do_hardlink(v9p, "08", "hardlink_file", "08/real_file");
+ tlink({
+ .client = v9p, .atPath = "08", .name = "hardlink_file",
+ .toPath = "08/real_file"
+ });
g_assert(stat(hardlink_file, &st_link) == 0);
- do_unlinkat(v9p, "08", "hardlink_file", 0);
+ tunlinkat({ .client = v9p, .atPath = "08", .name = "hardlink_file" });
/* symlink should be gone now */
g_assert(stat(hardlink_file, &st_link) != 0);
/* and old file should still exist */
diff --git a/tests/tcg/i386/test-avx.c b/tests/tcg/i386/test-avx.c
index 953e2906fe..c39c0e5bce 100644
--- a/tests/tcg/i386/test-avx.c
+++ b/tests/tcg/i386/test-avx.c
@@ -28,6 +28,7 @@ typedef struct {
} TestDef;
reg_state initI;
+reg_state initF16;
reg_state initF32;
reg_state initF64;
@@ -221,6 +222,7 @@ static void run_all(void)
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
+uint16_t val_f16[] = { 0x4000, 0xbc00, 0x44cd, 0x3a66, 0x4200, 0x7a1a, 0x4780, 0x4826 };
float val_f32[] = {2.0, -1.0, 4.8, 0.8, 3, -42.0, 5e6, 7.5, 8.3};
double val_f64[] = {2.0, -1.0, 4.8, 0.8, 3, -42.0, 5e6, 7.5};
v4di val_i64[] = {
@@ -241,6 +243,12 @@ v4di indexd = {0x00000002000000efull, 0xfffffff500000010ull,
v4di gather_mem[0x20];
+void init_f16reg(v4di *r)
+{
+ memset(r, 0, sizeof(*r));
+ memcpy(r, val_f16, sizeof(val_f16));
+}
+
void init_f32reg(v4di *r)
{
static int n;
@@ -315,6 +323,15 @@ int main(int argc, char *argv[])
printf("Int:\n");
dump_regs(&initI);
+ init_all(&initF16);
+ init_f16reg(&initF16.ymm[10]);
+ init_f16reg(&initF16.ymm[11]);
+ init_f16reg(&initF16.ymm[12]);
+ init_f16reg(&initF16.mem0[1]);
+ initF16.ff = 16;
+ printf("F16:\n");
+ dump_regs(&initF16);
+
init_all(&initF32);
init_f32reg(&initF32.ymm[10]);
init_f32reg(&initF32.ymm[11]);
diff --git a/tests/tcg/i386/test-avx.py b/tests/tcg/i386/test-avx.py
index 02982329f1..d9ca00a49e 100755
--- a/tests/tcg/i386/test-avx.py
+++ b/tests/tcg/i386/test-avx.py
@@ -9,6 +9,7 @@ from fnmatch import fnmatch
archs = [
"SSE", "SSE2", "SSE3", "SSSE3", "SSE4_1", "SSE4_2",
"AES", "AVX", "AVX2", "AES+AVX", "VAES+AVX",
+ "F16C", "FMA",
]
ignore = set(["FISTTP",
@@ -19,6 +20,7 @@ imask = {
'vBLENDPS': 0x0f,
'CMP[PS][SD]': 0x07,
'VCMP[PS][SD]': 0x1f,
+ 'vCVTPS2PH': 0x7,
'vDPPD': 0x33,
'vDPPS': 0xff,
'vEXTRACTPS': 0x03,
@@ -221,8 +223,10 @@ def ArgGenerator(arg, op):
class InsnGenerator:
def __init__(self, op, args):
self.op = op
- if op[-2:] in ["PS", "PD", "SS", "SD"]:
- if op[-1] == 'S':
+ if op[-2:] in ["PH", "PS", "PD", "SS", "SD"]:
+ if op[-1] == 'H':
+ self.optype = 'F16'
+ elif op[-1] == 'S':
self.optype = 'F32'
else:
self.optype = 'F64'