Compare commits
274 Commits
pull-ui-20
...
machine-pu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34294e2f54 | ||
|
|
0e6aac87fd | ||
|
|
d1f8764099 | ||
|
|
fec44a8c70 | ||
|
|
6717f587a4 | ||
|
|
355a8ccc5c | ||
|
|
5e9c2a8dac | ||
|
|
97398d900c | ||
|
|
a2a8dfa8d8 | ||
|
|
327d8e4ed2 | ||
|
|
43e3346e43 | ||
|
|
0c69996e22 | ||
|
|
c04bd47db6 | ||
|
|
eccfa35e9f | ||
|
|
a66d815cd5 | ||
|
|
d552f675fb | ||
|
|
f4b2add6cc | ||
|
|
c91a5883c3 | ||
|
|
4833e15f74 | ||
|
|
9c94d8e6c9 | ||
|
|
ed796373b4 | ||
|
|
1b4093ea66 | ||
|
|
8bfd0550be | ||
|
|
f09f9bd9fa | ||
|
|
0ebc03bc06 | ||
|
|
8bc92a762a | ||
|
|
8816c600d3 | ||
|
|
4674da1c49 | ||
|
|
6aeda86890 | ||
|
|
cad0b273e5 | ||
|
|
f235538e38 | ||
|
|
20e2dec149 | ||
|
|
af1d3ebbef | ||
|
|
588c36cac7 | ||
|
|
36e9916811 | ||
|
|
f6b5319d41 | ||
|
|
3356128cd1 | ||
|
|
a36304fdca | ||
|
|
72700d7e73 | ||
|
|
c1fa017c7e | ||
|
|
fbb4e98341 | ||
|
|
76a9e9f680 | ||
|
|
3153119e9b | ||
|
|
f1a6cf3ef7 | ||
|
|
c18ad9a54b | ||
|
|
e5c0d3ce40 | ||
|
|
a7a00a729a | ||
|
|
788d2599de | ||
|
|
a88dced8eb | ||
|
|
14646457ae | ||
|
|
1e440cbc99 | ||
|
|
d6f1445faf | ||
|
|
3ba6a710e6 | ||
|
|
4caecccbc1 | ||
|
|
e76d1798fa | ||
|
|
281b2201e4 | ||
|
|
33577b47c6 | ||
|
|
39c350ee12 | ||
|
|
e1fb647199 | ||
|
|
fd97fd4408 | ||
|
|
2ae823d4f7 | ||
|
|
b094f2e015 | ||
|
|
a6cdb77f81 | ||
|
|
a58a4cb187 | ||
|
|
fad7fb9ccd | ||
|
|
f84d587111 | ||
|
|
6a991e07bb | ||
|
|
cfd47a71df | ||
|
|
d41e0bed7b | ||
|
|
9828f9b6c8 | ||
|
|
7aac531ef2 | ||
|
|
05061d8548 | ||
|
|
3feea4447f | ||
|
|
1252cf40a8 | ||
|
|
9dfbf250d2 | ||
|
|
98c63057d2 | ||
|
|
15d62af4b6 | ||
|
|
fc6c9257c6 | ||
|
|
de40abfecf | ||
|
|
0d6ff71ae3 | ||
|
|
1a8b408168 | ||
|
|
492a4c94be | ||
|
|
4467c6c118 | ||
|
|
b9f9c5b41a | ||
|
|
f083d92c03 | ||
|
|
26317698ef | ||
|
|
e2e02a8207 | ||
|
|
a657f79e32 | ||
|
|
880f848650 | ||
|
|
8b33e82b86 | ||
|
|
0d611402a1 | ||
|
|
7223c48cff | ||
|
|
dc59997871 | ||
|
|
6d425eb94d | ||
|
|
b9c600d207 | ||
|
|
e3f66e0368 | ||
|
|
fcce736719 | ||
|
|
ebab225910 | ||
|
|
9a3f5cf1bf | ||
|
|
78f9dc859d | ||
|
|
b2f56462d5 | ||
|
|
618a5a8bc5 | ||
|
|
b8f45cdf78 | ||
|
|
c4bea1690e | ||
|
|
10bf03af12 | ||
|
|
a08f0c3b5f | ||
|
|
fba98d455a | ||
|
|
8a56fdadaf | ||
|
|
23588797b6 | ||
|
|
6af4016020 | ||
|
|
8942764f54 | ||
|
|
c10c9d9615 | ||
|
|
6340472c54 | ||
|
|
2073d410ce | ||
|
|
abb21ac3e6 | ||
|
|
71968dbfd8 | ||
|
|
5997c210b9 | ||
|
|
965415eb20 | ||
|
|
a81d616437 | ||
|
|
73176bee99 | ||
|
|
f86b8b584b | ||
|
|
924e8a2bbc | ||
|
|
0ae053b7e1 | ||
|
|
58346b82ed | ||
|
|
1001dd9f84 | ||
|
|
fb9245c261 | ||
|
|
798609bbe2 | ||
|
|
c540d53ac8 | ||
|
|
c21cc6ca98 | ||
|
|
b07363a1a3 | ||
|
|
2b77e60ab8 | ||
|
|
396374caea | ||
|
|
6dcea61425 | ||
|
|
0dcee62261 | ||
|
|
8326ec2c83 | ||
|
|
d1ab9681ac | ||
|
|
f2d089425d | ||
|
|
5a68be94ac | ||
|
|
5167560b03 | ||
|
|
52fc01d973 | ||
|
|
4fa9f08e96 | ||
|
|
0bc6001f0d | ||
|
|
6acb971a94 | ||
|
|
7f996411ad | ||
|
|
a580d82085 | ||
|
|
4f298a4b29 | ||
|
|
5da4fb0018 | ||
|
|
494f7b572e | ||
|
|
ed2ef10c0c | ||
|
|
adcb89d55d | ||
|
|
2adba0a18a | ||
|
|
907e7c94d1 | ||
|
|
5803fce389 | ||
|
|
3d3ebcad6a | ||
|
|
3811ef14f5 | ||
|
|
ebde2465a9 | ||
|
|
ae29883508 | ||
|
|
f9735fd53f | ||
|
|
342f7a9d05 | ||
|
|
6167ebbd91 | ||
|
|
869a58af86 | ||
|
|
b7fcb3603c | ||
|
|
c1bf3531ae | ||
|
|
f7df22de56 | ||
|
|
18c440e1e1 | ||
|
|
b99514135b | ||
|
|
5fe79386ba | ||
|
|
b63283d7c3 | ||
|
|
2c02a48e6d | ||
|
|
c82f503dd5 | ||
|
|
7335a95abd | ||
|
|
226419d615 | ||
|
|
75fd6f13af | ||
|
|
79248c22ad | ||
|
|
27b9fc54d2 | ||
|
|
e08fde0c5e | ||
|
|
bda055096b | ||
|
|
9b613f4e40 | ||
|
|
c9f4b77ad5 | ||
|
|
fff4e48ed5 | ||
|
|
a0d06486b4 | ||
|
|
fc1769b758 | ||
|
|
631a438755 | ||
|
|
4eae2a657d | ||
|
|
f203549108 | ||
|
|
3f3009c098 | ||
|
|
9815cba502 | ||
|
|
39b6dbd8d7 | ||
|
|
32c3db5b26 | ||
|
|
a587a3fe6c | ||
|
|
8646992279 | ||
|
|
568b01caf3 | ||
|
|
99b88c6d1f | ||
|
|
062ed5d8d6 | ||
|
|
e593c0211b | ||
|
|
e2e5ee9c56 | ||
|
|
2d82f8a3cd | ||
|
|
db0da029a1 | ||
|
|
b16a44e13e | ||
|
|
a2d96af4bb | ||
|
|
08b758b482 | ||
|
|
317856cac8 | ||
|
|
f50dfe457f | ||
|
|
a589720567 | ||
|
|
30fd3e2790 | ||
|
|
de7971ffb9 | ||
|
|
b83b68a013 | ||
|
|
e560d141ab | ||
|
|
5151d23e65 | ||
|
|
294bbbb425 | ||
|
|
256920eb94 | ||
|
|
a9d5aed12d | ||
|
|
abc981bf29 | ||
|
|
5838d66e73 | ||
|
|
0a27af918b | ||
|
|
c619644067 | ||
|
|
469002263a | ||
|
|
7df9381b7a | ||
|
|
75cfb3bb41 | ||
|
|
8b8a61ad8c | ||
|
|
4fca654872 | ||
|
|
3a3c752f0b | ||
|
|
a006b67fe4 | ||
|
|
96b1a8bb55 | ||
|
|
502edbf834 | ||
|
|
25637d31f2 | ||
|
|
c6644fc88b | ||
|
|
ef3027affc | ||
|
|
d2eae20790 | ||
|
|
a648c13738 | ||
|
|
4ba364b472 | ||
|
|
8519c8e073 | ||
|
|
3293680dc7 | ||
|
|
5763795f93 | ||
|
|
eda509fa0a | ||
|
|
28b90d9c19 | ||
|
|
99f2dbd343 | ||
|
|
614e8018ed | ||
|
|
a60c785608 | ||
|
|
36a43ea83b | ||
|
|
70bee80d6b | ||
|
|
e2ec75685c | ||
|
|
305ae88895 | ||
|
|
d1cc881d54 | ||
|
|
ce9a2aa372 | ||
|
|
362786f14a | ||
|
|
f1b2bc601a | ||
|
|
338d3f415e | ||
|
|
9fe7101f1d | ||
|
|
031143c8d5 | ||
|
|
39e0c4f47d | ||
|
|
0ab9cd9a4b | ||
|
|
9fbad2ca36 | ||
|
|
3a2d44f6dd | ||
|
|
d24b2b1ccc | ||
|
|
5dd2d45e34 | ||
|
|
415ab35a44 | ||
|
|
443590c204 | ||
|
|
97556fe80e | ||
|
|
4792b7e9d5 | ||
|
|
8269fb7082 | ||
|
|
a95e9a485b | ||
|
|
ef00bdaf8c | ||
|
|
8210f5f6f5 | ||
|
|
778d9f9b25 | ||
|
|
729633c2bc | ||
|
|
29cb533d8c | ||
|
|
f1060c55bf | ||
|
|
8e41fb63c5 | ||
|
|
7ebb2745ac | ||
|
|
0a75601853 | ||
|
|
528f46af6e | ||
|
|
bb8f32c031 | ||
|
|
c586eac336 |
12
MAINTAINERS
12
MAINTAINERS
@@ -234,6 +234,7 @@ L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
F: kvm-*
|
||||
F: */kvm.*
|
||||
F: include/sysemu/kvm*.h
|
||||
|
||||
ARM
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@@ -716,6 +717,12 @@ F: hw/timer/hpet*
|
||||
F: hw/timer/i8254*
|
||||
F: hw/timer/mc146818rtc*
|
||||
|
||||
Machine core
|
||||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
S: Supported
|
||||
F: hw/core/machine.c
|
||||
F: include/hw/boards.h
|
||||
|
||||
Xtensa Machines
|
||||
---------------
|
||||
@@ -865,6 +872,7 @@ VFIO
|
||||
M: Alex Williamson <alex.williamson@redhat.com>
|
||||
S: Supported
|
||||
F: hw/vfio/*
|
||||
F: include/hw/vfio/
|
||||
|
||||
vhost
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
@@ -876,6 +884,7 @@ M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
F: hw/*/virtio*
|
||||
F: net/vhost-user.c
|
||||
F: include/hw/virtio/
|
||||
|
||||
virtio-9p
|
||||
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
@@ -1122,6 +1131,7 @@ Network device backends
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
S: Maintained
|
||||
F: net/
|
||||
F: include/net/
|
||||
T: git git://github.com/jasowang/qemu.git net
|
||||
|
||||
Netmap network backend
|
||||
@@ -1217,10 +1227,12 @@ F: scripts/qmp/
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
SLIRP
|
||||
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
M: Jan Kiszka <jan.kiszka@siemens.com>
|
||||
S: Maintained
|
||||
F: slirp/
|
||||
F: net/slirp.c
|
||||
F: include/net/slirp.h
|
||||
T: git git://git.kiszka.org/qemu.git queues/slirp
|
||||
|
||||
Tracing
|
||||
|
||||
4
Makefile
4
Makefile
@@ -238,7 +238,7 @@ qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-o
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o libqemuutil.a libqemustub.a
|
||||
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
@@ -329,7 +329,7 @@ ifneq ($(EXESUF),)
|
||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||
endif
|
||||
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y)
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
@@ -49,11 +49,10 @@ static void rng_egd_request_entropy(RngBackend *b, RngRequest *req)
|
||||
static int rng_egd_chr_can_read(void *opaque)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(opaque);
|
||||
GSList *i;
|
||||
RngRequest *req;
|
||||
int size = 0;
|
||||
|
||||
for (i = s->parent.requests; i; i = i->next) {
|
||||
RngRequest *req = i->data;
|
||||
QSIMPLEQ_FOREACH(req, &s->parent.requests, next) {
|
||||
size += req->size - req->offset;
|
||||
}
|
||||
|
||||
@@ -65,8 +64,8 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
RngEgd *s = RNG_EGD(opaque);
|
||||
size_t buf_offset = 0;
|
||||
|
||||
while (size > 0 && s->parent.requests) {
|
||||
RngRequest *req = s->parent.requests->data;
|
||||
while (size > 0 && !QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
|
||||
int len = MIN(size, req->size - req->offset);
|
||||
|
||||
memcpy(req->data + req->offset, buf + buf_offset, len);
|
||||
|
||||
@@ -35,8 +35,8 @@ static void entropy_available(void *opaque)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(opaque);
|
||||
|
||||
while (s->parent.requests != NULL) {
|
||||
RngRequest *req = s->parent.requests->data;
|
||||
while (!QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
|
||||
ssize_t len;
|
||||
|
||||
len = read(s->fd, req->data, req->size);
|
||||
@@ -58,7 +58,7 @@ static void rng_random_request_entropy(RngBackend *b, RngRequest *req)
|
||||
{
|
||||
RndRandom *s = RNG_RANDOM(b);
|
||||
|
||||
if (s->parent.requests == NULL) {
|
||||
if (QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||
/* If there are no pending requests yet, we need to
|
||||
* install our fd handler. */
|
||||
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
|
||||
|
||||
@@ -33,7 +33,7 @@ void rng_backend_request_entropy(RngBackend *s, size_t size,
|
||||
|
||||
k->request_entropy(s, req);
|
||||
|
||||
s->requests = g_slist_append(s->requests, req);
|
||||
QSIMPLEQ_INSERT_TAIL(&s->requests, req, next);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,24 +83,27 @@ static void rng_backend_free_request(RngRequest *req)
|
||||
|
||||
static void rng_backend_free_requests(RngBackend *s)
|
||||
{
|
||||
GSList *i;
|
||||
RngRequest *req, *next;
|
||||
|
||||
for (i = s->requests; i; i = i->next) {
|
||||
rng_backend_free_request(i->data);
|
||||
QSIMPLEQ_FOREACH_SAFE(req, &s->requests, next, next) {
|
||||
rng_backend_free_request(req);
|
||||
}
|
||||
|
||||
g_slist_free(s->requests);
|
||||
s->requests = NULL;
|
||||
QSIMPLEQ_INIT(&s->requests);
|
||||
}
|
||||
|
||||
void rng_backend_finalize_request(RngBackend *s, RngRequest *req)
|
||||
{
|
||||
s->requests = g_slist_remove(s->requests, req);
|
||||
QSIMPLEQ_REMOVE(&s->requests, req, RngRequest, next);
|
||||
rng_backend_free_request(req);
|
||||
}
|
||||
|
||||
static void rng_backend_init(Object *obj)
|
||||
{
|
||||
RngBackend *s = RNG_BACKEND(obj);
|
||||
|
||||
QSIMPLEQ_INIT(&s->requests);
|
||||
|
||||
object_property_add_bool(obj, "opened",
|
||||
rng_backend_prop_get_opened,
|
||||
rng_backend_prop_set_opened,
|
||||
|
||||
396
block.c
396
block.c
@@ -53,23 +53,6 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A BdrvDirtyBitmap can be in three possible states:
|
||||
* (1) successor is NULL and disabled is false: full r/w mode
|
||||
* (2) successor is NULL and disabled is true: read only mode ("disabled")
|
||||
* (3) successor is set: frozen mode.
|
||||
* A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set,
|
||||
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
||||
*/
|
||||
struct BdrvDirtyBitmap {
|
||||
HBitmap *bitmap; /* Dirty sector bitmap implementation */
|
||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||
char *name; /* Optional non-empty unique ID */
|
||||
int64_t size; /* Size of the bitmap (Number of sectors) */
|
||||
bool disabled; /* Bitmap is read-only */
|
||||
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
||||
};
|
||||
|
||||
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
|
||||
|
||||
struct BdrvStates bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states);
|
||||
@@ -88,9 +71,6 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildRole *child_role, Error **errp);
|
||||
|
||||
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
|
||||
static void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs);
|
||||
|
||||
/* If non-zero, use only whitelisted block drivers */
|
||||
static int use_bdrv_whitelist;
|
||||
|
||||
@@ -687,13 +667,19 @@ int bdrv_parse_cache_flags(const char *mode, int *flags)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the flags that a temporary snapshot should get, based on the
|
||||
* originally requested flags (the originally requested image will have flags
|
||||
* like a backing file)
|
||||
* Returns the options and flags that a temporary snapshot should get, based on
|
||||
* the originally requested flags (the originally requested image will have
|
||||
* flags like a backing file)
|
||||
*/
|
||||
static int bdrv_temp_snapshot_flags(int flags)
|
||||
static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
{
|
||||
return (flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY;
|
||||
*child_flags = (parent_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY;
|
||||
|
||||
/* For temporary files, unconditional cache=unsafe is fine */
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_WB, "on");
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1424,13 +1410,13 @@ done:
|
||||
return c;
|
||||
}
|
||||
|
||||
int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
||||
static int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags,
|
||||
QDict *snapshot_options, Error **errp)
|
||||
{
|
||||
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
|
||||
char *tmp_filename = g_malloc0(PATH_MAX + 1);
|
||||
int64_t total_size;
|
||||
QemuOpts *opts = NULL;
|
||||
QDict *snapshot_options;
|
||||
BlockDriverState *bs_snapshot;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
@@ -1464,8 +1450,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Prepare a new options QDict for the temporary file */
|
||||
snapshot_options = qdict_new();
|
||||
/* Prepare options QDict for the temporary file */
|
||||
qdict_put(snapshot_options, "file.driver",
|
||||
qstring_from_str("file"));
|
||||
qdict_put(snapshot_options, "file.filename",
|
||||
@@ -1477,6 +1462,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
||||
|
||||
ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
|
||||
flags, &local_err);
|
||||
snapshot_options = NULL;
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
@@ -1485,6 +1471,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
||||
bdrv_append(bs_snapshot, bs);
|
||||
|
||||
out:
|
||||
QDECREF(snapshot_options);
|
||||
g_free(tmp_filename);
|
||||
return ret;
|
||||
}
|
||||
@@ -1516,6 +1503,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
const char *drvname;
|
||||
const char *backing;
|
||||
Error *local_err = NULL;
|
||||
QDict *snapshot_options = NULL;
|
||||
int snapshot_flags = 0;
|
||||
|
||||
assert(pbs);
|
||||
@@ -1607,7 +1595,9 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
flags |= BDRV_O_ALLOW_RDWR;
|
||||
}
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
snapshot_flags = bdrv_temp_snapshot_flags(flags);
|
||||
snapshot_options = qdict_new();
|
||||
bdrv_temp_snapshot_options(&snapshot_flags, snapshot_options,
|
||||
flags, options);
|
||||
bdrv_backing_options(&flags, options, flags, options);
|
||||
}
|
||||
|
||||
@@ -1709,7 +1699,9 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
|
||||
* temporary snapshot afterwards. */
|
||||
if (snapshot_flags) {
|
||||
ret = bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err);
|
||||
ret = bdrv_append_temp_snapshot(bs, snapshot_flags, snapshot_options,
|
||||
&local_err);
|
||||
snapshot_options = NULL;
|
||||
if (local_err) {
|
||||
goto close_and_fail;
|
||||
}
|
||||
@@ -1721,6 +1713,7 @@ fail:
|
||||
if (file != NULL) {
|
||||
bdrv_unref_child(bs, file);
|
||||
}
|
||||
QDECREF(snapshot_options);
|
||||
QDECREF(bs->explicit_options);
|
||||
QDECREF(bs->options);
|
||||
QDECREF(options);
|
||||
@@ -1743,6 +1736,7 @@ close_and_fail:
|
||||
} else {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
QDECREF(snapshot_options);
|
||||
QDECREF(options);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -3431,346 +3425,6 @@ void bdrv_lock_medium(BlockDriverState *bs, bool locked)
|
||||
}
|
||||
}
|
||||
|
||||
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
|
||||
assert(name);
|
||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||
if (bm->name && !strcmp(name, bm->name)) {
|
||||
return bm;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
g_free(bitmap->name);
|
||||
bitmap->name = NULL;
|
||||
}
|
||||
|
||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t bitmap_size;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint32_t sector_granularity;
|
||||
|
||||
assert((granularity & (granularity - 1)) == 0);
|
||||
|
||||
if (name && bdrv_find_dirty_bitmap(bs, name)) {
|
||||
error_setg(errp, "Bitmap already exists: %s", name);
|
||||
return NULL;
|
||||
}
|
||||
sector_granularity = granularity >> BDRV_SECTOR_BITS;
|
||||
assert(sector_granularity);
|
||||
bitmap_size = bdrv_nb_sectors(bs);
|
||||
if (bitmap_size < 0) {
|
||||
error_setg_errno(errp, -bitmap_size, "could not get length of device");
|
||||
errno = -bitmap_size;
|
||||
return NULL;
|
||||
}
|
||||
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
||||
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity));
|
||||
bitmap->size = bitmap_size;
|
||||
bitmap->name = g_strdup(name);
|
||||
bitmap->disabled = false;
|
||||
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->successor;
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return !(bitmap->disabled || bitmap->successor);
|
||||
}
|
||||
|
||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_DISABLED;
|
||||
} else {
|
||||
return DIRTY_BITMAP_STATUS_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a successor bitmap destined to replace this bitmap after an operation.
|
||||
* Requires that the bitmap is not frozen and has no successor.
|
||||
*/
|
||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap, Error **errp)
|
||||
{
|
||||
uint64_t granularity;
|
||||
BdrvDirtyBitmap *child;
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp, "Cannot create a successor for a bitmap that is "
|
||||
"currently frozen");
|
||||
return -1;
|
||||
}
|
||||
assert(!bitmap->successor);
|
||||
|
||||
/* Create an anonymous successor */
|
||||
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||
child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
|
||||
if (!child) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Successor will be on or off based on our current state. */
|
||||
child->disabled = bitmap->disabled;
|
||||
|
||||
/* Install the successor and freeze the parent */
|
||||
bitmap->successor = child;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a bitmap with a successor, yield our name to the successor,
|
||||
* delete the old bitmap, and return a handle to the new bitmap.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
Error **errp)
|
||||
{
|
||||
char *name;
|
||||
BdrvDirtyBitmap *successor = bitmap->successor;
|
||||
|
||||
if (successor == NULL) {
|
||||
error_setg(errp, "Cannot relinquish control if "
|
||||
"there's no successor present");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = bitmap->name;
|
||||
bitmap->name = NULL;
|
||||
successor->name = name;
|
||||
bitmap->successor = NULL;
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
|
||||
return successor;
|
||||
}
|
||||
|
||||
/**
|
||||
* In cases of failure where we can no longer safely delete the parent,
|
||||
* we may wish to re-join the parent and child/successor.
|
||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *successor = parent->successor;
|
||||
|
||||
if (!successor) {
|
||||
error_setg(errp, "Cannot reclaim a successor when none is present");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
|
||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||
return NULL;
|
||||
}
|
||||
bdrv_release_dirty_bitmap(bs, successor);
|
||||
parent->successor = NULL;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates _all_ bitmaps attached to a BDS.
|
||||
*/
|
||||
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint64_t size = bdrv_nb_sectors(bs);
|
||||
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
hbitmap_truncate(bitmap->bitmap, size);
|
||||
bitmap->size = size;
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
bool only_named)
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
|
||||
assert(!bdrv_dirty_bitmap_frozen(bm));
|
||||
QLIST_REMOVE(bm, list);
|
||||
hbitmap_free(bm->bitmap);
|
||||
g_free(bm->name);
|
||||
g_free(bm);
|
||||
|
||||
if (bitmap) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
||||
* There must not be any frozen bitmaps attached.
|
||||
*/
|
||||
static void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL, true);
|
||||
}
|
||||
|
||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = true;
|
||||
}
|
||||
|
||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = false;
|
||||
}
|
||||
|
||||
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
BlockDirtyInfoList *list = NULL;
|
||||
BlockDirtyInfoList **plist = &list;
|
||||
|
||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
|
||||
BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
|
||||
info->count = bdrv_get_dirty_count(bm);
|
||||
info->granularity = bdrv_dirty_bitmap_granularity(bm);
|
||||
info->has_name = !!bm->name;
|
||||
info->name = g_strdup(bm->name);
|
||||
info->status = bdrv_dirty_bitmap_status(bm);
|
||||
entry->value = info;
|
||||
*plist = entry;
|
||||
plist = &entry->next;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t sector)
|
||||
{
|
||||
if (bitmap) {
|
||||
return hbitmap_get(bitmap->bitmap, sector);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses a default granularity based on the existing cluster size,
|
||||
* but clamped between [4K, 64K]. Defaults to 64K in the case that there
|
||||
* is no cluster size information available.
|
||||
*/
|
||||
uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
uint32_t granularity;
|
||||
|
||||
if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) {
|
||||
granularity = MAX(4096, bdi.cluster_size);
|
||||
granularity = MIN(65536, granularity);
|
||||
} else {
|
||||
granularity = 65536;
|
||||
}
|
||||
|
||||
return granularity;
|
||||
}
|
||||
|
||||
uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap);
|
||||
}
|
||||
|
||||
void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi)
|
||||
{
|
||||
hbitmap_iter_init(hbi, bitmap->bitmap, 0);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int nr_sectors)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int nr_sectors)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
if (!out) {
|
||||
hbitmap_reset_all(bitmap->bitmap);
|
||||
} else {
|
||||
HBitmap *backup = bitmap->bitmap;
|
||||
bitmap->bitmap = hbitmap_alloc(bitmap->size,
|
||||
hbitmap_granularity(backup));
|
||||
*out = backup;
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||
{
|
||||
HBitmap *tmp = bitmap->bitmap;
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
bitmap->bitmap = in;
|
||||
hbitmap_free(tmp);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
continue;
|
||||
}
|
||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance an HBitmapIter to an arbitrary offset.
|
||||
*/
|
||||
void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset)
|
||||
{
|
||||
assert(hbi->hb);
|
||||
hbitmap_iter_init(hbi, hbi->hb, offset);
|
||||
}
|
||||
|
||||
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return hbitmap_count(bitmap->bitmap);
|
||||
}
|
||||
|
||||
/* Get a reference to bs */
|
||||
void bdrv_ref(BlockDriverState *bs)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ block-obj-$(CONFIG_RBD) += rbd.o
|
||||
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
||||
block-obj-$(CONFIG_ARCHIPELAGO) += archipelago.o
|
||||
block-obj-$(CONFIG_LIBSSH2) += ssh.o
|
||||
block-obj-y += accounting.o
|
||||
block-obj-y += accounting.o dirty-bitmap.o
|
||||
block-obj-y += write-threshold.o
|
||||
|
||||
common-obj-y += stream.o
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/bitmap.h"
|
||||
|
||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
@@ -42,7 +43,7 @@ typedef struct BackupBlockJob {
|
||||
BlockdevOnError on_target_error;
|
||||
CoRwlock flush_rwlock;
|
||||
uint64_t sectors_read;
|
||||
HBitmap *bitmap;
|
||||
unsigned long *done_bitmap;
|
||||
int64_t cluster_size;
|
||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||
} BackupBlockJob;
|
||||
@@ -116,7 +117,7 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
cow_request_begin(&cow_request, job, start, end);
|
||||
|
||||
for (; start < end; start++) {
|
||||
if (hbitmap_get(job->bitmap, start)) {
|
||||
if (test_bit(start, job->done_bitmap)) {
|
||||
trace_backup_do_cow_skip(job, start);
|
||||
continue; /* already copied */
|
||||
}
|
||||
@@ -167,7 +168,7 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
||||
goto out;
|
||||
}
|
||||
|
||||
hbitmap_set(job->bitmap, start, 1);
|
||||
set_bit(start, job->done_bitmap);
|
||||
|
||||
/* Publish progress, guest I/O counts as progress too. Note that the
|
||||
* offset field is an opaque progress value, it is not a disk offset.
|
||||
@@ -399,7 +400,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
start = 0;
|
||||
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
|
||||
job->bitmap = hbitmap_alloc(end, 0);
|
||||
job->done_bitmap = bitmap_new(end);
|
||||
|
||||
bdrv_set_enable_write_cache(target, true);
|
||||
if (target->blk) {
|
||||
@@ -480,7 +481,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
/* wait until pending backup_do_cow() calls have completed */
|
||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
hbitmap_free(job->bitmap);
|
||||
g_free(job->done_bitmap);
|
||||
|
||||
if (target->blk) {
|
||||
blk_iostatus_disable(target->blk);
|
||||
|
||||
@@ -50,6 +50,8 @@ struct BlockBackend {
|
||||
bool iostatus_enabled;
|
||||
BlockDeviceIoStatus iostatus;
|
||||
|
||||
bool allow_write_beyond_eof;
|
||||
|
||||
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
||||
};
|
||||
|
||||
@@ -579,6 +581,11 @@ void blk_iostatus_set_err(BlockBackend *blk, int error)
|
||||
}
|
||||
}
|
||||
|
||||
void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow)
|
||||
{
|
||||
blk->allow_write_beyond_eof = allow;
|
||||
}
|
||||
|
||||
static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||
size_t size)
|
||||
{
|
||||
@@ -592,17 +599,19 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
len = blk_getlength(blk);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (offset > len || len - offset < size) {
|
||||
return -EIO;
|
||||
if (!blk->allow_write_beyond_eof) {
|
||||
len = blk_getlength(blk);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
if (offset > len || len - offset < size) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
387
block/dirty-bitmap.c
Normal file
387
block/dirty-bitmap.c
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* Block Dirty Bitmap
|
||||
*
|
||||
* Copyright (c) 2016 Red Hat. Inc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "config-host.h"
|
||||
#include "qemu-common.h"
|
||||
#include "trace.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
|
||||
/**
|
||||
* A BdrvDirtyBitmap can be in three possible states:
|
||||
* (1) successor is NULL and disabled is false: full r/w mode
|
||||
* (2) successor is NULL and disabled is true: read only mode ("disabled")
|
||||
* (3) successor is set: frozen mode.
|
||||
* A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set,
|
||||
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
||||
*/
|
||||
struct BdrvDirtyBitmap {
|
||||
HBitmap *bitmap; /* Dirty sector bitmap implementation */
|
||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||
char *name; /* Optional non-empty unique ID */
|
||||
int64_t size; /* Size of the bitmap (Number of sectors) */
|
||||
bool disabled; /* Bitmap is read-only */
|
||||
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
||||
};
|
||||
|
||||
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
|
||||
assert(name);
|
||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||
if (bm->name && !strcmp(name, bm->name)) {
|
||||
return bm;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
g_free(bitmap->name);
|
||||
bitmap->name = NULL;
|
||||
}
|
||||
|
||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t bitmap_size;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint32_t sector_granularity;
|
||||
|
||||
assert((granularity & (granularity - 1)) == 0);
|
||||
|
||||
if (name && bdrv_find_dirty_bitmap(bs, name)) {
|
||||
error_setg(errp, "Bitmap already exists: %s", name);
|
||||
return NULL;
|
||||
}
|
||||
sector_granularity = granularity >> BDRV_SECTOR_BITS;
|
||||
assert(sector_granularity);
|
||||
bitmap_size = bdrv_nb_sectors(bs);
|
||||
if (bitmap_size < 0) {
|
||||
error_setg_errno(errp, -bitmap_size, "could not get length of device");
|
||||
errno = -bitmap_size;
|
||||
return NULL;
|
||||
}
|
||||
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
||||
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity));
|
||||
bitmap->size = bitmap_size;
|
||||
bitmap->name = g_strdup(name);
|
||||
bitmap->disabled = false;
|
||||
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->successor;
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return !(bitmap->disabled || bitmap->successor);
|
||||
}
|
||||
|
||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_DISABLED;
|
||||
} else {
|
||||
return DIRTY_BITMAP_STATUS_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a successor bitmap destined to replace this bitmap after an operation.
|
||||
* Requires that the bitmap is not frozen and has no successor.
|
||||
*/
|
||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap, Error **errp)
|
||||
{
|
||||
uint64_t granularity;
|
||||
BdrvDirtyBitmap *child;
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp, "Cannot create a successor for a bitmap that is "
|
||||
"currently frozen");
|
||||
return -1;
|
||||
}
|
||||
assert(!bitmap->successor);
|
||||
|
||||
/* Create an anonymous successor */
|
||||
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||
child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
|
||||
if (!child) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Successor will be on or off based on our current state. */
|
||||
child->disabled = bitmap->disabled;
|
||||
|
||||
/* Install the successor and freeze the parent */
|
||||
bitmap->successor = child;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a bitmap with a successor, yield our name to the successor,
|
||||
* delete the old bitmap, and return a handle to the new bitmap.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
Error **errp)
|
||||
{
|
||||
char *name;
|
||||
BdrvDirtyBitmap *successor = bitmap->successor;
|
||||
|
||||
if (successor == NULL) {
|
||||
error_setg(errp, "Cannot relinquish control if "
|
||||
"there's no successor present");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = bitmap->name;
|
||||
bitmap->name = NULL;
|
||||
successor->name = name;
|
||||
bitmap->successor = NULL;
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
|
||||
return successor;
|
||||
}
|
||||
|
||||
/**
|
||||
* In cases of failure where we can no longer safely delete the parent,
|
||||
* we may wish to re-join the parent and child/successor.
|
||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *parent,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *successor = parent->successor;
|
||||
|
||||
if (!successor) {
|
||||
error_setg(errp, "Cannot reclaim a successor when none is present");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
|
||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||
return NULL;
|
||||
}
|
||||
bdrv_release_dirty_bitmap(bs, successor);
|
||||
parent->successor = NULL;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates _all_ bitmaps attached to a BDS.
|
||||
*/
|
||||
void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint64_t size = bdrv_nb_sectors(bs);
|
||||
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
hbitmap_truncate(bitmap->bitmap, size);
|
||||
bitmap->size = size;
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
bool only_named)
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
|
||||
assert(!bdrv_dirty_bitmap_frozen(bm));
|
||||
QLIST_REMOVE(bm, list);
|
||||
hbitmap_free(bm->bitmap);
|
||||
g_free(bm->name);
|
||||
g_free(bm);
|
||||
|
||||
if (bitmap) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
||||
* There must not be any frozen bitmaps attached.
|
||||
*/
|
||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL, true);
|
||||
}
|
||||
|
||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = true;
|
||||
}
|
||||
|
||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = false;
|
||||
}
|
||||
|
||||
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
BlockDirtyInfoList *list = NULL;
|
||||
BlockDirtyInfoList **plist = &list;
|
||||
|
||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
|
||||
BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
|
||||
info->count = bdrv_get_dirty_count(bm);
|
||||
info->granularity = bdrv_dirty_bitmap_granularity(bm);
|
||||
info->has_name = !!bm->name;
|
||||
info->name = g_strdup(bm->name);
|
||||
info->status = bdrv_dirty_bitmap_status(bm);
|
||||
entry->value = info;
|
||||
*plist = entry;
|
||||
plist = &entry->next;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
int64_t sector)
|
||||
{
|
||||
if (bitmap) {
|
||||
return hbitmap_get(bitmap->bitmap, sector);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses a default granularity based on the existing cluster size,
|
||||
* but clamped between [4K, 64K]. Defaults to 64K in the case that there
|
||||
* is no cluster size information available.
|
||||
*/
|
||||
uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
uint32_t granularity;
|
||||
|
||||
if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) {
|
||||
granularity = MAX(4096, bdi.cluster_size);
|
||||
granularity = MIN(65536, granularity);
|
||||
} else {
|
||||
granularity = 65536;
|
||||
}
|
||||
|
||||
return granularity;
|
||||
}
|
||||
|
||||
uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap);
|
||||
}
|
||||
|
||||
void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi)
|
||||
{
|
||||
hbitmap_iter_init(hbi, bitmap->bitmap, 0);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int nr_sectors)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t cur_sector, int nr_sectors)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
|
||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
if (!out) {
|
||||
hbitmap_reset_all(bitmap->bitmap);
|
||||
} else {
|
||||
HBitmap *backup = bitmap->bitmap;
|
||||
bitmap->bitmap = hbitmap_alloc(bitmap->size,
|
||||
hbitmap_granularity(backup));
|
||||
*out = backup;
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||
{
|
||||
HBitmap *tmp = bitmap->bitmap;
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
bitmap->bitmap = in;
|
||||
hbitmap_free(tmp);
|
||||
}
|
||||
|
||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||
int nr_sectors)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
continue;
|
||||
}
|
||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance an HBitmapIter to an arbitrary offset.
|
||||
*/
|
||||
void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset)
|
||||
{
|
||||
assert(hbi->hb);
|
||||
hbitmap_iter_init(hbi, hbi->hb, offset);
|
||||
}
|
||||
|
||||
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return hbitmap_count(bitmap->bitmap);
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qapi/util.h"
|
||||
@@ -461,7 +462,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int64_t total_size, cl_size;
|
||||
uint8_t tmp[BDRV_SECTOR_SIZE];
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *file;
|
||||
BlockBackend *file;
|
||||
uint32_t bat_entries, bat_sectors;
|
||||
ParallelsHeader header;
|
||||
int ret;
|
||||
@@ -477,14 +478,17 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
file = NULL;
|
||||
ret = bdrv_open(&file, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
if (ret < 0) {
|
||||
file = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (file == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
return -EIO;
|
||||
}
|
||||
ret = bdrv_truncate(file, 0);
|
||||
|
||||
blk_set_allow_write_beyond_eof(file, true);
|
||||
|
||||
ret = blk_truncate(file, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -508,18 +512,18 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
memcpy(tmp, &header, sizeof(header));
|
||||
|
||||
ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE);
|
||||
ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0);
|
||||
ret = blk_write_zeroes(file, 1, bat_sectors - 1, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
bdrv_unref(file);
|
||||
blk_unref(file);
|
||||
return ret;
|
||||
|
||||
exit:
|
||||
|
||||
190
block/qapi.c
190
block/qapi.c
@@ -355,100 +355,116 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
qapi_free_BlockInfo(info);
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
|
||||
bool query_backing)
|
||||
static BlockStats *bdrv_query_stats(BlockBackend *blk,
|
||||
const BlockDriverState *bs,
|
||||
bool query_backing);
|
||||
|
||||
static void bdrv_query_blk_stats(BlockStats *s, BlockBackend *blk)
|
||||
{
|
||||
BlockStats *s;
|
||||
BlockAcctStats *stats = blk_get_stats(blk);
|
||||
BlockAcctTimedStats *ts = NULL;
|
||||
|
||||
s = g_malloc0(sizeof(*s));
|
||||
s->has_device = true;
|
||||
s->device = g_strdup(blk_name(blk));
|
||||
|
||||
if (bdrv_get_device_name(bs)[0]) {
|
||||
s->has_device = true;
|
||||
s->device = g_strdup(bdrv_get_device_name(bs));
|
||||
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
||||
s->stats->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
|
||||
|
||||
s->stats->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
|
||||
s->stats->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
|
||||
s->stats->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->invalid_flush_operations =
|
||||
stats->invalid_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->rd_merged = stats->merged[BLOCK_ACCT_READ];
|
||||
s->stats->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
|
||||
s->stats->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->has_idle_time_ns = stats->last_access_time_ns > 0;
|
||||
if (s->stats->has_idle_time_ns) {
|
||||
s->stats->idle_time_ns = block_acct_idle_time_ns(stats);
|
||||
}
|
||||
|
||||
s->stats->account_invalid = stats->account_invalid;
|
||||
s->stats->account_failed = stats->account_failed;
|
||||
|
||||
while ((ts = block_acct_interval_next(stats, ts))) {
|
||||
BlockDeviceTimedStatsList *timed_stats =
|
||||
g_malloc0(sizeof(*timed_stats));
|
||||
BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats));
|
||||
timed_stats->next = s->stats->timed_stats;
|
||||
timed_stats->value = dev_stats;
|
||||
s->stats->timed_stats = timed_stats;
|
||||
|
||||
TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ];
|
||||
TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE];
|
||||
TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH];
|
||||
|
||||
dev_stats->interval_length = ts->interval_length;
|
||||
|
||||
dev_stats->min_rd_latency_ns = timed_average_min(rd);
|
||||
dev_stats->max_rd_latency_ns = timed_average_max(rd);
|
||||
dev_stats->avg_rd_latency_ns = timed_average_avg(rd);
|
||||
|
||||
dev_stats->min_wr_latency_ns = timed_average_min(wr);
|
||||
dev_stats->max_wr_latency_ns = timed_average_max(wr);
|
||||
dev_stats->avg_wr_latency_ns = timed_average_avg(wr);
|
||||
|
||||
dev_stats->min_flush_latency_ns = timed_average_min(fl);
|
||||
dev_stats->max_flush_latency_ns = timed_average_max(fl);
|
||||
dev_stats->avg_flush_latency_ns = timed_average_avg(fl);
|
||||
|
||||
dev_stats->avg_rd_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_READ);
|
||||
dev_stats->avg_wr_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_query_bds_stats(BlockStats *s, const BlockDriverState *bs,
|
||||
bool query_backing)
|
||||
{
|
||||
if (bdrv_get_node_name(bs)[0]) {
|
||||
s->has_node_name = true;
|
||||
s->node_name = g_strdup(bdrv_get_node_name(bs));
|
||||
}
|
||||
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
if (bs->blk) {
|
||||
BlockAcctStats *stats = blk_get_stats(bs->blk);
|
||||
BlockAcctTimedStats *ts = NULL;
|
||||
|
||||
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
||||
s->stats->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
|
||||
|
||||
s->stats->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
|
||||
s->stats->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
|
||||
s->stats->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
|
||||
s->stats->invalid_flush_operations =
|
||||
stats->invalid_ops[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->rd_merged = stats->merged[BLOCK_ACCT_READ];
|
||||
s->stats->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
|
||||
s->stats->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
|
||||
s->stats->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
|
||||
s->stats->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
|
||||
s->stats->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
|
||||
|
||||
s->stats->has_idle_time_ns = stats->last_access_time_ns > 0;
|
||||
if (s->stats->has_idle_time_ns) {
|
||||
s->stats->idle_time_ns = block_acct_idle_time_ns(stats);
|
||||
}
|
||||
|
||||
s->stats->account_invalid = stats->account_invalid;
|
||||
s->stats->account_failed = stats->account_failed;
|
||||
|
||||
while ((ts = block_acct_interval_next(stats, ts))) {
|
||||
BlockDeviceTimedStatsList *timed_stats =
|
||||
g_malloc0(sizeof(*timed_stats));
|
||||
BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats));
|
||||
timed_stats->next = s->stats->timed_stats;
|
||||
timed_stats->value = dev_stats;
|
||||
s->stats->timed_stats = timed_stats;
|
||||
|
||||
TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ];
|
||||
TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE];
|
||||
TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH];
|
||||
|
||||
dev_stats->interval_length = ts->interval_length;
|
||||
|
||||
dev_stats->min_rd_latency_ns = timed_average_min(rd);
|
||||
dev_stats->max_rd_latency_ns = timed_average_max(rd);
|
||||
dev_stats->avg_rd_latency_ns = timed_average_avg(rd);
|
||||
|
||||
dev_stats->min_wr_latency_ns = timed_average_min(wr);
|
||||
dev_stats->max_wr_latency_ns = timed_average_max(wr);
|
||||
dev_stats->avg_wr_latency_ns = timed_average_avg(wr);
|
||||
|
||||
dev_stats->min_flush_latency_ns = timed_average_min(fl);
|
||||
dev_stats->max_flush_latency_ns = timed_average_max(fl);
|
||||
dev_stats->avg_flush_latency_ns = timed_average_avg(fl);
|
||||
|
||||
dev_stats->avg_rd_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_READ);
|
||||
dev_stats->avg_wr_queue_depth =
|
||||
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
||||
|
||||
if (bs->file) {
|
||||
s->has_parent = true;
|
||||
s->parent = bdrv_query_stats(bs->file->bs, query_backing);
|
||||
s->parent = bdrv_query_stats(NULL, bs->file->bs, query_backing);
|
||||
}
|
||||
|
||||
if (query_backing && bs->backing) {
|
||||
s->has_backing = true;
|
||||
s->backing = bdrv_query_stats(bs->backing->bs, query_backing);
|
||||
s->backing = bdrv_query_stats(NULL, bs->backing->bs, query_backing);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_stats(BlockBackend *blk,
|
||||
const BlockDriverState *bs,
|
||||
bool query_backing)
|
||||
{
|
||||
BlockStats *s;
|
||||
|
||||
s = g_malloc0(sizeof(*s));
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
|
||||
if (blk) {
|
||||
bdrv_query_blk_stats(s, blk);
|
||||
}
|
||||
if (bs) {
|
||||
bdrv_query_bds_stats(s, bs, query_backing);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -477,22 +493,38 @@ BlockInfoList *qmp_query_block(Error **errp)
|
||||
return head;
|
||||
}
|
||||
|
||||
static bool next_query_bds(BlockBackend **blk, BlockDriverState **bs,
|
||||
bool query_nodes)
|
||||
{
|
||||
if (query_nodes) {
|
||||
*bs = bdrv_next_node(*bs);
|
||||
return !!*bs;
|
||||
}
|
||||
|
||||
*blk = blk_next(*blk);
|
||||
*bs = *blk ? blk_bs(*blk) : NULL;
|
||||
|
||||
return !!*blk;
|
||||
}
|
||||
|
||||
BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||
bool query_nodes,
|
||||
Error **errp)
|
||||
{
|
||||
BlockStatsList *head = NULL, **p_next = &head;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
/* Just to be safe if query_nodes is not always initialized */
|
||||
query_nodes = has_query_nodes && query_nodes;
|
||||
|
||||
while ((bs = query_nodes ? bdrv_next_node(bs) : bdrv_next(bs))) {
|
||||
while (next_query_bds(&blk, &bs, query_nodes)) {
|
||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
AioContext *ctx = blk ? blk_get_aio_context(blk)
|
||||
: bdrv_get_aio_context(bs);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
info->value = bdrv_query_stats(bs, !query_nodes);
|
||||
info->value = bdrv_query_stats(blk, bs, !query_nodes);
|
||||
aio_context_release(ctx);
|
||||
|
||||
*p_next = info;
|
||||
|
||||
24
block/qcow.c
24
block/qcow.c
@@ -24,6 +24,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include <zlib.h>
|
||||
#include "qapi/qmp/qerror.h"
|
||||
@@ -780,7 +781,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int flags = 0;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *qcow_bs;
|
||||
BlockBackend *qcow_blk;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
@@ -796,15 +797,18 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
qcow_bs = NULL;
|
||||
ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
if (ret < 0) {
|
||||
qcow_blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (qcow_blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(qcow_bs, 0);
|
||||
blk_set_allow_write_beyond_eof(qcow_blk, true);
|
||||
|
||||
ret = blk_truncate(qcow_blk, 0);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
}
|
||||
@@ -844,13 +848,13 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
/* write all the data */
|
||||
ret = bdrv_pwrite(qcow_bs, 0, &header, sizeof(header));
|
||||
ret = blk_pwrite(qcow_blk, 0, &header, sizeof(header));
|
||||
if (ret != sizeof(header)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (backing_file) {
|
||||
ret = bdrv_pwrite(qcow_bs, sizeof(header),
|
||||
ret = blk_pwrite(qcow_blk, sizeof(header),
|
||||
backing_file, backing_filename_len);
|
||||
if (ret != backing_filename_len) {
|
||||
goto exit;
|
||||
@@ -860,7 +864,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
tmp = g_malloc0(BDRV_SECTOR_SIZE);
|
||||
for (i = 0; i < ((sizeof(uint64_t)*l1_size + BDRV_SECTOR_SIZE - 1)/
|
||||
BDRV_SECTOR_SIZE); i++) {
|
||||
ret = bdrv_pwrite(qcow_bs, header_size +
|
||||
ret = blk_pwrite(qcow_blk, header_size +
|
||||
BDRV_SECTOR_SIZE*i, tmp, BDRV_SECTOR_SIZE);
|
||||
if (ret != BDRV_SECTOR_SIZE) {
|
||||
g_free(tmp);
|
||||
@@ -871,7 +875,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
g_free(tmp);
|
||||
ret = 0;
|
||||
exit:
|
||||
bdrv_unref(qcow_bs);
|
||||
blk_unref(qcow_blk);
|
||||
cleanup:
|
||||
g_free(backing_file);
|
||||
return ret;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include <zlib.h>
|
||||
#include "block/qcow2.h"
|
||||
@@ -2097,7 +2098,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
|
||||
* size for any qcow2 image.
|
||||
*/
|
||||
BlockDriverState* bs;
|
||||
BlockBackend *blk;
|
||||
QCowHeader *header;
|
||||
uint64_t* refcount_table;
|
||||
Error *local_err = NULL;
|
||||
@@ -2172,14 +2173,16 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Write the header */
|
||||
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
|
||||
header = g_malloc0(cluster_size);
|
||||
@@ -2207,7 +2210,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs, 0, header, cluster_size);
|
||||
ret = blk_pwrite(blk, 0, header, cluster_size);
|
||||
g_free(header);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not write qcow2 header");
|
||||
@@ -2217,7 +2220,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
/* Write a refcount table with one refcount block */
|
||||
refcount_table = g_malloc0(2 * cluster_size);
|
||||
refcount_table[0] = cpu_to_be64(2 * cluster_size);
|
||||
ret = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size);
|
||||
ret = blk_pwrite(blk, cluster_size, refcount_table, 2 * cluster_size);
|
||||
g_free(refcount_table);
|
||||
|
||||
if (ret < 0) {
|
||||
@@ -2225,8 +2228,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
goto out;
|
||||
}
|
||||
|
||||
bdrv_unref(bs);
|
||||
bs = NULL;
|
||||
blk_unref(blk);
|
||||
blk = NULL;
|
||||
|
||||
/*
|
||||
* And now open the image and make it consistent first (i.e. increase the
|
||||
@@ -2235,15 +2238,16 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
*/
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
||||
ret = bdrv_open(&bs, filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open("image-qcow2", filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_clusters(bs, 3 * cluster_size);
|
||||
ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 "
|
||||
"header and refcount table");
|
||||
@@ -2255,14 +2259,14 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
}
|
||||
|
||||
/* Create a full header (including things like feature table) */
|
||||
ret = qcow2_update_header(bs);
|
||||
ret = qcow2_update_header(blk_bs(blk));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not update qcow2 header");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Okay, now that we have a valid image, let's give it the right size */
|
||||
ret = bdrv_truncate(bs, total_size);
|
||||
ret = blk_truncate(blk, total_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not resize image");
|
||||
goto out;
|
||||
@@ -2270,7 +2274,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
|
||||
/* Want a backing file? There you go.*/
|
||||
if (backing_file) {
|
||||
ret = bdrv_change_backing_file(bs, backing_file, backing_format);
|
||||
ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
|
||||
"with format '%s'", backing_file, backing_format);
|
||||
@@ -2280,9 +2284,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
|
||||
/* And if we're supposed to preallocate metadata, do that now */
|
||||
if (prealloc != PREALLOC_MODE_OFF) {
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BDRVQcow2State *s = blk_bs(blk)->opaque;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = preallocate(bs);
|
||||
ret = preallocate(blk_bs(blk));
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not preallocate metadata");
|
||||
@@ -2290,24 +2294,25 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_unref(bs);
|
||||
bs = NULL;
|
||||
blk_unref(blk);
|
||||
blk = NULL;
|
||||
|
||||
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
||||
ret = bdrv_open(&bs, filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
blk = blk_new_open("image-flush", filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
28
block/qed.c
28
block/qed.c
@@ -18,6 +18,7 @@
|
||||
#include "qed.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "migration/migration.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
static const AIOCBInfo qed_aiocb_info = {
|
||||
.aiocb_size = sizeof(QEDAIOCB),
|
||||
@@ -580,7 +581,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
size_t l1_size = header.cluster_size * header.table_size;
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
@@ -588,17 +589,18 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* File must start empty and grow, check truncate is supported */
|
||||
ret = bdrv_truncate(bs, 0);
|
||||
ret = blk_truncate(blk, 0);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -614,18 +616,18 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
}
|
||||
|
||||
qed_header_cpu_to_le(&header, &le_header);
|
||||
ret = bdrv_pwrite(bs, 0, &le_header, sizeof(le_header));
|
||||
ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header));
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = bdrv_pwrite(bs, sizeof(le_header), backing_file,
|
||||
header.backing_filename_size);
|
||||
ret = blk_pwrite(blk, sizeof(le_header), backing_file,
|
||||
header.backing_filename_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
l1_table = g_malloc0(l1_size);
|
||||
ret = bdrv_pwrite(bs, header.l1_table_offset, l1_table, l1_size);
|
||||
ret = blk_pwrite(blk, header.l1_table_offset, l1_table, l1_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
@@ -633,7 +635,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||
ret = 0; /* success */
|
||||
out:
|
||||
g_free(l1_table);
|
||||
bdrv_unref(bs);
|
||||
blk_unref(blk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -215,14 +215,16 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
||||
return acb;
|
||||
}
|
||||
|
||||
static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
|
||||
static void quorum_report_bad(QuorumOpType type, uint64_t sector_num,
|
||||
int nb_sectors, char *node_name, int ret)
|
||||
{
|
||||
const char *msg = NULL;
|
||||
if (ret < 0) {
|
||||
msg = strerror(-ret);
|
||||
}
|
||||
qapi_event_send_quorum_report_bad(!!msg, msg, node_name,
|
||||
acb->sector_num, acb->nb_sectors, &error_abort);
|
||||
|
||||
qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name,
|
||||
sector_num, nb_sectors, &error_abort);
|
||||
}
|
||||
|
||||
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||
@@ -282,6 +284,7 @@ static void quorum_aio_cb(void *opaque, int ret)
|
||||
QuorumChildRequest *sacb = opaque;
|
||||
QuorumAIOCB *acb = sacb->parent;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
QuorumOpType type;
|
||||
bool rewrite = false;
|
||||
|
||||
if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) {
|
||||
@@ -300,12 +303,14 @@ static void quorum_aio_cb(void *opaque, int ret)
|
||||
return;
|
||||
}
|
||||
|
||||
type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE;
|
||||
sacb->ret = ret;
|
||||
acb->count++;
|
||||
if (ret == 0) {
|
||||
acb->success_count++;
|
||||
} else {
|
||||
quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret);
|
||||
quorum_report_bad(type, acb->sector_num, acb->nb_sectors,
|
||||
sacb->aiocb->bs->node_name, ret);
|
||||
}
|
||||
assert(acb->count <= s->num_children);
|
||||
assert(acb->success_count <= s->num_children);
|
||||
@@ -338,7 +343,9 @@ static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
quorum_report_bad(acb, s->children[item->index]->bs->node_name, 0);
|
||||
quorum_report_bad(QUORUM_OP_TYPE_READ, acb->sector_num,
|
||||
acb->nb_sectors,
|
||||
s->children[item->index]->bs->node_name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -648,8 +655,9 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb)
|
||||
}
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bdrv_aio_readv(s->children[i]->bs, acb->sector_num, &acb->qcrs[i].qiov,
|
||||
acb->nb_sectors, quorum_aio_cb, &acb->qcrs[i]);
|
||||
acb->qcrs[i].aiocb = bdrv_aio_readv(s->children[i]->bs, acb->sector_num,
|
||||
&acb->qcrs[i].qiov, acb->nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[i]);
|
||||
}
|
||||
|
||||
return &acb->common;
|
||||
@@ -664,9 +672,10 @@ static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb)
|
||||
qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov);
|
||||
qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov,
|
||||
acb->qcrs[acb->child_iter].buf);
|
||||
bdrv_aio_readv(s->children[acb->child_iter]->bs, acb->sector_num,
|
||||
&acb->qcrs[acb->child_iter].qiov, acb->nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[acb->child_iter]);
|
||||
acb->qcrs[acb->child_iter].aiocb =
|
||||
bdrv_aio_readv(s->children[acb->child_iter]->bs, acb->sector_num,
|
||||
&acb->qcrs[acb->child_iter].qiov, acb->nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[acb->child_iter]);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
@@ -760,19 +769,30 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
|
||||
QuorumVoteValue result_value;
|
||||
int i;
|
||||
int result = 0;
|
||||
int success_count = 0;
|
||||
|
||||
QLIST_INIT(&error_votes.vote_list);
|
||||
error_votes.compare = quorum_64bits_compare;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
result = bdrv_co_flush(s->children[i]->bs);
|
||||
result_value.l = result;
|
||||
quorum_count_vote(&error_votes, &result_value, i);
|
||||
if (result) {
|
||||
quorum_report_bad(QUORUM_OP_TYPE_FLUSH, 0,
|
||||
bdrv_nb_sectors(s->children[i]->bs),
|
||||
s->children[i]->bs->node_name, result);
|
||||
result_value.l = result;
|
||||
quorum_count_vote(&error_votes, &result_value, i);
|
||||
} else {
|
||||
success_count++;
|
||||
}
|
||||
}
|
||||
|
||||
winner = quorum_get_vote_winner(&error_votes);
|
||||
result = winner->value.l;
|
||||
|
||||
if (success_count >= s->threshold) {
|
||||
result = 0;
|
||||
} else {
|
||||
winner = quorum_get_vote_winner(&error_votes);
|
||||
result = winner->value.l;
|
||||
}
|
||||
quorum_free_vote_list(&error_votes);
|
||||
|
||||
return result;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
#define SD_PROTO_VER 0x01
|
||||
@@ -615,14 +616,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
|
||||
ret = qemu_co_send(sockfd, hdr, sizeof(*hdr));
|
||||
if (ret != sizeof(*hdr)) {
|
||||
error_report("failed to send a req, %s", strerror(errno));
|
||||
ret = -socket_error();
|
||||
return ret;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ret = qemu_co_send(sockfd, data, *wlen);
|
||||
if (ret != *wlen) {
|
||||
ret = -socket_error();
|
||||
error_report("failed to send a req, %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1637,7 +1637,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
|
||||
|
||||
static int sd_prealloc(const char *filename, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
BDRVSheepdogState *base = NULL;
|
||||
unsigned long buf_size;
|
||||
uint32_t idx, max_idx;
|
||||
@@ -1646,19 +1646,23 @@ static int sd_prealloc(const char *filename, Error **errp)
|
||||
void *buf = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open("image-prealloc", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
errp);
|
||||
if (blk == NULL) {
|
||||
ret = -EIO;
|
||||
goto out_with_err_set;
|
||||
}
|
||||
|
||||
vdi_size = bdrv_getlength(bs);
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
vdi_size = blk_getlength(blk);
|
||||
if (vdi_size < 0) {
|
||||
ret = vdi_size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
base = bs->opaque;
|
||||
base = blk_bs(blk)->opaque;
|
||||
object_size = (UINT32_C(1) << base->inode.block_size_shift);
|
||||
buf_size = MIN(object_size, SD_DATA_OBJ_SIZE);
|
||||
buf = g_malloc0(buf_size);
|
||||
@@ -1670,23 +1674,24 @@ static int sd_prealloc(const char *filename, Error **errp)
|
||||
* The created image can be a cloned image, so we need to read
|
||||
* a data from the source image.
|
||||
*/
|
||||
ret = bdrv_pread(bs, idx * buf_size, buf, buf_size);
|
||||
ret = blk_pread(blk, idx * buf_size, buf, buf_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size);
|
||||
ret = blk_pwrite(blk, idx * buf_size, buf, buf_size);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
||||
}
|
||||
out_with_err_set:
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
}
|
||||
g_free(buf);
|
||||
|
||||
@@ -1826,7 +1831,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
||||
}
|
||||
|
||||
if (backing_file) {
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
BDRVSheepdogState *base;
|
||||
BlockDriver *drv;
|
||||
|
||||
@@ -1838,22 +1843,23 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, errp);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open("backing", backing_file, NULL, NULL,
|
||||
BDRV_O_PROTOCOL | BDRV_O_CACHE_WB, errp);
|
||||
if (blk == NULL) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
base = bs->opaque;
|
||||
base = blk_bs(blk)->opaque;
|
||||
|
||||
if (!is_snapshot(&base->inode)) {
|
||||
error_setg(errp, "cannot clone from a non snapshot vdi");
|
||||
bdrv_unref(bs);
|
||||
blk_unref(blk);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
s->inode.vdi_id = base->inode.vdi_id;
|
||||
bdrv_unref(bs);
|
||||
blk_unref(blk);
|
||||
}
|
||||
|
||||
s->aio_context = qemu_get_aio_context();
|
||||
|
||||
22
block/vdi.c
22
block/vdi.c
@@ -52,6 +52,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "migration/migration.h"
|
||||
#include "qemu/coroutine.h"
|
||||
@@ -733,7 +734,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
size_t bmap_size;
|
||||
int64_t offset = 0;
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
uint32_t *bmap = NULL;
|
||||
|
||||
logout("\n");
|
||||
@@ -766,13 +767,18 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* We need enough blocks to store the given disk size,
|
||||
so always round up. */
|
||||
blocks = DIV_ROUND_UP(bytes, block_size);
|
||||
@@ -802,7 +808,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
vdi_header_print(&header);
|
||||
#endif
|
||||
vdi_header_to_le(&header);
|
||||
ret = bdrv_pwrite_sync(bs, offset, &header, sizeof(header));
|
||||
ret = blk_pwrite(blk, offset, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing header to %s", filename);
|
||||
goto exit;
|
||||
@@ -823,7 +829,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
bmap[i] = VDI_UNALLOCATED;
|
||||
}
|
||||
}
|
||||
ret = bdrv_pwrite_sync(bs, offset, bmap, bmap_size);
|
||||
ret = blk_pwrite(blk, offset, bmap, bmap_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Error writing bmap to %s", filename);
|
||||
goto exit;
|
||||
@@ -832,7 +838,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
if (image_type == VDI_TYPE_STATIC) {
|
||||
ret = bdrv_truncate(bs, offset + blocks * block_size);
|
||||
ret = blk_truncate(blk, offset + blocks * block_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to statically allocate %s", filename);
|
||||
goto exit;
|
||||
@@ -840,7 +846,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
exit:
|
||||
bdrv_unref(bs);
|
||||
blk_unref(blk);
|
||||
g_free(bmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
28
block/vhdx.c
28
block/vhdx.c
@@ -18,6 +18,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/crc32c.h"
|
||||
#include "block/vhdx.h"
|
||||
@@ -1772,7 +1773,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
gunichar2 *creator = NULL;
|
||||
glong creator_items;
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
char *type = NULL;
|
||||
VHDXImageType image_type;
|
||||
Error *local_err = NULL;
|
||||
@@ -1837,14 +1838,17 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
bs = NULL;
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/* Create (A) */
|
||||
|
||||
/* The creator field is optional, but may be useful for
|
||||
@@ -1852,13 +1856,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
|
||||
&creator_items, NULL);
|
||||
signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
|
||||
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
||||
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
}
|
||||
if (creator) {
|
||||
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
||||
creator, creator_items * sizeof(gunichar2));
|
||||
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
||||
creator, creator_items * sizeof(gunichar2));
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
}
|
||||
@@ -1866,13 +1870,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
|
||||
/* Creates (B),(C) */
|
||||
ret = vhdx_create_new_headers(bs, image_size, log_size);
|
||||
ret = vhdx_create_new_headers(blk_bs(blk), image_size, log_size);
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
}
|
||||
|
||||
/* Creates (D),(E),(G) explicitly. (F) created as by-product */
|
||||
ret = vhdx_create_new_region_table(bs, image_size, block_size, 512,
|
||||
ret = vhdx_create_new_region_table(blk_bs(blk), image_size, block_size, 512,
|
||||
log_size, use_zero_blocks, image_type,
|
||||
&metadata_offset);
|
||||
if (ret < 0) {
|
||||
@@ -1880,7 +1884,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
/* Creates (H) */
|
||||
ret = vhdx_create_new_metadata(bs, image_size, block_size, 512,
|
||||
ret = vhdx_create_new_metadata(blk_bs(blk), image_size, block_size, 512,
|
||||
metadata_offset, image_type);
|
||||
if (ret < 0) {
|
||||
goto delete_and_exit;
|
||||
@@ -1888,7 +1892,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
|
||||
|
||||
delete_and_exit:
|
||||
bdrv_unref(bs);
|
||||
blk_unref(blk);
|
||||
exit:
|
||||
g_free(type);
|
||||
g_free(creator);
|
||||
|
||||
121
block/vmdk.c
121
block/vmdk.c
@@ -26,6 +26,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
@@ -242,15 +243,17 @@ static void vmdk_free_last_extent(BlockDriverState *bs)
|
||||
|
||||
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
{
|
||||
char desc[DESC_SIZE];
|
||||
char *desc;
|
||||
uint32_t cid = 0xffffffff;
|
||||
const char *p_name, *cid_str;
|
||||
size_t cid_str_size;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
desc = g_malloc0(DESC_SIZE);
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
g_free(desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -269,41 +272,45 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
sscanf(p_name, "%" SCNx32, &cid);
|
||||
}
|
||||
|
||||
g_free(desc);
|
||||
return cid;
|
||||
}
|
||||
|
||||
static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
{
|
||||
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
|
||||
char *desc, *tmp_desc;
|
||||
char *p_name, *tmp_str;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
desc = g_malloc0(DESC_SIZE);
|
||||
tmp_desc = g_malloc0(DESC_SIZE);
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
desc[DESC_SIZE - 1] = '\0';
|
||||
tmp_str = strstr(desc, "parentCID");
|
||||
if (tmp_str == NULL) {
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
|
||||
pstrcpy(tmp_desc, DESC_SIZE, tmp_str);
|
||||
p_name = strstr(desc, "CID");
|
||||
if (p_name != NULL) {
|
||||
p_name += sizeof("CID");
|
||||
snprintf(p_name, sizeof(desc) - (p_name - desc), "%" PRIx32 "\n", cid);
|
||||
pstrcat(desc, sizeof(desc), tmp_desc);
|
||||
snprintf(p_name, DESC_SIZE - (p_name - desc), "%" PRIx32 "\n", cid);
|
||||
pstrcat(desc, DESC_SIZE, tmp_desc);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite_sync(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
g_free(desc);
|
||||
g_free(tmp_desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||
@@ -337,15 +344,16 @@ static int vmdk_reopen_prepare(BDRVReopenState *state,
|
||||
static int vmdk_parent_open(BlockDriverState *bs)
|
||||
{
|
||||
char *p_name;
|
||||
char desc[DESC_SIZE + 1];
|
||||
char *desc;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
desc[DESC_SIZE] = '\0';
|
||||
desc = g_malloc0(DESC_SIZE + 1);
|
||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
p_name = strstr(desc, "parentFileNameHint");
|
||||
if (p_name != NULL) {
|
||||
@@ -354,16 +362,20 @@ static int vmdk_parent_open(BlockDriverState *bs)
|
||||
p_name += sizeof("parentFileNameHint") + 1;
|
||||
end_name = strchr(p_name, '\"');
|
||||
if (end_name == NULL) {
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
g_free(desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create and append extent to the extent array. Return the added VmdkExtent
|
||||
@@ -1639,7 +1651,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
int ret, i;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
VMDK4Header header;
|
||||
Error *local_err = NULL;
|
||||
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
||||
@@ -1652,16 +1664,19 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
assert(bs == NULL);
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
blk = blk_new_open("extent", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
if (flat) {
|
||||
ret = bdrv_truncate(bs, filesize);
|
||||
ret = blk_truncate(blk, filesize);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
}
|
||||
@@ -1716,18 +1731,18 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
header.check_bytes[3] = 0xa;
|
||||
|
||||
/* write all the data */
|
||||
ret = bdrv_pwrite(bs, 0, &magic, sizeof(magic));
|
||||
ret = blk_pwrite(blk, 0, &magic, sizeof(magic));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_pwrite(bs, sizeof(magic), &header, sizeof(header));
|
||||
ret = blk_pwrite(blk, sizeof(magic), &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9);
|
||||
ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
goto exit;
|
||||
@@ -1740,8 +1755,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
i < gt_count; i++, tmp += gt_size) {
|
||||
gd_buf[i] = cpu_to_le32(tmp);
|
||||
}
|
||||
ret = bdrv_pwrite(bs, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
ret = blk_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
@@ -1752,8 +1767,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
i < gt_count; i++, tmp += gt_size) {
|
||||
gd_buf[i] = cpu_to_le32(tmp);
|
||||
}
|
||||
ret = bdrv_pwrite(bs, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
ret = blk_pwrite(blk, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE,
|
||||
gd_buf, gd_buf_size);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
goto exit;
|
||||
@@ -1761,8 +1776,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
}
|
||||
g_free(gd_buf);
|
||||
return ret;
|
||||
@@ -1811,7 +1826,7 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
|
||||
static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
int idx = 0;
|
||||
BlockDriverState *new_bs = NULL;
|
||||
BlockBackend *new_blk = NULL;
|
||||
Error *local_err = NULL;
|
||||
char *desc = NULL;
|
||||
int64_t total_size = 0, filesize;
|
||||
@@ -1922,7 +1937,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
if (backing_file) {
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockBackend *blk;
|
||||
char *full_backing = g_new0(char, PATH_MAX);
|
||||
bdrv_get_full_backing_filename_from_filename(filename, backing_file,
|
||||
full_backing, PATH_MAX,
|
||||
@@ -1933,18 +1948,21 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
ret = -ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, errp);
|
||||
|
||||
blk = blk_new_open("backing", full_backing, NULL, NULL,
|
||||
BDRV_O_NO_BACKING | BDRV_O_CACHE_WB, errp);
|
||||
g_free(full_backing);
|
||||
if (ret != 0) {
|
||||
if (blk == NULL) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
if (strcmp(bs->drv->format_name, "vmdk")) {
|
||||
bdrv_unref(bs);
|
||||
if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) {
|
||||
blk_unref(blk);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
parent_cid = vmdk_read_cid(bs, 0);
|
||||
bdrv_unref(bs);
|
||||
parent_cid = vmdk_read_cid(blk_bs(blk), 0);
|
||||
blk_unref(blk);
|
||||
snprintf(parent_desc_line, BUF_SIZE,
|
||||
"parentFileNameHint=\"%s\"", backing_file);
|
||||
}
|
||||
@@ -2002,14 +2020,19 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
assert(new_bs == NULL);
|
||||
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||
if (ret < 0) {
|
||||
|
||||
new_blk = blk_new_open("descriptor", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (new_blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len);
|
||||
|
||||
blk_set_allow_write_beyond_eof(new_blk, true);
|
||||
|
||||
ret = blk_pwrite(new_blk, desc_offset, desc, desc_len);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not write description");
|
||||
goto exit;
|
||||
@@ -2017,14 +2040,14 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
||||
* for description file */
|
||||
if (desc_offset == 0) {
|
||||
ret = bdrv_truncate(new_bs, desc_len);
|
||||
ret = blk_truncate(new_blk, desc_len);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if (new_bs) {
|
||||
bdrv_unref(new_bs);
|
||||
if (new_blk) {
|
||||
blk_unref(new_blk);
|
||||
}
|
||||
g_free(adapter_type);
|
||||
g_free(backing_file);
|
||||
|
||||
165
block/vpc.c
165
block/vpc.c
@@ -25,6 +25,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "migration/migration.h"
|
||||
#if defined(CONFIG_UUID)
|
||||
@@ -46,8 +47,14 @@ enum vhd_type {
|
||||
// Seconds since Jan 1, 2000 0:00:00 (UTC)
|
||||
#define VHD_TIMESTAMP_BASE 946684800
|
||||
|
||||
#define VHD_CHS_MAX_C 65535LL
|
||||
#define VHD_CHS_MAX_H 16
|
||||
#define VHD_CHS_MAX_S 255
|
||||
|
||||
#define VHD_MAX_SECTORS (65535LL * 255 * 255)
|
||||
#define VHD_MAX_GEOMETRY (65535LL * 16 * 255)
|
||||
#define VHD_MAX_GEOMETRY (VHD_CHS_MAX_C * VHD_CHS_MAX_H * VHD_CHS_MAX_S)
|
||||
|
||||
#define VPC_OPT_FORCE_SIZE "force_size"
|
||||
|
||||
// always big-endian
|
||||
typedef struct vhd_footer {
|
||||
@@ -128,6 +135,8 @@ typedef struct BDRVVPCState {
|
||||
|
||||
uint32_t block_size;
|
||||
uint32_t bitmap_size;
|
||||
bool force_use_chs;
|
||||
bool force_use_sz;
|
||||
|
||||
#ifdef CACHE
|
||||
uint8_t *pageentry_u8;
|
||||
@@ -140,6 +149,22 @@ typedef struct BDRVVPCState {
|
||||
Error *migration_blocker;
|
||||
} BDRVVPCState;
|
||||
|
||||
#define VPC_OPT_SIZE_CALC "force_size_calc"
|
||||
static QemuOptsList vpc_runtime_opts = {
|
||||
.name = "vpc-runtime-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(vpc_runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = VPC_OPT_SIZE_CALC,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Force disk size calculation to use either CHS geometry, "
|
||||
"or use the disk current_size specified in the VHD footer. "
|
||||
"{chs, current_size}"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
@@ -159,6 +184,25 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
const char *size_calc;
|
||||
|
||||
size_calc = qemu_opt_get(opts, VPC_OPT_SIZE_CALC);
|
||||
|
||||
if (!size_calc) {
|
||||
/* no override, use autodetect only */
|
||||
} else if (!strcmp(size_calc, "current_size")) {
|
||||
s->force_use_sz = true;
|
||||
} else if (!strcmp(size_calc, "chs")) {
|
||||
s->force_use_chs = true;
|
||||
} else {
|
||||
error_setg(errp, "Invalid size calculation mode: '%s'", size_calc);
|
||||
}
|
||||
}
|
||||
|
||||
static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -166,6 +210,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int i;
|
||||
VHDFooter *footer;
|
||||
VHDDynDiskHeader *dyndisk_header;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
bool use_chs;
|
||||
uint8_t buf[HEADER_SIZE];
|
||||
uint32_t checksum;
|
||||
uint64_t computed_size;
|
||||
@@ -173,6 +220,21 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int disk_type = VHD_DYNAMIC;
|
||||
int ret;
|
||||
|
||||
opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vpc_parse_options(bs, opts, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file->bs, 0, s->footer_buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -218,12 +280,36 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
bs->total_sectors = (int64_t)
|
||||
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
|
||||
|
||||
/* Images that have exactly the maximum geometry are probably bigger and
|
||||
* would be truncated if we adhered to the geometry for them. Rely on
|
||||
* footer->current_size for them. */
|
||||
if (bs->total_sectors == VHD_MAX_GEOMETRY) {
|
||||
/* Microsoft Virtual PC and Microsoft Hyper-V produce and read
|
||||
* VHD image sizes differently. VPC will rely on CHS geometry,
|
||||
* while Hyper-V and disk2vhd use the size specified in the footer.
|
||||
*
|
||||
* We use a couple of approaches to try and determine the correct method:
|
||||
* look at the Creator App field, and look for images that have CHS
|
||||
* geometry that is the maximum value.
|
||||
*
|
||||
* If the CHS geometry is the maximum CHS geometry, then we assume that
|
||||
* the size is the footer->current_size to avoid truncation. Otherwise,
|
||||
* we follow the table based on footer->creator_app:
|
||||
*
|
||||
* Known creator apps:
|
||||
* 'vpc ' : CHS Virtual PC (uses disk geometry)
|
||||
* 'qemu' : CHS QEMU (uses disk geometry)
|
||||
* 'qem2' : current_size QEMU (uses current_size)
|
||||
* 'win ' : current_size Hyper-V
|
||||
* 'd2v ' : current_size Disk2vhd
|
||||
*
|
||||
* The user can override the table values via drive options, however
|
||||
* even with an override we will still use current_size for images
|
||||
* that have CHS geometry of the maximum size.
|
||||
*/
|
||||
use_chs = (!!strncmp(footer->creator_app, "win ", 4) &&
|
||||
!!strncmp(footer->creator_app, "qem2", 4) &&
|
||||
!!strncmp(footer->creator_app, "d2v ", 4)) || s->force_use_chs;
|
||||
|
||||
if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) {
|
||||
bs->total_sectors = be64_to_cpu(footer->current_size) /
|
||||
BDRV_SECTOR_SIZE;
|
||||
BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Allow a maximum disk size of approximately 2 TB */
|
||||
@@ -673,7 +759,7 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf,
|
||||
int64_t total_sectors)
|
||||
{
|
||||
VHDDynDiskHeader *dyndisk_header =
|
||||
@@ -687,13 +773,13 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
block_size = 0x200000;
|
||||
num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
|
||||
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE);
|
||||
ret = blk_pwrite(blk, offset, buf, HEADER_SIZE);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset = 1536 + ((num_bat_entries * 4 + 511) & ~511);
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE);
|
||||
ret = blk_pwrite(blk, offset, buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -703,7 +789,7 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
|
||||
memset(buf, 0xFF, 512);
|
||||
for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) {
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, 512);
|
||||
ret = blk_pwrite(blk, offset, buf, 512);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -730,7 +816,7 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
// Write the header
|
||||
offset = 512;
|
||||
|
||||
ret = bdrv_pwrite_sync(bs, offset, buf, 1024);
|
||||
ret = blk_pwrite(blk, offset, buf, 1024);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -739,7 +825,7 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
||||
int64_t total_size)
|
||||
{
|
||||
int ret;
|
||||
@@ -747,12 +833,12 @@ static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
/* Add footer to total size */
|
||||
total_size += HEADER_SIZE;
|
||||
|
||||
ret = bdrv_truncate(bs, total_size);
|
||||
ret = blk_truncate(blk, total_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite_sync(bs, total_size - HEADER_SIZE, buf, HEADER_SIZE);
|
||||
ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -773,8 +859,9 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
int64_t total_size;
|
||||
int disk_type;
|
||||
int ret = -EIO;
|
||||
bool force_size;
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
@@ -793,30 +880,44 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
disk_type = VHD_DYNAMIC;
|
||||
}
|
||||
|
||||
force_size = qemu_opt_get_bool_del(opts, VPC_OPT_FORCE_SIZE, false);
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
|
||||
blk = blk_new_open("image", filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
/*
|
||||
* Calculate matching total_size and geometry. Increase the number of
|
||||
* sectors requested until we get enough (or fail). This ensures that
|
||||
* qemu-img convert doesn't truncate images, but rather rounds up.
|
||||
*
|
||||
* If the image size can't be represented by a spec conform CHS geometry,
|
||||
* If the image size can't be represented by a spec conformant CHS geometry,
|
||||
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
|
||||
* the image size from the VHD footer to calculate total_sectors.
|
||||
*/
|
||||
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
|
||||
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
||||
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
|
||||
if (force_size) {
|
||||
/* This will force the use of total_size for sector count, below */
|
||||
cyls = VHD_CHS_MAX_C;
|
||||
heads = VHD_CHS_MAX_H;
|
||||
secs_per_cyl = VHD_CHS_MAX_S;
|
||||
} else {
|
||||
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
|
||||
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
||||
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
|
||||
}
|
||||
}
|
||||
|
||||
if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
|
||||
@@ -835,8 +936,11 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
memset(buf, 0, 1024);
|
||||
|
||||
memcpy(footer->creator, "conectix", 8);
|
||||
/* TODO Check if "qemu" creator_app is ok for VPC */
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
if (force_size) {
|
||||
memcpy(footer->creator_app, "qem2", 4);
|
||||
} else {
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
}
|
||||
memcpy(footer->creator_os, "Wi2k", 4);
|
||||
|
||||
footer->features = cpu_to_be32(0x02);
|
||||
@@ -866,13 +970,13 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE));
|
||||
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
ret = create_dynamic_disk(bs, buf, total_sectors);
|
||||
ret = create_dynamic_disk(blk, buf, total_sectors);
|
||||
} else {
|
||||
ret = create_fixed_disk(bs, buf, total_size);
|
||||
ret = create_fixed_disk(blk, buf, total_size);
|
||||
}
|
||||
|
||||
out:
|
||||
bdrv_unref(bs);
|
||||
blk_unref(blk);
|
||||
g_free(disk_type_param);
|
||||
return ret;
|
||||
}
|
||||
@@ -917,6 +1021,13 @@ static QemuOptsList vpc_create_opts = {
|
||||
"Type of virtual hard disk format. Supported formats are "
|
||||
"{dynamic (default) | fixed} "
|
||||
},
|
||||
{
|
||||
.name = VPC_OPT_FORCE_SIZE,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Force disk size calculation to use the actual size "
|
||||
"specified, rather than using the nearest CHS-based "
|
||||
"calculation"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
60
blockdev.c
60
blockdev.c
@@ -593,13 +593,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||
|
||||
if (snapshot) {
|
||||
/* always use cache=unsafe with snapshot */
|
||||
qdict_put(bs_opts, BDRV_OPT_CACHE_WB, qstring_from_str("on"));
|
||||
qdict_put(bs_opts, BDRV_OPT_CACHE_DIRECT, qstring_from_str("off"));
|
||||
qdict_put(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, qstring_from_str("on"));
|
||||
}
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
bdrv_flags |= BDRV_O_INACTIVE;
|
||||
}
|
||||
@@ -682,6 +675,13 @@ static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
|
||||
* with other callers) rather than what we want as the real defaults.
|
||||
* Apply the defaults here instead. */
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_WB, "on");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
bdrv_flags |= BDRV_O_INACTIVE;
|
||||
}
|
||||
@@ -1732,10 +1732,15 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
/* create new image w/backing file */
|
||||
mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
||||
int64_t size = bdrv_getlength(state->old_bs);
|
||||
if (size < 0) {
|
||||
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
||||
return;
|
||||
}
|
||||
bdrv_img_create(new_image_file, format,
|
||||
state->old_bs->filename,
|
||||
state->old_bs->drv->format_name,
|
||||
NULL, -1, flags, &local_err, false);
|
||||
NULL, size, flags, &local_err, false);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -2819,6 +2824,15 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict)
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
|
||||
bs = bdrv_find_node(id);
|
||||
if (bs) {
|
||||
qmp_x_blockdev_del(false, NULL, true, id, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
blk = blk_by_name(id);
|
||||
if (!blk) {
|
||||
error_report("Device '%s' not found", id);
|
||||
@@ -3870,6 +3884,36 @@ out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void hmp_drive_add_node(Monitor *mon, const char *optstr)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
QDict *qdict;
|
||||
Error *local_err = NULL;
|
||||
|
||||
opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false);
|
||||
if (!opts) {
|
||||
return;
|
||||
}
|
||||
|
||||
qdict = qemu_opts_to_qdict(opts, NULL);
|
||||
|
||||
if (!qdict_get_try_str(qdict, "node-name")) {
|
||||
error_report("'node-name' needs to be specified");
|
||||
goto out;
|
||||
}
|
||||
|
||||
BlockDriverState *bs = bds_tree_init(qdict, &local_err);
|
||||
if (!bs) {
|
||||
error_report_err(local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
|
||||
|
||||
out:
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
|
||||
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *ov = qmp_output_visitor_new();
|
||||
|
||||
21
configure
vendored
21
configure
vendored
@@ -280,6 +280,7 @@ libusb=""
|
||||
usb_redir=""
|
||||
opengl=""
|
||||
opengl_dmabuf="no"
|
||||
avx2_opt="no"
|
||||
zlib="yes"
|
||||
lzo=""
|
||||
snappy=""
|
||||
@@ -1773,6 +1774,21 @@ EOF
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# avx2 optimization requirement check
|
||||
|
||||
cat > $TMPC << EOF
|
||||
static void bar(void) {}
|
||||
static void *bar_ifunc(void) {return (void*) bar;}
|
||||
static void foo(void) __attribute__((ifunc("bar_ifunc")));
|
||||
int main(void) { foo(); return 0; }
|
||||
EOF
|
||||
if compile_prog "-mavx2" "" ; then
|
||||
if readelf --syms $TMPE |grep "IFUNC.*foo" >/dev/null 2>&1; then
|
||||
avx2_opt="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
#########################################
|
||||
# zlib check
|
||||
|
||||
if test "$zlib" != "no" ; then
|
||||
@@ -4790,6 +4806,7 @@ echo "bzip2 support $bzip2"
|
||||
echo "NUMA host support $numa"
|
||||
echo "tcmalloc support $tcmalloc"
|
||||
echo "jemalloc support $jemalloc"
|
||||
echo "avx2 optimization $avx2_opt"
|
||||
|
||||
if test "$sdl_too_old" = "yes"; then
|
||||
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
||||
@@ -5178,6 +5195,10 @@ if test "$opengl" = "yes" ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$avx2_opt" = "yes" ; then
|
||||
echo "CONFIG_AVX2_OPT=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$lzo" = "yes" ; then
|
||||
echo "CONFIG_LZO=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
65
cpus.c
65
cpus.c
@@ -370,9 +370,12 @@ static void icount_warp_rt(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void icount_dummy_timer(void *opaque)
|
||||
static void icount_timer_cb(void *opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
/* No need for a checkpoint because the timer already synchronizes
|
||||
* with CHECKPOINT_CLOCK_VIRTUAL_RT.
|
||||
*/
|
||||
icount_warp_rt();
|
||||
}
|
||||
|
||||
void qtest_clock_warp(int64_t dest)
|
||||
@@ -396,17 +399,12 @@ void qtest_clock_warp(int64_t dest)
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
void qemu_clock_warp(QEMUClockType type)
|
||||
void qemu_start_warp_timer(void)
|
||||
{
|
||||
int64_t clock;
|
||||
int64_t deadline;
|
||||
|
||||
/*
|
||||
* There are too many global variables to make the "warp" behavior
|
||||
* applicable to other clocks. But a clock argument removes the
|
||||
* need for if statements all over the place.
|
||||
*/
|
||||
if (type != QEMU_CLOCK_VIRTUAL || !use_icount) {
|
||||
if (!use_icount) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -418,29 +416,17 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
}
|
||||
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP)) {
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (icount_sleep) {
|
||||
/*
|
||||
* If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now.
|
||||
* This ensures that the deadline for the timer is computed correctly
|
||||
* below.
|
||||
* This also makes sure that the insn counter is synchronized before
|
||||
* the CPU starts running, in case the CPU is woken by an event other
|
||||
* than the earliest QEMU_CLOCK_VIRTUAL timer.
|
||||
*/
|
||||
icount_warp_rt();
|
||||
timer_del(icount_warp_timer);
|
||||
}
|
||||
if (!all_cpu_threads_idle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* When testing, qtest commands advance icount. */
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
/* We want to use the earliest deadline from ALL vm_clocks */
|
||||
@@ -496,6 +482,28 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_account_warp_timer(void)
|
||||
{
|
||||
if (!use_icount || !icount_sleep) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
|
||||
* do not fire, so computing the deadline does not make sense.
|
||||
*/
|
||||
if (!runstate_is_running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
timer_del(icount_warp_timer);
|
||||
icount_warp_rt();
|
||||
}
|
||||
|
||||
static bool icount_state_needed(void *opaque)
|
||||
{
|
||||
return use_icount;
|
||||
@@ -624,13 +632,13 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
||||
icount_sleep = qemu_opt_get_bool(opts, "sleep", true);
|
||||
if (icount_sleep) {
|
||||
icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||
icount_dummy_timer, NULL);
|
||||
icount_timer_cb, NULL);
|
||||
}
|
||||
|
||||
icount_align_option = qemu_opt_get_bool(opts, "align", false);
|
||||
|
||||
if (icount_align_option && !icount_sleep) {
|
||||
error_setg(errp, "align=on and sleep=no are incompatible");
|
||||
error_setg(errp, "align=on and sleep=off are incompatible");
|
||||
}
|
||||
if (strcmp(option, "auto") != 0) {
|
||||
errno = 0;
|
||||
@@ -643,7 +651,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
||||
} else if (icount_align_option) {
|
||||
error_setg(errp, "shift=auto and align=on are incompatible");
|
||||
} else if (!icount_sleep) {
|
||||
error_setg(errp, "shift=auto and sleep=no are incompatible");
|
||||
error_setg(errp, "shift=auto and sleep=off are incompatible");
|
||||
}
|
||||
|
||||
use_icount = 2;
|
||||
@@ -995,9 +1003,6 @@ static void qemu_wait_io_event_common(CPUState *cpu)
|
||||
static void qemu_tcg_wait_io_event(CPUState *cpu)
|
||||
{
|
||||
while (all_cpu_threads_idle()) {
|
||||
/* Start accounting real time to the virtual clock if the CPUs
|
||||
are idle. */
|
||||
qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
|
||||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||
}
|
||||
|
||||
@@ -1499,7 +1504,7 @@ static void tcg_exec_all(void)
|
||||
int r;
|
||||
|
||||
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
|
||||
qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
|
||||
qemu_account_warp_timer();
|
||||
|
||||
if (next_cpu == NULL) {
|
||||
next_cpu = first_cpu;
|
||||
|
||||
4
cputlb.c
4
cputlb.c
@@ -416,8 +416,8 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
/* Write access calls the I/O callback. */
|
||||
te->addr_write = address | TLB_MMIO;
|
||||
} else if (memory_region_is_ram(section->mr)
|
||||
&& cpu_physical_memory_is_clean(section->mr->ram_addr
|
||||
+ xlat)) {
|
||||
&& cpu_physical_memory_is_clean(
|
||||
memory_region_get_ram_addr(section->mr) + xlat)) {
|
||||
te->addr_write = address | TLB_NOTDIRTY;
|
||||
} else {
|
||||
te->addr_write = address;
|
||||
|
||||
@@ -110,3 +110,4 @@ CONFIG_IOH3420=y
|
||||
CONFIG_I82801B11=y
|
||||
CONFIG_ACPI=y
|
||||
CONFIG_SMBIOS=y
|
||||
CONFIG_ASPEED_SOC=y
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "qemu/config-file.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "block/block_int.h"
|
||||
|
||||
static DriveInfo *add_init_drive(const char *optstr)
|
||||
{
|
||||
@@ -55,6 +56,12 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
DriveInfo *dinfo = NULL;
|
||||
const char *opts = qdict_get_str(qdict, "opts");
|
||||
bool node = qdict_get_try_bool(qdict, "node", false);
|
||||
|
||||
if (node) {
|
||||
hmp_drive_add_node(mon, opts);
|
||||
return;
|
||||
}
|
||||
|
||||
dinfo = add_init_drive(opts);
|
||||
if (!dinfo) {
|
||||
|
||||
@@ -180,8 +180,8 @@ aliases that leave holes then the lower priority region will appear in these
|
||||
holes too.)
|
||||
|
||||
For example, suppose we have a container A of size 0x8000 with two subregions
|
||||
B and C. B is a container mapped at 0x2000, size 0x4000, priority 1; C is
|
||||
an MMIO region mapped at 0x0, size 0x6000, priority 2. B currently has two
|
||||
B and C. B is a container mapped at 0x2000, size 0x4000, priority 2; C is
|
||||
an MMIO region mapped at 0x0, size 0x6000, priority 1. B currently has two
|
||||
of its own subregions: D of size 0x1000 at offset 0 and E of size 0x1000 at
|
||||
offset 0x2000. As a diagram:
|
||||
|
||||
@@ -297,8 +297,9 @@ various constraints can be supplied to control how these callbacks are called:
|
||||
- .valid.min_access_size, .valid.max_access_size define the access sizes
|
||||
(in bytes) which the device accepts; accesses outside this range will
|
||||
have device and bus specific behaviour (ignored, or machine check)
|
||||
- .valid.aligned specifies that the device only accepts naturally aligned
|
||||
accesses. Unaligned accesses invoke device and bus specific behaviour.
|
||||
- .valid.unaligned specifies that the *device being modelled* supports
|
||||
unaligned accesses; if false, unaligned accesses will invoke the
|
||||
appropriate bus or CPU specific behaviour.
|
||||
- .impl.min_access_size, .impl.max_access_size define the access sizes
|
||||
(in bytes) supported by the *implementation*; other access sizes will be
|
||||
emulated using the ones available. For example a 4-byte write will be
|
||||
@@ -306,5 +307,5 @@ various constraints can be supplied to control how these callbacks are called:
|
||||
- .impl.unaligned specifies that the *implementation* supports unaligned
|
||||
accesses; if false, unaligned accesses will be emulated by two aligned
|
||||
accesses.
|
||||
- .old_mmio can be used to ease porting from code using
|
||||
- .old_mmio eases the porting of code that was formerly using
|
||||
cpu_register_io_memory(). It should not be used in new code.
|
||||
|
||||
@@ -333,7 +333,7 @@ doesn't finish in a given time the switch is made to postcopy.
|
||||
To enable postcopy, issue this command on the monitor prior to the
|
||||
start of migration:
|
||||
|
||||
migrate_set_capability x-postcopy-ram on
|
||||
migrate_set_capability postcopy-ram on
|
||||
|
||||
The normal commands are then used to start a migration, which is still
|
||||
started in precopy mode. Issuing:
|
||||
|
||||
@@ -24,8 +24,8 @@ A detailed command line would be:
|
||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0
|
||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1
|
||||
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd
|
||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8, -device e1000,bus=bridge2,addr=0x3
|
||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40, -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
|
||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8 -device e1000,bus=bridge2,addr=0x3
|
||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
|
||||
|
||||
Here you have:
|
||||
- 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes)
|
||||
@@ -43,7 +43,7 @@ Implementation
|
||||
==============
|
||||
The PXB is composed by:
|
||||
- HostBridge (TYPE_PXB_HOST)
|
||||
The host bridge allows to register and query the PXB's rPCI root bus in QEMU.
|
||||
The host bridge allows to register and query the PXB's PCI root bus in QEMU.
|
||||
- PXBDev(TYPE_PXB_DEVICE)
|
||||
It is a regular PCI Device that resides on the piix host-bridge bus and its bus uses the same PCI domain.
|
||||
However, the bus behind is exposed through ACPI as a primary PCI bus and starts a new PCI hierarchy.
|
||||
|
||||
@@ -325,6 +325,7 @@ Emitted to report a corruption of a Quorum file.
|
||||
|
||||
Data:
|
||||
|
||||
- "type": Quorum operation type
|
||||
- "error": Error message (json-string, optional)
|
||||
Only present on failure. This field contains a human-readable
|
||||
error message. There are no semantics other than that the
|
||||
@@ -336,10 +337,18 @@ Data:
|
||||
|
||||
Example:
|
||||
|
||||
Read operation:
|
||||
{ "event": "QUORUM_REPORT_BAD",
|
||||
"data": { "node-name": "1.raw", "sector-num": 345435, "sectors-count": 5 },
|
||||
"data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
|
||||
"type": "read" },
|
||||
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
|
||||
|
||||
Flush operation:
|
||||
{ "event": "QUORUM_REPORT_BAD",
|
||||
"data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
|
||||
"type": "flush", "error": "Broken pipe" },
|
||||
"timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
|
||||
|
||||
Note: this event is rate-limited.
|
||||
|
||||
RESET
|
||||
|
||||
@@ -107,7 +107,7 @@ at the specified moments of time. There are several kinds of timers:
|
||||
sources (e.g. real time clock chip). Host clock is the one of the sources
|
||||
of non-determinism. Host clock read operations should be logged to
|
||||
make the execution deterministic.
|
||||
* Real time clock for icount. This clock is similar to real time clock but
|
||||
* Virtual real time clock. This clock is similar to real time clock but
|
||||
it is used only for increasing virtual clock while virtual machine is
|
||||
sleeping. Due to its nature it is also non-deterministic as the host clock
|
||||
and has to be logged too.
|
||||
@@ -134,11 +134,20 @@ of time. That's why we do not process a group of timers until the checkpoint
|
||||
event will be read from the log. Such an event allows synchronizing CPU
|
||||
execution and timer events.
|
||||
|
||||
Another checkpoints application in record/replay is instruction counting
|
||||
while the virtual machine is idle. This function (qemu_clock_warp) is called
|
||||
from the wait loop. It changes virtual machine state and must be deterministic
|
||||
then. That is why we added checkpoint to this function to prevent its
|
||||
operation in replay mode when it does not correspond to record mode.
|
||||
Two other checkpoints govern the "warping" of the virtual clock.
|
||||
While the virtual machine is idle, the virtual clock increments at
|
||||
1 ns per *real time* nanosecond. This is done by setting up a timer
|
||||
(called the warp timer) on the virtual real time clock, so that the
|
||||
timer fires at the next deadline of the virtual clock; the virtual clock
|
||||
is then incremented (which is called "warping" the virtual clock) as
|
||||
soon as the timer fires or the CPUs need to go out of the idle state.
|
||||
Two functions are used for this purpose; because these actions change
|
||||
virtual machine state and must be deterministic, each of them creates a
|
||||
checkpoint. qemu_start_warp_timer checks if the CPUs are idle and if so
|
||||
starts accounting real time to virtual clock. qemu_account_warp_timer
|
||||
is called when the CPUs get an interrupt or when the warp timer fires,
|
||||
and it warps the virtual clock by the amount of real time that has passed
|
||||
since qemu_start_warp_timer.
|
||||
|
||||
Bottom halves
|
||||
-------------
|
||||
|
||||
@@ -84,6 +84,15 @@ Selector Register address: Base + 8 (2 bytes)
|
||||
Data Register address: Base + 0 (8 bytes)
|
||||
DMA Address address: Base + 16 (8 bytes)
|
||||
|
||||
== ACPI Interface ==
|
||||
|
||||
The fw_cfg device is defined with ACPI ID "QEMU0002". Since we expect
|
||||
ACPI tables to be passed into the guest through the fw_cfg device itself,
|
||||
the guest-side firmware can not use ACPI to find fw_cfg. However, once the
|
||||
firmware is finished setting up ACPI tables and hands control over to the
|
||||
guest kernel, the latter can use the fw_cfg ACPI node for a more accurate
|
||||
inventory of in-use IOport or MMIO regions.
|
||||
|
||||
== Firmware Configuration Items ==
|
||||
|
||||
=== Signature (Key 0x0000, FW_CFG_SIGNATURE) ===
|
||||
|
||||
@@ -15,13 +15,23 @@ The 1000 -> 10ff device ID range is used as follows for virtio-pci devices.
|
||||
Note that this allocation separate from the virtio device IDs, which are
|
||||
maintained as part of the virtio specification.
|
||||
|
||||
1af4:1000 network device
|
||||
1af4:1001 block device
|
||||
1af4:1002 balloon device
|
||||
1af4:1003 console device
|
||||
1af4:1004 SCSI host bus adapter device
|
||||
1af4:1005 entropy generator device
|
||||
1af4:1009 9p filesystem device
|
||||
1af4:1000 network device (legacy)
|
||||
1af4:1001 block device (legacy)
|
||||
1af4:1002 balloon device (legacy)
|
||||
1af4:1003 console device (legacy)
|
||||
1af4:1004 SCSI host bus adapter device (legacy)
|
||||
1af4:1005 entropy generator device (legacy)
|
||||
1af4:1009 9p filesystem device (legacy)
|
||||
|
||||
1af4:1041 network device (modern)
|
||||
1af4:1042 block device (modern)
|
||||
1af4:1043 console device (modern)
|
||||
1af4:1044 entropy generator device (modern)
|
||||
1af4:1045 balloon device (modern)
|
||||
1af4:1048 SCSI host bus adapter device (modern)
|
||||
1af4:1049 9p filesystem device (modern)
|
||||
1af4:1050 virtio gpu device (modern)
|
||||
1af4:1052 virtio input device (modern)
|
||||
|
||||
1af4:10f0 Available for experimental usage without registration. Must get
|
||||
to official ID when the code leaves the test lab (i.e. when seeking
|
||||
|
||||
234
exec.c
234
exec.c
@@ -135,6 +135,7 @@ typedef struct PhysPageMap {
|
||||
struct AddressSpaceDispatch {
|
||||
struct rcu_head rcu;
|
||||
|
||||
MemoryRegionSection *mru_section;
|
||||
/* This is a multi-level map on the physical address space.
|
||||
* The bottom level has pointers to MemoryRegionSections.
|
||||
*/
|
||||
@@ -307,6 +308,17 @@ static void phys_page_compact_all(AddressSpaceDispatch *d, int nodes_nb)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool section_covers_addr(const MemoryRegionSection *section,
|
||||
hwaddr addr)
|
||||
{
|
||||
/* Memory topology clips a memory region to [0, 2^64); size.hi > 0 means
|
||||
* the section must cover the entire address space.
|
||||
*/
|
||||
return section->size.hi ||
|
||||
range_covers_byte(section->offset_within_address_space,
|
||||
section->size.lo, addr);
|
||||
}
|
||||
|
||||
static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr,
|
||||
Node *nodes, MemoryRegionSection *sections)
|
||||
{
|
||||
@@ -322,9 +334,7 @@ static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr,
|
||||
lp = p[(index >> (i * P_L2_BITS)) & (P_L2_SIZE - 1)];
|
||||
}
|
||||
|
||||
if (sections[lp.ptr].size.hi ||
|
||||
range_covers_byte(sections[lp.ptr].offset_within_address_space,
|
||||
sections[lp.ptr].size.lo, addr)) {
|
||||
if (section_covers_addr(§ions[lp.ptr], addr)) {
|
||||
return §ions[lp.ptr];
|
||||
} else {
|
||||
return §ions[PHYS_SECTION_UNASSIGNED];
|
||||
@@ -342,14 +352,25 @@ static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d,
|
||||
hwaddr addr,
|
||||
bool resolve_subpage)
|
||||
{
|
||||
MemoryRegionSection *section;
|
||||
MemoryRegionSection *section = atomic_read(&d->mru_section);
|
||||
subpage_t *subpage;
|
||||
bool update;
|
||||
|
||||
section = phys_page_find(d->phys_map, addr, d->map.nodes, d->map.sections);
|
||||
if (section && section != &d->map.sections[PHYS_SECTION_UNASSIGNED] &&
|
||||
section_covers_addr(section, addr)) {
|
||||
update = false;
|
||||
} else {
|
||||
section = phys_page_find(d->phys_map, addr, d->map.nodes,
|
||||
d->map.sections);
|
||||
update = true;
|
||||
}
|
||||
if (resolve_subpage && section->mr->subpage) {
|
||||
subpage = container_of(section->mr, subpage_t, iomem);
|
||||
section = &d->map.sections[subpage->sub_section[SUBPAGE_IDX(addr)]];
|
||||
}
|
||||
if (update) {
|
||||
atomic_set(&d->mru_section, section);
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
@@ -1207,92 +1228,83 @@ void qemu_mutex_unlock_ramlist(void)
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#define HUGETLBFS_MAGIC 0x958458f6
|
||||
|
||||
static long gethugepagesize(const char *path, Error **errp)
|
||||
{
|
||||
struct statfs fs;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = statfs(path, &fs);
|
||||
} while (ret != 0 && errno == EINTR);
|
||||
|
||||
if (ret != 0) {
|
||||
error_setg_errno(errp, errno, "failed to get page size of file %s",
|
||||
path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fs.f_bsize;
|
||||
}
|
||||
|
||||
static void *file_ram_alloc(RAMBlock *block,
|
||||
ram_addr_t memory,
|
||||
const char *path,
|
||||
Error **errp)
|
||||
{
|
||||
struct stat st;
|
||||
bool unlink_on_error = false;
|
||||
char *filename;
|
||||
char *sanitized_name;
|
||||
char *c;
|
||||
void *area;
|
||||
int fd;
|
||||
uint64_t hpagesize;
|
||||
Error *local_err = NULL;
|
||||
|
||||
hpagesize = gethugepagesize(path, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto error;
|
||||
}
|
||||
block->mr->align = hpagesize;
|
||||
|
||||
if (memory < hpagesize) {
|
||||
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
||||
"or larger than huge page size 0x%" PRIx64,
|
||||
memory, hpagesize);
|
||||
goto error;
|
||||
}
|
||||
int64_t page_size;
|
||||
|
||||
if (kvm_enabled() && !kvm_has_sync_mmu()) {
|
||||
error_setg(errp,
|
||||
"host lacks kvm mmu notifiers, -mem-path unsupported");
|
||||
goto error;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
|
||||
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
||||
sanitized_name = g_strdup(memory_region_name(block->mr));
|
||||
for (c = sanitized_name; *c != '\0'; c++) {
|
||||
if (*c == '/') {
|
||||
*c = '_';
|
||||
}
|
||||
}
|
||||
|
||||
filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path,
|
||||
sanitized_name);
|
||||
g_free(sanitized_name);
|
||||
|
||||
fd = mkstemp(filename);
|
||||
for (;;) {
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd >= 0) {
|
||||
unlink(filename);
|
||||
/* @path names an existing file, use it */
|
||||
break;
|
||||
}
|
||||
g_free(filename);
|
||||
} else {
|
||||
fd = open(path, O_RDWR | O_CREAT, 0644);
|
||||
if (errno == ENOENT) {
|
||||
/* @path names a file that doesn't exist, create it */
|
||||
fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
|
||||
if (fd >= 0) {
|
||||
unlink_on_error = true;
|
||||
break;
|
||||
}
|
||||
} else if (errno == EISDIR) {
|
||||
/* @path names a directory, create a file there */
|
||||
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
||||
sanitized_name = g_strdup(memory_region_name(block->mr));
|
||||
for (c = sanitized_name; *c != '\0'; c++) {
|
||||
if (*c == '/') {
|
||||
*c = '_';
|
||||
}
|
||||
}
|
||||
|
||||
filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path,
|
||||
sanitized_name);
|
||||
g_free(sanitized_name);
|
||||
|
||||
fd = mkstemp(filename);
|
||||
if (fd >= 0) {
|
||||
unlink(filename);
|
||||
g_free(filename);
|
||||
break;
|
||||
}
|
||||
g_free(filename);
|
||||
}
|
||||
if (errno != EEXIST && errno != EINTR) {
|
||||
error_setg_errno(errp, errno,
|
||||
"can't open backing store %s for guest RAM",
|
||||
path);
|
||||
goto error;
|
||||
}
|
||||
/*
|
||||
* Try again on EINTR and EEXIST. The latter happens when
|
||||
* something else creates the file between our two open().
|
||||
*/
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
error_setg_errno(errp, errno,
|
||||
"unable to create backing store for hugepages");
|
||||
page_size = qemu_fd_getpagesize(fd);
|
||||
block->mr->align = page_size;
|
||||
|
||||
if (memory < page_size) {
|
||||
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
||||
"or larger than page size 0x%" PRIx64,
|
||||
memory, page_size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
memory = ROUND_UP(memory, hpagesize);
|
||||
memory = ROUND_UP(memory, page_size);
|
||||
|
||||
/*
|
||||
* ftruncate is not supported by hugetlbfs in older
|
||||
@@ -1304,10 +1316,10 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
perror("ftruncate");
|
||||
}
|
||||
|
||||
area = qemu_ram_mmap(fd, memory, hpagesize, block->flags & RAM_SHARED);
|
||||
area = qemu_ram_mmap(fd, memory, page_size, block->flags & RAM_SHARED);
|
||||
if (area == MAP_FAILED) {
|
||||
error_setg_errno(errp, errno,
|
||||
"unable to map backing store for hugepages");
|
||||
"unable to map backing store for guest RAM");
|
||||
close(fd);
|
||||
goto error;
|
||||
}
|
||||
@@ -1320,6 +1332,10 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
return area;
|
||||
|
||||
error:
|
||||
if (unlink_on_error) {
|
||||
unlink(path);
|
||||
}
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@@ -1554,7 +1570,7 @@ static void dirty_memory_extend(ram_addr_t old_ram_size,
|
||||
}
|
||||
}
|
||||
|
||||
static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
static void ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
{
|
||||
RAMBlock *block;
|
||||
RAMBlock *last_block = NULL;
|
||||
@@ -1573,7 +1589,7 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
qemu_mutex_unlock_ramlist();
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
new_block->host = phys_mem_alloc(new_block->max_length,
|
||||
@@ -1583,7 +1599,7 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
"cannot set up guest memory '%s'",
|
||||
memory_region_name(new_block->mr));
|
||||
qemu_mutex_unlock_ramlist();
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
memory_try_enable_merging(new_block->host, new_block->max_length);
|
||||
}
|
||||
@@ -1631,22 +1647,19 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
kvm_setup_guest_memory(new_block->host, new_block->max_length);
|
||||
}
|
||||
}
|
||||
|
||||
return new_block->offset;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
bool share, const char *mem_path,
|
||||
Error **errp)
|
||||
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
bool share, const char *mem_path,
|
||||
Error **errp)
|
||||
{
|
||||
RAMBlock *new_block;
|
||||
ram_addr_t addr;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (xen_enabled()) {
|
||||
error_setg(errp, "-mem-path not supported with Xen");
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (phys_mem_alloc != qemu_anon_ram_alloc) {
|
||||
@@ -1657,7 +1670,7 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
*/
|
||||
error_setg(errp,
|
||||
"-mem-path not supported with this accelerator");
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = HOST_PAGE_ALIGN(size);
|
||||
@@ -1670,29 +1683,28 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
mem_path, errp);
|
||||
if (!new_block->host) {
|
||||
g_free(new_block);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
addr = ram_block_add(new_block, &local_err);
|
||||
ram_block_add(new_block, &local_err);
|
||||
if (local_err) {
|
||||
g_free(new_block);
|
||||
error_propagate(errp, local_err);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
return addr;
|
||||
return new_block;
|
||||
}
|
||||
#endif
|
||||
|
||||
static
|
||||
ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
||||
void (*resized)(const char*,
|
||||
uint64_t length,
|
||||
void *host),
|
||||
void *host, bool resizeable,
|
||||
MemoryRegion *mr, Error **errp)
|
||||
RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
||||
void (*resized)(const char*,
|
||||
uint64_t length,
|
||||
void *host),
|
||||
void *host, bool resizeable,
|
||||
MemoryRegion *mr, Error **errp)
|
||||
{
|
||||
RAMBlock *new_block;
|
||||
ram_addr_t addr;
|
||||
Error *local_err = NULL;
|
||||
|
||||
size = HOST_PAGE_ALIGN(size);
|
||||
@@ -1711,29 +1723,27 @@ ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
||||
if (resizeable) {
|
||||
new_block->flags |= RAM_RESIZEABLE;
|
||||
}
|
||||
addr = ram_block_add(new_block, &local_err);
|
||||
ram_block_add(new_block, &local_err);
|
||||
if (local_err) {
|
||||
g_free(new_block);
|
||||
error_propagate(errp, local_err);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mr->ram_block = new_block;
|
||||
return addr;
|
||||
return new_block;
|
||||
}
|
||||
|
||||
ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
||||
RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
||||
MemoryRegion *mr, Error **errp)
|
||||
{
|
||||
return qemu_ram_alloc_internal(size, size, NULL, host, false, mr, errp);
|
||||
}
|
||||
|
||||
ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp)
|
||||
RAMBlock *qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp)
|
||||
{
|
||||
return qemu_ram_alloc_internal(size, size, NULL, NULL, false, mr, errp);
|
||||
}
|
||||
|
||||
ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz,
|
||||
RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz,
|
||||
void (*resized)(const char*,
|
||||
uint64_t length,
|
||||
void *host),
|
||||
@@ -1759,22 +1769,15 @@ static void reclaim_ramblock(RAMBlock *block)
|
||||
g_free(block);
|
||||
}
|
||||
|
||||
void qemu_ram_free(ram_addr_t addr)
|
||||
void qemu_ram_free(RAMBlock *block)
|
||||
{
|
||||
RAMBlock *block;
|
||||
|
||||
qemu_mutex_lock_ramlist();
|
||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
||||
if (addr == block->offset) {
|
||||
QLIST_REMOVE_RCU(block, next);
|
||||
ram_list.mru_block = NULL;
|
||||
/* Write list before version */
|
||||
smp_wmb();
|
||||
ram_list.version++;
|
||||
call_rcu(block, reclaim_ramblock, rcu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
QLIST_REMOVE_RCU(block, next);
|
||||
ram_list.mru_block = NULL;
|
||||
/* Write list before version */
|
||||
smp_wmb();
|
||||
ram_list.version++;
|
||||
call_rcu(block, reclaim_ramblock, rcu);
|
||||
qemu_mutex_unlock_ramlist();
|
||||
}
|
||||
|
||||
@@ -2707,7 +2710,8 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
|
||||
}
|
||||
} else {
|
||||
/* RAM case */
|
||||
ptr = qemu_get_ram_ptr(mr->ram_block, mr->ram_addr + addr1);
|
||||
ptr = qemu_get_ram_ptr(mr->ram_block,
|
||||
memory_region_get_ram_addr(mr) + addr1);
|
||||
memcpy(buf, ptr, l);
|
||||
}
|
||||
|
||||
|
||||
@@ -83,4 +83,4 @@ static void fsdev_register_config(void)
|
||||
qemu_add_opts(&qemu_fsdev_opts);
|
||||
qemu_add_opts(&qemu_virtfs_opts);
|
||||
}
|
||||
machine_init(fsdev_register_config);
|
||||
opts_init(fsdev_register_config);
|
||||
|
||||
@@ -1752,7 +1752,7 @@ int gdbserver_start(const char *device)
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
}
|
||||
#endif
|
||||
chr = qemu_chr_new("gdb", device, NULL);
|
||||
chr = qemu_chr_new_noreplay("gdb", device, NULL);
|
||||
if (!chr)
|
||||
return -1;
|
||||
|
||||
|
||||
@@ -1026,7 +1026,7 @@ ETEXI
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "Followup to a migration command to switch the migration"
|
||||
" to postcopy mode. The x-postcopy-ram capability must "
|
||||
" to postcopy mode. The postcopy-ram capability must "
|
||||
"be set before the original migration command.",
|
||||
.mhandler.cmd = hmp_migrate_start_postcopy,
|
||||
},
|
||||
@@ -1201,8 +1201,8 @@ ETEXI
|
||||
|
||||
{
|
||||
.name = "drive_add",
|
||||
.args_type = "pci_addr:s,opts:s",
|
||||
.params = "[[<domain>:]<bus>:]<slot>\n"
|
||||
.args_type = "node:-n,pci_addr:s,opts:s",
|
||||
.params = "[-n] [[<domain>:]<bus>:]<slot>\n"
|
||||
"[file=file][,if=type][,bus=n]\n"
|
||||
"[,unit=m][,media=d][,index=i]\n"
|
||||
"[,cyls=c,heads=h,secs=s[,trans=t]]\n"
|
||||
|
||||
@@ -2,7 +2,7 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
|
||||
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
|
||||
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o cpu_hotplug_acpi_table.o
|
||||
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o
|
||||
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
|
||||
obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
|
||||
common-obj-$(CONFIG_ACPI) += acpi_interface.o
|
||||
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
|
||||
common-obj-$(CONFIG_ACPI) += aml-build.o
|
||||
|
||||
@@ -258,6 +258,34 @@ static void build_append_int(GArray *table, uint64_t value)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build NAME(XXXX, 0x00000000) where 0x00000000 is encoded as a dword,
|
||||
* and return the offset to 0x00000000 for runtime patching.
|
||||
*
|
||||
* Warning: runtime patching is best avoided. Only use this as
|
||||
* a replacement for DataTableRegion (for guests that don't
|
||||
* support it).
|
||||
*/
|
||||
int
|
||||
build_append_named_dword(GArray *array, const char *name_format, ...)
|
||||
{
|
||||
int offset;
|
||||
va_list ap;
|
||||
|
||||
build_append_byte(array, 0x08); /* NameOp */
|
||||
va_start(ap, name_format);
|
||||
build_append_namestringv(array, name_format, ap);
|
||||
va_end(ap);
|
||||
|
||||
build_append_byte(array, 0x0C); /* DWordPrefix */
|
||||
|
||||
offset = array->len;
|
||||
build_append_int_noprefix(array, 0x00000000, 4);
|
||||
assert(array->len == offset + 4);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static GPtrArray *alloc_list;
|
||||
|
||||
static Aml *aml_alloc(void)
|
||||
@@ -942,14 +970,14 @@ Aml *aml_package(uint8_t num_elements)
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefOpRegion */
|
||||
Aml *aml_operation_region(const char *name, AmlRegionSpace rs,
|
||||
uint32_t offset, uint32_t len)
|
||||
Aml *offset, uint32_t len)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x80); /* OpRegionOp */
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
build_append_byte(var->buf, rs);
|
||||
build_append_int(var->buf, offset);
|
||||
aml_append(var, offset);
|
||||
build_append_int(var->buf, len);
|
||||
return var;
|
||||
}
|
||||
@@ -997,6 +1025,20 @@ Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name)
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateField */
|
||||
Aml *aml_create_field(Aml *srcbuf, Aml *bit_index, Aml *num_bits,
|
||||
const char *name)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x13); /* CreateFieldOp */
|
||||
aml_append(var, srcbuf);
|
||||
aml_append(var, bit_index);
|
||||
aml_append(var, num_bits);
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
|
||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
|
||||
{
|
||||
@@ -1423,6 +1465,13 @@ Aml *aml_alias(const char *source_object, const char *alias_object)
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefConcat */
|
||||
Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target)
|
||||
{
|
||||
return build_opcode_2arg_dst(0x73 /* ConcatOp */, source1, source2,
|
||||
target);
|
||||
}
|
||||
|
||||
void
|
||||
build_header(GArray *linker, GArray *table_data,
|
||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
|
||||
|
||||
@@ -67,7 +67,7 @@ static void acpi_register_config(void)
|
||||
qemu_add_opts(&qemu_acpi_opts);
|
||||
}
|
||||
|
||||
machine_init(acpi_register_config);
|
||||
opts_init(acpi_register_config);
|
||||
|
||||
static int acpi_checksum(const uint8_t *data, int len)
|
||||
{
|
||||
|
||||
231
hw/acpi/nvdimm.c
231
hw/acpi/nvdimm.c
@@ -29,6 +29,8 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/acpi/bios-linker-loader.h"
|
||||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
|
||||
static int nvdimm_plugged_device_list(Object *obj, void *opaque)
|
||||
@@ -370,15 +372,131 @@ static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets,
|
||||
g_array_free(structures, true);
|
||||
}
|
||||
|
||||
struct NvdimmDsmIn {
|
||||
uint32_t handle;
|
||||
uint32_t revision;
|
||||
uint32_t function;
|
||||
/* the remaining size in the page is used by arg3. */
|
||||
union {
|
||||
uint8_t arg3[0];
|
||||
};
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmDsmIn NvdimmDsmIn;
|
||||
|
||||
struct NvdimmDsmOut {
|
||||
/* the size of buffer filled by QEMU. */
|
||||
uint32_t len;
|
||||
uint8_t data[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmDsmOut NvdimmDsmOut;
|
||||
|
||||
struct NvdimmDsmFunc0Out {
|
||||
/* the size of buffer filled by QEMU. */
|
||||
uint32_t len;
|
||||
uint32_t supported_func;
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmDsmFunc0Out NvdimmDsmFunc0Out;
|
||||
|
||||
struct NvdimmDsmFuncNoPayloadOut {
|
||||
/* the size of buffer filled by QEMU. */
|
||||
uint32_t len;
|
||||
uint32_t func_ret_status;
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmDsmFuncNoPayloadOut NvdimmDsmFuncNoPayloadOut;
|
||||
|
||||
static uint64_t
|
||||
nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
nvdimm_debug("BUG: we never read _DSM IO Port.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
NvdimmDsmIn *in;
|
||||
hwaddr dsm_mem_addr = val;
|
||||
|
||||
nvdimm_debug("dsm memory address %#" HWADDR_PRIx ".\n", dsm_mem_addr);
|
||||
|
||||
/*
|
||||
* The DSM memory is mapped to guest address space so an evil guest
|
||||
* can change its content while we are doing DSM emulation. Avoid
|
||||
* this by copying DSM memory to QEMU local memory.
|
||||
*/
|
||||
in = g_malloc(TARGET_PAGE_SIZE);
|
||||
cpu_physical_memory_read(dsm_mem_addr, in, TARGET_PAGE_SIZE);
|
||||
|
||||
le32_to_cpus(&in->revision);
|
||||
le32_to_cpus(&in->function);
|
||||
le32_to_cpus(&in->handle);
|
||||
|
||||
nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision,
|
||||
in->handle, in->function);
|
||||
|
||||
/*
|
||||
* function 0 is called to inquire which functions are supported by
|
||||
* OSPM
|
||||
*/
|
||||
if (in->function == 0) {
|
||||
NvdimmDsmFunc0Out func0 = {
|
||||
.len = cpu_to_le32(sizeof(func0)),
|
||||
/* No function supported other than function 0 */
|
||||
.supported_func = cpu_to_le32(0),
|
||||
};
|
||||
cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof func0);
|
||||
} else {
|
||||
/* No function except function 0 is supported yet. */
|
||||
NvdimmDsmFuncNoPayloadOut out = {
|
||||
.len = cpu_to_le32(sizeof(out)),
|
||||
.func_ret_status = cpu_to_le32(1) /* Not Supported */,
|
||||
};
|
||||
cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out));
|
||||
}
|
||||
|
||||
g_free(in);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps nvdimm_dsm_ops = {
|
||||
.read = nvdimm_dsm_read,
|
||||
.write = nvdimm_dsm_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io,
|
||||
FWCfgState *fw_cfg, Object *owner)
|
||||
{
|
||||
memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state,
|
||||
"nvdimm-acpi-io", NVDIMM_ACPI_IO_LEN);
|
||||
memory_region_add_subregion(io, NVDIMM_ACPI_IO_BASE, &state->io_mr);
|
||||
|
||||
state->dsm_mem = g_array_new(false, true /* clear */, 1);
|
||||
acpi_data_push(state->dsm_mem, TARGET_PAGE_SIZE);
|
||||
fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data,
|
||||
state->dsm_mem->len);
|
||||
}
|
||||
|
||||
#define NVDIMM_COMMON_DSM "NCAL"
|
||||
#define NVDIMM_ACPI_MEM_ADDR "MEMA"
|
||||
|
||||
static void nvdimm_build_common_dsm(Aml *dev)
|
||||
{
|
||||
Aml *method, *ifctx, *function;
|
||||
Aml *method, *ifctx, *function, *dsm_mem, *unpatched, *result_size;
|
||||
uint8_t byte_list[1];
|
||||
|
||||
method = aml_method(NVDIMM_COMMON_DSM, 4, AML_NOTSERIALIZED);
|
||||
method = aml_method(NVDIMM_COMMON_DSM, 4, AML_SERIALIZED);
|
||||
function = aml_arg(2);
|
||||
dsm_mem = aml_name(NVDIMM_ACPI_MEM_ADDR);
|
||||
|
||||
/*
|
||||
* do not support any method if DSM memory address has not been
|
||||
* patched.
|
||||
*/
|
||||
unpatched = aml_if(aml_equal(dsm_mem, aml_int(0x0)));
|
||||
|
||||
/*
|
||||
* function 0 is called to inquire what functions are supported by
|
||||
@@ -387,12 +505,38 @@ static void nvdimm_build_common_dsm(Aml *dev)
|
||||
ifctx = aml_if(aml_equal(function, aml_int(0)));
|
||||
byte_list[0] = 0 /* No function Supported */;
|
||||
aml_append(ifctx, aml_return(aml_buffer(1, byte_list)));
|
||||
aml_append(method, ifctx);
|
||||
aml_append(unpatched, ifctx);
|
||||
|
||||
/* No function is supported yet. */
|
||||
byte_list[0] = 1 /* Not Supported */;
|
||||
aml_append(method, aml_return(aml_buffer(1, byte_list)));
|
||||
aml_append(unpatched, aml_return(aml_buffer(1, byte_list)));
|
||||
aml_append(method, unpatched);
|
||||
|
||||
/*
|
||||
* The HDLE indicates the DSM function is issued from which device,
|
||||
* it is not used at this time as no function is supported yet.
|
||||
* Currently we make it always be 0 for all the devices and will set
|
||||
* the appropriate value once real function is implemented.
|
||||
*/
|
||||
aml_append(method, aml_store(aml_int(0x0), aml_name("HDLE")));
|
||||
aml_append(method, aml_store(aml_arg(1), aml_name("REVS")));
|
||||
aml_append(method, aml_store(aml_arg(2), aml_name("FUNC")));
|
||||
|
||||
/*
|
||||
* tell QEMU about the real address of DSM memory, then QEMU
|
||||
* gets the control and fills the result in DSM memory.
|
||||
*/
|
||||
aml_append(method, aml_store(dsm_mem, aml_name("NTFI")));
|
||||
|
||||
result_size = aml_local(1);
|
||||
aml_append(method, aml_store(aml_name("RLEN"), result_size));
|
||||
aml_append(method, aml_store(aml_shiftleft(result_size, aml_int(3)),
|
||||
result_size));
|
||||
aml_append(method, aml_create_field(aml_name("ODAT"), aml_int(0),
|
||||
result_size, "OBUF"));
|
||||
aml_append(method, aml_concatenate(aml_buffer(0, NULL), aml_name("OBUF"),
|
||||
aml_arg(6)));
|
||||
aml_append(method, aml_return(aml_arg(6)));
|
||||
aml_append(dev, method);
|
||||
}
|
||||
|
||||
@@ -435,7 +579,8 @@ static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev)
|
||||
static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
|
||||
GArray *table_data, GArray *linker)
|
||||
{
|
||||
Aml *ssdt, *sb_scope, *dev;
|
||||
Aml *ssdt, *sb_scope, *dev, *field;
|
||||
int mem_addr_offset, nvdimm_ssdt;
|
||||
|
||||
acpi_add_table(table_offsets, table_data);
|
||||
|
||||
@@ -459,19 +604,89 @@ static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
|
||||
*/
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012")));
|
||||
|
||||
/* map DSM memory and IO into ACPI namespace. */
|
||||
aml_append(dev, aml_operation_region("NPIO", AML_SYSTEM_IO,
|
||||
aml_int(NVDIMM_ACPI_IO_BASE), NVDIMM_ACPI_IO_LEN));
|
||||
aml_append(dev, aml_operation_region("NRAM", AML_SYSTEM_MEMORY,
|
||||
aml_name(NVDIMM_ACPI_MEM_ADDR), TARGET_PAGE_SIZE));
|
||||
|
||||
/*
|
||||
* DSM notifier:
|
||||
* NTFI: write the address of DSM memory and notify QEMU to emulate
|
||||
* the access.
|
||||
*
|
||||
* It is the IO port so that accessing them will cause VM-exit, the
|
||||
* control will be transferred to QEMU.
|
||||
*/
|
||||
field = aml_field("NPIO", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("NTFI",
|
||||
sizeof(uint32_t) * BITS_PER_BYTE));
|
||||
aml_append(dev, field);
|
||||
|
||||
/*
|
||||
* DSM input:
|
||||
* HDLE: store device's handle, it's zero if the _DSM call happens
|
||||
* on NVDIMM Root Device.
|
||||
* REVS: store the Arg1 of _DSM call.
|
||||
* FUNC: store the Arg2 of _DSM call.
|
||||
* ARG3: store the Arg3 of _DSM call.
|
||||
*
|
||||
* They are RAM mapping on host so that these accesses never cause
|
||||
* VM-EXIT.
|
||||
*/
|
||||
field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("HDLE",
|
||||
sizeof(typeof_field(NvdimmDsmIn, handle)) * BITS_PER_BYTE));
|
||||
aml_append(field, aml_named_field("REVS",
|
||||
sizeof(typeof_field(NvdimmDsmIn, revision)) * BITS_PER_BYTE));
|
||||
aml_append(field, aml_named_field("FUNC",
|
||||
sizeof(typeof_field(NvdimmDsmIn, function)) * BITS_PER_BYTE));
|
||||
aml_append(field, aml_named_field("ARG3",
|
||||
(TARGET_PAGE_SIZE - offsetof(NvdimmDsmIn, arg3)) *
|
||||
BITS_PER_BYTE));
|
||||
aml_append(dev, field);
|
||||
|
||||
/*
|
||||
* DSM output:
|
||||
* RLEN: the size of the buffer filled by QEMU.
|
||||
* ODAT: the buffer QEMU uses to store the result.
|
||||
*
|
||||
* Since the page is reused by both input and out, the input data
|
||||
* will be lost after storing new result into ODAT so we should fetch
|
||||
* all the input data before writing the result.
|
||||
*/
|
||||
field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("RLEN",
|
||||
sizeof(typeof_field(NvdimmDsmOut, len)) * BITS_PER_BYTE));
|
||||
aml_append(field, aml_named_field("ODAT",
|
||||
(TARGET_PAGE_SIZE - offsetof(NvdimmDsmOut, data)) *
|
||||
BITS_PER_BYTE));
|
||||
aml_append(dev, field);
|
||||
|
||||
nvdimm_build_common_dsm(dev);
|
||||
nvdimm_build_device_dsm(dev);
|
||||
|
||||
nvdimm_build_nvdimm_devices(device_list, dev);
|
||||
|
||||
aml_append(sb_scope, dev);
|
||||
|
||||
aml_append(ssdt, sb_scope);
|
||||
|
||||
nvdimm_ssdt = table_data->len;
|
||||
|
||||
/* copy AML table into ACPI tables blob and patch header there */
|
||||
g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
|
||||
mem_addr_offset = build_append_named_dword(table_data,
|
||||
NVDIMM_ACPI_MEM_ADDR);
|
||||
|
||||
bios_linker_loader_alloc(linker, NVDIMM_DSM_MEM_FILE, TARGET_PAGE_SIZE,
|
||||
false /* high memory */);
|
||||
bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
|
||||
NVDIMM_DSM_MEM_FILE, table_data,
|
||||
table_data->data + mem_addr_offset,
|
||||
sizeof(uint32_t));
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + table_data->len - ssdt->buf->len),
|
||||
"SSDT", ssdt->buf->len, 1, NULL, "NVDIMM");
|
||||
(void *)(table_data->data + nvdimm_ssdt),
|
||||
"SSDT", table_data->len - nvdimm_ssdt, 1, NULL, "NVDIMM");
|
||||
free_aml_allocator();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,3 +16,4 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
|
||||
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
|
||||
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
|
||||
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
|
||||
obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o
|
||||
|
||||
137
hw/arm/ast2400.c
Normal file
137
hw/arm/ast2400.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* AST2400 SoC
|
||||
*
|
||||
* Andrew Jeffery <andrew@aj.id.au>
|
||||
* Jeremy Kerr <jk@ozlabs.org>
|
||||
*
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/ast2400.h"
|
||||
#include "hw/char/serial.h"
|
||||
|
||||
#define AST2400_UART_5_BASE 0x00184000
|
||||
#define AST2400_IOMEM_SIZE 0x00200000
|
||||
#define AST2400_IOMEM_BASE 0x1E600000
|
||||
#define AST2400_VIC_BASE 0x1E6C0000
|
||||
#define AST2400_TIMER_BASE 0x1E782000
|
||||
|
||||
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
|
||||
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
|
||||
|
||||
/*
|
||||
* IO handlers: simply catch any reads/writes to IO addresses that aren't
|
||||
* handled by a device mapping.
|
||||
*/
|
||||
|
||||
static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n",
|
||||
__func__, offset, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
|
||||
__func__, offset, value, size);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ast2400_io_ops = {
|
||||
.read = ast2400_io_read,
|
||||
.write = ast2400_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ast2400_init(Object *obj)
|
||||
{
|
||||
AST2400State *s = AST2400(obj);
|
||||
|
||||
s->cpu = cpu_arm_init("arm926");
|
||||
|
||||
object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC);
|
||||
object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL);
|
||||
qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default());
|
||||
|
||||
object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
|
||||
object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL);
|
||||
qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default());
|
||||
}
|
||||
|
||||
static void ast2400_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
int i;
|
||||
AST2400State *s = AST2400(dev);
|
||||
Error *err = NULL;
|
||||
|
||||
/* IO space */
|
||||
memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
|
||||
"ast2400.io", AST2400_IOMEM_SIZE);
|
||||
memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE,
|
||||
&s->iomem, -1);
|
||||
|
||||
/* VIC */
|
||||
object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE);
|
||||
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));
|
||||
|
||||
/* 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, AST2400_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_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
|
||||
}
|
||||
|
||||
/* UART - attach an 8250 to the IO space as our UART5 */
|
||||
if (serial_hds[0]) {
|
||||
qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
|
||||
serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2,
|
||||
uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN);
|
||||
}
|
||||
}
|
||||
|
||||
static void ast2400_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = ast2400_realize;
|
||||
|
||||
/*
|
||||
* Reason: creates an ARM CPU, thus use after free(), see
|
||||
* arm_cpu_class_init()
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo ast2400_type_info = {
|
||||
.name = TYPE_AST2400,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AST2400State),
|
||||
.instance_init = ast2400_init,
|
||||
.class_init = ast2400_class_init,
|
||||
};
|
||||
|
||||
static void ast2400_register_types(void)
|
||||
{
|
||||
type_register_static(&ast2400_type_info);
|
||||
}
|
||||
|
||||
type_init(ast2400_register_types)
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "hw/arm/bcm2835_peripherals.h"
|
||||
#include "hw/misc/bcm2835_mbox_defs.h"
|
||||
#include "hw/arm/raspi_platform.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
/* Peripheral base address on the VC (GPU) system bus */
|
||||
#define BCM2835_VC_PERI_BASE 0x7e000000
|
||||
@@ -48,6 +49,11 @@ static void bcm2835_peripherals_init(Object *obj)
|
||||
object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
|
||||
qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
|
||||
|
||||
/* AUX / UART1 */
|
||||
object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX);
|
||||
object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL);
|
||||
qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default());
|
||||
|
||||
/* Mailboxes */
|
||||
object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
|
||||
object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
|
||||
@@ -56,6 +62,16 @@ static void bcm2835_peripherals_init(Object *obj)
|
||||
object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
|
||||
OBJECT(&s->mbox_mr), &error_abort);
|
||||
|
||||
/* Framebuffer */
|
||||
object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB);
|
||||
object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL);
|
||||
object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size",
|
||||
&error_abort);
|
||||
qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default());
|
||||
|
||||
object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
|
||||
OBJECT(&s->gpu_bus_mr), &error_abort);
|
||||
|
||||
/* Property channel */
|
||||
object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY);
|
||||
object_property_add_child(obj, "property", OBJECT(&s->property), NULL);
|
||||
@@ -63,6 +79,8 @@ static void bcm2835_peripherals_init(Object *obj)
|
||||
"board-rev", &error_abort);
|
||||
qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default());
|
||||
|
||||
object_property_add_const_link(OBJECT(&s->property), "fb",
|
||||
OBJECT(&s->fb), &error_abort);
|
||||
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
|
||||
OBJECT(&s->gpu_bus_mr), &error_abort);
|
||||
|
||||
@@ -70,6 +88,14 @@ static void bcm2835_peripherals_init(Object *obj)
|
||||
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
|
||||
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
|
||||
qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default());
|
||||
|
||||
/* DMA Channels */
|
||||
object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA);
|
||||
object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL);
|
||||
qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default());
|
||||
|
||||
object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
|
||||
OBJECT(&s->gpu_bus_mr), &error_abort);
|
||||
}
|
||||
|
||||
static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
@@ -78,7 +104,8 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
Object *obj;
|
||||
MemoryRegion *ram;
|
||||
Error *err = NULL;
|
||||
uint32_t ram_size;
|
||||
uint32_t ram_size, vcram_size;
|
||||
CharDriverState *chr;
|
||||
int n;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "ram", &err);
|
||||
@@ -131,6 +158,29 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
||||
INTERRUPT_UART));
|
||||
|
||||
/* AUX / UART1 */
|
||||
/* TODO: don't call qemu_char_get_next_serial() here, instead set
|
||||
* chardev properties for each uart at the board level, once pl011
|
||||
* (uart0) has been updated to avoid qemu_char_get_next_serial()
|
||||
*/
|
||||
chr = qemu_char_get_next_serial();
|
||||
if (chr == NULL) {
|
||||
chr = qemu_chr_new("bcm2835.uart1", "null", NULL);
|
||||
}
|
||||
qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
||||
INTERRUPT_AUX));
|
||||
|
||||
/* Mailboxes */
|
||||
object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
|
||||
if (err) {
|
||||
@@ -144,13 +194,33 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
|
||||
INTERRUPT_ARM_MAILBOX));
|
||||
|
||||
/* Property channel */
|
||||
object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err);
|
||||
/* Framebuffer */
|
||||
vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size",
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size,
|
||||
"vcram-base", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->fb), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
|
||||
|
||||
/* Property channel */
|
||||
object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
@@ -171,6 +241,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
@@ -189,6 +266,24 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/* DMA Channels */
|
||||
object_property_set_bool(OBJECT(&s->dma), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_add_subregion(&s->peri_mr, DMA_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0));
|
||||
memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1));
|
||||
|
||||
for (n = 0; n <= 12; n++) {
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->ic),
|
||||
BCM2835_IC_GPU_IRQ,
|
||||
INTERRUPT_DMA0 + n));
|
||||
}
|
||||
}
|
||||
|
||||
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
|
||||
@@ -196,6 +291,8 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = bcm2835_peripherals_realize;
|
||||
/* Reason: realize() method uses qemu_char_get_next_serial() */
|
||||
dc->cannot_instantiate_with_device_add_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo bcm2835_peripherals_type_info = {
|
||||
|
||||
@@ -42,6 +42,8 @@ static void bcm2836_init(Object *obj)
|
||||
&error_abort);
|
||||
object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals),
|
||||
"board-rev", &error_abort);
|
||||
object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals),
|
||||
"vcram-size", &error_abort);
|
||||
qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default());
|
||||
}
|
||||
|
||||
|
||||
@@ -181,4 +181,4 @@ static void exynos4_machines_init(void)
|
||||
type_register_static(&smdkc210_type);
|
||||
}
|
||||
|
||||
machine_init(exynos4_machines_init)
|
||||
type_init(exynos4_machines_init)
|
||||
|
||||
@@ -291,6 +291,7 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
|
||||
* arm_cpu_class_init()
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
dc->desc = "i.MX25 SOC";
|
||||
}
|
||||
|
||||
static const TypeInfo fsl_imx25_type_info = {
|
||||
|
||||
@@ -265,6 +265,7 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
|
||||
* arm_cpu_class_init()
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
dc->desc = "i.MX31 SOC";
|
||||
}
|
||||
|
||||
static const TypeInfo fsl_imx31_type_info = {
|
||||
|
||||
@@ -156,4 +156,4 @@ static void gumstix_machine_init(void)
|
||||
type_register_static(&verdex_type);
|
||||
}
|
||||
|
||||
machine_init(gumstix_machine_init)
|
||||
type_init(gumstix_machine_init)
|
||||
|
||||
@@ -437,4 +437,4 @@ static void calxeda_machines_init(void)
|
||||
type_register_static(&midway_type);
|
||||
}
|
||||
|
||||
machine_init(calxeda_machines_init)
|
||||
type_init(calxeda_machines_init)
|
||||
|
||||
@@ -1450,4 +1450,4 @@ static void nseries_machine_init(void)
|
||||
type_register_static(&n810_type);
|
||||
}
|
||||
|
||||
machine_init(nseries_machine_init)
|
||||
type_init(nseries_machine_init)
|
||||
|
||||
@@ -252,4 +252,4 @@ static void sx1_machine_init(void)
|
||||
type_register_static(&sx1_machine_v2_type);
|
||||
}
|
||||
|
||||
machine_init(sx1_machine_init)
|
||||
type_init(sx1_machine_init)
|
||||
|
||||
65
hw/arm/palmetto-bmc.c
Normal file
65
hw/arm/palmetto-bmc.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* OpenPOWER Palmetto BMC
|
||||
*
|
||||
* Andrew Jeffery <andrew@aj.id.au>
|
||||
*
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/arm.h"
|
||||
#include "hw/arm/ast2400.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
static struct arm_boot_info palmetto_bmc_binfo = {
|
||||
.loader_start = AST2400_SDRAM_BASE,
|
||||
.board_id = 0,
|
||||
.nb_cpus = 1,
|
||||
};
|
||||
|
||||
typedef struct PalmettoBMCState {
|
||||
AST2400State soc;
|
||||
MemoryRegion ram;
|
||||
} PalmettoBMCState;
|
||||
|
||||
static void palmetto_bmc_init(MachineState *machine)
|
||||
{
|
||||
PalmettoBMCState *bmc;
|
||||
|
||||
bmc = g_new0(PalmettoBMCState, 1);
|
||||
object_initialize(&bmc->soc, (sizeof(bmc->soc)), TYPE_AST2400);
|
||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&bmc->soc),
|
||||
&error_abort);
|
||||
|
||||
memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
|
||||
memory_region_add_subregion(get_system_memory(), AST2400_SDRAM_BASE,
|
||||
&bmc->ram);
|
||||
object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
|
||||
&error_abort);
|
||||
|
||||
palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
|
||||
palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
|
||||
palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
|
||||
palmetto_bmc_binfo.ram_size = ram_size;
|
||||
arm_load_kernel(ARM_CPU(first_cpu), &palmetto_bmc_binfo);
|
||||
}
|
||||
|
||||
static void palmetto_bmc_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "OpenPOWER Palmetto BMC";
|
||||
mc->init = palmetto_bmc_init;
|
||||
mc->max_cpus = 1;
|
||||
mc->no_sdcard = 1;
|
||||
mc->no_floppy = 1;
|
||||
mc->no_cdrom = 1;
|
||||
mc->no_sdcard = 1;
|
||||
mc->no_parallel = 1;
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("palmetto-bmc", palmetto_bmc_machine_init);
|
||||
@@ -113,6 +113,7 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
|
||||
static void raspi2_init(MachineState *machine)
|
||||
{
|
||||
RasPiState *s = g_new0(RasPiState, 1);
|
||||
uint32_t vcram_size;
|
||||
DriveInfo *di;
|
||||
BlockBackend *blk;
|
||||
BusState *bus;
|
||||
@@ -149,7 +150,9 @@ static void raspi2_init(MachineState *machine)
|
||||
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
||||
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
|
||||
|
||||
setup_boot(machine, 2, machine->ram_size);
|
||||
vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size",
|
||||
&error_abort);
|
||||
setup_boot(machine, 2, machine->ram_size - vcram_size);
|
||||
}
|
||||
|
||||
static void raspi2_machine_init(MachineClass *mc)
|
||||
@@ -161,11 +164,6 @@ static void raspi2_machine_init(MachineClass *mc)
|
||||
mc->no_floppy = 1;
|
||||
mc->no_cdrom = 1;
|
||||
mc->max_cpus = BCM2836_NCPUS;
|
||||
|
||||
/* XXX: Temporary restriction in RAM size from the full 1GB. Since
|
||||
* we do not yet support the framebuffer / GPU, we need to limit
|
||||
* RAM usable by the OS to sit below the peripherals.
|
||||
*/
|
||||
mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */
|
||||
mc->default_ram_size = 1024 * 1024 * 1024;
|
||||
};
|
||||
DEFINE_MACHINE("raspi2", raspi2_machine_init)
|
||||
|
||||
@@ -457,4 +457,4 @@ static void realview_machine_init(void)
|
||||
type_register_static(&realview_pbx_a9_type);
|
||||
}
|
||||
|
||||
machine_init(realview_machine_init)
|
||||
type_init(realview_machine_init)
|
||||
|
||||
@@ -1037,7 +1037,7 @@ static void spitz_machine_init(void)
|
||||
type_register_static(&terrierpda_type);
|
||||
}
|
||||
|
||||
machine_init(spitz_machine_init)
|
||||
type_init(spitz_machine_init)
|
||||
|
||||
static bool is_version_0(void *opaque, int version_id)
|
||||
{
|
||||
|
||||
@@ -1420,7 +1420,7 @@ static void stellaris_machine_init(void)
|
||||
type_register_static(&lm3s6965evb_type);
|
||||
}
|
||||
|
||||
machine_init(stellaris_machine_init)
|
||||
type_init(stellaris_machine_init)
|
||||
|
||||
static void stellaris_i2c_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
@@ -240,7 +240,7 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
|
||||
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
|
||||
reg_attr[2 * i] = cpu_to_be32(mmio_base);
|
||||
reg_attr[2 * i + 1] = cpu_to_be32(
|
||||
memory_region_size(&vdev->regions[i]->mem));
|
||||
memory_region_size(vdev->regions[i]->mem));
|
||||
}
|
||||
qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
|
||||
vbasedev->num_regions * 2 * sizeof(uint32_t));
|
||||
@@ -374,7 +374,7 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque)
|
||||
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
|
||||
reg_attr[2 * i] = cpu_to_be32(mmio_base);
|
||||
reg_attr[2 * i + 1] = cpu_to_be32(
|
||||
memory_region_size(&vdev->regions[i]->mem));
|
||||
memory_region_size(vdev->regions[i]->mem));
|
||||
}
|
||||
qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr,
|
||||
vbasedev->num_regions * 2 * sizeof(uint32_t));
|
||||
|
||||
@@ -419,7 +419,7 @@ static void versatile_machine_init(void)
|
||||
type_register_static(&versatileab_type);
|
||||
}
|
||||
|
||||
machine_init(versatile_machine_init)
|
||||
type_init(versatile_machine_init)
|
||||
|
||||
static void vpb_sic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
@@ -800,4 +800,4 @@ static void vexpress_machine_init(void)
|
||||
type_register_static(&vexpress_a15_info);
|
||||
}
|
||||
|
||||
machine_init(vexpress_machine_init);
|
||||
type_init(vexpress_machine_init);
|
||||
|
||||
@@ -81,6 +81,20 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap)
|
||||
{
|
||||
Aml *dev = aml_device("FWCF");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
|
||||
/* device present, functioning, decoding, not shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||
|
||||
Aml *crs = aml_resource_template();
|
||||
aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base,
|
||||
fw_cfg_memmap->size, AML_READ_WRITE));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
|
||||
{
|
||||
Aml *dev, *crs;
|
||||
@@ -549,6 +563,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
||||
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
|
||||
(irqmap[VIRT_UART] + ARM_SPI_BASE));
|
||||
acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
|
||||
acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
|
||||
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
|
||||
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
|
||||
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
|
||||
|
||||
@@ -1345,7 +1345,32 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static void virt_instance_init(Object *obj)
|
||||
static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->init = machvirt_init;
|
||||
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
|
||||
* it later in machvirt_init, where we have more information about the
|
||||
* configuration of the particular instance.
|
||||
*/
|
||||
mc->max_cpus = MAX_CPUMASK_BITS;
|
||||
mc->has_dynamic_sysbus = true;
|
||||
mc->block_default_type = IF_VIRTIO;
|
||||
mc->no_cdrom = 1;
|
||||
mc->pci_allow_0_address = true;
|
||||
}
|
||||
|
||||
static const TypeInfo virt_machine_info = {
|
||||
.name = TYPE_VIRT_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.abstract = true,
|
||||
.instance_size = sizeof(VirtMachineState),
|
||||
.class_size = sizeof(VirtMachineClass),
|
||||
.class_init = virt_machine_class_init,
|
||||
};
|
||||
|
||||
static void virt_2_6_instance_init(Object *obj)
|
||||
{
|
||||
VirtMachineState *vms = VIRT_MACHINE(obj);
|
||||
|
||||
@@ -1378,35 +1403,29 @@ static void virt_instance_init(Object *obj)
|
||||
"Valid values are 2, 3 and host", NULL);
|
||||
}
|
||||
|
||||
static void virt_class_init(ObjectClass *oc, void *data)
|
||||
static void virt_2_6_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
static GlobalProperty compat_props[] = {
|
||||
{ /* end of list */ }
|
||||
};
|
||||
|
||||
mc->desc = "ARM Virtual Machine",
|
||||
mc->init = machvirt_init;
|
||||
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
|
||||
* it later in machvirt_init, where we have more information about the
|
||||
* configuration of the particular instance.
|
||||
*/
|
||||
mc->max_cpus = MAX_CPUMASK_BITS;
|
||||
mc->has_dynamic_sysbus = true;
|
||||
mc->block_default_type = IF_VIRTIO;
|
||||
mc->no_cdrom = 1;
|
||||
mc->pci_allow_0_address = true;
|
||||
mc->desc = "QEMU 2.6 ARM Virtual Machine";
|
||||
mc->alias = "virt";
|
||||
mc->compat_props = compat_props;
|
||||
}
|
||||
|
||||
static const TypeInfo machvirt_info = {
|
||||
.name = TYPE_VIRT_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(VirtMachineState),
|
||||
.instance_init = virt_instance_init,
|
||||
.class_size = sizeof(VirtMachineClass),
|
||||
.class_init = virt_class_init,
|
||||
.name = MACHINE_TYPE_NAME("virt-2.6"),
|
||||
.parent = TYPE_VIRT_MACHINE,
|
||||
.instance_init = virt_2_6_instance_init,
|
||||
.class_init = virt_2_6_class_init,
|
||||
};
|
||||
|
||||
static void machvirt_machine_init(void)
|
||||
{
|
||||
type_register_static(&virt_machine_info);
|
||||
type_register_static(&machvirt_info);
|
||||
}
|
||||
|
||||
machine_init(machvirt_machine_init);
|
||||
type_init(machvirt_machine_init);
|
||||
|
||||
@@ -2557,6 +2557,29 @@ FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
|
||||
return isa->state.drives[i].drive;
|
||||
}
|
||||
|
||||
void isa_fdc_get_drive_max_chs(FloppyDriveType type,
|
||||
uint8_t *maxc, uint8_t *maxh, uint8_t *maxs)
|
||||
{
|
||||
const FDFormat *fdf;
|
||||
|
||||
*maxc = *maxh = *maxs = 0;
|
||||
for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) {
|
||||
if (fdf->drive != type) {
|
||||
continue;
|
||||
}
|
||||
if (*maxc < fdf->max_track) {
|
||||
*maxc = fdf->max_track;
|
||||
}
|
||||
if (*maxh < fdf->max_head) {
|
||||
*maxh = fdf->max_head;
|
||||
}
|
||||
if (*maxs < fdf->last_sect) {
|
||||
*maxs = fdf->last_sect;
|
||||
}
|
||||
}
|
||||
(*maxc)--;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_isa_fdc ={
|
||||
.name = "fdc",
|
||||
.version_id = 2,
|
||||
|
||||
@@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_vty.o
|
||||
obj-$(CONFIG_DIGIC) += digic-uart.o
|
||||
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
|
||||
obj-$(CONFIG_RASPI) += bcm2835_aux.o
|
||||
|
||||
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
||||
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
|
||||
|
||||
316
hw/char/bcm2835_aux.c
Normal file
316
hw/char/bcm2835_aux.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
|
||||
* Copyright (c) 2015, Microsoft
|
||||
* Written by Andrew Baumann
|
||||
* Based on pl011.c, copyright terms below:
|
||||
*
|
||||
* Arm PrimeCell PL011 UART
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licensed under the GPL.
|
||||
*
|
||||
* At present only the core UART functions (data path for tx/rx) are
|
||||
* implemented. The following features/registers are unimplemented:
|
||||
* - Line/modem control
|
||||
* - Scratch register
|
||||
* - Extra control
|
||||
* - Baudrate
|
||||
* - SPI interfaces
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/char/bcm2835_aux.h"
|
||||
|
||||
#define AUX_IRQ 0x0
|
||||
#define AUX_ENABLES 0x4
|
||||
#define AUX_MU_IO_REG 0x40
|
||||
#define AUX_MU_IER_REG 0x44
|
||||
#define AUX_MU_IIR_REG 0x48
|
||||
#define AUX_MU_LCR_REG 0x4c
|
||||
#define AUX_MU_MCR_REG 0x50
|
||||
#define AUX_MU_LSR_REG 0x54
|
||||
#define AUX_MU_MSR_REG 0x58
|
||||
#define AUX_MU_SCRATCH 0x5c
|
||||
#define AUX_MU_CNTL_REG 0x60
|
||||
#define AUX_MU_STAT_REG 0x64
|
||||
#define AUX_MU_BAUD_REG 0x68
|
||||
|
||||
/* bits in IER/IIR registers */
|
||||
#define TX_INT 0x1
|
||||
#define RX_INT 0x2
|
||||
|
||||
static void bcm2835_aux_update(BCM2835AuxState *s)
|
||||
{
|
||||
/* signal an interrupt if either:
|
||||
* 1. rx interrupt is enabled and we have a non-empty rx fifo, or
|
||||
* 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
|
||||
*/
|
||||
s->iir = 0;
|
||||
if ((s->ier & RX_INT) && s->read_count != 0) {
|
||||
s->iir |= RX_INT;
|
||||
}
|
||||
if (s->ier & TX_INT) {
|
||||
s->iir |= TX_INT;
|
||||
}
|
||||
qemu_set_irq(s->irq, s->iir != 0);
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
uint32_t c, res;
|
||||
|
||||
switch (offset) {
|
||||
case AUX_IRQ:
|
||||
return s->iir != 0;
|
||||
|
||||
case AUX_ENABLES:
|
||||
return 1; /* mini UART permanently enabled */
|
||||
|
||||
case AUX_MU_IO_REG:
|
||||
/* "DLAB bit set means access baudrate register" is NYI */
|
||||
c = s->read_fifo[s->read_pos];
|
||||
if (s->read_count > 0) {
|
||||
s->read_count--;
|
||||
if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
|
||||
s->read_pos = 0;
|
||||
}
|
||||
}
|
||||
if (s->chr) {
|
||||
qemu_chr_accept_input(s->chr);
|
||||
}
|
||||
bcm2835_aux_update(s);
|
||||
return c;
|
||||
|
||||
case AUX_MU_IER_REG:
|
||||
/* "DLAB bit set means access baudrate register" is NYI */
|
||||
return 0xc0 | s->ier; /* FIFO enables always read 1 */
|
||||
|
||||
case AUX_MU_IIR_REG:
|
||||
res = 0xc0; /* FIFO enables */
|
||||
/* The spec is unclear on what happens when both tx and rx
|
||||
* interrupts are active, besides that this cannot occur. At
|
||||
* present, we choose to prioritise the rx interrupt, since
|
||||
* the tx fifo is always empty. */
|
||||
if (s->read_count != 0) {
|
||||
res |= 0x4;
|
||||
} else {
|
||||
res |= 0x2;
|
||||
}
|
||||
if (s->iir == 0) {
|
||||
res |= 0x1;
|
||||
}
|
||||
return res;
|
||||
|
||||
case AUX_MU_LCR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
case AUX_MU_MCR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
case AUX_MU_LSR_REG:
|
||||
res = 0x60; /* tx idle, empty */
|
||||
if (s->read_count != 0) {
|
||||
res |= 0x1;
|
||||
}
|
||||
return res;
|
||||
|
||||
case AUX_MU_MSR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
case AUX_MU_SCRATCH:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
case AUX_MU_CNTL_REG:
|
||||
return 0x3; /* tx, rx enabled */
|
||||
|
||||
case AUX_MU_STAT_REG:
|
||||
res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
|
||||
if (s->read_count > 0) {
|
||||
res |= 0x1; /* data in input buffer */
|
||||
assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
|
||||
res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
|
||||
}
|
||||
return res;
|
||||
|
||||
case AUX_MU_BAUD_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
unsigned char ch;
|
||||
|
||||
switch (offset) {
|
||||
case AUX_ENABLES:
|
||||
if (value != 1) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
|
||||
"or disable UART\n", __func__);
|
||||
}
|
||||
break;
|
||||
|
||||
case AUX_MU_IO_REG:
|
||||
/* "DLAB bit set means access baudrate register" is NYI */
|
||||
ch = value;
|
||||
if (s->chr) {
|
||||
qemu_chr_fe_write(s->chr, &ch, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case AUX_MU_IER_REG:
|
||||
/* "DLAB bit set means access baudrate register" is NYI */
|
||||
s->ier = value & (TX_INT | RX_INT);
|
||||
bcm2835_aux_update(s);
|
||||
break;
|
||||
|
||||
case AUX_MU_IIR_REG:
|
||||
if (value & 0x2) {
|
||||
s->read_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case AUX_MU_LCR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
case AUX_MU_MCR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
case AUX_MU_SCRATCH:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
case AUX_MU_CNTL_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
case AUX_MU_BAUD_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
}
|
||||
|
||||
bcm2835_aux_update(s);
|
||||
}
|
||||
|
||||
static int bcm2835_aux_can_receive(void *opaque)
|
||||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
|
||||
return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
|
||||
}
|
||||
|
||||
static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
|
||||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
int slot;
|
||||
|
||||
slot = s->read_pos + s->read_count;
|
||||
if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
|
||||
slot -= BCM2835_AUX_RX_FIFO_LEN;
|
||||
}
|
||||
s->read_fifo[slot] = value;
|
||||
s->read_count++;
|
||||
if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
|
||||
/* buffer full */
|
||||
}
|
||||
bcm2835_aux_update(s);
|
||||
}
|
||||
|
||||
static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
bcm2835_aux_put_fifo(opaque, *buf);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bcm2835_aux_ops = {
|
||||
.read = bcm2835_aux_read,
|
||||
.write = bcm2835_aux_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_bcm2835_aux = {
|
||||
.name = TYPE_BCM2835_AUX,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
|
||||
BCM2835_AUX_RX_FIFO_LEN),
|
||||
VMSTATE_UINT8(read_pos, BCM2835AuxState),
|
||||
VMSTATE_UINT8(read_count, BCM2835AuxState),
|
||||
VMSTATE_UINT8(ier, BCM2835AuxState),
|
||||
VMSTATE_UINT8(iir, BCM2835AuxState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void bcm2835_aux_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
BCM2835AuxState *s = BCM2835_AUX(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
|
||||
TYPE_BCM2835_AUX, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
BCM2835AuxState *s = BCM2835_AUX(dev);
|
||||
|
||||
if (s->chr) {
|
||||
qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive,
|
||||
bcm2835_aux_receive, NULL, s);
|
||||
}
|
||||
}
|
||||
|
||||
static Property bcm2835_aux_props[] = {
|
||||
DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = bcm2835_aux_realize;
|
||||
dc->vmsd = &vmstate_bcm2835_aux;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
dc->props = bcm2835_aux_props;
|
||||
}
|
||||
|
||||
static const TypeInfo bcm2835_aux_info = {
|
||||
.name = TYPE_BCM2835_AUX,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(BCM2835AuxState),
|
||||
.instance_init = bcm2835_aux_init,
|
||||
.class_init = bcm2835_aux_class_init,
|
||||
};
|
||||
|
||||
static void bcm2835_aux_register_types(void)
|
||||
{
|
||||
type_register_static(&bcm2835_aux_info);
|
||||
}
|
||||
|
||||
type_init(bcm2835_aux_register_types)
|
||||
@@ -27,6 +27,7 @@ endif
|
||||
obj-$(CONFIG_OMAP) += omap_dss.o
|
||||
obj-$(CONFIG_OMAP) += omap_lcdc.o
|
||||
obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o
|
||||
obj-$(CONFIG_RASPI) += bcm2835_fb.o
|
||||
obj-$(CONFIG_SM501) += sm501.o
|
||||
obj-$(CONFIG_TCX) += tcx.o
|
||||
obj-$(CONFIG_CG3) += cg3.o
|
||||
|
||||
424
hw/display/bcm2835_fb.c
Normal file
424
hw/display/bcm2835_fb.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Raspberry Pi emulation (c) 2012 Gregory Estrade
|
||||
* Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
|
||||
* This code is licensed under the GNU GPLv2 and later.
|
||||
*
|
||||
* Heavily based on milkymist-vgafb.c, copyright terms below:
|
||||
* QEMU model of the Milkymist VGA framebuffer.
|
||||
*
|
||||
* Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/display/bcm2835_fb.h"
|
||||
#include "hw/display/framebuffer.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "hw/misc/bcm2835_mbox_defs.h"
|
||||
|
||||
#define DEFAULT_VCRAM_SIZE 0x4000000
|
||||
#define BCM2835_FB_OFFSET 0x00100000
|
||||
|
||||
static void fb_invalidate_display(void *opaque)
|
||||
{
|
||||
BCM2835FBState *s = BCM2835_FB(opaque);
|
||||
|
||||
s->invalidate = true;
|
||||
}
|
||||
|
||||
static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
|
||||
int width, int deststep)
|
||||
{
|
||||
BCM2835FBState *s = opaque;
|
||||
uint16_t rgb565;
|
||||
uint32_t rgb888;
|
||||
uint8_t r, g, b;
|
||||
DisplaySurface *surface = qemu_console_surface(s->con);
|
||||
int bpp = surface_bits_per_pixel(surface);
|
||||
|
||||
while (width--) {
|
||||
switch (s->bpp) {
|
||||
case 8:
|
||||
/* lookup palette starting at video ram base
|
||||
* TODO: cache translation, rather than doing this each time!
|
||||
*/
|
||||
rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2));
|
||||
r = (rgb888 >> 0) & 0xff;
|
||||
g = (rgb888 >> 8) & 0xff;
|
||||
b = (rgb888 >> 16) & 0xff;
|
||||
src++;
|
||||
break;
|
||||
case 16:
|
||||
rgb565 = lduw_le_p(src);
|
||||
r = ((rgb565 >> 11) & 0x1f) << 3;
|
||||
g = ((rgb565 >> 5) & 0x3f) << 2;
|
||||
b = ((rgb565 >> 0) & 0x1f) << 3;
|
||||
src += 2;
|
||||
break;
|
||||
case 24:
|
||||
rgb888 = ldl_le_p(src);
|
||||
r = (rgb888 >> 0) & 0xff;
|
||||
g = (rgb888 >> 8) & 0xff;
|
||||
b = (rgb888 >> 16) & 0xff;
|
||||
src += 3;
|
||||
break;
|
||||
case 32:
|
||||
rgb888 = ldl_le_p(src);
|
||||
r = (rgb888 >> 0) & 0xff;
|
||||
g = (rgb888 >> 8) & 0xff;
|
||||
b = (rgb888 >> 16) & 0xff;
|
||||
src += 4;
|
||||
break;
|
||||
default:
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->pixo == 0) {
|
||||
/* swap to BGR pixel format */
|
||||
uint8_t tmp = r;
|
||||
r = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
switch (bpp) {
|
||||
case 8:
|
||||
*dst++ = rgb_to_pixel8(r, g, b);
|
||||
break;
|
||||
case 15:
|
||||
*(uint16_t *)dst = rgb_to_pixel15(r, g, b);
|
||||
dst += 2;
|
||||
break;
|
||||
case 16:
|
||||
*(uint16_t *)dst = rgb_to_pixel16(r, g, b);
|
||||
dst += 2;
|
||||
break;
|
||||
case 24:
|
||||
rgb888 = rgb_to_pixel24(r, g, b);
|
||||
*dst++ = rgb888 & 0xff;
|
||||
*dst++ = (rgb888 >> 8) & 0xff;
|
||||
*dst++ = (rgb888 >> 16) & 0xff;
|
||||
break;
|
||||
case 32:
|
||||
*(uint32_t *)dst = rgb_to_pixel32(r, g, b);
|
||||
dst += 4;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fb_update_display(void *opaque)
|
||||
{
|
||||
BCM2835FBState *s = opaque;
|
||||
DisplaySurface *surface = qemu_console_surface(s->con);
|
||||
int first = 0;
|
||||
int last = 0;
|
||||
int src_width = 0;
|
||||
int dest_width = 0;
|
||||
|
||||
if (s->lock || !s->xres) {
|
||||
return;
|
||||
}
|
||||
|
||||
src_width = s->xres * (s->bpp >> 3);
|
||||
dest_width = s->xres;
|
||||
|
||||
switch (surface_bits_per_pixel(surface)) {
|
||||
case 0:
|
||||
return;
|
||||
case 8:
|
||||
break;
|
||||
case 15:
|
||||
dest_width *= 2;
|
||||
break;
|
||||
case 16:
|
||||
dest_width *= 2;
|
||||
break;
|
||||
case 24:
|
||||
dest_width *= 3;
|
||||
break;
|
||||
case 32:
|
||||
dest_width *= 4;
|
||||
break;
|
||||
default:
|
||||
hw_error("bcm2835_fb: bad color depth\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->invalidate) {
|
||||
framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base,
|
||||
s->yres, src_width);
|
||||
}
|
||||
|
||||
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
|
||||
src_width, dest_width, 0, s->invalidate,
|
||||
draw_line_src16, s, &first, &last);
|
||||
|
||||
if (first >= 0) {
|
||||
dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1);
|
||||
}
|
||||
|
||||
s->invalidate = false;
|
||||
}
|
||||
|
||||
static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
|
||||
{
|
||||
value &= ~0xf;
|
||||
|
||||
s->lock = true;
|
||||
|
||||
s->xres = ldl_le_phys(&s->dma_as, value);
|
||||
s->yres = ldl_le_phys(&s->dma_as, value + 4);
|
||||
s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
|
||||
s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
|
||||
s->bpp = ldl_le_phys(&s->dma_as, value + 20);
|
||||
s->xoffset = ldl_le_phys(&s->dma_as, value + 24);
|
||||
s->yoffset = ldl_le_phys(&s->dma_as, value + 28);
|
||||
|
||||
s->base = s->vcram_base | (value & 0xc0000000);
|
||||
s->base += BCM2835_FB_OFFSET;
|
||||
|
||||
/* TODO - Manage properly virtual resolution */
|
||||
|
||||
s->pitch = s->xres * (s->bpp >> 3);
|
||||
s->size = s->yres * s->pitch;
|
||||
|
||||
stl_le_phys(&s->dma_as, value + 16, s->pitch);
|
||||
stl_le_phys(&s->dma_as, value + 32, s->base);
|
||||
stl_le_phys(&s->dma_as, value + 36, s->size);
|
||||
|
||||
s->invalidate = true;
|
||||
qemu_console_resize(s->con, s->xres, s->yres);
|
||||
s->lock = false;
|
||||
}
|
||||
|
||||
void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
|
||||
uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
|
||||
uint32_t *pixo, uint32_t *alpha)
|
||||
{
|
||||
s->lock = true;
|
||||
|
||||
/* TODO: input validation! */
|
||||
if (xres) {
|
||||
s->xres = *xres;
|
||||
}
|
||||
if (yres) {
|
||||
s->yres = *yres;
|
||||
}
|
||||
if (xoffset) {
|
||||
s->xoffset = *xoffset;
|
||||
}
|
||||
if (yoffset) {
|
||||
s->yoffset = *yoffset;
|
||||
}
|
||||
if (bpp) {
|
||||
s->bpp = *bpp;
|
||||
}
|
||||
if (pixo) {
|
||||
s->pixo = *pixo;
|
||||
}
|
||||
if (alpha) {
|
||||
s->alpha = *alpha;
|
||||
}
|
||||
|
||||
/* TODO - Manage properly virtual resolution */
|
||||
|
||||
s->pitch = s->xres * (s->bpp >> 3);
|
||||
s->size = s->yres * s->pitch;
|
||||
|
||||
s->invalidate = true;
|
||||
qemu_console_resize(s->con, s->xres, s->yres);
|
||||
s->lock = false;
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
BCM2835FBState *s = opaque;
|
||||
uint32_t res = 0;
|
||||
|
||||
switch (offset) {
|
||||
case MBOX_AS_DATA:
|
||||
res = MBOX_CHAN_FB;
|
||||
s->pending = false;
|
||||
qemu_set_irq(s->mbox_irq, 0);
|
||||
break;
|
||||
|
||||
case MBOX_AS_PENDING:
|
||||
res = s->pending;
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
BCM2835FBState *s = opaque;
|
||||
|
||||
switch (offset) {
|
||||
case MBOX_AS_DATA:
|
||||
/* bcm2835_mbox should check our pending status before pushing */
|
||||
assert(!s->pending);
|
||||
s->pending = true;
|
||||
bcm2835_fb_mbox_push(s, value);
|
||||
qemu_set_irq(s->mbox_irq, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bcm2835_fb_ops = {
|
||||
.read = bcm2835_fb_read,
|
||||
.write = bcm2835_fb_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_bcm2835_fb = {
|
||||
.name = TYPE_BCM2835_FB,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(lock, BCM2835FBState),
|
||||
VMSTATE_BOOL(invalidate, BCM2835FBState),
|
||||
VMSTATE_BOOL(pending, BCM2835FBState),
|
||||
VMSTATE_UINT32(xres, BCM2835FBState),
|
||||
VMSTATE_UINT32(yres, BCM2835FBState),
|
||||
VMSTATE_UINT32(xres_virtual, BCM2835FBState),
|
||||
VMSTATE_UINT32(yres_virtual, BCM2835FBState),
|
||||
VMSTATE_UINT32(xoffset, BCM2835FBState),
|
||||
VMSTATE_UINT32(yoffset, BCM2835FBState),
|
||||
VMSTATE_UINT32(bpp, BCM2835FBState),
|
||||
VMSTATE_UINT32(base, BCM2835FBState),
|
||||
VMSTATE_UINT32(pitch, BCM2835FBState),
|
||||
VMSTATE_UINT32(size, BCM2835FBState),
|
||||
VMSTATE_UINT32(pixo, BCM2835FBState),
|
||||
VMSTATE_UINT32(alpha, BCM2835FBState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const GraphicHwOps vgafb_ops = {
|
||||
.invalidate = fb_invalidate_display,
|
||||
.gfx_update = fb_update_display,
|
||||
};
|
||||
|
||||
static void bcm2835_fb_init(Object *obj)
|
||||
{
|
||||
BCM2835FBState *s = BCM2835_FB(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB,
|
||||
0x10);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
|
||||
}
|
||||
|
||||
static void bcm2835_fb_reset(DeviceState *dev)
|
||||
{
|
||||
BCM2835FBState *s = BCM2835_FB(dev);
|
||||
|
||||
s->pending = false;
|
||||
|
||||
s->xres_virtual = s->xres;
|
||||
s->yres_virtual = s->yres;
|
||||
s->xoffset = 0;
|
||||
s->yoffset = 0;
|
||||
s->base = s->vcram_base + BCM2835_FB_OFFSET;
|
||||
s->pitch = s->xres * (s->bpp >> 3);
|
||||
s->size = s->yres * s->pitch;
|
||||
|
||||
s->invalidate = true;
|
||||
s->lock = false;
|
||||
}
|
||||
|
||||
static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
BCM2835FBState *s = BCM2835_FB(dev);
|
||||
Error *err = NULL;
|
||||
Object *obj;
|
||||
|
||||
if (s->vcram_base == 0) {
|
||||
error_setg(errp, "%s: required vcram-base property not set", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
|
||||
if (obj == NULL) {
|
||||
error_setg(errp, "%s: required dma-mr link not found: %s",
|
||||
__func__, error_get_pretty(err));
|
||||
return;
|
||||
}
|
||||
|
||||
s->dma_mr = MEMORY_REGION(obj);
|
||||
address_space_init(&s->dma_as, s->dma_mr, NULL);
|
||||
|
||||
bcm2835_fb_reset(dev);
|
||||
|
||||
s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
|
||||
qemu_console_resize(s->con, s->xres, s->yres);
|
||||
}
|
||||
|
||||
static Property bcm2835_fb_props[] = {
|
||||
DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
|
||||
DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
|
||||
DEFAULT_VCRAM_SIZE),
|
||||
DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
|
||||
DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
|
||||
DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
|
||||
DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
|
||||
DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void bcm2835_fb_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->props = bcm2835_fb_props;
|
||||
dc->realize = bcm2835_fb_realize;
|
||||
dc->reset = bcm2835_fb_reset;
|
||||
dc->vmsd = &vmstate_bcm2835_fb;
|
||||
}
|
||||
|
||||
static TypeInfo bcm2835_fb_info = {
|
||||
.name = TYPE_BCM2835_FB,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(BCM2835FBState),
|
||||
.class_init = bcm2835_fb_class_init,
|
||||
.instance_init = bcm2835_fb_init,
|
||||
};
|
||||
|
||||
static void bcm2835_fb_register_types(void)
|
||||
{
|
||||
type_register_static(&bcm2835_fb_info);
|
||||
}
|
||||
|
||||
type_init(bcm2835_fb_register_types)
|
||||
@@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
|
||||
|
||||
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
|
||||
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
|
||||
obj-$(CONFIG_RASPI) += bcm2835_dma.o
|
||||
|
||||
408
hw/dma/bcm2835_dma.c
Normal file
408
hw/dma/bcm2835_dma.c
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Raspberry Pi emulation (c) 2012 Gregory Estrade
|
||||
* This code is licensed under the GNU GPLv2 and later.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/dma/bcm2835_dma.h"
|
||||
|
||||
/* DMA CS Control and Status bits */
|
||||
#define BCM2708_DMA_ACTIVE (1 << 0)
|
||||
#define BCM2708_DMA_END (1 << 1) /* GE */
|
||||
#define BCM2708_DMA_INT (1 << 2)
|
||||
#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */
|
||||
#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */
|
||||
#define BCM2708_DMA_ERR (1 << 8)
|
||||
#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */
|
||||
#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */
|
||||
|
||||
/* DMA control block "info" field bits */
|
||||
#define BCM2708_DMA_INT_EN (1 << 0)
|
||||
#define BCM2708_DMA_TDMODE (1 << 1)
|
||||
#define BCM2708_DMA_WAIT_RESP (1 << 3)
|
||||
#define BCM2708_DMA_D_INC (1 << 4)
|
||||
#define BCM2708_DMA_D_WIDTH (1 << 5)
|
||||
#define BCM2708_DMA_D_DREQ (1 << 6)
|
||||
#define BCM2708_DMA_D_IGNORE (1 << 7)
|
||||
#define BCM2708_DMA_S_INC (1 << 8)
|
||||
#define BCM2708_DMA_S_WIDTH (1 << 9)
|
||||
#define BCM2708_DMA_S_DREQ (1 << 10)
|
||||
#define BCM2708_DMA_S_IGNORE (1 << 11)
|
||||
|
||||
/* Register offsets */
|
||||
#define BCM2708_DMA_CS 0x00 /* Control and Status */
|
||||
#define BCM2708_DMA_ADDR 0x04 /* Control block address */
|
||||
/* the current control block appears in the following registers - read only */
|
||||
#define BCM2708_DMA_INFO 0x08
|
||||
#define BCM2708_DMA_SOURCE_AD 0x0c
|
||||
#define BCM2708_DMA_DEST_AD 0x10
|
||||
#define BCM2708_DMA_TXFR_LEN 0x14
|
||||
#define BCM2708_DMA_STRIDE 0x18
|
||||
#define BCM2708_DMA_NEXTCB 0x1C
|
||||
#define BCM2708_DMA_DEBUG 0x20
|
||||
|
||||
#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */
|
||||
#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */
|
||||
|
||||
#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */
|
||||
|
||||
static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c)
|
||||
{
|
||||
BCM2835DMAChan *ch = &s->chan[c];
|
||||
uint32_t data, xlen, ylen;
|
||||
int16_t dst_stride, src_stride;
|
||||
|
||||
if (!(s->enable & (1 << c))) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) {
|
||||
/* CB fetch */
|
||||
ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad);
|
||||
ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4);
|
||||
ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8);
|
||||
ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12);
|
||||
ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16);
|
||||
ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20);
|
||||
|
||||
if (ch->ti & BCM2708_DMA_TDMODE) {
|
||||
/* 2D transfer mode */
|
||||
ylen = (ch->txfr_len >> 16) & 0x3fff;
|
||||
xlen = ch->txfr_len & 0xffff;
|
||||
dst_stride = ch->stride >> 16;
|
||||
src_stride = ch->stride & 0xffff;
|
||||
} else {
|
||||
ylen = 1;
|
||||
xlen = ch->txfr_len;
|
||||
dst_stride = 0;
|
||||
src_stride = 0;
|
||||
}
|
||||
|
||||
while (ylen != 0) {
|
||||
/* Normal transfer mode */
|
||||
while (xlen != 0) {
|
||||
if (ch->ti & BCM2708_DMA_S_IGNORE) {
|
||||
/* Ignore reads */
|
||||
data = 0;
|
||||
} else {
|
||||
data = ldl_le_phys(&s->dma_as, ch->source_ad);
|
||||
}
|
||||
if (ch->ti & BCM2708_DMA_S_INC) {
|
||||
ch->source_ad += 4;
|
||||
}
|
||||
|
||||
if (ch->ti & BCM2708_DMA_D_IGNORE) {
|
||||
/* Ignore writes */
|
||||
} else {
|
||||
stl_le_phys(&s->dma_as, ch->dest_ad, data);
|
||||
}
|
||||
if (ch->ti & BCM2708_DMA_D_INC) {
|
||||
ch->dest_ad += 4;
|
||||
}
|
||||
|
||||
/* update remaining transfer length */
|
||||
xlen -= 4;
|
||||
if (ch->ti & BCM2708_DMA_TDMODE) {
|
||||
ch->txfr_len = (ylen << 16) | xlen;
|
||||
} else {
|
||||
ch->txfr_len = xlen;
|
||||
}
|
||||
}
|
||||
|
||||
if (--ylen != 0) {
|
||||
ch->source_ad += src_stride;
|
||||
ch->dest_ad += dst_stride;
|
||||
}
|
||||
}
|
||||
ch->cs |= BCM2708_DMA_END;
|
||||
if (ch->ti & BCM2708_DMA_INT_EN) {
|
||||
ch->cs |= BCM2708_DMA_INT;
|
||||
s->int_status |= (1 << c);
|
||||
qemu_set_irq(ch->irq, 1);
|
||||
}
|
||||
|
||||
/* Process next CB */
|
||||
ch->conblk_ad = ch->nextconbk;
|
||||
}
|
||||
|
||||
ch->cs &= ~BCM2708_DMA_ACTIVE;
|
||||
ch->cs |= BCM2708_DMA_ISPAUSED;
|
||||
}
|
||||
|
||||
static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
|
||||
{
|
||||
ch->cs = 0;
|
||||
ch->conblk_ad = 0;
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset,
|
||||
unsigned size, unsigned c)
|
||||
{
|
||||
BCM2835DMAChan *ch;
|
||||
uint32_t res = 0;
|
||||
|
||||
assert(size == 4);
|
||||
assert(c < BCM2835_DMA_NCHANS);
|
||||
|
||||
ch = &s->chan[c];
|
||||
|
||||
switch (offset) {
|
||||
case BCM2708_DMA_CS:
|
||||
res = ch->cs;
|
||||
break;
|
||||
case BCM2708_DMA_ADDR:
|
||||
res = ch->conblk_ad;
|
||||
break;
|
||||
case BCM2708_DMA_INFO:
|
||||
res = ch->ti;
|
||||
break;
|
||||
case BCM2708_DMA_SOURCE_AD:
|
||||
res = ch->source_ad;
|
||||
break;
|
||||
case BCM2708_DMA_DEST_AD:
|
||||
res = ch->dest_ad;
|
||||
break;
|
||||
case BCM2708_DMA_TXFR_LEN:
|
||||
res = ch->txfr_len;
|
||||
break;
|
||||
case BCM2708_DMA_STRIDE:
|
||||
res = ch->stride;
|
||||
break;
|
||||
case BCM2708_DMA_NEXTCB:
|
||||
res = ch->nextconbk;
|
||||
break;
|
||||
case BCM2708_DMA_DEBUG:
|
||||
res = ch->debug;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset,
|
||||
uint64_t value, unsigned size, unsigned c)
|
||||
{
|
||||
BCM2835DMAChan *ch;
|
||||
uint32_t oldcs;
|
||||
|
||||
assert(size == 4);
|
||||
assert(c < BCM2835_DMA_NCHANS);
|
||||
|
||||
ch = &s->chan[c];
|
||||
|
||||
switch (offset) {
|
||||
case BCM2708_DMA_CS:
|
||||
oldcs = ch->cs;
|
||||
if (value & BCM2708_DMA_RESET) {
|
||||
bcm2835_dma_chan_reset(ch);
|
||||
}
|
||||
if (value & BCM2708_DMA_ABORT) {
|
||||
/* abort is a no-op, since we always run to completion */
|
||||
}
|
||||
if (value & BCM2708_DMA_END) {
|
||||
ch->cs &= ~BCM2708_DMA_END;
|
||||
}
|
||||
if (value & BCM2708_DMA_INT) {
|
||||
ch->cs &= ~BCM2708_DMA_INT;
|
||||
s->int_status &= ~(1 << c);
|
||||
qemu_set_irq(ch->irq, 0);
|
||||
}
|
||||
ch->cs &= ~BCM2708_DMA_CS_RW_MASK;
|
||||
ch->cs |= (value & BCM2708_DMA_CS_RW_MASK);
|
||||
if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) {
|
||||
bcm2835_dma_update(s, c);
|
||||
}
|
||||
break;
|
||||
case BCM2708_DMA_ADDR:
|
||||
ch->conblk_ad = value;
|
||||
break;
|
||||
case BCM2708_DMA_DEBUG:
|
||||
ch->debug = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
BCM2835DMAState *s = opaque;
|
||||
|
||||
if (offset < 0xf00) {
|
||||
return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf);
|
||||
} else {
|
||||
switch (offset) {
|
||||
case BCM2708_DMA_INT_STATUS:
|
||||
return s->int_status;
|
||||
case BCM2708_DMA_ENABLE:
|
||||
return s->enable;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
return bcm2835_dma_read(opaque, (offset & 0xff), size, 15);
|
||||
}
|
||||
|
||||
static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
BCM2835DMAState *s = opaque;
|
||||
|
||||
if (offset < 0xf00) {
|
||||
bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf);
|
||||
} else {
|
||||
switch (offset) {
|
||||
case BCM2708_DMA_INT_STATUS:
|
||||
break;
|
||||
case BCM2708_DMA_ENABLE:
|
||||
s->enable = (value & 0xffff);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bcm2835_dma0_ops = {
|
||||
.read = bcm2835_dma0_read,
|
||||
.write = bcm2835_dma0_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps bcm2835_dma15_ops = {
|
||||
.read = bcm2835_dma15_read,
|
||||
.write = bcm2835_dma15_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_bcm2835_dma_chan = {
|
||||
.name = TYPE_BCM2835_DMA "-chan",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(cs, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(conblk_ad, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(ti, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(source_ad, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(dest_ad, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(txfr_len, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(stride, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(nextconbk, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(debug, BCM2835DMAChan),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_bcm2835_dma = {
|
||||
.name = TYPE_BCM2835_DMA,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1,
|
||||
vmstate_bcm2835_dma_chan, BCM2835DMAChan),
|
||||
VMSTATE_UINT32(int_status, BCM2835DMAState),
|
||||
VMSTATE_UINT32(enable, BCM2835DMAState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void bcm2835_dma_init(Object *obj)
|
||||
{
|
||||
BCM2835DMAState *s = BCM2835_DMA(obj);
|
||||
int n;
|
||||
|
||||
/* DMA channels 0-14 occupy a contiguous block of IO memory, along
|
||||
* with the global enable and interrupt status bits. Channel 15
|
||||
* has the same register map, but is mapped at a discontiguous
|
||||
* address in a separate IO block.
|
||||
*/
|
||||
memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s,
|
||||
TYPE_BCM2835_DMA, 0x1000);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0);
|
||||
|
||||
memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s,
|
||||
TYPE_BCM2835_DMA "-chan15", 0x100);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15);
|
||||
|
||||
for (n = 0; n < 16; n++) {
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcm2835_dma_reset(DeviceState *dev)
|
||||
{
|
||||
BCM2835DMAState *s = BCM2835_DMA(dev);
|
||||
int n;
|
||||
|
||||
s->enable = 0xffff;
|
||||
s->int_status = 0;
|
||||
for (n = 0; n < BCM2835_DMA_NCHANS; n++) {
|
||||
bcm2835_dma_chan_reset(&s->chan[n]);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcm2835_dma_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
BCM2835DMAState *s = BCM2835_DMA(dev);
|
||||
Error *err = NULL;
|
||||
Object *obj;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
|
||||
if (obj == NULL) {
|
||||
error_setg(errp, "%s: required dma-mr link not found: %s",
|
||||
__func__, error_get_pretty(err));
|
||||
return;
|
||||
}
|
||||
|
||||
s->dma_mr = MEMORY_REGION(obj);
|
||||
address_space_init(&s->dma_as, s->dma_mr, NULL);
|
||||
|
||||
bcm2835_dma_reset(dev);
|
||||
}
|
||||
|
||||
static void bcm2835_dma_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = bcm2835_dma_realize;
|
||||
dc->reset = bcm2835_dma_reset;
|
||||
dc->vmsd = &vmstate_bcm2835_dma;
|
||||
}
|
||||
|
||||
static TypeInfo bcm2835_dma_info = {
|
||||
.name = TYPE_BCM2835_DMA,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(BCM2835DMAState),
|
||||
.class_init = bcm2835_dma_class_init,
|
||||
.instance_init = bcm2835_dma_init,
|
||||
};
|
||||
|
||||
static void bcm2835_dma_register_types(void)
|
||||
{
|
||||
type_register_static(&bcm2835_dma_info);
|
||||
}
|
||||
|
||||
type_init(bcm2835_dma_register_types)
|
||||
@@ -342,6 +342,10 @@ static void i8257_channel_run(I8257State *d, int ichan)
|
||||
r->now[COUNT], (r->base[COUNT] + 1) << ncont);
|
||||
r->now[COUNT] = n;
|
||||
ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
|
||||
if (n == (r->base[COUNT] + 1) << ncont) {
|
||||
ldebug("transfer done\n");
|
||||
d->status |= (1 << ichan);
|
||||
}
|
||||
}
|
||||
|
||||
static void i8257_dma_run(void *opaque)
|
||||
|
||||
@@ -319,6 +319,7 @@ static void imx_i2c_class_init(ObjectClass *klass, void *data)
|
||||
dc->vmsd = &imx_i2c_vmstate;
|
||||
dc->reset = imx_i2c_reset;
|
||||
dc->realize = imx_i2c_realize;
|
||||
dc->desc = "i.MX I2C Controller";
|
||||
}
|
||||
|
||||
static const TypeInfo imx_i2c_type_info = {
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
#include "hw/acpi/bios-linker-loader.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/block/fdc.h"
|
||||
#include "hw/acpi/memory_hotplug.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
#include "sysemu/tpm.h"
|
||||
#include "hw/acpi/tpm.h"
|
||||
#include "sysemu/tpm_backend.h"
|
||||
@@ -76,10 +76,6 @@
|
||||
#define ACPI_BUILD_DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
typedef struct AcpiCpuInfo {
|
||||
DECLARE_BITMAP(found_cpus, ACPI_CPU_HOTPLUG_ID_LIMIT);
|
||||
} AcpiCpuInfo;
|
||||
|
||||
typedef struct AcpiMcfgInfo {
|
||||
uint64_t mcfg_base;
|
||||
uint32_t mcfg_size;
|
||||
@@ -121,31 +117,6 @@ typedef struct AcpiBuildPciBusHotplugState {
|
||||
bool pcihp_bridge_en;
|
||||
} AcpiBuildPciBusHotplugState;
|
||||
|
||||
static
|
||||
int acpi_add_cpu_info(Object *o, void *opaque)
|
||||
{
|
||||
AcpiCpuInfo *cpu = opaque;
|
||||
uint64_t apic_id;
|
||||
|
||||
if (object_dynamic_cast(o, TYPE_CPU)) {
|
||||
apic_id = object_property_get_int(o, "apic-id", NULL);
|
||||
assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
|
||||
|
||||
set_bit(apic_id, cpu->found_cpus);
|
||||
}
|
||||
|
||||
object_child_foreach(o, acpi_add_cpu_info, opaque);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_get_cpu_info(AcpiCpuInfo *cpu)
|
||||
{
|
||||
Object *root = object_get_root();
|
||||
|
||||
memset(cpu->found_cpus, 0, sizeof cpu->found_cpus);
|
||||
object_child_foreach(root, acpi_add_cpu_info, cpu);
|
||||
}
|
||||
|
||||
static void acpi_get_pm_info(AcpiPmInfo *pm)
|
||||
{
|
||||
Object *piix = piix4_pm_find();
|
||||
@@ -362,9 +333,10 @@ build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm,
|
||||
}
|
||||
|
||||
static void
|
||||
build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu)
|
||||
build_madt(GArray *table_data, GArray *linker, PCMachineState *pcms)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
||||
MachineClass *mc = MACHINE_GET_CLASS(pcms);
|
||||
CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(pcms));
|
||||
int madt_start = table_data->len;
|
||||
|
||||
AcpiMultipleApicTable *madt;
|
||||
@@ -377,18 +349,28 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu)
|
||||
madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS);
|
||||
madt->flags = cpu_to_le32(1);
|
||||
|
||||
for (i = 0; i < pcms->apic_id_limit; i++) {
|
||||
for (i = 0; i < apic_ids->len; i++) {
|
||||
AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic);
|
||||
int apic_id = apic_ids->cpus[i].arch_id;
|
||||
|
||||
apic->type = ACPI_APIC_PROCESSOR;
|
||||
apic->length = sizeof(*apic);
|
||||
apic->processor_id = i;
|
||||
apic->local_apic_id = i;
|
||||
if (test_bit(i, cpu->found_cpus)) {
|
||||
apic->processor_id = apic_id;
|
||||
apic->local_apic_id = apic_id;
|
||||
if (apic_ids->cpus[i].cpu != NULL) {
|
||||
apic->flags = cpu_to_le32(1);
|
||||
} else {
|
||||
/* ACPI spec says that LAPIC entry for non present
|
||||
* CPU may be omitted from MADT or it must be marked
|
||||
* as disabled. However omitting non present CPU from
|
||||
* MADT breaks hotplug on linux. So possible CPUs
|
||||
* should be put in MADT but kept disabled.
|
||||
*/
|
||||
apic->flags = cpu_to_le32(0);
|
||||
}
|
||||
}
|
||||
g_free(apic_ids);
|
||||
|
||||
io_apic = acpi_data_push(table_data, sizeof *io_apic);
|
||||
io_apic->type = ACPI_APIC_IO;
|
||||
io_apic->length = sizeof(*io_apic);
|
||||
@@ -960,21 +942,24 @@ static Aml *build_crs(PCIHostState *host,
|
||||
return crs;
|
||||
}
|
||||
|
||||
static void build_processor_devices(Aml *sb_scope, unsigned acpi_cpus,
|
||||
AcpiCpuInfo *cpu, AcpiPmInfo *pm)
|
||||
static void build_processor_devices(Aml *sb_scope, MachineState *machine,
|
||||
AcpiPmInfo *pm)
|
||||
{
|
||||
int i;
|
||||
int i, apic_idx;
|
||||
Aml *dev;
|
||||
Aml *crs;
|
||||
Aml *pkg;
|
||||
Aml *field;
|
||||
Aml *ifctx;
|
||||
Aml *method;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
|
||||
/* The current AML generator can cover the APIC ID range [0..255],
|
||||
* inclusive, for VCPU hotplug. */
|
||||
QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256);
|
||||
g_assert(acpi_cpus <= ACPI_CPU_HOTPLUG_ID_LIMIT);
|
||||
g_assert(pcms->apic_id_limit <= ACPI_CPU_HOTPLUG_ID_LIMIT);
|
||||
|
||||
/* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
|
||||
dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
|
||||
@@ -993,28 +978,33 @@ static void build_processor_devices(Aml *sb_scope, unsigned acpi_cpus,
|
||||
aml_append(sb_scope, dev);
|
||||
/* declare CPU hotplug MMIO region and PRS field to access it */
|
||||
aml_append(sb_scope, aml_operation_region(
|
||||
"PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
|
||||
"PRST", AML_SYSTEM_IO, aml_int(pm->cpu_hp_io_base), pm->cpu_hp_io_len));
|
||||
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PRS", 256));
|
||||
aml_append(sb_scope, field);
|
||||
|
||||
/* build Processor object for each processor */
|
||||
for (i = 0; i < acpi_cpus; i++) {
|
||||
dev = aml_processor(i, 0, 0, "CP%.02X", i);
|
||||
for (i = 0; i < apic_ids->len; i++) {
|
||||
int apic_id = apic_ids->cpus[i].arch_id;
|
||||
|
||||
assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
|
||||
|
||||
dev = aml_processor(apic_id, 0, 0, "CP%.02X", apic_id);
|
||||
|
||||
method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_return(aml_call1(CPU_MAT_METHOD, aml_int(i))));
|
||||
aml_return(aml_call1(CPU_MAT_METHOD, aml_int(apic_id))));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(i))));
|
||||
aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id))));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(i), aml_arg(0)))
|
||||
aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id),
|
||||
aml_arg(0)))
|
||||
);
|
||||
aml_append(dev, method);
|
||||
|
||||
@@ -1026,10 +1016,12 @@ static void build_processor_devices(Aml *sb_scope, unsigned acpi_cpus,
|
||||
*/
|
||||
/* Arg0 = Processor ID = APIC ID */
|
||||
method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED);
|
||||
for (i = 0; i < acpi_cpus; i++) {
|
||||
ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
|
||||
for (i = 0; i < apic_ids->len; i++) {
|
||||
int apic_id = apic_ids->cpus[i].arch_id;
|
||||
|
||||
ifctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id)));
|
||||
aml_append(ifctx,
|
||||
aml_notify(aml_name("CP%.02X", i), aml_arg(1))
|
||||
aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1))
|
||||
);
|
||||
aml_append(method, ifctx);
|
||||
}
|
||||
@@ -1042,14 +1034,20 @@ static void build_processor_devices(Aml *sb_scope, unsigned acpi_cpus,
|
||||
* ith up to 255 elements. Windows guests up to win2k8 fail when
|
||||
* VarPackageOp is used.
|
||||
*/
|
||||
pkg = acpi_cpus <= 255 ? aml_package(acpi_cpus) :
|
||||
aml_varpackage(acpi_cpus);
|
||||
pkg = pcms->apic_id_limit <= 255 ? aml_package(pcms->apic_id_limit) :
|
||||
aml_varpackage(pcms->apic_id_limit);
|
||||
|
||||
for (i = 0; i < acpi_cpus; i++) {
|
||||
uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00;
|
||||
aml_append(pkg, aml_int(b));
|
||||
for (i = 0, apic_idx = 0; i < apic_ids->len; i++) {
|
||||
int apic_id = apic_ids->cpus[i].arch_id;
|
||||
|
||||
for (; apic_idx < apic_id; apic_idx++) {
|
||||
aml_append(pkg, aml_int(0));
|
||||
}
|
||||
aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0));
|
||||
apic_idx = apic_id + 1;
|
||||
}
|
||||
aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg));
|
||||
g_free(apic_ids);
|
||||
}
|
||||
|
||||
static void build_memory_devices(Aml *sb_scope, int nr_mem,
|
||||
@@ -1078,7 +1076,7 @@ static void build_memory_devices(Aml *sb_scope, int nr_mem,
|
||||
|
||||
aml_append(scope, aml_operation_region(
|
||||
MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO,
|
||||
io_base, io_len)
|
||||
aml_int(io_base), io_len)
|
||||
);
|
||||
|
||||
field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC,
|
||||
@@ -1192,7 +1190,8 @@ static void build_hpet_aml(Aml *table)
|
||||
aml_append(dev, aml_name_decl("_UID", zero));
|
||||
|
||||
aml_append(dev,
|
||||
aml_operation_region("HPTM", AML_SYSTEM_MEMORY, HPET_BASE, HPET_LEN));
|
||||
aml_operation_region("HPTM", AML_SYSTEM_MEMORY, aml_int(HPET_BASE),
|
||||
HPET_LEN));
|
||||
field = aml_field("HPTM", AML_DWORD_ACC, AML_LOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("VEND", 32));
|
||||
aml_append(field, aml_named_field("PRD", 32));
|
||||
@@ -1227,33 +1226,63 @@ static void build_hpet_aml(Aml *table)
|
||||
aml_append(table, scope);
|
||||
}
|
||||
|
||||
static Aml *build_fdc_device_aml(void)
|
||||
static Aml *build_fdinfo_aml(int idx, FloppyDriveType type)
|
||||
{
|
||||
Aml *dev, *fdi;
|
||||
uint8_t maxc, maxh, maxs;
|
||||
|
||||
isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs);
|
||||
|
||||
dev = aml_device("FLP%c", 'A' + idx);
|
||||
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(idx)));
|
||||
|
||||
fdi = aml_package(16);
|
||||
aml_append(fdi, aml_int(idx)); /* Drive Number */
|
||||
aml_append(fdi,
|
||||
aml_int(cmos_get_fd_drive_type(type))); /* Device Type */
|
||||
/*
|
||||
* the values below are the limits of the drive, and are thus independent
|
||||
* of the inserted media
|
||||
*/
|
||||
aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */
|
||||
aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */
|
||||
aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */
|
||||
/*
|
||||
* SeaBIOS returns the below values for int 0x13 func 0x08 regardless of
|
||||
* the drive type, so shall we
|
||||
*/
|
||||
aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */
|
||||
aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */
|
||||
aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */
|
||||
aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */
|
||||
aml_append(fdi, aml_int(0x12)); /* disk_eot */
|
||||
aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */
|
||||
aml_append(fdi, aml_int(0xFF)); /* disk_dtl */
|
||||
aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */
|
||||
aml_append(fdi, aml_int(0xF6)); /* disk_fill */
|
||||
aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */
|
||||
aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */
|
||||
|
||||
aml_append(dev, aml_name_decl("_FDI", fdi));
|
||||
return dev;
|
||||
}
|
||||
|
||||
static Aml *build_fdc_device_aml(ISADevice *fdc)
|
||||
{
|
||||
int i;
|
||||
Aml *dev;
|
||||
Aml *crs;
|
||||
Aml *method;
|
||||
Aml *if_ctx;
|
||||
Aml *else_ctx;
|
||||
Aml *zero = aml_int(0);
|
||||
Aml *is_present = aml_local(0);
|
||||
|
||||
#define ACPI_FDE_MAX_FD 4
|
||||
uint32_t fde_buf[5] = {
|
||||
0, 0, 0, 0, /* presence of floppy drives #0 - #3 */
|
||||
cpu_to_le32(2) /* tape presence (2 == never present) */
|
||||
};
|
||||
|
||||
dev = aml_device("FDC0");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700")));
|
||||
|
||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_store(aml_name("FDEN"), is_present));
|
||||
if_ctx = aml_if(aml_equal(is_present, zero));
|
||||
{
|
||||
aml_append(if_ctx, aml_return(aml_int(0x00)));
|
||||
}
|
||||
aml_append(method, if_ctx);
|
||||
else_ctx = aml_else();
|
||||
{
|
||||
aml_append(else_ctx, aml_return(aml_int(0x0f)));
|
||||
}
|
||||
aml_append(method, else_ctx);
|
||||
aml_append(dev, method);
|
||||
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04));
|
||||
aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01));
|
||||
@@ -1262,6 +1291,17 @@ static Aml *build_fdc_device_aml(void)
|
||||
aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) {
|
||||
FloppyDriveType type = isa_fdc_get_drive_type(fdc, i);
|
||||
|
||||
if (type < FLOPPY_DRIVE_TYPE_NONE) {
|
||||
fde_buf[i] = cpu_to_le32(1); /* drive present */
|
||||
aml_append(dev, build_fdinfo_aml(i, type));
|
||||
}
|
||||
}
|
||||
aml_append(dev, aml_name_decl("_FDE",
|
||||
aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf)));
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
@@ -1406,12 +1446,16 @@ static Aml *build_com_device_aml(uint8_t uid)
|
||||
|
||||
static void build_isa_devices_aml(Aml *table)
|
||||
{
|
||||
ISADevice *fdc = pc_find_fdc0();
|
||||
|
||||
Aml *scope = aml_scope("_SB.PCI0.ISA");
|
||||
|
||||
aml_append(scope, build_rtc_device_aml());
|
||||
aml_append(scope, build_kbd_device_aml());
|
||||
aml_append(scope, build_mouse_device_aml());
|
||||
aml_append(scope, build_fdc_device_aml());
|
||||
if (fdc) {
|
||||
aml_append(scope, build_fdc_device_aml(fdc));
|
||||
}
|
||||
aml_append(scope, build_lpt_device_aml());
|
||||
aml_append(scope, build_com_device_aml(1));
|
||||
aml_append(scope, build_com_device_aml(2));
|
||||
@@ -1430,7 +1474,7 @@ static void build_dbg_aml(Aml *table)
|
||||
Aml *idx = aml_local(2);
|
||||
|
||||
aml_append(scope,
|
||||
aml_operation_region("DBG", AML_SYSTEM_IO, 0x0402, 0x01));
|
||||
aml_operation_region("DBG", AML_SYSTEM_IO, aml_int(0x0402), 0x01));
|
||||
field = aml_field("DBG", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("DBGB", 8));
|
||||
aml_append(scope, field);
|
||||
@@ -1509,6 +1553,12 @@ static Aml *build_gsi_link_dev(const char *name, uint8_t uid, uint8_t gsi)
|
||||
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
/*
|
||||
* _DIS can be no-op because the interrupt cannot be disabled.
|
||||
*/
|
||||
method = aml_method("_DIS", 0, AML_NOTSERIALIZED);
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
||||
aml_append(dev, method);
|
||||
|
||||
@@ -1742,18 +1792,14 @@ static void build_q35_pci0_int(Aml *table)
|
||||
aml_append(sb_scope, build_link_dev("LNKG", 6, aml_name("PRQG")));
|
||||
aml_append(sb_scope, build_link_dev("LNKH", 7, aml_name("PRQH")));
|
||||
|
||||
/*
|
||||
* TODO: UID probably shouldn't be the same for GSIx devices
|
||||
* but that's how it was in original ASL so keep it for now
|
||||
*/
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIA", 0, 0x10));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIB", 0, 0x11));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIC", 0, 0x12));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSID", 0, 0x13));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIE", 0, 0x14));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIF", 0, 0x15));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIG", 0, 0x16));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIH", 0, 0x17));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIA", 0x10, 0x10));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIB", 0x11, 0x11));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIC", 0x12, 0x12));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSID", 0x13, 0x13));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIE", 0x14, 0x14));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIF", 0x15, 0x15));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIG", 0x16, 0x16));
|
||||
aml_append(sb_scope, build_gsi_link_dev("GSIH", 0x17, 0x17));
|
||||
|
||||
aml_append(table, sb_scope);
|
||||
}
|
||||
@@ -1770,28 +1816,25 @@ static void build_q35_isa_bridge(Aml *table)
|
||||
|
||||
/* ICH9 PCI to ISA irq remapping */
|
||||
aml_append(dev, aml_operation_region("PIRQ", AML_PCI_CONFIG,
|
||||
0x60, 0x0C));
|
||||
aml_int(0x60), 0x0C));
|
||||
|
||||
aml_append(dev, aml_operation_region("LPCD", AML_PCI_CONFIG,
|
||||
0x80, 0x02));
|
||||
aml_int(0x80), 0x02));
|
||||
field = aml_field("LPCD", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("COMA", 3));
|
||||
aml_append(field, aml_reserved_field(1));
|
||||
aml_append(field, aml_named_field("COMB", 3));
|
||||
aml_append(field, aml_reserved_field(1));
|
||||
aml_append(field, aml_named_field("LPTD", 2));
|
||||
aml_append(field, aml_reserved_field(2));
|
||||
aml_append(field, aml_named_field("FDCD", 2));
|
||||
aml_append(dev, field);
|
||||
|
||||
aml_append(dev, aml_operation_region("LPCE", AML_PCI_CONFIG,
|
||||
0x82, 0x02));
|
||||
aml_int(0x82), 0x02));
|
||||
/* enable bits */
|
||||
field = aml_field("LPCE", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("CAEN", 1));
|
||||
aml_append(field, aml_named_field("CBEN", 1));
|
||||
aml_append(field, aml_named_field("LPEN", 1));
|
||||
aml_append(field, aml_named_field("FDEN", 1));
|
||||
aml_append(dev, field);
|
||||
|
||||
aml_append(scope, dev);
|
||||
@@ -1808,7 +1851,7 @@ static void build_piix4_pm(Aml *table)
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010003)));
|
||||
|
||||
aml_append(dev, aml_operation_region("P13C", AML_PCI_CONFIG,
|
||||
0x00, 0xff));
|
||||
aml_int(0x00), 0xff));
|
||||
aml_append(scope, dev);
|
||||
aml_append(table, scope);
|
||||
}
|
||||
@@ -1825,7 +1868,7 @@ static void build_piix4_isa_bridge(Aml *table)
|
||||
|
||||
/* PIIX PCI to ISA irq remapping */
|
||||
aml_append(dev, aml_operation_region("P40C", AML_PCI_CONFIG,
|
||||
0x60, 0x04));
|
||||
aml_int(0x60), 0x04));
|
||||
/* enable bits */
|
||||
field = aml_field("^PX13.P13C", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
/* Offset(0x5f),, 7, */
|
||||
@@ -1839,7 +1882,6 @@ static void build_piix4_isa_bridge(Aml *table)
|
||||
aml_append(field, aml_reserved_field(3));
|
||||
aml_append(field, aml_named_field("CBEN", 1));
|
||||
aml_append(dev, field);
|
||||
aml_append(dev, aml_name_decl("FDEN", aml_int(1)));
|
||||
|
||||
aml_append(scope, dev);
|
||||
aml_append(table, scope);
|
||||
@@ -1854,20 +1896,20 @@ static void build_piix4_pci_hotplug(Aml *table)
|
||||
scope = aml_scope("_SB.PCI0");
|
||||
|
||||
aml_append(scope,
|
||||
aml_operation_region("PCST", AML_SYSTEM_IO, 0xae00, 0x08));
|
||||
aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x08));
|
||||
field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||
aml_append(field, aml_named_field("PCIU", 32));
|
||||
aml_append(field, aml_named_field("PCID", 32));
|
||||
aml_append(scope, field);
|
||||
|
||||
aml_append(scope,
|
||||
aml_operation_region("SEJ", AML_SYSTEM_IO, 0xae08, 0x04));
|
||||
aml_operation_region("SEJ", AML_SYSTEM_IO, aml_int(0xae08), 0x04));
|
||||
field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||
aml_append(field, aml_named_field("B0EJ", 32));
|
||||
aml_append(scope, field);
|
||||
|
||||
aml_append(scope,
|
||||
aml_operation_region("BNMR", AML_SYSTEM_IO, 0xae10, 0x04));
|
||||
aml_operation_region("BNMR", AML_SYSTEM_IO, aml_int(0xae10), 0x04));
|
||||
field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||
aml_append(field, aml_named_field("BNUM", 32));
|
||||
aml_append(scope, field);
|
||||
@@ -1937,14 +1979,13 @@ static Aml *build_q35_osc_method(void)
|
||||
|
||||
static void
|
||||
build_dsdt(GArray *table_data, GArray *linker,
|
||||
AcpiCpuInfo *cpu, AcpiPmInfo *pm, AcpiMiscInfo *misc,
|
||||
PcPciInfo *pci)
|
||||
AcpiPmInfo *pm, AcpiMiscInfo *misc,
|
||||
PcPciInfo *pci, MachineState *machine)
|
||||
{
|
||||
CrsRangeEntry *entry;
|
||||
Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs;
|
||||
GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
||||
GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
uint32_t nr_mem = machine->ram_slots;
|
||||
int root_bus_limit = 0xFF;
|
||||
@@ -1975,9 +2016,9 @@ build_dsdt(GArray *table_data, GArray *linker,
|
||||
} else {
|
||||
sb_scope = aml_scope("_SB");
|
||||
aml_append(sb_scope,
|
||||
aml_operation_region("PCST", AML_SYSTEM_IO, 0xae00, 0x0c));
|
||||
aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x0c));
|
||||
aml_append(sb_scope,
|
||||
aml_operation_region("PCSB", AML_SYSTEM_IO, 0xae0c, 0x01));
|
||||
aml_operation_region("PCSB", AML_SYSTEM_IO, aml_int(0xae0c), 0x01));
|
||||
field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||
aml_append(field, aml_named_field("PCIB", 8));
|
||||
aml_append(sb_scope, field);
|
||||
@@ -2190,6 +2231,35 @@ build_dsdt(GArray *table_data, GArray *linker,
|
||||
aml_append(scope, aml_name_decl("_S5", pkg));
|
||||
aml_append(dsdt, scope);
|
||||
|
||||
/* create fw_cfg node, unconditionally */
|
||||
{
|
||||
/* when using port i/o, the 8-bit data register *always* overlaps
|
||||
* with half of the 16-bit control register. Hence, the total size
|
||||
* of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the
|
||||
* DMA control register is located at FW_CFG_DMA_IO_BASE + 4 */
|
||||
uint8_t io_size = object_property_get_bool(OBJECT(pcms->fw_cfg),
|
||||
"dma_enabled", NULL) ?
|
||||
ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) :
|
||||
FW_CFG_CTL_SIZE;
|
||||
|
||||
scope = aml_scope("\\_SB.PCI0");
|
||||
dev = aml_device("FWCF");
|
||||
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
|
||||
|
||||
/* device present, functioning, decoding, not shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_io(AML_DECODE16, FW_CFG_IO_BASE, FW_CFG_IO_BASE, 0x01, io_size)
|
||||
);
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
aml_append(scope, dev);
|
||||
aml_append(dsdt, scope);
|
||||
}
|
||||
|
||||
if (misc->applesmc_io_base) {
|
||||
scope = aml_scope("\\_SB.PCI0.ISA");
|
||||
dev = aml_device("SMC");
|
||||
@@ -2223,7 +2293,7 @@ build_dsdt(GArray *table_data, GArray *linker,
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
|
||||
misc->pvpanic_port, 1));
|
||||
aml_int(misc->pvpanic_port), 1));
|
||||
field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PEPT", 8));
|
||||
aml_append(dev, field);
|
||||
@@ -2246,7 +2316,7 @@ build_dsdt(GArray *table_data, GArray *linker,
|
||||
|
||||
sb_scope = aml_scope("\\_SB");
|
||||
{
|
||||
build_processor_devices(sb_scope, pcms->apic_id_limit, cpu, pm);
|
||||
build_processor_devices(sb_scope, machine, pm);
|
||||
|
||||
build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base,
|
||||
pm->mem_hp_io_len);
|
||||
@@ -2367,7 +2437,7 @@ acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
|
||||
}
|
||||
|
||||
static void
|
||||
build_srat(GArray *table_data, GArray *linker)
|
||||
build_srat(GArray *table_data, GArray *linker, MachineState *machine)
|
||||
{
|
||||
AcpiSystemResourceAffinityTable *srat;
|
||||
AcpiSratProcessorAffinity *core;
|
||||
@@ -2377,7 +2447,9 @@ build_srat(GArray *table_data, GArray *linker)
|
||||
uint64_t curnode;
|
||||
int srat_start, numa_start, slots;
|
||||
uint64_t mem_len, mem_base, next_base;
|
||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
ram_addr_t hotplugabble_address_space_size =
|
||||
object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE,
|
||||
NULL);
|
||||
@@ -2386,14 +2458,15 @@ build_srat(GArray *table_data, GArray *linker)
|
||||
|
||||
srat = acpi_data_push(table_data, sizeof *srat);
|
||||
srat->reserved1 = cpu_to_le32(1);
|
||||
core = (void *)(srat + 1);
|
||||
|
||||
for (i = 0; i < pcms->apic_id_limit; ++i) {
|
||||
for (i = 0; i < apic_ids->len; i++) {
|
||||
int apic_id = apic_ids->cpus[i].arch_id;
|
||||
|
||||
core = acpi_data_push(table_data, sizeof *core);
|
||||
core->type = ACPI_SRAT_PROCESSOR;
|
||||
core->length = sizeof(*core);
|
||||
core->local_apic_id = i;
|
||||
curnode = pcms->node_cpu[i];
|
||||
core->local_apic_id = apic_id;
|
||||
curnode = pcms->node_cpu[apic_id];
|
||||
core->proximity_lo = curnode;
|
||||
memset(core->proximity_hi, 0, 3);
|
||||
core->local_sapic_eid = 0;
|
||||
@@ -2458,6 +2531,7 @@ build_srat(GArray *table_data, GArray *linker)
|
||||
(void *)(table_data->data + srat_start),
|
||||
"SRAT",
|
||||
table_data->len - srat_start, 1, NULL, NULL);
|
||||
g_free(apic_ids);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2581,21 +2655,13 @@ static bool acpi_has_iommu(void)
|
||||
return intel_iommu && !ambiguous;
|
||||
}
|
||||
|
||||
static bool acpi_has_nvdimm(void)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
||||
|
||||
return pcms->nvdimm;
|
||||
}
|
||||
|
||||
static
|
||||
void acpi_build(AcpiBuildTables *tables)
|
||||
void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
GArray *table_offsets;
|
||||
unsigned facs, dsdt, rsdt, fadt;
|
||||
AcpiCpuInfo cpu;
|
||||
AcpiPmInfo pm;
|
||||
AcpiMiscInfo misc;
|
||||
AcpiMcfgInfo mcfg;
|
||||
@@ -2605,7 +2671,6 @@ void acpi_build(AcpiBuildTables *tables)
|
||||
GArray *tables_blob = tables->table_data;
|
||||
AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL };
|
||||
|
||||
acpi_get_cpu_info(&cpu);
|
||||
acpi_get_pm_info(&pm);
|
||||
acpi_get_misc_info(&misc);
|
||||
acpi_get_pci_info(&pci);
|
||||
@@ -2629,7 +2694,7 @@ void acpi_build(AcpiBuildTables *tables)
|
||||
|
||||
/* DSDT is pointed to by FADT */
|
||||
dsdt = tables_blob->len;
|
||||
build_dsdt(tables_blob, tables->linker, &cpu, &pm, &misc, &pci);
|
||||
build_dsdt(tables_blob, tables->linker, &pm, &misc, &pci, machine);
|
||||
|
||||
/* Count the size of the DSDT and SSDT, we will need it for legacy
|
||||
* sizing of ACPI tables.
|
||||
@@ -2644,7 +2709,7 @@ void acpi_build(AcpiBuildTables *tables)
|
||||
aml_len += tables_blob->len - fadt;
|
||||
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_madt(tables_blob, tables->linker, &cpu);
|
||||
build_madt(tables_blob, tables->linker, pcms);
|
||||
|
||||
if (misc.has_hpet) {
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
@@ -2661,7 +2726,7 @@ void acpi_build(AcpiBuildTables *tables)
|
||||
}
|
||||
if (pcms->numa_nodes) {
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_srat(tables_blob, tables->linker);
|
||||
build_srat(tables_blob, tables->linker, machine);
|
||||
}
|
||||
if (acpi_get_mcfg(&mcfg)) {
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
@@ -2671,8 +2736,7 @@ void acpi_build(AcpiBuildTables *tables)
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_dmar_q35(tables_blob, tables->linker);
|
||||
}
|
||||
|
||||
if (acpi_has_nvdimm()) {
|
||||
if (pcms->acpi_nvdimm_state.is_enabled) {
|
||||
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker);
|
||||
}
|
||||
|
||||
@@ -2766,7 +2830,7 @@ static void acpi_build_update(void *build_opaque)
|
||||
|
||||
acpi_build_tables_init(&tables);
|
||||
|
||||
acpi_build(&tables);
|
||||
acpi_build(&tables, MACHINE(qdev_get_machine()));
|
||||
|
||||
acpi_ram_update(build_state->table_mr, tables.table_data);
|
||||
|
||||
@@ -2831,7 +2895,7 @@ void acpi_setup(void)
|
||||
acpi_set_pci_info();
|
||||
|
||||
acpi_build_tables_init(&tables);
|
||||
acpi_build(&tables);
|
||||
acpi_build(&tables, MACHINE(pcms));
|
||||
|
||||
/* Now expose it all to Guest */
|
||||
build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data,
|
||||
|
||||
@@ -186,7 +186,7 @@ static void kvm_apic_realize(DeviceState *dev, Error **errp)
|
||||
APIC_SPACE_SIZE);
|
||||
|
||||
if (kvm_has_gsi_routing()) {
|
||||
msi_supported = true;
|
||||
msi_nonbroken = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
102
hw/i386/pc.c
102
hw/i386/pc.c
@@ -78,7 +78,6 @@
|
||||
#define DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define BIOS_CFG_IOPORT 0x510
|
||||
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
|
||||
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
|
||||
#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
|
||||
@@ -200,7 +199,7 @@ static void pic_irq_request(void *opaque, int irq, int level)
|
||||
|
||||
#define REG_EQUIPMENT_BYTE 0x14
|
||||
|
||||
static int cmos_get_fd_drive_type(FloppyDriveType fd0)
|
||||
int cmos_get_fd_drive_type(FloppyDriveType fd0)
|
||||
{
|
||||
int val;
|
||||
|
||||
@@ -700,18 +699,6 @@ static uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index)
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates the limit to CPU APIC ID values
|
||||
*
|
||||
* This function returns the limit for the APIC ID value, so that all
|
||||
* CPU APIC IDs are < pc_apic_id_limit().
|
||||
*
|
||||
* This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init().
|
||||
*/
|
||||
static unsigned int pc_apic_id_limit(unsigned int max_cpus)
|
||||
{
|
||||
return x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
|
||||
}
|
||||
|
||||
static void pc_build_smbios(FWCfgState *fw_cfg)
|
||||
{
|
||||
uint8_t *smbios_tables, *smbios_anchor;
|
||||
@@ -749,14 +736,13 @@ static void pc_build_smbios(FWCfgState *fw_cfg)
|
||||
}
|
||||
}
|
||||
|
||||
static FWCfgState *bochs_bios_init(AddressSpace *as)
|
||||
static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms)
|
||||
{
|
||||
FWCfgState *fw_cfg;
|
||||
uint64_t *numa_fw_cfg;
|
||||
int i, j;
|
||||
unsigned int apic_id_limit = pc_apic_id_limit(max_cpus);
|
||||
|
||||
fw_cfg = fw_cfg_init_io_dma(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 4, as);
|
||||
fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4, as);
|
||||
|
||||
/* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
|
||||
*
|
||||
@@ -772,7 +758,7 @@ static FWCfgState *bochs_bios_init(AddressSpace *as)
|
||||
* [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is
|
||||
* the APIC ID, not the "CPU index"
|
||||
*/
|
||||
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit);
|
||||
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)pcms->apic_id_limit);
|
||||
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES,
|
||||
acpi_tables, acpi_tables_len);
|
||||
@@ -790,11 +776,11 @@ static FWCfgState *bochs_bios_init(AddressSpace *as)
|
||||
* of nodes, one word for each VCPU->node and one word for each node to
|
||||
* hold the amount of memory.
|
||||
*/
|
||||
numa_fw_cfg = g_new0(uint64_t, 1 + apic_id_limit + nb_numa_nodes);
|
||||
numa_fw_cfg = g_new0(uint64_t, 1 + pcms->apic_id_limit + nb_numa_nodes);
|
||||
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
|
||||
for (i = 0; i < max_cpus; i++) {
|
||||
unsigned int apic_id = x86_cpu_apic_id_from_index(i);
|
||||
assert(apic_id < apic_id_limit);
|
||||
assert(apic_id < pcms->apic_id_limit);
|
||||
for (j = 0; j < nb_numa_nodes; j++) {
|
||||
if (test_bit(i, numa_info[j].node_cpu)) {
|
||||
numa_fw_cfg[apic_id + 1] = cpu_to_le64(j);
|
||||
@@ -803,10 +789,11 @@ static FWCfgState *bochs_bios_init(AddressSpace *as)
|
||||
}
|
||||
}
|
||||
for (i = 0; i < nb_numa_nodes; i++) {
|
||||
numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(numa_info[i].node_mem);
|
||||
numa_fw_cfg[pcms->apic_id_limit + 1 + i] =
|
||||
cpu_to_le64(numa_info[i].node_mem);
|
||||
}
|
||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg,
|
||||
(1 + apic_id_limit + nb_numa_nodes) *
|
||||
(1 + pcms->apic_id_limit + nb_numa_nodes) *
|
||||
sizeof(*numa_fw_cfg));
|
||||
|
||||
return fw_cfg;
|
||||
@@ -1120,7 +1107,6 @@ void pc_cpus_init(PCMachineState *pcms)
|
||||
int i;
|
||||
X86CPU *cpu = NULL;
|
||||
MachineState *machine = MACHINE(pcms);
|
||||
unsigned long apic_id_limit;
|
||||
|
||||
/* init CPUs */
|
||||
if (machine->cpu_model == NULL) {
|
||||
@@ -1131,17 +1117,31 @@ void pc_cpus_init(PCMachineState *pcms)
|
||||
#endif
|
||||
}
|
||||
|
||||
apic_id_limit = pc_apic_id_limit(max_cpus);
|
||||
if (apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
|
||||
error_report("max_cpus is too large. APIC ID of last CPU is %lu",
|
||||
apic_id_limit - 1);
|
||||
/* Calculates the limit to CPU APIC ID values
|
||||
*
|
||||
* Limit for the APIC ID value, so that all
|
||||
* CPU APIC IDs are < pcms->apic_id_limit.
|
||||
*
|
||||
* This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init().
|
||||
*/
|
||||
pcms->apic_id_limit = x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
|
||||
if (pcms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
|
||||
error_report("max_cpus is too large. APIC ID of last CPU is %u",
|
||||
pcms->apic_id_limit - 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i),
|
||||
&error_fatal);
|
||||
object_unref(OBJECT(cpu));
|
||||
pcms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
|
||||
sizeof(CPUArchId) * max_cpus);
|
||||
for (i = 0; i < max_cpus; i++) {
|
||||
pcms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(i);
|
||||
pcms->possible_cpus->len++;
|
||||
if (i < smp_cpus) {
|
||||
cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i),
|
||||
&error_fatal);
|
||||
pcms->possible_cpus->cpus[i].cpu = CPU(cpu);
|
||||
object_unref(OBJECT(cpu));
|
||||
}
|
||||
}
|
||||
|
||||
/* tell smbios about cpuid version and features */
|
||||
@@ -1187,7 +1187,6 @@ void pc_guest_info_init(PCMachineState *pcms)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
pcms->apic_id_limit = pc_apic_id_limit(max_cpus);
|
||||
pcms->apic_xrupt_override = kvm_allows_irq0_override();
|
||||
pcms->numa_nodes = nb_numa_nodes;
|
||||
pcms->node_mem = g_malloc0(pcms->numa_nodes *
|
||||
@@ -1258,7 +1257,7 @@ void xen_load_linux(PCMachineState *pcms)
|
||||
|
||||
assert(MACHINE(pcms)->kernel_filename != NULL);
|
||||
|
||||
fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT);
|
||||
fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE);
|
||||
rom_set_fw(fw_cfg);
|
||||
|
||||
load_linux(pcms, fw_cfg);
|
||||
@@ -1372,7 +1371,7 @@ void pc_memory_init(PCMachineState *pcms,
|
||||
option_rom_mr,
|
||||
1);
|
||||
|
||||
fw_cfg = bochs_bios_init(&address_space_memory);
|
||||
fw_cfg = bochs_bios_init(&address_space_memory, pcms);
|
||||
|
||||
rom_set_fw(fw_cfg);
|
||||
|
||||
@@ -1665,9 +1664,19 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static int pc_apic_cmp(const void *a, const void *b)
|
||||
{
|
||||
CPUArchId *apic_a = (CPUArchId *)a;
|
||||
CPUArchId *apic_b = (CPUArchId *)b;
|
||||
|
||||
return apic_a->arch_id - apic_b->arch_id;
|
||||
}
|
||||
|
||||
static void pc_cpu_plug(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(dev);
|
||||
CPUArchId apic_id, *found_cpu;
|
||||
HotplugHandlerClass *hhc;
|
||||
Error *local_err = NULL;
|
||||
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
||||
@@ -1690,6 +1699,13 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev,
|
||||
|
||||
/* increment the number of CPUs */
|
||||
rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1);
|
||||
|
||||
apic_id.arch_id = cc->get_arch_id(CPU(dev));
|
||||
found_cpu = bsearch(&apic_id, pcms->possible_cpus->cpus,
|
||||
pcms->possible_cpus->len, sizeof(*pcms->possible_cpus->cpus),
|
||||
pc_apic_cmp);
|
||||
assert(found_cpu);
|
||||
found_cpu->cpu = CPU(dev);
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
@@ -1854,14 +1870,14 @@ static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(obj);
|
||||
|
||||
return pcms->nvdimm;
|
||||
return pcms->acpi_nvdimm_state.is_enabled;
|
||||
}
|
||||
|
||||
static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(obj);
|
||||
|
||||
pcms->nvdimm = value;
|
||||
pcms->acpi_nvdimm_state.is_enabled = value;
|
||||
}
|
||||
|
||||
static void pc_machine_initfn(Object *obj)
|
||||
@@ -1900,7 +1916,7 @@ static void pc_machine_initfn(Object *obj)
|
||||
&error_abort);
|
||||
|
||||
/* nvdimm is disabled on default. */
|
||||
pcms->nvdimm = false;
|
||||
pcms->acpi_nvdimm_state.is_enabled = false;
|
||||
object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
|
||||
pc_machine_set_nvdimm, &error_abort);
|
||||
}
|
||||
@@ -1932,6 +1948,17 @@ static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index)
|
||||
return topo.pkg_id;
|
||||
}
|
||||
|
||||
static CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *machine)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
int len = sizeof(CPUArchIdList) +
|
||||
sizeof(CPUArchId) * (pcms->possible_cpus->len);
|
||||
CPUArchIdList *list = g_malloc(len);
|
||||
|
||||
memcpy(list, pcms->possible_cpus, len);
|
||||
return list;
|
||||
}
|
||||
|
||||
static void pc_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
@@ -1954,6 +1981,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
|
||||
pcmc->save_tsc_khz = true;
|
||||
mc->get_hotplug_handler = pc_get_hotpug_handler;
|
||||
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
|
||||
mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids;
|
||||
mc->default_boot_order = "cad";
|
||||
mc->hot_add_cpu = pc_hot_add_cpu;
|
||||
mc->max_cpus = 255;
|
||||
|
||||
@@ -274,6 +274,11 @@ static void pc_init1(MachineState *machine,
|
||||
if (pcmc->pci_enabled) {
|
||||
pc_pci_device_init(pci_bus);
|
||||
}
|
||||
|
||||
if (pcms->acpi_nvdimm_state.is_enabled) {
|
||||
nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
|
||||
pcms->fw_cfg, OBJECT(pcms));
|
||||
}
|
||||
}
|
||||
|
||||
/* Looking for a pc_compat_2_4() function? It doesn't exist.
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "hw/kvm/clock.h"
|
||||
#include "hw/pci-host/q35.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/i386/ich9.h"
|
||||
#include "hw/smbios/smbios.h"
|
||||
#include "hw/ide/pci.h"
|
||||
@@ -61,6 +62,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
PCIDevice *lpc;
|
||||
BusState *idebus[MAX_SATA_PORTS];
|
||||
ISADevice *rtc_state;
|
||||
MemoryRegion *system_io = get_system_io();
|
||||
MemoryRegion *pci_memory;
|
||||
MemoryRegion *rom_memory;
|
||||
MemoryRegion *ram_memory;
|
||||
@@ -145,7 +147,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
|
||||
/* irq lines */
|
||||
gsi_state = g_malloc0(sizeof(*gsi_state));
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
if (kvm_ioapic_in_kernel()) {
|
||||
kvm_pc_setup_irq_routing(pcmc->pci_enabled);
|
||||
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
|
||||
GSI_NUM_PINS);
|
||||
@@ -160,7 +162,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
q35_host->mch.ram_memory = ram_memory;
|
||||
q35_host->mch.pci_address_space = pci_memory;
|
||||
q35_host->mch.system_memory = get_system_memory();
|
||||
q35_host->mch.address_space_io = get_system_io();
|
||||
q35_host->mch.address_space_io = system_io;
|
||||
q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
|
||||
q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
|
||||
/* pci */
|
||||
@@ -192,7 +194,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
/*end early*/
|
||||
isa_bus_irqs(isa_bus, gsi);
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
if (kvm_pic_in_kernel()) {
|
||||
i8259 = kvm_i8259_init(isa_bus);
|
||||
} else if (xen_enabled()) {
|
||||
i8259 = xen_interrupt_controller_init();
|
||||
@@ -251,6 +253,11 @@ static void pc_q35_init(MachineState *machine)
|
||||
if (pcmc->pci_enabled) {
|
||||
pc_pci_device_init(host_bus);
|
||||
}
|
||||
|
||||
if (pcms->acpi_nvdimm_state.is_enabled) {
|
||||
nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
|
||||
pcms->fw_cfg, OBJECT(pcms));
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \
|
||||
|
||||
@@ -44,7 +44,7 @@ static void xen_apic_realize(DeviceState *dev, Error **errp)
|
||||
s->vapic_control = 0;
|
||||
memory_region_init_io(&s->io_memory, OBJECT(s), &xen_apic_io_ops, s,
|
||||
"xen-apic-msi", APIC_SPACE_SIZE);
|
||||
msi_supported = true;
|
||||
msi_nonbroken = true;
|
||||
}
|
||||
|
||||
static void xen_apic_set_base(APICCommonState *s, uint64_t val)
|
||||
|
||||
@@ -31,3 +31,4 @@ obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
||||
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
|
||||
obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o
|
||||
|
||||
@@ -874,7 +874,7 @@ static void apic_realize(DeviceState *dev, Error **errp)
|
||||
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s);
|
||||
local_apics[s->idx] = s;
|
||||
|
||||
msi_supported = true;
|
||||
msi_nonbroken = true;
|
||||
}
|
||||
|
||||
static void apic_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
@@ -148,7 +148,7 @@ static void gicv2m_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]);
|
||||
}
|
||||
|
||||
msi_supported = true;
|
||||
msi_nonbroken = true;
|
||||
kvm_gsi_direct_mapping = true;
|
||||
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
|
||||
}
|
||||
|
||||
339
hw/intc/aspeed_vic.c
Normal file
339
hw/intc/aspeed_vic.c
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* ASPEED Interrupt Controller (New)
|
||||
*
|
||||
* Andrew Jeffery <andrew@aj.id.au>
|
||||
*
|
||||
* Copyright 2015, 2016 IBM Corp.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
/* The hardware exposes two register sets, a legacy set and a 'new' set. The
|
||||
* model implements the 'new' register set, and logs warnings on accesses to
|
||||
* the legacy IO space.
|
||||
*
|
||||
* The hardware uses 32bit registers to manage 51 IRQs, with low and high
|
||||
* registers for each conceptual register. The device model's implementation
|
||||
* uses 64bit data types to store both low and high register values (in the one
|
||||
* member), but must cope with access offset values in multiples of 4 passed to
|
||||
* the callbacks. As such the read() and write() implementations process the
|
||||
* provided offset to understand whether the access is requesting the lower or
|
||||
* upper 32 bits of the 64bit member.
|
||||
*
|
||||
* Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt"
|
||||
* fields have separate "enable"/"status" and "clear" registers, where set bits
|
||||
* are written to one or the other to change state (avoiding a
|
||||
* read-modify-write sequence).
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <inttypes.h>
|
||||
#include "hw/intc/aspeed_vic.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define AVIC_NEW_BASE_OFFSET 0x80
|
||||
|
||||
#define AVIC_L_MASK 0xFFFFFFFFU
|
||||
#define AVIC_H_MASK 0x0007FFFFU
|
||||
#define AVIC_EVENT_W_MASK (0x78000ULL << 32)
|
||||
|
||||
static void aspeed_vic_update(AspeedVICState *s)
|
||||
{
|
||||
uint64_t new = (s->raw & s->enable);
|
||||
uint64_t flags;
|
||||
|
||||
flags = new & s->select;
|
||||
trace_aspeed_vic_update_fiq(!!flags);
|
||||
qemu_set_irq(s->fiq, !!flags);
|
||||
|
||||
flags = new & ~s->select;
|
||||
trace_aspeed_vic_update_irq(!!flags);
|
||||
qemu_set_irq(s->irq, !!flags);
|
||||
}
|
||||
|
||||
static void aspeed_vic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
uint64_t irq_mask;
|
||||
bool raise;
|
||||
AspeedVICState *s = (AspeedVICState *)opaque;
|
||||
|
||||
if (irq > ASPEED_VIC_NR_IRQS) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
|
||||
__func__, irq);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_aspeed_vic_set_irq(irq, level);
|
||||
|
||||
irq_mask = BIT(irq);
|
||||
if (s->sense & irq_mask) {
|
||||
/* level-triggered */
|
||||
if (s->event & irq_mask) {
|
||||
/* high-sensitive */
|
||||
raise = level;
|
||||
} else {
|
||||
/* low-sensitive */
|
||||
raise = !level;
|
||||
}
|
||||
s->raw = deposit64(s->raw, irq, 1, raise);
|
||||
} else {
|
||||
uint64_t old_level = s->level & irq_mask;
|
||||
|
||||
/* edge-triggered */
|
||||
if (s->dual_edge & irq_mask) {
|
||||
raise = (!!old_level) != (!!level);
|
||||
} else {
|
||||
if (s->event & irq_mask) {
|
||||
/* rising-sensitive */
|
||||
raise = !old_level && level;
|
||||
} else {
|
||||
/* falling-sensitive */
|
||||
raise = old_level && !level;
|
||||
}
|
||||
}
|
||||
if (raise) {
|
||||
s->raw = deposit64(s->raw, irq, 1, raise);
|
||||
}
|
||||
}
|
||||
s->level = deposit64(s->level, irq, 1, level);
|
||||
aspeed_vic_update(s);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
n_offset -= AVIC_NEW_BASE_OFFSET;
|
||||
|
||||
switch (n_offset) {
|
||||
case 0x0: /* IRQ Status */
|
||||
val = s->raw & ~s->select & s->enable;
|
||||
break;
|
||||
case 0x08: /* FIQ Status */
|
||||
val = s->raw & s->select & s->enable;
|
||||
break;
|
||||
case 0x10: /* Raw Interrupt Status */
|
||||
val = s->raw;
|
||||
break;
|
||||
case 0x18: /* Interrupt Selection */
|
||||
val = s->select;
|
||||
break;
|
||||
case 0x20: /* Interrupt Enable */
|
||||
val = s->enable;
|
||||
break;
|
||||
case 0x30: /* Software Interrupt */
|
||||
val = s->trigger;
|
||||
break;
|
||||
case 0x40: /* Interrupt Sensitivity */
|
||||
val = s->sense;
|
||||
break;
|
||||
case 0x48: /* Interrupt Both Edge Trigger Control */
|
||||
val = s->dual_edge;
|
||||
break;
|
||||
case 0x50: /* Interrupt Event */
|
||||
val = s->event;
|
||||
break;
|
||||
case 0x60: /* 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 */
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Read of write-only register with offset 0x%"
|
||||
HWADDR_PRIx "\n", __func__, offset);
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
if (high) {
|
||||
val = extract64(val, 32, 19);
|
||||
}
|
||||
trace_aspeed_vic_read(offset, size, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
n_offset -= AVIC_NEW_BASE_OFFSET;
|
||||
trace_aspeed_vic_write(offset, size, data);
|
||||
|
||||
/* Given we have members using separate enable/clear registers, deposit64()
|
||||
* isn't quite the tool for the job. Instead, relocate the incoming bits to
|
||||
* the required bit offset based on the provided access address
|
||||
*/
|
||||
if (high) {
|
||||
data &= AVIC_H_MASK;
|
||||
data <<= 32;
|
||||
} else {
|
||||
data &= AVIC_L_MASK;
|
||||
}
|
||||
|
||||
switch (n_offset) {
|
||||
case 0x18: /* Interrupt Selection */
|
||||
/* Register has deposit64() semantics - overwrite requested 32 bits */
|
||||
if (high) {
|
||||
s->select &= AVIC_L_MASK;
|
||||
} else {
|
||||
s->select &= ((uint64_t) AVIC_H_MASK) << 32;
|
||||
}
|
||||
s->select |= data;
|
||||
break;
|
||||
case 0x20: /* Interrupt Enable */
|
||||
s->enable |= data;
|
||||
break;
|
||||
case 0x28: /* Interrupt Enable Clear */
|
||||
s->enable &= ~data;
|
||||
break;
|
||||
case 0x30: /* Software Interrupt */
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
|
||||
"IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
|
||||
break;
|
||||
case 0x38: /* Software Interrupt Clear */
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
|
||||
"IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
|
||||
break;
|
||||
case 0x50: /* 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 */
|
||||
if (high) {
|
||||
s->event &= ~AVIC_EVENT_W_MASK;
|
||||
s->event |= (data & AVIC_EVENT_W_MASK);
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Ignoring invalid write to interrupt event register");
|
||||
}
|
||||
break;
|
||||
case 0x58: /* Edge Triggered Interrupt Clear */
|
||||
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 */
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Write of read-only register with offset 0x%"
|
||||
HWADDR_PRIx "\n", __func__, offset);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
aspeed_vic_update(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aspeed_vic_ops = {
|
||||
.read = aspeed_vic_read,
|
||||
.write = aspeed_vic_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
.valid.unaligned = false,
|
||||
};
|
||||
|
||||
static void aspeed_vic_reset(DeviceState *dev)
|
||||
{
|
||||
AspeedVICState *s = ASPEED_VIC(dev);
|
||||
|
||||
s->level = 0;
|
||||
s->raw = 0;
|
||||
s->select = 0;
|
||||
s->enable = 0;
|
||||
s->trigger = 0;
|
||||
s->sense = 0x1F07FFF8FFFFULL;
|
||||
s->dual_edge = 0xF800070000ULL;
|
||||
s->event = 0x5F07FFF8FFFFULL;
|
||||
}
|
||||
|
||||
#define AVIC_IO_REGION_SIZE 0x20000
|
||||
|
||||
static void aspeed_vic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
AspeedVICState *s = ASPEED_VIC(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
|
||||
TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
|
||||
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->fiq);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_aspeed_vic = {
|
||||
.name = "aspeed.new-vic",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(level, AspeedVICState),
|
||||
VMSTATE_UINT64(raw, AspeedVICState),
|
||||
VMSTATE_UINT64(select, AspeedVICState),
|
||||
VMSTATE_UINT64(enable, AspeedVICState),
|
||||
VMSTATE_UINT64(trigger, AspeedVICState),
|
||||
VMSTATE_UINT64(sense, AspeedVICState),
|
||||
VMSTATE_UINT64(dual_edge, AspeedVICState),
|
||||
VMSTATE_UINT64(event, AspeedVICState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void aspeed_vic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = aspeed_vic_realize;
|
||||
dc->reset = aspeed_vic_reset;
|
||||
dc->desc = "ASPEED Interrupt Controller (New)";
|
||||
dc->vmsd = &vmstate_aspeed_vic;
|
||||
}
|
||||
|
||||
static const TypeInfo aspeed_vic_info = {
|
||||
.name = TYPE_ASPEED_VIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AspeedVICState),
|
||||
.class_init = aspeed_vic_class_init,
|
||||
};
|
||||
|
||||
static void aspeed_vic_register_types(void)
|
||||
{
|
||||
type_register_static(&aspeed_vic_info);
|
||||
}
|
||||
|
||||
type_init(aspeed_vic_register_types);
|
||||
@@ -1375,7 +1375,7 @@ static void fsl_common_init(OpenPICState *opp)
|
||||
|
||||
opp->irq_msi = 224;
|
||||
|
||||
msi_supported = true;
|
||||
msi_nonbroken = true;
|
||||
for (i = 0; i < opp->fsl->max_ext; i++) {
|
||||
opp->src[i].level = false;
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ static void kvm_openpic_realize(DeviceState *dev, Error **errp)
|
||||
memory_listener_register(&opp->mem_listener, &address_space_memory);
|
||||
|
||||
/* indicate pic capabilities */
|
||||
msi_supported = true;
|
||||
msi_nonbroken = true;
|
||||
kvm_kernel_irqchip = true;
|
||||
kvm_async_interrupts_allowed = true;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -409,18 +409,18 @@ ich9_lpc_pmbase_update(ICH9LPCState *lpc)
|
||||
ich9_pm_iospace_update(&lpc->pm, pm_io_base);
|
||||
}
|
||||
|
||||
/* config:RBCA */
|
||||
static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old)
|
||||
/* config:RCBA */
|
||||
static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rcba_old)
|
||||
{
|
||||
uint32_t rbca = pci_get_long(lpc->d.config + ICH9_LPC_RCBA);
|
||||
uint32_t rcba = pci_get_long(lpc->d.config + ICH9_LPC_RCBA);
|
||||
|
||||
if (rbca_old & ICH9_LPC_RCBA_EN) {
|
||||
memory_region_del_subregion(get_system_memory(), &lpc->rbca_mem);
|
||||
if (rcba_old & ICH9_LPC_RCBA_EN) {
|
||||
memory_region_del_subregion(get_system_memory(), &lpc->rcrb_mem);
|
||||
}
|
||||
if (rbca & ICH9_LPC_RCBA_EN) {
|
||||
memory_region_add_subregion_overlap(get_system_memory(),
|
||||
rbca & ICH9_LPC_RCBA_BA_MASK,
|
||||
&lpc->rbca_mem, 1);
|
||||
if (rcba & ICH9_LPC_RCBA_EN) {
|
||||
memory_region_add_subregion_overlap(get_system_memory(),
|
||||
rcba & ICH9_LPC_RCBA_BA_MASK,
|
||||
&lpc->rcrb_mem, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ static int ich9_lpc_post_load(void *opaque, int version_id)
|
||||
ICH9LPCState *lpc = opaque;
|
||||
|
||||
ich9_lpc_pmbase_update(lpc);
|
||||
ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */);
|
||||
ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RCBA_EN */);
|
||||
ich9_lpc_pmcon_update(lpc);
|
||||
return 0;
|
||||
}
|
||||
@@ -453,14 +453,14 @@ static void ich9_lpc_config_write(PCIDevice *d,
|
||||
uint32_t addr, uint32_t val, int len)
|
||||
{
|
||||
ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
|
||||
uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA);
|
||||
uint32_t rcba_old = pci_get_long(d->config + ICH9_LPC_RCBA);
|
||||
|
||||
pci_default_write_config(d, addr, val, len);
|
||||
if (ranges_overlap(addr, len, ICH9_LPC_PMBASE, 4)) {
|
||||
ich9_lpc_pmbase_update(lpc);
|
||||
}
|
||||
if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) {
|
||||
ich9_lpc_rcba_update(lpc, rbca_old);
|
||||
ich9_lpc_rcba_update(lpc, rcba_old);
|
||||
}
|
||||
if (ranges_overlap(addr, len, ICH9_LPC_PIRQA_ROUT, 4)) {
|
||||
pci_bus_fire_intx_routing_notifier(lpc->d.bus);
|
||||
@@ -477,7 +477,7 @@ static void ich9_lpc_reset(DeviceState *qdev)
|
||||
{
|
||||
PCIDevice *d = PCI_DEVICE(qdev);
|
||||
ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
|
||||
uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA);
|
||||
uint32_t rcba_old = pci_get_long(d->config + ICH9_LPC_RCBA);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
@@ -496,13 +496,14 @@ static void ich9_lpc_reset(DeviceState *qdev)
|
||||
ich9_cc_reset(lpc);
|
||||
|
||||
ich9_lpc_pmbase_update(lpc);
|
||||
ich9_lpc_rcba_update(lpc, rbca_old);
|
||||
ich9_lpc_rcba_update(lpc, rcba_old);
|
||||
|
||||
lpc->sci_level = 0;
|
||||
lpc->rst_cnt = 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps rbca_mmio_ops = {
|
||||
/* root complex register block is mapped into memory space */
|
||||
static const MemoryRegionOps rcrb_mmio_ops = {
|
||||
.read = ich9_cc_read,
|
||||
.write = ich9_cc_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
@@ -616,8 +617,8 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp)
|
||||
pci_set_long(d->wmask + ICH9_LPC_PMBASE,
|
||||
ICH9_LPC_PMBASE_BASE_ADDRESS_MASK);
|
||||
|
||||
memory_region_init_io(&lpc->rbca_mem, OBJECT(d), &rbca_mmio_ops, lpc,
|
||||
"lpc-rbca-mmio", ICH9_CC_SIZE);
|
||||
memory_region_init_io(&lpc->rcrb_mem, OBJECT(d), &rcrb_mmio_ops, lpc,
|
||||
"lpc-rcrb-mmio", ICH9_CC_SIZE);
|
||||
|
||||
lpc->isa_bus = isa_bus;
|
||||
|
||||
|
||||
@@ -329,4 +329,4 @@ static void lm32_machine_init(void)
|
||||
type_register_static(&lm32_uclinux_type);
|
||||
}
|
||||
|
||||
machine_init(lm32_machine_init)
|
||||
type_init(lm32_machine_init)
|
||||
|
||||
@@ -364,15 +364,22 @@ static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name,
|
||||
Object *val, Error **errp)
|
||||
{
|
||||
MemoryRegion *mr;
|
||||
Error *local_err = NULL;
|
||||
|
||||
mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp);
|
||||
mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
if (memory_region_is_mapped(mr)) {
|
||||
char *path = object_get_canonical_path_component(val);
|
||||
error_setg(errp, "can't use already busy memdev: %s", path);
|
||||
error_setg(&local_err, "can't use already busy memdev: %s", path);
|
||||
g_free(path);
|
||||
} else {
|
||||
qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
|
||||
qdev_prop_allow_set_link_before_realize(obj, name, val, &local_err);
|
||||
}
|
||||
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void pc_dimm_init(Object *obj)
|
||||
|
||||
@@ -387,4 +387,4 @@ static void mips_jazz_machine_init(void)
|
||||
type_register_static(&mips_pica61_type);
|
||||
}
|
||||
|
||||
machine_init(mips_jazz_machine_init)
|
||||
type_init(mips_jazz_machine_init)
|
||||
|
||||
@@ -28,6 +28,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
|
||||
obj-$(CONFIG_IMX) += imx_ccm.o
|
||||
obj-$(CONFIG_IMX) += imx31_ccm.o
|
||||
obj-$(CONFIG_IMX) += imx25_ccm.o
|
||||
obj-$(CONFIG_IMX) += imx6_ccm.o
|
||||
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
|
||||
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
|
||||
obj-$(CONFIG_MAINSTONE) += mst_fpga.o
|
||||
|
||||
@@ -17,6 +17,11 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
||||
uint32_t tot_len;
|
||||
size_t resplen;
|
||||
uint32_t tmp;
|
||||
int n;
|
||||
uint32_t offset, length, color;
|
||||
uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha;
|
||||
uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL,
|
||||
*newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL;
|
||||
|
||||
value &= ~0xf;
|
||||
|
||||
@@ -60,7 +65,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
||||
/* base */
|
||||
stl_le_phys(&s->dma_as, value + 12, 0);
|
||||
/* size */
|
||||
stl_le_phys(&s->dma_as, value + 16, s->ram_size);
|
||||
stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00010006: /* Get VC memory */
|
||||
/* base */
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base);
|
||||
/* size */
|
||||
stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00028001: /* Set power state */
|
||||
@@ -122,6 +134,114 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
||||
resplen = 8;
|
||||
break;
|
||||
|
||||
/* Frame buffer */
|
||||
|
||||
case 0x00040001: /* Allocate buffer */
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->base);
|
||||
stl_le_phys(&s->dma_as, value + 16, s->fbdev->size);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00048001: /* Release buffer */
|
||||
resplen = 0;
|
||||
break;
|
||||
case 0x00040002: /* Blank screen */
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040003: /* Get display width/height */
|
||||
case 0x00040004:
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres);
|
||||
stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00044003: /* Test display width/height */
|
||||
case 0x00044004:
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00048003: /* Set display width/height */
|
||||
case 0x00048004:
|
||||
xres = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newxres = &xres;
|
||||
yres = ldl_le_phys(&s->dma_as, value + 16);
|
||||
newyres = &yres;
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00040005: /* Get depth */
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp);
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00044005: /* Test depth */
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00048005: /* Set depth */
|
||||
bpp = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newbpp = &bpp;
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040006: /* Get pixel order */
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo);
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00044006: /* Test pixel order */
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00048006: /* Set pixel order */
|
||||
pixo = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newpixo = &pixo;
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040007: /* Get alpha */
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha);
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00044007: /* Test pixel alpha */
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00048007: /* Set alpha */
|
||||
alpha = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newalpha = α
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040008: /* Get pitch */
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch);
|
||||
resplen = 4;
|
||||
break;
|
||||
case 0x00040009: /* Get virtual offset */
|
||||
stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset);
|
||||
stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset);
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00044009: /* Test virtual offset */
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x00048009: /* Set virtual offset */
|
||||
xoffset = ldl_le_phys(&s->dma_as, value + 12);
|
||||
newxoffset = &xoffset;
|
||||
yoffset = ldl_le_phys(&s->dma_as, value + 16);
|
||||
newyoffset = &yoffset;
|
||||
resplen = 8;
|
||||
break;
|
||||
case 0x0004000a: /* Get/Test/Set overscan */
|
||||
case 0x0004400a:
|
||||
case 0x0004800a:
|
||||
stl_le_phys(&s->dma_as, value + 12, 0);
|
||||
stl_le_phys(&s->dma_as, value + 16, 0);
|
||||
stl_le_phys(&s->dma_as, value + 20, 0);
|
||||
stl_le_phys(&s->dma_as, value + 24, 0);
|
||||
resplen = 16;
|
||||
break;
|
||||
case 0x0004800b: /* Set palette */
|
||||
offset = ldl_le_phys(&s->dma_as, value + 12);
|
||||
length = ldl_le_phys(&s->dma_as, value + 16);
|
||||
n = 0;
|
||||
while (n < length - offset) {
|
||||
color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2));
|
||||
stl_le_phys(&s->dma_as,
|
||||
s->fbdev->vcram_base + ((offset + n) << 2), color);
|
||||
n++;
|
||||
}
|
||||
stl_le_phys(&s->dma_as, value + 12, 0);
|
||||
resplen = 4;
|
||||
break;
|
||||
|
||||
case 0x00060001: /* Get DMA channels */
|
||||
/* channels 2-5 */
|
||||
@@ -147,6 +267,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
||||
value += bufsize + 12;
|
||||
}
|
||||
|
||||
/* Reconfigure framebuffer if required */
|
||||
if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo
|
||||
|| newalpha) {
|
||||
bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset,
|
||||
newyoffset, newbpp, newpixo, newalpha);
|
||||
}
|
||||
|
||||
/* Buffer response code */
|
||||
stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
|
||||
}
|
||||
@@ -241,6 +368,15 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp)
|
||||
Object *obj;
|
||||
Error *err = NULL;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "fb", &err);
|
||||
if (obj == NULL) {
|
||||
error_setg(errp, "%s: required fb link not found: %s",
|
||||
__func__, error_get_pretty(err));
|
||||
return;
|
||||
}
|
||||
|
||||
s->fbdev = BCM2835_FB(obj);
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
|
||||
if (obj == NULL) {
|
||||
error_setg(errp, "%s: required dma-mr link not found: %s",
|
||||
@@ -259,7 +395,6 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
static Property bcm2835_property_props[] = {
|
||||
DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
|
||||
DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
|
||||
@@ -120,20 +120,6 @@ static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx25_ccm_get_upll_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq = 0;
|
||||
IMX25CCMState *s = IMX25_CCM(dev);
|
||||
|
||||
if (!EXTRACT(s->reg[IMX25_CCM_CCTL_REG], UPLL_DIS)) {
|
||||
freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_UPCTL_REG], CKIH_FREQ);
|
||||
}
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
@@ -182,21 +168,10 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
|
||||
DPRINTF("Clock = %d)\n", clock);
|
||||
|
||||
switch (clock) {
|
||||
case NOCLK:
|
||||
break;
|
||||
case CLK_MPLL:
|
||||
freq = imx25_ccm_get_mpll_clk(dev);
|
||||
break;
|
||||
case CLK_UPLL:
|
||||
freq = imx25_ccm_get_upll_clk(dev);
|
||||
break;
|
||||
case CLK_MCU:
|
||||
freq = imx25_ccm_get_mcu_clk(dev);
|
||||
break;
|
||||
case CLK_AHB:
|
||||
freq = imx25_ccm_get_ahb_clk(dev);
|
||||
case CLK_NONE:
|
||||
break;
|
||||
case CLK_IPG:
|
||||
case CLK_IPG_HIGH:
|
||||
freq = imx25_ccm_get_ipg_clk(dev);
|
||||
break;
|
||||
case CLK_32k:
|
||||
|
||||
@@ -152,32 +152,6 @@ static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
freq = imx31_ccm_get_mcu_main_clk(dev)
|
||||
/ (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MCU));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
freq = imx31_ccm_get_mcu_main_clk(dev)
|
||||
/ (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], HSP));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
@@ -209,15 +183,10 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
|
||||
uint32_t freq = 0;
|
||||
|
||||
switch (clock) {
|
||||
case NOCLK:
|
||||
break;
|
||||
case CLK_MCU:
|
||||
freq = imx31_ccm_get_mcu_clk(dev);
|
||||
break;
|
||||
case CLK_HSP:
|
||||
freq = imx31_ccm_get_hsp_clk(dev);
|
||||
case CLK_NONE:
|
||||
break;
|
||||
case CLK_IPG:
|
||||
case CLK_IPG_HIGH:
|
||||
freq = imx31_ccm_get_ipg_clk(dev);
|
||||
break;
|
||||
case CLK_32k:
|
||||
|
||||
774
hw/misc/imx6_ccm.c
Normal file
774
hw/misc/imx6_ccm.c
Normal file
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
* IMX6 Clock Control Module
|
||||
*
|
||||
* Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* To get the timer frequencies right, we need to emulate at least part of
|
||||
* the CCM.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/misc/imx6_ccm.h"
|
||||
|
||||
#ifndef DEBUG_IMX6_CCM
|
||||
#define DEBUG_IMX6_CCM 0
|
||||
#endif
|
||||
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { \
|
||||
if (DEBUG_IMX6_CCM) { \
|
||||
fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \
|
||||
__func__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static char const *imx6_ccm_reg_name(uint32_t reg)
|
||||
{
|
||||
static char unknown[20];
|
||||
|
||||
switch (reg) {
|
||||
case CCM_CCR:
|
||||
return "CCR";
|
||||
case CCM_CCDR:
|
||||
return "CCDR";
|
||||
case CCM_CSR:
|
||||
return "CSR";
|
||||
case CCM_CCSR:
|
||||
return "CCSR";
|
||||
case CCM_CACRR:
|
||||
return "CACRR";
|
||||
case CCM_CBCDR:
|
||||
return "CBCDR";
|
||||
case CCM_CBCMR:
|
||||
return "CBCMR";
|
||||
case CCM_CSCMR1:
|
||||
return "CSCMR1";
|
||||
case CCM_CSCMR2:
|
||||
return "CSCMR2";
|
||||
case CCM_CSCDR1:
|
||||
return "CSCDR1";
|
||||
case CCM_CS1CDR:
|
||||
return "CS1CDR";
|
||||
case CCM_CS2CDR:
|
||||
return "CS2CDR";
|
||||
case CCM_CDCDR:
|
||||
return "CDCDR";
|
||||
case CCM_CHSCCDR:
|
||||
return "CHSCCDR";
|
||||
case CCM_CSCDR2:
|
||||
return "CSCDR2";
|
||||
case CCM_CSCDR3:
|
||||
return "CSCDR3";
|
||||
case CCM_CDHIPR:
|
||||
return "CDHIPR";
|
||||
case CCM_CTOR:
|
||||
return "CTOR";
|
||||
case CCM_CLPCR:
|
||||
return "CLPCR";
|
||||
case CCM_CISR:
|
||||
return "CISR";
|
||||
case CCM_CIMR:
|
||||
return "CIMR";
|
||||
case CCM_CCOSR:
|
||||
return "CCOSR";
|
||||
case CCM_CGPR:
|
||||
return "CGPR";
|
||||
case CCM_CCGR0:
|
||||
return "CCGR0";
|
||||
case CCM_CCGR1:
|
||||
return "CCGR1";
|
||||
case CCM_CCGR2:
|
||||
return "CCGR2";
|
||||
case CCM_CCGR3:
|
||||
return "CCGR3";
|
||||
case CCM_CCGR4:
|
||||
return "CCGR4";
|
||||
case CCM_CCGR5:
|
||||
return "CCGR5";
|
||||
case CCM_CCGR6:
|
||||
return "CCGR6";
|
||||
case CCM_CMEOR:
|
||||
return "CMEOR";
|
||||
default:
|
||||
sprintf(unknown, "%d ?", reg);
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
static char const *imx6_analog_reg_name(uint32_t reg)
|
||||
{
|
||||
static char unknown[20];
|
||||
|
||||
switch (reg) {
|
||||
case CCM_ANALOG_PLL_ARM:
|
||||
return "PLL_ARM";
|
||||
case CCM_ANALOG_PLL_ARM_SET:
|
||||
return "PLL_ARM_SET";
|
||||
case CCM_ANALOG_PLL_ARM_CLR:
|
||||
return "PLL_ARM_CLR";
|
||||
case CCM_ANALOG_PLL_ARM_TOG:
|
||||
return "PLL_ARM_TOG";
|
||||
case CCM_ANALOG_PLL_USB1:
|
||||
return "PLL_USB1";
|
||||
case CCM_ANALOG_PLL_USB1_SET:
|
||||
return "PLL_USB1_SET";
|
||||
case CCM_ANALOG_PLL_USB1_CLR:
|
||||
return "PLL_USB1_CLR";
|
||||
case CCM_ANALOG_PLL_USB1_TOG:
|
||||
return "PLL_USB1_TOG";
|
||||
case CCM_ANALOG_PLL_USB2:
|
||||
return "PLL_USB2";
|
||||
case CCM_ANALOG_PLL_USB2_SET:
|
||||
return "PLL_USB2_SET";
|
||||
case CCM_ANALOG_PLL_USB2_CLR:
|
||||
return "PLL_USB2_CLR";
|
||||
case CCM_ANALOG_PLL_USB2_TOG:
|
||||
return "PLL_USB2_TOG";
|
||||
case CCM_ANALOG_PLL_SYS:
|
||||
return "PLL_SYS";
|
||||
case CCM_ANALOG_PLL_SYS_SET:
|
||||
return "PLL_SYS_SET";
|
||||
case CCM_ANALOG_PLL_SYS_CLR:
|
||||
return "PLL_SYS_CLR";
|
||||
case CCM_ANALOG_PLL_SYS_TOG:
|
||||
return "PLL_SYS_TOG";
|
||||
case CCM_ANALOG_PLL_SYS_SS:
|
||||
return "PLL_SYS_SS";
|
||||
case CCM_ANALOG_PLL_SYS_NUM:
|
||||
return "PLL_SYS_NUM";
|
||||
case CCM_ANALOG_PLL_SYS_DENOM:
|
||||
return "PLL_SYS_DENOM";
|
||||
case CCM_ANALOG_PLL_AUDIO:
|
||||
return "PLL_AUDIO";
|
||||
case CCM_ANALOG_PLL_AUDIO_SET:
|
||||
return "PLL_AUDIO_SET";
|
||||
case CCM_ANALOG_PLL_AUDIO_CLR:
|
||||
return "PLL_AUDIO_CLR";
|
||||
case CCM_ANALOG_PLL_AUDIO_TOG:
|
||||
return "PLL_AUDIO_TOG";
|
||||
case CCM_ANALOG_PLL_AUDIO_NUM:
|
||||
return "PLL_AUDIO_NUM";
|
||||
case CCM_ANALOG_PLL_AUDIO_DENOM:
|
||||
return "PLL_AUDIO_DENOM";
|
||||
case CCM_ANALOG_PLL_VIDEO:
|
||||
return "PLL_VIDEO";
|
||||
case CCM_ANALOG_PLL_VIDEO_SET:
|
||||
return "PLL_VIDEO_SET";
|
||||
case CCM_ANALOG_PLL_VIDEO_CLR:
|
||||
return "PLL_VIDEO_CLR";
|
||||
case CCM_ANALOG_PLL_VIDEO_TOG:
|
||||
return "PLL_VIDEO_TOG";
|
||||
case CCM_ANALOG_PLL_VIDEO_NUM:
|
||||
return "PLL_VIDEO_NUM";
|
||||
case CCM_ANALOG_PLL_VIDEO_DENOM:
|
||||
return "PLL_VIDEO_DENOM";
|
||||
case CCM_ANALOG_PLL_MLB:
|
||||
return "PLL_MLB";
|
||||
case CCM_ANALOG_PLL_MLB_SET:
|
||||
return "PLL_MLB_SET";
|
||||
case CCM_ANALOG_PLL_MLB_CLR:
|
||||
return "PLL_MLB_CLR";
|
||||
case CCM_ANALOG_PLL_MLB_TOG:
|
||||
return "PLL_MLB_TOG";
|
||||
case CCM_ANALOG_PLL_ENET:
|
||||
return "PLL_ENET";
|
||||
case CCM_ANALOG_PLL_ENET_SET:
|
||||
return "PLL_ENET_SET";
|
||||
case CCM_ANALOG_PLL_ENET_CLR:
|
||||
return "PLL_ENET_CLR";
|
||||
case CCM_ANALOG_PLL_ENET_TOG:
|
||||
return "PLL_ENET_TOG";
|
||||
case CCM_ANALOG_PFD_480:
|
||||
return "PFD_480";
|
||||
case CCM_ANALOG_PFD_480_SET:
|
||||
return "PFD_480_SET";
|
||||
case CCM_ANALOG_PFD_480_CLR:
|
||||
return "PFD_480_CLR";
|
||||
case CCM_ANALOG_PFD_480_TOG:
|
||||
return "PFD_480_TOG";
|
||||
case CCM_ANALOG_PFD_528:
|
||||
return "PFD_528";
|
||||
case CCM_ANALOG_PFD_528_SET:
|
||||
return "PFD_528_SET";
|
||||
case CCM_ANALOG_PFD_528_CLR:
|
||||
return "PFD_528_CLR";
|
||||
case CCM_ANALOG_PFD_528_TOG:
|
||||
return "PFD_528_TOG";
|
||||
case CCM_ANALOG_MISC0:
|
||||
return "MISC0";
|
||||
case CCM_ANALOG_MISC0_SET:
|
||||
return "MISC0_SET";
|
||||
case CCM_ANALOG_MISC0_CLR:
|
||||
return "MISC0_CLR";
|
||||
case CCM_ANALOG_MISC0_TOG:
|
||||
return "MISC0_TOG";
|
||||
case CCM_ANALOG_MISC2:
|
||||
return "MISC2";
|
||||
case CCM_ANALOG_MISC2_SET:
|
||||
return "MISC2_SET";
|
||||
case CCM_ANALOG_MISC2_CLR:
|
||||
return "MISC2_CLR";
|
||||
case CCM_ANALOG_MISC2_TOG:
|
||||
return "MISC2_TOG";
|
||||
case PMU_REG_1P1:
|
||||
return "PMU_REG_1P1";
|
||||
case PMU_REG_3P0:
|
||||
return "PMU_REG_3P0";
|
||||
case PMU_REG_2P5:
|
||||
return "PMU_REG_2P5";
|
||||
case PMU_REG_CORE:
|
||||
return "PMU_REG_CORE";
|
||||
case PMU_MISC1:
|
||||
return "PMU_MISC1";
|
||||
case PMU_MISC1_SET:
|
||||
return "PMU_MISC1_SET";
|
||||
case PMU_MISC1_CLR:
|
||||
return "PMU_MISC1_CLR";
|
||||
case PMU_MISC1_TOG:
|
||||
return "PMU_MISC1_TOG";
|
||||
case USB_ANALOG_DIGPROG:
|
||||
return "USB_ANALOG_DIGPROG";
|
||||
default:
|
||||
sprintf(unknown, "%d ?", reg);
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
#define CKIH_FREQ 24000000 /* 24MHz crystal input */
|
||||
|
||||
static const VMStateDescription vmstate_imx6_ccm = {
|
||||
.name = TYPE_IMX6_CCM,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX),
|
||||
VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev)
|
||||
{
|
||||
uint64_t freq = 24000000;
|
||||
|
||||
if (EXTRACT(dev->analog[CCM_ANALOG_PLL_SYS], DIV_SELECT)) {
|
||||
freq *= 22;
|
||||
} else {
|
||||
freq *= 20;
|
||||
}
|
||||
|
||||
DPRINTF("freq = %d\n", (uint32_t)freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev)
|
||||
{
|
||||
uint64_t freq = 0;
|
||||
|
||||
freq = imx6_analog_get_pll2_clk(dev) * 18
|
||||
/ EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC);
|
||||
|
||||
DPRINTF("freq = %d\n", (uint32_t)freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev)
|
||||
{
|
||||
uint64_t freq = 0;
|
||||
|
||||
freq = imx6_analog_get_pll2_clk(dev) * 18
|
||||
/ EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC);
|
||||
|
||||
DPRINTF("freq = %d\n", (uint32_t)freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev)
|
||||
{
|
||||
uint64_t freq = 0;
|
||||
|
||||
switch (EXTRACT(dev->ccm[CCM_CBCMR], PRE_PERIPH_CLK_SEL)) {
|
||||
case 0:
|
||||
freq = imx6_analog_get_pll2_clk(dev);
|
||||
break;
|
||||
case 1:
|
||||
freq = imx6_analog_get_pll2_pfd2_clk(dev);
|
||||
break;
|
||||
case 2:
|
||||
freq = imx6_analog_get_pll2_pfd0_clk(dev);
|
||||
break;
|
||||
case 3:
|
||||
freq = imx6_analog_get_pll2_pfd2_clk(dev) / 2;
|
||||
break;
|
||||
default:
|
||||
/* We should never get there */
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("freq = %d\n", (uint32_t)freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev)
|
||||
{
|
||||
uint64_t freq = 0;
|
||||
|
||||
freq = imx6_analog_get_periph_clk(dev)
|
||||
/ (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF));
|
||||
|
||||
DPRINTF("freq = %d\n", (uint32_t)freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev)
|
||||
{
|
||||
uint64_t freq = 0;
|
||||
|
||||
freq = imx6_ccm_get_ahb_clk(dev)
|
||||
/ (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));;
|
||||
|
||||
DPRINTF("freq = %d\n", (uint32_t)freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev)
|
||||
{
|
||||
uint64_t freq = 0;
|
||||
|
||||
freq = imx6_ccm_get_ipg_clk(dev)
|
||||
/ (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF));
|
||||
|
||||
DPRINTF("freq = %d\n", (uint32_t)freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
|
||||
{
|
||||
uint32_t freq = 0;
|
||||
IMX6CCMState *s = IMX6_CCM(dev);
|
||||
|
||||
switch (clock) {
|
||||
case CLK_NONE:
|
||||
break;
|
||||
case CLK_IPG:
|
||||
freq = imx6_ccm_get_ipg_clk(s);
|
||||
break;
|
||||
case CLK_IPG_HIGH:
|
||||
freq = imx6_ccm_get_per_clk(s);
|
||||
break;
|
||||
case CLK_32k:
|
||||
freq = CKIL_FREQ;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
|
||||
TYPE_IMX6_CCM, __func__, clock);
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("Clock = %d) = %d\n", clock, freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static void imx6_ccm_reset(DeviceState *dev)
|
||||
{
|
||||
IMX6CCMState *s = IMX6_CCM(dev);
|
||||
|
||||
DPRINTF("\n");
|
||||
|
||||
s->ccm[CCM_CCR] = 0x040116FF;
|
||||
s->ccm[CCM_CCDR] = 0x00000000;
|
||||
s->ccm[CCM_CSR] = 0x00000010;
|
||||
s->ccm[CCM_CCSR] = 0x00000100;
|
||||
s->ccm[CCM_CACRR] = 0x00000000;
|
||||
s->ccm[CCM_CBCDR] = 0x00018D40;
|
||||
s->ccm[CCM_CBCMR] = 0x00022324;
|
||||
s->ccm[CCM_CSCMR1] = 0x00F00000;
|
||||
s->ccm[CCM_CSCMR2] = 0x02B92F06;
|
||||
s->ccm[CCM_CSCDR1] = 0x00490B00;
|
||||
s->ccm[CCM_CS1CDR] = 0x0EC102C1;
|
||||
s->ccm[CCM_CS2CDR] = 0x000736C1;
|
||||
s->ccm[CCM_CDCDR] = 0x33F71F92;
|
||||
s->ccm[CCM_CHSCCDR] = 0x0002A150;
|
||||
s->ccm[CCM_CSCDR2] = 0x0002A150;
|
||||
s->ccm[CCM_CSCDR3] = 0x00014841;
|
||||
s->ccm[CCM_CDHIPR] = 0x00000000;
|
||||
s->ccm[CCM_CTOR] = 0x00000000;
|
||||
s->ccm[CCM_CLPCR] = 0x00000079;
|
||||
s->ccm[CCM_CISR] = 0x00000000;
|
||||
s->ccm[CCM_CIMR] = 0xFFFFFFFF;
|
||||
s->ccm[CCM_CCOSR] = 0x000A0001;
|
||||
s->ccm[CCM_CGPR] = 0x0000FE62;
|
||||
s->ccm[CCM_CCGR0] = 0xFFFFFFFF;
|
||||
s->ccm[CCM_CCGR1] = 0xFFFFFFFF;
|
||||
s->ccm[CCM_CCGR2] = 0xFC3FFFFF;
|
||||
s->ccm[CCM_CCGR3] = 0xFFFFFFFF;
|
||||
s->ccm[CCM_CCGR4] = 0xFFFFFFFF;
|
||||
s->ccm[CCM_CCGR5] = 0xFFFFFFFF;
|
||||
s->ccm[CCM_CCGR6] = 0xFFFFFFFF;
|
||||
s->ccm[CCM_CMEOR] = 0xFFFFFFFF;
|
||||
|
||||
s->analog[CCM_ANALOG_PLL_ARM] = 0x00013042;
|
||||
s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000;
|
||||
s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000;
|
||||
s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001;
|
||||
s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000;
|
||||
s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000;
|
||||
s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012;
|
||||
s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006;
|
||||
s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100;
|
||||
s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C;
|
||||
s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C;
|
||||
s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100;
|
||||
s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447;
|
||||
s->analog[CCM_ANALOG_PLL_MLB] = 0x00010000;
|
||||
s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001;
|
||||
s->analog[CCM_ANALOG_PFD_480] = 0x1311100C;
|
||||
s->analog[CCM_ANALOG_PFD_528] = 0x1018101B;
|
||||
|
||||
s->analog[PMU_REG_1P1] = 0x00001073;
|
||||
s->analog[PMU_REG_3P0] = 0x00000F74;
|
||||
s->analog[PMU_REG_2P5] = 0x00005071;
|
||||
s->analog[PMU_REG_CORE] = 0x00402010;
|
||||
s->analog[PMU_MISC0] = 0x04000000;
|
||||
s->analog[PMU_MISC1] = 0x00000000;
|
||||
s->analog[PMU_MISC2] = 0x00272727;
|
||||
|
||||
s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x00000004;
|
||||
s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000;
|
||||
s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000;
|
||||
s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000;
|
||||
s->analog[USB_ANALOG_USB1_MISC] = 0x00000002;
|
||||
s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x00000004;
|
||||
s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000;
|
||||
s->analog[USB_ANALOG_USB2_MISC] = 0x00000002;
|
||||
s->analog[USB_ANALOG_DIGPROG] = 0x00000000;
|
||||
|
||||
/* all PLLs need to be locked */
|
||||
s->analog[CCM_ANALOG_PLL_ARM] |= CCM_ANALOG_PLL_LOCK;
|
||||
s->analog[CCM_ANALOG_PLL_USB1] |= CCM_ANALOG_PLL_LOCK;
|
||||
s->analog[CCM_ANALOG_PLL_USB2] |= CCM_ANALOG_PLL_LOCK;
|
||||
s->analog[CCM_ANALOG_PLL_SYS] |= CCM_ANALOG_PLL_LOCK;
|
||||
s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK;
|
||||
s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK;
|
||||
s->analog[CCM_ANALOG_PLL_MLB] |= CCM_ANALOG_PLL_LOCK;
|
||||
s->analog[CCM_ANALOG_PLL_ENET] |= CCM_ANALOG_PLL_LOCK;
|
||||
}
|
||||
|
||||
static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
uint32_t index = offset >> 2;
|
||||
IMX6CCMState *s = (IMX6CCMState *)opaque;
|
||||
|
||||
value = s->ccm[index];
|
||||
|
||||
DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value);
|
||||
|
||||
return (uint64_t)value;
|
||||
}
|
||||
|
||||
static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
uint32_t index = offset >> 2;
|
||||
IMX6CCMState *s = (IMX6CCMState *)opaque;
|
||||
|
||||
DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index),
|
||||
(uint32_t)value);
|
||||
|
||||
/*
|
||||
* We will do a better implementation later. In particular some bits
|
||||
* cannot be written to.
|
||||
*/
|
||||
s->ccm[index] = (uint32_t)value;
|
||||
}
|
||||
|
||||
static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
uint32_t value;
|
||||
uint32_t index = offset >> 2;
|
||||
IMX6CCMState *s = (IMX6CCMState *)opaque;
|
||||
|
||||
switch (index) {
|
||||
case CCM_ANALOG_PLL_ARM_SET:
|
||||
case CCM_ANALOG_PLL_USB1_SET:
|
||||
case CCM_ANALOG_PLL_USB2_SET:
|
||||
case CCM_ANALOG_PLL_SYS_SET:
|
||||
case CCM_ANALOG_PLL_AUDIO_SET:
|
||||
case CCM_ANALOG_PLL_VIDEO_SET:
|
||||
case CCM_ANALOG_PLL_MLB_SET:
|
||||
case CCM_ANALOG_PLL_ENET_SET:
|
||||
case CCM_ANALOG_PFD_480_SET:
|
||||
case CCM_ANALOG_PFD_528_SET:
|
||||
case CCM_ANALOG_MISC0_SET:
|
||||
case PMU_MISC1_SET:
|
||||
case CCM_ANALOG_MISC2_SET:
|
||||
case USB_ANALOG_USB1_VBUS_DETECT_SET:
|
||||
case USB_ANALOG_USB1_CHRG_DETECT_SET:
|
||||
case USB_ANALOG_USB1_MISC_SET:
|
||||
case USB_ANALOG_USB2_VBUS_DETECT_SET:
|
||||
case USB_ANALOG_USB2_CHRG_DETECT_SET:
|
||||
case USB_ANALOG_USB2_MISC_SET:
|
||||
/*
|
||||
* All REG_NAME_SET register access are in fact targeting the
|
||||
* the REG_NAME register.
|
||||
*/
|
||||
value = s->analog[index - 1];
|
||||
break;
|
||||
case CCM_ANALOG_PLL_ARM_CLR:
|
||||
case CCM_ANALOG_PLL_USB1_CLR:
|
||||
case CCM_ANALOG_PLL_USB2_CLR:
|
||||
case CCM_ANALOG_PLL_SYS_CLR:
|
||||
case CCM_ANALOG_PLL_AUDIO_CLR:
|
||||
case CCM_ANALOG_PLL_VIDEO_CLR:
|
||||
case CCM_ANALOG_PLL_MLB_CLR:
|
||||
case CCM_ANALOG_PLL_ENET_CLR:
|
||||
case CCM_ANALOG_PFD_480_CLR:
|
||||
case CCM_ANALOG_PFD_528_CLR:
|
||||
case CCM_ANALOG_MISC0_CLR:
|
||||
case PMU_MISC1_CLR:
|
||||
case CCM_ANALOG_MISC2_CLR:
|
||||
case USB_ANALOG_USB1_VBUS_DETECT_CLR:
|
||||
case USB_ANALOG_USB1_CHRG_DETECT_CLR:
|
||||
case USB_ANALOG_USB1_MISC_CLR:
|
||||
case USB_ANALOG_USB2_VBUS_DETECT_CLR:
|
||||
case USB_ANALOG_USB2_CHRG_DETECT_CLR:
|
||||
case USB_ANALOG_USB2_MISC_CLR:
|
||||
/*
|
||||
* All REG_NAME_CLR register access are in fact targeting the
|
||||
* the REG_NAME register.
|
||||
*/
|
||||
value = s->analog[index - 2];
|
||||
break;
|
||||
case CCM_ANALOG_PLL_ARM_TOG:
|
||||
case CCM_ANALOG_PLL_USB1_TOG:
|
||||
case CCM_ANALOG_PLL_USB2_TOG:
|
||||
case CCM_ANALOG_PLL_SYS_TOG:
|
||||
case CCM_ANALOG_PLL_AUDIO_TOG:
|
||||
case CCM_ANALOG_PLL_VIDEO_TOG:
|
||||
case CCM_ANALOG_PLL_MLB_TOG:
|
||||
case CCM_ANALOG_PLL_ENET_TOG:
|
||||
case CCM_ANALOG_PFD_480_TOG:
|
||||
case CCM_ANALOG_PFD_528_TOG:
|
||||
case CCM_ANALOG_MISC0_TOG:
|
||||
case PMU_MISC1_TOG:
|
||||
case CCM_ANALOG_MISC2_TOG:
|
||||
case USB_ANALOG_USB1_VBUS_DETECT_TOG:
|
||||
case USB_ANALOG_USB1_CHRG_DETECT_TOG:
|
||||
case USB_ANALOG_USB1_MISC_TOG:
|
||||
case USB_ANALOG_USB2_VBUS_DETECT_TOG:
|
||||
case USB_ANALOG_USB2_CHRG_DETECT_TOG:
|
||||
case USB_ANALOG_USB2_MISC_TOG:
|
||||
/*
|
||||
* All REG_NAME_TOG register access are in fact targeting the
|
||||
* the REG_NAME register.
|
||||
*/
|
||||
value = s->analog[index - 3];
|
||||
break;
|
||||
default:
|
||||
value = s->analog[index];
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value);
|
||||
|
||||
return (uint64_t)value;
|
||||
}
|
||||
|
||||
static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
uint32_t index = offset >> 2;
|
||||
IMX6CCMState *s = (IMX6CCMState *)opaque;
|
||||
|
||||
DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index),
|
||||
(uint32_t)value);
|
||||
|
||||
switch (index) {
|
||||
case CCM_ANALOG_PLL_ARM_SET:
|
||||
case CCM_ANALOG_PLL_USB1_SET:
|
||||
case CCM_ANALOG_PLL_USB2_SET:
|
||||
case CCM_ANALOG_PLL_SYS_SET:
|
||||
case CCM_ANALOG_PLL_AUDIO_SET:
|
||||
case CCM_ANALOG_PLL_VIDEO_SET:
|
||||
case CCM_ANALOG_PLL_MLB_SET:
|
||||
case CCM_ANALOG_PLL_ENET_SET:
|
||||
case CCM_ANALOG_PFD_480_SET:
|
||||
case CCM_ANALOG_PFD_528_SET:
|
||||
case CCM_ANALOG_MISC0_SET:
|
||||
case PMU_MISC1_SET:
|
||||
case CCM_ANALOG_MISC2_SET:
|
||||
case USB_ANALOG_USB1_VBUS_DETECT_SET:
|
||||
case USB_ANALOG_USB1_CHRG_DETECT_SET:
|
||||
case USB_ANALOG_USB1_MISC_SET:
|
||||
case USB_ANALOG_USB2_VBUS_DETECT_SET:
|
||||
case USB_ANALOG_USB2_CHRG_DETECT_SET:
|
||||
case USB_ANALOG_USB2_MISC_SET:
|
||||
/*
|
||||
* All REG_NAME_SET register access are in fact targeting the
|
||||
* the REG_NAME register. So we change the value of the
|
||||
* REG_NAME register, setting bits passed in the value.
|
||||
*/
|
||||
s->analog[index - 1] |= value;
|
||||
break;
|
||||
case CCM_ANALOG_PLL_ARM_CLR:
|
||||
case CCM_ANALOG_PLL_USB1_CLR:
|
||||
case CCM_ANALOG_PLL_USB2_CLR:
|
||||
case CCM_ANALOG_PLL_SYS_CLR:
|
||||
case CCM_ANALOG_PLL_AUDIO_CLR:
|
||||
case CCM_ANALOG_PLL_VIDEO_CLR:
|
||||
case CCM_ANALOG_PLL_MLB_CLR:
|
||||
case CCM_ANALOG_PLL_ENET_CLR:
|
||||
case CCM_ANALOG_PFD_480_CLR:
|
||||
case CCM_ANALOG_PFD_528_CLR:
|
||||
case CCM_ANALOG_MISC0_CLR:
|
||||
case PMU_MISC1_CLR:
|
||||
case CCM_ANALOG_MISC2_CLR:
|
||||
case USB_ANALOG_USB1_VBUS_DETECT_CLR:
|
||||
case USB_ANALOG_USB1_CHRG_DETECT_CLR:
|
||||
case USB_ANALOG_USB1_MISC_CLR:
|
||||
case USB_ANALOG_USB2_VBUS_DETECT_CLR:
|
||||
case USB_ANALOG_USB2_CHRG_DETECT_CLR:
|
||||
case USB_ANALOG_USB2_MISC_CLR:
|
||||
/*
|
||||
* All REG_NAME_CLR register access are in fact targeting the
|
||||
* the REG_NAME register. So we change the value of the
|
||||
* REG_NAME register, unsetting bits passed in the value.
|
||||
*/
|
||||
s->analog[index - 2] &= ~value;
|
||||
break;
|
||||
case CCM_ANALOG_PLL_ARM_TOG:
|
||||
case CCM_ANALOG_PLL_USB1_TOG:
|
||||
case CCM_ANALOG_PLL_USB2_TOG:
|
||||
case CCM_ANALOG_PLL_SYS_TOG:
|
||||
case CCM_ANALOG_PLL_AUDIO_TOG:
|
||||
case CCM_ANALOG_PLL_VIDEO_TOG:
|
||||
case CCM_ANALOG_PLL_MLB_TOG:
|
||||
case CCM_ANALOG_PLL_ENET_TOG:
|
||||
case CCM_ANALOG_PFD_480_TOG:
|
||||
case CCM_ANALOG_PFD_528_TOG:
|
||||
case CCM_ANALOG_MISC0_TOG:
|
||||
case PMU_MISC1_TOG:
|
||||
case CCM_ANALOG_MISC2_TOG:
|
||||
case USB_ANALOG_USB1_VBUS_DETECT_TOG:
|
||||
case USB_ANALOG_USB1_CHRG_DETECT_TOG:
|
||||
case USB_ANALOG_USB1_MISC_TOG:
|
||||
case USB_ANALOG_USB2_VBUS_DETECT_TOG:
|
||||
case USB_ANALOG_USB2_CHRG_DETECT_TOG:
|
||||
case USB_ANALOG_USB2_MISC_TOG:
|
||||
/*
|
||||
* All REG_NAME_TOG register access are in fact targeting the
|
||||
* the REG_NAME register. So we change the value of the
|
||||
* REG_NAME register, toggling bits passed in the value.
|
||||
*/
|
||||
s->analog[index - 3] ^= value;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* We will do a better implementation later. In particular some bits
|
||||
* cannot be written to.
|
||||
*/
|
||||
s->analog[index] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct MemoryRegionOps imx6_ccm_ops = {
|
||||
.read = imx6_ccm_read,
|
||||
.write = imx6_ccm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
/*
|
||||
* Our device would not work correctly if the guest was doing
|
||||
* unaligned access. This might not be a limitation on the real
|
||||
* device but in practice there is no reason for a guest to access
|
||||
* this device unaligned.
|
||||
*/
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
.unaligned = false,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct MemoryRegionOps imx6_analog_ops = {
|
||||
.read = imx6_analog_read,
|
||||
.write = imx6_analog_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
/*
|
||||
* Our device would not work correctly if the guest was doing
|
||||
* unaligned access. This might not be a limitation on the real
|
||||
* device but in practice there is no reason for a guest to access
|
||||
* this device unaligned.
|
||||
*/
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
.unaligned = false,
|
||||
},
|
||||
};
|
||||
|
||||
static void imx6_ccm_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
|
||||
IMX6CCMState *s = IMX6_CCM(obj);
|
||||
|
||||
/* initialize a container for the all memory range */
|
||||
memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6_CCM, 0x5000);
|
||||
|
||||
/* We initialize an IO memory region for the CCM part */
|
||||
memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6_ccm_ops, s,
|
||||
TYPE_IMX6_CCM ".ccm", CCM_MAX * sizeof(uint32_t));
|
||||
|
||||
/* Add the CCM as a subregion at offset 0 */
|
||||
memory_region_add_subregion(&s->container, 0, &s->ioccm);
|
||||
|
||||
/* We initialize an IO memory region for the ANALOG part */
|
||||
memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6_analog_ops, s,
|
||||
TYPE_IMX6_CCM ".analog",
|
||||
CCM_ANALOG_MAX * sizeof(uint32_t));
|
||||
|
||||
/* Add the ANALOG as a subregion at offset 0x4000 */
|
||||
memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog);
|
||||
|
||||
sysbus_init_mmio(sd, &s->container);
|
||||
}
|
||||
|
||||
static void imx6_ccm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
|
||||
|
||||
dc->reset = imx6_ccm_reset;
|
||||
dc->vmsd = &vmstate_imx6_ccm;
|
||||
dc->desc = "i.MX6 Clock Control Module";
|
||||
|
||||
ccm->get_clock_frequency = imx6_ccm_get_clock_frequency;
|
||||
}
|
||||
|
||||
static const TypeInfo imx6_ccm_info = {
|
||||
.name = TYPE_IMX6_CCM,
|
||||
.parent = TYPE_IMX_CCM,
|
||||
.instance_size = sizeof(IMX6CCMState),
|
||||
.instance_init = imx6_ccm_init,
|
||||
.class_init = imx6_ccm_class_init,
|
||||
};
|
||||
|
||||
static void imx6_ccm_register_types(void)
|
||||
{
|
||||
type_register_static(&imx6_ccm_info);
|
||||
}
|
||||
|
||||
type_init(imx6_ccm_register_types)
|
||||
@@ -400,7 +400,7 @@ static int create_shared_memory_BAR(IVShmemState *s, int fd, uint8_t attr,
|
||||
|
||||
memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2",
|
||||
s->ivshmem_size, ptr);
|
||||
qemu_set_ram_fd(s->ivshmem.ram_addr, fd);
|
||||
qemu_set_ram_fd(memory_region_get_ram_addr(&s->ivshmem), fd);
|
||||
vmstate_register_ram(&s->ivshmem, DEVICE(s));
|
||||
memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
|
||||
|
||||
@@ -661,7 +661,8 @@ static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
|
||||
}
|
||||
memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s),
|
||||
"ivshmem.bar2", s->ivshmem_size, map_ptr);
|
||||
qemu_set_ram_fd(s->ivshmem.ram_addr, incoming_fd);
|
||||
qemu_set_ram_fd(memory_region_get_ram_addr(&s->ivshmem),
|
||||
incoming_fd);
|
||||
vmstate_register_ram(&s->ivshmem, DEVICE(s));
|
||||
|
||||
IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n",
|
||||
@@ -996,8 +997,10 @@ static void pci_ivshmem_exit(PCIDevice *dev)
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if ((fd = qemu_get_ram_fd(s->ivshmem.ram_addr)) != -1)
|
||||
fd = qemu_get_ram_fd(memory_region_get_ram_addr(&s->ivshmem));
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
|
||||
|
||||
@@ -693,6 +693,7 @@ static void imx_fec_class_init(ObjectClass *klass, void *data)
|
||||
dc->reset = imx_fec_reset;
|
||||
dc->props = imx_fec_properties;
|
||||
dc->realize = imx_fec_realize;
|
||||
dc->desc = "i.MX FEC Ethernet Controller";
|
||||
}
|
||||
|
||||
static const TypeInfo imx_fec_info = {
|
||||
|
||||
@@ -155,6 +155,10 @@ static int ne2000_buffer_full(NE2000State *s)
|
||||
{
|
||||
int avail, index, boundary;
|
||||
|
||||
if (s->stop <= s->start) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
index = s->curpag << 8;
|
||||
boundary = s->boundary << 8;
|
||||
if (index < boundary)
|
||||
|
||||
@@ -43,6 +43,7 @@ struct rocker {
|
||||
|
||||
/* switch configuration */
|
||||
char *name; /* switch name */
|
||||
char *world_name; /* world name */
|
||||
uint32_t fp_ports; /* front-panel port count */
|
||||
NICPeers *fp_ports_peers;
|
||||
MACAddr fp_start_macaddr; /* front-panel port 0 mac addr */
|
||||
@@ -400,7 +401,13 @@ static int cmd_set_port_settings(Rocker *r,
|
||||
|
||||
if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]) {
|
||||
mode = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]);
|
||||
fp_port_set_world(fp_port, r->worlds[mode]);
|
||||
if (mode >= ROCKER_WORLD_TYPE_MAX) {
|
||||
return -ROCKER_EINVAL;
|
||||
}
|
||||
/* We don't support world change. */
|
||||
if (!fp_port_check_world(fp_port, r->worlds[mode])) {
|
||||
return -ROCKER_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]) {
|
||||
@@ -1280,6 +1287,18 @@ static void rocker_msix_uninit(Rocker *r)
|
||||
rocker_msix_vectors_unuse(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
|
||||
}
|
||||
|
||||
static World *rocker_world_type_by_name(Rocker *r, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
|
||||
if (strcmp(name, world_name(r->worlds[i])) == 0) {
|
||||
return r->worlds[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int pci_rocker_init(PCIDevice *dev)
|
||||
{
|
||||
Rocker *r = to_rocker(dev);
|
||||
@@ -1291,14 +1310,27 @@ static int pci_rocker_init(PCIDevice *dev)
|
||||
/* allocate worlds */
|
||||
|
||||
r->worlds[ROCKER_WORLD_TYPE_OF_DPA] = of_dpa_world_alloc(r);
|
||||
r->world_dflt = r->worlds[ROCKER_WORLD_TYPE_OF_DPA];
|
||||
|
||||
for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
|
||||
if (!r->worlds[i]) {
|
||||
err = -ENOMEM;
|
||||
goto err_world_alloc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!r->world_name) {
|
||||
r->world_name = g_strdup(world_name(r->worlds[ROCKER_WORLD_TYPE_OF_DPA]));
|
||||
}
|
||||
|
||||
r->world_dflt = rocker_world_type_by_name(r, r->world_name);
|
||||
if (!r->world_dflt) {
|
||||
fprintf(stderr,
|
||||
"rocker: requested world \"%s\" does not exist\n",
|
||||
r->world_name);
|
||||
err = -EINVAL;
|
||||
goto err_world_type_by_name;
|
||||
}
|
||||
|
||||
/* set up memory-mapped region at BAR0 */
|
||||
|
||||
memory_region_init_io(&r->mmio, OBJECT(r), &rocker_mmio_ops, r,
|
||||
@@ -1432,6 +1464,7 @@ err_duplicate:
|
||||
err_msix_init:
|
||||
object_unparent(OBJECT(&r->msix_bar));
|
||||
object_unparent(OBJECT(&r->mmio));
|
||||
err_world_type_by_name:
|
||||
err_world_alloc:
|
||||
for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
|
||||
if (r->worlds[i]) {
|
||||
@@ -1503,6 +1536,7 @@ static void rocker_reset(DeviceState *dev)
|
||||
|
||||
static Property rocker_properties[] = {
|
||||
DEFINE_PROP_STRING("name", Rocker, name),
|
||||
DEFINE_PROP_STRING("world", Rocker, world_name),
|
||||
DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker,
|
||||
fp_start_macaddr),
|
||||
DEFINE_PROP_UINT64("switch_id", Rocker,
|
||||
|
||||
@@ -186,6 +186,11 @@ void fp_port_set_world(FpPort *port, World *world)
|
||||
port->world = world;
|
||||
}
|
||||
|
||||
bool fp_port_check_world(FpPort *port, World *world)
|
||||
{
|
||||
return port->world == world;
|
||||
}
|
||||
|
||||
bool fp_port_enabled(FpPort *port)
|
||||
{
|
||||
return port->enabled;
|
||||
|
||||
@@ -40,6 +40,7 @@ int fp_port_set_settings(FpPort *port, uint32_t speed,
|
||||
bool fp_port_from_pport(uint32_t pport, uint32_t *port);
|
||||
World *fp_port_get_world(FpPort *port);
|
||||
void fp_port_set_world(FpPort *port, World *world);
|
||||
bool fp_port_check_world(FpPort *port, World *world);
|
||||
bool fp_port_enabled(FpPort *port);
|
||||
void fp_port_enable(FpPort *port);
|
||||
void fp_port_disable(FpPort *port);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user