Compare commits
658 Commits
pull-roms-
...
pull-input
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47c03744b3 | ||
|
|
6f90f3d786 | ||
|
|
5643706a09 | ||
|
|
5c07d00f1b | ||
|
|
70b52f62b8 | ||
|
|
e842c68d44 | ||
|
|
a8dfb1c34f | ||
|
|
4a33f45e2e | ||
|
|
4798648e32 | ||
|
|
2d0755d21c | ||
|
|
16b0ecd168 | ||
|
|
faecd955ce | ||
|
|
c43ce5512f | ||
|
|
21bae11a39 | ||
|
|
2e08c665cc | ||
|
|
c751a74afe | ||
|
|
f100db385d | ||
|
|
14768eba46 | ||
|
|
3ab193e662 | ||
|
|
192f81bfce | ||
|
|
edd85a3d9e | ||
|
|
502c8db5b4 | ||
|
|
d3535431e8 | ||
|
|
43579403a3 | ||
|
|
cd10032888 | ||
|
|
de8f580b23 | ||
|
|
8d447d10b7 | ||
|
|
a25f545d68 | ||
|
|
af98ba92ac | ||
|
|
d2a9260335 | ||
|
|
9784e57930 | ||
|
|
6567147588 | ||
|
|
c8b405b679 | ||
|
|
8b6b0c59a6 | ||
|
|
bbd1b1cc25 | ||
|
|
031fa96439 | ||
|
|
7ad95ff76c | ||
|
|
d4c8533755 | ||
|
|
f55ea6297c | ||
|
|
ac458e121c | ||
|
|
e00ef747f0 | ||
|
|
4a29420ea1 | ||
|
|
4fd42afe61 | ||
|
|
c2cb92f9ea | ||
|
|
739aa555b8 | ||
|
|
d47e95c0c8 | ||
|
|
57f45b6207 | ||
|
|
b774539743 | ||
|
|
25a7017555 | ||
|
|
949ceeb31b | ||
|
|
56bed4135f | ||
|
|
a105acbce3 | ||
|
|
678e48a2e4 | ||
|
|
2fc0043283 | ||
|
|
7ad993b480 | ||
|
|
aabbd472a0 | ||
|
|
2c38b60010 | ||
|
|
c2216a8a7a | ||
|
|
ab22ad96ce | ||
|
|
357765fed5 | ||
|
|
25254a7191 | ||
|
|
cae8a9289b | ||
|
|
67d065c3db | ||
|
|
777872e5c6 | ||
|
|
cc99c6f5ff | ||
|
|
aa830cdc28 | ||
|
|
7edd9ddc97 | ||
|
|
8ead601883 | ||
|
|
c5d3c49896 | ||
|
|
55e7c29e46 | ||
|
|
0064aceb29 | ||
|
|
de580dafad | ||
|
|
1c884abede | ||
|
|
de92f3f86a | ||
|
|
ffe9fe3a25 | ||
|
|
4864512389 | ||
|
|
3eba13ec25 | ||
|
|
9147d019f3 | ||
|
|
69bef7931e | ||
|
|
4b350f1de1 | ||
|
|
0c762736df | ||
|
|
a9e6a0cbe2 | ||
|
|
adccfbcd60 | ||
|
|
1b37b3442f | ||
|
|
9a05feabd5 | ||
|
|
7d6dc7f30c | ||
|
|
4ab23a9182 | ||
|
|
b53ccc30c4 | ||
|
|
d12f57ec66 | ||
|
|
64cfba6a47 | ||
|
|
d0686c7291 | ||
|
|
298f116827 | ||
|
|
7aad248d35 | ||
|
|
607dacd0a0 | ||
|
|
4835ef7784 | ||
|
|
5d31babe5c | ||
|
|
fda053875e | ||
|
|
6a519918b3 | ||
|
|
b5ba1cc626 | ||
|
|
cb48da7f81 | ||
|
|
4e47e39ab0 | ||
|
|
d2fe51bda8 | ||
|
|
fbb0621a0f | ||
|
|
cd159d0954 | ||
|
|
d844a7b656 | ||
|
|
9fbee91a13 | ||
|
|
73795cea96 | ||
|
|
af87bf290f | ||
|
|
9eb08a435a | ||
|
|
ead4cf04f8 | ||
|
|
f9681f116c | ||
|
|
7f00eb30fe | ||
|
|
1eecf41b3e | ||
|
|
04c2b5168e | ||
|
|
f7d3e46676 | ||
|
|
49f5c9e98a | ||
|
|
0ca3611221 | ||
|
|
5d739a4787 | ||
|
|
d1028f1b5b | ||
|
|
c804c2a717 | ||
|
|
6fbef18a4c | ||
|
|
477a72a1ef | ||
|
|
65e526c24e | ||
|
|
e8803d93df | ||
|
|
6e25280216 | ||
|
|
a0fa2cb8cc | ||
|
|
9da45bb217 | ||
|
|
77319f2263 | ||
|
|
f2c55d1735 | ||
|
|
0788082a4b | ||
|
|
819bd3091e | ||
|
|
3a553fc658 | ||
|
|
216db403d0 | ||
|
|
2ce5868ca1 | ||
|
|
6f6831f61a | ||
|
|
bc3fbad816 | ||
|
|
28c05edff5 | ||
|
|
4b9430294e | ||
|
|
52aa17cbd8 | ||
|
|
82d0794565 | ||
|
|
13665a2d2f | ||
|
|
c04018e933 | ||
|
|
a5ae7e3984 | ||
|
|
432a0a130e | ||
|
|
1c8be73d4e | ||
|
|
c3143ba877 | ||
|
|
024c6e2ea5 | ||
|
|
63a31905cb | ||
|
|
eb0ecd5ad9 | ||
|
|
0956ff5a4e | ||
|
|
1f79ee32b5 | ||
|
|
34222fb810 | ||
|
|
9cfa0b4e4c | ||
|
|
4cc35614a0 | ||
|
|
1ed69e82b8 | ||
|
|
d9ea7d290b | ||
|
|
cd5c11b84b | ||
|
|
0b45451e58 | ||
|
|
e60cef860f | ||
|
|
a7adc4b779 | ||
|
|
4b7fff2fab | ||
|
|
327ed10fa2 | ||
|
|
a505d7fe5f | ||
|
|
cb2e37dffa | ||
|
|
5ebafdf31a | ||
|
|
b0fe242751 | ||
|
|
91e240698f | ||
|
|
168aa23bb0 | ||
|
|
8af35c37d2 | ||
|
|
cd4da63177 | ||
|
|
0eef9d9833 | ||
|
|
7da845b0f4 | ||
|
|
67ed771ded | ||
|
|
855011be05 | ||
|
|
1da41cc1c6 | ||
|
|
0a6a7ccaae | ||
|
|
d6032e06d1 | ||
|
|
876074c228 | ||
|
|
6453fa998a | ||
|
|
c10f7fc3d1 | ||
|
|
cf143ad350 | ||
|
|
fce0a82608 | ||
|
|
775fda92a1 | ||
|
|
cba933b225 | ||
|
|
106a73b6d2 | ||
|
|
ec1efab957 | ||
|
|
d5001cf787 | ||
|
|
d91a68a73b | ||
|
|
b8d4e1c43b | ||
|
|
29873712e6 | ||
|
|
13c9bfbfbb | ||
|
|
8174196b7f | ||
|
|
81cce07ec6 | ||
|
|
05a738c4ec | ||
|
|
aa0d1f4488 | ||
|
|
a89d97df1f | ||
|
|
ad37bb3b00 | ||
|
|
e3e48565c1 | ||
|
|
d6085e3ace | ||
|
|
41310c6878 | ||
|
|
6d3cb1f970 | ||
|
|
24a370ef23 | ||
|
|
aded6539d9 | ||
|
|
0459650d94 | ||
|
|
05fd3bf2a1 | ||
|
|
0a985b3727 | ||
|
|
f6c65bfb93 | ||
|
|
3bac80d31a | ||
|
|
cf528b8958 | ||
|
|
2e753bcc7d | ||
|
|
1f55ac4586 | ||
|
|
e96dfd110e | ||
|
|
6e50d18847 | ||
|
|
e7a1d6c52a | ||
|
|
c58e291591 | ||
|
|
6dedf0522c | ||
|
|
98b21dcdb3 | ||
|
|
9c70434f82 | ||
|
|
cd6c88305f | ||
|
|
604e1f9cd0 | ||
|
|
676056d4f1 | ||
|
|
2c09eee112 | ||
|
|
a2e67072b7 | ||
|
|
d0fa1f0df3 | ||
|
|
e848dd4248 | ||
|
|
7c84259019 | ||
|
|
6502668237 | ||
|
|
b807b5ff89 | ||
|
|
e0db904d1d | ||
|
|
a749f42da5 | ||
|
|
10b7c5dd0d | ||
|
|
2c958923bc | ||
|
|
d9e1f574cb | ||
|
|
ff8adbcfdb | ||
|
|
4c1b8f1e83 | ||
|
|
d9738fd246 | ||
|
|
64cc22841e | ||
|
|
24d3bd67ac | ||
|
|
837c390137 | ||
|
|
7ef8cf9a08 | ||
|
|
703dd81aca | ||
|
|
c5f52875b9 | ||
|
|
6141f3bd69 | ||
|
|
8a87f3d722 | ||
|
|
c7fc5bc2a4 | ||
|
|
c88a1de51a | ||
|
|
98a7a38f81 | ||
|
|
1c508d174d | ||
|
|
a28e4c408b | ||
|
|
d55dee2044 | ||
|
|
95c6bff356 | ||
|
|
7db6982a19 | ||
|
|
f70d7f7e4d | ||
|
|
13e7956e31 | ||
|
|
cadebd7a2a | ||
|
|
27cec15e4e | ||
|
|
64757582da | ||
|
|
7841c76884 | ||
|
|
bae3f92a01 | ||
|
|
ae39c4b201 | ||
|
|
a33cc31d08 | ||
|
|
a283cb6e58 | ||
|
|
626f84f39d | ||
|
|
2dc8328b4c | ||
|
|
77386bf6eb | ||
|
|
7cc07ab8da | ||
|
|
5b7aa9b56d | ||
|
|
76abe4071d | ||
|
|
89ac8480a8 | ||
|
|
37f09e5e3d | ||
|
|
d1833ef52b | ||
|
|
a8842e6d2a | ||
|
|
c0f92b526d | ||
|
|
6890aad46b | ||
|
|
0fea6b7972 | ||
|
|
b6d5066d32 | ||
|
|
2a94fee3f6 | ||
|
|
f8d924e481 | ||
|
|
a7451cb850 | ||
|
|
24897a767b | ||
|
|
f2917853f7 | ||
|
|
35cb1748d5 | ||
|
|
537b41f501 | ||
|
|
c06b72781d | ||
|
|
77e8b9ca64 | ||
|
|
a69d9af449 | ||
|
|
f7d9fd8c72 | ||
|
|
5acd9d81e1 | ||
|
|
5469a2a688 | ||
|
|
d4446eae63 | ||
|
|
5d12aa63c7 | ||
|
|
2e40134bfd | ||
|
|
ddf5636dc9 | ||
|
|
f67503e5bd | ||
|
|
e6dc8a1f83 | ||
|
|
a71835a0cc | ||
|
|
105a060188 | ||
|
|
3e890c77cf | ||
|
|
7a87a7b3e4 | ||
|
|
e607784fed | ||
|
|
9bd9d5e357 | ||
|
|
774d566cdb | ||
|
|
521f438e36 | ||
|
|
58da5b1e01 | ||
|
|
15e8159e76 | ||
|
|
2ca92bb993 | ||
|
|
3d2bb5cc81 | ||
|
|
61e8a92364 | ||
|
|
f966f9ddd1 | ||
|
|
66518bf668 | ||
|
|
d3399d7cf8 | ||
|
|
cc47569881 | ||
|
|
e3be6f0ecc | ||
|
|
e26110cfc6 | ||
|
|
17969268f5 | ||
|
|
13b6ce0ec9 | ||
|
|
6ebc91e5d0 | ||
|
|
5c0d52bcd3 | ||
|
|
ba1183da9a | ||
|
|
10f5bff622 | ||
|
|
4c0c9bbe78 | ||
|
|
2ea5a2ca1f | ||
|
|
60510aed69 | ||
|
|
e4b998d47d | ||
|
|
dfc15c7ceb | ||
|
|
70d7f984a0 | ||
|
|
13caf1fd2b | ||
|
|
d324b36ad9 | ||
|
|
7900e9f1f9 | ||
|
|
59a1c327d7 | ||
|
|
ea4571eb87 | ||
|
|
c4241c7d38 | ||
|
|
92611c0019 | ||
|
|
00108f2d4d | ||
|
|
fcd252062a | ||
|
|
f59df3f235 | ||
|
|
e508a92b62 | ||
|
|
626187d86b | ||
|
|
1456364ff0 | ||
|
|
99f678a679 | ||
|
|
76e3e1bcae | ||
|
|
83e9a4aec9 | ||
|
|
057d5f62f8 | ||
|
|
67d43538ae | ||
|
|
bc242f9bb6 | ||
|
|
8908f4d185 | ||
|
|
b033cd3d00 | ||
|
|
9f82e0ff4b | ||
|
|
c44ad1fddc | ||
|
|
f5e51e7f10 | ||
|
|
873169022a | ||
|
|
6d093a4f49 | ||
|
|
b15d422a23 | ||
|
|
cef60c925c | ||
|
|
98f9e35bef | ||
|
|
69d4c703a5 | ||
|
|
fff8c539bd | ||
|
|
94783de6fe | ||
|
|
5d59fd998f | ||
|
|
ef3ef4a040 | ||
|
|
e6bf23f82d | ||
|
|
9530570fa5 | ||
|
|
bf15f63cad | ||
|
|
501bb4b0cb | ||
|
|
3a5d30bf27 | ||
|
|
6afafa86f3 | ||
|
|
4fc4732047 | ||
|
|
d6bb65fcd2 | ||
|
|
4f9cc73422 | ||
|
|
cc03ff9d0a | ||
|
|
786ad214c7 | ||
|
|
c348e48175 | ||
|
|
f6969b9fef | ||
|
|
bd93976a1a | ||
|
|
91f32b0c92 | ||
|
|
1094fd3a62 | ||
|
|
b48fa074b5 | ||
|
|
992d3e64c8 | ||
|
|
fca72d9b49 | ||
|
|
77d1c3c63f | ||
|
|
84d18f065f | ||
|
|
ff9ec34de8 | ||
|
|
dde3a21840 | ||
|
|
6399ab3325 | ||
|
|
9d2eec202f | ||
|
|
ecc7e84327 | ||
|
|
a1b29c9ae0 | ||
|
|
189f792dc5 | ||
|
|
464a1441c1 | ||
|
|
e64e958e20 | ||
|
|
e201b56418 | ||
|
|
23ec69ed37 | ||
|
|
c8d7027253 | ||
|
|
f096dc9618 | ||
|
|
3031244b01 | ||
|
|
e46b225a31 | ||
|
|
7a3a00979d | ||
|
|
46eef33b89 | ||
|
|
f6aa2f7dee | ||
|
|
0dbcf95a1e | ||
|
|
078a1c37ca | ||
|
|
44e3a39f30 | ||
|
|
90ce3d76eb | ||
|
|
a50f98b066 | ||
|
|
88c1ee73d3 | ||
|
|
5556332aba | ||
|
|
b33276a7a8 | ||
|
|
24c84e687e | ||
|
|
262471794d | ||
|
|
62dc90c668 | ||
|
|
21684af023 | ||
|
|
3f281822b2 | ||
|
|
c1570e2a1f | ||
|
|
a63e5e0c0d | ||
|
|
e41b509d68 | ||
|
|
85ca1202d1 | ||
|
|
104059da54 | ||
|
|
f31c41ff5e | ||
|
|
c7bcc85d66 | ||
|
|
515f23462b | ||
|
|
dae3bda422 | ||
|
|
0b7593e085 | ||
|
|
98a6528461 | ||
|
|
7ce7ffe027 | ||
|
|
03ff777048 | ||
|
|
9e4d9620c4 | ||
|
|
7d9268647c | ||
|
|
a5829ccfc3 | ||
|
|
23802b4fe0 | ||
|
|
1f9c4cfda4 | ||
|
|
08c9cacf0e | ||
|
|
227d327252 | ||
|
|
a21ac343d1 | ||
|
|
5c57090255 | ||
|
|
371468297c | ||
|
|
76491071b3 | ||
|
|
b815ec5eea | ||
|
|
5297ea6fb8 | ||
|
|
0c5e94ee83 | ||
|
|
dd67fa5052 | ||
|
|
57b6bdf37c | ||
|
|
cc67f4d1f9 | ||
|
|
2aa4a86f59 | ||
|
|
18968ca1a3 | ||
|
|
e001807847 | ||
|
|
39a611a3e0 | ||
|
|
92838a19c1 | ||
|
|
85f68d552b | ||
|
|
74769fe7c8 | ||
|
|
4a053e7f71 | ||
|
|
a21baf7999 | ||
|
|
9fe451a08e | ||
|
|
dd37dfa9e7 | ||
|
|
59ab56b9ad | ||
|
|
933069eb53 | ||
|
|
b8bcf811bf | ||
|
|
b1be45153e | ||
|
|
bc229b0f90 | ||
|
|
a5f96db7e8 | ||
|
|
70b5169032 | ||
|
|
96dca6b9a9 | ||
|
|
a5c828525e | ||
|
|
6749695eaa | ||
|
|
3687d53259 | ||
|
|
bb6c5e3c3a | ||
|
|
5c1904f103 | ||
|
|
5ec889b4b1 | ||
|
|
28f106afb3 | ||
|
|
ee13ed1cbc | ||
|
|
1a838745b8 | ||
|
|
933b19ea97 | ||
|
|
292363e15c | ||
|
|
aaab89a68e | ||
|
|
b3be57c358 | ||
|
|
0888a29caa | ||
|
|
f673e70ccc | ||
|
|
9d74f6fef0 | ||
|
|
628a746cf0 | ||
|
|
5631e69c26 | ||
|
|
a4550442b9 | ||
|
|
2e38847b8d | ||
|
|
2a22165194 | ||
|
|
db3be60deb | ||
|
|
5ce5944dc0 | ||
|
|
2198a12143 | ||
|
|
ab1da85791 | ||
|
|
f606604f1c | ||
|
|
41701aa4ee | ||
|
|
2c17449b30 | ||
|
|
fdfba1a298 | ||
|
|
33bde2e13f | ||
|
|
09daed848c | ||
|
|
c6c6958c98 | ||
|
|
1b3fb98ff0 | ||
|
|
3be91e862a | ||
|
|
777170946f | ||
|
|
29d8ec7bee | ||
|
|
8fa7574904 | ||
|
|
702f6df960 | ||
|
|
a87f39543a | ||
|
|
ea81ce06a4 | ||
|
|
736d120af4 | ||
|
|
b4dd99a363 | ||
|
|
b4160af160 | ||
|
|
e527526d35 | ||
|
|
417c45ab2f | ||
|
|
5e95494380 | ||
|
|
a66e657e18 | ||
|
|
5d268704d7 | ||
|
|
c24d5e0b91 | ||
|
|
2897ae0267 | ||
|
|
5fdae20cef | ||
|
|
1a37eca107 | ||
|
|
0ee4de6ce1 | ||
|
|
9f117d4184 | ||
|
|
a1483f88b6 | ||
|
|
963116b0b3 | ||
|
|
c0f2bf9b3f | ||
|
|
73db416ae7 | ||
|
|
e96126ffa5 | ||
|
|
eaf944a438 | ||
|
|
5f5bcd80f8 | ||
|
|
af91f9a73c | ||
|
|
99c4a85ce6 | ||
|
|
ad6aef43d3 | ||
|
|
693a50ade3 | ||
|
|
7c2bbf4aa6 | ||
|
|
33304ec9fa | ||
|
|
16f0587e0a | ||
|
|
5d259fc7da | ||
|
|
765003db02 | ||
|
|
170632dbc9 | ||
|
|
a19737f1bd | ||
|
|
ecd792fd21 | ||
|
|
1f7bf7d068 | ||
|
|
6542aa9c75 | ||
|
|
f51062061e | ||
|
|
1f6b12f75f | ||
|
|
69991d7dcb | ||
|
|
db7dfd4c7e | ||
|
|
22f90bcb2b | ||
|
|
58892d4782 | ||
|
|
c4e57af852 | ||
|
|
999b53ec87 | ||
|
|
37fd5b53ba | ||
|
|
878a735d00 | ||
|
|
3144f78b3f | ||
|
|
c3dc9fd5ac | ||
|
|
a9d477c4e3 | ||
|
|
a1b1d277cd | ||
|
|
aa7d461ae9 | ||
|
|
40d225009e | ||
|
|
8d999995e4 | ||
|
|
239c20c7c8 | ||
|
|
f93d013895 | ||
|
|
39d8211893 | ||
|
|
d980fd59a4 | ||
|
|
86cbc418ce | ||
|
|
94b6c911c6 | ||
|
|
45aecc6dbb | ||
|
|
effa8e0643 | ||
|
|
c0b2b5fa36 | ||
|
|
3720a7ea36 | ||
|
|
c1de788ab9 | ||
|
|
0173a00521 | ||
|
|
8b12a0cfc1 | ||
|
|
6d9571f7d8 | ||
|
|
3ea3bd6245 | ||
|
|
4db0014521 | ||
|
|
91abb80b5f | ||
|
|
bc1c72171c | ||
|
|
26530780c2 | ||
|
|
31db5b3638 | ||
|
|
e5d3df6deb | ||
|
|
7454e51d2b | ||
|
|
d160024fe5 | ||
|
|
35b307126c | ||
|
|
e358edc8e9 | ||
|
|
a1b4d9eff9 | ||
|
|
99d09dd328 | ||
|
|
a7b613cf68 | ||
|
|
8f5001f9ed | ||
|
|
5a2223ca26 | ||
|
|
f41152bd9d | ||
|
|
667ff9612b | ||
|
|
8c5edce5b7 | ||
|
|
9a59e6e307 | ||
|
|
3dd3a2b965 | ||
|
|
2b2449f7e4 | ||
|
|
34f4aa83f9 | ||
|
|
3604a76fea | ||
|
|
2f45356427 | ||
|
|
02d3bf7fe7 | ||
|
|
b2c623a3d9 | ||
|
|
f40c49ac9f | ||
|
|
e67b3ca53a | ||
|
|
9396b05a5a | ||
|
|
89db9987c0 | ||
|
|
a17b2fd358 | ||
|
|
a5615b14a6 | ||
|
|
905f26f222 | ||
|
|
c91e681a55 | ||
|
|
f6c6483b25 | ||
|
|
f9ee9f9ac2 | ||
|
|
20bcf73fa8 | ||
|
|
7c08db30e6 | ||
|
|
82beb53633 | ||
|
|
c1f412260b | ||
|
|
48a5f3bcbb | ||
|
|
5ef68987e5 | ||
|
|
1c90ef2619 | ||
|
|
7bc3d711b4 | ||
|
|
234cc64796 | ||
|
|
977c7b6d89 | ||
|
|
f0b9b11164 | ||
|
|
2bc65d2b02 | ||
|
|
2a573259eb | ||
|
|
803a932706 | ||
|
|
b73dcfb16f | ||
|
|
7171a3933f | ||
|
|
81e207707e | ||
|
|
56db2e5843 | ||
|
|
1ad3c6abc0 | ||
|
|
8cfc114a2f | ||
|
|
ce603d8ef1 | ||
|
|
4a6da670f8 | ||
|
|
3065839c72 | ||
|
|
d66433ffdc | ||
|
|
fd5d5afad8 | ||
|
|
8909114649 | ||
|
|
795ca114d3 | ||
|
|
3761abb167 | ||
|
|
f06b85218a | ||
|
|
f8b7ee38b3 | ||
|
|
1a9978a51a | ||
|
|
b9317661d1 | ||
|
|
0e5fc994d2 | ||
|
|
b76afd1072 | ||
|
|
5eaac2f828 | ||
|
|
d5d1507b34 | ||
|
|
c6830cdb2c | ||
|
|
c428c5a21c | ||
|
|
1165ae613d | ||
|
|
dc9a353cf7 | ||
|
|
1b7650ef2f | ||
|
|
170a60345e | ||
|
|
f50159fa9b | ||
|
|
fb0a078f3a | ||
|
|
f43aa8e18a | ||
|
|
14b4a8b9c6 | ||
|
|
55aff7f133 | ||
|
|
136cd19d05 | ||
|
|
44c68de044 | ||
|
|
234eef51a1 | ||
|
|
492044581c | ||
|
|
8cc3aecf84 | ||
|
|
5f04c14a10 | ||
|
|
fbb9c590ca |
155
.gitignore
vendored
155
.gitignore
vendored
@@ -1,69 +1,74 @@
|
|||||||
config-devices.*
|
/config-devices.*
|
||||||
config-all-devices.*
|
/config-all-devices.*
|
||||||
config-all-disas.*
|
/config-all-disas.*
|
||||||
config-host.*
|
/config-host.*
|
||||||
config-target.*
|
/config-target.*
|
||||||
config.status
|
/config.status
|
||||||
trace/generated-tracers.h
|
/trace/generated-tracers.h
|
||||||
trace/generated-tracers.c
|
/trace/generated-tracers.c
|
||||||
trace/generated-tracers-dtrace.h
|
/trace/generated-tracers-dtrace.h
|
||||||
trace/generated-tracers.dtrace
|
/trace/generated-tracers.dtrace
|
||||||
trace/generated-events.h
|
/trace/generated-events.h
|
||||||
trace/generated-events.c
|
/trace/generated-events.c
|
||||||
libcacard/trace/generated-tracers.c
|
/trace/generated-ust-provider.h
|
||||||
|
/trace/generated-ust.c
|
||||||
|
/libcacard/trace/generated-tracers.c
|
||||||
*-timestamp
|
*-timestamp
|
||||||
*-softmmu
|
/*-softmmu
|
||||||
*-darwin-user
|
/*-darwin-user
|
||||||
*-linux-user
|
/*-linux-user
|
||||||
*-bsd-user
|
/*-bsd-user
|
||||||
libdis*
|
libdis*
|
||||||
libuser
|
libuser
|
||||||
linux-headers/asm
|
/linux-headers/asm
|
||||||
qapi-generated
|
/qapi-generated
|
||||||
qapi-types.[ch]
|
/qapi-types.[ch]
|
||||||
qapi-visit.[ch]
|
/qapi-visit.[ch]
|
||||||
qmp-commands.h
|
/qmp-commands.h
|
||||||
qmp-marshal.c
|
/qmp-marshal.c
|
||||||
qemu-doc.html
|
/qemu-doc.html
|
||||||
qemu-tech.html
|
/qemu-tech.html
|
||||||
qemu-doc.info
|
/qemu-doc.info
|
||||||
qemu-tech.info
|
/qemu-tech.info
|
||||||
qemu.1
|
/qemu.1
|
||||||
qemu.pod
|
/qemu.pod
|
||||||
qemu-img.1
|
/qemu-img.1
|
||||||
qemu-img.pod
|
/qemu-img.pod
|
||||||
qemu-img
|
/qemu-img
|
||||||
qemu-nbd
|
/qemu-nbd
|
||||||
qemu-nbd.8
|
/qemu-nbd.8
|
||||||
qemu-nbd.pod
|
/qemu-nbd.pod
|
||||||
qemu-options.def
|
/qemu-options.def
|
||||||
qemu-options.texi
|
/qemu-options.texi
|
||||||
qemu-img-cmds.texi
|
/qemu-img-cmds.texi
|
||||||
qemu-img-cmds.h
|
/qemu-img-cmds.h
|
||||||
qemu-io
|
/qemu-io
|
||||||
qemu-ga
|
/qemu-ga
|
||||||
qemu-bridge-helper
|
/qemu-bridge-helper
|
||||||
qemu-monitor.texi
|
/qemu-monitor.texi
|
||||||
vscclient
|
/qmp-commands.txt
|
||||||
qmp-commands.txt
|
/vscclient
|
||||||
test-bitops
|
/test-bitops
|
||||||
test-coroutine
|
/test-coroutine
|
||||||
test-int128
|
/test-int128
|
||||||
test-opts-visitor
|
/test-opts-visitor
|
||||||
test-qmp-input-visitor
|
/test-qmp-input-visitor
|
||||||
test-qmp-output-visitor
|
/test-qmp-output-visitor
|
||||||
test-string-input-visitor
|
/test-string-input-visitor
|
||||||
test-string-output-visitor
|
/test-string-output-visitor
|
||||||
test-visitor-serialization
|
/test-visitor-serialization
|
||||||
fsdev/virtfs-proxy-helper
|
/fsdev/virtfs-proxy-helper
|
||||||
fsdev/virtfs-proxy-helper.1
|
/fsdev/virtfs-proxy-helper.1
|
||||||
fsdev/virtfs-proxy-helper.pod
|
/fsdev/virtfs-proxy-helper.pod
|
||||||
.gdbinit
|
/.gdbinit
|
||||||
*.a
|
*.a
|
||||||
*.aux
|
*.aux
|
||||||
*.cp
|
*.cp
|
||||||
*.dvi
|
*.dvi
|
||||||
*.exe
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.mo
|
||||||
*.fn
|
*.fn
|
||||||
*.ky
|
*.ky
|
||||||
*.log
|
*.log
|
||||||
@@ -77,7 +82,7 @@ fsdev/virtfs-proxy-helper.pod
|
|||||||
*.tp
|
*.tp
|
||||||
*.vr
|
*.vr
|
||||||
*.d
|
*.d
|
||||||
!scripts/qemu-guest-agent/fsfreeze-hook.d
|
!/scripts/qemu-guest-agent/fsfreeze-hook.d
|
||||||
*.o
|
*.o
|
||||||
*.lo
|
*.lo
|
||||||
*.la
|
*.la
|
||||||
@@ -90,22 +95,22 @@ fsdev/virtfs-proxy-helper.pod
|
|||||||
*.gcda
|
*.gcda
|
||||||
*.gcno
|
*.gcno
|
||||||
patches
|
patches
|
||||||
pc-bios/bios-pq/status
|
/pc-bios/bios-pq/status
|
||||||
pc-bios/vgabios-pq/status
|
/pc-bios/vgabios-pq/status
|
||||||
pc-bios/optionrom/linuxboot.asm
|
/pc-bios/optionrom/linuxboot.asm
|
||||||
pc-bios/optionrom/linuxboot.bin
|
/pc-bios/optionrom/linuxboot.bin
|
||||||
pc-bios/optionrom/linuxboot.raw
|
/pc-bios/optionrom/linuxboot.raw
|
||||||
pc-bios/optionrom/linuxboot.img
|
/pc-bios/optionrom/linuxboot.img
|
||||||
pc-bios/optionrom/multiboot.asm
|
/pc-bios/optionrom/multiboot.asm
|
||||||
pc-bios/optionrom/multiboot.bin
|
/pc-bios/optionrom/multiboot.bin
|
||||||
pc-bios/optionrom/multiboot.raw
|
/pc-bios/optionrom/multiboot.raw
|
||||||
pc-bios/optionrom/multiboot.img
|
/pc-bios/optionrom/multiboot.img
|
||||||
pc-bios/optionrom/kvmvapic.asm
|
/pc-bios/optionrom/kvmvapic.asm
|
||||||
pc-bios/optionrom/kvmvapic.bin
|
/pc-bios/optionrom/kvmvapic.bin
|
||||||
pc-bios/optionrom/kvmvapic.raw
|
/pc-bios/optionrom/kvmvapic.raw
|
||||||
pc-bios/optionrom/kvmvapic.img
|
/pc-bios/optionrom/kvmvapic.img
|
||||||
pc-bios/s390-ccw/s390-ccw.elf
|
/pc-bios/s390-ccw/s390-ccw.elf
|
||||||
pc-bios/s390-ccw/s390-ccw.img
|
/pc-bios/s390-ccw/s390-ccw.img
|
||||||
.stgit-*
|
.stgit-*
|
||||||
cscope.*
|
cscope.*
|
||||||
tags
|
tags
|
||||||
|
|||||||
11
MAINTAINERS
11
MAINTAINERS
@@ -726,7 +726,7 @@ F: vl.c
|
|||||||
|
|
||||||
Human Monitor (HMP)
|
Human Monitor (HMP)
|
||||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||||
S: Supported
|
S: Maintained
|
||||||
F: monitor.c
|
F: monitor.c
|
||||||
F: hmp.c
|
F: hmp.c
|
||||||
F: hmp-commands.hx
|
F: hmp-commands.hx
|
||||||
@@ -758,7 +758,7 @@ T: git git://github.com/bonzini/qemu.git nbd-next
|
|||||||
QAPI
|
QAPI
|
||||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||||
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||||
S: Supported
|
S: Maintained
|
||||||
F: qapi/
|
F: qapi/
|
||||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||||
|
|
||||||
@@ -772,7 +772,7 @@ T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
|||||||
|
|
||||||
QMP
|
QMP
|
||||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||||
S: Supported
|
S: Maintained
|
||||||
F: qmp.c
|
F: qmp.c
|
||||||
F: monitor.c
|
F: monitor.c
|
||||||
F: qmp-commands.hx
|
F: qmp-commands.hx
|
||||||
@@ -937,6 +937,11 @@ M: Peter Lieven <pl@kamp.de>
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: block/iscsi.c
|
F: block/iscsi.c
|
||||||
|
|
||||||
|
NFS
|
||||||
|
M: Peter Lieven <pl@kamp.de>
|
||||||
|
S: Maintained
|
||||||
|
F: block/nfs.c
|
||||||
|
|
||||||
SSH
|
SSH
|
||||||
M: Richard W.M. Jones <rjones@redhat.com>
|
M: Richard W.M. Jones <rjones@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
|
|||||||
37
Makefile
37
Makefile
@@ -57,6 +57,11 @@ GENERATED_HEADERS += trace/generated-tracers-dtrace.h
|
|||||||
endif
|
endif
|
||||||
GENERATED_SOURCES += trace/generated-tracers.c
|
GENERATED_SOURCES += trace/generated-tracers.c
|
||||||
|
|
||||||
|
ifeq ($(TRACE_BACKEND),ust)
|
||||||
|
GENERATED_HEADERS += trace/generated-ust-provider.h
|
||||||
|
GENERATED_SOURCES += trace/generated-ust.c
|
||||||
|
endif
|
||||||
|
|
||||||
# Don't try to regenerate Makefile or configure
|
# Don't try to regenerate Makefile or configure
|
||||||
# We don't generate any of them
|
# We don't generate any of them
|
||||||
Makefile: ;
|
Makefile: ;
|
||||||
@@ -122,13 +127,29 @@ defconfig:
|
|||||||
|
|
||||||
ifneq ($(wildcard config-host.mak),)
|
ifneq ($(wildcard config-host.mak),)
|
||||||
include $(SRC_PATH)/Makefile.objs
|
include $(SRC_PATH)/Makefile.objs
|
||||||
|
endif
|
||||||
|
|
||||||
|
dummy := $(call unnest-vars,, \
|
||||||
|
stub-obj-y \
|
||||||
|
util-obj-y \
|
||||||
|
qga-obj-y \
|
||||||
|
block-obj-y \
|
||||||
|
block-obj-m \
|
||||||
|
common-obj-y \
|
||||||
|
common-obj-m)
|
||||||
|
|
||||||
|
ifneq ($(wildcard config-host.mak),)
|
||||||
include $(SRC_PATH)/tests/Makefile
|
include $(SRC_PATH)/tests/Makefile
|
||||||
endif
|
endif
|
||||||
ifeq ($(CONFIG_SMARTCARD_NSS),y)
|
ifeq ($(CONFIG_SMARTCARD_NSS),y)
|
||||||
include $(SRC_PATH)/libcacard/Makefile
|
include $(SRC_PATH)/libcacard/Makefile
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
|
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
|
||||||
|
|
||||||
|
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||||
|
|
||||||
|
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
||||||
|
|
||||||
config-host.h: config-host.h-timestamp
|
config-host.h: config-host.h-timestamp
|
||||||
config-host.h-timestamp: config-host.mak
|
config-host.h-timestamp: config-host.mak
|
||||||
@@ -138,6 +159,7 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx
|
|||||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||||
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
||||||
|
|
||||||
|
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
||||||
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
|
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
|
||||||
|
|
||||||
subdir-%:
|
subdir-%:
|
||||||
@@ -187,6 +209,9 @@ Makefile: $(version-obj-y) $(version-lobj-y)
|
|||||||
libqemustub.a: $(stub-obj-y)
|
libqemustub.a: $(stub-obj-y)
|
||||||
libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
|
libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
|
||||||
|
|
||||||
|
block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL
|
||||||
|
util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
qemu-img.o: qemu-img-cmds.h
|
qemu-img.o: qemu-img-cmds.h
|
||||||
@@ -242,6 +267,8 @@ clean:
|
|||||||
rm -f qemu-options.def
|
rm -f qemu-options.def
|
||||||
find . -name '*.[oda]' -type f -exec rm -f {} +
|
find . -name '*.[oda]' -type f -exec rm -f {} +
|
||||||
find . -name '*.l[oa]' -type f -exec rm -f {} +
|
find . -name '*.l[oa]' -type f -exec rm -f {} +
|
||||||
|
find . -name '*$(DSOSUF)' -type f -exec rm -f {} +
|
||||||
|
find . -name '*.mo' -type f -exec rm -f {} +
|
||||||
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||||
rm -f fsdev/*.pod
|
rm -f fsdev/*.pod
|
||||||
rm -rf .libs */.libs
|
rm -rf .libs */.libs
|
||||||
@@ -293,7 +320,7 @@ ifdef INSTALL_BLOBS
|
|||||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
|
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
|
||||||
acpi-dsdt.aml q35-acpi-dsdt.aml \
|
acpi-dsdt.aml q35-acpi-dsdt.aml \
|
||||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin \
|
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||||
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
|
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
|
||||||
@@ -349,6 +376,12 @@ install-datadir install-localstatedir
|
|||||||
ifneq ($(TOOLS),)
|
ifneq ($(TOOLS),)
|
||||||
$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
|
$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
|
||||||
endif
|
endif
|
||||||
|
ifneq ($(CONFIG_MODULES),)
|
||||||
|
$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"
|
||||||
|
for s in $(patsubst %.mo,%$(DSOSUF),$(modules-m)); do \
|
||||||
|
$(INSTALL_PROG) $(STRIP_OPT) $$s "$(DESTDIR)$(qemu_moddir)/$${s//\//-}"; \
|
||||||
|
done
|
||||||
|
endif
|
||||||
ifneq ($(HELPERS-y),)
|
ifneq ($(HELPERS-y),)
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(libexecdir)"
|
$(INSTALL_DIR) "$(DESTDIR)$(libexecdir)"
|
||||||
$(INSTALL_PROG) $(STRIP_OPT) $(HELPERS-y) "$(DESTDIR)$(libexecdir)"
|
$(INSTALL_PROG) $(STRIP_OPT) $(HELPERS-y) "$(DESTDIR)$(libexecdir)"
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
|
|||||||
block-obj-y += qemu-coroutine-sleep.o
|
block-obj-y += qemu-coroutine-sleep.o
|
||||||
block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
|
block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
|
||||||
|
|
||||||
|
block-obj-m = block/
|
||||||
|
|
||||||
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
|
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
|
||||||
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
|
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
|
||||||
# only pull in the actual virtio-9p device if we also enabled virtio.
|
# only pull in the actual virtio-9p device if we also enabled virtio.
|
||||||
@@ -41,7 +43,7 @@ libcacard-y += libcacard/vcardt.o
|
|||||||
# single QEMU executable should support all CPUs and machines.
|
# single QEMU executable should support all CPUs and machines.
|
||||||
|
|
||||||
ifeq ($(CONFIG_SOFTMMU),y)
|
ifeq ($(CONFIG_SOFTMMU),y)
|
||||||
common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
|
common-obj-y = blockdev.o blockdev-nbd.o block/
|
||||||
common-obj-y += net/
|
common-obj-y += net/
|
||||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||||
@@ -111,18 +113,3 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo
|
|||||||
# by libqemuutil.a. These should be moved to a separate .json schema.
|
# by libqemuutil.a. These should be moved to a separate .json schema.
|
||||||
qga-obj-y = qga/ qapi-types.o qapi-visit.o
|
qga-obj-y = qga/ qapi-types.o qapi-visit.o
|
||||||
qga-vss-dll-obj-y = qga/
|
qga-vss-dll-obj-y = qga/
|
||||||
|
|
||||||
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
|
||||||
|
|
||||||
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
|
||||||
|
|
||||||
QEMU_CFLAGS+=$(GLIB_CFLAGS)
|
|
||||||
|
|
||||||
nested-vars += \
|
|
||||||
stub-obj-y \
|
|
||||||
util-obj-y \
|
|
||||||
qga-obj-y \
|
|
||||||
qga-vss-dll-obj-y \
|
|
||||||
block-obj-y \
|
|
||||||
common-obj-y
|
|
||||||
dummy := $(call unnest-vars)
|
|
||||||
|
|||||||
@@ -130,8 +130,6 @@ else
|
|||||||
obj-y += hw/$(TARGET_BASE_ARCH)/
|
obj-y += hw/$(TARGET_BASE_ARCH)/
|
||||||
endif
|
endif
|
||||||
|
|
||||||
main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
|
||||||
|
|
||||||
GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h
|
GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h
|
||||||
|
|
||||||
endif # CONFIG_SOFTMMU
|
endif # CONFIG_SOFTMMU
|
||||||
@@ -139,13 +137,26 @@ endif # CONFIG_SOFTMMU
|
|||||||
# Workaround for http://gcc.gnu.org/PR55489, see configure.
|
# Workaround for http://gcc.gnu.org/PR55489, see configure.
|
||||||
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
|
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
|
||||||
|
|
||||||
nested-vars += obj-y
|
dummy := $(call unnest-vars,,obj-y)
|
||||||
|
|
||||||
# This resolves all nested paths, so it must come last
|
# we are making another call to unnest-vars with different vars, protect obj-y,
|
||||||
|
# it can be overriden in subdir Makefile.objs
|
||||||
|
obj-y-save := $(obj-y)
|
||||||
|
|
||||||
|
block-obj-y :=
|
||||||
|
common-obj-y :=
|
||||||
include $(SRC_PATH)/Makefile.objs
|
include $(SRC_PATH)/Makefile.objs
|
||||||
|
dummy := $(call unnest-vars,.., \
|
||||||
|
block-obj-y \
|
||||||
|
block-obj-m \
|
||||||
|
common-obj-y \
|
||||||
|
common-obj-m)
|
||||||
|
|
||||||
all-obj-y = $(obj-y)
|
# Now restore obj-y
|
||||||
all-obj-y += $(addprefix ../, $(common-obj-y))
|
obj-y := $(obj-y-save)
|
||||||
|
|
||||||
|
all-obj-y = $(obj-y) $(common-obj-y)
|
||||||
|
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
|
||||||
|
|
||||||
ifndef CONFIG_HAIKU
|
ifndef CONFIG_HAIKU
|
||||||
LIBS+=-lm
|
LIBS+=-lm
|
||||||
|
|||||||
113
arch_init.c
113
arch_init.c
@@ -122,7 +122,6 @@ static void check_guest_throttling(void);
|
|||||||
#define RAM_SAVE_FLAG_XBZRLE 0x40
|
#define RAM_SAVE_FLAG_XBZRLE 0x40
|
||||||
/* 0x80 is reserved in migration.h start with 0x100 next */
|
/* 0x80 is reserved in migration.h start with 0x100 next */
|
||||||
|
|
||||||
|
|
||||||
static struct defconfig_file {
|
static struct defconfig_file {
|
||||||
const char *filename;
|
const char *filename;
|
||||||
/* Indicates it is an user config file (disabled by -no-user-config) */
|
/* Indicates it is an user config file (disabled by -no-user-config) */
|
||||||
@@ -133,6 +132,7 @@ static struct defconfig_file {
|
|||||||
{ NULL }, /* end of list */
|
{ NULL }, /* end of list */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE];
|
||||||
|
|
||||||
int qemu_read_default_config_files(bool userconfig)
|
int qemu_read_default_config_files(bool userconfig)
|
||||||
{
|
{
|
||||||
@@ -164,20 +164,22 @@ static struct {
|
|||||||
uint8_t *encoded_buf;
|
uint8_t *encoded_buf;
|
||||||
/* buffer for storing page content */
|
/* buffer for storing page content */
|
||||||
uint8_t *current_buf;
|
uint8_t *current_buf;
|
||||||
/* buffer used for XBZRLE decoding */
|
|
||||||
uint8_t *decoded_buf;
|
|
||||||
/* Cache for XBZRLE */
|
/* Cache for XBZRLE */
|
||||||
PageCache *cache;
|
PageCache *cache;
|
||||||
} XBZRLE = {
|
} XBZRLE = {
|
||||||
.encoded_buf = NULL,
|
.encoded_buf = NULL,
|
||||||
.current_buf = NULL,
|
.current_buf = NULL,
|
||||||
.decoded_buf = NULL,
|
|
||||||
.cache = NULL,
|
.cache = NULL,
|
||||||
};
|
};
|
||||||
|
/* buffer used for XBZRLE decoding */
|
||||||
|
static uint8_t *xbzrle_decoded_buf;
|
||||||
|
|
||||||
int64_t xbzrle_cache_resize(int64_t new_size)
|
int64_t xbzrle_cache_resize(int64_t new_size)
|
||||||
{
|
{
|
||||||
|
if (new_size < TARGET_PAGE_SIZE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (XBZRLE.cache != NULL) {
|
if (XBZRLE.cache != NULL) {
|
||||||
return cache_resize(XBZRLE.cache, new_size / TARGET_PAGE_SIZE) *
|
return cache_resize(XBZRLE.cache, new_size / TARGET_PAGE_SIZE) *
|
||||||
TARGET_PAGE_SIZE;
|
TARGET_PAGE_SIZE;
|
||||||
@@ -271,6 +273,34 @@ static size_t save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This is the last block that we have visited serching for dirty pages
|
||||||
|
*/
|
||||||
|
static RAMBlock *last_seen_block;
|
||||||
|
/* This is the last block from where we have sent data */
|
||||||
|
static RAMBlock *last_sent_block;
|
||||||
|
static ram_addr_t last_offset;
|
||||||
|
static unsigned long *migration_bitmap;
|
||||||
|
static uint64_t migration_dirty_pages;
|
||||||
|
static uint32_t last_version;
|
||||||
|
static bool ram_bulk_stage;
|
||||||
|
|
||||||
|
/* Update the xbzrle cache to reflect a page that's been sent as all 0.
|
||||||
|
* The important thing is that a stale (not-yet-0'd) page be replaced
|
||||||
|
* by the new data.
|
||||||
|
* As a bonus, if the page wasn't in the cache it gets added so that
|
||||||
|
* when a small write is made into the 0'd page it gets XBZRLE sent
|
||||||
|
*/
|
||||||
|
static void xbzrle_cache_zero_page(ram_addr_t current_addr)
|
||||||
|
{
|
||||||
|
if (ram_bulk_stage || !migrate_use_xbzrle()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't care if this fails to allocate a new cache page
|
||||||
|
* as long as it updated an old one */
|
||||||
|
cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE);
|
||||||
|
}
|
||||||
|
|
||||||
#define ENCODING_FLAG_XBZRLE 0x1
|
#define ENCODING_FLAG_XBZRLE 0x1
|
||||||
|
|
||||||
static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
|
static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
|
||||||
@@ -282,7 +312,9 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
|
|||||||
|
|
||||||
if (!cache_is_cached(XBZRLE.cache, current_addr)) {
|
if (!cache_is_cached(XBZRLE.cache, current_addr)) {
|
||||||
if (!last_stage) {
|
if (!last_stage) {
|
||||||
cache_insert(XBZRLE.cache, current_addr, current_data);
|
if (cache_insert(XBZRLE.cache, current_addr, current_data) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
acct_info.xbzrle_cache_miss++;
|
acct_info.xbzrle_cache_miss++;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -325,18 +357,6 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
|
|||||||
return bytes_sent;
|
return bytes_sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This is the last block that we have visited serching for dirty pages
|
|
||||||
*/
|
|
||||||
static RAMBlock *last_seen_block;
|
|
||||||
/* This is the last block from where we have sent data */
|
|
||||||
static RAMBlock *last_sent_block;
|
|
||||||
static ram_addr_t last_offset;
|
|
||||||
static unsigned long *migration_bitmap;
|
|
||||||
static uint64_t migration_dirty_pages;
|
|
||||||
static uint32_t last_version;
|
|
||||||
static bool ram_bulk_stage;
|
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
|
ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
|
||||||
ram_addr_t start)
|
ram_addr_t start)
|
||||||
@@ -508,6 +528,7 @@ static int ram_save_block(QEMUFile *f, bool last_stage)
|
|||||||
} else {
|
} else {
|
||||||
int ret;
|
int ret;
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
|
bool send_async = true;
|
||||||
int cont = (block == last_sent_block) ?
|
int cont = (block == last_sent_block) ?
|
||||||
RAM_SAVE_FLAG_CONTINUE : 0;
|
RAM_SAVE_FLAG_CONTINUE : 0;
|
||||||
|
|
||||||
@@ -518,6 +539,7 @@ static int ram_save_block(QEMUFile *f, bool last_stage)
|
|||||||
ret = ram_control_save_page(f, block->offset,
|
ret = ram_control_save_page(f, block->offset,
|
||||||
offset, TARGET_PAGE_SIZE, &bytes_sent);
|
offset, TARGET_PAGE_SIZE, &bytes_sent);
|
||||||
|
|
||||||
|
current_addr = block->offset + offset;
|
||||||
if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
|
||||||
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
if (ret != RAM_SAVE_CONTROL_DELAYED) {
|
||||||
if (bytes_sent > 0) {
|
if (bytes_sent > 0) {
|
||||||
@@ -532,19 +554,35 @@ static int ram_save_block(QEMUFile *f, bool last_stage)
|
|||||||
RAM_SAVE_FLAG_COMPRESS);
|
RAM_SAVE_FLAG_COMPRESS);
|
||||||
qemu_put_byte(f, 0);
|
qemu_put_byte(f, 0);
|
||||||
bytes_sent++;
|
bytes_sent++;
|
||||||
|
/* Must let xbzrle know, otherwise a previous (now 0'd) cached
|
||||||
|
* page would be stale
|
||||||
|
*/
|
||||||
|
xbzrle_cache_zero_page(current_addr);
|
||||||
} else if (!ram_bulk_stage && migrate_use_xbzrle()) {
|
} else if (!ram_bulk_stage && migrate_use_xbzrle()) {
|
||||||
current_addr = block->offset + offset;
|
|
||||||
bytes_sent = save_xbzrle_page(f, p, current_addr, block,
|
bytes_sent = save_xbzrle_page(f, p, current_addr, block,
|
||||||
offset, cont, last_stage);
|
offset, cont, last_stage);
|
||||||
if (!last_stage) {
|
if (!last_stage) {
|
||||||
|
/* We must send exactly what's in the xbzrle cache
|
||||||
|
* even if the page wasn't xbzrle compressed, so that
|
||||||
|
* it's right next time.
|
||||||
|
*/
|
||||||
p = get_cached_data(XBZRLE.cache, current_addr);
|
p = get_cached_data(XBZRLE.cache, current_addr);
|
||||||
|
|
||||||
|
/* Can't send this cached data async, since the cache page
|
||||||
|
* might get updated before it gets to the wire
|
||||||
|
*/
|
||||||
|
send_async = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XBZRLE overflow or normal page */
|
/* XBZRLE overflow or normal page */
|
||||||
if (bytes_sent == -1) {
|
if (bytes_sent == -1) {
|
||||||
bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
|
bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
|
||||||
qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
|
if (send_async) {
|
||||||
|
qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
|
||||||
|
} else {
|
||||||
|
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
|
||||||
|
}
|
||||||
bytes_sent += TARGET_PAGE_SIZE;
|
bytes_sent += TARGET_PAGE_SIZE;
|
||||||
acct_info.norm_pages++;
|
acct_info.norm_pages++;
|
||||||
}
|
}
|
||||||
@@ -602,6 +640,12 @@ uint64_t ram_bytes_total(void)
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void free_xbzrle_decoded_buf(void)
|
||||||
|
{
|
||||||
|
g_free(xbzrle_decoded_buf);
|
||||||
|
xbzrle_decoded_buf = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void migration_end(void)
|
static void migration_end(void)
|
||||||
{
|
{
|
||||||
if (migration_bitmap) {
|
if (migration_bitmap) {
|
||||||
@@ -615,8 +659,9 @@ static void migration_end(void)
|
|||||||
g_free(XBZRLE.cache);
|
g_free(XBZRLE.cache);
|
||||||
g_free(XBZRLE.encoded_buf);
|
g_free(XBZRLE.encoded_buf);
|
||||||
g_free(XBZRLE.current_buf);
|
g_free(XBZRLE.current_buf);
|
||||||
g_free(XBZRLE.decoded_buf);
|
|
||||||
XBZRLE.cache = NULL;
|
XBZRLE.cache = NULL;
|
||||||
|
XBZRLE.encoded_buf = NULL;
|
||||||
|
XBZRLE.current_buf = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,8 +700,22 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
|||||||
DPRINTF("Error creating cache\n");
|
DPRINTF("Error creating cache\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE);
|
|
||||||
XBZRLE.current_buf = g_malloc(TARGET_PAGE_SIZE);
|
/* We prefer not to abort if there is no memory */
|
||||||
|
XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE);
|
||||||
|
if (!XBZRLE.encoded_buf) {
|
||||||
|
DPRINTF("Error allocating encoded_buf\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE);
|
||||||
|
if (!XBZRLE.current_buf) {
|
||||||
|
DPRINTF("Error allocating current_buf\n");
|
||||||
|
g_free(XBZRLE.encoded_buf);
|
||||||
|
XBZRLE.encoded_buf = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
acct_clear();
|
acct_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,8 +866,8 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
|
|||||||
unsigned int xh_len;
|
unsigned int xh_len;
|
||||||
int xh_flags;
|
int xh_flags;
|
||||||
|
|
||||||
if (!XBZRLE.decoded_buf) {
|
if (!xbzrle_decoded_buf) {
|
||||||
XBZRLE.decoded_buf = g_malloc(TARGET_PAGE_SIZE);
|
xbzrle_decoded_buf = g_malloc(TARGET_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extract RLE header */
|
/* extract RLE header */
|
||||||
@@ -825,10 +884,10 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* load data and decode */
|
/* load data and decode */
|
||||||
qemu_get_buffer(f, XBZRLE.decoded_buf, xh_len);
|
qemu_get_buffer(f, xbzrle_decoded_buf, xh_len);
|
||||||
|
|
||||||
/* decode RLE */
|
/* decode RLE */
|
||||||
ret = xbzrle_decode_buffer(XBZRLE.decoded_buf, xh_len, host,
|
ret = xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host,
|
||||||
TARGET_PAGE_SIZE);
|
TARGET_PAGE_SIZE);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
fprintf(stderr, "Failed to load XBZRLE page - decode error!\n");
|
fprintf(stderr, "Failed to load XBZRLE page - decode error!\n");
|
||||||
|
|||||||
@@ -25,8 +25,17 @@
|
|||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
|
||||||
#define LINE_IN_SAMPLES 1024
|
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
|
||||||
#define LINE_OUT_SAMPLES 1024
|
#define LINE_OUT_SAMPLES (480 * 4)
|
||||||
|
#else
|
||||||
|
#define LINE_OUT_SAMPLES (256 * 4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
|
||||||
|
#define LINE_IN_SAMPLES (480 * 4)
|
||||||
|
#else
|
||||||
|
#define LINE_IN_SAMPLES (256 * 4)
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct SpiceRateCtl {
|
typedef struct SpiceRateCtl {
|
||||||
int64_t start_ticks;
|
int64_t start_ticks;
|
||||||
@@ -111,7 +120,11 @@ static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
|
|||||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||||
struct audsettings settings;
|
struct audsettings settings;
|
||||||
|
|
||||||
|
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
|
||||||
|
settings.freq = spice_server_get_best_playback_rate(NULL);
|
||||||
|
#else
|
||||||
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
||||||
|
#endif
|
||||||
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
||||||
settings.fmt = AUD_FMT_S16;
|
settings.fmt = AUD_FMT_S16;
|
||||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||||
@@ -122,6 +135,9 @@ static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
|
|||||||
|
|
||||||
out->sin.base.sif = &playback_sif.base;
|
out->sin.base.sif = &playback_sif.base;
|
||||||
qemu_spice_add_interface (&out->sin.base);
|
qemu_spice_add_interface (&out->sin.base);
|
||||||
|
#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
|
||||||
|
spice_server_set_playback_rate(&out->sin, settings.freq);
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +248,11 @@ static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
|
|||||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||||
struct audsettings settings;
|
struct audsettings settings;
|
||||||
|
|
||||||
|
#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
|
||||||
|
settings.freq = spice_server_get_best_record_rate(NULL);
|
||||||
|
#else
|
||||||
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
|
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
|
||||||
|
#endif
|
||||||
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
||||||
settings.fmt = AUD_FMT_S16;
|
settings.fmt = AUD_FMT_S16;
|
||||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||||
@@ -243,6 +263,9 @@ static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
|
|||||||
|
|
||||||
in->sin.base.sif = &record_sif.base;
|
in->sin.base.sif = &record_sif.base;
|
||||||
qemu_spice_add_interface (&in->sin.base);
|
qemu_spice_add_interface (&in->sin.base);
|
||||||
|
#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3
|
||||||
|
spice_server_set_record_rate(&in->sin, settings.freq);
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -566,7 +566,7 @@ CharDriverState *chr_baum_init(void)
|
|||||||
BaumDriverState *baum;
|
BaumDriverState *baum;
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
brlapi_handle_t *handle;
|
brlapi_handle_t *handle;
|
||||||
#ifdef CONFIG_SDL
|
#if defined(CONFIG_SDL) && SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
|
||||||
SDL_SysWMinfo info;
|
SDL_SysWMinfo info;
|
||||||
#endif
|
#endif
|
||||||
int tty;
|
int tty;
|
||||||
@@ -595,7 +595,7 @@ CharDriverState *chr_baum_init(void)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SDL
|
#if defined(CONFIG_SDL) && SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
|
||||||
memset(&info, 0, sizeof(info));
|
memset(&info, 0, sizeof(info));
|
||||||
SDL_VERSION(&info.version);
|
SDL_VERSION(&info.version);
|
||||||
if (SDL_GetWMInfo(&info))
|
if (SDL_GetWMInfo(&info))
|
||||||
|
|||||||
314
block.c
314
block.c
@@ -421,7 +421,7 @@ static void coroutine_fn bdrv_create_co_entry(void *opaque)
|
|||||||
assert(cco->drv);
|
assert(cco->drv);
|
||||||
|
|
||||||
ret = cco->drv->bdrv_create(cco->filename, cco->options, &local_err);
|
ret = cco->drv->bdrv_create(cco->filename, cco->options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(&cco->err, local_err);
|
error_propagate(&cco->err, local_err);
|
||||||
}
|
}
|
||||||
cco->ret = ret;
|
cco->ret = ret;
|
||||||
@@ -460,7 +460,7 @@ int bdrv_create(BlockDriver *drv, const char* filename,
|
|||||||
|
|
||||||
ret = cco.ret;
|
ret = cco.ret;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (error_is_set(&cco.err)) {
|
if (cco.err) {
|
||||||
error_propagate(errp, cco.err);
|
error_propagate(errp, cco.err);
|
||||||
} else {
|
} else {
|
||||||
error_setg_errno(errp, -ret, "Could not create image");
|
error_setg_errno(errp, -ret, "Could not create image");
|
||||||
@@ -486,7 +486,7 @@ int bdrv_create_file(const char* filename, QEMUOptionParameter *options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_create(drv, filename, options, &local_err);
|
ret = bdrv_create(drv, filename, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -547,8 +547,9 @@ int get_tmp_filename(char *filename, int size)
|
|||||||
int fd;
|
int fd;
|
||||||
const char *tmpdir;
|
const char *tmpdir;
|
||||||
tmpdir = getenv("TMPDIR");
|
tmpdir = getenv("TMPDIR");
|
||||||
if (!tmpdir)
|
if (!tmpdir) {
|
||||||
tmpdir = "/tmp";
|
tmpdir = "/var/tmp";
|
||||||
|
}
|
||||||
if (snprintf(filename, size, "%s/vl.XXXXXX", tmpdir) >= size) {
|
if (snprintf(filename, size, "%s/vl.XXXXXX", tmpdir) >= size) {
|
||||||
return -EOVERFLOW;
|
return -EOVERFLOW;
|
||||||
}
|
}
|
||||||
@@ -796,6 +797,13 @@ static int bdrv_assign_node_name(BlockDriverState *bs,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* takes care of avoiding namespaces collisions */
|
||||||
|
if (bdrv_find(node_name)) {
|
||||||
|
error_setg(errp, "node-name=%s is conflicting with a device id",
|
||||||
|
node_name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* takes care of avoiding duplicates node names */
|
/* takes care of avoiding duplicates node names */
|
||||||
if (bdrv_find_node(node_name)) {
|
if (bdrv_find_node(node_name)) {
|
||||||
error_setg(errp, "Duplicate node name");
|
error_setg(errp, "Duplicate node name");
|
||||||
@@ -832,6 +840,12 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
|||||||
filename = qdict_get_try_str(options, "filename");
|
filename = qdict_get_try_str(options, "filename");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (drv->bdrv_needs_filename && !filename) {
|
||||||
|
error_setg(errp, "The '%s' block driver requires a file name",
|
||||||
|
drv->format_name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
|
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
|
||||||
|
|
||||||
node_name = qdict_get_try_str(options, "node-name");
|
node_name = qdict_get_try_str(options, "node-name");
|
||||||
@@ -903,7 +917,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
} else if (bs->filename[0]) {
|
} else if (bs->filename[0]) {
|
||||||
error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename);
|
error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename);
|
||||||
@@ -942,54 +956,27 @@ free_and_fail:
|
|||||||
/*
|
/*
|
||||||
* Opens a file using a protocol (file, host_device, nbd, ...)
|
* Opens a file using a protocol (file, host_device, nbd, ...)
|
||||||
*
|
*
|
||||||
* options is a QDict of options to pass to the block drivers, or NULL for an
|
* options is an indirect pointer to a QDict of options to pass to the block
|
||||||
* empty set of options. The reference to the QDict belongs to the block layer
|
* drivers, or pointer to NULL for an empty set of options. If this function
|
||||||
* after the call (even on failure), so if the caller intends to reuse the
|
* takes ownership of the QDict reference, it will set *options to NULL;
|
||||||
* dictionary, it needs to use QINCREF() before calling bdrv_file_open.
|
* otherwise, it will contain unused/unrecognized options after this function
|
||||||
|
* returns. Then, the caller is responsible for freeing it. If it intends to
|
||||||
|
* reuse the QDict, QINCREF() should be called beforehand.
|
||||||
*/
|
*/
|
||||||
int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
static int bdrv_file_open(BlockDriverState *bs, const char *filename,
|
||||||
const char *reference, QDict *options, int flags,
|
QDict **options, int flags, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = NULL;
|
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
const char *drvname;
|
const char *drvname;
|
||||||
bool allow_protocol_prefix = false;
|
bool allow_protocol_prefix = false;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* NULL means an empty set of options */
|
|
||||||
if (options == NULL) {
|
|
||||||
options = qdict_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reference) {
|
|
||||||
if (filename || qdict_size(options)) {
|
|
||||||
error_setg(errp, "Cannot reference an existing block device with "
|
|
||||||
"additional options or a new filename");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
QDECREF(options);
|
|
||||||
|
|
||||||
bs = bdrv_find(reference);
|
|
||||||
if (!bs) {
|
|
||||||
error_setg(errp, "Cannot find block device '%s'", reference);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
bdrv_ref(bs);
|
|
||||||
*pbs = bs;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs = bdrv_new("");
|
|
||||||
bs->options = options;
|
|
||||||
options = qdict_clone_shallow(options);
|
|
||||||
|
|
||||||
/* Fetch the file name from the options QDict if necessary */
|
/* Fetch the file name from the options QDict if necessary */
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
filename = qdict_get_try_str(options, "filename");
|
filename = qdict_get_try_str(*options, "filename");
|
||||||
} else if (filename && !qdict_haskey(options, "filename")) {
|
} else if (filename && !qdict_haskey(*options, "filename")) {
|
||||||
qdict_put(options, "filename", qstring_from_str(filename));
|
qdict_put(*options, "filename", qstring_from_str(filename));
|
||||||
allow_protocol_prefix = true;
|
allow_protocol_prefix = true;
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Can't specify 'file' and 'filename' options at the "
|
error_setg(errp, "Can't specify 'file' and 'filename' options at the "
|
||||||
@@ -999,13 +986,13 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find the right block driver */
|
/* Find the right block driver */
|
||||||
drvname = qdict_get_try_str(options, "driver");
|
drvname = qdict_get_try_str(*options, "driver");
|
||||||
if (drvname) {
|
if (drvname) {
|
||||||
drv = bdrv_find_format(drvname);
|
drv = bdrv_find_format(drvname);
|
||||||
if (!drv) {
|
if (!drv) {
|
||||||
error_setg(errp, "Unknown driver '%s'", drvname);
|
error_setg(errp, "Unknown driver '%s'", drvname);
|
||||||
}
|
}
|
||||||
qdict_del(options, "driver");
|
qdict_del(*options, "driver");
|
||||||
} else if (filename) {
|
} else if (filename) {
|
||||||
drv = bdrv_find_protocol(filename, allow_protocol_prefix);
|
drv = bdrv_find_protocol(filename, allow_protocol_prefix);
|
||||||
if (!drv) {
|
if (!drv) {
|
||||||
@@ -1024,51 +1011,30 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
|||||||
|
|
||||||
/* Parse the filename and open it */
|
/* Parse the filename and open it */
|
||||||
if (drv->bdrv_parse_filename && filename) {
|
if (drv->bdrv_parse_filename && filename) {
|
||||||
drv->bdrv_parse_filename(filename, options, &local_err);
|
drv->bdrv_parse_filename(filename, *options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
qdict_del(options, "filename");
|
qdict_del(*options, "filename");
|
||||||
} else if (drv->bdrv_needs_filename && !filename) {
|
|
||||||
error_setg(errp, "The '%s' block driver requires a file name",
|
|
||||||
drv->format_name);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!drv->bdrv_file_open) {
|
if (!drv->bdrv_file_open) {
|
||||||
ret = bdrv_open(bs, filename, options, flags, drv, &local_err);
|
ret = bdrv_open(&bs, filename, NULL, *options, flags, drv, &local_err);
|
||||||
options = NULL;
|
*options = NULL;
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err);
|
ret = bdrv_open_common(bs, NULL, *options, flags, drv, &local_err);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if any unknown options were used */
|
|
||||||
if (options && (qdict_size(options) != 0)) {
|
|
||||||
const QDictEntry *entry = qdict_first(options);
|
|
||||||
error_setg(errp, "Block protocol '%s' doesn't support the option '%s'",
|
|
||||||
drv->format_name, entry->key);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
QDECREF(options);
|
|
||||||
|
|
||||||
bs->growable = 1;
|
bs->growable = 1;
|
||||||
*pbs = bs;
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
QDECREF(options);
|
|
||||||
if (!bs->drv) {
|
|
||||||
QDECREF(bs->options);
|
|
||||||
}
|
|
||||||
bdrv_unref(bs);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1108,8 +1074,6 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
|||||||
sizeof(backing_filename));
|
sizeof(backing_filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->backing_hd = bdrv_new("");
|
|
||||||
|
|
||||||
if (bs->backing_format[0] != '\0') {
|
if (bs->backing_format[0] != '\0') {
|
||||||
back_drv = bdrv_find_format(bs->backing_format);
|
back_drv = bdrv_find_format(bs->backing_format);
|
||||||
}
|
}
|
||||||
@@ -1118,11 +1082,11 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
|||||||
back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT |
|
back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT |
|
||||||
BDRV_O_COPY_ON_READ);
|
BDRV_O_COPY_ON_READ);
|
||||||
|
|
||||||
ret = bdrv_open(bs->backing_hd,
|
assert(bs->backing_hd == NULL);
|
||||||
*backing_filename ? backing_filename : NULL, options,
|
ret = bdrv_open(&bs->backing_hd,
|
||||||
|
*backing_filename ? backing_filename : NULL, NULL, options,
|
||||||
back_flags, back_drv, &local_err);
|
back_flags, back_drv, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_unref(bs->backing_hd);
|
|
||||||
bs->backing_hd = NULL;
|
bs->backing_hd = NULL;
|
||||||
bs->open_flags |= BDRV_O_NO_BACKING;
|
bs->open_flags |= BDRV_O_NO_BACKING;
|
||||||
error_setg(errp, "Could not open backing file: %s",
|
error_setg(errp, "Could not open backing file: %s",
|
||||||
@@ -1146,10 +1110,6 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
|||||||
* Opens a disk image whose options are given as BlockdevRef in another block
|
* Opens a disk image whose options are given as BlockdevRef in another block
|
||||||
* device's options.
|
* device's options.
|
||||||
*
|
*
|
||||||
* If force_raw is true, bdrv_file_open() will be used, thereby preventing any
|
|
||||||
* image format auto-detection. If it is false and a filename is given,
|
|
||||||
* bdrv_open() will be used for auto-detection.
|
|
||||||
*
|
|
||||||
* If allow_none is true, no image will be opened if filename is false and no
|
* If allow_none is true, no image will be opened if filename is false and no
|
||||||
* BlockdevRef is given. *pbs will remain unchanged and 0 will be returned.
|
* BlockdevRef is given. *pbs will remain unchanged and 0 will be returned.
|
||||||
*
|
*
|
||||||
@@ -1159,16 +1119,21 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
|||||||
* BlockdevRef.
|
* BlockdevRef.
|
||||||
*
|
*
|
||||||
* The BlockdevRef will be removed from the options QDict.
|
* The BlockdevRef will be removed from the options QDict.
|
||||||
|
*
|
||||||
|
* To conform with the behavior of bdrv_open(), *pbs has to be NULL.
|
||||||
*/
|
*/
|
||||||
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||||
QDict *options, const char *bdref_key, int flags,
|
QDict *options, const char *bdref_key, int flags,
|
||||||
bool force_raw, bool allow_none, Error **errp)
|
bool allow_none, Error **errp)
|
||||||
{
|
{
|
||||||
QDict *image_options;
|
QDict *image_options;
|
||||||
int ret;
|
int ret;
|
||||||
char *bdref_key_dot;
|
char *bdref_key_dot;
|
||||||
const char *reference;
|
const char *reference;
|
||||||
|
|
||||||
|
assert(pbs);
|
||||||
|
assert(*pbs == NULL);
|
||||||
|
|
||||||
bdref_key_dot = g_strdup_printf("%s.", bdref_key);
|
bdref_key_dot = g_strdup_printf("%s.", bdref_key);
|
||||||
qdict_extract_subqdict(options, &image_options, bdref_key_dot);
|
qdict_extract_subqdict(options, &image_options, bdref_key_dot);
|
||||||
g_free(bdref_key_dot);
|
g_free(bdref_key_dot);
|
||||||
@@ -1185,30 +1150,7 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filename && !force_raw) {
|
ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp);
|
||||||
/* If a filename is given and the block driver should be detected
|
|
||||||
automatically (instead of using none), use bdrv_open() in order to do
|
|
||||||
that auto-detection. */
|
|
||||||
BlockDriverState *bs;
|
|
||||||
|
|
||||||
if (reference) {
|
|
||||||
error_setg(errp, "Cannot reference an existing block device while "
|
|
||||||
"giving a filename");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs = bdrv_new("");
|
|
||||||
ret = bdrv_open(bs, filename, image_options, flags, NULL, errp);
|
|
||||||
if (ret < 0) {
|
|
||||||
bdrv_unref(bs);
|
|
||||||
} else {
|
|
||||||
*pbs = bs;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = bdrv_file_open(pbs, filename, reference, image_options, flags,
|
|
||||||
errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
qdict_del(options, bdref_key);
|
qdict_del(options, bdref_key);
|
||||||
@@ -1222,17 +1164,58 @@ done:
|
|||||||
* empty set of options. The reference to the QDict belongs to the block layer
|
* empty set of options. The reference to the QDict belongs to the block layer
|
||||||
* after the call (even on failure), so if the caller intends to reuse the
|
* after the call (even on failure), so if the caller intends to reuse the
|
||||||
* dictionary, it needs to use QINCREF() before calling bdrv_open.
|
* dictionary, it needs to use QINCREF() before calling bdrv_open.
|
||||||
|
*
|
||||||
|
* If *pbs is NULL, a new BDS will be created with a pointer to it stored there.
|
||||||
|
* If it is not NULL, the referenced BDS will be reused.
|
||||||
|
*
|
||||||
|
* The reference parameter may be used to specify an existing block device which
|
||||||
|
* should be opened. If specified, neither options nor a filename may be given,
|
||||||
|
* nor can an existing BDS be reused (that is, *pbs has to be NULL).
|
||||||
*/
|
*/
|
||||||
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||||
int flags, BlockDriver *drv, Error **errp)
|
const char *reference, QDict *options, int flags,
|
||||||
|
BlockDriver *drv, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
/* 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[PATH_MAX + 1];
|
char tmp_filename[PATH_MAX + 1];
|
||||||
BlockDriverState *file = NULL;
|
BlockDriverState *file = NULL, *bs;
|
||||||
const char *drvname;
|
const char *drvname;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
assert(pbs);
|
||||||
|
|
||||||
|
if (reference) {
|
||||||
|
bool options_non_empty = options ? qdict_size(options) : false;
|
||||||
|
QDECREF(options);
|
||||||
|
|
||||||
|
if (*pbs) {
|
||||||
|
error_setg(errp, "Cannot reuse an existing BDS when referencing "
|
||||||
|
"another block device");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename || options_non_empty) {
|
||||||
|
error_setg(errp, "Cannot reference an existing block device with "
|
||||||
|
"additional options or a new filename");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bs = bdrv_lookup_bs(reference, reference, errp);
|
||||||
|
if (!bs) {
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
bdrv_ref(bs);
|
||||||
|
*pbs = bs;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pbs) {
|
||||||
|
bs = *pbs;
|
||||||
|
} else {
|
||||||
|
bs = bdrv_new("");
|
||||||
|
}
|
||||||
|
|
||||||
/* NULL means an empty set of options */
|
/* NULL means an empty set of options */
|
||||||
if (options == NULL) {
|
if (options == NULL) {
|
||||||
options = qdict_new();
|
options = qdict_new();
|
||||||
@@ -1241,6 +1224,19 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||||||
bs->options = options;
|
bs->options = options;
|
||||||
options = qdict_clone_shallow(options);
|
options = qdict_clone_shallow(options);
|
||||||
|
|
||||||
|
if (flags & BDRV_O_PROTOCOL) {
|
||||||
|
assert(!drv);
|
||||||
|
ret = bdrv_file_open(bs, filename, &options, flags & ~BDRV_O_PROTOCOL,
|
||||||
|
&local_err);
|
||||||
|
if (!ret) {
|
||||||
|
goto done;
|
||||||
|
} else if (bs->drv) {
|
||||||
|
goto close_and_fail;
|
||||||
|
} else {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* For snapshot=on, create a temporary qcow2 overlay */
|
/* For snapshot=on, create a temporary qcow2 overlay */
|
||||||
if (flags & BDRV_O_SNAPSHOT) {
|
if (flags & BDRV_O_SNAPSHOT) {
|
||||||
BlockDriverState *bs1;
|
BlockDriverState *bs1;
|
||||||
@@ -1253,12 +1249,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||||||
instead of opening 'filename' directly */
|
instead of opening 'filename' directly */
|
||||||
|
|
||||||
/* Get the required size from the image */
|
/* Get the required size from the image */
|
||||||
bs1 = bdrv_new("");
|
|
||||||
QINCREF(options);
|
QINCREF(options);
|
||||||
ret = bdrv_open(bs1, filename, options, BDRV_O_NO_BACKING,
|
bs1 = NULL;
|
||||||
|
ret = bdrv_open(&bs1, filename, NULL, options, BDRV_O_NO_BACKING,
|
||||||
drv, &local_err);
|
drv, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_unref(bs1);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
|
total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
|
||||||
@@ -1315,9 +1310,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||||||
flags |= BDRV_O_ALLOW_RDWR;
|
flags |= BDRV_O_ALLOW_RDWR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(file == NULL);
|
||||||
ret = bdrv_open_image(&file, filename, options, "file",
|
ret = bdrv_open_image(&file, filename, options, "file",
|
||||||
bdrv_open_flags(bs, flags | BDRV_O_UNMAP), true, true,
|
bdrv_open_flags(bs, flags | BDRV_O_UNMAP) |
|
||||||
&local_err);
|
BDRV_O_PROTOCOL, true, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1370,12 +1366,18 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
/* Check if any unknown options were used */
|
/* Check if any unknown options were used */
|
||||||
if (qdict_size(options) != 0) {
|
if (options && (qdict_size(options) != 0)) {
|
||||||
const QDictEntry *entry = qdict_first(options);
|
const QDictEntry *entry = qdict_first(options);
|
||||||
error_setg(errp, "Block format '%s' used by device '%s' doesn't "
|
if (flags & BDRV_O_PROTOCOL) {
|
||||||
"support the option '%s'", drv->format_name, bs->device_name,
|
error_setg(errp, "Block protocol '%s' doesn't support the option "
|
||||||
entry->key);
|
"'%s'", drv->format_name, entry->key);
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "Block format '%s' used by device '%s' doesn't "
|
||||||
|
"support the option '%s'", drv->format_name,
|
||||||
|
bs->device_name, entry->key);
|
||||||
|
}
|
||||||
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto close_and_fail;
|
goto close_and_fail;
|
||||||
@@ -1386,6 +1388,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||||||
bdrv_dev_change_media_cb(bs, true);
|
bdrv_dev_change_media_cb(bs, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*pbs = bs;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
unlink_and_fail:
|
unlink_and_fail:
|
||||||
@@ -1399,15 +1402,26 @@ fail:
|
|||||||
QDECREF(bs->options);
|
QDECREF(bs->options);
|
||||||
QDECREF(options);
|
QDECREF(options);
|
||||||
bs->options = NULL;
|
bs->options = NULL;
|
||||||
if (error_is_set(&local_err)) {
|
if (!*pbs) {
|
||||||
|
/* If *pbs is NULL, a new BDS has been created in this function and
|
||||||
|
needs to be freed now. Otherwise, it does not need to be closed,
|
||||||
|
since it has not really been opened yet. */
|
||||||
|
bdrv_unref(bs);
|
||||||
|
}
|
||||||
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
close_and_fail:
|
close_and_fail:
|
||||||
bdrv_close(bs);
|
/* See fail path, but now the BDS has to be always closed */
|
||||||
|
if (*pbs) {
|
||||||
|
bdrv_close(bs);
|
||||||
|
} else {
|
||||||
|
bdrv_unref(bs);
|
||||||
|
}
|
||||||
QDECREF(options);
|
QDECREF(options);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2239,11 +2253,11 @@ static void tracked_request_begin(BdrvTrackedRequest *req,
|
|||||||
QLIST_INSERT_HEAD(&bs->tracked_requests, req, list);
|
QLIST_INSERT_HEAD(&bs->tracked_requests, req, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_request_serialising(BdrvTrackedRequest *req, size_t align)
|
static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
||||||
{
|
{
|
||||||
int64_t overlap_offset = req->offset & ~(align - 1);
|
int64_t overlap_offset = req->offset & ~(align - 1);
|
||||||
int overlap_bytes = ROUND_UP(req->offset + req->bytes, align)
|
unsigned int overlap_bytes = ROUND_UP(req->offset + req->bytes, align)
|
||||||
- overlap_offset;
|
- overlap_offset;
|
||||||
|
|
||||||
if (!req->serialising) {
|
if (!req->serialising) {
|
||||||
req->bs->serialising_in_flight++;
|
req->bs->serialising_in_flight++;
|
||||||
@@ -2914,8 +2928,8 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
total_sectors = DIV_ROUND_UP(len, BDRV_SECTOR_SIZE);
|
total_sectors = DIV_ROUND_UP(len, BDRV_SECTOR_SIZE);
|
||||||
max_nb_sectors = MAX(0, ROUND_UP(total_sectors - sector_num,
|
max_nb_sectors = ROUND_UP(MAX(0, total_sectors - sector_num),
|
||||||
align >> BDRV_SECTOR_BITS));
|
align >> BDRV_SECTOR_BITS);
|
||||||
if (max_nb_sectors > 0) {
|
if (max_nb_sectors > 0) {
|
||||||
ret = drv->bdrv_co_readv(bs, sector_num,
|
ret = drv->bdrv_co_readv(bs, sector_num,
|
||||||
MIN(nb_sectors, max_nb_sectors), qiov);
|
MIN(nb_sectors, max_nb_sectors), qiov);
|
||||||
@@ -3133,6 +3147,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
|
|||||||
|
|
||||||
waited = wait_serialising_requests(req);
|
waited = wait_serialising_requests(req);
|
||||||
assert(!waited || !req->serialising);
|
assert(!waited || !req->serialising);
|
||||||
|
assert(req->overlap_offset <= offset);
|
||||||
|
assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
|
||||||
|
|
||||||
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
|
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
|
||||||
|
|
||||||
@@ -3278,9 +3294,9 @@ fail:
|
|||||||
|
|
||||||
if (use_local_qiov) {
|
if (use_local_qiov) {
|
||||||
qemu_iovec_destroy(&local_qiov);
|
qemu_iovec_destroy(&local_qiov);
|
||||||
qemu_vfree(head_buf);
|
|
||||||
qemu_vfree(tail_buf);
|
|
||||||
}
|
}
|
||||||
|
qemu_vfree(head_buf);
|
||||||
|
qemu_vfree(tail_buf);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -3571,30 +3587,26 @@ BlockDriverState *bdrv_lookup_bs(const char *device,
|
|||||||
{
|
{
|
||||||
BlockDriverState *bs = NULL;
|
BlockDriverState *bs = NULL;
|
||||||
|
|
||||||
if ((!device && !node_name) || (device && node_name)) {
|
|
||||||
error_setg(errp, "Use either device or node-name but not both");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device) {
|
if (device) {
|
||||||
bs = bdrv_find(device);
|
bs = bdrv_find(device);
|
||||||
|
|
||||||
if (!bs) {
|
if (bs) {
|
||||||
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
return bs;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = bdrv_find_node(node_name);
|
if (node_name) {
|
||||||
|
bs = bdrv_find_node(node_name);
|
||||||
|
|
||||||
if (!bs) {
|
if (bs) {
|
||||||
error_set(errp, QERR_DEVICE_NOT_FOUND, node_name);
|
return bs;
|
||||||
return NULL;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bs;
|
error_setg(errp, "Cannot find device=%s nor node_name=%s",
|
||||||
|
device ? device : "",
|
||||||
|
node_name ? node_name : "");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDriverState *bdrv_next(BlockDriverState *bs)
|
BlockDriverState *bdrv_next(BlockDriverState *bs)
|
||||||
@@ -5285,9 +5297,8 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
|||||||
back_flags =
|
back_flags =
|
||||||
flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||||
|
|
||||||
bs = bdrv_new("");
|
bs = NULL;
|
||||||
|
ret = bdrv_open(&bs, backing_file->value.s, NULL, NULL, back_flags,
|
||||||
ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags,
|
|
||||||
backing_drv, &local_err);
|
backing_drv, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not open '%s': %s",
|
error_setg_errno(errp, -ret, "Could not open '%s': %s",
|
||||||
@@ -5295,7 +5306,6 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
|||||||
error_get_pretty(local_err));
|
error_get_pretty(local_err));
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
local_err = NULL;
|
local_err = NULL;
|
||||||
bdrv_unref(bs);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
bdrv_get_geometry(bs, &size);
|
bdrv_get_geometry(bs, &size);
|
||||||
@@ -5335,7 +5345,7 @@ out:
|
|||||||
free_option_parameters(create_options);
|
free_option_parameters(create_options);
|
||||||
free_option_parameters(param);
|
free_option_parameters(param);
|
||||||
|
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5411,11 +5421,7 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate)
|
|||||||
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
|
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
|
||||||
bool perm;
|
bool perm;
|
||||||
|
|
||||||
if (!bs->file) {
|
perm = bdrv_recurse_is_first_non_filter(bs, candidate);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
perm = bdrv_recurse_is_first_non_filter(bs->file, candidate);
|
|
||||||
|
|
||||||
/* candidate is the first non filter */
|
/* candidate is the first non filter */
|
||||||
if (perm) {
|
if (perm) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c
|
|||||||
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||||
block-obj-y += qed-check.o
|
block-obj-y += qed-check.o
|
||||||
block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
|
block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
|
||||||
|
block-obj-$(CONFIG_QUORUM) += quorum.o
|
||||||
block-obj-y += parallels.o blkdebug.o blkverify.o
|
block-obj-y += parallels.o blkdebug.o blkverify.o
|
||||||
block-obj-y += snapshot.o qapi.o
|
block-obj-y += snapshot.o qapi.o
|
||||||
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||||
@@ -12,6 +13,7 @@ block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
|||||||
ifeq ($(CONFIG_POSIX),y)
|
ifeq ($(CONFIG_POSIX),y)
|
||||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||||
|
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
||||||
block-obj-$(CONFIG_CURL) += curl.o
|
block-obj-$(CONFIG_CURL) += curl.o
|
||||||
block-obj-$(CONFIG_RBD) += rbd.o
|
block-obj-$(CONFIG_RBD) += rbd.o
|
||||||
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
||||||
@@ -23,4 +25,15 @@ common-obj-y += commit.o
|
|||||||
common-obj-y += mirror.o
|
common-obj-y += mirror.o
|
||||||
common-obj-y += backup.o
|
common-obj-y += backup.o
|
||||||
|
|
||||||
$(obj)/curl.o: QEMU_CFLAGS+=$(CURL_CFLAGS)
|
iscsi.o-cflags := $(LIBISCSI_CFLAGS)
|
||||||
|
iscsi.o-libs := $(LIBISCSI_LIBS)
|
||||||
|
curl.o-cflags := $(CURL_CFLAGS)
|
||||||
|
curl.o-libs := $(CURL_LIBS)
|
||||||
|
rbd.o-cflags := $(RBD_CFLAGS)
|
||||||
|
rbd.o-libs := $(RBD_LIBS)
|
||||||
|
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
|
||||||
|
gluster.o-libs := $(GLUSTERFS_LIBS)
|
||||||
|
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
||||||
|
ssh.o-libs := $(LIBSSH2_LIBS)
|
||||||
|
qcow.o-libs := -lz
|
||||||
|
linux-aio.o-libs := -laio
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
qemu_config_parse_qdict(options, config_groups, &local_err);
|
qemu_config_parse_qdict(options, config_groups, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -393,28 +393,29 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read rules from config file or command line options */
|
/* Read rules from config file or command line options */
|
||||||
config = qemu_opt_get(opts, "config");
|
config = qemu_opt_get(opts, "config");
|
||||||
ret = read_config(s, config, options, errp);
|
ret = read_config(s, config, options, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set initial state */
|
/* Set initial state */
|
||||||
s->state = 1;
|
s->state = 1;
|
||||||
|
|
||||||
/* Open the backing file */
|
/* Open the backing file */
|
||||||
|
assert(bs->file == NULL);
|
||||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
|
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
|
||||||
flags, true, false, &local_err);
|
flags | BDRV_O_PROTOCOL, false, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set request alignment */
|
/* Set request alignment */
|
||||||
@@ -424,11 +425,15 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Invalid alignment");
|
error_setg(errp, "Invalid alignment");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail_unref;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
goto out;
|
||||||
|
|
||||||
|
fail_unref:
|
||||||
|
bdrv_unref(bs->file);
|
||||||
|
out:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,23 +128,25 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open the raw file */
|
/* Open the raw file */
|
||||||
|
assert(bs->file == NULL);
|
||||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
|
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
|
||||||
"raw", flags, true, false, &local_err);
|
"raw", flags | BDRV_O_PROTOCOL, false, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open the test file */
|
/* Open the test file */
|
||||||
|
assert(s->test_file == NULL);
|
||||||
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
|
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
|
||||||
"test", flags, false, false, &local_err);
|
"test", flags, false, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
s->test_file = NULL;
|
s->test_file = NULL;
|
||||||
@@ -171,110 +173,6 @@ static int64_t blkverify_getlength(BlockDriverState *bs)
|
|||||||
return bdrv_getlength(s->test_file);
|
return bdrv_getlength(s->test_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that I/O vector contents are identical
|
|
||||||
*
|
|
||||||
* @a: I/O vector
|
|
||||||
* @b: I/O vector
|
|
||||||
* @ret: Offset to first mismatching byte or -1 if match
|
|
||||||
*/
|
|
||||||
static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
ssize_t offset = 0;
|
|
||||||
|
|
||||||
assert(a->niov == b->niov);
|
|
||||||
for (i = 0; i < a->niov; i++) {
|
|
||||||
size_t len = 0;
|
|
||||||
uint8_t *p = (uint8_t *)a->iov[i].iov_base;
|
|
||||||
uint8_t *q = (uint8_t *)b->iov[i].iov_base;
|
|
||||||
|
|
||||||
assert(a->iov[i].iov_len == b->iov[i].iov_len);
|
|
||||||
while (len < a->iov[i].iov_len && *p++ == *q++) {
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += len;
|
|
||||||
|
|
||||||
if (len != a->iov[i].iov_len) {
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int src_index;
|
|
||||||
struct iovec *src_iov;
|
|
||||||
void *dest_base;
|
|
||||||
} IOVectorSortElem;
|
|
||||||
|
|
||||||
static int sortelem_cmp_src_base(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
const IOVectorSortElem *elem_a = a;
|
|
||||||
const IOVectorSortElem *elem_b = b;
|
|
||||||
|
|
||||||
/* Don't overflow */
|
|
||||||
if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
|
|
||||||
return -1;
|
|
||||||
} else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sortelem_cmp_src_index(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
const IOVectorSortElem *elem_a = a;
|
|
||||||
const IOVectorSortElem *elem_b = b;
|
|
||||||
|
|
||||||
return elem_a->src_index - elem_b->src_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy contents of I/O vector
|
|
||||||
*
|
|
||||||
* The relative relationships of overlapping iovecs are preserved. This is
|
|
||||||
* necessary to ensure identical semantics in the cloned I/O vector.
|
|
||||||
*/
|
|
||||||
static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src,
|
|
||||||
void *buf)
|
|
||||||
{
|
|
||||||
IOVectorSortElem sortelems[src->niov];
|
|
||||||
void *last_end;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Sort by source iovecs by base address */
|
|
||||||
for (i = 0; i < src->niov; i++) {
|
|
||||||
sortelems[i].src_index = i;
|
|
||||||
sortelems[i].src_iov = &src->iov[i];
|
|
||||||
}
|
|
||||||
qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
|
|
||||||
|
|
||||||
/* Allocate buffer space taking into account overlapping iovecs */
|
|
||||||
last_end = NULL;
|
|
||||||
for (i = 0; i < src->niov; i++) {
|
|
||||||
struct iovec *cur = sortelems[i].src_iov;
|
|
||||||
ptrdiff_t rewind = 0;
|
|
||||||
|
|
||||||
/* Detect overlap */
|
|
||||||
if (last_end && last_end > cur->iov_base) {
|
|
||||||
rewind = last_end - cur->iov_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortelems[i].dest_base = buf - rewind;
|
|
||||||
buf += cur->iov_len - MIN(rewind, cur->iov_len);
|
|
||||||
last_end = MAX(cur->iov_base + cur->iov_len, last_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sort by source iovec index and build destination iovec */
|
|
||||||
qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
|
|
||||||
for (i = 0; i < src->niov; i++) {
|
|
||||||
qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
||||||
int64_t sector_num, QEMUIOVector *qiov,
|
int64_t sector_num, QEMUIOVector *qiov,
|
||||||
int nb_sectors,
|
int nb_sectors,
|
||||||
@@ -338,7 +236,7 @@ static void blkverify_aio_cb(void *opaque, int ret)
|
|||||||
|
|
||||||
static void blkverify_verify_readv(BlkverifyAIOCB *acb)
|
static void blkverify_verify_readv(BlkverifyAIOCB *acb)
|
||||||
{
|
{
|
||||||
ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
|
ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov);
|
||||||
if (offset != -1) {
|
if (offset != -1) {
|
||||||
blkverify_err(acb, "contents mismatch in sector %" PRId64,
|
blkverify_err(acb, "contents mismatch in sector %" PRId64,
|
||||||
acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
|
acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
|
||||||
@@ -356,7 +254,7 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
|
|||||||
acb->verify = blkverify_verify_readv;
|
acb->verify = blkverify_verify_readv;
|
||||||
acb->buf = qemu_blockalign(bs->file, qiov->size);
|
acb->buf = qemu_blockalign(bs->file, qiov->size);
|
||||||
qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
|
qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
|
||||||
blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
|
qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
|
||||||
|
|
||||||
bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
|
bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
|
||||||
blkverify_aio_cb, acb);
|
blkverify_aio_cb, acb);
|
||||||
|
|||||||
@@ -129,7 +129,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
strcmp(bochs.subtype, GROWING_TYPE) ||
|
strcmp(bochs.subtype, GROWING_TYPE) ||
|
||||||
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
||||||
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
||||||
return -EMEDIUMTYPE;
|
error_setg(errp, "Image not in Bochs format");
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (le32_to_cpu(bochs.version) == HEADER_V1) {
|
if (le32_to_cpu(bochs.version) == HEADER_V1) {
|
||||||
|
|||||||
16
block/cow.c
16
block/cow.c
@@ -74,7 +74,8 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
|
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
|
||||||
ret = -EMEDIUMTYPE;
|
error_setg(errp, "Image not in COW format");
|
||||||
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
char version[64];
|
char version[64];
|
||||||
snprintf(version, sizeof(version),
|
snprintf(version, sizeof(version),
|
||||||
"COW version %d", cow_header.version);
|
"COW version %d", cow_header.version);
|
||||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||||
bs->device_name, "cow", version);
|
bs->device_name, "cow", version);
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -346,16 +347,15 @@ static int cow_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
|
|
||||||
ret = bdrv_create_file(filename, options, &local_err);
|
ret = bdrv_create_file(filename, options, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&cow_bs, filename, NULL, NULL, BDRV_O_RDWR,
|
cow_bs = NULL;
|
||||||
&local_err);
|
ret = bdrv_open(&cow_bs, filename, NULL, NULL,
|
||||||
|
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
block/curl.c
15
block/curl.c
@@ -456,30 +456,27 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
static int inited = 0;
|
static int inited = 0;
|
||||||
|
|
||||||
if (flags & BDRV_O_RDWR) {
|
if (flags & BDRV_O_RDWR) {
|
||||||
qerror_report(ERROR_CLASS_GENERIC_ERROR,
|
error_setg(errp, "curl block device does not support writes");
|
||||||
"curl block device does not support writes");
|
|
||||||
return -EROFS;
|
return -EROFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
|
s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
|
||||||
if ((s->readahead_size & 0x1ff) != 0) {
|
if ((s->readahead_size & 0x1ff) != 0) {
|
||||||
fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
|
error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
|
||||||
s->readahead_size);
|
s->readahead_size);
|
||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
}
|
}
|
||||||
|
|
||||||
file = qemu_opt_get(opts, "url");
|
file = qemu_opt_get(opts, "url");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "curl block driver requires "
|
error_setg(errp, "curl block driver requires an 'url' option");
|
||||||
"an 'url' option");
|
|
||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
173
block/gluster.c
173
block/gluster.c
@@ -45,11 +45,13 @@ typedef struct GlusterConf {
|
|||||||
|
|
||||||
static void qemu_gluster_gconf_free(GlusterConf *gconf)
|
static void qemu_gluster_gconf_free(GlusterConf *gconf)
|
||||||
{
|
{
|
||||||
g_free(gconf->server);
|
if (gconf) {
|
||||||
g_free(gconf->volname);
|
g_free(gconf->server);
|
||||||
g_free(gconf->image);
|
g_free(gconf->volname);
|
||||||
g_free(gconf->transport);
|
g_free(gconf->image);
|
||||||
g_free(gconf);
|
g_free(gconf->transport);
|
||||||
|
g_free(gconf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_volume_options(GlusterConf *gconf, char *path)
|
static int parse_volume_options(GlusterConf *gconf, char *path)
|
||||||
@@ -127,7 +129,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* transport */
|
/* transport */
|
||||||
if (!strcmp(uri->scheme, "gluster")) {
|
if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
|
||||||
gconf->transport = g_strdup("tcp");
|
gconf->transport = g_strdup("tcp");
|
||||||
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
|
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
|
||||||
gconf->transport = g_strdup("tcp");
|
gconf->transport = g_strdup("tcp");
|
||||||
@@ -163,7 +165,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
|
|||||||
}
|
}
|
||||||
gconf->server = g_strdup(qp->p[0].value);
|
gconf->server = g_strdup(qp->p[0].value);
|
||||||
} else {
|
} else {
|
||||||
gconf->server = g_strdup(uri->server);
|
gconf->server = g_strdup(uri->server ? uri->server : "localhost");
|
||||||
gconf->port = uri->port;
|
gconf->port = uri->port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +177,8 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
|
static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
struct glfs *glfs = NULL;
|
struct glfs *glfs = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -183,8 +186,8 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
|
|||||||
|
|
||||||
ret = qemu_gluster_parseuri(gconf, filename);
|
ret = qemu_gluster_parseuri(gconf, filename);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("Usage: file=gluster[+transport]://[server[:port]]/"
|
error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/"
|
||||||
"volname/image[?socket=...]");
|
"volname/image[?socket=...]");
|
||||||
errno = -ret;
|
errno = -ret;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -211,9 +214,11 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
|
|||||||
|
|
||||||
ret = glfs_init(glfs);
|
ret = glfs_init(glfs);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("Gluster connection failed for server=%s port=%d "
|
error_setg_errno(errp, errno,
|
||||||
"volume=%s image=%s transport=%s", gconf->server, gconf->port,
|
"Gluster connection failed for server=%s port=%d "
|
||||||
gconf->volname, gconf->image, gconf->transport);
|
"volume=%s image=%s transport=%s", gconf->server,
|
||||||
|
gconf->port, gconf->volname, gconf->image,
|
||||||
|
gconf->transport);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
return glfs;
|
return glfs;
|
||||||
@@ -269,11 +274,28 @@ static QemuOptsList runtime_opts = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
|
||||||
|
{
|
||||||
|
assert(open_flags != NULL);
|
||||||
|
|
||||||
|
*open_flags |= O_BINARY;
|
||||||
|
|
||||||
|
if (bdrv_flags & BDRV_O_RDWR) {
|
||||||
|
*open_flags |= O_RDWR;
|
||||||
|
} else {
|
||||||
|
*open_flags |= O_RDONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((bdrv_flags & BDRV_O_NOCACHE)) {
|
||||||
|
*open_flags |= O_DIRECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
||||||
int bdrv_flags, Error **errp)
|
int bdrv_flags, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVGlusterState *s = bs->opaque;
|
BDRVGlusterState *s = bs->opaque;
|
||||||
int open_flags = O_BINARY;
|
int open_flags = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
@@ -282,30 +304,21 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
|||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
filename = qemu_opt_get(opts, "filename");
|
filename = qemu_opt_get(opts, "filename");
|
||||||
|
|
||||||
s->glfs = qemu_gluster_init(gconf, filename);
|
s->glfs = qemu_gluster_init(gconf, filename, errp);
|
||||||
if (!s->glfs) {
|
if (!s->glfs) {
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bdrv_flags & BDRV_O_RDWR) {
|
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
|
||||||
open_flags |= O_RDWR;
|
|
||||||
} else {
|
|
||||||
open_flags |= O_RDONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((bdrv_flags & BDRV_O_NOCACHE)) {
|
|
||||||
open_flags |= O_DIRECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->fd = glfs_open(s->glfs, gconf->image, open_flags);
|
s->fd = glfs_open(s->glfs, gconf->image, open_flags);
|
||||||
if (!s->fd) {
|
if (!s->fd) {
|
||||||
@@ -327,6 +340,96 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct BDRVGlusterReopenState {
|
||||||
|
struct glfs *glfs;
|
||||||
|
struct glfs_fd *fd;
|
||||||
|
} BDRVGlusterReopenState;
|
||||||
|
|
||||||
|
|
||||||
|
static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
|
||||||
|
BlockReopenQueue *queue, Error **errp)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
BDRVGlusterReopenState *reop_s;
|
||||||
|
GlusterConf *gconf = NULL;
|
||||||
|
int open_flags = 0;
|
||||||
|
|
||||||
|
assert(state != NULL);
|
||||||
|
assert(state->bs != NULL);
|
||||||
|
|
||||||
|
state->opaque = g_malloc0(sizeof(BDRVGlusterReopenState));
|
||||||
|
reop_s = state->opaque;
|
||||||
|
|
||||||
|
qemu_gluster_parse_flags(state->flags, &open_flags);
|
||||||
|
|
||||||
|
gconf = g_malloc0(sizeof(GlusterConf));
|
||||||
|
|
||||||
|
reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename, errp);
|
||||||
|
if (reop_s->glfs == NULL) {
|
||||||
|
ret = -errno;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
reop_s->fd = glfs_open(reop_s->glfs, gconf->image, open_flags);
|
||||||
|
if (reop_s->fd == NULL) {
|
||||||
|
/* reops->glfs will be cleaned up in _abort */
|
||||||
|
ret = -errno;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
/* state->opaque will be freed in either the _abort or _commit */
|
||||||
|
qemu_gluster_gconf_free(gconf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_gluster_reopen_commit(BDRVReopenState *state)
|
||||||
|
{
|
||||||
|
BDRVGlusterReopenState *reop_s = state->opaque;
|
||||||
|
BDRVGlusterState *s = state->bs->opaque;
|
||||||
|
|
||||||
|
|
||||||
|
/* close the old */
|
||||||
|
if (s->fd) {
|
||||||
|
glfs_close(s->fd);
|
||||||
|
}
|
||||||
|
if (s->glfs) {
|
||||||
|
glfs_fini(s->glfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* use the newly opened image / connection */
|
||||||
|
s->fd = reop_s->fd;
|
||||||
|
s->glfs = reop_s->glfs;
|
||||||
|
|
||||||
|
g_free(state->opaque);
|
||||||
|
state->opaque = NULL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void qemu_gluster_reopen_abort(BDRVReopenState *state)
|
||||||
|
{
|
||||||
|
BDRVGlusterReopenState *reop_s = state->opaque;
|
||||||
|
|
||||||
|
if (reop_s == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reop_s->fd) {
|
||||||
|
glfs_close(reop_s->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reop_s->glfs) {
|
||||||
|
glfs_fini(reop_s->glfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(state->opaque);
|
||||||
|
state->opaque = NULL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
#ifdef CONFIG_GLUSTERFS_ZEROFILL
|
||||||
static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs,
|
static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
||||||
@@ -389,9 +492,9 @@ static int qemu_gluster_create(const char *filename,
|
|||||||
int64_t total_size = 0;
|
int64_t total_size = 0;
|
||||||
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
|
||||||
|
|
||||||
glfs = qemu_gluster_init(gconf, filename);
|
glfs = qemu_gluster_init(gconf, filename, errp);
|
||||||
if (!glfs) {
|
if (!glfs) {
|
||||||
ret = -errno;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,6 +720,9 @@ static BlockDriver bdrv_gluster = {
|
|||||||
.instance_size = sizeof(BDRVGlusterState),
|
.instance_size = sizeof(BDRVGlusterState),
|
||||||
.bdrv_needs_filename = true,
|
.bdrv_needs_filename = true,
|
||||||
.bdrv_file_open = qemu_gluster_open,
|
.bdrv_file_open = qemu_gluster_open,
|
||||||
|
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
|
||||||
|
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||||
|
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||||
.bdrv_close = qemu_gluster_close,
|
.bdrv_close = qemu_gluster_close,
|
||||||
.bdrv_create = qemu_gluster_create,
|
.bdrv_create = qemu_gluster_create,
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
@@ -641,6 +747,9 @@ static BlockDriver bdrv_gluster_tcp = {
|
|||||||
.instance_size = sizeof(BDRVGlusterState),
|
.instance_size = sizeof(BDRVGlusterState),
|
||||||
.bdrv_needs_filename = true,
|
.bdrv_needs_filename = true,
|
||||||
.bdrv_file_open = qemu_gluster_open,
|
.bdrv_file_open = qemu_gluster_open,
|
||||||
|
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
|
||||||
|
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||||
|
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||||
.bdrv_close = qemu_gluster_close,
|
.bdrv_close = qemu_gluster_close,
|
||||||
.bdrv_create = qemu_gluster_create,
|
.bdrv_create = qemu_gluster_create,
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
@@ -665,6 +774,9 @@ static BlockDriver bdrv_gluster_unix = {
|
|||||||
.instance_size = sizeof(BDRVGlusterState),
|
.instance_size = sizeof(BDRVGlusterState),
|
||||||
.bdrv_needs_filename = true,
|
.bdrv_needs_filename = true,
|
||||||
.bdrv_file_open = qemu_gluster_open,
|
.bdrv_file_open = qemu_gluster_open,
|
||||||
|
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
|
||||||
|
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||||
|
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||||
.bdrv_close = qemu_gluster_close,
|
.bdrv_close = qemu_gluster_close,
|
||||||
.bdrv_create = qemu_gluster_create,
|
.bdrv_create = qemu_gluster_create,
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
@@ -689,6 +801,9 @@ static BlockDriver bdrv_gluster_rdma = {
|
|||||||
.instance_size = sizeof(BDRVGlusterState),
|
.instance_size = sizeof(BDRVGlusterState),
|
||||||
.bdrv_needs_filename = true,
|
.bdrv_needs_filename = true,
|
||||||
.bdrv_file_open = qemu_gluster_open,
|
.bdrv_file_open = qemu_gluster_open,
|
||||||
|
.bdrv_reopen_prepare = qemu_gluster_reopen_prepare,
|
||||||
|
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
|
||||||
|
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
|
||||||
.bdrv_close = qemu_gluster_close,
|
.bdrv_close = qemu_gluster_close,
|
||||||
.bdrv_create = qemu_gluster_create,
|
.bdrv_create = qemu_gluster_create,
|
||||||
.bdrv_getlength = qemu_gluster_getlength,
|
.bdrv_getlength = qemu_gluster_getlength,
|
||||||
|
|||||||
280
block/iscsi.c
280
block/iscsi.c
@@ -145,12 +145,13 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
|||||||
|
|
||||||
if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION
|
if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION
|
||||||
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
|
||||||
|
error_report("iSCSI CheckCondition: %s", iscsi_get_error(iscsi));
|
||||||
iTask->do_retry = 1;
|
iTask->do_retry = 1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != SCSI_STATUS_GOOD) {
|
if (status != SCSI_STATUS_GOOD) {
|
||||||
error_report("iSCSI: Failure. %s", iscsi_get_error(iscsi));
|
error_report("iSCSI Failure: %s", iscsi_get_error(iscsi));
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@@ -325,6 +326,7 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (iTask.do_retry) {
|
if (iTask.do_retry) {
|
||||||
|
iTask.complete = 0;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,6 +401,7 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (iTask.do_retry) {
|
if (iTask.do_retry) {
|
||||||
|
iTask.complete = 0;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,6 +436,7 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (iTask.do_retry) {
|
if (iTask.do_retry) {
|
||||||
|
iTask.complete = 0;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,6 +687,7 @@ retry:
|
|||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
iTask.task = NULL;
|
iTask.task = NULL;
|
||||||
}
|
}
|
||||||
|
iTask.complete = 0;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,6 +772,7 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (iTask.do_retry) {
|
if (iTask.do_retry) {
|
||||||
|
iTask.complete = 0;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -830,24 +836,26 @@ retry:
|
|||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
|
||||||
|
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
||||||
|
iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) {
|
||||||
|
/* WRITE SAME is not supported by the target */
|
||||||
|
iscsilun->has_write_same = false;
|
||||||
|
scsi_free_scsi_task(iTask.task);
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
if (iTask.task != NULL) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
iTask.task = NULL;
|
iTask.task = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iTask.do_retry) {
|
if (iTask.do_retry) {
|
||||||
|
iTask.complete = 0;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||||
if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
|
|
||||||
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
|
||||||
iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) {
|
|
||||||
/* WRITE SAME is not supported by the target */
|
|
||||||
iscsilun->has_write_same = false;
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,7 +864,8 @@ retry:
|
|||||||
|
|
||||||
#endif /* SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED */
|
#endif /* SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED */
|
||||||
|
|
||||||
static int parse_chap(struct iscsi_context *iscsi, const char *target)
|
static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QemuOptsList *list;
|
QemuOptsList *list;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
@@ -865,37 +874,35 @@ static int parse_chap(struct iscsi_context *iscsi, const char *target)
|
|||||||
|
|
||||||
list = qemu_find_opts("iscsi");
|
list = qemu_find_opts("iscsi");
|
||||||
if (!list) {
|
if (!list) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = qemu_opts_find(list, target);
|
opts = qemu_opts_find(list, target);
|
||||||
if (opts == NULL) {
|
if (opts == NULL) {
|
||||||
opts = QTAILQ_FIRST(&list->head);
|
opts = QTAILQ_FIRST(&list->head);
|
||||||
if (!opts) {
|
if (!opts) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user = qemu_opt_get(opts, "user");
|
user = qemu_opt_get(opts, "user");
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
password = qemu_opt_get(opts, "password");
|
password = qemu_opt_get(opts, "password");
|
||||||
if (!password) {
|
if (!password) {
|
||||||
error_report("CHAP username specified but no password was given");
|
error_setg(errp, "CHAP username specified but no password was given");
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
||||||
error_report("Failed to set initiator username and password");
|
error_setg(errp, "Failed to set initiator username and password");
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
|
static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QemuOptsList *list;
|
QemuOptsList *list;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
@@ -928,7 +935,7 @@ static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
|
|||||||
} else if (!strcmp(digest, "NONE-CRC32C")) {
|
} else if (!strcmp(digest, "NONE-CRC32C")) {
|
||||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||||
} else {
|
} else {
|
||||||
error_report("Invalid header-digest setting : %s", digest);
|
error_setg(errp, "Invalid header-digest setting : %s", digest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -986,12 +993,11 @@ static void iscsi_nop_timed_event(void *opaque)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
|
static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
|
||||||
{
|
{
|
||||||
struct scsi_task *task = NULL;
|
struct scsi_task *task = NULL;
|
||||||
struct scsi_readcapacity10 *rc10 = NULL;
|
struct scsi_readcapacity10 *rc10 = NULL;
|
||||||
struct scsi_readcapacity16 *rc16 = NULL;
|
struct scsi_readcapacity16 *rc16 = NULL;
|
||||||
int ret = 0;
|
|
||||||
int retries = ISCSI_CMD_RETRIES;
|
int retries = ISCSI_CMD_RETRIES;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@@ -1006,8 +1012,7 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
|
|||||||
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
|
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
|
||||||
rc16 = scsi_datain_unmarshall(task);
|
rc16 = scsi_datain_unmarshall(task);
|
||||||
if (rc16 == NULL) {
|
if (rc16 == NULL) {
|
||||||
error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
|
error_setg(errp, "iSCSI: Failed to unmarshall readcapacity16 data.");
|
||||||
ret = -EINVAL;
|
|
||||||
} else {
|
} else {
|
||||||
iscsilun->block_size = rc16->block_length;
|
iscsilun->block_size = rc16->block_length;
|
||||||
iscsilun->num_blocks = rc16->returned_lba + 1;
|
iscsilun->num_blocks = rc16->returned_lba + 1;
|
||||||
@@ -1021,8 +1026,7 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
|
|||||||
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
|
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
|
||||||
rc10 = scsi_datain_unmarshall(task);
|
rc10 = scsi_datain_unmarshall(task);
|
||||||
if (rc10 == NULL) {
|
if (rc10 == NULL) {
|
||||||
error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
|
error_setg(errp, "iSCSI: Failed to unmarshall readcapacity10 data.");
|
||||||
ret = -EINVAL;
|
|
||||||
} else {
|
} else {
|
||||||
iscsilun->block_size = rc10->block_size;
|
iscsilun->block_size = rc10->block_size;
|
||||||
if (rc10->lba == 0) {
|
if (rc10->lba == 0) {
|
||||||
@@ -1035,20 +1039,18 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
} while (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
|
} while (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
|
||||||
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
||||||
&& retries-- > 0);
|
&& retries-- > 0);
|
||||||
|
|
||||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||||
error_report("iSCSI: failed to send readcapacity10 command.");
|
error_setg(errp, "iSCSI: failed to send readcapacity10 command.");
|
||||||
ret = -EINVAL;
|
|
||||||
}
|
}
|
||||||
if (task) {
|
if (task) {
|
||||||
scsi_free_scsi_task(task);
|
scsi_free_scsi_task(task);
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Convert to fine grained options */
|
/* TODO Convert to fine grained options */
|
||||||
@@ -1065,40 +1067,50 @@ static QemuOptsList runtime_opts = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi,
|
static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun,
|
||||||
int lun, int evpd, int pc) {
|
int evpd, int pc, void **inq, Error **errp)
|
||||||
int full_size;
|
{
|
||||||
struct scsi_task *task = NULL;
|
int full_size;
|
||||||
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
|
struct scsi_task *task = NULL;
|
||||||
|
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
|
||||||
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
full_size = scsi_datain_getfullsize(task);
|
||||||
|
if (full_size > task->datain.size) {
|
||||||
|
scsi_free_scsi_task(task);
|
||||||
|
|
||||||
|
/* we need more data for the full list */
|
||||||
|
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
|
||||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
full_size = scsi_datain_getfullsize(task);
|
}
|
||||||
if (full_size > task->datain.size) {
|
|
||||||
scsi_free_scsi_task(task);
|
|
||||||
|
|
||||||
/* we need more data for the full list */
|
*inq = scsi_datain_unmarshall(task);
|
||||||
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
|
if (*inq == NULL) {
|
||||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
error_report("iSCSI: Inquiry command failed : %s",
|
error_setg(errp, "iSCSI: Inquiry command failed : %s",
|
||||||
iscsi_get_error(iscsi));
|
iscsi_get_error(iscsi));
|
||||||
if (task) {
|
if (task != NULL) {
|
||||||
scsi_free_scsi_task(task);
|
scsi_free_scsi_task(task);
|
||||||
return NULL;
|
}
|
||||||
}
|
return NULL;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We support iscsi url's on the form
|
* We support iscsi url's on the form
|
||||||
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
|
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
|
||||||
|
*
|
||||||
|
* Note: flags are currently not used by iscsi_open. If this function
|
||||||
|
* is changed such that flags are used, please examine iscsi_reopen_prepare()
|
||||||
|
* to see if needs to be changed as well.
|
||||||
*/
|
*/
|
||||||
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
@@ -1108,34 +1120,33 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
struct iscsi_url *iscsi_url = NULL;
|
struct iscsi_url *iscsi_url = NULL;
|
||||||
struct scsi_task *task = NULL;
|
struct scsi_task *task = NULL;
|
||||||
struct scsi_inquiry_standard *inq = NULL;
|
struct scsi_inquiry_standard *inq = NULL;
|
||||||
|
struct scsi_inquiry_supported_pages *inq_vpd;
|
||||||
char *initiator_name = NULL;
|
char *initiator_name = NULL;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
int ret;
|
int i, ret;
|
||||||
|
|
||||||
if ((BDRV_SECTOR_SIZE % 512) != 0) {
|
if ((BDRV_SECTOR_SIZE % 512) != 0) {
|
||||||
error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "
|
error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. "
|
||||||
"BDRV_SECTOR_SIZE(%lld) is not a multiple "
|
"BDRV_SECTOR_SIZE(%lld) is not a multiple "
|
||||||
"of 512", BDRV_SECTOR_SIZE);
|
"of 512", BDRV_SECTOR_SIZE);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
filename = qemu_opt_get(opts, "filename");
|
filename = qemu_opt_get(opts, "filename");
|
||||||
|
|
||||||
|
|
||||||
iscsi_url = iscsi_parse_full_url(iscsi, filename);
|
iscsi_url = iscsi_parse_full_url(iscsi, filename);
|
||||||
if (iscsi_url == NULL) {
|
if (iscsi_url == NULL) {
|
||||||
error_report("Failed to parse URL : %s", filename);
|
error_setg(errp, "Failed to parse URL : %s", filename);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -1146,13 +1157,13 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
iscsi = iscsi_create_context(initiator_name);
|
iscsi = iscsi_create_context(initiator_name);
|
||||||
if (iscsi == NULL) {
|
if (iscsi == NULL) {
|
||||||
error_report("iSCSI: Failed to create iSCSI context.");
|
error_setg(errp, "iSCSI: Failed to create iSCSI context.");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
|
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
|
||||||
error_report("iSCSI: Failed to set target name.");
|
error_setg(errp, "iSCSI: Failed to set target name.");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -1161,21 +1172,22 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
|
ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
|
||||||
iscsi_url->passwd);
|
iscsi_url->passwd);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
error_report("Failed to set initiator username and password");
|
error_setg(errp, "Failed to set initiator username and password");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if we got CHAP username/password via the options */
|
/* check if we got CHAP username/password via the options */
|
||||||
if (parse_chap(iscsi, iscsi_url->target) != 0) {
|
parse_chap(iscsi, iscsi_url->target, &local_err);
|
||||||
error_report("iSCSI: Failed to set CHAP user/password");
|
if (local_err != NULL) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
|
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
|
||||||
error_report("iSCSI: Failed to set session type to normal.");
|
error_setg(errp, "iSCSI: Failed to set session type to normal.");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -1183,10 +1195,15 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||||
|
|
||||||
/* check if we got HEADER_DIGEST via the options */
|
/* check if we got HEADER_DIGEST via the options */
|
||||||
parse_header_digest(iscsi, iscsi_url->target);
|
parse_header_digest(iscsi, iscsi_url->target, &local_err);
|
||||||
|
if (local_err != NULL) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
|
if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
|
||||||
error_report("iSCSI: Failed to connect to LUN : %s",
|
error_setg(errp, "iSCSI: Failed to connect to LUN : %s",
|
||||||
iscsi_get_error(iscsi));
|
iscsi_get_error(iscsi));
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -1194,26 +1211,21 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
iscsilun->iscsi = iscsi;
|
iscsilun->iscsi = iscsi;
|
||||||
iscsilun->lun = iscsi_url->lun;
|
iscsilun->lun = iscsi_url->lun;
|
||||||
|
|
||||||
task = iscsi_inquiry_sync(iscsi, iscsilun->lun, 0, 0, 36);
|
|
||||||
|
|
||||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
||||||
error_report("iSCSI: failed to send inquiry command.");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
inq = scsi_datain_unmarshall(task);
|
|
||||||
if (inq == NULL) {
|
|
||||||
error_report("iSCSI: Failed to unmarshall inquiry data.");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
iscsilun->type = inq->periperal_device_type;
|
|
||||||
iscsilun->has_write_same = true;
|
iscsilun->has_write_same = true;
|
||||||
|
|
||||||
if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
|
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 0, 0,
|
||||||
|
(void **) &inq, errp);
|
||||||
|
if (task == NULL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
iscsilun->type = inq->periperal_device_type;
|
||||||
|
scsi_free_scsi_task(task);
|
||||||
|
task = NULL;
|
||||||
|
|
||||||
|
iscsi_readcapacity_sync(iscsilun, &local_err);
|
||||||
|
if (local_err != NULL) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
|
bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
|
||||||
@@ -1228,45 +1240,48 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
bs->sg = 1;
|
bs->sg = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iscsilun->lbpme) {
|
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||||
|
SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES,
|
||||||
|
(void **) &inq_vpd, errp);
|
||||||
|
if (task == NULL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
for (i = 0; i < inq_vpd->num_pages; i++) {
|
||||||
|
struct scsi_task *inq_task;
|
||||||
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
||||||
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
|
||||||
SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING);
|
|
||||||
if (task == NULL) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
inq_lbp = scsi_datain_unmarshall(task);
|
|
||||||
if (inq_lbp == NULL) {
|
|
||||||
error_report("iSCSI: failed to unmarshall inquiry datain blob");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
memcpy(&iscsilun->lbp, inq_lbp,
|
|
||||||
sizeof(struct scsi_inquiry_logical_block_provisioning));
|
|
||||||
scsi_free_scsi_task(task);
|
|
||||||
task = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) {
|
|
||||||
struct scsi_inquiry_block_limits *inq_bl;
|
struct scsi_inquiry_block_limits *inq_bl;
|
||||||
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
switch (inq_vpd->pages[i]) {
|
||||||
SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS);
|
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
||||||
if (task == NULL) {
|
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||||
ret = -EINVAL;
|
SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING,
|
||||||
goto out;
|
(void **) &inq_lbp, errp);
|
||||||
|
if (inq_task == NULL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
memcpy(&iscsilun->lbp, inq_lbp,
|
||||||
|
sizeof(struct scsi_inquiry_logical_block_provisioning));
|
||||||
|
scsi_free_scsi_task(inq_task);
|
||||||
|
break;
|
||||||
|
case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS:
|
||||||
|
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||||
|
SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS,
|
||||||
|
(void **) &inq_bl, errp);
|
||||||
|
if (inq_task == NULL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
memcpy(&iscsilun->bl, inq_bl,
|
||||||
|
sizeof(struct scsi_inquiry_block_limits));
|
||||||
|
scsi_free_scsi_task(inq_task);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
inq_bl = scsi_datain_unmarshall(task);
|
|
||||||
if (inq_bl == NULL) {
|
|
||||||
error_report("iSCSI: failed to unmarshall inquiry datain blob");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
memcpy(&iscsilun->bl, inq_bl,
|
|
||||||
sizeof(struct scsi_inquiry_block_limits));
|
|
||||||
scsi_free_scsi_task(task);
|
|
||||||
task = NULL;
|
|
||||||
}
|
}
|
||||||
|
scsi_free_scsi_task(task);
|
||||||
|
task = NULL;
|
||||||
|
|
||||||
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
|
||||||
/* Set up a timer for sending out iSCSI NOPs */
|
/* Set up a timer for sending out iSCSI NOPs */
|
||||||
@@ -1330,32 +1345,35 @@ static int iscsi_refresh_limits(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
|
bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
|
||||||
iscsilun);
|
iscsilun);
|
||||||
|
|
||||||
bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
|
|
||||||
iscsilun);
|
|
||||||
}
|
}
|
||||||
|
bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
|
||||||
|
iscsilun);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We have nothing to do for iSCSI reopen, stub just returns
|
/* Since iscsi_open() ignores bdrv_flags, there is nothing to do here in
|
||||||
* success */
|
* prepare. Note that this will not re-establish a connection with an iSCSI
|
||||||
|
* target - it is effectively a NOP. */
|
||||||
static int iscsi_reopen_prepare(BDRVReopenState *state,
|
static int iscsi_reopen_prepare(BDRVReopenState *state,
|
||||||
BlockReopenQueue *queue, Error **errp)
|
BlockReopenQueue *queue, Error **errp)
|
||||||
{
|
{
|
||||||
|
/* NOP */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
{
|
{
|
||||||
IscsiLun *iscsilun = bs->opaque;
|
IscsiLun *iscsilun = bs->opaque;
|
||||||
int ret = 0;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
if (iscsilun->type != TYPE_DISK) {
|
if (iscsilun->type != TYPE_DISK) {
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
|
iscsi_readcapacity_sync(iscsilun, &local_err);
|
||||||
return ret;
|
if (local_err != NULL) {
|
||||||
|
error_free(local_err);
|
||||||
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset > iscsi_getlength(bs)) {
|
if (offset > iscsi_getlength(bs)) {
|
||||||
|
|||||||
@@ -633,6 +633,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
|||||||
{
|
{
|
||||||
int64_t length, base_length;
|
int64_t length, base_length;
|
||||||
int orig_base_flags;
|
int orig_base_flags;
|
||||||
|
int ret;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
orig_base_flags = bdrv_get_flags(base);
|
orig_base_flags = bdrv_get_flags(base);
|
||||||
|
|
||||||
@@ -642,19 +644,23 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
|||||||
|
|
||||||
length = bdrv_getlength(bs);
|
length = bdrv_getlength(bs);
|
||||||
if (length < 0) {
|
if (length < 0) {
|
||||||
error_setg(errp, "Unable to determine length of %s", bs->filename);
|
error_setg_errno(errp, -length,
|
||||||
|
"Unable to determine length of %s", bs->filename);
|
||||||
goto error_restore_flags;
|
goto error_restore_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
base_length = bdrv_getlength(base);
|
base_length = bdrv_getlength(base);
|
||||||
if (base_length < 0) {
|
if (base_length < 0) {
|
||||||
error_setg(errp, "Unable to determine length of %s", base->filename);
|
error_setg_errno(errp, -base_length,
|
||||||
|
"Unable to determine length of %s", base->filename);
|
||||||
goto error_restore_flags;
|
goto error_restore_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length > base_length) {
|
if (length > base_length) {
|
||||||
if (bdrv_truncate(base, length) < 0) {
|
ret = bdrv_truncate(base, length);
|
||||||
error_setg(errp, "Top image %s is larger than base image %s, and "
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret,
|
||||||
|
"Top image %s is larger than base image %s, and "
|
||||||
"resize of base image failed",
|
"resize of base image failed",
|
||||||
bs->filename, base->filename);
|
bs->filename, base->filename);
|
||||||
goto error_restore_flags;
|
goto error_restore_flags;
|
||||||
@@ -663,9 +669,10 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
|||||||
|
|
||||||
bdrv_ref(base);
|
bdrv_ref(base);
|
||||||
mirror_start_job(bs, base, speed, 0, 0,
|
mirror_start_job(bs, base, speed, 0, 0,
|
||||||
on_error, on_error, cb, opaque, errp,
|
on_error, on_error, cb, opaque, &local_err,
|
||||||
&commit_active_job_driver, false, base);
|
&commit_active_job_driver, false, base);
|
||||||
if (error_is_set(errp)) {
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
goto error_restore_flags;
|
goto error_restore_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
45
block/nbd.c
45
block/nbd.c
@@ -188,31 +188,28 @@ out:
|
|||||||
g_free(file);
|
g_free(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_config(BDRVNBDState *s, QDict *options, char **export)
|
static void nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
if (qdict_haskey(options, "path")) {
|
if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) {
|
||||||
if (qdict_haskey(options, "host")) {
|
if (qdict_haskey(options, "path")) {
|
||||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "path and host may not "
|
error_setg(errp, "path and host may not be used at the same time.");
|
||||||
"be used at the same time.");
|
} else {
|
||||||
return -EINVAL;
|
error_setg(errp, "one of path and host must be specified.");
|
||||||
}
|
}
|
||||||
s->client.is_unix = true;
|
return;
|
||||||
} else if (qdict_haskey(options, "host")) {
|
|
||||||
s->client.is_unix = false;
|
|
||||||
} else {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->client.is_unix = qdict_haskey(options, "path");
|
||||||
s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
|
s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
|
|
||||||
qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
|
qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
return;
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!qemu_opt_get(s->socket_opts, "port")) {
|
if (!qemu_opt_get(s->socket_opts, "port")) {
|
||||||
@@ -223,19 +220,17 @@ static int nbd_config(BDRVNBDState *s, QDict *options, char **export)
|
|||||||
if (*export) {
|
if (*export) {
|
||||||
qdict_del(options, "export");
|
qdict_del(options, "export");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_establish_connection(BlockDriverState *bs)
|
static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVNBDState *s = bs->opaque;
|
BDRVNBDState *s = bs->opaque;
|
||||||
int sock;
|
int sock;
|
||||||
|
|
||||||
if (s->client.is_unix) {
|
if (s->client.is_unix) {
|
||||||
sock = unix_socket_outgoing(qemu_opt_get(s->socket_opts, "path"));
|
sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL);
|
||||||
} else {
|
} else {
|
||||||
sock = tcp_socket_outgoing_opts(s->socket_opts);
|
sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL);
|
||||||
if (sock >= 0) {
|
if (sock >= 0) {
|
||||||
socket_set_nodelay(sock);
|
socket_set_nodelay(sock);
|
||||||
}
|
}
|
||||||
@@ -256,17 +251,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
BDRVNBDState *s = bs->opaque;
|
BDRVNBDState *s = bs->opaque;
|
||||||
char *export = NULL;
|
char *export = NULL;
|
||||||
int result, sock;
|
int result, sock;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
/* Pop the config into our state object. Exit if invalid. */
|
/* Pop the config into our state object. Exit if invalid. */
|
||||||
result = nbd_config(s, options, &export);
|
nbd_config(s, options, &export, &local_err);
|
||||||
if (result != 0) {
|
if (local_err) {
|
||||||
return result;
|
error_propagate(errp, local_err);
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* establish TCP connection, return error if it fails
|
/* establish TCP connection, return error if it fails
|
||||||
* TODO: Configurable retry-until-timeout behaviour.
|
* TODO: Configurable retry-until-timeout behaviour.
|
||||||
*/
|
*/
|
||||||
sock = nbd_establish_connection(bs);
|
sock = nbd_establish_connection(bs, errp);
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|||||||
439
block/nfs.c
Normal file
439
block/nfs.c
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
/*
|
||||||
|
* QEMU Block driver for native access to files on NFS shares
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Peter Lieven <pl@kamp.de>
|
||||||
|
*
|
||||||
|
* 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 "config-host.h"
|
||||||
|
|
||||||
|
#include <poll.h>
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/config-file.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "block/block_int.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "qemu/iov.h"
|
||||||
|
#include "qemu/uri.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include <nfsc/libnfs.h>
|
||||||
|
|
||||||
|
typedef struct NFSClient {
|
||||||
|
struct nfs_context *context;
|
||||||
|
struct nfsfh *fh;
|
||||||
|
int events;
|
||||||
|
bool has_zero_init;
|
||||||
|
} NFSClient;
|
||||||
|
|
||||||
|
typedef struct NFSRPC {
|
||||||
|
int ret;
|
||||||
|
int complete;
|
||||||
|
QEMUIOVector *iov;
|
||||||
|
struct stat *st;
|
||||||
|
Coroutine *co;
|
||||||
|
QEMUBH *bh;
|
||||||
|
} NFSRPC;
|
||||||
|
|
||||||
|
static void nfs_process_read(void *arg);
|
||||||
|
static void nfs_process_write(void *arg);
|
||||||
|
|
||||||
|
static void nfs_set_events(NFSClient *client)
|
||||||
|
{
|
||||||
|
int ev = nfs_which_events(client->context);
|
||||||
|
if (ev != client->events) {
|
||||||
|
qemu_aio_set_fd_handler(nfs_get_fd(client->context),
|
||||||
|
(ev & POLLIN) ? nfs_process_read : NULL,
|
||||||
|
(ev & POLLOUT) ? nfs_process_write : NULL,
|
||||||
|
client);
|
||||||
|
|
||||||
|
}
|
||||||
|
client->events = ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfs_process_read(void *arg)
|
||||||
|
{
|
||||||
|
NFSClient *client = arg;
|
||||||
|
nfs_service(client->context, POLLIN);
|
||||||
|
nfs_set_events(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfs_process_write(void *arg)
|
||||||
|
{
|
||||||
|
NFSClient *client = arg;
|
||||||
|
nfs_service(client->context, POLLOUT);
|
||||||
|
nfs_set_events(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfs_co_init_task(NFSClient *client, NFSRPC *task)
|
||||||
|
{
|
||||||
|
*task = (NFSRPC) {
|
||||||
|
.co = qemu_coroutine_self(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfs_co_generic_bh_cb(void *opaque)
|
||||||
|
{
|
||||||
|
NFSRPC *task = opaque;
|
||||||
|
qemu_bh_delete(task->bh);
|
||||||
|
qemu_coroutine_enter(task->co, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
|
||||||
|
void *private_data)
|
||||||
|
{
|
||||||
|
NFSRPC *task = private_data;
|
||||||
|
task->complete = 1;
|
||||||
|
task->ret = ret;
|
||||||
|
if (task->ret > 0 && task->iov) {
|
||||||
|
if (task->ret <= task->iov->size) {
|
||||||
|
qemu_iovec_from_buf(task->iov, 0, data, task->ret);
|
||||||
|
} else {
|
||||||
|
task->ret = -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (task->ret == 0 && task->st) {
|
||||||
|
memcpy(task->st, data, sizeof(struct stat));
|
||||||
|
}
|
||||||
|
if (task->co) {
|
||||||
|
task->bh = qemu_bh_new(nfs_co_generic_bh_cb, task);
|
||||||
|
qemu_bh_schedule(task->bh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn nfs_co_readv(BlockDriverState *bs,
|
||||||
|
int64_t sector_num, int nb_sectors,
|
||||||
|
QEMUIOVector *iov)
|
||||||
|
{
|
||||||
|
NFSClient *client = bs->opaque;
|
||||||
|
NFSRPC task;
|
||||||
|
|
||||||
|
nfs_co_init_task(client, &task);
|
||||||
|
task.iov = iov;
|
||||||
|
|
||||||
|
if (nfs_pread_async(client->context, client->fh,
|
||||||
|
sector_num * BDRV_SECTOR_SIZE,
|
||||||
|
nb_sectors * BDRV_SECTOR_SIZE,
|
||||||
|
nfs_co_generic_cb, &task) != 0) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!task.complete) {
|
||||||
|
nfs_set_events(client);
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.ret < 0) {
|
||||||
|
return task.ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* zero pad short reads */
|
||||||
|
if (task.ret < iov->size) {
|
||||||
|
qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn nfs_co_writev(BlockDriverState *bs,
|
||||||
|
int64_t sector_num, int nb_sectors,
|
||||||
|
QEMUIOVector *iov)
|
||||||
|
{
|
||||||
|
NFSClient *client = bs->opaque;
|
||||||
|
NFSRPC task;
|
||||||
|
char *buf = NULL;
|
||||||
|
|
||||||
|
nfs_co_init_task(client, &task);
|
||||||
|
|
||||||
|
buf = g_malloc(nb_sectors * BDRV_SECTOR_SIZE);
|
||||||
|
qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
|
if (nfs_pwrite_async(client->context, client->fh,
|
||||||
|
sector_num * BDRV_SECTOR_SIZE,
|
||||||
|
nb_sectors * BDRV_SECTOR_SIZE,
|
||||||
|
buf, nfs_co_generic_cb, &task) != 0) {
|
||||||
|
g_free(buf);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!task.complete) {
|
||||||
|
nfs_set_events(client);
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(buf);
|
||||||
|
|
||||||
|
if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) {
|
||||||
|
return task.ret < 0 ? task.ret : -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
NFSClient *client = bs->opaque;
|
||||||
|
NFSRPC task;
|
||||||
|
|
||||||
|
nfs_co_init_task(client, &task);
|
||||||
|
|
||||||
|
if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb,
|
||||||
|
&task) != 0) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!task.complete) {
|
||||||
|
nfs_set_events(client);
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO Convert to fine grained options */
|
||||||
|
static QemuOptsList runtime_opts = {
|
||||||
|
.name = "nfs",
|
||||||
|
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||||
|
.desc = {
|
||||||
|
{
|
||||||
|
.name = "filename",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "URL to the NFS file",
|
||||||
|
},
|
||||||
|
{ /* end of list */ }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void nfs_client_close(NFSClient *client)
|
||||||
|
{
|
||||||
|
if (client->context) {
|
||||||
|
if (client->fh) {
|
||||||
|
nfs_close(client->context, client->fh);
|
||||||
|
}
|
||||||
|
qemu_aio_set_fd_handler(nfs_get_fd(client->context), NULL, NULL, NULL);
|
||||||
|
nfs_destroy_context(client->context);
|
||||||
|
}
|
||||||
|
memset(client, 0, sizeof(NFSClient));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfs_file_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
NFSClient *client = bs->opaque;
|
||||||
|
nfs_client_close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
||||||
|
int flags, Error **errp)
|
||||||
|
{
|
||||||
|
int ret = -EINVAL, i;
|
||||||
|
struct stat st;
|
||||||
|
URI *uri;
|
||||||
|
QueryParams *qp = NULL;
|
||||||
|
char *file = NULL, *strp = NULL;
|
||||||
|
|
||||||
|
uri = uri_parse(filename);
|
||||||
|
if (!uri) {
|
||||||
|
error_setg(errp, "Invalid URL specified");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
strp = strrchr(uri->path, '/');
|
||||||
|
if (strp == NULL) {
|
||||||
|
error_setg(errp, "Invalid URL specified");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
file = g_strdup(strp);
|
||||||
|
*strp = 0;
|
||||||
|
|
||||||
|
client->context = nfs_init_context();
|
||||||
|
if (client->context == NULL) {
|
||||||
|
error_setg(errp, "Failed to init NFS context");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
qp = query_params_parse(uri->query);
|
||||||
|
for (i = 0; i < qp->n; i++) {
|
||||||
|
if (!qp->p[i].value) {
|
||||||
|
error_setg(errp, "Value for NFS parameter expected: %s",
|
||||||
|
qp->p[i].name);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!strncmp(qp->p[i].name, "uid", 3)) {
|
||||||
|
nfs_set_uid(client->context, atoi(qp->p[i].value));
|
||||||
|
} else if (!strncmp(qp->p[i].name, "gid", 3)) {
|
||||||
|
nfs_set_gid(client->context, atoi(qp->p[i].value));
|
||||||
|
} else if (!strncmp(qp->p[i].name, "tcp-syncnt", 10)) {
|
||||||
|
nfs_set_tcp_syncnt(client->context, atoi(qp->p[i].value));
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "Unknown NFS parameter name: %s",
|
||||||
|
qp->p[i].name);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nfs_mount(client->context, uri->server, uri->path);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "Failed to mount nfs share: %s",
|
||||||
|
nfs_get_error(client->context));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & O_CREAT) {
|
||||||
|
ret = nfs_creat(client->context, file, 0600, &client->fh);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "Failed to create file: %s",
|
||||||
|
nfs_get_error(client->context));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = nfs_open(client->context, file, flags, &client->fh);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "Failed to open file : %s",
|
||||||
|
nfs_get_error(client->context));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nfs_fstat(client->context, client->fh, &st);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "Failed to fstat file: %s",
|
||||||
|
nfs_get_error(client->context));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE);
|
||||||
|
client->has_zero_init = S_ISREG(st.st_mode);
|
||||||
|
goto out;
|
||||||
|
fail:
|
||||||
|
nfs_client_close(client);
|
||||||
|
out:
|
||||||
|
if (qp) {
|
||||||
|
query_params_free(qp);
|
||||||
|
}
|
||||||
|
uri_free(uri);
|
||||||
|
g_free(file);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
Error **errp) {
|
||||||
|
NFSClient *client = bs->opaque;
|
||||||
|
int64_t ret;
|
||||||
|
QemuOpts *opts;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
ret = nfs_client_open(client, qemu_opt_get(opts, "filename"),
|
||||||
|
(flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
|
||||||
|
errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
bs->total_sectors = ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nfs_file_create(const char *url, QEMUOptionParameter *options,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int64_t total_size = 0;
|
||||||
|
NFSClient *client = g_malloc0(sizeof(NFSClient));
|
||||||
|
|
||||||
|
/* Read out options */
|
||||||
|
while (options && options->name) {
|
||||||
|
if (!strcmp(options->name, "size")) {
|
||||||
|
total_size = options->value.n;
|
||||||
|
}
|
||||||
|
options++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nfs_client_open(client, url, O_CREAT, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = nfs_ftruncate(client->context, client->fh, total_size);
|
||||||
|
nfs_client_close(client);
|
||||||
|
out:
|
||||||
|
g_free(client);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nfs_has_zero_init(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
NFSClient *client = bs->opaque;
|
||||||
|
return client->has_zero_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
NFSClient *client = bs->opaque;
|
||||||
|
NFSRPC task = {0};
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
task.st = &st;
|
||||||
|
if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb,
|
||||||
|
&task) != 0) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!task.complete) {
|
||||||
|
nfs_set_events(client);
|
||||||
|
qemu_aio_wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nfs_file_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
|
{
|
||||||
|
NFSClient *client = bs->opaque;
|
||||||
|
return nfs_ftruncate(client->context, client->fh, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockDriver bdrv_nfs = {
|
||||||
|
.format_name = "nfs",
|
||||||
|
.protocol_name = "nfs",
|
||||||
|
|
||||||
|
.instance_size = sizeof(NFSClient),
|
||||||
|
.bdrv_needs_filename = true,
|
||||||
|
.bdrv_has_zero_init = nfs_has_zero_init,
|
||||||
|
.bdrv_get_allocated_file_size = nfs_get_allocated_file_size,
|
||||||
|
.bdrv_truncate = nfs_file_truncate,
|
||||||
|
|
||||||
|
.bdrv_file_open = nfs_file_open,
|
||||||
|
.bdrv_close = nfs_file_close,
|
||||||
|
.bdrv_create = nfs_file_create,
|
||||||
|
|
||||||
|
.bdrv_co_readv = nfs_co_readv,
|
||||||
|
.bdrv_co_writev = nfs_co_writev,
|
||||||
|
.bdrv_co_flush_to_disk = nfs_co_flush,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void nfs_block_init(void)
|
||||||
|
{
|
||||||
|
bdrv_register(&bdrv_nfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_init(nfs_block_init);
|
||||||
@@ -85,7 +85,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
|
if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
|
||||||
(le32_to_cpu(ph.version) != HEADER_VERSION)) {
|
(le32_to_cpu(ph.version) != HEADER_VERSION)) {
|
||||||
ret = -EMEDIUMTYPE;
|
error_setg(errp, "Image not in Parallels format");
|
||||||
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ void bdrv_query_info(BlockDriverState *bs,
|
|||||||
p_image_info = &info->inserted->image;
|
p_image_info = &info->inserted->image;
|
||||||
while (1) {
|
while (1) {
|
||||||
bdrv_query_image_info(bs0, p_image_info, &local_err);
|
bdrv_query_image_info(bs0, p_image_info, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@@ -336,7 +336,7 @@ BlockInfoList *qmp_query_block(Error **errp)
|
|||||||
while ((bs = bdrv_next(bs))) {
|
while ((bs = bdrv_next(bs))) {
|
||||||
BlockInfoList *info = g_malloc0(sizeof(*info));
|
BlockInfoList *info = g_malloc0(sizeof(*info));
|
||||||
bdrv_query_info(bs, &info->value, &local_err);
|
bdrv_query_info(bs, &info->value, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|||||||
20
block/qcow.c
20
block/qcow.c
@@ -113,23 +113,26 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
be64_to_cpus(&header.l1_table_offset);
|
be64_to_cpus(&header.l1_table_offset);
|
||||||
|
|
||||||
if (header.magic != QCOW_MAGIC) {
|
if (header.magic != QCOW_MAGIC) {
|
||||||
ret = -EMEDIUMTYPE;
|
error_setg(errp, "Image not in qcow format");
|
||||||
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (header.version != QCOW_VERSION) {
|
if (header.version != QCOW_VERSION) {
|
||||||
char version[64];
|
char version[64];
|
||||||
snprintf(version, sizeof(version), "QCOW version %d", header.version);
|
snprintf(version, sizeof(version), "QCOW version %d", header.version);
|
||||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||||
bs->device_name, "qcow", version);
|
bs->device_name, "qcow", version);
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.size <= 1 || header.cluster_bits < 9) {
|
if (header.size <= 1 || header.cluster_bits < 9) {
|
||||||
|
error_setg(errp, "invalid value in qcow header");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (header.crypt_method > QCOW_CRYPT_AES) {
|
if (header.crypt_method > QCOW_CRYPT_AES) {
|
||||||
|
error_setg(errp, "invalid encryption method in qcow header");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -686,16 +689,15 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
|
|
||||||
ret = bdrv_create_file(filename, options, &local_err);
|
ret = bdrv_create_file(filename, options, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&qcow_bs, filename, NULL, NULL, BDRV_O_RDWR,
|
qcow_bs = NULL;
|
||||||
&local_err);
|
ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
|
||||||
|
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1182,7 +1182,7 @@ fail:
|
|||||||
* Return 0 on success and -errno in error cases
|
* Return 0 on success and -errno in error cases
|
||||||
*/
|
*/
|
||||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m)
|
int *num, uint64_t *host_offset, QCowL2Meta **m)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
uint64_t start, remaining;
|
uint64_t start, remaining;
|
||||||
@@ -1190,15 +1190,13 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
uint64_t cur_bytes;
|
uint64_t cur_bytes;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset,
|
trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *num);
|
||||||
n_start, n_end);
|
|
||||||
|
|
||||||
assert(n_start * BDRV_SECTOR_SIZE == offset_into_cluster(s, offset));
|
assert((offset & ~BDRV_SECTOR_MASK) == 0);
|
||||||
offset = start_of_cluster(s, offset);
|
|
||||||
|
|
||||||
again:
|
again:
|
||||||
start = offset + (n_start << BDRV_SECTOR_BITS);
|
start = offset;
|
||||||
remaining = (n_end - n_start) << BDRV_SECTOR_BITS;
|
remaining = *num << BDRV_SECTOR_BITS;
|
||||||
cluster_offset = 0;
|
cluster_offset = 0;
|
||||||
*host_offset = 0;
|
*host_offset = 0;
|
||||||
cur_bytes = 0;
|
cur_bytes = 0;
|
||||||
@@ -1284,7 +1282,7 @@ again:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*num = (n_end - n_start) - (remaining >> BDRV_SECTOR_BITS);
|
*num -= remaining >> BDRV_SECTOR_BITS;
|
||||||
assert(*num > 0);
|
assert(*num > 0);
|
||||||
assert(*host_offset != 0);
|
assert(*host_offset != 0);
|
||||||
|
|
||||||
@@ -1369,13 +1367,31 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
|||||||
uint64_t old_offset;
|
uint64_t old_offset;
|
||||||
|
|
||||||
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
old_offset = be64_to_cpu(l2_table[l2_index + i]);
|
||||||
if ((old_offset & L2E_OFFSET_MASK) == 0) {
|
|
||||||
|
/*
|
||||||
|
* Make sure that a discarded area reads back as zeroes for v3 images
|
||||||
|
* (we cannot do it for v2 without actually writing a zero-filled
|
||||||
|
* buffer). We can skip the operation if the cluster is already marked
|
||||||
|
* as zero, or if it's unallocated and we don't have a backing file.
|
||||||
|
*
|
||||||
|
* TODO We might want to use bdrv_get_block_status(bs) here, but we're
|
||||||
|
* holding s->lock, so that doesn't work today.
|
||||||
|
*/
|
||||||
|
if (old_offset & QCOW_OFLAG_ZERO) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((old_offset & L2E_OFFSET_MASK) == 0 && !bs->backing_hd) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First remove L2 entries */
|
/* First remove L2 entries */
|
||||||
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
||||||
l2_table[l2_index + i] = cpu_to_be64(0);
|
if (s->qcow_version >= 3) {
|
||||||
|
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
|
||||||
|
} else {
|
||||||
|
l2_table[l2_index + i] = cpu_to_be64(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Then decrease the refcount */
|
/* Then decrease the refcount */
|
||||||
qcow2_free_any_clusters(bs, old_offset, 1, type);
|
qcow2_free_any_clusters(bs, old_offset, 1, type);
|
||||||
|
|||||||
@@ -676,7 +676,13 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
|||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
uint64_t cluster_index;
|
uint64_t cluster_index;
|
||||||
uint64_t old_free_cluster_index;
|
uint64_t old_free_cluster_index;
|
||||||
int i, refcount, ret;
|
uint64_t i;
|
||||||
|
int refcount, ret;
|
||||||
|
|
||||||
|
assert(nb_clusters >= 0);
|
||||||
|
if (nb_clusters == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check how many clusters there are free */
|
/* Check how many clusters there are free */
|
||||||
cluster_index = offset >> s->cluster_bits;
|
cluster_index = offset >> s->cluster_bits;
|
||||||
|
|||||||
@@ -606,7 +606,8 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
|||||||
s->nb_snapshots--;
|
s->nb_snapshots--;
|
||||||
ret = qcow2_write_snapshots(bs);
|
ret = qcow2_write_snapshots(bs);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Failed to remove snapshot from snapshot list");
|
error_setg_errno(errp, -ret,
|
||||||
|
"Failed to remove snapshot from snapshot list");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +625,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
|||||||
ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
|
ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
|
||||||
sn.l1_size, -1);
|
sn.l1_size, -1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Failed to free the cluster and L1 table");
|
error_setg_errno(errp, -ret, "Failed to free the cluster and L1 table");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t),
|
qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t),
|
||||||
@@ -633,7 +634,8 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
|||||||
/* must update the copied flag on the current cluster offsets */
|
/* must update the copied flag on the current cluster offsets */
|
||||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
|
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Failed to update snapshot status in disk");
|
error_setg_errno(errp, -ret,
|
||||||
|
"Failed to update snapshot status in disk");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
if (header.magic != QCOW_MAGIC) {
|
if (header.magic != QCOW_MAGIC) {
|
||||||
error_setg(errp, "Image is not in qcow2 format");
|
error_setg(errp, "Image is not in qcow2 format");
|
||||||
ret = -EMEDIUMTYPE;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (header.version < 2 || header.version > 3) {
|
if (header.version < 2 || header.version > 3) {
|
||||||
@@ -671,7 +671,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
/* Enable lazy_refcounts according to image and command line options */
|
/* Enable lazy_refcounts according to image and command line options */
|
||||||
opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -1000,7 +1000,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
|||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
int index_in_cluster;
|
int index_in_cluster;
|
||||||
int n_end;
|
|
||||||
int ret;
|
int ret;
|
||||||
int cur_nr_sectors; /* number of sectors in current iteration */
|
int cur_nr_sectors; /* number of sectors in current iteration */
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
@@ -1024,14 +1023,16 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
|||||||
|
|
||||||
trace_qcow2_writev_start_part(qemu_coroutine_self());
|
trace_qcow2_writev_start_part(qemu_coroutine_self());
|
||||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||||
n_end = index_in_cluster + remaining_sectors;
|
cur_nr_sectors = remaining_sectors;
|
||||||
if (s->crypt_method &&
|
if (s->crypt_method &&
|
||||||
n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) {
|
cur_nr_sectors >
|
||||||
n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors;
|
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster) {
|
||||||
|
cur_nr_sectors =
|
||||||
|
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qcow2_alloc_cluster_offset(bs, sector_num << 9,
|
ret = qcow2_alloc_cluster_offset(bs, sector_num << 9,
|
||||||
index_in_cluster, n_end, &cur_nr_sectors, &cluster_offset, &l2meta);
|
&cur_nr_sectors, &cluster_offset, &l2meta);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1403,34 +1404,34 @@ static int preallocate(BlockDriverState *bs)
|
|||||||
int ret;
|
int ret;
|
||||||
QCowL2Meta *meta;
|
QCowL2Meta *meta;
|
||||||
|
|
||||||
nb_sectors = bdrv_getlength(bs) >> 9;
|
nb_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
while (nb_sectors) {
|
while (nb_sectors) {
|
||||||
num = MIN(nb_sectors, INT_MAX >> 9);
|
num = MIN(nb_sectors, INT_MAX >> BDRV_SECTOR_BITS);
|
||||||
ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num,
|
ret = qcow2_alloc_cluster_offset(bs, offset, &num,
|
||||||
&host_offset, &meta);
|
&host_offset, &meta);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qcow2_alloc_cluster_link_l2(bs, meta);
|
|
||||||
if (ret < 0) {
|
|
||||||
qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters,
|
|
||||||
QCOW2_DISCARD_NEVER);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* There are no dependent requests, but we need to remove our request
|
|
||||||
* from the list of in-flight requests */
|
|
||||||
if (meta != NULL) {
|
if (meta != NULL) {
|
||||||
|
ret = qcow2_alloc_cluster_link_l2(bs, meta);
|
||||||
|
if (ret < 0) {
|
||||||
|
qcow2_free_any_clusters(bs, meta->alloc_offset,
|
||||||
|
meta->nb_clusters, QCOW2_DISCARD_NEVER);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There are no dependent requests, but we need to remove our
|
||||||
|
* request from the list of in-flight requests */
|
||||||
QLIST_REMOVE(meta, next_in_flight);
|
QLIST_REMOVE(meta, next_in_flight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Preallocate data if requested */
|
/* TODO Preallocate data if requested */
|
||||||
|
|
||||||
nb_sectors -= num;
|
nb_sectors -= num;
|
||||||
offset += num << 9;
|
offset += num << BDRV_SECTOR_BITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1439,9 +1440,10 @@ static int preallocate(BlockDriverState *bs)
|
|||||||
* EOF). Extend the image to the last allocated sector.
|
* EOF). Extend the image to the last allocated sector.
|
||||||
*/
|
*/
|
||||||
if (host_offset != 0) {
|
if (host_offset != 0) {
|
||||||
uint8_t buf[512];
|
uint8_t buf[BDRV_SECTOR_SIZE];
|
||||||
memset(buf, 0, 512);
|
memset(buf, 0, BDRV_SECTOR_SIZE);
|
||||||
ret = bdrv_write(bs->file, (host_offset >> 9) + num - 1, buf, 1);
|
ret = bdrv_write(bs->file, (host_offset >> BDRV_SECTOR_BITS) + num - 1,
|
||||||
|
buf, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1491,7 +1493,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
bs = NULL;
|
||||||
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
|
NULL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1541,7 +1545,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_close(bs);
|
bdrv_unref(bs);
|
||||||
|
bs = 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
|
||||||
@@ -1550,7 +1555,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
*/
|
*/
|
||||||
BlockDriver* drv = bdrv_find_format("qcow2");
|
BlockDriver* drv = bdrv_find_format("qcow2");
|
||||||
assert(drv != NULL);
|
assert(drv != NULL);
|
||||||
ret = bdrv_open(bs, filename, NULL,
|
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err);
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
@@ -1597,20 +1602,23 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_close(bs);
|
bdrv_unref(bs);
|
||||||
|
bs = 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 */
|
||||||
ret = bdrv_open(bs, filename, NULL,
|
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
||||||
drv, &local_err);
|
drv, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
bdrv_unref(bs);
|
if (bs) {
|
||||||
|
bdrv_unref(bs);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1683,32 +1691,12 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
|
|
||||||
ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
|
ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
|
||||||
cluster_size, prealloc, options, version, &local_err);
|
cluster_size, prealloc, options, version, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcow2_make_empty(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
/* XXX: not correct */
|
|
||||||
BDRVQcowState *s = bs->opaque;
|
|
||||||
uint32_t l1_length = s->l1_size * sizeof(uint64_t);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
memset(s->l1_table, 0, l1_length);
|
|
||||||
if (bdrv_pwrite(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0)
|
|
||||||
return -1;
|
|
||||||
ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
l2_cache_reset(bs);
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
|
static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
||||||
{
|
{
|
||||||
@@ -2252,7 +2240,6 @@ static BlockDriver bdrv_qcow2 = {
|
|||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_get_block_status = qcow2_co_get_block_status,
|
.bdrv_co_get_block_status = qcow2_co_get_block_status,
|
||||||
.bdrv_set_key = qcow2_set_key,
|
.bdrv_set_key = qcow2_set_key,
|
||||||
.bdrv_make_empty = qcow2_make_empty,
|
|
||||||
|
|
||||||
.bdrv_co_readv = qcow2_co_readv,
|
.bdrv_co_readv = qcow2_co_readv,
|
||||||
.bdrv_co_writev = qcow2_co_writev,
|
.bdrv_co_writev = qcow2_co_writev,
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
|||||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int *num, uint64_t *cluster_offset);
|
int *num, uint64_t *cluster_offset);
|
||||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m);
|
int *num, uint64_t *host_offset, QCowL2Meta **m);
|
||||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
int compressed_size);
|
int compressed_size);
|
||||||
|
|||||||
30
block/qed.c
30
block/qed.c
@@ -391,14 +391,15 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
qed_header_le_to_cpu(&le_header, &s->header);
|
qed_header_le_to_cpu(&le_header, &s->header);
|
||||||
|
|
||||||
if (s->header.magic != QED_MAGIC) {
|
if (s->header.magic != QED_MAGIC) {
|
||||||
return -EMEDIUMTYPE;
|
error_setg(errp, "Image not in QED format");
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (s->header.features & ~QED_FEATURE_MASK) {
|
if (s->header.features & ~QED_FEATURE_MASK) {
|
||||||
/* image uses unsupported feature bits */
|
/* image uses unsupported feature bits */
|
||||||
char buf[64];
|
char buf[64];
|
||||||
snprintf(buf, sizeof(buf), "%" PRIx64,
|
snprintf(buf, sizeof(buf), "%" PRIx64,
|
||||||
s->header.features & ~QED_FEATURE_MASK);
|
s->header.features & ~QED_FEATURE_MASK);
|
||||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||||
bs->device_name, "QED", buf);
|
bs->device_name, "QED", buf);
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
@@ -545,7 +546,8 @@ static void bdrv_qed_close(BlockDriverState *bs)
|
|||||||
|
|
||||||
static int qed_create(const char *filename, uint32_t cluster_size,
|
static int qed_create(const char *filename, uint32_t cluster_size,
|
||||||
uint64_t image_size, uint32_t table_size,
|
uint64_t image_size, uint32_t table_size,
|
||||||
const char *backing_file, const char *backing_fmt)
|
const char *backing_file, const char *backing_fmt,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QEDHeader header = {
|
QEDHeader header = {
|
||||||
.magic = QED_MAGIC,
|
.magic = QED_MAGIC,
|
||||||
@@ -562,20 +564,20 @@ 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 = NULL;
|
BlockDriverState *bs;
|
||||||
|
|
||||||
ret = bdrv_create_file(filename, NULL, &local_err);
|
ret = bdrv_create_file(filename, NULL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, NULL,
|
bs = NULL;
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB, &local_err);
|
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||||
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL,
|
||||||
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,7 +667,7 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return qed_create(filename, cluster_size, image_size, table_size,
|
return qed_create(filename, cluster_size, image_size, table_size,
|
||||||
backing_file, backing_fmt);
|
backing_file, backing_fmt, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -731,11 +733,6 @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
|
|||||||
return cb.status;
|
return cb.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bdrv_qed_make_empty(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
|
static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
|
||||||
{
|
{
|
||||||
return acb->common.bs->opaque;
|
return acb->common.bs->opaque;
|
||||||
@@ -1617,7 +1614,6 @@ static BlockDriver bdrv_qed = {
|
|||||||
.bdrv_create = bdrv_qed_create,
|
.bdrv_create = bdrv_qed_create,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
|
.bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
|
||||||
.bdrv_make_empty = bdrv_qed_make_empty,
|
|
||||||
.bdrv_aio_readv = bdrv_qed_aio_readv,
|
.bdrv_aio_readv = bdrv_qed_aio_readv,
|
||||||
.bdrv_aio_writev = bdrv_qed_aio_writev,
|
.bdrv_aio_writev = bdrv_qed_aio_writev,
|
||||||
.bdrv_co_write_zeroes = bdrv_qed_co_write_zeroes,
|
.bdrv_co_write_zeroes = bdrv_qed_co_write_zeroes,
|
||||||
|
|||||||
873
block/quorum.c
Normal file
873
block/quorum.c
Normal file
@@ -0,0 +1,873 @@
|
|||||||
|
/*
|
||||||
|
* Quorum Block filter
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2014 Nodalink, EURL.
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Benoît Canet <benoit.canet@irqsave.net>
|
||||||
|
*
|
||||||
|
* Based on the design and code of blkverify.c (Copyright (C) 2010 IBM, Corp)
|
||||||
|
* and blkmirror.c (Copyright (C) 2011 Red Hat, Inc).
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
#include <gnutls/crypto.h>
|
||||||
|
#include "block/block_int.h"
|
||||||
|
#include "qapi/qmp/qjson.h"
|
||||||
|
|
||||||
|
#define HASH_LENGTH 32
|
||||||
|
|
||||||
|
#define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold"
|
||||||
|
#define QUORUM_OPT_BLKVERIFY "blkverify"
|
||||||
|
|
||||||
|
/* This union holds a vote hash value */
|
||||||
|
typedef union QuorumVoteValue {
|
||||||
|
char h[HASH_LENGTH]; /* SHA-256 hash */
|
||||||
|
int64_t l; /* simpler 64 bits hash */
|
||||||
|
} QuorumVoteValue;
|
||||||
|
|
||||||
|
/* A vote item */
|
||||||
|
typedef struct QuorumVoteItem {
|
||||||
|
int index;
|
||||||
|
QLIST_ENTRY(QuorumVoteItem) next;
|
||||||
|
} QuorumVoteItem;
|
||||||
|
|
||||||
|
/* this structure is a vote version. A version is the set of votes sharing the
|
||||||
|
* same vote value.
|
||||||
|
* The set of votes will be tracked with the items field and its cardinality is
|
||||||
|
* vote_count.
|
||||||
|
*/
|
||||||
|
typedef struct QuorumVoteVersion {
|
||||||
|
QuorumVoteValue value;
|
||||||
|
int index;
|
||||||
|
int vote_count;
|
||||||
|
QLIST_HEAD(, QuorumVoteItem) items;
|
||||||
|
QLIST_ENTRY(QuorumVoteVersion) next;
|
||||||
|
} QuorumVoteVersion;
|
||||||
|
|
||||||
|
/* this structure holds a group of vote versions together */
|
||||||
|
typedef struct QuorumVotes {
|
||||||
|
QLIST_HEAD(, QuorumVoteVersion) vote_list;
|
||||||
|
bool (*compare)(QuorumVoteValue *a, QuorumVoteValue *b);
|
||||||
|
} QuorumVotes;
|
||||||
|
|
||||||
|
/* the following structure holds the state of one quorum instance */
|
||||||
|
typedef struct BDRVQuorumState {
|
||||||
|
BlockDriverState **bs; /* children BlockDriverStates */
|
||||||
|
int num_children; /* children count */
|
||||||
|
int threshold; /* if less than threshold children reads gave the
|
||||||
|
* same result a quorum error occurs.
|
||||||
|
*/
|
||||||
|
bool is_blkverify; /* true if the driver is in blkverify mode
|
||||||
|
* Writes are mirrored on two children devices.
|
||||||
|
* On reads the two children devices' contents are
|
||||||
|
* compared and if a difference is spotted its
|
||||||
|
* location is printed and the code aborts.
|
||||||
|
* It is useful to debug other block drivers by
|
||||||
|
* comparing them with a reference one.
|
||||||
|
*/
|
||||||
|
} BDRVQuorumState;
|
||||||
|
|
||||||
|
typedef struct QuorumAIOCB QuorumAIOCB;
|
||||||
|
|
||||||
|
/* Quorum will create one instance of the following structure per operation it
|
||||||
|
* performs on its children.
|
||||||
|
* So for each read/write operation coming from the upper layer there will be
|
||||||
|
* $children_count QuorumChildRequest.
|
||||||
|
*/
|
||||||
|
typedef struct QuorumChildRequest {
|
||||||
|
BlockDriverAIOCB *aiocb;
|
||||||
|
QEMUIOVector qiov;
|
||||||
|
uint8_t *buf;
|
||||||
|
int ret;
|
||||||
|
QuorumAIOCB *parent;
|
||||||
|
} QuorumChildRequest;
|
||||||
|
|
||||||
|
/* Quorum will use the following structure to track progress of each read/write
|
||||||
|
* operation received by the upper layer.
|
||||||
|
* This structure hold pointers to the QuorumChildRequest structures instances
|
||||||
|
* used to do operations on each children and track overall progress.
|
||||||
|
*/
|
||||||
|
struct QuorumAIOCB {
|
||||||
|
BlockDriverAIOCB common;
|
||||||
|
|
||||||
|
/* Request metadata */
|
||||||
|
uint64_t sector_num;
|
||||||
|
int nb_sectors;
|
||||||
|
|
||||||
|
QEMUIOVector *qiov; /* calling IOV */
|
||||||
|
|
||||||
|
QuorumChildRequest *qcrs; /* individual child requests */
|
||||||
|
int count; /* number of completed AIOCB */
|
||||||
|
int success_count; /* number of successfully completed AIOCB */
|
||||||
|
|
||||||
|
QuorumVotes votes;
|
||||||
|
|
||||||
|
bool is_read;
|
||||||
|
int vote_ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void quorum_vote(QuorumAIOCB *acb);
|
||||||
|
|
||||||
|
static void quorum_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||||
|
{
|
||||||
|
QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common);
|
||||||
|
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* cancel all callbacks */
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
bdrv_aio_cancel(acb->qcrs[i].aiocb);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(acb->qcrs);
|
||||||
|
qemu_aio_release(acb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AIOCBInfo quorum_aiocb_info = {
|
||||||
|
.aiocb_size = sizeof(QuorumAIOCB),
|
||||||
|
.cancel = quorum_aio_cancel,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void quorum_aio_finalize(QuorumAIOCB *acb)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||||
|
int i, ret = 0;
|
||||||
|
|
||||||
|
if (acb->vote_ret) {
|
||||||
|
ret = acb->vote_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
acb->common.cb(acb->common.opaque, ret);
|
||||||
|
|
||||||
|
if (acb->is_read) {
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
qemu_vfree(acb->qcrs[i].buf);
|
||||||
|
qemu_iovec_destroy(&acb->qcrs[i].qiov);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(acb->qcrs);
|
||||||
|
qemu_aio_release(acb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||||
|
{
|
||||||
|
return !memcmp(a->h, b->h, HASH_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||||
|
{
|
||||||
|
return a->l == b->l;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
||||||
|
BlockDriverState *bs,
|
||||||
|
QEMUIOVector *qiov,
|
||||||
|
uint64_t sector_num,
|
||||||
|
int nb_sectors,
|
||||||
|
BlockDriverCompletionFunc *cb,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
acb->common.bs->opaque = s;
|
||||||
|
acb->sector_num = sector_num;
|
||||||
|
acb->nb_sectors = nb_sectors;
|
||||||
|
acb->qiov = qiov;
|
||||||
|
acb->qcrs = g_new0(QuorumChildRequest, s->num_children);
|
||||||
|
acb->count = 0;
|
||||||
|
acb->success_count = 0;
|
||||||
|
acb->votes.compare = quorum_sha256_compare;
|
||||||
|
QLIST_INIT(&acb->votes.vote_list);
|
||||||
|
acb->is_read = false;
|
||||||
|
acb->vote_ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
acb->qcrs[i].buf = NULL;
|
||||||
|
acb->qcrs[i].ret = 0;
|
||||||
|
acb->qcrs[i].parent = acb;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
|
||||||
|
{
|
||||||
|
QObject *data;
|
||||||
|
assert(node_name);
|
||||||
|
data = qobject_from_jsonf("{ 'node-name': %s"
|
||||||
|
", 'sector-num': %" PRId64
|
||||||
|
", 'sectors-count': %d }",
|
||||||
|
node_name, acb->sector_num, acb->nb_sectors);
|
||||||
|
if (ret < 0) {
|
||||||
|
QDict *dict = qobject_to_qdict(data);
|
||||||
|
qdict_put(dict, "error", qstring_from_str(strerror(-ret)));
|
||||||
|
}
|
||||||
|
monitor_protocol_event(QEVENT_QUORUM_REPORT_BAD, data);
|
||||||
|
qobject_decref(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||||
|
{
|
||||||
|
QObject *data;
|
||||||
|
const char *reference = acb->common.bs->device_name[0] ?
|
||||||
|
acb->common.bs->device_name :
|
||||||
|
acb->common.bs->node_name;
|
||||||
|
data = qobject_from_jsonf("{ 'reference': %s"
|
||||||
|
", 'sector-num': %" PRId64
|
||||||
|
", 'sectors-count': %d }",
|
||||||
|
reference, acb->sector_num, acb->nb_sectors);
|
||||||
|
monitor_protocol_event(QEVENT_QUORUM_FAILURE, data);
|
||||||
|
qobject_decref(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int quorum_vote_error(QuorumAIOCB *acb);
|
||||||
|
|
||||||
|
static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||||
|
|
||||||
|
if (acb->success_count < s->threshold) {
|
||||||
|
acb->vote_ret = quorum_vote_error(acb);
|
||||||
|
quorum_report_failure(acb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_aio_cb(void *opaque, int ret)
|
||||||
|
{
|
||||||
|
QuorumChildRequest *sacb = opaque;
|
||||||
|
QuorumAIOCB *acb = sacb->parent;
|
||||||
|
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||||
|
|
||||||
|
sacb->ret = ret;
|
||||||
|
acb->count++;
|
||||||
|
if (ret == 0) {
|
||||||
|
acb->success_count++;
|
||||||
|
} else {
|
||||||
|
quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret);
|
||||||
|
}
|
||||||
|
assert(acb->count <= s->num_children);
|
||||||
|
assert(acb->success_count <= s->num_children);
|
||||||
|
if (acb->count < s->num_children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do the vote on read */
|
||||||
|
if (acb->is_read) {
|
||||||
|
quorum_vote(acb);
|
||||||
|
} else {
|
||||||
|
quorum_has_too_much_io_failed(acb);
|
||||||
|
}
|
||||||
|
|
||||||
|
quorum_aio_finalize(acb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||||
|
QuorumAIOCB *acb,
|
||||||
|
QuorumVoteValue *value)
|
||||||
|
{
|
||||||
|
QuorumVoteVersion *version;
|
||||||
|
QuorumVoteItem *item;
|
||||||
|
|
||||||
|
QLIST_FOREACH(version, &acb->votes.vote_list, next) {
|
||||||
|
if (acb->votes.compare(&version->value, value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QLIST_FOREACH(item, &version->items, next) {
|
||||||
|
quorum_report_bad(acb, s->bs[item->index]->node_name, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
assert(dest->niov == source->niov);
|
||||||
|
assert(dest->size == source->size);
|
||||||
|
for (i = 0; i < source->niov; i++) {
|
||||||
|
assert(dest->iov[i].iov_len == source->iov[i].iov_len);
|
||||||
|
memcpy(dest->iov[i].iov_base,
|
||||||
|
source->iov[i].iov_base,
|
||||||
|
source->iov[i].iov_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_count_vote(QuorumVotes *votes,
|
||||||
|
QuorumVoteValue *value,
|
||||||
|
int index)
|
||||||
|
{
|
||||||
|
QuorumVoteVersion *v = NULL, *version = NULL;
|
||||||
|
QuorumVoteItem *item;
|
||||||
|
|
||||||
|
/* look if we have something with this hash */
|
||||||
|
QLIST_FOREACH(v, &votes->vote_list, next) {
|
||||||
|
if (votes->compare(&v->value, value)) {
|
||||||
|
version = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It's a version not yet in the list add it */
|
||||||
|
if (!version) {
|
||||||
|
version = g_new0(QuorumVoteVersion, 1);
|
||||||
|
QLIST_INIT(&version->items);
|
||||||
|
memcpy(&version->value, value, sizeof(version->value));
|
||||||
|
version->index = index;
|
||||||
|
version->vote_count = 0;
|
||||||
|
QLIST_INSERT_HEAD(&votes->vote_list, version, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
version->vote_count++;
|
||||||
|
|
||||||
|
item = g_new0(QuorumVoteItem, 1);
|
||||||
|
item->index = index;
|
||||||
|
QLIST_INSERT_HEAD(&version->items, item, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_free_vote_list(QuorumVotes *votes)
|
||||||
|
{
|
||||||
|
QuorumVoteVersion *version, *next_version;
|
||||||
|
QuorumVoteItem *item, *next_item;
|
||||||
|
|
||||||
|
QLIST_FOREACH_SAFE(version, &votes->vote_list, next, next_version) {
|
||||||
|
QLIST_REMOVE(version, next);
|
||||||
|
QLIST_FOREACH_SAFE(item, &version->items, next, next_item) {
|
||||||
|
QLIST_REMOVE(item, next);
|
||||||
|
g_free(item);
|
||||||
|
}
|
||||||
|
g_free(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash)
|
||||||
|
{
|
||||||
|
int j, ret;
|
||||||
|
gnutls_hash_hd_t dig;
|
||||||
|
QEMUIOVector *qiov = &acb->qcrs[i].qiov;
|
||||||
|
|
||||||
|
ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < qiov->niov; j++) {
|
||||||
|
ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len);
|
||||||
|
if (ret < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gnutls_hash_deinit(dig, (void *) hash);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes)
|
||||||
|
{
|
||||||
|
int max = 0;
|
||||||
|
QuorumVoteVersion *candidate, *winner = NULL;
|
||||||
|
|
||||||
|
QLIST_FOREACH(candidate, &votes->vote_list, next) {
|
||||||
|
if (candidate->vote_count > max) {
|
||||||
|
max = candidate->vote_count;
|
||||||
|
winner = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return winner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* qemu_iovec_compare is handy for blkverify mode because it returns the first
|
||||||
|
* differing byte location. Yet it is handcoded to compare vectors one byte
|
||||||
|
* after another so it does not benefit from the libc SIMD optimizations.
|
||||||
|
* quorum_iovec_compare is written for speed and should be used in the non
|
||||||
|
* blkverify mode of quorum.
|
||||||
|
*/
|
||||||
|
static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
assert(a->niov == b->niov);
|
||||||
|
for (i = 0; i < a->niov; i++) {
|
||||||
|
assert(a->iov[i].iov_len == b->iov[i].iov_len);
|
||||||
|
result = memcmp(a->iov[i].iov_base,
|
||||||
|
b->iov[i].iov_base,
|
||||||
|
a->iov[i].iov_len);
|
||||||
|
if (result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
|
||||||
|
const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
fprintf(stderr, "quorum: sector_num=%" PRId64 " nb_sectors=%d ",
|
||||||
|
acb->sector_num, acb->nb_sectors);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
va_end(ap);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool quorum_compare(QuorumAIOCB *acb,
|
||||||
|
QEMUIOVector *a,
|
||||||
|
QEMUIOVector *b)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||||
|
ssize_t offset;
|
||||||
|
|
||||||
|
/* This driver will replace blkverify in this particular case */
|
||||||
|
if (s->is_blkverify) {
|
||||||
|
offset = qemu_iovec_compare(a, b);
|
||||||
|
if (offset != -1) {
|
||||||
|
quorum_err(acb, "contents mismatch in sector %" PRId64,
|
||||||
|
acb->sector_num +
|
||||||
|
(uint64_t)(offset / BDRV_SECTOR_SIZE));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return quorum_iovec_compare(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do a vote to get the error code */
|
||||||
|
static int quorum_vote_error(QuorumAIOCB *acb)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||||
|
QuorumVoteVersion *winner = NULL;
|
||||||
|
QuorumVotes error_votes;
|
||||||
|
QuorumVoteValue result_value;
|
||||||
|
int i, ret = 0;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
QLIST_INIT(&error_votes.vote_list);
|
||||||
|
error_votes.compare = quorum_64bits_compare;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
ret = acb->qcrs[i].ret;
|
||||||
|
if (ret) {
|
||||||
|
error = true;
|
||||||
|
result_value.l = ret;
|
||||||
|
quorum_count_vote(&error_votes, &result_value, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
winner = quorum_get_vote_winner(&error_votes);
|
||||||
|
ret = winner->value.l;
|
||||||
|
}
|
||||||
|
|
||||||
|
quorum_free_vote_list(&error_votes);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_vote(QuorumAIOCB *acb)
|
||||||
|
{
|
||||||
|
bool quorum = true;
|
||||||
|
int i, j, ret;
|
||||||
|
QuorumVoteValue hash;
|
||||||
|
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||||
|
QuorumVoteVersion *winner;
|
||||||
|
|
||||||
|
if (quorum_has_too_much_io_failed(acb)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the index of the first successful read */
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
if (!acb->qcrs[i].ret) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(i < s->num_children);
|
||||||
|
|
||||||
|
/* compare this read with all other successful reads stopping at quorum
|
||||||
|
* failure
|
||||||
|
*/
|
||||||
|
for (j = i + 1; j < s->num_children; j++) {
|
||||||
|
if (acb->qcrs[j].ret) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
quorum = quorum_compare(acb, &acb->qcrs[i].qiov, &acb->qcrs[j].qiov);
|
||||||
|
if (!quorum) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Every successful read agrees */
|
||||||
|
if (quorum) {
|
||||||
|
quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute hashes for each successful read, also store indexes */
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
if (acb->qcrs[i].ret) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ret = quorum_compute_hash(acb, i, &hash);
|
||||||
|
/* if ever the hash computation failed */
|
||||||
|
if (ret < 0) {
|
||||||
|
acb->vote_ret = ret;
|
||||||
|
goto free_exit;
|
||||||
|
}
|
||||||
|
quorum_count_vote(&acb->votes, &hash, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vote to select the most represented version */
|
||||||
|
winner = quorum_get_vote_winner(&acb->votes);
|
||||||
|
|
||||||
|
/* if the winner count is smaller than threshold the read fails */
|
||||||
|
if (winner->vote_count < s->threshold) {
|
||||||
|
quorum_report_failure(acb);
|
||||||
|
acb->vote_ret = -EIO;
|
||||||
|
goto free_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we have a winner: copy it */
|
||||||
|
quorum_copy_qiov(acb->qiov, &acb->qcrs[winner->index].qiov);
|
||||||
|
|
||||||
|
/* some versions are bad print them */
|
||||||
|
quorum_report_bad_versions(s, acb, &winner->value);
|
||||||
|
|
||||||
|
free_exit:
|
||||||
|
/* free lists */
|
||||||
|
quorum_free_vote_list(&acb->votes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockDriverAIOCB *quorum_aio_readv(BlockDriverState *bs,
|
||||||
|
int64_t sector_num,
|
||||||
|
QEMUIOVector *qiov,
|
||||||
|
int nb_sectors,
|
||||||
|
BlockDriverCompletionFunc *cb,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num,
|
||||||
|
nb_sectors, cb, opaque);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
acb->is_read = true;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
acb->qcrs[i].buf = qemu_blockalign(s->bs[i], qiov->size);
|
||||||
|
qemu_iovec_init(&acb->qcrs[i].qiov, qiov->niov);
|
||||||
|
qemu_iovec_clone(&acb->qcrs[i].qiov, qiov, acb->qcrs[i].buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
bdrv_aio_readv(s->bs[i], sector_num, &acb->qcrs[i].qiov, nb_sectors,
|
||||||
|
quorum_aio_cb, &acb->qcrs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return &acb->common;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockDriverAIOCB *quorum_aio_writev(BlockDriverState *bs,
|
||||||
|
int64_t sector_num,
|
||||||
|
QEMUIOVector *qiov,
|
||||||
|
int nb_sectors,
|
||||||
|
BlockDriverCompletionFunc *cb,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors,
|
||||||
|
cb, opaque);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov,
|
||||||
|
nb_sectors, &quorum_aio_cb,
|
||||||
|
&acb->qcrs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return &acb->common;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t quorum_getlength(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
int64_t result;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* check that all file have the same length */
|
||||||
|
result = bdrv_getlength(s->bs[0]);
|
||||||
|
if (result < 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (i = 1; i < s->num_children; i++) {
|
||||||
|
int64_t value = bdrv_getlength(s->bs[i]);
|
||||||
|
if (value < 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (value != result) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_invalidate_cache(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
bdrv_invalidate_cache(s->bs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
QuorumVoteVersion *winner = NULL;
|
||||||
|
QuorumVotes error_votes;
|
||||||
|
QuorumVoteValue result_value;
|
||||||
|
int i;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
QLIST_INIT(&error_votes.vote_list);
|
||||||
|
error_votes.compare = quorum_64bits_compare;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
result = bdrv_co_flush(s->bs[i]);
|
||||||
|
result_value.l = result;
|
||||||
|
quorum_count_vote(&error_votes, &result_value, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
winner = quorum_get_vote_winner(&error_votes);
|
||||||
|
result = winner->value.l;
|
||||||
|
|
||||||
|
quorum_free_vote_list(&error_votes);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||||
|
BlockDriverState *candidate)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
bool perm = bdrv_recurse_is_first_non_filter(s->bs[i],
|
||||||
|
candidate);
|
||||||
|
if (perm) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int quorum_valid_threshold(int threshold, int num_children, Error **errp)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (threshold < 1) {
|
||||||
|
error_set(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||||
|
"vote-threshold", "value >= 1");
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threshold > num_children) {
|
||||||
|
error_setg(errp, "threshold may not exceed children count");
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QemuOptsList quorum_runtime_opts = {
|
||||||
|
.name = "quorum",
|
||||||
|
.head = QTAILQ_HEAD_INITIALIZER(quorum_runtime_opts.head),
|
||||||
|
.desc = {
|
||||||
|
{
|
||||||
|
.name = QUORUM_OPT_VOTE_THRESHOLD,
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "The number of vote needed for reaching quorum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = QUORUM_OPT_BLKVERIFY,
|
||||||
|
.type = QEMU_OPT_BOOL,
|
||||||
|
.help = "Trigger block verify mode if set",
|
||||||
|
},
|
||||||
|
{ /* end of list */ }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
QemuOpts *opts;
|
||||||
|
bool *opened;
|
||||||
|
QDict *sub = NULL;
|
||||||
|
QList *list = NULL;
|
||||||
|
const QListEntry *lentry;
|
||||||
|
int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
qdict_flatten(options);
|
||||||
|
qdict_extract_subqdict(options, &sub, "children.");
|
||||||
|
qdict_array_split(sub, &list);
|
||||||
|
|
||||||
|
if (qdict_size(sub)) {
|
||||||
|
error_setg(&local_err, "Invalid option children.%s",
|
||||||
|
qdict_first(sub)->key);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* count how many different children are present */
|
||||||
|
s->num_children = qlist_size(list);
|
||||||
|
if (s->num_children < 2) {
|
||||||
|
error_setg(&local_err,
|
||||||
|
"Number of provided children must be greater than 1");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = qemu_opts_create(&quorum_runtime_opts, NULL, 0, &error_abort);
|
||||||
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0);
|
||||||
|
|
||||||
|
/* and validate it against s->num_children */
|
||||||
|
ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is the driver in blkverify mode */
|
||||||
|
if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
|
||||||
|
s->num_children == 2 && s->threshold == 2) {
|
||||||
|
s->is_blkverify = true;
|
||||||
|
} else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
|
||||||
|
fprintf(stderr, "blkverify mode is set by setting blkverify=on "
|
||||||
|
"and using two files with vote_threshold=2\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate the children BlockDriverState array */
|
||||||
|
s->bs = g_new0(BlockDriverState *, s->num_children);
|
||||||
|
opened = g_new0(bool, s->num_children);
|
||||||
|
|
||||||
|
for (i = 0, lentry = qlist_first(list); lentry;
|
||||||
|
lentry = qlist_next(lentry), i++) {
|
||||||
|
QDict *d;
|
||||||
|
QString *string;
|
||||||
|
|
||||||
|
switch (qobject_type(lentry->value))
|
||||||
|
{
|
||||||
|
/* List of options */
|
||||||
|
case QTYPE_QDICT:
|
||||||
|
d = qobject_to_qdict(lentry->value);
|
||||||
|
QINCREF(d);
|
||||||
|
ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL,
|
||||||
|
&local_err);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* QMP reference */
|
||||||
|
case QTYPE_QSTRING:
|
||||||
|
string = qobject_to_qstring(lentry->value);
|
||||||
|
ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
|
||||||
|
flags, NULL, &local_err);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_setg(&local_err, "Specification of child block device %i "
|
||||||
|
"is invalid", i);
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
goto close_exit;
|
||||||
|
}
|
||||||
|
opened[i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(opened);
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
close_exit:
|
||||||
|
/* cleanup on error */
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
if (!opened[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bdrv_unref(s->bs[i]);
|
||||||
|
}
|
||||||
|
g_free(s->bs);
|
||||||
|
g_free(opened);
|
||||||
|
exit:
|
||||||
|
/* propagate error */
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
}
|
||||||
|
QDECREF(list);
|
||||||
|
QDECREF(sub);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quorum_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQuorumState *s = bs->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_children; i++) {
|
||||||
|
bdrv_unref(s->bs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(s->bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockDriver bdrv_quorum = {
|
||||||
|
.format_name = "quorum",
|
||||||
|
.protocol_name = "quorum",
|
||||||
|
|
||||||
|
.instance_size = sizeof(BDRVQuorumState),
|
||||||
|
|
||||||
|
.bdrv_file_open = quorum_open,
|
||||||
|
.bdrv_close = quorum_close,
|
||||||
|
|
||||||
|
.authorizations = { true, true },
|
||||||
|
|
||||||
|
.bdrv_co_flush_to_disk = quorum_co_flush,
|
||||||
|
|
||||||
|
.bdrv_getlength = quorum_getlength,
|
||||||
|
|
||||||
|
.bdrv_aio_readv = quorum_aio_readv,
|
||||||
|
.bdrv_aio_writev = quorum_aio_writev,
|
||||||
|
.bdrv_invalidate_cache = quorum_invalidate_cache,
|
||||||
|
|
||||||
|
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bdrv_quorum_init(void)
|
||||||
|
{
|
||||||
|
bdrv_register(&bdrv_quorum);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_init(bdrv_quorum_init);
|
||||||
@@ -361,7 +361,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||||||
|
|
||||||
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -448,7 +448,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
s->type = FTYPE_FILE;
|
s->type = FTYPE_FILE;
|
||||||
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1597,7 +1597,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1832,7 +1832,7 @@ static int floppy_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
|
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
|
||||||
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1961,7 +1961,7 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
/* open will not fail even if no CD is inserted, so add O_NONBLOCK */
|
/* open will not fail even if no CD is inserted, so add O_NONBLOCK */
|
||||||
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2078,7 +2078,7 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
ret = raw_open_common(bs, options, flags, 0, &local_err);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -594,7 +594,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
QemuOpts *opts = qemu_opts_create(&raw_runtime_opts, NULL, 0,
|
QemuOpts *opts = qemu_opts_create(&raw_runtime_opts, NULL, 0,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto done;
|
goto done;
|
||||||
|
|||||||
@@ -90,6 +90,12 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
|||||||
return bdrv_get_info(bs->file, bdi);
|
return bdrv_get_info(bs->file, bdi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int raw_refresh_limits(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
bs->bl = bs->file->bl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
{
|
{
|
||||||
return bdrv_truncate(bs->file, offset);
|
return bdrv_truncate(bs->file, offset);
|
||||||
@@ -140,7 +146,7 @@ static int raw_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_create_file(filename, options, &local_err);
|
ret = bdrv_create_file(filename, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -150,7 +156,6 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
bs->sg = bs->file->sg;
|
bs->sg = bs->file->sg;
|
||||||
bs->bl = bs->file->bl;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +187,7 @@ static BlockDriver bdrv_raw = {
|
|||||||
.bdrv_getlength = &raw_getlength,
|
.bdrv_getlength = &raw_getlength,
|
||||||
.has_variable_length = true,
|
.has_variable_length = true,
|
||||||
.bdrv_get_info = &raw_get_info,
|
.bdrv_get_info = &raw_get_info,
|
||||||
|
.bdrv_refresh_limits = &raw_refresh_limits,
|
||||||
.bdrv_is_inserted = &raw_is_inserted,
|
.bdrv_is_inserted = &raw_is_inserted,
|
||||||
.bdrv_media_changed = &raw_media_changed,
|
.bdrv_media_changed = &raw_media_changed,
|
||||||
.bdrv_eject = &raw_eject,
|
.bdrv_eject = &raw_eject,
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
|
|||||||
@@ -1385,7 +1385,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
@@ -1534,7 +1534,8 @@ static int sd_prealloc(const char *filename)
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
|
NULL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
@@ -1695,7 +1696,9 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, backing_file, NULL, NULL, 0, &local_err);
|
bs = NULL;
|
||||||
|
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL,
|
||||||
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
|
|||||||
ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err);
|
ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
29
block/vdi.c
29
block/vdi.c
@@ -395,43 +395,50 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (header.signature != VDI_SIGNATURE) {
|
if (header.signature != VDI_SIGNATURE) {
|
||||||
logout("bad vdi signature %08x\n", header.signature);
|
error_setg(errp, "Image not in VDI format (bad signature %08x)", header.signature);
|
||||||
ret = -EMEDIUMTYPE;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (header.version != VDI_VERSION_1_1) {
|
} else if (header.version != VDI_VERSION_1_1) {
|
||||||
logout("unsupported version %u.%u\n",
|
error_setg(errp, "unsupported VDI image (version %u.%u)",
|
||||||
header.version >> 16, header.version & 0xffff);
|
header.version >> 16, header.version & 0xffff);
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (header.offset_bmap % SECTOR_SIZE != 0) {
|
} else if (header.offset_bmap % SECTOR_SIZE != 0) {
|
||||||
/* We only support block maps which start on a sector boundary. */
|
/* We only support block maps which start on a sector boundary. */
|
||||||
logout("unsupported block map offset 0x%x B\n", header.offset_bmap);
|
error_setg(errp, "unsupported VDI image (unaligned block map offset "
|
||||||
|
"0x%x)", header.offset_bmap);
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (header.offset_data % SECTOR_SIZE != 0) {
|
} else if (header.offset_data % SECTOR_SIZE != 0) {
|
||||||
/* We only support data blocks which start on a sector boundary. */
|
/* We only support data blocks which start on a sector boundary. */
|
||||||
logout("unsupported data offset 0x%x B\n", header.offset_data);
|
error_setg(errp, "unsupported VDI image (unaligned data offset 0x%x)",
|
||||||
|
header.offset_data);
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (header.sector_size != SECTOR_SIZE) {
|
} else if (header.sector_size != SECTOR_SIZE) {
|
||||||
logout("unsupported sector size %u B\n", header.sector_size);
|
error_setg(errp, "unsupported VDI image (sector size %u is not %u)",
|
||||||
|
header.sector_size, SECTOR_SIZE);
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (header.block_size != 1 * MiB) {
|
} else if (header.block_size != 1 * MiB) {
|
||||||
logout("unsupported block size %u B\n", header.block_size);
|
error_setg(errp, "unsupported VDI image (sector size %u is not %u)",
|
||||||
|
header.block_size, 1 * MiB);
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (header.disk_size >
|
} else if (header.disk_size >
|
||||||
(uint64_t)header.blocks_in_image * header.block_size) {
|
(uint64_t)header.blocks_in_image * header.block_size) {
|
||||||
logout("unsupported disk size %" PRIu64 " B\n", header.disk_size);
|
error_setg(errp, "unsupported VDI image (disk size %" PRIu64 ", "
|
||||||
|
"image bitmap has room for %" PRIu64 ")",
|
||||||
|
header.disk_size,
|
||||||
|
(uint64_t)header.blocks_in_image * header.block_size);
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (!uuid_is_null(header.uuid_link)) {
|
} else if (!uuid_is_null(header.uuid_link)) {
|
||||||
logout("link uuid != 0, unsupported\n");
|
error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if (!uuid_is_null(header.uuid_parent)) {
|
} else if (!uuid_is_null(header.uuid_parent)) {
|
||||||
logout("parent uuid != 0, unsupported\n");
|
error_setg(errp, "unsupported VDI image (non-NULL parent UUID)");
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -965,8 +965,8 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
|
|||||||
cpu_to_le32s((uint32_t *)(buffer + 4));
|
cpu_to_le32s((uint32_t *)(buffer + 4));
|
||||||
|
|
||||||
/* now write to the log */
|
/* now write to the log */
|
||||||
vhdx_log_write_sectors(bs, &s->log, §ors_written, buffer,
|
ret = vhdx_log_write_sectors(bs, &s->log, §ors_written, buffer,
|
||||||
desc_sectors + sectors);
|
desc_sectors + sectors);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|||||||
33
block/vhdx.c
33
block/vhdx.c
@@ -374,7 +374,7 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
|
|||||||
inactive_header->log_guid = *log_guid;
|
inactive_header->log_guid = *log_guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
vhdx_write_header(bs->file, inactive_header, header_offset, true);
|
ret = vhdx_write_header(bs->file, inactive_header, header_offset, true);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -402,9 +402,10 @@ int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* opens the specified header block from the VHDX file header section */
|
/* opens the specified header block from the VHDX file header section */
|
||||||
static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
|
static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret;
|
||||||
VHDXHeader *header1;
|
VHDXHeader *header1;
|
||||||
VHDXHeader *header2;
|
VHDXHeader *header2;
|
||||||
bool h1_valid = false;
|
bool h1_valid = false;
|
||||||
@@ -462,7 +463,6 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
|
|||||||
} else if (!h1_valid && h2_valid) {
|
} else if (!h1_valid && h2_valid) {
|
||||||
s->curr_header = 1;
|
s->curr_header = 1;
|
||||||
} else if (!h1_valid && !h2_valid) {
|
} else if (!h1_valid && !h2_valid) {
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
goto fail;
|
||||||
} else {
|
} else {
|
||||||
/* If both headers are valid, then we choose the active one by the
|
/* If both headers are valid, then we choose the active one by the
|
||||||
@@ -473,27 +473,22 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
|
|||||||
} else if (h2_seq > h1_seq) {
|
} else if (h2_seq > h1_seq) {
|
||||||
s->curr_header = 1;
|
s->curr_header = 1;
|
||||||
} else {
|
} else {
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
|
vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
|
||||||
s->headers[s->curr_header]->log_length);
|
s->headers[s->curr_header]->log_length);
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "No valid VHDX header found");
|
error_setg_errno(errp, -ret, "No valid VHDX header found");
|
||||||
qemu_vfree(header1);
|
qemu_vfree(header1);
|
||||||
qemu_vfree(header2);
|
qemu_vfree(header2);
|
||||||
s->headers[0] = NULL;
|
s->headers[0] = NULL;
|
||||||
s->headers[1] = NULL;
|
s->headers[1] = NULL;
|
||||||
exit:
|
exit:
|
||||||
qemu_vfree(buffer);
|
qemu_vfree(buffer);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -878,7 +873,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint64_t signature;
|
uint64_t signature;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
s->bat = NULL;
|
s->bat = NULL;
|
||||||
s->first_visible_write = true;
|
s->first_visible_write = true;
|
||||||
@@ -901,8 +896,10 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
* header update */
|
* header update */
|
||||||
vhdx_guid_generate(&s->session_guid);
|
vhdx_guid_generate(&s->session_guid);
|
||||||
|
|
||||||
ret = vhdx_parse_header(bs, s);
|
vhdx_parse_header(bs, s, &local_err);
|
||||||
if (ret < 0) {
|
if (local_err != NULL) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1797,7 +1794,9 @@ static int vhdx_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
bs = NULL;
|
||||||
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
|
NULL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -1810,13 +1809,13 @@ static int vhdx_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
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);
|
||||||
bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
|
ret = bdrv_pwrite(bs, 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) {
|
||||||
bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature), creator,
|
ret = bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature),
|
||||||
creator_items * sizeof(gunichar2));
|
creator, creator_items * sizeof(gunichar2));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
|
|||||||
198
block/vmdk.c
198
block/vmdk.c
@@ -526,8 +526,34 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||||
uint64_t desc_offset, Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
|
static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int64_t size;
|
||||||
|
char *buf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
size = bdrv_getlength(file);
|
||||||
|
if (size < 0) {
|
||||||
|
error_setg_errno(errp, -size, "Could not access file");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = MIN(size, 1 << 20); /* avoid unbounded allocation */
|
||||||
|
buf = g_malloc0(size + 1);
|
||||||
|
|
||||||
|
ret = bdrv_pread(file, desc_offset, buf, size);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Could not read from file");
|
||||||
|
g_free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
static int vmdk_open_vmdk4(BlockDriverState *bs,
|
static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||||
BlockDriverState *file,
|
BlockDriverState *file,
|
||||||
@@ -546,11 +572,18 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
|||||||
error_setg_errno(errp, -ret,
|
error_setg_errno(errp, -ret,
|
||||||
"Could not read header from file '%s'",
|
"Could not read header from file '%s'",
|
||||||
file->filename);
|
file->filename);
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (header.capacity == 0) {
|
if (header.capacity == 0) {
|
||||||
uint64_t desc_offset = le64_to_cpu(header.desc_offset);
|
uint64_t desc_offset = le64_to_cpu(header.desc_offset);
|
||||||
if (desc_offset) {
|
if (desc_offset) {
|
||||||
return vmdk_open_desc_file(bs, flags, desc_offset << 9, errp);
|
char *buf = vmdk_read_desc(file, desc_offset << 9, errp);
|
||||||
|
if (!buf) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
ret = vmdk_open_desc_file(bs, flags, buf, errp);
|
||||||
|
g_free(buf);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,8 +642,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
|||||||
char buf[64];
|
char buf[64];
|
||||||
snprintf(buf, sizeof(buf), "VMDK version %d",
|
snprintf(buf, sizeof(buf), "VMDK version %d",
|
||||||
le32_to_cpu(header.version));
|
le32_to_cpu(header.version));
|
||||||
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
|
||||||
bs->device_name, "vmdk", buf);
|
bs->device_name, "vmdk", buf);
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
} else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) {
|
} else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) {
|
||||||
/* VMware KB 2064959 explains that version 3 added support for
|
/* VMware KB 2064959 explains that version 3 added support for
|
||||||
@@ -622,7 +655,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (le32_to_cpu(header.num_gtes_per_gt) > 512) {
|
if (le32_to_cpu(header.num_gtes_per_gt) > 512) {
|
||||||
error_report("L2 table size too big");
|
error_setg(errp, "L2 table size too big");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,8 +671,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
if (bdrv_getlength(file) <
|
if (bdrv_getlength(file) <
|
||||||
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) {
|
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) {
|
||||||
error_report("File truncated, expecting at least %lld bytes",
|
error_setg(errp, "File truncated, expecting at least %lld bytes",
|
||||||
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
|
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,16 +734,12 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
|
|||||||
|
|
||||||
/* Open an extent file and append to bs array */
|
/* Open an extent file and append to bs array */
|
||||||
static int vmdk_open_sparse(BlockDriverState *bs,
|
static int vmdk_open_sparse(BlockDriverState *bs,
|
||||||
BlockDriverState *file,
|
BlockDriverState *file, int flags,
|
||||||
int flags, Error **errp)
|
char *buf, Error **errp)
|
||||||
{
|
{
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
|
|
||||||
if (bdrv_pread(file, 0, &magic, sizeof(magic)) != sizeof(magic)) {
|
magic = ldl_be_p(buf);
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
magic = be32_to_cpu(magic);
|
|
||||||
switch (magic) {
|
switch (magic) {
|
||||||
case VMDK3_MAGIC:
|
case VMDK3_MAGIC:
|
||||||
return vmdk_open_vmfs_sparse(bs, file, flags, errp);
|
return vmdk_open_vmfs_sparse(bs, file, flags, errp);
|
||||||
@@ -719,7 +748,8 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
|||||||
return vmdk_open_vmdk4(bs, file, flags, errp);
|
return vmdk_open_vmdk4(bs, file, flags, errp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EMEDIUMTYPE;
|
error_setg(errp, "Image not in VMDK format");
|
||||||
|
return -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -776,8 +806,9 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||||||
|
|
||||||
path_combine(extent_path, sizeof(extent_path),
|
path_combine(extent_path, sizeof(extent_path),
|
||||||
desc_file_path, fname);
|
desc_file_path, fname);
|
||||||
ret = bdrv_file_open(&extent_file, extent_path, NULL, NULL,
|
extent_file = NULL;
|
||||||
bs->open_flags, errp);
|
ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
|
||||||
|
bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -794,8 +825,14 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||||||
extent->flat_start_offset = flat_offset << 9;
|
extent->flat_start_offset = flat_offset << 9;
|
||||||
} else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
|
} else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
|
||||||
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
|
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
|
||||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, errp);
|
char *buf = vmdk_read_desc(extent_file, 0, errp);
|
||||||
|
if (!buf) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
} else {
|
||||||
|
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
|
||||||
|
}
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
g_free(buf);
|
||||||
bdrv_unref(extent_file);
|
bdrv_unref(extent_file);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -818,29 +855,16 @@ next_line:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||||
uint64_t desc_offset, Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
char *buf = NULL;
|
|
||||||
char ct[128];
|
char ct[128];
|
||||||
BDRVVmdkState *s = bs->opaque;
|
BDRVVmdkState *s = bs->opaque;
|
||||||
int64_t size;
|
|
||||||
|
|
||||||
size = bdrv_getlength(bs->file);
|
|
||||||
if (size < 0) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size = MIN(size, 1 << 20); /* avoid unbounded allocation */
|
|
||||||
buf = g_malloc0(size + 1);
|
|
||||||
|
|
||||||
ret = bdrv_pread(bs->file, desc_offset, buf, size);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
|
if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
|
||||||
ret = -EMEDIUMTYPE;
|
error_setg(errp, "invalid VMDK image descriptor");
|
||||||
|
ret = -EINVAL;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strcmp(ct, "monolithicFlat") &&
|
if (strcmp(ct, "monolithicFlat") &&
|
||||||
@@ -856,24 +880,37 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
|
|||||||
s->desc_offset = 0;
|
s->desc_offset = 0;
|
||||||
ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
|
ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
|
||||||
exit:
|
exit:
|
||||||
g_free(buf);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
char *buf = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
BDRVVmdkState *s = bs->opaque;
|
BDRVVmdkState *s = bs->opaque;
|
||||||
|
uint32_t magic;
|
||||||
|
|
||||||
if (vmdk_open_sparse(bs, bs->file, flags, errp) == 0) {
|
buf = vmdk_read_desc(bs->file, 0, errp);
|
||||||
s->desc_offset = 0x200;
|
if (!buf) {
|
||||||
} else {
|
return -EINVAL;
|
||||||
ret = vmdk_open_desc_file(bs, flags, 0, errp);
|
|
||||||
if (ret) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
magic = ldl_be_p(buf);
|
||||||
|
switch (magic) {
|
||||||
|
case VMDK3_MAGIC:
|
||||||
|
case VMDK4_MAGIC:
|
||||||
|
ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp);
|
||||||
|
s->desc_offset = 0x200;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = vmdk_open_desc_file(bs, flags, buf, errp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
/* try to open parent images, if exist */
|
/* try to open parent images, if exist */
|
||||||
ret = vmdk_parent_open(bs);
|
ret = vmdk_parent_open(bs);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@@ -888,10 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
|
||||||
"vmdk", bs->device_name, "live migration");
|
"vmdk", bs->device_name, "live migration");
|
||||||
migrate_add_blocker(s->migration_blocker);
|
migrate_add_blocker(s->migration_blocker);
|
||||||
|
g_free(buf);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
g_free(buf);
|
||||||
g_free(s->create_type);
|
g_free(s->create_type);
|
||||||
s->create_type = NULL;
|
s->create_type = NULL;
|
||||||
vmdk_free_extents(bs);
|
vmdk_free_extents(bs);
|
||||||
@@ -1146,7 +1184,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
|||||||
break;
|
break;
|
||||||
case VMDK_OK:
|
case VMDK_OK:
|
||||||
ret = BDRV_BLOCK_DATA;
|
ret = BDRV_BLOCK_DATA;
|
||||||
if (extent->file == bs->file) {
|
if (extent->file == bs->file && !extent->compressed) {
|
||||||
ret |= BDRV_BLOCK_OFFSET_VALID | offset;
|
ret |= BDRV_BLOCK_OFFSET_VALID | offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1493,7 +1531,9 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
assert(bs == NULL);
|
||||||
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
|
NULL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -1502,7 +1542,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
if (flat) {
|
if (flat) {
|
||||||
ret = bdrv_truncate(bs, filesize);
|
ret = bdrv_truncate(bs, filesize);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Could not truncate file");
|
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||||
}
|
}
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -1562,7 +1602,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
|
|
||||||
ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9);
|
ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Could not truncate file");
|
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1755,10 +1795,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (backing_file) {
|
if (backing_file) {
|
||||||
BlockDriverState *bs = bdrv_new("");
|
BlockDriverState *bs = NULL;
|
||||||
ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp);
|
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_NO_BACKING, NULL,
|
||||||
|
errp);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
bdrv_unref(bs);
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strcmp(bs->drv->format_name, "vmdk")) {
|
if (strcmp(bs->drv->format_name, "vmdk")) {
|
||||||
@@ -1831,7 +1871,9 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = bdrv_file_open(&new_bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
|
assert(new_bs == NULL);
|
||||||
|
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
||||||
|
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||||
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;
|
||||||
@@ -1846,7 +1888,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
if (desc_offset == 0) {
|
if (desc_offset == 0) {
|
||||||
ret = bdrv_truncate(new_bs, desc_len);
|
ret = bdrv_truncate(new_bs, desc_len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "Could not truncate file");
|
error_setg_errno(errp, -ret, "Could not truncate file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exit:
|
exit:
|
||||||
@@ -1942,6 +1984,53 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||||
|
BdrvCheckMode fix)
|
||||||
|
{
|
||||||
|
BDRVVmdkState *s = bs->opaque;
|
||||||
|
VmdkExtent *extent = NULL;
|
||||||
|
int64_t sector_num = 0;
|
||||||
|
int64_t total_sectors = bdrv_getlength(bs) / BDRV_SECTOR_SIZE;
|
||||||
|
int ret;
|
||||||
|
uint64_t cluster_offset;
|
||||||
|
|
||||||
|
if (fix) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (sector_num >= total_sectors) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
extent = find_extent(s, sector_num, extent);
|
||||||
|
if (!extent) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: could not find extent for sector %" PRId64 "\n",
|
||||||
|
sector_num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = get_cluster_offset(bs, extent, NULL,
|
||||||
|
sector_num << BDRV_SECTOR_BITS,
|
||||||
|
0, &cluster_offset);
|
||||||
|
if (ret == VMDK_ERROR) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: could not get cluster_offset for sector %"
|
||||||
|
PRId64 "\n", sector_num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret == VMDK_OK && cluster_offset >= bdrv_getlength(extent->file)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: cluster offset for sector %"
|
||||||
|
PRId64 " points after EOF\n", sector_num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sector_num += extent->cluster_sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->corruptions++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
|
static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -2015,6 +2104,7 @@ static BlockDriver bdrv_vmdk = {
|
|||||||
.instance_size = sizeof(BDRVVmdkState),
|
.instance_size = sizeof(BDRVVmdkState),
|
||||||
.bdrv_probe = vmdk_probe,
|
.bdrv_probe = vmdk_probe,
|
||||||
.bdrv_open = vmdk_open,
|
.bdrv_open = vmdk_open,
|
||||||
|
.bdrv_check = vmdk_check,
|
||||||
.bdrv_reopen_prepare = vmdk_reopen_prepare,
|
.bdrv_reopen_prepare = vmdk_reopen_prepare,
|
||||||
.bdrv_read = vmdk_co_read,
|
.bdrv_read = vmdk_co_read,
|
||||||
.bdrv_write = vmdk_co_write,
|
.bdrv_write = vmdk_co_write,
|
||||||
|
|||||||
@@ -190,7 +190,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (strncmp(footer->creator, "conectix", 8)) {
|
if (strncmp(footer->creator, "conectix", 8)) {
|
||||||
ret = -EMEDIUMTYPE;
|
error_setg(errp, "invalid VPC image");
|
||||||
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
disk_type = VHD_FIXED;
|
disk_type = VHD_FIXED;
|
||||||
|
|||||||
@@ -1085,17 +1085,15 @@ DLOG(if (stderr == NULL) {
|
|||||||
|
|
||||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
error_propagate(errp, local_err);
|
||||||
error_free(local_err);
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
dirname = qemu_opt_get(opts, "dir");
|
dirname = qemu_opt_get(opts, "dir");
|
||||||
if (!dirname) {
|
if (!dirname) {
|
||||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "vvfat block driver requires "
|
error_setg(errp, "vvfat block driver requires a 'dir' option");
|
||||||
"a 'dir' option");
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1135,8 +1133,7 @@ DLOG(if (stderr == NULL) {
|
|||||||
case 12:
|
case 12:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Valid FAT types are only "
|
error_setg(errp, "Valid FAT types are only 12, 16 and 32");
|
||||||
"12, 16 and 32");
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -2936,15 +2933,13 @@ static int enable_write_target(BDRVVVFATState *s)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->qcow = bdrv_new("");
|
s->qcow = NULL;
|
||||||
|
ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL,
|
||||||
ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
|
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
bdrv_unref(s->qcow);
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
123
blockdev.c
123
blockdev.c
@@ -308,7 +308,6 @@ typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
|||||||
|
|
||||||
/* Takes the ownership of bs_opts */
|
/* Takes the ownership of bs_opts */
|
||||||
static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
||||||
BlockInterfaceType type,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
const char *buf;
|
const char *buf;
|
||||||
@@ -331,13 +330,13 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
* stay in bs_opts for processing by bdrv_open(). */
|
* stay in bs_opts for processing by bdrv_open(). */
|
||||||
id = qdict_get_try_str(bs_opts, "id");
|
id = qdict_get_try_str(bs_opts, "id");
|
||||||
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
|
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
|
||||||
if (error_is_set(&error)) {
|
if (error) {
|
||||||
error_propagate(errp, error);
|
error_propagate(errp, error);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_opts_absorb_qdict(opts, bs_opts, &error);
|
qemu_opts_absorb_qdict(opts, bs_opts, &error);
|
||||||
if (error_is_set(&error)) {
|
if (error) {
|
||||||
error_propagate(errp, error);
|
error_propagate(errp, error);
|
||||||
goto early_err;
|
goto early_err;
|
||||||
}
|
}
|
||||||
@@ -437,13 +436,8 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
|
|
||||||
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
||||||
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
||||||
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
|
|
||||||
error_setg(errp, "werror is not supported by this bus type");
|
|
||||||
goto early_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
on_write_error = parse_block_error_action(buf, 0, &error);
|
on_write_error = parse_block_error_action(buf, 0, &error);
|
||||||
if (error_is_set(&error)) {
|
if (error) {
|
||||||
error_propagate(errp, error);
|
error_propagate(errp, error);
|
||||||
goto early_err;
|
goto early_err;
|
||||||
}
|
}
|
||||||
@@ -451,25 +445,25 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
|
|
||||||
on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||||
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
||||||
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
|
|
||||||
error_report("rerror is not supported by this bus type");
|
|
||||||
goto early_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
on_read_error = parse_block_error_action(buf, 1, &error);
|
on_read_error = parse_block_error_action(buf, 1, &error);
|
||||||
if (error_is_set(&error)) {
|
if (error) {
|
||||||
error_propagate(errp, error);
|
error_propagate(errp, error);
|
||||||
goto early_err;
|
goto early_err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bdrv_find_node(qemu_opts_id(opts))) {
|
||||||
|
error_setg(errp, "device id=%s is conflicting with a node-name",
|
||||||
|
qemu_opts_id(opts));
|
||||||
|
goto early_err;
|
||||||
|
}
|
||||||
|
|
||||||
/* init */
|
/* init */
|
||||||
dinfo = g_malloc0(sizeof(*dinfo));
|
dinfo = g_malloc0(sizeof(*dinfo));
|
||||||
dinfo->id = g_strdup(qemu_opts_id(opts));
|
dinfo->id = g_strdup(qemu_opts_id(opts));
|
||||||
dinfo->bdrv = bdrv_new(dinfo->id);
|
dinfo->bdrv = bdrv_new(dinfo->id);
|
||||||
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
||||||
dinfo->bdrv->read_only = ro;
|
dinfo->bdrv->read_only = ro;
|
||||||
dinfo->type = type;
|
|
||||||
dinfo->refcount = 1;
|
dinfo->refcount = 1;
|
||||||
if (serial != NULL) {
|
if (serial != NULL) {
|
||||||
dinfo->serial = g_strdup(serial);
|
dinfo->serial = g_strdup(serial);
|
||||||
@@ -510,7 +504,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
||||||
|
|
||||||
QINCREF(bs_opts);
|
QINCREF(bs_opts);
|
||||||
ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
|
ret = bdrv_open(&dinfo->bdrv, file, NULL, bs_opts, bdrv_flags, drv, &error);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg(errp, "could not open disk image %s: %s",
|
error_setg(errp, "could not open disk image %s: %s",
|
||||||
@@ -608,6 +602,14 @@ QemuOptsList qemu_legacy_drive_opts = {
|
|||||||
.name = "read-only",
|
.name = "read-only",
|
||||||
.type = QEMU_OPT_BOOL,
|
.type = QEMU_OPT_BOOL,
|
||||||
.help = "open drive file as read-only",
|
.help = "open drive file as read-only",
|
||||||
|
},{
|
||||||
|
.name = "rerror",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "read error action",
|
||||||
|
},{
|
||||||
|
.name = "werror",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "write error action",
|
||||||
},{
|
},{
|
||||||
.name = "copy-on-read",
|
.name = "copy-on-read",
|
||||||
.type = QEMU_OPT_BOOL,
|
.type = QEMU_OPT_BOOL,
|
||||||
@@ -629,6 +631,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
int cyls, heads, secs, translation;
|
int cyls, heads, secs, translation;
|
||||||
int max_devs, bus_id, unit_id, index;
|
int max_devs, bus_id, unit_id, index;
|
||||||
const char *devaddr;
|
const char *devaddr;
|
||||||
|
const char *werror, *rerror;
|
||||||
bool read_only = false;
|
bool read_only = false;
|
||||||
bool copy_on_read;
|
bool copy_on_read;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
@@ -688,7 +691,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0,
|
legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
|
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -776,6 +779,10 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
translation = BIOS_ATA_TRANSLATION_NONE;
|
translation = BIOS_ATA_TRANSLATION_NONE;
|
||||||
} else if (!strcmp(value, "lba")) {
|
} else if (!strcmp(value, "lba")) {
|
||||||
translation = BIOS_ATA_TRANSLATION_LBA;
|
translation = BIOS_ATA_TRANSLATION_LBA;
|
||||||
|
} else if (!strcmp(value, "large")) {
|
||||||
|
translation = BIOS_ATA_TRANSLATION_LARGE;
|
||||||
|
} else if (!strcmp(value, "rechs")) {
|
||||||
|
translation = BIOS_ATA_TRANSLATION_RECHS;
|
||||||
} else if (!strcmp(value, "auto")) {
|
} else if (!strcmp(value, "auto")) {
|
||||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||||
} else {
|
} else {
|
||||||
@@ -872,16 +879,37 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
|
|
||||||
filename = qemu_opt_get(legacy_opts, "file");
|
filename = qemu_opt_get(legacy_opts, "file");
|
||||||
|
|
||||||
|
/* Check werror/rerror compatibility with if=... */
|
||||||
|
werror = qemu_opt_get(legacy_opts, "werror");
|
||||||
|
if (werror != NULL) {
|
||||||
|
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO &&
|
||||||
|
type != IF_NONE) {
|
||||||
|
error_report("werror is not supported by this bus type");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
qdict_put(bs_opts, "werror", qstring_from_str(werror));
|
||||||
|
}
|
||||||
|
|
||||||
|
rerror = qemu_opt_get(legacy_opts, "rerror");
|
||||||
|
if (rerror != NULL) {
|
||||||
|
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI &&
|
||||||
|
type != IF_NONE) {
|
||||||
|
error_report("rerror is not supported by this bus type");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
qdict_put(bs_opts, "rerror", qstring_from_str(rerror));
|
||||||
|
}
|
||||||
|
|
||||||
/* Actual block device init: Functionality shared with blockdev-add */
|
/* Actual block device init: Functionality shared with blockdev-add */
|
||||||
dinfo = blockdev_init(filename, bs_opts, type, &local_err);
|
dinfo = blockdev_init(filename, bs_opts, &local_err);
|
||||||
if (dinfo == NULL) {
|
if (dinfo == NULL) {
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
}
|
}
|
||||||
goto fail;
|
goto fail;
|
||||||
} else {
|
} else {
|
||||||
assert(!error_is_set(&local_err));
|
assert(!local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set legacy DriveInfo fields */
|
/* Set legacy DriveInfo fields */
|
||||||
@@ -893,6 +921,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
dinfo->secs = secs;
|
dinfo->secs = secs;
|
||||||
dinfo->trans = translation;
|
dinfo->trans = translation;
|
||||||
|
|
||||||
|
dinfo->type = type;
|
||||||
dinfo->bus = bus_id;
|
dinfo->bus = bus_id;
|
||||||
dinfo->unit = unit_id;
|
dinfo->unit = unit_id;
|
||||||
dinfo->devaddr = devaddr;
|
dinfo->devaddr = devaddr;
|
||||||
@@ -1017,7 +1046,7 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
|
ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1030,7 +1059,7 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bdrv_snapshot_delete(bs, id, name, &local_err);
|
bdrv_snapshot_delete(bs, id, name, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1244,7 +1273,7 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
|||||||
state->old_bs = bdrv_lookup_bs(has_device ? device : NULL,
|
state->old_bs = bdrv_lookup_bs(has_device ? device : NULL,
|
||||||
has_node_name ? node_name : NULL,
|
has_node_name ? node_name : NULL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1289,7 +1318,7 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
|||||||
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, -1, flags, &local_err, false);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1301,17 +1330,15 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
|||||||
qstring_from_str(snapshot_node_name));
|
qstring_from_str(snapshot_node_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We will manually add the backing_hd field to the bs later */
|
|
||||||
state->new_bs = bdrv_new("");
|
|
||||||
/* TODO Inherit bs->options or only take explicit options with an
|
/* TODO Inherit bs->options or only take explicit options with an
|
||||||
* extended QMP command? */
|
* extended QMP command? */
|
||||||
ret = bdrv_open(state->new_bs, new_image_file, options,
|
assert(state->new_bs == NULL);
|
||||||
|
ret = bdrv_open(&state->new_bs, new_image_file, NULL, options,
|
||||||
flags | BDRV_O_NO_BACKING, drv, &local_err);
|
flags | BDRV_O_NO_BACKING, drv, &local_err);
|
||||||
|
/* We will manually add the backing_hd field to the bs later */
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDECREF(options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void external_snapshot_commit(BlkTransactionState *common)
|
static void external_snapshot_commit(BlkTransactionState *common)
|
||||||
@@ -1360,7 +1387,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
|
|||||||
backup->has_on_source_error, backup->on_source_error,
|
backup->has_on_source_error, backup->on_source_error,
|
||||||
backup->has_on_target_error, backup->on_target_error,
|
backup->has_on_target_error, backup->on_target_error,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
state->bs = NULL;
|
state->bs = NULL;
|
||||||
state->job = NULL;
|
state->job = NULL;
|
||||||
@@ -1452,7 +1479,7 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp)
|
|||||||
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
|
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
|
||||||
|
|
||||||
state->ops->prepare(state, &local_err);
|
state->ops->prepare(state, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto delete_and_fail;
|
goto delete_and_fail;
|
||||||
}
|
}
|
||||||
@@ -1533,7 +1560,7 @@ void qmp_block_passwd(bool has_device, const char *device,
|
|||||||
bs = bdrv_lookup_bs(has_device ? device : NULL,
|
bs = bdrv_lookup_bs(has_device ? device : NULL,
|
||||||
has_node_name ? node_name : NULL,
|
has_node_name ? node_name : NULL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1555,7 +1582,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err);
|
ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
@@ -1598,7 +1625,7 @@ void qmp_change_blockdev(const char *device, const char *filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
eject_device(bs, 0, &err);
|
eject_device(bs, 0, &err);
|
||||||
if (error_is_set(&err)) {
|
if (err) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1735,7 +1762,7 @@ void qmp_block_resize(bool has_device, const char *device,
|
|||||||
bs = bdrv_lookup_bs(has_device ? device : NULL,
|
bs = bdrv_lookup_bs(has_device ? device : NULL,
|
||||||
has_node_name ? node_name : NULL,
|
has_node_name ? node_name : NULL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1828,7 +1855,7 @@ void qmp_block_stream(const char *device, bool has_base,
|
|||||||
|
|
||||||
stream_start(bs, base_bs, base, has_speed ? speed : 0,
|
stream_start(bs, base_bs, base, has_speed ? speed : 0,
|
||||||
on_error, block_job_cb, bs, &local_err);
|
on_error, block_job_cb, bs, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1986,15 +2013,14 @@ void qmp_drive_backup(const char *device, const char *target,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_bs = bdrv_new("");
|
target_bs = NULL;
|
||||||
ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err);
|
ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_unref(target_bs);
|
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2127,7 +2153,7 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2135,11 +2161,10 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||||||
/* Mirroring takes care of copy-on-write using the source's backing
|
/* Mirroring takes care of copy-on-write using the source's backing
|
||||||
* file.
|
* file.
|
||||||
*/
|
*/
|
||||||
target_bs = bdrv_new("");
|
target_bs = NULL;
|
||||||
ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv,
|
ret = bdrv_open(&target_bs, target, NULL, NULL, flags | BDRV_O_NO_BACKING,
|
||||||
&local_err);
|
drv, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_unref(target_bs);
|
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2266,7 +2291,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
|||||||
|
|
||||||
visit_type_BlockdevOptions(qmp_output_get_visitor(ov),
|
visit_type_BlockdevOptions(qmp_output_get_visitor(ov),
|
||||||
&options, NULL, &local_err);
|
&options, NULL, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -2276,8 +2301,8 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
|
|||||||
|
|
||||||
qdict_flatten(qdict);
|
qdict_flatten(qdict);
|
||||||
|
|
||||||
blockdev_init(NULL, qdict, IF_NONE, &local_err);
|
blockdev_init(NULL, qdict, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
block_job_set_speed(job, speed, &local_err);
|
block_job_set_speed(job, speed, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
bs->job = NULL;
|
bs->job = NULL;
|
||||||
g_free(job);
|
g_free(job);
|
||||||
bdrv_set_in_use(bs, 0);
|
bdrv_set_in_use(bs, 0);
|
||||||
@@ -92,7 +92,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
job->driver->set_speed(job, speed, &local_err);
|
job->driver->set_speed(job, speed, &local_err);
|
||||||
if (error_is_set(&local_err)) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
459
configure
vendored
459
configure
vendored
@@ -12,7 +12,10 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
|
TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
|
||||||
TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
|
TMPB="qemu-conf-${RANDOM}-$$-${RANDOM}"
|
||||||
|
TMPO="${TMPDIR1}/${TMPB}.o"
|
||||||
|
TMPL="${TMPDIR1}/${TMPB}.lo"
|
||||||
|
TMPA="${TMPDIR1}/lib${TMPB}.la"
|
||||||
TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe"
|
TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe"
|
||||||
|
|
||||||
# NB: do not call "exit" in the trap handler; this is buggy with some shells;
|
# NB: do not call "exit" in the trap handler; this is buggy with some shells;
|
||||||
@@ -86,6 +89,38 @@ compile_prog() {
|
|||||||
do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags
|
do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
do_libtool() {
|
||||||
|
local mode=$1
|
||||||
|
shift
|
||||||
|
# Run the compiler, capturing its output to the log.
|
||||||
|
echo $libtool $mode --tag=CC $cc "$@" >> config.log
|
||||||
|
$libtool $mode --tag=CC $cc "$@" >> config.log 2>&1 || return $?
|
||||||
|
# Test passed. If this is an --enable-werror build, rerun
|
||||||
|
# the test with -Werror and bail out if it fails. This
|
||||||
|
# makes warning-generating-errors in configure test code
|
||||||
|
# obvious to developers.
|
||||||
|
if test "$werror" != "yes"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# Don't bother rerunning the compile if we were already using -Werror
|
||||||
|
case "$*" in
|
||||||
|
*-Werror*)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
echo $libtool $mode --tag=CC $cc -Werror "$@" >> config.log
|
||||||
|
$libtool $mode --tag=CC $cc -Werror "$@" >> config.log 2>&1 && return $?
|
||||||
|
error_exit "configure test passed without -Werror but failed with -Werror." \
|
||||||
|
"This is probably a bug in the configure script. The failing command" \
|
||||||
|
"will be at the bottom of config.log." \
|
||||||
|
"You can run configure with --disable-werror to bypass this check."
|
||||||
|
}
|
||||||
|
|
||||||
|
libtool_prog() {
|
||||||
|
do_libtool --mode=compile $QEMU_CFLAGS -c -fPIE -DPIE -o $TMPO $TMPC || return $?
|
||||||
|
do_libtool --mode=link $LDFLAGS -o $TMPA $TMPL -rpath /usr/local/lib
|
||||||
|
}
|
||||||
|
|
||||||
# symbolically link $1 to $2. Portable version of "ln -sf".
|
# symbolically link $1 to $2. Portable version of "ln -sf".
|
||||||
symlink() {
|
symlink() {
|
||||||
rm -rf "$2"
|
rm -rf "$2"
|
||||||
@@ -172,6 +207,7 @@ fdt=""
|
|||||||
netmap="no"
|
netmap="no"
|
||||||
pixman=""
|
pixman=""
|
||||||
sdl=""
|
sdl=""
|
||||||
|
sdlabi="1.2"
|
||||||
virtfs=""
|
virtfs=""
|
||||||
vnc="yes"
|
vnc="yes"
|
||||||
sparse="no"
|
sparse="no"
|
||||||
@@ -205,6 +241,9 @@ mingw32="no"
|
|||||||
gcov="no"
|
gcov="no"
|
||||||
gcov_tool="gcov"
|
gcov_tool="gcov"
|
||||||
EXESUF=""
|
EXESUF=""
|
||||||
|
DSOSUF=".so"
|
||||||
|
LDFLAGS_SHARED="-shared"
|
||||||
|
modules="no"
|
||||||
prefix="/usr/local"
|
prefix="/usr/local"
|
||||||
mandir="\${prefix}/share/man"
|
mandir="\${prefix}/share/man"
|
||||||
datadir="\${prefix}/share"
|
datadir="\${prefix}/share"
|
||||||
@@ -245,12 +284,15 @@ libusb=""
|
|||||||
usb_redir=""
|
usb_redir=""
|
||||||
glx=""
|
glx=""
|
||||||
zlib="yes"
|
zlib="yes"
|
||||||
|
lzo="no"
|
||||||
|
snappy="no"
|
||||||
guest_agent=""
|
guest_agent=""
|
||||||
guest_agent_with_vss="no"
|
guest_agent_with_vss="no"
|
||||||
vss_win32_sdk=""
|
vss_win32_sdk=""
|
||||||
win_sdk="no"
|
win_sdk="no"
|
||||||
want_tools="yes"
|
want_tools="yes"
|
||||||
libiscsi=""
|
libiscsi=""
|
||||||
|
libnfs=""
|
||||||
coroutine=""
|
coroutine=""
|
||||||
coroutine_pool=""
|
coroutine_pool=""
|
||||||
seccomp=""
|
seccomp=""
|
||||||
@@ -263,6 +305,7 @@ gtkabi="2.0"
|
|||||||
tpm="no"
|
tpm="no"
|
||||||
libssh2=""
|
libssh2=""
|
||||||
vhdx=""
|
vhdx=""
|
||||||
|
quorum="no"
|
||||||
|
|
||||||
# parse CC options first
|
# parse CC options first
|
||||||
for opt do
|
for opt do
|
||||||
@@ -324,6 +367,7 @@ query_pkg_config() {
|
|||||||
}
|
}
|
||||||
pkg_config=query_pkg_config
|
pkg_config=query_pkg_config
|
||||||
sdl_config="${SDL_CONFIG-${cross_prefix}sdl-config}"
|
sdl_config="${SDL_CONFIG-${cross_prefix}sdl-config}"
|
||||||
|
sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}"
|
||||||
|
|
||||||
# If the user hasn't specified ARFLAGS, default to 'rv', just as make does.
|
# If the user hasn't specified ARFLAGS, default to 'rv', just as make does.
|
||||||
ARFLAGS="${ARFLAGS-rv}"
|
ARFLAGS="${ARFLAGS-rv}"
|
||||||
@@ -514,11 +558,10 @@ OpenBSD)
|
|||||||
Darwin)
|
Darwin)
|
||||||
bsd="yes"
|
bsd="yes"
|
||||||
darwin="yes"
|
darwin="yes"
|
||||||
|
LDFLAGS_SHARED="-bundle -undefined dynamic_lookup"
|
||||||
if [ "$cpu" = "x86_64" ] ; then
|
if [ "$cpu" = "x86_64" ] ; then
|
||||||
QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
|
QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
|
||||||
LDFLAGS="-arch x86_64 $LDFLAGS"
|
LDFLAGS="-arch x86_64 $LDFLAGS"
|
||||||
else
|
|
||||||
QEMU_CFLAGS="-mdynamic-no-pic $QEMU_CFLAGS"
|
|
||||||
fi
|
fi
|
||||||
cocoa="yes"
|
cocoa="yes"
|
||||||
audio_drv_list="coreaudio"
|
audio_drv_list="coreaudio"
|
||||||
@@ -609,6 +652,7 @@ fi
|
|||||||
|
|
||||||
if test "$mingw32" = "yes" ; then
|
if test "$mingw32" = "yes" ; then
|
||||||
EXESUF=".exe"
|
EXESUF=".exe"
|
||||||
|
DSOSUF=".dll"
|
||||||
QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS"
|
QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS"
|
||||||
# enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later)
|
# enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later)
|
||||||
QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS"
|
QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS"
|
||||||
@@ -675,6 +719,9 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--disable-debug-info)
|
--disable-debug-info)
|
||||||
;;
|
;;
|
||||||
|
--enable-modules)
|
||||||
|
modules="yes"
|
||||||
|
;;
|
||||||
--cpu=*)
|
--cpu=*)
|
||||||
;;
|
;;
|
||||||
--target-list=*) target_list="$optarg"
|
--target-list=*) target_list="$optarg"
|
||||||
@@ -730,6 +777,8 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--enable-sdl) sdl="yes"
|
--enable-sdl) sdl="yes"
|
||||||
;;
|
;;
|
||||||
|
--with-sdlabi=*) sdlabi="$optarg"
|
||||||
|
;;
|
||||||
--disable-qom-cast-debug) qom_cast_debug="no"
|
--disable-qom-cast-debug) qom_cast_debug="no"
|
||||||
;;
|
;;
|
||||||
--enable-qom-cast-debug) qom_cast_debug="yes"
|
--enable-qom-cast-debug) qom_cast_debug="yes"
|
||||||
@@ -840,6 +889,10 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--enable-libiscsi) libiscsi="yes"
|
--enable-libiscsi) libiscsi="yes"
|
||||||
;;
|
;;
|
||||||
|
--disable-libnfs) libnfs="no"
|
||||||
|
;;
|
||||||
|
--enable-libnfs) libnfs="yes"
|
||||||
|
;;
|
||||||
--enable-profiler) profiler="yes"
|
--enable-profiler) profiler="yes"
|
||||||
;;
|
;;
|
||||||
--disable-cocoa) cocoa="no"
|
--disable-cocoa) cocoa="no"
|
||||||
@@ -948,6 +1001,10 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--disable-zlib-test) zlib="no"
|
--disable-zlib-test) zlib="no"
|
||||||
;;
|
;;
|
||||||
|
--enable-lzo) lzo="yes"
|
||||||
|
;;
|
||||||
|
--enable-snappy) snappy="yes"
|
||||||
|
;;
|
||||||
--enable-guest-agent) guest_agent="yes"
|
--enable-guest-agent) guest_agent="yes"
|
||||||
;;
|
;;
|
||||||
--disable-guest-agent) guest_agent="no"
|
--disable-guest-agent) guest_agent="no"
|
||||||
@@ -1000,6 +1057,10 @@ for opt do
|
|||||||
;;
|
;;
|
||||||
--disable-vhdx) vhdx="no"
|
--disable-vhdx) vhdx="no"
|
||||||
;;
|
;;
|
||||||
|
--disable-quorum) quorum="no"
|
||||||
|
;;
|
||||||
|
--enable-quorum) quorum="yes"
|
||||||
|
;;
|
||||||
*) echo "ERROR: unknown option $opt"; show_help="yes"
|
*) echo "ERROR: unknown option $opt"; show_help="yes"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -1126,7 +1187,8 @@ Advanced options (experts only):
|
|||||||
--libdir=PATH install libraries in PATH
|
--libdir=PATH install libraries in PATH
|
||||||
--sysconfdir=PATH install config in PATH$confsuffix
|
--sysconfdir=PATH install config in PATH$confsuffix
|
||||||
--localstatedir=PATH install local state in PATH (set at runtime on win32)
|
--localstatedir=PATH install local state in PATH (set at runtime on win32)
|
||||||
--with-confsuffix=SUFFIX suffix for QEMU data inside datadir and sysconfdir [$confsuffix]
|
--with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [$confsuffix]
|
||||||
|
--enable-modules enable modules support
|
||||||
--enable-debug-tcg enable TCG debugging
|
--enable-debug-tcg enable TCG debugging
|
||||||
--disable-debug-tcg disable TCG debugging (default)
|
--disable-debug-tcg disable TCG debugging (default)
|
||||||
--enable-debug-info enable debugging information (default)
|
--enable-debug-info enable debugging information (default)
|
||||||
@@ -1138,6 +1200,7 @@ Advanced options (experts only):
|
|||||||
--disable-werror disable compilation abort on warning
|
--disable-werror disable compilation abort on warning
|
||||||
--disable-sdl disable SDL
|
--disable-sdl disable SDL
|
||||||
--enable-sdl enable SDL
|
--enable-sdl enable SDL
|
||||||
|
--with-sdlabi select preferred SDL ABI 1.2 or 2.0
|
||||||
--disable-gtk disable gtk UI
|
--disable-gtk disable gtk UI
|
||||||
--enable-gtk enable gtk UI
|
--enable-gtk enable gtk UI
|
||||||
--disable-virtfs disable VirtFS
|
--disable-virtfs disable VirtFS
|
||||||
@@ -1229,12 +1292,16 @@ Advanced options (experts only):
|
|||||||
--enable-rbd enable building the rados block device (rbd)
|
--enable-rbd enable building the rados block device (rbd)
|
||||||
--disable-libiscsi disable iscsi support
|
--disable-libiscsi disable iscsi support
|
||||||
--enable-libiscsi enable iscsi support
|
--enable-libiscsi enable iscsi support
|
||||||
|
--disable-libnfs disable nfs support
|
||||||
|
--enable-libnfs enable nfs support
|
||||||
--disable-smartcard-nss disable smartcard nss support
|
--disable-smartcard-nss disable smartcard nss support
|
||||||
--enable-smartcard-nss enable smartcard nss support
|
--enable-smartcard-nss enable smartcard nss support
|
||||||
--disable-libusb disable libusb (for usb passthrough)
|
--disable-libusb disable libusb (for usb passthrough)
|
||||||
--enable-libusb enable libusb (for usb passthrough)
|
--enable-libusb enable libusb (for usb passthrough)
|
||||||
--disable-usb-redir disable usb network redirection support
|
--disable-usb-redir disable usb network redirection support
|
||||||
--enable-usb-redir enable usb network redirection support
|
--enable-usb-redir enable usb network redirection support
|
||||||
|
--enable-lzo enable the support of lzo compression library
|
||||||
|
--enable-snappy enable the support of snappy compression library
|
||||||
--disable-guest-agent disable building of the QEMU Guest Agent
|
--disable-guest-agent disable building of the QEMU Guest Agent
|
||||||
--enable-guest-agent enable building of the QEMU Guest Agent
|
--enable-guest-agent enable building of the QEMU Guest Agent
|
||||||
--with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent
|
--with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent
|
||||||
@@ -1254,6 +1321,8 @@ Advanced options (experts only):
|
|||||||
--enable-libssh2 enable ssh block device support
|
--enable-libssh2 enable ssh block device support
|
||||||
--disable-vhdx disables support for the Microsoft VHDX image format
|
--disable-vhdx disables support for the Microsoft VHDX image format
|
||||||
--enable-vhdx enable support for the Microsoft VHDX image format
|
--enable-vhdx enable support for the Microsoft VHDX image format
|
||||||
|
--disable-quorum disable quorum block filter support
|
||||||
|
--enable-quorum enable quorum block filter support
|
||||||
|
|
||||||
NOTE: The object files are built at the place where configure is launched
|
NOTE: The object files are built at the place where configure is launched
|
||||||
EOF
|
EOF
|
||||||
@@ -1282,6 +1351,35 @@ else
|
|||||||
error_exit "\"$cc\" either does not exist or does not work"
|
error_exit "\"$cc\" either does not exist or does not work"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check that the C++ compiler exists and works with the C compiler
|
||||||
|
if has $cxx; then
|
||||||
|
cat > $TMPC <<EOF
|
||||||
|
int c_function(void);
|
||||||
|
int main(void) { return c_function(); }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
compile_object
|
||||||
|
|
||||||
|
cat > $TMPC <<EOF
|
||||||
|
extern "C" {
|
||||||
|
int c_function(void);
|
||||||
|
}
|
||||||
|
int c_function(void) { return 42; }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if (cc=$cxx do_cc $QEMU_CFLAGS -o $TMPE $TMPC $TMPO $LDFLAGS); then
|
||||||
|
# C++ compiler $cxx works ok with C compiler $cc
|
||||||
|
:
|
||||||
|
else
|
||||||
|
echo "C++ compiler $cxx does not work with C compiler $cc"
|
||||||
|
echo "Disabling C++ specific optional code"
|
||||||
|
cxx=
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No C++ compiler available; disabling C++ specific optional code"
|
||||||
|
cxx=
|
||||||
|
fi
|
||||||
|
|
||||||
# Consult white-list to determine whether to enable werror
|
# Consult white-list to determine whether to enable werror
|
||||||
# by default. Only enable by default for git builds
|
# by default. Only enable by default for git builds
|
||||||
z_version=`cut -f3 -d. $source_path/VERSION`
|
z_version=`cut -f3 -d. $source_path/VERSION`
|
||||||
@@ -1339,6 +1437,9 @@ if compile_prog "-Werror -fno-gcse" "" ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$static" = "yes" ; then
|
if test "$static" = "yes" ; then
|
||||||
|
if test "$modules" = "yes" ; then
|
||||||
|
error_exit "static and modules are mutually incompatible"
|
||||||
|
fi
|
||||||
if test "$pie" = "yes" ; then
|
if test "$pie" = "yes" ; then
|
||||||
error_exit "static and pie are mutually incompatible"
|
error_exit "static and pie are mutually incompatible"
|
||||||
else
|
else
|
||||||
@@ -1385,6 +1486,37 @@ EOF
|
|||||||
pie="no"
|
pie="no"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if compile_prog "-fno-pie" "-nopie"; then
|
||||||
|
CFLAGS_NOPIE="-fno-pie"
|
||||||
|
LDFLAGS_NOPIE="-nopie"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check for broken gcc and libtool in RHEL5
|
||||||
|
if test -n "$libtool" -a "$pie" != "no" ; then
|
||||||
|
cat > $TMPC <<EOF
|
||||||
|
|
||||||
|
void *f(unsigned char *buf, int len);
|
||||||
|
void *g(unsigned char *buf, int len);
|
||||||
|
|
||||||
|
void *
|
||||||
|
f(unsigned char *buf, int len)
|
||||||
|
{
|
||||||
|
return (void*)0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
g(unsigned char *buf, int len)
|
||||||
|
{
|
||||||
|
return f(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
if ! libtool_prog; then
|
||||||
|
echo "Disabling libtool due to broken toolchain support"
|
||||||
|
libtool=
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
@@ -1467,9 +1599,11 @@ esac
|
|||||||
|
|
||||||
feature_not_found() {
|
feature_not_found() {
|
||||||
feature=$1
|
feature=$1
|
||||||
|
remedy=$2
|
||||||
|
|
||||||
error_exit "User requested feature $feature" \
|
error_exit "User requested feature $feature" \
|
||||||
"configure was not able to find it"
|
"configure was not able to find it." \
|
||||||
|
"$remedy"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
@@ -1517,7 +1651,7 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
if ! compile_object ; then
|
if ! compile_object ; then
|
||||||
feature_not_found "nptl"
|
feature_not_found "nptl" "Install glibc and linux kernel headers."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1536,7 +1670,43 @@ EOF
|
|||||||
"Make sure to have the zlib libs and headers installed."
|
"Make sure to have the zlib libs and headers installed."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
libs_softmmu="$libs_softmmu -lz"
|
LIBS="$LIBS -lz"
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# lzo check
|
||||||
|
|
||||||
|
if test "$lzo" != "no" ; then
|
||||||
|
cat > $TMPC << EOF
|
||||||
|
#include <lzo/lzo1x.h>
|
||||||
|
int main(void) { lzo_version(); return 0; }
|
||||||
|
EOF
|
||||||
|
if compile_prog "" "-llzo2" ; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
error_exit "lzo check failed" \
|
||||||
|
"Make sure to have the lzo libs and headers installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
libs_softmmu="$libs_softmmu -llzo2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# snappy check
|
||||||
|
|
||||||
|
if test "$snappy" != "no" ; then
|
||||||
|
cat > $TMPC << EOF
|
||||||
|
#include <snappy-c.h>
|
||||||
|
int main(void) { snappy_max_compressed_length(4096); return 0; }
|
||||||
|
EOF
|
||||||
|
if compile_prog "" "-lsnappy" ; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
error_exit "snappy check failed" \
|
||||||
|
"Make sure to have the snappy libs and headers installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
libs_softmmu="$libs_softmmu -lsnappy"
|
||||||
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# libseccomp check
|
# libseccomp check
|
||||||
@@ -1548,7 +1718,7 @@ if test "$seccomp" != "no" ; then
|
|||||||
seccomp="yes"
|
seccomp="yes"
|
||||||
else
|
else
|
||||||
if test "$seccomp" = "yes"; then
|
if test "$seccomp" = "yes"; then
|
||||||
feature_not_found "libseccomp"
|
feature_not_found "libseccomp" "Install libseccomp devel >= 2.1.0"
|
||||||
fi
|
fi
|
||||||
seccomp="no"
|
seccomp="no"
|
||||||
fi
|
fi
|
||||||
@@ -1573,7 +1743,7 @@ EOF
|
|||||||
if ! compile_prog "" "$xen_libs" ; then
|
if ! compile_prog "" "$xen_libs" ; then
|
||||||
# Xen not found
|
# Xen not found
|
||||||
if test "$xen" = "yes" ; then
|
if test "$xen" = "yes" ; then
|
||||||
feature_not_found "xen"
|
feature_not_found "xen" "Install xen devel"
|
||||||
fi
|
fi
|
||||||
xen=no
|
xen=no
|
||||||
|
|
||||||
@@ -1696,7 +1866,7 @@ EOF
|
|||||||
# Xen version unsupported
|
# Xen version unsupported
|
||||||
else
|
else
|
||||||
if test "$xen" = "yes" ; then
|
if test "$xen" = "yes" ; then
|
||||||
feature_not_found "xen (unsupported version)"
|
feature_not_found "xen (unsupported version)" "Install supported xen (e.g. 4.0, 3.4, 3.3)"
|
||||||
fi
|
fi
|
||||||
xen=no
|
xen=no
|
||||||
fi
|
fi
|
||||||
@@ -1745,7 +1915,7 @@ if test "$sparse" != "no" ; then
|
|||||||
sparse=yes
|
sparse=yes
|
||||||
else
|
else
|
||||||
if test "$sparse" = "yes" ; then
|
if test "$sparse" = "yes" ; then
|
||||||
feature_not_found "sparse"
|
feature_not_found "sparse" "Install sparse binary"
|
||||||
fi
|
fi
|
||||||
sparse=no
|
sparse=no
|
||||||
fi
|
fi
|
||||||
@@ -1767,7 +1937,7 @@ if test "$gtk" != "no"; then
|
|||||||
fi
|
fi
|
||||||
if ! $pkg_config --exists "$gtkpackage >= $gtkversion"; then
|
if ! $pkg_config --exists "$gtkpackage >= $gtkversion"; then
|
||||||
if test "$gtk" = "yes" ; then
|
if test "$gtk" = "yes" ; then
|
||||||
feature_not_found "gtk"
|
feature_not_found "gtk" "Install gtk2 or gtk3 (requires --with-gtkabi=3.0 option to configure) devel"
|
||||||
fi
|
fi
|
||||||
gtk="no"
|
gtk="no"
|
||||||
elif ! $pkg_config --exists "$vtepackage >= $vteversion"; then
|
elif ! $pkg_config --exists "$vtepackage >= $vteversion"; then
|
||||||
@@ -1790,19 +1960,29 @@ fi
|
|||||||
|
|
||||||
# Look for sdl configuration program (pkg-config or sdl-config). Try
|
# Look for sdl configuration program (pkg-config or sdl-config). Try
|
||||||
# sdl-config even without cross prefix, and favour pkg-config over sdl-config.
|
# sdl-config even without cross prefix, and favour pkg-config over sdl-config.
|
||||||
if test "`basename $sdl_config`" != sdl-config && ! has ${sdl_config}; then
|
|
||||||
sdl_config=sdl-config
|
if test $sdlabi = "2.0"; then
|
||||||
|
sdl_config=$sdl2_config
|
||||||
|
sdlname=sdl2
|
||||||
|
sdlconfigname=sdl2_config
|
||||||
|
else
|
||||||
|
sdlname=sdl
|
||||||
|
sdlconfigname=sdl_config
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if $pkg_config sdl --exists; then
|
if test "`basename $sdl_config`" != $sdlconfigname && ! has ${sdl_config}; then
|
||||||
sdlconfig="$pkg_config sdl"
|
sdl_config=$sdlconfigname
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $pkg_config $sdlname --exists; then
|
||||||
|
sdlconfig="$pkg_config $sdlname"
|
||||||
_sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'`
|
_sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'`
|
||||||
elif has ${sdl_config}; then
|
elif has ${sdl_config}; then
|
||||||
sdlconfig="$sdl_config"
|
sdlconfig="$sdl_config"
|
||||||
_sdlversion=`$sdlconfig --version | sed 's/[^0-9]//g'`
|
_sdlversion=`$sdlconfig --version | sed 's/[^0-9]//g'`
|
||||||
else
|
else
|
||||||
if test "$sdl" = "yes" ; then
|
if test "$sdl" = "yes" ; then
|
||||||
feature_not_found "sdl"
|
feature_not_found "sdl" "Install SDL devel"
|
||||||
fi
|
fi
|
||||||
sdl=no
|
sdl=no
|
||||||
fi
|
fi
|
||||||
@@ -1846,7 +2026,7 @@ EOF
|
|||||||
fi # static link
|
fi # static link
|
||||||
else # sdl not found
|
else # sdl not found
|
||||||
if test "$sdl" = "yes" ; then
|
if test "$sdl" = "yes" ; then
|
||||||
feature_not_found "sdl"
|
feature_not_found "sdl" "Install SDL devel"
|
||||||
fi
|
fi
|
||||||
sdl=no
|
sdl=no
|
||||||
fi # sdl compile test
|
fi # sdl compile test
|
||||||
@@ -1912,16 +2092,40 @@ EOF
|
|||||||
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
|
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
|
||||||
else
|
else
|
||||||
if test "$vnc_tls" = "yes" ; then
|
if test "$vnc_tls" = "yes" ; then
|
||||||
feature_not_found "vnc-tls"
|
feature_not_found "vnc-tls" "Install gnutls devel"
|
||||||
fi
|
fi
|
||||||
if test "$vnc_ws" = "yes" ; then
|
if test "$vnc_ws" = "yes" ; then
|
||||||
feature_not_found "vnc-ws"
|
feature_not_found "vnc-ws" "Install gnutls devel"
|
||||||
fi
|
fi
|
||||||
vnc_tls=no
|
vnc_tls=no
|
||||||
vnc_ws=no
|
vnc_ws=no
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# Quorum probe (check for gnutls)
|
||||||
|
if test "$quorum" != "no" ; then
|
||||||
|
cat > $TMPC <<EOF
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
#include <gnutls/crypto.h>
|
||||||
|
int main(void) {char data[4096], digest[32];
|
||||||
|
gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
|
||||||
|
quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
|
||||||
|
if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then
|
||||||
|
qcow_tls=yes
|
||||||
|
libs_softmmu="$quorum_tls_libs $libs_softmmu"
|
||||||
|
libs_tools="$quorum_tls_libs $libs_softmmu"
|
||||||
|
QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags"
|
||||||
|
else
|
||||||
|
echo "gnutls > 2.10.0 required to compile Quorum"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# VNC SASL detection
|
# VNC SASL detection
|
||||||
if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then
|
if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then
|
||||||
@@ -1939,7 +2143,7 @@ EOF
|
|||||||
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_sasl_cflags"
|
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_sasl_cflags"
|
||||||
else
|
else
|
||||||
if test "$vnc_sasl" = "yes" ; then
|
if test "$vnc_sasl" = "yes" ; then
|
||||||
feature_not_found "vnc-sasl"
|
feature_not_found "vnc-sasl" "Install Cyrus SASL devel"
|
||||||
fi
|
fi
|
||||||
vnc_sasl=no
|
vnc_sasl=no
|
||||||
fi
|
fi
|
||||||
@@ -1961,7 +2165,7 @@ EOF
|
|||||||
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_jpeg_cflags"
|
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_jpeg_cflags"
|
||||||
else
|
else
|
||||||
if test "$vnc_jpeg" = "yes" ; then
|
if test "$vnc_jpeg" = "yes" ; then
|
||||||
feature_not_found "vnc-jpeg"
|
feature_not_found "vnc-jpeg" "Install libjpeg-turbo devel"
|
||||||
fi
|
fi
|
||||||
vnc_jpeg=no
|
vnc_jpeg=no
|
||||||
fi
|
fi
|
||||||
@@ -1993,7 +2197,7 @@ EOF
|
|||||||
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_png_cflags"
|
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_png_cflags"
|
||||||
else
|
else
|
||||||
if test "$vnc_png" = "yes" ; then
|
if test "$vnc_png" = "yes" ; then
|
||||||
feature_not_found "vnc-png"
|
feature_not_found "vnc-png" "Install libpng devel"
|
||||||
fi
|
fi
|
||||||
vnc_png=no
|
vnc_png=no
|
||||||
fi
|
fi
|
||||||
@@ -2037,7 +2241,7 @@ EOF
|
|||||||
libs_tools="$uuid_libs $libs_tools"
|
libs_tools="$uuid_libs $libs_tools"
|
||||||
else
|
else
|
||||||
if test "$uuid" = "yes" ; then
|
if test "$uuid" = "yes" ; then
|
||||||
feature_not_found "uuid"
|
feature_not_found "uuid" "Install libuuid devel"
|
||||||
fi
|
fi
|
||||||
uuid=no
|
uuid=no
|
||||||
fi
|
fi
|
||||||
@@ -2071,7 +2275,7 @@ EOF
|
|||||||
xfs="yes"
|
xfs="yes"
|
||||||
else
|
else
|
||||||
if test "$xfs" = "yes" ; then
|
if test "$xfs" = "yes" ; then
|
||||||
feature_not_found "xfs"
|
feature_not_found "xfs" "Instal xfsprogs/xfslibs devel"
|
||||||
fi
|
fi
|
||||||
xfs=no
|
xfs=no
|
||||||
fi
|
fi
|
||||||
@@ -2097,20 +2301,28 @@ EOF
|
|||||||
libs_tools="$vde_libs $libs_tools"
|
libs_tools="$vde_libs $libs_tools"
|
||||||
else
|
else
|
||||||
if test "$vde" = "yes" ; then
|
if test "$vde" = "yes" ; then
|
||||||
feature_not_found "vde"
|
feature_not_found "vde" "Install vde (Virtual Distributed Ethernet) devel"
|
||||||
fi
|
fi
|
||||||
vde=no
|
vde=no
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# netmap headers probe
|
# netmap support probe
|
||||||
|
# Apart from looking for netmap headers, we make sure that the host API version
|
||||||
|
# supports the netmap backend (>=11). The upper bound (15) is meant to simulate
|
||||||
|
# a minor/major version number. Minor new features will be marked with values up
|
||||||
|
# to 15, and if something happens that requires a change to the backend we will
|
||||||
|
# move above 15, submit the backend fixes and modify this two bounds.
|
||||||
if test "$netmap" != "no" ; then
|
if test "$netmap" != "no" ; then
|
||||||
cat > $TMPC << EOF
|
cat > $TMPC << EOF
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <net/netmap.h>
|
#include <net/netmap.h>
|
||||||
#include <net/netmap_user.h>
|
#include <net/netmap_user.h>
|
||||||
|
#if (NETMAP_API < 11) || (NETMAP_API > 15)
|
||||||
|
#error
|
||||||
|
#endif
|
||||||
int main(void) { return 0; }
|
int main(void) { return 0; }
|
||||||
EOF
|
EOF
|
||||||
if compile_prog "" "" ; then
|
if compile_prog "" "" ; then
|
||||||
@@ -2140,7 +2352,7 @@ EOF
|
|||||||
libs_tools="$cap_libs $libs_tools"
|
libs_tools="$cap_libs $libs_tools"
|
||||||
else
|
else
|
||||||
if test "$cap_ng" = "yes" ; then
|
if test "$cap_ng" = "yes" ; then
|
||||||
feature_not_found "cap_ng"
|
feature_not_found "cap_ng" "Install libcap-ng devel"
|
||||||
fi
|
fi
|
||||||
cap_ng=no
|
cap_ng=no
|
||||||
fi
|
fi
|
||||||
@@ -2245,7 +2457,7 @@ EOF
|
|||||||
libs_softmmu="$brlapi_libs $libs_softmmu"
|
libs_softmmu="$brlapi_libs $libs_softmmu"
|
||||||
else
|
else
|
||||||
if test "$brlapi" = "yes" ; then
|
if test "$brlapi" = "yes" ; then
|
||||||
feature_not_found "brlapi"
|
feature_not_found "brlapi" "Install brlapi devel"
|
||||||
fi
|
fi
|
||||||
brlapi=no
|
brlapi=no
|
||||||
fi
|
fi
|
||||||
@@ -2282,7 +2494,7 @@ EOF
|
|||||||
curses=yes
|
curses=yes
|
||||||
else
|
else
|
||||||
if test "$curses" = "yes" ; then
|
if test "$curses" = "yes" ; then
|
||||||
feature_not_found "curses"
|
feature_not_found "curses" "Install ncurses devel"
|
||||||
fi
|
fi
|
||||||
curses=no
|
curses=no
|
||||||
fi
|
fi
|
||||||
@@ -2304,11 +2516,9 @@ EOF
|
|||||||
curl_libs=`$curlconfig --libs 2>/dev/null`
|
curl_libs=`$curlconfig --libs 2>/dev/null`
|
||||||
if compile_prog "$curl_cflags" "$curl_libs" ; then
|
if compile_prog "$curl_cflags" "$curl_libs" ; then
|
||||||
curl=yes
|
curl=yes
|
||||||
libs_tools="$curl_libs $libs_tools"
|
|
||||||
libs_softmmu="$curl_libs $libs_softmmu"
|
|
||||||
else
|
else
|
||||||
if test "$curl" = "yes" ; then
|
if test "$curl" = "yes" ; then
|
||||||
feature_not_found "curl"
|
feature_not_found "curl" "Install libcurl devel"
|
||||||
fi
|
fi
|
||||||
curl=no
|
curl=no
|
||||||
fi
|
fi
|
||||||
@@ -2328,7 +2538,7 @@ EOF
|
|||||||
libs_softmmu="$bluez_libs $libs_softmmu"
|
libs_softmmu="$bluez_libs $libs_softmmu"
|
||||||
else
|
else
|
||||||
if test "$bluez" = "yes" ; then
|
if test "$bluez" = "yes" ; then
|
||||||
feature_not_found "bluez"
|
feature_not_found "bluez" "Install bluez-libs/libbluetooth devel"
|
||||||
fi
|
fi
|
||||||
bluez="no"
|
bluez="no"
|
||||||
fi
|
fi
|
||||||
@@ -2343,13 +2553,36 @@ if test "$mingw32" = yes; then
|
|||||||
else
|
else
|
||||||
glib_req_ver=2.12
|
glib_req_ver=2.12
|
||||||
fi
|
fi
|
||||||
if $pkg_config --atleast-version=$glib_req_ver gthread-2.0; then
|
glib_modules=gthread-2.0
|
||||||
glib_cflags=`$pkg_config --cflags gthread-2.0`
|
if test "$modules" = yes; then
|
||||||
glib_libs=`$pkg_config --libs gthread-2.0`
|
glib_modules="$glib_modules gmodule-2.0"
|
||||||
LIBS="$glib_libs $LIBS"
|
fi
|
||||||
libs_qga="$glib_libs $libs_qga"
|
|
||||||
else
|
for i in $glib_modules; do
|
||||||
error_exit "glib-$glib_req_ver required to compile QEMU"
|
if $pkg_config --atleast-version=$glib_req_ver $i; then
|
||||||
|
glib_cflags=`$pkg_config --cflags $i`
|
||||||
|
glib_libs=`$pkg_config --libs $i`
|
||||||
|
CFLAGS="$glib_cflags $CFLAGS"
|
||||||
|
LIBS="$glib_libs $LIBS"
|
||||||
|
libs_qga="$glib_libs $libs_qga"
|
||||||
|
else
|
||||||
|
error_exit "glib-$glib_req_ver $i is required to compile QEMU"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# SHA command probe for modules
|
||||||
|
if test "$modules" = yes; then
|
||||||
|
shacmd_probe="sha1sum sha1 shasum"
|
||||||
|
for c in $shacmd_probe; do
|
||||||
|
if which $c &>/dev/null; then
|
||||||
|
shacmd="$c"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if test "$shacmd" = ""; then
|
||||||
|
error_exit "one of the checksum commands is required to enable modules: $shacmd_probe"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
@@ -2460,11 +2693,9 @@ EOF
|
|||||||
rbd_libs="-lrbd -lrados"
|
rbd_libs="-lrbd -lrados"
|
||||||
if compile_prog "" "$rbd_libs" ; then
|
if compile_prog "" "$rbd_libs" ; then
|
||||||
rbd=yes
|
rbd=yes
|
||||||
libs_tools="$rbd_libs $libs_tools"
|
|
||||||
libs_softmmu="$rbd_libs $libs_softmmu"
|
|
||||||
else
|
else
|
||||||
if test "$rbd" = "yes" ; then
|
if test "$rbd" = "yes" ; then
|
||||||
feature_not_found "rados block device"
|
feature_not_found "rados block device" "Install librbd/ceph devel"
|
||||||
fi
|
fi
|
||||||
rbd=no
|
rbd=no
|
||||||
fi
|
fi
|
||||||
@@ -2478,9 +2709,6 @@ if test "$libssh2" != "no" ; then
|
|||||||
libssh2_cflags=`$pkg_config libssh2 --cflags`
|
libssh2_cflags=`$pkg_config libssh2 --cflags`
|
||||||
libssh2_libs=`$pkg_config libssh2 --libs`
|
libssh2_libs=`$pkg_config libssh2 --libs`
|
||||||
libssh2=yes
|
libssh2=yes
|
||||||
libs_tools="$libssh2_libs $libs_tools"
|
|
||||||
libs_softmmu="$libssh2_libs $libs_softmmu"
|
|
||||||
QEMU_CFLAGS="$QEMU_CFLAGS $libssh2_cflags"
|
|
||||||
else
|
else
|
||||||
if test "$libssh2" = "yes" ; then
|
if test "$libssh2" = "yes" ; then
|
||||||
error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2"
|
error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2"
|
||||||
@@ -2526,11 +2754,9 @@ int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); retu
|
|||||||
EOF
|
EOF
|
||||||
if compile_prog "" "-laio" ; then
|
if compile_prog "" "-laio" ; then
|
||||||
linux_aio=yes
|
linux_aio=yes
|
||||||
libs_softmmu="$libs_softmmu -laio"
|
|
||||||
libs_tools="$libs_tools -laio"
|
|
||||||
else
|
else
|
||||||
if test "$linux_aio" = "yes" ; then
|
if test "$linux_aio" = "yes" ; then
|
||||||
feature_not_found "linux AIO"
|
feature_not_found "linux AIO" "Install libaio devel"
|
||||||
fi
|
fi
|
||||||
linux_aio=no
|
linux_aio=no
|
||||||
fi
|
fi
|
||||||
@@ -2578,7 +2804,7 @@ EOF
|
|||||||
libattr=yes
|
libattr=yes
|
||||||
else
|
else
|
||||||
if test "$attr" = "yes" ; then
|
if test "$attr" = "yes" ; then
|
||||||
feature_not_found "ATTR"
|
feature_not_found "ATTR" "Install libc6 or libattr devel"
|
||||||
fi
|
fi
|
||||||
attr=no
|
attr=no
|
||||||
fi
|
fi
|
||||||
@@ -2655,8 +2881,8 @@ EOF
|
|||||||
fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs"
|
fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs"
|
||||||
elif test "$fdt" = "yes" ; then
|
elif test "$fdt" = "yes" ; then
|
||||||
# have neither and want - prompt for system/submodule install
|
# have neither and want - prompt for system/submodule install
|
||||||
error_exit "DTC not present. Your options:" \
|
error_exit "DTC (libfdt) not present. Your options:" \
|
||||||
" (1) Preferred: Install the DTC devel package" \
|
" (1) Preferred: Install the DTC (libfdt) devel package" \
|
||||||
" (2) Fetch the DTC submodule, using:" \
|
" (2) Fetch the DTC submodule, using:" \
|
||||||
" git submodule update --init dtc"
|
" git submodule update --init dtc"
|
||||||
else
|
else
|
||||||
@@ -2682,7 +2908,7 @@ EOF
|
|||||||
glx=yes
|
glx=yes
|
||||||
else
|
else
|
||||||
if test "$glx" = "yes" ; then
|
if test "$glx" = "yes" ; then
|
||||||
feature_not_found "glx"
|
feature_not_found "glx" "Install GL devel (e.g. MESA)"
|
||||||
fi
|
fi
|
||||||
glx_libs=
|
glx_libs=
|
||||||
glx=no
|
glx=no
|
||||||
@@ -2696,9 +2922,6 @@ if test "$glusterfs" != "no" ; then
|
|||||||
glusterfs="yes"
|
glusterfs="yes"
|
||||||
glusterfs_cflags=`$pkg_config --cflags glusterfs-api`
|
glusterfs_cflags=`$pkg_config --cflags glusterfs-api`
|
||||||
glusterfs_libs=`$pkg_config --libs glusterfs-api`
|
glusterfs_libs=`$pkg_config --libs glusterfs-api`
|
||||||
CFLAGS="$CFLAGS $glusterfs_cflags"
|
|
||||||
libs_tools="$glusterfs_libs $libs_tools"
|
|
||||||
libs_softmmu="$glusterfs_libs $libs_softmmu"
|
|
||||||
if $pkg_config --atleast-version=5 glusterfs-api; then
|
if $pkg_config --atleast-version=5 glusterfs-api; then
|
||||||
glusterfs_discard="yes"
|
glusterfs_discard="yes"
|
||||||
fi
|
fi
|
||||||
@@ -2707,7 +2930,7 @@ if test "$glusterfs" != "no" ; then
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if test "$glusterfs" = "yes" ; then
|
if test "$glusterfs" = "yes" ; then
|
||||||
feature_not_found "GlusterFS backend support"
|
feature_not_found "GlusterFS backend support" "Install glusterfs-api devel"
|
||||||
fi
|
fi
|
||||||
glusterfs="no"
|
glusterfs="no"
|
||||||
fi
|
fi
|
||||||
@@ -3027,7 +3250,7 @@ if test "$docs" != "no" ; then
|
|||||||
docs=yes
|
docs=yes
|
||||||
else
|
else
|
||||||
if test "$docs" = "yes" ; then
|
if test "$docs" = "yes" ; then
|
||||||
feature_not_found "docs"
|
feature_not_found "docs" "Install texinfo and Perl/perl-podlators"
|
||||||
fi
|
fi
|
||||||
docs=no
|
docs=no
|
||||||
fi
|
fi
|
||||||
@@ -3069,14 +3292,12 @@ EOF
|
|||||||
libiscsi="yes"
|
libiscsi="yes"
|
||||||
libiscsi_cflags=$($pkg_config --cflags libiscsi)
|
libiscsi_cflags=$($pkg_config --cflags libiscsi)
|
||||||
libiscsi_libs=$($pkg_config --libs libiscsi)
|
libiscsi_libs=$($pkg_config --libs libiscsi)
|
||||||
CFLAGS="$CFLAGS $libiscsi_cflags"
|
|
||||||
LIBS="$LIBS $libiscsi_libs"
|
|
||||||
elif compile_prog "" "-liscsi" ; then
|
elif compile_prog "" "-liscsi" ; then
|
||||||
libiscsi="yes"
|
libiscsi="yes"
|
||||||
LIBS="$LIBS -liscsi"
|
libiscsi_libs="-liscsi"
|
||||||
else
|
else
|
||||||
if test "$libiscsi" = "yes" ; then
|
if test "$libiscsi" = "yes" ; then
|
||||||
feature_not_found "libiscsi"
|
feature_not_found "libiscsi" "Install libiscsi devel"
|
||||||
fi
|
fi
|
||||||
libiscsi="no"
|
libiscsi="no"
|
||||||
fi
|
fi
|
||||||
@@ -3160,7 +3381,7 @@ EOF
|
|||||||
spice_server_version=$($pkg_config --modversion spice-server)
|
spice_server_version=$($pkg_config --modversion spice-server)
|
||||||
else
|
else
|
||||||
if test "$spice" = "yes" ; then
|
if test "$spice" = "yes" ; then
|
||||||
feature_not_found "spice"
|
feature_not_found "spice" "Install spice-server and spice-protocol devel"
|
||||||
fi
|
fi
|
||||||
spice="no"
|
spice="no"
|
||||||
fi
|
fi
|
||||||
@@ -3210,7 +3431,7 @@ if test "$libusb" != "no" ; then
|
|||||||
libs_softmmu="$libs_softmmu $libusb_libs"
|
libs_softmmu="$libs_softmmu $libusb_libs"
|
||||||
else
|
else
|
||||||
if test "$libusb" = "yes"; then
|
if test "$libusb" = "yes"; then
|
||||||
feature_not_found "libusb"
|
feature_not_found "libusb" "Install libusb devel"
|
||||||
fi
|
fi
|
||||||
libusb="no"
|
libusb="no"
|
||||||
fi
|
fi
|
||||||
@@ -3226,7 +3447,7 @@ if test "$usb_redir" != "no" ; then
|
|||||||
libs_softmmu="$libs_softmmu $usb_redir_libs"
|
libs_softmmu="$libs_softmmu $usb_redir_libs"
|
||||||
else
|
else
|
||||||
if test "$usb_redir" = "yes"; then
|
if test "$usb_redir" = "yes"; then
|
||||||
feature_not_found "usb-redir"
|
feature_not_found "usb-redir" "Install usbredir devel"
|
||||||
fi
|
fi
|
||||||
usb_redir="no"
|
usb_redir="no"
|
||||||
fi
|
fi
|
||||||
@@ -3365,15 +3586,25 @@ fi
|
|||||||
# For 'ust' backend, test if ust headers are present
|
# For 'ust' backend, test if ust headers are present
|
||||||
if test "$trace_backend" = "ust"; then
|
if test "$trace_backend" = "ust"; then
|
||||||
cat > $TMPC << EOF
|
cat > $TMPC << EOF
|
||||||
#include <ust/tracepoint.h>
|
#include <lttng/tracepoint.h>
|
||||||
#include <ust/marker.h>
|
|
||||||
int main(void) { return 0; }
|
int main(void) { return 0; }
|
||||||
EOF
|
EOF
|
||||||
if compile_prog "" "" ; then
|
if compile_prog "" "" ; then
|
||||||
LIBS="-lust -lurcu-bp $LIBS"
|
if $pkg_config lttng-ust --exists; then
|
||||||
libs_qga="-lust -lurcu-bp $libs_qga"
|
lttng_ust_libs=`$pkg_config --libs lttng-ust`
|
||||||
|
else
|
||||||
|
lttng_ust_libs="-llttng-ust"
|
||||||
|
fi
|
||||||
|
if $pkg_config liburcu-bp --exists; then
|
||||||
|
urcu_bp_libs=`$pkg_config --libs liburcu-bp`
|
||||||
|
else
|
||||||
|
urcu_bp_libs="-lurcu-bp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LIBS="$lttng_ust_libs $urcu_bp_libs $LIBS"
|
||||||
|
libs_qga="$lttng_ust_libs $urcu_bp_libs $libs_qga"
|
||||||
else
|
else
|
||||||
error_exit "Trace backend 'ust' missing libust header files"
|
error_exit "Trace backend 'ust' missing lttng-ust header files"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -3550,7 +3781,18 @@ cpuid_h=no
|
|||||||
cat > $TMPC << EOF
|
cat > $TMPC << EOF
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
int main(void) {
|
int main(void) {
|
||||||
return 0;
|
unsigned a, b, c, d;
|
||||||
|
int max = __get_cpuid_max(0, 0);
|
||||||
|
|
||||||
|
if (max >= 1) {
|
||||||
|
__cpuid(1, a, b, c, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max >= 7) {
|
||||||
|
__cpuid_count(7, 0, a, b, c, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
if compile_prog "" "" ; then
|
if compile_prog "" "" ; then
|
||||||
@@ -3600,6 +3842,20 @@ elif test "$debug" = "no" ; then
|
|||||||
CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS"
|
CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
# Do we have libnfs
|
||||||
|
if test "$libnfs" != "no" ; then
|
||||||
|
if $pkg_config --atleast-version=1.9.2 libnfs; then
|
||||||
|
libnfs="yes"
|
||||||
|
libnfs_libs=$($pkg_config --libs libnfs)
|
||||||
|
LIBS="$LIBS $libnfs_libs"
|
||||||
|
else
|
||||||
|
if test "$libnfs" = "yes" ; then
|
||||||
|
feature_not_found "libnfs"
|
||||||
|
fi
|
||||||
|
libnfs="no"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Disable zero malloc errors for official releases unless explicitly told to
|
# Disable zero malloc errors for official releases unless explicitly told to
|
||||||
# enable/disable
|
# enable/disable
|
||||||
@@ -3639,6 +3895,7 @@ if test "$mingw32" = "yes" ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
qemu_confdir=$sysconfdir$confsuffix
|
qemu_confdir=$sysconfdir$confsuffix
|
||||||
|
qemu_moddir=$libdir$confsuffix
|
||||||
qemu_datadir=$datadir$confsuffix
|
qemu_datadir=$datadir$confsuffix
|
||||||
qemu_localedir="$datadir/locale"
|
qemu_localedir="$datadir/locale"
|
||||||
|
|
||||||
@@ -3729,6 +3986,7 @@ echo "Install prefix $prefix"
|
|||||||
echo "BIOS directory `eval echo $qemu_datadir`"
|
echo "BIOS directory `eval echo $qemu_datadir`"
|
||||||
echo "binary directory `eval echo $bindir`"
|
echo "binary directory `eval echo $bindir`"
|
||||||
echo "library directory `eval echo $libdir`"
|
echo "library directory `eval echo $libdir`"
|
||||||
|
echo "module directory `eval echo $qemu_moddir`"
|
||||||
echo "libexec directory `eval echo $libexecdir`"
|
echo "libexec directory `eval echo $libexecdir`"
|
||||||
echo "include directory `eval echo $includedir`"
|
echo "include directory `eval echo $includedir`"
|
||||||
echo "config directory `eval echo $sysconfdir`"
|
echo "config directory `eval echo $sysconfdir`"
|
||||||
@@ -3755,6 +4013,7 @@ echo "python $python"
|
|||||||
if test "$slirp" = "yes" ; then
|
if test "$slirp" = "yes" ; then
|
||||||
echo "smbd $smbd"
|
echo "smbd $smbd"
|
||||||
fi
|
fi
|
||||||
|
echo "module support $modules"
|
||||||
echo "host CPU $cpu"
|
echo "host CPU $cpu"
|
||||||
echo "host big endian $bigendian"
|
echo "host big endian $bigendian"
|
||||||
echo "target list $target_list"
|
echo "target list $target_list"
|
||||||
@@ -3829,6 +4088,7 @@ echo "libiscsi support $libiscsi (1.4.0)"
|
|||||||
else
|
else
|
||||||
echo "libiscsi support $libiscsi"
|
echo "libiscsi support $libiscsi"
|
||||||
fi
|
fi
|
||||||
|
echo "libnfs support $libnfs"
|
||||||
echo "build guest agent $guest_agent"
|
echo "build guest agent $guest_agent"
|
||||||
echo "QGA VSS support $guest_agent_with_vss"
|
echo "QGA VSS support $guest_agent_with_vss"
|
||||||
echo "seccomp support $seccomp"
|
echo "seccomp support $seccomp"
|
||||||
@@ -3843,6 +4103,9 @@ echo "libssh2 support $libssh2"
|
|||||||
echo "TPM passthrough $tpm_passthrough"
|
echo "TPM passthrough $tpm_passthrough"
|
||||||
echo "QOM debugging $qom_cast_debug"
|
echo "QOM debugging $qom_cast_debug"
|
||||||
echo "vhdx $vhdx"
|
echo "vhdx $vhdx"
|
||||||
|
echo "Quorum $quorum"
|
||||||
|
echo "lzo support $lzo"
|
||||||
|
echo "snappy support $snappy"
|
||||||
|
|
||||||
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"
|
||||||
@@ -3866,6 +4129,7 @@ echo "sysconfdir=$sysconfdir" >> $config_host_mak
|
|||||||
echo "qemu_confdir=$qemu_confdir" >> $config_host_mak
|
echo "qemu_confdir=$qemu_confdir" >> $config_host_mak
|
||||||
echo "qemu_datadir=$qemu_datadir" >> $config_host_mak
|
echo "qemu_datadir=$qemu_datadir" >> $config_host_mak
|
||||||
echo "qemu_docdir=$qemu_docdir" >> $config_host_mak
|
echo "qemu_docdir=$qemu_docdir" >> $config_host_mak
|
||||||
|
echo "qemu_moddir=$qemu_moddir" >> $config_host_mak
|
||||||
if test "$mingw32" = "no" ; then
|
if test "$mingw32" = "no" ; then
|
||||||
echo "qemu_localstatedir=$local_statedir" >> $config_host_mak
|
echo "qemu_localstatedir=$local_statedir" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
@@ -3998,6 +4262,12 @@ echo "TARGET_DIRS=$target_list" >> $config_host_mak
|
|||||||
if [ "$docs" = "yes" ] ; then
|
if [ "$docs" = "yes" ] ; then
|
||||||
echo "BUILD_DOCS=yes" >> $config_host_mak
|
echo "BUILD_DOCS=yes" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
if test "$modules" = "yes"; then
|
||||||
|
# $shacmd can generate a hash started with digit, which the compiler doesn't
|
||||||
|
# like as an symbol. So prefix it with an underscore
|
||||||
|
echo "CONFIG_STAMP=_`(echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ `" >> $config_host_mak
|
||||||
|
echo "CONFIG_MODULES=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
if test "$sdl" = "yes" ; then
|
if test "$sdl" = "yes" ; then
|
||||||
echo "CONFIG_SDL=y" >> $config_host_mak
|
echo "CONFIG_SDL=y" >> $config_host_mak
|
||||||
echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak
|
echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak
|
||||||
@@ -4069,8 +4339,9 @@ if test "$bswap_h" = "yes" ; then
|
|||||||
echo "CONFIG_MACHINE_BSWAP_H=y" >> $config_host_mak
|
echo "CONFIG_MACHINE_BSWAP_H=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
if test "$curl" = "yes" ; then
|
if test "$curl" = "yes" ; then
|
||||||
echo "CONFIG_CURL=y" >> $config_host_mak
|
echo "CONFIG_CURL=m" >> $config_host_mak
|
||||||
echo "CURL_CFLAGS=$curl_cflags" >> $config_host_mak
|
echo "CURL_CFLAGS=$curl_cflags" >> $config_host_mak
|
||||||
|
echo "CURL_LIBS=$curl_libs" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
if test "$brlapi" = "yes" ; then
|
if test "$brlapi" = "yes" ; then
|
||||||
echo "CONFIG_BRLAPI=y" >> $config_host_mak
|
echo "CONFIG_BRLAPI=y" >> $config_host_mak
|
||||||
@@ -4158,11 +4429,25 @@ if test "$glx" = "yes" ; then
|
|||||||
echo "GLX_LIBS=$glx_libs" >> $config_host_mak
|
echo "GLX_LIBS=$glx_libs" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$lzo" = "yes" ; then
|
||||||
|
echo "CONFIG_LZO=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$snappy" = "yes" ; then
|
||||||
|
echo "CONFIG_SNAPPY=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
if test "$libiscsi" = "yes" ; then
|
if test "$libiscsi" = "yes" ; then
|
||||||
echo "CONFIG_LIBISCSI=y" >> $config_host_mak
|
echo "CONFIG_LIBISCSI=m" >> $config_host_mak
|
||||||
if test "$libiscsi_version" = "1.4.0"; then
|
if test "$libiscsi_version" = "1.4.0"; then
|
||||||
echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak
|
echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak
|
||||||
|
echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$libnfs" = "yes" ; then
|
||||||
|
echo "CONFIG_LIBNFS=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$seccomp" = "yes"; then
|
if test "$seccomp" = "yes"; then
|
||||||
@@ -4183,7 +4468,9 @@ if test "$qom_cast_debug" = "yes" ; then
|
|||||||
echo "CONFIG_QOM_CAST_DEBUG=y" >> $config_host_mak
|
echo "CONFIG_QOM_CAST_DEBUG=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
if test "$rbd" = "yes" ; then
|
if test "$rbd" = "yes" ; then
|
||||||
echo "CONFIG_RBD=y" >> $config_host_mak
|
echo "CONFIG_RBD=m" >> $config_host_mak
|
||||||
|
echo "RBD_CFLAGS=$rbd_cflags" >> $config_host_mak
|
||||||
|
echo "RBD_LIBS=$rbd_libs" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak
|
echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak
|
||||||
@@ -4226,7 +4513,9 @@ if test "$getauxval" = "yes" ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$glusterfs" = "yes" ; then
|
if test "$glusterfs" = "yes" ; then
|
||||||
echo "CONFIG_GLUSTERFS=y" >> $config_host_mak
|
echo "CONFIG_GLUSTERFS=m" >> $config_host_mak
|
||||||
|
echo "GLUSTERFS_CFLAGS=$glusterfs_cflags" >> $config_host_mak
|
||||||
|
echo "GLUSTERFS_LIBS=$glusterfs_libs" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$glusterfs_discard" = "yes" ; then
|
if test "$glusterfs_discard" = "yes" ; then
|
||||||
@@ -4238,7 +4527,13 @@ if test "$glusterfs_zerofill" = "yes" ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$libssh2" = "yes" ; then
|
if test "$libssh2" = "yes" ; then
|
||||||
echo "CONFIG_LIBSSH2=y" >> $config_host_mak
|
echo "CONFIG_LIBSSH2=m" >> $config_host_mak
|
||||||
|
echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
|
||||||
|
echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$quorum" = "yes" ; then
|
||||||
|
echo "CONFIG_QUORUM=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$virtio_blk_data_plane" = "yes" ; then
|
if test "$virtio_blk_data_plane" = "yes" ; then
|
||||||
@@ -4294,7 +4589,7 @@ if test "$trace_backend" = "ftrace"; then
|
|||||||
echo "CONFIG_TRACE_FTRACE=y" >> $config_host_mak
|
echo "CONFIG_TRACE_FTRACE=y" >> $config_host_mak
|
||||||
trace_default=no
|
trace_default=no
|
||||||
else
|
else
|
||||||
feature_not_found "ftrace(trace backend)"
|
feature_not_found "ftrace(trace backend)" "ftrace requires Linux"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak
|
echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak
|
||||||
@@ -4350,6 +4645,7 @@ echo "LD=$ld" >> $config_host_mak
|
|||||||
echo "WINDRES=$windres" >> $config_host_mak
|
echo "WINDRES=$windres" >> $config_host_mak
|
||||||
echo "LIBTOOL=$libtool" >> $config_host_mak
|
echo "LIBTOOL=$libtool" >> $config_host_mak
|
||||||
echo "CFLAGS=$CFLAGS" >> $config_host_mak
|
echo "CFLAGS=$CFLAGS" >> $config_host_mak
|
||||||
|
echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak
|
||||||
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
|
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
|
||||||
echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
|
echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
|
||||||
if test "$sparse" = "yes" ; then
|
if test "$sparse" = "yes" ; then
|
||||||
@@ -4363,10 +4659,13 @@ else
|
|||||||
echo "AUTOCONF_HOST := " >> $config_host_mak
|
echo "AUTOCONF_HOST := " >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
echo "LDFLAGS=$LDFLAGS" >> $config_host_mak
|
echo "LDFLAGS=$LDFLAGS" >> $config_host_mak
|
||||||
|
echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak
|
||||||
echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak
|
echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak
|
||||||
echo "LIBS+=$LIBS" >> $config_host_mak
|
echo "LIBS+=$LIBS" >> $config_host_mak
|
||||||
echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak
|
echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak
|
||||||
echo "EXESUF=$EXESUF" >> $config_host_mak
|
echo "EXESUF=$EXESUF" >> $config_host_mak
|
||||||
|
echo "DSOSUF=$DSOSUF" >> $config_host_mak
|
||||||
|
echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak
|
||||||
echo "LIBS_QGA+=$libs_qga" >> $config_host_mak
|
echo "LIBS_QGA+=$libs_qga" >> $config_host_mak
|
||||||
echo "POD2MAN=$POD2MAN" >> $config_host_mak
|
echo "POD2MAN=$POD2MAN" >> $config_host_mak
|
||||||
echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak
|
echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak
|
||||||
@@ -4641,6 +4940,10 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
|
|||||||
arm)
|
arm)
|
||||||
echo "CONFIG_ARM_DIS=y" >> $config_target_mak
|
echo "CONFIG_ARM_DIS=y" >> $config_target_mak
|
||||||
echo "CONFIG_ARM_DIS=y" >> config-all-disas.mak
|
echo "CONFIG_ARM_DIS=y" >> config-all-disas.mak
|
||||||
|
if test -n "${cxx}"; then
|
||||||
|
echo "CONFIG_ARM_A64_DIS=y" >> $config_target_mak
|
||||||
|
echo "CONFIG_ARM_A64_DIS=y" >> config-all-disas.mak
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
cris)
|
cris)
|
||||||
echo "CONFIG_CRIS_DIS=y" >> $config_target_mak
|
echo "CONFIG_CRIS_DIS=y" >> $config_target_mak
|
||||||
|
|||||||
@@ -395,7 +395,10 @@ int cpu_exec(CPUArchState *env)
|
|||||||
/* FIXME: this should respect TPR */
|
/* FIXME: this should respect TPR */
|
||||||
cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR,
|
cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR,
|
||||||
0);
|
0);
|
||||||
intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
|
intno = ldl_phys(cpu->as,
|
||||||
|
env->vm_vmcb
|
||||||
|
+ offsetof(struct vmcb,
|
||||||
|
control.int_vector));
|
||||||
qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno);
|
qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno);
|
||||||
do_interrupt_x86_hardirq(env, intno, 1);
|
do_interrupt_x86_hardirq(env, intno, 1);
|
||||||
cpu->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
|
cpu->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
|
||||||
@@ -474,7 +477,7 @@ int cpu_exec(CPUArchState *env)
|
|||||||
}
|
}
|
||||||
#elif defined(TARGET_ARM)
|
#elif defined(TARGET_ARM)
|
||||||
if (interrupt_request & CPU_INTERRUPT_FIQ
|
if (interrupt_request & CPU_INTERRUPT_FIQ
|
||||||
&& !(env->uncached_cpsr & CPSR_F)) {
|
&& !(env->daif & PSTATE_F)) {
|
||||||
env->exception_index = EXCP_FIQ;
|
env->exception_index = EXCP_FIQ;
|
||||||
cc->do_interrupt(cpu);
|
cc->do_interrupt(cpu);
|
||||||
next_tb = 0;
|
next_tb = 0;
|
||||||
@@ -490,7 +493,7 @@ int cpu_exec(CPUArchState *env)
|
|||||||
pc contains a magic address. */
|
pc contains a magic address. */
|
||||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||||
&& ((IS_M(env) && env->regs[15] < 0xfffffff0)
|
&& ((IS_M(env) && env->regs[15] < 0xfffffff0)
|
||||||
|| !(env->uncached_cpsr & CPSR_I))) {
|
|| !(env->daif & PSTATE_I))) {
|
||||||
env->exception_index = EXCP_IRQ;
|
env->exception_index = EXCP_IRQ;
|
||||||
cc->do_interrupt(cpu);
|
cc->do_interrupt(cpu);
|
||||||
next_tb = 0;
|
next_tb = 0;
|
||||||
|
|||||||
2
cpus.c
2
cpus.c
@@ -1119,6 +1119,8 @@ void resume_all_vcpus(void)
|
|||||||
|
|
||||||
static void qemu_tcg_init_vcpu(CPUState *cpu)
|
static void qemu_tcg_init_vcpu(CPUState *cpu)
|
||||||
{
|
{
|
||||||
|
tcg_cpu_address_space_init(cpu, cpu->as);
|
||||||
|
|
||||||
/* share a single thread for all cpus with TCG */
|
/* share a single thread for all cpus with TCG */
|
||||||
if (!tcg_cpu_thread) {
|
if (!tcg_cpu_thread) {
|
||||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||||
|
|||||||
7
cputlb.c
7
cputlb.c
@@ -232,6 +232,7 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr,
|
|||||||
uintptr_t addend;
|
uintptr_t addend;
|
||||||
CPUTLBEntry *te;
|
CPUTLBEntry *te;
|
||||||
hwaddr iotlb, xlat, sz;
|
hwaddr iotlb, xlat, sz;
|
||||||
|
CPUState *cpu = ENV_GET_CPU(env);
|
||||||
|
|
||||||
assert(size >= TARGET_PAGE_SIZE);
|
assert(size >= TARGET_PAGE_SIZE);
|
||||||
if (size != TARGET_PAGE_SIZE) {
|
if (size != TARGET_PAGE_SIZE) {
|
||||||
@@ -239,7 +240,7 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
sz = size;
|
sz = size;
|
||||||
section = address_space_translate_for_iotlb(&address_space_memory, paddr,
|
section = address_space_translate_for_iotlb(cpu->as, paddr,
|
||||||
&xlat, &sz);
|
&xlat, &sz);
|
||||||
assert(sz >= TARGET_PAGE_SIZE);
|
assert(sz >= TARGET_PAGE_SIZE);
|
||||||
|
|
||||||
@@ -305,6 +306,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
|||||||
int mmu_idx, page_index, pd;
|
int mmu_idx, page_index, pd;
|
||||||
void *p;
|
void *p;
|
||||||
MemoryRegion *mr;
|
MemoryRegion *mr;
|
||||||
|
CPUState *cpu = ENV_GET_CPU(env1);
|
||||||
|
|
||||||
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
mmu_idx = cpu_mmu_index(env1);
|
mmu_idx = cpu_mmu_index(env1);
|
||||||
@@ -313,9 +315,8 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
|||||||
cpu_ldub_code(env1, addr);
|
cpu_ldub_code(env1, addr);
|
||||||
}
|
}
|
||||||
pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK;
|
pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK;
|
||||||
mr = iotlb_to_region(pd);
|
mr = iotlb_to_region(cpu->as, pd);
|
||||||
if (memory_region_is_unassigned(mr)) {
|
if (memory_region_is_unassigned(mr)) {
|
||||||
CPUState *cpu = ENV_GET_CPU(env1);
|
|
||||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||||
|
|
||||||
if (cc->do_unassigned_access) {
|
if (cc->do_unassigned_access) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ CONFIG_SSI_SD=y
|
|||||||
CONFIG_SSI_M25P80=y
|
CONFIG_SSI_M25P80=y
|
||||||
CONFIG_LAN9118=y
|
CONFIG_LAN9118=y
|
||||||
CONFIG_SMC91C111=y
|
CONFIG_SMC91C111=y
|
||||||
|
CONFIG_ALLWINNER_EMAC=y
|
||||||
CONFIG_DS1338=y
|
CONFIG_DS1338=y
|
||||||
CONFIG_PFLASH_CFI01=y
|
CONFIG_PFLASH_CFI01=y
|
||||||
CONFIG_PFLASH_CFI02=y
|
CONFIG_PFLASH_CFI02=y
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ CONFIG_I8259=y
|
|||||||
CONFIG_XILINX=y
|
CONFIG_XILINX=y
|
||||||
CONFIG_XILINX_ETHLITE=y
|
CONFIG_XILINX_ETHLITE=y
|
||||||
CONFIG_OPENPIC=y
|
CONFIG_OPENPIC=y
|
||||||
|
CONFIG_PREP=y
|
||||||
|
CONFIG_MAC=y
|
||||||
CONFIG_E500=y
|
CONFIG_E500=y
|
||||||
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
||||||
# For PReP
|
# For PReP
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ CONFIG_XILINX=y
|
|||||||
CONFIG_XILINX_ETHLITE=y
|
CONFIG_XILINX_ETHLITE=y
|
||||||
CONFIG_OPENPIC=y
|
CONFIG_OPENPIC=y
|
||||||
CONFIG_PSERIES=y
|
CONFIG_PSERIES=y
|
||||||
|
CONFIG_PREP=y
|
||||||
|
CONFIG_MAC=y
|
||||||
CONFIG_E500=y
|
CONFIG_E500=y
|
||||||
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
||||||
# For pSeries
|
# For pSeries
|
||||||
|
|||||||
@@ -3,32 +3,12 @@
|
|||||||
include pci.mak
|
include pci.mak
|
||||||
include sound.mak
|
include sound.mak
|
||||||
include usb.mak
|
include usb.mak
|
||||||
CONFIG_ISA_MMIO=y
|
|
||||||
CONFIG_ESCC=y
|
|
||||||
CONFIG_M48T59=y
|
CONFIG_M48T59=y
|
||||||
CONFIG_VGA=y
|
CONFIG_VGA=y
|
||||||
CONFIG_VGA_PCI=y
|
CONFIG_VGA_PCI=y
|
||||||
CONFIG_SERIAL=y
|
CONFIG_SERIAL=y
|
||||||
CONFIG_I8254=y
|
|
||||||
CONFIG_PCKBD=y
|
|
||||||
CONFIG_FDC=y
|
|
||||||
CONFIG_I8257=y
|
CONFIG_I8257=y
|
||||||
CONFIG_OPENPIC=y
|
CONFIG_OPENPIC=y
|
||||||
CONFIG_PREP_PCI=y
|
|
||||||
CONFIG_MACIO=y
|
|
||||||
CONFIG_CUDA=y
|
|
||||||
CONFIG_ADB=y
|
|
||||||
CONFIG_MAC_NVRAM=y
|
|
||||||
CONFIG_MAC_DBDMA=y
|
|
||||||
CONFIG_HEATHROW_PIC=y
|
|
||||||
CONFIG_GRACKLE_PCI=y
|
|
||||||
CONFIG_UNIN_PCI=y
|
|
||||||
CONFIG_DEC_PCI=y
|
|
||||||
CONFIG_PPCE500_PCI=y
|
|
||||||
CONFIG_IDE_ISA=y
|
|
||||||
CONFIG_IDE_CMD646=y
|
|
||||||
CONFIG_IDE_MACIO=y
|
|
||||||
CONFIG_NE2000_ISA=y
|
|
||||||
CONFIG_PFLASH_CFI01=y
|
CONFIG_PFLASH_CFI01=y
|
||||||
CONFIG_PFLASH_CFI02=y
|
CONFIG_PFLASH_CFI02=y
|
||||||
CONFIG_PTIMER=y
|
CONFIG_PTIMER=y
|
||||||
@@ -36,8 +16,3 @@ CONFIG_I8259=y
|
|||||||
CONFIG_XILINX=y
|
CONFIG_XILINX=y
|
||||||
CONFIG_XILINX_ETHLITE=y
|
CONFIG_XILINX_ETHLITE=y
|
||||||
CONFIG_OPENPIC=y
|
CONFIG_OPENPIC=y
|
||||||
CONFIG_E500=y
|
|
||||||
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
|
||||||
# For PReP
|
|
||||||
CONFIG_MC146818RTC=y
|
|
||||||
CONFIG_ISA_TESTDEV=y
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
CONFIG_VIRTIO=y
|
CONFIG_VIRTIO=y
|
||||||
CONFIG_SCLPCONSOLE=y
|
CONFIG_SCLPCONSOLE=y
|
||||||
|
CONFIG_S390_FLIC=$(CONFIG_KVM)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ CONFIG_EMPTY_SLOT=y
|
|||||||
CONFIG_PCNET_COMMON=y
|
CONFIG_PCNET_COMMON=y
|
||||||
CONFIG_LANCE=y
|
CONFIG_LANCE=y
|
||||||
CONFIG_TCX=y
|
CONFIG_TCX=y
|
||||||
|
CONFIG_CG3=y
|
||||||
CONFIG_SLAVIO=y
|
CONFIG_SLAVIO=y
|
||||||
CONFIG_CS4231=y
|
CONFIG_CS4231=y
|
||||||
CONFIG_GRLIB=y
|
CONFIG_GRLIB=y
|
||||||
|
|||||||
14
disas.c
14
disas.c
@@ -190,7 +190,7 @@ static int print_insn_od_target(bfd_vma pc, disassemble_info *info)
|
|||||||
/* Disassemble this for me please... (debugging). 'flags' has the following
|
/* Disassemble this for me please... (debugging). 'flags' has the following
|
||||||
values:
|
values:
|
||||||
i386 - 1 means 16 bit code, 2 means 64 bit code
|
i386 - 1 means 16 bit code, 2 means 64 bit code
|
||||||
arm - bit 0 = thumb, bit 1 = reverse endian
|
arm - bit 0 = thumb, bit 1 = reverse endian, bit 2 = A64
|
||||||
ppc - nonzero means little endian
|
ppc - nonzero means little endian
|
||||||
other targets - unused
|
other targets - unused
|
||||||
*/
|
*/
|
||||||
@@ -225,7 +225,15 @@ void target_disas(FILE *out, CPUArchState *env, target_ulong code,
|
|||||||
}
|
}
|
||||||
print_insn = print_insn_i386;
|
print_insn = print_insn_i386;
|
||||||
#elif defined(TARGET_ARM)
|
#elif defined(TARGET_ARM)
|
||||||
if (flags & 1) {
|
if (flags & 4) {
|
||||||
|
/* We might not be compiled with the A64 disassembler
|
||||||
|
* because it needs a C++ compiler; in that case we will
|
||||||
|
* fall through to the default print_insn_od case.
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ARM_A64_DIS)
|
||||||
|
print_insn = print_insn_arm_a64;
|
||||||
|
#endif
|
||||||
|
} else if (flags & 1) {
|
||||||
print_insn = print_insn_thumb1;
|
print_insn = print_insn_thumb1;
|
||||||
} else {
|
} else {
|
||||||
print_insn = print_insn_arm;
|
print_insn = print_insn_arm;
|
||||||
@@ -356,6 +364,8 @@ void disas(FILE *out, void *code, unsigned long size)
|
|||||||
#elif defined(_ARCH_PPC)
|
#elif defined(_ARCH_PPC)
|
||||||
s.info.disassembler_options = (char *)"any";
|
s.info.disassembler_options = (char *)"any";
|
||||||
print_insn = print_insn_ppc;
|
print_insn = print_insn_ppc;
|
||||||
|
#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
|
||||||
|
print_insn = print_insn_arm_a64;
|
||||||
#elif defined(__alpha__)
|
#elif defined(__alpha__)
|
||||||
print_insn = print_insn_alpha;
|
print_insn = print_insn_alpha;
|
||||||
#elif defined(__sparc__)
|
#elif defined(__sparc__)
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
|
||||||
common-obj-$(CONFIG_ALPHA_DIS) += alpha.o
|
common-obj-$(CONFIG_ALPHA_DIS) += alpha.o
|
||||||
common-obj-$(CONFIG_ARM_DIS) += arm.o
|
common-obj-$(CONFIG_ARM_DIS) += arm.o
|
||||||
|
common-obj-$(CONFIG_ARM_A64_DIS) += arm-a64.o
|
||||||
|
common-obj-$(CONFIG_ARM_A64_DIS) += libvixl/
|
||||||
|
libvixldir = $(SRC_PATH)/disas/libvixl
|
||||||
|
$(obj)/arm-a64.o: QEMU_CFLAGS += -I$(libvixldir)
|
||||||
common-obj-$(CONFIG_CRIS_DIS) += cris.o
|
common-obj-$(CONFIG_CRIS_DIS) += cris.o
|
||||||
common-obj-$(CONFIG_HPPA_DIS) += hppa.o
|
common-obj-$(CONFIG_HPPA_DIS) += hppa.o
|
||||||
common-obj-$(CONFIG_I386_DIS) += i386.o
|
common-obj-$(CONFIG_I386_DIS) += i386.o
|
||||||
|
|||||||
87
disas/arm-a64.cc
Normal file
87
disas/arm-a64.cc
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* ARM A64 disassembly output wrapper to libvixl
|
||||||
|
* Copyright (c) 2013 Linaro Limited
|
||||||
|
* Written by Claudio Fontana
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "a64/disasm-a64.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "disas/bfd.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace vixl;
|
||||||
|
|
||||||
|
static Decoder *vixl_decoder = NULL;
|
||||||
|
static Disassembler *vixl_disasm = NULL;
|
||||||
|
|
||||||
|
/* We don't use libvixl's PrintDisassembler because its output
|
||||||
|
* is a little unhelpful (trailing newlines, for example).
|
||||||
|
* Instead we use our own very similar variant so we have
|
||||||
|
* control over the format.
|
||||||
|
*/
|
||||||
|
class QEMUDisassembler : public Disassembler {
|
||||||
|
public:
|
||||||
|
explicit QEMUDisassembler(FILE *stream) : stream_(stream) { }
|
||||||
|
~QEMUDisassembler() { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ProcessOutput(Instruction *instr) {
|
||||||
|
fprintf(stream_, "%08" PRIx32 " %s",
|
||||||
|
instr->InstructionBits(), GetOutput());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *stream_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int vixl_is_initialized(void)
|
||||||
|
{
|
||||||
|
return vixl_decoder != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vixl_init(FILE *f) {
|
||||||
|
vixl_decoder = new Decoder();
|
||||||
|
vixl_disasm = new QEMUDisassembler(f);
|
||||||
|
vixl_decoder->AppendVisitor(vixl_disasm);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INSN_SIZE 4
|
||||||
|
|
||||||
|
/* Disassemble ARM A64 instruction. This is our only entry
|
||||||
|
* point from QEMU's C code.
|
||||||
|
*/
|
||||||
|
int print_insn_arm_a64(uint64_t addr, disassemble_info *info)
|
||||||
|
{
|
||||||
|
uint8_t bytes[INSN_SIZE];
|
||||||
|
uint32_t instr;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = info->read_memory_func(addr, bytes, INSN_SIZE, info);
|
||||||
|
if (status != 0) {
|
||||||
|
info->memory_error_func(status, addr, info);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vixl_is_initialized()) {
|
||||||
|
vixl_init(info->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
instr = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24;
|
||||||
|
vixl_decoder->Decode(reinterpret_cast<Instruction*>(&instr));
|
||||||
|
|
||||||
|
return INSN_SIZE;
|
||||||
|
}
|
||||||
146
disas/i386.c
146
disas/i386.c
@@ -171,6 +171,7 @@ static void print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma dis
|
|||||||
static void print_displacement (char *, bfd_vma);
|
static void print_displacement (char *, bfd_vma);
|
||||||
static void OP_E (int, int);
|
static void OP_E (int, int);
|
||||||
static void OP_G (int, int);
|
static void OP_G (int, int);
|
||||||
|
static void OP_vvvv (int, int);
|
||||||
static bfd_vma get64 (void);
|
static bfd_vma get64 (void);
|
||||||
static bfd_signed_vma get32 (void);
|
static bfd_signed_vma get32 (void);
|
||||||
static bfd_signed_vma get32s (void);
|
static bfd_signed_vma get32s (void);
|
||||||
@@ -264,6 +265,9 @@ static int rex_used;
|
|||||||
current instruction. */
|
current instruction. */
|
||||||
static int used_prefixes;
|
static int used_prefixes;
|
||||||
|
|
||||||
|
/* The VEX.vvvv register, unencoded. */
|
||||||
|
static int vex_reg;
|
||||||
|
|
||||||
/* Flags stored in PREFIXES. */
|
/* Flags stored in PREFIXES. */
|
||||||
#define PREFIX_REPZ 1
|
#define PREFIX_REPZ 1
|
||||||
#define PREFIX_REPNZ 2
|
#define PREFIX_REPNZ 2
|
||||||
@@ -278,6 +282,10 @@ static int used_prefixes;
|
|||||||
#define PREFIX_ADDR 0x400
|
#define PREFIX_ADDR 0x400
|
||||||
#define PREFIX_FWAIT 0x800
|
#define PREFIX_FWAIT 0x800
|
||||||
|
|
||||||
|
#define PREFIX_VEX_0F 0x1000
|
||||||
|
#define PREFIX_VEX_0F38 0x2000
|
||||||
|
#define PREFIX_VEX_0F3A 0x4000
|
||||||
|
|
||||||
/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
|
/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
|
||||||
to ADDR (exclusive) are valid. Returns 1 for success, longjmps
|
to ADDR (exclusive) are valid. Returns 1 for success, longjmps
|
||||||
on error. */
|
on error. */
|
||||||
@@ -323,6 +331,7 @@ fetch_data(struct disassemble_info *info, bfd_byte *addr)
|
|||||||
|
|
||||||
#define XX { NULL, 0 }
|
#define XX { NULL, 0 }
|
||||||
|
|
||||||
|
#define Bv { OP_vvvv, v_mode }
|
||||||
#define Eb { OP_E, b_mode }
|
#define Eb { OP_E, b_mode }
|
||||||
#define Ev { OP_E, v_mode }
|
#define Ev { OP_E, v_mode }
|
||||||
#define Ed { OP_E, d_mode }
|
#define Ed { OP_E, d_mode }
|
||||||
@@ -671,7 +680,8 @@ fetch_data(struct disassemble_info *info, bfd_byte *addr)
|
|||||||
#define PREGRP102 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 102 } }
|
#define PREGRP102 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 102 } }
|
||||||
#define PREGRP103 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 103 } }
|
#define PREGRP103 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 103 } }
|
||||||
#define PREGRP104 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 104 } }
|
#define PREGRP104 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 104 } }
|
||||||
|
#define PREGRP105 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 105 } }
|
||||||
|
#define PREGRP106 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 106 } }
|
||||||
|
|
||||||
#define X86_64_0 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 0 } }
|
#define X86_64_0 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 0 } }
|
||||||
#define X86_64_1 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 1 } }
|
#define X86_64_1 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 1 } }
|
||||||
@@ -1449,7 +1459,7 @@ static const unsigned char threebyte_0x38_uses_DATA_prefix[256] = {
|
|||||||
/* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
|
/* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
|
||||||
/* d0 */ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1, /* df */
|
/* d0 */ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1, /* df */
|
||||||
/* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
|
/* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
|
||||||
/* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
|
/* f0 */ 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, /* ff */
|
||||||
/* ------------------------------- */
|
/* ------------------------------- */
|
||||||
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
||||||
};
|
};
|
||||||
@@ -1473,7 +1483,7 @@ static const unsigned char threebyte_0x38_uses_REPNZ_prefix[256] = {
|
|||||||
/* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
|
/* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
|
||||||
/* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
|
/* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
|
||||||
/* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
|
/* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
|
||||||
/* f0 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
|
/* f0 */ 1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0, /* ff */
|
||||||
/* ------------------------------- */
|
/* ------------------------------- */
|
||||||
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
||||||
};
|
};
|
||||||
@@ -1497,7 +1507,7 @@ static const unsigned char threebyte_0x38_uses_REPZ_prefix[256] = {
|
|||||||
/* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
|
/* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
|
||||||
/* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
|
/* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
|
||||||
/* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
|
/* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
|
||||||
/* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
|
/* f0 */ 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, /* ff */
|
||||||
/* ------------------------------- */
|
/* ------------------------------- */
|
||||||
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
||||||
};
|
};
|
||||||
@@ -2774,6 +2784,22 @@ static const struct dis386 prefix_user_table[][4] = {
|
|||||||
{ "(bad)", { XX } },
|
{ "(bad)", { XX } },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* PREGRP105 */
|
||||||
|
{
|
||||||
|
{ "andnS", { Gv, Bv, Ev } },
|
||||||
|
{ "(bad)", { XX } },
|
||||||
|
{ "(bad)", { XX } },
|
||||||
|
{ "(bad)", { XX } },
|
||||||
|
},
|
||||||
|
|
||||||
|
/* PREGRP106 */
|
||||||
|
{
|
||||||
|
{ "bextrS", { Gv, Ev, Bv } },
|
||||||
|
{ "sarxS", { Gv, Ev, Bv } },
|
||||||
|
{ "shlxS", { Gv, Ev, Bv } },
|
||||||
|
{ "shrxS", { Gv, Ev, Bv } },
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dis386 x86_64_table[][2] = {
|
static const struct dis386 x86_64_table[][2] = {
|
||||||
@@ -3071,12 +3097,12 @@ static const struct dis386 three_byte_table[][256] = {
|
|||||||
/* f0 */
|
/* f0 */
|
||||||
{ PREGRP87 },
|
{ PREGRP87 },
|
||||||
{ PREGRP88 },
|
{ PREGRP88 },
|
||||||
|
{ PREGRP105 },
|
||||||
{ "(bad)", { XX } },
|
{ "(bad)", { XX } },
|
||||||
{ "(bad)", { XX } },
|
{ "(bad)", { XX } },
|
||||||
{ "(bad)", { XX } },
|
{ "(bad)", { XX } },
|
||||||
{ "(bad)", { XX } },
|
{ "(bad)", { XX } },
|
||||||
{ "(bad)", { XX } },
|
{ PREGRP106 },
|
||||||
{ "(bad)", { XX } },
|
|
||||||
/* f8 */
|
/* f8 */
|
||||||
{ "(bad)", { XX } },
|
{ "(bad)", { XX } },
|
||||||
{ "(bad)", { XX } },
|
{ "(bad)", { XX } },
|
||||||
@@ -3477,6 +3503,74 @@ ckprefix (void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ckvexprefix (void)
|
||||||
|
{
|
||||||
|
int op, vex2, vex3, newrex = 0, newpfx = prefixes;
|
||||||
|
|
||||||
|
if (address_mode == mode_16bit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_data(the_info, codep + 1);
|
||||||
|
op = *codep;
|
||||||
|
|
||||||
|
if (op != 0xc4 && op != 0xc5) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_data(the_info, codep + 2);
|
||||||
|
vex2 = codep[1];
|
||||||
|
|
||||||
|
if (address_mode == mode_32bit && (vex2 & 0xc0) != 0xc0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == 0xc4) {
|
||||||
|
/* Three byte VEX prefix. */
|
||||||
|
fetch_data(the_info, codep + 3);
|
||||||
|
vex3 = codep[2];
|
||||||
|
|
||||||
|
newrex |= (vex2 & 0x80 ? 0 : REX_R);
|
||||||
|
newrex |= (vex2 & 0x40 ? 0 : REX_X);
|
||||||
|
newrex |= (vex2 & 0x20 ? 0 : REX_B);
|
||||||
|
newrex |= (vex3 & 0x80 ? REX_W : 0);
|
||||||
|
switch (vex2 & 0x1f) { /* VEX.m-mmmm */
|
||||||
|
case 1:
|
||||||
|
newpfx |= PREFIX_VEX_0F;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
newpfx |= PREFIX_VEX_0F | PREFIX_VEX_0F38;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
newpfx |= PREFIX_VEX_0F | PREFIX_VEX_0F3A;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vex2 = vex3;
|
||||||
|
codep += 3;
|
||||||
|
} else {
|
||||||
|
/* Two byte VEX prefix. */
|
||||||
|
newrex |= (vex2 & 0x80 ? 0 : REX_R);
|
||||||
|
codep += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
vex_reg = (~vex2 >> 3) & 15; /* VEX.vvvv */
|
||||||
|
switch (vex2 & 3) { /* VEX.pp */
|
||||||
|
case 1:
|
||||||
|
newpfx |= PREFIX_DATA; /* 0x66 */
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
newpfx |= PREFIX_REPZ; /* 0xf3 */
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
newpfx |= PREFIX_REPNZ; /* 0xf2 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rex = newrex;
|
||||||
|
prefixes = newpfx;
|
||||||
|
}
|
||||||
|
|
||||||
/* Return the name of the prefix byte PREF, or NULL if PREF is not a
|
/* Return the name of the prefix byte PREF, or NULL if PREF is not a
|
||||||
prefix byte. */
|
prefix byte. */
|
||||||
|
|
||||||
@@ -3598,6 +3692,7 @@ print_insn (bfd_vma pc, disassemble_info *info)
|
|||||||
const char *p;
|
const char *p;
|
||||||
struct dis_private priv;
|
struct dis_private priv;
|
||||||
unsigned char op;
|
unsigned char op;
|
||||||
|
unsigned char threebyte;
|
||||||
|
|
||||||
if (info->mach == bfd_mach_x86_64_intel_syntax
|
if (info->mach == bfd_mach_x86_64_intel_syntax
|
||||||
|| info->mach == bfd_mach_x86_64)
|
|| info->mach == bfd_mach_x86_64)
|
||||||
@@ -3752,6 +3847,7 @@ print_insn (bfd_vma pc, disassemble_info *info)
|
|||||||
|
|
||||||
obufp = obuf;
|
obufp = obuf;
|
||||||
ckprefix ();
|
ckprefix ();
|
||||||
|
ckvexprefix ();
|
||||||
|
|
||||||
insn_codep = codep;
|
insn_codep = codep;
|
||||||
sizeflag = priv.orig_sizeflag;
|
sizeflag = priv.orig_sizeflag;
|
||||||
@@ -3775,18 +3871,29 @@ print_insn (bfd_vma pc, disassemble_info *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
op = 0;
|
op = 0;
|
||||||
|
if (prefixes & PREFIX_VEX_0F)
|
||||||
|
{
|
||||||
|
used_prefixes |= PREFIX_VEX_0F | PREFIX_VEX_0F38 | PREFIX_VEX_0F3A;
|
||||||
|
if (prefixes & PREFIX_VEX_0F38)
|
||||||
|
threebyte = 0x38;
|
||||||
|
else if (prefixes & PREFIX_VEX_0F3A)
|
||||||
|
threebyte = 0x3a;
|
||||||
|
else
|
||||||
|
threebyte = *codep++;
|
||||||
|
goto vex_opcode;
|
||||||
|
}
|
||||||
if (*codep == 0x0f)
|
if (*codep == 0x0f)
|
||||||
{
|
{
|
||||||
unsigned char threebyte;
|
|
||||||
fetch_data(info, codep + 2);
|
fetch_data(info, codep + 2);
|
||||||
threebyte = *++codep;
|
threebyte = codep[1];
|
||||||
|
codep += 2;
|
||||||
|
vex_opcode:
|
||||||
dp = &dis386_twobyte[threebyte];
|
dp = &dis386_twobyte[threebyte];
|
||||||
need_modrm = twobyte_has_modrm[*codep];
|
need_modrm = twobyte_has_modrm[threebyte];
|
||||||
uses_DATA_prefix = twobyte_uses_DATA_prefix[*codep];
|
uses_DATA_prefix = twobyte_uses_DATA_prefix[threebyte];
|
||||||
uses_REPNZ_prefix = twobyte_uses_REPNZ_prefix[*codep];
|
uses_REPNZ_prefix = twobyte_uses_REPNZ_prefix[threebyte];
|
||||||
uses_REPZ_prefix = twobyte_uses_REPZ_prefix[*codep];
|
uses_REPZ_prefix = twobyte_uses_REPZ_prefix[threebyte];
|
||||||
uses_LOCK_prefix = (*codep & ~0x02) == 0x20;
|
uses_LOCK_prefix = (threebyte & ~0x02) == 0x20;
|
||||||
codep++;
|
|
||||||
if (dp->name == NULL && dp->op[0].bytemode == IS_3BYTE_OPCODE)
|
if (dp->name == NULL && dp->op[0].bytemode == IS_3BYTE_OPCODE)
|
||||||
{
|
{
|
||||||
fetch_data(info, codep + 2);
|
fetch_data(info, codep + 2);
|
||||||
@@ -5291,6 +5398,17 @@ OP_G (int bytemode, int sizeflag)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
OP_vvvv (int bytemode, int sizeflags)
|
||||||
|
{
|
||||||
|
USED_REX (REX_W);
|
||||||
|
if (rex & REX_W) {
|
||||||
|
oappend(names64[vex_reg]);
|
||||||
|
} else {
|
||||||
|
oappend(names32[vex_reg]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bfd_vma
|
static bfd_vma
|
||||||
get64 (void)
|
get64 (void)
|
||||||
{
|
{
|
||||||
|
|||||||
30
disas/libvixl/LICENCE
Normal file
30
disas/libvixl/LICENCE
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
LICENCE
|
||||||
|
=======
|
||||||
|
|
||||||
|
The software in this repository is covered by the following licence.
|
||||||
|
|
||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
8
disas/libvixl/Makefile.objs
Normal file
8
disas/libvixl/Makefile.objs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
libvixl_OBJS = utils.o \
|
||||||
|
a64/instructions-a64.o \
|
||||||
|
a64/decoder-a64.o \
|
||||||
|
a64/disasm-a64.o
|
||||||
|
|
||||||
|
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS += -I$(SRC_PATH)/disas/libvixl
|
||||||
|
|
||||||
|
common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS)
|
||||||
12
disas/libvixl/README
Normal file
12
disas/libvixl/README
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
The code in this directory is a subset of libvixl:
|
||||||
|
https://github.com/armvixl/vixl
|
||||||
|
(specifically, it is the set of files needed for disassembly only,
|
||||||
|
taken from libvixl 1.1).
|
||||||
|
Bugfixes should preferably be sent upstream initially.
|
||||||
|
|
||||||
|
The disassembler does not currently support the entire A64 instruction
|
||||||
|
set. Notably:
|
||||||
|
* No Advanced SIMD support.
|
||||||
|
* Limited support for system instructions.
|
||||||
|
* A few miscellaneous integer and floating point instructions are missing.
|
||||||
1784
disas/libvixl/a64/assembler-a64.h
Normal file
1784
disas/libvixl/a64/assembler-a64.h
Normal file
File diff suppressed because it is too large
Load Diff
1104
disas/libvixl/a64/constants-a64.h
Normal file
1104
disas/libvixl/a64/constants-a64.h
Normal file
File diff suppressed because it is too large
Load Diff
56
disas/libvixl/a64/cpu-a64.h
Normal file
56
disas/libvixl/a64/cpu-a64.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef VIXL_CPU_A64_H
|
||||||
|
#define VIXL_CPU_A64_H
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
|
||||||
|
class CPU {
|
||||||
|
public:
|
||||||
|
// Initialise CPU support.
|
||||||
|
static void SetUp();
|
||||||
|
|
||||||
|
// Ensures the data at a given address and with a given size is the same for
|
||||||
|
// the I and D caches. I and D caches are not automatically coherent on ARM
|
||||||
|
// so this operation is required before any dynamically generated code can
|
||||||
|
// safely run.
|
||||||
|
static void EnsureIAndDCacheCoherency(void *address, size_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Return the content of the cache type register.
|
||||||
|
static uint32_t GetCacheType();
|
||||||
|
|
||||||
|
// I and D cache line size in bytes.
|
||||||
|
static unsigned icache_line_size_;
|
||||||
|
static unsigned dcache_line_size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace vixl
|
||||||
|
|
||||||
|
#endif // VIXL_CPU_A64_H
|
||||||
712
disas/libvixl/a64/decoder-a64.cc
Normal file
712
disas/libvixl/a64/decoder-a64.cc
Normal file
@@ -0,0 +1,712 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "a64/decoder-a64.h"
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
// Top-level instruction decode function.
|
||||||
|
void Decoder::Decode(Instruction *instr) {
|
||||||
|
if (instr->Bits(28, 27) == 0) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
switch (instr->Bits(27, 24)) {
|
||||||
|
// 0: PC relative addressing.
|
||||||
|
case 0x0: DecodePCRelAddressing(instr); break;
|
||||||
|
|
||||||
|
// 1: Add/sub immediate.
|
||||||
|
case 0x1: DecodeAddSubImmediate(instr); break;
|
||||||
|
|
||||||
|
// A: Logical shifted register.
|
||||||
|
// Add/sub with carry.
|
||||||
|
// Conditional compare register.
|
||||||
|
// Conditional compare immediate.
|
||||||
|
// Conditional select.
|
||||||
|
// Data processing 1 source.
|
||||||
|
// Data processing 2 source.
|
||||||
|
// B: Add/sub shifted register.
|
||||||
|
// Add/sub extended register.
|
||||||
|
// Data processing 3 source.
|
||||||
|
case 0xA:
|
||||||
|
case 0xB: DecodeDataProcessing(instr); break;
|
||||||
|
|
||||||
|
// 2: Logical immediate.
|
||||||
|
// Move wide immediate.
|
||||||
|
case 0x2: DecodeLogical(instr); break;
|
||||||
|
|
||||||
|
// 3: Bitfield.
|
||||||
|
// Extract.
|
||||||
|
case 0x3: DecodeBitfieldExtract(instr); break;
|
||||||
|
|
||||||
|
// 4: Unconditional branch immediate.
|
||||||
|
// Exception generation.
|
||||||
|
// Compare and branch immediate.
|
||||||
|
// 5: Compare and branch immediate.
|
||||||
|
// Conditional branch.
|
||||||
|
// System.
|
||||||
|
// 6,7: Unconditional branch.
|
||||||
|
// Test and branch immediate.
|
||||||
|
case 0x4:
|
||||||
|
case 0x5:
|
||||||
|
case 0x6:
|
||||||
|
case 0x7: DecodeBranchSystemException(instr); break;
|
||||||
|
|
||||||
|
// 8,9: Load/store register pair post-index.
|
||||||
|
// Load register literal.
|
||||||
|
// Load/store register unscaled immediate.
|
||||||
|
// Load/store register immediate post-index.
|
||||||
|
// Load/store register immediate pre-index.
|
||||||
|
// Load/store register offset.
|
||||||
|
// Load/store exclusive.
|
||||||
|
// C,D: Load/store register pair offset.
|
||||||
|
// Load/store register pair pre-index.
|
||||||
|
// Load/store register unsigned immediate.
|
||||||
|
// Advanced SIMD.
|
||||||
|
case 0x8:
|
||||||
|
case 0x9:
|
||||||
|
case 0xC:
|
||||||
|
case 0xD: DecodeLoadStore(instr); break;
|
||||||
|
|
||||||
|
// E: FP fixed point conversion.
|
||||||
|
// FP integer conversion.
|
||||||
|
// FP data processing 1 source.
|
||||||
|
// FP compare.
|
||||||
|
// FP immediate.
|
||||||
|
// FP data processing 2 source.
|
||||||
|
// FP conditional compare.
|
||||||
|
// FP conditional select.
|
||||||
|
// Advanced SIMD.
|
||||||
|
// F: FP data processing 3 source.
|
||||||
|
// Advanced SIMD.
|
||||||
|
case 0xE:
|
||||||
|
case 0xF: DecodeFP(instr); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decoder::AppendVisitor(DecoderVisitor* new_visitor) {
|
||||||
|
visitors_.remove(new_visitor);
|
||||||
|
visitors_.push_front(new_visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::PrependVisitor(DecoderVisitor* new_visitor) {
|
||||||
|
visitors_.remove(new_visitor);
|
||||||
|
visitors_.push_back(new_visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor,
|
||||||
|
DecoderVisitor* registered_visitor) {
|
||||||
|
visitors_.remove(new_visitor);
|
||||||
|
std::list<DecoderVisitor*>::iterator it;
|
||||||
|
for (it = visitors_.begin(); it != visitors_.end(); it++) {
|
||||||
|
if (*it == registered_visitor) {
|
||||||
|
visitors_.insert(it, new_visitor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We reached the end of the list. The last element must be
|
||||||
|
// registered_visitor.
|
||||||
|
ASSERT(*it == registered_visitor);
|
||||||
|
visitors_.insert(it, new_visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor,
|
||||||
|
DecoderVisitor* registered_visitor) {
|
||||||
|
visitors_.remove(new_visitor);
|
||||||
|
std::list<DecoderVisitor*>::iterator it;
|
||||||
|
for (it = visitors_.begin(); it != visitors_.end(); it++) {
|
||||||
|
if (*it == registered_visitor) {
|
||||||
|
it++;
|
||||||
|
visitors_.insert(it, new_visitor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We reached the end of the list. The last element must be
|
||||||
|
// registered_visitor.
|
||||||
|
ASSERT(*it == registered_visitor);
|
||||||
|
visitors_.push_back(new_visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::RemoveVisitor(DecoderVisitor* visitor) {
|
||||||
|
visitors_.remove(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodePCRelAddressing(Instruction* instr) {
|
||||||
|
ASSERT(instr->Bits(27, 24) == 0x0);
|
||||||
|
// We know bit 28 is set, as <b28:b27> = 0 is filtered out at the top level
|
||||||
|
// decode.
|
||||||
|
ASSERT(instr->Bit(28) == 0x1);
|
||||||
|
VisitPCRelAddressing(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeBranchSystemException(Instruction* instr) {
|
||||||
|
ASSERT((instr->Bits(27, 24) == 0x4) ||
|
||||||
|
(instr->Bits(27, 24) == 0x5) ||
|
||||||
|
(instr->Bits(27, 24) == 0x6) ||
|
||||||
|
(instr->Bits(27, 24) == 0x7) );
|
||||||
|
|
||||||
|
switch (instr->Bits(31, 29)) {
|
||||||
|
case 0:
|
||||||
|
case 4: {
|
||||||
|
VisitUnconditionalBranch(instr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
case 5: {
|
||||||
|
if (instr->Bit(25) == 0) {
|
||||||
|
VisitCompareBranch(instr);
|
||||||
|
} else {
|
||||||
|
VisitTestBranch(instr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
if (instr->Bit(25) == 0) {
|
||||||
|
if ((instr->Bit(24) == 0x1) ||
|
||||||
|
(instr->Mask(0x01000010) == 0x00000010)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitConditionalBranch(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 6: {
|
||||||
|
if (instr->Bit(25) == 0) {
|
||||||
|
if (instr->Bit(24) == 0) {
|
||||||
|
if ((instr->Bits(4, 2) != 0) ||
|
||||||
|
(instr->Mask(0x00E0001D) == 0x00200001) ||
|
||||||
|
(instr->Mask(0x00E0001D) == 0x00400001) ||
|
||||||
|
(instr->Mask(0x00E0001E) == 0x00200002) ||
|
||||||
|
(instr->Mask(0x00E0001E) == 0x00400002) ||
|
||||||
|
(instr->Mask(0x00E0001C) == 0x00600000) ||
|
||||||
|
(instr->Mask(0x00E0001C) == 0x00800000) ||
|
||||||
|
(instr->Mask(0x00E0001F) == 0x00A00000) ||
|
||||||
|
(instr->Mask(0x00C0001C) == 0x00C00000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitException(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instr->Bits(23, 22) == 0) {
|
||||||
|
const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0);
|
||||||
|
if ((instr->Bits(21, 19) == 0x4) ||
|
||||||
|
(masked_003FF0E0 == 0x00033000) ||
|
||||||
|
(masked_003FF0E0 == 0x003FF020) ||
|
||||||
|
(masked_003FF0E0 == 0x003FF060) ||
|
||||||
|
(masked_003FF0E0 == 0x003FF0E0) ||
|
||||||
|
(instr->Mask(0x00388000) == 0x00008000) ||
|
||||||
|
(instr->Mask(0x0038E000) == 0x00000000) ||
|
||||||
|
(instr->Mask(0x0039E000) == 0x00002000) ||
|
||||||
|
(instr->Mask(0x003AE000) == 0x00002000) ||
|
||||||
|
(instr->Mask(0x003CE000) == 0x00042000) ||
|
||||||
|
(instr->Mask(0x003FFFC0) == 0x000320C0) ||
|
||||||
|
(instr->Mask(0x003FF100) == 0x00032100) ||
|
||||||
|
(instr->Mask(0x003FF200) == 0x00032200) ||
|
||||||
|
(instr->Mask(0x003FF400) == 0x00032400) ||
|
||||||
|
(instr->Mask(0x003FF800) == 0x00032800) ||
|
||||||
|
(instr->Mask(0x0038F000) == 0x00005000) ||
|
||||||
|
(instr->Mask(0x0038E000) == 0x00006000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitSystem(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((instr->Bit(24) == 0x1) ||
|
||||||
|
(instr->Bits(20, 16) != 0x1F) ||
|
||||||
|
(instr->Bits(15, 10) != 0) ||
|
||||||
|
(instr->Bits(4, 0) != 0) ||
|
||||||
|
(instr->Bits(24, 21) == 0x3) ||
|
||||||
|
(instr->Bits(24, 22) == 0x3)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitUnconditionalBranchToRegister(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
case 7: {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeLoadStore(Instruction* instr) {
|
||||||
|
ASSERT((instr->Bits(27, 24) == 0x8) ||
|
||||||
|
(instr->Bits(27, 24) == 0x9) ||
|
||||||
|
(instr->Bits(27, 24) == 0xC) ||
|
||||||
|
(instr->Bits(27, 24) == 0xD) );
|
||||||
|
|
||||||
|
if (instr->Bit(24) == 0) {
|
||||||
|
if (instr->Bit(28) == 0) {
|
||||||
|
if (instr->Bit(29) == 0) {
|
||||||
|
if (instr->Bit(26) == 0) {
|
||||||
|
// TODO: VisitLoadStoreExclusive.
|
||||||
|
VisitUnimplemented(instr);
|
||||||
|
} else {
|
||||||
|
DecodeAdvSIMDLoadStore(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((instr->Bits(31, 30) == 0x3) ||
|
||||||
|
(instr->Mask(0xC4400000) == 0x40000000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(23) == 0) {
|
||||||
|
if (instr->Mask(0xC4400000) == 0xC0400000) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitLoadStorePairNonTemporal(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VisitLoadStorePairPostIndex(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(29) == 0) {
|
||||||
|
if (instr->Mask(0xC4000000) == 0xC4000000) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitLoadLiteral(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((instr->Mask(0x84C00000) == 0x80C00000) ||
|
||||||
|
(instr->Mask(0x44800000) == 0x44800000) ||
|
||||||
|
(instr->Mask(0x84800000) == 0x84800000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(21) == 0) {
|
||||||
|
switch (instr->Bits(11, 10)) {
|
||||||
|
case 0: {
|
||||||
|
VisitLoadStoreUnscaledOffset(instr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
if (instr->Mask(0xC4C00000) == 0xC0800000) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitLoadStorePostIndex(instr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
// TODO: VisitLoadStoreRegisterOffsetUnpriv.
|
||||||
|
VisitUnimplemented(instr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
if (instr->Mask(0xC4C00000) == 0xC0800000) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitLoadStorePreIndex(instr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instr->Bits(11, 10) == 0x2) {
|
||||||
|
if (instr->Bit(14) == 0) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitLoadStoreRegisterOffset(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(28) == 0) {
|
||||||
|
if (instr->Bit(29) == 0) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if ((instr->Bits(31, 30) == 0x3) ||
|
||||||
|
(instr->Mask(0xC4400000) == 0x40000000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(23) == 0) {
|
||||||
|
VisitLoadStorePairOffset(instr);
|
||||||
|
} else {
|
||||||
|
VisitLoadStorePairPreIndex(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(29) == 0) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if ((instr->Mask(0x84C00000) == 0x80C00000) ||
|
||||||
|
(instr->Mask(0x44800000) == 0x44800000) ||
|
||||||
|
(instr->Mask(0x84800000) == 0x84800000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitLoadStoreUnsignedOffset(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeLogical(Instruction* instr) {
|
||||||
|
ASSERT(instr->Bits(27, 24) == 0x2);
|
||||||
|
|
||||||
|
if (instr->Mask(0x80400000) == 0x00400000) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(23) == 0) {
|
||||||
|
VisitLogicalImmediate(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bits(30, 29) == 0x1) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitMoveWideImmediate(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeBitfieldExtract(Instruction* instr) {
|
||||||
|
ASSERT(instr->Bits(27, 24) == 0x3);
|
||||||
|
|
||||||
|
if ((instr->Mask(0x80400000) == 0x80000000) ||
|
||||||
|
(instr->Mask(0x80400000) == 0x00400000) ||
|
||||||
|
(instr->Mask(0x80008000) == 0x00008000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else if (instr->Bit(23) == 0) {
|
||||||
|
if ((instr->Mask(0x80200000) == 0x00200000) ||
|
||||||
|
(instr->Mask(0x60000000) == 0x60000000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitBitfield(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((instr->Mask(0x60200000) == 0x00200000) ||
|
||||||
|
(instr->Mask(0x60000000) != 0x00000000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitExtract(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeAddSubImmediate(Instruction* instr) {
|
||||||
|
ASSERT(instr->Bits(27, 24) == 0x1);
|
||||||
|
if (instr->Bit(23) == 1) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitAddSubImmediate(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeDataProcessing(Instruction* instr) {
|
||||||
|
ASSERT((instr->Bits(27, 24) == 0xA) ||
|
||||||
|
(instr->Bits(27, 24) == 0xB) );
|
||||||
|
|
||||||
|
if (instr->Bit(24) == 0) {
|
||||||
|
if (instr->Bit(28) == 0) {
|
||||||
|
if (instr->Mask(0x80008000) == 0x00008000) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitLogicalShifted(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (instr->Bits(23, 21)) {
|
||||||
|
case 0: {
|
||||||
|
if (instr->Mask(0x0000FC00) != 0) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitAddSubWithCarry(instr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
if ((instr->Bit(29) == 0) ||
|
||||||
|
(instr->Mask(0x00000410) != 0)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(11) == 0) {
|
||||||
|
VisitConditionalCompareRegister(instr);
|
||||||
|
} else {
|
||||||
|
VisitConditionalCompareImmediate(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
if (instr->Mask(0x20000800) != 0x00000000) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitConditionalSelect(instr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 6: {
|
||||||
|
if (instr->Bit(29) == 0x1) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(30) == 0) {
|
||||||
|
if ((instr->Bit(15) == 0x1) ||
|
||||||
|
(instr->Bits(15, 11) == 0) ||
|
||||||
|
(instr->Bits(15, 12) == 0x1) ||
|
||||||
|
(instr->Bits(15, 12) == 0x3) ||
|
||||||
|
(instr->Bits(15, 13) == 0x3) ||
|
||||||
|
(instr->Mask(0x8000EC00) == 0x00004C00) ||
|
||||||
|
(instr->Mask(0x8000E800) == 0x80004000) ||
|
||||||
|
(instr->Mask(0x8000E400) == 0x80004000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitDataProcessing2Source(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((instr->Bit(13) == 1) ||
|
||||||
|
(instr->Bits(20, 16) != 0) ||
|
||||||
|
(instr->Bits(15, 14) != 0) ||
|
||||||
|
(instr->Mask(0xA01FFC00) == 0x00000C00) ||
|
||||||
|
(instr->Mask(0x201FF800) == 0x00001800)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitDataProcessing1Source(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
case 3:
|
||||||
|
case 5:
|
||||||
|
case 7: VisitUnallocated(instr); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(28) == 0) {
|
||||||
|
if (instr->Bit(21) == 0) {
|
||||||
|
if ((instr->Bits(23, 22) == 0x3) ||
|
||||||
|
(instr->Mask(0x80008000) == 0x00008000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitAddSubShifted(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((instr->Mask(0x00C00000) != 0x00000000) ||
|
||||||
|
(instr->Mask(0x00001400) == 0x00001400) ||
|
||||||
|
(instr->Mask(0x00001800) == 0x00001800)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitAddSubExtended(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((instr->Bit(30) == 0x1) ||
|
||||||
|
(instr->Bits(30, 29) == 0x1) ||
|
||||||
|
(instr->Mask(0xE0600000) == 0x00200000) ||
|
||||||
|
(instr->Mask(0xE0608000) == 0x00400000) ||
|
||||||
|
(instr->Mask(0x60608000) == 0x00408000) ||
|
||||||
|
(instr->Mask(0x60E00000) == 0x00E00000) ||
|
||||||
|
(instr->Mask(0x60E00000) == 0x00800000) ||
|
||||||
|
(instr->Mask(0x60E00000) == 0x00600000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitDataProcessing3Source(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeFP(Instruction* instr) {
|
||||||
|
ASSERT((instr->Bits(27, 24) == 0xE) ||
|
||||||
|
(instr->Bits(27, 24) == 0xF) );
|
||||||
|
|
||||||
|
if (instr->Bit(28) == 0) {
|
||||||
|
DecodeAdvSIMDDataProcessing(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(29) == 1) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bits(31, 30) == 0x3) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else if (instr->Bits(31, 30) == 0x1) {
|
||||||
|
DecodeAdvSIMDDataProcessing(instr);
|
||||||
|
} else {
|
||||||
|
if (instr->Bit(24) == 0) {
|
||||||
|
if (instr->Bit(21) == 0) {
|
||||||
|
if ((instr->Bit(23) == 1) ||
|
||||||
|
(instr->Bit(18) == 1) ||
|
||||||
|
(instr->Mask(0x80008000) == 0x00000000) ||
|
||||||
|
(instr->Mask(0x000E0000) == 0x00000000) ||
|
||||||
|
(instr->Mask(0x000E0000) == 0x000A0000) ||
|
||||||
|
(instr->Mask(0x00160000) == 0x00000000) ||
|
||||||
|
(instr->Mask(0x00160000) == 0x00120000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitFPFixedPointConvert(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instr->Bits(15, 10) == 32) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else if (instr->Bits(15, 10) == 0) {
|
||||||
|
if ((instr->Bits(23, 22) == 0x3) ||
|
||||||
|
(instr->Mask(0x000E0000) == 0x000A0000) ||
|
||||||
|
(instr->Mask(0x000E0000) == 0x000C0000) ||
|
||||||
|
(instr->Mask(0x00160000) == 0x00120000) ||
|
||||||
|
(instr->Mask(0x00160000) == 0x00140000) ||
|
||||||
|
(instr->Mask(0x20C40000) == 0x00800000) ||
|
||||||
|
(instr->Mask(0x20C60000) == 0x00840000) ||
|
||||||
|
(instr->Mask(0xA0C60000) == 0x80060000) ||
|
||||||
|
(instr->Mask(0xA0C60000) == 0x00860000) ||
|
||||||
|
(instr->Mask(0xA0C60000) == 0x00460000) ||
|
||||||
|
(instr->Mask(0xA0CE0000) == 0x80860000) ||
|
||||||
|
(instr->Mask(0xA0CE0000) == 0x804E0000) ||
|
||||||
|
(instr->Mask(0xA0CE0000) == 0x000E0000) ||
|
||||||
|
(instr->Mask(0xA0D60000) == 0x00160000) ||
|
||||||
|
(instr->Mask(0xA0D60000) == 0x80560000) ||
|
||||||
|
(instr->Mask(0xA0D60000) == 0x80960000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitFPIntegerConvert(instr);
|
||||||
|
}
|
||||||
|
} else if (instr->Bits(14, 10) == 16) {
|
||||||
|
const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000);
|
||||||
|
if ((instr->Mask(0x80180000) != 0) ||
|
||||||
|
(masked_A0DF8000 == 0x00020000) ||
|
||||||
|
(masked_A0DF8000 == 0x00030000) ||
|
||||||
|
(masked_A0DF8000 == 0x00068000) ||
|
||||||
|
(masked_A0DF8000 == 0x00428000) ||
|
||||||
|
(masked_A0DF8000 == 0x00430000) ||
|
||||||
|
(masked_A0DF8000 == 0x00468000) ||
|
||||||
|
(instr->Mask(0xA0D80000) == 0x00800000) ||
|
||||||
|
(instr->Mask(0xA0DE0000) == 0x00C00000) ||
|
||||||
|
(instr->Mask(0xA0DF0000) == 0x00C30000) ||
|
||||||
|
(instr->Mask(0xA0DC0000) == 0x00C40000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitFPDataProcessing1Source(instr);
|
||||||
|
}
|
||||||
|
} else if (instr->Bits(13, 10) == 8) {
|
||||||
|
if ((instr->Bits(15, 14) != 0) ||
|
||||||
|
(instr->Bits(2, 0) != 0) ||
|
||||||
|
(instr->Mask(0x80800000) != 0x00000000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitFPCompare(instr);
|
||||||
|
}
|
||||||
|
} else if (instr->Bits(12, 10) == 4) {
|
||||||
|
if ((instr->Bits(9, 5) != 0) ||
|
||||||
|
(instr->Mask(0x80800000) != 0x00000000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitFPImmediate(instr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instr->Mask(0x80800000) != 0x00000000) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
switch (instr->Bits(11, 10)) {
|
||||||
|
case 1: {
|
||||||
|
VisitFPConditionalCompare(instr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
if ((instr->Bits(15, 14) == 0x3) ||
|
||||||
|
(instr->Mask(0x00009000) == 0x00009000) ||
|
||||||
|
(instr->Mask(0x0000A000) == 0x0000A000)) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitFPDataProcessing2Source(instr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
VisitFPConditionalSelect(instr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Bit 30 == 1 has been handled earlier.
|
||||||
|
ASSERT(instr->Bit(30) == 0);
|
||||||
|
if (instr->Mask(0xA0800000) != 0) {
|
||||||
|
VisitUnallocated(instr);
|
||||||
|
} else {
|
||||||
|
VisitFPDataProcessing3Source(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeAdvSIMDLoadStore(Instruction* instr) {
|
||||||
|
// TODO: Implement Advanced SIMD load/store instruction decode.
|
||||||
|
ASSERT(instr->Bits(29, 25) == 0x6);
|
||||||
|
VisitUnimplemented(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decoder::DecodeAdvSIMDDataProcessing(Instruction* instr) {
|
||||||
|
// TODO: Implement Advanced SIMD data processing instruction decode.
|
||||||
|
ASSERT(instr->Bits(27, 25) == 0x7);
|
||||||
|
VisitUnimplemented(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define DEFINE_VISITOR_CALLERS(A) \
|
||||||
|
void Decoder::Visit##A(Instruction *instr) { \
|
||||||
|
ASSERT(instr->Mask(A##FMask) == A##Fixed); \
|
||||||
|
std::list<DecoderVisitor*>::iterator it; \
|
||||||
|
for (it = visitors_.begin(); it != visitors_.end(); it++) { \
|
||||||
|
(*it)->Visit##A(instr); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
VISITOR_LIST(DEFINE_VISITOR_CALLERS)
|
||||||
|
#undef DEFINE_VISITOR_CALLERS
|
||||||
|
} // namespace vixl
|
||||||
198
disas/libvixl/a64/decoder-a64.h
Normal file
198
disas/libvixl/a64/decoder-a64.h
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef VIXL_A64_DECODER_A64_H_
|
||||||
|
#define VIXL_A64_DECODER_A64_H_
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
#include "a64/instructions-a64.h"
|
||||||
|
|
||||||
|
|
||||||
|
// List macro containing all visitors needed by the decoder class.
|
||||||
|
|
||||||
|
#define VISITOR_LIST(V) \
|
||||||
|
V(PCRelAddressing) \
|
||||||
|
V(AddSubImmediate) \
|
||||||
|
V(LogicalImmediate) \
|
||||||
|
V(MoveWideImmediate) \
|
||||||
|
V(Bitfield) \
|
||||||
|
V(Extract) \
|
||||||
|
V(UnconditionalBranch) \
|
||||||
|
V(UnconditionalBranchToRegister) \
|
||||||
|
V(CompareBranch) \
|
||||||
|
V(TestBranch) \
|
||||||
|
V(ConditionalBranch) \
|
||||||
|
V(System) \
|
||||||
|
V(Exception) \
|
||||||
|
V(LoadStorePairPostIndex) \
|
||||||
|
V(LoadStorePairOffset) \
|
||||||
|
V(LoadStorePairPreIndex) \
|
||||||
|
V(LoadStorePairNonTemporal) \
|
||||||
|
V(LoadLiteral) \
|
||||||
|
V(LoadStoreUnscaledOffset) \
|
||||||
|
V(LoadStorePostIndex) \
|
||||||
|
V(LoadStorePreIndex) \
|
||||||
|
V(LoadStoreRegisterOffset) \
|
||||||
|
V(LoadStoreUnsignedOffset) \
|
||||||
|
V(LogicalShifted) \
|
||||||
|
V(AddSubShifted) \
|
||||||
|
V(AddSubExtended) \
|
||||||
|
V(AddSubWithCarry) \
|
||||||
|
V(ConditionalCompareRegister) \
|
||||||
|
V(ConditionalCompareImmediate) \
|
||||||
|
V(ConditionalSelect) \
|
||||||
|
V(DataProcessing1Source) \
|
||||||
|
V(DataProcessing2Source) \
|
||||||
|
V(DataProcessing3Source) \
|
||||||
|
V(FPCompare) \
|
||||||
|
V(FPConditionalCompare) \
|
||||||
|
V(FPConditionalSelect) \
|
||||||
|
V(FPImmediate) \
|
||||||
|
V(FPDataProcessing1Source) \
|
||||||
|
V(FPDataProcessing2Source) \
|
||||||
|
V(FPDataProcessing3Source) \
|
||||||
|
V(FPIntegerConvert) \
|
||||||
|
V(FPFixedPointConvert) \
|
||||||
|
V(Unallocated) \
|
||||||
|
V(Unimplemented)
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
|
||||||
|
// The Visitor interface. Disassembler and simulator (and other tools)
|
||||||
|
// must provide implementations for all of these functions.
|
||||||
|
class DecoderVisitor {
|
||||||
|
public:
|
||||||
|
#define DECLARE(A) virtual void Visit##A(Instruction* instr) = 0;
|
||||||
|
VISITOR_LIST(DECLARE)
|
||||||
|
#undef DECLARE
|
||||||
|
|
||||||
|
virtual ~DecoderVisitor() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Visitors are registered in a list.
|
||||||
|
std::list<DecoderVisitor*> visitors_;
|
||||||
|
|
||||||
|
friend class Decoder;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Decoder: public DecoderVisitor {
|
||||||
|
public:
|
||||||
|
Decoder() {}
|
||||||
|
|
||||||
|
// Top-level instruction decoder function. Decodes an instruction and calls
|
||||||
|
// the visitor functions registered with the Decoder class.
|
||||||
|
void Decode(Instruction *instr);
|
||||||
|
|
||||||
|
// Register a new visitor class with the decoder.
|
||||||
|
// Decode() will call the corresponding visitor method from all registered
|
||||||
|
// visitor classes when decoding reaches the leaf node of the instruction
|
||||||
|
// decode tree.
|
||||||
|
// Visitors are called in the order.
|
||||||
|
// A visitor can only be registered once.
|
||||||
|
// Registering an already registered visitor will update its position.
|
||||||
|
//
|
||||||
|
// d.AppendVisitor(V1);
|
||||||
|
// d.AppendVisitor(V2);
|
||||||
|
// d.PrependVisitor(V2); // Move V2 at the start of the list.
|
||||||
|
// d.InsertVisitorBefore(V3, V2);
|
||||||
|
// d.AppendVisitor(V4);
|
||||||
|
// d.AppendVisitor(V4); // No effect.
|
||||||
|
//
|
||||||
|
// d.Decode(i);
|
||||||
|
//
|
||||||
|
// will call in order visitor methods in V3, V2, V1, V4.
|
||||||
|
void AppendVisitor(DecoderVisitor* visitor);
|
||||||
|
void PrependVisitor(DecoderVisitor* visitor);
|
||||||
|
void InsertVisitorBefore(DecoderVisitor* new_visitor,
|
||||||
|
DecoderVisitor* registered_visitor);
|
||||||
|
void InsertVisitorAfter(DecoderVisitor* new_visitor,
|
||||||
|
DecoderVisitor* registered_visitor);
|
||||||
|
|
||||||
|
// Remove a previously registered visitor class from the list of visitors
|
||||||
|
// stored by the decoder.
|
||||||
|
void RemoveVisitor(DecoderVisitor* visitor);
|
||||||
|
|
||||||
|
#define DECLARE(A) void Visit##A(Instruction* instr);
|
||||||
|
VISITOR_LIST(DECLARE)
|
||||||
|
#undef DECLARE
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Decode the PC relative addressing instruction, and call the corresponding
|
||||||
|
// visitors.
|
||||||
|
// On entry, instruction bits 27:24 = 0x0.
|
||||||
|
void DecodePCRelAddressing(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the add/subtract immediate instruction, and call the correspoding
|
||||||
|
// visitors.
|
||||||
|
// On entry, instruction bits 27:24 = 0x1.
|
||||||
|
void DecodeAddSubImmediate(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the branch, system command, and exception generation parts of
|
||||||
|
// the instruction tree, and call the corresponding visitors.
|
||||||
|
// On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}.
|
||||||
|
void DecodeBranchSystemException(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the load and store parts of the instruction tree, and call
|
||||||
|
// the corresponding visitors.
|
||||||
|
// On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}.
|
||||||
|
void DecodeLoadStore(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the logical immediate and move wide immediate parts of the
|
||||||
|
// instruction tree, and call the corresponding visitors.
|
||||||
|
// On entry, instruction bits 27:24 = 0x2.
|
||||||
|
void DecodeLogical(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the bitfield and extraction parts of the instruction tree,
|
||||||
|
// and call the corresponding visitors.
|
||||||
|
// On entry, instruction bits 27:24 = 0x3.
|
||||||
|
void DecodeBitfieldExtract(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the data processing parts of the instruction tree, and call the
|
||||||
|
// corresponding visitors.
|
||||||
|
// On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}.
|
||||||
|
void DecodeDataProcessing(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the floating point parts of the instruction tree, and call the
|
||||||
|
// corresponding visitors.
|
||||||
|
// On entry, instruction bits 27:24 = {0xE, 0xF}.
|
||||||
|
void DecodeFP(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the Advanced SIMD (NEON) load/store part of the instruction tree,
|
||||||
|
// and call the corresponding visitors.
|
||||||
|
// On entry, instruction bits 29:25 = 0x6.
|
||||||
|
void DecodeAdvSIMDLoadStore(Instruction* instr);
|
||||||
|
|
||||||
|
// Decode the Advanced SIMD (NEON) data processing part of the instruction
|
||||||
|
// tree, and call the corresponding visitors.
|
||||||
|
// On entry, instruction bits 27:25 = 0x7.
|
||||||
|
void DecodeAdvSIMDDataProcessing(Instruction* instr);
|
||||||
|
};
|
||||||
|
} // namespace vixl
|
||||||
|
|
||||||
|
#endif // VIXL_A64_DECODER_A64_H_
|
||||||
1678
disas/libvixl/a64/disasm-a64.cc
Normal file
1678
disas/libvixl/a64/disasm-a64.cc
Normal file
File diff suppressed because it is too large
Load Diff
109
disas/libvixl/a64/disasm-a64.h
Normal file
109
disas/libvixl/a64/disasm-a64.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef VIXL_A64_DISASM_A64_H
|
||||||
|
#define VIXL_A64_DISASM_A64_H
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "instructions-a64.h"
|
||||||
|
#include "decoder-a64.h"
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
|
||||||
|
class Disassembler: public DecoderVisitor {
|
||||||
|
public:
|
||||||
|
Disassembler();
|
||||||
|
Disassembler(char* text_buffer, int buffer_size);
|
||||||
|
virtual ~Disassembler();
|
||||||
|
char* GetOutput();
|
||||||
|
|
||||||
|
// Declare all Visitor functions.
|
||||||
|
#define DECLARE(A) void Visit##A(Instruction* instr);
|
||||||
|
VISITOR_LIST(DECLARE)
|
||||||
|
#undef DECLARE
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void ProcessOutput(Instruction* instr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Format(Instruction* instr, const char* mnemonic, const char* format);
|
||||||
|
void Substitute(Instruction* instr, const char* string);
|
||||||
|
int SubstituteField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteRegisterField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteImmediateField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteLiteralField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteBitfieldImmediateField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteShiftField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteExtendField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteConditionField(Instruction* instr, const char* format);
|
||||||
|
int SubstitutePCRelAddressField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteBranchTargetField(Instruction* instr, const char* format);
|
||||||
|
int SubstituteLSRegOffsetField(Instruction* instr, const char* format);
|
||||||
|
int SubstitutePrefetchField(Instruction* instr, const char* format);
|
||||||
|
|
||||||
|
inline bool RdIsZROrSP(Instruction* instr) const {
|
||||||
|
return (instr->Rd() == kZeroRegCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool RnIsZROrSP(Instruction* instr) const {
|
||||||
|
return (instr->Rn() == kZeroRegCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool RmIsZROrSP(Instruction* instr) const {
|
||||||
|
return (instr->Rm() == kZeroRegCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool RaIsZROrSP(Instruction* instr) const {
|
||||||
|
return (instr->Ra() == kZeroRegCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsMovzMovnImm(unsigned reg_size, uint64_t value);
|
||||||
|
|
||||||
|
void ResetOutput();
|
||||||
|
void AppendToOutput(const char* string, ...);
|
||||||
|
|
||||||
|
char* buffer_;
|
||||||
|
uint32_t buffer_pos_;
|
||||||
|
uint32_t buffer_size_;
|
||||||
|
bool own_buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PrintDisassembler: public Disassembler {
|
||||||
|
public:
|
||||||
|
explicit PrintDisassembler(FILE* stream) : stream_(stream) { }
|
||||||
|
~PrintDisassembler() { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void ProcessOutput(Instruction* instr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *stream_;
|
||||||
|
};
|
||||||
|
} // namespace vixl
|
||||||
|
|
||||||
|
#endif // VIXL_A64_DISASM_A64_H
|
||||||
238
disas/libvixl/a64/instructions-a64.cc
Normal file
238
disas/libvixl/a64/instructions-a64.cc
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "a64/instructions-a64.h"
|
||||||
|
#include "a64/assembler-a64.h"
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
|
||||||
|
|
||||||
|
static uint64_t RotateRight(uint64_t value,
|
||||||
|
unsigned int rotate,
|
||||||
|
unsigned int width) {
|
||||||
|
ASSERT(width <= 64);
|
||||||
|
rotate &= 63;
|
||||||
|
return ((value & ((1UL << rotate) - 1UL)) << (width - rotate)) |
|
||||||
|
(value >> rotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint64_t RepeatBitsAcrossReg(unsigned reg_size,
|
||||||
|
uint64_t value,
|
||||||
|
unsigned width) {
|
||||||
|
ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) ||
|
||||||
|
(width == 32));
|
||||||
|
ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize));
|
||||||
|
uint64_t result = value & ((1UL << width) - 1UL);
|
||||||
|
for (unsigned i = width; i < reg_size; i *= 2) {
|
||||||
|
result |= (result << i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Logical immediates can't encode zero, so a return value of zero is used to
|
||||||
|
// indicate a failure case. Specifically, where the constraints on imm_s are
|
||||||
|
// not met.
|
||||||
|
uint64_t Instruction::ImmLogical() {
|
||||||
|
unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize;
|
||||||
|
int64_t n = BitN();
|
||||||
|
int64_t imm_s = ImmSetBits();
|
||||||
|
int64_t imm_r = ImmRotate();
|
||||||
|
|
||||||
|
// An integer is constructed from the n, imm_s and imm_r bits according to
|
||||||
|
// the following table:
|
||||||
|
//
|
||||||
|
// N imms immr size S R
|
||||||
|
// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
|
||||||
|
// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
|
||||||
|
// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
|
||||||
|
// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
|
||||||
|
// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
|
||||||
|
// 0 11110s xxxxxr 2 UInt(s) UInt(r)
|
||||||
|
// (s bits must not be all set)
|
||||||
|
//
|
||||||
|
// A pattern is constructed of size bits, where the least significant S+1
|
||||||
|
// bits are set. The pattern is rotated right by R, and repeated across a
|
||||||
|
// 32 or 64-bit value, depending on destination register width.
|
||||||
|
//
|
||||||
|
|
||||||
|
if (n == 1) {
|
||||||
|
if (imm_s == 0x3F) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint64_t bits = (1UL << (imm_s + 1)) - 1;
|
||||||
|
return RotateRight(bits, imm_r, 64);
|
||||||
|
} else {
|
||||||
|
if ((imm_s >> 1) == 0x1F) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (int width = 0x20; width >= 0x2; width >>= 1) {
|
||||||
|
if ((imm_s & width) == 0) {
|
||||||
|
int mask = width - 1;
|
||||||
|
if ((imm_s & mask) == mask) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint64_t bits = (1UL << ((imm_s & mask) + 1)) - 1;
|
||||||
|
return RepeatBitsAcrossReg(reg_size,
|
||||||
|
RotateRight(bits, imm_r & mask, width),
|
||||||
|
width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float Instruction::ImmFP32() {
|
||||||
|
// ImmFP: abcdefgh (8 bits)
|
||||||
|
// Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits)
|
||||||
|
// where B is b ^ 1
|
||||||
|
uint32_t bits = ImmFP();
|
||||||
|
uint32_t bit7 = (bits >> 7) & 0x1;
|
||||||
|
uint32_t bit6 = (bits >> 6) & 0x1;
|
||||||
|
uint32_t bit5_to_0 = bits & 0x3f;
|
||||||
|
uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19);
|
||||||
|
|
||||||
|
return rawbits_to_float(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double Instruction::ImmFP64() {
|
||||||
|
// ImmFP: abcdefgh (8 bits)
|
||||||
|
// Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000
|
||||||
|
// 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits)
|
||||||
|
// where B is b ^ 1
|
||||||
|
uint32_t bits = ImmFP();
|
||||||
|
uint64_t bit7 = (bits >> 7) & 0x1;
|
||||||
|
uint64_t bit6 = (bits >> 6) & 0x1;
|
||||||
|
uint64_t bit5_to_0 = bits & 0x3f;
|
||||||
|
uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48);
|
||||||
|
|
||||||
|
return rawbits_to_double(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LSDataSize CalcLSPairDataSize(LoadStorePairOp op) {
|
||||||
|
switch (op) {
|
||||||
|
case STP_x:
|
||||||
|
case LDP_x:
|
||||||
|
case STP_d:
|
||||||
|
case LDP_d: return LSDoubleWord;
|
||||||
|
default: return LSWord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Instruction* Instruction::ImmPCOffsetTarget() {
|
||||||
|
ptrdiff_t offset;
|
||||||
|
if (IsPCRelAddressing()) {
|
||||||
|
// PC-relative addressing. Only ADR is supported.
|
||||||
|
offset = ImmPCRel();
|
||||||
|
} else {
|
||||||
|
// All PC-relative branches.
|
||||||
|
ASSERT(BranchType() != UnknownBranchType);
|
||||||
|
// Relative branch offsets are instruction-size-aligned.
|
||||||
|
offset = ImmBranch() << kInstructionSizeLog2;
|
||||||
|
}
|
||||||
|
return this + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline int Instruction::ImmBranch() const {
|
||||||
|
switch (BranchType()) {
|
||||||
|
case CondBranchType: return ImmCondBranch();
|
||||||
|
case UncondBranchType: return ImmUncondBranch();
|
||||||
|
case CompareBranchType: return ImmCmpBranch();
|
||||||
|
case TestBranchType: return ImmTestBranch();
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Instruction::SetImmPCOffsetTarget(Instruction* target) {
|
||||||
|
if (IsPCRelAddressing()) {
|
||||||
|
SetPCRelImmTarget(target);
|
||||||
|
} else {
|
||||||
|
SetBranchImmTarget(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Instruction::SetPCRelImmTarget(Instruction* target) {
|
||||||
|
// ADRP is not supported, so 'this' must point to an ADR instruction.
|
||||||
|
ASSERT(Mask(PCRelAddressingMask) == ADR);
|
||||||
|
|
||||||
|
Instr imm = Assembler::ImmPCRelAddress(target - this);
|
||||||
|
|
||||||
|
SetInstructionBits(Mask(~ImmPCRel_mask) | imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Instruction::SetBranchImmTarget(Instruction* target) {
|
||||||
|
ASSERT(((target - this) & 3) == 0);
|
||||||
|
Instr branch_imm = 0;
|
||||||
|
uint32_t imm_mask = 0;
|
||||||
|
int offset = (target - this) >> kInstructionSizeLog2;
|
||||||
|
switch (BranchType()) {
|
||||||
|
case CondBranchType: {
|
||||||
|
branch_imm = Assembler::ImmCondBranch(offset);
|
||||||
|
imm_mask = ImmCondBranch_mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UncondBranchType: {
|
||||||
|
branch_imm = Assembler::ImmUncondBranch(offset);
|
||||||
|
imm_mask = ImmUncondBranch_mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CompareBranchType: {
|
||||||
|
branch_imm = Assembler::ImmCmpBranch(offset);
|
||||||
|
imm_mask = ImmCmpBranch_mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TestBranchType: {
|
||||||
|
branch_imm = Assembler::ImmTestBranch(offset);
|
||||||
|
imm_mask = ImmTestBranch_mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
SetInstructionBits(Mask(~imm_mask) | branch_imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Instruction::SetImmLLiteral(Instruction* source) {
|
||||||
|
ASSERT(((source - this) & 3) == 0);
|
||||||
|
int offset = (source - this) >> kLiteralEntrySizeLog2;
|
||||||
|
Instr imm = Assembler::ImmLLiteral(offset);
|
||||||
|
Instr mask = ImmLLiteral_mask;
|
||||||
|
|
||||||
|
SetInstructionBits(Mask(~mask) | imm);
|
||||||
|
}
|
||||||
|
} // namespace vixl
|
||||||
|
|
||||||
344
disas/libvixl/a64/instructions-a64.h
Normal file
344
disas/libvixl/a64/instructions-a64.h
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef VIXL_A64_INSTRUCTIONS_A64_H_
|
||||||
|
#define VIXL_A64_INSTRUCTIONS_A64_H_
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "a64/constants-a64.h"
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
// ISA constants. --------------------------------------------------------------
|
||||||
|
|
||||||
|
typedef uint32_t Instr;
|
||||||
|
const unsigned kInstructionSize = 4;
|
||||||
|
const unsigned kInstructionSizeLog2 = 2;
|
||||||
|
const unsigned kLiteralEntrySize = 4;
|
||||||
|
const unsigned kLiteralEntrySizeLog2 = 2;
|
||||||
|
const unsigned kMaxLoadLiteralRange = 1 * MBytes;
|
||||||
|
|
||||||
|
const unsigned kWRegSize = 32;
|
||||||
|
const unsigned kWRegSizeLog2 = 5;
|
||||||
|
const unsigned kWRegSizeInBytes = kWRegSize / 8;
|
||||||
|
const unsigned kXRegSize = 64;
|
||||||
|
const unsigned kXRegSizeLog2 = 6;
|
||||||
|
const unsigned kXRegSizeInBytes = kXRegSize / 8;
|
||||||
|
const unsigned kSRegSize = 32;
|
||||||
|
const unsigned kSRegSizeLog2 = 5;
|
||||||
|
const unsigned kSRegSizeInBytes = kSRegSize / 8;
|
||||||
|
const unsigned kDRegSize = 64;
|
||||||
|
const unsigned kDRegSizeLog2 = 6;
|
||||||
|
const unsigned kDRegSizeInBytes = kDRegSize / 8;
|
||||||
|
const int64_t kWRegMask = 0x00000000ffffffffLL;
|
||||||
|
const int64_t kXRegMask = 0xffffffffffffffffLL;
|
||||||
|
const int64_t kSRegMask = 0x00000000ffffffffLL;
|
||||||
|
const int64_t kDRegMask = 0xffffffffffffffffLL;
|
||||||
|
const int64_t kXSignMask = 0x1LL << 63;
|
||||||
|
const int64_t kWSignMask = 0x1LL << 31;
|
||||||
|
const int64_t kByteMask = 0xffL;
|
||||||
|
const int64_t kHalfWordMask = 0xffffL;
|
||||||
|
const int64_t kWordMask = 0xffffffffLL;
|
||||||
|
const uint64_t kXMaxUInt = 0xffffffffffffffffULL;
|
||||||
|
const uint64_t kWMaxUInt = 0xffffffffULL;
|
||||||
|
const int64_t kXMaxInt = 0x7fffffffffffffffLL;
|
||||||
|
const int64_t kXMinInt = 0x8000000000000000LL;
|
||||||
|
const int32_t kWMaxInt = 0x7fffffff;
|
||||||
|
const int32_t kWMinInt = 0x80000000;
|
||||||
|
const unsigned kLinkRegCode = 30;
|
||||||
|
const unsigned kZeroRegCode = 31;
|
||||||
|
const unsigned kSPRegInternalCode = 63;
|
||||||
|
const unsigned kRegCodeMask = 0x1f;
|
||||||
|
|
||||||
|
// AArch64 floating-point specifics. These match IEEE-754.
|
||||||
|
const unsigned kDoubleMantissaBits = 52;
|
||||||
|
const unsigned kDoubleExponentBits = 11;
|
||||||
|
const unsigned kFloatMantissaBits = 23;
|
||||||
|
const unsigned kFloatExponentBits = 8;
|
||||||
|
|
||||||
|
const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000);
|
||||||
|
const float kFP32NegativeInfinity = rawbits_to_float(0xff800000);
|
||||||
|
const double kFP64PositiveInfinity = rawbits_to_double(0x7ff0000000000000ULL);
|
||||||
|
const double kFP64NegativeInfinity = rawbits_to_double(0xfff0000000000000ULL);
|
||||||
|
|
||||||
|
// This value is a signalling NaN as both a double and as a float (taking the
|
||||||
|
// least-significant word).
|
||||||
|
static const double kFP64SignallingNaN = rawbits_to_double(0x7ff000007f800001ULL);
|
||||||
|
static const float kFP32SignallingNaN = rawbits_to_float(0x7f800001);
|
||||||
|
|
||||||
|
// A similar value, but as a quiet NaN.
|
||||||
|
static const double kFP64QuietNaN = rawbits_to_double(0x7ff800007fc00001ULL);
|
||||||
|
static const float kFP32QuietNaN = rawbits_to_float(0x7fc00001);
|
||||||
|
|
||||||
|
enum LSDataSize {
|
||||||
|
LSByte = 0,
|
||||||
|
LSHalfword = 1,
|
||||||
|
LSWord = 2,
|
||||||
|
LSDoubleWord = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
LSDataSize CalcLSPairDataSize(LoadStorePairOp op);
|
||||||
|
|
||||||
|
enum ImmBranchType {
|
||||||
|
UnknownBranchType = 0,
|
||||||
|
CondBranchType = 1,
|
||||||
|
UncondBranchType = 2,
|
||||||
|
CompareBranchType = 3,
|
||||||
|
TestBranchType = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AddrMode {
|
||||||
|
Offset,
|
||||||
|
PreIndex,
|
||||||
|
PostIndex
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FPRounding {
|
||||||
|
// The first four values are encodable directly by FPCR<RMode>.
|
||||||
|
FPTieEven = 0x0,
|
||||||
|
FPPositiveInfinity = 0x1,
|
||||||
|
FPNegativeInfinity = 0x2,
|
||||||
|
FPZero = 0x3,
|
||||||
|
|
||||||
|
// The final rounding mode is only available when explicitly specified by the
|
||||||
|
// instruction (such as with fcvta). It cannot be set in FPCR.
|
||||||
|
FPTieAway
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Reg31Mode {
|
||||||
|
Reg31IsStackPointer,
|
||||||
|
Reg31IsZeroRegister
|
||||||
|
};
|
||||||
|
|
||||||
|
// Instructions. ---------------------------------------------------------------
|
||||||
|
|
||||||
|
class Instruction {
|
||||||
|
public:
|
||||||
|
inline Instr InstructionBits() const {
|
||||||
|
return *(reinterpret_cast<const Instr*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetInstructionBits(Instr new_instr) {
|
||||||
|
*(reinterpret_cast<Instr*>(this)) = new_instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int Bit(int pos) const {
|
||||||
|
return (InstructionBits() >> pos) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t Bits(int msb, int lsb) const {
|
||||||
|
return unsigned_bitextract_32(msb, lsb, InstructionBits());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int32_t SignedBits(int msb, int lsb) const {
|
||||||
|
int32_t bits = *(reinterpret_cast<const int32_t*>(this));
|
||||||
|
return signed_bitextract_32(msb, lsb, bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Instr Mask(uint32_t mask) const {
|
||||||
|
return InstructionBits() & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
|
||||||
|
inline int64_t Name() const { return Func(HighBit, LowBit); }
|
||||||
|
INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
|
||||||
|
#undef DEFINE_GETTER
|
||||||
|
|
||||||
|
// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
|
||||||
|
// formed from ImmPCRelLo and ImmPCRelHi.
|
||||||
|
int ImmPCRel() const {
|
||||||
|
int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
|
||||||
|
int const width = ImmPCRelLo_width + ImmPCRelHi_width;
|
||||||
|
return signed_bitextract_32(width-1, 0, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ImmLogical();
|
||||||
|
float ImmFP32();
|
||||||
|
double ImmFP64();
|
||||||
|
|
||||||
|
inline LSDataSize SizeLSPair() const {
|
||||||
|
return CalcLSPairDataSize(
|
||||||
|
static_cast<LoadStorePairOp>(Mask(LoadStorePairMask)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers.
|
||||||
|
inline bool IsCondBranchImm() const {
|
||||||
|
return Mask(ConditionalBranchFMask) == ConditionalBranchFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsUncondBranchImm() const {
|
||||||
|
return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsCompareBranch() const {
|
||||||
|
return Mask(CompareBranchFMask) == CompareBranchFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsTestBranch() const {
|
||||||
|
return Mask(TestBranchFMask) == TestBranchFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsPCRelAddressing() const {
|
||||||
|
return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsLogicalImmediate() const {
|
||||||
|
return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsAddSubImmediate() const {
|
||||||
|
return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsAddSubExtended() const {
|
||||||
|
return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsLoadOrStore() const {
|
||||||
|
return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsMovn() const {
|
||||||
|
return (Mask(MoveWideImmediateMask) == MOVN_x) ||
|
||||||
|
(Mask(MoveWideImmediateMask) == MOVN_w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicate whether Rd can be the stack pointer or the zero register. This
|
||||||
|
// does not check that the instruction actually has an Rd field.
|
||||||
|
inline Reg31Mode RdMode() const {
|
||||||
|
// The following instructions use sp or wsp as Rd:
|
||||||
|
// Add/sub (immediate) when not setting the flags.
|
||||||
|
// Add/sub (extended) when not setting the flags.
|
||||||
|
// Logical (immediate) when not setting the flags.
|
||||||
|
// Otherwise, r31 is the zero register.
|
||||||
|
if (IsAddSubImmediate() || IsAddSubExtended()) {
|
||||||
|
if (Mask(AddSubSetFlagsBit)) {
|
||||||
|
return Reg31IsZeroRegister;
|
||||||
|
} else {
|
||||||
|
return Reg31IsStackPointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsLogicalImmediate()) {
|
||||||
|
// Of the logical (immediate) instructions, only ANDS (and its aliases)
|
||||||
|
// can set the flags. The others can all write into sp.
|
||||||
|
// Note that some logical operations are not available to
|
||||||
|
// immediate-operand instructions, so we have to combine two masks here.
|
||||||
|
if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) {
|
||||||
|
return Reg31IsZeroRegister;
|
||||||
|
} else {
|
||||||
|
return Reg31IsStackPointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Reg31IsZeroRegister;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicate whether Rn can be the stack pointer or the zero register. This
|
||||||
|
// does not check that the instruction actually has an Rn field.
|
||||||
|
inline Reg31Mode RnMode() const {
|
||||||
|
// The following instructions use sp or wsp as Rn:
|
||||||
|
// All loads and stores.
|
||||||
|
// Add/sub (immediate).
|
||||||
|
// Add/sub (extended).
|
||||||
|
// Otherwise, r31 is the zero register.
|
||||||
|
if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) {
|
||||||
|
return Reg31IsStackPointer;
|
||||||
|
}
|
||||||
|
return Reg31IsZeroRegister;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImmBranchType BranchType() const {
|
||||||
|
if (IsCondBranchImm()) {
|
||||||
|
return CondBranchType;
|
||||||
|
} else if (IsUncondBranchImm()) {
|
||||||
|
return UncondBranchType;
|
||||||
|
} else if (IsCompareBranch()) {
|
||||||
|
return CompareBranchType;
|
||||||
|
} else if (IsTestBranch()) {
|
||||||
|
return TestBranchType;
|
||||||
|
} else {
|
||||||
|
return UnknownBranchType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the target of this instruction. 'this' may be a branch or a
|
||||||
|
// PC-relative addressing instruction.
|
||||||
|
Instruction* ImmPCOffsetTarget();
|
||||||
|
|
||||||
|
// Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
|
||||||
|
// a PC-relative addressing instruction.
|
||||||
|
void SetImmPCOffsetTarget(Instruction* target);
|
||||||
|
// Patch a literal load instruction to load from 'source'.
|
||||||
|
void SetImmLLiteral(Instruction* source);
|
||||||
|
|
||||||
|
inline uint8_t* LiteralAddress() {
|
||||||
|
int offset = ImmLLiteral() << kLiteralEntrySizeLog2;
|
||||||
|
return reinterpret_cast<uint8_t*>(this) + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t Literal32() {
|
||||||
|
uint32_t literal;
|
||||||
|
memcpy(&literal, LiteralAddress(), sizeof(literal));
|
||||||
|
|
||||||
|
return literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint64_t Literal64() {
|
||||||
|
uint64_t literal;
|
||||||
|
memcpy(&literal, LiteralAddress(), sizeof(literal));
|
||||||
|
|
||||||
|
return literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float LiteralFP32() {
|
||||||
|
return rawbits_to_float(Literal32());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double LiteralFP64() {
|
||||||
|
return rawbits_to_double(Literal64());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Instruction* NextInstruction() {
|
||||||
|
return this + kInstructionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Instruction* InstructionAtOffset(int64_t offset) {
|
||||||
|
ASSERT(IsWordAligned(this + offset));
|
||||||
|
return this + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> static inline Instruction* Cast(T src) {
|
||||||
|
return reinterpret_cast<Instruction*>(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline int ImmBranch() const;
|
||||||
|
|
||||||
|
void SetPCRelImmTarget(Instruction* target);
|
||||||
|
void SetBranchImmTarget(Instruction* target);
|
||||||
|
};
|
||||||
|
} // namespace vixl
|
||||||
|
|
||||||
|
#endif // VIXL_A64_INSTRUCTIONS_A64_H_
|
||||||
65
disas/libvixl/globals.h
Normal file
65
disas/libvixl/globals.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef VIXL_GLOBALS_H
|
||||||
|
#define VIXL_GLOBALS_H
|
||||||
|
|
||||||
|
// Get the standard printf format macros for C99 stdint types.
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef uint8_t byte;
|
||||||
|
|
||||||
|
const int KBytes = 1024;
|
||||||
|
const int MBytes = 1024 * KBytes;
|
||||||
|
|
||||||
|
#define ABORT() printf("in %s, line %i", __FILE__, __LINE__); abort()
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define ASSERT(condition) assert(condition)
|
||||||
|
#define CHECK(condition) ASSERT(condition)
|
||||||
|
#define UNIMPLEMENTED() printf("UNIMPLEMENTED\t"); ABORT()
|
||||||
|
#define UNREACHABLE() printf("UNREACHABLE\t"); ABORT()
|
||||||
|
#else
|
||||||
|
#define ASSERT(condition) ((void) 0)
|
||||||
|
#define CHECK(condition) assert(condition)
|
||||||
|
#define UNIMPLEMENTED() ((void) 0)
|
||||||
|
#define UNREACHABLE() ((void) 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T> inline void USE(T) {}
|
||||||
|
|
||||||
|
#define ALIGNMENT_EXCEPTION() printf("ALIGNMENT EXCEPTION\t"); ABORT()
|
||||||
|
|
||||||
|
#endif // VIXL_GLOBALS_H
|
||||||
43
disas/libvixl/platform.h
Normal file
43
disas/libvixl/platform.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef PLATFORM_H
|
||||||
|
#define PLATFORM_H
|
||||||
|
|
||||||
|
// Define platform specific functionalities.
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
#ifdef USE_SIMULATOR
|
||||||
|
// Currently we assume running the simulator implies running on x86 hardware.
|
||||||
|
inline void HostBreakpoint() { asm("int3"); }
|
||||||
|
#else
|
||||||
|
inline void HostBreakpoint() {
|
||||||
|
// TODO: Implement HostBreakpoint on a64.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace vixl
|
||||||
|
|
||||||
|
#endif
|
||||||
126
disas/libvixl/utils.cc
Normal file
126
disas/libvixl/utils.cc
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
|
||||||
|
uint32_t float_to_rawbits(float value) {
|
||||||
|
uint32_t bits = 0;
|
||||||
|
memcpy(&bits, &value, 4);
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t double_to_rawbits(double value) {
|
||||||
|
uint64_t bits = 0;
|
||||||
|
memcpy(&bits, &value, 8);
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float rawbits_to_float(uint32_t bits) {
|
||||||
|
float value = 0.0;
|
||||||
|
memcpy(&value, &bits, 4);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double rawbits_to_double(uint64_t bits) {
|
||||||
|
double value = 0.0;
|
||||||
|
memcpy(&value, &bits, 8);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CountLeadingZeros(uint64_t value, int width) {
|
||||||
|
ASSERT((width == 32) || (width == 64));
|
||||||
|
int count = 0;
|
||||||
|
uint64_t bit_test = 1UL << (width - 1);
|
||||||
|
while ((count < width) && ((bit_test & value) == 0)) {
|
||||||
|
count++;
|
||||||
|
bit_test >>= 1;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CountLeadingSignBits(int64_t value, int width) {
|
||||||
|
ASSERT((width == 32) || (width == 64));
|
||||||
|
if (value >= 0) {
|
||||||
|
return CountLeadingZeros(value, width) - 1;
|
||||||
|
} else {
|
||||||
|
return CountLeadingZeros(~value, width) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CountTrailingZeros(uint64_t value, int width) {
|
||||||
|
ASSERT((width == 32) || (width == 64));
|
||||||
|
int count = 0;
|
||||||
|
while ((count < width) && (((value >> count) & 1) == 0)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CountSetBits(uint64_t value, int width) {
|
||||||
|
// TODO: Other widths could be added here, as the implementation already
|
||||||
|
// supports them.
|
||||||
|
ASSERT((width == 32) || (width == 64));
|
||||||
|
|
||||||
|
// Mask out unused bits to ensure that they are not counted.
|
||||||
|
value &= (0xffffffffffffffffULL >> (64-width));
|
||||||
|
|
||||||
|
// Add up the set bits.
|
||||||
|
// The algorithm works by adding pairs of bit fields together iteratively,
|
||||||
|
// where the size of each bit field doubles each time.
|
||||||
|
// An example for an 8-bit value:
|
||||||
|
// Bits: h g f e d c b a
|
||||||
|
// \ | \ | \ | \ |
|
||||||
|
// value = h+g f+e d+c b+a
|
||||||
|
// \ | \ |
|
||||||
|
// value = h+g+f+e d+c+b+a
|
||||||
|
// \ |
|
||||||
|
// value = h+g+f+e+d+c+b+a
|
||||||
|
value = ((value >> 1) & 0x5555555555555555ULL) +
|
||||||
|
(value & 0x5555555555555555ULL);
|
||||||
|
value = ((value >> 2) & 0x3333333333333333ULL) +
|
||||||
|
(value & 0x3333333333333333ULL);
|
||||||
|
value = ((value >> 4) & 0x0f0f0f0f0f0f0f0fULL) +
|
||||||
|
(value & 0x0f0f0f0f0f0f0f0fULL);
|
||||||
|
value = ((value >> 8) & 0x00ff00ff00ff00ffULL) +
|
||||||
|
(value & 0x00ff00ff00ff00ffULL);
|
||||||
|
value = ((value >> 16) & 0x0000ffff0000ffffULL) +
|
||||||
|
(value & 0x0000ffff0000ffffULL);
|
||||||
|
value = ((value >> 32) & 0x00000000ffffffffULL) +
|
||||||
|
(value & 0x00000000ffffffffULL);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
} // namespace vixl
|
||||||
126
disas/libvixl/utils.h
Normal file
126
disas/libvixl/utils.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2013, ARM Limited
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without
|
||||||
|
// specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef VIXL_UTILS_H
|
||||||
|
#define VIXL_UTILS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
namespace vixl {
|
||||||
|
|
||||||
|
// Check number width.
|
||||||
|
inline bool is_intn(unsigned n, int64_t x) {
|
||||||
|
ASSERT((0 < n) && (n < 64));
|
||||||
|
int64_t limit = 1ULL << (n - 1);
|
||||||
|
return (-limit <= x) && (x < limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_uintn(unsigned n, int64_t x) {
|
||||||
|
ASSERT((0 < n) && (n < 64));
|
||||||
|
return !(x >> n);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned truncate_to_intn(unsigned n, int64_t x) {
|
||||||
|
ASSERT((0 < n) && (n < 64));
|
||||||
|
return (x & ((1ULL << n) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INT_1_TO_63_LIST(V) \
|
||||||
|
V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \
|
||||||
|
V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \
|
||||||
|
V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \
|
||||||
|
V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \
|
||||||
|
V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \
|
||||||
|
V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \
|
||||||
|
V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \
|
||||||
|
V(57) V(58) V(59) V(60) V(61) V(62) V(63)
|
||||||
|
|
||||||
|
#define DECLARE_IS_INT_N(N) \
|
||||||
|
inline bool is_int##N(int64_t x) { return is_intn(N, x); }
|
||||||
|
#define DECLARE_IS_UINT_N(N) \
|
||||||
|
inline bool is_uint##N(int64_t x) { return is_uintn(N, x); }
|
||||||
|
#define DECLARE_TRUNCATE_TO_INT_N(N) \
|
||||||
|
inline int truncate_to_int##N(int x) { return truncate_to_intn(N, x); }
|
||||||
|
INT_1_TO_63_LIST(DECLARE_IS_INT_N)
|
||||||
|
INT_1_TO_63_LIST(DECLARE_IS_UINT_N)
|
||||||
|
INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N)
|
||||||
|
#undef DECLARE_IS_INT_N
|
||||||
|
#undef DECLARE_IS_UINT_N
|
||||||
|
#undef DECLARE_TRUNCATE_TO_INT_N
|
||||||
|
|
||||||
|
// Bit field extraction.
|
||||||
|
inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) {
|
||||||
|
return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) {
|
||||||
|
return (x >> lsb) & ((static_cast<uint64_t>(1) << (1 + msb - lsb)) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) {
|
||||||
|
return (x << (31 - msb)) >> (lsb + 31 - msb);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) {
|
||||||
|
return (x << (63 - msb)) >> (lsb + 63 - msb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating point representation
|
||||||
|
uint32_t float_to_rawbits(float value);
|
||||||
|
uint64_t double_to_rawbits(double value);
|
||||||
|
float rawbits_to_float(uint32_t bits);
|
||||||
|
double rawbits_to_double(uint64_t bits);
|
||||||
|
|
||||||
|
// Bits counting.
|
||||||
|
int CountLeadingZeros(uint64_t value, int width);
|
||||||
|
int CountLeadingSignBits(int64_t value, int width);
|
||||||
|
int CountTrailingZeros(uint64_t value, int width);
|
||||||
|
int CountSetBits(uint64_t value, int width);
|
||||||
|
|
||||||
|
// Pointer alignment
|
||||||
|
// TODO: rename/refactor to make it specific to instructions.
|
||||||
|
template<typename T>
|
||||||
|
bool IsWordAligned(T pointer) {
|
||||||
|
ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof)
|
||||||
|
return (reinterpret_cast<intptr_t>(pointer) & 3) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment a pointer until it has the specified alignment.
|
||||||
|
template<class T>
|
||||||
|
T AlignUp(T pointer, size_t alignment) {
|
||||||
|
ASSERT(sizeof(pointer) == sizeof(uintptr_t));
|
||||||
|
uintptr_t pointer_raw = reinterpret_cast<uintptr_t>(pointer);
|
||||||
|
size_t align_step = (alignment - pointer_raw) % alignment;
|
||||||
|
ASSERT((pointer_raw + align_step) % alignment == 0);
|
||||||
|
return reinterpret_cast<T>(pointer_raw + align_step);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace vixl
|
||||||
|
|
||||||
|
#endif // VIXL_UTILS_H
|
||||||
@@ -225,6 +225,45 @@ Data:
|
|||||||
"timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
|
"timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUORUM_FAILURE
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Emitted by the Quorum block driver if it fails to establish a quorum.
|
||||||
|
|
||||||
|
Data:
|
||||||
|
|
||||||
|
- "reference": device name if defined else node name.
|
||||||
|
- "sector-num": Number of the first sector of the failed read operation.
|
||||||
|
- "sector-count": Failed read operation sector count.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{ "event": "QUORUM_FAILURE",
|
||||||
|
"data": { "reference": "usr1", "sector-num": 345435, "sector-count": 5 },
|
||||||
|
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
|
||||||
|
|
||||||
|
QUORUM_REPORT_BAD
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Emitted to report a corruption of a Quorum file.
|
||||||
|
|
||||||
|
Data:
|
||||||
|
|
||||||
|
- "error": Error message (json-string, optional)
|
||||||
|
Only present on failure. This field contains a human-readable
|
||||||
|
error message. There are no semantics other than that the
|
||||||
|
block layer reported an error and clients should not try to
|
||||||
|
interpret the error string.
|
||||||
|
- "node-name": The graph node name of the block driver state.
|
||||||
|
- "sector-num": Number of the first sector of the failed read operation.
|
||||||
|
- "sector-count": Failed read operation sector count.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{ "event": "QUORUM_REPORT_BAD",
|
||||||
|
"data": { "node-name": "1.raw", "sector-num": 345435, "sector-count": 5 },
|
||||||
|
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
|
||||||
|
|
||||||
RESET
|
RESET
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ bulk-phase round of the migration and can be enabled for extremely
|
|||||||
high-performance RDMA hardware using the following command:
|
high-performance RDMA hardware using the following command:
|
||||||
|
|
||||||
QEMU Monitor Command:
|
QEMU Monitor Command:
|
||||||
$ migrate_set_capability x-rdma-pin-all on # disabled by default
|
$ migrate_set_capability rdma-pin-all on # disabled by default
|
||||||
|
|
||||||
Performing this action will cause all 8GB to be pinned, so if that's
|
Performing this action will cause all 8GB to be pinned, so if that's
|
||||||
not what you want, then please ignore this step altogether.
|
not what you want, then please ignore this step altogether.
|
||||||
@@ -93,12 +93,12 @@ $ migrate_set_speed 40g # or whatever is the MAX of your RDMA device
|
|||||||
|
|
||||||
Next, on the destination machine, add the following to the QEMU command line:
|
Next, on the destination machine, add the following to the QEMU command line:
|
||||||
|
|
||||||
qemu ..... -incoming x-rdma:host:port
|
qemu ..... -incoming rdma:host:port
|
||||||
|
|
||||||
Finally, perform the actual migration on the source machine:
|
Finally, perform the actual migration on the source machine:
|
||||||
|
|
||||||
QEMU Monitor Command:
|
QEMU Monitor Command:
|
||||||
$ migrate -d x-rdma:host:port
|
$ migrate -d rdma:host:port
|
||||||
|
|
||||||
PERFORMANCE
|
PERFORMANCE
|
||||||
===========
|
===========
|
||||||
@@ -120,8 +120,8 @@ For example, in the same 8GB RAM example with all 8GB of memory in
|
|||||||
active use and the VM itself is completely idle using the same 40 gbps
|
active use and the VM itself is completely idle using the same 40 gbps
|
||||||
infiniband link:
|
infiniband link:
|
||||||
|
|
||||||
1. x-rdma-pin-all disabled total time: approximately 7.5 seconds @ 9.5 Gbps
|
1. rdma-pin-all disabled total time: approximately 7.5 seconds @ 9.5 Gbps
|
||||||
2. x-rdma-pin-all enabled total time: approximately 4 seconds @ 26 Gbps
|
2. rdma-pin-all enabled total time: approximately 4 seconds @ 26 Gbps
|
||||||
|
|
||||||
These numbers would of course scale up to whatever size virtual machine
|
These numbers would of course scale up to whatever size virtual machine
|
||||||
you have to migrate using RDMA.
|
you have to migrate using RDMA.
|
||||||
@@ -407,18 +407,14 @@ socket is broken during a non-RDMA based migration.
|
|||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
=====
|
=====
|
||||||
1. 'migrate x-rdma:host:port' and '-incoming x-rdma' options will be
|
1. Currently, 'ulimit -l' mlock() limits as well as cgroups swap limits
|
||||||
renamed to 'rdma' after the experimental phase of this work has
|
|
||||||
completed upstream.
|
|
||||||
2. Currently, 'ulimit -l' mlock() limits as well as cgroups swap limits
|
|
||||||
are not compatible with infinband memory pinning and will result in
|
are not compatible with infinband memory pinning and will result in
|
||||||
an aborted migration (but with the source VM left unaffected).
|
an aborted migration (but with the source VM left unaffected).
|
||||||
3. Use of the recent /proc/<pid>/pagemap would likely speed up
|
2. Use of the recent /proc/<pid>/pagemap would likely speed up
|
||||||
the use of KSM and ballooning while using RDMA.
|
the use of KSM and ballooning while using RDMA.
|
||||||
4. Also, some form of balloon-device usage tracking would also
|
3. Also, some form of balloon-device usage tracking would also
|
||||||
help alleviate some issues.
|
help alleviate some issues.
|
||||||
5. Move UNREGISTER requests to a separate thread.
|
4. Use LRU to provide more fine-grained direction of UNREGISTER
|
||||||
6. Use LRU to provide more fine-grained direction of UNREGISTER
|
|
||||||
requests for unpinning memory in an overcommitted environment.
|
requests for unpinning memory in an overcommitted environment.
|
||||||
7. Expose UNREGISTER support to the user by way of workload-specific
|
5. Expose UNREGISTER support to the user by way of workload-specific
|
||||||
hints about application behavior.
|
hints about application behavior.
|
||||||
|
|||||||
@@ -214,6 +214,42 @@ The "ust" backend uses the LTTng Userspace Tracer library. There are no
|
|||||||
monitor commands built into QEMU, instead UST utilities should be used to list,
|
monitor commands built into QEMU, instead UST utilities should be used to list,
|
||||||
enable/disable, and dump traces.
|
enable/disable, and dump traces.
|
||||||
|
|
||||||
|
Package lttng-tools is required for userspace tracing. You must ensure that the
|
||||||
|
current user belongs to the "tracing" group, or manually launch the
|
||||||
|
lttng-sessiond daemon for the current user prior to running any instance of
|
||||||
|
QEMU.
|
||||||
|
|
||||||
|
While running an instrumented QEMU, LTTng should be able to list all available
|
||||||
|
events:
|
||||||
|
|
||||||
|
lttng list -u
|
||||||
|
|
||||||
|
Create tracing session:
|
||||||
|
|
||||||
|
lttng create mysession
|
||||||
|
|
||||||
|
Enable events:
|
||||||
|
|
||||||
|
lttng enable-event qemu:g_malloc -u
|
||||||
|
|
||||||
|
Where the events can either be a comma-separated list of events, or "-a" to
|
||||||
|
enable all tracepoint events. Start and stop tracing as needed:
|
||||||
|
|
||||||
|
lttng start
|
||||||
|
lttng stop
|
||||||
|
|
||||||
|
View the trace:
|
||||||
|
|
||||||
|
lttng view
|
||||||
|
|
||||||
|
Destroy tracing session:
|
||||||
|
|
||||||
|
lttng destroy
|
||||||
|
|
||||||
|
Babeltrace can be used at any later time to view the trace:
|
||||||
|
|
||||||
|
babeltrace $HOME/lttng-traces/mysession-<date>-<time>
|
||||||
|
|
||||||
=== SystemTap ===
|
=== SystemTap ===
|
||||||
|
|
||||||
The "dtrace" backend uses DTrace sdt probes but has only been tested with
|
The "dtrace" backend uses DTrace sdt probes but has only been tested with
|
||||||
|
|||||||
222
exec.c
222
exec.c
@@ -17,9 +17,7 @@
|
|||||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifdef _WIN32
|
#ifndef _WIN32
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -138,6 +136,7 @@ typedef struct subpage_t {
|
|||||||
|
|
||||||
static void io_mem_init(void);
|
static void io_mem_init(void);
|
||||||
static void memory_map_init(void);
|
static void memory_map_init(void);
|
||||||
|
static void tcg_commit(MemoryListener *listener);
|
||||||
|
|
||||||
static MemoryRegion io_mem_watch;
|
static MemoryRegion io_mem_watch;
|
||||||
#endif
|
#endif
|
||||||
@@ -325,7 +324,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
|
|||||||
hwaddr *plen, bool resolve_subpage)
|
hwaddr *plen, bool resolve_subpage)
|
||||||
{
|
{
|
||||||
MemoryRegionSection *section;
|
MemoryRegionSection *section;
|
||||||
Int128 diff, diff_page;
|
Int128 diff;
|
||||||
|
|
||||||
section = address_space_lookup_region(d, addr, resolve_subpage);
|
section = address_space_lookup_region(d, addr, resolve_subpage);
|
||||||
/* Compute offset within MemoryRegionSection */
|
/* Compute offset within MemoryRegionSection */
|
||||||
@@ -334,13 +333,23 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
|
|||||||
/* Compute offset within MemoryRegion */
|
/* Compute offset within MemoryRegion */
|
||||||
*xlat = addr + section->offset_within_region;
|
*xlat = addr + section->offset_within_region;
|
||||||
|
|
||||||
diff_page = int128_make64(((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr);
|
|
||||||
diff = int128_sub(section->mr->size, int128_make64(addr));
|
diff = int128_sub(section->mr->size, int128_make64(addr));
|
||||||
diff = int128_min(diff, diff_page);
|
|
||||||
*plen = int128_get64(int128_min(diff, int128_make64(*plen)));
|
*plen = int128_get64(int128_min(diff, int128_make64(*plen)));
|
||||||
return section;
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
|
||||||
|
{
|
||||||
|
if (memory_region_is_ram(mr)) {
|
||||||
|
return !(is_write && mr->readonly);
|
||||||
|
}
|
||||||
|
if (memory_region_is_romd(mr)) {
|
||||||
|
return !is_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
||||||
hwaddr *xlat, hwaddr *plen,
|
hwaddr *xlat, hwaddr *plen,
|
||||||
bool is_write)
|
bool is_write)
|
||||||
@@ -351,7 +360,7 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
|||||||
hwaddr len = *plen;
|
hwaddr len = *plen;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
section = address_space_translate_internal(as->dispatch, addr, &addr, &len, true);
|
section = address_space_translate_internal(as->dispatch, addr, &addr, plen, true);
|
||||||
mr = section->mr;
|
mr = section->mr;
|
||||||
|
|
||||||
if (!mr->iommu_ops) {
|
if (!mr->iommu_ops) {
|
||||||
@@ -370,6 +379,11 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
|||||||
as = iotlb.target_as;
|
as = iotlb.target_as;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (memory_access_is_direct(mr, is_write)) {
|
||||||
|
hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr;
|
||||||
|
len = MIN(page, len);
|
||||||
|
}
|
||||||
|
|
||||||
*plen = len;
|
*plen = len;
|
||||||
*xlat = addr;
|
*xlat = addr;
|
||||||
return mr;
|
return mr;
|
||||||
@@ -438,6 +452,22 @@ CPUState *qemu_get_cpu(int index)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
void tcg_cpu_address_space_init(CPUState *cpu, AddressSpace *as)
|
||||||
|
{
|
||||||
|
/* We only support one address space per cpu at the moment. */
|
||||||
|
assert(cpu->as == as);
|
||||||
|
|
||||||
|
if (cpu->tcg_as_listener) {
|
||||||
|
memory_listener_unregister(cpu->tcg_as_listener);
|
||||||
|
} else {
|
||||||
|
cpu->tcg_as_listener = g_new0(MemoryListener, 1);
|
||||||
|
}
|
||||||
|
cpu->tcg_as_listener->commit = tcg_commit;
|
||||||
|
memory_listener_register(cpu->tcg_as_listener, as);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void cpu_exec_init(CPUArchState *env)
|
void cpu_exec_init(CPUArchState *env)
|
||||||
{
|
{
|
||||||
CPUState *cpu = ENV_GET_CPU(env);
|
CPUState *cpu = ENV_GET_CPU(env);
|
||||||
@@ -457,6 +487,7 @@ void cpu_exec_init(CPUArchState *env)
|
|||||||
QTAILQ_INIT(&env->breakpoints);
|
QTAILQ_INIT(&env->breakpoints);
|
||||||
QTAILQ_INIT(&env->watchpoints);
|
QTAILQ_INIT(&env->watchpoints);
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
cpu->as = &address_space_memory;
|
||||||
cpu->thread_id = qemu_get_thread_id();
|
cpu->thread_id = qemu_get_thread_id();
|
||||||
#endif
|
#endif
|
||||||
QTAILQ_INSERT_TAIL(&cpus, cpu, node);
|
QTAILQ_INSERT_TAIL(&cpus, cpu, node);
|
||||||
@@ -488,7 +519,8 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
|||||||
{
|
{
|
||||||
hwaddr phys = cpu_get_phys_page_debug(cpu, pc);
|
hwaddr phys = cpu_get_phys_page_debug(cpu, pc);
|
||||||
if (phys != -1) {
|
if (phys != -1) {
|
||||||
tb_invalidate_phys_addr(phys | (pc & ~TARGET_PAGE_MASK));
|
tb_invalidate_phys_addr(cpu->as,
|
||||||
|
phys | (pc & ~TARGET_PAGE_MASK));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -780,7 +812,7 @@ hwaddr memory_region_section_get_iotlb(CPUArchState *env,
|
|||||||
iotlb |= PHYS_SECTION_ROM;
|
iotlb |= PHYS_SECTION_ROM;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
iotlb = section - address_space_memory.dispatch->map.sections;
|
iotlb = section - section->address_space->dispatch->map.sections;
|
||||||
iotlb += xlat;
|
iotlb += xlat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,6 +908,7 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti
|
|||||||
|
|
||||||
if (!(existing->mr->subpage)) {
|
if (!(existing->mr->subpage)) {
|
||||||
subpage = subpage_init(d->as, base);
|
subpage = subpage_init(d->as, base);
|
||||||
|
subsection.address_space = d->as;
|
||||||
subsection.mr = &subpage->iomem;
|
subsection.mr = &subpage->iomem;
|
||||||
phys_page_set(d, base >> TARGET_PAGE_BITS, 1,
|
phys_page_set(d, base >> TARGET_PAGE_BITS, 1,
|
||||||
phys_section_add(&d->map, &subsection));
|
phys_section_add(&d->map, &subsection));
|
||||||
@@ -1577,9 +1610,9 @@ static uint64_t watch_mem_read(void *opaque, hwaddr addr,
|
|||||||
{
|
{
|
||||||
check_watchpoint(addr & ~TARGET_PAGE_MASK, ~(size - 1), BP_MEM_READ);
|
check_watchpoint(addr & ~TARGET_PAGE_MASK, ~(size - 1), BP_MEM_READ);
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1: return ldub_phys(addr);
|
case 1: return ldub_phys(&address_space_memory, addr);
|
||||||
case 2: return lduw_phys(addr);
|
case 2: return lduw_phys(&address_space_memory, addr);
|
||||||
case 4: return ldl_phys(addr);
|
case 4: return ldl_phys(&address_space_memory, addr);
|
||||||
default: abort();
|
default: abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1590,13 +1623,13 @@ static void watch_mem_write(void *opaque, hwaddr addr,
|
|||||||
check_watchpoint(addr & ~TARGET_PAGE_MASK, ~(size - 1), BP_MEM_WRITE);
|
check_watchpoint(addr & ~TARGET_PAGE_MASK, ~(size - 1), BP_MEM_WRITE);
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
stb_phys(addr, val);
|
stb_phys(&address_space_memory, addr, val);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
stw_phys(addr, val);
|
stw_phys(&address_space_memory, addr, val);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
stl_phys(addr, val);
|
stl_phys(&address_space_memory, addr, val);
|
||||||
break;
|
break;
|
||||||
default: abort();
|
default: abort();
|
||||||
}
|
}
|
||||||
@@ -1721,6 +1754,7 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base)
|
|||||||
static uint16_t dummy_section(PhysPageMap *map, MemoryRegion *mr)
|
static uint16_t dummy_section(PhysPageMap *map, MemoryRegion *mr)
|
||||||
{
|
{
|
||||||
MemoryRegionSection section = {
|
MemoryRegionSection section = {
|
||||||
|
.address_space = &address_space_memory,
|
||||||
.mr = mr,
|
.mr = mr,
|
||||||
.offset_within_address_space = 0,
|
.offset_within_address_space = 0,
|
||||||
.offset_within_region = 0,
|
.offset_within_region = 0,
|
||||||
@@ -1730,10 +1764,9 @@ static uint16_t dummy_section(PhysPageMap *map, MemoryRegion *mr)
|
|||||||
return phys_section_add(map, §ion);
|
return phys_section_add(map, §ion);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryRegion *iotlb_to_region(hwaddr index)
|
MemoryRegion *iotlb_to_region(AddressSpace *as, hwaddr index)
|
||||||
{
|
{
|
||||||
return address_space_memory.dispatch->map.sections[
|
return as->dispatch->map.sections[index & ~TARGET_PAGE_MASK].mr;
|
||||||
index & ~TARGET_PAGE_MASK].mr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void io_mem_init(void)
|
static void io_mem_init(void)
|
||||||
@@ -1793,6 +1826,11 @@ static void tcg_commit(MemoryListener *listener)
|
|||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
CPUArchState *env = cpu->env_ptr;
|
CPUArchState *env = cpu->env_ptr;
|
||||||
|
|
||||||
|
/* FIXME: Disentangle the cpu.h circular files deps so we can
|
||||||
|
directly get the right CPU from listener. */
|
||||||
|
if (cpu->tcg_as_listener != listener) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
tlb_flush(env, 1);
|
tlb_flush(env, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1813,10 +1851,6 @@ static MemoryListener core_memory_listener = {
|
|||||||
.priority = 1,
|
.priority = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static MemoryListener tcg_memory_listener = {
|
|
||||||
.commit = tcg_commit,
|
|
||||||
};
|
|
||||||
|
|
||||||
void address_space_init_dispatch(AddressSpace *as)
|
void address_space_init_dispatch(AddressSpace *as)
|
||||||
{
|
{
|
||||||
as->dispatch = NULL;
|
as->dispatch = NULL;
|
||||||
@@ -1852,9 +1886,6 @@ static void memory_map_init(void)
|
|||||||
address_space_init(&address_space_io, system_io, "I/O");
|
address_space_init(&address_space_io, system_io, "I/O");
|
||||||
|
|
||||||
memory_listener_register(&core_memory_listener, &address_space_memory);
|
memory_listener_register(&core_memory_listener, &address_space_memory);
|
||||||
if (tcg_enabled()) {
|
|
||||||
memory_listener_register(&tcg_memory_listener, &address_space_memory);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryRegion *get_system_memory(void)
|
MemoryRegion *get_system_memory(void)
|
||||||
@@ -1925,18 +1956,6 @@ static void invalidate_and_set_dirty(hwaddr addr,
|
|||||||
xen_modified_memory(addr, length);
|
xen_modified_memory(addr, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
|
|
||||||
{
|
|
||||||
if (memory_region_is_ram(mr)) {
|
|
||||||
return !(is_write && mr->readonly);
|
|
||||||
}
|
|
||||||
if (memory_region_is_romd(mr)) {
|
|
||||||
return !is_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr)
|
static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr)
|
||||||
{
|
{
|
||||||
unsigned access_size_max = mr->ops->valid.max_access_size;
|
unsigned access_size_max = mr->ops->valid.max_access_size;
|
||||||
@@ -2081,7 +2100,7 @@ enum write_rom_type {
|
|||||||
FLUSH_CACHE,
|
FLUSH_CACHE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void cpu_physical_memory_write_rom_internal(
|
static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
|
||||||
hwaddr addr, const uint8_t *buf, int len, enum write_rom_type type)
|
hwaddr addr, const uint8_t *buf, int len, enum write_rom_type type)
|
||||||
{
|
{
|
||||||
hwaddr l;
|
hwaddr l;
|
||||||
@@ -2091,8 +2110,7 @@ static inline void cpu_physical_memory_write_rom_internal(
|
|||||||
|
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
l = len;
|
l = len;
|
||||||
mr = address_space_translate(&address_space_memory,
|
mr = address_space_translate(as, addr, &addr1, &l, true);
|
||||||
addr, &addr1, &l, true);
|
|
||||||
|
|
||||||
if (!(memory_region_is_ram(mr) ||
|
if (!(memory_region_is_ram(mr) ||
|
||||||
memory_region_is_romd(mr))) {
|
memory_region_is_romd(mr))) {
|
||||||
@@ -2118,10 +2136,10 @@ static inline void cpu_physical_memory_write_rom_internal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* used for ROM loading : can write in RAM and ROM */
|
/* used for ROM loading : can write in RAM and ROM */
|
||||||
void cpu_physical_memory_write_rom(hwaddr addr,
|
void cpu_physical_memory_write_rom(AddressSpace *as, hwaddr addr,
|
||||||
const uint8_t *buf, int len)
|
const uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
cpu_physical_memory_write_rom_internal(addr, buf, len, WRITE_DATA);
|
cpu_physical_memory_write_rom_internal(as, addr, buf, len, WRITE_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpu_flush_icache_range(hwaddr start, int len)
|
void cpu_flush_icache_range(hwaddr start, int len)
|
||||||
@@ -2136,7 +2154,8 @@ void cpu_flush_icache_range(hwaddr start, int len)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_physical_memory_write_rom_internal(start, NULL, len, FLUSH_CACHE);
|
cpu_physical_memory_write_rom_internal(&address_space_memory,
|
||||||
|
start, NULL, len, FLUSH_CACHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -2327,7 +2346,7 @@ void cpu_physical_memory_unmap(void *buffer, hwaddr len,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* warning: addr must be aligned */
|
/* warning: addr must be aligned */
|
||||||
static inline uint32_t ldl_phys_internal(hwaddr addr,
|
static inline uint32_t ldl_phys_internal(AddressSpace *as, hwaddr addr,
|
||||||
enum device_endian endian)
|
enum device_endian endian)
|
||||||
{
|
{
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
@@ -2336,8 +2355,7 @@ static inline uint32_t ldl_phys_internal(hwaddr addr,
|
|||||||
hwaddr l = 4;
|
hwaddr l = 4;
|
||||||
hwaddr addr1;
|
hwaddr addr1;
|
||||||
|
|
||||||
mr = address_space_translate(&address_space_memory, addr, &addr1, &l,
|
mr = address_space_translate(as, addr, &addr1, &l, false);
|
||||||
false);
|
|
||||||
if (l < 4 || !memory_access_is_direct(mr, false)) {
|
if (l < 4 || !memory_access_is_direct(mr, false)) {
|
||||||
/* I/O case */
|
/* I/O case */
|
||||||
io_mem_read(mr, addr1, &val, 4);
|
io_mem_read(mr, addr1, &val, 4);
|
||||||
@@ -2370,23 +2388,23 @@ static inline uint32_t ldl_phys_internal(hwaddr addr,
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ldl_phys(hwaddr addr)
|
uint32_t ldl_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return ldl_phys_internal(addr, DEVICE_NATIVE_ENDIAN);
|
return ldl_phys_internal(as, addr, DEVICE_NATIVE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ldl_le_phys(hwaddr addr)
|
uint32_t ldl_le_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return ldl_phys_internal(addr, DEVICE_LITTLE_ENDIAN);
|
return ldl_phys_internal(as, addr, DEVICE_LITTLE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ldl_be_phys(hwaddr addr)
|
uint32_t ldl_be_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return ldl_phys_internal(addr, DEVICE_BIG_ENDIAN);
|
return ldl_phys_internal(as, addr, DEVICE_BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* warning: addr must be aligned */
|
/* warning: addr must be aligned */
|
||||||
static inline uint64_t ldq_phys_internal(hwaddr addr,
|
static inline uint64_t ldq_phys_internal(AddressSpace *as, hwaddr addr,
|
||||||
enum device_endian endian)
|
enum device_endian endian)
|
||||||
{
|
{
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
@@ -2395,7 +2413,7 @@ static inline uint64_t ldq_phys_internal(hwaddr addr,
|
|||||||
hwaddr l = 8;
|
hwaddr l = 8;
|
||||||
hwaddr addr1;
|
hwaddr addr1;
|
||||||
|
|
||||||
mr = address_space_translate(&address_space_memory, addr, &addr1, &l,
|
mr = address_space_translate(as, addr, &addr1, &l,
|
||||||
false);
|
false);
|
||||||
if (l < 8 || !memory_access_is_direct(mr, false)) {
|
if (l < 8 || !memory_access_is_direct(mr, false)) {
|
||||||
/* I/O case */
|
/* I/O case */
|
||||||
@@ -2429,31 +2447,31 @@ static inline uint64_t ldq_phys_internal(hwaddr addr,
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ldq_phys(hwaddr addr)
|
uint64_t ldq_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return ldq_phys_internal(addr, DEVICE_NATIVE_ENDIAN);
|
return ldq_phys_internal(as, addr, DEVICE_NATIVE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ldq_le_phys(hwaddr addr)
|
uint64_t ldq_le_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return ldq_phys_internal(addr, DEVICE_LITTLE_ENDIAN);
|
return ldq_phys_internal(as, addr, DEVICE_LITTLE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ldq_be_phys(hwaddr addr)
|
uint64_t ldq_be_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return ldq_phys_internal(addr, DEVICE_BIG_ENDIAN);
|
return ldq_phys_internal(as, addr, DEVICE_BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: optimize */
|
/* XXX: optimize */
|
||||||
uint32_t ldub_phys(hwaddr addr)
|
uint32_t ldub_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
uint8_t val;
|
uint8_t val;
|
||||||
cpu_physical_memory_read(addr, &val, 1);
|
address_space_rw(as, addr, &val, 1, 0);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* warning: addr must be aligned */
|
/* warning: addr must be aligned */
|
||||||
static inline uint32_t lduw_phys_internal(hwaddr addr,
|
static inline uint32_t lduw_phys_internal(AddressSpace *as, hwaddr addr,
|
||||||
enum device_endian endian)
|
enum device_endian endian)
|
||||||
{
|
{
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
@@ -2462,7 +2480,7 @@ static inline uint32_t lduw_phys_internal(hwaddr addr,
|
|||||||
hwaddr l = 2;
|
hwaddr l = 2;
|
||||||
hwaddr addr1;
|
hwaddr addr1;
|
||||||
|
|
||||||
mr = address_space_translate(&address_space_memory, addr, &addr1, &l,
|
mr = address_space_translate(as, addr, &addr1, &l,
|
||||||
false);
|
false);
|
||||||
if (l < 2 || !memory_access_is_direct(mr, false)) {
|
if (l < 2 || !memory_access_is_direct(mr, false)) {
|
||||||
/* I/O case */
|
/* I/O case */
|
||||||
@@ -2496,32 +2514,32 @@ static inline uint32_t lduw_phys_internal(hwaddr addr,
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t lduw_phys(hwaddr addr)
|
uint32_t lduw_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return lduw_phys_internal(addr, DEVICE_NATIVE_ENDIAN);
|
return lduw_phys_internal(as, addr, DEVICE_NATIVE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t lduw_le_phys(hwaddr addr)
|
uint32_t lduw_le_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return lduw_phys_internal(addr, DEVICE_LITTLE_ENDIAN);
|
return lduw_phys_internal(as, addr, DEVICE_LITTLE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t lduw_be_phys(hwaddr addr)
|
uint32_t lduw_be_phys(AddressSpace *as, hwaddr addr)
|
||||||
{
|
{
|
||||||
return lduw_phys_internal(addr, DEVICE_BIG_ENDIAN);
|
return lduw_phys_internal(as, addr, DEVICE_BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* warning: addr must be aligned. The ram page is not masked as dirty
|
/* warning: addr must be aligned. The ram page is not masked as dirty
|
||||||
and the code inside is not invalidated. It is useful if the dirty
|
and the code inside is not invalidated. It is useful if the dirty
|
||||||
bits are used to track modified PTEs */
|
bits are used to track modified PTEs */
|
||||||
void stl_phys_notdirty(hwaddr addr, uint32_t val)
|
void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val)
|
||||||
{
|
{
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
MemoryRegion *mr;
|
MemoryRegion *mr;
|
||||||
hwaddr l = 4;
|
hwaddr l = 4;
|
||||||
hwaddr addr1;
|
hwaddr addr1;
|
||||||
|
|
||||||
mr = address_space_translate(&address_space_memory, addr, &addr1, &l,
|
mr = address_space_translate(as, addr, &addr1, &l,
|
||||||
true);
|
true);
|
||||||
if (l < 4 || !memory_access_is_direct(mr, true)) {
|
if (l < 4 || !memory_access_is_direct(mr, true)) {
|
||||||
io_mem_write(mr, addr1, val, 4);
|
io_mem_write(mr, addr1, val, 4);
|
||||||
@@ -2544,7 +2562,8 @@ void stl_phys_notdirty(hwaddr addr, uint32_t val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* warning: addr must be aligned */
|
/* warning: addr must be aligned */
|
||||||
static inline void stl_phys_internal(hwaddr addr, uint32_t val,
|
static inline void stl_phys_internal(AddressSpace *as,
|
||||||
|
hwaddr addr, uint32_t val,
|
||||||
enum device_endian endian)
|
enum device_endian endian)
|
||||||
{
|
{
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
@@ -2552,7 +2571,7 @@ static inline void stl_phys_internal(hwaddr addr, uint32_t val,
|
|||||||
hwaddr l = 4;
|
hwaddr l = 4;
|
||||||
hwaddr addr1;
|
hwaddr addr1;
|
||||||
|
|
||||||
mr = address_space_translate(&address_space_memory, addr, &addr1, &l,
|
mr = address_space_translate(as, addr, &addr1, &l,
|
||||||
true);
|
true);
|
||||||
if (l < 4 || !memory_access_is_direct(mr, true)) {
|
if (l < 4 || !memory_access_is_direct(mr, true)) {
|
||||||
#if defined(TARGET_WORDS_BIGENDIAN)
|
#if defined(TARGET_WORDS_BIGENDIAN)
|
||||||
@@ -2584,30 +2603,31 @@ static inline void stl_phys_internal(hwaddr addr, uint32_t val,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stl_phys(hwaddr addr, uint32_t val)
|
void stl_phys(AddressSpace *as, hwaddr addr, uint32_t val)
|
||||||
{
|
{
|
||||||
stl_phys_internal(addr, val, DEVICE_NATIVE_ENDIAN);
|
stl_phys_internal(as, addr, val, DEVICE_NATIVE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stl_le_phys(hwaddr addr, uint32_t val)
|
void stl_le_phys(AddressSpace *as, hwaddr addr, uint32_t val)
|
||||||
{
|
{
|
||||||
stl_phys_internal(addr, val, DEVICE_LITTLE_ENDIAN);
|
stl_phys_internal(as, addr, val, DEVICE_LITTLE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stl_be_phys(hwaddr addr, uint32_t val)
|
void stl_be_phys(AddressSpace *as, hwaddr addr, uint32_t val)
|
||||||
{
|
{
|
||||||
stl_phys_internal(addr, val, DEVICE_BIG_ENDIAN);
|
stl_phys_internal(as, addr, val, DEVICE_BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: optimize */
|
/* XXX: optimize */
|
||||||
void stb_phys(hwaddr addr, uint32_t val)
|
void stb_phys(AddressSpace *as, hwaddr addr, uint32_t val)
|
||||||
{
|
{
|
||||||
uint8_t v = val;
|
uint8_t v = val;
|
||||||
cpu_physical_memory_write(addr, &v, 1);
|
address_space_rw(as, addr, &v, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* warning: addr must be aligned */
|
/* warning: addr must be aligned */
|
||||||
static inline void stw_phys_internal(hwaddr addr, uint32_t val,
|
static inline void stw_phys_internal(AddressSpace *as,
|
||||||
|
hwaddr addr, uint32_t val,
|
||||||
enum device_endian endian)
|
enum device_endian endian)
|
||||||
{
|
{
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
@@ -2615,8 +2635,7 @@ static inline void stw_phys_internal(hwaddr addr, uint32_t val,
|
|||||||
hwaddr l = 2;
|
hwaddr l = 2;
|
||||||
hwaddr addr1;
|
hwaddr addr1;
|
||||||
|
|
||||||
mr = address_space_translate(&address_space_memory, addr, &addr1, &l,
|
mr = address_space_translate(as, addr, &addr1, &l, true);
|
||||||
true);
|
|
||||||
if (l < 2 || !memory_access_is_direct(mr, true)) {
|
if (l < 2 || !memory_access_is_direct(mr, true)) {
|
||||||
#if defined(TARGET_WORDS_BIGENDIAN)
|
#if defined(TARGET_WORDS_BIGENDIAN)
|
||||||
if (endian == DEVICE_LITTLE_ENDIAN) {
|
if (endian == DEVICE_LITTLE_ENDIAN) {
|
||||||
@@ -2647,38 +2666,38 @@ static inline void stw_phys_internal(hwaddr addr, uint32_t val,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stw_phys(hwaddr addr, uint32_t val)
|
void stw_phys(AddressSpace *as, hwaddr addr, uint32_t val)
|
||||||
{
|
{
|
||||||
stw_phys_internal(addr, val, DEVICE_NATIVE_ENDIAN);
|
stw_phys_internal(as, addr, val, DEVICE_NATIVE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stw_le_phys(hwaddr addr, uint32_t val)
|
void stw_le_phys(AddressSpace *as, hwaddr addr, uint32_t val)
|
||||||
{
|
{
|
||||||
stw_phys_internal(addr, val, DEVICE_LITTLE_ENDIAN);
|
stw_phys_internal(as, addr, val, DEVICE_LITTLE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stw_be_phys(hwaddr addr, uint32_t val)
|
void stw_be_phys(AddressSpace *as, hwaddr addr, uint32_t val)
|
||||||
{
|
{
|
||||||
stw_phys_internal(addr, val, DEVICE_BIG_ENDIAN);
|
stw_phys_internal(as, addr, val, DEVICE_BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: optimize */
|
/* XXX: optimize */
|
||||||
void stq_phys(hwaddr addr, uint64_t val)
|
void stq_phys(AddressSpace *as, hwaddr addr, uint64_t val)
|
||||||
{
|
{
|
||||||
val = tswap64(val);
|
val = tswap64(val);
|
||||||
cpu_physical_memory_write(addr, &val, 8);
|
address_space_rw(as, addr, (void *) &val, 8, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stq_le_phys(hwaddr addr, uint64_t val)
|
void stq_le_phys(AddressSpace *as, hwaddr addr, uint64_t val)
|
||||||
{
|
{
|
||||||
val = cpu_to_le64(val);
|
val = cpu_to_le64(val);
|
||||||
cpu_physical_memory_write(addr, &val, 8);
|
address_space_rw(as, addr, (void *) &val, 8, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stq_be_phys(hwaddr addr, uint64_t val)
|
void stq_be_phys(AddressSpace *as, hwaddr addr, uint64_t val)
|
||||||
{
|
{
|
||||||
val = cpu_to_be64(val);
|
val = cpu_to_be64(val);
|
||||||
cpu_physical_memory_write(addr, &val, 8);
|
address_space_rw(as, addr, (void *) &val, 8, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* virtual memory access for debug (includes writing to ROM) */
|
/* virtual memory access for debug (includes writing to ROM) */
|
||||||
@@ -2699,10 +2718,11 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
|||||||
if (l > len)
|
if (l > len)
|
||||||
l = len;
|
l = len;
|
||||||
phys_addr += (addr & ~TARGET_PAGE_MASK);
|
phys_addr += (addr & ~TARGET_PAGE_MASK);
|
||||||
if (is_write)
|
if (is_write) {
|
||||||
cpu_physical_memory_write_rom(phys_addr, buf, l);
|
cpu_physical_memory_write_rom(cpu->as, phys_addr, buf, l);
|
||||||
else
|
} else {
|
||||||
cpu_physical_memory_rw(phys_addr, buf, l, is_write);
|
address_space_rw(cpu->as, phys_addr, buf, l, 0);
|
||||||
|
}
|
||||||
len -= l;
|
len -= l;
|
||||||
buf += l;
|
buf += l;
|
||||||
addr += l;
|
addr += l;
|
||||||
|
|||||||
@@ -2372,6 +2372,17 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Zero plus something non-zero : just return the something */
|
/* Zero plus something non-zero : just return the something */
|
||||||
|
if (flags & float_muladd_halve_result) {
|
||||||
|
if (cExp == 0) {
|
||||||
|
normalizeFloat32Subnormal(cSig, &cExp, &cSig);
|
||||||
|
}
|
||||||
|
/* Subtract one to halve, and one again because roundAndPackFloat32
|
||||||
|
* wants one less than the true exponent.
|
||||||
|
*/
|
||||||
|
cExp -= 2;
|
||||||
|
cSig = (cSig | 0x00800000) << 7;
|
||||||
|
return roundAndPackFloat32(cSign ^ signflip, cExp, cSig STATUS_VAR);
|
||||||
|
}
|
||||||
return packFloat32(cSign ^ signflip, cExp, cSig);
|
return packFloat32(cSign ^ signflip, cExp, cSig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2408,6 +2419,9 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM)
|
|||||||
/* Throw out the special case of c being an exact zero now */
|
/* Throw out the special case of c being an exact zero now */
|
||||||
shift64RightJamming(pSig64, 32, &pSig64);
|
shift64RightJamming(pSig64, 32, &pSig64);
|
||||||
pSig = pSig64;
|
pSig = pSig64;
|
||||||
|
if (flags & float_muladd_halve_result) {
|
||||||
|
pExp--;
|
||||||
|
}
|
||||||
return roundAndPackFloat32(zSign, pExp - 1,
|
return roundAndPackFloat32(zSign, pExp - 1,
|
||||||
pSig STATUS_VAR);
|
pSig STATUS_VAR);
|
||||||
}
|
}
|
||||||
@@ -2472,6 +2486,10 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM)
|
|||||||
zSig64 <<= shiftcount;
|
zSig64 <<= shiftcount;
|
||||||
zExp -= shiftcount;
|
zExp -= shiftcount;
|
||||||
}
|
}
|
||||||
|
if (flags & float_muladd_halve_result) {
|
||||||
|
zExp--;
|
||||||
|
}
|
||||||
|
|
||||||
shift64RightJamming(zSig64, 32, &zSig64);
|
shift64RightJamming(zSig64, 32, &zSig64);
|
||||||
return roundAndPackFloat32(zSign, zExp, zSig64 STATUS_VAR);
|
return roundAndPackFloat32(zSign, zExp, zSig64 STATUS_VAR);
|
||||||
}
|
}
|
||||||
@@ -4088,6 +4106,17 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Zero plus something non-zero : just return the something */
|
/* Zero plus something non-zero : just return the something */
|
||||||
|
if (flags & float_muladd_halve_result) {
|
||||||
|
if (cExp == 0) {
|
||||||
|
normalizeFloat64Subnormal(cSig, &cExp, &cSig);
|
||||||
|
}
|
||||||
|
/* Subtract one to halve, and one again because roundAndPackFloat64
|
||||||
|
* wants one less than the true exponent.
|
||||||
|
*/
|
||||||
|
cExp -= 2;
|
||||||
|
cSig = (cSig | 0x0010000000000000ULL) << 10;
|
||||||
|
return roundAndPackFloat64(cSign ^ signflip, cExp, cSig STATUS_VAR);
|
||||||
|
}
|
||||||
return packFloat64(cSign ^ signflip, cExp, cSig);
|
return packFloat64(cSign ^ signflip, cExp, cSig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4123,6 +4152,9 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM)
|
|||||||
if (!cSig) {
|
if (!cSig) {
|
||||||
/* Throw out the special case of c being an exact zero now */
|
/* Throw out the special case of c being an exact zero now */
|
||||||
shift128RightJamming(pSig0, pSig1, 64, &pSig0, &pSig1);
|
shift128RightJamming(pSig0, pSig1, 64, &pSig0, &pSig1);
|
||||||
|
if (flags & float_muladd_halve_result) {
|
||||||
|
pExp--;
|
||||||
|
}
|
||||||
return roundAndPackFloat64(zSign, pExp - 1,
|
return roundAndPackFloat64(zSign, pExp - 1,
|
||||||
pSig1 STATUS_VAR);
|
pSig1 STATUS_VAR);
|
||||||
}
|
}
|
||||||
@@ -4159,6 +4191,9 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM)
|
|||||||
zExp--;
|
zExp--;
|
||||||
}
|
}
|
||||||
shift128RightJamming(zSig0, zSig1, 64, &zSig0, &zSig1);
|
shift128RightJamming(zSig0, zSig1, 64, &zSig0, &zSig1);
|
||||||
|
if (flags & float_muladd_halve_result) {
|
||||||
|
zExp--;
|
||||||
|
}
|
||||||
return roundAndPackFloat64(zSign, zExp, zSig1 STATUS_VAR);
|
return roundAndPackFloat64(zSign, zExp, zSig1 STATUS_VAR);
|
||||||
} else {
|
} else {
|
||||||
/* Subtraction */
|
/* Subtraction */
|
||||||
@@ -4209,6 +4244,9 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM)
|
|||||||
zExp -= (shiftcount + 64);
|
zExp -= (shiftcount + 64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (flags & float_muladd_halve_result) {
|
||||||
|
zExp--;
|
||||||
|
}
|
||||||
return roundAndPackFloat64(zSign, zExp, zSig0 STATUS_VAR);
|
return roundAndPackFloat64(zSign, zExp, zSig0 STATUS_VAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
hmp.c
16
hmp.c
@@ -881,7 +881,7 @@ void hmp_balloon(Monitor *mon, const QDict *qdict)
|
|||||||
Error *errp = NULL;
|
Error *errp = NULL;
|
||||||
|
|
||||||
qmp_balloon(value, &errp);
|
qmp_balloon(value, &errp);
|
||||||
if (error_is_set(&errp)) {
|
if (errp) {
|
||||||
monitor_printf(mon, "balloon: %s\n", error_get_pretty(errp));
|
monitor_printf(mon, "balloon: %s\n", error_get_pretty(errp));
|
||||||
error_free(errp);
|
error_free(errp);
|
||||||
}
|
}
|
||||||
@@ -1118,7 +1118,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qmp_change(device, target, !!arg, arg, &err);
|
qmp_change(device, target, !!arg, arg, &err);
|
||||||
if (error_is_set(&err) &&
|
if (err &&
|
||||||
error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) {
|
error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) {
|
||||||
error_free(err);
|
error_free(err);
|
||||||
monitor_read_block_device_key(mon, device, NULL, NULL);
|
monitor_read_block_device_key(mon, device, NULL, NULL);
|
||||||
@@ -1234,7 +1234,8 @@ static void hmp_migrate_status_cb(void *opaque)
|
|||||||
MigrationInfo *info;
|
MigrationInfo *info;
|
||||||
|
|
||||||
info = qmp_query_migrate(NULL);
|
info = qmp_query_migrate(NULL);
|
||||||
if (!info->has_status || strcmp(info->status, "active") == 0) {
|
if (!info->has_status || strcmp(info->status, "active") == 0 ||
|
||||||
|
strcmp(info->status, "setup") == 0) {
|
||||||
if (info->has_disk) {
|
if (info->has_disk) {
|
||||||
int progress;
|
int progress;
|
||||||
|
|
||||||
@@ -1310,8 +1311,11 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
|||||||
const char *file = qdict_get_str(qdict, "filename");
|
const char *file = qdict_get_str(qdict, "filename");
|
||||||
bool has_begin = qdict_haskey(qdict, "begin");
|
bool has_begin = qdict_haskey(qdict, "begin");
|
||||||
bool has_length = qdict_haskey(qdict, "length");
|
bool has_length = qdict_haskey(qdict, "length");
|
||||||
|
/* kdump-compressed format is not supported for HMP */
|
||||||
|
bool has_format = false;
|
||||||
int64_t begin = 0;
|
int64_t begin = 0;
|
||||||
int64_t length = 0;
|
int64_t length = 0;
|
||||||
|
enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
|
||||||
char *prot;
|
char *prot;
|
||||||
|
|
||||||
if (has_begin) {
|
if (has_begin) {
|
||||||
@@ -1324,7 +1328,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
|||||||
prot = g_strconcat("file:", file, NULL);
|
prot = g_strconcat("file:", file, NULL);
|
||||||
|
|
||||||
qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
|
qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
|
||||||
&errp);
|
has_format, dump_format, &errp);
|
||||||
hmp_handle_error(mon, &errp);
|
hmp_handle_error(mon, &errp);
|
||||||
g_free(prot);
|
g_free(prot);
|
||||||
}
|
}
|
||||||
@@ -1335,12 +1339,12 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict)
|
|||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
|
|
||||||
opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err);
|
opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err);
|
||||||
if (error_is_set(&err)) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
netdev_add(opts, &err);
|
netdev_add(opts, &err);
|
||||||
if (error_is_set(&err)) {
|
if (err) {
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,6 @@ int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
|
|||||||
});
|
});
|
||||||
v9fs_path_unlock(s);
|
v9fs_path_unlock(s);
|
||||||
}
|
}
|
||||||
/* The ioctl may not be supported depending on the path */
|
|
||||||
if (err == -ENOTTY) {
|
|
||||||
err = 0;
|
|
||||||
}
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -582,6 +582,7 @@ static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
|
|||||||
static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
||||||
mode_t st_mode, uint64_t *st_gen)
|
mode_t st_mode, uint64_t *st_gen)
|
||||||
{
|
{
|
||||||
|
#ifdef FS_IOC_GETVERSION
|
||||||
int err;
|
int err;
|
||||||
V9fsFidOpenState fid_open;
|
V9fsFidOpenState fid_open;
|
||||||
|
|
||||||
@@ -590,7 +591,8 @@ static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
|||||||
* We can get fd for regular files and directories only
|
* We can get fd for regular files and directories only
|
||||||
*/
|
*/
|
||||||
if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
|
if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
|
||||||
return 0;
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
err = handle_open(ctx, path, O_RDONLY, &fid_open);
|
err = handle_open(ctx, path, O_RDONLY, &fid_open);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
@@ -599,6 +601,10 @@ static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
|||||||
err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
|
err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
|
||||||
handle_close(ctx, &fid_open);
|
handle_close(ctx, &fid_open);
|
||||||
return err;
|
return err;
|
||||||
|
#else
|
||||||
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_init(FsContext *ctx)
|
static int handle_init(FsContext *ctx)
|
||||||
|
|||||||
@@ -1068,8 +1068,8 @@ err_out:
|
|||||||
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
||||||
mode_t st_mode, uint64_t *st_gen)
|
mode_t st_mode, uint64_t *st_gen)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
#ifdef FS_IOC_GETVERSION
|
#ifdef FS_IOC_GETVERSION
|
||||||
|
int err;
|
||||||
V9fsFidOpenState fid_open;
|
V9fsFidOpenState fid_open;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1077,7 +1077,8 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
|||||||
* We can get fd for regular files and directories only
|
* We can get fd for regular files and directories only
|
||||||
*/
|
*/
|
||||||
if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
|
if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
|
||||||
return 0;
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
err = local_open(ctx, path, O_RDONLY, &fid_open);
|
err = local_open(ctx, path, O_RDONLY, &fid_open);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
@@ -1085,10 +1086,11 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
|||||||
}
|
}
|
||||||
err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
|
err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
|
||||||
local_close(ctx, &fid_open);
|
local_close(ctx, &fid_open);
|
||||||
#else
|
|
||||||
err = -ENOTTY;
|
|
||||||
#endif
|
|
||||||
return err;
|
return err;
|
||||||
|
#else
|
||||||
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int local_init(FsContext *ctx)
|
static int local_init(FsContext *ctx)
|
||||||
|
|||||||
@@ -1086,7 +1086,8 @@ static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path,
|
|||||||
* we can get fd for regular files and directories only
|
* we can get fd for regular files and directories only
|
||||||
*/
|
*/
|
||||||
if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
|
if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
|
||||||
return 0;
|
errno = ENOTTY;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, "s", path);
|
err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, "s", path);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|||||||
@@ -1080,10 +1080,18 @@ static void v9fs_getattr(void *opaque)
|
|||||||
/* fill st_gen if requested and supported by underlying fs */
|
/* fill st_gen if requested and supported by underlying fs */
|
||||||
if (request_mask & P9_STATS_GEN) {
|
if (request_mask & P9_STATS_GEN) {
|
||||||
retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl);
|
retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl);
|
||||||
if (retval < 0) {
|
switch (retval) {
|
||||||
|
case 0:
|
||||||
|
/* we have valid st_gen: update result mask */
|
||||||
|
v9stat_dotl.st_result_mask |= P9_STATS_GEN;
|
||||||
|
break;
|
||||||
|
case -EINTR:
|
||||||
|
/* request cancelled, e.g. by Tflush */
|
||||||
goto out;
|
goto out;
|
||||||
|
default:
|
||||||
|
/* failed to get st_gen: not fatal, ignore */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
v9stat_dotl.st_result_mask |= P9_STATS_GEN;
|
|
||||||
}
|
}
|
||||||
retval = pdu_marshal(pdu, offset, "A", &v9stat_dotl);
|
retval = pdu_marshal(pdu, offset, "A", &v9stat_dotl);
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += i2c/
|
|||||||
devices-dirs-$(CONFIG_SOFTMMU) += ide/
|
devices-dirs-$(CONFIG_SOFTMMU) += ide/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += input/
|
devices-dirs-$(CONFIG_SOFTMMU) += input/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += intc/
|
devices-dirs-$(CONFIG_SOFTMMU) += intc/
|
||||||
|
devices-dirs-$(CONFIG_IPACK) += ipack/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += isa/
|
devices-dirs-$(CONFIG_SOFTMMU) += isa/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += misc/
|
devices-dirs-$(CONFIG_SOFTMMU) += misc/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += net/
|
devices-dirs-$(CONFIG_SOFTMMU) += net/
|
||||||
|
|||||||
127
hw/acpi/pcihp.c
127
hw/acpi/pcihp.c
@@ -46,13 +46,15 @@
|
|||||||
# define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0)
|
# define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PCI_HOTPLUG_ADDR 0xae00
|
#define ACPI_PCI_HOTPLUG_STATUS 2
|
||||||
#define PCI_HOTPLUG_SIZE 0x0014
|
#define ACPI_PCIHP_ADDR 0xae00
|
||||||
#define PCI_UP_BASE 0xae00
|
#define ACPI_PCIHP_SIZE 0x0014
|
||||||
#define PCI_DOWN_BASE 0xae04
|
#define ACPI_PCIHP_LEGACY_SIZE 0x000f
|
||||||
#define PCI_EJ_BASE 0xae08
|
#define PCI_UP_BASE 0x0000
|
||||||
#define PCI_RMV_BASE 0xae0c
|
#define PCI_DOWN_BASE 0x0004
|
||||||
#define PCI_SEL_BASE 0xae10
|
#define PCI_EJ_BASE 0x0008
|
||||||
|
#define PCI_RMV_BASE 0x000c
|
||||||
|
#define PCI_SEL_BASE 0x0010
|
||||||
|
|
||||||
typedef struct AcpiPciHpFind {
|
typedef struct AcpiPciHpFind {
|
||||||
int bsel;
|
int bsel;
|
||||||
@@ -104,19 +106,19 @@ static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel)
|
|||||||
static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev)
|
static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev)
|
||||||
{
|
{
|
||||||
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
|
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
|
||||||
|
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
||||||
/*
|
/*
|
||||||
* ACPI doesn't allow hotplug of bridge devices. Don't allow
|
* ACPI doesn't allow hotplug of bridge devices. Don't allow
|
||||||
* hot-unplug of bridge devices unless they were added by hotplug
|
* hot-unplug of bridge devices unless they were added by hotplug
|
||||||
* (and so, not described by acpi).
|
* (and so, not described by acpi).
|
||||||
*/
|
*/
|
||||||
return (pc->is_bridge && !dev->qdev.hotplugged) || pc->no_hotplug;
|
return (pc->is_bridge && !dev->qdev.hotplugged) || !dc->hotpluggable;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
|
static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
|
||||||
{
|
{
|
||||||
BusChild *kid, *next;
|
BusChild *kid, *next;
|
||||||
int slot = ffs(slots) - 1;
|
int slot = ffs(slots) - 1;
|
||||||
bool slot_free = true;
|
|
||||||
PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
|
PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
|
||||||
|
|
||||||
if (!bus) {
|
if (!bus) {
|
||||||
@@ -125,21 +127,17 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo
|
|||||||
|
|
||||||
/* Mark request as complete */
|
/* Mark request as complete */
|
||||||
s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot);
|
s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot);
|
||||||
|
s->acpi_pcihp_pci_status[bsel].up &= ~(1U << slot);
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
|
QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
|
||||||
DeviceState *qdev = kid->child;
|
DeviceState *qdev = kid->child;
|
||||||
PCIDevice *dev = PCI_DEVICE(qdev);
|
PCIDevice *dev = PCI_DEVICE(qdev);
|
||||||
if (PCI_SLOT(dev->devfn) == slot) {
|
if (PCI_SLOT(dev->devfn) == slot) {
|
||||||
if (acpi_pcihp_pc_no_hotplug(s, dev)) {
|
if (!acpi_pcihp_pc_no_hotplug(s, dev)) {
|
||||||
slot_free = false;
|
|
||||||
} else {
|
|
||||||
object_unparent(OBJECT(qdev));
|
object_unparent(OBJECT(qdev));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slot_free) {
|
|
||||||
s->acpi_pcihp_pci_status[bsel].device_present &= ~(1U << slot);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
|
static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
|
||||||
@@ -153,7 +151,6 @@ static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0;
|
s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0;
|
||||||
s->acpi_pcihp_pci_status[bsel].device_present = 0;
|
|
||||||
|
|
||||||
if (!bus) {
|
if (!bus) {
|
||||||
return;
|
return;
|
||||||
@@ -166,8 +163,6 @@ static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
|
|||||||
if (acpi_pcihp_pc_no_hotplug(s, pdev)) {
|
if (acpi_pcihp_pc_no_hotplug(s, pdev)) {
|
||||||
s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot);
|
s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,40 +180,47 @@ void acpi_pcihp_reset(AcpiPciHpState *s)
|
|||||||
acpi_pcihp_update(s);
|
acpi_pcihp_update(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void enable_device(AcpiPciHpState *s, unsigned bsel, int slot)
|
void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s,
|
||||||
|
DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
|
PCIDevice *pdev = PCI_DEVICE(dev);
|
||||||
}
|
int slot = PCI_SLOT(pdev->devfn);
|
||||||
|
int bsel = acpi_pcihp_get_bsel(pdev->bus);
|
||||||
static void disable_device(AcpiPciHpState *s, unsigned bsel, int slot)
|
|
||||||
{
|
|
||||||
s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
int acpi_pcihp_device_hotplug(AcpiPciHpState *s, PCIDevice *dev,
|
|
||||||
PCIHotplugState state)
|
|
||||||
{
|
|
||||||
int slot = PCI_SLOT(dev->devfn);
|
|
||||||
int bsel = acpi_pcihp_get_bsel(dev->bus);
|
|
||||||
if (bsel < 0) {
|
if (bsel < 0) {
|
||||||
return -1;
|
error_setg(errp, "Unsupported bus. Bus doesn't have property '"
|
||||||
|
ACPI_PCIHP_PROP_BSEL "' set");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't send event when device is enabled during qemu machine creation:
|
/* Don't send event when device is enabled during qemu machine creation:
|
||||||
* it is present on boot, no hotplug event is necessary. We do send an
|
* it is present on boot, no hotplug event is necessary. We do send an
|
||||||
* event when the device is disabled later. */
|
* event when the device is disabled later. */
|
||||||
if (state == PCI_COLDPLUG_ENABLED) {
|
if (!dev->hotplugged) {
|
||||||
s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
|
return;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == PCI_HOTPLUG_ENABLED) {
|
s->acpi_pcihp_pci_status[bsel].up |= (1U << slot);
|
||||||
enable_device(s, bsel, slot);
|
|
||||||
} else {
|
ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS;
|
||||||
disable_device(s, bsel, slot);
|
acpi_update_sci(ar, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s,
|
||||||
|
DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
PCIDevice *pdev = PCI_DEVICE(dev);
|
||||||
|
int slot = PCI_SLOT(pdev->devfn);
|
||||||
|
int bsel = acpi_pcihp_get_bsel(pdev->bus);
|
||||||
|
if (bsel < 0) {
|
||||||
|
error_setg(errp, "Unsupported bus. Bus doesn't have property '"
|
||||||
|
ACPI_PCIHP_PROP_BSEL "' set");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
|
||||||
|
|
||||||
|
ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS;
|
||||||
|
acpi_update_sci(ar, irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
|
static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
|
||||||
@@ -232,26 +234,26 @@ static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case PCI_UP_BASE - PCI_HOTPLUG_ADDR:
|
case PCI_UP_BASE:
|
||||||
/* Manufacture an "up" value to cause a device check on any hotplug
|
val = s->acpi_pcihp_pci_status[bsel].up;
|
||||||
* slot with a device. Extra device checks are harmless. */
|
if (!s->legacy_piix) {
|
||||||
val = s->acpi_pcihp_pci_status[bsel].device_present &
|
s->acpi_pcihp_pci_status[bsel].up = 0;
|
||||||
s->acpi_pcihp_pci_status[bsel].hotplug_enable;
|
}
|
||||||
ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val);
|
ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val);
|
||||||
break;
|
break;
|
||||||
case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR:
|
case PCI_DOWN_BASE:
|
||||||
val = s->acpi_pcihp_pci_status[bsel].down;
|
val = s->acpi_pcihp_pci_status[bsel].down;
|
||||||
ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val);
|
ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val);
|
||||||
break;
|
break;
|
||||||
case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
|
case PCI_EJ_BASE:
|
||||||
/* No feature defined yet */
|
/* No feature defined yet */
|
||||||
ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val);
|
ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val);
|
||||||
break;
|
break;
|
||||||
case PCI_RMV_BASE - PCI_HOTPLUG_ADDR:
|
case PCI_RMV_BASE:
|
||||||
val = s->acpi_pcihp_pci_status[bsel].hotplug_enable;
|
val = s->acpi_pcihp_pci_status[bsel].hotplug_enable;
|
||||||
ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val);
|
ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val);
|
||||||
break;
|
break;
|
||||||
case PCI_SEL_BASE - PCI_HOTPLUG_ADDR:
|
case PCI_SEL_BASE:
|
||||||
val = s->hotplug_select;
|
val = s->hotplug_select;
|
||||||
ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val);
|
ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val);
|
||||||
default:
|
default:
|
||||||
@@ -266,7 +268,7 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data,
|
|||||||
{
|
{
|
||||||
AcpiPciHpState *s = opaque;
|
AcpiPciHpState *s = opaque;
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
|
case PCI_EJ_BASE:
|
||||||
if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
|
if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -274,7 +276,7 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data,
|
|||||||
ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n",
|
ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n",
|
||||||
addr, data);
|
addr, data);
|
||||||
break;
|
break;
|
||||||
case PCI_SEL_BASE - PCI_HOTPLUG_ADDR:
|
case PCI_SEL_BASE:
|
||||||
s->hotplug_select = data;
|
s->hotplug_select = data;
|
||||||
ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n",
|
ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n",
|
||||||
addr, data);
|
addr, data);
|
||||||
@@ -294,13 +296,26 @@ static const MemoryRegionOps acpi_pcihp_io_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void acpi_pcihp_init(AcpiPciHpState *s, PCIBus *root_bus,
|
void acpi_pcihp_init(AcpiPciHpState *s, PCIBus *root_bus,
|
||||||
MemoryRegion *address_space_io)
|
MemoryRegion *address_space_io, bool bridges_enabled)
|
||||||
{
|
{
|
||||||
|
uint16_t io_size = ACPI_PCIHP_SIZE;
|
||||||
|
|
||||||
s->root= root_bus;
|
s->root= root_bus;
|
||||||
|
s->legacy_piix = !bridges_enabled;
|
||||||
|
|
||||||
|
if (s->legacy_piix) {
|
||||||
|
unsigned *bus_bsel = g_malloc(sizeof *bus_bsel);
|
||||||
|
|
||||||
|
io_size = ACPI_PCIHP_LEGACY_SIZE;
|
||||||
|
|
||||||
|
*bus_bsel = ACPI_PCIHP_BSEL_DEFAULT;
|
||||||
|
object_property_add_uint32_ptr(OBJECT(root_bus), ACPI_PCIHP_PROP_BSEL,
|
||||||
|
bus_bsel, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
memory_region_init_io(&s->io, NULL, &acpi_pcihp_io_ops, s,
|
memory_region_init_io(&s->io, NULL, &acpi_pcihp_io_ops, s,
|
||||||
"acpi-pci-hotplug",
|
"acpi-pci-hotplug", io_size);
|
||||||
PCI_HOTPLUG_SIZE);
|
memory_region_add_subregion(address_space_io, ACPI_PCIHP_ADDR, &s->io);
|
||||||
memory_region_add_subregion(address_space_io, PCI_HOTPLUG_ADDR, &s->io);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VMStateDescription vmstate_acpi_pcihp_pci_status = {
|
const VMStateDescription vmstate_acpi_pcihp_pci_status = {
|
||||||
|
|||||||
332
hw/acpi/piix4.c
332
hw/acpi/piix4.c
@@ -32,6 +32,7 @@
|
|||||||
#include "hw/acpi/piix4.h"
|
#include "hw/acpi/piix4.h"
|
||||||
#include "hw/acpi/pcihp.h"
|
#include "hw/acpi/pcihp.h"
|
||||||
#include "hw/acpi/cpu_hotplug.h"
|
#include "hw/acpi/cpu_hotplug.h"
|
||||||
|
#include "hw/hotplug.h"
|
||||||
|
|
||||||
//#define DEBUG
|
//#define DEBUG
|
||||||
|
|
||||||
@@ -44,15 +45,6 @@
|
|||||||
#define GPE_BASE 0xafe0
|
#define GPE_BASE 0xafe0
|
||||||
#define GPE_LEN 4
|
#define GPE_LEN 4
|
||||||
|
|
||||||
#define PCI_HOTPLUG_ADDR 0xae00
|
|
||||||
#define PCI_HOTPLUG_SIZE 0x000f
|
|
||||||
#define PCI_UP_BASE 0xae00
|
|
||||||
#define PCI_DOWN_BASE 0xae04
|
|
||||||
#define PCI_EJ_BASE 0xae08
|
|
||||||
#define PCI_RMV_BASE 0xae0c
|
|
||||||
|
|
||||||
#define PIIX4_PCI_HOTPLUG_STATUS 2
|
|
||||||
|
|
||||||
struct pci_status {
|
struct pci_status {
|
||||||
uint32_t up; /* deprecated, maintained for migration compatibility */
|
uint32_t up; /* deprecated, maintained for migration compatibility */
|
||||||
uint32_t down;
|
uint32_t down;
|
||||||
@@ -80,13 +72,6 @@ typedef struct PIIX4PMState {
|
|||||||
Notifier machine_ready;
|
Notifier machine_ready;
|
||||||
Notifier powerdown_notifier;
|
Notifier powerdown_notifier;
|
||||||
|
|
||||||
/* for legacy pci hotplug (compatible with qemu 1.6 and older) */
|
|
||||||
MemoryRegion io_pci;
|
|
||||||
struct pci_status pci0_status;
|
|
||||||
uint32_t pci0_hotplug_enable;
|
|
||||||
uint32_t pci0_slot_device_present;
|
|
||||||
|
|
||||||
/* for new pci hotplug (with PCI2PCI bridge support) */
|
|
||||||
AcpiPciHpState acpi_pci_hotplug;
|
AcpiPciHpState acpi_pci_hotplug;
|
||||||
bool use_acpi_pci_hotplug;
|
bool use_acpi_pci_hotplug;
|
||||||
|
|
||||||
@@ -170,17 +155,6 @@ static void pm_write_config(PCIDevice *d,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vmstate_pci_status_pre_save(void *opaque)
|
|
||||||
{
|
|
||||||
struct pci_status *pci0_status = opaque;
|
|
||||||
PIIX4PMState *s = container_of(pci0_status, PIIX4PMState, pci0_status);
|
|
||||||
|
|
||||||
/* We no longer track up, so build a safe value for migrating
|
|
||||||
* to a version that still does... of course these might get lost
|
|
||||||
* by an old buggy implementation, but we try. */
|
|
||||||
pci0_status->up = s->pci0_slot_device_present & s->pci0_hotplug_enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vmstate_acpi_post_load(void *opaque, int version_id)
|
static int vmstate_acpi_post_load(void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
PIIX4PMState *s = opaque;
|
PIIX4PMState *s = opaque;
|
||||||
@@ -216,10 +190,9 @@ static const VMStateDescription vmstate_pci_status = {
|
|||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.minimum_version_id_old = 1,
|
.minimum_version_id_old = 1,
|
||||||
.pre_save = vmstate_pci_status_pre_save,
|
|
||||||
.fields = (VMStateField []) {
|
.fields = (VMStateField []) {
|
||||||
VMSTATE_UINT32(up, struct pci_status),
|
VMSTATE_UINT32(up, struct AcpiPciHpPciStatus),
|
||||||
VMSTATE_UINT32(down, struct pci_status),
|
VMSTATE_UINT32(down, struct AcpiPciHpPciStatus),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -256,7 +229,8 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
|
|||||||
qemu_get_be16s(f, &temp);
|
qemu_get_be16s(f, &temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = vmstate_load_state(f, &vmstate_pci_status, &s->pci0_status, 1);
|
ret = vmstate_load_state(f, &vmstate_pci_status,
|
||||||
|
&s->acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT], 1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,70 +268,18 @@ static const VMStateDescription vmstate_acpi = {
|
|||||||
VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState),
|
VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState),
|
||||||
VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
|
VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
|
||||||
VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
|
VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
|
||||||
VMSTATE_STRUCT_TEST(pci0_status, PIIX4PMState,
|
VMSTATE_STRUCT_TEST(
|
||||||
vmstate_test_no_use_acpi_pci_hotplug,
|
acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT],
|
||||||
2, vmstate_pci_status,
|
PIIX4PMState,
|
||||||
struct pci_status),
|
vmstate_test_no_use_acpi_pci_hotplug,
|
||||||
|
2, vmstate_pci_status,
|
||||||
|
struct AcpiPciHpPciStatus),
|
||||||
VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState,
|
VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState,
|
||||||
vmstate_test_use_acpi_pci_hotplug),
|
vmstate_test_use_acpi_pci_hotplug),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots)
|
|
||||||
{
|
|
||||||
BusChild *kid, *next;
|
|
||||||
BusState *bus = qdev_get_parent_bus(DEVICE(s));
|
|
||||||
int slot = ffs(slots) - 1;
|
|
||||||
bool slot_free = true;
|
|
||||||
|
|
||||||
/* Mark request as complete */
|
|
||||||
s->pci0_status.down &= ~(1U << slot);
|
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) {
|
|
||||||
DeviceState *qdev = kid->child;
|
|
||||||
PCIDevice *dev = PCI_DEVICE(qdev);
|
|
||||||
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
|
|
||||||
if (PCI_SLOT(dev->devfn) == slot) {
|
|
||||||
if (pc->no_hotplug) {
|
|
||||||
slot_free = false;
|
|
||||||
} else {
|
|
||||||
object_unparent(OBJECT(qdev));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (slot_free) {
|
|
||||||
s->pci0_slot_device_present &= ~(1U << slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void piix4_update_hotplug(PIIX4PMState *s)
|
|
||||||
{
|
|
||||||
BusState *bus = qdev_get_parent_bus(DEVICE(s));
|
|
||||||
BusChild *kid, *next;
|
|
||||||
|
|
||||||
/* Execute any pending removes during reset */
|
|
||||||
while (s->pci0_status.down) {
|
|
||||||
acpi_piix_eject_slot(s, s->pci0_status.down);
|
|
||||||
}
|
|
||||||
|
|
||||||
s->pci0_hotplug_enable = ~0;
|
|
||||||
s->pci0_slot_device_present = 0;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) {
|
|
||||||
DeviceState *qdev = kid->child;
|
|
||||||
PCIDevice *pdev = PCI_DEVICE(qdev);
|
|
||||||
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pdev);
|
|
||||||
int slot = PCI_SLOT(pdev->devfn);
|
|
||||||
|
|
||||||
if (pc->no_hotplug) {
|
|
||||||
s->pci0_hotplug_enable &= ~(1U << slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
s->pci0_slot_device_present |= (1U << slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void piix4_reset(void *opaque)
|
static void piix4_reset(void *opaque)
|
||||||
{
|
{
|
||||||
PIIX4PMState *s = opaque;
|
PIIX4PMState *s = opaque;
|
||||||
@@ -377,11 +299,7 @@ static void piix4_reset(void *opaque)
|
|||||||
pci_conf[0x5B] = 0x02;
|
pci_conf[0x5B] = 0x02;
|
||||||
}
|
}
|
||||||
pm_io_space_update(s);
|
pm_io_space_update(s);
|
||||||
if (s->use_acpi_pci_hotplug) {
|
acpi_pcihp_reset(&s->acpi_pci_hotplug);
|
||||||
acpi_pcihp_reset(&s->acpi_pci_hotplug);
|
|
||||||
} else {
|
|
||||||
piix4_update_hotplug(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
|
static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
|
||||||
@@ -392,24 +310,26 @@ static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
|
|||||||
acpi_pm1_evt_power_down(&s->ar);
|
acpi_pm1_evt_power_down(&s->ar);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int piix4_acpi_pci_hotplug(DeviceState *qdev, PCIDevice *dev,
|
static void piix4_pci_device_plug_cb(HotplugHandler *hotplug_dev,
|
||||||
PCIHotplugState state)
|
DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
PIIX4PMState *s = PIIX4_PM(qdev);
|
PIIX4PMState *s = PIIX4_PM(hotplug_dev);
|
||||||
int ret = acpi_pcihp_device_hotplug(&s->acpi_pci_hotplug, dev, state);
|
acpi_pcihp_device_plug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, errp);
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
|
|
||||||
|
|
||||||
acpi_update_sci(&s->ar, s->irq);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void piix4_update_bus_hotplug(PCIBus *bus, void *opaque)
|
static void piix4_pci_device_unplug_cb(HotplugHandler *hotplug_dev,
|
||||||
|
DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
PIIX4PMState *s = PIIX4_PM(hotplug_dev);
|
||||||
|
acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev,
|
||||||
|
errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void piix4_update_bus_hotplug(PCIBus *pci_bus, void *opaque)
|
||||||
{
|
{
|
||||||
PIIX4PMState *s = opaque;
|
PIIX4PMState *s = opaque;
|
||||||
pci_bus_hotplug(bus, piix4_acpi_pci_hotplug, DEVICE(s));
|
|
||||||
|
qbus_set_hotplug_handler(BUS(pci_bus), DEVICE(s), &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void piix4_pm_machine_ready(Notifier *n, void *opaque)
|
static void piix4_pm_machine_ready(Notifier *n, void *opaque)
|
||||||
@@ -428,6 +348,8 @@ static void piix4_pm_machine_ready(Notifier *n, void *opaque)
|
|||||||
|
|
||||||
if (s->use_acpi_pci_hotplug) {
|
if (s->use_acpi_pci_hotplug) {
|
||||||
pci_for_each_bus(d->bus, piix4_update_bus_hotplug, s);
|
pci_for_each_bus(d->bus, piix4_update_bus_hotplug, s);
|
||||||
|
} else {
|
||||||
|
piix4_update_bus_hotplug(d->bus, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,9 +439,9 @@ Object *piix4_pm_find(void)
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
|
I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
|
||||||
qemu_irq sci_irq, qemu_irq smi_irq,
|
qemu_irq sci_irq, qemu_irq smi_irq,
|
||||||
int kvm_enabled, FWCfgState *fw_cfg)
|
int kvm_enabled, FWCfgState *fw_cfg)
|
||||||
{
|
{
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
PIIX4PMState *s;
|
PIIX4PMState *s;
|
||||||
@@ -545,52 +467,6 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
|
|||||||
return s->smb.smbus;
|
return s->smb.smbus;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Property piix4_pm_properties[] = {
|
|
||||||
DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
|
|
||||||
DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
|
|
||||||
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0),
|
|
||||||
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2),
|
|
||||||
DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState,
|
|
||||||
use_acpi_pci_hotplug, true),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
|
||||||
};
|
|
||||||
|
|
||||||
static void piix4_pm_class_init(ObjectClass *klass, void *data)
|
|
||||||
{
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
||||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
k->no_hotplug = 1;
|
|
||||||
k->init = piix4_pm_initfn;
|
|
||||||
k->config_write = pm_write_config;
|
|
||||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
|
||||||
k->device_id = PCI_DEVICE_ID_INTEL_82371AB_3;
|
|
||||||
k->revision = 0x03;
|
|
||||||
k->class_id = PCI_CLASS_BRIDGE_OTHER;
|
|
||||||
dc->desc = "PM";
|
|
||||||
dc->vmsd = &vmstate_acpi;
|
|
||||||
dc->props = piix4_pm_properties;
|
|
||||||
/*
|
|
||||||
* Reason: part of PIIX4 southbridge, needs to be wired up,
|
|
||||||
* e.g. by mips_malta_init()
|
|
||||||
*/
|
|
||||||
dc->cannot_instantiate_with_device_add_yet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const TypeInfo piix4_pm_info = {
|
|
||||||
.name = TYPE_PIIX4_PM,
|
|
||||||
.parent = TYPE_PCI_DEVICE,
|
|
||||||
.instance_size = sizeof(PIIX4PMState),
|
|
||||||
.class_init = piix4_pm_class_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void piix4_pm_register_types(void)
|
|
||||||
{
|
|
||||||
type_register_static(&piix4_pm_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
type_init(piix4_pm_register_types)
|
|
||||||
|
|
||||||
static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width)
|
static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width)
|
||||||
{
|
{
|
||||||
PIIX4PMState *s = opaque;
|
PIIX4PMState *s = opaque;
|
||||||
@@ -621,60 +497,6 @@ static const MemoryRegionOps piix4_gpe_ops = {
|
|||||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
|
|
||||||
{
|
|
||||||
PIIX4PMState *s = opaque;
|
|
||||||
uint32_t val = 0;
|
|
||||||
|
|
||||||
switch (addr) {
|
|
||||||
case PCI_UP_BASE - PCI_HOTPLUG_ADDR:
|
|
||||||
/* Manufacture an "up" value to cause a device check on any hotplug
|
|
||||||
* slot with a device. Extra device checks are harmless. */
|
|
||||||
val = s->pci0_slot_device_present & s->pci0_hotplug_enable;
|
|
||||||
PIIX4_DPRINTF("pci_up_read %" PRIu32 "\n", val);
|
|
||||||
break;
|
|
||||||
case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR:
|
|
||||||
val = s->pci0_status.down;
|
|
||||||
PIIX4_DPRINTF("pci_down_read %" PRIu32 "\n", val);
|
|
||||||
break;
|
|
||||||
case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
|
|
||||||
/* No feature defined yet */
|
|
||||||
PIIX4_DPRINTF("pci_features_read %" PRIu32 "\n", val);
|
|
||||||
break;
|
|
||||||
case PCI_RMV_BASE - PCI_HOTPLUG_ADDR:
|
|
||||||
val = s->pci0_hotplug_enable;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pci_write(void *opaque, hwaddr addr, uint64_t data,
|
|
||||||
unsigned int size)
|
|
||||||
{
|
|
||||||
switch (addr) {
|
|
||||||
case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
|
|
||||||
acpi_piix_eject_slot(opaque, (uint32_t)data);
|
|
||||||
PIIX4_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n",
|
|
||||||
addr, data);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const MemoryRegionOps piix4_pci_ops = {
|
|
||||||
.read = pci_read,
|
|
||||||
.write = pci_write,
|
|
||||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
||||||
.valid = {
|
|
||||||
.min_access_size = 4,
|
|
||||||
.max_access_size = 4,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void piix4_cpu_added_req(Notifier *n, void *opaque)
|
static void piix4_cpu_added_req(Notifier *n, void *opaque)
|
||||||
{
|
{
|
||||||
PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_added_notifier);
|
PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_added_notifier);
|
||||||
@@ -684,9 +506,6 @@ static void piix4_cpu_added_req(Notifier *n, void *opaque)
|
|||||||
acpi_update_sci(&s->ar, s->irq);
|
acpi_update_sci(&s->ar, s->irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
|
|
||||||
PCIHotplugState state);
|
|
||||||
|
|
||||||
static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
|
static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
|
||||||
PCIBus *bus, PIIX4PMState *s)
|
PCIBus *bus, PIIX4PMState *s)
|
||||||
{
|
{
|
||||||
@@ -694,15 +513,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
|
|||||||
"acpi-gpe0", GPE_LEN);
|
"acpi-gpe0", GPE_LEN);
|
||||||
memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe);
|
memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe);
|
||||||
|
|
||||||
if (s->use_acpi_pci_hotplug) {
|
acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent,
|
||||||
acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent);
|
s->use_acpi_pci_hotplug);
|
||||||
} else {
|
|
||||||
memory_region_init_io(&s->io_pci, OBJECT(s), &piix4_pci_ops, s,
|
|
||||||
"acpi-pci-hotplug", PCI_HOTPLUG_SIZE);
|
|
||||||
memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR,
|
|
||||||
&s->io_pci);
|
|
||||||
pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
AcpiCpuHotplug_init(parent, OBJECT(s), &s->gpe_cpu,
|
AcpiCpuHotplug_init(parent, OBJECT(s), &s->gpe_cpu,
|
||||||
PIIX4_CPU_HOTPLUG_IO_BASE);
|
PIIX4_CPU_HOTPLUG_IO_BASE);
|
||||||
@@ -710,39 +522,55 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
|
|||||||
qemu_register_cpu_added_notifier(&s->cpu_added_notifier);
|
qemu_register_cpu_added_notifier(&s->cpu_added_notifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void enable_device(PIIX4PMState *s, int slot)
|
static Property piix4_pm_properties[] = {
|
||||||
|
DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
|
||||||
|
DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
|
||||||
|
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0),
|
||||||
|
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2),
|
||||||
|
DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState,
|
||||||
|
use_acpi_pci_hotplug, true),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void piix4_pm_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
s->pci0_slot_device_present |= (1U << slot);
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||||
|
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||||
|
|
||||||
|
k->init = piix4_pm_initfn;
|
||||||
|
k->config_write = pm_write_config;
|
||||||
|
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||||
|
k->device_id = PCI_DEVICE_ID_INTEL_82371AB_3;
|
||||||
|
k->revision = 0x03;
|
||||||
|
k->class_id = PCI_CLASS_BRIDGE_OTHER;
|
||||||
|
dc->desc = "PM";
|
||||||
|
dc->vmsd = &vmstate_acpi;
|
||||||
|
dc->props = piix4_pm_properties;
|
||||||
|
/*
|
||||||
|
* Reason: part of PIIX4 southbridge, needs to be wired up,
|
||||||
|
* e.g. by mips_malta_init()
|
||||||
|
*/
|
||||||
|
dc->cannot_instantiate_with_device_add_yet = true;
|
||||||
|
dc->hotpluggable = false;
|
||||||
|
hc->plug = piix4_pci_device_plug_cb;
|
||||||
|
hc->unplug = piix4_pci_device_unplug_cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disable_device(PIIX4PMState *s, int slot)
|
static const TypeInfo piix4_pm_info = {
|
||||||
{
|
.name = TYPE_PIIX4_PM,
|
||||||
s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
|
.parent = TYPE_PCI_DEVICE,
|
||||||
s->pci0_status.down |= (1U << slot);
|
.instance_size = sizeof(PIIX4PMState),
|
||||||
}
|
.class_init = piix4_pm_class_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
|
{ TYPE_HOTPLUG_HANDLER },
|
||||||
PCIHotplugState state)
|
{ }
|
||||||
{
|
|
||||||
int slot = PCI_SLOT(dev->devfn);
|
|
||||||
PIIX4PMState *s = PIIX4_PM(qdev);
|
|
||||||
|
|
||||||
/* Don't send event when device is enabled during qemu machine creation:
|
|
||||||
* it is present on boot, no hotplug event is necessary. We do send an
|
|
||||||
* event when the device is disabled later. */
|
|
||||||
if (state == PCI_COLDPLUG_ENABLED) {
|
|
||||||
s->pci0_slot_device_present |= (1U << slot);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (state == PCI_HOTPLUG_ENABLED) {
|
static void piix4_pm_register_types(void)
|
||||||
enable_device(s, slot);
|
{
|
||||||
} else {
|
type_register_static(&piix4_pm_info);
|
||||||
disable_device(s, slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
acpi_update_sci(&s->ar, s->irq);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type_init(piix4_pm_register_types)
|
||||||
|
|||||||
@@ -161,8 +161,9 @@ static void clipper_init(QEMUMachineInitArgs *args)
|
|||||||
load_image_targphys(initrd_filename, initrd_base,
|
load_image_targphys(initrd_filename, initrd_base,
|
||||||
ram_size - initrd_base);
|
ram_size - initrd_base);
|
||||||
|
|
||||||
stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000ULL);
|
stq_phys(&address_space_memory,
|
||||||
stq_phys(param_offset + 0x108, initrd_size);
|
param_offset + 0x100, initrd_base + 0xfffffc0000000000ULL);
|
||||||
|
stq_phys(&address_space_memory, param_offset + 0x108, initrd_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -613,7 +613,7 @@ static bool make_iommu_tlbe(hwaddr taddr, hwaddr mask, IOMMUTLBEntry *ret)
|
|||||||
translation, given the address of the PTE. */
|
translation, given the address of the PTE. */
|
||||||
static bool pte_translate(hwaddr pte_addr, IOMMUTLBEntry *ret)
|
static bool pte_translate(hwaddr pte_addr, IOMMUTLBEntry *ret)
|
||||||
{
|
{
|
||||||
uint64_t pte = ldq_phys(pte_addr);
|
uint64_t pte = ldq_phys(&address_space_memory, pte_addr);
|
||||||
|
|
||||||
/* Check valid bit. */
|
/* Check valid bit. */
|
||||||
if ((pte & 1) == 0) {
|
if ((pte & 1) == 0) {
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ static void aw_a10_init(Object *obj)
|
|||||||
|
|
||||||
object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
|
object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
|
||||||
qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
|
qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
|
||||||
|
|
||||||
|
object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default());
|
||||||
|
if (nd_table[0].used) {
|
||||||
|
qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
|
||||||
|
qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aw_a10_realize(DeviceState *dev, Error **errp)
|
static void aw_a10_realize(DeviceState *dev, Error **errp)
|
||||||
@@ -76,6 +83,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
|
|||||||
sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
|
sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
|
||||||
sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
|
sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
|
||||||
|
|
||||||
|
object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
|
||||||
|
if (err != NULL) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sysbusdev = SYS_BUS_DEVICE(&s->emac);
|
||||||
|
sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE);
|
||||||
|
sysbus_connect_irq(sysbusdev, 0, s->irq[55]);
|
||||||
|
|
||||||
serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
|
serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
|
||||||
115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
|
115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "elf.h"
|
#include "elf.h"
|
||||||
#include "sysemu/device_tree.h"
|
#include "sysemu/device_tree.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
|
||||||
/* Kernel boot protocol is specified in the kernel docs
|
/* Kernel boot protocol is specified in the kernel docs
|
||||||
* Documentation/arm/Booting and Documentation/arm64/booting.txt
|
* Documentation/arm/Booting and Documentation/arm64/booting.txt
|
||||||
@@ -169,7 +170,7 @@ static void default_reset_secondary(ARMCPU *cpu,
|
|||||||
{
|
{
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
|
|
||||||
stl_phys_notdirty(info->smp_bootreg_addr, 0);
|
stl_phys_notdirty(&address_space_memory, info->smp_bootreg_addr, 0);
|
||||||
env->regs[15] = info->smp_loader_start;
|
env->regs[15] = info->smp_loader_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +180,7 @@ static inline bool have_dtb(const struct arm_boot_info *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define WRITE_WORD(p, value) do { \
|
#define WRITE_WORD(p, value) do { \
|
||||||
stl_phys_notdirty(p, value); \
|
stl_phys_notdirty(&address_space_memory, p, value); \
|
||||||
p += 4; \
|
p += 4; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|||||||
@@ -36,10 +36,17 @@ static void cubieboard_init(QEMUMachineInitArgs *args)
|
|||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
s->a10 = AW_A10(object_new(TYPE_AW_A10));
|
s->a10 = AW_A10(object_new(TYPE_AW_A10));
|
||||||
|
|
||||||
|
object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err);
|
||||||
|
if (err != NULL) {
|
||||||
|
error_report("Couldn't set phy address: %s", error_get_pretty(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
|
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
error_report("Couldn't realize Allwinner A10: %s\n",
|
error_report("Couldn't realize Allwinner A10: %s",
|
||||||
error_get_pretty(err));
|
error_get_pretty(err));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
|||||||
busdev = SYS_BUS_DEVICE(dev);
|
busdev = SYS_BUS_DEVICE(dev);
|
||||||
sysbus_connect_irq(busdev, 0, i2c_irq);
|
sysbus_connect_irq(busdev, 0, i2c_irq);
|
||||||
sysbus_mmio_map(busdev, 0, addr);
|
sysbus_mmio_map(busdev, 0, addr);
|
||||||
s->i2c_if[n] = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
|
s->i2c_if[n] = (I2CBus *)qdev_get_child_bus(dev, "i2c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -69,11 +69,11 @@ static void hb_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
|
|||||||
|
|
||||||
switch (info->nb_cpus) {
|
switch (info->nb_cpus) {
|
||||||
case 4:
|
case 4:
|
||||||
stl_phys_notdirty(SMP_BOOT_REG + 0x30, 0);
|
stl_phys_notdirty(&address_space_memory, SMP_BOOT_REG + 0x30, 0);
|
||||||
case 3:
|
case 3:
|
||||||
stl_phys_notdirty(SMP_BOOT_REG + 0x20, 0);
|
stl_phys_notdirty(&address_space_memory, SMP_BOOT_REG + 0x20, 0);
|
||||||
case 2:
|
case 2:
|
||||||
stl_phys_notdirty(SMP_BOOT_REG + 0x10, 0);
|
stl_phys_notdirty(&address_space_memory, SMP_BOOT_REG + 0x10, 0);
|
||||||
env->regs[15] = SMP_BOOT_ADDR;
|
env->regs[15] = SMP_BOOT_ADDR;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -92,8 +92,6 @@
|
|||||||
#define MP_ETH_CRDP3 0x4AC
|
#define MP_ETH_CRDP3 0x4AC
|
||||||
#define MP_ETH_CTDP0 0x4E0
|
#define MP_ETH_CTDP0 0x4E0
|
||||||
#define MP_ETH_CTDP1 0x4E4
|
#define MP_ETH_CTDP1 0x4E4
|
||||||
#define MP_ETH_CTDP2 0x4E8
|
|
||||||
#define MP_ETH_CTDP3 0x4EC
|
|
||||||
|
|
||||||
/* MII PHY access */
|
/* MII PHY access */
|
||||||
#define MP_ETH_SMIR_DATA 0x0000FFFF
|
#define MP_ETH_SMIR_DATA 0x0000FFFF
|
||||||
@@ -308,7 +306,7 @@ static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
|
|||||||
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
|
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
|
||||||
return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
|
return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
|
||||||
|
|
||||||
case MP_ETH_CTDP0 ... MP_ETH_CTDP3:
|
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
|
||||||
return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
|
return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -362,7 +360,7 @@ static void mv88w8618_eth_write(void *opaque, hwaddr offset,
|
|||||||
s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
|
s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MP_ETH_CTDP0 ... MP_ETH_CTDP3:
|
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
|
||||||
s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
|
s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -632,7 +630,7 @@ static int musicpal_lcd_init(SysBusDevice *sbd)
|
|||||||
"musicpal-lcd", MP_LCD_SIZE);
|
"musicpal-lcd", MP_LCD_SIZE);
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
|
|
||||||
s->con = graphic_console_init(dev, &musicpal_gfx_ops, s);
|
s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
|
||||||
qemu_console_resize(s->con, 128*3, 64*3);
|
qemu_console_resize(s->con, 128*3, 64*3);
|
||||||
|
|
||||||
qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
|
qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
|
||||||
@@ -1593,7 +1591,7 @@ static void musicpal_init(QEMUMachineInitArgs *args)
|
|||||||
DeviceState *key_dev;
|
DeviceState *key_dev;
|
||||||
DeviceState *wm8750_dev;
|
DeviceState *wm8750_dev;
|
||||||
SysBusDevice *s;
|
SysBusDevice *s;
|
||||||
i2c_bus *i2c;
|
I2CBus *i2c;
|
||||||
int i;
|
int i;
|
||||||
unsigned long flash_size;
|
unsigned long flash_size;
|
||||||
DriveInfo *dinfo;
|
DriveInfo *dinfo;
|
||||||
@@ -1687,7 +1685,7 @@ static void musicpal_init(QEMUMachineInitArgs *args)
|
|||||||
dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE,
|
dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE,
|
||||||
pic[MP_GPIO_IRQ]);
|
pic[MP_GPIO_IRQ]);
|
||||||
i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL);
|
i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL);
|
||||||
i2c = (i2c_bus *)qdev_get_child_bus(i2c_dev, "i2c");
|
i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c");
|
||||||
|
|
||||||
lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL);
|
lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL);
|
||||||
key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL);
|
key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL);
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ static void n8x0_i2c_setup(struct n800_s *s)
|
|||||||
{
|
{
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
qemu_irq tmp_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_TMP105_GPIO);
|
qemu_irq tmp_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_TMP105_GPIO);
|
||||||
i2c_bus *i2c = omap_i2c_bus(s->mpu->i2c[0]);
|
I2CBus *i2c = omap_i2c_bus(s->mpu->i2c[0]);
|
||||||
|
|
||||||
/* Attach a menelaus PM chip */
|
/* Attach a menelaus PM chip */
|
||||||
dev = i2c_create_slave(i2c, "twl92230", N8X0_MENELAUS_ADDR);
|
dev = i2c_create_slave(i2c, "twl92230", N8X0_MENELAUS_ADDR);
|
||||||
|
|||||||
@@ -224,27 +224,24 @@ static const VMStateDescription vmstate_pxa2xx_cm = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int pxa2xx_clkcfg_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
static uint64_t pxa2xx_clkcfg_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
uint64_t *value)
|
|
||||||
{
|
{
|
||||||
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
||||||
*value = s->clkcfg;
|
return s->clkcfg;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_clkcfg_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
static void pxa2xx_clkcfg_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
uint64_t value)
|
uint64_t value)
|
||||||
{
|
{
|
||||||
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
||||||
s->clkcfg = value & 0xf;
|
s->clkcfg = value & 0xf;
|
||||||
if (value & 2) {
|
if (value & 2) {
|
||||||
printf("%s: CPU frequency change attempt\n", __func__);
|
printf("%s: CPU frequency change attempt\n", __func__);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
static void pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
uint64_t value)
|
uint64_t value)
|
||||||
{
|
{
|
||||||
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
||||||
static const char *pwrmode[8] = {
|
static const char *pwrmode[8] = {
|
||||||
@@ -275,11 +272,11 @@ static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||||||
goto message;
|
goto message;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
s->cpu->env.uncached_cpsr =
|
s->cpu->env.uncached_cpsr = ARM_CPU_MODE_SVC;
|
||||||
ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
|
s->cpu->env.daif = PSTATE_A | PSTATE_F | PSTATE_I;
|
||||||
s->cpu->env.cp15.c1_sys = 0;
|
s->cpu->env.cp15.c1_sys = 0;
|
||||||
s->cpu->env.cp15.c1_coproc = 0;
|
s->cpu->env.cp15.c1_coproc = 0;
|
||||||
s->cpu->env.cp15.c2_base0 = 0;
|
s->cpu->env.cp15.ttbr0_el1 = 0;
|
||||||
s->cpu->env.cp15.c3 = 0;
|
s->cpu->env.cp15.c3 = 0;
|
||||||
s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
|
s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
|
||||||
s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
|
s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
|
||||||
@@ -310,36 +307,29 @@ static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||||||
printf("%s: machine entered %s mode\n", __func__,
|
printf("%s: machine entered %s mode\n", __func__,
|
||||||
pwrmode[value & 7]);
|
pwrmode[value & 7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_cppmnc_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
static uint64_t pxa2xx_cppmnc_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
uint64_t *value)
|
|
||||||
{
|
{
|
||||||
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
||||||
*value = s->pmnc;
|
return s->pmnc;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_cppmnc_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
static void pxa2xx_cppmnc_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
uint64_t value)
|
uint64_t value)
|
||||||
{
|
{
|
||||||
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
||||||
s->pmnc = value;
|
s->pmnc = value;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
static uint64_t pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
uint64_t *value)
|
|
||||||
{
|
{
|
||||||
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
PXA2xxState *s = (PXA2xxState *)ri->opaque;
|
||||||
if (s->pmnc & 1) {
|
if (s->pmnc & 1) {
|
||||||
*value = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
} else {
|
} else {
|
||||||
*value = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const ARMCPRegInfo pxa_cp_reginfo[] = {
|
static const ARMCPRegInfo pxa_cp_reginfo[] = {
|
||||||
@@ -1222,8 +1212,14 @@ static const TypeInfo pxa2xx_rtc_sysbus_info = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* I2C Interface */
|
/* I2C Interface */
|
||||||
typedef struct {
|
|
||||||
I2CSlave i2c;
|
#define TYPE_PXA2XX_I2C_SLAVE "pxa2xx-i2c-slave"
|
||||||
|
#define PXA2XX_I2C_SLAVE(obj) \
|
||||||
|
OBJECT_CHECK(PXA2xxI2CSlaveState, (obj), TYPE_PXA2XX_I2C_SLAVE)
|
||||||
|
|
||||||
|
typedef struct PXA2xxI2CSlaveState {
|
||||||
|
I2CSlave parent_obj;
|
||||||
|
|
||||||
PXA2xxI2CState *host;
|
PXA2xxI2CState *host;
|
||||||
} PXA2xxI2CSlaveState;
|
} PXA2xxI2CSlaveState;
|
||||||
|
|
||||||
@@ -1238,7 +1234,7 @@ struct PXA2xxI2CState {
|
|||||||
|
|
||||||
MemoryRegion iomem;
|
MemoryRegion iomem;
|
||||||
PXA2xxI2CSlaveState *slave;
|
PXA2xxI2CSlaveState *slave;
|
||||||
i2c_bus *bus;
|
I2CBus *bus;
|
||||||
qemu_irq irq;
|
qemu_irq irq;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
uint32_t region_size;
|
uint32_t region_size;
|
||||||
@@ -1268,7 +1264,7 @@ static void pxa2xx_i2c_update(PXA2xxI2CState *s)
|
|||||||
/* These are only stubs now. */
|
/* These are only stubs now. */
|
||||||
static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
||||||
{
|
{
|
||||||
PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
|
PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c);
|
||||||
PXA2xxI2CState *s = slave->host;
|
PXA2xxI2CState *s = slave->host;
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
@@ -1292,10 +1288,12 @@ static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
|
|||||||
|
|
||||||
static int pxa2xx_i2c_rx(I2CSlave *i2c)
|
static int pxa2xx_i2c_rx(I2CSlave *i2c)
|
||||||
{
|
{
|
||||||
PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
|
PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c);
|
||||||
PXA2xxI2CState *s = slave->host;
|
PXA2xxI2CState *s = slave->host;
|
||||||
if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
|
|
||||||
|
if ((s->control & (1 << 14)) || !(s->control & (1 << 6))) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (s->status & (1 << 0)) { /* RWM */
|
if (s->status & (1 << 0)) { /* RWM */
|
||||||
s->status |= 1 << 6; /* set ITE */
|
s->status |= 1 << 6; /* set ITE */
|
||||||
@@ -1307,10 +1305,12 @@ static int pxa2xx_i2c_rx(I2CSlave *i2c)
|
|||||||
|
|
||||||
static int pxa2xx_i2c_tx(I2CSlave *i2c, uint8_t data)
|
static int pxa2xx_i2c_tx(I2CSlave *i2c, uint8_t data)
|
||||||
{
|
{
|
||||||
PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
|
PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c);
|
||||||
PXA2xxI2CState *s = slave->host;
|
PXA2xxI2CState *s = slave->host;
|
||||||
if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
|
|
||||||
|
if ((s->control & (1 << 14)) || !(s->control & (1 << 6))) {
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(s->status & (1 << 0))) { /* RWM */
|
if (!(s->status & (1 << 0))) { /* RWM */
|
||||||
s->status |= 1 << 7; /* set IRF */
|
s->status |= 1 << 7; /* set IRF */
|
||||||
@@ -1325,6 +1325,7 @@ static uint64_t pxa2xx_i2c_read(void *opaque, hwaddr addr,
|
|||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
PXA2xxI2CState *s = (PXA2xxI2CState *) opaque;
|
PXA2xxI2CState *s = (PXA2xxI2CState *) opaque;
|
||||||
|
I2CSlave *slave;
|
||||||
|
|
||||||
addr -= s->offset;
|
addr -= s->offset;
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
@@ -1333,7 +1334,8 @@ static uint64_t pxa2xx_i2c_read(void *opaque, hwaddr addr,
|
|||||||
case ISR:
|
case ISR:
|
||||||
return s->status | (i2c_bus_busy(s->bus) << 2);
|
return s->status | (i2c_bus_busy(s->bus) << 2);
|
||||||
case ISAR:
|
case ISAR:
|
||||||
return s->slave->i2c.address;
|
slave = I2C_SLAVE(s->slave);
|
||||||
|
return slave->address;
|
||||||
case IDBR:
|
case IDBR:
|
||||||
return s->data;
|
return s->data;
|
||||||
case IBMR:
|
case IBMR:
|
||||||
@@ -1408,7 +1410,7 @@ static void pxa2xx_i2c_write(void *opaque, hwaddr addr,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ISAR:
|
case ISAR:
|
||||||
i2c_set_slave_address(&s->slave->i2c, value & 0x7f);
|
i2c_set_slave_address(I2C_SLAVE(s->slave), value & 0x7f);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDBR:
|
case IDBR:
|
||||||
@@ -1432,7 +1434,7 @@ static const VMStateDescription vmstate_pxa2xx_i2c_slave = {
|
|||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.minimum_version_id_old = 1,
|
.minimum_version_id_old = 1,
|
||||||
.fields = (VMStateField []) {
|
.fields = (VMStateField []) {
|
||||||
VMSTATE_I2C_SLAVE(i2c, PXA2xxI2CSlaveState),
|
VMSTATE_I2C_SLAVE(parent_obj, PXA2xxI2CSlaveState),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1448,7 +1450,7 @@ static const VMStateDescription vmstate_pxa2xx_i2c = {
|
|||||||
VMSTATE_UINT8(ibmr, PXA2xxI2CState),
|
VMSTATE_UINT8(ibmr, PXA2xxI2CState),
|
||||||
VMSTATE_UINT8(data, PXA2xxI2CState),
|
VMSTATE_UINT8(data, PXA2xxI2CState),
|
||||||
VMSTATE_STRUCT_POINTER(slave, PXA2xxI2CState,
|
VMSTATE_STRUCT_POINTER(slave, PXA2xxI2CState,
|
||||||
vmstate_pxa2xx_i2c_slave, PXA2xxI2CSlaveState *),
|
vmstate_pxa2xx_i2c_slave, PXA2xxI2CSlaveState),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1470,7 +1472,7 @@ static void pxa2xx_i2c_slave_class_init(ObjectClass *klass, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo pxa2xx_i2c_slave_info = {
|
static const TypeInfo pxa2xx_i2c_slave_info = {
|
||||||
.name = "pxa2xx-i2c-slave",
|
.name = TYPE_PXA2XX_I2C_SLAVE,
|
||||||
.parent = TYPE_I2C_SLAVE,
|
.parent = TYPE_I2C_SLAVE,
|
||||||
.instance_size = sizeof(PXA2xxI2CSlaveState),
|
.instance_size = sizeof(PXA2xxI2CSlaveState),
|
||||||
.class_init = pxa2xx_i2c_slave_class_init,
|
.class_init = pxa2xx_i2c_slave_class_init,
|
||||||
@@ -1482,7 +1484,7 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base,
|
|||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
SysBusDevice *i2c_dev;
|
SysBusDevice *i2c_dev;
|
||||||
PXA2xxI2CState *s;
|
PXA2xxI2CState *s;
|
||||||
i2c_bus *i2cbus;
|
I2CBus *i2cbus;
|
||||||
|
|
||||||
dev = qdev_create(NULL, TYPE_PXA2XX_I2C);
|
dev = qdev_create(NULL, TYPE_PXA2XX_I2C);
|
||||||
qdev_prop_set_uint32(dev, "size", region_size + 1);
|
qdev_prop_set_uint32(dev, "size", region_size + 1);
|
||||||
@@ -1496,8 +1498,8 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base,
|
|||||||
s = PXA2XX_I2C(i2c_dev);
|
s = PXA2XX_I2C(i2c_dev);
|
||||||
/* FIXME: Should the slave device really be on a separate bus? */
|
/* FIXME: Should the slave device really be on a separate bus? */
|
||||||
i2cbus = i2c_init_bus(dev, "dummy");
|
i2cbus = i2c_init_bus(dev, "dummy");
|
||||||
dev = i2c_create_slave(i2cbus, "pxa2xx-i2c-slave", 0);
|
dev = i2c_create_slave(i2cbus, TYPE_PXA2XX_I2C_SLAVE, 0);
|
||||||
s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE(dev));
|
s->slave = PXA2XX_I2C_SLAVE(dev);
|
||||||
s->slave->host = s;
|
s->slave->host = s;
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@@ -1518,7 +1520,7 @@ static int pxa2xx_i2c_initfn(SysBusDevice *sbd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_bus *pxa2xx_i2c_bus(PXA2xxI2CState *s)
|
I2CBus *pxa2xx_i2c_bus(PXA2xxI2CState *s)
|
||||||
{
|
{
|
||||||
return s->bus;
|
return s->bus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,20 +217,17 @@ static const int pxa2xx_cp_reg_map[0x10] = {
|
|||||||
[0xa] = ICPR2,
|
[0xa] = ICPR2,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int pxa2xx_pic_cp_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
static uint64_t pxa2xx_pic_cp_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
uint64_t *value)
|
|
||||||
{
|
{
|
||||||
int offset = pxa2xx_cp_reg_map[ri->crn];
|
int offset = pxa2xx_cp_reg_map[ri->crn];
|
||||||
*value = pxa2xx_pic_mem_read(ri->opaque, offset, 4);
|
return pxa2xx_pic_mem_read(ri->opaque, offset, 4);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
static void pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
uint64_t value)
|
uint64_t value)
|
||||||
{
|
{
|
||||||
int offset = pxa2xx_cp_reg_map[ri->crn];
|
int offset = pxa2xx_cp_reg_map[ri->crn];
|
||||||
pxa2xx_pic_mem_write(ri->opaque, offset, value, 4);
|
pxa2xx_pic_mem_write(ri->opaque, offset, value, 4);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define REGINFO_FOR_PIC_CP(NAME, CRN) \
|
#define REGINFO_FOR_PIC_CP(NAME, CRN) \
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user