Compare commits
109 Commits
master
...
stable-2.1
Author | SHA1 | Date |
---|---|---|
Michael Roth | e22f675bdd | |
Fam Zheng | aae299a68d | |
KONRAD Frederic | b102aea574 | |
Christian Borntraeger | 336cd382dc | |
Marc-André Lureau | bf1cb819e9 | |
Markus Armbruster | 08c4a51c65 | |
Markus Armbruster | 441784598e | |
Michael Roth | 2f36efaeb1 | |
Marc-André Lureau | 90b2d94123 | |
Cornelia Huck | c16427177a | |
Richard Henderson | e8488edcb3 | |
Peter Lieven | 3afe55ff38 | |
Yunjian Wang | 1b817abcd4 | |
Emilio G. Cota | 0935356e43 | |
Pankaj Gupta | d109f8eb7e | |
Eric Blake | 2379ac134a | |
Eric Blake | f8b3b02933 | |
Richard Henderson | 78747264b9 | |
Vladimir Sementsov-Ogievskiy | d8a7ec1deb | |
Alex Williamson | 2cb041a82d | |
Olaf Hering | 6d3ed3798b | |
Kevin Wolf | 58119514f5 | |
Marc-André Lureau | 008ffc7a2f | |
Philippe Mathieu-Daudé | 5e10c00f61 | |
Alex Bennée | ca11f0ab77 | |
Alex Bennée | ffc3a15018 | |
Alex Bennée | f3816879f9 | |
Alex Bennée | 246dad2f3c | |
Richard Henderson | 0819a17250 | |
Richard Henderson | 7133cd4cfe | |
Richard Henderson | d1ed4a60ba | |
Richard Henderson | 7c38f3703d | |
Richard Henderson | baa552e54f | |
Richard Henderson | 4ec6a17a04 | |
Petr Tesarik | 781cde6d94 | |
Richard Henderson | e5af958dd2 | |
Richard Henderson | c708ce7d6e | |
Richard Henderson | 0aaf1cca02 | |
Richard Henderson | 994b0cf997 | |
Peter Maydell | e653eee8d8 | |
Peter Maydell | fbaeb1068c | |
John Snow | 0779afdc89 | |
Michael Clark | 4a67f4a953 | |
Richard Henderson | 9363c34825 | |
Peter Maydell | 51d5decb32 | |
Peter Maydell | 0e4b4b4fd3 | |
Eric Blake | 6951158023 | |
KONRAD Frederic | b129914a8d | |
KONRAD Frederic | db6f66eff7 | |
KONRAD Frederic | 26cf05c1a1 | |
Max Reitz | 7bc615f88f | |
Max Reitz | 1eddfab31c | |
Laszlo Ersek | 3882183fda | |
Vladimir Sementsov-Ogievskiy | 3b52d47418 | |
Vladimir Sementsov-Ogievskiy | f155487bef | |
Cédric Le Goater | 54eb6cc6d7 | |
Stefan Hajnoczi | 9eb3e5a8a8 | |
Gerd Hoffmann | 05a3e663b1 | |
Philippe Mathieu-Daudé | 073198b8e8 | |
Philippe Mathieu-Daudé | 5da7e93f51 | |
Shannon Zhao | c5dd07b529 | |
Eric Blake | 396d79c36c | |
Eric Blake | 26cdf35f69 | |
linzhecheng | fb7f173c2c | |
Brijesh Singh | 2f2b189235 | |
Konrad Rzeszutek Wilk | 43163837d3 | |
Konrad Rzeszutek Wilk | 3129ddb943 | |
Daniel P. Berrangé | 8a302f42a5 | |
Alberto Garcia | ef67e67388 | |
Max Reitz | 081eac8b30 | |
Max Reitz | 5aa76f3a8c | |
Max Reitz | bd64fec665 | |
Shannon Zhao | 5459c0c458 | |
John Snow | 5c9266fa97 | |
John Thomson | df00a166c4 | |
Shannon Zhao | 77df190051 | |
Peter Xu | f4b4095a8f | |
Peter Xu | 08aa25f5f8 | |
Peter Xu | d5c60a950a | |
Peter Xu | 78b85a98a3 | |
Peter Xu | 28048f7bcd | |
Peter Xu | 1e5b93f620 | |
Peter Xu | 5cf61b56a4 | |
Peter Xu | d64604326f | |
Peter Xu | 93a53137be | |
Jan Kiszka | 91f6149592 | |
Michal Privoznik | 81e46e3c82 | |
Cornelia Huck | a5c8fbbeac | |
Cornelia Huck | c9bb077871 | |
Thomas Huth | 3372a3168a | |
Cornelia Huck | 87efdb9820 | |
Fam Zheng | 51691e9244 | |
Michael Walle | 4f9df08749 | |
Max Reitz | ca3150da6d | |
Max Reitz | 9e724c05a0 | |
Max Reitz | e8d8f6a3aa | |
Max Reitz | b3a18683f9 | |
Max Reitz | f9e0e53add | |
Olaf Hering | f81672a5c6 | |
Gerd Hoffmann | 9ec09b6542 | |
Greg Kurz | 2dbaba7af0 | |
Greg Kurz | 62f7a38610 | |
Peter Maydell | 1ace462f9b | |
Henry Wertz | b90c93106e | |
Cornelia Huck | 38b7a3ea72 | |
Eric Blake | cb7a41f3f9 | |
Jason Andryuk | 8ca471da10 | |
Geert Uytterhoeven | 1783745673 | |
Marc-André Lureau | 4319ae939c |
|
@ -1781,6 +1781,12 @@ F: include/sysemu/replay.h
|
|||
F: docs/replay.txt
|
||||
F: stubs/replay.c
|
||||
|
||||
IOVA Tree
|
||||
M: Peter Xu <peterx@redhat.com>
|
||||
S: Maintained
|
||||
F: include/qemu/iova-tree.h
|
||||
F: util/iova-tree.c
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
|
|
17
block.c
17
block.c
|
@ -1620,13 +1620,24 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs)
|
|||
|
||||
/* Returns whether the image file can be written to after the reopen queue @q
|
||||
* has been successfully applied, or right now if @q is NULL. */
|
||||
static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q)
|
||||
static bool bdrv_is_writable_after_reopen(BlockDriverState *bs,
|
||||
BlockReopenQueue *q)
|
||||
{
|
||||
int flags = bdrv_reopen_get_flags(q, bs);
|
||||
|
||||
return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether the BDS can be written to. This is not necessarily
|
||||
* the same as !bdrv_is_read_only(bs), as inactivated images may not
|
||||
* be written to but do not count as read-only images.
|
||||
*/
|
||||
bool bdrv_is_writable(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_is_writable_after_reopen(bs, NULL);
|
||||
}
|
||||
|
||||
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
|
||||
BdrvChild *c, const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
|
@ -1664,7 +1675,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|||
|
||||
/* Write permissions never work with read-only images */
|
||||
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
|
||||
!bdrv_is_writable(bs, q))
|
||||
!bdrv_is_writable_after_reopen(bs, q))
|
||||
{
|
||||
error_setg(errp, "Block node is read-only");
|
||||
return -EPERM;
|
||||
|
@ -1956,7 +1967,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
|||
&perm, &shared);
|
||||
|
||||
/* Format drivers may touch metadata even if the guest doesn't write */
|
||||
if (bdrv_is_writable(bs, reopen_queue)) {
|
||||
if (bdrv_is_writable_after_reopen(bs, reopen_queue)) {
|
||||
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1728,6 +1728,9 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
|
|||
num = MIN(left, 65536);
|
||||
result = write(fd, buf, num);
|
||||
if (result < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
result = -errno;
|
||||
error_setg_errno(errp, -result,
|
||||
"Could not write zeros for preallocation");
|
||||
|
|
|
@ -732,7 +732,7 @@ retry:
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
*pnum = lbasd->num_blocks * iscsilun->block_size;
|
||||
*pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size;
|
||||
|
||||
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
|
||||
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
||||
|
|
|
@ -868,12 +868,16 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
}
|
||||
|
||||
ret = 0;
|
||||
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common) && s->common.force) {
|
||||
break;
|
||||
} else if (!should_complete) {
|
||||
|
||||
if (s->synced && !should_complete) {
|
||||
delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0);
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
}
|
||||
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common) &&
|
||||
(!s->synced || s->common.force))
|
||||
{
|
||||
break;
|
||||
}
|
||||
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
}
|
||||
|
|
|
@ -259,14 +259,18 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
|||
|
||||
if (extent->length == 0 ||
|
||||
(client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
|
||||
client->info.min_block)) ||
|
||||
extent->length > orig_length)
|
||||
{
|
||||
client->info.min_block))) {
|
||||
error_setg(errp, "Protocol error: server sent status chunk with "
|
||||
"invalid length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The server is allowed to send us extra information on the final
|
||||
* extent; just clamp it to the length we requested. */
|
||||
if (extent->length > orig_length) {
|
||||
extent->length = orig_length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -557,6 +557,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
|
|||
BlockdevOptionsNfs *opts = NULL;
|
||||
QObject *crumpled = NULL;
|
||||
Visitor *v;
|
||||
const QDictEntry *e;
|
||||
Error *local_err = NULL;
|
||||
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
|
@ -573,6 +574,12 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove the processed options from the QDict (the visitor processes
|
||||
* _all_ options in the QDict) */
|
||||
while ((e = qdict_first(options))) {
|
||||
qdict_del(options, e->key);
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
|
|
|
@ -934,6 +934,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
|||
ret = 0;
|
||||
exit:
|
||||
blk_unref(qcow_blk);
|
||||
bdrv_unref(bs);
|
||||
qcrypto_block_free(crypto);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -4386,7 +4386,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
|||
char *message;
|
||||
va_list ap;
|
||||
|
||||
fatal = fatal && !bs->read_only;
|
||||
fatal = fatal && bdrv_is_writable(bs);
|
||||
|
||||
if (s->signaled_corruption &&
|
||||
(!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT)))
|
||||
|
|
|
@ -167,16 +167,37 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
|||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
/* Check and adjust the offset, against 'offset' and 'size' options. */
|
||||
static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset,
|
||||
uint64_t bytes, bool is_write)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) {
|
||||
/* There's not enough space for the write, or the read request is
|
||||
* out-of-range. Don't read/write anything to prevent leaking out of
|
||||
* the size specified in options. */
|
||||
return is_write ? -ENOSPC : -EINVAL;;
|
||||
}
|
||||
|
||||
if (*offset > INT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
*offset += s->offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
ret = raw_adjust_offset(bs, &offset, bytes, false);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
offset += s->offset;
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||
|
@ -186,23 +207,11 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||
uint64_t bytes, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
void *buf = NULL;
|
||||
BlockDriver *drv;
|
||||
QEMUIOVector local_qiov;
|
||||
int ret;
|
||||
|
||||
if (s->has_size && (offset > s->size || bytes > (s->size - offset))) {
|
||||
/* There's not enough space for the data. Don't write anything and just
|
||||
* fail to prevent leaking out of the size specified in options. */
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) {
|
||||
/* Handling partial writes would be a pain - so we just
|
||||
* require that guests have 512-byte request alignment if
|
||||
|
@ -237,7 +246,10 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||
qiov = &local_qiov;
|
||||
}
|
||||
|
||||
offset += s->offset;
|
||||
ret = raw_adjust_offset(bs, &offset, bytes, true);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||
|
@ -267,22 +279,24 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs,
|
|||
int64_t offset, int bytes,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
int ret;
|
||||
|
||||
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
offset += s->offset;
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
int ret;
|
||||
|
||||
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
offset += s->offset;
|
||||
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,9 +36,12 @@ static QemuOptsList throttle_opts = {
|
|||
},
|
||||
};
|
||||
|
||||
static int throttle_configure_tgm(BlockDriverState *bs,
|
||||
ThrottleGroupMember *tgm,
|
||||
QDict *options, Error **errp)
|
||||
/*
|
||||
* If this function succeeds then the throttle group name is stored in
|
||||
* @group and must be freed by the caller.
|
||||
* If there's an error then @group remains unmodified.
|
||||
*/
|
||||
static int throttle_parse_options(QDict *options, char **group, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
const char *group_name;
|
||||
|
@ -63,8 +66,7 @@ static int throttle_configure_tgm(BlockDriverState *bs,
|
|||
goto fin;
|
||||
}
|
||||
|
||||
/* Register membership to group with name group_name */
|
||||
throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
|
||||
*group = g_strdup(group_name);
|
||||
ret = 0;
|
||||
fin:
|
||||
qemu_opts_del(opts);
|
||||
|
@ -75,6 +77,8 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
|
|||
int flags, Error **errp)
|
||||
{
|
||||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
char *group;
|
||||
int ret;
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs,
|
||||
&child_file, false, errp);
|
||||
|
@ -84,7 +88,14 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
|
|||
bs->supported_write_flags = bs->file->bs->supported_write_flags;
|
||||
bs->supported_zero_flags = bs->file->bs->supported_zero_flags;
|
||||
|
||||
return throttle_configure_tgm(bs, tgm, options, errp);
|
||||
ret = throttle_parse_options(options, &group, errp);
|
||||
if (ret == 0) {
|
||||
/* Register membership to group with name group_name */
|
||||
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
|
||||
g_free(group);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void throttle_close(BlockDriverState *bs)
|
||||
|
@ -160,35 +171,36 @@ static void throttle_attach_aio_context(BlockDriverState *bs,
|
|||
static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
ThrottleGroupMember *tgm;
|
||||
int ret;
|
||||
char *group = NULL;
|
||||
|
||||
assert(reopen_state != NULL);
|
||||
assert(reopen_state->bs != NULL);
|
||||
|
||||
reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
|
||||
tgm = reopen_state->opaque;
|
||||
|
||||
return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
|
||||
errp);
|
||||
ret = throttle_parse_options(reopen_state->options, &group, errp);
|
||||
reopen_state->opaque = group;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void throttle_reopen_commit(BDRVReopenState *reopen_state)
|
||||
{
|
||||
ThrottleGroupMember *old_tgm = reopen_state->bs->opaque;
|
||||
ThrottleGroupMember *new_tgm = reopen_state->opaque;
|
||||
BlockDriverState *bs = reopen_state->bs;
|
||||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
char *group = reopen_state->opaque;
|
||||
|
||||
throttle_group_unregister_tgm(old_tgm);
|
||||
g_free(old_tgm);
|
||||
reopen_state->bs->opaque = new_tgm;
|
||||
assert(group);
|
||||
|
||||
if (strcmp(group, throttle_group_get_name(tgm))) {
|
||||
throttle_group_unregister_tgm(tgm);
|
||||
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
|
||||
}
|
||||
g_free(reopen_state->opaque);
|
||||
reopen_state->opaque = NULL;
|
||||
}
|
||||
|
||||
static void throttle_reopen_abort(BDRVReopenState *reopen_state)
|
||||
{
|
||||
ThrottleGroupMember *tgm = reopen_state->opaque;
|
||||
|
||||
throttle_group_unregister_tgm(tgm);
|
||||
g_free(tgm);
|
||||
g_free(reopen_state->opaque);
|
||||
reopen_state->opaque = NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -831,6 +831,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
|
|||
info->status = job->status;
|
||||
info->auto_finalize = job->auto_finalize;
|
||||
info->auto_dismiss = job->auto_dismiss;
|
||||
info->has_error = job->ret != 0;
|
||||
info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL;
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
|
@ -304,6 +304,7 @@ void mux_set_focus(Chardev *chr, int focus)
|
|||
}
|
||||
|
||||
d->focus = focus;
|
||||
chr->be = d->backends[focus];
|
||||
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
|
||||
}
|
||||
|
||||
|
|
|
@ -959,6 +959,8 @@ for opt do
|
|||
;;
|
||||
--firmwarepath=*) firmwarepath="$optarg"
|
||||
;;
|
||||
--host=*|--build=*|\
|
||||
--disable-dependency-tracking|\
|
||||
--sbindir=*|--sharedstatedir=*|\
|
||||
--oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\
|
||||
--htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*)
|
||||
|
@ -3732,7 +3734,7 @@ fi
|
|||
fdt_required=no
|
||||
for target in $target_list; do
|
||||
case $target in
|
||||
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
|
||||
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu)
|
||||
fdt_required=yes
|
||||
;;
|
||||
esac
|
||||
|
|
18
cpus.c
18
cpus.c
|
@ -1648,7 +1648,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
|||
/* process any pending work */
|
||||
cpu->exit_request = 1;
|
||||
|
||||
while (1) {
|
||||
do {
|
||||
if (cpu_can_run(cpu)) {
|
||||
int r;
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
@ -2218,11 +2218,25 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp)
|
|||
info->value->props = props;
|
||||
}
|
||||
|
||||
#if defined(TARGET_S390X)
|
||||
#if defined(TARGET_I386)
|
||||
info->value->arch = CPU_INFO_ARCH_X86;
|
||||
#elif defined(TARGET_PPC)
|
||||
info->value->arch = CPU_INFO_ARCH_PPC;
|
||||
#elif defined(TARGET_SPARC)
|
||||
info->value->arch = CPU_INFO_ARCH_SPARC;
|
||||
#elif defined(TARGET_MIPS)
|
||||
info->value->arch = CPU_INFO_ARCH_MIPS;
|
||||
#elif defined(TARGET_TRICORE)
|
||||
info->value->arch = CPU_INFO_ARCH_TRICORE;
|
||||
#elif defined(TARGET_S390X)
|
||||
s390_cpu = S390_CPU(cpu);
|
||||
env = &s390_cpu->env;
|
||||
info->value->arch = CPU_INFO_ARCH_S390;
|
||||
info->value->u.s390.cpu_state = env->cpu_state;
|
||||
#elif defined(TARGET_RISCV)
|
||||
info->value->arch = CPU_INFO_ARCH_RISCV;
|
||||
#else
|
||||
info->value->arch = CPU_INFO_ARCH_OTHER;
|
||||
#endif
|
||||
if (!cur_item) {
|
||||
head = cur_item = info;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#include <libfdt.h>
|
||||
|
||||
#define FDT_MAX_SIZE 0x10000
|
||||
#define FDT_MAX_SIZE 0x100000
|
||||
|
||||
void *create_device_tree(int *sizep)
|
||||
{
|
||||
|
|
|
@ -602,34 +602,42 @@ static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s)
|
|||
static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c,
|
||||
bool inf_zero, float_status *s)
|
||||
{
|
||||
int which;
|
||||
|
||||
if (is_snan(a.cls) || is_snan(b.cls) || is_snan(c.cls)) {
|
||||
s->float_exception_flags |= float_flag_invalid;
|
||||
}
|
||||
|
||||
if (s->default_nan_mode) {
|
||||
a.cls = float_class_dnan;
|
||||
} else {
|
||||
switch (pickNaNMulAdd(is_qnan(a.cls), is_snan(a.cls),
|
||||
is_qnan(b.cls), is_snan(b.cls),
|
||||
is_qnan(c.cls), is_snan(c.cls),
|
||||
inf_zero, s)) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
a = b;
|
||||
break;
|
||||
case 2:
|
||||
a = c;
|
||||
break;
|
||||
case 3:
|
||||
a.cls = float_class_dnan;
|
||||
return a;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
which = pickNaNMulAdd(is_qnan(a.cls), is_snan(a.cls),
|
||||
is_qnan(b.cls), is_snan(b.cls),
|
||||
is_qnan(c.cls), is_snan(c.cls),
|
||||
inf_zero, s);
|
||||
|
||||
a.cls = float_class_msnan;
|
||||
if (s->default_nan_mode) {
|
||||
/* Note that this check is after pickNaNMulAdd so that function
|
||||
* has an opportunity to set the Invalid flag.
|
||||
*/
|
||||
a.cls = float_class_dnan;
|
||||
return a;
|
||||
}
|
||||
|
||||
switch (which) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
a = b;
|
||||
break;
|
||||
case 2:
|
||||
a = c;
|
||||
break;
|
||||
case 3:
|
||||
a.cls = float_class_dnan;
|
||||
return a;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
a.cls = float_class_msnan;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
@ -1360,14 +1368,14 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode,
|
|||
r = UINT64_MAX;
|
||||
}
|
||||
if (p.sign) {
|
||||
if (r < -(uint64_t) min) {
|
||||
if (r <= -(uint64_t) min) {
|
||||
return -r;
|
||||
} else {
|
||||
s->float_exception_flags = orig_flags | float_flag_invalid;
|
||||
return min;
|
||||
}
|
||||
} else {
|
||||
if (r < max) {
|
||||
if (r <= max) {
|
||||
return r;
|
||||
} else {
|
||||
s->float_exception_flags = orig_flags | float_flag_invalid;
|
||||
|
@ -3139,7 +3147,7 @@ float128 uint64_to_float128(uint64_t a, float_status *status)
|
|||
if (a == 0) {
|
||||
return float128_zero;
|
||||
}
|
||||
return normalizeRoundAndPackFloat128(0, 0x406E, a, 0, status);
|
||||
return normalizeRoundAndPackFloat128(0, 0x406E, 0, a, status);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -169,7 +169,8 @@ void qxl_render_update(PCIQXLDevice *qxl)
|
|||
|
||||
qemu_mutex_lock(&qxl->ssd.lock);
|
||||
|
||||
if (!runstate_is_running() || !qxl->guest_primary.commands) {
|
||||
if (!runstate_is_running() || !qxl->guest_primary.commands ||
|
||||
qxl->mode == QXL_MODE_UNDEFINED) {
|
||||
qxl_render_update_area_unlocked(qxl);
|
||||
qemu_mutex_unlock(&qxl->ssd.lock);
|
||||
return;
|
||||
|
|
|
@ -128,6 +128,22 @@ static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr,
|
|||
return new_val;
|
||||
}
|
||||
|
||||
static inline void vtd_iommu_lock(IntelIOMMUState *s)
|
||||
{
|
||||
qemu_mutex_lock(&s->iommu_lock);
|
||||
}
|
||||
|
||||
static inline void vtd_iommu_unlock(IntelIOMMUState *s)
|
||||
{
|
||||
qemu_mutex_unlock(&s->iommu_lock);
|
||||
}
|
||||
|
||||
/* Whether the address space needs to notify new mappings */
|
||||
static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as)
|
||||
{
|
||||
return as->notifier_flags & IOMMU_NOTIFIER_MAP;
|
||||
}
|
||||
|
||||
/* GHashTable functions */
|
||||
static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2)
|
||||
{
|
||||
|
@ -172,9 +188,9 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value,
|
|||
}
|
||||
|
||||
/* Reset all the gen of VTDAddressSpace to zero and set the gen of
|
||||
* IntelIOMMUState to 1.
|
||||
* IntelIOMMUState to 1. Must be called with IOMMU lock held.
|
||||
*/
|
||||
static void vtd_reset_context_cache(IntelIOMMUState *s)
|
||||
static void vtd_reset_context_cache_locked(IntelIOMMUState *s)
|
||||
{
|
||||
VTDAddressSpace *vtd_as;
|
||||
VTDBus *vtd_bus;
|
||||
|
@ -197,12 +213,20 @@ static void vtd_reset_context_cache(IntelIOMMUState *s)
|
|||
s->context_cache_gen = 1;
|
||||
}
|
||||
|
||||
static void vtd_reset_iotlb(IntelIOMMUState *s)
|
||||
/* Must be called with IOMMU lock held. */
|
||||
static void vtd_reset_iotlb_locked(IntelIOMMUState *s)
|
||||
{
|
||||
assert(s->iotlb);
|
||||
g_hash_table_remove_all(s->iotlb);
|
||||
}
|
||||
|
||||
static void vtd_reset_iotlb(IntelIOMMUState *s)
|
||||
{
|
||||
vtd_iommu_lock(s);
|
||||
vtd_reset_iotlb_locked(s);
|
||||
vtd_iommu_unlock(s);
|
||||
}
|
||||
|
||||
static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id,
|
||||
uint32_t level)
|
||||
{
|
||||
|
@ -215,6 +239,7 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level)
|
|||
return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K;
|
||||
}
|
||||
|
||||
/* Must be called with IOMMU lock held */
|
||||
static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id,
|
||||
hwaddr addr)
|
||||
{
|
||||
|
@ -235,6 +260,7 @@ out:
|
|||
return entry;
|
||||
}
|
||||
|
||||
/* Must be with IOMMU lock held */
|
||||
static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
|
||||
uint16_t domain_id, hwaddr addr, uint64_t slpte,
|
||||
uint8_t access_flags, uint32_t level)
|
||||
|
@ -246,7 +272,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
|
|||
trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id);
|
||||
if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) {
|
||||
trace_vtd_iotlb_reset("iotlb exceeds size limit");
|
||||
vtd_reset_iotlb(s);
|
||||
vtd_reset_iotlb_locked(s);
|
||||
}
|
||||
|
||||
entry->gfn = gfn;
|
||||
|
@ -722,23 +748,117 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write,
|
|||
|
||||
typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private);
|
||||
|
||||
/**
|
||||
* Constant information used during page walking
|
||||
*
|
||||
* @hook_fn: hook func to be called when detected page
|
||||
* @private: private data to be passed into hook func
|
||||
* @notify_unmap: whether we should notify invalid entries
|
||||
* @as: VT-d address space of the device
|
||||
* @aw: maximum address width
|
||||
* @domain: domain ID of the page walk
|
||||
*/
|
||||
typedef struct {
|
||||
VTDAddressSpace *as;
|
||||
vtd_page_walk_hook hook_fn;
|
||||
void *private;
|
||||
bool notify_unmap;
|
||||
uint8_t aw;
|
||||
uint16_t domain_id;
|
||||
} vtd_page_walk_info;
|
||||
|
||||
static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info)
|
||||
{
|
||||
VTDAddressSpace *as = info->as;
|
||||
vtd_page_walk_hook hook_fn = info->hook_fn;
|
||||
void *private = info->private;
|
||||
DMAMap target = {
|
||||
.iova = entry->iova,
|
||||
.size = entry->addr_mask,
|
||||
.translated_addr = entry->translated_addr,
|
||||
.perm = entry->perm,
|
||||
};
|
||||
DMAMap *mapped = iova_tree_find(as->iova_tree, &target);
|
||||
|
||||
if (entry->perm == IOMMU_NONE && !info->notify_unmap) {
|
||||
trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(hook_fn);
|
||||
|
||||
/* Update local IOVA mapped ranges */
|
||||
if (entry->perm) {
|
||||
if (mapped) {
|
||||
/* If it's exactly the same translation, skip */
|
||||
if (!memcmp(mapped, &target, sizeof(target))) {
|
||||
trace_vtd_page_walk_one_skip_map(entry->iova, entry->addr_mask,
|
||||
entry->translated_addr);
|
||||
return 0;
|
||||
} else {
|
||||
/*
|
||||
* Translation changed. Normally this should not
|
||||
* happen, but it can happen when with buggy guest
|
||||
* OSes. Note that there will be a small window that
|
||||
* we don't have map at all. But that's the best
|
||||
* effort we can do. The ideal way to emulate this is
|
||||
* atomically modify the PTE to follow what has
|
||||
* changed, but we can't. One example is that vfio
|
||||
* driver only has VFIO_IOMMU_[UN]MAP_DMA but no
|
||||
* interface to modify a mapping (meanwhile it seems
|
||||
* meaningless to even provide one). Anyway, let's
|
||||
* mark this as a TODO in case one day we'll have
|
||||
* a better solution.
|
||||
*/
|
||||
IOMMUAccessFlags cache_perm = entry->perm;
|
||||
int ret;
|
||||
|
||||
/* Emulate an UNMAP */
|
||||
entry->perm = IOMMU_NONE;
|
||||
trace_vtd_page_walk_one(info->domain_id,
|
||||
entry->iova,
|
||||
entry->translated_addr,
|
||||
entry->addr_mask,
|
||||
entry->perm);
|
||||
ret = hook_fn(entry, private);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
/* Drop any existing mapping */
|
||||
iova_tree_remove(as->iova_tree, &target);
|
||||
/* Recover the correct permission */
|
||||
entry->perm = cache_perm;
|
||||
}
|
||||
}
|
||||
iova_tree_insert(as->iova_tree, &target);
|
||||
} else {
|
||||
if (!mapped) {
|
||||
/* Skip since we didn't map this range at all */
|
||||
trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask);
|
||||
return 0;
|
||||
}
|
||||
iova_tree_remove(as->iova_tree, &target);
|
||||
}
|
||||
|
||||
trace_vtd_page_walk_one(info->domain_id, entry->iova,
|
||||
entry->translated_addr, entry->addr_mask,
|
||||
entry->perm);
|
||||
return hook_fn(entry, private);
|
||||
}
|
||||
|
||||
/**
|
||||
* vtd_page_walk_level - walk over specific level for IOVA range
|
||||
*
|
||||
* @addr: base GPA addr to start the walk
|
||||
* @start: IOVA range start address
|
||||
* @end: IOVA range end address (start <= addr < end)
|
||||
* @hook_fn: hook func to be called when detected page
|
||||
* @private: private data to be passed into hook func
|
||||
* @read: whether parent level has read permission
|
||||
* @write: whether parent level has write permission
|
||||
* @notify_unmap: whether we should notify invalid entries
|
||||
* @aw: maximum address width
|
||||
* @info: constant information for the page walk
|
||||
*/
|
||||
static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,
|
||||
uint64_t end, vtd_page_walk_hook hook_fn,
|
||||
void *private, uint32_t level, bool read,
|
||||
bool write, bool notify_unmap, uint8_t aw)
|
||||
uint64_t end, uint32_t level, bool read,
|
||||
bool write, vtd_page_walk_info *info)
|
||||
{
|
||||
bool read_cur, write_cur, entry_valid;
|
||||
uint32_t offset;
|
||||
|
@ -781,37 +901,34 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,
|
|||
*/
|
||||
entry_valid = read_cur | write_cur;
|
||||
|
||||
if (vtd_is_last_slpte(slpte, level)) {
|
||||
if (!vtd_is_last_slpte(slpte, level) && entry_valid) {
|
||||
/*
|
||||
* This is a valid PDE (or even bigger than PDE). We need
|
||||
* to walk one further level.
|
||||
*/
|
||||
ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw),
|
||||
iova, MIN(iova_next, end), level - 1,
|
||||
read_cur, write_cur, info);
|
||||
} else {
|
||||
/*
|
||||
* This means we are either:
|
||||
*
|
||||
* (1) the real page entry (either 4K page, or huge page)
|
||||
* (2) the whole range is invalid
|
||||
*
|
||||
* In either case, we send an IOTLB notification down.
|
||||
*/
|
||||
entry.target_as = &address_space_memory;
|
||||
entry.iova = iova & subpage_mask;
|
||||
/* NOTE: this is only meaningful if entry_valid == true */
|
||||
entry.translated_addr = vtd_get_slpte_addr(slpte, aw);
|
||||
entry.addr_mask = ~subpage_mask;
|
||||
entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur);
|
||||
if (!entry_valid && !notify_unmap) {
|
||||
trace_vtd_page_walk_skip_perm(iova, iova_next);
|
||||
goto next;
|
||||
}
|
||||
trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr,
|
||||
entry.addr_mask, entry.perm);
|
||||
if (hook_fn) {
|
||||
ret = hook_fn(&entry, private);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!entry_valid) {
|
||||
trace_vtd_page_walk_skip_perm(iova, iova_next);
|
||||
goto next;
|
||||
}
|
||||
ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova,
|
||||
MIN(iova_next, end), hook_fn, private,
|
||||
level - 1, read_cur, write_cur,
|
||||
notify_unmap, aw);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
entry.addr_mask = ~subpage_mask;
|
||||
/* NOTE: this is only meaningful if entry_valid == true */
|
||||
entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw);
|
||||
ret = vtd_page_walk_one(&entry, info);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
next:
|
||||
|
@ -827,28 +944,24 @@ next:
|
|||
* @ce: context entry to walk upon
|
||||
* @start: IOVA address to start the walk
|
||||
* @end: IOVA range end address (start <= addr < end)
|
||||
* @hook_fn: the hook that to be called for each detected area
|
||||
* @private: private data for the hook function
|
||||
* @aw: maximum address width
|
||||
* @info: page walking information struct
|
||||
*/
|
||||
static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end,
|
||||
vtd_page_walk_hook hook_fn, void *private,
|
||||
bool notify_unmap, uint8_t aw)
|
||||
vtd_page_walk_info *info)
|
||||
{
|
||||
dma_addr_t addr = vtd_ce_get_slpt_base(ce);
|
||||
uint32_t level = vtd_ce_get_level(ce);
|
||||
|
||||
if (!vtd_iova_range_check(start, ce, aw)) {
|
||||
if (!vtd_iova_range_check(start, ce, info->aw)) {
|
||||
return -VTD_FR_ADDR_BEYOND_MGAW;
|
||||
}
|
||||
|
||||
if (!vtd_iova_range_check(end, ce, aw)) {
|
||||
if (!vtd_iova_range_check(end, ce, info->aw)) {
|
||||
/* Fix end so that it reaches the maximum */
|
||||
end = vtd_iova_limit(ce, aw);
|
||||
end = vtd_iova_limit(ce, info->aw);
|
||||
}
|
||||
|
||||
return vtd_page_walk_level(addr, start, end, hook_fn, private,
|
||||
level, true, true, notify_unmap, aw);
|
||||
return vtd_page_walk_level(addr, start, end, level, true, true, info);
|
||||
}
|
||||
|
||||
/* Map a device to its corresponding domain (context-entry) */
|
||||
|
@ -907,6 +1020,58 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry,
|
||||
void *private)
|
||||
{
|
||||
memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If context entry is NULL, we'll try to fetch it on our own. */
|
||||
static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as,
|
||||
VTDContextEntry *ce,
|
||||
hwaddr addr, hwaddr size)
|
||||
{
|
||||
IntelIOMMUState *s = vtd_as->iommu_state;
|
||||
vtd_page_walk_info info = {
|
||||
.hook_fn = vtd_sync_shadow_page_hook,
|
||||
.private = (void *)&vtd_as->iommu,
|
||||
.notify_unmap = true,
|
||||
.aw = s->aw_bits,
|
||||
.as = vtd_as,
|
||||
};
|
||||
VTDContextEntry ce_cache;
|
||||
int ret;
|
||||
|
||||
if (ce) {
|
||||
/* If the caller provided context entry, use it */
|
||||
ce_cache = *ce;
|
||||
} else {
|
||||
/* If the caller didn't provide ce, try to fetch */
|
||||
ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
|
||||
vtd_as->devfn, &ce_cache);
|
||||
if (ret) {
|
||||
/*
|
||||
* This should not really happen, but in case it happens,
|
||||
* we just skip the sync for this time. After all we even
|
||||
* don't have the root table pointer!
|
||||
*/
|
||||
trace_vtd_err("Detected invalid context entry when "
|
||||
"trying to sync shadow page table");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi);
|
||||
|
||||
return vtd_page_walk(&ce_cache, addr, addr + size, &info);
|
||||
}
|
||||
|
||||
static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as)
|
||||
{
|
||||
return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch translation type for specific device. Returns <0 if error
|
||||
* happens, otherwise return the shifted type to check against
|
||||
|
@ -1088,7 +1253,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
|
|||
IntelIOMMUState *s = vtd_as->iommu_state;
|
||||
VTDContextEntry ce;
|
||||
uint8_t bus_num = pci_bus_num(bus);
|
||||
VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry;
|
||||
VTDContextCacheEntry *cc_entry;
|
||||
uint64_t slpte, page_mask;
|
||||
uint32_t level;
|
||||
uint16_t source_id = vtd_make_source_id(bus_num, devfn);
|
||||
|
@ -1105,6 +1270,10 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
|
|||
*/
|
||||
assert(!vtd_is_interrupt_addr(addr));
|
||||
|
||||
vtd_iommu_lock(s);
|
||||
|
||||
cc_entry = &vtd_as->context_cache_entry;
|
||||
|
||||
/* Try to fetch slpte form IOTLB */
|
||||
iotlb_entry = vtd_lookup_iotlb(s, source_id, addr);
|
||||
if (iotlb_entry) {
|
||||
|
@ -1164,7 +1333,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
|
|||
* IOMMU region can be swapped back.
|
||||
*/
|
||||
vtd_pt_enable_fast_path(s, source_id);
|
||||
|
||||
vtd_iommu_unlock(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1185,6 +1354,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
|
|||
vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte,
|
||||
access_flags, level);
|
||||
out:
|
||||
vtd_iommu_unlock(s);
|
||||
entry->iova = addr & page_mask;
|
||||
entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask;
|
||||
entry->addr_mask = ~page_mask;
|
||||
|
@ -1192,6 +1362,7 @@ out:
|
|||
return true;
|
||||
|
||||
error:
|
||||
vtd_iommu_unlock(s);
|
||||
entry->iova = 0;
|
||||
entry->translated_addr = 0;
|
||||
entry->addr_mask = 0;
|
||||
|
@ -1230,20 +1401,23 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s)
|
|||
|
||||
static void vtd_iommu_replay_all(IntelIOMMUState *s)
|
||||
{
|
||||
IntelIOMMUNotifierNode *node;
|
||||
VTDAddressSpace *vtd_as;
|
||||
|
||||
QLIST_FOREACH(node, &s->notifiers_list, next) {
|
||||
memory_region_iommu_replay_all(&node->vtd_as->iommu);
|
||||
QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) {
|
||||
vtd_sync_shadow_page_table(vtd_as);
|
||||
}
|
||||
}
|
||||
|
||||
static void vtd_context_global_invalidate(IntelIOMMUState *s)
|
||||
{
|
||||
trace_vtd_inv_desc_cc_global();
|
||||
/* Protects context cache */
|
||||
vtd_iommu_lock(s);
|
||||
s->context_cache_gen++;
|
||||
if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) {
|
||||
vtd_reset_context_cache(s);
|
||||
vtd_reset_context_cache_locked(s);
|
||||
}
|
||||
vtd_iommu_unlock(s);
|
||||
vtd_switch_address_space_all(s);
|
||||
/*
|
||||
* From VT-d spec 6.5.2.1, a global context entry invalidation
|
||||
|
@ -1295,7 +1469,9 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
|
|||
if (vtd_as && ((devfn_it & mask) == (devfn & mask))) {
|
||||
trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it),
|
||||
VTD_PCI_FUNC(devfn_it));
|
||||
vtd_iommu_lock(s);
|
||||
vtd_as->context_cache_entry.context_cache_gen = 0;
|
||||
vtd_iommu_unlock(s);
|
||||
/*
|
||||
* Do switch address space when needed, in case if the
|
||||
* device passthrough bit is switched.
|
||||
|
@ -1303,14 +1479,13 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
|
|||
vtd_switch_address_space(vtd_as);
|
||||
/*
|
||||
* So a device is moving out of (or moving into) a
|
||||
* domain, a replay() suites here to notify all the
|
||||
* IOMMU_NOTIFIER_MAP registers about this change.
|
||||
* domain, resync the shadow page table.
|
||||
* This won't bring bad even if we have no such
|
||||
* notifier registered - the IOMMU notification
|
||||
* framework will skip MAP notifications if that
|
||||
* happened.
|
||||
*/
|
||||
memory_region_iommu_replay_all(&vtd_as->iommu);
|
||||
vtd_sync_shadow_page_table(vtd_as);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1354,48 +1529,60 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s)
|
|||
|
||||
static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id)
|
||||
{
|
||||
IntelIOMMUNotifierNode *node;
|
||||
VTDContextEntry ce;
|
||||
VTDAddressSpace *vtd_as;
|
||||
|
||||
trace_vtd_inv_desc_iotlb_domain(domain_id);
|
||||
|
||||
vtd_iommu_lock(s);
|
||||
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain,
|
||||
&domain_id);
|
||||
vtd_iommu_unlock(s);
|
||||
|
||||
QLIST_FOREACH(node, &s->notifiers_list, next) {
|
||||
vtd_as = node->vtd_as;
|
||||
QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) {
|
||||
if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
|
||||
vtd_as->devfn, &ce) &&
|
||||
domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) {
|
||||
memory_region_iommu_replay_all(&vtd_as->iommu);
|
||||
vtd_sync_shadow_page_table(vtd_as);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry,
|
||||
void *private)
|
||||
{
|
||||
memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s,
|
||||
uint16_t domain_id, hwaddr addr,
|
||||
uint8_t am)
|
||||
{
|
||||
IntelIOMMUNotifierNode *node;
|
||||
VTDAddressSpace *vtd_as;
|
||||
VTDContextEntry ce;
|
||||
int ret;
|
||||
hwaddr size = (1 << am) * VTD_PAGE_SIZE;
|
||||
|
||||
QLIST_FOREACH(node, &(s->notifiers_list), next) {
|
||||
VTDAddressSpace *vtd_as = node->vtd_as;
|
||||
QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) {
|
||||
ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
|
||||
vtd_as->devfn, &ce);
|
||||
if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) {
|
||||
vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE,
|
||||
vtd_page_invalidate_notify_hook,
|
||||
(void *)&vtd_as->iommu, true, s->aw_bits);
|
||||
if (vtd_as_has_map_notifier(vtd_as)) {
|
||||
/*
|
||||
* As long as we have MAP notifications registered in
|
||||
* any of our IOMMU notifiers, we need to sync the
|
||||
* shadow page table.
|
||||
*/
|
||||
vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size);
|
||||
} else {
|
||||
/*
|
||||
* For UNMAP-only notifiers, we don't need to walk the
|
||||
* page tables. We just deliver the PSI down to
|
||||
* invalidate caches.
|
||||
*/
|
||||
IOMMUTLBEntry entry = {
|
||||
.target_as = &address_space_memory,
|
||||
.iova = addr,
|
||||
.translated_addr = 0,
|
||||
.addr_mask = size - 1,
|
||||
.perm = IOMMU_NONE,
|
||||
};
|
||||
memory_region_notify_iommu(&vtd_as->iommu, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1411,7 +1598,9 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
|
|||
info.domain_id = domain_id;
|
||||
info.addr = addr;
|
||||
info.mask = ~((1 << am) - 1);
|
||||
vtd_iommu_lock(s);
|
||||
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
|
||||
vtd_iommu_unlock(s);
|
||||
vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am);
|
||||
}
|
||||
|
||||
|
@ -2326,8 +2515,6 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu,
|
|||
{
|
||||
VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
|
||||
IntelIOMMUState *s = vtd_as->iommu_state;
|
||||
IntelIOMMUNotifierNode *node = NULL;
|
||||
IntelIOMMUNotifierNode *next_node = NULL;
|
||||
|
||||
if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) {
|
||||
error_report("We need to set caching-mode=1 for intel-iommu to enable "
|
||||
|
@ -2335,22 +2522,13 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu,
|
|||
exit(1);
|
||||
}
|
||||
|
||||
if (old == IOMMU_NOTIFIER_NONE) {
|
||||
node = g_malloc0(sizeof(*node));
|
||||
node->vtd_as = vtd_as;
|
||||
QLIST_INSERT_HEAD(&s->notifiers_list, node, next);
|
||||
return;
|
||||
}
|
||||
/* Update per-address-space notifier flags */
|
||||
vtd_as->notifier_flags = new;
|
||||
|
||||
/* update notifier node with new flags */
|
||||
QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) {
|
||||
if (node->vtd_as == vtd_as) {
|
||||
if (new == IOMMU_NOTIFIER_NONE) {
|
||||
QLIST_REMOVE(node, next);
|
||||
g_free(node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (old == IOMMU_NOTIFIER_NONE) {
|
||||
QLIST_INSERT_HEAD(&s->vtd_as_with_notifiers, vtd_as, next);
|
||||
} else if (new == IOMMU_NOTIFIER_NONE) {
|
||||
QLIST_REMOVE(vtd_as, next);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2719,6 +2897,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
|
|||
vtd_dev_as->devfn = (uint8_t)devfn;
|
||||
vtd_dev_as->iommu_state = s;
|
||||
vtd_dev_as->context_cache_entry.context_cache_gen = 0;
|
||||
vtd_dev_as->iova_tree = iova_tree_new();
|
||||
|
||||
/*
|
||||
* Memory region relationships looks like (Address range shows
|
||||
|
@ -2771,6 +2950,7 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
|
|||
hwaddr start = n->start;
|
||||
hwaddr end = n->end;
|
||||
IntelIOMMUState *s = as->iommu_state;
|
||||
DMAMap map;
|
||||
|
||||
/*
|
||||
* Note: all the codes in this function has a assumption that IOVA
|
||||
|
@ -2815,17 +2995,19 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
|
|||
VTD_PCI_FUNC(as->devfn),
|
||||
entry.iova, size);
|
||||
|
||||
map.iova = entry.iova;
|
||||
map.size = entry.addr_mask;
|
||||
iova_tree_remove(as->iova_tree, &map);
|
||||
|
||||
memory_region_notify_one(n, &entry);
|
||||
}
|
||||
|
||||
static void vtd_address_space_unmap_all(IntelIOMMUState *s)
|
||||
{
|
||||
IntelIOMMUNotifierNode *node;
|
||||
VTDAddressSpace *vtd_as;
|
||||
IOMMUNotifier *n;
|
||||
|
||||
QLIST_FOREACH(node, &s->notifiers_list, next) {
|
||||
vtd_as = node->vtd_as;
|
||||
QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) {
|
||||
IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) {
|
||||
vtd_address_space_unmap(vtd_as, n);
|
||||
}
|
||||
|
@ -2857,8 +3039,19 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n)
|
|||
PCI_FUNC(vtd_as->devfn),
|
||||
VTD_CONTEXT_ENTRY_DID(ce.hi),
|
||||
ce.hi, ce.lo);
|
||||
vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false,
|
||||
s->aw_bits);
|
||||
if (vtd_as_has_map_notifier(vtd_as)) {
|
||||
/* This is required only for MAP typed notifiers */
|
||||
vtd_page_walk_info info = {
|
||||
.hook_fn = vtd_replay_hook,
|
||||
.private = (void *)n,
|
||||
.notify_unmap = false,
|
||||
.aw = s->aw_bits,
|
||||
.as = vtd_as,
|
||||
.domain_id = VTD_CONTEXT_ENTRY_DID(ce.hi),
|
||||
};
|
||||
|
||||
vtd_page_walk(&ce, 0, ~0ULL, &info);
|
||||
}
|
||||
} else {
|
||||
trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn),
|
||||
PCI_FUNC(vtd_as->devfn));
|
||||
|
@ -2930,8 +3123,10 @@ static void vtd_init(IntelIOMMUState *s)
|
|||
s->cap |= VTD_CAP_CM;
|
||||
}
|
||||
|
||||
vtd_reset_context_cache(s);
|
||||
vtd_reset_iotlb(s);
|
||||
vtd_iommu_lock(s);
|
||||
vtd_reset_context_cache_locked(s);
|
||||
vtd_reset_iotlb_locked(s);
|
||||
vtd_iommu_unlock(s);
|
||||
|
||||
/* Define registers with default values and bit semantics */
|
||||
vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0);
|
||||
|
@ -3070,7 +3265,8 @@ static void vtd_realize(DeviceState *dev, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
QLIST_INIT(&s->notifiers_list);
|
||||
QLIST_INIT(&s->vtd_as_with_notifiers);
|
||||
qemu_mutex_init(&s->iommu_lock);
|
||||
memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
|
||||
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
|
||||
"intel_iommu", DMAR_REG_SIZE);
|
||||
|
|
|
@ -39,9 +39,10 @@ vtd_fault_disabled(void) "Fault processing disabled for context entry"
|
|||
vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64
|
||||
vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8
|
||||
vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64
|
||||
vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d"
|
||||
vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d"
|
||||
vtd_page_walk_one_skip_map(uint64_t iova, uint64_t mask, uint64_t translated) "iova 0x%"PRIx64" mask 0x%"PRIx64" translated 0x%"PRIx64
|
||||
vtd_page_walk_one_skip_unmap(uint64_t iova, uint64_t mask) "iova 0x%"PRIx64" mask 0x%"PRIx64
|
||||
vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read"
|
||||
vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty"
|
||||
vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set"
|
||||
vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)"
|
||||
vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64
|
||||
|
|
|
@ -532,13 +532,6 @@ static void ahci_check_cmd_bh(void *opaque)
|
|||
qemu_bh_delete(ad->check_bh);
|
||||
ad->check_bh = NULL;
|
||||
|
||||
if ((ad->busy_slot != -1) &&
|
||||
!(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) {
|
||||
/* no longer busy */
|
||||
ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
|
||||
ad->busy_slot = -1;
|
||||
}
|
||||
|
||||
check_cmd(ad->hba, ad->port_no);
|
||||
}
|
||||
|
||||
|
@ -1425,6 +1418,12 @@ static void ahci_cmd_done(IDEDMA *dma)
|
|||
|
||||
trace_ahci_cmd_done(ad->hba, ad->port_no);
|
||||
|
||||
/* no longer busy */
|
||||
if (ad->busy_slot != -1) {
|
||||
ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
|
||||
ad->busy_slot = -1;
|
||||
}
|
||||
|
||||
/* update d2h status */
|
||||
ahci_write_fis_d2h(ad);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "hw/intc/arm_gicv3_common.h"
|
||||
#include "gicv3_internal.h"
|
||||
#include "hw/arm/linux-boot-if.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
static int gicv3_pre_save(void *opaque)
|
||||
{
|
||||
|
@ -141,6 +142,79 @@ static const VMStateDescription vmstate_gicv3_cpu = {
|
|||
}
|
||||
};
|
||||
|
||||
static int gicv3_gicd_no_migration_shift_bug_pre_load(void *opaque)
|
||||
{
|
||||
GICv3State *cs = opaque;
|
||||
|
||||
/*
|
||||
* The gicd_no_migration_shift_bug flag is used for migration compatibility
|
||||
* for old version QEMU which may have the GICD bmp shift bug under KVM mode.
|
||||
* Strictly, what we want to know is whether the migration source is using
|
||||
* KVM. Since we don't have any way to determine that, we look at whether the
|
||||
* destination is using KVM; this is close enough because for the older QEMU
|
||||
* versions with this bug KVM -> TCG migration didn't work anyway. If the
|
||||
* source is a newer QEMU without this bug it will transmit the migration
|
||||
* subsection which sets the flag to true; otherwise it will remain set to
|
||||
* the value we select here.
|
||||
*/
|
||||
if (kvm_enabled()) {
|
||||
cs->gicd_no_migration_shift_bug = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gicv3_gicd_no_migration_shift_bug_post_load(void *opaque,
|
||||
int version_id)
|
||||
{
|
||||
GICv3State *cs = opaque;
|
||||
|
||||
if (cs->gicd_no_migration_shift_bug) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Older versions of QEMU had a bug in the handling of state save/restore
|
||||
* to the KVM GICv3: they got the offset in the bitmap arrays wrong,
|
||||
* so that instead of the data for external interrupts 32 and up
|
||||
* starting at bit position 32 in the bitmap, it started at bit
|
||||
* position 64. If we're receiving data from a QEMU with that bug,
|
||||
* we must move the data down into the right place.
|
||||
*/
|
||||
memmove(cs->group, (uint8_t *)cs->group + GIC_INTERNAL / 8,
|
||||
sizeof(cs->group) - GIC_INTERNAL / 8);
|
||||
memmove(cs->grpmod, (uint8_t *)cs->grpmod + GIC_INTERNAL / 8,
|
||||
sizeof(cs->grpmod) - GIC_INTERNAL / 8);
|
||||
memmove(cs->enabled, (uint8_t *)cs->enabled + GIC_INTERNAL / 8,
|
||||
sizeof(cs->enabled) - GIC_INTERNAL / 8);
|
||||
memmove(cs->pending, (uint8_t *)cs->pending + GIC_INTERNAL / 8,
|
||||
sizeof(cs->pending) - GIC_INTERNAL / 8);
|
||||
memmove(cs->active, (uint8_t *)cs->active + GIC_INTERNAL / 8,
|
||||
sizeof(cs->active) - GIC_INTERNAL / 8);
|
||||
memmove(cs->edge_trigger, (uint8_t *)cs->edge_trigger + GIC_INTERNAL / 8,
|
||||
sizeof(cs->edge_trigger) - GIC_INTERNAL / 8);
|
||||
|
||||
/*
|
||||
* While this new version QEMU doesn't have this kind of bug as we fix it,
|
||||
* so it needs to set the flag to true to indicate that and it's necessary
|
||||
* for next migration to work from this new version QEMU.
|
||||
*/
|
||||
cs->gicd_no_migration_shift_bug = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = {
|
||||
.name = "arm_gicv3/gicd_no_migration_shift_bug",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.pre_load = gicv3_gicd_no_migration_shift_bug_pre_load,
|
||||
.post_load = gicv3_gicd_no_migration_shift_bug_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(gicd_no_migration_shift_bug, GICv3State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_gicv3 = {
|
||||
.name = "arm_gicv3",
|
||||
.version_id = 1,
|
||||
|
@ -165,6 +239,10 @@ static const VMStateDescription vmstate_gicv3 = {
|
|||
VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, GICv3State, num_cpu,
|
||||
vmstate_gicv3_cpu, GICv3CPUState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription * []) {
|
||||
&vmstate_gicv3_gicd_no_migration_shift_bug,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -364,6 +442,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
|
|||
gicv3_gicd_group_set(s, i);
|
||||
}
|
||||
}
|
||||
s->gicd_no_migration_shift_bug = true;
|
||||
}
|
||||
|
||||
static void arm_gic_common_linux_init(ARMLinuxBootIf *obj,
|
||||
|
|
|
@ -431,7 +431,7 @@ static uint64_t icv_ap_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
|||
{
|
||||
GICv3CPUState *cs = icc_cs_from_env(env);
|
||||
int regno = ri->opc2 & 3;
|
||||
int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS;
|
||||
int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0;
|
||||
uint64_t value = cs->ich_apr[grp][regno];
|
||||
|
||||
trace_gicv3_icv_ap_read(ri->crm & 1, regno, gicv3_redist_affid(cs), value);
|
||||
|
@ -443,7 +443,7 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
{
|
||||
GICv3CPUState *cs = icc_cs_from_env(env);
|
||||
int regno = ri->opc2 & 3;
|
||||
int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS;
|
||||
int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0;
|
||||
|
||||
trace_gicv3_icv_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value);
|
||||
|
||||
|
@ -1465,7 +1465,7 @@ static uint64_t icc_ap_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
|||
uint64_t value;
|
||||
|
||||
int regno = ri->opc2 & 3;
|
||||
int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1;
|
||||
int grp = (ri->crm & 1) ? GICV3_G1 : GICV3_G0;
|
||||
|
||||
if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) {
|
||||
return icv_ap_read(env, ri);
|
||||
|
@ -1487,7 +1487,7 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
GICv3CPUState *cs = icc_cs_from_env(env);
|
||||
|
||||
int regno = ri->opc2 & 3;
|
||||
int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1;
|
||||
int grp = (ri->crm & 1) ? GICV3_G1 : GICV3_G0;
|
||||
|
||||
if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) {
|
||||
icv_ap_write(env, ri, value);
|
||||
|
@ -2296,7 +2296,7 @@ static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
|||
{
|
||||
GICv3CPUState *cs = icc_cs_from_env(env);
|
||||
int regno = ri->opc2 & 3;
|
||||
int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS;
|
||||
int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0;
|
||||
uint64_t value;
|
||||
|
||||
value = cs->ich_apr[grp][regno];
|
||||
|
@ -2309,7 +2309,7 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
{
|
||||
GICv3CPUState *cs = icc_cs_from_env(env);
|
||||
int regno = ri->opc2 & 3;
|
||||
int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS;
|
||||
int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0;
|
||||
|
||||
trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value);
|
||||
|
||||
|
|
|
@ -135,7 +135,14 @@ static void kvm_dist_get_priority(GICv3State *s, uint32_t offset, uint8_t *bmp)
|
|||
uint32_t reg, *field;
|
||||
int irq;
|
||||
|
||||
field = (uint32_t *)bmp;
|
||||
/* For the KVM GICv3, affinity routing is always enabled, and the first 8
|
||||
* GICD_IPRIORITYR<n> registers are always RAZ/WI. The corresponding
|
||||
* functionality is replaced by GICR_IPRIORITYR<n>. It doesn't need to
|
||||
* sync them. So it needs to skip the field of GIC_INTERNAL irqs in bmp and
|
||||
* offset.
|
||||
*/
|
||||
field = (uint32_t *)(bmp + GIC_INTERNAL);
|
||||
offset += (GIC_INTERNAL * 8) / 8;
|
||||
for_each_dist_irq_reg(irq, s->num_irq, 8) {
|
||||
kvm_gicd_access(s, offset, ®, false);
|
||||
*field = reg;
|
||||
|
@ -149,7 +156,14 @@ static void kvm_dist_put_priority(GICv3State *s, uint32_t offset, uint8_t *bmp)
|
|||
uint32_t reg, *field;
|
||||
int irq;
|
||||
|
||||
field = (uint32_t *)bmp;
|
||||
/* For the KVM GICv3, affinity routing is always enabled, and the first 8
|
||||
* GICD_IPRIORITYR<n> registers are always RAZ/WI. The corresponding
|
||||
* functionality is replaced by GICR_IPRIORITYR<n>. It doesn't need to
|
||||
* sync them. So it needs to skip the field of GIC_INTERNAL irqs in bmp and
|
||||
* offset.
|
||||
*/
|
||||
field = (uint32_t *)(bmp + GIC_INTERNAL);
|
||||
offset += (GIC_INTERNAL * 8) / 8;
|
||||
for_each_dist_irq_reg(irq, s->num_irq, 8) {
|
||||
reg = *field;
|
||||
kvm_gicd_access(s, offset, ®, true);
|
||||
|
@ -164,6 +178,14 @@ static void kvm_dist_get_edge_trigger(GICv3State *s, uint32_t offset,
|
|||
uint32_t reg;
|
||||
int irq;
|
||||
|
||||
/* For the KVM GICv3, affinity routing is always enabled, and the first 2
|
||||
* GICD_ICFGR<n> registers are always RAZ/WI. The corresponding
|
||||
* functionality is replaced by GICR_ICFGR<n>. It doesn't need to sync
|
||||
* them. So it should increase the offset to skip GIC_INTERNAL irqs.
|
||||
* This matches the for_each_dist_irq_reg() macro which also skips the
|
||||
* first GIC_INTERNAL irqs.
|
||||
*/
|
||||
offset += (GIC_INTERNAL * 2) / 8;
|
||||
for_each_dist_irq_reg(irq, s->num_irq, 2) {
|
||||
kvm_gicd_access(s, offset, ®, false);
|
||||
reg = half_unshuffle32(reg >> 1);
|
||||
|
@ -181,6 +203,14 @@ static void kvm_dist_put_edge_trigger(GICv3State *s, uint32_t offset,
|
|||
uint32_t reg;
|
||||
int irq;
|
||||
|
||||
/* For the KVM GICv3, affinity routing is always enabled, and the first 2
|
||||
* GICD_ICFGR<n> registers are always RAZ/WI. The corresponding
|
||||
* functionality is replaced by GICR_ICFGR<n>. It doesn't need to sync
|
||||
* them. So it should increase the offset to skip GIC_INTERNAL irqs.
|
||||
* This matches the for_each_dist_irq_reg() macro which also skips the
|
||||
* first GIC_INTERNAL irqs.
|
||||
*/
|
||||
offset += (GIC_INTERNAL * 2) / 8;
|
||||
for_each_dist_irq_reg(irq, s->num_irq, 2) {
|
||||
reg = *gic_bmp_ptr32(bmp, irq);
|
||||
if (irq % 32 != 0) {
|
||||
|
@ -222,6 +252,15 @@ static void kvm_dist_getbmp(GICv3State *s, uint32_t offset, uint32_t *bmp)
|
|||
uint32_t reg;
|
||||
int irq;
|
||||
|
||||
/* For the KVM GICv3, affinity routing is always enabled, and the
|
||||
* GICD_IGROUPR0/GICD_IGRPMODR0/GICD_ISENABLER0/GICD_ISPENDR0/
|
||||
* GICD_ISACTIVER0 registers are always RAZ/WI. The corresponding
|
||||
* functionality is replaced by the GICR registers. It doesn't need to sync
|
||||
* them. So it should increase the offset to skip GIC_INTERNAL irqs.
|
||||
* This matches the for_each_dist_irq_reg() macro which also skips the
|
||||
* first GIC_INTERNAL irqs.
|
||||
*/
|
||||
offset += (GIC_INTERNAL * 1) / 8;
|
||||
for_each_dist_irq_reg(irq, s->num_irq, 1) {
|
||||
kvm_gicd_access(s, offset, ®, false);
|
||||
*gic_bmp_ptr32(bmp, irq) = reg;
|
||||
|
@ -235,6 +274,19 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset,
|
|||
uint32_t reg;
|
||||
int irq;
|
||||
|
||||
/* For the KVM GICv3, affinity routing is always enabled, and the
|
||||
* GICD_IGROUPR0/GICD_IGRPMODR0/GICD_ISENABLER0/GICD_ISPENDR0/
|
||||
* GICD_ISACTIVER0 registers are always RAZ/WI. The corresponding
|
||||
* functionality is replaced by the GICR registers. It doesn't need to sync
|
||||
* them. So it should increase the offset and clroffset to skip GIC_INTERNAL
|
||||
* irqs. This matches the for_each_dist_irq_reg() macro which also skips the
|
||||
* first GIC_INTERNAL irqs.
|
||||
*/
|
||||
offset += (GIC_INTERNAL * 1) / 8;
|
||||
if (clroffset != 0) {
|
||||
clroffset += (GIC_INTERNAL * 1) / 8;
|
||||
}
|
||||
|
||||
for_each_dist_irq_reg(irq, s->num_irq, 1) {
|
||||
/* If this bitmap is a set/clear register pair, first write to the
|
||||
* clear-reg to clear all bits before using the set-reg to write
|
||||
|
@ -243,6 +295,7 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset,
|
|||
if (clroffset != 0) {
|
||||
reg = 0;
|
||||
kvm_gicd_access(s, clroffset, ®, true);
|
||||
clroffset += 4;
|
||||
}
|
||||
reg = *gic_bmp_ptr32(bmp, irq);
|
||||
kvm_gicd_access(s, offset, ®, true);
|
||||
|
|
|
@ -43,7 +43,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp)
|
|||
if (!k->parallel.is_enabled || k->parallel.is_enabled(sio, i)) {
|
||||
/* FIXME use a qdev chardev prop instead of parallel_hds[] */
|
||||
chr = parallel_hds[i];
|
||||
if (chr == NULL || chr->be) {
|
||||
if (chr == NULL) {
|
||||
name = g_strdup_printf("discarding-parallel%d", i);
|
||||
chr = qemu_chr_new(name, "null");
|
||||
} else {
|
||||
|
@ -81,9 +81,9 @@ static void isa_superio_realize(DeviceState *dev, Error **errp)
|
|||
break;
|
||||
}
|
||||
if (!k->serial.is_enabled || k->serial.is_enabled(sio, i)) {
|
||||
/* FIXME use a qdev chardev prop instead of serial_hds[] */
|
||||
/* FIXME use a qdev chardev prop instead of serial_hd() */
|
||||
chr = serial_hds[i];
|
||||
if (chr == NULL || chr->be) {
|
||||
if (chr == NULL) {
|
||||
name = g_strdup_printf("discarding-serial%d", i);
|
||||
chr = qemu_chr_new(name, "null");
|
||||
} else {
|
||||
|
|
|
@ -2392,6 +2392,7 @@ static void spapr_machine_init(MachineState *machine)
|
|||
long load_limit, fw_size;
|
||||
char *filename;
|
||||
Error *resize_hpt_err = NULL;
|
||||
PowerPCCPU *first_ppc_cpu;
|
||||
|
||||
msi_nonbroken = true;
|
||||
|
||||
|
@ -2484,11 +2485,6 @@ static void spapr_machine_init(MachineState *machine)
|
|||
}
|
||||
|
||||
spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY);
|
||||
if (!kvm_enabled() || kvmppc_has_cap_mmu_radix()) {
|
||||
/* KVM and TCG always allow GTSE with radix... */
|
||||
spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE);
|
||||
}
|
||||
/* ... but not with hash (currently). */
|
||||
|
||||
/* advertise support for dedicated HP event source to guests */
|
||||
if (spapr->use_hotplug_event_source) {
|
||||
|
@ -2503,6 +2499,15 @@ static void spapr_machine_init(MachineState *machine)
|
|||
/* init CPUs */
|
||||
spapr_init_cpus(spapr);
|
||||
|
||||
first_ppc_cpu = POWERPC_CPU(first_cpu);
|
||||
if ((!kvm_enabled() || kvmppc_has_cap_mmu_radix()) &&
|
||||
ppc_check_compat(first_ppc_cpu, CPU_POWERPC_LOGICAL_3_00, 0,
|
||||
spapr->max_compat_pvr)) {
|
||||
/* KVM and TCG always allow GTSE with radix... */
|
||||
spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE);
|
||||
}
|
||||
/* ... but not with hash (currently). */
|
||||
|
||||
if (kvm_enabled()) {
|
||||
/* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */
|
||||
kvmppc_enable_logical_ci_hcalls();
|
||||
|
|
|
@ -41,17 +41,20 @@
|
|||
} while (0)
|
||||
|
||||
static uint64_t fromhost_addr, tohost_addr;
|
||||
static int address_symbol_set;
|
||||
|
||||
void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
|
||||
uint64_t st_size)
|
||||
uint64_t st_size)
|
||||
{
|
||||
if (strcmp("fromhost", st_name) == 0) {
|
||||
address_symbol_set |= 1;
|
||||
fromhost_addr = st_value;
|
||||
if (st_size != 8) {
|
||||
error_report("HTIF fromhost must be 8 bytes");
|
||||
exit(1);
|
||||
}
|
||||
} else if (strcmp("tohost", st_name) == 0) {
|
||||
address_symbol_set |= 2;
|
||||
tohost_addr = st_value;
|
||||
if (st_size != 8) {
|
||||
error_report("HTIF tohost must be 8 bytes");
|
||||
|
@ -248,10 +251,11 @@ HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
|
|||
qemu_chr_fe_init(&s->chr, chr, &error_abort);
|
||||
qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
|
||||
htif_be_change, s, NULL, true);
|
||||
if (base) {
|
||||
if (address_symbol_set == 3) {
|
||||
memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
|
||||
TYPE_HTIF_UART, size);
|
||||
memory_region_add_subregion(address_space, base, &s->mmio);
|
||||
TYPE_HTIF_UART, size);
|
||||
memory_region_add_subregion_overlap(address_space, base,
|
||||
&s->mmio, 1);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
|
|
@ -250,9 +250,9 @@ static void riscv_sifive_u_init(MachineState *machine)
|
|||
|
||||
/* boot rom */
|
||||
memory_region_init_ram(boot_rom, NULL, "riscv.sifive.u.mrom",
|
||||
memmap[SIFIVE_U_MROM].base, &error_fatal);
|
||||
memory_region_set_readonly(boot_rom, true);
|
||||
memory_region_add_subregion(sys_memory, 0x0, boot_rom);
|
||||
memmap[SIFIVE_U_MROM].size, &error_fatal);
|
||||
memory_region_add_subregion(sys_memory, memmap[SIFIVE_U_MROM].base,
|
||||
boot_rom);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
|
@ -282,6 +282,7 @@ static void riscv_sifive_u_init(MachineState *machine)
|
|||
qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
|
||||
cpu_physical_memory_write(memmap[SIFIVE_U_MROM].base +
|
||||
sizeof(reset_vec), s->fdt, s->fdt_size);
|
||||
memory_region_set_readonly(boot_rom, true);
|
||||
|
||||
/* MMIO */
|
||||
s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base,
|
||||
|
|
|
@ -40,6 +40,13 @@ static Property ccw_device_properties[] = {
|
|||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ccw_device_reset(DeviceState *d)
|
||||
{
|
||||
CcwDevice *ccw_dev = CCW_DEVICE(d);
|
||||
|
||||
css_reset_sch(ccw_dev->sch);
|
||||
}
|
||||
|
||||
static void ccw_device_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
@ -48,6 +55,7 @@ static void ccw_device_class_init(ObjectClass *klass, void *data)
|
|||
k->realize = ccw_device_realize;
|
||||
k->refill_ids = ccw_device_refill_ids;
|
||||
dc->props = ccw_device_properties;
|
||||
dc->reset = ccw_device_reset;
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_ccw_dev = {
|
||||
|
|
|
@ -616,6 +616,14 @@ void css_inject_io_interrupt(SubchDev *sch)
|
|||
|
||||
void css_conditional_io_interrupt(SubchDev *sch)
|
||||
{
|
||||
/*
|
||||
* If the subchannel is not enabled, it is not made status pending
|
||||
* (see PoP p. 16-17, "Status Control").
|
||||
*/
|
||||
if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the subchannel is not currently status pending, make it pending
|
||||
* with alert status.
|
||||
|
|
|
@ -319,6 +319,7 @@ static void sclp_memory_init(SCLPDevice *sclp)
|
|||
initial_mem = initial_mem >> increment_size << increment_size;
|
||||
|
||||
machine->ram_size = initial_mem;
|
||||
machine->maxram_size = initial_mem;
|
||||
/* let's propagate the changed ram size into the global variable. */
|
||||
ram_size = initial_mem;
|
||||
}
|
||||
|
|
|
@ -1058,10 +1058,12 @@ static void virtio_ccw_reset(DeviceState *d)
|
|||
{
|
||||
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
CcwDevice *ccw_dev = CCW_DEVICE(d);
|
||||
VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
|
||||
|
||||
virtio_ccw_reset_virtio(dev, vdev);
|
||||
css_reset_sch(ccw_dev->sch);
|
||||
if (vdc->parent_reset) {
|
||||
vdc->parent_reset(d);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
|
||||
|
@ -1345,7 +1347,6 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_net_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_net_properties;
|
||||
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
|
||||
}
|
||||
|
@ -1373,7 +1374,6 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_blk_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_blk_properties;
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
}
|
||||
|
@ -1401,7 +1401,6 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_serial_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_serial_properties;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
}
|
||||
|
@ -1429,7 +1428,6 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_balloon_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_balloon_properties;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
}
|
||||
|
@ -1457,7 +1455,6 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_scsi_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_scsi_properties;
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
}
|
||||
|
@ -1484,7 +1481,6 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = vhost_ccw_scsi_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = vhost_ccw_scsi_properties;
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
}
|
||||
|
@ -1521,7 +1517,6 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_rng_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_rng_properties;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
}
|
||||
|
@ -1559,7 +1554,6 @@ static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_crypto_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_crypto_properties;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
}
|
||||
|
@ -1597,7 +1591,6 @@ static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_gpu_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_gpu_properties;
|
||||
dc->hotpluggable = false;
|
||||
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
|
||||
|
@ -1626,7 +1619,6 @@ static void virtio_ccw_input_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->realize = virtio_ccw_input_realize;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_input_properties;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
}
|
||||
|
@ -1725,11 +1717,13 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
|
|||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
CCWDeviceClass *k = CCW_DEVICE_CLASS(dc);
|
||||
VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->unplug = virtio_ccw_busdev_unplug;
|
||||
dc->realize = virtio_ccw_busdev_realize;
|
||||
dc->unrealize = virtio_ccw_busdev_unrealize;
|
||||
dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
|
||||
device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset);
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_ccw_device_info = {
|
||||
|
@ -1806,7 +1800,6 @@ static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
k->realize = virtio_ccw_9p_realize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_9p_properties;
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
}
|
||||
|
@ -1856,7 +1849,6 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data)
|
|||
k->unrealize = virtio_ccw_unrealize;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
dc->props = vhost_vsock_ccw_properties;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
}
|
||||
|
||||
static void vhost_vsock_ccw_instance_init(Object *obj)
|
||||
|
|
|
@ -77,6 +77,7 @@ typedef struct VirtIOCCWDeviceClass {
|
|||
CCWDeviceClass parent_class;
|
||||
void (*realize)(VirtioCcwDevice *dev, Error **errp);
|
||||
void (*unrealize)(VirtioCcwDevice *dev, Error **errp);
|
||||
void (*parent_reset)(DeviceState *dev);
|
||||
} VirtIOCCWDeviceClass;
|
||||
|
||||
/* Performance improves when virtqueue kick processing is decoupled from the
|
||||
|
|
|
@ -345,7 +345,7 @@ static void passthru_realize(CCIDCardState *base, Error **errp)
|
|||
card->vscard_in_pos = 0;
|
||||
card->vscard_in_hdr = 0;
|
||||
if (qemu_chr_fe_backend_connected(&card->cs)) {
|
||||
error_setg(errp, "ccid-card-passthru: initing chardev");
|
||||
DPRINTF(card, D_INFO, "ccid-card-passthru: initing chardev");
|
||||
qemu_chr_fe_set_handlers(&card->cs,
|
||||
ccid_card_vscard_can_read,
|
||||
ccid_card_vscard_read,
|
||||
|
|
|
@ -1017,12 +1017,16 @@ static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c,
|
|||
static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c,
|
||||
MTPObject *o)
|
||||
{
|
||||
MTPData *d = usb_mtp_data_alloc(c);
|
||||
MTPData *d;
|
||||
off_t offset;
|
||||
|
||||
if (c->argc <= 2) {
|
||||
return NULL;
|
||||
}
|
||||
trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path,
|
||||
c->argv[1], c->argv[2]);
|
||||
|
||||
d = usb_mtp_data_alloc(c);
|
||||
d->fd = open(o->path, O_RDONLY);
|
||||
if (d->fd == -1) {
|
||||
usb_mtp_data_free(d);
|
||||
|
|
|
@ -329,8 +329,8 @@ static const uint8_t qemu_ccid_descriptor[] = {
|
|||
*/
|
||||
0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */
|
||||
|
||||
0x00, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
|
||||
0x01, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
|
||||
0x01, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
|
||||
0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
|
||||
/* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */
|
||||
0xa0, 0x0f, 0x00, 0x00,
|
||||
/* u32 dwMaximumClock; */
|
||||
|
|
|
@ -247,7 +247,11 @@ static int usb_host_init(void)
|
|||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
#if LIBUSB_API_VERSION >= 0x01000106
|
||||
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, loglevel);
|
||||
#else
|
||||
libusb_set_debug(ctx, loglevel);
|
||||
#endif
|
||||
#ifdef CONFIG_WIN32
|
||||
/* FIXME: add support for Windows. */
|
||||
#else
|
||||
|
|
|
@ -795,7 +795,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
|||
usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_32bits_bulk_length));
|
||||
|
||||
if (ep & USB_DIR_IN) {
|
||||
if (ep & USB_DIR_IN || size == 0) {
|
||||
usbredirparser_send_bulk_packet(dev->parser, p->id,
|
||||
&bulk_packet, NULL, 0);
|
||||
} else {
|
||||
|
|
|
@ -3154,7 +3154,7 @@ static Property vfio_pci_dev_properties[] = {
|
|||
DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host),
|
||||
DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev),
|
||||
DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice,
|
||||
display, ON_OFF_AUTO_AUTO),
|
||||
display, ON_OFF_AUTO_OFF),
|
||||
DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice,
|
||||
intx.mmap_timeout, 1100),
|
||||
DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features,
|
||||
|
|
|
@ -156,6 +156,19 @@ static void check_rate_limit(void *opaque)
|
|||
vrng->activate_timer = true;
|
||||
}
|
||||
|
||||
static void virtio_rng_set_status(VirtIODevice *vdev, uint8_t status)
|
||||
{
|
||||
VirtIORNG *vrng = VIRTIO_RNG(vdev);
|
||||
|
||||
if (!vdev->vm_running) {
|
||||
return;
|
||||
}
|
||||
vdev->status = status;
|
||||
|
||||
/* Something changed, try to process buffers */
|
||||
virtio_rng_process(vrng);
|
||||
}
|
||||
|
||||
static void virtio_rng_device_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
|
@ -261,6 +274,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data)
|
|||
vdc->realize = virtio_rng_device_realize;
|
||||
vdc->unrealize = virtio_rng_device_unrealize;
|
||||
vdc->get_features = get_features;
|
||||
vdc->set_status = virtio_rng_set_status;
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_rng_info = {
|
||||
|
|
|
@ -400,6 +400,7 @@ bool bdrv_is_read_only(BlockDriverState *bs);
|
|||
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
|
||||
bool ignore_allow_rdw, Error **errp);
|
||||
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
|
||||
bool bdrv_is_writable(BlockDriverState *bs);
|
||||
bool bdrv_is_sg(BlockDriverState *bs);
|
||||
bool bdrv_is_inserted(BlockDriverState *bs);
|
||||
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "hw/i386/ioapic.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/iova-tree.h"
|
||||
|
||||
#define TYPE_INTEL_IOMMU_DEVICE "intel-iommu"
|
||||
#define INTEL_IOMMU_DEVICE(obj) \
|
||||
|
@ -67,7 +68,6 @@ typedef union VTD_IR_TableEntry VTD_IR_TableEntry;
|
|||
typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress;
|
||||
typedef struct VTDIrq VTDIrq;
|
||||
typedef struct VTD_MSIMessage VTD_MSIMessage;
|
||||
typedef struct IntelIOMMUNotifierNode IntelIOMMUNotifierNode;
|
||||
|
||||
/* Context-Entry */
|
||||
struct VTDContextEntry {
|
||||
|
@ -93,6 +93,10 @@ struct VTDAddressSpace {
|
|||
MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */
|
||||
IntelIOMMUState *iommu_state;
|
||||
VTDContextCacheEntry context_cache_entry;
|
||||
QLIST_ENTRY(VTDAddressSpace) next;
|
||||
/* Superset of notifier flags that this address space has */
|
||||
IOMMUNotifierFlag notifier_flags;
|
||||
IOVATree *iova_tree; /* Traces mapped IOVA ranges */
|
||||
};
|
||||
|
||||
struct VTDBus {
|
||||
|
@ -253,11 +257,6 @@ struct VTD_MSIMessage {
|
|||
/* When IR is enabled, all MSI/MSI-X data bits should be zero */
|
||||
#define VTD_IR_MSI_DATA (0)
|
||||
|
||||
struct IntelIOMMUNotifierNode {
|
||||
VTDAddressSpace *vtd_as;
|
||||
QLIST_ENTRY(IntelIOMMUNotifierNode) next;
|
||||
};
|
||||
|
||||
/* The iommu (DMAR) device state struct */
|
||||
struct IntelIOMMUState {
|
||||
X86IOMMUState x86_iommu;
|
||||
|
@ -295,7 +294,7 @@ struct IntelIOMMUState {
|
|||
GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */
|
||||
VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */
|
||||
/* list of registered notifiers */
|
||||
QLIST_HEAD(, IntelIOMMUNotifierNode) notifiers_list;
|
||||
QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers;
|
||||
|
||||
/* interrupt remapping */
|
||||
bool intr_enabled; /* Whether guest enabled IR */
|
||||
|
@ -305,6 +304,12 @@ struct IntelIOMMUState {
|
|||
OnOffAuto intr_eim; /* Toggle for EIM cabability */
|
||||
bool buggy_eim; /* Force buggy EIM unless eim=off */
|
||||
uint8_t aw_bits; /* Host/IOVA address width (in bits) */
|
||||
|
||||
/*
|
||||
* Protects IOMMU states in general. Currently it protects the
|
||||
* per-IOMMU IOTLB cache, and context entry cache in VTDAddressSpace.
|
||||
*/
|
||||
QemuMutex iommu_lock;
|
||||
};
|
||||
|
||||
/* Find the VTD Address space associated with the given bus pointer,
|
||||
|
|
|
@ -217,6 +217,7 @@ struct GICv3State {
|
|||
uint32_t revision;
|
||||
bool security_extn;
|
||||
bool irq_reset_nonsecure;
|
||||
bool gicd_no_migration_shift_bug;
|
||||
|
||||
int dev_fd; /* kvm device fd if backed by kvm vgic support */
|
||||
Error *migration_blocker;
|
||||
|
|
|
@ -47,7 +47,7 @@ void qmp_enable_command(QmpCommandList *cmds, const char *name);
|
|||
bool qmp_command_is_enabled(const QmpCommand *cmd);
|
||||
const char *qmp_command_name(const QmpCommand *cmd);
|
||||
bool qmp_has_success_response(const QmpCommand *cmd);
|
||||
QObject *qmp_build_error_object(Error *err);
|
||||
QDict *qmp_error_response(Error *err);
|
||||
QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp);
|
||||
bool qmp_is_oob(QDict *dict);
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
|
|||
QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp)
|
||||
GCC_FMT_ATTR(1, 0);
|
||||
|
||||
QDict *qdict_from_jsonf_nofail(const char *string, ...) GCC_FMT_ATTR(1, 2);
|
||||
|
||||
QString *qobject_to_json(const QObject *obj);
|
||||
QString *qobject_to_json_pretty(const QObject *obj);
|
||||
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* An very simplified iova tree implementation based on GTree.
|
||||
*
|
||||
* Copyright 2018 Red Hat, Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Peter Xu <peterx@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*/
|
||||
#ifndef IOVA_TREE_H
|
||||
#define IOVA_TREE_H
|
||||
|
||||
/*
|
||||
* Currently the iova tree will only allow to keep ranges
|
||||
* information, and no extra user data is allowed for each element. A
|
||||
* benefit is that we can merge adjacent ranges internally within the
|
||||
* tree. It can save a lot of memory when the ranges are splitted but
|
||||
* mostly continuous.
|
||||
*
|
||||
* Note that current implementation does not provide any thread
|
||||
* protections. Callers of the iova tree should be responsible
|
||||
* for the thread safety issue.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/hwaddr.h"
|
||||
|
||||
#define IOVA_OK (0)
|
||||
#define IOVA_ERR_INVALID (-1) /* Invalid parameters */
|
||||
#define IOVA_ERR_OVERLAP (-2) /* IOVA range overlapped */
|
||||
|
||||
typedef struct IOVATree IOVATree;
|
||||
typedef struct DMAMap {
|
||||
hwaddr iova;
|
||||
hwaddr translated_addr;
|
||||
hwaddr size; /* Inclusive */
|
||||
IOMMUAccessFlags perm;
|
||||
} QEMU_PACKED DMAMap;
|
||||
typedef gboolean (*iova_tree_iterator)(DMAMap *map);
|
||||
|
||||
/**
|
||||
* iova_tree_new:
|
||||
*
|
||||
* Create a new iova tree.
|
||||
*
|
||||
* Returns: the tree pointer when succeeded, or NULL if error.
|
||||
*/
|
||||
IOVATree *iova_tree_new(void);
|
||||
|
||||
/**
|
||||
* iova_tree_insert:
|
||||
*
|
||||
* @tree: the iova tree to insert
|
||||
* @map: the mapping to insert
|
||||
*
|
||||
* Insert an iova range to the tree. If there is overlapped
|
||||
* ranges, IOVA_ERR_OVERLAP will be returned.
|
||||
*
|
||||
* Return: 0 if succeeded, or <0 if error.
|
||||
*/
|
||||
int iova_tree_insert(IOVATree *tree, DMAMap *map);
|
||||
|
||||
/**
|
||||
* iova_tree_remove:
|
||||
*
|
||||
* @tree: the iova tree to remove range from
|
||||
* @map: the map range to remove
|
||||
*
|
||||
* Remove mappings from the tree that are covered by the map range
|
||||
* provided. The range does not need to be exactly what has inserted,
|
||||
* all the mappings that are included in the provided range will be
|
||||
* removed from the tree. Here map->translated_addr is meaningless.
|
||||
*
|
||||
* Return: 0 if succeeded, or <0 if error.
|
||||
*/
|
||||
int iova_tree_remove(IOVATree *tree, DMAMap *map);
|
||||
|
||||
/**
|
||||
* iova_tree_find:
|
||||
*
|
||||
* @tree: the iova tree to search from
|
||||
* @map: the mapping to search
|
||||
*
|
||||
* Search for a mapping in the iova tree that overlaps with the
|
||||
* mapping range specified. Only the first found mapping will be
|
||||
* returned.
|
||||
*
|
||||
* Return: DMAMap pointer if found, or NULL if not found. Note that
|
||||
* the returned DMAMap pointer is maintained internally. User should
|
||||
* only read the content but never modify or free the content. Also,
|
||||
* user is responsible to make sure the pointer is valid (say, no
|
||||
* concurrent deletion in progress).
|
||||
*/
|
||||
DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map);
|
||||
|
||||
/**
|
||||
* iova_tree_find_address:
|
||||
*
|
||||
* @tree: the iova tree to search from
|
||||
* @iova: the iova address to find
|
||||
*
|
||||
* Similar to iova_tree_find(), but it tries to find mapping with
|
||||
* range iova=iova & size=0.
|
||||
*
|
||||
* Return: same as iova_tree_find().
|
||||
*/
|
||||
DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova);
|
||||
|
||||
/**
|
||||
* iova_tree_foreach:
|
||||
*
|
||||
* @tree: the iova tree to iterate on
|
||||
* @iterator: the interator for the mappings, return true to stop
|
||||
*
|
||||
* Iterate over the iova tree.
|
||||
*
|
||||
* Return: 1 if found any overlap, 0 if not, <0 if error.
|
||||
*/
|
||||
void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator);
|
||||
|
||||
/**
|
||||
* iova_tree_destroy:
|
||||
*
|
||||
* @tree: the iova tree to destroy
|
||||
*
|
||||
* Destroy an existing iova tree.
|
||||
*
|
||||
* Return: None.
|
||||
*/
|
||||
void iova_tree_destroy(IOVATree *tree);
|
||||
|
||||
#endif
|
|
@ -600,6 +600,7 @@ static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s)
|
|||
ret = qemu_get_buffer(f, buf, buf_size);
|
||||
if (ret != buf_size) {
|
||||
error_report("Failed to read bitmap bits");
|
||||
g_free(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -671,6 +672,9 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id)
|
|||
|
||||
do {
|
||||
ret = dirty_bitmap_load_header(f, &s);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (s.flags & DIRTY_BITMAP_MIG_FLAG_START) {
|
||||
ret = dirty_bitmap_load_start(f, &s);
|
||||
|
|
|
@ -4036,14 +4036,9 @@ static int monitor_can_read(void *opaque)
|
|||
static void monitor_qmp_respond(Monitor *mon, QObject *rsp,
|
||||
Error *err, QObject *id)
|
||||
{
|
||||
QDict *qdict = NULL;
|
||||
|
||||
if (err) {
|
||||
assert(!rsp);
|
||||
qdict = qdict_new();
|
||||
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
|
||||
error_free(err);
|
||||
rsp = QOBJECT(qdict);
|
||||
rsp = QOBJECT(qmp_error_response(err));
|
||||
}
|
||||
|
||||
if (rsp) {
|
||||
|
|
18
nbd/client.c
18
nbd/client.c
|
@ -435,8 +435,8 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
|
|||
}
|
||||
be32_to_cpus(&info->min_block);
|
||||
if (!is_power_of_2(info->min_block)) {
|
||||
error_setg(errp, "server minimum block size %" PRId32
|
||||
"is not a power of two", info->min_block);
|
||||
error_setg(errp, "server minimum block size %" PRIu32
|
||||
" is not a power of two", info->min_block);
|
||||
nbd_send_opt_abort(ioc);
|
||||
return -1;
|
||||
}
|
||||
|
@ -450,8 +450,8 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
|
|||
be32_to_cpus(&info->opt_block);
|
||||
if (!is_power_of_2(info->opt_block) ||
|
||||
info->opt_block < info->min_block) {
|
||||
error_setg(errp, "server preferred block size %" PRId32
|
||||
"is not valid", info->opt_block);
|
||||
error_setg(errp, "server preferred block size %" PRIu32
|
||||
" is not valid", info->opt_block);
|
||||
nbd_send_opt_abort(ioc);
|
||||
return -1;
|
||||
}
|
||||
|
@ -462,6 +462,12 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname,
|
|||
return -1;
|
||||
}
|
||||
be32_to_cpus(&info->max_block);
|
||||
if (info->max_block < info->min_block) {
|
||||
error_setg(errp, "server maximum block size %" PRIu32
|
||||
" is not valid", info->max_block);
|
||||
nbd_send_opt_abort(ioc);
|
||||
return -1;
|
||||
}
|
||||
trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block,
|
||||
info->max_block);
|
||||
break;
|
||||
|
@ -613,8 +619,8 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
|
|||
{
|
||||
int ret;
|
||||
NBDOptionReply reply;
|
||||
uint32_t received_id;
|
||||
bool received;
|
||||
uint32_t received_id = 0;
|
||||
bool received = false;
|
||||
uint32_t export_len = strlen(export);
|
||||
uint32_t context_len = strlen(context);
|
||||
uint32_t data_len = sizeof(export_len) + export_len +
|
||||
|
|
|
@ -2007,6 +2007,10 @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
|
|||
"discard failed", errp);
|
||||
|
||||
case NBD_CMD_BLOCK_STATUS:
|
||||
if (!request->len) {
|
||||
return nbd_send_generic_reply(client, request->handle, -EINVAL,
|
||||
"need non-zero length", errp);
|
||||
}
|
||||
if (client->export_meta.valid && client->export_meta.base_allocation) {
|
||||
return nbd_co_send_block_status(client, request->handle,
|
||||
blk_bs(exp->blk), request->from,
|
||||
|
|
18
net/tap.c
18
net/tap.c
|
@ -40,6 +40,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/sockets.h"
|
||||
|
||||
#include "net/tap.h"
|
||||
|
||||
|
@ -693,6 +694,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
|
|||
}
|
||||
return;
|
||||
}
|
||||
qemu_set_nonblock(vhostfd);
|
||||
} else {
|
||||
vhostfd = open("/dev/vhost-net", O_RDWR);
|
||||
if (vhostfd < 0) {
|
||||
|
@ -803,7 +805,8 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|||
} else if (tap->has_fds) {
|
||||
char **fds;
|
||||
char **vhost_fds;
|
||||
int nfds, nvhosts;
|
||||
int nfds = 0, nvhosts = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
|
||||
tap->has_vnet_hdr || tap->has_helper || tap->has_queues ||
|
||||
|
@ -823,6 +826,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|||
if (nfds != nvhosts) {
|
||||
error_setg(errp, "The number of fds passed does not match "
|
||||
"the number of vhostfds passed");
|
||||
ret = -1;
|
||||
goto free_fail;
|
||||
}
|
||||
}
|
||||
|
@ -831,6 +835,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|||
fd = monitor_fd_param(cur_mon, fds[i], &err);
|
||||
if (fd == -1) {
|
||||
error_propagate(errp, err);
|
||||
ret = -1;
|
||||
goto free_fail;
|
||||
}
|
||||
|
||||
|
@ -841,6 +846,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|||
} else if (vnet_hdr != tap_probe_vnet_hdr(fd)) {
|
||||
error_setg(errp,
|
||||
"vnet_hdr not consistent across given tap fds");
|
||||
ret = -1;
|
||||
goto free_fail;
|
||||
}
|
||||
|
||||
|
@ -850,21 +856,21 @@ int net_init_tap(const Netdev *netdev, const char *name,
|
|||
vnet_hdr, fd, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
ret = -1;
|
||||
goto free_fail;
|
||||
}
|
||||
}
|
||||
g_free(fds);
|
||||
g_free(vhost_fds);
|
||||
return 0;
|
||||
|
||||
free_fail:
|
||||
for (i = 0; i < nvhosts; i++) {
|
||||
g_free(vhost_fds[i]);
|
||||
}
|
||||
for (i = 0; i < nfds; i++) {
|
||||
g_free(fds[i]);
|
||||
g_free(vhost_fds[i]);
|
||||
}
|
||||
g_free(fds);
|
||||
g_free(vhost_fds);
|
||||
return -1;
|
||||
return ret;
|
||||
} else if (tap->has_helper) {
|
||||
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
|
||||
tap->has_vnet_hdr || tap->has_queues || tap->has_vhostfds) {
|
||||
|
|
|
@ -299,7 +299,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
|
|||
s = DO_UPCAST(VhostUserState, nc, nc);
|
||||
if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
|
||||
error_report_err(err);
|
||||
return -1;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,7 +309,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
|
|||
do {
|
||||
if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
|
||||
error_report_err(err);
|
||||
return -1;
|
||||
goto err;
|
||||
}
|
||||
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
|
||||
net_vhost_user_event, NULL, nc0->name, NULL,
|
||||
|
@ -319,6 +319,13 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
|
|||
assert(s->vhost_net);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (nc0) {
|
||||
qemu_del_net_client(nc0);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static Chardev *net_vhost_claim_chardev(
|
||||
|
|
Binary file not shown.
|
@ -125,7 +125,7 @@ struct tpi_info {
|
|||
__u32 reserved3 : 12;
|
||||
__u32 int_type : 3;
|
||||
__u32 reserved4 : 12;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
/* channel command word (type 1) */
|
||||
struct ccw1 {
|
||||
|
|
|
@ -101,10 +101,11 @@ static inline bool manage_iplb(IplParameterBlock *iplb, bool store)
|
|||
{
|
||||
register unsigned long addr asm("0") = (unsigned long) iplb;
|
||||
register unsigned long rc asm("1") = 0;
|
||||
unsigned long subcode = store ? 6 : 5;
|
||||
|
||||
asm volatile ("diag %0,%2,0x308\n"
|
||||
: "+d" (addr), "+d" (rc)
|
||||
: "d" (store ? 6 : 5)
|
||||
: "d" (subcode)
|
||||
: "memory", "cc");
|
||||
return rc == 0x01;
|
||||
}
|
||||
|
|
|
@ -1172,6 +1172,9 @@
|
|||
# @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the NULL
|
||||
# state and disappearing from the query list. (since 2.12)
|
||||
#
|
||||
# @error: Error information if the job did not complete successfully.
|
||||
# Not set if the job completed successfully. (since 2.12.1)
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'struct': 'BlockJobInfo',
|
||||
|
@ -1179,7 +1182,8 @@
|
|||
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
|
||||
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
|
||||
'status': 'BlockJobStatus',
|
||||
'auto-finalize': 'bool', 'auto-dismiss': 'bool' } }
|
||||
'auto-finalize': 'bool', 'auto-dismiss': 'bool',
|
||||
'*error': 'str' } }
|
||||
|
||||
##
|
||||
# @query-block-jobs:
|
||||
|
|
|
@ -573,7 +573,7 @@
|
|||
'mips': 'CpuInfoOther',
|
||||
'tricore': 'CpuInfoOther',
|
||||
's390': 'CpuInfoS390',
|
||||
'riscv': 'CpuInfoRISCV',
|
||||
'riscv': 'CpuInfoOther',
|
||||
'other': 'CpuInfoOther' } }
|
||||
|
||||
##
|
||||
|
|
|
@ -122,11 +122,15 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
|||
return ret;
|
||||
}
|
||||
|
||||
QObject *qmp_build_error_object(Error *err)
|
||||
QDict *qmp_error_response(Error *err)
|
||||
{
|
||||
return qobject_from_jsonf("{ 'class': %s, 'desc': %s }",
|
||||
QapiErrorClass_str(error_get_class(err)),
|
||||
error_get_pretty(err));
|
||||
QDict *rsp;
|
||||
|
||||
rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }",
|
||||
QapiErrorClass_str(error_get_class(err)),
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
return rsp;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -159,15 +163,13 @@ QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request)
|
|||
|
||||
ret = do_qmp_dispatch(cmds, request, &err);
|
||||
|
||||
rsp = qdict_new();
|
||||
if (err) {
|
||||
qdict_put_obj(rsp, "error", qmp_build_error_object(err));
|
||||
error_free(err);
|
||||
rsp = qmp_error_response(err);
|
||||
} else if (ret) {
|
||||
rsp = qdict_new();
|
||||
qdict_put_obj(rsp, "return", ret);
|
||||
} else {
|
||||
QDECREF(rsp);
|
||||
return NULL;
|
||||
rsp = NULL;
|
||||
}
|
||||
|
||||
return QOBJECT(rsp);
|
||||
|
|
45
qemu-img.c
45
qemu-img.c
|
@ -277,12 +277,12 @@ static BlockBackend *img_open_opts(const char *optstr,
|
|||
options = qemu_opts_to_qdict(opts, NULL);
|
||||
if (force_share) {
|
||||
if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE)
|
||||
&& !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) {
|
||||
&& strcmp(qdict_get_str(options, BDRV_OPT_FORCE_SHARE), "on")) {
|
||||
error_report("--force-share/-U conflicts with image options");
|
||||
QDECREF(options);
|
||||
return NULL;
|
||||
}
|
||||
qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
|
||||
qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on");
|
||||
}
|
||||
blk = blk_new_open(NULL, NULL, options, flags, &local_err);
|
||||
if (!blk) {
|
||||
|
@ -1912,6 +1912,8 @@ static int convert_do_copy(ImgConvertState *s)
|
|||
return s->ret;
|
||||
}
|
||||
|
||||
#define MAX_BUF_SECTORS 32768
|
||||
|
||||
static int img_convert(int argc, char **argv)
|
||||
{
|
||||
int c, bs_i, flags, src_flags = 0;
|
||||
|
@ -2008,8 +2010,12 @@ static int img_convert(int argc, char **argv)
|
|||
int64_t sval;
|
||||
|
||||
sval = cvtnum(optarg);
|
||||
if (sval < 0) {
|
||||
error_report("Invalid minimum zero buffer size for sparse output specified");
|
||||
if (sval < 0 || sval & (BDRV_SECTOR_SIZE - 1) ||
|
||||
sval / BDRV_SECTOR_SIZE > MAX_BUF_SECTORS) {
|
||||
error_report("Invalid buffer size for sparse output specified. "
|
||||
"Valid sizes are multiples of %llu up to %llu. Select "
|
||||
"0 to disable sparse detection (fully allocates output).",
|
||||
BDRV_SECTOR_SIZE, MAX_BUF_SECTORS * BDRV_SECTOR_SIZE);
|
||||
goto fail_getopt;
|
||||
}
|
||||
|
||||
|
@ -2297,9 +2303,9 @@ static int img_convert(int argc, char **argv)
|
|||
}
|
||||
|
||||
/* increase bufsectors from the default 4096 (2M) if opt_transfer
|
||||
* or discard_alignment of the out_bs is greater. Limit to 32768 (16MB)
|
||||
* as maximum. */
|
||||
s.buf_sectors = MIN(32768,
|
||||
* or discard_alignment of the out_bs is greater. Limit to
|
||||
* MAX_BUF_SECTORS as maximum which is currently 32768 (16MB). */
|
||||
s.buf_sectors = MIN(MAX_BUF_SECTORS,
|
||||
MAX(s.buf_sectors,
|
||||
MAX(out_bs->bl.opt_transfer >> BDRV_SECTOR_BITS,
|
||||
out_bs->bl.pdiscard_alignment >>
|
||||
|
@ -2827,7 +2833,7 @@ static int img_map(int argc, char **argv)
|
|||
int64_t n;
|
||||
|
||||
/* Probe up to 1 GiB at a time. */
|
||||
n = QEMU_ALIGN_DOWN(MIN(1 << 30, length - offset), BDRV_SECTOR_SIZE);
|
||||
n = MIN(1 << 30, length - offset);
|
||||
ret = get_block_status(bs, offset, n, &next);
|
||||
|
||||
if (ret < 0) {
|
||||
|
@ -3191,6 +3197,9 @@ static int img_rebase(int argc, char **argv)
|
|||
}
|
||||
|
||||
if (out_baseimg[0]) {
|
||||
const char *overlay_filename;
|
||||
char *out_real_path;
|
||||
|
||||
options = qdict_new();
|
||||
if (out_basefmt) {
|
||||
qdict_put_str(options, "driver", out_basefmt);
|
||||
|
@ -3199,8 +3208,26 @@ static int img_rebase(int argc, char **argv)
|
|||
qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
|
||||
}
|
||||
|
||||
blk_new_backing = blk_new_open(out_baseimg, NULL,
|
||||
overlay_filename = bs->exact_filename[0] ? bs->exact_filename
|
||||
: bs->filename;
|
||||
out_real_path = g_malloc(PATH_MAX);
|
||||
|
||||
bdrv_get_full_backing_filename_from_filename(overlay_filename,
|
||||
out_baseimg,
|
||||
out_real_path,
|
||||
PATH_MAX,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_reportf_err(local_err,
|
||||
"Could not resolve backing filename: ");
|
||||
ret = -1;
|
||||
g_free(out_real_path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk_new_backing = blk_new_open(out_real_path, NULL,
|
||||
options, src_flags, &local_err);
|
||||
g_free(out_real_path);
|
||||
if (!blk_new_backing) {
|
||||
error_reportf_err(local_err,
|
||||
"Could not open new backing file '%s': ",
|
||||
|
|
|
@ -95,12 +95,12 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share,
|
|||
opts = qdict_new();
|
||||
}
|
||||
if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE)
|
||||
&& !qdict_get_bool(opts, BDRV_OPT_FORCE_SHARE)) {
|
||||
&& strcmp(qdict_get_str(opts, BDRV_OPT_FORCE_SHARE), "on")) {
|
||||
error_report("-U conflicts with image options");
|
||||
QDECREF(opts);
|
||||
return 1;
|
||||
}
|
||||
qdict_put_bool(opts, BDRV_OPT_FORCE_SHARE, true);
|
||||
qdict_put_str(opts, BDRV_OPT_FORCE_SHARE, "on");
|
||||
}
|
||||
qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err);
|
||||
if (!qemuio_blk) {
|
||||
|
|
58
qga/main.c
58
qga/main.c
|
@ -600,46 +600,42 @@ static void process_command(GAState *s, QDict *req)
|
|||
static void process_event(JSONMessageParser *parser, GQueue *tokens)
|
||||
{
|
||||
GAState *s = container_of(parser, GAState, parser);
|
||||
QDict *qdict;
|
||||
QObject *obj;
|
||||
QDict *req, *rsp;
|
||||
Error *err = NULL;
|
||||
int ret;
|
||||
|
||||
g_assert(s && parser);
|
||||
|
||||
g_debug("process_event: called");
|
||||
qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err));
|
||||
if (err || !qdict) {
|
||||
QDECREF(qdict);
|
||||
qdict = qdict_new();
|
||||
if (!err) {
|
||||
g_warning("failed to parse event: unknown error");
|
||||
error_setg(&err, QERR_JSON_PARSING);
|
||||
} else {
|
||||
g_warning("failed to parse event: %s", error_get_pretty(err));
|
||||
}
|
||||
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
|
||||
error_free(err);
|
||||
obj = json_parser_parse_err(tokens, NULL, &err);
|
||||
if (err) {
|
||||
goto err;
|
||||
}
|
||||
req = qobject_to(QDict, obj);
|
||||
if (!req) {
|
||||
error_setg(&err, QERR_JSON_PARSING);
|
||||
goto err;
|
||||
}
|
||||
if (!qdict_haskey(req, "execute")) {
|
||||
g_warning("unrecognized payload format");
|
||||
error_setg(&err, QERR_UNSUPPORTED);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* handle host->guest commands */
|
||||
if (qdict_haskey(qdict, "execute")) {
|
||||
process_command(s, qdict);
|
||||
} else {
|
||||
if (!qdict_haskey(qdict, "error")) {
|
||||
QDECREF(qdict);
|
||||
qdict = qdict_new();
|
||||
g_warning("unrecognized payload format");
|
||||
error_setg(&err, QERR_UNSUPPORTED);
|
||||
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
|
||||
error_free(err);
|
||||
}
|
||||
ret = send_response(s, QOBJECT(qdict));
|
||||
if (ret < 0) {
|
||||
g_warning("error sending error response: %s", strerror(-ret));
|
||||
}
|
||||
}
|
||||
process_command(s, req);
|
||||
qobject_decref(obj);
|
||||
return;
|
||||
|
||||
QDECREF(qdict);
|
||||
err:
|
||||
g_warning("failed to parse event: %s", error_get_pretty(err));
|
||||
rsp = qmp_error_response(err);
|
||||
ret = send_response(s, QOBJECT(rsp));
|
||||
if (ret < 0) {
|
||||
g_warning("error sending error response: %s", strerror(-ret));
|
||||
}
|
||||
QDECREF(rsp);
|
||||
qobject_decref(obj);
|
||||
}
|
||||
|
||||
/* false return signals GAChannel to close the current client connection */
|
||||
|
|
|
@ -76,6 +76,24 @@ QObject *qobject_from_jsonf(const char *string, ...)
|
|||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse @string as JSON object with %-escapes interpolated.
|
||||
* Abort on error. Do not use with untrusted @string.
|
||||
* Return the resulting QDict. It is never null.
|
||||
*/
|
||||
QDict *qdict_from_jsonf_nofail(const char *string, ...)
|
||||
{
|
||||
QDict *obj;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, string);
|
||||
obj = qobject_to(QDict, qobject_from_jsonv(string, &ap, &error_abort));
|
||||
va_end(ap);
|
||||
|
||||
assert(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
typedef struct ToJsonIterState
|
||||
{
|
||||
int indent;
|
||||
|
|
|
@ -311,6 +311,8 @@ static void arm_cpu_reset(CPUState *s)
|
|||
&env->vfp.fp_status);
|
||||
set_float_detect_tininess(float_tininess_before_rounding,
|
||||
&env->vfp.standard_fp_status);
|
||||
set_float_detect_tininess(float_tininess_before_rounding,
|
||||
&env->vfp.fp_status_f16);
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (kvm_enabled()) {
|
||||
kvm_arm_reset_vcpu(cpu);
|
||||
|
|
|
@ -85,6 +85,16 @@ static inline uint32_t float_rel_to_flags(int res)
|
|||
return flags;
|
||||
}
|
||||
|
||||
uint64_t HELPER(vfp_cmph_a64)(float16 x, float16 y, void *fp_status)
|
||||
{
|
||||
return float_rel_to_flags(float16_compare_quiet(x, y, fp_status));
|
||||
}
|
||||
|
||||
uint64_t HELPER(vfp_cmpeh_a64)(float16 x, float16 y, void *fp_status)
|
||||
{
|
||||
return float_rel_to_flags(float16_compare(x, y, fp_status));
|
||||
}
|
||||
|
||||
uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, void *fp_status)
|
||||
{
|
||||
return float_rel_to_flags(float32_compare_quiet(x, y, fp_status));
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
|
||||
DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
|
||||
DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64)
|
||||
DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr)
|
||||
DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr)
|
||||
DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr)
|
||||
DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, ptr)
|
||||
DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, ptr)
|
||||
|
|
|
@ -11409,11 +11409,94 @@ VFP_CONV_FIX_A64(sq, s, 32, 64, int64)
|
|||
VFP_CONV_FIX(uh, s, 32, 32, uint16)
|
||||
VFP_CONV_FIX(ul, s, 32, 32, uint32)
|
||||
VFP_CONV_FIX_A64(uq, s, 32, 64, uint64)
|
||||
VFP_CONV_FIX_A64(sl, h, 16, 32, int32)
|
||||
VFP_CONV_FIX_A64(ul, h, 16, 32, uint32)
|
||||
|
||||
#undef VFP_CONV_FIX
|
||||
#undef VFP_CONV_FIX_FLOAT
|
||||
#undef VFP_CONV_FLOAT_FIX_ROUND
|
||||
#undef VFP_CONV_FIX_A64
|
||||
|
||||
/* Conversion to/from f16 can overflow to infinity before/after scaling.
|
||||
* Therefore we convert to f64, scale, and then convert f64 to f16; or
|
||||
* vice versa for conversion to integer.
|
||||
*
|
||||
* For 16- and 32-bit integers, the conversion to f64 never rounds.
|
||||
* For 64-bit integers, any integer that would cause rounding will also
|
||||
* overflow to f16 infinity, so there is no double rounding problem.
|
||||
*/
|
||||
|
||||
static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst)
|
||||
{
|
||||
return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst);
|
||||
}
|
||||
|
||||
float16 HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst);
|
||||
}
|
||||
|
||||
float16 HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst);
|
||||
}
|
||||
|
||||
float16 HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst);
|
||||
}
|
||||
|
||||
float16 HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst);
|
||||
}
|
||||
|
||||
static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst)
|
||||
{
|
||||
if (unlikely(float16_is_any_nan(f))) {
|
||||
float_raise(float_flag_invalid, fpst);
|
||||
return 0;
|
||||
} else {
|
||||
int old_exc_flags = get_float_exception_flags(fpst);
|
||||
float64 ret;
|
||||
|
||||
ret = float16_to_float64(f, true, fpst);
|
||||
ret = float64_scalbn(ret, shift, fpst);
|
||||
old_exc_flags |= get_float_exception_flags(fpst)
|
||||
& float_flag_input_denormal;
|
||||
set_float_exception_flags(old_exc_flags, fpst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t HELPER(vfp_toshh)(float16 x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst);
|
||||
}
|
||||
|
||||
uint32_t HELPER(vfp_touhh)(float16 x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst);
|
||||
}
|
||||
|
||||
uint32_t HELPER(vfp_toslh)(float16 x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst);
|
||||
}
|
||||
|
||||
uint32_t HELPER(vfp_toulh)(float16 x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst);
|
||||
}
|
||||
|
||||
uint64_t HELPER(vfp_tosqh)(float16 x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst);
|
||||
}
|
||||
|
||||
uint64_t HELPER(vfp_touqh)(float16 x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst);
|
||||
}
|
||||
|
||||
/* Set the current fp rounding mode and return the old one.
|
||||
* The argument is a softfloat float_round_ value.
|
||||
|
|
|
@ -149,8 +149,12 @@ DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, ptr)
|
|||
DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, ptr)
|
||||
DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr)
|
||||
DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr)
|
||||
DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr)
|
||||
DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr)
|
||||
DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr)
|
||||
DEF_HELPER_3(vfp_toslh, i32, f16, i32, ptr)
|
||||
DEF_HELPER_3(vfp_touqh, i64, f16, i32, ptr)
|
||||
DEF_HELPER_3(vfp_tosqh, i64, f16, i32, ptr)
|
||||
DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr)
|
||||
DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr)
|
||||
DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr)
|
||||
|
@ -177,6 +181,8 @@ DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr)
|
|||
DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr)
|
||||
DEF_HELPER_3(vfp_sltoh, f16, i32, i32, ptr)
|
||||
DEF_HELPER_3(vfp_ultoh, f16, i32, i32, ptr)
|
||||
DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, ptr)
|
||||
DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr)
|
||||
DEF_HELPER_FLAGS_2(set_neon_rmode, TCG_CALL_NO_RWG, i32, i32, env)
|
||||
|
|
|
@ -614,6 +614,14 @@ static TCGv_i32 read_fp_sreg(DisasContext *s, int reg)
|
|||
return v;
|
||||
}
|
||||
|
||||
static TCGv_i32 read_fp_hreg(DisasContext *s, int reg)
|
||||
{
|
||||
TCGv_i32 v = tcg_temp_new_i32();
|
||||
|
||||
tcg_gen_ld16u_i32(v, cpu_env, fp_reg_offset(s, reg, MO_16));
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Clear the bits above an N-bit vector, for N = (is_q ? 128 : 64).
|
||||
* If SVE is not enabled, then there are only 128 bits in the vector.
|
||||
*/
|
||||
|
@ -4461,14 +4469,14 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn)
|
|||
}
|
||||
}
|
||||
|
||||
static void handle_fp_compare(DisasContext *s, bool is_double,
|
||||
static void handle_fp_compare(DisasContext *s, int size,
|
||||
unsigned int rn, unsigned int rm,
|
||||
bool cmp_with_zero, bool signal_all_nans)
|
||||
{
|
||||
TCGv_i64 tcg_flags = tcg_temp_new_i64();
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(false);
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(size == MO_16);
|
||||
|
||||
if (is_double) {
|
||||
if (size == MO_64) {
|
||||
TCGv_i64 tcg_vn, tcg_vm;
|
||||
|
||||
tcg_vn = read_fp_dreg(s, rn);
|
||||
|
@ -4485,19 +4493,35 @@ static void handle_fp_compare(DisasContext *s, bool is_double,
|
|||
tcg_temp_free_i64(tcg_vn);
|
||||
tcg_temp_free_i64(tcg_vm);
|
||||
} else {
|
||||
TCGv_i32 tcg_vn, tcg_vm;
|
||||
TCGv_i32 tcg_vn = tcg_temp_new_i32();
|
||||
TCGv_i32 tcg_vm = tcg_temp_new_i32();
|
||||
|
||||
tcg_vn = read_fp_sreg(s, rn);
|
||||
read_vec_element_i32(s, tcg_vn, rn, 0, size);
|
||||
if (cmp_with_zero) {
|
||||
tcg_vm = tcg_const_i32(0);
|
||||
tcg_gen_movi_i32(tcg_vm, 0);
|
||||
} else {
|
||||
tcg_vm = read_fp_sreg(s, rm);
|
||||
read_vec_element_i32(s, tcg_vm, rm, 0, size);
|
||||
}
|
||||
if (signal_all_nans) {
|
||||
gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
|
||||
} else {
|
||||
gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
|
||||
|
||||
switch (size) {
|
||||
case MO_32:
|
||||
if (signal_all_nans) {
|
||||
gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
|
||||
} else {
|
||||
gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
|
||||
}
|
||||
break;
|
||||
case MO_16:
|
||||
if (signal_all_nans) {
|
||||
gen_helper_vfp_cmpeh_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
|
||||
} else {
|
||||
gen_helper_vfp_cmph_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
tcg_temp_free_i32(tcg_vn);
|
||||
tcg_temp_free_i32(tcg_vm);
|
||||
}
|
||||
|
@ -4518,16 +4542,35 @@ static void handle_fp_compare(DisasContext *s, bool is_double,
|
|||
static void disas_fp_compare(DisasContext *s, uint32_t insn)
|
||||
{
|
||||
unsigned int mos, type, rm, op, rn, opc, op2r;
|
||||
int size;
|
||||
|
||||
mos = extract32(insn, 29, 3);
|
||||
type = extract32(insn, 22, 2); /* 0 = single, 1 = double */
|
||||
type = extract32(insn, 22, 2);
|
||||
rm = extract32(insn, 16, 5);
|
||||
op = extract32(insn, 14, 2);
|
||||
rn = extract32(insn, 5, 5);
|
||||
opc = extract32(insn, 3, 2);
|
||||
op2r = extract32(insn, 0, 3);
|
||||
|
||||
if (mos || op || op2r || type > 1) {
|
||||
if (mos || op || op2r) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
size = MO_32;
|
||||
break;
|
||||
case 1:
|
||||
size = MO_64;
|
||||
break;
|
||||
case 3:
|
||||
size = MO_16;
|
||||
if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
@ -4536,7 +4579,7 @@ static void disas_fp_compare(DisasContext *s, uint32_t insn)
|
|||
return;
|
||||
}
|
||||
|
||||
handle_fp_compare(s, type, rn, rm, opc & 1, opc & 2);
|
||||
handle_fp_compare(s, size, rn, rm, opc & 1, opc & 2);
|
||||
}
|
||||
|
||||
/* Floating point conditional compare
|
||||
|
@ -4550,16 +4593,35 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn)
|
|||
unsigned int mos, type, rm, cond, rn, op, nzcv;
|
||||
TCGv_i64 tcg_flags;
|
||||
TCGLabel *label_continue = NULL;
|
||||
int size;
|
||||
|
||||
mos = extract32(insn, 29, 3);
|
||||
type = extract32(insn, 22, 2); /* 0 = single, 1 = double */
|
||||
type = extract32(insn, 22, 2);
|
||||
rm = extract32(insn, 16, 5);
|
||||
cond = extract32(insn, 12, 4);
|
||||
rn = extract32(insn, 5, 5);
|
||||
op = extract32(insn, 4, 1);
|
||||
nzcv = extract32(insn, 0, 4);
|
||||
|
||||
if (mos || type > 1) {
|
||||
if (mos) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
size = MO_32;
|
||||
break;
|
||||
case 1:
|
||||
size = MO_64;
|
||||
break;
|
||||
case 3:
|
||||
size = MO_16;
|
||||
if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
@ -4580,7 +4642,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn)
|
|||
gen_set_label(label_match);
|
||||
}
|
||||
|
||||
handle_fp_compare(s, type, rn, rm, false, op);
|
||||
handle_fp_compare(s, size, rn, rm, false, op);
|
||||
|
||||
if (cond < 0x0e) {
|
||||
gen_set_label(label_continue);
|
||||
|
@ -4598,15 +4660,34 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn)
|
|||
unsigned int mos, type, rm, cond, rn, rd;
|
||||
TCGv_i64 t_true, t_false, t_zero;
|
||||
DisasCompare64 c;
|
||||
TCGMemOp sz;
|
||||
|
||||
mos = extract32(insn, 29, 3);
|
||||
type = extract32(insn, 22, 2); /* 0 = single, 1 = double */
|
||||
type = extract32(insn, 22, 2);
|
||||
rm = extract32(insn, 16, 5);
|
||||
cond = extract32(insn, 12, 4);
|
||||
rn = extract32(insn, 5, 5);
|
||||
rd = extract32(insn, 0, 5);
|
||||
|
||||
if (mos || type > 1) {
|
||||
if (mos) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
sz = MO_32;
|
||||
break;
|
||||
case 1:
|
||||
sz = MO_64;
|
||||
break;
|
||||
case 3:
|
||||
sz = MO_16;
|
||||
if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
@ -4615,11 +4696,11 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Zero extend sreg inputs to 64 bits now. */
|
||||
/* Zero extend sreg & hreg inputs to 64 bits now. */
|
||||
t_true = tcg_temp_new_i64();
|
||||
t_false = tcg_temp_new_i64();
|
||||
read_vec_element(s, t_true, rn, 0, type ? MO_64 : MO_32);
|
||||
read_vec_element(s, t_false, rm, 0, type ? MO_64 : MO_32);
|
||||
read_vec_element(s, t_true, rn, 0, sz);
|
||||
read_vec_element(s, t_false, rm, 0, sz);
|
||||
|
||||
a64_test_cc(&c, cond);
|
||||
t_zero = tcg_const_i64(0);
|
||||
|
@ -4628,7 +4709,7 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn)
|
|||
tcg_temp_free_i64(t_false);
|
||||
a64_free_cc(&c);
|
||||
|
||||
/* Note that sregs write back zeros to the high bits,
|
||||
/* Note that sregs & hregs write back zeros to the high bits,
|
||||
and we've already done the zero-extension. */
|
||||
write_fp_dreg(s, rd, t_true);
|
||||
tcg_temp_free_i64(t_true);
|
||||
|
@ -4638,11 +4719,9 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn)
|
|||
static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn)
|
||||
{
|
||||
TCGv_ptr fpst = NULL;
|
||||
TCGv_i32 tcg_op = tcg_temp_new_i32();
|
||||
TCGv_i32 tcg_op = read_fp_hreg(s, rn);
|
||||
TCGv_i32 tcg_res = tcg_temp_new_i32();
|
||||
|
||||
read_vec_element_i32(s, tcg_op, rn, 0, MO_16);
|
||||
|
||||
switch (opcode) {
|
||||
case 0x0: /* FMOV */
|
||||
tcg_gen_mov_i32(tcg_res, tcg_op);
|
||||
|
@ -4654,7 +4733,8 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn)
|
|||
tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000);
|
||||
break;
|
||||
case 0x3: /* FSQRT */
|
||||
gen_helper_sqrt_f16(tcg_res, tcg_op, cpu_env);
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
gen_helper_sqrt_f16(tcg_res, tcg_op, fpst);
|
||||
break;
|
||||
case 0x8: /* FRINTN */
|
||||
case 0x9: /* FRINTP */
|
||||
|
@ -5050,6 +5130,61 @@ static void handle_fp_2src_double(DisasContext *s, int opcode,
|
|||
tcg_temp_free_i64(tcg_res);
|
||||
}
|
||||
|
||||
/* Floating-point data-processing (2 source) - half precision */
|
||||
static void handle_fp_2src_half(DisasContext *s, int opcode,
|
||||
int rd, int rn, int rm)
|
||||
{
|
||||
TCGv_i32 tcg_op1;
|
||||
TCGv_i32 tcg_op2;
|
||||
TCGv_i32 tcg_res;
|
||||
TCGv_ptr fpst;
|
||||
|
||||
tcg_res = tcg_temp_new_i32();
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
tcg_op1 = read_fp_hreg(s, rn);
|
||||
tcg_op2 = read_fp_hreg(s, rm);
|
||||
|
||||
switch (opcode) {
|
||||
case 0x0: /* FMUL */
|
||||
gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
break;
|
||||
case 0x1: /* FDIV */
|
||||
gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
break;
|
||||
case 0x2: /* FADD */
|
||||
gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
break;
|
||||
case 0x3: /* FSUB */
|
||||
gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
break;
|
||||
case 0x4: /* FMAX */
|
||||
gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
break;
|
||||
case 0x5: /* FMIN */
|
||||
gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
break;
|
||||
case 0x6: /* FMAXNM */
|
||||
gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
break;
|
||||
case 0x7: /* FMINNM */
|
||||
gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
break;
|
||||
case 0x8: /* FNMUL */
|
||||
gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
tcg_gen_xori_i32(tcg_res, tcg_res, 0x8000);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
write_fp_sreg(s, rd, tcg_res);
|
||||
|
||||
tcg_temp_free_ptr(fpst);
|
||||
tcg_temp_free_i32(tcg_op1);
|
||||
tcg_temp_free_i32(tcg_op2);
|
||||
tcg_temp_free_i32(tcg_res);
|
||||
}
|
||||
|
||||
/* Floating point data-processing (2 source)
|
||||
* 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
|
||||
* +---+---+---+-----------+------+---+------+--------+-----+------+------+
|
||||
|
@ -5082,6 +5217,16 @@ static void disas_fp_2src(DisasContext *s, uint32_t insn)
|
|||
}
|
||||
handle_fp_2src_double(s, opcode, rd, rn, rm);
|
||||
break;
|
||||
case 3:
|
||||
if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
if (!fp_access_check(s)) {
|
||||
return;
|
||||
}
|
||||
handle_fp_2src_half(s, opcode, rd, rn, rm);
|
||||
break;
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
}
|
||||
|
@ -5163,6 +5308,44 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1,
|
|||
tcg_temp_free_i64(tcg_res);
|
||||
}
|
||||
|
||||
/* Floating-point data-processing (3 source) - half precision */
|
||||
static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1,
|
||||
int rd, int rn, int rm, int ra)
|
||||
{
|
||||
TCGv_i32 tcg_op1, tcg_op2, tcg_op3;
|
||||
TCGv_i32 tcg_res = tcg_temp_new_i32();
|
||||
TCGv_ptr fpst = get_fpstatus_ptr(true);
|
||||
|
||||
tcg_op1 = read_fp_hreg(s, rn);
|
||||
tcg_op2 = read_fp_hreg(s, rm);
|
||||
tcg_op3 = read_fp_hreg(s, ra);
|
||||
|
||||
/* These are fused multiply-add, and must be done as one
|
||||
* floating point operation with no rounding between the
|
||||
* multiplication and addition steps.
|
||||
* NB that doing the negations here as separate steps is
|
||||
* correct : an input NaN should come out with its sign bit
|
||||
* flipped if it is a negated-input.
|
||||
*/
|
||||
if (o1 == true) {
|
||||
tcg_gen_xori_i32(tcg_op3, tcg_op3, 0x8000);
|
||||
}
|
||||
|
||||
if (o0 != o1) {
|
||||
tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000);
|
||||
}
|
||||
|
||||
gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst);
|
||||
|
||||
write_fp_sreg(s, rd, tcg_res);
|
||||
|
||||
tcg_temp_free_ptr(fpst);
|
||||
tcg_temp_free_i32(tcg_op1);
|
||||
tcg_temp_free_i32(tcg_op2);
|
||||
tcg_temp_free_i32(tcg_op3);
|
||||
tcg_temp_free_i32(tcg_res);
|
||||
}
|
||||
|
||||
/* Floating point data-processing (3 source)
|
||||
* 31 30 29 28 24 23 22 21 20 16 15 14 10 9 5 4 0
|
||||
* +---+---+---+-----------+------+----+------+----+------+------+------+
|
||||
|
@ -5192,6 +5375,16 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn)
|
|||
}
|
||||
handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra);
|
||||
break;
|
||||
case 3:
|
||||
if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
if (!fp_access_check(s)) {
|
||||
return;
|
||||
}
|
||||
handle_fp_3src_half(s, o0, o1, rd, rn, rm, ra);
|
||||
break;
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
}
|
||||
|
@ -5239,11 +5432,25 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn)
|
|||
{
|
||||
int rd = extract32(insn, 0, 5);
|
||||
int imm8 = extract32(insn, 13, 8);
|
||||
int is_double = extract32(insn, 22, 2);
|
||||
int type = extract32(insn, 22, 2);
|
||||
uint64_t imm;
|
||||
TCGv_i64 tcg_res;
|
||||
TCGMemOp sz;
|
||||
|
||||
if (is_double > 1) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
sz = MO_32;
|
||||
break;
|
||||
case 1:
|
||||
sz = MO_64;
|
||||
break;
|
||||
case 3:
|
||||
sz = MO_16;
|
||||
if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
@ -5252,7 +5459,7 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn)
|
|||
return;
|
||||
}
|
||||
|
||||
imm = vfp_expand_imm(MO_32 + is_double, imm8);
|
||||
imm = vfp_expand_imm(sz, imm8);
|
||||
|
||||
tcg_res = tcg_const_i64(imm);
|
||||
write_fp_dreg(s, rd, tcg_res);
|
||||
|
@ -5268,11 +5475,11 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
|
|||
bool itof, int rmode, int scale, int sf, int type)
|
||||
{
|
||||
bool is_signed = !(opcode & 1);
|
||||
bool is_double = type;
|
||||
TCGv_ptr tcg_fpstatus;
|
||||
TCGv_i32 tcg_shift;
|
||||
TCGv_i32 tcg_shift, tcg_single;
|
||||
TCGv_i64 tcg_double;
|
||||
|
||||
tcg_fpstatus = get_fpstatus_ptr(false);
|
||||
tcg_fpstatus = get_fpstatus_ptr(type == 3);
|
||||
|
||||
tcg_shift = tcg_const_i32(64 - scale);
|
||||
|
||||
|
@ -5290,8 +5497,9 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
|
|||
tcg_int = tcg_extend;
|
||||
}
|
||||
|
||||
if (is_double) {
|
||||
TCGv_i64 tcg_double = tcg_temp_new_i64();
|
||||
switch (type) {
|
||||
case 1: /* float64 */
|
||||
tcg_double = tcg_temp_new_i64();
|
||||
if (is_signed) {
|
||||
gen_helper_vfp_sqtod(tcg_double, tcg_int,
|
||||
tcg_shift, tcg_fpstatus);
|
||||
|
@ -5301,8 +5509,10 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
|
|||
}
|
||||
write_fp_dreg(s, rd, tcg_double);
|
||||
tcg_temp_free_i64(tcg_double);
|
||||
} else {
|
||||
TCGv_i32 tcg_single = tcg_temp_new_i32();
|
||||
break;
|
||||
|
||||
case 0: /* float32 */
|
||||
tcg_single = tcg_temp_new_i32();
|
||||
if (is_signed) {
|
||||
gen_helper_vfp_sqtos(tcg_single, tcg_int,
|
||||
tcg_shift, tcg_fpstatus);
|
||||
|
@ -5312,6 +5522,23 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
|
|||
}
|
||||
write_fp_sreg(s, rd, tcg_single);
|
||||
tcg_temp_free_i32(tcg_single);
|
||||
break;
|
||||
|
||||
case 3: /* float16 */
|
||||
tcg_single = tcg_temp_new_i32();
|
||||
if (is_signed) {
|
||||
gen_helper_vfp_sqtoh(tcg_single, tcg_int,
|
||||
tcg_shift, tcg_fpstatus);
|
||||
} else {
|
||||
gen_helper_vfp_uqtoh(tcg_single, tcg_int,
|
||||
tcg_shift, tcg_fpstatus);
|
||||
}
|
||||
write_fp_sreg(s, rd, tcg_single);
|
||||
tcg_temp_free_i32(tcg_single);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
} else {
|
||||
TCGv_i64 tcg_int = cpu_reg(s, rd);
|
||||
|
@ -5328,8 +5555,9 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
|
|||
|
||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
|
||||
|
||||
if (is_double) {
|
||||
TCGv_i64 tcg_double = read_fp_dreg(s, rn);
|
||||
switch (type) {
|
||||
case 1: /* float64 */
|
||||
tcg_double = read_fp_dreg(s, rn);
|
||||
if (is_signed) {
|
||||
if (!sf) {
|
||||
gen_helper_vfp_tosld(tcg_int, tcg_double,
|
||||
|
@ -5347,9 +5575,14 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
|
|||
tcg_shift, tcg_fpstatus);
|
||||
}
|
||||
}
|
||||
if (!sf) {
|
||||
tcg_gen_ext32u_i64(tcg_int, tcg_int);
|
||||
}
|
||||
tcg_temp_free_i64(tcg_double);
|
||||
} else {
|
||||
TCGv_i32 tcg_single = read_fp_sreg(s, rn);
|
||||
break;
|
||||
|
||||
case 0: /* float32 */
|
||||
tcg_single = read_fp_sreg(s, rn);
|
||||
if (sf) {
|
||||
if (is_signed) {
|
||||
gen_helper_vfp_tosqs(tcg_int, tcg_single,
|
||||
|
@ -5371,14 +5604,39 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
|
|||
tcg_temp_free_i32(tcg_dest);
|
||||
}
|
||||
tcg_temp_free_i32(tcg_single);
|
||||
break;
|
||||
|
||||
case 3: /* float16 */
|
||||
tcg_single = read_fp_sreg(s, rn);
|
||||
if (sf) {
|
||||
if (is_signed) {
|
||||
gen_helper_vfp_tosqh(tcg_int, tcg_single,
|
||||
tcg_shift, tcg_fpstatus);
|
||||
} else {
|
||||
gen_helper_vfp_touqh(tcg_int, tcg_single,
|
||||
tcg_shift, tcg_fpstatus);
|
||||
}
|
||||
} else {
|
||||
TCGv_i32 tcg_dest = tcg_temp_new_i32();
|
||||
if (is_signed) {
|
||||
gen_helper_vfp_toslh(tcg_dest, tcg_single,
|
||||
tcg_shift, tcg_fpstatus);
|
||||
} else {
|
||||
gen_helper_vfp_toulh(tcg_dest, tcg_single,
|
||||
tcg_shift, tcg_fpstatus);
|
||||
}
|
||||
tcg_gen_extu_i32_i64(tcg_int, tcg_dest);
|
||||
tcg_temp_free_i32(tcg_dest);
|
||||
}
|
||||
tcg_temp_free_i32(tcg_single);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
|
||||
tcg_temp_free_i32(tcg_rmode);
|
||||
|
||||
if (!sf) {
|
||||
tcg_gen_ext32u_i64(tcg_int, tcg_int);
|
||||
}
|
||||
}
|
||||
|
||||
tcg_temp_free_ptr(tcg_fpstatus);
|
||||
|
@ -5403,8 +5661,21 @@ static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn)
|
|||
bool sf = extract32(insn, 31, 1);
|
||||
bool itof;
|
||||
|
||||
if (sbit || (type > 1)
|
||||
|| (!sf && scale < 32)) {
|
||||
if (sbit || (!sf && scale < 32)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0: /* float32 */
|
||||
case 1: /* float64 */
|
||||
break;
|
||||
case 3: /* float16 */
|
||||
if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
@ -5438,32 +5709,34 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof)
|
|||
|
||||
if (itof) {
|
||||
TCGv_i64 tcg_rn = cpu_reg(s, rn);
|
||||
TCGv_i64 tmp;
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
{
|
||||
/* 32 bit */
|
||||
TCGv_i64 tmp = tcg_temp_new_i64();
|
||||
tmp = tcg_temp_new_i64();
|
||||
tcg_gen_ext32u_i64(tmp, tcg_rn);
|
||||
tcg_gen_st_i64(tmp, cpu_env, fp_reg_offset(s, rd, MO_64));
|
||||
tcg_gen_movi_i64(tmp, 0);
|
||||
tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(s, rd));
|
||||
write_fp_dreg(s, rd, tmp);
|
||||
tcg_temp_free_i64(tmp);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
/* 64 bit */
|
||||
TCGv_i64 tmp = tcg_const_i64(0);
|
||||
tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_offset(s, rd, MO_64));
|
||||
tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(s, rd));
|
||||
tcg_temp_free_i64(tmp);
|
||||
write_fp_dreg(s, rd, tcg_rn);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
/* 64 bit to top half. */
|
||||
tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(s, rd));
|
||||
clear_vec_high(s, true, rd);
|
||||
break;
|
||||
case 3:
|
||||
/* 16 bit */
|
||||
tmp = tcg_temp_new_i64();
|
||||
tcg_gen_ext16u_i64(tmp, tcg_rn);
|
||||
write_fp_dreg(s, rd, tmp);
|
||||
tcg_temp_free_i64(tmp);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
} else {
|
||||
TCGv_i64 tcg_rd = cpu_reg(s, rd);
|
||||
|
@ -5481,6 +5754,12 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof)
|
|||
/* 64 bits from top half */
|
||||
tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_hi_offset(s, rn));
|
||||
break;
|
||||
case 3:
|
||||
/* 16 bit */
|
||||
tcg_gen_ld16u_i64(tcg_rd, cpu_env, fp_reg_offset(s, rn, MO_16));
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5520,6 +5799,12 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn)
|
|||
case 0xa: /* 64 bit */
|
||||
case 0xd: /* 64 bit to top half of quad */
|
||||
break;
|
||||
case 0x6: /* 16-bit float, 32-bit int */
|
||||
case 0xe: /* 16-bit float, 64-bit int */
|
||||
if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
/* all other sf/type/rmode combinations are invalid */
|
||||
unallocated_encoding(s);
|
||||
|
@ -5534,7 +5819,20 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn)
|
|||
/* actual FP conversions */
|
||||
bool itof = extract32(opcode, 1, 1);
|
||||
|
||||
if (type > 1 || (rmode != 0 && opcode > 1)) {
|
||||
if (rmode != 0 && opcode > 1) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
case 0: /* float32 */
|
||||
case 1: /* float64 */
|
||||
break;
|
||||
case 3: /* float16 */
|
||||
if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
default:
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
@ -7159,13 +7457,26 @@ static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar,
|
|||
int immh, int immb, int opcode,
|
||||
int rn, int rd)
|
||||
{
|
||||
bool is_double = extract32(immh, 3, 1);
|
||||
int size = is_double ? MO_64 : MO_32;
|
||||
int elements;
|
||||
int size, elements, fracbits;
|
||||
int immhb = immh << 3 | immb;
|
||||
int fracbits = (is_double ? 128 : 64) - immhb;
|
||||
|
||||
if (!extract32(immh, 2, 2)) {
|
||||
if (immh & 8) {
|
||||
size = MO_64;
|
||||
if (!is_scalar && !is_q) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
} else if (immh & 4) {
|
||||
size = MO_32;
|
||||
} else if (immh & 2) {
|
||||
size = MO_16;
|
||||
if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* immh == 0 would be a failure of the decode logic */
|
||||
g_assert(immh == 1);
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
@ -7173,20 +7484,14 @@ static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar,
|
|||
if (is_scalar) {
|
||||
elements = 1;
|
||||
} else {
|
||||
elements = is_double ? 2 : is_q ? 4 : 2;
|
||||
if (is_double && !is_q) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
elements = (8 << is_q) >> size;
|
||||
}
|
||||
fracbits = (16 << size) - immhb;
|
||||
|
||||
if (!fp_access_check(s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* immh == 0 would be a failure of the decode logic */
|
||||
g_assert(immh);
|
||||
|
||||
handle_simd_intfp_conv(s, rd, rn, elements, !is_u, fracbits, size);
|
||||
}
|
||||
|
||||
|
@ -7195,19 +7500,28 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar,
|
|||
bool is_q, bool is_u,
|
||||
int immh, int immb, int rn, int rd)
|
||||
{
|
||||
bool is_double = extract32(immh, 3, 1);
|
||||
int immhb = immh << 3 | immb;
|
||||
int fracbits = (is_double ? 128 : 64) - immhb;
|
||||
int pass;
|
||||
int pass, size, fracbits;
|
||||
TCGv_ptr tcg_fpstatus;
|
||||
TCGv_i32 tcg_rmode, tcg_shift;
|
||||
|
||||
if (!extract32(immh, 2, 2)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_scalar && !is_q && is_double) {
|
||||
if (immh & 0x8) {
|
||||
size = MO_64;
|
||||
if (!is_scalar && !is_q) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
} else if (immh & 0x4) {
|
||||
size = MO_32;
|
||||
} else if (immh & 0x2) {
|
||||
size = MO_16;
|
||||
if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Should have split out AdvSIMD modified immediate earlier. */
|
||||
assert(immh == 1);
|
||||
unallocated_encoding(s);
|
||||
return;
|
||||
}
|
||||
|
@ -7219,11 +7533,12 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar,
|
|||
assert(!(is_scalar && is_q));
|
||||
|
||||
tcg_rmode = tcg_const_i32(arm_rmode_to_sf(FPROUNDING_ZERO));
|
||||
tcg_fpstatus = get_fpstatus_ptr(false);
|
||||
tcg_fpstatus = get_fpstatus_ptr(size == MO_16);
|
||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
|
||||
fracbits = (16 << size) - immhb;
|
||||
tcg_shift = tcg_const_i32(fracbits);
|
||||
|
||||
if (is_double) {
|
||||
if (size == MO_64) {
|
||||
int maxpass = is_scalar ? 1 : 2;
|
||||
|
||||
for (pass = 0; pass < maxpass; pass++) {
|
||||
|
@ -7240,20 +7555,37 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar,
|
|||
}
|
||||
clear_vec_high(s, is_q, rd);
|
||||
} else {
|
||||
int maxpass = is_scalar ? 1 : is_q ? 4 : 2;
|
||||
void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr);
|
||||
int maxpass = is_scalar ? 1 : ((8 << is_q) >> size);
|
||||
|
||||
switch (size) {
|
||||
case MO_16:
|
||||
if (is_u) {
|
||||
fn = gen_helper_vfp_touhh;
|
||||
} else {
|
||||
fn = gen_helper_vfp_toshh;
|
||||
}
|
||||
break;
|
||||
case MO_32:
|
||||
if (is_u) {
|
||||
fn = gen_helper_vfp_touls;
|
||||
} else {
|
||||
fn = gen_helper_vfp_tosls;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
for (pass = 0; pass < maxpass; pass++) {
|
||||
TCGv_i32 tcg_op = tcg_temp_new_i32();
|
||||
|
||||
read_vec_element_i32(s, tcg_op, rn, pass, MO_32);
|
||||
if (is_u) {
|
||||
gen_helper_vfp_touls(tcg_op, tcg_op, tcg_shift, tcg_fpstatus);
|
||||
} else {
|
||||
gen_helper_vfp_tosls(tcg_op, tcg_op, tcg_shift, tcg_fpstatus);
|
||||
}
|
||||
read_vec_element_i32(s, tcg_op, rn, pass, size);
|
||||
fn(tcg_op, tcg_op, tcg_shift, tcg_fpstatus);
|
||||
if (is_scalar) {
|
||||
write_fp_sreg(s, rd, tcg_op);
|
||||
} else {
|
||||
write_vec_element_i32(s, tcg_op, rd, pass, MO_32);
|
||||
write_vec_element_i32(s, tcg_op, rd, pass, size);
|
||||
}
|
||||
tcg_temp_free_i32(tcg_op);
|
||||
}
|
||||
|
@ -7413,13 +7745,10 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn)
|
|||
tcg_temp_free_i64(tcg_op2);
|
||||
tcg_temp_free_i64(tcg_res);
|
||||
} else {
|
||||
TCGv_i32 tcg_op1 = tcg_temp_new_i32();
|
||||
TCGv_i32 tcg_op2 = tcg_temp_new_i32();
|
||||
TCGv_i32 tcg_op1 = read_fp_hreg(s, rn);
|
||||
TCGv_i32 tcg_op2 = read_fp_hreg(s, rm);
|
||||
TCGv_i64 tcg_res = tcg_temp_new_i64();
|
||||
|
||||
read_vec_element_i32(s, tcg_op1, rn, 0, MO_16);
|
||||
read_vec_element_i32(s, tcg_op2, rm, 0, MO_16);
|
||||
|
||||
gen_helper_neon_mull_s16(tcg_res, tcg_op1, tcg_op2);
|
||||
gen_helper_neon_addl_saturate_s32(tcg_res, cpu_env, tcg_res, tcg_res);
|
||||
|
||||
|
@ -7960,13 +8289,10 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s,
|
|||
|
||||
fpst = get_fpstatus_ptr(true);
|
||||
|
||||
tcg_op1 = tcg_temp_new_i32();
|
||||
tcg_op2 = tcg_temp_new_i32();
|
||||
tcg_op1 = read_fp_hreg(s, rn);
|
||||
tcg_op2 = read_fp_hreg(s, rm);
|
||||
tcg_res = tcg_temp_new_i32();
|
||||
|
||||
read_vec_element_i32(s, tcg_op1, rn, 0, MO_16);
|
||||
read_vec_element_i32(s, tcg_op2, rm, 0, MO_16);
|
||||
|
||||
switch (fpopcode) {
|
||||
case 0x03: /* FMULX */
|
||||
gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst);
|
||||
|
@ -11885,11 +12211,9 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn)
|
|||
}
|
||||
|
||||
if (is_scalar) {
|
||||
TCGv_i32 tcg_op = tcg_temp_new_i32();
|
||||
TCGv_i32 tcg_op = read_fp_hreg(s, rn);
|
||||
TCGv_i32 tcg_res = tcg_temp_new_i32();
|
||||
|
||||
read_vec_element_i32(s, tcg_op, rn, 0, MO_16);
|
||||
|
||||
switch (fpop) {
|
||||
case 0x1a: /* FCVTNS */
|
||||
case 0x1b: /* FCVTMS */
|
||||
|
|
|
@ -10783,8 +10783,23 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
|
|||
/* Coprocessor. */
|
||||
if (arm_dc_feature(s, ARM_FEATURE_M)) {
|
||||
/* We don't currently implement M profile FP support,
|
||||
* so this entire space should give a NOCP fault.
|
||||
* so this entire space should give a NOCP fault, with
|
||||
* the exception of the v8M VLLDM and VLSTM insns, which
|
||||
* must be NOPs in Secure state and UNDEF in Nonsecure state.
|
||||
*/
|
||||
if (arm_dc_feature(s, ARM_FEATURE_V8) &&
|
||||
(insn & 0xffa00f00) == 0xec200a00) {
|
||||
/* 0b1110_1100_0x1x_xxxx_xxxx_1010_xxxx_xxxx
|
||||
* - VLLDM, VLSTM
|
||||
* We choose to UNDEF if the RAZ bits are non-zero.
|
||||
*/
|
||||
if (!s->v8m_secure || (insn & 0x0040f0ff)) {
|
||||
goto illegal_op;
|
||||
}
|
||||
/* Just NOP since FP support is not implemented */
|
||||
break;
|
||||
}
|
||||
/* All other insns: NOCP */
|
||||
gen_exception_insn(s, 4, EXCP_NOCP, syn_uncategorized(),
|
||||
default_exception_el(s));
|
||||
break;
|
||||
|
|
|
@ -510,7 +510,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
|
|||
NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, "spec-ctrl", NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, "ssbd",
|
||||
},
|
||||
.cpuid_eax = 7,
|
||||
.cpuid_needs_ecx = true, .cpuid_ecx = 0,
|
||||
|
@ -541,7 +541,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
|
|||
"ibpb", NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
NULL, "virt-ssbd", NULL, NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
},
|
||||
.cpuid_eax = 0x80000008,
|
||||
|
|
|
@ -351,6 +351,7 @@ typedef enum X86Seg {
|
|||
#define MSR_IA32_FEATURE_CONTROL 0x0000003a
|
||||
#define MSR_TSC_ADJUST 0x0000003b
|
||||
#define MSR_IA32_SPEC_CTRL 0x48
|
||||
#define MSR_VIRT_SSBD 0xc001011f
|
||||
#define MSR_IA32_TSCDEADLINE 0x6e0
|
||||
|
||||
#define FEATURE_CONTROL_LOCKED (1<<0)
|
||||
|
@ -684,6 +685,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
|
|||
#define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */
|
||||
#define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */
|
||||
#define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */
|
||||
#define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) /* Speculative Store Bypass Disable */
|
||||
|
||||
#define KVM_HINTS_DEDICATED (1U << 0)
|
||||
|
||||
|
@ -1149,6 +1151,7 @@ typedef struct CPUX86State {
|
|||
uint32_t pkru;
|
||||
|
||||
uint64_t spec_ctrl;
|
||||
uint64_t virt_ssbd;
|
||||
|
||||
/* End of state preserved by INIT (dummy marker). */
|
||||
struct {} end_init_save;
|
||||
|
|
|
@ -92,6 +92,7 @@ static bool has_msr_hv_stimer;
|
|||
static bool has_msr_hv_frequencies;
|
||||
static bool has_msr_xss;
|
||||
static bool has_msr_spec_ctrl;
|
||||
static bool has_msr_virt_ssbd;
|
||||
static bool has_msr_smi_count;
|
||||
|
||||
static uint32_t has_architectural_pmu_version;
|
||||
|
@ -1218,6 +1219,9 @@ static int kvm_get_supported_msrs(KVMState *s)
|
|||
case MSR_IA32_SPEC_CTRL:
|
||||
has_msr_spec_ctrl = true;
|
||||
break;
|
||||
case MSR_VIRT_SSBD:
|
||||
has_msr_virt_ssbd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1706,6 +1710,10 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
|
|||
if (has_msr_spec_ctrl) {
|
||||
kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, env->spec_ctrl);
|
||||
}
|
||||
if (has_msr_virt_ssbd) {
|
||||
kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, env->virt_ssbd);
|
||||
}
|
||||
|
||||
#ifdef TARGET_X86_64
|
||||
if (lm_capable_kernel) {
|
||||
kvm_msr_entry_add(cpu, MSR_CSTAR, env->cstar);
|
||||
|
@ -2077,8 +2085,9 @@ static int kvm_get_msrs(X86CPU *cpu)
|
|||
if (has_msr_spec_ctrl) {
|
||||
kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, 0);
|
||||
}
|
||||
|
||||
|
||||
if (has_msr_virt_ssbd) {
|
||||
kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, 0);
|
||||
}
|
||||
if (!env->tsc_valid) {
|
||||
kvm_msr_entry_add(cpu, MSR_IA32_TSC, 0);
|
||||
env->tsc_valid = !runstate_is_running();
|
||||
|
@ -2444,6 +2453,9 @@ static int kvm_get_msrs(X86CPU *cpu)
|
|||
case MSR_IA32_SPEC_CTRL:
|
||||
env->spec_ctrl = msrs[i].data;
|
||||
break;
|
||||
case MSR_VIRT_SSBD:
|
||||
env->virt_ssbd = msrs[i].data;
|
||||
break;
|
||||
case MSR_IA32_RTIT_CTL:
|
||||
env->msr_rtit_ctrl = msrs[i].data;
|
||||
break;
|
||||
|
|
|
@ -893,6 +893,25 @@ static const VMStateDescription vmstate_msr_intel_pt = {
|
|||
}
|
||||
};
|
||||
|
||||
static bool virt_ssbd_needed(void *opaque)
|
||||
{
|
||||
X86CPU *cpu = opaque;
|
||||
CPUX86State *env = &cpu->env;
|
||||
|
||||
return env->virt_ssbd != 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_msr_virt_ssbd = {
|
||||
.name = "cpu/virt_ssbd",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = virt_ssbd_needed,
|
||||
.fields = (VMStateField[]){
|
||||
VMSTATE_UINT64(env.virt_ssbd, X86CPU),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
VMStateDescription vmstate_x86_cpu = {
|
||||
.name = "cpu",
|
||||
.version_id = 12,
|
||||
|
@ -1015,6 +1034,7 @@ VMStateDescription vmstate_x86_cpu = {
|
|||
&vmstate_spec_ctrl,
|
||||
&vmstate_mcg_ext_ctl,
|
||||
&vmstate_msr_intel_pt,
|
||||
&vmstate_msr_virt_ssbd,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
|
|
@ -102,12 +102,16 @@ void HELPER(wcsr_dc)(CPULM32State *env, uint32_t dc)
|
|||
|
||||
void HELPER(wcsr_im)(CPULM32State *env, uint32_t im)
|
||||
{
|
||||
qemu_mutex_lock_iothread();
|
||||
lm32_pic_set_im(env->pic_state, im);
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
void HELPER(wcsr_ip)(CPULM32State *env, uint32_t im)
|
||||
{
|
||||
qemu_mutex_lock_iothread();
|
||||
lm32_pic_set_ip(env->pic_state, im);
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
void HELPER(wcsr_jtx)(CPULM32State *env, uint32_t jtx)
|
||||
|
|
|
@ -200,6 +200,11 @@ static int cpu_pre_save(void *opaque)
|
|||
;
|
||||
cpu->mig_msr_mask = env->msr_mask & ~metamask;
|
||||
cpu->mig_insns_flags = env->insns_flags & insns_compat_mask;
|
||||
/* CPU models supported by old machines all have PPC_MEM_TLBIE,
|
||||
* so we set it unconditionally to allow backward migration from
|
||||
* a POWER9 host to a POWER8 host.
|
||||
*/
|
||||
cpu->mig_insns_flags |= PPC_MEM_TLBIE;
|
||||
cpu->mig_insns_flags2 = env->insns_flags2 & insns_compat_mask2;
|
||||
cpu->mig_nb_BATs = env->nb_BATs;
|
||||
}
|
||||
|
|
|
@ -7296,6 +7296,7 @@ static bool ppc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
|
|||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
|
||||
gen_debug_exception(ctx);
|
||||
dcbase->is_jmp = DISAS_NORETURN;
|
||||
/* The address covered by the breakpoint must be included in
|
||||
[tb->pc, tb->pc + tb->size) in order to for it to be
|
||||
properly cleared -- thus we increment the PC here so that
|
||||
|
|
|
@ -1733,7 +1733,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
|||
tcg_out_insn(s, 3305, LDR, offset, TCG_REG_TMP);
|
||||
}
|
||||
tcg_out_insn(s, 3207, BR, TCG_REG_TMP);
|
||||
s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s);
|
||||
set_jmp_reset_offset(s, a0);
|
||||
break;
|
||||
|
||||
case INDEX_op_goto_ptr:
|
||||
|
|
|
@ -159,8 +159,8 @@ typedef enum {
|
|||
INSN_STRD_IMM = 0x004000f0,
|
||||
INSN_STRD_REG = 0x000000f0,
|
||||
|
||||
INSN_DMB_ISH = 0x5bf07ff5,
|
||||
INSN_DMB_MCR = 0xba0f07ee,
|
||||
INSN_DMB_ISH = 0xf57ff05b,
|
||||
INSN_DMB_MCR = 0xee070fba,
|
||||
|
||||
/* Architected nop introduced in v6k. */
|
||||
/* ??? This is an MSR (imm) 0,0,0 insn. Anyone know if this
|
||||
|
@ -1822,7 +1822,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
|||
tcg_out_movi32(s, COND_AL, base, ptr - dil);
|
||||
}
|
||||
tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, base, dil);
|
||||
s->tb_jmp_reset_offset[args[0]] = tcg_current_code_size(s);
|
||||
set_jmp_reset_offset(s, args[0]);
|
||||
}
|
||||
break;
|
||||
case INDEX_op_goto_ptr:
|
||||
|
|
|
@ -854,11 +854,11 @@ static void tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece,
|
|||
switch (vece) {
|
||||
case MO_8:
|
||||
/* ??? With zero in a register, use PSHUFB. */
|
||||
tcg_out_vex_modrm(s, OPC_PUNPCKLBW, r, 0, a);
|
||||
tcg_out_vex_modrm(s, OPC_PUNPCKLBW, r, a, a);
|
||||
a = r;
|
||||
/* FALLTHRU */
|
||||
case MO_16:
|
||||
tcg_out_vex_modrm(s, OPC_PUNPCKLWD, r, 0, a);
|
||||
tcg_out_vex_modrm(s, OPC_PUNPCKLWD, r, a, a);
|
||||
a = r;
|
||||
/* FALLTHRU */
|
||||
case MO_32:
|
||||
|
@ -867,7 +867,7 @@ static void tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece,
|
|||
tcg_out8(s, 0);
|
||||
break;
|
||||
case MO_64:
|
||||
tcg_out_vex_modrm(s, OPC_PUNPCKLQDQ, r, 0, a);
|
||||
tcg_out_vex_modrm(s, OPC_PUNPCKLQDQ, r, a, a);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
|
@ -2245,7 +2245,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
|||
tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1,
|
||||
(intptr_t)(s->tb_jmp_target_addr + a0));
|
||||
}
|
||||
s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s);
|
||||
set_jmp_reset_offset(s, a0);
|
||||
break;
|
||||
case INDEX_op_goto_ptr:
|
||||
/* jmp to the given host address (could be epilogue) */
|
||||
|
@ -3529,7 +3529,7 @@ static void tcg_target_init(TCGContext *s)
|
|||
tcg_target_available_regs[TCG_TYPE_V256] = ALL_VECTOR_REGS;
|
||||
}
|
||||
|
||||
tcg_target_call_clobber_regs = 0;
|
||||
tcg_target_call_clobber_regs = ALL_VECTOR_REGS;
|
||||
tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX);
|
||||
tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX);
|
||||
tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX);
|
||||
|
|
|
@ -1744,7 +1744,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
|||
tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0);
|
||||
}
|
||||
tcg_out_nop(s);
|
||||
s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s);
|
||||
set_jmp_reset_offset(s, a0);
|
||||
break;
|
||||
case INDEX_op_goto_ptr:
|
||||
/* jmp to the given host address (could be epilogue) */
|
||||
|
|
|
@ -2025,10 +2025,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
|
|||
}
|
||||
tcg_out32(s, MTSPR | RS(TCG_REG_TB) | CTR);
|
||||
tcg_out32(s, BCCTR | BO_ALWAYS);
|
||||
s->tb_jmp_reset_offset[args[0]] = c = tcg_current_code_size(s);
|
||||
set_jmp_reset_offset(s, args[0]);
|
||||
if (USE_REG_TB) {
|
||||
/* For the unlinked case, need to reset TCG_REG_TB. */
|
||||
c = -c;
|
||||
c = -tcg_current_code_size(s);
|
||||
assert(c == (int16_t)c);
|
||||
tcg_out32(s, ADDI | TAI(TCG_REG_TB, TCG_REG_TB, c));
|
||||
}
|
||||
|
|
|
@ -1783,7 +1783,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
|||
/* and go there */
|
||||
tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_TB);
|
||||
}
|
||||
s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s);
|
||||
set_jmp_reset_offset(s, a0);
|
||||
|
||||
/* For the unlinked path of goto_tb, we need to reset
|
||||
TCG_REG_TB to the beginning of this TB. */
|
||||
|
|
|
@ -1388,12 +1388,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
|||
tcg_out_arithi(s, TCG_REG_G0, TCG_REG_TB, 0, JMPL);
|
||||
tcg_out_nop(s);
|
||||
}
|
||||
s->tb_jmp_reset_offset[a0] = c = tcg_current_code_size(s);
|
||||
set_jmp_reset_offset(s, a0);
|
||||
|
||||
/* For the unlinked path of goto_tb, we need to reset
|
||||
TCG_REG_TB to the beginning of this TB. */
|
||||
if (USE_REG_TB) {
|
||||
c = -c;
|
||||
c = -tcg_current_code_size(s);
|
||||
if (check_fit_i32(c, 13)) {
|
||||
tcg_out_arithi(s, TCG_REG_TB, TCG_REG_TB, c, ARITH_ADD);
|
||||
} else {
|
||||
|
|
16
tcg/tcg.c
16
tcg/tcg.c
|
@ -293,6 +293,14 @@ TCGLabel *gen_new_label(void)
|
|||
return l;
|
||||
}
|
||||
|
||||
static void set_jmp_reset_offset(TCGContext *s, int which)
|
||||
{
|
||||
size_t off = tcg_current_code_size(s);
|
||||
s->tb_jmp_reset_offset[which] = off;
|
||||
/* Make sure that we didn't overflow the stored offset. */
|
||||
assert(s->tb_jmp_reset_offset[which] == off);
|
||||
}
|
||||
|
||||
#include "tcg-target.inc.c"
|
||||
|
||||
static void tcg_region_bounds(size_t curr_region, void **pstart, void **pend)
|
||||
|
@ -866,6 +874,7 @@ void tcg_func_start(TCGContext *s)
|
|||
/* No temps have been previously allocated for size or locality. */
|
||||
memset(s->free_temps, 0, sizeof(s->free_temps));
|
||||
|
||||
s->nb_ops = 0;
|
||||
s->nb_labels = 0;
|
||||
s->current_frame_offset = s->frame_start;
|
||||
|
||||
|
@ -1983,6 +1992,7 @@ void tcg_op_remove(TCGContext *s, TCGOp *op)
|
|||
{
|
||||
QTAILQ_REMOVE(&s->ops, op, link);
|
||||
QTAILQ_INSERT_TAIL(&s->free_ops, op, link);
|
||||
s->nb_ops--;
|
||||
|
||||
#ifdef CONFIG_PROFILER
|
||||
atomic_set(&s->prof.del_op_count, s->prof.del_op_count + 1);
|
||||
|
@ -2002,6 +2012,7 @@ static TCGOp *tcg_op_alloc(TCGOpcode opc)
|
|||
}
|
||||
memset(op, 0, offsetof(TCGOp, link));
|
||||
op->opc = opc;
|
||||
s->nb_ops++;
|
||||
|
||||
return op;
|
||||
}
|
||||
|
@ -3351,7 +3362,10 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
|
|||
break;
|
||||
case INDEX_op_insn_start:
|
||||
if (num_insns >= 0) {
|
||||
s->gen_insn_end_off[num_insns] = tcg_current_code_size(s);
|
||||
size_t off = tcg_current_code_size(s);
|
||||
s->gen_insn_end_off[num_insns] = off;
|
||||
/* Assert that we do not overflow our stored offset. */
|
||||
assert(s->gen_insn_end_off[num_insns] == off);
|
||||
}
|
||||
num_insns++;
|
||||
for (i = 0; i < TARGET_INSN_START_WORDS; ++i) {
|
||||
|
|
10
tcg/tcg.h
10
tcg/tcg.h
|
@ -655,6 +655,7 @@ struct TCGContext {
|
|||
int nb_globals;
|
||||
int nb_temps;
|
||||
int nb_indirects;
|
||||
int nb_ops;
|
||||
|
||||
/* goto_tb support */
|
||||
tcg_insn_unit *code_buf;
|
||||
|
@ -844,7 +845,14 @@ static inline TCGOp *tcg_last_op(void)
|
|||
/* Test for whether to terminate the TB for using too many opcodes. */
|
||||
static inline bool tcg_op_buf_full(void)
|
||||
{
|
||||
return false;
|
||||
/* This is not a hard limit, it merely stops translation when
|
||||
* we have produced "enough" opcodes. We want to limit TB size
|
||||
* such that a RISC host can reasonably use a 16-bit signed
|
||||
* branch within the TB. We also need to be mindful of the
|
||||
* 16-bit unsigned offsets, TranslationBlock.jmp_reset_offset[]
|
||||
* and TCGContext.gen_insn_end_off[].
|
||||
*/
|
||||
return tcg_ctx->nb_ops >= 4000;
|
||||
}
|
||||
|
||||
/* pool based memory allocation */
|
||||
|
|
|
@ -574,7 +574,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
|
|||
/* Indirect jump method. */
|
||||
TODO();
|
||||
}
|
||||
s->tb_jmp_reset_offset[args[0]] = tcg_current_code_size(s);
|
||||
set_jmp_reset_offset(s, args[0]);
|
||||
break;
|
||||
case INDEX_op_br:
|
||||
tci_out_label(s, arg_label(args[0]));
|
||||
|
|
|
@ -29,9 +29,14 @@ status=1 # failure is the default!
|
|||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
rm -f "$TEST_DIR/t.$IMGFMT.base_old"
|
||||
rm -f "$TEST_DIR/t.$IMGFMT.base_new"
|
||||
_cleanup_test_img
|
||||
rm -f "$TEST_DIR/t.$IMGFMT.base_old"
|
||||
rm -f "$TEST_DIR/t.$IMGFMT.base_new"
|
||||
|
||||
rm -f "$TEST_DIR/subdir/t.$IMGFMT"
|
||||
rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_old"
|
||||
rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_new"
|
||||
rmdir "$TEST_DIR/subdir" 2> /dev/null
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
|
@ -123,6 +128,77 @@ io_pattern readv $((13 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00
|
|||
io_pattern readv $((14 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x11
|
||||
io_pattern readv $((15 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00
|
||||
|
||||
echo
|
||||
echo "=== Test rebase in a subdirectory of the working directory ==="
|
||||
echo
|
||||
|
||||
# Clean up the old images beforehand so they do not interfere with
|
||||
# this test
|
||||
_cleanup
|
||||
|
||||
mkdir "$TEST_DIR/subdir"
|
||||
|
||||
# Relative to the overlay
|
||||
BASE_OLD_OREL="t.$IMGFMT.base_old"
|
||||
BASE_NEW_OREL="t.$IMGFMT.base_new"
|
||||
|
||||
# Relative to $TEST_DIR (which is going to be our working directory)
|
||||
OVERLAY_WREL="subdir/t.$IMGFMT"
|
||||
|
||||
BASE_OLD="$TEST_DIR/subdir/$BASE_OLD_OREL"
|
||||
BASE_NEW="$TEST_DIR/subdir/$BASE_NEW_OREL"
|
||||
OVERLAY="$TEST_DIR/$OVERLAY_WREL"
|
||||
|
||||
# Test done here:
|
||||
#
|
||||
# Backing (old): 11 11 -- 11
|
||||
# Backing (new): -- 22 22 11
|
||||
# Overlay: -- -- -- --
|
||||
#
|
||||
# Rebasing works, we have verified that above. Here, we just want to
|
||||
# see that rebasing is done for the correct target backing file.
|
||||
|
||||
TEST_IMG=$BASE_OLD _make_test_img 1M
|
||||
TEST_IMG=$BASE_NEW _make_test_img 1M
|
||||
TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD_OREL" 1M
|
||||
|
||||
echo
|
||||
|
||||
$QEMU_IO "$BASE_OLD" \
|
||||
-c "write -P 0x11 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
|
||||
-c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \
|
||||
| _filter_qemu_io
|
||||
|
||||
$QEMU_IO "$BASE_NEW" \
|
||||
-c "write -P 0x22 $((1 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
|
||||
-c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \
|
||||
| _filter_qemu_io
|
||||
|
||||
echo
|
||||
|
||||
pushd "$TEST_DIR" >/dev/null
|
||||
$QEMU_IMG rebase -f "$IMGFMT" -b "$BASE_NEW_OREL" "$OVERLAY_WREL"
|
||||
popd >/dev/null
|
||||
|
||||
# Verify the backing path is correct
|
||||
TEST_IMG=$OVERLAY _img_info | grep '^backing file'
|
||||
|
||||
echo
|
||||
|
||||
# Verify the data is correct
|
||||
$QEMU_IO "$OVERLAY" \
|
||||
-c "read -P 0x11 $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
|
||||
-c "read -P 0x11 $((1 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
|
||||
-c "read -P 0x00 $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
|
||||
-c "read -P 0x11 $((3 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
|
||||
| _filter_qemu_io
|
||||
|
||||
echo
|
||||
|
||||
# Verify that cluster #3 is not allocated (because it is the same in
|
||||
# $BASE_OLD and $BASE_NEW)
|
||||
$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map
|
||||
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
|
|
|
@ -141,4 +141,34 @@ read 65536/65536 bytes at offset 917504
|
|||
=== IO: pattern 0x00
|
||||
read 65536/65536 bytes at offset 983040
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
=== Test rebase in a subdirectory of the working directory ===
|
||||
|
||||
Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=1048576
|
||||
Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=1048576
|
||||
Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=t.IMGFMT.base_old
|
||||
|
||||
wrote 131072/131072 bytes at offset 0
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 65536/65536 bytes at offset 196608
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 131072/131072 bytes at offset 65536
|
||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 65536/65536 bytes at offset 196608
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
backing file: t.IMGFMT.base_new (actual path: TEST_DIR/subdir/t.IMGFMT.base_new)
|
||||
|
||||
read 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 65536/65536 bytes at offset 65536
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 65536/65536 bytes at offset 131072
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 65536/65536 bytes at offset 196608
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
Offset Length File
|
||||
0 0x30000 TEST_DIR/subdir/t.IMGFMT
|
||||
0x30000 0x10000 TEST_DIR/subdir/t.IMGFMT.base_new
|
||||
*** done
|
||||
|
|
|
@ -440,6 +440,36 @@ echo "{'execute': 'qmp_capabilities'}
|
|||
-drive if=none,node-name=drive,file="$TEST_IMG",driver=qcow2 \
|
||||
| _filter_qmp | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Testing incoming inactive corrupted image ==="
|
||||
echo
|
||||
|
||||
_make_test_img 64M
|
||||
# Create an unaligned L1 entry, so qemu will signal a corruption when
|
||||
# reading from the covered area
|
||||
poke_file "$TEST_IMG" "$l1_offset" "\x00\x00\x00\x00\x2a\x2a\x2a\x2a"
|
||||
|
||||
# Inactive images are effectively read-only images, so this should be a
|
||||
# non-fatal corruption (which does not modify the image)
|
||||
echo "{'execute': 'qmp_capabilities'}
|
||||
{'execute': 'human-monitor-command',
|
||||
'arguments': {'command-line': 'qemu-io drive \"read 0 512\"'}}
|
||||
{'execute': 'quit'}" \
|
||||
| $QEMU -qmp stdio -nographic -nodefaults \
|
||||
-blockdev "{'node-name': 'drive',
|
||||
'driver': 'qcow2',
|
||||
'file': {
|
||||
'driver': 'file',
|
||||
'filename': '$TEST_IMG'
|
||||
}}" \
|
||||
-incoming exec:'cat /dev/null' \
|
||||
2>&1 \
|
||||
| _filter_qmp | _filter_qemu_io
|
||||
|
||||
echo
|
||||
# Image should not have been marked corrupt
|
||||
_img_info --format-specific | grep 'corrupt:'
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
|
|
@ -420,4 +420,18 @@ write failed: Input/output error
|
|||
{"return": ""}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
|
||||
|
||||
=== Testing incoming inactive corrupted image ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
QMP_VERSION
|
||||
{"return": {}}
|
||||
qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}}
|
||||
read failed: Input/output error
|
||||
{"return": ""}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
|
||||
|
||||
corrupt: false
|
||||
*** done
|
||||
|
|
|
@ -242,6 +242,23 @@ _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
|
|||
|
||||
_cleanup_qemu
|
||||
|
||||
echo
|
||||
echo "== Detecting -U and force-share conflicts =="
|
||||
|
||||
echo
|
||||
echo 'No conflict:'
|
||||
$QEMU_IMG info -U --image-opts driver=null-co,force-share=on
|
||||
echo
|
||||
echo 'Conflict:'
|
||||
$QEMU_IMG info -U --image-opts driver=null-co,force-share=off
|
||||
|
||||
echo
|
||||
echo 'No conflict:'
|
||||
$QEMU_IO -c 'open -r -U -o driver=null-co,force-share=on'
|
||||
echo
|
||||
echo 'Conflict:'
|
||||
$QEMU_IO -c 'open -r -U -o driver=null-co,force-share=off'
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
|
|
@ -399,4 +399,20 @@ Is another process using the image?
|
|||
Closing the other
|
||||
|
||||
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
|
||||
|
||||
== Detecting -U and force-share conflicts ==
|
||||
|
||||
No conflict:
|
||||
image: null-co://
|
||||
file format: null-co
|
||||
virtual size: 1.0G (1073741824 bytes)
|
||||
disk size: unavailable
|
||||
|
||||
Conflict:
|
||||
qemu-img: --force-share/-U conflicts with image options
|
||||
|
||||
No conflict:
|
||||
|
||||
Conflict:
|
||||
-U conflicts with image options
|
||||
*** done
|
||||
|
|
|
@ -36,9 +36,9 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q
|
|||
{"return": {}}
|
||||
Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}}
|
||||
{"return": {}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}}
|
||||
|
||||
=== Start backup job and exit qemu ===
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# This test covers what happens when a mirror block job is cancelled
|
||||
# in various phases of its existence.
|
||||
#
|
||||
# Note that this test only checks the emitted events (i.e.
|
||||
# BLOCK_JOB_COMPLETED vs. BLOCK_JOB_CANCELLED), it does not compare
|
||||
# whether the target is in sync with the source when the
|
||||
# BLOCK_JOB_COMPLETED event occurs. This is covered by other tests
|
||||
# (such as 041).
|
||||
#
|
||||
# Copyright (C) 2018 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Creator/Owner: Max Reitz <mreitz@redhat.com>
|
||||
|
||||
import iotests
|
||||
from iotests import log
|
||||
|
||||
iotests.verify_platform(['linux'])
|
||||
|
||||
|
||||
# Launches the VM, adds two null-co nodes (source and target), and
|
||||
# starts a blockdev-mirror job on them.
|
||||
#
|
||||
# Either both or none of speed and buf_size must be given.
|
||||
|
||||
def start_mirror(vm, speed=None, buf_size=None):
|
||||
vm.launch()
|
||||
|
||||
ret = vm.qmp('blockdev-add',
|
||||
node_name='source',
|
||||
driver='null-co',
|
||||
size=1048576)
|
||||
assert ret['return'] == {}
|
||||
|
||||
ret = vm.qmp('blockdev-add',
|
||||
node_name='target',
|
||||
driver='null-co',
|
||||
size=1048576)
|
||||
assert ret['return'] == {}
|
||||
|
||||
if speed is not None:
|
||||
ret = vm.qmp('blockdev-mirror',
|
||||
job_id='mirror',
|
||||
device='source',
|
||||
target='target',
|
||||
sync='full',
|
||||
speed=speed,
|
||||
buf_size=buf_size)
|
||||
else:
|
||||
ret = vm.qmp('blockdev-mirror',
|
||||
job_id='mirror',
|
||||
device='source',
|
||||
target='target',
|
||||
sync='full')
|
||||
|
||||
assert ret['return'] == {}
|
||||
|
||||
|
||||
log('')
|
||||
log('=== Cancel mirror job before convergence ===')
|
||||
log('')
|
||||
|
||||
log('--- force=false ---')
|
||||
log('')
|
||||
|
||||
with iotests.VM() as vm:
|
||||
# Low speed so it does not converge
|
||||
start_mirror(vm, 65536, 65536)
|
||||
|
||||
log('Cancelling job')
|
||||
log(vm.qmp('block-job-cancel', device='mirror', force=False))
|
||||
|
||||
log(vm.event_wait('BLOCK_JOB_CANCELLED'),
|
||||
filters=[iotests.filter_qmp_event])
|
||||
|
||||
log('')
|
||||
log('--- force=true ---')
|
||||
log('')
|
||||
|
||||
with iotests.VM() as vm:
|
||||
# Low speed so it does not converge
|
||||
start_mirror(vm, 65536, 65536)
|
||||
|
||||
log('Cancelling job')
|
||||
log(vm.qmp('block-job-cancel', device='mirror', force=True))
|
||||
|
||||
log(vm.event_wait('BLOCK_JOB_CANCELLED'),
|
||||
filters=[iotests.filter_qmp_event])
|
||||
|
||||
|
||||
log('')
|
||||
log('=== Cancel mirror job after convergence ===')
|
||||
log('')
|
||||
|
||||
log('--- force=false ---')
|
||||
log('')
|
||||
|
||||
with iotests.VM() as vm:
|
||||
start_mirror(vm)
|
||||
|
||||
log(vm.event_wait('BLOCK_JOB_READY'),
|
||||
filters=[iotests.filter_qmp_event])
|
||||
|
||||
log('Cancelling job')
|
||||
log(vm.qmp('block-job-cancel', device='mirror', force=False))
|
||||
|
||||
log(vm.event_wait('BLOCK_JOB_COMPLETED'),
|
||||
filters=[iotests.filter_qmp_event])
|
||||
|
||||
log('')
|
||||
log('--- force=true ---')
|
||||
log('')
|
||||
|
||||
with iotests.VM() as vm:
|
||||
start_mirror(vm)
|
||||
|
||||
log(vm.event_wait('BLOCK_JOB_READY'),
|
||||
filters=[iotests.filter_qmp_event])
|
||||
|
||||
log('Cancelling job')
|
||||
log(vm.qmp('block-job-cancel', device='mirror', force=True))
|
||||
|
||||
log(vm.event_wait('BLOCK_JOB_CANCELLED'),
|
||||
filters=[iotests.filter_qmp_event])
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
=== Cancel mirror job before convergence ===
|
||||
|
||||
--- force=false ---
|
||||
|
||||
Cancelling job
|
||||
{u'return': {}}
|
||||
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'}
|
||||
|
||||
--- force=true ---
|
||||
|
||||
Cancelling job
|
||||
{u'return': {}}
|
||||
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'}
|
||||
|
||||
=== Cancel mirror job after convergence ===
|
||||
|
||||
--- force=false ---
|
||||
|
||||
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'}
|
||||
Cancelling job
|
||||
{u'return': {}}
|
||||
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_COMPLETED'}
|
||||
|
||||
--- force=true ---
|
||||
|
||||
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'}
|
||||
Cancelling job
|
||||
{u'return': {}}
|
||||
{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_CANCELLED'}
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Test qemu-img vs. unaligned images
|
||||
#
|
||||
# Copyright (C) 2018 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
seq="$(basename $0)"
|
||||
echo "QA output created by $seq"
|
||||
|
||||
here="$PWD"
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt raw
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
echo
|
||||
echo "=== Check mapping of unaligned raw image ==="
|
||||
echo
|
||||
|
||||
_make_test_img 43009 # qemu-img create rounds size up
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
truncate --size=43009 "$TEST_IMG" # so we resize it and check again
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
$QEMU_IO -c 'w 43008 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
truncate --size=43009 "$TEST_IMG" # so we resize it and check again
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
# success, all done
|
||||
echo '*** done'
|
||||
rm -f $seq.full
|
||||
status=0
|
|
@ -0,0 +1,16 @@
|
|||
QA output created by 221
|
||||
|
||||
=== Check mapping of unaligned raw image ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=43009
|
||||
[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
wrote 1/1 bytes at offset 43008
|
||||
1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
*** done
|
|
@ -212,3 +212,5 @@
|
|||
211 rw auto quick
|
||||
212 rw auto quick
|
||||
213 rw auto quick
|
||||
218 rw auto quick
|
||||
221 rw auto quick
|
||||
|
|
|
@ -214,6 +214,10 @@ static void char_mux_test(void)
|
|||
g_assert_cmpint(h2.last_event, ==, -1);
|
||||
|
||||
/* switch focus */
|
||||
qemu_chr_be_write(base, (void *)"\1b", 2);
|
||||
g_assert_cmpint(h1.last_event, ==, 42);
|
||||
g_assert_cmpint(h2.last_event, ==, CHR_EVENT_BREAK);
|
||||
|
||||
qemu_chr_be_write(base, (void *)"\1c", 2);
|
||||
g_assert_cmpint(h1.last_event, ==, CHR_EVENT_MUX_IN);
|
||||
g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT);
|
||||
|
@ -227,6 +231,10 @@ static void char_mux_test(void)
|
|||
g_assert_cmpstr(h1.read_buf, ==, "hello");
|
||||
h1.read_count = 0;
|
||||
|
||||
qemu_chr_be_write(base, (void *)"\1b", 2);
|
||||
g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK);
|
||||
g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT);
|
||||
|
||||
/* remove first handler */
|
||||
qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, true);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue