Compare commits
318 Commits
pull-ui-20
...
pull-ui-20
Author | SHA1 | Date | |
---|---|---|---|
|
c62e90af8c | ||
|
06bb88145c | ||
|
67c4c2bd95 | ||
|
fba958c692 | ||
|
4aaddc2976 | ||
|
6d5322442a | ||
|
0e88d45a33 | ||
|
df32c8d436 | ||
|
232afac113 | ||
|
4552a09dd4 | ||
|
365162f7c0 | ||
|
b47d3af755 | ||
|
551747491d | ||
|
19b6d84316 | ||
|
fefd749ce2 | ||
|
d0d7708ba2 | ||
|
f1c17521e7 | ||
|
eb38c3b670 | ||
|
1a6245a5b0 | ||
|
798bfe0006 | ||
|
ee7d7aabda | ||
|
e1dc68155c | ||
|
1cb6d137ff | ||
|
46f296cd3a | ||
|
fca1031839 | ||
|
36fef36b91 | ||
|
4c1396cb57 | ||
|
76c64d3360 | ||
|
3daa41078a | ||
|
5a57acb66f | ||
|
67736a25f8 | ||
|
ade0d0c0d3 | ||
|
7d68e47f12 | ||
|
bada8e4470 | ||
|
da2b91409f | ||
|
b6e05aa473 | ||
|
8161befdd1 | ||
|
b09afd58e4 | ||
|
f1cd483004 | ||
|
dc3b89ef87 | ||
|
deb2db996c | ||
|
f02ccf5369 | ||
|
17c8a21978 | ||
|
cd0b19a20b | ||
|
ed56fdb256 | ||
|
91728bda76 | ||
|
5d596c245d | ||
|
533fdaedeb | ||
|
24da21f265 | ||
|
e6da780d5f | ||
|
433672b0d5 | ||
|
bf89e87427 | ||
|
c3d2d68ad6 | ||
|
d28d737fb9 | ||
|
e4937694b6 | ||
|
9af9e0fed7 | ||
|
b988468149 | ||
|
d410fe1454 | ||
|
b83baa6025 | ||
|
e43bfd9c87 | ||
|
c29b77f955 | ||
|
8aa802a6b7 | ||
|
8277d2aa58 | ||
|
73eaa04777 | ||
|
a4699e55f5 | ||
|
cd5c2dac2e | ||
|
f4d0064afc | ||
|
7828867198 | ||
|
193227f9e5 | ||
|
4fffeb5e19 | ||
|
85b01e0960 | ||
|
acef5c02e5 | ||
|
7e274652e4 | ||
|
675463d9b6 | ||
|
d10e54329b | ||
|
3a80ceadcb | ||
|
c72fbf98cb | ||
|
7b55044f9d | ||
|
543202c0dd | ||
|
9280eb34de | ||
|
5a8de107e3 | ||
|
b097e48121 | ||
|
84a3a53cf6 | ||
|
c525436e69 | ||
|
6231a6da9f | ||
|
007b06578a | ||
|
8d780f4392 | ||
|
c1bc66263c | ||
|
063e760a5f | ||
|
4addcd4fdc | ||
|
6ba996bb45 | ||
|
93d7af6ff0 | ||
|
48781e5bf2 | ||
|
88bfa2166a | ||
|
1789f4e37c | ||
|
cf87e0a3ee | ||
|
e11f463295 | ||
|
de37b0b87f | ||
|
3be5c2078b | ||
|
7b3fdbd9a8 | ||
|
3538fb6f89 | ||
|
b21b75981f | ||
|
d9eb0be229 | ||
|
649a1bbaf9 | ||
|
8acc216b95 | ||
|
cf57c2f18b | ||
|
00588a0aa2 | ||
|
c5620e658e | ||
|
e8109694c7 | ||
|
9350df7cea | ||
|
b682d3a7cf | ||
|
b1b66c3b5e | ||
|
b88641e236 | ||
|
d0b282a58c | ||
|
54d268b26a | ||
|
248de4a899 | ||
|
4ab0359a8a | ||
|
97225170f6 | ||
|
7b8a354d47 | ||
|
fea9b3ca9c | ||
|
5de6f3c0f4 | ||
|
b3d21a04b8 | ||
|
fea01f9604 | ||
|
ac0d9dbf33 | ||
|
530c003252 | ||
|
692a5519ab | ||
|
5a53dc5042 | ||
|
e73eecbdc2 | ||
|
cc06ca4c97 | ||
|
10e1b75961 | ||
|
fad6c58a3d | ||
|
d21ccd7bb9 | ||
|
fe02fc5209 | ||
|
1063477834 | ||
|
ff433bbb82 | ||
|
4e6f7cfbf9 | ||
|
529490e5d6 | ||
|
8f32510f1c | ||
|
a2a645d967 | ||
|
9f23b27d0d | ||
|
b429d363ed | ||
|
9763af08f8 | ||
|
f922254c87 | ||
|
713572a7b5 | ||
|
66c058cb69 | ||
|
b3afe33526 | ||
|
82407515fe | ||
|
240125bc49 | ||
|
34f22fc034 | ||
|
477cea9e6d | ||
|
87bbdd9caf | ||
|
215e209846 | ||
|
57040d4513 | ||
|
4b23699c82 | ||
|
fccbc78500 | ||
|
fc9f38c3c0 | ||
|
5013c54746 | ||
|
f949b4e5f5 | ||
|
877f8931b9 | ||
|
0eb9054c60 | ||
|
64f0f70a00 | ||
|
1c5f29bbc8 | ||
|
aec39c5349 | ||
|
3dc0a66d26 | ||
|
7d94a30b54 | ||
|
9c7ffe2664 | ||
|
3be9b3528d | ||
|
aa7f9966df | ||
|
007cd223de | ||
|
7d6d347d06 | ||
|
3509866ab3 | ||
|
f713d4d2f1 | ||
|
b79f17a9bc | ||
|
b22e0aef46 | ||
|
9c087a0504 | ||
|
f9262dae13 | ||
|
671f66f87f | ||
|
b50c7d452f | ||
|
c12d82ef15 | ||
|
8856be1512 | ||
|
5ae3e91c35 | ||
|
c469669ef7 | ||
|
fde58177aa | ||
|
663fb1e172 | ||
|
aa4a3dce1c | ||
|
dd3c168471 | ||
|
71c2f5b9b3 | ||
|
2e4ca7dbc1 | ||
|
938cdfefee | ||
|
54c6de864f | ||
|
6e50216461 | ||
|
8a1be662a6 | ||
|
56a571d9c8 | ||
|
50e5ae4dc3 | ||
|
2cf0148674 | ||
|
424e4a87d2 | ||
|
9fc6502606 | ||
|
3db119da79 | ||
|
9e4afc0995 | ||
|
afa06e1828 | ||
|
f97a88a85e | ||
|
e9fce79861 | ||
|
65aef4de5e | ||
|
22b5b8bf31 | ||
|
0dafe3b336 | ||
|
41f95a5200 | ||
|
78e1ad0509 | ||
|
16682a9d86 | ||
|
12e3b1f750 | ||
|
80b32df529 | ||
|
32b9741f50 | ||
|
b616ec4d6f | ||
|
196e213783 | ||
|
06989b8861 | ||
|
100681ccf1 | ||
|
c35b6e8032 | ||
|
4c5eebc1fa | ||
|
e4db279804 | ||
|
28f1f0e929 | ||
|
8b1da5f8fd | ||
|
95ed7e97e4 | ||
|
c355cb2c0f | ||
|
f58190e2c2 | ||
|
ee13584996 | ||
|
3892a2b741 | ||
|
a57d708d17 | ||
|
5ca5efa4a6 | ||
|
6b30608774 | ||
|
f294ecbc13 | ||
|
40f981a02d | ||
|
1d608d13eb | ||
|
fbd7a6b8e2 | ||
|
1295e21a17 | ||
|
f84548dda4 | ||
|
7f4495e1c1 | ||
|
f177d40ae2 | ||
|
2ca66546ce | ||
|
c943764596 | ||
|
e328e31660 | ||
|
2fbd884372 | ||
|
fb775d9074 | ||
|
b98401223d | ||
|
e8f9db491d | ||
|
12fdadb574 | ||
|
b2344f3e63 | ||
|
30bd0cf465 | ||
|
7cb08cb2d7 | ||
|
f070efa8d9 | ||
|
349a3b1cc9 | ||
|
c8e6c93857 | ||
|
c0d3573632 | ||
|
27e112f9fd | ||
|
e6deac9cf9 | ||
|
2a0fa68fb9 | ||
|
861d72cd28 | ||
|
0cf227229b | ||
|
7b36f78274 | ||
|
5d4d366585 | ||
|
ff626f2d9e | ||
|
6bb9ead762 | ||
|
9df2513730 | ||
|
d9767f1bfa | ||
|
2a0c56aa4c | ||
|
60ce86c714 | ||
|
72a189770a | ||
|
0192cc5d79 | ||
|
4b311c5f0b | ||
|
0d3716b4e6 | ||
|
ebe74f8ba2 | ||
|
592707af7f | ||
|
f657b17a63 | ||
|
fe9fa96d7c | ||
|
0e173b24b5 | ||
|
3e24bb3f12 | ||
|
928bed6a05 | ||
|
e36800c91a | ||
|
0e2082d9e5 | ||
|
dc295f8353 | ||
|
ad38ce9ed1 | ||
|
2209bd050a | ||
|
087462c773 | ||
|
fd0a10cd20 | ||
|
ec93e158b1 | ||
|
c22d5dcd7a | ||
|
0922c3f606 | ||
|
7d938fd14b | ||
|
84d04e2162 | ||
|
829dd2861a | ||
|
71042cffc0 | ||
|
756cb74a59 | ||
|
267ae092e2 | ||
|
364031f179 | ||
|
494a8ebe71 | ||
|
d57b78002c | ||
|
f00d4f596b | ||
|
3b9ca04653 | ||
|
fe52840c87 | ||
|
94ca2c7395 | ||
|
df92562e68 | ||
|
e40e5027f6 | ||
|
4193cdd771 | ||
|
05e4d14bf3 | ||
|
dd87de06fa | ||
|
f943078e42 | ||
|
61b422265e | ||
|
ae7e0bf461 | ||
|
1b935e1dc9 | ||
|
9b652fbe1d | ||
|
5581018400 | ||
|
49557d656c | ||
|
f19f11896e | ||
|
71ad761766 | ||
|
f2bbcd3f7f | ||
|
9b337ae90a | ||
|
0fa296eb00 | ||
|
fc27291daf | ||
|
7467d94cc4 | ||
|
253597d8cf |
@@ -1036,7 +1036,8 @@ Device Tree
|
||||
M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: device_tree.[ch]
|
||||
F: device_tree.c
|
||||
F: include/sysemu/device_tree.h
|
||||
|
||||
Error reporting
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
@@ -1115,8 +1116,9 @@ F: net/netmap.c
|
||||
Network Block Device (NBD)
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: block/nbd.c
|
||||
F: nbd.*
|
||||
F: block/nbd*
|
||||
F: nbd/
|
||||
F: include/block/nbd*
|
||||
F: qemu-nbd.c
|
||||
T: git git://github.com/bonzini/qemu.git nbd-next
|
||||
|
||||
@@ -1199,6 +1201,7 @@ SLIRP
|
||||
M: Jan Kiszka <jan.kiszka@siemens.com>
|
||||
S: Maintained
|
||||
F: slirp/
|
||||
F: net/slirp.c
|
||||
T: git git://git.kiszka.org/qemu.git queues/slirp
|
||||
|
||||
Tracing
|
||||
|
2
Makefile
2
Makefile
@@ -240,7 +240,7 @@ qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuu
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
||||
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o libqemuutil.a libqemustub.a
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
|
||||
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
|
||||
|
@@ -8,7 +8,8 @@ util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
block-obj-y = async.o thread-pool.o
|
||||
block-obj-y += nbd.o block.o blockjob.o
|
||||
block-obj-y += nbd/
|
||||
block-obj-y += block.o blockjob.o
|
||||
block-obj-y += main-loop.o iohandler.o qemu-timer.o
|
||||
block-obj-$(CONFIG_POSIX) += aio-posix.o
|
||||
block-obj-$(CONFIG_WIN32) += aio-win32.o
|
||||
|
@@ -258,9 +258,7 @@ void do_acpitable_option(const QemuOpts *opts)
|
||||
|
||||
acpi_table_add(opts, &err);
|
||||
if (err) {
|
||||
error_report("Wrong acpi table provided: %s",
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_reportf_err(err, "Wrong acpi table provided: ");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
@@ -1806,9 +1806,6 @@ static void audio_init (void)
|
||||
atexit (audio_atexit);
|
||||
|
||||
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
|
||||
if (!s->ts) {
|
||||
hw_error("Could not create audio timer\n");
|
||||
}
|
||||
|
||||
audio_process_options ("AUDIO", audio_options);
|
||||
|
||||
@@ -1859,12 +1856,8 @@ static void audio_init (void)
|
||||
|
||||
if (!done) {
|
||||
done = !audio_driver_init (s, &no_audio_driver);
|
||||
if (!done) {
|
||||
hw_error("Could not initialize audio subsystem\n");
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Using timer based audio emulation\n");
|
||||
}
|
||||
assert(done);
|
||||
dolog("warning: Using timer based audio emulation\n");
|
||||
}
|
||||
|
||||
if (conf.period.hertz <= 0) {
|
||||
|
@@ -566,6 +566,7 @@ static CharDriverState *chr_baum_init(const char *id,
|
||||
ChardevReturn *ret,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevCommon *common = qapi_ChardevDummy_base(backend->u.braille);
|
||||
BaumDriverState *baum;
|
||||
CharDriverState *chr;
|
||||
brlapi_handle_t *handle;
|
||||
@@ -576,8 +577,12 @@ static CharDriverState *chr_baum_init(const char *id,
|
||||
#endif
|
||||
int tty;
|
||||
|
||||
chr = qemu_chr_alloc(common, errp);
|
||||
if (!chr) {
|
||||
return NULL;
|
||||
}
|
||||
baum = g_malloc0(sizeof(BaumDriverState));
|
||||
baum->chr = chr = qemu_chr_alloc();
|
||||
baum->chr = chr;
|
||||
|
||||
chr->opaque = baum;
|
||||
chr->chr_write = baum_write;
|
||||
|
@@ -68,9 +68,13 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
|
||||
ChardevReturn *ret,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevCommon *common = qapi_ChardevDummy_base(backend->u.msmouse);
|
||||
CharDriverState *chr;
|
||||
|
||||
chr = qemu_chr_alloc();
|
||||
chr = qemu_chr_alloc(common, errp);
|
||||
if (!chr) {
|
||||
return NULL;
|
||||
}
|
||||
chr->chr_write = msmouse_chr_write;
|
||||
chr->chr_close = msmouse_chr_close;
|
||||
chr->explicit_be_open = true;
|
||||
|
24
block.c
24
block.c
@@ -1349,12 +1349,10 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
ret = bdrv_open_inherit(&backing_hd,
|
||||
*backing_filename ? backing_filename : NULL,
|
||||
reference, options, 0, bs, &child_backing,
|
||||
&local_err);
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
bs->open_flags |= BDRV_O_NO_BACKING;
|
||||
error_setg(errp, "Could not open backing file: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
error_prepend(errp, "Could not open backing file: ");
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
@@ -1460,13 +1458,11 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
||||
opts = qemu_opts_create(bdrv_qcow2.create_opts, NULL, 0,
|
||||
&error_abort);
|
||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_size, &error_abort);
|
||||
ret = bdrv_create(&bdrv_qcow2, tmp_filename, opts, &local_err);
|
||||
ret = bdrv_create(&bdrv_qcow2, tmp_filename, opts, errp);
|
||||
qemu_opts_del(opts);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not create temporary overlay "
|
||||
"'%s': %s", tmp_filename,
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
error_prepend(errp, "Could not create temporary overlay '%s': ",
|
||||
tmp_filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2154,9 +2150,10 @@ void bdrv_close(BlockDriverState *bs)
|
||||
bdrv_io_limits_disable(bs);
|
||||
}
|
||||
|
||||
bdrv_drain(bs); /* complete I/O */
|
||||
bdrv_drained_begin(bs); /* complete I/O */
|
||||
bdrv_flush(bs);
|
||||
bdrv_drain(bs); /* in case flush left pending I/O */
|
||||
|
||||
notifier_list_notify(&bs->close_notifiers, bs);
|
||||
|
||||
if (bs->blk) {
|
||||
@@ -2206,6 +2203,7 @@ void bdrv_close(BlockDriverState *bs)
|
||||
g_free(ban);
|
||||
}
|
||||
QLIST_INIT(&bs->aio_notifiers);
|
||||
bdrv_drained_end(bs);
|
||||
}
|
||||
|
||||
void bdrv_close_all(void)
|
||||
@@ -3728,9 +3726,9 @@ bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp)
|
||||
if (!QLIST_EMPTY(&bs->op_blockers[op])) {
|
||||
blocker = QLIST_FIRST(&bs->op_blockers[op]);
|
||||
if (errp) {
|
||||
error_setg(errp, "Node '%s' is busy: %s",
|
||||
bdrv_get_device_or_node_name(bs),
|
||||
error_get_pretty(blocker->reason));
|
||||
*errp = error_copy(blocker->reason);
|
||||
error_prepend(errp, "Node '%s' is busy: ",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@@ -1033,6 +1033,11 @@ void blk_set_guest_block_size(BlockBackend *blk, int align)
|
||||
blk->guest_block_size = align;
|
||||
}
|
||||
|
||||
void *blk_try_blockalign(BlockBackend *blk, size_t size)
|
||||
{
|
||||
return qemu_try_blockalign(blk ? blk->bs : NULL, size);
|
||||
}
|
||||
|
||||
void *blk_blockalign(BlockBackend *blk, size_t size)
|
||||
{
|
||||
return qemu_blockalign(blk ? blk->bs : NULL, size);
|
||||
|
@@ -1243,8 +1243,13 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
||||
iscsilun->lbprz = !!rc16->lbprz;
|
||||
iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
if (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
|
||||
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
||||
break;
|
||||
}
|
||||
/* Fall through and try READ CAPACITY(10) instead. */
|
||||
case TYPE_ROM:
|
||||
task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0);
|
||||
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
|
||||
@@ -1270,7 +1275,7 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
||||
&& retries-- > 0);
|
||||
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
error_setg(errp, "iSCSI: failed to send readcapacity10 command.");
|
||||
error_setg(errp, "iSCSI: failed to send readcapacity10/16 command");
|
||||
} else if (!iscsilun->block_size ||
|
||||
iscsilun->block_size % BDRV_SECTOR_SIZE) {
|
||||
error_setg(errp, "iSCSI: the target returned an invalid "
|
||||
|
@@ -250,6 +250,7 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
g_free(backing_filename2);
|
||||
backing_filename2 = NULL;
|
||||
error_free(err);
|
||||
err = NULL;
|
||||
}
|
||||
|
||||
/* Always report the full_backing_filename if present, even if it's the
|
||||
|
@@ -1762,9 +1762,8 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
ret = qcow2_open(bs, options, flags, &local_err);
|
||||
QDECREF(options);
|
||||
if (local_err) {
|
||||
error_setg(errp, "Could not reopen qcow2 layer: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "Could not reopen qcow2 layer: ");
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qcow2 layer");
|
||||
|
@@ -1611,9 +1611,8 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
memset(s, 0, sizeof(BDRVQEDState));
|
||||
ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err);
|
||||
if (local_err) {
|
||||
error_setg(errp, "Could not reopen qed layer: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "Could not reopen qed layer: ");
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qed layer");
|
||||
|
@@ -1861,8 +1861,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
||||
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
if (fd < 0) {
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
error_report_err(local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
@@ -2406,9 +2405,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
|
||||
ret = do_sd_create(s, &new_vid, 1, &local_err);
|
||||
if (ret < 0) {
|
||||
error_report("failed to create inode for snapshot: %s",
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
error_reportf_err(local_err,
|
||||
"failed to create inode for snapshot: ");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
@@ -784,12 +784,13 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
|
||||
if (logs.valid) {
|
||||
if (bs->read_only) {
|
||||
ret = -EPERM;
|
||||
error_setg_errno(errp, EPERM,
|
||||
"VHDX image file '%s' opened read-only, but "
|
||||
"contains a log that needs to be replayed. To "
|
||||
"replay the log, execute:\n qemu-img check -r "
|
||||
"all '%s'",
|
||||
bs->filename, bs->filename);
|
||||
error_setg(errp,
|
||||
"VHDX image file '%s' opened read-only, but "
|
||||
"contains a log that needs to be replayed",
|
||||
bs->filename);
|
||||
error_append_hint(errp, "To replay the log, run:\n"
|
||||
"qemu-img check -r all '%s'\n",
|
||||
bs->filename);
|
||||
goto exit;
|
||||
}
|
||||
/* now flush the log */
|
||||
|
50
block/vmdk.c
50
block/vmdk.c
@@ -760,6 +760,17 @@ static int vmdk_open_sparse(BlockDriverState *bs, BdrvChild *file, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
static const char *next_line(const char *s)
|
||||
{
|
||||
while (*s) {
|
||||
if (*s == '\n') {
|
||||
return s + 1;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
const char *desc_file_path, QDict *options,
|
||||
Error **errp)
|
||||
@@ -769,7 +780,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
char access[11];
|
||||
char type[11];
|
||||
char fname[512];
|
||||
const char *p = desc;
|
||||
const char *p, *np;
|
||||
int64_t sectors = 0;
|
||||
int64_t flat_offset;
|
||||
char *extent_path;
|
||||
@@ -779,7 +790,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
char extent_opt_prefix[32];
|
||||
Error *local_err = NULL;
|
||||
|
||||
while (*p) {
|
||||
for (p = desc; *p; p = next_line(p)) {
|
||||
/* parse extent line in one of below formats:
|
||||
*
|
||||
* RW [size in sectors] FLAT "file-name.vmdk" OFFSET
|
||||
@@ -791,29 +802,26 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64,
|
||||
access, §ors, type, fname, &flat_offset);
|
||||
if (matches < 4 || strcmp(access, "RW")) {
|
||||
goto next_line;
|
||||
continue;
|
||||
} else if (!strcmp(type, "FLAT")) {
|
||||
if (matches != 5 || flat_offset < 0) {
|
||||
error_setg(errp, "Invalid extent lines: \n%s", p);
|
||||
return -EINVAL;
|
||||
goto invalid;
|
||||
}
|
||||
} else if (!strcmp(type, "VMFS")) {
|
||||
if (matches == 4) {
|
||||
flat_offset = 0;
|
||||
} else {
|
||||
error_setg(errp, "Invalid extent lines:\n%s", p);
|
||||
return -EINVAL;
|
||||
goto invalid;
|
||||
}
|
||||
} else if (matches != 4) {
|
||||
error_setg(errp, "Invalid extent lines:\n%s", p);
|
||||
return -EINVAL;
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (sectors <= 0 ||
|
||||
(strcmp(type, "FLAT") && strcmp(type, "SPARSE") &&
|
||||
strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE")) ||
|
||||
(strcmp(access, "RW"))) {
|
||||
goto next_line;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!path_is_absolute(fname) && !path_has_protocol(fname) &&
|
||||
@@ -870,17 +878,17 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
return -ENOTSUP;
|
||||
}
|
||||
extent->type = g_strdup(type);
|
||||
next_line:
|
||||
/* move to next line */
|
||||
while (*p) {
|
||||
if (*p == '\n') {
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
invalid:
|
||||
np = next_line(p);
|
||||
assert(np != p);
|
||||
if (np[-1] == '\n') {
|
||||
np--;
|
||||
}
|
||||
error_setg(errp, "Invalid extent line: %.*s", (int)(np - p), p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
@@ -1494,8 +1502,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
|
||||
if (sector_num > bs->total_sectors) {
|
||||
error_report("Wrong offset: sector_num=0x%" PRIx64
|
||||
" total_sectors=0x%" PRIx64 "\n",
|
||||
sector_num, bs->total_sectors);
|
||||
" total_sectors=0x%" PRIx64,
|
||||
sector_num, bs->total_sectors);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@@ -27,9 +27,8 @@ static void nbd_accept(void *opaque)
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
|
||||
int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
|
||||
if (fd >= 0 && !nbd_client_new(NULL, fd, nbd_client_put)) {
|
||||
shutdown(fd, 2);
|
||||
close(fd);
|
||||
if (fd >= 0) {
|
||||
nbd_client_new(NULL, fd, nbd_client_put);
|
||||
}
|
||||
}
|
||||
|
||||
|
191
blockdev.c
191
blockdev.c
@@ -1582,13 +1582,11 @@ static void internal_snapshot_abort(BlkActionState *common)
|
||||
}
|
||||
|
||||
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
|
||||
error_report("Failed to delete snapshot with id '%s' and name '%s' on "
|
||||
"device '%s' in abort: %s",
|
||||
sn->id_str,
|
||||
sn->name,
|
||||
bdrv_get_device_name(bs),
|
||||
error_get_pretty(local_error));
|
||||
error_free(local_error);
|
||||
error_reportf_err(local_error,
|
||||
"Failed to delete snapshot with id '%s' and "
|
||||
"name '%s' on device '%s' in abort: ",
|
||||
sn->id_str, sn->name,
|
||||
bdrv_get_device_name(bs));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3291,29 +3289,23 @@ void qmp_blockdev_backup(const char *device, const char *target,
|
||||
NULL, errp);
|
||||
}
|
||||
|
||||
void qmp_drive_mirror(const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
bool has_node_name, const char *node_name,
|
||||
bool has_replaces, const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error, BlockdevOnError on_source_error,
|
||||
bool has_on_target_error, BlockdevOnError on_target_error,
|
||||
bool has_unmap, bool unmap,
|
||||
Error **errp)
|
||||
/* Parameter check and block job starting for drive mirroring.
|
||||
* Caller should hold @device and @target's aio context (must be the same).
|
||||
**/
|
||||
static void blockdev_mirror_common(BlockDriverState *bs,
|
||||
BlockDriverState *target,
|
||||
bool has_replaces, const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error,
|
||||
BlockdevOnError on_source_error,
|
||||
bool has_on_target_error,
|
||||
BlockdevOnError on_target_error,
|
||||
bool has_unmap, bool unmap,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *source, *target_bs;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
QDict *options;
|
||||
int flags;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
if (!has_speed) {
|
||||
speed = 0;
|
||||
@@ -3324,9 +3316,6 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
if (!has_on_target_error) {
|
||||
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
}
|
||||
if (!has_mode) {
|
||||
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
if (!has_granularity) {
|
||||
granularity = 0;
|
||||
}
|
||||
@@ -3348,6 +3337,55 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
|
||||
return;
|
||||
}
|
||||
if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) {
|
||||
return;
|
||||
}
|
||||
if (target->blk) {
|
||||
error_setg(errp, "Cannot mirror to an attached block device");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bs->backing && sync == MIRROR_SYNC_MODE_TOP) {
|
||||
sync = MIRROR_SYNC_MODE_FULL;
|
||||
}
|
||||
|
||||
/* pass the node name to replace to mirror start since it's loose coupling
|
||||
* and will allow to check whether the node still exist at mirror completion
|
||||
*/
|
||||
mirror_start(bs, target,
|
||||
has_replaces ? replaces : NULL,
|
||||
speed, granularity, buf_size, sync,
|
||||
on_source_error, on_target_error, unmap,
|
||||
block_job_cb, bs, errp);
|
||||
}
|
||||
|
||||
void qmp_drive_mirror(const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
bool has_node_name, const char *node_name,
|
||||
bool has_replaces, const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error, BlockdevOnError on_source_error,
|
||||
bool has_on_target_error, BlockdevOnError on_target_error,
|
||||
bool has_unmap, bool unmap,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *source, *target_bs;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
QDict *options = NULL;
|
||||
int flags;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
blk = blk_by_name(device);
|
||||
if (!blk) {
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
||||
@@ -3363,15 +3401,14 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
goto out;
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
if (!has_mode) {
|
||||
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
|
||||
if (!has_format) {
|
||||
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
flags = bs->open_flags | BDRV_O_RDWR;
|
||||
source = backing_bs(bs);
|
||||
if (!source && sync == MIRROR_SYNC_MODE_TOP) {
|
||||
@@ -3466,22 +3503,78 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
|
||||
bdrv_set_aio_context(target_bs, aio_context);
|
||||
|
||||
/* pass the node name to replace to mirror start since it's loose coupling
|
||||
* and will allow to check whether the node still exist at mirror completion
|
||||
*/
|
||||
mirror_start(bs, target_bs,
|
||||
has_replaces ? replaces : NULL,
|
||||
speed, granularity, buf_size, sync,
|
||||
on_source_error, on_target_error,
|
||||
unmap,
|
||||
block_job_cb, bs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
bdrv_unref(target_bs);
|
||||
blockdev_mirror_common(bs, target_bs,
|
||||
has_replaces, replaces, sync,
|
||||
has_speed, speed,
|
||||
has_granularity, granularity,
|
||||
has_buf_size, buf_size,
|
||||
has_on_source_error, on_source_error,
|
||||
has_on_target_error, on_target_error,
|
||||
has_unmap, unmap,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
bdrv_unref(target_bs);
|
||||
}
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_blockdev_mirror(const char *device, const char *target,
|
||||
bool has_replaces, const char *replaces,
|
||||
MirrorSyncMode sync,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_buf_size, int64_t buf_size,
|
||||
bool has_on_source_error,
|
||||
BlockdevOnError on_source_error,
|
||||
bool has_on_target_error,
|
||||
BlockdevOnError on_target_error,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *target_bs;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
|
||||
blk = blk_by_name(device);
|
||||
if (!blk) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
return;
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
|
||||
if (!bs) {
|
||||
error_setg(errp, "Device '%s' has no media", device);
|
||||
return;
|
||||
}
|
||||
|
||||
target_bs = bdrv_lookup_bs(target, target, errp);
|
||||
if (!target_bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_ref(target_bs);
|
||||
bdrv_set_aio_context(target_bs, aio_context);
|
||||
|
||||
blockdev_mirror_common(bs, target_bs,
|
||||
has_replaces, replaces, sync,
|
||||
has_speed, speed,
|
||||
has_granularity, granularity,
|
||||
has_buf_size, buf_size,
|
||||
has_on_source_error, on_source_error,
|
||||
has_on_target_error, on_target_error,
|
||||
true, true,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
bdrv_unref(target_bs);
|
||||
}
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
|
2
configure
vendored
2
configure
vendored
@@ -4817,7 +4817,7 @@ echo "libcap-ng support $cap_ng"
|
||||
echo "vhost-net support $vhost_net"
|
||||
echo "vhost-scsi support $vhost_scsi"
|
||||
echo "Trace backends $trace_backends"
|
||||
if test "$trace_backend" = "simple"; then
|
||||
if have_backend "simple"; then
|
||||
echo "Trace output file $trace_file-<pid>"
|
||||
fi
|
||||
if test "$spice" = "yes"; then
|
||||
|
@@ -65,7 +65,7 @@ ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
unsigned long long v;
|
||||
Error *errp = NULL;
|
||||
Error *err = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv,
|
||||
"h" /* help */
|
||||
@@ -104,11 +104,9 @@ ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'l': /* shm_size */
|
||||
parse_option_size("shm_size", optarg, &args->shm_size, &errp);
|
||||
if (errp) {
|
||||
fprintf(stderr, "cannot parse shm size: %s\n",
|
||||
error_get_pretty(errp));
|
||||
error_free(errp);
|
||||
parse_option_size("shm_size", optarg, &args->shm_size, &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
ivshmem_server_usage(argv[0], 1);
|
||||
}
|
||||
break;
|
||||
|
@@ -4,7 +4,10 @@ common-obj-$(CONFIG_ARM_DIS) += arm.o
|
||||
common-obj-$(CONFIG_ARM_A64_DIS) += arm-a64.o
|
||||
common-obj-$(CONFIG_ARM_A64_DIS) += libvixl/
|
||||
libvixldir = $(SRC_PATH)/disas/libvixl
|
||||
arm-a64.o-cflags := -I$(libvixldir)
|
||||
# The -Wno-sign-compare is needed only for gcc 4.6, which complains about
|
||||
# some signed-unsigned equality comparisons in libvixl which later gcc
|
||||
# versions do not.
|
||||
arm-a64.o-cflags := -I$(libvixldir) -Wno-sign-compare
|
||||
common-obj-$(CONFIG_CRIS_DIS) += cris.o
|
||||
common-obj-$(CONFIG_HPPA_DIS) += hppa.o
|
||||
common-obj-$(CONFIG_I386_DIS) += i386.o
|
||||
|
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "a64/disasm-a64.h"
|
||||
#include "vixl/a64/disasm-a64.h"
|
||||
|
||||
extern "C" {
|
||||
#include "disas/bfd.h"
|
||||
|
@@ -1,8 +1,11 @@
|
||||
libvixl_OBJS = utils.o \
|
||||
a64/instructions-a64.o \
|
||||
a64/decoder-a64.o \
|
||||
a64/disasm-a64.o
|
||||
libvixl_OBJS = vixl/utils.o \
|
||||
vixl/compiler-intrinsics.o \
|
||||
vixl/a64/instructions-a64.o \
|
||||
vixl/a64/decoder-a64.o \
|
||||
vixl/a64/disasm-a64.o
|
||||
|
||||
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CFLAGS)
|
||||
# The -Wno-sign-compare is needed only for gcc 4.6, which complains about
|
||||
# some signed-unsigned equality comparisons which later gcc versions do not.
|
||||
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CFLAGS) -Wno-sign-compare
|
||||
|
||||
common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS)
|
||||
|
@@ -2,11 +2,10 @@
|
||||
The code in this directory is a subset of libvixl:
|
||||
https://github.com/armvixl/vixl
|
||||
(specifically, it is the set of files needed for disassembly only,
|
||||
taken from libvixl 1.7).
|
||||
taken from libvixl 1.12).
|
||||
Bugfixes should preferably be sent upstream initially.
|
||||
|
||||
The disassembler does not currently support the entire A64 instruction
|
||||
set. Notably:
|
||||
* No Advanced SIMD support.
|
||||
* Limited support for system instructions.
|
||||
* A few miscellaneous integer and floating point instructions are missing.
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,314 +0,0 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "a64/instructions-a64.h"
|
||||
#include "a64/assembler-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
|
||||
// Floating-point infinity values.
|
||||
const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000);
|
||||
const float kFP32NegativeInfinity = rawbits_to_float(0xff800000);
|
||||
const double kFP64PositiveInfinity =
|
||||
rawbits_to_double(UINT64_C(0x7ff0000000000000));
|
||||
const double kFP64NegativeInfinity =
|
||||
rawbits_to_double(UINT64_C(0xfff0000000000000));
|
||||
|
||||
|
||||
// The default NaN values (for FPCR.DN=1).
|
||||
const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000));
|
||||
const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000);
|
||||
|
||||
|
||||
static uint64_t RotateRight(uint64_t value,
|
||||
unsigned int rotate,
|
||||
unsigned int width) {
|
||||
VIXL_ASSERT(width <= 64);
|
||||
rotate &= 63;
|
||||
return ((value & ((UINT64_C(1) << rotate) - 1)) <<
|
||||
(width - rotate)) | (value >> rotate);
|
||||
}
|
||||
|
||||
|
||||
static uint64_t RepeatBitsAcrossReg(unsigned reg_size,
|
||||
uint64_t value,
|
||||
unsigned width) {
|
||||
VIXL_ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) ||
|
||||
(width == 32));
|
||||
VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize));
|
||||
uint64_t result = value & ((UINT64_C(1) << width) - 1);
|
||||
for (unsigned i = width; i < reg_size; i *= 2) {
|
||||
result |= (result << i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsLoad() const {
|
||||
if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) {
|
||||
return Mask(LoadStorePairLBit) != 0;
|
||||
} else {
|
||||
LoadStoreOp op = static_cast<LoadStoreOp>(Mask(LoadStoreOpMask));
|
||||
switch (op) {
|
||||
case LDRB_w:
|
||||
case LDRH_w:
|
||||
case LDR_w:
|
||||
case LDR_x:
|
||||
case LDRSB_w:
|
||||
case LDRSB_x:
|
||||
case LDRSH_w:
|
||||
case LDRSH_x:
|
||||
case LDRSW_x:
|
||||
case LDR_s:
|
||||
case LDR_d: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsStore() const {
|
||||
if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) {
|
||||
return Mask(LoadStorePairLBit) == 0;
|
||||
} else {
|
||||
LoadStoreOp op = static_cast<LoadStoreOp>(Mask(LoadStoreOpMask));
|
||||
switch (op) {
|
||||
case STRB_w:
|
||||
case STRH_w:
|
||||
case STR_w:
|
||||
case STR_x:
|
||||
case STR_s:
|
||||
case STR_d: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Logical immediates can't encode zero, so a return value of zero is used to
|
||||
// indicate a failure case. Specifically, where the constraints on imm_s are
|
||||
// not met.
|
||||
uint64_t Instruction::ImmLogical() const {
|
||||
unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize;
|
||||
int64_t n = BitN();
|
||||
int64_t imm_s = ImmSetBits();
|
||||
int64_t imm_r = ImmRotate();
|
||||
|
||||
// An integer is constructed from the n, imm_s and imm_r bits according to
|
||||
// the following table:
|
||||
//
|
||||
// N imms immr size S R
|
||||
// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
|
||||
// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
|
||||
// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
|
||||
// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
|
||||
// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
|
||||
// 0 11110s xxxxxr 2 UInt(s) UInt(r)
|
||||
// (s bits must not be all set)
|
||||
//
|
||||
// A pattern is constructed of size bits, where the least significant S+1
|
||||
// bits are set. The pattern is rotated right by R, and repeated across a
|
||||
// 32 or 64-bit value, depending on destination register width.
|
||||
//
|
||||
|
||||
if (n == 1) {
|
||||
if (imm_s == 0x3F) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t bits = (UINT64_C(1) << (imm_s + 1)) - 1;
|
||||
return RotateRight(bits, imm_r, 64);
|
||||
} else {
|
||||
if ((imm_s >> 1) == 0x1F) {
|
||||
return 0;
|
||||
}
|
||||
for (int width = 0x20; width >= 0x2; width >>= 1) {
|
||||
if ((imm_s & width) == 0) {
|
||||
int mask = width - 1;
|
||||
if ((imm_s & mask) == mask) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t bits = (UINT64_C(1) << ((imm_s & mask) + 1)) - 1;
|
||||
return RepeatBitsAcrossReg(reg_size,
|
||||
RotateRight(bits, imm_r & mask, width),
|
||||
width);
|
||||
}
|
||||
}
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float Instruction::ImmFP32() const {
|
||||
// ImmFP: abcdefgh (8 bits)
|
||||
// Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = ImmFP();
|
||||
uint32_t bit7 = (bits >> 7) & 0x1;
|
||||
uint32_t bit6 = (bits >> 6) & 0x1;
|
||||
uint32_t bit5_to_0 = bits & 0x3f;
|
||||
uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19);
|
||||
|
||||
return rawbits_to_float(result);
|
||||
}
|
||||
|
||||
|
||||
double Instruction::ImmFP64() const {
|
||||
// ImmFP: abcdefgh (8 bits)
|
||||
// Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000
|
||||
// 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = ImmFP();
|
||||
uint64_t bit7 = (bits >> 7) & 0x1;
|
||||
uint64_t bit6 = (bits >> 6) & 0x1;
|
||||
uint64_t bit5_to_0 = bits & 0x3f;
|
||||
uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48);
|
||||
|
||||
return rawbits_to_double(result);
|
||||
}
|
||||
|
||||
|
||||
LSDataSize CalcLSPairDataSize(LoadStorePairOp op) {
|
||||
switch (op) {
|
||||
case STP_x:
|
||||
case LDP_x:
|
||||
case STP_d:
|
||||
case LDP_d: return LSDoubleWord;
|
||||
default: return LSWord;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Instruction* Instruction::ImmPCOffsetTarget() const {
|
||||
const Instruction * base = this;
|
||||
ptrdiff_t offset;
|
||||
if (IsPCRelAddressing()) {
|
||||
// ADR and ADRP.
|
||||
offset = ImmPCRel();
|
||||
if (Mask(PCRelAddressingMask) == ADRP) {
|
||||
base = AlignDown(base, kPageSize);
|
||||
offset *= kPageSize;
|
||||
} else {
|
||||
VIXL_ASSERT(Mask(PCRelAddressingMask) == ADR);
|
||||
}
|
||||
} else {
|
||||
// All PC-relative branches.
|
||||
VIXL_ASSERT(BranchType() != UnknownBranchType);
|
||||
// Relative branch offsets are instruction-size-aligned.
|
||||
offset = ImmBranch() << kInstructionSizeLog2;
|
||||
}
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
|
||||
inline int Instruction::ImmBranch() const {
|
||||
switch (BranchType()) {
|
||||
case CondBranchType: return ImmCondBranch();
|
||||
case UncondBranchType: return ImmUncondBranch();
|
||||
case CompareBranchType: return ImmCmpBranch();
|
||||
case TestBranchType: return ImmTestBranch();
|
||||
default: VIXL_UNREACHABLE();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetImmPCOffsetTarget(const Instruction* target) {
|
||||
if (IsPCRelAddressing()) {
|
||||
SetPCRelImmTarget(target);
|
||||
} else {
|
||||
SetBranchImmTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetPCRelImmTarget(const Instruction* target) {
|
||||
int32_t imm21;
|
||||
if ((Mask(PCRelAddressingMask) == ADR)) {
|
||||
imm21 = target - this;
|
||||
} else {
|
||||
VIXL_ASSERT(Mask(PCRelAddressingMask) == ADRP);
|
||||
uintptr_t this_page = reinterpret_cast<uintptr_t>(this) / kPageSize;
|
||||
uintptr_t target_page = reinterpret_cast<uintptr_t>(target) / kPageSize;
|
||||
imm21 = target_page - this_page;
|
||||
}
|
||||
Instr imm = Assembler::ImmPCRelAddress(imm21);
|
||||
|
||||
SetInstructionBits(Mask(~ImmPCRel_mask) | imm);
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetBranchImmTarget(const Instruction* target) {
|
||||
VIXL_ASSERT(((target - this) & 3) == 0);
|
||||
Instr branch_imm = 0;
|
||||
uint32_t imm_mask = 0;
|
||||
int offset = (target - this) >> kInstructionSizeLog2;
|
||||
switch (BranchType()) {
|
||||
case CondBranchType: {
|
||||
branch_imm = Assembler::ImmCondBranch(offset);
|
||||
imm_mask = ImmCondBranch_mask;
|
||||
break;
|
||||
}
|
||||
case UncondBranchType: {
|
||||
branch_imm = Assembler::ImmUncondBranch(offset);
|
||||
imm_mask = ImmUncondBranch_mask;
|
||||
break;
|
||||
}
|
||||
case CompareBranchType: {
|
||||
branch_imm = Assembler::ImmCmpBranch(offset);
|
||||
imm_mask = ImmCmpBranch_mask;
|
||||
break;
|
||||
}
|
||||
case TestBranchType: {
|
||||
branch_imm = Assembler::ImmTestBranch(offset);
|
||||
imm_mask = ImmTestBranch_mask;
|
||||
break;
|
||||
}
|
||||
default: VIXL_UNREACHABLE();
|
||||
}
|
||||
SetInstructionBits(Mask(~imm_mask) | branch_imm);
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetImmLLiteral(const Instruction* source) {
|
||||
VIXL_ASSERT(IsWordAligned(source));
|
||||
ptrdiff_t offset = (source - this) >> kLiteralEntrySizeLog2;
|
||||
Instr imm = Assembler::ImmLLiteral(offset);
|
||||
Instr mask = ImmLLiteral_mask;
|
||||
|
||||
SetInstructionBits(Mask(~mask) | imm);
|
||||
}
|
||||
} // namespace vixl
|
||||
|
@@ -1,384 +0,0 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_A64_INSTRUCTIONS_A64_H_
|
||||
#define VIXL_A64_INSTRUCTIONS_A64_H_
|
||||
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "a64/constants-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
// ISA constants. --------------------------------------------------------------
|
||||
|
||||
typedef uint32_t Instr;
|
||||
const unsigned kInstructionSize = 4;
|
||||
const unsigned kInstructionSizeLog2 = 2;
|
||||
const unsigned kLiteralEntrySize = 4;
|
||||
const unsigned kLiteralEntrySizeLog2 = 2;
|
||||
const unsigned kMaxLoadLiteralRange = 1 * MBytes;
|
||||
|
||||
// This is the nominal page size (as used by the adrp instruction); the actual
|
||||
// size of the memory pages allocated by the kernel is likely to differ.
|
||||
const unsigned kPageSize = 4 * KBytes;
|
||||
const unsigned kPageSizeLog2 = 12;
|
||||
|
||||
const unsigned kWRegSize = 32;
|
||||
const unsigned kWRegSizeLog2 = 5;
|
||||
const unsigned kWRegSizeInBytes = kWRegSize / 8;
|
||||
const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3;
|
||||
const unsigned kXRegSize = 64;
|
||||
const unsigned kXRegSizeLog2 = 6;
|
||||
const unsigned kXRegSizeInBytes = kXRegSize / 8;
|
||||
const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3;
|
||||
const unsigned kSRegSize = 32;
|
||||
const unsigned kSRegSizeLog2 = 5;
|
||||
const unsigned kSRegSizeInBytes = kSRegSize / 8;
|
||||
const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3;
|
||||
const unsigned kDRegSize = 64;
|
||||
const unsigned kDRegSizeLog2 = 6;
|
||||
const unsigned kDRegSizeInBytes = kDRegSize / 8;
|
||||
const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3;
|
||||
const uint64_t kWRegMask = UINT64_C(0xffffffff);
|
||||
const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff);
|
||||
const uint64_t kSRegMask = UINT64_C(0xffffffff);
|
||||
const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff);
|
||||
const uint64_t kSSignMask = UINT64_C(0x80000000);
|
||||
const uint64_t kDSignMask = UINT64_C(0x8000000000000000);
|
||||
const uint64_t kWSignMask = UINT64_C(0x80000000);
|
||||
const uint64_t kXSignMask = UINT64_C(0x8000000000000000);
|
||||
const uint64_t kByteMask = UINT64_C(0xff);
|
||||
const uint64_t kHalfWordMask = UINT64_C(0xffff);
|
||||
const uint64_t kWordMask = UINT64_C(0xffffffff);
|
||||
const uint64_t kXMaxUInt = UINT64_C(0xffffffffffffffff);
|
||||
const uint64_t kWMaxUInt = UINT64_C(0xffffffff);
|
||||
const int64_t kXMaxInt = INT64_C(0x7fffffffffffffff);
|
||||
const int64_t kXMinInt = INT64_C(0x8000000000000000);
|
||||
const int32_t kWMaxInt = INT32_C(0x7fffffff);
|
||||
const int32_t kWMinInt = INT32_C(0x80000000);
|
||||
const unsigned kLinkRegCode = 30;
|
||||
const unsigned kZeroRegCode = 31;
|
||||
const unsigned kSPRegInternalCode = 63;
|
||||
const unsigned kRegCodeMask = 0x1f;
|
||||
|
||||
const unsigned kAddressTagOffset = 56;
|
||||
const unsigned kAddressTagWidth = 8;
|
||||
const uint64_t kAddressTagMask =
|
||||
((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset;
|
||||
VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000));
|
||||
|
||||
// AArch64 floating-point specifics. These match IEEE-754.
|
||||
const unsigned kDoubleMantissaBits = 52;
|
||||
const unsigned kDoubleExponentBits = 11;
|
||||
const unsigned kFloatMantissaBits = 23;
|
||||
const unsigned kFloatExponentBits = 8;
|
||||
|
||||
// Floating-point infinity values.
|
||||
extern const float kFP32PositiveInfinity;
|
||||
extern const float kFP32NegativeInfinity;
|
||||
extern const double kFP64PositiveInfinity;
|
||||
extern const double kFP64NegativeInfinity;
|
||||
|
||||
// The default NaN values (for FPCR.DN=1).
|
||||
extern const double kFP64DefaultNaN;
|
||||
extern const float kFP32DefaultNaN;
|
||||
|
||||
|
||||
enum LSDataSize {
|
||||
LSByte = 0,
|
||||
LSHalfword = 1,
|
||||
LSWord = 2,
|
||||
LSDoubleWord = 3
|
||||
};
|
||||
|
||||
LSDataSize CalcLSPairDataSize(LoadStorePairOp op);
|
||||
|
||||
enum ImmBranchType {
|
||||
UnknownBranchType = 0,
|
||||
CondBranchType = 1,
|
||||
UncondBranchType = 2,
|
||||
CompareBranchType = 3,
|
||||
TestBranchType = 4
|
||||
};
|
||||
|
||||
enum AddrMode {
|
||||
Offset,
|
||||
PreIndex,
|
||||
PostIndex
|
||||
};
|
||||
|
||||
enum FPRounding {
|
||||
// The first four values are encodable directly by FPCR<RMode>.
|
||||
FPTieEven = 0x0,
|
||||
FPPositiveInfinity = 0x1,
|
||||
FPNegativeInfinity = 0x2,
|
||||
FPZero = 0x3,
|
||||
|
||||
// The final rounding mode is only available when explicitly specified by the
|
||||
// instruction (such as with fcvta). It cannot be set in FPCR.
|
||||
FPTieAway
|
||||
};
|
||||
|
||||
enum Reg31Mode {
|
||||
Reg31IsStackPointer,
|
||||
Reg31IsZeroRegister
|
||||
};
|
||||
|
||||
// Instructions. ---------------------------------------------------------------
|
||||
|
||||
class Instruction {
|
||||
public:
|
||||
Instr InstructionBits() const {
|
||||
return *(reinterpret_cast<const Instr*>(this));
|
||||
}
|
||||
|
||||
void SetInstructionBits(Instr new_instr) {
|
||||
*(reinterpret_cast<Instr*>(this)) = new_instr;
|
||||
}
|
||||
|
||||
int Bit(int pos) const {
|
||||
return (InstructionBits() >> pos) & 1;
|
||||
}
|
||||
|
||||
uint32_t Bits(int msb, int lsb) const {
|
||||
return unsigned_bitextract_32(msb, lsb, InstructionBits());
|
||||
}
|
||||
|
||||
int32_t SignedBits(int msb, int lsb) const {
|
||||
int32_t bits = *(reinterpret_cast<const int32_t*>(this));
|
||||
return signed_bitextract_32(msb, lsb, bits);
|
||||
}
|
||||
|
||||
Instr Mask(uint32_t mask) const {
|
||||
return InstructionBits() & mask;
|
||||
}
|
||||
|
||||
#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
|
||||
int64_t Name() const { return Func(HighBit, LowBit); }
|
||||
INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
|
||||
#undef DEFINE_GETTER
|
||||
|
||||
// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
|
||||
// formed from ImmPCRelLo and ImmPCRelHi.
|
||||
int ImmPCRel() const {
|
||||
int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
|
||||
int const width = ImmPCRelLo_width + ImmPCRelHi_width;
|
||||
return signed_bitextract_32(width-1, 0, offset);
|
||||
}
|
||||
|
||||
uint64_t ImmLogical() const;
|
||||
float ImmFP32() const;
|
||||
double ImmFP64() const;
|
||||
|
||||
LSDataSize SizeLSPair() const {
|
||||
return CalcLSPairDataSize(
|
||||
static_cast<LoadStorePairOp>(Mask(LoadStorePairMask)));
|
||||
}
|
||||
|
||||
// Helpers.
|
||||
bool IsCondBranchImm() const {
|
||||
return Mask(ConditionalBranchFMask) == ConditionalBranchFixed;
|
||||
}
|
||||
|
||||
bool IsUncondBranchImm() const {
|
||||
return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed;
|
||||
}
|
||||
|
||||
bool IsCompareBranch() const {
|
||||
return Mask(CompareBranchFMask) == CompareBranchFixed;
|
||||
}
|
||||
|
||||
bool IsTestBranch() const {
|
||||
return Mask(TestBranchFMask) == TestBranchFixed;
|
||||
}
|
||||
|
||||
bool IsPCRelAddressing() const {
|
||||
return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
|
||||
}
|
||||
|
||||
bool IsLogicalImmediate() const {
|
||||
return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
|
||||
}
|
||||
|
||||
bool IsAddSubImmediate() const {
|
||||
return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
|
||||
}
|
||||
|
||||
bool IsAddSubExtended() const {
|
||||
return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
|
||||
}
|
||||
|
||||
bool IsLoadOrStore() const {
|
||||
return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
|
||||
}
|
||||
|
||||
bool IsLoad() const;
|
||||
bool IsStore() const;
|
||||
|
||||
bool IsLoadLiteral() const {
|
||||
// This includes PRFM_lit.
|
||||
return Mask(LoadLiteralFMask) == LoadLiteralFixed;
|
||||
}
|
||||
|
||||
bool IsMovn() const {
|
||||
return (Mask(MoveWideImmediateMask) == MOVN_x) ||
|
||||
(Mask(MoveWideImmediateMask) == MOVN_w);
|
||||
}
|
||||
|
||||
// Indicate whether Rd can be the stack pointer or the zero register. This
|
||||
// does not check that the instruction actually has an Rd field.
|
||||
Reg31Mode RdMode() const {
|
||||
// The following instructions use sp or wsp as Rd:
|
||||
// Add/sub (immediate) when not setting the flags.
|
||||
// Add/sub (extended) when not setting the flags.
|
||||
// Logical (immediate) when not setting the flags.
|
||||
// Otherwise, r31 is the zero register.
|
||||
if (IsAddSubImmediate() || IsAddSubExtended()) {
|
||||
if (Mask(AddSubSetFlagsBit)) {
|
||||
return Reg31IsZeroRegister;
|
||||
} else {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
}
|
||||
if (IsLogicalImmediate()) {
|
||||
// Of the logical (immediate) instructions, only ANDS (and its aliases)
|
||||
// can set the flags. The others can all write into sp.
|
||||
// Note that some logical operations are not available to
|
||||
// immediate-operand instructions, so we have to combine two masks here.
|
||||
if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) {
|
||||
return Reg31IsZeroRegister;
|
||||
} else {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
}
|
||||
return Reg31IsZeroRegister;
|
||||
}
|
||||
|
||||
// Indicate whether Rn can be the stack pointer or the zero register. This
|
||||
// does not check that the instruction actually has an Rn field.
|
||||
Reg31Mode RnMode() const {
|
||||
// The following instructions use sp or wsp as Rn:
|
||||
// All loads and stores.
|
||||
// Add/sub (immediate).
|
||||
// Add/sub (extended).
|
||||
// Otherwise, r31 is the zero register.
|
||||
if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
return Reg31IsZeroRegister;
|
||||
}
|
||||
|
||||
ImmBranchType BranchType() const {
|
||||
if (IsCondBranchImm()) {
|
||||
return CondBranchType;
|
||||
} else if (IsUncondBranchImm()) {
|
||||
return UncondBranchType;
|
||||
} else if (IsCompareBranch()) {
|
||||
return CompareBranchType;
|
||||
} else if (IsTestBranch()) {
|
||||
return TestBranchType;
|
||||
} else {
|
||||
return UnknownBranchType;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the target of this instruction. 'this' may be a branch or a
|
||||
// PC-relative addressing instruction.
|
||||
const Instruction* ImmPCOffsetTarget() const;
|
||||
|
||||
// Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
|
||||
// a PC-relative addressing instruction.
|
||||
void SetImmPCOffsetTarget(const Instruction* target);
|
||||
// Patch a literal load instruction to load from 'source'.
|
||||
void SetImmLLiteral(const Instruction* source);
|
||||
|
||||
// Calculate the address of a literal referred to by a load-literal
|
||||
// instruction, and return it as the specified type.
|
||||
//
|
||||
// The literal itself is safely mutable only if the backing buffer is safely
|
||||
// mutable.
|
||||
template <typename T>
|
||||
T LiteralAddress() const {
|
||||
uint64_t base_raw = reinterpret_cast<uintptr_t>(this);
|
||||
ptrdiff_t offset = ImmLLiteral() << kLiteralEntrySizeLog2;
|
||||
uint64_t address_raw = base_raw + offset;
|
||||
|
||||
// Cast the address using a C-style cast. A reinterpret_cast would be
|
||||
// appropriate, but it can't cast one integral type to another.
|
||||
T address = (T)(address_raw);
|
||||
|
||||
// Assert that the address can be represented by the specified type.
|
||||
VIXL_ASSERT((uint64_t)(address) == address_raw);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
uint32_t Literal32() const {
|
||||
uint32_t literal;
|
||||
memcpy(&literal, LiteralAddress<const void*>(), sizeof(literal));
|
||||
return literal;
|
||||
}
|
||||
|
||||
uint64_t Literal64() const {
|
||||
uint64_t literal;
|
||||
memcpy(&literal, LiteralAddress<const void*>(), sizeof(literal));
|
||||
return literal;
|
||||
}
|
||||
|
||||
float LiteralFP32() const {
|
||||
return rawbits_to_float(Literal32());
|
||||
}
|
||||
|
||||
double LiteralFP64() const {
|
||||
return rawbits_to_double(Literal64());
|
||||
}
|
||||
|
||||
const Instruction* NextInstruction() const {
|
||||
return this + kInstructionSize;
|
||||
}
|
||||
|
||||
const Instruction* InstructionAtOffset(int64_t offset) const {
|
||||
VIXL_ASSERT(IsWordAligned(this + offset));
|
||||
return this + offset;
|
||||
}
|
||||
|
||||
template<typename T> static Instruction* Cast(T src) {
|
||||
return reinterpret_cast<Instruction*>(src);
|
||||
}
|
||||
|
||||
template<typename T> static const Instruction* CastConst(T src) {
|
||||
return reinterpret_cast<const Instruction*>(src);
|
||||
}
|
||||
|
||||
private:
|
||||
int ImmBranch() const;
|
||||
|
||||
void SetPCRelImmTarget(const Instruction* target);
|
||||
void SetBranchImmTarget(const Instruction* target);
|
||||
};
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_A64_INSTRUCTIONS_A64_H_
|
4624
disas/libvixl/vixl/a64/assembler-a64.h
Normal file
4624
disas/libvixl/vixl/a64/assembler-a64.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// Copyright 2014, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,8 +27,8 @@
|
||||
#ifndef VIXL_CPU_A64_H
|
||||
#define VIXL_CPU_A64_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "instructions-a64.h"
|
||||
#include "vixl/globals.h"
|
||||
#include "vixl/a64/instructions-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// Copyright 2014, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -24,9 +24,9 @@
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "a64/decoder-a64.h"
|
||||
#include "vixl/globals.h"
|
||||
#include "vixl/utils.h"
|
||||
#include "vixl/a64/decoder-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
@@ -271,6 +271,11 @@ void Decoder::DecodeLoadStore(const Instruction* instr) {
|
||||
(instr->Bits(27, 24) == 0x9) ||
|
||||
(instr->Bits(27, 24) == 0xC) ||
|
||||
(instr->Bits(27, 24) == 0xD) );
|
||||
// TODO(all): rearrange the tree to integrate this branch.
|
||||
if ((instr->Bit(28) == 0) && (instr->Bit(29) == 0) && (instr->Bit(26) == 1)) {
|
||||
DecodeNEONLoadStore(instr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (instr->Bit(24) == 0) {
|
||||
if (instr->Bit(28) == 0) {
|
||||
@@ -278,7 +283,7 @@ void Decoder::DecodeLoadStore(const Instruction* instr) {
|
||||
if (instr->Bit(26) == 0) {
|
||||
VisitLoadStoreExclusive(instr);
|
||||
} else {
|
||||
DecodeAdvSIMDLoadStore(instr);
|
||||
VIXL_UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
if ((instr->Bits(31, 30) == 0x3) ||
|
||||
@@ -483,6 +488,7 @@ void Decoder::DecodeDataProcessing(const Instruction* instr) {
|
||||
case 6: {
|
||||
if (instr->Bit(29) == 0x1) {
|
||||
VisitUnallocated(instr);
|
||||
VIXL_FALLTHROUGH();
|
||||
} else {
|
||||
if (instr->Bit(30) == 0) {
|
||||
if ((instr->Bit(15) == 0x1) ||
|
||||
@@ -556,18 +562,15 @@ void Decoder::DecodeDataProcessing(const Instruction* instr) {
|
||||
void Decoder::DecodeFP(const Instruction* instr) {
|
||||
VIXL_ASSERT((instr->Bits(27, 24) == 0xE) ||
|
||||
(instr->Bits(27, 24) == 0xF));
|
||||
|
||||
if (instr->Bit(28) == 0) {
|
||||
DecodeAdvSIMDDataProcessing(instr);
|
||||
DecodeNEONVectorDataProcessing(instr);
|
||||
} else {
|
||||
if (instr->Bit(29) == 1) {
|
||||
if (instr->Bits(31, 30) == 0x3) {
|
||||
VisitUnallocated(instr);
|
||||
} else if (instr->Bits(31, 30) == 0x1) {
|
||||
DecodeNEONScalarDataProcessing(instr);
|
||||
} else {
|
||||
if (instr->Bits(31, 30) == 0x3) {
|
||||
VisitUnallocated(instr);
|
||||
} else if (instr->Bits(31, 30) == 0x1) {
|
||||
DecodeAdvSIMDDataProcessing(instr);
|
||||
} else {
|
||||
if (instr->Bit(29) == 0) {
|
||||
if (instr->Bit(24) == 0) {
|
||||
if (instr->Bit(21) == 0) {
|
||||
if ((instr->Bit(23) == 1) ||
|
||||
@@ -674,23 +677,190 @@ void Decoder::DecodeFP(const Instruction* instr) {
|
||||
VisitFPDataProcessing3Source(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeAdvSIMDLoadStore(const Instruction* instr) {
|
||||
// TODO: Implement Advanced SIMD load/store instruction decode.
|
||||
void Decoder::DecodeNEONLoadStore(const Instruction* instr) {
|
||||
VIXL_ASSERT(instr->Bits(29, 25) == 0x6);
|
||||
VisitUnimplemented(instr);
|
||||
if (instr->Bit(31) == 0) {
|
||||
if ((instr->Bit(24) == 0) && (instr->Bit(21) == 1)) {
|
||||
VisitUnallocated(instr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (instr->Bit(23) == 0) {
|
||||
if (instr->Bits(20, 16) == 0) {
|
||||
if (instr->Bit(24) == 0) {
|
||||
VisitNEONLoadStoreMultiStruct(instr);
|
||||
} else {
|
||||
VisitNEONLoadStoreSingleStruct(instr);
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(24) == 0) {
|
||||
VisitNEONLoadStoreMultiStructPostIndex(instr);
|
||||
} else {
|
||||
VisitNEONLoadStoreSingleStructPostIndex(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeAdvSIMDDataProcessing(const Instruction* instr) {
|
||||
// TODO: Implement Advanced SIMD data processing instruction decode.
|
||||
VIXL_ASSERT(instr->Bits(27, 25) == 0x7);
|
||||
VisitUnimplemented(instr);
|
||||
void Decoder::DecodeNEONVectorDataProcessing(const Instruction* instr) {
|
||||
VIXL_ASSERT(instr->Bits(28, 25) == 0x7);
|
||||
if (instr->Bit(31) == 0) {
|
||||
if (instr->Bit(24) == 0) {
|
||||
if (instr->Bit(21) == 0) {
|
||||
if (instr->Bit(15) == 0) {
|
||||
if (instr->Bit(10) == 0) {
|
||||
if (instr->Bit(29) == 0) {
|
||||
if (instr->Bit(11) == 0) {
|
||||
VisitNEONTable(instr);
|
||||
} else {
|
||||
VisitNEONPerm(instr);
|
||||
}
|
||||
} else {
|
||||
VisitNEONExtract(instr);
|
||||
}
|
||||
} else {
|
||||
if (instr->Bits(23, 22) == 0) {
|
||||
VisitNEONCopy(instr);
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(10) == 0) {
|
||||
if (instr->Bit(11) == 0) {
|
||||
VisitNEON3Different(instr);
|
||||
} else {
|
||||
if (instr->Bits(18, 17) == 0) {
|
||||
if (instr->Bit(20) == 0) {
|
||||
if (instr->Bit(19) == 0) {
|
||||
VisitNEON2RegMisc(instr);
|
||||
} else {
|
||||
if (instr->Bits(30, 29) == 0x2) {
|
||||
VisitCryptoAES(instr);
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(19) == 0) {
|
||||
VisitNEONAcrossLanes(instr);
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitNEON3Same(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(10) == 0) {
|
||||
VisitNEONByIndexedElement(instr);
|
||||
} else {
|
||||
if (instr->Bit(23) == 0) {
|
||||
if (instr->Bits(22, 19) == 0) {
|
||||
VisitNEONModifiedImmediate(instr);
|
||||
} else {
|
||||
VisitNEONShiftImmediate(instr);
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeNEONScalarDataProcessing(const Instruction* instr) {
|
||||
VIXL_ASSERT(instr->Bits(28, 25) == 0xF);
|
||||
if (instr->Bit(24) == 0) {
|
||||
if (instr->Bit(21) == 0) {
|
||||
if (instr->Bit(15) == 0) {
|
||||
if (instr->Bit(10) == 0) {
|
||||
if (instr->Bit(29) == 0) {
|
||||
if (instr->Bit(11) == 0) {
|
||||
VisitCrypto3RegSHA(instr);
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
} else {
|
||||
if (instr->Bits(23, 22) == 0) {
|
||||
VisitNEONScalarCopy(instr);
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(10) == 0) {
|
||||
if (instr->Bit(11) == 0) {
|
||||
VisitNEONScalar3Diff(instr);
|
||||
} else {
|
||||
if (instr->Bits(18, 17) == 0) {
|
||||
if (instr->Bit(20) == 0) {
|
||||
if (instr->Bit(19) == 0) {
|
||||
VisitNEONScalar2RegMisc(instr);
|
||||
} else {
|
||||
if (instr->Bit(29) == 0) {
|
||||
VisitCrypto2RegSHA(instr);
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(19) == 0) {
|
||||
VisitNEONScalarPairwise(instr);
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VisitNEONScalar3Same(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(10) == 0) {
|
||||
VisitNEONScalarByIndexedElement(instr);
|
||||
} else {
|
||||
if (instr->Bit(23) == 0) {
|
||||
VisitNEONScalarShiftImmediate(instr);
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// Copyright 2014, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -29,13 +29,13 @@
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "globals.h"
|
||||
#include "a64/instructions-a64.h"
|
||||
#include "vixl/globals.h"
|
||||
#include "vixl/a64/instructions-a64.h"
|
||||
|
||||
|
||||
// List macro containing all visitors needed by the decoder class.
|
||||
|
||||
#define VISITOR_LIST(V) \
|
||||
#define VISITOR_LIST_THAT_RETURN(V) \
|
||||
V(PCRelAddressing) \
|
||||
V(AddSubImmediate) \
|
||||
V(LogicalImmediate) \
|
||||
@@ -79,8 +79,39 @@
|
||||
V(FPDataProcessing3Source) \
|
||||
V(FPIntegerConvert) \
|
||||
V(FPFixedPointConvert) \
|
||||
V(Unallocated) \
|
||||
V(Unimplemented)
|
||||
V(Crypto2RegSHA) \
|
||||
V(Crypto3RegSHA) \
|
||||
V(CryptoAES) \
|
||||
V(NEON2RegMisc) \
|
||||
V(NEON3Different) \
|
||||
V(NEON3Same) \
|
||||
V(NEONAcrossLanes) \
|
||||
V(NEONByIndexedElement) \
|
||||
V(NEONCopy) \
|
||||
V(NEONExtract) \
|
||||
V(NEONLoadStoreMultiStruct) \
|
||||
V(NEONLoadStoreMultiStructPostIndex) \
|
||||
V(NEONLoadStoreSingleStruct) \
|
||||
V(NEONLoadStoreSingleStructPostIndex) \
|
||||
V(NEONModifiedImmediate) \
|
||||
V(NEONScalar2RegMisc) \
|
||||
V(NEONScalar3Diff) \
|
||||
V(NEONScalar3Same) \
|
||||
V(NEONScalarByIndexedElement) \
|
||||
V(NEONScalarCopy) \
|
||||
V(NEONScalarPairwise) \
|
||||
V(NEONScalarShiftImmediate) \
|
||||
V(NEONShiftImmediate) \
|
||||
V(NEONTable) \
|
||||
V(NEONPerm) \
|
||||
|
||||
#define VISITOR_LIST_THAT_DONT_RETURN(V) \
|
||||
V(Unallocated) \
|
||||
V(Unimplemented) \
|
||||
|
||||
#define VISITOR_LIST(V) \
|
||||
VISITOR_LIST_THAT_RETURN(V) \
|
||||
VISITOR_LIST_THAT_DONT_RETURN(V) \
|
||||
|
||||
namespace vixl {
|
||||
|
||||
@@ -222,12 +253,17 @@ class Decoder {
|
||||
// Decode the Advanced SIMD (NEON) load/store part of the instruction tree,
|
||||
// and call the corresponding visitors.
|
||||
// On entry, instruction bits 29:25 = 0x6.
|
||||
void DecodeAdvSIMDLoadStore(const Instruction* instr);
|
||||
void DecodeNEONLoadStore(const Instruction* instr);
|
||||
|
||||
// Decode the Advanced SIMD (NEON) data processing part of the instruction
|
||||
// tree, and call the corresponding visitors.
|
||||
// On entry, instruction bits 27:25 = 0x7.
|
||||
void DecodeAdvSIMDDataProcessing(const Instruction* instr);
|
||||
// Decode the Advanced SIMD (NEON) vector data processing part of the
|
||||
// instruction tree, and call the corresponding visitors.
|
||||
// On entry, instruction bits 28:25 = 0x7.
|
||||
void DecodeNEONVectorDataProcessing(const Instruction* instr);
|
||||
|
||||
// Decode the Advanced SIMD (NEON) scalar data processing part of the
|
||||
// instruction tree, and call the corresponding visitors.
|
||||
// On entry, instruction bits 28:25 = 0xF.
|
||||
void DecodeNEONScalarDataProcessing(const Instruction* instr);
|
||||
|
||||
private:
|
||||
// Visitors are registered in a list.
|
3487
disas/libvixl/vixl/a64/disasm-a64.cc
Normal file
3487
disas/libvixl/vixl/a64/disasm-a64.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,11 +27,11 @@
|
||||
#ifndef VIXL_A64_DISASM_A64_H
|
||||
#define VIXL_A64_DISASM_A64_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "instructions-a64.h"
|
||||
#include "decoder-a64.h"
|
||||
#include "assembler-a64.h"
|
||||
#include "vixl/globals.h"
|
||||
#include "vixl/utils.h"
|
||||
#include "vixl/a64/instructions-a64.h"
|
||||
#include "vixl/a64/decoder-a64.h"
|
||||
#include "vixl/a64/assembler-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
@@ -55,6 +55,7 @@ class Disassembler: public DecoderVisitor {
|
||||
// customize the disassembly output.
|
||||
|
||||
// Prints the name of a register.
|
||||
// TODO: This currently doesn't allow renaming of V registers.
|
||||
virtual void AppendRegisterNameToOutput(const Instruction* instr,
|
||||
const CPURegister& reg);
|
||||
|
||||
@@ -122,7 +123,8 @@ class Disassembler: public DecoderVisitor {
|
||||
int SubstituteLSRegOffsetField(const Instruction* instr, const char* format);
|
||||
int SubstitutePrefetchField(const Instruction* instr, const char* format);
|
||||
int SubstituteBarrierField(const Instruction* instr, const char* format);
|
||||
|
||||
int SubstituteSysOpField(const Instruction* instr, const char* format);
|
||||
int SubstituteCrField(const Instruction* instr, const char* format);
|
||||
bool RdIsZROrSP(const Instruction* instr) const {
|
||||
return (instr->Rd() == kZeroRegCode);
|
||||
}
|
||||
@@ -163,7 +165,6 @@ class Disassembler: public DecoderVisitor {
|
||||
class PrintDisassembler: public Disassembler {
|
||||
public:
|
||||
explicit PrintDisassembler(FILE* stream) : stream_(stream) { }
|
||||
virtual ~PrintDisassembler() { }
|
||||
|
||||
protected:
|
||||
virtual void ProcessOutput(const Instruction* instr);
|
622
disas/libvixl/vixl/a64/instructions-a64.cc
Normal file
622
disas/libvixl/vixl/a64/instructions-a64.cc
Normal file
@@ -0,0 +1,622 @@
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "vixl/a64/instructions-a64.h"
|
||||
#include "vixl/a64/assembler-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
|
||||
// Floating-point infinity values.
|
||||
const float16 kFP16PositiveInfinity = 0x7c00;
|
||||
const float16 kFP16NegativeInfinity = 0xfc00;
|
||||
const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000);
|
||||
const float kFP32NegativeInfinity = rawbits_to_float(0xff800000);
|
||||
const double kFP64PositiveInfinity =
|
||||
rawbits_to_double(UINT64_C(0x7ff0000000000000));
|
||||
const double kFP64NegativeInfinity =
|
||||
rawbits_to_double(UINT64_C(0xfff0000000000000));
|
||||
|
||||
|
||||
// The default NaN values (for FPCR.DN=1).
|
||||
const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000));
|
||||
const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000);
|
||||
const float16 kFP16DefaultNaN = 0x7e00;
|
||||
|
||||
|
||||
static uint64_t RotateRight(uint64_t value,
|
||||
unsigned int rotate,
|
||||
unsigned int width) {
|
||||
VIXL_ASSERT(width <= 64);
|
||||
rotate &= 63;
|
||||
return ((value & ((UINT64_C(1) << rotate) - 1)) <<
|
||||
(width - rotate)) | (value >> rotate);
|
||||
}
|
||||
|
||||
|
||||
static uint64_t RepeatBitsAcrossReg(unsigned reg_size,
|
||||
uint64_t value,
|
||||
unsigned width) {
|
||||
VIXL_ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) ||
|
||||
(width == 32));
|
||||
VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize));
|
||||
uint64_t result = value & ((UINT64_C(1) << width) - 1);
|
||||
for (unsigned i = width; i < reg_size; i *= 2) {
|
||||
result |= (result << i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsLoad() const {
|
||||
if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) {
|
||||
return Mask(LoadStorePairLBit) != 0;
|
||||
} else {
|
||||
LoadStoreOp op = static_cast<LoadStoreOp>(Mask(LoadStoreMask));
|
||||
switch (op) {
|
||||
case LDRB_w:
|
||||
case LDRH_w:
|
||||
case LDR_w:
|
||||
case LDR_x:
|
||||
case LDRSB_w:
|
||||
case LDRSB_x:
|
||||
case LDRSH_w:
|
||||
case LDRSH_x:
|
||||
case LDRSW_x:
|
||||
case LDR_b:
|
||||
case LDR_h:
|
||||
case LDR_s:
|
||||
case LDR_d:
|
||||
case LDR_q: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsStore() const {
|
||||
if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) {
|
||||
return Mask(LoadStorePairLBit) == 0;
|
||||
} else {
|
||||
LoadStoreOp op = static_cast<LoadStoreOp>(Mask(LoadStoreMask));
|
||||
switch (op) {
|
||||
case STRB_w:
|
||||
case STRH_w:
|
||||
case STR_w:
|
||||
case STR_x:
|
||||
case STR_b:
|
||||
case STR_h:
|
||||
case STR_s:
|
||||
case STR_d:
|
||||
case STR_q: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Logical immediates can't encode zero, so a return value of zero is used to
|
||||
// indicate a failure case. Specifically, where the constraints on imm_s are
|
||||
// not met.
|
||||
uint64_t Instruction::ImmLogical() const {
|
||||
unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize;
|
||||
int32_t n = BitN();
|
||||
int32_t imm_s = ImmSetBits();
|
||||
int32_t imm_r = ImmRotate();
|
||||
|
||||
// An integer is constructed from the n, imm_s and imm_r bits according to
|
||||
// the following table:
|
||||
//
|
||||
// N imms immr size S R
|
||||
// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
|
||||
// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
|
||||
// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
|
||||
// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
|
||||
// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
|
||||
// 0 11110s xxxxxr 2 UInt(s) UInt(r)
|
||||
// (s bits must not be all set)
|
||||
//
|
||||
// A pattern is constructed of size bits, where the least significant S+1
|
||||
// bits are set. The pattern is rotated right by R, and repeated across a
|
||||
// 32 or 64-bit value, depending on destination register width.
|
||||
//
|
||||
|
||||
if (n == 1) {
|
||||
if (imm_s == 0x3f) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t bits = (UINT64_C(1) << (imm_s + 1)) - 1;
|
||||
return RotateRight(bits, imm_r, 64);
|
||||
} else {
|
||||
if ((imm_s >> 1) == 0x1f) {
|
||||
return 0;
|
||||
}
|
||||
for (int width = 0x20; width >= 0x2; width >>= 1) {
|
||||
if ((imm_s & width) == 0) {
|
||||
int mask = width - 1;
|
||||
if ((imm_s & mask) == mask) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t bits = (UINT64_C(1) << ((imm_s & mask) + 1)) - 1;
|
||||
return RepeatBitsAcrossReg(reg_size,
|
||||
RotateRight(bits, imm_r & mask, width),
|
||||
width);
|
||||
}
|
||||
}
|
||||
}
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Instruction::ImmNEONabcdefgh() const {
|
||||
return ImmNEONabc() << 5 | ImmNEONdefgh();
|
||||
}
|
||||
|
||||
|
||||
float Instruction::Imm8ToFP32(uint32_t imm8) {
|
||||
// Imm8: abcdefgh (8 bits)
|
||||
// Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = imm8;
|
||||
uint32_t bit7 = (bits >> 7) & 0x1;
|
||||
uint32_t bit6 = (bits >> 6) & 0x1;
|
||||
uint32_t bit5_to_0 = bits & 0x3f;
|
||||
uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19);
|
||||
|
||||
return rawbits_to_float(result);
|
||||
}
|
||||
|
||||
|
||||
float Instruction::ImmFP32() const {
|
||||
return Imm8ToFP32(ImmFP());
|
||||
}
|
||||
|
||||
|
||||
double Instruction::Imm8ToFP64(uint32_t imm8) {
|
||||
// Imm8: abcdefgh (8 bits)
|
||||
// Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000
|
||||
// 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = imm8;
|
||||
uint64_t bit7 = (bits >> 7) & 0x1;
|
||||
uint64_t bit6 = (bits >> 6) & 0x1;
|
||||
uint64_t bit5_to_0 = bits & 0x3f;
|
||||
uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48);
|
||||
|
||||
return rawbits_to_double(result);
|
||||
}
|
||||
|
||||
|
||||
double Instruction::ImmFP64() const {
|
||||
return Imm8ToFP64(ImmFP());
|
||||
}
|
||||
|
||||
|
||||
float Instruction::ImmNEONFP32() const {
|
||||
return Imm8ToFP32(ImmNEONabcdefgh());
|
||||
}
|
||||
|
||||
|
||||
double Instruction::ImmNEONFP64() const {
|
||||
return Imm8ToFP64(ImmNEONabcdefgh());
|
||||
}
|
||||
|
||||
|
||||
unsigned CalcLSDataSize(LoadStoreOp op) {
|
||||
VIXL_ASSERT((LSSize_offset + LSSize_width) == (kInstructionSize * 8));
|
||||
unsigned size = static_cast<Instr>(op) >> LSSize_offset;
|
||||
if ((op & LSVector_mask) != 0) {
|
||||
// Vector register memory operations encode the access size in the "size"
|
||||
// and "opc" fields.
|
||||
if ((size == 0) && ((op & LSOpc_mask) >> LSOpc_offset) >= 2) {
|
||||
size = kQRegSizeInBytesLog2;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
unsigned CalcLSPairDataSize(LoadStorePairOp op) {
|
||||
VIXL_STATIC_ASSERT(kXRegSizeInBytes == kDRegSizeInBytes);
|
||||
VIXL_STATIC_ASSERT(kWRegSizeInBytes == kSRegSizeInBytes);
|
||||
switch (op) {
|
||||
case STP_q:
|
||||
case LDP_q: return kQRegSizeInBytesLog2;
|
||||
case STP_x:
|
||||
case LDP_x:
|
||||
case STP_d:
|
||||
case LDP_d: return kXRegSizeInBytesLog2;
|
||||
default: return kWRegSizeInBytesLog2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Instruction::ImmBranchRangeBitwidth(ImmBranchType branch_type) {
|
||||
switch (branch_type) {
|
||||
case UncondBranchType:
|
||||
return ImmUncondBranch_width;
|
||||
case CondBranchType:
|
||||
return ImmCondBranch_width;
|
||||
case CompareBranchType:
|
||||
return ImmCmpBranch_width;
|
||||
case TestBranchType:
|
||||
return ImmTestBranch_width;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t Instruction::ImmBranchForwardRange(ImmBranchType branch_type) {
|
||||
int32_t encoded_max = 1 << (ImmBranchRangeBitwidth(branch_type) - 1);
|
||||
return encoded_max * kInstructionSize;
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsValidImmPCOffset(ImmBranchType branch_type,
|
||||
int64_t offset) {
|
||||
return is_intn(ImmBranchRangeBitwidth(branch_type), offset);
|
||||
}
|
||||
|
||||
|
||||
const Instruction* Instruction::ImmPCOffsetTarget() const {
|
||||
const Instruction * base = this;
|
||||
ptrdiff_t offset;
|
||||
if (IsPCRelAddressing()) {
|
||||
// ADR and ADRP.
|
||||
offset = ImmPCRel();
|
||||
if (Mask(PCRelAddressingMask) == ADRP) {
|
||||
base = AlignDown(base, kPageSize);
|
||||
offset *= kPageSize;
|
||||
} else {
|
||||
VIXL_ASSERT(Mask(PCRelAddressingMask) == ADR);
|
||||
}
|
||||
} else {
|
||||
// All PC-relative branches.
|
||||
VIXL_ASSERT(BranchType() != UnknownBranchType);
|
||||
// Relative branch offsets are instruction-size-aligned.
|
||||
offset = ImmBranch() << kInstructionSizeLog2;
|
||||
}
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
|
||||
int Instruction::ImmBranch() const {
|
||||
switch (BranchType()) {
|
||||
case CondBranchType: return ImmCondBranch();
|
||||
case UncondBranchType: return ImmUncondBranch();
|
||||
case CompareBranchType: return ImmCmpBranch();
|
||||
case TestBranchType: return ImmTestBranch();
|
||||
default: VIXL_UNREACHABLE();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetImmPCOffsetTarget(const Instruction* target) {
|
||||
if (IsPCRelAddressing()) {
|
||||
SetPCRelImmTarget(target);
|
||||
} else {
|
||||
SetBranchImmTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetPCRelImmTarget(const Instruction* target) {
|
||||
ptrdiff_t imm21;
|
||||
if ((Mask(PCRelAddressingMask) == ADR)) {
|
||||
imm21 = target - this;
|
||||
} else {
|
||||
VIXL_ASSERT(Mask(PCRelAddressingMask) == ADRP);
|
||||
uintptr_t this_page = reinterpret_cast<uintptr_t>(this) / kPageSize;
|
||||
uintptr_t target_page = reinterpret_cast<uintptr_t>(target) / kPageSize;
|
||||
imm21 = target_page - this_page;
|
||||
}
|
||||
Instr imm = Assembler::ImmPCRelAddress(static_cast<int32_t>(imm21));
|
||||
|
||||
SetInstructionBits(Mask(~ImmPCRel_mask) | imm);
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetBranchImmTarget(const Instruction* target) {
|
||||
VIXL_ASSERT(((target - this) & 3) == 0);
|
||||
Instr branch_imm = 0;
|
||||
uint32_t imm_mask = 0;
|
||||
int offset = static_cast<int>((target - this) >> kInstructionSizeLog2);
|
||||
switch (BranchType()) {
|
||||
case CondBranchType: {
|
||||
branch_imm = Assembler::ImmCondBranch(offset);
|
||||
imm_mask = ImmCondBranch_mask;
|
||||
break;
|
||||
}
|
||||
case UncondBranchType: {
|
||||
branch_imm = Assembler::ImmUncondBranch(offset);
|
||||
imm_mask = ImmUncondBranch_mask;
|
||||
break;
|
||||
}
|
||||
case CompareBranchType: {
|
||||
branch_imm = Assembler::ImmCmpBranch(offset);
|
||||
imm_mask = ImmCmpBranch_mask;
|
||||
break;
|
||||
}
|
||||
case TestBranchType: {
|
||||
branch_imm = Assembler::ImmTestBranch(offset);
|
||||
imm_mask = ImmTestBranch_mask;
|
||||
break;
|
||||
}
|
||||
default: VIXL_UNREACHABLE();
|
||||
}
|
||||
SetInstructionBits(Mask(~imm_mask) | branch_imm);
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetImmLLiteral(const Instruction* source) {
|
||||
VIXL_ASSERT(IsWordAligned(source));
|
||||
ptrdiff_t offset = (source - this) >> kLiteralEntrySizeLog2;
|
||||
Instr imm = Assembler::ImmLLiteral(static_cast<int>(offset));
|
||||
Instr mask = ImmLLiteral_mask;
|
||||
|
||||
SetInstructionBits(Mask(~mask) | imm);
|
||||
}
|
||||
|
||||
|
||||
VectorFormat VectorFormatHalfWidth(const VectorFormat vform) {
|
||||
VIXL_ASSERT(vform == kFormat8H || vform == kFormat4S || vform == kFormat2D ||
|
||||
vform == kFormatH || vform == kFormatS || vform == kFormatD);
|
||||
switch (vform) {
|
||||
case kFormat8H: return kFormat8B;
|
||||
case kFormat4S: return kFormat4H;
|
||||
case kFormat2D: return kFormat2S;
|
||||
case kFormatH: return kFormatB;
|
||||
case kFormatS: return kFormatH;
|
||||
case kFormatD: return kFormatS;
|
||||
default: VIXL_UNREACHABLE(); return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat VectorFormatDoubleWidth(const VectorFormat vform) {
|
||||
VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S ||
|
||||
vform == kFormatB || vform == kFormatH || vform == kFormatS);
|
||||
switch (vform) {
|
||||
case kFormat8B: return kFormat8H;
|
||||
case kFormat4H: return kFormat4S;
|
||||
case kFormat2S: return kFormat2D;
|
||||
case kFormatB: return kFormatH;
|
||||
case kFormatH: return kFormatS;
|
||||
case kFormatS: return kFormatD;
|
||||
default: VIXL_UNREACHABLE(); return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat VectorFormatFillQ(const VectorFormat vform) {
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormat8B:
|
||||
case kFormat16B: return kFormat16B;
|
||||
case kFormatH:
|
||||
case kFormat4H:
|
||||
case kFormat8H: return kFormat8H;
|
||||
case kFormatS:
|
||||
case kFormat2S:
|
||||
case kFormat4S: return kFormat4S;
|
||||
case kFormatD:
|
||||
case kFormat1D:
|
||||
case kFormat2D: return kFormat2D;
|
||||
default: VIXL_UNREACHABLE(); return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform) {
|
||||
switch (vform) {
|
||||
case kFormat4H: return kFormat8B;
|
||||
case kFormat8H: return kFormat16B;
|
||||
case kFormat2S: return kFormat4H;
|
||||
case kFormat4S: return kFormat8H;
|
||||
case kFormat1D: return kFormat2S;
|
||||
case kFormat2D: return kFormat4S;
|
||||
default: VIXL_UNREACHABLE(); return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
VectorFormat VectorFormatDoubleLanes(const VectorFormat vform) {
|
||||
VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S);
|
||||
switch (vform) {
|
||||
case kFormat8B: return kFormat16B;
|
||||
case kFormat4H: return kFormat8H;
|
||||
case kFormat2S: return kFormat4S;
|
||||
default: VIXL_UNREACHABLE(); return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat VectorFormatHalfLanes(const VectorFormat vform) {
|
||||
VIXL_ASSERT(vform == kFormat16B || vform == kFormat8H || vform == kFormat4S);
|
||||
switch (vform) {
|
||||
case kFormat16B: return kFormat8B;
|
||||
case kFormat8H: return kFormat4H;
|
||||
case kFormat4S: return kFormat2S;
|
||||
default: VIXL_UNREACHABLE(); return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorFormat ScalarFormatFromLaneSize(int laneSize) {
|
||||
switch (laneSize) {
|
||||
case 8: return kFormatB;
|
||||
case 16: return kFormatH;
|
||||
case 32: return kFormatS;
|
||||
case 64: return kFormatD;
|
||||
default: VIXL_UNREACHABLE(); return kFormatUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned RegisterSizeInBitsFromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB: return kBRegSize;
|
||||
case kFormatH: return kHRegSize;
|
||||
case kFormatS: return kSRegSize;
|
||||
case kFormatD: return kDRegSize;
|
||||
case kFormat8B:
|
||||
case kFormat4H:
|
||||
case kFormat2S:
|
||||
case kFormat1D: return kDRegSize;
|
||||
default: return kQRegSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned RegisterSizeInBytesFromFormat(VectorFormat vform) {
|
||||
return RegisterSizeInBitsFromFormat(vform) / 8;
|
||||
}
|
||||
|
||||
|
||||
unsigned LaneSizeInBitsFromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormat8B:
|
||||
case kFormat16B: return 8;
|
||||
case kFormatH:
|
||||
case kFormat4H:
|
||||
case kFormat8H: return 16;
|
||||
case kFormatS:
|
||||
case kFormat2S:
|
||||
case kFormat4S: return 32;
|
||||
case kFormatD:
|
||||
case kFormat1D:
|
||||
case kFormat2D: return 64;
|
||||
default: VIXL_UNREACHABLE(); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int LaneSizeInBytesFromFormat(VectorFormat vform) {
|
||||
return LaneSizeInBitsFromFormat(vform) / 8;
|
||||
}
|
||||
|
||||
|
||||
int LaneSizeInBytesLog2FromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormat8B:
|
||||
case kFormat16B: return 0;
|
||||
case kFormatH:
|
||||
case kFormat4H:
|
||||
case kFormat8H: return 1;
|
||||
case kFormatS:
|
||||
case kFormat2S:
|
||||
case kFormat4S: return 2;
|
||||
case kFormatD:
|
||||
case kFormat1D:
|
||||
case kFormat2D: return 3;
|
||||
default: VIXL_UNREACHABLE(); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int LaneCountFromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormat16B: return 16;
|
||||
case kFormat8B:
|
||||
case kFormat8H: return 8;
|
||||
case kFormat4H:
|
||||
case kFormat4S: return 4;
|
||||
case kFormat2S:
|
||||
case kFormat2D: return 2;
|
||||
case kFormat1D:
|
||||
case kFormatB:
|
||||
case kFormatH:
|
||||
case kFormatS:
|
||||
case kFormatD: return 1;
|
||||
default: VIXL_UNREACHABLE(); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int MaxLaneCountFromFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormat8B:
|
||||
case kFormat16B: return 16;
|
||||
case kFormatH:
|
||||
case kFormat4H:
|
||||
case kFormat8H: return 8;
|
||||
case kFormatS:
|
||||
case kFormat2S:
|
||||
case kFormat4S: return 4;
|
||||
case kFormatD:
|
||||
case kFormat1D:
|
||||
case kFormat2D: return 2;
|
||||
default: VIXL_UNREACHABLE(); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Does 'vform' indicate a vector format or a scalar format?
|
||||
bool IsVectorFormat(VectorFormat vform) {
|
||||
VIXL_ASSERT(vform != kFormatUndefined);
|
||||
switch (vform) {
|
||||
case kFormatB:
|
||||
case kFormatH:
|
||||
case kFormatS:
|
||||
case kFormatD: return false;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int64_t MaxIntFromFormat(VectorFormat vform) {
|
||||
return INT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform));
|
||||
}
|
||||
|
||||
|
||||
int64_t MinIntFromFormat(VectorFormat vform) {
|
||||
return INT64_MIN >> (64 - LaneSizeInBitsFromFormat(vform));
|
||||
}
|
||||
|
||||
|
||||
uint64_t MaxUintFromFormat(VectorFormat vform) {
|
||||
return UINT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform));
|
||||
}
|
||||
} // namespace vixl
|
||||
|
757
disas/libvixl/vixl/a64/instructions-a64.h
Normal file
757
disas/libvixl/vixl/a64/instructions-a64.h
Normal file
@@ -0,0 +1,757 @@
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_A64_INSTRUCTIONS_A64_H_
|
||||
#define VIXL_A64_INSTRUCTIONS_A64_H_
|
||||
|
||||
#include "vixl/globals.h"
|
||||
#include "vixl/utils.h"
|
||||
#include "vixl/a64/constants-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
// ISA constants. --------------------------------------------------------------
|
||||
|
||||
typedef uint32_t Instr;
|
||||
const unsigned kInstructionSize = 4;
|
||||
const unsigned kInstructionSizeLog2 = 2;
|
||||
const unsigned kLiteralEntrySize = 4;
|
||||
const unsigned kLiteralEntrySizeLog2 = 2;
|
||||
const unsigned kMaxLoadLiteralRange = 1 * MBytes;
|
||||
|
||||
// This is the nominal page size (as used by the adrp instruction); the actual
|
||||
// size of the memory pages allocated by the kernel is likely to differ.
|
||||
const unsigned kPageSize = 4 * KBytes;
|
||||
const unsigned kPageSizeLog2 = 12;
|
||||
|
||||
const unsigned kBRegSize = 8;
|
||||
const unsigned kBRegSizeLog2 = 3;
|
||||
const unsigned kBRegSizeInBytes = kBRegSize / 8;
|
||||
const unsigned kBRegSizeInBytesLog2 = kBRegSizeLog2 - 3;
|
||||
const unsigned kHRegSize = 16;
|
||||
const unsigned kHRegSizeLog2 = 4;
|
||||
const unsigned kHRegSizeInBytes = kHRegSize / 8;
|
||||
const unsigned kHRegSizeInBytesLog2 = kHRegSizeLog2 - 3;
|
||||
const unsigned kWRegSize = 32;
|
||||
const unsigned kWRegSizeLog2 = 5;
|
||||
const unsigned kWRegSizeInBytes = kWRegSize / 8;
|
||||
const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3;
|
||||
const unsigned kXRegSize = 64;
|
||||
const unsigned kXRegSizeLog2 = 6;
|
||||
const unsigned kXRegSizeInBytes = kXRegSize / 8;
|
||||
const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3;
|
||||
const unsigned kSRegSize = 32;
|
||||
const unsigned kSRegSizeLog2 = 5;
|
||||
const unsigned kSRegSizeInBytes = kSRegSize / 8;
|
||||
const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3;
|
||||
const unsigned kDRegSize = 64;
|
||||
const unsigned kDRegSizeLog2 = 6;
|
||||
const unsigned kDRegSizeInBytes = kDRegSize / 8;
|
||||
const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3;
|
||||
const unsigned kQRegSize = 128;
|
||||
const unsigned kQRegSizeLog2 = 7;
|
||||
const unsigned kQRegSizeInBytes = kQRegSize / 8;
|
||||
const unsigned kQRegSizeInBytesLog2 = kQRegSizeLog2 - 3;
|
||||
const uint64_t kWRegMask = UINT64_C(0xffffffff);
|
||||
const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff);
|
||||
const uint64_t kSRegMask = UINT64_C(0xffffffff);
|
||||
const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff);
|
||||
const uint64_t kSSignMask = UINT64_C(0x80000000);
|
||||
const uint64_t kDSignMask = UINT64_C(0x8000000000000000);
|
||||
const uint64_t kWSignMask = UINT64_C(0x80000000);
|
||||
const uint64_t kXSignMask = UINT64_C(0x8000000000000000);
|
||||
const uint64_t kByteMask = UINT64_C(0xff);
|
||||
const uint64_t kHalfWordMask = UINT64_C(0xffff);
|
||||
const uint64_t kWordMask = UINT64_C(0xffffffff);
|
||||
const uint64_t kXMaxUInt = UINT64_C(0xffffffffffffffff);
|
||||
const uint64_t kWMaxUInt = UINT64_C(0xffffffff);
|
||||
const int64_t kXMaxInt = INT64_C(0x7fffffffffffffff);
|
||||
const int64_t kXMinInt = INT64_C(0x8000000000000000);
|
||||
const int32_t kWMaxInt = INT32_C(0x7fffffff);
|
||||
const int32_t kWMinInt = INT32_C(0x80000000);
|
||||
const unsigned kLinkRegCode = 30;
|
||||
const unsigned kZeroRegCode = 31;
|
||||
const unsigned kSPRegInternalCode = 63;
|
||||
const unsigned kRegCodeMask = 0x1f;
|
||||
|
||||
const unsigned kAddressTagOffset = 56;
|
||||
const unsigned kAddressTagWidth = 8;
|
||||
const uint64_t kAddressTagMask =
|
||||
((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset;
|
||||
VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000));
|
||||
|
||||
// AArch64 floating-point specifics. These match IEEE-754.
|
||||
const unsigned kDoubleMantissaBits = 52;
|
||||
const unsigned kDoubleExponentBits = 11;
|
||||
const unsigned kFloatMantissaBits = 23;
|
||||
const unsigned kFloatExponentBits = 8;
|
||||
const unsigned kFloat16MantissaBits = 10;
|
||||
const unsigned kFloat16ExponentBits = 5;
|
||||
|
||||
// Floating-point infinity values.
|
||||
extern const float16 kFP16PositiveInfinity;
|
||||
extern const float16 kFP16NegativeInfinity;
|
||||
extern const float kFP32PositiveInfinity;
|
||||
extern const float kFP32NegativeInfinity;
|
||||
extern const double kFP64PositiveInfinity;
|
||||
extern const double kFP64NegativeInfinity;
|
||||
|
||||
// The default NaN values (for FPCR.DN=1).
|
||||
extern const float16 kFP16DefaultNaN;
|
||||
extern const float kFP32DefaultNaN;
|
||||
extern const double kFP64DefaultNaN;
|
||||
|
||||
unsigned CalcLSDataSize(LoadStoreOp op);
|
||||
unsigned CalcLSPairDataSize(LoadStorePairOp op);
|
||||
|
||||
enum ImmBranchType {
|
||||
UnknownBranchType = 0,
|
||||
CondBranchType = 1,
|
||||
UncondBranchType = 2,
|
||||
CompareBranchType = 3,
|
||||
TestBranchType = 4
|
||||
};
|
||||
|
||||
enum AddrMode {
|
||||
Offset,
|
||||
PreIndex,
|
||||
PostIndex
|
||||
};
|
||||
|
||||
enum FPRounding {
|
||||
// The first four values are encodable directly by FPCR<RMode>.
|
||||
FPTieEven = 0x0,
|
||||
FPPositiveInfinity = 0x1,
|
||||
FPNegativeInfinity = 0x2,
|
||||
FPZero = 0x3,
|
||||
|
||||
// The final rounding modes are only available when explicitly specified by
|
||||
// the instruction (such as with fcvta). It cannot be set in FPCR.
|
||||
FPTieAway,
|
||||
FPRoundOdd
|
||||
};
|
||||
|
||||
enum Reg31Mode {
|
||||
Reg31IsStackPointer,
|
||||
Reg31IsZeroRegister
|
||||
};
|
||||
|
||||
// Instructions. ---------------------------------------------------------------
|
||||
|
||||
class Instruction {
|
||||
public:
|
||||
Instr InstructionBits() const {
|
||||
return *(reinterpret_cast<const Instr*>(this));
|
||||
}
|
||||
|
||||
void SetInstructionBits(Instr new_instr) {
|
||||
*(reinterpret_cast<Instr*>(this)) = new_instr;
|
||||
}
|
||||
|
||||
int Bit(int pos) const {
|
||||
return (InstructionBits() >> pos) & 1;
|
||||
}
|
||||
|
||||
uint32_t Bits(int msb, int lsb) const {
|
||||
return unsigned_bitextract_32(msb, lsb, InstructionBits());
|
||||
}
|
||||
|
||||
int32_t SignedBits(int msb, int lsb) const {
|
||||
int32_t bits = *(reinterpret_cast<const int32_t*>(this));
|
||||
return signed_bitextract_32(msb, lsb, bits);
|
||||
}
|
||||
|
||||
Instr Mask(uint32_t mask) const {
|
||||
return InstructionBits() & mask;
|
||||
}
|
||||
|
||||
#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
|
||||
int32_t Name() const { return Func(HighBit, LowBit); }
|
||||
INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
|
||||
#undef DEFINE_GETTER
|
||||
|
||||
// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
|
||||
// formed from ImmPCRelLo and ImmPCRelHi.
|
||||
int ImmPCRel() const {
|
||||
int offset =
|
||||
static_cast<int>((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
|
||||
int width = ImmPCRelLo_width + ImmPCRelHi_width;
|
||||
return signed_bitextract_32(width - 1, 0, offset);
|
||||
}
|
||||
|
||||
uint64_t ImmLogical() const;
|
||||
unsigned ImmNEONabcdefgh() const;
|
||||
float ImmFP32() const;
|
||||
double ImmFP64() const;
|
||||
float ImmNEONFP32() const;
|
||||
double ImmNEONFP64() const;
|
||||
|
||||
unsigned SizeLS() const {
|
||||
return CalcLSDataSize(static_cast<LoadStoreOp>(Mask(LoadStoreMask)));
|
||||
}
|
||||
|
||||
unsigned SizeLSPair() const {
|
||||
return CalcLSPairDataSize(
|
||||
static_cast<LoadStorePairOp>(Mask(LoadStorePairMask)));
|
||||
}
|
||||
|
||||
int NEONLSIndex(int access_size_shift) const {
|
||||
int64_t q = NEONQ();
|
||||
int64_t s = NEONS();
|
||||
int64_t size = NEONLSSize();
|
||||
int64_t index = (q << 3) | (s << 2) | size;
|
||||
return static_cast<int>(index >> access_size_shift);
|
||||
}
|
||||
|
||||
// Helpers.
|
||||
bool IsCondBranchImm() const {
|
||||
return Mask(ConditionalBranchFMask) == ConditionalBranchFixed;
|
||||
}
|
||||
|
||||
bool IsUncondBranchImm() const {
|
||||
return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed;
|
||||
}
|
||||
|
||||
bool IsCompareBranch() const {
|
||||
return Mask(CompareBranchFMask) == CompareBranchFixed;
|
||||
}
|
||||
|
||||
bool IsTestBranch() const {
|
||||
return Mask(TestBranchFMask) == TestBranchFixed;
|
||||
}
|
||||
|
||||
bool IsImmBranch() const {
|
||||
return BranchType() != UnknownBranchType;
|
||||
}
|
||||
|
||||
bool IsPCRelAddressing() const {
|
||||
return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
|
||||
}
|
||||
|
||||
bool IsLogicalImmediate() const {
|
||||
return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
|
||||
}
|
||||
|
||||
bool IsAddSubImmediate() const {
|
||||
return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
|
||||
}
|
||||
|
||||
bool IsAddSubExtended() const {
|
||||
return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
|
||||
}
|
||||
|
||||
bool IsLoadOrStore() const {
|
||||
return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
|
||||
}
|
||||
|
||||
bool IsLoad() const;
|
||||
bool IsStore() const;
|
||||
|
||||
bool IsLoadLiteral() const {
|
||||
// This includes PRFM_lit.
|
||||
return Mask(LoadLiteralFMask) == LoadLiteralFixed;
|
||||
}
|
||||
|
||||
bool IsMovn() const {
|
||||
return (Mask(MoveWideImmediateMask) == MOVN_x) ||
|
||||
(Mask(MoveWideImmediateMask) == MOVN_w);
|
||||
}
|
||||
|
||||
static int ImmBranchRangeBitwidth(ImmBranchType branch_type);
|
||||
static int32_t ImmBranchForwardRange(ImmBranchType branch_type);
|
||||
static bool IsValidImmPCOffset(ImmBranchType branch_type, int64_t offset);
|
||||
|
||||
// Indicate whether Rd can be the stack pointer or the zero register. This
|
||||
// does not check that the instruction actually has an Rd field.
|
||||
Reg31Mode RdMode() const {
|
||||
// The following instructions use sp or wsp as Rd:
|
||||
// Add/sub (immediate) when not setting the flags.
|
||||
// Add/sub (extended) when not setting the flags.
|
||||
// Logical (immediate) when not setting the flags.
|
||||
// Otherwise, r31 is the zero register.
|
||||
if (IsAddSubImmediate() || IsAddSubExtended()) {
|
||||
if (Mask(AddSubSetFlagsBit)) {
|
||||
return Reg31IsZeroRegister;
|
||||
} else {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
}
|
||||
if (IsLogicalImmediate()) {
|
||||
// Of the logical (immediate) instructions, only ANDS (and its aliases)
|
||||
// can set the flags. The others can all write into sp.
|
||||
// Note that some logical operations are not available to
|
||||
// immediate-operand instructions, so we have to combine two masks here.
|
||||
if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) {
|
||||
return Reg31IsZeroRegister;
|
||||
} else {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
}
|
||||
return Reg31IsZeroRegister;
|
||||
}
|
||||
|
||||
// Indicate whether Rn can be the stack pointer or the zero register. This
|
||||
// does not check that the instruction actually has an Rn field.
|
||||
Reg31Mode RnMode() const {
|
||||
// The following instructions use sp or wsp as Rn:
|
||||
// All loads and stores.
|
||||
// Add/sub (immediate).
|
||||
// Add/sub (extended).
|
||||
// Otherwise, r31 is the zero register.
|
||||
if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
return Reg31IsZeroRegister;
|
||||
}
|
||||
|
||||
ImmBranchType BranchType() const {
|
||||
if (IsCondBranchImm()) {
|
||||
return CondBranchType;
|
||||
} else if (IsUncondBranchImm()) {
|
||||
return UncondBranchType;
|
||||
} else if (IsCompareBranch()) {
|
||||
return CompareBranchType;
|
||||
} else if (IsTestBranch()) {
|
||||
return TestBranchType;
|
||||
} else {
|
||||
return UnknownBranchType;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the target of this instruction. 'this' may be a branch or a
|
||||
// PC-relative addressing instruction.
|
||||
const Instruction* ImmPCOffsetTarget() const;
|
||||
|
||||
// Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
|
||||
// a PC-relative addressing instruction.
|
||||
void SetImmPCOffsetTarget(const Instruction* target);
|
||||
// Patch a literal load instruction to load from 'source'.
|
||||
void SetImmLLiteral(const Instruction* source);
|
||||
|
||||
// The range of a load literal instruction, expressed as 'instr +- range'.
|
||||
// The range is actually the 'positive' range; the branch instruction can
|
||||
// target [instr - range - kInstructionSize, instr + range].
|
||||
static const int kLoadLiteralImmBitwidth = 19;
|
||||
static const int kLoadLiteralRange =
|
||||
(1 << kLoadLiteralImmBitwidth) / 2 - kInstructionSize;
|
||||
|
||||
// Calculate the address of a literal referred to by a load-literal
|
||||
// instruction, and return it as the specified type.
|
||||
//
|
||||
// The literal itself is safely mutable only if the backing buffer is safely
|
||||
// mutable.
|
||||
template <typename T>
|
||||
T LiteralAddress() const {
|
||||
uint64_t base_raw = reinterpret_cast<uint64_t>(this);
|
||||
int64_t offset = ImmLLiteral() << kLiteralEntrySizeLog2;
|
||||
uint64_t address_raw = base_raw + offset;
|
||||
|
||||
// Cast the address using a C-style cast. A reinterpret_cast would be
|
||||
// appropriate, but it can't cast one integral type to another.
|
||||
T address = (T)(address_raw);
|
||||
|
||||
// Assert that the address can be represented by the specified type.
|
||||
VIXL_ASSERT((uint64_t)(address) == address_raw);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
uint32_t Literal32() const {
|
||||
uint32_t literal;
|
||||
memcpy(&literal, LiteralAddress<const void*>(), sizeof(literal));
|
||||
return literal;
|
||||
}
|
||||
|
||||
uint64_t Literal64() const {
|
||||
uint64_t literal;
|
||||
memcpy(&literal, LiteralAddress<const void*>(), sizeof(literal));
|
||||
return literal;
|
||||
}
|
||||
|
||||
float LiteralFP32() const {
|
||||
return rawbits_to_float(Literal32());
|
||||
}
|
||||
|
||||
double LiteralFP64() const {
|
||||
return rawbits_to_double(Literal64());
|
||||
}
|
||||
|
||||
const Instruction* NextInstruction() const {
|
||||
return this + kInstructionSize;
|
||||
}
|
||||
|
||||
const Instruction* InstructionAtOffset(int64_t offset) const {
|
||||
VIXL_ASSERT(IsWordAligned(this + offset));
|
||||
return this + offset;
|
||||
}
|
||||
|
||||
template<typename T> static Instruction* Cast(T src) {
|
||||
return reinterpret_cast<Instruction*>(src);
|
||||
}
|
||||
|
||||
template<typename T> static const Instruction* CastConst(T src) {
|
||||
return reinterpret_cast<const Instruction*>(src);
|
||||
}
|
||||
|
||||
private:
|
||||
int ImmBranch() const;
|
||||
|
||||
static float Imm8ToFP32(uint32_t imm8);
|
||||
static double Imm8ToFP64(uint32_t imm8);
|
||||
|
||||
void SetPCRelImmTarget(const Instruction* target);
|
||||
void SetBranchImmTarget(const Instruction* target);
|
||||
};
|
||||
|
||||
|
||||
// Functions for handling NEON vector format information.
|
||||
enum VectorFormat {
|
||||
kFormatUndefined = 0xffffffff,
|
||||
kFormat8B = NEON_8B,
|
||||
kFormat16B = NEON_16B,
|
||||
kFormat4H = NEON_4H,
|
||||
kFormat8H = NEON_8H,
|
||||
kFormat2S = NEON_2S,
|
||||
kFormat4S = NEON_4S,
|
||||
kFormat1D = NEON_1D,
|
||||
kFormat2D = NEON_2D,
|
||||
|
||||
// Scalar formats. We add the scalar bit to distinguish between scalar and
|
||||
// vector enumerations; the bit is always set in the encoding of scalar ops
|
||||
// and always clear for vector ops. Although kFormatD and kFormat1D appear
|
||||
// to be the same, their meaning is subtly different. The first is a scalar
|
||||
// operation, the second a vector operation that only affects one lane.
|
||||
kFormatB = NEON_B | NEONScalar,
|
||||
kFormatH = NEON_H | NEONScalar,
|
||||
kFormatS = NEON_S | NEONScalar,
|
||||
kFormatD = NEON_D | NEONScalar
|
||||
};
|
||||
|
||||
VectorFormat VectorFormatHalfWidth(const VectorFormat vform);
|
||||
VectorFormat VectorFormatDoubleWidth(const VectorFormat vform);
|
||||
VectorFormat VectorFormatDoubleLanes(const VectorFormat vform);
|
||||
VectorFormat VectorFormatHalfLanes(const VectorFormat vform);
|
||||
VectorFormat ScalarFormatFromLaneSize(int lanesize);
|
||||
VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform);
|
||||
VectorFormat VectorFormatFillQ(const VectorFormat vform);
|
||||
unsigned RegisterSizeInBitsFromFormat(VectorFormat vform);
|
||||
unsigned RegisterSizeInBytesFromFormat(VectorFormat vform);
|
||||
// TODO: Make the return types of these functions consistent.
|
||||
unsigned LaneSizeInBitsFromFormat(VectorFormat vform);
|
||||
int LaneSizeInBytesFromFormat(VectorFormat vform);
|
||||
int LaneSizeInBytesLog2FromFormat(VectorFormat vform);
|
||||
int LaneCountFromFormat(VectorFormat vform);
|
||||
int MaxLaneCountFromFormat(VectorFormat vform);
|
||||
bool IsVectorFormat(VectorFormat vform);
|
||||
int64_t MaxIntFromFormat(VectorFormat vform);
|
||||
int64_t MinIntFromFormat(VectorFormat vform);
|
||||
uint64_t MaxUintFromFormat(VectorFormat vform);
|
||||
|
||||
|
||||
enum NEONFormat {
|
||||
NF_UNDEF = 0,
|
||||
NF_8B = 1,
|
||||
NF_16B = 2,
|
||||
NF_4H = 3,
|
||||
NF_8H = 4,
|
||||
NF_2S = 5,
|
||||
NF_4S = 6,
|
||||
NF_1D = 7,
|
||||
NF_2D = 8,
|
||||
NF_B = 9,
|
||||
NF_H = 10,
|
||||
NF_S = 11,
|
||||
NF_D = 12
|
||||
};
|
||||
|
||||
static const unsigned kNEONFormatMaxBits = 6;
|
||||
|
||||
struct NEONFormatMap {
|
||||
// The bit positions in the instruction to consider.
|
||||
uint8_t bits[kNEONFormatMaxBits];
|
||||
|
||||
// Mapping from concatenated bits to format.
|
||||
NEONFormat map[1 << kNEONFormatMaxBits];
|
||||
};
|
||||
|
||||
class NEONFormatDecoder {
|
||||
public:
|
||||
enum SubstitutionMode {
|
||||
kPlaceholder,
|
||||
kFormat
|
||||
};
|
||||
|
||||
// Construct a format decoder with increasingly specific format maps for each
|
||||
// subsitution. If no format map is specified, the default is the integer
|
||||
// format map.
|
||||
explicit NEONFormatDecoder(const Instruction* instr) {
|
||||
instrbits_ = instr->InstructionBits();
|
||||
SetFormatMaps(IntegerFormatMap());
|
||||
}
|
||||
NEONFormatDecoder(const Instruction* instr,
|
||||
const NEONFormatMap* format) {
|
||||
instrbits_ = instr->InstructionBits();
|
||||
SetFormatMaps(format);
|
||||
}
|
||||
NEONFormatDecoder(const Instruction* instr,
|
||||
const NEONFormatMap* format0,
|
||||
const NEONFormatMap* format1) {
|
||||
instrbits_ = instr->InstructionBits();
|
||||
SetFormatMaps(format0, format1);
|
||||
}
|
||||
NEONFormatDecoder(const Instruction* instr,
|
||||
const NEONFormatMap* format0,
|
||||
const NEONFormatMap* format1,
|
||||
const NEONFormatMap* format2) {
|
||||
instrbits_ = instr->InstructionBits();
|
||||
SetFormatMaps(format0, format1, format2);
|
||||
}
|
||||
|
||||
// Set the format mapping for all or individual substitutions.
|
||||
void SetFormatMaps(const NEONFormatMap* format0,
|
||||
const NEONFormatMap* format1 = NULL,
|
||||
const NEONFormatMap* format2 = NULL) {
|
||||
VIXL_ASSERT(format0 != NULL);
|
||||
formats_[0] = format0;
|
||||
formats_[1] = (format1 == NULL) ? formats_[0] : format1;
|
||||
formats_[2] = (format2 == NULL) ? formats_[1] : format2;
|
||||
}
|
||||
void SetFormatMap(unsigned index, const NEONFormatMap* format) {
|
||||
VIXL_ASSERT(index <= (sizeof(formats_) / sizeof(formats_[0])));
|
||||
VIXL_ASSERT(format != NULL);
|
||||
formats_[index] = format;
|
||||
}
|
||||
|
||||
// Substitute %s in the input string with the placeholder string for each
|
||||
// register, ie. "'B", "'H", etc.
|
||||
const char* SubstitutePlaceholders(const char* string) {
|
||||
return Substitute(string, kPlaceholder, kPlaceholder, kPlaceholder);
|
||||
}
|
||||
|
||||
// Substitute %s in the input string with a new string based on the
|
||||
// substitution mode.
|
||||
const char* Substitute(const char* string,
|
||||
SubstitutionMode mode0 = kFormat,
|
||||
SubstitutionMode mode1 = kFormat,
|
||||
SubstitutionMode mode2 = kFormat) {
|
||||
snprintf(form_buffer_, sizeof(form_buffer_), string,
|
||||
GetSubstitute(0, mode0),
|
||||
GetSubstitute(1, mode1),
|
||||
GetSubstitute(2, mode2));
|
||||
return form_buffer_;
|
||||
}
|
||||
|
||||
// Append a "2" to a mnemonic string based of the state of the Q bit.
|
||||
const char* Mnemonic(const char* mnemonic) {
|
||||
if ((instrbits_ & NEON_Q) != 0) {
|
||||
snprintf(mne_buffer_, sizeof(mne_buffer_), "%s2", mnemonic);
|
||||
return mne_buffer_;
|
||||
}
|
||||
return mnemonic;
|
||||
}
|
||||
|
||||
VectorFormat GetVectorFormat(int format_index = 0) {
|
||||
return GetVectorFormat(formats_[format_index]);
|
||||
}
|
||||
|
||||
VectorFormat GetVectorFormat(const NEONFormatMap* format_map) {
|
||||
static const VectorFormat vform[] = {
|
||||
kFormatUndefined,
|
||||
kFormat8B, kFormat16B, kFormat4H, kFormat8H,
|
||||
kFormat2S, kFormat4S, kFormat1D, kFormat2D,
|
||||
kFormatB, kFormatH, kFormatS, kFormatD
|
||||
};
|
||||
VIXL_ASSERT(GetNEONFormat(format_map) < (sizeof(vform) / sizeof(vform[0])));
|
||||
return vform[GetNEONFormat(format_map)];
|
||||
}
|
||||
|
||||
// Built in mappings for common cases.
|
||||
|
||||
// The integer format map uses three bits (Q, size<1:0>) to encode the
|
||||
// "standard" set of NEON integer vector formats.
|
||||
static const NEONFormatMap* IntegerFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{23, 22, 30},
|
||||
{NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_UNDEF, NF_2D}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The long integer format map uses two bits (size<1:0>) to encode the
|
||||
// long set of NEON integer vector formats. These are used in narrow, wide
|
||||
// and long operations.
|
||||
static const NEONFormatMap* LongIntegerFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{23, 22}, {NF_8H, NF_4S, NF_2D}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The FP format map uses two bits (Q, size<0>) to encode the NEON FP vector
|
||||
// formats: NF_2S, NF_4S, NF_2D.
|
||||
static const NEONFormatMap* FPFormatMap() {
|
||||
// The FP format map assumes two bits (Q, size<0>) are used to encode the
|
||||
// NEON FP vector formats: NF_2S, NF_4S, NF_2D.
|
||||
static const NEONFormatMap map = {
|
||||
{22, 30}, {NF_2S, NF_4S, NF_UNDEF, NF_2D}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The load/store format map uses three bits (Q, 11, 10) to encode the
|
||||
// set of NEON vector formats.
|
||||
static const NEONFormatMap* LoadStoreFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{11, 10, 30},
|
||||
{NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The logical format map uses one bit (Q) to encode the NEON vector format:
|
||||
// NF_8B, NF_16B.
|
||||
static const NEONFormatMap* LogicalFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{30}, {NF_8B, NF_16B}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The triangular format map uses between two and five bits to encode the NEON
|
||||
// vector format:
|
||||
// xxx10->8B, xxx11->16B, xx100->4H, xx101->8H
|
||||
// x1000->2S, x1001->4S, 10001->2D, all others undefined.
|
||||
static const NEONFormatMap* TriangularFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{19, 18, 17, 16, 30},
|
||||
{NF_UNDEF, NF_UNDEF, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_2S,
|
||||
NF_4S, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_UNDEF, NF_2D,
|
||||
NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_2S, NF_4S, NF_8B, NF_16B,
|
||||
NF_4H, NF_8H, NF_8B, NF_16B}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The scalar format map uses two bits (size<1:0>) to encode the NEON scalar
|
||||
// formats: NF_B, NF_H, NF_S, NF_D.
|
||||
static const NEONFormatMap* ScalarFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{23, 22}, {NF_B, NF_H, NF_S, NF_D}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The long scalar format map uses two bits (size<1:0>) to encode the longer
|
||||
// NEON scalar formats: NF_H, NF_S, NF_D.
|
||||
static const NEONFormatMap* LongScalarFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{23, 22}, {NF_H, NF_S, NF_D}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The FP scalar format map assumes one bit (size<0>) is used to encode the
|
||||
// NEON FP scalar formats: NF_S, NF_D.
|
||||
static const NEONFormatMap* FPScalarFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{22}, {NF_S, NF_D}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
// The triangular scalar format map uses between one and four bits to encode
|
||||
// the NEON FP scalar formats:
|
||||
// xxx1->B, xx10->H, x100->S, 1000->D, all others undefined.
|
||||
static const NEONFormatMap* TriangularScalarFormatMap() {
|
||||
static const NEONFormatMap map = {
|
||||
{19, 18, 17, 16},
|
||||
{NF_UNDEF, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B,
|
||||
NF_D, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B}
|
||||
};
|
||||
return ↦
|
||||
}
|
||||
|
||||
private:
|
||||
// Get a pointer to a string that represents the format or placeholder for
|
||||
// the specified substitution index, based on the format map and instruction.
|
||||
const char* GetSubstitute(int index, SubstitutionMode mode) {
|
||||
if (mode == kFormat) {
|
||||
return NEONFormatAsString(GetNEONFormat(formats_[index]));
|
||||
}
|
||||
VIXL_ASSERT(mode == kPlaceholder);
|
||||
return NEONFormatAsPlaceholder(GetNEONFormat(formats_[index]));
|
||||
}
|
||||
|
||||
// Get the NEONFormat enumerated value for bits obtained from the
|
||||
// instruction based on the specified format mapping.
|
||||
NEONFormat GetNEONFormat(const NEONFormatMap* format_map) {
|
||||
return format_map->map[PickBits(format_map->bits)];
|
||||
}
|
||||
|
||||
// Convert a NEONFormat into a string.
|
||||
static const char* NEONFormatAsString(NEONFormat format) {
|
||||
static const char* formats[] = {
|
||||
"undefined",
|
||||
"8b", "16b", "4h", "8h", "2s", "4s", "1d", "2d",
|
||||
"b", "h", "s", "d"
|
||||
};
|
||||
VIXL_ASSERT(format < (sizeof(formats) / sizeof(formats[0])));
|
||||
return formats[format];
|
||||
}
|
||||
|
||||
// Convert a NEONFormat into a register placeholder string.
|
||||
static const char* NEONFormatAsPlaceholder(NEONFormat format) {
|
||||
VIXL_ASSERT((format == NF_B) || (format == NF_H) ||
|
||||
(format == NF_S) || (format == NF_D) ||
|
||||
(format == NF_UNDEF));
|
||||
static const char* formats[] = {
|
||||
"undefined",
|
||||
"undefined", "undefined", "undefined", "undefined",
|
||||
"undefined", "undefined", "undefined", "undefined",
|
||||
"'B", "'H", "'S", "'D"
|
||||
};
|
||||
return formats[format];
|
||||
}
|
||||
|
||||
// Select bits from instrbits_ defined by the bits array, concatenate them,
|
||||
// and return the value.
|
||||
uint8_t PickBits(const uint8_t bits[]) {
|
||||
uint8_t result = 0;
|
||||
for (unsigned b = 0; b < kNEONFormatMaxBits; b++) {
|
||||
if (bits[b] == 0) break;
|
||||
result <<= 1;
|
||||
result |= ((instrbits_ & (1 << bits[b])) == 0) ? 0 : 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Instr instrbits_;
|
||||
const NEONFormatMap* formats_[3];
|
||||
char form_buffer_[64];
|
||||
char mne_buffer_[16];
|
||||
};
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_A64_INSTRUCTIONS_A64_H_
|
@@ -28,7 +28,7 @@
|
||||
#define VIXL_CODE_BUFFER_H
|
||||
|
||||
#include <string.h>
|
||||
#include "globals.h"
|
||||
#include "vixl/globals.h"
|
||||
|
||||
namespace vixl {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -24,53 +24,13 @@
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "utils.h"
|
||||
#include <stdio.h>
|
||||
#include "compiler-intrinsics.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
uint32_t float_to_rawbits(float value) {
|
||||
uint32_t bits = 0;
|
||||
memcpy(&bits, &value, 4);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
uint64_t double_to_rawbits(double value) {
|
||||
uint64_t bits = 0;
|
||||
memcpy(&bits, &value, 8);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
float rawbits_to_float(uint32_t bits) {
|
||||
float value = 0.0;
|
||||
memcpy(&value, &bits, 4);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
double rawbits_to_double(uint64_t bits) {
|
||||
double value = 0.0;
|
||||
memcpy(&value, &bits, 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
int CountLeadingZeros(uint64_t value, int width) {
|
||||
VIXL_ASSERT((width == 32) || (width == 64));
|
||||
int count = 0;
|
||||
uint64_t bit_test = UINT64_C(1) << (width - 1);
|
||||
while ((count < width) && ((bit_test & value) == 0)) {
|
||||
count++;
|
||||
bit_test >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int CountLeadingSignBits(int64_t value, int width) {
|
||||
VIXL_ASSERT((width == 32) || (width == 64));
|
||||
int CountLeadingSignBitsFallBack(int64_t value, int width) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
if (value >= 0) {
|
||||
return CountLeadingZeros(value, width) - 1;
|
||||
} else {
|
||||
@@ -79,23 +39,46 @@ int CountLeadingSignBits(int64_t value, int width) {
|
||||
}
|
||||
|
||||
|
||||
int CountTrailingZeros(uint64_t value, int width) {
|
||||
VIXL_ASSERT((width == 32) || (width == 64));
|
||||
int count = 0;
|
||||
while ((count < width) && (((value >> count) & 1) == 0)) {
|
||||
count++;
|
||||
int CountLeadingZerosFallBack(uint64_t value, int width) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
if (value == 0) {
|
||||
return width;
|
||||
}
|
||||
int count = 0;
|
||||
value = value << (64 - width);
|
||||
if ((value & UINT64_C(0xffffffff00000000)) == 0) {
|
||||
count += 32;
|
||||
value = value << 32;
|
||||
}
|
||||
if ((value & UINT64_C(0xffff000000000000)) == 0) {
|
||||
count += 16;
|
||||
value = value << 16;
|
||||
}
|
||||
if ((value & UINT64_C(0xff00000000000000)) == 0) {
|
||||
count += 8;
|
||||
value = value << 8;
|
||||
}
|
||||
if ((value & UINT64_C(0xf000000000000000)) == 0) {
|
||||
count += 4;
|
||||
value = value << 4;
|
||||
}
|
||||
if ((value & UINT64_C(0xc000000000000000)) == 0) {
|
||||
count += 2;
|
||||
value = value << 2;
|
||||
}
|
||||
if ((value & UINT64_C(0x8000000000000000)) == 0) {
|
||||
count += 1;
|
||||
}
|
||||
count += (value == 0);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int CountSetBits(uint64_t value, int width) {
|
||||
// TODO: Other widths could be added here, as the implementation already
|
||||
// supports them.
|
||||
VIXL_ASSERT((width == 32) || (width == 64));
|
||||
int CountSetBitsFallBack(uint64_t value, int width) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
|
||||
// Mask out unused bits to ensure that they are not counted.
|
||||
value &= (UINT64_C(0xffffffffffffffff) >> (64-width));
|
||||
value &= (UINT64_C(0xffffffffffffffff) >> (64 - width));
|
||||
|
||||
// Add up the set bits.
|
||||
// The algorithm works by adding pairs of bit fields together iteratively,
|
||||
@@ -122,30 +105,40 @@ int CountSetBits(uint64_t value, int width) {
|
||||
value = ((value >> shift) & kMasks[i]) + (value & kMasks[i]);
|
||||
}
|
||||
|
||||
return value;
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
|
||||
|
||||
uint64_t LowestSetBit(uint64_t value) {
|
||||
return value & -value;
|
||||
}
|
||||
|
||||
|
||||
bool IsPowerOf2(int64_t value) {
|
||||
return (value != 0) && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
|
||||
unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) {
|
||||
VIXL_ASSERT((reg_size % 8) == 0);
|
||||
int CountTrailingZerosFallBack(uint64_t value, int width) {
|
||||
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
|
||||
int count = 0;
|
||||
for (unsigned i = 0; i < (reg_size / 16); i++) {
|
||||
if ((imm & 0xffff) == 0) {
|
||||
count++;
|
||||
}
|
||||
imm >>= 16;
|
||||
value = value << (64 - width);
|
||||
if ((value & UINT64_C(0xffffffff)) == 0) {
|
||||
count += 32;
|
||||
value = value >> 32;
|
||||
}
|
||||
return count;
|
||||
if ((value & 0xffff) == 0) {
|
||||
count += 16;
|
||||
value = value >> 16;
|
||||
}
|
||||
if ((value & 0xff) == 0) {
|
||||
count += 8;
|
||||
value = value >> 8;
|
||||
}
|
||||
if ((value & 0xf) == 0) {
|
||||
count += 4;
|
||||
value = value >> 4;
|
||||
}
|
||||
if ((value & 0x3) == 0) {
|
||||
count += 2;
|
||||
value = value >> 2;
|
||||
}
|
||||
if ((value & 0x1) == 0) {
|
||||
count += 1;
|
||||
}
|
||||
count += (value == 0);
|
||||
return count - (64 - width);
|
||||
}
|
||||
|
||||
|
||||
} // namespace vixl
|
155
disas/libvixl/vixl/compiler-intrinsics.h
Normal file
155
disas/libvixl/vixl/compiler-intrinsics.h
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#ifndef VIXL_COMPILER_INTRINSICS_H
|
||||
#define VIXL_COMPILER_INTRINSICS_H
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
// Helper to check whether the version of GCC used is greater than the specified
|
||||
// requirement.
|
||||
#define MAJOR 1000000
|
||||
#define MINOR 1000
|
||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
|
||||
#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) \
|
||||
((__GNUC__ * MAJOR + __GNUC_MINOR__ * MINOR + __GNUC_PATCHLEVEL__) >= \
|
||||
((major) * MAJOR + (minor) * MINOR + (patchlevel)))
|
||||
#elif defined(__GNUC__) && defined(__GNUC_MINOR__)
|
||||
#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) \
|
||||
((__GNUC__ * MAJOR + __GNUC_MINOR__ * MINOR) >= \
|
||||
((major) * MAJOR + (minor) * MINOR + (patchlevel)))
|
||||
#else
|
||||
#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) 0
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__clang__) && !defined(VIXL_NO_COMPILER_BUILTINS)
|
||||
|
||||
#define COMPILER_HAS_BUILTIN_CLRSB (__has_builtin(__builtin_clrsb))
|
||||
#define COMPILER_HAS_BUILTIN_CLZ (__has_builtin(__builtin_clz))
|
||||
#define COMPILER_HAS_BUILTIN_CTZ (__has_builtin(__builtin_ctz))
|
||||
#define COMPILER_HAS_BUILTIN_FFS (__has_builtin(__builtin_ffs))
|
||||
#define COMPILER_HAS_BUILTIN_POPCOUNT (__has_builtin(__builtin_popcount))
|
||||
|
||||
#elif defined(__GNUC__) && !defined(VIXL_NO_COMPILER_BUILTINS)
|
||||
// The documentation for these builtins is available at:
|
||||
// https://gcc.gnu.org/onlinedocs/gcc-$MAJOR.$MINOR.$PATCHLEVEL/gcc//Other-Builtins.html
|
||||
|
||||
# define COMPILER_HAS_BUILTIN_CLRSB (GCC_VERSION_OR_NEWER(4, 7, 0))
|
||||
# define COMPILER_HAS_BUILTIN_CLZ (GCC_VERSION_OR_NEWER(3, 4, 0))
|
||||
# define COMPILER_HAS_BUILTIN_CTZ (GCC_VERSION_OR_NEWER(3, 4, 0))
|
||||
# define COMPILER_HAS_BUILTIN_FFS (GCC_VERSION_OR_NEWER(3, 4, 0))
|
||||
# define COMPILER_HAS_BUILTIN_POPCOUNT (GCC_VERSION_OR_NEWER(3, 4, 0))
|
||||
|
||||
#else
|
||||
// One can define VIXL_NO_COMPILER_BUILTINS to force using the manually
|
||||
// implemented C++ methods.
|
||||
|
||||
#define COMPILER_HAS_BUILTIN_BSWAP false
|
||||
#define COMPILER_HAS_BUILTIN_CLRSB false
|
||||
#define COMPILER_HAS_BUILTIN_CLZ false
|
||||
#define COMPILER_HAS_BUILTIN_CTZ false
|
||||
#define COMPILER_HAS_BUILTIN_FFS false
|
||||
#define COMPILER_HAS_BUILTIN_POPCOUNT false
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename V>
|
||||
inline bool IsPowerOf2(V value) {
|
||||
return (value != 0) && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
|
||||
// Declaration of fallback functions.
|
||||
int CountLeadingSignBitsFallBack(int64_t value, int width);
|
||||
int CountLeadingZerosFallBack(uint64_t value, int width);
|
||||
int CountSetBitsFallBack(uint64_t value, int width);
|
||||
int CountTrailingZerosFallBack(uint64_t value, int width);
|
||||
|
||||
|
||||
// Implementation of intrinsics functions.
|
||||
// TODO: The implementations could be improved for sizes different from 32bit
|
||||
// and 64bit: we could mask the values and call the appropriate builtin.
|
||||
|
||||
template<typename V>
|
||||
inline int CountLeadingSignBits(V value, int width = (sizeof(V) * 8)) {
|
||||
#if COMPILER_HAS_BUILTIN_CLRSB
|
||||
if (width == 32) {
|
||||
return __builtin_clrsb(value);
|
||||
} else if (width == 64) {
|
||||
return __builtin_clrsbll(value);
|
||||
}
|
||||
#endif
|
||||
return CountLeadingSignBitsFallBack(value, width);
|
||||
}
|
||||
|
||||
|
||||
template<typename V>
|
||||
inline int CountLeadingZeros(V value, int width = (sizeof(V) * 8)) {
|
||||
#if COMPILER_HAS_BUILTIN_CLZ
|
||||
if (width == 32) {
|
||||
return (value == 0) ? 32 : __builtin_clz(static_cast<unsigned>(value));
|
||||
} else if (width == 64) {
|
||||
return (value == 0) ? 64 : __builtin_clzll(value);
|
||||
}
|
||||
#endif
|
||||
return CountLeadingZerosFallBack(value, width);
|
||||
}
|
||||
|
||||
|
||||
template<typename V>
|
||||
inline int CountSetBits(V value, int width = (sizeof(V) * 8)) {
|
||||
#if COMPILER_HAS_BUILTIN_POPCOUNT
|
||||
if (width == 32) {
|
||||
return __builtin_popcount(static_cast<unsigned>(value));
|
||||
} else if (width == 64) {
|
||||
return __builtin_popcountll(value);
|
||||
}
|
||||
#endif
|
||||
return CountSetBitsFallBack(value, width);
|
||||
}
|
||||
|
||||
|
||||
template<typename V>
|
||||
inline int CountTrailingZeros(V value, int width = (sizeof(V) * 8)) {
|
||||
#if COMPILER_HAS_BUILTIN_CTZ
|
||||
if (width == 32) {
|
||||
return (value == 0) ? 32 : __builtin_ctz(static_cast<unsigned>(value));
|
||||
} else if (width == 64) {
|
||||
return (value == 0) ? 64 : __builtin_ctzll(value);
|
||||
}
|
||||
#endif
|
||||
return CountTrailingZerosFallBack(value, width);
|
||||
}
|
||||
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_COMPILER_INTRINSICS_H
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -49,20 +49,26 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include "platform.h"
|
||||
#include "vixl/platform.h"
|
||||
|
||||
|
||||
typedef uint8_t byte;
|
||||
|
||||
// Type for half-precision (16 bit) floating point numbers.
|
||||
typedef uint16_t float16;
|
||||
|
||||
const int KBytes = 1024;
|
||||
const int MBytes = 1024 * KBytes;
|
||||
|
||||
#define VIXL_ABORT() printf("in %s, line %i", __FILE__, __LINE__); abort()
|
||||
#define VIXL_ABORT() \
|
||||
do { printf("in %s, line %i", __FILE__, __LINE__); abort(); } while (false)
|
||||
#ifdef VIXL_DEBUG
|
||||
#define VIXL_ASSERT(condition) assert(condition)
|
||||
#define VIXL_CHECK(condition) VIXL_ASSERT(condition)
|
||||
#define VIXL_UNIMPLEMENTED() printf("UNIMPLEMENTED\t"); VIXL_ABORT()
|
||||
#define VIXL_UNREACHABLE() printf("UNREACHABLE\t"); VIXL_ABORT()
|
||||
#define VIXL_UNIMPLEMENTED() \
|
||||
do { fprintf(stderr, "UNIMPLEMENTED\t"); VIXL_ABORT(); } while (false)
|
||||
#define VIXL_UNREACHABLE() \
|
||||
do { fprintf(stderr, "UNREACHABLE\t"); VIXL_ABORT(); } while (false)
|
||||
#else
|
||||
#define VIXL_ASSERT(condition) ((void) 0)
|
||||
#define VIXL_CHECK(condition) assert(condition)
|
||||
@@ -76,10 +82,70 @@ const int MBytes = 1024 * KBytes;
|
||||
#define VIXL_STATIC_ASSERT_LINE(line, condition) \
|
||||
typedef char VIXL_CONCAT(STATIC_ASSERT_LINE_, line)[(condition) ? 1 : -1] \
|
||||
__attribute__((unused))
|
||||
#define VIXL_STATIC_ASSERT(condition) VIXL_STATIC_ASSERT_LINE(__LINE__, condition) //NOLINT
|
||||
#define VIXL_STATIC_ASSERT(condition) \
|
||||
VIXL_STATIC_ASSERT_LINE(__LINE__, condition)
|
||||
|
||||
template <typename T> inline void USE(T) {}
|
||||
template <typename T1>
|
||||
inline void USE(T1) {}
|
||||
|
||||
#define VIXL_ALIGNMENT_EXCEPTION() printf("ALIGNMENT EXCEPTION\t"); VIXL_ABORT()
|
||||
template <typename T1, typename T2>
|
||||
inline void USE(T1, T2) {}
|
||||
|
||||
template <typename T1, typename T2, typename T3>
|
||||
inline void USE(T1, T2, T3) {}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4>
|
||||
inline void USE(T1, T2, T3, T4) {}
|
||||
|
||||
#define VIXL_ALIGNMENT_EXCEPTION() \
|
||||
do { fprintf(stderr, "ALIGNMENT EXCEPTION\t"); VIXL_ABORT(); } while (0)
|
||||
|
||||
// The clang::fallthrough attribute is used along with the Wimplicit-fallthrough
|
||||
// argument to annotate intentional fall-through between switch labels.
|
||||
// For more information please refer to:
|
||||
// http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
|
||||
#ifndef __has_warning
|
||||
#define __has_warning(x) 0
|
||||
#endif
|
||||
|
||||
// Note: This option is only available for Clang. And will only be enabled for
|
||||
// C++11(201103L).
|
||||
#if __has_warning("-Wimplicit-fallthrough") && __cplusplus >= 201103L
|
||||
#define VIXL_FALLTHROUGH() [[clang::fallthrough]] //NOLINT
|
||||
#else
|
||||
#define VIXL_FALLTHROUGH() do {} while (0)
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#define VIXL_NO_RETURN [[noreturn]] //NOLINT
|
||||
#else
|
||||
#define VIXL_NO_RETURN __attribute__((noreturn))
|
||||
#endif
|
||||
|
||||
// Some functions might only be marked as "noreturn" for the DEBUG build. This
|
||||
// macro should be used for such cases (for more details see what
|
||||
// VIXL_UNREACHABLE expands to).
|
||||
#ifdef VIXL_DEBUG
|
||||
#define VIXL_DEBUG_NO_RETURN VIXL_NO_RETURN
|
||||
#else
|
||||
#define VIXL_DEBUG_NO_RETURN
|
||||
#endif
|
||||
|
||||
#ifdef VIXL_INCLUDE_SIMULATOR
|
||||
#ifndef VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE
|
||||
#define VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE 1
|
||||
#endif
|
||||
#else
|
||||
#ifndef VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE
|
||||
#define VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE 0
|
||||
#endif
|
||||
#if VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE
|
||||
#warning "Generating Simulator instructions without Simulator support."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_SIMULATOR
|
||||
#error "Please see the release notes for USE_SIMULATOR."
|
||||
#endif
|
||||
|
||||
#endif // VIXL_GLOBALS_H
|
775
disas/libvixl/vixl/invalset.h
Normal file
775
disas/libvixl/vixl/invalset.h
Normal file
@@ -0,0 +1,775 @@
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_INVALSET_H_
|
||||
#define VIXL_INVALSET_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "vixl/globals.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
// We define a custom data structure template and its iterator as `std`
|
||||
// containers do not fit the performance requirements for some of our use cases.
|
||||
//
|
||||
// The structure behaves like an iterable unordered set with special properties
|
||||
// and restrictions. "InvalSet" stands for "Invalidatable Set".
|
||||
//
|
||||
// Restrictions and requirements:
|
||||
// - Adding an element already present in the set is illegal. In debug mode,
|
||||
// this is checked at insertion time.
|
||||
// - The templated class `ElementType` must provide comparison operators so that
|
||||
// `std::sort()` can be used.
|
||||
// - A key must be available to represent invalid elements.
|
||||
// - Elements with an invalid key must compare higher or equal to any other
|
||||
// element.
|
||||
//
|
||||
// Use cases and performance considerations:
|
||||
// Our use cases present two specificities that allow us to design this
|
||||
// structure to provide fast insertion *and* fast search and deletion
|
||||
// operations:
|
||||
// - Elements are (generally) inserted in order (sorted according to their key).
|
||||
// - A key is available to mark elements as invalid (deleted).
|
||||
// The backing `std::vector` allows for fast insertions. When
|
||||
// searching for an element we ensure the elements are sorted (this is generally
|
||||
// the case) and perform a binary search. When deleting an element we do not
|
||||
// free the associated memory immediately. Instead, an element to be deleted is
|
||||
// marked with the 'invalid' key. Other methods of the container take care of
|
||||
// ignoring entries marked as invalid.
|
||||
// To avoid the overhead of the `std::vector` container when only few entries
|
||||
// are used, a number of elements are preallocated.
|
||||
|
||||
// 'ElementType' and 'KeyType' are respectively the types of the elements and
|
||||
// their key. The structure only reclaims memory when safe to do so, if the
|
||||
// number of elements that can be reclaimed is greater than `RECLAIM_FROM` and
|
||||
// greater than `<total number of elements> / RECLAIM_FACTOR.
|
||||
#define TEMPLATE_INVALSET_P_DECL \
|
||||
class ElementType, \
|
||||
unsigned N_PREALLOCATED_ELEMENTS, \
|
||||
class KeyType, \
|
||||
KeyType INVALID_KEY, \
|
||||
size_t RECLAIM_FROM, \
|
||||
unsigned RECLAIM_FACTOR
|
||||
|
||||
#define TEMPLATE_INVALSET_P_DEF \
|
||||
ElementType, N_PREALLOCATED_ELEMENTS, \
|
||||
KeyType, INVALID_KEY, RECLAIM_FROM, RECLAIM_FACTOR
|
||||
|
||||
template<class S> class InvalSetIterator; // Forward declaration.
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL> class InvalSet {
|
||||
public:
|
||||
InvalSet();
|
||||
~InvalSet();
|
||||
|
||||
static const size_t kNPreallocatedElements = N_PREALLOCATED_ELEMENTS;
|
||||
static const KeyType kInvalidKey = INVALID_KEY;
|
||||
|
||||
// It is illegal to insert an element already present in the set.
|
||||
void insert(const ElementType& element);
|
||||
|
||||
// Looks for the specified element in the set and - if found - deletes it.
|
||||
void erase(const ElementType& element);
|
||||
|
||||
// This indicates the number of (valid) elements stored in this set.
|
||||
size_t size() const;
|
||||
|
||||
// Returns true if no elements are stored in the set.
|
||||
// Note that this does not mean the the backing storage is empty: it can still
|
||||
// contain invalid elements.
|
||||
bool empty() const;
|
||||
|
||||
void clear();
|
||||
|
||||
const ElementType min_element();
|
||||
|
||||
// This returns the key of the minimum element in the set.
|
||||
KeyType min_element_key();
|
||||
|
||||
static bool IsValid(const ElementType& element);
|
||||
static KeyType Key(const ElementType& element);
|
||||
static void SetKey(ElementType* element, KeyType key);
|
||||
|
||||
protected:
|
||||
// Returns a pointer to the element in vector_ if it was found, or NULL
|
||||
// otherwise.
|
||||
ElementType* Search(const ElementType& element);
|
||||
|
||||
// The argument *must* point to an element stored in *this* set.
|
||||
// This function is not allowed to move elements in the backing vector
|
||||
// storage.
|
||||
void EraseInternal(ElementType* element);
|
||||
|
||||
// The elements in the range searched must be sorted.
|
||||
ElementType* BinarySearch(const ElementType& element,
|
||||
ElementType* start,
|
||||
ElementType* end) const;
|
||||
|
||||
// Sort the elements.
|
||||
enum SortType {
|
||||
// The 'hard' version guarantees that invalid elements are moved to the end
|
||||
// of the container.
|
||||
kHardSort,
|
||||
// The 'soft' version only guarantees that the elements will be sorted.
|
||||
// Invalid elements may still be present anywhere in the set.
|
||||
kSoftSort
|
||||
};
|
||||
void Sort(SortType sort_type);
|
||||
|
||||
// Delete the elements that have an invalid key. The complexity is linear
|
||||
// with the size of the vector.
|
||||
void Clean();
|
||||
|
||||
const ElementType Front() const;
|
||||
const ElementType Back() const;
|
||||
|
||||
// Delete invalid trailing elements and return the last valid element in the
|
||||
// set.
|
||||
const ElementType CleanBack();
|
||||
|
||||
// Returns a pointer to the start or end of the backing storage.
|
||||
const ElementType* StorageBegin() const;
|
||||
const ElementType* StorageEnd() const;
|
||||
ElementType* StorageBegin();
|
||||
ElementType* StorageEnd();
|
||||
|
||||
// Returns the index of the element within the backing storage. The element
|
||||
// must belong to the backing storage.
|
||||
size_t ElementIndex(const ElementType* element) const;
|
||||
|
||||
// Returns the element at the specified index in the backing storage.
|
||||
const ElementType* ElementAt(size_t index) const;
|
||||
ElementType* ElementAt(size_t index);
|
||||
|
||||
static const ElementType* FirstValidElement(const ElementType* from,
|
||||
const ElementType* end);
|
||||
|
||||
void CacheMinElement();
|
||||
const ElementType CachedMinElement() const;
|
||||
|
||||
bool ShouldReclaimMemory() const;
|
||||
void ReclaimMemory();
|
||||
|
||||
bool IsUsingVector() const { return vector_ != NULL; }
|
||||
void set_sorted(bool sorted) { sorted_ = sorted; }
|
||||
|
||||
// We cache some data commonly required by users to improve performance.
|
||||
// We cannot cache pointers to elements as we do not control the backing
|
||||
// storage.
|
||||
bool valid_cached_min_;
|
||||
size_t cached_min_index_; // Valid iff `valid_cached_min_` is true.
|
||||
KeyType cached_min_key_; // Valid iff `valid_cached_min_` is true.
|
||||
|
||||
// Indicates whether the elements are sorted.
|
||||
bool sorted_;
|
||||
|
||||
// This represents the number of (valid) elements in this set.
|
||||
size_t size_;
|
||||
|
||||
// The backing storage is either the array of preallocated elements or the
|
||||
// vector. The structure starts by using the preallocated elements, and
|
||||
// transitions (permanently) to using the vector once more than
|
||||
// kNPreallocatedElements are used.
|
||||
// Elements are only invalidated when using the vector. The preallocated
|
||||
// storage always only contains valid elements.
|
||||
ElementType preallocated_[kNPreallocatedElements];
|
||||
std::vector<ElementType>* vector_;
|
||||
|
||||
#ifdef VIXL_DEBUG
|
||||
// Iterators acquire and release this monitor. While a set is acquired,
|
||||
// certain operations are illegal to ensure that the iterator will
|
||||
// correctly iterate over the elements in the set.
|
||||
int monitor_;
|
||||
int monitor() const { return monitor_; }
|
||||
void Acquire() { monitor_++; }
|
||||
void Release() {
|
||||
monitor_--;
|
||||
VIXL_ASSERT(monitor_ >= 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
friend class InvalSetIterator<InvalSet<TEMPLATE_INVALSET_P_DEF> >;
|
||||
typedef ElementType _ElementType;
|
||||
typedef KeyType _KeyType;
|
||||
};
|
||||
|
||||
|
||||
template<class S> class InvalSetIterator {
|
||||
private:
|
||||
// Redefine types to mirror the associated set types.
|
||||
typedef typename S::_ElementType ElementType;
|
||||
typedef typename S::_KeyType KeyType;
|
||||
|
||||
public:
|
||||
explicit InvalSetIterator(S* inval_set);
|
||||
~InvalSetIterator();
|
||||
|
||||
ElementType* Current() const;
|
||||
void Advance();
|
||||
bool Done() const;
|
||||
|
||||
// Mark this iterator as 'done'.
|
||||
void Finish();
|
||||
|
||||
// Delete the current element and advance the iterator to point to the next
|
||||
// element.
|
||||
void DeleteCurrentAndAdvance();
|
||||
|
||||
static bool IsValid(const ElementType& element);
|
||||
static KeyType Key(const ElementType& element);
|
||||
|
||||
protected:
|
||||
void MoveToValidElement();
|
||||
|
||||
// Indicates if the iterator is looking at the vector or at the preallocated
|
||||
// elements.
|
||||
const bool using_vector_;
|
||||
// Used when looking at the preallocated elements, or in debug mode when using
|
||||
// the vector to track how many times the iterator has advanced.
|
||||
size_t index_;
|
||||
typename std::vector<ElementType>::iterator iterator_;
|
||||
S* inval_set_;
|
||||
};
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
InvalSet<TEMPLATE_INVALSET_P_DEF>::InvalSet()
|
||||
: valid_cached_min_(false),
|
||||
sorted_(true), size_(0), vector_(NULL) {
|
||||
#ifdef VIXL_DEBUG
|
||||
monitor_ = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
InvalSet<TEMPLATE_INVALSET_P_DEF>::~InvalSet() {
|
||||
VIXL_ASSERT(monitor_ == 0);
|
||||
delete vector_;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
void InvalSet<TEMPLATE_INVALSET_P_DEF>::insert(const ElementType& element) {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
VIXL_ASSERT(IsValid(element));
|
||||
VIXL_ASSERT(Search(element) == NULL);
|
||||
set_sorted(empty() || (sorted_ && (element > CleanBack())));
|
||||
if (IsUsingVector()) {
|
||||
vector_->push_back(element);
|
||||
} else {
|
||||
if (size_ < kNPreallocatedElements) {
|
||||
preallocated_[size_] = element;
|
||||
} else {
|
||||
// Transition to using the vector.
|
||||
vector_ = new std::vector<ElementType>(preallocated_,
|
||||
preallocated_ + size_);
|
||||
vector_->push_back(element);
|
||||
}
|
||||
}
|
||||
size_++;
|
||||
|
||||
if (valid_cached_min_ && (element < min_element())) {
|
||||
cached_min_index_ = IsUsingVector() ? vector_->size() - 1 : size_ - 1;
|
||||
cached_min_key_ = Key(element);
|
||||
valid_cached_min_ = true;
|
||||
}
|
||||
|
||||
if (ShouldReclaimMemory()) {
|
||||
ReclaimMemory();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
void InvalSet<TEMPLATE_INVALSET_P_DEF>::erase(const ElementType& element) {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
VIXL_ASSERT(IsValid(element));
|
||||
ElementType* local_element = Search(element);
|
||||
if (local_element != NULL) {
|
||||
EraseInternal(local_element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::Search(
|
||||
const ElementType& element) {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
if (empty()) {
|
||||
return NULL;
|
||||
}
|
||||
if (ShouldReclaimMemory()) {
|
||||
ReclaimMemory();
|
||||
}
|
||||
if (!sorted_) {
|
||||
Sort(kHardSort);
|
||||
}
|
||||
if (!valid_cached_min_) {
|
||||
CacheMinElement();
|
||||
}
|
||||
return BinarySearch(element, ElementAt(cached_min_index_), StorageEnd());
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
size_t InvalSet<TEMPLATE_INVALSET_P_DEF>::size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
bool InvalSet<TEMPLATE_INVALSET_P_DEF>::empty() const {
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
void InvalSet<TEMPLATE_INVALSET_P_DEF>::clear() {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
size_ = 0;
|
||||
if (IsUsingVector()) {
|
||||
vector_->clear();
|
||||
}
|
||||
set_sorted(true);
|
||||
valid_cached_min_ = false;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
const ElementType InvalSet<TEMPLATE_INVALSET_P_DEF>::min_element() {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
VIXL_ASSERT(!empty());
|
||||
CacheMinElement();
|
||||
return *ElementAt(cached_min_index_);
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
KeyType InvalSet<TEMPLATE_INVALSET_P_DEF>::min_element_key() {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
if (valid_cached_min_) {
|
||||
return cached_min_key_;
|
||||
} else {
|
||||
return Key(min_element());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
bool InvalSet<TEMPLATE_INVALSET_P_DEF>::IsValid(const ElementType& element) {
|
||||
return Key(element) != kInvalidKey;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
void InvalSet<TEMPLATE_INVALSET_P_DEF>::EraseInternal(ElementType* element) {
|
||||
// Note that this function must be safe even while an iterator has acquired
|
||||
// this set.
|
||||
VIXL_ASSERT(element != NULL);
|
||||
size_t deleted_index = ElementIndex(element);
|
||||
if (IsUsingVector()) {
|
||||
VIXL_ASSERT((&(vector_->front()) <= element) &&
|
||||
(element <= &(vector_->back())));
|
||||
SetKey(element, kInvalidKey);
|
||||
} else {
|
||||
VIXL_ASSERT((preallocated_ <= element) &&
|
||||
(element < (preallocated_ + kNPreallocatedElements)));
|
||||
ElementType* end = preallocated_ + kNPreallocatedElements;
|
||||
size_t copy_size = sizeof(*element) * (end - element - 1);
|
||||
memmove(element, element + 1, copy_size);
|
||||
}
|
||||
size_--;
|
||||
|
||||
if (valid_cached_min_ &&
|
||||
(deleted_index == cached_min_index_)) {
|
||||
if (sorted_ && !empty()) {
|
||||
const ElementType* min = FirstValidElement(element, StorageEnd());
|
||||
cached_min_index_ = ElementIndex(min);
|
||||
cached_min_key_ = Key(*min);
|
||||
valid_cached_min_ = true;
|
||||
} else {
|
||||
valid_cached_min_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::BinarySearch(
|
||||
const ElementType& element, ElementType* start, ElementType* end) const {
|
||||
if (start == end) {
|
||||
return NULL;
|
||||
}
|
||||
VIXL_ASSERT(sorted_);
|
||||
VIXL_ASSERT(start < end);
|
||||
VIXL_ASSERT(!empty());
|
||||
|
||||
// Perform a binary search through the elements while ignoring invalid
|
||||
// elements.
|
||||
ElementType* elements = start;
|
||||
size_t low = 0;
|
||||
size_t high = (end - start) - 1;
|
||||
while (low < high) {
|
||||
// Find valid bounds.
|
||||
while (!IsValid(elements[low]) && (low < high)) ++low;
|
||||
while (!IsValid(elements[high]) && (low < high)) --high;
|
||||
VIXL_ASSERT(low <= high);
|
||||
// Avoid overflow when computing the middle index.
|
||||
size_t middle = low / 2 + high / 2 + (low & high & 1);
|
||||
if ((middle == low) || (middle == high)) {
|
||||
break;
|
||||
}
|
||||
while (!IsValid(elements[middle]) && (middle < high - 1)) ++middle;
|
||||
while (!IsValid(elements[middle]) && (low + 1 < middle)) --middle;
|
||||
if (!IsValid(elements[middle])) {
|
||||
break;
|
||||
}
|
||||
if (elements[middle] < element) {
|
||||
low = middle;
|
||||
} else {
|
||||
high = middle;
|
||||
}
|
||||
}
|
||||
|
||||
if (elements[low] == element) return &elements[low];
|
||||
if (elements[high] == element) return &elements[high];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
void InvalSet<TEMPLATE_INVALSET_P_DEF>::Sort(SortType sort_type) {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
if (sort_type == kSoftSort) {
|
||||
if (sorted_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Clean();
|
||||
std::sort(StorageBegin(), StorageEnd());
|
||||
|
||||
set_sorted(true);
|
||||
cached_min_index_ = 0;
|
||||
cached_min_key_ = Key(Front());
|
||||
valid_cached_min_ = true;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
void InvalSet<TEMPLATE_INVALSET_P_DEF>::Clean() {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
if (empty() || !IsUsingVector()) {
|
||||
return;
|
||||
}
|
||||
// Manually iterate through the vector storage to discard invalid elements.
|
||||
ElementType* start = &(vector_->front());
|
||||
ElementType* end = start + vector_->size();
|
||||
ElementType* c = start;
|
||||
ElementType* first_invalid;
|
||||
ElementType* first_valid;
|
||||
ElementType* next_invalid;
|
||||
|
||||
while (c < end && IsValid(*c)) { c++; }
|
||||
first_invalid = c;
|
||||
|
||||
while (c < end) {
|
||||
while (c < end && !IsValid(*c)) { c++; }
|
||||
first_valid = c;
|
||||
while (c < end && IsValid(*c)) { c++; }
|
||||
next_invalid = c;
|
||||
|
||||
ptrdiff_t n_moved_elements = (next_invalid - first_valid);
|
||||
memmove(first_invalid, first_valid, n_moved_elements * sizeof(*c));
|
||||
first_invalid = first_invalid + n_moved_elements;
|
||||
c = next_invalid;
|
||||
}
|
||||
|
||||
// Delete the trailing invalid elements.
|
||||
vector_->erase(vector_->begin() + (first_invalid - start), vector_->end());
|
||||
VIXL_ASSERT(vector_->size() == size_);
|
||||
|
||||
if (sorted_) {
|
||||
valid_cached_min_ = true;
|
||||
cached_min_index_ = 0;
|
||||
cached_min_key_ = Key(*ElementAt(0));
|
||||
} else {
|
||||
valid_cached_min_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
const ElementType InvalSet<TEMPLATE_INVALSET_P_DEF>::Front() const {
|
||||
VIXL_ASSERT(!empty());
|
||||
return IsUsingVector() ? vector_->front() : preallocated_[0];
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
const ElementType InvalSet<TEMPLATE_INVALSET_P_DEF>::Back() const {
|
||||
VIXL_ASSERT(!empty());
|
||||
return IsUsingVector() ? vector_->back() : preallocated_[size_ - 1];
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
const ElementType InvalSet<TEMPLATE_INVALSET_P_DEF>::CleanBack() {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
if (IsUsingVector()) {
|
||||
// Delete the invalid trailing elements.
|
||||
typename std::vector<ElementType>::reverse_iterator it = vector_->rbegin();
|
||||
while (!IsValid(*it)) {
|
||||
it++;
|
||||
}
|
||||
vector_->erase(it.base(), vector_->end());
|
||||
}
|
||||
return Back();
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
const ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::StorageBegin() const {
|
||||
return IsUsingVector() ? &(vector_->front()) : preallocated_;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
const ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::StorageEnd() const {
|
||||
return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::StorageBegin() {
|
||||
return IsUsingVector() ? &(vector_->front()) : preallocated_;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::StorageEnd() {
|
||||
return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
size_t InvalSet<TEMPLATE_INVALSET_P_DEF>::ElementIndex(
|
||||
const ElementType* element) const {
|
||||
VIXL_ASSERT((StorageBegin() <= element) && (element < StorageEnd()));
|
||||
return element - StorageBegin();
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
const ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::ElementAt(
|
||||
size_t index) const {
|
||||
VIXL_ASSERT(
|
||||
(IsUsingVector() && (index < vector_->size())) || (index < size_));
|
||||
return StorageBegin() + index;
|
||||
}
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::ElementAt(size_t index) {
|
||||
VIXL_ASSERT(
|
||||
(IsUsingVector() && (index < vector_->size())) || (index < size_));
|
||||
return StorageBegin() + index;
|
||||
}
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
const ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::FirstValidElement(
|
||||
const ElementType* from, const ElementType* end) {
|
||||
while ((from < end) && !IsValid(*from)) {
|
||||
from++;
|
||||
}
|
||||
return from;
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
void InvalSet<TEMPLATE_INVALSET_P_DEF>::CacheMinElement() {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
VIXL_ASSERT(!empty());
|
||||
|
||||
if (valid_cached_min_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sorted_) {
|
||||
const ElementType* min = FirstValidElement(StorageBegin(), StorageEnd());
|
||||
cached_min_index_ = ElementIndex(min);
|
||||
cached_min_key_ = Key(*min);
|
||||
valid_cached_min_ = true;
|
||||
} else {
|
||||
Sort(kHardSort);
|
||||
}
|
||||
VIXL_ASSERT(valid_cached_min_);
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
bool InvalSet<TEMPLATE_INVALSET_P_DEF>::ShouldReclaimMemory() const {
|
||||
if (!IsUsingVector()) {
|
||||
return false;
|
||||
}
|
||||
size_t n_invalid_elements = vector_->size() - size_;
|
||||
return (n_invalid_elements > RECLAIM_FROM) &&
|
||||
(n_invalid_elements > vector_->size() / RECLAIM_FACTOR);
|
||||
}
|
||||
|
||||
|
||||
template<TEMPLATE_INVALSET_P_DECL>
|
||||
void InvalSet<TEMPLATE_INVALSET_P_DEF>::ReclaimMemory() {
|
||||
VIXL_ASSERT(monitor() == 0);
|
||||
Clean();
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
InvalSetIterator<S>::InvalSetIterator(S* inval_set)
|
||||
: using_vector_((inval_set != NULL) && inval_set->IsUsingVector()),
|
||||
index_(0),
|
||||
inval_set_(inval_set) {
|
||||
if (inval_set != NULL) {
|
||||
inval_set->Sort(S::kSoftSort);
|
||||
#ifdef VIXL_DEBUG
|
||||
inval_set->Acquire();
|
||||
#endif
|
||||
if (using_vector_) {
|
||||
iterator_ = typename std::vector<ElementType>::iterator(
|
||||
inval_set_->vector_->begin());
|
||||
}
|
||||
MoveToValidElement();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
InvalSetIterator<S>::~InvalSetIterator() {
|
||||
#ifdef VIXL_DEBUG
|
||||
if (inval_set_ != NULL) {
|
||||
inval_set_->Release();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
typename S::_ElementType* InvalSetIterator<S>::Current() const {
|
||||
VIXL_ASSERT(!Done());
|
||||
if (using_vector_) {
|
||||
return &(*iterator_);
|
||||
} else {
|
||||
return &(inval_set_->preallocated_[index_]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
void InvalSetIterator<S>::Advance() {
|
||||
VIXL_ASSERT(!Done());
|
||||
if (using_vector_) {
|
||||
iterator_++;
|
||||
#ifdef VIXL_DEBUG
|
||||
index_++;
|
||||
#endif
|
||||
MoveToValidElement();
|
||||
} else {
|
||||
index_++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
bool InvalSetIterator<S>::Done() const {
|
||||
if (using_vector_) {
|
||||
bool done = (iterator_ == inval_set_->vector_->end());
|
||||
VIXL_ASSERT(done == (index_ == inval_set_->size()));
|
||||
return done;
|
||||
} else {
|
||||
return index_ == inval_set_->size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
void InvalSetIterator<S>::Finish() {
|
||||
VIXL_ASSERT(inval_set_->sorted_);
|
||||
if (using_vector_) {
|
||||
iterator_ = inval_set_->vector_->end();
|
||||
}
|
||||
index_ = inval_set_->size();
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
void InvalSetIterator<S>::DeleteCurrentAndAdvance() {
|
||||
if (using_vector_) {
|
||||
inval_set_->EraseInternal(&(*iterator_));
|
||||
MoveToValidElement();
|
||||
} else {
|
||||
inval_set_->EraseInternal(inval_set_->preallocated_ + index_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
bool InvalSetIterator<S>::IsValid(const ElementType& element) {
|
||||
return S::IsValid(element);
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
typename S::_KeyType InvalSetIterator<S>::Key(const ElementType& element) {
|
||||
return S::Key(element);
|
||||
}
|
||||
|
||||
|
||||
template<class S>
|
||||
void InvalSetIterator<S>::MoveToValidElement() {
|
||||
if (using_vector_) {
|
||||
while ((iterator_ != inval_set_->vector_->end()) && !IsValid(*iterator_)) {
|
||||
iterator_++;
|
||||
}
|
||||
} else {
|
||||
VIXL_ASSERT(inval_set_->empty() || IsValid(inval_set_->preallocated_[0]));
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
#undef TEMPLATE_INVALSET_P_DECL
|
||||
#undef TEMPLATE_INVALSET_P_DEF
|
||||
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_INVALSET_H_
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// Copyright 2014, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
142
disas/libvixl/vixl/utils.cc
Normal file
142
disas/libvixl/vixl/utils.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "vixl/utils.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace vixl {
|
||||
|
||||
uint32_t float_to_rawbits(float value) {
|
||||
uint32_t bits = 0;
|
||||
memcpy(&bits, &value, 4);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
uint64_t double_to_rawbits(double value) {
|
||||
uint64_t bits = 0;
|
||||
memcpy(&bits, &value, 8);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
float rawbits_to_float(uint32_t bits) {
|
||||
float value = 0.0;
|
||||
memcpy(&value, &bits, 4);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
double rawbits_to_double(uint64_t bits) {
|
||||
double value = 0.0;
|
||||
memcpy(&value, &bits, 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
uint32_t float_sign(float val) {
|
||||
uint32_t rawbits = float_to_rawbits(val);
|
||||
return unsigned_bitextract_32(31, 31, rawbits);
|
||||
}
|
||||
|
||||
|
||||
uint32_t float_exp(float val) {
|
||||
uint32_t rawbits = float_to_rawbits(val);
|
||||
return unsigned_bitextract_32(30, 23, rawbits);
|
||||
}
|
||||
|
||||
|
||||
uint32_t float_mantissa(float val) {
|
||||
uint32_t rawbits = float_to_rawbits(val);
|
||||
return unsigned_bitextract_32(22, 0, rawbits);
|
||||
}
|
||||
|
||||
|
||||
uint32_t double_sign(double val) {
|
||||
uint64_t rawbits = double_to_rawbits(val);
|
||||
return static_cast<uint32_t>(unsigned_bitextract_64(63, 63, rawbits));
|
||||
}
|
||||
|
||||
|
||||
uint32_t double_exp(double val) {
|
||||
uint64_t rawbits = double_to_rawbits(val);
|
||||
return static_cast<uint32_t>(unsigned_bitextract_64(62, 52, rawbits));
|
||||
}
|
||||
|
||||
|
||||
uint64_t double_mantissa(double val) {
|
||||
uint64_t rawbits = double_to_rawbits(val);
|
||||
return unsigned_bitextract_64(51, 0, rawbits);
|
||||
}
|
||||
|
||||
|
||||
float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa) {
|
||||
uint32_t bits = (sign << 31) | (exp << 23) | mantissa;
|
||||
return rawbits_to_float(bits);
|
||||
}
|
||||
|
||||
|
||||
double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa) {
|
||||
uint64_t bits = (sign << 63) | (exp << 52) | mantissa;
|
||||
return rawbits_to_double(bits);
|
||||
}
|
||||
|
||||
|
||||
int float16classify(float16 value) {
|
||||
uint16_t exponent_max = (1 << 5) - 1;
|
||||
uint16_t exponent_mask = exponent_max << 10;
|
||||
uint16_t mantissa_mask = (1 << 10) - 1;
|
||||
|
||||
uint16_t exponent = (value & exponent_mask) >> 10;
|
||||
uint16_t mantissa = value & mantissa_mask;
|
||||
if (exponent == 0) {
|
||||
if (mantissa == 0) {
|
||||
return FP_ZERO;
|
||||
}
|
||||
return FP_SUBNORMAL;
|
||||
} else if (exponent == exponent_max) {
|
||||
if (mantissa == 0) {
|
||||
return FP_INFINITE;
|
||||
}
|
||||
return FP_NAN;
|
||||
}
|
||||
return FP_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) {
|
||||
VIXL_ASSERT((reg_size % 8) == 0);
|
||||
int count = 0;
|
||||
for (unsigned i = 0; i < (reg_size / 16); i++) {
|
||||
if ((imm & 0xffff) == 0) {
|
||||
count++;
|
||||
}
|
||||
imm >>= 16;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace vixl
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013, ARM Limited
|
||||
// Copyright 2015, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -27,16 +27,17 @@
|
||||
#ifndef VIXL_UTILS_H
|
||||
#define VIXL_UTILS_H
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "globals.h"
|
||||
#include <cmath>
|
||||
#include "vixl/globals.h"
|
||||
#include "vixl/compiler-intrinsics.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
// Macros for compile-time format checking.
|
||||
#if defined(__GNUC__)
|
||||
#if GCC_VERSION_OR_NEWER(4, 4, 0)
|
||||
#define PRINTF_CHECK(format_index, varargs_index) \
|
||||
__attribute__((format(printf, format_index, varargs_index)))
|
||||
__attribute__((format(gnu_printf, format_index, varargs_index)))
|
||||
#else
|
||||
#define PRINTF_CHECK(format_index, varargs_index)
|
||||
#endif
|
||||
@@ -53,9 +54,9 @@ inline bool is_uintn(unsigned n, int64_t x) {
|
||||
return !(x >> n);
|
||||
}
|
||||
|
||||
inline unsigned truncate_to_intn(unsigned n, int64_t x) {
|
||||
inline uint32_t truncate_to_intn(unsigned n, int64_t x) {
|
||||
VIXL_ASSERT((0 < n) && (n < 64));
|
||||
return (x & ((INT64_C(1) << n) - 1));
|
||||
return static_cast<uint32_t>(x & ((INT64_C(1) << n) - 1));
|
||||
}
|
||||
|
||||
#define INT_1_TO_63_LIST(V) \
|
||||
@@ -73,7 +74,7 @@ inline bool is_int##N(int64_t x) { return is_intn(N, x); }
|
||||
#define DECLARE_IS_UINT_N(N) \
|
||||
inline bool is_uint##N(int64_t x) { return is_uintn(N, x); }
|
||||
#define DECLARE_TRUNCATE_TO_INT_N(N) \
|
||||
inline int truncate_to_int##N(int x) { return truncate_to_intn(N, x); }
|
||||
inline uint32_t truncate_to_int##N(int x) { return truncate_to_intn(N, x); }
|
||||
INT_1_TO_63_LIST(DECLARE_IS_INT_N)
|
||||
INT_1_TO_63_LIST(DECLARE_IS_UINT_N)
|
||||
INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N)
|
||||
@@ -104,12 +105,24 @@ uint64_t double_to_rawbits(double value);
|
||||
float rawbits_to_float(uint32_t bits);
|
||||
double rawbits_to_double(uint64_t bits);
|
||||
|
||||
uint32_t float_sign(float val);
|
||||
uint32_t float_exp(float val);
|
||||
uint32_t float_mantissa(float val);
|
||||
uint32_t double_sign(double val);
|
||||
uint32_t double_exp(double val);
|
||||
uint64_t double_mantissa(double val);
|
||||
|
||||
float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa);
|
||||
double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa);
|
||||
|
||||
// An fpclassify() function for 16-bit half-precision floats.
|
||||
int float16classify(float16 value);
|
||||
|
||||
// NaN tests.
|
||||
inline bool IsSignallingNaN(double num) {
|
||||
const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000);
|
||||
uint64_t raw = double_to_rawbits(num);
|
||||
if (isnan(num) && ((raw & kFP64QuietNaNMask) == 0)) {
|
||||
if (std::isnan(num) && ((raw & kFP64QuietNaNMask) == 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -119,30 +132,37 @@ inline bool IsSignallingNaN(double num) {
|
||||
inline bool IsSignallingNaN(float num) {
|
||||
const uint32_t kFP32QuietNaNMask = 0x00400000;
|
||||
uint32_t raw = float_to_rawbits(num);
|
||||
if (isnan(num) && ((raw & kFP32QuietNaNMask) == 0)) {
|
||||
if (std::isnan(num) && ((raw & kFP32QuietNaNMask) == 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
inline bool IsSignallingNaN(float16 num) {
|
||||
const uint16_t kFP16QuietNaNMask = 0x0200;
|
||||
return (float16classify(num) == FP_NAN) &&
|
||||
((num & kFP16QuietNaNMask) == 0);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline bool IsQuietNaN(T num) {
|
||||
return isnan(num) && !IsSignallingNaN(num);
|
||||
return std::isnan(num) && !IsSignallingNaN(num);
|
||||
}
|
||||
|
||||
|
||||
// Convert the NaN in 'num' to a quiet NaN.
|
||||
inline double ToQuietNaN(double num) {
|
||||
const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000);
|
||||
VIXL_ASSERT(isnan(num));
|
||||
VIXL_ASSERT(std::isnan(num));
|
||||
return rawbits_to_double(double_to_rawbits(num) | kFP64QuietNaNMask);
|
||||
}
|
||||
|
||||
|
||||
inline float ToQuietNaN(float num) {
|
||||
const uint32_t kFP32QuietNaNMask = 0x00400000;
|
||||
VIXL_ASSERT(isnan(num));
|
||||
VIXL_ASSERT(std::isnan(num));
|
||||
return rawbits_to_float(float_to_rawbits(num) | kFP32QuietNaNMask);
|
||||
}
|
||||
|
||||
@@ -158,16 +178,71 @@ inline float FusedMultiplyAdd(float op1, float op2, float a) {
|
||||
}
|
||||
|
||||
|
||||
// Bit counting.
|
||||
int CountLeadingZeros(uint64_t value, int width);
|
||||
int CountLeadingSignBits(int64_t value, int width);
|
||||
int CountTrailingZeros(uint64_t value, int width);
|
||||
int CountSetBits(uint64_t value, int width);
|
||||
uint64_t LowestSetBit(uint64_t value);
|
||||
bool IsPowerOf2(int64_t value);
|
||||
inline uint64_t LowestSetBit(uint64_t value) {
|
||||
return value & -value;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline int HighestSetBitPosition(T value) {
|
||||
VIXL_ASSERT(value != 0);
|
||||
return (sizeof(value) * 8 - 1) - CountLeadingZeros(value);
|
||||
}
|
||||
|
||||
|
||||
template<typename V>
|
||||
inline int WhichPowerOf2(V value) {
|
||||
VIXL_ASSERT(IsPowerOf2(value));
|
||||
return CountTrailingZeros(value);
|
||||
}
|
||||
|
||||
|
||||
unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size);
|
||||
|
||||
|
||||
template <typename T>
|
||||
T ReverseBits(T value) {
|
||||
VIXL_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) ||
|
||||
(sizeof(value) == 4) || (sizeof(value) == 8));
|
||||
T result = 0;
|
||||
for (unsigned i = 0; i < (sizeof(value) * 8); i++) {
|
||||
result = (result << 1) | (value & 1);
|
||||
value >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
T ReverseBytes(T value, int block_bytes_log2) {
|
||||
VIXL_ASSERT((sizeof(value) == 4) || (sizeof(value) == 8));
|
||||
VIXL_ASSERT((1U << block_bytes_log2) <= sizeof(value));
|
||||
// Split the 64-bit value into an 8-bit array, where b[0] is the least
|
||||
// significant byte, and b[7] is the most significant.
|
||||
uint8_t bytes[8];
|
||||
uint64_t mask = UINT64_C(0xff00000000000000);
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
bytes[i] = (static_cast<uint64_t>(value) & mask) >> (i * 8);
|
||||
mask >>= 8;
|
||||
}
|
||||
|
||||
// Permutation tables for REV instructions.
|
||||
// permute_table[0] is used by REV16_x, REV16_w
|
||||
// permute_table[1] is used by REV32_x, REV_w
|
||||
// permute_table[2] is used by REV_x
|
||||
VIXL_ASSERT((0 < block_bytes_log2) && (block_bytes_log2 < 4));
|
||||
static const uint8_t permute_table[3][8] = { {6, 7, 4, 5, 2, 3, 0, 1},
|
||||
{4, 5, 6, 7, 0, 1, 2, 3},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7} };
|
||||
T result = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
result <<= 8;
|
||||
result |= bytes[permute_table[block_bytes_log2 - 1][i]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Pointer alignment
|
||||
// TODO: rename/refactor to make it specific to instructions.
|
||||
template<typename T>
|
@@ -496,6 +496,20 @@ Example:
|
||||
{"timestamp": {"seconds": 1432121972, "microseconds": 744001},
|
||||
"event": "MIGRATION", "data": {"status": "completed"}}
|
||||
|
||||
MIGRATION_PASS
|
||||
--------------
|
||||
|
||||
Emitted from the source side of a migration at the start of each pass
|
||||
(when it syncs the dirty bitmap)
|
||||
|
||||
Data: None.
|
||||
|
||||
- "pass": An incrementing count (starting at 1 on the first pass)
|
||||
|
||||
Example:
|
||||
{"timestamp": {"seconds": 1449669631, "microseconds": 239225},
|
||||
"event": "MIGRATION_PASS", "data": {"pass": 2}}
|
||||
|
||||
STOP
|
||||
----
|
||||
|
||||
|
131
dump.c
131
dump.c
@@ -347,18 +347,18 @@ static void write_memory(DumpState *s, GuestPhysBlock *block, ram_addr_t start,
|
||||
int64_t i;
|
||||
Error *local_err = NULL;
|
||||
|
||||
for (i = 0; i < size / TARGET_PAGE_SIZE; i++) {
|
||||
write_data(s, block->host_addr + start + i * TARGET_PAGE_SIZE,
|
||||
TARGET_PAGE_SIZE, &local_err);
|
||||
for (i = 0; i < size / s->dump_info.page_size; i++) {
|
||||
write_data(s, block->host_addr + start + i * s->dump_info.page_size,
|
||||
s->dump_info.page_size, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((size % TARGET_PAGE_SIZE) != 0) {
|
||||
write_data(s, block->host_addr + start + i * TARGET_PAGE_SIZE,
|
||||
size % TARGET_PAGE_SIZE, &local_err);
|
||||
if ((size % s->dump_info.page_size) != 0) {
|
||||
write_data(s, block->host_addr + start + i * s->dump_info.page_size,
|
||||
size % s->dump_info.page_size, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -737,7 +737,7 @@ static void create_header32(DumpState *s, Error **errp)
|
||||
|
||||
strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE));
|
||||
dh->header_version = cpu_to_dump32(s, 6);
|
||||
block_size = TARGET_PAGE_SIZE;
|
||||
block_size = s->dump_info.page_size;
|
||||
dh->block_size = cpu_to_dump32(s, block_size);
|
||||
sub_hdr_size = sizeof(struct KdumpSubHeader32) + s->note_size;
|
||||
sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size);
|
||||
@@ -775,7 +775,7 @@ static void create_header32(DumpState *s, Error **errp)
|
||||
|
||||
/* 64bit max_mapnr_64 */
|
||||
kh->max_mapnr_64 = cpu_to_dump64(s, s->max_mapnr);
|
||||
kh->phys_base = cpu_to_dump32(s, PHYS_BASE);
|
||||
kh->phys_base = cpu_to_dump32(s, s->dump_info.phys_base);
|
||||
kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL);
|
||||
|
||||
offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size;
|
||||
@@ -837,7 +837,7 @@ static void create_header64(DumpState *s, Error **errp)
|
||||
|
||||
strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE));
|
||||
dh->header_version = cpu_to_dump32(s, 6);
|
||||
block_size = TARGET_PAGE_SIZE;
|
||||
block_size = s->dump_info.page_size;
|
||||
dh->block_size = cpu_to_dump32(s, block_size);
|
||||
sub_hdr_size = sizeof(struct KdumpSubHeader64) + s->note_size;
|
||||
sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size);
|
||||
@@ -875,7 +875,7 @@ static void create_header64(DumpState *s, Error **errp)
|
||||
|
||||
/* 64bit max_mapnr_64 */
|
||||
kh->max_mapnr_64 = cpu_to_dump64(s, s->max_mapnr);
|
||||
kh->phys_base = cpu_to_dump64(s, PHYS_BASE);
|
||||
kh->phys_base = cpu_to_dump64(s, s->dump_info.phys_base);
|
||||
kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL);
|
||||
|
||||
offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size;
|
||||
@@ -933,6 +933,11 @@ static void write_dump_header(DumpState *s, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static size_t dump_bitmap_get_bufsize(DumpState *s)
|
||||
{
|
||||
return s->dump_info.page_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* set dump_bitmap sequencely. the bit before last_pfn is not allowed to be
|
||||
* rewritten, so if need to set the first bit, set last_pfn and pfn to 0.
|
||||
@@ -946,6 +951,8 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value,
|
||||
off_t old_offset, new_offset;
|
||||
off_t offset_bitmap1, offset_bitmap2;
|
||||
uint32_t byte, bit;
|
||||
size_t bitmap_bufsize = dump_bitmap_get_bufsize(s);
|
||||
size_t bits_per_buf = bitmap_bufsize * CHAR_BIT;
|
||||
|
||||
/* should not set the previous place */
|
||||
assert(last_pfn <= pfn);
|
||||
@@ -956,14 +963,14 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value,
|
||||
* making new_offset be bigger than old_offset can also sync remained data
|
||||
* into vmcore.
|
||||
*/
|
||||
old_offset = BUFSIZE_BITMAP * (last_pfn / PFN_BUFBITMAP);
|
||||
new_offset = BUFSIZE_BITMAP * (pfn / PFN_BUFBITMAP);
|
||||
old_offset = bitmap_bufsize * (last_pfn / bits_per_buf);
|
||||
new_offset = bitmap_bufsize * (pfn / bits_per_buf);
|
||||
|
||||
while (old_offset < new_offset) {
|
||||
/* calculate the offset and write dump_bitmap */
|
||||
offset_bitmap1 = s->offset_dump_bitmap + old_offset;
|
||||
if (write_buffer(s->fd, offset_bitmap1, buf,
|
||||
BUFSIZE_BITMAP) < 0) {
|
||||
bitmap_bufsize) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -971,17 +978,17 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value,
|
||||
offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap +
|
||||
old_offset;
|
||||
if (write_buffer(s->fd, offset_bitmap2, buf,
|
||||
BUFSIZE_BITMAP) < 0) {
|
||||
bitmap_bufsize) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(buf, 0, BUFSIZE_BITMAP);
|
||||
old_offset += BUFSIZE_BITMAP;
|
||||
memset(buf, 0, bitmap_bufsize);
|
||||
old_offset += bitmap_bufsize;
|
||||
}
|
||||
|
||||
/* get the exact place of the bit in the buf, and set it */
|
||||
byte = (pfn % PFN_BUFBITMAP) / CHAR_BIT;
|
||||
bit = (pfn % PFN_BUFBITMAP) % CHAR_BIT;
|
||||
byte = (pfn % bits_per_buf) / CHAR_BIT;
|
||||
bit = (pfn % bits_per_buf) % CHAR_BIT;
|
||||
if (value) {
|
||||
buf[byte] |= 1u << bit;
|
||||
} else {
|
||||
@@ -991,6 +998,20 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t dump_paddr_to_pfn(DumpState *s, uint64_t addr)
|
||||
{
|
||||
int target_page_shift = ctz32(s->dump_info.page_size);
|
||||
|
||||
return (addr >> target_page_shift) - ARCH_PFN_OFFSET;
|
||||
}
|
||||
|
||||
static uint64_t dump_pfn_to_paddr(DumpState *s, uint64_t pfn)
|
||||
{
|
||||
int target_page_shift = ctz32(s->dump_info.page_size);
|
||||
|
||||
return (pfn + ARCH_PFN_OFFSET) << target_page_shift;
|
||||
}
|
||||
|
||||
/*
|
||||
* exam every page and return the page frame number and the address of the page.
|
||||
* bufptr can be NULL. note: the blocks here is supposed to reflect guest-phys
|
||||
@@ -1001,16 +1022,16 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr,
|
||||
uint8_t **bufptr, DumpState *s)
|
||||
{
|
||||
GuestPhysBlock *block = *blockptr;
|
||||
hwaddr addr;
|
||||
hwaddr addr, target_page_mask = ~((hwaddr)s->dump_info.page_size - 1);
|
||||
uint8_t *buf;
|
||||
|
||||
/* block == NULL means the start of the iteration */
|
||||
if (!block) {
|
||||
block = QTAILQ_FIRST(&s->guest_phys_blocks.head);
|
||||
*blockptr = block;
|
||||
assert((block->target_start & ~TARGET_PAGE_MASK) == 0);
|
||||
assert((block->target_end & ~TARGET_PAGE_MASK) == 0);
|
||||
*pfnptr = paddr_to_pfn(block->target_start);
|
||||
assert((block->target_start & ~target_page_mask) == 0);
|
||||
assert((block->target_end & ~target_page_mask) == 0);
|
||||
*pfnptr = dump_paddr_to_pfn(s, block->target_start);
|
||||
if (bufptr) {
|
||||
*bufptr = block->host_addr;
|
||||
}
|
||||
@@ -1018,10 +1039,10 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr,
|
||||
}
|
||||
|
||||
*pfnptr = *pfnptr + 1;
|
||||
addr = pfn_to_paddr(*pfnptr);
|
||||
addr = dump_pfn_to_paddr(s, *pfnptr);
|
||||
|
||||
if ((addr >= block->target_start) &&
|
||||
(addr + TARGET_PAGE_SIZE <= block->target_end)) {
|
||||
(addr + s->dump_info.page_size <= block->target_end)) {
|
||||
buf = block->host_addr + (addr - block->target_start);
|
||||
} else {
|
||||
/* the next page is in the next block */
|
||||
@@ -1030,9 +1051,9 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr,
|
||||
if (!block) {
|
||||
return false;
|
||||
}
|
||||
assert((block->target_start & ~TARGET_PAGE_MASK) == 0);
|
||||
assert((block->target_end & ~TARGET_PAGE_MASK) == 0);
|
||||
*pfnptr = paddr_to_pfn(block->target_start);
|
||||
assert((block->target_start & ~target_page_mask) == 0);
|
||||
assert((block->target_end & ~target_page_mask) == 0);
|
||||
*pfnptr = dump_paddr_to_pfn(s, block->target_start);
|
||||
buf = block->host_addr;
|
||||
}
|
||||
|
||||
@@ -1050,9 +1071,11 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
|
||||
void *dump_bitmap_buf;
|
||||
size_t num_dumpable;
|
||||
GuestPhysBlock *block_iter = NULL;
|
||||
size_t bitmap_bufsize = dump_bitmap_get_bufsize(s);
|
||||
size_t bits_per_buf = bitmap_bufsize * CHAR_BIT;
|
||||
|
||||
/* dump_bitmap_buf is used to store dump_bitmap temporarily */
|
||||
dump_bitmap_buf = g_malloc0(BUFSIZE_BITMAP);
|
||||
dump_bitmap_buf = g_malloc0(bitmap_bufsize);
|
||||
|
||||
num_dumpable = 0;
|
||||
last_pfn = 0;
|
||||
@@ -1074,11 +1097,11 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
|
||||
|
||||
/*
|
||||
* set_dump_bitmap will always leave the recently set bit un-sync. Here we
|
||||
* set last_pfn + PFN_BUFBITMAP to 0 and those set but un-sync bit will be
|
||||
* synchronized into vmcore.
|
||||
* set the remaining bits from last_pfn to the end of the bitmap buffer to
|
||||
* 0. With those set, the un-sync bit will be synchronized into the vmcore.
|
||||
*/
|
||||
if (num_dumpable > 0) {
|
||||
ret = set_dump_bitmap(last_pfn, last_pfn + PFN_BUFBITMAP, false,
|
||||
ret = set_dump_bitmap(last_pfn, last_pfn + bits_per_buf, false,
|
||||
dump_bitmap_buf, s);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to sync dump_bitmap", errp);
|
||||
@@ -1098,8 +1121,8 @@ static void prepare_data_cache(DataCache *data_cache, DumpState *s,
|
||||
{
|
||||
data_cache->fd = s->fd;
|
||||
data_cache->data_size = 0;
|
||||
data_cache->buf_size = BUFSIZE_DATA_CACHE;
|
||||
data_cache->buf = g_malloc0(BUFSIZE_DATA_CACHE);
|
||||
data_cache->buf_size = 4 * dump_bitmap_get_bufsize(s);
|
||||
data_cache->buf = g_malloc0(data_cache->buf_size);
|
||||
data_cache->offset = offset;
|
||||
}
|
||||
|
||||
@@ -1193,7 +1216,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
||||
prepare_data_cache(&page_data, s, offset_data);
|
||||
|
||||
/* prepare buffer to store compressed data */
|
||||
len_buf_out = get_len_buf_out(TARGET_PAGE_SIZE, s->flag_compress);
|
||||
len_buf_out = get_len_buf_out(s->dump_info.page_size, s->flag_compress);
|
||||
assert(len_buf_out != 0);
|
||||
|
||||
#ifdef CONFIG_LZO
|
||||
@@ -1206,19 +1229,19 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
||||
* init zero page's page_desc and page_data, because every zero page
|
||||
* uses the same page_data
|
||||
*/
|
||||
pd_zero.size = cpu_to_dump32(s, TARGET_PAGE_SIZE);
|
||||
pd_zero.size = cpu_to_dump32(s, s->dump_info.page_size);
|
||||
pd_zero.flags = cpu_to_dump32(s, 0);
|
||||
pd_zero.offset = cpu_to_dump64(s, offset_data);
|
||||
pd_zero.page_flags = cpu_to_dump64(s, 0);
|
||||
buf = g_malloc0(TARGET_PAGE_SIZE);
|
||||
ret = write_cache(&page_data, buf, TARGET_PAGE_SIZE, false);
|
||||
buf = g_malloc0(s->dump_info.page_size);
|
||||
ret = write_cache(&page_data, buf, s->dump_info.page_size, false);
|
||||
g_free(buf);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write page data (zero page)", errp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
offset_data += TARGET_PAGE_SIZE;
|
||||
offset_data += s->dump_info.page_size;
|
||||
|
||||
/*
|
||||
* dump memory to vmcore page by page. zero page will all be resided in the
|
||||
@@ -1226,7 +1249,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
||||
*/
|
||||
while (get_next_page(&block_iter, &pfn_iter, &buf, s)) {
|
||||
/* check zero page */
|
||||
if (is_zero_page(buf, TARGET_PAGE_SIZE)) {
|
||||
if (is_zero_page(buf, s->dump_info.page_size)) {
|
||||
ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor),
|
||||
false);
|
||||
if (ret < 0) {
|
||||
@@ -1248,8 +1271,8 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
||||
size_out = len_buf_out;
|
||||
if ((s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) &&
|
||||
(compress2(buf_out, (uLongf *)&size_out, buf,
|
||||
TARGET_PAGE_SIZE, Z_BEST_SPEED) == Z_OK) &&
|
||||
(size_out < TARGET_PAGE_SIZE)) {
|
||||
s->dump_info.page_size, Z_BEST_SPEED) == Z_OK) &&
|
||||
(size_out < s->dump_info.page_size)) {
|
||||
pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_ZLIB);
|
||||
pd.size = cpu_to_dump32(s, size_out);
|
||||
|
||||
@@ -1260,9 +1283,9 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
||||
}
|
||||
#ifdef CONFIG_LZO
|
||||
} else if ((s->flag_compress & DUMP_DH_COMPRESSED_LZO) &&
|
||||
(lzo1x_1_compress(buf, TARGET_PAGE_SIZE, buf_out,
|
||||
(lzo1x_1_compress(buf, s->dump_info.page_size, buf_out,
|
||||
(lzo_uint *)&size_out, wrkmem) == LZO_E_OK) &&
|
||||
(size_out < TARGET_PAGE_SIZE)) {
|
||||
(size_out < s->dump_info.page_size)) {
|
||||
pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_LZO);
|
||||
pd.size = cpu_to_dump32(s, size_out);
|
||||
|
||||
@@ -1274,9 +1297,9 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
||||
#endif
|
||||
#ifdef CONFIG_SNAPPY
|
||||
} else if ((s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) &&
|
||||
(snappy_compress((char *)buf, TARGET_PAGE_SIZE,
|
||||
(snappy_compress((char *)buf, s->dump_info.page_size,
|
||||
(char *)buf_out, &size_out) == SNAPPY_OK) &&
|
||||
(size_out < TARGET_PAGE_SIZE)) {
|
||||
(size_out < s->dump_info.page_size)) {
|
||||
pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_SNAPPY);
|
||||
pd.size = cpu_to_dump32(s, size_out);
|
||||
|
||||
@@ -1289,13 +1312,14 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
||||
} else {
|
||||
/*
|
||||
* fall back to save in plaintext, size_out should be
|
||||
* assigned TARGET_PAGE_SIZE
|
||||
* assigned the target's page size
|
||||
*/
|
||||
pd.flags = cpu_to_dump32(s, 0);
|
||||
size_out = TARGET_PAGE_SIZE;
|
||||
size_out = s->dump_info.page_size;
|
||||
pd.size = cpu_to_dump32(s, size_out);
|
||||
|
||||
ret = write_cache(&page_data, buf, TARGET_PAGE_SIZE, false);
|
||||
ret = write_cache(&page_data, buf,
|
||||
s->dump_info.page_size, false);
|
||||
if (ret < 0) {
|
||||
dump_error(s, "dump: failed to write page data", errp);
|
||||
goto out;
|
||||
@@ -1430,7 +1454,7 @@ static void get_max_mapnr(DumpState *s)
|
||||
GuestPhysBlock *last_block;
|
||||
|
||||
last_block = QTAILQ_LAST(&s->guest_phys_blocks.head, GuestPhysBlockHead);
|
||||
s->max_mapnr = paddr_to_pfn(last_block->target_end);
|
||||
s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end);
|
||||
}
|
||||
|
||||
static void dump_init(DumpState *s, int fd, bool has_format,
|
||||
@@ -1489,6 +1513,10 @@ static void dump_init(DumpState *s, int fd, bool has_format,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!s->dump_info.page_size) {
|
||||
s->dump_info.page_size = TARGET_PAGE_SIZE;
|
||||
}
|
||||
|
||||
s->note_size = cpu_get_note_size(s->dump_info.d_class,
|
||||
s->dump_info.d_machine, nr_cpus);
|
||||
if (s->note_size < 0) {
|
||||
@@ -1512,8 +1540,9 @@ static void dump_init(DumpState *s, int fd, bool has_format,
|
||||
get_max_mapnr(s);
|
||||
|
||||
uint64_t tmp;
|
||||
tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), TARGET_PAGE_SIZE);
|
||||
s->len_dump_bitmap = tmp * TARGET_PAGE_SIZE;
|
||||
tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT),
|
||||
s->dump_info.page_size);
|
||||
s->len_dump_bitmap = tmp * s->dump_info.page_size;
|
||||
|
||||
/* init for kdump-compressed format */
|
||||
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
|
||||
|
10
exec.c
10
exec.c
@@ -1757,6 +1757,16 @@ int qemu_get_ram_fd(ram_addr_t addr)
|
||||
return fd;
|
||||
}
|
||||
|
||||
void qemu_set_ram_fd(ram_addr_t addr, int fd)
|
||||
{
|
||||
RAMBlock *block;
|
||||
|
||||
rcu_read_lock();
|
||||
block = qemu_get_ram_block(addr);
|
||||
block->fd = fd;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
|
||||
{
|
||||
RAMBlock *block;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p backend
|
||||
* 9p backend
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
@@ -22,40 +22,9 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "qemu/compiler.h"
|
||||
#include "virtio-9p-marshal.h"
|
||||
#include "9p-iov-marshal.h"
|
||||
#include "qemu/bswap.h"
|
||||
|
||||
void v9fs_string_free(V9fsString *str)
|
||||
{
|
||||
g_free(str->data);
|
||||
str->data = NULL;
|
||||
str->size = 0;
|
||||
}
|
||||
|
||||
void v9fs_string_null(V9fsString *str)
|
||||
{
|
||||
v9fs_string_free(str);
|
||||
}
|
||||
|
||||
void GCC_FMT_ATTR(2, 3)
|
||||
v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
v9fs_string_free(str);
|
||||
|
||||
va_start(ap, fmt);
|
||||
str->size = g_vasprintf(&str->data, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
|
||||
{
|
||||
v9fs_string_free(lhs);
|
||||
v9fs_string_sprintf(lhs, "%s", rhs->data);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t v9fs_packunpack(void *addr, struct iovec *sg, int sg_count,
|
||||
size_t offset, size_t size, int pack)
|
||||
{
|
||||
@@ -107,15 +76,13 @@ ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset,
|
||||
return v9fs_packunpack((void *)src, in_sg, in_num, offset, size, 1);
|
||||
}
|
||||
|
||||
ssize_t v9fs_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
|
||||
int bswap, const char *fmt, ...)
|
||||
ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset,
|
||||
int bswap, const char *fmt, va_list ap)
|
||||
{
|
||||
int i;
|
||||
va_list ap;
|
||||
ssize_t copied = 0;
|
||||
size_t old_offset = offset;
|
||||
|
||||
va_start(ap, fmt);
|
||||
for (i = 0; fmt[i]; i++) {
|
||||
switch (fmt[i]) {
|
||||
case 'b': {
|
||||
@@ -158,8 +125,8 @@ ssize_t v9fs_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
|
||||
}
|
||||
case 's': {
|
||||
V9fsString *str = va_arg(ap, V9fsString *);
|
||||
copied = v9fs_unmarshal(out_sg, out_num, offset, bswap,
|
||||
"w", &str->size);
|
||||
copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
|
||||
"w", &str->size);
|
||||
if (copied > 0) {
|
||||
offset += copied;
|
||||
str->data = g_malloc(str->size + 1);
|
||||
@@ -175,56 +142,70 @@ ssize_t v9fs_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
|
||||
}
|
||||
case 'Q': {
|
||||
V9fsQID *qidp = va_arg(ap, V9fsQID *);
|
||||
copied = v9fs_unmarshal(out_sg, out_num, offset, bswap, "bdq",
|
||||
&qidp->type, &qidp->version, &qidp->path);
|
||||
copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
|
||||
"bdq", &qidp->type, &qidp->version,
|
||||
&qidp->path);
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
V9fsStat *statp = va_arg(ap, V9fsStat *);
|
||||
copied = v9fs_unmarshal(out_sg, out_num, offset, bswap,
|
||||
"wwdQdddqsssssddd",
|
||||
&statp->size, &statp->type, &statp->dev,
|
||||
&statp->qid, &statp->mode, &statp->atime,
|
||||
&statp->mtime, &statp->length,
|
||||
&statp->name, &statp->uid, &statp->gid,
|
||||
&statp->muid, &statp->extension,
|
||||
&statp->n_uid, &statp->n_gid,
|
||||
&statp->n_muid);
|
||||
copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
|
||||
"wwdQdddqsssssddd",
|
||||
&statp->size, &statp->type,
|
||||
&statp->dev, &statp->qid,
|
||||
&statp->mode, &statp->atime,
|
||||
&statp->mtime, &statp->length,
|
||||
&statp->name, &statp->uid,
|
||||
&statp->gid, &statp->muid,
|
||||
&statp->extension,
|
||||
&statp->n_uid, &statp->n_gid,
|
||||
&statp->n_muid);
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
|
||||
copied = v9fs_unmarshal(out_sg, out_num, offset, bswap,
|
||||
"ddddqqqqq",
|
||||
&iattr->valid, &iattr->mode,
|
||||
&iattr->uid, &iattr->gid, &iattr->size,
|
||||
&iattr->atime_sec, &iattr->atime_nsec,
|
||||
&iattr->mtime_sec, &iattr->mtime_nsec);
|
||||
copied = v9fs_iov_unmarshal(out_sg, out_num, offset, bswap,
|
||||
"ddddqqqqq",
|
||||
&iattr->valid, &iattr->mode,
|
||||
&iattr->uid, &iattr->gid,
|
||||
&iattr->size, &iattr->atime_sec,
|
||||
&iattr->atime_nsec,
|
||||
&iattr->mtime_sec,
|
||||
&iattr->mtime_nsec);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (copied < 0) {
|
||||
va_end(ap);
|
||||
return copied;
|
||||
}
|
||||
offset += copied;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return offset - old_offset;
|
||||
}
|
||||
|
||||
ssize_t v9fs_marshal(struct iovec *in_sg, int in_num, size_t offset,
|
||||
int bswap, const char *fmt, ...)
|
||||
ssize_t v9fs_iov_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
|
||||
int bswap, const char *fmt, ...)
|
||||
{
|
||||
ssize_t ret;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = v9fs_iov_vunmarshal(out_sg, out_num, offset, bswap, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset,
|
||||
int bswap, const char *fmt, va_list ap)
|
||||
{
|
||||
int i;
|
||||
va_list ap;
|
||||
ssize_t copied = 0;
|
||||
size_t old_offset = offset;
|
||||
|
||||
va_start(ap, fmt);
|
||||
for (i = 0; fmt[i]; i++) {
|
||||
switch (fmt[i]) {
|
||||
case 'b': {
|
||||
@@ -264,8 +245,8 @@ ssize_t v9fs_marshal(struct iovec *in_sg, int in_num, size_t offset,
|
||||
}
|
||||
case 's': {
|
||||
V9fsString *str = va_arg(ap, V9fsString *);
|
||||
copied = v9fs_marshal(in_sg, in_num, offset, bswap,
|
||||
"w", str->size);
|
||||
copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
|
||||
"w", str->size);
|
||||
if (copied > 0) {
|
||||
offset += copied;
|
||||
copied = v9fs_pack(in_sg, in_num, offset, str->data, str->size);
|
||||
@@ -274,49 +255,65 @@ ssize_t v9fs_marshal(struct iovec *in_sg, int in_num, size_t offset,
|
||||
}
|
||||
case 'Q': {
|
||||
V9fsQID *qidp = va_arg(ap, V9fsQID *);
|
||||
copied = v9fs_marshal(in_sg, in_num, offset, bswap, "bdq",
|
||||
qidp->type, qidp->version, qidp->path);
|
||||
copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap, "bdq",
|
||||
qidp->type, qidp->version,
|
||||
qidp->path);
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
V9fsStat *statp = va_arg(ap, V9fsStat *);
|
||||
copied = v9fs_marshal(in_sg, in_num, offset, bswap,
|
||||
"wwdQdddqsssssddd",
|
||||
statp->size, statp->type, statp->dev,
|
||||
&statp->qid, statp->mode, statp->atime,
|
||||
statp->mtime, statp->length, &statp->name,
|
||||
&statp->uid, &statp->gid, &statp->muid,
|
||||
&statp->extension, statp->n_uid,
|
||||
statp->n_gid, statp->n_muid);
|
||||
copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
|
||||
"wwdQdddqsssssddd",
|
||||
statp->size, statp->type, statp->dev,
|
||||
&statp->qid, statp->mode, statp->atime,
|
||||
statp->mtime, statp->length,
|
||||
&statp->name,
|
||||
&statp->uid, &statp->gid, &statp->muid,
|
||||
&statp->extension, statp->n_uid,
|
||||
statp->n_gid, statp->n_muid);
|
||||
break;
|
||||
}
|
||||
case 'A': {
|
||||
V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
|
||||
copied = v9fs_marshal(in_sg, in_num, offset, bswap,
|
||||
"qQdddqqqqqqqqqqqqqqq",
|
||||
statp->st_result_mask,
|
||||
&statp->qid, statp->st_mode,
|
||||
statp->st_uid, statp->st_gid,
|
||||
statp->st_nlink, statp->st_rdev,
|
||||
statp->st_size, statp->st_blksize,
|
||||
statp->st_blocks, statp->st_atime_sec,
|
||||
statp->st_atime_nsec, statp->st_mtime_sec,
|
||||
statp->st_mtime_nsec, statp->st_ctime_sec,
|
||||
statp->st_ctime_nsec, statp->st_btime_sec,
|
||||
statp->st_btime_nsec, statp->st_gen,
|
||||
statp->st_data_version);
|
||||
copied = v9fs_iov_marshal(in_sg, in_num, offset, bswap,
|
||||
"qQdddqqqqqqqqqqqqqqq",
|
||||
statp->st_result_mask,
|
||||
&statp->qid, statp->st_mode,
|
||||
statp->st_uid, statp->st_gid,
|
||||
statp->st_nlink, statp->st_rdev,
|
||||
statp->st_size, statp->st_blksize,
|
||||
statp->st_blocks, statp->st_atime_sec,
|
||||
statp->st_atime_nsec,
|
||||
statp->st_mtime_sec,
|
||||
statp->st_mtime_nsec,
|
||||
statp->st_ctime_sec,
|
||||
statp->st_ctime_nsec,
|
||||
statp->st_btime_sec,
|
||||
statp->st_btime_nsec, statp->st_gen,
|
||||
statp->st_data_version);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (copied < 0) {
|
||||
va_end(ap);
|
||||
return copied;
|
||||
}
|
||||
offset += copied;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return offset - old_offset;
|
||||
}
|
||||
|
||||
ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset,
|
||||
int bswap, const char *fmt, ...)
|
||||
{
|
||||
ssize_t ret;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = v9fs_iov_vmarshal(in_sg, in_num, offset, bswap, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
18
fsdev/9p-iov-marshal.h
Normal file
18
fsdev/9p-iov-marshal.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _QEMU_9P_IOV_MARSHAL_H
|
||||
#define _QEMU_9P_IOV_MARSHAL_H
|
||||
|
||||
#include "9p-marshal.h"
|
||||
|
||||
|
||||
ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset,
|
||||
const void *src, size_t size);
|
||||
ssize_t v9fs_iov_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
|
||||
int bswap, const char *fmt, ...);
|
||||
ssize_t v9fs_iov_marshal(struct iovec *in_sg, int in_num, size_t offset,
|
||||
int bswap, const char *fmt, ...);
|
||||
|
||||
ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset,
|
||||
int bswap, const char *fmt, va_list ap);
|
||||
ssize_t v9fs_iov_vmarshal(struct iovec *in_sg, int in_num, size_t offset,
|
||||
int bswap, const char *fmt, va_list ap);
|
||||
#endif
|
56
fsdev/9p-marshal.c
Normal file
56
fsdev/9p-marshal.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 9p backend
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/time.h>
|
||||
#include <utime.h>
|
||||
#include <sys/uio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "qemu/compiler.h"
|
||||
#include "9p-marshal.h"
|
||||
|
||||
void v9fs_string_free(V9fsString *str)
|
||||
{
|
||||
g_free(str->data);
|
||||
str->data = NULL;
|
||||
str->size = 0;
|
||||
}
|
||||
|
||||
void v9fs_string_null(V9fsString *str)
|
||||
{
|
||||
v9fs_string_free(str);
|
||||
}
|
||||
|
||||
void GCC_FMT_ATTR(2, 3)
|
||||
v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
v9fs_string_free(str);
|
||||
|
||||
va_start(ap, fmt);
|
||||
str->size = g_vasprintf(&str->data, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
|
||||
{
|
||||
v9fs_string_free(lhs);
|
||||
v9fs_string_sprintf(lhs, "%s", rhs->data);
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
#ifndef _QEMU_VIRTIO_9P_MARSHAL_H
|
||||
#define _QEMU_VIRTIO_9P_MARSHAL_H
|
||||
#ifndef _QEMU_9P_MARSHAL_H
|
||||
#define _QEMU_9P_MARSHAL_H
|
||||
|
||||
typedef struct V9fsString
|
||||
{
|
||||
@@ -30,7 +30,7 @@ typedef struct V9fsStat
|
||||
V9fsString muid;
|
||||
/* 9p2000.u */
|
||||
V9fsString extension;
|
||||
int32_t n_uid;
|
||||
int32_t n_uid;
|
||||
int32_t n_gid;
|
||||
int32_t n_muid;
|
||||
} V9fsStat;
|
||||
@@ -81,10 +81,4 @@ extern void v9fs_string_null(V9fsString *str);
|
||||
extern void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...);
|
||||
extern void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs);
|
||||
|
||||
ssize_t v9fs_pack(struct iovec *in_sg, int in_num, size_t offset,
|
||||
const void *src, size_t size);
|
||||
ssize_t v9fs_unmarshal(struct iovec *out_sg, int out_num, size_t offset,
|
||||
int bswap, const char *fmt, ...);
|
||||
ssize_t v9fs_marshal(struct iovec *in_sg, int in_num, size_t offset,
|
||||
int bswap, const char *fmt, ...);
|
||||
#endif
|
@@ -1,7 +1,7 @@
|
||||
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
|
||||
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
|
||||
# only pull in the actual virtio-9p device if we also enabled virtio.
|
||||
common-obj-y = qemu-fsdev.o virtio-9p-marshal.o
|
||||
common-obj-y = qemu-fsdev.o 9p-marshal.o 9p-iov-marshal.o
|
||||
else
|
||||
common-obj-y = qemu-fsdev-dummy.o
|
||||
endif
|
||||
|
@@ -23,9 +23,9 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/xattr.h"
|
||||
#include "virtio-9p-marshal.h"
|
||||
#include "hw/9pfs/virtio-9p-proxy.h"
|
||||
#include "fsdev/virtio-9p-marshal.h"
|
||||
#include "9p-iov-marshal.h"
|
||||
#include "hw/9pfs/9p-proxy.h"
|
||||
#include "fsdev/9p-iov-marshal.h"
|
||||
|
||||
#define PROGNAME "virtfs-proxy-helper"
|
||||
|
||||
|
@@ -1732,6 +1732,7 @@ int gdbserver_start(const char *device)
|
||||
char gdbstub_device_name[128];
|
||||
CharDriverState *chr = NULL;
|
||||
CharDriverState *mon_chr;
|
||||
ChardevCommon common = { 0 };
|
||||
|
||||
if (!device)
|
||||
return -1;
|
||||
@@ -1768,7 +1769,7 @@ int gdbserver_start(const char *device)
|
||||
qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
|
||||
|
||||
/* Initialize a monitor terminal for gdb */
|
||||
mon_chr = qemu_chr_alloc();
|
||||
mon_chr = qemu_chr_alloc(&common, &error_abort);
|
||||
mon_chr->chr_write = gdb_monitor_write;
|
||||
monitor_init(mon_chr, 0);
|
||||
} else {
|
||||
|
61
hmp.c
61
hmp.c
@@ -41,8 +41,7 @@ static void hmp_handle_error(Monitor *mon, Error **errp)
|
||||
{
|
||||
assert(errp);
|
||||
if (*errp) {
|
||||
monitor_printf(mon, "%s\n", error_get_pretty(*errp));
|
||||
error_free(*errp);
|
||||
error_report_err(*errp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,8 +555,7 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
|
||||
|
||||
info = qmp_query_vnc(&err);
|
||||
if (err) {
|
||||
monitor_printf(mon, "%s\n", error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_report_err(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -679,8 +677,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict)
|
||||
|
||||
info = qmp_query_balloon(&err);
|
||||
if (err) {
|
||||
monitor_printf(mon, "%s\n", error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_report_err(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -948,8 +945,7 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict)
|
||||
|
||||
data = qmp_ringbuf_read(chardev, size, false, 0, &err);
|
||||
if (err) {
|
||||
monitor_printf(mon, "%s\n", error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_report_err(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1042,8 +1038,7 @@ void hmp_balloon(Monitor *mon, const QDict *qdict)
|
||||
|
||||
qmp_balloon(value, &err);
|
||||
if (err) {
|
||||
monitor_printf(mon, "balloon: %s\n", error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_report_err(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,8 +1186,7 @@ void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict)
|
||||
|
||||
qmp_migrate_set_cache_size(value, &err);
|
||||
if (err) {
|
||||
monitor_printf(mon, "%s\n", error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_report_err(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1229,9 +1223,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
|
||||
qapi_free_MigrationCapabilityStatusList(caps);
|
||||
|
||||
if (err) {
|
||||
monitor_printf(mon, "migrate_set_capability: %s\n",
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_report_err(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1281,9 +1273,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
|
||||
if (err) {
|
||||
monitor_printf(mon, "migrate_set_parameter: %s\n",
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_report_err(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1544,8 +1534,7 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
|
||||
|
||||
qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, &err);
|
||||
if (err) {
|
||||
monitor_printf(mon, "migrate: %s\n", error_get_pretty(err));
|
||||
error_free(err);
|
||||
error_report_err(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2089,11 +2078,11 @@ void hmp_rocker(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
RockerSwitch *rocker;
|
||||
Error *errp = NULL;
|
||||
Error *err = NULL;
|
||||
|
||||
rocker = qmp_query_rocker(name, &errp);
|
||||
if (errp != NULL) {
|
||||
hmp_handle_error(mon, &errp);
|
||||
rocker = qmp_query_rocker(name, &err);
|
||||
if (err != NULL) {
|
||||
hmp_handle_error(mon, &err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2108,11 +2097,11 @@ void hmp_rocker_ports(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
RockerPortList *list, *port;
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
Error *errp = NULL;
|
||||
Error *err = NULL;
|
||||
|
||||
list = qmp_query_rocker_ports(name, &errp);
|
||||
if (errp != NULL) {
|
||||
hmp_handle_error(mon, &errp);
|
||||
list = qmp_query_rocker_ports(name, &err);
|
||||
if (err != NULL) {
|
||||
hmp_handle_error(mon, &err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2137,11 +2126,11 @@ void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict)
|
||||
RockerOfDpaFlowList *list, *info;
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1);
|
||||
Error *errp = NULL;
|
||||
Error *err = NULL;
|
||||
|
||||
list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &errp);
|
||||
if (errp != NULL) {
|
||||
hmp_handle_error(mon, &errp);
|
||||
list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err);
|
||||
if (err != NULL) {
|
||||
hmp_handle_error(mon, &err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2287,12 +2276,12 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict)
|
||||
RockerOfDpaGroupList *list, *g;
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
uint8_t type = qdict_get_try_int(qdict, "type", 9);
|
||||
Error *errp = NULL;
|
||||
Error *err = NULL;
|
||||
bool set = false;
|
||||
|
||||
list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &errp);
|
||||
if (errp != NULL) {
|
||||
hmp_handle_error(mon, &errp);
|
||||
list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err);
|
||||
if (err != NULL) {
|
||||
hmp_handle_error(mon, &err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p handle callback
|
||||
* 9p handle callback
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
@@ -11,9 +11,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "9p.h"
|
||||
#include "9p-xattr.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p Posix callback
|
||||
* 9p Posix callback
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
@@ -11,9 +11,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "9p.h"
|
||||
#include "9p-xattr.h"
|
||||
#include "fsdev/qemu-fsdev.h" /* local_ops */
|
||||
#include <arpa/inet.h>
|
||||
#include <pwd.h>
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p system.posix* xattr callback
|
||||
* 9p system.posix* xattr callback
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
@@ -13,10 +13,9 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "qemu/xattr.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "9p.h"
|
||||
#include "fsdev/file-op-9p.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "9p-xattr.h"
|
||||
|
||||
#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access"
|
||||
#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default"
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p Proxy callback
|
||||
* 9p Proxy callback
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
@@ -11,11 +11,10 @@
|
||||
*/
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "9p.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "virtio-9p-proxy.h"
|
||||
#include "9p-proxy.h"
|
||||
|
||||
typedef struct V9fsProxy {
|
||||
int sockfd;
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p Proxy callback
|
||||
* 9p Proxy callback
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
@@ -9,8 +9,8 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef _QEMU_VIRTIO_9P_PROXY_H
|
||||
#define _QEMU_VIRTIO_9P_PROXY_H
|
||||
#ifndef _QEMU_9P_PROXY_H
|
||||
#define _QEMU_9P_PROXY_H
|
||||
|
||||
#define PROXY_MAX_IO_SZ (64 * 1024)
|
||||
#define V9FS_FD_VALID INT_MAX
|
||||
@@ -20,9 +20,9 @@
|
||||
* marsha/unmarshal doesn't do little endian conversion.
|
||||
*/
|
||||
#define proxy_unmarshal(in_sg, offset, fmt, args...) \
|
||||
v9fs_unmarshal(in_sg, 1, offset, 0, fmt, ##args)
|
||||
v9fs_iov_unmarshal(in_sg, 1, offset, 0, fmt, ##args)
|
||||
#define proxy_marshal(out_sg, offset, fmt, args...) \
|
||||
v9fs_marshal(out_sg, 1, offset, 0, fmt, ##args)
|
||||
v9fs_iov_marshal(out_sg, 1, offset, 0, fmt, ##args)
|
||||
|
||||
union MsgControl {
|
||||
struct cmsghdr cmsg;
|
@@ -13,10 +13,10 @@
|
||||
*/
|
||||
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "9p.h"
|
||||
#include "9p-xattr.h"
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "virtio-9p-synth.h"
|
||||
#include "9p-synth.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "qemu/rcu_queue.h"
|
||||
#include <sys/stat.h>
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p
|
||||
* 9p
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
@@ -10,8 +10,8 @@
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#ifndef HW_9PFS_VIRTIO9P_SYNTH_H
|
||||
#define HW_9PFS_VIRTIO9P_SYNTH_H 1
|
||||
#ifndef HW_9PFS_SYNTH_H
|
||||
#define HW_9PFS_SYNTH_H 1
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p user. xattr callback
|
||||
* 9p user. xattr callback
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
@@ -12,10 +12,9 @@
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "9p.h"
|
||||
#include "fsdev/file-op-9p.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "9p-xattr.h"
|
||||
|
||||
|
||||
static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p xattr callback
|
||||
* 9p xattr callback
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
@@ -11,10 +11,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "9p.h"
|
||||
#include "fsdev/file-op-9p.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "9p-xattr.h"
|
||||
|
||||
|
||||
static XattrOperations *get_xattr_operations(XattrOperations **h,
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p
|
||||
* 9p
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
@@ -10,8 +10,8 @@
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#ifndef _QEMU_VIRTIO_9P_XATTR_H
|
||||
#define _QEMU_VIRTIO_9P_XATTR_H
|
||||
#ifndef _QEMU_9P_XATTR_H
|
||||
#define _QEMU_9P_XATTR_H
|
||||
|
||||
#include "qemu/xattr.h"
|
||||
|
@@ -18,8 +18,8 @@
|
||||
#include "qemu/sockets.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "virtio-9p-coth.h"
|
||||
#include "9p-xattr.h"
|
||||
#include "coth.h"
|
||||
#include "trace.h"
|
||||
#include "migration/migration.h"
|
||||
|
||||
@@ -39,6 +39,35 @@ enum {
|
||||
Oappend = 0x80,
|
||||
};
|
||||
|
||||
ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
||||
{
|
||||
ssize_t ret;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = virtio_pdu_vmarshal(pdu, offset, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
||||
{
|
||||
ssize_t ret;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = virtio_pdu_vunmarshal(pdu, offset, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pdu_push_and_notify(V9fsPDU *pdu)
|
||||
{
|
||||
virtio_9p_push_and_notify(pdu);
|
||||
}
|
||||
|
||||
static int omode_to_uflags(int8_t mode)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -563,7 +592,7 @@ static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static V9fsPDU *alloc_pdu(V9fsState *s)
|
||||
V9fsPDU *pdu_alloc(V9fsState *s)
|
||||
{
|
||||
V9fsPDU *pdu = NULL;
|
||||
|
||||
@@ -575,9 +604,10 @@ static V9fsPDU *alloc_pdu(V9fsState *s)
|
||||
return pdu;
|
||||
}
|
||||
|
||||
static void free_pdu(V9fsState *s, V9fsPDU *pdu)
|
||||
void pdu_free(V9fsPDU *pdu)
|
||||
{
|
||||
if (pdu) {
|
||||
V9fsState *s = pdu->s;
|
||||
/*
|
||||
* Cancelled pdu are added back to the freelist
|
||||
* by flush request .
|
||||
@@ -594,9 +624,10 @@ static void free_pdu(V9fsState *s, V9fsPDU *pdu)
|
||||
* because we always expect to have enough space to encode
|
||||
* error details
|
||||
*/
|
||||
static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
|
||||
static void pdu_complete(V9fsPDU *pdu, ssize_t len)
|
||||
{
|
||||
int8_t id = pdu->id + 1; /* Response */
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
if (len < 0) {
|
||||
int err = -len;
|
||||
@@ -627,16 +658,12 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
|
||||
pdu->size = len;
|
||||
pdu->id = id;
|
||||
|
||||
/* push onto queue and notify */
|
||||
virtqueue_push(s->vq, &pdu->elem, len);
|
||||
|
||||
/* FIXME: we should batch these completions */
|
||||
virtio_notify(VIRTIO_DEVICE(s), s->vq);
|
||||
pdu_push_and_notify(pdu);
|
||||
|
||||
/* Now wakeup anybody waiting in flush for this request */
|
||||
qemu_co_queue_next(&pdu->complete);
|
||||
|
||||
free_pdu(s, pdu);
|
||||
pdu_free(pdu);
|
||||
}
|
||||
|
||||
static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
|
||||
@@ -931,7 +958,7 @@ static void v9fs_version(void *opaque)
|
||||
offset += err;
|
||||
trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data);
|
||||
out:
|
||||
complete_pdu(s, pdu, offset);
|
||||
pdu_complete(pdu, offset);
|
||||
v9fs_string_free(&version);
|
||||
}
|
||||
|
||||
@@ -995,7 +1022,7 @@ static void v9fs_attach(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&uname);
|
||||
v9fs_string_free(&aname);
|
||||
}
|
||||
@@ -1009,7 +1036,6 @@ static void v9fs_stat(void *opaque)
|
||||
struct stat stbuf;
|
||||
V9fsFidState *fidp;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
err = pdu_unmarshal(pdu, offset, "d", &fid);
|
||||
if (err < 0) {
|
||||
@@ -1042,7 +1068,7 @@ static void v9fs_stat(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static void v9fs_getattr(void *opaque)
|
||||
@@ -1105,7 +1131,7 @@ static void v9fs_getattr(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, retval);
|
||||
pdu_complete(pdu, retval);
|
||||
}
|
||||
|
||||
/* Attribute flags */
|
||||
@@ -1129,7 +1155,6 @@ static void v9fs_setattr(void *opaque)
|
||||
size_t offset = 7;
|
||||
V9fsIattr v9iattr;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
err = pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr);
|
||||
if (err < 0) {
|
||||
@@ -1203,7 +1228,7 @@ static void v9fs_setattr(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids)
|
||||
@@ -1245,7 +1270,7 @@ static void v9fs_walk(void *opaque)
|
||||
|
||||
err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames);
|
||||
if (err < 0) {
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
return ;
|
||||
}
|
||||
offset += err;
|
||||
@@ -1313,7 +1338,7 @@ out:
|
||||
v9fs_path_free(&dpath);
|
||||
v9fs_path_free(&path);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
if (nwnames && nwnames <= P9_MAXWELEM) {
|
||||
for (name_idx = 0; name_idx < nwnames; name_idx++) {
|
||||
v9fs_string_free(&wnames[name_idx]);
|
||||
@@ -1430,7 +1455,7 @@ static void v9fs_open(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static void v9fs_lcreate(void *opaque)
|
||||
@@ -1487,7 +1512,7 @@ static void v9fs_lcreate(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(pdu->s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
}
|
||||
|
||||
@@ -1499,7 +1524,6 @@ static void v9fs_fsync(void *opaque)
|
||||
size_t offset = 7;
|
||||
V9fsFidState *fidp;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
err = pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
|
||||
if (err < 0) {
|
||||
@@ -1518,7 +1542,7 @@ static void v9fs_fsync(void *opaque)
|
||||
}
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static void v9fs_clunk(void *opaque)
|
||||
@@ -1551,7 +1575,7 @@ static void v9fs_clunk(void *opaque)
|
||||
err = offset;
|
||||
}
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
|
||||
@@ -1561,6 +1585,8 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
|
||||
size_t offset = 7;
|
||||
int read_count;
|
||||
int64_t xattr_len;
|
||||
V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
|
||||
VirtQueueElement *elem = &v->elems[pdu->idx];
|
||||
|
||||
xattr_len = fidp->fs.xattr.len;
|
||||
read_count = xattr_len - off;
|
||||
@@ -1577,7 +1603,8 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
|
||||
return err;
|
||||
}
|
||||
offset += err;
|
||||
err = v9fs_pack(pdu->elem.in_sg, pdu->elem.in_num, offset,
|
||||
|
||||
err = v9fs_pack(elem->in_sg, elem->in_num, offset,
|
||||
((char *)fidp->fs.xattr.value) + off,
|
||||
read_count);
|
||||
if (err < 0) {
|
||||
@@ -1667,13 +1694,7 @@ static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu,
|
||||
struct iovec *iov;
|
||||
unsigned int niov;
|
||||
|
||||
if (is_write) {
|
||||
iov = pdu->elem.out_sg;
|
||||
niov = pdu->elem.out_num;
|
||||
} else {
|
||||
iov = pdu->elem.in_sg;
|
||||
niov = pdu->elem.in_num;
|
||||
}
|
||||
virtio_init_iov_from_pdu(pdu, &iov, &niov, is_write);
|
||||
|
||||
qemu_iovec_init_external(&elem, iov, niov);
|
||||
qemu_iovec_init(qiov, niov);
|
||||
@@ -1761,7 +1782,7 @@ static void v9fs_read(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static size_t v9fs_readdir_data_size(V9fsString *name)
|
||||
@@ -1848,7 +1869,6 @@ static void v9fs_readdir(void *opaque)
|
||||
int32_t count;
|
||||
uint32_t max_count;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
retval = pdu_unmarshal(pdu, offset, "dqd", &fid,
|
||||
&initial_offset, &max_count);
|
||||
@@ -1885,7 +1905,7 @@ static void v9fs_readdir(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, retval);
|
||||
pdu_complete(pdu, retval);
|
||||
}
|
||||
|
||||
static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
|
||||
@@ -1952,7 +1972,7 @@ static void v9fs_write(void *opaque)
|
||||
|
||||
err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count);
|
||||
if (err < 0) {
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
return;
|
||||
}
|
||||
offset += err;
|
||||
@@ -2015,7 +2035,7 @@ out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
qemu_iovec_destroy(&qiov_full);
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static void v9fs_create(void *opaque)
|
||||
@@ -2182,7 +2202,7 @@ static void v9fs_create(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(pdu->s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
v9fs_string_free(&extension);
|
||||
v9fs_path_free(&path);
|
||||
@@ -2229,7 +2249,7 @@ static void v9fs_symlink(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, dfidp);
|
||||
out_nofid:
|
||||
complete_pdu(pdu->s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
v9fs_string_free(&symname);
|
||||
}
|
||||
@@ -2245,7 +2265,7 @@ static void v9fs_flush(void *opaque)
|
||||
|
||||
err = pdu_unmarshal(pdu, offset, "w", &tag);
|
||||
if (err < 0) {
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
return;
|
||||
}
|
||||
trace_v9fs_flush(pdu->tag, pdu->id, tag);
|
||||
@@ -2262,15 +2282,14 @@ static void v9fs_flush(void *opaque)
|
||||
*/
|
||||
qemu_co_queue_wait(&cancel_pdu->complete);
|
||||
cancel_pdu->cancelled = 0;
|
||||
free_pdu(pdu->s, cancel_pdu);
|
||||
pdu_free(cancel_pdu);
|
||||
}
|
||||
complete_pdu(s, pdu, 7);
|
||||
pdu_complete(pdu, 7);
|
||||
}
|
||||
|
||||
static void v9fs_link(void *opaque)
|
||||
{
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
int32_t dfid, oldfid;
|
||||
V9fsFidState *dfidp, *oldfidp;
|
||||
V9fsString name;
|
||||
@@ -2303,7 +2322,7 @@ out:
|
||||
put_fid(pdu, dfidp);
|
||||
out_nofid:
|
||||
v9fs_string_free(&name);
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
/* Only works with path name based fid */
|
||||
@@ -2348,7 +2367,7 @@ out_err:
|
||||
clunk_fid(pdu->s, fidp->fid);
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(pdu->s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static void v9fs_unlinkat(void *opaque)
|
||||
@@ -2392,7 +2411,7 @@ out_err:
|
||||
put_fid(pdu, dfidp);
|
||||
v9fs_path_free(&path);
|
||||
out_nofid:
|
||||
complete_pdu(pdu->s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
}
|
||||
|
||||
@@ -2492,7 +2511,7 @@ static void v9fs_rename(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
}
|
||||
|
||||
@@ -2593,7 +2612,7 @@ static void v9fs_renameat(void *opaque)
|
||||
}
|
||||
|
||||
out_err:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&old_name);
|
||||
v9fs_string_free(&new_name);
|
||||
}
|
||||
@@ -2608,7 +2627,6 @@ static void v9fs_wstat(void *opaque)
|
||||
struct stat stbuf;
|
||||
V9fsFidState *fidp;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
v9fs_stat_init(&v9stat);
|
||||
err = pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat);
|
||||
@@ -2690,7 +2708,7 @@ out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
v9fs_stat_free(&v9stat);
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf)
|
||||
@@ -2769,7 +2787,7 @@ static void v9fs_statfs(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, retval);
|
||||
pdu_complete(pdu, retval);
|
||||
}
|
||||
|
||||
static void v9fs_mknod(void *opaque)
|
||||
@@ -2786,7 +2804,6 @@ static void v9fs_mknod(void *opaque)
|
||||
struct stat stbuf;
|
||||
V9fsFidState *fidp;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
v9fs_string_init(&name);
|
||||
err = pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode,
|
||||
@@ -2817,7 +2834,7 @@ static void v9fs_mknod(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
}
|
||||
|
||||
@@ -2838,7 +2855,6 @@ static void v9fs_lock(void *opaque)
|
||||
V9fsFidState *fidp;
|
||||
int32_t fid, err = 0;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
status = P9_LOCK_ERROR;
|
||||
v9fs_string_init(&flock.client_id);
|
||||
@@ -2875,7 +2891,7 @@ out_nofid:
|
||||
err += offset;
|
||||
}
|
||||
trace_v9fs_lock_return(pdu->tag, pdu->id, status);
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&flock.client_id);
|
||||
}
|
||||
|
||||
@@ -2891,7 +2907,6 @@ static void v9fs_getlock(void *opaque)
|
||||
V9fsGetlock glock;
|
||||
int32_t fid, err = 0;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
v9fs_string_init(&glock.client_id);
|
||||
err = pdu_unmarshal(pdu, offset, "dbqqds", &fid, &glock.type,
|
||||
@@ -2925,7 +2940,7 @@ static void v9fs_getlock(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&glock.client_id);
|
||||
}
|
||||
|
||||
@@ -2969,7 +2984,7 @@ static void v9fs_mkdir(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(pdu->s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
}
|
||||
|
||||
@@ -3075,7 +3090,7 @@ out:
|
||||
put_fid(pdu, xattr_fidp);
|
||||
}
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
}
|
||||
|
||||
@@ -3090,7 +3105,6 @@ static void v9fs_xattrcreate(void *opaque)
|
||||
V9fsFidState *file_fidp;
|
||||
V9fsFidState *xattr_fidp;
|
||||
V9fsPDU *pdu = opaque;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
v9fs_string_init(&name);
|
||||
err = pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags);
|
||||
@@ -3116,7 +3130,7 @@ static void v9fs_xattrcreate(void *opaque)
|
||||
err = offset;
|
||||
put_fid(pdu, file_fidp);
|
||||
out_nofid:
|
||||
complete_pdu(s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
v9fs_string_free(&name);
|
||||
}
|
||||
|
||||
@@ -3156,7 +3170,7 @@ static void v9fs_readlink(void *opaque)
|
||||
out:
|
||||
put_fid(pdu, fidp);
|
||||
out_nofid:
|
||||
complete_pdu(pdu->s, pdu, err);
|
||||
pdu_complete(pdu, err);
|
||||
}
|
||||
|
||||
static CoroutineEntry *pdu_co_handlers[] = {
|
||||
@@ -3199,13 +3213,13 @@ static CoroutineEntry *pdu_co_handlers[] = {
|
||||
static void v9fs_op_not_supp(void *opaque)
|
||||
{
|
||||
V9fsPDU *pdu = opaque;
|
||||
complete_pdu(pdu->s, pdu, -EOPNOTSUPP);
|
||||
pdu_complete(pdu, -EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static void v9fs_fs_ro(void *opaque)
|
||||
{
|
||||
V9fsPDU *pdu = opaque;
|
||||
complete_pdu(pdu->s, pdu, -EROFS);
|
||||
pdu_complete(pdu, -EROFS);
|
||||
}
|
||||
|
||||
static inline bool is_read_only_op(V9fsPDU *pdu)
|
||||
@@ -3235,10 +3249,11 @@ static inline bool is_read_only_op(V9fsPDU *pdu)
|
||||
}
|
||||
}
|
||||
|
||||
static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
|
||||
void pdu_submit(V9fsPDU *pdu)
|
||||
{
|
||||
Coroutine *co;
|
||||
CoroutineEntry *handler;
|
||||
V9fsState *s = pdu->s;
|
||||
|
||||
if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
|
||||
(pdu_co_handlers[pdu->id] == NULL)) {
|
||||
@@ -3254,41 +3269,104 @@ static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
|
||||
qemu_coroutine_enter(co, pdu);
|
||||
}
|
||||
|
||||
void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
|
||||
/* Returns 0 on success, 1 on failure. */
|
||||
int v9fs_device_realize_common(V9fsState *s, Error **errp)
|
||||
{
|
||||
V9fsState *s = (V9fsState *)vdev;
|
||||
V9fsPDU *pdu;
|
||||
ssize_t len;
|
||||
V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
|
||||
int i, len;
|
||||
struct stat stat;
|
||||
FsDriverEntry *fse;
|
||||
V9fsPath path;
|
||||
int rc = 1;
|
||||
|
||||
while ((pdu = alloc_pdu(s)) &&
|
||||
(len = virtqueue_pop(vq, &pdu->elem)) != 0) {
|
||||
struct {
|
||||
uint32_t size_le;
|
||||
uint8_t id;
|
||||
uint16_t tag_le;
|
||||
} QEMU_PACKED out;
|
||||
int len;
|
||||
|
||||
pdu->s = s;
|
||||
BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
|
||||
QEMU_BUILD_BUG_ON(sizeof out != 7);
|
||||
|
||||
len = iov_to_buf(pdu->elem.out_sg, pdu->elem.out_num, 0,
|
||||
&out, sizeof out);
|
||||
BUG_ON(len != sizeof out);
|
||||
|
||||
pdu->size = le32_to_cpu(out.size_le);
|
||||
|
||||
pdu->id = out.id;
|
||||
pdu->tag = le16_to_cpu(out.tag_le);
|
||||
|
||||
qemu_co_queue_init(&pdu->complete);
|
||||
submit_pdu(s, pdu);
|
||||
/* initialize pdu allocator */
|
||||
QLIST_INIT(&s->free_list);
|
||||
QLIST_INIT(&s->active_list);
|
||||
for (i = 0; i < (MAX_REQ - 1); i++) {
|
||||
QLIST_INSERT_HEAD(&s->free_list, &v->pdus[i], next);
|
||||
v->pdus[i].s = s;
|
||||
v->pdus[i].idx = i;
|
||||
}
|
||||
free_pdu(s, pdu);
|
||||
|
||||
v9fs_path_init(&path);
|
||||
|
||||
fse = get_fsdev_fsentry(s->fsconf.fsdev_id);
|
||||
|
||||
if (!fse) {
|
||||
/* We don't have a fsdev identified by fsdev_id */
|
||||
error_setg(errp, "9pfs device couldn't find fsdev with the "
|
||||
"id = %s",
|
||||
s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!s->fsconf.tag) {
|
||||
/* we haven't specified a mount_tag */
|
||||
error_setg(errp, "fsdev with id %s needs mount_tag arguments",
|
||||
s->fsconf.fsdev_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->ctx.export_flags = fse->export_flags;
|
||||
s->ctx.fs_root = g_strdup(fse->path);
|
||||
s->ctx.exops.get_st_gen = NULL;
|
||||
len = strlen(s->fsconf.tag);
|
||||
if (len > MAX_TAG_LEN - 1) {
|
||||
error_setg(errp, "mount tag '%s' (%d bytes) is longer than "
|
||||
"maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->tag = g_strdup(s->fsconf.tag);
|
||||
s->ctx.uid = -1;
|
||||
|
||||
s->ops = fse->ops;
|
||||
|
||||
s->fid_list = NULL;
|
||||
qemu_co_rwlock_init(&s->rename_lock);
|
||||
|
||||
if (s->ops->init(&s->ctx) < 0) {
|
||||
error_setg(errp, "9pfs Failed to initialize fs-driver with id:%s"
|
||||
" and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check details of export path, We need to use fs driver
|
||||
* call back to do that. Since we are in the init path, we don't
|
||||
* use co-routines here.
|
||||
*/
|
||||
if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) {
|
||||
error_setg(errp,
|
||||
"error in converting name to path %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
if (s->ops->lstat(&s->ctx, &path, &stat)) {
|
||||
error_setg(errp, "share path %s does not exist", fse->path);
|
||||
goto out;
|
||||
} else if (!S_ISDIR(stat.st_mode)) {
|
||||
error_setg(errp, "share path %s is not a directory", fse->path);
|
||||
goto out;
|
||||
}
|
||||
v9fs_path_free(&path);
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
if (rc) {
|
||||
g_free(s->ctx.fs_root);
|
||||
g_free(s->tag);
|
||||
v9fs_path_free(&path);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __attribute__((__constructor__)) virtio_9p_set_fd_limit(void)
|
||||
void v9fs_device_unrealize_common(V9fsState *s, Error **errp)
|
||||
{
|
||||
g_free(s->ctx.fs_root);
|
||||
g_free(s->tag);
|
||||
}
|
||||
|
||||
static void __attribute__((__constructor__)) v9fs_set_fd_limit(void)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
|
326
hw/9pfs/9p.h
Normal file
326
hw/9pfs/9p.h
Normal file
@@ -0,0 +1,326 @@
|
||||
#ifndef _QEMU_9P_H
|
||||
#define _QEMU_9P_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/time.h>
|
||||
#include <utime.h>
|
||||
#include <sys/resource.h>
|
||||
#include <glib.h>
|
||||
#include "standard-headers/linux/virtio_9p.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "fsdev/file-op-9p.h"
|
||||
#include "fsdev/9p-iov-marshal.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/coroutine.h"
|
||||
|
||||
enum {
|
||||
P9_TLERROR = 6,
|
||||
P9_RLERROR,
|
||||
P9_TSTATFS = 8,
|
||||
P9_RSTATFS,
|
||||
P9_TLOPEN = 12,
|
||||
P9_RLOPEN,
|
||||
P9_TLCREATE = 14,
|
||||
P9_RLCREATE,
|
||||
P9_TSYMLINK = 16,
|
||||
P9_RSYMLINK,
|
||||
P9_TMKNOD = 18,
|
||||
P9_RMKNOD,
|
||||
P9_TRENAME = 20,
|
||||
P9_RRENAME,
|
||||
P9_TREADLINK = 22,
|
||||
P9_RREADLINK,
|
||||
P9_TGETATTR = 24,
|
||||
P9_RGETATTR,
|
||||
P9_TSETATTR = 26,
|
||||
P9_RSETATTR,
|
||||
P9_TXATTRWALK = 30,
|
||||
P9_RXATTRWALK,
|
||||
P9_TXATTRCREATE = 32,
|
||||
P9_RXATTRCREATE,
|
||||
P9_TREADDIR = 40,
|
||||
P9_RREADDIR,
|
||||
P9_TFSYNC = 50,
|
||||
P9_RFSYNC,
|
||||
P9_TLOCK = 52,
|
||||
P9_RLOCK,
|
||||
P9_TGETLOCK = 54,
|
||||
P9_RGETLOCK,
|
||||
P9_TLINK = 70,
|
||||
P9_RLINK,
|
||||
P9_TMKDIR = 72,
|
||||
P9_RMKDIR,
|
||||
P9_TRENAMEAT = 74,
|
||||
P9_RRENAMEAT,
|
||||
P9_TUNLINKAT = 76,
|
||||
P9_RUNLINKAT,
|
||||
P9_TVERSION = 100,
|
||||
P9_RVERSION,
|
||||
P9_TAUTH = 102,
|
||||
P9_RAUTH,
|
||||
P9_TATTACH = 104,
|
||||
P9_RATTACH,
|
||||
P9_TERROR = 106,
|
||||
P9_RERROR,
|
||||
P9_TFLUSH = 108,
|
||||
P9_RFLUSH,
|
||||
P9_TWALK = 110,
|
||||
P9_RWALK,
|
||||
P9_TOPEN = 112,
|
||||
P9_ROPEN,
|
||||
P9_TCREATE = 114,
|
||||
P9_RCREATE,
|
||||
P9_TREAD = 116,
|
||||
P9_RREAD,
|
||||
P9_TWRITE = 118,
|
||||
P9_RWRITE,
|
||||
P9_TCLUNK = 120,
|
||||
P9_RCLUNK,
|
||||
P9_TREMOVE = 122,
|
||||
P9_RREMOVE,
|
||||
P9_TSTAT = 124,
|
||||
P9_RSTAT,
|
||||
P9_TWSTAT = 126,
|
||||
P9_RWSTAT,
|
||||
};
|
||||
|
||||
|
||||
/* qid.types */
|
||||
enum {
|
||||
P9_QTDIR = 0x80,
|
||||
P9_QTAPPEND = 0x40,
|
||||
P9_QTEXCL = 0x20,
|
||||
P9_QTMOUNT = 0x10,
|
||||
P9_QTAUTH = 0x08,
|
||||
P9_QTTMP = 0x04,
|
||||
P9_QTSYMLINK = 0x02,
|
||||
P9_QTLINK = 0x01,
|
||||
P9_QTFILE = 0x00,
|
||||
};
|
||||
|
||||
enum p9_proto_version {
|
||||
V9FS_PROTO_2000U = 0x01,
|
||||
V9FS_PROTO_2000L = 0x02,
|
||||
};
|
||||
|
||||
#define P9_NOTAG (u16)(~0)
|
||||
#define P9_NOFID (u32)(~0)
|
||||
#define P9_MAXWELEM 16
|
||||
|
||||
#define FID_REFERENCED 0x1
|
||||
#define FID_NON_RECLAIMABLE 0x2
|
||||
static inline char *rpath(FsContext *ctx, const char *path)
|
||||
{
|
||||
return g_strdup_printf("%s/%s", ctx->fs_root, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* ample room for Twrite/Rread header
|
||||
* size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
|
||||
*/
|
||||
#define P9_IOHDRSZ 24
|
||||
|
||||
typedef struct V9fsPDU V9fsPDU;
|
||||
struct V9fsState;
|
||||
|
||||
struct V9fsPDU
|
||||
{
|
||||
uint32_t size;
|
||||
uint16_t tag;
|
||||
uint8_t id;
|
||||
uint8_t cancelled;
|
||||
CoQueue complete;
|
||||
struct V9fsState *s;
|
||||
QLIST_ENTRY(V9fsPDU) next;
|
||||
uint32_t idx;
|
||||
};
|
||||
|
||||
|
||||
/* FIXME
|
||||
* 1) change user needs to set groups and stuff
|
||||
*/
|
||||
|
||||
#define MAX_REQ 128
|
||||
#define MAX_TAG_LEN 32
|
||||
|
||||
#define BUG_ON(cond) assert(!(cond))
|
||||
|
||||
typedef struct V9fsFidState V9fsFidState;
|
||||
|
||||
enum {
|
||||
P9_FID_NONE = 0,
|
||||
P9_FID_FILE,
|
||||
P9_FID_DIR,
|
||||
P9_FID_XATTR,
|
||||
};
|
||||
|
||||
typedef struct V9fsConf
|
||||
{
|
||||
/* tag name for the device */
|
||||
char *tag;
|
||||
char *fsdev_id;
|
||||
} V9fsConf;
|
||||
|
||||
typedef struct V9fsXattr
|
||||
{
|
||||
int64_t copied_len;
|
||||
int64_t len;
|
||||
void *value;
|
||||
V9fsString name;
|
||||
int flags;
|
||||
} V9fsXattr;
|
||||
|
||||
/*
|
||||
* Filled by fs driver on open and other
|
||||
* calls.
|
||||
*/
|
||||
union V9fsFidOpenState {
|
||||
int fd;
|
||||
DIR *dir;
|
||||
V9fsXattr xattr;
|
||||
/*
|
||||
* private pointer for fs drivers, that
|
||||
* have its own internal representation of
|
||||
* open files.
|
||||
*/
|
||||
void *private;
|
||||
};
|
||||
|
||||
struct V9fsFidState
|
||||
{
|
||||
int fid_type;
|
||||
int32_t fid;
|
||||
V9fsPath path;
|
||||
V9fsFidOpenState fs;
|
||||
V9fsFidOpenState fs_reclaim;
|
||||
int flags;
|
||||
int open_flags;
|
||||
uid_t uid;
|
||||
int ref;
|
||||
int clunked;
|
||||
V9fsFidState *next;
|
||||
V9fsFidState *rclm_lst;
|
||||
};
|
||||
|
||||
typedef struct V9fsState
|
||||
{
|
||||
QLIST_HEAD(, V9fsPDU) free_list;
|
||||
QLIST_HEAD(, V9fsPDU) active_list;
|
||||
V9fsFidState *fid_list;
|
||||
FileOperations *ops;
|
||||
FsContext ctx;
|
||||
char *tag;
|
||||
enum p9_proto_version proto_version;
|
||||
int32_t msize;
|
||||
/*
|
||||
* lock ensuring atomic path update
|
||||
* on rename.
|
||||
*/
|
||||
CoRwlock rename_lock;
|
||||
int32_t root_fid;
|
||||
Error *migration_blocker;
|
||||
V9fsConf fsconf;
|
||||
} V9fsState;
|
||||
|
||||
/* 9p2000.L open flags */
|
||||
#define P9_DOTL_RDONLY 00000000
|
||||
#define P9_DOTL_WRONLY 00000001
|
||||
#define P9_DOTL_RDWR 00000002
|
||||
#define P9_DOTL_NOACCESS 00000003
|
||||
#define P9_DOTL_CREATE 00000100
|
||||
#define P9_DOTL_EXCL 00000200
|
||||
#define P9_DOTL_NOCTTY 00000400
|
||||
#define P9_DOTL_TRUNC 00001000
|
||||
#define P9_DOTL_APPEND 00002000
|
||||
#define P9_DOTL_NONBLOCK 00004000
|
||||
#define P9_DOTL_DSYNC 00010000
|
||||
#define P9_DOTL_FASYNC 00020000
|
||||
#define P9_DOTL_DIRECT 00040000
|
||||
#define P9_DOTL_LARGEFILE 00100000
|
||||
#define P9_DOTL_DIRECTORY 00200000
|
||||
#define P9_DOTL_NOFOLLOW 00400000
|
||||
#define P9_DOTL_NOATIME 01000000
|
||||
#define P9_DOTL_CLOEXEC 02000000
|
||||
#define P9_DOTL_SYNC 04000000
|
||||
|
||||
/* 9p2000.L at flags */
|
||||
#define P9_DOTL_AT_REMOVEDIR 0x200
|
||||
|
||||
/* 9P2000.L lock type */
|
||||
#define P9_LOCK_TYPE_RDLCK 0
|
||||
#define P9_LOCK_TYPE_WRLCK 1
|
||||
#define P9_LOCK_TYPE_UNLCK 2
|
||||
|
||||
#define P9_LOCK_SUCCESS 0
|
||||
#define P9_LOCK_BLOCKED 1
|
||||
#define P9_LOCK_ERROR 2
|
||||
#define P9_LOCK_GRACE 3
|
||||
|
||||
#define P9_LOCK_FLAGS_BLOCK 1
|
||||
#define P9_LOCK_FLAGS_RECLAIM 2
|
||||
|
||||
typedef struct V9fsFlock
|
||||
{
|
||||
uint8_t type;
|
||||
uint32_t flags;
|
||||
uint64_t start; /* absolute offset */
|
||||
uint64_t length;
|
||||
uint32_t proc_id;
|
||||
V9fsString client_id;
|
||||
} V9fsFlock;
|
||||
|
||||
typedef struct V9fsGetlock
|
||||
{
|
||||
uint8_t type;
|
||||
uint64_t start; /* absolute offset */
|
||||
uint64_t length;
|
||||
uint32_t proc_id;
|
||||
V9fsString client_id;
|
||||
} V9fsGetlock;
|
||||
|
||||
extern int open_fd_hw;
|
||||
extern int total_open_fd;
|
||||
|
||||
static inline void v9fs_path_write_lock(V9fsState *s)
|
||||
{
|
||||
if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
|
||||
qemu_co_rwlock_wrlock(&s->rename_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void v9fs_path_read_lock(V9fsState *s)
|
||||
{
|
||||
if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
|
||||
qemu_co_rwlock_rdlock(&s->rename_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void v9fs_path_unlock(V9fsState *s)
|
||||
{
|
||||
if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
|
||||
qemu_co_rwlock_unlock(&s->rename_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t v9fs_request_cancelled(V9fsPDU *pdu)
|
||||
{
|
||||
return pdu->cancelled;
|
||||
}
|
||||
|
||||
extern void v9fs_reclaim_fd(V9fsPDU *pdu);
|
||||
extern void v9fs_path_init(V9fsPath *path);
|
||||
extern void v9fs_path_free(V9fsPath *path);
|
||||
extern void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs);
|
||||
extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
|
||||
const char *name, V9fsPath *path);
|
||||
extern int v9fs_device_realize_common(V9fsState *s, Error **errp);
|
||||
extern void v9fs_device_unrealize_common(V9fsState *s, Error **errp);
|
||||
|
||||
ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...);
|
||||
ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...);
|
||||
V9fsPDU *pdu_alloc(V9fsState *s);
|
||||
void pdu_free(V9fsPDU *pdu);
|
||||
void pdu_submit(V9fsPDU *pdu);
|
||||
|
||||
#endif
|
@@ -1,9 +1,9 @@
|
||||
common-obj-y = virtio-9p.o
|
||||
common-obj-y += virtio-9p-local.o virtio-9p-xattr.o
|
||||
common-obj-y += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
|
||||
common-obj-y += virtio-9p-coth.o cofs.o codir.o cofile.o
|
||||
common-obj-y += coxattr.o virtio-9p-synth.o
|
||||
common-obj-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o
|
||||
common-obj-y += virtio-9p-proxy.o
|
||||
common-obj-y = 9p.o
|
||||
common-obj-y += 9p-local.o 9p-xattr.o
|
||||
common-obj-y += 9p-xattr-user.o 9p-posix-acl.o
|
||||
common-obj-y += coth.o cofs.o codir.o cofile.o
|
||||
common-obj-y += coxattr.o 9p-synth.o
|
||||
common-obj-$(CONFIG_OPEN_BY_HANDLE) += 9p-handle.o
|
||||
common-obj-y += 9p-proxy.o
|
||||
|
||||
obj-y += virtio-9p-device.o
|
||||
|
@@ -15,7 +15,7 @@
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "virtio-9p-coth.h"
|
||||
#include "coth.h"
|
||||
|
||||
int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent,
|
||||
struct dirent **result)
|
||||
|
@@ -15,7 +15,7 @@
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "virtio-9p-coth.h"
|
||||
#include "coth.h"
|
||||
|
||||
int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
|
||||
V9fsStatDotl *v9stat)
|
||||
|
@@ -15,7 +15,7 @@
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "virtio-9p-coth.h"
|
||||
#include "coth.h"
|
||||
|
||||
static ssize_t __readlink(V9fsState *s, V9fsPath *path, V9fsString *buf)
|
||||
{
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p backend
|
||||
* 9p backend
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "block/thread-pool.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "virtio-9p-coth.h"
|
||||
#include "coth.h"
|
||||
|
||||
/* Called from QEMU I/O thread. */
|
||||
static void coroutine_enter_cb(void *opaque, int ret)
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Virtio 9p backend
|
||||
* 9p backend
|
||||
*
|
||||
* Copyright IBM, Corp. 2010
|
||||
*
|
||||
@@ -12,8 +12,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _QEMU_VIRTIO_9P_COTH_H
|
||||
#define _QEMU_VIRTIO_9P_COTH_H
|
||||
#ifndef _QEMU_9P_COTH_H
|
||||
#define _QEMU_9P_COTH_H
|
||||
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/coroutine.h"
|
@@ -15,7 +15,7 @@
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "virtio-9p-coth.h"
|
||||
#include "coth.h"
|
||||
|
||||
int v9fs_co_llistxattr(V9fsPDU *pdu, V9fsPath *path, void *value, size_t size)
|
||||
{
|
||||
|
@@ -12,14 +12,65 @@
|
||||
*/
|
||||
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-9p.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "virtio-9p.h"
|
||||
#include "fsdev/qemu-fsdev.h"
|
||||
#include "virtio-9p-xattr.h"
|
||||
#include "virtio-9p-coth.h"
|
||||
#include "9p-xattr.h"
|
||||
#include "coth.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "qemu/iov.h"
|
||||
|
||||
void virtio_9p_push_and_notify(V9fsPDU *pdu)
|
||||
{
|
||||
V9fsState *s = pdu->s;
|
||||
V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
|
||||
VirtQueueElement *elem = &v->elems[pdu->idx];
|
||||
|
||||
/* push onto queue and notify */
|
||||
virtqueue_push(v->vq, elem, pdu->size);
|
||||
|
||||
/* FIXME: we should batch these completions */
|
||||
virtio_notify(VIRTIO_DEVICE(v), v->vq);
|
||||
}
|
||||
|
||||
static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
V9fsVirtioState *v = (V9fsVirtioState *)vdev;
|
||||
V9fsState *s = &v->state;
|
||||
V9fsPDU *pdu;
|
||||
ssize_t len;
|
||||
|
||||
while ((pdu = pdu_alloc(s))) {
|
||||
struct {
|
||||
uint32_t size_le;
|
||||
uint8_t id;
|
||||
uint16_t tag_le;
|
||||
} QEMU_PACKED out;
|
||||
VirtQueueElement *elem = &v->elems[pdu->idx];
|
||||
|
||||
len = virtqueue_pop(vq, elem);
|
||||
if (!len) {
|
||||
pdu_free(pdu);
|
||||
break;
|
||||
}
|
||||
|
||||
BUG_ON(elem->out_num == 0 || elem->in_num == 0);
|
||||
QEMU_BUILD_BUG_ON(sizeof out != 7);
|
||||
|
||||
len = iov_to_buf(elem->out_sg, elem->out_num, 0,
|
||||
&out, sizeof out);
|
||||
BUG_ON(len != sizeof out);
|
||||
|
||||
pdu->size = le32_to_cpu(out.size_le);
|
||||
|
||||
pdu->id = out.id;
|
||||
pdu->tag = le16_to_cpu(out.tag_le);
|
||||
|
||||
qemu_co_queue_init(&pdu->complete);
|
||||
pdu_submit(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t virtio_9p_get_features(VirtIODevice *vdev, uint64_t features,
|
||||
Error **errp)
|
||||
@@ -32,14 +83,15 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
|
||||
{
|
||||
int len;
|
||||
struct virtio_9p_config *cfg;
|
||||
V9fsState *s = VIRTIO_9P(vdev);
|
||||
V9fsVirtioState *v = VIRTIO_9P(vdev);
|
||||
V9fsState *s = &v->state;
|
||||
|
||||
len = strlen(s->tag);
|
||||
cfg = g_malloc0(sizeof(struct virtio_9p_config) + len);
|
||||
virtio_stw_p(vdev, &cfg->tag_len, len);
|
||||
/* We don't copy the terminating null to config space */
|
||||
memcpy(cfg->tag, s->tag, len);
|
||||
memcpy(config, cfg, s->config_size);
|
||||
memcpy(config, cfg, v->config_size);
|
||||
g_free(cfg);
|
||||
}
|
||||
|
||||
@@ -56,111 +108,74 @@ static int virtio_9p_load(QEMUFile *f, void *opaque, int version_id)
|
||||
static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
V9fsState *s = VIRTIO_9P(dev);
|
||||
int i, len;
|
||||
struct stat stat;
|
||||
FsDriverEntry *fse;
|
||||
V9fsPath path;
|
||||
V9fsVirtioState *v = VIRTIO_9P(dev);
|
||||
V9fsState *s = &v->state;
|
||||
|
||||
virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P,
|
||||
sizeof(struct virtio_9p_config) + MAX_TAG_LEN);
|
||||
|
||||
/* initialize pdu allocator */
|
||||
QLIST_INIT(&s->free_list);
|
||||
QLIST_INIT(&s->active_list);
|
||||
for (i = 0; i < (MAX_REQ - 1); i++) {
|
||||
QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
|
||||
}
|
||||
|
||||
s->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output);
|
||||
|
||||
v9fs_path_init(&path);
|
||||
|
||||
fse = get_fsdev_fsentry(s->fsconf.fsdev_id);
|
||||
|
||||
if (!fse) {
|
||||
/* We don't have a fsdev identified by fsdev_id */
|
||||
error_setg(errp, "Virtio-9p device couldn't find fsdev with the "
|
||||
"id = %s",
|
||||
s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL");
|
||||
if (v9fs_device_realize_common(s, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!s->fsconf.tag) {
|
||||
/* we haven't specified a mount_tag */
|
||||
error_setg(errp, "fsdev with id %s needs mount_tag arguments",
|
||||
s->fsconf.fsdev_id);
|
||||
goto out;
|
||||
}
|
||||
v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag);
|
||||
virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size);
|
||||
v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output);
|
||||
register_savevm(dev, "virtio-9p", -1, 1, virtio_9p_save, virtio_9p_load, v);
|
||||
|
||||
s->ctx.export_flags = fse->export_flags;
|
||||
s->ctx.fs_root = g_strdup(fse->path);
|
||||
s->ctx.exops.get_st_gen = NULL;
|
||||
len = strlen(s->fsconf.tag);
|
||||
if (len > MAX_TAG_LEN - 1) {
|
||||
error_setg(errp, "mount tag '%s' (%d bytes) is longer than "
|
||||
"maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->tag = g_strdup(s->fsconf.tag);
|
||||
s->ctx.uid = -1;
|
||||
|
||||
s->ops = fse->ops;
|
||||
s->config_size = sizeof(struct virtio_9p_config) + len;
|
||||
s->fid_list = NULL;
|
||||
qemu_co_rwlock_init(&s->rename_lock);
|
||||
|
||||
if (s->ops->init(&s->ctx) < 0) {
|
||||
error_setg(errp, "Virtio-9p Failed to initialize fs-driver with id:%s"
|
||||
" and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check details of export path, We need to use fs driver
|
||||
* call back to do that. Since we are in the init path, we don't
|
||||
* use co-routines here.
|
||||
*/
|
||||
if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) {
|
||||
error_setg(errp,
|
||||
"error in converting name to path %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
if (s->ops->lstat(&s->ctx, &path, &stat)) {
|
||||
error_setg(errp, "share path %s does not exist", fse->path);
|
||||
goto out;
|
||||
} else if (!S_ISDIR(stat.st_mode)) {
|
||||
error_setg(errp, "share path %s is not a directory", fse->path);
|
||||
goto out;
|
||||
}
|
||||
v9fs_path_free(&path);
|
||||
|
||||
register_savevm(dev, "virtio-9p", -1, 1, virtio_9p_save, virtio_9p_load, s);
|
||||
return;
|
||||
out:
|
||||
g_free(s->ctx.fs_root);
|
||||
g_free(s->tag);
|
||||
virtio_cleanup(vdev);
|
||||
v9fs_path_free(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
V9fsState *s = VIRTIO_9P(dev);
|
||||
V9fsVirtioState *v = VIRTIO_9P(dev);
|
||||
V9fsState *s = &v->state;
|
||||
|
||||
virtio_cleanup(vdev);
|
||||
unregister_savevm(dev, "virtio-9p", s);
|
||||
g_free(s->ctx.fs_root);
|
||||
g_free(s->tag);
|
||||
unregister_savevm(dev, "virtio-9p", v);
|
||||
v9fs_device_unrealize_common(s, errp);
|
||||
}
|
||||
|
||||
ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
V9fsState *s = pdu->s;
|
||||
V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
|
||||
VirtQueueElement *elem = &v->elems[pdu->idx];
|
||||
|
||||
return v9fs_iov_vmarshal(elem->in_sg, elem->in_num, offset, 1, fmt, ap);
|
||||
}
|
||||
|
||||
ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
V9fsState *s = pdu->s;
|
||||
V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
|
||||
VirtQueueElement *elem = &v->elems[pdu->idx];
|
||||
|
||||
return v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap);
|
||||
}
|
||||
|
||||
void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov,
|
||||
unsigned int *pniov, bool is_write)
|
||||
{
|
||||
V9fsState *s = pdu->s;
|
||||
V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
|
||||
VirtQueueElement *elem = &v->elems[pdu->idx];
|
||||
|
||||
if (is_write) {
|
||||
*piov = elem->out_sg;
|
||||
*pniov = elem->out_num;
|
||||
} else {
|
||||
*piov = elem->in_sg;
|
||||
*pniov = elem->in_num;
|
||||
}
|
||||
}
|
||||
|
||||
/* virtio-9p device */
|
||||
|
||||
static Property virtio_9p_properties[] = {
|
||||
DEFINE_PROP_STRING("mount_tag", V9fsState, fsconf.tag),
|
||||
DEFINE_PROP_STRING("fsdev", V9fsState, fsconf.fsdev_id),
|
||||
DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag),
|
||||
DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@@ -180,7 +195,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data)
|
||||
static const TypeInfo virtio_device_info = {
|
||||
.name = TYPE_VIRTIO_9P,
|
||||
.parent = TYPE_VIRTIO_DEVICE,
|
||||
.instance_size = sizeof(V9fsState),
|
||||
.instance_size = sizeof(V9fsVirtioState),
|
||||
.class_init = virtio_9p_class_init,
|
||||
};
|
||||
|
||||
|
@@ -1,394 +1,31 @@
|
||||
#ifndef _QEMU_VIRTIO_9P_H
|
||||
#define _QEMU_VIRTIO_9P_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/time.h>
|
||||
#include <utime.h>
|
||||
#include <sys/resource.h>
|
||||
#include <glib.h>
|
||||
#include "standard-headers/linux/virtio_9p.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-9p.h"
|
||||
#include "fsdev/file-op-9p.h"
|
||||
#include "fsdev/virtio-9p-marshal.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "9p.h"
|
||||
|
||||
enum {
|
||||
P9_TLERROR = 6,
|
||||
P9_RLERROR,
|
||||
P9_TSTATFS = 8,
|
||||
P9_RSTATFS,
|
||||
P9_TLOPEN = 12,
|
||||
P9_RLOPEN,
|
||||
P9_TLCREATE = 14,
|
||||
P9_RLCREATE,
|
||||
P9_TSYMLINK = 16,
|
||||
P9_RSYMLINK,
|
||||
P9_TMKNOD = 18,
|
||||
P9_RMKNOD,
|
||||
P9_TRENAME = 20,
|
||||
P9_RRENAME,
|
||||
P9_TREADLINK = 22,
|
||||
P9_RREADLINK,
|
||||
P9_TGETATTR = 24,
|
||||
P9_RGETATTR,
|
||||
P9_TSETATTR = 26,
|
||||
P9_RSETATTR,
|
||||
P9_TXATTRWALK = 30,
|
||||
P9_RXATTRWALK,
|
||||
P9_TXATTRCREATE = 32,
|
||||
P9_RXATTRCREATE,
|
||||
P9_TREADDIR = 40,
|
||||
P9_RREADDIR,
|
||||
P9_TFSYNC = 50,
|
||||
P9_RFSYNC,
|
||||
P9_TLOCK = 52,
|
||||
P9_RLOCK,
|
||||
P9_TGETLOCK = 54,
|
||||
P9_RGETLOCK,
|
||||
P9_TLINK = 70,
|
||||
P9_RLINK,
|
||||
P9_TMKDIR = 72,
|
||||
P9_RMKDIR,
|
||||
P9_TRENAMEAT = 74,
|
||||
P9_RRENAMEAT,
|
||||
P9_TUNLINKAT = 76,
|
||||
P9_RUNLINKAT,
|
||||
P9_TVERSION = 100,
|
||||
P9_RVERSION,
|
||||
P9_TAUTH = 102,
|
||||
P9_RAUTH,
|
||||
P9_TATTACH = 104,
|
||||
P9_RATTACH,
|
||||
P9_TERROR = 106,
|
||||
P9_RERROR,
|
||||
P9_TFLUSH = 108,
|
||||
P9_RFLUSH,
|
||||
P9_TWALK = 110,
|
||||
P9_RWALK,
|
||||
P9_TOPEN = 112,
|
||||
P9_ROPEN,
|
||||
P9_TCREATE = 114,
|
||||
P9_RCREATE,
|
||||
P9_TREAD = 116,
|
||||
P9_RREAD,
|
||||
P9_TWRITE = 118,
|
||||
P9_RWRITE,
|
||||
P9_TCLUNK = 120,
|
||||
P9_RCLUNK,
|
||||
P9_TREMOVE = 122,
|
||||
P9_RREMOVE,
|
||||
P9_TSTAT = 124,
|
||||
P9_RSTAT,
|
||||
P9_TWSTAT = 126,
|
||||
P9_RWSTAT,
|
||||
};
|
||||
|
||||
|
||||
/* qid.types */
|
||||
enum {
|
||||
P9_QTDIR = 0x80,
|
||||
P9_QTAPPEND = 0x40,
|
||||
P9_QTEXCL = 0x20,
|
||||
P9_QTMOUNT = 0x10,
|
||||
P9_QTAUTH = 0x08,
|
||||
P9_QTTMP = 0x04,
|
||||
P9_QTSYMLINK = 0x02,
|
||||
P9_QTLINK = 0x01,
|
||||
P9_QTFILE = 0x00,
|
||||
};
|
||||
|
||||
enum p9_proto_version {
|
||||
V9FS_PROTO_2000U = 0x01,
|
||||
V9FS_PROTO_2000L = 0x02,
|
||||
};
|
||||
|
||||
#define P9_NOTAG (u16)(~0)
|
||||
#define P9_NOFID (u32)(~0)
|
||||
#define P9_MAXWELEM 16
|
||||
|
||||
#define FID_REFERENCED 0x1
|
||||
#define FID_NON_RECLAIMABLE 0x2
|
||||
static inline char *rpath(FsContext *ctx, const char *path)
|
||||
{
|
||||
return g_strdup_printf("%s/%s", ctx->fs_root, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* ample room for Twrite/Rread header
|
||||
* size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
|
||||
*/
|
||||
#define P9_IOHDRSZ 24
|
||||
|
||||
typedef struct V9fsPDU V9fsPDU;
|
||||
struct V9fsState;
|
||||
|
||||
struct V9fsPDU
|
||||
{
|
||||
uint32_t size;
|
||||
uint16_t tag;
|
||||
uint8_t id;
|
||||
uint8_t cancelled;
|
||||
CoQueue complete;
|
||||
VirtQueueElement elem;
|
||||
struct V9fsState *s;
|
||||
QLIST_ENTRY(V9fsPDU) next;
|
||||
};
|
||||
|
||||
|
||||
/* FIXME
|
||||
* 1) change user needs to set groups and stuff
|
||||
*/
|
||||
|
||||
#define MAX_REQ 128
|
||||
#define MAX_TAG_LEN 32
|
||||
|
||||
#define BUG_ON(cond) assert(!(cond))
|
||||
|
||||
typedef struct V9fsFidState V9fsFidState;
|
||||
|
||||
enum {
|
||||
P9_FID_NONE = 0,
|
||||
P9_FID_FILE,
|
||||
P9_FID_DIR,
|
||||
P9_FID_XATTR,
|
||||
};
|
||||
|
||||
typedef struct V9fsXattr
|
||||
{
|
||||
int64_t copied_len;
|
||||
int64_t len;
|
||||
void *value;
|
||||
V9fsString name;
|
||||
int flags;
|
||||
} V9fsXattr;
|
||||
|
||||
/*
|
||||
* Filled by fs driver on open and other
|
||||
* calls.
|
||||
*/
|
||||
union V9fsFidOpenState {
|
||||
int fd;
|
||||
DIR *dir;
|
||||
V9fsXattr xattr;
|
||||
/*
|
||||
* private pointer for fs drivers, that
|
||||
* have its own internal representation of
|
||||
* open files.
|
||||
*/
|
||||
void *private;
|
||||
};
|
||||
|
||||
struct V9fsFidState
|
||||
{
|
||||
int fid_type;
|
||||
int32_t fid;
|
||||
V9fsPath path;
|
||||
V9fsFidOpenState fs;
|
||||
V9fsFidOpenState fs_reclaim;
|
||||
int flags;
|
||||
int open_flags;
|
||||
uid_t uid;
|
||||
int ref;
|
||||
int clunked;
|
||||
V9fsFidState *next;
|
||||
V9fsFidState *rclm_lst;
|
||||
};
|
||||
|
||||
typedef struct V9fsState
|
||||
typedef struct V9fsVirtioState
|
||||
{
|
||||
VirtIODevice parent_obj;
|
||||
VirtQueue *vq;
|
||||
V9fsPDU pdus[MAX_REQ];
|
||||
QLIST_HEAD(, V9fsPDU) free_list;
|
||||
QLIST_HEAD(, V9fsPDU) active_list;
|
||||
V9fsFidState *fid_list;
|
||||
FileOperations *ops;
|
||||
FsContext ctx;
|
||||
char *tag;
|
||||
size_t config_size;
|
||||
enum p9_proto_version proto_version;
|
||||
int32_t msize;
|
||||
/*
|
||||
* lock ensuring atomic path update
|
||||
* on rename.
|
||||
*/
|
||||
CoRwlock rename_lock;
|
||||
int32_t root_fid;
|
||||
Error *migration_blocker;
|
||||
V9fsConf fsconf;
|
||||
} V9fsState;
|
||||
V9fsPDU pdus[MAX_REQ];
|
||||
VirtQueueElement elems[MAX_REQ];
|
||||
V9fsState state;
|
||||
} V9fsVirtioState;
|
||||
|
||||
typedef struct V9fsStatState {
|
||||
V9fsPDU *pdu;
|
||||
size_t offset;
|
||||
V9fsStat v9stat;
|
||||
V9fsFidState *fidp;
|
||||
struct stat stbuf;
|
||||
} V9fsStatState;
|
||||
extern void virtio_9p_push_and_notify(V9fsPDU *pdu);
|
||||
|
||||
typedef struct V9fsOpenState {
|
||||
V9fsPDU *pdu;
|
||||
size_t offset;
|
||||
int32_t mode;
|
||||
V9fsFidState *fidp;
|
||||
V9fsQID qid;
|
||||
struct stat stbuf;
|
||||
int iounit;
|
||||
} V9fsOpenState;
|
||||
|
||||
typedef struct V9fsReadState {
|
||||
V9fsPDU *pdu;
|
||||
size_t offset;
|
||||
int32_t count;
|
||||
int32_t total;
|
||||
int64_t off;
|
||||
V9fsFidState *fidp;
|
||||
struct iovec iov[128]; /* FIXME: bad, bad, bad */
|
||||
struct iovec *sg;
|
||||
off_t dir_pos;
|
||||
struct dirent *dent;
|
||||
struct stat stbuf;
|
||||
V9fsString name;
|
||||
V9fsStat v9stat;
|
||||
int32_t len;
|
||||
int32_t cnt;
|
||||
int32_t max_count;
|
||||
} V9fsReadState;
|
||||
|
||||
typedef struct V9fsWriteState {
|
||||
V9fsPDU *pdu;
|
||||
size_t offset;
|
||||
int32_t len;
|
||||
int32_t count;
|
||||
int32_t total;
|
||||
int64_t off;
|
||||
V9fsFidState *fidp;
|
||||
struct iovec iov[128]; /* FIXME: bad, bad, bad */
|
||||
struct iovec *sg;
|
||||
int cnt;
|
||||
} V9fsWriteState;
|
||||
|
||||
typedef struct V9fsMkState {
|
||||
V9fsPDU *pdu;
|
||||
size_t offset;
|
||||
V9fsQID qid;
|
||||
struct stat stbuf;
|
||||
V9fsString name;
|
||||
V9fsString fullname;
|
||||
} V9fsMkState;
|
||||
|
||||
/* 9p2000.L open flags */
|
||||
#define P9_DOTL_RDONLY 00000000
|
||||
#define P9_DOTL_WRONLY 00000001
|
||||
#define P9_DOTL_RDWR 00000002
|
||||
#define P9_DOTL_NOACCESS 00000003
|
||||
#define P9_DOTL_CREATE 00000100
|
||||
#define P9_DOTL_EXCL 00000200
|
||||
#define P9_DOTL_NOCTTY 00000400
|
||||
#define P9_DOTL_TRUNC 00001000
|
||||
#define P9_DOTL_APPEND 00002000
|
||||
#define P9_DOTL_NONBLOCK 00004000
|
||||
#define P9_DOTL_DSYNC 00010000
|
||||
#define P9_DOTL_FASYNC 00020000
|
||||
#define P9_DOTL_DIRECT 00040000
|
||||
#define P9_DOTL_LARGEFILE 00100000
|
||||
#define P9_DOTL_DIRECTORY 00200000
|
||||
#define P9_DOTL_NOFOLLOW 00400000
|
||||
#define P9_DOTL_NOATIME 01000000
|
||||
#define P9_DOTL_CLOEXEC 02000000
|
||||
#define P9_DOTL_SYNC 04000000
|
||||
|
||||
/* 9p2000.L at flags */
|
||||
#define P9_DOTL_AT_REMOVEDIR 0x200
|
||||
|
||||
/* 9P2000.L lock type */
|
||||
#define P9_LOCK_TYPE_RDLCK 0
|
||||
#define P9_LOCK_TYPE_WRLCK 1
|
||||
#define P9_LOCK_TYPE_UNLCK 2
|
||||
|
||||
#define P9_LOCK_SUCCESS 0
|
||||
#define P9_LOCK_BLOCKED 1
|
||||
#define P9_LOCK_ERROR 2
|
||||
#define P9_LOCK_GRACE 3
|
||||
|
||||
#define P9_LOCK_FLAGS_BLOCK 1
|
||||
#define P9_LOCK_FLAGS_RECLAIM 2
|
||||
|
||||
typedef struct V9fsFlock
|
||||
{
|
||||
uint8_t type;
|
||||
uint32_t flags;
|
||||
uint64_t start; /* absolute offset */
|
||||
uint64_t length;
|
||||
uint32_t proc_id;
|
||||
V9fsString client_id;
|
||||
} V9fsFlock;
|
||||
|
||||
typedef struct V9fsGetlock
|
||||
{
|
||||
uint8_t type;
|
||||
uint64_t start; /* absolute offset */
|
||||
uint64_t length;
|
||||
uint32_t proc_id;
|
||||
V9fsString client_id;
|
||||
} V9fsGetlock;
|
||||
|
||||
extern int open_fd_hw;
|
||||
extern int total_open_fd;
|
||||
|
||||
size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
|
||||
size_t offset, size_t size, int pack);
|
||||
|
||||
static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count,
|
||||
size_t offset, size_t size)
|
||||
{
|
||||
return pdu_packunpack(dst, sg, sg_count, offset, size, 0);
|
||||
}
|
||||
|
||||
static inline void v9fs_path_write_lock(V9fsState *s)
|
||||
{
|
||||
if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
|
||||
qemu_co_rwlock_wrlock(&s->rename_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void v9fs_path_read_lock(V9fsState *s)
|
||||
{
|
||||
if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
|
||||
qemu_co_rwlock_rdlock(&s->rename_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void v9fs_path_unlock(V9fsState *s)
|
||||
{
|
||||
if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
|
||||
qemu_co_rwlock_unlock(&s->rename_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t v9fs_request_cancelled(V9fsPDU *pdu)
|
||||
{
|
||||
return pdu->cancelled;
|
||||
}
|
||||
|
||||
extern void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq);
|
||||
extern void v9fs_reclaim_fd(V9fsPDU *pdu);
|
||||
extern void v9fs_path_init(V9fsPath *path);
|
||||
extern void v9fs_path_free(V9fsPath *path);
|
||||
extern void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs);
|
||||
extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
|
||||
const char *name, V9fsPath *path);
|
||||
|
||||
#define pdu_marshal(pdu, offset, fmt, args...) \
|
||||
v9fs_marshal(pdu->elem.in_sg, pdu->elem.in_num, offset, 1, fmt, ##args)
|
||||
#define pdu_unmarshal(pdu, offset, fmt, args...) \
|
||||
v9fs_unmarshal(pdu->elem.out_sg, pdu->elem.out_num, offset, 1, fmt, ##args)
|
||||
ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset,
|
||||
const char *fmt, va_list ap);
|
||||
ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset,
|
||||
const char *fmt, va_list ap);
|
||||
void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov,
|
||||
unsigned int *pniov, bool is_write);
|
||||
|
||||
#define TYPE_VIRTIO_9P "virtio-9p-device"
|
||||
#define VIRTIO_9P(obj) \
|
||||
OBJECT_CHECK(V9fsState, (obj), TYPE_VIRTIO_9P)
|
||||
OBJECT_CHECK(V9fsVirtioState, (obj), TYPE_VIRTIO_9P)
|
||||
|
||||
#endif
|
||||
|
@@ -1,7 +1,7 @@
|
||||
common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
|
||||
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
|
||||
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
|
||||
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
|
||||
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o cpu_hotplug_acpi_table.o
|
||||
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o
|
||||
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
|
||||
common-obj-$(CONFIG_ACPI) += acpi_interface.o
|
||||
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
|
||||
|
135
hw/acpi/cpu_hotplug_acpi_table.c
Normal file
135
hw/acpi/cpu_hotplug_acpi_table.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "hw/acpi/cpu_hotplug.h"
|
||||
|
||||
void build_cpu_hotplug_aml(Aml *ctx)
|
||||
{
|
||||
Aml *method;
|
||||
Aml *if_ctx;
|
||||
Aml *else_ctx;
|
||||
Aml *sb_scope = aml_scope("_SB");
|
||||
uint8_t madt_tmpl[8] = {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0};
|
||||
Aml *cpu_id = aml_arg(0);
|
||||
Aml *cpu_on = aml_local(0);
|
||||
Aml *madt = aml_local(1);
|
||||
Aml *cpus_map = aml_name(CPU_ON_BITMAP);
|
||||
Aml *zero = aml_int(0);
|
||||
Aml *one = aml_int(1);
|
||||
|
||||
/*
|
||||
* _MAT method - creates an madt apic buffer
|
||||
* cpu_id = Arg0 = Processor ID = Local APIC ID
|
||||
* cpu_on = Local0 = CPON flag for this cpu
|
||||
* madt = Local1 = Buffer (in madt apic form) to return
|
||||
*/
|
||||
method = aml_method(CPU_MAT_METHOD, 1, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_store(aml_derefof(aml_index(cpus_map, cpu_id)), cpu_on));
|
||||
aml_append(method,
|
||||
aml_store(aml_buffer(sizeof(madt_tmpl), madt_tmpl), madt));
|
||||
/* Update the processor id, lapic id, and enable/disable status */
|
||||
aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(2))));
|
||||
aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(3))));
|
||||
aml_append(method, aml_store(cpu_on, aml_index(madt, aml_int(4))));
|
||||
aml_append(method, aml_return(madt));
|
||||
aml_append(sb_scope, method);
|
||||
|
||||
/*
|
||||
* _STA method - return ON status of cpu
|
||||
* cpu_id = Arg0 = Processor ID = Local APIC ID
|
||||
* cpu_on = Local0 = CPON flag for this cpu
|
||||
*/
|
||||
method = aml_method(CPU_STATUS_METHOD, 1, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_store(aml_derefof(aml_index(cpus_map, cpu_id)), cpu_on));
|
||||
if_ctx = aml_if(cpu_on);
|
||||
{
|
||||
aml_append(if_ctx, aml_return(aml_int(0xF)));
|
||||
}
|
||||
aml_append(method, if_ctx);
|
||||
else_ctx = aml_else();
|
||||
{
|
||||
aml_append(else_ctx, aml_return(zero));
|
||||
}
|
||||
aml_append(method, else_ctx);
|
||||
aml_append(sb_scope, method);
|
||||
|
||||
method = aml_method(CPU_EJECT_METHOD, 2, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_sleep(200));
|
||||
aml_append(sb_scope, method);
|
||||
|
||||
method = aml_method(CPU_SCAN_METHOD, 0, AML_NOTSERIALIZED);
|
||||
{
|
||||
Aml *while_ctx, *if_ctx2, *else_ctx2;
|
||||
Aml *bus_check_evt = aml_int(1);
|
||||
Aml *remove_evt = aml_int(3);
|
||||
Aml *status_map = aml_local(5); /* Local5 = active cpu bitmap */
|
||||
Aml *byte = aml_local(2); /* Local2 = last read byte from bitmap */
|
||||
Aml *idx = aml_local(0); /* Processor ID / APIC ID iterator */
|
||||
Aml *is_cpu_on = aml_local(1); /* Local1 = CPON flag for cpu */
|
||||
Aml *status = aml_local(3); /* Local3 = active state for cpu */
|
||||
|
||||
aml_append(method, aml_store(aml_name(CPU_STATUS_MAP), status_map));
|
||||
aml_append(method, aml_store(zero, byte));
|
||||
aml_append(method, aml_store(zero, idx));
|
||||
|
||||
/* While (idx < SizeOf(CPON)) */
|
||||
while_ctx = aml_while(aml_lless(idx, aml_sizeof(cpus_map)));
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_derefof(aml_index(cpus_map, idx)), is_cpu_on));
|
||||
|
||||
if_ctx = aml_if(aml_and(idx, aml_int(0x07), NULL));
|
||||
{
|
||||
/* Shift down previously read bitmap byte */
|
||||
aml_append(if_ctx, aml_shiftright(byte, one, byte));
|
||||
}
|
||||
aml_append(while_ctx, if_ctx);
|
||||
|
||||
else_ctx = aml_else();
|
||||
{
|
||||
/* Read next byte from cpu bitmap */
|
||||
aml_append(else_ctx, aml_store(aml_derefof(aml_index(status_map,
|
||||
aml_shiftright(idx, aml_int(3), NULL))), byte));
|
||||
}
|
||||
aml_append(while_ctx, else_ctx);
|
||||
|
||||
aml_append(while_ctx, aml_store(aml_and(byte, one, NULL), status));
|
||||
if_ctx = aml_if(aml_lnot(aml_equal(is_cpu_on, status)));
|
||||
{
|
||||
/* State change - update CPON with new state */
|
||||
aml_append(if_ctx, aml_store(status, aml_index(cpus_map, idx)));
|
||||
if_ctx2 = aml_if(aml_equal(status, one));
|
||||
{
|
||||
aml_append(if_ctx2,
|
||||
aml_call2(AML_NOTIFY_METHOD, idx, bus_check_evt));
|
||||
}
|
||||
aml_append(if_ctx, if_ctx2);
|
||||
else_ctx2 = aml_else();
|
||||
{
|
||||
aml_append(else_ctx2,
|
||||
aml_call2(AML_NOTIFY_METHOD, idx, remove_evt));
|
||||
}
|
||||
}
|
||||
aml_append(if_ctx, else_ctx2);
|
||||
aml_append(while_ctx, if_ctx);
|
||||
|
||||
aml_append(while_ctx, aml_increment(idx)); /* go to next cpu */
|
||||
aml_append(method, while_ctx);
|
||||
}
|
||||
aml_append(sb_scope, method);
|
||||
|
||||
aml_append(ctx, sb_scope);
|
||||
}
|
@@ -250,7 +250,6 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
|
||||
/* do ACPI magic */
|
||||
acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq,
|
||||
|
262
hw/acpi/memory_hotplug_acpi_table.c
Normal file
262
hw/acpi/memory_hotplug_acpi_table.c
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Memory hotplug AML code of DSDT ACPI table
|
||||
*
|
||||
* Copyright (C) 2015 Red Hat Inc
|
||||
*
|
||||
* Author: Igor Mammedov <imammedo@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "hw/acpi/memory_hotplug.h"
|
||||
#include "include/hw/acpi/pc-hotplug.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
void build_memory_hotplug_aml(Aml *ctx, uint32_t nr_mem,
|
||||
uint16_t io_base, uint16_t io_len)
|
||||
{
|
||||
Aml *ifctx;
|
||||
Aml *method;
|
||||
Aml *pci_scope;
|
||||
Aml *mem_ctrl_dev;
|
||||
|
||||
/* scope for memory hotplug controller device node */
|
||||
pci_scope = aml_scope("_SB.PCI0");
|
||||
mem_ctrl_dev = aml_device(MEMORY_HOTPLUG_DEVICE);
|
||||
{
|
||||
Aml *one = aml_int(1);
|
||||
Aml *zero = aml_int(0);
|
||||
Aml *ret_val = aml_local(0);
|
||||
Aml *slot_arg0 = aml_arg(0);
|
||||
Aml *slots_nr = aml_name(MEMORY_SLOTS_NUMBER);
|
||||
Aml *ctrl_lock = aml_name(MEMORY_SLOT_LOCK);
|
||||
Aml *slot_selector = aml_name(MEMORY_SLOT_SLECTOR);
|
||||
|
||||
aml_append(mem_ctrl_dev, aml_name_decl("_HID", aml_string("PNP0A06")));
|
||||
aml_append(mem_ctrl_dev,
|
||||
aml_name_decl("_UID", aml_string("Memory hotplug resources")));
|
||||
|
||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||
ifctx = aml_if(aml_equal(slots_nr, zero));
|
||||
{
|
||||
aml_append(ifctx, aml_return(zero));
|
||||
}
|
||||
aml_append(method, ifctx);
|
||||
/* present, functioning, decoding, not shown in UI */
|
||||
aml_append(method, aml_return(aml_int(0xB)));
|
||||
aml_append(mem_ctrl_dev, method);
|
||||
|
||||
aml_append(mem_ctrl_dev, aml_mutex(MEMORY_SLOT_LOCK, 0));
|
||||
|
||||
method = aml_method(MEMORY_SLOT_SCAN_METHOD, 0, AML_NOTSERIALIZED);
|
||||
{
|
||||
Aml *else_ctx;
|
||||
Aml *while_ctx;
|
||||
Aml *idx = aml_local(0);
|
||||
Aml *eject_req = aml_int(3);
|
||||
Aml *dev_chk = aml_int(1);
|
||||
|
||||
ifctx = aml_if(aml_equal(slots_nr, zero));
|
||||
{
|
||||
aml_append(ifctx, aml_return(zero));
|
||||
}
|
||||
aml_append(method, ifctx);
|
||||
|
||||
aml_append(method, aml_store(zero, idx));
|
||||
aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
|
||||
/* build AML that:
|
||||
* loops over all slots and Notifies DIMMs with
|
||||
* Device Check or Eject Request notifications if
|
||||
* slot has corresponding status bit set and clears
|
||||
* slot status.
|
||||
*/
|
||||
while_ctx = aml_while(aml_lless(idx, slots_nr));
|
||||
{
|
||||
Aml *ins_evt = aml_name(MEMORY_SLOT_INSERT_EVENT);
|
||||
Aml *rm_evt = aml_name(MEMORY_SLOT_REMOVE_EVENT);
|
||||
|
||||
aml_append(while_ctx, aml_store(idx, slot_selector));
|
||||
ifctx = aml_if(aml_equal(ins_evt, one));
|
||||
{
|
||||
aml_append(ifctx,
|
||||
aml_call2(MEMORY_SLOT_NOTIFY_METHOD,
|
||||
idx, dev_chk));
|
||||
aml_append(ifctx, aml_store(one, ins_evt));
|
||||
}
|
||||
aml_append(while_ctx, ifctx);
|
||||
|
||||
else_ctx = aml_else();
|
||||
ifctx = aml_if(aml_equal(rm_evt, one));
|
||||
{
|
||||
aml_append(ifctx,
|
||||
aml_call2(MEMORY_SLOT_NOTIFY_METHOD,
|
||||
idx, eject_req));
|
||||
aml_append(ifctx, aml_store(one, rm_evt));
|
||||
}
|
||||
aml_append(else_ctx, ifctx);
|
||||
aml_append(while_ctx, else_ctx);
|
||||
|
||||
aml_append(while_ctx, aml_add(idx, one, idx));
|
||||
}
|
||||
aml_append(method, while_ctx);
|
||||
aml_append(method, aml_release(ctrl_lock));
|
||||
aml_append(method, aml_return(one));
|
||||
}
|
||||
aml_append(mem_ctrl_dev, method);
|
||||
|
||||
method = aml_method(MEMORY_SLOT_STATUS_METHOD, 1, AML_NOTSERIALIZED);
|
||||
{
|
||||
Aml *slot_enabled = aml_name(MEMORY_SLOT_ENABLED);
|
||||
|
||||
aml_append(method, aml_store(zero, ret_val));
|
||||
aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
|
||||
aml_append(method,
|
||||
aml_store(aml_to_integer(slot_arg0), slot_selector));
|
||||
|
||||
ifctx = aml_if(aml_equal(slot_enabled, one));
|
||||
{
|
||||
aml_append(ifctx, aml_store(aml_int(0xF), ret_val));
|
||||
}
|
||||
aml_append(method, ifctx);
|
||||
|
||||
aml_append(method, aml_release(ctrl_lock));
|
||||
aml_append(method, aml_return(ret_val));
|
||||
}
|
||||
aml_append(mem_ctrl_dev, method);
|
||||
|
||||
method = aml_method(MEMORY_SLOT_CRS_METHOD, 1, AML_SERIALIZED);
|
||||
{
|
||||
Aml *mr64 = aml_name("MR64");
|
||||
Aml *mr32 = aml_name("MR32");
|
||||
Aml *crs_tmpl = aml_resource_template();
|
||||
Aml *minl = aml_name("MINL");
|
||||
Aml *minh = aml_name("MINH");
|
||||
Aml *maxl = aml_name("MAXL");
|
||||
Aml *maxh = aml_name("MAXH");
|
||||
Aml *lenl = aml_name("LENL");
|
||||
Aml *lenh = aml_name("LENH");
|
||||
|
||||
aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
|
||||
aml_append(method, aml_store(aml_to_integer(slot_arg0),
|
||||
slot_selector));
|
||||
|
||||
aml_append(crs_tmpl,
|
||||
aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
|
||||
AML_CACHEABLE, AML_READ_WRITE,
|
||||
0, 0x0, 0xFFFFFFFFFFFFFFFEULL, 0,
|
||||
0xFFFFFFFFFFFFFFFFULL));
|
||||
aml_append(method, aml_name_decl("MR64", crs_tmpl));
|
||||
aml_append(method,
|
||||
aml_create_dword_field(mr64, aml_int(14), "MINL"));
|
||||
aml_append(method,
|
||||
aml_create_dword_field(mr64, aml_int(18), "MINH"));
|
||||
aml_append(method,
|
||||
aml_create_dword_field(mr64, aml_int(38), "LENL"));
|
||||
aml_append(method,
|
||||
aml_create_dword_field(mr64, aml_int(42), "LENH"));
|
||||
aml_append(method,
|
||||
aml_create_dword_field(mr64, aml_int(22), "MAXL"));
|
||||
aml_append(method,
|
||||
aml_create_dword_field(mr64, aml_int(26), "MAXH"));
|
||||
|
||||
aml_append(method,
|
||||
aml_store(aml_name(MEMORY_SLOT_ADDR_HIGH), minh));
|
||||
aml_append(method,
|
||||
aml_store(aml_name(MEMORY_SLOT_ADDR_LOW), minl));
|
||||
aml_append(method,
|
||||
aml_store(aml_name(MEMORY_SLOT_SIZE_HIGH), lenh));
|
||||
aml_append(method,
|
||||
aml_store(aml_name(MEMORY_SLOT_SIZE_LOW), lenl));
|
||||
|
||||
/* 64-bit math: MAX = MIN + LEN - 1 */
|
||||
aml_append(method, aml_add(minl, lenl, maxl));
|
||||
aml_append(method, aml_add(minh, lenh, maxh));
|
||||
ifctx = aml_if(aml_lless(maxl, minl));
|
||||
{
|
||||
aml_append(ifctx, aml_add(maxh, one, maxh));
|
||||
}
|
||||
aml_append(method, ifctx);
|
||||
ifctx = aml_if(aml_lless(maxl, one));
|
||||
{
|
||||
aml_append(ifctx, aml_subtract(maxh, one, maxh));
|
||||
}
|
||||
aml_append(method, ifctx);
|
||||
aml_append(method, aml_subtract(maxl, one, maxl));
|
||||
|
||||
/* return 32-bit _CRS if addr/size is in low mem */
|
||||
/* TODO: remove it since all hotplugged DIMMs are in high mem */
|
||||
ifctx = aml_if(aml_equal(maxh, zero));
|
||||
{
|
||||
crs_tmpl = aml_resource_template();
|
||||
aml_append(crs_tmpl,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0, 0x0, 0xFFFFFFFE, 0,
|
||||
0xFFFFFFFF));
|
||||
aml_append(ifctx, aml_name_decl("MR32", crs_tmpl));
|
||||
aml_append(ifctx,
|
||||
aml_create_dword_field(mr32, aml_int(10), "MIN"));
|
||||
aml_append(ifctx,
|
||||
aml_create_dword_field(mr32, aml_int(14), "MAX"));
|
||||
aml_append(ifctx,
|
||||
aml_create_dword_field(mr32, aml_int(22), "LEN"));
|
||||
aml_append(ifctx, aml_store(minl, aml_name("MIN")));
|
||||
aml_append(ifctx, aml_store(maxl, aml_name("MAX")));
|
||||
aml_append(ifctx, aml_store(lenl, aml_name("LEN")));
|
||||
|
||||
aml_append(ifctx, aml_release(ctrl_lock));
|
||||
aml_append(ifctx, aml_return(mr32));
|
||||
}
|
||||
aml_append(method, ifctx);
|
||||
|
||||
aml_append(method, aml_release(ctrl_lock));
|
||||
aml_append(method, aml_return(mr64));
|
||||
}
|
||||
aml_append(mem_ctrl_dev, method);
|
||||
|
||||
method = aml_method(MEMORY_SLOT_PROXIMITY_METHOD, 1,
|
||||
AML_NOTSERIALIZED);
|
||||
{
|
||||
Aml *proximity = aml_name(MEMORY_SLOT_PROXIMITY);
|
||||
|
||||
aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
|
||||
aml_append(method, aml_store(aml_to_integer(slot_arg0),
|
||||
slot_selector));
|
||||
aml_append(method, aml_store(proximity, ret_val));
|
||||
aml_append(method, aml_release(ctrl_lock));
|
||||
aml_append(method, aml_return(ret_val));
|
||||
}
|
||||
aml_append(mem_ctrl_dev, method);
|
||||
|
||||
method = aml_method(MEMORY_SLOT_OST_METHOD, 4, AML_NOTSERIALIZED);
|
||||
{
|
||||
Aml *ost_evt = aml_name(MEMORY_SLOT_OST_EVENT);
|
||||
Aml *ost_status = aml_name(MEMORY_SLOT_OST_STATUS);
|
||||
|
||||
aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
|
||||
aml_append(method, aml_store(aml_to_integer(slot_arg0),
|
||||
slot_selector));
|
||||
aml_append(method, aml_store(aml_arg(1), ost_evt));
|
||||
aml_append(method, aml_store(aml_arg(2), ost_status));
|
||||
aml_append(method, aml_release(ctrl_lock));
|
||||
}
|
||||
aml_append(mem_ctrl_dev, method);
|
||||
|
||||
method = aml_method(MEMORY_SLOT_EJECT_METHOD, 2, AML_NOTSERIALIZED);
|
||||
{
|
||||
Aml *eject = aml_name(MEMORY_SLOT_EJECT);
|
||||
|
||||
aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
|
||||
aml_append(method, aml_store(aml_to_integer(slot_arg0),
|
||||
slot_selector));
|
||||
aml_append(method, aml_store(one, eject));
|
||||
aml_append(method, aml_release(ctrl_lock));
|
||||
}
|
||||
aml_append(mem_ctrl_dev, method);
|
||||
}
|
||||
aml_append(pci_scope, mem_ctrl_dev);
|
||||
aml_append(ctx, pci_scope);
|
||||
}
|
@@ -353,16 +353,18 @@ static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets,
|
||||
GArray *table_data, GArray *linker)
|
||||
{
|
||||
GArray *structures = nvdimm_build_device_structure(device_list);
|
||||
void *header;
|
||||
unsigned int header;
|
||||
|
||||
acpi_add_table(table_offsets, table_data);
|
||||
|
||||
/* NFIT header. */
|
||||
header = acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
|
||||
header = table_data->len;
|
||||
acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
|
||||
/* NVDIMM device structures. */
|
||||
g_array_append_vals(table_data, structures->data, structures->len);
|
||||
|
||||
build_header(linker, table_data, header, "NFIT",
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + header), "NFIT",
|
||||
sizeof(NvdimmNfitHeader) + structures->len, 1, NULL);
|
||||
g_array_free(structures, true);
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "hw/loader.h"
|
||||
#include "hw/boards.h"
|
||||
#include "alpha_sys.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/timer/mc146818rtc.h"
|
||||
#include "hw/ide.h"
|
||||
@@ -104,14 +105,14 @@ static void clipper_init(MachineState *machine)
|
||||
palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
|
||||
bios_name ? bios_name : "palcode-clipper");
|
||||
if (palcode_filename == NULL) {
|
||||
hw_error("no palcode provided\n");
|
||||
error_report("no palcode provided");
|
||||
exit(1);
|
||||
}
|
||||
size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
|
||||
NULL, &palcode_entry, &palcode_low, &palcode_high,
|
||||
0, EM_ALPHA, 0);
|
||||
if (size < 0) {
|
||||
hw_error("could not load palcode '%s'\n", palcode_filename);
|
||||
error_report("could not load palcode '%s'", palcode_filename);
|
||||
exit(1);
|
||||
}
|
||||
g_free(palcode_filename);
|
||||
@@ -131,7 +132,7 @@ static void clipper_init(MachineState *machine)
|
||||
NULL, &kernel_entry, &kernel_low, &kernel_high,
|
||||
0, EM_ALPHA, 0);
|
||||
if (size < 0) {
|
||||
hw_error("could not load kernel '%s'\n", kernel_filename);
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -148,8 +149,8 @@ static void clipper_init(MachineState *machine)
|
||||
|
||||
initrd_size = get_image_size(initrd_filename);
|
||||
if (initrd_size < 0) {
|
||||
hw_error("could not load initial ram disk '%s'\n",
|
||||
initrd_filename);
|
||||
error_report("could not load initial ram disk '%s'",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@@ -920,7 +920,8 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
|
||||
{
|
||||
qemu_irq *isa_irqs;
|
||||
|
||||
*isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io);
|
||||
*isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io,
|
||||
&error_abort);
|
||||
isa_irqs = i8259_init(*isa_bus,
|
||||
qemu_allocate_irq(typhoon_set_isa_irq, s, 0));
|
||||
isa_bus_irqs(*isa_bus, isa_irqs);
|
||||
|
@@ -39,27 +39,26 @@ static void cubieboard_init(MachineState *machine)
|
||||
|
||||
object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't set phy address: %s", error_get_pretty(err));
|
||||
error_reportf_err(err, "Couldn't set phy address: ");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
object_property_set_int(OBJECT(&s->a10->timer), 32768, "clk0-freq", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't set clk0 frequency: %s", error_get_pretty(err));
|
||||
error_reportf_err(err, "Couldn't set clk0 frequency: ");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
object_property_set_int(OBJECT(&s->a10->timer), 24000000, "clk1-freq",
|
||||
&err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't set clk1 frequency: %s", error_get_pretty(err));
|
||||
error_reportf_err(err, "Couldn't set clk1 frequency: ");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't realize Allwinner A10: %s",
|
||||
error_get_pretty(err));
|
||||
error_reportf_err(err, "Couldn't realize Allwinner A10: ");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@@ -64,8 +64,7 @@ static void digic4_board_init(DigicBoard *board)
|
||||
s->digic = DIGIC(object_new(TYPE_DIGIC));
|
||||
object_property_set_bool(OBJECT(s->digic), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't realize DIGIC SoC: %s",
|
||||
error_get_pretty(err));
|
||||
error_reportf_err(err, "Couldn't realize DIGIC SoC: ");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@@ -150,27 +150,18 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
||||
|
||||
for (n = 0; n < EXYNOS4210_NCPUS; n++) {
|
||||
Object *cpuobj = object_new(object_class_get_name(cpu_oc));
|
||||
Error *err = NULL;
|
||||
|
||||
/* By default A9 CPUs have EL3 enabled. This board does not currently
|
||||
* support EL3 so the CPU EL3 property is disabled before realization.
|
||||
*/
|
||||
if (object_property_find(cpuobj, "has_el3", NULL)) {
|
||||
object_property_set_bool(cpuobj, false, "has_el3", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, false, "has_el3", &error_fatal);
|
||||
}
|
||||
|
||||
s->cpu[n] = ARM_CPU(cpuobj);
|
||||
object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR,
|
||||
"reset-cbar", &error_abort);
|
||||
object_property_set_bool(cpuobj, true, "realized", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||
}
|
||||
|
||||
/*** IRQs ***/
|
||||
|
@@ -279,7 +279,6 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
|
||||
ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
|
||||
Object *cpuobj;
|
||||
ARMCPU *cpu;
|
||||
Error *err = NULL;
|
||||
|
||||
cpuobj = object_new(object_class_get_name(oc));
|
||||
cpu = ARM_CPU(cpuobj);
|
||||
@@ -297,11 +296,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
|
||||
object_property_set_int(cpuobj, MPCORE_PERIPHBASE,
|
||||
"reset-cbar", &error_abort);
|
||||
}
|
||||
object_property_set_bool(cpuobj, true, "realized", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
|
||||
cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ);
|
||||
}
|
||||
@@ -320,11 +315,13 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
|
||||
sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||
if (sysboot_filename != NULL) {
|
||||
if (load_image_targphys(sysboot_filename, 0xfff88000, 0x8000) < 0) {
|
||||
hw_error("Unable to load %s\n", bios_name);
|
||||
error_report("Unable to load %s", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
g_free(sysboot_filename);
|
||||
} else {
|
||||
hw_error("Unable to find %s\n", bios_name);
|
||||
error_report("Unable to find %s", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -75,7 +75,7 @@ static void imx25_pdk_init(MachineState *machine)
|
||||
|
||||
object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@@ -533,7 +533,6 @@ static void integratorcp_init(MachineState *machine)
|
||||
qemu_irq pic[32];
|
||||
DeviceState *dev, *sic, *icp;
|
||||
int i;
|
||||
Error *err = NULL;
|
||||
|
||||
if (!cpu_model) {
|
||||
cpu_model = "arm926";
|
||||
@@ -552,18 +551,10 @@ static void integratorcp_init(MachineState *machine)
|
||||
* realization.
|
||||
*/
|
||||
if (object_property_find(cpuobj, "has_el3", NULL)) {
|
||||
object_property_set_bool(cpuobj, false, "has_el3", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, false, "has_el3", &error_fatal);
|
||||
}
|
||||
|
||||
object_property_set_bool(cpuobj, true, "realized", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||
|
||||
cpu = ARM_CPU(cpuobj);
|
||||
|
||||
|
@@ -74,7 +74,7 @@ static void kzm_init(MachineState *machine)
|
||||
|
||||
object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@@ -38,7 +38,7 @@ static void netduino2_init(MachineState *machine)
|
||||
qdev_prop_set_string(dev, "cpu-model", "cortex-m3");
|
||||
object_property_set_bool(OBJECT(dev), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@@ -172,8 +172,8 @@ static void n8x0_nand_setup(struct n800_s *s)
|
||||
qdev_prop_set_int32(s->nand, "shift", 1);
|
||||
dinfo = drive_get(IF_MTD, 0, 0);
|
||||
if (dinfo) {
|
||||
qdev_prop_set_drive_nofail(s->nand, "drive",
|
||||
blk_by_legacy_dinfo(dinfo));
|
||||
qdev_prop_set_drive(s->nand, "drive", blk_by_legacy_dinfo(dinfo),
|
||||
&error_fatal);
|
||||
}
|
||||
qdev_init_nofail(s->nand);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(s->nand), 0,
|
||||
|
@@ -99,33 +99,21 @@ static void realview_init(MachineState *machine,
|
||||
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
Object *cpuobj = object_new(object_class_get_name(cpu_oc));
|
||||
Error *err = NULL;
|
||||
|
||||
/* By default A9,A15 and ARM1176 CPUs have EL3 enabled. This board
|
||||
* does not currently support EL3 so the CPU EL3 property is disabled
|
||||
* before realization.
|
||||
*/
|
||||
if (object_property_find(cpuobj, "has_el3", NULL)) {
|
||||
object_property_set_bool(cpuobj, false, "has_el3", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, false, "has_el3", &error_fatal);
|
||||
}
|
||||
|
||||
if (is_pb && is_mpcore) {
|
||||
object_property_set_int(cpuobj, periphbase, "reset-cbar", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_int(cpuobj, periphbase, "reset-cbar",
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
object_property_set_bool(cpuobj, true, "realized", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||
|
||||
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpuobj), ARM_CPU_IRQ);
|
||||
}
|
||||
|
@@ -192,7 +192,6 @@ static void versatile_init(MachineState *machine, int board_id)
|
||||
int n;
|
||||
int done_smc = 0;
|
||||
DriveInfo *dinfo;
|
||||
Error *err = NULL;
|
||||
|
||||
if (!machine->cpu_model) {
|
||||
machine->cpu_model = "arm926";
|
||||
@@ -211,18 +210,10 @@ static void versatile_init(MachineState *machine, int board_id)
|
||||
* realization.
|
||||
*/
|
||||
if (object_property_find(cpuobj, "has_el3", NULL)) {
|
||||
object_property_set_bool(cpuobj, false, "has_el3", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, false, "has_el3", &error_fatal);
|
||||
}
|
||||
|
||||
object_property_set_bool(cpuobj, true, "realized", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||
|
||||
cpu = ARM_CPU(cpuobj);
|
||||
|
||||
|
@@ -211,7 +211,6 @@ static void init_cpus(const char *cpu_model, const char *privdev,
|
||||
/* Create the actual CPUs */
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
Object *cpuobj = object_new(object_class_get_name(cpu_oc));
|
||||
Error *err = NULL;
|
||||
|
||||
if (!secure) {
|
||||
object_property_set_bool(cpuobj, false, "has_el3", NULL);
|
||||
@@ -221,11 +220,7 @@ static void init_cpus(const char *cpu_model, const char *privdev,
|
||||
object_property_set_int(cpuobj, periphbase,
|
||||
"reset-cbar", &error_abort);
|
||||
}
|
||||
object_property_set_bool(cpuobj, true, "realized", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||
}
|
||||
|
||||
/* Create the private peripheral devices (including the GIC);
|
||||
|
@@ -94,23 +94,6 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
|
||||
uint32_t rtc_irq)
|
||||
{
|
||||
Aml *dev = aml_device("RTC0");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013")));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
|
||||
|
||||
Aml *crs = aml_resource_template();
|
||||
aml_append(crs, aml_memory32_fixed(rtc_memmap->base,
|
||||
rtc_memmap->size, AML_READ_WRITE));
|
||||
aml_append(crs,
|
||||
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, &rtc_irq, 1));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
|
||||
{
|
||||
Aml *dev, *crs;
|
||||
@@ -571,12 +554,15 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
||||
/* Reserve space for header */
|
||||
acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
|
||||
|
||||
/* When booting the VM with UEFI, UEFI takes ownership of the RTC hardware.
|
||||
* While UEFI can use libfdt to disable the RTC device node in the DTB that
|
||||
* it passes to the OS, it cannot modify AML. Therefore, we won't generate
|
||||
* the RTC ACPI device at all when using UEFI.
|
||||
*/
|
||||
scope = aml_scope("\\_SB");
|
||||
acpi_dsdt_add_cpus(scope, guest_info->smp_cpus);
|
||||
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
|
||||
(irqmap[VIRT_UART] + ARM_SPI_BASE));
|
||||
acpi_dsdt_add_rtc(scope, &memmap[VIRT_RTC],
|
||||
(irqmap[VIRT_RTC] + ARM_SPI_BASE));
|
||||
acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
|
||||
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
|
||||
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
|
||||
|
@@ -808,6 +808,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
|
||||
DeviceState *dev;
|
||||
char *nodename;
|
||||
int i;
|
||||
PCIHostState *pci;
|
||||
|
||||
dev = qdev_create(NULL, TYPE_GPEX_HOST);
|
||||
qdev_init_nofail(dev);
|
||||
@@ -847,6 +848,19 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
|
||||
}
|
||||
|
||||
pci = PCI_HOST_BRIDGE(dev);
|
||||
if (pci->bus) {
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
NICInfo *nd = &nd_table[i];
|
||||
|
||||
if (!nd->model) {
|
||||
nd->model = g_strdup("virtio");
|
||||
}
|
||||
|
||||
pci_nic_init_nofail(nd, pci->bus, nd->model, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
nodename = g_strdup_printf("/pcie@%" PRIx64, base);
|
||||
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||
qemu_fdt_setprop_string(vbi->fdt, nodename,
|
||||
@@ -1186,9 +1200,8 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
|
||||
} else if (!strcmp(value, "host")) {
|
||||
vms->gic_version = 0; /* Will probe later */
|
||||
} else {
|
||||
error_report("Invalid gic-version option value");
|
||||
error_printf("Allowed gic-version values are: 3, 2, host\n");
|
||||
exit(1);
|
||||
error_setg(errp, "Invalid gic-version value");
|
||||
error_append_hint(errp, "Valid values are 3, 2, host.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -156,7 +156,6 @@ static void zynq_init(MachineState *machine)
|
||||
DeviceState *dev;
|
||||
SysBusDevice *busdev;
|
||||
qemu_irq pic[64];
|
||||
Error *err = NULL;
|
||||
int n;
|
||||
|
||||
if (!cpu_model) {
|
||||
@@ -171,29 +170,14 @@ static void zynq_init(MachineState *machine)
|
||||
* realization.
|
||||
*/
|
||||
if (object_property_find(OBJECT(cpu), "has_el3", NULL)) {
|
||||
object_property_set_bool(OBJECT(cpu), false, "has_el3", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(OBJECT(cpu), false, "has_el3", &error_fatal);
|
||||
}
|
||||
|
||||
object_property_set_int(OBJECT(cpu), ZYNQ_BOARD_MIDR, "midr", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_int(OBJECT(cpu), ZYNQ_BOARD_MIDR, "midr",
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar",
|
||||
&error_fatal);
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &error_fatal);
|
||||
|
||||
/* max 2GB ram */
|
||||
if (ram_size > 0x80000000) {
|
||||
|
@@ -25,42 +25,44 @@ typedef struct XlnxEP108 {
|
||||
MemoryRegion ddr_ram;
|
||||
} XlnxEP108;
|
||||
|
||||
/* Max 2GB RAM */
|
||||
#define EP108_MAX_RAM_SIZE 0x80000000ull
|
||||
|
||||
static struct arm_boot_info xlnx_ep108_binfo;
|
||||
|
||||
static void xlnx_ep108_init(MachineState *machine)
|
||||
{
|
||||
XlnxEP108 *s = g_new0(XlnxEP108, 1);
|
||||
Error *err = NULL;
|
||||
uint64_t ram_size = machine->ram_size;
|
||||
|
||||
/* Create the memory region to pass to the SoC */
|
||||
if (ram_size > XLNX_ZYNQMP_MAX_RAM_SIZE) {
|
||||
error_report("ERROR: RAM size 0x%" PRIx64 " above max supported of "
|
||||
"0x%llx", ram_size,
|
||||
XLNX_ZYNQMP_MAX_RAM_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ram_size < 0x08000000) {
|
||||
qemu_log("WARNING: RAM size 0x%" PRIx64 " is small for EP108",
|
||||
ram_size);
|
||||
}
|
||||
|
||||
memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram",
|
||||
ram_size);
|
||||
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_XLNX_ZYNQMP);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||
&error_abort);
|
||||
|
||||
object_property_set_link(OBJECT(&s->soc), OBJECT(&s->ddr_ram),
|
||||
"ddr-ram", &error_abort);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (machine->ram_size > EP108_MAX_RAM_SIZE) {
|
||||
error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, "
|
||||
"reduced to %llx", machine->ram_size, EP108_MAX_RAM_SIZE);
|
||||
machine->ram_size = EP108_MAX_RAM_SIZE;
|
||||
}
|
||||
|
||||
if (machine->ram_size < 0x08000000) {
|
||||
qemu_log("WARNING: RAM size " RAM_ADDR_FMT " is small for EP108",
|
||||
machine->ram_size);
|
||||
}
|
||||
|
||||
memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram",
|
||||
machine->ram_size);
|
||||
memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram);
|
||||
|
||||
xlnx_ep108_binfo.ram_size = machine->ram_size;
|
||||
xlnx_ep108_binfo.ram_size = ram_size;
|
||||
xlnx_ep108_binfo.kernel_filename = machine->kernel_filename;
|
||||
xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
|
||||
xlnx_ep108_binfo.initrd_filename = machine->initrd_filename;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user