Compare commits
185 Commits
qemu-sparc
...
x86-next-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b77146e9a1 | ||
|
|
e37a5c7fa4 | ||
|
|
be7773268d | ||
|
|
b16a54da06 | ||
|
|
819fd4699c | ||
|
|
5df089564b | ||
|
|
12c06d6f96 | ||
|
|
f771c5440e | ||
|
|
b153f9019b | ||
|
|
cd2452b279 | ||
|
|
e181f8bbd0 | ||
|
|
2e5567c903 | ||
|
|
f1bd313264 | ||
|
|
70763fea4f | ||
|
|
4c70280592 | ||
|
|
2f92f37c9e | ||
|
|
c923cbedb2 | ||
|
|
11c82b584a | ||
|
|
2ca5c43091 | ||
|
|
722cd74964 | ||
|
|
0e39c4aa7e | ||
|
|
20f59d1200 | ||
|
|
f96d6651e4 | ||
|
|
dd0ee30cae | ||
|
|
ef9c5160a1 | ||
|
|
86b124bc76 | ||
|
|
b255734531 | ||
|
|
ab105cc138 | ||
|
|
e4ae62b802 | ||
|
|
076a0fc32a | ||
|
|
08022a916c | ||
|
|
0c3fb03f7e | ||
|
|
75a96f5e1c | ||
|
|
2ed61fb57b | ||
|
|
586634b9a8 | ||
|
|
dc16538a98 | ||
|
|
9076ddb3f6 | ||
|
|
a0032cc542 | ||
|
|
bab52d4bba | ||
|
|
86f0a186d6 | ||
|
|
c4487d76d5 | ||
|
|
2764040785 | ||
|
|
36f876cea4 | ||
|
|
0bbabbb414 | ||
|
|
8c5931de0a | ||
|
|
7a53fb907f | ||
|
|
e1eecd1d9d | ||
|
|
3b505bbae1 | ||
|
|
85fc716732 | ||
|
|
843361ed04 | ||
|
|
757282ada8 | ||
|
|
d64e5eabc4 | ||
|
|
8f2ba1f278 | ||
|
|
f9a697112e | ||
|
|
b39b61e410 | ||
|
|
a1be5921e3 | ||
|
|
6c25be6e30 | ||
|
|
068f161536 | ||
|
|
40ad087330 | ||
|
|
67b453ed73 | ||
|
|
248efb66fb | ||
|
|
50067bd16f | ||
|
|
4b5c65b8f0 | ||
|
|
21794244d4 | ||
|
|
39eaefcedb | ||
|
|
0bfed484a5 | ||
|
|
990dc39cfa | ||
|
|
fcca5dce20 | ||
|
|
cffad426f5 | ||
|
|
56ea7450aa | ||
|
|
39218a771a | ||
|
|
cd8b7aaa07 | ||
|
|
89b259eeaa | ||
|
|
4906da7e4d | ||
|
|
375f0b9266 | ||
|
|
ec2f54182c | ||
|
|
16e4bdb1bf | ||
|
|
63fd65a0a5 | ||
|
|
a595e4bcca | ||
|
|
a1a42af422 | ||
|
|
c22a034545 | ||
|
|
aa045c2d37 | ||
|
|
d41a55885d | ||
|
|
1bebea37b4 | ||
|
|
4bfb274165 | ||
|
|
4ff4504974 | ||
|
|
3d9136f972 | ||
|
|
71c87815f9 | ||
|
|
ab8bda76a0 | ||
|
|
3766ef579c | ||
|
|
927f11e131 | ||
|
|
b0292b851b | ||
|
|
e8eb863778 | ||
|
|
b76b4f6045 | ||
|
|
bcebf102cc | ||
|
|
37974a97d4 | ||
|
|
575ef8bff6 | ||
|
|
72215395b9 | ||
|
|
e4b5dad826 | ||
|
|
60900b7bd9 | ||
|
|
e1d74bc6c6 | ||
|
|
29ca9e450d | ||
|
|
cbf2b7c404 | ||
|
|
8ca5bfdc46 | ||
|
|
c2808abaf7 | ||
|
|
5361468974 | ||
|
|
0c2ada8136 | ||
|
|
db5794f1f1 | ||
|
|
a8475d7573 | ||
|
|
c7a9d81d70 | ||
|
|
c9a442e450 | ||
|
|
314e8d3928 | ||
|
|
0cf0e5980b | ||
|
|
2fd6163884 | ||
|
|
2b148f392b | ||
|
|
9fb4dfc570 | ||
|
|
1fafcd9368 | ||
|
|
1a0c2bfb03 | ||
|
|
8b220eb7c8 | ||
|
|
f87e08f9e2 | ||
|
|
9a069775a8 | ||
|
|
d9bbfea646 | ||
|
|
9fa673c3e3 | ||
|
|
4486e89c21 | ||
|
|
184b962346 | ||
|
|
1010cadf62 | ||
|
|
b89d92f3cf | ||
|
|
f6d81cdec8 | ||
|
|
24118af846 | ||
|
|
12c1c7d7ce | ||
|
|
7c9e274829 | ||
|
|
c060332c76 | ||
|
|
6af978ae8b | ||
|
|
1ffed98f24 | ||
|
|
bc61c8c626 | ||
|
|
08b824aa2d | ||
|
|
2231384e56 | ||
|
|
81ce6aa51a | ||
|
|
3c4e9baacf | ||
|
|
adab99be66 | ||
|
|
5600086976 | ||
|
|
c575fa678b | ||
|
|
b2dba411cf | ||
|
|
83d2e94cba | ||
|
|
0ab4537f08 | ||
|
|
3ef91576b9 | ||
|
|
854a4436dd | ||
|
|
4b9006a41e | ||
|
|
ce5eb6dc4d | ||
|
|
7a2e43cc96 | ||
|
|
2a8fcd119e | ||
|
|
25fa194b7b | ||
|
|
a7240d1e4a | ||
|
|
eb637edb12 | ||
|
|
e6b8552c65 | ||
|
|
bb72692cbd | ||
|
|
04331d0b56 | ||
|
|
88a07990fa | ||
|
|
5b4beba124 | ||
|
|
1e24429e40 | ||
|
|
1c77c410b6 | ||
|
|
4b50b8d9f2 | ||
|
|
5033606780 | ||
|
|
a2480ffa88 | ||
|
|
47ae93cdfe | ||
|
|
65c5b75c38 | ||
|
|
55c2a12cbc | ||
|
|
9438fe7d7c | ||
|
|
f798f1e29b | ||
|
|
0c3e702aca | ||
|
|
ea10325917 | ||
|
|
dc5bd18fa5 | ||
|
|
f71a8eaffb | ||
|
|
4dc62b1532 | ||
|
|
fcbed221ff | ||
|
|
9d9dcd9602 | ||
|
|
cc7d320f5d | ||
|
|
49d302fe3d | ||
|
|
ec427498da | ||
|
|
1939ccdaa6 | ||
|
|
8005fdd8fa | ||
|
|
a17536c594 | ||
|
|
938c8b79e5 | ||
|
|
315409c711 | ||
|
|
7c28768fef |
@@ -23,8 +23,6 @@ env:
|
||||
TARGET_LIST=mips-softmmu,mipsel-linux-user
|
||||
- IMAGE=debian-mips64el-cross
|
||||
TARGET_LIST=mips64el-softmmu,mips64el-linux-user
|
||||
- IMAGE=debian-powerpc-cross
|
||||
TARGET_LIST=ppc-softmmu,ppcemb-softmmu,ppc-linux-user
|
||||
- IMAGE=debian-ppc64el-cross
|
||||
TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
|
||||
build:
|
||||
|
||||
19
MAINTAINERS
19
MAINTAINERS
@@ -233,6 +233,17 @@ F: hw/ppc/
|
||||
F: include/hw/ppc/
|
||||
F: disas/ppc.c
|
||||
|
||||
RISC-V
|
||||
M: Michael Clark <mjc@sifive.com>
|
||||
M: Palmer Dabbelt <palmer@sifive.com>
|
||||
M: Sagar Karandikar <sagark@eecs.berkeley.edu>
|
||||
M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
|
||||
S: Maintained
|
||||
F: target/riscv/
|
||||
F: hw/riscv/
|
||||
F: include/hw/riscv/
|
||||
F: disas/riscv.c
|
||||
|
||||
S390
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
@@ -1089,6 +1100,14 @@ M: Peter Crosthwaite <crosthwaite.peter@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/ssi/xilinx_*
|
||||
|
||||
SD (Secure Card)
|
||||
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
S: Odd Fixes
|
||||
F: include/hw/sd/sd*
|
||||
F: hw/sd/core.c
|
||||
F: hw/sd/sd*
|
||||
F: tests/sd*
|
||||
|
||||
USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
|
||||
2
Makefile
2
Makefile
@@ -851,7 +851,7 @@ ifneq ($(BLOBS),)
|
||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
||||
done
|
||||
endif
|
||||
ifeq ($(CONFIG_GTK),y)
|
||||
ifeq ($(CONFIG_GTK),m)
|
||||
$(MAKE) -C po $@
|
||||
endif
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
|
||||
|
||||
@@ -233,6 +233,7 @@ trace-events-subdirs += hw/alpha
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/xen
|
||||
trace-events-subdirs += hw/ide
|
||||
trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += ui
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += net
|
||||
|
||||
2
README
2
README
@@ -73,7 +73,7 @@ The QEMU website is also maintained under source control.
|
||||
git clone git://git.qemu.org/qemu-web.git
|
||||
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
|
||||
|
||||
A 'git-profile' utility was created to make above process less
|
||||
A 'git-publish' utility was created to make above process less
|
||||
cumbersome, and is highly recommended for making regular contributions,
|
||||
or even just for sending consecutive patch series revisions. It also
|
||||
requires a working 'git send-email' setup, and by default doesn't
|
||||
|
||||
@@ -71,6 +71,8 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_OPENRISC
|
||||
#elif defined(TARGET_PPC)
|
||||
#define QEMU_ARCH QEMU_ARCH_PPC
|
||||
#elif defined(TARGET_RISCV)
|
||||
#define QEMU_ARCH QEMU_ARCH_RISCV
|
||||
#elif defined(TARGET_S390X)
|
||||
#define QEMU_ARCH QEMU_ARCH_S390X
|
||||
#elif defined(TARGET_SH4)
|
||||
|
||||
138
block.c
138
block.c
@@ -34,6 +34,8 @@
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-output-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/notify.h"
|
||||
@@ -368,7 +370,7 @@ BlockDriver *bdrv_find_format(const char *format_name)
|
||||
return bdrv_do_find_format(format_name);
|
||||
}
|
||||
|
||||
static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
|
||||
int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
|
||||
{
|
||||
static const char *whitelist_rw[] = {
|
||||
CONFIG_BDRV_RW_WHITELIST
|
||||
@@ -2406,6 +2408,51 @@ BdrvChild *bdrv_open_child(const char *filename,
|
||||
return c;
|
||||
}
|
||||
|
||||
/* TODO Future callers may need to specify parent/child_role in order for
|
||||
* option inheritance to work. Existing callers use it for the root node. */
|
||||
BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
QObject *obj = NULL;
|
||||
QDict *qdict = NULL;
|
||||
const char *reference = NULL;
|
||||
Visitor *v = NULL;
|
||||
|
||||
if (ref->type == QTYPE_QSTRING) {
|
||||
reference = ref->u.reference;
|
||||
} else {
|
||||
BlockdevOptions *options = &ref->u.definition;
|
||||
assert(ref->type == QTYPE_QDICT);
|
||||
|
||||
v = qobject_output_visitor_new(&obj);
|
||||
visit_type_BlockdevOptions(v, NULL, &options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
visit_complete(v, &obj);
|
||||
|
||||
qdict = qobject_to_qdict(obj);
|
||||
qdict_flatten(qdict);
|
||||
|
||||
/* bdrv_open_inherit() defaults to the values in bdrv_flags (for
|
||||
* compatibility with other callers) rather than what we want as the
|
||||
* real defaults. Apply the defaults here instead. */
|
||||
qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||
qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off");
|
||||
}
|
||||
|
||||
bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
|
||||
obj = NULL;
|
||||
|
||||
fail:
|
||||
qobject_decref(obj);
|
||||
visit_free(v);
|
||||
return bs;
|
||||
}
|
||||
|
||||
static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
|
||||
int flags,
|
||||
QDict *snapshot_options,
|
||||
@@ -3455,17 +3502,54 @@ static void bdrv_delete(BlockDriverState *bs)
|
||||
* free of errors) or -errno when an internal error occurred. The results of the
|
||||
* check are stored in res.
|
||||
*/
|
||||
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix)
|
||||
static int coroutine_fn bdrv_co_check(BlockDriverState *bs,
|
||||
BdrvCheckResult *res, BdrvCheckMode fix)
|
||||
{
|
||||
if (bs->drv == NULL) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
if (bs->drv->bdrv_check == NULL) {
|
||||
if (bs->drv->bdrv_co_check == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
memset(res, 0, sizeof(*res));
|
||||
return bs->drv->bdrv_check(bs, res, fix);
|
||||
return bs->drv->bdrv_co_check(bs, res, fix);
|
||||
}
|
||||
|
||||
typedef struct CheckCo {
|
||||
BlockDriverState *bs;
|
||||
BdrvCheckResult *res;
|
||||
BdrvCheckMode fix;
|
||||
int ret;
|
||||
} CheckCo;
|
||||
|
||||
static void bdrv_check_co_entry(void *opaque)
|
||||
{
|
||||
CheckCo *cco = opaque;
|
||||
cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
|
||||
}
|
||||
|
||||
int bdrv_check(BlockDriverState *bs,
|
||||
BdrvCheckResult *res, BdrvCheckMode fix)
|
||||
{
|
||||
Coroutine *co;
|
||||
CheckCo cco = {
|
||||
.bs = bs,
|
||||
.res = res,
|
||||
.ret = -EINPROGRESS,
|
||||
.fix = fix,
|
||||
};
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
/* Fast-path if already in coroutine context */
|
||||
bdrv_check_co_entry(&cco);
|
||||
} else {
|
||||
co = qemu_coroutine_create(bdrv_check_co_entry, &cco);
|
||||
qemu_coroutine_enter(co);
|
||||
BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS);
|
||||
}
|
||||
|
||||
return cco.ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3635,6 +3719,11 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
|
||||
error_setg(errp, "No medium inserted");
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
if (offset < 0) {
|
||||
error_setg(errp, "Image size cannot be negative");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!drv->bdrv_truncate) {
|
||||
if (bs->file && drv->is_filter) {
|
||||
return bdrv_truncate(bs->file, offset, prealloc, errp);
|
||||
@@ -4209,7 +4298,8 @@ void bdrv_init_with_whitelist(void)
|
||||
bdrv_init();
|
||||
}
|
||||
|
||||
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvChild *child, *parent;
|
||||
uint64_t perm, shared_perm;
|
||||
@@ -4225,7 +4315,7 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_invalidate_cache(child->bs, &local_err);
|
||||
bdrv_co_invalidate_cache(child->bs, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -4255,8 +4345,8 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
bdrv_set_perm(bs, perm, shared_perm);
|
||||
|
||||
if (bs->drv->bdrv_invalidate_cache) {
|
||||
bs->drv->bdrv_invalidate_cache(bs, &local_err);
|
||||
if (bs->drv->bdrv_co_invalidate_cache) {
|
||||
bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
|
||||
if (local_err) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_propagate(errp, local_err);
|
||||
@@ -4282,6 +4372,38 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct InvalidateCacheCo {
|
||||
BlockDriverState *bs;
|
||||
Error **errp;
|
||||
bool done;
|
||||
} InvalidateCacheCo;
|
||||
|
||||
static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
|
||||
{
|
||||
InvalidateCacheCo *ico = opaque;
|
||||
bdrv_co_invalidate_cache(ico->bs, ico->errp);
|
||||
ico->done = true;
|
||||
}
|
||||
|
||||
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
Coroutine *co;
|
||||
InvalidateCacheCo ico = {
|
||||
.bs = bs,
|
||||
.done = false,
|
||||
.errp = errp
|
||||
};
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
/* Fast-path if already in coroutine context */
|
||||
bdrv_invalidate_cache_co_entry(&ico);
|
||||
} else {
|
||||
co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico);
|
||||
qemu_coroutine_enter(co);
|
||||
BDRV_POLL_WHILE(bs, !ico.done);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_invalidate_cache_all(Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
||||
@@ -9,7 +9,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o
|
||||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += file-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
block-obj-y += null.o mirror.o commit.o io.o
|
||||
block-obj-y += null.o mirror.o commit.o io.o create.o
|
||||
block-obj-y += throttle-groups.o
|
||||
block-obj-$(CONFIG_LINUX) += nvme.o
|
||||
|
||||
|
||||
@@ -1150,7 +1150,7 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
|
||||
typedef struct BlkRwCo {
|
||||
BlockBackend *blk;
|
||||
int64_t offset;
|
||||
QEMUIOVector *qiov;
|
||||
void *iobuf;
|
||||
int ret;
|
||||
BdrvRequestFlags flags;
|
||||
} BlkRwCo;
|
||||
@@ -1158,17 +1158,19 @@ typedef struct BlkRwCo {
|
||||
static void blk_read_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, rwco->qiov->size,
|
||||
rwco->qiov, rwco->flags);
|
||||
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, rwco->flags);
|
||||
}
|
||||
|
||||
static void blk_write_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, rwco->qiov->size,
|
||||
rwco->qiov, rwco->flags);
|
||||
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, rwco->flags);
|
||||
}
|
||||
|
||||
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||
@@ -1188,7 +1190,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||
rwco = (BlkRwCo) {
|
||||
.blk = blk,
|
||||
.offset = offset,
|
||||
.qiov = &qiov,
|
||||
.iobuf = &qiov,
|
||||
.flags = flags,
|
||||
.ret = NOT_DONE,
|
||||
};
|
||||
@@ -1296,7 +1298,7 @@ static void blk_aio_complete_bh(void *opaque)
|
||||
}
|
||||
|
||||
static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
|
||||
QEMUIOVector *qiov, CoroutineEntry co_entry,
|
||||
void *iobuf, CoroutineEntry co_entry,
|
||||
BdrvRequestFlags flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
@@ -1308,7 +1310,7 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
|
||||
acb->rwco = (BlkRwCo) {
|
||||
.blk = blk,
|
||||
.offset = offset,
|
||||
.qiov = qiov,
|
||||
.iobuf = iobuf,
|
||||
.flags = flags,
|
||||
.ret = NOT_DONE,
|
||||
};
|
||||
@@ -1331,10 +1333,11 @@ static void blk_aio_read_entry(void *opaque)
|
||||
{
|
||||
BlkAioEmAIOCB *acb = opaque;
|
||||
BlkRwCo *rwco = &acb->rwco;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
assert(rwco->qiov->size == acb->bytes);
|
||||
assert(qiov->size == acb->bytes);
|
||||
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, acb->bytes,
|
||||
rwco->qiov, rwco->flags);
|
||||
qiov, rwco->flags);
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
||||
@@ -1342,10 +1345,11 @@ static void blk_aio_write_entry(void *opaque)
|
||||
{
|
||||
BlkAioEmAIOCB *acb = opaque;
|
||||
BlkRwCo *rwco = &acb->rwco;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
assert(!rwco->qiov || rwco->qiov->size == acb->bytes);
|
||||
assert(!qiov || qiov->size == acb->bytes);
|
||||
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, acb->bytes,
|
||||
rwco->qiov, rwco->flags);
|
||||
qiov, rwco->flags);
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
||||
@@ -1474,8 +1478,10 @@ int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
static void blk_ioctl_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
|
||||
rwco->qiov->iov[0].iov_base);
|
||||
qiov->iov[0].iov_base);
|
||||
}
|
||||
|
||||
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
@@ -1488,24 +1494,15 @@ static void blk_aio_ioctl_entry(void *opaque)
|
||||
BlkAioEmAIOCB *acb = opaque;
|
||||
BlkRwCo *rwco = &acb->rwco;
|
||||
|
||||
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
|
||||
rwco->qiov->iov[0].iov_base);
|
||||
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, rwco->iobuf);
|
||||
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
QEMUIOVector qiov;
|
||||
struct iovec iov;
|
||||
|
||||
iov = (struct iovec) {
|
||||
.iov_base = buf,
|
||||
.iov_len = 0,
|
||||
};
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
return blk_aio_prwv(blk, req, 0, &qiov, blk_aio_ioctl_entry, 0, cb, opaque);
|
||||
return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque);
|
||||
}
|
||||
|
||||
int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
@@ -1949,7 +1946,9 @@ int blk_truncate(BlockBackend *blk, int64_t offset, PreallocMode prealloc,
|
||||
static void blk_pdiscard_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, rwco->qiov->size);
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
|
||||
}
|
||||
|
||||
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
|
||||
76
block/create.c
Normal file
76
block/create.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Block layer code related to image creation
|
||||
*
|
||||
* Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qapi-commands-block-core.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
typedef struct BlockdevCreateCo {
|
||||
BlockDriver *drv;
|
||||
BlockdevCreateOptions *opts;
|
||||
int ret;
|
||||
Error **errp;
|
||||
} BlockdevCreateCo;
|
||||
|
||||
static void coroutine_fn bdrv_co_create_co_entry(void *opaque)
|
||||
{
|
||||
BlockdevCreateCo *cco = opaque;
|
||||
cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
|
||||
}
|
||||
|
||||
void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
const char *fmt = BlockdevDriver_str(options->driver);
|
||||
BlockDriver *drv = bdrv_find_format(fmt);
|
||||
Coroutine *co;
|
||||
BlockdevCreateCo cco;
|
||||
|
||||
/* If the driver is in the schema, we know that it exists. But it may not
|
||||
* be whitelisted. */
|
||||
assert(drv);
|
||||
if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) {
|
||||
error_setg(errp, "Driver is not whitelisted");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Call callback if it exists */
|
||||
if (!drv->bdrv_co_create) {
|
||||
error_setg(errp, "Driver does not support blockdev-create");
|
||||
return;
|
||||
}
|
||||
|
||||
cco = (BlockdevCreateCo) {
|
||||
.drv = drv,
|
||||
.opts = options,
|
||||
.ret = -EINPROGRESS,
|
||||
.errp = errp,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco);
|
||||
qemu_coroutine_enter(co);
|
||||
while (cco.ret == -EINPROGRESS) {
|
||||
aio_poll(qemu_get_aio_context(), true);
|
||||
}
|
||||
}
|
||||
@@ -384,6 +384,12 @@ static void block_crypto_close(BlockDriverState *bs)
|
||||
qcrypto_block_free(crypto->block);
|
||||
}
|
||||
|
||||
static int block_crypto_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
/* nothing needs checking */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1 MB bounce buffer gives good performance / memory tradeoff
|
||||
@@ -621,6 +627,7 @@ BlockDriver bdrv_crypto_luks = {
|
||||
.bdrv_truncate = block_crypto_truncate,
|
||||
.create_opts = &block_crypto_create_opts_luks,
|
||||
|
||||
.bdrv_reopen_prepare = block_crypto_reopen_prepare,
|
||||
.bdrv_refresh_limits = block_crypto_refresh_limits,
|
||||
.bdrv_co_preadv = block_crypto_co_preadv,
|
||||
.bdrv_co_pwritev = block_crypto_co_pwritev,
|
||||
|
||||
@@ -1686,11 +1686,15 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
|
||||
* file systems that do not support fallocate(), trying to check if a
|
||||
* block is allocated before allocating it, so don't do that here.
|
||||
*/
|
||||
result = -posix_fallocate(fd, current_length, offset - current_length);
|
||||
if (result != 0) {
|
||||
/* posix_fallocate() doesn't set errno. */
|
||||
error_setg_errno(errp, -result,
|
||||
"Could not preallocate new data");
|
||||
if (offset != current_length) {
|
||||
result = -posix_fallocate(fd, current_length, offset - current_length);
|
||||
if (result != 0) {
|
||||
/* posix_fallocate() doesn't set errno. */
|
||||
error_setg_errno(errp, -result,
|
||||
"Could not preallocate new data");
|
||||
}
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
goto out;
|
||||
#endif
|
||||
@@ -1982,34 +1986,25 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
|
||||
return (int64_t)st.st_blocks * 512;
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsFile *file_opts;
|
||||
int fd;
|
||||
int result = 0;
|
||||
int64_t total_size = 0;
|
||||
bool nocow = false;
|
||||
PreallocMode prealloc;
|
||||
char *buf = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
strstart(filename, "file:", &filename);
|
||||
/* Validate options and set default values */
|
||||
assert(options->driver == BLOCKDEV_DRIVER_FILE);
|
||||
file_opts = &options->u.file;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
|
||||
PREALLOC_MODE_OFF, &local_err);
|
||||
g_free(buf);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
if (!file_opts->has_nocow) {
|
||||
file_opts->nocow = false;
|
||||
}
|
||||
if (!file_opts->has_preallocation) {
|
||||
file_opts->preallocation = PREALLOC_MODE_OFF;
|
||||
}
|
||||
|
||||
fd = qemu_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
|
||||
/* Create file */
|
||||
fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
result = -errno;
|
||||
@@ -2017,7 +2012,7 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nocow) {
|
||||
if (file_opts->nocow) {
|
||||
#ifdef __linux__
|
||||
/* Set NOCOW flag to solve performance issue on fs like btrfs.
|
||||
* This is an optimisation. The FS_IOC_SETFLAGS ioctl return value
|
||||
@@ -2032,7 +2027,8 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
#endif
|
||||
}
|
||||
|
||||
result = raw_regular_truncate(fd, total_size, prealloc, errp);
|
||||
result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation,
|
||||
errp);
|
||||
if (result < 0) {
|
||||
goto out_close;
|
||||
}
|
||||
@@ -2046,6 +2042,46 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions options;
|
||||
int64_t total_size = 0;
|
||||
bool nocow = false;
|
||||
PreallocMode prealloc;
|
||||
char *buf = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Skip file: protocol prefix */
|
||||
strstart(filename, "file:", &filename);
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
|
||||
PREALLOC_MODE_OFF, &local_err);
|
||||
g_free(buf);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
options = (BlockdevCreateOptions) {
|
||||
.driver = BLOCKDEV_DRIVER_FILE,
|
||||
.u.file = {
|
||||
.filename = (char *) filename,
|
||||
.size = total_size,
|
||||
.has_preallocation = true,
|
||||
.preallocation = prealloc,
|
||||
.has_nocow = true,
|
||||
.nocow = nocow,
|
||||
},
|
||||
};
|
||||
return raw_co_create(&options, errp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find allocation range in @bs around offset @start.
|
||||
* May change underlying file descriptor's file offset.
|
||||
@@ -2277,6 +2313,7 @@ BlockDriver bdrv_file = {
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_co_create = raw_co_create,
|
||||
.bdrv_co_create_opts = raw_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_block_status = raw_co_block_status,
|
||||
|
||||
@@ -553,10 +553,40 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsFile *file_opts;
|
||||
int fd;
|
||||
|
||||
assert(options->driver == BLOCKDEV_DRIVER_FILE);
|
||||
file_opts = &options->u.file;
|
||||
|
||||
if (file_opts->has_preallocation) {
|
||||
error_setg(errp, "Preallocation is not supported on Windows");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (file_opts->has_nocow) {
|
||||
error_setg(errp, "nocow is not supported on Windows");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = qemu_open(file_opts->filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
error_setg_errno(errp, errno, "Could not create file");
|
||||
return -EIO;
|
||||
}
|
||||
set_sparse(fd);
|
||||
ftruncate(fd, file_opts->size);
|
||||
qemu_close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
int fd;
|
||||
BlockdevCreateOptions options;
|
||||
int64_t total_size = 0;
|
||||
|
||||
strstart(filename, "file:", &filename);
|
||||
@@ -565,19 +595,18 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (fd < 0) {
|
||||
error_setg_errno(errp, errno, "Could not create file");
|
||||
return -EIO;
|
||||
}
|
||||
set_sparse(fd);
|
||||
ftruncate(fd, total_size);
|
||||
qemu_close(fd);
|
||||
return 0;
|
||||
options = (BlockdevCreateOptions) {
|
||||
.driver = BLOCKDEV_DRIVER_FILE,
|
||||
.u.file = {
|
||||
.filename = (char *) filename,
|
||||
.size = total_size,
|
||||
.has_preallocation = false,
|
||||
.has_nocow = false,
|
||||
},
|
||||
};
|
||||
return raw_co_create(&options, errp);
|
||||
}
|
||||
|
||||
|
||||
static QemuOptsList raw_create_opts = {
|
||||
.name = "raw-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
|
||||
|
||||
135
block/gluster.c
135
block/gluster.c
@@ -655,9 +655,11 @@ out:
|
||||
return -errno;
|
||||
}
|
||||
|
||||
static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
|
||||
const char *filename,
|
||||
QDict *options, Error **errp)
|
||||
/* Converts options given in @filename and the @options QDict into the QAPI
|
||||
* object @gconf. */
|
||||
static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
|
||||
const char *filename,
|
||||
QDict *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
if (filename) {
|
||||
@@ -668,8 +670,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
|
||||
"[host[:port]]volume/path[?socket=...]"
|
||||
"[,file.debug=N]"
|
||||
"[,file.logfile=/path/filename.log]\n");
|
||||
errno = -ret;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = qemu_gluster_parse_json(gconf, options, errp);
|
||||
@@ -685,10 +686,23 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
|
||||
"file.server.1.transport=unix,"
|
||||
"file.server.1.socket=/var/run/glusterd.socket ..."
|
||||
"\n");
|
||||
errno = -ret;
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
|
||||
const char *filename,
|
||||
QDict *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = qemu_gluster_parse(gconf, filename, options, errp);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return qemu_gluster_glfs_init(gconf, errp);
|
||||
@@ -1021,20 +1035,72 @@ static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qemu_gluster_co_create(BlockdevCreateOptions *options,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsGluster *opts = &options->u.gluster;
|
||||
struct glfs *glfs;
|
||||
struct glfs_fd *fd = NULL;
|
||||
int ret = 0;
|
||||
|
||||
assert(options->driver == BLOCKDEV_DRIVER_GLUSTER);
|
||||
|
||||
glfs = qemu_gluster_glfs_init(opts->location, errp);
|
||||
if (!glfs) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = glfs_creat(glfs, opts->location->path,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
|
||||
if (!fd) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qemu_gluster_do_truncate(fd, opts->size, opts->preallocation, errp);
|
||||
|
||||
out:
|
||||
if (fd) {
|
||||
if (glfs_close(fd) != 0 && ret == 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
}
|
||||
glfs_clear_preopened(glfs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *options;
|
||||
BlockdevCreateOptionsGluster *gopts;
|
||||
BlockdevOptionsGluster *gconf;
|
||||
struct glfs *glfs;
|
||||
struct glfs_fd *fd = NULL;
|
||||
int ret = 0;
|
||||
PreallocMode prealloc;
|
||||
int64_t total_size = 0;
|
||||
char *tmp = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
options = g_new0(BlockdevCreateOptions, 1);
|
||||
options->driver = BLOCKDEV_DRIVER_GLUSTER;
|
||||
gopts = &options->u.gluster;
|
||||
|
||||
gconf = g_new0(BlockdevOptionsGluster, 1);
|
||||
gopts->location = gconf;
|
||||
|
||||
gopts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
gopts->preallocation = qapi_enum_parse(&PreallocMode_lookup, tmp,
|
||||
PREALLOC_MODE_OFF, &local_err);
|
||||
g_free(tmp);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
|
||||
GLUSTER_DEBUG_DEFAULT);
|
||||
if (gconf->debug < 0) {
|
||||
@@ -1050,42 +1116,19 @@ static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
|
||||
}
|
||||
gconf->has_logfile = true;
|
||||
|
||||
glfs = qemu_gluster_init(gconf, filename, NULL, errp);
|
||||
if (!glfs) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
ret = qemu_gluster_parse(gconf, filename, NULL, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(&PreallocMode_lookup, tmp, PREALLOC_MODE_OFF,
|
||||
&local_err);
|
||||
g_free(tmp);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
ret = qemu_gluster_co_create(options, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fd = glfs_creat(glfs, gconf->path,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
|
||||
if (!fd) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp);
|
||||
|
||||
out:
|
||||
if (fd) {
|
||||
if (glfs_close(fd) != 0 && ret == 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
}
|
||||
qapi_free_BlockdevOptionsGluster(gconf);
|
||||
glfs_clear_preopened(glfs);
|
||||
ret = 0;
|
||||
fail:
|
||||
qapi_free_BlockdevCreateOptions(options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1436,6 +1479,7 @@ static BlockDriver bdrv_gluster = {
|
||||
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_co_create = qemu_gluster_co_create,
|
||||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
@@ -1464,6 +1508,7 @@ static BlockDriver bdrv_gluster_tcp = {
|
||||
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_co_create = qemu_gluster_co_create,
|
||||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
@@ -1492,6 +1537,7 @@ static BlockDriver bdrv_gluster_unix = {
|
||||
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_co_create = qemu_gluster_co_create,
|
||||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
@@ -1526,6 +1572,7 @@ static BlockDriver bdrv_gluster_rdma = {
|
||||
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||
.bdrv_close = qemu_gluster_close,
|
||||
.bdrv_co_create = qemu_gluster_co_create,
|
||||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
|
||||
|
||||
@@ -2177,8 +2177,8 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iscsi_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
iscsi_allocmap_invalidate(iscsilun);
|
||||
@@ -2209,7 +2209,7 @@ static BlockDriver bdrv_iscsi = {
|
||||
.create_opts = &iscsi_create_opts,
|
||||
.bdrv_reopen_prepare = iscsi_reopen_prepare,
|
||||
.bdrv_reopen_commit = iscsi_reopen_commit,
|
||||
.bdrv_invalidate_cache = iscsi_invalidate_cache,
|
||||
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
|
||||
|
||||
.bdrv_getlength = iscsi_getlength,
|
||||
.bdrv_get_info = iscsi_get_info,
|
||||
|
||||
244
block/nfs.c
244
block/nfs.c
@@ -367,49 +367,6 @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
|
||||
return task.ret;
|
||||
}
|
||||
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "nfs",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "path",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Path of the image on the host",
|
||||
},
|
||||
{
|
||||
.name = "user",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "UID value to use when talking to the server",
|
||||
},
|
||||
{
|
||||
.name = "group",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "GID value to use when talking to the server",
|
||||
},
|
||||
{
|
||||
.name = "tcp-syn-count",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "Number of SYNs to send during the session establish",
|
||||
},
|
||||
{
|
||||
.name = "readahead-size",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "Set the readahead size in bytes",
|
||||
},
|
||||
{
|
||||
.name = "page-cache-size",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "Set the pagecache size in bytes",
|
||||
},
|
||||
{
|
||||
.name = "debug",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "Set the NFS debug level (max 2)",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static void nfs_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
@@ -452,71 +409,16 @@ static void nfs_file_close(BlockDriverState *bs)
|
||||
nfs_client_close(client);
|
||||
}
|
||||
|
||||
static NFSServer *nfs_config(QDict *options, Error **errp)
|
||||
{
|
||||
NFSServer *server = NULL;
|
||||
QDict *addr = NULL;
|
||||
QObject *crumpled_addr = NULL;
|
||||
Visitor *iv = NULL;
|
||||
Error *local_error = NULL;
|
||||
|
||||
qdict_extract_subqdict(options, &addr, "server.");
|
||||
if (!qdict_size(addr)) {
|
||||
error_setg(errp, "NFS server address missing");
|
||||
goto out;
|
||||
}
|
||||
|
||||
crumpled_addr = qdict_crumple(addr, errp);
|
||||
if (!crumpled_addr) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Caution: this works only because all scalar members of
|
||||
* NFSServer are QString in @crumpled_addr. The visitor expects
|
||||
* @crumpled_addr to be typed according to the QAPI schema. It
|
||||
* is when @options come from -blockdev or blockdev_add. But when
|
||||
* they come from -drive, they're all QString.
|
||||
*/
|
||||
iv = qobject_input_visitor_new(crumpled_addr);
|
||||
visit_type_NFSServer(iv, NULL, &server, &local_error);
|
||||
if (local_error) {
|
||||
error_propagate(errp, local_error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
QDECREF(addr);
|
||||
qobject_decref(crumpled_addr);
|
||||
visit_free(iv);
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
static int64_t nfs_client_open(NFSClient *client, QDict *options,
|
||||
static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
|
||||
int flags, int open_flags, Error **errp)
|
||||
{
|
||||
int64_t ret = -EINVAL;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
struct stat st;
|
||||
char *file = NULL, *strp = NULL;
|
||||
|
||||
qemu_mutex_init(&client->mutex);
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
client->path = g_strdup(qemu_opt_get(opts, "path"));
|
||||
if (!client->path) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "No path was specified");
|
||||
goto fail;
|
||||
}
|
||||
client->path = g_strdup(opts->path);
|
||||
|
||||
strp = strrchr(client->path, '/');
|
||||
if (strp == NULL) {
|
||||
@@ -526,12 +428,10 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
|
||||
file = g_strdup(strp);
|
||||
*strp = 0;
|
||||
|
||||
/* Pop the config into our state object, Exit if invalid */
|
||||
client->server = nfs_config(options, errp);
|
||||
if (!client->server) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
/* Steal the NFSServer object from opts; set the original pointer to NULL
|
||||
* to avoid use after free and double free. */
|
||||
client->server = opts->server;
|
||||
opts->server = NULL;
|
||||
|
||||
client->context = nfs_init_context();
|
||||
if (client->context == NULL) {
|
||||
@@ -539,29 +439,29 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (qemu_opt_get(opts, "user")) {
|
||||
client->uid = qemu_opt_get_number(opts, "user", 0);
|
||||
if (opts->has_user) {
|
||||
client->uid = opts->user;
|
||||
nfs_set_uid(client->context, client->uid);
|
||||
}
|
||||
|
||||
if (qemu_opt_get(opts, "group")) {
|
||||
client->gid = qemu_opt_get_number(opts, "group", 0);
|
||||
if (opts->has_group) {
|
||||
client->gid = opts->group;
|
||||
nfs_set_gid(client->context, client->gid);
|
||||
}
|
||||
|
||||
if (qemu_opt_get(opts, "tcp-syn-count")) {
|
||||
client->tcp_syncnt = qemu_opt_get_number(opts, "tcp-syn-count", 0);
|
||||
if (opts->has_tcp_syn_count) {
|
||||
client->tcp_syncnt = opts->tcp_syn_count;
|
||||
nfs_set_tcp_syncnt(client->context, client->tcp_syncnt);
|
||||
}
|
||||
|
||||
#ifdef LIBNFS_FEATURE_READAHEAD
|
||||
if (qemu_opt_get(opts, "readahead-size")) {
|
||||
if (opts->has_readahead_size) {
|
||||
if (open_flags & BDRV_O_NOCACHE) {
|
||||
error_setg(errp, "Cannot enable NFS readahead "
|
||||
"if cache.direct = on");
|
||||
goto fail;
|
||||
}
|
||||
client->readahead = qemu_opt_get_number(opts, "readahead-size", 0);
|
||||
client->readahead = opts->readahead_size;
|
||||
if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) {
|
||||
warn_report("Truncating NFS readahead size to %d",
|
||||
QEMU_NFS_MAX_READAHEAD_SIZE);
|
||||
@@ -576,13 +476,13 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
|
||||
#endif
|
||||
|
||||
#ifdef LIBNFS_FEATURE_PAGECACHE
|
||||
if (qemu_opt_get(opts, "page-cache-size")) {
|
||||
if (opts->has_page_cache_size) {
|
||||
if (open_flags & BDRV_O_NOCACHE) {
|
||||
error_setg(errp, "Cannot enable NFS pagecache "
|
||||
"if cache.direct = on");
|
||||
goto fail;
|
||||
}
|
||||
client->pagecache = qemu_opt_get_number(opts, "page-cache-size", 0);
|
||||
client->pagecache = opts->page_cache_size;
|
||||
if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) {
|
||||
warn_report("Truncating NFS pagecache size to %d pages",
|
||||
QEMU_NFS_MAX_PAGECACHE_SIZE);
|
||||
@@ -595,8 +495,8 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
|
||||
#endif
|
||||
|
||||
#ifdef LIBNFS_FEATURE_DEBUG
|
||||
if (qemu_opt_get(opts, "debug")) {
|
||||
client->debug = qemu_opt_get_number(opts, "debug", 0);
|
||||
if (opts->has_debug) {
|
||||
client->debug = opts->debug;
|
||||
/* limit the maximum debug level to avoid potential flooding
|
||||
* of our log files. */
|
||||
if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) {
|
||||
@@ -647,11 +547,53 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
|
||||
fail:
|
||||
nfs_client_close(client);
|
||||
out:
|
||||
qemu_opts_del(opts);
|
||||
g_free(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevOptionsNfs *opts = NULL;
|
||||
QObject *crumpled = NULL;
|
||||
Visitor *v;
|
||||
Error *local_err = NULL;
|
||||
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
|
||||
int flags, int open_flags, Error **errp)
|
||||
{
|
||||
BlockdevOptionsNfs *opts;
|
||||
int ret;
|
||||
|
||||
opts = nfs_options_qdict_to_qapi(options, errp);
|
||||
if (opts == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = nfs_client_open(client, opts, flags, open_flags, errp);
|
||||
fail:
|
||||
qapi_free_BlockdevOptionsNfs(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp) {
|
||||
NFSClient *client = bs->opaque;
|
||||
@@ -659,9 +601,9 @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
client->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
ret = nfs_client_open(client, options,
|
||||
(flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
|
||||
bs->open_flags, errp);
|
||||
ret = nfs_client_open_qdict(client, options,
|
||||
(flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
|
||||
bs->open_flags, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -684,18 +626,43 @@ static QemuOptsList nfs_create_opts = {
|
||||
}
|
||||
};
|
||||
|
||||
static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
|
||||
Error **errp)
|
||||
static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
int64_t ret, total_size;
|
||||
BlockdevCreateOptionsNfs *opts = &options->u.nfs;
|
||||
NFSClient *client = g_new0(NFSClient, 1);
|
||||
QDict *options = NULL;
|
||||
int ret;
|
||||
|
||||
assert(options->driver == BLOCKDEV_DRIVER_NFS);
|
||||
|
||||
client->aio_context = qemu_get_aio_context();
|
||||
|
||||
ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = nfs_ftruncate(client->context, client->fh, opts->size);
|
||||
nfs_client_close(client);
|
||||
|
||||
out:
|
||||
g_free(client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options;
|
||||
BlockdevCreateOptionsNfs *nfs_opts;
|
||||
QDict *options;
|
||||
int ret;
|
||||
|
||||
create_options = g_new0(BlockdevCreateOptions, 1);
|
||||
create_options->driver = BLOCKDEV_DRIVER_NFS;
|
||||
nfs_opts = &create_options->u.nfs;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
options = qdict_new();
|
||||
ret = nfs_parse_uri(url, options, errp);
|
||||
@@ -703,15 +670,21 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nfs_client_open(client, options, O_CREAT, 0, errp);
|
||||
nfs_opts->location = nfs_options_qdict_to_qapi(options, errp);
|
||||
if (nfs_opts->location == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nfs_file_co_create(create_options, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = nfs_ftruncate(client->context, client->fh, total_size);
|
||||
nfs_client_close(client);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
QDECREF(options);
|
||||
g_free(client);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -876,8 +849,8 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
}
|
||||
|
||||
#ifdef LIBNFS_FEATURE_PAGECACHE
|
||||
static void nfs_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
nfs_pagecache_invalidate(client->context, client->fh);
|
||||
@@ -898,6 +871,7 @@ static BlockDriver bdrv_nfs = {
|
||||
|
||||
.bdrv_file_open = nfs_file_open,
|
||||
.bdrv_close = nfs_file_close,
|
||||
.bdrv_co_create = nfs_file_co_create,
|
||||
.bdrv_co_create_opts = nfs_file_co_create_opts,
|
||||
.bdrv_reopen_prepare = nfs_reopen_prepare,
|
||||
|
||||
@@ -910,7 +884,7 @@ static BlockDriver bdrv_nfs = {
|
||||
.bdrv_refresh_filename = nfs_refresh_filename,
|
||||
|
||||
#ifdef LIBNFS_FEATURE_PAGECACHE
|
||||
.bdrv_invalidate_cache = nfs_invalidate_cache,
|
||||
.bdrv_co_invalidate_cache = nfs_co_invalidate_cache,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -378,8 +378,9 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
|
||||
static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
static int coroutine_fn parallels_co_check(BlockDriverState *bs,
|
||||
BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
int64_t size, prev_off, high_off;
|
||||
@@ -394,6 +395,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
return size;
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (s->header_unclean) {
|
||||
fprintf(stderr, "%s image was not closed correctly\n",
|
||||
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR");
|
||||
@@ -442,11 +444,12 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
prev_off = off;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
if (flush_bat) {
|
||||
ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size);
|
||||
if (ret < 0) {
|
||||
res->check_errors++;
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,13 +468,15 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
res->check_errors++;
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
res->leaks_fixed += count;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -799,7 +804,7 @@ static BlockDriver bdrv_parallels = {
|
||||
.bdrv_co_writev = parallels_co_writev,
|
||||
.supports_backing = true,
|
||||
.bdrv_co_create_opts = parallels_co_create_opts,
|
||||
.bdrv_check = parallels_check,
|
||||
.bdrv_co_check = parallels_co_check,
|
||||
.create_opts = ¶llels_create_opts,
|
||||
};
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ static int update_header_sync(BlockDriverState *bs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_flush(bs);
|
||||
return bdrv_flush(bs->file->bs);
|
||||
}
|
||||
|
||||
static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
|
||||
@@ -882,7 +882,7 @@ static int update_ext_header_and_dir(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_flush(bs->file->bs);
|
||||
ret = qcow2_flush_caches(bs);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include <zlib.h>
|
||||
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
@@ -2092,11 +2093,21 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
for (i = 0; i < s->nb_snapshots; i++) {
|
||||
int l1_sectors = DIV_ROUND_UP(s->snapshots[i].l1_size *
|
||||
sizeof(uint64_t), BDRV_SECTOR_SIZE);
|
||||
int l1_size2;
|
||||
uint64_t *new_l1_table;
|
||||
Error *local_err = NULL;
|
||||
|
||||
uint64_t *new_l1_table =
|
||||
g_try_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
|
||||
ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset,
|
||||
s->snapshots[i].l1_size, sizeof(uint64_t),
|
||||
QCOW_MAX_L1_SIZE, "Snapshot L1 table",
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
l1_size2 = s->snapshots[i].l1_size * sizeof(uint64_t);
|
||||
new_l1_table = g_try_realloc(l1_table, l1_size2);
|
||||
|
||||
if (!new_l1_table) {
|
||||
ret = -ENOMEM;
|
||||
@@ -2105,9 +2116,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
|
||||
l1_table = new_l1_table;
|
||||
|
||||
ret = bdrv_read(bs->file,
|
||||
s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE,
|
||||
(void *)l1_table, l1_sectors);
|
||||
ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset,
|
||||
l1_table, l1_size2);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -1171,7 +1171,35 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
||||
}
|
||||
}
|
||||
|
||||
int coroutine_fn qcow2_write_caches(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
ret = qcow2_cache_write(bs, s->l2_table_cache);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (qcow2_need_accurate_refcounts(s)) {
|
||||
ret = qcow2_cache_write(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coroutine_fn qcow2_flush_caches(BlockDriverState *bs)
|
||||
{
|
||||
int ret = qcow2_write_caches(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_flush(bs->file->bs);
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/* snapshots and image creation */
|
||||
@@ -2019,6 +2047,20 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
/* snapshots */
|
||||
for (i = 0; i < s->nb_snapshots; i++) {
|
||||
sn = s->snapshots + i;
|
||||
if (offset_into_cluster(s, sn->l1_table_offset)) {
|
||||
fprintf(stderr, "ERROR snapshot %s (%s) l1_offset=%#" PRIx64 ": "
|
||||
"L1 table is not cluster aligned; snapshot table entry "
|
||||
"corrupted\n", sn->id_str, sn->name, sn->l1_table_offset);
|
||||
res->corruptions++;
|
||||
continue;
|
||||
}
|
||||
if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
|
||||
fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": "
|
||||
"L1 table is too large; snapshot table entry corrupted\n",
|
||||
sn->id_str, sn->name, sn->l1_size);
|
||||
res->corruptions++;
|
||||
continue;
|
||||
}
|
||||
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
|
||||
sn->l1_table_offset, sn->l1_size, 0, fix);
|
||||
if (ret < 0) {
|
||||
@@ -2614,9 +2656,17 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
|
||||
uint32_t l1_sz = s->snapshots[i].l1_size;
|
||||
uint64_t l1_sz2 = l1_sz * sizeof(uint64_t);
|
||||
uint64_t *l1 = g_try_malloc(l1_sz2);
|
||||
uint64_t *l1;
|
||||
int ret;
|
||||
|
||||
ret = qcow2_validate_table(bs, l1_ofs, l1_sz, sizeof(uint64_t),
|
||||
QCOW_MAX_L1_SIZE, "", NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
l1 = g_try_malloc(l1_sz2);
|
||||
|
||||
if (l1_sz2 && l1 == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -465,6 +465,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
QCowSnapshot *sn;
|
||||
Error *local_err = NULL;
|
||||
int i, snapshot_index;
|
||||
int cur_l1_bytes, sn_l1_bytes;
|
||||
int ret;
|
||||
@@ -477,6 +478,14 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||
}
|
||||
sn = &s->snapshots[snapshot_index];
|
||||
|
||||
ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size,
|
||||
sizeof(uint64_t), QCOW_MAX_L1_SIZE,
|
||||
"Snapshot L1 table", &local_err);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
|
||||
error_report("qcow2: Loading snapshots with different disk "
|
||||
"size is not implemented");
|
||||
@@ -602,6 +611,13 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
}
|
||||
sn = s->snapshots[snapshot_index];
|
||||
|
||||
ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size,
|
||||
sizeof(uint64_t), QCOW_MAX_L1_SIZE,
|
||||
"Snapshot L1 table", errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Remove it from the snapshot list */
|
||||
memmove(s->snapshots + snapshot_index,
|
||||
s->snapshots + snapshot_index + 1,
|
||||
@@ -704,9 +720,11 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
sn = &s->snapshots[snapshot_index];
|
||||
|
||||
/* Allocate and read in the snapshot's L1 table */
|
||||
if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
|
||||
error_setg(errp, "Snapshot L1 table too large");
|
||||
return -EFBIG;
|
||||
ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size,
|
||||
sizeof(uint64_t), QCOW_MAX_L1_SIZE,
|
||||
"Snapshot L1 table", errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
|
||||
new_l1_table = qemu_try_blockalign(bs->file->bs,
|
||||
|
||||
580
block/qcow2.c
580
block/qcow2.c
@@ -37,7 +37,8 @@
|
||||
#include "qemu/option_int.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "block/crypto.h"
|
||||
|
||||
/*
|
||||
@@ -500,7 +501,7 @@ static int qcow2_mark_clean(BlockDriverState *bs)
|
||||
|
||||
s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
|
||||
|
||||
ret = bdrv_flush(bs);
|
||||
ret = qcow2_flush_caches(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -530,7 +531,7 @@ int qcow2_mark_consistent(BlockDriverState *bs)
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
|
||||
if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
|
||||
int ret = bdrv_flush(bs);
|
||||
int ret = qcow2_flush_caches(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -541,8 +542,9 @@ int qcow2_mark_consistent(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs,
|
||||
BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
int ret = qcow2_check_refcounts(bs, result, fix);
|
||||
if (ret < 0) {
|
||||
@@ -559,26 +561,36 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t entries, size_t entry_len)
|
||||
static int coroutine_fn qcow2_co_check(BlockDriverState *bs,
|
||||
BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t size;
|
||||
int ret;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_co_check_locked(bs, result, fix);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t entries, size_t entry_len,
|
||||
int64_t max_size_bytes, const char *table_name,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
|
||||
if (entries > max_size_bytes / entry_len) {
|
||||
error_setg(errp, "%s too large", table_name);
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
/* Use signed INT64_MAX as the maximum even for uint64_t header fields,
|
||||
* because values will be passed to qemu functions taking int64_t. */
|
||||
if (entries > INT64_MAX / entry_len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = entries * entry_len;
|
||||
|
||||
if (INT64_MAX - size < offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Tables must be cluster aligned */
|
||||
if (offset_into_cluster(s, offset) != 0) {
|
||||
if ((INT64_MAX - entries * entry_len < offset) ||
|
||||
(offset_into_cluster(s, offset) != 0)) {
|
||||
error_setg(errp, "%s offset invalid", table_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -1118,8 +1130,9 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
/* Called with s->lock held. */
|
||||
static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
unsigned int len, i;
|
||||
@@ -1308,47 +1321,42 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->refcount_table_size =
|
||||
header.refcount_table_clusters << (s->cluster_bits - 3);
|
||||
|
||||
if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) {
|
||||
error_setg(errp, "Reference count table too large");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) {
|
||||
error_setg(errp, "Image does not contain a reference count table");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = validate_table_offset(bs, s->refcount_table_offset,
|
||||
s->refcount_table_size, sizeof(uint64_t));
|
||||
ret = qcow2_validate_table(bs, s->refcount_table_offset,
|
||||
header.refcount_table_clusters,
|
||||
s->cluster_size, QCOW_MAX_REFTABLE_SIZE,
|
||||
"Reference count table", errp);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Invalid reference count table offset");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Snapshot table offset/length */
|
||||
if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) {
|
||||
error_setg(errp, "Too many snapshots");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = validate_table_offset(bs, header.snapshots_offset,
|
||||
header.nb_snapshots,
|
||||
sizeof(QCowSnapshotHeader));
|
||||
/* The total size in bytes of the snapshot table is checked in
|
||||
* qcow2_read_snapshots() because the size of each snapshot is
|
||||
* variable and we don't know it yet.
|
||||
* Here we only check the offset and number of snapshots. */
|
||||
ret = qcow2_validate_table(bs, header.snapshots_offset,
|
||||
header.nb_snapshots,
|
||||
sizeof(QCowSnapshotHeader),
|
||||
sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS,
|
||||
"Snapshot table", errp);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Invalid snapshot table offset");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* read the level 1 table */
|
||||
if (header.l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
|
||||
error_setg(errp, "Active L1 table too large");
|
||||
ret = -EFBIG;
|
||||
ret = qcow2_validate_table(bs, header.l1_table_offset,
|
||||
header.l1_size, sizeof(uint64_t),
|
||||
QCOW_MAX_L1_SIZE, "Active L1 table", errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
s->l1_size = header.l1_size;
|
||||
s->l1_table_offset = header.l1_table_offset;
|
||||
|
||||
l1_vm_state_index = size_to_l1(s, header.size);
|
||||
if (l1_vm_state_index > INT_MAX) {
|
||||
@@ -1366,15 +1374,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = validate_table_offset(bs, header.l1_table_offset,
|
||||
header.l1_size, sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Invalid L1 table offset");
|
||||
goto fail;
|
||||
}
|
||||
s->l1_table_offset = header.l1_table_offset;
|
||||
|
||||
|
||||
if (s->l1_size > 0) {
|
||||
s->l1_table = qemu_try_blockalign(bs->file->bs,
|
||||
ROUND_UP(s->l1_size * sizeof(uint64_t), 512));
|
||||
@@ -1498,8 +1497,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialise locks */
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0;
|
||||
|
||||
/* Repair image if dirty */
|
||||
@@ -1507,7 +1504,8 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
|
||||
BdrvCheckResult result = {0};
|
||||
|
||||
ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
|
||||
ret = qcow2_co_check_locked(bs, &result,
|
||||
BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
|
||||
if (ret < 0 || result.check_errors) {
|
||||
if (ret >= 0) {
|
||||
ret = -EIO;
|
||||
@@ -1545,16 +1543,53 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct QCow2OpenCo {
|
||||
BlockDriverState *bs;
|
||||
QDict *options;
|
||||
int flags;
|
||||
Error **errp;
|
||||
int ret;
|
||||
} QCow2OpenCo;
|
||||
|
||||
static void coroutine_fn qcow2_open_entry(void *opaque)
|
||||
{
|
||||
QCow2OpenCo *qoc = opaque;
|
||||
BDRVQcow2State *s = qoc->bs->opaque;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
}
|
||||
|
||||
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
QCow2OpenCo qoc = {
|
||||
.bs = bs,
|
||||
.options = options,
|
||||
.flags = flags,
|
||||
.errp = errp,
|
||||
.ret = -EINPROGRESS
|
||||
};
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return qcow2_do_open(bs, options, flags, errp);
|
||||
/* Initialise locks */
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
/* From bdrv_co_create. */
|
||||
qcow2_open_entry(&qoc);
|
||||
} else {
|
||||
qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc));
|
||||
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
|
||||
}
|
||||
return qoc.ret;
|
||||
}
|
||||
|
||||
static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
@@ -2106,7 +2141,8 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
qcow2_free_snapshots(bs);
|
||||
}
|
||||
|
||||
static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int flags = s->flags;
|
||||
@@ -2129,7 +2165,9 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
options = qdict_clone_shallow(bs->options);
|
||||
|
||||
flags &= ~BDRV_O_INACTIVE;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_do_open(bs, options, flags, &local_err);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
QDECREF(options);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -2412,39 +2450,26 @@ static int qcow2_crypt_method_from_format(const char *encryptfmt)
|
||||
}
|
||||
}
|
||||
|
||||
static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
|
||||
QemuOpts *opts, Error **errp)
|
||||
static int qcow2_set_up_encryption(BlockDriverState *bs,
|
||||
QCryptoBlockCreateOptions *cryptoopts,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
QCryptoBlockCreateOptions *cryptoopts = NULL;
|
||||
QCryptoBlock *crypto = NULL;
|
||||
int ret = -EINVAL;
|
||||
QDict *options, *encryptopts;
|
||||
int fmt;
|
||||
int fmt, ret;
|
||||
|
||||
options = qemu_opts_to_qdict(opts, NULL);
|
||||
qdict_extract_subqdict(options, &encryptopts, "encrypt.");
|
||||
QDECREF(options);
|
||||
|
||||
fmt = qcow2_crypt_method_from_format(encryptfmt);
|
||||
|
||||
switch (fmt) {
|
||||
case QCOW_CRYPT_LUKS:
|
||||
cryptoopts = block_crypto_create_opts_init(
|
||||
Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp);
|
||||
switch (cryptoopts->format) {
|
||||
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
|
||||
fmt = QCOW_CRYPT_LUKS;
|
||||
break;
|
||||
case QCOW_CRYPT_AES:
|
||||
cryptoopts = block_crypto_create_opts_init(
|
||||
Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
|
||||
case Q_CRYPTO_BLOCK_FORMAT_QCOW:
|
||||
fmt = QCOW_CRYPT_AES;
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Unknown encryption format '%s'", encryptfmt);
|
||||
break;
|
||||
}
|
||||
if (!cryptoopts) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
error_setg(errp, "Crypto format not supported in qcow2");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s->crypt_method_header = fmt;
|
||||
|
||||
crypto = qcrypto_block_create(cryptoopts, "encrypt.",
|
||||
@@ -2452,8 +2477,7 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
|
||||
qcow2_crypto_hdr_write_func,
|
||||
bs, errp);
|
||||
if (!crypto) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = qcow2_update_header(bs);
|
||||
@@ -2462,10 +2486,9 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
QDECREF(encryptopts);
|
||||
qcrypto_block_free(crypto);
|
||||
qapi_free_QCryptoBlockCreateOptions(cryptoopts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2663,19 +2686,26 @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size,
|
||||
return meta_size + aligned_total_size;
|
||||
}
|
||||
|
||||
static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
|
||||
static bool validate_cluster_size(size_t cluster_size, Error **errp)
|
||||
{
|
||||
size_t cluster_size;
|
||||
int cluster_bits;
|
||||
|
||||
cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
cluster_bits = ctz32(cluster_size);
|
||||
int cluster_bits = ctz32(cluster_size);
|
||||
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
|
||||
(1 << cluster_bits) != cluster_size)
|
||||
{
|
||||
error_setg(errp, "Cluster size must be a power of two between %d and "
|
||||
"%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
|
||||
{
|
||||
size_t cluster_size;
|
||||
|
||||
cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
if (!validate_cluster_size(cluster_size, errp)) {
|
||||
return 0;
|
||||
}
|
||||
return cluster_size;
|
||||
@@ -2724,12 +2754,9 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
qcow2_co_create2(const char *filename, int64_t total_size,
|
||||
const char *backing_file, const char *backing_format,
|
||||
int flags, size_t cluster_size, PreallocMode prealloc,
|
||||
QemuOpts *opts, int version, int refcount_order,
|
||||
const char *encryptfmt, Error **errp)
|
||||
qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsQcow2 *qcow2_opts;
|
||||
QDict *options;
|
||||
|
||||
/*
|
||||
@@ -2744,36 +2771,132 @@ qcow2_co_create2(const char *filename, int64_t total_size,
|
||||
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
|
||||
* size for any qcow2 image.
|
||||
*/
|
||||
BlockBackend *blk;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QCowHeader *header;
|
||||
size_t cluster_size;
|
||||
int version;
|
||||
int refcount_order;
|
||||
uint64_t* refcount_table;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
|
||||
int64_t prealloc_size =
|
||||
qcow2_calc_prealloc_size(total_size, cluster_size, refcount_order);
|
||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
|
||||
qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc),
|
||||
&error_abort);
|
||||
}
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
|
||||
qcow2_opts = &create_options->u.qcow2;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp);
|
||||
if (bs == NULL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Validate options and set default values */
|
||||
if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) {
|
||||
error_setg(errp, "Image size must be a multiple of 512 bytes");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qcow2_opts->has_version) {
|
||||
switch (qcow2_opts->version) {
|
||||
case BLOCKDEV_QCOW2_VERSION_V2:
|
||||
version = 2;
|
||||
break;
|
||||
case BLOCKDEV_QCOW2_VERSION_V3:
|
||||
version = 3;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
} else {
|
||||
version = 3;
|
||||
}
|
||||
|
||||
if (qcow2_opts->has_cluster_size) {
|
||||
cluster_size = qcow2_opts->cluster_size;
|
||||
} else {
|
||||
cluster_size = DEFAULT_CLUSTER_SIZE;
|
||||
}
|
||||
|
||||
if (!validate_cluster_size(cluster_size, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!qcow2_opts->has_preallocation) {
|
||||
qcow2_opts->preallocation = PREALLOC_MODE_OFF;
|
||||
}
|
||||
if (qcow2_opts->has_backing_file &&
|
||||
qcow2_opts->preallocation != PREALLOC_MODE_OFF)
|
||||
{
|
||||
error_setg(errp, "Backing file and preallocation cannot be used at "
|
||||
"the same time");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (qcow2_opts->has_backing_fmt && !qcow2_opts->has_backing_file) {
|
||||
error_setg(errp, "Backing format cannot be used without backing file");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!qcow2_opts->has_lazy_refcounts) {
|
||||
qcow2_opts->lazy_refcounts = false;
|
||||
}
|
||||
if (version < 3 && qcow2_opts->lazy_refcounts) {
|
||||
error_setg(errp, "Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use version=v3 or greater)");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!qcow2_opts->has_refcount_bits) {
|
||||
qcow2_opts->refcount_bits = 16;
|
||||
}
|
||||
if (qcow2_opts->refcount_bits > 64 ||
|
||||
!is_power_of_2(qcow2_opts->refcount_bits))
|
||||
{
|
||||
error_setg(errp, "Refcount width must be a power of two and may not "
|
||||
"exceed 64 bits");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (version < 3 && qcow2_opts->refcount_bits != 16) {
|
||||
error_setg(errp, "Different refcount widths than 16 bits require "
|
||||
"compatibility level 1.1 or above (use version=v3 or "
|
||||
"greater)");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
refcount_order = ctz32(qcow2_opts->refcount_bits);
|
||||
|
||||
|
||||
/* Create BlockBackend to write to the image */
|
||||
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Clear the protocol layer and preallocate it if necessary */
|
||||
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qcow2_opts->preallocation == PREALLOC_MODE_FULL ||
|
||||
qcow2_opts->preallocation == PREALLOC_MODE_FALLOC)
|
||||
{
|
||||
int64_t prealloc_size =
|
||||
qcow2_calc_prealloc_size(qcow2_opts->size, cluster_size,
|
||||
refcount_order);
|
||||
|
||||
ret = blk_truncate(blk, prealloc_size, qcow2_opts->preallocation, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the header */
|
||||
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
|
||||
header = g_malloc0(cluster_size);
|
||||
@@ -2793,7 +2916,7 @@ qcow2_co_create2(const char *filename, int64_t total_size,
|
||||
/* We'll update this to correct value later */
|
||||
header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
|
||||
|
||||
if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
|
||||
if (qcow2_opts->lazy_refcounts) {
|
||||
header->compatible_features |=
|
||||
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
|
||||
}
|
||||
@@ -2826,7 +2949,8 @@ qcow2_co_create2(const char *filename, int64_t total_size,
|
||||
*/
|
||||
options = qdict_new();
|
||||
qdict_put_str(options, "driver", "qcow2");
|
||||
blk = blk_new_open(filename, NULL, options,
|
||||
qdict_put_str(options, "file", bs->node_name);
|
||||
blk = blk_new_open(NULL, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
@@ -2854,33 +2978,41 @@ qcow2_co_create2(const char *filename, int64_t total_size,
|
||||
}
|
||||
|
||||
/* Okay, now that we have a valid image, let's give it the right size */
|
||||
ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp);
|
||||
ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp);
|
||||
if (ret < 0) {
|
||||
error_prepend(errp, "Could not resize image: ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Want a backing file? There you go.*/
|
||||
if (backing_file) {
|
||||
ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format);
|
||||
if (qcow2_opts->has_backing_file) {
|
||||
const char *backing_format = NULL;
|
||||
|
||||
if (qcow2_opts->has_backing_fmt) {
|
||||
backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt);
|
||||
}
|
||||
|
||||
ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file,
|
||||
backing_format);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
|
||||
"with format '%s'", backing_file, backing_format);
|
||||
"with format '%s'", qcow2_opts->backing_file,
|
||||
backing_format);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Want encryption? There you go. */
|
||||
if (encryptfmt) {
|
||||
ret = qcow2_set_up_encryption(blk_bs(blk), encryptfmt, opts, errp);
|
||||
if (qcow2_opts->has_encrypt) {
|
||||
ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* And if we're supposed to preallocate metadata, do that now */
|
||||
if (prealloc != PREALLOC_MODE_OFF) {
|
||||
ret = preallocate(blk_bs(blk), 0, total_size);
|
||||
if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) {
|
||||
ret = preallocate(blk_bs(blk), 0, qcow2_opts->size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not preallocate metadata");
|
||||
goto out;
|
||||
@@ -2898,7 +3030,8 @@ qcow2_co_create2(const char *filename, int64_t total_size,
|
||||
*/
|
||||
options = qdict_new();
|
||||
qdict_put_str(options, "driver", "qcow2");
|
||||
blk = blk_new_open(filename, NULL, options,
|
||||
qdict_put_str(options, "file", bs->node_name);
|
||||
blk = blk_new_open(NULL, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
@@ -2909,104 +3042,120 @@ qcow2_co_create2(const char *filename, int64_t total_size,
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
}
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
char *backing_file = NULL;
|
||||
char *backing_fmt = NULL;
|
||||
char *buf = NULL;
|
||||
uint64_t size = 0;
|
||||
int flags = 0;
|
||||
size_t cluster_size = DEFAULT_CLUSTER_SIZE;
|
||||
PreallocMode prealloc;
|
||||
int version;
|
||||
uint64_t refcount_bits;
|
||||
int refcount_order;
|
||||
char *encryptfmt = NULL;
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
const char *val;
|
||||
int ret;
|
||||
|
||||
/* Read out options */
|
||||
size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
|
||||
encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
|
||||
if (encryptfmt) {
|
||||
if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
|
||||
error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
|
||||
BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
} else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
|
||||
encryptfmt = g_strdup("aes");
|
||||
/* Only the keyval visitor supports the dotted syntax needed for
|
||||
* encryption, so go through a QDict before getting a QAPI type. Ignore
|
||||
* options meant for the protocol layer so that the visitor doesn't
|
||||
* complain. */
|
||||
qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
|
||||
true);
|
||||
|
||||
/* Handle encryption options */
|
||||
val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
|
||||
if (val && !strcmp(val, "on")) {
|
||||
qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
|
||||
} else if (val && !strcmp(val, "off")) {
|
||||
qdict_del(qdict, BLOCK_OPT_ENCRYPT);
|
||||
}
|
||||
cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
|
||||
val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
|
||||
if (val && !strcmp(val, "aes")) {
|
||||
qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
|
||||
}
|
||||
|
||||
/* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into
|
||||
* version=v2/v3 below. */
|
||||
val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL);
|
||||
if (val && !strcmp(val, "0.10")) {
|
||||
qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2");
|
||||
} else if (val && !strcmp(val, "1.1")) {
|
||||
qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3");
|
||||
}
|
||||
|
||||
/* Change legacy command line options into QMP ones */
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
|
||||
{ BLOCK_OPT_BACKING_FMT, "backing-fmt" },
|
||||
{ BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
|
||||
{ BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" },
|
||||
{ BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" },
|
||||
{ BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
|
||||
{ BLOCK_OPT_COMPAT_LEVEL, "version" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
|
||||
PREALLOC_MODE_OFF, &local_err);
|
||||
|
||||
/* Create and open the file (protocol layer) */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Set 'driver' and 'node' options */
|
||||
qdict_put_str(qdict, "driver", "qcow2");
|
||||
qdict_put_str(qdict, "file", bs->node_name);
|
||||
|
||||
/* Now get the QAPI type BlockdevCreateOptions */
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to_qdict(qobj);
|
||||
if (qdict == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
version = qcow2_opt_get_version_del(opts, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
/* Silently round up size */
|
||||
create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size,
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
/* Create the qcow2 image (format layer) */
|
||||
ret = qcow2_co_create(create_options, errp);
|
||||
if (ret < 0) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) {
|
||||
flags |= BLOCK_FLAG_LAZY_REFCOUNTS;
|
||||
}
|
||||
|
||||
if (backing_file && prealloc != PREALLOC_MODE_OFF) {
|
||||
error_setg(errp, "Backing file and preallocation cannot be used at "
|
||||
"the same time");
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
|
||||
error_setg(errp, "Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use compat=1.1 or greater)");
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
refcount_order = ctz32(refcount_bits);
|
||||
|
||||
ret = qcow2_co_create2(filename, size, backing_file, backing_fmt, flags,
|
||||
cluster_size, prealloc, opts, version, refcount_order,
|
||||
encryptfmt, &local_err);
|
||||
error_propagate(errp, local_err);
|
||||
|
||||
ret = 0;
|
||||
finish:
|
||||
g_free(backing_file);
|
||||
g_free(backing_fmt);
|
||||
g_free(encryptfmt);
|
||||
g_free(buf);
|
||||
QDECREF(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3647,22 +3796,10 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||
int ret;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_cache_write(bs, s->l2_table_cache);
|
||||
if (ret < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (qcow2_need_accurate_refcounts(s)) {
|
||||
ret = qcow2_cache_write(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = qcow2_write_caches(bs);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
|
||||
@@ -4353,6 +4490,7 @@ BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_join_options = qcow2_join_options,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create_opts = qcow2_co_create_opts,
|
||||
.bdrv_co_create = qcow2_co_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_block_status = qcow2_co_block_status,
|
||||
|
||||
@@ -4382,11 +4520,11 @@ BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_change_backing_file = qcow2_change_backing_file,
|
||||
|
||||
.bdrv_refresh_limits = qcow2_refresh_limits,
|
||||
.bdrv_invalidate_cache = qcow2_invalidate_cache,
|
||||
.bdrv_co_invalidate_cache = qcow2_co_invalidate_cache,
|
||||
.bdrv_inactivate = qcow2_inactivate,
|
||||
|
||||
.create_opts = &qcow2_create_opts,
|
||||
.bdrv_check = qcow2_check,
|
||||
.bdrv_co_check = qcow2_co_check,
|
||||
.bdrv_amend_options = qcow2_amend_options,
|
||||
|
||||
.bdrv_detach_aio_context = qcow2_detach_aio_context,
|
||||
|
||||
@@ -485,11 +485,6 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
|
||||
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
|
||||
}
|
||||
|
||||
static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s)
|
||||
{
|
||||
return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
|
||||
}
|
||||
|
||||
static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
|
||||
{
|
||||
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
||||
@@ -547,6 +542,11 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
||||
int64_t size, const char *message_format, ...)
|
||||
GCC_FMT_ATTR(5, 6);
|
||||
|
||||
int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t entries, size_t entry_len,
|
||||
int64_t max_size_bytes, const char *table_name,
|
||||
Error **errp);
|
||||
|
||||
/* qcow2-refcount.c functions */
|
||||
int qcow2_refcount_init(BlockDriverState *bs);
|
||||
void qcow2_refcount_close(BlockDriverState *bs);
|
||||
@@ -576,6 +576,8 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
||||
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
int64_t l1_table_offset, int l1_size, int addend);
|
||||
|
||||
int coroutine_fn qcow2_flush_caches(BlockDriverState *bs);
|
||||
int coroutine_fn qcow2_write_caches(BlockDriverState *bs);
|
||||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix);
|
||||
|
||||
|
||||
@@ -217,6 +217,7 @@ static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result)
|
||||
qed_write_header_sync(s);
|
||||
}
|
||||
|
||||
/* Called with table_lock held. */
|
||||
int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
|
||||
{
|
||||
QEDCheck check = {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "qed.h"
|
||||
#include "qemu/bswap.h"
|
||||
|
||||
/* Called either from qed_check or with table_lock held. */
|
||||
/* Called with table_lock held. */
|
||||
static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
|
||||
{
|
||||
QEMUIOVector qiov;
|
||||
@@ -33,13 +33,9 @@ static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
|
||||
|
||||
trace_qed_read_table(s, offset, table);
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
}
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
ret = bdrv_preadv(s->bs->file, offset, &qiov);
|
||||
if (qemu_in_coroutine()) {
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
}
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -67,7 +63,7 @@ out:
|
||||
* @n: Number of elements
|
||||
* @flush: Whether or not to sync to disk
|
||||
*
|
||||
* Called either from qed_check or with table_lock held.
|
||||
* Called with table_lock held.
|
||||
*/
|
||||
static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
unsigned int index, unsigned int n, bool flush)
|
||||
@@ -104,13 +100,9 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
|
||||
/* Adjust for offset into table */
|
||||
offset += start * sizeof(uint64_t);
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
}
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
ret = bdrv_pwritev(s->bs->file, offset, &qiov);
|
||||
if (qemu_in_coroutine()) {
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
}
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
trace_qed_write_table_cb(s, table, flush, ret);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
@@ -134,7 +126,7 @@ int qed_read_l1_table_sync(BDRVQEDState *s)
|
||||
return qed_read_table(s, s->header.l1_table_offset, s->l1_table);
|
||||
}
|
||||
|
||||
/* Called either from qed_check or with table_lock held. */
|
||||
/* Called with table_lock held. */
|
||||
int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n)
|
||||
{
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
|
||||
@@ -148,7 +140,7 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
|
||||
return qed_write_l1_table(s, index, n);
|
||||
}
|
||||
|
||||
/* Called either from qed_check or with table_lock held. */
|
||||
/* Called with table_lock held. */
|
||||
int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
|
||||
{
|
||||
int ret;
|
||||
@@ -191,7 +183,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
|
||||
return qed_read_l2_table(s, request, offset);
|
||||
}
|
||||
|
||||
/* Called either from qed_check or with table_lock held. */
|
||||
/* Called with table_lock held. */
|
||||
int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
|
||||
unsigned int index, unsigned int n, bool flush)
|
||||
{
|
||||
|
||||
66
block/qed.c
66
block/qed.c
@@ -381,8 +381,9 @@ static void bdrv_qed_init_state(BlockDriverState *bs)
|
||||
qemu_co_queue_init(&s->allocating_write_reqs);
|
||||
}
|
||||
|
||||
static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
/* Called with table_lock held. */
|
||||
static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
QEDHeader le_header;
|
||||
@@ -513,9 +514,35 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct QEDOpenCo {
|
||||
BlockDriverState *bs;
|
||||
QDict *options;
|
||||
int flags;
|
||||
Error **errp;
|
||||
int ret;
|
||||
} QEDOpenCo;
|
||||
|
||||
static void coroutine_fn bdrv_qed_open_entry(void *opaque)
|
||||
{
|
||||
QEDOpenCo *qoc = opaque;
|
||||
BDRVQEDState *s = qoc->bs->opaque;
|
||||
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
qoc->ret = bdrv_qed_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
}
|
||||
|
||||
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QEDOpenCo qoc = {
|
||||
.bs = bs,
|
||||
.options = options,
|
||||
.flags = flags,
|
||||
.errp = errp,
|
||||
.ret = -EINPROGRESS
|
||||
};
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
@@ -523,7 +550,14 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
bdrv_qed_init_state(bs);
|
||||
return bdrv_qed_do_open(bs, options, flags, errp);
|
||||
if (qemu_in_coroutine()) {
|
||||
bdrv_qed_open_entry(&qoc);
|
||||
} else {
|
||||
qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc));
|
||||
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
|
||||
}
|
||||
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
|
||||
return qoc.ret;
|
||||
}
|
||||
|
||||
static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
@@ -1487,7 +1521,8 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
@@ -1496,13 +1531,9 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
bdrv_qed_close(bs);
|
||||
|
||||
bdrv_qed_init_state(bs);
|
||||
if (qemu_in_coroutine()) {
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
}
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
|
||||
if (qemu_in_coroutine()) {
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
}
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "Could not reopen qed layer: ");
|
||||
@@ -1513,12 +1544,17 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
static int bdrv_qed_co_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
return qed_check(s, result, !!fix);
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
ret = qed_check(s, result, !!fix);
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QemuOptsList qed_create_opts = {
|
||||
@@ -1577,8 +1613,8 @@ static BlockDriver bdrv_qed = {
|
||||
.bdrv_get_info = bdrv_qed_get_info,
|
||||
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
|
||||
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
|
||||
.bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
|
||||
.bdrv_check = bdrv_qed_check,
|
||||
.bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache,
|
||||
.bdrv_co_check = bdrv_qed_co_check,
|
||||
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
|
||||
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
|
||||
.bdrv_co_drain_begin = bdrv_qed_co_drain_begin,
|
||||
|
||||
405
block/rbd.c
405
block/rbd.c
@@ -24,6 +24,8 @@
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
|
||||
/*
|
||||
* When specifying the image filename use:
|
||||
@@ -101,6 +103,11 @@ typedef struct BDRVRBDState {
|
||||
char *snap;
|
||||
} BDRVRBDState;
|
||||
|
||||
static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
||||
BlockdevOptionsRbd *opts, bool cache,
|
||||
const char *keypairs, const char *secretid,
|
||||
Error **errp);
|
||||
|
||||
static char *qemu_rbd_next_tok(char *src, char delim, char **p)
|
||||
{
|
||||
char *end;
|
||||
@@ -268,13 +275,14 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
|
||||
key = qstring_get_str(name);
|
||||
|
||||
ret = rados_conf_set(cluster, key, qstring_get_str(value));
|
||||
QDECREF(name);
|
||||
QDECREF(value);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "invalid conf option %s", key);
|
||||
QDECREF(name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
QDECREF(name);
|
||||
}
|
||||
|
||||
QDECREF(keypairs);
|
||||
@@ -325,66 +333,92 @@ static QemuOptsList runtime_opts = {
|
||||
/*
|
||||
* server.* extracted manually, see qemu_rbd_mon_host()
|
||||
*/
|
||||
{
|
||||
.name = "password-secret",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "ID of secret providing the password",
|
||||
},
|
||||
|
||||
/*
|
||||
* Keys for qemu_rbd_parse_filename(), not in the QAPI schema
|
||||
*/
|
||||
{
|
||||
/*
|
||||
* HACK: name starts with '=' so that qemu_opts_parse()
|
||||
* can't set it
|
||||
*/
|
||||
.name = "=keyvalue-pairs",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Legacy rados key/value option parameters",
|
||||
},
|
||||
{
|
||||
.name = "filename",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
/* FIXME Deprecate and remove keypairs or make it available in QMP.
|
||||
* password_secret should eventually be configurable in opts->location. Support
|
||||
* for it in .bdrv_open will make it work here as well. */
|
||||
static int qemu_rbd_do_create(BlockdevCreateOptions *options,
|
||||
const char *keypairs, const char *password_secret,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptionsRbd *opts = &options->u.rbd;
|
||||
rados_t cluster;
|
||||
rados_ioctx_t io_ctx;
|
||||
int obj_order = 0;
|
||||
int ret;
|
||||
|
||||
assert(options->driver == BLOCKDEV_DRIVER_RBD);
|
||||
if (opts->location->has_snapshot) {
|
||||
error_setg(errp, "Can't use snapshot name for image creation");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (opts->has_cluster_size) {
|
||||
int64_t objsize = opts->cluster_size;
|
||||
if ((objsize - 1) & objsize) { /* not a power of 2? */
|
||||
error_setg(errp, "obj size needs to be power of 2");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (objsize < 4096) {
|
||||
error_setg(errp, "obj size too small");
|
||||
return -EINVAL;
|
||||
}
|
||||
obj_order = ctz32(objsize);
|
||||
}
|
||||
|
||||
ret = qemu_rbd_connect(&cluster, &io_ctx, opts->location, false, keypairs,
|
||||
password_secret, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rbd_create(io_ctx, opts->location->image, opts->size, &obj_order);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "error rbd create");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
rados_ioctx_destroy(io_ctx);
|
||||
rados_shutdown(cluster);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
return qemu_rbd_do_create(options, NULL, NULL, errp);
|
||||
}
|
||||
|
||||
static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options;
|
||||
BlockdevCreateOptionsRbd *rbd_opts;
|
||||
BlockdevOptionsRbd *loc;
|
||||
Error *local_err = NULL;
|
||||
int64_t bytes = 0;
|
||||
int64_t objsize;
|
||||
int obj_order = 0;
|
||||
const char *pool, *image_name, *conf, *user, *keypairs;
|
||||
const char *secretid;
|
||||
rados_t cluster;
|
||||
rados_ioctx_t io_ctx;
|
||||
const char *keypairs, *password_secret;
|
||||
QDict *options = NULL;
|
||||
int ret = 0;
|
||||
|
||||
secretid = qemu_opt_get(opts, "password-secret");
|
||||
create_options = g_new0(BlockdevCreateOptions, 1);
|
||||
create_options->driver = BLOCKDEV_DRIVER_RBD;
|
||||
rbd_opts = &create_options->u.rbd;
|
||||
|
||||
rbd_opts->location = g_new0(BlockdevOptionsRbd, 1);
|
||||
|
||||
password_secret = qemu_opt_get(opts, "password-secret");
|
||||
|
||||
/* Read out options */
|
||||
bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0);
|
||||
if (objsize) {
|
||||
if ((objsize - 1) & objsize) { /* not a power of 2? */
|
||||
error_setg(errp, "obj size needs to be power of 2");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
if (objsize < 4096) {
|
||||
error_setg(errp, "obj size too small");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
obj_order = ctz32(objsize);
|
||||
}
|
||||
rbd_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
rbd_opts->cluster_size = qemu_opt_get_size_del(opts,
|
||||
BLOCK_OPT_CLUSTER_SIZE, 0);
|
||||
rbd_opts->has_cluster_size = (rbd_opts->cluster_size != 0);
|
||||
|
||||
options = qdict_new();
|
||||
qemu_rbd_parse_filename(filename, options, &local_err);
|
||||
@@ -400,61 +434,23 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
|
||||
* or blockdev_add, its members are typed according to the QAPI
|
||||
* schema, but when they come from -drive, they're all QString.
|
||||
*/
|
||||
pool = qdict_get_try_str(options, "pool");
|
||||
conf = qdict_get_try_str(options, "conf");
|
||||
user = qdict_get_try_str(options, "user");
|
||||
image_name = qdict_get_try_str(options, "image");
|
||||
keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
|
||||
loc = rbd_opts->location;
|
||||
loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
|
||||
loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
|
||||
loc->has_conf = !!loc->conf;
|
||||
loc->user = g_strdup(qdict_get_try_str(options, "user"));
|
||||
loc->has_user = !!loc->user;
|
||||
loc->image = g_strdup(qdict_get_try_str(options, "image"));
|
||||
keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
|
||||
|
||||
ret = rados_create(&cluster, user);
|
||||
ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "error initializing");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* try default location when conf=NULL, but ignore failure */
|
||||
ret = rados_conf_read_file(cluster, conf);
|
||||
if (conf && ret < 0) {
|
||||
error_setg_errno(errp, -ret, "error reading conf file %s", conf);
|
||||
ret = -EIO;
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
ret = qemu_rbd_set_keypairs(cluster, keypairs, errp);
|
||||
if (ret < 0) {
|
||||
ret = -EIO;
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
|
||||
ret = -EIO;
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
ret = rados_connect(cluster);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "error connecting");
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
ret = rados_ioctx_create(cluster, pool, &io_ctx);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "error opening pool %s", pool);
|
||||
goto shutdown;
|
||||
}
|
||||
|
||||
ret = rbd_create(io_ctx, image_name, bytes, &obj_order);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "error rbd create");
|
||||
}
|
||||
|
||||
rados_ioctx_destroy(io_ctx);
|
||||
|
||||
shutdown:
|
||||
rados_shutdown(cluster);
|
||||
|
||||
exit:
|
||||
QDECREF(options);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -505,131 +501,83 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static char *qemu_rbd_mon_host(QDict *options, Error **errp)
|
||||
static char *qemu_rbd_mon_host(BlockdevOptionsRbd *opts, Error **errp)
|
||||
{
|
||||
const char **vals = g_new(const char *, qdict_size(options) + 1);
|
||||
char keybuf[32];
|
||||
const char **vals;
|
||||
const char *host, *port;
|
||||
char *rados_str;
|
||||
int i;
|
||||
InetSocketAddressBaseList *p;
|
||||
int i, cnt;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
sprintf(keybuf, "server.%d.host", i);
|
||||
host = qdict_get_try_str(options, keybuf);
|
||||
qdict_del(options, keybuf);
|
||||
sprintf(keybuf, "server.%d.port", i);
|
||||
port = qdict_get_try_str(options, keybuf);
|
||||
qdict_del(options, keybuf);
|
||||
if (!host && !port) {
|
||||
break;
|
||||
}
|
||||
if (!host) {
|
||||
error_setg(errp, "Parameter server.%d.host is missing", i);
|
||||
rados_str = NULL;
|
||||
goto out;
|
||||
}
|
||||
if (!opts->has_server) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (cnt = 0, p = opts->server; p; p = p->next) {
|
||||
cnt++;
|
||||
}
|
||||
|
||||
vals = g_new(const char *, cnt + 1);
|
||||
|
||||
for (i = 0, p = opts->server; p; p = p->next, i++) {
|
||||
host = p->value->host;
|
||||
port = p->value->port;
|
||||
|
||||
if (strchr(host, ':')) {
|
||||
vals[i] = port ? g_strdup_printf("[%s]:%s", host, port)
|
||||
: g_strdup_printf("[%s]", host);
|
||||
vals[i] = g_strdup_printf("[%s]:%s", host, port);
|
||||
} else {
|
||||
vals[i] = port ? g_strdup_printf("%s:%s", host, port)
|
||||
: g_strdup(host);
|
||||
vals[i] = g_strdup_printf("%s:%s", host, port);
|
||||
}
|
||||
}
|
||||
vals[i] = NULL;
|
||||
|
||||
rados_str = i ? g_strjoinv(";", (char **)vals) : NULL;
|
||||
out:
|
||||
g_strfreev((char **)vals);
|
||||
return rados_str;
|
||||
}
|
||||
|
||||
static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
||||
BlockdevOptionsRbd *opts, bool cache,
|
||||
const char *keypairs, const char *secretid,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
const char *pool, *snap, *conf, *user, *image_name, *keypairs;
|
||||
const char *secretid, *filename;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
char *mon_host = NULL;
|
||||
Error *local_err = NULL;
|
||||
int r;
|
||||
|
||||
/* If we are given a filename, parse the filename, with precedence given to
|
||||
* filename encoded options */
|
||||
filename = qdict_get_try_str(options, "filename");
|
||||
if (filename) {
|
||||
warn_report("'filename' option specified. "
|
||||
"This is an unsupported option, and may be deprecated "
|
||||
"in the future");
|
||||
qemu_rbd_parse_filename(filename, options, &local_err);
|
||||
if (local_err) {
|
||||
r = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
mon_host = qemu_rbd_mon_host(opts, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
r = -EINVAL;
|
||||
goto failed_opts;
|
||||
}
|
||||
|
||||
mon_host = qemu_rbd_mon_host(options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
r = -EINVAL;
|
||||
goto failed_opts;
|
||||
}
|
||||
|
||||
secretid = qemu_opt_get(opts, "password-secret");
|
||||
|
||||
pool = qemu_opt_get(opts, "pool");
|
||||
conf = qemu_opt_get(opts, "conf");
|
||||
snap = qemu_opt_get(opts, "snapshot");
|
||||
user = qemu_opt_get(opts, "user");
|
||||
image_name = qemu_opt_get(opts, "image");
|
||||
keypairs = qemu_opt_get(opts, "=keyvalue-pairs");
|
||||
|
||||
if (!pool || !image_name) {
|
||||
error_setg(errp, "Parameters 'pool' and 'image' are required");
|
||||
r = -EINVAL;
|
||||
goto failed_opts;
|
||||
}
|
||||
|
||||
r = rados_create(&s->cluster, user);
|
||||
r = rados_create(cluster, opts->user);
|
||||
if (r < 0) {
|
||||
error_setg_errno(errp, -r, "error initializing");
|
||||
goto failed_opts;
|
||||
}
|
||||
|
||||
s->snap = g_strdup(snap);
|
||||
s->image_name = g_strdup(image_name);
|
||||
|
||||
/* try default location when conf=NULL, but ignore failure */
|
||||
r = rados_conf_read_file(s->cluster, conf);
|
||||
if (conf && r < 0) {
|
||||
error_setg_errno(errp, -r, "error reading conf file %s", conf);
|
||||
r = rados_conf_read_file(*cluster, opts->conf);
|
||||
if (opts->has_conf && r < 0) {
|
||||
error_setg_errno(errp, -r, "error reading conf file %s", opts->conf);
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
r = qemu_rbd_set_keypairs(s->cluster, keypairs, errp);
|
||||
r = qemu_rbd_set_keypairs(*cluster, keypairs, errp);
|
||||
if (r < 0) {
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
if (mon_host) {
|
||||
r = rados_conf_set(s->cluster, "mon_host", mon_host);
|
||||
r = rados_conf_set(*cluster, "mon_host", mon_host);
|
||||
if (r < 0) {
|
||||
goto failed_shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
|
||||
if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
|
||||
r = -EIO;
|
||||
goto failed_shutdown;
|
||||
}
|
||||
@@ -641,24 +589,97 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
* librbd defaults to no caching. If write through caching cannot
|
||||
* be set up, fall back to no caching.
|
||||
*/
|
||||
if (flags & BDRV_O_NOCACHE) {
|
||||
rados_conf_set(s->cluster, "rbd_cache", "false");
|
||||
if (cache) {
|
||||
rados_conf_set(*cluster, "rbd_cache", "true");
|
||||
} else {
|
||||
rados_conf_set(s->cluster, "rbd_cache", "true");
|
||||
rados_conf_set(*cluster, "rbd_cache", "false");
|
||||
}
|
||||
|
||||
r = rados_connect(s->cluster);
|
||||
r = rados_connect(*cluster);
|
||||
if (r < 0) {
|
||||
error_setg_errno(errp, -r, "error connecting");
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
|
||||
r = rados_ioctx_create(*cluster, opts->pool, io_ctx);
|
||||
if (r < 0) {
|
||||
error_setg_errno(errp, -r, "error opening pool %s", pool);
|
||||
error_setg_errno(errp, -r, "error opening pool %s", opts->pool);
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed_shutdown:
|
||||
rados_shutdown(*cluster);
|
||||
failed_opts:
|
||||
g_free(mon_host);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
BlockdevOptionsRbd *opts = NULL;
|
||||
Visitor *v;
|
||||
QObject *crumpled = NULL;
|
||||
Error *local_err = NULL;
|
||||
const char *filename;
|
||||
char *keypairs, *secretid;
|
||||
int r;
|
||||
|
||||
/* If we are given a filename, parse the filename, with precedence given to
|
||||
* filename encoded options */
|
||||
filename = qdict_get_try_str(options, "filename");
|
||||
if (filename) {
|
||||
warn_report("'filename' option specified. "
|
||||
"This is an unsupported option, and may be deprecated "
|
||||
"in the future");
|
||||
qemu_rbd_parse_filename(filename, options, &local_err);
|
||||
qdict_del(options, "filename");
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
|
||||
if (keypairs) {
|
||||
qdict_del(options, "=keyvalue-pairs");
|
||||
}
|
||||
|
||||
secretid = g_strdup(qdict_get_try_str(options, "password-secret"));
|
||||
if (secretid) {
|
||||
qdict_del(options, "password-secret");
|
||||
}
|
||||
|
||||
/* Convert the remaining options into a QAPI object */
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts,
|
||||
!(flags & BDRV_O_NOCACHE), keypairs, secretid, errp);
|
||||
if (r < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->snap = g_strdup(opts->snapshot);
|
||||
s->image_name = g_strdup(opts->image);
|
||||
|
||||
/* rbd_open is always r/w */
|
||||
r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap);
|
||||
if (r < 0) {
|
||||
@@ -683,19 +704,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
r = 0;
|
||||
goto out;
|
||||
|
||||
failed_open:
|
||||
rados_ioctx_destroy(s->io_ctx);
|
||||
failed_shutdown:
|
||||
rados_shutdown(s->cluster);
|
||||
g_free(s->snap);
|
||||
g_free(s->image_name);
|
||||
failed_opts:
|
||||
qemu_opts_del(opts);
|
||||
g_free(mon_host);
|
||||
exit:
|
||||
rados_shutdown(s->cluster);
|
||||
out:
|
||||
qapi_free_BlockdevOptionsRbd(opts);
|
||||
g_free(keypairs);
|
||||
g_free(secretid);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1093,8 +1113,8 @@ static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs,
|
||||
#endif
|
||||
|
||||
#ifdef LIBRBD_SUPPORTS_INVALIDATE
|
||||
static void qemu_rbd_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
int r = rbd_invalidate_cache(s->image);
|
||||
@@ -1134,6 +1154,7 @@ static BlockDriver bdrv_rbd = {
|
||||
.bdrv_file_open = qemu_rbd_open,
|
||||
.bdrv_close = qemu_rbd_close,
|
||||
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
|
||||
.bdrv_co_create = qemu_rbd_co_create,
|
||||
.bdrv_co_create_opts = qemu_rbd_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_get_info = qemu_rbd_getinfo,
|
||||
@@ -1160,7 +1181,7 @@ static BlockDriver bdrv_rbd = {
|
||||
.bdrv_snapshot_list = qemu_rbd_snap_list,
|
||||
.bdrv_snapshot_goto = qemu_rbd_snap_rollback,
|
||||
#ifdef LIBRBD_SUPPORTS_INVALIDATE
|
||||
.bdrv_invalidate_cache = qemu_rbd_invalidate_cache,
|
||||
.bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
323
block/sheepdog.c
323
block/sheepdog.c
@@ -15,8 +15,10 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qobject-output-visitor.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
@@ -533,23 +535,6 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
|
||||
qemu_co_mutex_unlock(&s->queue_lock);
|
||||
}
|
||||
|
||||
static SocketAddress *sd_socket_address(const char *path,
|
||||
const char *host, const char *port)
|
||||
{
|
||||
SocketAddress *addr = g_new0(SocketAddress, 1);
|
||||
|
||||
if (path) {
|
||||
addr->type = SOCKET_ADDRESS_TYPE_UNIX;
|
||||
addr->u.q_unix.path = g_strdup(path);
|
||||
} else {
|
||||
addr->type = SOCKET_ADDRESS_TYPE_INET;
|
||||
addr->u.inet.host = g_strdup(host ?: SD_DEFAULT_ADDR);
|
||||
addr->u.inet.port = g_strdup(port ?: stringify(SD_DEFAULT_PORT));
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static SocketAddress *sd_server_config(QDict *options, Error **errp)
|
||||
{
|
||||
QDict *server = NULL;
|
||||
@@ -1882,6 +1867,86 @@ out_with_err_set:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
Visitor *v;
|
||||
QObject *obj = NULL;
|
||||
QDict *qdict;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
v = qobject_output_visitor_new(&obj);
|
||||
visit_type_BlockdevOptionsSheepdog(v, NULL, &location, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
qobject_decref(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qdict = qobject_to_qdict(obj);
|
||||
qdict_flatten(qdict);
|
||||
|
||||
qdict_put_str(qdict, "driver", "sheepdog");
|
||||
|
||||
bs = bdrv_open(NULL, NULL, qdict, BDRV_O_PROTOCOL | BDRV_O_RDWR, errp);
|
||||
if (bs == NULL) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = sd_prealloc(bs, 0, size, errp);
|
||||
fail:
|
||||
bdrv_unref(bs);
|
||||
QDECREF(qdict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_redundancy(BDRVSheepdogState *s, SheepdogRedundancy *opt)
|
||||
{
|
||||
struct SheepdogInode *inode = &s->inode;
|
||||
|
||||
switch (opt->type) {
|
||||
case SHEEPDOG_REDUNDANCY_TYPE_FULL:
|
||||
if (opt->u.full.copies > SD_MAX_COPIES || opt->u.full.copies < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
inode->copy_policy = 0;
|
||||
inode->nr_copies = opt->u.full.copies;
|
||||
return 0;
|
||||
|
||||
case SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED:
|
||||
{
|
||||
int64_t copy = opt->u.erasure_coded.data_strips;
|
||||
int64_t parity = opt->u.erasure_coded.parity_strips;
|
||||
|
||||
if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (parity >= SD_EC_MAX_STRIP || parity < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 4 bits for parity and 4 bits for data.
|
||||
* We have to compress upper data bits because it can't represent 16
|
||||
*/
|
||||
inode->copy_policy = ((copy / 2) << 4) + parity;
|
||||
inode->nr_copies = copy + parity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sheepdog support two kinds of redundancy, full replication and erasure
|
||||
* coding.
|
||||
@@ -1892,60 +1957,61 @@ out_with_err_set:
|
||||
* # create a erasure coded vdi with x data strips and y parity strips
|
||||
* -o redundancy=x:y (x must be one of {2,4,8,16} and 1 <= y < SD_EC_MAX_STRIP)
|
||||
*/
|
||||
static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
|
||||
static SheepdogRedundancy *parse_redundancy_str(const char *opt)
|
||||
{
|
||||
struct SheepdogInode *inode = &s->inode;
|
||||
SheepdogRedundancy *redundancy;
|
||||
const char *n1, *n2;
|
||||
long copy, parity;
|
||||
char p[10];
|
||||
int ret;
|
||||
|
||||
pstrcpy(p, sizeof(p), opt);
|
||||
n1 = strtok(p, ":");
|
||||
n2 = strtok(NULL, ":");
|
||||
|
||||
if (!n1) {
|
||||
return -EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy = strtol(n1, NULL, 10);
|
||||
/* FIXME fix error checking by switching to qemu_strtol() */
|
||||
if (copy > SD_MAX_COPIES || copy < 1) {
|
||||
return -EINVAL;
|
||||
ret = qemu_strtol(n1, NULL, 10, ©);
|
||||
if (ret < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
redundancy = g_new0(SheepdogRedundancy, 1);
|
||||
if (!n2) {
|
||||
inode->copy_policy = 0;
|
||||
inode->nr_copies = copy;
|
||||
return 0;
|
||||
*redundancy = (SheepdogRedundancy) {
|
||||
.type = SHEEPDOG_REDUNDANCY_TYPE_FULL,
|
||||
.u.full.copies = copy,
|
||||
};
|
||||
} else {
|
||||
ret = qemu_strtol(n2, NULL, 10, &parity);
|
||||
if (ret < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*redundancy = (SheepdogRedundancy) {
|
||||
.type = SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED,
|
||||
.u.erasure_coded = {
|
||||
.data_strips = copy,
|
||||
.parity_strips = parity,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
parity = strtol(n2, NULL, 10);
|
||||
/* FIXME fix error checking by switching to qemu_strtol() */
|
||||
if (parity >= SD_EC_MAX_STRIP || parity < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 4 bits for parity and 4 bits for data.
|
||||
* We have to compress upper data bits because it can't represent 16
|
||||
*/
|
||||
inode->copy_policy = ((copy / 2) << 4) + parity;
|
||||
inode->nr_copies = copy + parity;
|
||||
|
||||
return 0;
|
||||
return redundancy;
|
||||
}
|
||||
|
||||
static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
|
||||
static int parse_block_size_shift(BDRVSheepdogState *s,
|
||||
BlockdevCreateOptionsSheepdog *opts)
|
||||
{
|
||||
struct SheepdogInode *inode = &s->inode;
|
||||
uint64_t object_size;
|
||||
int obj_order;
|
||||
|
||||
object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0);
|
||||
if (object_size) {
|
||||
if (opts->has_object_size) {
|
||||
object_size = opts->object_size;
|
||||
|
||||
if ((object_size - 1) & object_size) { /* not a power of 2? */
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1959,57 +2025,55 @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
static int sd_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
BlockdevCreateOptionsSheepdog *opts = &options->u.sheepdog;
|
||||
int ret = 0;
|
||||
uint32_t vid = 0;
|
||||
char *backing_file = NULL;
|
||||
char *buf = NULL;
|
||||
BDRVSheepdogState *s;
|
||||
SheepdogConfig cfg;
|
||||
uint64_t max_vdi_size;
|
||||
bool prealloc = false;
|
||||
|
||||
assert(options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
|
||||
|
||||
s = g_new0(BDRVSheepdogState, 1);
|
||||
|
||||
if (strstr(filename, "://")) {
|
||||
sd_parse_uri(&cfg, filename, &err);
|
||||
} else {
|
||||
parse_vdiname(&cfg, filename, &err);
|
||||
}
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
/* Steal SocketAddress from QAPI, set NULL to prevent double free */
|
||||
s->addr = opts->location->server;
|
||||
opts->location->server = NULL;
|
||||
|
||||
if (strlen(opts->location->vdi) >= sizeof(s->name)) {
|
||||
error_setg(errp, "'vdi' string too long");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
pstrcpy(s->name, sizeof(s->name), opts->location->vdi);
|
||||
|
||||
buf = cfg.port ? g_strdup_printf("%d", cfg.port) : NULL;
|
||||
s->addr = sd_socket_address(cfg.path, cfg.host, buf);
|
||||
g_free(buf);
|
||||
strcpy(s->name, cfg.vdi);
|
||||
sd_config_done(&cfg);
|
||||
s->inode.vdi_size = opts->size;
|
||||
backing_file = opts->backing_file;
|
||||
|
||||
s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
if (!buf || !strcmp(buf, "off")) {
|
||||
if (!opts->has_preallocation) {
|
||||
opts->preallocation = PREALLOC_MODE_OFF;
|
||||
}
|
||||
switch (opts->preallocation) {
|
||||
case PREALLOC_MODE_OFF:
|
||||
prealloc = false;
|
||||
} else if (!strcmp(buf, "full")) {
|
||||
break;
|
||||
case PREALLOC_MODE_FULL:
|
||||
prealloc = true;
|
||||
} else {
|
||||
error_setg(errp, "Invalid preallocation mode: '%s'", buf);
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Preallocation mode not supported for Sheepdog");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
g_free(buf);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
|
||||
if (buf) {
|
||||
ret = parse_redundancy(s, buf);
|
||||
if (opts->has_redundancy) {
|
||||
ret = parse_redundancy(s, opts->redundancy);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Invalid redundancy mode: '%s'", buf);
|
||||
error_setg(errp, "Invalid redundancy mode");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -2021,20 +2085,20 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (backing_file) {
|
||||
if (opts->has_backing_file) {
|
||||
BlockBackend *blk;
|
||||
BDRVSheepdogState *base;
|
||||
BlockDriver *drv;
|
||||
|
||||
/* Currently, only Sheepdog backing image is supported. */
|
||||
drv = bdrv_find_protocol(backing_file, true, NULL);
|
||||
drv = bdrv_find_protocol(opts->backing_file, true, NULL);
|
||||
if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
|
||||
error_setg(errp, "backing_file must be a sheepdog image");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk = blk_new_open(backing_file, NULL, NULL,
|
||||
blk = blk_new_open(opts->backing_file, NULL, NULL,
|
||||
BDRV_O_PROTOCOL, errp);
|
||||
if (blk == NULL) {
|
||||
ret = -EIO;
|
||||
@@ -2102,28 +2166,96 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
}
|
||||
|
||||
if (prealloc) {
|
||||
BlockDriverState *bs;
|
||||
QDict *opts;
|
||||
|
||||
opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", "sheepdog");
|
||||
bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR,
|
||||
errp);
|
||||
if (!bs) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp);
|
||||
|
||||
bdrv_unref(bs);
|
||||
ret = sd_create_prealloc(opts->location, opts->size, errp);
|
||||
}
|
||||
out:
|
||||
g_free(backing_file);
|
||||
g_free(buf);
|
||||
g_free(s->addr);
|
||||
g_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict, *location_qdict;
|
||||
QObject *crumpled;
|
||||
Visitor *v;
|
||||
const char *redundancy;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
redundancy = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
|
||||
|
||||
qdict = qemu_opts_to_qdict(opts, NULL);
|
||||
qdict_put_str(qdict, "driver", "sheepdog");
|
||||
|
||||
location_qdict = qdict_new();
|
||||
qdict_put(qdict, "location", location_qdict);
|
||||
|
||||
sd_parse_filename(filename, location_qdict, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qdict_flatten(qdict);
|
||||
|
||||
/* Change legacy command line options into QMP ones */
|
||||
static const QDictRenames opt_renames[] = {
|
||||
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
|
||||
{ BLOCK_OPT_OBJECT_SIZE, "object-size" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Get the QAPI object */
|
||||
crumpled = qdict_crumple(qdict, errp);
|
||||
if (crumpled == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
|
||||
create_options->u.sheepdog.size =
|
||||
ROUND_UP(create_options->u.sheepdog.size, BDRV_SECTOR_SIZE);
|
||||
|
||||
if (redundancy) {
|
||||
create_options->u.sheepdog.has_redundancy = true;
|
||||
create_options->u.sheepdog.redundancy =
|
||||
parse_redundancy_str(redundancy);
|
||||
if (create_options->u.sheepdog.redundancy == NULL) {
|
||||
error_setg(errp, "Invalid redundancy mode");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = sd_co_create(create_options, errp);
|
||||
fail:
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
QDECREF(qdict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sd_close(BlockDriverState *bs)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
@@ -3103,6 +3235,7 @@ static BlockDriver bdrv_sheepdog = {
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_co_create = sd_co_create,
|
||||
.bdrv_co_create_opts = sd_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
@@ -3139,6 +3272,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_co_create = sd_co_create,
|
||||
.bdrv_co_create_opts = sd_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
@@ -3175,6 +3309,7 @@ static BlockDriver bdrv_sheepdog_unix = {
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_co_create = sd_co_create,
|
||||
.bdrv_co_create_opts = sd_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
|
||||
298
block/ssh.c
298
block/ssh.c
@@ -35,6 +35,7 @@
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
@@ -430,31 +431,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
|
||||
}
|
||||
|
||||
static int check_host_key(BDRVSSHState *s, const char *host, int port,
|
||||
const char *host_key_check, Error **errp)
|
||||
SshHostKeyCheck *hkc, Error **errp)
|
||||
{
|
||||
/* host_key_check=no */
|
||||
if (strcmp(host_key_check, "no") == 0) {
|
||||
SshHostKeyCheckMode mode;
|
||||
|
||||
if (hkc) {
|
||||
mode = hkc->mode;
|
||||
} else {
|
||||
mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case SSH_HOST_KEY_CHECK_MODE_NONE:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* host_key_check=md5:xx:yy:zz:... */
|
||||
if (strncmp(host_key_check, "md5:", 4) == 0) {
|
||||
return check_host_key_hash(s, &host_key_check[4],
|
||||
LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
|
||||
}
|
||||
|
||||
/* host_key_check=sha1:xx:yy:zz:... */
|
||||
if (strncmp(host_key_check, "sha1:", 5) == 0) {
|
||||
return check_host_key_hash(s, &host_key_check[5],
|
||||
LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
|
||||
}
|
||||
|
||||
/* host_key_check=yes */
|
||||
if (strcmp(host_key_check, "yes") == 0) {
|
||||
case SSH_HOST_KEY_CHECK_MODE_HASH:
|
||||
if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
|
||||
return check_host_key_hash(s, hkc->u.hash.hash,
|
||||
LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
|
||||
} else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
|
||||
return check_host_key_hash(s, hkc->u.hash.hash,
|
||||
LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
|
||||
}
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
|
||||
return check_host_key_knownhosts(s, host, port, errp);
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -543,16 +548,6 @@ static QemuOptsList ssh_runtime_opts = {
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "Port to connect to",
|
||||
},
|
||||
{
|
||||
.name = "path",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Path of the image on the host",
|
||||
},
|
||||
{
|
||||
.name = "user",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "User as which to connect",
|
||||
},
|
||||
{
|
||||
.name = "host_key_check",
|
||||
.type = QEMU_OPT_STRING,
|
||||
@@ -562,12 +557,13 @@ static QemuOptsList ssh_runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static bool ssh_process_legacy_socket_options(QDict *output_opts,
|
||||
QemuOpts *legacy_opts,
|
||||
Error **errp)
|
||||
static bool ssh_process_legacy_options(QDict *output_opts,
|
||||
QemuOpts *legacy_opts,
|
||||
Error **errp)
|
||||
{
|
||||
const char *host = qemu_opt_get(legacy_opts, "host");
|
||||
const char *port = qemu_opt_get(legacy_opts, "port");
|
||||
const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check");
|
||||
|
||||
if (!host && port) {
|
||||
error_setg(errp, "port may not be used without host");
|
||||
@@ -579,26 +575,56 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts,
|
||||
qdict_put_str(output_opts, "server.port", port ?: stringify(22));
|
||||
}
|
||||
|
||||
if (host_key_check) {
|
||||
if (strcmp(host_key_check, "no") == 0) {
|
||||
qdict_put_str(output_opts, "host-key-check.mode", "none");
|
||||
} else if (strncmp(host_key_check, "md5:", 4) == 0) {
|
||||
qdict_put_str(output_opts, "host-key-check.mode", "hash");
|
||||
qdict_put_str(output_opts, "host-key-check.type", "md5");
|
||||
qdict_put_str(output_opts, "host-key-check.hash",
|
||||
&host_key_check[4]);
|
||||
} else if (strncmp(host_key_check, "sha1:", 5) == 0) {
|
||||
qdict_put_str(output_opts, "host-key-check.mode", "hash");
|
||||
qdict_put_str(output_opts, "host-key-check.type", "sha1");
|
||||
qdict_put_str(output_opts, "host-key-check.hash",
|
||||
&host_key_check[5]);
|
||||
} else if (strcmp(host_key_check, "yes") == 0) {
|
||||
qdict_put_str(output_opts, "host-key-check.mode", "known_hosts");
|
||||
} else {
|
||||
error_setg(errp, "unknown host_key_check setting (%s)",
|
||||
host_key_check);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static InetSocketAddress *ssh_config(QDict *options, Error **errp)
|
||||
static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
|
||||
{
|
||||
InetSocketAddress *inet = NULL;
|
||||
QDict *addr = NULL;
|
||||
QObject *crumpled_addr = NULL;
|
||||
Visitor *iv = NULL;
|
||||
Error *local_error = NULL;
|
||||
BlockdevOptionsSsh *result = NULL;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
QObject *crumpled;
|
||||
const QDictEntry *e;
|
||||
Visitor *v;
|
||||
|
||||
qdict_extract_subqdict(options, &addr, "server.");
|
||||
if (!qdict_size(addr)) {
|
||||
error_setg(errp, "SSH server address missing");
|
||||
goto out;
|
||||
/* Translate legacy options */
|
||||
opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
crumpled_addr = qdict_crumple(addr, errp);
|
||||
if (!crumpled_addr) {
|
||||
goto out;
|
||||
if (!ssh_process_legacy_options(options, opts, errp)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create the QAPI object */
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -609,51 +635,37 @@ static InetSocketAddress *ssh_config(QDict *options, Error **errp)
|
||||
* but when they come from -drive, they're all QString. The
|
||||
* visitor expects the former.
|
||||
*/
|
||||
iv = qobject_input_visitor_new(crumpled_addr);
|
||||
visit_type_InetSocketAddress(iv, NULL, &inet, &local_error);
|
||||
if (local_error) {
|
||||
error_propagate(errp, local_error);
|
||||
goto out;
|
||||
v = qobject_input_visitor_new(crumpled);
|
||||
visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
out:
|
||||
QDECREF(addr);
|
||||
qobject_decref(crumpled_addr);
|
||||
visit_free(iv);
|
||||
return inet;
|
||||
/* Remove the processed options from the QDict (the visitor processes
|
||||
* _all_ options in the QDict) */
|
||||
while ((e = qdict_first(options))) {
|
||||
qdict_del(options, e->key);
|
||||
}
|
||||
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
|
||||
int ssh_flags, int creat_mode, Error **errp)
|
||||
{
|
||||
int r, ret;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
const char *user, *path, *host_key_check;
|
||||
const char *user;
|
||||
long port = 0;
|
||||
|
||||
opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!ssh_process_legacy_socket_options(options, opts, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
path = qemu_opt_get(opts, "path");
|
||||
if (!path) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "No path was specified");
|
||||
goto err;
|
||||
}
|
||||
|
||||
user = qemu_opt_get(opts, "user");
|
||||
if (!user) {
|
||||
if (opts->has_user) {
|
||||
user = opts->user;
|
||||
} else {
|
||||
user = g_get_user_name();
|
||||
if (!user) {
|
||||
error_setg_errno(errp, errno, "Can't get user name");
|
||||
@@ -662,17 +674,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
}
|
||||
}
|
||||
|
||||
host_key_check = qemu_opt_get(opts, "host_key_check");
|
||||
if (!host_key_check) {
|
||||
host_key_check = "yes";
|
||||
}
|
||||
|
||||
/* Pop the config into our state object, Exit if invalid */
|
||||
s->inet = ssh_config(options, errp);
|
||||
if (!s->inet) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
s->inet = opts->server;
|
||||
opts->server = NULL;
|
||||
|
||||
if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
|
||||
error_setg(errp, "Use only numeric port value");
|
||||
@@ -707,8 +711,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
}
|
||||
|
||||
/* Check the remote host's key against known_hosts. */
|
||||
ret = check_host_key(s, s->inet->host, port, host_key_check,
|
||||
errp);
|
||||
ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
@@ -729,16 +732,16 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
|
||||
/* Open the remote file. */
|
||||
DPRINTF("opening file %s flags=0x%x creat_mode=0%o",
|
||||
path, ssh_flags, creat_mode);
|
||||
s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
|
||||
opts->path, ssh_flags, creat_mode);
|
||||
s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
|
||||
creat_mode);
|
||||
if (!s->sftp_handle) {
|
||||
session_error_setg(errp, s, "failed to open remote file '%s'", path);
|
||||
session_error_setg(errp, s, "failed to open remote file '%s'",
|
||||
opts->path);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
|
||||
if (r < 0) {
|
||||
sftp_error_setg(errp, s, "failed to read file attributes");
|
||||
@@ -764,8 +767,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||
}
|
||||
s->session = NULL;
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -773,6 +774,7 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVSSHState *s = bs->opaque;
|
||||
BlockdevOptionsSsh *opts;
|
||||
int ret;
|
||||
int ssh_flags;
|
||||
|
||||
@@ -783,8 +785,13 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
|
||||
ssh_flags |= LIBSSH2_FXF_WRITE;
|
||||
}
|
||||
|
||||
opts = ssh_parse_options(options, errp);
|
||||
if (opts == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Start up SSH. */
|
||||
ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
|
||||
ret = connect_to_ssh(s, opts, ssh_flags, 0, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
@@ -792,6 +799,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
|
||||
/* Go non-blocking. */
|
||||
libssh2_session_set_blocking(s->session, 0);
|
||||
|
||||
qapi_free_BlockdevOptionsSsh(opts);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@@ -800,6 +809,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
|
||||
}
|
||||
s->sock = -1;
|
||||
|
||||
qapi_free_BlockdevOptionsSsh(opts);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -843,51 +854,71 @@ static QemuOptsList ssh_create_opts = {
|
||||
}
|
||||
};
|
||||
|
||||
static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
static int ssh_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
{
|
||||
int r, ret;
|
||||
int64_t total_size = 0;
|
||||
QDict *uri_options = NULL;
|
||||
BlockdevCreateOptionsSsh *opts = &options->u.ssh;
|
||||
BDRVSSHState s;
|
||||
int ret;
|
||||
|
||||
assert(options->driver == BLOCKDEV_DRIVER_SSH);
|
||||
|
||||
ssh_state_init(&s);
|
||||
|
||||
/* Get desired file size. */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
DPRINTF("total_size=%" PRIi64, total_size);
|
||||
|
||||
uri_options = qdict_new();
|
||||
r = parse_uri(filename, uri_options, errp);
|
||||
if (r < 0) {
|
||||
ret = r;
|
||||
goto out;
|
||||
ret = connect_to_ssh(&s, opts->location,
|
||||
LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
|
||||
LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
|
||||
0644, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = connect_to_ssh(&s, uri_options,
|
||||
LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
|
||||
LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
|
||||
0644, errp);
|
||||
if (r < 0) {
|
||||
ret = r;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (total_size > 0) {
|
||||
ret = ssh_grow_file(&s, total_size, errp);
|
||||
if (opts->size > 0) {
|
||||
ret = ssh_grow_file(&s, opts->size, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
ssh_state_free(&s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options;
|
||||
BlockdevCreateOptionsSsh *ssh_opts;
|
||||
int ret;
|
||||
QDict *uri_options = NULL;
|
||||
|
||||
create_options = g_new0(BlockdevCreateOptions, 1);
|
||||
create_options->driver = BLOCKDEV_DRIVER_SSH;
|
||||
ssh_opts = &create_options->u.ssh;
|
||||
|
||||
/* Get desired file size. */
|
||||
ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
DPRINTF("total_size=%" PRIi64, ssh_opts->size);
|
||||
|
||||
uri_options = qdict_new();
|
||||
ret = parse_uri(filename, uri_options, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ssh_opts->location = ssh_parse_options(uri_options, errp);
|
||||
if (ssh_opts->location == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ssh_co_create(create_options, errp);
|
||||
|
||||
out:
|
||||
ssh_state_free(&s);
|
||||
if (uri_options != NULL) {
|
||||
QDECREF(uri_options);
|
||||
}
|
||||
QDECREF(uri_options);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1249,6 +1280,7 @@ static BlockDriver bdrv_ssh = {
|
||||
.instance_size = sizeof(BDRVSSHState),
|
||||
.bdrv_parse_filename = ssh_parse_filename,
|
||||
.bdrv_file_open = ssh_file_open,
|
||||
.bdrv_co_create = ssh_co_create,
|
||||
.bdrv_co_create_opts = ssh_co_create_opts,
|
||||
.bdrv_close = ssh_close,
|
||||
.bdrv_has_zero_init = ssh_has_zero_init,
|
||||
|
||||
@@ -263,8 +263,8 @@ static void vdi_header_print(VdiHeader *header)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
/* TODO: additional checks possible. */
|
||||
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
|
||||
@@ -908,7 +908,7 @@ static BlockDriver bdrv_vdi = {
|
||||
.bdrv_get_info = vdi_get_info,
|
||||
|
||||
.create_opts = &vdi_create_opts,
|
||||
.bdrv_check = vdi_check,
|
||||
.bdrv_co_check = vdi_co_check,
|
||||
};
|
||||
|
||||
static void bdrv_vdi_init(void)
|
||||
|
||||
@@ -1944,8 +1944,9 @@ exit:
|
||||
* r/w and any log has already been replayed, so there is nothing (currently)
|
||||
* for us to do here
|
||||
*/
|
||||
static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
static int coroutine_fn vhdx_co_check(BlockDriverState *bs,
|
||||
BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVVHDXState *s = bs->opaque;
|
||||
|
||||
@@ -2006,7 +2007,7 @@ static BlockDriver bdrv_vhdx = {
|
||||
.bdrv_co_writev = vhdx_co_writev,
|
||||
.bdrv_co_create_opts = vhdx_co_create_opts,
|
||||
.bdrv_get_info = vhdx_get_info,
|
||||
.bdrv_check = vhdx_check,
|
||||
.bdrv_co_check = vhdx_co_check,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
|
||||
.create_opts = &vhdx_create_opts,
|
||||
|
||||
@@ -2221,8 +2221,9 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
|
||||
return info;
|
||||
}
|
||||
|
||||
static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
static int coroutine_fn vmdk_co_check(BlockDriverState *bs,
|
||||
BdrvCheckResult *result,
|
||||
BdrvCheckMode fix)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
VmdkExtent *extent = NULL;
|
||||
@@ -2391,7 +2392,7 @@ static BlockDriver bdrv_vmdk = {
|
||||
.instance_size = sizeof(BDRVVmdkState),
|
||||
.bdrv_probe = vmdk_probe,
|
||||
.bdrv_open = vmdk_open,
|
||||
.bdrv_check = vmdk_check,
|
||||
.bdrv_co_check = vmdk_co_check,
|
||||
.bdrv_reopen_prepare = vmdk_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_preadv = vmdk_co_preadv,
|
||||
|
||||
@@ -707,6 +707,7 @@ static void tcp_chr_tls_init(Chardev *chr)
|
||||
qio_channel_tls_handshake(tioc,
|
||||
tcp_chr_tls_handshake,
|
||||
chr,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
@@ -871,7 +872,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
qio_channel_socket_connect_async(sioc, s->addr,
|
||||
qemu_chr_socket_connected,
|
||||
chr, NULL);
|
||||
chr, NULL, NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -955,7 +956,7 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
qio_channel_socket_connect_async(sioc, s->addr,
|
||||
qemu_chr_socket_connected,
|
||||
chr, NULL);
|
||||
chr, NULL, NULL);
|
||||
} else {
|
||||
if (s->is_listen) {
|
||||
char *name;
|
||||
|
||||
16
configure
vendored
16
configure
vendored
@@ -1692,6 +1692,7 @@ gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
|
||||
gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags"
|
||||
gcc_flags="-Wno-initializer-overrides -Wexpansion-to-defined $gcc_flags"
|
||||
gcc_flags="-Wno-string-plus-int $gcc_flags"
|
||||
gcc_flags="-Wno-error=address-of-packed-member $gcc_flags"
|
||||
# Note that we do not add -Werror to gcc_flags here, because that would
|
||||
# enable it for all configure tests. If a configure test failed due
|
||||
# to -Werror this would just silently disable some features,
|
||||
@@ -2874,6 +2875,7 @@ if test "$sdl" != "no" ; then
|
||||
int main( void ) { return SDL_Init (SDL_INIT_VIDEO); }
|
||||
EOF
|
||||
sdl_cflags=$($sdlconfig --cflags 2>/dev/null)
|
||||
sdl_cflags="$sdl_cflags -Wno-undef" # workaround 2.0.8 bug
|
||||
if test "$static" = "yes" ; then
|
||||
if $pkg_config $sdlname --exists; then
|
||||
sdl_libs=$($pkg_config $sdlname --static --libs 2>/dev/null)
|
||||
@@ -4860,7 +4862,6 @@ fi
|
||||
pragma_disable_unused_but_set=no
|
||||
cat > $TMPC << EOF
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
@@ -6797,6 +6798,16 @@ case "$target_name" in
|
||||
echo "TARGET_ABI32=y" >> $config_target_mak
|
||||
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
||||
;;
|
||||
riscv32)
|
||||
TARGET_BASE_ARCH=riscv
|
||||
TARGET_ABI_DIR=riscv
|
||||
mttcg=yes
|
||||
;;
|
||||
riscv64)
|
||||
TARGET_BASE_ARCH=riscv
|
||||
TARGET_ABI_DIR=riscv
|
||||
mttcg=yes
|
||||
;;
|
||||
sh4|sh4eb)
|
||||
TARGET_ARCH=sh4
|
||||
bflt="yes"
|
||||
@@ -6966,6 +6977,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
|
||||
ppc*)
|
||||
disas_config "PPC"
|
||||
;;
|
||||
riscv)
|
||||
disas_config "RISCV"
|
||||
;;
|
||||
s390*)
|
||||
disas_config "S390"
|
||||
;;
|
||||
|
||||
22
cpus.c
22
cpus.c
@@ -993,7 +993,7 @@ void cpu_synchronize_all_pre_loadvm(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int do_vm_stop(RunState state)
|
||||
static int do_vm_stop(RunState state, bool send_stop)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@@ -1002,7 +1002,9 @@ static int do_vm_stop(RunState state)
|
||||
pause_all_vcpus();
|
||||
runstate_set(state);
|
||||
vm_state_notify(0, state);
|
||||
qapi_event_send_stop(&error_abort);
|
||||
if (send_stop) {
|
||||
qapi_event_send_stop(&error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_drain_all();
|
||||
@@ -1012,6 +1014,14 @@ static int do_vm_stop(RunState state)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Special vm_stop() variant for terminating the process. Historically clients
|
||||
* did not expect a QMP STOP event and so we need to retain compatibility.
|
||||
*/
|
||||
int vm_shutdown(void)
|
||||
{
|
||||
return do_vm_stop(RUN_STATE_SHUTDOWN, false);
|
||||
}
|
||||
|
||||
static bool cpu_can_run(CPUState *cpu)
|
||||
{
|
||||
if (cpu->stop) {
|
||||
@@ -1994,7 +2004,7 @@ int vm_stop(RunState state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return do_vm_stop(state);
|
||||
return do_vm_stop(state, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2081,6 +2091,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
||||
#elif defined(TARGET_SPARC)
|
||||
SPARCCPU *sparc_cpu = SPARC_CPU(cpu);
|
||||
CPUSPARCState *env = &sparc_cpu->env;
|
||||
#elif defined(TARGET_RISCV)
|
||||
RISCVCPU *riscv_cpu = RISCV_CPU(cpu);
|
||||
CPURISCVState *env = &riscv_cpu->env;
|
||||
#elif defined(TARGET_MIPS)
|
||||
MIPSCPU *mips_cpu = MIPS_CPU(cpu);
|
||||
CPUMIPSState *env = &mips_cpu->env;
|
||||
@@ -2120,6 +2133,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
||||
#elif defined(TARGET_S390X)
|
||||
info->value->arch = CPU_INFO_ARCH_S390;
|
||||
info->value->u.s390.cpu_state = env->cpu_state;
|
||||
#elif defined(TARGET_RISCV)
|
||||
info->value->arch = CPU_INFO_ARCH_RISCV;
|
||||
info->value->u.riscv.pc = env->pc;
|
||||
#else
|
||||
info->value->arch = CPU_INFO_ARCH_OTHER;
|
||||
#endif
|
||||
|
||||
@@ -47,6 +47,7 @@ CONFIG_A9MPCORE=y
|
||||
CONFIG_A15MPCORE=y
|
||||
|
||||
CONFIG_ARM_V7M=y
|
||||
CONFIG_NETDUINO2=y
|
||||
|
||||
CONFIG_ARM_GIC=y
|
||||
CONFIG_ARM_GIC_KVM=$(CONFIG_KVM)
|
||||
@@ -109,6 +110,7 @@ CONFIG_TZ_PPC=y
|
||||
CONFIG_IOTKIT=y
|
||||
CONFIG_IOTKIT_SECCTL=y
|
||||
|
||||
CONFIG_VERSATILE=y
|
||||
CONFIG_VERSATILE_PCI=y
|
||||
CONFIG_VERSATILE_I2C=y
|
||||
|
||||
@@ -117,6 +119,7 @@ CONFIG_VFIO_XGMAC=y
|
||||
CONFIG_VFIO_AMD_XGBE=y
|
||||
|
||||
CONFIG_SDHCI=y
|
||||
CONFIG_INTEGRATOR=y
|
||||
CONFIG_INTEGRATOR_DEBUG=y
|
||||
|
||||
CONFIG_ALLWINNER_A10_PIT=y
|
||||
@@ -126,6 +129,7 @@ CONFIG_ALLWINNER_A10=y
|
||||
CONFIG_FSL_IMX6=y
|
||||
CONFIG_FSL_IMX31=y
|
||||
CONFIG_FSL_IMX25=y
|
||||
CONFIG_FSL_IMX7=y
|
||||
|
||||
CONFIG_IMX_I2C=y
|
||||
|
||||
@@ -140,3 +144,8 @@ CONFIG_GPIO_KEY=y
|
||||
CONFIG_MSF2=y
|
||||
CONFIG_FW_CFG_DMA=y
|
||||
CONFIG_XILINX_AXI=y
|
||||
CONFIG_PCI_DESIGNWARE=y
|
||||
|
||||
CONFIG_STRONGARM=y
|
||||
CONFIG_HIGHBANK=y
|
||||
CONFIG_MUSICPAL=y
|
||||
|
||||
1
default-configs/riscv32-linux-user.mak
Normal file
1
default-configs/riscv32-linux-user.mak
Normal file
@@ -0,0 +1 @@
|
||||
# Default configuration for riscv-linux-user
|
||||
4
default-configs/riscv32-softmmu.mak
Normal file
4
default-configs/riscv32-softmmu.mak
Normal file
@@ -0,0 +1,4 @@
|
||||
# Default configuration for riscv-softmmu
|
||||
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_VIRTIO=y
|
||||
1
default-configs/riscv64-linux-user.mak
Normal file
1
default-configs/riscv64-linux-user.mak
Normal file
@@ -0,0 +1 @@
|
||||
# Default configuration for riscv-linux-user
|
||||
4
default-configs/riscv64-softmmu.mak
Normal file
4
default-configs/riscv64-softmmu.mak
Normal file
@@ -0,0 +1,4 @@
|
||||
# Default configuration for riscv-softmmu
|
||||
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_VIRTIO=y
|
||||
2
disas.c
2
disas.c
@@ -522,6 +522,8 @@ void disas(FILE *out, void *code, unsigned long size)
|
||||
# ifdef _ARCH_PPC64
|
||||
s.info.cap_mode = CS_MODE_64;
|
||||
# endif
|
||||
#elif defined(__riscv__)
|
||||
print_insn = print_insn_riscv;
|
||||
#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
|
||||
print_insn = print_insn_arm_a64;
|
||||
s.info.cap_arch = CS_ARCH_ARM64;
|
||||
|
||||
@@ -17,6 +17,7 @@ common-obj-$(CONFIG_MIPS_DIS) += mips.o
|
||||
common-obj-$(CONFIG_NIOS2_DIS) += nios2.o
|
||||
common-obj-$(CONFIG_MOXIE_DIS) += moxie.o
|
||||
common-obj-$(CONFIG_PPC_DIS) += ppc.o
|
||||
common-obj-$(CONFIG_RISCV_DIS) += riscv.o
|
||||
common-obj-$(CONFIG_S390_DIS) += s390.o
|
||||
common-obj-$(CONFIG_SH4_DIS) += sh4.o
|
||||
common-obj-$(CONFIG_SPARC_DIS) += sparc.o
|
||||
|
||||
3048
disas/riscv.c
Normal file
3048
disas/riscv.c
Normal file
File diff suppressed because it is too large
Load Diff
1
exec.c
1
exec.c
@@ -3393,7 +3393,6 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len,
|
||||
if (!memory_access_is_direct(mr, is_write)) {
|
||||
l = memory_access_size(mr, l, addr);
|
||||
if (!memory_region_access_valid(mr, xlat, l, is_write)) {
|
||||
rcu_read_unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,8 @@ float32 float32_default_nan(float_status *status)
|
||||
#if defined(TARGET_SPARC) || defined(TARGET_M68K)
|
||||
return const_float32(0x7FFFFFFF);
|
||||
#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
|
||||
defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE)
|
||||
defined(TARGET_XTENSA) || defined(TARGET_S390X) || \
|
||||
defined(TARGET_TRICORE) || defined(TARGET_RISCV)
|
||||
return const_float32(0x7FC00000);
|
||||
#elif defined(TARGET_HPPA)
|
||||
return const_float32(0x7FA00000);
|
||||
@@ -139,7 +140,7 @@ float64 float64_default_nan(float_status *status)
|
||||
#if defined(TARGET_SPARC) || defined(TARGET_M68K)
|
||||
return const_float64(LIT64(0x7FFFFFFFFFFFFFFF));
|
||||
#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
|
||||
defined(TARGET_S390X)
|
||||
defined(TARGET_S390X) || defined(TARGET_RISCV)
|
||||
return const_float64(LIT64(0x7FF8000000000000));
|
||||
#elif defined(TARGET_HPPA)
|
||||
return const_float64(LIT64(0x7FF4000000000000));
|
||||
@@ -203,7 +204,7 @@ float128 float128_default_nan(float_status *status)
|
||||
r.high = LIT64(0x7FFF7FFFFFFFFFFF);
|
||||
} else {
|
||||
r.low = LIT64(0x0000000000000000);
|
||||
#if defined(TARGET_S390X) || defined(TARGET_PPC)
|
||||
#if defined(TARGET_S390X) || defined(TARGET_PPC) || defined(TARGET_RISCV)
|
||||
r.high = LIT64(0x7FFF800000000000);
|
||||
#else
|
||||
r.high = LIT64(0xFFFF800000000000);
|
||||
|
||||
@@ -1342,6 +1342,8 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode,
|
||||
switch (p.cls) {
|
||||
case float_class_snan:
|
||||
case float_class_qnan:
|
||||
case float_class_dnan:
|
||||
case float_class_msnan:
|
||||
return max;
|
||||
case float_class_inf:
|
||||
return p.sign ? min : max;
|
||||
@@ -1430,6 +1432,8 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max,
|
||||
switch (p.cls) {
|
||||
case float_class_snan:
|
||||
case float_class_qnan:
|
||||
case float_class_dnan:
|
||||
case float_class_msnan:
|
||||
s->float_exception_flags = orig_flags | float_flag_invalid;
|
||||
return max;
|
||||
case float_class_inf:
|
||||
|
||||
@@ -253,9 +253,10 @@ ETEXI
|
||||
|
||||
{
|
||||
.name = "screendump",
|
||||
.args_type = "filename:F",
|
||||
.params = "filename",
|
||||
.help = "save screen into PPM image 'filename'",
|
||||
.args_type = "filename:F,device:s?,head:i?",
|
||||
.params = "filename [device [head]]",
|
||||
.help = "save screen from head 'head' of display device 'device' "
|
||||
"into PPM image 'filename'",
|
||||
.cmd = hmp_screendump,
|
||||
},
|
||||
|
||||
|
||||
4
hmp.c
4
hmp.c
@@ -2140,9 +2140,11 @@ err_out:
|
||||
void hmp_screendump(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *filename = qdict_get_str(qdict, "filename");
|
||||
const char *id = qdict_get_try_str(qdict, "device");
|
||||
int64_t head = qdict_get_try_int(qdict, "head", 0);
|
||||
Error *err = NULL;
|
||||
|
||||
qmp_screendump(filename, &err);
|
||||
qmp_screendump(filename, id != NULL, id, id != NULL, head, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
|
||||
obj-$(CONFIG_DIGIC) += digic_boards.o
|
||||
obj-y += integratorcp.o mainstone.o musicpal.o nseries.o
|
||||
obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
|
||||
obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
|
||||
obj-y += boot.o virt.o sysbus-fdt.o
|
||||
obj-$(CONFIG_ACPI) += virt-acpi-build.o
|
||||
obj-y += netduino2.o
|
||||
obj-y += sysbus-fdt.o
|
||||
obj-$(CONFIG_DIGIC) += digic_boards.o
|
||||
obj-$(CONFIG_EXYNOS4) += exynos4_boards.o
|
||||
obj-$(CONFIG_HIGHBANK) += highbank.o
|
||||
obj-$(CONFIG_INTEGRATOR) += integratorcp.o
|
||||
obj-$(CONFIG_MAINSTONE) += mainstone.o
|
||||
obj-$(CONFIG_MUSICPAL) += musicpal.o
|
||||
obj-$(CONFIG_NETDUINO2) += netduino2.o
|
||||
obj-$(CONFIG_NSERIES) += nseries.o
|
||||
obj-$(CONFIG_OMAP) += omap_sx1.o palm.o
|
||||
obj-$(CONFIG_PXA2XX) += gumstix.o spitz.o tosa.o z2.o
|
||||
obj-$(CONFIG_REALVIEW) += realview.o
|
||||
obj-$(CONFIG_STELLARIS) += stellaris.o
|
||||
obj-$(CONFIG_STRONGARM) += collie.o
|
||||
obj-$(CONFIG_VERSATILE) += vexpress.o versatilepb.o
|
||||
obj-$(CONFIG_ZYNQ) += xilinx_zynq.o
|
||||
|
||||
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
|
||||
obj-$(CONFIG_ARM_V7M) += armv7m.o
|
||||
obj-$(CONFIG_EXYNOS4) += exynos4210.o
|
||||
obj-$(CONFIG_PXA2XX) += pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
|
||||
obj-$(CONFIG_DIGIC) += digic.o
|
||||
obj-y += omap1.o omap2.o strongarm.o
|
||||
obj-$(CONFIG_OMAP) += omap1.o omap2.o
|
||||
obj-$(CONFIG_STRONGARM) += strongarm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
|
||||
obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
|
||||
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
|
||||
@@ -22,3 +34,4 @@ obj-$(CONFIG_MPS2) += mps2.o
|
||||
obj-$(CONFIG_MPS2) += mps2-tz.o
|
||||
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
|
||||
obj-$(CONFIG_IOTKIT) += iotkit.o
|
||||
obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
|
||||
|
||||
@@ -829,6 +829,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
|
||||
|
||||
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
|
||||
if (err) {
|
||||
error_free(err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -890,7 +891,8 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
|
||||
}
|
||||
|
||||
/* check the arm64 magic header value -- very old kernels may not have it */
|
||||
if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
|
||||
if (size > ARM64_MAGIC_OFFSET + 4 &&
|
||||
memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
|
||||
uint64_t hdrvals[2];
|
||||
|
||||
/* The arm64 Image header has text_offset and image_size fields at 8 and
|
||||
|
||||
582
hw/arm/fsl-imx7.c
Normal file
582
hw/arm/fsl-imx7.c
Normal file
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Impinj, Inc.
|
||||
*
|
||||
* i.MX7 SoC definitions
|
||||
*
|
||||
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
|
||||
*
|
||||
* Based on hw/arm/fsl-imx6.c
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/arm/fsl-imx7.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define NAME_SIZE 20
|
||||
|
||||
static void fsl_imx7_init(Object *obj)
|
||||
{
|
||||
BusState *sysbus = sysbus_get_default();
|
||||
FslIMX7State *s = FSL_IMX7(obj);
|
||||
char name[NAME_SIZE];
|
||||
int i;
|
||||
|
||||
if (smp_cpus > FSL_IMX7_NUM_CPUS) {
|
||||
error_report("%s: Only %d CPUs are supported (%d requested)",
|
||||
TYPE_FSL_IMX7, FSL_IMX7_NUM_CPUS, smp_cpus);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
|
||||
ARM_CPU_TYPE_NAME("cortex-a7"));
|
||||
snprintf(name, NAME_SIZE, "cpu%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->cpu[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* A7MPCORE
|
||||
*/
|
||||
object_initialize(&s->a7mpcore, sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV);
|
||||
qdev_set_parent_bus(DEVICE(&s->a7mpcore), sysbus);
|
||||
object_property_add_child(obj, "a7mpcore",
|
||||
OBJECT(&s->a7mpcore), &error_fatal);
|
||||
|
||||
/*
|
||||
* GPIOs 1 to 7
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
|
||||
object_initialize(&s->gpio[i], sizeof(s->gpio[i]),
|
||||
TYPE_IMX_GPIO);
|
||||
qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "gpio%d", i);
|
||||
object_property_add_child(obj, name,
|
||||
OBJECT(&s->gpio[i]), &error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* GPT1, 2, 3, 4
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
|
||||
object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX7_GPT);
|
||||
qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "gpt%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->gpt[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* CCM
|
||||
*/
|
||||
object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM);
|
||||
qdev_set_parent_bus(DEVICE(&s->ccm), sysbus);
|
||||
object_property_add_child(obj, "ccm", OBJECT(&s->ccm), &error_fatal);
|
||||
|
||||
/*
|
||||
* Analog
|
||||
*/
|
||||
object_initialize(&s->analog, sizeof(s->analog), TYPE_IMX7_ANALOG);
|
||||
qdev_set_parent_bus(DEVICE(&s->analog), sysbus);
|
||||
object_property_add_child(obj, "analog", OBJECT(&s->analog), &error_fatal);
|
||||
|
||||
/*
|
||||
* GPCv2
|
||||
*/
|
||||
object_initialize(&s->gpcv2, sizeof(s->gpcv2), TYPE_IMX_GPCV2);
|
||||
qdev_set_parent_bus(DEVICE(&s->gpcv2), sysbus);
|
||||
object_property_add_child(obj, "gpcv2", OBJECT(&s->gpcv2), &error_fatal);
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
|
||||
object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI);
|
||||
qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
|
||||
snprintf(name, NAME_SIZE, "spi%d", i + 1);
|
||||
object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL);
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
|
||||
object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
|
||||
qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
|
||||
snprintf(name, NAME_SIZE, "i2c%d", i + 1);
|
||||
object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* UART
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
|
||||
object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
|
||||
qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "uart%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->uart[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
|
||||
object_initialize(&s->eth[i], sizeof(s->eth[i]), TYPE_IMX_ENET);
|
||||
qdev_set_parent_bus(DEVICE(&s->eth[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "eth%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->eth[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* SDHCI
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
|
||||
object_initialize(&s->usdhc[i], sizeof(s->usdhc[i]),
|
||||
TYPE_IMX_USDHC);
|
||||
qdev_set_parent_bus(DEVICE(&s->usdhc[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "usdhc%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->usdhc[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* SNVS
|
||||
*/
|
||||
object_initialize(&s->snvs, sizeof(s->snvs), TYPE_IMX7_SNVS);
|
||||
qdev_set_parent_bus(DEVICE(&s->snvs), sysbus);
|
||||
object_property_add_child(obj, "snvs", OBJECT(&s->snvs), &error_fatal);
|
||||
|
||||
/*
|
||||
* Watchdog
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
|
||||
object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_IMX2_WDT);
|
||||
qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "wdt%d", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->wdt[i]),
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
/*
|
||||
* GPR
|
||||
*/
|
||||
object_initialize(&s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR);
|
||||
qdev_set_parent_bus(DEVICE(&s->gpr), sysbus);
|
||||
object_property_add_child(obj, "gpr", OBJECT(&s->gpr), &error_fatal);
|
||||
|
||||
object_initialize(&s->pcie, sizeof(s->pcie), TYPE_DESIGNWARE_PCIE_HOST);
|
||||
qdev_set_parent_bus(DEVICE(&s->pcie), sysbus);
|
||||
object_property_add_child(obj, "pcie", OBJECT(&s->pcie), &error_fatal);
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
|
||||
object_initialize(&s->usb[i],
|
||||
sizeof(s->usb[i]), TYPE_CHIPIDEA);
|
||||
qdev_set_parent_bus(DEVICE(&s->usb[i]), sysbus);
|
||||
snprintf(name, NAME_SIZE, "usb%d", i);
|
||||
object_property_add_child(obj, name,
|
||||
OBJECT(&s->usb[i]), &error_fatal);
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_imx7_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
FslIMX7State *s = FSL_IMX7(dev);
|
||||
Object *o;
|
||||
int i;
|
||||
qemu_irq irq;
|
||||
char name[NAME_SIZE];
|
||||
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
o = OBJECT(&s->cpu[i]);
|
||||
|
||||
object_property_set_int(o, QEMU_PSCI_CONDUIT_SMC,
|
||||
"psci-conduit", &error_abort);
|
||||
|
||||
/* On uniprocessor, the CBAR is set to 0 */
|
||||
if (smp_cpus > 1) {
|
||||
object_property_set_int(o, FSL_IMX7_A7MPCORE_ADDR,
|
||||
"reset-cbar", &error_abort);
|
||||
}
|
||||
|
||||
if (i) {
|
||||
/* Secondary CPUs start in PSCI powered-down state */
|
||||
object_property_set_bool(o, true,
|
||||
"start-powered-off", &error_abort);
|
||||
}
|
||||
|
||||
object_property_set_bool(o, true, "realized", &error_abort);
|
||||
}
|
||||
|
||||
/*
|
||||
* A7MPCORE
|
||||
*/
|
||||
object_property_set_int(OBJECT(&s->a7mpcore), smp_cpus, "num-cpu",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->a7mpcore),
|
||||
FSL_IMX7_MAX_IRQ + GIC_INTERNAL,
|
||||
"num-irq", &error_abort);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX7_A7MPCORE_ADDR);
|
||||
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore);
|
||||
DeviceState *d = DEVICE(qemu_get_cpu(i));
|
||||
|
||||
irq = qdev_get_gpio_in(d, ARM_CPU_IRQ);
|
||||
sysbus_connect_irq(sbd, i, irq);
|
||||
irq = qdev_get_gpio_in(d, ARM_CPU_FIQ);
|
||||
sysbus_connect_irq(sbd, i + smp_cpus, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* A7MPCORE DAP
|
||||
*/
|
||||
create_unimplemented_device("a7mpcore-dap", FSL_IMX7_A7MPCORE_DAP_ADDR,
|
||||
0x100000);
|
||||
|
||||
/*
|
||||
* GPT1, 2, 3, 4
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
|
||||
static const hwaddr FSL_IMX7_GPTn_ADDR[FSL_IMX7_NUM_GPTS] = {
|
||||
FSL_IMX7_GPT1_ADDR,
|
||||
FSL_IMX7_GPT2_ADDR,
|
||||
FSL_IMX7_GPT3_ADDR,
|
||||
FSL_IMX7_GPT4_ADDR,
|
||||
};
|
||||
|
||||
s->gpt[i].ccm = IMX_CCM(&s->ccm);
|
||||
object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
|
||||
static const hwaddr FSL_IMX7_GPIOn_ADDR[FSL_IMX7_NUM_GPIOS] = {
|
||||
FSL_IMX7_GPIO1_ADDR,
|
||||
FSL_IMX7_GPIO2_ADDR,
|
||||
FSL_IMX7_GPIO3_ADDR,
|
||||
FSL_IMX7_GPIO4_ADDR,
|
||||
FSL_IMX7_GPIO5_ADDR,
|
||||
FSL_IMX7_GPIO6_ADDR,
|
||||
FSL_IMX7_GPIO7_ADDR,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, FSL_IMX7_GPIOn_ADDR[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* IOMUXC and IOMUXC_LPSR
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_IOMUXCS; i++) {
|
||||
static const hwaddr FSL_IMX7_IOMUXCn_ADDR[FSL_IMX7_NUM_IOMUXCS] = {
|
||||
FSL_IMX7_IOMUXC_ADDR,
|
||||
FSL_IMX7_IOMUXC_LPSR_ADDR,
|
||||
};
|
||||
|
||||
snprintf(name, NAME_SIZE, "iomuxc%d", i);
|
||||
create_unimplemented_device(name, FSL_IMX7_IOMUXCn_ADDR[i],
|
||||
FSL_IMX7_IOMUXCn_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* CCM
|
||||
*/
|
||||
object_property_set_bool(OBJECT(&s->ccm), true, "realized", &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX7_CCM_ADDR);
|
||||
|
||||
/*
|
||||
* Analog
|
||||
*/
|
||||
object_property_set_bool(OBJECT(&s->analog), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, FSL_IMX7_ANALOG_ADDR);
|
||||
|
||||
/*
|
||||
* GPCv2
|
||||
*/
|
||||
object_property_set_bool(OBJECT(&s->gpcv2), true,
|
||||
"realized", &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX7_GPC_ADDR);
|
||||
|
||||
/* Initialize all ECSPI */
|
||||
for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
|
||||
static const hwaddr FSL_IMX7_SPIn_ADDR[FSL_IMX7_NUM_ECSPIS] = {
|
||||
FSL_IMX7_ECSPI1_ADDR,
|
||||
FSL_IMX7_ECSPI2_ADDR,
|
||||
FSL_IMX7_ECSPI3_ADDR,
|
||||
FSL_IMX7_ECSPI4_ADDR,
|
||||
};
|
||||
|
||||
static const hwaddr FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = {
|
||||
FSL_IMX7_ECSPI1_IRQ,
|
||||
FSL_IMX7_ECSPI2_IRQ,
|
||||
FSL_IMX7_ECSPI3_IRQ,
|
||||
FSL_IMX7_ECSPI4_IRQ,
|
||||
};
|
||||
|
||||
/* Initialize the SPI */
|
||||
object_property_set_bool(OBJECT(&s->spi[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
|
||||
FSL_IMX7_SPIn_ADDR[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->a7mpcore),
|
||||
FSL_IMX7_SPIn_IRQ[i]));
|
||||
}
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
|
||||
static const hwaddr FSL_IMX7_I2Cn_ADDR[FSL_IMX7_NUM_I2CS] = {
|
||||
FSL_IMX7_I2C1_ADDR,
|
||||
FSL_IMX7_I2C2_ADDR,
|
||||
FSL_IMX7_I2C3_ADDR,
|
||||
FSL_IMX7_I2C4_ADDR,
|
||||
};
|
||||
|
||||
static const hwaddr FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = {
|
||||
FSL_IMX7_I2C1_IRQ,
|
||||
FSL_IMX7_I2C2_IRQ,
|
||||
FSL_IMX7_I2C3_IRQ,
|
||||
FSL_IMX7_I2C4_IRQ,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX7_I2Cn_ADDR[i]);
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->a7mpcore),
|
||||
FSL_IMX7_I2Cn_IRQ[i]));
|
||||
}
|
||||
|
||||
/*
|
||||
* UART
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
|
||||
static const hwaddr FSL_IMX7_UARTn_ADDR[FSL_IMX7_NUM_UARTS] = {
|
||||
FSL_IMX7_UART1_ADDR,
|
||||
FSL_IMX7_UART2_ADDR,
|
||||
FSL_IMX7_UART3_ADDR,
|
||||
FSL_IMX7_UART4_ADDR,
|
||||
FSL_IMX7_UART5_ADDR,
|
||||
FSL_IMX7_UART6_ADDR,
|
||||
FSL_IMX7_UART7_ADDR,
|
||||
};
|
||||
|
||||
static const int FSL_IMX7_UARTn_IRQ[FSL_IMX7_NUM_UARTS] = {
|
||||
FSL_IMX7_UART1_IRQ,
|
||||
FSL_IMX7_UART2_IRQ,
|
||||
FSL_IMX7_UART3_IRQ,
|
||||
FSL_IMX7_UART4_IRQ,
|
||||
FSL_IMX7_UART5_IRQ,
|
||||
FSL_IMX7_UART6_IRQ,
|
||||
FSL_IMX7_UART7_IRQ,
|
||||
};
|
||||
|
||||
|
||||
if (i < MAX_SERIAL_PORTS) {
|
||||
qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->uart[i]), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, FSL_IMX7_UARTn_ADDR[i]);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_UARTn_IRQ[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
|
||||
static const hwaddr FSL_IMX7_ENETn_ADDR[FSL_IMX7_NUM_ETHS] = {
|
||||
FSL_IMX7_ENET1_ADDR,
|
||||
FSL_IMX7_ENET2_ADDR,
|
||||
};
|
||||
|
||||
object_property_set_uint(OBJECT(&s->eth[i]), FSL_IMX7_ETH_NUM_TX_RINGS,
|
||||
"tx-ring-num", &error_abort);
|
||||
qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]);
|
||||
object_property_set_bool(OBJECT(&s->eth[i]), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, FSL_IMX7_ENETn_ADDR[i]);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 0));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 3));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* USDHC
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
|
||||
static const hwaddr FSL_IMX7_USDHCn_ADDR[FSL_IMX7_NUM_USDHCS] = {
|
||||
FSL_IMX7_USDHC1_ADDR,
|
||||
FSL_IMX7_USDHC2_ADDR,
|
||||
FSL_IMX7_USDHC3_ADDR,
|
||||
};
|
||||
|
||||
static const int FSL_IMX7_USDHCn_IRQ[FSL_IMX7_NUM_USDHCS] = {
|
||||
FSL_IMX7_USDHC1_IRQ,
|
||||
FSL_IMX7_USDHC2_IRQ,
|
||||
FSL_IMX7_USDHC3_IRQ,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->usdhc[i]), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0,
|
||||
FSL_IMX7_USDHCn_ADDR[i]);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USDHCn_IRQ[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* SNVS
|
||||
*/
|
||||
object_property_set_bool(OBJECT(&s->snvs), true, "realized", &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX7_SNVS_ADDR);
|
||||
|
||||
/*
|
||||
* SRC
|
||||
*/
|
||||
create_unimplemented_device("sdma", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE);
|
||||
|
||||
/*
|
||||
* Watchdog
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
|
||||
static const hwaddr FSL_IMX7_WDOGn_ADDR[FSL_IMX7_NUM_WDTS] = {
|
||||
FSL_IMX7_WDOG1_ADDR,
|
||||
FSL_IMX7_WDOG2_ADDR,
|
||||
FSL_IMX7_WDOG3_ADDR,
|
||||
FSL_IMX7_WDOG4_ADDR,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX7_WDOGn_ADDR[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* SDMA
|
||||
*/
|
||||
create_unimplemented_device("sdma", FSL_IMX7_SDMA_ADDR, FSL_IMX7_SDMA_SIZE);
|
||||
|
||||
|
||||
object_property_set_bool(OBJECT(&s->gpr), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX7_GPR_ADDR);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->pcie), true,
|
||||
"realized", &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq);
|
||||
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
|
||||
static const hwaddr FSL_IMX7_USBMISCn_ADDR[FSL_IMX7_NUM_USBS] = {
|
||||
FSL_IMX7_USBMISC1_ADDR,
|
||||
FSL_IMX7_USBMISC2_ADDR,
|
||||
FSL_IMX7_USBMISC3_ADDR,
|
||||
};
|
||||
|
||||
static const hwaddr FSL_IMX7_USBn_ADDR[FSL_IMX7_NUM_USBS] = {
|
||||
FSL_IMX7_USB1_ADDR,
|
||||
FSL_IMX7_USB2_ADDR,
|
||||
FSL_IMX7_USB3_ADDR,
|
||||
};
|
||||
|
||||
static const hwaddr FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = {
|
||||
FSL_IMX7_USB1_IRQ,
|
||||
FSL_IMX7_USB2_IRQ,
|
||||
FSL_IMX7_USB3_IRQ,
|
||||
};
|
||||
|
||||
object_property_set_bool(OBJECT(&s->usb[i]), true, "realized",
|
||||
&error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0,
|
||||
FSL_IMX7_USBn_ADDR[i]);
|
||||
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USBn_IRQ[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, irq);
|
||||
|
||||
snprintf(name, NAME_SIZE, "usbmisc%d", i);
|
||||
create_unimplemented_device(name, FSL_IMX7_USBMISCn_ADDR[i],
|
||||
FSL_IMX7_USBMISCn_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* ADCs
|
||||
*/
|
||||
for (i = 0; i < FSL_IMX7_NUM_ADCS; i++) {
|
||||
static const hwaddr FSL_IMX7_ADCn_ADDR[FSL_IMX7_NUM_ADCS] = {
|
||||
FSL_IMX7_ADC1_ADDR,
|
||||
FSL_IMX7_ADC2_ADDR,
|
||||
};
|
||||
|
||||
snprintf(name, NAME_SIZE, "adc%d", i);
|
||||
create_unimplemented_device(name, FSL_IMX7_ADCn_ADDR[i],
|
||||
FSL_IMX7_ADCn_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* LCD
|
||||
*/
|
||||
create_unimplemented_device("lcdif", FSL_IMX7_LCDIF_ADDR,
|
||||
FSL_IMX7_LCDIF_SIZE);
|
||||
}
|
||||
|
||||
static void fsl_imx7_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = fsl_imx7_realize;
|
||||
|
||||
/* Reason: Uses serial_hds and nd_table in realize() directly */
|
||||
dc->user_creatable = false;
|
||||
dc->desc = "i.MX7 SOC";
|
||||
}
|
||||
|
||||
static const TypeInfo fsl_imx7_type_info = {
|
||||
.name = TYPE_FSL_IMX7,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(FslIMX7State),
|
||||
.instance_init = fsl_imx7_init,
|
||||
.class_init = fsl_imx7_class_init,
|
||||
};
|
||||
|
||||
static void fsl_imx7_register_types(void)
|
||||
{
|
||||
type_register_static(&fsl_imx7_type_info);
|
||||
}
|
||||
type_init(fsl_imx7_register_types)
|
||||
90
hw/arm/mcimx7d-sabre.c
Normal file
90
hw/arm/mcimx7d-sabre.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Impinj, Inc.
|
||||
*
|
||||
* MCIMX7D_SABRE Board System emulation.
|
||||
*
|
||||
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
|
||||
*
|
||||
* This code is licensed under the GPL, version 2 or later.
|
||||
* See the file `COPYING' in the top level directory.
|
||||
*
|
||||
* It (partially) emulates a mcimx7d_sabre board, with a Freescale
|
||||
* i.MX7 SoC
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/arm/fsl-imx7.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "net/net.h"
|
||||
|
||||
typedef struct {
|
||||
FslIMX7State soc;
|
||||
MemoryRegion ram;
|
||||
} MCIMX7Sabre;
|
||||
|
||||
static void mcimx7d_sabre_init(MachineState *machine)
|
||||
{
|
||||
static struct arm_boot_info boot_info;
|
||||
MCIMX7Sabre *s = g_new0(MCIMX7Sabre, 1);
|
||||
Object *soc;
|
||||
int i;
|
||||
|
||||
if (machine->ram_size > FSL_IMX7_MMDC_SIZE) {
|
||||
error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)",
|
||||
machine->ram_size, FSL_IMX7_MMDC_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
boot_info = (struct arm_boot_info) {
|
||||
.loader_start = FSL_IMX7_MMDC_ADDR,
|
||||
.board_id = -1,
|
||||
.ram_size = machine->ram_size,
|
||||
.kernel_filename = machine->kernel_filename,
|
||||
.kernel_cmdline = machine->kernel_cmdline,
|
||||
.initrd_filename = machine->initrd_filename,
|
||||
.nb_cpus = smp_cpus,
|
||||
};
|
||||
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX7);
|
||||
soc = OBJECT(&s->soc);
|
||||
object_property_add_child(OBJECT(machine), "soc", soc, &error_fatal);
|
||||
object_property_set_bool(soc, true, "realized", &error_fatal);
|
||||
|
||||
memory_region_allocate_system_memory(&s->ram, NULL, "mcimx7d-sabre.ram",
|
||||
machine->ram_size);
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
FSL_IMX7_MMDC_ADDR, &s->ram);
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
|
||||
BusState *bus;
|
||||
DeviceState *carddev;
|
||||
DriveInfo *di;
|
||||
BlockBackend *blk;
|
||||
|
||||
di = drive_get_next(IF_SD);
|
||||
blk = di ? blk_by_legacy_dinfo(di) : NULL;
|
||||
bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus");
|
||||
carddev = qdev_create(bus, TYPE_SD_CARD);
|
||||
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
||||
object_property_set_bool(OBJECT(carddev), true,
|
||||
"realized", &error_fatal);
|
||||
}
|
||||
|
||||
if (!qtest_enabled()) {
|
||||
arm_load_kernel(&s->soc.cpu[0], &boot_info);
|
||||
}
|
||||
}
|
||||
|
||||
static void mcimx7d_sabre_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "Freescale i.MX7 DUAL SABRE (Cortex A7)";
|
||||
mc->init = mcimx7d_sabre_init;
|
||||
mc->max_cpus = FSL_IMX7_NUM_CPUS;
|
||||
}
|
||||
DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init)
|
||||
@@ -169,6 +169,7 @@ static const char *valid_cpus[] = {
|
||||
ARM_CPU_TYPE_NAME("cortex-a53"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a57"),
|
||||
ARM_CPU_TYPE_NAME("host"),
|
||||
ARM_CPU_TYPE_NAME("max"),
|
||||
};
|
||||
|
||||
static bool cpu_type_valid(const char *cpu)
|
||||
@@ -1206,16 +1207,23 @@ static void machvirt_init(MachineState *machine)
|
||||
/* We can probe only here because during property set
|
||||
* KVM is not available yet
|
||||
*/
|
||||
if (!vms->gic_version) {
|
||||
if (vms->gic_version <= 0) {
|
||||
/* "host" or "max" */
|
||||
if (!kvm_enabled()) {
|
||||
error_report("gic-version=host requires KVM");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
vms->gic_version = kvm_arm_vgic_probe();
|
||||
if (!vms->gic_version) {
|
||||
error_report("Unable to determine GIC version supported by host");
|
||||
exit(1);
|
||||
if (vms->gic_version == 0) {
|
||||
error_report("gic-version=host requires KVM");
|
||||
exit(1);
|
||||
} else {
|
||||
/* "max": currently means 3 for TCG */
|
||||
vms->gic_version = 3;
|
||||
}
|
||||
} else {
|
||||
vms->gic_version = kvm_arm_vgic_probe();
|
||||
if (!vms->gic_version) {
|
||||
error_report(
|
||||
"Unable to determine GIC version supported by host");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,9 +1487,11 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
|
||||
vms->gic_version = 2;
|
||||
} else if (!strcmp(value, "host")) {
|
||||
vms->gic_version = 0; /* Will probe later */
|
||||
} else if (!strcmp(value, "max")) {
|
||||
vms->gic_version = -1; /* Will probe later */
|
||||
} else {
|
||||
error_setg(errp, "Invalid gic-version value");
|
||||
error_append_hint(errp, "Valid values are 3, 2, host.\n");
|
||||
error_append_hint(errp, "Valid values are 3, 2, host, max.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -282,6 +282,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
s->virt, "has_el2", NULL);
|
||||
object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
|
||||
"reset-cbar", &error_abort);
|
||||
object_property_set_int(OBJECT(&s->apu_cpu[i]), num_apus,
|
||||
"core-count", &error_abort);
|
||||
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
|
||||
&err);
|
||||
if (err) {
|
||||
|
||||
@@ -34,6 +34,7 @@ struct VirtIOBlockDataPlane {
|
||||
VirtIODevice *vdev;
|
||||
QEMUBH *bh; /* bh for guest notification */
|
||||
unsigned long *batch_notify_vqs;
|
||||
bool batch_notifications;
|
||||
|
||||
/* Note that these EventNotifiers are assigned by value. This is
|
||||
* fine as long as you do not call event_notifier_cleanup on them
|
||||
@@ -47,8 +48,12 @@ struct VirtIOBlockDataPlane {
|
||||
/* Raise an interrupt to signal guest, if necessary */
|
||||
void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq)
|
||||
{
|
||||
set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs);
|
||||
qemu_bh_schedule(s->bh);
|
||||
if (s->batch_notifications) {
|
||||
set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs);
|
||||
qemu_bh_schedule(s->bh);
|
||||
} else {
|
||||
virtio_notify_irqfd(s->vdev, vq);
|
||||
}
|
||||
}
|
||||
|
||||
static void notify_guest_bh(void *opaque)
|
||||
@@ -177,6 +182,12 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev)
|
||||
|
||||
s->starting = true;
|
||||
|
||||
if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
|
||||
s->batch_notifications = true;
|
||||
} else {
|
||||
s->batch_notifications = false;
|
||||
}
|
||||
|
||||
/* Set up guest notifier (irq) */
|
||||
r = k->set_guest_notifiers(qbus->parent, nvqs, true);
|
||||
if (r != 0) {
|
||||
@@ -229,6 +240,22 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/* Stop notifications for new requests from guest.
|
||||
*
|
||||
* Context: BH in IOThread
|
||||
*/
|
||||
static void virtio_blk_data_plane_stop_bh(void *opaque)
|
||||
{
|
||||
VirtIOBlockDataPlane *s = opaque;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < s->conf->num_queues; i++) {
|
||||
VirtQueue *vq = virtio_get_queue(s->vdev, i);
|
||||
|
||||
virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Context: QEMU global mutex held */
|
||||
void virtio_blk_data_plane_stop(VirtIODevice *vdev)
|
||||
{
|
||||
@@ -253,13 +280,7 @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev)
|
||||
trace_virtio_blk_data_plane_stop(s);
|
||||
|
||||
aio_context_acquire(s->ctx);
|
||||
|
||||
/* Stop notifications for new requests from guest */
|
||||
for (i = 0; i < nvqs; i++) {
|
||||
VirtQueue *vq = virtio_get_queue(s->vdev, i);
|
||||
|
||||
virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
|
||||
}
|
||||
aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s);
|
||||
|
||||
/* Drain and switch bs back to the QEMU main loop */
|
||||
blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
|
||||
|
||||
@@ -102,12 +102,12 @@ static bool can_handle_event(uint8_t type)
|
||||
return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD;
|
||||
}
|
||||
|
||||
static unsigned int send_mask(void)
|
||||
static sccb_mask_t send_mask(void)
|
||||
{
|
||||
return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD;
|
||||
}
|
||||
|
||||
static unsigned int receive_mask(void)
|
||||
static sccb_mask_t receive_mask(void)
|
||||
{
|
||||
return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD;
|
||||
}
|
||||
@@ -318,11 +318,6 @@ static int console_init(SCLPEvent *event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int console_exit(SCLPEvent *event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void console_reset(DeviceState *dev)
|
||||
{
|
||||
SCLPEvent *event = SCLP_EVENT(dev);
|
||||
@@ -349,7 +344,6 @@ static void console_class_init(ObjectClass *klass, void *data)
|
||||
dc->reset = console_reset;
|
||||
dc->vmsd = &vmstate_sclplmconsole;
|
||||
ec->init = console_init;
|
||||
ec->exit = console_exit;
|
||||
ec->get_send_mask = send_mask;
|
||||
ec->get_receive_mask = receive_mask;
|
||||
ec->can_handle_event = can_handle_event;
|
||||
|
||||
@@ -83,12 +83,12 @@ static bool can_handle_event(uint8_t type)
|
||||
return type == SCLP_EVENT_ASCII_CONSOLE_DATA;
|
||||
}
|
||||
|
||||
static unsigned int send_mask(void)
|
||||
static sccb_mask_t send_mask(void)
|
||||
{
|
||||
return SCLP_EVENT_MASK_MSG_ASCII;
|
||||
}
|
||||
|
||||
static unsigned int receive_mask(void)
|
||||
static sccb_mask_t receive_mask(void)
|
||||
{
|
||||
return SCLP_EVENT_MASK_MSG_ASCII;
|
||||
}
|
||||
@@ -246,11 +246,6 @@ static void console_reset(DeviceState *dev)
|
||||
scon->notify = false;
|
||||
}
|
||||
|
||||
static int console_exit(SCLPEvent *event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Property console_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@@ -265,7 +260,6 @@ static void console_class_init(ObjectClass *klass, void *data)
|
||||
dc->reset = console_reset;
|
||||
dc->vmsd = &vmstate_sclpconsole;
|
||||
ec->init = console_init;
|
||||
ec->exit = console_exit;
|
||||
ec->get_send_mask = send_mask;
|
||||
ec->get_receive_mask = receive_mask;
|
||||
ec->can_handle_event = can_handle_event;
|
||||
|
||||
@@ -449,6 +449,20 @@ int load_elf_ram(const char *filename,
|
||||
uint64_t *highaddr, int big_endian, int elf_machine,
|
||||
int clear_lsb, int data_swab, AddressSpace *as,
|
||||
bool load_rom)
|
||||
{
|
||||
return load_elf_ram_sym(filename, translate_fn, translate_opaque,
|
||||
pentry, lowaddr, highaddr, big_endian,
|
||||
elf_machine, clear_lsb, data_swab, as,
|
||||
load_rom, NULL);
|
||||
}
|
||||
|
||||
/* return < 0 if error, otherwise the number of bytes loaded in memory */
|
||||
int load_elf_ram_sym(const char *filename,
|
||||
uint64_t (*translate_fn)(void *, uint64_t),
|
||||
void *translate_opaque, uint64_t *pentry,
|
||||
uint64_t *lowaddr, uint64_t *highaddr, int big_endian,
|
||||
int elf_machine, int clear_lsb, int data_swab,
|
||||
AddressSpace *as, bool load_rom, symbol_fn_t sym_cb)
|
||||
{
|
||||
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
@@ -488,11 +502,11 @@ int load_elf_ram(const char *filename,
|
||||
if (e_ident[EI_CLASS] == ELFCLASS64) {
|
||||
ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
|
||||
pentry, lowaddr, highaddr, elf_machine, clear_lsb,
|
||||
data_swab, as, load_rom);
|
||||
data_swab, as, load_rom, sym_cb);
|
||||
} else {
|
||||
ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
|
||||
pentry, lowaddr, highaddr, elf_machine, clear_lsb,
|
||||
data_swab, as, load_rom);
|
||||
data_swab, as, load_rom, sym_cb);
|
||||
}
|
||||
|
||||
fail:
|
||||
|
||||
@@ -31,12 +31,13 @@
|
||||
#include "hw/loader.h"
|
||||
#include "elf.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
/* Show multiboot debug output */
|
||||
//#define DEBUG_MULTIBOOT
|
||||
|
||||
#ifdef DEBUG_MULTIBOOT
|
||||
#define mb_debug(a...) fprintf(stderr, ## a)
|
||||
#define mb_debug(a...) error_report(a)
|
||||
#else
|
||||
#define mb_debug(a...)
|
||||
#endif
|
||||
@@ -137,7 +138,7 @@ static void mb_add_mod(MultibootState *s,
|
||||
stl_p(p + MB_MOD_END, end);
|
||||
stl_p(p + MB_MOD_CMDLINE, cmdline_phys);
|
||||
|
||||
mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx"\n",
|
||||
mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx,
|
||||
s->mb_mods_count, start, end);
|
||||
|
||||
s->mb_mods_count++;
|
||||
@@ -179,12 +180,12 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
if (!is_multiboot)
|
||||
return 0; /* no multiboot */
|
||||
|
||||
mb_debug("qemu: I believe we found a multiboot image!\n");
|
||||
mb_debug("qemu: I believe we found a multiboot image!");
|
||||
memset(bootinfo, 0, sizeof(bootinfo));
|
||||
memset(&mbs, 0, sizeof(mbs));
|
||||
|
||||
if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */
|
||||
fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n");
|
||||
error_report("qemu: multiboot knows VBE. we don't.");
|
||||
}
|
||||
if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */
|
||||
uint64_t elf_entry;
|
||||
@@ -193,7 +194,7 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
fclose(f);
|
||||
|
||||
if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) {
|
||||
fprintf(stderr, "Cannot load x86-64 image, give a 32bit one.\n");
|
||||
error_report("Cannot load x86-64 image, give a 32bit one.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -201,7 +202,7 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
&elf_low, &elf_high, 0, I386_ELF_MACHINE,
|
||||
0, 0);
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "Error while loading elf kernel\n");
|
||||
error_report("Error while loading elf kernel");
|
||||
exit(1);
|
||||
}
|
||||
mh_load_addr = elf_low;
|
||||
@@ -210,12 +211,13 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
|
||||
mbs.mb_buf = g_malloc(mb_kernel_size);
|
||||
if (rom_copy(mbs.mb_buf, mh_load_addr, mb_kernel_size) != mb_kernel_size) {
|
||||
fprintf(stderr, "Error while fetching elf kernel from rom\n");
|
||||
error_report("Error while fetching elf kernel from rom");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mb_debug("qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n",
|
||||
mb_kernel_size, (size_t)mh_entry_addr);
|
||||
mb_debug("qemu: loading multiboot-elf kernel "
|
||||
"(%#x bytes) with entry %#zx",
|
||||
mb_kernel_size, (size_t)mh_entry_addr);
|
||||
} else {
|
||||
/* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */
|
||||
uint32_t mh_header_addr = ldl_p(header+i+12);
|
||||
@@ -224,7 +226,7 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
|
||||
mh_load_addr = ldl_p(header+i+16);
|
||||
if (mh_header_addr < mh_load_addr) {
|
||||
fprintf(stderr, "invalid mh_load_addr address\n");
|
||||
error_report("invalid load_addr address");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -233,43 +235,39 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
mh_entry_addr = ldl_p(header+i+28);
|
||||
|
||||
if (mh_load_end_addr) {
|
||||
if (mh_bss_end_addr < mh_load_addr) {
|
||||
fprintf(stderr, "invalid mh_bss_end_addr address\n");
|
||||
exit(1);
|
||||
}
|
||||
mb_kernel_size = mh_bss_end_addr - mh_load_addr;
|
||||
|
||||
if (mh_load_end_addr < mh_load_addr) {
|
||||
fprintf(stderr, "invalid mh_load_end_addr address\n");
|
||||
error_report("invalid load_end_addr address");
|
||||
exit(1);
|
||||
}
|
||||
mb_load_size = mh_load_end_addr - mh_load_addr;
|
||||
} else {
|
||||
if (kernel_file_size < mb_kernel_text_offset) {
|
||||
fprintf(stderr, "invalid kernel_file_size\n");
|
||||
error_report("invalid kernel_file_size");
|
||||
exit(1);
|
||||
}
|
||||
mb_kernel_size = kernel_file_size - mb_kernel_text_offset;
|
||||
mb_load_size = mb_kernel_size;
|
||||
mb_load_size = kernel_file_size - mb_kernel_text_offset;
|
||||
}
|
||||
if (mh_bss_end_addr) {
|
||||
if (mh_bss_end_addr < (mh_load_addr + mb_load_size)) {
|
||||
error_report("invalid bss_end_addr address");
|
||||
exit(1);
|
||||
}
|
||||
mb_kernel_size = mh_bss_end_addr - mh_load_addr;
|
||||
} else {
|
||||
mb_kernel_size = mb_load_size;
|
||||
}
|
||||
|
||||
/* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE.
|
||||
uint32_t mh_mode_type = ldl_p(header+i+32);
|
||||
uint32_t mh_width = ldl_p(header+i+36);
|
||||
uint32_t mh_height = ldl_p(header+i+40);
|
||||
uint32_t mh_depth = ldl_p(header+i+44); */
|
||||
|
||||
mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr);
|
||||
mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr);
|
||||
mb_debug("multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr);
|
||||
mb_debug("multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr);
|
||||
mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n",
|
||||
mb_debug("multiboot: header_addr = %#x", mh_header_addr);
|
||||
mb_debug("multiboot: load_addr = %#x", mh_load_addr);
|
||||
mb_debug("multiboot: load_end_addr = %#x", mh_load_end_addr);
|
||||
mb_debug("multiboot: bss_end_addr = %#x", mh_bss_end_addr);
|
||||
mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x",
|
||||
mb_load_size, mh_load_addr);
|
||||
|
||||
mbs.mb_buf = g_malloc(mb_kernel_size);
|
||||
fseek(f, mb_kernel_text_offset, SEEK_SET);
|
||||
if (fread(mbs.mb_buf, 1, mb_load_size, f) != mb_load_size) {
|
||||
fprintf(stderr, "fread() failed\n");
|
||||
error_report("fread() failed");
|
||||
exit(1);
|
||||
}
|
||||
memset(mbs.mb_buf + mb_load_size, 0, mb_kernel_size - mb_load_size);
|
||||
@@ -323,10 +321,10 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
hwaddr c = mb_add_cmdline(&mbs, tmpbuf);
|
||||
if ((next_space = strchr(tmpbuf, ' ')))
|
||||
*next_space = '\0';
|
||||
mb_debug("multiboot loading module: %s\n", tmpbuf);
|
||||
mb_debug("multiboot loading module: %s", tmpbuf);
|
||||
mb_mod_length = get_image_size(tmpbuf);
|
||||
if (mb_mod_length < 0) {
|
||||
fprintf(stderr, "Failed to open file '%s'\n", tmpbuf);
|
||||
error_report("Failed to open file '%s'", tmpbuf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -337,7 +335,7 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
mb_add_mod(&mbs, mbs.mb_buf_phys + offs,
|
||||
mbs.mb_buf_phys + offs + mb_mod_length, c);
|
||||
|
||||
mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx"\n",
|
||||
mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx,
|
||||
(char *)mbs.mb_buf + offs,
|
||||
(char *)mbs.mb_buf + offs + mb_mod_length, c);
|
||||
initrd_filename = next_initrd+1;
|
||||
@@ -365,10 +363,11 @@ int load_multiboot(FWCfgState *fw_cfg,
|
||||
stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */
|
||||
stl_p(bootinfo + MBI_MMAP_ADDR, ADDR_E820_MAP);
|
||||
|
||||
mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
|
||||
mb_debug(" mb_buf_phys = "TARGET_FMT_plx"\n", mbs.mb_buf_phys);
|
||||
mb_debug(" mod_start = "TARGET_FMT_plx"\n", mbs.mb_buf_phys + mbs.offset_mods);
|
||||
mb_debug(" mb_mods_count = %d\n", mbs.mb_mods_count);
|
||||
mb_debug("multiboot: entry_addr = %#x", mh_entry_addr);
|
||||
mb_debug(" mb_buf_phys = "TARGET_FMT_plx, mbs.mb_buf_phys);
|
||||
mb_debug(" mod_start = "TARGET_FMT_plx,
|
||||
mbs.mb_buf_phys + mbs.offset_mods);
|
||||
mb_debug(" mb_mods_count = %d", mbs.mb_mods_count);
|
||||
|
||||
/* save bootinfo off the stack */
|
||||
mb_bootinfo_data = g_memdup(bootinfo, sizeof(bootinfo));
|
||||
|
||||
@@ -17,3 +17,5 @@ common-obj-$(CONFIG_PCI_PIIX) += piix.o
|
||||
common-obj-$(CONFIG_PCI_Q35) += q35.o
|
||||
common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
|
||||
common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o
|
||||
|
||||
common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o
|
||||
|
||||
754
hw/pci-host/designware.c
Normal file
754
hw/pci-host/designware.c
Normal file
@@ -0,0 +1,754 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Impinj, Inc.
|
||||
*
|
||||
* Designware PCIe IP block emulation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/pci-host/designware.h"
|
||||
|
||||
#define DESIGNWARE_PCIE_PORT_LINK_CONTROL 0x710
|
||||
#define DESIGNWARE_PCIE_PHY_DEBUG_R1 0x72C
|
||||
#define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4)
|
||||
#define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
|
||||
#define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE BIT(17)
|
||||
#define DESIGNWARE_PCIE_MSI_ADDR_LO 0x820
|
||||
#define DESIGNWARE_PCIE_MSI_ADDR_HI 0x824
|
||||
#define DESIGNWARE_PCIE_MSI_INTR0_ENABLE 0x828
|
||||
#define DESIGNWARE_PCIE_MSI_INTR0_MASK 0x82C
|
||||
#define DESIGNWARE_PCIE_MSI_INTR0_STATUS 0x830
|
||||
#define DESIGNWARE_PCIE_ATU_VIEWPORT 0x900
|
||||
#define DESIGNWARE_PCIE_ATU_REGION_INBOUND BIT(31)
|
||||
#define DESIGNWARE_PCIE_ATU_CR1 0x904
|
||||
#define DESIGNWARE_PCIE_ATU_TYPE_MEM (0x0 << 0)
|
||||
#define DESIGNWARE_PCIE_ATU_CR2 0x908
|
||||
#define DESIGNWARE_PCIE_ATU_ENABLE BIT(31)
|
||||
#define DESIGNWARE_PCIE_ATU_LOWER_BASE 0x90C
|
||||
#define DESIGNWARE_PCIE_ATU_UPPER_BASE 0x910
|
||||
#define DESIGNWARE_PCIE_ATU_LIMIT 0x914
|
||||
#define DESIGNWARE_PCIE_ATU_LOWER_TARGET 0x918
|
||||
#define DESIGNWARE_PCIE_ATU_BUS(x) (((x) >> 24) & 0xff)
|
||||
#define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff)
|
||||
#define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
static DesignwarePCIEHost *
|
||||
designware_pcie_root_to_host(DesignwarePCIERoot *root)
|
||||
{
|
||||
BusState *bus = qdev_get_parent_bus(DEVICE(root));
|
||||
return DESIGNWARE_PCIE_HOST(bus->parent);
|
||||
}
|
||||
|
||||
static void designware_pcie_root_msi_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned len)
|
||||
{
|
||||
DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
|
||||
DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
|
||||
|
||||
root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable;
|
||||
|
||||
if (root->msi.intr[0].status & ~root->msi.intr[0].mask) {
|
||||
qemu_set_irq(host->pci.irqs[0], 1);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps designware_pci_host_msi_ops = {
|
||||
.write = designware_pcie_root_msi_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root)
|
||||
|
||||
{
|
||||
MemoryRegion *mem = &root->msi.iomem;
|
||||
const uint64_t base = root->msi.base;
|
||||
const bool enable = root->msi.intr[0].enable;
|
||||
|
||||
memory_region_set_address(mem, base);
|
||||
memory_region_set_enabled(mem, enable);
|
||||
}
|
||||
|
||||
static DesignwarePCIEViewport *
|
||||
designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root)
|
||||
{
|
||||
const unsigned int idx = root->atu_viewport & 0xF;
|
||||
const unsigned int dir =
|
||||
!!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND);
|
||||
return &root->viewports[dir][idx];
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len)
|
||||
{
|
||||
DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
|
||||
DesignwarePCIEViewport *viewport =
|
||||
designware_pcie_root_get_current_viewport(root);
|
||||
|
||||
uint32_t val;
|
||||
|
||||
switch (address) {
|
||||
case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
|
||||
/*
|
||||
* Linux guest uses this register only to configure number of
|
||||
* PCIE lane (which in our case is irrelevant) and doesn't
|
||||
* really care about the value it reads from this register
|
||||
*/
|
||||
val = 0xDEADBEEF;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
|
||||
/*
|
||||
* To make sure that any code in guest waiting for speed
|
||||
* change does not time out we always report
|
||||
* PORT_LOGIC_SPEED_CHANGE as set
|
||||
*/
|
||||
val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_LO:
|
||||
val = root->msi.base;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_HI:
|
||||
val = root->msi.base >> 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
|
||||
val = root->msi.intr[0].enable;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_MASK:
|
||||
val = root->msi.intr[0].mask;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
|
||||
val = root->msi.intr[0].status;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_PHY_DEBUG_R1:
|
||||
val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_VIEWPORT:
|
||||
val = root->atu_viewport;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LOWER_BASE:
|
||||
val = viewport->base;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_UPPER_BASE:
|
||||
val = viewport->base >> 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
|
||||
val = viewport->target;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
|
||||
val = viewport->target >> 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LIMIT:
|
||||
val = viewport->limit;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_CR1:
|
||||
case DESIGNWARE_PCIE_ATU_CR2: /* FALLTHROUGH */
|
||||
val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) /
|
||||
sizeof(uint32_t)];
|
||||
break;
|
||||
|
||||
default:
|
||||
val = pci_default_read_config(d, address, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr,
|
||||
uint64_t *val, unsigned len)
|
||||
{
|
||||
DesignwarePCIEViewport *viewport = opaque;
|
||||
DesignwarePCIERoot *root = viewport->root;
|
||||
|
||||
const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target);
|
||||
const uint8_t devfn = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target);
|
||||
PCIBus *pcibus = pci_get_bus(PCI_DEVICE(root));
|
||||
PCIDevice *pcidev = pci_find_device(pcibus, busnum, devfn);
|
||||
|
||||
if (pcidev) {
|
||||
addr &= pci_config_size(pcidev) - 1;
|
||||
|
||||
if (val) {
|
||||
pci_host_config_write_common(pcidev, addr,
|
||||
pci_config_size(pcidev),
|
||||
*val, len);
|
||||
} else {
|
||||
return pci_host_config_read_common(pcidev, addr,
|
||||
pci_config_size(pcidev),
|
||||
len);
|
||||
}
|
||||
}
|
||||
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr,
|
||||
unsigned len)
|
||||
{
|
||||
return designware_pcie_root_data_access(opaque, addr, NULL, len);
|
||||
}
|
||||
|
||||
static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned len)
|
||||
{
|
||||
designware_pcie_root_data_access(opaque, addr, &val, len);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps designware_pci_host_conf_ops = {
|
||||
.read = designware_pcie_root_data_read,
|
||||
.write = designware_pcie_root_data_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
|
||||
DesignwarePCIEViewport *viewport)
|
||||
{
|
||||
const uint64_t target = viewport->target;
|
||||
const uint64_t base = viewport->base;
|
||||
const uint64_t size = (uint64_t)viewport->limit - base + 1;
|
||||
const bool enabled = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE;
|
||||
|
||||
MemoryRegion *current, *other;
|
||||
|
||||
if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) {
|
||||
current = &viewport->mem;
|
||||
other = &viewport->cfg;
|
||||
memory_region_set_alias_offset(current, target);
|
||||
} else {
|
||||
current = &viewport->cfg;
|
||||
other = &viewport->mem;
|
||||
}
|
||||
|
||||
/*
|
||||
* An outbound viewport can be reconfigure from being MEM to CFG,
|
||||
* to account for that we disable the "other" memory region that
|
||||
* becomes unused due to that fact.
|
||||
*/
|
||||
memory_region_set_enabled(other, false);
|
||||
if (enabled) {
|
||||
memory_region_set_size(current, size);
|
||||
memory_region_set_address(current, base);
|
||||
}
|
||||
memory_region_set_enabled(current, enabled);
|
||||
}
|
||||
|
||||
static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
|
||||
DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
|
||||
DesignwarePCIEViewport *viewport =
|
||||
designware_pcie_root_get_current_viewport(root);
|
||||
|
||||
switch (address) {
|
||||
case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
|
||||
case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
|
||||
case DESIGNWARE_PCIE_PHY_DEBUG_R1:
|
||||
/* No-op */
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_LO:
|
||||
root->msi.base &= 0xFFFFFFFF00000000ULL;
|
||||
root->msi.base |= val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_HI:
|
||||
root->msi.base &= 0x00000000FFFFFFFFULL;
|
||||
root->msi.base |= (uint64_t)val << 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: {
|
||||
const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val;
|
||||
|
||||
root->msi.intr[0].enable = val;
|
||||
|
||||
if (update_msi_mapping) {
|
||||
designware_pcie_root_update_msi_mapping(root);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_MASK:
|
||||
root->msi.intr[0].mask = val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
|
||||
root->msi.intr[0].status ^= val;
|
||||
if (!root->msi.intr[0].status) {
|
||||
qemu_set_irq(host->pci.irqs[0], 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_VIEWPORT:
|
||||
root->atu_viewport = val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LOWER_BASE:
|
||||
viewport->base &= 0xFFFFFFFF00000000ULL;
|
||||
viewport->base |= val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_UPPER_BASE:
|
||||
viewport->base &= 0x00000000FFFFFFFFULL;
|
||||
viewport->base |= (uint64_t)val << 32;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
|
||||
viewport->target &= 0xFFFFFFFF00000000ULL;
|
||||
viewport->target |= val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
|
||||
viewport->target &= 0x00000000FFFFFFFFULL;
|
||||
viewport->target |= val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_LIMIT:
|
||||
viewport->limit = val;
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_ATU_CR1:
|
||||
viewport->cr[0] = val;
|
||||
break;
|
||||
case DESIGNWARE_PCIE_ATU_CR2:
|
||||
viewport->cr[1] = val;
|
||||
designware_pcie_update_viewport(root, viewport);
|
||||
break;
|
||||
|
||||
default:
|
||||
pci_bridge_write_config(d, address, val, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static char *designware_pcie_viewport_name(const char *direction,
|
||||
unsigned int i,
|
||||
const char *type)
|
||||
{
|
||||
return g_strdup_printf("PCI %s Viewport %u [%s]",
|
||||
direction, i, type);
|
||||
}
|
||||
|
||||
static void designware_pcie_root_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
|
||||
DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
|
||||
MemoryRegion *address_space = &host->pci.memory;
|
||||
PCIBridge *br = PCI_BRIDGE(dev);
|
||||
DesignwarePCIEViewport *viewport;
|
||||
/*
|
||||
* Dummy values used for initial configuration of MemoryRegions
|
||||
* that belong to a given viewport
|
||||
*/
|
||||
const hwaddr dummy_offset = 0;
|
||||
const uint64_t dummy_size = 4;
|
||||
size_t i;
|
||||
|
||||
br->bus_name = "dw-pcie";
|
||||
|
||||
pci_set_word(dev->config + PCI_COMMAND,
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
|
||||
pci_config_set_interrupt_pin(dev->config, 1);
|
||||
pci_bridge_initfn(dev, TYPE_PCIE_BUS);
|
||||
|
||||
pcie_port_init_reg(dev);
|
||||
|
||||
pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT,
|
||||
0, &error_fatal);
|
||||
|
||||
msi_nonbroken = true;
|
||||
msi_init(dev, 0x50, 32, true, true, &error_fatal);
|
||||
|
||||
for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) {
|
||||
MemoryRegion *source, *destination, *mem;
|
||||
const char *direction;
|
||||
char *name;
|
||||
|
||||
viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
|
||||
viewport->inbound = true;
|
||||
viewport->base = 0x0000000000000000ULL;
|
||||
viewport->target = 0x0000000000000000ULL;
|
||||
viewport->limit = UINT32_MAX;
|
||||
viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
|
||||
|
||||
source = &host->pci.address_space_root;
|
||||
destination = get_system_memory();
|
||||
direction = "Inbound";
|
||||
|
||||
/*
|
||||
* Configure MemoryRegion implementing PCI -> CPU memory
|
||||
* access
|
||||
*/
|
||||
mem = &viewport->mem;
|
||||
name = designware_pcie_viewport_name(direction, i, "MEM");
|
||||
memory_region_init_alias(mem, OBJECT(root), name, destination,
|
||||
dummy_offset, dummy_size);
|
||||
memory_region_add_subregion_overlap(source, dummy_offset, mem, -1);
|
||||
memory_region_set_enabled(mem, false);
|
||||
g_free(name);
|
||||
|
||||
viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i];
|
||||
viewport->root = root;
|
||||
viewport->inbound = false;
|
||||
viewport->base = 0x0000000000000000ULL;
|
||||
viewport->target = 0x0000000000000000ULL;
|
||||
viewport->limit = UINT32_MAX;
|
||||
viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
|
||||
|
||||
destination = &host->pci.memory;
|
||||
direction = "Outbound";
|
||||
source = get_system_memory();
|
||||
|
||||
/*
|
||||
* Configure MemoryRegion implementing CPU -> PCI memory
|
||||
* access
|
||||
*/
|
||||
mem = &viewport->mem;
|
||||
name = designware_pcie_viewport_name(direction, i, "MEM");
|
||||
memory_region_init_alias(mem, OBJECT(root), name, destination,
|
||||
dummy_offset, dummy_size);
|
||||
memory_region_add_subregion(source, dummy_offset, mem);
|
||||
memory_region_set_enabled(mem, false);
|
||||
g_free(name);
|
||||
|
||||
/*
|
||||
* Configure MemoryRegion implementing access to configuration
|
||||
* space
|
||||
*/
|
||||
mem = &viewport->cfg;
|
||||
name = designware_pcie_viewport_name(direction, i, "CFG");
|
||||
memory_region_init_io(&viewport->cfg, OBJECT(root),
|
||||
&designware_pci_host_conf_ops,
|
||||
viewport, name, dummy_size);
|
||||
memory_region_add_subregion(source, dummy_offset, mem);
|
||||
memory_region_set_enabled(mem, false);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* If no inbound iATU windows are configured, HW defaults to
|
||||
* letting inbound TLPs to pass in. We emulate that by exlicitly
|
||||
* configuring first inbound window to cover all of target's
|
||||
* address space.
|
||||
*
|
||||
* NOTE: This will not work correctly for the case when first
|
||||
* configured inbound window is window 0
|
||||
*/
|
||||
viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0];
|
||||
viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE;
|
||||
designware_pcie_update_viewport(root, viewport);
|
||||
|
||||
memory_region_init_io(&root->msi.iomem, OBJECT(root),
|
||||
&designware_pci_host_msi_ops,
|
||||
root, "pcie-msi", 0x4);
|
||||
/*
|
||||
* We initially place MSI interrupt I/O region a adress 0 and
|
||||
* disable it. It'll be later moved to correct offset and enabled
|
||||
* in designware_pcie_root_update_msi_mapping() as a part of
|
||||
* initialization done by guest OS
|
||||
*/
|
||||
memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem);
|
||||
memory_region_set_enabled(&root->msi.iomem, false);
|
||||
}
|
||||
|
||||
static void designware_pcie_set_irq(void *opaque, int irq_num, int level)
|
||||
{
|
||||
DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque);
|
||||
|
||||
qemu_set_irq(host->pci.irqs[irq_num], level);
|
||||
}
|
||||
|
||||
static const char *
|
||||
designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus)
|
||||
{
|
||||
return "0000:00";
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_msi_bank = {
|
||||
.name = "designware-pcie-msi-bank",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(enable, DesignwarePCIEMSIBank),
|
||||
VMSTATE_UINT32(mask, DesignwarePCIEMSIBank),
|
||||
VMSTATE_UINT32(status, DesignwarePCIEMSIBank),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_msi = {
|
||||
.name = "designware-pcie-msi",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(base, DesignwarePCIEMSI),
|
||||
VMSTATE_STRUCT_ARRAY(intr,
|
||||
DesignwarePCIEMSI,
|
||||
DESIGNWARE_PCIE_NUM_MSI_BANKS,
|
||||
1,
|
||||
vmstate_designware_pcie_msi_bank,
|
||||
DesignwarePCIEMSIBank),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_viewport = {
|
||||
.name = "designware-pcie-viewport",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(base, DesignwarePCIEViewport),
|
||||
VMSTATE_UINT64(target, DesignwarePCIEViewport),
|
||||
VMSTATE_UINT32(limit, DesignwarePCIEViewport),
|
||||
VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_root = {
|
||||
.name = "designware-pcie-root",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
|
||||
VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot),
|
||||
VMSTATE_STRUCT_2DARRAY(viewports,
|
||||
DesignwarePCIERoot,
|
||||
2,
|
||||
DESIGNWARE_PCIE_NUM_VIEWPORTS,
|
||||
1,
|
||||
vmstate_designware_pcie_viewport,
|
||||
DesignwarePCIEViewport),
|
||||
VMSTATE_STRUCT(msi,
|
||||
DesignwarePCIERoot,
|
||||
1,
|
||||
vmstate_designware_pcie_msi,
|
||||
DesignwarePCIEMSI),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void designware_pcie_root_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_SYNOPSYS;
|
||||
k->device_id = 0xABCD;
|
||||
k->revision = 0;
|
||||
k->class_id = PCI_CLASS_BRIDGE_PCI;
|
||||
k->is_bridge = true;
|
||||
k->exit = pci_bridge_exitfn;
|
||||
k->realize = designware_pcie_root_realize;
|
||||
k->config_read = designware_pcie_root_config_read;
|
||||
k->config_write = designware_pcie_root_config_write;
|
||||
|
||||
dc->reset = pci_bridge_reset;
|
||||
/*
|
||||
* PCI-facing part of the host bridge, not usable without the
|
||||
* host-facing part, which can't be device_add'ed, yet.
|
||||
*/
|
||||
dc->user_creatable = false;
|
||||
dc->vmsd = &vmstate_designware_pcie_root;
|
||||
}
|
||||
|
||||
static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr,
|
||||
unsigned int size)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
|
||||
PCIDevice *device = pci_find_device(pci->bus, 0, 0);
|
||||
|
||||
return pci_host_config_read_common(device,
|
||||
addr,
|
||||
pci_config_size(device),
|
||||
size);
|
||||
}
|
||||
|
||||
static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned int size)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
|
||||
PCIDevice *device = pci_find_device(pci->bus, 0, 0);
|
||||
|
||||
return pci_host_config_write_common(device,
|
||||
addr,
|
||||
pci_config_size(device),
|
||||
val, size);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps designware_pci_mmio_ops = {
|
||||
.read = designware_pcie_host_mmio_read,
|
||||
.write = designware_pcie_host_mmio_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
/*
|
||||
* Our device would not work correctly if the guest was doing
|
||||
* unaligned access. This might not be a limitation on the real
|
||||
* device but in practice there is no reason for a guest to access
|
||||
* this device unaligned.
|
||||
*/
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
.unaligned = false,
|
||||
},
|
||||
};
|
||||
|
||||
static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque,
|
||||
int devfn)
|
||||
{
|
||||
DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque);
|
||||
|
||||
return &s->pci.address_space;
|
||||
}
|
||||
|
||||
static void designware_pcie_host_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||
DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) {
|
||||
sysbus_init_irq(sbd, &s->pci.irqs[i]);
|
||||
}
|
||||
|
||||
memory_region_init_io(&s->mmio,
|
||||
OBJECT(s),
|
||||
&designware_pci_mmio_ops,
|
||||
s,
|
||||
"pcie.reg", 4 * 1024);
|
||||
sysbus_init_mmio(sbd, &s->mmio);
|
||||
|
||||
memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio", 16);
|
||||
memory_region_init(&s->pci.memory, OBJECT(s),
|
||||
"pcie-bus-memory",
|
||||
UINT64_MAX);
|
||||
|
||||
pci->bus = pci_register_root_bus(dev, "pcie",
|
||||
designware_pcie_set_irq,
|
||||
pci_swizzle_map_irq_fn,
|
||||
s,
|
||||
&s->pci.memory,
|
||||
&s->pci.io,
|
||||
0, 4,
|
||||
TYPE_PCIE_BUS);
|
||||
|
||||
memory_region_init(&s->pci.address_space_root,
|
||||
OBJECT(s),
|
||||
"pcie-bus-address-space-root",
|
||||
UINT64_MAX);
|
||||
memory_region_add_subregion(&s->pci.address_space_root,
|
||||
0x0, &s->pci.memory);
|
||||
address_space_init(&s->pci.address_space,
|
||||
&s->pci.address_space_root,
|
||||
"pcie-bus-address-space");
|
||||
pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s);
|
||||
|
||||
qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
|
||||
qdev_init_nofail(DEVICE(&s->root));
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_designware_pcie_host = {
|
||||
.name = "designware-pcie-host",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(root,
|
||||
DesignwarePCIEHost,
|
||||
1,
|
||||
vmstate_designware_pcie_root,
|
||||
DesignwarePCIERoot),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void designware_pcie_host_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||
|
||||
hc->root_bus_path = designware_pcie_host_root_bus_path;
|
||||
dc->realize = designware_pcie_host_realize;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->fw_name = "pci";
|
||||
dc->vmsd = &vmstate_designware_pcie_host;
|
||||
}
|
||||
|
||||
static void designware_pcie_host_init(Object *obj)
|
||||
{
|
||||
DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj);
|
||||
DesignwarePCIERoot *root = &s->root;
|
||||
|
||||
object_initialize(root, sizeof(*root), TYPE_DESIGNWARE_PCIE_ROOT);
|
||||
object_property_add_child(obj, "root", OBJECT(root), NULL);
|
||||
qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
|
||||
qdev_prop_set_bit(DEVICE(root), "multifunction", false);
|
||||
}
|
||||
|
||||
static const TypeInfo designware_pcie_root_info = {
|
||||
.name = TYPE_DESIGNWARE_PCIE_ROOT,
|
||||
.parent = TYPE_PCI_BRIDGE,
|
||||
.instance_size = sizeof(DesignwarePCIERoot),
|
||||
.class_init = designware_pcie_root_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static const TypeInfo designware_pcie_host_info = {
|
||||
.name = TYPE_DESIGNWARE_PCIE_HOST,
|
||||
.parent = TYPE_PCI_HOST_BRIDGE,
|
||||
.instance_size = sizeof(DesignwarePCIEHost),
|
||||
.instance_init = designware_pcie_host_init,
|
||||
.class_init = designware_pcie_host_class_init,
|
||||
};
|
||||
|
||||
static void designware_pcie_register(void)
|
||||
{
|
||||
type_register_static(&designware_pcie_root_info);
|
||||
type_register_static(&designware_pcie_host_info);
|
||||
}
|
||||
type_init(designware_pcie_register)
|
||||
11
hw/riscv/Makefile.objs
Normal file
11
hw/riscv/Makefile.objs
Normal file
@@ -0,0 +1,11 @@
|
||||
obj-y += riscv_htif.o
|
||||
obj-y += riscv_hart.o
|
||||
obj-y += sifive_e.o
|
||||
obj-y += sifive_clint.o
|
||||
obj-y += sifive_prci.o
|
||||
obj-y += sifive_plic.o
|
||||
obj-y += sifive_test.o
|
||||
obj-y += sifive_u.o
|
||||
obj-y += sifive_uart.o
|
||||
obj-y += spike.o
|
||||
obj-y += virt.o
|
||||
89
hw/riscv/riscv_hart.c
Normal file
89
hw/riscv/riscv_hart.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* QEMU RISCV Hart Array
|
||||
*
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
*
|
||||
* Holds the state of a heterogenous array of RISC-V harts
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
|
||||
static Property riscv_harts_props[] = {
|
||||
DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
|
||||
DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void riscv_harts_cpu_reset(void *opaque)
|
||||
{
|
||||
RISCVCPU *cpu = opaque;
|
||||
cpu_reset(CPU(cpu));
|
||||
}
|
||||
|
||||
static void riscv_harts_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
RISCVHartArrayState *s = RISCV_HART_ARRAY(dev);
|
||||
Error *err = NULL;
|
||||
int n;
|
||||
|
||||
s->harts = g_new0(RISCVCPU, s->num_harts);
|
||||
|
||||
for (n = 0; n < s->num_harts; n++) {
|
||||
|
||||
object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type);
|
||||
s->harts[n].env.mhartid = n;
|
||||
object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]),
|
||||
&error_abort);
|
||||
qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]);
|
||||
object_property_set_bool(OBJECT(&s->harts[n]), true,
|
||||
"realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_harts_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->props = riscv_harts_props;
|
||||
dc->realize = riscv_harts_realize;
|
||||
}
|
||||
|
||||
static void riscv_harts_init(Object *obj)
|
||||
{
|
||||
/* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_harts_info = {
|
||||
.name = TYPE_RISCV_HART_ARRAY,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RISCVHartArrayState),
|
||||
.instance_init = riscv_harts_init,
|
||||
.class_init = riscv_harts_class_init,
|
||||
};
|
||||
|
||||
static void riscv_harts_register_types(void)
|
||||
{
|
||||
type_register_static(&riscv_harts_info);
|
||||
}
|
||||
|
||||
type_init(riscv_harts_register_types)
|
||||
258
hw/riscv/riscv_htif.c
Normal file
258
hw/riscv/riscv_htif.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* QEMU RISC-V Host Target Interface (HTIF) Emulation
|
||||
*
|
||||
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
||||
* Copyright (c) 2017-2018 SiFive, Inc.
|
||||
*
|
||||
* This provides HTIF device emulation for QEMU. At the moment this allows
|
||||
* for identical copies of bbl/linux to run on both spike and QEMU.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "chardev/char.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "hw/riscv/riscv_htif.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define RISCV_DEBUG_HTIF 0
|
||||
#define HTIF_DEBUG(fmt, ...) \
|
||||
do { \
|
||||
if (RISCV_DEBUG_HTIF) { \
|
||||
qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static uint64_t fromhost_addr, tohost_addr;
|
||||
|
||||
void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
|
||||
uint64_t st_size)
|
||||
{
|
||||
if (strcmp("fromhost", st_name) == 0) {
|
||||
fromhost_addr = st_value;
|
||||
if (st_size != 8) {
|
||||
error_report("HTIF fromhost must be 8 bytes");
|
||||
exit(1);
|
||||
}
|
||||
} else if (strcmp("tohost", st_name) == 0) {
|
||||
tohost_addr = st_value;
|
||||
if (st_size != 8) {
|
||||
error_report("HTIF tohost must be 8 bytes");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the char dev to see if HTIF is ready to accept input.
|
||||
*/
|
||||
static int htif_can_recv(void *opaque)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the char dev to supply input to HTIF console.
|
||||
* We assume that we will receive one character at a time.
|
||||
*/
|
||||
static void htif_recv(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
HTIFState *htifstate = opaque;
|
||||
|
||||
if (size != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO - we need to check whether mfromhost is zero which indicates
|
||||
the device is ready to receive. The current implementation
|
||||
will drop characters */
|
||||
|
||||
uint64_t val_written = htifstate->pending_read;
|
||||
uint64_t resp = 0x100 | *buf;
|
||||
|
||||
htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the char dev to supply special events to the HTIF console.
|
||||
* Not used for HTIF.
|
||||
*/
|
||||
static void htif_event(void *opaque, int event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static int htif_be_change(void *opaque)
|
||||
{
|
||||
HTIFState *s = opaque;
|
||||
|
||||
qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
|
||||
htif_be_change, s, NULL, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written)
|
||||
{
|
||||
uint8_t device = val_written >> 56;
|
||||
uint8_t cmd = val_written >> 48;
|
||||
uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
|
||||
int resp = 0;
|
||||
|
||||
HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64
|
||||
" -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload);
|
||||
|
||||
/*
|
||||
* Currently, there is a fixed mapping of devices:
|
||||
* 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy)
|
||||
* 1: Console
|
||||
*/
|
||||
if (unlikely(device == 0x0)) {
|
||||
/* frontend syscall handler, shutdown and exit code support */
|
||||
if (cmd == 0x0) {
|
||||
if (payload & 0x1) {
|
||||
/* exit code */
|
||||
int exit_code = payload >> 1;
|
||||
exit(exit_code);
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n");
|
||||
}
|
||||
} else {
|
||||
qemu_log("HTIF device %d: unknown command\n", device);
|
||||
}
|
||||
} else if (likely(device == 0x1)) {
|
||||
/* HTIF Console */
|
||||
if (cmd == 0x0) {
|
||||
/* this should be a queue, but not yet implemented as such */
|
||||
htifstate->pending_read = val_written;
|
||||
htifstate->env->mtohost = 0; /* clear to indicate we read */
|
||||
return;
|
||||
} else if (cmd == 0x1) {
|
||||
qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1);
|
||||
resp = 0x100 | (uint8_t)payload;
|
||||
} else {
|
||||
qemu_log("HTIF device %d: unknown command\n", device);
|
||||
}
|
||||
} else {
|
||||
qemu_log("HTIF unknown device or command\n");
|
||||
HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64
|
||||
" payload: %016" PRIx64, device, cmd, payload & 0xFF, payload);
|
||||
}
|
||||
/*
|
||||
* - latest bbl does not set fromhost to 0 if there is a value in tohost
|
||||
* - with this code enabled, qemu hangs waiting for fromhost to go to 0
|
||||
* - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10
|
||||
* - HTIF needs protocol documentation and a more complete state machine
|
||||
|
||||
while (!htifstate->fromhost_inprogress &&
|
||||
htifstate->env->mfromhost != 0x0) {
|
||||
}
|
||||
*/
|
||||
htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
|
||||
htifstate->env->mtohost = 0; /* clear to indicate we read */
|
||||
}
|
||||
|
||||
#define TOHOST_OFFSET1 (htifstate->tohost_offset)
|
||||
#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4)
|
||||
#define FROMHOST_OFFSET1 (htifstate->fromhost_offset)
|
||||
#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4)
|
||||
|
||||
/* CPU wants to read an HTIF register */
|
||||
static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
HTIFState *htifstate = opaque;
|
||||
if (addr == TOHOST_OFFSET1) {
|
||||
return htifstate->env->mtohost & 0xFFFFFFFF;
|
||||
} else if (addr == TOHOST_OFFSET2) {
|
||||
return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF;
|
||||
} else if (addr == FROMHOST_OFFSET1) {
|
||||
return htifstate->env->mfromhost & 0xFFFFFFFF;
|
||||
} else if (addr == FROMHOST_OFFSET2) {
|
||||
return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF;
|
||||
} else {
|
||||
qemu_log("Invalid htif read: address %016" PRIx64 "\n",
|
||||
(uint64_t)addr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* CPU wrote to an HTIF register */
|
||||
static void htif_mm_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
HTIFState *htifstate = opaque;
|
||||
if (addr == TOHOST_OFFSET1) {
|
||||
if (htifstate->env->mtohost == 0x0) {
|
||||
htifstate->allow_tohost = 1;
|
||||
htifstate->env->mtohost = value & 0xFFFFFFFF;
|
||||
} else {
|
||||
htifstate->allow_tohost = 0;
|
||||
}
|
||||
} else if (addr == TOHOST_OFFSET2) {
|
||||
if (htifstate->allow_tohost) {
|
||||
htifstate->env->mtohost |= value << 32;
|
||||
htif_handle_tohost_write(htifstate, htifstate->env->mtohost);
|
||||
}
|
||||
} else if (addr == FROMHOST_OFFSET1) {
|
||||
htifstate->fromhost_inprogress = 1;
|
||||
htifstate->env->mfromhost = value & 0xFFFFFFFF;
|
||||
} else if (addr == FROMHOST_OFFSET2) {
|
||||
htifstate->env->mfromhost |= value << 32;
|
||||
htifstate->fromhost_inprogress = 0;
|
||||
} else {
|
||||
qemu_log("Invalid htif write: address %016" PRIx64 "\n",
|
||||
(uint64_t)addr);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps htif_mm_ops = {
|
||||
.read = htif_mm_read,
|
||||
.write = htif_mm_write,
|
||||
};
|
||||
|
||||
HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
|
||||
CPURISCVState *env, Chardev *chr)
|
||||
{
|
||||
uint64_t base = MIN(tohost_addr, fromhost_addr);
|
||||
uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base;
|
||||
uint64_t tohost_offset = tohost_addr - base;
|
||||
uint64_t fromhost_offset = fromhost_addr - base;
|
||||
|
||||
HTIFState *s = g_malloc0(sizeof(HTIFState));
|
||||
s->address_space = address_space;
|
||||
s->main_mem = main_mem;
|
||||
s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem);
|
||||
s->env = env;
|
||||
s->tohost_offset = tohost_offset;
|
||||
s->fromhost_offset = fromhost_offset;
|
||||
s->pending_read = 0;
|
||||
s->allow_tohost = 0;
|
||||
s->fromhost_inprogress = 0;
|
||||
qemu_chr_fe_init(&s->chr, chr, &error_abort);
|
||||
qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
|
||||
htif_be_change, s, NULL, true);
|
||||
if (base) {
|
||||
memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
|
||||
TYPE_HTIF_UART, size);
|
||||
memory_region_add_subregion(address_space, base, &s->mmio);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
254
hw/riscv/sifive_clint.c
Normal file
254
hw/riscv/sifive_clint.c
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* SiFive CLINT (Core Local Interruptor)
|
||||
*
|
||||
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
*
|
||||
* This provides real-time clock, timer and interprocessor interrupts.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/sifive_clint.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
/* See: riscv-pk/machine/sbi_entry.S and arch/riscv/kernel/time.c */
|
||||
#define TIMER_FREQ (10 * 1000 * 1000)
|
||||
|
||||
static uint64_t cpu_riscv_read_rtc(void)
|
||||
{
|
||||
return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), TIMER_FREQ,
|
||||
NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when timecmp is written to update the QEMU timer or immediately
|
||||
* trigger timer interrupt if mtimecmp <= current timer value.
|
||||
*/
|
||||
static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value)
|
||||
{
|
||||
uint64_t next;
|
||||
uint64_t diff;
|
||||
|
||||
uint64_t rtc_r = cpu_riscv_read_rtc();
|
||||
|
||||
cpu->env.timecmp = value;
|
||||
if (cpu->env.timecmp <= rtc_r) {
|
||||
/* if we're setting an MTIMECMP value in the "past",
|
||||
immediately raise the timer interrupt */
|
||||
riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* otherwise, set up the future timer interrupt */
|
||||
riscv_set_local_interrupt(cpu, MIP_MTIP, 0);
|
||||
diff = cpu->env.timecmp - rtc_r;
|
||||
/* back to ns (note args switched in muldiv64) */
|
||||
next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
muldiv64(diff, NANOSECONDS_PER_SECOND, TIMER_FREQ);
|
||||
timer_mod(cpu->env.timer, next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback used when the timer set using timer_mod expires.
|
||||
* Should raise the timer interrupt line
|
||||
*/
|
||||
static void sifive_clint_timer_cb(void *opaque)
|
||||
{
|
||||
RISCVCPU *cpu = opaque;
|
||||
riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
|
||||
}
|
||||
|
||||
/* CPU wants to read rtc or timecmp register */
|
||||
static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
SiFiveCLINTState *clint = opaque;
|
||||
if (addr >= clint->sip_base &&
|
||||
addr < clint->sip_base + (clint->num_harts << 2)) {
|
||||
size_t hartid = (addr - clint->sip_base) >> 2;
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||
} else if ((addr & 0x3) == 0) {
|
||||
return (env->mip & MIP_MSIP) > 0;
|
||||
} else {
|
||||
error_report("clint: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
} else if (addr >= clint->timecmp_base &&
|
||||
addr < clint->timecmp_base + (clint->num_harts << 3)) {
|
||||
size_t hartid = (addr - clint->timecmp_base) >> 3;
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||
} else if ((addr & 0x7) == 0) {
|
||||
/* timecmp_lo */
|
||||
uint64_t timecmp = env->timecmp;
|
||||
return timecmp & 0xFFFFFFFF;
|
||||
} else if ((addr & 0x7) == 4) {
|
||||
/* timecmp_hi */
|
||||
uint64_t timecmp = env->timecmp;
|
||||
return (timecmp >> 32) & 0xFFFFFFFF;
|
||||
} else {
|
||||
error_report("clint: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
} else if (addr == clint->time_base) {
|
||||
/* time_lo */
|
||||
return cpu_riscv_read_rtc() & 0xFFFFFFFF;
|
||||
} else if (addr == clint->time_base + 4) {
|
||||
/* time_hi */
|
||||
return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
error_report("clint: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CPU wrote to rtc or timecmp register */
|
||||
static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
SiFiveCLINTState *clint = opaque;
|
||||
|
||||
if (addr >= clint->sip_base &&
|
||||
addr < clint->sip_base + (clint->num_harts << 2)) {
|
||||
size_t hartid = (addr - clint->sip_base) >> 2;
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||
} else if ((addr & 0x3) == 0) {
|
||||
riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0);
|
||||
} else {
|
||||
error_report("clint: invalid sip write: %08x", (uint32_t)addr);
|
||||
}
|
||||
return;
|
||||
} else if (addr >= clint->timecmp_base &&
|
||||
addr < clint->timecmp_base + (clint->num_harts << 3)) {
|
||||
size_t hartid = (addr - clint->timecmp_base) >> 3;
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||
} else if ((addr & 0x7) == 0) {
|
||||
/* timecmp_lo */
|
||||
uint64_t timecmp = env->timecmp;
|
||||
sifive_clint_write_timecmp(RISCV_CPU(cpu),
|
||||
timecmp << 32 | (value & 0xFFFFFFFF));
|
||||
return;
|
||||
} else if ((addr & 0x7) == 4) {
|
||||
/* timecmp_hi */
|
||||
uint64_t timecmp = env->timecmp;
|
||||
sifive_clint_write_timecmp(RISCV_CPU(cpu),
|
||||
value << 32 | (timecmp & 0xFFFFFFFF));
|
||||
} else {
|
||||
error_report("clint: invalid timecmp write: %08x", (uint32_t)addr);
|
||||
}
|
||||
return;
|
||||
} else if (addr == clint->time_base) {
|
||||
/* time_lo */
|
||||
error_report("clint: time_lo write not implemented");
|
||||
return;
|
||||
} else if (addr == clint->time_base + 4) {
|
||||
/* time_hi */
|
||||
error_report("clint: time_hi write not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
error_report("clint: invalid write: %08x", (uint32_t)addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sifive_clint_ops = {
|
||||
.read = sifive_clint_read,
|
||||
.write = sifive_clint_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static Property sifive_clint_properties[] = {
|
||||
DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0),
|
||||
DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0),
|
||||
DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0),
|
||||
DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0),
|
||||
DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void sifive_clint_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SiFiveCLINTState *s = SIFIVE_CLINT(dev);
|
||||
memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s,
|
||||
TYPE_SIFIVE_CLINT, s->aperture_size);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
|
||||
}
|
||||
|
||||
static void sifive_clint_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = sifive_clint_realize;
|
||||
dc->props = sifive_clint_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_clint_info = {
|
||||
.name = TYPE_SIFIVE_CLINT,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFiveCLINTState),
|
||||
.class_init = sifive_clint_class_init,
|
||||
};
|
||||
|
||||
static void sifive_clint_register_types(void)
|
||||
{
|
||||
type_register_static(&sifive_clint_info);
|
||||
}
|
||||
|
||||
type_init(sifive_clint_register_types)
|
||||
|
||||
|
||||
/*
|
||||
* Create CLINT device.
|
||||
*/
|
||||
DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts,
|
||||
uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_harts; i++) {
|
||||
CPUState *cpu = qemu_get_cpu(i);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
continue;
|
||||
}
|
||||
env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
&sifive_clint_timer_cb, cpu);
|
||||
env->timecmp = 0;
|
||||
}
|
||||
|
||||
DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_CLINT);
|
||||
qdev_prop_set_uint32(dev, "num-harts", num_harts);
|
||||
qdev_prop_set_uint32(dev, "sip-base", sip_base);
|
||||
qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
|
||||
qdev_prop_set_uint32(dev, "time-base", time_base);
|
||||
qdev_prop_set_uint32(dev, "aperture-size", size);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
|
||||
return dev;
|
||||
}
|
||||
234
hw/riscv/sifive_e.c
Normal file
234
hw/riscv/sifive_e.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* QEMU RISC-V Board Compatible with SiFive Freedom E SDK
|
||||
*
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
*
|
||||
* Provides a board compatible with the SiFive Freedom E SDK:
|
||||
*
|
||||
* 0) UART
|
||||
* 1) CLINT (Core Level Interruptor)
|
||||
* 2) PLIC (Platform Level Interrupt Controller)
|
||||
* 3) PRCI (Power, Reset, Clock, Interrupt)
|
||||
* 4) Registers emulated as RAM: AON, GPIO, QSPI, PWM
|
||||
* 5) Flash memory emulated as RAM
|
||||
*
|
||||
* The Mask ROM reset vector jumps to the flash payload at 0x2040_0000.
|
||||
* The OTP ROM and Flash boot code will be emulated in a future version.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/sifive_plic.h"
|
||||
#include "hw/riscv/sifive_clint.h"
|
||||
#include "hw/riscv/sifive_prci.h"
|
||||
#include "hw/riscv/sifive_uart.h"
|
||||
#include "hw/riscv/sifive_e.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "elf.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} sifive_e_memmap[] = {
|
||||
[SIFIVE_E_DEBUG] = { 0x0, 0x100 },
|
||||
[SIFIVE_E_MROM] = { 0x1000, 0x2000 },
|
||||
[SIFIVE_E_OTP] = { 0x20000, 0x2000 },
|
||||
[SIFIVE_E_CLINT] = { 0x2000000, 0x10000 },
|
||||
[SIFIVE_E_PLIC] = { 0xc000000, 0x4000000 },
|
||||
[SIFIVE_E_AON] = { 0x10000000, 0x8000 },
|
||||
[SIFIVE_E_PRCI] = { 0x10008000, 0x8000 },
|
||||
[SIFIVE_E_OTP_CTRL] = { 0x10010000, 0x1000 },
|
||||
[SIFIVE_E_GPIO0] = { 0x10012000, 0x1000 },
|
||||
[SIFIVE_E_UART0] = { 0x10013000, 0x1000 },
|
||||
[SIFIVE_E_QSPI0] = { 0x10014000, 0x1000 },
|
||||
[SIFIVE_E_PWM0] = { 0x10015000, 0x1000 },
|
||||
[SIFIVE_E_UART1] = { 0x10023000, 0x1000 },
|
||||
[SIFIVE_E_QSPI1] = { 0x10024000, 0x1000 },
|
||||
[SIFIVE_E_PWM1] = { 0x10025000, 0x1000 },
|
||||
[SIFIVE_E_QSPI2] = { 0x10034000, 0x1000 },
|
||||
[SIFIVE_E_PWM2] = { 0x10035000, 0x1000 },
|
||||
[SIFIVE_E_XIP] = { 0x20000000, 0x20000000 },
|
||||
[SIFIVE_E_DTIM] = { 0x80000000, 0x4000 }
|
||||
};
|
||||
|
||||
static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < (len >> 2); i++) {
|
||||
stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t identity_translate(void *opaque, uint64_t addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
|
||||
static uint64_t load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf(kernel_filename, identity_translate, NULL,
|
||||
&kernel_entry, NULL, &kernel_high,
|
||||
0, ELF_MACHINE, 1, 0) < 0) {
|
||||
error_report("qemu: could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static void sifive_mmio_emulate(MemoryRegion *parent, const char *name,
|
||||
uintptr_t offset, uintptr_t length)
|
||||
{
|
||||
MemoryRegion *mock_mmio = g_new(MemoryRegion, 1);
|
||||
memory_region_init_ram(mock_mmio, NULL, name, length, &error_fatal);
|
||||
memory_region_add_subregion(parent, offset, mock_mmio);
|
||||
}
|
||||
|
||||
static void riscv_sifive_e_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = sifive_e_memmap;
|
||||
|
||||
SiFiveEState *s = g_new0(SiFiveEState, 1);
|
||||
MemoryRegion *sys_mem = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *xip_mem = g_new(MemoryRegion, 1);
|
||||
|
||||
/* Initialize SOC */
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||
&error_abort);
|
||||
object_property_set_str(OBJECT(&s->soc), SIFIVE_E_CPU, "cpu-type",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&s->soc), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
/* Data Tightly Integrated Memory */
|
||||
memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram",
|
||||
memmap[SIFIVE_E_DTIM].size, &error_fatal);
|
||||
memory_region_add_subregion(sys_mem,
|
||||
memmap[SIFIVE_E_DTIM].base, main_mem);
|
||||
|
||||
/* Mask ROM */
|
||||
memory_region_init_ram(mask_rom, NULL, "riscv.sifive.e.mrom",
|
||||
memmap[SIFIVE_E_MROM].size, &error_fatal);
|
||||
memory_region_add_subregion(sys_mem,
|
||||
memmap[SIFIVE_E_MROM].base, mask_rom);
|
||||
|
||||
/* MMIO */
|
||||
s->plic = sifive_plic_create(memmap[SIFIVE_E_PLIC].base,
|
||||
(char *)SIFIVE_E_PLIC_HART_CONFIG,
|
||||
SIFIVE_E_PLIC_NUM_SOURCES,
|
||||
SIFIVE_E_PLIC_NUM_PRIORITIES,
|
||||
SIFIVE_E_PLIC_PRIORITY_BASE,
|
||||
SIFIVE_E_PLIC_PENDING_BASE,
|
||||
SIFIVE_E_PLIC_ENABLE_BASE,
|
||||
SIFIVE_E_PLIC_ENABLE_STRIDE,
|
||||
SIFIVE_E_PLIC_CONTEXT_BASE,
|
||||
SIFIVE_E_PLIC_CONTEXT_STRIDE,
|
||||
memmap[SIFIVE_E_PLIC].size);
|
||||
sifive_clint_create(memmap[SIFIVE_E_CLINT].base,
|
||||
memmap[SIFIVE_E_CLINT].size, smp_cpus,
|
||||
SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
|
||||
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon",
|
||||
memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size);
|
||||
sifive_prci_create(memmap[SIFIVE_E_PRCI].base);
|
||||
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0",
|
||||
memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size);
|
||||
sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base,
|
||||
serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]);
|
||||
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0",
|
||||
memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size);
|
||||
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0",
|
||||
memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size);
|
||||
/* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base,
|
||||
serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */
|
||||
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1",
|
||||
memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size);
|
||||
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1",
|
||||
memmap[SIFIVE_E_PWM1].base, memmap[SIFIVE_E_PWM1].size);
|
||||
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi2",
|
||||
memmap[SIFIVE_E_QSPI2].base, memmap[SIFIVE_E_QSPI2].size);
|
||||
sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm2",
|
||||
memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size);
|
||||
|
||||
/* Flash memory */
|
||||
memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e.xip",
|
||||
memmap[SIFIVE_E_XIP].size, &error_fatal);
|
||||
memory_region_set_readonly(xip_mem, true);
|
||||
memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem);
|
||||
|
||||
/* Mask ROM reset vector */
|
||||
uint32_t reset_vec[2] = {
|
||||
0x204002b7, /* 0x1000: lui t0,0x20400 */
|
||||
0x00028067, /* 0x1004: jr t0 */
|
||||
};
|
||||
|
||||
/* copy in the reset vector */
|
||||
copy_le32_to_phys(memmap[SIFIVE_E_MROM].base, reset_vec, sizeof(reset_vec));
|
||||
memory_region_set_readonly(mask_rom, true);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
}
|
||||
}
|
||||
|
||||
static int riscv_sifive_e_sysbus_device_init(SysBusDevice *sysbusdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void riscv_sifive_e_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
k->init = riscv_sifive_e_sysbus_device_init;
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_sifive_e_device = {
|
||||
.name = TYPE_SIFIVE_E,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFiveEState),
|
||||
.class_init = riscv_sifive_e_class_init,
|
||||
};
|
||||
|
||||
static void riscv_sifive_e_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "RISC-V Board compatible with SiFive E SDK";
|
||||
mc->init = riscv_sifive_e_init;
|
||||
mc->max_cpus = 1;
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init)
|
||||
|
||||
static void riscv_sifive_e_register_types(void)
|
||||
{
|
||||
type_register_static(&riscv_sifive_e_device);
|
||||
}
|
||||
|
||||
type_init(riscv_sifive_e_register_types);
|
||||
505
hw/riscv/sifive_plic.c
Normal file
505
hw/riscv/sifive_plic.c
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* SiFive PLIC (Platform Level Interrupt Controller)
|
||||
*
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
*
|
||||
* This provides a parameterizable interrupt controller based on SiFive's PLIC.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/sifive_plic.h"
|
||||
|
||||
#define RISCV_DEBUG_PLIC 0
|
||||
|
||||
static PLICMode char_to_mode(char c)
|
||||
{
|
||||
switch (c) {
|
||||
case 'U': return PLICMode_U;
|
||||
case 'S': return PLICMode_S;
|
||||
case 'H': return PLICMode_H;
|
||||
case 'M': return PLICMode_M;
|
||||
default:
|
||||
error_report("plic: invalid mode '%c'", c);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static char mode_to_char(PLICMode m)
|
||||
{
|
||||
switch (m) {
|
||||
case PLICMode_U: return 'U';
|
||||
case PLICMode_S: return 'S';
|
||||
case PLICMode_H: return 'H';
|
||||
case PLICMode_M: return 'M';
|
||||
default: return '?';
|
||||
}
|
||||
}
|
||||
|
||||
static void sifive_plic_print_state(SiFivePLICState *plic)
|
||||
{
|
||||
int i;
|
||||
int addrid;
|
||||
|
||||
/* pending */
|
||||
qemu_log("pending : ");
|
||||
for (i = plic->bitfield_words - 1; i >= 0; i--) {
|
||||
qemu_log("%08x", plic->pending[i]);
|
||||
}
|
||||
qemu_log("\n");
|
||||
|
||||
/* pending */
|
||||
qemu_log("claimed : ");
|
||||
for (i = plic->bitfield_words - 1; i >= 0; i--) {
|
||||
qemu_log("%08x", plic->claimed[i]);
|
||||
}
|
||||
qemu_log("\n");
|
||||
|
||||
for (addrid = 0; addrid < plic->num_addrs; addrid++) {
|
||||
qemu_log("hart%d-%c enable: ",
|
||||
plic->addr_config[addrid].hartid,
|
||||
mode_to_char(plic->addr_config[addrid].mode));
|
||||
for (i = plic->bitfield_words - 1; i >= 0; i--) {
|
||||
qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]);
|
||||
}
|
||||
qemu_log("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending)
|
||||
{
|
||||
qemu_mutex_lock(&plic->lock);
|
||||
uint32_t word = irq >> 5;
|
||||
if (pending) {
|
||||
plic->pending[word] |= (1 << (irq & 31));
|
||||
} else {
|
||||
plic->pending[word] &= ~(1 << (irq & 31));
|
||||
}
|
||||
qemu_mutex_unlock(&plic->lock);
|
||||
}
|
||||
|
||||
static
|
||||
void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed)
|
||||
{
|
||||
qemu_mutex_lock(&plic->lock);
|
||||
uint32_t word = irq >> 5;
|
||||
if (claimed) {
|
||||
plic->claimed[word] |= (1 << (irq & 31));
|
||||
} else {
|
||||
plic->claimed[word] &= ~(1 << (irq & 31));
|
||||
}
|
||||
qemu_mutex_unlock(&plic->lock);
|
||||
}
|
||||
|
||||
static
|
||||
int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
|
||||
{
|
||||
int i, j, count = 0;
|
||||
for (i = 0; i < plic->bitfield_words; i++) {
|
||||
uint32_t pending_enabled_not_claimed =
|
||||
(plic->pending[i] & ~plic->claimed[i]) &
|
||||
plic->enable[addrid * plic->bitfield_words + i];
|
||||
if (!pending_enabled_not_claimed) {
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < 32; j++) {
|
||||
int irq = (i << 5) + j;
|
||||
uint32_t prio = plic->source_priority[irq];
|
||||
int enabled = pending_enabled_not_claimed & (1 << j);
|
||||
if (enabled && prio > plic->target_priority[addrid]) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void sifive_plic_update(SiFivePLICState *plic)
|
||||
{
|
||||
int addrid;
|
||||
|
||||
/* raise irq on harts where this irq is enabled */
|
||||
for (addrid = 0; addrid < plic->num_addrs; addrid++) {
|
||||
uint32_t hartid = plic->addr_config[addrid].hartid;
|
||||
PLICMode mode = plic->addr_config[addrid].mode;
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
continue;
|
||||
}
|
||||
int level = sifive_plic_num_irqs_pending(plic, addrid) > 0;
|
||||
switch (mode) {
|
||||
case PLICMode_M:
|
||||
riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
|
||||
break;
|
||||
case PLICMode_S:
|
||||
riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
sifive_plic_print_state(plic);
|
||||
}
|
||||
}
|
||||
|
||||
void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq)
|
||||
{
|
||||
sifive_plic_set_pending(plic, irq, true);
|
||||
sifive_plic_update(plic);
|
||||
}
|
||||
|
||||
void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq)
|
||||
{
|
||||
sifive_plic_set_pending(plic, irq, false);
|
||||
sifive_plic_update(plic);
|
||||
}
|
||||
|
||||
static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < plic->bitfield_words; i++) {
|
||||
uint32_t pending_enabled_not_claimed =
|
||||
(plic->pending[i] & ~plic->claimed[i]) &
|
||||
plic->enable[addrid * plic->bitfield_words + i];
|
||||
if (!pending_enabled_not_claimed) {
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < 32; j++) {
|
||||
int irq = (i << 5) + j;
|
||||
uint32_t prio = plic->source_priority[irq];
|
||||
int enabled = pending_enabled_not_claimed & (1 << j);
|
||||
if (enabled && prio > plic->target_priority[addrid]) {
|
||||
sifive_plic_set_pending(plic, irq, false);
|
||||
sifive_plic_set_claimed(plic, irq, true);
|
||||
return irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
SiFivePLICState *plic = opaque;
|
||||
|
||||
/* writes must be 4 byte words */
|
||||
if ((addr & 0x3) != 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (addr >= plic->priority_base && /* 4 bytes per source */
|
||||
addr < plic->priority_base + (plic->num_sources << 2))
|
||||
{
|
||||
uint32_t irq = (addr - plic->priority_base) >> 2;
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: read priority: irq=%d priority=%d\n",
|
||||
irq, plic->source_priority[irq]);
|
||||
}
|
||||
return plic->source_priority[irq];
|
||||
} else if (addr >= plic->pending_base && /* 1 bit per source */
|
||||
addr < plic->pending_base + (plic->num_sources >> 3))
|
||||
{
|
||||
uint32_t word = (addr - plic->priority_base) >> 2;
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: read pending: word=%d value=%d\n",
|
||||
word, plic->pending[word]);
|
||||
}
|
||||
return plic->pending[word];
|
||||
} else if (addr >= plic->enable_base && /* 1 bit per source */
|
||||
addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
|
||||
{
|
||||
uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
|
||||
uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
|
||||
if (wordid < plic->bitfield_words) {
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
|
||||
plic->addr_config[addrid].hartid,
|
||||
mode_to_char(plic->addr_config[addrid].mode), wordid,
|
||||
plic->enable[addrid * plic->bitfield_words + wordid]);
|
||||
}
|
||||
return plic->enable[addrid * plic->bitfield_words + wordid];
|
||||
}
|
||||
} else if (addr >= plic->context_base && /* 1 bit per source */
|
||||
addr < plic->context_base + plic->num_addrs * plic->context_stride)
|
||||
{
|
||||
uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
|
||||
uint32_t contextid = (addr & (plic->context_stride - 1));
|
||||
if (contextid == 0) {
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: read priority: hart%d-%c priority=%x\n",
|
||||
plic->addr_config[addrid].hartid,
|
||||
mode_to_char(plic->addr_config[addrid].mode),
|
||||
plic->target_priority[addrid]);
|
||||
}
|
||||
return plic->target_priority[addrid];
|
||||
} else if (contextid == 4) {
|
||||
uint32_t value = sifive_plic_claim(plic, addrid);
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: read claim: hart%d-%c irq=%x\n",
|
||||
plic->addr_config[addrid].hartid,
|
||||
mode_to_char(plic->addr_config[addrid].mode),
|
||||
value);
|
||||
sifive_plic_print_state(plic);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
error_report("plic: invalid register read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
SiFivePLICState *plic = opaque;
|
||||
|
||||
/* writes must be 4 byte words */
|
||||
if ((addr & 0x3) != 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (addr >= plic->priority_base && /* 4 bytes per source */
|
||||
addr < plic->priority_base + (plic->num_sources << 2))
|
||||
{
|
||||
uint32_t irq = (addr - plic->priority_base) >> 2;
|
||||
plic->source_priority[irq] = value & 7;
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: write priority: irq=%d priority=%d\n",
|
||||
irq, plic->source_priority[irq]);
|
||||
}
|
||||
return;
|
||||
} else if (addr >= plic->pending_base && /* 1 bit per source */
|
||||
addr < plic->pending_base + (plic->num_sources >> 3))
|
||||
{
|
||||
error_report("plic: invalid pending write: %08x", (uint32_t)addr);
|
||||
return;
|
||||
} else if (addr >= plic->enable_base && /* 1 bit per source */
|
||||
addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
|
||||
{
|
||||
uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
|
||||
uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
|
||||
if (wordid < plic->bitfield_words) {
|
||||
plic->enable[addrid * plic->bitfield_words + wordid] = value;
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
|
||||
plic->addr_config[addrid].hartid,
|
||||
mode_to_char(plic->addr_config[addrid].mode), wordid,
|
||||
plic->enable[addrid * plic->bitfield_words + wordid]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (addr >= plic->context_base && /* 4 bytes per reg */
|
||||
addr < plic->context_base + plic->num_addrs * plic->context_stride)
|
||||
{
|
||||
uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
|
||||
uint32_t contextid = (addr & (plic->context_stride - 1));
|
||||
if (contextid == 0) {
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: write priority: hart%d-%c priority=%x\n",
|
||||
plic->addr_config[addrid].hartid,
|
||||
mode_to_char(plic->addr_config[addrid].mode),
|
||||
plic->target_priority[addrid]);
|
||||
}
|
||||
if (value <= plic->num_priorities) {
|
||||
plic->target_priority[addrid] = value;
|
||||
sifive_plic_update(plic);
|
||||
}
|
||||
return;
|
||||
} else if (contextid == 4) {
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("plic: write claim: hart%d-%c irq=%x\n",
|
||||
plic->addr_config[addrid].hartid,
|
||||
mode_to_char(plic->addr_config[addrid].mode),
|
||||
(uint32_t)value);
|
||||
}
|
||||
if (value < plic->num_sources) {
|
||||
sifive_plic_set_claimed(plic, value, false);
|
||||
sifive_plic_update(plic);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
error_report("plic: invalid register write: %08x", (uint32_t)addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sifive_plic_ops = {
|
||||
.read = sifive_plic_read,
|
||||
.write = sifive_plic_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static Property sifive_plic_properties[] = {
|
||||
DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
|
||||
DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
|
||||
DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
|
||||
DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
|
||||
DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
|
||||
DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
|
||||
DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
|
||||
DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
|
||||
DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
|
||||
DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
/*
|
||||
* parse PLIC hart/mode address offset config
|
||||
*
|
||||
* "M" 1 hart with M mode
|
||||
* "MS,MS" 2 harts, 0-1 with M and S mode
|
||||
* "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode
|
||||
*/
|
||||
static void parse_hart_config(SiFivePLICState *plic)
|
||||
{
|
||||
int addrid, hartid, modes;
|
||||
const char *p;
|
||||
char c;
|
||||
|
||||
/* count and validate hart/mode combinations */
|
||||
addrid = 0, hartid = 0, modes = 0;
|
||||
p = plic->hart_config;
|
||||
while ((c = *p++)) {
|
||||
if (c == ',') {
|
||||
addrid += __builtin_popcount(modes);
|
||||
modes = 0;
|
||||
hartid++;
|
||||
} else {
|
||||
int m = 1 << char_to_mode(c);
|
||||
if (modes == (modes | m)) {
|
||||
error_report("plic: duplicate mode '%c' in config: %s",
|
||||
c, plic->hart_config);
|
||||
exit(1);
|
||||
}
|
||||
modes |= m;
|
||||
}
|
||||
}
|
||||
if (modes) {
|
||||
addrid += __builtin_popcount(modes);
|
||||
}
|
||||
hartid++;
|
||||
|
||||
/* store hart/mode combinations */
|
||||
plic->num_addrs = addrid;
|
||||
plic->addr_config = g_new(PLICAddr, plic->num_addrs);
|
||||
addrid = 0, hartid = 0;
|
||||
p = plic->hart_config;
|
||||
while ((c = *p++)) {
|
||||
if (c == ',') {
|
||||
hartid++;
|
||||
} else {
|
||||
plic->addr_config[addrid].addrid = addrid;
|
||||
plic->addr_config[addrid].hartid = hartid;
|
||||
plic->addr_config[addrid].mode = char_to_mode(c);
|
||||
addrid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sifive_plic_irq_request(void *opaque, int irq, int level)
|
||||
{
|
||||
SiFivePLICState *plic = opaque;
|
||||
if (RISCV_DEBUG_PLIC) {
|
||||
qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
|
||||
}
|
||||
sifive_plic_set_pending(plic, irq, level > 0);
|
||||
sifive_plic_update(plic);
|
||||
}
|
||||
|
||||
static void sifive_plic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SiFivePLICState *plic = SIFIVE_PLIC(dev);
|
||||
int i;
|
||||
|
||||
memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
|
||||
TYPE_SIFIVE_PLIC, plic->aperture_size);
|
||||
parse_hart_config(plic);
|
||||
qemu_mutex_init(&plic->lock);
|
||||
plic->bitfield_words = (plic->num_sources + 31) >> 5;
|
||||
plic->source_priority = g_new0(uint32_t, plic->num_sources);
|
||||
plic->target_priority = g_new(uint32_t, plic->num_addrs);
|
||||
plic->pending = g_new0(uint32_t, plic->bitfield_words);
|
||||
plic->claimed = g_new0(uint32_t, plic->bitfield_words);
|
||||
plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
|
||||
plic->irqs = g_new0(qemu_irq, plic->num_sources + 1);
|
||||
for (i = 0; i <= plic->num_sources; i++) {
|
||||
plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void sifive_plic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->props = sifive_plic_properties;
|
||||
dc->realize = sifive_plic_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_plic_info = {
|
||||
.name = TYPE_SIFIVE_PLIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFivePLICState),
|
||||
.class_init = sifive_plic_class_init,
|
||||
};
|
||||
|
||||
static void sifive_plic_register_types(void)
|
||||
{
|
||||
type_register_static(&sifive_plic_info);
|
||||
}
|
||||
|
||||
type_init(sifive_plic_register_types)
|
||||
|
||||
/*
|
||||
* Create PLIC device.
|
||||
*/
|
||||
DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
|
||||
uint32_t num_sources, uint32_t num_priorities,
|
||||
uint32_t priority_base, uint32_t pending_base,
|
||||
uint32_t enable_base, uint32_t enable_stride,
|
||||
uint32_t context_base, uint32_t context_stride,
|
||||
uint32_t aperture_size)
|
||||
{
|
||||
DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC);
|
||||
assert(enable_stride == (enable_stride & -enable_stride));
|
||||
assert(context_stride == (context_stride & -context_stride));
|
||||
qdev_prop_set_string(dev, "hart-config", hart_config);
|
||||
qdev_prop_set_uint32(dev, "num-sources", num_sources);
|
||||
qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
|
||||
qdev_prop_set_uint32(dev, "priority-base", priority_base);
|
||||
qdev_prop_set_uint32(dev, "pending-base", pending_base);
|
||||
qdev_prop_set_uint32(dev, "enable-base", enable_base);
|
||||
qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
|
||||
qdev_prop_set_uint32(dev, "context-base", context_base);
|
||||
qdev_prop_set_uint32(dev, "context-stride", context_stride);
|
||||
qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
|
||||
return dev;
|
||||
}
|
||||
89
hw/riscv/sifive_prci.c
Normal file
89
hw/riscv/sifive_prci.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* QEMU SiFive PRCI (Power, Reset, Clock, Interrupt)
|
||||
*
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
*
|
||||
* Simple model of the PRCI to emulate register reads made by the SDK BSP
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/sifive_prci.h"
|
||||
|
||||
/* currently implements enough to mock freedom-e-sdk BSP clock programming */
|
||||
|
||||
static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
if (addr == 0 /* PRCI_HFROSCCFG */) {
|
||||
return 1 << 31; /* ROSC_RDY */
|
||||
}
|
||||
if (addr == 8 /* PRCI_PLLCFG */) {
|
||||
return 1 << 31; /* PLL_LOCK */
|
||||
}
|
||||
hw_error("%s: read: addr=0x%x\n", __func__, (int)addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sifive_prci_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
/* discard writes */
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sifive_prci_ops = {
|
||||
.read = sifive_prci_read,
|
||||
.write = sifive_prci_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static void sifive_prci_init(Object *obj)
|
||||
{
|
||||
SiFivePRCIState *s = SIFIVE_PRCI(obj);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &sifive_prci_ops, s,
|
||||
TYPE_SIFIVE_PRCI, 0x8000);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_prci_info = {
|
||||
.name = TYPE_SIFIVE_PRCI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFivePRCIState),
|
||||
.instance_init = sifive_prci_init,
|
||||
};
|
||||
|
||||
static void sifive_prci_register_types(void)
|
||||
{
|
||||
type_register_static(&sifive_prci_info);
|
||||
}
|
||||
|
||||
type_init(sifive_prci_register_types)
|
||||
|
||||
|
||||
/*
|
||||
* Create PRCI device.
|
||||
*/
|
||||
DeviceState *sifive_prci_create(hwaddr addr)
|
||||
{
|
||||
DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
|
||||
return dev;
|
||||
}
|
||||
93
hw/riscv/sifive_test.c
Normal file
93
hw/riscv/sifive_test.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* QEMU SiFive Test Finisher
|
||||
*
|
||||
* Copyright (c) 2018 SiFive, Inc.
|
||||
*
|
||||
* Test finisher memory mapped device used to exit simulation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/sifive_test.h"
|
||||
|
||||
static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sifive_test_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
if (addr == 0) {
|
||||
int status = val64 & 0xffff;
|
||||
int code = (val64 >> 16) & 0xffff;
|
||||
switch (status) {
|
||||
case FINISHER_FAIL:
|
||||
exit(code);
|
||||
case FINISHER_PASS:
|
||||
exit(0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
|
||||
__func__, (int)addr, val64);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sifive_test_ops = {
|
||||
.read = sifive_test_read,
|
||||
.write = sifive_test_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static void sifive_test_init(Object *obj)
|
||||
{
|
||||
SiFiveTestState *s = SIFIVE_TEST(obj);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s,
|
||||
TYPE_SIFIVE_TEST, 0x1000);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_test_info = {
|
||||
.name = TYPE_SIFIVE_TEST,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFiveTestState),
|
||||
.instance_init = sifive_test_init,
|
||||
};
|
||||
|
||||
static void sifive_test_register_types(void)
|
||||
{
|
||||
type_register_static(&sifive_test_info);
|
||||
}
|
||||
|
||||
type_init(sifive_test_register_types)
|
||||
|
||||
|
||||
/*
|
||||
* Create Test device.
|
||||
*/
|
||||
DeviceState *sifive_test_create(hwaddr addr)
|
||||
{
|
||||
DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_TEST);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
|
||||
return dev;
|
||||
}
|
||||
339
hw/riscv/sifive_u.c
Normal file
339
hw/riscv/sifive_u.c
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* QEMU RISC-V Board Compatible with SiFive Freedom U SDK
|
||||
*
|
||||
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
*
|
||||
* Provides a board compatible with the SiFive Freedom U SDK:
|
||||
*
|
||||
* 0) UART
|
||||
* 1) CLINT (Core Level Interruptor)
|
||||
* 2) PLIC (Platform Level Interrupt Controller)
|
||||
*
|
||||
* This board currently uses a hardcoded devicetree that indicates one hart.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/sifive_plic.h"
|
||||
#include "hw/riscv/sifive_clint.h"
|
||||
#include "hw/riscv/sifive_uart.h"
|
||||
#include "hw/riscv/sifive_prci.h"
|
||||
#include "hw/riscv/sifive_u.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "elf.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} sifive_u_memmap[] = {
|
||||
[SIFIVE_U_DEBUG] = { 0x0, 0x100 },
|
||||
[SIFIVE_U_MROM] = { 0x1000, 0x2000 },
|
||||
[SIFIVE_U_CLINT] = { 0x2000000, 0x10000 },
|
||||
[SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 },
|
||||
[SIFIVE_U_UART0] = { 0x10013000, 0x1000 },
|
||||
[SIFIVE_U_UART1] = { 0x10023000, 0x1000 },
|
||||
[SIFIVE_U_DRAM] = { 0x80000000, 0x0 },
|
||||
};
|
||||
|
||||
static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < (len >> 2); i++) {
|
||||
stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t identity_translate(void *opaque, uint64_t addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
|
||||
static uint64_t load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf(kernel_filename, identity_translate, NULL,
|
||||
&kernel_entry, NULL, &kernel_high,
|
||||
0, ELF_MACHINE, 1, 0) < 0) {
|
||||
error_report("qemu: could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline)
|
||||
{
|
||||
void *fdt;
|
||||
int cpu;
|
||||
uint32_t *cells;
|
||||
char *nodename;
|
||||
uint32_t plic_phandle;
|
||||
|
||||
fdt = s->fdt = create_device_tree(&s->fdt_size);
|
||||
if (!fdt) {
|
||||
error_report("create_device_tree() failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
|
||||
qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/soc");
|
||||
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
|
||||
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc");
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
|
||||
|
||||
nodename = g_strdup_printf("/memory@%lx",
|
||||
(long)memmap[SIFIVE_U_DRAM].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
memmap[SIFIVE_U_DRAM].base >> 32, memmap[SIFIVE_U_DRAM].base,
|
||||
mem_size >> 32, mem_size);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
|
||||
g_free(nodename);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/cpus");
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
|
||||
|
||||
for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
|
||||
nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
char *isa = riscv_isa_string(&s->soc.harts[cpu]);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
|
||||
qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
|
||||
qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
|
||||
qemu_fdt_add_subnode(fdt, intc);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
|
||||
qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
|
||||
qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
|
||||
g_free(isa);
|
||||
g_free(intc);
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
cells = g_new0(uint32_t, s->soc.num_harts * 4);
|
||||
for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
|
||||
nodename =
|
||||
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
|
||||
cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
|
||||
g_free(nodename);
|
||||
}
|
||||
nodename = g_strdup_printf("/soc/clint@%lx",
|
||||
(long)memmap[SIFIVE_U_CLINT].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[SIFIVE_U_CLINT].base,
|
||||
0x0, memmap[SIFIVE_U_CLINT].size);
|
||||
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
|
||||
cells, s->soc.num_harts * sizeof(uint32_t) * 4);
|
||||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
cells = g_new0(uint32_t, s->soc.num_harts * 4);
|
||||
for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
|
||||
nodename =
|
||||
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
|
||||
cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
|
||||
g_free(nodename);
|
||||
}
|
||||
nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
|
||||
(long)memmap[SIFIVE_U_PLIC].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
|
||||
qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
|
||||
cells, s->soc.num_harts * sizeof(uint32_t) * 4);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[SIFIVE_U_PLIC].base,
|
||||
0x0, memmap[SIFIVE_U_PLIC].size);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 4);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2);
|
||||
plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
nodename = g_strdup_printf("/uart@%lx",
|
||||
(long)memmap[SIFIVE_U_UART0].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0");
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[SIFIVE_U_UART0].base,
|
||||
0x0, memmap[SIFIVE_U_UART0].size);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 1);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/chosen");
|
||||
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
|
||||
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
static void riscv_sifive_u_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = sifive_u_memmap;
|
||||
|
||||
SiFiveUState *s = g_new0(SiFiveUState, 1);
|
||||
MemoryRegion *sys_memory = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
|
||||
|
||||
/* Initialize SOC */
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||
&error_abort);
|
||||
object_property_set_str(OBJECT(&s->soc), SIFIVE_U_CPU, "cpu-type",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&s->soc), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
/* register RAM */
|
||||
memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram",
|
||||
machine->ram_size, &error_fatal);
|
||||
memory_region_add_subregion(sys_memory, memmap[SIFIVE_U_DRAM].base,
|
||||
main_mem);
|
||||
|
||||
/* create device tree */
|
||||
create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
|
||||
|
||||
/* boot rom */
|
||||
memory_region_init_ram(boot_rom, NULL, "riscv.sifive.u.mrom",
|
||||
memmap[SIFIVE_U_MROM].base, &error_fatal);
|
||||
memory_region_set_readonly(boot_rom, true);
|
||||
memory_region_add_subregion(sys_memory, 0x0, boot_rom);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
uint32_t reset_vec[8] = {
|
||||
0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
|
||||
0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
|
||||
0xf1402573, /* csrr a0, mhartid */
|
||||
#if defined(TARGET_RISCV32)
|
||||
0x0182a283, /* lw t0, 24(t0) */
|
||||
#elif defined(TARGET_RISCV64)
|
||||
0x0182b283, /* ld t0, 24(t0) */
|
||||
#endif
|
||||
0x00028067, /* jr t0 */
|
||||
0x00000000,
|
||||
memmap[SIFIVE_U_DRAM].base, /* start: .dword DRAM_BASE */
|
||||
0x00000000,
|
||||
/* dtb: */
|
||||
};
|
||||
|
||||
/* copy in the reset vector */
|
||||
copy_le32_to_phys(memmap[SIFIVE_U_MROM].base, reset_vec, sizeof(reset_vec));
|
||||
|
||||
/* copy in the device tree */
|
||||
qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
|
||||
cpu_physical_memory_write(memmap[SIFIVE_U_MROM].base +
|
||||
sizeof(reset_vec), s->fdt, s->fdt_size);
|
||||
|
||||
/* MMIO */
|
||||
s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base,
|
||||
(char *)SIFIVE_U_PLIC_HART_CONFIG,
|
||||
SIFIVE_U_PLIC_NUM_SOURCES,
|
||||
SIFIVE_U_PLIC_NUM_PRIORITIES,
|
||||
SIFIVE_U_PLIC_PRIORITY_BASE,
|
||||
SIFIVE_U_PLIC_PENDING_BASE,
|
||||
SIFIVE_U_PLIC_ENABLE_BASE,
|
||||
SIFIVE_U_PLIC_ENABLE_STRIDE,
|
||||
SIFIVE_U_PLIC_CONTEXT_BASE,
|
||||
SIFIVE_U_PLIC_CONTEXT_STRIDE,
|
||||
memmap[SIFIVE_U_PLIC].size);
|
||||
sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART0].base,
|
||||
serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]);
|
||||
/* sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART1].base,
|
||||
serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */
|
||||
sifive_clint_create(memmap[SIFIVE_U_CLINT].base,
|
||||
memmap[SIFIVE_U_CLINT].size, smp_cpus,
|
||||
SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
|
||||
}
|
||||
|
||||
static int riscv_sifive_u_sysbus_device_init(SysBusDevice *sysbusdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void riscv_sifive_u_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
k->init = riscv_sifive_u_sysbus_device_init;
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_sifive_u_device = {
|
||||
.name = TYPE_SIFIVE_U,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFiveUState),
|
||||
.class_init = riscv_sifive_u_class_init,
|
||||
};
|
||||
|
||||
static void riscv_sifive_u_register_types(void)
|
||||
{
|
||||
type_register_static(&riscv_sifive_u_device);
|
||||
}
|
||||
|
||||
type_init(riscv_sifive_u_register_types);
|
||||
|
||||
static void riscv_sifive_u_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "RISC-V Board compatible with SiFive U SDK";
|
||||
mc->init = riscv_sifive_u_init;
|
||||
mc->max_cpus = 1;
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init)
|
||||
176
hw/riscv/sifive_uart.c
Normal file
176
hw/riscv/sifive_uart.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* QEMU model of the UART on the SiFive E300 and U500 series SOCs.
|
||||
*
|
||||
* Copyright (c) 2016 Stefan O'Rear
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "chardev/char.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/sifive_uart.h"
|
||||
|
||||
/*
|
||||
* Not yet implemented:
|
||||
*
|
||||
* Transmit FIFO using "qemu/fifo8.h"
|
||||
* SIFIVE_UART_IE_TXWM interrupts
|
||||
* SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
|
||||
* Rx FIFO watermark interrupt trigger threshold
|
||||
* Tx FIFO watermark interrupt trigger threshold.
|
||||
*/
|
||||
|
||||
static void update_irq(SiFiveUARTState *s)
|
||||
{
|
||||
int cond = 0;
|
||||
if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
|
||||
cond = 1;
|
||||
}
|
||||
if (cond) {
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
uart_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
SiFiveUARTState *s = opaque;
|
||||
unsigned char r;
|
||||
switch (addr) {
|
||||
case SIFIVE_UART_RXFIFO:
|
||||
if (s->rx_fifo_len) {
|
||||
r = s->rx_fifo[0];
|
||||
memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
|
||||
s->rx_fifo_len--;
|
||||
qemu_chr_fe_accept_input(&s->chr);
|
||||
update_irq(s);
|
||||
return r;
|
||||
}
|
||||
return 0x80000000;
|
||||
|
||||
case SIFIVE_UART_TXFIFO:
|
||||
return 0; /* Should check tx fifo */
|
||||
case SIFIVE_UART_IE:
|
||||
return s->ie;
|
||||
case SIFIVE_UART_IP:
|
||||
return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
|
||||
case SIFIVE_UART_TXCTRL:
|
||||
return s->txctrl;
|
||||
case SIFIVE_UART_RXCTRL:
|
||||
return s->rxctrl;
|
||||
case SIFIVE_UART_DIV:
|
||||
return s->div;
|
||||
}
|
||||
|
||||
hw_error("%s: bad read: addr=0x%x\n",
|
||||
__func__, (int)addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
uart_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
SiFiveUARTState *s = opaque;
|
||||
uint32_t value = val64;
|
||||
unsigned char ch = value;
|
||||
|
||||
switch (addr) {
|
||||
case SIFIVE_UART_TXFIFO:
|
||||
qemu_chr_fe_write(&s->chr, &ch, 1);
|
||||
return;
|
||||
case SIFIVE_UART_IE:
|
||||
s->ie = val64;
|
||||
update_irq(s);
|
||||
return;
|
||||
case SIFIVE_UART_TXCTRL:
|
||||
s->txctrl = val64;
|
||||
return;
|
||||
case SIFIVE_UART_RXCTRL:
|
||||
s->rxctrl = val64;
|
||||
return;
|
||||
case SIFIVE_UART_DIV:
|
||||
s->div = val64;
|
||||
return;
|
||||
}
|
||||
hw_error("%s: bad write: addr=0x%x v=0x%x\n",
|
||||
__func__, (int)addr, (int)value);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps uart_ops = {
|
||||
.read = uart_read,
|
||||
.write = uart_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static void uart_rx(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
SiFiveUARTState *s = opaque;
|
||||
|
||||
/* Got a byte. */
|
||||
if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
|
||||
printf("WARNING: UART dropped char.\n");
|
||||
return;
|
||||
}
|
||||
s->rx_fifo[s->rx_fifo_len++] = *buf;
|
||||
|
||||
update_irq(s);
|
||||
}
|
||||
|
||||
static int uart_can_rx(void *opaque)
|
||||
{
|
||||
SiFiveUARTState *s = opaque;
|
||||
|
||||
return s->rx_fifo_len < sizeof(s->rx_fifo);
|
||||
}
|
||||
|
||||
static void uart_event(void *opaque, int event)
|
||||
{
|
||||
}
|
||||
|
||||
static int uart_be_change(void *opaque)
|
||||
{
|
||||
SiFiveUARTState *s = opaque;
|
||||
|
||||
qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
|
||||
uart_be_change, s, NULL, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create UART device.
|
||||
*/
|
||||
SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
|
||||
Chardev *chr, qemu_irq irq)
|
||||
{
|
||||
SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
|
||||
s->irq = irq;
|
||||
qemu_chr_fe_init(&s->chr, chr, &error_abort);
|
||||
qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
|
||||
uart_be_change, s, NULL, true);
|
||||
memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
|
||||
TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
|
||||
memory_region_add_subregion(address_space, base, &s->mmio);
|
||||
return s;
|
||||
}
|
||||
376
hw/riscv/spike.c
Normal file
376
hw/riscv/spike.c
Normal file
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* QEMU RISC-V Spike Board
|
||||
*
|
||||
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
||||
* Copyright (c) 2017-2018 SiFive, Inc.
|
||||
*
|
||||
* This provides a RISC-V Board with the following devices:
|
||||
*
|
||||
* 0) HTIF Console and Poweroff
|
||||
* 1) CLINT (Timer and IPI)
|
||||
* 2) PLIC (Platform Level Interrupt Controller)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/riscv_htif.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/sifive_clint.h"
|
||||
#include "hw/riscv/spike.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "elf.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} spike_memmap[] = {
|
||||
[SPIKE_MROM] = { 0x1000, 0x2000 },
|
||||
[SPIKE_CLINT] = { 0x2000000, 0x10000 },
|
||||
[SPIKE_DRAM] = { 0x80000000, 0x0 },
|
||||
};
|
||||
|
||||
static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < (len >> 2); i++) {
|
||||
stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t identity_translate(void *opaque, uint64_t addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
|
||||
static uint64_t load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf_ram_sym(kernel_filename, identity_translate, NULL,
|
||||
&kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0,
|
||||
NULL, true, htif_symbol_callback) < 0) {
|
||||
error_report("qemu: could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline)
|
||||
{
|
||||
void *fdt;
|
||||
int cpu;
|
||||
uint32_t *cells;
|
||||
char *nodename;
|
||||
|
||||
fdt = s->fdt = create_device_tree(&s->fdt_size);
|
||||
if (!fdt) {
|
||||
error_report("create_device_tree() failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
|
||||
qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/htif");
|
||||
qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0");
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/soc");
|
||||
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
|
||||
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc");
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
|
||||
|
||||
nodename = g_strdup_printf("/memory@%lx",
|
||||
(long)memmap[SPIKE_DRAM].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
memmap[SPIKE_DRAM].base >> 32, memmap[SPIKE_DRAM].base,
|
||||
mem_size >> 32, mem_size);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
|
||||
g_free(nodename);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/cpus");
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
|
||||
|
||||
for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
|
||||
nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
char *isa = riscv_isa_string(&s->soc.harts[cpu]);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
|
||||
qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
|
||||
qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
|
||||
qemu_fdt_add_subnode(fdt, intc);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
|
||||
qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
|
||||
qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
|
||||
g_free(isa);
|
||||
g_free(intc);
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
cells = g_new0(uint32_t, s->soc.num_harts * 4);
|
||||
for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
|
||||
nodename =
|
||||
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
|
||||
cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
|
||||
g_free(nodename);
|
||||
}
|
||||
nodename = g_strdup_printf("/soc/clint@%lx",
|
||||
(long)memmap[SPIKE_CLINT].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[SPIKE_CLINT].base,
|
||||
0x0, memmap[SPIKE_CLINT].size);
|
||||
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
|
||||
cells, s->soc.num_harts * sizeof(uint32_t) * 4);
|
||||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/chosen");
|
||||
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
|
||||
}
|
||||
|
||||
static void spike_v1_10_0_board_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = spike_memmap;
|
||||
|
||||
SpikeState *s = g_new0(SpikeState, 1);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
|
||||
|
||||
/* Initialize SOC */
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||
&error_abort);
|
||||
object_property_set_str(OBJECT(&s->soc), SPIKE_V1_10_0_CPU, "cpu-type",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&s->soc), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
/* register system main memory (actual RAM) */
|
||||
memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
|
||||
machine->ram_size, &error_fatal);
|
||||
memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
|
||||
main_mem);
|
||||
|
||||
/* create device tree */
|
||||
create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
|
||||
|
||||
/* boot rom */
|
||||
memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom",
|
||||
s->fdt_size + 0x2000, &error_fatal);
|
||||
memory_region_add_subregion(system_memory, 0x0, boot_rom);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
uint32_t reset_vec[8] = {
|
||||
0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
|
||||
0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
|
||||
0xf1402573, /* csrr a0, mhartid */
|
||||
#if defined(TARGET_RISCV32)
|
||||
0x0182a283, /* lw t0, 24(t0) */
|
||||
#elif defined(TARGET_RISCV64)
|
||||
0x0182b283, /* ld t0, 24(t0) */
|
||||
#endif
|
||||
0x00028067, /* jr t0 */
|
||||
0x00000000,
|
||||
memmap[SPIKE_DRAM].base, /* start: .dword DRAM_BASE */
|
||||
0x00000000,
|
||||
/* dtb: */
|
||||
};
|
||||
|
||||
/* copy in the reset vector */
|
||||
copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec));
|
||||
|
||||
/* copy in the device tree */
|
||||
qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
|
||||
cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec),
|
||||
s->fdt, s->fdt_size);
|
||||
|
||||
/* initialize HTIF using symbols found in load_kernel */
|
||||
htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]);
|
||||
|
||||
/* Core Local Interruptor (timer and IPI) */
|
||||
sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size,
|
||||
smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
|
||||
}
|
||||
|
||||
static void spike_v1_09_1_board_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = spike_memmap;
|
||||
|
||||
SpikeState *s = g_new0(SpikeState, 1);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
|
||||
|
||||
/* Initialize SOC */
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||
&error_abort);
|
||||
object_property_set_str(OBJECT(&s->soc), SPIKE_V1_09_1_CPU, "cpu-type",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&s->soc), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
/* register system main memory (actual RAM) */
|
||||
memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
|
||||
machine->ram_size, &error_fatal);
|
||||
memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
|
||||
main_mem);
|
||||
|
||||
/* boot rom */
|
||||
memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom",
|
||||
0x40000, &error_fatal);
|
||||
memory_region_add_subregion(system_memory, 0x0, boot_rom);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
load_kernel(machine->kernel_filename);
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
uint32_t reset_vec[8] = {
|
||||
0x297 + memmap[SPIKE_DRAM].base - memmap[SPIKE_MROM].base, /* lui */
|
||||
0x00028067, /* jump to DRAM_BASE */
|
||||
0x00000000, /* reserved */
|
||||
memmap[SPIKE_MROM].base + sizeof(reset_vec), /* config string pointer */
|
||||
0, 0, 0, 0 /* trap vector */
|
||||
};
|
||||
|
||||
/* part one of config string - before memory size specified */
|
||||
const char *config_string_tmpl =
|
||||
"platform {\n"
|
||||
" vendor ucb;\n"
|
||||
" arch spike;\n"
|
||||
"};\n"
|
||||
"rtc {\n"
|
||||
" addr 0x%" PRIx64 "x;\n"
|
||||
"};\n"
|
||||
"ram {\n"
|
||||
" 0 {\n"
|
||||
" addr 0x%" PRIx64 "x;\n"
|
||||
" size 0x%" PRIx64 "x;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"core {\n"
|
||||
" 0" " {\n"
|
||||
" " "0 {\n"
|
||||
" isa %s;\n"
|
||||
" timecmp 0x%" PRIx64 "x;\n"
|
||||
" ipi 0x%" PRIx64 "x;\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
"};\n";
|
||||
|
||||
/* build config string with supplied memory size */
|
||||
char *isa = riscv_isa_string(&s->soc.harts[0]);
|
||||
size_t config_string_size = strlen(config_string_tmpl) + 48;
|
||||
char *config_string = malloc(config_string_size);
|
||||
snprintf(config_string, config_string_size, config_string_tmpl,
|
||||
(uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE,
|
||||
(uint64_t)memmap[SPIKE_DRAM].base,
|
||||
(uint64_t)ram_size, isa,
|
||||
(uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIMECMP_BASE,
|
||||
(uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_SIP_BASE);
|
||||
g_free(isa);
|
||||
size_t config_string_len = strlen(config_string);
|
||||
|
||||
/* copy in the reset vector */
|
||||
copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec));
|
||||
|
||||
/* copy in the config string */
|
||||
cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec),
|
||||
config_string, config_string_len);
|
||||
|
||||
/* initialize HTIF using symbols found in load_kernel */
|
||||
htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]);
|
||||
|
||||
/* Core Local Interruptor (timer and IPI) */
|
||||
sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size,
|
||||
smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
|
||||
}
|
||||
|
||||
static const TypeInfo spike_v_1_09_1_device = {
|
||||
.name = TYPE_RISCV_SPIKE_V1_09_1_BOARD,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SpikeState),
|
||||
};
|
||||
|
||||
static const TypeInfo spike_v_1_10_0_device = {
|
||||
.name = TYPE_RISCV_SPIKE_V1_10_0_BOARD,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SpikeState),
|
||||
};
|
||||
|
||||
static void spike_v1_09_1_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)";
|
||||
mc->init = spike_v1_09_1_board_init;
|
||||
mc->max_cpus = 1;
|
||||
}
|
||||
|
||||
static void spike_v1_10_0_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "RISC-V Spike Board (Privileged ISA v1.10)";
|
||||
mc->init = spike_v1_10_0_board_init;
|
||||
mc->max_cpus = 1;
|
||||
mc->is_default = 1;
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("spike_v1.9.1", spike_v1_09_1_machine_init)
|
||||
DEFINE_MACHINE("spike_v1.10", spike_v1_10_0_machine_init)
|
||||
|
||||
static void riscv_spike_board_register_types(void)
|
||||
{
|
||||
type_register_static(&spike_v_1_09_1_device);
|
||||
type_register_static(&spike_v_1_10_0_device);
|
||||
}
|
||||
|
||||
type_init(riscv_spike_board_register_types);
|
||||
420
hw/riscv/virt.c
Normal file
420
hw/riscv/virt.c
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* QEMU RISC-V VirtIO Board
|
||||
*
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
*
|
||||
* RISC-V machine with 16550a UART and VirtIO MMIO
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/riscv_htif.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/sifive_plic.h"
|
||||
#include "hw/riscv/sifive_clint.h"
|
||||
#include "hw/riscv/sifive_test.h"
|
||||
#include "hw/riscv/virt.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "elf.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} virt_memmap[] = {
|
||||
[VIRT_DEBUG] = { 0x0, 0x100 },
|
||||
[VIRT_MROM] = { 0x1000, 0x2000 },
|
||||
[VIRT_TEST] = { 0x4000, 0x1000 },
|
||||
[VIRT_CLINT] = { 0x2000000, 0x10000 },
|
||||
[VIRT_PLIC] = { 0xc000000, 0x4000000 },
|
||||
[VIRT_UART0] = { 0x10000000, 0x100 },
|
||||
[VIRT_VIRTIO] = { 0x10001000, 0x1000 },
|
||||
[VIRT_DRAM] = { 0x80000000, 0x0 },
|
||||
};
|
||||
|
||||
static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < (len >> 2); i++) {
|
||||
stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t identity_translate(void *opaque, uint64_t addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
|
||||
static uint64_t load_kernel(const char *kernel_filename)
|
||||
{
|
||||
uint64_t kernel_entry, kernel_high;
|
||||
|
||||
if (load_elf(kernel_filename, identity_translate, NULL,
|
||||
&kernel_entry, NULL, &kernel_high,
|
||||
0, ELF_MACHINE, 1, 0) < 0) {
|
||||
error_report("qemu: could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
return kernel_entry;
|
||||
}
|
||||
|
||||
static hwaddr load_initrd(const char *filename, uint64_t mem_size,
|
||||
uint64_t kernel_entry, hwaddr *start)
|
||||
{
|
||||
int size;
|
||||
|
||||
/* We want to put the initrd far enough into RAM that when the
|
||||
* kernel is uncompressed it will not clobber the initrd. However
|
||||
* on boards without much RAM we must ensure that we still leave
|
||||
* enough room for a decent sized initrd, and on boards with large
|
||||
* amounts of RAM we must avoid the initrd being so far up in RAM
|
||||
* that it is outside lowmem and inaccessible to the kernel.
|
||||
* So for boards with less than 256MB of RAM we put the initrd
|
||||
* halfway into RAM, and for boards with 256MB of RAM or more we put
|
||||
* the initrd at 128MB.
|
||||
*/
|
||||
*start = kernel_entry + MIN(mem_size / 2, 128 * 1024 * 1024);
|
||||
|
||||
size = load_ramdisk(filename, *start, mem_size - *start);
|
||||
if (size == -1) {
|
||||
size = load_image_targphys(filename, *start, mem_size - *start);
|
||||
if (size == -1) {
|
||||
error_report("qemu: could not load ramdisk '%s'", filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return *start + size;
|
||||
}
|
||||
|
||||
static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline)
|
||||
{
|
||||
void *fdt;
|
||||
int cpu;
|
||||
uint32_t *cells;
|
||||
char *nodename;
|
||||
uint32_t plic_phandle, phandle = 1;
|
||||
int i;
|
||||
|
||||
fdt = s->fdt = create_device_tree(&s->fdt_size);
|
||||
if (!fdt) {
|
||||
error_report("create_device_tree() failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
qemu_fdt_setprop_string(fdt, "/", "model", "riscv-virtio,qemu");
|
||||
qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio");
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/soc");
|
||||
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
|
||||
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "riscv-virtio-soc");
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
|
||||
|
||||
nodename = g_strdup_printf("/memory@%lx",
|
||||
(long)memmap[VIRT_DRAM].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
memmap[VIRT_DRAM].base >> 32, memmap[VIRT_DRAM].base,
|
||||
mem_size >> 32, mem_size);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
|
||||
g_free(nodename);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/cpus");
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
|
||||
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
|
||||
|
||||
for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
|
||||
int cpu_phandle = phandle++;
|
||||
nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
char *isa = riscv_isa_string(&s->soc.harts[cpu]);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
|
||||
qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
|
||||
qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
|
||||
qemu_fdt_add_subnode(fdt, intc);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle);
|
||||
qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
|
||||
qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
|
||||
g_free(isa);
|
||||
g_free(intc);
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
cells = g_new0(uint32_t, s->soc.num_harts * 4);
|
||||
for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
|
||||
nodename =
|
||||
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
|
||||
cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
|
||||
g_free(nodename);
|
||||
}
|
||||
nodename = g_strdup_printf("/soc/clint@%lx",
|
||||
(long)memmap[VIRT_CLINT].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[VIRT_CLINT].base,
|
||||
0x0, memmap[VIRT_CLINT].size);
|
||||
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
|
||||
cells, s->soc.num_harts * sizeof(uint32_t) * 4);
|
||||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
plic_phandle = phandle++;
|
||||
cells = g_new0(uint32_t, s->soc.num_harts * 4);
|
||||
for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
|
||||
nodename =
|
||||
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
|
||||
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
|
||||
cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
|
||||
cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
|
||||
g_free(nodename);
|
||||
}
|
||||
nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
|
||||
(long)memmap[VIRT_PLIC].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
|
||||
qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
|
||||
cells, s->soc.num_harts * sizeof(uint32_t) * 4);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[VIRT_PLIC].base,
|
||||
0x0, memmap[VIRT_PLIC].size);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", VIRTIO_NDEV);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle);
|
||||
plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
|
||||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
for (i = 0; i < VIRTIO_COUNT; i++) {
|
||||
nodename = g_strdup_printf("/virtio_mmio@%lx",
|
||||
(long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size));
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "virtio,mmio");
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
|
||||
0x0, memmap[VIRT_VIRTIO].size);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "interrupts", VIRTIO_IRQ + i);
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
nodename = g_strdup_printf("/test@%lx",
|
||||
(long)memmap[VIRT_TEST].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,test0");
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[VIRT_TEST].base,
|
||||
0x0, memmap[VIRT_TEST].size);
|
||||
|
||||
nodename = g_strdup_printf("/uart@%lx",
|
||||
(long)memmap[VIRT_UART0].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a");
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[VIRT_UART0].base,
|
||||
0x0, memmap[VIRT_UART0].size);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ);
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/chosen");
|
||||
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
|
||||
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
|
||||
g_free(nodename);
|
||||
|
||||
return fdt;
|
||||
}
|
||||
|
||||
static void riscv_virt_board_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = virt_memmap;
|
||||
|
||||
RISCVVirtState *s = g_new0(RISCVVirtState, 1);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
|
||||
char *plic_hart_config;
|
||||
size_t plic_hart_config_len;
|
||||
int i;
|
||||
void *fdt;
|
||||
|
||||
/* Initialize SOC */
|
||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||
&error_abort);
|
||||
object_property_set_str(OBJECT(&s->soc), VIRT_CPU, "cpu-type",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&s->soc), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
/* register system main memory (actual RAM) */
|
||||
memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram",
|
||||
machine->ram_size, &error_fatal);
|
||||
memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base,
|
||||
main_mem);
|
||||
|
||||
/* create device tree */
|
||||
fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
|
||||
|
||||
/* boot rom */
|
||||
memory_region_init_ram(boot_rom, NULL, "riscv_virt_board.bootrom",
|
||||
s->fdt_size + 0x2000, &error_fatal);
|
||||
memory_region_add_subregion(system_memory, 0x0, boot_rom);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
uint64_t kernel_entry = load_kernel(machine->kernel_filename);
|
||||
|
||||
if (machine->initrd_filename) {
|
||||
hwaddr start;
|
||||
hwaddr end = load_initrd(machine->initrd_filename,
|
||||
machine->ram_size, kernel_entry,
|
||||
&start);
|
||||
qemu_fdt_setprop_cell(fdt, "/chosen",
|
||||
"linux,initrd-start", start);
|
||||
qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
|
||||
end);
|
||||
}
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
uint32_t reset_vec[8] = {
|
||||
0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
|
||||
0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
|
||||
0xf1402573, /* csrr a0, mhartid */
|
||||
#if defined(TARGET_RISCV32)
|
||||
0x0182a283, /* lw t0, 24(t0) */
|
||||
#elif defined(TARGET_RISCV64)
|
||||
0x0182b283, /* ld t0, 24(t0) */
|
||||
#endif
|
||||
0x00028067, /* jr t0 */
|
||||
0x00000000,
|
||||
memmap[VIRT_DRAM].base, /* start: .dword memmap[VIRT_DRAM].base */
|
||||
0x00000000,
|
||||
/* dtb: */
|
||||
};
|
||||
|
||||
/* copy in the reset vector */
|
||||
copy_le32_to_phys(ROM_BASE, reset_vec, sizeof(reset_vec));
|
||||
|
||||
/* copy in the device tree */
|
||||
qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
|
||||
cpu_physical_memory_write(ROM_BASE + sizeof(reset_vec),
|
||||
s->fdt, s->fdt_size);
|
||||
|
||||
/* create PLIC hart topology configuration string */
|
||||
plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus;
|
||||
plic_hart_config = g_malloc0(plic_hart_config_len);
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
if (i != 0) {
|
||||
strncat(plic_hart_config, ",", plic_hart_config_len);
|
||||
}
|
||||
strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len);
|
||||
plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1);
|
||||
}
|
||||
|
||||
/* MMIO */
|
||||
s->plic = sifive_plic_create(memmap[VIRT_PLIC].base,
|
||||
plic_hart_config,
|
||||
VIRT_PLIC_NUM_SOURCES,
|
||||
VIRT_PLIC_NUM_PRIORITIES,
|
||||
VIRT_PLIC_PRIORITY_BASE,
|
||||
VIRT_PLIC_PENDING_BASE,
|
||||
VIRT_PLIC_ENABLE_BASE,
|
||||
VIRT_PLIC_ENABLE_STRIDE,
|
||||
VIRT_PLIC_CONTEXT_BASE,
|
||||
VIRT_PLIC_CONTEXT_STRIDE,
|
||||
memmap[VIRT_PLIC].size);
|
||||
sifive_clint_create(memmap[VIRT_CLINT].base,
|
||||
memmap[VIRT_CLINT].size, smp_cpus,
|
||||
SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
|
||||
sifive_test_create(memmap[VIRT_TEST].base);
|
||||
|
||||
for (i = 0; i < VIRTIO_COUNT; i++) {
|
||||
sysbus_create_simple("virtio-mmio",
|
||||
memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
|
||||
SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]);
|
||||
}
|
||||
|
||||
serial_mm_init(system_memory, memmap[VIRT_UART0].base,
|
||||
0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193,
|
||||
serial_hds[0], DEVICE_LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void riscv_virt_board_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
k->init = riscv_virt_board_sysbus_device_init;
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_virt_board_device = {
|
||||
.name = TYPE_RISCV_VIRT_BOARD,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RISCVVirtState),
|
||||
.class_init = riscv_virt_board_class_init,
|
||||
};
|
||||
|
||||
static void riscv_virt_board_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "RISC-V VirtIO Board (Privileged spec v1.10)";
|
||||
mc->init = riscv_virt_board_init;
|
||||
mc->max_cpus = 8; /* hardcoded limit in BBL */
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("virt", riscv_virt_board_machine_init)
|
||||
|
||||
static void riscv_virt_board_register_types(void)
|
||||
{
|
||||
type_register_static(&riscv_virt_board_device);
|
||||
}
|
||||
|
||||
type_init(riscv_virt_board_register_types);
|
||||
@@ -29,8 +29,17 @@ typedef struct SCLPEventsBus {
|
||||
struct SCLPEventFacility {
|
||||
SysBusDevice parent_obj;
|
||||
SCLPEventsBus sbus;
|
||||
/* guest' receive mask */
|
||||
unsigned int receive_mask;
|
||||
/* guest's receive mask */
|
||||
sccb_mask_t receive_mask;
|
||||
/*
|
||||
* when false, we keep the same broken, backwards compatible behaviour as
|
||||
* before, allowing only masks of size exactly 4; when true, we implement
|
||||
* the architecture correctly, allowing all valid mask sizes. Needed for
|
||||
* migration toward older versions.
|
||||
*/
|
||||
bool allow_all_mask_sizes;
|
||||
/* length of the receive mask */
|
||||
uint16_t mask_length;
|
||||
};
|
||||
|
||||
/* return true if any child has event pending set */
|
||||
@@ -52,9 +61,9 @@ static bool event_pending(SCLPEventFacility *ef)
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned int get_host_send_mask(SCLPEventFacility *ef)
|
||||
static sccb_mask_t get_host_send_mask(SCLPEventFacility *ef)
|
||||
{
|
||||
unsigned int mask;
|
||||
sccb_mask_t mask;
|
||||
BusChild *kid;
|
||||
SCLPEventClass *child;
|
||||
|
||||
@@ -68,9 +77,9 @@ static unsigned int get_host_send_mask(SCLPEventFacility *ef)
|
||||
return mask;
|
||||
}
|
||||
|
||||
static unsigned int get_host_receive_mask(SCLPEventFacility *ef)
|
||||
static sccb_mask_t get_host_receive_mask(SCLPEventFacility *ef)
|
||||
{
|
||||
unsigned int mask;
|
||||
sccb_mask_t mask;
|
||||
BusChild *kid;
|
||||
SCLPEventClass *child;
|
||||
|
||||
@@ -180,7 +189,7 @@ out:
|
||||
}
|
||||
|
||||
static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb,
|
||||
unsigned int mask)
|
||||
sccb_mask_t mask)
|
||||
{
|
||||
uint16_t rc;
|
||||
int slen;
|
||||
@@ -220,10 +229,21 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* copy up to src_len bytes and fill the rest of dst with zeroes */
|
||||
static void copy_mask(uint8_t *dst, uint8_t *src, uint16_t dst_len,
|
||||
uint16_t src_len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dst_len; i++) {
|
||||
dst[i] = i < src_len ? src[i] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
|
||||
{
|
||||
unsigned int sclp_active_selection_mask;
|
||||
unsigned int sclp_cp_receive_mask;
|
||||
sccb_mask_t sclp_active_selection_mask;
|
||||
sccb_mask_t sclp_cp_receive_mask;
|
||||
|
||||
ReadEventData *red = (ReadEventData *) sccb;
|
||||
|
||||
@@ -240,7 +260,9 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
|
||||
sclp_active_selection_mask = sclp_cp_receive_mask;
|
||||
break;
|
||||
case SCLP_SELECTIVE_READ:
|
||||
sclp_active_selection_mask = be32_to_cpu(red->mask);
|
||||
copy_mask((uint8_t *)&sclp_active_selection_mask, (uint8_t *)&red->mask,
|
||||
sizeof(sclp_active_selection_mask), ef->mask_length);
|
||||
sclp_active_selection_mask = be32_to_cpu(sclp_active_selection_mask);
|
||||
if (!sclp_cp_receive_mask ||
|
||||
(sclp_active_selection_mask & ~sclp_cp_receive_mask)) {
|
||||
sccb->h.response_code =
|
||||
@@ -259,24 +281,14 @@ out:
|
||||
return;
|
||||
}
|
||||
|
||||
/* copy up to dst_len bytes and fill the rest of dst with zeroes */
|
||||
static void copy_mask(uint8_t *dst, uint8_t *src, uint16_t dst_len,
|
||||
uint16_t src_len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dst_len; i++) {
|
||||
dst[i] = i < src_len ? src[i] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb)
|
||||
{
|
||||
WriteEventMask *we_mask = (WriteEventMask *) sccb;
|
||||
uint16_t mask_length = be16_to_cpu(we_mask->mask_length);
|
||||
uint32_t tmp_mask;
|
||||
sccb_mask_t tmp_mask;
|
||||
|
||||
if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX)) {
|
||||
if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX) ||
|
||||
((mask_length != 4) && !ef->allow_all_mask_sizes)) {
|
||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH);
|
||||
goto out;
|
||||
}
|
||||
@@ -301,6 +313,7 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb)
|
||||
mask_length, sizeof(tmp_mask));
|
||||
|
||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
|
||||
ef->mask_length = mask_length;
|
||||
|
||||
out:
|
||||
return;
|
||||
@@ -356,6 +369,24 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
|
||||
}
|
||||
}
|
||||
|
||||
static bool vmstate_event_facility_mask_length_needed(void *opaque)
|
||||
{
|
||||
SCLPEventFacility *ef = opaque;
|
||||
|
||||
return ef->allow_all_mask_sizes;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_event_facility_mask_length = {
|
||||
.name = "vmstate-event-facility/mask_length",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.needed = vmstate_event_facility_mask_length_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT16(mask_length, SCLPEventFacility),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_event_facility = {
|
||||
.name = "vmstate-event-facility",
|
||||
.version_id = 0,
|
||||
@@ -363,15 +394,39 @@ static const VMStateDescription vmstate_event_facility = {
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(receive_mask, SCLPEventFacility),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription * []) {
|
||||
&vmstate_event_facility_mask_length,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
static void sclp_event_set_allow_all_mask_sizes(Object *obj, bool value,
|
||||
Error **errp)
|
||||
{
|
||||
SCLPEventFacility *ef = (SCLPEventFacility *)obj;
|
||||
|
||||
ef->allow_all_mask_sizes = value;
|
||||
}
|
||||
|
||||
static bool sclp_event_get_allow_all_mask_sizes(Object *obj, Error **e)
|
||||
{
|
||||
SCLPEventFacility *ef = (SCLPEventFacility *)obj;
|
||||
|
||||
return ef->allow_all_mask_sizes;
|
||||
}
|
||||
|
||||
static void init_event_facility(Object *obj)
|
||||
{
|
||||
SCLPEventFacility *event_facility = EVENT_FACILITY(obj);
|
||||
DeviceState *sdev = DEVICE(obj);
|
||||
Object *new;
|
||||
|
||||
event_facility->mask_length = 4;
|
||||
event_facility->allow_all_mask_sizes = true;
|
||||
object_property_add_bool(obj, "allow_all_mask_sizes",
|
||||
sclp_event_get_allow_all_mask_sizes,
|
||||
sclp_event_set_allow_all_mask_sizes, NULL);
|
||||
/* Spawn a new bus for SCLP events */
|
||||
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
|
||||
TYPE_SCLP_EVENTS_BUS, sdev, NULL);
|
||||
@@ -431,26 +486,12 @@ static void event_realize(DeviceState *qdev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static void event_unrealize(DeviceState *qdev, Error **errp)
|
||||
{
|
||||
SCLPEvent *event = SCLP_EVENT(qdev);
|
||||
SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
|
||||
if (child->exit) {
|
||||
int rc = child->exit(event);
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "SCLP event exit failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void event_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->bus_type = TYPE_SCLP_EVENTS_BUS;
|
||||
dc->realize = event_realize;
|
||||
dc->unrealize = event_unrealize;
|
||||
}
|
||||
|
||||
static const TypeInfo sclp_event_type_info = {
|
||||
|
||||
@@ -234,7 +234,7 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
|
||||
if (!get_boot_device(0)) {
|
||||
if (boot_menu) {
|
||||
error_report("boot menu requires a bootindex to be specified for "
|
||||
"the IPL device.");
|
||||
"the IPL device");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -250,7 +250,9 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
break;
|
||||
default:
|
||||
error_report("boot menu is not supported for this device type.");
|
||||
if (boot_menu) {
|
||||
error_report("boot menu is not supported for this device type");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -263,13 +265,13 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
|
||||
tmp = qemu_opt_get(opts, "splash-time");
|
||||
|
||||
if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) {
|
||||
error_report("splash-time is invalid, forcing it to 0.");
|
||||
error_report("splash-time is invalid, forcing it to 0");
|
||||
*timeout = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (splash_time > 0xffffffff) {
|
||||
error_report("splash-time is too large, forcing it to max value.");
|
||||
error_report("splash-time is too large, forcing it to max value");
|
||||
*timeout = 0xffffffff;
|
||||
return;
|
||||
}
|
||||
@@ -380,7 +382,8 @@ static int load_netboot_image(Error **errp)
|
||||
|
||||
netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
|
||||
if (netboot_filename == NULL) {
|
||||
error_setg(errp, "Could not find network bootloader");
|
||||
error_setg(errp, "Could not find network bootloader '%s'",
|
||||
ipl->netboot_fw);
|
||||
goto unref_mr;
|
||||
}
|
||||
|
||||
@@ -489,7 +492,7 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
|
||||
if (ipl->netboot) {
|
||||
if (load_netboot_image(&err) < 0) {
|
||||
error_report_err(err);
|
||||
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
||||
exit(1);
|
||||
}
|
||||
ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "s390-pci-bus.h"
|
||||
#include "hw/s390x/storage-keys.h"
|
||||
#include "hw/s390x/storage-attributes.h"
|
||||
#include "hw/s390x/event-facility.h"
|
||||
#include "hw/compat.h"
|
||||
#include "ipl.h"
|
||||
#include "hw/s390x/s390-virtio-ccw.h"
|
||||
@@ -254,8 +255,10 @@ static void s390_init_ipl_dev(const char *kernel_filename,
|
||||
}
|
||||
qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
|
||||
qdev_prop_set_string(dev, "firmware", firmware);
|
||||
qdev_prop_set_string(dev, "netboot_fw", netboot_fw);
|
||||
qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
|
||||
if (!strlen(object_property_get_str(new, "netboot_fw", &error_abort))) {
|
||||
qdev_prop_set_string(dev, "netboot_fw", netboot_fw);
|
||||
}
|
||||
object_property_add_child(qdev_get_machine(), TYPE_S390_IPL,
|
||||
new, NULL);
|
||||
object_unref(new);
|
||||
@@ -388,12 +391,14 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev,
|
||||
}
|
||||
}
|
||||
|
||||
static CpuInstanceProperties s390_cpu_index_to_props(MachineState *machine,
|
||||
static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms,
|
||||
unsigned cpu_index)
|
||||
{
|
||||
g_assert(machine->possible_cpus && cpu_index < machine->possible_cpus->len);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
|
||||
|
||||
return machine->possible_cpus->cpus[cpu_index].props;
|
||||
assert(cpu_index < possible_cpus->len);
|
||||
return possible_cpus->cpus[cpu_index].props;
|
||||
}
|
||||
|
||||
static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms)
|
||||
@@ -664,7 +669,12 @@ bool css_migration_enabled(void)
|
||||
type_init(ccw_machine_register_##suffix)
|
||||
|
||||
#define CCW_COMPAT_2_11 \
|
||||
HW_COMPAT_2_11
|
||||
HW_COMPAT_2_11 \
|
||||
{\
|
||||
.driver = TYPE_SCLP_EVENT_FACILITY,\
|
||||
.property = "allow_all_mask_sizes",\
|
||||
.value = "off",\
|
||||
},
|
||||
|
||||
#define CCW_COMPAT_2_10 \
|
||||
HW_COMPAT_2_10
|
||||
|
||||
@@ -37,12 +37,12 @@ void raise_irq_cpu_hotplug(void)
|
||||
sclp_service_interrupt(0);
|
||||
}
|
||||
|
||||
static unsigned int send_mask(void)
|
||||
static sccb_mask_t send_mask(void)
|
||||
{
|
||||
return SCLP_EVENT_MASK_CONFIG_MGT_DATA;
|
||||
}
|
||||
|
||||
static unsigned int receive_mask(void)
|
||||
static sccb_mask_t receive_mask(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ static bool can_handle_event(uint8_t type)
|
||||
return type == SCLP_EVENT_SIGNAL_QUIESCE;
|
||||
}
|
||||
|
||||
static unsigned int send_mask(void)
|
||||
static sccb_mask_t send_mask(void)
|
||||
{
|
||||
return SCLP_EVENT_MASK_SIGNAL_QUIESCE;
|
||||
}
|
||||
|
||||
static unsigned int receive_mask(void)
|
||||
static sccb_mask_t receive_mask(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -752,7 +752,7 @@ out_err:
|
||||
g_free(sch);
|
||||
}
|
||||
|
||||
static int virtio_ccw_exit(VirtioCcwDevice *dev)
|
||||
static void virtio_ccw_unrealize(VirtioCcwDevice *dev, Error **errp)
|
||||
{
|
||||
CcwDevice *ccw_dev = CCW_DEVICE(dev);
|
||||
SubchDev *sch = ccw_dev->sch;
|
||||
@@ -760,12 +760,12 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
|
||||
if (sch) {
|
||||
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
|
||||
g_free(sch);
|
||||
ccw_dev->sch = NULL;
|
||||
}
|
||||
if (dev->indicators) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators);
|
||||
dev->indicators = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp)
|
||||
@@ -1344,7 +1344,7 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_net_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_net_properties;
|
||||
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
|
||||
@@ -1372,7 +1372,7 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_blk_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_blk_properties;
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
@@ -1400,7 +1400,7 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_serial_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_serial_properties;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
@@ -1428,7 +1428,7 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_balloon_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_balloon_properties;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
@@ -1456,7 +1456,7 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_scsi_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_scsi_properties;
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
@@ -1483,7 +1483,7 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = vhost_ccw_scsi_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = vhost_ccw_scsi_properties;
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
@@ -1520,7 +1520,7 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_rng_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_rng_properties;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
@@ -1558,7 +1558,7 @@ static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_crypto_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_crypto_properties;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
@@ -1596,7 +1596,7 @@ static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_gpu_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_gpu_properties;
|
||||
dc->hotpluggable = false;
|
||||
@@ -1625,7 +1625,7 @@ static void virtio_ccw_input_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_ccw_input_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_input_properties;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
@@ -1705,12 +1705,12 @@ static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp)
|
||||
virtio_ccw_device_realize(_dev, errp);
|
||||
}
|
||||
|
||||
static int virtio_ccw_busdev_exit(DeviceState *dev)
|
||||
static void virtio_ccw_busdev_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
|
||||
VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
|
||||
|
||||
return _info->exit(_dev);
|
||||
_info->unrealize(_dev, errp);
|
||||
}
|
||||
|
||||
static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
|
||||
@@ -1728,7 +1728,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
k->unplug = virtio_ccw_busdev_unplug;
|
||||
dc->realize = virtio_ccw_busdev_realize;
|
||||
dc->exit = virtio_ccw_busdev_exit;
|
||||
dc->unrealize = virtio_ccw_busdev_unrealize;
|
||||
dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
|
||||
}
|
||||
|
||||
@@ -1804,7 +1804,7 @@ static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
k->realize = virtio_ccw_9p_realize;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = virtio_ccw_9p_properties;
|
||||
@@ -1853,7 +1853,7 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data)
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = vhost_vsock_ccw_realize;
|
||||
k->exit = virtio_ccw_exit;
|
||||
k->unrealize = virtio_ccw_unrealize;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
dc->props = vhost_vsock_ccw_properties;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
|
||||
@@ -76,7 +76,7 @@ typedef struct VirtioCcwDevice VirtioCcwDevice;
|
||||
typedef struct VirtIOCCWDeviceClass {
|
||||
CCWDeviceClass parent_class;
|
||||
void (*realize)(VirtioCcwDevice *dev, Error **errp);
|
||||
int (*exit)(VirtioCcwDevice *dev);
|
||||
void (*unrealize)(VirtioCcwDevice *dev, Error **errp);
|
||||
} VirtIOCCWDeviceClass;
|
||||
|
||||
/* Performance improves when virtqueue kick processing is decoupled from the
|
||||
|
||||
@@ -107,9 +107,10 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* assumes s->ctx held */
|
||||
static void virtio_scsi_clear_aio(VirtIOSCSI *s)
|
||||
/* Context: BH in IOThread */
|
||||
static void virtio_scsi_dataplane_stop_bh(void *opaque)
|
||||
{
|
||||
VirtIOSCSI *s = opaque;
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
int i;
|
||||
|
||||
@@ -171,7 +172,7 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev)
|
||||
return 0;
|
||||
|
||||
fail_vrings:
|
||||
virtio_scsi_clear_aio(s);
|
||||
aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
|
||||
aio_context_release(s->ctx);
|
||||
for (i = 0; i < vs->conf.num_queues + 2; i++) {
|
||||
virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
|
||||
@@ -207,7 +208,7 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
|
||||
s->dataplane_stopping = true;
|
||||
|
||||
aio_context_acquire(s->ctx);
|
||||
virtio_scsi_clear_aio(s);
|
||||
aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
|
||||
aio_context_release(s->ctx);
|
||||
|
||||
blk_drain_all(); /* ensure there are no in-flight requests */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
common-obj-$(CONFIG_PL181) += pl181.o
|
||||
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
|
||||
common-obj-$(CONFIG_SD) += sd.o core.o
|
||||
common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o
|
||||
common-obj-$(CONFIG_SDHCI) += sdhci.o
|
||||
|
||||
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
|
||||
|
||||
55
hw/sd/sd.c
55
hw/sd/sd.c
@@ -120,6 +120,7 @@ struct SDState {
|
||||
qemu_irq readonly_cb;
|
||||
qemu_irq inserted_cb;
|
||||
QEMUTimer *ocr_power_timer;
|
||||
const char *proto_name;
|
||||
bool enable;
|
||||
uint8_t dat_lines;
|
||||
bool cmd_line;
|
||||
@@ -866,13 +867,19 @@ static void sd_lock_command(SDState *sd)
|
||||
sd->card_status &= ~CARD_IS_LOCKED;
|
||||
}
|
||||
|
||||
static sd_rsp_type_t sd_normal_command(SDState *sd,
|
||||
SDRequest req)
|
||||
static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
{
|
||||
uint32_t rca = 0x0000;
|
||||
uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
|
||||
|
||||
trace_sdcard_normal_command(req.cmd, req.arg, sd_state_name(sd->state));
|
||||
/* CMD55 precedes an ACMD, so we are not interested in tracing it.
|
||||
* However there is no ACMD55, so we want to trace this particular case.
|
||||
*/
|
||||
if (req.cmd != 55 || sd->expecting_acmd) {
|
||||
trace_sdcard_normal_command(sd->proto_name,
|
||||
sd_cmd_name(req.cmd), req.cmd,
|
||||
req.arg, sd_state_name(sd->state));
|
||||
}
|
||||
|
||||
/* Not interpreting this as an app command */
|
||||
sd->card_status &= ~APP_CMD;
|
||||
@@ -1162,6 +1169,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
||||
}
|
||||
break;
|
||||
|
||||
case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
|
||||
if (sd->state == sd_transfer_state) {
|
||||
sd->state = sd_sendingdata_state;
|
||||
sd->data_offset = 0;
|
||||
return sd_r1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 23: /* CMD23: SET_BLOCK_COUNT */
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
@@ -1450,7 +1465,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
||||
static sd_rsp_type_t sd_app_command(SDState *sd,
|
||||
SDRequest req)
|
||||
{
|
||||
trace_sdcard_app_command(req.cmd, req.arg);
|
||||
trace_sdcard_app_command(sd->proto_name, sd_acmd_name(req.cmd),
|
||||
req.cmd, req.arg, sd_state_name(sd->state));
|
||||
sd->card_status |= APP_CMD;
|
||||
switch (req.cmd) {
|
||||
case 6: /* ACMD6: SET_BUS_WIDTH */
|
||||
@@ -1765,7 +1781,9 @@ void sd_write_data(SDState *sd, uint8_t value)
|
||||
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
|
||||
return;
|
||||
|
||||
trace_sdcard_write_data(sd->current_cmd, value);
|
||||
trace_sdcard_write_data(sd->proto_name,
|
||||
sd_acmd_name(sd->current_cmd),
|
||||
sd->current_cmd, value);
|
||||
switch (sd->current_cmd) {
|
||||
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
|
||||
sd->data[sd->data_offset ++] = value;
|
||||
@@ -1883,6 +1901,20 @@ void sd_write_data(SDState *sd, uint8_t value)
|
||||
}
|
||||
}
|
||||
|
||||
#define SD_TUNING_BLOCK_SIZE 64
|
||||
|
||||
static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = {
|
||||
/* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */
|
||||
0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc,
|
||||
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
|
||||
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
|
||||
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
|
||||
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
|
||||
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
|
||||
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
|
||||
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
|
||||
};
|
||||
|
||||
uint8_t sd_read_data(SDState *sd)
|
||||
{
|
||||
/* TODO: Append CRCs */
|
||||
@@ -1903,7 +1935,9 @@ uint8_t sd_read_data(SDState *sd)
|
||||
|
||||
io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
|
||||
|
||||
trace_sdcard_read_data(sd->current_cmd, io_len);
|
||||
trace_sdcard_read_data(sd->proto_name,
|
||||
sd_acmd_name(sd->current_cmd),
|
||||
sd->current_cmd, io_len);
|
||||
switch (sd->current_cmd) {
|
||||
case 6: /* CMD6: SWITCH_FUNCTION */
|
||||
ret = sd->data[sd->data_offset ++];
|
||||
@@ -1960,6 +1994,13 @@ uint8_t sd_read_data(SDState *sd)
|
||||
}
|
||||
break;
|
||||
|
||||
case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
|
||||
if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) {
|
||||
sd->state = sd_transfer_state;
|
||||
}
|
||||
ret = sd_tuning_block_pattern[sd->data_offset++];
|
||||
break;
|
||||
|
||||
case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
|
||||
ret = sd->data[sd->data_offset ++];
|
||||
|
||||
@@ -2029,6 +2070,8 @@ static void sd_realize(DeviceState *dev, Error **errp)
|
||||
SDState *sd = SD_CARD(dev);
|
||||
int ret;
|
||||
|
||||
sd->proto_name = sd->spi ? "SPI" : "SD";
|
||||
|
||||
if (sd->blk && blk_is_read_only(sd->blk)) {
|
||||
error_setg(errp, "Cannot use read-only drive as SD card");
|
||||
return;
|
||||
|
||||
@@ -433,13 +433,13 @@ static void sdhci_read_block_from_card(SDHCIState *s)
|
||||
for (index = 0; index < blk_size; index++) {
|
||||
data = sdbus_read_data(&s->sdbus);
|
||||
if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
|
||||
/* Device is not in tunning */
|
||||
/* Device is not in tuning */
|
||||
s->fifo_buffer[index] = data;
|
||||
}
|
||||
}
|
||||
|
||||
if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
|
||||
/* Device is in tunning */
|
||||
/* Device is in tuning */
|
||||
s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK;
|
||||
s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK;
|
||||
s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ |
|
||||
|
||||
72
hw/sd/sdmmc-internal.c
Normal file
72
hw/sd/sdmmc-internal.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SD/MMC cards common helpers
|
||||
*
|
||||
* Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sdmmc-internal.h"
|
||||
|
||||
const char *sd_cmd_name(uint8_t cmd)
|
||||
{
|
||||
static const char *cmd_abbrev[SDMMC_CMD_MAX] = {
|
||||
[0] = "GO_IDLE_STATE",
|
||||
[2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR",
|
||||
[4] = "SET_DSR", [5] = "IO_SEND_OP_COND",
|
||||
[6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD",
|
||||
[8] = "SEND_IF_COND", [9] = "SEND_CSD",
|
||||
[10] = "SEND_CID", [11] = "VOLTAGE_SWITCH",
|
||||
[12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS",
|
||||
[15] = "GO_INACTIVE_STATE",
|
||||
[16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK",
|
||||
[18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK",
|
||||
[20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec",
|
||||
[23] = "SET_BLOCK_COUNT",
|
||||
[24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK",
|
||||
[26] = "MANUF_RSVD", [27] = "PROGRAM_CSD",
|
||||
[28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT",
|
||||
[30] = "SEND_WRITE_PROT",
|
||||
[32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END",
|
||||
[34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD",
|
||||
[36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD",
|
||||
[38] = "ERASE",
|
||||
[40] = "DPS_spec",
|
||||
[42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT",
|
||||
[44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B",
|
||||
[46] = "Q_RD_TASK", [47] = "Q_WR_TASK",
|
||||
[48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE",
|
||||
[50] = "SW_FUNC_RSVD",
|
||||
[52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED",
|
||||
[54] = "SDIO_RSVD", [55] = "APP_CMD",
|
||||
[56] = "GEN_CMD", [57] = "SW_FUNC_RSVD",
|
||||
[58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI",
|
||||
[60] = "MANUF_RSVD", [61] = "MANUF_RSVD",
|
||||
[62] = "MANUF_RSVD", [63] = "MANUF_RSVD",
|
||||
};
|
||||
return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD";
|
||||
}
|
||||
|
||||
const char *sd_acmd_name(uint8_t cmd)
|
||||
{
|
||||
static const char *acmd_abbrev[SDMMC_CMD_MAX] = {
|
||||
[6] = "SET_BUS_WIDTH",
|
||||
[13] = "SD_STATUS",
|
||||
[14] = "DPS_spec", [15] = "DPS_spec",
|
||||
[16] = "DPS_spec",
|
||||
[18] = "SECU_spec",
|
||||
[22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT",
|
||||
[41] = "SD_SEND_OP_COND",
|
||||
[42] = "SET_CLR_CARD_DETECT",
|
||||
[51] = "SEND_SCR",
|
||||
[52] = "SECU_spec", [53] = "SECU_spec",
|
||||
[54] = "SECU_spec",
|
||||
[56] = "SECU_spec", [57] = "SECU_spec",
|
||||
[58] = "SECU_spec", [59] = "SECU_spec",
|
||||
};
|
||||
|
||||
return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD";
|
||||
}
|
||||
@@ -12,4 +12,28 @@
|
||||
|
||||
#define SDMMC_CMD_MAX 64
|
||||
|
||||
/**
|
||||
* sd_cmd_name:
|
||||
* @cmd: A SD "normal" command, up to SDMMC_CMD_MAX.
|
||||
*
|
||||
* Returns a human-readable name describing the command.
|
||||
* The return value is always a static string which does not need
|
||||
* to be freed after use.
|
||||
*
|
||||
* Returns: The command name of @cmd or "UNKNOWN_CMD".
|
||||
*/
|
||||
const char *sd_cmd_name(uint8_t cmd);
|
||||
|
||||
/**
|
||||
* sd_acmd_name:
|
||||
* @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX.
|
||||
*
|
||||
* Returns a human-readable name describing the application command.
|
||||
* The return value is always a static string which does not need
|
||||
* to be freed after use.
|
||||
*
|
||||
* Returns: The application command name of @cmd or "UNKNOWN_ACMD".
|
||||
*/
|
||||
const char *sd_acmd_name(uint8_t cmd);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,8 +24,8 @@ sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of
|
||||
sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
|
||||
|
||||
# hw/sd/sd.c
|
||||
sdcard_normal_command(uint8_t cmd, uint32_t arg, const char *state) "CMD%d arg 0x%08x (state %s)"
|
||||
sdcard_app_command(uint8_t acmd, uint32_t arg) "ACMD%d arg 0x%08x"
|
||||
sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)"
|
||||
sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)"
|
||||
sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)"
|
||||
sdcard_powerup(void) ""
|
||||
sdcard_inquiry_cmd41(void) ""
|
||||
@@ -39,8 +39,8 @@ sdcard_lock(void) ""
|
||||
sdcard_unlock(void) ""
|
||||
sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
|
||||
sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
|
||||
sdcard_write_data(uint8_t cmd, uint8_t value) "CMD%02d value 0x%02x"
|
||||
sdcard_read_data(uint8_t cmd, int length) "CMD%02d len %d"
|
||||
sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x"
|
||||
sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d"
|
||||
sdcard_set_voltage(uint16_t millivolts) "%u mV"
|
||||
|
||||
# hw/sd/milkymist-memcard.c
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "sysemu/reset.h"
|
||||
#include "tpm_int.h"
|
||||
#include "tpm_util.h"
|
||||
#include "trace.h"
|
||||
|
||||
typedef struct CRBState {
|
||||
DeviceState parent_obj;
|
||||
@@ -44,14 +45,6 @@ typedef struct CRBState {
|
||||
|
||||
#define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB)
|
||||
|
||||
#define DEBUG_CRB 0
|
||||
|
||||
#define DPRINTF(fmt, ...) do { \
|
||||
if (DEBUG_CRB) { \
|
||||
printf(fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CRB_INTF_TYPE_CRB_ACTIVE 0b1
|
||||
#define CRB_INTF_VERSION_CRB 0b1
|
||||
#define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0
|
||||
@@ -91,8 +84,8 @@ static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
|
||||
unsigned offset = addr & 3;
|
||||
uint32_t val = *(uint32_t *)regs >> (8 * offset);
|
||||
|
||||
DPRINTF("CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 "\n",
|
||||
addr, size, val);
|
||||
trace_tpm_crb_mmio_read(addr, size, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -100,8 +93,8 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
CRBState *s = CRB(opaque);
|
||||
DPRINTF("CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx64 "\n",
|
||||
addr, size, val);
|
||||
|
||||
trace_tpm_crb_mmio_write(addr, size, val);
|
||||
|
||||
switch (addr) {
|
||||
case A_CRB_CTRL_REQ:
|
||||
|
||||
@@ -40,14 +40,7 @@
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-tpm.h"
|
||||
#include "chardev/char-fe.h"
|
||||
|
||||
#define DEBUG_TPM 0
|
||||
|
||||
#define DPRINTF(fmt, ...) do { \
|
||||
if (DEBUG_TPM) { \
|
||||
fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#include "trace.h"
|
||||
|
||||
#define TYPE_TPM_EMULATOR "tpm-emulator"
|
||||
#define TPM_EMULATOR(obj) \
|
||||
@@ -152,13 +145,12 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
|
||||
{
|
||||
ptm_loc loc;
|
||||
|
||||
DPRINTF("%s : locality: 0x%x", __func__, locty_number);
|
||||
|
||||
if (tpm_emu->cur_locty_number == locty_number) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DPRINTF("setting locality : 0x%x", locty_number);
|
||||
trace_tpm_emulator_set_locality(locty_number);
|
||||
|
||||
loc.u.req.loc = locty_number;
|
||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc,
|
||||
sizeof(loc), sizeof(loc)) < 0) {
|
||||
@@ -184,7 +176,7 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
|
||||
{
|
||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||
|
||||
DPRINTF("processing TPM command");
|
||||
trace_tpm_emulator_handle_request();
|
||||
|
||||
if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 ||
|
||||
tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
|
||||
@@ -196,7 +188,6 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
|
||||
|
||||
static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
|
||||
{
|
||||
DPRINTF("%s", __func__);
|
||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY,
|
||||
&tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {
|
||||
error_report("tpm-emulator: probing failed : %s", strerror(errno));
|
||||
@@ -205,7 +196,7 @@ static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
|
||||
|
||||
tpm_emu->caps = be64_to_cpu(tpm_emu->caps);
|
||||
|
||||
DPRINTF("capabilities : 0x%"PRIx64, tpm_emu->caps);
|
||||
trace_tpm_emulator_probe_caps(tpm_emu->caps);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -294,7 +285,7 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
|
||||
*actual_size = be32_to_cpu(psbs.u.resp.buffersize);
|
||||
}
|
||||
|
||||
DPRINTF("buffer size: %u, min: %u, max: %u\n",
|
||||
trace_tpm_emulator_set_buffer_size(
|
||||
be32_to_cpu(psbs.u.resp.buffersize),
|
||||
be32_to_cpu(psbs.u.resp.minsize),
|
||||
be32_to_cpu(psbs.u.resp.maxsize));
|
||||
@@ -315,7 +306,7 @@ static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
DPRINTF("%s", __func__);
|
||||
trace_tpm_emulator_startup_tpm();
|
||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
|
||||
sizeof(init)) < 0) {
|
||||
error_report("tpm-emulator: could not send INIT: %s",
|
||||
@@ -349,7 +340,7 @@ static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
DPRINTF("got established flag: %0x", est.u.resp.bit);
|
||||
trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit);
|
||||
|
||||
tpm_emu->established_flag_cached = 1;
|
||||
tpm_emu->established_flag = (est.u.resp.bit != 0);
|
||||
@@ -396,7 +387,7 @@ static void tpm_emulator_cancel_cmd(TPMBackend *tb)
|
||||
ptm_res res;
|
||||
|
||||
if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) {
|
||||
DPRINTF("Backend does not support CANCEL_TPM_CMD");
|
||||
trace_tpm_emulator_cancel_cmd_not_supt();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -522,8 +513,16 @@ static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
|
||||
goto err;
|
||||
}
|
||||
|
||||
DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" :
|
||||
(tpm_emu->tpm_version == TPM_VERSION_2_0 ? "2.0" : "Unspecified"));
|
||||
switch (tpm_emu->tpm_version) {
|
||||
case TPM_VERSION_1_2:
|
||||
trace_tpm_emulator_handle_device_opts_tpm12();
|
||||
break;
|
||||
case TPM_VERSION_2_0:
|
||||
trace_tpm_emulator_handle_device_opts_tpm2();
|
||||
break;
|
||||
default:
|
||||
trace_tpm_emulator_handle_device_opts_unspec();
|
||||
}
|
||||
|
||||
if (tpm_emulator_probe_caps(tpm_emu) ||
|
||||
tpm_emulator_check_caps(tpm_emu)) {
|
||||
@@ -533,7 +532,8 @@ static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
|
||||
return tpm_emulator_block_migration(tpm_emu);
|
||||
|
||||
err:
|
||||
DPRINTF("Startup error");
|
||||
trace_tpm_emulator_handle_device_opts_startup_error();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -574,7 +574,8 @@ static void tpm_emulator_inst_init(Object *obj)
|
||||
{
|
||||
TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
|
||||
|
||||
DPRINTF("%s", __func__);
|
||||
trace_tpm_emulator_inst_init();
|
||||
|
||||
tpm_emu->options = g_new0(TPMEmulatorOptions, 1);
|
||||
tpm_emu->cur_locty_number = ~0;
|
||||
qemu_mutex_init(&tpm_emu->mutex);
|
||||
|
||||
@@ -32,14 +32,7 @@
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-tpm.h"
|
||||
#include "tpm_util.h"
|
||||
|
||||
#define DEBUG_TPM 0
|
||||
|
||||
#define DPRINTF(fmt, ...) do { \
|
||||
if (DEBUG_TPM) { \
|
||||
fprintf(stderr, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#include "trace.h"
|
||||
|
||||
#define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
|
||||
#define TPM_PASSTHROUGH(obj) \
|
||||
@@ -138,7 +131,7 @@ static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
|
||||
{
|
||||
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
|
||||
|
||||
DPRINTF("tpm_passthrough: processing command %p\n", cmd);
|
||||
trace_tpm_passthrough_handle_request(cmd);
|
||||
|
||||
tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
|
||||
cmd->out, cmd->out_len, &cmd->selftest_done,
|
||||
@@ -147,7 +140,7 @@ static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
|
||||
|
||||
static void tpm_passthrough_reset(TPMBackend *tb)
|
||||
{
|
||||
DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
|
||||
trace_tpm_passthrough_reset();
|
||||
|
||||
tpm_passthrough_cancel_cmd(tb);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "sysemu/tpm_backend.h"
|
||||
#include "tpm_int.h"
|
||||
#include "tpm_util.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */
|
||||
#define TPM_TIS_LOCALITY_SHIFT 12
|
||||
@@ -86,12 +87,6 @@ typedef struct TPMState {
|
||||
|
||||
#define DEBUG_TIS 0
|
||||
|
||||
#define DPRINTF(fmt, ...) do { \
|
||||
if (DEBUG_TIS) { \
|
||||
printf(fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* local prototypes */
|
||||
|
||||
static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
|
||||
@@ -107,19 +102,17 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
|
||||
static void tpm_tis_show_buffer(const unsigned char *buffer,
|
||||
size_t buffer_size, const char *string)
|
||||
{
|
||||
#ifdef DEBUG_TIS
|
||||
uint32_t len, i;
|
||||
|
||||
len = MIN(tpm_cmd_get_size(buffer), buffer_size);
|
||||
DPRINTF("tpm_tis: %s length = %d\n", string, len);
|
||||
printf("tpm_tis: %s length = %d\n", string, len);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i && !(i % 16)) {
|
||||
DPRINTF("\n");
|
||||
printf("\n");
|
||||
}
|
||||
DPRINTF("%.2X ", buffer[i]);
|
||||
printf("%.2X ", buffer[i]);
|
||||
}
|
||||
DPRINTF("\n");
|
||||
#endif
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -146,8 +139,10 @@ static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
|
||||
*/
|
||||
static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
|
||||
{
|
||||
tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
|
||||
"tpm_tis: To TPM");
|
||||
if (DEBUG_TIS) {
|
||||
tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
|
||||
"tpm_tis: To TPM");
|
||||
}
|
||||
|
||||
/*
|
||||
* rw_offset serves as length indicator for length of data;
|
||||
@@ -175,7 +170,7 @@ static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
|
||||
|
||||
if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
|
||||
(s->loc[locty].inte & irqmask)) {
|
||||
DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
|
||||
trace_tpm_tis_raise_irq(irqmask);
|
||||
qemu_irq_raise(s->irq);
|
||||
s->loc[locty].ints |= irqmask;
|
||||
}
|
||||
@@ -223,7 +218,7 @@ static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
|
||||
|
||||
s->active_locty = new_active_locty;
|
||||
|
||||
DPRINTF("tpm_tis: Active locality is now %d\n", s->active_locty);
|
||||
trace_tpm_tis_new_active_locality(s->active_locty);
|
||||
|
||||
if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
|
||||
/* set flags on the new active locality */
|
||||
@@ -242,7 +237,7 @@ static void tpm_tis_abort(TPMState *s, uint8_t locty)
|
||||
{
|
||||
s->rw_offset = 0;
|
||||
|
||||
DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", s->next_locty);
|
||||
trace_tpm_tis_abort(s->next_locty);
|
||||
|
||||
/*
|
||||
* Need to react differently depending on who's aborting now and
|
||||
@@ -310,8 +305,10 @@ static void tpm_tis_request_completed(TPMIf *ti, int ret)
|
||||
s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
|
||||
s->rw_offset = 0;
|
||||
|
||||
tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
|
||||
"tpm_tis: From TPM");
|
||||
if (DEBUG_TIS) {
|
||||
tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
|
||||
"tpm_tis: From TPM");
|
||||
}
|
||||
|
||||
if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
|
||||
tpm_tis_abort(s, locty);
|
||||
@@ -339,8 +336,7 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
|
||||
tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
|
||||
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
|
||||
}
|
||||
DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
|
||||
ret, s->rw_offset - 1);
|
||||
trace_tpm_tis_data_read(ret, s->rw_offset - 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -364,29 +360,29 @@ static void tpm_tis_dump_state(void *opaque, hwaddr addr)
|
||||
hwaddr base = addr & ~0xfff;
|
||||
TPMState *s = opaque;
|
||||
|
||||
DPRINTF("tpm_tis: active locality : %d\n"
|
||||
"tpm_tis: state of locality %d : %d\n"
|
||||
"tpm_tis: register dump:\n",
|
||||
s->active_locty,
|
||||
locty, s->loc[locty].state);
|
||||
printf("tpm_tis: active locality : %d\n"
|
||||
"tpm_tis: state of locality %d : %d\n"
|
||||
"tpm_tis: register dump:\n",
|
||||
s->active_locty,
|
||||
locty, s->loc[locty].state);
|
||||
|
||||
for (idx = 0; regs[idx] != 0xfff; idx++) {
|
||||
DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
|
||||
(int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
|
||||
printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
|
||||
(int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
|
||||
}
|
||||
|
||||
DPRINTF("tpm_tis: r/w offset : %d\n"
|
||||
"tpm_tis: result buffer : ",
|
||||
s->rw_offset);
|
||||
printf("tpm_tis: r/w offset : %d\n"
|
||||
"tpm_tis: result buffer : ",
|
||||
s->rw_offset);
|
||||
for (idx = 0;
|
||||
idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size);
|
||||
idx++) {
|
||||
DPRINTF("%c%02x%s",
|
||||
s->rw_offset == idx ? '>' : ' ',
|
||||
s->buffer[idx],
|
||||
((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
|
||||
printf("%c%02x%s",
|
||||
s->rw_offset == idx ? '>' : ' ',
|
||||
s->buffer[idx],
|
||||
((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
|
||||
}
|
||||
DPRINTF("\n");
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -506,7 +502,7 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
|
||||
val >>= shift;
|
||||
}
|
||||
|
||||
DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (int)val);
|
||||
trace_tpm_tis_mmio_read(size, addr, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -527,10 +523,10 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
|
||||
uint16_t len;
|
||||
uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
|
||||
|
||||
DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (int)val);
|
||||
trace_tpm_tis_mmio_write(size, addr, val);
|
||||
|
||||
if (locty == 4) {
|
||||
DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n");
|
||||
trace_tpm_tis_mmio_write_locty4();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -560,20 +556,18 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
|
||||
if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
|
||||
/* give up locality if currently owned */
|
||||
if (s->active_locty == locty) {
|
||||
DPRINTF("tpm_tis: Releasing locality %d\n", locty);
|
||||
trace_tpm_tis_mmio_write_release_locty(locty);
|
||||
|
||||
uint8_t newlocty = TPM_TIS_NO_LOCALITY;
|
||||
/* anybody wants the locality ? */
|
||||
for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
|
||||
if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
|
||||
DPRINTF("tpm_tis: Locality %d requests use.\n", c);
|
||||
trace_tpm_tis_mmio_write_locty_req_use(c);
|
||||
newlocty = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
|
||||
"Next active locality: %d\n",
|
||||
newlocty);
|
||||
trace_tpm_tis_mmio_write_next_locty(newlocty);
|
||||
|
||||
if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
|
||||
set_new_locty = 0;
|
||||
@@ -627,10 +621,10 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
|
||||
}
|
||||
|
||||
s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
|
||||
DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
|
||||
"Locality %d seized from locality %d\n",
|
||||
locty, s->active_locty);
|
||||
DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
|
||||
|
||||
trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty);
|
||||
trace_tpm_tis_mmio_write_init_abort();
|
||||
|
||||
set_new_locty = 0;
|
||||
tpm_tis_prep_abort(s, s->active_locty, locty);
|
||||
break;
|
||||
@@ -677,7 +671,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
|
||||
s->loc[locty].ints &= ~val;
|
||||
if (s->loc[locty].ints == 0) {
|
||||
qemu_irq_lower(s->irq);
|
||||
DPRINTF("tpm_tis: Lowering IRQ\n");
|
||||
trace_tpm_tis_mmio_write_lowering_irq();
|
||||
}
|
||||
}
|
||||
s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
|
||||
@@ -725,8 +719,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
|
||||
case TPM_TIS_STATE_EXECUTION:
|
||||
case TPM_TIS_STATE_RECEPTION:
|
||||
/* abort currently running command */
|
||||
DPRINTF("tpm_tis: %s: Initiating abort.\n",
|
||||
__func__);
|
||||
trace_tpm_tis_mmio_write_init_abort();
|
||||
tpm_tis_prep_abort(s, locty, locty);
|
||||
break;
|
||||
|
||||
@@ -780,8 +773,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
|
||||
s->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
|
||||
/* drop the byte */
|
||||
} else {
|
||||
DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n",
|
||||
(int)val, size);
|
||||
trace_tpm_tis_mmio_write_data2send(val, size);
|
||||
if (s->loc[locty].state == TPM_TIS_STATE_READY) {
|
||||
s->loc[locty].state = TPM_TIS_STATE_RECEPTION;
|
||||
tpm_tis_sts_set(&s->loc[locty],
|
||||
|
||||
@@ -28,14 +28,7 @@
|
||||
#include "exec/memory.h"
|
||||
#include "sysemu/tpm_backend.h"
|
||||
#include "hw/qdev.h"
|
||||
|
||||
#define DEBUG_TPM 0
|
||||
|
||||
#define DPRINTF(fmt, ...) do { \
|
||||
if (DEBUG_TPM) { \
|
||||
fprintf(stderr, "tpm-util:"fmt"\n", ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#include "trace.h"
|
||||
|
||||
/* tpm backend property */
|
||||
|
||||
@@ -279,10 +272,11 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
|
||||
|
||||
if (be32_to_cpu(tpm_resp.hdr.len) != sizeof(tpm_resp) ||
|
||||
be32_to_cpu(tpm_resp.len) != sizeof(uint32_t)) {
|
||||
DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n",
|
||||
be32_to_cpu(tpm_resp.hdr.len), sizeof(tpm_resp));
|
||||
DPRINTF("tpm_resp->len = %u, expected = %zu\n",
|
||||
be32_to_cpu(tpm_resp.len), sizeof(uint32_t));
|
||||
trace_tpm_util_get_buffer_size_hdr_len(
|
||||
be32_to_cpu(tpm_resp.hdr.len),
|
||||
sizeof(tpm_resp));
|
||||
trace_tpm_util_get_buffer_size_len(be32_to_cpu(tpm_resp.len),
|
||||
sizeof(uint32_t));
|
||||
error_report("tpm_util: Got unexpected response to "
|
||||
"TPM_GetCapability; errcode: 0x%x",
|
||||
be32_to_cpu(tpm_resp.hdr.errcode));
|
||||
@@ -327,10 +321,11 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
|
||||
|
||||
if (be32_to_cpu(tpm2_resp.hdr.len) != sizeof(tpm2_resp) ||
|
||||
be32_to_cpu(tpm2_resp.count) != 2) {
|
||||
DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n",
|
||||
be32_to_cpu(tpm2_resp.hdr.len), sizeof(tpm2_resp));
|
||||
DPRINTF("tpm2_resp->len = %u, expected = %u\n",
|
||||
be32_to_cpu(tpm2_resp.count), 2);
|
||||
trace_tpm_util_get_buffer_size_hdr_len2(
|
||||
be32_to_cpu(tpm2_resp.hdr.len),
|
||||
sizeof(tpm2_resp));
|
||||
trace_tpm_util_get_buffer_size_len2(
|
||||
be32_to_cpu(tpm2_resp.count), 2);
|
||||
error_report("tpm_util: Got unexpected response to "
|
||||
"TPM2_GetCapability; errcode: 0x%x",
|
||||
be32_to_cpu(tpm2_resp.hdr.errcode));
|
||||
@@ -344,7 +339,7 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
DPRINTF("buffersize of device: %zu\n", *buffersize);
|
||||
trace_tpm_util_get_buffer_size(*buffersize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
46
hw/tpm/trace-events
Normal file
46
hw/tpm/trace-events
Normal file
@@ -0,0 +1,46 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# hw/tpm/tpm_crb.c
|
||||
tpm_crb_mmio_read(uint64_t addr, unsigned size, uint32_t val) "CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32
|
||||
tpm_crb_mmio_write(uint64_t addr, unsigned size, uint32_t val) "CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32
|
||||
|
||||
# hw/tpm/tpm_passthrough.c
|
||||
tpm_passthrough_handle_request(void *cmd) "processing command %p"
|
||||
tpm_passthrough_reset(void) "reset"
|
||||
|
||||
# hw/tpm/tpm_util.c
|
||||
tpm_util_get_buffer_size_hdr_len(uint32_t len, size_t expected) "tpm_resp->hdr.len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu"
|
||||
tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu"
|
||||
|
||||
# hw/tpm/tpm_emulator.c
|
||||
tpm_emulator_set_locality(uint8_t locty) "setting locality to %d"
|
||||
tpm_emulator_handle_request(void) "processing TPM command"
|
||||
tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64
|
||||
tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u"
|
||||
tpm_emulator_startup_tpm(void) "startup"
|
||||
tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d"
|
||||
tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD"
|
||||
tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2"
|
||||
tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2"
|
||||
tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified"
|
||||
tpm_emulator_handle_device_opts_startup_error(void) "Startup error"
|
||||
tpm_emulator_inst_init(void) ""
|
||||
|
||||
# hw/tpm/tpm_tis.c
|
||||
tpm_tis_raise_irq(uint32_t irqmask) "Raising IRQ for flag 0x%08x"
|
||||
tpm_tis_new_active_locality(uint8_t locty) "Active locality is now %d"
|
||||
tpm_tis_abort(uint8_t locty) "New active locality is %d"
|
||||
tpm_tis_data_read(uint32_t value, uint32_t off) "byte 0x%02x [%d]"
|
||||
tpm_tis_mmio_read(unsigned size, uint32_t addr, uint32_t val) " read.%u(0x%08x) = 0x%08x"
|
||||
tpm_tis_mmio_write(unsigned size, uint32_t addr, uint32_t val) "write.%u(0x%08x) = 0x%08x"
|
||||
tpm_tis_mmio_write_locty4(void) "Access to locality 4 only allowed from hardware"
|
||||
tpm_tis_mmio_write_release_locty(uint8_t locty) "Releasing locality %d"
|
||||
tpm_tis_mmio_write_locty_req_use(uint8_t locty) "Locality %d requests use"
|
||||
tpm_tis_mmio_write_next_locty(uint8_t locty) "Next active locality is %d"
|
||||
tpm_tis_mmio_write_locty_seized(uint8_t locty, uint8_t active) "Locality %d seized from locality %d"
|
||||
tpm_tis_mmio_write_init_abort(void) "Initiating abort"
|
||||
tpm_tis_mmio_write_lowering_irq(void) "Lowering IRQ"
|
||||
tpm_tis_mmio_write_data2send(uint32_t value, unsigned size) "Data to send to TPM: 0x%08x (size=%d)"
|
||||
@@ -6,8 +6,8 @@
|
||||
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
|
||||
* Pierre Morel <pmorel@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or(at
|
||||
* your option) any version. See the COPYING file in the top-level
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||
* your option) any later version. See the COPYING file in the top-level
|
||||
* directory.
|
||||
*/
|
||||
|
||||
|
||||
@@ -113,4 +113,17 @@ typedef struct {
|
||||
*/
|
||||
void aio_wait_kick(AioWait *wait);
|
||||
|
||||
/**
|
||||
* aio_wait_bh_oneshot:
|
||||
* @ctx: the aio context
|
||||
* @cb: the BH callback function
|
||||
* @opaque: user data for the BH callback function
|
||||
*
|
||||
* Run a BH in @ctx and wait for it to complete.
|
||||
*
|
||||
* Must be called from the main loop thread with @ctx acquired exactly once.
|
||||
* Note that main loop event processing may occur.
|
||||
*/
|
||||
void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque);
|
||||
|
||||
#endif /* QEMU_AIO_WAIT */
|
||||
|
||||
@@ -226,6 +226,7 @@ char *bdrv_perm_names(uint64_t perm);
|
||||
void bdrv_init(void);
|
||||
void bdrv_init_with_whitelist(void);
|
||||
bool bdrv_uses_whitelist(void);
|
||||
int bdrv_is_whitelisted(BlockDriver *drv, bool read_only);
|
||||
BlockDriver *bdrv_find_protocol(const char *filename,
|
||||
bool allow_protocol_prefix,
|
||||
Error **errp);
|
||||
@@ -246,6 +247,7 @@ BdrvChild *bdrv_open_child(const char *filename,
|
||||
BlockDriverState* parent,
|
||||
const BdrvChildRole *child_role,
|
||||
bool allow_none, Error **errp);
|
||||
BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp);
|
||||
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
|
||||
Error **errp);
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
|
||||
@@ -129,8 +129,11 @@ struct BlockDriver {
|
||||
int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp);
|
||||
void (*bdrv_close)(BlockDriverState *bs);
|
||||
int coroutine_fn (*bdrv_co_create_opts)(const char *filename, QemuOpts *opts,
|
||||
int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts,
|
||||
Error **errp);
|
||||
int coroutine_fn (*bdrv_co_create_opts)(const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp);
|
||||
int (*bdrv_make_empty)(BlockDriverState *bs);
|
||||
|
||||
void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options);
|
||||
@@ -224,7 +227,8 @@ struct BlockDriver {
|
||||
/*
|
||||
* Invalidate any cached meta-data.
|
||||
*/
|
||||
void (*bdrv_invalidate_cache)(BlockDriverState *bs, Error **errp);
|
||||
void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs,
|
||||
Error **errp);
|
||||
int (*bdrv_inactivate)(BlockDriverState *bs);
|
||||
|
||||
/*
|
||||
@@ -306,8 +310,9 @@ struct BlockDriver {
|
||||
* Returns 0 for completed check, -errno for internal errors.
|
||||
* The check results are stored in result.
|
||||
*/
|
||||
int (*bdrv_check)(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
BdrvCheckMode fix);
|
||||
int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs,
|
||||
BdrvCheckResult *result,
|
||||
BdrvCheckMode fix);
|
||||
|
||||
int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
|
||||
@@ -429,6 +429,8 @@ int print_insn_lm32 (bfd_vma, disassemble_info*);
|
||||
int print_insn_big_nios2 (bfd_vma, disassemble_info*);
|
||||
int print_insn_little_nios2 (bfd_vma, disassemble_info*);
|
||||
int print_insn_xtensa (bfd_vma, disassemble_info*);
|
||||
int print_insn_riscv32 (bfd_vma, disassemble_info*);
|
||||
int print_insn_riscv64 (bfd_vma, disassemble_info*);
|
||||
|
||||
#if 0
|
||||
/* Fetch the disassembler for a given BFD, if that support is available. */
|
||||
|
||||
@@ -119,6 +119,8 @@ typedef int64_t Elf64_Sxword;
|
||||
|
||||
#define EM_UNICORE32 110 /* UniCore32 */
|
||||
|
||||
#define EM_RISCV 243 /* RISC-V */
|
||||
|
||||
/*
|
||||
* This is an interim value that we will use until the committee comes
|
||||
* up with a final number.
|
||||
|
||||
222
include/hw/arm/fsl-imx7.h
Normal file
222
include/hw/arm/fsl-imx7.h
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Impinj, Inc.
|
||||
*
|
||||
* i.MX7 SoC definitions
|
||||
*
|
||||
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FSL_IMX7_H
|
||||
#define FSL_IMX7_H
|
||||
|
||||
#include "hw/arm/arm.h"
|
||||
#include "hw/cpu/a15mpcore.h"
|
||||
#include "hw/intc/imx_gpcv2.h"
|
||||
#include "hw/misc/imx7_ccm.h"
|
||||
#include "hw/misc/imx7_snvs.h"
|
||||
#include "hw/misc/imx7_gpr.h"
|
||||
#include "hw/misc/imx6_src.h"
|
||||
#include "hw/misc/imx2_wdt.h"
|
||||
#include "hw/gpio/imx_gpio.h"
|
||||
#include "hw/char/imx_serial.h"
|
||||
#include "hw/timer/imx_gpt.h"
|
||||
#include "hw/timer/imx_epit.h"
|
||||
#include "hw/i2c/imx_i2c.h"
|
||||
#include "hw/gpio/imx_gpio.h"
|
||||
#include "hw/sd/sdhci.h"
|
||||
#include "hw/ssi/imx_spi.h"
|
||||
#include "hw/net/imx_fec.h"
|
||||
#include "hw/pci-host/designware.h"
|
||||
#include "hw/usb/chipidea.h"
|
||||
#include "exec/memory.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#define TYPE_FSL_IMX7 "fsl,imx7"
|
||||
#define FSL_IMX7(obj) OBJECT_CHECK(FslIMX7State, (obj), TYPE_FSL_IMX7)
|
||||
|
||||
enum FslIMX7Configuration {
|
||||
FSL_IMX7_NUM_CPUS = 2,
|
||||
FSL_IMX7_NUM_UARTS = 7,
|
||||
FSL_IMX7_NUM_ETHS = 2,
|
||||
FSL_IMX7_ETH_NUM_TX_RINGS = 3,
|
||||
FSL_IMX7_NUM_USDHCS = 3,
|
||||
FSL_IMX7_NUM_WDTS = 4,
|
||||
FSL_IMX7_NUM_GPTS = 4,
|
||||
FSL_IMX7_NUM_IOMUXCS = 2,
|
||||
FSL_IMX7_NUM_GPIOS = 7,
|
||||
FSL_IMX7_NUM_I2CS = 4,
|
||||
FSL_IMX7_NUM_ECSPIS = 4,
|
||||
FSL_IMX7_NUM_USBS = 3,
|
||||
FSL_IMX7_NUM_ADCS = 2,
|
||||
};
|
||||
|
||||
typedef struct FslIMX7State {
|
||||
/*< private >*/
|
||||
DeviceState parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
ARMCPU cpu[FSL_IMX7_NUM_CPUS];
|
||||
A15MPPrivState a7mpcore;
|
||||
IMXGPTState gpt[FSL_IMX7_NUM_GPTS];
|
||||
IMXGPIOState gpio[FSL_IMX7_NUM_GPIOS];
|
||||
IMX7CCMState ccm;
|
||||
IMX7AnalogState analog;
|
||||
IMX7SNVSState snvs;
|
||||
IMXGPCv2State gpcv2;
|
||||
IMXSPIState spi[FSL_IMX7_NUM_ECSPIS];
|
||||
IMXI2CState i2c[FSL_IMX7_NUM_I2CS];
|
||||
IMXSerialState uart[FSL_IMX7_NUM_UARTS];
|
||||
IMXFECState eth[FSL_IMX7_NUM_ETHS];
|
||||
SDHCIState usdhc[FSL_IMX7_NUM_USDHCS];
|
||||
IMX2WdtState wdt[FSL_IMX7_NUM_WDTS];
|
||||
IMX7GPRState gpr;
|
||||
ChipideaState usb[FSL_IMX7_NUM_USBS];
|
||||
DesignwarePCIEHost pcie;
|
||||
} FslIMX7State;
|
||||
|
||||
enum FslIMX7MemoryMap {
|
||||
FSL_IMX7_MMDC_ADDR = 0x80000000,
|
||||
FSL_IMX7_MMDC_SIZE = 2 * 1024 * 1024 * 1024UL,
|
||||
|
||||
FSL_IMX7_GPIO1_ADDR = 0x30200000,
|
||||
FSL_IMX7_GPIO2_ADDR = 0x30210000,
|
||||
FSL_IMX7_GPIO3_ADDR = 0x30220000,
|
||||
FSL_IMX7_GPIO4_ADDR = 0x30230000,
|
||||
FSL_IMX7_GPIO5_ADDR = 0x30240000,
|
||||
FSL_IMX7_GPIO6_ADDR = 0x30250000,
|
||||
FSL_IMX7_GPIO7_ADDR = 0x30260000,
|
||||
|
||||
FSL_IMX7_IOMUXC_LPSR_GPR_ADDR = 0x30270000,
|
||||
|
||||
FSL_IMX7_WDOG1_ADDR = 0x30280000,
|
||||
FSL_IMX7_WDOG2_ADDR = 0x30290000,
|
||||
FSL_IMX7_WDOG3_ADDR = 0x302A0000,
|
||||
FSL_IMX7_WDOG4_ADDR = 0x302B0000,
|
||||
|
||||
FSL_IMX7_IOMUXC_LPSR_ADDR = 0x302C0000,
|
||||
|
||||
FSL_IMX7_GPT1_ADDR = 0x302D0000,
|
||||
FSL_IMX7_GPT2_ADDR = 0x302E0000,
|
||||
FSL_IMX7_GPT3_ADDR = 0x302F0000,
|
||||
FSL_IMX7_GPT4_ADDR = 0x30300000,
|
||||
|
||||
FSL_IMX7_IOMUXC_ADDR = 0x30330000,
|
||||
FSL_IMX7_IOMUXC_GPR_ADDR = 0x30340000,
|
||||
FSL_IMX7_IOMUXCn_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_ANALOG_ADDR = 0x30360000,
|
||||
FSL_IMX7_SNVS_ADDR = 0x30370000,
|
||||
FSL_IMX7_CCM_ADDR = 0x30380000,
|
||||
|
||||
FSL_IMX7_SRC_ADDR = 0x30390000,
|
||||
FSL_IMX7_SRC_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_ADC1_ADDR = 0x30610000,
|
||||
FSL_IMX7_ADC2_ADDR = 0x30620000,
|
||||
FSL_IMX7_ADCn_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_GPC_ADDR = 0x303A0000,
|
||||
|
||||
FSL_IMX7_I2C1_ADDR = 0x30A20000,
|
||||
FSL_IMX7_I2C2_ADDR = 0x30A30000,
|
||||
FSL_IMX7_I2C3_ADDR = 0x30A40000,
|
||||
FSL_IMX7_I2C4_ADDR = 0x30A50000,
|
||||
|
||||
FSL_IMX7_ECSPI1_ADDR = 0x30820000,
|
||||
FSL_IMX7_ECSPI2_ADDR = 0x30830000,
|
||||
FSL_IMX7_ECSPI3_ADDR = 0x30840000,
|
||||
FSL_IMX7_ECSPI4_ADDR = 0x30630000,
|
||||
|
||||
FSL_IMX7_LCDIF_ADDR = 0x30730000,
|
||||
FSL_IMX7_LCDIF_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_UART1_ADDR = 0x30860000,
|
||||
/*
|
||||
* Some versions of the reference manual claim that UART2 is @
|
||||
* 0x30870000, but experiments with HW + DT files in upstream
|
||||
* Linux kernel show that not to be true and that block is
|
||||
* acutally located @ 0x30890000
|
||||
*/
|
||||
FSL_IMX7_UART2_ADDR = 0x30890000,
|
||||
FSL_IMX7_UART3_ADDR = 0x30880000,
|
||||
FSL_IMX7_UART4_ADDR = 0x30A60000,
|
||||
FSL_IMX7_UART5_ADDR = 0x30A70000,
|
||||
FSL_IMX7_UART6_ADDR = 0x30A80000,
|
||||
FSL_IMX7_UART7_ADDR = 0x30A90000,
|
||||
|
||||
FSL_IMX7_ENET1_ADDR = 0x30BE0000,
|
||||
FSL_IMX7_ENET2_ADDR = 0x30BF0000,
|
||||
|
||||
FSL_IMX7_USB1_ADDR = 0x30B10000,
|
||||
FSL_IMX7_USBMISC1_ADDR = 0x30B10200,
|
||||
FSL_IMX7_USB2_ADDR = 0x30B20000,
|
||||
FSL_IMX7_USBMISC2_ADDR = 0x30B20200,
|
||||
FSL_IMX7_USB3_ADDR = 0x30B30000,
|
||||
FSL_IMX7_USBMISC3_ADDR = 0x30B30200,
|
||||
FSL_IMX7_USBMISCn_SIZE = 0x200,
|
||||
|
||||
FSL_IMX7_USDHC1_ADDR = 0x30B40000,
|
||||
FSL_IMX7_USDHC2_ADDR = 0x30B50000,
|
||||
FSL_IMX7_USDHC3_ADDR = 0x30B60000,
|
||||
|
||||
FSL_IMX7_SDMA_ADDR = 0x30BD0000,
|
||||
FSL_IMX7_SDMA_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_A7MPCORE_ADDR = 0x31000000,
|
||||
FSL_IMX7_A7MPCORE_DAP_ADDR = 0x30000000,
|
||||
|
||||
FSL_IMX7_PCIE_REG_ADDR = 0x33800000,
|
||||
FSL_IMX7_PCIE_REG_SIZE = 16 * 1024,
|
||||
|
||||
FSL_IMX7_GPR_ADDR = 0x30340000,
|
||||
};
|
||||
|
||||
enum FslIMX7IRQs {
|
||||
FSL_IMX7_USDHC1_IRQ = 22,
|
||||
FSL_IMX7_USDHC2_IRQ = 23,
|
||||
FSL_IMX7_USDHC3_IRQ = 24,
|
||||
|
||||
FSL_IMX7_UART1_IRQ = 26,
|
||||
FSL_IMX7_UART2_IRQ = 27,
|
||||
FSL_IMX7_UART3_IRQ = 28,
|
||||
FSL_IMX7_UART4_IRQ = 29,
|
||||
FSL_IMX7_UART5_IRQ = 30,
|
||||
FSL_IMX7_UART6_IRQ = 16,
|
||||
|
||||
FSL_IMX7_ECSPI1_IRQ = 31,
|
||||
FSL_IMX7_ECSPI2_IRQ = 32,
|
||||
FSL_IMX7_ECSPI3_IRQ = 33,
|
||||
FSL_IMX7_ECSPI4_IRQ = 34,
|
||||
|
||||
FSL_IMX7_I2C1_IRQ = 35,
|
||||
FSL_IMX7_I2C2_IRQ = 36,
|
||||
FSL_IMX7_I2C3_IRQ = 37,
|
||||
FSL_IMX7_I2C4_IRQ = 38,
|
||||
|
||||
FSL_IMX7_USB1_IRQ = 43,
|
||||
FSL_IMX7_USB2_IRQ = 42,
|
||||
FSL_IMX7_USB3_IRQ = 40,
|
||||
|
||||
FSL_IMX7_PCI_INTA_IRQ = 122,
|
||||
FSL_IMX7_PCI_INTB_IRQ = 123,
|
||||
FSL_IMX7_PCI_INTC_IRQ = 124,
|
||||
FSL_IMX7_PCI_INTD_IRQ = 125,
|
||||
|
||||
FSL_IMX7_UART7_IRQ = 126,
|
||||
|
||||
#define FSL_IMX7_ENET_IRQ(i, n) ((n) + ((i) ? 100 : 118))
|
||||
|
||||
FSL_IMX7_MAX_IRQ = 128,
|
||||
};
|
||||
|
||||
#endif /* FSL_IMX7_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user