Compare commits
378 Commits
pull-input
...
machine-pu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34294e2f54 | ||
|
|
0e6aac87fd | ||
|
|
d1f8764099 | ||
|
|
fec44a8c70 | ||
|
|
6717f587a4 | ||
|
|
355a8ccc5c | ||
|
|
5e9c2a8dac | ||
|
|
97398d900c | ||
|
|
a2a8dfa8d8 | ||
|
|
327d8e4ed2 | ||
|
|
43e3346e43 | ||
|
|
0c69996e22 | ||
|
|
c04bd47db6 | ||
|
|
eccfa35e9f | ||
|
|
a66d815cd5 | ||
|
|
d552f675fb | ||
|
|
f4b2add6cc | ||
|
|
c91a5883c3 | ||
|
|
4833e15f74 | ||
|
|
9c94d8e6c9 | ||
|
|
ed796373b4 | ||
|
|
1b4093ea66 | ||
|
|
8bfd0550be | ||
|
|
f09f9bd9fa | ||
|
|
0ebc03bc06 | ||
|
|
8bc92a762a | ||
|
|
8816c600d3 | ||
|
|
4674da1c49 | ||
|
|
6aeda86890 | ||
|
|
cad0b273e5 | ||
|
|
f235538e38 | ||
|
|
20e2dec149 | ||
|
|
af1d3ebbef | ||
|
|
588c36cac7 | ||
|
|
36e9916811 | ||
|
|
f6b5319d41 | ||
|
|
3356128cd1 | ||
|
|
a36304fdca | ||
|
|
72700d7e73 | ||
|
|
c1fa017c7e | ||
|
|
fbb4e98341 | ||
|
|
76a9e9f680 | ||
|
|
3153119e9b | ||
|
|
f1a6cf3ef7 | ||
|
|
c18ad9a54b | ||
|
|
e5c0d3ce40 | ||
|
|
a7a00a729a | ||
|
|
788d2599de | ||
|
|
a88dced8eb | ||
|
|
14646457ae | ||
|
|
1e440cbc99 | ||
|
|
d6f1445faf | ||
|
|
3ba6a710e6 | ||
|
|
4caecccbc1 | ||
|
|
e76d1798fa | ||
|
|
281b2201e4 | ||
|
|
33577b47c6 | ||
|
|
39c350ee12 | ||
|
|
e1fb647199 | ||
|
|
fd97fd4408 | ||
|
|
2ae823d4f7 | ||
|
|
b094f2e015 | ||
|
|
a6cdb77f81 | ||
|
|
a58a4cb187 | ||
|
|
fad7fb9ccd | ||
|
|
f84d587111 | ||
|
|
6a991e07bb | ||
|
|
cfd47a71df | ||
|
|
d41e0bed7b | ||
|
|
9828f9b6c8 | ||
|
|
7aac531ef2 | ||
|
|
05061d8548 | ||
|
|
3feea4447f | ||
|
|
1252cf40a8 | ||
|
|
9dfbf250d2 | ||
|
|
98c63057d2 | ||
|
|
15d62af4b6 | ||
|
|
fc6c9257c6 | ||
|
|
de40abfecf | ||
|
|
0d6ff71ae3 | ||
|
|
1a8b408168 | ||
|
|
492a4c94be | ||
|
|
4467c6c118 | ||
|
|
b9f9c5b41a | ||
|
|
f083d92c03 | ||
|
|
26317698ef | ||
|
|
e2e02a8207 | ||
|
|
a657f79e32 | ||
|
|
880f848650 | ||
|
|
8b33e82b86 | ||
|
|
0d611402a1 | ||
|
|
7223c48cff | ||
|
|
dc59997871 | ||
|
|
6d425eb94d | ||
|
|
b9c600d207 | ||
|
|
e3f66e0368 | ||
|
|
fcce736719 | ||
|
|
ebab225910 | ||
|
|
9a3f5cf1bf | ||
|
|
78f9dc859d | ||
|
|
b2f56462d5 | ||
|
|
618a5a8bc5 | ||
|
|
b8f45cdf78 | ||
|
|
c4bea1690e | ||
|
|
10bf03af12 | ||
|
|
a08f0c3b5f | ||
|
|
fba98d455a | ||
|
|
8a56fdadaf | ||
|
|
23588797b6 | ||
|
|
6af4016020 | ||
|
|
8942764f54 | ||
|
|
c10c9d9615 | ||
|
|
6340472c54 | ||
|
|
2073d410ce | ||
|
|
abb21ac3e6 | ||
|
|
71968dbfd8 | ||
|
|
5997c210b9 | ||
|
|
965415eb20 | ||
|
|
a81d616437 | ||
|
|
73176bee99 | ||
|
|
f86b8b584b | ||
|
|
924e8a2bbc | ||
|
|
0ae053b7e1 | ||
|
|
58346b82ed | ||
|
|
1001dd9f84 | ||
|
|
fb9245c261 | ||
|
|
798609bbe2 | ||
|
|
c540d53ac8 | ||
|
|
c21cc6ca98 | ||
|
|
b07363a1a3 | ||
|
|
2b77e60ab8 | ||
|
|
396374caea | ||
|
|
6dcea61425 | ||
|
|
0dcee62261 | ||
|
|
8326ec2c83 | ||
|
|
d1ab9681ac | ||
|
|
f2d089425d | ||
|
|
5a68be94ac | ||
|
|
5167560b03 | ||
|
|
52fc01d973 | ||
|
|
4fa9f08e96 | ||
|
|
0bc6001f0d | ||
|
|
6acb971a94 | ||
|
|
7f996411ad | ||
|
|
a580d82085 | ||
|
|
4f298a4b29 | ||
|
|
5da4fb0018 | ||
|
|
494f7b572e | ||
|
|
ed2ef10c0c | ||
|
|
adcb89d55d | ||
|
|
2adba0a18a | ||
|
|
907e7c94d1 | ||
|
|
5803fce389 | ||
|
|
3d3ebcad6a | ||
|
|
3811ef14f5 | ||
|
|
ebde2465a9 | ||
|
|
ae29883508 | ||
|
|
f9735fd53f | ||
|
|
342f7a9d05 | ||
|
|
6167ebbd91 | ||
|
|
869a58af86 | ||
|
|
b7fcb3603c | ||
|
|
c1bf3531ae | ||
|
|
f7df22de56 | ||
|
|
18c440e1e1 | ||
|
|
b99514135b | ||
|
|
5fe79386ba | ||
|
|
b63283d7c3 | ||
|
|
2c02a48e6d | ||
|
|
c82f503dd5 | ||
|
|
7335a95abd | ||
|
|
226419d615 | ||
|
|
75fd6f13af | ||
|
|
79248c22ad | ||
|
|
27b9fc54d2 | ||
|
|
e08fde0c5e | ||
|
|
bda055096b | ||
|
|
9b613f4e40 | ||
|
|
c9f4b77ad5 | ||
|
|
fff4e48ed5 | ||
|
|
a0d06486b4 | ||
|
|
fc1769b758 | ||
|
|
631a438755 | ||
|
|
4eae2a657d | ||
|
|
f203549108 | ||
|
|
3f3009c098 | ||
|
|
9815cba502 | ||
|
|
39b6dbd8d7 | ||
|
|
32c3db5b26 | ||
|
|
a587a3fe6c | ||
|
|
8646992279 | ||
|
|
568b01caf3 | ||
|
|
99b88c6d1f | ||
|
|
062ed5d8d6 | ||
|
|
e593c0211b | ||
|
|
e2e5ee9c56 | ||
|
|
2d82f8a3cd | ||
|
|
db0da029a1 | ||
|
|
b16a44e13e | ||
|
|
a2d96af4bb | ||
|
|
08b758b482 | ||
|
|
317856cac8 | ||
|
|
f50dfe457f | ||
|
|
a589720567 | ||
|
|
30fd3e2790 | ||
|
|
de7971ffb9 | ||
|
|
b83b68a013 | ||
|
|
e560d141ab | ||
|
|
5151d23e65 | ||
|
|
294bbbb425 | ||
|
|
256920eb94 | ||
|
|
a9d5aed12d | ||
|
|
abc981bf29 | ||
|
|
5838d66e73 | ||
|
|
0a27af918b | ||
|
|
c619644067 | ||
|
|
469002263a | ||
|
|
7df9381b7a | ||
|
|
75cfb3bb41 | ||
|
|
8b8a61ad8c | ||
|
|
4fca654872 | ||
|
|
3a3c752f0b | ||
|
|
a006b67fe4 | ||
|
|
96b1a8bb55 | ||
|
|
502edbf834 | ||
|
|
25637d31f2 | ||
|
|
c6644fc88b | ||
|
|
ef3027affc | ||
|
|
d2eae20790 | ||
|
|
a648c13738 | ||
|
|
58aa7d8e44 | ||
|
|
4ba364b472 | ||
|
|
8519c8e073 | ||
|
|
3293680dc7 | ||
|
|
5763795f93 | ||
|
|
eda509fa0a | ||
|
|
28b90d9c19 | ||
|
|
99f2dbd343 | ||
|
|
614e8018ed | ||
|
|
a6ccabd676 | ||
|
|
46d921bebe | ||
|
|
e0d2bd5195 | ||
|
|
a60c785608 | ||
|
|
36a43ea83b | ||
|
|
70bee80d6b | ||
|
|
e2ec75685c | ||
|
|
305ae88895 | ||
|
|
d1cc881d54 | ||
|
|
ce9a2aa372 | ||
|
|
91ec41dc3f | ||
|
|
362786f14a | ||
|
|
f1b2bc601a | ||
|
|
338d3f415e | ||
|
|
9fe7101f1d | ||
|
|
031143c8d5 | ||
|
|
39e0c4f47d | ||
|
|
0ab9cd9a4b | ||
|
|
9fbad2ca36 | ||
|
|
3a2d44f6dd | ||
|
|
d24b2b1ccc | ||
|
|
5dd2d45e34 | ||
|
|
415ab35a44 | ||
|
|
443590c204 | ||
|
|
97556fe80e | ||
|
|
4792b7e9d5 | ||
|
|
8269fb7082 | ||
|
|
a95e9a485b | ||
|
|
ef00bdaf8c | ||
|
|
8210f5f6f5 | ||
|
|
778d9f9b25 | ||
|
|
729633c2bc | ||
|
|
29cb533d8c | ||
|
|
f1060c55bf | ||
|
|
8e41fb63c5 | ||
|
|
7ebb2745ac | ||
|
|
0a75601853 | ||
|
|
528f46af6e | ||
|
|
bb8f32c031 | ||
|
|
c586eac336 | ||
|
|
1464ad45cd | ||
|
|
48eb62a74f | ||
|
|
b1918fbb1c | ||
|
|
10f759079e | ||
|
|
b5a1b44318 | ||
|
|
0399293e5b | ||
|
|
f194a1ae53 | ||
|
|
9ee86b8526 | ||
|
|
4d91e9115c | ||
|
|
c81200b014 | ||
|
|
14f00c6c49 | ||
|
|
96a1616c85 | ||
|
|
e55250c6cb | ||
|
|
3c0f12df65 | ||
|
|
ba63cf47a9 | ||
|
|
a55c910e0b | ||
|
|
9776f63645 | ||
|
|
7ef295ea5b | ||
|
|
140b7ce5ff | ||
|
|
04ae712a9f | ||
|
|
e334bd3190 | ||
|
|
9886ecdf31 | ||
|
|
91cca2cda9 | ||
|
|
aa6489da4e | ||
|
|
dacf0a2ff7 | ||
|
|
12dcc3217d | ||
|
|
73462dddf6 | ||
|
|
c3ae85fc8f | ||
|
|
9c5a746038 | ||
|
|
b2e62d9a7b | ||
|
|
ed50ff7875 | ||
|
|
f9fd40ebe4 | ||
|
|
49017bd8b4 | ||
|
|
a0e1e6d705 | ||
|
|
eab713941a | ||
|
|
4824a61a6d | ||
|
|
738a5d9fbb | ||
|
|
16f4a8dc5c | ||
|
|
76151cacfe | ||
|
|
83ec1923cd | ||
|
|
8b41c30525 | ||
|
|
0719e71e52 | ||
|
|
71c2768433 | ||
|
|
8c4f0eb94c | ||
|
|
2d3b7c0164 | ||
|
|
f8693c2cd0 | ||
|
|
60253ed1e6 | ||
|
|
9f14b0add1 | ||
|
|
74074e8a7c | ||
|
|
3c52ddcdc5 | ||
|
|
750cf86932 | ||
|
|
ed6128ebbd | ||
|
|
4ade0541de | ||
|
|
3d211d9f4d | ||
|
|
b23197f9cf | ||
|
|
bc9beb47c7 | ||
|
|
5d4e1a1081 | ||
|
|
1bcea73e13 | ||
|
|
56797b1fbc | ||
|
|
3596f524d4 | ||
|
|
62cb4145bb | ||
|
|
4779dc1d19 | ||
|
|
23d92d68e7 | ||
|
|
2c140f5f2c | ||
|
|
6411dd1334 | ||
|
|
9c279bec75 | ||
|
|
646fd16865 | ||
|
|
ce350f32e4 | ||
|
|
d90527178c | ||
|
|
6aaa681c9b | ||
|
|
ce1307e180 | ||
|
|
bc994b74ea | ||
|
|
562f5e0b97 | ||
|
|
8581c115d2 | ||
|
|
a28d8391e3 | ||
|
|
99abd0d6f7 | ||
|
|
fe345a3d5d | ||
|
|
8777f6abdb | ||
|
|
c5b2ee4c7a | ||
|
|
5ab0e547bf | ||
|
|
66fb2d5467 | ||
|
|
0b85d73583 | ||
|
|
d9c7737e57 | ||
|
|
9c74a85304 | ||
|
|
fee5b753ff | ||
|
|
05fa1c742f | ||
|
|
d2ba7ecb34 | ||
|
|
cc199b16cf | ||
|
|
4c9bca7e39 | ||
|
|
16096a4d47 | ||
|
|
21cd917ff5 | ||
|
|
e5b43573e2 | ||
|
|
04a3615860 | ||
|
|
939901dcd2 | ||
|
|
b189346eb1 | ||
|
|
1bff960642 | ||
|
|
60390a2192 | ||
|
|
eab8eb8db3 | ||
|
|
7725b8bf12 |
20
MAINTAINERS
20
MAINTAINERS
@@ -234,6 +234,7 @@ L: kvm@vger.kernel.org
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: kvm-*
|
F: kvm-*
|
||||||
F: */kvm.*
|
F: */kvm.*
|
||||||
|
F: include/sysemu/kvm*.h
|
||||||
|
|
||||||
ARM
|
ARM
|
||||||
M: Peter Maydell <peter.maydell@linaro.org>
|
M: Peter Maydell <peter.maydell@linaro.org>
|
||||||
@@ -656,12 +657,6 @@ F: hw/*/grlib*
|
|||||||
|
|
||||||
S390 Machines
|
S390 Machines
|
||||||
-------------
|
-------------
|
||||||
S390 Virtio
|
|
||||||
M: Alexander Graf <agraf@suse.de>
|
|
||||||
S: Maintained
|
|
||||||
F: hw/s390x/s390-*.c
|
|
||||||
X: hw/s390x/*pci*.[hc]
|
|
||||||
|
|
||||||
S390 Virtio-ccw
|
S390 Virtio-ccw
|
||||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||||
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
||||||
@@ -669,7 +664,6 @@ M: Alexander Graf <agraf@suse.de>
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: hw/char/sclp*.[hc]
|
F: hw/char/sclp*.[hc]
|
||||||
F: hw/s390x/
|
F: hw/s390x/
|
||||||
X: hw/s390x/s390-virtio-bus.[ch]
|
|
||||||
F: include/hw/s390x/
|
F: include/hw/s390x/
|
||||||
F: pc-bios/s390-ccw/
|
F: pc-bios/s390-ccw/
|
||||||
F: hw/watchdog/wdt_diag288.c
|
F: hw/watchdog/wdt_diag288.c
|
||||||
@@ -723,6 +717,12 @@ F: hw/timer/hpet*
|
|||||||
F: hw/timer/i8254*
|
F: hw/timer/i8254*
|
||||||
F: hw/timer/mc146818rtc*
|
F: hw/timer/mc146818rtc*
|
||||||
|
|
||||||
|
Machine core
|
||||||
|
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||||
|
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||||
|
S: Supported
|
||||||
|
F: hw/core/machine.c
|
||||||
|
F: include/hw/boards.h
|
||||||
|
|
||||||
Xtensa Machines
|
Xtensa Machines
|
||||||
---------------
|
---------------
|
||||||
@@ -872,6 +872,7 @@ VFIO
|
|||||||
M: Alex Williamson <alex.williamson@redhat.com>
|
M: Alex Williamson <alex.williamson@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/vfio/*
|
F: hw/vfio/*
|
||||||
|
F: include/hw/vfio/
|
||||||
|
|
||||||
vhost
|
vhost
|
||||||
M: Michael S. Tsirkin <mst@redhat.com>
|
M: Michael S. Tsirkin <mst@redhat.com>
|
||||||
@@ -883,6 +884,7 @@ M: Michael S. Tsirkin <mst@redhat.com>
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: hw/*/virtio*
|
F: hw/*/virtio*
|
||||||
F: net/vhost-user.c
|
F: net/vhost-user.c
|
||||||
|
F: include/hw/virtio/
|
||||||
|
|
||||||
virtio-9p
|
virtio-9p
|
||||||
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||||
@@ -928,6 +930,7 @@ M: Amit Shah <amit.shah@redhat.com>
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: hw/virtio/virtio-rng.c
|
F: hw/virtio/virtio-rng.c
|
||||||
F: include/hw/virtio/virtio-rng.h
|
F: include/hw/virtio/virtio-rng.h
|
||||||
|
F: include/sysemu/rng*.h
|
||||||
F: backends/rng*.c
|
F: backends/rng*.c
|
||||||
|
|
||||||
nvme
|
nvme
|
||||||
@@ -1128,6 +1131,7 @@ Network device backends
|
|||||||
M: Jason Wang <jasowang@redhat.com>
|
M: Jason Wang <jasowang@redhat.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: net/
|
F: net/
|
||||||
|
F: include/net/
|
||||||
T: git git://github.com/jasowang/qemu.git net
|
T: git git://github.com/jasowang/qemu.git net
|
||||||
|
|
||||||
Netmap network backend
|
Netmap network backend
|
||||||
@@ -1223,10 +1227,12 @@ F: scripts/qmp/
|
|||||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||||
|
|
||||||
SLIRP
|
SLIRP
|
||||||
|
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||||
M: Jan Kiszka <jan.kiszka@siemens.com>
|
M: Jan Kiszka <jan.kiszka@siemens.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: slirp/
|
F: slirp/
|
||||||
F: net/slirp.c
|
F: net/slirp.c
|
||||||
|
F: include/net/slirp.h
|
||||||
T: git git://git.kiszka.org/qemu.git queues/slirp
|
T: git git://git.kiszka.org/qemu.git queues/slirp
|
||||||
|
|
||||||
Tracing
|
Tracing
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -238,7 +238,7 @@ qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-o
|
|||||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||||
|
|
||||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
|
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o libqemuutil.a libqemustub.a
|
||||||
|
|
||||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a
|
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a
|
||||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||||
@@ -329,7 +329,7 @@ ifneq ($(EXESUF),)
|
|||||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y)
|
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) libqemuutil.a libqemustub.a
|
||||||
$(call LINK, $^)
|
$(call LINK, $^)
|
||||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a
|
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a
|
||||||
$(call LINK, $^)
|
$(call LINK, $^)
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ static CharDriverState *chr_baum_init(const char *id,
|
|||||||
ChardevReturn *ret,
|
ChardevReturn *ret,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
ChardevCommon *common = qapi_ChardevDummy_base(backend->u.braille);
|
ChardevCommon *common = backend->u.braille;
|
||||||
BaumDriverState *baum;
|
BaumDriverState *baum;
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
brlapi_handle_t *handle;
|
brlapi_handle_t *handle;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
|
|||||||
ChardevReturn *ret,
|
ChardevReturn *ret,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
ChardevCommon *common = qapi_ChardevDummy_base(backend->u.msmouse);
|
ChardevCommon *common = backend->u.msmouse;
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
|
|
||||||
chr = qemu_chr_alloc(common, errp);
|
chr = qemu_chr_alloc(common, errp);
|
||||||
|
|||||||
@@ -25,33 +25,12 @@ typedef struct RngEgd
|
|||||||
|
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
char *chr_name;
|
char *chr_name;
|
||||||
|
|
||||||
GSList *requests;
|
|
||||||
} RngEgd;
|
} RngEgd;
|
||||||
|
|
||||||
typedef struct RngRequest
|
static void rng_egd_request_entropy(RngBackend *b, RngRequest *req)
|
||||||
{
|
|
||||||
EntropyReceiveFunc *receive_entropy;
|
|
||||||
uint8_t *data;
|
|
||||||
void *opaque;
|
|
||||||
size_t offset;
|
|
||||||
size_t size;
|
|
||||||
} RngRequest;
|
|
||||||
|
|
||||||
static void rng_egd_request_entropy(RngBackend *b, size_t size,
|
|
||||||
EntropyReceiveFunc *receive_entropy,
|
|
||||||
void *opaque)
|
|
||||||
{
|
{
|
||||||
RngEgd *s = RNG_EGD(b);
|
RngEgd *s = RNG_EGD(b);
|
||||||
RngRequest *req;
|
size_t size = req->size;
|
||||||
|
|
||||||
req = g_malloc(sizeof(*req));
|
|
||||||
|
|
||||||
req->offset = 0;
|
|
||||||
req->size = size;
|
|
||||||
req->receive_entropy = receive_entropy;
|
|
||||||
req->opaque = opaque;
|
|
||||||
req->data = g_malloc(req->size);
|
|
||||||
|
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
uint8_t header[2];
|
uint8_t header[2];
|
||||||
@@ -65,24 +44,15 @@ static void rng_egd_request_entropy(RngBackend *b, size_t size,
|
|||||||
|
|
||||||
size -= len;
|
size -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->requests = g_slist_append(s->requests, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rng_egd_free_request(RngRequest *req)
|
|
||||||
{
|
|
||||||
g_free(req->data);
|
|
||||||
g_free(req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rng_egd_chr_can_read(void *opaque)
|
static int rng_egd_chr_can_read(void *opaque)
|
||||||
{
|
{
|
||||||
RngEgd *s = RNG_EGD(opaque);
|
RngEgd *s = RNG_EGD(opaque);
|
||||||
GSList *i;
|
RngRequest *req;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
for (i = s->requests; i; i = i->next) {
|
QSIMPLEQ_FOREACH(req, &s->parent.requests, next) {
|
||||||
RngRequest *req = i->data;
|
|
||||||
size += req->size - req->offset;
|
size += req->size - req->offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,8 +64,8 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
RngEgd *s = RNG_EGD(opaque);
|
RngEgd *s = RNG_EGD(opaque);
|
||||||
size_t buf_offset = 0;
|
size_t buf_offset = 0;
|
||||||
|
|
||||||
while (size > 0 && s->requests) {
|
while (size > 0 && !QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||||
RngRequest *req = s->requests->data;
|
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
|
||||||
int len = MIN(size, req->size - req->offset);
|
int len = MIN(size, req->size - req->offset);
|
||||||
|
|
||||||
memcpy(req->data + req->offset, buf + buf_offset, len);
|
memcpy(req->data + req->offset, buf + buf_offset, len);
|
||||||
@@ -104,38 +74,13 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
size -= len;
|
size -= len;
|
||||||
|
|
||||||
if (req->offset == req->size) {
|
if (req->offset == req->size) {
|
||||||
s->requests = g_slist_remove_link(s->requests, s->requests);
|
|
||||||
|
|
||||||
req->receive_entropy(req->opaque, req->data, req->size);
|
req->receive_entropy(req->opaque, req->data, req->size);
|
||||||
|
|
||||||
rng_egd_free_request(req);
|
rng_backend_finalize_request(&s->parent, req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rng_egd_free_requests(RngEgd *s)
|
|
||||||
{
|
|
||||||
GSList *i;
|
|
||||||
|
|
||||||
for (i = s->requests; i; i = i->next) {
|
|
||||||
rng_egd_free_request(i->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_slist_free(s->requests);
|
|
||||||
s->requests = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rng_egd_cancel_requests(RngBackend *b)
|
|
||||||
{
|
|
||||||
RngEgd *s = RNG_EGD(b);
|
|
||||||
|
|
||||||
/* We simply delete the list of pending requests. If there is data in the
|
|
||||||
* queue waiting to be read, this is okay, because there will always be
|
|
||||||
* more data than we requested originally
|
|
||||||
*/
|
|
||||||
rng_egd_free_requests(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rng_egd_opened(RngBackend *b, Error **errp)
|
static void rng_egd_opened(RngBackend *b, Error **errp)
|
||||||
{
|
{
|
||||||
RngEgd *s = RNG_EGD(b);
|
RngEgd *s = RNG_EGD(b);
|
||||||
@@ -204,8 +149,6 @@ static void rng_egd_finalize(Object *obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_free(s->chr_name);
|
g_free(s->chr_name);
|
||||||
|
|
||||||
rng_egd_free_requests(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rng_egd_class_init(ObjectClass *klass, void *data)
|
static void rng_egd_class_init(ObjectClass *klass, void *data)
|
||||||
@@ -213,7 +156,6 @@ static void rng_egd_class_init(ObjectClass *klass, void *data)
|
|||||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||||
|
|
||||||
rbc->request_entropy = rng_egd_request_entropy;
|
rbc->request_entropy = rng_egd_request_entropy;
|
||||||
rbc->cancel_requests = rng_egd_cancel_requests;
|
|
||||||
rbc->opened = rng_egd_opened;
|
rbc->opened = rng_egd_opened;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,6 @@ struct RndRandom
|
|||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
||||||
EntropyReceiveFunc *receive_func;
|
|
||||||
void *opaque;
|
|
||||||
size_t size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,37 +34,36 @@ struct RndRandom
|
|||||||
static void entropy_available(void *opaque)
|
static void entropy_available(void *opaque)
|
||||||
{
|
{
|
||||||
RndRandom *s = RNG_RANDOM(opaque);
|
RndRandom *s = RNG_RANDOM(opaque);
|
||||||
uint8_t buffer[s->size];
|
|
||||||
|
while (!QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||||
|
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
len = read(s->fd, buffer, s->size);
|
len = read(s->fd, req->data, req->size);
|
||||||
if (len < 0 && errno == EAGAIN) {
|
if (len < 0 && errno == EAGAIN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g_assert(len != -1);
|
g_assert(len != -1);
|
||||||
|
|
||||||
s->receive_func(s->opaque, buffer, len);
|
req->receive_entropy(req->opaque, req->data, len);
|
||||||
s->receive_func = NULL;
|
|
||||||
|
|
||||||
|
rng_backend_finalize_request(&s->parent, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We've drained all requests, the fd handler can be reset. */
|
||||||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rng_random_request_entropy(RngBackend *b, size_t size,
|
static void rng_random_request_entropy(RngBackend *b, RngRequest *req)
|
||||||
EntropyReceiveFunc *receive_entropy,
|
|
||||||
void *opaque)
|
|
||||||
{
|
{
|
||||||
RndRandom *s = RNG_RANDOM(b);
|
RndRandom *s = RNG_RANDOM(b);
|
||||||
|
|
||||||
if (s->receive_func) {
|
if (QSIMPLEQ_EMPTY(&s->parent.requests)) {
|
||||||
s->receive_func(s->opaque, NULL, 0);
|
/* If there are no pending requests yet, we need to
|
||||||
}
|
* install our fd handler. */
|
||||||
|
|
||||||
s->receive_func = receive_entropy;
|
|
||||||
s->opaque = opaque;
|
|
||||||
s->size = size;
|
|
||||||
|
|
||||||
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
|
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void rng_random_opened(RngBackend *b, Error **errp)
|
static void rng_random_opened(RngBackend *b, Error **errp)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,18 +20,20 @@ void rng_backend_request_entropy(RngBackend *s, size_t size,
|
|||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
||||||
|
RngRequest *req;
|
||||||
|
|
||||||
if (k->request_entropy) {
|
if (k->request_entropy) {
|
||||||
k->request_entropy(s, size, receive_entropy, opaque);
|
req = g_malloc(sizeof(*req));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void rng_backend_cancel_requests(RngBackend *s)
|
req->offset = 0;
|
||||||
{
|
req->size = size;
|
||||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
req->receive_entropy = receive_entropy;
|
||||||
|
req->opaque = opaque;
|
||||||
|
req->data = g_malloc(req->size);
|
||||||
|
|
||||||
if (k->cancel_requests) {
|
k->request_entropy(s, req);
|
||||||
k->cancel_requests(s);
|
|
||||||
|
QSIMPLEQ_INSERT_TAIL(&s->requests, req, next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,14 +75,48 @@ static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
|||||||
s->opened = true;
|
s->opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rng_backend_free_request(RngRequest *req)
|
||||||
|
{
|
||||||
|
g_free(req->data);
|
||||||
|
g_free(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rng_backend_free_requests(RngBackend *s)
|
||||||
|
{
|
||||||
|
RngRequest *req, *next;
|
||||||
|
|
||||||
|
QSIMPLEQ_FOREACH_SAFE(req, &s->requests, next, next) {
|
||||||
|
rng_backend_free_request(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSIMPLEQ_INIT(&s->requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rng_backend_finalize_request(RngBackend *s, RngRequest *req)
|
||||||
|
{
|
||||||
|
QSIMPLEQ_REMOVE(&s->requests, req, RngRequest, next);
|
||||||
|
rng_backend_free_request(req);
|
||||||
|
}
|
||||||
|
|
||||||
static void rng_backend_init(Object *obj)
|
static void rng_backend_init(Object *obj)
|
||||||
{
|
{
|
||||||
|
RngBackend *s = RNG_BACKEND(obj);
|
||||||
|
|
||||||
|
QSIMPLEQ_INIT(&s->requests);
|
||||||
|
|
||||||
object_property_add_bool(obj, "opened",
|
object_property_add_bool(obj, "opened",
|
||||||
rng_backend_prop_get_opened,
|
rng_backend_prop_get_opened,
|
||||||
rng_backend_prop_set_opened,
|
rng_backend_prop_set_opened,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rng_backend_finalize(Object *obj)
|
||||||
|
{
|
||||||
|
RngBackend *s = RNG_BACKEND(obj);
|
||||||
|
|
||||||
|
rng_backend_free_requests(s);
|
||||||
|
}
|
||||||
|
|
||||||
static void rng_backend_class_init(ObjectClass *oc, void *data)
|
static void rng_backend_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||||
@@ -93,6 +129,7 @@ static const TypeInfo rng_backend_info = {
|
|||||||
.parent = TYPE_OBJECT,
|
.parent = TYPE_OBJECT,
|
||||||
.instance_size = sizeof(RngBackend),
|
.instance_size = sizeof(RngBackend),
|
||||||
.instance_init = rng_backend_init,
|
.instance_init = rng_backend_init,
|
||||||
|
.instance_finalize = rng_backend_finalize,
|
||||||
.class_size = sizeof(RngBackendClass),
|
.class_size = sizeof(RngBackendClass),
|
||||||
.class_init = rng_backend_class_init,
|
.class_init = rng_backend_class_init,
|
||||||
.abstract = true,
|
.abstract = true,
|
||||||
|
|||||||
396
block.c
396
block.c
@@ -53,23 +53,6 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* A BdrvDirtyBitmap can be in three possible states:
|
|
||||||
* (1) successor is NULL and disabled is false: full r/w mode
|
|
||||||
* (2) successor is NULL and disabled is true: read only mode ("disabled")
|
|
||||||
* (3) successor is set: frozen mode.
|
|
||||||
* A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set,
|
|
||||||
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
|
||||||
*/
|
|
||||||
struct BdrvDirtyBitmap {
|
|
||||||
HBitmap *bitmap; /* Dirty sector bitmap implementation */
|
|
||||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
|
||||||
char *name; /* Optional non-empty unique ID */
|
|
||||||
int64_t size; /* Size of the bitmap (Number of sectors) */
|
|
||||||
bool disabled; /* Bitmap is read-only */
|
|
||||||
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
|
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
|
||||||
|
|
||||||
struct BdrvStates bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states);
|
struct BdrvStates bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states);
|
||||||
@@ -88,9 +71,6 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
|||||||
BlockDriverState *parent,
|
BlockDriverState *parent,
|
||||||
const BdrvChildRole *child_role, Error **errp);
|
const BdrvChildRole *child_role, Error **errp);
|
||||||
|
|
||||||
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
|
|
||||||
static void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs);
|
|
||||||
|
|
||||||
/* If non-zero, use only whitelisted block drivers */
|
/* If non-zero, use only whitelisted block drivers */
|
||||||
static int use_bdrv_whitelist;
|
static int use_bdrv_whitelist;
|
||||||
|
|
||||||
@@ -687,13 +667,19 @@ int bdrv_parse_cache_flags(const char *mode, int *flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the flags that a temporary snapshot should get, based on the
|
* Returns the options and flags that a temporary snapshot should get, based on
|
||||||
* originally requested flags (the originally requested image will have flags
|
* the originally requested flags (the originally requested image will have
|
||||||
* like a backing file)
|
* flags like a backing file)
|
||||||
*/
|
*/
|
||||||
static int bdrv_temp_snapshot_flags(int flags)
|
static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options,
|
||||||
|
int parent_flags, QDict *parent_options)
|
||||||
{
|
{
|
||||||
return (flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY;
|
*child_flags = (parent_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY;
|
||||||
|
|
||||||
|
/* For temporary files, unconditional cache=unsafe is fine */
|
||||||
|
qdict_set_default_str(child_options, BDRV_OPT_CACHE_WB, "on");
|
||||||
|
qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off");
|
||||||
|
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1424,13 +1410,13 @@ done:
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
static int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags,
|
||||||
|
QDict *snapshot_options, Error **errp)
|
||||||
{
|
{
|
||||||
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
|
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
|
||||||
char *tmp_filename = g_malloc0(PATH_MAX + 1);
|
char *tmp_filename = g_malloc0(PATH_MAX + 1);
|
||||||
int64_t total_size;
|
int64_t total_size;
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
QDict *snapshot_options;
|
|
||||||
BlockDriverState *bs_snapshot;
|
BlockDriverState *bs_snapshot;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -1464,8 +1450,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare a new options QDict for the temporary file */
|
/* Prepare options QDict for the temporary file */
|
||||||
snapshot_options = qdict_new();
|
|
||||||
qdict_put(snapshot_options, "file.driver",
|
qdict_put(snapshot_options, "file.driver",
|
||||||
qstring_from_str("file"));
|
qstring_from_str("file"));
|
||||||
qdict_put(snapshot_options, "file.filename",
|
qdict_put(snapshot_options, "file.filename",
|
||||||
@@ -1477,6 +1462,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
|||||||
|
|
||||||
ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
|
ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
|
||||||
flags, &local_err);
|
flags, &local_err);
|
||||||
|
snapshot_options = NULL;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
@@ -1485,6 +1471,7 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
|||||||
bdrv_append(bs_snapshot, bs);
|
bdrv_append(bs_snapshot, bs);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
QDECREF(snapshot_options);
|
||||||
g_free(tmp_filename);
|
g_free(tmp_filename);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1516,6 +1503,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
|||||||
const char *drvname;
|
const char *drvname;
|
||||||
const char *backing;
|
const char *backing;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
QDict *snapshot_options = NULL;
|
||||||
int snapshot_flags = 0;
|
int snapshot_flags = 0;
|
||||||
|
|
||||||
assert(pbs);
|
assert(pbs);
|
||||||
@@ -1607,7 +1595,9 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
|||||||
flags |= BDRV_O_ALLOW_RDWR;
|
flags |= BDRV_O_ALLOW_RDWR;
|
||||||
}
|
}
|
||||||
if (flags & BDRV_O_SNAPSHOT) {
|
if (flags & BDRV_O_SNAPSHOT) {
|
||||||
snapshot_flags = bdrv_temp_snapshot_flags(flags);
|
snapshot_options = qdict_new();
|
||||||
|
bdrv_temp_snapshot_options(&snapshot_flags, snapshot_options,
|
||||||
|
flags, options);
|
||||||
bdrv_backing_options(&flags, options, flags, options);
|
bdrv_backing_options(&flags, options, flags, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1709,7 +1699,9 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
|||||||
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
|
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
|
||||||
* temporary snapshot afterwards. */
|
* temporary snapshot afterwards. */
|
||||||
if (snapshot_flags) {
|
if (snapshot_flags) {
|
||||||
ret = bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err);
|
ret = bdrv_append_temp_snapshot(bs, snapshot_flags, snapshot_options,
|
||||||
|
&local_err);
|
||||||
|
snapshot_options = NULL;
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
goto close_and_fail;
|
goto close_and_fail;
|
||||||
}
|
}
|
||||||
@@ -1721,6 +1713,7 @@ fail:
|
|||||||
if (file != NULL) {
|
if (file != NULL) {
|
||||||
bdrv_unref_child(bs, file);
|
bdrv_unref_child(bs, file);
|
||||||
}
|
}
|
||||||
|
QDECREF(snapshot_options);
|
||||||
QDECREF(bs->explicit_options);
|
QDECREF(bs->explicit_options);
|
||||||
QDECREF(bs->options);
|
QDECREF(bs->options);
|
||||||
QDECREF(options);
|
QDECREF(options);
|
||||||
@@ -1743,6 +1736,7 @@ close_and_fail:
|
|||||||
} else {
|
} else {
|
||||||
bdrv_unref(bs);
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
|
QDECREF(snapshot_options);
|
||||||
QDECREF(options);
|
QDECREF(options);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
@@ -3431,346 +3425,6 @@ void bdrv_lock_medium(BlockDriverState *bs, bool locked)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
|
||||||
{
|
|
||||||
BdrvDirtyBitmap *bm;
|
|
||||||
|
|
||||||
assert(name);
|
|
||||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
|
||||||
if (bm->name && !strcmp(name, bm->name)) {
|
|
||||||
return bm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
|
||||||
g_free(bitmap->name);
|
|
||||||
bitmap->name = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
|
||||||
uint32_t granularity,
|
|
||||||
const char *name,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
int64_t bitmap_size;
|
|
||||||
BdrvDirtyBitmap *bitmap;
|
|
||||||
uint32_t sector_granularity;
|
|
||||||
|
|
||||||
assert((granularity & (granularity - 1)) == 0);
|
|
||||||
|
|
||||||
if (name && bdrv_find_dirty_bitmap(bs, name)) {
|
|
||||||
error_setg(errp, "Bitmap already exists: %s", name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
sector_granularity = granularity >> BDRV_SECTOR_BITS;
|
|
||||||
assert(sector_granularity);
|
|
||||||
bitmap_size = bdrv_nb_sectors(bs);
|
|
||||||
if (bitmap_size < 0) {
|
|
||||||
error_setg_errno(errp, -bitmap_size, "could not get length of device");
|
|
||||||
errno = -bitmap_size;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
|
||||||
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity));
|
|
||||||
bitmap->size = bitmap_size;
|
|
||||||
bitmap->name = g_strdup(name);
|
|
||||||
bitmap->disabled = false;
|
|
||||||
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
return bitmap->successor;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
return !(bitmap->disabled || bitmap->successor);
|
|
||||||
}
|
|
||||||
|
|
||||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
|
||||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
|
||||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
|
||||||
return DIRTY_BITMAP_STATUS_DISABLED;
|
|
||||||
} else {
|
|
||||||
return DIRTY_BITMAP_STATUS_ACTIVE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a successor bitmap destined to replace this bitmap after an operation.
|
|
||||||
* Requires that the bitmap is not frozen and has no successor.
|
|
||||||
*/
|
|
||||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
|
||||||
BdrvDirtyBitmap *bitmap, Error **errp)
|
|
||||||
{
|
|
||||||
uint64_t granularity;
|
|
||||||
BdrvDirtyBitmap *child;
|
|
||||||
|
|
||||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
|
||||||
error_setg(errp, "Cannot create a successor for a bitmap that is "
|
|
||||||
"currently frozen");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
assert(!bitmap->successor);
|
|
||||||
|
|
||||||
/* Create an anonymous successor */
|
|
||||||
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
|
||||||
child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
|
|
||||||
if (!child) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Successor will be on or off based on our current state. */
|
|
||||||
child->disabled = bitmap->disabled;
|
|
||||||
|
|
||||||
/* Install the successor and freeze the parent */
|
|
||||||
bitmap->successor = child;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For a bitmap with a successor, yield our name to the successor,
|
|
||||||
* delete the old bitmap, and return a handle to the new bitmap.
|
|
||||||
*/
|
|
||||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
|
||||||
BdrvDirtyBitmap *bitmap,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
char *name;
|
|
||||||
BdrvDirtyBitmap *successor = bitmap->successor;
|
|
||||||
|
|
||||||
if (successor == NULL) {
|
|
||||||
error_setg(errp, "Cannot relinquish control if "
|
|
||||||
"there's no successor present");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = bitmap->name;
|
|
||||||
bitmap->name = NULL;
|
|
||||||
successor->name = name;
|
|
||||||
bitmap->successor = NULL;
|
|
||||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
|
||||||
|
|
||||||
return successor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In cases of failure where we can no longer safely delete the parent,
|
|
||||||
* we may wish to re-join the parent and child/successor.
|
|
||||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
|
||||||
*/
|
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
|
||||||
BdrvDirtyBitmap *parent,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
BdrvDirtyBitmap *successor = parent->successor;
|
|
||||||
|
|
||||||
if (!successor) {
|
|
||||||
error_setg(errp, "Cannot reclaim a successor when none is present");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
|
|
||||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
bdrv_release_dirty_bitmap(bs, successor);
|
|
||||||
parent->successor = NULL;
|
|
||||||
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Truncates _all_ bitmaps attached to a BDS.
|
|
||||||
*/
|
|
||||||
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
BdrvDirtyBitmap *bitmap;
|
|
||||||
uint64_t size = bdrv_nb_sectors(bs);
|
|
||||||
|
|
||||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
|
||||||
hbitmap_truncate(bitmap->bitmap, size);
|
|
||||||
bitmap->size = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
|
||||||
BdrvDirtyBitmap *bitmap,
|
|
||||||
bool only_named)
|
|
||||||
{
|
|
||||||
BdrvDirtyBitmap *bm, *next;
|
|
||||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
|
||||||
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
|
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bm));
|
|
||||||
QLIST_REMOVE(bm, list);
|
|
||||||
hbitmap_free(bm->bitmap);
|
|
||||||
g_free(bm->name);
|
|
||||||
g_free(bm);
|
|
||||||
|
|
||||||
if (bitmap) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
|
||||||
* There must not be any frozen bitmaps attached.
|
|
||||||
*/
|
|
||||||
static void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
|
||||||
bitmap->disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
|
||||||
bitmap->disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
BdrvDirtyBitmap *bm;
|
|
||||||
BlockDirtyInfoList *list = NULL;
|
|
||||||
BlockDirtyInfoList **plist = &list;
|
|
||||||
|
|
||||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
|
||||||
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
|
|
||||||
BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
|
|
||||||
info->count = bdrv_get_dirty_count(bm);
|
|
||||||
info->granularity = bdrv_dirty_bitmap_granularity(bm);
|
|
||||||
info->has_name = !!bm->name;
|
|
||||||
info->name = g_strdup(bm->name);
|
|
||||||
info->status = bdrv_dirty_bitmap_status(bm);
|
|
||||||
entry->value = info;
|
|
||||||
*plist = entry;
|
|
||||||
plist = &entry->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t sector)
|
|
||||||
{
|
|
||||||
if (bitmap) {
|
|
||||||
return hbitmap_get(bitmap->bitmap, sector);
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chooses a default granularity based on the existing cluster size,
|
|
||||||
* but clamped between [4K, 64K]. Defaults to 64K in the case that there
|
|
||||||
* is no cluster size information available.
|
|
||||||
*/
|
|
||||||
uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
BlockDriverInfo bdi;
|
|
||||||
uint32_t granularity;
|
|
||||||
|
|
||||||
if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) {
|
|
||||||
granularity = MAX(4096, bdi.cluster_size);
|
|
||||||
granularity = MIN(65536, granularity);
|
|
||||||
} else {
|
|
||||||
granularity = 65536;
|
|
||||||
}
|
|
||||||
|
|
||||||
return granularity;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi)
|
|
||||||
{
|
|
||||||
hbitmap_iter_init(hbi, bitmap->bitmap, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
|
||||||
int64_t cur_sector, int nr_sectors)
|
|
||||||
{
|
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
|
||||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
|
||||||
int64_t cur_sector, int nr_sectors)
|
|
||||||
{
|
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
|
||||||
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
|
||||||
{
|
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
|
||||||
if (!out) {
|
|
||||||
hbitmap_reset_all(bitmap->bitmap);
|
|
||||||
} else {
|
|
||||||
HBitmap *backup = bitmap->bitmap;
|
|
||||||
bitmap->bitmap = hbitmap_alloc(bitmap->size,
|
|
||||||
hbitmap_granularity(backup));
|
|
||||||
*out = backup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
|
||||||
{
|
|
||||||
HBitmap *tmp = bitmap->bitmap;
|
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
|
||||||
bitmap->bitmap = in;
|
|
||||||
hbitmap_free(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
|
||||||
int nr_sectors)
|
|
||||||
{
|
|
||||||
BdrvDirtyBitmap *bitmap;
|
|
||||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
|
||||||
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Advance an HBitmapIter to an arbitrary offset.
|
|
||||||
*/
|
|
||||||
void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset)
|
|
||||||
{
|
|
||||||
assert(hbi->hb);
|
|
||||||
hbitmap_iter_init(hbi, hbi->hb, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
return hbitmap_count(bitmap->bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get a reference to bs */
|
/* Get a reference to bs */
|
||||||
void bdrv_ref(BlockDriverState *bs)
|
void bdrv_ref(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ block-obj-$(CONFIG_RBD) += rbd.o
|
|||||||
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
||||||
block-obj-$(CONFIG_ARCHIPELAGO) += archipelago.o
|
block-obj-$(CONFIG_ARCHIPELAGO) += archipelago.o
|
||||||
block-obj-$(CONFIG_LIBSSH2) += ssh.o
|
block-obj-$(CONFIG_LIBSSH2) += ssh.o
|
||||||
block-obj-y += accounting.o
|
block-obj-y += accounting.o dirty-bitmap.o
|
||||||
block-obj-y += write-threshold.o
|
block-obj-y += write-threshold.o
|
||||||
|
|
||||||
common-obj-y += stream.o
|
common-obj-y += stream.o
|
||||||
|
|||||||
@@ -20,11 +20,9 @@
|
|||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qemu/ratelimit.h"
|
#include "qemu/ratelimit.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
|
#include "qemu/bitmap.h"
|
||||||
|
|
||||||
#define BACKUP_CLUSTER_BITS 16
|
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||||
#define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS)
|
|
||||||
#define BACKUP_SECTORS_PER_CLUSTER (BACKUP_CLUSTER_SIZE / BDRV_SECTOR_SIZE)
|
|
||||||
|
|
||||||
#define SLICE_TIME 100000000ULL /* ns */
|
#define SLICE_TIME 100000000ULL /* ns */
|
||||||
|
|
||||||
typedef struct CowRequest {
|
typedef struct CowRequest {
|
||||||
@@ -45,10 +43,17 @@ typedef struct BackupBlockJob {
|
|||||||
BlockdevOnError on_target_error;
|
BlockdevOnError on_target_error;
|
||||||
CoRwlock flush_rwlock;
|
CoRwlock flush_rwlock;
|
||||||
uint64_t sectors_read;
|
uint64_t sectors_read;
|
||||||
HBitmap *bitmap;
|
unsigned long *done_bitmap;
|
||||||
|
int64_t cluster_size;
|
||||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||||
} BackupBlockJob;
|
} BackupBlockJob;
|
||||||
|
|
||||||
|
/* Size of a cluster in sectors, instead of bytes. */
|
||||||
|
static inline int64_t cluster_size_sectors(BackupBlockJob *job)
|
||||||
|
{
|
||||||
|
return job->cluster_size / BDRV_SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
/* See if in-flight requests overlap and wait for them to complete */
|
/* See if in-flight requests overlap and wait for them to complete */
|
||||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||||
int64_t start,
|
int64_t start,
|
||||||
@@ -97,13 +102,14 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
QEMUIOVector bounce_qiov;
|
QEMUIOVector bounce_qiov;
|
||||||
void *bounce_buffer = NULL;
|
void *bounce_buffer = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
int64_t start, end;
|
int64_t start, end;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
||||||
|
|
||||||
start = sector_num / BACKUP_SECTORS_PER_CLUSTER;
|
start = sector_num / sectors_per_cluster;
|
||||||
end = DIV_ROUND_UP(sector_num + nb_sectors, BACKUP_SECTORS_PER_CLUSTER);
|
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster);
|
||||||
|
|
||||||
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
|
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
|
||||||
|
|
||||||
@@ -111,19 +117,19 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
cow_request_begin(&cow_request, job, start, end);
|
cow_request_begin(&cow_request, job, start, end);
|
||||||
|
|
||||||
for (; start < end; start++) {
|
for (; start < end; start++) {
|
||||||
if (hbitmap_get(job->bitmap, start)) {
|
if (test_bit(start, job->done_bitmap)) {
|
||||||
trace_backup_do_cow_skip(job, start);
|
trace_backup_do_cow_skip(job, start);
|
||||||
continue; /* already copied */
|
continue; /* already copied */
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_backup_do_cow_process(job, start);
|
trace_backup_do_cow_process(job, start);
|
||||||
|
|
||||||
n = MIN(BACKUP_SECTORS_PER_CLUSTER,
|
n = MIN(sectors_per_cluster,
|
||||||
job->common.len / BDRV_SECTOR_SIZE -
|
job->common.len / BDRV_SECTOR_SIZE -
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER);
|
start * sectors_per_cluster);
|
||||||
|
|
||||||
if (!bounce_buffer) {
|
if (!bounce_buffer) {
|
||||||
bounce_buffer = qemu_blockalign(bs, BACKUP_CLUSTER_SIZE);
|
bounce_buffer = qemu_blockalign(bs, job->cluster_size);
|
||||||
}
|
}
|
||||||
iov.iov_base = bounce_buffer;
|
iov.iov_base = bounce_buffer;
|
||||||
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
||||||
@@ -131,10 +137,10 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (is_write_notifier) {
|
if (is_write_notifier) {
|
||||||
ret = bdrv_co_readv_no_serialising(bs,
|
ret = bdrv_co_readv_no_serialising(bs,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
start * sectors_per_cluster,
|
||||||
n, &bounce_qiov);
|
n, &bounce_qiov);
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n,
|
ret = bdrv_co_readv(bs, start * sectors_per_cluster, n,
|
||||||
&bounce_qiov);
|
&bounce_qiov);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -147,11 +153,11 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||||
ret = bdrv_co_write_zeroes(job->target,
|
ret = bdrv_co_write_zeroes(job->target,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
start * sectors_per_cluster,
|
||||||
n, BDRV_REQ_MAY_UNMAP);
|
n, BDRV_REQ_MAY_UNMAP);
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_co_writev(job->target,
|
ret = bdrv_co_writev(job->target,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER, n,
|
start * sectors_per_cluster, n,
|
||||||
&bounce_qiov);
|
&bounce_qiov);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -162,7 +168,7 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
hbitmap_set(job->bitmap, start, 1);
|
set_bit(start, job->done_bitmap);
|
||||||
|
|
||||||
/* Publish progress, guest I/O counts as progress too. Note that the
|
/* Publish progress, guest I/O counts as progress too. Note that the
|
||||||
* offset field is an opaque progress value, it is not a disk offset.
|
* offset field is an opaque progress value, it is not a disk offset.
|
||||||
@@ -322,21 +328,22 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
int64_t cluster;
|
int64_t cluster;
|
||||||
int64_t end;
|
int64_t end;
|
||||||
int64_t last_cluster = -1;
|
int64_t last_cluster = -1;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
BlockDriverState *bs = job->common.bs;
|
BlockDriverState *bs = job->common.bs;
|
||||||
HBitmapIter hbi;
|
HBitmapIter hbi;
|
||||||
|
|
||||||
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||||
clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1);
|
clusters_per_iter = MAX((granularity / job->cluster_size), 1);
|
||||||
bdrv_dirty_iter_init(job->sync_bitmap, &hbi);
|
bdrv_dirty_iter_init(job->sync_bitmap, &hbi);
|
||||||
|
|
||||||
/* Find the next dirty sector(s) */
|
/* Find the next dirty sector(s) */
|
||||||
while ((sector = hbitmap_iter_next(&hbi)) != -1) {
|
while ((sector = hbitmap_iter_next(&hbi)) != -1) {
|
||||||
cluster = sector / BACKUP_SECTORS_PER_CLUSTER;
|
cluster = sector / sectors_per_cluster;
|
||||||
|
|
||||||
/* Fake progress updates for any clusters we skipped */
|
/* Fake progress updates for any clusters we skipped */
|
||||||
if (cluster != last_cluster + 1) {
|
if (cluster != last_cluster + 1) {
|
||||||
job->common.offset += ((cluster - last_cluster - 1) *
|
job->common.offset += ((cluster - last_cluster - 1) *
|
||||||
BACKUP_CLUSTER_SIZE);
|
job->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
||||||
@@ -344,8 +351,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
if (yield_and_check(job)) {
|
if (yield_and_check(job)) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER,
|
ret = backup_do_cow(bs, cluster * sectors_per_cluster,
|
||||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read,
|
sectors_per_cluster, &error_is_read,
|
||||||
false);
|
false);
|
||||||
if ((ret < 0) &&
|
if ((ret < 0) &&
|
||||||
backup_error_action(job, error_is_read, -ret) ==
|
backup_error_action(job, error_is_read, -ret) ==
|
||||||
@@ -357,17 +364,17 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
|
|
||||||
/* If the bitmap granularity is smaller than the backup granularity,
|
/* If the bitmap granularity is smaller than the backup granularity,
|
||||||
* we need to advance the iterator pointer to the next cluster. */
|
* we need to advance the iterator pointer to the next cluster. */
|
||||||
if (granularity < BACKUP_CLUSTER_SIZE) {
|
if (granularity < job->cluster_size) {
|
||||||
bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER);
|
bdrv_set_dirty_iter(&hbi, cluster * sectors_per_cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_cluster = cluster - 1;
|
last_cluster = cluster - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Play some final catchup with the progress meter */
|
/* Play some final catchup with the progress meter */
|
||||||
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
|
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||||
if (last_cluster + 1 < end) {
|
if (last_cluster + 1 < end) {
|
||||||
job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE);
|
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -384,15 +391,16 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
.notify = backup_before_write_notify,
|
.notify = backup_before_write_notify,
|
||||||
};
|
};
|
||||||
int64_t start, end;
|
int64_t start, end;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
QLIST_INIT(&job->inflight_reqs);
|
QLIST_INIT(&job->inflight_reqs);
|
||||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||||
|
|
||||||
start = 0;
|
start = 0;
|
||||||
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
|
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||||
|
|
||||||
job->bitmap = hbitmap_alloc(end, 0);
|
job->done_bitmap = bitmap_new(end);
|
||||||
|
|
||||||
bdrv_set_enable_write_cache(target, true);
|
bdrv_set_enable_write_cache(target, true);
|
||||||
if (target->blk) {
|
if (target->blk) {
|
||||||
@@ -427,7 +435,7 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
/* Check to see if these blocks are already in the
|
/* Check to see if these blocks are already in the
|
||||||
* backing file. */
|
* backing file. */
|
||||||
|
|
||||||
for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) {
|
for (i = 0; i < sectors_per_cluster;) {
|
||||||
/* bdrv_is_allocated() only returns true/false based
|
/* bdrv_is_allocated() only returns true/false based
|
||||||
* on the first set of sectors it comes across that
|
* on the first set of sectors it comes across that
|
||||||
* are are all in the same state.
|
* are are all in the same state.
|
||||||
@@ -436,8 +444,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
* needed but at some point that is always the case. */
|
* needed but at some point that is always the case. */
|
||||||
alloced =
|
alloced =
|
||||||
bdrv_is_allocated(bs,
|
bdrv_is_allocated(bs,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER + i,
|
start * sectors_per_cluster + i,
|
||||||
BACKUP_SECTORS_PER_CLUSTER - i, &n);
|
sectors_per_cluster - i, &n);
|
||||||
i += n;
|
i += n;
|
||||||
|
|
||||||
if (alloced == 1 || n == 0) {
|
if (alloced == 1 || n == 0) {
|
||||||
@@ -452,8 +460,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* FULL sync mode we copy the whole drive. */
|
/* FULL sync mode we copy the whole drive. */
|
||||||
ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER,
|
ret = backup_do_cow(bs, start * sectors_per_cluster,
|
||||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read, false);
|
sectors_per_cluster, &error_is_read, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Depending on error action, fail now or retry cluster */
|
/* Depending on error action, fail now or retry cluster */
|
||||||
BlockErrorAction action =
|
BlockErrorAction action =
|
||||||
@@ -473,7 +481,7 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
/* wait until pending backup_do_cow() calls have completed */
|
/* wait until pending backup_do_cow() calls have completed */
|
||||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||||
hbitmap_free(job->bitmap);
|
g_free(job->done_bitmap);
|
||||||
|
|
||||||
if (target->blk) {
|
if (target->blk) {
|
||||||
blk_iostatus_disable(target->blk);
|
blk_iostatus_disable(target->blk);
|
||||||
@@ -494,6 +502,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
|||||||
BlockJobTxn *txn, Error **errp)
|
BlockJobTxn *txn, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t len;
|
int64_t len;
|
||||||
|
BlockDriverInfo bdi;
|
||||||
|
int ret;
|
||||||
|
|
||||||
assert(bs);
|
assert(bs);
|
||||||
assert(target);
|
assert(target);
|
||||||
@@ -563,14 +573,32 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_op_block_all(target, job->common.blocker);
|
|
||||||
|
|
||||||
job->on_source_error = on_source_error;
|
job->on_source_error = on_source_error;
|
||||||
job->on_target_error = on_target_error;
|
job->on_target_error = on_target_error;
|
||||||
job->target = target;
|
job->target = target;
|
||||||
job->sync_mode = sync_mode;
|
job->sync_mode = sync_mode;
|
||||||
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
|
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
|
||||||
sync_bitmap : NULL;
|
sync_bitmap : NULL;
|
||||||
|
|
||||||
|
/* If there is no backing file on the target, we cannot rely on COW if our
|
||||||
|
* backup cluster size is smaller than the target cluster size. Even for
|
||||||
|
* targets with a backing file, try to avoid COW if possible. */
|
||||||
|
ret = bdrv_get_info(job->target, &bdi);
|
||||||
|
if (ret < 0 && !target->backing) {
|
||||||
|
error_setg_errno(errp, -ret,
|
||||||
|
"Couldn't determine the cluster size of the target image, "
|
||||||
|
"which has no backing file");
|
||||||
|
error_append_hint(errp,
|
||||||
|
"Aborting, since this may create an unusable destination image\n");
|
||||||
|
goto error;
|
||||||
|
} else if (ret < 0 && target->backing) {
|
||||||
|
/* Not fatal; just trudge on ahead. */
|
||||||
|
job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||||
|
} else {
|
||||||
|
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_op_block_all(target, job->common.blocker);
|
||||||
job->common.len = len;
|
job->common.len = len;
|
||||||
job->common.co = qemu_coroutine_create(backup_run);
|
job->common.co = qemu_coroutine_create(backup_run);
|
||||||
block_job_txn_add_job(txn, &job->common);
|
block_job_txn_add_job(txn, &job->common);
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ struct BlockBackend {
|
|||||||
bool iostatus_enabled;
|
bool iostatus_enabled;
|
||||||
BlockDeviceIoStatus iostatus;
|
BlockDeviceIoStatus iostatus;
|
||||||
|
|
||||||
|
bool allow_write_beyond_eof;
|
||||||
|
|
||||||
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -579,6 +581,11 @@ void blk_iostatus_set_err(BlockBackend *blk, int error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow)
|
||||||
|
{
|
||||||
|
blk->allow_write_beyond_eof = allow;
|
||||||
|
}
|
||||||
|
|
||||||
static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
@@ -592,18 +599,20 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
|||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (offset < 0) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blk->allow_write_beyond_eof) {
|
||||||
len = blk_getlength(blk);
|
len = blk_getlength(blk);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset < 0) {
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset > len || len - offset < size) {
|
if (offset > len || len - offset < size) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
66
block/curl.c
66
block/curl.c
@@ -27,6 +27,7 @@
|
|||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qapi/qmp/qbool.h"
|
#include "qapi/qmp/qbool.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
|
#include "crypto/secret.h"
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
// #define DEBUG_CURL
|
// #define DEBUG_CURL
|
||||||
@@ -78,6 +79,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
|||||||
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
||||||
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
|
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
|
||||||
#define CURL_BLOCK_OPT_COOKIE "cookie"
|
#define CURL_BLOCK_OPT_COOKIE "cookie"
|
||||||
|
#define CURL_BLOCK_OPT_USERNAME "username"
|
||||||
|
#define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret"
|
||||||
|
#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
|
||||||
|
#define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
|
||||||
|
|
||||||
struct BDRVCURLState;
|
struct BDRVCURLState;
|
||||||
|
|
||||||
@@ -120,6 +125,10 @@ typedef struct BDRVCURLState {
|
|||||||
char *cookie;
|
char *cookie;
|
||||||
bool accept_range;
|
bool accept_range;
|
||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
|
char *username;
|
||||||
|
char *password;
|
||||||
|
char *proxyusername;
|
||||||
|
char *proxypassword;
|
||||||
} BDRVCURLState;
|
} BDRVCURLState;
|
||||||
|
|
||||||
static void curl_clean_state(CURLState *s);
|
static void curl_clean_state(CURLState *s);
|
||||||
@@ -419,6 +428,21 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
|
|||||||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
||||||
|
|
||||||
|
if (s->username) {
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username);
|
||||||
|
}
|
||||||
|
if (s->password) {
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password);
|
||||||
|
}
|
||||||
|
if (s->proxyusername) {
|
||||||
|
curl_easy_setopt(state->curl,
|
||||||
|
CURLOPT_PROXYUSERNAME, s->proxyusername);
|
||||||
|
}
|
||||||
|
if (s->proxypassword) {
|
||||||
|
curl_easy_setopt(state->curl,
|
||||||
|
CURLOPT_PROXYPASSWORD, s->proxypassword);
|
||||||
|
}
|
||||||
|
|
||||||
/* Restrict supported protocols to avoid security issues in the more
|
/* Restrict supported protocols to avoid security issues in the more
|
||||||
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
|
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
|
||||||
* CVE-2013-0249.
|
* CVE-2013-0249.
|
||||||
@@ -525,10 +549,31 @@ static QemuOptsList runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "Pass the cookie or list of cookies with each request"
|
.help = "Pass the cookie or list of cookies with each request"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_USERNAME,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "Username for HTTP auth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_PASSWORD_SECRET,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret used as password for HTTP auth",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_PROXY_USERNAME,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "Username for HTTP proxy auth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret used as password for HTTP proxy auth",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@@ -539,6 +584,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
const char *file;
|
const char *file;
|
||||||
const char *cookie;
|
const char *cookie;
|
||||||
double d;
|
double d;
|
||||||
|
const char *secretid;
|
||||||
|
|
||||||
static int inited = 0;
|
static int inited = 0;
|
||||||
|
|
||||||
@@ -580,6 +626,26 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME));
|
||||||
|
secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORD_SECRET);
|
||||||
|
|
||||||
|
if (secretid) {
|
||||||
|
s->password = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||||
|
if (!s->password) {
|
||||||
|
goto out_noclean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s->proxyusername = g_strdup(
|
||||||
|
qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_USERNAME));
|
||||||
|
secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET);
|
||||||
|
if (secretid) {
|
||||||
|
s->proxypassword = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||||
|
if (!s->proxypassword) {
|
||||||
|
goto out_noclean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!inited) {
|
if (!inited) {
|
||||||
curl_global_init(CURL_GLOBAL_ALL);
|
curl_global_init(CURL_GLOBAL_ALL);
|
||||||
inited = 1;
|
inited = 1;
|
||||||
|
|||||||
387
block/dirty-bitmap.c
Normal file
387
block/dirty-bitmap.c
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
/*
|
||||||
|
* Block Dirty Bitmap
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Red Hat. Inc
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "config-host.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "block/block_int.h"
|
||||||
|
#include "block/blockjob.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A BdrvDirtyBitmap can be in three possible states:
|
||||||
|
* (1) successor is NULL and disabled is false: full r/w mode
|
||||||
|
* (2) successor is NULL and disabled is true: read only mode ("disabled")
|
||||||
|
* (3) successor is set: frozen mode.
|
||||||
|
* A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set,
|
||||||
|
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
||||||
|
*/
|
||||||
|
struct BdrvDirtyBitmap {
|
||||||
|
HBitmap *bitmap; /* Dirty sector bitmap implementation */
|
||||||
|
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||||
|
char *name; /* Optional non-empty unique ID */
|
||||||
|
int64_t size; /* Size of the bitmap (Number of sectors) */
|
||||||
|
bool disabled; /* Bitmap is read-only */
|
||||||
|
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
||||||
|
};
|
||||||
|
|
||||||
|
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
||||||
|
{
|
||||||
|
BdrvDirtyBitmap *bm;
|
||||||
|
|
||||||
|
assert(name);
|
||||||
|
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||||
|
if (bm->name && !strcmp(name, bm->name)) {
|
||||||
|
return bm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
|
g_free(bitmap->name);
|
||||||
|
bitmap->name = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||||
|
uint32_t granularity,
|
||||||
|
const char *name,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int64_t bitmap_size;
|
||||||
|
BdrvDirtyBitmap *bitmap;
|
||||||
|
uint32_t sector_granularity;
|
||||||
|
|
||||||
|
assert((granularity & (granularity - 1)) == 0);
|
||||||
|
|
||||||
|
if (name && bdrv_find_dirty_bitmap(bs, name)) {
|
||||||
|
error_setg(errp, "Bitmap already exists: %s", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
sector_granularity = granularity >> BDRV_SECTOR_BITS;
|
||||||
|
assert(sector_granularity);
|
||||||
|
bitmap_size = bdrv_nb_sectors(bs);
|
||||||
|
if (bitmap_size < 0) {
|
||||||
|
error_setg_errno(errp, -bitmap_size, "could not get length of device");
|
||||||
|
errno = -bitmap_size;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
||||||
|
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(sector_granularity));
|
||||||
|
bitmap->size = bitmap_size;
|
||||||
|
bitmap->name = g_strdup(name);
|
||||||
|
bitmap->disabled = false;
|
||||||
|
QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
return bitmap->successor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
return !(bitmap->disabled || bitmap->successor);
|
||||||
|
}
|
||||||
|
|
||||||
|
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||||
|
return DIRTY_BITMAP_STATUS_FROZEN;
|
||||||
|
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||||
|
return DIRTY_BITMAP_STATUS_DISABLED;
|
||||||
|
} else {
|
||||||
|
return DIRTY_BITMAP_STATUS_ACTIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a successor bitmap destined to replace this bitmap after an operation.
|
||||||
|
* Requires that the bitmap is not frozen and has no successor.
|
||||||
|
*/
|
||||||
|
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||||
|
BdrvDirtyBitmap *bitmap, Error **errp)
|
||||||
|
{
|
||||||
|
uint64_t granularity;
|
||||||
|
BdrvDirtyBitmap *child;
|
||||||
|
|
||||||
|
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||||
|
error_setg(errp, "Cannot create a successor for a bitmap that is "
|
||||||
|
"currently frozen");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
assert(!bitmap->successor);
|
||||||
|
|
||||||
|
/* Create an anonymous successor */
|
||||||
|
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||||
|
child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
|
||||||
|
if (!child) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Successor will be on or off based on our current state. */
|
||||||
|
child->disabled = bitmap->disabled;
|
||||||
|
|
||||||
|
/* Install the successor and freeze the parent */
|
||||||
|
bitmap->successor = child;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a bitmap with a successor, yield our name to the successor,
|
||||||
|
* delete the old bitmap, and return a handle to the new bitmap.
|
||||||
|
*/
|
||||||
|
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||||
|
BdrvDirtyBitmap *bitmap,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
BdrvDirtyBitmap *successor = bitmap->successor;
|
||||||
|
|
||||||
|
if (successor == NULL) {
|
||||||
|
error_setg(errp, "Cannot relinquish control if "
|
||||||
|
"there's no successor present");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = bitmap->name;
|
||||||
|
bitmap->name = NULL;
|
||||||
|
successor->name = name;
|
||||||
|
bitmap->successor = NULL;
|
||||||
|
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||||
|
|
||||||
|
return successor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In cases of failure where we can no longer safely delete the parent,
|
||||||
|
* we may wish to re-join the parent and child/successor.
|
||||||
|
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
||||||
|
*/
|
||||||
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||||
|
BdrvDirtyBitmap *parent,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
BdrvDirtyBitmap *successor = parent->successor;
|
||||||
|
|
||||||
|
if (!successor) {
|
||||||
|
error_setg(errp, "Cannot reclaim a successor when none is present");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
|
||||||
|
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bdrv_release_dirty_bitmap(bs, successor);
|
||||||
|
parent->successor = NULL;
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncates _all_ bitmaps attached to a BDS.
|
||||||
|
*/
|
||||||
|
void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BdrvDirtyBitmap *bitmap;
|
||||||
|
uint64_t size = bdrv_nb_sectors(bs);
|
||||||
|
|
||||||
|
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||||
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
|
hbitmap_truncate(bitmap->bitmap, size);
|
||||||
|
bitmap->size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs,
|
||||||
|
BdrvDirtyBitmap *bitmap,
|
||||||
|
bool only_named)
|
||||||
|
{
|
||||||
|
BdrvDirtyBitmap *bm, *next;
|
||||||
|
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||||
|
if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) {
|
||||||
|
assert(!bdrv_dirty_bitmap_frozen(bm));
|
||||||
|
QLIST_REMOVE(bm, list);
|
||||||
|
hbitmap_free(bm->bitmap);
|
||||||
|
g_free(bm->name);
|
||||||
|
g_free(bm);
|
||||||
|
|
||||||
|
if (bitmap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
||||||
|
* There must not be any frozen bitmaps attached.
|
||||||
|
*/
|
||||||
|
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
bdrv_do_release_matching_dirty_bitmap(bs, NULL, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
|
bitmap->disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||||
|
bitmap->disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BdrvDirtyBitmap *bm;
|
||||||
|
BlockDirtyInfoList *list = NULL;
|
||||||
|
BlockDirtyInfoList **plist = &list;
|
||||||
|
|
||||||
|
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||||
|
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
|
||||||
|
BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
|
||||||
|
info->count = bdrv_get_dirty_count(bm);
|
||||||
|
info->granularity = bdrv_dirty_bitmap_granularity(bm);
|
||||||
|
info->has_name = !!bm->name;
|
||||||
|
info->name = g_strdup(bm->name);
|
||||||
|
info->status = bdrv_dirty_bitmap_status(bm);
|
||||||
|
entry->value = info;
|
||||||
|
*plist = entry;
|
||||||
|
plist = &entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||||
|
int64_t sector)
|
||||||
|
{
|
||||||
|
if (bitmap) {
|
||||||
|
return hbitmap_get(bitmap->bitmap, sector);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chooses a default granularity based on the existing cluster size,
|
||||||
|
* but clamped between [4K, 64K]. Defaults to 64K in the case that there
|
||||||
|
* is no cluster size information available.
|
||||||
|
*/
|
||||||
|
uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BlockDriverInfo bdi;
|
||||||
|
uint32_t granularity;
|
||||||
|
|
||||||
|
if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) {
|
||||||
|
granularity = MAX(4096, bdi.cluster_size);
|
||||||
|
granularity = MIN(65536, granularity);
|
||||||
|
} else {
|
||||||
|
granularity = 65536;
|
||||||
|
}
|
||||||
|
|
||||||
|
return granularity;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi)
|
||||||
|
{
|
||||||
|
hbitmap_iter_init(hbi, bitmap->bitmap, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
|
int64_t cur_sector, int nr_sectors)
|
||||||
|
{
|
||||||
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
|
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
|
int64_t cur_sector, int nr_sectors)
|
||||||
|
{
|
||||||
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
|
hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||||
|
{
|
||||||
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
|
if (!out) {
|
||||||
|
hbitmap_reset_all(bitmap->bitmap);
|
||||||
|
} else {
|
||||||
|
HBitmap *backup = bitmap->bitmap;
|
||||||
|
bitmap->bitmap = hbitmap_alloc(bitmap->size,
|
||||||
|
hbitmap_granularity(backup));
|
||||||
|
*out = backup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||||
|
{
|
||||||
|
HBitmap *tmp = bitmap->bitmap;
|
||||||
|
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||||
|
bitmap->bitmap = in;
|
||||||
|
hbitmap_free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||||
|
int nr_sectors)
|
||||||
|
{
|
||||||
|
BdrvDirtyBitmap *bitmap;
|
||||||
|
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||||
|
if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hbitmap_set(bitmap->bitmap, cur_sector, nr_sectors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advance an HBitmapIter to an arbitrary offset.
|
||||||
|
*/
|
||||||
|
void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset)
|
||||||
|
{
|
||||||
|
assert(hbi->hb);
|
||||||
|
hbitmap_iter_init(hbi, hbi->hb, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
return hbitmap_count(bitmap->bitmap);
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
|
#include "crypto/secret.h"
|
||||||
|
|
||||||
#include <iscsi/iscsi.h>
|
#include <iscsi/iscsi.h>
|
||||||
#include <iscsi/scsi-lowlevel.h>
|
#include <iscsi/scsi-lowlevel.h>
|
||||||
@@ -1080,6 +1081,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
|||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
const char *user = NULL;
|
const char *user = NULL;
|
||||||
const char *password = NULL;
|
const char *password = NULL;
|
||||||
|
const char *secretid;
|
||||||
|
char *secret = NULL;
|
||||||
|
|
||||||
list = qemu_find_opts("iscsi");
|
list = qemu_find_opts("iscsi");
|
||||||
if (!list) {
|
if (!list) {
|
||||||
@@ -1099,8 +1102,20 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secretid = qemu_opt_get(opts, "password-secret");
|
||||||
password = qemu_opt_get(opts, "password");
|
password = qemu_opt_get(opts, "password");
|
||||||
if (!password) {
|
if (secretid && password) {
|
||||||
|
error_setg(errp, "'password' and 'password-secret' properties are "
|
||||||
|
"mutually exclusive");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (secretid) {
|
||||||
|
secret = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||||
|
if (!secret) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
password = secret;
|
||||||
|
} else if (!password) {
|
||||||
error_setg(errp, "CHAP username specified but no password was given");
|
error_setg(errp, "CHAP username specified but no password was given");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1108,6 +1123,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
|||||||
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
||||||
error_setg(errp, "Failed to set initiator username and password");
|
error_setg(errp, "Failed to set initiator username and password");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free(secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
|
static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
|
||||||
@@ -1857,6 +1874,11 @@ static QemuOptsList qemu_iscsi_opts = {
|
|||||||
.name = "password",
|
.name = "password",
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "password for CHAP authentication to target",
|
.help = "password for CHAP authentication to target",
|
||||||
|
},{
|
||||||
|
.name = "password-secret",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of the secret providing password for CHAP "
|
||||||
|
"authentication to target",
|
||||||
},{
|
},{
|
||||||
.name = "header-digest",
|
.name = "header-digest",
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
|
|||||||
343
block/mirror.c
343
block/mirror.c
@@ -47,7 +47,6 @@ typedef struct MirrorBlockJob {
|
|||||||
BlockdevOnError on_source_error, on_target_error;
|
BlockdevOnError on_source_error, on_target_error;
|
||||||
bool synced;
|
bool synced;
|
||||||
bool should_complete;
|
bool should_complete;
|
||||||
int64_t sector_num;
|
|
||||||
int64_t granularity;
|
int64_t granularity;
|
||||||
size_t buf_size;
|
size_t buf_size;
|
||||||
int64_t bdev_length;
|
int64_t bdev_length;
|
||||||
@@ -64,6 +63,8 @@ typedef struct MirrorBlockJob {
|
|||||||
int ret;
|
int ret;
|
||||||
bool unmap;
|
bool unmap;
|
||||||
bool waiting_for_io;
|
bool waiting_for_io;
|
||||||
|
int target_cluster_sectors;
|
||||||
|
int max_iov;
|
||||||
} MirrorBlockJob;
|
} MirrorBlockJob;
|
||||||
|
|
||||||
typedef struct MirrorOp {
|
typedef struct MirrorOp {
|
||||||
@@ -159,116 +160,85 @@ static void mirror_read_complete(void *opaque, int ret)
|
|||||||
mirror_write_complete, op);
|
mirror_write_complete, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
/* Round sector_num and/or nb_sectors to target cluster if COW is needed, and
|
||||||
|
* return the offset of the adjusted tail sector against original. */
|
||||||
|
static int mirror_cow_align(MirrorBlockJob *s,
|
||||||
|
int64_t *sector_num,
|
||||||
|
int *nb_sectors)
|
||||||
|
{
|
||||||
|
bool need_cow;
|
||||||
|
int ret = 0;
|
||||||
|
int chunk_sectors = s->granularity >> BDRV_SECTOR_BITS;
|
||||||
|
int64_t align_sector_num = *sector_num;
|
||||||
|
int align_nb_sectors = *nb_sectors;
|
||||||
|
int max_sectors = chunk_sectors * s->max_iov;
|
||||||
|
|
||||||
|
need_cow = !test_bit(*sector_num / chunk_sectors, s->cow_bitmap);
|
||||||
|
need_cow |= !test_bit((*sector_num + *nb_sectors - 1) / chunk_sectors,
|
||||||
|
s->cow_bitmap);
|
||||||
|
if (need_cow) {
|
||||||
|
bdrv_round_to_clusters(s->target, *sector_num, *nb_sectors,
|
||||||
|
&align_sector_num, &align_nb_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (align_nb_sectors > max_sectors) {
|
||||||
|
align_nb_sectors = max_sectors;
|
||||||
|
if (need_cow) {
|
||||||
|
align_nb_sectors = QEMU_ALIGN_DOWN(align_nb_sectors,
|
||||||
|
s->target_cluster_sectors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = align_sector_num + align_nb_sectors - (*sector_num + *nb_sectors);
|
||||||
|
*sector_num = align_sector_num;
|
||||||
|
*nb_sectors = align_nb_sectors;
|
||||||
|
assert(ret >= 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mirror_wait_for_io(MirrorBlockJob *s)
|
||||||
|
{
|
||||||
|
assert(!s->waiting_for_io);
|
||||||
|
s->waiting_for_io = true;
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
s->waiting_for_io = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Submit async read while handling COW.
|
||||||
|
* Returns: nb_sectors if no alignment is necessary, or
|
||||||
|
* (new_end - sector_num) if tail is rounded up or down due to
|
||||||
|
* alignment or buffer limit.
|
||||||
|
*/
|
||||||
|
static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
||||||
|
int nb_sectors)
|
||||||
{
|
{
|
||||||
BlockDriverState *source = s->common.bs;
|
BlockDriverState *source = s->common.bs;
|
||||||
int nb_sectors, sectors_per_chunk, nb_chunks, max_iov;
|
int sectors_per_chunk, nb_chunks;
|
||||||
int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector;
|
int ret = nb_sectors;
|
||||||
uint64_t delay_ns = 0;
|
|
||||||
MirrorOp *op;
|
MirrorOp *op;
|
||||||
int pnum;
|
|
||||||
int64_t ret;
|
|
||||||
BlockDriverState *file;
|
|
||||||
|
|
||||||
max_iov = MIN(source->bl.max_iov, s->target->bl.max_iov);
|
|
||||||
|
|
||||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
|
||||||
if (s->sector_num < 0) {
|
|
||||||
bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
|
|
||||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
|
||||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
|
||||||
assert(s->sector_num >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
hbitmap_next_sector = s->sector_num;
|
|
||||||
sector_num = s->sector_num;
|
|
||||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||||
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
|
||||||
|
|
||||||
/* Extend the QEMUIOVector to include all adjacent blocks that will
|
/* We can only handle as much as buf_size at a time. */
|
||||||
* be copied in this operation.
|
nb_sectors = MIN(s->buf_size >> BDRV_SECTOR_BITS, nb_sectors);
|
||||||
*
|
assert(nb_sectors);
|
||||||
* We have to do this if we have no backing file yet in the destination,
|
|
||||||
* and the cluster size is very large. Then we need to do COW ourselves.
|
|
||||||
* The first time a cluster is copied, copy it entirely. Note that,
|
|
||||||
* because both the granularity and the cluster size are powers of two,
|
|
||||||
* the number of sectors to copy cannot exceed one cluster.
|
|
||||||
*
|
|
||||||
* We also want to extend the QEMUIOVector to include more adjacent
|
|
||||||
* dirty blocks if possible, to limit the number of I/O operations and
|
|
||||||
* run efficiently even with a small granularity.
|
|
||||||
*/
|
|
||||||
nb_chunks = 0;
|
|
||||||
nb_sectors = 0;
|
|
||||||
next_sector = sector_num;
|
|
||||||
next_chunk = sector_num / sectors_per_chunk;
|
|
||||||
|
|
||||||
/* Wait for I/O to this cluster (from a previous iteration) to be done. */
|
if (s->cow_bitmap) {
|
||||||
while (test_bit(next_chunk, s->in_flight_bitmap)) {
|
ret += mirror_cow_align(s, §or_num, &nb_sectors);
|
||||||
|
}
|
||||||
|
assert(nb_sectors << BDRV_SECTOR_BITS <= s->buf_size);
|
||||||
|
/* The sector range must meet granularity because:
|
||||||
|
* 1) Caller passes in aligned values;
|
||||||
|
* 2) mirror_cow_align is used only when target cluster is larger. */
|
||||||
|
assert(!(nb_sectors % sectors_per_chunk));
|
||||||
|
assert(!(sector_num % sectors_per_chunk));
|
||||||
|
nb_chunks = nb_sectors / sectors_per_chunk;
|
||||||
|
|
||||||
|
while (s->buf_free_count < nb_chunks) {
|
||||||
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
||||||
s->waiting_for_io = true;
|
mirror_wait_for_io(s);
|
||||||
qemu_coroutine_yield();
|
|
||||||
s->waiting_for_io = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
|
||||||
int added_sectors, added_chunks;
|
|
||||||
|
|
||||||
if (!bdrv_get_dirty(source, s->dirty_bitmap, next_sector) ||
|
|
||||||
test_bit(next_chunk, s->in_flight_bitmap)) {
|
|
||||||
assert(nb_sectors > 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
added_sectors = sectors_per_chunk;
|
|
||||||
if (s->cow_bitmap && !test_bit(next_chunk, s->cow_bitmap)) {
|
|
||||||
bdrv_round_to_clusters(s->target,
|
|
||||||
next_sector, added_sectors,
|
|
||||||
&next_sector, &added_sectors);
|
|
||||||
|
|
||||||
/* On the first iteration, the rounding may make us copy
|
|
||||||
* sectors before the first dirty one.
|
|
||||||
*/
|
|
||||||
if (next_sector < sector_num) {
|
|
||||||
assert(nb_sectors == 0);
|
|
||||||
sector_num = next_sector;
|
|
||||||
next_chunk = next_sector / sectors_per_chunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
added_sectors = MIN(added_sectors, end - (sector_num + nb_sectors));
|
|
||||||
added_chunks = (added_sectors + sectors_per_chunk - 1) / sectors_per_chunk;
|
|
||||||
|
|
||||||
/* When doing COW, it may happen that there is not enough space for
|
|
||||||
* a full cluster. Wait if that is the case.
|
|
||||||
*/
|
|
||||||
while (nb_chunks == 0 && s->buf_free_count < added_chunks) {
|
|
||||||
trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight);
|
|
||||||
s->waiting_for_io = true;
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
s->waiting_for_io = false;
|
|
||||||
}
|
|
||||||
if (s->buf_free_count < nb_chunks + added_chunks) {
|
|
||||||
trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (max_iov < nb_chunks + added_chunks) {
|
|
||||||
trace_mirror_break_iov_max(s, nb_chunks, added_chunks);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We have enough free space to copy these sectors. */
|
|
||||||
bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks);
|
|
||||||
|
|
||||||
nb_sectors += added_sectors;
|
|
||||||
nb_chunks += added_chunks;
|
|
||||||
next_sector += added_sectors;
|
|
||||||
next_chunk += added_chunks;
|
|
||||||
if (!s->synced && s->common.speed) {
|
|
||||||
delay_ns = ratelimit_calculate_delay(&s->limit, added_sectors);
|
|
||||||
}
|
|
||||||
} while (delay_ns == 0 && next_sector < end);
|
|
||||||
|
|
||||||
/* Allocate a MirrorOp that is used as an AIO callback. */
|
/* Allocate a MirrorOp that is used as an AIO callback. */
|
||||||
op = g_new(MirrorOp, 1);
|
op = g_new(MirrorOp, 1);
|
||||||
op->s = s;
|
op->s = s;
|
||||||
@@ -279,47 +249,151 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||||||
* from s->buf_free.
|
* from s->buf_free.
|
||||||
*/
|
*/
|
||||||
qemu_iovec_init(&op->qiov, nb_chunks);
|
qemu_iovec_init(&op->qiov, nb_chunks);
|
||||||
next_sector = sector_num;
|
|
||||||
while (nb_chunks-- > 0) {
|
while (nb_chunks-- > 0) {
|
||||||
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
|
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
|
||||||
size_t remaining = (nb_sectors * BDRV_SECTOR_SIZE) - op->qiov.size;
|
size_t remaining = nb_sectors * BDRV_SECTOR_SIZE - op->qiov.size;
|
||||||
|
|
||||||
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
|
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
|
||||||
s->buf_free_count--;
|
s->buf_free_count--;
|
||||||
qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining));
|
qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining));
|
||||||
|
|
||||||
/* Advance the HBitmapIter in parallel, so that we do not examine
|
|
||||||
* the same sector twice.
|
|
||||||
*/
|
|
||||||
if (next_sector > hbitmap_next_sector
|
|
||||||
&& bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
|
||||||
hbitmap_next_sector = hbitmap_iter_next(&s->hbi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
next_sector += sectors_per_chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num, nb_sectors);
|
|
||||||
|
|
||||||
/* Copy the dirty cluster. */
|
/* Copy the dirty cluster. */
|
||||||
s->in_flight++;
|
s->in_flight++;
|
||||||
s->sectors_in_flight += nb_sectors;
|
s->sectors_in_flight += nb_sectors;
|
||||||
trace_mirror_one_iteration(s, sector_num, nb_sectors);
|
trace_mirror_one_iteration(s, sector_num, nb_sectors);
|
||||||
|
|
||||||
ret = bdrv_get_block_status_above(source, NULL, sector_num,
|
|
||||||
nb_sectors, &pnum, &file);
|
|
||||||
if (ret < 0 || pnum < nb_sectors ||
|
|
||||||
(ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) {
|
|
||||||
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
||||||
mirror_read_complete, op);
|
mirror_read_complete, op);
|
||||||
} else if (ret & BDRV_BLOCK_ZERO) {
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mirror_do_zero_or_discard(MirrorBlockJob *s,
|
||||||
|
int64_t sector_num,
|
||||||
|
int nb_sectors,
|
||||||
|
bool is_discard)
|
||||||
|
{
|
||||||
|
MirrorOp *op;
|
||||||
|
|
||||||
|
/* Allocate a MirrorOp that is used as an AIO callback. The qiov is zeroed
|
||||||
|
* so the freeing in mirror_iteration_done is nop. */
|
||||||
|
op = g_new0(MirrorOp, 1);
|
||||||
|
op->s = s;
|
||||||
|
op->sector_num = sector_num;
|
||||||
|
op->nb_sectors = nb_sectors;
|
||||||
|
|
||||||
|
s->in_flight++;
|
||||||
|
s->sectors_in_flight += nb_sectors;
|
||||||
|
if (is_discard) {
|
||||||
|
bdrv_aio_discard(s->target, sector_num, op->nb_sectors,
|
||||||
|
mirror_write_complete, op);
|
||||||
|
} else {
|
||||||
bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors,
|
bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors,
|
||||||
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
||||||
mirror_write_complete, op);
|
mirror_write_complete, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
|
{
|
||||||
|
BlockDriverState *source = s->common.bs;
|
||||||
|
int64_t sector_num;
|
||||||
|
uint64_t delay_ns = 0;
|
||||||
|
/* At least the first dirty chunk is mirrored in one iteration. */
|
||||||
|
int nb_chunks = 1;
|
||||||
|
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||||
|
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||||
|
|
||||||
|
sector_num = hbitmap_iter_next(&s->hbi);
|
||||||
|
if (sector_num < 0) {
|
||||||
|
bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
|
||||||
|
sector_num = hbitmap_iter_next(&s->hbi);
|
||||||
|
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
||||||
|
assert(sector_num >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the number of consective dirty chunks following the first dirty
|
||||||
|
* one, and wait for in flight requests in them. */
|
||||||
|
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
||||||
|
int64_t hbitmap_next;
|
||||||
|
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
|
||||||
|
int64_t next_chunk = next_sector / sectors_per_chunk;
|
||||||
|
if (next_sector >= end ||
|
||||||
|
!bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||||
|
if (nb_chunks > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
trace_mirror_yield_in_flight(s, next_sector, s->in_flight);
|
||||||
|
mirror_wait_for_io(s);
|
||||||
|
/* Now retry. */
|
||||||
} else {
|
} else {
|
||||||
assert(!(ret & BDRV_BLOCK_DATA));
|
hbitmap_next = hbitmap_iter_next(&s->hbi);
|
||||||
bdrv_aio_discard(s->target, sector_num, op->nb_sectors,
|
assert(hbitmap_next == next_sector);
|
||||||
mirror_write_complete, op);
|
nb_chunks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear dirty bits before querying the block status, because
|
||||||
|
* calling bdrv_get_block_status_above could yield - if some blocks are
|
||||||
|
* marked dirty in this window, we need to know.
|
||||||
|
*/
|
||||||
|
bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num,
|
||||||
|
nb_chunks * sectors_per_chunk);
|
||||||
|
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
|
||||||
|
while (nb_chunks > 0 && sector_num < end) {
|
||||||
|
int ret;
|
||||||
|
int io_sectors;
|
||||||
|
BlockDriverState *file;
|
||||||
|
enum MirrorMethod {
|
||||||
|
MIRROR_METHOD_COPY,
|
||||||
|
MIRROR_METHOD_ZERO,
|
||||||
|
MIRROR_METHOD_DISCARD
|
||||||
|
} mirror_method = MIRROR_METHOD_COPY;
|
||||||
|
|
||||||
|
assert(!(sector_num % sectors_per_chunk));
|
||||||
|
ret = bdrv_get_block_status_above(source, NULL, sector_num,
|
||||||
|
nb_chunks * sectors_per_chunk,
|
||||||
|
&io_sectors, &file);
|
||||||
|
if (ret < 0) {
|
||||||
|
io_sectors = nb_chunks * sectors_per_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_sectors -= io_sectors % sectors_per_chunk;
|
||||||
|
if (io_sectors < sectors_per_chunk) {
|
||||||
|
io_sectors = sectors_per_chunk;
|
||||||
|
} else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) {
|
||||||
|
int64_t target_sector_num;
|
||||||
|
int target_nb_sectors;
|
||||||
|
bdrv_round_to_clusters(s->target, sector_num, io_sectors,
|
||||||
|
&target_sector_num, &target_nb_sectors);
|
||||||
|
if (target_sector_num == sector_num &&
|
||||||
|
target_nb_sectors == io_sectors) {
|
||||||
|
mirror_method = ret & BDRV_BLOCK_ZERO ?
|
||||||
|
MIRROR_METHOD_ZERO :
|
||||||
|
MIRROR_METHOD_DISCARD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mirror_method) {
|
||||||
|
case MIRROR_METHOD_COPY:
|
||||||
|
io_sectors = mirror_do_read(s, sector_num, io_sectors);
|
||||||
|
break;
|
||||||
|
case MIRROR_METHOD_ZERO:
|
||||||
|
mirror_do_zero_or_discard(s, sector_num, io_sectors, false);
|
||||||
|
break;
|
||||||
|
case MIRROR_METHOD_DISCARD:
|
||||||
|
mirror_do_zero_or_discard(s, sector_num, io_sectors, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
assert(io_sectors);
|
||||||
|
sector_num += io_sectors;
|
||||||
|
nb_chunks -= io_sectors / sectors_per_chunk;
|
||||||
|
delay_ns += ratelimit_calculate_delay(&s->limit, io_sectors);
|
||||||
}
|
}
|
||||||
return delay_ns;
|
return delay_ns;
|
||||||
}
|
}
|
||||||
@@ -344,9 +418,7 @@ static void mirror_free_init(MirrorBlockJob *s)
|
|||||||
static void mirror_drain(MirrorBlockJob *s)
|
static void mirror_drain(MirrorBlockJob *s)
|
||||||
{
|
{
|
||||||
while (s->in_flight > 0) {
|
while (s->in_flight > 0) {
|
||||||
s->waiting_for_io = true;
|
mirror_wait_for_io(s);
|
||||||
qemu_coroutine_yield();
|
|
||||||
s->waiting_for_io = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,6 +492,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
checking for a NULL string */
|
checking for a NULL string */
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int n;
|
int n;
|
||||||
|
int target_cluster_size = BDRV_SECTOR_SIZE;
|
||||||
|
|
||||||
if (block_job_is_cancelled(&s->common)) {
|
if (block_job_is_cancelled(&s->common)) {
|
||||||
goto immediate_exit;
|
goto immediate_exit;
|
||||||
@@ -449,16 +522,16 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
*/
|
*/
|
||||||
bdrv_get_backing_filename(s->target, backing_filename,
|
bdrv_get_backing_filename(s->target, backing_filename,
|
||||||
sizeof(backing_filename));
|
sizeof(backing_filename));
|
||||||
if (backing_filename[0] && !s->target->backing) {
|
if (!bdrv_get_info(s->target, &bdi) && bdi.cluster_size) {
|
||||||
ret = bdrv_get_info(s->target, &bdi);
|
target_cluster_size = bdi.cluster_size;
|
||||||
if (ret < 0) {
|
|
||||||
goto immediate_exit;
|
|
||||||
}
|
}
|
||||||
if (s->granularity < bdi.cluster_size) {
|
if (backing_filename[0] && !s->target->backing
|
||||||
s->buf_size = MAX(s->buf_size, bdi.cluster_size);
|
&& s->granularity < target_cluster_size) {
|
||||||
|
s->buf_size = MAX(s->buf_size, target_cluster_size);
|
||||||
s->cow_bitmap = bitmap_new(length);
|
s->cow_bitmap = bitmap_new(length);
|
||||||
}
|
}
|
||||||
}
|
s->target_cluster_sectors = target_cluster_size >> BDRV_SECTOR_BITS;
|
||||||
|
s->max_iov = MIN(s->common.bs->bl.max_iov, s->target->bl.max_iov);
|
||||||
|
|
||||||
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||||
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
||||||
@@ -533,9 +606,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
||||||
(cnt == 0 && s->in_flight > 0)) {
|
(cnt == 0 && s->in_flight > 0)) {
|
||||||
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
|
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
|
||||||
s->waiting_for_io = true;
|
mirror_wait_for_io(s);
|
||||||
qemu_coroutine_yield();
|
|
||||||
s->waiting_for_io = false;
|
|
||||||
continue;
|
continue;
|
||||||
} else if (cnt != 0) {
|
} else if (cnt != 0) {
|
||||||
delay_ns = mirror_iteration(s);
|
delay_ns = mirror_iteration(s);
|
||||||
|
|||||||
14
block/nbd.c
14
block/nbd.c
@@ -204,18 +204,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
|||||||
saddr = g_new0(SocketAddress, 1);
|
saddr = g_new0(SocketAddress, 1);
|
||||||
|
|
||||||
if (qdict_haskey(options, "path")) {
|
if (qdict_haskey(options, "path")) {
|
||||||
|
UnixSocketAddress *q_unix;
|
||||||
saddr->type = SOCKET_ADDRESS_KIND_UNIX;
|
saddr->type = SOCKET_ADDRESS_KIND_UNIX;
|
||||||
saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
|
q_unix = saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
|
||||||
saddr->u.q_unix->path = g_strdup(qdict_get_str(options, "path"));
|
q_unix->path = g_strdup(qdict_get_str(options, "path"));
|
||||||
qdict_del(options, "path");
|
qdict_del(options, "path");
|
||||||
} else {
|
} else {
|
||||||
|
InetSocketAddress *inet;
|
||||||
saddr->type = SOCKET_ADDRESS_KIND_INET;
|
saddr->type = SOCKET_ADDRESS_KIND_INET;
|
||||||
saddr->u.inet = g_new0(InetSocketAddress, 1);
|
inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
|
||||||
saddr->u.inet->host = g_strdup(qdict_get_str(options, "host"));
|
inet->host = g_strdup(qdict_get_str(options, "host"));
|
||||||
if (!qdict_get_try_str(options, "port")) {
|
if (!qdict_get_try_str(options, "port")) {
|
||||||
saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
|
inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
|
||||||
} else {
|
} else {
|
||||||
saddr->u.inet->port = g_strdup(qdict_get_str(options, "port"));
|
inet->port = g_strdup(qdict_get_str(options, "port"));
|
||||||
}
|
}
|
||||||
qdict_del(options, "host");
|
qdict_del(options, "host");
|
||||||
qdict_del(options, "port");
|
qdict_del(options, "port");
|
||||||
|
|||||||
12
block/nfs.c
12
block/nfs.c
@@ -36,6 +36,7 @@
|
|||||||
#include <nfsc/libnfs.h>
|
#include <nfsc/libnfs.h>
|
||||||
|
|
||||||
#define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
|
#define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
|
||||||
|
#define QEMU_NFS_MAX_DEBUG_LEVEL 2
|
||||||
|
|
||||||
typedef struct NFSClient {
|
typedef struct NFSClient {
|
||||||
struct nfs_context *context;
|
struct nfs_context *context;
|
||||||
@@ -333,6 +334,17 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
|||||||
val = QEMU_NFS_MAX_READAHEAD_SIZE;
|
val = QEMU_NFS_MAX_READAHEAD_SIZE;
|
||||||
}
|
}
|
||||||
nfs_set_readahead(client->context, val);
|
nfs_set_readahead(client->context, val);
|
||||||
|
#endif
|
||||||
|
#ifdef LIBNFS_FEATURE_DEBUG
|
||||||
|
} else if (!strcmp(qp->p[i].name, "debug")) {
|
||||||
|
/* limit the maximum debug level to avoid potential flooding
|
||||||
|
* of our log files. */
|
||||||
|
if (val > QEMU_NFS_MAX_DEBUG_LEVEL) {
|
||||||
|
error_report("NFS Warning: Limiting NFS debug level"
|
||||||
|
" to %d", QEMU_NFS_MAX_DEBUG_LEVEL);
|
||||||
|
val = QEMU_NFS_MAX_DEBUG_LEVEL;
|
||||||
|
}
|
||||||
|
nfs_set_debug(client->context, val);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Unknown NFS parameter name: %s",
|
error_setg(errp, "Unknown NFS parameter name: %s",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/bitmap.h"
|
#include "qemu/bitmap.h"
|
||||||
#include "qapi/util.h"
|
#include "qapi/util.h"
|
||||||
@@ -461,7 +462,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
int64_t total_size, cl_size;
|
int64_t total_size, cl_size;
|
||||||
uint8_t tmp[BDRV_SECTOR_SIZE];
|
uint8_t tmp[BDRV_SECTOR_SIZE];
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
BlockDriverState *file;
|
BlockBackend *file;
|
||||||
uint32_t bat_entries, bat_sectors;
|
uint32_t bat_entries, bat_sectors;
|
||||||
ParallelsHeader header;
|
ParallelsHeader header;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -477,14 +478,17 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
file = NULL;
|
file = blk_new_open("image", filename, NULL, NULL,
|
||||||
ret = bdrv_open(&file, filename, NULL, NULL,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (file == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return ret;
|
return -EIO;
|
||||||
}
|
}
|
||||||
ret = bdrv_truncate(file, 0);
|
|
||||||
|
blk_set_allow_write_beyond_eof(file, true);
|
||||||
|
|
||||||
|
ret = blk_truncate(file, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -508,18 +512,18 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
memset(tmp, 0, sizeof(tmp));
|
memset(tmp, 0, sizeof(tmp));
|
||||||
memcpy(tmp, &header, sizeof(header));
|
memcpy(tmp, &header, sizeof(header));
|
||||||
|
|
||||||
ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE);
|
ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0);
|
ret = blk_write_zeroes(file, 1, bat_sectors - 1, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
bdrv_unref(file);
|
blk_unref(file);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
|||||||
80
block/qapi.c
80
block/qapi.c
@@ -355,28 +355,18 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
|||||||
qapi_free_BlockInfo(info);
|
qapi_free_BlockInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
|
static BlockStats *bdrv_query_stats(BlockBackend *blk,
|
||||||
bool query_backing)
|
const BlockDriverState *bs,
|
||||||
|
bool query_backing);
|
||||||
|
|
||||||
|
static void bdrv_query_blk_stats(BlockStats *s, BlockBackend *blk)
|
||||||
{
|
{
|
||||||
BlockStats *s;
|
BlockAcctStats *stats = blk_get_stats(blk);
|
||||||
|
|
||||||
s = g_malloc0(sizeof(*s));
|
|
||||||
|
|
||||||
if (bdrv_get_device_name(bs)[0]) {
|
|
||||||
s->has_device = true;
|
|
||||||
s->device = g_strdup(bdrv_get_device_name(bs));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bdrv_get_node_name(bs)[0]) {
|
|
||||||
s->has_node_name = true;
|
|
||||||
s->node_name = g_strdup(bdrv_get_node_name(bs));
|
|
||||||
}
|
|
||||||
|
|
||||||
s->stats = g_malloc0(sizeof(*s->stats));
|
|
||||||
if (bs->blk) {
|
|
||||||
BlockAcctStats *stats = blk_get_stats(bs->blk);
|
|
||||||
BlockAcctTimedStats *ts = NULL;
|
BlockAcctTimedStats *ts = NULL;
|
||||||
|
|
||||||
|
s->has_device = true;
|
||||||
|
s->device = g_strdup(blk_name(blk));
|
||||||
|
|
||||||
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
|
||||||
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
|
||||||
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
|
||||||
@@ -439,16 +429,42 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bdrv_query_bds_stats(BlockStats *s, const BlockDriverState *bs,
|
||||||
|
bool query_backing)
|
||||||
|
{
|
||||||
|
if (bdrv_get_node_name(bs)[0]) {
|
||||||
|
s->has_node_name = true;
|
||||||
|
s->node_name = g_strdup(bdrv_get_node_name(bs));
|
||||||
|
}
|
||||||
|
|
||||||
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
s->stats->wr_highest_offset = bs->wr_highest_offset;
|
||||||
|
|
||||||
if (bs->file) {
|
if (bs->file) {
|
||||||
s->has_parent = true;
|
s->has_parent = true;
|
||||||
s->parent = bdrv_query_stats(bs->file->bs, query_backing);
|
s->parent = bdrv_query_stats(NULL, bs->file->bs, query_backing);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query_backing && bs->backing) {
|
if (query_backing && bs->backing) {
|
||||||
s->has_backing = true;
|
s->has_backing = true;
|
||||||
s->backing = bdrv_query_stats(bs->backing->bs, query_backing);
|
s->backing = bdrv_query_stats(NULL, bs->backing->bs, query_backing);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockStats *bdrv_query_stats(BlockBackend *blk,
|
||||||
|
const BlockDriverState *bs,
|
||||||
|
bool query_backing)
|
||||||
|
{
|
||||||
|
BlockStats *s;
|
||||||
|
|
||||||
|
s = g_malloc0(sizeof(*s));
|
||||||
|
s->stats = g_malloc0(sizeof(*s->stats));
|
||||||
|
|
||||||
|
if (blk) {
|
||||||
|
bdrv_query_blk_stats(s, blk);
|
||||||
|
}
|
||||||
|
if (bs) {
|
||||||
|
bdrv_query_bds_stats(s, bs, query_backing);
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@@ -477,22 +493,38 @@ BlockInfoList *qmp_query_block(Error **errp)
|
|||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool next_query_bds(BlockBackend **blk, BlockDriverState **bs,
|
||||||
|
bool query_nodes)
|
||||||
|
{
|
||||||
|
if (query_nodes) {
|
||||||
|
*bs = bdrv_next_node(*bs);
|
||||||
|
return !!*bs;
|
||||||
|
}
|
||||||
|
|
||||||
|
*blk = blk_next(*blk);
|
||||||
|
*bs = *blk ? blk_bs(*blk) : NULL;
|
||||||
|
|
||||||
|
return !!*blk;
|
||||||
|
}
|
||||||
|
|
||||||
BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||||
bool query_nodes,
|
bool query_nodes,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockStatsList *head = NULL, **p_next = &head;
|
BlockStatsList *head = NULL, **p_next = &head;
|
||||||
|
BlockBackend *blk = NULL;
|
||||||
BlockDriverState *bs = NULL;
|
BlockDriverState *bs = NULL;
|
||||||
|
|
||||||
/* Just to be safe if query_nodes is not always initialized */
|
/* Just to be safe if query_nodes is not always initialized */
|
||||||
query_nodes = has_query_nodes && query_nodes;
|
query_nodes = has_query_nodes && query_nodes;
|
||||||
|
|
||||||
while ((bs = query_nodes ? bdrv_next_node(bs) : bdrv_next(bs))) {
|
while (next_query_bds(&blk, &bs, query_nodes)) {
|
||||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
AioContext *ctx = blk ? blk_get_aio_context(blk)
|
||||||
|
: bdrv_get_aio_context(bs);
|
||||||
|
|
||||||
aio_context_acquire(ctx);
|
aio_context_acquire(ctx);
|
||||||
info->value = bdrv_query_stats(bs, !query_nodes);
|
info->value = bdrv_query_stats(blk, bs, !query_nodes);
|
||||||
aio_context_release(ctx);
|
aio_context_release(ctx);
|
||||||
|
|
||||||
*p_next = info;
|
*p_next = info;
|
||||||
|
|||||||
24
block/qcow.c
24
block/qcow.c
@@ -24,6 +24,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
@@ -780,7 +781,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
int flags = 0;
|
int flags = 0;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
BlockDriverState *qcow_bs;
|
BlockBackend *qcow_blk;
|
||||||
|
|
||||||
/* Read out options */
|
/* Read out options */
|
||||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||||
@@ -796,15 +797,18 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
qcow_bs = NULL;
|
qcow_blk = blk_new_open("image", filename, NULL, NULL,
|
||||||
ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (qcow_blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_truncate(qcow_bs, 0);
|
blk_set_allow_write_beyond_eof(qcow_blk, true);
|
||||||
|
|
||||||
|
ret = blk_truncate(qcow_blk, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -844,13 +848,13 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* write all the data */
|
/* write all the data */
|
||||||
ret = bdrv_pwrite(qcow_bs, 0, &header, sizeof(header));
|
ret = blk_pwrite(qcow_blk, 0, &header, sizeof(header));
|
||||||
if (ret != sizeof(header)) {
|
if (ret != sizeof(header)) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backing_file) {
|
if (backing_file) {
|
||||||
ret = bdrv_pwrite(qcow_bs, sizeof(header),
|
ret = blk_pwrite(qcow_blk, sizeof(header),
|
||||||
backing_file, backing_filename_len);
|
backing_file, backing_filename_len);
|
||||||
if (ret != backing_filename_len) {
|
if (ret != backing_filename_len) {
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -860,7 +864,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
tmp = g_malloc0(BDRV_SECTOR_SIZE);
|
tmp = g_malloc0(BDRV_SECTOR_SIZE);
|
||||||
for (i = 0; i < ((sizeof(uint64_t)*l1_size + BDRV_SECTOR_SIZE - 1)/
|
for (i = 0; i < ((sizeof(uint64_t)*l1_size + BDRV_SECTOR_SIZE - 1)/
|
||||||
BDRV_SECTOR_SIZE); i++) {
|
BDRV_SECTOR_SIZE); i++) {
|
||||||
ret = bdrv_pwrite(qcow_bs, header_size +
|
ret = blk_pwrite(qcow_blk, header_size +
|
||||||
BDRV_SECTOR_SIZE*i, tmp, BDRV_SECTOR_SIZE);
|
BDRV_SECTOR_SIZE*i, tmp, BDRV_SECTOR_SIZE);
|
||||||
if (ret != BDRV_SECTOR_SIZE) {
|
if (ret != BDRV_SECTOR_SIZE) {
|
||||||
g_free(tmp);
|
g_free(tmp);
|
||||||
@@ -871,7 +875,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
g_free(tmp);
|
g_free(tmp);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
exit:
|
exit:
|
||||||
bdrv_unref(qcow_bs);
|
blk_unref(qcow_blk);
|
||||||
cleanup:
|
cleanup:
|
||||||
g_free(backing_file);
|
g_free(backing_file);
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "block/qcow2.h"
|
#include "block/qcow2.h"
|
||||||
@@ -2097,7 +2098,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
|
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
|
||||||
* size for any qcow2 image.
|
* size for any qcow2 image.
|
||||||
*/
|
*/
|
||||||
BlockDriverState* bs;
|
BlockBackend *blk;
|
||||||
QCowHeader *header;
|
QCowHeader *header;
|
||||||
uint64_t* refcount_table;
|
uint64_t* refcount_table;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
@@ -2172,14 +2173,16 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = NULL;
|
blk = blk_new_open("image", filename, NULL, NULL,
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return ret;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
/* Write the header */
|
/* Write the header */
|
||||||
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
|
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
|
||||||
header = g_malloc0(cluster_size);
|
header = g_malloc0(cluster_size);
|
||||||
@@ -2207,7 +2210,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
|
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_pwrite(bs, 0, header, cluster_size);
|
ret = blk_pwrite(blk, 0, header, cluster_size);
|
||||||
g_free(header);
|
g_free(header);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not write qcow2 header");
|
error_setg_errno(errp, -ret, "Could not write qcow2 header");
|
||||||
@@ -2217,7 +2220,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
/* Write a refcount table with one refcount block */
|
/* Write a refcount table with one refcount block */
|
||||||
refcount_table = g_malloc0(2 * cluster_size);
|
refcount_table = g_malloc0(2 * cluster_size);
|
||||||
refcount_table[0] = cpu_to_be64(2 * cluster_size);
|
refcount_table[0] = cpu_to_be64(2 * cluster_size);
|
||||||
ret = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size);
|
ret = blk_pwrite(blk, cluster_size, refcount_table, 2 * cluster_size);
|
||||||
g_free(refcount_table);
|
g_free(refcount_table);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -2225,8 +2228,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
bs = NULL;
|
blk = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And now open the image and make it consistent first (i.e. increase the
|
* And now open the image and make it consistent first (i.e. increase the
|
||||||
@@ -2235,15 +2238,16 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
*/
|
*/
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
||||||
ret = bdrv_open(&bs, filename, NULL, options,
|
blk = blk_new_open("image-qcow2", filename, NULL, options,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qcow2_alloc_clusters(bs, 3 * cluster_size);
|
ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 "
|
error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 "
|
||||||
"header and refcount table");
|
"header and refcount table");
|
||||||
@@ -2255,14 +2259,14 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create a full header (including things like feature table) */
|
/* Create a full header (including things like feature table) */
|
||||||
ret = qcow2_update_header(bs);
|
ret = qcow2_update_header(blk_bs(blk));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not update qcow2 header");
|
error_setg_errno(errp, -ret, "Could not update qcow2 header");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Okay, now that we have a valid image, let's give it the right size */
|
/* Okay, now that we have a valid image, let's give it the right size */
|
||||||
ret = bdrv_truncate(bs, total_size);
|
ret = blk_truncate(blk, total_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not resize image");
|
error_setg_errno(errp, -ret, "Could not resize image");
|
||||||
goto out;
|
goto out;
|
||||||
@@ -2270,7 +2274,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
|
|
||||||
/* Want a backing file? There you go.*/
|
/* Want a backing file? There you go.*/
|
||||||
if (backing_file) {
|
if (backing_file) {
|
||||||
ret = bdrv_change_backing_file(bs, backing_file, backing_format);
|
ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
|
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
|
||||||
"with format '%s'", backing_file, backing_format);
|
"with format '%s'", backing_file, backing_format);
|
||||||
@@ -2280,9 +2284,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
|
|
||||||
/* And if we're supposed to preallocate metadata, do that now */
|
/* And if we're supposed to preallocate metadata, do that now */
|
||||||
if (prealloc != PREALLOC_MODE_OFF) {
|
if (prealloc != PREALLOC_MODE_OFF) {
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = blk_bs(blk)->opaque;
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
ret = preallocate(bs);
|
ret = preallocate(blk_bs(blk));
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not preallocate metadata");
|
error_setg_errno(errp, -ret, "Could not preallocate metadata");
|
||||||
@@ -2290,24 +2294,25 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
bs = NULL;
|
blk = NULL;
|
||||||
|
|
||||||
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
||||||
ret = bdrv_open(&bs, filename, NULL, options,
|
blk = blk_new_open("image-flush", filename, NULL, options,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (local_err) {
|
if (blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
if (bs) {
|
if (blk) {
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
22
block/qed.c
22
block/qed.c
@@ -18,6 +18,7 @@
|
|||||||
#include "qed.h"
|
#include "qed.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/migration.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
|
|
||||||
static const AIOCBInfo qed_aiocb_info = {
|
static const AIOCBInfo qed_aiocb_info = {
|
||||||
.aiocb_size = sizeof(QEDAIOCB),
|
.aiocb_size = sizeof(QEDAIOCB),
|
||||||
@@ -580,7 +581,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
|||||||
size_t l1_size = header.cluster_size * header.table_size;
|
size_t l1_size = header.cluster_size * header.table_size;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
BlockDriverState *bs;
|
BlockBackend *blk;
|
||||||
|
|
||||||
ret = bdrv_create_file(filename, opts, &local_err);
|
ret = bdrv_create_file(filename, opts, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -588,17 +589,18 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = NULL;
|
blk = blk_new_open("image", filename, NULL, NULL,
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return ret;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
/* File must start empty and grow, check truncate is supported */
|
/* File must start empty and grow, check truncate is supported */
|
||||||
ret = bdrv_truncate(bs, 0);
|
ret = blk_truncate(blk, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -614,18 +616,18 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
qed_header_cpu_to_le(&header, &le_header);
|
qed_header_cpu_to_le(&header, &le_header);
|
||||||
ret = bdrv_pwrite(bs, 0, &le_header, sizeof(le_header));
|
ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite(bs, sizeof(le_header), backing_file,
|
ret = blk_pwrite(blk, sizeof(le_header), backing_file,
|
||||||
header.backing_filename_size);
|
header.backing_filename_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
l1_table = g_malloc0(l1_size);
|
l1_table = g_malloc0(l1_size);
|
||||||
ret = bdrv_pwrite(bs, header.l1_table_offset, l1_table, l1_size);
|
ret = blk_pwrite(blk, header.l1_table_offset, l1_table, l1_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -633,7 +635,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
|||||||
ret = 0; /* success */
|
ret = 0; /* success */
|
||||||
out:
|
out:
|
||||||
g_free(l1_table);
|
g_free(l1_table);
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -215,14 +215,16 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
|||||||
return acb;
|
return acb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
|
static void quorum_report_bad(QuorumOpType type, uint64_t sector_num,
|
||||||
|
int nb_sectors, char *node_name, int ret)
|
||||||
{
|
{
|
||||||
const char *msg = NULL;
|
const char *msg = NULL;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
msg = strerror(-ret);
|
msg = strerror(-ret);
|
||||||
}
|
}
|
||||||
qapi_event_send_quorum_report_bad(!!msg, msg, node_name,
|
|
||||||
acb->sector_num, acb->nb_sectors, &error_abort);
|
qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name,
|
||||||
|
sector_num, nb_sectors, &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void quorum_report_failure(QuorumAIOCB *acb)
|
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||||
@@ -282,6 +284,7 @@ static void quorum_aio_cb(void *opaque, int ret)
|
|||||||
QuorumChildRequest *sacb = opaque;
|
QuorumChildRequest *sacb = opaque;
|
||||||
QuorumAIOCB *acb = sacb->parent;
|
QuorumAIOCB *acb = sacb->parent;
|
||||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||||
|
QuorumOpType type;
|
||||||
bool rewrite = false;
|
bool rewrite = false;
|
||||||
|
|
||||||
if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) {
|
if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) {
|
||||||
@@ -300,12 +303,14 @@ static void quorum_aio_cb(void *opaque, int ret)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE;
|
||||||
sacb->ret = ret;
|
sacb->ret = ret;
|
||||||
acb->count++;
|
acb->count++;
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
acb->success_count++;
|
acb->success_count++;
|
||||||
} else {
|
} else {
|
||||||
quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret);
|
quorum_report_bad(type, acb->sector_num, acb->nb_sectors,
|
||||||
|
sacb->aiocb->bs->node_name, ret);
|
||||||
}
|
}
|
||||||
assert(acb->count <= s->num_children);
|
assert(acb->count <= s->num_children);
|
||||||
assert(acb->success_count <= s->num_children);
|
assert(acb->success_count <= s->num_children);
|
||||||
@@ -338,7 +343,9 @@ static void quorum_report_bad_versions(BDRVQuorumState *s,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QLIST_FOREACH(item, &version->items, next) {
|
QLIST_FOREACH(item, &version->items, next) {
|
||||||
quorum_report_bad(acb, s->children[item->index]->bs->node_name, 0);
|
quorum_report_bad(QUORUM_OP_TYPE_READ, acb->sector_num,
|
||||||
|
acb->nb_sectors,
|
||||||
|
s->children[item->index]->bs->node_name, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -648,8 +655,9 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < s->num_children; i++) {
|
for (i = 0; i < s->num_children; i++) {
|
||||||
bdrv_aio_readv(s->children[i]->bs, acb->sector_num, &acb->qcrs[i].qiov,
|
acb->qcrs[i].aiocb = bdrv_aio_readv(s->children[i]->bs, acb->sector_num,
|
||||||
acb->nb_sectors, quorum_aio_cb, &acb->qcrs[i]);
|
&acb->qcrs[i].qiov, acb->nb_sectors,
|
||||||
|
quorum_aio_cb, &acb->qcrs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return &acb->common;
|
return &acb->common;
|
||||||
@@ -664,6 +672,7 @@ static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb)
|
|||||||
qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov);
|
qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov);
|
||||||
qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov,
|
qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov,
|
||||||
acb->qcrs[acb->child_iter].buf);
|
acb->qcrs[acb->child_iter].buf);
|
||||||
|
acb->qcrs[acb->child_iter].aiocb =
|
||||||
bdrv_aio_readv(s->children[acb->child_iter]->bs, acb->sector_num,
|
bdrv_aio_readv(s->children[acb->child_iter]->bs, acb->sector_num,
|
||||||
&acb->qcrs[acb->child_iter].qiov, acb->nb_sectors,
|
&acb->qcrs[acb->child_iter].qiov, acb->nb_sectors,
|
||||||
quorum_aio_cb, &acb->qcrs[acb->child_iter]);
|
quorum_aio_cb, &acb->qcrs[acb->child_iter]);
|
||||||
@@ -760,19 +769,30 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
|
|||||||
QuorumVoteValue result_value;
|
QuorumVoteValue result_value;
|
||||||
int i;
|
int i;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
int success_count = 0;
|
||||||
|
|
||||||
QLIST_INIT(&error_votes.vote_list);
|
QLIST_INIT(&error_votes.vote_list);
|
||||||
error_votes.compare = quorum_64bits_compare;
|
error_votes.compare = quorum_64bits_compare;
|
||||||
|
|
||||||
for (i = 0; i < s->num_children; i++) {
|
for (i = 0; i < s->num_children; i++) {
|
||||||
result = bdrv_co_flush(s->children[i]->bs);
|
result = bdrv_co_flush(s->children[i]->bs);
|
||||||
|
if (result) {
|
||||||
|
quorum_report_bad(QUORUM_OP_TYPE_FLUSH, 0,
|
||||||
|
bdrv_nb_sectors(s->children[i]->bs),
|
||||||
|
s->children[i]->bs->node_name, result);
|
||||||
result_value.l = result;
|
result_value.l = result;
|
||||||
quorum_count_vote(&error_votes, &result_value, i);
|
quorum_count_vote(&error_votes, &result_value, i);
|
||||||
|
} else {
|
||||||
|
success_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (success_count >= s->threshold) {
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
winner = quorum_get_vote_winner(&error_votes);
|
winner = quorum_get_vote_winner(&error_votes);
|
||||||
result = winner->value.l;
|
result = winner->value.l;
|
||||||
|
}
|
||||||
quorum_free_vote_list(&error_votes);
|
quorum_free_vote_list(&error_votes);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
47
block/rbd.c
47
block/rbd.c
@@ -16,6 +16,7 @@
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "crypto/secret.h"
|
||||||
|
|
||||||
#include <rbd/librbd.h>
|
#include <rbd/librbd.h>
|
||||||
|
|
||||||
@@ -228,6 +229,27 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
if (secretid == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
|
||||||
|
errp);
|
||||||
|
if (!secret) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rados_conf_set(cluster, "key", secret);
|
||||||
|
g_free(secret);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
|
static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
|
||||||
bool only_read_conf_file,
|
bool only_read_conf_file,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
@@ -299,10 +321,13 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
char conf[RBD_MAX_CONF_SIZE];
|
char conf[RBD_MAX_CONF_SIZE];
|
||||||
char clientname_buf[RBD_MAX_CONF_SIZE];
|
char clientname_buf[RBD_MAX_CONF_SIZE];
|
||||||
char *clientname;
|
char *clientname;
|
||||||
|
const char *secretid;
|
||||||
rados_t cluster;
|
rados_t cluster;
|
||||||
rados_ioctx_t io_ctx;
|
rados_ioctx_t io_ctx;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
secretid = qemu_opt_get(opts, "password-secret");
|
||||||
|
|
||||||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||||
snap_buf, sizeof(snap_buf),
|
snap_buf, sizeof(snap_buf),
|
||||||
name, sizeof(name),
|
name, sizeof(name),
|
||||||
@@ -350,6 +375,11 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
|
||||||
|
rados_shutdown(cluster);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
if (rados_connect(cluster) < 0) {
|
if (rados_connect(cluster) < 0) {
|
||||||
error_setg(errp, "error connecting");
|
error_setg(errp, "error connecting");
|
||||||
rados_shutdown(cluster);
|
rados_shutdown(cluster);
|
||||||
@@ -423,6 +453,11 @@ static QemuOptsList runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "Specification of the rbd image",
|
.help = "Specification of the rbd image",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "password-secret",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret providing the password",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -436,6 +471,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
char conf[RBD_MAX_CONF_SIZE];
|
char conf[RBD_MAX_CONF_SIZE];
|
||||||
char clientname_buf[RBD_MAX_CONF_SIZE];
|
char clientname_buf[RBD_MAX_CONF_SIZE];
|
||||||
char *clientname;
|
char *clientname;
|
||||||
|
const char *secretid;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
@@ -450,6 +486,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
filename = qemu_opt_get(opts, "filename");
|
filename = qemu_opt_get(opts, "filename");
|
||||||
|
secretid = qemu_opt_get(opts, "password-secret");
|
||||||
|
|
||||||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||||
snap_buf, sizeof(snap_buf),
|
snap_buf, sizeof(snap_buf),
|
||||||
@@ -488,6 +525,11 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
|
||||||
|
r = -EIO;
|
||||||
|
goto failed_shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fallback to more conservative semantics if setting cache
|
* Fallback to more conservative semantics if setting cache
|
||||||
* options fails. Ignore errors from setting rbd_cache because the
|
* options fails. Ignore errors from setting rbd_cache because the
|
||||||
@@ -919,6 +961,11 @@ static QemuOptsList qemu_rbd_create_opts = {
|
|||||||
.type = QEMU_OPT_SIZE,
|
.type = QEMU_OPT_SIZE,
|
||||||
.help = "RBD object size"
|
.help = "RBD object size"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "password-secret",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret providing the password",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
169
block/sheepdog.c
169
block/sheepdog.c
@@ -18,6 +18,7 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
|
|
||||||
#define SD_PROTO_VER 0x01
|
#define SD_PROTO_VER 0x01
|
||||||
@@ -284,6 +285,12 @@ static inline bool is_snapshot(struct SheepdogInode *inode)
|
|||||||
return !!inode->snap_ctime;
|
return !!inode->snap_ctime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline size_t count_data_objs(const struct SheepdogInode *inode)
|
||||||
|
{
|
||||||
|
return DIV_ROUND_UP(inode->vdi_size,
|
||||||
|
(1UL << inode->block_size_shift));
|
||||||
|
}
|
||||||
|
|
||||||
#undef DPRINTF
|
#undef DPRINTF
|
||||||
#ifdef DEBUG_SDOG
|
#ifdef DEBUG_SDOG
|
||||||
#define DPRINTF(fmt, args...) \
|
#define DPRINTF(fmt, args...) \
|
||||||
@@ -609,14 +616,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
|
|||||||
ret = qemu_co_send(sockfd, hdr, sizeof(*hdr));
|
ret = qemu_co_send(sockfd, hdr, sizeof(*hdr));
|
||||||
if (ret != sizeof(*hdr)) {
|
if (ret != sizeof(*hdr)) {
|
||||||
error_report("failed to send a req, %s", strerror(errno));
|
error_report("failed to send a req, %s", strerror(errno));
|
||||||
ret = -socket_error();
|
return -errno;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qemu_co_send(sockfd, data, *wlen);
|
ret = qemu_co_send(sockfd, data, *wlen);
|
||||||
if (ret != *wlen) {
|
if (ret != *wlen) {
|
||||||
ret = -socket_error();
|
|
||||||
error_report("failed to send a req, %s", strerror(errno));
|
error_report("failed to send a req, %s", strerror(errno));
|
||||||
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1631,7 +1637,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
|
|||||||
|
|
||||||
static int sd_prealloc(const char *filename, Error **errp)
|
static int sd_prealloc(const char *filename, Error **errp)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = NULL;
|
BlockBackend *blk = NULL;
|
||||||
BDRVSheepdogState *base = NULL;
|
BDRVSheepdogState *base = NULL;
|
||||||
unsigned long buf_size;
|
unsigned long buf_size;
|
||||||
uint32_t idx, max_idx;
|
uint32_t idx, max_idx;
|
||||||
@@ -1640,19 +1646,23 @@ static int sd_prealloc(const char *filename, Error **errp)
|
|||||||
void *buf = NULL;
|
void *buf = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
blk = blk_new_open("image-prealloc", filename, NULL, NULL,
|
||||||
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
errp);
|
errp);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
|
ret = -EIO;
|
||||||
goto out_with_err_set;
|
goto out_with_err_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
vdi_size = bdrv_getlength(bs);
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
|
vdi_size = blk_getlength(blk);
|
||||||
if (vdi_size < 0) {
|
if (vdi_size < 0) {
|
||||||
ret = vdi_size;
|
ret = vdi_size;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
base = bs->opaque;
|
base = blk_bs(blk)->opaque;
|
||||||
object_size = (UINT32_C(1) << base->inode.block_size_shift);
|
object_size = (UINT32_C(1) << base->inode.block_size_shift);
|
||||||
buf_size = MIN(object_size, SD_DATA_OBJ_SIZE);
|
buf_size = MIN(object_size, SD_DATA_OBJ_SIZE);
|
||||||
buf = g_malloc0(buf_size);
|
buf = g_malloc0(buf_size);
|
||||||
@@ -1664,23 +1674,24 @@ static int sd_prealloc(const char *filename, Error **errp)
|
|||||||
* The created image can be a cloned image, so we need to read
|
* The created image can be a cloned image, so we need to read
|
||||||
* a data from the source image.
|
* a data from the source image.
|
||||||
*/
|
*/
|
||||||
ret = bdrv_pread(bs, idx * buf_size, buf, buf_size);
|
ret = blk_pread(blk, idx * buf_size, buf, buf_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite(bs, idx * buf_size, buf, buf_size);
|
ret = blk_pwrite(blk, idx * buf_size, buf, buf_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
||||||
}
|
}
|
||||||
out_with_err_set:
|
out_with_err_set:
|
||||||
if (bs) {
|
if (blk) {
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
}
|
}
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
|
|
||||||
@@ -1820,7 +1831,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (backing_file) {
|
if (backing_file) {
|
||||||
BlockDriverState *bs;
|
BlockBackend *blk;
|
||||||
BDRVSheepdogState *base;
|
BDRVSheepdogState *base;
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
|
|
||||||
@@ -1832,22 +1843,23 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = NULL;
|
blk = blk_new_open("backing", backing_file, NULL, NULL,
|
||||||
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, errp);
|
BDRV_O_PROTOCOL | BDRV_O_CACHE_WB, errp);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
|
ret = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
base = bs->opaque;
|
base = blk_bs(blk)->opaque;
|
||||||
|
|
||||||
if (!is_snapshot(&base->inode)) {
|
if (!is_snapshot(&base->inode)) {
|
||||||
error_setg(errp, "cannot clone from a non snapshot vdi");
|
error_setg(errp, "cannot clone from a non snapshot vdi");
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
s->inode.vdi_id = base->inode.vdi_id;
|
s->inode.vdi_id = base->inode.vdi_id;
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
}
|
}
|
||||||
|
|
||||||
s->aio_context = qemu_get_aio_context();
|
s->aio_context = qemu_get_aio_context();
|
||||||
@@ -2478,13 +2490,128 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NR_BATCHED_DISCARD 128
|
||||||
|
|
||||||
|
static bool remove_objects(BDRVSheepdogState *s)
|
||||||
|
{
|
||||||
|
int fd, i = 0, nr_objs = 0;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
bool result = true;
|
||||||
|
SheepdogInode *inode = &s->inode;
|
||||||
|
|
||||||
|
fd = connect_to_sdog(s, &local_err);
|
||||||
|
if (fd < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nr_objs = count_data_objs(inode);
|
||||||
|
while (i < nr_objs) {
|
||||||
|
int start_idx, nr_filled_idx;
|
||||||
|
|
||||||
|
while (i < nr_objs && !inode->data_vdi_id[i]) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
start_idx = i;
|
||||||
|
|
||||||
|
nr_filled_idx = 0;
|
||||||
|
while (i < nr_objs && nr_filled_idx < NR_BATCHED_DISCARD) {
|
||||||
|
if (inode->data_vdi_id[i]) {
|
||||||
|
inode->data_vdi_id[i] = 0;
|
||||||
|
nr_filled_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = write_object(fd, s->aio_context,
|
||||||
|
(char *)&inode->data_vdi_id[start_idx],
|
||||||
|
vid_to_vdi_oid(s->inode.vdi_id), inode->nr_copies,
|
||||||
|
(i - start_idx) * sizeof(uint32_t),
|
||||||
|
offsetof(struct SheepdogInode,
|
||||||
|
data_vdi_id[start_idx]),
|
||||||
|
false, s->cache_flags);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("failed to discard snapshot inode.");
|
||||||
|
result = false;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
closesocket(fd);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static int sd_snapshot_delete(BlockDriverState *bs,
|
static int sd_snapshot_delete(BlockDriverState *bs,
|
||||||
const char *snapshot_id,
|
const char *snapshot_id,
|
||||||
const char *name,
|
const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* FIXME: Delete specified snapshot id. */
|
uint32_t snap_id = 0;
|
||||||
return 0;
|
char snap_tag[SD_MAX_VDI_TAG_LEN];
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int fd, ret;
|
||||||
|
char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
|
||||||
|
BDRVSheepdogState *s = bs->opaque;
|
||||||
|
unsigned int wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN, rlen = 0;
|
||||||
|
uint32_t vid;
|
||||||
|
SheepdogVdiReq hdr = {
|
||||||
|
.opcode = SD_OP_DEL_VDI,
|
||||||
|
.data_length = wlen,
|
||||||
|
.flags = SD_FLAG_CMD_WRITE,
|
||||||
|
};
|
||||||
|
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
|
||||||
|
|
||||||
|
if (!remove_objects(s)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
memset(snap_tag, 0, sizeof(snap_tag));
|
||||||
|
pstrcpy(buf, SD_MAX_VDI_LEN, s->name);
|
||||||
|
if (qemu_strtoul(snapshot_id, NULL, 10, (unsigned long *)&snap_id)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snap_id) {
|
||||||
|
hdr.snapid = snap_id;
|
||||||
|
} else {
|
||||||
|
pstrcpy(snap_tag, sizeof(snap_tag), snapshot_id);
|
||||||
|
pstrcpy(buf + SD_MAX_VDI_LEN, SD_MAX_VDI_TAG_LEN, snap_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = find_vdi_name(s, s->name, snap_id, snap_tag, &vid, true,
|
||||||
|
&local_err);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = connect_to_sdog(s, &local_err);
|
||||||
|
if (fd < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr,
|
||||||
|
buf, &wlen, &rlen);
|
||||||
|
closesocket(fd);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rsp->result) {
|
||||||
|
case SD_RES_NO_VDI:
|
||||||
|
error_report("%s was already deleted", s->name);
|
||||||
|
case SD_RES_SUCCESS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_report("%s, %s", sd_strerror(rsp->result), s->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||||
|
|||||||
20
block/vdi.c
20
block/vdi.c
@@ -52,6 +52,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/migration.h"
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
@@ -733,7 +734,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
size_t bmap_size;
|
size_t bmap_size;
|
||||||
int64_t offset = 0;
|
int64_t offset = 0;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
BlockDriverState *bs = NULL;
|
BlockBackend *blk = NULL;
|
||||||
uint32_t *bmap = NULL;
|
uint32_t *bmap = NULL;
|
||||||
|
|
||||||
logout("\n");
|
logout("\n");
|
||||||
@@ -766,13 +767,18 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
|
||||||
|
blk = blk_new_open("image", filename, NULL, NULL,
|
||||||
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
/* We need enough blocks to store the given disk size,
|
/* We need enough blocks to store the given disk size,
|
||||||
so always round up. */
|
so always round up. */
|
||||||
blocks = DIV_ROUND_UP(bytes, block_size);
|
blocks = DIV_ROUND_UP(bytes, block_size);
|
||||||
@@ -802,7 +808,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
vdi_header_print(&header);
|
vdi_header_print(&header);
|
||||||
#endif
|
#endif
|
||||||
vdi_header_to_le(&header);
|
vdi_header_to_le(&header);
|
||||||
ret = bdrv_pwrite_sync(bs, offset, &header, sizeof(header));
|
ret = blk_pwrite(blk, offset, &header, sizeof(header));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Error writing header to %s", filename);
|
error_setg(errp, "Error writing header to %s", filename);
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -823,7 +829,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
bmap[i] = VDI_UNALLOCATED;
|
bmap[i] = VDI_UNALLOCATED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite_sync(bs, offset, bmap, bmap_size);
|
ret = blk_pwrite(blk, offset, bmap, bmap_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Error writing bmap to %s", filename);
|
error_setg(errp, "Error writing bmap to %s", filename);
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -832,7 +838,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (image_type == VDI_TYPE_STATIC) {
|
if (image_type == VDI_TYPE_STATIC) {
|
||||||
ret = bdrv_truncate(bs, offset + blocks * block_size);
|
ret = blk_truncate(blk, offset + blocks * block_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Failed to statically allocate %s", filename);
|
error_setg(errp, "Failed to statically allocate %s", filename);
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -840,7 +846,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
g_free(bmap);
|
g_free(bmap);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
42
block/vhdx.c
42
block/vhdx.c
@@ -18,6 +18,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/crc32c.h"
|
#include "qemu/crc32c.h"
|
||||||
#include "block/vhdx.h"
|
#include "block/vhdx.h"
|
||||||
@@ -264,10 +265,10 @@ static void vhdx_region_unregister_all(BDRVVHDXState *s)
|
|||||||
|
|
||||||
static void vhdx_set_shift_bits(BDRVVHDXState *s)
|
static void vhdx_set_shift_bits(BDRVVHDXState *s)
|
||||||
{
|
{
|
||||||
s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size);
|
s->logical_sector_size_bits = ctz32(s->logical_sector_size);
|
||||||
s->sectors_per_block_bits = 31 - clz32(s->sectors_per_block);
|
s->sectors_per_block_bits = ctz32(s->sectors_per_block);
|
||||||
s->chunk_ratio_bits = 63 - clz64(s->chunk_ratio);
|
s->chunk_ratio_bits = ctz64(s->chunk_ratio);
|
||||||
s->block_size_bits = 31 - clz32(s->block_size);
|
s->block_size_bits = ctz32(s->block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -857,14 +858,8 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s)
|
|||||||
{
|
{
|
||||||
uint32_t data_blocks_cnt, bitmap_blocks_cnt;
|
uint32_t data_blocks_cnt, bitmap_blocks_cnt;
|
||||||
|
|
||||||
data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits;
|
data_blocks_cnt = DIV_ROUND_UP(s->virtual_disk_size, s->block_size);
|
||||||
if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) {
|
bitmap_blocks_cnt = DIV_ROUND_UP(data_blocks_cnt, s->chunk_ratio);
|
||||||
data_blocks_cnt++;
|
|
||||||
}
|
|
||||||
bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits;
|
|
||||||
if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) {
|
|
||||||
bitmap_blocks_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->parent_entries) {
|
if (s->parent_entries) {
|
||||||
s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1);
|
s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1);
|
||||||
@@ -1778,7 +1773,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
|
|
||||||
gunichar2 *creator = NULL;
|
gunichar2 *creator = NULL;
|
||||||
glong creator_items;
|
glong creator_items;
|
||||||
BlockDriverState *bs;
|
BlockBackend *blk;
|
||||||
char *type = NULL;
|
char *type = NULL;
|
||||||
VHDXImageType image_type;
|
VHDXImageType image_type;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
@@ -1843,14 +1838,17 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = NULL;
|
blk = blk_new_open("image", filename, NULL, NULL,
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
/* Create (A) */
|
/* Create (A) */
|
||||||
|
|
||||||
/* The creator field is optional, but may be useful for
|
/* The creator field is optional, but may be useful for
|
||||||
@@ -1858,12 +1856,12 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
|
creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
|
||||||
&creator_items, NULL);
|
&creator_items, NULL);
|
||||||
signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
|
signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
|
||||||
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
if (creator) {
|
if (creator) {
|
||||||
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
||||||
creator, creator_items * sizeof(gunichar2));
|
creator, creator_items * sizeof(gunichar2));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
@@ -1872,13 +1870,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
|
|
||||||
|
|
||||||
/* Creates (B),(C) */
|
/* Creates (B),(C) */
|
||||||
ret = vhdx_create_new_headers(bs, image_size, log_size);
|
ret = vhdx_create_new_headers(blk_bs(blk), image_size, log_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Creates (D),(E),(G) explicitly. (F) created as by-product */
|
/* Creates (D),(E),(G) explicitly. (F) created as by-product */
|
||||||
ret = vhdx_create_new_region_table(bs, image_size, block_size, 512,
|
ret = vhdx_create_new_region_table(blk_bs(blk), image_size, block_size, 512,
|
||||||
log_size, use_zero_blocks, image_type,
|
log_size, use_zero_blocks, image_type,
|
||||||
&metadata_offset);
|
&metadata_offset);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -1886,7 +1884,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Creates (H) */
|
/* Creates (H) */
|
||||||
ret = vhdx_create_new_metadata(bs, image_size, block_size, 512,
|
ret = vhdx_create_new_metadata(blk_bs(blk), image_size, block_size, 512,
|
||||||
metadata_offset, image_type);
|
metadata_offset, image_type);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
@@ -1894,7 +1892,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
|
|
||||||
|
|
||||||
delete_and_exit:
|
delete_and_exit:
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
exit:
|
exit:
|
||||||
g_free(type);
|
g_free(type);
|
||||||
g_free(creator);
|
g_free(creator);
|
||||||
|
|||||||
115
block/vmdk.c
115
block/vmdk.c
@@ -26,6 +26,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
@@ -242,15 +243,17 @@ static void vmdk_free_last_extent(BlockDriverState *bs)
|
|||||||
|
|
||||||
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||||
{
|
{
|
||||||
char desc[DESC_SIZE];
|
char *desc;
|
||||||
uint32_t cid = 0xffffffff;
|
uint32_t cid = 0xffffffff;
|
||||||
const char *p_name, *cid_str;
|
const char *p_name, *cid_str;
|
||||||
size_t cid_str_size;
|
size_t cid_str_size;
|
||||||
BDRVVmdkState *s = bs->opaque;
|
BDRVVmdkState *s = bs->opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
desc = g_malloc0(DESC_SIZE);
|
||||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
g_free(desc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,41 +272,45 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
|||||||
sscanf(p_name, "%" SCNx32, &cid);
|
sscanf(p_name, "%" SCNx32, &cid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free(desc);
|
||||||
return cid;
|
return cid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||||
{
|
{
|
||||||
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
|
char *desc, *tmp_desc;
|
||||||
char *p_name, *tmp_str;
|
char *p_name, *tmp_str;
|
||||||
BDRVVmdkState *s = bs->opaque;
|
BDRVVmdkState *s = bs->opaque;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
|
desc = g_malloc0(DESC_SIZE);
|
||||||
|
tmp_desc = g_malloc0(DESC_SIZE);
|
||||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
desc[DESC_SIZE - 1] = '\0';
|
desc[DESC_SIZE - 1] = '\0';
|
||||||
tmp_str = strstr(desc, "parentCID");
|
tmp_str = strstr(desc, "parentCID");
|
||||||
if (tmp_str == NULL) {
|
if (tmp_str == NULL) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
|
pstrcpy(tmp_desc, DESC_SIZE, tmp_str);
|
||||||
p_name = strstr(desc, "CID");
|
p_name = strstr(desc, "CID");
|
||||||
if (p_name != NULL) {
|
if (p_name != NULL) {
|
||||||
p_name += sizeof("CID");
|
p_name += sizeof("CID");
|
||||||
snprintf(p_name, sizeof(desc) - (p_name - desc), "%" PRIx32 "\n", cid);
|
snprintf(p_name, DESC_SIZE - (p_name - desc), "%" PRIx32 "\n", cid);
|
||||||
pstrcat(desc, sizeof(desc), tmp_desc);
|
pstrcat(desc, DESC_SIZE, tmp_desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_pwrite_sync(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
ret = bdrv_pwrite_sync(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
g_free(desc);
|
||||||
|
g_free(tmp_desc);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmdk_is_cid_valid(BlockDriverState *bs)
|
static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||||
@@ -337,15 +344,16 @@ static int vmdk_reopen_prepare(BDRVReopenState *state,
|
|||||||
static int vmdk_parent_open(BlockDriverState *bs)
|
static int vmdk_parent_open(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
char *p_name;
|
char *p_name;
|
||||||
char desc[DESC_SIZE + 1];
|
char *desc;
|
||||||
BDRVVmdkState *s = bs->opaque;
|
BDRVVmdkState *s = bs->opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
desc[DESC_SIZE] = '\0';
|
desc = g_malloc0(DESC_SIZE + 1);
|
||||||
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
goto out;
|
||||||
}
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
p_name = strstr(desc, "parentFileNameHint");
|
p_name = strstr(desc, "parentFileNameHint");
|
||||||
if (p_name != NULL) {
|
if (p_name != NULL) {
|
||||||
@@ -354,16 +362,20 @@ static int vmdk_parent_open(BlockDriverState *bs)
|
|||||||
p_name += sizeof("parentFileNameHint") + 1;
|
p_name += sizeof("parentFileNameHint") + 1;
|
||||||
end_name = strchr(p_name, '\"');
|
end_name = strchr(p_name, '\"');
|
||||||
if (end_name == NULL) {
|
if (end_name == NULL) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
|
if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
|
pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
g_free(desc);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create and append extent to the extent array. Return the added VmdkExtent
|
/* Create and append extent to the extent array. Return the added VmdkExtent
|
||||||
@@ -1639,7 +1651,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
QemuOpts *opts, Error **errp)
|
QemuOpts *opts, Error **errp)
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
BlockDriverState *bs = NULL;
|
BlockBackend *blk = NULL;
|
||||||
VMDK4Header header;
|
VMDK4Header header;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
||||||
@@ -1652,16 +1664,19 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(bs == NULL);
|
blk = blk_new_open("extent", filename, NULL, NULL,
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
if (flat) {
|
if (flat) {
|
||||||
ret = bdrv_truncate(bs, filesize);
|
ret = blk_truncate(blk, filesize);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||||
}
|
}
|
||||||
@@ -1716,18 +1731,18 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
header.check_bytes[3] = 0xa;
|
header.check_bytes[3] = 0xa;
|
||||||
|
|
||||||
/* write all the data */
|
/* write all the data */
|
||||||
ret = bdrv_pwrite(bs, 0, &magic, sizeof(magic));
|
ret = blk_pwrite(blk, 0, &magic, sizeof(magic));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, QERR_IO_ERROR);
|
error_setg(errp, QERR_IO_ERROR);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite(bs, sizeof(magic), &header, sizeof(header));
|
ret = blk_pwrite(blk, sizeof(magic), &header, sizeof(header));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, QERR_IO_ERROR);
|
error_setg(errp, QERR_IO_ERROR);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9);
|
ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -1740,7 +1755,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
i < gt_count; i++, tmp += gt_size) {
|
i < gt_count; i++, tmp += gt_size) {
|
||||||
gd_buf[i] = cpu_to_le32(tmp);
|
gd_buf[i] = cpu_to_le32(tmp);
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite(bs, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
|
ret = blk_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
|
||||||
gd_buf, gd_buf_size);
|
gd_buf, gd_buf_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, QERR_IO_ERROR);
|
error_setg(errp, QERR_IO_ERROR);
|
||||||
@@ -1752,7 +1767,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
i < gt_count; i++, tmp += gt_size) {
|
i < gt_count; i++, tmp += gt_size) {
|
||||||
gd_buf[i] = cpu_to_le32(tmp);
|
gd_buf[i] = cpu_to_le32(tmp);
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite(bs, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE,
|
ret = blk_pwrite(blk, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE,
|
||||||
gd_buf, gd_buf_size);
|
gd_buf, gd_buf_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, QERR_IO_ERROR);
|
error_setg(errp, QERR_IO_ERROR);
|
||||||
@@ -1761,8 +1776,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
exit:
|
exit:
|
||||||
if (bs) {
|
if (blk) {
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
}
|
}
|
||||||
g_free(gd_buf);
|
g_free(gd_buf);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1811,7 +1826,7 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
|
|||||||
static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||||
{
|
{
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
BlockDriverState *new_bs = NULL;
|
BlockBackend *new_blk = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
char *desc = NULL;
|
char *desc = NULL;
|
||||||
int64_t total_size = 0, filesize;
|
int64_t total_size = 0, filesize;
|
||||||
@@ -1922,7 +1937,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (backing_file) {
|
if (backing_file) {
|
||||||
BlockDriverState *bs = NULL;
|
BlockBackend *blk;
|
||||||
char *full_backing = g_new0(char, PATH_MAX);
|
char *full_backing = g_new0(char, PATH_MAX);
|
||||||
bdrv_get_full_backing_filename_from_filename(filename, backing_file,
|
bdrv_get_full_backing_filename_from_filename(filename, backing_file,
|
||||||
full_backing, PATH_MAX,
|
full_backing, PATH_MAX,
|
||||||
@@ -1933,18 +1948,21 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, errp);
|
|
||||||
|
blk = blk_new_open("backing", full_backing, NULL, NULL,
|
||||||
|
BDRV_O_NO_BACKING | BDRV_O_CACHE_WB, errp);
|
||||||
g_free(full_backing);
|
g_free(full_backing);
|
||||||
if (ret != 0) {
|
if (blk == NULL) {
|
||||||
|
ret = -EIO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strcmp(bs->drv->format_name, "vmdk")) {
|
if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) {
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
parent_cid = vmdk_read_cid(bs, 0);
|
parent_cid = vmdk_read_cid(blk_bs(blk), 0);
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
snprintf(parent_desc_line, BUF_SIZE,
|
snprintf(parent_desc_line, BUF_SIZE,
|
||||||
"parentFileNameHint=\"%s\"", backing_file);
|
"parentFileNameHint=\"%s\"", backing_file);
|
||||||
}
|
}
|
||||||
@@ -2002,14 +2020,19 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(new_bs == NULL);
|
|
||||||
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
new_blk = blk_new_open("descriptor", filename, NULL, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
if (ret < 0) {
|
&local_err);
|
||||||
|
if (new_blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len);
|
|
||||||
|
blk_set_allow_write_beyond_eof(new_blk, true);
|
||||||
|
|
||||||
|
ret = blk_pwrite(new_blk, desc_offset, desc, desc_len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not write description");
|
error_setg_errno(errp, -ret, "Could not write description");
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -2017,14 +2040,14 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
||||||
* for description file */
|
* for description file */
|
||||||
if (desc_offset == 0) {
|
if (desc_offset == 0) {
|
||||||
ret = bdrv_truncate(new_bs, desc_len);
|
ret = blk_truncate(new_blk, desc_len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not truncate file");
|
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exit:
|
exit:
|
||||||
if (new_bs) {
|
if (new_blk) {
|
||||||
bdrv_unref(new_bs);
|
blk_unref(new_blk);
|
||||||
}
|
}
|
||||||
g_free(adapter_type);
|
g_free(adapter_type);
|
||||||
g_free(backing_file);
|
g_free(backing_file);
|
||||||
|
|||||||
153
block/vpc.c
153
block/vpc.c
@@ -25,6 +25,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "migration/migration.h"
|
#include "migration/migration.h"
|
||||||
#if defined(CONFIG_UUID)
|
#if defined(CONFIG_UUID)
|
||||||
@@ -46,8 +47,14 @@ enum vhd_type {
|
|||||||
// Seconds since Jan 1, 2000 0:00:00 (UTC)
|
// Seconds since Jan 1, 2000 0:00:00 (UTC)
|
||||||
#define VHD_TIMESTAMP_BASE 946684800
|
#define VHD_TIMESTAMP_BASE 946684800
|
||||||
|
|
||||||
|
#define VHD_CHS_MAX_C 65535LL
|
||||||
|
#define VHD_CHS_MAX_H 16
|
||||||
|
#define VHD_CHS_MAX_S 255
|
||||||
|
|
||||||
#define VHD_MAX_SECTORS (65535LL * 255 * 255)
|
#define VHD_MAX_SECTORS (65535LL * 255 * 255)
|
||||||
#define VHD_MAX_GEOMETRY (65535LL * 16 * 255)
|
#define VHD_MAX_GEOMETRY (VHD_CHS_MAX_C * VHD_CHS_MAX_H * VHD_CHS_MAX_S)
|
||||||
|
|
||||||
|
#define VPC_OPT_FORCE_SIZE "force_size"
|
||||||
|
|
||||||
// always big-endian
|
// always big-endian
|
||||||
typedef struct vhd_footer {
|
typedef struct vhd_footer {
|
||||||
@@ -128,6 +135,8 @@ typedef struct BDRVVPCState {
|
|||||||
|
|
||||||
uint32_t block_size;
|
uint32_t block_size;
|
||||||
uint32_t bitmap_size;
|
uint32_t bitmap_size;
|
||||||
|
bool force_use_chs;
|
||||||
|
bool force_use_sz;
|
||||||
|
|
||||||
#ifdef CACHE
|
#ifdef CACHE
|
||||||
uint8_t *pageentry_u8;
|
uint8_t *pageentry_u8;
|
||||||
@@ -140,6 +149,22 @@ typedef struct BDRVVPCState {
|
|||||||
Error *migration_blocker;
|
Error *migration_blocker;
|
||||||
} BDRVVPCState;
|
} BDRVVPCState;
|
||||||
|
|
||||||
|
#define VPC_OPT_SIZE_CALC "force_size_calc"
|
||||||
|
static QemuOptsList vpc_runtime_opts = {
|
||||||
|
.name = "vpc-runtime-opts",
|
||||||
|
.head = QTAILQ_HEAD_INITIALIZER(vpc_runtime_opts.head),
|
||||||
|
.desc = {
|
||||||
|
{
|
||||||
|
.name = VPC_OPT_SIZE_CALC,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "Force disk size calculation to use either CHS geometry, "
|
||||||
|
"or use the disk current_size specified in the VHD footer. "
|
||||||
|
"{chs, current_size}"
|
||||||
|
},
|
||||||
|
{ /* end of list */ }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
|
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
|
||||||
{
|
{
|
||||||
uint32_t res = 0;
|
uint32_t res = 0;
|
||||||
@@ -159,6 +184,25 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
BDRVVPCState *s = bs->opaque;
|
||||||
|
const char *size_calc;
|
||||||
|
|
||||||
|
size_calc = qemu_opt_get(opts, VPC_OPT_SIZE_CALC);
|
||||||
|
|
||||||
|
if (!size_calc) {
|
||||||
|
/* no override, use autodetect only */
|
||||||
|
} else if (!strcmp(size_calc, "current_size")) {
|
||||||
|
s->force_use_sz = true;
|
||||||
|
} else if (!strcmp(size_calc, "chs")) {
|
||||||
|
s->force_use_chs = true;
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "Invalid size calculation mode: '%s'", size_calc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@@ -166,6 +210,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
int i;
|
int i;
|
||||||
VHDFooter *footer;
|
VHDFooter *footer;
|
||||||
VHDDynDiskHeader *dyndisk_header;
|
VHDDynDiskHeader *dyndisk_header;
|
||||||
|
QemuOpts *opts = NULL;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
bool use_chs;
|
||||||
uint8_t buf[HEADER_SIZE];
|
uint8_t buf[HEADER_SIZE];
|
||||||
uint32_t checksum;
|
uint32_t checksum;
|
||||||
uint64_t computed_size;
|
uint64_t computed_size;
|
||||||
@@ -173,6 +220,21 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
int disk_type = VHD_DYNAMIC;
|
int disk_type = VHD_DYNAMIC;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort);
|
||||||
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
vpc_parse_options(bs, opts, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
ret = bdrv_pread(bs->file->bs, 0, s->footer_buf, HEADER_SIZE);
|
ret = bdrv_pread(bs->file->bs, 0, s->footer_buf, HEADER_SIZE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -218,10 +280,34 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
bs->total_sectors = (int64_t)
|
bs->total_sectors = (int64_t)
|
||||||
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
|
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
|
||||||
|
|
||||||
/* Images that have exactly the maximum geometry are probably bigger and
|
/* Microsoft Virtual PC and Microsoft Hyper-V produce and read
|
||||||
* would be truncated if we adhered to the geometry for them. Rely on
|
* VHD image sizes differently. VPC will rely on CHS geometry,
|
||||||
* footer->current_size for them. */
|
* while Hyper-V and disk2vhd use the size specified in the footer.
|
||||||
if (bs->total_sectors == VHD_MAX_GEOMETRY) {
|
*
|
||||||
|
* We use a couple of approaches to try and determine the correct method:
|
||||||
|
* look at the Creator App field, and look for images that have CHS
|
||||||
|
* geometry that is the maximum value.
|
||||||
|
*
|
||||||
|
* If the CHS geometry is the maximum CHS geometry, then we assume that
|
||||||
|
* the size is the footer->current_size to avoid truncation. Otherwise,
|
||||||
|
* we follow the table based on footer->creator_app:
|
||||||
|
*
|
||||||
|
* Known creator apps:
|
||||||
|
* 'vpc ' : CHS Virtual PC (uses disk geometry)
|
||||||
|
* 'qemu' : CHS QEMU (uses disk geometry)
|
||||||
|
* 'qem2' : current_size QEMU (uses current_size)
|
||||||
|
* 'win ' : current_size Hyper-V
|
||||||
|
* 'd2v ' : current_size Disk2vhd
|
||||||
|
*
|
||||||
|
* The user can override the table values via drive options, however
|
||||||
|
* even with an override we will still use current_size for images
|
||||||
|
* that have CHS geometry of the maximum size.
|
||||||
|
*/
|
||||||
|
use_chs = (!!strncmp(footer->creator_app, "win ", 4) &&
|
||||||
|
!!strncmp(footer->creator_app, "qem2", 4) &&
|
||||||
|
!!strncmp(footer->creator_app, "d2v ", 4)) || s->force_use_chs;
|
||||||
|
|
||||||
|
if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) {
|
||||||
bs->total_sectors = be64_to_cpu(footer->current_size) /
|
bs->total_sectors = be64_to_cpu(footer->current_size) /
|
||||||
BDRV_SECTOR_SIZE;
|
BDRV_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
@@ -673,7 +759,7 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf,
|
||||||
int64_t total_sectors)
|
int64_t total_sectors)
|
||||||
{
|
{
|
||||||
VHDDynDiskHeader *dyndisk_header =
|
VHDDynDiskHeader *dyndisk_header =
|
||||||
@@ -687,13 +773,13 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
|||||||
block_size = 0x200000;
|
block_size = 0x200000;
|
||||||
num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
|
num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
|
||||||
|
|
||||||
ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE);
|
ret = blk_pwrite(blk, offset, buf, HEADER_SIZE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = 1536 + ((num_bat_entries * 4 + 511) & ~511);
|
offset = 1536 + ((num_bat_entries * 4 + 511) & ~511);
|
||||||
ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE);
|
ret = blk_pwrite(blk, offset, buf, HEADER_SIZE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -703,7 +789,7 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
|||||||
|
|
||||||
memset(buf, 0xFF, 512);
|
memset(buf, 0xFF, 512);
|
||||||
for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) {
|
for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) {
|
||||||
ret = bdrv_pwrite_sync(bs, offset, buf, 512);
|
ret = blk_pwrite(blk, offset, buf, 512);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -730,7 +816,7 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
|||||||
// Write the header
|
// Write the header
|
||||||
offset = 512;
|
offset = 512;
|
||||||
|
|
||||||
ret = bdrv_pwrite_sync(bs, offset, buf, 1024);
|
ret = blk_pwrite(blk, offset, buf, 1024);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -739,7 +825,7 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf,
|
static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
||||||
int64_t total_size)
|
int64_t total_size)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@@ -747,12 +833,12 @@ static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf,
|
|||||||
/* Add footer to total size */
|
/* Add footer to total size */
|
||||||
total_size += HEADER_SIZE;
|
total_size += HEADER_SIZE;
|
||||||
|
|
||||||
ret = bdrv_truncate(bs, total_size);
|
ret = blk_truncate(blk, total_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_pwrite_sync(bs, total_size - HEADER_SIZE, buf, HEADER_SIZE);
|
ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -773,8 +859,9 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
int64_t total_size;
|
int64_t total_size;
|
||||||
int disk_type;
|
int disk_type;
|
||||||
int ret = -EIO;
|
int ret = -EIO;
|
||||||
|
bool force_size;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
BlockDriverState *bs = NULL;
|
BlockBackend *blk = NULL;
|
||||||
|
|
||||||
/* Read out options */
|
/* Read out options */
|
||||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||||
@@ -793,31 +880,45 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
disk_type = VHD_DYNAMIC;
|
disk_type = VHD_DYNAMIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
force_size = qemu_opt_get_bool_del(opts, VPC_OPT_FORCE_SIZE, false);
|
||||||
|
|
||||||
ret = bdrv_create_file(filename, opts, &local_err);
|
ret = bdrv_create_file(filename, opts, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
|
||||||
|
blk = blk_new_open("image", filename, NULL, NULL,
|
||||||
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (blk == NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate matching total_size and geometry. Increase the number of
|
* Calculate matching total_size and geometry. Increase the number of
|
||||||
* sectors requested until we get enough (or fail). This ensures that
|
* sectors requested until we get enough (or fail). This ensures that
|
||||||
* qemu-img convert doesn't truncate images, but rather rounds up.
|
* qemu-img convert doesn't truncate images, but rather rounds up.
|
||||||
*
|
*
|
||||||
* If the image size can't be represented by a spec conform CHS geometry,
|
* If the image size can't be represented by a spec conformant CHS geometry,
|
||||||
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
|
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
|
||||||
* the image size from the VHD footer to calculate total_sectors.
|
* the image size from the VHD footer to calculate total_sectors.
|
||||||
*/
|
*/
|
||||||
|
if (force_size) {
|
||||||
|
/* This will force the use of total_size for sector count, below */
|
||||||
|
cyls = VHD_CHS_MAX_C;
|
||||||
|
heads = VHD_CHS_MAX_H;
|
||||||
|
secs_per_cyl = VHD_CHS_MAX_S;
|
||||||
|
} else {
|
||||||
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
|
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
|
||||||
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
||||||
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
|
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
|
if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
|
||||||
total_sectors = total_size / BDRV_SECTOR_SIZE;
|
total_sectors = total_size / BDRV_SECTOR_SIZE;
|
||||||
@@ -835,8 +936,11 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
memset(buf, 0, 1024);
|
memset(buf, 0, 1024);
|
||||||
|
|
||||||
memcpy(footer->creator, "conectix", 8);
|
memcpy(footer->creator, "conectix", 8);
|
||||||
/* TODO Check if "qemu" creator_app is ok for VPC */
|
if (force_size) {
|
||||||
|
memcpy(footer->creator_app, "qem2", 4);
|
||||||
|
} else {
|
||||||
memcpy(footer->creator_app, "qemu", 4);
|
memcpy(footer->creator_app, "qemu", 4);
|
||||||
|
}
|
||||||
memcpy(footer->creator_os, "Wi2k", 4);
|
memcpy(footer->creator_os, "Wi2k", 4);
|
||||||
|
|
||||||
footer->features = cpu_to_be32(0x02);
|
footer->features = cpu_to_be32(0x02);
|
||||||
@@ -866,13 +970,13 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE));
|
footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE));
|
||||||
|
|
||||||
if (disk_type == VHD_DYNAMIC) {
|
if (disk_type == VHD_DYNAMIC) {
|
||||||
ret = create_dynamic_disk(bs, buf, total_sectors);
|
ret = create_dynamic_disk(blk, buf, total_sectors);
|
||||||
} else {
|
} else {
|
||||||
ret = create_fixed_disk(bs, buf, total_size);
|
ret = create_fixed_disk(blk, buf, total_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
bdrv_unref(bs);
|
blk_unref(blk);
|
||||||
g_free(disk_type_param);
|
g_free(disk_type_param);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -917,6 +1021,13 @@ static QemuOptsList vpc_create_opts = {
|
|||||||
"Type of virtual hard disk format. Supported formats are "
|
"Type of virtual hard disk format. Supported formats are "
|
||||||
"{dynamic (default) | fixed} "
|
"{dynamic (default) | fixed} "
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = VPC_OPT_FORCE_SIZE,
|
||||||
|
.type = QEMU_OPT_BOOL,
|
||||||
|
.help = "Force disk size calculation to use the actual size "
|
||||||
|
"specified, rather than using the nearest CHS-based "
|
||||||
|
"calculation"
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
91
blockdev.c
91
blockdev.c
@@ -593,13 +593,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
||||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||||
|
|
||||||
if (snapshot) {
|
|
||||||
/* always use cache=unsafe with snapshot */
|
|
||||||
qdict_put(bs_opts, BDRV_OPT_CACHE_WB, qstring_from_str("on"));
|
|
||||||
qdict_put(bs_opts, BDRV_OPT_CACHE_DIRECT, qstring_from_str("off"));
|
|
||||||
qdict_put(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, qstring_from_str("on"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||||
bdrv_flags |= BDRV_O_INACTIVE;
|
bdrv_flags |= BDRV_O_INACTIVE;
|
||||||
}
|
}
|
||||||
@@ -682,6 +675,13 @@ static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
|
||||||
|
* with other callers) rather than what we want as the real defaults.
|
||||||
|
* Apply the defaults here instead. */
|
||||||
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_WB, "on");
|
||||||
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
||||||
|
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||||
|
|
||||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||||
bdrv_flags |= BDRV_O_INACTIVE;
|
bdrv_flags |= BDRV_O_INACTIVE;
|
||||||
}
|
}
|
||||||
@@ -1202,15 +1202,11 @@ void hmp_commit(Monitor *mon, const QDict *qdict)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blockdev_do_action(TransactionActionKind type, void *data,
|
static void blockdev_do_action(TransactionAction *action, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
TransactionAction action;
|
|
||||||
TransactionActionList list;
|
TransactionActionList list;
|
||||||
|
|
||||||
action.type = type;
|
list.value = action;
|
||||||
action.u.data = data;
|
|
||||||
list.value = &action;
|
|
||||||
list.next = NULL;
|
list.next = NULL;
|
||||||
qmp_transaction(&list, false, NULL, errp);
|
qmp_transaction(&list, false, NULL, errp);
|
||||||
}
|
}
|
||||||
@@ -1236,8 +1232,11 @@ void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
|
|||||||
.has_mode = has_mode,
|
.has_mode = has_mode,
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
};
|
};
|
||||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
|
TransactionAction action = {
|
||||||
&snapshot, errp);
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
|
||||||
|
.u.blockdev_snapshot_sync = &snapshot,
|
||||||
|
};
|
||||||
|
blockdev_do_action(&action, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
||||||
@@ -1247,9 +1246,11 @@ void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
|||||||
.node = (char *) node,
|
.node = (char *) node,
|
||||||
.overlay = (char *) overlay
|
.overlay = (char *) overlay
|
||||||
};
|
};
|
||||||
|
TransactionAction action = {
|
||||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
|
||||||
&snapshot_data, errp);
|
.u.blockdev_snapshot = &snapshot_data,
|
||||||
|
};
|
||||||
|
blockdev_do_action(&action, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_blockdev_snapshot_internal_sync(const char *device,
|
void qmp_blockdev_snapshot_internal_sync(const char *device,
|
||||||
@@ -1260,9 +1261,11 @@ void qmp_blockdev_snapshot_internal_sync(const char *device,
|
|||||||
.device = (char *) device,
|
.device = (char *) device,
|
||||||
.name = (char *) name
|
.name = (char *) name
|
||||||
};
|
};
|
||||||
|
TransactionAction action = {
|
||||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
|
||||||
&snapshot, errp);
|
.u.blockdev_snapshot_internal_sync = &snapshot,
|
||||||
|
};
|
||||||
|
blockdev_do_action(&action, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
||||||
@@ -1729,10 +1732,15 @@ static void external_snapshot_prepare(BlkActionState *common,
|
|||||||
/* create new image w/backing file */
|
/* create new image w/backing file */
|
||||||
mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||||
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
||||||
|
int64_t size = bdrv_getlength(state->old_bs);
|
||||||
|
if (size < 0) {
|
||||||
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
bdrv_img_create(new_image_file, format,
|
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, -1, flags, &local_err, false);
|
NULL, size, flags, &local_err, false);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
@@ -2816,6 +2824,15 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict)
|
|||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
bs = bdrv_find_node(id);
|
||||||
|
if (bs) {
|
||||||
|
qmp_x_blockdev_del(false, NULL, true, id, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
blk = blk_by_name(id);
|
blk = blk_by_name(id);
|
||||||
if (!blk) {
|
if (!blk) {
|
||||||
error_report("Device '%s' not found", id);
|
error_report("Device '%s' not found", id);
|
||||||
@@ -3867,6 +3884,36 @@ out:
|
|||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hmp_drive_add_node(Monitor *mon, const char *optstr)
|
||||||
|
{
|
||||||
|
QemuOpts *opts;
|
||||||
|
QDict *qdict;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false);
|
||||||
|
if (!opts) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdict = qemu_opts_to_qdict(opts, NULL);
|
||||||
|
|
||||||
|
if (!qdict_get_try_str(qdict, "node-name")) {
|
||||||
|
error_report("'node-name' needs to be specified");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockDriverState *bs = bds_tree_init(qdict, &local_err);
|
||||||
|
if (!bs) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
|
||||||
|
|
||||||
|
out:
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
}
|
||||||
|
|
||||||
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
||||||
{
|
{
|
||||||
QmpOutputVisitor *ov = qmp_output_visitor_new();
|
QmpOutputVisitor *ov = qmp_output_visitor_new();
|
||||||
|
|||||||
21
configure
vendored
21
configure
vendored
@@ -280,6 +280,7 @@ libusb=""
|
|||||||
usb_redir=""
|
usb_redir=""
|
||||||
opengl=""
|
opengl=""
|
||||||
opengl_dmabuf="no"
|
opengl_dmabuf="no"
|
||||||
|
avx2_opt="no"
|
||||||
zlib="yes"
|
zlib="yes"
|
||||||
lzo=""
|
lzo=""
|
||||||
snappy=""
|
snappy=""
|
||||||
@@ -1773,6 +1774,21 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
|
# avx2 optimization requirement check
|
||||||
|
|
||||||
|
cat > $TMPC << EOF
|
||||||
|
static void bar(void) {}
|
||||||
|
static void *bar_ifunc(void) {return (void*) bar;}
|
||||||
|
static void foo(void) __attribute__((ifunc("bar_ifunc")));
|
||||||
|
int main(void) { foo(); return 0; }
|
||||||
|
EOF
|
||||||
|
if compile_prog "-mavx2" "" ; then
|
||||||
|
if readelf --syms $TMPE |grep "IFUNC.*foo" >/dev/null 2>&1; then
|
||||||
|
avx2_opt="yes"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#########################################
|
||||||
# zlib check
|
# zlib check
|
||||||
|
|
||||||
if test "$zlib" != "no" ; then
|
if test "$zlib" != "no" ; then
|
||||||
@@ -4790,6 +4806,7 @@ echo "bzip2 support $bzip2"
|
|||||||
echo "NUMA host support $numa"
|
echo "NUMA host support $numa"
|
||||||
echo "tcmalloc support $tcmalloc"
|
echo "tcmalloc support $tcmalloc"
|
||||||
echo "jemalloc support $jemalloc"
|
echo "jemalloc support $jemalloc"
|
||||||
|
echo "avx2 optimization $avx2_opt"
|
||||||
|
|
||||||
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"
|
||||||
@@ -5178,6 +5195,10 @@ if test "$opengl" = "yes" ; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$avx2_opt" = "yes" ; then
|
||||||
|
echo "CONFIG_AVX2_OPT=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
if test "$lzo" = "yes" ; then
|
if test "$lzo" = "yes" ; then
|
||||||
echo "CONFIG_LZO=y" >> $config_host_mak
|
echo "CONFIG_LZO=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|||||||
63
cpus.c
63
cpus.c
@@ -370,9 +370,12 @@ static void icount_warp_rt(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void icount_dummy_timer(void *opaque)
|
static void icount_timer_cb(void *opaque)
|
||||||
{
|
{
|
||||||
(void)opaque;
|
/* No need for a checkpoint because the timer already synchronizes
|
||||||
|
* with CHECKPOINT_CLOCK_VIRTUAL_RT.
|
||||||
|
*/
|
||||||
|
icount_warp_rt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void qtest_clock_warp(int64_t dest)
|
void qtest_clock_warp(int64_t dest)
|
||||||
@@ -396,17 +399,12 @@ void qtest_clock_warp(int64_t dest)
|
|||||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_clock_warp(QEMUClockType type)
|
void qemu_start_warp_timer(void)
|
||||||
{
|
{
|
||||||
int64_t clock;
|
int64_t clock;
|
||||||
int64_t deadline;
|
int64_t deadline;
|
||||||
|
|
||||||
/*
|
if (!use_icount) {
|
||||||
* There are too many global variables to make the "warp" behavior
|
|
||||||
* applicable to other clocks. But a clock argument removes the
|
|
||||||
* need for if statements all over the place.
|
|
||||||
*/
|
|
||||||
if (type != QEMU_CLOCK_VIRTUAL || !use_icount) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,22 +416,10 @@ void qemu_clock_warp(QEMUClockType type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* warp clock deterministically in record/replay mode */
|
/* warp clock deterministically in record/replay mode */
|
||||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP)) {
|
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (icount_sleep) {
|
|
||||||
/*
|
|
||||||
* If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now.
|
|
||||||
* This ensures that the deadline for the timer is computed correctly
|
|
||||||
* below.
|
|
||||||
* This also makes sure that the insn counter is synchronized before
|
|
||||||
* the CPU starts running, in case the CPU is woken by an event other
|
|
||||||
* than the earliest QEMU_CLOCK_VIRTUAL timer.
|
|
||||||
*/
|
|
||||||
icount_warp_rt();
|
|
||||||
timer_del(icount_warp_timer);
|
|
||||||
}
|
|
||||||
if (!all_cpu_threads_idle()) {
|
if (!all_cpu_threads_idle()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -496,6 +482,28 @@ void qemu_clock_warp(QEMUClockType type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qemu_account_warp_timer(void)
|
||||||
|
{
|
||||||
|
if (!use_icount || !icount_sleep) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
|
||||||
|
* do not fire, so computing the deadline does not make sense.
|
||||||
|
*/
|
||||||
|
if (!runstate_is_running()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* warp clock deterministically in record/replay mode */
|
||||||
|
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_del(icount_warp_timer);
|
||||||
|
icount_warp_rt();
|
||||||
|
}
|
||||||
|
|
||||||
static bool icount_state_needed(void *opaque)
|
static bool icount_state_needed(void *opaque)
|
||||||
{
|
{
|
||||||
return use_icount;
|
return use_icount;
|
||||||
@@ -624,13 +632,13 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
|||||||
icount_sleep = qemu_opt_get_bool(opts, "sleep", true);
|
icount_sleep = qemu_opt_get_bool(opts, "sleep", true);
|
||||||
if (icount_sleep) {
|
if (icount_sleep) {
|
||||||
icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||||
icount_dummy_timer, NULL);
|
icount_timer_cb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
icount_align_option = qemu_opt_get_bool(opts, "align", false);
|
icount_align_option = qemu_opt_get_bool(opts, "align", false);
|
||||||
|
|
||||||
if (icount_align_option && !icount_sleep) {
|
if (icount_align_option && !icount_sleep) {
|
||||||
error_setg(errp, "align=on and sleep=no are incompatible");
|
error_setg(errp, "align=on and sleep=off are incompatible");
|
||||||
}
|
}
|
||||||
if (strcmp(option, "auto") != 0) {
|
if (strcmp(option, "auto") != 0) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
@@ -643,7 +651,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
|||||||
} else if (icount_align_option) {
|
} else if (icount_align_option) {
|
||||||
error_setg(errp, "shift=auto and align=on are incompatible");
|
error_setg(errp, "shift=auto and align=on are incompatible");
|
||||||
} else if (!icount_sleep) {
|
} else if (!icount_sleep) {
|
||||||
error_setg(errp, "shift=auto and sleep=no are incompatible");
|
error_setg(errp, "shift=auto and sleep=off are incompatible");
|
||||||
}
|
}
|
||||||
|
|
||||||
use_icount = 2;
|
use_icount = 2;
|
||||||
@@ -995,9 +1003,6 @@ static void qemu_wait_io_event_common(CPUState *cpu)
|
|||||||
static void qemu_tcg_wait_io_event(CPUState *cpu)
|
static void qemu_tcg_wait_io_event(CPUState *cpu)
|
||||||
{
|
{
|
||||||
while (all_cpu_threads_idle()) {
|
while (all_cpu_threads_idle()) {
|
||||||
/* Start accounting real time to the virtual clock if the CPUs
|
|
||||||
are idle. */
|
|
||||||
qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
|
|
||||||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1499,7 +1504,7 @@ static void tcg_exec_all(void)
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
|
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
|
||||||
qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
|
qemu_account_warp_timer();
|
||||||
|
|
||||||
if (next_cpu == NULL) {
|
if (next_cpu == NULL) {
|
||||||
next_cpu = first_cpu;
|
next_cpu = first_cpu;
|
||||||
|
|||||||
4
cputlb.c
4
cputlb.c
@@ -416,8 +416,8 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
|||||||
/* Write access calls the I/O callback. */
|
/* Write access calls the I/O callback. */
|
||||||
te->addr_write = address | TLB_MMIO;
|
te->addr_write = address | TLB_MMIO;
|
||||||
} else if (memory_region_is_ram(section->mr)
|
} else if (memory_region_is_ram(section->mr)
|
||||||
&& cpu_physical_memory_is_clean(section->mr->ram_addr
|
&& cpu_physical_memory_is_clean(
|
||||||
+ xlat)) {
|
memory_region_get_ram_addr(section->mr) + xlat)) {
|
||||||
te->addr_write = address | TLB_NOTDIRTY;
|
te->addr_write = address | TLB_NOTDIRTY;
|
||||||
} else {
|
} else {
|
||||||
te->addr_write = address;
|
te->addr_write = address;
|
||||||
|
|||||||
@@ -110,3 +110,4 @@ CONFIG_IOH3420=y
|
|||||||
CONFIG_I82801B11=y
|
CONFIG_I82801B11=y
|
||||||
CONFIG_ACPI=y
|
CONFIG_ACPI=y
|
||||||
CONFIG_SMBIOS=y
|
CONFIG_SMBIOS=y
|
||||||
|
CONFIG_ASPEED_SOC=y
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
|
#include "block/block_int.h"
|
||||||
|
|
||||||
static DriveInfo *add_init_drive(const char *optstr)
|
static DriveInfo *add_init_drive(const char *optstr)
|
||||||
{
|
{
|
||||||
@@ -55,6 +56,12 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict)
|
|||||||
{
|
{
|
||||||
DriveInfo *dinfo = NULL;
|
DriveInfo *dinfo = NULL;
|
||||||
const char *opts = qdict_get_str(qdict, "opts");
|
const char *opts = qdict_get_str(qdict, "opts");
|
||||||
|
bool node = qdict_get_try_bool(qdict, "node", false);
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
hmp_drive_add_node(mon, opts);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dinfo = add_init_drive(opts);
|
dinfo = add_init_drive(opts);
|
||||||
if (!dinfo) {
|
if (!dinfo) {
|
||||||
|
|||||||
@@ -180,8 +180,8 @@ aliases that leave holes then the lower priority region will appear in these
|
|||||||
holes too.)
|
holes too.)
|
||||||
|
|
||||||
For example, suppose we have a container A of size 0x8000 with two subregions
|
For example, suppose we have a container A of size 0x8000 with two subregions
|
||||||
B and C. B is a container mapped at 0x2000, size 0x4000, priority 1; C is
|
B and C. B is a container mapped at 0x2000, size 0x4000, priority 2; C is
|
||||||
an MMIO region mapped at 0x0, size 0x6000, priority 2. B currently has two
|
an MMIO region mapped at 0x0, size 0x6000, priority 1. B currently has two
|
||||||
of its own subregions: D of size 0x1000 at offset 0 and E of size 0x1000 at
|
of its own subregions: D of size 0x1000 at offset 0 and E of size 0x1000 at
|
||||||
offset 0x2000. As a diagram:
|
offset 0x2000. As a diagram:
|
||||||
|
|
||||||
@@ -297,8 +297,9 @@ various constraints can be supplied to control how these callbacks are called:
|
|||||||
- .valid.min_access_size, .valid.max_access_size define the access sizes
|
- .valid.min_access_size, .valid.max_access_size define the access sizes
|
||||||
(in bytes) which the device accepts; accesses outside this range will
|
(in bytes) which the device accepts; accesses outside this range will
|
||||||
have device and bus specific behaviour (ignored, or machine check)
|
have device and bus specific behaviour (ignored, or machine check)
|
||||||
- .valid.aligned specifies that the device only accepts naturally aligned
|
- .valid.unaligned specifies that the *device being modelled* supports
|
||||||
accesses. Unaligned accesses invoke device and bus specific behaviour.
|
unaligned accesses; if false, unaligned accesses will invoke the
|
||||||
|
appropriate bus or CPU specific behaviour.
|
||||||
- .impl.min_access_size, .impl.max_access_size define the access sizes
|
- .impl.min_access_size, .impl.max_access_size define the access sizes
|
||||||
(in bytes) supported by the *implementation*; other access sizes will be
|
(in bytes) supported by the *implementation*; other access sizes will be
|
||||||
emulated using the ones available. For example a 4-byte write will be
|
emulated using the ones available. For example a 4-byte write will be
|
||||||
@@ -306,5 +307,5 @@ various constraints can be supplied to control how these callbacks are called:
|
|||||||
- .impl.unaligned specifies that the *implementation* supports unaligned
|
- .impl.unaligned specifies that the *implementation* supports unaligned
|
||||||
accesses; if false, unaligned accesses will be emulated by two aligned
|
accesses; if false, unaligned accesses will be emulated by two aligned
|
||||||
accesses.
|
accesses.
|
||||||
- .old_mmio can be used to ease porting from code using
|
- .old_mmio eases the porting of code that was formerly using
|
||||||
cpu_register_io_memory(). It should not be used in new code.
|
cpu_register_io_memory(). It should not be used in new code.
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ doesn't finish in a given time the switch is made to postcopy.
|
|||||||
To enable postcopy, issue this command on the monitor prior to the
|
To enable postcopy, issue this command on the monitor prior to the
|
||||||
start of migration:
|
start of migration:
|
||||||
|
|
||||||
migrate_set_capability x-postcopy-ram on
|
migrate_set_capability postcopy-ram on
|
||||||
|
|
||||||
The normal commands are then used to start a migration, which is still
|
The normal commands are then used to start a migration, which is still
|
||||||
started in precopy mode. Issuing:
|
started in precopy mode. Issuing:
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ A detailed command line would be:
|
|||||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0
|
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0
|
||||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1
|
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1
|
||||||
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd
|
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd
|
||||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8, -device e1000,bus=bridge2,addr=0x3
|
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8 -device e1000,bus=bridge2,addr=0x3
|
||||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40, -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
|
-device pxb,id=bridge3,bus=pci.0,bus_nr=40 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
|
||||||
|
|
||||||
Here you have:
|
Here you have:
|
||||||
- 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes)
|
- 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes)
|
||||||
@@ -43,7 +43,7 @@ Implementation
|
|||||||
==============
|
==============
|
||||||
The PXB is composed by:
|
The PXB is composed by:
|
||||||
- HostBridge (TYPE_PXB_HOST)
|
- HostBridge (TYPE_PXB_HOST)
|
||||||
The host bridge allows to register and query the PXB's rPCI root bus in QEMU.
|
The host bridge allows to register and query the PXB's PCI root bus in QEMU.
|
||||||
- PXBDev(TYPE_PXB_DEVICE)
|
- PXBDev(TYPE_PXB_DEVICE)
|
||||||
It is a regular PCI Device that resides on the piix host-bridge bus and its bus uses the same PCI domain.
|
It is a regular PCI Device that resides on the piix host-bridge bus and its bus uses the same PCI domain.
|
||||||
However, the bus behind is exposed through ACPI as a primary PCI bus and starts a new PCI hierarchy.
|
However, the bus behind is exposed through ACPI as a primary PCI bus and starts a new PCI hierarchy.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
= How to use the QAPI code generator =
|
= How to use the QAPI code generator =
|
||||||
|
|
||||||
Copyright IBM Corp. 2011
|
Copyright IBM Corp. 2011
|
||||||
Copyright (C) 2012-2015 Red Hat, Inc.
|
Copyright (C) 2012-2016 Red Hat, Inc.
|
||||||
|
|
||||||
This work is licensed under the terms of the GNU GPL, version 2 or
|
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
later. See the COPYING file in the top-level directory.
|
later. See the COPYING file in the top-level directory.
|
||||||
@@ -52,7 +52,7 @@ schema. The documentation is delimited between two lines of ##, then
|
|||||||
the first line names the expression, an optional overview is provided,
|
the first line names the expression, an optional overview is provided,
|
||||||
then individual documentation about each member of 'data' is provided,
|
then individual documentation about each member of 'data' is provided,
|
||||||
and finally, a 'Since: x.y.z' tag lists the release that introduced
|
and finally, a 'Since: x.y.z' tag lists the release that introduced
|
||||||
the expression. Optional fields are tagged with the phrase
|
the expression. Optional members are tagged with the phrase
|
||||||
'#optional', often with their default value; and extensions added
|
'#optional', often with their default value; and extensions added
|
||||||
after the expression was first released are also given a '(since
|
after the expression was first released are also given a '(since
|
||||||
x.y.z)' comment. For example:
|
x.y.z)' comment. For example:
|
||||||
@@ -108,15 +108,15 @@ user-defined type names, while built-in types are lowercase. Type
|
|||||||
definitions should not end in 'Kind', as this namespace is used for
|
definitions should not end in 'Kind', as this namespace is used for
|
||||||
creating implicit C enums for visiting union types, or in 'List', as
|
creating implicit C enums for visiting union types, or in 'List', as
|
||||||
this namespace is used for creating array types. Command names,
|
this namespace is used for creating array types. Command names,
|
||||||
and field names within a type, should be all lower case with words
|
and member names within a type, should be all lower case with words
|
||||||
separated by a hyphen. However, some existing older commands and
|
separated by a hyphen. However, some existing older commands and
|
||||||
complex types use underscore; when extending such expressions,
|
complex types use underscore; when extending such expressions,
|
||||||
consistency is preferred over blindly avoiding underscore. Event
|
consistency is preferred over blindly avoiding underscore. Event
|
||||||
names should be ALL_CAPS with words separated by underscore. Field
|
names should be ALL_CAPS with words separated by underscore. Member
|
||||||
names cannot start with 'has-' or 'has_', as this is reserved for
|
names cannot start with 'has-' or 'has_', as this is reserved for
|
||||||
tracking optional fields.
|
tracking optional members.
|
||||||
|
|
||||||
Any name (command, event, type, field, or enum value) beginning with
|
Any name (command, event, type, member, or enum value) beginning with
|
||||||
"x-" is marked experimental, and may be withdrawn or changed
|
"x-" is marked experimental, and may be withdrawn or changed
|
||||||
incompatibly in a future release. All names must begin with a letter,
|
incompatibly in a future release. All names must begin with a letter,
|
||||||
and contain only ASCII letters, digits, dash, and underscore. There
|
and contain only ASCII letters, digits, dash, and underscore. There
|
||||||
@@ -127,7 +127,7 @@ the vendor), even if the rest of the name uses dash (example:
|
|||||||
__com.redhat_drive-mirror). Names beginning with 'q_' are reserved
|
__com.redhat_drive-mirror). Names beginning with 'q_' are reserved
|
||||||
for the generator: QMP names that resemble C keywords or other
|
for the generator: QMP names that resemble C keywords or other
|
||||||
problematic strings will be munged in C to use this prefix. For
|
problematic strings will be munged in C to use this prefix. For
|
||||||
example, a field named "default" in qapi becomes "q_default" in the
|
example, a member named "default" in qapi becomes "q_default" in the
|
||||||
generated C code.
|
generated C code.
|
||||||
|
|
||||||
In the rest of this document, usage lines are given for each
|
In the rest of this document, usage lines are given for each
|
||||||
@@ -217,17 +217,18 @@ and must continue to work).
|
|||||||
|
|
||||||
On output structures (only mentioned in the 'returns' side of a command),
|
On output structures (only mentioned in the 'returns' side of a command),
|
||||||
changing from mandatory to optional is in general unsafe (older clients may be
|
changing from mandatory to optional is in general unsafe (older clients may be
|
||||||
expecting the field, and could crash if it is missing), although it can be done
|
expecting the member, and could crash if it is missing), although it
|
||||||
if the only way that the optional argument will be omitted is when it is
|
can be done if the only way that the optional argument will be omitted
|
||||||
triggered by the presence of a new input flag to the command that older clients
|
is when it is triggered by the presence of a new input flag to the
|
||||||
don't know to send. Changing from optional to mandatory is safe.
|
command that older clients don't know to send. Changing from optional
|
||||||
|
to mandatory is safe.
|
||||||
|
|
||||||
A structure that is used in both input and output of various commands
|
A structure that is used in both input and output of various commands
|
||||||
must consider the backwards compatibility constraints of both directions
|
must consider the backwards compatibility constraints of both directions
|
||||||
of use.
|
of use.
|
||||||
|
|
||||||
A struct definition can specify another struct as its base.
|
A struct definition can specify another struct as its base.
|
||||||
In this case, the fields of the base type are included as top-level fields
|
In this case, the members of the base type are included as top-level members
|
||||||
of the new struct's dictionary in the Client JSON Protocol wire
|
of the new struct's dictionary in the Client JSON Protocol wire
|
||||||
format. An example definition is:
|
format. An example definition is:
|
||||||
|
|
||||||
@@ -237,7 +238,7 @@ format. An example definition is:
|
|||||||
'data': { '*backing': 'str' } }
|
'data': { '*backing': 'str' } }
|
||||||
|
|
||||||
An example BlockdevOptionsGenericCOWFormat object on the wire could use
|
An example BlockdevOptionsGenericCOWFormat object on the wire could use
|
||||||
both fields like this:
|
both members like this:
|
||||||
|
|
||||||
{ "file": "/some/place/my-image",
|
{ "file": "/some/place/my-image",
|
||||||
"backing": "/some/place/my-backing-file" }
|
"backing": "/some/place/my-backing-file" }
|
||||||
@@ -262,7 +263,7 @@ The enum constants will be named by using a heuristic to turn the
|
|||||||
type name into a set of underscore separated words. For the example
|
type name into a set of underscore separated words. For the example
|
||||||
above, 'MyEnum' will turn into 'MY_ENUM' giving a constant name
|
above, 'MyEnum' will turn into 'MY_ENUM' giving a constant name
|
||||||
of 'MY_ENUM_VALUE1' for the first value. If the default heuristic
|
of 'MY_ENUM_VALUE1' for the first value. If the default heuristic
|
||||||
does not result in a desirable name, the optional 'prefix' field
|
does not result in a desirable name, the optional 'prefix' member
|
||||||
can be used when defining the enum.
|
can be used when defining the enum.
|
||||||
|
|
||||||
The enumeration values are passed as strings over the Client JSON
|
The enumeration values are passed as strings over the Client JSON
|
||||||
@@ -275,9 +276,9 @@ converting between strings and enum values. Since the wire format
|
|||||||
always passes by name, it is acceptable to reorder or add new
|
always passes by name, it is acceptable to reorder or add new
|
||||||
enumeration members in any location without breaking clients of Client
|
enumeration members in any location without breaking clients of Client
|
||||||
JSON Protocol; however, removing enum values would break
|
JSON Protocol; however, removing enum values would break
|
||||||
compatibility. For any struct that has a field that will only contain
|
compatibility. For any struct that has a member that will only contain
|
||||||
a finite set of string values, using an enum type for that field is
|
a finite set of string values, using an enum type for that member is
|
||||||
better than open-coding the field to be type 'str'.
|
better than open-coding the member to be type 'str'.
|
||||||
|
|
||||||
|
|
||||||
=== Union types ===
|
=== Union types ===
|
||||||
@@ -305,8 +306,8 @@ values to data types like in this example:
|
|||||||
'qcow2': 'Qcow2Options' } }
|
'qcow2': 'Qcow2Options' } }
|
||||||
|
|
||||||
In the Client JSON Protocol, a simple union is represented by a
|
In the Client JSON Protocol, a simple union is represented by a
|
||||||
dictionary that contains the 'type' field as a discriminator, and a
|
dictionary that contains the 'type' member as a discriminator, and a
|
||||||
'data' field that is of the specified data type corresponding to the
|
'data' member that is of the specified data type corresponding to the
|
||||||
discriminator value, as in these examples:
|
discriminator value, as in these examples:
|
||||||
|
|
||||||
{ "type": "file", "data" : { "filename": "/some/place/my-image" } }
|
{ "type": "file", "data" : { "filename": "/some/place/my-image" } }
|
||||||
@@ -321,14 +322,14 @@ enum. The value for each branch can be of any type.
|
|||||||
|
|
||||||
A flat union definition specifies a struct as its base, and
|
A flat union definition specifies a struct as its base, and
|
||||||
avoids nesting on the wire. All branches of the union must be
|
avoids nesting on the wire. All branches of the union must be
|
||||||
complex types, and the top-level fields of the union dictionary on
|
complex types, and the top-level members of the union dictionary on
|
||||||
the wire will be combination of fields from both the base type and the
|
the wire will be combination of members from both the base type and the
|
||||||
appropriate branch type (when merging two dictionaries, there must be
|
appropriate branch type (when merging two dictionaries, there must be
|
||||||
no keys in common). The 'discriminator' field must be the name of an
|
no keys in common). The 'discriminator' member must be the name of an
|
||||||
enum-typed member of the base struct.
|
enum-typed member of the base struct.
|
||||||
|
|
||||||
The following example enhances the above simple union example by
|
The following example enhances the above simple union example by
|
||||||
adding a common field 'readonly', renaming the discriminator to
|
adding a common member 'readonly', renaming the discriminator to
|
||||||
something more applicable, and reducing the number of {} required on
|
something more applicable, and reducing the number of {} required on
|
||||||
the wire:
|
the wire:
|
||||||
|
|
||||||
@@ -353,8 +354,8 @@ the user, but because it must map to a base member with enum type, the
|
|||||||
code generator can ensure that branches exist for all values of the
|
code generator can ensure that branches exist for all values of the
|
||||||
enum (although the order of the keys need not match the declaration of
|
enum (although the order of the keys need not match the declaration of
|
||||||
the enum). In the resulting generated C data types, a flat union is
|
the enum). In the resulting generated C data types, a flat union is
|
||||||
represented as a struct with the base member fields included directly,
|
represented as a struct with the base members included directly, and
|
||||||
and then a union of structures for each branch of the struct.
|
then a union of structures for each branch of the struct.
|
||||||
|
|
||||||
A simple union can always be re-written as a flat union where the base
|
A simple union can always be re-written as a flat union where the base
|
||||||
class has a single member named 'type', and where each branch of the
|
class has a single member named 'type', and where each branch of the
|
||||||
@@ -424,10 +425,10 @@ string name of a complex type, or a dictionary that declares an
|
|||||||
anonymous type with the same semantics as a 'struct' expression, with
|
anonymous type with the same semantics as a 'struct' expression, with
|
||||||
one exception noted below when 'gen' is used.
|
one exception noted below when 'gen' is used.
|
||||||
|
|
||||||
The 'returns' member describes what will appear in the "return" field
|
The 'returns' member describes what will appear in the "return" member
|
||||||
of a Client JSON Protocol reply on successful completion of a command.
|
of a Client JSON Protocol reply on successful completion of a command.
|
||||||
The member is optional from the command declaration; if absent, the
|
The member is optional from the command declaration; if absent, the
|
||||||
"return" field will be an empty dictionary. If 'returns' is present,
|
"return" member will be an empty dictionary. If 'returns' is present,
|
||||||
it must be the string name of a complex or built-in type, a
|
it must be the string name of a complex or built-in type, a
|
||||||
one-element array containing the name of a complex or built-in type,
|
one-element array containing the name of a complex or built-in type,
|
||||||
with one exception noted below when 'gen' is used. Although it is
|
with one exception noted below when 'gen' is used. Although it is
|
||||||
@@ -435,7 +436,7 @@ permitted to have the 'returns' member name a built-in type or an
|
|||||||
array of built-in types, any command that does this cannot be extended
|
array of built-in types, any command that does this cannot be extended
|
||||||
to return additional information in the future; thus, new commands
|
to return additional information in the future; thus, new commands
|
||||||
should strongly consider returning a dictionary-based type or an array
|
should strongly consider returning a dictionary-based type or an array
|
||||||
of dictionaries, even if the dictionary only contains one field at the
|
of dictionaries, even if the dictionary only contains one member at the
|
||||||
present.
|
present.
|
||||||
|
|
||||||
All commands in Client JSON Protocol use a dictionary to report
|
All commands in Client JSON Protocol use a dictionary to report
|
||||||
@@ -478,7 +479,7 @@ response is not possible (although the command will still return a
|
|||||||
normal dictionary error on failure). When a successful reply is not
|
normal dictionary error on failure). When a successful reply is not
|
||||||
possible, the command expression should include the optional key
|
possible, the command expression should include the optional key
|
||||||
'success-response' with boolean value false. So far, only QGA makes
|
'success-response' with boolean value false. So far, only QGA makes
|
||||||
use of this field.
|
use of this member.
|
||||||
|
|
||||||
|
|
||||||
=== Events ===
|
=== Events ===
|
||||||
@@ -656,7 +657,7 @@ Union types
|
|||||||
|
|
||||||
{ "name": "BlockdevOptions", "meta-type": "object",
|
{ "name": "BlockdevOptions", "meta-type": "object",
|
||||||
"members": [
|
"members": [
|
||||||
{ "name": "kind", "type": "BlockdevOptionsKind" } ],
|
{ "name": "type", "type": "BlockdevOptionsKind" } ],
|
||||||
"tag": "type",
|
"tag": "type",
|
||||||
"variants": [
|
"variants": [
|
||||||
{ "case": "file", "type": ":obj-FileOptions-wrapper" },
|
{ "case": "file", "type": ":obj-FileOptions-wrapper" },
|
||||||
@@ -722,33 +723,38 @@ the names of built-in types. Clients should examine member
|
|||||||
|
|
||||||
== Code generation ==
|
== Code generation ==
|
||||||
|
|
||||||
Schemas are fed into four scripts to generate all the code/files that,
|
Schemas are fed into five scripts to generate all the code/files that,
|
||||||
paired with the core QAPI libraries, comprise everything required to
|
paired with the core QAPI libraries, comprise everything required to
|
||||||
take JSON commands read in by a Client JSON Protocol server, unmarshal
|
take JSON commands read in by a Client JSON Protocol server, unmarshal
|
||||||
the arguments into the underlying C types, call into the corresponding
|
the arguments into the underlying C types, call into the corresponding
|
||||||
C function, and map the response back to a Client JSON Protocol
|
C function, map the response back to a Client JSON Protocol response
|
||||||
response to be returned to the user.
|
to be returned to the user, and introspect the commands.
|
||||||
|
|
||||||
As an example, we'll use the following schema, which describes a single
|
As an example, we'll use the following schema, which describes a
|
||||||
complex user-defined type (which will produce a C struct, along with a list
|
single complex user-defined type, along with command which takes a
|
||||||
node structure that can be used to chain together a list of such types in
|
list of that type as a parameter, and returns a single element of that
|
||||||
case we want to accept/return a list of this type with a command), and a
|
type. The user is responsible for writing the implementation of
|
||||||
command which takes that type as a parameter and returns the same type:
|
qmp_my_command(); everything else is produced by the generator.
|
||||||
|
|
||||||
$ cat example-schema.json
|
$ cat example-schema.json
|
||||||
{ 'struct': 'UserDefOne',
|
{ 'struct': 'UserDefOne',
|
||||||
'data': { 'integer': 'int', 'string': 'str' } }
|
'data': { 'integer': 'int', '*string': 'str' } }
|
||||||
|
|
||||||
{ 'command': 'my-command',
|
{ 'command': 'my-command',
|
||||||
'data': {'arg1': 'UserDefOne'},
|
'data': { 'arg1': ['UserDefOne'] },
|
||||||
'returns': 'UserDefOne' }
|
'returns': 'UserDefOne' }
|
||||||
|
|
||||||
{ 'event': 'MY_EVENT' }
|
{ 'event': 'MY_EVENT' }
|
||||||
|
|
||||||
|
For a more thorough look at generated code, the testsuite includes
|
||||||
|
tests/qapi-schema/qapi-schema-tests.json that covers more examples of
|
||||||
|
what the generator will accept, and compiles the resulting C code as
|
||||||
|
part of 'make check-unit'.
|
||||||
|
|
||||||
=== scripts/qapi-types.py ===
|
=== scripts/qapi-types.py ===
|
||||||
|
|
||||||
Used to generate the C types defined by a schema. The following files are
|
Used to generate the C types defined by a schema, along with
|
||||||
created:
|
supporting code. The following files are created:
|
||||||
|
|
||||||
$(prefix)qapi-types.h - C types corresponding to types defined in
|
$(prefix)qapi-types.h - C types corresponding to types defined in
|
||||||
the schema you pass in
|
the schema you pass in
|
||||||
@@ -763,38 +769,6 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-types.py --output-dir="qapi-generated" \
|
$ python scripts/qapi-types.py --output-dir="qapi-generated" \
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
$ cat qapi-generated/example-qapi-types.c
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
void qapi_free_UserDefOne(UserDefOne *obj)
|
|
||||||
{
|
|
||||||
QapiDeallocVisitor *qdv;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qdv = qapi_dealloc_visitor_new();
|
|
||||||
v = qapi_dealloc_get_visitor(qdv);
|
|
||||||
visit_type_UserDefOne(v, &obj, NULL, NULL);
|
|
||||||
qapi_dealloc_visitor_cleanup(qdv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void qapi_free_UserDefOneList(UserDefOneList *obj)
|
|
||||||
{
|
|
||||||
QapiDeallocVisitor *qdv;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qdv = qapi_dealloc_visitor_new();
|
|
||||||
v = qapi_dealloc_get_visitor(qdv);
|
|
||||||
visit_type_UserDefOneList(v, &obj, NULL, NULL);
|
|
||||||
qapi_dealloc_visitor_cleanup(qdv);
|
|
||||||
}
|
|
||||||
$ cat qapi-generated/example-qapi-types.h
|
$ cat qapi-generated/example-qapi-types.h
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
@@ -809,29 +783,59 @@ Example:
|
|||||||
|
|
||||||
struct UserDefOne {
|
struct UserDefOne {
|
||||||
int64_t integer;
|
int64_t integer;
|
||||||
|
bool has_string;
|
||||||
char *string;
|
char *string;
|
||||||
};
|
};
|
||||||
|
|
||||||
void qapi_free_UserDefOne(UserDefOne *obj);
|
void qapi_free_UserDefOne(UserDefOne *obj);
|
||||||
|
|
||||||
struct UserDefOneList {
|
struct UserDefOneList {
|
||||||
union {
|
|
||||||
UserDefOne *value;
|
|
||||||
uint64_t padding;
|
|
||||||
};
|
|
||||||
UserDefOneList *next;
|
UserDefOneList *next;
|
||||||
|
UserDefOne *value;
|
||||||
};
|
};
|
||||||
|
|
||||||
void qapi_free_UserDefOneList(UserDefOneList *obj);
|
void qapi_free_UserDefOneList(UserDefOneList *obj);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
$ cat qapi-generated/example-qapi-types.c
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
void qapi_free_UserDefOne(UserDefOne *obj)
|
||||||
|
{
|
||||||
|
QapiDeallocVisitor *qdv;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdv = qapi_dealloc_visitor_new();
|
||||||
|
v = qapi_dealloc_get_visitor(qdv);
|
||||||
|
visit_type_UserDefOne(v, NULL, &obj, NULL);
|
||||||
|
qapi_dealloc_visitor_cleanup(qdv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qapi_free_UserDefOneList(UserDefOneList *obj)
|
||||||
|
{
|
||||||
|
QapiDeallocVisitor *qdv;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdv = qapi_dealloc_visitor_new();
|
||||||
|
v = qapi_dealloc_get_visitor(qdv);
|
||||||
|
visit_type_UserDefOneList(v, NULL, &obj, NULL);
|
||||||
|
qapi_dealloc_visitor_cleanup(qdv);
|
||||||
|
}
|
||||||
|
|
||||||
=== scripts/qapi-visit.py ===
|
=== scripts/qapi-visit.py ===
|
||||||
|
|
||||||
Used to generate the visitor functions used to walk through and convert
|
Used to generate the visitor functions used to walk through and
|
||||||
a QObject (as provided by QMP) to a native C data structure and
|
convert between a native QAPI C data structure and some other format
|
||||||
vice-versa, as well as the visitor function used to dealloc a complex
|
(such as QObject); the generated functions are named visit_type_FOO()
|
||||||
schema-defined C type.
|
and visit_type_FOO_members().
|
||||||
|
|
||||||
The following files are generated:
|
The following files are generated:
|
||||||
|
|
||||||
@@ -848,41 +852,62 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-visit.py --output-dir="qapi-generated"
|
$ python scripts/qapi-visit.py --output-dir="qapi-generated"
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
|
$ cat qapi-generated/example-qapi-visit.h
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
#ifndef EXAMPLE_QAPI_VISIT_H
|
||||||
|
#define EXAMPLE_QAPI_VISIT_H
|
||||||
|
|
||||||
|
[Visitors for built-in types omitted...]
|
||||||
|
|
||||||
|
void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp);
|
||||||
|
void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp);
|
||||||
|
void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp);
|
||||||
|
|
||||||
|
#endif
|
||||||
$ cat qapi-generated/example-qapi-visit.c
|
$ cat qapi-generated/example-qapi-visit.c
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
static void visit_type_UserDefOne_fields(Visitor *v, UserDefOne **obj, Error **errp)
|
void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
visit_type_int(v, &(*obj)->integer, "integer", &err);
|
visit_type_int(v, "integer", &obj->integer, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
visit_type_str(v, &(*obj)->string, "string", &err);
|
if (visit_optional(v, "string", &obj->has_string)) {
|
||||||
|
visit_type_str(v, "string", &obj->string, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_type_UserDefOne(Visitor *v, UserDefOne **obj, const char *name, Error **errp)
|
void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
visit_start_struct(v, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err);
|
visit_start_struct(v, name, (void **)obj, sizeof(UserDefOne), &err);
|
||||||
if (!err) {
|
if (err) {
|
||||||
if (*obj) {
|
goto out;
|
||||||
visit_type_UserDefOne_fields(v, obj, errp);
|
|
||||||
}
|
}
|
||||||
|
if (!*obj) {
|
||||||
|
goto out_obj;
|
||||||
|
}
|
||||||
|
visit_type_UserDefOne_members(v, *obj, &err);
|
||||||
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
|
out_obj:
|
||||||
visit_end_struct(v, &err);
|
visit_end_struct(v, &err);
|
||||||
}
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_type_UserDefOneList(Visitor *v, UserDefOneList **obj, const char *name, Error **errp)
|
void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
GenericList *i, **prev;
|
GenericList *i, **prev;
|
||||||
@@ -893,35 +918,24 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (prev = (GenericList **)obj;
|
for (prev = (GenericList **)obj;
|
||||||
!err && (i = visit_next_list(v, prev, &err)) != NULL;
|
!err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
|
||||||
prev = &i) {
|
prev = &i) {
|
||||||
UserDefOneList *native_i = (UserDefOneList *)i;
|
UserDefOneList *native_i = (UserDefOneList *)i;
|
||||||
visit_type_UserDefOne(v, &native_i->value, NULL, &err);
|
visit_type_UserDefOne(v, NULL, &native_i->value, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_propagate(errp, err);
|
visit_end_list(v);
|
||||||
err = NULL;
|
|
||||||
visit_end_list(v, &err);
|
|
||||||
out:
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
$ cat qapi-generated/example-qapi-visit.h
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
#ifndef EXAMPLE_QAPI_VISIT_H
|
|
||||||
#define EXAMPLE_QAPI_VISIT_H
|
|
||||||
|
|
||||||
[Visitors for built-in types omitted...]
|
|
||||||
|
|
||||||
void visit_type_UserDefOne(Visitor *v, UserDefOne **obj, const char *name, Error **errp);
|
|
||||||
void visit_type_UserDefOneList(Visitor *v, UserDefOneList **obj, const char *name, Error **errp);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
=== scripts/qapi-commands.py ===
|
=== scripts/qapi-commands.py ===
|
||||||
|
|
||||||
Used to generate the marshaling/dispatch functions for the commands defined
|
Used to generate the marshaling/dispatch functions for the commands
|
||||||
in the schema. The following files are generated:
|
defined in the schema. The generated code implements
|
||||||
|
qmp_marshal_COMMAND() (mentioned in qmp-commands.hx, and registered
|
||||||
|
automatically), and declares qmp_COMMAND() that the user must
|
||||||
|
implement. The following files are generated:
|
||||||
|
|
||||||
$(prefix)qmp-marshal.c: command marshal/dispatch functions for each
|
$(prefix)qmp-marshal.c: command marshal/dispatch functions for each
|
||||||
QMP command defined in the schema. Functions
|
QMP command defined in the schema. Functions
|
||||||
@@ -939,6 +953,19 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-commands.py --output-dir="qapi-generated"
|
$ python scripts/qapi-commands.py --output-dir="qapi-generated"
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
|
$ cat qapi-generated/example-qmp-commands.h
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
#ifndef EXAMPLE_QMP_COMMANDS_H
|
||||||
|
#define EXAMPLE_QMP_COMMANDS_H
|
||||||
|
|
||||||
|
#include "example-qapi-types.h"
|
||||||
|
#include "qapi/qmp/qdict.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
|
UserDefOne *qmp_my_command(UserDefOneList *arg1, Error **errp);
|
||||||
|
|
||||||
|
#endif
|
||||||
$ cat qapi-generated/example-qmp-marshal.c
|
$ cat qapi-generated/example-qmp-marshal.c
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
@@ -950,7 +977,7 @@ Example:
|
|||||||
Visitor *v;
|
Visitor *v;
|
||||||
|
|
||||||
v = qmp_output_get_visitor(qov);
|
v = qmp_output_get_visitor(qov);
|
||||||
visit_type_UserDefOne(v, &ret_in, "unused", &err);
|
visit_type_UserDefOne(v, "unused", &ret_in, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -961,7 +988,7 @@ Example:
|
|||||||
qmp_output_visitor_cleanup(qov);
|
qmp_output_visitor_cleanup(qov);
|
||||||
qdv = qapi_dealloc_visitor_new();
|
qdv = qapi_dealloc_visitor_new();
|
||||||
v = qapi_dealloc_get_visitor(qdv);
|
v = qapi_dealloc_get_visitor(qdv);
|
||||||
visit_type_UserDefOne(v, &ret_in, "unused", NULL);
|
visit_type_UserDefOne(v, "unused", &ret_in, NULL);
|
||||||
qapi_dealloc_visitor_cleanup(qdv);
|
qapi_dealloc_visitor_cleanup(qdv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,10 +999,10 @@ Example:
|
|||||||
QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
|
QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
|
||||||
QapiDeallocVisitor *qdv;
|
QapiDeallocVisitor *qdv;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
UserDefOne *arg1 = NULL;
|
UserDefOneList *arg1 = NULL;
|
||||||
|
|
||||||
v = qmp_input_get_visitor(qiv);
|
v = qmp_input_get_visitor(qiv);
|
||||||
visit_type_UserDefOne(v, &arg1, "arg1", &err);
|
visit_type_UserDefOneList(v, "arg1", &arg1, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -992,7 +1019,7 @@ Example:
|
|||||||
qmp_input_visitor_cleanup(qiv);
|
qmp_input_visitor_cleanup(qiv);
|
||||||
qdv = qapi_dealloc_visitor_new();
|
qdv = qapi_dealloc_visitor_new();
|
||||||
v = qapi_dealloc_get_visitor(qdv);
|
v = qapi_dealloc_get_visitor(qdv);
|
||||||
visit_type_UserDefOne(v, &arg1, "arg1", NULL);
|
visit_type_UserDefOneList(v, "arg1", &arg1, NULL);
|
||||||
qapi_dealloc_visitor_cleanup(qdv);
|
qapi_dealloc_visitor_cleanup(qdv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,24 +1029,12 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
qapi_init(qmp_init_marshal);
|
qapi_init(qmp_init_marshal);
|
||||||
$ cat qapi-generated/example-qmp-commands.h
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
#ifndef EXAMPLE_QMP_COMMANDS_H
|
|
||||||
#define EXAMPLE_QMP_COMMANDS_H
|
|
||||||
|
|
||||||
#include "example-qapi-types.h"
|
|
||||||
#include "qapi/qmp/qdict.h"
|
|
||||||
#include "qapi/error.h"
|
|
||||||
|
|
||||||
UserDefOne *qmp_my_command(UserDefOne *arg1, Error **errp);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
=== scripts/qapi-event.py ===
|
=== scripts/qapi-event.py ===
|
||||||
|
|
||||||
Used to generate the event-related C code defined by a schema. The
|
Used to generate the event-related C code defined by a schema, with
|
||||||
following files are created:
|
implementations for qapi_event_send_FOO(). The following files are
|
||||||
|
created:
|
||||||
|
|
||||||
$(prefix)qapi-event.h - Function prototypes for each event type, plus an
|
$(prefix)qapi-event.h - Function prototypes for each event type, plus an
|
||||||
enumeration of all event names
|
enumeration of all event names
|
||||||
@@ -1029,6 +1044,27 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-event.py --output-dir="qapi-generated"
|
$ python scripts/qapi-event.py --output-dir="qapi-generated"
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
|
$ cat qapi-generated/example-qapi-event.h
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
#ifndef EXAMPLE_QAPI_EVENT_H
|
||||||
|
#define EXAMPLE_QAPI_EVENT_H
|
||||||
|
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/qmp/qdict.h"
|
||||||
|
#include "example-qapi-types.h"
|
||||||
|
|
||||||
|
|
||||||
|
void qapi_event_send_my_event(Error **errp);
|
||||||
|
|
||||||
|
typedef enum example_QAPIEvent {
|
||||||
|
EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
|
||||||
|
EXAMPLE_QAPI_EVENT__MAX = 1,
|
||||||
|
} example_QAPIEvent;
|
||||||
|
|
||||||
|
extern const char *const example_QAPIEvent_lookup[];
|
||||||
|
|
||||||
|
#endif
|
||||||
$ cat qapi-generated/example-qapi-event.c
|
$ cat qapi-generated/example-qapi-event.c
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
@@ -1054,27 +1090,6 @@ Example:
|
|||||||
[EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
|
[EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
|
||||||
[EXAMPLE_QAPI_EVENT__MAX] = NULL,
|
[EXAMPLE_QAPI_EVENT__MAX] = NULL,
|
||||||
};
|
};
|
||||||
$ cat qapi-generated/example-qapi-event.h
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
#ifndef EXAMPLE_QAPI_EVENT_H
|
|
||||||
#define EXAMPLE_QAPI_EVENT_H
|
|
||||||
|
|
||||||
#include "qapi/error.h"
|
|
||||||
#include "qapi/qmp/qdict.h"
|
|
||||||
#include "example-qapi-types.h"
|
|
||||||
|
|
||||||
|
|
||||||
void qapi_event_send_my_event(Error **errp);
|
|
||||||
|
|
||||||
typedef enum example_QAPIEvent {
|
|
||||||
EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
|
|
||||||
EXAMPLE_QAPI_EVENT__MAX = 1,
|
|
||||||
} example_QAPIEvent;
|
|
||||||
|
|
||||||
extern const char *const example_QAPIEvent_lookup[];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
=== scripts/qapi-introspect.py ===
|
=== scripts/qapi-introspect.py ===
|
||||||
|
|
||||||
@@ -1089,17 +1104,6 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-introspect.py --output-dir="qapi-generated"
|
$ python scripts/qapi-introspect.py --output-dir="qapi-generated"
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
$ cat qapi-generated/example-qmp-introspect.c
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
const char example_qmp_schema_json[] = "["
|
|
||||||
"{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
|
|
||||||
"{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
|
|
||||||
"{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
|
|
||||||
"{\"members\": [{\"name\": \"arg1\", \"type\": \"2\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
|
|
||||||
"{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
|
|
||||||
"{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
|
|
||||||
"{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
|
|
||||||
$ cat qapi-generated/example-qmp-introspect.h
|
$ cat qapi-generated/example-qmp-introspect.h
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
@@ -1109,3 +1113,15 @@ Example:
|
|||||||
extern const char example_qmp_schema_json[];
|
extern const char example_qmp_schema_json[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
$ cat qapi-generated/example-qmp-introspect.c
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
const char example_qmp_schema_json[] = "["
|
||||||
|
"{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
|
||||||
|
"{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
|
||||||
|
"{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
|
||||||
|
"{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
|
||||||
|
"{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
|
||||||
|
"{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
|
||||||
|
"{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
|
||||||
|
"{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
|
||||||
|
|||||||
@@ -325,6 +325,7 @@ Emitted to report a corruption of a Quorum file.
|
|||||||
|
|
||||||
Data:
|
Data:
|
||||||
|
|
||||||
|
- "type": Quorum operation type
|
||||||
- "error": Error message (json-string, optional)
|
- "error": Error message (json-string, optional)
|
||||||
Only present on failure. This field contains a human-readable
|
Only present on failure. This field contains a human-readable
|
||||||
error message. There are no semantics other than that the
|
error message. There are no semantics other than that the
|
||||||
@@ -336,10 +337,18 @@ Data:
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
Read operation:
|
||||||
{ "event": "QUORUM_REPORT_BAD",
|
{ "event": "QUORUM_REPORT_BAD",
|
||||||
"data": { "node-name": "1.raw", "sector-num": 345435, "sectors-count": 5 },
|
"data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
|
||||||
|
"type": "read" },
|
||||||
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
|
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
|
||||||
|
|
||||||
|
Flush operation:
|
||||||
|
{ "event": "QUORUM_REPORT_BAD",
|
||||||
|
"data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
|
||||||
|
"type": "flush", "error": "Broken pipe" },
|
||||||
|
"timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
|
||||||
|
|
||||||
Note: this event is rate-limited.
|
Note: this event is rate-limited.
|
||||||
|
|
||||||
RESET
|
RESET
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
0. About This Document
|
0. About This Document
|
||||||
======================
|
======================
|
||||||
|
|
||||||
Copyright (C) 2009-2015 Red Hat, Inc.
|
Copyright (C) 2009-2016 Red Hat, Inc.
|
||||||
|
|
||||||
This work is licensed under the terms of the GNU GPL, version 2 or
|
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
later. See the COPYING file in the top-level directory.
|
later. See the COPYING file in the top-level directory.
|
||||||
@@ -277,7 +277,7 @@ However, Clients must not assume any particular:
|
|||||||
- Amount of errors generated by a command, that is, new errors can be added
|
- Amount of errors generated by a command, that is, new errors can be added
|
||||||
to any existing command in newer versions of the Server
|
to any existing command in newer versions of the Server
|
||||||
|
|
||||||
Any command or field name beginning with "x-" is deemed experimental,
|
Any command or member name beginning with "x-" is deemed experimental,
|
||||||
and may be withdrawn or changed in an incompatible manner in a future
|
and may be withdrawn or changed in an incompatible manner in a future
|
||||||
release.
|
release.
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ at the specified moments of time. There are several kinds of timers:
|
|||||||
sources (e.g. real time clock chip). Host clock is the one of the sources
|
sources (e.g. real time clock chip). Host clock is the one of the sources
|
||||||
of non-determinism. Host clock read operations should be logged to
|
of non-determinism. Host clock read operations should be logged to
|
||||||
make the execution deterministic.
|
make the execution deterministic.
|
||||||
* Real time clock for icount. This clock is similar to real time clock but
|
* Virtual real time clock. This clock is similar to real time clock but
|
||||||
it is used only for increasing virtual clock while virtual machine is
|
it is used only for increasing virtual clock while virtual machine is
|
||||||
sleeping. Due to its nature it is also non-deterministic as the host clock
|
sleeping. Due to its nature it is also non-deterministic as the host clock
|
||||||
and has to be logged too.
|
and has to be logged too.
|
||||||
@@ -134,11 +134,20 @@ of time. That's why we do not process a group of timers until the checkpoint
|
|||||||
event will be read from the log. Such an event allows synchronizing CPU
|
event will be read from the log. Such an event allows synchronizing CPU
|
||||||
execution and timer events.
|
execution and timer events.
|
||||||
|
|
||||||
Another checkpoints application in record/replay is instruction counting
|
Two other checkpoints govern the "warping" of the virtual clock.
|
||||||
while the virtual machine is idle. This function (qemu_clock_warp) is called
|
While the virtual machine is idle, the virtual clock increments at
|
||||||
from the wait loop. It changes virtual machine state and must be deterministic
|
1 ns per *real time* nanosecond. This is done by setting up a timer
|
||||||
then. That is why we added checkpoint to this function to prevent its
|
(called the warp timer) on the virtual real time clock, so that the
|
||||||
operation in replay mode when it does not correspond to record mode.
|
timer fires at the next deadline of the virtual clock; the virtual clock
|
||||||
|
is then incremented (which is called "warping" the virtual clock) as
|
||||||
|
soon as the timer fires or the CPUs need to go out of the idle state.
|
||||||
|
Two functions are used for this purpose; because these actions change
|
||||||
|
virtual machine state and must be deterministic, each of them creates a
|
||||||
|
checkpoint. qemu_start_warp_timer checks if the CPUs are idle and if so
|
||||||
|
starts accounting real time to virtual clock. qemu_account_warp_timer
|
||||||
|
is called when the CPUs get an interrupt or when the warp timer fires,
|
||||||
|
and it warps the virtual clock by the amount of real time that has passed
|
||||||
|
since qemu_start_warp_timer.
|
||||||
|
|
||||||
Bottom halves
|
Bottom halves
|
||||||
-------------
|
-------------
|
||||||
|
|||||||
@@ -84,6 +84,15 @@ Selector Register address: Base + 8 (2 bytes)
|
|||||||
Data Register address: Base + 0 (8 bytes)
|
Data Register address: Base + 0 (8 bytes)
|
||||||
DMA Address address: Base + 16 (8 bytes)
|
DMA Address address: Base + 16 (8 bytes)
|
||||||
|
|
||||||
|
== ACPI Interface ==
|
||||||
|
|
||||||
|
The fw_cfg device is defined with ACPI ID "QEMU0002". Since we expect
|
||||||
|
ACPI tables to be passed into the guest through the fw_cfg device itself,
|
||||||
|
the guest-side firmware can not use ACPI to find fw_cfg. However, once the
|
||||||
|
firmware is finished setting up ACPI tables and hands control over to the
|
||||||
|
guest kernel, the latter can use the fw_cfg ACPI node for a more accurate
|
||||||
|
inventory of in-use IOport or MMIO regions.
|
||||||
|
|
||||||
== Firmware Configuration Items ==
|
== Firmware Configuration Items ==
|
||||||
|
|
||||||
=== Signature (Key 0x0000, FW_CFG_SIGNATURE) ===
|
=== Signature (Key 0x0000, FW_CFG_SIGNATURE) ===
|
||||||
|
|||||||
@@ -15,13 +15,23 @@ The 1000 -> 10ff device ID range is used as follows for virtio-pci devices.
|
|||||||
Note that this allocation separate from the virtio device IDs, which are
|
Note that this allocation separate from the virtio device IDs, which are
|
||||||
maintained as part of the virtio specification.
|
maintained as part of the virtio specification.
|
||||||
|
|
||||||
1af4:1000 network device
|
1af4:1000 network device (legacy)
|
||||||
1af4:1001 block device
|
1af4:1001 block device (legacy)
|
||||||
1af4:1002 balloon device
|
1af4:1002 balloon device (legacy)
|
||||||
1af4:1003 console device
|
1af4:1003 console device (legacy)
|
||||||
1af4:1004 SCSI host bus adapter device
|
1af4:1004 SCSI host bus adapter device (legacy)
|
||||||
1af4:1005 entropy generator device
|
1af4:1005 entropy generator device (legacy)
|
||||||
1af4:1009 9p filesystem device
|
1af4:1009 9p filesystem device (legacy)
|
||||||
|
|
||||||
|
1af4:1041 network device (modern)
|
||||||
|
1af4:1042 block device (modern)
|
||||||
|
1af4:1043 console device (modern)
|
||||||
|
1af4:1044 entropy generator device (modern)
|
||||||
|
1af4:1045 balloon device (modern)
|
||||||
|
1af4:1048 SCSI host bus adapter device (modern)
|
||||||
|
1af4:1049 9p filesystem device (modern)
|
||||||
|
1af4:1050 virtio gpu device (modern)
|
||||||
|
1af4:1052 virtio input device (modern)
|
||||||
|
|
||||||
1af4:10f0 Available for experimental usage without registration. Must get
|
1af4:10f0 Available for experimental usage without registration. Must get
|
||||||
to official ID when the code leaves the test lab (i.e. when seeking
|
to official ID when the code leaves the test lab (i.e. when seeking
|
||||||
|
|||||||
@@ -172,9 +172,6 @@ source tree. It may not be as powerful as platform-specific or third-party
|
|||||||
trace backends but it is portable. This is the recommended trace backend
|
trace backends but it is portable. This is the recommended trace backend
|
||||||
unless you have specific needs for more advanced backends.
|
unless you have specific needs for more advanced backends.
|
||||||
|
|
||||||
The "simple" backend currently does not capture string arguments, it simply
|
|
||||||
records the char* pointer value instead of the string that is pointed to.
|
|
||||||
|
|
||||||
=== Ftrace ===
|
=== Ftrace ===
|
||||||
|
|
||||||
The "ftrace" backend writes trace data to ftrace marker. This effectively
|
The "ftrace" backend writes trace data to ftrace marker. This effectively
|
||||||
@@ -347,3 +344,44 @@ This will immediately call:
|
|||||||
and will generate the TCG code to call:
|
and will generate the TCG code to call:
|
||||||
|
|
||||||
void trace_foo(uint8_t a1, uint32_t a2);
|
void trace_foo(uint8_t a1, uint32_t a2);
|
||||||
|
|
||||||
|
=== "vcpu" ===
|
||||||
|
|
||||||
|
Identifies events that trace vCPU-specific information. It implicitly adds a
|
||||||
|
"CPUState*" argument, and extends the tracing print format to show the vCPU
|
||||||
|
information. If used together with the "tcg" property, it adds a second
|
||||||
|
"TCGv_env" argument that must point to the per-target global TCG register that
|
||||||
|
points to the vCPU when guest code is executed (usually the "cpu_env" variable).
|
||||||
|
|
||||||
|
The following example events:
|
||||||
|
|
||||||
|
foo(uint32_t a) "a=%x"
|
||||||
|
vcpu bar(uint32_t a) "a=%x"
|
||||||
|
tcg vcpu baz(uint32_t a) "a=%x", "a=%x"
|
||||||
|
|
||||||
|
Can be used as:
|
||||||
|
|
||||||
|
#include "trace-tcg.h"
|
||||||
|
|
||||||
|
CPUArchState *env;
|
||||||
|
TCGv_ptr cpu_env;
|
||||||
|
|
||||||
|
void some_disassembly_func(...)
|
||||||
|
{
|
||||||
|
/* trace emitted at this point */
|
||||||
|
trace_foo(0xd1);
|
||||||
|
/* trace emitted at this point */
|
||||||
|
trace_bar(ENV_GET_CPU(env), 0xd2);
|
||||||
|
/* trace emitted at this point (env) and when guest code is executed (cpu_env) */
|
||||||
|
trace_baz_tcg(ENV_GET_CPU(env), cpu_env, 0xd3);
|
||||||
|
}
|
||||||
|
|
||||||
|
If the translating vCPU has address 0xc1 and code is later executed by vCPU
|
||||||
|
0xc2, this would be an example output:
|
||||||
|
|
||||||
|
// at guest code translation
|
||||||
|
foo a=0xd1
|
||||||
|
bar cpu=0xc1 a=0xd2
|
||||||
|
baz_trans cpu=0xc1 a=0xd3
|
||||||
|
// at guest code execution
|
||||||
|
baz_exec cpu=0xc2 a=0xd3
|
||||||
|
|||||||
180
exec.c
180
exec.c
@@ -135,6 +135,7 @@ typedef struct PhysPageMap {
|
|||||||
struct AddressSpaceDispatch {
|
struct AddressSpaceDispatch {
|
||||||
struct rcu_head rcu;
|
struct rcu_head rcu;
|
||||||
|
|
||||||
|
MemoryRegionSection *mru_section;
|
||||||
/* This is a multi-level map on the physical address space.
|
/* This is a multi-level map on the physical address space.
|
||||||
* The bottom level has pointers to MemoryRegionSections.
|
* The bottom level has pointers to MemoryRegionSections.
|
||||||
*/
|
*/
|
||||||
@@ -307,6 +308,17 @@ static void phys_page_compact_all(AddressSpaceDispatch *d, int nodes_nb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool section_covers_addr(const MemoryRegionSection *section,
|
||||||
|
hwaddr addr)
|
||||||
|
{
|
||||||
|
/* Memory topology clips a memory region to [0, 2^64); size.hi > 0 means
|
||||||
|
* the section must cover the entire address space.
|
||||||
|
*/
|
||||||
|
return section->size.hi ||
|
||||||
|
range_covers_byte(section->offset_within_address_space,
|
||||||
|
section->size.lo, addr);
|
||||||
|
}
|
||||||
|
|
||||||
static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr,
|
static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr,
|
||||||
Node *nodes, MemoryRegionSection *sections)
|
Node *nodes, MemoryRegionSection *sections)
|
||||||
{
|
{
|
||||||
@@ -322,9 +334,7 @@ static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr,
|
|||||||
lp = p[(index >> (i * P_L2_BITS)) & (P_L2_SIZE - 1)];
|
lp = p[(index >> (i * P_L2_BITS)) & (P_L2_SIZE - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sections[lp.ptr].size.hi ||
|
if (section_covers_addr(§ions[lp.ptr], addr)) {
|
||||||
range_covers_byte(sections[lp.ptr].offset_within_address_space,
|
|
||||||
sections[lp.ptr].size.lo, addr)) {
|
|
||||||
return §ions[lp.ptr];
|
return §ions[lp.ptr];
|
||||||
} else {
|
} else {
|
||||||
return §ions[PHYS_SECTION_UNASSIGNED];
|
return §ions[PHYS_SECTION_UNASSIGNED];
|
||||||
@@ -342,14 +352,25 @@ static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d,
|
|||||||
hwaddr addr,
|
hwaddr addr,
|
||||||
bool resolve_subpage)
|
bool resolve_subpage)
|
||||||
{
|
{
|
||||||
MemoryRegionSection *section;
|
MemoryRegionSection *section = atomic_read(&d->mru_section);
|
||||||
subpage_t *subpage;
|
subpage_t *subpage;
|
||||||
|
bool update;
|
||||||
|
|
||||||
section = phys_page_find(d->phys_map, addr, d->map.nodes, d->map.sections);
|
if (section && section != &d->map.sections[PHYS_SECTION_UNASSIGNED] &&
|
||||||
|
section_covers_addr(section, addr)) {
|
||||||
|
update = false;
|
||||||
|
} else {
|
||||||
|
section = phys_page_find(d->phys_map, addr, d->map.nodes,
|
||||||
|
d->map.sections);
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
if (resolve_subpage && section->mr->subpage) {
|
if (resolve_subpage && section->mr->subpage) {
|
||||||
subpage = container_of(section->mr, subpage_t, iomem);
|
subpage = container_of(section->mr, subpage_t, iomem);
|
||||||
section = &d->map.sections[subpage->sub_section[SUBPAGE_IDX(addr)]];
|
section = &d->map.sections[subpage->sub_section[SUBPAGE_IDX(addr)]];
|
||||||
}
|
}
|
||||||
|
if (update) {
|
||||||
|
atomic_set(&d->mru_section, section);
|
||||||
|
}
|
||||||
return section;
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1207,64 +1228,40 @@ void qemu_mutex_unlock_ramlist(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
||||||
#include <sys/vfs.h>
|
|
||||||
|
|
||||||
#define HUGETLBFS_MAGIC 0x958458f6
|
|
||||||
|
|
||||||
static long gethugepagesize(const char *path, Error **errp)
|
|
||||||
{
|
|
||||||
struct statfs fs;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
do {
|
|
||||||
ret = statfs(path, &fs);
|
|
||||||
} while (ret != 0 && errno == EINTR);
|
|
||||||
|
|
||||||
if (ret != 0) {
|
|
||||||
error_setg_errno(errp, errno, "failed to get page size of file %s",
|
|
||||||
path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fs.f_bsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *file_ram_alloc(RAMBlock *block,
|
static void *file_ram_alloc(RAMBlock *block,
|
||||||
ram_addr_t memory,
|
ram_addr_t memory,
|
||||||
const char *path,
|
const char *path,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
struct stat st;
|
bool unlink_on_error = false;
|
||||||
char *filename;
|
char *filename;
|
||||||
char *sanitized_name;
|
char *sanitized_name;
|
||||||
char *c;
|
char *c;
|
||||||
void *area;
|
void *area;
|
||||||
int fd;
|
int fd;
|
||||||
uint64_t hpagesize;
|
int64_t page_size;
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
hpagesize = gethugepagesize(path, &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
block->mr->align = hpagesize;
|
|
||||||
|
|
||||||
if (memory < hpagesize) {
|
|
||||||
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
|
||||||
"or larger than huge page size 0x%" PRIx64,
|
|
||||||
memory, hpagesize);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kvm_enabled() && !kvm_has_sync_mmu()) {
|
if (kvm_enabled() && !kvm_has_sync_mmu()) {
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"host lacks kvm mmu notifiers, -mem-path unsupported");
|
"host lacks kvm mmu notifiers, -mem-path unsupported");
|
||||||
goto error;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
|
for (;;) {
|
||||||
|
fd = open(path, O_RDWR);
|
||||||
|
if (fd >= 0) {
|
||||||
|
/* @path names an existing file, use it */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
/* @path names a file that doesn't exist, create it */
|
||||||
|
fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
|
||||||
|
if (fd >= 0) {
|
||||||
|
unlink_on_error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (errno == EISDIR) {
|
||||||
|
/* @path names a directory, create a file there */
|
||||||
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
||||||
sanitized_name = g_strdup(memory_region_name(block->mr));
|
sanitized_name = g_strdup(memory_region_name(block->mr));
|
||||||
for (c = sanitized_name; *c != '\0'; c++) {
|
for (c = sanitized_name; *c != '\0'; c++) {
|
||||||
@@ -1280,19 +1277,34 @@ static void *file_ram_alloc(RAMBlock *block,
|
|||||||
fd = mkstemp(filename);
|
fd = mkstemp(filename);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
unlink(filename);
|
unlink(filename);
|
||||||
|
g_free(filename);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
g_free(filename);
|
g_free(filename);
|
||||||
} else {
|
}
|
||||||
fd = open(path, O_RDWR | O_CREAT, 0644);
|
if (errno != EEXIST && errno != EINTR) {
|
||||||
|
error_setg_errno(errp, errno,
|
||||||
|
"can't open backing store %s for guest RAM",
|
||||||
|
path);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Try again on EINTR and EEXIST. The latter happens when
|
||||||
|
* something else creates the file between our two open().
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fd < 0) {
|
page_size = qemu_fd_getpagesize(fd);
|
||||||
error_setg_errno(errp, errno,
|
block->mr->align = page_size;
|
||||||
"unable to create backing store for hugepages");
|
|
||||||
|
if (memory < page_size) {
|
||||||
|
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
||||||
|
"or larger than page size 0x%" PRIx64,
|
||||||
|
memory, page_size);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory = ROUND_UP(memory, hpagesize);
|
memory = ROUND_UP(memory, page_size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ftruncate is not supported by hugetlbfs in older
|
* ftruncate is not supported by hugetlbfs in older
|
||||||
@@ -1304,10 +1316,10 @@ static void *file_ram_alloc(RAMBlock *block,
|
|||||||
perror("ftruncate");
|
perror("ftruncate");
|
||||||
}
|
}
|
||||||
|
|
||||||
area = qemu_ram_mmap(fd, memory, hpagesize, block->flags & RAM_SHARED);
|
area = qemu_ram_mmap(fd, memory, page_size, block->flags & RAM_SHARED);
|
||||||
if (area == MAP_FAILED) {
|
if (area == MAP_FAILED) {
|
||||||
error_setg_errno(errp, errno,
|
error_setg_errno(errp, errno,
|
||||||
"unable to map backing store for hugepages");
|
"unable to map backing store for guest RAM");
|
||||||
close(fd);
|
close(fd);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -1320,6 +1332,10 @@ static void *file_ram_alloc(RAMBlock *block,
|
|||||||
return area;
|
return area;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
if (unlink_on_error) {
|
||||||
|
unlink(path);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1554,7 +1570,7 @@ static void dirty_memory_extend(ram_addr_t old_ram_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
|
static void ram_block_add(RAMBlock *new_block, Error **errp)
|
||||||
{
|
{
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
RAMBlock *last_block = NULL;
|
RAMBlock *last_block = NULL;
|
||||||
@@ -1573,7 +1589,7 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
|
|||||||
if (err) {
|
if (err) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
qemu_mutex_unlock_ramlist();
|
qemu_mutex_unlock_ramlist();
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
new_block->host = phys_mem_alloc(new_block->max_length,
|
new_block->host = phys_mem_alloc(new_block->max_length,
|
||||||
@@ -1583,7 +1599,7 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
|
|||||||
"cannot set up guest memory '%s'",
|
"cannot set up guest memory '%s'",
|
||||||
memory_region_name(new_block->mr));
|
memory_region_name(new_block->mr));
|
||||||
qemu_mutex_unlock_ramlist();
|
qemu_mutex_unlock_ramlist();
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
memory_try_enable_merging(new_block->host, new_block->max_length);
|
memory_try_enable_merging(new_block->host, new_block->max_length);
|
||||||
}
|
}
|
||||||
@@ -1631,22 +1647,19 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
|
|||||||
kvm_setup_guest_memory(new_block->host, new_block->max_length);
|
kvm_setup_guest_memory(new_block->host, new_block->max_length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_block->offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||||
bool share, const char *mem_path,
|
bool share, const char *mem_path,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
RAMBlock *new_block;
|
RAMBlock *new_block;
|
||||||
ram_addr_t addr;
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
if (xen_enabled()) {
|
if (xen_enabled()) {
|
||||||
error_setg(errp, "-mem-path not supported with Xen");
|
error_setg(errp, "-mem-path not supported with Xen");
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phys_mem_alloc != qemu_anon_ram_alloc) {
|
if (phys_mem_alloc != qemu_anon_ram_alloc) {
|
||||||
@@ -1657,7 +1670,7 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
|||||||
*/
|
*/
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"-mem-path not supported with this accelerator");
|
"-mem-path not supported with this accelerator");
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = HOST_PAGE_ALIGN(size);
|
size = HOST_PAGE_ALIGN(size);
|
||||||
@@ -1670,21 +1683,21 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
|||||||
mem_path, errp);
|
mem_path, errp);
|
||||||
if (!new_block->host) {
|
if (!new_block->host) {
|
||||||
g_free(new_block);
|
g_free(new_block);
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = ram_block_add(new_block, &local_err);
|
ram_block_add(new_block, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
g_free(new_block);
|
g_free(new_block);
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
return addr;
|
return new_block;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static
|
static
|
||||||
ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
||||||
void (*resized)(const char*,
|
void (*resized)(const char*,
|
||||||
uint64_t length,
|
uint64_t length,
|
||||||
void *host),
|
void *host),
|
||||||
@@ -1692,7 +1705,6 @@ ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
|||||||
MemoryRegion *mr, Error **errp)
|
MemoryRegion *mr, Error **errp)
|
||||||
{
|
{
|
||||||
RAMBlock *new_block;
|
RAMBlock *new_block;
|
||||||
ram_addr_t addr;
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
size = HOST_PAGE_ALIGN(size);
|
size = HOST_PAGE_ALIGN(size);
|
||||||
@@ -1711,29 +1723,27 @@ ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
|||||||
if (resizeable) {
|
if (resizeable) {
|
||||||
new_block->flags |= RAM_RESIZEABLE;
|
new_block->flags |= RAM_RESIZEABLE;
|
||||||
}
|
}
|
||||||
addr = ram_block_add(new_block, &local_err);
|
ram_block_add(new_block, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
g_free(new_block);
|
g_free(new_block);
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return -1;
|
return NULL;
|
||||||
|
}
|
||||||
|
return new_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
mr->ram_block = new_block;
|
RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
|
|
||||||
MemoryRegion *mr, Error **errp)
|
MemoryRegion *mr, Error **errp)
|
||||||
{
|
{
|
||||||
return qemu_ram_alloc_internal(size, size, NULL, host, false, mr, errp);
|
return qemu_ram_alloc_internal(size, size, NULL, host, false, mr, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp)
|
RAMBlock *qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp)
|
||||||
{
|
{
|
||||||
return qemu_ram_alloc_internal(size, size, NULL, NULL, false, mr, errp);
|
return qemu_ram_alloc_internal(size, size, NULL, NULL, false, mr, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz,
|
RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz,
|
||||||
void (*resized)(const char*,
|
void (*resized)(const char*,
|
||||||
uint64_t length,
|
uint64_t length,
|
||||||
void *host),
|
void *host),
|
||||||
@@ -1759,22 +1769,15 @@ static void reclaim_ramblock(RAMBlock *block)
|
|||||||
g_free(block);
|
g_free(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_ram_free(ram_addr_t addr)
|
void qemu_ram_free(RAMBlock *block)
|
||||||
{
|
{
|
||||||
RAMBlock *block;
|
|
||||||
|
|
||||||
qemu_mutex_lock_ramlist();
|
qemu_mutex_lock_ramlist();
|
||||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
|
||||||
if (addr == block->offset) {
|
|
||||||
QLIST_REMOVE_RCU(block, next);
|
QLIST_REMOVE_RCU(block, next);
|
||||||
ram_list.mru_block = NULL;
|
ram_list.mru_block = NULL;
|
||||||
/* Write list before version */
|
/* Write list before version */
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
ram_list.version++;
|
ram_list.version++;
|
||||||
call_rcu(block, reclaim_ramblock, rcu);
|
call_rcu(block, reclaim_ramblock, rcu);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qemu_mutex_unlock_ramlist();
|
qemu_mutex_unlock_ramlist();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2707,7 +2710,8 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block, mr->ram_addr + addr1);
|
ptr = qemu_get_ram_ptr(mr->ram_block,
|
||||||
|
memory_region_get_ram_addr(mr) + addr1);
|
||||||
memcpy(buf, ptr, l);
|
memcpy(buf, ptr, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,4 +83,4 @@ static void fsdev_register_config(void)
|
|||||||
qemu_add_opts(&qemu_fsdev_opts);
|
qemu_add_opts(&qemu_fsdev_opts);
|
||||||
qemu_add_opts(&qemu_virtfs_opts);
|
qemu_add_opts(&qemu_virtfs_opts);
|
||||||
}
|
}
|
||||||
machine_init(fsdev_register_config);
|
opts_init(fsdev_register_config);
|
||||||
|
|||||||
@@ -1752,7 +1752,7 @@ int gdbserver_start(const char *device)
|
|||||||
sigaction(SIGINT, &act, NULL);
|
sigaction(SIGINT, &act, NULL);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
chr = qemu_chr_new("gdb", device, NULL);
|
chr = qemu_chr_new_noreplay("gdb", device, NULL);
|
||||||
if (!chr)
|
if (!chr)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|||||||
@@ -1026,7 +1026,7 @@ ETEXI
|
|||||||
.args_type = "",
|
.args_type = "",
|
||||||
.params = "",
|
.params = "",
|
||||||
.help = "Followup to a migration command to switch the migration"
|
.help = "Followup to a migration command to switch the migration"
|
||||||
" to postcopy mode. The x-postcopy-ram capability must "
|
" to postcopy mode. The postcopy-ram capability must "
|
||||||
"be set before the original migration command.",
|
"be set before the original migration command.",
|
||||||
.mhandler.cmd = hmp_migrate_start_postcopy,
|
.mhandler.cmd = hmp_migrate_start_postcopy,
|
||||||
},
|
},
|
||||||
@@ -1201,8 +1201,8 @@ ETEXI
|
|||||||
|
|
||||||
{
|
{
|
||||||
.name = "drive_add",
|
.name = "drive_add",
|
||||||
.args_type = "pci_addr:s,opts:s",
|
.args_type = "node:-n,pci_addr:s,opts:s",
|
||||||
.params = "[[<domain>:]<bus>:]<slot>\n"
|
.params = "[-n] [[<domain>:]<bus>:]<slot>\n"
|
||||||
"[file=file][,if=type][,bus=n]\n"
|
"[file=file][,if=type][,bus=n]\n"
|
||||||
"[,unit=m][,media=d][,index=i]\n"
|
"[,unit=m][,media=d][,index=i]\n"
|
||||||
"[,cyls=c,heads=h,secs=s[,trans=t]]\n"
|
"[,cyls=c,heads=h,secs=s[,trans=t]]\n"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
|
|||||||
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
|
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
|
||||||
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o cpu_hotplug_acpi_table.o
|
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o cpu_hotplug_acpi_table.o
|
||||||
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o
|
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o
|
||||||
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
|
obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
|
||||||
common-obj-$(CONFIG_ACPI) += acpi_interface.o
|
common-obj-$(CONFIG_ACPI) += acpi_interface.o
|
||||||
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
|
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
|
||||||
common-obj-$(CONFIG_ACPI) += aml-build.o
|
common-obj-$(CONFIG_ACPI) += aml-build.o
|
||||||
|
|||||||
@@ -258,6 +258,34 @@ static void build_append_int(GArray *table, uint64_t value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build NAME(XXXX, 0x00000000) where 0x00000000 is encoded as a dword,
|
||||||
|
* and return the offset to 0x00000000 for runtime patching.
|
||||||
|
*
|
||||||
|
* Warning: runtime patching is best avoided. Only use this as
|
||||||
|
* a replacement for DataTableRegion (for guests that don't
|
||||||
|
* support it).
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
build_append_named_dword(GArray *array, const char *name_format, ...)
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
build_append_byte(array, 0x08); /* NameOp */
|
||||||
|
va_start(ap, name_format);
|
||||||
|
build_append_namestringv(array, name_format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
build_append_byte(array, 0x0C); /* DWordPrefix */
|
||||||
|
|
||||||
|
offset = array->len;
|
||||||
|
build_append_int_noprefix(array, 0x00000000, 4);
|
||||||
|
assert(array->len == offset + 4);
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
static GPtrArray *alloc_list;
|
static GPtrArray *alloc_list;
|
||||||
|
|
||||||
static Aml *aml_alloc(void)
|
static Aml *aml_alloc(void)
|
||||||
@@ -942,14 +970,14 @@ Aml *aml_package(uint8_t num_elements)
|
|||||||
|
|
||||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefOpRegion */
|
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefOpRegion */
|
||||||
Aml *aml_operation_region(const char *name, AmlRegionSpace rs,
|
Aml *aml_operation_region(const char *name, AmlRegionSpace rs,
|
||||||
uint32_t offset, uint32_t len)
|
Aml *offset, uint32_t len)
|
||||||
{
|
{
|
||||||
Aml *var = aml_alloc();
|
Aml *var = aml_alloc();
|
||||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||||
build_append_byte(var->buf, 0x80); /* OpRegionOp */
|
build_append_byte(var->buf, 0x80); /* OpRegionOp */
|
||||||
build_append_namestring(var->buf, "%s", name);
|
build_append_namestring(var->buf, "%s", name);
|
||||||
build_append_byte(var->buf, rs);
|
build_append_byte(var->buf, rs);
|
||||||
build_append_int(var->buf, offset);
|
aml_append(var, offset);
|
||||||
build_append_int(var->buf, len);
|
build_append_int(var->buf, len);
|
||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
@@ -997,6 +1025,20 @@ Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name)
|
|||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateField */
|
||||||
|
Aml *aml_create_field(Aml *srcbuf, Aml *bit_index, Aml *num_bits,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
Aml *var = aml_alloc();
|
||||||
|
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||||
|
build_append_byte(var->buf, 0x13); /* CreateFieldOp */
|
||||||
|
aml_append(var, srcbuf);
|
||||||
|
aml_append(var, bit_index);
|
||||||
|
aml_append(var, num_bits);
|
||||||
|
build_append_namestring(var->buf, "%s", name);
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
|
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
|
||||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
|
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
|
||||||
{
|
{
|
||||||
@@ -1423,6 +1465,13 @@ Aml *aml_alias(const char *source_object, const char *alias_object)
|
|||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefConcat */
|
||||||
|
Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target)
|
||||||
|
{
|
||||||
|
return build_opcode_2arg_dst(0x73 /* ConcatOp */, source1, source2,
|
||||||
|
target);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
build_header(GArray *linker, GArray *table_data,
|
build_header(GArray *linker, GArray *table_data,
|
||||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
|
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#include "hw/nvram/fw_cfg.h"
|
#include "hw/nvram/fw_cfg.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qapi/opts-visitor.h"
|
#include "qapi/opts-visitor.h"
|
||||||
#include "qapi/dealloc-visitor.h"
|
|
||||||
#include "qapi-visit.h"
|
#include "qapi-visit.h"
|
||||||
#include "qapi-event.h"
|
#include "qapi-event.h"
|
||||||
|
|
||||||
@@ -68,7 +67,7 @@ static void acpi_register_config(void)
|
|||||||
qemu_add_opts(&qemu_acpi_opts);
|
qemu_add_opts(&qemu_acpi_opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(acpi_register_config);
|
opts_init(acpi_register_config);
|
||||||
|
|
||||||
static int acpi_checksum(const uint8_t *data, int len)
|
static int acpi_checksum(const uint8_t *data, int len)
|
||||||
{
|
{
|
||||||
@@ -297,15 +296,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
|
|||||||
out:
|
out:
|
||||||
g_free(blob);
|
g_free(blob);
|
||||||
g_strfreev(pathnames);
|
g_strfreev(pathnames);
|
||||||
|
qapi_free_AcpiTableOptions(hdrs);
|
||||||
if (hdrs != NULL) {
|
|
||||||
QapiDeallocVisitor *dv;
|
|
||||||
|
|
||||||
dv = qapi_dealloc_visitor_new();
|
|
||||||
visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), NULL, &hdrs,
|
|
||||||
NULL);
|
|
||||||
qapi_dealloc_visitor_cleanup(dv);
|
|
||||||
}
|
|
||||||
|
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|||||||
231
hw/acpi/nvdimm.c
231
hw/acpi/nvdimm.c
@@ -29,6 +29,8 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "hw/acpi/acpi.h"
|
#include "hw/acpi/acpi.h"
|
||||||
#include "hw/acpi/aml-build.h"
|
#include "hw/acpi/aml-build.h"
|
||||||
|
#include "hw/acpi/bios-linker-loader.h"
|
||||||
|
#include "hw/nvram/fw_cfg.h"
|
||||||
#include "hw/mem/nvdimm.h"
|
#include "hw/mem/nvdimm.h"
|
||||||
|
|
||||||
static int nvdimm_plugged_device_list(Object *obj, void *opaque)
|
static int nvdimm_plugged_device_list(Object *obj, void *opaque)
|
||||||
@@ -370,15 +372,131 @@ static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets,
|
|||||||
g_array_free(structures, true);
|
g_array_free(structures, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NvdimmDsmIn {
|
||||||
|
uint32_t handle;
|
||||||
|
uint32_t revision;
|
||||||
|
uint32_t function;
|
||||||
|
/* the remaining size in the page is used by arg3. */
|
||||||
|
union {
|
||||||
|
uint8_t arg3[0];
|
||||||
|
};
|
||||||
|
} QEMU_PACKED;
|
||||||
|
typedef struct NvdimmDsmIn NvdimmDsmIn;
|
||||||
|
|
||||||
|
struct NvdimmDsmOut {
|
||||||
|
/* the size of buffer filled by QEMU. */
|
||||||
|
uint32_t len;
|
||||||
|
uint8_t data[0];
|
||||||
|
} QEMU_PACKED;
|
||||||
|
typedef struct NvdimmDsmOut NvdimmDsmOut;
|
||||||
|
|
||||||
|
struct NvdimmDsmFunc0Out {
|
||||||
|
/* the size of buffer filled by QEMU. */
|
||||||
|
uint32_t len;
|
||||||
|
uint32_t supported_func;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
typedef struct NvdimmDsmFunc0Out NvdimmDsmFunc0Out;
|
||||||
|
|
||||||
|
struct NvdimmDsmFuncNoPayloadOut {
|
||||||
|
/* the size of buffer filled by QEMU. */
|
||||||
|
uint32_t len;
|
||||||
|
uint32_t func_ret_status;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
typedef struct NvdimmDsmFuncNoPayloadOut NvdimmDsmFuncNoPayloadOut;
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
|
{
|
||||||
|
nvdimm_debug("BUG: we never read _DSM IO Port.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||||
|
{
|
||||||
|
NvdimmDsmIn *in;
|
||||||
|
hwaddr dsm_mem_addr = val;
|
||||||
|
|
||||||
|
nvdimm_debug("dsm memory address %#" HWADDR_PRIx ".\n", dsm_mem_addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The DSM memory is mapped to guest address space so an evil guest
|
||||||
|
* can change its content while we are doing DSM emulation. Avoid
|
||||||
|
* this by copying DSM memory to QEMU local memory.
|
||||||
|
*/
|
||||||
|
in = g_malloc(TARGET_PAGE_SIZE);
|
||||||
|
cpu_physical_memory_read(dsm_mem_addr, in, TARGET_PAGE_SIZE);
|
||||||
|
|
||||||
|
le32_to_cpus(&in->revision);
|
||||||
|
le32_to_cpus(&in->function);
|
||||||
|
le32_to_cpus(&in->handle);
|
||||||
|
|
||||||
|
nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision,
|
||||||
|
in->handle, in->function);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* function 0 is called to inquire which functions are supported by
|
||||||
|
* OSPM
|
||||||
|
*/
|
||||||
|
if (in->function == 0) {
|
||||||
|
NvdimmDsmFunc0Out func0 = {
|
||||||
|
.len = cpu_to_le32(sizeof(func0)),
|
||||||
|
/* No function supported other than function 0 */
|
||||||
|
.supported_func = cpu_to_le32(0),
|
||||||
|
};
|
||||||
|
cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof func0);
|
||||||
|
} else {
|
||||||
|
/* No function except function 0 is supported yet. */
|
||||||
|
NvdimmDsmFuncNoPayloadOut out = {
|
||||||
|
.len = cpu_to_le32(sizeof(out)),
|
||||||
|
.func_ret_status = cpu_to_le32(1) /* Not Supported */,
|
||||||
|
};
|
||||||
|
cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps nvdimm_dsm_ops = {
|
||||||
|
.read = nvdimm_dsm_read,
|
||||||
|
.write = nvdimm_dsm_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io,
|
||||||
|
FWCfgState *fw_cfg, Object *owner)
|
||||||
|
{
|
||||||
|
memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state,
|
||||||
|
"nvdimm-acpi-io", NVDIMM_ACPI_IO_LEN);
|
||||||
|
memory_region_add_subregion(io, NVDIMM_ACPI_IO_BASE, &state->io_mr);
|
||||||
|
|
||||||
|
state->dsm_mem = g_array_new(false, true /* clear */, 1);
|
||||||
|
acpi_data_push(state->dsm_mem, TARGET_PAGE_SIZE);
|
||||||
|
fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data,
|
||||||
|
state->dsm_mem->len);
|
||||||
|
}
|
||||||
|
|
||||||
#define NVDIMM_COMMON_DSM "NCAL"
|
#define NVDIMM_COMMON_DSM "NCAL"
|
||||||
|
#define NVDIMM_ACPI_MEM_ADDR "MEMA"
|
||||||
|
|
||||||
static void nvdimm_build_common_dsm(Aml *dev)
|
static void nvdimm_build_common_dsm(Aml *dev)
|
||||||
{
|
{
|
||||||
Aml *method, *ifctx, *function;
|
Aml *method, *ifctx, *function, *dsm_mem, *unpatched, *result_size;
|
||||||
uint8_t byte_list[1];
|
uint8_t byte_list[1];
|
||||||
|
|
||||||
method = aml_method(NVDIMM_COMMON_DSM, 4, AML_NOTSERIALIZED);
|
method = aml_method(NVDIMM_COMMON_DSM, 4, AML_SERIALIZED);
|
||||||
function = aml_arg(2);
|
function = aml_arg(2);
|
||||||
|
dsm_mem = aml_name(NVDIMM_ACPI_MEM_ADDR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* do not support any method if DSM memory address has not been
|
||||||
|
* patched.
|
||||||
|
*/
|
||||||
|
unpatched = aml_if(aml_equal(dsm_mem, aml_int(0x0)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* function 0 is called to inquire what functions are supported by
|
* function 0 is called to inquire what functions are supported by
|
||||||
@@ -387,12 +505,38 @@ static void nvdimm_build_common_dsm(Aml *dev)
|
|||||||
ifctx = aml_if(aml_equal(function, aml_int(0)));
|
ifctx = aml_if(aml_equal(function, aml_int(0)));
|
||||||
byte_list[0] = 0 /* No function Supported */;
|
byte_list[0] = 0 /* No function Supported */;
|
||||||
aml_append(ifctx, aml_return(aml_buffer(1, byte_list)));
|
aml_append(ifctx, aml_return(aml_buffer(1, byte_list)));
|
||||||
aml_append(method, ifctx);
|
aml_append(unpatched, ifctx);
|
||||||
|
|
||||||
/* No function is supported yet. */
|
/* No function is supported yet. */
|
||||||
byte_list[0] = 1 /* Not Supported */;
|
byte_list[0] = 1 /* Not Supported */;
|
||||||
aml_append(method, aml_return(aml_buffer(1, byte_list)));
|
aml_append(unpatched, aml_return(aml_buffer(1, byte_list)));
|
||||||
|
aml_append(method, unpatched);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The HDLE indicates the DSM function is issued from which device,
|
||||||
|
* it is not used at this time as no function is supported yet.
|
||||||
|
* Currently we make it always be 0 for all the devices and will set
|
||||||
|
* the appropriate value once real function is implemented.
|
||||||
|
*/
|
||||||
|
aml_append(method, aml_store(aml_int(0x0), aml_name("HDLE")));
|
||||||
|
aml_append(method, aml_store(aml_arg(1), aml_name("REVS")));
|
||||||
|
aml_append(method, aml_store(aml_arg(2), aml_name("FUNC")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tell QEMU about the real address of DSM memory, then QEMU
|
||||||
|
* gets the control and fills the result in DSM memory.
|
||||||
|
*/
|
||||||
|
aml_append(method, aml_store(dsm_mem, aml_name("NTFI")));
|
||||||
|
|
||||||
|
result_size = aml_local(1);
|
||||||
|
aml_append(method, aml_store(aml_name("RLEN"), result_size));
|
||||||
|
aml_append(method, aml_store(aml_shiftleft(result_size, aml_int(3)),
|
||||||
|
result_size));
|
||||||
|
aml_append(method, aml_create_field(aml_name("ODAT"), aml_int(0),
|
||||||
|
result_size, "OBUF"));
|
||||||
|
aml_append(method, aml_concatenate(aml_buffer(0, NULL), aml_name("OBUF"),
|
||||||
|
aml_arg(6)));
|
||||||
|
aml_append(method, aml_return(aml_arg(6)));
|
||||||
aml_append(dev, method);
|
aml_append(dev, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +579,8 @@ static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev)
|
|||||||
static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
|
static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
|
||||||
GArray *table_data, GArray *linker)
|
GArray *table_data, GArray *linker)
|
||||||
{
|
{
|
||||||
Aml *ssdt, *sb_scope, *dev;
|
Aml *ssdt, *sb_scope, *dev, *field;
|
||||||
|
int mem_addr_offset, nvdimm_ssdt;
|
||||||
|
|
||||||
acpi_add_table(table_offsets, table_data);
|
acpi_add_table(table_offsets, table_data);
|
||||||
|
|
||||||
@@ -459,19 +604,89 @@ static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
|
|||||||
*/
|
*/
|
||||||
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012")));
|
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012")));
|
||||||
|
|
||||||
|
/* map DSM memory and IO into ACPI namespace. */
|
||||||
|
aml_append(dev, aml_operation_region("NPIO", AML_SYSTEM_IO,
|
||||||
|
aml_int(NVDIMM_ACPI_IO_BASE), NVDIMM_ACPI_IO_LEN));
|
||||||
|
aml_append(dev, aml_operation_region("NRAM", AML_SYSTEM_MEMORY,
|
||||||
|
aml_name(NVDIMM_ACPI_MEM_ADDR), TARGET_PAGE_SIZE));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DSM notifier:
|
||||||
|
* NTFI: write the address of DSM memory and notify QEMU to emulate
|
||||||
|
* the access.
|
||||||
|
*
|
||||||
|
* It is the IO port so that accessing them will cause VM-exit, the
|
||||||
|
* control will be transferred to QEMU.
|
||||||
|
*/
|
||||||
|
field = aml_field("NPIO", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
|
aml_append(field, aml_named_field("NTFI",
|
||||||
|
sizeof(uint32_t) * BITS_PER_BYTE));
|
||||||
|
aml_append(dev, field);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DSM input:
|
||||||
|
* HDLE: store device's handle, it's zero if the _DSM call happens
|
||||||
|
* on NVDIMM Root Device.
|
||||||
|
* REVS: store the Arg1 of _DSM call.
|
||||||
|
* FUNC: store the Arg2 of _DSM call.
|
||||||
|
* ARG3: store the Arg3 of _DSM call.
|
||||||
|
*
|
||||||
|
* They are RAM mapping on host so that these accesses never cause
|
||||||
|
* VM-EXIT.
|
||||||
|
*/
|
||||||
|
field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
|
aml_append(field, aml_named_field("HDLE",
|
||||||
|
sizeof(typeof_field(NvdimmDsmIn, handle)) * BITS_PER_BYTE));
|
||||||
|
aml_append(field, aml_named_field("REVS",
|
||||||
|
sizeof(typeof_field(NvdimmDsmIn, revision)) * BITS_PER_BYTE));
|
||||||
|
aml_append(field, aml_named_field("FUNC",
|
||||||
|
sizeof(typeof_field(NvdimmDsmIn, function)) * BITS_PER_BYTE));
|
||||||
|
aml_append(field, aml_named_field("ARG3",
|
||||||
|
(TARGET_PAGE_SIZE - offsetof(NvdimmDsmIn, arg3)) *
|
||||||
|
BITS_PER_BYTE));
|
||||||
|
aml_append(dev, field);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DSM output:
|
||||||
|
* RLEN: the size of the buffer filled by QEMU.
|
||||||
|
* ODAT: the buffer QEMU uses to store the result.
|
||||||
|
*
|
||||||
|
* Since the page is reused by both input and out, the input data
|
||||||
|
* will be lost after storing new result into ODAT so we should fetch
|
||||||
|
* all the input data before writing the result.
|
||||||
|
*/
|
||||||
|
field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
|
aml_append(field, aml_named_field("RLEN",
|
||||||
|
sizeof(typeof_field(NvdimmDsmOut, len)) * BITS_PER_BYTE));
|
||||||
|
aml_append(field, aml_named_field("ODAT",
|
||||||
|
(TARGET_PAGE_SIZE - offsetof(NvdimmDsmOut, data)) *
|
||||||
|
BITS_PER_BYTE));
|
||||||
|
aml_append(dev, field);
|
||||||
|
|
||||||
nvdimm_build_common_dsm(dev);
|
nvdimm_build_common_dsm(dev);
|
||||||
nvdimm_build_device_dsm(dev);
|
nvdimm_build_device_dsm(dev);
|
||||||
|
|
||||||
nvdimm_build_nvdimm_devices(device_list, dev);
|
nvdimm_build_nvdimm_devices(device_list, dev);
|
||||||
|
|
||||||
aml_append(sb_scope, dev);
|
aml_append(sb_scope, dev);
|
||||||
|
|
||||||
aml_append(ssdt, sb_scope);
|
aml_append(ssdt, sb_scope);
|
||||||
|
|
||||||
|
nvdimm_ssdt = table_data->len;
|
||||||
|
|
||||||
/* copy AML table into ACPI tables blob and patch header there */
|
/* copy AML table into ACPI tables blob and patch header there */
|
||||||
g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
|
g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
|
||||||
|
mem_addr_offset = build_append_named_dword(table_data,
|
||||||
|
NVDIMM_ACPI_MEM_ADDR);
|
||||||
|
|
||||||
|
bios_linker_loader_alloc(linker, NVDIMM_DSM_MEM_FILE, TARGET_PAGE_SIZE,
|
||||||
|
false /* high memory */);
|
||||||
|
bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
|
||||||
|
NVDIMM_DSM_MEM_FILE, table_data,
|
||||||
|
table_data->data + mem_addr_offset,
|
||||||
|
sizeof(uint32_t));
|
||||||
build_header(linker, table_data,
|
build_header(linker, table_data,
|
||||||
(void *)(table_data->data + table_data->len - ssdt->buf->len),
|
(void *)(table_data->data + nvdimm_ssdt),
|
||||||
"SSDT", ssdt->buf->len, 1, NULL, "NVDIMM");
|
"SSDT", table_data->len - nvdimm_ssdt, 1, NULL, "NVDIMM");
|
||||||
free_aml_allocator();
|
free_aml_allocator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ static void clipper_init(MachineState *machine)
|
|||||||
}
|
}
|
||||||
size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
|
size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
|
||||||
NULL, &palcode_entry, &palcode_low, &palcode_high,
|
NULL, &palcode_entry, &palcode_low, &palcode_high,
|
||||||
0, EM_ALPHA, 0);
|
0, EM_ALPHA, 0, 0);
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
error_report("could not load palcode '%s'", palcode_filename);
|
error_report("could not load palcode '%s'", palcode_filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -131,7 +131,7 @@ static void clipper_init(MachineState *machine)
|
|||||||
|
|
||||||
size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
|
size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
|
||||||
NULL, &kernel_entry, &kernel_low, &kernel_high,
|
NULL, &kernel_entry, &kernel_low, &kernel_high,
|
||||||
0, EM_ALPHA, 0);
|
0, EM_ALPHA, 0, 0);
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
error_report("could not load kernel '%s'", kernel_filename);
|
error_report("could not load kernel '%s'", kernel_filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
|
|||||||
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
|
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
|
||||||
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
|
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
|
||||||
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
|
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
|
||||||
|
obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
|
|||||||
|
|
||||||
if (kernel_filename) {
|
if (kernel_filename) {
|
||||||
image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
|
image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
|
||||||
NULL, big_endian, EM_ARM, 1);
|
NULL, big_endian, EM_ARM, 1, 0);
|
||||||
if (image_size < 0) {
|
if (image_size < 0) {
|
||||||
image_size = load_image_targphys(kernel_filename, 0, mem_size);
|
image_size = load_image_targphys(kernel_filename, 0, mem_size);
|
||||||
lowaddr = 0;
|
lowaddr = 0;
|
||||||
|
|||||||
137
hw/arm/ast2400.c
Normal file
137
hw/arm/ast2400.c
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* AST2400 SoC
|
||||||
|
*
|
||||||
|
* Andrew Jeffery <andrew@aj.id.au>
|
||||||
|
* Jeremy Kerr <jk@ozlabs.org>
|
||||||
|
*
|
||||||
|
* Copyright 2016 IBM Corp.
|
||||||
|
*
|
||||||
|
* This code is licensed under the GPL version 2 or later. See
|
||||||
|
* the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "hw/arm/ast2400.h"
|
||||||
|
#include "hw/char/serial.h"
|
||||||
|
|
||||||
|
#define AST2400_UART_5_BASE 0x00184000
|
||||||
|
#define AST2400_IOMEM_SIZE 0x00200000
|
||||||
|
#define AST2400_IOMEM_BASE 0x1E600000
|
||||||
|
#define AST2400_VIC_BASE 0x1E6C0000
|
||||||
|
#define AST2400_TIMER_BASE 0x1E782000
|
||||||
|
|
||||||
|
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
|
||||||
|
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IO handlers: simply catch any reads/writes to IO addresses that aren't
|
||||||
|
* handled by a device mapping.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n",
|
||||||
|
__func__, offset, size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
|
||||||
|
__func__, offset, value, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps ast2400_io_ops = {
|
||||||
|
.read = ast2400_io_read,
|
||||||
|
.write = ast2400_io_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ast2400_init(Object *obj)
|
||||||
|
{
|
||||||
|
AST2400State *s = AST2400(obj);
|
||||||
|
|
||||||
|
s->cpu = cpu_arm_init("arm926");
|
||||||
|
|
||||||
|
object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC);
|
||||||
|
object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default());
|
||||||
|
|
||||||
|
object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
|
||||||
|
object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ast2400_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
AST2400State *s = AST2400(dev);
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
/* IO space */
|
||||||
|
memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
|
||||||
|
"ast2400.io", AST2400_IOMEM_SIZE);
|
||||||
|
memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE,
|
||||||
|
&s->iomem, -1);
|
||||||
|
|
||||||
|
/* VIC */
|
||||||
|
object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE);
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
|
||||||
|
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
|
||||||
|
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ));
|
||||||
|
|
||||||
|
/* Timer */
|
||||||
|
object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, AST2400_TIMER_BASE);
|
||||||
|
for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) {
|
||||||
|
qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]);
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UART - attach an 8250 to the IO space as our UART5 */
|
||||||
|
if (serial_hds[0]) {
|
||||||
|
qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
|
||||||
|
serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2,
|
||||||
|
uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ast2400_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
|
||||||
|
dc->realize = ast2400_realize;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reason: creates an ARM CPU, thus use after free(), see
|
||||||
|
* arm_cpu_class_init()
|
||||||
|
*/
|
||||||
|
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo ast2400_type_info = {
|
||||||
|
.name = TYPE_AST2400,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(AST2400State),
|
||||||
|
.instance_init = ast2400_init,
|
||||||
|
.class_init = ast2400_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ast2400_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&ast2400_type_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(ast2400_register_types)
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "hw/arm/bcm2835_peripherals.h"
|
#include "hw/arm/bcm2835_peripherals.h"
|
||||||
#include "hw/misc/bcm2835_mbox_defs.h"
|
#include "hw/misc/bcm2835_mbox_defs.h"
|
||||||
#include "hw/arm/raspi_platform.h"
|
#include "hw/arm/raspi_platform.h"
|
||||||
|
#include "sysemu/char.h"
|
||||||
|
|
||||||
/* Peripheral base address on the VC (GPU) system bus */
|
/* Peripheral base address on the VC (GPU) system bus */
|
||||||
#define BCM2835_VC_PERI_BASE 0x7e000000
|
#define BCM2835_VC_PERI_BASE 0x7e000000
|
||||||
@@ -48,6 +49,11 @@ static void bcm2835_peripherals_init(Object *obj)
|
|||||||
object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
|
object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
|
||||||
qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
|
qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
|
||||||
|
|
||||||
|
/* AUX / UART1 */
|
||||||
|
object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX);
|
||||||
|
object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default());
|
||||||
|
|
||||||
/* Mailboxes */
|
/* Mailboxes */
|
||||||
object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
|
object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
|
||||||
object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
|
object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
|
||||||
@@ -56,6 +62,16 @@ static void bcm2835_peripherals_init(Object *obj)
|
|||||||
object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
|
object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
|
||||||
OBJECT(&s->mbox_mr), &error_abort);
|
OBJECT(&s->mbox_mr), &error_abort);
|
||||||
|
|
||||||
|
/* Framebuffer */
|
||||||
|
object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB);
|
||||||
|
object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL);
|
||||||
|
object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size",
|
||||||
|
&error_abort);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default());
|
||||||
|
|
||||||
|
object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
|
||||||
|
OBJECT(&s->gpu_bus_mr), &error_abort);
|
||||||
|
|
||||||
/* Property channel */
|
/* Property channel */
|
||||||
object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY);
|
object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY);
|
||||||
object_property_add_child(obj, "property", OBJECT(&s->property), NULL);
|
object_property_add_child(obj, "property", OBJECT(&s->property), NULL);
|
||||||
@@ -63,6 +79,8 @@ static void bcm2835_peripherals_init(Object *obj)
|
|||||||
"board-rev", &error_abort);
|
"board-rev", &error_abort);
|
||||||
qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default());
|
qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default());
|
||||||
|
|
||||||
|
object_property_add_const_link(OBJECT(&s->property), "fb",
|
||||||
|
OBJECT(&s->fb), &error_abort);
|
||||||
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
|
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
|
||||||
OBJECT(&s->gpu_bus_mr), &error_abort);
|
OBJECT(&s->gpu_bus_mr), &error_abort);
|
||||||
|
|
||||||
@@ -70,6 +88,14 @@ static void bcm2835_peripherals_init(Object *obj)
|
|||||||
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
|
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
|
||||||
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
|
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
|
||||||
qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default());
|
qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default());
|
||||||
|
|
||||||
|
/* DMA Channels */
|
||||||
|
object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA);
|
||||||
|
object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default());
|
||||||
|
|
||||||
|
object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
|
||||||
|
OBJECT(&s->gpu_bus_mr), &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||||
@@ -78,7 +104,8 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||||||
Object *obj;
|
Object *obj;
|
||||||
MemoryRegion *ram;
|
MemoryRegion *ram;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
uint32_t ram_size;
|
uint32_t ram_size, vcram_size;
|
||||||
|
CharDriverState *chr;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
obj = object_property_get_link(OBJECT(dev), "ram", &err);
|
obj = object_property_get_link(OBJECT(dev), "ram", &err);
|
||||||
@@ -131,6 +158,29 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
||||||
INTERRUPT_UART));
|
INTERRUPT_UART));
|
||||||
|
|
||||||
|
/* AUX / UART1 */
|
||||||
|
/* TODO: don't call qemu_char_get_next_serial() here, instead set
|
||||||
|
* chardev properties for each uart at the board level, once pl011
|
||||||
|
* (uart0) has been updated to avoid qemu_char_get_next_serial()
|
||||||
|
*/
|
||||||
|
chr = qemu_char_get_next_serial();
|
||||||
|
if (chr == NULL) {
|
||||||
|
chr = qemu_chr_new("bcm2835.uart1", "null", NULL);
|
||||||
|
}
|
||||||
|
qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr);
|
||||||
|
|
||||||
|
object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
|
||||||
|
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
|
||||||
|
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
||||||
|
INTERRUPT_AUX));
|
||||||
|
|
||||||
/* Mailboxes */
|
/* Mailboxes */
|
||||||
object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
|
object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -144,13 +194,33 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
|
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
|
||||||
INTERRUPT_ARM_MAILBOX));
|
INTERRUPT_ARM_MAILBOX));
|
||||||
|
|
||||||
/* Property channel */
|
/* Framebuffer */
|
||||||
object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err);
|
vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size",
|
||||||
|
&err);
|
||||||
if (err) {
|
if (err) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size,
|
||||||
|
"vcram-base", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object_property_set_bool(OBJECT(&s->fb), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT,
|
||||||
|
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0));
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0,
|
||||||
|
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
|
||||||
|
|
||||||
|
/* Property channel */
|
||||||
object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
|
object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
@@ -171,6 +241,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
|
||||||
|
&err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
|
object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
@@ -189,6 +266,24 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* DMA Channels */
|
||||||
|
object_property_set_bool(OBJECT(&s->dma), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_add_subregion(&s->peri_mr, DMA_OFFSET,
|
||||||
|
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0));
|
||||||
|
memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET,
|
||||||
|
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1));
|
||||||
|
|
||||||
|
for (n = 0; n <= 12; n++) {
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n,
|
||||||
|
qdev_get_gpio_in_named(DEVICE(&s->ic),
|
||||||
|
BCM2835_IC_GPU_IRQ,
|
||||||
|
INTERRUPT_DMA0 + n));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
|
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
|
||||||
@@ -196,6 +291,8 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
|
|||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
|
||||||
dc->realize = bcm2835_peripherals_realize;
|
dc->realize = bcm2835_peripherals_realize;
|
||||||
|
/* Reason: realize() method uses qemu_char_get_next_serial() */
|
||||||
|
dc->cannot_instantiate_with_device_add_yet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo bcm2835_peripherals_type_info = {
|
static const TypeInfo bcm2835_peripherals_type_info = {
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ static void bcm2836_init(Object *obj)
|
|||||||
&error_abort);
|
&error_abort);
|
||||||
object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals),
|
object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals),
|
||||||
"board-rev", &error_abort);
|
"board-rev", &error_abort);
|
||||||
|
object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals),
|
||||||
|
"vcram-size", &error_abort);
|
||||||
qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default());
|
qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -518,9 +518,34 @@ static void do_cpu_reset(void *opaque)
|
|||||||
cpu_reset(cs);
|
cpu_reset(cs);
|
||||||
if (info) {
|
if (info) {
|
||||||
if (!info->is_linux) {
|
if (!info->is_linux) {
|
||||||
|
int i;
|
||||||
/* Jump to the entry point. */
|
/* Jump to the entry point. */
|
||||||
uint64_t entry = info->entry;
|
uint64_t entry = info->entry;
|
||||||
|
|
||||||
|
switch (info->endianness) {
|
||||||
|
case ARM_ENDIANNESS_LE:
|
||||||
|
env->cp15.sctlr_el[1] &= ~SCTLR_E0E;
|
||||||
|
for (i = 1; i < 4; ++i) {
|
||||||
|
env->cp15.sctlr_el[i] &= ~SCTLR_EE;
|
||||||
|
}
|
||||||
|
env->uncached_cpsr &= ~CPSR_E;
|
||||||
|
break;
|
||||||
|
case ARM_ENDIANNESS_BE8:
|
||||||
|
env->cp15.sctlr_el[1] |= SCTLR_E0E;
|
||||||
|
for (i = 1; i < 4; ++i) {
|
||||||
|
env->cp15.sctlr_el[i] |= SCTLR_EE;
|
||||||
|
}
|
||||||
|
env->uncached_cpsr |= CPSR_E;
|
||||||
|
break;
|
||||||
|
case ARM_ENDIANNESS_BE32:
|
||||||
|
env->cp15.sctlr_el[1] |= SCTLR_B;
|
||||||
|
break;
|
||||||
|
case ARM_ENDIANNESS_UNKNOWN:
|
||||||
|
break; /* Board's decision */
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
if (!env->aarch64) {
|
if (!env->aarch64) {
|
||||||
env->thumb = info->entry & 1;
|
env->thumb = info->entry & 1;
|
||||||
entry &= 0xfffffffe;
|
entry &= 0xfffffffe;
|
||||||
@@ -638,6 +663,62 @@ static int do_arm_linux_init(Object *obj, void *opaque)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
|
||||||
|
uint64_t *lowaddr, uint64_t *highaddr,
|
||||||
|
int elf_machine)
|
||||||
|
{
|
||||||
|
bool elf_is64;
|
||||||
|
union {
|
||||||
|
Elf32_Ehdr h32;
|
||||||
|
Elf64_Ehdr h64;
|
||||||
|
} elf_header;
|
||||||
|
int data_swab = 0;
|
||||||
|
bool big_endian;
|
||||||
|
uint64_t ret = -1;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
|
||||||
|
if (err) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elf_is64) {
|
||||||
|
big_endian = elf_header.h64.e_ident[EI_DATA] == ELFDATA2MSB;
|
||||||
|
info->endianness = big_endian ? ARM_ENDIANNESS_BE8
|
||||||
|
: ARM_ENDIANNESS_LE;
|
||||||
|
} else {
|
||||||
|
big_endian = elf_header.h32.e_ident[EI_DATA] == ELFDATA2MSB;
|
||||||
|
if (big_endian) {
|
||||||
|
if (bswap32(elf_header.h32.e_flags) & EF_ARM_BE8) {
|
||||||
|
info->endianness = ARM_ENDIANNESS_BE8;
|
||||||
|
} else {
|
||||||
|
info->endianness = ARM_ENDIANNESS_BE32;
|
||||||
|
/* In BE32, the CPU has a different view of the per-byte
|
||||||
|
* address map than the rest of the system. BE32 ELF files
|
||||||
|
* are organised such that they can be programmed through
|
||||||
|
* the CPU's per-word byte-reversed view of the world. QEMU
|
||||||
|
* however loads ELF files independently of the CPU. So
|
||||||
|
* tell the ELF loader to byte reverse the data for us.
|
||||||
|
*/
|
||||||
|
data_swab = 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info->endianness = ARM_ENDIANNESS_LE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = load_elf(info->kernel_filename, NULL, NULL,
|
||||||
|
pentry, lowaddr, highaddr, big_endian, elf_machine,
|
||||||
|
1, data_swab);
|
||||||
|
if (ret <= 0) {
|
||||||
|
/* The header loaded but the image didn't */
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
||||||
{
|
{
|
||||||
CPUState *cs;
|
CPUState *cs;
|
||||||
@@ -647,7 +728,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
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, kernel_load_offset;
|
||||||
int big_endian;
|
|
||||||
static const ARMInsnFixup *primary_loader;
|
static const ARMInsnFixup *primary_loader;
|
||||||
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
||||||
notifier, notifier);
|
notifier, notifier);
|
||||||
@@ -733,12 +813,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
if (info->nb_cpus == 0)
|
if (info->nb_cpus == 0)
|
||||||
info->nb_cpus = 1;
|
info->nb_cpus = 1;
|
||||||
|
|
||||||
#ifdef TARGET_WORDS_BIGENDIAN
|
|
||||||
big_endian = 1;
|
|
||||||
#else
|
|
||||||
big_endian = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* We want to put the initrd far enough into RAM that when the
|
/* We want to put the initrd far enough into RAM that when the
|
||||||
* kernel is uncompressed it will not clobber the initrd. However
|
* kernel is uncompressed it will not clobber the initrd. However
|
||||||
* on boards without much RAM we must ensure that we still leave
|
* on boards without much RAM we must ensure that we still leave
|
||||||
@@ -753,9 +827,8 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
MIN(info->ram_size / 2, 128 * 1024 * 1024);
|
MIN(info->ram_size / 2, 128 * 1024 * 1024);
|
||||||
|
|
||||||
/* Assume that raw images are linux kernels, and ELF images are not. */
|
/* Assume that raw images are linux kernels, and ELF images are not. */
|
||||||
kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
|
kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr,
|
||||||
&elf_low_addr, &elf_high_addr, big_endian,
|
&elf_high_addr, elf_machine);
|
||||||
elf_machine, 1);
|
|
||||||
if (kernel_size > 0 && have_dtb(info)) {
|
if (kernel_size > 0 && have_dtb(info)) {
|
||||||
/* If there is still some room left at the base of RAM, try and put
|
/* If there is still some room left at the base of RAM, try and put
|
||||||
* the DTB there like we do for images loaded with -bios or -pflash.
|
* the DTB there like we do for images loaded with -bios or -pflash.
|
||||||
|
|||||||
@@ -181,4 +181,4 @@ static void exynos4_machines_init(void)
|
|||||||
type_register_static(&smdkc210_type);
|
type_register_static(&smdkc210_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(exynos4_machines_init)
|
type_init(exynos4_machines_init)
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
|
|||||||
* arm_cpu_class_init()
|
* arm_cpu_class_init()
|
||||||
*/
|
*/
|
||||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||||
|
dc->desc = "i.MX25 SOC";
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo fsl_imx25_type_info = {
|
static const TypeInfo fsl_imx25_type_info = {
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
|
|||||||
* arm_cpu_class_init()
|
* arm_cpu_class_init()
|
||||||
*/
|
*/
|
||||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||||
|
dc->desc = "i.MX31 SOC";
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo fsl_imx31_type_info = {
|
static const TypeInfo fsl_imx31_type_info = {
|
||||||
|
|||||||
@@ -156,4 +156,4 @@ static void gumstix_machine_init(void)
|
|||||||
type_register_static(&verdex_type);
|
type_register_static(&verdex_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(gumstix_machine_init)
|
type_init(gumstix_machine_init)
|
||||||
|
|||||||
@@ -437,4 +437,4 @@ static void calxeda_machines_init(void)
|
|||||||
type_register_static(&midway_type);
|
type_register_static(&midway_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(calxeda_machines_init)
|
type_init(calxeda_machines_init)
|
||||||
|
|||||||
@@ -1450,4 +1450,4 @@ static void nseries_machine_init(void)
|
|||||||
type_register_static(&n810_type);
|
type_register_static(&n810_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(nseries_machine_init)
|
type_init(nseries_machine_init)
|
||||||
|
|||||||
@@ -252,4 +252,4 @@ static void sx1_machine_init(void)
|
|||||||
type_register_static(&sx1_machine_v2_type);
|
type_register_static(&sx1_machine_v2_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(sx1_machine_init)
|
type_init(sx1_machine_init)
|
||||||
|
|||||||
65
hw/arm/palmetto-bmc.c
Normal file
65
hw/arm/palmetto-bmc.c
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* OpenPOWER Palmetto BMC
|
||||||
|
*
|
||||||
|
* Andrew Jeffery <andrew@aj.id.au>
|
||||||
|
*
|
||||||
|
* Copyright 2016 IBM Corp.
|
||||||
|
*
|
||||||
|
* This code is licensed under the GPL version 2 or later. See
|
||||||
|
* the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "hw/arm/arm.h"
|
||||||
|
#include "hw/arm/ast2400.h"
|
||||||
|
#include "hw/boards.h"
|
||||||
|
|
||||||
|
static struct arm_boot_info palmetto_bmc_binfo = {
|
||||||
|
.loader_start = AST2400_SDRAM_BASE,
|
||||||
|
.board_id = 0,
|
||||||
|
.nb_cpus = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct PalmettoBMCState {
|
||||||
|
AST2400State soc;
|
||||||
|
MemoryRegion ram;
|
||||||
|
} PalmettoBMCState;
|
||||||
|
|
||||||
|
static void palmetto_bmc_init(MachineState *machine)
|
||||||
|
{
|
||||||
|
PalmettoBMCState *bmc;
|
||||||
|
|
||||||
|
bmc = g_new0(PalmettoBMCState, 1);
|
||||||
|
object_initialize(&bmc->soc, (sizeof(bmc->soc)), TYPE_AST2400);
|
||||||
|
object_property_add_child(OBJECT(machine), "soc", OBJECT(&bmc->soc),
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
|
||||||
|
memory_region_add_subregion(get_system_memory(), AST2400_SDRAM_BASE,
|
||||||
|
&bmc->ram);
|
||||||
|
object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
|
||||||
|
&error_abort);
|
||||||
|
object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
|
||||||
|
palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
|
||||||
|
palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
|
||||||
|
palmetto_bmc_binfo.ram_size = ram_size;
|
||||||
|
arm_load_kernel(ARM_CPU(first_cpu), &palmetto_bmc_binfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void palmetto_bmc_machine_init(MachineClass *mc)
|
||||||
|
{
|
||||||
|
mc->desc = "OpenPOWER Palmetto BMC";
|
||||||
|
mc->init = palmetto_bmc_init;
|
||||||
|
mc->max_cpus = 1;
|
||||||
|
mc->no_sdcard = 1;
|
||||||
|
mc->no_floppy = 1;
|
||||||
|
mc->no_cdrom = 1;
|
||||||
|
mc->no_sdcard = 1;
|
||||||
|
mc->no_parallel = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_MACHINE("palmetto-bmc", palmetto_bmc_machine_init);
|
||||||
@@ -113,6 +113,7 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
|
|||||||
static void raspi2_init(MachineState *machine)
|
static void raspi2_init(MachineState *machine)
|
||||||
{
|
{
|
||||||
RasPiState *s = g_new0(RasPiState, 1);
|
RasPiState *s = g_new0(RasPiState, 1);
|
||||||
|
uint32_t vcram_size;
|
||||||
DriveInfo *di;
|
DriveInfo *di;
|
||||||
BlockBackend *blk;
|
BlockBackend *blk;
|
||||||
BusState *bus;
|
BusState *bus;
|
||||||
@@ -149,7 +150,9 @@ static void raspi2_init(MachineState *machine)
|
|||||||
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
||||||
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
|
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
|
||||||
|
|
||||||
setup_boot(machine, 2, machine->ram_size);
|
vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size",
|
||||||
|
&error_abort);
|
||||||
|
setup_boot(machine, 2, machine->ram_size - vcram_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void raspi2_machine_init(MachineClass *mc)
|
static void raspi2_machine_init(MachineClass *mc)
|
||||||
@@ -161,11 +164,6 @@ static void raspi2_machine_init(MachineClass *mc)
|
|||||||
mc->no_floppy = 1;
|
mc->no_floppy = 1;
|
||||||
mc->no_cdrom = 1;
|
mc->no_cdrom = 1;
|
||||||
mc->max_cpus = BCM2836_NCPUS;
|
mc->max_cpus = BCM2836_NCPUS;
|
||||||
|
mc->default_ram_size = 1024 * 1024 * 1024;
|
||||||
/* XXX: Temporary restriction in RAM size from the full 1GB. Since
|
|
||||||
* we do not yet support the framebuffer / GPU, we need to limit
|
|
||||||
* RAM usable by the OS to sit below the peripherals.
|
|
||||||
*/
|
|
||||||
mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */
|
|
||||||
};
|
};
|
||||||
DEFINE_MACHINE("raspi2", raspi2_machine_init)
|
DEFINE_MACHINE("raspi2", raspi2_machine_init)
|
||||||
|
|||||||
@@ -457,4 +457,4 @@ static void realview_machine_init(void)
|
|||||||
type_register_static(&realview_pbx_a9_type);
|
type_register_static(&realview_pbx_a9_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(realview_machine_init)
|
type_init(realview_machine_init)
|
||||||
|
|||||||
@@ -1037,7 +1037,7 @@ static void spitz_machine_init(void)
|
|||||||
type_register_static(&terrierpda_type);
|
type_register_static(&terrierpda_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(spitz_machine_init)
|
type_init(spitz_machine_init)
|
||||||
|
|
||||||
static bool is_version_0(void *opaque, int version_id)
|
static bool is_version_0(void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1420,7 +1420,7 @@ static void stellaris_machine_init(void)
|
|||||||
type_register_static(&lm3s6965evb_type);
|
type_register_static(&lm3s6965evb_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(stellaris_machine_init)
|
type_init(stellaris_machine_init)
|
||||||
|
|
||||||
static void stellaris_i2c_class_init(ObjectClass *klass, void *data)
|
static void stellaris_i2c_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
|
|||||||
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
|
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
|
||||||
reg_attr[2 * i] = cpu_to_be32(mmio_base);
|
reg_attr[2 * i] = cpu_to_be32(mmio_base);
|
||||||
reg_attr[2 * i + 1] = cpu_to_be32(
|
reg_attr[2 * i + 1] = cpu_to_be32(
|
||||||
memory_region_size(&vdev->regions[i]->mem));
|
memory_region_size(vdev->regions[i]->mem));
|
||||||
}
|
}
|
||||||
qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
|
qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
|
||||||
vbasedev->num_regions * 2 * sizeof(uint32_t));
|
vbasedev->num_regions * 2 * sizeof(uint32_t));
|
||||||
@@ -374,7 +374,7 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque)
|
|||||||
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
|
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
|
||||||
reg_attr[2 * i] = cpu_to_be32(mmio_base);
|
reg_attr[2 * i] = cpu_to_be32(mmio_base);
|
||||||
reg_attr[2 * i + 1] = cpu_to_be32(
|
reg_attr[2 * i + 1] = cpu_to_be32(
|
||||||
memory_region_size(&vdev->regions[i]->mem));
|
memory_region_size(vdev->regions[i]->mem));
|
||||||
}
|
}
|
||||||
qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr,
|
qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr,
|
||||||
vbasedev->num_regions * 2 * sizeof(uint32_t));
|
vbasedev->num_regions * 2 * sizeof(uint32_t));
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ static void versatile_machine_init(void)
|
|||||||
type_register_static(&versatileab_type);
|
type_register_static(&versatileab_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(versatile_machine_init)
|
type_init(versatile_machine_init)
|
||||||
|
|
||||||
static void vpb_sic_class_init(ObjectClass *klass, void *data)
|
static void vpb_sic_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -800,4 +800,4 @@ static void vexpress_machine_init(void)
|
|||||||
type_register_static(&vexpress_a15_info);
|
type_register_static(&vexpress_a15_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(vexpress_machine_init);
|
type_init(vexpress_machine_init);
|
||||||
|
|||||||
@@ -81,6 +81,20 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
|||||||
aml_append(scope, dev);
|
aml_append(scope, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap)
|
||||||
|
{
|
||||||
|
Aml *dev = aml_device("FWCF");
|
||||||
|
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
|
||||||
|
/* device present, functioning, decoding, not shown in UI */
|
||||||
|
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||||
|
|
||||||
|
Aml *crs = aml_resource_template();
|
||||||
|
aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base,
|
||||||
|
fw_cfg_memmap->size, AML_READ_WRITE));
|
||||||
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||||
|
aml_append(scope, dev);
|
||||||
|
}
|
||||||
|
|
||||||
static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
|
static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
|
||||||
{
|
{
|
||||||
Aml *dev, *crs;
|
Aml *dev, *crs;
|
||||||
@@ -549,6 +563,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
|||||||
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
|
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
|
||||||
(irqmap[VIRT_UART] + ARM_SPI_BASE));
|
(irqmap[VIRT_UART] + ARM_SPI_BASE));
|
||||||
acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
|
acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
|
||||||
|
acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
|
||||||
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
|
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
|
||||||
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
|
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
|
||||||
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
|
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
|
||||||
|
|||||||
207
hw/arm/virt.c
207
hw/arm/virt.c
@@ -73,6 +73,7 @@ typedef struct VirtBoardInfo {
|
|||||||
uint32_t clock_phandle;
|
uint32_t clock_phandle;
|
||||||
uint32_t gic_phandle;
|
uint32_t gic_phandle;
|
||||||
uint32_t v2m_phandle;
|
uint32_t v2m_phandle;
|
||||||
|
bool using_psci;
|
||||||
} VirtBoardInfo;
|
} VirtBoardInfo;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -95,6 +96,23 @@ typedef struct {
|
|||||||
#define VIRT_MACHINE_CLASS(klass) \
|
#define VIRT_MACHINE_CLASS(klass) \
|
||||||
OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE)
|
OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE)
|
||||||
|
|
||||||
|
/* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means
|
||||||
|
* RAM can go up to the 256GB mark, leaving 256GB of the physical
|
||||||
|
* address space unallocated and free for future use between 256G and 512G.
|
||||||
|
* If we need to provide more RAM to VMs in the future then we need to:
|
||||||
|
* * allocate a second bank of RAM starting at 2TB and working up
|
||||||
|
* * fix the DT and ACPI table generation code in QEMU to correctly
|
||||||
|
* report two split lumps of RAM to the guest
|
||||||
|
* * fix KVM in the host kernel to allow guests with >40 bit address spaces
|
||||||
|
* (We don't want to fill all the way up to 512GB with RAM because
|
||||||
|
* we might want it for non-RAM purposes later. Conversely it seems
|
||||||
|
* reasonable to assume that anybody configuring a VM with a quarter
|
||||||
|
* of a terabyte of RAM will be doing it on a host with more than a
|
||||||
|
* terabyte of physical address space.)
|
||||||
|
*/
|
||||||
|
#define RAMLIMIT_GB 255
|
||||||
|
#define RAMLIMIT_BYTES (RAMLIMIT_GB * 1024ULL * 1024 * 1024)
|
||||||
|
|
||||||
/* Addresses and sizes of our components.
|
/* Addresses and sizes of our components.
|
||||||
* 0..128MB is space for a flash device so we can run bootrom code such as UEFI.
|
* 0..128MB is space for a flash device so we can run bootrom code such as UEFI.
|
||||||
* 128MB..256MB is used for miscellaneous device I/O.
|
* 128MB..256MB is used for miscellaneous device I/O.
|
||||||
@@ -127,10 +145,11 @@ static const MemMapEntry a15memmap[] = {
|
|||||||
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
||||||
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
||||||
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
||||||
|
[VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 },
|
||||||
[VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
|
[VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
|
||||||
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
|
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
|
||||||
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
|
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
|
||||||
[VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
|
[VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES },
|
||||||
/* Second PCIe window, 512GB wide at the 512GB boundary */
|
/* Second PCIe window, 512GB wide at the 512GB boundary */
|
||||||
[VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL },
|
[VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL },
|
||||||
};
|
};
|
||||||
@@ -230,6 +249,10 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi)
|
|||||||
void *fdt = vbi->fdt;
|
void *fdt = vbi->fdt;
|
||||||
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
|
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
|
||||||
|
|
||||||
|
if (!vbi->using_psci) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qemu_fdt_add_subnode(fdt, "/psci");
|
qemu_fdt_add_subnode(fdt, "/psci");
|
||||||
if (armcpu->psci_version == 2) {
|
if (armcpu->psci_version == 2) {
|
||||||
const char comp[] = "arm,psci-0.2\0arm,psci";
|
const char comp[] = "arm,psci-0.2\0arm,psci";
|
||||||
@@ -341,7 +364,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
|
|||||||
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible",
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible",
|
||||||
armcpu->dtb_compatible);
|
armcpu->dtb_compatible);
|
||||||
|
|
||||||
if (vbi->smp_cpus > 1) {
|
if (vbi->using_psci && vbi->smp_cpus > 1) {
|
||||||
qemu_fdt_setprop_string(vbi->fdt, nodename,
|
qemu_fdt_setprop_string(vbi->fdt, nodename,
|
||||||
"enable-method", "psci");
|
"enable-method", "psci");
|
||||||
}
|
}
|
||||||
@@ -678,13 +701,15 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void create_one_flash(const char *name, hwaddr flashbase,
|
static void create_one_flash(const char *name, hwaddr flashbase,
|
||||||
hwaddr flashsize)
|
hwaddr flashsize, const char *file,
|
||||||
|
MemoryRegion *sysmem)
|
||||||
{
|
{
|
||||||
/* Create and map a single flash device. We use the same
|
/* Create and map a single flash device. We use the same
|
||||||
* parameters as the flash devices on the Versatile Express board.
|
* parameters as the flash devices on the Versatile Express board.
|
||||||
*/
|
*/
|
||||||
DriveInfo *dinfo = drive_get_next(IF_PFLASH);
|
DriveInfo *dinfo = drive_get_next(IF_PFLASH);
|
||||||
DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
|
DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
|
||||||
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
const uint64_t sectorlength = 256 * 1024;
|
const uint64_t sectorlength = 256 * 1024;
|
||||||
|
|
||||||
if (dinfo) {
|
if (dinfo) {
|
||||||
@@ -704,19 +729,10 @@ static void create_one_flash(const char *name, hwaddr flashbase,
|
|||||||
qdev_prop_set_string(dev, "name", name);
|
qdev_prop_set_string(dev, "name", name);
|
||||||
qdev_init_nofail(dev);
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, flashbase);
|
memory_region_add_subregion(sysmem, flashbase,
|
||||||
}
|
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
|
||||||
|
|
||||||
static void create_flash(const VirtBoardInfo *vbi)
|
if (file) {
|
||||||
{
|
|
||||||
/* Create two flash devices to fill the VIRT_FLASH space in the memmap.
|
|
||||||
* Any file passed via -bios goes in the first of these.
|
|
||||||
*/
|
|
||||||
hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2;
|
|
||||||
hwaddr flashbase = vbi->memmap[VIRT_FLASH].base;
|
|
||||||
char *nodename;
|
|
||||||
|
|
||||||
if (bios_name) {
|
|
||||||
char *fn;
|
char *fn;
|
||||||
int image_size;
|
int image_size;
|
||||||
|
|
||||||
@@ -726,22 +742,43 @@ static void create_flash(const VirtBoardInfo *vbi)
|
|||||||
"but you cannot use both options at once");
|
"but you cannot use both options at once");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, file);
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
error_report("Could not find ROM image '%s'", bios_name);
|
error_report("Could not find ROM image '%s'", file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
image_size = load_image_targphys(fn, flashbase, flashsize);
|
image_size = load_image_mr(fn, sysbus_mmio_get_region(sbd, 0));
|
||||||
g_free(fn);
|
g_free(fn);
|
||||||
if (image_size < 0) {
|
if (image_size < 0) {
|
||||||
error_report("Could not load ROM image '%s'", bios_name);
|
error_report("Could not load ROM image '%s'", file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
create_one_flash("virt.flash0", flashbase, flashsize);
|
static void create_flash(const VirtBoardInfo *vbi,
|
||||||
create_one_flash("virt.flash1", flashbase + flashsize, flashsize);
|
MemoryRegion *sysmem,
|
||||||
|
MemoryRegion *secure_sysmem)
|
||||||
|
{
|
||||||
|
/* Create two flash devices to fill the VIRT_FLASH space in the memmap.
|
||||||
|
* Any file passed via -bios goes in the first of these.
|
||||||
|
* sysmem is the system memory space. secure_sysmem is the secure view
|
||||||
|
* of the system, and the first flash device should be made visible only
|
||||||
|
* there. The second flash device is visible to both secure and nonsecure.
|
||||||
|
* If sysmem == secure_sysmem this means there is no separate Secure
|
||||||
|
* address space and both flash devices are generally visible.
|
||||||
|
*/
|
||||||
|
hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2;
|
||||||
|
hwaddr flashbase = vbi->memmap[VIRT_FLASH].base;
|
||||||
|
char *nodename;
|
||||||
|
|
||||||
|
create_one_flash("virt.flash0", flashbase, flashsize,
|
||||||
|
bios_name, secure_sysmem);
|
||||||
|
create_one_flash("virt.flash1", flashbase + flashsize, flashsize,
|
||||||
|
NULL, sysmem);
|
||||||
|
|
||||||
|
if (sysmem == secure_sysmem) {
|
||||||
|
/* Report both flash devices as a single node in the DT */
|
||||||
nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
|
nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
|
||||||
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
|
||||||
@@ -750,6 +787,28 @@ static void create_flash(const VirtBoardInfo *vbi)
|
|||||||
2, flashbase + flashsize, 2, flashsize);
|
2, flashbase + flashsize, 2, flashsize);
|
||||||
qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
|
qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
|
||||||
g_free(nodename);
|
g_free(nodename);
|
||||||
|
} else {
|
||||||
|
/* Report the devices as separate nodes so we can mark one as
|
||||||
|
* only visible to the secure world.
|
||||||
|
*/
|
||||||
|
nodename = g_strdup_printf("/secflash@%" PRIx64, flashbase);
|
||||||
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
|
||||||
|
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
||||||
|
2, flashbase, 2, flashsize);
|
||||||
|
qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled");
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay");
|
||||||
|
g_free(nodename);
|
||||||
|
|
||||||
|
nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
|
||||||
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
|
||||||
|
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
||||||
|
2, flashbase + flashsize, 2, flashsize);
|
||||||
|
qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
|
||||||
|
g_free(nodename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as)
|
static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as)
|
||||||
@@ -960,6 +1019,27 @@ static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic)
|
|||||||
sysbus_mmio_get_region(s, 0));
|
sysbus_mmio_get_region(s, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void create_secure_ram(VirtBoardInfo *vbi, MemoryRegion *secure_sysmem)
|
||||||
|
{
|
||||||
|
MemoryRegion *secram = g_new(MemoryRegion, 1);
|
||||||
|
char *nodename;
|
||||||
|
hwaddr base = vbi->memmap[VIRT_SECURE_MEM].base;
|
||||||
|
hwaddr size = vbi->memmap[VIRT_SECURE_MEM].size;
|
||||||
|
|
||||||
|
memory_region_init_ram(secram, NULL, "virt.secure-ram", size, &error_fatal);
|
||||||
|
vmstate_register_ram_global(secram);
|
||||||
|
memory_region_add_subregion(secure_sysmem, base, secram);
|
||||||
|
|
||||||
|
nodename = g_strdup_printf("/secram@%" PRIx64, base);
|
||||||
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "memory");
|
||||||
|
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base, 2, size);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled");
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay");
|
||||||
|
|
||||||
|
g_free(nodename);
|
||||||
|
}
|
||||||
|
|
||||||
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
|
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
|
||||||
{
|
{
|
||||||
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
|
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
|
||||||
@@ -1020,6 +1100,7 @@ static void machvirt_init(MachineState *machine)
|
|||||||
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
|
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
|
||||||
VirtGuestInfo *guest_info = &guest_info_state->info;
|
VirtGuestInfo *guest_info = &guest_info_state->info;
|
||||||
char **cpustr;
|
char **cpustr;
|
||||||
|
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
||||||
|
|
||||||
if (!cpu_model) {
|
if (!cpu_model) {
|
||||||
cpu_model = "cortex-a15";
|
cpu_model = "cortex-a15";
|
||||||
@@ -1047,6 +1128,15 @@ static void machvirt_init(MachineState *machine)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we have an EL3 boot ROM then the assumption is that it will
|
||||||
|
* implement PSCI itself, so disable QEMU's internal implementation
|
||||||
|
* so it doesn't get in the way. Instead of starting secondary
|
||||||
|
* CPUs in PSCI powerdown state we will start them all running and
|
||||||
|
* let the boot ROM sort them out.
|
||||||
|
* The usual case is that we do use QEMU's PSCI implementation.
|
||||||
|
*/
|
||||||
|
vbi->using_psci = !(vms->secure && firmware_loaded);
|
||||||
|
|
||||||
/* The maximum number of CPUs depends on the GIC version, or on how
|
/* The maximum number of CPUs depends on the GIC version, or on how
|
||||||
* many redistributors we can fit into the memory map.
|
* many redistributors we can fit into the memory map.
|
||||||
*/
|
*/
|
||||||
@@ -1066,7 +1156,7 @@ static void machvirt_init(MachineState *machine)
|
|||||||
vbi->smp_cpus = smp_cpus;
|
vbi->smp_cpus = smp_cpus;
|
||||||
|
|
||||||
if (machine->ram_size > vbi->memmap[VIRT_MEM].size) {
|
if (machine->ram_size > vbi->memmap[VIRT_MEM].size) {
|
||||||
error_report("mach-virt: cannot model more than 30GB RAM");
|
error_report("mach-virt: cannot model more than %dGB RAM", RAMLIMIT_GB);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1114,12 +1204,15 @@ static void machvirt_init(MachineState *machine)
|
|||||||
object_property_set_bool(cpuobj, false, "has_el3", NULL);
|
object_property_set_bool(cpuobj, false, "has_el3", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, "psci-conduit",
|
if (vbi->using_psci) {
|
||||||
NULL);
|
object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC,
|
||||||
|
"psci-conduit", NULL);
|
||||||
|
|
||||||
/* Secondary CPUs start in PSCI powered-down state */
|
/* Secondary CPUs start in PSCI powered-down state */
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
object_property_set_bool(cpuobj, true, "start-powered-off", NULL);
|
object_property_set_bool(cpuobj, true,
|
||||||
|
"start-powered-off", NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
|
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
|
||||||
@@ -1145,13 +1238,14 @@ static void machvirt_init(MachineState *machine)
|
|||||||
machine->ram_size);
|
machine->ram_size);
|
||||||
memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram);
|
memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram);
|
||||||
|
|
||||||
create_flash(vbi);
|
create_flash(vbi, sysmem, secure_sysmem ? secure_sysmem : sysmem);
|
||||||
|
|
||||||
create_gic(vbi, pic, gic_version, vms->secure);
|
create_gic(vbi, pic, gic_version, vms->secure);
|
||||||
|
|
||||||
create_uart(vbi, pic, VIRT_UART, sysmem);
|
create_uart(vbi, pic, VIRT_UART, sysmem);
|
||||||
|
|
||||||
if (vms->secure) {
|
if (vms->secure) {
|
||||||
|
create_secure_ram(vbi, secure_sysmem);
|
||||||
create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem);
|
create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1187,7 +1281,7 @@ static void machvirt_init(MachineState *machine)
|
|||||||
vbi->bootinfo.board_id = -1;
|
vbi->bootinfo.board_id = -1;
|
||||||
vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base;
|
vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base;
|
||||||
vbi->bootinfo.get_dtb = machvirt_dtb;
|
vbi->bootinfo.get_dtb = machvirt_dtb;
|
||||||
vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
vbi->bootinfo.firmware_loaded = firmware_loaded;
|
||||||
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo);
|
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1251,7 +1345,32 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virt_instance_init(Object *obj)
|
static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
|
||||||
|
mc->init = machvirt_init;
|
||||||
|
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
|
||||||
|
* it later in machvirt_init, where we have more information about the
|
||||||
|
* configuration of the particular instance.
|
||||||
|
*/
|
||||||
|
mc->max_cpus = MAX_CPUMASK_BITS;
|
||||||
|
mc->has_dynamic_sysbus = true;
|
||||||
|
mc->block_default_type = IF_VIRTIO;
|
||||||
|
mc->no_cdrom = 1;
|
||||||
|
mc->pci_allow_0_address = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo virt_machine_info = {
|
||||||
|
.name = TYPE_VIRT_MACHINE,
|
||||||
|
.parent = TYPE_MACHINE,
|
||||||
|
.abstract = true,
|
||||||
|
.instance_size = sizeof(VirtMachineState),
|
||||||
|
.class_size = sizeof(VirtMachineClass),
|
||||||
|
.class_init = virt_machine_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void virt_2_6_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
VirtMachineState *vms = VIRT_MACHINE(obj);
|
VirtMachineState *vms = VIRT_MACHINE(obj);
|
||||||
|
|
||||||
@@ -1284,35 +1403,29 @@ static void virt_instance_init(Object *obj)
|
|||||||
"Valid values are 2, 3 and host", NULL);
|
"Valid values are 2, 3 and host", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virt_class_init(ObjectClass *oc, void *data)
|
static void virt_2_6_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
MachineClass *mc = MACHINE_CLASS(oc);
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
static GlobalProperty compat_props[] = {
|
||||||
|
{ /* end of list */ }
|
||||||
|
};
|
||||||
|
|
||||||
mc->desc = "ARM Virtual Machine",
|
mc->desc = "QEMU 2.6 ARM Virtual Machine";
|
||||||
mc->init = machvirt_init;
|
mc->alias = "virt";
|
||||||
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
|
mc->compat_props = compat_props;
|
||||||
* it later in machvirt_init, where we have more information about the
|
|
||||||
* configuration of the particular instance.
|
|
||||||
*/
|
|
||||||
mc->max_cpus = MAX_CPUMASK_BITS;
|
|
||||||
mc->has_dynamic_sysbus = true;
|
|
||||||
mc->block_default_type = IF_VIRTIO;
|
|
||||||
mc->no_cdrom = 1;
|
|
||||||
mc->pci_allow_0_address = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo machvirt_info = {
|
static const TypeInfo machvirt_info = {
|
||||||
.name = TYPE_VIRT_MACHINE,
|
.name = MACHINE_TYPE_NAME("virt-2.6"),
|
||||||
.parent = TYPE_MACHINE,
|
.parent = TYPE_VIRT_MACHINE,
|
||||||
.instance_size = sizeof(VirtMachineState),
|
.instance_init = virt_2_6_instance_init,
|
||||||
.instance_init = virt_instance_init,
|
.class_init = virt_2_6_class_init,
|
||||||
.class_size = sizeof(VirtMachineClass),
|
|
||||||
.class_init = virt_class_init,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void machvirt_machine_init(void)
|
static void machvirt_machine_init(void)
|
||||||
{
|
{
|
||||||
|
type_register_static(&virt_machine_info);
|
||||||
type_register_static(&machvirt_info);
|
type_register_static(&machvirt_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
machine_init(machvirt_machine_init);
|
type_init(machvirt_machine_init);
|
||||||
|
|||||||
@@ -2557,6 +2557,29 @@ FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
|
|||||||
return isa->state.drives[i].drive;
|
return isa->state.drives[i].drive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void isa_fdc_get_drive_max_chs(FloppyDriveType type,
|
||||||
|
uint8_t *maxc, uint8_t *maxh, uint8_t *maxs)
|
||||||
|
{
|
||||||
|
const FDFormat *fdf;
|
||||||
|
|
||||||
|
*maxc = *maxh = *maxs = 0;
|
||||||
|
for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) {
|
||||||
|
if (fdf->drive != type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*maxc < fdf->max_track) {
|
||||||
|
*maxc = fdf->max_track;
|
||||||
|
}
|
||||||
|
if (*maxh < fdf->max_head) {
|
||||||
|
*maxh = fdf->max_head;
|
||||||
|
}
|
||||||
|
if (*maxs < fdf->last_sect) {
|
||||||
|
*maxs = fdf->last_sect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(*maxc)--;
|
||||||
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_isa_fdc ={
|
static const VMStateDescription vmstate_isa_fdc ={
|
||||||
.name = "fdc",
|
.name = "fdc",
|
||||||
.version_id = 2,
|
.version_id = 2,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o
|
|||||||
obj-$(CONFIG_PSERIES) += spapr_vty.o
|
obj-$(CONFIG_PSERIES) += spapr_vty.o
|
||||||
obj-$(CONFIG_DIGIC) += digic-uart.o
|
obj-$(CONFIG_DIGIC) += digic-uart.o
|
||||||
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
|
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
|
||||||
|
obj-$(CONFIG_RASPI) += bcm2835_aux.o
|
||||||
|
|
||||||
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
||||||
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
|
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
|
||||||
|
|||||||
316
hw/char/bcm2835_aux.c
Normal file
316
hw/char/bcm2835_aux.c
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
* BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
|
||||||
|
* Copyright (c) 2015, Microsoft
|
||||||
|
* Written by Andrew Baumann
|
||||||
|
* Based on pl011.c, copyright terms below:
|
||||||
|
*
|
||||||
|
* Arm PrimeCell PL011 UART
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 CodeSourcery.
|
||||||
|
* Written by Paul Brook
|
||||||
|
*
|
||||||
|
* This code is licensed under the GPL.
|
||||||
|
*
|
||||||
|
* At present only the core UART functions (data path for tx/rx) are
|
||||||
|
* implemented. The following features/registers are unimplemented:
|
||||||
|
* - Line/modem control
|
||||||
|
* - Scratch register
|
||||||
|
* - Extra control
|
||||||
|
* - Baudrate
|
||||||
|
* - SPI interfaces
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hw/char/bcm2835_aux.h"
|
||||||
|
|
||||||
|
#define AUX_IRQ 0x0
|
||||||
|
#define AUX_ENABLES 0x4
|
||||||
|
#define AUX_MU_IO_REG 0x40
|
||||||
|
#define AUX_MU_IER_REG 0x44
|
||||||
|
#define AUX_MU_IIR_REG 0x48
|
||||||
|
#define AUX_MU_LCR_REG 0x4c
|
||||||
|
#define AUX_MU_MCR_REG 0x50
|
||||||
|
#define AUX_MU_LSR_REG 0x54
|
||||||
|
#define AUX_MU_MSR_REG 0x58
|
||||||
|
#define AUX_MU_SCRATCH 0x5c
|
||||||
|
#define AUX_MU_CNTL_REG 0x60
|
||||||
|
#define AUX_MU_STAT_REG 0x64
|
||||||
|
#define AUX_MU_BAUD_REG 0x68
|
||||||
|
|
||||||
|
/* bits in IER/IIR registers */
|
||||||
|
#define TX_INT 0x1
|
||||||
|
#define RX_INT 0x2
|
||||||
|
|
||||||
|
static void bcm2835_aux_update(BCM2835AuxState *s)
|
||||||
|
{
|
||||||
|
/* signal an interrupt if either:
|
||||||
|
* 1. rx interrupt is enabled and we have a non-empty rx fifo, or
|
||||||
|
* 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
|
||||||
|
*/
|
||||||
|
s->iir = 0;
|
||||||
|
if ((s->ier & RX_INT) && s->read_count != 0) {
|
||||||
|
s->iir |= RX_INT;
|
||||||
|
}
|
||||||
|
if (s->ier & TX_INT) {
|
||||||
|
s->iir |= TX_INT;
|
||||||
|
}
|
||||||
|
qemu_set_irq(s->irq, s->iir != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835AuxState *s = opaque;
|
||||||
|
uint32_t c, res;
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case AUX_IRQ:
|
||||||
|
return s->iir != 0;
|
||||||
|
|
||||||
|
case AUX_ENABLES:
|
||||||
|
return 1; /* mini UART permanently enabled */
|
||||||
|
|
||||||
|
case AUX_MU_IO_REG:
|
||||||
|
/* "DLAB bit set means access baudrate register" is NYI */
|
||||||
|
c = s->read_fifo[s->read_pos];
|
||||||
|
if (s->read_count > 0) {
|
||||||
|
s->read_count--;
|
||||||
|
if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
|
||||||
|
s->read_pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s->chr) {
|
||||||
|
qemu_chr_accept_input(s->chr);
|
||||||
|
}
|
||||||
|
bcm2835_aux_update(s);
|
||||||
|
return c;
|
||||||
|
|
||||||
|
case AUX_MU_IER_REG:
|
||||||
|
/* "DLAB bit set means access baudrate register" is NYI */
|
||||||
|
return 0xc0 | s->ier; /* FIFO enables always read 1 */
|
||||||
|
|
||||||
|
case AUX_MU_IIR_REG:
|
||||||
|
res = 0xc0; /* FIFO enables */
|
||||||
|
/* The spec is unclear on what happens when both tx and rx
|
||||||
|
* interrupts are active, besides that this cannot occur. At
|
||||||
|
* present, we choose to prioritise the rx interrupt, since
|
||||||
|
* the tx fifo is always empty. */
|
||||||
|
if (s->read_count != 0) {
|
||||||
|
res |= 0x4;
|
||||||
|
} else {
|
||||||
|
res |= 0x2;
|
||||||
|
}
|
||||||
|
if (s->iir == 0) {
|
||||||
|
res |= 0x1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
|
||||||
|
case AUX_MU_LCR_REG:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUX_MU_MCR_REG:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUX_MU_LSR_REG:
|
||||||
|
res = 0x60; /* tx idle, empty */
|
||||||
|
if (s->read_count != 0) {
|
||||||
|
res |= 0x1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
|
||||||
|
case AUX_MU_MSR_REG:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUX_MU_SCRATCH:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUX_MU_CNTL_REG:
|
||||||
|
return 0x3; /* tx, rx enabled */
|
||||||
|
|
||||||
|
case AUX_MU_STAT_REG:
|
||||||
|
res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
|
||||||
|
if (s->read_count > 0) {
|
||||||
|
res |= 0x1; /* data in input buffer */
|
||||||
|
assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
|
||||||
|
res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
|
||||||
|
case AUX_MU_BAUD_REG:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835AuxState *s = opaque;
|
||||||
|
unsigned char ch;
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case AUX_ENABLES:
|
||||||
|
if (value != 1) {
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
|
||||||
|
"or disable UART\n", __func__);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUX_MU_IO_REG:
|
||||||
|
/* "DLAB bit set means access baudrate register" is NYI */
|
||||||
|
ch = value;
|
||||||
|
if (s->chr) {
|
||||||
|
qemu_chr_fe_write(s->chr, &ch, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUX_MU_IER_REG:
|
||||||
|
/* "DLAB bit set means access baudrate register" is NYI */
|
||||||
|
s->ier = value & (TX_INT | RX_INT);
|
||||||
|
bcm2835_aux_update(s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUX_MU_IIR_REG:
|
||||||
|
if (value & 0x2) {
|
||||||
|
s->read_count = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUX_MU_LCR_REG:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUX_MU_MCR_REG:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUX_MU_SCRATCH:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUX_MU_CNTL_REG:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUX_MU_BAUD_REG:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bcm2835_aux_update(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm2835_aux_can_receive(void *opaque)
|
||||||
|
{
|
||||||
|
BCM2835AuxState *s = opaque;
|
||||||
|
|
||||||
|
return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
|
||||||
|
{
|
||||||
|
BCM2835AuxState *s = opaque;
|
||||||
|
int slot;
|
||||||
|
|
||||||
|
slot = s->read_pos + s->read_count;
|
||||||
|
if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
|
||||||
|
slot -= BCM2835_AUX_RX_FIFO_LEN;
|
||||||
|
}
|
||||||
|
s->read_fifo[slot] = value;
|
||||||
|
s->read_count++;
|
||||||
|
if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
|
||||||
|
/* buffer full */
|
||||||
|
}
|
||||||
|
bcm2835_aux_update(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
|
||||||
|
{
|
||||||
|
bcm2835_aux_put_fifo(opaque, *buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps bcm2835_aux_ops = {
|
||||||
|
.read = bcm2835_aux_read,
|
||||||
|
.write = bcm2835_aux_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
.valid.min_access_size = 4,
|
||||||
|
.valid.max_access_size = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_bcm2835_aux = {
|
||||||
|
.name = TYPE_BCM2835_AUX,
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
|
||||||
|
BCM2835_AUX_RX_FIFO_LEN),
|
||||||
|
VMSTATE_UINT8(read_pos, BCM2835AuxState),
|
||||||
|
VMSTATE_UINT8(read_count, BCM2835AuxState),
|
||||||
|
VMSTATE_UINT8(ier, BCM2835AuxState),
|
||||||
|
VMSTATE_UINT8(iir, BCM2835AuxState),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_aux_init(Object *obj)
|
||||||
|
{
|
||||||
|
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||||
|
BCM2835AuxState *s = BCM2835_AUX(obj);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
|
||||||
|
TYPE_BCM2835_AUX, 0x100);
|
||||||
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
|
sysbus_init_irq(sbd, &s->irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
BCM2835AuxState *s = BCM2835_AUX(dev);
|
||||||
|
|
||||||
|
if (s->chr) {
|
||||||
|
qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive,
|
||||||
|
bcm2835_aux_receive, NULL, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property bcm2835_aux_props[] = {
|
||||||
|
DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
|
||||||
|
dc->realize = bcm2835_aux_realize;
|
||||||
|
dc->vmsd = &vmstate_bcm2835_aux;
|
||||||
|
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||||
|
dc->props = bcm2835_aux_props;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo bcm2835_aux_info = {
|
||||||
|
.name = TYPE_BCM2835_AUX,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(BCM2835AuxState),
|
||||||
|
.instance_init = bcm2835_aux_init,
|
||||||
|
.class_init = bcm2835_aux_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_aux_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&bcm2835_aux_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(bcm2835_aux_register_types)
|
||||||
@@ -842,14 +842,16 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
|
|||||||
{
|
{
|
||||||
ChannelState *s = (ChannelState *)dev;
|
ChannelState *s = (ChannelState *)dev;
|
||||||
int qcode, keycode;
|
int qcode, keycode;
|
||||||
|
InputKeyEvent *key;
|
||||||
|
|
||||||
assert(evt->type == INPUT_EVENT_KIND_KEY);
|
assert(evt->type == INPUT_EVENT_KIND_KEY);
|
||||||
qcode = qemu_input_key_value_to_qcode(evt->u.key->key);
|
key = evt->u.key;
|
||||||
|
qcode = qemu_input_key_value_to_qcode(key->key);
|
||||||
trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
|
trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
|
||||||
evt->u.key->down);
|
key->down);
|
||||||
|
|
||||||
if (qcode == Q_KEY_CODE_CAPS_LOCK) {
|
if (qcode == Q_KEY_CODE_CAPS_LOCK) {
|
||||||
if (evt->u.key->down) {
|
if (key->down) {
|
||||||
s->caps_lock_mode ^= 1;
|
s->caps_lock_mode ^= 1;
|
||||||
if (s->caps_lock_mode == 2) {
|
if (s->caps_lock_mode == 2) {
|
||||||
return; /* Drop second press */
|
return; /* Drop second press */
|
||||||
@@ -863,7 +865,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (qcode == Q_KEY_CODE_NUM_LOCK) {
|
if (qcode == Q_KEY_CODE_NUM_LOCK) {
|
||||||
if (evt->u.key->down) {
|
if (key->down) {
|
||||||
s->num_lock_mode ^= 1;
|
s->num_lock_mode ^= 1;
|
||||||
if (s->num_lock_mode == 2) {
|
if (s->num_lock_mode == 2) {
|
||||||
return; /* Drop second press */
|
return; /* Drop second press */
|
||||||
@@ -877,7 +879,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
|
|||||||
}
|
}
|
||||||
|
|
||||||
keycode = qcode_to_keycode[qcode];
|
keycode = qcode_to_keycode[qcode];
|
||||||
if (!evt->u.key->down) {
|
if (!key->down) {
|
||||||
keycode |= 0x80;
|
keycode |= 0x80;
|
||||||
}
|
}
|
||||||
trace_escc_sunkbd_event_out(keycode);
|
trace_escc_sunkbd_event_out(keycode);
|
||||||
|
|||||||
@@ -147,6 +147,28 @@ int load_image_targphys(const char *filename,
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int load_image_mr(const char *filename, MemoryRegion *mr)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (!memory_access_is_direct(mr, false)) {
|
||||||
|
/* Can only load an image into RAM or ROM */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = get_image_size(filename);
|
||||||
|
|
||||||
|
if (size > memory_region_size(mr)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (size > 0) {
|
||||||
|
if (rom_add_file_mr(filename, mr, -1) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size,
|
void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size,
|
||||||
const char *source)
|
const char *source)
|
||||||
{
|
{
|
||||||
@@ -332,10 +354,66 @@ const char *load_elf_strerror(int error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
uint8_t e_ident_local[EI_NIDENT];
|
||||||
|
uint8_t *e_ident;
|
||||||
|
size_t hdr_size, off;
|
||||||
|
bool is64l;
|
||||||
|
|
||||||
|
if (!hdr) {
|
||||||
|
hdr = e_ident_local;
|
||||||
|
}
|
||||||
|
e_ident = hdr;
|
||||||
|
|
||||||
|
fd = open(filename, O_RDONLY | O_BINARY);
|
||||||
|
if (fd < 0) {
|
||||||
|
error_setg_errno(errp, errno, "Failed to open file: %s", filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (read(fd, hdr, EI_NIDENT) != EI_NIDENT) {
|
||||||
|
error_setg_errno(errp, errno, "Failed to read file: %s", filename);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (e_ident[0] != ELFMAG0 ||
|
||||||
|
e_ident[1] != ELFMAG1 ||
|
||||||
|
e_ident[2] != ELFMAG2 ||
|
||||||
|
e_ident[3] != ELFMAG3) {
|
||||||
|
error_setg(errp, "Bad ELF magic");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
is64l = e_ident[EI_CLASS] == ELFCLASS64;
|
||||||
|
hdr_size = is64l ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr);
|
||||||
|
if (is64) {
|
||||||
|
*is64 = is64l;
|
||||||
|
}
|
||||||
|
|
||||||
|
off = EI_NIDENT;
|
||||||
|
while (hdr != e_ident_local && off < hdr_size) {
|
||||||
|
size_t br = read(fd, hdr + off, hdr_size - off);
|
||||||
|
switch (br) {
|
||||||
|
case 0:
|
||||||
|
error_setg(errp, "File too short: %s", filename);
|
||||||
|
goto fail;
|
||||||
|
case -1:
|
||||||
|
error_setg_errno(errp, errno, "Failed to read file: %s",
|
||||||
|
filename);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
off += br;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
/* return < 0 if error, otherwise the number of bytes loaded in memory */
|
/* return < 0 if error, otherwise the number of bytes loaded in memory */
|
||||||
int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
||||||
void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
|
void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
|
||||||
uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
|
uint64_t *highaddr, int big_endian, int elf_machine,
|
||||||
|
int clear_lsb, int data_swab)
|
||||||
{
|
{
|
||||||
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
|
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
|
||||||
uint8_t e_ident[EI_NIDENT];
|
uint8_t e_ident[EI_NIDENT];
|
||||||
@@ -374,10 +452,12 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
|||||||
lseek(fd, 0, SEEK_SET);
|
lseek(fd, 0, SEEK_SET);
|
||||||
if (e_ident[EI_CLASS] == ELFCLASS64) {
|
if (e_ident[EI_CLASS] == ELFCLASS64) {
|
||||||
ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
|
ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
|
||||||
pentry, lowaddr, highaddr, elf_machine, clear_lsb);
|
pentry, lowaddr, highaddr, elf_machine, clear_lsb,
|
||||||
|
data_swab);
|
||||||
} else {
|
} else {
|
||||||
ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
|
ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
|
||||||
pentry, lowaddr, highaddr, elf_machine, clear_lsb);
|
pentry, lowaddr, highaddr, elf_machine, clear_lsb,
|
||||||
|
data_swab);
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@@ -751,7 +831,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name)
|
|||||||
|
|
||||||
int rom_add_file(const char *file, const char *fw_dir,
|
int rom_add_file(const char *file, const char *fw_dir,
|
||||||
hwaddr addr, int32_t bootindex,
|
hwaddr addr, int32_t bootindex,
|
||||||
bool option_rom)
|
bool option_rom, MemoryRegion *mr)
|
||||||
{
|
{
|
||||||
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
||||||
Rom *rom;
|
Rom *rom;
|
||||||
@@ -817,9 +897,14 @@ int rom_add_file(const char *file, const char *fw_dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize);
|
fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize);
|
||||||
|
} else {
|
||||||
|
if (mr) {
|
||||||
|
rom->mr = mr;
|
||||||
|
snprintf(devpath, sizeof(devpath), "/rom@%s", file);
|
||||||
} else {
|
} else {
|
||||||
snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
|
snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
add_boot_device_path(bootindex, NULL, devpath);
|
add_boot_device_path(bootindex, NULL, devpath);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -892,12 +977,12 @@ int rom_add_elf_program(const char *name, void *data, size_t datasize,
|
|||||||
|
|
||||||
int rom_add_vga(const char *file)
|
int rom_add_vga(const char *file)
|
||||||
{
|
{
|
||||||
return rom_add_file(file, "vgaroms", 0, -1, true);
|
return rom_add_file(file, "vgaroms", 0, -1, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int rom_add_option(const char *file, int32_t bootindex)
|
int rom_add_option(const char *file, int32_t bootindex)
|
||||||
{
|
{
|
||||||
return rom_add_file(file, "genroms", 0, bootindex, true);
|
return rom_add_file(file, "genroms", 0, bootindex, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rom_reset(void *unused)
|
static void rom_reset(void *unused)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
|
|||||||
/* Memory map (addresses are offsets from PERIPHBASE):
|
/* Memory map (addresses are offsets from PERIPHBASE):
|
||||||
* 0x0000-0x0fff -- reserved
|
* 0x0000-0x0fff -- reserved
|
||||||
* 0x1000-0x1fff -- GIC Distributor
|
* 0x1000-0x1fff -- GIC Distributor
|
||||||
* 0x2000-0x2fff -- GIC CPU interface
|
* 0x2000-0x3fff -- GIC CPU interface
|
||||||
* 0x4000-0x4fff -- GIC virtual interface control (not modelled)
|
* 0x4000-0x4fff -- GIC virtual interface control (not modelled)
|
||||||
* 0x5000-0x5fff -- GIC virtual interface control (not modelled)
|
* 0x5000-0x5fff -- GIC virtual interface control (not modelled)
|
||||||
* 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
|
* 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ void cris_load_image(CRISCPU *cpu, struct cris_load_info *li)
|
|||||||
/* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis
|
/* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis
|
||||||
devboard SDK. */
|
devboard SDK. */
|
||||||
image_size = load_elf(li->image_filename, translate_kernel_address, NULL,
|
image_size = load_elf(li->image_filename, translate_kernel_address, NULL,
|
||||||
&entry, NULL, &high, 0, EM_CRIS, 0);
|
&entry, NULL, &high, 0, EM_CRIS, 0, 0);
|
||||||
li->entry = entry;
|
li->entry = entry;
|
||||||
if (image_size < 0) {
|
if (image_size < 0) {
|
||||||
/* Takes a kimage from the axis devboard SDK. */
|
/* Takes a kimage from the axis devboard SDK. */
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ endif
|
|||||||
obj-$(CONFIG_OMAP) += omap_dss.o
|
obj-$(CONFIG_OMAP) += omap_dss.o
|
||||||
obj-$(CONFIG_OMAP) += omap_lcdc.o
|
obj-$(CONFIG_OMAP) += omap_lcdc.o
|
||||||
obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o
|
obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o
|
||||||
|
obj-$(CONFIG_RASPI) += bcm2835_fb.o
|
||||||
obj-$(CONFIG_SM501) += sm501.o
|
obj-$(CONFIG_SM501) += sm501.o
|
||||||
obj-$(CONFIG_TCX) += tcx.o
|
obj-$(CONFIG_TCX) += tcx.o
|
||||||
obj-$(CONFIG_CG3) += cg3.o
|
obj-$(CONFIG_CG3) += cg3.o
|
||||||
|
|||||||
424
hw/display/bcm2835_fb.c
Normal file
424
hw/display/bcm2835_fb.c
Normal file
@@ -0,0 +1,424 @@
|
|||||||
|
/*
|
||||||
|
* Raspberry Pi emulation (c) 2012 Gregory Estrade
|
||||||
|
* Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
|
||||||
|
* This code is licensed under the GNU GPLv2 and later.
|
||||||
|
*
|
||||||
|
* Heavily based on milkymist-vgafb.c, copyright terms below:
|
||||||
|
* QEMU model of the Milkymist VGA framebuffer.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hw/display/bcm2835_fb.h"
|
||||||
|
#include "hw/display/framebuffer.h"
|
||||||
|
#include "ui/pixel_ops.h"
|
||||||
|
#include "hw/misc/bcm2835_mbox_defs.h"
|
||||||
|
|
||||||
|
#define DEFAULT_VCRAM_SIZE 0x4000000
|
||||||
|
#define BCM2835_FB_OFFSET 0x00100000
|
||||||
|
|
||||||
|
static void fb_invalidate_display(void *opaque)
|
||||||
|
{
|
||||||
|
BCM2835FBState *s = BCM2835_FB(opaque);
|
||||||
|
|
||||||
|
s->invalidate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
|
||||||
|
int width, int deststep)
|
||||||
|
{
|
||||||
|
BCM2835FBState *s = opaque;
|
||||||
|
uint16_t rgb565;
|
||||||
|
uint32_t rgb888;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
DisplaySurface *surface = qemu_console_surface(s->con);
|
||||||
|
int bpp = surface_bits_per_pixel(surface);
|
||||||
|
|
||||||
|
while (width--) {
|
||||||
|
switch (s->bpp) {
|
||||||
|
case 8:
|
||||||
|
/* lookup palette starting at video ram base
|
||||||
|
* TODO: cache translation, rather than doing this each time!
|
||||||
|
*/
|
||||||
|
rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2));
|
||||||
|
r = (rgb888 >> 0) & 0xff;
|
||||||
|
g = (rgb888 >> 8) & 0xff;
|
||||||
|
b = (rgb888 >> 16) & 0xff;
|
||||||
|
src++;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
rgb565 = lduw_le_p(src);
|
||||||
|
r = ((rgb565 >> 11) & 0x1f) << 3;
|
||||||
|
g = ((rgb565 >> 5) & 0x3f) << 2;
|
||||||
|
b = ((rgb565 >> 0) & 0x1f) << 3;
|
||||||
|
src += 2;
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
rgb888 = ldl_le_p(src);
|
||||||
|
r = (rgb888 >> 0) & 0xff;
|
||||||
|
g = (rgb888 >> 8) & 0xff;
|
||||||
|
b = (rgb888 >> 16) & 0xff;
|
||||||
|
src += 3;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
rgb888 = ldl_le_p(src);
|
||||||
|
r = (rgb888 >> 0) & 0xff;
|
||||||
|
g = (rgb888 >> 8) & 0xff;
|
||||||
|
b = (rgb888 >> 16) & 0xff;
|
||||||
|
src += 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r = 0;
|
||||||
|
g = 0;
|
||||||
|
b = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->pixo == 0) {
|
||||||
|
/* swap to BGR pixel format */
|
||||||
|
uint8_t tmp = r;
|
||||||
|
r = b;
|
||||||
|
b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (bpp) {
|
||||||
|
case 8:
|
||||||
|
*dst++ = rgb_to_pixel8(r, g, b);
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
*(uint16_t *)dst = rgb_to_pixel15(r, g, b);
|
||||||
|
dst += 2;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
*(uint16_t *)dst = rgb_to_pixel16(r, g, b);
|
||||||
|
dst += 2;
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
rgb888 = rgb_to_pixel24(r, g, b);
|
||||||
|
*dst++ = rgb888 & 0xff;
|
||||||
|
*dst++ = (rgb888 >> 8) & 0xff;
|
||||||
|
*dst++ = (rgb888 >> 16) & 0xff;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
*(uint32_t *)dst = rgb_to_pixel32(r, g, b);
|
||||||
|
dst += 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fb_update_display(void *opaque)
|
||||||
|
{
|
||||||
|
BCM2835FBState *s = opaque;
|
||||||
|
DisplaySurface *surface = qemu_console_surface(s->con);
|
||||||
|
int first = 0;
|
||||||
|
int last = 0;
|
||||||
|
int src_width = 0;
|
||||||
|
int dest_width = 0;
|
||||||
|
|
||||||
|
if (s->lock || !s->xres) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
src_width = s->xres * (s->bpp >> 3);
|
||||||
|
dest_width = s->xres;
|
||||||
|
|
||||||
|
switch (surface_bits_per_pixel(surface)) {
|
||||||
|
case 0:
|
||||||
|
return;
|
||||||
|
case 8:
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
dest_width *= 2;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
dest_width *= 2;
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
dest_width *= 3;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
dest_width *= 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hw_error("bcm2835_fb: bad color depth\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->invalidate) {
|
||||||
|
framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base,
|
||||||
|
s->yres, src_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
|
||||||
|
src_width, dest_width, 0, s->invalidate,
|
||||||
|
draw_line_src16, s, &first, &last);
|
||||||
|
|
||||||
|
if (first >= 0) {
|
||||||
|
dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->invalidate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
|
||||||
|
{
|
||||||
|
value &= ~0xf;
|
||||||
|
|
||||||
|
s->lock = true;
|
||||||
|
|
||||||
|
s->xres = ldl_le_phys(&s->dma_as, value);
|
||||||
|
s->yres = ldl_le_phys(&s->dma_as, value + 4);
|
||||||
|
s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
|
||||||
|
s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
|
||||||
|
s->bpp = ldl_le_phys(&s->dma_as, value + 20);
|
||||||
|
s->xoffset = ldl_le_phys(&s->dma_as, value + 24);
|
||||||
|
s->yoffset = ldl_le_phys(&s->dma_as, value + 28);
|
||||||
|
|
||||||
|
s->base = s->vcram_base | (value & 0xc0000000);
|
||||||
|
s->base += BCM2835_FB_OFFSET;
|
||||||
|
|
||||||
|
/* TODO - Manage properly virtual resolution */
|
||||||
|
|
||||||
|
s->pitch = s->xres * (s->bpp >> 3);
|
||||||
|
s->size = s->yres * s->pitch;
|
||||||
|
|
||||||
|
stl_le_phys(&s->dma_as, value + 16, s->pitch);
|
||||||
|
stl_le_phys(&s->dma_as, value + 32, s->base);
|
||||||
|
stl_le_phys(&s->dma_as, value + 36, s->size);
|
||||||
|
|
||||||
|
s->invalidate = true;
|
||||||
|
qemu_console_resize(s->con, s->xres, s->yres);
|
||||||
|
s->lock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
|
||||||
|
uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
|
||||||
|
uint32_t *pixo, uint32_t *alpha)
|
||||||
|
{
|
||||||
|
s->lock = true;
|
||||||
|
|
||||||
|
/* TODO: input validation! */
|
||||||
|
if (xres) {
|
||||||
|
s->xres = *xres;
|
||||||
|
}
|
||||||
|
if (yres) {
|
||||||
|
s->yres = *yres;
|
||||||
|
}
|
||||||
|
if (xoffset) {
|
||||||
|
s->xoffset = *xoffset;
|
||||||
|
}
|
||||||
|
if (yoffset) {
|
||||||
|
s->yoffset = *yoffset;
|
||||||
|
}
|
||||||
|
if (bpp) {
|
||||||
|
s->bpp = *bpp;
|
||||||
|
}
|
||||||
|
if (pixo) {
|
||||||
|
s->pixo = *pixo;
|
||||||
|
}
|
||||||
|
if (alpha) {
|
||||||
|
s->alpha = *alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO - Manage properly virtual resolution */
|
||||||
|
|
||||||
|
s->pitch = s->xres * (s->bpp >> 3);
|
||||||
|
s->size = s->yres * s->pitch;
|
||||||
|
|
||||||
|
s->invalidate = true;
|
||||||
|
qemu_console_resize(s->con, s->xres, s->yres);
|
||||||
|
s->lock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835FBState *s = opaque;
|
||||||
|
uint32_t res = 0;
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case MBOX_AS_DATA:
|
||||||
|
res = MBOX_CHAN_FB;
|
||||||
|
s->pending = false;
|
||||||
|
qemu_set_irq(s->mbox_irq, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MBOX_AS_PENDING:
|
||||||
|
res = s->pending;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835FBState *s = opaque;
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case MBOX_AS_DATA:
|
||||||
|
/* bcm2835_mbox should check our pending status before pushing */
|
||||||
|
assert(!s->pending);
|
||||||
|
s->pending = true;
|
||||||
|
bcm2835_fb_mbox_push(s, value);
|
||||||
|
qemu_set_irq(s->mbox_irq, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps bcm2835_fb_ops = {
|
||||||
|
.read = bcm2835_fb_read,
|
||||||
|
.write = bcm2835_fb_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
.valid.min_access_size = 4,
|
||||||
|
.valid.max_access_size = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_bcm2835_fb = {
|
||||||
|
.name = TYPE_BCM2835_FB,
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_BOOL(lock, BCM2835FBState),
|
||||||
|
VMSTATE_BOOL(invalidate, BCM2835FBState),
|
||||||
|
VMSTATE_BOOL(pending, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(xres, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(yres, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(xres_virtual, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(yres_virtual, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(xoffset, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(yoffset, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(bpp, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(base, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(pitch, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(size, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(pixo, BCM2835FBState),
|
||||||
|
VMSTATE_UINT32(alpha, BCM2835FBState),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const GraphicHwOps vgafb_ops = {
|
||||||
|
.invalidate = fb_invalidate_display,
|
||||||
|
.gfx_update = fb_update_display,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_fb_init(Object *obj)
|
||||||
|
{
|
||||||
|
BCM2835FBState *s = BCM2835_FB(obj);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB,
|
||||||
|
0x10);
|
||||||
|
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
|
||||||
|
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_fb_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
BCM2835FBState *s = BCM2835_FB(dev);
|
||||||
|
|
||||||
|
s->pending = false;
|
||||||
|
|
||||||
|
s->xres_virtual = s->xres;
|
||||||
|
s->yres_virtual = s->yres;
|
||||||
|
s->xoffset = 0;
|
||||||
|
s->yoffset = 0;
|
||||||
|
s->base = s->vcram_base + BCM2835_FB_OFFSET;
|
||||||
|
s->pitch = s->xres * (s->bpp >> 3);
|
||||||
|
s->size = s->yres * s->pitch;
|
||||||
|
|
||||||
|
s->invalidate = true;
|
||||||
|
s->lock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
BCM2835FBState *s = BCM2835_FB(dev);
|
||||||
|
Error *err = NULL;
|
||||||
|
Object *obj;
|
||||||
|
|
||||||
|
if (s->vcram_base == 0) {
|
||||||
|
error_setg(errp, "%s: required vcram-base property not set", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
|
||||||
|
if (obj == NULL) {
|
||||||
|
error_setg(errp, "%s: required dma-mr link not found: %s",
|
||||||
|
__func__, error_get_pretty(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->dma_mr = MEMORY_REGION(obj);
|
||||||
|
address_space_init(&s->dma_as, s->dma_mr, NULL);
|
||||||
|
|
||||||
|
bcm2835_fb_reset(dev);
|
||||||
|
|
||||||
|
s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
|
||||||
|
qemu_console_resize(s->con, s->xres, s->yres);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property bcm2835_fb_props[] = {
|
||||||
|
DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
|
||||||
|
DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
|
||||||
|
DEFAULT_VCRAM_SIZE),
|
||||||
|
DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
|
||||||
|
DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
|
||||||
|
DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
|
||||||
|
DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
|
||||||
|
DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
|
||||||
|
DEFINE_PROP_END_OF_LIST()
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_fb_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->props = bcm2835_fb_props;
|
||||||
|
dc->realize = bcm2835_fb_realize;
|
||||||
|
dc->reset = bcm2835_fb_reset;
|
||||||
|
dc->vmsd = &vmstate_bcm2835_fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeInfo bcm2835_fb_info = {
|
||||||
|
.name = TYPE_BCM2835_FB,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(BCM2835FBState),
|
||||||
|
.class_init = bcm2835_fb_class_init,
|
||||||
|
.instance_init = bcm2835_fb_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_fb_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&bcm2835_fb_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(bcm2835_fb_register_types)
|
||||||
@@ -276,14 +276,14 @@ static bool blit_region_is_unsafe(struct CirrusVGAState *s,
|
|||||||
+ ((int64_t)s->cirrus_blt_height-1) * pitch;
|
+ ((int64_t)s->cirrus_blt_height-1) * pitch;
|
||||||
int32_t max = addr
|
int32_t max = addr
|
||||||
+ s->cirrus_blt_width;
|
+ s->cirrus_blt_width;
|
||||||
if (min < 0 || max >= s->vga.vram_size) {
|
if (min < 0 || max > s->vga.vram_size) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int64_t max = addr
|
int64_t max = addr
|
||||||
+ ((int64_t)s->cirrus_blt_height-1) * pitch
|
+ ((int64_t)s->cirrus_blt_height-1) * pitch
|
||||||
+ s->cirrus_blt_width;
|
+ s->cirrus_blt_width;
|
||||||
if (max >= s->vga.vram_size) {
|
if (max > s->vga.vram_size) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1156,7 +1156,9 @@ static void qxl_soft_reset(PCIQXLDevice *d)
|
|||||||
trace_qxl_soft_reset(d->id);
|
trace_qxl_soft_reset(d->id);
|
||||||
qxl_check_state(d);
|
qxl_check_state(d);
|
||||||
qxl_clear_guest_bug(d);
|
qxl_clear_guest_bug(d);
|
||||||
|
qemu_mutex_lock(&d->async_lock);
|
||||||
d->current_async = QXL_UNDEFINED_IO;
|
d->current_async = QXL_UNDEFINED_IO;
|
||||||
|
qemu_mutex_unlock(&d->async_lock);
|
||||||
|
|
||||||
if (d->id == 0) {
|
if (d->id == 0) {
|
||||||
qxl_enter_vga_mode(d);
|
qxl_enter_vga_mode(d);
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
|
|||||||
|
|
||||||
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
|
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
|
||||||
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
|
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
|
||||||
|
obj-$(CONFIG_RASPI) += bcm2835_dma.o
|
||||||
|
|||||||
408
hw/dma/bcm2835_dma.c
Normal file
408
hw/dma/bcm2835_dma.c
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
/*
|
||||||
|
* Raspberry Pi emulation (c) 2012 Gregory Estrade
|
||||||
|
* This code is licensed under the GNU GPLv2 and later.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hw/dma/bcm2835_dma.h"
|
||||||
|
|
||||||
|
/* DMA CS Control and Status bits */
|
||||||
|
#define BCM2708_DMA_ACTIVE (1 << 0)
|
||||||
|
#define BCM2708_DMA_END (1 << 1) /* GE */
|
||||||
|
#define BCM2708_DMA_INT (1 << 2)
|
||||||
|
#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */
|
||||||
|
#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */
|
||||||
|
#define BCM2708_DMA_ERR (1 << 8)
|
||||||
|
#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */
|
||||||
|
#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */
|
||||||
|
|
||||||
|
/* DMA control block "info" field bits */
|
||||||
|
#define BCM2708_DMA_INT_EN (1 << 0)
|
||||||
|
#define BCM2708_DMA_TDMODE (1 << 1)
|
||||||
|
#define BCM2708_DMA_WAIT_RESP (1 << 3)
|
||||||
|
#define BCM2708_DMA_D_INC (1 << 4)
|
||||||
|
#define BCM2708_DMA_D_WIDTH (1 << 5)
|
||||||
|
#define BCM2708_DMA_D_DREQ (1 << 6)
|
||||||
|
#define BCM2708_DMA_D_IGNORE (1 << 7)
|
||||||
|
#define BCM2708_DMA_S_INC (1 << 8)
|
||||||
|
#define BCM2708_DMA_S_WIDTH (1 << 9)
|
||||||
|
#define BCM2708_DMA_S_DREQ (1 << 10)
|
||||||
|
#define BCM2708_DMA_S_IGNORE (1 << 11)
|
||||||
|
|
||||||
|
/* Register offsets */
|
||||||
|
#define BCM2708_DMA_CS 0x00 /* Control and Status */
|
||||||
|
#define BCM2708_DMA_ADDR 0x04 /* Control block address */
|
||||||
|
/* the current control block appears in the following registers - read only */
|
||||||
|
#define BCM2708_DMA_INFO 0x08
|
||||||
|
#define BCM2708_DMA_SOURCE_AD 0x0c
|
||||||
|
#define BCM2708_DMA_DEST_AD 0x10
|
||||||
|
#define BCM2708_DMA_TXFR_LEN 0x14
|
||||||
|
#define BCM2708_DMA_STRIDE 0x18
|
||||||
|
#define BCM2708_DMA_NEXTCB 0x1C
|
||||||
|
#define BCM2708_DMA_DEBUG 0x20
|
||||||
|
|
||||||
|
#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */
|
||||||
|
#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */
|
||||||
|
|
||||||
|
#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */
|
||||||
|
|
||||||
|
static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c)
|
||||||
|
{
|
||||||
|
BCM2835DMAChan *ch = &s->chan[c];
|
||||||
|
uint32_t data, xlen, ylen;
|
||||||
|
int16_t dst_stride, src_stride;
|
||||||
|
|
||||||
|
if (!(s->enable & (1 << c))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) {
|
||||||
|
/* CB fetch */
|
||||||
|
ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad);
|
||||||
|
ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4);
|
||||||
|
ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8);
|
||||||
|
ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12);
|
||||||
|
ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16);
|
||||||
|
ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20);
|
||||||
|
|
||||||
|
if (ch->ti & BCM2708_DMA_TDMODE) {
|
||||||
|
/* 2D transfer mode */
|
||||||
|
ylen = (ch->txfr_len >> 16) & 0x3fff;
|
||||||
|
xlen = ch->txfr_len & 0xffff;
|
||||||
|
dst_stride = ch->stride >> 16;
|
||||||
|
src_stride = ch->stride & 0xffff;
|
||||||
|
} else {
|
||||||
|
ylen = 1;
|
||||||
|
xlen = ch->txfr_len;
|
||||||
|
dst_stride = 0;
|
||||||
|
src_stride = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ylen != 0) {
|
||||||
|
/* Normal transfer mode */
|
||||||
|
while (xlen != 0) {
|
||||||
|
if (ch->ti & BCM2708_DMA_S_IGNORE) {
|
||||||
|
/* Ignore reads */
|
||||||
|
data = 0;
|
||||||
|
} else {
|
||||||
|
data = ldl_le_phys(&s->dma_as, ch->source_ad);
|
||||||
|
}
|
||||||
|
if (ch->ti & BCM2708_DMA_S_INC) {
|
||||||
|
ch->source_ad += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch->ti & BCM2708_DMA_D_IGNORE) {
|
||||||
|
/* Ignore writes */
|
||||||
|
} else {
|
||||||
|
stl_le_phys(&s->dma_as, ch->dest_ad, data);
|
||||||
|
}
|
||||||
|
if (ch->ti & BCM2708_DMA_D_INC) {
|
||||||
|
ch->dest_ad += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update remaining transfer length */
|
||||||
|
xlen -= 4;
|
||||||
|
if (ch->ti & BCM2708_DMA_TDMODE) {
|
||||||
|
ch->txfr_len = (ylen << 16) | xlen;
|
||||||
|
} else {
|
||||||
|
ch->txfr_len = xlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--ylen != 0) {
|
||||||
|
ch->source_ad += src_stride;
|
||||||
|
ch->dest_ad += dst_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ch->cs |= BCM2708_DMA_END;
|
||||||
|
if (ch->ti & BCM2708_DMA_INT_EN) {
|
||||||
|
ch->cs |= BCM2708_DMA_INT;
|
||||||
|
s->int_status |= (1 << c);
|
||||||
|
qemu_set_irq(ch->irq, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process next CB */
|
||||||
|
ch->conblk_ad = ch->nextconbk;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch->cs &= ~BCM2708_DMA_ACTIVE;
|
||||||
|
ch->cs |= BCM2708_DMA_ISPAUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
|
||||||
|
{
|
||||||
|
ch->cs = 0;
|
||||||
|
ch->conblk_ad = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset,
|
||||||
|
unsigned size, unsigned c)
|
||||||
|
{
|
||||||
|
BCM2835DMAChan *ch;
|
||||||
|
uint32_t res = 0;
|
||||||
|
|
||||||
|
assert(size == 4);
|
||||||
|
assert(c < BCM2835_DMA_NCHANS);
|
||||||
|
|
||||||
|
ch = &s->chan[c];
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case BCM2708_DMA_CS:
|
||||||
|
res = ch->cs;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_ADDR:
|
||||||
|
res = ch->conblk_ad;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_INFO:
|
||||||
|
res = ch->ti;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_SOURCE_AD:
|
||||||
|
res = ch->source_ad;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_DEST_AD:
|
||||||
|
res = ch->dest_ad;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_TXFR_LEN:
|
||||||
|
res = ch->txfr_len;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_STRIDE:
|
||||||
|
res = ch->stride;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_NEXTCB:
|
||||||
|
res = ch->nextconbk;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_DEBUG:
|
||||||
|
res = ch->debug;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset,
|
||||||
|
uint64_t value, unsigned size, unsigned c)
|
||||||
|
{
|
||||||
|
BCM2835DMAChan *ch;
|
||||||
|
uint32_t oldcs;
|
||||||
|
|
||||||
|
assert(size == 4);
|
||||||
|
assert(c < BCM2835_DMA_NCHANS);
|
||||||
|
|
||||||
|
ch = &s->chan[c];
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case BCM2708_DMA_CS:
|
||||||
|
oldcs = ch->cs;
|
||||||
|
if (value & BCM2708_DMA_RESET) {
|
||||||
|
bcm2835_dma_chan_reset(ch);
|
||||||
|
}
|
||||||
|
if (value & BCM2708_DMA_ABORT) {
|
||||||
|
/* abort is a no-op, since we always run to completion */
|
||||||
|
}
|
||||||
|
if (value & BCM2708_DMA_END) {
|
||||||
|
ch->cs &= ~BCM2708_DMA_END;
|
||||||
|
}
|
||||||
|
if (value & BCM2708_DMA_INT) {
|
||||||
|
ch->cs &= ~BCM2708_DMA_INT;
|
||||||
|
s->int_status &= ~(1 << c);
|
||||||
|
qemu_set_irq(ch->irq, 0);
|
||||||
|
}
|
||||||
|
ch->cs &= ~BCM2708_DMA_CS_RW_MASK;
|
||||||
|
ch->cs |= (value & BCM2708_DMA_CS_RW_MASK);
|
||||||
|
if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) {
|
||||||
|
bcm2835_dma_update(s, c);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_ADDR:
|
||||||
|
ch->conblk_ad = value;
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_DEBUG:
|
||||||
|
ch->debug = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835DMAState *s = opaque;
|
||||||
|
|
||||||
|
if (offset < 0xf00) {
|
||||||
|
return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf);
|
||||||
|
} else {
|
||||||
|
switch (offset) {
|
||||||
|
case BCM2708_DMA_INT_STATUS:
|
||||||
|
return s->int_status;
|
||||||
|
case BCM2708_DMA_ENABLE:
|
||||||
|
return s->enable;
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
return bcm2835_dma_read(opaque, (offset & 0xff), size, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835DMAState *s = opaque;
|
||||||
|
|
||||||
|
if (offset < 0xf00) {
|
||||||
|
bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf);
|
||||||
|
} else {
|
||||||
|
switch (offset) {
|
||||||
|
case BCM2708_DMA_INT_STATUS:
|
||||||
|
break;
|
||||||
|
case BCM2708_DMA_ENABLE:
|
||||||
|
s->enable = (value & 0xffff);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps bcm2835_dma0_ops = {
|
||||||
|
.read = bcm2835_dma0_read,
|
||||||
|
.write = bcm2835_dma0_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
.valid.min_access_size = 4,
|
||||||
|
.valid.max_access_size = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const MemoryRegionOps bcm2835_dma15_ops = {
|
||||||
|
.read = bcm2835_dma15_read,
|
||||||
|
.write = bcm2835_dma15_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
.valid.min_access_size = 4,
|
||||||
|
.valid.max_access_size = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_bcm2835_dma_chan = {
|
||||||
|
.name = TYPE_BCM2835_DMA "-chan",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(cs, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(conblk_ad, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(ti, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(source_ad, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(dest_ad, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(txfr_len, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(stride, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(nextconbk, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(debug, BCM2835DMAChan),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_bcm2835_dma = {
|
||||||
|
.name = TYPE_BCM2835_DMA,
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1,
|
||||||
|
vmstate_bcm2835_dma_chan, BCM2835DMAChan),
|
||||||
|
VMSTATE_UINT32(int_status, BCM2835DMAState),
|
||||||
|
VMSTATE_UINT32(enable, BCM2835DMAState),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_dma_init(Object *obj)
|
||||||
|
{
|
||||||
|
BCM2835DMAState *s = BCM2835_DMA(obj);
|
||||||
|
int n;
|
||||||
|
|
||||||
|
/* DMA channels 0-14 occupy a contiguous block of IO memory, along
|
||||||
|
* with the global enable and interrupt status bits. Channel 15
|
||||||
|
* has the same register map, but is mapped at a discontiguous
|
||||||
|
* address in a separate IO block.
|
||||||
|
*/
|
||||||
|
memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s,
|
||||||
|
TYPE_BCM2835_DMA, 0x1000);
|
||||||
|
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s,
|
||||||
|
TYPE_BCM2835_DMA "-chan15", 0x100);
|
||||||
|
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15);
|
||||||
|
|
||||||
|
for (n = 0; n < 16; n++) {
|
||||||
|
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_dma_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
BCM2835DMAState *s = BCM2835_DMA(dev);
|
||||||
|
int n;
|
||||||
|
|
||||||
|
s->enable = 0xffff;
|
||||||
|
s->int_status = 0;
|
||||||
|
for (n = 0; n < BCM2835_DMA_NCHANS; n++) {
|
||||||
|
bcm2835_dma_chan_reset(&s->chan[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_dma_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
BCM2835DMAState *s = BCM2835_DMA(dev);
|
||||||
|
Error *err = NULL;
|
||||||
|
Object *obj;
|
||||||
|
|
||||||
|
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
|
||||||
|
if (obj == NULL) {
|
||||||
|
error_setg(errp, "%s: required dma-mr link not found: %s",
|
||||||
|
__func__, error_get_pretty(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->dma_mr = MEMORY_REGION(obj);
|
||||||
|
address_space_init(&s->dma_as, s->dma_mr, NULL);
|
||||||
|
|
||||||
|
bcm2835_dma_reset(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_dma_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->realize = bcm2835_dma_realize;
|
||||||
|
dc->reset = bcm2835_dma_reset;
|
||||||
|
dc->vmsd = &vmstate_bcm2835_dma;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeInfo bcm2835_dma_info = {
|
||||||
|
.name = TYPE_BCM2835_DMA,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(BCM2835DMAState),
|
||||||
|
.class_init = bcm2835_dma_class_init,
|
||||||
|
.instance_init = bcm2835_dma_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_dma_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&bcm2835_dma_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(bcm2835_dma_register_types)
|
||||||
@@ -342,6 +342,10 @@ static void i8257_channel_run(I8257State *d, int ichan)
|
|||||||
r->now[COUNT], (r->base[COUNT] + 1) << ncont);
|
r->now[COUNT], (r->base[COUNT] + 1) << ncont);
|
||||||
r->now[COUNT] = n;
|
r->now[COUNT] = n;
|
||||||
ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
|
ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
|
||||||
|
if (n == (r->base[COUNT] + 1) << ncont) {
|
||||||
|
ldebug("transfer done\n");
|
||||||
|
d->status |= (1 << ichan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i8257_dma_run(void *opaque)
|
static void i8257_dma_run(void *opaque)
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ static void imx_i2c_class_init(ObjectClass *klass, void *data)
|
|||||||
dc->vmsd = &imx_i2c_vmstate;
|
dc->vmsd = &imx_i2c_vmstate;
|
||||||
dc->reset = imx_i2c_reset;
|
dc->reset = imx_i2c_reset;
|
||||||
dc->realize = imx_i2c_realize;
|
dc->realize = imx_i2c_realize;
|
||||||
|
dc->desc = "i.MX I2C Controller";
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo imx_i2c_type_info = {
|
static const TypeInfo imx_i2c_type_info = {
|
||||||
|
|||||||
@@ -37,8 +37,8 @@
|
|||||||
#include "hw/acpi/bios-linker-loader.h"
|
#include "hw/acpi/bios-linker-loader.h"
|
||||||
#include "hw/loader.h"
|
#include "hw/loader.h"
|
||||||
#include "hw/isa/isa.h"
|
#include "hw/isa/isa.h"
|
||||||
|
#include "hw/block/fdc.h"
|
||||||
#include "hw/acpi/memory_hotplug.h"
|
#include "hw/acpi/memory_hotplug.h"
|
||||||
#include "hw/mem/nvdimm.h"
|
|
||||||
#include "sysemu/tpm.h"
|
#include "sysemu/tpm.h"
|
||||||
#include "hw/acpi/tpm.h"
|
#include "hw/acpi/tpm.h"
|
||||||
#include "sysemu/tpm_backend.h"
|
#include "sysemu/tpm_backend.h"
|
||||||
@@ -76,10 +76,6 @@
|
|||||||
#define ACPI_BUILD_DPRINTF(fmt, ...)
|
#define ACPI_BUILD_DPRINTF(fmt, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct AcpiCpuInfo {
|
|
||||||
DECLARE_BITMAP(found_cpus, ACPI_CPU_HOTPLUG_ID_LIMIT);
|
|
||||||
} AcpiCpuInfo;
|
|
||||||
|
|
||||||
typedef struct AcpiMcfgInfo {
|
typedef struct AcpiMcfgInfo {
|
||||||
uint64_t mcfg_base;
|
uint64_t mcfg_base;
|
||||||
uint32_t mcfg_size;
|
uint32_t mcfg_size;
|
||||||
@@ -121,31 +117,6 @@ typedef struct AcpiBuildPciBusHotplugState {
|
|||||||
bool pcihp_bridge_en;
|
bool pcihp_bridge_en;
|
||||||
} AcpiBuildPciBusHotplugState;
|
} AcpiBuildPciBusHotplugState;
|
||||||
|
|
||||||
static
|
|
||||||
int acpi_add_cpu_info(Object *o, void *opaque)
|
|
||||||
{
|
|
||||||
AcpiCpuInfo *cpu = opaque;
|
|
||||||
uint64_t apic_id;
|
|
||||||
|
|
||||||
if (object_dynamic_cast(o, TYPE_CPU)) {
|
|
||||||
apic_id = object_property_get_int(o, "apic-id", NULL);
|
|
||||||
assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
|
|
||||||
|
|
||||||
set_bit(apic_id, cpu->found_cpus);
|
|
||||||
}
|
|
||||||
|
|
||||||
object_child_foreach(o, acpi_add_cpu_info, opaque);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void acpi_get_cpu_info(AcpiCpuInfo *cpu)
|
|
||||||
{
|
|
||||||
Object *root = object_get_root();
|
|
||||||
|
|
||||||
memset(cpu->found_cpus, 0, sizeof cpu->found_cpus);
|
|
||||||
object_child_foreach(root, acpi_add_cpu_info, cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void acpi_get_pm_info(AcpiPmInfo *pm)
|
static void acpi_get_pm_info(AcpiPmInfo *pm)
|
||||||
{
|
{
|
||||||
Object *piix = piix4_pm_find();
|
Object *piix = piix4_pm_find();
|
||||||
@@ -362,9 +333,10 @@ build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu)
|
build_madt(GArray *table_data, GArray *linker, PCMachineState *pcms)
|
||||||
{
|
{
|
||||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
MachineClass *mc = MACHINE_GET_CLASS(pcms);
|
||||||
|
CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(pcms));
|
||||||
int madt_start = table_data->len;
|
int madt_start = table_data->len;
|
||||||
|
|
||||||
AcpiMultipleApicTable *madt;
|
AcpiMultipleApicTable *madt;
|
||||||
@@ -377,18 +349,28 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu)
|
|||||||
madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS);
|
madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS);
|
||||||
madt->flags = cpu_to_le32(1);
|
madt->flags = cpu_to_le32(1);
|
||||||
|
|
||||||
for (i = 0; i < pcms->apic_id_limit; i++) {
|
for (i = 0; i < apic_ids->len; i++) {
|
||||||
AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic);
|
AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic);
|
||||||
|
int apic_id = apic_ids->cpus[i].arch_id;
|
||||||
|
|
||||||
apic->type = ACPI_APIC_PROCESSOR;
|
apic->type = ACPI_APIC_PROCESSOR;
|
||||||
apic->length = sizeof(*apic);
|
apic->length = sizeof(*apic);
|
||||||
apic->processor_id = i;
|
apic->processor_id = apic_id;
|
||||||
apic->local_apic_id = i;
|
apic->local_apic_id = apic_id;
|
||||||
if (test_bit(i, cpu->found_cpus)) {
|
if (apic_ids->cpus[i].cpu != NULL) {
|
||||||
apic->flags = cpu_to_le32(1);
|
apic->flags = cpu_to_le32(1);
|
||||||
} else {
|
} else {
|
||||||
|
/* ACPI spec says that LAPIC entry for non present
|
||||||
|
* CPU may be omitted from MADT or it must be marked
|
||||||
|
* as disabled. However omitting non present CPU from
|
||||||
|
* MADT breaks hotplug on linux. So possible CPUs
|
||||||
|
* should be put in MADT but kept disabled.
|
||||||
|
*/
|
||||||
apic->flags = cpu_to_le32(0);
|
apic->flags = cpu_to_le32(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
g_free(apic_ids);
|
||||||
|
|
||||||
io_apic = acpi_data_push(table_data, sizeof *io_apic);
|
io_apic = acpi_data_push(table_data, sizeof *io_apic);
|
||||||
io_apic->type = ACPI_APIC_IO;
|
io_apic->type = ACPI_APIC_IO;
|
||||||
io_apic->length = sizeof(*io_apic);
|
io_apic->length = sizeof(*io_apic);
|
||||||
@@ -960,21 +942,24 @@ static Aml *build_crs(PCIHostState *host,
|
|||||||
return crs;
|
return crs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void build_processor_devices(Aml *sb_scope, unsigned acpi_cpus,
|
static void build_processor_devices(Aml *sb_scope, MachineState *machine,
|
||||||
AcpiCpuInfo *cpu, AcpiPmInfo *pm)
|
AcpiPmInfo *pm)
|
||||||
{
|
{
|
||||||
int i;
|
int i, apic_idx;
|
||||||
Aml *dev;
|
Aml *dev;
|
||||||
Aml *crs;
|
Aml *crs;
|
||||||
Aml *pkg;
|
Aml *pkg;
|
||||||
Aml *field;
|
Aml *field;
|
||||||
Aml *ifctx;
|
Aml *ifctx;
|
||||||
Aml *method;
|
Aml *method;
|
||||||
|
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||||
|
CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
|
||||||
|
PCMachineState *pcms = PC_MACHINE(machine);
|
||||||
|
|
||||||
/* The current AML generator can cover the APIC ID range [0..255],
|
/* The current AML generator can cover the APIC ID range [0..255],
|
||||||
* inclusive, for VCPU hotplug. */
|
* inclusive, for VCPU hotplug. */
|
||||||
QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256);
|
QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256);
|
||||||
g_assert(acpi_cpus <= ACPI_CPU_HOTPLUG_ID_LIMIT);
|
g_assert(pcms->apic_id_limit <= ACPI_CPU_HOTPLUG_ID_LIMIT);
|
||||||
|
|
||||||
/* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
|
/* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
|
||||||
dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
|
dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
|
||||||
@@ -993,28 +978,33 @@ static void build_processor_devices(Aml *sb_scope, unsigned acpi_cpus,
|
|||||||
aml_append(sb_scope, dev);
|
aml_append(sb_scope, dev);
|
||||||
/* declare CPU hotplug MMIO region and PRS field to access it */
|
/* declare CPU hotplug MMIO region and PRS field to access it */
|
||||||
aml_append(sb_scope, aml_operation_region(
|
aml_append(sb_scope, aml_operation_region(
|
||||||
"PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
|
"PRST", AML_SYSTEM_IO, aml_int(pm->cpu_hp_io_base), pm->cpu_hp_io_len));
|
||||||
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
aml_append(field, aml_named_field("PRS", 256));
|
aml_append(field, aml_named_field("PRS", 256));
|
||||||
aml_append(sb_scope, field);
|
aml_append(sb_scope, field);
|
||||||
|
|
||||||
/* build Processor object for each processor */
|
/* build Processor object for each processor */
|
||||||
for (i = 0; i < acpi_cpus; i++) {
|
for (i = 0; i < apic_ids->len; i++) {
|
||||||
dev = aml_processor(i, 0, 0, "CP%.02X", i);
|
int apic_id = apic_ids->cpus[i].arch_id;
|
||||||
|
|
||||||
|
assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT);
|
||||||
|
|
||||||
|
dev = aml_processor(apic_id, 0, 0, "CP%.02X", apic_id);
|
||||||
|
|
||||||
method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
|
method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
|
||||||
aml_append(method,
|
aml_append(method,
|
||||||
aml_return(aml_call1(CPU_MAT_METHOD, aml_int(i))));
|
aml_return(aml_call1(CPU_MAT_METHOD, aml_int(apic_id))));
|
||||||
aml_append(dev, method);
|
aml_append(dev, method);
|
||||||
|
|
||||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||||
aml_append(method,
|
aml_append(method,
|
||||||
aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(i))));
|
aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id))));
|
||||||
aml_append(dev, method);
|
aml_append(dev, method);
|
||||||
|
|
||||||
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
|
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
|
||||||
aml_append(method,
|
aml_append(method,
|
||||||
aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(i), aml_arg(0)))
|
aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id),
|
||||||
|
aml_arg(0)))
|
||||||
);
|
);
|
||||||
aml_append(dev, method);
|
aml_append(dev, method);
|
||||||
|
|
||||||
@@ -1026,10 +1016,12 @@ static void build_processor_devices(Aml *sb_scope, unsigned acpi_cpus,
|
|||||||
*/
|
*/
|
||||||
/* Arg0 = Processor ID = APIC ID */
|
/* Arg0 = Processor ID = APIC ID */
|
||||||
method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED);
|
method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED);
|
||||||
for (i = 0; i < acpi_cpus; i++) {
|
for (i = 0; i < apic_ids->len; i++) {
|
||||||
ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
|
int apic_id = apic_ids->cpus[i].arch_id;
|
||||||
|
|
||||||
|
ifctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id)));
|
||||||
aml_append(ifctx,
|
aml_append(ifctx,
|
||||||
aml_notify(aml_name("CP%.02X", i), aml_arg(1))
|
aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1))
|
||||||
);
|
);
|
||||||
aml_append(method, ifctx);
|
aml_append(method, ifctx);
|
||||||
}
|
}
|
||||||
@@ -1042,14 +1034,20 @@ static void build_processor_devices(Aml *sb_scope, unsigned acpi_cpus,
|
|||||||
* ith up to 255 elements. Windows guests up to win2k8 fail when
|
* ith up to 255 elements. Windows guests up to win2k8 fail when
|
||||||
* VarPackageOp is used.
|
* VarPackageOp is used.
|
||||||
*/
|
*/
|
||||||
pkg = acpi_cpus <= 255 ? aml_package(acpi_cpus) :
|
pkg = pcms->apic_id_limit <= 255 ? aml_package(pcms->apic_id_limit) :
|
||||||
aml_varpackage(acpi_cpus);
|
aml_varpackage(pcms->apic_id_limit);
|
||||||
|
|
||||||
for (i = 0; i < acpi_cpus; i++) {
|
for (i = 0, apic_idx = 0; i < apic_ids->len; i++) {
|
||||||
uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00;
|
int apic_id = apic_ids->cpus[i].arch_id;
|
||||||
aml_append(pkg, aml_int(b));
|
|
||||||
|
for (; apic_idx < apic_id; apic_idx++) {
|
||||||
|
aml_append(pkg, aml_int(0));
|
||||||
|
}
|
||||||
|
aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0));
|
||||||
|
apic_idx = apic_id + 1;
|
||||||
}
|
}
|
||||||
aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg));
|
aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg));
|
||||||
|
g_free(apic_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void build_memory_devices(Aml *sb_scope, int nr_mem,
|
static void build_memory_devices(Aml *sb_scope, int nr_mem,
|
||||||
@@ -1078,7 +1076,7 @@ static void build_memory_devices(Aml *sb_scope, int nr_mem,
|
|||||||
|
|
||||||
aml_append(scope, aml_operation_region(
|
aml_append(scope, aml_operation_region(
|
||||||
MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO,
|
MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO,
|
||||||
io_base, io_len)
|
aml_int(io_base), io_len)
|
||||||
);
|
);
|
||||||
|
|
||||||
field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC,
|
field = aml_field(MEMORY_HOTPLUG_IO_REGION, AML_DWORD_ACC,
|
||||||
@@ -1192,7 +1190,8 @@ static void build_hpet_aml(Aml *table)
|
|||||||
aml_append(dev, aml_name_decl("_UID", zero));
|
aml_append(dev, aml_name_decl("_UID", zero));
|
||||||
|
|
||||||
aml_append(dev,
|
aml_append(dev,
|
||||||
aml_operation_region("HPTM", AML_SYSTEM_MEMORY, HPET_BASE, HPET_LEN));
|
aml_operation_region("HPTM", AML_SYSTEM_MEMORY, aml_int(HPET_BASE),
|
||||||
|
HPET_LEN));
|
||||||
field = aml_field("HPTM", AML_DWORD_ACC, AML_LOCK, AML_PRESERVE);
|
field = aml_field("HPTM", AML_DWORD_ACC, AML_LOCK, AML_PRESERVE);
|
||||||
aml_append(field, aml_named_field("VEND", 32));
|
aml_append(field, aml_named_field("VEND", 32));
|
||||||
aml_append(field, aml_named_field("PRD", 32));
|
aml_append(field, aml_named_field("PRD", 32));
|
||||||
@@ -1227,33 +1226,63 @@ static void build_hpet_aml(Aml *table)
|
|||||||
aml_append(table, scope);
|
aml_append(table, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Aml *build_fdc_device_aml(void)
|
static Aml *build_fdinfo_aml(int idx, FloppyDriveType type)
|
||||||
{
|
{
|
||||||
|
Aml *dev, *fdi;
|
||||||
|
uint8_t maxc, maxh, maxs;
|
||||||
|
|
||||||
|
isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs);
|
||||||
|
|
||||||
|
dev = aml_device("FLP%c", 'A' + idx);
|
||||||
|
|
||||||
|
aml_append(dev, aml_name_decl("_ADR", aml_int(idx)));
|
||||||
|
|
||||||
|
fdi = aml_package(16);
|
||||||
|
aml_append(fdi, aml_int(idx)); /* Drive Number */
|
||||||
|
aml_append(fdi,
|
||||||
|
aml_int(cmos_get_fd_drive_type(type))); /* Device Type */
|
||||||
|
/*
|
||||||
|
* the values below are the limits of the drive, and are thus independent
|
||||||
|
* of the inserted media
|
||||||
|
*/
|
||||||
|
aml_append(fdi, aml_int(maxc)); /* Maximum Cylinder Number */
|
||||||
|
aml_append(fdi, aml_int(maxs)); /* Maximum Sector Number */
|
||||||
|
aml_append(fdi, aml_int(maxh)); /* Maximum Head Number */
|
||||||
|
/*
|
||||||
|
* SeaBIOS returns the below values for int 0x13 func 0x08 regardless of
|
||||||
|
* the drive type, so shall we
|
||||||
|
*/
|
||||||
|
aml_append(fdi, aml_int(0xAF)); /* disk_specify_1 */
|
||||||
|
aml_append(fdi, aml_int(0x02)); /* disk_specify_2 */
|
||||||
|
aml_append(fdi, aml_int(0x25)); /* disk_motor_wait */
|
||||||
|
aml_append(fdi, aml_int(0x02)); /* disk_sector_siz */
|
||||||
|
aml_append(fdi, aml_int(0x12)); /* disk_eot */
|
||||||
|
aml_append(fdi, aml_int(0x1B)); /* disk_rw_gap */
|
||||||
|
aml_append(fdi, aml_int(0xFF)); /* disk_dtl */
|
||||||
|
aml_append(fdi, aml_int(0x6C)); /* disk_formt_gap */
|
||||||
|
aml_append(fdi, aml_int(0xF6)); /* disk_fill */
|
||||||
|
aml_append(fdi, aml_int(0x0F)); /* disk_head_sttl */
|
||||||
|
aml_append(fdi, aml_int(0x08)); /* disk_motor_strt */
|
||||||
|
|
||||||
|
aml_append(dev, aml_name_decl("_FDI", fdi));
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Aml *build_fdc_device_aml(ISADevice *fdc)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
Aml *dev;
|
Aml *dev;
|
||||||
Aml *crs;
|
Aml *crs;
|
||||||
Aml *method;
|
|
||||||
Aml *if_ctx;
|
#define ACPI_FDE_MAX_FD 4
|
||||||
Aml *else_ctx;
|
uint32_t fde_buf[5] = {
|
||||||
Aml *zero = aml_int(0);
|
0, 0, 0, 0, /* presence of floppy drives #0 - #3 */
|
||||||
Aml *is_present = aml_local(0);
|
cpu_to_le32(2) /* tape presence (2 == never present) */
|
||||||
|
};
|
||||||
|
|
||||||
dev = aml_device("FDC0");
|
dev = aml_device("FDC0");
|
||||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700")));
|
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700")));
|
||||||
|
|
||||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
|
||||||
aml_append(method, aml_store(aml_name("FDEN"), is_present));
|
|
||||||
if_ctx = aml_if(aml_equal(is_present, zero));
|
|
||||||
{
|
|
||||||
aml_append(if_ctx, aml_return(aml_int(0x00)));
|
|
||||||
}
|
|
||||||
aml_append(method, if_ctx);
|
|
||||||
else_ctx = aml_else();
|
|
||||||
{
|
|
||||||
aml_append(else_ctx, aml_return(aml_int(0x0f)));
|
|
||||||
}
|
|
||||||
aml_append(method, else_ctx);
|
|
||||||
aml_append(dev, method);
|
|
||||||
|
|
||||||
crs = aml_resource_template();
|
crs = aml_resource_template();
|
||||||
aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04));
|
aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04));
|
||||||
aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01));
|
aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01));
|
||||||
@@ -1262,6 +1291,17 @@ static Aml *build_fdc_device_aml(void)
|
|||||||
aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2));
|
aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2));
|
||||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||||
|
|
||||||
|
for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) {
|
||||||
|
FloppyDriveType type = isa_fdc_get_drive_type(fdc, i);
|
||||||
|
|
||||||
|
if (type < FLOPPY_DRIVE_TYPE_NONE) {
|
||||||
|
fde_buf[i] = cpu_to_le32(1); /* drive present */
|
||||||
|
aml_append(dev, build_fdinfo_aml(i, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aml_append(dev, aml_name_decl("_FDE",
|
||||||
|
aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf)));
|
||||||
|
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1406,12 +1446,16 @@ static Aml *build_com_device_aml(uint8_t uid)
|
|||||||
|
|
||||||
static void build_isa_devices_aml(Aml *table)
|
static void build_isa_devices_aml(Aml *table)
|
||||||
{
|
{
|
||||||
|
ISADevice *fdc = pc_find_fdc0();
|
||||||
|
|
||||||
Aml *scope = aml_scope("_SB.PCI0.ISA");
|
Aml *scope = aml_scope("_SB.PCI0.ISA");
|
||||||
|
|
||||||
aml_append(scope, build_rtc_device_aml());
|
aml_append(scope, build_rtc_device_aml());
|
||||||
aml_append(scope, build_kbd_device_aml());
|
aml_append(scope, build_kbd_device_aml());
|
||||||
aml_append(scope, build_mouse_device_aml());
|
aml_append(scope, build_mouse_device_aml());
|
||||||
aml_append(scope, build_fdc_device_aml());
|
if (fdc) {
|
||||||
|
aml_append(scope, build_fdc_device_aml(fdc));
|
||||||
|
}
|
||||||
aml_append(scope, build_lpt_device_aml());
|
aml_append(scope, build_lpt_device_aml());
|
||||||
aml_append(scope, build_com_device_aml(1));
|
aml_append(scope, build_com_device_aml(1));
|
||||||
aml_append(scope, build_com_device_aml(2));
|
aml_append(scope, build_com_device_aml(2));
|
||||||
@@ -1430,7 +1474,7 @@ static void build_dbg_aml(Aml *table)
|
|||||||
Aml *idx = aml_local(2);
|
Aml *idx = aml_local(2);
|
||||||
|
|
||||||
aml_append(scope,
|
aml_append(scope,
|
||||||
aml_operation_region("DBG", AML_SYSTEM_IO, 0x0402, 0x01));
|
aml_operation_region("DBG", AML_SYSTEM_IO, aml_int(0x0402), 0x01));
|
||||||
field = aml_field("DBG", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
field = aml_field("DBG", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
aml_append(field, aml_named_field("DBGB", 8));
|
aml_append(field, aml_named_field("DBGB", 8));
|
||||||
aml_append(scope, field);
|
aml_append(scope, field);
|
||||||
@@ -1509,6 +1553,12 @@ static Aml *build_gsi_link_dev(const char *name, uint8_t uid, uint8_t gsi)
|
|||||||
|
|
||||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _DIS can be no-op because the interrupt cannot be disabled.
|
||||||
|
*/
|
||||||
|
method = aml_method("_DIS", 0, AML_NOTSERIALIZED);
|
||||||
|
aml_append(dev, method);
|
||||||
|
|
||||||
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
||||||
aml_append(dev, method);
|
aml_append(dev, method);
|
||||||
|
|
||||||
@@ -1742,18 +1792,14 @@ static void build_q35_pci0_int(Aml *table)
|
|||||||
aml_append(sb_scope, build_link_dev("LNKG", 6, aml_name("PRQG")));
|
aml_append(sb_scope, build_link_dev("LNKG", 6, aml_name("PRQG")));
|
||||||
aml_append(sb_scope, build_link_dev("LNKH", 7, aml_name("PRQH")));
|
aml_append(sb_scope, build_link_dev("LNKH", 7, aml_name("PRQH")));
|
||||||
|
|
||||||
/*
|
aml_append(sb_scope, build_gsi_link_dev("GSIA", 0x10, 0x10));
|
||||||
* TODO: UID probably shouldn't be the same for GSIx devices
|
aml_append(sb_scope, build_gsi_link_dev("GSIB", 0x11, 0x11));
|
||||||
* but that's how it was in original ASL so keep it for now
|
aml_append(sb_scope, build_gsi_link_dev("GSIC", 0x12, 0x12));
|
||||||
*/
|
aml_append(sb_scope, build_gsi_link_dev("GSID", 0x13, 0x13));
|
||||||
aml_append(sb_scope, build_gsi_link_dev("GSIA", 0, 0x10));
|
aml_append(sb_scope, build_gsi_link_dev("GSIE", 0x14, 0x14));
|
||||||
aml_append(sb_scope, build_gsi_link_dev("GSIB", 0, 0x11));
|
aml_append(sb_scope, build_gsi_link_dev("GSIF", 0x15, 0x15));
|
||||||
aml_append(sb_scope, build_gsi_link_dev("GSIC", 0, 0x12));
|
aml_append(sb_scope, build_gsi_link_dev("GSIG", 0x16, 0x16));
|
||||||
aml_append(sb_scope, build_gsi_link_dev("GSID", 0, 0x13));
|
aml_append(sb_scope, build_gsi_link_dev("GSIH", 0x17, 0x17));
|
||||||
aml_append(sb_scope, build_gsi_link_dev("GSIE", 0, 0x14));
|
|
||||||
aml_append(sb_scope, build_gsi_link_dev("GSIF", 0, 0x15));
|
|
||||||
aml_append(sb_scope, build_gsi_link_dev("GSIG", 0, 0x16));
|
|
||||||
aml_append(sb_scope, build_gsi_link_dev("GSIH", 0, 0x17));
|
|
||||||
|
|
||||||
aml_append(table, sb_scope);
|
aml_append(table, sb_scope);
|
||||||
}
|
}
|
||||||
@@ -1770,28 +1816,25 @@ static void build_q35_isa_bridge(Aml *table)
|
|||||||
|
|
||||||
/* ICH9 PCI to ISA irq remapping */
|
/* ICH9 PCI to ISA irq remapping */
|
||||||
aml_append(dev, aml_operation_region("PIRQ", AML_PCI_CONFIG,
|
aml_append(dev, aml_operation_region("PIRQ", AML_PCI_CONFIG,
|
||||||
0x60, 0x0C));
|
aml_int(0x60), 0x0C));
|
||||||
|
|
||||||
aml_append(dev, aml_operation_region("LPCD", AML_PCI_CONFIG,
|
aml_append(dev, aml_operation_region("LPCD", AML_PCI_CONFIG,
|
||||||
0x80, 0x02));
|
aml_int(0x80), 0x02));
|
||||||
field = aml_field("LPCD", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
field = aml_field("LPCD", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
aml_append(field, aml_named_field("COMA", 3));
|
aml_append(field, aml_named_field("COMA", 3));
|
||||||
aml_append(field, aml_reserved_field(1));
|
aml_append(field, aml_reserved_field(1));
|
||||||
aml_append(field, aml_named_field("COMB", 3));
|
aml_append(field, aml_named_field("COMB", 3));
|
||||||
aml_append(field, aml_reserved_field(1));
|
aml_append(field, aml_reserved_field(1));
|
||||||
aml_append(field, aml_named_field("LPTD", 2));
|
aml_append(field, aml_named_field("LPTD", 2));
|
||||||
aml_append(field, aml_reserved_field(2));
|
|
||||||
aml_append(field, aml_named_field("FDCD", 2));
|
|
||||||
aml_append(dev, field);
|
aml_append(dev, field);
|
||||||
|
|
||||||
aml_append(dev, aml_operation_region("LPCE", AML_PCI_CONFIG,
|
aml_append(dev, aml_operation_region("LPCE", AML_PCI_CONFIG,
|
||||||
0x82, 0x02));
|
aml_int(0x82), 0x02));
|
||||||
/* enable bits */
|
/* enable bits */
|
||||||
field = aml_field("LPCE", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
field = aml_field("LPCE", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
aml_append(field, aml_named_field("CAEN", 1));
|
aml_append(field, aml_named_field("CAEN", 1));
|
||||||
aml_append(field, aml_named_field("CBEN", 1));
|
aml_append(field, aml_named_field("CBEN", 1));
|
||||||
aml_append(field, aml_named_field("LPEN", 1));
|
aml_append(field, aml_named_field("LPEN", 1));
|
||||||
aml_append(field, aml_named_field("FDEN", 1));
|
|
||||||
aml_append(dev, field);
|
aml_append(dev, field);
|
||||||
|
|
||||||
aml_append(scope, dev);
|
aml_append(scope, dev);
|
||||||
@@ -1808,7 +1851,7 @@ static void build_piix4_pm(Aml *table)
|
|||||||
aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010003)));
|
aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010003)));
|
||||||
|
|
||||||
aml_append(dev, aml_operation_region("P13C", AML_PCI_CONFIG,
|
aml_append(dev, aml_operation_region("P13C", AML_PCI_CONFIG,
|
||||||
0x00, 0xff));
|
aml_int(0x00), 0xff));
|
||||||
aml_append(scope, dev);
|
aml_append(scope, dev);
|
||||||
aml_append(table, scope);
|
aml_append(table, scope);
|
||||||
}
|
}
|
||||||
@@ -1825,7 +1868,7 @@ static void build_piix4_isa_bridge(Aml *table)
|
|||||||
|
|
||||||
/* PIIX PCI to ISA irq remapping */
|
/* PIIX PCI to ISA irq remapping */
|
||||||
aml_append(dev, aml_operation_region("P40C", AML_PCI_CONFIG,
|
aml_append(dev, aml_operation_region("P40C", AML_PCI_CONFIG,
|
||||||
0x60, 0x04));
|
aml_int(0x60), 0x04));
|
||||||
/* enable bits */
|
/* enable bits */
|
||||||
field = aml_field("^PX13.P13C", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
field = aml_field("^PX13.P13C", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
/* Offset(0x5f),, 7, */
|
/* Offset(0x5f),, 7, */
|
||||||
@@ -1839,7 +1882,6 @@ static void build_piix4_isa_bridge(Aml *table)
|
|||||||
aml_append(field, aml_reserved_field(3));
|
aml_append(field, aml_reserved_field(3));
|
||||||
aml_append(field, aml_named_field("CBEN", 1));
|
aml_append(field, aml_named_field("CBEN", 1));
|
||||||
aml_append(dev, field);
|
aml_append(dev, field);
|
||||||
aml_append(dev, aml_name_decl("FDEN", aml_int(1)));
|
|
||||||
|
|
||||||
aml_append(scope, dev);
|
aml_append(scope, dev);
|
||||||
aml_append(table, scope);
|
aml_append(table, scope);
|
||||||
@@ -1854,20 +1896,20 @@ static void build_piix4_pci_hotplug(Aml *table)
|
|||||||
scope = aml_scope("_SB.PCI0");
|
scope = aml_scope("_SB.PCI0");
|
||||||
|
|
||||||
aml_append(scope,
|
aml_append(scope,
|
||||||
aml_operation_region("PCST", AML_SYSTEM_IO, 0xae00, 0x08));
|
aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x08));
|
||||||
field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||||
aml_append(field, aml_named_field("PCIU", 32));
|
aml_append(field, aml_named_field("PCIU", 32));
|
||||||
aml_append(field, aml_named_field("PCID", 32));
|
aml_append(field, aml_named_field("PCID", 32));
|
||||||
aml_append(scope, field);
|
aml_append(scope, field);
|
||||||
|
|
||||||
aml_append(scope,
|
aml_append(scope,
|
||||||
aml_operation_region("SEJ", AML_SYSTEM_IO, 0xae08, 0x04));
|
aml_operation_region("SEJ", AML_SYSTEM_IO, aml_int(0xae08), 0x04));
|
||||||
field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||||
aml_append(field, aml_named_field("B0EJ", 32));
|
aml_append(field, aml_named_field("B0EJ", 32));
|
||||||
aml_append(scope, field);
|
aml_append(scope, field);
|
||||||
|
|
||||||
aml_append(scope,
|
aml_append(scope,
|
||||||
aml_operation_region("BNMR", AML_SYSTEM_IO, 0xae10, 0x04));
|
aml_operation_region("BNMR", AML_SYSTEM_IO, aml_int(0xae10), 0x04));
|
||||||
field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||||
aml_append(field, aml_named_field("BNUM", 32));
|
aml_append(field, aml_named_field("BNUM", 32));
|
||||||
aml_append(scope, field);
|
aml_append(scope, field);
|
||||||
@@ -1937,14 +1979,13 @@ static Aml *build_q35_osc_method(void)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
build_dsdt(GArray *table_data, GArray *linker,
|
build_dsdt(GArray *table_data, GArray *linker,
|
||||||
AcpiCpuInfo *cpu, AcpiPmInfo *pm, AcpiMiscInfo *misc,
|
AcpiPmInfo *pm, AcpiMiscInfo *misc,
|
||||||
PcPciInfo *pci)
|
PcPciInfo *pci, MachineState *machine)
|
||||||
{
|
{
|
||||||
CrsRangeEntry *entry;
|
CrsRangeEntry *entry;
|
||||||
Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs;
|
Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs;
|
||||||
GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
||||||
GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
||||||
MachineState *machine = MACHINE(qdev_get_machine());
|
|
||||||
PCMachineState *pcms = PC_MACHINE(machine);
|
PCMachineState *pcms = PC_MACHINE(machine);
|
||||||
uint32_t nr_mem = machine->ram_slots;
|
uint32_t nr_mem = machine->ram_slots;
|
||||||
int root_bus_limit = 0xFF;
|
int root_bus_limit = 0xFF;
|
||||||
@@ -1975,9 +2016,9 @@ build_dsdt(GArray *table_data, GArray *linker,
|
|||||||
} else {
|
} else {
|
||||||
sb_scope = aml_scope("_SB");
|
sb_scope = aml_scope("_SB");
|
||||||
aml_append(sb_scope,
|
aml_append(sb_scope,
|
||||||
aml_operation_region("PCST", AML_SYSTEM_IO, 0xae00, 0x0c));
|
aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x0c));
|
||||||
aml_append(sb_scope,
|
aml_append(sb_scope,
|
||||||
aml_operation_region("PCSB", AML_SYSTEM_IO, 0xae0c, 0x01));
|
aml_operation_region("PCSB", AML_SYSTEM_IO, aml_int(0xae0c), 0x01));
|
||||||
field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||||
aml_append(field, aml_named_field("PCIB", 8));
|
aml_append(field, aml_named_field("PCIB", 8));
|
||||||
aml_append(sb_scope, field);
|
aml_append(sb_scope, field);
|
||||||
@@ -2190,6 +2231,35 @@ build_dsdt(GArray *table_data, GArray *linker,
|
|||||||
aml_append(scope, aml_name_decl("_S5", pkg));
|
aml_append(scope, aml_name_decl("_S5", pkg));
|
||||||
aml_append(dsdt, scope);
|
aml_append(dsdt, scope);
|
||||||
|
|
||||||
|
/* create fw_cfg node, unconditionally */
|
||||||
|
{
|
||||||
|
/* when using port i/o, the 8-bit data register *always* overlaps
|
||||||
|
* with half of the 16-bit control register. Hence, the total size
|
||||||
|
* of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the
|
||||||
|
* DMA control register is located at FW_CFG_DMA_IO_BASE + 4 */
|
||||||
|
uint8_t io_size = object_property_get_bool(OBJECT(pcms->fw_cfg),
|
||||||
|
"dma_enabled", NULL) ?
|
||||||
|
ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) :
|
||||||
|
FW_CFG_CTL_SIZE;
|
||||||
|
|
||||||
|
scope = aml_scope("\\_SB.PCI0");
|
||||||
|
dev = aml_device("FWCF");
|
||||||
|
|
||||||
|
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
|
||||||
|
|
||||||
|
/* device present, functioning, decoding, not shown in UI */
|
||||||
|
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||||
|
|
||||||
|
crs = aml_resource_template();
|
||||||
|
aml_append(crs,
|
||||||
|
aml_io(AML_DECODE16, FW_CFG_IO_BASE, FW_CFG_IO_BASE, 0x01, io_size)
|
||||||
|
);
|
||||||
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||||
|
|
||||||
|
aml_append(scope, dev);
|
||||||
|
aml_append(dsdt, scope);
|
||||||
|
}
|
||||||
|
|
||||||
if (misc->applesmc_io_base) {
|
if (misc->applesmc_io_base) {
|
||||||
scope = aml_scope("\\_SB.PCI0.ISA");
|
scope = aml_scope("\\_SB.PCI0.ISA");
|
||||||
dev = aml_device("SMC");
|
dev = aml_device("SMC");
|
||||||
@@ -2223,7 +2293,7 @@ build_dsdt(GArray *table_data, GArray *linker,
|
|||||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||||
|
|
||||||
aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
|
aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
|
||||||
misc->pvpanic_port, 1));
|
aml_int(misc->pvpanic_port), 1));
|
||||||
field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||||
aml_append(field, aml_named_field("PEPT", 8));
|
aml_append(field, aml_named_field("PEPT", 8));
|
||||||
aml_append(dev, field);
|
aml_append(dev, field);
|
||||||
@@ -2246,7 +2316,7 @@ build_dsdt(GArray *table_data, GArray *linker,
|
|||||||
|
|
||||||
sb_scope = aml_scope("\\_SB");
|
sb_scope = aml_scope("\\_SB");
|
||||||
{
|
{
|
||||||
build_processor_devices(sb_scope, pcms->apic_id_limit, cpu, pm);
|
build_processor_devices(sb_scope, machine, pm);
|
||||||
|
|
||||||
build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base,
|
build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base,
|
||||||
pm->mem_hp_io_len);
|
pm->mem_hp_io_len);
|
||||||
@@ -2367,7 +2437,7 @@ acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
build_srat(GArray *table_data, GArray *linker)
|
build_srat(GArray *table_data, GArray *linker, MachineState *machine)
|
||||||
{
|
{
|
||||||
AcpiSystemResourceAffinityTable *srat;
|
AcpiSystemResourceAffinityTable *srat;
|
||||||
AcpiSratProcessorAffinity *core;
|
AcpiSratProcessorAffinity *core;
|
||||||
@@ -2377,7 +2447,9 @@ build_srat(GArray *table_data, GArray *linker)
|
|||||||
uint64_t curnode;
|
uint64_t curnode;
|
||||||
int srat_start, numa_start, slots;
|
int srat_start, numa_start, slots;
|
||||||
uint64_t mem_len, mem_base, next_base;
|
uint64_t mem_len, mem_base, next_base;
|
||||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||||
|
CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
|
||||||
|
PCMachineState *pcms = PC_MACHINE(machine);
|
||||||
ram_addr_t hotplugabble_address_space_size =
|
ram_addr_t hotplugabble_address_space_size =
|
||||||
object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE,
|
object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE,
|
||||||
NULL);
|
NULL);
|
||||||
@@ -2386,14 +2458,15 @@ build_srat(GArray *table_data, GArray *linker)
|
|||||||
|
|
||||||
srat = acpi_data_push(table_data, sizeof *srat);
|
srat = acpi_data_push(table_data, sizeof *srat);
|
||||||
srat->reserved1 = cpu_to_le32(1);
|
srat->reserved1 = cpu_to_le32(1);
|
||||||
core = (void *)(srat + 1);
|
|
||||||
|
|
||||||
for (i = 0; i < pcms->apic_id_limit; ++i) {
|
for (i = 0; i < apic_ids->len; i++) {
|
||||||
|
int apic_id = apic_ids->cpus[i].arch_id;
|
||||||
|
|
||||||
core = acpi_data_push(table_data, sizeof *core);
|
core = acpi_data_push(table_data, sizeof *core);
|
||||||
core->type = ACPI_SRAT_PROCESSOR;
|
core->type = ACPI_SRAT_PROCESSOR;
|
||||||
core->length = sizeof(*core);
|
core->length = sizeof(*core);
|
||||||
core->local_apic_id = i;
|
core->local_apic_id = apic_id;
|
||||||
curnode = pcms->node_cpu[i];
|
curnode = pcms->node_cpu[apic_id];
|
||||||
core->proximity_lo = curnode;
|
core->proximity_lo = curnode;
|
||||||
memset(core->proximity_hi, 0, 3);
|
memset(core->proximity_hi, 0, 3);
|
||||||
core->local_sapic_eid = 0;
|
core->local_sapic_eid = 0;
|
||||||
@@ -2458,6 +2531,7 @@ build_srat(GArray *table_data, GArray *linker)
|
|||||||
(void *)(table_data->data + srat_start),
|
(void *)(table_data->data + srat_start),
|
||||||
"SRAT",
|
"SRAT",
|
||||||
table_data->len - srat_start, 1, NULL, NULL);
|
table_data->len - srat_start, 1, NULL, NULL);
|
||||||
|
g_free(apic_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -2581,21 +2655,13 @@ static bool acpi_has_iommu(void)
|
|||||||
return intel_iommu && !ambiguous;
|
return intel_iommu && !ambiguous;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool acpi_has_nvdimm(void)
|
|
||||||
{
|
|
||||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
|
||||||
|
|
||||||
return pcms->nvdimm;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
static
|
||||||
void acpi_build(AcpiBuildTables *tables)
|
void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||||
{
|
{
|
||||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
PCMachineState *pcms = PC_MACHINE(machine);
|
||||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||||
GArray *table_offsets;
|
GArray *table_offsets;
|
||||||
unsigned facs, dsdt, rsdt, fadt;
|
unsigned facs, dsdt, rsdt, fadt;
|
||||||
AcpiCpuInfo cpu;
|
|
||||||
AcpiPmInfo pm;
|
AcpiPmInfo pm;
|
||||||
AcpiMiscInfo misc;
|
AcpiMiscInfo misc;
|
||||||
AcpiMcfgInfo mcfg;
|
AcpiMcfgInfo mcfg;
|
||||||
@@ -2605,7 +2671,6 @@ void acpi_build(AcpiBuildTables *tables)
|
|||||||
GArray *tables_blob = tables->table_data;
|
GArray *tables_blob = tables->table_data;
|
||||||
AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL };
|
AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL };
|
||||||
|
|
||||||
acpi_get_cpu_info(&cpu);
|
|
||||||
acpi_get_pm_info(&pm);
|
acpi_get_pm_info(&pm);
|
||||||
acpi_get_misc_info(&misc);
|
acpi_get_misc_info(&misc);
|
||||||
acpi_get_pci_info(&pci);
|
acpi_get_pci_info(&pci);
|
||||||
@@ -2629,7 +2694,7 @@ void acpi_build(AcpiBuildTables *tables)
|
|||||||
|
|
||||||
/* DSDT is pointed to by FADT */
|
/* DSDT is pointed to by FADT */
|
||||||
dsdt = tables_blob->len;
|
dsdt = tables_blob->len;
|
||||||
build_dsdt(tables_blob, tables->linker, &cpu, &pm, &misc, &pci);
|
build_dsdt(tables_blob, tables->linker, &pm, &misc, &pci, machine);
|
||||||
|
|
||||||
/* Count the size of the DSDT and SSDT, we will need it for legacy
|
/* Count the size of the DSDT and SSDT, we will need it for legacy
|
||||||
* sizing of ACPI tables.
|
* sizing of ACPI tables.
|
||||||
@@ -2644,7 +2709,7 @@ void acpi_build(AcpiBuildTables *tables)
|
|||||||
aml_len += tables_blob->len - fadt;
|
aml_len += tables_blob->len - fadt;
|
||||||
|
|
||||||
acpi_add_table(table_offsets, tables_blob);
|
acpi_add_table(table_offsets, tables_blob);
|
||||||
build_madt(tables_blob, tables->linker, &cpu);
|
build_madt(tables_blob, tables->linker, pcms);
|
||||||
|
|
||||||
if (misc.has_hpet) {
|
if (misc.has_hpet) {
|
||||||
acpi_add_table(table_offsets, tables_blob);
|
acpi_add_table(table_offsets, tables_blob);
|
||||||
@@ -2661,7 +2726,7 @@ void acpi_build(AcpiBuildTables *tables)
|
|||||||
}
|
}
|
||||||
if (pcms->numa_nodes) {
|
if (pcms->numa_nodes) {
|
||||||
acpi_add_table(table_offsets, tables_blob);
|
acpi_add_table(table_offsets, tables_blob);
|
||||||
build_srat(tables_blob, tables->linker);
|
build_srat(tables_blob, tables->linker, machine);
|
||||||
}
|
}
|
||||||
if (acpi_get_mcfg(&mcfg)) {
|
if (acpi_get_mcfg(&mcfg)) {
|
||||||
acpi_add_table(table_offsets, tables_blob);
|
acpi_add_table(table_offsets, tables_blob);
|
||||||
@@ -2671,8 +2736,7 @@ void acpi_build(AcpiBuildTables *tables)
|
|||||||
acpi_add_table(table_offsets, tables_blob);
|
acpi_add_table(table_offsets, tables_blob);
|
||||||
build_dmar_q35(tables_blob, tables->linker);
|
build_dmar_q35(tables_blob, tables->linker);
|
||||||
}
|
}
|
||||||
|
if (pcms->acpi_nvdimm_state.is_enabled) {
|
||||||
if (acpi_has_nvdimm()) {
|
|
||||||
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker);
|
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2766,7 +2830,7 @@ static void acpi_build_update(void *build_opaque)
|
|||||||
|
|
||||||
acpi_build_tables_init(&tables);
|
acpi_build_tables_init(&tables);
|
||||||
|
|
||||||
acpi_build(&tables);
|
acpi_build(&tables, MACHINE(qdev_get_machine()));
|
||||||
|
|
||||||
acpi_ram_update(build_state->table_mr, tables.table_data);
|
acpi_ram_update(build_state->table_mr, tables.table_data);
|
||||||
|
|
||||||
@@ -2831,7 +2895,7 @@ void acpi_setup(void)
|
|||||||
acpi_set_pci_info();
|
acpi_set_pci_info();
|
||||||
|
|
||||||
acpi_build_tables_init(&tables);
|
acpi_build_tables_init(&tables);
|
||||||
acpi_build(&tables);
|
acpi_build(&tables, MACHINE(pcms));
|
||||||
|
|
||||||
/* Now expose it all to Guest */
|
/* Now expose it all to Guest */
|
||||||
build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data,
|
build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data,
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ static void kvm_apic_realize(DeviceState *dev, Error **errp)
|
|||||||
APIC_SPACE_SIZE);
|
APIC_SPACE_SIZE);
|
||||||
|
|
||||||
if (kvm_has_gsi_routing()) {
|
if (kvm_has_gsi_routing()) {
|
||||||
msi_supported = true;
|
msi_nonbroken = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,8 @@ int load_multiboot(FWCfgState *fw_cfg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
||||||
&elf_low, &elf_high, 0, I386_ELF_MACHINE, 0);
|
&elf_low, &elf_high, 0, I386_ELF_MACHINE,
|
||||||
|
0, 0);
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
fprintf(stderr, "Error while loading elf kernel\n");
|
fprintf(stderr, "Error while loading elf kernel\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
96
hw/i386/pc.c
96
hw/i386/pc.c
@@ -78,7 +78,6 @@
|
|||||||
#define DPRINTF(fmt, ...)
|
#define DPRINTF(fmt, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BIOS_CFG_IOPORT 0x510
|
|
||||||
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
|
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
|
||||||
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
|
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
|
||||||
#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
|
#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
|
||||||
@@ -200,7 +199,7 @@ static void pic_irq_request(void *opaque, int irq, int level)
|
|||||||
|
|
||||||
#define REG_EQUIPMENT_BYTE 0x14
|
#define REG_EQUIPMENT_BYTE 0x14
|
||||||
|
|
||||||
static int cmos_get_fd_drive_type(FloppyDriveType fd0)
|
int cmos_get_fd_drive_type(FloppyDriveType fd0)
|
||||||
{
|
{
|
||||||
int val;
|
int val;
|
||||||
|
|
||||||
@@ -700,18 +699,6 @@ static uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculates the limit to CPU APIC ID values
|
|
||||||
*
|
|
||||||
* This function returns the limit for the APIC ID value, so that all
|
|
||||||
* CPU APIC IDs are < pc_apic_id_limit().
|
|
||||||
*
|
|
||||||
* This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init().
|
|
||||||
*/
|
|
||||||
static unsigned int pc_apic_id_limit(unsigned int max_cpus)
|
|
||||||
{
|
|
||||||
return x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pc_build_smbios(FWCfgState *fw_cfg)
|
static void pc_build_smbios(FWCfgState *fw_cfg)
|
||||||
{
|
{
|
||||||
uint8_t *smbios_tables, *smbios_anchor;
|
uint8_t *smbios_tables, *smbios_anchor;
|
||||||
@@ -749,14 +736,13 @@ static void pc_build_smbios(FWCfgState *fw_cfg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static FWCfgState *bochs_bios_init(AddressSpace *as)
|
static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms)
|
||||||
{
|
{
|
||||||
FWCfgState *fw_cfg;
|
FWCfgState *fw_cfg;
|
||||||
uint64_t *numa_fw_cfg;
|
uint64_t *numa_fw_cfg;
|
||||||
int i, j;
|
int i, j;
|
||||||
unsigned int apic_id_limit = pc_apic_id_limit(max_cpus);
|
|
||||||
|
|
||||||
fw_cfg = fw_cfg_init_io_dma(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 4, as);
|
fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4, as);
|
||||||
|
|
||||||
/* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
|
/* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
|
||||||
*
|
*
|
||||||
@@ -772,7 +758,7 @@ static FWCfgState *bochs_bios_init(AddressSpace *as)
|
|||||||
* [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is
|
* [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is
|
||||||
* the APIC ID, not the "CPU index"
|
* the APIC ID, not the "CPU index"
|
||||||
*/
|
*/
|
||||||
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit);
|
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)pcms->apic_id_limit);
|
||||||
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
||||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES,
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES,
|
||||||
acpi_tables, acpi_tables_len);
|
acpi_tables, acpi_tables_len);
|
||||||
@@ -790,11 +776,11 @@ static FWCfgState *bochs_bios_init(AddressSpace *as)
|
|||||||
* of nodes, one word for each VCPU->node and one word for each node to
|
* of nodes, one word for each VCPU->node and one word for each node to
|
||||||
* hold the amount of memory.
|
* hold the amount of memory.
|
||||||
*/
|
*/
|
||||||
numa_fw_cfg = g_new0(uint64_t, 1 + apic_id_limit + nb_numa_nodes);
|
numa_fw_cfg = g_new0(uint64_t, 1 + pcms->apic_id_limit + nb_numa_nodes);
|
||||||
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
|
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
|
||||||
for (i = 0; i < max_cpus; i++) {
|
for (i = 0; i < max_cpus; i++) {
|
||||||
unsigned int apic_id = x86_cpu_apic_id_from_index(i);
|
unsigned int apic_id = x86_cpu_apic_id_from_index(i);
|
||||||
assert(apic_id < apic_id_limit);
|
assert(apic_id < pcms->apic_id_limit);
|
||||||
for (j = 0; j < nb_numa_nodes; j++) {
|
for (j = 0; j < nb_numa_nodes; j++) {
|
||||||
if (test_bit(i, numa_info[j].node_cpu)) {
|
if (test_bit(i, numa_info[j].node_cpu)) {
|
||||||
numa_fw_cfg[apic_id + 1] = cpu_to_le64(j);
|
numa_fw_cfg[apic_id + 1] = cpu_to_le64(j);
|
||||||
@@ -803,10 +789,11 @@ static FWCfgState *bochs_bios_init(AddressSpace *as)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < nb_numa_nodes; i++) {
|
for (i = 0; i < nb_numa_nodes; i++) {
|
||||||
numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(numa_info[i].node_mem);
|
numa_fw_cfg[pcms->apic_id_limit + 1 + i] =
|
||||||
|
cpu_to_le64(numa_info[i].node_mem);
|
||||||
}
|
}
|
||||||
fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg,
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg,
|
||||||
(1 + apic_id_limit + nb_numa_nodes) *
|
(1 + pcms->apic_id_limit + nb_numa_nodes) *
|
||||||
sizeof(*numa_fw_cfg));
|
sizeof(*numa_fw_cfg));
|
||||||
|
|
||||||
return fw_cfg;
|
return fw_cfg;
|
||||||
@@ -1120,7 +1107,6 @@ void pc_cpus_init(PCMachineState *pcms)
|
|||||||
int i;
|
int i;
|
||||||
X86CPU *cpu = NULL;
|
X86CPU *cpu = NULL;
|
||||||
MachineState *machine = MACHINE(pcms);
|
MachineState *machine = MACHINE(pcms);
|
||||||
unsigned long apic_id_limit;
|
|
||||||
|
|
||||||
/* init CPUs */
|
/* init CPUs */
|
||||||
if (machine->cpu_model == NULL) {
|
if (machine->cpu_model == NULL) {
|
||||||
@@ -1131,18 +1117,32 @@ void pc_cpus_init(PCMachineState *pcms)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
apic_id_limit = pc_apic_id_limit(max_cpus);
|
/* Calculates the limit to CPU APIC ID values
|
||||||
if (apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
|
*
|
||||||
error_report("max_cpus is too large. APIC ID of last CPU is %lu",
|
* Limit for the APIC ID value, so that all
|
||||||
apic_id_limit - 1);
|
* CPU APIC IDs are < pcms->apic_id_limit.
|
||||||
|
*
|
||||||
|
* This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init().
|
||||||
|
*/
|
||||||
|
pcms->apic_id_limit = x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
|
||||||
|
if (pcms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
|
||||||
|
error_report("max_cpus is too large. APIC ID of last CPU is %u",
|
||||||
|
pcms->apic_id_limit - 1);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < smp_cpus; i++) {
|
pcms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
|
||||||
|
sizeof(CPUArchId) * max_cpus);
|
||||||
|
for (i = 0; i < max_cpus; i++) {
|
||||||
|
pcms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(i);
|
||||||
|
pcms->possible_cpus->len++;
|
||||||
|
if (i < smp_cpus) {
|
||||||
cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i),
|
cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i),
|
||||||
&error_fatal);
|
&error_fatal);
|
||||||
|
pcms->possible_cpus->cpus[i].cpu = CPU(cpu);
|
||||||
object_unref(OBJECT(cpu));
|
object_unref(OBJECT(cpu));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* tell smbios about cpuid version and features */
|
/* tell smbios about cpuid version and features */
|
||||||
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
|
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
|
||||||
@@ -1187,7 +1187,6 @@ void pc_guest_info_init(PCMachineState *pcms)
|
|||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
pcms->apic_id_limit = pc_apic_id_limit(max_cpus);
|
|
||||||
pcms->apic_xrupt_override = kvm_allows_irq0_override();
|
pcms->apic_xrupt_override = kvm_allows_irq0_override();
|
||||||
pcms->numa_nodes = nb_numa_nodes;
|
pcms->numa_nodes = nb_numa_nodes;
|
||||||
pcms->node_mem = g_malloc0(pcms->numa_nodes *
|
pcms->node_mem = g_malloc0(pcms->numa_nodes *
|
||||||
@@ -1258,7 +1257,7 @@ void xen_load_linux(PCMachineState *pcms)
|
|||||||
|
|
||||||
assert(MACHINE(pcms)->kernel_filename != NULL);
|
assert(MACHINE(pcms)->kernel_filename != NULL);
|
||||||
|
|
||||||
fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT);
|
fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE);
|
||||||
rom_set_fw(fw_cfg);
|
rom_set_fw(fw_cfg);
|
||||||
|
|
||||||
load_linux(pcms, fw_cfg);
|
load_linux(pcms, fw_cfg);
|
||||||
@@ -1372,7 +1371,7 @@ void pc_memory_init(PCMachineState *pcms,
|
|||||||
option_rom_mr,
|
option_rom_mr,
|
||||||
1);
|
1);
|
||||||
|
|
||||||
fw_cfg = bochs_bios_init(&address_space_memory);
|
fw_cfg = bochs_bios_init(&address_space_memory, pcms);
|
||||||
|
|
||||||
rom_set_fw(fw_cfg);
|
rom_set_fw(fw_cfg);
|
||||||
|
|
||||||
@@ -1665,9 +1664,19 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
|
|||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pc_apic_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
CPUArchId *apic_a = (CPUArchId *)a;
|
||||||
|
CPUArchId *apic_b = (CPUArchId *)b;
|
||||||
|
|
||||||
|
return apic_a->arch_id - apic_b->arch_id;
|
||||||
|
}
|
||||||
|
|
||||||
static void pc_cpu_plug(HotplugHandler *hotplug_dev,
|
static void pc_cpu_plug(HotplugHandler *hotplug_dev,
|
||||||
DeviceState *dev, Error **errp)
|
DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
|
CPUClass *cc = CPU_GET_CLASS(dev);
|
||||||
|
CPUArchId apic_id, *found_cpu;
|
||||||
HotplugHandlerClass *hhc;
|
HotplugHandlerClass *hhc;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
||||||
@@ -1690,6 +1699,13 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev,
|
|||||||
|
|
||||||
/* increment the number of CPUs */
|
/* increment the number of CPUs */
|
||||||
rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1);
|
rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1);
|
||||||
|
|
||||||
|
apic_id.arch_id = cc->get_arch_id(CPU(dev));
|
||||||
|
found_cpu = bsearch(&apic_id, pcms->possible_cpus->cpus,
|
||||||
|
pcms->possible_cpus->len, sizeof(*pcms->possible_cpus->cpus),
|
||||||
|
pc_apic_cmp);
|
||||||
|
assert(found_cpu);
|
||||||
|
found_cpu->cpu = CPU(dev);
|
||||||
out:
|
out:
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
@@ -1854,14 +1870,14 @@ static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
|
|||||||
{
|
{
|
||||||
PCMachineState *pcms = PC_MACHINE(obj);
|
PCMachineState *pcms = PC_MACHINE(obj);
|
||||||
|
|
||||||
return pcms->nvdimm;
|
return pcms->acpi_nvdimm_state.is_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
|
static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
|
||||||
{
|
{
|
||||||
PCMachineState *pcms = PC_MACHINE(obj);
|
PCMachineState *pcms = PC_MACHINE(obj);
|
||||||
|
|
||||||
pcms->nvdimm = value;
|
pcms->acpi_nvdimm_state.is_enabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pc_machine_initfn(Object *obj)
|
static void pc_machine_initfn(Object *obj)
|
||||||
@@ -1900,7 +1916,7 @@ static void pc_machine_initfn(Object *obj)
|
|||||||
&error_abort);
|
&error_abort);
|
||||||
|
|
||||||
/* nvdimm is disabled on default. */
|
/* nvdimm is disabled on default. */
|
||||||
pcms->nvdimm = false;
|
pcms->acpi_nvdimm_state.is_enabled = false;
|
||||||
object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
|
object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
|
||||||
pc_machine_set_nvdimm, &error_abort);
|
pc_machine_set_nvdimm, &error_abort);
|
||||||
}
|
}
|
||||||
@@ -1932,6 +1948,17 @@ static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index)
|
|||||||
return topo.pkg_id;
|
return topo.pkg_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *machine)
|
||||||
|
{
|
||||||
|
PCMachineState *pcms = PC_MACHINE(machine);
|
||||||
|
int len = sizeof(CPUArchIdList) +
|
||||||
|
sizeof(CPUArchId) * (pcms->possible_cpus->len);
|
||||||
|
CPUArchIdList *list = g_malloc(len);
|
||||||
|
|
||||||
|
memcpy(list, pcms->possible_cpus, len);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
static void pc_machine_class_init(ObjectClass *oc, void *data)
|
static void pc_machine_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
MachineClass *mc = MACHINE_CLASS(oc);
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
@@ -1954,6 +1981,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
|
|||||||
pcmc->save_tsc_khz = true;
|
pcmc->save_tsc_khz = true;
|
||||||
mc->get_hotplug_handler = pc_get_hotpug_handler;
|
mc->get_hotplug_handler = pc_get_hotpug_handler;
|
||||||
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
|
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
|
||||||
|
mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids;
|
||||||
mc->default_boot_order = "cad";
|
mc->default_boot_order = "cad";
|
||||||
mc->hot_add_cpu = pc_hot_add_cpu;
|
mc->hot_add_cpu = pc_hot_add_cpu;
|
||||||
mc->max_cpus = 255;
|
mc->max_cpus = 255;
|
||||||
|
|||||||
@@ -274,6 +274,11 @@ static void pc_init1(MachineState *machine,
|
|||||||
if (pcmc->pci_enabled) {
|
if (pcmc->pci_enabled) {
|
||||||
pc_pci_device_init(pci_bus);
|
pc_pci_device_init(pci_bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pcms->acpi_nvdimm_state.is_enabled) {
|
||||||
|
nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
|
||||||
|
pcms->fw_cfg, OBJECT(pcms));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Looking for a pc_compat_2_4() function? It doesn't exist.
|
/* Looking for a pc_compat_2_4() function? It doesn't exist.
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "hw/kvm/clock.h"
|
#include "hw/kvm/clock.h"
|
||||||
#include "hw/pci-host/q35.h"
|
#include "hw/pci-host/q35.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
|
#include "hw/i386/pc.h"
|
||||||
#include "hw/i386/ich9.h"
|
#include "hw/i386/ich9.h"
|
||||||
#include "hw/smbios/smbios.h"
|
#include "hw/smbios/smbios.h"
|
||||||
#include "hw/ide/pci.h"
|
#include "hw/ide/pci.h"
|
||||||
@@ -61,6 +62,7 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
PCIDevice *lpc;
|
PCIDevice *lpc;
|
||||||
BusState *idebus[MAX_SATA_PORTS];
|
BusState *idebus[MAX_SATA_PORTS];
|
||||||
ISADevice *rtc_state;
|
ISADevice *rtc_state;
|
||||||
|
MemoryRegion *system_io = get_system_io();
|
||||||
MemoryRegion *pci_memory;
|
MemoryRegion *pci_memory;
|
||||||
MemoryRegion *rom_memory;
|
MemoryRegion *rom_memory;
|
||||||
MemoryRegion *ram_memory;
|
MemoryRegion *ram_memory;
|
||||||
@@ -145,7 +147,7 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
|
|
||||||
/* irq lines */
|
/* irq lines */
|
||||||
gsi_state = g_malloc0(sizeof(*gsi_state));
|
gsi_state = g_malloc0(sizeof(*gsi_state));
|
||||||
if (kvm_irqchip_in_kernel()) {
|
if (kvm_ioapic_in_kernel()) {
|
||||||
kvm_pc_setup_irq_routing(pcmc->pci_enabled);
|
kvm_pc_setup_irq_routing(pcmc->pci_enabled);
|
||||||
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
|
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
|
||||||
GSI_NUM_PINS);
|
GSI_NUM_PINS);
|
||||||
@@ -160,7 +162,7 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
q35_host->mch.ram_memory = ram_memory;
|
q35_host->mch.ram_memory = ram_memory;
|
||||||
q35_host->mch.pci_address_space = pci_memory;
|
q35_host->mch.pci_address_space = pci_memory;
|
||||||
q35_host->mch.system_memory = get_system_memory();
|
q35_host->mch.system_memory = get_system_memory();
|
||||||
q35_host->mch.address_space_io = get_system_io();
|
q35_host->mch.address_space_io = system_io;
|
||||||
q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
|
q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
|
||||||
q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
|
q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
|
||||||
/* pci */
|
/* pci */
|
||||||
@@ -192,7 +194,7 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
/*end early*/
|
/*end early*/
|
||||||
isa_bus_irqs(isa_bus, gsi);
|
isa_bus_irqs(isa_bus, gsi);
|
||||||
|
|
||||||
if (kvm_irqchip_in_kernel()) {
|
if (kvm_pic_in_kernel()) {
|
||||||
i8259 = kvm_i8259_init(isa_bus);
|
i8259 = kvm_i8259_init(isa_bus);
|
||||||
} else if (xen_enabled()) {
|
} else if (xen_enabled()) {
|
||||||
i8259 = xen_interrupt_controller_init();
|
i8259 = xen_interrupt_controller_init();
|
||||||
@@ -251,6 +253,11 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
if (pcmc->pci_enabled) {
|
if (pcmc->pci_enabled) {
|
||||||
pc_pci_device_init(host_bus);
|
pc_pci_device_init(host_bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pcms->acpi_nvdimm_state.is_enabled) {
|
||||||
|
nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
|
||||||
|
pcms->fw_cfg, OBJECT(pcms));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \
|
#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ static void xen_apic_realize(DeviceState *dev, Error **errp)
|
|||||||
s->vapic_control = 0;
|
s->vapic_control = 0;
|
||||||
memory_region_init_io(&s->io_memory, OBJECT(s), &xen_apic_io_ops, s,
|
memory_region_init_io(&s->io_memory, OBJECT(s), &xen_apic_io_ops, s,
|
||||||
"xen-apic-msi", APIC_SPACE_SIZE);
|
"xen-apic-msi", APIC_SPACE_SIZE);
|
||||||
msi_supported = true;
|
msi_nonbroken = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xen_apic_set_base(APICCommonState *s, uint64_t val)
|
static void xen_apic_set_base(APICCommonState *s, uint64_t val)
|
||||||
|
|||||||
@@ -116,37 +116,42 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
|
|||||||
};
|
};
|
||||||
HIDState *hs = (HIDState *)dev;
|
HIDState *hs = (HIDState *)dev;
|
||||||
HIDPointerEvent *e;
|
HIDPointerEvent *e;
|
||||||
|
InputMoveEvent *move;
|
||||||
|
InputBtnEvent *btn;
|
||||||
|
|
||||||
assert(hs->n < QUEUE_LENGTH);
|
assert(hs->n < QUEUE_LENGTH);
|
||||||
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
|
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
|
||||||
|
|
||||||
switch (evt->type) {
|
switch (evt->type) {
|
||||||
case INPUT_EVENT_KIND_REL:
|
case INPUT_EVENT_KIND_REL:
|
||||||
if (evt->u.rel->axis == INPUT_AXIS_X) {
|
move = evt->u.rel;
|
||||||
e->xdx += evt->u.rel->value;
|
if (move->axis == INPUT_AXIS_X) {
|
||||||
} else if (evt->u.rel->axis == INPUT_AXIS_Y) {
|
e->xdx += move->value;
|
||||||
e->ydy += evt->u.rel->value;
|
} else if (move->axis == INPUT_AXIS_Y) {
|
||||||
|
e->ydy += move->value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INPUT_EVENT_KIND_ABS:
|
case INPUT_EVENT_KIND_ABS:
|
||||||
if (evt->u.rel->axis == INPUT_AXIS_X) {
|
move = evt->u.abs;
|
||||||
e->xdx = evt->u.rel->value;
|
if (move->axis == INPUT_AXIS_X) {
|
||||||
} else if (evt->u.rel->axis == INPUT_AXIS_Y) {
|
e->xdx = move->value;
|
||||||
e->ydy = evt->u.rel->value;
|
} else if (move->axis == INPUT_AXIS_Y) {
|
||||||
|
e->ydy = move->value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INPUT_EVENT_KIND_BTN:
|
case INPUT_EVENT_KIND_BTN:
|
||||||
if (evt->u.btn->down) {
|
btn = evt->u.btn;
|
||||||
e->buttons_state |= bmap[evt->u.btn->button];
|
if (btn->down) {
|
||||||
if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
|
e->buttons_state |= bmap[btn->button];
|
||||||
|
if (btn->button == INPUT_BUTTON_WHEEL_UP) {
|
||||||
e->dz--;
|
e->dz--;
|
||||||
} else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
|
} else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
|
||||||
e->dz++;
|
e->dz++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
e->buttons_state &= ~bmap[evt->u.btn->button];
|
e->buttons_state &= ~bmap[btn->button];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -223,9 +228,10 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
|
|||||||
HIDState *hs = (HIDState *)dev;
|
HIDState *hs = (HIDState *)dev;
|
||||||
int scancodes[3], i, count;
|
int scancodes[3], i, count;
|
||||||
int slot;
|
int slot;
|
||||||
|
InputKeyEvent *key = evt->u.key;
|
||||||
|
|
||||||
count = qemu_input_key_value_to_scancode(evt->u.key->key,
|
count = qemu_input_key_value_to_scancode(key->key,
|
||||||
evt->u.key->down,
|
key->down,
|
||||||
scancodes);
|
scancodes);
|
||||||
if (hs->n + count > QUEUE_LENGTH) {
|
if (hs->n + count > QUEUE_LENGTH) {
|
||||||
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user