Compare commits
172 Commits
qemu-openb
...
qemu-sparc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09340f497e | ||
|
|
29df47a5cc | ||
|
|
1058e1a377 | ||
|
|
076489c043 | ||
|
|
6bdc3707d9 | ||
|
|
fcd23a6787 | ||
|
|
506179e421 | ||
|
|
efa85a4d1a | ||
|
|
bf1b9edeb0 | ||
|
|
8ef53cdb50 | ||
|
|
f4af847316 | ||
|
|
c4e42a9c2b | ||
|
|
d247c8e7f4 | ||
|
|
ab200dafc0 | ||
|
|
0e8818f023 | ||
|
|
1d09f7008b | ||
|
|
30685c000c | ||
|
|
13025fee7f | ||
|
|
cf6af766f4 | ||
|
|
aa9c6fa757 | ||
|
|
c6644548c7 | ||
|
|
944458b659 | ||
|
|
0852827115 | ||
|
|
ef2fdbfb4d | ||
|
|
4623027d86 | ||
|
|
add993477b | ||
|
|
c1112b2d3d | ||
|
|
21c520d0c1 | ||
|
|
59377b4a4b | ||
|
|
436e353076 | ||
|
|
e9bbc8bd8a | ||
|
|
c624b015bf | ||
|
|
96a07d5bf4 | ||
|
|
170d3bd341 | ||
|
|
d24f80234b | ||
|
|
3ae0343db6 | ||
|
|
b034993717 | ||
|
|
80f2c625cb | ||
|
|
ddb6f22548 | ||
|
|
a50547aca5 | ||
|
|
a979104239 | ||
|
|
46fb7809b5 | ||
|
|
8a508e7064 | ||
|
|
102f0f79a5 | ||
|
|
6465905355 | ||
|
|
c2c1bf44a9 | ||
|
|
d6874c8391 | ||
|
|
9ac45b886a | ||
|
|
1eb27d692e | ||
|
|
91d0231213 | ||
|
|
6682bc1ee4 | ||
|
|
aff498cf30 | ||
|
|
06e8b8e3e1 | ||
|
|
3e4bcf89b7 | ||
|
|
c3d25271b2 | ||
|
|
7f7bdcaff5 | ||
|
|
1d311e738b | ||
|
|
aeaf6c20db | ||
|
|
6536987fd6 | ||
|
|
c1474acd5d | ||
|
|
e8aa2d95ea | ||
|
|
60b725b6d4 | ||
|
|
1c3d4a8f4b | ||
|
|
aaa450300e | ||
|
|
0df68c7ed6 | ||
|
|
8256870ada | ||
|
|
fe9a9d527d | ||
|
|
d0e9bc0407 | ||
|
|
981b1c6266 | ||
|
|
a2166410ad | ||
|
|
c9f4e4d8b6 | ||
|
|
5ba5335d93 | ||
|
|
2aba168e50 | ||
|
|
6ae4a57ab0 | ||
|
|
9922962011 | ||
|
|
23d0766bd9 | ||
|
|
8d830485fc | ||
|
|
033e1fcd97 | ||
|
|
75cf84cbee | ||
|
|
e0d6a362be | ||
|
|
99125c7499 | ||
|
|
00084a25ad | ||
|
|
03b32c092e | ||
|
|
001d235c7e | ||
|
|
9723295a72 | ||
|
|
4812f26152 | ||
|
|
330a21e3c4 | ||
|
|
ab3d15fa84 | ||
|
|
64fb96214c | ||
|
|
eab9f191a0 | ||
|
|
2fb4c6528e | ||
|
|
363ce377da | ||
|
|
6d893a4d70 | ||
|
|
da6e10177a | ||
|
|
7a660e776e | ||
|
|
8d08fa93bb | ||
|
|
25c79a3089 | ||
|
|
7e10b57dd9 | ||
|
|
2a17583082 | ||
|
|
cf3b0334f2 | ||
|
|
d9b9e6f6b9 | ||
|
|
7701aeed0f | ||
|
|
7abc0c6d35 | ||
|
|
d9293c4843 | ||
|
|
d9715d6772 | ||
|
|
740a19313b | ||
|
|
c29a0b0fb3 | ||
|
|
709044fd2d | ||
|
|
e1a9b7d1fc | ||
|
|
b87a0100cd | ||
|
|
fad189d1f6 | ||
|
|
0094908375 | ||
|
|
787a7e76c2 | ||
|
|
b59f479bee | ||
|
|
21fbea8c8a | ||
|
|
4a15527c9f | ||
|
|
0c6ad94809 | ||
|
|
e9d652824b | ||
|
|
20e62dd8c8 | ||
|
|
e21b551cb6 | ||
|
|
ebae861fc6 | ||
|
|
864806156a | ||
|
|
6cdca173ef | ||
|
|
9798ac7162 | ||
|
|
9a223097e4 | ||
|
|
2c8ec397f8 | ||
|
|
ed3baad15b | ||
|
|
b601e0cd78 | ||
|
|
7efefd9bbb | ||
|
|
07774d5842 | ||
|
|
87f4f18348 | ||
|
|
e9fdf45324 | ||
|
|
64580903c2 | ||
|
|
3059c2f5a8 | ||
|
|
ebd205c080 | ||
|
|
118c82e7ff | ||
|
|
aae7a18d47 | ||
|
|
6da4433fc5 | ||
|
|
ad1a978218 | ||
|
|
026498a8f1 | ||
|
|
055762479b | ||
|
|
696942b8bc | ||
|
|
58044b5cf5 | ||
|
|
8137355e85 | ||
|
|
6734099048 | ||
|
|
ece09beec4 | ||
|
|
75fb4577fc | ||
|
|
979672cf51 | ||
|
|
d783d1fe58 | ||
|
|
b456b1132e | ||
|
|
01b96ec8c4 | ||
|
|
97b7e29bce | ||
|
|
4eb42b81c5 | ||
|
|
6ee51e961e | ||
|
|
f0d877dc5e | ||
|
|
4414942e7e | ||
|
|
dd97ef044a | ||
|
|
b48b064009 | ||
|
|
8b3410deb2 | ||
|
|
6c11dda922 | ||
|
|
263807f4e8 | ||
|
|
0cca7e7bfd | ||
|
|
350ef09674 | ||
|
|
866ad5f5ff | ||
|
|
c82c7336de | ||
|
|
d718b7475b | ||
|
|
60853009be | ||
|
|
8fadea24de | ||
|
|
b5e89f044d | ||
|
|
ef506f804a | ||
|
|
220ae9002f | ||
|
|
62a31161ee |
11
MAINTAINERS
11
MAINTAINERS
@@ -383,6 +383,8 @@ F: target/s390x/kvm-stub.c
|
||||
F: target/s390x/ioinst.[ch]
|
||||
F: target/s390x/machine.c
|
||||
F: target/s390x/sigp.c
|
||||
F: target/s390x/cpu_features*.[ch]
|
||||
F: target/s390x/cpu_models.[ch]
|
||||
F: hw/intc/s390_flic.c
|
||||
F: hw/intc/s390_flic_kvm.c
|
||||
F: include/hw/s390x/s390_flic.h
|
||||
@@ -728,6 +730,14 @@ F: include/hw/arm/fsl-imx6.h
|
||||
F: include/hw/misc/imx6_*.h
|
||||
F: include/hw/ssi/imx_spi.h
|
||||
|
||||
SBSA-REF
|
||||
M: Radoslaw Biernacki <radoslaw.biernacki@linaro.org>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
R: Leif Lindholm <leif.lindholm@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/sbsa-ref.c
|
||||
|
||||
Sharp SL-5500 (Collie) PDA
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
@@ -1934,6 +1944,7 @@ M: Jason Wang <jasowang@redhat.com>
|
||||
S: Maintained
|
||||
F: net/
|
||||
F: include/net/
|
||||
F: qemu-bridge-helper.c
|
||||
T: git https://github.com/jasowang/qemu.git net
|
||||
F: qapi/net.json
|
||||
|
||||
|
||||
16
Makefile
16
Makefile
@@ -879,19 +879,19 @@ ifneq ($(DESCS),)
|
||||
done
|
||||
endif
|
||||
for s in $(ICON_SIZES); do \
|
||||
mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \
|
||||
mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/$${s}/apps"; \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \
|
||||
"$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \
|
||||
"$(DESTDIR)$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \
|
||||
done; \
|
||||
mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps"; \
|
||||
mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/32x32/apps"; \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \
|
||||
"$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \
|
||||
mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps"; \
|
||||
"$(DESTDIR)$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \
|
||||
mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/scalable/apps"; \
|
||||
$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \
|
||||
"$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps/qemu.svg"
|
||||
mkdir -p "$(DESTDIR)/$(qemu_desktopdir)"
|
||||
"$(DESTDIR)$(qemu_icondir)/hicolor/scalable/apps/qemu.svg"
|
||||
mkdir -p "$(DESTDIR)$(qemu_desktopdir)"
|
||||
$(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \
|
||||
"$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop"
|
||||
"$(DESTDIR)$(qemu_desktopdir)/qemu.desktop"
|
||||
ifdef CONFIG_GTK
|
||||
$(MAKE) -C po $@
|
||||
endif
|
||||
|
||||
@@ -174,7 +174,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
break;
|
||||
}
|
||||
/* Copy if allocated above the base */
|
||||
ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base),
|
||||
ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base), false,
|
||||
offset, COMMIT_BUFFER_SIZE, &n);
|
||||
copy = (ret == 1);
|
||||
trace_commit_one_iteration(s, offset, n, ret);
|
||||
|
||||
21
block/io.c
21
block/io.c
@@ -2295,10 +2295,11 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
|
||||
/*
|
||||
* Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP]
|
||||
*
|
||||
* Return true if (a prefix of) the given range is allocated in any image
|
||||
* between BASE and TOP (inclusive). BASE can be NULL to check if the given
|
||||
* offset is allocated in any image of the chain. Return false otherwise,
|
||||
* or negative errno on failure.
|
||||
* Return 1 if (a prefix of) the given range is allocated in any image
|
||||
* between BASE and TOP (BASE is only included if include_base is set).
|
||||
* BASE can be NULL to check if the given offset is allocated in any
|
||||
* image of the chain. Return 0 otherwise, or negative errno on
|
||||
* failure.
|
||||
*
|
||||
* 'pnum' is set to the number of bytes (including and immediately
|
||||
* following the specified offset) that are known to be in the same
|
||||
@@ -2310,17 +2311,21 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
|
||||
*/
|
||||
int bdrv_is_allocated_above(BlockDriverState *top,
|
||||
BlockDriverState *base,
|
||||
int64_t offset, int64_t bytes, int64_t *pnum)
|
||||
bool include_base, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum)
|
||||
{
|
||||
BlockDriverState *intermediate;
|
||||
int ret;
|
||||
int64_t n = bytes;
|
||||
|
||||
assert(base || !include_base);
|
||||
|
||||
intermediate = top;
|
||||
while (intermediate && intermediate != base) {
|
||||
while (include_base || intermediate != base) {
|
||||
int64_t pnum_inter;
|
||||
int64_t size_inter;
|
||||
|
||||
assert(intermediate);
|
||||
ret = bdrv_is_allocated(intermediate, offset, bytes, &pnum_inter);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -2339,6 +2344,10 @@ int bdrv_is_allocated_above(BlockDriverState *top,
|
||||
n = pnum_inter;
|
||||
}
|
||||
|
||||
if (intermediate == base) {
|
||||
break;
|
||||
}
|
||||
|
||||
intermediate = backing_bs(intermediate);
|
||||
}
|
||||
|
||||
|
||||
@@ -808,7 +808,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = bdrv_is_allocated_above(bs, base, offset, bytes, &count);
|
||||
ret = bdrv_is_allocated_above(bs, base, false, offset, bytes, &count);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2148,7 +2148,8 @@ static bool is_unallocated(BlockDriverState *bs, int64_t offset, int64_t bytes)
|
||||
{
|
||||
int64_t nr;
|
||||
return !bytes ||
|
||||
(!bdrv_is_allocated_above(bs, NULL, offset, bytes, &nr) && nr == bytes);
|
||||
(!bdrv_is_allocated_above(bs, NULL, false, offset, bytes, &nr) &&
|
||||
nr == bytes);
|
||||
}
|
||||
|
||||
static bool is_zero_cow(BlockDriverState *bs, QCowL2Meta *m)
|
||||
|
||||
42
block/rbd.c
42
block/rbd.c
@@ -103,6 +103,7 @@ typedef struct BDRVRBDState {
|
||||
rbd_image_t image;
|
||||
char *image_name;
|
||||
char *snap;
|
||||
uint64_t image_size;
|
||||
} BDRVRBDState;
|
||||
|
||||
static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
||||
@@ -778,6 +779,14 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto failed_open;
|
||||
}
|
||||
|
||||
r = rbd_get_size(s->image, &s->image_size);
|
||||
if (r < 0) {
|
||||
error_setg_errno(errp, -r, "error getting image size from %s",
|
||||
s->image_name);
|
||||
rbd_close(s->image);
|
||||
goto failed_open;
|
||||
}
|
||||
|
||||
/* If we are using an rbd snapshot, we must be r/o, otherwise
|
||||
* leave as-is */
|
||||
if (s->snap != NULL) {
|
||||
@@ -834,6 +843,22 @@ static void qemu_rbd_close(BlockDriverState *bs)
|
||||
rados_shutdown(s->cluster);
|
||||
}
|
||||
|
||||
/* Resize the RBD image and update the 'image_size' with the current size */
|
||||
static int qemu_rbd_resize(BlockDriverState *bs, uint64_t size)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
int r;
|
||||
|
||||
r = rbd_resize(s->image, size);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
s->image_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const AIOCBInfo rbd_aiocb_info = {
|
||||
.aiocb_size = sizeof(RBDAIOCB),
|
||||
};
|
||||
@@ -935,13 +960,25 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case RBD_AIO_WRITE:
|
||||
case RBD_AIO_WRITE: {
|
||||
/*
|
||||
* RBD APIs don't allow us to write more than actual size, so in order
|
||||
* to support growing images, we resize the image before write
|
||||
* operations that exceed the current size.
|
||||
*/
|
||||
if (off + size > s->image_size) {
|
||||
r = qemu_rbd_resize(bs, off + size);
|
||||
if (r < 0) {
|
||||
goto failed_completion;
|
||||
}
|
||||
}
|
||||
#ifdef LIBRBD_SUPPORTS_IOVEC
|
||||
r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c);
|
||||
#else
|
||||
r = rbd_aio_write(s->image, off, size, rcb->buf, c);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case RBD_AIO_READ:
|
||||
#ifdef LIBRBD_SUPPORTS_IOVEC
|
||||
r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c);
|
||||
@@ -1052,7 +1089,6 @@ static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
|
||||
PreallocMode prealloc,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
int r;
|
||||
|
||||
if (prealloc != PREALLOC_MODE_OFF) {
|
||||
@@ -1061,7 +1097,7 @@ static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
r = rbd_resize(s->image, offset);
|
||||
r = qemu_rbd_resize(bs, offset);
|
||||
if (r < 0) {
|
||||
error_setg_errno(errp, -r, "Failed to resize file");
|
||||
return r;
|
||||
|
||||
@@ -275,7 +275,7 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
||||
while (remaining_sectors > 0) {
|
||||
int64_t count;
|
||||
|
||||
ret = bdrv_is_allocated_above(top->bs, base->bs,
|
||||
ret = bdrv_is_allocated_above(top->bs, base->bs, false,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
remaining_sectors * BDRV_SECTOR_SIZE,
|
||||
&count);
|
||||
|
||||
@@ -31,7 +31,7 @@ enum {
|
||||
|
||||
typedef struct StreamBlockJob {
|
||||
BlockJob common;
|
||||
BlockDriverState *base;
|
||||
BlockDriverState *bottom;
|
||||
BlockdevOnError on_error;
|
||||
char *backing_file_str;
|
||||
bool bs_read_only;
|
||||
@@ -54,7 +54,7 @@ static void stream_abort(Job *job)
|
||||
|
||||
if (s->chain_frozen) {
|
||||
BlockJob *bjob = &s->common;
|
||||
bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->base);
|
||||
bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,11 +63,11 @@ static int stream_prepare(Job *job)
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
||||
BlockJob *bjob = &s->common;
|
||||
BlockDriverState *bs = blk_bs(bjob->blk);
|
||||
BlockDriverState *base = s->base;
|
||||
BlockDriverState *base = backing_bs(s->bottom);
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
|
||||
bdrv_unfreeze_backing_chain(bs, base);
|
||||
bdrv_unfreeze_backing_chain(bs, s->bottom);
|
||||
s->chain_frozen = false;
|
||||
|
||||
if (bs->backing) {
|
||||
@@ -110,7 +110,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
||||
BlockBackend *blk = s->common.blk;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
BlockDriverState *base = s->base;
|
||||
bool enable_cor = !backing_bs(s->bottom);
|
||||
int64_t len;
|
||||
int64_t offset = 0;
|
||||
uint64_t delay_ns = 0;
|
||||
@@ -119,14 +119,14 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
||||
int64_t n = 0; /* bytes */
|
||||
void *buf;
|
||||
|
||||
if (!bs->backing) {
|
||||
goto out;
|
||||
if (bs == s->bottom) {
|
||||
/* Nothing to stream */
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = bdrv_getlength(bs);
|
||||
if (len < 0) {
|
||||
ret = len;
|
||||
goto out;
|
||||
return len;
|
||||
}
|
||||
job_progress_set_remaining(&s->common.job, len);
|
||||
|
||||
@@ -137,7 +137,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
||||
* backing chain since the copy-on-read operation does not take base into
|
||||
* account.
|
||||
*/
|
||||
if (!base) {
|
||||
if (enable_cor) {
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
}
|
||||
|
||||
@@ -160,9 +160,8 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
||||
} else if (ret >= 0) {
|
||||
/* Copy if allocated in the intermediate images. Limit to the
|
||||
* known-unallocated area [offset, offset+n*BDRV_SECTOR_SIZE). */
|
||||
ret = bdrv_is_allocated_above(backing_bs(bs), base,
|
||||
ret = bdrv_is_allocated_above(backing_bs(bs), s->bottom, true,
|
||||
offset, n, &n);
|
||||
|
||||
/* Finish early if end of backing file has been reached */
|
||||
if (ret == 0 && n == 0) {
|
||||
n = len - offset;
|
||||
@@ -199,18 +198,14 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!base) {
|
||||
if (enable_cor) {
|
||||
bdrv_disable_copy_on_read(bs);
|
||||
}
|
||||
|
||||
/* Do not remove the backing file if an error was there but ignored. */
|
||||
ret = error;
|
||||
|
||||
qemu_vfree(buf);
|
||||
|
||||
out:
|
||||
/* Modify backing chain and close BDSes in main loop */
|
||||
return ret;
|
||||
/* Do not remove the backing file if an error was there but ignored. */
|
||||
return error;
|
||||
}
|
||||
|
||||
static const BlockJobDriver stream_job_driver = {
|
||||
@@ -235,8 +230,10 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
StreamBlockJob *s;
|
||||
BlockDriverState *iter;
|
||||
bool bs_read_only;
|
||||
int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
|
||||
BlockDriverState *bottom = bdrv_find_overlay(bs, base);
|
||||
|
||||
if (bdrv_freeze_backing_chain(bs, base, errp) < 0) {
|
||||
if (bdrv_freeze_backing_chain(bs, bottom, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -253,10 +250,8 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
* already have our own plans. Also don't allow resize as the image size is
|
||||
* queried only at the job start and then cached. */
|
||||
s = block_job_create(job_id, &stream_job_driver, NULL, bs,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_GRAPH_MOD,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_WRITE,
|
||||
basic_flags | BLK_PERM_GRAPH_MOD,
|
||||
basic_flags | BLK_PERM_WRITE,
|
||||
speed, creation_flags, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
goto fail;
|
||||
@@ -264,15 +259,18 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
|
||||
/* Block all intermediate nodes between bs and base, because they will
|
||||
* disappear from the chain after this operation. The streaming job reads
|
||||
* every block only once, assuming that it doesn't change, so block writes
|
||||
* and resizes. */
|
||||
* every block only once, assuming that it doesn't change, so forbid writes
|
||||
* and resizes. Reassign the base node pointer because the backing BS of the
|
||||
* bottom node might change after the call to bdrv_reopen_set_read_only()
|
||||
* due to parallel block jobs running.
|
||||
*/
|
||||
base = backing_bs(bottom);
|
||||
for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) {
|
||||
block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED,
|
||||
&error_abort);
|
||||
basic_flags, &error_abort);
|
||||
}
|
||||
|
||||
s->base = base;
|
||||
s->bottom = bottom;
|
||||
s->backing_file_str = g_strdup(backing_file_str);
|
||||
s->bs_read_only = bs_read_only;
|
||||
s->chain_frozen = true;
|
||||
|
||||
@@ -5,3 +5,4 @@ include arm-softmmu.mak
|
||||
|
||||
CONFIG_XLNX_ZYNQMP_ARM=y
|
||||
CONFIG_XLNX_VERSAL=y
|
||||
CONFIG_SBSA_REF=y
|
||||
|
||||
@@ -34,19 +34,118 @@ CAS Negotiation
|
||||
---------------
|
||||
|
||||
QEMU advertises the supported interrupt modes in the device tree
|
||||
property "ibm,arch-vec-5-platform-support" in byte 23 and the OS
|
||||
Selection for XIVE is indicated in the "ibm,architecture-vec-5"
|
||||
property ``ibm,arch-vec-5-platform-support`` in byte 23 and the OS
|
||||
Selection for XIVE is indicated in the ``ibm,architecture-vec-5``
|
||||
property byte 23.
|
||||
|
||||
The interrupt modes supported by the machine depend on the CPU type
|
||||
(POWER9 is required for XIVE) but also on the machine property
|
||||
``ic-mode`` which can be set on the command line. It can take the
|
||||
following values: ``xics``, ``xive``, ``dual`` and currently ``xics``
|
||||
is the default but it may change in the future.
|
||||
following values: ``xics``, ``xive``, and ``dual`` which is the
|
||||
default mode. ``dual`` means that both modes XICS **and** XIVE are
|
||||
supported and if the guest OS supports XIVE, this mode will be
|
||||
selected.
|
||||
|
||||
The choosen interrupt mode is activated after a reconfiguration done
|
||||
in a machine reset.
|
||||
|
||||
KVM negotiation
|
||||
---------------
|
||||
|
||||
When the guest starts under KVM, the capabilities of the host kernel
|
||||
and QEMU are also negotiated. Depending on the version of the host
|
||||
kernel, KVM will advertise the XIVE capability to QEMU or not.
|
||||
|
||||
Nevertheless, the available interrupt modes in the machine should not
|
||||
depend on the XIVE KVM capability of the host. On older kernels
|
||||
without XIVE KVM support, QEMU will use the emulated XIVE device as a
|
||||
fallback and on newer kernels (>=5.2), the KVM XIVE device.
|
||||
|
||||
As a final refinement, the user can also switch the use of the KVM
|
||||
device with the machine option ``kernel_irqchip``.
|
||||
|
||||
|
||||
XIVE support in KVM
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For guest OSes supporting XIVE, the resulting interrupt modes on host
|
||||
kernels with XIVE KVM support are the following:
|
||||
|
||||
============== ============= ============= ================
|
||||
ic-mode kernel_irqchip
|
||||
-------------- ----------------------------------------------
|
||||
/ allowed off on
|
||||
(default)
|
||||
============== ============= ============= ================
|
||||
dual (default) XIVE KVM XIVE emul. XIVE KVM
|
||||
xive XIVE KVM XIVE emul. XIVE KVM
|
||||
xics XICS KVM XICS emul. XICS KVM
|
||||
============== ============= ============= ================
|
||||
|
||||
For legacy guest OSes without XIVE support, the resulting interrupt
|
||||
modes are the following:
|
||||
|
||||
============== ============= ============= ================
|
||||
ic-mode kernel_irqchip
|
||||
-------------- ----------------------------------------------
|
||||
/ allowed off on
|
||||
(default)
|
||||
============== ============= ============= ================
|
||||
dual (default) XICS KVM XICS emul. XICS KVM
|
||||
xive QEMU error(3) QEMU error(3) QEMU error(3)
|
||||
xics XICS KVM XICS emul. XICS KVM
|
||||
============== ============= ============= ================
|
||||
|
||||
(3) QEMU fails at CAS with ``Guest requested unavailable interrupt
|
||||
mode (XICS), either don't set the ic-mode machine property or try
|
||||
ic-mode=xics or ic-mode=dual``
|
||||
|
||||
|
||||
No XIVE support in KVM
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For guest OSes supporting XIVE, the resulting interrupt modes on host
|
||||
kernels without XIVE KVM support are the following:
|
||||
|
||||
============== ============= ============= ================
|
||||
ic-mode kernel_irqchip
|
||||
-------------- ----------------------------------------------
|
||||
/ allowed off on
|
||||
(default)
|
||||
============== ============= ============= ================
|
||||
dual (default) XIVE emul.(1) XIVE emul. QEMU error (2)
|
||||
xive XIVE emul.(1) XIVE emul. QEMU error (2)
|
||||
xics XICS KVM XICS emul. XICS KVM
|
||||
============== ============= ============= ================
|
||||
|
||||
|
||||
(1) QEMU warns with ``warning: kernel_irqchip requested but unavailable:
|
||||
IRQ_XIVE capability must be present for KVM``
|
||||
(2) QEMU fails with ``kernel_irqchip requested but unavailable:
|
||||
IRQ_XIVE capability must be present for KVM``
|
||||
|
||||
|
||||
For legacy guest OSes without XIVE support, the resulting interrupt
|
||||
modes are the following:
|
||||
|
||||
============== ============= ============= ================
|
||||
ic-mode kernel_irqchip
|
||||
-------------- ----------------------------------------------
|
||||
/ allowed off on
|
||||
(default)
|
||||
============== ============= ============= ================
|
||||
dual (default) QEMU error(4) XICS emul. QEMU error(4)
|
||||
xive QEMU error(3) QEMU error(3) QEMU error(3)
|
||||
xics XICS KVM XICS emul. XICS KVM
|
||||
============== ============= ============= ================
|
||||
|
||||
(3) QEMU fails at CAS with ``Guest requested unavailable interrupt
|
||||
mode (XICS), either don't set the ic-mode machine property or try
|
||||
ic-mode=xics or ic-mode=dual``
|
||||
(4) QEMU/KVM incompatibility due to device destruction in reset. QEMU fails
|
||||
with ``KVM is too old to support ic-mode=dual,kernel-irqchip=on``
|
||||
|
||||
|
||||
XIVE Device tree properties
|
||||
---------------------------
|
||||
|
||||
@@ -92,10 +191,11 @@ for both interrupt mode. The different ranges are defined as follow :
|
||||
- ``0x0000 .. 0x0FFF`` 4K CPU IPIs (only used under XIVE)
|
||||
- ``0x1000 .. 0x1000`` 1 EPOW
|
||||
- ``0x1001 .. 0x1001`` 1 HOTPLUG
|
||||
- ``0x1002 .. 0x10FF`` unused
|
||||
- ``0x1100 .. 0x11FF`` 256 VIO devices
|
||||
- ``0x1200 .. 0x127F`` 32 PHBs devices
|
||||
- ``0x1200 .. 0x127F`` 32x4 LSIs for PHB devices
|
||||
- ``0x1280 .. 0x12FF`` unused
|
||||
- ``0x1300 .. 0x1FFF`` PHB MSIs
|
||||
- ``0x1300 .. 0x1FFF`` PHB MSIs (dynamically allocated)
|
||||
|
||||
Monitoring XIVE
|
||||
---------------
|
||||
|
||||
@@ -20,10 +20,11 @@ The XIVE IC is composed of three sub-engines, each taking care of a
|
||||
processing layer of external interrupts:
|
||||
|
||||
- Interrupt Virtualization Source Engine (IVSE), or Source Controller
|
||||
(SC). These are found in PCI PHBs, in the PSI host bridge
|
||||
controller, but also inside the main controller for the core IPIs
|
||||
and other sub-chips (NX, CAP, NPU) of the chip/processor. They are
|
||||
configured to feed the IVRE with events.
|
||||
(SC). These are found in PCI PHBs, in the Processor Service
|
||||
Interface (PSI) host bridge Controller, but also inside the main
|
||||
controller for the core IPIs and other sub-chips (NX, CAP, NPU) of
|
||||
the chip/processor. They are configured to feed the IVRE with
|
||||
events.
|
||||
- Interrupt Virtualization Routing Engine (IVRE) or Virtualization
|
||||
Controller (VC). It handles event coalescing and perform interrupt
|
||||
routing by matching an event source number with an Event
|
||||
|
||||
@@ -955,8 +955,8 @@ ETEXI
|
||||
|
||||
{
|
||||
.name = "announce_self",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.args_type = "interfaces:s?,id:s?",
|
||||
.params = "[interfaces] [id]",
|
||||
.help = "Trigger GARP/RARP announcements",
|
||||
.cmd = hmp_announce_self,
|
||||
},
|
||||
@@ -967,6 +967,9 @@ STEXI
|
||||
Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating the
|
||||
network infrastructure after a reconfiguration or some forms of migration.
|
||||
The timings of the round are set by the migration announce parameters.
|
||||
An optional comma separated @var{interfaces} list restricts the announce to the
|
||||
named set of interfaces. An optional @var{id} can be used to start a separate announce
|
||||
timer and to change the parameters of it later.
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
||||
@@ -184,6 +184,20 @@ config REALVIEW
|
||||
select DS1338 # I2C RTC+NVRAM
|
||||
select USB_OHCI
|
||||
|
||||
config SBSA_REF
|
||||
bool
|
||||
imply PCI_DEVICES
|
||||
select AHCI
|
||||
select ARM_SMMUV3
|
||||
select GPIO_KEY
|
||||
select PCI_EXPRESS
|
||||
select PCI_EXPRESS_GENERIC_BRIDGE
|
||||
select PFLASH_CFI01
|
||||
select PL011 # UART
|
||||
select PL031 # RTC
|
||||
select PL061 # GPIO
|
||||
select USB_EHCI_SYSBUS
|
||||
|
||||
config SABRELITE
|
||||
bool
|
||||
select FSL_IMX6
|
||||
|
||||
@@ -19,6 +19,7 @@ obj-$(CONFIG_SPITZ) += spitz.o
|
||||
obj-$(CONFIG_TOSA) += tosa.o
|
||||
obj-$(CONFIG_Z2) += z2.o
|
||||
obj-$(CONFIG_REALVIEW) += realview.o
|
||||
obj-$(CONFIG_SBSA_REF) += sbsa-ref.o
|
||||
obj-$(CONFIG_STELLARIS) += stellaris.o
|
||||
obj-$(CONFIG_COLLIE) += collie.o
|
||||
obj-$(CONFIG_VERSATILE) += versatilepb.o
|
||||
|
||||
@@ -22,17 +22,18 @@
|
||||
#include "hw/misc/tmp105.h"
|
||||
#include "qemu/log.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/loader.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/units.h"
|
||||
|
||||
static struct arm_boot_info aspeed_board_binfo = {
|
||||
.board_id = -1, /* device-tree-only board */
|
||||
.nb_cpus = 1,
|
||||
};
|
||||
|
||||
struct AspeedBoardState {
|
||||
AspeedSoCState soc;
|
||||
MemoryRegion ram_container;
|
||||
MemoryRegion ram;
|
||||
MemoryRegion max_ram;
|
||||
};
|
||||
@@ -72,6 +73,17 @@ struct AspeedBoardState {
|
||||
SCU_AST2500_HW_STRAP_ACPI_ENABLE | \
|
||||
SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER))
|
||||
|
||||
/* Swift hardware value: 0xF11AD206 */
|
||||
#define SWIFT_BMC_HW_STRAP1 ( \
|
||||
AST2500_HW_STRAP1_DEFAULTS | \
|
||||
SCU_AST2500_HW_STRAP_SPI_AUTOFETCH_ENABLE | \
|
||||
SCU_AST2500_HW_STRAP_GPIO_STRAP_ENABLE | \
|
||||
SCU_AST2500_HW_STRAP_UART_DEBUG | \
|
||||
SCU_AST2500_HW_STRAP_DDR4_ENABLE | \
|
||||
SCU_H_PLL_BYPASS_EN | \
|
||||
SCU_AST2500_HW_STRAP_ACPI_ENABLE | \
|
||||
SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER))
|
||||
|
||||
/* Witherspoon hardware value: 0xF10AD216 (but use romulus definition) */
|
||||
#define WITHERSPOON_BMC_HW_STRAP1 ROMULUS_BMC_HW_STRAP1
|
||||
|
||||
@@ -159,6 +171,10 @@ static void aspeed_board_init(MachineState *machine,
|
||||
ram_addr_t max_ram_size;
|
||||
|
||||
bmc = g_new0(AspeedBoardState, 1);
|
||||
|
||||
memory_region_init(&bmc->ram_container, NULL, "aspeed-ram-container",
|
||||
UINT32_MAX);
|
||||
|
||||
object_initialize_child(OBJECT(machine), "soc", &bmc->soc,
|
||||
(sizeof(bmc->soc)), cfg->soc_name, &error_abort,
|
||||
NULL);
|
||||
@@ -171,6 +187,8 @@ static void aspeed_board_init(MachineState *machine,
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&bmc->soc), cfg->num_cs, "num-cs",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&bmc->soc), smp_cpus, "num-cpus",
|
||||
&error_abort);
|
||||
if (machine->kernel_filename) {
|
||||
/*
|
||||
* When booting with a -kernel command line there is no u-boot
|
||||
@@ -191,18 +209,16 @@ static void aspeed_board_init(MachineState *machine,
|
||||
&error_abort);
|
||||
|
||||
memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
|
||||
memory_region_add_subregion(get_system_memory(), sc->info->sdram_base,
|
||||
&bmc->ram);
|
||||
object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
|
||||
&error_abort);
|
||||
memory_region_add_subregion(&bmc->ram_container, 0, &bmc->ram);
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
sc->info->memmap[ASPEED_SDRAM],
|
||||
&bmc->ram_container);
|
||||
|
||||
max_ram_size = object_property_get_uint(OBJECT(&bmc->soc), "max-ram-size",
|
||||
&error_abort);
|
||||
memory_region_init_io(&bmc->max_ram, NULL, &max_ram_ops, NULL,
|
||||
"max_ram", max_ram_size - ram_size);
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
sc->info->sdram_base + ram_size,
|
||||
&bmc->max_ram);
|
||||
memory_region_add_subregion(&bmc->ram_container, ram_size, &bmc->max_ram);
|
||||
|
||||
aspeed_board_init_flashes(&bmc->soc.fmc, cfg->fmc_model, &error_abort);
|
||||
aspeed_board_init_flashes(&bmc->soc.spi[0], cfg->spi_model, &error_abort);
|
||||
@@ -229,7 +245,8 @@ static void aspeed_board_init(MachineState *machine,
|
||||
aspeed_board_binfo.initrd_filename = machine->initrd_filename;
|
||||
aspeed_board_binfo.kernel_cmdline = machine->kernel_cmdline;
|
||||
aspeed_board_binfo.ram_size = ram_size;
|
||||
aspeed_board_binfo.loader_start = sc->info->sdram_base;
|
||||
aspeed_board_binfo.loader_start = sc->info->memmap[ASPEED_SDRAM];
|
||||
aspeed_board_binfo.nb_cpus = bmc->soc.num_cpus;
|
||||
|
||||
if (cfg->i2c_init) {
|
||||
cfg->i2c_init(bmc);
|
||||
@@ -286,6 +303,35 @@ static void romulus_bmc_i2c_init(AspeedBoardState *bmc)
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32);
|
||||
}
|
||||
|
||||
static void swift_bmc_i2c_init(AspeedBoardState *bmc)
|
||||
{
|
||||
AspeedSoCState *soc = &bmc->soc;
|
||||
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), "pca9552", 0x60);
|
||||
|
||||
/* The swift board expects a TMP275 but a TMP105 is compatible */
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x48);
|
||||
/* The swift board expects a pca9551 but a pca9552 is compatible */
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "pca9552", 0x60);
|
||||
|
||||
/* The swift board expects an Epson RX8900 RTC but a ds1338 is compatible */
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 8), "ds1338", 0x32);
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 8), "pca9552", 0x60);
|
||||
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), "tmp423", 0x4c);
|
||||
/* The swift board expects a pca9539 but a pca9552 is compatible */
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), "pca9552", 0x74);
|
||||
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 10), "tmp423", 0x4c);
|
||||
/* The swift board expects a pca9539 but a pca9552 is compatible */
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 10), "pca9552",
|
||||
0x74);
|
||||
|
||||
/* The swift board expects a TMP275 but a TMP105 is compatible */
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 12), "tmp105", 0x48);
|
||||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 12), "tmp105", 0x4a);
|
||||
}
|
||||
|
||||
static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc)
|
||||
{
|
||||
AspeedSoCState *soc = &bmc->soc;
|
||||
@@ -326,7 +372,7 @@ static void aspeed_machine_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
mc->desc = board->desc;
|
||||
mc->init = aspeed_machine_init;
|
||||
mc->max_cpus = 1;
|
||||
mc->max_cpus = ASPEED_CPUS_NUM;
|
||||
mc->no_sdcard = 1;
|
||||
mc->no_floppy = 1;
|
||||
mc->no_cdrom = 1;
|
||||
@@ -376,6 +422,16 @@ static const AspeedBoardConfig aspeed_boards[] = {
|
||||
.num_cs = 2,
|
||||
.i2c_init = romulus_bmc_i2c_init,
|
||||
.ram = 512 * MiB,
|
||||
}, {
|
||||
.name = MACHINE_TYPE_NAME("swift-bmc"),
|
||||
.desc = "OpenPOWER Swift BMC (ARM1176)",
|
||||
.soc_name = "ast2500-a1",
|
||||
.hw_strap1 = SWIFT_BMC_HW_STRAP1,
|
||||
.fmc_model = "mx66l1g45g",
|
||||
.spi_model = "mx66l1g45g",
|
||||
.num_cs = 2,
|
||||
.i2c_init = swift_bmc_i2c_init,
|
||||
.ram = 512 * MiB,
|
||||
}, {
|
||||
.name = MACHINE_TYPE_NAME("witherspoon-bmc"),
|
||||
.desc = "OpenPOWER Witherspoon BMC (ARM1176)",
|
||||
|
||||
@@ -19,36 +19,99 @@
|
||||
#include "hw/char/serial.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/i2c/aspeed_i2c.h"
|
||||
#include "net/net.h"
|
||||
|
||||
#define ASPEED_SOC_UART_5_BASE 0x00184000
|
||||
#define ASPEED_SOC_IOMEM_SIZE 0x00200000
|
||||
#define ASPEED_SOC_IOMEM_BASE 0x1E600000
|
||||
#define ASPEED_SOC_FMC_BASE 0x1E620000
|
||||
#define ASPEED_SOC_SPI_BASE 0x1E630000
|
||||
#define ASPEED_SOC_SPI2_BASE 0x1E631000
|
||||
#define ASPEED_SOC_VIC_BASE 0x1E6C0000
|
||||
#define ASPEED_SOC_SDMC_BASE 0x1E6E0000
|
||||
#define ASPEED_SOC_SCU_BASE 0x1E6E2000
|
||||
#define ASPEED_SOC_SRAM_BASE 0x1E720000
|
||||
#define ASPEED_SOC_TIMER_BASE 0x1E782000
|
||||
#define ASPEED_SOC_WDT_BASE 0x1E785000
|
||||
#define ASPEED_SOC_I2C_BASE 0x1E78A000
|
||||
#define ASPEED_SOC_ETH1_BASE 0x1E660000
|
||||
#define ASPEED_SOC_ETH2_BASE 0x1E680000
|
||||
|
||||
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
|
||||
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
|
||||
static const hwaddr aspeed_soc_ast2400_memmap[] = {
|
||||
[ASPEED_IOMEM] = 0x1E600000,
|
||||
[ASPEED_FMC] = 0x1E620000,
|
||||
[ASPEED_SPI1] = 0x1E630000,
|
||||
[ASPEED_VIC] = 0x1E6C0000,
|
||||
[ASPEED_SDMC] = 0x1E6E0000,
|
||||
[ASPEED_SCU] = 0x1E6E2000,
|
||||
[ASPEED_XDMA] = 0x1E6E7000,
|
||||
[ASPEED_ADC] = 0x1E6E9000,
|
||||
[ASPEED_SRAM] = 0x1E720000,
|
||||
[ASPEED_GPIO] = 0x1E780000,
|
||||
[ASPEED_RTC] = 0x1E781000,
|
||||
[ASPEED_TIMER1] = 0x1E782000,
|
||||
[ASPEED_WDT] = 0x1E785000,
|
||||
[ASPEED_PWM] = 0x1E786000,
|
||||
[ASPEED_LPC] = 0x1E789000,
|
||||
[ASPEED_IBT] = 0x1E789140,
|
||||
[ASPEED_I2C] = 0x1E78A000,
|
||||
[ASPEED_ETH1] = 0x1E660000,
|
||||
[ASPEED_ETH2] = 0x1E680000,
|
||||
[ASPEED_UART1] = 0x1E783000,
|
||||
[ASPEED_UART5] = 0x1E784000,
|
||||
[ASPEED_VUART] = 0x1E787000,
|
||||
[ASPEED_SDRAM] = 0x40000000,
|
||||
};
|
||||
|
||||
#define AST2400_SDRAM_BASE 0x40000000
|
||||
#define AST2500_SDRAM_BASE 0x80000000
|
||||
static const hwaddr aspeed_soc_ast2500_memmap[] = {
|
||||
[ASPEED_IOMEM] = 0x1E600000,
|
||||
[ASPEED_FMC] = 0x1E620000,
|
||||
[ASPEED_SPI1] = 0x1E630000,
|
||||
[ASPEED_SPI2] = 0x1E631000,
|
||||
[ASPEED_VIC] = 0x1E6C0000,
|
||||
[ASPEED_SDMC] = 0x1E6E0000,
|
||||
[ASPEED_SCU] = 0x1E6E2000,
|
||||
[ASPEED_XDMA] = 0x1E6E7000,
|
||||
[ASPEED_ADC] = 0x1E6E9000,
|
||||
[ASPEED_SRAM] = 0x1E720000,
|
||||
[ASPEED_GPIO] = 0x1E780000,
|
||||
[ASPEED_RTC] = 0x1E781000,
|
||||
[ASPEED_TIMER1] = 0x1E782000,
|
||||
[ASPEED_WDT] = 0x1E785000,
|
||||
[ASPEED_PWM] = 0x1E786000,
|
||||
[ASPEED_LPC] = 0x1E789000,
|
||||
[ASPEED_IBT] = 0x1E789140,
|
||||
[ASPEED_I2C] = 0x1E78A000,
|
||||
[ASPEED_ETH1] = 0x1E660000,
|
||||
[ASPEED_ETH2] = 0x1E680000,
|
||||
[ASPEED_UART1] = 0x1E783000,
|
||||
[ASPEED_UART5] = 0x1E784000,
|
||||
[ASPEED_VUART] = 0x1E787000,
|
||||
[ASPEED_SDRAM] = 0x80000000,
|
||||
};
|
||||
|
||||
static const int aspeed_soc_ast2400_irqmap[] = {
|
||||
[ASPEED_UART1] = 9,
|
||||
[ASPEED_UART2] = 32,
|
||||
[ASPEED_UART3] = 33,
|
||||
[ASPEED_UART4] = 34,
|
||||
[ASPEED_UART5] = 10,
|
||||
[ASPEED_VUART] = 8,
|
||||
[ASPEED_FMC] = 19,
|
||||
[ASPEED_SDMC] = 0,
|
||||
[ASPEED_SCU] = 21,
|
||||
[ASPEED_ADC] = 31,
|
||||
[ASPEED_GPIO] = 20,
|
||||
[ASPEED_RTC] = 22,
|
||||
[ASPEED_TIMER1] = 16,
|
||||
[ASPEED_TIMER2] = 17,
|
||||
[ASPEED_TIMER3] = 18,
|
||||
[ASPEED_TIMER4] = 35,
|
||||
[ASPEED_TIMER5] = 36,
|
||||
[ASPEED_TIMER6] = 37,
|
||||
[ASPEED_TIMER7] = 38,
|
||||
[ASPEED_TIMER8] = 39,
|
||||
[ASPEED_WDT] = 27,
|
||||
[ASPEED_PWM] = 28,
|
||||
[ASPEED_LPC] = 8,
|
||||
[ASPEED_IBT] = 8, /* LPC */
|
||||
[ASPEED_I2C] = 12,
|
||||
[ASPEED_ETH1] = 2,
|
||||
[ASPEED_ETH2] = 3,
|
||||
[ASPEED_XDMA] = 6,
|
||||
};
|
||||
|
||||
#define aspeed_soc_ast2500_irqmap aspeed_soc_ast2400_irqmap
|
||||
|
||||
static const hwaddr aspeed_soc_ast2400_spi_bases[] = { ASPEED_SOC_SPI_BASE };
|
||||
static const char *aspeed_soc_ast2400_typenames[] = { "aspeed.smc.spi" };
|
||||
|
||||
static const hwaddr aspeed_soc_ast2500_spi_bases[] = { ASPEED_SOC_SPI_BASE,
|
||||
ASPEED_SOC_SPI2_BASE};
|
||||
static const char *aspeed_soc_ast2500_typenames[] = {
|
||||
"aspeed.smc.ast2500-spi1", "aspeed.smc.ast2500-spi2" };
|
||||
|
||||
@@ -57,57 +120,71 @@ static const AspeedSoCInfo aspeed_socs[] = {
|
||||
.name = "ast2400-a0",
|
||||
.cpu_type = ARM_CPU_TYPE_NAME("arm926"),
|
||||
.silicon_rev = AST2400_A0_SILICON_REV,
|
||||
.sdram_base = AST2400_SDRAM_BASE,
|
||||
.sram_size = 0x8000,
|
||||
.spis_num = 1,
|
||||
.spi_bases = aspeed_soc_ast2400_spi_bases,
|
||||
.fmc_typename = "aspeed.smc.fmc",
|
||||
.spi_typename = aspeed_soc_ast2400_typenames,
|
||||
.wdts_num = 2,
|
||||
.irqmap = aspeed_soc_ast2400_irqmap,
|
||||
.memmap = aspeed_soc_ast2400_memmap,
|
||||
.num_cpus = 1,
|
||||
}, {
|
||||
.name = "ast2400-a1",
|
||||
.cpu_type = ARM_CPU_TYPE_NAME("arm926"),
|
||||
.silicon_rev = AST2400_A1_SILICON_REV,
|
||||
.sdram_base = AST2400_SDRAM_BASE,
|
||||
.sram_size = 0x8000,
|
||||
.spis_num = 1,
|
||||
.spi_bases = aspeed_soc_ast2400_spi_bases,
|
||||
.fmc_typename = "aspeed.smc.fmc",
|
||||
.spi_typename = aspeed_soc_ast2400_typenames,
|
||||
.wdts_num = 2,
|
||||
.irqmap = aspeed_soc_ast2400_irqmap,
|
||||
.memmap = aspeed_soc_ast2400_memmap,
|
||||
.num_cpus = 1,
|
||||
}, {
|
||||
.name = "ast2400",
|
||||
.cpu_type = ARM_CPU_TYPE_NAME("arm926"),
|
||||
.silicon_rev = AST2400_A0_SILICON_REV,
|
||||
.sdram_base = AST2400_SDRAM_BASE,
|
||||
.sram_size = 0x8000,
|
||||
.spis_num = 1,
|
||||
.spi_bases = aspeed_soc_ast2400_spi_bases,
|
||||
.fmc_typename = "aspeed.smc.fmc",
|
||||
.spi_typename = aspeed_soc_ast2400_typenames,
|
||||
.wdts_num = 2,
|
||||
.irqmap = aspeed_soc_ast2400_irqmap,
|
||||
.memmap = aspeed_soc_ast2400_memmap,
|
||||
.num_cpus = 1,
|
||||
}, {
|
||||
.name = "ast2500-a1",
|
||||
.cpu_type = ARM_CPU_TYPE_NAME("arm1176"),
|
||||
.silicon_rev = AST2500_A1_SILICON_REV,
|
||||
.sdram_base = AST2500_SDRAM_BASE,
|
||||
.sram_size = 0x9000,
|
||||
.spis_num = 2,
|
||||
.spi_bases = aspeed_soc_ast2500_spi_bases,
|
||||
.fmc_typename = "aspeed.smc.ast2500-fmc",
|
||||
.spi_typename = aspeed_soc_ast2500_typenames,
|
||||
.wdts_num = 3,
|
||||
.irqmap = aspeed_soc_ast2500_irqmap,
|
||||
.memmap = aspeed_soc_ast2500_memmap,
|
||||
.num_cpus = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl)
|
||||
{
|
||||
AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
|
||||
|
||||
return qdev_get_gpio_in(DEVICE(&s->vic), sc->info->irqmap[ctrl]);
|
||||
}
|
||||
|
||||
static void aspeed_soc_init(Object *obj)
|
||||
{
|
||||
AspeedSoCState *s = ASPEED_SOC(obj);
|
||||
AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
|
||||
int i;
|
||||
|
||||
object_initialize_child(obj, "cpu", OBJECT(&s->cpu), sizeof(s->cpu),
|
||||
sc->info->cpu_type, &error_abort, NULL);
|
||||
for (i = 0; i < sc->info->num_cpus; i++) {
|
||||
object_initialize_child(obj, "cpu[*]", OBJECT(&s->cpu[i]),
|
||||
sizeof(s->cpu[i]), sc->info->cpu_type,
|
||||
&error_abort, NULL);
|
||||
}
|
||||
|
||||
sysbus_init_child_obj(obj, "scu", OBJECT(&s->scu), sizeof(s->scu),
|
||||
TYPE_ASPEED_SCU);
|
||||
@@ -123,6 +200,9 @@ static void aspeed_soc_init(Object *obj)
|
||||
sysbus_init_child_obj(obj, "vic", OBJECT(&s->vic), sizeof(s->vic),
|
||||
TYPE_ASPEED_VIC);
|
||||
|
||||
sysbus_init_child_obj(obj, "rtc", OBJECT(&s->rtc), sizeof(s->rtc),
|
||||
TYPE_ASPEED_RTC);
|
||||
|
||||
sysbus_init_child_obj(obj, "timerctrl", OBJECT(&s->timerctrl),
|
||||
sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
|
||||
object_property_add_const_link(OBJECT(&s->timerctrl), "scu",
|
||||
@@ -155,10 +235,17 @@ static void aspeed_soc_init(Object *obj)
|
||||
sizeof(s->wdt[i]), TYPE_ASPEED_WDT);
|
||||
qdev_prop_set_uint32(DEVICE(&s->wdt[i]), "silicon-rev",
|
||||
sc->info->silicon_rev);
|
||||
object_property_add_const_link(OBJECT(&s->wdt[i]), "scu",
|
||||
OBJECT(&s->scu), &error_abort);
|
||||
}
|
||||
|
||||
sysbus_init_child_obj(obj, "ftgmac100", OBJECT(&s->ftgmac100),
|
||||
sizeof(s->ftgmac100), TYPE_FTGMAC100);
|
||||
for (i = 0; i < ASPEED_MACS_NUM; i++) {
|
||||
sysbus_init_child_obj(obj, "ftgmac100[*]", OBJECT(&s->ftgmac100[i]),
|
||||
sizeof(s->ftgmac100[i]), TYPE_FTGMAC100);
|
||||
}
|
||||
|
||||
sysbus_init_child_obj(obj, "xdma", OBJECT(&s->xdma), sizeof(s->xdma),
|
||||
TYPE_ASPEED_XDMA);
|
||||
}
|
||||
|
||||
static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
@@ -169,14 +256,22 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
Error *err = NULL, *local_err = NULL;
|
||||
|
||||
/* IO space */
|
||||
create_unimplemented_device("aspeed_soc.io",
|
||||
ASPEED_SOC_IOMEM_BASE, ASPEED_SOC_IOMEM_SIZE);
|
||||
create_unimplemented_device("aspeed_soc.io", sc->info->memmap[ASPEED_IOMEM],
|
||||
ASPEED_SOC_IOMEM_SIZE);
|
||||
|
||||
if (s->num_cpus > sc->info->num_cpus) {
|
||||
warn_report("%s: invalid number of CPUs %d, using default %d",
|
||||
sc->info->name, s->num_cpus, sc->info->num_cpus);
|
||||
s->num_cpus = sc->info->num_cpus;
|
||||
}
|
||||
|
||||
/* CPU */
|
||||
object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
for (i = 0; i < s->num_cpus; i++) {
|
||||
object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* SRAM */
|
||||
@@ -186,8 +281,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
memory_region_add_subregion(get_system_memory(), ASPEED_SOC_SRAM_BASE,
|
||||
&s->sram);
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
sc->info->memmap[ASPEED_SRAM], &s->sram);
|
||||
|
||||
/* SCU */
|
||||
object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
|
||||
@@ -195,7 +290,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, ASPEED_SOC_SCU_BASE);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->info->memmap[ASPEED_SCU]);
|
||||
|
||||
/* VIC */
|
||||
object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
|
||||
@@ -203,29 +298,39 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, ASPEED_SOC_VIC_BASE);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, sc->info->memmap[ASPEED_VIC]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
|
||||
qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ));
|
||||
|
||||
/* RTC */
|
||||
object_property_set_bool(OBJECT(&s->rtc), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->info->memmap[ASPEED_RTC]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0,
|
||||
aspeed_soc_get_irq(s, ASPEED_RTC));
|
||||
|
||||
/* Timer */
|
||||
object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, ASPEED_SOC_TIMER_BASE);
|
||||
for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) {
|
||||
qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0,
|
||||
sc->info->memmap[ASPEED_TIMER1]);
|
||||
for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
|
||||
qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_TIMER1 + i);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
|
||||
}
|
||||
|
||||
/* UART - attach an 8250 to the IO space as our UART5 */
|
||||
if (serial_hd(0)) {
|
||||
qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
|
||||
serial_mm_init(get_system_memory(),
|
||||
ASPEED_SOC_IOMEM_BASE + ASPEED_SOC_UART_5_BASE, 2,
|
||||
qemu_irq uart5 = aspeed_soc_get_irq(s, ASPEED_UART5);
|
||||
serial_mm_init(get_system_memory(), sc->info->memmap[ASPEED_UART5], 2,
|
||||
uart5, 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
@@ -235,21 +340,27 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, ASPEED_SOC_I2C_BASE);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->info->memmap[ASPEED_I2C]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->vic), 12));
|
||||
aspeed_soc_get_irq(s, ASPEED_I2C));
|
||||
|
||||
/* FMC, The number of CS is set at the board level */
|
||||
object_property_set_int(OBJECT(&s->fmc), sc->info->memmap[ASPEED_SDRAM],
|
||||
"sdram-base", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
object_property_set_bool(OBJECT(&s->fmc), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, ASPEED_SOC_FMC_BASE);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->info->memmap[ASPEED_FMC]);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1,
|
||||
s->fmc.ctrl->flash_window_base);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->vic), 19));
|
||||
aspeed_soc_get_irq(s, ASPEED_FMC));
|
||||
|
||||
/* SPI */
|
||||
for (i = 0; i < sc->info->spis_num; i++) {
|
||||
@@ -261,7 +372,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, sc->info->spi_bases[i]);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
|
||||
sc->info->memmap[ASPEED_SPI1 + i]);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1,
|
||||
s->spi[i].ctrl->flash_window_base);
|
||||
}
|
||||
@@ -272,7 +384,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, ASPEED_SOC_SDMC_BASE);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->info->memmap[ASPEED_SDMC]);
|
||||
|
||||
/* Watch dog */
|
||||
for (i = 0; i < sc->info->wdts_num; i++) {
|
||||
@@ -282,23 +394,42 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0,
|
||||
ASPEED_SOC_WDT_BASE + i * 0x20);
|
||||
sc->info->memmap[ASPEED_WDT] + i * 0x20);
|
||||
}
|
||||
|
||||
/* Net */
|
||||
qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]);
|
||||
object_property_set_bool(OBJECT(&s->ftgmac100), true, "aspeed", &err);
|
||||
object_property_set_bool(OBJECT(&s->ftgmac100), true, "realized",
|
||||
&local_err);
|
||||
error_propagate(&err, local_err);
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
qdev_set_nic_properties(DEVICE(&s->ftgmac100[i]), &nd_table[i]);
|
||||
object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "aspeed",
|
||||
&err);
|
||||
object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "realized",
|
||||
&local_err);
|
||||
error_propagate(&err, local_err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0,
|
||||
sc->info->memmap[ASPEED_ETH1 + i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0,
|
||||
aspeed_soc_get_irq(s, ASPEED_ETH1 + i));
|
||||
}
|
||||
|
||||
/* XDMA */
|
||||
object_property_set_bool(OBJECT(&s->xdma), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100), 0, ASPEED_SOC_ETH1_BASE);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->vic), 2));
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0,
|
||||
sc->info->memmap[ASPEED_XDMA]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0,
|
||||
aspeed_soc_get_irq(s, ASPEED_XDMA));
|
||||
}
|
||||
static Property aspeed_soc_properties[] = {
|
||||
DEFINE_PROP_UINT32("num-cpus", AspeedSoCState, num_cpus, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void aspeed_soc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
@@ -309,6 +440,7 @@ static void aspeed_soc_class_init(ObjectClass *oc, void *data)
|
||||
dc->realize = aspeed_soc_realize;
|
||||
/* Reason: Uses serial_hds and nd_table in realize() directly */
|
||||
dc->user_creatable = false;
|
||||
dc->props = aspeed_soc_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo aspeed_soc_type_info = {
|
||||
|
||||
@@ -1109,10 +1109,11 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
|
||||
info->initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
if (info->initrd_start + initrd_size > info->ram_size) {
|
||||
if (info->initrd_start + initrd_size > ram_end) {
|
||||
error_report("could not load initrd '%s': "
|
||||
"too big to fit into RAM after the kernel",
|
||||
info->initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_size = 0;
|
||||
|
||||
@@ -526,6 +526,17 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp)
|
||||
*/
|
||||
create_unimplemented_device("lcdif", FSL_IMX7_LCDIF_ADDR,
|
||||
FSL_IMX7_LCDIF_SIZE);
|
||||
|
||||
/*
|
||||
* DMA APBH
|
||||
*/
|
||||
create_unimplemented_device("dma-apbh", FSL_IMX7_DMA_APBH_ADDR,
|
||||
FSL_IMX7_DMA_APBH_SIZE);
|
||||
/*
|
||||
* PCIe PHY
|
||||
*/
|
||||
create_unimplemented_device("pcie-phy", FSL_IMX7_PCIE_PHY_ADDR,
|
||||
FSL_IMX7_PCIE_PHY_SIZE);
|
||||
}
|
||||
|
||||
static void fsl_imx7_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
@@ -53,6 +53,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
|
||||
if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
|
||||
error_report("This board can only be used with CPU %s",
|
||||
mc->default_cpu_type);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memory_region_init_ram(ddr, NULL, "ddr-ram", DDR_SIZE,
|
||||
|
||||
806
hw/arm/sbsa-ref.c
Normal file
806
hw/arm/sbsa-ref.c
Normal file
@@ -0,0 +1,806 @@
|
||||
/*
|
||||
* ARM SBSA Reference Platform emulation
|
||||
*
|
||||
* Copyright (c) 2018 Linaro Limited
|
||||
* Written by Hongbo Zhang <hongbo.zhang@linaro.org>
|
||||
*
|
||||
* 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-common.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/units.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "sysemu/numa.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "kvm_arm.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/ide/internal.h"
|
||||
#include "hw/ide/ahci_internal.h"
|
||||
#include "hw/intc/arm_gicv3_common.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/pci-host/gpex.h"
|
||||
#include "hw/usb.h"
|
||||
#include "net/net.h"
|
||||
|
||||
#define RAMLIMIT_GB 8192
|
||||
#define RAMLIMIT_BYTES (RAMLIMIT_GB * GiB)
|
||||
|
||||
#define NUM_IRQS 256
|
||||
#define NUM_SMMU_IRQS 4
|
||||
#define NUM_SATA_PORTS 6
|
||||
|
||||
#define VIRTUAL_PMU_IRQ 7
|
||||
#define ARCH_GIC_MAINT_IRQ 9
|
||||
#define ARCH_TIMER_VIRT_IRQ 11
|
||||
#define ARCH_TIMER_S_EL1_IRQ 13
|
||||
#define ARCH_TIMER_NS_EL1_IRQ 14
|
||||
#define ARCH_TIMER_NS_EL2_IRQ 10
|
||||
|
||||
enum {
|
||||
SBSA_FLASH,
|
||||
SBSA_MEM,
|
||||
SBSA_CPUPERIPHS,
|
||||
SBSA_GIC_DIST,
|
||||
SBSA_GIC_REDIST,
|
||||
SBSA_SMMU,
|
||||
SBSA_UART,
|
||||
SBSA_RTC,
|
||||
SBSA_PCIE,
|
||||
SBSA_PCIE_MMIO,
|
||||
SBSA_PCIE_MMIO_HIGH,
|
||||
SBSA_PCIE_PIO,
|
||||
SBSA_PCIE_ECAM,
|
||||
SBSA_GPIO,
|
||||
SBSA_SECURE_UART,
|
||||
SBSA_SECURE_UART_MM,
|
||||
SBSA_SECURE_MEM,
|
||||
SBSA_AHCI,
|
||||
SBSA_EHCI,
|
||||
};
|
||||
|
||||
typedef struct MemMapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} MemMapEntry;
|
||||
|
||||
typedef struct {
|
||||
MachineState parent;
|
||||
struct arm_boot_info bootinfo;
|
||||
int smp_cpus;
|
||||
void *fdt;
|
||||
int fdt_size;
|
||||
int psci_conduit;
|
||||
PFlashCFI01 *flash[2];
|
||||
} SBSAMachineState;
|
||||
|
||||
#define TYPE_SBSA_MACHINE MACHINE_TYPE_NAME("sbsa-ref")
|
||||
#define SBSA_MACHINE(obj) \
|
||||
OBJECT_CHECK(SBSAMachineState, (obj), TYPE_SBSA_MACHINE)
|
||||
|
||||
static const MemMapEntry sbsa_ref_memmap[] = {
|
||||
/* 512M boot ROM */
|
||||
[SBSA_FLASH] = { 0, 0x20000000 },
|
||||
/* 512M secure memory */
|
||||
[SBSA_SECURE_MEM] = { 0x20000000, 0x20000000 },
|
||||
/* Space reserved for CPU peripheral devices */
|
||||
[SBSA_CPUPERIPHS] = { 0x40000000, 0x00040000 },
|
||||
[SBSA_GIC_DIST] = { 0x40060000, 0x00010000 },
|
||||
[SBSA_GIC_REDIST] = { 0x40080000, 0x04000000 },
|
||||
[SBSA_UART] = { 0x60000000, 0x00001000 },
|
||||
[SBSA_RTC] = { 0x60010000, 0x00001000 },
|
||||
[SBSA_GPIO] = { 0x60020000, 0x00001000 },
|
||||
[SBSA_SECURE_UART] = { 0x60030000, 0x00001000 },
|
||||
[SBSA_SECURE_UART_MM] = { 0x60040000, 0x00001000 },
|
||||
[SBSA_SMMU] = { 0x60050000, 0x00020000 },
|
||||
/* Space here reserved for more SMMUs */
|
||||
[SBSA_AHCI] = { 0x60100000, 0x00010000 },
|
||||
[SBSA_EHCI] = { 0x60110000, 0x00010000 },
|
||||
/* Space here reserved for other devices */
|
||||
[SBSA_PCIE_PIO] = { 0x7fff0000, 0x00010000 },
|
||||
/* 32-bit address PCIE MMIO space */
|
||||
[SBSA_PCIE_MMIO] = { 0x80000000, 0x70000000 },
|
||||
/* 256M PCIE ECAM space */
|
||||
[SBSA_PCIE_ECAM] = { 0xf0000000, 0x10000000 },
|
||||
/* ~1TB PCIE MMIO space (4GB to 1024GB boundary) */
|
||||
[SBSA_PCIE_MMIO_HIGH] = { 0x100000000ULL, 0xFF00000000ULL },
|
||||
[SBSA_MEM] = { 0x10000000000ULL, RAMLIMIT_BYTES },
|
||||
};
|
||||
|
||||
static const int sbsa_ref_irqmap[] = {
|
||||
[SBSA_UART] = 1,
|
||||
[SBSA_RTC] = 2,
|
||||
[SBSA_PCIE] = 3, /* ... to 6 */
|
||||
[SBSA_GPIO] = 7,
|
||||
[SBSA_SECURE_UART] = 8,
|
||||
[SBSA_SECURE_UART_MM] = 9,
|
||||
[SBSA_AHCI] = 10,
|
||||
[SBSA_EHCI] = 11,
|
||||
};
|
||||
|
||||
/*
|
||||
* Firmware on this machine only uses ACPI table to load OS, these limited
|
||||
* device tree nodes are just to let firmware know the info which varies from
|
||||
* command line parameters, so it is not necessary to be fully compatible
|
||||
* with the kernel CPU and NUMA binding rules.
|
||||
*/
|
||||
static void create_fdt(SBSAMachineState *sms)
|
||||
{
|
||||
void *fdt = create_device_tree(&sms->fdt_size);
|
||||
const MachineState *ms = MACHINE(sms);
|
||||
int cpu;
|
||||
|
||||
if (!fdt) {
|
||||
error_report("create_device_tree() failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sms->fdt = fdt;
|
||||
|
||||
qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,sbsa-ref");
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
|
||||
|
||||
if (have_numa_distance) {
|
||||
int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t);
|
||||
uint32_t *matrix = g_malloc0(size);
|
||||
int idx, i, j;
|
||||
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
for (j = 0; j < nb_numa_nodes; j++) {
|
||||
idx = (i * nb_numa_nodes + j) * 3;
|
||||
matrix[idx + 0] = cpu_to_be32(i);
|
||||
matrix[idx + 1] = cpu_to_be32(j);
|
||||
matrix[idx + 2] = cpu_to_be32(numa_info[i].distance[j]);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/distance-map");
|
||||
qemu_fdt_setprop(fdt, "/distance-map", "distance-matrix",
|
||||
matrix, size);
|
||||
g_free(matrix);
|
||||
}
|
||||
|
||||
qemu_fdt_add_subnode(sms->fdt, "/cpus");
|
||||
|
||||
for (cpu = sms->smp_cpus - 1; cpu >= 0; cpu--) {
|
||||
char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
|
||||
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
|
||||
CPUState *cs = CPU(armcpu);
|
||||
|
||||
qemu_fdt_add_subnode(sms->fdt, nodename);
|
||||
|
||||
if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) {
|
||||
qemu_fdt_setprop_cell(sms->fdt, nodename, "numa-node-id",
|
||||
ms->possible_cpus->cpus[cs->cpu_index].props.node_id);
|
||||
}
|
||||
|
||||
g_free(nodename);
|
||||
}
|
||||
}
|
||||
|
||||
#define SBSA_FLASH_SECTOR_SIZE (256 * KiB)
|
||||
|
||||
static PFlashCFI01 *sbsa_flash_create1(SBSAMachineState *sms,
|
||||
const char *name,
|
||||
const char *alias_prop_name)
|
||||
{
|
||||
/*
|
||||
* Create a single flash device. We use the same parameters as
|
||||
* the flash devices on the Versatile Express board.
|
||||
*/
|
||||
DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01);
|
||||
|
||||
qdev_prop_set_uint64(dev, "sector-length", SBSA_FLASH_SECTOR_SIZE);
|
||||
qdev_prop_set_uint8(dev, "width", 4);
|
||||
qdev_prop_set_uint8(dev, "device-width", 2);
|
||||
qdev_prop_set_bit(dev, "big-endian", false);
|
||||
qdev_prop_set_uint16(dev, "id0", 0x89);
|
||||
qdev_prop_set_uint16(dev, "id1", 0x18);
|
||||
qdev_prop_set_uint16(dev, "id2", 0x00);
|
||||
qdev_prop_set_uint16(dev, "id3", 0x00);
|
||||
qdev_prop_set_string(dev, "name", name);
|
||||
object_property_add_child(OBJECT(sms), name, OBJECT(dev),
|
||||
&error_abort);
|
||||
object_property_add_alias(OBJECT(sms), alias_prop_name,
|
||||
OBJECT(dev), "drive", &error_abort);
|
||||
return PFLASH_CFI01(dev);
|
||||
}
|
||||
|
||||
static void sbsa_flash_create(SBSAMachineState *sms)
|
||||
{
|
||||
sms->flash[0] = sbsa_flash_create1(sms, "sbsa.flash0", "pflash0");
|
||||
sms->flash[1] = sbsa_flash_create1(sms, "sbsa.flash1", "pflash1");
|
||||
}
|
||||
|
||||
static void sbsa_flash_map1(PFlashCFI01 *flash,
|
||||
hwaddr base, hwaddr size,
|
||||
MemoryRegion *sysmem)
|
||||
{
|
||||
DeviceState *dev = DEVICE(flash);
|
||||
|
||||
assert(size % SBSA_FLASH_SECTOR_SIZE == 0);
|
||||
assert(size / SBSA_FLASH_SECTOR_SIZE <= UINT32_MAX);
|
||||
qdev_prop_set_uint32(dev, "num-blocks", size / SBSA_FLASH_SECTOR_SIZE);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
memory_region_add_subregion(sysmem, base,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
|
||||
0));
|
||||
}
|
||||
|
||||
static void sbsa_flash_map(SBSAMachineState *sms,
|
||||
MemoryRegion *sysmem,
|
||||
MemoryRegion *secure_sysmem)
|
||||
{
|
||||
/*
|
||||
* Map two flash devices to fill the SBSA_FLASH space in the memmap.
|
||||
* sysmem is the system memory space. secure_sysmem is the secure view
|
||||
* of the system, and the first flash device should be made visible only
|
||||
* there. The second flash device is visible to both secure and nonsecure.
|
||||
* If sysmem == secure_sysmem this means there is no separate Secure
|
||||
* address space and both flash devices are generally visible.
|
||||
*/
|
||||
hwaddr flashsize = sbsa_ref_memmap[SBSA_FLASH].size / 2;
|
||||
hwaddr flashbase = sbsa_ref_memmap[SBSA_FLASH].base;
|
||||
|
||||
sbsa_flash_map1(sms->flash[0], flashbase, flashsize,
|
||||
secure_sysmem);
|
||||
sbsa_flash_map1(sms->flash[1], flashbase + flashsize, flashsize,
|
||||
sysmem);
|
||||
}
|
||||
|
||||
static bool sbsa_firmware_init(SBSAMachineState *sms,
|
||||
MemoryRegion *sysmem,
|
||||
MemoryRegion *secure_sysmem)
|
||||
{
|
||||
int i;
|
||||
BlockBackend *pflash_blk0;
|
||||
|
||||
/* Map legacy -drive if=pflash to machine properties */
|
||||
for (i = 0; i < ARRAY_SIZE(sms->flash); i++) {
|
||||
pflash_cfi01_legacy_drive(sms->flash[i],
|
||||
drive_get(IF_PFLASH, 0, i));
|
||||
}
|
||||
|
||||
sbsa_flash_map(sms, sysmem, secure_sysmem);
|
||||
|
||||
pflash_blk0 = pflash_cfi01_get_blk(sms->flash[0]);
|
||||
|
||||
if (bios_name) {
|
||||
char *fname;
|
||||
MemoryRegion *mr;
|
||||
int image_size;
|
||||
|
||||
if (pflash_blk0) {
|
||||
error_report("The contents of the first flash device may be "
|
||||
"specified with -bios or with -drive if=pflash... "
|
||||
"but you cannot use both options at once");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Fall back to -bios */
|
||||
|
||||
fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||
if (!fname) {
|
||||
error_report("Could not find ROM image '%s'", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(sms->flash[0]), 0);
|
||||
image_size = load_image_mr(fname, mr);
|
||||
g_free(fname);
|
||||
if (image_size < 0) {
|
||||
error_report("Could not load ROM image '%s'", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return pflash_blk0 || bios_name;
|
||||
}
|
||||
|
||||
static void create_secure_ram(SBSAMachineState *sms,
|
||||
MemoryRegion *secure_sysmem)
|
||||
{
|
||||
MemoryRegion *secram = g_new(MemoryRegion, 1);
|
||||
hwaddr base = sbsa_ref_memmap[SBSA_SECURE_MEM].base;
|
||||
hwaddr size = sbsa_ref_memmap[SBSA_SECURE_MEM].size;
|
||||
|
||||
memory_region_init_ram(secram, NULL, "sbsa-ref.secure-ram", size,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(secure_sysmem, base, secram);
|
||||
}
|
||||
|
||||
static void create_gic(SBSAMachineState *sms, qemu_irq *pic)
|
||||
{
|
||||
DeviceState *gicdev;
|
||||
SysBusDevice *gicbusdev;
|
||||
const char *gictype;
|
||||
uint32_t redist0_capacity, redist0_count;
|
||||
int i;
|
||||
|
||||
gictype = gicv3_class_name();
|
||||
|
||||
gicdev = qdev_create(NULL, gictype);
|
||||
qdev_prop_set_uint32(gicdev, "revision", 3);
|
||||
qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus);
|
||||
/*
|
||||
* Note that the num-irq property counts both internal and external
|
||||
* interrupts; there are always 32 of the former (mandated by GIC spec).
|
||||
*/
|
||||
qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32);
|
||||
qdev_prop_set_bit(gicdev, "has-security-extensions", true);
|
||||
|
||||
redist0_capacity =
|
||||
sbsa_ref_memmap[SBSA_GIC_REDIST].size / GICV3_REDIST_SIZE;
|
||||
redist0_count = MIN(smp_cpus, redist0_capacity);
|
||||
|
||||
qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1);
|
||||
qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count);
|
||||
|
||||
qdev_init_nofail(gicdev);
|
||||
gicbusdev = SYS_BUS_DEVICE(gicdev);
|
||||
sysbus_mmio_map(gicbusdev, 0, sbsa_ref_memmap[SBSA_GIC_DIST].base);
|
||||
sysbus_mmio_map(gicbusdev, 1, sbsa_ref_memmap[SBSA_GIC_REDIST].base);
|
||||
|
||||
/*
|
||||
* Wire the outputs from each CPU's generic timer and the GICv3
|
||||
* maintenance interrupt signal to the appropriate GIC PPI inputs,
|
||||
* and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs.
|
||||
*/
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
|
||||
int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS;
|
||||
int irq;
|
||||
/*
|
||||
* Mapping from the output timer irq lines from the CPU to the
|
||||
* GIC PPI inputs used for this board.
|
||||
*/
|
||||
const int timer_irq[] = {
|
||||
[GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
|
||||
[GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
|
||||
[GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
|
||||
[GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
|
||||
};
|
||||
|
||||
for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
|
||||
qdev_connect_gpio_out(cpudev, irq,
|
||||
qdev_get_gpio_in(gicdev,
|
||||
ppibase + timer_irq[irq]));
|
||||
}
|
||||
|
||||
qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0,
|
||||
qdev_get_gpio_in(gicdev, ppibase
|
||||
+ ARCH_GIC_MAINT_IRQ));
|
||||
qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0,
|
||||
qdev_get_gpio_in(gicdev, ppibase
|
||||
+ VIRTUAL_PMU_IRQ));
|
||||
|
||||
sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
|
||||
sysbus_connect_irq(gicbusdev, i + smp_cpus,
|
||||
qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
|
||||
sysbus_connect_irq(gicbusdev, i + 2 * smp_cpus,
|
||||
qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
|
||||
sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus,
|
||||
qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_IRQS; i++) {
|
||||
pic[i] = qdev_get_gpio_in(gicdev, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_uart(const SBSAMachineState *sms, qemu_irq *pic, int uart,
|
||||
MemoryRegion *mem, Chardev *chr)
|
||||
{
|
||||
hwaddr base = sbsa_ref_memmap[uart].base;
|
||||
int irq = sbsa_ref_irqmap[uart];
|
||||
DeviceState *dev = qdev_create(NULL, "pl011");
|
||||
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
||||
|
||||
qdev_prop_set_chr(dev, "chardev", chr);
|
||||
qdev_init_nofail(dev);
|
||||
memory_region_add_subregion(mem, base,
|
||||
sysbus_mmio_get_region(s, 0));
|
||||
sysbus_connect_irq(s, 0, pic[irq]);
|
||||
}
|
||||
|
||||
static void create_rtc(const SBSAMachineState *sms, qemu_irq *pic)
|
||||
{
|
||||
hwaddr base = sbsa_ref_memmap[SBSA_RTC].base;
|
||||
int irq = sbsa_ref_irqmap[SBSA_RTC];
|
||||
|
||||
sysbus_create_simple("pl031", base, pic[irq]);
|
||||
}
|
||||
|
||||
static DeviceState *gpio_key_dev;
|
||||
static void sbsa_ref_powerdown_req(Notifier *n, void *opaque)
|
||||
{
|
||||
/* use gpio Pin 3 for power button event */
|
||||
qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
|
||||
}
|
||||
|
||||
static Notifier sbsa_ref_powerdown_notifier = {
|
||||
.notify = sbsa_ref_powerdown_req
|
||||
};
|
||||
|
||||
static void create_gpio(const SBSAMachineState *sms, qemu_irq *pic)
|
||||
{
|
||||
DeviceState *pl061_dev;
|
||||
hwaddr base = sbsa_ref_memmap[SBSA_GPIO].base;
|
||||
int irq = sbsa_ref_irqmap[SBSA_GPIO];
|
||||
|
||||
pl061_dev = sysbus_create_simple("pl061", base, pic[irq]);
|
||||
|
||||
gpio_key_dev = sysbus_create_simple("gpio-key", -1,
|
||||
qdev_get_gpio_in(pl061_dev, 3));
|
||||
|
||||
/* connect powerdown request */
|
||||
qemu_register_powerdown_notifier(&sbsa_ref_powerdown_notifier);
|
||||
}
|
||||
|
||||
static void create_ahci(const SBSAMachineState *sms, qemu_irq *pic)
|
||||
{
|
||||
hwaddr base = sbsa_ref_memmap[SBSA_AHCI].base;
|
||||
int irq = sbsa_ref_irqmap[SBSA_AHCI];
|
||||
DeviceState *dev;
|
||||
DriveInfo *hd[NUM_SATA_PORTS];
|
||||
SysbusAHCIState *sysahci;
|
||||
AHCIState *ahci;
|
||||
int i;
|
||||
|
||||
dev = qdev_create(NULL, "sysbus-ahci");
|
||||
qdev_prop_set_uint32(dev, "num-ports", NUM_SATA_PORTS);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irq]);
|
||||
|
||||
sysahci = SYSBUS_AHCI(dev);
|
||||
ahci = &sysahci->ahci;
|
||||
ide_drive_get(hd, ARRAY_SIZE(hd));
|
||||
for (i = 0; i < ahci->ports; i++) {
|
||||
if (hd[i] == NULL) {
|
||||
continue;
|
||||
}
|
||||
ide_create_drive(&ahci->dev[i].port, 0, hd[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_ehci(const SBSAMachineState *sms, qemu_irq *pic)
|
||||
{
|
||||
hwaddr base = sbsa_ref_memmap[SBSA_EHCI].base;
|
||||
int irq = sbsa_ref_irqmap[SBSA_EHCI];
|
||||
|
||||
sysbus_create_simple("platform-ehci-usb", base, pic[irq]);
|
||||
}
|
||||
|
||||
static void create_smmu(const SBSAMachineState *sms, qemu_irq *pic,
|
||||
PCIBus *bus)
|
||||
{
|
||||
hwaddr base = sbsa_ref_memmap[SBSA_SMMU].base;
|
||||
int irq = sbsa_ref_irqmap[SBSA_SMMU];
|
||||
DeviceState *dev;
|
||||
int i;
|
||||
|
||||
dev = qdev_create(NULL, "arm-smmuv3");
|
||||
|
||||
object_property_set_link(OBJECT(dev), OBJECT(bus), "primary-bus",
|
||||
&error_abort);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
|
||||
for (i = 0; i < NUM_SMMU_IRQS; i++) {
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_pcie(SBSAMachineState *sms, qemu_irq *pic)
|
||||
{
|
||||
hwaddr base_ecam = sbsa_ref_memmap[SBSA_PCIE_ECAM].base;
|
||||
hwaddr size_ecam = sbsa_ref_memmap[SBSA_PCIE_ECAM].size;
|
||||
hwaddr base_mmio = sbsa_ref_memmap[SBSA_PCIE_MMIO].base;
|
||||
hwaddr size_mmio = sbsa_ref_memmap[SBSA_PCIE_MMIO].size;
|
||||
hwaddr base_mmio_high = sbsa_ref_memmap[SBSA_PCIE_MMIO_HIGH].base;
|
||||
hwaddr size_mmio_high = sbsa_ref_memmap[SBSA_PCIE_MMIO_HIGH].size;
|
||||
hwaddr base_pio = sbsa_ref_memmap[SBSA_PCIE_PIO].base;
|
||||
int irq = sbsa_ref_irqmap[SBSA_PCIE];
|
||||
MemoryRegion *mmio_alias, *mmio_alias_high, *mmio_reg;
|
||||
MemoryRegion *ecam_alias, *ecam_reg;
|
||||
DeviceState *dev;
|
||||
PCIHostState *pci;
|
||||
int i;
|
||||
|
||||
dev = qdev_create(NULL, TYPE_GPEX_HOST);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
/* Map ECAM space */
|
||||
ecam_alias = g_new0(MemoryRegion, 1);
|
||||
ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
|
||||
memory_region_init_alias(ecam_alias, OBJECT(dev), "pcie-ecam",
|
||||
ecam_reg, 0, size_ecam);
|
||||
memory_region_add_subregion(get_system_memory(), base_ecam, ecam_alias);
|
||||
|
||||
/* Map the MMIO space */
|
||||
mmio_alias = g_new0(MemoryRegion, 1);
|
||||
mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
|
||||
memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio",
|
||||
mmio_reg, base_mmio, size_mmio);
|
||||
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
|
||||
|
||||
/* Map the MMIO_HIGH space */
|
||||
mmio_alias_high = g_new0(MemoryRegion, 1);
|
||||
memory_region_init_alias(mmio_alias_high, OBJECT(dev), "pcie-mmio-high",
|
||||
mmio_reg, base_mmio_high, size_mmio_high);
|
||||
memory_region_add_subregion(get_system_memory(), base_mmio_high,
|
||||
mmio_alias_high);
|
||||
|
||||
/* Map IO port space */
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
|
||||
|
||||
for (i = 0; i < GPEX_NUM_IRQS; i++) {
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
|
||||
gpex_set_irq_num(GPEX_HOST(dev), i, irq + i);
|
||||
}
|
||||
|
||||
pci = PCI_HOST_BRIDGE(dev);
|
||||
if (pci->bus) {
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
NICInfo *nd = &nd_table[i];
|
||||
|
||||
if (!nd->model) {
|
||||
nd->model = g_strdup("e1000e");
|
||||
}
|
||||
|
||||
pci_nic_init_nofail(nd, pci->bus, nd->model, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
pci_create_simple(pci->bus, -1, "VGA");
|
||||
|
||||
create_smmu(sms, pic, pci->bus);
|
||||
}
|
||||
|
||||
static void *sbsa_ref_dtb(const struct arm_boot_info *binfo, int *fdt_size)
|
||||
{
|
||||
const SBSAMachineState *board = container_of(binfo, SBSAMachineState,
|
||||
bootinfo);
|
||||
|
||||
*fdt_size = board->fdt_size;
|
||||
return board->fdt;
|
||||
}
|
||||
|
||||
static void sbsa_ref_init(MachineState *machine)
|
||||
{
|
||||
SBSAMachineState *sms = SBSA_MACHINE(machine);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *secure_sysmem = NULL;
|
||||
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
||||
bool firmware_loaded;
|
||||
const CPUArchIdList *possible_cpus;
|
||||
int n, sbsa_max_cpus;
|
||||
qemu_irq pic[NUM_IRQS];
|
||||
|
||||
if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a57"))) {
|
||||
error_report("sbsa-ref: CPU type other than the built-in "
|
||||
"cortex-a57 not supported");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (kvm_enabled()) {
|
||||
error_report("sbsa-ref: KVM is not supported for this machine");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The Secure view of the world is the same as the NonSecure,
|
||||
* but with a few extra devices. Create it as a container region
|
||||
* containing the system memory at low priority; any secure-only
|
||||
* devices go in at higher priority and take precedence.
|
||||
*/
|
||||
secure_sysmem = g_new(MemoryRegion, 1);
|
||||
memory_region_init(secure_sysmem, OBJECT(machine), "secure-memory",
|
||||
UINT64_MAX);
|
||||
memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1);
|
||||
|
||||
firmware_loaded = sbsa_firmware_init(sms, sysmem,
|
||||
secure_sysmem ?: sysmem);
|
||||
|
||||
if (machine->kernel_filename && firmware_loaded) {
|
||||
error_report("sbsa-ref: No fw_cfg device on this machine, "
|
||||
"so -kernel option is not supported when firmware loaded, "
|
||||
"please load OS from hard disk instead");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This machine has EL3 enabled, external firmware should supply PSCI
|
||||
* implementation, so the QEMU's internal PSCI is disabled.
|
||||
*/
|
||||
sms->psci_conduit = QEMU_PSCI_CONDUIT_DISABLED;
|
||||
|
||||
sbsa_max_cpus = sbsa_ref_memmap[SBSA_GIC_REDIST].size / GICV3_REDIST_SIZE;
|
||||
|
||||
if (max_cpus > sbsa_max_cpus) {
|
||||
error_report("Number of SMP CPUs requested (%d) exceeds max CPUs "
|
||||
"supported by machine 'sbsa-ref' (%d)",
|
||||
max_cpus, sbsa_max_cpus);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sms->smp_cpus = smp_cpus;
|
||||
|
||||
if (machine->ram_size > sbsa_ref_memmap[SBSA_MEM].size) {
|
||||
error_report("sbsa-ref: cannot model more than %dGB RAM", RAMLIMIT_GB);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
possible_cpus = mc->possible_cpu_arch_ids(machine);
|
||||
for (n = 0; n < possible_cpus->len; n++) {
|
||||
Object *cpuobj;
|
||||
CPUState *cs;
|
||||
|
||||
if (n >= smp_cpus) {
|
||||
break;
|
||||
}
|
||||
|
||||
cpuobj = object_new(possible_cpus->cpus[n].type);
|
||||
object_property_set_int(cpuobj, possible_cpus->cpus[n].arch_id,
|
||||
"mp-affinity", NULL);
|
||||
|
||||
cs = CPU(cpuobj);
|
||||
cs->cpu_index = n;
|
||||
|
||||
numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj),
|
||||
&error_fatal);
|
||||
|
||||
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
|
||||
object_property_set_int(cpuobj,
|
||||
sbsa_ref_memmap[SBSA_CPUPERIPHS].base,
|
||||
"reset-cbar", &error_abort);
|
||||
}
|
||||
|
||||
object_property_set_link(cpuobj, OBJECT(sysmem), "memory",
|
||||
&error_abort);
|
||||
|
||||
object_property_set_link(cpuobj, OBJECT(secure_sysmem),
|
||||
"secure-memory", &error_abort);
|
||||
|
||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||
object_unref(cpuobj);
|
||||
}
|
||||
|
||||
memory_region_allocate_system_memory(ram, NULL, "sbsa-ref.ram",
|
||||
machine->ram_size);
|
||||
memory_region_add_subregion(sysmem, sbsa_ref_memmap[SBSA_MEM].base, ram);
|
||||
|
||||
create_fdt(sms);
|
||||
|
||||
create_secure_ram(sms, secure_sysmem);
|
||||
|
||||
create_gic(sms, pic);
|
||||
|
||||
create_uart(sms, pic, SBSA_UART, sysmem, serial_hd(0));
|
||||
create_uart(sms, pic, SBSA_SECURE_UART, secure_sysmem, serial_hd(1));
|
||||
/* Second secure UART for RAS and MM from EL0 */
|
||||
create_uart(sms, pic, SBSA_SECURE_UART_MM, secure_sysmem, serial_hd(2));
|
||||
|
||||
create_rtc(sms, pic);
|
||||
|
||||
create_gpio(sms, pic);
|
||||
|
||||
create_ahci(sms, pic);
|
||||
|
||||
create_ehci(sms, pic);
|
||||
|
||||
create_pcie(sms, pic);
|
||||
|
||||
sms->bootinfo.ram_size = machine->ram_size;
|
||||
sms->bootinfo.kernel_filename = machine->kernel_filename;
|
||||
sms->bootinfo.nb_cpus = smp_cpus;
|
||||
sms->bootinfo.board_id = -1;
|
||||
sms->bootinfo.loader_start = sbsa_ref_memmap[SBSA_MEM].base;
|
||||
sms->bootinfo.get_dtb = sbsa_ref_dtb;
|
||||
sms->bootinfo.firmware_loaded = firmware_loaded;
|
||||
arm_load_kernel(ARM_CPU(first_cpu), &sms->bootinfo);
|
||||
}
|
||||
|
||||
static uint64_t sbsa_ref_cpu_mp_affinity(SBSAMachineState *sms, int idx)
|
||||
{
|
||||
uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER;
|
||||
return arm_cpu_mp_affinity(idx, clustersz);
|
||||
}
|
||||
|
||||
static const CPUArchIdList *sbsa_ref_possible_cpu_arch_ids(MachineState *ms)
|
||||
{
|
||||
SBSAMachineState *sms = SBSA_MACHINE(ms);
|
||||
int n;
|
||||
|
||||
if (ms->possible_cpus) {
|
||||
assert(ms->possible_cpus->len == max_cpus);
|
||||
return ms->possible_cpus;
|
||||
}
|
||||
|
||||
ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
|
||||
sizeof(CPUArchId) * max_cpus);
|
||||
ms->possible_cpus->len = max_cpus;
|
||||
for (n = 0; n < ms->possible_cpus->len; n++) {
|
||||
ms->possible_cpus->cpus[n].type = ms->cpu_type;
|
||||
ms->possible_cpus->cpus[n].arch_id =
|
||||
sbsa_ref_cpu_mp_affinity(sms, n);
|
||||
ms->possible_cpus->cpus[n].props.has_thread_id = true;
|
||||
ms->possible_cpus->cpus[n].props.thread_id = n;
|
||||
}
|
||||
return ms->possible_cpus;
|
||||
}
|
||||
|
||||
static CpuInstanceProperties
|
||||
sbsa_ref_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
|
||||
|
||||
assert(cpu_index < possible_cpus->len);
|
||||
return possible_cpus->cpus[cpu_index].props;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sbsa_ref_get_default_cpu_node_id(const MachineState *ms, int idx)
|
||||
{
|
||||
return idx % nb_numa_nodes;
|
||||
}
|
||||
|
||||
static void sbsa_ref_instance_init(Object *obj)
|
||||
{
|
||||
SBSAMachineState *sms = SBSA_MACHINE(obj);
|
||||
|
||||
sbsa_flash_create(sms);
|
||||
}
|
||||
|
||||
static void sbsa_ref_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->init = sbsa_ref_init;
|
||||
mc->desc = "QEMU 'SBSA Reference' ARM Virtual Machine";
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a57");
|
||||
mc->max_cpus = 512;
|
||||
mc->pci_allow_0_address = true;
|
||||
mc->minimum_page_bits = 12;
|
||||
mc->block_default_type = IF_IDE;
|
||||
mc->no_cdrom = 1;
|
||||
mc->default_ram_size = 1 * GiB;
|
||||
mc->default_cpus = 4;
|
||||
mc->possible_cpu_arch_ids = sbsa_ref_possible_cpu_arch_ids;
|
||||
mc->cpu_index_to_instance_props = sbsa_ref_cpu_index_to_props;
|
||||
mc->get_default_cpu_node_id = sbsa_ref_get_default_cpu_node_id;
|
||||
}
|
||||
|
||||
static const TypeInfo sbsa_ref_info = {
|
||||
.name = TYPE_SBSA_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_init = sbsa_ref_instance_init,
|
||||
.class_init = sbsa_ref_class_init,
|
||||
.instance_size = sizeof(SBSAMachineState),
|
||||
};
|
||||
|
||||
static void sbsa_ref_machine_init(void)
|
||||
{
|
||||
type_register_static(&sbsa_ref_info);
|
||||
}
|
||||
|
||||
type_init(sbsa_ref_machine_init);
|
||||
@@ -176,6 +176,7 @@ static const int a15irqmap[] = {
|
||||
};
|
||||
|
||||
static const char *valid_cpus[] = {
|
||||
ARM_CPU_TYPE_NAME("cortex-a7"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a15"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a53"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a57"),
|
||||
|
||||
@@ -248,7 +248,6 @@ static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset,
|
||||
switch (width) {
|
||||
case 1:
|
||||
ret = p[offset];
|
||||
trace_pflash_data_read8(offset, ret);
|
||||
break;
|
||||
case 2:
|
||||
if (be) {
|
||||
@@ -258,7 +257,6 @@ static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset,
|
||||
ret = p[offset];
|
||||
ret |= p[offset + 1] << 8;
|
||||
}
|
||||
trace_pflash_data_read16(offset, ret);
|
||||
break;
|
||||
case 4:
|
||||
if (be) {
|
||||
@@ -272,12 +270,12 @@ static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset,
|
||||
ret |= p[offset + 2] << 16;
|
||||
ret |= p[offset + 3] << 24;
|
||||
}
|
||||
trace_pflash_data_read32(offset, ret);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("BUG in %s\n", __func__);
|
||||
abort();
|
||||
}
|
||||
trace_pflash_data_read(offset, width << 1, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -288,7 +286,6 @@ static uint32_t pflash_read(PFlashCFI01 *pfl, hwaddr offset,
|
||||
uint32_t ret;
|
||||
|
||||
ret = -1;
|
||||
trace_pflash_read(offset, pfl->cmd, width, pfl->wcycle);
|
||||
switch (pfl->cmd) {
|
||||
default:
|
||||
/* This should never happen : reset state & treat it as a read */
|
||||
@@ -391,6 +388,8 @@ static uint32_t pflash_read(PFlashCFI01 *pfl, hwaddr offset,
|
||||
|
||||
break;
|
||||
}
|
||||
trace_pflash_io_read(offset, width, width << 1, ret, pfl->cmd, pfl->wcycle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -414,7 +413,7 @@ static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset,
|
||||
{
|
||||
uint8_t *p = pfl->storage;
|
||||
|
||||
trace_pflash_data_write(offset, value, width, pfl->counter);
|
||||
trace_pflash_data_write(offset, width << 1, value, pfl->counter);
|
||||
switch (width) {
|
||||
case 1:
|
||||
p[offset] = value;
|
||||
@@ -453,7 +452,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
|
||||
|
||||
cmd = value;
|
||||
|
||||
trace_pflash_write(offset, value, width, pfl->wcycle);
|
||||
trace_pflash_io_write(offset, width, width << 1, value, pfl->wcycle);
|
||||
if (!pfl->wcycle) {
|
||||
/* Set the device in I/O access mode */
|
||||
memory_region_rom_device_set_romd(&pfl->mem, false);
|
||||
|
||||
@@ -29,10 +29,7 @@
|
||||
* - CFI queries
|
||||
*
|
||||
* It does not support flash interleaving.
|
||||
* It does not implement boot blocs with reduced size
|
||||
* It does not implement software data protection as found in many real chips
|
||||
* It does not implement erase suspend/resume commands
|
||||
* It does not implement multiple sectors erase
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@@ -40,6 +37,7 @@
|
||||
#include "hw/block/block.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/host-utils.h"
|
||||
@@ -47,26 +45,40 @@
|
||||
#include "hw/sysbus.h"
|
||||
#include "trace.h"
|
||||
|
||||
//#define PFLASH_DEBUG
|
||||
#ifdef PFLASH_DEBUG
|
||||
#define PFLASH_DEBUG false
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { \
|
||||
fprintf(stderr, "PFLASH: " fmt , ## __VA_ARGS__); \
|
||||
if (PFLASH_DEBUG) { \
|
||||
fprintf(stderr, "PFLASH: " fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define PFLASH_LAZY_ROMD_THRESHOLD 42
|
||||
|
||||
/*
|
||||
* The size of the cfi_table indirectly depends on this and the start of the
|
||||
* PRI table directly depends on it. 4 is the maximum size (and also what
|
||||
* seems common) without changing the PRT table address.
|
||||
*/
|
||||
#define PFLASH_MAX_ERASE_REGIONS 4
|
||||
|
||||
/* Special write cycles for CFI queries. */
|
||||
enum {
|
||||
WCYCLE_CFI = 7,
|
||||
WCYCLE_AUTOSELECT_CFI = 8,
|
||||
};
|
||||
|
||||
struct PFlashCFI02 {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
BlockBackend *blk;
|
||||
uint32_t sector_len;
|
||||
uint32_t nb_blocs;
|
||||
uint32_t uniform_nb_blocs;
|
||||
uint32_t uniform_sector_len;
|
||||
uint32_t total_sectors;
|
||||
uint32_t nb_blocs[PFLASH_MAX_ERASE_REGIONS];
|
||||
uint32_t sector_len[PFLASH_MAX_ERASE_REGIONS];
|
||||
uint32_t chip_len;
|
||||
uint8_t mappings;
|
||||
uint8_t width;
|
||||
@@ -83,7 +95,7 @@ struct PFlashCFI02 {
|
||||
uint16_t ident3;
|
||||
uint16_t unlock_addr0;
|
||||
uint16_t unlock_addr1;
|
||||
uint8_t cfi_table[0x52];
|
||||
uint8_t cfi_table[0x4d];
|
||||
QEMUTimer timer;
|
||||
/* The device replicates the flash memory across its memory space. Emulate
|
||||
* that by having a container (.mem) filled with an array of aliases
|
||||
@@ -94,10 +106,62 @@ struct PFlashCFI02 {
|
||||
MemoryRegion orig_mem;
|
||||
int rom_mode;
|
||||
int read_counter; /* used for lazy switch-back to rom mode */
|
||||
int sectors_to_erase;
|
||||
uint64_t erase_time_remaining;
|
||||
unsigned long *sector_erase_map;
|
||||
char *name;
|
||||
void *storage;
|
||||
};
|
||||
|
||||
/*
|
||||
* Toggle status bit DQ7.
|
||||
*/
|
||||
static inline void toggle_dq7(PFlashCFI02 *pfl)
|
||||
{
|
||||
pfl->status ^= 0x80;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set status bit DQ7 to bit 7 of value.
|
||||
*/
|
||||
static inline void set_dq7(PFlashCFI02 *pfl, uint8_t value)
|
||||
{
|
||||
pfl->status &= 0x7F;
|
||||
pfl->status |= value & 0x80;
|
||||
}
|
||||
|
||||
/*
|
||||
* Toggle status bit DQ6.
|
||||
*/
|
||||
static inline void toggle_dq6(PFlashCFI02 *pfl)
|
||||
{
|
||||
pfl->status ^= 0x40;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn on DQ3.
|
||||
*/
|
||||
static inline void assert_dq3(PFlashCFI02 *pfl)
|
||||
{
|
||||
pfl->status |= 0x08;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn off DQ3.
|
||||
*/
|
||||
static inline void reset_dq3(PFlashCFI02 *pfl)
|
||||
{
|
||||
pfl->status &= ~0x08;
|
||||
}
|
||||
|
||||
/*
|
||||
* Toggle status bit DQ2.
|
||||
*/
|
||||
static inline void toggle_dq2(PFlashCFI02 *pfl)
|
||||
{
|
||||
pfl->status ^= 0x04;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up replicated mappings of the same region.
|
||||
*/
|
||||
@@ -121,13 +185,63 @@ static void pflash_register_memory(PFlashCFI02 *pfl, int rom_mode)
|
||||
pfl->rom_mode = rom_mode;
|
||||
}
|
||||
|
||||
static void pflash_timer (void *opaque)
|
||||
static size_t pflash_regions_count(PFlashCFI02 *pfl)
|
||||
{
|
||||
return pfl->cfi_table[0x2c];
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the time it takes to erase the number of sectors scheduled for
|
||||
* erasure based on CFI address 0x21 which is "Typical timeout per individual
|
||||
* block erase 2^N ms."
|
||||
*/
|
||||
static uint64_t pflash_erase_time(PFlashCFI02 *pfl)
|
||||
{
|
||||
/*
|
||||
* If there are no sectors to erase (which can happen if all of the sectors
|
||||
* to be erased are protected), then erase takes 100 us. Protected sectors
|
||||
* aren't supported so this should never happen.
|
||||
*/
|
||||
return ((1ULL << pfl->cfi_table[0x21]) * pfl->sectors_to_erase) * SCALE_US;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the device is currently in erase suspend mode.
|
||||
*/
|
||||
static inline bool pflash_erase_suspend_mode(PFlashCFI02 *pfl)
|
||||
{
|
||||
return pfl->erase_time_remaining > 0;
|
||||
}
|
||||
|
||||
static void pflash_timer(void *opaque)
|
||||
{
|
||||
PFlashCFI02 *pfl = opaque;
|
||||
|
||||
trace_pflash_timer_expired(pfl->cmd);
|
||||
if (pfl->cmd == 0x30) {
|
||||
/*
|
||||
* Sector erase. If DQ3 is 0 when the timer expires, then the 50
|
||||
* us erase timeout has expired so we need to start the timer for the
|
||||
* sector erase algorithm. Otherwise, the erase completed and we should
|
||||
* go back to read array mode.
|
||||
*/
|
||||
if ((pfl->status & 0x08) == 0) {
|
||||
assert_dq3(pfl);
|
||||
uint64_t timeout = pflash_erase_time(pfl);
|
||||
timer_mod(&pfl->timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout);
|
||||
DPRINTF("%s: erase timeout fired; erasing %d sectors\n",
|
||||
__func__, pfl->sectors_to_erase);
|
||||
return;
|
||||
}
|
||||
DPRINTF("%s: sector erase complete\n", __func__);
|
||||
bitmap_zero(pfl->sector_erase_map, pfl->total_sectors);
|
||||
pfl->sectors_to_erase = 0;
|
||||
reset_dq3(pfl);
|
||||
}
|
||||
|
||||
/* Reset flash */
|
||||
pfl->status ^= 0x80;
|
||||
toggle_dq7(pfl);
|
||||
if (pfl->bypass) {
|
||||
pfl->wcycle = 2;
|
||||
} else {
|
||||
@@ -137,15 +251,63 @@ static void pflash_timer (void *opaque)
|
||||
pfl->cmd = 0;
|
||||
}
|
||||
|
||||
static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset,
|
||||
int width, int be)
|
||||
/*
|
||||
* Read data from flash.
|
||||
*/
|
||||
static uint64_t pflash_data_read(PFlashCFI02 *pfl, hwaddr offset,
|
||||
unsigned int width)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)pfl->storage + offset;
|
||||
uint64_t ret = pfl->be ? ldn_be_p(p, width) : ldn_le_p(p, width);
|
||||
trace_pflash_data_read(offset, width << 1, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t len;
|
||||
uint32_t num;
|
||||
} SectorInfo;
|
||||
|
||||
/*
|
||||
* offset should be a byte offset of the QEMU device and _not_ a device
|
||||
* offset.
|
||||
*/
|
||||
static SectorInfo pflash_sector_info(PFlashCFI02 *pfl, hwaddr offset)
|
||||
{
|
||||
assert(offset < pfl->chip_len);
|
||||
hwaddr addr = 0;
|
||||
uint32_t sector_num = 0;
|
||||
for (int i = 0; i < pflash_regions_count(pfl); ++i) {
|
||||
uint64_t region_size = (uint64_t)pfl->nb_blocs[i] * pfl->sector_len[i];
|
||||
if (addr <= offset && offset < addr + region_size) {
|
||||
return (SectorInfo) {
|
||||
.len = pfl->sector_len[i],
|
||||
.num = sector_num + (offset - addr) / pfl->sector_len[i],
|
||||
};
|
||||
}
|
||||
sector_num += pfl->nb_blocs[i];
|
||||
addr += region_size;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the offset refers to a flash sector that is currently being
|
||||
* erased.
|
||||
*/
|
||||
static bool pflash_sector_is_erasing(PFlashCFI02 *pfl, hwaddr offset)
|
||||
{
|
||||
long sector_num = pflash_sector_info(pfl, offset).num;
|
||||
return test_bit(sector_num, pfl->sector_erase_map);
|
||||
}
|
||||
|
||||
static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width)
|
||||
{
|
||||
PFlashCFI02 *pfl = opaque;
|
||||
hwaddr boff;
|
||||
uint32_t ret;
|
||||
uint8_t *p;
|
||||
uint64_t ret;
|
||||
|
||||
ret = -1;
|
||||
trace_pflash_read(offset, pfl->cmd, width, pfl->wcycle);
|
||||
/* Lazy reset to ROMD mode after a certain amount of read accesses */
|
||||
if (!pfl->rom_mode && pfl->wcycle == 0 &&
|
||||
++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
|
||||
@@ -153,10 +315,9 @@ static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset,
|
||||
}
|
||||
offset &= pfl->chip_len - 1;
|
||||
boff = offset & 0xFF;
|
||||
if (pfl->width == 2)
|
||||
if (pfl->width == 2) {
|
||||
boff = boff >> 1;
|
||||
else if (pfl->width == 4)
|
||||
boff = boff >> 2;
|
||||
}
|
||||
switch (pfl->cmd) {
|
||||
default:
|
||||
/* This should never happen : reset state & treat it as a read*/
|
||||
@@ -164,45 +325,22 @@ static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset,
|
||||
pfl->wcycle = 0;
|
||||
pfl->cmd = 0;
|
||||
/* fall through to the read code */
|
||||
case 0x80:
|
||||
case 0x80: /* Erase (unlock) */
|
||||
/* We accept reads during second unlock sequence... */
|
||||
case 0x00:
|
||||
flash_read:
|
||||
/* Flash area read */
|
||||
p = pfl->storage;
|
||||
switch (width) {
|
||||
case 1:
|
||||
ret = p[offset];
|
||||
trace_pflash_data_read8(offset, ret);
|
||||
break;
|
||||
case 2:
|
||||
if (be) {
|
||||
ret = p[offset] << 8;
|
||||
ret |= p[offset + 1];
|
||||
} else {
|
||||
ret = p[offset];
|
||||
ret |= p[offset + 1] << 8;
|
||||
}
|
||||
trace_pflash_data_read16(offset, ret);
|
||||
break;
|
||||
case 4:
|
||||
if (be) {
|
||||
ret = p[offset] << 24;
|
||||
ret |= p[offset + 1] << 16;
|
||||
ret |= p[offset + 2] << 8;
|
||||
ret |= p[offset + 3];
|
||||
} else {
|
||||
ret = p[offset];
|
||||
ret |= p[offset + 1] << 8;
|
||||
ret |= p[offset + 2] << 16;
|
||||
ret |= p[offset + 3] << 24;
|
||||
}
|
||||
trace_pflash_data_read32(offset, ret);
|
||||
if (pflash_erase_suspend_mode(pfl) &&
|
||||
pflash_sector_is_erasing(pfl, offset)) {
|
||||
/* Toggle bit 2, but not 6. */
|
||||
toggle_dq2(pfl);
|
||||
/* Status register read */
|
||||
ret = pfl->status;
|
||||
DPRINTF("%s: status %" PRIx64 "\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
/* Flash area read */
|
||||
ret = pflash_data_read(pfl, offset, width);
|
||||
break;
|
||||
case 0x90:
|
||||
/* flash ID read */
|
||||
case 0x90: /* flash ID read */
|
||||
switch (boff) {
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
@@ -214,23 +352,25 @@ static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset,
|
||||
case 0x0E:
|
||||
case 0x0F:
|
||||
ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
|
||||
if (ret == (uint8_t)-1) {
|
||||
goto flash_read;
|
||||
if (ret != (uint8_t)-1) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* Fall through to data read. */
|
||||
default:
|
||||
goto flash_read;
|
||||
ret = pflash_data_read(pfl, offset, width);
|
||||
}
|
||||
DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret);
|
||||
DPRINTF("%s: ID " TARGET_FMT_plx " %" PRIx64 "\n", __func__, boff, ret);
|
||||
break;
|
||||
case 0xA0:
|
||||
case 0x10:
|
||||
case 0x30:
|
||||
case 0x10: /* Chip Erase */
|
||||
case 0x30: /* Sector Erase */
|
||||
/* Toggle bit 2 during erase, but not program. */
|
||||
toggle_dq2(pfl);
|
||||
case 0xA0: /* Program */
|
||||
/* Toggle bit 6 */
|
||||
toggle_dq6(pfl);
|
||||
/* Status register read */
|
||||
ret = pfl->status;
|
||||
DPRINTF("%s: status %x\n", __func__, ret);
|
||||
/* Toggle bit 6 */
|
||||
pfl->status ^= 0x40;
|
||||
DPRINTF("%s: status %" PRIx64 "\n", __func__, ret);
|
||||
break;
|
||||
case 0x98:
|
||||
/* CFI query mode */
|
||||
@@ -241,13 +381,13 @@ static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset,
|
||||
}
|
||||
break;
|
||||
}
|
||||
trace_pflash_io_read(offset, width, width << 1, ret, pfl->cmd, pfl->wcycle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* update flash content on disk */
|
||||
static void pflash_update(PFlashCFI02 *pfl, int offset,
|
||||
int size)
|
||||
static void pflash_update(PFlashCFI02 *pfl, int offset, int size)
|
||||
{
|
||||
int offset_end;
|
||||
if (pfl->blk) {
|
||||
@@ -260,31 +400,56 @@ static void pflash_update(PFlashCFI02 *pfl, int offset,
|
||||
}
|
||||
}
|
||||
|
||||
static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
uint32_t value, int width, int be)
|
||||
static void pflash_sector_erase(PFlashCFI02 *pfl, hwaddr offset)
|
||||
{
|
||||
SectorInfo sector_info = pflash_sector_info(pfl, offset);
|
||||
uint64_t sector_len = sector_info.len;
|
||||
offset &= ~(sector_len - 1);
|
||||
DPRINTF("%s: start sector erase at %0*" PRIx64 "-%0*" PRIx64 "\n",
|
||||
__func__, pfl->width * 2, offset,
|
||||
pfl->width * 2, offset + sector_len - 1);
|
||||
if (!pfl->ro) {
|
||||
uint8_t *p = pfl->storage;
|
||||
memset(p + offset, 0xff, sector_len);
|
||||
pflash_update(pfl, offset, sector_len);
|
||||
}
|
||||
set_dq7(pfl, 0x00);
|
||||
++pfl->sectors_to_erase;
|
||||
set_bit(sector_info.num, pfl->sector_erase_map);
|
||||
/* Set (or reset) the 50 us timer for additional erase commands. */
|
||||
timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 50000);
|
||||
}
|
||||
|
||||
static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned int width)
|
||||
{
|
||||
PFlashCFI02 *pfl = opaque;
|
||||
hwaddr boff;
|
||||
uint8_t *p;
|
||||
uint8_t cmd;
|
||||
|
||||
trace_pflash_io_write(offset, width, width << 1, value, pfl->wcycle);
|
||||
cmd = value;
|
||||
if (pfl->cmd != 0xA0 && cmd == 0xF0) {
|
||||
#if 0
|
||||
DPRINTF("%s: flash reset asked (%02x %02x)\n",
|
||||
__func__, pfl->cmd, cmd);
|
||||
#endif
|
||||
goto reset_flash;
|
||||
if (pfl->cmd != 0xA0) {
|
||||
/* Reset does nothing during chip erase and sector erase. */
|
||||
if (cmd == 0xF0 && pfl->cmd != 0x10 && pfl->cmd != 0x30) {
|
||||
if (pfl->wcycle == WCYCLE_AUTOSELECT_CFI) {
|
||||
/* Return to autoselect mode. */
|
||||
pfl->wcycle = 3;
|
||||
pfl->cmd = 0x90;
|
||||
return;
|
||||
}
|
||||
goto reset_flash;
|
||||
}
|
||||
}
|
||||
trace_pflash_write(offset, value, width, pfl->wcycle);
|
||||
offset &= pfl->chip_len - 1;
|
||||
|
||||
DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__,
|
||||
offset, value, width);
|
||||
boff = offset & (pfl->sector_len - 1);
|
||||
if (pfl->width == 2)
|
||||
boff = offset;
|
||||
if (pfl->width == 2) {
|
||||
boff = boff >> 1;
|
||||
else if (pfl->width == 4)
|
||||
boff = boff >> 2;
|
||||
}
|
||||
/* Only the least-significant 11 bits are used in most cases. */
|
||||
boff &= 0x7FF;
|
||||
switch (pfl->wcycle) {
|
||||
case 0:
|
||||
/* Set the device in I/O access mode if required */
|
||||
@@ -294,12 +459,30 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
/* We're in read mode */
|
||||
check_unlock0:
|
||||
if (boff == 0x55 && cmd == 0x98) {
|
||||
enter_CFI_mode:
|
||||
/* Enter CFI query mode */
|
||||
pfl->wcycle = 7;
|
||||
pfl->wcycle = WCYCLE_CFI;
|
||||
pfl->cmd = 0x98;
|
||||
return;
|
||||
}
|
||||
/* Handle erase resume in erase suspend mode, otherwise reset. */
|
||||
if (cmd == 0x30) { /* Erase Resume */
|
||||
if (pflash_erase_suspend_mode(pfl)) {
|
||||
/* Resume the erase. */
|
||||
timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
pfl->erase_time_remaining);
|
||||
pfl->erase_time_remaining = 0;
|
||||
pfl->wcycle = 6;
|
||||
pfl->cmd = 0x30;
|
||||
set_dq7(pfl, 0x00);
|
||||
assert_dq3(pfl);
|
||||
return;
|
||||
}
|
||||
goto reset_flash;
|
||||
}
|
||||
/* Ignore erase suspend. */
|
||||
if (cmd == 0xB0) { /* Erase Suspend */
|
||||
return;
|
||||
}
|
||||
if (boff != pfl->unlock_addr0 || cmd != 0xAA) {
|
||||
DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n",
|
||||
__func__, boff, cmd, pfl->unlock_addr0);
|
||||
@@ -328,9 +511,9 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
case 0x20:
|
||||
pfl->bypass = 1;
|
||||
goto do_bypass;
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
case 0xA0:
|
||||
case 0x80: /* Erase */
|
||||
case 0x90: /* Autoselect */
|
||||
case 0xA0: /* Program */
|
||||
pfl->cmd = cmd;
|
||||
DPRINTF("%s: starting command %02x\n", __func__, cmd);
|
||||
break;
|
||||
@@ -341,57 +524,54 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
break;
|
||||
case 3:
|
||||
switch (pfl->cmd) {
|
||||
case 0x80:
|
||||
case 0x80: /* Erase */
|
||||
/* We need another unlock sequence */
|
||||
goto check_unlock0;
|
||||
case 0xA0:
|
||||
trace_pflash_data_write(offset, value, width, 0);
|
||||
p = pfl->storage;
|
||||
if (!pfl->ro) {
|
||||
switch (width) {
|
||||
case 1:
|
||||
p[offset] &= value;
|
||||
pflash_update(pfl, offset, 1);
|
||||
break;
|
||||
case 2:
|
||||
if (be) {
|
||||
p[offset] &= value >> 8;
|
||||
p[offset + 1] &= value;
|
||||
} else {
|
||||
p[offset] &= value;
|
||||
p[offset + 1] &= value >> 8;
|
||||
}
|
||||
pflash_update(pfl, offset, 2);
|
||||
break;
|
||||
case 4:
|
||||
if (be) {
|
||||
p[offset] &= value >> 24;
|
||||
p[offset + 1] &= value >> 16;
|
||||
p[offset + 2] &= value >> 8;
|
||||
p[offset + 3] &= value;
|
||||
} else {
|
||||
p[offset] &= value;
|
||||
p[offset + 1] &= value >> 8;
|
||||
p[offset + 2] &= value >> 16;
|
||||
p[offset + 3] &= value >> 24;
|
||||
}
|
||||
pflash_update(pfl, offset, 4);
|
||||
break;
|
||||
case 0xA0: /* Program */
|
||||
if (pflash_erase_suspend_mode(pfl) &&
|
||||
pflash_sector_is_erasing(pfl, offset)) {
|
||||
/* Ignore writes to erasing sectors. */
|
||||
if (pfl->bypass) {
|
||||
goto do_bypass;
|
||||
}
|
||||
goto reset_flash;
|
||||
}
|
||||
pfl->status = 0x00 | ~(value & 0x80);
|
||||
trace_pflash_data_write(offset, width << 1, value, 0);
|
||||
if (!pfl->ro) {
|
||||
p = (uint8_t *)pfl->storage + offset;
|
||||
if (pfl->be) {
|
||||
uint64_t current = ldn_be_p(p, width);
|
||||
stn_be_p(p, width, current & value);
|
||||
} else {
|
||||
uint64_t current = ldn_le_p(p, width);
|
||||
stn_le_p(p, width, current & value);
|
||||
}
|
||||
pflash_update(pfl, offset, width);
|
||||
}
|
||||
/*
|
||||
* While programming, status bit DQ7 should hold the opposite
|
||||
* value from how it was programmed.
|
||||
*/
|
||||
set_dq7(pfl, ~value);
|
||||
/* Let's pretend write is immediate */
|
||||
if (pfl->bypass)
|
||||
goto do_bypass;
|
||||
goto reset_flash;
|
||||
case 0x90:
|
||||
case 0x90: /* Autoselect */
|
||||
if (pfl->bypass && cmd == 0x00) {
|
||||
/* Unlock bypass reset */
|
||||
goto reset_flash;
|
||||
}
|
||||
/* We can enter CFI query mode from autoselect mode */
|
||||
if (boff == 0x55 && cmd == 0x98)
|
||||
goto enter_CFI_mode;
|
||||
/*
|
||||
* We can enter CFI query mode from autoselect mode, but we must
|
||||
* return to autoselect mode after a reset.
|
||||
*/
|
||||
if (boff == 0x55 && cmd == 0x98) {
|
||||
/* Enter autoselect CFI query mode */
|
||||
pfl->wcycle = WCYCLE_AUTOSELECT_CFI;
|
||||
pfl->cmd = 0x98;
|
||||
return;
|
||||
}
|
||||
/* No break here */
|
||||
default:
|
||||
DPRINTF("%s: invalid write for command %02x\n",
|
||||
@@ -400,11 +580,11 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
}
|
||||
case 4:
|
||||
switch (pfl->cmd) {
|
||||
case 0xA0:
|
||||
case 0xA0: /* Program */
|
||||
/* Ignore writes while flash data write is occurring */
|
||||
/* As we suppose write is immediate, this should never happen */
|
||||
return;
|
||||
case 0x80:
|
||||
case 0x80: /* Erase */
|
||||
goto check_unlock1;
|
||||
default:
|
||||
/* Should never happen */
|
||||
@@ -414,8 +594,12 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (pflash_erase_suspend_mode(pfl)) {
|
||||
/* Erasing is not supported in erase suspend mode. */
|
||||
goto reset_flash;
|
||||
}
|
||||
switch (cmd) {
|
||||
case 0x10:
|
||||
case 0x10: /* Chip Erase */
|
||||
if (boff != pfl->unlock_addr0) {
|
||||
DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n",
|
||||
__func__, offset);
|
||||
@@ -424,28 +608,16 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
/* Chip erase */
|
||||
DPRINTF("%s: start chip erase\n", __func__);
|
||||
if (!pfl->ro) {
|
||||
memset(pfl->storage, 0xFF, pfl->chip_len);
|
||||
memset(pfl->storage, 0xff, pfl->chip_len);
|
||||
pflash_update(pfl, 0, pfl->chip_len);
|
||||
}
|
||||
pfl->status = 0x00;
|
||||
/* Let's wait 5 seconds before chip erase is done */
|
||||
set_dq7(pfl, 0x00);
|
||||
/* Wait the time specified at CFI address 0x22. */
|
||||
timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
(NANOSECONDS_PER_SECOND * 5));
|
||||
(1ULL << pfl->cfi_table[0x22]) * SCALE_MS);
|
||||
break;
|
||||
case 0x30:
|
||||
/* Sector erase */
|
||||
p = pfl->storage;
|
||||
offset &= ~(pfl->sector_len - 1);
|
||||
DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__,
|
||||
offset);
|
||||
if (!pfl->ro) {
|
||||
memset(p + offset, 0xFF, pfl->sector_len);
|
||||
pflash_update(pfl, offset, pfl->sector_len);
|
||||
}
|
||||
pfl->status = 0x00;
|
||||
/* Let's wait 1/2 second before sector erase is done */
|
||||
timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
(NANOSECONDS_PER_SECOND / 2));
|
||||
case 0x30: /* Sector erase */
|
||||
pflash_sector_erase(pfl, offset);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
|
||||
@@ -455,11 +627,47 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
break;
|
||||
case 6:
|
||||
switch (pfl->cmd) {
|
||||
case 0x10:
|
||||
case 0x10: /* Chip Erase */
|
||||
/* Ignore writes during chip erase */
|
||||
return;
|
||||
case 0x30:
|
||||
/* Ignore writes during sector erase */
|
||||
case 0x30: /* Sector erase */
|
||||
if (cmd == 0xB0) {
|
||||
/*
|
||||
* If erase suspend happens during the erase timeout (so DQ3 is
|
||||
* 0), then the device suspends erasing immediately. Set the
|
||||
* remaining time to be the total time to erase. Otherwise,
|
||||
* there is a maximum amount of time it can take to enter
|
||||
* suspend mode. Let's ignore that and suspend immediately and
|
||||
* set the remaining time to the actual time remaining on the
|
||||
* timer.
|
||||
*/
|
||||
if ((pfl->status & 0x08) == 0) {
|
||||
pfl->erase_time_remaining = pflash_erase_time(pfl);
|
||||
} else {
|
||||
int64_t delta = timer_expire_time_ns(&pfl->timer) -
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
/* Make sure we have a positive time remaining. */
|
||||
pfl->erase_time_remaining = delta <= 0 ? 1 : delta;
|
||||
}
|
||||
reset_dq3(pfl);
|
||||
timer_del(&pfl->timer);
|
||||
pfl->wcycle = 0;
|
||||
pfl->cmd = 0;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* If DQ3 is 0, additional sector erase commands can be
|
||||
* written and anything else (other than an erase suspend) resets
|
||||
* the device.
|
||||
*/
|
||||
if ((pfl->status & 0x08) == 0) {
|
||||
if (cmd == 0x30) {
|
||||
pflash_sector_erase(pfl, offset);
|
||||
} else {
|
||||
goto reset_flash;
|
||||
}
|
||||
}
|
||||
/* Ignore writes during the actual erase. */
|
||||
return;
|
||||
default:
|
||||
/* Should never happen */
|
||||
@@ -468,7 +676,9 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
goto reset_flash;
|
||||
}
|
||||
break;
|
||||
case 7: /* Special value for CFI queries */
|
||||
/* Special values for CFI queries */
|
||||
case WCYCLE_CFI:
|
||||
case WCYCLE_AUTOSELECT_CFI:
|
||||
DPRINTF("%s: invalid write in CFI query mode\n", __func__);
|
||||
goto reset_flash;
|
||||
default:
|
||||
@@ -493,39 +703,10 @@ static void pflash_write(PFlashCFI02 *pfl, hwaddr offset,
|
||||
pfl->cmd = 0;
|
||||
}
|
||||
|
||||
static uint64_t pflash_be_readfn(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return pflash_read(opaque, addr, size, 1);
|
||||
}
|
||||
|
||||
static void pflash_be_writefn(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
pflash_write(opaque, addr, value, size, 1);
|
||||
}
|
||||
|
||||
static uint64_t pflash_le_readfn(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return pflash_read(opaque, addr, size, 0);
|
||||
}
|
||||
|
||||
static void pflash_le_writefn(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
pflash_write(opaque, addr, value, size, 0);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pflash_cfi02_ops_be = {
|
||||
.read = pflash_be_readfn,
|
||||
.write = pflash_be_writefn,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps pflash_cfi02_ops_le = {
|
||||
.read = pflash_le_readfn,
|
||||
.write = pflash_le_writefn,
|
||||
static const MemoryRegionOps pflash_cfi02_ops = {
|
||||
.read = pflash_read,
|
||||
.write = pflash_write,
|
||||
.impl.max_access_size = 2,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
@@ -534,15 +715,14 @@ static const MemoryRegionOps pflash_cfi02_ops_le = {
|
||||
static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PFlashCFI02 *pfl = PFLASH_CFI02(dev);
|
||||
uint32_t chip_len;
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (pfl->sector_len == 0) {
|
||||
if (pfl->uniform_sector_len == 0 && pfl->sector_len[0] == 0) {
|
||||
error_setg(errp, "attribute \"sector-length\" not specified or zero.");
|
||||
return;
|
||||
}
|
||||
if (pfl->nb_blocs == 0) {
|
||||
if (pfl->uniform_nb_blocs == 0 && pfl->nb_blocs[0] == 0) {
|
||||
error_setg(errp, "attribute \"num-blocks\" not specified or zero.");
|
||||
return;
|
||||
}
|
||||
@@ -551,18 +731,64 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
chip_len = pfl->sector_len * pfl->nb_blocs;
|
||||
int nb_regions;
|
||||
pfl->chip_len = 0;
|
||||
pfl->total_sectors = 0;
|
||||
for (nb_regions = 0; nb_regions < PFLASH_MAX_ERASE_REGIONS; ++nb_regions) {
|
||||
if (pfl->nb_blocs[nb_regions] == 0) {
|
||||
break;
|
||||
}
|
||||
pfl->total_sectors += pfl->nb_blocs[nb_regions];
|
||||
uint64_t sector_len_per_device = pfl->sector_len[nb_regions];
|
||||
|
||||
memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl), pfl->be ?
|
||||
&pflash_cfi02_ops_be : &pflash_cfi02_ops_le,
|
||||
pfl, pfl->name, chip_len, &local_err);
|
||||
/*
|
||||
* The size of each flash sector must be a power of 2 and it must be
|
||||
* aligned at the same power of 2.
|
||||
*/
|
||||
if (sector_len_per_device & 0xff ||
|
||||
sector_len_per_device >= (1 << 24) ||
|
||||
!is_power_of_2(sector_len_per_device))
|
||||
{
|
||||
error_setg(errp, "unsupported configuration: "
|
||||
"sector length[%d] per device = %" PRIx64 ".",
|
||||
nb_regions, sector_len_per_device);
|
||||
return;
|
||||
}
|
||||
if (pfl->chip_len & (sector_len_per_device - 1)) {
|
||||
error_setg(errp, "unsupported configuration: "
|
||||
"flash region %d not correctly aligned.",
|
||||
nb_regions);
|
||||
return;
|
||||
}
|
||||
|
||||
pfl->chip_len += (uint64_t)pfl->sector_len[nb_regions] *
|
||||
pfl->nb_blocs[nb_regions];
|
||||
}
|
||||
|
||||
uint64_t uniform_len = (uint64_t)pfl->uniform_nb_blocs *
|
||||
pfl->uniform_sector_len;
|
||||
if (nb_regions == 0) {
|
||||
nb_regions = 1;
|
||||
pfl->nb_blocs[0] = pfl->uniform_nb_blocs;
|
||||
pfl->sector_len[0] = pfl->uniform_sector_len;
|
||||
pfl->chip_len = uniform_len;
|
||||
pfl->total_sectors = pfl->uniform_nb_blocs;
|
||||
} else if (uniform_len != 0 && uniform_len != pfl->chip_len) {
|
||||
error_setg(errp, "\"num-blocks\"*\"sector-length\" "
|
||||
"different from \"num-blocks0\"*\'sector-length0\" + ... + "
|
||||
"\"num-blocks3\"*\"sector-length3\"");
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl),
|
||||
&pflash_cfi02_ops, pfl, pfl->name,
|
||||
pfl->chip_len, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
|
||||
pfl->chip_len = chip_len;
|
||||
|
||||
if (pfl->blk) {
|
||||
uint64_t perm;
|
||||
@@ -577,13 +803,20 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
if (pfl->blk) {
|
||||
if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, chip_len,
|
||||
errp)) {
|
||||
if (!blk_check_size_and_read_all(pfl->blk, pfl->storage,
|
||||
pfl->chip_len, errp)) {
|
||||
vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only 11 bits are used in the comparison. */
|
||||
pfl->unlock_addr0 &= 0x7FF;
|
||||
pfl->unlock_addr1 &= 0x7FF;
|
||||
|
||||
/* Allocate memory for a bitmap for sectors being erased. */
|
||||
pfl->sector_erase_map = bitmap_new(pfl->total_sectors);
|
||||
|
||||
pflash_setup_mappings(pfl);
|
||||
pfl->rom_mode = 1;
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
|
||||
@@ -592,7 +825,9 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
||||
pfl->wcycle = 0;
|
||||
pfl->cmd = 0;
|
||||
pfl->status = 0;
|
||||
|
||||
/* Hardcoded CFI table (mostly from SG29 Spansion flash) */
|
||||
const uint16_t pri_ofs = 0x40;
|
||||
/* Standard "QRY" string */
|
||||
pfl->cfi_table[0x10] = 'Q';
|
||||
pfl->cfi_table[0x11] = 'R';
|
||||
@@ -601,8 +836,8 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
||||
pfl->cfi_table[0x13] = 0x02;
|
||||
pfl->cfi_table[0x14] = 0x00;
|
||||
/* Primary extended table address */
|
||||
pfl->cfi_table[0x15] = 0x31;
|
||||
pfl->cfi_table[0x16] = 0x00;
|
||||
pfl->cfi_table[0x15] = pri_ofs;
|
||||
pfl->cfi_table[0x16] = pri_ofs >> 8;
|
||||
/* Alternate command set (none) */
|
||||
pfl->cfi_table[0x17] = 0x00;
|
||||
pfl->cfi_table[0x18] = 0x00;
|
||||
@@ -617,7 +852,7 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
||||
pfl->cfi_table[0x1D] = 0x00;
|
||||
/* Vpp max (no Vpp pin) */
|
||||
pfl->cfi_table[0x1E] = 0x00;
|
||||
/* Reserved */
|
||||
/* Timeout per single byte/word write (128 ms) */
|
||||
pfl->cfi_table[0x1F] = 0x07;
|
||||
/* Timeout for min size buffer write (NA) */
|
||||
pfl->cfi_table[0x20] = 0x00;
|
||||
@@ -634,7 +869,7 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
||||
/* Max timeout for chip erase */
|
||||
pfl->cfi_table[0x26] = 0x0D;
|
||||
/* Device size */
|
||||
pfl->cfi_table[0x27] = ctz32(chip_len);
|
||||
pfl->cfi_table[0x27] = ctz32(pfl->chip_len);
|
||||
/* Flash device interface (8 & 16 bits) */
|
||||
pfl->cfi_table[0x28] = 0x02;
|
||||
pfl->cfi_table[0x29] = 0x00;
|
||||
@@ -643,37 +878,60 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
||||
// pfl->cfi_table[0x2A] = 0x05;
|
||||
pfl->cfi_table[0x2A] = 0x00;
|
||||
pfl->cfi_table[0x2B] = 0x00;
|
||||
/* Number of erase block regions (uniform) */
|
||||
pfl->cfi_table[0x2C] = 0x01;
|
||||
/* Erase block region 1 */
|
||||
pfl->cfi_table[0x2D] = pfl->nb_blocs - 1;
|
||||
pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8;
|
||||
pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
|
||||
pfl->cfi_table[0x30] = pfl->sector_len >> 16;
|
||||
/* Number of erase block regions */
|
||||
pfl->cfi_table[0x2c] = nb_regions;
|
||||
/* Erase block regions */
|
||||
for (int i = 0; i < nb_regions; ++i) {
|
||||
uint32_t sector_len_per_device = pfl->sector_len[i];
|
||||
pfl->cfi_table[0x2d + 4 * i] = pfl->nb_blocs[i] - 1;
|
||||
pfl->cfi_table[0x2e + 4 * i] = (pfl->nb_blocs[i] - 1) >> 8;
|
||||
pfl->cfi_table[0x2f + 4 * i] = sector_len_per_device >> 8;
|
||||
pfl->cfi_table[0x30 + 4 * i] = sector_len_per_device >> 16;
|
||||
}
|
||||
assert(0x2c + 4 * nb_regions < pri_ofs);
|
||||
|
||||
/* Extended */
|
||||
pfl->cfi_table[0x31] = 'P';
|
||||
pfl->cfi_table[0x32] = 'R';
|
||||
pfl->cfi_table[0x33] = 'I';
|
||||
pfl->cfi_table[0x00 + pri_ofs] = 'P';
|
||||
pfl->cfi_table[0x01 + pri_ofs] = 'R';
|
||||
pfl->cfi_table[0x02 + pri_ofs] = 'I';
|
||||
|
||||
pfl->cfi_table[0x34] = '1';
|
||||
pfl->cfi_table[0x35] = '0';
|
||||
/* Extended version 1.0 */
|
||||
pfl->cfi_table[0x03 + pri_ofs] = '1';
|
||||
pfl->cfi_table[0x04 + pri_ofs] = '0';
|
||||
|
||||
pfl->cfi_table[0x36] = 0x00;
|
||||
pfl->cfi_table[0x37] = 0x00;
|
||||
pfl->cfi_table[0x38] = 0x00;
|
||||
pfl->cfi_table[0x39] = 0x00;
|
||||
/* Address sensitive unlock required. */
|
||||
pfl->cfi_table[0x05 + pri_ofs] = 0x00;
|
||||
/* Erase suspend to read/write. */
|
||||
pfl->cfi_table[0x06 + pri_ofs] = 0x02;
|
||||
/* Sector protect not supported. */
|
||||
pfl->cfi_table[0x07 + pri_ofs] = 0x00;
|
||||
/* Temporary sector unprotect not supported. */
|
||||
pfl->cfi_table[0x08 + pri_ofs] = 0x00;
|
||||
|
||||
pfl->cfi_table[0x3a] = 0x00;
|
||||
/* Sector protect/unprotect scheme. */
|
||||
pfl->cfi_table[0x09 + pri_ofs] = 0x00;
|
||||
|
||||
pfl->cfi_table[0x3b] = 0x00;
|
||||
pfl->cfi_table[0x3c] = 0x00;
|
||||
/* Simultaneous operation not supported. */
|
||||
pfl->cfi_table[0x0a + pri_ofs] = 0x00;
|
||||
/* Burst mode not supported. */
|
||||
pfl->cfi_table[0x0b + pri_ofs] = 0x00;
|
||||
/* Page mode not supported. */
|
||||
pfl->cfi_table[0x0c + pri_ofs] = 0x00;
|
||||
assert(0x0c + pri_ofs < ARRAY_SIZE(pfl->cfi_table));
|
||||
}
|
||||
|
||||
static Property pflash_cfi02_properties[] = {
|
||||
DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk),
|
||||
DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, nb_blocs, 0),
|
||||
DEFINE_PROP_UINT32("sector-length", PFlashCFI02, sector_len, 0),
|
||||
DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, uniform_nb_blocs, 0),
|
||||
DEFINE_PROP_UINT32("sector-length", PFlashCFI02, uniform_sector_len, 0),
|
||||
DEFINE_PROP_UINT32("num-blocks0", PFlashCFI02, nb_blocs[0], 0),
|
||||
DEFINE_PROP_UINT32("sector-length0", PFlashCFI02, sector_len[0], 0),
|
||||
DEFINE_PROP_UINT32("num-blocks1", PFlashCFI02, nb_blocs[1], 0),
|
||||
DEFINE_PROP_UINT32("sector-length1", PFlashCFI02, sector_len[1], 0),
|
||||
DEFINE_PROP_UINT32("num-blocks2", PFlashCFI02, nb_blocs[2], 0),
|
||||
DEFINE_PROP_UINT32("sector-length2", PFlashCFI02, sector_len[2], 0),
|
||||
DEFINE_PROP_UINT32("num-blocks3", PFlashCFI02, nb_blocs[3], 0),
|
||||
DEFINE_PROP_UINT32("sector-length3", PFlashCFI02, sector_len[3], 0),
|
||||
DEFINE_PROP_UINT8("width", PFlashCFI02, width, 0),
|
||||
DEFINE_PROP_UINT8("mappings", PFlashCFI02, mappings, 0),
|
||||
DEFINE_PROP_UINT8("big-endian", PFlashCFI02, be, 0),
|
||||
@@ -691,6 +949,7 @@ static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PFlashCFI02 *pfl = PFLASH_CFI02(dev);
|
||||
timer_del(&pfl->timer);
|
||||
g_free(pfl->sector_erase_map);
|
||||
}
|
||||
|
||||
static void pflash_cfi02_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
@@ -7,13 +7,11 @@ fdc_ioport_write(uint8_t reg, uint8_t value) "write reg 0x%02x val 0x%02x"
|
||||
# pflash_cfi02.c
|
||||
# pflash_cfi01.c
|
||||
pflash_reset(void) "reset"
|
||||
pflash_read(uint64_t offset, uint8_t cmd, int width, uint8_t wcycle) "offset:0x%04"PRIx64" cmd:0x%02x width:%d wcycle:%u"
|
||||
pflash_write(uint64_t offset, uint32_t value, int width, uint8_t wcycle) "offset:0x%04"PRIx64" value:0x%03x width:%d wcycle:%u"
|
||||
pflash_timer_expired(uint8_t cmd) "command 0x%02x done"
|
||||
pflash_data_read8(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%02x"
|
||||
pflash_data_read16(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%04x"
|
||||
pflash_data_read32(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%08x"
|
||||
pflash_data_write(uint64_t offset, uint32_t value, int width, uint64_t counter) "data offset:0x%04"PRIx64" value:0x%08x width:%d counter:0x%016"PRIx64
|
||||
pflash_io_read(uint64_t offset, int width, int fmt_width, uint32_t value, uint8_t cmd, uint8_t wcycle) "offset:0x%04"PRIx64" width:%d value:0x%0*x cmd:0x%02x wcycle:%u"
|
||||
pflash_io_write(uint64_t offset, int width, int fmt_width, uint32_t value, uint8_t wcycle) "offset:0x%04"PRIx64" width:%d value:0x%0*x wcycle:%u"
|
||||
pflash_data_read(uint64_t offset, int width, uint32_t value) "data offset:0x%04"PRIx64" value:0x%0*x"
|
||||
pflash_data_write(uint64_t offset, int width, uint32_t value, uint64_t counter) "data offset:0x%04"PRIx64" value:0x%0*x counter:0x%016"PRIx64
|
||||
pflash_manufacturer_id(uint16_t id) "Read Manufacturer ID: 0x%04x"
|
||||
pflash_device_id(uint16_t id) "Read Device ID: 0x%04x"
|
||||
pflash_device_info(uint64_t offset) "Read Device Information offset:0x%04"PRIx64
|
||||
|
||||
@@ -130,3 +130,5 @@ config ATI_VGA
|
||||
default y if PCI_DEVICES
|
||||
depends on PCI
|
||||
select VGA
|
||||
select BITBANG_I2C
|
||||
select DDC
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/display/i2c-ddc.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define ATI_DEBUG_HW_CURSOR 0
|
||||
@@ -215,6 +216,24 @@ static void ati_cursor_draw_line(VGACommonState *vga, uint8_t *d, int scr_y)
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ati_i2c(bitbang_i2c_interface *i2c, uint64_t data, int base)
|
||||
{
|
||||
bool c = (data & BIT(base + 17) ? !!(data & BIT(base + 1)) : 1);
|
||||
bool d = (data & BIT(base + 16) ? !!(data & BIT(base)) : 1);
|
||||
|
||||
bitbang_i2c_set(i2c, BITBANG_I2C_SCL, c);
|
||||
d = bitbang_i2c_set(i2c, BITBANG_I2C_SDA, d);
|
||||
|
||||
data &= ~0xf00ULL;
|
||||
if (c) {
|
||||
data |= BIT(base + 9);
|
||||
}
|
||||
if (d) {
|
||||
data |= BIT(base + 8);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline uint64_t ati_reg_read_offs(uint32_t reg, int offs,
|
||||
unsigned int size)
|
||||
{
|
||||
@@ -266,7 +285,16 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
case DAC_CNTL:
|
||||
val = s->regs.dac_cntl;
|
||||
break;
|
||||
/* case GPIO_MONID: FIXME hook up DDC I2C here */
|
||||
case GPIO_VGA_DDC:
|
||||
val = s->regs.gpio_vga_ddc;
|
||||
break;
|
||||
case GPIO_DVI_DDC:
|
||||
val = s->regs.gpio_dvi_ddc;
|
||||
break;
|
||||
case GPIO_MONID ... GPIO_MONID + 3:
|
||||
val = ati_reg_read_offs(s->regs.gpio_monid,
|
||||
addr - GPIO_MONID, size);
|
||||
break;
|
||||
case PALETTE_INDEX:
|
||||
/* FIXME unaligned access */
|
||||
val = vga_ioport_read(&s->vga, VGA_PEL_IR) << 16;
|
||||
@@ -391,9 +419,15 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
break;
|
||||
case DEFAULT_OFFSET:
|
||||
val = s->regs.default_offset;
|
||||
if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
val >>= 10;
|
||||
val |= s->regs.default_pitch << 16;
|
||||
val |= s->regs.default_tile << 30;
|
||||
}
|
||||
break;
|
||||
case DEFAULT_PITCH:
|
||||
val = s->regs.default_pitch;
|
||||
val |= s->regs.default_tile << 16;
|
||||
break;
|
||||
case DEFAULT_SC_BOTTOM_RIGHT:
|
||||
val = s->regs.default_sc_bottom_right;
|
||||
@@ -497,7 +531,33 @@ static void ati_mm_write(void *opaque, hwaddr addr,
|
||||
s->regs.dac_cntl = data & 0xffffe3ff;
|
||||
s->vga.dac_8bit = !!(data & DAC_8BIT_EN);
|
||||
break;
|
||||
/* case GPIO_MONID: FIXME hook up DDC I2C here */
|
||||
case GPIO_VGA_DDC:
|
||||
if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
/* FIXME: Maybe add a property to select VGA or DVI port? */
|
||||
}
|
||||
break;
|
||||
case GPIO_DVI_DDC:
|
||||
if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
s->regs.gpio_dvi_ddc = ati_i2c(s->bbi2c, data, 0);
|
||||
}
|
||||
break;
|
||||
case GPIO_MONID ... GPIO_MONID + 3:
|
||||
/* FIXME What does Radeon have here? */
|
||||
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
ati_reg_write_offs(&s->regs.gpio_monid,
|
||||
addr - GPIO_MONID, data, size);
|
||||
/*
|
||||
* Rage128p accesses DDC used to get EDID via these bits.
|
||||
* Only touch i2c when write overlaps 3rd byte because some
|
||||
* drivers access this reg via multiple partial writes and
|
||||
* without this spurious bits would be sent.
|
||||
*/
|
||||
if ((s->regs.gpio_monid & BIT(25)) &&
|
||||
addr <= GPIO_MONID + 2 && addr + size > GPIO_MONID + 2) {
|
||||
s->regs.gpio_monid = ati_i2c(s->bbi2c, s->regs.gpio_monid, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PALETTE_INDEX ... PALETTE_INDEX + 3:
|
||||
if (size == 4) {
|
||||
vga_ioport_write(&s->vga, VGA_PEL_IR, (data >> 16) & 0xff);
|
||||
@@ -628,22 +688,22 @@ static void ati_mm_write(void *opaque, hwaddr addr,
|
||||
break;
|
||||
case SRC_PITCH_OFFSET:
|
||||
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
s->regs.src_offset = (data & 0x1fffff) << 5;
|
||||
s->regs.src_pitch = (data >> 21) & 0x3ff;
|
||||
s->regs.src_offset = (data & 0x1fffff) << 4;
|
||||
s->regs.src_pitch = (data & 0x7fe00000) >> 21;
|
||||
s->regs.src_tile = data >> 31;
|
||||
} else {
|
||||
s->regs.src_offset = (data & 0x3fffff) << 11;
|
||||
s->regs.src_offset = (data & 0x3fffff) << 10;
|
||||
s->regs.src_pitch = (data & 0x3fc00000) >> 16;
|
||||
s->regs.src_tile = (data >> 30) & 1;
|
||||
}
|
||||
break;
|
||||
case DST_PITCH_OFFSET:
|
||||
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
s->regs.dst_offset = (data & 0x1fffff) << 5;
|
||||
s->regs.dst_pitch = (data >> 21) & 0x3ff;
|
||||
s->regs.dst_offset = (data & 0x1fffff) << 4;
|
||||
s->regs.dst_pitch = (data & 0x7fe00000) >> 21;
|
||||
s->regs.dst_tile = data >> 31;
|
||||
} else {
|
||||
s->regs.dst_offset = (data & 0x3fffff) << 11;
|
||||
s->regs.dst_offset = (data & 0x3fffff) << 10;
|
||||
s->regs.dst_pitch = (data & 0x3fc00000) >> 16;
|
||||
s->regs.dst_tile = data >> 30;
|
||||
}
|
||||
@@ -723,13 +783,19 @@ static void ati_mm_write(void *opaque, hwaddr addr,
|
||||
s->regs.dp_write_mask = data;
|
||||
break;
|
||||
case DEFAULT_OFFSET:
|
||||
data &= (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF ?
|
||||
0x03fffc00 : 0xfffffc00);
|
||||
s->regs.default_offset = data;
|
||||
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
s->regs.default_offset = data & 0xfffffff0;
|
||||
} else {
|
||||
/* Radeon has DEFAULT_PITCH_OFFSET here like DST_PITCH_OFFSET */
|
||||
s->regs.default_offset = (data & 0x3fffff) << 10;
|
||||
s->regs.default_pitch = (data & 0x3fc00000) >> 16;
|
||||
s->regs.default_tile = data >> 30;
|
||||
}
|
||||
break;
|
||||
case DEFAULT_PITCH:
|
||||
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
s->regs.default_pitch = data & 0x103ff;
|
||||
s->regs.default_pitch = data & 0x3fff;
|
||||
s->regs.default_tile = (data >> 16) & 1;
|
||||
}
|
||||
break;
|
||||
case DEFAULT_SC_BOTTOM_RIGHT:
|
||||
@@ -788,6 +854,12 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp)
|
||||
vga->cursor_draw_line = ati_cursor_draw_line;
|
||||
}
|
||||
|
||||
/* ddc, edid */
|
||||
I2CBus *i2cbus = i2c_init_bus(DEVICE(s), "ati-vga.ddc");
|
||||
s->bbi2c = bitbang_i2c_init(i2cbus);
|
||||
I2CSlave *i2cddc = I2C_SLAVE(qdev_create(BUS(i2cbus), TYPE_I2CDDC));
|
||||
i2c_set_slave_address(i2cddc, 0x50);
|
||||
|
||||
/* mmio register space */
|
||||
memory_region_init_io(&s->mm, OBJECT(s), &ati_mm_ops, s,
|
||||
"ati.mmregs", 0x4000);
|
||||
@@ -813,6 +885,7 @@ static void ati_vga_exit(PCIDevice *dev)
|
||||
ATIVGAState *s = ATI_VGA(dev);
|
||||
|
||||
graphic_console_close(s->vga.con);
|
||||
g_free(s->bbi2c);
|
||||
}
|
||||
|
||||
static Property ati_vga_properties[] = {
|
||||
@@ -837,7 +910,7 @@ static void ati_vga_class_init(ObjectClass *klass, void *data)
|
||||
k->class_id = PCI_CLASS_DISPLAY_VGA;
|
||||
k->vendor_id = PCI_VENDOR_ID_ATI;
|
||||
k->device_id = PCI_DEVICE_ID_ATI_RAGE128_PF;
|
||||
k->romfile = "vgabios-stdvga.bin";
|
||||
k->romfile = "vgabios-ati.bin";
|
||||
k->realize = ati_vga_realize;
|
||||
k->exit = ati_vga_exit;
|
||||
}
|
||||
|
||||
@@ -51,8 +51,9 @@ void ati_2d_blt(ATIVGAState *s)
|
||||
s->vga.vbe_start_addr, surface_data(ds), surface_stride(ds),
|
||||
surface_bits_per_pixel(ds),
|
||||
(s->regs.dp_mix & GMC_ROP3_MASK) >> 16);
|
||||
DPRINTF("%d %d, %d %d, (%d,%d) -> (%d,%d) %dx%d\n", s->regs.src_offset,
|
||||
s->regs.dst_offset, s->regs.src_pitch, s->regs.dst_pitch,
|
||||
DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d\n",
|
||||
s->regs.src_offset, s->regs.dst_offset, s->regs.default_offset,
|
||||
s->regs.src_pitch, s->regs.dst_pitch, s->regs.default_pitch,
|
||||
s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y,
|
||||
s->regs.dst_width, s->regs.dst_height);
|
||||
switch (s->regs.dp_mix & GMC_ROP3_MASK) {
|
||||
@@ -60,10 +61,16 @@ void ati_2d_blt(ATIVGAState *s)
|
||||
{
|
||||
uint8_t *src_bits, *dst_bits, *end;
|
||||
int src_stride, dst_stride, bpp = ati_bpp_from_datatype(s);
|
||||
src_bits = s->vga.vram_ptr + s->regs.src_offset;
|
||||
dst_bits = s->vga.vram_ptr + s->regs.dst_offset;
|
||||
src_stride = s->regs.src_pitch;
|
||||
dst_stride = s->regs.dst_pitch;
|
||||
src_bits = s->vga.vram_ptr +
|
||||
(s->regs.dp_gui_master_cntl & GMC_SRC_PITCH_OFFSET_CNTL ?
|
||||
s->regs.src_offset : s->regs.default_offset);
|
||||
dst_bits = s->vga.vram_ptr +
|
||||
(s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL ?
|
||||
s->regs.dst_offset : s->regs.default_offset);
|
||||
src_stride = (s->regs.dp_gui_master_cntl & GMC_SRC_PITCH_OFFSET_CNTL ?
|
||||
s->regs.src_pitch : s->regs.default_pitch);
|
||||
dst_stride = (s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL ?
|
||||
s->regs.dst_pitch : s->regs.default_pitch);
|
||||
|
||||
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
src_bits += s->regs.crtc_offset & 0x07ffffff;
|
||||
@@ -111,8 +118,11 @@ void ati_2d_blt(ATIVGAState *s)
|
||||
uint8_t *dst_bits, *end;
|
||||
int dst_stride, bpp = ati_bpp_from_datatype(s);
|
||||
uint32_t filler = 0;
|
||||
dst_bits = s->vga.vram_ptr + s->regs.dst_offset;
|
||||
dst_stride = s->regs.dst_pitch;
|
||||
dst_bits = s->vga.vram_ptr +
|
||||
(s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL ?
|
||||
s->regs.dst_offset : s->regs.default_offset);
|
||||
dst_stride = (s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL ?
|
||||
s->regs.dst_pitch : s->regs.default_pitch);
|
||||
|
||||
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
|
||||
dst_bits += s->regs.crtc_offset & 0x07ffffff;
|
||||
|
||||
@@ -19,6 +19,8 @@ static struct ati_regdesc ati_reg_names[] = {
|
||||
{"CRTC_GEN_CNTL", 0x0050},
|
||||
{"CRTC_EXT_CNTL", 0x0054},
|
||||
{"DAC_CNTL", 0x0058},
|
||||
{"GPIO_VGA_DDC", 0x0060},
|
||||
{"GPIO_DVI_DDC", 0x0064},
|
||||
{"GPIO_MONID", 0x0068},
|
||||
{"I2C_CNTL_1", 0x0094},
|
||||
{"PALETTE_INDEX", 0x00b0},
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define ATI_INT_H
|
||||
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/i2c/bitbang_i2c.h"
|
||||
#include "vga_int.h"
|
||||
|
||||
/*#define DEBUG_ATI*/
|
||||
@@ -35,6 +36,9 @@ typedef struct ATIVGARegs {
|
||||
uint32_t crtc_gen_cntl;
|
||||
uint32_t crtc_ext_cntl;
|
||||
uint32_t dac_cntl;
|
||||
uint32_t gpio_vga_ddc;
|
||||
uint32_t gpio_dvi_ddc;
|
||||
uint32_t gpio_monid;
|
||||
uint32_t crtc_h_total_disp;
|
||||
uint32_t crtc_h_sync_strt_wid;
|
||||
uint32_t crtc_v_total_disp;
|
||||
@@ -70,6 +74,7 @@ typedef struct ATIVGARegs {
|
||||
uint32_t dp_write_mask;
|
||||
uint32_t default_offset;
|
||||
uint32_t default_pitch;
|
||||
uint32_t default_tile;
|
||||
uint32_t default_sc_bottom_right;
|
||||
} ATIVGARegs;
|
||||
|
||||
@@ -83,6 +88,7 @@ typedef struct ATIVGAState {
|
||||
uint16_t cursor_size;
|
||||
uint32_t cursor_offset;
|
||||
QEMUCursor *cursor;
|
||||
bitbang_i2c_interface *bbi2c;
|
||||
MemoryRegion io;
|
||||
MemoryRegion mm;
|
||||
ATIVGARegs regs;
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#define CRTC_GEN_CNTL 0x0050
|
||||
#define CRTC_EXT_CNTL 0x0054
|
||||
#define DAC_CNTL 0x0058
|
||||
#define GPIO_VGA_DDC 0x0060
|
||||
#define GPIO_DVI_DDC 0x0064
|
||||
#define GPIO_MONID 0x0068
|
||||
#define I2C_CNTL_1 0x0094
|
||||
#define PALETTE_INDEX 0x00b0
|
||||
@@ -368,8 +370,8 @@
|
||||
#define BRUSH_SOLIDCOLOR 0x00000d00
|
||||
|
||||
/* DP_GUI_MASTER_CNTL bit constants */
|
||||
#define GMC_SRC_PITCH_OFFSET_DEFAULT 0x00000000
|
||||
#define GMC_DST_PITCH_OFFSET_DEFAULT 0x00000000
|
||||
#define GMC_SRC_PITCH_OFFSET_CNTL 0x00000001
|
||||
#define GMC_DST_PITCH_OFFSET_CNTL 0x00000002
|
||||
#define GMC_SRC_CLIP_DEFAULT 0x00000000
|
||||
#define GMC_DST_CLIP_DEFAULT 0x00000000
|
||||
#define GMC_BRUSH_SOLIDCOLOR 0x000000d0
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "bitbang_i2c.h"
|
||||
#include "hw/i2c/bitbang_i2c.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "cpu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i2c/ppc4xx_i2c.h"
|
||||
#include "bitbang_i2c.h"
|
||||
|
||||
#define PPC4xx_I2C_MEM_SIZE 18
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "bitbang_i2c.h"
|
||||
#include "hw/i2c/bitbang_i2c.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
|
||||
@@ -104,54 +104,63 @@ static void aspeed_vic_set_irq(void *opaque, int irq, int level)
|
||||
|
||||
static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
uint64_t val;
|
||||
const bool high = !!(offset & 0x4);
|
||||
hwaddr n_offset = (offset & ~0x4);
|
||||
AspeedVICState *s = (AspeedVICState *)opaque;
|
||||
hwaddr n_offset;
|
||||
uint64_t val;
|
||||
bool high;
|
||||
|
||||
if (offset < AVIC_NEW_BASE_OFFSET) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers "
|
||||
"at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size);
|
||||
return 0;
|
||||
high = false;
|
||||
n_offset = offset;
|
||||
} else {
|
||||
high = !!(offset & 0x4);
|
||||
n_offset = (offset & ~0x4);
|
||||
}
|
||||
|
||||
n_offset -= AVIC_NEW_BASE_OFFSET;
|
||||
|
||||
switch (n_offset) {
|
||||
case 0x0: /* IRQ Status */
|
||||
case 0x80: /* IRQ Status */
|
||||
case 0x00:
|
||||
val = s->raw & ~s->select & s->enable;
|
||||
break;
|
||||
case 0x08: /* FIQ Status */
|
||||
case 0x88: /* FIQ Status */
|
||||
case 0x04:
|
||||
val = s->raw & s->select & s->enable;
|
||||
break;
|
||||
case 0x10: /* Raw Interrupt Status */
|
||||
case 0x90: /* Raw Interrupt Status */
|
||||
case 0x08:
|
||||
val = s->raw;
|
||||
break;
|
||||
case 0x18: /* Interrupt Selection */
|
||||
case 0x98: /* Interrupt Selection */
|
||||
case 0x0c:
|
||||
val = s->select;
|
||||
break;
|
||||
case 0x20: /* Interrupt Enable */
|
||||
case 0xa0: /* Interrupt Enable */
|
||||
case 0x10:
|
||||
val = s->enable;
|
||||
break;
|
||||
case 0x30: /* Software Interrupt */
|
||||
case 0xb0: /* Software Interrupt */
|
||||
case 0x18:
|
||||
val = s->trigger;
|
||||
break;
|
||||
case 0x40: /* Interrupt Sensitivity */
|
||||
case 0xc0: /* Interrupt Sensitivity */
|
||||
case 0x24:
|
||||
val = s->sense;
|
||||
break;
|
||||
case 0x48: /* Interrupt Both Edge Trigger Control */
|
||||
case 0xc8: /* Interrupt Both Edge Trigger Control */
|
||||
case 0x28:
|
||||
val = s->dual_edge;
|
||||
break;
|
||||
case 0x50: /* Interrupt Event */
|
||||
case 0xd0: /* Interrupt Event */
|
||||
case 0x2c:
|
||||
val = s->event;
|
||||
break;
|
||||
case 0x60: /* Edge Triggered Interrupt Status */
|
||||
case 0xe0: /* Edge Triggered Interrupt Status */
|
||||
val = s->raw & ~s->sense;
|
||||
break;
|
||||
/* Illegal */
|
||||
case 0x28: /* Interrupt Enable Clear */
|
||||
case 0x38: /* Software Interrupt Clear */
|
||||
case 0x58: /* Edge Triggered Interrupt Clear */
|
||||
case 0xa8: /* Interrupt Enable Clear */
|
||||
case 0xb8: /* Software Interrupt Clear */
|
||||
case 0xd8: /* Edge Triggered Interrupt Clear */
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Read of write-only register with offset 0x%"
|
||||
HWADDR_PRIx "\n", __func__, offset);
|
||||
@@ -166,6 +175,8 @@ static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
|
||||
}
|
||||
if (high) {
|
||||
val = extract64(val, 32, 19);
|
||||
} else {
|
||||
val = extract64(val, 0, 32);
|
||||
}
|
||||
trace_aspeed_vic_read(offset, size, val);
|
||||
return val;
|
||||
@@ -174,19 +185,18 @@ static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
|
||||
static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
const bool high = !!(offset & 0x4);
|
||||
hwaddr n_offset = (offset & ~0x4);
|
||||
AspeedVICState *s = (AspeedVICState *)opaque;
|
||||
hwaddr n_offset;
|
||||
bool high;
|
||||
|
||||
if (offset < AVIC_NEW_BASE_OFFSET) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Ignoring write to legacy registers at 0x%"
|
||||
HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset,
|
||||
size, data);
|
||||
return;
|
||||
high = false;
|
||||
n_offset = offset;
|
||||
} else {
|
||||
high = !!(offset & 0x4);
|
||||
n_offset = (offset & ~0x4);
|
||||
}
|
||||
|
||||
n_offset -= AVIC_NEW_BASE_OFFSET;
|
||||
trace_aspeed_vic_write(offset, size, data);
|
||||
|
||||
/* Given we have members using separate enable/clear registers, deposit64()
|
||||
@@ -201,7 +211,8 @@ static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
|
||||
}
|
||||
|
||||
switch (n_offset) {
|
||||
case 0x18: /* Interrupt Selection */
|
||||
case 0x98: /* Interrupt Selection */
|
||||
case 0x0c:
|
||||
/* Register has deposit64() semantics - overwrite requested 32 bits */
|
||||
if (high) {
|
||||
s->select &= AVIC_L_MASK;
|
||||
@@ -210,21 +221,25 @@ static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
|
||||
}
|
||||
s->select |= data;
|
||||
break;
|
||||
case 0x20: /* Interrupt Enable */
|
||||
case 0xa0: /* Interrupt Enable */
|
||||
case 0x10:
|
||||
s->enable |= data;
|
||||
break;
|
||||
case 0x28: /* Interrupt Enable Clear */
|
||||
case 0xa8: /* Interrupt Enable Clear */
|
||||
case 0x14:
|
||||
s->enable &= ~data;
|
||||
break;
|
||||
case 0x30: /* Software Interrupt */
|
||||
case 0xb0: /* Software Interrupt */
|
||||
case 0x18:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
|
||||
"IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
|
||||
break;
|
||||
case 0x38: /* Software Interrupt Clear */
|
||||
case 0xb8: /* Software Interrupt Clear */
|
||||
case 0x1c:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
|
||||
"IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
|
||||
break;
|
||||
case 0x50: /* Interrupt Event */
|
||||
case 0xd0: /* Interrupt Event */
|
||||
/* Register has deposit64() semantics - overwrite the top four valid
|
||||
* IRQ bits, as only the top four IRQs (GPIOs) can change their event
|
||||
* type */
|
||||
@@ -236,15 +251,21 @@ static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
|
||||
"Ignoring invalid write to interrupt event register");
|
||||
}
|
||||
break;
|
||||
case 0x58: /* Edge Triggered Interrupt Clear */
|
||||
case 0xd8: /* Edge Triggered Interrupt Clear */
|
||||
case 0x38:
|
||||
s->raw &= ~(data & ~s->sense);
|
||||
break;
|
||||
case 0x00: /* IRQ Status */
|
||||
case 0x08: /* FIQ Status */
|
||||
case 0x10: /* Raw Interrupt Status */
|
||||
case 0x40: /* Interrupt Sensitivity */
|
||||
case 0x48: /* Interrupt Both Edge Trigger Control */
|
||||
case 0x60: /* Edge Triggered Interrupt Status */
|
||||
case 0x80: /* IRQ Status */
|
||||
case 0x00:
|
||||
case 0x88: /* FIQ Status */
|
||||
case 0x04:
|
||||
case 0x90: /* Raw Interrupt Status */
|
||||
case 0x08:
|
||||
case 0xc0: /* Interrupt Sensitivity */
|
||||
case 0x24:
|
||||
case 0xc8: /* Interrupt Both Edge Trigger Control */
|
||||
case 0x28:
|
||||
case 0xe0: /* Edge Triggered Interrupt Status */
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Write of read-only register with offset 0x%"
|
||||
HWADDR_PRIx "\n", __func__, offset);
|
||||
|
||||
@@ -169,7 +169,7 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type,
|
||||
vsd = ldq_be_dma(&address_space_memory, vsd_addr);
|
||||
|
||||
if (!(vsd & VSD_ADDRESS_MASK)) {
|
||||
xive_error(xive, "VST: invalid %s entry %x !?", info->name, 0);
|
||||
xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type,
|
||||
vsd = ldq_be_dma(&address_space_memory, vsd_addr);
|
||||
|
||||
if (!(vsd & VSD_ADDRESS_MASK)) {
|
||||
xive_error(xive, "VST: invalid %s entry %x !?", info->name, 0);
|
||||
xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -294,8 +294,12 @@ static int pnv_xive_write_end(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
|
||||
word_number);
|
||||
}
|
||||
|
||||
static int pnv_xive_end_update(PnvXive *xive, uint8_t blk, uint32_t idx)
|
||||
static int pnv_xive_end_update(PnvXive *xive)
|
||||
{
|
||||
uint8_t blk = GETFIELD(VC_EQC_CWATCH_BLOCKID,
|
||||
xive->regs[(VC_EQC_CWATCH_SPEC >> 3)]);
|
||||
uint32_t idx = GETFIELD(VC_EQC_CWATCH_OFFSET,
|
||||
xive->regs[(VC_EQC_CWATCH_SPEC >> 3)]);
|
||||
int i;
|
||||
uint64_t eqc_watch[4];
|
||||
|
||||
@@ -307,6 +311,24 @@ static int pnv_xive_end_update(PnvXive *xive, uint8_t blk, uint32_t idx)
|
||||
XIVE_VST_WORD_ALL);
|
||||
}
|
||||
|
||||
static void pnv_xive_end_cache_load(PnvXive *xive)
|
||||
{
|
||||
uint8_t blk = GETFIELD(VC_EQC_CWATCH_BLOCKID,
|
||||
xive->regs[(VC_EQC_CWATCH_SPEC >> 3)]);
|
||||
uint32_t idx = GETFIELD(VC_EQC_CWATCH_OFFSET,
|
||||
xive->regs[(VC_EQC_CWATCH_SPEC >> 3)]);
|
||||
uint64_t eqc_watch[4] = { 0 };
|
||||
int i;
|
||||
|
||||
if (pnv_xive_vst_read(xive, VST_TSEL_EQDT, blk, idx, eqc_watch)) {
|
||||
xive_error(xive, "VST: no END entry %x/%x !?", blk, idx);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eqc_watch); i++) {
|
||||
xive->regs[(VC_EQC_CWATCH_DAT0 >> 3) + i] = be64_to_cpu(eqc_watch[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int pnv_xive_get_nvt(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
|
||||
XiveNVT *nvt)
|
||||
{
|
||||
@@ -320,8 +342,12 @@ static int pnv_xive_write_nvt(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
|
||||
word_number);
|
||||
}
|
||||
|
||||
static int pnv_xive_nvt_update(PnvXive *xive, uint8_t blk, uint32_t idx)
|
||||
static int pnv_xive_nvt_update(PnvXive *xive)
|
||||
{
|
||||
uint8_t blk = GETFIELD(PC_VPC_CWATCH_BLOCKID,
|
||||
xive->regs[(PC_VPC_CWATCH_SPEC >> 3)]);
|
||||
uint32_t idx = GETFIELD(PC_VPC_CWATCH_OFFSET,
|
||||
xive->regs[(PC_VPC_CWATCH_SPEC >> 3)]);
|
||||
int i;
|
||||
uint64_t vpc_watch[8];
|
||||
|
||||
@@ -333,6 +359,24 @@ static int pnv_xive_nvt_update(PnvXive *xive, uint8_t blk, uint32_t idx)
|
||||
XIVE_VST_WORD_ALL);
|
||||
}
|
||||
|
||||
static void pnv_xive_nvt_cache_load(PnvXive *xive)
|
||||
{
|
||||
uint8_t blk = GETFIELD(PC_VPC_CWATCH_BLOCKID,
|
||||
xive->regs[(PC_VPC_CWATCH_SPEC >> 3)]);
|
||||
uint32_t idx = GETFIELD(PC_VPC_CWATCH_OFFSET,
|
||||
xive->regs[(PC_VPC_CWATCH_SPEC >> 3)]);
|
||||
uint64_t vpc_watch[8] = { 0 };
|
||||
int i;
|
||||
|
||||
if (pnv_xive_vst_read(xive, VST_TSEL_VPDT, blk, idx, vpc_watch)) {
|
||||
xive_error(xive, "VST: no NVT entry %x/%x !?", blk, idx);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vpc_watch); i++) {
|
||||
xive->regs[(PC_VPC_CWATCH_DAT0 >> 3) + i] = be64_to_cpu(vpc_watch[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
|
||||
XiveEAS *eas)
|
||||
{
|
||||
@@ -346,12 +390,6 @@ static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
|
||||
return pnv_xive_vst_read(xive, VST_TSEL_IVT, blk, idx, eas);
|
||||
}
|
||||
|
||||
static int pnv_xive_eas_update(PnvXive *xive, uint8_t blk, uint32_t idx)
|
||||
{
|
||||
/* All done. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static XiveTCTX *pnv_xive_get_tctx(XiveRouter *xrtr, CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
@@ -781,8 +819,7 @@ static void pnv_xive_ic_reg_write(void *opaque, hwaddr offset,
|
||||
* support recently though)
|
||||
*/
|
||||
if (val & (VC_SBC_CONF_CPLX_CIST | VC_SBC_CONF_CIST_BOTH)) {
|
||||
object_property_set_int(OBJECT(&xive->ipi_source),
|
||||
XIVE_SRC_STORE_EOI, "flags", &error_fatal);
|
||||
xive->ipi_source.esb_flags |= XIVE_SRC_STORE_EOI;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -951,28 +988,43 @@ static void pnv_xive_ic_reg_write(void *opaque, hwaddr offset,
|
||||
* XIVE PC & VC cache updates for EAS, NVT and END
|
||||
*/
|
||||
case VC_IVC_SCRUB_MASK:
|
||||
break;
|
||||
case VC_IVC_SCRUB_TRIG:
|
||||
pnv_xive_eas_update(xive, GETFIELD(PC_SCRUB_BLOCK_ID, val),
|
||||
GETFIELD(VC_SCRUB_OFFSET, val));
|
||||
break;
|
||||
|
||||
case VC_EQC_SCRUB_MASK:
|
||||
case VC_EQC_CWATCH_SPEC:
|
||||
case VC_EQC_CWATCH_DAT0 ... VC_EQC_CWATCH_DAT3:
|
||||
val &= ~VC_EQC_CWATCH_CONFLICT; /* HW resets this bit */
|
||||
break;
|
||||
case VC_EQC_CWATCH_DAT1 ... VC_EQC_CWATCH_DAT3:
|
||||
break;
|
||||
case VC_EQC_CWATCH_DAT0:
|
||||
/* writing to DATA0 triggers the cache write */
|
||||
xive->regs[reg] = val;
|
||||
pnv_xive_end_update(xive);
|
||||
break;
|
||||
case VC_EQC_SCRUB_MASK:
|
||||
case VC_EQC_SCRUB_TRIG:
|
||||
pnv_xive_end_update(xive, GETFIELD(VC_SCRUB_BLOCK_ID, val),
|
||||
GETFIELD(VC_SCRUB_OFFSET, val));
|
||||
/*
|
||||
* The scrubbing registers flush the cache in RAM and can also
|
||||
* invalidate.
|
||||
*/
|
||||
break;
|
||||
|
||||
case PC_VPC_SCRUB_MASK:
|
||||
case PC_VPC_CWATCH_SPEC:
|
||||
case PC_VPC_CWATCH_DAT0 ... PC_VPC_CWATCH_DAT7:
|
||||
val &= ~PC_VPC_CWATCH_CONFLICT; /* HW resets this bit */
|
||||
break;
|
||||
case PC_VPC_CWATCH_DAT1 ... PC_VPC_CWATCH_DAT7:
|
||||
break;
|
||||
case PC_VPC_CWATCH_DAT0:
|
||||
/* writing to DATA0 triggers the cache write */
|
||||
xive->regs[reg] = val;
|
||||
pnv_xive_nvt_update(xive);
|
||||
break;
|
||||
case PC_VPC_SCRUB_MASK:
|
||||
case PC_VPC_SCRUB_TRIG:
|
||||
pnv_xive_nvt_update(xive, GETFIELD(PC_SCRUB_BLOCK_ID, val),
|
||||
GETFIELD(PC_SCRUB_OFFSET, val));
|
||||
/*
|
||||
* The scrubbing registers flush the cache in RAM and can also
|
||||
* invalidate.
|
||||
*/
|
||||
break;
|
||||
|
||||
|
||||
@@ -1023,15 +1075,6 @@ static uint64_t pnv_xive_ic_reg_read(void *opaque, hwaddr offset, unsigned size)
|
||||
case PC_GLOBAL_CONFIG:
|
||||
|
||||
case PC_VPC_SCRUB_MASK:
|
||||
case PC_VPC_CWATCH_SPEC:
|
||||
case PC_VPC_CWATCH_DAT0:
|
||||
case PC_VPC_CWATCH_DAT1:
|
||||
case PC_VPC_CWATCH_DAT2:
|
||||
case PC_VPC_CWATCH_DAT3:
|
||||
case PC_VPC_CWATCH_DAT4:
|
||||
case PC_VPC_CWATCH_DAT5:
|
||||
case PC_VPC_CWATCH_DAT6:
|
||||
case PC_VPC_CWATCH_DAT7:
|
||||
|
||||
case VC_GLOBAL_CONFIG:
|
||||
case VC_AIB_TX_ORDER_TAG2:
|
||||
@@ -1044,12 +1087,6 @@ static uint64_t pnv_xive_ic_reg_read(void *opaque, hwaddr offset, unsigned size)
|
||||
case VC_IRQ_CONFIG_IPI_CASC:
|
||||
|
||||
case VC_EQC_SCRUB_MASK:
|
||||
case VC_EQC_CWATCH_DAT0:
|
||||
case VC_EQC_CWATCH_DAT1:
|
||||
case VC_EQC_CWATCH_DAT2:
|
||||
case VC_EQC_CWATCH_DAT3:
|
||||
|
||||
case VC_EQC_CWATCH_SPEC:
|
||||
case VC_IVC_SCRUB_MASK:
|
||||
case VC_SBC_CONFIG:
|
||||
case VC_AT_MACRO_KILL_MASK:
|
||||
@@ -1081,6 +1118,38 @@ static uint64_t pnv_xive_ic_reg_read(void *opaque, hwaddr offset, unsigned size)
|
||||
/*
|
||||
* XIVE PC & VC cache updates for EAS, NVT and END
|
||||
*/
|
||||
case VC_EQC_CWATCH_SPEC:
|
||||
xive->regs[reg] = ~(VC_EQC_CWATCH_FULL | VC_EQC_CWATCH_CONFLICT);
|
||||
val = xive->regs[reg];
|
||||
break;
|
||||
case VC_EQC_CWATCH_DAT0:
|
||||
/*
|
||||
* Load DATA registers from cache with data requested by the
|
||||
* SPEC register
|
||||
*/
|
||||
pnv_xive_end_cache_load(xive);
|
||||
val = xive->regs[reg];
|
||||
break;
|
||||
case VC_EQC_CWATCH_DAT1 ... VC_EQC_CWATCH_DAT3:
|
||||
val = xive->regs[reg];
|
||||
break;
|
||||
|
||||
case PC_VPC_CWATCH_SPEC:
|
||||
xive->regs[reg] = ~(PC_VPC_CWATCH_FULL | PC_VPC_CWATCH_CONFLICT);
|
||||
val = xive->regs[reg];
|
||||
break;
|
||||
case PC_VPC_CWATCH_DAT0:
|
||||
/*
|
||||
* Load DATA registers from cache with data requested by the
|
||||
* SPEC register
|
||||
*/
|
||||
pnv_xive_nvt_cache_load(xive);
|
||||
val = xive->regs[reg];
|
||||
break;
|
||||
case PC_VPC_CWATCH_DAT1 ... PC_VPC_CWATCH_DAT7:
|
||||
val = xive->regs[reg];
|
||||
break;
|
||||
|
||||
case PC_VPC_SCRUB_TRIG:
|
||||
case VC_IVC_SCRUB_TRIG:
|
||||
case VC_EQC_SCRUB_TRIG:
|
||||
|
||||
@@ -194,13 +194,6 @@ void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon)
|
||||
}
|
||||
}
|
||||
|
||||
void spapr_xive_map_mmio(SpaprXive *xive)
|
||||
{
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 2, xive->tm_base);
|
||||
}
|
||||
|
||||
void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable)
|
||||
{
|
||||
memory_region_set_enabled(&xive->source.esb_mmio, enable);
|
||||
@@ -305,6 +298,7 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio);
|
||||
|
||||
/*
|
||||
* Initialize the END ESB source
|
||||
@@ -318,6 +312,7 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio);
|
||||
|
||||
/* Set the mapping address of the END ESB pages after the source ESBs */
|
||||
xive->end_base = xive->vc_base + (1ull << xsrc->esb_shift) * xsrc->nr_irqs;
|
||||
@@ -333,31 +328,18 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
qemu_register_reset(spapr_xive_reset, dev);
|
||||
|
||||
/* Define all XIVE MMIO regions on SysBus */
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio);
|
||||
}
|
||||
|
||||
void spapr_xive_init(SpaprXive *xive, Error **errp)
|
||||
{
|
||||
XiveSource *xsrc = &xive->source;
|
||||
|
||||
/*
|
||||
* The emulated XIVE device can only be initialized once. If the
|
||||
* ESB memory region has been already mapped, it means we have been
|
||||
* through there.
|
||||
*/
|
||||
if (memory_region_is_mapped(&xsrc->esb_mmio)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TIMA initialization */
|
||||
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive,
|
||||
"xive.tima", 4ull << TM_SHIFT);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio);
|
||||
|
||||
/* Map all regions */
|
||||
spapr_xive_map_mmio(xive);
|
||||
/*
|
||||
* Map all regions. These will be enabled or disabled at reset and
|
||||
* can also be overridden by KVM memory regions if active
|
||||
*/
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 2, xive->tm_base);
|
||||
}
|
||||
|
||||
static int spapr_xive_get_eas(XiveRouter *xrtr, uint8_t eas_blk,
|
||||
|
||||
@@ -724,12 +724,13 @@ void kvmppc_xive_connect(SpaprXive *xive, Error **errp)
|
||||
xsrc->esb_mmap = kvmppc_xive_mmap(xive, KVM_XIVE_ESB_PAGE_OFFSET, esb_len,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memory_region_init_ram_device_ptr(&xsrc->esb_mmio, OBJECT(xsrc),
|
||||
memory_region_init_ram_device_ptr(&xsrc->esb_mmio_kvm, OBJECT(xsrc),
|
||||
"xive.esb", esb_len, xsrc->esb_mmap);
|
||||
memory_region_add_subregion_overlap(&xsrc->esb_mmio, 0,
|
||||
&xsrc->esb_mmio_kvm, 1);
|
||||
|
||||
/*
|
||||
* 2. END ESB pages (No KVM support yet)
|
||||
@@ -741,11 +742,12 @@ void kvmppc_xive_connect(SpaprXive *xive, Error **errp)
|
||||
xive->tm_mmap = kvmppc_xive_mmap(xive, KVM_XIVE_TIMA_PAGE_OFFSET, tima_len,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
memory_region_init_ram_device_ptr(&xive->tm_mmio, OBJECT(xive),
|
||||
memory_region_init_ram_device_ptr(&xive->tm_mmio_kvm, OBJECT(xive),
|
||||
"xive.tima", tima_len, xive->tm_mmap);
|
||||
memory_region_add_subregion_overlap(&xive->tm_mmio, 0,
|
||||
&xive->tm_mmio_kvm, 1);
|
||||
|
||||
xive->change = qemu_add_vm_change_state_handler(
|
||||
kvmppc_xive_change_state_handler, xive);
|
||||
@@ -756,24 +758,24 @@ void kvmppc_xive_connect(SpaprXive *xive, Error **errp)
|
||||
|
||||
kvmppc_xive_cpu_connect(spapr_cpu_state(cpu)->tctx, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the KVM sources */
|
||||
kvmppc_xive_source_reset(xsrc, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
kvm_kernel_irqchip = true;
|
||||
kvm_msi_via_irqfd_allowed = true;
|
||||
kvm_gsi_direct_mapping = true;
|
||||
return;
|
||||
|
||||
/* Map all regions */
|
||||
spapr_xive_map_mmio(xive);
|
||||
fail:
|
||||
error_propagate(errp, local_err);
|
||||
kvmppc_xive_disconnect(xive, NULL);
|
||||
}
|
||||
|
||||
void kvmppc_xive_disconnect(SpaprXive *xive, Error **errp)
|
||||
@@ -795,21 +797,29 @@ void kvmppc_xive_disconnect(SpaprXive *xive, Error **errp)
|
||||
xsrc = &xive->source;
|
||||
esb_len = (1ull << xsrc->esb_shift) * xsrc->nr_irqs;
|
||||
|
||||
sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 0);
|
||||
munmap(xsrc->esb_mmap, esb_len);
|
||||
if (xsrc->esb_mmap) {
|
||||
memory_region_del_subregion(&xsrc->esb_mmio, &xsrc->esb_mmio_kvm);
|
||||
object_unparent(OBJECT(&xsrc->esb_mmio_kvm));
|
||||
munmap(xsrc->esb_mmap, esb_len);
|
||||
xsrc->esb_mmap = NULL;
|
||||
}
|
||||
|
||||
sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 1);
|
||||
|
||||
sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 2);
|
||||
munmap(xive->tm_mmap, 4ull << TM_SHIFT);
|
||||
if (xive->tm_mmap) {
|
||||
memory_region_del_subregion(&xive->tm_mmio, &xive->tm_mmio_kvm);
|
||||
object_unparent(OBJECT(&xive->tm_mmio_kvm));
|
||||
munmap(xive->tm_mmap, 4ull << TM_SHIFT);
|
||||
xive->tm_mmap = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* When the KVM device fd is closed, the KVM device is destroyed
|
||||
* and removed from the list of devices of the VM. The VCPU
|
||||
* presenters are also detached from the device.
|
||||
*/
|
||||
close(xive->fd);
|
||||
xive->fd = -1;
|
||||
if (xive->fd != -1) {
|
||||
close(xive->fd);
|
||||
xive->fd = -1;
|
||||
}
|
||||
|
||||
kvm_kernel_irqchip = false;
|
||||
kvm_msi_via_irqfd_allowed = false;
|
||||
@@ -819,5 +829,8 @@ void kvmppc_xive_disconnect(SpaprXive *xive, Error **errp)
|
||||
kvm_cpu_disable_all();
|
||||
|
||||
/* VM Change state handler is not needed anymore */
|
||||
qemu_del_vm_change_state_handler(xive->change);
|
||||
if (xive->change) {
|
||||
qemu_del_vm_change_state_handler(xive->change);
|
||||
xive->change = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +267,14 @@ static int icp_post_load(void *opaque, int version_id)
|
||||
ICPState *icp = opaque;
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
return icp_set_kvm_state(icp);
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = icp_set_kvm_state(icp, &local_err);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -300,7 +307,12 @@ static void icp_reset_handler(void *dev)
|
||||
qemu_set_irq(icp->output, 0);
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
icp_set_kvm_state(ICP(dev));
|
||||
Error *local_err = NULL;
|
||||
|
||||
icp_set_kvm_state(ICP(dev), &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,6 +363,7 @@ static void icp_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Connect the presenter to the VCPU (required for CPU hotplug) */
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
icp_kvm_realize(dev, &err);
|
||||
if (err) {
|
||||
@@ -563,7 +576,12 @@ static void ics_simple_reset(DeviceState *dev)
|
||||
icsc->parent_reset(dev);
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
ics_set_kvm_state(ICS_BASE(dev));
|
||||
Error *local_err = NULL;
|
||||
|
||||
ics_set_kvm_state(ICS_BASE(dev), &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,7 +697,14 @@ static int ics_base_post_load(void *opaque, int version_id)
|
||||
ICSState *ics = opaque;
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
return ics_set_kvm_state(ics);
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = ics_set_kvm_state(ics, &local_err);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -765,8 +790,13 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
|
||||
lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
Error *local_err = NULL;
|
||||
|
||||
ics_reset_irq(ics->irqs + srcno);
|
||||
ics_set_kvm_state_one(ics, srcno);
|
||||
ics_set_kvm_state_one(ics, srcno, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ void icp_synchronize_state(ICPState *icp)
|
||||
}
|
||||
}
|
||||
|
||||
int icp_set_kvm_state(ICPState *icp)
|
||||
int icp_set_kvm_state(ICPState *icp, Error **errp)
|
||||
{
|
||||
uint64_t state;
|
||||
int ret;
|
||||
@@ -126,10 +126,11 @@ int icp_set_kvm_state(ICPState *icp)
|
||||
| ((uint64_t)icp->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT);
|
||||
|
||||
ret = kvm_set_one_reg(icp->cs, KVM_REG_PPC_ICP_STATE, &state);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to restore KVM interrupt controller state (0x%"
|
||||
PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(icp->cs),
|
||||
strerror(errno));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Unable to restore KVM interrupt controller state (0x%"
|
||||
PRIx64 ") for CPU %ld", state,
|
||||
kvm_arch_vcpu_id(icp->cs));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -240,10 +241,9 @@ void ics_synchronize_state(ICSState *ics)
|
||||
ics_get_kvm_state(ics);
|
||||
}
|
||||
|
||||
int ics_set_kvm_state_one(ICSState *ics, int srcno)
|
||||
int ics_set_kvm_state_one(ICSState *ics, int srcno, Error **errp)
|
||||
{
|
||||
uint64_t state;
|
||||
Error *local_err = NULL;
|
||||
ICSIRQState *irq = &ics->irqs[srcno];
|
||||
int ret;
|
||||
|
||||
@@ -278,16 +278,15 @@ int ics_set_kvm_state_one(ICSState *ics, int srcno)
|
||||
}
|
||||
|
||||
ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES,
|
||||
srcno + ics->offset, &state, true, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
srcno + ics->offset, &state, true, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ics_set_kvm_state(ICSState *ics)
|
||||
int ics_set_kvm_state(ICSState *ics, Error **errp)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -297,10 +296,12 @@ int ics_set_kvm_state(ICSState *ics)
|
||||
}
|
||||
|
||||
for (i = 0; i < ics->nr_irqs; i++) {
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = ics_set_kvm_state_one(ics, i);
|
||||
if (ret) {
|
||||
ret = ics_set_kvm_state_one(ics, i, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -331,16 +332,7 @@ void ics_kvm_set_irq(ICSState *ics, int srcno, int val)
|
||||
}
|
||||
}
|
||||
|
||||
static void rtas_dummy(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
error_report("pseries: %s must never be called for in-kernel XICS",
|
||||
__func__);
|
||||
}
|
||||
|
||||
int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
|
||||
int xics_kvm_connect(SpaprMachineState *spapr, Error **errp)
|
||||
{
|
||||
int rc;
|
||||
CPUState *cs;
|
||||
@@ -357,42 +349,41 @@ int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
|
||||
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) {
|
||||
error_setg(errp,
|
||||
"KVM and IRQ_XICS capability must be present for in-kernel XICS");
|
||||
goto fail;
|
||||
return -1;
|
||||
}
|
||||
|
||||
spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_dummy);
|
||||
spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_dummy);
|
||||
spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_dummy);
|
||||
spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_dummy);
|
||||
|
||||
rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_SET_XIVE, "ibm,set-xive");
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,set-xive");
|
||||
error_setg_errno(&local_err, -rc,
|
||||
"kvmppc_define_rtas_kernel_token: ibm,set-xive");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_GET_XIVE, "ibm,get-xive");
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,get-xive");
|
||||
error_setg_errno(&local_err, -rc,
|
||||
"kvmppc_define_rtas_kernel_token: ibm,get-xive");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_INT_ON, "ibm,int-on");
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-on");
|
||||
error_setg_errno(&local_err, -rc,
|
||||
"kvmppc_define_rtas_kernel_token: ibm,int-on");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_INT_OFF, "ibm,int-off");
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-off");
|
||||
error_setg_errno(&local_err, -rc,
|
||||
"kvmppc_define_rtas_kernel_token: ibm,int-off");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Create the KVM XICS device */
|
||||
rc = kvm_create_device(kvm_state, KVM_DEV_TYPE_XICS, false);
|
||||
if (rc < 0) {
|
||||
error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS");
|
||||
error_setg_errno(&local_err, -rc, "Error on KVM_CREATE_DEVICE for XICS");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -407,27 +398,30 @@ int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
|
||||
|
||||
icp_kvm_realize(DEVICE(spapr_cpu_state(cpu)->icp), &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the KVM sources */
|
||||
ics_set_kvm_state(spapr->ics);
|
||||
ics_set_kvm_state(spapr->ics, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Connect the presenters to the initial VCPUs of the machine */
|
||||
CPU_FOREACH(cs) {
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
icp_set_kvm_state(spapr_cpu_state(cpu)->icp);
|
||||
icp_set_kvm_state(spapr_cpu_state(cpu)->icp, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kvmppc_define_rtas_kernel_token(0, "ibm,set-xive");
|
||||
kvmppc_define_rtas_kernel_token(0, "ibm,get-xive");
|
||||
kvmppc_define_rtas_kernel_token(0, "ibm,int-on");
|
||||
kvmppc_define_rtas_kernel_token(0, "ibm,int-off");
|
||||
error_propagate(errp, local_err);
|
||||
xics_kvm_disconnect(spapr, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -451,13 +445,10 @@ void xics_kvm_disconnect(SpaprMachineState *spapr, Error **errp)
|
||||
* removed from the list of devices of the VM. The VCPU presenters
|
||||
* are also detached from the device.
|
||||
*/
|
||||
close(kernel_xics_fd);
|
||||
kernel_xics_fd = -1;
|
||||
|
||||
spapr_rtas_unregister(RTAS_IBM_SET_XIVE);
|
||||
spapr_rtas_unregister(RTAS_IBM_GET_XIVE);
|
||||
spapr_rtas_unregister(RTAS_IBM_INT_OFF);
|
||||
spapr_rtas_unregister(RTAS_IBM_INT_ON);
|
||||
if (kernel_xics_fd != -1) {
|
||||
close(kernel_xics_fd);
|
||||
kernel_xics_fd = -1;
|
||||
}
|
||||
|
||||
kvmppc_define_rtas_kernel_token(0, "ibm,set-xive");
|
||||
kvmppc_define_rtas_kernel_token(0, "ibm,get-xive");
|
||||
@@ -471,3 +462,33 @@ void xics_kvm_disconnect(SpaprMachineState *spapr, Error **errp)
|
||||
/* Clear the presenter from the VCPUs */
|
||||
kvm_disable_icps();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a heuristic to detect older KVMs on POWER9 hosts that don't
|
||||
* support destruction of a KVM XICS device while the VM is running.
|
||||
* Required to start a spapr machine with ic-mode=dual,kernel-irqchip=on.
|
||||
*/
|
||||
bool xics_kvm_has_broken_disconnect(SpaprMachineState *spapr)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = kvm_create_device(kvm_state, KVM_DEV_TYPE_XICS, false);
|
||||
if (rc < 0) {
|
||||
/*
|
||||
* The error is ignored on purpose. The KVM XICS setup code
|
||||
* will catch it again anyway. The goal here is to see if
|
||||
* close() actually destroys the device or not.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
close(rc);
|
||||
|
||||
rc = kvm_create_device(kvm_state, KVM_DEV_TYPE_XICS, false);
|
||||
if (rc >= 0) {
|
||||
close(rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return errno == EEXIST;
|
||||
}
|
||||
|
||||
@@ -41,11 +41,32 @@
|
||||
* Guest interfaces
|
||||
*/
|
||||
|
||||
static bool check_emulated_xics(SpaprMachineState *spapr, const char *func)
|
||||
{
|
||||
if (spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT) ||
|
||||
kvm_irqchip_in_kernel()) {
|
||||
error_report("pseries: %s must only be called for emulated XICS",
|
||||
func);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define CHECK_EMULATED_XICS_HCALL(spapr) \
|
||||
do { \
|
||||
if (!check_emulated_xics((spapr), __func__)) { \
|
||||
return H_HARDWARE; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static target_ulong h_cppr(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong cppr = args[0];
|
||||
|
||||
CHECK_EMULATED_XICS_HCALL(spapr);
|
||||
|
||||
icp_set_cppr(spapr_cpu_state(cpu)->icp, cppr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
@@ -56,6 +77,8 @@ static target_ulong h_ipi(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
target_ulong mfrr = args[1];
|
||||
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]);
|
||||
|
||||
CHECK_EMULATED_XICS_HCALL(spapr);
|
||||
|
||||
if (!icp) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
@@ -69,6 +92,8 @@ static target_ulong h_xirr(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
{
|
||||
uint32_t xirr = icp_accept(spapr_cpu_state(cpu)->icp);
|
||||
|
||||
CHECK_EMULATED_XICS_HCALL(spapr);
|
||||
|
||||
args[0] = xirr;
|
||||
return H_SUCCESS;
|
||||
}
|
||||
@@ -78,6 +103,8 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
{
|
||||
uint32_t xirr = icp_accept(spapr_cpu_state(cpu)->icp);
|
||||
|
||||
CHECK_EMULATED_XICS_HCALL(spapr);
|
||||
|
||||
args[0] = xirr;
|
||||
args[1] = cpu_get_host_ticks();
|
||||
return H_SUCCESS;
|
||||
@@ -88,6 +115,8 @@ static target_ulong h_eoi(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
{
|
||||
target_ulong xirr = args[0];
|
||||
|
||||
CHECK_EMULATED_XICS_HCALL(spapr);
|
||||
|
||||
icp_eoi(spapr_cpu_state(cpu)->icp, xirr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
@@ -99,6 +128,8 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
uint32_t mfrr;
|
||||
uint32_t xirr;
|
||||
|
||||
CHECK_EMULATED_XICS_HCALL(spapr);
|
||||
|
||||
if (!icp) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
@@ -111,6 +142,14 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
#define CHECK_EMULATED_XICS_RTAS(spapr, rets) \
|
||||
do { \
|
||||
if (!check_emulated_xics((spapr), __func__)) { \
|
||||
rtas_st((rets), 0, RTAS_OUT_HW_ERROR); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void rtas_set_xive(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
uint32_t token,
|
||||
uint32_t nargs, target_ulong args,
|
||||
@@ -119,6 +158,8 @@ static void rtas_set_xive(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
ICSState *ics = spapr->ics;
|
||||
uint32_t nr, srcno, server, priority;
|
||||
|
||||
CHECK_EMULATED_XICS_RTAS(spapr, rets);
|
||||
|
||||
if ((nargs != 3) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
@@ -152,6 +193,8 @@ static void rtas_get_xive(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
ICSState *ics = spapr->ics;
|
||||
uint32_t nr, srcno;
|
||||
|
||||
CHECK_EMULATED_XICS_RTAS(spapr, rets);
|
||||
|
||||
if ((nargs != 1) || (nret != 3)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
@@ -182,6 +225,8 @@ static void rtas_int_off(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
ICSState *ics = spapr->ics;
|
||||
uint32_t nr, srcno;
|
||||
|
||||
CHECK_EMULATED_XICS_RTAS(spapr, rets);
|
||||
|
||||
if ((nargs != 1) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
@@ -213,6 +258,8 @@ static void rtas_int_on(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
ICSState *ics = spapr->ics;
|
||||
uint32_t nr, srcno;
|
||||
|
||||
CHECK_EMULATED_XICS_RTAS(spapr, rets);
|
||||
|
||||
if ((nargs != 1) || (nret != 1)) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
return;
|
||||
@@ -239,14 +286,6 @@ static void rtas_int_on(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
|
||||
void xics_spapr_init(SpaprMachineState *spapr)
|
||||
{
|
||||
/* Emulated mode can only be initialized once. */
|
||||
if (spapr->ics->init) {
|
||||
return;
|
||||
}
|
||||
|
||||
spapr->ics->init = true;
|
||||
|
||||
/* Registration of global state belongs into realize */
|
||||
spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
|
||||
spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
|
||||
spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
|
||||
|
||||
@@ -132,6 +132,11 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
|
||||
xive_tctx_notify(tctx, ring);
|
||||
}
|
||||
|
||||
static inline uint32_t xive_tctx_word2(uint8_t *ring)
|
||||
{
|
||||
return *((uint32_t *) &ring[TM_WORD2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* XIVE Thread Interrupt Management Area (TIMA)
|
||||
*/
|
||||
@@ -150,11 +155,12 @@ static uint64_t xive_tm_ack_hv_reg(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
static uint64_t xive_tm_pull_pool_ctx(XiveTCTX *tctx, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
uint64_t ret;
|
||||
uint32_t qw2w2_prev = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]);
|
||||
uint32_t qw2w2;
|
||||
|
||||
ret = tctx->regs[TM_QW2_HV_POOL + TM_WORD2] & TM_QW2W2_POOL_CAM;
|
||||
tctx->regs[TM_QW2_HV_POOL + TM_WORD2] &= ~TM_QW2W2_POOL_CAM;
|
||||
return ret;
|
||||
qw2w2 = xive_set_field32(TM_QW2W2_VP, qw2w2_prev, 0);
|
||||
memcpy(&tctx->regs[TM_QW2_HV_POOL + TM_WORD2], &qw2w2, 4);
|
||||
return qw2w2;
|
||||
}
|
||||
|
||||
static void xive_tm_vt_push(XiveTCTX *tctx, hwaddr offset,
|
||||
@@ -182,31 +188,31 @@ static uint64_t xive_tm_vt_poll(XiveTCTX *tctx, hwaddr offset, unsigned size)
|
||||
*/
|
||||
|
||||
static const uint8_t xive_tm_hw_view[] = {
|
||||
/* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0,
|
||||
/* QW-1 OS */ 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0,
|
||||
/* QW-2 POOL */ 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0,
|
||||
/* QW-3 PHYS */ 3, 3, 3, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */
|
||||
3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */
|
||||
0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */
|
||||
3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 3, 3, 3, 0, /* QW-3 PHYS */
|
||||
};
|
||||
|
||||
static const uint8_t xive_tm_hv_view[] = {
|
||||
/* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0,
|
||||
/* QW-1 OS */ 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0,
|
||||
/* QW-2 POOL */ 0, 0, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0,
|
||||
/* QW-3 PHYS */ 3, 3, 3, 3, 0, 3, 0, 3, 3, 0, 0, 3, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */
|
||||
3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */
|
||||
0, 0, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */
|
||||
3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 0, 0, 0, 0, /* QW-3 PHYS */
|
||||
};
|
||||
|
||||
static const uint8_t xive_tm_os_view[] = {
|
||||
/* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0,
|
||||
/* QW-1 OS */ 2, 3, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* QW-2 POOL */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* QW-3 PHYS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */
|
||||
2, 3, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-1 OS */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-2 POOL */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-3 PHYS */
|
||||
};
|
||||
|
||||
static const uint8_t xive_tm_user_view[] = {
|
||||
/* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* QW-1 OS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* QW-2 POOL */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* QW-3 PHYS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-0 User */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-1 OS */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-2 POOL */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* QW-3 PHYS */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -484,11 +490,6 @@ const MemoryRegionOps xive_tm_ops = {
|
||||
},
|
||||
};
|
||||
|
||||
static inline uint32_t xive_tctx_word2(uint8_t *ring)
|
||||
{
|
||||
return *((uint32_t *) &ring[TM_WORD2]);
|
||||
}
|
||||
|
||||
static char *xive_tctx_ring_print(uint8_t *ring)
|
||||
{
|
||||
uint32_t w2 = xive_tctx_word2(ring);
|
||||
@@ -1229,27 +1230,16 @@ XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs)
|
||||
}
|
||||
|
||||
/*
|
||||
* By default on P9, the HW CAM line (23bits) is hardwired to :
|
||||
* Encode the HW CAM line in the block group mode format :
|
||||
*
|
||||
* 0x000||0b1||4Bit chip number||7Bit Thread number.
|
||||
*
|
||||
* When the block grouping is enabled, the CAM line is changed to :
|
||||
*
|
||||
* 4Bit chip number||0x001||7Bit Thread number.
|
||||
* chip << 19 | 0000000 0 0001 thread (7Bit)
|
||||
*/
|
||||
static uint32_t hw_cam_line(uint8_t chip_id, uint8_t tid)
|
||||
{
|
||||
return 1 << 11 | (chip_id & 0xf) << 7 | (tid & 0x7f);
|
||||
}
|
||||
|
||||
static bool xive_presenter_tctx_match_hw(XiveTCTX *tctx,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx)
|
||||
static uint32_t xive_tctx_hw_cam_line(XiveTCTX *tctx)
|
||||
{
|
||||
CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env;
|
||||
uint32_t pir = env->spr_cb[SPR_PIR].default_value;
|
||||
|
||||
return hw_cam_line((pir >> 8) & 0xf, pir & 0x7f) ==
|
||||
hw_cam_line(nvt_blk, nvt_idx);
|
||||
return xive_nvt_cam_line((pir >> 8) & 0xf, 1 << 7 | (pir & 0x7f));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1285,7 +1275,7 @@ static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format,
|
||||
|
||||
/* PHYS ring */
|
||||
if ((be32_to_cpu(qw3w2) & TM_QW3W2_VT) &&
|
||||
xive_presenter_tctx_match_hw(tctx, nvt_blk, nvt_idx)) {
|
||||
cam == xive_tctx_hw_cam_line(tctx)) {
|
||||
return TM_QW3_HV_PHYS;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ obj-$(CONFIG_ARMSSE_MHU) += armsse-mhu.o
|
||||
|
||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||
obj-$(CONFIG_AUX) += auxbus.o
|
||||
obj-$(CONFIG_ASPEED_SOC) += aspeed_xdma.o
|
||||
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
|
||||
obj-$(CONFIG_MSF2) += msf2-sysreg.o
|
||||
obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o
|
||||
|
||||
165
hw/misc/aspeed_xdma.c
Normal file
165
hw/misc/aspeed_xdma.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* ASPEED XDMA Controller
|
||||
* Eddie James <eajames@linux.ibm.com>
|
||||
*
|
||||
* Copyright (C) 2019 IBM Corp
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/misc/aspeed_xdma.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
#define XDMA_BMC_CMDQ_ADDR 0x10
|
||||
#define XDMA_BMC_CMDQ_ENDP 0x14
|
||||
#define XDMA_BMC_CMDQ_WRP 0x18
|
||||
#define XDMA_BMC_CMDQ_W_MASK 0x0003FFFF
|
||||
#define XDMA_BMC_CMDQ_RDP 0x1C
|
||||
#define XDMA_BMC_CMDQ_RDP_MAGIC 0xEE882266
|
||||
#define XDMA_IRQ_ENG_CTRL 0x20
|
||||
#define XDMA_IRQ_ENG_CTRL_US_COMP BIT(4)
|
||||
#define XDMA_IRQ_ENG_CTRL_DS_COMP BIT(5)
|
||||
#define XDMA_IRQ_ENG_CTRL_W_MASK 0xBFEFF07F
|
||||
#define XDMA_IRQ_ENG_STAT 0x24
|
||||
#define XDMA_IRQ_ENG_STAT_US_COMP BIT(4)
|
||||
#define XDMA_IRQ_ENG_STAT_DS_COMP BIT(5)
|
||||
#define XDMA_IRQ_ENG_STAT_RESET 0xF8000000
|
||||
#define XDMA_MEM_SIZE 0x1000
|
||||
|
||||
#define TO_REG(addr) ((addr) / sizeof(uint32_t))
|
||||
|
||||
static uint64_t aspeed_xdma_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
AspeedXDMAState *xdma = opaque;
|
||||
|
||||
if (addr < ASPEED_XDMA_REG_SIZE) {
|
||||
val = xdma->regs[TO_REG(addr)];
|
||||
}
|
||||
|
||||
return (uint64_t)val;
|
||||
}
|
||||
|
||||
static void aspeed_xdma_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned int size)
|
||||
{
|
||||
unsigned int idx;
|
||||
uint32_t val32 = (uint32_t)val;
|
||||
AspeedXDMAState *xdma = opaque;
|
||||
|
||||
if (addr >= ASPEED_XDMA_REG_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case XDMA_BMC_CMDQ_ENDP:
|
||||
xdma->regs[TO_REG(addr)] = val32 & XDMA_BMC_CMDQ_W_MASK;
|
||||
break;
|
||||
case XDMA_BMC_CMDQ_WRP:
|
||||
idx = TO_REG(addr);
|
||||
xdma->regs[idx] = val32 & XDMA_BMC_CMDQ_W_MASK;
|
||||
xdma->regs[TO_REG(XDMA_BMC_CMDQ_RDP)] = xdma->regs[idx];
|
||||
|
||||
trace_aspeed_xdma_write(addr, val);
|
||||
|
||||
if (xdma->bmc_cmdq_readp_set) {
|
||||
xdma->bmc_cmdq_readp_set = 0;
|
||||
} else {
|
||||
xdma->regs[TO_REG(XDMA_IRQ_ENG_STAT)] |=
|
||||
XDMA_IRQ_ENG_STAT_US_COMP | XDMA_IRQ_ENG_STAT_DS_COMP;
|
||||
|
||||
if (xdma->regs[TO_REG(XDMA_IRQ_ENG_CTRL)] &
|
||||
(XDMA_IRQ_ENG_CTRL_US_COMP | XDMA_IRQ_ENG_CTRL_DS_COMP))
|
||||
qemu_irq_raise(xdma->irq);
|
||||
}
|
||||
break;
|
||||
case XDMA_BMC_CMDQ_RDP:
|
||||
trace_aspeed_xdma_write(addr, val);
|
||||
|
||||
if (val32 == XDMA_BMC_CMDQ_RDP_MAGIC) {
|
||||
xdma->bmc_cmdq_readp_set = 1;
|
||||
}
|
||||
break;
|
||||
case XDMA_IRQ_ENG_CTRL:
|
||||
xdma->regs[TO_REG(addr)] = val32 & XDMA_IRQ_ENG_CTRL_W_MASK;
|
||||
break;
|
||||
case XDMA_IRQ_ENG_STAT:
|
||||
trace_aspeed_xdma_write(addr, val);
|
||||
|
||||
idx = TO_REG(addr);
|
||||
if (val32 & (XDMA_IRQ_ENG_STAT_US_COMP | XDMA_IRQ_ENG_STAT_DS_COMP)) {
|
||||
xdma->regs[idx] &=
|
||||
~(XDMA_IRQ_ENG_STAT_US_COMP | XDMA_IRQ_ENG_STAT_DS_COMP);
|
||||
qemu_irq_lower(xdma->irq);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
xdma->regs[TO_REG(addr)] = val32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aspeed_xdma_ops = {
|
||||
.read = aspeed_xdma_read,
|
||||
.write = aspeed_xdma_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static void aspeed_xdma_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
AspeedXDMAState *xdma = ASPEED_XDMA(dev);
|
||||
|
||||
sysbus_init_irq(sbd, &xdma->irq);
|
||||
memory_region_init_io(&xdma->iomem, OBJECT(xdma), &aspeed_xdma_ops, xdma,
|
||||
TYPE_ASPEED_XDMA, XDMA_MEM_SIZE);
|
||||
sysbus_init_mmio(sbd, &xdma->iomem);
|
||||
}
|
||||
|
||||
static void aspeed_xdma_reset(DeviceState *dev)
|
||||
{
|
||||
AspeedXDMAState *xdma = ASPEED_XDMA(dev);
|
||||
|
||||
xdma->bmc_cmdq_readp_set = 0;
|
||||
memset(xdma->regs, 0, ASPEED_XDMA_REG_SIZE);
|
||||
xdma->regs[TO_REG(XDMA_IRQ_ENG_STAT)] = XDMA_IRQ_ENG_STAT_RESET;
|
||||
|
||||
qemu_irq_lower(xdma->irq);
|
||||
}
|
||||
|
||||
static const VMStateDescription aspeed_xdma_vmstate = {
|
||||
.name = TYPE_ASPEED_XDMA,
|
||||
.version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(regs, AspeedXDMAState, ASPEED_XDMA_NUM_REGS),
|
||||
VMSTATE_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
||||
static void aspeed_xdma_class_init(ObjectClass *classp, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(classp);
|
||||
|
||||
dc->realize = aspeed_xdma_realize;
|
||||
dc->reset = aspeed_xdma_reset;
|
||||
dc->vmsd = &aspeed_xdma_vmstate;
|
||||
}
|
||||
|
||||
static const TypeInfo aspeed_xdma_info = {
|
||||
.name = TYPE_ASPEED_XDMA,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AspeedXDMAState),
|
||||
.class_init = aspeed_xdma_class_init,
|
||||
};
|
||||
|
||||
static void aspeed_xdma_register_type(void)
|
||||
{
|
||||
type_register_static(&aspeed_xdma_info);
|
||||
}
|
||||
type_init(aspeed_xdma_register_type);
|
||||
@@ -140,3 +140,6 @@ armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_I
|
||||
# armsse-mhu.c
|
||||
armsse_mhu_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
|
||||
# aspeed_xdma.c
|
||||
aspeed_xdma_write(uint64_t offset, uint64_t data) "XDMA write: offset 0x%" PRIx64 " data 0x%" PRIx64
|
||||
|
||||
@@ -1017,8 +1017,6 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
qemu_macaddr_default_if_unset(&s->conf.macaddr);
|
||||
|
||||
s->conf.peers.ncs[0] = nd_table[0].netdev;
|
||||
|
||||
s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
|
||||
object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
|
||||
s);
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#define HME_SEBI_STAT 0x100
|
||||
#define HME_SEBI_STAT_LINUXBUG 0x108
|
||||
#define HME_SEB_STAT_RXTOHOST 0x10000
|
||||
#define HME_SEB_STAT_NORXD 0x20000
|
||||
#define HME_SEB_STAT_MIFIRQ 0x800000
|
||||
#define HME_SEB_STAT_HOSTTOTX 0x1000000
|
||||
#define HME_SEB_STAT_TXALL 0x2000000
|
||||
@@ -209,6 +210,8 @@ static void sunhme_update_irq(SunHMEState *s)
|
||||
}
|
||||
|
||||
level = (seb ? 1 : 0);
|
||||
trace_sunhme_update_irq(mifmask, mif, sebmask, seb, level);
|
||||
|
||||
pci_set_irq(d, level);
|
||||
}
|
||||
|
||||
@@ -371,10 +374,20 @@ static void sunhme_mac_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
SunHMEState *s = SUNHME(opaque);
|
||||
uint64_t oldval = s->macregs[addr >> 2];
|
||||
|
||||
trace_sunhme_mac_write(addr, val);
|
||||
|
||||
s->macregs[addr >> 2] = val;
|
||||
|
||||
switch (addr) {
|
||||
case HME_MACI_RXCFG:
|
||||
if (!(oldval & HME_MAC_RXCFG_ENABLE) &&
|
||||
(val & HME_MAC_RXCFG_ENABLE)) {
|
||||
qemu_flush_queued_packets(qemu_get_queue(s->nic));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t sunhme_mac_read(void *opaque, hwaddr addr,
|
||||
@@ -647,7 +660,7 @@ static int sunhme_can_receive(NetClientState *nc)
|
||||
{
|
||||
SunHMEState *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
return s->macregs[HME_MAC_RXCFG_ENABLE >> 2] & HME_MAC_RXCFG_ENABLE;
|
||||
return s->macregs[HME_MACI_RXCFG >> 2] & HME_MAC_RXCFG_ENABLE;
|
||||
}
|
||||
|
||||
static void sunhme_link_status_changed(NetClientState *nc)
|
||||
@@ -716,7 +729,7 @@ static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf,
|
||||
|
||||
/* Do nothing if MAC RX disabled */
|
||||
if (!(s->macregs[HME_MACI_RXCFG >> 2] & HME_MAC_RXCFG_ENABLE)) {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_sunhme_rx_filter_destmac(buf[0], buf[1], buf[2],
|
||||
@@ -745,14 +758,14 @@ static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf,
|
||||
/* Didn't match hash filter */
|
||||
trace_sunhme_rx_filter_hash_nomatch();
|
||||
trace_sunhme_rx_filter_reject();
|
||||
return 0;
|
||||
return -1;
|
||||
} else {
|
||||
trace_sunhme_rx_filter_hash_match();
|
||||
}
|
||||
} else {
|
||||
/* Not for us */
|
||||
trace_sunhme_rx_filter_reject();
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
trace_sunhme_rx_filter_promisc_match();
|
||||
@@ -775,6 +788,14 @@ static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf,
|
||||
pci_dma_read(d, rb + cr * HME_DESC_SIZE, &status, 4);
|
||||
pci_dma_read(d, rb + cr * HME_DESC_SIZE + 4, &buffer, 4);
|
||||
|
||||
/* If we don't own the current descriptor then indicate overflow error */
|
||||
if (!(status & HME_XD_OWN)) {
|
||||
s->sebregs[HME_SEBI_STAT >> 2] |= HME_SEB_STAT_NORXD;
|
||||
sunhme_update_irq(s);
|
||||
trace_sunhme_rx_norxd();
|
||||
return -1;
|
||||
}
|
||||
|
||||
rxoffset = (s->erxregs[HME_ERXI_CFG >> 2] & HME_ERX_CFG_BYTEOFFSET) >>
|
||||
HME_ERX_CFG_BYTEOFFSET_SHIFT;
|
||||
|
||||
|
||||
@@ -359,6 +359,8 @@ sunhme_rx_filter_reject(void) "rejecting incoming frame"
|
||||
sunhme_rx_filter_accept(void) "accepting incoming frame"
|
||||
sunhme_rx_desc(uint32_t addr, int offset, uint32_t status, int len, int cr, int nr) "addr 0x%"PRIx32"(+0x%x) status 0x%"PRIx32 " len %d (ring %d/%d)"
|
||||
sunhme_rx_xsum_calc(uint16_t xsum) "calculated incoming xsum as 0x%x"
|
||||
sunhme_rx_norxd(void) "no free rx descriptors available"
|
||||
sunhme_update_irq(uint32_t mifmask, uint32_t mif, uint32_t sebmask, uint32_t seb, int level) "mifmask: 0x%x mif: 0x%x sebmask: 0x%x seb: 0x%x level: %d"
|
||||
|
||||
# virtio-net.c
|
||||
virtio_net_announce_notify(void) ""
|
||||
|
||||
@@ -2360,7 +2360,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
|
||||
timer_mod(n->announce_timer.tm,
|
||||
qemu_clock_get_ms(n->announce_timer.type));
|
||||
} else {
|
||||
qemu_announce_timer_del(&n->announce_timer);
|
||||
qemu_announce_timer_del(&n->announce_timer, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2784,7 +2784,7 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
|
||||
virtio_net_del_queue(n, i);
|
||||
}
|
||||
|
||||
qemu_announce_timer_del(&n->announce_timer);
|
||||
qemu_announce_timer_del(&n->announce_timer, false);
|
||||
g_free(n->vqs);
|
||||
qemu_del_nic(n->nic);
|
||||
virtio_net_rsc_cleanup(n);
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
#define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff)
|
||||
#define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
#define DESIGNWARE_PCIE_IRQ_MSI 3
|
||||
|
||||
static DesignwarePCIEHost *
|
||||
designware_pcie_root_to_host(DesignwarePCIERoot *root)
|
||||
{
|
||||
@@ -67,7 +69,7 @@ static void designware_pcie_root_msi_write(void *opaque, hwaddr addr,
|
||||
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);
|
||||
qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,23 +292,19 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_LO:
|
||||
root->msi.base &= 0xFFFFFFFF00000000ULL;
|
||||
root->msi.base |= val;
|
||||
designware_pcie_root_update_msi_mapping(root);
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_ADDR_HI:
|
||||
root->msi.base &= 0x00000000FFFFFFFFULL;
|
||||
root->msi.base |= (uint64_t)val << 32;
|
||||
designware_pcie_root_update_msi_mapping(root);
|
||||
break;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: {
|
||||
const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val;
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
|
||||
root->msi.intr[0].enable = val;
|
||||
|
||||
if (update_msi_mapping) {
|
||||
designware_pcie_root_update_msi_mapping(root);
|
||||
}
|
||||
designware_pcie_root_update_msi_mapping(root);
|
||||
break;
|
||||
}
|
||||
|
||||
case DESIGNWARE_PCIE_MSI_INTR0_MASK:
|
||||
root->msi.intr[0].mask = val;
|
||||
@@ -315,7 +313,7 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
|
||||
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);
|
||||
qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 0);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -437,13 +437,11 @@ static void ppc_core99_init(MachineState *machine)
|
||||
}
|
||||
|
||||
/* The NewWorld NVRAM is not located in the MacIO device */
|
||||
#ifdef CONFIG_KVM
|
||||
if (kvm_enabled() && getpagesize() > 4096) {
|
||||
/* We can't combine read-write and read-only in a single page, so
|
||||
move the NVRAM out of ROM again for KVM */
|
||||
nvram_addr = 0xFFE00000;
|
||||
}
|
||||
#endif
|
||||
dev = qdev_create(NULL, TYPE_MACIO_NVRAM);
|
||||
qdev_prop_set_uint32(dev, "size", 0x2000);
|
||||
qdev_prop_set_uint32(dev, "it_shift", 1);
|
||||
@@ -488,14 +486,12 @@ static void ppc_core99_init(MachineState *machine)
|
||||
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
|
||||
if (kvm_enabled()) {
|
||||
#ifdef CONFIG_KVM
|
||||
uint8_t *hypercall;
|
||||
|
||||
hypercall = g_malloc(16);
|
||||
kvmppc_get_hypercall(env, hypercall, 16);
|
||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
|
||||
#endif
|
||||
}
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, tbfreq);
|
||||
/* Mac OS X requires a "known good" clock-frequency value; pass it one. */
|
||||
|
||||
@@ -345,14 +345,12 @@ static void ppc_heathrow_init(MachineState *machine)
|
||||
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
|
||||
if (kvm_enabled()) {
|
||||
#ifdef CONFIG_KVM
|
||||
uint8_t *hypercall;
|
||||
|
||||
hypercall = g_malloc(16);
|
||||
kvmppc_get_hypercall(env, hypercall, 16);
|
||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
|
||||
#endif
|
||||
}
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, tbfreq);
|
||||
/* Mac OS X requires a "known good" clock-frequency value; pass it one. */
|
||||
|
||||
34
hw/ppc/pnv.c
34
hw/ppc/pnv.c
@@ -860,6 +860,14 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
Pnv8Psi *psi8 = &chip8->psi;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* XSCOM bridge is first */
|
||||
pnv_xscom_realize(chip, PNV_XSCOM_SIZE, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
|
||||
|
||||
pcc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -916,7 +924,6 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
|
||||
k->isa_create = pnv_chip_power8_isa_create;
|
||||
k->dt_populate = pnv_chip_power8_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power8_pic_print_info;
|
||||
k->xscom_base = 0x003fc0000000000ull;
|
||||
dc->desc = "PowerNV Chip POWER8E";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power8_realize,
|
||||
@@ -936,7 +943,6 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
|
||||
k->isa_create = pnv_chip_power8_isa_create;
|
||||
k->dt_populate = pnv_chip_power8_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power8_pic_print_info;
|
||||
k->xscom_base = 0x003fc0000000000ull;
|
||||
dc->desc = "PowerNV Chip POWER8";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power8_realize,
|
||||
@@ -956,7 +962,6 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
|
||||
k->isa_create = pnv_chip_power8nvl_isa_create;
|
||||
k->dt_populate = pnv_chip_power8_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power8_pic_print_info;
|
||||
k->xscom_base = 0x003fc0000000000ull;
|
||||
dc->desc = "PowerNV Chip POWER8NVL";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power8_realize,
|
||||
@@ -1024,6 +1029,14 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
Pnv9Psi *psi9 = &chip9->psi;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* XSCOM bridge is first */
|
||||
pnv_xscom_realize(chip, PNV9_XSCOM_SIZE, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV9_XSCOM_BASE(chip));
|
||||
|
||||
pcc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -1099,7 +1112,6 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
|
||||
k->isa_create = pnv_chip_power9_isa_create;
|
||||
k->dt_populate = pnv_chip_power9_dt_populate;
|
||||
k->pic_print_info = pnv_chip_power9_pic_print_info;
|
||||
k->xscom_base = 0x00603fc00000000ull;
|
||||
dc->desc = "PowerNV Chip POWER9";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power9_realize,
|
||||
@@ -1136,11 +1148,6 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_chip_instance_init(Object *obj)
|
||||
{
|
||||
PNV_CHIP(obj)->xscom_base = PNV_CHIP_GET_CLASS(obj)->xscom_base;
|
||||
}
|
||||
|
||||
static void pnv_chip_core_realize(PnvChip *chip, Error **errp)
|
||||
{
|
||||
Error *error = NULL;
|
||||
@@ -1206,14 +1213,6 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
|
||||
PnvChip *chip = PNV_CHIP(dev);
|
||||
Error *error = NULL;
|
||||
|
||||
/* XSCOM bridge */
|
||||
pnv_xscom_realize(chip, &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
|
||||
|
||||
/* Cores */
|
||||
pnv_chip_core_realize(chip, &error);
|
||||
if (error) {
|
||||
@@ -1398,7 +1397,6 @@ static const TypeInfo types[] = {
|
||||
.name = TYPE_PNV_CHIP,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.class_init = pnv_chip_class_init,
|
||||
.instance_init = pnv_chip_instance_init,
|
||||
.instance_size = sizeof(PnvChip),
|
||||
.class_size = sizeof(PnvChipClass),
|
||||
.abstract = true,
|
||||
|
||||
@@ -213,17 +213,17 @@ const MemoryRegionOps pnv_xscom_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
void pnv_xscom_realize(PnvChip *chip, Error **errp)
|
||||
void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(chip);
|
||||
char *name;
|
||||
|
||||
name = g_strdup_printf("xscom-%x", chip->chip_id);
|
||||
memory_region_init_io(&chip->xscom_mmio, OBJECT(chip), &pnv_xscom_ops,
|
||||
chip, name, PNV_XSCOM_SIZE);
|
||||
chip, name, size);
|
||||
sysbus_init_mmio(sbd, &chip->xscom_mmio);
|
||||
|
||||
memory_region_init(&chip->xscom, OBJECT(chip), name, PNV_XSCOM_SIZE);
|
||||
memory_region_init(&chip->xscom, OBJECT(chip), name, size);
|
||||
address_space_init(&chip->xscom_as, &chip->xscom, name);
|
||||
g_free(name);
|
||||
}
|
||||
@@ -265,12 +265,19 @@ static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom";
|
||||
|
||||
int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset)
|
||||
{
|
||||
uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(chip)),
|
||||
cpu_to_be64(PNV_XSCOM_SIZE) };
|
||||
uint64_t reg[2];
|
||||
int xscom_offset;
|
||||
ForeachPopulateArgs args;
|
||||
char *name;
|
||||
|
||||
if (pnv_chip_is_power9(chip)) {
|
||||
reg[0] = cpu_to_be64(PNV9_XSCOM_BASE(chip));
|
||||
reg[1] = cpu_to_be64(PNV9_XSCOM_SIZE);
|
||||
} else {
|
||||
reg[0] = cpu_to_be64(PNV_XSCOM_BASE(chip));
|
||||
reg[1] = cpu_to_be64(PNV_XSCOM_SIZE);
|
||||
}
|
||||
|
||||
name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0]));
|
||||
xscom_offset = fdt_add_subnode(fdt, root_offset, name);
|
||||
_FDT(xscom_offset);
|
||||
|
||||
@@ -80,9 +80,7 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
|
||||
}
|
||||
|
||||
if (old_pending != env->pending_interrupts) {
|
||||
#ifdef CONFIG_KVM
|
||||
kvmppc_set_interrupt(cpu, n_IRQ, level);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1036,10 +1034,7 @@ static void timebase_load(PPCTimebase *tb)
|
||||
CPU_FOREACH(cpu) {
|
||||
PowerPCCPU *pcpu = POWERPC_CPU(cpu);
|
||||
pcpu->env.tb_env->tb_offset = tb_off_adj;
|
||||
#if defined(CONFIG_KVM)
|
||||
kvm_set_one_reg(cpu, KVM_REG_PPC_TB_OFFSET,
|
||||
&pcpu->env.tb_env->tb_offset);
|
||||
#endif
|
||||
kvmppc_set_reg_tb_offset(pcpu, pcpu->env.tb_env->tb_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -780,7 +780,6 @@ static void ibm_40p_init(MachineState *machine)
|
||||
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
|
||||
if (kvm_enabled()) {
|
||||
#ifdef CONFIG_KVM
|
||||
uint8_t *hypercall;
|
||||
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
|
||||
@@ -788,7 +787,6 @@ static void ibm_40p_init(MachineState *machine)
|
||||
kvmppc_get_hypercall(env, hypercall, 16);
|
||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
|
||||
#endif
|
||||
} else {
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ void spapr_irq_msi_reset(SpaprMachineState *spapr)
|
||||
bitmap_clear(spapr->irq_map, 0, spapr->irq_map_nr);
|
||||
}
|
||||
|
||||
static void spapr_irq_init_device(SpaprMachineState *spapr,
|
||||
static void spapr_irq_init_kvm(SpaprMachineState *spapr,
|
||||
SpaprIrq *irq, Error **errp)
|
||||
{
|
||||
MachineState *machine = MACHINE(spapr);
|
||||
@@ -88,8 +88,6 @@ static void spapr_irq_init_device(SpaprMachineState *spapr,
|
||||
error_prepend(&local_err, "kernel_irqchip allowed but unavailable: ");
|
||||
warn_report_err(local_err);
|
||||
}
|
||||
|
||||
irq->init_emu(spapr, errp);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -114,6 +112,8 @@ static void spapr_irq_init_xics(SpaprMachineState *spapr, int nr_irqs,
|
||||
}
|
||||
|
||||
spapr->ics = ICS_BASE(obj);
|
||||
|
||||
xics_spapr_init(spapr);
|
||||
}
|
||||
|
||||
#define ICS_IRQ_FREE(ics, srcno) \
|
||||
@@ -222,7 +222,7 @@ static void spapr_irq_reset_xics(SpaprMachineState *spapr, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
spapr_irq_init_device(spapr, &spapr_irq_xics, &local_err);
|
||||
spapr_irq_init_kvm(spapr, &spapr_irq_xics, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -234,15 +234,10 @@ static const char *spapr_irq_get_nodename_xics(SpaprMachineState *spapr)
|
||||
return XICS_NODENAME;
|
||||
}
|
||||
|
||||
static void spapr_irq_init_emu_xics(SpaprMachineState *spapr, Error **errp)
|
||||
{
|
||||
xics_spapr_init(spapr);
|
||||
}
|
||||
|
||||
static void spapr_irq_init_kvm_xics(SpaprMachineState *spapr, Error **errp)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
xics_kvm_init(spapr, errp);
|
||||
xics_kvm_connect(spapr, errp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +261,6 @@ SpaprIrq spapr_irq_xics = {
|
||||
.reset = spapr_irq_reset_xics,
|
||||
.set_irq = spapr_irq_set_irq_xics,
|
||||
.get_nodename = spapr_irq_get_nodename_xics,
|
||||
.init_emu = spapr_irq_init_emu_xics,
|
||||
.init_kvm = spapr_irq_init_kvm_xics,
|
||||
};
|
||||
|
||||
@@ -384,7 +378,7 @@ static void spapr_irq_reset_xive(SpaprMachineState *spapr, Error **errp)
|
||||
spapr_xive_set_tctx_os_cam(spapr_cpu_state(cpu)->tctx);
|
||||
}
|
||||
|
||||
spapr_irq_init_device(spapr, &spapr_irq_xive, &local_err);
|
||||
spapr_irq_init_kvm(spapr, &spapr_irq_xive, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -410,11 +404,6 @@ static const char *spapr_irq_get_nodename_xive(SpaprMachineState *spapr)
|
||||
return spapr->xive->nodename;
|
||||
}
|
||||
|
||||
static void spapr_irq_init_emu_xive(SpaprMachineState *spapr, Error **errp)
|
||||
{
|
||||
spapr_xive_init(spapr->xive, errp);
|
||||
}
|
||||
|
||||
static void spapr_irq_init_kvm_xive(SpaprMachineState *spapr, Error **errp)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
@@ -446,7 +435,6 @@ SpaprIrq spapr_irq_xive = {
|
||||
.reset = spapr_irq_reset_xive,
|
||||
.set_irq = spapr_irq_set_irq_xive,
|
||||
.get_nodename = spapr_irq_get_nodename_xive,
|
||||
.init_emu = spapr_irq_init_emu_xive,
|
||||
.init_kvm = spapr_irq_init_kvm_xive,
|
||||
};
|
||||
|
||||
@@ -624,7 +612,6 @@ SpaprIrq spapr_irq_dual = {
|
||||
.reset = spapr_irq_reset_dual,
|
||||
.set_irq = spapr_irq_set_irq_dual,
|
||||
.get_nodename = spapr_irq_get_nodename_dual,
|
||||
.init_emu = NULL, /* should not be used */
|
||||
.init_kvm = NULL, /* should not be used */
|
||||
};
|
||||
|
||||
@@ -668,6 +655,19 @@ static void spapr_irq_check(SpaprMachineState *spapr, Error **errp)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* On a POWER9 host, some older KVM XICS devices cannot be destroyed and
|
||||
* re-created. Detect that early to avoid QEMU to exit later when the
|
||||
* guest reboots.
|
||||
*/
|
||||
if (kvm_enabled() &&
|
||||
spapr->irq == &spapr_irq_dual &&
|
||||
machine_kernel_irqchip_required(machine) &&
|
||||
xics_kvm_has_broken_disconnect(spapr)) {
|
||||
error_setg(errp, "KVM is too old to support ic-mode=dual,kernel-irqchip=on");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -827,6 +827,5 @@ SpaprIrq spapr_irq_xics_legacy = {
|
||||
.reset = spapr_irq_reset_xics,
|
||||
.set_irq = spapr_irq_set_irq_xics,
|
||||
.get_nodename = spapr_irq_get_nodename_xics,
|
||||
.init_emu = spapr_irq_init_emu_xics,
|
||||
.init_kvm = spapr_irq_init_kvm_xics,
|
||||
};
|
||||
|
||||
@@ -1343,6 +1343,7 @@ static void spapr_dt_pci_device_cb(PCIBus *bus, PCIDevice *pdev,
|
||||
static int spapr_dt_pci_bus(SpaprPhbState *sphb, PCIBus *bus,
|
||||
void *fdt, int offset)
|
||||
{
|
||||
Object *owner;
|
||||
PciWalkFdt cbinfo = {
|
||||
.fdt = fdt,
|
||||
.offset = offset,
|
||||
@@ -1356,15 +1357,20 @@ static int spapr_dt_pci_bus(SpaprPhbState *sphb, PCIBus *bus,
|
||||
_FDT(fdt_setprop_cell(fdt, offset, "#size-cells",
|
||||
RESOURCE_CELLS_SIZE));
|
||||
|
||||
if (bus) {
|
||||
pci_for_each_device_reverse(bus, pci_bus_num(bus),
|
||||
spapr_dt_pci_device_cb, &cbinfo);
|
||||
if (cbinfo.err) {
|
||||
return cbinfo.err;
|
||||
}
|
||||
assert(bus);
|
||||
pci_for_each_device_reverse(bus, pci_bus_num(bus),
|
||||
spapr_dt_pci_device_cb, &cbinfo);
|
||||
if (cbinfo.err) {
|
||||
return cbinfo.err;
|
||||
}
|
||||
|
||||
ret = spapr_dt_drc(fdt, offset, OBJECT(bus->parent_dev),
|
||||
if (pci_bus_is_root(bus)) {
|
||||
owner = OBJECT(sphb);
|
||||
} else {
|
||||
owner = OBJECT(pci_bridge_get_device(bus));
|
||||
}
|
||||
|
||||
ret = spapr_dt_drc(fdt, offset, owner,
|
||||
SPAPR_DR_CONNECTOR_TYPE_PCI);
|
||||
if (ret) {
|
||||
return ret;
|
||||
@@ -1782,6 +1788,12 @@ static void spapr_phb_unrealize(DeviceState *dev, Error **errp)
|
||||
|
||||
memory_region_del_subregion(&sphb->iommu_root, &sphb->msiwindow);
|
||||
|
||||
/*
|
||||
* An attached PCI device may have memory listeners, eg. VFIO PCI. We have
|
||||
* unmapped all sections. Remove the listeners now, before destroying the
|
||||
* address space.
|
||||
*/
|
||||
address_space_remove_listeners(&sphb->iommu_as);
|
||||
address_space_destroy(&sphb->iommu_as);
|
||||
|
||||
qbus_set_hotplug_handler(BUS(phb->bus), NULL, &error_abort);
|
||||
@@ -1945,11 +1957,9 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
||||
* For KVM we want to ensure that this memory is a full page so that
|
||||
* our memory slot is of page size granularity.
|
||||
*/
|
||||
#ifdef CONFIG_KVM
|
||||
if (kvm_enabled()) {
|
||||
msi_window_size = getpagesize();
|
||||
}
|
||||
#endif
|
||||
|
||||
memory_region_init_io(&sphb->msiwindow, OBJECT(sphb), &spapr_msi_ops, spapr,
|
||||
"msi", msi_window_size);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "trace.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
#include "hw/s390x/s390-virtio-ccw.h"
|
||||
#include "hw/s390x/s390-ccw.h"
|
||||
|
||||
typedef struct CrwContainer {
|
||||
CRW crw;
|
||||
@@ -1205,6 +1206,26 @@ static void sch_handle_start_func_virtual(SubchDev *sch)
|
||||
|
||||
}
|
||||
|
||||
static void sch_handle_halt_func_passthrough(SubchDev *sch)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = s390_ccw_halt(sch);
|
||||
if (ret == -ENOSYS) {
|
||||
sch_handle_halt_func(sch);
|
||||
}
|
||||
}
|
||||
|
||||
static void sch_handle_clear_func_passthrough(SubchDev *sch)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = s390_ccw_clear(sch);
|
||||
if (ret == -ENOSYS) {
|
||||
sch_handle_clear_func(sch);
|
||||
}
|
||||
}
|
||||
|
||||
static IOInstEnding sch_handle_start_func_passthrough(SubchDev *sch)
|
||||
{
|
||||
SCHIB *schib = &sch->curr_status;
|
||||
@@ -1244,11 +1265,9 @@ IOInstEnding do_subchannel_work_passthrough(SubchDev *sch)
|
||||
SCHIB *schib = &sch->curr_status;
|
||||
|
||||
if (schib->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) {
|
||||
/* TODO: Clear handling */
|
||||
sch_handle_clear_func(sch);
|
||||
sch_handle_clear_func_passthrough(sch);
|
||||
} else if (schib->scsw.ctrl & SCSW_FCTL_HALT_FUNC) {
|
||||
/* TODO: Halt handling */
|
||||
sch_handle_halt_func(sch);
|
||||
sch_handle_halt_func_passthrough(sch);
|
||||
} else if (schib->scsw.ctrl & SCSW_FCTL_START_FUNC) {
|
||||
return sch_handle_start_func_passthrough(sch);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,26 @@ IOInstEnding s390_ccw_cmd_request(SubchDev *sch)
|
||||
return cdc->handle_request(sch);
|
||||
}
|
||||
|
||||
int s390_ccw_halt(SubchDev *sch)
|
||||
{
|
||||
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
|
||||
|
||||
if (!cdc->handle_halt) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
return cdc->handle_halt(sch);
|
||||
}
|
||||
|
||||
int s390_ccw_clear(SubchDev *sch)
|
||||
{
|
||||
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
|
||||
|
||||
if (!cdc->handle_clear) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
return cdc->handle_clear(sch);
|
||||
}
|
||||
|
||||
static void s390_ccw_get_dev_info(S390CCWDevice *cdev,
|
||||
char *sysfsdev,
|
||||
Error **errp)
|
||||
|
||||
@@ -1406,6 +1406,7 @@ static void ss5_class_init(ObjectClass *oc, void *data)
|
||||
mc->is_default = 1;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Fujitsu-MB86904");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo ss5_type = {
|
||||
@@ -1424,6 +1425,7 @@ static void ss10_class_init(ObjectClass *oc, void *data)
|
||||
mc->max_cpus = 4;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-SuperSparc-II");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo ss10_type = {
|
||||
@@ -1442,6 +1444,7 @@ static void ss600mp_class_init(ObjectClass *oc, void *data)
|
||||
mc->max_cpus = 4;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-SuperSparc-II");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo ss600mp_type = {
|
||||
@@ -1460,6 +1463,7 @@ static void ss20_class_init(ObjectClass *oc, void *data)
|
||||
mc->max_cpus = 4;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-SuperSparc-II");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo ss20_type = {
|
||||
@@ -1477,6 +1481,7 @@ static void voyager_class_init(ObjectClass *oc, void *data)
|
||||
mc->block_default_type = IF_SCSI;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Fujitsu-MB86904");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo voyager_type = {
|
||||
@@ -1494,6 +1499,7 @@ static void ss_lx_class_init(ObjectClass *oc, void *data)
|
||||
mc->block_default_type = IF_SCSI;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-MicroSparc-I");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo ss_lx_type = {
|
||||
@@ -1511,6 +1517,7 @@ static void ss4_class_init(ObjectClass *oc, void *data)
|
||||
mc->block_default_type = IF_SCSI;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Fujitsu-MB86904");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo ss4_type = {
|
||||
@@ -1528,6 +1535,7 @@ static void scls_class_init(ObjectClass *oc, void *data)
|
||||
mc->block_default_type = IF_SCSI;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-MicroSparc-I");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo scls_type = {
|
||||
@@ -1545,6 +1553,7 @@ static void sbook_class_init(ObjectClass *oc, void *data)
|
||||
mc->block_default_type = IF_SCSI;
|
||||
mc->default_boot_order = "c";
|
||||
mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-MicroSparc-I");
|
||||
mc->default_display = "tcx";
|
||||
}
|
||||
|
||||
static const TypeInfo sbook_type = {
|
||||
|
||||
@@ -913,6 +913,7 @@ static const VMStateDescription vmstate_aspeed_smc = {
|
||||
|
||||
static Property aspeed_smc_properties[] = {
|
||||
DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1),
|
||||
DEFINE_PROP_UINT64("sdram-base", AspeedSMCState, sdram_base, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
|
||||
|
||||
common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o
|
||||
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
|
||||
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o aspeed_rtc.o
|
||||
|
||||
common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
|
||||
common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
|
||||
|
||||
180
hw/timer/aspeed_rtc.c
Normal file
180
hw/timer/aspeed_rtc.c
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* ASPEED Real Time Clock
|
||||
* Joel Stanley <joel@jms.id.au>
|
||||
*
|
||||
* Copyright 2019 IBM Corp
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/timer/aspeed_rtc.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
#define COUNTER1 (0x00 / 4)
|
||||
#define COUNTER2 (0x04 / 4)
|
||||
#define ALARM (0x08 / 4)
|
||||
#define CONTROL (0x10 / 4)
|
||||
#define ALARM_STATUS (0x14 / 4)
|
||||
|
||||
#define RTC_UNLOCKED BIT(1)
|
||||
#define RTC_ENABLED BIT(0)
|
||||
|
||||
static void aspeed_rtc_calc_offset(AspeedRtcState *rtc)
|
||||
{
|
||||
struct tm tm;
|
||||
uint32_t year, cent;
|
||||
uint32_t reg1 = rtc->reg[COUNTER1];
|
||||
uint32_t reg2 = rtc->reg[COUNTER2];
|
||||
|
||||
tm.tm_mday = (reg1 >> 24) & 0x1f;
|
||||
tm.tm_hour = (reg1 >> 16) & 0x1f;
|
||||
tm.tm_min = (reg1 >> 8) & 0x3f;
|
||||
tm.tm_sec = (reg1 >> 0) & 0x3f;
|
||||
|
||||
cent = (reg2 >> 16) & 0x1f;
|
||||
year = (reg2 >> 8) & 0x7f;
|
||||
tm.tm_mon = ((reg2 >> 0) & 0x0f) - 1;
|
||||
tm.tm_year = year + (cent * 100) - 1900;
|
||||
|
||||
rtc->offset = qemu_timedate_diff(&tm);
|
||||
}
|
||||
|
||||
static uint32_t aspeed_rtc_get_counter(AspeedRtcState *rtc, int r)
|
||||
{
|
||||
uint32_t year, cent;
|
||||
struct tm now;
|
||||
|
||||
qemu_get_timedate(&now, rtc->offset);
|
||||
|
||||
switch (r) {
|
||||
case COUNTER1:
|
||||
return (now.tm_mday << 24) | (now.tm_hour << 16) |
|
||||
(now.tm_min << 8) | now.tm_sec;
|
||||
case COUNTER2:
|
||||
cent = (now.tm_year + 1900) / 100;
|
||||
year = now.tm_year % 100;
|
||||
return ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) |
|
||||
((now.tm_mon + 1) & 0xf);
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t aspeed_rtc_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
AspeedRtcState *rtc = opaque;
|
||||
uint64_t val;
|
||||
uint32_t r = addr >> 2;
|
||||
|
||||
switch (r) {
|
||||
case COUNTER1:
|
||||
case COUNTER2:
|
||||
if (rtc->reg[CONTROL] & RTC_ENABLED) {
|
||||
rtc->reg[r] = aspeed_rtc_get_counter(rtc, r);
|
||||
}
|
||||
/* fall through */
|
||||
case CONTROL:
|
||||
val = rtc->reg[r];
|
||||
break;
|
||||
case ALARM:
|
||||
case ALARM_STATUS:
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_aspeed_rtc_read(addr, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void aspeed_rtc_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
AspeedRtcState *rtc = opaque;
|
||||
uint32_t r = addr >> 2;
|
||||
|
||||
switch (r) {
|
||||
case COUNTER1:
|
||||
case COUNTER2:
|
||||
if (!(rtc->reg[CONTROL] & RTC_UNLOCKED)) {
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case CONTROL:
|
||||
rtc->reg[r] = val;
|
||||
aspeed_rtc_calc_offset(rtc);
|
||||
break;
|
||||
case ALARM:
|
||||
case ALARM_STATUS:
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
trace_aspeed_rtc_write(addr, val);
|
||||
}
|
||||
|
||||
static void aspeed_rtc_reset(DeviceState *d)
|
||||
{
|
||||
AspeedRtcState *rtc = ASPEED_RTC(d);
|
||||
|
||||
rtc->offset = 0;
|
||||
memset(rtc->reg, 0, sizeof(rtc->reg));
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aspeed_rtc_ops = {
|
||||
.read = aspeed_rtc_read,
|
||||
.write = aspeed_rtc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_aspeed_rtc = {
|
||||
.name = TYPE_ASPEED_RTC,
|
||||
.version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(reg, AspeedRtcState, 0x18),
|
||||
VMSTATE_INT32(offset, AspeedRtcState),
|
||||
VMSTATE_INT32(offset, AspeedRtcState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void aspeed_rtc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
AspeedRtcState *s = ASPEED_RTC(dev);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_rtc_ops, s,
|
||||
"aspeed-rtc", 0x18ULL);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void aspeed_rtc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = aspeed_rtc_realize;
|
||||
dc->vmsd = &vmstate_aspeed_rtc;
|
||||
dc->reset = aspeed_rtc_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo aspeed_rtc_info = {
|
||||
.name = TYPE_ASPEED_RTC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AspeedRtcState),
|
||||
.class_init = aspeed_rtc_class_init,
|
||||
};
|
||||
|
||||
static void aspeed_rtc_register_types(void)
|
||||
{
|
||||
type_register_static(&aspeed_rtc_info);
|
||||
}
|
||||
|
||||
type_init(aspeed_rtc_register_types)
|
||||
@@ -107,39 +107,49 @@ static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
|
||||
return t->start + delta_ns;
|
||||
}
|
||||
|
||||
static inline uint32_t calculate_match(struct AspeedTimer *t, int i)
|
||||
{
|
||||
return t->match[i] < t->reload ? t->match[i] : 0;
|
||||
}
|
||||
|
||||
static uint64_t calculate_next(struct AspeedTimer *t)
|
||||
{
|
||||
uint64_t next = 0;
|
||||
uint32_t rate = calculate_rate(t);
|
||||
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
uint64_t next;
|
||||
|
||||
while (!next) {
|
||||
/* We don't know the relationship between the values in the match
|
||||
* registers, so sort using MAX/MIN/zero. We sort in that order as the
|
||||
* timer counts down to zero. */
|
||||
uint64_t seq[] = {
|
||||
calculate_time(t, MAX(t->match[0], t->match[1])),
|
||||
calculate_time(t, MIN(t->match[0], t->match[1])),
|
||||
calculate_time(t, 0),
|
||||
};
|
||||
uint64_t reload_ns;
|
||||
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
/*
|
||||
* We don't know the relationship between the values in the match
|
||||
* registers, so sort using MAX/MIN/zero. We sort in that order as
|
||||
* the timer counts down to zero.
|
||||
*/
|
||||
|
||||
if (now < seq[0]) {
|
||||
next = seq[0];
|
||||
} else if (now < seq[1]) {
|
||||
next = seq[1];
|
||||
} else if (now < seq[2]) {
|
||||
next = seq[2];
|
||||
} else if (t->reload) {
|
||||
reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
|
||||
t->start = now - ((now - t->start) % reload_ns);
|
||||
} else {
|
||||
/* no reload value, return 0 */
|
||||
break;
|
||||
}
|
||||
next = calculate_time(t, MAX(calculate_match(t, 0), calculate_match(t, 1)));
|
||||
if (now < next) {
|
||||
return next;
|
||||
}
|
||||
|
||||
return next;
|
||||
next = calculate_time(t, MIN(calculate_match(t, 0), calculate_match(t, 1)));
|
||||
if (now < next) {
|
||||
return next;
|
||||
}
|
||||
|
||||
next = calculate_time(t, 0);
|
||||
if (now < next) {
|
||||
return next;
|
||||
}
|
||||
|
||||
/* We've missed all deadlines, fire interrupt and try again */
|
||||
timer_del(&t->timer);
|
||||
|
||||
if (timer_overflow_interrupt(t)) {
|
||||
t->level = !t->level;
|
||||
qemu_set_irq(t->irq, t->level);
|
||||
}
|
||||
|
||||
next = MAX(MAX(calculate_match(t, 0), calculate_match(t, 1)), 0);
|
||||
t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
return calculate_time(t, next);
|
||||
}
|
||||
|
||||
static void aspeed_timer_mod(AspeedTimer *t)
|
||||
@@ -184,7 +194,11 @@ static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg)
|
||||
|
||||
switch (reg) {
|
||||
case TIMER_REG_STATUS:
|
||||
value = calculate_ticks(t, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
|
||||
if (timer_enabled(t)) {
|
||||
value = calculate_ticks(t, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
|
||||
} else {
|
||||
value = t->reload;
|
||||
}
|
||||
break;
|
||||
case TIMER_REG_RELOAD:
|
||||
value = t->reload;
|
||||
@@ -261,7 +275,11 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
|
||||
int64_t delta = (int64_t) value - (int64_t) calculate_ticks(t, now);
|
||||
uint32_t rate = calculate_rate(t);
|
||||
|
||||
t->start += muldiv64(delta, NANOSECONDS_PER_SECOND, rate);
|
||||
if (delta >= 0) {
|
||||
t->start += muldiv64(delta, NANOSECONDS_PER_SECOND, rate);
|
||||
} else {
|
||||
t->start -= muldiv64(-delta, NANOSECONDS_PER_SECOND, rate);
|
||||
}
|
||||
aspeed_timer_mod(t);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -66,6 +66,10 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A
|
||||
cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset"
|
||||
|
||||
# hw/timer/aspeed-rtc.c
|
||||
aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64
|
||||
aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64
|
||||
|
||||
# sun4v-rtc.c
|
||||
sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64
|
||||
sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64
|
||||
|
||||
160
hw/vfio/ccw.c
160
hw/vfio/ccw.c
@@ -2,9 +2,12 @@
|
||||
* vfio based subchannel assignment support
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
* Copyright 2019 Red Hat, Inc.
|
||||
*
|
||||
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
|
||||
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
|
||||
* Pierre Morel <pmorel@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cohuck@redhat.com>
|
||||
*
|
||||
* 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
|
||||
@@ -33,6 +36,9 @@ struct VFIOCCWDevice {
|
||||
uint64_t io_region_size;
|
||||
uint64_t io_region_offset;
|
||||
struct ccw_io_region *io_region;
|
||||
uint64_t async_cmd_region_size;
|
||||
uint64_t async_cmd_region_offset;
|
||||
struct ccw_cmd_region *async_cmd_region;
|
||||
EventNotifier io_notifier;
|
||||
bool force_orb_pfch;
|
||||
bool warned_orb_pfch;
|
||||
@@ -115,6 +121,87 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
static int vfio_ccw_handle_clear(SubchDev *sch)
|
||||
{
|
||||
S390CCWDevice *cdev = sch->driver_data;
|
||||
VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
|
||||
struct ccw_cmd_region *region = vcdev->async_cmd_region;
|
||||
int ret;
|
||||
|
||||
if (!vcdev->async_cmd_region) {
|
||||
/* Async command region not available, fall back to emulation */
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
memset(region, 0, sizeof(*region));
|
||||
region->command = VFIO_CCW_ASYNC_CMD_CSCH;
|
||||
|
||||
again:
|
||||
ret = pwrite(vcdev->vdev.fd, region,
|
||||
vcdev->async_cmd_region_size, vcdev->async_cmd_region_offset);
|
||||
if (ret != vcdev->async_cmd_region_size) {
|
||||
if (errno == EAGAIN) {
|
||||
goto again;
|
||||
}
|
||||
error_report("vfio-ccw: write cmd region failed with errno=%d", errno);
|
||||
ret = -errno;
|
||||
} else {
|
||||
ret = region->ret_code;
|
||||
}
|
||||
switch (ret) {
|
||||
case 0:
|
||||
case -ENODEV:
|
||||
case -EACCES:
|
||||
return 0;
|
||||
case -EFAULT:
|
||||
default:
|
||||
sch_gen_unit_exception(sch);
|
||||
css_inject_io_interrupt(sch);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int vfio_ccw_handle_halt(SubchDev *sch)
|
||||
{
|
||||
S390CCWDevice *cdev = sch->driver_data;
|
||||
VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
|
||||
struct ccw_cmd_region *region = vcdev->async_cmd_region;
|
||||
int ret;
|
||||
|
||||
if (!vcdev->async_cmd_region) {
|
||||
/* Async command region not available, fall back to emulation */
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
memset(region, 0, sizeof(*region));
|
||||
region->command = VFIO_CCW_ASYNC_CMD_HSCH;
|
||||
|
||||
again:
|
||||
ret = pwrite(vcdev->vdev.fd, region,
|
||||
vcdev->async_cmd_region_size, vcdev->async_cmd_region_offset);
|
||||
if (ret != vcdev->async_cmd_region_size) {
|
||||
if (errno == EAGAIN) {
|
||||
goto again;
|
||||
}
|
||||
error_report("vfio-ccw: write cmd region failed with errno=%d", errno);
|
||||
ret = -errno;
|
||||
} else {
|
||||
ret = region->ret_code;
|
||||
}
|
||||
switch (ret) {
|
||||
case 0:
|
||||
case -EBUSY:
|
||||
case -ENODEV:
|
||||
case -EACCES:
|
||||
return 0;
|
||||
case -EFAULT:
|
||||
default:
|
||||
sch_gen_unit_exception(sch);
|
||||
css_inject_io_interrupt(sch);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void vfio_ccw_reset(DeviceState *dev)
|
||||
{
|
||||
CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
|
||||
@@ -198,9 +285,8 @@ static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp)
|
||||
{
|
||||
VFIODevice *vdev = &vcdev->vdev;
|
||||
struct vfio_irq_info *irq_info;
|
||||
struct vfio_irq_set *irq_set;
|
||||
size_t argsz;
|
||||
int32_t *pfd;
|
||||
int fd;
|
||||
|
||||
if (vdev->num_irqs < VFIO_CCW_IO_IRQ_INDEX + 1) {
|
||||
error_setg(errp, "vfio: unexpected number of io irqs %u",
|
||||
@@ -224,56 +310,32 @@ static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp)
|
||||
goto out_free_info;
|
||||
}
|
||||
|
||||
argsz = sizeof(*irq_set) + sizeof(*pfd);
|
||||
irq_set = g_malloc0(argsz);
|
||||
irq_set->argsz = argsz;
|
||||
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
|
||||
VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irq_set->index = VFIO_CCW_IO_IRQ_INDEX;
|
||||
irq_set->start = 0;
|
||||
irq_set->count = 1;
|
||||
pfd = (int32_t *) &irq_set->data;
|
||||
fd = event_notifier_get_fd(&vcdev->io_notifier);
|
||||
qemu_set_fd_handler(fd, vfio_ccw_io_notifier_handler, NULL, vcdev);
|
||||
|
||||
*pfd = event_notifier_get_fd(&vcdev->io_notifier);
|
||||
qemu_set_fd_handler(*pfd, vfio_ccw_io_notifier_handler, NULL, vcdev);
|
||||
if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
|
||||
error_setg(errp, "vfio: Failed to set up io notification");
|
||||
qemu_set_fd_handler(*pfd, NULL, NULL, vcdev);
|
||||
if (vfio_set_irq_signaling(vdev, VFIO_CCW_IO_IRQ_INDEX, 0,
|
||||
VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) {
|
||||
qemu_set_fd_handler(fd, NULL, NULL, vcdev);
|
||||
event_notifier_cleanup(&vcdev->io_notifier);
|
||||
}
|
||||
|
||||
g_free(irq_set);
|
||||
|
||||
out_free_info:
|
||||
g_free(irq_info);
|
||||
}
|
||||
|
||||
static void vfio_ccw_unregister_io_notifier(VFIOCCWDevice *vcdev)
|
||||
{
|
||||
struct vfio_irq_set *irq_set;
|
||||
size_t argsz;
|
||||
int32_t *pfd;
|
||||
Error *err = NULL;
|
||||
|
||||
argsz = sizeof(*irq_set) + sizeof(*pfd);
|
||||
irq_set = g_malloc0(argsz);
|
||||
irq_set->argsz = argsz;
|
||||
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
|
||||
VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irq_set->index = VFIO_CCW_IO_IRQ_INDEX;
|
||||
irq_set->start = 0;
|
||||
irq_set->count = 1;
|
||||
pfd = (int32_t *) &irq_set->data;
|
||||
*pfd = -1;
|
||||
|
||||
if (ioctl(vcdev->vdev.fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
|
||||
error_report("vfio: Failed to de-assign device io fd: %m");
|
||||
vfio_set_irq_signaling(&vcdev->vdev, VFIO_CCW_IO_IRQ_INDEX, 0,
|
||||
VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err);
|
||||
if (err) {
|
||||
error_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name);
|
||||
}
|
||||
|
||||
qemu_set_fd_handler(event_notifier_get_fd(&vcdev->io_notifier),
|
||||
NULL, NULL, vcdev);
|
||||
event_notifier_cleanup(&vcdev->io_notifier);
|
||||
|
||||
g_free(irq_set);
|
||||
}
|
||||
|
||||
static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
|
||||
@@ -288,9 +350,13 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We always expect at least the I/O region to be present. We also
|
||||
* may have a variable number of regions governed by capabilities.
|
||||
*/
|
||||
if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) {
|
||||
error_setg(errp, "vfio: Unexpected number of the I/O region %u",
|
||||
vdev->num_regions);
|
||||
error_setg(errp, "vfio: too few regions (%u), expected at least %u",
|
||||
vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -310,11 +376,27 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
|
||||
vcdev->io_region_offset = info->offset;
|
||||
vcdev->io_region = g_malloc0(info->size);
|
||||
|
||||
/* check for the optional async command region */
|
||||
ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW,
|
||||
VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &info);
|
||||
if (!ret) {
|
||||
vcdev->async_cmd_region_size = info->size;
|
||||
if (sizeof(*vcdev->async_cmd_region) != vcdev->async_cmd_region_size) {
|
||||
error_setg(errp, "vfio: Unexpected size of the async cmd region");
|
||||
g_free(vcdev->io_region);
|
||||
g_free(info);
|
||||
return;
|
||||
}
|
||||
vcdev->async_cmd_region_offset = info->offset;
|
||||
vcdev->async_cmd_region = g_malloc0(info->size);
|
||||
}
|
||||
|
||||
g_free(info);
|
||||
}
|
||||
|
||||
static void vfio_ccw_put_region(VFIOCCWDevice *vcdev)
|
||||
{
|
||||
g_free(vcdev->async_cmd_region);
|
||||
g_free(vcdev->io_region);
|
||||
}
|
||||
|
||||
@@ -487,6 +569,8 @@ static void vfio_ccw_class_init(ObjectClass *klass, void *data)
|
||||
dc->reset = vfio_ccw_reset;
|
||||
|
||||
cdc->handle_request = vfio_ccw_handle_request;
|
||||
cdc->handle_halt = vfio_ccw_handle_halt;
|
||||
cdc->handle_clear = vfio_ccw_handle_clear;
|
||||
}
|
||||
|
||||
static const TypeInfo vfio_ccw_info = {
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
|
||||
#define WDT_RESTART_MAGIC 0x4755
|
||||
|
||||
#define SCU_RESET_CONTROL1 (0x04 / 4)
|
||||
#define SCU_RESET_SDRAM BIT(0)
|
||||
|
||||
static bool aspeed_wdt_is_enabled(const AspeedWDTState *s)
|
||||
{
|
||||
return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE;
|
||||
@@ -222,6 +225,13 @@ static void aspeed_wdt_timer_expired(void *dev)
|
||||
{
|
||||
AspeedWDTState *s = ASPEED_WDT(dev);
|
||||
|
||||
/* Do not reset on SDRAM controller reset */
|
||||
if (s->scu->regs[SCU_RESET_CONTROL1] & SCU_RESET_SDRAM) {
|
||||
timer_del(s->timer);
|
||||
s->regs[WDT_CTRL] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
|
||||
watchdog_perform_action();
|
||||
timer_del(s->timer);
|
||||
@@ -233,6 +243,16 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
AspeedWDTState *s = ASPEED_WDT(dev);
|
||||
Error *err = NULL;
|
||||
Object *obj;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "scu", &err);
|
||||
if (!obj) {
|
||||
error_propagate(errp, err);
|
||||
error_prepend(errp, "required link 'scu' not found: ");
|
||||
return;
|
||||
}
|
||||
s->scu = ASPEED_SCU(obj);
|
||||
|
||||
if (!is_supported_silicon_rev(s->silicon_rev)) {
|
||||
error_setg(errp, "Unknown silicon revision: 0x%" PRIx32,
|
||||
|
||||
@@ -449,7 +449,8 @@ int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base,
|
||||
int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
||||
int64_t *pnum);
|
||||
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
||||
int64_t offset, int64_t bytes, int64_t *pnum);
|
||||
bool include_base, int64_t offset, int64_t bytes,
|
||||
int64_t *pnum);
|
||||
|
||||
bool bdrv_is_read_only(BlockDriverState *bs);
|
||||
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
|
||||
|
||||
@@ -1757,6 +1757,16 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name);
|
||||
*/
|
||||
void address_space_destroy(AddressSpace *as);
|
||||
|
||||
/**
|
||||
* address_space_remove_listeners: unregister all listeners of an address space
|
||||
*
|
||||
* Removes all callbacks previously registered with memory_listener_register()
|
||||
* for @as.
|
||||
*
|
||||
* @as: an initialized #AddressSpace
|
||||
*/
|
||||
void address_space_remove_listeners(AddressSpace *as);
|
||||
|
||||
/**
|
||||
* address_space_rw: read from or write to an address space.
|
||||
*
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
#include "hw/intc/aspeed_vic.h"
|
||||
#include "hw/misc/aspeed_scu.h"
|
||||
#include "hw/misc/aspeed_sdmc.h"
|
||||
#include "hw/misc/aspeed_xdma.h"
|
||||
#include "hw/timer/aspeed_timer.h"
|
||||
#include "hw/timer/aspeed_rtc.h"
|
||||
#include "hw/i2c/aspeed_i2c.h"
|
||||
#include "hw/ssi/aspeed_smc.h"
|
||||
#include "hw/watchdog/wdt_aspeed.h"
|
||||
@@ -23,23 +25,28 @@
|
||||
|
||||
#define ASPEED_SPIS_NUM 2
|
||||
#define ASPEED_WDTS_NUM 3
|
||||
#define ASPEED_CPUS_NUM 2
|
||||
#define ASPEED_MACS_NUM 2
|
||||
|
||||
typedef struct AspeedSoCState {
|
||||
/*< private >*/
|
||||
DeviceState parent;
|
||||
|
||||
/*< public >*/
|
||||
ARMCPU cpu;
|
||||
ARMCPU cpu[ASPEED_CPUS_NUM];
|
||||
uint32_t num_cpus;
|
||||
MemoryRegion sram;
|
||||
AspeedVICState vic;
|
||||
AspeedRtcState rtc;
|
||||
AspeedTimerCtrlState timerctrl;
|
||||
AspeedI2CState i2c;
|
||||
AspeedSCUState scu;
|
||||
AspeedXDMAState xdma;
|
||||
AspeedSMCState fmc;
|
||||
AspeedSMCState spi[ASPEED_SPIS_NUM];
|
||||
AspeedSDMCState sdmc;
|
||||
AspeedWDTState wdt[ASPEED_WDTS_NUM];
|
||||
FTGMAC100State ftgmac100;
|
||||
FTGMAC100State ftgmac100[ASPEED_MACS_NUM];
|
||||
} AspeedSoCState;
|
||||
|
||||
#define TYPE_ASPEED_SOC "aspeed-soc"
|
||||
@@ -49,13 +56,14 @@ typedef struct AspeedSoCInfo {
|
||||
const char *name;
|
||||
const char *cpu_type;
|
||||
uint32_t silicon_rev;
|
||||
hwaddr sdram_base;
|
||||
uint64_t sram_size;
|
||||
int spis_num;
|
||||
const hwaddr *spi_bases;
|
||||
const char *fmc_typename;
|
||||
const char **spi_typename;
|
||||
int wdts_num;
|
||||
const int *irqmap;
|
||||
const hwaddr *memmap;
|
||||
uint32_t num_cpus;
|
||||
} AspeedSoCInfo;
|
||||
|
||||
typedef struct AspeedSoCClass {
|
||||
@@ -68,4 +76,41 @@ typedef struct AspeedSoCClass {
|
||||
#define ASPEED_SOC_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(AspeedSoCClass, (obj), TYPE_ASPEED_SOC)
|
||||
|
||||
enum {
|
||||
ASPEED_IOMEM,
|
||||
ASPEED_UART1,
|
||||
ASPEED_UART2,
|
||||
ASPEED_UART3,
|
||||
ASPEED_UART4,
|
||||
ASPEED_UART5,
|
||||
ASPEED_VUART,
|
||||
ASPEED_FMC,
|
||||
ASPEED_SPI1,
|
||||
ASPEED_SPI2,
|
||||
ASPEED_VIC,
|
||||
ASPEED_SDMC,
|
||||
ASPEED_SCU,
|
||||
ASPEED_ADC,
|
||||
ASPEED_SRAM,
|
||||
ASPEED_GPIO,
|
||||
ASPEED_RTC,
|
||||
ASPEED_TIMER1,
|
||||
ASPEED_TIMER2,
|
||||
ASPEED_TIMER3,
|
||||
ASPEED_TIMER4,
|
||||
ASPEED_TIMER5,
|
||||
ASPEED_TIMER6,
|
||||
ASPEED_TIMER7,
|
||||
ASPEED_TIMER8,
|
||||
ASPEED_WDT,
|
||||
ASPEED_PWM,
|
||||
ASPEED_LPC,
|
||||
ASPEED_IBT,
|
||||
ASPEED_I2C,
|
||||
ASPEED_ETH1,
|
||||
ASPEED_ETH2,
|
||||
ASPEED_SDRAM,
|
||||
ASPEED_XDMA,
|
||||
};
|
||||
|
||||
#endif /* ASPEED_SOC_H */
|
||||
|
||||
@@ -125,6 +125,9 @@ enum FslIMX7MemoryMap {
|
||||
FSL_IMX7_ADC2_ADDR = 0x30620000,
|
||||
FSL_IMX7_ADCn_SIZE = 0x1000,
|
||||
|
||||
FSL_IMX7_PCIE_PHY_ADDR = 0x306D0000,
|
||||
FSL_IMX7_PCIE_PHY_SIZE = 0x10000,
|
||||
|
||||
FSL_IMX7_GPC_ADDR = 0x303A0000,
|
||||
|
||||
FSL_IMX7_I2C1_ADDR = 0x30A20000,
|
||||
@@ -179,6 +182,9 @@ enum FslIMX7MemoryMap {
|
||||
FSL_IMX7_PCIE_REG_SIZE = 16 * 1024,
|
||||
|
||||
FSL_IMX7_GPR_ADDR = 0x30340000,
|
||||
|
||||
FSL_IMX7_DMA_APBH_ADDR = 0x33000000,
|
||||
FSL_IMX7_DMA_APBH_SIZE = 0x2000,
|
||||
};
|
||||
|
||||
enum FslIMX7IRQs {
|
||||
@@ -207,10 +213,10 @@ enum FslIMX7IRQs {
|
||||
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_PCI_INTA_IRQ = 125,
|
||||
FSL_IMX7_PCI_INTB_IRQ = 124,
|
||||
FSL_IMX7_PCI_INTC_IRQ = 123,
|
||||
FSL_IMX7_PCI_INTD_IRQ = 122,
|
||||
|
||||
FSL_IMX7_UART7_IRQ = 126,
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "hw/i2c/i2c.h"
|
||||
|
||||
typedef struct bitbang_i2c_interface bitbang_i2c_interface;
|
||||
|
||||
#define BITBANG_I2C_SDA 0
|
||||
#define BITBANG_I2C_SCL 1
|
||||
|
||||
@@ -81,8 +81,6 @@ uint8_t i2c_recv(I2CBus *bus);
|
||||
|
||||
DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr);
|
||||
|
||||
typedef struct bitbang_i2c_interface bitbang_i2c_interface;
|
||||
|
||||
/* lm832x.c */
|
||||
void lm832x_key_event(DeviceState *dev, int key, int state);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#define PPC4XX_I2C_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/i2c/bitbang_i2c.h"
|
||||
|
||||
#define TYPE_PPC4xx_I2C "ppc4xx-i2c"
|
||||
#define PPC4xx_I2C(obj) OBJECT_CHECK(PPC4xxI2CState, (obj), TYPE_PPC4xx_I2C)
|
||||
|
||||
30
include/hw/misc/aspeed_xdma.h
Normal file
30
include/hw/misc/aspeed_xdma.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* ASPEED XDMA Controller
|
||||
* Eddie James <eajames@linux.ibm.com>
|
||||
*
|
||||
* Copyright (C) 2019 IBM Corp.
|
||||
* SPDX-License-Identifer: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef ASPEED_XDMA_H
|
||||
#define ASPEED_XDMA_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_ASPEED_XDMA "aspeed.xdma"
|
||||
#define ASPEED_XDMA(obj) OBJECT_CHECK(AspeedXDMAState, (obj), TYPE_ASPEED_XDMA)
|
||||
|
||||
#define ASPEED_XDMA_NUM_REGS (ASPEED_XDMA_REG_SIZE / sizeof(uint32_t))
|
||||
#define ASPEED_XDMA_REG_SIZE 0x7C
|
||||
|
||||
typedef struct AspeedXDMAState {
|
||||
SysBusDevice parent;
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
|
||||
char bmc_cmdq_readp_set;
|
||||
uint32_t regs[ASPEED_XDMA_NUM_REGS];
|
||||
} AspeedXDMAState;
|
||||
|
||||
#endif /* ASPEED_XDMA_H */
|
||||
@@ -56,7 +56,6 @@ typedef struct PnvChip {
|
||||
uint64_t cores_mask;
|
||||
void *cores;
|
||||
|
||||
hwaddr xscom_base;
|
||||
MemoryRegion xscom_mmio;
|
||||
MemoryRegion xscom;
|
||||
AddressSpace xscom_as;
|
||||
@@ -105,8 +104,6 @@ typedef struct PnvChipClass {
|
||||
uint64_t chip_cfam_id;
|
||||
uint64_t cores_mask;
|
||||
|
||||
hwaddr xscom_base;
|
||||
|
||||
DeviceRealize parent_realize;
|
||||
|
||||
uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
|
||||
@@ -199,7 +196,7 @@ void pnv_bmc_powerdown(IPMIBmc *bmc);
|
||||
*/
|
||||
#define PNV_XSCOM_SIZE 0x800000000ull
|
||||
#define PNV_XSCOM_BASE(chip) \
|
||||
(chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
|
||||
(0x0003fc0000000000ull + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
|
||||
|
||||
/*
|
||||
* XSCOM 0x20109CA defines the ICP BAR:
|
||||
@@ -256,4 +253,7 @@ void pnv_bmc_powerdown(IPMIBmc *bmc);
|
||||
#define PNV9_PSIHB_ESB_SIZE 0x0000000000010000ull
|
||||
#define PNV9_PSIHB_ESB_BASE(chip) PNV9_CHIP_BASE(chip, 0x00060302031c0000ull)
|
||||
|
||||
#define PNV9_XSCOM_SIZE 0x0000000400000000ull
|
||||
#define PNV9_XSCOM_BASE(chip) PNV9_CHIP_BASE(chip, 0x00603fc00000000ull)
|
||||
|
||||
#endif /* PPC_PNV_H */
|
||||
|
||||
@@ -87,7 +87,7 @@ typedef struct PnvXScomInterfaceClass {
|
||||
#define PNV9_XSCOM_XIVE_BASE 0x5013000
|
||||
#define PNV9_XSCOM_XIVE_SIZE 0x300
|
||||
|
||||
extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
|
||||
extern void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp);
|
||||
extern int pnv_dt_xscom(PnvChip *chip, void *fdt, int offset);
|
||||
|
||||
extern void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset,
|
||||
|
||||
@@ -676,10 +676,6 @@ typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, SpaprMachineState *sm,
|
||||
uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets);
|
||||
void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn);
|
||||
static inline void spapr_rtas_unregister(int token)
|
||||
{
|
||||
spapr_rtas_register(token, NULL, NULL);
|
||||
}
|
||||
target_ulong spapr_rtas_call(PowerPCCPU *cpu, SpaprMachineState *sm,
|
||||
uint32_t token, uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets);
|
||||
|
||||
@@ -48,7 +48,6 @@ typedef struct SpaprIrq {
|
||||
void (*reset)(SpaprMachineState *spapr, Error **errp);
|
||||
void (*set_irq)(void *opaque, int srcno, int val);
|
||||
const char *(*get_nodename)(SpaprMachineState *spapr);
|
||||
void (*init_emu)(SpaprMachineState *spapr, Error **errp);
|
||||
void (*init_kvm)(SpaprMachineState *spapr, Error **errp);
|
||||
} SpaprIrq;
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ typedef struct SpaprXive {
|
||||
/* KVM support */
|
||||
int fd;
|
||||
void *tm_mmap;
|
||||
MemoryRegion tm_mmio_kvm;
|
||||
VMChangeStateEntry *change;
|
||||
} SpaprXive;
|
||||
|
||||
@@ -66,7 +67,6 @@ void spapr_xive_map_mmio(SpaprXive *xive);
|
||||
|
||||
int spapr_xive_end_to_target(uint8_t end_blk, uint32_t end_idx,
|
||||
uint32_t *out_server, uint8_t *out_prio);
|
||||
void spapr_xive_init(SpaprXive *xive, Error **errp);
|
||||
|
||||
/*
|
||||
* KVM XIVE device helpers
|
||||
|
||||
@@ -119,7 +119,6 @@ struct ICSState {
|
||||
uint32_t offset;
|
||||
ICSIRQState *irqs;
|
||||
XICSFabric *xics;
|
||||
bool init; /* sPAPR ICS device initialized */
|
||||
};
|
||||
|
||||
#define ICS_PROP_XICS "xics"
|
||||
@@ -191,13 +190,13 @@ Object *icp_create(Object *cpu, const char *type, XICSFabric *xi,
|
||||
|
||||
/* KVM */
|
||||
void icp_get_kvm_state(ICPState *icp);
|
||||
int icp_set_kvm_state(ICPState *icp);
|
||||
int icp_set_kvm_state(ICPState *icp, Error **errp);
|
||||
void icp_synchronize_state(ICPState *icp);
|
||||
void icp_kvm_realize(DeviceState *dev, Error **errp);
|
||||
|
||||
void ics_get_kvm_state(ICSState *ics);
|
||||
int ics_set_kvm_state_one(ICSState *ics, int srcno);
|
||||
int ics_set_kvm_state(ICSState *ics);
|
||||
int ics_set_kvm_state_one(ICSState *ics, int srcno, Error **errp);
|
||||
int ics_set_kvm_state(ICSState *ics, Error **errp);
|
||||
void ics_synchronize_state(ICSState *ics);
|
||||
void ics_kvm_set_irq(ICSState *ics, int srcno, int val);
|
||||
|
||||
|
||||
@@ -33,8 +33,9 @@
|
||||
|
||||
void spapr_dt_xics(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt,
|
||||
uint32_t phandle);
|
||||
int xics_kvm_init(SpaprMachineState *spapr, Error **errp);
|
||||
int xics_kvm_connect(SpaprMachineState *spapr, Error **errp);
|
||||
void xics_kvm_disconnect(SpaprMachineState *spapr, Error **errp);
|
||||
bool xics_kvm_has_broken_disconnect(SpaprMachineState *spapr);
|
||||
void xics_spapr_init(SpaprMachineState *spapr);
|
||||
|
||||
#endif /* XICS_SPAPR_H */
|
||||
|
||||
@@ -197,6 +197,7 @@ typedef struct XiveSource {
|
||||
|
||||
/* KVM support */
|
||||
void *esb_mmap;
|
||||
MemoryRegion esb_mmio_kvm;
|
||||
|
||||
XiveNotifier *xive;
|
||||
} XiveSource;
|
||||
|
||||
@@ -215,6 +215,9 @@ IOInstEnding s390_ccw_cmd_request(SubchDev *sch);
|
||||
IOInstEnding do_subchannel_work_virtual(SubchDev *sub);
|
||||
IOInstEnding do_subchannel_work_passthrough(SubchDev *sub);
|
||||
|
||||
int s390_ccw_halt(SubchDev *sch);
|
||||
int s390_ccw_clear(SubchDev *sch);
|
||||
|
||||
typedef enum {
|
||||
CSS_IO_ADAPTER_VIRTIO = 0,
|
||||
CSS_IO_ADAPTER_PCI = 1,
|
||||
|
||||
@@ -35,6 +35,8 @@ typedef struct S390CCWDeviceClass {
|
||||
void (*realize)(S390CCWDevice *dev, char *sysfsdev, Error **errp);
|
||||
void (*unrealize)(S390CCWDevice *dev, Error **errp);
|
||||
IOInstEnding (*handle_request) (SubchDev *sch);
|
||||
int (*handle_halt) (SubchDev *sch);
|
||||
int (*handle_clear) (SubchDev *sch);
|
||||
} S390CCWDeviceClass;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -97,6 +97,9 @@ typedef struct AspeedSMCState {
|
||||
uint8_t r_timings;
|
||||
uint8_t conf_enable_w0;
|
||||
|
||||
/* for DMA support */
|
||||
uint64_t sdram_base;
|
||||
|
||||
AspeedSMCFlash *flashes;
|
||||
|
||||
uint8_t snoop_index;
|
||||
|
||||
31
include/hw/timer/aspeed_rtc.h
Normal file
31
include/hw/timer/aspeed_rtc.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* ASPEED Real Time Clock
|
||||
* Joel Stanley <joel@jms.id.au>
|
||||
*
|
||||
* Copyright 2019 IBM Corp
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef ASPEED_RTC_H
|
||||
#define ASPEED_RTC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
typedef struct AspeedRtcState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t reg[0x18];
|
||||
int offset;
|
||||
|
||||
} AspeedRtcState;
|
||||
|
||||
#define TYPE_ASPEED_RTC "aspeed.rtc"
|
||||
#define ASPEED_RTC(obj) OBJECT_CHECK(AspeedRtcState, (obj), TYPE_ASPEED_RTC)
|
||||
|
||||
#endif /* ASPEED_RTC_H */
|
||||
@@ -27,6 +27,7 @@ typedef struct AspeedWDTState {
|
||||
MemoryRegion iomem;
|
||||
uint32_t regs[ASPEED_WDT_REGS_MAX];
|
||||
|
||||
AspeedSCUState *scu;
|
||||
uint32_t pclk_freq;
|
||||
uint32_t silicon_rev;
|
||||
uint32_t ext_pulse_width_mask;
|
||||
|
||||
@@ -22,8 +22,12 @@ struct AnnounceTimer {
|
||||
/* Returns: update the timer to the next time point */
|
||||
int64_t qemu_announce_timer_step(AnnounceTimer *timer);
|
||||
|
||||
/* Delete the underlying timer */
|
||||
void qemu_announce_timer_del(AnnounceTimer *timer);
|
||||
/*
|
||||
* Delete the underlying timer and other data
|
||||
* If 'free_named' true and the timer is a named timer, then remove
|
||||
* it from the list of named timers and free the AnnounceTimer itself.
|
||||
*/
|
||||
void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named);
|
||||
|
||||
/*
|
||||
* Under BQL/main thread
|
||||
|
||||
7
memory.c
7
memory.c
@@ -2723,6 +2723,13 @@ void memory_listener_unregister(MemoryListener *listener)
|
||||
listener->address_space = NULL;
|
||||
}
|
||||
|
||||
void address_space_remove_listeners(AddressSpace *as)
|
||||
{
|
||||
while (!QTAILQ_EMPTY(&as->listeners)) {
|
||||
memory_listener_unregister(QTAILQ_FIRST(&as->listeners));
|
||||
}
|
||||
}
|
||||
|
||||
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
|
||||
{
|
||||
memory_region_ref(root);
|
||||
|
||||
@@ -259,6 +259,8 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
|
||||
void qmp_xen_colo_do_checkpoint(Error **errp)
|
||||
{
|
||||
replication_do_checkpoint_all(errp);
|
||||
/* Notify all filters of all NIC to do checkpoint */
|
||||
colo_notify_filters_event(COLO_EVENT_CHECKPOINT, errp);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "monitor/monitor-internal.h"
|
||||
#include "monitor/qdev.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
#include "qapi/qapi-commands-block.h"
|
||||
@@ -38,6 +39,7 @@
|
||||
#include "qapi/qapi-commands-run-state.h"
|
||||
#include "qapi/qapi-commands-tpm.h"
|
||||
#include "qapi/qapi-commands-ui.h"
|
||||
#include "qapi/qapi-visit-net.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/string-input-visitor.h"
|
||||
@@ -67,6 +69,32 @@ static void hmp_handle_error(Monitor *mon, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Produce a strList from a comma separated list.
|
||||
* A NULL or empty input string return NULL.
|
||||
*/
|
||||
static strList *strList_from_comma_list(const char *in)
|
||||
{
|
||||
strList *res = NULL;
|
||||
strList **hook = &res;
|
||||
|
||||
while (in && in[0]) {
|
||||
char *comma = strchr(in, ',');
|
||||
*hook = g_new0(strList, 1);
|
||||
|
||||
if (comma) {
|
||||
(*hook)->value = g_strndup(in, comma - in);
|
||||
in = comma + 1; /* skip the , */
|
||||
} else {
|
||||
(*hook)->value = g_strdup(in);
|
||||
in = NULL;
|
||||
}
|
||||
hook = &(*hook)->next;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void hmp_info_name(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
NameInfo *info;
|
||||
@@ -1631,7 +1659,18 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
|
||||
|
||||
void hmp_announce_self(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
qmp_announce_self(migrate_announce_params(), NULL);
|
||||
const char *interfaces_str = qdict_get_try_str(qdict, "interfaces");
|
||||
const char *id = qdict_get_try_str(qdict, "id");
|
||||
AnnounceParameters *params = QAPI_CLONE(AnnounceParameters,
|
||||
migrate_announce_params());
|
||||
|
||||
qapi_free_strList(params->interfaces);
|
||||
params->interfaces = strList_from_comma_list(interfaces_str);
|
||||
params->has_interfaces = params->interfaces != NULL;
|
||||
params->id = g_strdup(id);
|
||||
params->has_id = !!params->id;
|
||||
qmp_announce_self(params, NULL);
|
||||
qapi_free_AnnounceParameters(params);
|
||||
}
|
||||
|
||||
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "qapi/qapi-commands-net.h"
|
||||
#include "trace.h"
|
||||
|
||||
static GData *named_timers;
|
||||
|
||||
int64_t qemu_announce_timer_step(AnnounceTimer *timer)
|
||||
{
|
||||
int64_t step;
|
||||
@@ -31,13 +33,38 @@ int64_t qemu_announce_timer_step(AnnounceTimer *timer)
|
||||
return step;
|
||||
}
|
||||
|
||||
void qemu_announce_timer_del(AnnounceTimer *timer)
|
||||
/*
|
||||
* If 'free_named' is true, then remove the timer from the list
|
||||
* and free the timer itself.
|
||||
*/
|
||||
void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named)
|
||||
{
|
||||
bool free_timer = false;
|
||||
if (timer->tm) {
|
||||
timer_del(timer->tm);
|
||||
timer_free(timer->tm);
|
||||
timer->tm = NULL;
|
||||
}
|
||||
qapi_free_strList(timer->params.interfaces);
|
||||
timer->params.interfaces = NULL;
|
||||
if (free_named && timer->params.has_id) {
|
||||
AnnounceTimer *list_timer;
|
||||
/*
|
||||
* Sanity check: There should only be one timer on the list with
|
||||
* the id.
|
||||
*/
|
||||
list_timer = g_datalist_get_data(&named_timers, timer->params.id);
|
||||
assert(timer == list_timer);
|
||||
free_timer = true;
|
||||
g_datalist_remove_data(&named_timers, timer->params.id);
|
||||
}
|
||||
trace_qemu_announce_timer_del(free_named, free_timer, timer->params.id);
|
||||
g_free(timer->params.id);
|
||||
timer->params.id = NULL;
|
||||
|
||||
if (free_timer) {
|
||||
g_free(timer);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -54,7 +81,7 @@ void qemu_announce_timer_reset(AnnounceTimer *timer,
|
||||
* We're under the BQL, so the current timer can't
|
||||
* be firing, so we should be able to delete it.
|
||||
*/
|
||||
qemu_announce_timer_del(timer);
|
||||
qemu_announce_timer_del(timer, false);
|
||||
|
||||
QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
|
||||
timer->round = params->rounds;
|
||||
@@ -96,29 +123,53 @@ static int announce_self_create(uint8_t *buf,
|
||||
|
||||
static void qemu_announce_self_iter(NICState *nic, void *opaque)
|
||||
{
|
||||
AnnounceTimer *timer = opaque;
|
||||
uint8_t buf[60];
|
||||
int len;
|
||||
bool skip;
|
||||
|
||||
trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr));
|
||||
len = announce_self_create(buf, nic->conf->macaddr.a);
|
||||
if (timer->params.has_interfaces) {
|
||||
strList *entry = timer->params.interfaces;
|
||||
/* Skip unless we find our name in the requested list */
|
||||
skip = true;
|
||||
|
||||
qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
|
||||
while (entry) {
|
||||
if (!strcmp(entry->value, nic->ncs->name)) {
|
||||
/* Found us */
|
||||
skip = false;
|
||||
break;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
} else {
|
||||
skip = false;
|
||||
}
|
||||
|
||||
/* if the NIC provides it's own announcement support, use it as well */
|
||||
if (nic->ncs->info->announce) {
|
||||
nic->ncs->info->announce(nic->ncs);
|
||||
trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
|
||||
nic->ncs->name,
|
||||
qemu_ether_ntoa(&nic->conf->macaddr), skip);
|
||||
|
||||
if (!skip) {
|
||||
len = announce_self_create(buf, nic->conf->macaddr.a);
|
||||
|
||||
qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
|
||||
|
||||
/* if the NIC provides it's own announcement support, use it as well */
|
||||
if (nic->ncs->info->announce) {
|
||||
nic->ncs->info->announce(nic->ncs);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void qemu_announce_self_once(void *opaque)
|
||||
{
|
||||
AnnounceTimer *timer = (AnnounceTimer *)opaque;
|
||||
|
||||
qemu_foreach_nic(qemu_announce_self_iter, NULL);
|
||||
qemu_foreach_nic(qemu_announce_self_iter, timer);
|
||||
|
||||
if (--timer->round) {
|
||||
qemu_announce_timer_step(timer);
|
||||
} else {
|
||||
qemu_announce_timer_del(timer);
|
||||
qemu_announce_timer_del(timer, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,12 +180,24 @@ void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
|
||||
if (params->rounds) {
|
||||
qemu_announce_self_once(timer);
|
||||
} else {
|
||||
qemu_announce_timer_del(timer);
|
||||
qemu_announce_timer_del(timer, true);
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_announce_self(AnnounceParameters *params, Error **errp)
|
||||
{
|
||||
static AnnounceTimer announce_timer;
|
||||
qemu_announce_self(&announce_timer, params);
|
||||
AnnounceTimer *named_timer;
|
||||
if (!params->has_id) {
|
||||
params->id = g_strdup("");
|
||||
params->has_id = true;
|
||||
}
|
||||
|
||||
named_timer = g_datalist_get_data(&named_timers, params->id);
|
||||
|
||||
if (!named_timer) {
|
||||
named_timer = g_new0(AnnounceTimer, 1);
|
||||
g_datalist_set_data(&named_timers, params->id, named_timer);
|
||||
}
|
||||
|
||||
qemu_announce_self(named_timer, params);
|
||||
}
|
||||
|
||||
@@ -83,11 +83,14 @@ typedef struct CompareState {
|
||||
char *pri_indev;
|
||||
char *sec_indev;
|
||||
char *outdev;
|
||||
char *notify_dev;
|
||||
CharBackend chr_pri_in;
|
||||
CharBackend chr_sec_in;
|
||||
CharBackend chr_out;
|
||||
CharBackend chr_notify_dev;
|
||||
SocketReadState pri_rs;
|
||||
SocketReadState sec_rs;
|
||||
SocketReadState notify_rs;
|
||||
bool vnet_hdr;
|
||||
|
||||
/*
|
||||
@@ -117,16 +120,33 @@ enum {
|
||||
SECONDARY_IN,
|
||||
};
|
||||
|
||||
static void colo_compare_inconsistency_notify(void)
|
||||
{
|
||||
notifier_list_notify(&colo_compare_notifiers,
|
||||
migrate_get_current());
|
||||
}
|
||||
|
||||
static int compare_chr_send(CompareState *s,
|
||||
const uint8_t *buf,
|
||||
uint32_t size,
|
||||
uint32_t vnet_hdr_len);
|
||||
uint32_t vnet_hdr_len,
|
||||
bool notify_remote_frame);
|
||||
|
||||
static void notify_remote_frame(CompareState *s)
|
||||
{
|
||||
char msg[] = "DO_CHECKPOINT";
|
||||
int ret = 0;
|
||||
|
||||
ret = compare_chr_send(s, (uint8_t *)msg, strlen(msg), 0, true);
|
||||
if (ret < 0) {
|
||||
error_report("Notify Xen COLO-frame failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void colo_compare_inconsistency_notify(CompareState *s)
|
||||
{
|
||||
if (s->notify_dev) {
|
||||
notify_remote_frame(s);
|
||||
} else {
|
||||
notifier_list_notify(&colo_compare_notifiers,
|
||||
migrate_get_current());
|
||||
}
|
||||
}
|
||||
|
||||
static gint seq_sorter(Packet *a, Packet *b, gpointer data)
|
||||
{
|
||||
@@ -238,7 +258,8 @@ static void colo_release_primary_pkt(CompareState *s, Packet *pkt)
|
||||
ret = compare_chr_send(s,
|
||||
pkt->data,
|
||||
pkt->size,
|
||||
pkt->vnet_hdr_len);
|
||||
pkt->vnet_hdr_len,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
error_report("colo send primary packet failed");
|
||||
}
|
||||
@@ -430,7 +451,7 @@ sec:
|
||||
qemu_hexdump((char *)spkt->data, stderr,
|
||||
"colo-compare spkt", spkt->size);
|
||||
|
||||
colo_compare_inconsistency_notify();
|
||||
colo_compare_inconsistency_notify(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,7 +593,7 @@ void colo_compare_unregister_notifier(Notifier *notify)
|
||||
}
|
||||
|
||||
static int colo_old_packet_check_one_conn(Connection *conn,
|
||||
void *user_data)
|
||||
CompareState *s)
|
||||
{
|
||||
GList *result = NULL;
|
||||
int64_t check_time = REGULAR_PACKET_CHECK_MS;
|
||||
@@ -583,7 +604,7 @@ static int colo_old_packet_check_one_conn(Connection *conn,
|
||||
|
||||
if (result) {
|
||||
/* Do checkpoint will flush old packet */
|
||||
colo_compare_inconsistency_notify();
|
||||
colo_compare_inconsistency_notify(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -603,7 +624,7 @@ static void colo_old_packet_check(void *opaque)
|
||||
* If we find one old packet, stop finding job and notify
|
||||
* COLO frame do checkpoint.
|
||||
*/
|
||||
g_queue_find_custom(&s->conn_list, NULL,
|
||||
g_queue_find_custom(&s->conn_list, s,
|
||||
(GCompareFunc)colo_old_packet_check_one_conn);
|
||||
}
|
||||
|
||||
@@ -632,7 +653,8 @@ static void colo_compare_packet(CompareState *s, Connection *conn,
|
||||
*/
|
||||
trace_colo_compare_main("packet different");
|
||||
g_queue_push_head(&conn->primary_list, pkt);
|
||||
colo_compare_inconsistency_notify();
|
||||
|
||||
colo_compare_inconsistency_notify(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -668,7 +690,8 @@ static void colo_compare_connection(void *opaque, void *user_data)
|
||||
static int compare_chr_send(CompareState *s,
|
||||
const uint8_t *buf,
|
||||
uint32_t size,
|
||||
uint32_t vnet_hdr_len)
|
||||
uint32_t vnet_hdr_len,
|
||||
bool notify_remote_frame)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t len = htonl(size);
|
||||
@@ -677,7 +700,14 @@ static int compare_chr_send(CompareState *s,
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
|
||||
if (notify_remote_frame) {
|
||||
ret = qemu_chr_fe_write_all(&s->chr_notify_dev,
|
||||
(uint8_t *)&len,
|
||||
sizeof(len));
|
||||
} else {
|
||||
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
|
||||
}
|
||||
|
||||
if (ret != sizeof(len)) {
|
||||
goto err;
|
||||
}
|
||||
@@ -688,13 +718,26 @@ static int compare_chr_send(CompareState *s,
|
||||
* know how to parse net packet correctly.
|
||||
*/
|
||||
len = htonl(vnet_hdr_len);
|
||||
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
|
||||
|
||||
if (!notify_remote_frame) {
|
||||
ret = qemu_chr_fe_write_all(&s->chr_out,
|
||||
(uint8_t *)&len,
|
||||
sizeof(len));
|
||||
}
|
||||
|
||||
if (ret != sizeof(len)) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size);
|
||||
if (notify_remote_frame) {
|
||||
ret = qemu_chr_fe_write_all(&s->chr_notify_dev,
|
||||
(uint8_t *)buf,
|
||||
size);
|
||||
} else {
|
||||
ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size);
|
||||
}
|
||||
|
||||
if (ret != size) {
|
||||
goto err;
|
||||
}
|
||||
@@ -744,6 +787,19 @@ static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size)
|
||||
}
|
||||
}
|
||||
|
||||
static void compare_notify_chr(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
CompareState *s = COLO_COMPARE(opaque);
|
||||
int ret;
|
||||
|
||||
ret = net_fill_rstate(&s->notify_rs, buf, size);
|
||||
if (ret == -1) {
|
||||
qemu_chr_fe_set_handlers(&s->chr_notify_dev, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, true);
|
||||
error_report("colo-compare notify_dev error");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check old packet regularly so it can watch for any packets
|
||||
* that the secondary hasn't produced equivalents of.
|
||||
@@ -831,6 +887,11 @@ static void colo_compare_iothread(CompareState *s)
|
||||
qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read,
|
||||
compare_sec_chr_in, NULL, NULL,
|
||||
s, s->worker_context, true);
|
||||
if (s->notify_dev) {
|
||||
qemu_chr_fe_set_handlers(&s->chr_notify_dev, compare_chr_can_read,
|
||||
compare_notify_chr, NULL, NULL,
|
||||
s, s->worker_context, true);
|
||||
}
|
||||
|
||||
colo_compare_timer_init(s);
|
||||
s->event_bh = qemu_bh_new(colo_compare_handle_event, s);
|
||||
@@ -897,6 +958,21 @@ static void compare_set_vnet_hdr(Object *obj,
|
||||
s->vnet_hdr = value;
|
||||
}
|
||||
|
||||
static char *compare_get_notify_dev(Object *obj, Error **errp)
|
||||
{
|
||||
CompareState *s = COLO_COMPARE(obj);
|
||||
|
||||
return g_strdup(s->notify_dev);
|
||||
}
|
||||
|
||||
static void compare_set_notify_dev(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
CompareState *s = COLO_COMPARE(obj);
|
||||
|
||||
g_free(s->notify_dev);
|
||||
s->notify_dev = g_strdup(value);
|
||||
}
|
||||
|
||||
static void compare_pri_rs_finalize(SocketReadState *pri_rs)
|
||||
{
|
||||
CompareState *s = container_of(pri_rs, CompareState, pri_rs);
|
||||
@@ -907,7 +983,8 @@ static void compare_pri_rs_finalize(SocketReadState *pri_rs)
|
||||
compare_chr_send(s,
|
||||
pri_rs->buf,
|
||||
pri_rs->packet_len,
|
||||
pri_rs->vnet_hdr_len);
|
||||
pri_rs->vnet_hdr_len,
|
||||
false);
|
||||
} else {
|
||||
/* compare packet in the specified connection */
|
||||
colo_compare_connection(conn, s);
|
||||
@@ -927,6 +1004,27 @@ static void compare_sec_rs_finalize(SocketReadState *sec_rs)
|
||||
}
|
||||
}
|
||||
|
||||
static void compare_notify_rs_finalize(SocketReadState *notify_rs)
|
||||
{
|
||||
CompareState *s = container_of(notify_rs, CompareState, notify_rs);
|
||||
|
||||
/* Get Xen colo-frame's notify and handle the message */
|
||||
char *data = g_memdup(notify_rs->buf, notify_rs->packet_len);
|
||||
char msg[] = "COLO_COMPARE_GET_XEN_INIT";
|
||||
int ret;
|
||||
|
||||
if (!strcmp(data, "COLO_USERSPACE_PROXY_INIT")) {
|
||||
ret = compare_chr_send(s, (uint8_t *)msg, strlen(msg), 0, true);
|
||||
if (ret < 0) {
|
||||
error_report("Notify Xen COLO-frame INIT failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(data, "COLO_CHECKPOINT")) {
|
||||
/* colo-compare do checkpoint, flush pri packet and remove sec packet */
|
||||
g_queue_foreach(&s->conn_list, colo_flush_packets, s);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 0 is success.
|
||||
@@ -997,6 +1095,17 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp)
|
||||
net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize, s->vnet_hdr);
|
||||
net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize, s->vnet_hdr);
|
||||
|
||||
/* Try to enable remote notify chardev, currently just for Xen COLO */
|
||||
if (s->notify_dev) {
|
||||
if (find_and_check_chardev(&chr, s->notify_dev, errp) ||
|
||||
!qemu_chr_fe_init(&s->chr_notify_dev, chr, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
net_socket_rs_init(&s->notify_rs, compare_notify_rs_finalize,
|
||||
s->vnet_hdr);
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_TAIL(&net_compares, s, next);
|
||||
|
||||
g_queue_init(&s->conn_list);
|
||||
@@ -1024,7 +1133,8 @@ static void colo_flush_packets(void *opaque, void *user_data)
|
||||
compare_chr_send(s,
|
||||
pkt->data,
|
||||
pkt->size,
|
||||
pkt->vnet_hdr_len);
|
||||
pkt->vnet_hdr_len,
|
||||
false);
|
||||
packet_destroy(pkt, NULL);
|
||||
}
|
||||
while (!g_queue_is_empty(&conn->secondary_list)) {
|
||||
@@ -1057,6 +1167,10 @@ static void colo_compare_init(Object *obj)
|
||||
(Object **)&s->iothread,
|
||||
object_property_allow_set_link,
|
||||
OBJ_PROP_LINK_STRONG, NULL);
|
||||
/* This parameter just for Xen COLO */
|
||||
object_property_add_str(obj, "notify_dev",
|
||||
compare_get_notify_dev, compare_set_notify_dev,
|
||||
NULL);
|
||||
|
||||
s->vnet_hdr = false;
|
||||
object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr,
|
||||
@@ -1071,6 +1185,10 @@ static void colo_compare_finalize(Object *obj)
|
||||
qemu_chr_fe_deinit(&s->chr_pri_in, false);
|
||||
qemu_chr_fe_deinit(&s->chr_sec_in, false);
|
||||
qemu_chr_fe_deinit(&s->chr_out, false);
|
||||
if (s->notify_dev) {
|
||||
qemu_chr_fe_deinit(&s->chr_notify_dev, false);
|
||||
}
|
||||
|
||||
if (s->iothread) {
|
||||
colo_compare_timer_del(s);
|
||||
}
|
||||
@@ -1103,6 +1221,7 @@ static void colo_compare_finalize(Object *obj)
|
||||
g_free(s->pri_indev);
|
||||
g_free(s->sec_indev);
|
||||
g_free(s->outdev);
|
||||
g_free(s->notify_dev);
|
||||
}
|
||||
|
||||
static const TypeInfo colo_compare_info = {
|
||||
|
||||
99
net/net.c
99
net/net.c
@@ -64,55 +64,42 @@ static QTAILQ_HEAD(, NetClientState) net_clients;
|
||||
/***********************************************************/
|
||||
/* network device redirectors */
|
||||
|
||||
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
|
||||
{
|
||||
const char *p, *p1;
|
||||
int len;
|
||||
p = *pp;
|
||||
p1 = strchr(p, sep);
|
||||
if (!p1)
|
||||
return -1;
|
||||
len = p1 - p;
|
||||
p1++;
|
||||
if (buf_size > 0) {
|
||||
if (len > buf_size - 1)
|
||||
len = buf_size - 1;
|
||||
memcpy(buf, p, len);
|
||||
buf[len] = '\0';
|
||||
}
|
||||
*pp = p1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_host_port(struct sockaddr_in *saddr, const char *str,
|
||||
Error **errp)
|
||||
{
|
||||
char buf[512];
|
||||
gchar **substrings;
|
||||
struct hostent *he;
|
||||
const char *p, *r;
|
||||
int port;
|
||||
const char *addr, *p, *r;
|
||||
int port, ret = 0;
|
||||
|
||||
p = str;
|
||||
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
||||
substrings = g_strsplit(str, ":", 2);
|
||||
if (!substrings || !substrings[0] || !substrings[1]) {
|
||||
error_setg(errp, "host address '%s' doesn't contain ':' "
|
||||
"separating host from port", str);
|
||||
return -1;
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
addr = substrings[0];
|
||||
p = substrings[1];
|
||||
|
||||
saddr->sin_family = AF_INET;
|
||||
if (buf[0] == '\0') {
|
||||
if (addr[0] == '\0') {
|
||||
saddr->sin_addr.s_addr = 0;
|
||||
} else {
|
||||
if (qemu_isdigit(buf[0])) {
|
||||
if (!inet_aton(buf, &saddr->sin_addr)) {
|
||||
if (qemu_isdigit(addr[0])) {
|
||||
if (!inet_aton(addr, &saddr->sin_addr)) {
|
||||
error_setg(errp, "host address '%s' is not a valid "
|
||||
"IPv4 address", buf);
|
||||
return -1;
|
||||
"IPv4 address", addr);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
he = gethostbyname(buf);
|
||||
he = gethostbyname(addr);
|
||||
if (he == NULL) {
|
||||
error_setg(errp, "can't resolve host address '%s'", buf);
|
||||
return - 1;
|
||||
error_setg(errp, "can't resolve host address '%s'", addr);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
saddr->sin_addr = *(struct in_addr *)he->h_addr;
|
||||
}
|
||||
@@ -120,10 +107,14 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str,
|
||||
port = strtol(p, (char **)&r, 0);
|
||||
if (r == p) {
|
||||
error_setg(errp, "port number '%s' is invalid", p);
|
||||
return -1;
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
saddr->sin_port = htons(port);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
g_strfreev(substrings);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *qemu_mac_strdup_printf(const uint8_t *macaddr)
|
||||
@@ -1105,6 +1096,7 @@ static void show_netdevs(void)
|
||||
|
||||
static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
|
||||
{
|
||||
gchar **substrings = NULL;
|
||||
void *object = NULL;
|
||||
Error *err = NULL;
|
||||
int ret = -1;
|
||||
@@ -1120,28 +1112,33 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
|
||||
const char *ip6_net = qemu_opt_get(opts, "ipv6-net");
|
||||
|
||||
if (ip6_net) {
|
||||
char buf[strlen(ip6_net) + 1];
|
||||
char *prefix_addr;
|
||||
unsigned long prefix_len = 64; /* Default 64bit prefix length. */
|
||||
|
||||
if (get_str_sep(buf, sizeof(buf), &ip6_net, '/') < 0) {
|
||||
/* Default 64bit prefix length. */
|
||||
qemu_opt_set(opts, "ipv6-prefix", ip6_net, &error_abort);
|
||||
qemu_opt_set_number(opts, "ipv6-prefixlen", 64, &error_abort);
|
||||
} else {
|
||||
substrings = g_strsplit(ip6_net, "/", 2);
|
||||
if (!substrings || !substrings[0]) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "ipv6-net",
|
||||
"a valid IPv6 prefix");
|
||||
goto out;
|
||||
}
|
||||
|
||||
prefix_addr = substrings[0];
|
||||
|
||||
if (substrings[1]) {
|
||||
/* User-specified prefix length. */
|
||||
unsigned long len;
|
||||
int err;
|
||||
|
||||
qemu_opt_set(opts, "ipv6-prefix", buf, &error_abort);
|
||||
err = qemu_strtoul(ip6_net, NULL, 10, &len);
|
||||
|
||||
err = qemu_strtoul(substrings[1], NULL, 10, &prefix_len);
|
||||
if (err) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
"ipv6-prefix", "a number");
|
||||
} else {
|
||||
qemu_opt_set_number(opts, "ipv6-prefixlen", len,
|
||||
&error_abort);
|
||||
"ipv6-prefixlen", "a number");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_opt_set(opts, "ipv6-prefix", prefix_addr, &error_abort);
|
||||
qemu_opt_set_number(opts, "ipv6-prefixlen", prefix_len,
|
||||
&error_abort);
|
||||
qemu_opt_unset(opts, "ipv6-net");
|
||||
}
|
||||
}
|
||||
@@ -1162,7 +1159,9 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
|
||||
qapi_free_NetLegacy(object);
|
||||
}
|
||||
|
||||
out:
|
||||
error_propagate(errp, err);
|
||||
g_strfreev(substrings);
|
||||
visit_free(v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# announce.c
|
||||
qemu_announce_self_iter(const char *mac) "%s"
|
||||
qemu_announce_self_iter(const char *id, const char *name, const char *mac, int skip) "%s:%s:%s skip: %d"
|
||||
qemu_announce_timer_del(bool free_named, bool free_timer, char *id) "free named: %d free timer: %d id: %s"
|
||||
|
||||
# vhost-user.c
|
||||
vhost_user_event(const char *chr, int event) "chr: %s got event: %d"
|
||||
|
||||
@@ -14,8 +14,11 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/spapr-rtas)
|
||||
|
||||
build-all: spapr-rtas.bin
|
||||
|
||||
%.o: %.S
|
||||
$(call quiet-command,$(CCAS) -mbig -c -o $@ $<,"CCAS","$(TARGET_DIR)$@")
|
||||
|
||||
%.img: %.o
|
||||
$(call quiet-command,$(CC) -nostdlib -o $@ $<,"Building","$(TARGET_DIR)$@")
|
||||
$(call quiet-command,$(CC) -nostdlib -mbig -o $@ $<,"Building","$(TARGET_DIR)$@")
|
||||
|
||||
%.bin: %.img
|
||||
$(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@,"Building","$(TARGET_DIR)$@")
|
||||
|
||||
BIN
pc-bios/vgabios-ati.bin
Normal file
BIN
pc-bios/vgabios-ati.bin
Normal file
Binary file not shown.
@@ -699,6 +699,13 @@
|
||||
#
|
||||
# @step: Delay increase (in ms) after each self-announcement attempt
|
||||
#
|
||||
# @interfaces: An optional list of interface names, which restricts the
|
||||
# announcement to the listed interfaces. (Since 4.1)
|
||||
#
|
||||
# @id: A name to be used to identify an instance of announce-timers
|
||||
# and to allow it to modified later. Not for use as
|
||||
# part of the migration parameters. (Since 4.1)
|
||||
#
|
||||
# Since: 4.0
|
||||
##
|
||||
|
||||
@@ -706,7 +713,9 @@
|
||||
'data': { 'initial': 'int',
|
||||
'max': 'int',
|
||||
'rounds': 'int',
|
||||
'step': 'int' } }
|
||||
'step': 'int',
|
||||
'*interfaces': ['str'],
|
||||
'*id' : 'str' } }
|
||||
|
||||
##
|
||||
# @announce-self:
|
||||
@@ -718,9 +727,10 @@
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# -> { "execute": "announce-self"
|
||||
# -> { "execute": "announce-self",
|
||||
# "arguments": {
|
||||
# "initial": 50, "max": 550, "rounds": 10, "step": 50 } }
|
||||
# "initial": 50, "max": 550, "rounds": 10, "step": 50,
|
||||
# "interfaces": ["vn2", "vn3"], "id": "bob" } }
|
||||
# <- { "return": {} }
|
||||
#
|
||||
# Since: 4.0
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user