Compare commits
333 Commits
v2.9.0-rc4
...
pull-input
Author | SHA1 | Date | |
---|---|---|---|
|
2222e0a633 | ||
|
05c6638b20 | ||
|
fa18f36a46 | ||
|
e619b14746 | ||
|
eb5d4f5329 | ||
|
14650df402 | ||
|
7eddf37c63 | ||
|
2a7cab9e17 | ||
|
e3ec38ffd6 | ||
|
7d1724976f | ||
|
17eb587aeb | ||
|
f95cc8b6cc | ||
|
0bed71edbc | ||
|
38bb54f323 | ||
|
64a6047d54 | ||
|
7ad691ec98 | ||
|
53c58e64d0 | ||
|
161a56a906 | ||
|
1dbfbc17fe | ||
|
0a3d197a71 | ||
|
1529605337 | ||
|
94d81ae896 | ||
|
54858553de | ||
|
f342cc93ec | ||
|
81b2d5ceb0 | ||
|
79b1af9062 | ||
|
51b9d495f2 | ||
|
41c7c7ef29 | ||
|
78f86a2b7c | ||
|
02ef6e878f | ||
|
6bb87be893 | ||
|
b7da97eef7 | ||
|
e9628441df | ||
|
dcaed66cbe | ||
|
52e94ea5de | ||
|
df02d2ca8b | ||
|
c364946dd5 | ||
|
b1c897d587 | ||
|
31b2b0f846 | ||
|
8f37e54e5b | ||
|
bce0b69159 | ||
|
aeaef83dab | ||
|
04f6c8b2c0 | ||
|
cb228f5a00 | ||
|
c5ffdcaea5 | ||
|
e7a3fee340 | ||
|
5a7e14a274 | ||
|
4d1df88b63 | ||
|
71cd4dace9 | ||
|
b88290cd9e | ||
|
cd60d85ef6 | ||
|
7fabcdb942 | ||
|
540c07d345 | ||
|
8c6fd7f341 | ||
|
4a44fd26db | ||
|
0722d05ad8 | ||
|
54f59d786c | ||
|
bf5615e77c | ||
|
5509db4aec | ||
|
960fbd29e5 | ||
|
47fea43aa3 | ||
|
36fc6f0800 | ||
|
99285aae16 | ||
|
439071a92d | ||
|
5bc8d26de2 | ||
|
06747ba6d4 | ||
|
ad5d1add86 | ||
|
ccd531b9c9 | ||
|
c88fa6dd4a | ||
|
a01f3432dd | ||
|
3dc410ae83 | ||
|
e957f6a9b9 | ||
|
9fb4541f58 | ||
|
86d5771a5a | ||
|
b4db54132f | ||
|
d77a98b015 | ||
|
cf1c4cce7c | ||
|
c64abd1f9c | ||
|
d6ee2a7c85 | ||
|
9d169fb3c8 | ||
|
147ff8079e | ||
|
3fa14fbe13 | ||
|
f3d9f303ac | ||
|
28b99f473b | ||
|
93d43e7e11 | ||
|
56e2cd2452 | ||
|
d6a3f64ad3 | ||
|
e737b6d5c3 | ||
|
4476e09e34 | ||
|
40a2389207 | ||
|
47b70fb1e4 | ||
|
f23ef34a5d | ||
|
b37eeb0201 | ||
|
fe491fa85c | ||
|
b8c7193fe9 | ||
|
2cf9953bee | ||
|
601b9a9008 | ||
|
4ba967ad74 | ||
|
0fc8aec7de | ||
|
184d4d4203 | ||
|
47bb83cad4 | ||
|
ea337c6549 | ||
|
1335fe3eb2 | ||
|
d77b71c2a4 | ||
|
f4b5b021c8 | ||
|
ecfa185400 | ||
|
56e7cf8df0 | ||
|
80b61a27c6 | ||
|
3d8ce171cb | ||
|
45803a0396 | ||
|
93ed524e3d | ||
|
d6fcdf06d9 | ||
|
e2b8247a32 | ||
|
fe5241bfe3 | ||
|
a98f49f46a | ||
|
ae0c0a3dec | ||
|
da92c3ff60 | ||
|
eab1e53cac | ||
|
4c55b1d0ba | ||
|
9eb2575e6c | ||
|
2edd6e4ac5 | ||
|
b612a49db2 | ||
|
1ae5e6eb42 | ||
|
01d2d584c9 | ||
|
6a2a5aae02 | ||
|
afef2e1d53 | ||
|
efae27848d | ||
|
c795fa8447 | ||
|
ca8a110470 | ||
|
70e46ca887 | ||
|
e2ee84760e | ||
|
64f1603b07 | ||
|
d526e5d874 | ||
|
cd1ea50895 | ||
|
729abb6a92 | ||
|
6f663d7be9 | ||
|
104bd1dc70 | ||
|
7fcf0c24e7 | ||
|
553bcce5ac | ||
|
167e9c7982 | ||
|
fec5e8c92b | ||
|
f3289f6f0f | ||
|
8deaf12ca1 | ||
|
d6eb141392 | ||
|
a27450ec04 | ||
|
e0665c3b5f | ||
|
6905b93447 | ||
|
021c9d25b3 | ||
|
4462a5307b | ||
|
bbfb89e38c | ||
|
bf46e67ddb | ||
|
c0ca74f6f3 | ||
|
cb8d4bf677 | ||
|
78bbd910bb | ||
|
51ccfa2dbf | ||
|
375092332e | ||
|
9217283dc8 | ||
|
1a9a7f25a0 | ||
|
2bdc6791b9 | ||
|
6dffc1f670 | ||
|
226799cec5 | ||
|
536eeea869 | ||
|
bd44300d1a | ||
|
30adcc8fab | ||
|
d25a7dabf2 | ||
|
e0a31457e1 | ||
|
c9fb47e7d0 | ||
|
f65eadb639 | ||
|
c1cdd9d5be | ||
|
14d015b6fc | ||
|
1c599472b0 | ||
|
f1167ee684 | ||
|
d655f34e6d | ||
|
da8090ccb7 | ||
|
32c7e0ab75 | ||
|
af7ec403b2 | ||
|
09fc586db3 | ||
|
b4c963fa82 | ||
|
bfec359afb | ||
|
2c02468c9b | ||
|
d80a0169e3 | ||
|
faec066ab8 | ||
|
343001f68d | ||
|
a23a6d1839 | ||
|
e8199e4895 | ||
|
66103a5796 | ||
|
b06424de62 | ||
|
329006799f | ||
|
21def24a5a | ||
|
9bed84c191 | ||
|
fab3500526 | ||
|
352b0de982 | ||
|
b8c4899398 | ||
|
f20e286516 | ||
|
a935e30fbb | ||
|
269ace2951 | ||
|
06b106889a | ||
|
2479569466 | ||
|
aaa2064c2a | ||
|
15440dd5a0 | ||
|
030ce1f861 | ||
|
20afaed98b | ||
|
a0a8aa147a | ||
|
5727309d25 | ||
|
6d358d9494 | ||
|
ce25d33781 | ||
|
204b88b869 | ||
|
96506894a3 | ||
|
47ad861976 | ||
|
abbf1d7f9b | ||
|
42d219d3b0 | ||
|
ec481c6c57 | ||
|
68a098f386 | ||
|
9edabd4de6 | ||
|
072c251157 | ||
|
2f4fde9352 | ||
|
eb859c53dd | ||
|
108cfae019 | ||
|
ceb4d16898 | ||
|
0d8ec885ed | ||
|
180f61f75a | ||
|
b07016b674 | ||
|
544c36f188 | ||
|
f36ada95de | ||
|
07ed50a2bb | ||
|
23b28c3c62 | ||
|
29cc3d8a9b | ||
|
b4d1c6e722 | ||
|
bedf53c14c | ||
|
5bb1272c38 | ||
|
f7ccd61b4c | ||
|
36040d9cb2 | ||
|
b5833fde40 | ||
|
68908ed665 | ||
|
a66cd90c74 | ||
|
c4bdf0cf4b | ||
|
eac7415958 | ||
|
f664da80fc | ||
|
5a98773896 | ||
|
8d820d6f67 | ||
|
6f37bb8bf3 | ||
|
3644915726 | ||
|
5e58f968f4 | ||
|
3d0684b2ad | ||
|
659370f71f | ||
|
3d1baccb08 | ||
|
c53eeaf75a | ||
|
3ccc0a0163 | ||
|
205f8618be | ||
|
3928d50bf1 | ||
|
d72915c60b | ||
|
ab08aec45f | ||
|
01f9cfab8b | ||
|
7cd37925a1 | ||
|
7497638642 | ||
|
973945804d | ||
|
ee72bed08c | ||
|
d18e101225 | ||
|
0a97c6c4f9 | ||
|
66dcabea47 | ||
|
36180430ac | ||
|
427ee02bc9 | ||
|
4b865c2809 | ||
|
9800b3c20e | ||
|
8c95e1f20c | ||
|
cb55c19a26 | ||
|
dde522bbc5 | ||
|
bc66d6cbca | ||
|
c572d3f313 | ||
|
5b00bef270 | ||
|
2a78ac660f | ||
|
d8d98db5f0 | ||
|
c35fc6aa18 | ||
|
229913f0ef | ||
|
08564ecd4d | ||
|
10890873ca | ||
|
be4221d993 | ||
|
66e2f304a3 | ||
|
08f00df4f4 | ||
|
d28fca153b | ||
|
40fda982f2 | ||
|
be9721f400 | ||
|
606fd0e206 | ||
|
6f4c60e47f | ||
|
4728b57410 | ||
|
991db24774 | ||
|
36cccb8c57 | ||
|
dd4d607e40 | ||
|
558e0024a4 | ||
|
f06a696dc9 | ||
|
10315b9b28 | ||
|
faa362e3cc | ||
|
bd2bfa4c52 | ||
|
de472e4a92 | ||
|
512fa40867 | ||
|
698feb5e13 | ||
|
fa54abb8c2 | ||
|
da92ada855 | ||
|
f4e8e4edda | ||
|
3bb8a96f53 | ||
|
064c379c99 | ||
|
b636649f5a | ||
|
f021b2c462 | ||
|
4d5e8c969a | ||
|
5425415ebb | ||
|
bedb8a6b09 | ||
|
9d7c59c84d | ||
|
20bff21307 | ||
|
a5517666b2 | ||
|
596b6f51b7 | ||
|
dacc0566ac | ||
|
75b7760212 | ||
|
0493a139c9 | ||
|
dffc58519e | ||
|
df3692e04b | ||
|
65ed2ed90d | ||
|
2c4a7cc5af | ||
|
32b81e620e | ||
|
885f271056 | ||
|
75c6d92e4c | ||
|
f2ad5140fa | ||
|
68115ed5fc | ||
|
53d6e53189 | ||
|
64c8ed97cc | ||
|
359c41abe3 | ||
|
ca55019dac | ||
|
d1263f8f18 | ||
|
91af091f92 | ||
|
178bd438af | ||
|
9c6b899f7a | ||
|
8f25e75441 | ||
|
5100afb5f5 | ||
|
260cabed71 |
@@ -327,6 +327,7 @@ L: xen-devel@lists.xenproject.org
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: xen-*
|
F: xen-*
|
||||||
F: */xen*
|
F: */xen*
|
||||||
|
F: hw/9pfs/xen-9p-backend.c
|
||||||
F: hw/char/xen_console.c
|
F: hw/char/xen_console.c
|
||||||
F: hw/display/xenfb.c
|
F: hw/display/xenfb.c
|
||||||
F: hw/net/xen_nic.c
|
F: hw/net/xen_nic.c
|
||||||
@@ -645,7 +646,6 @@ F: hw/ppc/ppc440_bamboo.c
|
|||||||
|
|
||||||
e500
|
e500
|
||||||
M: Alexander Graf <agraf@suse.de>
|
M: Alexander Graf <agraf@suse.de>
|
||||||
M: Scott Wood <scottwood@freescale.com>
|
|
||||||
L: qemu-ppc@nongnu.org
|
L: qemu-ppc@nongnu.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/ppc/e500.[hc]
|
F: hw/ppc/e500.[hc]
|
||||||
@@ -656,7 +656,6 @@ F: pc-bios/u-boot.e500
|
|||||||
|
|
||||||
mpc8544ds
|
mpc8544ds
|
||||||
M: Alexander Graf <agraf@suse.de>
|
M: Alexander Graf <agraf@suse.de>
|
||||||
M: Scott Wood <scottwood@freescale.com>
|
|
||||||
L: qemu-ppc@nongnu.org
|
L: qemu-ppc@nongnu.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/ppc/mpc8544ds.c
|
F: hw/ppc/mpc8544ds.c
|
||||||
@@ -933,7 +932,6 @@ F: include/hw/ppc/ppc4xx.h
|
|||||||
|
|
||||||
ppce500
|
ppce500
|
||||||
M: Alexander Graf <agraf@suse.de>
|
M: Alexander Graf <agraf@suse.de>
|
||||||
M: Scott Wood <scottwood@freescale.com>
|
|
||||||
L: qemu-ppc@nongnu.org
|
L: qemu-ppc@nongnu.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/ppc/e500*
|
F: hw/ppc/e500*
|
||||||
@@ -1817,8 +1815,8 @@ S: Supported
|
|||||||
F: tests/image-fuzzer/
|
F: tests/image-fuzzer/
|
||||||
|
|
||||||
Replication
|
Replication
|
||||||
M: Wen Congyang <wency@cn.fujitsu.com>
|
M: Wen Congyang <wencongyang2@huawei.com>
|
||||||
M: Changlong Xie <xiecl.fnst@cn.fujitsu.com>
|
M: Xie Changlong <xiechanglong.d@gmail.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: replication*
|
F: replication*
|
||||||
F: block/replication.c
|
F: block/replication.c
|
||||||
|
6
Makefile
6
Makefile
@@ -346,7 +346,7 @@ dtc/%:
|
|||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(chardev-obj-y) \
|
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(chardev-obj-y) \
|
||||||
$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) $(trace-obj-y)
|
$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
|
||||||
|
|
||||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||||
# Only keep -O and -g cflags
|
# Only keep -O and -g cflags
|
||||||
@@ -366,11 +366,11 @@ Makefile: $(version-obj-y)
|
|||||||
# Build libraries
|
# Build libraries
|
||||||
|
|
||||||
libqemustub.a: $(stub-obj-y)
|
libqemustub.a: $(stub-obj-y)
|
||||||
libqemuutil.a: $(util-obj-y)
|
libqemuutil.a: $(util-obj-y) $(trace-obj-y)
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
COMMON_LDADDS = $(trace-obj-y) libqemuutil.a libqemustub.a
|
COMMON_LDADDS = libqemuutil.a libqemustub.a
|
||||||
|
|
||||||
qemu-img.o: qemu-img-cmds.h
|
qemu-img.o: qemu-img-cmds.h
|
||||||
|
|
||||||
|
@@ -149,12 +149,6 @@ obj-y += dump.o
|
|||||||
obj-y += migration/ram.o migration/savevm.o
|
obj-y += migration/ram.o migration/savevm.o
|
||||||
LIBS := $(libs_softmmu) $(LIBS)
|
LIBS := $(libs_softmmu) $(LIBS)
|
||||||
|
|
||||||
# xen support
|
|
||||||
obj-$(CONFIG_XEN) += xen-common.o
|
|
||||||
obj-$(CONFIG_XEN_I386) += xen-hvm.o xen-mapcache.o
|
|
||||||
obj-$(call lnot,$(CONFIG_XEN)) += xen-common-stub.o
|
|
||||||
obj-$(call lnot,$(CONFIG_XEN_I386)) += xen-hvm-stub.o
|
|
||||||
|
|
||||||
# Hardware support
|
# Hardware support
|
||||||
ifeq ($(TARGET_NAME), sparc64)
|
ifeq ($(TARGET_NAME), sparc64)
|
||||||
obj-y += hw/sparc64/
|
obj-y += hw/sparc64/
|
||||||
@@ -188,8 +182,7 @@ dummy := $(call unnest-vars,.., \
|
|||||||
qom-obj-y \
|
qom-obj-y \
|
||||||
io-obj-y \
|
io-obj-y \
|
||||||
common-obj-y \
|
common-obj-y \
|
||||||
common-obj-m \
|
common-obj-m)
|
||||||
trace-obj-y)
|
|
||||||
target-obj-y := $(target-obj-y-save)
|
target-obj-y := $(target-obj-y-save)
|
||||||
all-obj-y += $(common-obj-y)
|
all-obj-y += $(common-obj-y)
|
||||||
all-obj-y += $(target-obj-y)
|
all-obj-y += $(target-obj-y)
|
||||||
@@ -201,7 +194,7 @@ all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
|||||||
|
|
||||||
$(QEMU_PROG_BUILD): config-devices.mak
|
$(QEMU_PROG_BUILD): config-devices.mak
|
||||||
|
|
||||||
COMMON_LDADDS = $(trace-obj-y) ../libqemuutil.a ../libqemustub.a
|
COMMON_LDADDS = ../libqemuutil.a ../libqemustub.a
|
||||||
|
|
||||||
# build either PROG or PROGW
|
# build either PROG or PROGW
|
||||||
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
|
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
|
||||||
|
@@ -51,7 +51,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
|||||||
#ifndef CONFIG_LINUX
|
#ifndef CONFIG_LINUX
|
||||||
error_setg(errp, "-mem-path not supported on this host");
|
error_setg(errp, "-mem-path not supported on this host");
|
||||||
#else
|
#else
|
||||||
if (!memory_region_size(&backend->mr)) {
|
if (!host_memory_backend_mr_inited(backend)) {
|
||||||
gchar *path;
|
gchar *path;
|
||||||
backend->force_prealloc = mem_prealloc;
|
backend->force_prealloc = mem_prealloc;
|
||||||
path = object_get_canonical_path(OBJECT(backend));
|
path = object_get_canonical_path(OBJECT(backend));
|
||||||
@@ -76,7 +76,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
|
|||||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||||
|
|
||||||
if (memory_region_size(&backend->mr)) {
|
if (host_memory_backend_mr_inited(backend)) {
|
||||||
error_setg(errp, "cannot change property value");
|
error_setg(errp, "cannot change property value");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ static void file_memory_backend_set_share(Object *o, bool value, Error **errp)
|
|||||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||||
|
|
||||||
if (memory_region_size(&backend->mr)) {
|
if (host_memory_backend_mr_inited(backend)) {
|
||||||
error_setg(errp, "cannot change property value");
|
error_setg(errp, "cannot change property value");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
|
|
||||||
if (memory_region_size(&backend->mr)) {
|
if (host_memory_backend_mr_inited(backend)) {
|
||||||
error_setg(&local_err, "cannot change property value");
|
error_setg(&local_err, "cannot change property value");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp)
|
|||||||
{
|
{
|
||||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||||
|
|
||||||
if (!memory_region_size(&backend->mr)) {
|
if (!host_memory_backend_mr_inited(backend)) {
|
||||||
backend->merge = value;
|
backend->merge = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp)
|
|||||||
{
|
{
|
||||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||||
|
|
||||||
if (!memory_region_size(&backend->mr)) {
|
if (!host_memory_backend_mr_inited(backend)) {
|
||||||
backend->dump = value;
|
backend->dump = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memory_region_size(&backend->mr)) {
|
if (!host_memory_backend_mr_inited(backend)) {
|
||||||
backend->prealloc = value;
|
backend->prealloc = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -237,10 +237,19 @@ static void host_memory_backend_init(Object *obj)
|
|||||||
backend->prealloc = mem_prealloc;
|
backend->prealloc = mem_prealloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: We forbid zero-length memory backend, so here zero means
|
||||||
|
* "we haven't inited the backend memory region yet".
|
||||||
|
*/
|
||||||
|
return memory_region_size(&backend->mr) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
MemoryRegion *
|
MemoryRegion *
|
||||||
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
|
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
|
||||||
{
|
{
|
||||||
return memory_region_size(&backend->mr) ? &backend->mr : NULL;
|
return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped)
|
void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped)
|
||||||
|
60
block.c
60
block.c
@@ -192,6 +192,43 @@ void path_combine(char *dest, int dest_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool bdrv_is_read_only(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
return bs->read_only;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
|
||||||
|
{
|
||||||
|
/* Do not set read_only if copy_on_read is enabled */
|
||||||
|
if (bs->copy_on_read && read_only) {
|
||||||
|
error_setg(errp, "Can't set node '%s' to r/o with copy-on-read enabled",
|
||||||
|
bdrv_get_device_or_node_name(bs));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do not clear read_only if it is prohibited */
|
||||||
|
if (!read_only && !(bs->open_flags & BDRV_O_ALLOW_RDWR)) {
|
||||||
|
error_setg(errp, "Node '%s' is read only",
|
||||||
|
bdrv_get_device_or_node_name(bs));
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = bdrv_can_set_read_only(bs, read_only, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bs->read_only = read_only;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void bdrv_get_full_backing_filename_from_filename(const char *backed,
|
void bdrv_get_full_backing_filename_from_filename(const char *backed,
|
||||||
const char *backing,
|
const char *backing,
|
||||||
char *dest, size_t sz,
|
char *dest, size_t sz,
|
||||||
@@ -2752,6 +2789,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
|||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
const char *value;
|
const char *value;
|
||||||
|
bool read_only;
|
||||||
|
|
||||||
assert(reopen_state != NULL);
|
assert(reopen_state != NULL);
|
||||||
assert(reopen_state->bs->drv != NULL);
|
assert(reopen_state->bs->drv != NULL);
|
||||||
@@ -2780,12 +2818,13 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
|||||||
qdict_put(reopen_state->options, "driver", qstring_from_str(value));
|
qdict_put(reopen_state->options, "driver", qstring_from_str(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if we are to stay read-only, do not allow permission change
|
/* If we are to stay read-only, do not allow permission change
|
||||||
* to r/w */
|
* to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is
|
||||||
if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) &&
|
* not set, or if the BDS still has copy_on_read enabled */
|
||||||
reopen_state->flags & BDRV_O_RDWR) {
|
read_only = !(reopen_state->flags & BDRV_O_RDWR);
|
||||||
error_setg(errp, "Node '%s' is read only",
|
ret = bdrv_can_set_read_only(reopen_state->bs, read_only, &local_err);
|
||||||
bdrv_get_device_or_node_name(reopen_state->bs));
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3355,11 +3394,6 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr)
|
|||||||
*nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors;
|
*nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_is_read_only(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
return bs->read_only;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bdrv_is_sg(BlockDriverState *bs)
|
bool bdrv_is_sg(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return bs->sg;
|
return bs->sg;
|
||||||
@@ -4161,8 +4195,8 @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs)
|
|||||||
|
|
||||||
void bdrv_img_create(const char *filename, const char *fmt,
|
void bdrv_img_create(const char *filename, const char *fmt,
|
||||||
const char *base_filename, const char *base_fmt,
|
const char *base_filename, const char *base_fmt,
|
||||||
char *options, uint64_t img_size, int flags,
|
char *options, uint64_t img_size, int flags, bool quiet,
|
||||||
Error **errp, bool quiet)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QemuOptsList *create_opts = NULL;
|
QemuOptsList *create_opts = NULL;
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
|
@@ -19,6 +19,7 @@ block-obj-$(CONFIG_LIBNFS) += nfs.o
|
|||||||
block-obj-$(CONFIG_CURL) += curl.o
|
block-obj-$(CONFIG_CURL) += curl.o
|
||||||
block-obj-$(CONFIG_RBD) += rbd.o
|
block-obj-$(CONFIG_RBD) += rbd.o
|
||||||
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
||||||
|
block-obj-$(CONFIG_VXHS) += vxhs.o
|
||||||
block-obj-$(CONFIG_LIBSSH2) += ssh.o
|
block-obj-$(CONFIG_LIBSSH2) += ssh.o
|
||||||
block-obj-y += accounting.o dirty-bitmap.o
|
block-obj-y += accounting.o dirty-bitmap.o
|
||||||
block-obj-y += write-threshold.o
|
block-obj-y += write-threshold.o
|
||||||
@@ -38,6 +39,7 @@ rbd.o-cflags := $(RBD_CFLAGS)
|
|||||||
rbd.o-libs := $(RBD_LIBS)
|
rbd.o-libs := $(RBD_LIBS)
|
||||||
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
|
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
|
||||||
gluster.o-libs := $(GLUSTERFS_LIBS)
|
gluster.o-libs := $(GLUSTERFS_LIBS)
|
||||||
|
vxhs.o-libs := $(VXHS_LIBS)
|
||||||
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
||||||
ssh.o-libs := $(LIBSSH2_LIBS)
|
ssh.o-libs := $(LIBSSH2_LIBS)
|
||||||
block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
|
block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
|
||||||
|
@@ -110,7 +110,10 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->read_only = true; /* no write support yet */
|
ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@@ -72,7 +72,10 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->read_only = true;
|
ret = bdrv_set_read_only(bs, true, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* read header */
|
/* read header */
|
||||||
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
||||||
|
@@ -56,11 +56,11 @@ static int block_crypto_probe_generic(QCryptoBlockFormat format,
|
|||||||
|
|
||||||
|
|
||||||
static ssize_t block_crypto_read_func(QCryptoBlock *block,
|
static ssize_t block_crypto_read_func(QCryptoBlock *block,
|
||||||
|
void *opaque,
|
||||||
size_t offset,
|
size_t offset,
|
||||||
uint8_t *buf,
|
uint8_t *buf,
|
||||||
size_t buflen,
|
size_t buflen,
|
||||||
Error **errp,
|
Error **errp)
|
||||||
void *opaque)
|
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = opaque;
|
BlockDriverState *bs = opaque;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
@@ -83,11 +83,11 @@ struct BlockCryptoCreateData {
|
|||||||
|
|
||||||
|
|
||||||
static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
||||||
|
void *opaque,
|
||||||
size_t offset,
|
size_t offset,
|
||||||
const uint8_t *buf,
|
const uint8_t *buf,
|
||||||
size_t buflen,
|
size_t buflen,
|
||||||
Error **errp,
|
Error **errp)
|
||||||
void *opaque)
|
|
||||||
{
|
{
|
||||||
struct BlockCryptoCreateData *data = opaque;
|
struct BlockCryptoCreateData *data = opaque;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
@@ -102,9 +102,9 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
|||||||
|
|
||||||
|
|
||||||
static ssize_t block_crypto_init_func(QCryptoBlock *block,
|
static ssize_t block_crypto_init_func(QCryptoBlock *block,
|
||||||
|
void *opaque,
|
||||||
size_t headerlen,
|
size_t headerlen,
|
||||||
Error **errp,
|
Error **errp)
|
||||||
void *opaque)
|
|
||||||
{
|
{
|
||||||
struct BlockCryptoCreateData *data = opaque;
|
struct BlockCryptoCreateData *data = opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
@@ -419,8 +419,12 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = bdrv_set_read_only(bs, true, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
block_module_load_one("dmg-bz2");
|
block_module_load_one("dmg-bz2");
|
||||||
bs->read_only = true;
|
|
||||||
|
|
||||||
s->n_chunks = 0;
|
s->n_chunks = 0;
|
||||||
s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
|
s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
|
||||||
|
23
block/io.c
23
block/io.c
@@ -158,7 +158,7 @@ bool bdrv_requests_pending(BlockDriverState *bs)
|
|||||||
|
|
||||||
static bool bdrv_drain_recurse(BlockDriverState *bs)
|
static bool bdrv_drain_recurse(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BdrvChild *child;
|
BdrvChild *child, *tmp;
|
||||||
bool waited;
|
bool waited;
|
||||||
|
|
||||||
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
|
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
|
||||||
@@ -167,8 +167,25 @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
|
|||||||
bs->drv->bdrv_drain(bs);
|
bs->drv->bdrv_drain(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(child, &bs->children, next) {
|
QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
|
||||||
waited |= bdrv_drain_recurse(child->bs);
|
BlockDriverState *bs = child->bs;
|
||||||
|
bool in_main_loop =
|
||||||
|
qemu_get_current_aio_context() == qemu_get_aio_context();
|
||||||
|
assert(bs->refcnt > 0);
|
||||||
|
if (in_main_loop) {
|
||||||
|
/* In case the recursive bdrv_drain_recurse processes a
|
||||||
|
* block_job_defer_to_main_loop BH and modifies the graph,
|
||||||
|
* let's hold a reference to bs until we are done.
|
||||||
|
*
|
||||||
|
* IOThread doesn't have such a BH, and it is not safe to call
|
||||||
|
* bdrv_unref without BQL, so skip doing it there.
|
||||||
|
*/
|
||||||
|
bdrv_ref(bs);
|
||||||
|
}
|
||||||
|
waited |= bdrv_drain_recurse(bs);
|
||||||
|
if (in_main_loop) {
|
||||||
|
bdrv_unref(bs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return waited;
|
return waited;
|
||||||
|
@@ -1112,10 +1112,11 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
|||||||
BlockdevOnError on_target_error,
|
BlockdevOnError on_target_error,
|
||||||
bool unmap,
|
bool unmap,
|
||||||
BlockCompletionFunc *cb,
|
BlockCompletionFunc *cb,
|
||||||
void *opaque, Error **errp,
|
void *opaque,
|
||||||
const BlockJobDriver *driver,
|
const BlockJobDriver *driver,
|
||||||
bool is_none_mode, BlockDriverState *base,
|
bool is_none_mode, BlockDriverState *base,
|
||||||
bool auto_complete, const char *filter_node_name)
|
bool auto_complete, const char *filter_node_name,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s;
|
MirrorBlockJob *s;
|
||||||
BlockDriverState *mirror_top_bs;
|
BlockDriverState *mirror_top_bs;
|
||||||
@@ -1280,17 +1281,17 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
|||||||
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
|
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
|
||||||
mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces,
|
mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces,
|
||||||
speed, granularity, buf_size, backing_mode,
|
speed, granularity, buf_size, backing_mode,
|
||||||
on_source_error, on_target_error, unmap, NULL, NULL, errp,
|
on_source_error, on_target_error, unmap, NULL, NULL,
|
||||||
&mirror_job_driver, is_none_mode, base, false,
|
&mirror_job_driver, is_none_mode, base, false,
|
||||||
filter_node_name);
|
filter_node_name, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void commit_active_start(const char *job_id, BlockDriverState *bs,
|
void commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||||
BlockDriverState *base, int creation_flags,
|
BlockDriverState *base, int creation_flags,
|
||||||
int64_t speed, BlockdevOnError on_error,
|
int64_t speed, BlockdevOnError on_error,
|
||||||
const char *filter_node_name,
|
const char *filter_node_name,
|
||||||
BlockCompletionFunc *cb, void *opaque, Error **errp,
|
BlockCompletionFunc *cb, void *opaque,
|
||||||
bool auto_complete)
|
bool auto_complete, Error **errp)
|
||||||
{
|
{
|
||||||
int orig_base_flags;
|
int orig_base_flags;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
@@ -1303,9 +1304,9 @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
|
|||||||
|
|
||||||
mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0,
|
mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0,
|
||||||
MIRROR_LEAVE_BACKING_CHAIN,
|
MIRROR_LEAVE_BACKING_CHAIN,
|
||||||
on_error, on_error, true, cb, opaque, &local_err,
|
on_error, on_error, true, cb, opaque,
|
||||||
&commit_active_job_driver, false, base, auto_complete,
|
&commit_active_job_driver, false, base, auto_complete,
|
||||||
filter_node_name);
|
filter_node_name, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto error_restore_flags;
|
goto error_restore_flags;
|
||||||
|
@@ -497,7 +497,7 @@ out:
|
|||||||
|
|
||||||
|
|
||||||
static int64_t nfs_client_open(NFSClient *client, QDict *options,
|
static int64_t nfs_client_open(NFSClient *client, QDict *options,
|
||||||
int flags, Error **errp, int open_flags)
|
int flags, int open_flags, Error **errp)
|
||||||
{
|
{
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
@@ -663,7 +663,7 @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
ret = nfs_client_open(client, options,
|
ret = nfs_client_open(client, options,
|
||||||
(flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
|
(flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
|
||||||
errp, bs->open_flags);
|
bs->open_flags, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -705,7 +705,7 @@ static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nfs_client_open(client, options, O_CREAT, errp, 0);
|
ret = nfs_client_open(client, options, O_CREAT, 0, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
65
block/rbd.c
65
block/rbd.c
@@ -94,7 +94,7 @@ typedef struct BDRVRBDState {
|
|||||||
rados_t cluster;
|
rados_t cluster;
|
||||||
rados_ioctx_t io_ctx;
|
rados_ioctx_t io_ctx;
|
||||||
rbd_image_t image;
|
rbd_image_t image;
|
||||||
char *name;
|
char *image_name;
|
||||||
char *snap;
|
char *snap;
|
||||||
} BDRVRBDState;
|
} BDRVRBDState;
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
int64_t bytes = 0;
|
int64_t bytes = 0;
|
||||||
int64_t objsize;
|
int64_t objsize;
|
||||||
int obj_order = 0;
|
int obj_order = 0;
|
||||||
const char *pool, *name, *conf, *clientname, *keypairs;
|
const char *pool, *image_name, *conf, *user, *keypairs;
|
||||||
const char *secretid;
|
const char *secretid;
|
||||||
rados_t cluster;
|
rados_t cluster;
|
||||||
rados_ioctx_t io_ctx;
|
rados_ioctx_t io_ctx;
|
||||||
@@ -393,11 +393,11 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
*/
|
*/
|
||||||
pool = qdict_get_try_str(options, "pool");
|
pool = qdict_get_try_str(options, "pool");
|
||||||
conf = qdict_get_try_str(options, "conf");
|
conf = qdict_get_try_str(options, "conf");
|
||||||
clientname = qdict_get_try_str(options, "user");
|
user = qdict_get_try_str(options, "user");
|
||||||
name = qdict_get_try_str(options, "image");
|
image_name = qdict_get_try_str(options, "image");
|
||||||
keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
|
keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
|
||||||
|
|
||||||
ret = rados_create(&cluster, clientname);
|
ret = rados_create(&cluster, user);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "error initializing");
|
error_setg_errno(errp, -ret, "error initializing");
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -434,7 +434,7 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
goto shutdown;
|
goto shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = rbd_create(io_ctx, name, bytes, &obj_order);
|
ret = rbd_create(io_ctx, image_name, bytes, &obj_order);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "error rbd create");
|
error_setg_errno(errp, -ret, "error rbd create");
|
||||||
}
|
}
|
||||||
@@ -540,7 +540,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVRBDState *s = bs->opaque;
|
BDRVRBDState *s = bs->opaque;
|
||||||
const char *pool, *snap, *conf, *clientname, *name, *keypairs;
|
const char *pool, *snap, *conf, *user, *image_name, *keypairs;
|
||||||
const char *secretid;
|
const char *secretid;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
@@ -567,24 +567,24 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
pool = qemu_opt_get(opts, "pool");
|
pool = qemu_opt_get(opts, "pool");
|
||||||
conf = qemu_opt_get(opts, "conf");
|
conf = qemu_opt_get(opts, "conf");
|
||||||
snap = qemu_opt_get(opts, "snapshot");
|
snap = qemu_opt_get(opts, "snapshot");
|
||||||
clientname = qemu_opt_get(opts, "user");
|
user = qemu_opt_get(opts, "user");
|
||||||
name = qemu_opt_get(opts, "image");
|
image_name = qemu_opt_get(opts, "image");
|
||||||
keypairs = qemu_opt_get(opts, "=keyvalue-pairs");
|
keypairs = qemu_opt_get(opts, "=keyvalue-pairs");
|
||||||
|
|
||||||
if (!pool || !name) {
|
if (!pool || !image_name) {
|
||||||
error_setg(errp, "Parameters 'pool' and 'image' are required");
|
error_setg(errp, "Parameters 'pool' and 'image' are required");
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
goto failed_opts;
|
goto failed_opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = rados_create(&s->cluster, clientname);
|
r = rados_create(&s->cluster, user);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
error_setg_errno(errp, -r, "error initializing");
|
error_setg_errno(errp, -r, "error initializing");
|
||||||
goto failed_opts;
|
goto failed_opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->snap = g_strdup(snap);
|
s->snap = g_strdup(snap);
|
||||||
s->name = g_strdup(name);
|
s->image_name = g_strdup(image_name);
|
||||||
|
|
||||||
/* try default location when conf=NULL, but ignore failure */
|
/* try default location when conf=NULL, but ignore failure */
|
||||||
r = rados_conf_read_file(s->cluster, conf);
|
r = rados_conf_read_file(s->cluster, conf);
|
||||||
@@ -635,13 +635,23 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto failed_shutdown;
|
goto failed_shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = rbd_open(s->io_ctx, s->name, &s->image, s->snap);
|
/* rbd_open is always r/w */
|
||||||
|
r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
error_setg_errno(errp, -r, "error reading header from %s", s->name);
|
error_setg_errno(errp, -r, "error reading header from %s",
|
||||||
|
s->image_name);
|
||||||
goto failed_open;
|
goto failed_open;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->read_only = (s->snap != NULL);
|
/* If we are using an rbd snapshot, we must be r/o, otherwise
|
||||||
|
* leave as-is */
|
||||||
|
if (s->snap != NULL) {
|
||||||
|
r = bdrv_set_read_only(bs, true, &local_err);
|
||||||
|
if (r < 0) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto failed_open;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -651,13 +661,33 @@ failed_open:
|
|||||||
failed_shutdown:
|
failed_shutdown:
|
||||||
rados_shutdown(s->cluster);
|
rados_shutdown(s->cluster);
|
||||||
g_free(s->snap);
|
g_free(s->snap);
|
||||||
g_free(s->name);
|
g_free(s->image_name);
|
||||||
failed_opts:
|
failed_opts:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
g_free(mon_host);
|
g_free(mon_host);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Since RBD is currently always opened R/W via the API,
|
||||||
|
* we just need to check if we are using a snapshot or not, in
|
||||||
|
* order to determine if we will allow it to be R/W */
|
||||||
|
static int qemu_rbd_reopen_prepare(BDRVReopenState *state,
|
||||||
|
BlockReopenQueue *queue, Error **errp)
|
||||||
|
{
|
||||||
|
BDRVRBDState *s = state->bs->opaque;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (s->snap && state->flags & BDRV_O_RDWR) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Cannot change node '%s' to r/w when using RBD snapshot",
|
||||||
|
bdrv_get_device_or_node_name(state->bs));
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void qemu_rbd_close(BlockDriverState *bs)
|
static void qemu_rbd_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVRBDState *s = bs->opaque;
|
BDRVRBDState *s = bs->opaque;
|
||||||
@@ -665,7 +695,7 @@ static void qemu_rbd_close(BlockDriverState *bs)
|
|||||||
rbd_close(s->image);
|
rbd_close(s->image);
|
||||||
rados_ioctx_destroy(s->io_ctx);
|
rados_ioctx_destroy(s->io_ctx);
|
||||||
g_free(s->snap);
|
g_free(s->snap);
|
||||||
g_free(s->name);
|
g_free(s->image_name);
|
||||||
rados_shutdown(s->cluster);
|
rados_shutdown(s->cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1064,6 +1094,7 @@ static BlockDriver bdrv_rbd = {
|
|||||||
.bdrv_parse_filename = qemu_rbd_parse_filename,
|
.bdrv_parse_filename = qemu_rbd_parse_filename,
|
||||||
.bdrv_file_open = qemu_rbd_open,
|
.bdrv_file_open = qemu_rbd_open,
|
||||||
.bdrv_close = qemu_rbd_close,
|
.bdrv_close = qemu_rbd_close,
|
||||||
|
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
|
||||||
.bdrv_create = qemu_rbd_create,
|
.bdrv_create = qemu_rbd_create,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_get_info = qemu_rbd_getinfo,
|
.bdrv_get_info = qemu_rbd_getinfo,
|
||||||
|
@@ -656,7 +656,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||||||
s->replication_state = BLOCK_REPLICATION_FAILOVER;
|
s->replication_state = BLOCK_REPLICATION_FAILOVER;
|
||||||
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
|
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
|
||||||
BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||||
NULL, replication_done, bs, errp, true);
|
NULL, replication_done, bs, true, errp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
|
@@ -595,7 +595,7 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
|
|||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = socket_connect(s->addr, errp, NULL, NULL);
|
fd = socket_connect(s->addr, NULL, NULL, errp);
|
||||||
|
|
||||||
if (s->addr->type == SOCKET_ADDRESS_KIND_INET && fd >= 0) {
|
if (s->addr->type == SOCKET_ADDRESS_KIND_INET && fd >= 0) {
|
||||||
int ret = socket_set_nodelay(fd);
|
int ret = socket_set_nodelay(fd);
|
||||||
|
@@ -681,7 +681,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Open the socket and connect. */
|
/* Open the socket and connect. */
|
||||||
s->sock = inet_connect_saddr(s->inet, errp, NULL, NULL);
|
s->sock = inet_connect_saddr(s->inet, NULL, NULL, errp);
|
||||||
if (s->sock < 0) {
|
if (s->sock < 0) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto err;
|
goto err;
|
||||||
|
@@ -110,3 +110,20 @@ qed_aio_write_data(void *s, void *acb, int ret, uint64_t offset, size_t len) "s
|
|||||||
qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64
|
qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64
|
||||||
qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64
|
qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64
|
||||||
qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu"
|
qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu"
|
||||||
|
|
||||||
|
# block/vxhs.c
|
||||||
|
vxhs_iio_callback(int error) "ctx is NULL: error %d"
|
||||||
|
vxhs_iio_callback_chnfail(int err, int error) "QNIO channel failed, no i/o %d, %d"
|
||||||
|
vxhs_iio_callback_unknwn(int opcode, int err) "unexpected opcode %d, errno %d"
|
||||||
|
vxhs_aio_rw_invalid(int req) "Invalid I/O request iodir %d"
|
||||||
|
vxhs_aio_rw_ioerr(char *guid, int iodir, uint64_t size, uint64_t off, void *acb, int ret, int err) "IO ERROR (vDisk %s) FOR : Read/Write = %d size = %"PRIu64" offset = %"PRIu64" ACB = %p. Error = %d, errno = %d"
|
||||||
|
vxhs_get_vdisk_stat_err(char *guid, int ret, int err) "vDisk (%s) stat ioctl failed, ret = %d, errno = %d"
|
||||||
|
vxhs_get_vdisk_stat(char *vdisk_guid, uint64_t vdisk_size) "vDisk %s stat ioctl returned size %"PRIu64
|
||||||
|
vxhs_complete_aio(void *acb, uint64_t ret) "aio failed acb %p ret %"PRIu64
|
||||||
|
vxhs_parse_uri_filename(const char *filename) "URI passed via bdrv_parse_filename %s"
|
||||||
|
vxhs_open_vdiskid(const char *vdisk_id) "Opening vdisk-id %s"
|
||||||
|
vxhs_open_hostinfo(char *of_vsa_addr, int port) "Adding host %s:%d to BDRVVXHSState"
|
||||||
|
vxhs_open_iio_open(const char *host) "Failed to connect to storage agent on host %s"
|
||||||
|
vxhs_parse_uri_hostinfo(char *host, int port) "Host: IP %s, Port %d"
|
||||||
|
vxhs_close(char *vdisk_guid) "Closing vdisk %s"
|
||||||
|
vxhs_get_creds(const char *cacert, const char *client_key, const char *client_cert) "cacert %s, client_key %s, client_cert %s"
|
||||||
|
@@ -1156,8 +1156,6 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
s->current_cluster=0xffffffff;
|
s->current_cluster=0xffffffff;
|
||||||
|
|
||||||
/* read only is the default for safety */
|
|
||||||
bs->read_only = true;
|
|
||||||
s->qcow = NULL;
|
s->qcow = NULL;
|
||||||
s->qcow_filename = NULL;
|
s->qcow_filename = NULL;
|
||||||
s->fat2 = NULL;
|
s->fat2 = NULL;
|
||||||
@@ -1169,11 +1167,24 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
|
s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
|
||||||
|
|
||||||
if (qemu_opt_get_bool(opts, "rw", false)) {
|
if (qemu_opt_get_bool(opts, "rw", false)) {
|
||||||
ret = enable_write_target(bs, errp);
|
if (!bdrv_is_read_only(bs)) {
|
||||||
if (ret < 0) {
|
ret = enable_write_target(bs, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = -EPERM;
|
||||||
|
error_setg(errp,
|
||||||
|
"Unable to set VVFAT to 'rw' when drive is read-only");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* read only is the default for safety */
|
||||||
|
ret = bdrv_set_read_only(bs, true, &local_err);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
bs->read_only = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->total_sectors = cyls * heads * secs;
|
bs->total_sectors = cyls * heads * secs;
|
||||||
|
575
block/vxhs.c
Normal file
575
block/vxhs.c
Normal file
@@ -0,0 +1,575 @@
|
|||||||
|
/*
|
||||||
|
* QEMU Block driver for Veritas HyperScale (VxHS)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Veritas Technologies LLC.
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include <qnio/qnio_api.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include "block/block_int.h"
|
||||||
|
#include "qapi/qmp/qerror.h"
|
||||||
|
#include "qapi/qmp/qdict.h"
|
||||||
|
#include "qapi/qmp/qstring.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "qemu/uri.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/uuid.h"
|
||||||
|
#include "crypto/tlscredsx509.h"
|
||||||
|
|
||||||
|
#define VXHS_OPT_FILENAME "filename"
|
||||||
|
#define VXHS_OPT_VDISK_ID "vdisk-id"
|
||||||
|
#define VXHS_OPT_SERVER "server"
|
||||||
|
#define VXHS_OPT_HOST "host"
|
||||||
|
#define VXHS_OPT_PORT "port"
|
||||||
|
|
||||||
|
/* Only accessed under QEMU global mutex */
|
||||||
|
static uint32_t vxhs_ref;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VDISK_AIO_READ,
|
||||||
|
VDISK_AIO_WRITE,
|
||||||
|
} VDISKAIOCmd;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HyperScale AIO callbacks structure
|
||||||
|
*/
|
||||||
|
typedef struct VXHSAIOCB {
|
||||||
|
BlockAIOCB common;
|
||||||
|
int err;
|
||||||
|
} VXHSAIOCB;
|
||||||
|
|
||||||
|
typedef struct VXHSvDiskHostsInfo {
|
||||||
|
void *dev_handle; /* Device handle */
|
||||||
|
char *host; /* Host name or IP */
|
||||||
|
int port; /* Host's port number */
|
||||||
|
} VXHSvDiskHostsInfo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure per vDisk maintained for state
|
||||||
|
*/
|
||||||
|
typedef struct BDRVVXHSState {
|
||||||
|
VXHSvDiskHostsInfo vdisk_hostinfo; /* Per host info */
|
||||||
|
char *vdisk_guid;
|
||||||
|
char *tlscredsid; /* tlscredsid */
|
||||||
|
} BDRVVXHSState;
|
||||||
|
|
||||||
|
static void vxhs_complete_aio_bh(void *opaque)
|
||||||
|
{
|
||||||
|
VXHSAIOCB *acb = opaque;
|
||||||
|
BlockCompletionFunc *cb = acb->common.cb;
|
||||||
|
void *cb_opaque = acb->common.opaque;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (acb->err != 0) {
|
||||||
|
trace_vxhs_complete_aio(acb, acb->err);
|
||||||
|
ret = (-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_aio_unref(acb);
|
||||||
|
cb(cb_opaque, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called from a libqnio thread
|
||||||
|
*/
|
||||||
|
static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error)
|
||||||
|
{
|
||||||
|
VXHSAIOCB *acb = NULL;
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
case IRP_READ_REQUEST:
|
||||||
|
case IRP_WRITE_REQUEST:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ctx is VXHSAIOCB*
|
||||||
|
* ctx is NULL if error is QNIOERROR_CHANNEL_HUP
|
||||||
|
*/
|
||||||
|
if (ctx) {
|
||||||
|
acb = ctx;
|
||||||
|
} else {
|
||||||
|
trace_vxhs_iio_callback(error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
if (!acb->err) {
|
||||||
|
acb->err = error;
|
||||||
|
}
|
||||||
|
trace_vxhs_iio_callback(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
|
||||||
|
vxhs_complete_aio_bh, acb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (error == QNIOERROR_HUP) {
|
||||||
|
/*
|
||||||
|
* Channel failed, spontaneous notification,
|
||||||
|
* not in response to I/O
|
||||||
|
*/
|
||||||
|
trace_vxhs_iio_callback_chnfail(error, errno);
|
||||||
|
} else {
|
||||||
|
trace_vxhs_iio_callback_unknwn(opcode, error);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QemuOptsList runtime_opts = {
|
||||||
|
.name = "vxhs",
|
||||||
|
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||||
|
.desc = {
|
||||||
|
{
|
||||||
|
.name = VXHS_OPT_FILENAME,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "URI to the Veritas HyperScale image",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = VXHS_OPT_VDISK_ID,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "UUID of the VxHS vdisk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "tls-creds",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of the TLS/SSL credentials to use",
|
||||||
|
},
|
||||||
|
{ /* end of list */ }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static QemuOptsList runtime_tcp_opts = {
|
||||||
|
.name = "vxhs_tcp",
|
||||||
|
.head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head),
|
||||||
|
.desc = {
|
||||||
|
{
|
||||||
|
.name = VXHS_OPT_HOST,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "host address (ipv4 addresses)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = VXHS_OPT_PORT,
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "port number on which VxHSD is listening (default 9999)",
|
||||||
|
.def_value_str = "9999"
|
||||||
|
},
|
||||||
|
{ /* end of list */ }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse incoming URI and populate *options with the host
|
||||||
|
* and device information
|
||||||
|
*/
|
||||||
|
static int vxhs_parse_uri(const char *filename, QDict *options)
|
||||||
|
{
|
||||||
|
URI *uri = NULL;
|
||||||
|
char *port;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
trace_vxhs_parse_uri_filename(filename);
|
||||||
|
uri = uri_parse(filename);
|
||||||
|
if (!uri || !uri->server || !uri->path) {
|
||||||
|
uri_free(uri);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdict_put(options, VXHS_OPT_SERVER".host", qstring_from_str(uri->server));
|
||||||
|
|
||||||
|
if (uri->port) {
|
||||||
|
port = g_strdup_printf("%d", uri->port);
|
||||||
|
qdict_put(options, VXHS_OPT_SERVER".port", qstring_from_str(port));
|
||||||
|
g_free(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
qdict_put(options, "vdisk-id", qstring_from_str(uri->path));
|
||||||
|
|
||||||
|
trace_vxhs_parse_uri_hostinfo(uri->server, uri->port);
|
||||||
|
uri_free(uri);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vxhs_parse_filename(const char *filename, QDict *options,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
if (qdict_haskey(options, "vdisk-id") || qdict_haskey(options, "server")) {
|
||||||
|
error_setg(errp, "vdisk-id/server and a file name may not be specified "
|
||||||
|
"at the same time");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strstr(filename, "://")) {
|
||||||
|
int ret = vxhs_parse_uri(filename, options);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "Invalid URI. URI should be of the form "
|
||||||
|
" vxhs://<host_ip>:<port>/<vdisk-id>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vxhs_init_and_ref(void)
|
||||||
|
{
|
||||||
|
if (vxhs_ref++ == 0) {
|
||||||
|
if (iio_init(QNIO_VERSION, vxhs_iio_callback)) {
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vxhs_unref(void)
|
||||||
|
{
|
||||||
|
if (--vxhs_ref == 0) {
|
||||||
|
iio_fini();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vxhs_get_tls_creds(const char *id, char **cacert,
|
||||||
|
char **key, char **cert, Error **errp)
|
||||||
|
{
|
||||||
|
Object *obj;
|
||||||
|
QCryptoTLSCreds *creds;
|
||||||
|
QCryptoTLSCredsX509 *creds_x509;
|
||||||
|
|
||||||
|
obj = object_resolve_path_component(
|
||||||
|
object_get_objects_root(), id);
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
error_setg(errp, "No TLS credentials with id '%s'",
|
||||||
|
id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
creds_x509 = (QCryptoTLSCredsX509 *)
|
||||||
|
object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS_X509);
|
||||||
|
|
||||||
|
if (!creds_x509) {
|
||||||
|
error_setg(errp, "Object with id '%s' is not TLS credentials",
|
||||||
|
id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
creds = &creds_x509->parent_obj;
|
||||||
|
|
||||||
|
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Expecting TLS credentials with a client endpoint");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the cacert, client_cert and client_key file names.
|
||||||
|
*/
|
||||||
|
if (!creds->dir) {
|
||||||
|
error_setg(errp, "TLS object missing 'dir' property value");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*cacert = g_strdup_printf("%s/%s", creds->dir,
|
||||||
|
QCRYPTO_TLS_CREDS_X509_CA_CERT);
|
||||||
|
*cert = g_strdup_printf("%s/%s", creds->dir,
|
||||||
|
QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
|
||||||
|
*key = g_strdup_printf("%s/%s", creds->dir,
|
||||||
|
QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vxhs_open(BlockDriverState *bs, QDict *options,
|
||||||
|
int bdrv_flags, Error **errp)
|
||||||
|
{
|
||||||
|
BDRVVXHSState *s = bs->opaque;
|
||||||
|
void *dev_handlep;
|
||||||
|
QDict *backing_options = NULL;
|
||||||
|
QemuOpts *opts = NULL;
|
||||||
|
QemuOpts *tcp_opts = NULL;
|
||||||
|
char *of_vsa_addr = NULL;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
const char *vdisk_id_opt;
|
||||||
|
const char *server_host_opt;
|
||||||
|
int ret = 0;
|
||||||
|
char *cacert = NULL;
|
||||||
|
char *client_key = NULL;
|
||||||
|
char *client_cert = NULL;
|
||||||
|
|
||||||
|
ret = vxhs_init_and_ref();
|
||||||
|
if (ret < 0) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create opts info from runtime_opts and runtime_tcp_opts list */
|
||||||
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
|
tcp_opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort);
|
||||||
|
|
||||||
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vdisk-id is the disk UUID */
|
||||||
|
vdisk_id_opt = qemu_opt_get(opts, VXHS_OPT_VDISK_ID);
|
||||||
|
if (!vdisk_id_opt) {
|
||||||
|
error_setg(&local_err, QERR_MISSING_PARAMETER, VXHS_OPT_VDISK_ID);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vdisk-id may contain a leading '/' */
|
||||||
|
if (strlen(vdisk_id_opt) > UUID_FMT_LEN + 1) {
|
||||||
|
error_setg(&local_err, "vdisk-id cannot be more than %d characters",
|
||||||
|
UUID_FMT_LEN);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->vdisk_guid = g_strdup(vdisk_id_opt);
|
||||||
|
trace_vxhs_open_vdiskid(vdisk_id_opt);
|
||||||
|
|
||||||
|
/* get the 'server.' arguments */
|
||||||
|
qdict_extract_subqdict(options, &backing_options, VXHS_OPT_SERVER".");
|
||||||
|
|
||||||
|
qemu_opts_absorb_qdict(tcp_opts, backing_options, &local_err);
|
||||||
|
if (local_err != NULL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_host_opt = qemu_opt_get(tcp_opts, VXHS_OPT_HOST);
|
||||||
|
if (!server_host_opt) {
|
||||||
|
error_setg(&local_err, QERR_MISSING_PARAMETER,
|
||||||
|
VXHS_OPT_SERVER"."VXHS_OPT_HOST);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(server_host_opt) > MAXHOSTNAMELEN) {
|
||||||
|
error_setg(&local_err, "server.host cannot be more than %d characters",
|
||||||
|
MAXHOSTNAMELEN);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if we got tls-creds via the --object argument */
|
||||||
|
s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds"));
|
||||||
|
if (s->tlscredsid) {
|
||||||
|
vxhs_get_tls_creds(s->tlscredsid, &cacert, &client_key,
|
||||||
|
&client_cert, &local_err);
|
||||||
|
if (local_err != NULL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
trace_vxhs_get_creds(cacert, client_key, client_cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->vdisk_hostinfo.host = g_strdup(server_host_opt);
|
||||||
|
s->vdisk_hostinfo.port = g_ascii_strtoll(qemu_opt_get(tcp_opts,
|
||||||
|
VXHS_OPT_PORT),
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
|
trace_vxhs_open_hostinfo(s->vdisk_hostinfo.host,
|
||||||
|
s->vdisk_hostinfo.port);
|
||||||
|
|
||||||
|
of_vsa_addr = g_strdup_printf("of://%s:%d",
|
||||||
|
s->vdisk_hostinfo.host,
|
||||||
|
s->vdisk_hostinfo.port);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open qnio channel to storage agent if not opened before
|
||||||
|
*/
|
||||||
|
dev_handlep = iio_open(of_vsa_addr, s->vdisk_guid, 0,
|
||||||
|
cacert, client_key, client_cert);
|
||||||
|
if (dev_handlep == NULL) {
|
||||||
|
trace_vxhs_open_iio_open(of_vsa_addr);
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
s->vdisk_hostinfo.dev_handle = dev_handlep;
|
||||||
|
|
||||||
|
out:
|
||||||
|
g_free(of_vsa_addr);
|
||||||
|
QDECREF(backing_options);
|
||||||
|
qemu_opts_del(tcp_opts);
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
g_free(cacert);
|
||||||
|
g_free(client_key);
|
||||||
|
g_free(client_cert);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
vxhs_unref();
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
g_free(s->vdisk_hostinfo.host);
|
||||||
|
g_free(s->vdisk_guid);
|
||||||
|
g_free(s->tlscredsid);
|
||||||
|
s->vdisk_guid = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const AIOCBInfo vxhs_aiocb_info = {
|
||||||
|
.aiocb_size = sizeof(VXHSAIOCB)
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This allocates QEMU-VXHS callback for each IO
|
||||||
|
* and is passed to QNIO. When QNIO completes the work,
|
||||||
|
* it will be passed back through the callback.
|
||||||
|
*/
|
||||||
|
static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
QEMUIOVector *qiov, int nb_sectors,
|
||||||
|
BlockCompletionFunc *cb, void *opaque,
|
||||||
|
VDISKAIOCmd iodir)
|
||||||
|
{
|
||||||
|
VXHSAIOCB *acb = NULL;
|
||||||
|
BDRVVXHSState *s = bs->opaque;
|
||||||
|
size_t size;
|
||||||
|
uint64_t offset;
|
||||||
|
int iio_flags = 0;
|
||||||
|
int ret = 0;
|
||||||
|
void *dev_handle = s->vdisk_hostinfo.dev_handle;
|
||||||
|
|
||||||
|
offset = sector_num * BDRV_SECTOR_SIZE;
|
||||||
|
size = nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
|
acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize VXHSAIOCB.
|
||||||
|
*/
|
||||||
|
acb->err = 0;
|
||||||
|
|
||||||
|
iio_flags = IIO_FLAG_ASYNC;
|
||||||
|
|
||||||
|
switch (iodir) {
|
||||||
|
case VDISK_AIO_WRITE:
|
||||||
|
ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov,
|
||||||
|
offset, (uint64_t)size, iio_flags);
|
||||||
|
break;
|
||||||
|
case VDISK_AIO_READ:
|
||||||
|
ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov,
|
||||||
|
offset, (uint64_t)size, iio_flags);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
trace_vxhs_aio_rw_invalid(iodir);
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
trace_vxhs_aio_rw_ioerr(s->vdisk_guid, iodir, size, offset,
|
||||||
|
acb, ret, errno);
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
return &acb->common;
|
||||||
|
|
||||||
|
errout:
|
||||||
|
qemu_aio_unref(acb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs,
|
||||||
|
int64_t sector_num, QEMUIOVector *qiov,
|
||||||
|
int nb_sectors,
|
||||||
|
BlockCompletionFunc *cb, void *opaque)
|
||||||
|
{
|
||||||
|
return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb,
|
||||||
|
opaque, VDISK_AIO_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs,
|
||||||
|
int64_t sector_num, QEMUIOVector *qiov,
|
||||||
|
int nb_sectors,
|
||||||
|
BlockCompletionFunc *cb, void *opaque)
|
||||||
|
{
|
||||||
|
return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors,
|
||||||
|
cb, opaque, VDISK_AIO_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vxhs_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVVXHSState *s = bs->opaque;
|
||||||
|
|
||||||
|
trace_vxhs_close(s->vdisk_guid);
|
||||||
|
|
||||||
|
g_free(s->vdisk_guid);
|
||||||
|
s->vdisk_guid = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close vDisk device
|
||||||
|
*/
|
||||||
|
if (s->vdisk_hostinfo.dev_handle) {
|
||||||
|
iio_close(s->vdisk_hostinfo.dev_handle);
|
||||||
|
s->vdisk_hostinfo.dev_handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vxhs_unref();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the dynamically allocated host string etc
|
||||||
|
*/
|
||||||
|
g_free(s->vdisk_hostinfo.host);
|
||||||
|
g_free(s->tlscredsid);
|
||||||
|
s->tlscredsid = NULL;
|
||||||
|
s->vdisk_hostinfo.host = NULL;
|
||||||
|
s->vdisk_hostinfo.port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t vxhs_get_vdisk_stat(BDRVVXHSState *s)
|
||||||
|
{
|
||||||
|
int64_t vdisk_size = -1;
|
||||||
|
int ret = 0;
|
||||||
|
void *dev_handle = s->vdisk_hostinfo.dev_handle;
|
||||||
|
|
||||||
|
ret = iio_ioctl(dev_handle, IOR_VDISK_STAT, &vdisk_size, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
trace_vxhs_get_vdisk_stat_err(s->vdisk_guid, ret, errno);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_vxhs_get_vdisk_stat(s->vdisk_guid, vdisk_size);
|
||||||
|
return vdisk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the size of vDisk in bytes. This is required
|
||||||
|
* by QEMU block upper block layer so that it is visible
|
||||||
|
* to guest.
|
||||||
|
*/
|
||||||
|
static int64_t vxhs_getlength(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVVXHSState *s = bs->opaque;
|
||||||
|
int64_t vdisk_size;
|
||||||
|
|
||||||
|
vdisk_size = vxhs_get_vdisk_stat(s);
|
||||||
|
if (vdisk_size < 0) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vdisk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockDriver bdrv_vxhs = {
|
||||||
|
.format_name = "vxhs",
|
||||||
|
.protocol_name = "vxhs",
|
||||||
|
.instance_size = sizeof(BDRVVXHSState),
|
||||||
|
.bdrv_file_open = vxhs_open,
|
||||||
|
.bdrv_parse_filename = vxhs_parse_filename,
|
||||||
|
.bdrv_close = vxhs_close,
|
||||||
|
.bdrv_getlength = vxhs_getlength,
|
||||||
|
.bdrv_aio_readv = vxhs_aio_readv,
|
||||||
|
.bdrv_aio_writev = vxhs_aio_writev,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bdrv_vxhs_init(void)
|
||||||
|
{
|
||||||
|
bdrv_register(&bdrv_vxhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_init(bdrv_vxhs_init);
|
12
blockdev.c
12
blockdev.c
@@ -1728,7 +1728,7 @@ static void external_snapshot_prepare(BlkActionState *common,
|
|||||||
bdrv_img_create(new_image_file, format,
|
bdrv_img_create(new_image_file, format,
|
||||||
state->old_bs->filename,
|
state->old_bs->filename,
|
||||||
state->old_bs->drv->format_name,
|
state->old_bs->drv->format_name,
|
||||||
NULL, size, flags, &local_err, false);
|
NULL, size, flags, false, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
@@ -3142,7 +3142,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
|||||||
}
|
}
|
||||||
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
|
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
|
||||||
BLOCK_JOB_DEFAULT, speed, on_error,
|
BLOCK_JOB_DEFAULT, speed, on_error,
|
||||||
filter_node_name, NULL, NULL, &local_err, false);
|
filter_node_name, NULL, NULL, false, &local_err);
|
||||||
} else {
|
} else {
|
||||||
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
|
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
|
||||||
if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
|
if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
|
||||||
@@ -3237,10 +3237,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
|||||||
if (source) {
|
if (source) {
|
||||||
bdrv_img_create(backup->target, backup->format, source->filename,
|
bdrv_img_create(backup->target, backup->format, source->filename,
|
||||||
source->drv->format_name, NULL,
|
source->drv->format_name, NULL,
|
||||||
size, flags, &local_err, false);
|
size, flags, false, &local_err);
|
||||||
} else {
|
} else {
|
||||||
bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
|
bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
|
||||||
size, flags, &local_err, false);
|
size, flags, false, &local_err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3531,7 +3531,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
|||||||
/* create new image w/o backing file */
|
/* create new image w/o backing file */
|
||||||
assert(format);
|
assert(format);
|
||||||
bdrv_img_create(arg->target, format,
|
bdrv_img_create(arg->target, format,
|
||||||
NULL, NULL, NULL, size, flags, &local_err, false);
|
NULL, NULL, NULL, size, flags, false, &local_err);
|
||||||
} else {
|
} else {
|
||||||
switch (arg->mode) {
|
switch (arg->mode) {
|
||||||
case NEW_IMAGE_MODE_EXISTING:
|
case NEW_IMAGE_MODE_EXISTING:
|
||||||
@@ -3541,7 +3541,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
|||||||
bdrv_img_create(arg->target, format,
|
bdrv_img_create(arg->target, format,
|
||||||
source->filename,
|
source->filename,
|
||||||
source->drv->format_name,
|
source->drv->format_name,
|
||||||
NULL, size, flags, &local_err, false);
|
NULL, size, flags, false, &local_err);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
|
236
configure
vendored
236
configure
vendored
@@ -320,6 +320,7 @@ numa=""
|
|||||||
tcmalloc="no"
|
tcmalloc="no"
|
||||||
jemalloc="no"
|
jemalloc="no"
|
||||||
replication="yes"
|
replication="yes"
|
||||||
|
vxhs=""
|
||||||
|
|
||||||
supported_cpu="no"
|
supported_cpu="no"
|
||||||
supported_os="no"
|
supported_os="no"
|
||||||
@@ -742,7 +743,7 @@ if test "$mingw32" = "yes" ; then
|
|||||||
sysconfdir="\${prefix}"
|
sysconfdir="\${prefix}"
|
||||||
local_statedir=
|
local_statedir=
|
||||||
confsuffix=""
|
confsuffix=""
|
||||||
libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi -lnetapi32 $libs_qga"
|
libs_qga="-lws2_32 -lwinmm -lpowrprof -lwtsapi32 -liphlpapi -lnetapi32 $libs_qga"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
werror=""
|
werror=""
|
||||||
@@ -1183,6 +1184,10 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--enable-replication) replication="yes"
|
--enable-replication) replication="yes"
|
||||||
;;
|
;;
|
||||||
|
--disable-vxhs) vxhs="no"
|
||||||
|
;;
|
||||||
|
--enable-vxhs) vxhs="yes"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "ERROR: unknown option $opt"
|
echo "ERROR: unknown option $opt"
|
||||||
echo "Try '$0 --help' for more information"
|
echo "Try '$0 --help' for more information"
|
||||||
@@ -1191,21 +1196,6 @@ for opt do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if ! has $python; then
|
|
||||||
error_exit "Python not found. Use --python=/path/to/python"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Note that if the Python conditional here evaluates True we will exit
|
|
||||||
# with status 1 which is a shell 'false' value.
|
|
||||||
if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6) or sys.version_info >= (3,))'; then
|
|
||||||
error_exit "Cannot use '$python', Python 2.6 or later is required." \
|
|
||||||
"Note that Python 3 or later is not yet supported." \
|
|
||||||
"Use --python=/path/to/python to specify a supported Python."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Suppress writing compiled files
|
|
||||||
python="$python -B"
|
|
||||||
|
|
||||||
case "$cpu" in
|
case "$cpu" in
|
||||||
ppc)
|
ppc)
|
||||||
CPU_CFLAGS="-m32"
|
CPU_CFLAGS="-m32"
|
||||||
@@ -1280,6 +1270,9 @@ for config in $mak_wilds; do
|
|||||||
default_target_list="${default_target_list} $(basename "$config" .mak)"
|
default_target_list="${default_target_list} $(basename "$config" .mak)"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Enumerate public trace backends for --help output
|
||||||
|
trace_backend_list=$(echo $(grep -le '^PUBLIC = True$' "$source_path"/scripts/tracetool/backend/*.py | sed -e 's/^.*\/\(.*\)\.py$/\1/'))
|
||||||
|
|
||||||
if test x"$show_help" = x"yes" ; then
|
if test x"$show_help" = x"yes" ; then
|
||||||
cat << EOF
|
cat << EOF
|
||||||
|
|
||||||
@@ -1333,7 +1326,7 @@ Advanced options (experts only):
|
|||||||
set block driver read-only whitelist
|
set block driver read-only whitelist
|
||||||
(affects only QEMU, not qemu-img)
|
(affects only QEMU, not qemu-img)
|
||||||
--enable-trace-backends=B Set trace backend
|
--enable-trace-backends=B Set trace backend
|
||||||
Available backends: $($python $source_path/scripts/tracetool.py --list-backends)
|
Available backends: $trace_backend_list
|
||||||
--with-trace-file=NAME Full PATH,NAME of file to store traces
|
--with-trace-file=NAME Full PATH,NAME of file to store traces
|
||||||
Default:trace-<pid>
|
Default:trace-<pid>
|
||||||
--disable-slirp disable SLIRP userspace network connectivity
|
--disable-slirp disable SLIRP userspace network connectivity
|
||||||
@@ -1427,12 +1420,28 @@ disabled with --disable-FEATURE, default is enabled if available:
|
|||||||
xfsctl xfsctl support
|
xfsctl xfsctl support
|
||||||
qom-cast-debug cast debugging support
|
qom-cast-debug cast debugging support
|
||||||
tools build qemu-io, qemu-nbd and qemu-image tools
|
tools build qemu-io, qemu-nbd and qemu-image tools
|
||||||
|
vxhs Veritas HyperScale vDisk backend support
|
||||||
|
|
||||||
NOTE: The object files are built at the place where configure is launched
|
NOTE: The object files are built at the place where configure is launched
|
||||||
EOF
|
EOF
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! has $python; then
|
||||||
|
error_exit "Python not found. Use --python=/path/to/python"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Note that if the Python conditional here evaluates True we will exit
|
||||||
|
# with status 1 which is a shell 'false' value.
|
||||||
|
if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6) or sys.version_info >= (3,))'; then
|
||||||
|
error_exit "Cannot use '$python', Python 2.6 or later is required." \
|
||||||
|
"Note that Python 3 or later is not yet supported." \
|
||||||
|
"Use --python=/path/to/python to specify a supported Python."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Suppress writing compiled files
|
||||||
|
python="$python -B"
|
||||||
|
|
||||||
# Now we have handled --enable-tcg-interpreter and know we're not just
|
# Now we have handled --enable-tcg-interpreter and know we're not just
|
||||||
# printing the help message, bail out if the host CPU isn't supported.
|
# printing the help message, bail out if the host CPU isn't supported.
|
||||||
if test "$ARCH" = "unknown"; then
|
if test "$ARCH" = "unknown"; then
|
||||||
@@ -1988,30 +1997,65 @@ fi
|
|||||||
# xen probe
|
# xen probe
|
||||||
|
|
||||||
if test "$xen" != "no" ; then
|
if test "$xen" != "no" ; then
|
||||||
xen_libs="-lxenstore -lxenctrl -lxenguest"
|
# Check whether Xen library path is specified via --extra-ldflags to avoid
|
||||||
xen_stable_libs="-lxenforeignmemory -lxengnttab -lxenevtchn"
|
# overriding this setting with pkg-config output. If not, try pkg-config
|
||||||
|
# to obtain all needed flags.
|
||||||
|
|
||||||
# First we test whether Xen headers and libraries are available.
|
if ! echo $EXTRA_LDFLAGS | grep tools/libxc > /dev/null && \
|
||||||
# If no, we are done and there is no Xen support.
|
$pkg_config --exists xencontrol ; then
|
||||||
# If yes, more tests are run to detect the Xen version.
|
xen_ctrl_version="$(printf '%d%02d%02d' \
|
||||||
|
$($pkg_config --modversion xencontrol | sed 's/\./ /g') )"
|
||||||
|
xen=yes
|
||||||
|
xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab"
|
||||||
|
xen_pc="$xen_pc xenevtchn xendevicemodel"
|
||||||
|
QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)"
|
||||||
|
libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu"
|
||||||
|
LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS"
|
||||||
|
else
|
||||||
|
|
||||||
# Xen (any)
|
xen_libs="-lxenstore -lxenctrl -lxenguest"
|
||||||
cat > $TMPC <<EOF
|
xen_stable_libs="-lxencall -lxenforeignmemory -lxengnttab -lxenevtchn"
|
||||||
|
|
||||||
|
# First we test whether Xen headers and libraries are available.
|
||||||
|
# If no, we are done and there is no Xen support.
|
||||||
|
# If yes, more tests are run to detect the Xen version.
|
||||||
|
|
||||||
|
# Xen (any)
|
||||||
|
cat > $TMPC <<EOF
|
||||||
#include <xenctrl.h>
|
#include <xenctrl.h>
|
||||||
int main(void) {
|
int main(void) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
if ! compile_prog "" "$xen_libs" ; then
|
if ! compile_prog "" "$xen_libs" ; then
|
||||||
# Xen not found
|
# Xen not found
|
||||||
if test "$xen" = "yes" ; then
|
if test "$xen" = "yes" ; then
|
||||||
feature_not_found "xen" "Install xen devel"
|
feature_not_found "xen" "Install xen devel"
|
||||||
fi
|
fi
|
||||||
xen=no
|
xen=no
|
||||||
|
|
||||||
# Xen unstable
|
# Xen unstable
|
||||||
elif
|
elif
|
||||||
cat > $TMPC <<EOF &&
|
cat > $TMPC <<EOF &&
|
||||||
|
#undef XC_WANT_COMPAT_DEVICEMODEL_API
|
||||||
|
#define __XEN_TOOLS__
|
||||||
|
#include <xendevicemodel.h>
|
||||||
|
int main(void) {
|
||||||
|
xendevicemodel_handle *xd;
|
||||||
|
|
||||||
|
xd = xendevicemodel_open(0, 0);
|
||||||
|
xendevicemodel_close(xd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs"
|
||||||
|
then
|
||||||
|
xen_stable_libs="-lxendevicemodel $xen_stable_libs"
|
||||||
|
xen_ctrl_version=40900
|
||||||
|
xen=yes
|
||||||
|
elif
|
||||||
|
cat > $TMPC <<EOF &&
|
||||||
/*
|
/*
|
||||||
* If we have stable libs the we don't want the libxc compat
|
* If we have stable libs the we don't want the libxc compat
|
||||||
* layers, regardless of what CFLAGS we may have been given.
|
* layers, regardless of what CFLAGS we may have been given.
|
||||||
@@ -2061,12 +2105,12 @@ int main(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
compile_prog "" "$xen_libs $xen_stable_libs"
|
compile_prog "" "$xen_libs $xen_stable_libs"
|
||||||
then
|
then
|
||||||
xen_ctrl_version=480
|
xen_ctrl_version=40800
|
||||||
xen=yes
|
xen=yes
|
||||||
elif
|
elif
|
||||||
cat > $TMPC <<EOF &&
|
cat > $TMPC <<EOF &&
|
||||||
/*
|
/*
|
||||||
* If we have stable libs the we don't want the libxc compat
|
* If we have stable libs the we don't want the libxc compat
|
||||||
* layers, regardless of what CFLAGS we may have been given.
|
* layers, regardless of what CFLAGS we may have been given.
|
||||||
@@ -2112,12 +2156,12 @@ int main(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
compile_prog "" "$xen_libs $xen_stable_libs"
|
compile_prog "" "$xen_libs $xen_stable_libs"
|
||||||
then
|
then
|
||||||
xen_ctrl_version=471
|
xen_ctrl_version=40701
|
||||||
xen=yes
|
xen=yes
|
||||||
elif
|
elif
|
||||||
cat > $TMPC <<EOF &&
|
cat > $TMPC <<EOF &&
|
||||||
#include <xenctrl.h>
|
#include <xenctrl.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
int main(void) {
|
int main(void) {
|
||||||
@@ -2127,14 +2171,14 @@ int main(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
compile_prog "" "$xen_libs"
|
compile_prog "" "$xen_libs"
|
||||||
then
|
then
|
||||||
xen_ctrl_version=470
|
xen_ctrl_version=40700
|
||||||
xen=yes
|
xen=yes
|
||||||
|
|
||||||
# Xen 4.6
|
# Xen 4.6
|
||||||
elif
|
elif
|
||||||
cat > $TMPC <<EOF &&
|
cat > $TMPC <<EOF &&
|
||||||
#include <xenctrl.h>
|
#include <xenctrl.h>
|
||||||
#include <xenstore.h>
|
#include <xenstore.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -2155,14 +2199,14 @@ int main(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
compile_prog "" "$xen_libs"
|
compile_prog "" "$xen_libs"
|
||||||
then
|
then
|
||||||
xen_ctrl_version=460
|
xen_ctrl_version=40600
|
||||||
xen=yes
|
xen=yes
|
||||||
|
|
||||||
# Xen 4.5
|
# Xen 4.5
|
||||||
elif
|
elif
|
||||||
cat > $TMPC <<EOF &&
|
cat > $TMPC <<EOF &&
|
||||||
#include <xenctrl.h>
|
#include <xenctrl.h>
|
||||||
#include <xenstore.h>
|
#include <xenstore.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -2182,13 +2226,13 @@ int main(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
compile_prog "" "$xen_libs"
|
compile_prog "" "$xen_libs"
|
||||||
then
|
then
|
||||||
xen_ctrl_version=450
|
xen_ctrl_version=40500
|
||||||
xen=yes
|
xen=yes
|
||||||
|
|
||||||
elif
|
elif
|
||||||
cat > $TMPC <<EOF &&
|
cat > $TMPC <<EOF &&
|
||||||
#include <xenctrl.h>
|
#include <xenctrl.h>
|
||||||
#include <xenstore.h>
|
#include <xenstore.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -2207,24 +2251,25 @@ int main(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
compile_prog "" "$xen_libs"
|
compile_prog "" "$xen_libs"
|
||||||
then
|
then
|
||||||
xen_ctrl_version=420
|
xen_ctrl_version=40200
|
||||||
xen=yes
|
xen=yes
|
||||||
|
|
||||||
else
|
else
|
||||||
if test "$xen" = "yes" ; then
|
if test "$xen" = "yes" ; then
|
||||||
feature_not_found "xen (unsupported version)" \
|
feature_not_found "xen (unsupported version)" \
|
||||||
"Install a supported xen (xen 4.2 or newer)"
|
"Install a supported xen (xen 4.2 or newer)"
|
||||||
|
fi
|
||||||
|
xen=no
|
||||||
fi
|
fi
|
||||||
xen=no
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test "$xen" = yes; then
|
if test "$xen" = yes; then
|
||||||
if test $xen_ctrl_version -ge 471 ; then
|
if test $xen_ctrl_version -ge 40701 ; then
|
||||||
libs_softmmu="$xen_stable_libs $libs_softmmu"
|
libs_softmmu="$xen_stable_libs $libs_softmmu"
|
||||||
|
fi
|
||||||
|
libs_softmmu="$xen_libs $libs_softmmu"
|
||||||
fi
|
fi
|
||||||
libs_softmmu="$xen_libs $libs_softmmu"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -4780,6 +4825,33 @@ if compile_prog "" "" ; then
|
|||||||
have_sysmacros=yes
|
have_sysmacros=yes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# Veritas HyperScale block driver VxHS
|
||||||
|
# Check if libvxhs is installed
|
||||||
|
|
||||||
|
if test "$vxhs" != "no" ; then
|
||||||
|
cat > $TMPC <<EOF
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <qnio/qnio_api.h>
|
||||||
|
|
||||||
|
void *vxhs_callback;
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
iio_init(QNIO_VERSION, vxhs_callback);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
vxhs_libs="-lvxhs -lssl"
|
||||||
|
if compile_prog "" "$vxhs_libs" ; then
|
||||||
|
vxhs=yes
|
||||||
|
else
|
||||||
|
if test "$vxhs" = "yes" ; then
|
||||||
|
feature_not_found "vxhs block device" "Install libvxhs See github"
|
||||||
|
fi
|
||||||
|
vxhs=no
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# End of CC checks
|
# End of CC checks
|
||||||
# After here, no more $cc or $ld runs
|
# After here, no more $cc or $ld runs
|
||||||
@@ -5146,6 +5218,7 @@ echo "tcmalloc support $tcmalloc"
|
|||||||
echo "jemalloc support $jemalloc"
|
echo "jemalloc support $jemalloc"
|
||||||
echo "avx2 optimization $avx2_opt"
|
echo "avx2 optimization $avx2_opt"
|
||||||
echo "replication support $replication"
|
echo "replication support $replication"
|
||||||
|
echo "VxHS block device $vxhs"
|
||||||
|
|
||||||
if test "$sdl_too_old" = "yes"; then
|
if test "$sdl_too_old" = "yes"; then
|
||||||
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
||||||
@@ -5785,6 +5858,11 @@ if test "$pthread_setname_np" = "yes" ; then
|
|||||||
echo "CONFIG_PTHREAD_SETNAME_NP=y" >> $config_host_mak
|
echo "CONFIG_PTHREAD_SETNAME_NP=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$vxhs" = "yes" ; then
|
||||||
|
echo "CONFIG_VXHS=y" >> $config_host_mak
|
||||||
|
echo "VXHS_LIBS=$vxhs_libs" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
if test "$tcg_interpreter" = "yes"; then
|
if test "$tcg_interpreter" = "yes"; then
|
||||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
|
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
|
||||||
elif test "$ARCH" = "sparc64" ; then
|
elif test "$ARCH" = "sparc64" ; then
|
||||||
|
@@ -473,10 +473,10 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
|
|||||||
* then encrypted.
|
* then encrypted.
|
||||||
*/
|
*/
|
||||||
rv = readfunc(block,
|
rv = readfunc(block,
|
||||||
|
opaque,
|
||||||
slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
||||||
splitkey, splitkeylen,
|
splitkey, splitkeylen,
|
||||||
errp,
|
errp);
|
||||||
opaque);
|
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -676,11 +676,10 @@ qcrypto_block_luks_open(QCryptoBlock *block,
|
|||||||
|
|
||||||
/* Read the entire LUKS header, minus the key material from
|
/* Read the entire LUKS header, minus the key material from
|
||||||
* the underlying device */
|
* the underlying device */
|
||||||
rv = readfunc(block, 0,
|
rv = readfunc(block, opaque, 0,
|
||||||
(uint8_t *)&luks->header,
|
(uint8_t *)&luks->header,
|
||||||
sizeof(luks->header),
|
sizeof(luks->header),
|
||||||
errp,
|
errp);
|
||||||
opaque);
|
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
ret = rv;
|
ret = rv;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -1246,7 +1245,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
|
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
|
||||||
|
|
||||||
/* Reserve header space to match payload offset */
|
/* Reserve header space to match payload offset */
|
||||||
initfunc(block, block->payload_offset, &local_err, opaque);
|
initfunc(block, opaque, block->payload_offset, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto error;
|
goto error;
|
||||||
@@ -1268,11 +1267,10 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
|
|
||||||
|
|
||||||
/* Write out the partition header and key slot headers */
|
/* Write out the partition header and key slot headers */
|
||||||
writefunc(block, 0,
|
writefunc(block, opaque, 0,
|
||||||
(const uint8_t *)&luks->header,
|
(const uint8_t *)&luks->header,
|
||||||
sizeof(luks->header),
|
sizeof(luks->header),
|
||||||
&local_err,
|
&local_err);
|
||||||
opaque);
|
|
||||||
|
|
||||||
/* Delay checking local_err until we've byte-swapped */
|
/* Delay checking local_err until we've byte-swapped */
|
||||||
|
|
||||||
@@ -1297,12 +1295,11 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
|
|
||||||
/* Write out the master key material, starting at the
|
/* Write out the master key material, starting at the
|
||||||
* sector immediately following the partition header. */
|
* sector immediately following the partition header. */
|
||||||
if (writefunc(block,
|
if (writefunc(block, opaque,
|
||||||
luks->header.key_slots[0].key_offset *
|
luks->header.key_slots[0].key_offset *
|
||||||
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
||||||
splitkey, splitkeylen,
|
splitkey, splitkeylen,
|
||||||
errp,
|
errp) != splitkeylen) {
|
||||||
opaque) != splitkeylen) {
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,6 +29,7 @@ CONFIG_LAN9118=y
|
|||||||
CONFIG_SMC91C111=y
|
CONFIG_SMC91C111=y
|
||||||
CONFIG_ALLWINNER_EMAC=y
|
CONFIG_ALLWINNER_EMAC=y
|
||||||
CONFIG_IMX_FEC=y
|
CONFIG_IMX_FEC=y
|
||||||
|
CONFIG_FTGMAC100=y
|
||||||
CONFIG_DS1338=y
|
CONFIG_DS1338=y
|
||||||
CONFIG_PFLASH_CFI01=y
|
CONFIG_PFLASH_CFI01=y
|
||||||
CONFIG_PFLASH_CFI02=y
|
CONFIG_PFLASH_CFI02=y
|
||||||
|
@@ -39,7 +39,6 @@ CONFIG_TPM_TIS=$(CONFIG_TPM)
|
|||||||
CONFIG_MC146818RTC=y
|
CONFIG_MC146818RTC=y
|
||||||
CONFIG_PCI_PIIX=y
|
CONFIG_PCI_PIIX=y
|
||||||
CONFIG_WDT_IB700=y
|
CONFIG_WDT_IB700=y
|
||||||
CONFIG_XEN_I386=$(CONFIG_XEN)
|
|
||||||
CONFIG_ISA_DEBUG=y
|
CONFIG_ISA_DEBUG=y
|
||||||
CONFIG_ISA_TESTDEV=y
|
CONFIG_ISA_TESTDEV=y
|
||||||
CONFIG_VMPORT=y
|
CONFIG_VMPORT=y
|
||||||
|
@@ -45,6 +45,7 @@ CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
|||||||
CONFIG_PLATFORM_BUS=y
|
CONFIG_PLATFORM_BUS=y
|
||||||
CONFIG_ETSEC=y
|
CONFIG_ETSEC=y
|
||||||
CONFIG_LIBDECNUMBER=y
|
CONFIG_LIBDECNUMBER=y
|
||||||
|
CONFIG_SM501=y
|
||||||
# For PReP
|
# For PReP
|
||||||
CONFIG_SERIAL_ISA=y
|
CONFIG_SERIAL_ISA=y
|
||||||
CONFIG_MC146818RTC=y
|
CONFIG_MC146818RTC=y
|
||||||
|
@@ -6,6 +6,10 @@ include usb.mak
|
|||||||
CONFIG_VIRTIO_VGA=y
|
CONFIG_VIRTIO_VGA=y
|
||||||
CONFIG_ESCC=y
|
CONFIG_ESCC=y
|
||||||
CONFIG_M48T59=y
|
CONFIG_M48T59=y
|
||||||
|
CONFIG_IPMI=y
|
||||||
|
CONFIG_IPMI_LOCAL=y
|
||||||
|
CONFIG_IPMI_EXTERN=y
|
||||||
|
CONFIG_ISA_IPMI_BT=y
|
||||||
CONFIG_SERIAL=y
|
CONFIG_SERIAL=y
|
||||||
CONFIG_PARALLEL=y
|
CONFIG_PARALLEL=y
|
||||||
CONFIG_I8254=y
|
CONFIG_I8254=y
|
||||||
@@ -47,6 +51,7 @@ CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
|||||||
CONFIG_PLATFORM_BUS=y
|
CONFIG_PLATFORM_BUS=y
|
||||||
CONFIG_ETSEC=y
|
CONFIG_ETSEC=y
|
||||||
CONFIG_LIBDECNUMBER=y
|
CONFIG_LIBDECNUMBER=y
|
||||||
|
CONFIG_SM501=y
|
||||||
# For pSeries
|
# For pSeries
|
||||||
CONFIG_XICS=$(CONFIG_PSERIES)
|
CONFIG_XICS=$(CONFIG_PSERIES)
|
||||||
CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
|
CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
|
||||||
|
@@ -15,3 +15,4 @@ CONFIG_I8259=y
|
|||||||
CONFIG_XILINX=y
|
CONFIG_XILINX=y
|
||||||
CONFIG_XILINX_ETHLITE=y
|
CONFIG_XILINX_ETHLITE=y
|
||||||
CONFIG_LIBDECNUMBER=y
|
CONFIG_LIBDECNUMBER=y
|
||||||
|
CONFIG_SM501=y
|
||||||
|
@@ -39,7 +39,6 @@ CONFIG_TPM_TIS=$(CONFIG_TPM)
|
|||||||
CONFIG_MC146818RTC=y
|
CONFIG_MC146818RTC=y
|
||||||
CONFIG_PCI_PIIX=y
|
CONFIG_PCI_PIIX=y
|
||||||
CONFIG_WDT_IB700=y
|
CONFIG_WDT_IB700=y
|
||||||
CONFIG_XEN_I386=$(CONFIG_XEN)
|
|
||||||
CONFIG_ISA_DEBUG=y
|
CONFIG_ISA_DEBUG=y
|
||||||
CONFIG_ISA_TESTDEV=y
|
CONFIG_ISA_TESTDEV=y
|
||||||
CONFIG_VMPORT=y
|
CONFIG_VMPORT=y
|
||||||
|
86
exec.c
86
exec.c
@@ -223,6 +223,12 @@ struct CPUAddressSpace {
|
|||||||
MemoryListener tcg_as_listener;
|
MemoryListener tcg_as_listener;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DirtyBitmapSnapshot {
|
||||||
|
ram_addr_t start;
|
||||||
|
ram_addr_t end;
|
||||||
|
unsigned long dirty[];
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
@@ -1061,6 +1067,75 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
|
|||||||
return dirty;
|
return dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty
|
||||||
|
(ram_addr_t start, ram_addr_t length, unsigned client)
|
||||||
|
{
|
||||||
|
DirtyMemoryBlocks *blocks;
|
||||||
|
unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL);
|
||||||
|
ram_addr_t first = QEMU_ALIGN_DOWN(start, align);
|
||||||
|
ram_addr_t last = QEMU_ALIGN_UP(start + length, align);
|
||||||
|
DirtyBitmapSnapshot *snap;
|
||||||
|
unsigned long page, end, dest;
|
||||||
|
|
||||||
|
snap = g_malloc0(sizeof(*snap) +
|
||||||
|
((last - first) >> (TARGET_PAGE_BITS + 3)));
|
||||||
|
snap->start = first;
|
||||||
|
snap->end = last;
|
||||||
|
|
||||||
|
page = first >> TARGET_PAGE_BITS;
|
||||||
|
end = last >> TARGET_PAGE_BITS;
|
||||||
|
dest = 0;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
|
||||||
|
|
||||||
|
while (page < end) {
|
||||||
|
unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
|
||||||
|
unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
|
||||||
|
unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset);
|
||||||
|
|
||||||
|
assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL)));
|
||||||
|
assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL)));
|
||||||
|
offset >>= BITS_PER_LEVEL;
|
||||||
|
|
||||||
|
bitmap_copy_and_clear_atomic(snap->dirty + dest,
|
||||||
|
blocks->blocks[idx] + offset,
|
||||||
|
num);
|
||||||
|
page += num;
|
||||||
|
dest += num >> BITS_PER_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (tcg_enabled()) {
|
||||||
|
tlb_reset_dirty_range_all(start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return snap;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap,
|
||||||
|
ram_addr_t start,
|
||||||
|
ram_addr_t length)
|
||||||
|
{
|
||||||
|
unsigned long page, end;
|
||||||
|
|
||||||
|
assert(start >= snap->start);
|
||||||
|
assert(start + length <= snap->end);
|
||||||
|
|
||||||
|
end = TARGET_PAGE_ALIGN(start + length - snap->start) >> TARGET_PAGE_BITS;
|
||||||
|
page = (start - snap->start) >> TARGET_PAGE_BITS;
|
||||||
|
|
||||||
|
while (page < end) {
|
||||||
|
if (test_bit(page, snap->dirty)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Called from RCU critical section */
|
/* Called from RCU critical section */
|
||||||
hwaddr memory_region_section_get_iotlb(CPUState *cpu,
|
hwaddr memory_region_section_get_iotlb(CPUState *cpu,
|
||||||
MemoryRegionSection *section,
|
MemoryRegionSection *section,
|
||||||
@@ -1528,7 +1603,7 @@ static ram_addr_t find_ram_offset(ram_addr_t size)
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
ram_addr_t last_ram_offset(void)
|
unsigned long last_ram_page(void)
|
||||||
{
|
{
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
ram_addr_t last = 0;
|
ram_addr_t last = 0;
|
||||||
@@ -1538,7 +1613,7 @@ ram_addr_t last_ram_offset(void)
|
|||||||
last = MAX(last, block->offset + block->max_length);
|
last = MAX(last, block->offset + block->max_length);
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return last;
|
return last >> TARGET_PAGE_BITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_ram_setup_dump(void *addr, ram_addr_t size)
|
static void qemu_ram_setup_dump(void *addr, ram_addr_t size)
|
||||||
@@ -1727,7 +1802,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
|||||||
ram_addr_t old_ram_size, new_ram_size;
|
ram_addr_t old_ram_size, new_ram_size;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
old_ram_size = last_ram_offset() >> TARGET_PAGE_BITS;
|
old_ram_size = last_ram_page();
|
||||||
|
|
||||||
qemu_mutex_lock_ramlist();
|
qemu_mutex_lock_ramlist();
|
||||||
new_block->offset = find_ram_offset(new_block->max_length);
|
new_block->offset = find_ram_offset(new_block->max_length);
|
||||||
@@ -1758,7 +1833,6 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
|||||||
new_ram_size = MAX(old_ram_size,
|
new_ram_size = MAX(old_ram_size,
|
||||||
(new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS);
|
(new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS);
|
||||||
if (new_ram_size > old_ram_size) {
|
if (new_ram_size > old_ram_size) {
|
||||||
migration_bitmap_extend(old_ram_size, new_ram_size);
|
|
||||||
dirty_memory_extend(old_ram_size, new_ram_size);
|
dirty_memory_extend(old_ram_size, new_ram_size);
|
||||||
}
|
}
|
||||||
/* Keep the list sorted from biggest to smallest block. Unlike QTAILQ,
|
/* Keep the list sorted from biggest to smallest block. Unlike QTAILQ,
|
||||||
@@ -3307,9 +3381,9 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
|||||||
* Allows code that needs to deal with migration bitmaps etc to still be built
|
* Allows code that needs to deal with migration bitmaps etc to still be built
|
||||||
* target independent.
|
* target independent.
|
||||||
*/
|
*/
|
||||||
size_t qemu_target_page_bits(void)
|
size_t qemu_target_page_size(void)
|
||||||
{
|
{
|
||||||
return TARGET_PAGE_BITS;
|
return TARGET_PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -523,6 +523,38 @@ Dump 80 16 bit values at the start of the video memory.
|
|||||||
0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
|
0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
|
||||||
@end smallexample
|
@end smallexample
|
||||||
@end itemize
|
@end itemize
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "gpa2hva",
|
||||||
|
.args_type = "addr:l",
|
||||||
|
.params = "addr",
|
||||||
|
.help = "print the host virtual address corresponding to a guest physical address",
|
||||||
|
.cmd = hmp_gpa2hva,
|
||||||
|
},
|
||||||
|
|
||||||
|
STEXI
|
||||||
|
@item gpa2hva @var{addr}
|
||||||
|
@findex gpa2hva
|
||||||
|
Print the host virtual address at which the guest's physical address @var{addr}
|
||||||
|
is mapped.
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
#ifdef CONFIG_LINUX
|
||||||
|
{
|
||||||
|
.name = "gpa2hpa",
|
||||||
|
.args_type = "addr:l",
|
||||||
|
.params = "addr",
|
||||||
|
.help = "print the host physical address corresponding to a guest physical address",
|
||||||
|
.cmd = hmp_gpa2hpa,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STEXI
|
||||||
|
@item gpa2hpa @var{addr}
|
||||||
|
@findex gpa2hpa
|
||||||
|
Print the host physical address at which the guest's physical address @var{addr}
|
||||||
|
is mapped.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
29
hmp.c
29
hmp.c
@@ -215,6 +215,9 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
|
|||||||
info->ram->normal_bytes >> 10);
|
info->ram->normal_bytes >> 10);
|
||||||
monitor_printf(mon, "dirty sync count: %" PRIu64 "\n",
|
monitor_printf(mon, "dirty sync count: %" PRIu64 "\n",
|
||||||
info->ram->dirty_sync_count);
|
info->ram->dirty_sync_count);
|
||||||
|
monitor_printf(mon, "page size: %" PRIu64 " kbytes\n",
|
||||||
|
info->ram->page_size >> 10);
|
||||||
|
|
||||||
if (info->ram->dirty_pages_rate) {
|
if (info->ram->dirty_pages_rate) {
|
||||||
monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n",
|
monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n",
|
||||||
info->ram->dirty_pages_rate);
|
info->ram->dirty_pages_rate);
|
||||||
@@ -265,13 +268,11 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
|
|||||||
caps = qmp_query_migrate_capabilities(NULL);
|
caps = qmp_query_migrate_capabilities(NULL);
|
||||||
|
|
||||||
if (caps) {
|
if (caps) {
|
||||||
monitor_printf(mon, "capabilities: ");
|
|
||||||
for (cap = caps; cap; cap = cap->next) {
|
for (cap = caps; cap; cap = cap->next) {
|
||||||
monitor_printf(mon, "%s: %s ",
|
monitor_printf(mon, "%s: %s\n",
|
||||||
MigrationCapability_lookup[cap->value->capability],
|
MigrationCapability_lookup[cap->value->capability],
|
||||||
cap->value->state ? "on" : "off");
|
cap->value->state ? "on" : "off");
|
||||||
}
|
}
|
||||||
monitor_printf(mon, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_free_MigrationCapabilityStatusList(caps);
|
qapi_free_MigrationCapabilityStatusList(caps);
|
||||||
@@ -284,46 +285,44 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
|
|||||||
params = qmp_query_migrate_parameters(NULL);
|
params = qmp_query_migrate_parameters(NULL);
|
||||||
|
|
||||||
if (params) {
|
if (params) {
|
||||||
monitor_printf(mon, "parameters:");
|
|
||||||
assert(params->has_compress_level);
|
assert(params->has_compress_level);
|
||||||
monitor_printf(mon, " %s: %" PRId64,
|
monitor_printf(mon, "%s: %" PRId64 "\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_LEVEL],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_LEVEL],
|
||||||
params->compress_level);
|
params->compress_level);
|
||||||
assert(params->has_compress_threads);
|
assert(params->has_compress_threads);
|
||||||
monitor_printf(mon, " %s: %" PRId64,
|
monitor_printf(mon, "%s: %" PRId64 "\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_THREADS],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_THREADS],
|
||||||
params->compress_threads);
|
params->compress_threads);
|
||||||
assert(params->has_decompress_threads);
|
assert(params->has_decompress_threads);
|
||||||
monitor_printf(mon, " %s: %" PRId64,
|
monitor_printf(mon, "%s: %" PRId64 "\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_DECOMPRESS_THREADS],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_DECOMPRESS_THREADS],
|
||||||
params->decompress_threads);
|
params->decompress_threads);
|
||||||
assert(params->has_cpu_throttle_initial);
|
assert(params->has_cpu_throttle_initial);
|
||||||
monitor_printf(mon, " %s: %" PRId64,
|
monitor_printf(mon, "%s: %" PRId64 "\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL],
|
||||||
params->cpu_throttle_initial);
|
params->cpu_throttle_initial);
|
||||||
assert(params->has_cpu_throttle_increment);
|
assert(params->has_cpu_throttle_increment);
|
||||||
monitor_printf(mon, " %s: %" PRId64,
|
monitor_printf(mon, "%s: %" PRId64 "\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT],
|
||||||
params->cpu_throttle_increment);
|
params->cpu_throttle_increment);
|
||||||
monitor_printf(mon, " %s: '%s'",
|
monitor_printf(mon, "%s: '%s'\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_CREDS],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_CREDS],
|
||||||
params->has_tls_creds ? params->tls_creds : "");
|
params->has_tls_creds ? params->tls_creds : "");
|
||||||
monitor_printf(mon, " %s: '%s'",
|
monitor_printf(mon, "%s: '%s'\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_HOSTNAME],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_HOSTNAME],
|
||||||
params->has_tls_hostname ? params->tls_hostname : "");
|
params->has_tls_hostname ? params->tls_hostname : "");
|
||||||
assert(params->has_max_bandwidth);
|
assert(params->has_max_bandwidth);
|
||||||
monitor_printf(mon, " %s: %" PRId64 " bytes/second",
|
monitor_printf(mon, "%s: %" PRId64 " bytes/second\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_MAX_BANDWIDTH],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_MAX_BANDWIDTH],
|
||||||
params->max_bandwidth);
|
params->max_bandwidth);
|
||||||
assert(params->has_downtime_limit);
|
assert(params->has_downtime_limit);
|
||||||
monitor_printf(mon, " %s: %" PRId64 " milliseconds",
|
monitor_printf(mon, "%s: %" PRId64 " milliseconds\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_DOWNTIME_LIMIT],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_DOWNTIME_LIMIT],
|
||||||
params->downtime_limit);
|
params->downtime_limit);
|
||||||
assert(params->has_x_checkpoint_delay);
|
assert(params->has_x_checkpoint_delay);
|
||||||
monitor_printf(mon, " %s: %" PRId64,
|
monitor_printf(mon, "%s: %" PRId64 "\n",
|
||||||
MigrationParameter_lookup[MIGRATION_PARAMETER_X_CHECKPOINT_DELAY],
|
MigrationParameter_lookup[MIGRATION_PARAMETER_X_CHECKPOINT_DELAY],
|
||||||
params->x_checkpoint_delay);
|
params->x_checkpoint_delay);
|
||||||
monitor_printf(mon, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_free_MigrationParameters(params);
|
qapi_free_MigrationParameters(params);
|
||||||
|
@@ -1098,8 +1098,13 @@ static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
|
|||||||
{
|
{
|
||||||
if (dir_path) {
|
if (dir_path) {
|
||||||
v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
|
v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
|
||||||
} else {
|
} else if (strcmp(name, "/")) {
|
||||||
v9fs_path_sprintf(target, "%s", name);
|
v9fs_path_sprintf(target, "%s", name);
|
||||||
|
} else {
|
||||||
|
/* We want the path of the export root to be relative, otherwise
|
||||||
|
* "*at()" syscalls would treat it as "/" in the host.
|
||||||
|
*/
|
||||||
|
v9fs_path_sprintf(target, "%s", ".");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -119,6 +119,12 @@ static inline char *rpath(FsContext *ctx, const char *path)
|
|||||||
typedef struct V9fsPDU V9fsPDU;
|
typedef struct V9fsPDU V9fsPDU;
|
||||||
struct V9fsState;
|
struct V9fsState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t size_le;
|
||||||
|
uint8_t id;
|
||||||
|
uint16_t tag_le;
|
||||||
|
} QEMU_PACKED P9MsgHeader;
|
||||||
|
|
||||||
struct V9fsPDU
|
struct V9fsPDU
|
||||||
{
|
{
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
@@ -5,5 +5,6 @@ common-obj-y += coth.o cofs.o codir.o cofile.o
|
|||||||
common-obj-y += coxattr.o 9p-synth.o
|
common-obj-y += coxattr.o 9p-synth.o
|
||||||
common-obj-$(CONFIG_OPEN_BY_HANDLE) += 9p-handle.o
|
common-obj-$(CONFIG_OPEN_BY_HANDLE) += 9p-handle.o
|
||||||
common-obj-y += 9p-proxy.o
|
common-obj-y += 9p-proxy.o
|
||||||
|
common-obj-$(CONFIG_XEN) += xen-9p-backend.o
|
||||||
|
|
||||||
obj-y += virtio-9p-device.o
|
obj-y += virtio-9p-device.o
|
||||||
|
@@ -46,11 +46,7 @@ static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
|
|||||||
VirtQueueElement *elem;
|
VirtQueueElement *elem;
|
||||||
|
|
||||||
while ((pdu = pdu_alloc(s))) {
|
while ((pdu = pdu_alloc(s))) {
|
||||||
struct {
|
P9MsgHeader out;
|
||||||
uint32_t size_le;
|
|
||||||
uint8_t id;
|
|
||||||
uint16_t tag_le;
|
|
||||||
} QEMU_PACKED out;
|
|
||||||
|
|
||||||
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
|
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
|
||||||
if (!elem) {
|
if (!elem) {
|
||||||
|
440
hw/9pfs/xen-9p-backend.c
Normal file
440
hw/9pfs/xen-9p-backend.c
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
/*
|
||||||
|
* Xen 9p backend
|
||||||
|
*
|
||||||
|
* Copyright Aporeto 2017
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stefano Stabellini <stefano@aporeto.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "hw/hw.h"
|
||||||
|
#include "hw/9pfs/9p.h"
|
||||||
|
#include "hw/xen/xen_backend.h"
|
||||||
|
#include "hw/9pfs/xen-9pfs.h"
|
||||||
|
#include "qemu/config-file.h"
|
||||||
|
#include "fsdev/qemu-fsdev.h"
|
||||||
|
|
||||||
|
#define VERSIONS "1"
|
||||||
|
#define MAX_RINGS 8
|
||||||
|
#define MAX_RING_ORDER 8
|
||||||
|
|
||||||
|
typedef struct Xen9pfsRing {
|
||||||
|
struct Xen9pfsDev *priv;
|
||||||
|
|
||||||
|
int ref;
|
||||||
|
xenevtchn_handle *evtchndev;
|
||||||
|
int evtchn;
|
||||||
|
int local_port;
|
||||||
|
int ring_order;
|
||||||
|
struct xen_9pfs_data_intf *intf;
|
||||||
|
unsigned char *data;
|
||||||
|
struct xen_9pfs_data ring;
|
||||||
|
|
||||||
|
struct iovec *sg;
|
||||||
|
QEMUBH *bh;
|
||||||
|
|
||||||
|
/* local copies, so that we can read/write PDU data directly from
|
||||||
|
* the ring */
|
||||||
|
RING_IDX out_cons, out_size, in_cons;
|
||||||
|
bool inprogress;
|
||||||
|
} Xen9pfsRing;
|
||||||
|
|
||||||
|
typedef struct Xen9pfsDev {
|
||||||
|
struct XenDevice xendev; /* must be first */
|
||||||
|
V9fsState state;
|
||||||
|
char *path;
|
||||||
|
char *security_model;
|
||||||
|
char *tag;
|
||||||
|
char *id;
|
||||||
|
|
||||||
|
int num_rings;
|
||||||
|
Xen9pfsRing *rings;
|
||||||
|
} Xen9pfsDev;
|
||||||
|
|
||||||
|
static void xen_9pfs_in_sg(Xen9pfsRing *ring,
|
||||||
|
struct iovec *in_sg,
|
||||||
|
int *num,
|
||||||
|
uint32_t idx,
|
||||||
|
uint32_t size)
|
||||||
|
{
|
||||||
|
RING_IDX cons, prod, masked_prod, masked_cons;
|
||||||
|
|
||||||
|
cons = ring->intf->in_cons;
|
||||||
|
prod = ring->intf->in_prod;
|
||||||
|
xen_rmb();
|
||||||
|
masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order));
|
||||||
|
masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order));
|
||||||
|
|
||||||
|
if (masked_prod < masked_cons) {
|
||||||
|
in_sg[0].iov_base = ring->ring.in + masked_prod;
|
||||||
|
in_sg[0].iov_len = masked_cons - masked_prod;
|
||||||
|
*num = 1;
|
||||||
|
} else {
|
||||||
|
in_sg[0].iov_base = ring->ring.in + masked_prod;
|
||||||
|
in_sg[0].iov_len = XEN_FLEX_RING_SIZE(ring->ring_order) - masked_prod;
|
||||||
|
in_sg[1].iov_base = ring->ring.in;
|
||||||
|
in_sg[1].iov_len = masked_cons;
|
||||||
|
*num = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_9pfs_out_sg(Xen9pfsRing *ring,
|
||||||
|
struct iovec *out_sg,
|
||||||
|
int *num,
|
||||||
|
uint32_t idx)
|
||||||
|
{
|
||||||
|
RING_IDX cons, prod, masked_prod, masked_cons;
|
||||||
|
|
||||||
|
cons = ring->intf->out_cons;
|
||||||
|
prod = ring->intf->out_prod;
|
||||||
|
xen_rmb();
|
||||||
|
masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order));
|
||||||
|
masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order));
|
||||||
|
|
||||||
|
if (masked_cons < masked_prod) {
|
||||||
|
out_sg[0].iov_base = ring->ring.out + masked_cons;
|
||||||
|
out_sg[0].iov_len = ring->out_size;
|
||||||
|
*num = 1;
|
||||||
|
} else {
|
||||||
|
if (ring->out_size >
|
||||||
|
(XEN_FLEX_RING_SIZE(ring->ring_order) - masked_cons)) {
|
||||||
|
out_sg[0].iov_base = ring->ring.out + masked_cons;
|
||||||
|
out_sg[0].iov_len = XEN_FLEX_RING_SIZE(ring->ring_order) -
|
||||||
|
masked_cons;
|
||||||
|
out_sg[1].iov_base = ring->ring.out;
|
||||||
|
out_sg[1].iov_len = ring->out_size -
|
||||||
|
(XEN_FLEX_RING_SIZE(ring->ring_order) -
|
||||||
|
masked_cons);
|
||||||
|
*num = 2;
|
||||||
|
} else {
|
||||||
|
out_sg[0].iov_base = ring->ring.out + masked_cons;
|
||||||
|
out_sg[0].iov_len = ring->out_size;
|
||||||
|
*num = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu,
|
||||||
|
size_t offset,
|
||||||
|
const char *fmt,
|
||||||
|
va_list ap)
|
||||||
|
{
|
||||||
|
Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state);
|
||||||
|
struct iovec in_sg[2];
|
||||||
|
int num;
|
||||||
|
|
||||||
|
xen_9pfs_in_sg(&xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings],
|
||||||
|
in_sg, &num, pdu->idx, ROUND_UP(offset + 128, 512));
|
||||||
|
return v9fs_iov_vmarshal(in_sg, num, offset, 0, fmt, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu,
|
||||||
|
size_t offset,
|
||||||
|
const char *fmt,
|
||||||
|
va_list ap)
|
||||||
|
{
|
||||||
|
Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state);
|
||||||
|
struct iovec out_sg[2];
|
||||||
|
int num;
|
||||||
|
|
||||||
|
xen_9pfs_out_sg(&xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings],
|
||||||
|
out_sg, &num, pdu->idx);
|
||||||
|
return v9fs_iov_vunmarshal(out_sg, num, offset, 0, fmt, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu,
|
||||||
|
struct iovec **piov,
|
||||||
|
unsigned int *pniov)
|
||||||
|
{
|
||||||
|
Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state);
|
||||||
|
Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings];
|
||||||
|
int num;
|
||||||
|
|
||||||
|
g_free(ring->sg);
|
||||||
|
|
||||||
|
ring->sg = g_malloc0(sizeof(*ring->sg) * 2);
|
||||||
|
xen_9pfs_out_sg(ring, ring->sg, &num, pdu->idx);
|
||||||
|
*piov = ring->sg;
|
||||||
|
*pniov = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu,
|
||||||
|
struct iovec **piov,
|
||||||
|
unsigned int *pniov,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state);
|
||||||
|
Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings];
|
||||||
|
int num;
|
||||||
|
|
||||||
|
g_free(ring->sg);
|
||||||
|
|
||||||
|
ring->sg = g_malloc0(sizeof(*ring->sg) * 2);
|
||||||
|
xen_9pfs_in_sg(ring, ring->sg, &num, pdu->idx, size);
|
||||||
|
*piov = ring->sg;
|
||||||
|
*pniov = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_9pfs_push_and_notify(V9fsPDU *pdu)
|
||||||
|
{
|
||||||
|
RING_IDX prod;
|
||||||
|
Xen9pfsDev *priv = container_of(pdu->s, Xen9pfsDev, state);
|
||||||
|
Xen9pfsRing *ring = &priv->rings[pdu->tag % priv->num_rings];
|
||||||
|
|
||||||
|
g_free(ring->sg);
|
||||||
|
ring->sg = NULL;
|
||||||
|
|
||||||
|
ring->intf->out_cons = ring->out_cons;
|
||||||
|
xen_wmb();
|
||||||
|
|
||||||
|
prod = ring->intf->in_prod;
|
||||||
|
xen_rmb();
|
||||||
|
ring->intf->in_prod = prod + pdu->size;
|
||||||
|
xen_wmb();
|
||||||
|
|
||||||
|
ring->inprogress = false;
|
||||||
|
xenevtchn_notify(ring->evtchndev, ring->local_port);
|
||||||
|
|
||||||
|
qemu_bh_schedule(ring->bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct V9fsTransport xen_9p_transport = {
|
||||||
|
.pdu_vmarshal = xen_9pfs_pdu_vmarshal,
|
||||||
|
.pdu_vunmarshal = xen_9pfs_pdu_vunmarshal,
|
||||||
|
.init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu,
|
||||||
|
.init_out_iov_from_pdu = xen_9pfs_init_out_iov_from_pdu,
|
||||||
|
.push_and_notify = xen_9pfs_push_and_notify,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int xen_9pfs_init(struct XenDevice *xendev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xen_9pfs_receive(Xen9pfsRing *ring)
|
||||||
|
{
|
||||||
|
P9MsgHeader h;
|
||||||
|
RING_IDX cons, prod, masked_prod, masked_cons;
|
||||||
|
V9fsPDU *pdu;
|
||||||
|
|
||||||
|
if (ring->inprogress) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cons = ring->intf->out_cons;
|
||||||
|
prod = ring->intf->out_prod;
|
||||||
|
xen_rmb();
|
||||||
|
|
||||||
|
if (xen_9pfs_queued(prod, cons, XEN_FLEX_RING_SIZE(ring->ring_order)) <
|
||||||
|
sizeof(h)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ring->inprogress = true;
|
||||||
|
|
||||||
|
masked_prod = xen_9pfs_mask(prod, XEN_FLEX_RING_SIZE(ring->ring_order));
|
||||||
|
masked_cons = xen_9pfs_mask(cons, XEN_FLEX_RING_SIZE(ring->ring_order));
|
||||||
|
|
||||||
|
xen_9pfs_read_packet((uint8_t *) &h, ring->ring.out, sizeof(h),
|
||||||
|
masked_prod, &masked_cons,
|
||||||
|
XEN_FLEX_RING_SIZE(ring->ring_order));
|
||||||
|
|
||||||
|
/* cannot fail, because we only handle one request per ring at a time */
|
||||||
|
pdu = pdu_alloc(&ring->priv->state);
|
||||||
|
pdu->size = le32_to_cpu(h.size_le);
|
||||||
|
pdu->id = h.id;
|
||||||
|
pdu->tag = le32_to_cpu(h.tag_le);
|
||||||
|
ring->out_size = le32_to_cpu(h.size_le);
|
||||||
|
ring->out_cons = cons + le32_to_cpu(h.size_le);
|
||||||
|
|
||||||
|
qemu_co_queue_init(&pdu->complete);
|
||||||
|
pdu_submit(pdu);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_9pfs_bh(void *opaque)
|
||||||
|
{
|
||||||
|
Xen9pfsRing *ring = opaque;
|
||||||
|
xen_9pfs_receive(ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_9pfs_evtchn_event(void *opaque)
|
||||||
|
{
|
||||||
|
Xen9pfsRing *ring = opaque;
|
||||||
|
evtchn_port_t port;
|
||||||
|
|
||||||
|
port = xenevtchn_pending(ring->evtchndev);
|
||||||
|
xenevtchn_unmask(ring->evtchndev, port);
|
||||||
|
|
||||||
|
qemu_bh_schedule(ring->bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xen_9pfs_free(struct XenDevice *xendev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
|
||||||
|
|
||||||
|
g_free(xen_9pdev->id);
|
||||||
|
g_free(xen_9pdev->tag);
|
||||||
|
g_free(xen_9pdev->path);
|
||||||
|
g_free(xen_9pdev->security_model);
|
||||||
|
|
||||||
|
for (i = 0; i < xen_9pdev->num_rings; i++) {
|
||||||
|
if (xen_9pdev->rings[i].data != NULL) {
|
||||||
|
xengnttab_unmap(xen_9pdev->xendev.gnttabdev,
|
||||||
|
xen_9pdev->rings[i].data,
|
||||||
|
(1 << xen_9pdev->rings[i].ring_order));
|
||||||
|
}
|
||||||
|
if (xen_9pdev->rings[i].intf != NULL) {
|
||||||
|
xengnttab_unmap(xen_9pdev->xendev.gnttabdev,
|
||||||
|
xen_9pdev->rings[i].intf,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
if (xen_9pdev->rings[i].evtchndev > 0) {
|
||||||
|
qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev),
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
xenevtchn_unbind(xen_9pdev->rings[i].evtchndev,
|
||||||
|
xen_9pdev->rings[i].local_port);
|
||||||
|
}
|
||||||
|
if (xen_9pdev->rings[i].bh != NULL) {
|
||||||
|
qemu_bh_delete(xen_9pdev->rings[i].bh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free(xen_9pdev->rings);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xen_9pfs_connect(struct XenDevice *xendev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev);
|
||||||
|
V9fsState *s = &xen_9pdev->state;
|
||||||
|
QemuOpts *fsdev;
|
||||||
|
|
||||||
|
if (xenstore_read_fe_int(&xen_9pdev->xendev, "num-rings",
|
||||||
|
&xen_9pdev->num_rings) == -1 ||
|
||||||
|
xen_9pdev->num_rings > MAX_RINGS || xen_9pdev->num_rings < 1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xen_9pdev->rings = g_malloc0(xen_9pdev->num_rings * sizeof(Xen9pfsRing));
|
||||||
|
for (i = 0; i < xen_9pdev->num_rings; i++) {
|
||||||
|
char *str;
|
||||||
|
int ring_order;
|
||||||
|
|
||||||
|
xen_9pdev->rings[i].priv = xen_9pdev;
|
||||||
|
xen_9pdev->rings[i].evtchn = -1;
|
||||||
|
xen_9pdev->rings[i].local_port = -1;
|
||||||
|
|
||||||
|
str = g_strdup_printf("ring-ref%u", i);
|
||||||
|
if (xenstore_read_fe_int(&xen_9pdev->xendev, str,
|
||||||
|
&xen_9pdev->rings[i].ref) == -1) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
g_free(str);
|
||||||
|
str = g_strdup_printf("event-channel-%u", i);
|
||||||
|
if (xenstore_read_fe_int(&xen_9pdev->xendev, str,
|
||||||
|
&xen_9pdev->rings[i].evtchn) == -1) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
g_free(str);
|
||||||
|
|
||||||
|
xen_9pdev->rings[i].intf = xengnttab_map_grant_ref(
|
||||||
|
xen_9pdev->xendev.gnttabdev,
|
||||||
|
xen_9pdev->xendev.dom,
|
||||||
|
xen_9pdev->rings[i].ref,
|
||||||
|
PROT_READ | PROT_WRITE);
|
||||||
|
if (!xen_9pdev->rings[i].intf) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ring_order = xen_9pdev->rings[i].intf->ring_order;
|
||||||
|
if (ring_order > MAX_RING_ORDER) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
xen_9pdev->rings[i].ring_order = ring_order;
|
||||||
|
xen_9pdev->rings[i].data = xengnttab_map_domain_grant_refs(
|
||||||
|
xen_9pdev->xendev.gnttabdev,
|
||||||
|
(1 << ring_order),
|
||||||
|
xen_9pdev->xendev.dom,
|
||||||
|
xen_9pdev->rings[i].intf->ref,
|
||||||
|
PROT_READ | PROT_WRITE);
|
||||||
|
if (!xen_9pdev->rings[i].data) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
xen_9pdev->rings[i].ring.in = xen_9pdev->rings[i].data;
|
||||||
|
xen_9pdev->rings[i].ring.out = xen_9pdev->rings[i].data +
|
||||||
|
XEN_FLEX_RING_SIZE(ring_order);
|
||||||
|
|
||||||
|
xen_9pdev->rings[i].bh = qemu_bh_new(xen_9pfs_bh, &xen_9pdev->rings[i]);
|
||||||
|
xen_9pdev->rings[i].out_cons = 0;
|
||||||
|
xen_9pdev->rings[i].out_size = 0;
|
||||||
|
xen_9pdev->rings[i].inprogress = false;
|
||||||
|
|
||||||
|
|
||||||
|
xen_9pdev->rings[i].evtchndev = xenevtchn_open(NULL, 0);
|
||||||
|
if (xen_9pdev->rings[i].evtchndev == NULL) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
fcntl(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), F_SETFD, FD_CLOEXEC);
|
||||||
|
xen_9pdev->rings[i].local_port = xenevtchn_bind_interdomain
|
||||||
|
(xen_9pdev->rings[i].evtchndev,
|
||||||
|
xendev->dom,
|
||||||
|
xen_9pdev->rings[i].evtchn);
|
||||||
|
if (xen_9pdev->rings[i].local_port == -1) {
|
||||||
|
xen_pv_printf(xendev, 0,
|
||||||
|
"xenevtchn_bind_interdomain failed port=%d\n",
|
||||||
|
xen_9pdev->rings[i].evtchn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
|
||||||
|
qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev),
|
||||||
|
xen_9pfs_evtchn_event, NULL, &xen_9pdev->rings[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
xen_9pdev->security_model = xenstore_read_be_str(xendev, "security_model");
|
||||||
|
xen_9pdev->path = xenstore_read_be_str(xendev, "path");
|
||||||
|
xen_9pdev->id = s->fsconf.fsdev_id =
|
||||||
|
g_strdup_printf("xen9p%d", xendev->dev);
|
||||||
|
xen_9pdev->tag = s->fsconf.tag = xenstore_read_fe_str(xendev, "tag");
|
||||||
|
v9fs_register_transport(s, &xen_9p_transport);
|
||||||
|
fsdev = qemu_opts_create(qemu_find_opts("fsdev"),
|
||||||
|
s->fsconf.tag,
|
||||||
|
1, NULL);
|
||||||
|
qemu_opt_set(fsdev, "fsdriver", "local", NULL);
|
||||||
|
qemu_opt_set(fsdev, "path", xen_9pdev->path, NULL);
|
||||||
|
qemu_opt_set(fsdev, "security_model", xen_9pdev->security_model, NULL);
|
||||||
|
qemu_opts_set_id(fsdev, s->fsconf.fsdev_id);
|
||||||
|
qemu_fsdev_add(fsdev);
|
||||||
|
v9fs_device_realize_common(s, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
xen_9pfs_free(xendev);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_9pfs_alloc(struct XenDevice *xendev)
|
||||||
|
{
|
||||||
|
xenstore_write_be_str(xendev, "versions", VERSIONS);
|
||||||
|
xenstore_write_be_int(xendev, "max-rings", MAX_RINGS);
|
||||||
|
xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_9pfs_disconnect(struct XenDevice *xendev)
|
||||||
|
{
|
||||||
|
/* Dynamic hotplug of PV filesystems at runtime is not supported. */
|
||||||
|
}
|
||||||
|
|
||||||
|
struct XenDevOps xen_9pfs_ops = {
|
||||||
|
.size = sizeof(Xen9pfsDev),
|
||||||
|
.flags = DEVOPS_FLAG_NEED_GNTDEV,
|
||||||
|
.alloc = xen_9pfs_alloc,
|
||||||
|
.init = xen_9pfs_init,
|
||||||
|
.initialise = xen_9pfs_connect,
|
||||||
|
.disconnect = xen_9pfs_disconnect,
|
||||||
|
.free = xen_9pfs_free,
|
||||||
|
};
|
21
hw/9pfs/xen-9pfs.h
Normal file
21
hw/9pfs/xen-9pfs.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Xen 9p backend
|
||||||
|
*
|
||||||
|
* Copyright Aporeto 2017
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stefano Stabellini <stefano@aporeto.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL version 2 or
|
||||||
|
* later. See the COPYING file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xen/io/protocols.h>
|
||||||
|
#include "hw/xen/io/ring.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do not merge into xen-9p-backend.c: clang doesn't allow unused static
|
||||||
|
* inline functions in c files.
|
||||||
|
*/
|
||||||
|
DEFINE_XEN_FLEX_RING_AND_INTF(xen_9pfs);
|
@@ -118,12 +118,6 @@ static void aw_a10_class_init(ObjectClass *oc, void *data)
|
|||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
|
||||||
dc->realize = aw_a10_realize;
|
dc->realize = aw_a10_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 aw_a10_type_info = {
|
static const TypeInfo aw_a10_type_info = {
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#include "hw/char/serial.h"
|
#include "hw/char/serial.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "hw/i2c/aspeed_i2c.h"
|
#include "hw/i2c/aspeed_i2c.h"
|
||||||
|
#include "net/net.h"
|
||||||
|
|
||||||
#define ASPEED_SOC_UART_5_BASE 0x00184000
|
#define ASPEED_SOC_UART_5_BASE 0x00184000
|
||||||
#define ASPEED_SOC_IOMEM_SIZE 0x00200000
|
#define ASPEED_SOC_IOMEM_SIZE 0x00200000
|
||||||
@@ -33,6 +34,8 @@
|
|||||||
#define ASPEED_SOC_TIMER_BASE 0x1E782000
|
#define ASPEED_SOC_TIMER_BASE 0x1E782000
|
||||||
#define ASPEED_SOC_WDT_BASE 0x1E785000
|
#define ASPEED_SOC_WDT_BASE 0x1E785000
|
||||||
#define ASPEED_SOC_I2C_BASE 0x1E78A000
|
#define ASPEED_SOC_I2C_BASE 0x1E78A000
|
||||||
|
#define ASPEED_SOC_ETH1_BASE 0x1E660000
|
||||||
|
#define ASPEED_SOC_ETH2_BASE 0x1E680000
|
||||||
|
|
||||||
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
|
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
|
||||||
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
|
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
|
||||||
@@ -175,6 +178,10 @@ static void aspeed_soc_init(Object *obj)
|
|||||||
object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ASPEED_WDT);
|
object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ASPEED_WDT);
|
||||||
object_property_add_child(obj, "wdt", OBJECT(&s->wdt), NULL);
|
object_property_add_child(obj, "wdt", OBJECT(&s->wdt), NULL);
|
||||||
qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default());
|
qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default());
|
||||||
|
|
||||||
|
object_initialize(&s->ftgmac100, sizeof(s->ftgmac100), TYPE_FTGMAC100);
|
||||||
|
object_property_add_child(obj, "ftgmac100", OBJECT(&s->ftgmac100), NULL);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->ftgmac100), sysbus_get_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
||||||
@@ -299,6 +306,20 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, ASPEED_SOC_WDT_BASE);
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, ASPEED_SOC_WDT_BASE);
|
||||||
|
|
||||||
|
/* Net */
|
||||||
|
qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]);
|
||||||
|
object_property_set_bool(OBJECT(&s->ftgmac100), true, "aspeed", &err);
|
||||||
|
object_property_set_bool(OBJECT(&s->ftgmac100), true, "realized",
|
||||||
|
&local_err);
|
||||||
|
error_propagate(&err, local_err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100), 0, ASPEED_SOC_ETH1_BASE);
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100), 0,
|
||||||
|
qdev_get_gpio_in(DEVICE(&s->vic), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aspeed_soc_class_init(ObjectClass *oc, void *data)
|
static void aspeed_soc_class_init(ObjectClass *oc, void *data)
|
||||||
|
@@ -160,12 +160,6 @@ static void bcm2836_class_init(ObjectClass *oc, void *data)
|
|||||||
|
|
||||||
dc->props = bcm2836_props;
|
dc->props = bcm2836_props;
|
||||||
dc->realize = bcm2836_realize;
|
dc->realize = bcm2836_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 bcm2836_type_info = {
|
static const TypeInfo bcm2836_type_info = {
|
||||||
|
@@ -31,6 +31,9 @@
|
|||||||
#define KERNEL_LOAD_ADDR 0x00010000
|
#define KERNEL_LOAD_ADDR 0x00010000
|
||||||
#define KERNEL64_LOAD_ADDR 0x00080000
|
#define KERNEL64_LOAD_ADDR 0x00080000
|
||||||
|
|
||||||
|
#define ARM64_TEXT_OFFSET_OFFSET 8
|
||||||
|
#define ARM64_MAGIC_OFFSET 56
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FIXUP_NONE = 0, /* do nothing */
|
FIXUP_NONE = 0, /* do nothing */
|
||||||
FIXUP_TERMINATOR, /* end of insns */
|
FIXUP_TERMINATOR, /* end of insns */
|
||||||
@@ -768,6 +771,49 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
|
||||||
|
hwaddr *entry)
|
||||||
|
{
|
||||||
|
hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR;
|
||||||
|
uint8_t *buffer;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
/* On aarch64, it's the bootloader's job to uncompress the kernel. */
|
||||||
|
size = load_image_gzipped_buffer(filename, LOAD_IMAGE_MAX_GUNZIP_BYTES,
|
||||||
|
&buffer);
|
||||||
|
|
||||||
|
if (size < 0) {
|
||||||
|
gsize len;
|
||||||
|
|
||||||
|
/* Load as raw file otherwise */
|
||||||
|
if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
size = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check the arm64 magic header value -- very old kernels may not have it */
|
||||||
|
if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
|
||||||
|
uint64_t hdrvals[2];
|
||||||
|
|
||||||
|
/* The arm64 Image header has text_offset and image_size fields at 8 and
|
||||||
|
* 16 bytes into the Image header, respectively. The text_offset field
|
||||||
|
* is only valid if the image_size is non-zero.
|
||||||
|
*/
|
||||||
|
memcpy(&hdrvals, buffer + ARM64_TEXT_OFFSET_OFFSET, sizeof(hdrvals));
|
||||||
|
if (hdrvals[1] != 0) {
|
||||||
|
kernel_load_offset = le64_to_cpu(hdrvals[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*entry = mem_base + kernel_load_offset;
|
||||||
|
rom_add_blob_fixed(filename, buffer, size, *entry);
|
||||||
|
|
||||||
|
g_free(buffer);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
||||||
{
|
{
|
||||||
CPUState *cs;
|
CPUState *cs;
|
||||||
@@ -776,7 +822,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
int is_linux = 0;
|
int is_linux = 0;
|
||||||
uint64_t elf_entry, elf_low_addr, elf_high_addr;
|
uint64_t elf_entry, elf_low_addr, elf_high_addr;
|
||||||
int elf_machine;
|
int elf_machine;
|
||||||
hwaddr entry, kernel_load_offset;
|
hwaddr entry;
|
||||||
static const ARMInsnFixup *primary_loader;
|
static const ARMInsnFixup *primary_loader;
|
||||||
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
||||||
notifier, notifier);
|
notifier, notifier);
|
||||||
@@ -841,14 +887,12 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
|
|
||||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||||
primary_loader = bootloader_aarch64;
|
primary_loader = bootloader_aarch64;
|
||||||
kernel_load_offset = KERNEL64_LOAD_ADDR;
|
|
||||||
elf_machine = EM_AARCH64;
|
elf_machine = EM_AARCH64;
|
||||||
} else {
|
} else {
|
||||||
primary_loader = bootloader;
|
primary_loader = bootloader;
|
||||||
if (!info->write_board_setup) {
|
if (!info->write_board_setup) {
|
||||||
primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET;
|
primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET;
|
||||||
}
|
}
|
||||||
kernel_load_offset = KERNEL_LOAD_ADDR;
|
|
||||||
elf_machine = EM_ARM;
|
elf_machine = EM_ARM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -900,17 +944,15 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
|
kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
|
||||||
&is_linux, NULL, NULL);
|
&is_linux, NULL, NULL);
|
||||||
}
|
}
|
||||||
/* On aarch64, it's the bootloader's job to uncompress the kernel. */
|
|
||||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
|
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
|
||||||
entry = info->loader_start + kernel_load_offset;
|
kernel_size = load_aarch64_image(info->kernel_filename,
|
||||||
kernel_size = load_image_gzipped(info->kernel_filename, entry,
|
info->loader_start, &entry);
|
||||||
info->ram_size - kernel_load_offset);
|
|
||||||
is_linux = 1;
|
is_linux = 1;
|
||||||
}
|
} else if (kernel_size < 0) {
|
||||||
if (kernel_size < 0) {
|
/* 32-bit ARM */
|
||||||
entry = info->loader_start + kernel_load_offset;
|
entry = info->loader_start + KERNEL_LOAD_ADDR;
|
||||||
kernel_size = load_image_targphys(info->kernel_filename, entry,
|
kernel_size = load_image_targphys(info->kernel_filename, entry,
|
||||||
info->ram_size - kernel_load_offset);
|
info->ram_size - KERNEL_LOAD_ADDR);
|
||||||
is_linux = 1;
|
is_linux = 1;
|
||||||
}
|
}
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
|
@@ -101,12 +101,6 @@ static void digic_class_init(ObjectClass *oc, void *data)
|
|||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
|
||||||
dc->realize = digic_realize;
|
dc->realize = digic_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 digic_type_info = {
|
static const TypeInfo digic_type_info = {
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#include "hw/arm/arm.h"
|
#include "hw/arm/arm.h"
|
||||||
#include "hw/loader.h"
|
#include "hw/loader.h"
|
||||||
#include "hw/arm/exynos4210.h"
|
#include "hw/arm/exynos4210.h"
|
||||||
|
#include "hw/sd/sd.h"
|
||||||
#include "hw/usb/hcd-ehci.h"
|
#include "hw/usb/hcd-ehci.h"
|
||||||
|
|
||||||
#define EXYNOS4210_CHIPID_ADDR 0x10000000
|
#define EXYNOS4210_CHIPID_ADDR 0x10000000
|
||||||
@@ -72,6 +73,13 @@
|
|||||||
#define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000
|
#define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000
|
||||||
#define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000
|
#define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000
|
||||||
|
|
||||||
|
/* SD/MMC host controllers */
|
||||||
|
#define EXYNOS4210_SDHCI_CAPABILITIES 0x05E80080
|
||||||
|
#define EXYNOS4210_SDHCI_BASE_ADDR 0x12510000
|
||||||
|
#define EXYNOS4210_SDHCI_ADDR(n) (EXYNOS4210_SDHCI_BASE_ADDR + \
|
||||||
|
0x00010000 * (n))
|
||||||
|
#define EXYNOS4210_SDHCI_NUMBER 4
|
||||||
|
|
||||||
/* PMU SFR base address */
|
/* PMU SFR base address */
|
||||||
#define EXYNOS4210_PMU_BASE_ADDR 0x10020000
|
#define EXYNOS4210_PMU_BASE_ADDR 0x10020000
|
||||||
|
|
||||||
@@ -382,6 +390,27 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
|||||||
EXYNOS4210_UART3_FIFO_SIZE, 3, NULL,
|
EXYNOS4210_UART3_FIFO_SIZE, 3, NULL,
|
||||||
s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]);
|
s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]);
|
||||||
|
|
||||||
|
/*** SD/MMC host controllers ***/
|
||||||
|
for (n = 0; n < EXYNOS4210_SDHCI_NUMBER; n++) {
|
||||||
|
DeviceState *carddev;
|
||||||
|
BlockBackend *blk;
|
||||||
|
DriveInfo *di;
|
||||||
|
|
||||||
|
dev = qdev_create(NULL, "generic-sdhci");
|
||||||
|
qdev_prop_set_uint32(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
|
busdev = SYS_BUS_DEVICE(dev);
|
||||||
|
sysbus_mmio_map(busdev, 0, EXYNOS4210_SDHCI_ADDR(n));
|
||||||
|
sysbus_connect_irq(busdev, 0, s->irq_table[exynos4210_get_irq(29, n)]);
|
||||||
|
|
||||||
|
di = drive_get(IF_SD, 0, n);
|
||||||
|
blk = di ? blk_by_legacy_dinfo(di) : NULL;
|
||||||
|
carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
|
||||||
|
qdev_prop_set_drive(carddev, "drive", blk, &error_abort);
|
||||||
|
qdev_init_nofail(carddev);
|
||||||
|
}
|
||||||
|
|
||||||
/*** Display controller (FIMD) ***/
|
/*** Display controller (FIMD) ***/
|
||||||
sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR,
|
sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR,
|
||||||
s->irq_table[exynos4210_get_irq(11, 0)],
|
s->irq_table[exynos4210_get_irq(11, 0)],
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
@@ -101,9 +102,9 @@ static Exynos4210State *exynos4_boards_init_common(MachineState *machine,
|
|||||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||||
|
|
||||||
if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) {
|
if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) {
|
||||||
fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus"
|
error_report("%s board supports only %d CPU cores, ignoring smp_cpus"
|
||||||
" value.\n",
|
" value",
|
||||||
mc->name, EXYNOS4210_NCPUS);
|
mc->name, EXYNOS4210_NCPUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type];
|
exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type];
|
||||||
|
@@ -290,11 +290,6 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
|
|||||||
|
|
||||||
dc->realize = fsl_imx25_realize;
|
dc->realize = fsl_imx25_realize;
|
||||||
|
|
||||||
/*
|
|
||||||
* Reason: creates an ARM CPU, thus use after free(), see
|
|
||||||
* arm_cpu_class_init()
|
|
||||||
*/
|
|
||||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
|
||||||
dc->desc = "i.MX25 SOC";
|
dc->desc = "i.MX25 SOC";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -262,11 +262,6 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
|
|||||||
|
|
||||||
dc->realize = fsl_imx31_realize;
|
dc->realize = fsl_imx31_realize;
|
||||||
|
|
||||||
/*
|
|
||||||
* Reason: creates an ARM CPU, thus use after free(), see
|
|
||||||
* arm_cpu_class_init()
|
|
||||||
*/
|
|
||||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
|
||||||
dc->desc = "i.MX31 SOC";
|
dc->desc = "i.MX31 SOC";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -442,11 +442,6 @@ static void fsl_imx6_class_init(ObjectClass *oc, void *data)
|
|||||||
|
|
||||||
dc->realize = fsl_imx6_realize;
|
dc->realize = fsl_imx6_realize;
|
||||||
|
|
||||||
/*
|
|
||||||
* Reason: creates an ARM CPU, thus use after free(), see
|
|
||||||
* arm_cpu_class_init()
|
|
||||||
*/
|
|
||||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
|
||||||
dc->desc = "i.MX6 SOC";
|
dc->desc = "i.MX6 SOC";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -755,19 +755,18 @@ static void pxa2xx_ssp_reset(DeviceState *d)
|
|||||||
s->rx_start = s->rx_level = 0;
|
s->rx_start = s->rx_level = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_ssp_init(SysBusDevice *sbd)
|
static void pxa2xx_ssp_init(Object *obj)
|
||||||
{
|
{
|
||||||
DeviceState *dev = DEVICE(sbd);
|
DeviceState *dev = DEVICE(obj);
|
||||||
PXA2xxSSPState *s = PXA2XX_SSP(dev);
|
PXA2xxSSPState *s = PXA2XX_SSP(obj);
|
||||||
|
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||||
sysbus_init_irq(sbd, &s->irq);
|
sysbus_init_irq(sbd, &s->irq);
|
||||||
|
|
||||||
memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s,
|
memory_region_init_io(&s->iomem, obj, &pxa2xx_ssp_ops, s,
|
||||||
"pxa2xx-ssp", 0x1000);
|
"pxa2xx-ssp", 0x1000);
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
|
|
||||||
s->bus = ssi_create_bus(dev, "ssi");
|
s->bus = ssi_create_bus(dev, "ssi");
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Real-Time Clock */
|
/* Real-Time Clock */
|
||||||
@@ -2321,10 +2320,8 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
|
|||||||
|
|
||||||
static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data)
|
static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
sdc->init = pxa2xx_ssp_init;
|
|
||||||
dc->reset = pxa2xx_ssp_reset;
|
dc->reset = pxa2xx_ssp_reset;
|
||||||
dc->vmsd = &vmstate_pxa2xx_ssp;
|
dc->vmsd = &vmstate_pxa2xx_ssp;
|
||||||
}
|
}
|
||||||
@@ -2333,6 +2330,7 @@ static const TypeInfo pxa2xx_ssp_info = {
|
|||||||
.name = TYPE_PXA2XX_SSP,
|
.name = TYPE_PXA2XX_SSP,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
.instance_size = sizeof(PXA2xxSSPState),
|
.instance_size = sizeof(PXA2xxSSPState),
|
||||||
|
.instance_init = pxa2xx_ssp_init,
|
||||||
.class_init = pxa2xx_ssp_class_init,
|
.class_init = pxa2xx_ssp_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -108,7 +108,10 @@ static void gptm_reload(gptm_state *s, int n, int reset)
|
|||||||
} else if (s->mode[n] == 0xa) {
|
} else if (s->mode[n] == 0xa) {
|
||||||
/* PWM mode. Not implemented. */
|
/* PWM mode. Not implemented. */
|
||||||
} else {
|
} else {
|
||||||
hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]);
|
qemu_log_mask(LOG_UNIMP,
|
||||||
|
"GPTM: 16-bit timer mode unimplemented: 0x%x\n",
|
||||||
|
s->mode[n]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
s->tick[n] = tick;
|
s->tick[n] = tick;
|
||||||
timer_mod(s->timer[n], tick);
|
timer_mod(s->timer[n], tick);
|
||||||
@@ -149,7 +152,9 @@ static void gptm_tick(void *opaque)
|
|||||||
} else if (s->mode[n] == 0xa) {
|
} else if (s->mode[n] == 0xa) {
|
||||||
/* PWM mode. Not implemented. */
|
/* PWM mode. Not implemented. */
|
||||||
} else {
|
} else {
|
||||||
hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]);
|
qemu_log_mask(LOG_UNIMP,
|
||||||
|
"GPTM: 16-bit timer mode unimplemented: 0x%x\n",
|
||||||
|
s->mode[n]);
|
||||||
}
|
}
|
||||||
gptm_update_irq(s);
|
gptm_update_irq(s);
|
||||||
}
|
}
|
||||||
@@ -286,7 +291,8 @@ static void gptm_write(void *opaque, hwaddr offset,
|
|||||||
s->match_prescale[0] = value;
|
s->match_prescale[0] = value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
hw_error("gptm_write: Bad offset 0x%x\n", (int)offset);
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"GPTM: read at bad offset 0x%x\n", (int)offset);
|
||||||
}
|
}
|
||||||
gptm_update_irq(s);
|
gptm_update_irq(s);
|
||||||
}
|
}
|
||||||
@@ -425,7 +431,10 @@ static int ssys_board_class(const ssys_state *s)
|
|||||||
}
|
}
|
||||||
/* for unknown classes, fall through */
|
/* for unknown classes, fall through */
|
||||||
default:
|
default:
|
||||||
hw_error("ssys_board_class: Unknown class 0x%08x\n", did0);
|
/* This can only happen if the hardwired constant did0 value
|
||||||
|
* in this board's stellaris_board_info struct is wrong.
|
||||||
|
*/
|
||||||
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,8 +488,7 @@ static uint64_t ssys_read(void *opaque, hwaddr offset,
|
|||||||
case DID0_CLASS_SANDSTORM:
|
case DID0_CLASS_SANDSTORM:
|
||||||
return pllcfg_sandstorm[xtal];
|
return pllcfg_sandstorm[xtal];
|
||||||
default:
|
default:
|
||||||
hw_error("ssys_read: Unhandled class for PLLCFG read.\n");
|
g_assert_not_reached();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 0x070: /* RCC2 */
|
case 0x070: /* RCC2 */
|
||||||
@@ -512,7 +520,8 @@ static uint64_t ssys_read(void *opaque, hwaddr offset,
|
|||||||
case 0x1e4: /* USER1 */
|
case 0x1e4: /* USER1 */
|
||||||
return s->user1;
|
return s->user1;
|
||||||
default:
|
default:
|
||||||
hw_error("ssys_read: Bad offset 0x%x\n", (int)offset);
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"SSYS: read at bad offset 0x%x\n", (int)offset);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -614,7 +623,8 @@ static void ssys_write(void *opaque, hwaddr offset,
|
|||||||
s->ldoarst = value;
|
s->ldoarst = value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
hw_error("ssys_write: Bad offset 0x%x\n", (int)offset);
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"SSYS: write at bad offset 0x%x\n", (int)offset);
|
||||||
}
|
}
|
||||||
ssys_update(s);
|
ssys_update(s);
|
||||||
}
|
}
|
||||||
@@ -748,7 +758,8 @@ static uint64_t stellaris_i2c_read(void *opaque, hwaddr offset,
|
|||||||
case 0x20: /* MCR */
|
case 0x20: /* MCR */
|
||||||
return s->mcr;
|
return s->mcr;
|
||||||
default:
|
default:
|
||||||
hw_error("strllaris_i2c_read: Bad offset 0x%x\n", (int)offset);
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"stellaris_i2c: read at bad offset 0x%x\n", (int)offset);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -823,17 +834,18 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset,
|
|||||||
s->mris &= ~value;
|
s->mris &= ~value;
|
||||||
break;
|
break;
|
||||||
case 0x20: /* MCR */
|
case 0x20: /* MCR */
|
||||||
if (value & 1)
|
if (value & 1) {
|
||||||
hw_error(
|
qemu_log_mask(LOG_UNIMP, "stellaris_i2c: Loopback not implemented");
|
||||||
"stellaris_i2c_write: Loopback not implemented\n");
|
}
|
||||||
if (value & 0x20)
|
if (value & 0x20) {
|
||||||
hw_error(
|
qemu_log_mask(LOG_UNIMP,
|
||||||
"stellaris_i2c_write: Slave mode not implemented\n");
|
"stellaris_i2c: Slave mode not implemented");
|
||||||
|
}
|
||||||
s->mcr = value & 0x31;
|
s->mcr = value & 0x31;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
hw_error("stellaris_i2c_write: Bad offset 0x%x\n",
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
(int)offset);
|
"stellaris_i2c: write at bad offset 0x%x\n", (int)offset);
|
||||||
}
|
}
|
||||||
stellaris_i2c_update(s);
|
stellaris_i2c_update(s);
|
||||||
}
|
}
|
||||||
@@ -1057,8 +1069,8 @@ static uint64_t stellaris_adc_read(void *opaque, hwaddr offset,
|
|||||||
case 0x30: /* SAC */
|
case 0x30: /* SAC */
|
||||||
return s->sac;
|
return s->sac;
|
||||||
default:
|
default:
|
||||||
hw_error("strllaris_adc_read: Bad offset 0x%x\n",
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
(int)offset);
|
"stellaris_adc: read at bad offset 0x%x\n", (int)offset);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1078,8 +1090,9 @@ static void stellaris_adc_write(void *opaque, hwaddr offset,
|
|||||||
return;
|
return;
|
||||||
case 0x04: /* SSCTL */
|
case 0x04: /* SSCTL */
|
||||||
if (value != 6) {
|
if (value != 6) {
|
||||||
hw_error("ADC: Unimplemented sequence %" PRIx64 "\n",
|
qemu_log_mask(LOG_UNIMP,
|
||||||
value);
|
"ADC: Unimplemented sequence %" PRIx64 "\n",
|
||||||
|
value);
|
||||||
}
|
}
|
||||||
s->ssctl[n] = value;
|
s->ssctl[n] = value;
|
||||||
return;
|
return;
|
||||||
@@ -1110,13 +1123,14 @@ static void stellaris_adc_write(void *opaque, hwaddr offset,
|
|||||||
s->sspri = value;
|
s->sspri = value;
|
||||||
break;
|
break;
|
||||||
case 0x28: /* PSSI */
|
case 0x28: /* PSSI */
|
||||||
hw_error("Not implemented: ADC sample initiate\n");
|
qemu_log_mask(LOG_UNIMP, "ADC: sample initiate unimplemented");
|
||||||
break;
|
break;
|
||||||
case 0x30: /* SAC */
|
case 0x30: /* SAC */
|
||||||
s->sac = value;
|
s->sac = value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
hw_error("stellaris_adc_write: Bad offset 0x%x\n", (int)offset);
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"stellaris_adc: write at bad offset 0x%x\n", (int)offset);
|
||||||
}
|
}
|
||||||
stellaris_adc_update(s);
|
stellaris_adc_update(s);
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,8 @@
|
|||||||
#define ARM_PHYS_TIMER_PPI 30
|
#define ARM_PHYS_TIMER_PPI 30
|
||||||
#define ARM_VIRT_TIMER_PPI 27
|
#define ARM_VIRT_TIMER_PPI 27
|
||||||
|
|
||||||
|
#define GEM_REVISION 0x40070106
|
||||||
|
|
||||||
#define GIC_BASE_ADDR 0xf9000000
|
#define GIC_BASE_ADDR 0xf9000000
|
||||||
#define GIC_DIST_ADDR 0xf9010000
|
#define GIC_DIST_ADDR 0xf9010000
|
||||||
#define GIC_CPU_ADDR 0xf9020000
|
#define GIC_CPU_ADDR 0xf9020000
|
||||||
@@ -334,8 +336,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
|||||||
qemu_check_nic_model(nd, TYPE_CADENCE_GEM);
|
qemu_check_nic_model(nd, TYPE_CADENCE_GEM);
|
||||||
qdev_set_nic_properties(DEVICE(&s->gem[i]), nd);
|
qdev_set_nic_properties(DEVICE(&s->gem[i]), nd);
|
||||||
}
|
}
|
||||||
|
object_property_set_int(OBJECT(&s->gem[i]), GEM_REVISION, "revision",
|
||||||
|
&error_abort);
|
||||||
object_property_set_int(OBJECT(&s->gem[i]), 2, "num-priority-queues",
|
object_property_set_int(OBJECT(&s->gem[i]), 2, "num-priority-queues",
|
||||||
&error_abort);
|
&error_abort);
|
||||||
object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err);
|
object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
@@ -439,12 +443,6 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
|
|||||||
|
|
||||||
dc->props = xlnx_zynqmp_props;
|
dc->props = xlnx_zynqmp_props;
|
||||||
dc->realize = xlnx_zynqmp_realize;
|
dc->realize = xlnx_zynqmp_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 xlnx_zynqmp_type_info = {
|
static const TypeInfo xlnx_zynqmp_type_info = {
|
||||||
|
@@ -2521,8 +2521,8 @@ static void fdctrl_result_timer(void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Init functions */
|
/* Init functions */
|
||||||
static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp,
|
static void fdctrl_connect_drives(FDCtrl *fdctrl, DeviceState *fdc_dev,
|
||||||
DeviceState *fdc_dev)
|
Error **errp)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
FDrive *drive;
|
FDrive *drive;
|
||||||
@@ -2675,7 +2675,7 @@ static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
floppy_bus_create(fdctrl, &fdctrl->bus, dev);
|
floppy_bus_create(fdctrl, &fdctrl->bus, dev);
|
||||||
fdctrl_connect_drives(fdctrl, errp, dev);
|
fdctrl_connect_drives(fdctrl, dev, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionPortio fdc_portio_list[] = {
|
static const MemoryRegionPortio fdc_portio_list[] = {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#ifndef XEN_BLKIF_H
|
#ifndef XEN_BLKIF_H
|
||||||
#define XEN_BLKIF_H
|
#define XEN_BLKIF_H
|
||||||
|
|
||||||
#include <xen/io/ring.h>
|
#include "hw/xen/io/ring.h"
|
||||||
#include <xen/io/blkif.h>
|
#include <xen/io/blkif.h>
|
||||||
#include <xen/io/protocols.h>
|
#include <xen/io/protocols.h>
|
||||||
|
|
||||||
|
@@ -492,7 +492,7 @@ static int ioreq_map(struct ioreq *ioreq)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 480
|
#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800
|
||||||
|
|
||||||
static void ioreq_free_copy_buffers(struct ioreq *ioreq)
|
static void ioreq_free_copy_buffers(struct ioreq *ioreq)
|
||||||
{
|
{
|
||||||
|
@@ -102,7 +102,7 @@ typedef struct Exynos4210UartReg {
|
|||||||
uint32_t reset_value;
|
uint32_t reset_value;
|
||||||
} Exynos4210UartReg;
|
} Exynos4210UartReg;
|
||||||
|
|
||||||
static Exynos4210UartReg exynos4210_uart_regs[] = {
|
static const Exynos4210UartReg exynos4210_uart_regs[] = {
|
||||||
{"ULCON", ULCON, 0x00000000},
|
{"ULCON", ULCON, 0x00000000},
|
||||||
{"UCON", UCON, 0x00003000},
|
{"UCON", UCON, 0x00003000},
|
||||||
{"UFCON", UFCON, 0x00000000},
|
{"UFCON", UFCON, 0x00000000},
|
||||||
@@ -220,7 +220,7 @@ static uint8_t fifo_retrieve(Exynos4210UartFIFO *q)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fifo_elements_number(Exynos4210UartFIFO *q)
|
static int fifo_elements_number(const Exynos4210UartFIFO *q)
|
||||||
{
|
{
|
||||||
if (q->sp < q->rp) {
|
if (q->sp < q->rp) {
|
||||||
return q->size - q->rp + q->sp;
|
return q->size - q->rp + q->sp;
|
||||||
@@ -229,7 +229,7 @@ static int fifo_elements_number(Exynos4210UartFIFO *q)
|
|||||||
return q->sp - q->rp;
|
return q->sp - q->rp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fifo_empty_elements_number(Exynos4210UartFIFO *q)
|
static int fifo_empty_elements_number(const Exynos4210UartFIFO *q)
|
||||||
{
|
{
|
||||||
return q->size - fifo_elements_number(q);
|
return q->size - fifo_elements_number(q);
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ static void fifo_reset(Exynos4210UartFIFO *q)
|
|||||||
q->rp = 0;
|
q->rp = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s)
|
static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(const Exynos4210UartState *s)
|
||||||
{
|
{
|
||||||
uint32_t level = 0;
|
uint32_t level = 0;
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
|
@@ -40,6 +40,12 @@ static void machine_none_init(MachineState *mch)
|
|||||||
memory_region_allocate_system_memory(ram, NULL, "ram", mch->ram_size);
|
memory_region_allocate_system_memory(ram, NULL, "ram", mch->ram_size);
|
||||||
memory_region_add_subregion(get_system_memory(), 0, ram);
|
memory_region_add_subregion(get_system_memory(), 0, ram);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mch->kernel_filename) {
|
||||||
|
error_report("The -kernel parameter is not supported "
|
||||||
|
"(use the generic 'loader' device instead).");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void machine_none_machine_init(MachineClass *mc)
|
static void machine_none_machine_init(MachineClass *mc)
|
||||||
|
@@ -409,7 +409,7 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name,
|
|||||||
if (value) {
|
if (value) {
|
||||||
ref = blk_name(value);
|
ref = blk_name(value);
|
||||||
if (!*ref) {
|
if (!*ref) {
|
||||||
BlockDriverState *bs = blk_bs(value);
|
const BlockDriverState *bs = blk_bs(value);
|
||||||
if (bs) {
|
if (bs) {
|
||||||
ref = bdrv_get_node_name(bs);
|
ref = bdrv_get_node_name(bs);
|
||||||
}
|
}
|
||||||
|
@@ -1010,7 +1010,8 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value)
|
|||||||
object_property_set_str(OBJECT(dev), value, name, &error_abort);
|
object_property_set_str(OBJECT(dev), value, name, &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value)
|
void qdev_prop_set_macaddr(DeviceState *dev, const char *name,
|
||||||
|
const uint8_t *value)
|
||||||
{
|
{
|
||||||
char str[2 * 6 + 5 + 1];
|
char str[2 * 6 + 5 + 1];
|
||||||
snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
|
snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
@@ -39,9 +39,9 @@
|
|||||||
#include "qapi-event.h"
|
#include "qapi-event.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/migration.h"
|
||||||
|
|
||||||
int qdev_hotplug = 0;
|
bool qdev_hotplug = false;
|
||||||
static bool qdev_hot_added = false;
|
static bool qdev_hot_added = false;
|
||||||
static bool qdev_hot_removed = false;
|
bool qdev_hot_removed = false;
|
||||||
|
|
||||||
const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
|
const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
|
||||||
{
|
{
|
||||||
@@ -271,40 +271,6 @@ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
|
|||||||
return hotplug_ctrl;
|
return hotplug_ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qdev_unplug(DeviceState *dev, Error **errp)
|
|
||||||
{
|
|
||||||
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
|
||||||
HotplugHandler *hotplug_ctrl;
|
|
||||||
HotplugHandlerClass *hdc;
|
|
||||||
|
|
||||||
if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) {
|
|
||||||
error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dc->hotpluggable) {
|
|
||||||
error_setg(errp, QERR_DEVICE_NO_HOTPLUG,
|
|
||||||
object_get_typename(OBJECT(dev)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qdev_hot_removed = true;
|
|
||||||
|
|
||||||
hotplug_ctrl = qdev_get_hotplug_handler(dev);
|
|
||||||
/* hotpluggable device MUST have HotplugHandler, if it doesn't
|
|
||||||
* then something is very wrong with it */
|
|
||||||
g_assert(hotplug_ctrl);
|
|
||||||
|
|
||||||
/* If device supports async unplug just request it to be done,
|
|
||||||
* otherwise just remove it synchronously */
|
|
||||||
hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl);
|
|
||||||
if (hdc->unplug_request) {
|
|
||||||
hotplug_handler_unplug_request(hotplug_ctrl, dev, errp);
|
|
||||||
} else {
|
|
||||||
hotplug_handler_unplug(hotplug_ctrl, dev, errp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int qdev_reset_one(DeviceState *dev, void *opaque)
|
static int qdev_reset_one(DeviceState *dev, void *opaque)
|
||||||
{
|
{
|
||||||
device_reset(dev);
|
device_reset(dev);
|
||||||
@@ -385,7 +351,7 @@ void qdev_machine_creation_done(void)
|
|||||||
* ok, initial machine setup is done, starting from now we can
|
* ok, initial machine setup is done, starting from now we can
|
||||||
* only create hotpluggable devices
|
* only create hotpluggable devices
|
||||||
*/
|
*/
|
||||||
qdev_hotplug = 1;
|
qdev_hotplug = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool qdev_machine_modified(void)
|
bool qdev_machine_modified(void)
|
||||||
@@ -1037,13 +1003,6 @@ static bool device_get_hotplugged(Object *obj, Error **err)
|
|||||||
return dev->hotplugged;
|
return dev->hotplugged;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void device_set_hotplugged(Object *obj, bool value, Error **err)
|
|
||||||
{
|
|
||||||
DeviceState *dev = DEVICE(obj);
|
|
||||||
|
|
||||||
dev->hotplugged = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void device_initfn(Object *obj)
|
static void device_initfn(Object *obj)
|
||||||
{
|
{
|
||||||
DeviceState *dev = DEVICE(obj);
|
DeviceState *dev = DEVICE(obj);
|
||||||
@@ -1063,7 +1022,7 @@ static void device_initfn(Object *obj)
|
|||||||
object_property_add_bool(obj, "hotpluggable",
|
object_property_add_bool(obj, "hotpluggable",
|
||||||
device_get_hotpluggable, NULL, NULL);
|
device_get_hotpluggable, NULL, NULL);
|
||||||
object_property_add_bool(obj, "hotplugged",
|
object_property_add_bool(obj, "hotplugged",
|
||||||
device_get_hotplugged, device_set_hotplugged,
|
device_get_hotplugged, NULL,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
|
|
||||||
class = object_get_class(OBJECT(dev));
|
class = object_get_class(OBJECT(dev));
|
||||||
|
@@ -26,7 +26,6 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "cpu.h"
|
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
@@ -114,8 +113,8 @@ static void cg3_update_display(void *opaque)
|
|||||||
for (y = 0; y < height; y++) {
|
for (y = 0; y < height; y++) {
|
||||||
int update = s->full_update;
|
int update = s->full_update;
|
||||||
|
|
||||||
page = (y * width) & TARGET_PAGE_MASK;
|
page = y * width;
|
||||||
update |= memory_region_get_dirty(&s->vram_mem, page, page + width,
|
update |= memory_region_get_dirty(&s->vram_mem, page, width,
|
||||||
DIRTY_MEMORY_VGA);
|
DIRTY_MEMORY_VGA);
|
||||||
if (update) {
|
if (update) {
|
||||||
if (y_start < 0) {
|
if (y_start < 0) {
|
||||||
@@ -148,8 +147,7 @@ static void cg3_update_display(void *opaque)
|
|||||||
}
|
}
|
||||||
if (page_max >= page_min) {
|
if (page_max >= page_min) {
|
||||||
memory_region_reset_dirty(&s->vram_mem,
|
memory_region_reset_dirty(&s->vram_mem,
|
||||||
page_min, page_max - page_min + TARGET_PAGE_SIZE,
|
page_min, page_max - page_min, DIRTY_MEMORY_VGA);
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
}
|
}
|
||||||
/* vsync interrupt? */
|
/* vsync interrupt? */
|
||||||
if (s->regs[0] & CG3_CR_ENABLE_INTS) {
|
if (s->regs[0] & CG3_CR_ENABLE_INTS) {
|
||||||
@@ -305,8 +303,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp)
|
|||||||
vmstate_register_ram_global(&s->rom);
|
vmstate_register_ram_global(&s->rom);
|
||||||
fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, CG3_ROM_FILE);
|
fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, CG3_ROM_FILE);
|
||||||
if (fcode_filename) {
|
if (fcode_filename) {
|
||||||
ret = load_image_targphys(fcode_filename, s->prom_addr,
|
ret = load_image_mr(fcode_filename, &s->rom);
|
||||||
FCODE_MAX_ROM_SIZE);
|
|
||||||
g_free(fcode_filename);
|
g_free(fcode_filename);
|
||||||
if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
|
if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
|
||||||
error_report("cg3: could not load prom '%s'", CG3_ROM_FILE);
|
error_report("cg3: could not load prom '%s'", CG3_ROM_FILE);
|
||||||
@@ -371,7 +368,6 @@ static Property cg3_properties[] = {
|
|||||||
DEFINE_PROP_UINT16("width", CG3State, width, -1),
|
DEFINE_PROP_UINT16("width", CG3State, width, -1),
|
||||||
DEFINE_PROP_UINT16("height", CG3State, height, -1),
|
DEFINE_PROP_UINT16("height", CG3State, height, -1),
|
||||||
DEFINE_PROP_UINT16("depth", CG3State, depth, -1),
|
DEFINE_PROP_UINT16("depth", CG3State, depth, -1),
|
||||||
DEFINE_PROP_UINT64("prom-addr", CG3State, prom_addr, -1),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1263,6 +1263,7 @@ static void exynos4210_fimd_update(void *opaque)
|
|||||||
Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
|
Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
|
||||||
DisplaySurface *surface;
|
DisplaySurface *surface;
|
||||||
Exynos4210fimdWindow *w;
|
Exynos4210fimdWindow *w;
|
||||||
|
DirtyBitmapSnapshot *snap;
|
||||||
int i, line;
|
int i, line;
|
||||||
hwaddr fb_line_addr, inc_size;
|
hwaddr fb_line_addr, inc_size;
|
||||||
int scrn_height;
|
int scrn_height;
|
||||||
@@ -1291,10 +1292,12 @@ static void exynos4210_fimd_update(void *opaque)
|
|||||||
memory_region_sync_dirty_bitmap(w->mem_section.mr);
|
memory_region_sync_dirty_bitmap(w->mem_section.mr);
|
||||||
host_fb_addr = w->host_fb_addr;
|
host_fb_addr = w->host_fb_addr;
|
||||||
fb_line_addr = w->mem_section.offset_within_region;
|
fb_line_addr = w->mem_section.offset_within_region;
|
||||||
|
snap = memory_region_snapshot_and_clear_dirty(w->mem_section.mr,
|
||||||
|
fb_line_addr, inc_size * scrn_height, DIRTY_MEMORY_VGA);
|
||||||
|
|
||||||
for (line = 0; line < scrn_height; line++) {
|
for (line = 0; line < scrn_height; line++) {
|
||||||
is_dirty = memory_region_get_dirty(w->mem_section.mr,
|
is_dirty = memory_region_snapshot_get_dirty(w->mem_section.mr,
|
||||||
fb_line_addr, scrn_width, DIRTY_MEMORY_VGA);
|
snap, fb_line_addr, scrn_width);
|
||||||
|
|
||||||
if (s->invalidate || is_dirty) {
|
if (s->invalidate || is_dirty) {
|
||||||
if (first_line == -1) {
|
if (first_line == -1) {
|
||||||
@@ -1309,9 +1312,7 @@ static void exynos4210_fimd_update(void *opaque)
|
|||||||
fb_line_addr += inc_size;
|
fb_line_addr += inc_size;
|
||||||
is_dirty = false;
|
is_dirty = false;
|
||||||
}
|
}
|
||||||
memory_region_reset_dirty(w->mem_section.mr,
|
g_free(snap);
|
||||||
w->mem_section.offset_within_region,
|
|
||||||
w->fb_len, DIRTY_MEMORY_VGA);
|
|
||||||
blend = true;
|
blend = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -67,7 +67,7 @@ void framebuffer_update_display(
|
|||||||
int *first_row, /* Input and output. */
|
int *first_row, /* Input and output. */
|
||||||
int *last_row /* Output only */)
|
int *last_row /* Output only */)
|
||||||
{
|
{
|
||||||
hwaddr src_len;
|
DirtyBitmapSnapshot *snap;
|
||||||
uint8_t *dest;
|
uint8_t *dest;
|
||||||
uint8_t *src;
|
uint8_t *src;
|
||||||
int first, last = 0;
|
int first, last = 0;
|
||||||
@@ -78,7 +78,6 @@ void framebuffer_update_display(
|
|||||||
|
|
||||||
i = *first_row;
|
i = *first_row;
|
||||||
*first_row = -1;
|
*first_row = -1;
|
||||||
src_len = (hwaddr)src_width * rows;
|
|
||||||
|
|
||||||
mem = mem_section->mr;
|
mem = mem_section->mr;
|
||||||
if (!mem) {
|
if (!mem) {
|
||||||
@@ -102,9 +101,10 @@ void framebuffer_update_display(
|
|||||||
src += i * src_width;
|
src += i * src_width;
|
||||||
dest += i * dest_row_pitch;
|
dest += i * dest_row_pitch;
|
||||||
|
|
||||||
|
snap = memory_region_snapshot_and_clear_dirty(mem, addr, src_width * rows,
|
||||||
|
DIRTY_MEMORY_VGA);
|
||||||
for (; i < rows; i++) {
|
for (; i < rows; i++) {
|
||||||
dirty = memory_region_get_dirty(mem, addr, src_width,
|
dirty = memory_region_snapshot_get_dirty(mem, snap, addr, src_width);
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
if (dirty || invalidate) {
|
if (dirty || invalidate) {
|
||||||
fn(opaque, dest, src, cols, dest_col_pitch);
|
fn(opaque, dest, src, cols, dest_col_pitch);
|
||||||
if (first == -1)
|
if (first == -1)
|
||||||
@@ -115,11 +115,10 @@ void framebuffer_update_display(
|
|||||||
src += src_width;
|
src += src_width;
|
||||||
dest += dest_row_pitch;
|
dest += dest_row_pitch;
|
||||||
}
|
}
|
||||||
|
g_free(snap);
|
||||||
if (first < 0) {
|
if (first < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memory_region_reset_dirty(mem, mem_section->offset_within_region, src_len,
|
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
*first_row = first;
|
*first_row = first;
|
||||||
*last_row = last;
|
*last_row = last;
|
||||||
}
|
}
|
||||||
|
@@ -64,17 +64,8 @@ typedef struct G364State {
|
|||||||
|
|
||||||
static inline int check_dirty(G364State *s, ram_addr_t page)
|
static inline int check_dirty(G364State *s, ram_addr_t page)
|
||||||
{
|
{
|
||||||
return memory_region_get_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
|
return memory_region_test_and_clear_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
|
||||||
DIRTY_MEMORY_VGA);
|
DIRTY_MEMORY_VGA);
|
||||||
}
|
|
||||||
|
|
||||||
static inline void reset_dirty(G364State *s,
|
|
||||||
ram_addr_t page_min, ram_addr_t page_max)
|
|
||||||
{
|
|
||||||
memory_region_reset_dirty(&s->mem_vram,
|
|
||||||
page_min,
|
|
||||||
page_max + G364_PAGE_SIZE - page_min - 1,
|
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void g364fb_draw_graphic8(G364State *s)
|
static void g364fb_draw_graphic8(G364State *s)
|
||||||
@@ -83,7 +74,7 @@ static void g364fb_draw_graphic8(G364State *s)
|
|||||||
int i, w;
|
int i, w;
|
||||||
uint8_t *vram;
|
uint8_t *vram;
|
||||||
uint8_t *data_display, *dd;
|
uint8_t *data_display, *dd;
|
||||||
ram_addr_t page, page_min, page_max;
|
ram_addr_t page;
|
||||||
int x, y;
|
int x, y;
|
||||||
int xmin, xmax;
|
int xmin, xmax;
|
||||||
int ymin, ymax;
|
int ymin, ymax;
|
||||||
@@ -114,8 +105,6 @@ static void g364fb_draw_graphic8(G364State *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
page = 0;
|
page = 0;
|
||||||
page_min = (ram_addr_t)-1;
|
|
||||||
page_max = 0;
|
|
||||||
|
|
||||||
x = y = 0;
|
x = y = 0;
|
||||||
xmin = s->width;
|
xmin = s->width;
|
||||||
@@ -137,9 +126,6 @@ static void g364fb_draw_graphic8(G364State *s)
|
|||||||
if (check_dirty(s, page)) {
|
if (check_dirty(s, page)) {
|
||||||
if (y < ymin)
|
if (y < ymin)
|
||||||
ymin = ymax = y;
|
ymin = ymax = y;
|
||||||
if (page_min == (ram_addr_t)-1)
|
|
||||||
page_min = page;
|
|
||||||
page_max = page;
|
|
||||||
if (x < xmin)
|
if (x < xmin)
|
||||||
xmin = x;
|
xmin = x;
|
||||||
for (i = 0; i < G364_PAGE_SIZE; i++) {
|
for (i = 0; i < G364_PAGE_SIZE; i++) {
|
||||||
@@ -196,10 +182,7 @@ static void g364fb_draw_graphic8(G364State *s)
|
|||||||
ymax = y;
|
ymax = y;
|
||||||
} else {
|
} else {
|
||||||
int dy;
|
int dy;
|
||||||
if (page_min != (ram_addr_t)-1) {
|
if (xmax || ymax) {
|
||||||
reset_dirty(s, page_min, page_max);
|
|
||||||
page_min = (ram_addr_t)-1;
|
|
||||||
page_max = 0;
|
|
||||||
dpy_gfx_update(s->con, xmin, ymin,
|
dpy_gfx_update(s->con, xmin, ymin,
|
||||||
xmax - xmin + 1, ymax - ymin + 1);
|
xmax - xmin + 1, ymax - ymin + 1);
|
||||||
xmin = s->width;
|
xmin = s->width;
|
||||||
@@ -219,9 +202,8 @@ static void g364fb_draw_graphic8(G364State *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (page_min != (ram_addr_t)-1) {
|
if (xmax || ymax) {
|
||||||
dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
|
dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
|
||||||
reset_dirty(s, page_min, page_max);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -305,6 +305,16 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
|
|||||||
qxl->ssd.cursor = cursor_builtin_hidden();
|
qxl->ssd.cursor = cursor_builtin_hidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* zlib xors the seed with 0xffffffff, and xors the result
|
||||||
|
* again with 0xffffffff; Both are not done with linux's crc32,
|
||||||
|
* which we want to be compatible with, so undo that.
|
||||||
|
*/
|
||||||
|
return crc32(0xffffffff, p, len) ^ 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
static ram_addr_t qxl_rom_size(void)
|
static ram_addr_t qxl_rom_size(void)
|
||||||
{
|
{
|
||||||
#define QXL_REQUIRED_SZ (sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes))
|
#define QXL_REQUIRED_SZ (sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes))
|
||||||
@@ -369,6 +379,18 @@ static void init_qxl_rom(PCIQXLDevice *d)
|
|||||||
rom->num_pages = cpu_to_le32(num_pages);
|
rom->num_pages = cpu_to_le32(num_pages);
|
||||||
rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size);
|
rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size);
|
||||||
|
|
||||||
|
if (d->xres && d->yres) {
|
||||||
|
/* needs linux kernel 4.12+ to work */
|
||||||
|
rom->client_monitors_config.count = 1;
|
||||||
|
rom->client_monitors_config.heads[0].left = 0;
|
||||||
|
rom->client_monitors_config.heads[0].top = 0;
|
||||||
|
rom->client_monitors_config.heads[0].right = cpu_to_le32(d->xres);
|
||||||
|
rom->client_monitors_config.heads[0].bottom = cpu_to_le32(d->yres);
|
||||||
|
rom->client_monitors_config_crc = qxl_crc32(
|
||||||
|
(const uint8_t *)&rom->client_monitors_config,
|
||||||
|
sizeof(rom->client_monitors_config));
|
||||||
|
}
|
||||||
|
|
||||||
d->shadow_rom = *rom;
|
d->shadow_rom = *rom;
|
||||||
d->rom = rom;
|
d->rom = rom;
|
||||||
d->modes = modes;
|
d->modes = modes;
|
||||||
@@ -1011,16 +1033,6 @@ static void interface_set_client_capabilities(QXLInstance *sin,
|
|||||||
qxl_send_events(qxl, QXL_INTERRUPT_CLIENT);
|
qxl_send_events(qxl, QXL_INTERRUPT_CLIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* zlib xors the seed with 0xffffffff, and xors the result
|
|
||||||
* again with 0xffffffff; Both are not done with linux's crc32,
|
|
||||||
* which we want to be compatible with, so undo that.
|
|
||||||
*/
|
|
||||||
return crc32(0xffffffff, p, len) ^ 0xffffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool qxl_rom_monitors_config_changed(QXLRom *rom,
|
static bool qxl_rom_monitors_config_changed(QXLRom *rom,
|
||||||
VDAgentMonitorsConfig *monitors_config,
|
VDAgentMonitorsConfig *monitors_config,
|
||||||
unsigned int max_outputs)
|
unsigned int max_outputs)
|
||||||
@@ -2397,6 +2409,8 @@ static Property qxl_properties[] = {
|
|||||||
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
|
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
|
||||||
DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0),
|
DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0),
|
||||||
#endif
|
#endif
|
||||||
|
DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0),
|
||||||
|
DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -119,6 +119,8 @@ typedef struct PCIQXLDevice {
|
|||||||
uint32_t vram_size_mb;
|
uint32_t vram_size_mb;
|
||||||
uint32_t vram32_size_mb;
|
uint32_t vram32_size_mb;
|
||||||
uint32_t vgamem_size_mb;
|
uint32_t vgamem_size_mb;
|
||||||
|
uint32_t xres;
|
||||||
|
uint32_t yres;
|
||||||
|
|
||||||
/* qxl_render_update state */
|
/* qxl_render_update state */
|
||||||
int render_update_cookie_num;
|
int render_update_cookie_num;
|
||||||
|
1752
hw/display/sm501.c
1752
hw/display/sm501.c
File diff suppressed because it is too large
Load Diff
@@ -47,81 +47,67 @@ static void glue(draw_line8_, PIXEL_NAME)(
|
|||||||
{
|
{
|
||||||
uint8_t v, r, g, b;
|
uint8_t v, r, g, b;
|
||||||
do {
|
do {
|
||||||
v = ldub_p(s);
|
v = ldub_p(s);
|
||||||
r = (pal[v] >> 16) & 0xff;
|
r = (pal[v] >> 16) & 0xff;
|
||||||
g = (pal[v] >> 8) & 0xff;
|
g = (pal[v] >> 8) & 0xff;
|
||||||
b = (pal[v] >> 0) & 0xff;
|
b = (pal[v] >> 0) & 0xff;
|
||||||
((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
*(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
||||||
s ++;
|
s++;
|
||||||
d += BPP;
|
d += BPP;
|
||||||
} while (-- width != 0);
|
} while (--width != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void glue(draw_line16_, PIXEL_NAME)(
|
static void glue(draw_line16_, PIXEL_NAME)(
|
||||||
uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
|
uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
|
||||||
{
|
{
|
||||||
uint16_t rgb565;
|
uint16_t rgb565;
|
||||||
uint8_t r, g, b;
|
uint8_t r, g, b;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
rgb565 = lduw_p(s);
|
rgb565 = lduw_le_p(s);
|
||||||
r = ((rgb565 >> 11) & 0x1f) << 3;
|
r = (rgb565 >> 8) & 0xf8;
|
||||||
g = ((rgb565 >> 5) & 0x3f) << 2;
|
g = (rgb565 >> 3) & 0xfc;
|
||||||
b = ((rgb565 >> 0) & 0x1f) << 3;
|
b = (rgb565 << 3) & 0xf8;
|
||||||
((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
*(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
||||||
s += 2;
|
s += 2;
|
||||||
d += BPP;
|
d += BPP;
|
||||||
} while (-- width != 0);
|
} while (--width != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void glue(draw_line32_, PIXEL_NAME)(
|
static void glue(draw_line32_, PIXEL_NAME)(
|
||||||
uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
|
uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
|
||||||
{
|
{
|
||||||
uint8_t r, g, b;
|
uint8_t r, g, b;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
ldub_p(s);
|
|
||||||
#if defined(TARGET_WORDS_BIGENDIAN)
|
|
||||||
r = s[1];
|
|
||||||
g = s[2];
|
|
||||||
b = s[3];
|
|
||||||
#else
|
|
||||||
b = s[0];
|
|
||||||
g = s[1];
|
|
||||||
r = s[2];
|
r = s[2];
|
||||||
#endif
|
g = s[1];
|
||||||
((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
b = s[0];
|
||||||
s += 4;
|
*(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
||||||
d += BPP;
|
s += 4;
|
||||||
} while (-- width != 0);
|
d += BPP;
|
||||||
|
} while (--width != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw hardware cursor image on the given line.
|
* Draw hardware cursor image on the given line.
|
||||||
*/
|
*/
|
||||||
static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt,
|
static void glue(draw_hwc_line_, PIXEL_NAME)(uint8_t *d, const uint8_t *s,
|
||||||
uint8_t * palette, int c_y, uint8_t *d, int width)
|
int width, const uint8_t *palette, int c_x, int c_y)
|
||||||
{
|
{
|
||||||
int x, i;
|
int i;
|
||||||
uint8_t bitset = 0;
|
uint8_t r, g, b, v, bitset = 0;
|
||||||
|
|
||||||
/* get hardware cursor pattern */
|
|
||||||
uint32_t cursor_addr = get_hwc_address(s, crt);
|
|
||||||
assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
|
|
||||||
cursor_addr += 64 * c_y / 4; /* 4 pixels per byte */
|
|
||||||
cursor_addr += s->base;
|
|
||||||
|
|
||||||
/* get cursor position */
|
/* get cursor position */
|
||||||
x = get_hwc_x(s, crt);
|
assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
|
||||||
d += x * BPP;
|
s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */
|
||||||
|
d += c_x * BPP;
|
||||||
for (i = 0; i < SM501_HWC_WIDTH && x + i < width; i++) {
|
|
||||||
uint8_t v;
|
|
||||||
|
|
||||||
|
for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) {
|
||||||
/* get pixel value */
|
/* get pixel value */
|
||||||
if (i % 4 == 0) {
|
if (i % 4 == 0) {
|
||||||
bitset = ldub_phys(&address_space_memory, cursor_addr);
|
bitset = ldub_p(s);
|
||||||
cursor_addr++;
|
s++;
|
||||||
}
|
}
|
||||||
v = bitset & 3;
|
v = bitset & 3;
|
||||||
bitset >>= 2;
|
bitset >>= 2;
|
||||||
@@ -129,10 +115,10 @@ static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt,
|
|||||||
/* write pixel */
|
/* write pixel */
|
||||||
if (v) {
|
if (v) {
|
||||||
v--;
|
v--;
|
||||||
uint8_t r = palette[v * 3 + 0];
|
r = palette[v * 3 + 0];
|
||||||
uint8_t g = palette[v * 3 + 1];
|
g = palette[v * 3 + 1];
|
||||||
uint8_t b = palette[v * 3 + 2];
|
b = palette[v * 3 + 2];
|
||||||
((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
*(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
||||||
}
|
}
|
||||||
d += BPP;
|
d += BPP;
|
||||||
}
|
}
|
||||||
|
298
hw/display/tcx.c
298
hw/display/tcx.c
@@ -25,7 +25,6 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "cpu.h" /* FIXME shouldn't use TARGET_PAGE_SIZE */
|
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
#include "ui/pixel_ops.h"
|
#include "ui/pixel_ops.h"
|
||||||
#include "hw/loader.h"
|
#include "hw/loader.h"
|
||||||
@@ -93,41 +92,46 @@ typedef struct TCXState {
|
|||||||
uint16_t cursy;
|
uint16_t cursy;
|
||||||
} TCXState;
|
} TCXState;
|
||||||
|
|
||||||
static void tcx_set_dirty(TCXState *s)
|
static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
|
||||||
{
|
{
|
||||||
memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
|
memory_region_set_dirty(&s->vram_mem, addr, len);
|
||||||
|
|
||||||
|
if (s->depth == 24) {
|
||||||
|
memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
|
||||||
|
len * 4);
|
||||||
|
memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
|
||||||
|
len * 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int tcx24_check_dirty(TCXState *s, ram_addr_t page,
|
static int tcx_check_dirty(TCXState *s, ram_addr_t addr, int len)
|
||||||
ram_addr_t page24, ram_addr_t cpage)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE,
|
ret = memory_region_get_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA);
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4,
|
if (s->depth == 24) {
|
||||||
DIRTY_MEMORY_VGA);
|
ret |= memory_region_get_dirty(&s->vram_mem,
|
||||||
ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4,
|
s->vram24_offset + addr * 4, len * 4,
|
||||||
DIRTY_MEMORY_VGA);
|
DIRTY_MEMORY_VGA);
|
||||||
|
ret |= memory_region_get_dirty(&s->vram_mem,
|
||||||
|
s->cplane_offset + addr * 4, len * 4,
|
||||||
|
DIRTY_MEMORY_VGA);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void tcx24_reset_dirty(TCXState *ts, ram_addr_t page_min,
|
static void tcx_reset_dirty(TCXState *s, ram_addr_t addr, int len)
|
||||||
ram_addr_t page_max, ram_addr_t page24,
|
|
||||||
ram_addr_t cpage)
|
|
||||||
{
|
{
|
||||||
memory_region_reset_dirty(&ts->vram_mem,
|
memory_region_reset_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA);
|
||||||
page_min,
|
|
||||||
(page_max - page_min) + TARGET_PAGE_SIZE,
|
if (s->depth == 24) {
|
||||||
DIRTY_MEMORY_VGA);
|
memory_region_reset_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
|
||||||
memory_region_reset_dirty(&ts->vram_mem,
|
len * 4, DIRTY_MEMORY_VGA);
|
||||||
page24 + page_min * 4,
|
memory_region_reset_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
|
||||||
(page_max - page_min) * 4 + TARGET_PAGE_SIZE,
|
len * 4, DIRTY_MEMORY_VGA);
|
||||||
DIRTY_MEMORY_VGA);
|
}
|
||||||
memory_region_reset_dirty(&ts->vram_mem,
|
|
||||||
cpage + page_min * 4,
|
|
||||||
(page_max - page_min) * 4 + TARGET_PAGE_SIZE,
|
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_palette_entries(TCXState *s, int start, int end)
|
static void update_palette_entries(TCXState *s, int start, int end)
|
||||||
@@ -136,27 +140,14 @@ static void update_palette_entries(TCXState *s, int start, int end)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = start; i < end; i++) {
|
for (i = start; i < end; i++) {
|
||||||
switch (surface_bits_per_pixel(surface)) {
|
if (is_surface_bgr(surface)) {
|
||||||
default:
|
s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
|
||||||
case 8:
|
} else {
|
||||||
s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
|
s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
|
||||||
break;
|
|
||||||
case 15:
|
|
||||||
s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
if (is_surface_bgr(surface)) {
|
|
||||||
s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
|
|
||||||
} else {
|
|
||||||
s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
tcx_set_dirty(s);
|
tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcx_draw_line32(TCXState *s1, uint8_t *d,
|
static void tcx_draw_line32(TCXState *s1, uint8_t *d,
|
||||||
@@ -172,31 +163,6 @@ static void tcx_draw_line32(TCXState *s1, uint8_t *d,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcx_draw_line16(TCXState *s1, uint8_t *d,
|
|
||||||
const uint8_t *s, int width)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
uint8_t val;
|
|
||||||
uint16_t *p = (uint16_t *)d;
|
|
||||||
|
|
||||||
for (x = 0; x < width; x++) {
|
|
||||||
val = *s++;
|
|
||||||
*p++ = s1->palette[val];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tcx_draw_line8(TCXState *s1, uint8_t *d,
|
|
||||||
const uint8_t *s, int width)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
uint8_t val;
|
|
||||||
|
|
||||||
for(x = 0; x < width; x++) {
|
|
||||||
val = *s++;
|
|
||||||
*d++ = s1->palette[val];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
|
static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
|
||||||
int y, int width)
|
int y, int width)
|
||||||
{
|
{
|
||||||
@@ -223,57 +189,6 @@ static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcx_draw_cursor16(TCXState *s1, uint8_t *d,
|
|
||||||
int y, int width)
|
|
||||||
{
|
|
||||||
int x, len;
|
|
||||||
uint32_t mask, bits;
|
|
||||||
uint16_t *p = (uint16_t *)d;
|
|
||||||
|
|
||||||
y = y - s1->cursy;
|
|
||||||
mask = s1->cursmask[y];
|
|
||||||
bits = s1->cursbits[y];
|
|
||||||
len = MIN(width - s1->cursx, 32);
|
|
||||||
p = &p[s1->cursx];
|
|
||||||
for (x = 0; x < len; x++) {
|
|
||||||
if (mask & 0x80000000) {
|
|
||||||
if (bits & 0x80000000) {
|
|
||||||
*p = s1->palette[259];
|
|
||||||
} else {
|
|
||||||
*p = s1->palette[258];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p++;
|
|
||||||
mask <<= 1;
|
|
||||||
bits <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tcx_draw_cursor8(TCXState *s1, uint8_t *d,
|
|
||||||
int y, int width)
|
|
||||||
{
|
|
||||||
int x, len;
|
|
||||||
uint32_t mask, bits;
|
|
||||||
|
|
||||||
y = y - s1->cursy;
|
|
||||||
mask = s1->cursmask[y];
|
|
||||||
bits = s1->cursbits[y];
|
|
||||||
len = MIN(width - s1->cursx, 32);
|
|
||||||
d = &d[s1->cursx];
|
|
||||||
for (x = 0; x < len; x++) {
|
|
||||||
if (mask & 0x80000000) {
|
|
||||||
if (bits & 0x80000000) {
|
|
||||||
*d = s1->palette[259];
|
|
||||||
} else {
|
|
||||||
*d = s1->palette[258];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d++;
|
|
||||||
mask <<= 1;
|
|
||||||
bits <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
XXX Could be much more optimal:
|
XXX Could be much more optimal:
|
||||||
* detect if line/page/whole screen is in 24 bit mode
|
* detect if line/page/whole screen is in 24 bit mode
|
||||||
@@ -322,10 +237,8 @@ static void tcx_update_display(void *opaque)
|
|||||||
ram_addr_t page, page_min, page_max;
|
ram_addr_t page, page_min, page_max;
|
||||||
int y, y_start, dd, ds;
|
int y, y_start, dd, ds;
|
||||||
uint8_t *d, *s;
|
uint8_t *d, *s;
|
||||||
void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
|
|
||||||
void (*fc)(TCXState *s1, uint8_t *dst, int y, int width);
|
|
||||||
|
|
||||||
if (surface_bits_per_pixel(surface) == 0) {
|
if (surface_bits_per_pixel(surface) != 32) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,29 +251,9 @@ static void tcx_update_display(void *opaque)
|
|||||||
dd = surface_stride(surface);
|
dd = surface_stride(surface);
|
||||||
ds = 1024;
|
ds = 1024;
|
||||||
|
|
||||||
switch (surface_bits_per_pixel(surface)) {
|
|
||||||
case 32:
|
|
||||||
f = tcx_draw_line32;
|
|
||||||
fc = tcx_draw_cursor32;
|
|
||||||
break;
|
|
||||||
case 15:
|
|
||||||
case 16:
|
|
||||||
f = tcx_draw_line16;
|
|
||||||
fc = tcx_draw_cursor16;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case 8:
|
|
||||||
f = tcx_draw_line8;
|
|
||||||
fc = tcx_draw_cursor8;
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_region_sync_dirty_bitmap(&ts->vram_mem);
|
memory_region_sync_dirty_bitmap(&ts->vram_mem);
|
||||||
for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) {
|
for (y = 0; y < ts->height; y++, page += ds) {
|
||||||
if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE,
|
if (tcx_check_dirty(ts, page, ds)) {
|
||||||
DIRTY_MEMORY_VGA)) {
|
|
||||||
if (y_start < 0)
|
if (y_start < 0)
|
||||||
y_start = y;
|
y_start = y;
|
||||||
if (page < page_min)
|
if (page < page_min)
|
||||||
@@ -368,37 +261,10 @@ static void tcx_update_display(void *opaque)
|
|||||||
if (page > page_max)
|
if (page > page_max)
|
||||||
page_max = page;
|
page_max = page;
|
||||||
|
|
||||||
f(ts, d, s, ts->width);
|
tcx_draw_line32(ts, d, s, ts->width);
|
||||||
if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
|
if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
|
||||||
fc(ts, d, y, ts->width);
|
tcx_draw_cursor32(ts, d, y, ts->width);
|
||||||
}
|
}
|
||||||
d += dd;
|
|
||||||
s += ds;
|
|
||||||
y++;
|
|
||||||
|
|
||||||
f(ts, d, s, ts->width);
|
|
||||||
if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
|
|
||||||
fc(ts, d, y, ts->width);
|
|
||||||
}
|
|
||||||
d += dd;
|
|
||||||
s += ds;
|
|
||||||
y++;
|
|
||||||
|
|
||||||
f(ts, d, s, ts->width);
|
|
||||||
if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
|
|
||||||
fc(ts, d, y, ts->width);
|
|
||||||
}
|
|
||||||
d += dd;
|
|
||||||
s += ds;
|
|
||||||
y++;
|
|
||||||
|
|
||||||
f(ts, d, s, ts->width);
|
|
||||||
if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
|
|
||||||
fc(ts, d, y, ts->width);
|
|
||||||
}
|
|
||||||
d += dd;
|
|
||||||
s += ds;
|
|
||||||
y++;
|
|
||||||
} else {
|
} else {
|
||||||
if (y_start >= 0) {
|
if (y_start >= 0) {
|
||||||
/* flush to display */
|
/* flush to display */
|
||||||
@@ -406,10 +272,9 @@ static void tcx_update_display(void *opaque)
|
|||||||
ts->width, y - y_start);
|
ts->width, y - y_start);
|
||||||
y_start = -1;
|
y_start = -1;
|
||||||
}
|
}
|
||||||
d += dd * 4;
|
|
||||||
s += ds * 4;
|
|
||||||
y += 4;
|
|
||||||
}
|
}
|
||||||
|
s += ds;
|
||||||
|
d += dd;
|
||||||
}
|
}
|
||||||
if (y_start >= 0) {
|
if (y_start >= 0) {
|
||||||
/* flush to display */
|
/* flush to display */
|
||||||
@@ -418,10 +283,7 @@ static void tcx_update_display(void *opaque)
|
|||||||
}
|
}
|
||||||
/* reset modified pages */
|
/* reset modified pages */
|
||||||
if (page_max >= page_min) {
|
if (page_max >= page_min) {
|
||||||
memory_region_reset_dirty(&ts->vram_mem,
|
tcx_reset_dirty(ts, page_min, page_max - page_min);
|
||||||
page_min,
|
|
||||||
(page_max - page_min) + TARGET_PAGE_SIZE,
|
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +291,7 @@ static void tcx24_update_display(void *opaque)
|
|||||||
{
|
{
|
||||||
TCXState *ts = opaque;
|
TCXState *ts = opaque;
|
||||||
DisplaySurface *surface = qemu_console_surface(ts->con);
|
DisplaySurface *surface = qemu_console_surface(ts->con);
|
||||||
ram_addr_t page, page_min, page_max, cpage, page24;
|
ram_addr_t page, page_min, page_max;
|
||||||
int y, y_start, dd, ds;
|
int y, y_start, dd, ds;
|
||||||
uint8_t *d, *s;
|
uint8_t *d, *s;
|
||||||
uint32_t *cptr, *s24;
|
uint32_t *cptr, *s24;
|
||||||
@@ -439,8 +301,6 @@ static void tcx24_update_display(void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
page = 0;
|
page = 0;
|
||||||
page24 = ts->vram24_offset;
|
|
||||||
cpage = ts->cplane_offset;
|
|
||||||
y_start = -1;
|
y_start = -1;
|
||||||
page_min = -1;
|
page_min = -1;
|
||||||
page_max = 0;
|
page_max = 0;
|
||||||
@@ -452,9 +312,8 @@ static void tcx24_update_display(void *opaque)
|
|||||||
ds = 1024;
|
ds = 1024;
|
||||||
|
|
||||||
memory_region_sync_dirty_bitmap(&ts->vram_mem);
|
memory_region_sync_dirty_bitmap(&ts->vram_mem);
|
||||||
for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE,
|
for (y = 0; y < ts->height; y++, page += ds) {
|
||||||
page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
|
if (tcx_check_dirty(ts, page, ds)) {
|
||||||
if (tcx24_check_dirty(ts, page, page24, cpage)) {
|
|
||||||
if (y_start < 0)
|
if (y_start < 0)
|
||||||
y_start = y;
|
y_start = y;
|
||||||
if (page < page_min)
|
if (page < page_min)
|
||||||
@@ -465,38 +324,6 @@ static void tcx24_update_display(void *opaque)
|
|||||||
if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
|
if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
|
||||||
tcx_draw_cursor32(ts, d, y, ts->width);
|
tcx_draw_cursor32(ts, d, y, ts->width);
|
||||||
}
|
}
|
||||||
d += dd;
|
|
||||||
s += ds;
|
|
||||||
cptr += ds;
|
|
||||||
s24 += ds;
|
|
||||||
y++;
|
|
||||||
tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
|
|
||||||
if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
|
|
||||||
tcx_draw_cursor32(ts, d, y, ts->width);
|
|
||||||
}
|
|
||||||
d += dd;
|
|
||||||
s += ds;
|
|
||||||
cptr += ds;
|
|
||||||
s24 += ds;
|
|
||||||
y++;
|
|
||||||
tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
|
|
||||||
if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
|
|
||||||
tcx_draw_cursor32(ts, d, y, ts->width);
|
|
||||||
}
|
|
||||||
d += dd;
|
|
||||||
s += ds;
|
|
||||||
cptr += ds;
|
|
||||||
s24 += ds;
|
|
||||||
y++;
|
|
||||||
tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
|
|
||||||
if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
|
|
||||||
tcx_draw_cursor32(ts, d, y, ts->width);
|
|
||||||
}
|
|
||||||
d += dd;
|
|
||||||
s += ds;
|
|
||||||
cptr += ds;
|
|
||||||
s24 += ds;
|
|
||||||
y++;
|
|
||||||
} else {
|
} else {
|
||||||
if (y_start >= 0) {
|
if (y_start >= 0) {
|
||||||
/* flush to display */
|
/* flush to display */
|
||||||
@@ -504,12 +331,11 @@ static void tcx24_update_display(void *opaque)
|
|||||||
ts->width, y - y_start);
|
ts->width, y - y_start);
|
||||||
y_start = -1;
|
y_start = -1;
|
||||||
}
|
}
|
||||||
d += dd * 4;
|
|
||||||
s += ds * 4;
|
|
||||||
cptr += ds * 4;
|
|
||||||
s24 += ds * 4;
|
|
||||||
y += 4;
|
|
||||||
}
|
}
|
||||||
|
d += dd;
|
||||||
|
s += ds;
|
||||||
|
cptr += ds;
|
||||||
|
s24 += ds;
|
||||||
}
|
}
|
||||||
if (y_start >= 0) {
|
if (y_start >= 0) {
|
||||||
/* flush to display */
|
/* flush to display */
|
||||||
@@ -518,7 +344,7 @@ static void tcx24_update_display(void *opaque)
|
|||||||
}
|
}
|
||||||
/* reset modified pages */
|
/* reset modified pages */
|
||||||
if (page_max >= page_min) {
|
if (page_max >= page_min) {
|
||||||
tcx24_reset_dirty(ts, page_min, page_max, page24, cpage);
|
tcx_reset_dirty(ts, page_min, page_max - page_min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +352,7 @@ static void tcx_invalidate_display(void *opaque)
|
|||||||
{
|
{
|
||||||
TCXState *s = opaque;
|
TCXState *s = opaque;
|
||||||
|
|
||||||
tcx_set_dirty(s);
|
tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
|
||||||
qemu_console_resize(s->con, s->width, s->height);
|
qemu_console_resize(s->con, s->width, s->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,7 +360,7 @@ static void tcx24_invalidate_display(void *opaque)
|
|||||||
{
|
{
|
||||||
TCXState *s = opaque;
|
TCXState *s = opaque;
|
||||||
|
|
||||||
tcx_set_dirty(s);
|
tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
|
||||||
qemu_console_resize(s->con, s->width, s->height);
|
qemu_console_resize(s->con, s->width, s->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,7 +369,7 @@ static int vmstate_tcx_post_load(void *opaque, int version_id)
|
|||||||
TCXState *s = opaque;
|
TCXState *s = opaque;
|
||||||
|
|
||||||
update_palette_entries(s, 0, 256);
|
update_palette_entries(s, 0, 256);
|
||||||
tcx_set_dirty(s);
|
tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,7 +525,7 @@ static void tcx_stip_writel(void *opaque, hwaddr addr,
|
|||||||
val <<= 1;
|
val <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memory_region_set_dirty(&s->vram_mem, addr, 32);
|
tcx_set_dirty(s, addr, 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,7 +558,7 @@ static void tcx_rstip_writel(void *opaque, hwaddr addr,
|
|||||||
val <<= 1;
|
val <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memory_region_set_dirty(&s->vram_mem, addr, 32);
|
tcx_set_dirty(s, addr, 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +616,7 @@ static void tcx_blit_writel(void *opaque, hwaddr addr,
|
|||||||
memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
|
memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memory_region_set_dirty(&s->vram_mem, addr, len);
|
tcx_set_dirty(s, addr, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,7 +650,7 @@ static void tcx_rblit_writel(void *opaque, hwaddr addr,
|
|||||||
memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
|
memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memory_region_set_dirty(&s->vram_mem, addr, len);
|
tcx_set_dirty(s, addr, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,7 +687,7 @@ static void tcx_invalidate_cursor_position(TCXState *s)
|
|||||||
start = ymin * 1024;
|
start = ymin * 1024;
|
||||||
end = ymax * 1024;
|
end = ymax * 1024;
|
||||||
|
|
||||||
memory_region_set_dirty(&s->vram_mem, start, end-start);
|
tcx_set_dirty(s, start, end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
|
static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
|
||||||
@@ -1017,8 +843,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp)
|
|||||||
vmstate_register_ram_global(&s->rom);
|
vmstate_register_ram_global(&s->rom);
|
||||||
fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
|
fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
|
||||||
if (fcode_filename) {
|
if (fcode_filename) {
|
||||||
ret = load_image_targphys(fcode_filename, s->prom_addr,
|
ret = load_image_mr(fcode_filename, &s->rom);
|
||||||
FCODE_MAX_ROM_SIZE);
|
|
||||||
g_free(fcode_filename);
|
g_free(fcode_filename);
|
||||||
if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
|
if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
|
||||||
error_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
|
error_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
|
||||||
@@ -1076,7 +901,6 @@ static Property tcx_properties[] = {
|
|||||||
DEFINE_PROP_UINT16("width", TCXState, width, -1),
|
DEFINE_PROP_UINT16("width", TCXState, width, -1),
|
||||||
DEFINE_PROP_UINT16("height", TCXState, height, -1),
|
DEFINE_PROP_UINT16("height", TCXState, height, -1),
|
||||||
DEFINE_PROP_UINT16("depth", TCXState, depth, -1),
|
DEFINE_PROP_UINT16("depth", TCXState, depth, -1),
|
||||||
DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1434,6 +1434,14 @@ void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool vga_scanline_invalidated(VGACommonState *s, int y)
|
||||||
|
{
|
||||||
|
if (y >= VGA_MAX_HEIGHT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return s->invalidated_y_table[y >> 5] & (1 << (y & 0x1f));
|
||||||
|
}
|
||||||
|
|
||||||
void vga_sync_dirty_bitmap(VGACommonState *s)
|
void vga_sync_dirty_bitmap(VGACommonState *s)
|
||||||
{
|
{
|
||||||
memory_region_sync_dirty_bitmap(&s->vram);
|
memory_region_sync_dirty_bitmap(&s->vram);
|
||||||
@@ -1457,7 +1465,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
|
|||||||
DisplaySurface *surface = qemu_console_surface(s->con);
|
DisplaySurface *surface = qemu_console_surface(s->con);
|
||||||
int y1, y, update, linesize, y_start, double_scan, mask, depth;
|
int y1, y, update, linesize, y_start, double_scan, mask, depth;
|
||||||
int width, height, shift_control, line_offset, bwidth, bits;
|
int width, height, shift_control, line_offset, bwidth, bits;
|
||||||
ram_addr_t page0, page1, page_min, page_max;
|
ram_addr_t page0, page1;
|
||||||
|
DirtyBitmapSnapshot *snap = NULL;
|
||||||
int disp_width, multi_scan, multi_run;
|
int disp_width, multi_scan, multi_run;
|
||||||
uint8_t *d;
|
uint8_t *d;
|
||||||
uint32_t v, addr1, addr;
|
uint32_t v, addr1, addr;
|
||||||
@@ -1472,9 +1481,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
|
|||||||
|
|
||||||
full_update |= update_basic_params(s);
|
full_update |= update_basic_params(s);
|
||||||
|
|
||||||
if (!full_update)
|
|
||||||
vga_sync_dirty_bitmap(s);
|
|
||||||
|
|
||||||
s->get_resolution(s, &width, &height);
|
s->get_resolution(s, &width, &height);
|
||||||
disp_width = width;
|
disp_width = width;
|
||||||
|
|
||||||
@@ -1617,11 +1623,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
|
|||||||
addr1 = (s->start_addr * 4);
|
addr1 = (s->start_addr * 4);
|
||||||
bwidth = (width * bits + 7) / 8;
|
bwidth = (width * bits + 7) / 8;
|
||||||
y_start = -1;
|
y_start = -1;
|
||||||
page_min = -1;
|
|
||||||
page_max = 0;
|
|
||||||
d = surface_data(surface);
|
d = surface_data(surface);
|
||||||
linesize = surface_stride(surface);
|
linesize = surface_stride(surface);
|
||||||
y1 = 0;
|
y1 = 0;
|
||||||
|
|
||||||
|
if (!full_update) {
|
||||||
|
vga_sync_dirty_bitmap(s);
|
||||||
|
snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1,
|
||||||
|
bwidth * height,
|
||||||
|
DIRTY_MEMORY_VGA);
|
||||||
|
}
|
||||||
|
|
||||||
for(y = 0; y < height; y++) {
|
for(y = 0; y < height; y++) {
|
||||||
addr = addr1;
|
addr = addr1;
|
||||||
if (!(s->cr[VGA_CRTC_MODE] & 1)) {
|
if (!(s->cr[VGA_CRTC_MODE] & 1)) {
|
||||||
@@ -1636,17 +1648,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
|
|||||||
update = full_update;
|
update = full_update;
|
||||||
page0 = addr;
|
page0 = addr;
|
||||||
page1 = addr + bwidth - 1;
|
page1 = addr + bwidth - 1;
|
||||||
update |= memory_region_get_dirty(&s->vram, page0, page1 - page0,
|
if (full_update) {
|
||||||
DIRTY_MEMORY_VGA);
|
update = 1;
|
||||||
/* explicit invalidation for the hardware cursor */
|
} else {
|
||||||
update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
|
update = memory_region_snapshot_get_dirty(&s->vram, snap,
|
||||||
|
page0, page1 - page0);
|
||||||
|
}
|
||||||
|
/* explicit invalidation for the hardware cursor (cirrus only) */
|
||||||
|
update |= vga_scanline_invalidated(s, y);
|
||||||
if (update) {
|
if (update) {
|
||||||
if (y_start < 0)
|
if (y_start < 0)
|
||||||
y_start = y;
|
y_start = y;
|
||||||
if (page0 < page_min)
|
|
||||||
page_min = page0;
|
|
||||||
if (page1 > page_max)
|
|
||||||
page_max = page1;
|
|
||||||
if (!(is_buffer_shared(surface))) {
|
if (!(is_buffer_shared(surface))) {
|
||||||
vga_draw_line(s, d, s->vram_ptr + addr, width);
|
vga_draw_line(s, d, s->vram_ptr + addr, width);
|
||||||
if (s->cursor_draw_line)
|
if (s->cursor_draw_line)
|
||||||
@@ -1679,14 +1691,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
|
|||||||
dpy_gfx_update(s->con, 0, y_start,
|
dpy_gfx_update(s->con, 0, y_start,
|
||||||
disp_width, y - y_start);
|
disp_width, y - y_start);
|
||||||
}
|
}
|
||||||
/* reset modified pages */
|
g_free(snap);
|
||||||
if (page_max >= page_min) {
|
memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table));
|
||||||
memory_region_reset_dirty(&s->vram,
|
|
||||||
page_min,
|
|
||||||
page_max - page_min,
|
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
}
|
|
||||||
memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vga_draw_blank(VGACommonState *s, int full_update)
|
static void vga_draw_blank(VGACommonState *s, int full_update)
|
||||||
|
@@ -258,41 +258,22 @@ void virtio_gpu_get_display_info(VirtIOGPU *g,
|
|||||||
static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format)
|
static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format)
|
||||||
{
|
{
|
||||||
switch (virtio_gpu_format) {
|
switch (virtio_gpu_format) {
|
||||||
#ifdef HOST_WORDS_BIGENDIAN
|
|
||||||
case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
|
case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
|
||||||
return PIXMAN_b8g8r8x8;
|
return PIXMAN_BE_b8g8r8x8;
|
||||||
case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
|
case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
|
||||||
return PIXMAN_b8g8r8a8;
|
return PIXMAN_BE_b8g8r8a8;
|
||||||
case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
|
case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
|
||||||
return PIXMAN_x8r8g8b8;
|
return PIXMAN_BE_x8r8g8b8;
|
||||||
case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
|
case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
|
||||||
return PIXMAN_a8r8g8b8;
|
return PIXMAN_BE_a8r8g8b8;
|
||||||
case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
|
case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
|
||||||
return PIXMAN_r8g8b8x8;
|
return PIXMAN_BE_r8g8b8x8;
|
||||||
case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
|
case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
|
||||||
return PIXMAN_r8g8b8a8;
|
return PIXMAN_BE_r8g8b8a8;
|
||||||
case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
|
case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
|
||||||
return PIXMAN_x8b8g8r8;
|
return PIXMAN_BE_x8b8g8r8;
|
||||||
case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
|
case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
|
||||||
return PIXMAN_a8b8g8r8;
|
return PIXMAN_BE_a8b8g8r8;
|
||||||
#else
|
|
||||||
case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
|
|
||||||
return PIXMAN_x8r8g8b8;
|
|
||||||
case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
|
|
||||||
return PIXMAN_a8r8g8b8;
|
|
||||||
case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
|
|
||||||
return PIXMAN_b8g8r8x8;
|
|
||||||
case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
|
|
||||||
return PIXMAN_b8g8r8a8;
|
|
||||||
case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
|
|
||||||
return PIXMAN_x8b8g8r8;
|
|
||||||
case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
|
|
||||||
return PIXMAN_a8b8g8r8;
|
|
||||||
case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
|
|
||||||
return PIXMAN_r8g8b8x8;
|
|
||||||
case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
|
|
||||||
return PIXMAN_r8g8b8a8;
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1170,8 +1151,8 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
|
|||||||
virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
|
virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
|
||||||
g->config_size);
|
g->config_size);
|
||||||
|
|
||||||
g->req_state[0].width = 1024;
|
g->req_state[0].width = g->conf.xres;
|
||||||
g->req_state[0].height = 768;
|
g->req_state[0].height = g->conf.yres;
|
||||||
|
|
||||||
if (virtio_gpu_virgl_enabled(g->conf)) {
|
if (virtio_gpu_virgl_enabled(g->conf)) {
|
||||||
/* use larger control queue in 3d mode */
|
/* use larger control queue in 3d mode */
|
||||||
@@ -1291,6 +1272,8 @@ static Property virtio_gpu_properties[] = {
|
|||||||
DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags,
|
DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags,
|
||||||
VIRTIO_GPU_FLAG_STATS_ENABLED, false),
|
VIRTIO_GPU_FLAG_STATS_ENABLED, false),
|
||||||
#endif
|
#endif
|
||||||
|
DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024),
|
||||||
|
DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1118,9 +1118,9 @@ static void vmsvga_update_display(void *opaque)
|
|||||||
{
|
{
|
||||||
struct vmsvga_state_s *s = opaque;
|
struct vmsvga_state_s *s = opaque;
|
||||||
DisplaySurface *surface;
|
DisplaySurface *surface;
|
||||||
bool dirty = false;
|
|
||||||
|
|
||||||
if (!s->enable) {
|
if (!s->enable || !s->config) {
|
||||||
|
/* in standard vga mode */
|
||||||
s->vga.hw_ops->gfx_update(&s->vga);
|
s->vga.hw_ops->gfx_update(&s->vga);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1131,26 +1131,11 @@ static void vmsvga_update_display(void *opaque)
|
|||||||
vmsvga_fifo_run(s);
|
vmsvga_fifo_run(s);
|
||||||
vmsvga_update_rect_flush(s);
|
vmsvga_update_rect_flush(s);
|
||||||
|
|
||||||
/*
|
if (s->invalidated) {
|
||||||
* Is it more efficient to look at vram VGA-dirty bits or wait
|
|
||||||
* for the driver to issue SVGA_CMD_UPDATE?
|
|
||||||
*/
|
|
||||||
if (memory_region_is_logging(&s->vga.vram, DIRTY_MEMORY_VGA)) {
|
|
||||||
vga_sync_dirty_bitmap(&s->vga);
|
|
||||||
dirty = memory_region_get_dirty(&s->vga.vram, 0,
|
|
||||||
surface_stride(surface) * surface_height(surface),
|
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
}
|
|
||||||
if (s->invalidated || dirty) {
|
|
||||||
s->invalidated = 0;
|
s->invalidated = 0;
|
||||||
dpy_gfx_update(s->vga.con, 0, 0,
|
dpy_gfx_update(s->vga.con, 0, 0,
|
||||||
surface_width(surface), surface_height(surface));
|
surface_width(surface), surface_height(surface));
|
||||||
}
|
}
|
||||||
if (dirty) {
|
|
||||||
memory_region_reset_dirty(&s->vga.vram, 0,
|
|
||||||
surface_stride(surface) * surface_height(surface),
|
|
||||||
DIRTY_MEMORY_VGA);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vmsvga_reset(DeviceState *dev)
|
static void vmsvga_reset(DeviceState *dev)
|
||||||
|
@@ -595,6 +595,22 @@ static inline uint32_t vtd_get_agaw_from_context_entry(VTDContextEntry *ce)
|
|||||||
return 30 + (ce->hi & VTD_CONTEXT_ENTRY_AW) * 9;
|
return 30 + (ce->hi & VTD_CONTEXT_ENTRY_AW) * 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint64_t vtd_iova_limit(VTDContextEntry *ce)
|
||||||
|
{
|
||||||
|
uint32_t ce_agaw = vtd_get_agaw_from_context_entry(ce);
|
||||||
|
return 1ULL << MIN(ce_agaw, VTD_MGAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if IOVA passes range check, otherwise false. */
|
||||||
|
static inline bool vtd_iova_range_check(uint64_t iova, VTDContextEntry *ce)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check if @iova is above 2^X-1, where X is the minimum of MGAW
|
||||||
|
* in CAP_REG and AW in context-entry.
|
||||||
|
*/
|
||||||
|
return !(iova & ~(vtd_iova_limit(ce) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
static const uint64_t vtd_paging_entry_rsvd_field[] = {
|
static const uint64_t vtd_paging_entry_rsvd_field[] = {
|
||||||
[0] = ~0ULL,
|
[0] = ~0ULL,
|
||||||
/* For not large page */
|
/* For not large page */
|
||||||
@@ -630,13 +646,9 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write,
|
|||||||
uint32_t level = vtd_get_level_from_context_entry(ce);
|
uint32_t level = vtd_get_level_from_context_entry(ce);
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
uint64_t slpte;
|
uint64_t slpte;
|
||||||
uint32_t ce_agaw = vtd_get_agaw_from_context_entry(ce);
|
|
||||||
uint64_t access_right_check;
|
uint64_t access_right_check;
|
||||||
|
|
||||||
/* Check if @iova is above 2^X-1, where X is the minimum of MGAW
|
if (!vtd_iova_range_check(iova, ce)) {
|
||||||
* in CAP_REG and AW in context-entry.
|
|
||||||
*/
|
|
||||||
if (iova & ~((1ULL << MIN(ce_agaw, VTD_MGAW)) - 1)) {
|
|
||||||
VTD_DPRINTF(GENERAL, "error: iova 0x%"PRIx64 " exceeds limits", iova);
|
VTD_DPRINTF(GENERAL, "error: iova 0x%"PRIx64 " exceeds limits", iova);
|
||||||
return -VTD_FR_ADDR_BEYOND_MGAW;
|
return -VTD_FR_ADDR_BEYOND_MGAW;
|
||||||
}
|
}
|
||||||
@@ -684,6 +696,135 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vtd_page_walk_level - walk over specific level for IOVA range
|
||||||
|
*
|
||||||
|
* @addr: base GPA addr to start the walk
|
||||||
|
* @start: IOVA range start address
|
||||||
|
* @end: IOVA range end address (start <= addr < end)
|
||||||
|
* @hook_fn: hook func to be called when detected page
|
||||||
|
* @private: private data to be passed into hook func
|
||||||
|
* @read: whether parent level has read permission
|
||||||
|
* @write: whether parent level has write permission
|
||||||
|
* @notify_unmap: whether we should notify invalid entries
|
||||||
|
*/
|
||||||
|
static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,
|
||||||
|
uint64_t end, vtd_page_walk_hook hook_fn,
|
||||||
|
void *private, uint32_t level,
|
||||||
|
bool read, bool write, bool notify_unmap)
|
||||||
|
{
|
||||||
|
bool read_cur, write_cur, entry_valid;
|
||||||
|
uint32_t offset;
|
||||||
|
uint64_t slpte;
|
||||||
|
uint64_t subpage_size, subpage_mask;
|
||||||
|
IOMMUTLBEntry entry;
|
||||||
|
uint64_t iova = start;
|
||||||
|
uint64_t iova_next;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
trace_vtd_page_walk_level(addr, level, start, end);
|
||||||
|
|
||||||
|
subpage_size = 1ULL << vtd_slpt_level_shift(level);
|
||||||
|
subpage_mask = vtd_slpt_level_page_mask(level);
|
||||||
|
|
||||||
|
while (iova < end) {
|
||||||
|
iova_next = (iova & subpage_mask) + subpage_size;
|
||||||
|
|
||||||
|
offset = vtd_iova_level_offset(iova, level);
|
||||||
|
slpte = vtd_get_slpte(addr, offset);
|
||||||
|
|
||||||
|
if (slpte == (uint64_t)-1) {
|
||||||
|
trace_vtd_page_walk_skip_read(iova, iova_next);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vtd_slpte_nonzero_rsvd(slpte, level)) {
|
||||||
|
trace_vtd_page_walk_skip_reserve(iova, iova_next);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Permissions are stacked with parents' */
|
||||||
|
read_cur = read && (slpte & VTD_SL_R);
|
||||||
|
write_cur = write && (slpte & VTD_SL_W);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As long as we have either read/write permission, this is a
|
||||||
|
* valid entry. The rule works for both page entries and page
|
||||||
|
* table entries.
|
||||||
|
*/
|
||||||
|
entry_valid = read_cur | write_cur;
|
||||||
|
|
||||||
|
if (vtd_is_last_slpte(slpte, level)) {
|
||||||
|
entry.target_as = &address_space_memory;
|
||||||
|
entry.iova = iova & subpage_mask;
|
||||||
|
/* NOTE: this is only meaningful if entry_valid == true */
|
||||||
|
entry.translated_addr = vtd_get_slpte_addr(slpte);
|
||||||
|
entry.addr_mask = ~subpage_mask;
|
||||||
|
entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur);
|
||||||
|
if (!entry_valid && !notify_unmap) {
|
||||||
|
trace_vtd_page_walk_skip_perm(iova, iova_next);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr,
|
||||||
|
entry.addr_mask, entry.perm);
|
||||||
|
if (hook_fn) {
|
||||||
|
ret = hook_fn(&entry, private);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!entry_valid) {
|
||||||
|
trace_vtd_page_walk_skip_perm(iova, iova_next);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte), iova,
|
||||||
|
MIN(iova_next, end), hook_fn, private,
|
||||||
|
level - 1, read_cur, write_cur,
|
||||||
|
notify_unmap);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
iova = iova_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vtd_page_walk - walk specific IOVA range, and call the hook
|
||||||
|
*
|
||||||
|
* @ce: context entry to walk upon
|
||||||
|
* @start: IOVA address to start the walk
|
||||||
|
* @end: IOVA range end address (start <= addr < end)
|
||||||
|
* @hook_fn: the hook that to be called for each detected area
|
||||||
|
* @private: private data for the hook function
|
||||||
|
*/
|
||||||
|
static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end,
|
||||||
|
vtd_page_walk_hook hook_fn, void *private,
|
||||||
|
bool notify_unmap)
|
||||||
|
{
|
||||||
|
dma_addr_t addr = vtd_get_slpt_base_from_context(ce);
|
||||||
|
uint32_t level = vtd_get_level_from_context_entry(ce);
|
||||||
|
|
||||||
|
if (!vtd_iova_range_check(start, ce)) {
|
||||||
|
return -VTD_FR_ADDR_BEYOND_MGAW;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vtd_iova_range_check(end, ce)) {
|
||||||
|
/* Fix end so that it reaches the maximum */
|
||||||
|
end = vtd_iova_limit(ce);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vtd_page_walk_level(addr, start, end, hook_fn, private,
|
||||||
|
level, true, true, notify_unmap);
|
||||||
|
}
|
||||||
|
|
||||||
/* Map a device to its corresponding domain (context-entry) */
|
/* Map a device to its corresponding domain (context-entry) */
|
||||||
static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num,
|
static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num,
|
||||||
uint8_t devfn, VTDContextEntry *ce)
|
uint8_t devfn, VTDContextEntry *ce)
|
||||||
@@ -898,6 +1039,15 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s)
|
|||||||
s->intr_root, s->intr_size);
|
s->intr_root, s->intr_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vtd_iommu_replay_all(IntelIOMMUState *s)
|
||||||
|
{
|
||||||
|
IntelIOMMUNotifierNode *node;
|
||||||
|
|
||||||
|
QLIST_FOREACH(node, &s->notifiers_list, next) {
|
||||||
|
memory_region_iommu_replay_all(&node->vtd_as->iommu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void vtd_context_global_invalidate(IntelIOMMUState *s)
|
static void vtd_context_global_invalidate(IntelIOMMUState *s)
|
||||||
{
|
{
|
||||||
trace_vtd_inv_desc_cc_global();
|
trace_vtd_inv_desc_cc_global();
|
||||||
@@ -905,6 +1055,14 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s)
|
|||||||
if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) {
|
if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) {
|
||||||
vtd_reset_context_cache(s);
|
vtd_reset_context_cache(s);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* From VT-d spec 6.5.2.1, a global context entry invalidation
|
||||||
|
* should be followed by a IOTLB global invalidation, so we should
|
||||||
|
* be safe even without this. Hoewever, let's replay the region as
|
||||||
|
* well to be safer, and go back here when we need finer tunes for
|
||||||
|
* VT-d emulation codes.
|
||||||
|
*/
|
||||||
|
vtd_iommu_replay_all(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -971,6 +1129,16 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
|
|||||||
trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it),
|
trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it),
|
||||||
VTD_PCI_FUNC(devfn_it));
|
VTD_PCI_FUNC(devfn_it));
|
||||||
vtd_as->context_cache_entry.context_cache_gen = 0;
|
vtd_as->context_cache_entry.context_cache_gen = 0;
|
||||||
|
/*
|
||||||
|
* So a device is moving out of (or moving into) a
|
||||||
|
* domain, a replay() suites here to notify all the
|
||||||
|
* IOMMU_NOTIFIER_MAP registers about this change.
|
||||||
|
* This won't bring bad even if we have no such
|
||||||
|
* notifier registered - the IOMMU notification
|
||||||
|
* framework will skip MAP notifications if that
|
||||||
|
* happened.
|
||||||
|
*/
|
||||||
|
memory_region_iommu_replay_all(&vtd_as->iommu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1012,12 +1180,53 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s)
|
|||||||
{
|
{
|
||||||
trace_vtd_iotlb_reset("global invalidation recved");
|
trace_vtd_iotlb_reset("global invalidation recved");
|
||||||
vtd_reset_iotlb(s);
|
vtd_reset_iotlb(s);
|
||||||
|
vtd_iommu_replay_all(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id)
|
static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id)
|
||||||
{
|
{
|
||||||
|
IntelIOMMUNotifierNode *node;
|
||||||
|
VTDContextEntry ce;
|
||||||
|
VTDAddressSpace *vtd_as;
|
||||||
|
|
||||||
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain,
|
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain,
|
||||||
&domain_id);
|
&domain_id);
|
||||||
|
|
||||||
|
QLIST_FOREACH(node, &s->notifiers_list, next) {
|
||||||
|
vtd_as = node->vtd_as;
|
||||||
|
if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
|
||||||
|
vtd_as->devfn, &ce) &&
|
||||||
|
domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) {
|
||||||
|
memory_region_iommu_replay_all(&vtd_as->iommu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry,
|
||||||
|
void *private)
|
||||||
|
{
|
||||||
|
memory_region_notify_iommu((MemoryRegion *)private, *entry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s,
|
||||||
|
uint16_t domain_id, hwaddr addr,
|
||||||
|
uint8_t am)
|
||||||
|
{
|
||||||
|
IntelIOMMUNotifierNode *node;
|
||||||
|
VTDContextEntry ce;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
QLIST_FOREACH(node, &(s->notifiers_list), next) {
|
||||||
|
VTDAddressSpace *vtd_as = node->vtd_as;
|
||||||
|
ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
|
||||||
|
vtd_as->devfn, &ce);
|
||||||
|
if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) {
|
||||||
|
vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE,
|
||||||
|
vtd_page_invalidate_notify_hook,
|
||||||
|
(void *)&vtd_as->iommu, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
|
static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
|
||||||
@@ -1030,6 +1239,7 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
|
|||||||
info.addr = addr;
|
info.addr = addr;
|
||||||
info.mask = ~((1 << am) - 1);
|
info.mask = ~((1 << am) - 1);
|
||||||
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
|
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
|
||||||
|
vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flush IOTLB
|
/* Flush IOTLB
|
||||||
@@ -1151,9 +1361,49 @@ static void vtd_handle_gcmd_sirtp(IntelIOMMUState *s)
|
|||||||
vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_IRTPS);
|
vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_IRTPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vtd_switch_address_space(VTDAddressSpace *as)
|
||||||
|
{
|
||||||
|
assert(as);
|
||||||
|
|
||||||
|
trace_vtd_switch_address_space(pci_bus_num(as->bus),
|
||||||
|
VTD_PCI_SLOT(as->devfn),
|
||||||
|
VTD_PCI_FUNC(as->devfn),
|
||||||
|
as->iommu_state->dmar_enabled);
|
||||||
|
|
||||||
|
/* Turn off first then on the other */
|
||||||
|
if (as->iommu_state->dmar_enabled) {
|
||||||
|
memory_region_set_enabled(&as->sys_alias, false);
|
||||||
|
memory_region_set_enabled(&as->iommu, true);
|
||||||
|
} else {
|
||||||
|
memory_region_set_enabled(&as->iommu, false);
|
||||||
|
memory_region_set_enabled(&as->sys_alias, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vtd_switch_address_space_all(IntelIOMMUState *s)
|
||||||
|
{
|
||||||
|
GHashTableIter iter;
|
||||||
|
VTDBus *vtd_bus;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
g_hash_table_iter_init(&iter, s->vtd_as_by_busptr);
|
||||||
|
while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) {
|
||||||
|
for (i = 0; i < X86_IOMMU_PCI_DEVFN_MAX; i++) {
|
||||||
|
if (!vtd_bus->dev_as[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
vtd_switch_address_space(vtd_bus->dev_as[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle Translation Enable/Disable */
|
/* Handle Translation Enable/Disable */
|
||||||
static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en)
|
static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en)
|
||||||
{
|
{
|
||||||
|
if (s->dmar_enabled == en) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
VTD_DPRINTF(CSR, "Translation Enable %s", (en ? "on" : "off"));
|
VTD_DPRINTF(CSR, "Translation Enable %s", (en ? "on" : "off"));
|
||||||
|
|
||||||
if (en) {
|
if (en) {
|
||||||
@@ -1168,6 +1418,8 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en)
|
|||||||
/* Ok - report back to driver */
|
/* Ok - report back to driver */
|
||||||
vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0);
|
vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vtd_switch_address_space_all(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle Interrupt Remap Enable/Disable */
|
/* Handle Interrupt Remap Enable/Disable */
|
||||||
@@ -1457,7 +1709,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s,
|
|||||||
entry.iova = addr;
|
entry.iova = addr;
|
||||||
entry.perm = IOMMU_NONE;
|
entry.perm = IOMMU_NONE;
|
||||||
entry.translated_addr = 0;
|
entry.translated_addr = 0;
|
||||||
memory_region_notify_iommu(entry.target_as->root, entry);
|
memory_region_notify_iommu(&vtd_dev_as->iommu, entry);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
return true;
|
return true;
|
||||||
@@ -2005,15 +2257,33 @@ static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu,
|
|||||||
IOMMUNotifierFlag new)
|
IOMMUNotifierFlag new)
|
||||||
{
|
{
|
||||||
VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
|
VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
|
||||||
|
IntelIOMMUState *s = vtd_as->iommu_state;
|
||||||
|
IntelIOMMUNotifierNode *node = NULL;
|
||||||
|
IntelIOMMUNotifierNode *next_node = NULL;
|
||||||
|
|
||||||
if (new & IOMMU_NOTIFIER_MAP) {
|
if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) {
|
||||||
error_report("Device at bus %s addr %02x.%d requires iommu "
|
error_report("We need to set cache_mode=1 for intel-iommu to enable "
|
||||||
"notifier which is currently not supported by "
|
"device assignment with IOMMU protection.");
|
||||||
"intel-iommu emulation",
|
|
||||||
vtd_as->bus->qbus.name, PCI_SLOT(vtd_as->devfn),
|
|
||||||
PCI_FUNC(vtd_as->devfn));
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (old == IOMMU_NOTIFIER_NONE) {
|
||||||
|
node = g_malloc0(sizeof(*node));
|
||||||
|
node->vtd_as = vtd_as;
|
||||||
|
QLIST_INSERT_HEAD(&s->notifiers_list, node, next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update notifier node with new flags */
|
||||||
|
QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) {
|
||||||
|
if (node->vtd_as == vtd_as) {
|
||||||
|
if (new == IOMMU_NOTIFIER_NONE) {
|
||||||
|
QLIST_REMOVE(node, next);
|
||||||
|
g_free(node);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription vtd_vmstate = {
|
static const VMStateDescription vtd_vmstate = {
|
||||||
@@ -2389,19 +2659,150 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
|
|||||||
vtd_dev_as->devfn = (uint8_t)devfn;
|
vtd_dev_as->devfn = (uint8_t)devfn;
|
||||||
vtd_dev_as->iommu_state = s;
|
vtd_dev_as->iommu_state = s;
|
||||||
vtd_dev_as->context_cache_entry.context_cache_gen = 0;
|
vtd_dev_as->context_cache_entry.context_cache_gen = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory region relationships looks like (Address range shows
|
||||||
|
* only lower 32 bits to make it short in length...):
|
||||||
|
*
|
||||||
|
* |-----------------+-------------------+----------|
|
||||||
|
* | Name | Address range | Priority |
|
||||||
|
* |-----------------+-------------------+----------+
|
||||||
|
* | vtd_root | 00000000-ffffffff | 0 |
|
||||||
|
* | intel_iommu | 00000000-ffffffff | 1 |
|
||||||
|
* | vtd_sys_alias | 00000000-ffffffff | 1 |
|
||||||
|
* | intel_iommu_ir | fee00000-feefffff | 64 |
|
||||||
|
* |-----------------+-------------------+----------|
|
||||||
|
*
|
||||||
|
* We enable/disable DMAR by switching enablement for
|
||||||
|
* vtd_sys_alias and intel_iommu regions. IR region is always
|
||||||
|
* enabled.
|
||||||
|
*/
|
||||||
memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s),
|
memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s),
|
||||||
&s->iommu_ops, "intel_iommu", UINT64_MAX);
|
&s->iommu_ops, "intel_iommu_dmar",
|
||||||
|
UINT64_MAX);
|
||||||
|
memory_region_init_alias(&vtd_dev_as->sys_alias, OBJECT(s),
|
||||||
|
"vtd_sys_alias", get_system_memory(),
|
||||||
|
0, memory_region_size(get_system_memory()));
|
||||||
memory_region_init_io(&vtd_dev_as->iommu_ir, OBJECT(s),
|
memory_region_init_io(&vtd_dev_as->iommu_ir, OBJECT(s),
|
||||||
&vtd_mem_ir_ops, s, "intel_iommu_ir",
|
&vtd_mem_ir_ops, s, "intel_iommu_ir",
|
||||||
VTD_INTERRUPT_ADDR_SIZE);
|
VTD_INTERRUPT_ADDR_SIZE);
|
||||||
memory_region_add_subregion(&vtd_dev_as->iommu, VTD_INTERRUPT_ADDR_FIRST,
|
memory_region_init(&vtd_dev_as->root, OBJECT(s),
|
||||||
&vtd_dev_as->iommu_ir);
|
"vtd_root", UINT64_MAX);
|
||||||
address_space_init(&vtd_dev_as->as,
|
memory_region_add_subregion_overlap(&vtd_dev_as->root,
|
||||||
&vtd_dev_as->iommu, name);
|
VTD_INTERRUPT_ADDR_FIRST,
|
||||||
|
&vtd_dev_as->iommu_ir, 64);
|
||||||
|
address_space_init(&vtd_dev_as->as, &vtd_dev_as->root, name);
|
||||||
|
memory_region_add_subregion_overlap(&vtd_dev_as->root, 0,
|
||||||
|
&vtd_dev_as->sys_alias, 1);
|
||||||
|
memory_region_add_subregion_overlap(&vtd_dev_as->root, 0,
|
||||||
|
&vtd_dev_as->iommu, 1);
|
||||||
|
vtd_switch_address_space(vtd_dev_as);
|
||||||
}
|
}
|
||||||
return vtd_dev_as;
|
return vtd_dev_as;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unmap the whole range in the notifier's scope. */
|
||||||
|
static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
|
||||||
|
{
|
||||||
|
IOMMUTLBEntry entry;
|
||||||
|
hwaddr size;
|
||||||
|
hwaddr start = n->start;
|
||||||
|
hwaddr end = n->end;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: all the codes in this function has a assumption that IOVA
|
||||||
|
* bits are no more than VTD_MGAW bits (which is restricted by
|
||||||
|
* VT-d spec), otherwise we need to consider overflow of 64 bits.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (end > VTD_ADDRESS_SIZE) {
|
||||||
|
/*
|
||||||
|
* Don't need to unmap regions that is bigger than the whole
|
||||||
|
* VT-d supported address space size
|
||||||
|
*/
|
||||||
|
end = VTD_ADDRESS_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(start <= end);
|
||||||
|
size = end - start;
|
||||||
|
|
||||||
|
if (ctpop64(size) != 1) {
|
||||||
|
/*
|
||||||
|
* This size cannot format a correct mask. Let's enlarge it to
|
||||||
|
* suite the minimum available mask.
|
||||||
|
*/
|
||||||
|
int n = 64 - clz64(size);
|
||||||
|
if (n > VTD_MGAW) {
|
||||||
|
/* should not happen, but in case it happens, limit it */
|
||||||
|
n = VTD_MGAW;
|
||||||
|
}
|
||||||
|
size = 1ULL << n;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.target_as = &address_space_memory;
|
||||||
|
/* Adjust iova for the size */
|
||||||
|
entry.iova = n->start & ~(size - 1);
|
||||||
|
/* This field is meaningless for unmap */
|
||||||
|
entry.translated_addr = 0;
|
||||||
|
entry.perm = IOMMU_NONE;
|
||||||
|
entry.addr_mask = size - 1;
|
||||||
|
|
||||||
|
trace_vtd_as_unmap_whole(pci_bus_num(as->bus),
|
||||||
|
VTD_PCI_SLOT(as->devfn),
|
||||||
|
VTD_PCI_FUNC(as->devfn),
|
||||||
|
entry.iova, size);
|
||||||
|
|
||||||
|
memory_region_notify_one(n, &entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vtd_address_space_unmap_all(IntelIOMMUState *s)
|
||||||
|
{
|
||||||
|
IntelIOMMUNotifierNode *node;
|
||||||
|
VTDAddressSpace *vtd_as;
|
||||||
|
IOMMUNotifier *n;
|
||||||
|
|
||||||
|
QLIST_FOREACH(node, &s->notifiers_list, next) {
|
||||||
|
vtd_as = node->vtd_as;
|
||||||
|
IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) {
|
||||||
|
vtd_address_space_unmap(vtd_as, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private)
|
||||||
|
{
|
||||||
|
memory_region_notify_one((IOMMUNotifier *)private, entry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vtd_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n)
|
||||||
|
{
|
||||||
|
VTDAddressSpace *vtd_as = container_of(mr, VTDAddressSpace, iommu);
|
||||||
|
IntelIOMMUState *s = vtd_as->iommu_state;
|
||||||
|
uint8_t bus_n = pci_bus_num(vtd_as->bus);
|
||||||
|
VTDContextEntry ce;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The replay can be triggered by either a invalidation or a newly
|
||||||
|
* created entry. No matter what, we release existing mappings
|
||||||
|
* (it means flushing caches for UNMAP-only registers).
|
||||||
|
*/
|
||||||
|
vtd_address_space_unmap(vtd_as, n);
|
||||||
|
|
||||||
|
if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) {
|
||||||
|
trace_vtd_replay_ce_valid(bus_n, PCI_SLOT(vtd_as->devfn),
|
||||||
|
PCI_FUNC(vtd_as->devfn),
|
||||||
|
VTD_CONTEXT_ENTRY_DID(ce.hi),
|
||||||
|
ce.hi, ce.lo);
|
||||||
|
vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false);
|
||||||
|
} else {
|
||||||
|
trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn),
|
||||||
|
PCI_FUNC(vtd_as->devfn));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Do the initialization. It will also be called when reset, so pay
|
/* Do the initialization. It will also be called when reset, so pay
|
||||||
* attention when adding new initialization stuff.
|
* attention when adding new initialization stuff.
|
||||||
*/
|
*/
|
||||||
@@ -2416,6 +2817,7 @@ static void vtd_init(IntelIOMMUState *s)
|
|||||||
|
|
||||||
s->iommu_ops.translate = vtd_iommu_translate;
|
s->iommu_ops.translate = vtd_iommu_translate;
|
||||||
s->iommu_ops.notify_flag_changed = vtd_iommu_notify_flag_changed;
|
s->iommu_ops.notify_flag_changed = vtd_iommu_notify_flag_changed;
|
||||||
|
s->iommu_ops.replay = vtd_iommu_replay;
|
||||||
s->root = 0;
|
s->root = 0;
|
||||||
s->root_extended = false;
|
s->root_extended = false;
|
||||||
s->dmar_enabled = false;
|
s->dmar_enabled = false;
|
||||||
@@ -2511,6 +2913,11 @@ static void vtd_reset(DeviceState *dev)
|
|||||||
|
|
||||||
VTD_DPRINTF(GENERAL, "");
|
VTD_DPRINTF(GENERAL, "");
|
||||||
vtd_init(s);
|
vtd_init(s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When device reset, throw away all mappings and external caches
|
||||||
|
*/
|
||||||
|
vtd_address_space_unmap_all(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
||||||
@@ -2574,6 +2981,7 @@ static void vtd_realize(DeviceState *dev, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QLIST_INIT(&s->notifiers_list);
|
||||||
memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
|
memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
|
||||||
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
|
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
|
||||||
"intel_iommu", DMAR_REG_SIZE);
|
"intel_iommu", DMAR_REG_SIZE);
|
||||||
|
@@ -197,6 +197,7 @@
|
|||||||
#define VTD_DOMAIN_ID_MASK ((1UL << VTD_DOMAIN_ID_SHIFT) - 1)
|
#define VTD_DOMAIN_ID_MASK ((1UL << VTD_DOMAIN_ID_SHIFT) - 1)
|
||||||
#define VTD_CAP_ND (((VTD_DOMAIN_ID_SHIFT - 4) / 2) & 7ULL)
|
#define VTD_CAP_ND (((VTD_DOMAIN_ID_SHIFT - 4) / 2) & 7ULL)
|
||||||
#define VTD_MGAW 39 /* Maximum Guest Address Width */
|
#define VTD_MGAW 39 /* Maximum Guest Address Width */
|
||||||
|
#define VTD_ADDRESS_SIZE (1ULL << VTD_MGAW)
|
||||||
#define VTD_CAP_MGAW (((VTD_MGAW - 1) & 0x3fULL) << 16)
|
#define VTD_CAP_MGAW (((VTD_MGAW - 1) & 0x3fULL) << 16)
|
||||||
#define VTD_MAMV 18ULL
|
#define VTD_MAMV 18ULL
|
||||||
#define VTD_CAP_MAMV (VTD_MAMV << 48)
|
#define VTD_CAP_MAMV (VTD_MAMV << 48)
|
||||||
|
@@ -1104,9 +1104,7 @@ static void pc_new_cpu(const char *typename, int64_t apic_id, Error **errp)
|
|||||||
object_property_set_bool(cpu, true, "realized", &local_err);
|
object_property_set_bool(cpu, true, "realized", &local_err);
|
||||||
|
|
||||||
object_unref(cpu);
|
object_unref(cpu);
|
||||||
if (local_err) {
|
error_propagate(errp, local_err);
|
||||||
error_propagate(errp, local_err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pc_hot_add_cpu(const int64_t id, Error **errp)
|
void pc_hot_add_cpu(const int64_t id, Error **errp)
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32
|
x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32
|
||||||
|
|
||||||
# hw/i386/intel_iommu.c
|
# hw/i386/intel_iommu.c
|
||||||
vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)"
|
|
||||||
vtd_inv_desc(const char *type, uint64_t hi, uint64_t lo) "invalidate desc type %s high 0x%"PRIx64" low 0x%"PRIx64
|
vtd_inv_desc(const char *type, uint64_t hi, uint64_t lo) "invalidate desc type %s high 0x%"PRIx64" low 0x%"PRIx64
|
||||||
vtd_inv_desc_invalid(uint64_t hi, uint64_t lo) "invalid inv desc hi 0x%"PRIx64" lo 0x%"PRIx64
|
vtd_inv_desc_invalid(uint64_t hi, uint64_t lo) "invalid inv desc hi 0x%"PRIx64" lo 0x%"PRIx64
|
||||||
vtd_inv_desc_cc_domain(uint16_t domain) "context invalidate domain 0x%"PRIx16
|
vtd_inv_desc_cc_domain(uint16_t domain) "context invalidate domain 0x%"PRIx16
|
||||||
@@ -30,6 +29,15 @@ vtd_iotlb_cc_hit(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32
|
|||||||
vtd_iotlb_cc_update(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32_t gen1, uint32_t gen2) "IOTLB context update bus 0x%"PRIx8" devfn 0x%"PRIx8" high 0x%"PRIx64" low 0x%"PRIx64" gen %"PRIu32" -> gen %"PRIu32
|
vtd_iotlb_cc_update(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32_t gen1, uint32_t gen2) "IOTLB context update bus 0x%"PRIx8" devfn 0x%"PRIx8" high 0x%"PRIx64" low 0x%"PRIx64" gen %"PRIu32" -> gen %"PRIu32
|
||||||
vtd_iotlb_reset(const char *reason) "IOTLB reset (reason: %s)"
|
vtd_iotlb_reset(const char *reason) "IOTLB reset (reason: %s)"
|
||||||
vtd_fault_disabled(void) "Fault processing disabled for context entry"
|
vtd_fault_disabled(void) "Fault processing disabled for context entry"
|
||||||
|
vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64
|
||||||
|
vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8
|
||||||
|
vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64
|
||||||
|
vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d"
|
||||||
|
vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read"
|
||||||
|
vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty"
|
||||||
|
vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set"
|
||||||
|
vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)"
|
||||||
|
vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64
|
||||||
|
|
||||||
# hw/i386/amd_iommu.c
|
# hw/i386/amd_iommu.c
|
||||||
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32
|
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32
|
||||||
|
@@ -1 +1 @@
|
|||||||
obj-y += xen_platform.o xen_apic.o xen_pvdevice.o
|
obj-y += xen_platform.o xen_apic.o xen_pvdevice.o xen-hvm.o xen-mapcache.o
|
||||||
|
@@ -4,3 +4,20 @@ xen_platform_log(char *s) "xen platform: %s"
|
|||||||
# hw/i386/xen/xen_pvdevice.c
|
# hw/i386/xen/xen_pvdevice.c
|
||||||
xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address %"PRIx64")"
|
xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address %"PRIx64")"
|
||||||
xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address %"PRIx64")"
|
xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address %"PRIx64")"
|
||||||
|
|
||||||
|
# xen-hvm.c
|
||||||
|
xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx"
|
||||||
|
xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "%#"PRIx64" size %#lx, log_dirty %i"
|
||||||
|
handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=%#"PRIx64" port=%#"PRIx64" size=%d"
|
||||||
|
cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=%#"PRIx64" port=%#"PRIx64" size=%d"
|
||||||
|
cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
|
||||||
|
# xen-mapcache.c
|
||||||
|
xen_map_cache(uint64_t phys_addr) "want %#"PRIx64
|
||||||
|
xen_remap_bucket(uint64_t index) "index %#"PRIx64
|
||||||
|
xen_map_cache_return(void* ptr) "%p"
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/range.h"
|
#include "qemu/range.h"
|
||||||
#include "sysemu/xen-mapcache.h"
|
#include "sysemu/xen-mapcache.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
|
|
||||||
#include <xen/hvm/ioreq.h>
|
#include <xen/hvm/ioreq.h>
|
||||||
@@ -125,8 +125,8 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
|
|||||||
|
|
||||||
void xen_piix3_set_irq(void *opaque, int irq_num, int level)
|
void xen_piix3_set_irq(void *opaque, int irq_num, int level)
|
||||||
{
|
{
|
||||||
xc_hvm_set_pci_intx_level(xen_xc, xen_domid, 0, 0, irq_num >> 2,
|
xen_set_pci_intx_level(xen_domid, 0, 0, irq_num >> 2,
|
||||||
irq_num & 3, level);
|
irq_num & 3, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len)
|
void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len)
|
||||||
@@ -141,7 +141,7 @@ void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len)
|
|||||||
}
|
}
|
||||||
v &= 0xf;
|
v &= 0xf;
|
||||||
if (((address + i) >= 0x60) && ((address + i) <= 0x63)) {
|
if (((address + i) >= 0x60) && ((address + i) <= 0x63)) {
|
||||||
xc_hvm_set_pci_link_route(xen_xc, xen_domid, address + i - 0x60, v);
|
xen_set_pci_link_route(xen_domid, address + i - 0x60, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ int xen_is_pirq_msi(uint32_t msi_data)
|
|||||||
|
|
||||||
void xen_hvm_inject_msi(uint64_t addr, uint32_t data)
|
void xen_hvm_inject_msi(uint64_t addr, uint32_t data)
|
||||||
{
|
{
|
||||||
xc_hvm_inject_msi(xen_xc, xen_domid, addr, data);
|
xen_inject_msi(xen_domid, addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xen_suspend_notifier(Notifier *notifier, void *data)
|
static void xen_suspend_notifier(Notifier *notifier, void *data)
|
||||||
@@ -168,7 +168,7 @@ static void xen_suspend_notifier(Notifier *notifier, void *data)
|
|||||||
|
|
||||||
static void xen_set_irq(void *opaque, int irq, int level)
|
static void xen_set_irq(void *opaque, int irq, int level)
|
||||||
{
|
{
|
||||||
xc_hvm_set_isa_irq_level(xen_xc, xen_domid, irq, level);
|
xen_set_isa_irq_level(xen_domid, irq, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_irq *xen_interrupt_controller_init(void)
|
qemu_irq *xen_interrupt_controller_init(void)
|
||||||
@@ -454,10 +454,10 @@ static void xen_set_memory(struct MemoryListener *listener,
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (add) {
|
if (add) {
|
||||||
xen_map_memory_section(xen_xc, xen_domid, state->ioservid,
|
xen_map_memory_section(xen_domid, state->ioservid,
|
||||||
section);
|
section);
|
||||||
} else {
|
} else {
|
||||||
xen_unmap_memory_section(xen_xc, xen_domid, state->ioservid,
|
xen_unmap_memory_section(xen_domid, state->ioservid,
|
||||||
section);
|
section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -481,10 +481,10 @@ static void xen_set_memory(struct MemoryListener *listener,
|
|||||||
section->mr, section->offset_within_region);
|
section->mr, section->offset_within_region);
|
||||||
} else {
|
} else {
|
||||||
mem_type = HVMMEM_ram_ro;
|
mem_type = HVMMEM_ram_ro;
|
||||||
if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type,
|
if (xen_set_mem_type(xen_domid, mem_type,
|
||||||
start_addr >> TARGET_PAGE_BITS,
|
start_addr >> TARGET_PAGE_BITS,
|
||||||
size >> TARGET_PAGE_BITS)) {
|
size >> TARGET_PAGE_BITS)) {
|
||||||
DPRINTF("xc_hvm_set_mem_type error, addr: "TARGET_FMT_plx"\n",
|
DPRINTF("xen_set_mem_type error, addr: "TARGET_FMT_plx"\n",
|
||||||
start_addr);
|
start_addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -521,7 +521,7 @@ static void xen_io_add(MemoryListener *listener,
|
|||||||
|
|
||||||
memory_region_ref(mr);
|
memory_region_ref(mr);
|
||||||
|
|
||||||
xen_map_io_section(xen_xc, xen_domid, state->ioservid, section);
|
xen_map_io_section(xen_domid, state->ioservid, section);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xen_io_del(MemoryListener *listener,
|
static void xen_io_del(MemoryListener *listener,
|
||||||
@@ -534,7 +534,7 @@ static void xen_io_del(MemoryListener *listener,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
xen_unmap_io_section(xen_xc, xen_domid, state->ioservid, section);
|
xen_unmap_io_section(xen_domid, state->ioservid, section);
|
||||||
|
|
||||||
memory_region_unref(mr);
|
memory_region_unref(mr);
|
||||||
}
|
}
|
||||||
@@ -547,7 +547,7 @@ static void xen_device_realize(DeviceListener *listener,
|
|||||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||||
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
||||||
|
|
||||||
xen_map_pcidev(xen_xc, xen_domid, state->ioservid, pci_dev);
|
xen_map_pcidev(xen_domid, state->ioservid, pci_dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,7 +559,7 @@ static void xen_device_unrealize(DeviceListener *listener,
|
|||||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||||
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
||||||
|
|
||||||
xen_unmap_pcidev(xen_xc, xen_domid, state->ioservid, pci_dev);
|
xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,9 +586,8 @@ static void xen_sync_dirty_bitmap(XenIOState *state,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = xc_hvm_track_dirty_vram(xen_xc, xen_domid,
|
rc = xen_track_dirty_vram(xen_domid, start_addr >> TARGET_PAGE_BITS,
|
||||||
start_addr >> TARGET_PAGE_BITS, npages,
|
npages, bitmap);
|
||||||
bitmap);
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
#ifndef ENODATA
|
#ifndef ENODATA
|
||||||
#define ENODATA ENOENT
|
#define ENODATA ENOENT
|
||||||
@@ -634,7 +633,7 @@ static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section,
|
|||||||
if (old & ~new & (1 << DIRTY_MEMORY_VGA)) {
|
if (old & ~new & (1 << DIRTY_MEMORY_VGA)) {
|
||||||
state->log_for_dirtybit = NULL;
|
state->log_for_dirtybit = NULL;
|
||||||
/* Disable dirty bit tracking */
|
/* Disable dirty bit tracking */
|
||||||
xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL);
|
xen_track_dirty_vram(xen_domid, 0, 0, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1139,7 +1138,7 @@ static void xen_hvm_change_state_handler(void *opaque, int running,
|
|||||||
xen_main_loop_prepare(state);
|
xen_main_loop_prepare(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
xen_set_ioreq_server_state(xen_xc, xen_domid,
|
xen_set_ioreq_server_state(xen_domid,
|
||||||
state->ioservid,
|
state->ioservid,
|
||||||
(rstate == RUN_STATE_RUNNING));
|
(rstate == RUN_STATE_RUNNING));
|
||||||
}
|
}
|
||||||
@@ -1227,7 +1226,15 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
xen_create_ioreq_server(xen_xc, xen_domid, &state->ioservid);
|
if (xen_domid_restrict) {
|
||||||
|
rc = xen_restrict(xen_domid);
|
||||||
|
if (rc < 0) {
|
||||||
|
error_report("failed to restrict: error %d", errno);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xen_create_ioreq_server(xen_domid, &state->ioservid);
|
||||||
|
|
||||||
state->exit.notify = xen_exit_notifier;
|
state->exit.notify = xen_exit_notifier;
|
||||||
qemu_add_exit_notifier(&state->exit);
|
qemu_add_exit_notifier(&state->exit);
|
||||||
@@ -1238,7 +1245,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory)
|
|||||||
state->wakeup.notify = xen_wakeup_notifier;
|
state->wakeup.notify = xen_wakeup_notifier;
|
||||||
qemu_register_wakeup_notifier(&state->wakeup);
|
qemu_register_wakeup_notifier(&state->wakeup);
|
||||||
|
|
||||||
rc = xen_get_ioreq_server_info(xen_xc, xen_domid, state->ioservid,
|
rc = xen_get_ioreq_server_info(xen_domid, state->ioservid,
|
||||||
&ioreq_pfn, &bufioreq_pfn,
|
&ioreq_pfn, &bufioreq_pfn,
|
||||||
&bufioreq_evtchn);
|
&bufioreq_evtchn);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
@@ -1288,7 +1295,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory)
|
|||||||
/* Note: cpus is empty at this point in init */
|
/* Note: cpus is empty at this point in init */
|
||||||
state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *));
|
state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *));
|
||||||
|
|
||||||
rc = xen_set_ioreq_server_state(xen_xc, xen_domid, state->ioservid, true);
|
rc = xen_set_ioreq_server_state(xen_domid, state->ioservid, true);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
error_report("failed to enable ioreq server info: error %d handle=%p",
|
error_report("failed to enable ioreq server info: error %d handle=%p",
|
||||||
errno, xen_xc);
|
errno, xen_xc);
|
||||||
@@ -1391,7 +1398,7 @@ void xen_shutdown_fatal_error(const char *fmt, ...)
|
|||||||
qemu_system_shutdown_request();
|
qemu_system_shutdown_request();
|
||||||
}
|
}
|
||||||
|
|
||||||
void xen_modified_memory(ram_addr_t start, ram_addr_t length)
|
void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length)
|
||||||
{
|
{
|
||||||
if (unlikely(xen_in_migration)) {
|
if (unlikely(xen_in_migration)) {
|
||||||
int rc;
|
int rc;
|
||||||
@@ -1403,7 +1410,7 @@ void xen_modified_memory(ram_addr_t start, ram_addr_t length)
|
|||||||
start_pfn = start >> TARGET_PAGE_BITS;
|
start_pfn = start >> TARGET_PAGE_BITS;
|
||||||
nb_pages = ((start + length + TARGET_PAGE_SIZE - 1) >> TARGET_PAGE_BITS)
|
nb_pages = ((start + length + TARGET_PAGE_SIZE - 1) >> TARGET_PAGE_BITS)
|
||||||
- start_pfn;
|
- start_pfn;
|
||||||
rc = xc_hvm_modified_memory(xen_xc, xen_domid, start_pfn, nb_pages);
|
rc = xen_modified_memory(xen_domid, start_pfn, nb_pages);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%s failed for "RAM_ADDR_FMT" ("RAM_ADDR_FMT"): %i, %s\n",
|
"%s failed for "RAM_ADDR_FMT" ("RAM_ADDR_FMT"): %i, %s\n",
|
@@ -19,7 +19,7 @@
|
|||||||
#include <xen/hvm/params.h>
|
#include <xen/hvm/params.h>
|
||||||
|
|
||||||
#include "sysemu/xen-mapcache.h"
|
#include "sysemu/xen-mapcache.h"
|
||||||
#include "trace-root.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
|
||||||
//#define MAPCACHE_DEBUG
|
//#define MAPCACHE_DEBUG
|
@@ -195,7 +195,7 @@ static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t v
|
|||||||
case 0: /* Platform flags */ {
|
case 0: /* Platform flags */ {
|
||||||
hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ?
|
hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ?
|
||||||
HVMMEM_ram_ro : HVMMEM_ram_rw;
|
HVMMEM_ram_ro : HVMMEM_ram_rw;
|
||||||
if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type, 0xc0, 0x40)) {
|
if (xen_set_mem_type(xen_domid, mem_type, 0xc0, 0x40)) {
|
||||||
DPRINTF("unable to change ro/rw state of ROM memory area!\n");
|
DPRINTF("unable to change ro/rw state of ROM memory area!\n");
|
||||||
} else {
|
} else {
|
||||||
s->flags = val & PFFLAG_ROM_LOCK;
|
s->flags = val & PFFLAG_ROM_LOCK;
|
||||||
|
@@ -256,6 +256,10 @@ static void hid_keyboard_process_keycode(HIDState *hs)
|
|||||||
slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
|
slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
|
||||||
keycode = hs->kbd.keycodes[slot];
|
keycode = hs->kbd.keycodes[slot];
|
||||||
|
|
||||||
|
if (!hs->n) {
|
||||||
|
trace_hid_kbd_queue_empty();
|
||||||
|
}
|
||||||
|
|
||||||
key = keycode & 0x7f;
|
key = keycode & 0x7f;
|
||||||
index = key | ((hs->kbd.modifiers & (1 << 8)) >> 1);
|
index = key | ((hs->kbd.modifiers & (1 << 8)) >> 1);
|
||||||
hid_code = hid_usage_keys[index];
|
hid_code = hid_usage_keys[index];
|
||||||
|
@@ -24,6 +24,7 @@ milkymist_softusb_pulse_irq(void) "Pulse IRQ"
|
|||||||
|
|
||||||
# hw/input/hid.c
|
# hw/input/hid.c
|
||||||
hid_kbd_queue_full(void) "queue full"
|
hid_kbd_queue_full(void) "queue full"
|
||||||
|
hid_kbd_queue_empty(void) "queue empty"
|
||||||
|
|
||||||
# hw/input/virtio
|
# hw/input/virtio
|
||||||
virtio_input_queue_full(void) "queue full"
|
virtio_input_queue_full(void) "queue full"
|
||||||
|
@@ -35,6 +35,7 @@ obj-$(CONFIG_SH4) += sh_intc.o
|
|||||||
obj-$(CONFIG_XICS) += xics.o
|
obj-$(CONFIG_XICS) += xics.o
|
||||||
obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
|
obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
|
||||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||||
|
obj-$(CONFIG_POWERNV) += xics_pnv.o
|
||||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||||
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
||||||
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
|
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
|
||||||
|
@@ -21,11 +21,14 @@
|
|||||||
|
|
||||||
S390FLICState *s390_get_flic(void)
|
S390FLICState *s390_get_flic(void)
|
||||||
{
|
{
|
||||||
S390FLICState *fs;
|
static S390FLICState *fs;
|
||||||
|
|
||||||
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL));
|
|
||||||
if (!fs) {
|
if (!fs) {
|
||||||
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL));
|
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL));
|
||||||
|
if (!fs) {
|
||||||
|
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC,
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
@@ -38,21 +38,10 @@
|
|||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
#include "hw/intc/intc.h"
|
#include "hw/intc/intc.h"
|
||||||
|
|
||||||
int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
|
|
||||||
{
|
|
||||||
PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id);
|
|
||||||
|
|
||||||
if (cpu) {
|
|
||||||
return cpu->parent_obj.cpu_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
|
void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
CPUState *cs = CPU(cpu);
|
||||||
ICPState *icp = xics_icp_get(xi, cs->cpu_index);
|
ICPState *icp = ICP(cpu->intc);
|
||||||
|
|
||||||
assert(icp);
|
assert(icp);
|
||||||
assert(cs == icp->cs);
|
assert(cs == icp->cs);
|
||||||
@@ -61,15 +50,15 @@ void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
|
|||||||
icp->cs = NULL;
|
icp->cs = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu)
|
void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
CPUState *cs = CPU(cpu);
|
||||||
CPUPPCState *env = &cpu->env;
|
CPUPPCState *env = &cpu->env;
|
||||||
ICPState *icp = xics_icp_get(xi, cs->cpu_index);
|
|
||||||
ICPStateClass *icpc;
|
ICPStateClass *icpc;
|
||||||
|
|
||||||
assert(icp);
|
assert(icp);
|
||||||
|
|
||||||
|
cpu->intc = OBJECT(icp);
|
||||||
icp->cs = cs;
|
icp->cs = cs;
|
||||||
|
|
||||||
icpc = ICP_GET_CLASS(icp);
|
icpc = ICP_GET_CLASS(icp);
|
||||||
@@ -348,6 +337,7 @@ static void icp_reset(void *dev)
|
|||||||
static void icp_realize(DeviceState *dev, Error **errp)
|
static void icp_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
ICPState *icp = ICP(dev);
|
ICPState *icp = ICP(dev);
|
||||||
|
ICPStateClass *icpc = ICP_GET_CLASS(dev);
|
||||||
Object *obj;
|
Object *obj;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
@@ -360,6 +350,10 @@ static void icp_realize(DeviceState *dev, Error **errp)
|
|||||||
|
|
||||||
icp->xics = XICS_FABRIC(obj);
|
icp->xics = XICS_FABRIC(obj);
|
||||||
|
|
||||||
|
if (icpc->realize) {
|
||||||
|
icpc->realize(dev, errp);
|
||||||
|
}
|
||||||
|
|
||||||
qemu_register_reset(icp_reset, dev);
|
qemu_register_reset(icp_reset, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
192
hw/intc/xics_pnv.c
Normal file
192
hw/intc/xics_pnv.c
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* QEMU PowerPC PowerNV Interrupt Control Presenter (ICP) model
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017, IBM Corporation.
|
||||||
|
*
|
||||||
|
* 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 "sysemu/sysemu.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "hw/ppc/xics.h"
|
||||||
|
|
||||||
|
#define ICP_XIRR_POLL 0 /* 1 byte (CPRR) or 4 bytes */
|
||||||
|
#define ICP_XIRR 4 /* 1 byte (CPRR) or 4 bytes */
|
||||||
|
#define ICP_MFRR 12 /* 1 byte access only */
|
||||||
|
|
||||||
|
#define ICP_LINKA 16 /* unused */
|
||||||
|
#define ICP_LINKB 20 /* unused */
|
||||||
|
#define ICP_LINKC 24 /* unused */
|
||||||
|
|
||||||
|
static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width)
|
||||||
|
{
|
||||||
|
ICPState *icp = ICP(opaque);
|
||||||
|
PnvICPState *picp = PNV_ICP(opaque);
|
||||||
|
bool byte0 = (width == 1 && (addr & 0x3) == 0);
|
||||||
|
uint64_t val = 0xffffffff;
|
||||||
|
|
||||||
|
switch (addr & 0xffc) {
|
||||||
|
case ICP_XIRR_POLL:
|
||||||
|
val = icp_ipoll(icp, NULL);
|
||||||
|
if (byte0) {
|
||||||
|
val >>= 24;
|
||||||
|
} else if (width != 4) {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_XIRR:
|
||||||
|
if (byte0) {
|
||||||
|
val = icp_ipoll(icp, NULL) >> 24;
|
||||||
|
} else if (width == 4) {
|
||||||
|
val = icp_accept(icp);
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_MFRR:
|
||||||
|
if (byte0) {
|
||||||
|
val = icp->mfrr;
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_LINKA:
|
||||||
|
if (width == 4) {
|
||||||
|
val = picp->links[0];
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_LINKB:
|
||||||
|
if (width == 4) {
|
||||||
|
val = picp->links[1];
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_LINKC:
|
||||||
|
if (width == 4) {
|
||||||
|
val = picp->links[2];
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bad_access:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
|
||||||
|
HWADDR_PRIx"/%d\n", addr, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val,
|
||||||
|
unsigned width)
|
||||||
|
{
|
||||||
|
ICPState *icp = ICP(opaque);
|
||||||
|
PnvICPState *picp = PNV_ICP(opaque);
|
||||||
|
bool byte0 = (width == 1 && (addr & 0x3) == 0);
|
||||||
|
|
||||||
|
switch (addr & 0xffc) {
|
||||||
|
case ICP_XIRR:
|
||||||
|
if (byte0) {
|
||||||
|
icp_set_cppr(icp, val);
|
||||||
|
} else if (width == 4) {
|
||||||
|
icp_eoi(icp, val);
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_MFRR:
|
||||||
|
if (byte0) {
|
||||||
|
icp_set_mfrr(icp, val);
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_LINKA:
|
||||||
|
if (width == 4) {
|
||||||
|
picp->links[0] = val;
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_LINKB:
|
||||||
|
if (width == 4) {
|
||||||
|
picp->links[1] = val;
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICP_LINKC:
|
||||||
|
if (width == 4) {
|
||||||
|
picp->links[2] = val;
|
||||||
|
} else {
|
||||||
|
goto bad_access;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bad_access:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
|
||||||
|
HWADDR_PRIx"/%d\n", addr, width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps pnv_icp_ops = {
|
||||||
|
.read = pnv_icp_read,
|
||||||
|
.write = pnv_icp_write,
|
||||||
|
.endianness = DEVICE_BIG_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 1,
|
||||||
|
.max_access_size = 4,
|
||||||
|
},
|
||||||
|
.impl = {
|
||||||
|
.min_access_size = 1,
|
||||||
|
.max_access_size = 4,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pnv_icp_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
PnvICPState *icp = PNV_ICP(dev);
|
||||||
|
|
||||||
|
memory_region_init_io(&icp->mmio, OBJECT(dev), &pnv_icp_ops,
|
||||||
|
icp, "icp-thread", 0x1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_icp_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
ICPStateClass *icpc = ICP_CLASS(klass);
|
||||||
|
|
||||||
|
icpc->realize = pnv_icp_realize;
|
||||||
|
dc->desc = "PowerNV ICP";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo pnv_icp_info = {
|
||||||
|
.name = TYPE_PNV_ICP,
|
||||||
|
.parent = TYPE_ICP,
|
||||||
|
.instance_size = sizeof(PnvICPState),
|
||||||
|
.class_init = pnv_icp_class_init,
|
||||||
|
.class_size = sizeof(ICPStateClass),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pnv_icp_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&pnv_icp_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(pnv_icp_register_types)
|
@@ -43,20 +43,17 @@
|
|||||||
static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
target_ulong opcode, target_ulong *args)
|
target_ulong opcode, target_ulong *args)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
|
||||||
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
|
|
||||||
target_ulong cppr = args[0];
|
target_ulong cppr = args[0];
|
||||||
|
|
||||||
icp_set_cppr(icp, cppr);
|
icp_set_cppr(ICP(cpu->intc), cppr);
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
target_ulong opcode, target_ulong *args)
|
target_ulong opcode, target_ulong *args)
|
||||||
{
|
{
|
||||||
target_ulong server = xics_get_cpu_index_by_dt_id(args[0]);
|
|
||||||
target_ulong mfrr = args[1];
|
target_ulong mfrr = args[1];
|
||||||
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), server);
|
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]);
|
||||||
|
|
||||||
if (!icp) {
|
if (!icp) {
|
||||||
return H_PARAMETER;
|
return H_PARAMETER;
|
||||||
@@ -69,9 +66,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
|||||||
static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
target_ulong opcode, target_ulong *args)
|
target_ulong opcode, target_ulong *args)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
uint32_t xirr = icp_accept(ICP(cpu->intc));
|
||||||
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
|
|
||||||
uint32_t xirr = icp_accept(icp);
|
|
||||||
|
|
||||||
args[0] = xirr;
|
args[0] = xirr;
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
@@ -80,9 +75,7 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
|||||||
static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
target_ulong opcode, target_ulong *args)
|
target_ulong opcode, target_ulong *args)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
uint32_t xirr = icp_accept(ICP(cpu->intc));
|
||||||
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
|
|
||||||
uint32_t xirr = icp_accept(icp);
|
|
||||||
|
|
||||||
args[0] = xirr;
|
args[0] = xirr;
|
||||||
args[1] = cpu_get_host_ticks();
|
args[1] = cpu_get_host_ticks();
|
||||||
@@ -92,21 +85,17 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
|||||||
static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
target_ulong opcode, target_ulong *args)
|
target_ulong opcode, target_ulong *args)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
|
||||||
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
|
|
||||||
target_ulong xirr = args[0];
|
target_ulong xirr = args[0];
|
||||||
|
|
||||||
icp_eoi(icp, xirr);
|
icp_eoi(ICP(cpu->intc), xirr);
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
||||||
target_ulong opcode, target_ulong *args)
|
target_ulong opcode, target_ulong *args)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
|
||||||
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
|
|
||||||
uint32_t mfrr;
|
uint32_t mfrr;
|
||||||
uint32_t xirr = icp_ipoll(icp, &mfrr);
|
uint32_t xirr = icp_ipoll(ICP(cpu->intc), &mfrr);
|
||||||
|
|
||||||
args[0] = xirr;
|
args[0] = xirr;
|
||||||
args[1] = mfrr;
|
args[1] = mfrr;
|
||||||
@@ -132,7 +121,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
nr = rtas_ld(args, 0);
|
nr = rtas_ld(args, 0);
|
||||||
server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1));
|
server = rtas_ld(args, 1);
|
||||||
priority = rtas_ld(args, 2);
|
priority = rtas_ld(args, 2);
|
||||||
|
|
||||||
if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server)
|
if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server)
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "hw/ipmi/ipmi.h"
|
#include "hw/ipmi/ipmi.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
#include "hw/loader.h"
|
||||||
|
|
||||||
#define IPMI_NETFN_CHASSIS 0x00
|
#define IPMI_NETFN_CHASSIS 0x00
|
||||||
|
|
||||||
@@ -79,6 +80,9 @@
|
|||||||
#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A
|
#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A
|
||||||
#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B
|
#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B
|
||||||
#define IPMI_CMD_RUN_INIT_AGENT 0x2C
|
#define IPMI_CMD_RUN_INIT_AGENT 0x2C
|
||||||
|
#define IPMI_CMD_GET_FRU_AREA_INFO 0x10
|
||||||
|
#define IPMI_CMD_READ_FRU_DATA 0x11
|
||||||
|
#define IPMI_CMD_WRITE_FRU_DATA 0x12
|
||||||
#define IPMI_CMD_GET_SEL_INFO 0x40
|
#define IPMI_CMD_GET_SEL_INFO 0x40
|
||||||
#define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41
|
#define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41
|
||||||
#define IPMI_CMD_RESERVE_SEL 0x42
|
#define IPMI_CMD_RESERVE_SEL 0x42
|
||||||
@@ -121,6 +125,13 @@ typedef struct IPMISdr {
|
|||||||
uint8_t overflow;
|
uint8_t overflow;
|
||||||
} IPMISdr;
|
} IPMISdr;
|
||||||
|
|
||||||
|
typedef struct IPMIFru {
|
||||||
|
char *filename;
|
||||||
|
unsigned int nentries;
|
||||||
|
uint16_t areasize;
|
||||||
|
uint8_t *data;
|
||||||
|
} IPMIFru;
|
||||||
|
|
||||||
typedef struct IPMISensor {
|
typedef struct IPMISensor {
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
uint8_t reading;
|
uint8_t reading;
|
||||||
@@ -212,7 +223,9 @@ struct IPMIBmcSim {
|
|||||||
|
|
||||||
IPMISel sel;
|
IPMISel sel;
|
||||||
IPMISdr sdr;
|
IPMISdr sdr;
|
||||||
|
IPMIFru fru;
|
||||||
IPMISensor sensors[MAX_SENSORS];
|
IPMISensor sensors[MAX_SENSORS];
|
||||||
|
char *sdr_filename;
|
||||||
|
|
||||||
/* Odd netfns are for responses, so we only need the even ones. */
|
/* Odd netfns are for responses, so we only need the even ones. */
|
||||||
const IPMINetfn *netfns[MAX_NETFNS / 2];
|
const IPMINetfn *netfns[MAX_NETFNS / 2];
|
||||||
@@ -403,6 +416,22 @@ static int sdr_find_entry(IPMISdr *sdr, uint16_t recid,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid,
|
||||||
|
const struct ipmi_sdr_compact **sdr, uint16_t *nextrec)
|
||||||
|
|
||||||
|
{
|
||||||
|
IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b);
|
||||||
|
unsigned int pos;
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
if (sdr_find_entry(&ibs->sdr, recid, &pos, nextrec)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*sdr = (const struct ipmi_sdr_compact *) &ibs->sdr.sdr[pos];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void sel_inc_reservation(IPMISel *sel)
|
static void sel_inc_reservation(IPMISel *sel)
|
||||||
{
|
{
|
||||||
sel->reservation++;
|
sel->reservation++;
|
||||||
@@ -444,6 +473,30 @@ static int attn_irq_enabled(IPMIBmcSim *ibs)
|
|||||||
IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs));
|
IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log)
|
||||||
|
{
|
||||||
|
IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b);
|
||||||
|
IPMIInterface *s = ibs->parent.intf;
|
||||||
|
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||||
|
|
||||||
|
if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log && IPMI_BMC_EVENT_LOG_ENABLED(ibs)) {
|
||||||
|
sel_add_event(ibs, evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ibs->evtbuf, evt, 16);
|
||||||
|
ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL;
|
||||||
|
k->set_atn(s, 1, attn_irq_enabled(ibs));
|
||||||
|
out:
|
||||||
|
return;
|
||||||
|
}
|
||||||
static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert,
|
static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert,
|
||||||
uint8_t evd1, uint8_t evd2, uint8_t evd3)
|
uint8_t evd1, uint8_t evd2, uint8_t evd3)
|
||||||
{
|
{
|
||||||
@@ -1315,6 +1368,91 @@ static void get_sel_info(IPMIBmcSim *ibs,
|
|||||||
rsp_buffer_push(rsp, (ibs->sel.overflow << 7) | 0x02);
|
rsp_buffer_push(rsp, (ibs->sel.overflow << 7) | 0x02);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_fru_area_info(IPMIBmcSim *ibs,
|
||||||
|
uint8_t *cmd, unsigned int cmd_len,
|
||||||
|
RspBuffer *rsp)
|
||||||
|
{
|
||||||
|
uint8_t fruid;
|
||||||
|
uint16_t fru_entry_size;
|
||||||
|
|
||||||
|
fruid = cmd[2];
|
||||||
|
|
||||||
|
if (fruid >= ibs->fru.nentries) {
|
||||||
|
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fru_entry_size = ibs->fru.areasize;
|
||||||
|
|
||||||
|
rsp_buffer_push(rsp, fru_entry_size & 0xff);
|
||||||
|
rsp_buffer_push(rsp, fru_entry_size >> 8 & 0xff);
|
||||||
|
rsp_buffer_push(rsp, 0x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_fru_data(IPMIBmcSim *ibs,
|
||||||
|
uint8_t *cmd, unsigned int cmd_len,
|
||||||
|
RspBuffer *rsp)
|
||||||
|
{
|
||||||
|
uint8_t fruid;
|
||||||
|
uint16_t offset;
|
||||||
|
int i;
|
||||||
|
uint8_t *fru_entry;
|
||||||
|
unsigned int count;
|
||||||
|
|
||||||
|
fruid = cmd[2];
|
||||||
|
offset = (cmd[3] | cmd[4] << 8);
|
||||||
|
|
||||||
|
if (fruid >= ibs->fru.nentries) {
|
||||||
|
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= ibs->fru.areasize - 1) {
|
||||||
|
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize];
|
||||||
|
|
||||||
|
count = MIN(cmd[5], ibs->fru.areasize - offset);
|
||||||
|
|
||||||
|
rsp_buffer_push(rsp, count & 0xff);
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
rsp_buffer_push(rsp, fru_entry[offset + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_fru_data(IPMIBmcSim *ibs,
|
||||||
|
uint8_t *cmd, unsigned int cmd_len,
|
||||||
|
RspBuffer *rsp)
|
||||||
|
{
|
||||||
|
uint8_t fruid;
|
||||||
|
uint16_t offset;
|
||||||
|
uint8_t *fru_entry;
|
||||||
|
unsigned int count;
|
||||||
|
|
||||||
|
fruid = cmd[2];
|
||||||
|
offset = (cmd[3] | cmd[4] << 8);
|
||||||
|
|
||||||
|
if (fruid >= ibs->fru.nentries) {
|
||||||
|
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= ibs->fru.areasize - 1) {
|
||||||
|
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize];
|
||||||
|
|
||||||
|
count = MIN(cmd_len - 5, ibs->fru.areasize - offset);
|
||||||
|
|
||||||
|
memcpy(fru_entry + offset, cmd + 5, count);
|
||||||
|
|
||||||
|
rsp_buffer_push(rsp, count & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
static void reserve_sel(IPMIBmcSim *ibs,
|
static void reserve_sel(IPMIBmcSim *ibs,
|
||||||
uint8_t *cmd, unsigned int cmd_len,
|
uint8_t *cmd, unsigned int cmd_len,
|
||||||
RspBuffer *rsp)
|
RspBuffer *rsp)
|
||||||
@@ -1651,6 +1789,9 @@ static const IPMINetfn app_netfn = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const IPMICmdHandler storage_cmds[] = {
|
static const IPMICmdHandler storage_cmds[] = {
|
||||||
|
[IPMI_CMD_GET_FRU_AREA_INFO] = { get_fru_area_info, 3 },
|
||||||
|
[IPMI_CMD_READ_FRU_DATA] = { read_fru_data, 5 },
|
||||||
|
[IPMI_CMD_WRITE_FRU_DATA] = { write_fru_data, 5 },
|
||||||
[IPMI_CMD_GET_SDR_REP_INFO] = { get_sdr_rep_info },
|
[IPMI_CMD_GET_SDR_REP_INFO] = { get_sdr_rep_info },
|
||||||
[IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep },
|
[IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep },
|
||||||
[IPMI_CMD_GET_SDR] = { get_sdr, 8 },
|
[IPMI_CMD_GET_SDR] = { get_sdr, 8 },
|
||||||
@@ -1696,22 +1837,33 @@ static void ipmi_sdr_init(IPMIBmcSim *ibs)
|
|||||||
|
|
||||||
sdrs_size = sizeof(init_sdrs);
|
sdrs_size = sizeof(init_sdrs);
|
||||||
sdrs = init_sdrs;
|
sdrs = init_sdrs;
|
||||||
|
if (ibs->sdr_filename &&
|
||||||
|
!g_file_get_contents(ibs->sdr_filename, (gchar **) &sdrs, &sdrs_size,
|
||||||
|
NULL)) {
|
||||||
|
error_report("failed to load sdr file '%s'", ibs->sdr_filename);
|
||||||
|
sdrs_size = sizeof(init_sdrs);
|
||||||
|
sdrs = init_sdrs;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < sdrs_size; i += len) {
|
for (i = 0; i < sdrs_size; i += len) {
|
||||||
struct ipmi_sdr_header *sdrh;
|
struct ipmi_sdr_header *sdrh;
|
||||||
|
|
||||||
if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) {
|
if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) {
|
||||||
error_report("Problem with recid 0x%4.4x", i);
|
error_report("Problem with recid 0x%4.4x", i);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
sdrh = (struct ipmi_sdr_header *) &sdrs[i];
|
sdrh = (struct ipmi_sdr_header *) &sdrs[i];
|
||||||
len = ipmi_sdr_length(sdrh);
|
len = ipmi_sdr_length(sdrh);
|
||||||
if (i + len > sdrs_size) {
|
if (i + len > sdrs_size) {
|
||||||
error_report("Problem with recid 0x%4.4x", i);
|
error_report("Problem with recid 0x%4.4x", i);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
sdr_add_entry(ibs, sdrh, len, NULL);
|
sdr_add_entry(ibs, sdrh, len, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sdrs != init_sdrs) {
|
||||||
|
g_free(sdrs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_ipmi_sim = {
|
static const VMStateDescription vmstate_ipmi_sim = {
|
||||||
@@ -1742,6 +1894,36 @@ static const VMStateDescription vmstate_ipmi_sim = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void ipmi_fru_init(IPMIFru *fru)
|
||||||
|
{
|
||||||
|
int fsize;
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
if (!fru->filename) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsize = get_image_size(fru->filename);
|
||||||
|
if (fsize > 0) {
|
||||||
|
size = QEMU_ALIGN_UP(fsize, fru->areasize);
|
||||||
|
fru->data = g_malloc0(size);
|
||||||
|
if (load_image_size(fru->filename, fru->data, fsize) != fsize) {
|
||||||
|
error_report("Could not load file '%s'", fru->filename);
|
||||||
|
g_free(fru->data);
|
||||||
|
fru->data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (!fru->data) {
|
||||||
|
/* give one default FRU */
|
||||||
|
size = fru->areasize;
|
||||||
|
fru->data = g_malloc0(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fru->nentries = size / fru->areasize;
|
||||||
|
}
|
||||||
|
|
||||||
static void ipmi_sim_realize(DeviceState *dev, Error **errp)
|
static void ipmi_sim_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
IPMIBmc *b = IPMI_BMC(dev);
|
IPMIBmc *b = IPMI_BMC(dev);
|
||||||
@@ -1763,6 +1945,8 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp)
|
|||||||
|
|
||||||
ipmi_sdr_init(ibs);
|
ipmi_sdr_init(ibs);
|
||||||
|
|
||||||
|
ipmi_fru_init(&ibs->fru);
|
||||||
|
|
||||||
ibs->acpi_power_state[0] = 0;
|
ibs->acpi_power_state[0] = 0;
|
||||||
ibs->acpi_power_state[1] = 0;
|
ibs->acpi_power_state[1] = 0;
|
||||||
|
|
||||||
@@ -1780,6 +1964,13 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp)
|
|||||||
vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs);
|
vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Property ipmi_sim_properties[] = {
|
||||||
|
DEFINE_PROP_UINT16("fruareasize", IPMIBmcSim, fru.areasize, 1024),
|
||||||
|
DEFINE_PROP_STRING("frudatafile", IPMIBmcSim, fru.filename),
|
||||||
|
DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
static void ipmi_sim_class_init(ObjectClass *oc, void *data)
|
static void ipmi_sim_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
@@ -1787,6 +1978,7 @@ static void ipmi_sim_class_init(ObjectClass *oc, void *data)
|
|||||||
|
|
||||||
dc->hotpluggable = false;
|
dc->hotpluggable = false;
|
||||||
dc->realize = ipmi_sim_realize;
|
dc->realize = ipmi_sim_realize;
|
||||||
|
dc->props = ipmi_sim_properties;
|
||||||
bk->handle_command = ipmi_sim_handle_command;
|
bk->handle_command = ipmi_sim_handle_command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -401,8 +401,8 @@ static uint64_t exynos4210_pmu_read(void *opaque, hwaddr offset,
|
|||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
|
Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
|
||||||
unsigned i;
|
|
||||||
const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
|
const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
|
for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
|
||||||
if (reg_p->offset == offset) {
|
if (reg_p->offset == offset) {
|
||||||
@@ -420,8 +420,8 @@ static void exynos4210_pmu_write(void *opaque, hwaddr offset,
|
|||||||
uint64_t val, unsigned size)
|
uint64_t val, unsigned size)
|
||||||
{
|
{
|
||||||
Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
|
Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
|
||||||
unsigned i;
|
|
||||||
const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
|
const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
|
for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
|
||||||
if (reg_p->offset == offset) {
|
if (reg_p->offset == offset) {
|
||||||
|
@@ -26,6 +26,7 @@ common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
|
|||||||
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
|
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
|
||||||
common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
|
common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
|
||||||
common-obj-$(CONFIG_LANCE) += lance.o
|
common-obj-$(CONFIG_LANCE) += lance.o
|
||||||
|
common-obj-$(CONFIG_FTGMAC100) += ftgmac100.o
|
||||||
|
|
||||||
obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o
|
obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o
|
||||||
obj-$(CONFIG_COLDFIRE) += mcf_fec.o
|
obj-$(CONFIG_COLDFIRE) += mcf_fec.o
|
||||||
|
@@ -300,6 +300,8 @@
|
|||||||
#define DESC_1_RX_SOF 0x00004000
|
#define DESC_1_RX_SOF 0x00004000
|
||||||
#define DESC_1_RX_EOF 0x00008000
|
#define DESC_1_RX_EOF 0x00008000
|
||||||
|
|
||||||
|
#define GEM_MODID_VALUE 0x00020118
|
||||||
|
|
||||||
static inline unsigned tx_desc_get_buffer(unsigned *desc)
|
static inline unsigned tx_desc_get_buffer(unsigned *desc)
|
||||||
{
|
{
|
||||||
return desc[0];
|
return desc[0];
|
||||||
@@ -481,14 +483,17 @@ static int gem_can_receive(NetClientState *nc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < s->num_priority_queues; i++) {
|
for (i = 0; i < s->num_priority_queues; i++) {
|
||||||
if (rx_desc_get_ownership(s->rx_desc[i]) == 1) {
|
if (rx_desc_get_ownership(s->rx_desc[i]) != 1) {
|
||||||
if (s->can_rx_state != 2) {
|
break;
|
||||||
s->can_rx_state = 2;
|
|
||||||
DB_PRINT("can't receive - busy buffer descriptor (q%d) 0x%x\n",
|
|
||||||
i, s->rx_desc_addr[i]);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (i == s->num_priority_queues) {
|
||||||
|
if (s->can_rx_state != 2) {
|
||||||
|
s->can_rx_state = 2;
|
||||||
|
DB_PRINT("can't receive - all the buffer descriptors are busy\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->can_rx_state != 0) {
|
if (s->can_rx_state != 0) {
|
||||||
@@ -506,7 +511,18 @@ static void gem_update_int_status(CadenceGEMState *s)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if ((s->num_priority_queues == 1) && s->regs[GEM_ISR]) {
|
if (!s->regs[GEM_ISR]) {
|
||||||
|
/* ISR isn't set, clear all the interrupts */
|
||||||
|
for (i = 0; i < s->num_priority_queues; ++i) {
|
||||||
|
qemu_set_irq(s->irq[i], 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we get here we know s->regs[GEM_ISR] is set, so we don't need to
|
||||||
|
* check it again.
|
||||||
|
*/
|
||||||
|
if (s->num_priority_queues == 1) {
|
||||||
/* No priority queues, just trigger the interrupt */
|
/* No priority queues, just trigger the interrupt */
|
||||||
DB_PRINT("asserting int.\n");
|
DB_PRINT("asserting int.\n");
|
||||||
qemu_set_irq(s->irq[0], 1);
|
qemu_set_irq(s->irq[0], 1);
|
||||||
@@ -790,8 +806,8 @@ static void gem_get_rx_desc(CadenceGEMState *s, int q)
|
|||||||
{
|
{
|
||||||
DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr[q]);
|
DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr[q]);
|
||||||
/* read current descriptor */
|
/* read current descriptor */
|
||||||
cpu_physical_memory_read(s->rx_desc_addr[0],
|
cpu_physical_memory_read(s->rx_desc_addr[q],
|
||||||
(uint8_t *)s->rx_desc[0], sizeof(s->rx_desc[0]));
|
(uint8_t *)s->rx_desc[q], sizeof(s->rx_desc[q]));
|
||||||
|
|
||||||
/* Descriptor owned by software ? */
|
/* Descriptor owned by software ? */
|
||||||
if (rx_desc_get_ownership(s->rx_desc[q]) == 1) {
|
if (rx_desc_get_ownership(s->rx_desc[q]) == 1) {
|
||||||
@@ -1209,7 +1225,7 @@ static void gem_reset(DeviceState *d)
|
|||||||
s->regs[GEM_TXPAUSE] = 0x0000ffff;
|
s->regs[GEM_TXPAUSE] = 0x0000ffff;
|
||||||
s->regs[GEM_TXPARTIALSF] = 0x000003ff;
|
s->regs[GEM_TXPARTIALSF] = 0x000003ff;
|
||||||
s->regs[GEM_RXPARTIALSF] = 0x000003ff;
|
s->regs[GEM_RXPARTIALSF] = 0x000003ff;
|
||||||
s->regs[GEM_MODID] = 0x00020118;
|
s->regs[GEM_MODID] = s->revision;
|
||||||
s->regs[GEM_DESCONF] = 0x02500111;
|
s->regs[GEM_DESCONF] = 0x02500111;
|
||||||
s->regs[GEM_DESCONF2] = 0x2ab13fff;
|
s->regs[GEM_DESCONF2] = 0x2ab13fff;
|
||||||
s->regs[GEM_DESCONF5] = 0x002f2145;
|
s->regs[GEM_DESCONF5] = 0x002f2145;
|
||||||
@@ -1271,7 +1287,6 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
|
|||||||
{
|
{
|
||||||
CadenceGEMState *s;
|
CadenceGEMState *s;
|
||||||
uint32_t retval;
|
uint32_t retval;
|
||||||
int i;
|
|
||||||
s = (CadenceGEMState *)opaque;
|
s = (CadenceGEMState *)opaque;
|
||||||
|
|
||||||
offset >>= 2;
|
offset >>= 2;
|
||||||
@@ -1282,9 +1297,7 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
|
|||||||
switch (offset) {
|
switch (offset) {
|
||||||
case GEM_ISR:
|
case GEM_ISR:
|
||||||
DB_PRINT("lowering irqs on ISR read\n");
|
DB_PRINT("lowering irqs on ISR read\n");
|
||||||
for (i = 0; i < s->num_priority_queues; ++i) {
|
/* The interrupts get updated at the end of the function. */
|
||||||
qemu_set_irq(s->irq[i], 0);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case GEM_PHYMNTNC:
|
case GEM_PHYMNTNC:
|
||||||
if (retval & GEM_PHYMNTNC_OP_R) {
|
if (retval & GEM_PHYMNTNC_OP_R) {
|
||||||
@@ -1508,6 +1521,8 @@ static const VMStateDescription vmstate_cadence_gem = {
|
|||||||
|
|
||||||
static Property gem_properties[] = {
|
static Property gem_properties[] = {
|
||||||
DEFINE_NIC_PROPERTIES(CadenceGEMState, conf),
|
DEFINE_NIC_PROPERTIES(CadenceGEMState, conf),
|
||||||
|
DEFINE_PROP_UINT32("revision", CadenceGEMState, revision,
|
||||||
|
GEM_MODID_VALUE),
|
||||||
DEFINE_PROP_UINT8("num-priority-queues", CadenceGEMState,
|
DEFINE_PROP_UINT8("num-priority-queues", CadenceGEMState,
|
||||||
num_priority_queues, 1),
|
num_priority_queues, 1),
|
||||||
DEFINE_PROP_UINT8("num-type1-screeners", CadenceGEMState,
|
DEFINE_PROP_UINT8("num-type1-screeners", CadenceGEMState,
|
||||||
|
1016
hw/net/ftgmac100.c
Normal file
1016
hw/net/ftgmac100.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -380,20 +380,8 @@ static void pci_vpb_reset(DeviceState *d)
|
|||||||
|
|
||||||
static void pci_vpb_init(Object *obj)
|
static void pci_vpb_init(Object *obj)
|
||||||
{
|
{
|
||||||
PCIHostState *h = PCI_HOST_BRIDGE(obj);
|
|
||||||
PCIVPBState *s = PCI_VPB(obj);
|
PCIVPBState *s = PCI_VPB(obj);
|
||||||
|
|
||||||
memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 1ULL << 32);
|
|
||||||
memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 1ULL << 32);
|
|
||||||
|
|
||||||
pci_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), "pci",
|
|
||||||
&s->pci_mem_space, &s->pci_io_space,
|
|
||||||
PCI_DEVFN(11, 0), TYPE_PCI_BUS);
|
|
||||||
h->bus = &s->pci_bus;
|
|
||||||
|
|
||||||
object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST);
|
|
||||||
qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus));
|
|
||||||
|
|
||||||
/* Window sizes for VersatilePB; realview_pci's init will override */
|
/* Window sizes for VersatilePB; realview_pci's init will override */
|
||||||
s->mem_win_size[0] = 0x0c000000;
|
s->mem_win_size[0] = 0x0c000000;
|
||||||
s->mem_win_size[1] = 0x10000000;
|
s->mem_win_size[1] = 0x10000000;
|
||||||
@@ -403,10 +391,22 @@ static void pci_vpb_init(Object *obj)
|
|||||||
static void pci_vpb_realize(DeviceState *dev, Error **errp)
|
static void pci_vpb_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
PCIVPBState *s = PCI_VPB(dev);
|
PCIVPBState *s = PCI_VPB(dev);
|
||||||
|
PCIHostState *h = PCI_HOST_BRIDGE(dev);
|
||||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
pci_map_irq_fn mapfn;
|
pci_map_irq_fn mapfn;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 1ULL << 32);
|
||||||
|
memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 1ULL << 32);
|
||||||
|
|
||||||
|
pci_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), dev, "pci",
|
||||||
|
&s->pci_mem_space, &s->pci_io_space,
|
||||||
|
PCI_DEVFN(11, 0), TYPE_PCI_BUS);
|
||||||
|
h->bus = &s->pci_bus;
|
||||||
|
|
||||||
|
object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus));
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
sysbus_init_irq(sbd, &s->irq[i]);
|
sysbus_init_irq(sbd, &s->irq[i]);
|
||||||
}
|
}
|
||||||
@@ -503,8 +503,6 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data)
|
|||||||
dc->reset = pci_vpb_reset;
|
dc->reset = pci_vpb_reset;
|
||||||
dc->vmsd = &pci_vpb_vmstate;
|
dc->vmsd = &pci_vpb_vmstate;
|
||||||
dc->props = pci_vpb_properties;
|
dc->props = pci_vpb_properties;
|
||||||
/* Reason: object_unref() hangs */
|
|
||||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo pci_vpb_info = {
|
static const TypeInfo pci_vpb_info = {
|
||||||
@@ -526,19 +524,10 @@ static void pci_realview_init(Object *obj)
|
|||||||
s->mem_win_size[2] = 0x08000000;
|
s->mem_win_size[2] = 0x08000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_realview_class_init(ObjectClass *class, void *data)
|
|
||||||
{
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(class);
|
|
||||||
|
|
||||||
/* Reason: object_unref() hangs */
|
|
||||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const TypeInfo pci_realview_info = {
|
static const TypeInfo pci_realview_info = {
|
||||||
.name = "realview_pci",
|
.name = "realview_pci",
|
||||||
.parent = TYPE_VERSATILE_PCI,
|
.parent = TYPE_VERSATILE_PCI,
|
||||||
.instance_init = pci_realview_init,
|
.instance_init = pci_realview_init,
|
||||||
.class_init = pci_realview_class_init,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void versatile_pci_register_types(void)
|
static void versatile_pci_register_types(void)
|
||||||
|
@@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
|
|||||||
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
|
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
|
||||||
obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
|
obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
|
||||||
# IBM PowerNV
|
# IBM PowerNV
|
||||||
obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
|
obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o
|
||||||
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
|
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
|
||||||
obj-y += spapr_pci_vfio.o
|
obj-y += spapr_pci_vfio.o
|
||||||
endif
|
endif
|
||||||
|
411
hw/ppc/pnv.c
411
hw/ppc/pnv.c
@@ -33,7 +33,11 @@
|
|||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
|
#include "monitor/monitor.h"
|
||||||
|
#include "hw/intc/intc.h"
|
||||||
|
#include "hw/ipmi/ipmi.h"
|
||||||
|
|
||||||
|
#include "hw/ppc/xics.h"
|
||||||
#include "hw/ppc/pnv_xscom.h"
|
#include "hw/ppc/pnv_xscom.h"
|
||||||
|
|
||||||
#include "hw/isa/isa.h"
|
#include "hw/isa/isa.h"
|
||||||
@@ -215,6 +219,55 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
|
|||||||
servers_prop, sizeof(servers_prop))));
|
servers_prop, sizeof(servers_prop))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void powernv_populate_icp(PnvChip *chip, void *fdt, uint32_t pir,
|
||||||
|
uint32_t nr_threads)
|
||||||
|
{
|
||||||
|
uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12);
|
||||||
|
char *name;
|
||||||
|
const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
|
||||||
|
uint32_t irange[2], i, rsize;
|
||||||
|
uint64_t *reg;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
irange[0] = cpu_to_be32(pir);
|
||||||
|
irange[1] = cpu_to_be32(nr_threads);
|
||||||
|
|
||||||
|
rsize = sizeof(uint64_t) * 2 * nr_threads;
|
||||||
|
reg = g_malloc(rsize);
|
||||||
|
for (i = 0; i < nr_threads; i++) {
|
||||||
|
reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
|
||||||
|
reg[i * 2 + 1] = cpu_to_be64(0x1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
|
||||||
|
offset = fdt_add_subnode(fdt, 0, name);
|
||||||
|
_FDT(offset);
|
||||||
|
g_free(name);
|
||||||
|
|
||||||
|
_FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
|
||||||
|
_FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
|
||||||
|
_FDT((fdt_setprop_string(fdt, offset, "device_type",
|
||||||
|
"PowerPC-External-Interrupt-Presentation")));
|
||||||
|
_FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
|
||||||
|
_FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
|
||||||
|
irange, sizeof(irange))));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
|
||||||
|
g_free(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pnv_chip_lpc_offset(PnvChip *chip, void *fdt)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x",
|
||||||
|
(uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE);
|
||||||
|
offset = fdt_path_offset(fdt, name);
|
||||||
|
g_free(name);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
static void powernv_populate_chip(PnvChip *chip, void *fdt)
|
static void powernv_populate_chip(PnvChip *chip, void *fdt)
|
||||||
{
|
{
|
||||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||||
@@ -224,10 +277,24 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
|
|||||||
|
|
||||||
pnv_xscom_populate(chip, fdt, 0);
|
pnv_xscom_populate(chip, fdt, 0);
|
||||||
|
|
||||||
|
/* The default LPC bus of a multichip system is on chip 0. It's
|
||||||
|
* recognized by the firmware (skiboot) using a "primary"
|
||||||
|
* property.
|
||||||
|
*/
|
||||||
|
if (chip->chip_id == 0x0) {
|
||||||
|
int lpc_offset = pnv_chip_lpc_offset(chip, fdt);
|
||||||
|
|
||||||
|
_FDT((fdt_setprop(fdt, lpc_offset, "primary", NULL, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < chip->nr_cores; i++) {
|
for (i = 0; i < chip->nr_cores; i++) {
|
||||||
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
|
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
|
||||||
|
|
||||||
powernv_create_core_node(chip, pnv_core, fdt);
|
powernv_create_core_node(chip, pnv_core, fdt);
|
||||||
|
|
||||||
|
/* Interrupt Control Presenters (ICP). One per core. */
|
||||||
|
powernv_populate_icp(chip, fdt, pnv_core->pir,
|
||||||
|
CPU_CORE(pnv_core)->nr_threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chip->ram_size) {
|
if (chip->ram_size) {
|
||||||
@@ -237,6 +304,127 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
|
|||||||
g_free(typename);
|
g_free(typename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void powernv_populate_rtc(ISADevice *d, void *fdt, int lpc_off)
|
||||||
|
{
|
||||||
|
uint32_t io_base = d->ioport_id;
|
||||||
|
uint32_t io_regs[] = {
|
||||||
|
cpu_to_be32(1),
|
||||||
|
cpu_to_be32(io_base),
|
||||||
|
cpu_to_be32(2)
|
||||||
|
};
|
||||||
|
char *name;
|
||||||
|
int node;
|
||||||
|
|
||||||
|
name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base);
|
||||||
|
node = fdt_add_subnode(fdt, lpc_off, name);
|
||||||
|
_FDT(node);
|
||||||
|
g_free(name);
|
||||||
|
|
||||||
|
_FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs))));
|
||||||
|
_FDT((fdt_setprop_string(fdt, node, "compatible", "pnpPNP,b00")));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void powernv_populate_serial(ISADevice *d, void *fdt, int lpc_off)
|
||||||
|
{
|
||||||
|
const char compatible[] = "ns16550\0pnpPNP,501";
|
||||||
|
uint32_t io_base = d->ioport_id;
|
||||||
|
uint32_t io_regs[] = {
|
||||||
|
cpu_to_be32(1),
|
||||||
|
cpu_to_be32(io_base),
|
||||||
|
cpu_to_be32(8)
|
||||||
|
};
|
||||||
|
char *name;
|
||||||
|
int node;
|
||||||
|
|
||||||
|
name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base);
|
||||||
|
node = fdt_add_subnode(fdt, lpc_off, name);
|
||||||
|
_FDT(node);
|
||||||
|
g_free(name);
|
||||||
|
|
||||||
|
_FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs))));
|
||||||
|
_FDT((fdt_setprop(fdt, node, "compatible", compatible,
|
||||||
|
sizeof(compatible))));
|
||||||
|
|
||||||
|
_FDT((fdt_setprop_cell(fdt, node, "clock-frequency", 1843200)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, node, "current-speed", 115200)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, node, "interrupts", d->isairq[0])));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, node, "interrupt-parent",
|
||||||
|
fdt_get_phandle(fdt, lpc_off))));
|
||||||
|
|
||||||
|
/* This is needed by Linux */
|
||||||
|
_FDT((fdt_setprop_string(fdt, node, "device_type", "serial")));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off)
|
||||||
|
{
|
||||||
|
const char compatible[] = "bt\0ipmi-bt";
|
||||||
|
uint32_t io_base;
|
||||||
|
uint32_t io_regs[] = {
|
||||||
|
cpu_to_be32(1),
|
||||||
|
0, /* 'io_base' retrieved from the 'ioport' property of 'isa-ipmi-bt' */
|
||||||
|
cpu_to_be32(3)
|
||||||
|
};
|
||||||
|
uint32_t irq;
|
||||||
|
char *name;
|
||||||
|
int node;
|
||||||
|
|
||||||
|
io_base = object_property_get_int(OBJECT(d), "ioport", &error_fatal);
|
||||||
|
io_regs[1] = cpu_to_be32(io_base);
|
||||||
|
|
||||||
|
irq = object_property_get_int(OBJECT(d), "irq", &error_fatal);
|
||||||
|
|
||||||
|
name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base);
|
||||||
|
node = fdt_add_subnode(fdt, lpc_off, name);
|
||||||
|
_FDT(node);
|
||||||
|
g_free(name);
|
||||||
|
|
||||||
|
fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs));
|
||||||
|
fdt_setprop(fdt, node, "compatible", compatible, sizeof(compatible));
|
||||||
|
|
||||||
|
/* Mark it as reserved to avoid Linux trying to claim it */
|
||||||
|
_FDT((fdt_setprop_string(fdt, node, "status", "reserved")));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, node, "interrupts", irq)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, node, "interrupt-parent",
|
||||||
|
fdt_get_phandle(fdt, lpc_off))));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct ForeachPopulateArgs {
|
||||||
|
void *fdt;
|
||||||
|
int offset;
|
||||||
|
} ForeachPopulateArgs;
|
||||||
|
|
||||||
|
static int powernv_populate_isa_device(DeviceState *dev, void *opaque)
|
||||||
|
{
|
||||||
|
ForeachPopulateArgs *args = opaque;
|
||||||
|
ISADevice *d = ISA_DEVICE(dev);
|
||||||
|
|
||||||
|
if (object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC)) {
|
||||||
|
powernv_populate_rtc(d, args->fdt, args->offset);
|
||||||
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_ISA_SERIAL)) {
|
||||||
|
powernv_populate_serial(d, args->fdt, args->offset);
|
||||||
|
} else if (object_dynamic_cast(OBJECT(dev), "isa-ipmi-bt")) {
|
||||||
|
powernv_populate_ipmi_bt(d, args->fdt, args->offset);
|
||||||
|
} else {
|
||||||
|
error_report("unknown isa device %s@i%x", qdev_fw_name(dev),
|
||||||
|
d->ioport_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void powernv_populate_isa(ISABus *bus, void *fdt, int lpc_offset)
|
||||||
|
{
|
||||||
|
ForeachPopulateArgs args = {
|
||||||
|
.fdt = fdt,
|
||||||
|
.offset = lpc_offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ISA devices are not necessarily parented to the ISA bus so we
|
||||||
|
* can not use object_child_foreach() */
|
||||||
|
qbus_walk_children(BUS(bus), powernv_populate_isa_device,
|
||||||
|
NULL, NULL, NULL, &args);
|
||||||
|
}
|
||||||
|
|
||||||
static void *powernv_create_fdt(MachineState *machine)
|
static void *powernv_create_fdt(MachineState *machine)
|
||||||
{
|
{
|
||||||
const char plat_compat[] = "qemu,powernv\0ibm,powernv";
|
const char plat_compat[] = "qemu,powernv\0ibm,powernv";
|
||||||
@@ -245,6 +433,7 @@ static void *powernv_create_fdt(MachineState *machine)
|
|||||||
char *buf;
|
char *buf;
|
||||||
int off;
|
int off;
|
||||||
int i;
|
int i;
|
||||||
|
int lpc_offset;
|
||||||
|
|
||||||
fdt = g_malloc0(FDT_MAX_SIZE);
|
fdt = g_malloc0(FDT_MAX_SIZE);
|
||||||
_FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
|
_FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
|
||||||
@@ -284,16 +473,49 @@ static void *powernv_create_fdt(MachineState *machine)
|
|||||||
for (i = 0; i < pnv->num_chips; i++) {
|
for (i = 0; i < pnv->num_chips; i++) {
|
||||||
powernv_populate_chip(pnv->chips[i], fdt);
|
powernv_populate_chip(pnv->chips[i], fdt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Populate ISA devices on chip 0 */
|
||||||
|
lpc_offset = pnv_chip_lpc_offset(pnv->chips[0], fdt);
|
||||||
|
powernv_populate_isa(pnv->isa_bus, fdt, lpc_offset);
|
||||||
|
|
||||||
|
if (pnv->bmc) {
|
||||||
|
pnv_bmc_populate_sensors(pnv->bmc, fdt);
|
||||||
|
}
|
||||||
|
|
||||||
return fdt;
|
return fdt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pnv_powerdown_notify(Notifier *n, void *opaque)
|
||||||
|
{
|
||||||
|
PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
|
||||||
|
|
||||||
|
if (pnv->bmc) {
|
||||||
|
pnv_bmc_powerdown(pnv->bmc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ppc_powernv_reset(void)
|
static void ppc_powernv_reset(void)
|
||||||
{
|
{
|
||||||
MachineState *machine = MACHINE(qdev_get_machine());
|
MachineState *machine = MACHINE(qdev_get_machine());
|
||||||
|
PnvMachineState *pnv = POWERNV_MACHINE(machine);
|
||||||
void *fdt;
|
void *fdt;
|
||||||
|
Object *obj;
|
||||||
|
|
||||||
qemu_devices_reset();
|
qemu_devices_reset();
|
||||||
|
|
||||||
|
/* OpenPOWER systems have a BMC, which can be defined on the
|
||||||
|
* command line with:
|
||||||
|
*
|
||||||
|
* -device ipmi-bmc-sim,id=bmc0
|
||||||
|
*
|
||||||
|
* This is the internal simulator but it could also be an external
|
||||||
|
* BMC.
|
||||||
|
*/
|
||||||
|
obj = object_resolve_path_type("", TYPE_IPMI_BMC, NULL);
|
||||||
|
if (obj) {
|
||||||
|
pnv->bmc = IPMI_BMC(obj);
|
||||||
|
}
|
||||||
|
|
||||||
fdt = powernv_create_fdt(machine);
|
fdt = powernv_create_fdt(machine);
|
||||||
|
|
||||||
/* Pack resulting tree */
|
/* Pack resulting tree */
|
||||||
@@ -302,29 +524,6 @@ static void ppc_powernv_reset(void)
|
|||||||
cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
|
cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we don't use the built-in LPC interrupt deserializer, we need
|
|
||||||
* to provide a set of qirqs for the ISA bus or things will go bad.
|
|
||||||
*
|
|
||||||
* Most machines using pre-Naples chips (without said deserializer)
|
|
||||||
* have a CPLD that will collect the SerIRQ and shoot them as a
|
|
||||||
* single level interrupt to the P8 chip. So let's setup a hook
|
|
||||||
* for doing just that.
|
|
||||||
*
|
|
||||||
* Note: The actual interrupt input isn't emulated yet, this will
|
|
||||||
* come with the PSI bridge model.
|
|
||||||
*/
|
|
||||||
static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
|
|
||||||
{
|
|
||||||
/* We don't yet emulate the PSI bridge which provides the external
|
|
||||||
* interrupt, so just drop interrupts on the floor
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
|
|
||||||
{
|
|
||||||
/* XXX TODO */
|
|
||||||
}
|
|
||||||
|
|
||||||
static ISABus *pnv_isa_create(PnvChip *chip)
|
static ISABus *pnv_isa_create(PnvChip *chip)
|
||||||
{
|
{
|
||||||
PnvLpcController *lpc = &chip->lpc;
|
PnvLpcController *lpc = &chip->lpc;
|
||||||
@@ -339,16 +538,7 @@ static ISABus *pnv_isa_create(PnvChip *chip)
|
|||||||
isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
|
isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
|
||||||
&error_fatal);
|
&error_fatal);
|
||||||
|
|
||||||
/* Not all variants have a working serial irq decoder. If not,
|
irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS);
|
||||||
* handling of LPC interrupts becomes a platform issue (some
|
|
||||||
* platforms have a CPLD to do it).
|
|
||||||
*/
|
|
||||||
if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
|
|
||||||
irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS);
|
|
||||||
} else {
|
|
||||||
irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip,
|
|
||||||
ISA_NUM_IRQS);
|
|
||||||
}
|
|
||||||
|
|
||||||
isa_bus_irqs(isa_bus, irqs);
|
isa_bus_irqs(isa_bus, irqs);
|
||||||
return isa_bus;
|
return isa_bus;
|
||||||
@@ -457,6 +647,11 @@ static void ppc_powernv_init(MachineState *machine)
|
|||||||
|
|
||||||
/* Create an RTC ISA device too */
|
/* Create an RTC ISA device too */
|
||||||
rtc_init(pnv->isa_bus, 2000, NULL);
|
rtc_init(pnv->isa_bus, 2000, NULL);
|
||||||
|
|
||||||
|
/* OpenPOWER systems use a IPMI SEL Event message to notify the
|
||||||
|
* host to powerdown */
|
||||||
|
pnv->powerdown_notifier.notify = pnv_powerdown_notify;
|
||||||
|
qemu_register_powerdown_notifier(&pnv->powerdown_notifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -638,6 +833,52 @@ static void pnv_chip_init(Object *obj)
|
|||||||
|
|
||||||
object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
|
object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
|
||||||
object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
|
object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
|
||||||
|
|
||||||
|
object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
|
||||||
|
object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
|
||||||
|
object_property_add_const_link(OBJECT(&chip->psi), "xics",
|
||||||
|
OBJECT(qdev_get_machine()), &error_abort);
|
||||||
|
|
||||||
|
object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC);
|
||||||
|
object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
|
||||||
|
object_property_add_const_link(OBJECT(&chip->occ), "psi",
|
||||||
|
OBJECT(&chip->psi), &error_abort);
|
||||||
|
|
||||||
|
/* The LPC controller needs PSI to generate interrupts */
|
||||||
|
object_property_add_const_link(OBJECT(&chip->lpc), "psi",
|
||||||
|
OBJECT(&chip->psi), &error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
|
||||||
|
{
|
||||||
|
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
|
||||||
|
char *typename = pnv_core_typename(pcc->cpu_model);
|
||||||
|
size_t typesize = object_type_get_instance_size(typename);
|
||||||
|
int i, j;
|
||||||
|
char *name;
|
||||||
|
XICSFabric *xi = XICS_FABRIC(qdev_get_machine());
|
||||||
|
|
||||||
|
name = g_strdup_printf("icp-%x", chip->chip_id);
|
||||||
|
memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE);
|
||||||
|
sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio);
|
||||||
|
g_free(name);
|
||||||
|
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip));
|
||||||
|
|
||||||
|
/* Map the ICP registers for each thread */
|
||||||
|
for (i = 0; i < chip->nr_cores; i++) {
|
||||||
|
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
|
||||||
|
int core_hwid = CPU_CORE(pnv_core)->core_id;
|
||||||
|
|
||||||
|
for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
|
||||||
|
uint32_t pir = pcc->core_pir(chip, core_hwid) + j;
|
||||||
|
PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir));
|
||||||
|
|
||||||
|
memory_region_add_subregion(&chip->icp_mmio, pir << 12, &icp->mmio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(typename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pnv_chip_realize(DeviceState *dev, Error **errp)
|
static void pnv_chip_realize(DeviceState *dev, Error **errp)
|
||||||
@@ -691,6 +932,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
|
|||||||
object_property_set_int(OBJECT(pnv_core),
|
object_property_set_int(OBJECT(pnv_core),
|
||||||
pcc->core_pir(chip, core_hwid),
|
pcc->core_pir(chip, core_hwid),
|
||||||
"pir", &error_fatal);
|
"pir", &error_fatal);
|
||||||
|
object_property_add_const_link(OBJECT(pnv_core), "xics",
|
||||||
|
qdev_get_machine(), &error_fatal);
|
||||||
object_property_set_bool(OBJECT(pnv_core), true, "realized",
|
object_property_set_bool(OBJECT(pnv_core), true, "realized",
|
||||||
&error_fatal);
|
&error_fatal);
|
||||||
object_unref(OBJECT(pnv_core));
|
object_unref(OBJECT(pnv_core));
|
||||||
@@ -708,6 +951,32 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp)
|
|||||||
object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
|
object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
|
||||||
&error_fatal);
|
&error_fatal);
|
||||||
pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs);
|
pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs);
|
||||||
|
|
||||||
|
/* Interrupt Management Area. This is the memory region holding
|
||||||
|
* all the Interrupt Control Presenter (ICP) registers */
|
||||||
|
pnv_chip_icp_realize(chip, &error);
|
||||||
|
if (error) {
|
||||||
|
error_propagate(errp, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Processor Service Interface (PSI) Host Bridge */
|
||||||
|
object_property_set_int(OBJECT(&chip->psi), PNV_PSIHB_BASE(chip),
|
||||||
|
"bar", &error_fatal);
|
||||||
|
object_property_set_bool(OBJECT(&chip->psi), true, "realized", &error);
|
||||||
|
if (error) {
|
||||||
|
error_propagate(errp, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs);
|
||||||
|
|
||||||
|
/* Create the simplified OCC model */
|
||||||
|
object_property_set_bool(OBJECT(&chip->occ), true, "realized", &error);
|
||||||
|
if (error) {
|
||||||
|
error_propagate(errp, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Property pnv_chip_properties[] = {
|
static Property pnv_chip_properties[] = {
|
||||||
@@ -723,6 +992,7 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data)
|
|||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
set_bit(DEVICE_CATEGORY_CPU, dc->categories);
|
||||||
dc->realize = pnv_chip_realize;
|
dc->realize = pnv_chip_realize;
|
||||||
dc->props = pnv_chip_properties;
|
dc->props = pnv_chip_properties;
|
||||||
dc->desc = "PowerNV Chip";
|
dc->desc = "PowerNV Chip";
|
||||||
@@ -737,6 +1007,70 @@ static const TypeInfo pnv_chip_info = {
|
|||||||
.abstract = true,
|
.abstract = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
||||||
|
{
|
||||||
|
PnvMachineState *pnv = POWERNV_MACHINE(xi);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < pnv->num_chips; i++) {
|
||||||
|
if (ics_valid_irq(&pnv->chips[i]->psi.ics, irq)) {
|
||||||
|
return &pnv->chips[i]->psi.ics;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_ics_resend(XICSFabric *xi)
|
||||||
|
{
|
||||||
|
PnvMachineState *pnv = POWERNV_MACHINE(xi);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < pnv->num_chips; i++) {
|
||||||
|
ics_resend(&pnv->chips[i]->psi.ics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PowerPCCPU *ppc_get_vcpu_by_pir(int pir)
|
||||||
|
{
|
||||||
|
CPUState *cs;
|
||||||
|
|
||||||
|
CPU_FOREACH(cs) {
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
CPUPPCState *env = &cpu->env;
|
||||||
|
|
||||||
|
if (env->spr_cb[SPR_PIR].default_value == pir) {
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ICPState *pnv_icp_get(XICSFabric *xi, int pir)
|
||||||
|
{
|
||||||
|
PowerPCCPU *cpu = ppc_get_vcpu_by_pir(pir);
|
||||||
|
|
||||||
|
return cpu ? ICP(cpu->intc) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_pic_print_info(InterruptStatsProvider *obj,
|
||||||
|
Monitor *mon)
|
||||||
|
{
|
||||||
|
PnvMachineState *pnv = POWERNV_MACHINE(obj);
|
||||||
|
int i;
|
||||||
|
CPUState *cs;
|
||||||
|
|
||||||
|
CPU_FOREACH(cs) {
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
|
||||||
|
icp_pic_print_info(ICP(cpu->intc), mon);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < pnv->num_chips; i++) {
|
||||||
|
ics_pic_print_info(&pnv->chips[i]->psi.ics, mon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
|
static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
|
||||||
void *opaque, Error **errp)
|
void *opaque, Error **errp)
|
||||||
{
|
{
|
||||||
@@ -787,6 +1121,8 @@ static void powernv_machine_class_props_init(ObjectClass *oc)
|
|||||||
static void powernv_machine_class_init(ObjectClass *oc, void *data)
|
static void powernv_machine_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
MachineClass *mc = MACHINE_CLASS(oc);
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
XICSFabricClass *xic = XICS_FABRIC_CLASS(oc);
|
||||||
|
InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc);
|
||||||
|
|
||||||
mc->desc = "IBM PowerNV (Non-Virtualized)";
|
mc->desc = "IBM PowerNV (Non-Virtualized)";
|
||||||
mc->init = ppc_powernv_init;
|
mc->init = ppc_powernv_init;
|
||||||
@@ -797,6 +1133,10 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data)
|
|||||||
mc->no_parallel = 1;
|
mc->no_parallel = 1;
|
||||||
mc->default_boot_order = NULL;
|
mc->default_boot_order = NULL;
|
||||||
mc->default_ram_size = 1 * G_BYTE;
|
mc->default_ram_size = 1 * G_BYTE;
|
||||||
|
xic->icp_get = pnv_icp_get;
|
||||||
|
xic->ics_get = pnv_ics_get;
|
||||||
|
xic->ics_resend = pnv_ics_resend;
|
||||||
|
ispc->print_info = pnv_pic_print_info;
|
||||||
|
|
||||||
powernv_machine_class_props_init(oc);
|
powernv_machine_class_props_init(oc);
|
||||||
}
|
}
|
||||||
@@ -807,6 +1147,11 @@ static const TypeInfo powernv_machine_info = {
|
|||||||
.instance_size = sizeof(PnvMachineState),
|
.instance_size = sizeof(PnvMachineState),
|
||||||
.instance_init = powernv_machine_initfn,
|
.instance_init = powernv_machine_initfn,
|
||||||
.class_init = powernv_machine_class_init,
|
.class_init = powernv_machine_class_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
|
{ TYPE_XICS_FABRIC },
|
||||||
|
{ TYPE_INTERRUPT_STATS_PROVIDER },
|
||||||
|
{ },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void powernv_machine_register_types(void)
|
static void powernv_machine_register_types(void)
|
||||||
|
122
hw/ppc/pnv_bmc.c
Normal file
122
hw/ppc/pnv_bmc.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* QEMU PowerNV, BMC related functions
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2017, IBM Corporation.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License, version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hw/hw.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "target/ppc/cpu.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "hw/ipmi/ipmi.h"
|
||||||
|
#include "hw/ppc/fdt.h"
|
||||||
|
|
||||||
|
#include "hw/ppc/pnv.h"
|
||||||
|
|
||||||
|
#include <libfdt.h>
|
||||||
|
|
||||||
|
/* TODO: include definition in ipmi.h */
|
||||||
|
#define IPMI_SDR_FULL_TYPE 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OEM SEL Event data packet sent by BMC in response of a Read Event
|
||||||
|
* Message Buffer command
|
||||||
|
*/
|
||||||
|
typedef struct OemSel {
|
||||||
|
/* SEL header */
|
||||||
|
uint8_t id[2];
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t timestamp[4];
|
||||||
|
uint8_t manuf_id[3];
|
||||||
|
|
||||||
|
/* OEM SEL data (6 bytes) follows */
|
||||||
|
uint8_t netfun;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t data[4];
|
||||||
|
} OemSel;
|
||||||
|
|
||||||
|
#define SOFT_OFF 0x00
|
||||||
|
#define SOFT_REBOOT 0x01
|
||||||
|
|
||||||
|
static void pnv_gen_oem_sel(IPMIBmc *bmc, uint8_t reboot)
|
||||||
|
{
|
||||||
|
/* IPMI SEL Event are 16 bytes long */
|
||||||
|
OemSel sel = {
|
||||||
|
.id = { 0x55 , 0x55 },
|
||||||
|
.type = 0xC0, /* OEM */
|
||||||
|
.manuf_id = { 0x0, 0x0, 0x0 },
|
||||||
|
.timestamp = { 0x0, 0x0, 0x0, 0x0 },
|
||||||
|
.netfun = 0x3A, /* IBM */
|
||||||
|
.cmd = 0x04, /* AMI OEM SEL Power Notification */
|
||||||
|
.data = { reboot, 0xFF, 0xFF, 0xFF },
|
||||||
|
};
|
||||||
|
|
||||||
|
ipmi_bmc_gen_event(bmc, (uint8_t *) &sel, 0 /* do not log the event */);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pnv_bmc_powerdown(IPMIBmc *bmc)
|
||||||
|
{
|
||||||
|
pnv_gen_oem_sel(bmc, SOFT_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt)
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
int i;
|
||||||
|
const struct ipmi_sdr_compact *sdr;
|
||||||
|
uint16_t nextrec;
|
||||||
|
|
||||||
|
offset = fdt_add_subnode(fdt, 0, "/bmc");
|
||||||
|
_FDT(offset);
|
||||||
|
|
||||||
|
_FDT((fdt_setprop_string(fdt, offset, "name", "bmc")));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0)));
|
||||||
|
|
||||||
|
offset = fdt_add_subnode(fdt, offset, "sensors");
|
||||||
|
_FDT(offset);
|
||||||
|
|
||||||
|
_FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0)));
|
||||||
|
|
||||||
|
for (i = 0; !ipmi_bmc_sdr_find(bmc, i, &sdr, &nextrec); i++) {
|
||||||
|
int off;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
if (sdr->header.rec_type != IPMI_SDR_COMPACT_TYPE &&
|
||||||
|
sdr->header.rec_type != IPMI_SDR_FULL_TYPE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = g_strdup_printf("sensor@%x", sdr->sensor_owner_number);
|
||||||
|
off = fdt_add_subnode(fdt, offset, name);
|
||||||
|
_FDT(off);
|
||||||
|
g_free(name);
|
||||||
|
|
||||||
|
_FDT((fdt_setprop_cell(fdt, off, "reg", sdr->sensor_owner_number)));
|
||||||
|
_FDT((fdt_setprop_string(fdt, off, "name", "sensor")));
|
||||||
|
_FDT((fdt_setprop_string(fdt, off, "compatible", "ibm,ipmi-sensor")));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-reading-type",
|
||||||
|
sdr->reading_type)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-id",
|
||||||
|
sdr->entity_id)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-instance",
|
||||||
|
sdr->entity_instance)));
|
||||||
|
_FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-type",
|
||||||
|
sdr->sensor_type)));
|
||||||
|
}
|
||||||
|
}
|
@@ -25,6 +25,7 @@
|
|||||||
#include "hw/ppc/pnv.h"
|
#include "hw/ppc/pnv.h"
|
||||||
#include "hw/ppc/pnv_core.h"
|
#include "hw/ppc/pnv_core.h"
|
||||||
#include "hw/ppc/pnv_xscom.h"
|
#include "hw/ppc/pnv_xscom.h"
|
||||||
|
#include "hw/ppc/xics.h"
|
||||||
|
|
||||||
static void powernv_cpu_reset(void *opaque)
|
static void powernv_cpu_reset(void *opaque)
|
||||||
{
|
{
|
||||||
@@ -110,23 +111,37 @@ static const MemoryRegionOps pnv_core_xscom_ops = {
|
|||||||
.endianness = DEVICE_BIG_ENDIAN,
|
.endianness = DEVICE_BIG_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void pnv_core_realize_child(Object *child, Error **errp)
|
static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp)
|
||||||
{
|
{
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
CPUState *cs = CPU(child);
|
CPUState *cs = CPU(child);
|
||||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
Object *obj;
|
||||||
|
|
||||||
|
obj = object_new(TYPE_PNV_ICP);
|
||||||
|
object_property_add_child(OBJECT(cpu), "icp", obj, NULL);
|
||||||
|
object_property_add_const_link(obj, "xics", OBJECT(xi), &error_abort);
|
||||||
|
object_property_set_bool(obj, true, "realized", &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
object_property_set_bool(child, true, "realized", &local_err);
|
object_property_set_bool(child, true, "realized", &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
object_unparent(obj);
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
powernv_cpu_init(cpu, &local_err);
|
powernv_cpu_init(cpu, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
object_unparent(obj);
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xics_cpu_setup(xi, cpu, ICP(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pnv_core_realize(DeviceState *dev, Error **errp)
|
static void pnv_core_realize(DeviceState *dev, Error **errp)
|
||||||
@@ -140,6 +155,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
|
|||||||
void *obj;
|
void *obj;
|
||||||
int i, j;
|
int i, j;
|
||||||
char name[32];
|
char name[32];
|
||||||
|
Object *xi;
|
||||||
|
|
||||||
|
xi = object_property_get_link(OBJECT(dev), "xics", &local_err);
|
||||||
|
if (!xi) {
|
||||||
|
error_setg(errp, "%s: required link 'xics' not found: %s",
|
||||||
|
__func__, error_get_pretty(local_err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pc->threads = g_malloc0(size * cc->nr_threads);
|
pc->threads = g_malloc0(size * cc->nr_threads);
|
||||||
for (i = 0; i < cc->nr_threads; i++) {
|
for (i = 0; i < cc->nr_threads; i++) {
|
||||||
@@ -160,7 +183,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
|
|||||||
for (j = 0; j < cc->nr_threads; j++) {
|
for (j = 0; j < cc->nr_threads; j++) {
|
||||||
obj = pc->threads + j * size;
|
obj = pc->threads + j * size;
|
||||||
|
|
||||||
pnv_core_realize_child(obj, &local_err);
|
pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
106
hw/ppc/pnv_lpc.c
106
hw/ppc/pnv_lpc.c
@@ -92,14 +92,6 @@ enum {
|
|||||||
#define LPC_HC_REGS_OPB_SIZE 0x00001000
|
#define LPC_HC_REGS_OPB_SIZE 0x00001000
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: the "primary" cell should only be added on chip 0. This is
|
|
||||||
* how skiboot chooses the default LPC controller on multichip
|
|
||||||
* systems.
|
|
||||||
*
|
|
||||||
* It would be easly done if we can change the populate() interface to
|
|
||||||
* replace the PnvXScomInterface parameter by a PnvChip one
|
|
||||||
*/
|
|
||||||
static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
|
static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
|
||||||
{
|
{
|
||||||
const char compat[] = "ibm,power8-lpc\0ibm,lpc";
|
const char compat[] = "ibm,power8-lpc\0ibm,lpc";
|
||||||
@@ -119,7 +111,6 @@ static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
|
|||||||
_FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
|
_FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
|
||||||
_FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
|
_FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
|
||||||
_FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
|
_FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
|
||||||
_FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
|
|
||||||
_FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
|
_FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -250,6 +241,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
|
|||||||
.endianness = DEVICE_BIG_ENDIAN,
|
.endianness = DEVICE_BIG_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
|
||||||
|
{
|
||||||
|
bool lpc_to_opb_irq = false;
|
||||||
|
|
||||||
|
/* Update LPC controller to OPB line */
|
||||||
|
if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
|
||||||
|
uint32_t irqs;
|
||||||
|
|
||||||
|
irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
|
||||||
|
lpc_to_opb_irq = (irqs != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't honor the polarity register, it's pointless and unused
|
||||||
|
* anyway
|
||||||
|
*/
|
||||||
|
if (lpc_to_opb_irq) {
|
||||||
|
lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
|
||||||
|
} else {
|
||||||
|
lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update OPB internal latch */
|
||||||
|
lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
|
||||||
|
|
||||||
|
/* Reflect the interrupt */
|
||||||
|
pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0);
|
||||||
|
}
|
||||||
|
|
||||||
static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
|
static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
{
|
{
|
||||||
PnvLpcController *lpc = opaque;
|
PnvLpcController *lpc = opaque;
|
||||||
@@ -300,12 +319,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
|
|||||||
break;
|
break;
|
||||||
case LPC_HC_IRQSER_CTRL:
|
case LPC_HC_IRQSER_CTRL:
|
||||||
lpc->lpc_hc_irqser_ctrl = val;
|
lpc->lpc_hc_irqser_ctrl = val;
|
||||||
|
pnv_lpc_eval_irqs(lpc);
|
||||||
break;
|
break;
|
||||||
case LPC_HC_IRQMASK:
|
case LPC_HC_IRQMASK:
|
||||||
lpc->lpc_hc_irqmask = val;
|
lpc->lpc_hc_irqmask = val;
|
||||||
|
pnv_lpc_eval_irqs(lpc);
|
||||||
break;
|
break;
|
||||||
case LPC_HC_IRQSTAT:
|
case LPC_HC_IRQSTAT:
|
||||||
lpc->lpc_hc_irqstat &= ~val;
|
lpc->lpc_hc_irqstat &= ~val;
|
||||||
|
pnv_lpc_eval_irqs(lpc);
|
||||||
break;
|
break;
|
||||||
case LPC_HC_ERROR_ADDRESS:
|
case LPC_HC_ERROR_ADDRESS:
|
||||||
break;
|
break;
|
||||||
@@ -363,14 +385,15 @@ static void opb_master_write(void *opaque, hwaddr addr,
|
|||||||
switch (addr) {
|
switch (addr) {
|
||||||
case OPB_MASTER_LS_IRQ_STAT:
|
case OPB_MASTER_LS_IRQ_STAT:
|
||||||
lpc->opb_irq_stat &= ~val;
|
lpc->opb_irq_stat &= ~val;
|
||||||
|
pnv_lpc_eval_irqs(lpc);
|
||||||
break;
|
break;
|
||||||
case OPB_MASTER_LS_IRQ_MASK:
|
case OPB_MASTER_LS_IRQ_MASK:
|
||||||
/* XXX Filter out reserved bits */
|
|
||||||
lpc->opb_irq_mask = val;
|
lpc->opb_irq_mask = val;
|
||||||
|
pnv_lpc_eval_irqs(lpc);
|
||||||
break;
|
break;
|
||||||
case OPB_MASTER_LS_IRQ_POL:
|
case OPB_MASTER_LS_IRQ_POL:
|
||||||
/* XXX Filter out reserved bits */
|
|
||||||
lpc->opb_irq_pol = val;
|
lpc->opb_irq_pol = val;
|
||||||
|
pnv_lpc_eval_irqs(lpc);
|
||||||
break;
|
break;
|
||||||
case OPB_MASTER_LS_IRQ_INPUT:
|
case OPB_MASTER_LS_IRQ_INPUT:
|
||||||
/* Read only */
|
/* Read only */
|
||||||
@@ -398,6 +421,8 @@ static const MemoryRegionOps opb_master_ops = {
|
|||||||
static void pnv_lpc_realize(DeviceState *dev, Error **errp)
|
static void pnv_lpc_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
PnvLpcController *lpc = PNV_LPC(dev);
|
PnvLpcController *lpc = PNV_LPC(dev);
|
||||||
|
Object *obj;
|
||||||
|
Error *error = NULL;
|
||||||
|
|
||||||
/* Reg inits */
|
/* Reg inits */
|
||||||
lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
|
lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
|
||||||
@@ -441,6 +466,15 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
|
|||||||
pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev),
|
pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev),
|
||||||
&pnv_lpc_xscom_ops, lpc, "xscom-lpc",
|
&pnv_lpc_xscom_ops, lpc, "xscom-lpc",
|
||||||
PNV_XSCOM_LPC_SIZE);
|
PNV_XSCOM_LPC_SIZE);
|
||||||
|
|
||||||
|
/* get PSI object from chip */
|
||||||
|
obj = object_property_get_link(OBJECT(dev), "psi", &error);
|
||||||
|
if (!obj) {
|
||||||
|
error_setg(errp, "%s: required link 'psi' not found: %s",
|
||||||
|
__func__, error_get_pretty(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lpc->psi = PNV_PSI(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pnv_lpc_class_init(ObjectClass *klass, void *data)
|
static void pnv_lpc_class_init(ObjectClass *klass, void *data)
|
||||||
@@ -470,3 +504,53 @@ static void pnv_lpc_register_types(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
type_init(pnv_lpc_register_types)
|
type_init(pnv_lpc_register_types)
|
||||||
|
|
||||||
|
/* If we don't use the built-in LPC interrupt deserializer, we need
|
||||||
|
* to provide a set of qirqs for the ISA bus or things will go bad.
|
||||||
|
*
|
||||||
|
* Most machines using pre-Naples chips (without said deserializer)
|
||||||
|
* have a CPLD that will collect the SerIRQ and shoot them as a
|
||||||
|
* single level interrupt to the P8 chip. So let's setup a hook
|
||||||
|
* for doing just that.
|
||||||
|
*/
|
||||||
|
static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
|
||||||
|
{
|
||||||
|
PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
|
||||||
|
uint32_t old_state = pnv->cpld_irqstate;
|
||||||
|
PnvLpcController *lpc = PNV_LPC(opaque);
|
||||||
|
|
||||||
|
if (level) {
|
||||||
|
pnv->cpld_irqstate |= 1u << n;
|
||||||
|
} else {
|
||||||
|
pnv->cpld_irqstate &= ~(1u << n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pnv->cpld_irqstate != old_state) {
|
||||||
|
pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_EXTERNAL, pnv->cpld_irqstate != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
|
||||||
|
{
|
||||||
|
PnvLpcController *lpc = PNV_LPC(opaque);
|
||||||
|
|
||||||
|
/* The Naples HW latches the 1 levels, clearing is done by SW */
|
||||||
|
if (level) {
|
||||||
|
lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n;
|
||||||
|
pnv_lpc_eval_irqs(lpc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type,
|
||||||
|
int nirqs)
|
||||||
|
{
|
||||||
|
/* Not all variants have a working serial irq decoder. If not,
|
||||||
|
* handling of LPC interrupts becomes a platform issue (some
|
||||||
|
* platforms have a CPLD to do it).
|
||||||
|
*/
|
||||||
|
if (chip_type == PNV_CHIP_POWER8NVL) {
|
||||||
|
return qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, nirqs);
|
||||||
|
} else {
|
||||||
|
return qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, lpc, nirqs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user