Compare commits
2393
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efb602cda3 | ||
|
|
26a16623b8 | ||
|
|
d8ecc0b913 | ||
|
|
19c80e50ee | ||
|
|
a0f51eadc6 | ||
|
|
b6aaab3b86 | ||
|
|
df517cec51 | ||
|
|
2afbeee757 | ||
|
|
7b9c30c5ee | ||
|
|
c636bb66cc | ||
|
|
84f2e8ef05 | ||
|
|
a9ce859052 | ||
|
|
564c337efd | ||
|
|
2a2528266e | ||
|
|
263466f53a | ||
|
|
1e2bed4f7e | ||
|
|
831952376e | ||
|
|
4fe8b87a7c | ||
|
|
1057eaa709 | ||
|
|
d96372efaa | ||
|
|
fa2948167f | ||
|
|
b49d07ba03 | ||
|
|
33ef55bd96 | ||
|
|
3fffc2234f | ||
|
|
4a109bfbca | ||
|
|
01d6a890b4 | ||
|
|
6a1cbf68b7 | ||
|
|
c4e27dd441 | ||
|
|
d80c7d1c47 | ||
|
|
0499e4a02a | ||
|
|
0dcece76f0 | ||
|
|
01feaa058e | ||
|
|
bf53ca0190 | ||
|
|
5a7b542bbd | ||
|
|
fd6776422b | ||
|
|
46ea33974d | ||
|
|
90e950d17f | ||
|
|
3ddd00658e | ||
|
|
a2d1ebaf89 | ||
|
|
4046d9130e | ||
|
|
f749998939 | ||
|
|
e5b0bc445e | ||
|
|
dff5efc848 | ||
|
|
64423fb206 | ||
|
|
6c3675cac3 | ||
|
|
71c2fd5cdf | ||
|
|
df5cf72160 | ||
|
|
44cbbf18b9 | ||
|
|
6070dd07e9 | ||
|
|
e6a71ae327 | ||
|
|
5f4da8c0f3 | ||
|
|
b29a0341d7 | ||
|
|
4de9b249d3 | ||
|
|
30c4bbace1 | ||
|
|
9c2149c8e0 | ||
|
|
17c275d9ce | ||
|
|
81e1e20979 | ||
|
|
3b1c8be4f4 | ||
|
|
c281868e26 | ||
|
|
3c07f8e894 | ||
|
|
72f341ed1e | ||
|
|
bc814401c2 | ||
|
|
8e71621f78 | ||
|
|
57be54bb3e | ||
|
|
5f12ab4b10 | ||
|
|
af23902bd9 | ||
|
|
472c5273e2 | ||
|
|
15dcf5aa86 | ||
|
|
7f1c9da998 | ||
|
|
284ec167e7 | ||
|
|
831b78254c | ||
|
|
54421cb17b | ||
|
|
0da75eb135 | ||
|
|
605686cd7a | ||
|
|
8182266352 | ||
|
|
54fd9cdfb4 | ||
|
|
9dbd25e664 | ||
|
|
2c52c8169a | ||
|
|
f1770b3e1f | ||
|
|
72742c8626 | ||
|
|
adb4796796 | ||
|
|
dabd98ddf5 | ||
|
|
daf90626be | ||
|
|
5856de800d | ||
|
|
fde7d5bd73 | ||
|
|
5c2b87e34d | ||
|
|
567daa491f | ||
|
|
8f1c91d801 | ||
|
|
abcebc7e80 | ||
|
|
f158755072 | ||
|
|
bf8c534200 | ||
|
|
0178196398 | ||
|
|
18607dcb7c | ||
|
|
96d30e4801 | ||
|
|
2f67a0d55a | ||
|
|
21664424ed | ||
|
|
3b39528c15 | ||
|
|
3b9f94e1a8 | ||
|
|
f5e25d7007 | ||
|
|
2909b29aee | ||
|
|
f7bcd4e373 | ||
|
|
43c1b7e4c3 | ||
|
|
86e94dea5b | ||
|
|
8f28f3fbbe | ||
|
|
43f238d7b6 | ||
|
|
4dbb0f5006 | ||
|
|
6850dd945e | ||
|
|
b92090309e | ||
|
|
2e9671daee | ||
|
|
eec85c2a0d | ||
|
|
9ae0255520 | ||
|
|
a0ae05aa63 | ||
|
|
455204eb1a | ||
|
|
6f30fa853b | ||
|
|
b371dc594b | ||
|
|
2ee4aed86f | ||
|
|
df628ff14e | ||
|
|
240f24e013 | ||
|
|
0feef828e1 | ||
|
|
3a3f24fce8 | ||
|
|
7495fd0f4a | ||
|
|
f8c6ff6c3c | ||
|
|
04e897efdd | ||
|
|
388c45084d | ||
|
|
fa1fb14cd2 | ||
|
|
1f95844967 | ||
|
|
a14d6c8c65 | ||
|
|
04d4b0c33f | ||
|
|
9042c0e20d | ||
|
|
70ead43412 | ||
|
|
5bf089345b | ||
|
|
eddbd288a0 | ||
|
|
28d657fc0b | ||
|
|
4258b780af | ||
|
|
52f61fdeec | ||
|
|
c26c1c4b3d | ||
|
|
c032e2a98c | ||
|
|
42550fde7e | ||
|
|
62ee021121 | ||
|
|
fef3074347 | ||
|
|
bd491d6a4e | ||
|
|
71e3ceb800 | ||
|
|
73fc97427b | ||
|
|
ffd843bcdc | ||
|
|
1cb6c3fd38 | ||
|
|
eaf7e70b01 | ||
|
|
8bdc2159db | ||
|
|
416b5d366d | ||
|
|
d8b20157fc | ||
|
|
92190c648a | ||
|
|
a86c8f29de | ||
|
|
5dc4b74480 | ||
|
|
c570fd169c | ||
|
|
328a42406d | ||
|
|
67d3b957e6 | ||
|
|
179e32bbcc | ||
|
|
e4630047e1 | ||
|
|
73e14b623f | ||
|
|
768a4a36a4 | ||
|
|
0d3267a728 | ||
|
|
8e33c08c84 | ||
|
|
197ea35ab2 | ||
|
|
366dfc521e | ||
|
|
667accab8e | ||
|
|
a2d4e44b48 | ||
|
|
3bcb80f1af | ||
|
|
a42aa81571 | ||
|
|
ca7c2b1b9f | ||
|
|
7d600c804d | ||
|
|
71fb7241c6 | ||
|
|
9f08349345 | ||
|
|
afdfa781e6 | ||
|
|
cd75cf10ca | ||
|
|
ea55ffb373 | ||
|
|
aa328addc0 | ||
|
|
e144966416 | ||
|
|
bb8a53ad91 | ||
|
|
e16fe40c87 | ||
|
|
7a387fffce | ||
|
|
8c0fdd856c | ||
|
|
873eb01234 | ||
|
|
6ae817752b | ||
|
|
814b9a4749 | ||
|
|
ec2309289d | ||
|
|
29bfb11725 | ||
|
|
08f396d720 | ||
|
|
38d0662a4c | ||
|
|
1be9e1dc56 | ||
|
|
ec2db7de7a | ||
|
|
417f38f066 | ||
|
|
2d37be61e3 | ||
|
|
483dcf538e | ||
|
|
b362e5e067 | ||
|
|
d08b2a28e6 | ||
|
|
597a0559fd | ||
|
|
188157fe87 | ||
|
|
58126404f5 | ||
|
|
569f5d668c | ||
|
|
750afe93fd | ||
|
|
00a67ba19a | ||
|
|
fafffaef4f | ||
|
|
a94a6abff4 | ||
|
|
c688a6eb18 | ||
|
|
27908725b9 | ||
|
|
dd016883fb | ||
|
|
d37aca6625 | ||
|
|
e6e5906b6e | ||
|
|
223b8a40d1 | ||
|
|
1f9519c92d | ||
|
|
b4511723bd | ||
|
|
d1beab8220 | ||
|
|
e3e97e7c90 | ||
|
|
47d02f6d5c | ||
|
|
93eac243d5 | ||
|
|
a7e6f8ba22 | ||
|
|
326199c270 | ||
|
|
8d7b0fbb3f | ||
|
|
5e966ce6f4 | ||
|
|
74ce674fa8 | ||
|
|
8988ae8945 | ||
|
|
69c3bcb48f | ||
|
|
42fc925ec0 | ||
|
|
6f15b608f2 | ||
|
|
17100159f1 | ||
|
|
79266dbd9e | ||
|
|
84631fd79c | ||
|
|
4f3baa4b36 | ||
|
|
f00fc47c15 | ||
|
|
ee0ea1d0dd | ||
|
|
02a1602e62 | ||
|
|
a5954d5c34 | ||
|
|
f537a28c97 | ||
|
|
ab1e34add6 | ||
|
|
3b21e03e04 | ||
|
|
ba86345802 | ||
|
|
d4afc623ae | ||
|
|
80b3ada7dd | ||
|
|
d2b5931756 | ||
|
|
e69954b9fc | ||
|
|
37dd208d38 | ||
|
|
05f83f0f95 | ||
|
|
df52b0009b | ||
|
|
8f40c388ef | ||
|
|
aef445bd7e | ||
|
|
6787f5fae0 | ||
|
|
b8174937ed | ||
|
|
519945dfbe | ||
|
|
707b3ee068 | ||
|
|
78600320a8 | ||
|
|
35db099ddb | ||
|
|
6bcb76c3b6 | ||
|
|
e4a89056df | ||
|
|
715748fa08 | ||
|
|
21206a104f | ||
|
|
094eed6ca5 | ||
|
|
9b94dc325b | ||
|
|
c6d46c200f | ||
|
|
e17a36ce41 | ||
|
|
03c1847584 | ||
|
|
67e999be93 | ||
|
|
91cc029598 | ||
|
|
c03b0f0fd8 | ||
|
|
6192bc374f | ||
|
|
a917d384ac | ||
|
|
508d92d057 | ||
|
|
64f5a135a7 | ||
|
|
a817d93656 | ||
|
|
fd44d81821 | ||
|
|
f45512feac | ||
|
|
6b21b973ab | ||
|
|
2192c33250 | ||
|
|
f542086dd4 | ||
|
|
ee0f47514c | ||
|
|
60659e3b44 | ||
|
|
f0e4217213 | ||
|
|
e6cda8e299 | ||
|
|
b56bdb328a | ||
|
|
a9f277ba8f | ||
|
|
19cb37389f | ||
|
|
66c6ef7678 | ||
|
|
ea185bbda7 | ||
|
|
83acc96b23 | ||
|
|
6e1b3e4da4 | ||
|
|
87b47350ed | ||
|
|
243a273ee0 | ||
|
|
c3d78997a3 | ||
|
|
e6cf6a8c7d | ||
|
|
1941d19c65 | ||
|
|
89b6b50892 | ||
|
|
d2269f6f64 | ||
|
|
dfd92d3a46 | ||
|
|
4c279bdf3a | ||
|
|
3dc1cb3428 | ||
|
|
35f1df84b6 | ||
|
|
69fba4a51e | ||
|
|
4d611c9a2f | ||
|
|
4ca9c76f36 | ||
|
|
2a98c1981f | ||
|
|
35b961cf8d | ||
|
|
8ccad811e6 | ||
|
|
e84a4fedf7 | ||
|
|
fb43f4dddc | ||
|
|
19d36792c0 | ||
|
|
90765429aa | ||
|
|
ce1a14dc0d | ||
|
|
51d6bae7a8 | ||
|
|
13a2e80f04 | ||
|
|
c88676f89c | ||
|
|
d15a771da1 | ||
|
|
a946592212 | ||
|
|
1aacf3489b | ||
|
|
51c1ebb1bc | ||
|
|
6eb5733a3c | ||
|
|
15e6690aca | ||
|
|
c47c33b098 | ||
|
|
e70332b376 | ||
|
|
faea38e786 | ||
|
|
42ca638823 | ||
|
|
9ab8a34a18 | ||
|
|
585f8587ad | ||
|
|
9e46cfa4c6 | ||
|
|
e2f909bef9 | ||
|
|
7f1a8398ab | ||
|
|
e7b81015e5 | ||
|
|
436e15b827 | ||
|
|
550be12730 | ||
|
|
979b67ad86 | ||
|
|
83f6409109 | ||
|
|
7954c73498 | ||
|
|
93b6b2a3cd | ||
|
|
d62ca2bb9b | ||
|
|
4615218210 | ||
|
|
f3a9676a60 | ||
|
|
47dbd1f32d | ||
|
|
d07edbfa00 | ||
|
|
059809e451 | ||
|
|
3b2ccc57c7 | ||
|
|
74ccb34e6b | ||
|
|
5ef54116ea | ||
|
|
725cb90bf7 | ||
|
|
a3c259974e | ||
|
|
69db0ac75a | ||
|
|
ec36b695b0 | ||
|
|
6330126439 | ||
|
|
634fce96eb | ||
|
|
13224a87fb | ||
|
|
7ba1260ac1 | ||
|
|
bd468840d4 | ||
|
|
3c656346c9 | ||
|
|
29b9a3456f | ||
|
|
a8e5ac33d2 | ||
|
|
ec607da7c2 | ||
|
|
1dce7c3c22 | ||
|
|
effedbc915 | ||
|
|
9f909fefd5 | ||
|
|
718da2b9b0 | ||
|
|
0de6bb73fd | ||
|
|
3d7374c5da | ||
|
|
4f2092904d | ||
|
|
d929eba5d4 | ||
|
|
219fb12503 | ||
|
|
8ead62cfc2 | ||
|
|
feea13e186 | ||
|
|
6c270db741 | ||
|
|
47378eb1ca | ||
|
|
e3c2613f91 | ||
|
|
6cadb320c7 | ||
|
|
856074ec70 | ||
|
|
99ba31b4fe | ||
|
|
0510224e89 | ||
|
|
106ec87921 | ||
|
|
951f13516a | ||
|
|
1f6e24e73c | ||
|
|
3e382bc84c | ||
|
|
beac80cd43 | ||
|
|
3587d7e69c | ||
|
|
d796321b6b | ||
|
|
567d4107a6 | ||
|
|
a80dde0837 | ||
|
|
29133e9a0f | ||
|
|
9d42037b1d | ||
|
|
2a4188a38f | ||
|
|
4f4fbf77ad | ||
|
|
26a76461f2 | ||
|
|
3b42c9794c | ||
|
|
755d13753b | ||
|
|
26489844dc | ||
|
|
87ac54273f | ||
|
|
a18e524af0 | ||
|
|
e15d737181 | ||
|
|
be995c2764 | ||
|
|
6b1575b746 | ||
|
|
9d9754a31d | ||
|
|
1579bde84d | ||
|
|
0bab00f30f | ||
|
|
3532fa7402 | ||
|
|
480c1cdb39 | ||
|
|
80e7d52103 | ||
|
|
13846e700f | ||
|
|
3cb0853ad7 | ||
|
|
1bdb68ea13 | ||
|
|
65ce8c2fb4 | ||
|
|
ee6c0b51e9 | ||
|
|
48dc41eb8b | ||
|
|
a891c7a194 | ||
|
|
b2a8e59224 | ||
|
|
447c2cefcb | ||
|
|
75956cf032 | ||
|
|
ded3ab80dd | ||
|
|
908f52b05c | ||
|
|
9c2a9ea1b1 | ||
|
|
397e923f7f | ||
|
|
4dbed8972b | ||
|
|
355fb23d83 | ||
|
|
9854bc4662 | ||
|
|
978efd6aac | ||
|
|
e6de1bad46 | ||
|
|
30a604f363 | ||
|
|
856860f5e3 | ||
|
|
ce05c32384 | ||
|
|
1ce549abf9 | ||
|
|
a08beb33cd | ||
|
|
fdbb46910a | ||
|
|
43057ab127 | ||
|
|
c5d6edc3fc | ||
|
|
52ca8d6af0 | ||
|
|
5cbfcd00b0 | ||
|
|
ec3757de32 | ||
|
|
e8445331b6 | ||
|
|
8f447cc753 | ||
|
|
eda9b09b1d | ||
|
|
191f9a93f4 | ||
|
|
d1e42c5c1e | ||
|
|
4bb3973f61 | ||
|
|
bc1ad2decd | ||
|
|
83fcb51548 | ||
|
|
5b9053a5ea | ||
|
|
6ea83fedc8 | ||
|
|
180b700dc7 | ||
|
|
55e4f6644e | ||
|
|
d8e3326c8e | ||
|
|
0986ac3be2 | ||
|
|
9d0869b630 | ||
|
|
8fa00e0fec | ||
|
|
8454df8b1e | ||
|
|
99589bdcd1 | ||
|
|
74a14f22b8 | ||
|
|
c66b0d4cf4 | ||
|
|
8785a8ddcc | ||
|
|
79737e4a7d | ||
|
|
e5fe0c5230 | ||
|
|
ac62f715c6 | ||
|
|
7a6cba611d | ||
|
|
17acfe326c | ||
|
|
9f149aa9c1 | ||
|
|
7d8406be69 | ||
|
|
0fc5c15a4f | ||
|
|
cac782d496 | ||
|
|
4be9a500e7 | ||
|
|
7c22dd5216 | ||
|
|
0aff66b5c8 | ||
|
|
2e5d83bbef | ||
|
|
e6f3e5e016 | ||
|
|
159f366388 | ||
|
|
ba9a74dae0 | ||
|
|
3d9fb9fefe | ||
|
|
42fe404458 | ||
|
|
9d05095e5f | ||
|
|
ea4e754f5a | ||
|
|
cae41b10a4 | ||
|
|
6106487019 | ||
|
|
6cb7ee859a | ||
|
|
e4d165c24b | ||
|
|
0d92ed3022 | ||
|
|
6650ee6d33 | ||
|
|
db59203d10 | ||
|
|
1c46d7139a | ||
|
|
f815fa45da | ||
|
|
3512779a88 | ||
|
|
5c3ff3a7be | ||
|
|
0cb3fb1e30 | ||
|
|
c59372208a | ||
|
|
00a9bf191b | ||
|
|
b9dea4fbc6 | ||
|
|
214feb514b | ||
|
|
502a53952d | ||
|
|
4aa4253115 | ||
|
|
5627148a19 | ||
|
|
1fc2244d87 | ||
|
|
d3079cd241 | ||
|
|
38f3e7c2f5 | ||
|
|
73540ca962 | ||
|
|
064aae138b | ||
|
|
699e4642a9 | ||
|
|
64866c3d5c | ||
|
|
294e863721 | ||
|
|
67f3656039 | ||
|
|
7ef4da1c3a | ||
|
|
6583391f0d | ||
|
|
6515b20370 | ||
|
|
107654552c | ||
|
|
ceb5caaf18 | ||
|
|
c904d61f78 | ||
|
|
215cf0bed8 | ||
|
|
691fce48a3 | ||
|
|
8dbca8dd8a | ||
|
|
68cae3d8c1 | ||
|
|
c91fde65f4 | ||
|
|
f5d2a381d1 | ||
|
|
e553272d75 | ||
|
|
3aee288bc8 | ||
|
|
bdbd7676fd | ||
|
|
38cfa06cbd | ||
|
|
e035649ea3 | ||
|
|
29e3055c37 | ||
|
|
06d9f2f7d4 | ||
|
|
eade0f192e | ||
|
|
07de1eaa9d | ||
|
|
4f552e3b9a | ||
|
|
773f2cdd3c | ||
|
|
fedc54adaa | ||
|
|
4b6ccfdec9 | ||
|
|
7d510b8c0c | ||
|
|
38954dca9f | ||
|
|
6ca957f08f | ||
|
|
f354832878 | ||
|
|
45a8f3ca06 | ||
|
|
debc70650a | ||
|
|
985d1742db | ||
|
|
3f423c9c8f | ||
|
|
7f881e5674 | ||
|
|
24236869fb | ||
|
|
a46e4035e2 | ||
|
|
4dbe19e181 | ||
|
|
a1e7547389 | ||
|
|
7918bf476b | ||
|
|
1640695026 | ||
|
|
008a8818d9 | ||
|
|
27c7ca7e77 | ||
|
|
fdf9b3e831 | ||
|
|
66a93e0f47 | ||
|
|
9ee3c02942 | ||
|
|
94ac515889 | ||
|
|
3598ecb620 | ||
|
|
a8ca632cc0 | ||
|
|
ec530c81ef | ||
|
|
96b74a0221 | ||
|
|
c2ff060fd4 | ||
|
|
467d409f7e | ||
|
|
fd06c37550 | ||
|
|
52328140e2 | ||
|
|
135e73c5f7 | ||
|
|
72899afc5d | ||
|
|
56bebe70bd | ||
|
|
fd4a43e4e2 | ||
|
|
ad1a5b7853 | ||
|
|
6c3ee14ff3 | ||
|
|
ba6526df38 | ||
|
|
1b2b0af50d | ||
|
|
e774a278d8 | ||
|
|
fa7cf687ac | ||
|
|
465e983875 | ||
|
|
b854608e0c | ||
|
|
f4e15b4b4b | ||
|
|
bbeb7b5cbd | ||
|
|
f9ebe432db | ||
|
|
cc8ae6de58 | ||
|
|
d0ecd2aaf9 | ||
|
|
b37837317f | ||
|
|
5fe141fd30 | ||
|
|
ce2f4b3cb9 | ||
|
|
cd7dd10f09 | ||
|
|
76e050c2e6 | ||
|
|
da2414e933 | ||
|
|
a7350fa109 | ||
|
|
132ea32fae | ||
|
|
d7ce493a38 | ||
|
|
6a8826434f | ||
|
|
a03a60532a | ||
|
|
9c03850684 | ||
|
|
99773bd4b4 | ||
|
|
2483668940 | ||
|
|
d4b8f0396a | ||
|
|
8606e5b450 | ||
|
|
b1a550a0da | ||
|
|
0f8134bfd6 | ||
|
|
ad06484063 | ||
|
|
115defd163 | ||
|
|
ffcdb539de | ||
|
|
210fe0be2a | ||
|
|
09b26c5ec0 | ||
|
|
6a15fd12ca | ||
|
|
3e749fe1f7 | ||
|
|
f331110f35 | ||
|
|
1236cab73d | ||
|
|
358bf29e80 | ||
|
|
cdbdb648b7 | ||
|
|
95219897ff | ||
|
|
07435f7462 | ||
|
|
e3f4e2a4b0 | ||
|
|
706cd4b547 | ||
|
|
c2f07f81a2 | ||
|
|
af5db58e8b | ||
|
|
7783e9f002 | ||
|
|
33698e5ffc | ||
|
|
894244f6ca | ||
|
|
1298fe6316 | ||
|
|
307b0c24de | ||
|
|
61b9415691 | ||
|
|
89ba1a738e | ||
|
|
53a5960aad | ||
|
|
26f69dc09f | ||
|
|
cad25d69ad | ||
|
|
0a8e90f401 | ||
|
|
8637c67fc5 | ||
|
|
d80cfb3f70 | ||
|
|
19b045dec9 | ||
|
|
b55669bf57 | ||
|
|
f5ba07d399 | ||
|
|
ce5c37c2a4 | ||
|
|
5b31187812 | ||
|
|
38ca0f6dee | ||
|
|
09c56b842e | ||
|
|
ecd78a0ac7 | ||
|
|
4e9aec746e | ||
|
|
56b194039e | ||
|
|
98c1b82b6c | ||
|
|
6d6f7c288d | ||
|
|
d2ec1774eb | ||
|
|
38260998a2 | ||
|
|
647c593038 | ||
|
|
9540a78b90 | ||
|
|
e10c2bfb73 | ||
|
|
023e9351d0 | ||
|
|
ed96ca3571 | ||
|
|
40f137e1ea | ||
|
|
4081fccf14 | ||
|
|
be9d365723 | ||
|
|
242011157f | ||
|
|
2ae23e7504 | ||
|
|
3b7f5d479c | ||
|
|
1247c5f7be | ||
|
|
e0b3073f53 | ||
|
|
29517134c6 | ||
|
|
ce4defa062 | ||
|
|
b88a38324b | ||
|
|
89bfc105d0 | ||
|
|
f32fc64851 | ||
|
|
f1c85677fc | ||
|
|
5f1ce9487c | ||
|
|
05c2a3e731 | ||
|
|
f94f5d717c | ||
|
|
3aa22b4b53 | ||
|
|
af2f67333f | ||
|
|
bdd5003ae5 | ||
|
|
a41b2ff2dd | ||
|
|
d861b05ea3 | ||
|
|
191abaa2f0 | ||
|
|
3442e8964e | ||
|
|
e89f07d384 | ||
|
|
06c949e62a | ||
|
|
0240ded8bb | ||
|
|
0fd14b72ac | ||
|
|
7fb843f8cc | ||
|
|
9445880298 | ||
|
|
8147cfca56 | ||
|
|
28a5c9c8b2 | ||
|
|
acff9df6a8 | ||
|
|
039af320d9 | ||
|
|
fd1dff4b41 | ||
|
|
ff3fbb307d | ||
|
|
90dc3b395f | ||
|
|
85b2c68832 | ||
|
|
48c2f068e4 | ||
|
|
1538800276 | ||
|
|
6a36d84e10 | ||
|
|
3f9f3aa1ca | ||
|
|
31febb71f4 | ||
|
|
2d7a3b9d7b | ||
|
|
7c206a754a | ||
|
|
2efc32658e | ||
|
|
91fc211974 | ||
|
|
2c6cadd49e | ||
|
|
a046433a16 | ||
|
|
95389c8681 | ||
|
|
1658b44bf5 | ||
|
|
183b4a3806 | ||
|
|
5198cfd927 | ||
|
|
68998c5de3 | ||
|
|
6d7e63262c | ||
|
|
3d830459b1 | ||
|
|
87022ff52b | ||
|
|
cd072e01d8 | ||
|
|
d3e9db933f | ||
|
|
01dbbdf1e5 | ||
|
|
76b3030c56 | ||
|
|
265d349776 | ||
|
|
dbf2c23a60 | ||
|
|
56902eee82 | ||
|
|
9f25f11fe5 | ||
|
|
909a8762ee | ||
|
|
c20eb47362 | ||
|
|
01f5e596ed | ||
|
|
ea31eb5b0c | ||
|
|
cc4adeef98 | ||
|
|
6900e84b20 | ||
|
|
ba3c64fb47 | ||
|
|
b9788fc4c4 | ||
|
|
227671c93b | ||
|
|
f881a0d4f6 | ||
|
|
4ad40f366f | ||
|
|
6810e15490 | ||
|
|
a64d4718f1 | ||
|
|
2d7272a588 | ||
|
|
30d6cb8479 | ||
|
|
6f970bd90e | ||
|
|
89984cd2e5 | ||
|
|
ee0971849e | ||
|
|
80337b66a8 | ||
|
|
54ca9095f0 | ||
|
|
56c8f68f1d | ||
|
|
c960bde13c | ||
|
|
148f50581b | ||
|
|
4b4f782c78 | ||
|
|
84b7b8e778 | ||
|
|
5cf3839607 | ||
|
|
5732fd2779 | ||
|
|
649ea05a2c | ||
|
|
7664728bdf | ||
|
|
50443c98e4 | ||
|
|
6f5a9f7e56 | ||
|
|
4a38940da0 | ||
|
|
048f6b4df7 | ||
|
|
eeef26cd42 | ||
|
|
cc9442b9fc | ||
|
|
15338fd765 | ||
|
|
79639d423f | ||
|
|
9332f9dafa | ||
|
|
e8ebb8a8d7 | ||
|
|
b5ff1b3127 | ||
|
|
0e43e99c04 | ||
|
|
98699967b8 | ||
|
|
daa579632d | ||
|
|
e80e1cc4b1 | ||
|
|
f24e5695e5 | ||
|
|
7668a27f1d | ||
|
|
e5d13e2f64 | ||
|
|
2023a2c836 | ||
|
|
5a1e3cfcb0 | ||
|
|
d2ac63e03e | ||
|
|
ad49ff9de3 | ||
|
|
15a7644956 | ||
|
|
8dd69b8f2c | ||
|
|
089af99118 | ||
|
|
3476562d36 | ||
|
|
59b8ad81c4 | ||
|
|
c68ea7043f | ||
|
|
173d6cfe51 | ||
|
|
0e1fd3694e | ||
|
|
e0fd87812f | ||
|
|
6a00d60127 | ||
|
|
f0aca8227f | ||
|
|
e59c11393b | ||
|
|
32d448c470 | ||
|
|
571ec3d68d | ||
|
|
5e941d4b51 | ||
|
|
509035303d | ||
|
|
546754dc1d | ||
|
|
8a40a180d3 | ||
|
|
313adae905 | ||
|
|
a316d3353c | ||
|
|
6e256c935c | ||
|
|
c1942362bc | ||
|
|
0b7a4a9711 | ||
|
|
2df3b95dbb | ||
|
|
5e9ab4c493 | ||
|
|
7e89463d4a | ||
|
|
41d03949e1 | ||
|
|
7c9d8e07e1 | ||
|
|
868bfe2b2b | ||
|
|
9e61bde56a | ||
|
|
4787c71d17 | ||
|
|
541e084426 | ||
|
|
e7cad33853 | ||
|
|
575b5dc4dc | ||
|
|
b41cffbeb4 | ||
|
|
0bd4885002 | ||
|
|
946fc94733 | ||
|
|
a0d01ed9ea | ||
|
|
e57a8c0eef | ||
|
|
2122c51a9c | ||
|
|
f8d179e33d | ||
|
|
3f87bf6959 | ||
|
|
2531fc7bc0 | ||
|
|
3dbbdc2555 | ||
|
|
48024e4a48 | ||
|
|
b389dbfb58 | ||
|
|
a594cfbf3e | ||
|
|
8738a8d079 | ||
|
|
c0fe3827ea | ||
|
|
f04308e452 | ||
|
|
59ae540c3d | ||
|
|
92414fdca0 | ||
|
|
bb36d4708b | ||
|
|
1aff381f59 | ||
|
|
9903da21e3 | ||
|
|
c53be33474 | ||
|
|
d5d11eac6c | ||
|
|
24741ef3de | ||
|
|
74c33bed31 | ||
|
|
afce2927aa | ||
|
|
bd6ea3c8f3 | ||
|
|
02aab46a36 | ||
|
|
aab3309407 | ||
|
|
05f3fb8de3 | ||
|
|
6f5f11a5bc | ||
|
|
1d14ffa97e | ||
|
|
3b0d4f61c9 | ||
|
|
87f48e6a1a | ||
|
|
b3ecf620de | ||
|
|
a9049a07bb | ||
|
|
bb3911a609 | ||
|
|
4e3b1ea1b8 | ||
|
|
4f6200f03b | ||
|
|
aea3ce4c8d | ||
|
|
f69a86955e | ||
|
|
7b936c0c42 | ||
|
|
819385c58b | ||
|
|
48b2c19353 | ||
|
|
1983a3956c | ||
|
|
c0b24a1dd6 | ||
|
|
de75815006 | ||
|
|
7fa98e2a7a | ||
|
|
8289336c98 | ||
|
|
a332e112b7 | ||
|
|
ca0d1734b4 | ||
|
|
aa0bc6b68c | ||
|
|
1c213d1976 | ||
|
|
a7c15abbb1 | ||
|
|
df5f895699 | ||
|
|
fc8dc06020 | ||
|
|
f23db1692b | ||
|
|
3f20e1ddf2 | ||
|
|
75913b727e | ||
|
|
ecada8a2dd | ||
|
|
1e8a7cfd11 | ||
|
|
0bccf03d6f | ||
|
|
89353a790b | ||
|
|
7ebab69910 | ||
|
|
697584ab24 | ||
|
|
c96a29cdef | ||
|
|
4b7df22f91 | ||
|
|
2c8e030185 | ||
|
|
81eea5ebb6 | ||
|
|
5e6ad6f90e | ||
|
|
aa06297340 | ||
|
|
3a7d929e62 | ||
|
|
04c504cc4f | ||
|
|
ff7b8f5b0f | ||
|
|
6688bc6d04 | ||
|
|
bc3fc8dac0 | ||
|
|
1f3358c87d | ||
|
|
92510b8cf5 | ||
|
|
6cc721cf42 | ||
|
|
e99f906055 | ||
|
|
f5a8510c7c | ||
|
|
93856aac7b | ||
|
|
5cedb46460 | ||
|
|
0f4c64157f | ||
|
|
90cb949352 | ||
|
|
db6e6ed77e | ||
|
|
57e4c06ed7 | ||
|
|
09d459a1db | ||
|
|
108c49b8a2 | ||
|
|
90f18422d9 | ||
|
|
9529397248 | ||
|
|
a2458627f9 | ||
|
|
b1fc0348b1 | ||
|
|
45bbbb466c | ||
|
|
d592d3033d | ||
|
|
1ff5c1a68e | ||
|
|
e5d80f94c5 | ||
|
|
8f091a5960 | ||
|
|
2efbe911d3 | ||
|
|
667f38b167 | ||
|
|
8346901560 | ||
|
|
b7c7b18129 | ||
|
|
9835236910 | ||
|
|
e573335624 | ||
|
|
4e588a4d0e | ||
|
|
a368741bf2 | ||
|
|
61271e5c2d | ||
|
|
aefce9af41 | ||
|
|
ee5bbe38b1 | ||
|
|
e37e863f5e | ||
|
|
fdabc366bd | ||
|
|
2157fa0682 | ||
|
|
d24b15a8d8 | ||
|
|
9d0a8e6f8f | ||
|
|
51a36cb2cb | ||
|
|
33d084399c | ||
|
|
e0727e17f3 | ||
|
|
97067eb5bc | ||
|
|
4157a66212 | ||
|
|
0289b2c1df | ||
|
|
d5295253b0 | ||
|
|
fb3444b86c | ||
|
|
2be0071f22 | ||
|
|
f68c781c2d | ||
|
|
fa296b0fb4 | ||
|
|
3fc6c082e3 | ||
|
|
2f636b458f | ||
|
|
3de388f676 | ||
|
|
73133662c6 | ||
|
|
bf82d81801 | ||
|
|
b195775fef | ||
|
|
1b351e5291 | ||
|
|
d325856010 | ||
|
|
0d8aca8c67 | ||
|
|
8549850891 | ||
|
|
7a962d3087 | ||
|
|
e1d9a50836 | ||
|
|
568b600d85 | ||
|
|
bc2c390907 | ||
|
|
9827e95c78 | ||
|
|
51e11d9e6c | ||
|
|
90b37806ba | ||
|
|
0699b54839 | ||
|
|
9d1d106a3d | ||
|
|
ae022501f2 | ||
|
|
899abcf513 | ||
|
|
dfae6487c0 | ||
|
|
de12d6369b | ||
|
|
bc9ed47b12 | ||
|
|
9fb63ac281 | ||
|
|
6af0bf9c7c | ||
|
|
6643d27ea0 | ||
|
|
3475187dd8 | ||
|
|
8979b2277d | ||
|
|
97ccc689e6 | ||
|
|
c98baaac2f | ||
|
|
101c593562 | ||
|
|
b685369795 | ||
|
|
3f1a88f450 | ||
|
|
a84eaf0c9b | ||
|
|
6e20a45f53 | ||
|
|
cadae95f33 | ||
|
|
e68b9b2b10 | ||
|
|
c0e564d53b | ||
|
|
384d887691 | ||
|
|
5457c8ceeb | ||
|
|
0aa6a4a250 | ||
|
|
938828a263 | ||
|
|
b5ff2d6e2d | ||
|
|
54fa5af546 | ||
|
|
cc1daa40f1 | ||
|
|
2d61879305 | ||
|
|
7c48011b45 | ||
|
|
8dd4983c4e | ||
|
|
71be0fc3eb | ||
|
|
30aec8768f | ||
|
|
8993433789 | ||
|
|
d094807b9b | ||
|
|
6d506e6dc2 | ||
|
|
43ef9eb267 | ||
|
|
e4cf1adc80 | ||
|
|
72cc6cfeef | ||
|
|
bc380d1719 | ||
|
|
ff8263a951 | ||
|
|
04d81be884 | ||
|
|
2d5262f991 | ||
|
|
a09db21f71 | ||
|
|
b671f9ed2d | ||
|
|
de167e416f | ||
|
|
712e78744e | ||
|
|
7c35359cbf | ||
|
|
d37282add1 | ||
|
|
a343df1659 | ||
|
|
98ff7d30f2 | ||
|
|
43095f3198 | ||
|
|
5899f386ba | ||
|
|
6a0f9e82c5 | ||
|
|
c2d551ff5a | ||
|
|
192c7bd927 | ||
|
|
b48a8bb6b1 | ||
|
|
e5484d3391 | ||
|
|
7674e7bf08 | ||
|
|
c747cd1fa2 | ||
|
|
11650e3638 | ||
|
|
a8753c3466 | ||
|
|
c99280bc29 | ||
|
|
905f20b151 | ||
|
|
ff1afc72f2 | ||
|
|
6d82d04a49 | ||
|
|
ad81218e40 | ||
|
|
e4afee9716 | ||
|
|
d0b3e73525 | ||
|
|
e90096763d | ||
|
|
43fb823b5f | ||
|
|
e50e6a2019 | ||
|
|
430116a1d8 | ||
|
|
1026f1336b | ||
|
|
e362b55a32 | ||
|
|
fc9f715de8 | ||
|
|
b359d4e7e4 | ||
|
|
c45b3c0e1b | ||
|
|
e04f40b5aa | ||
|
|
dff293e751 | ||
|
|
b3180cdc01 | ||
|
|
4162503368 | ||
|
|
6bae7ed8b9 | ||
|
|
74c161bd17 | ||
|
|
08e489025b | ||
|
|
c326e0afec | ||
|
|
8aaca4c0b4 | ||
|
|
a4f81979e8 | ||
|
|
89344d5ad7 | ||
|
|
da9b266bb8 | ||
|
|
dccfafc4e1 | ||
|
|
111bfab3b5 | ||
|
|
c7d344af8f | ||
|
|
e1a2849c90 | ||
|
|
e06e5259c3 | ||
|
|
aba9d61e34 | ||
|
|
a6f379881e | ||
|
|
f419b32104 | ||
|
|
8d9bfc2b48 | ||
|
|
c28e951fc7 | ||
|
|
07f4ddbf7e | ||
|
|
5516d670f6 | ||
|
|
cc6f538bf6 | ||
|
|
1fddef4b1b | ||
|
|
6e4255f6a6 | ||
|
|
74ffc7729e | ||
|
|
e3a4e4b643 | ||
|
|
40545f84cf | ||
|
|
d39c0b990a | ||
|
|
2b03a7a5bc | ||
|
|
b8076a748d | ||
|
|
7a674b1363 | ||
|
|
5a246934eb | ||
|
|
b7e2c11dbd | ||
|
|
9d60cac01f | ||
|
|
8e96005d86 | ||
|
|
85d8be6bf2 | ||
|
|
c194feda5f | ||
|
|
7e6c3f34bf | ||
|
|
39d2243955 | ||
|
|
66321a11a4 | ||
|
|
c44644bb96 | ||
|
|
ed91024191 | ||
|
|
b81b3b10aa | ||
|
|
8be1f5c889 | ||
|
|
d827220bbf | ||
|
|
2f275b8f9f | ||
|
|
c3278b7bf0 | ||
|
|
86bd2ca58a | ||
|
|
8422b11337 | ||
|
|
b7a100da9c | ||
|
|
b109f9f867 | ||
|
|
1d6bda3561 | ||
|
|
53cd663792 | ||
|
|
7a0e1f41ce | ||
|
|
4ecc31906d | ||
|
|
4c2e770f37 | ||
|
|
2049521883 | ||
|
|
158142c2c2 | ||
|
|
4f716dc681 | ||
|
|
539827ecf5 | ||
|
|
6eea2b1b81 | ||
|
|
ae200d1062 | ||
|
|
ba68055eab | ||
|
|
6f7e9aec5e | ||
|
|
b756921ad1 | ||
|
|
313132138a | ||
|
|
d057099aa8 | ||
|
|
da4dbf742d | ||
|
|
776f2227f6 | ||
|
|
d785e6be4d | ||
|
|
5e83e8e3e7 | ||
|
|
24b55b9650 | ||
|
|
232ba5ab2e | ||
|
|
b6f479d355 | ||
|
|
1289f43ab1 | ||
|
|
76472292e4 | ||
|
|
5b0753e0d8 | ||
|
|
157777ef3e | ||
|
|
f94daedd27 | ||
|
|
6df700c206 | ||
|
|
b7bcbe9524 | ||
|
|
55754d9ef2 | ||
|
|
afc7df1148 | ||
|
|
713c45faf8 | ||
|
|
0b9dc5e4c3 | ||
|
|
194884dd6f | ||
|
|
91aa5d4975 | ||
|
|
6f2f2b2489 | ||
|
|
1d6e34fd37 | ||
|
|
2623cbaf1a | ||
|
|
a20b552469 | ||
|
|
f512c6fb6e | ||
|
|
3cc6237083 | ||
|
|
c4decf377c | ||
|
|
3988e8977b | ||
|
|
7483750d7d | ||
|
|
61ff6f58e9 | ||
|
|
0bee699e1d | ||
|
|
878d3096d2 | ||
|
|
1a0c3292b5 | ||
|
|
824d560f09 | ||
|
|
580a5e27c6 | ||
|
|
0443eaf6ae | ||
|
|
9117a4ab91 | ||
|
|
441c72ba15 | ||
|
|
bf079a1e70 | ||
|
|
9df217a317 | ||
|
|
92a31b1fff | ||
|
|
0a962c0276 | ||
|
|
d993e0260b | ||
|
|
49b470eb96 | ||
|
|
c9ec1fe474 | ||
|
|
e3086fbf8f | ||
|
|
7c3fc84d86 | ||
|
|
d7ce296f57 | ||
|
|
f8407028b4 | ||
|
|
18fba28c95 | ||
|
|
68016c627b | ||
|
|
9d89330183 | ||
|
|
b8a9e8f133 | ||
|
|
4955a2cd16 | ||
|
|
a8d3431ae9 | ||
|
|
7ff4d2180b | ||
|
|
e88de09993 | ||
|
|
832ed0fa34 | ||
|
|
78573df6b2 | ||
|
|
9f0777ed88 | ||
|
|
6bae70713c | ||
|
|
90f11f95fe | ||
|
|
fa15e030bf | ||
|
|
1ef3868708 | ||
|
|
99c475abf1 | ||
|
|
dfe86665b8 | ||
|
|
a315a14547 | ||
|
|
4fa5d7722d | ||
|
|
64b3ab2439 | ||
|
|
c1135f6152 | ||
|
|
3a5c313852 | ||
|
|
af7bf89b1f | ||
|
|
49be803015 | ||
|
|
8df1cd076c | ||
|
|
bb05683b12 | ||
|
|
662f3c86ec | ||
|
|
5416376efe | ||
|
|
f34c9d6f10 | ||
|
|
e3db7226b4 | ||
|
|
d79284e07a | ||
|
|
9191d4d19f | ||
|
|
0b74ed78ef | ||
|
|
f51589dad5 | ||
|
|
82e41634cd | ||
|
|
bd3fae3df6 | ||
|
|
9230e66e5c | ||
|
|
0523c6b7c5 | ||
|
|
39c61f49f4 | ||
|
|
4d6b6c0aec | ||
|
|
79f91c27ba | ||
|
|
dc9543dc22 | ||
|
|
bef79c34a2 | ||
|
|
83b34f8b57 | ||
|
|
18a6d284ad | ||
|
|
ada89ce61b | ||
|
|
b328f873ab | ||
|
|
ca954f6d90 | ||
|
|
97ed14aead | ||
|
|
d52cf7a64a | ||
|
|
bb2d531499 | ||
|
|
c82913f742 | ||
|
|
6508fe59e0 | ||
|
|
e5843bc816 | ||
|
|
d3c617219b | ||
|
|
735a8fd38e | ||
|
|
1bde465e06 | ||
|
|
a4682cc20a | ||
|
|
839fa98885 | ||
|
|
7c2e623559 | ||
|
|
977d5710e6 | ||
|
|
34444131ad | ||
|
|
8adbc566c9 | ||
|
|
5327cf489f | ||
|
|
e995898b06 | ||
|
|
9df8aa4abb | ||
|
|
ae063a68dc | ||
|
|
8636b5d873 | ||
|
|
664e0f195a | ||
|
|
085339a12b | ||
|
|
abd2c7dc9c | ||
|
|
a8ede8ba8b | ||
|
|
826461bb40 | ||
|
|
02536f8b1f | ||
|
|
06c2f5066e | ||
|
|
bdfaf503dc | ||
|
|
14ce26e755 | ||
|
|
c46878786a | ||
|
|
0fa85d43d4 | ||
|
|
b4ff598727 | ||
|
|
526ff7de82 | ||
|
|
995179f1cc | ||
|
|
0b0babc623 | ||
|
|
20f3228237 | ||
|
|
c27004ec78 | ||
|
|
612458f544 | ||
|
|
80a9d03503 | ||
|
|
75598f6131 | ||
|
|
4f7631cfb5 | ||
|
|
62a46c6168 | ||
|
|
574bbf7b0d | ||
|
|
acae4681ae | ||
|
|
42ad6ae973 | ||
|
|
808c4954bf | ||
|
|
e80cfcfc88 | ||
|
|
9772c73bbc | ||
|
|
de06c511ff | ||
|
|
bc7712a4ac | ||
|
|
4ca0074c8c | ||
|
|
fed4a9adc0 | ||
|
|
3d11d0eb33 | ||
|
|
7b91a17212 | ||
|
|
585d0ed98b | ||
|
|
a483b654b5 | ||
|
|
6a78ece5c5 | ||
|
|
1e8d4eec48 | ||
|
|
88920f344d | ||
|
|
f7cce89882 | ||
|
|
fe2cece60e | ||
|
|
978a66ff73 | ||
|
|
f7806f9467 | ||
|
|
45aea85cef | ||
|
|
c451ee717a | ||
|
|
a07167d3d4 | ||
|
|
02cfb0b4f3 | ||
|
|
3bc2175dcc | ||
|
|
bed5cd8048 | ||
|
|
c169c906a3 | ||
|
|
17444c9c84 | ||
|
|
bf1b938fce | ||
|
|
46d4767d93 | ||
|
|
e35c55fe38 | ||
|
|
acd935ef62 | ||
|
|
c9c0eae84e | ||
|
|
c72a345f5b | ||
|
|
102a52e471 | ||
|
|
c76338c34f | ||
|
|
bf2b84e4a7 | ||
|
|
9f059eca52 | ||
|
|
53360e00e2 | ||
|
|
ef6ff6b71e | ||
|
|
546fa6abd1 | ||
|
|
e875c40a15 | ||
|
|
a98d49b136 | ||
|
|
44a095a77c | ||
|
|
15b6147000 | ||
|
|
9e89a4be8e | ||
|
|
61a8c4ec3a | ||
|
|
9746b15b4e | ||
|
|
7372f88dc1 | ||
|
|
d7382233d8 | ||
|
|
fb065187e4 | ||
|
|
bf71c9d9b6 | ||
|
|
7a987127f4 | ||
|
|
e0fe67aa72 | ||
|
|
f6c958c865 | ||
|
|
9bb34eac8b | ||
|
|
85571bc741 | ||
|
|
8f46820d92 | ||
|
|
dbb2c92142 | ||
|
|
0d1a29f9fc | ||
|
|
b8b5ac6376 | ||
|
|
40b6ecc6bc | ||
|
|
f3ff649d3b | ||
|
|
953569d21b | ||
|
|
a0c4cb4a70 | ||
|
|
188d857911 | ||
|
|
9bc9d1c75a | ||
|
|
8926b517e9 | ||
|
|
6d46bf8ae3 | ||
|
|
032a8c9e35 | ||
|
|
a7dfe172fa | ||
|
|
7fe48483cd | ||
|
|
8e3a9fd280 | ||
|
|
d75d9f6be9 | ||
|
|
769bec7271 | ||
|
|
655aa52a90 | ||
|
|
ba6c23778c | ||
|
|
4b19ec0c2b | ||
|
|
e388818682 | ||
|
|
02d2c54cd3 | ||
|
|
890fa6bebb | ||
|
|
a4c4785b93 | ||
|
|
7993f8bc51 | ||
|
|
8d5f07fa3b | ||
|
|
023fcb9507 | ||
|
|
a5ba1ca608 | ||
|
|
b769d8fef6 | ||
|
|
32ff25bf68 | ||
|
|
f98593103b | ||
|
|
30ca2aab8e | ||
|
|
8a8a608f6e | ||
|
|
d63d307f6e | ||
|
|
487be8a1a7 | ||
|
|
2518bd0dc2 | ||
|
|
e95c8d51c2 | ||
|
|
4971b827da | ||
|
|
420557e898 | ||
|
|
6d5e216de9 | ||
|
|
e8af50a30e | ||
|
|
525d67bcc8 | ||
|
|
d981b88344 | ||
|
|
4a4883b84d | ||
|
|
16c460b154 | ||
|
|
0ecf89aae3 | ||
|
|
28d34b8246 | ||
|
|
3c56521b70 | ||
|
|
096b7ea42b | ||
|
|
d5a8f07c52 | ||
|
|
345fbaa3ca | ||
|
|
1d96905d76 | ||
|
|
99679ececc | ||
|
|
eb45f5fec4 | ||
|
|
b86bda5bb1 | ||
|
|
e2731add29 | ||
|
|
c9a621176e | ||
|
|
cf720db33a | ||
|
|
29e619b1e8 | ||
|
|
6f28fb86c9 | ||
|
|
c94c8d6499 | ||
|
|
01038d2a76 | ||
|
|
03ffbb69a8 | ||
|
|
a3d4af03bb | ||
|
|
9d728e8c4e | ||
|
|
36d54d15e1 | ||
|
|
c4dfa5b7be | ||
|
|
75c2380584 | ||
|
|
7c08dbf325 | ||
|
|
9bf05444b2 | ||
|
|
a3504c87ca | ||
|
|
7143c62c95 | ||
|
|
4e8b5da233 | ||
|
|
c7f746434f | ||
|
|
60e336dbb8 | ||
|
|
8d11df9e5a | ||
|
|
05d5818c5c | ||
|
|
cabf23c380 | ||
|
|
e82d8ade13 | ||
|
|
d2bfb39ad2 | ||
|
|
3611a29c09 | ||
|
|
0f6e3eb211 | ||
|
|
57d1a2b62c | ||
|
|
d5249393ef | ||
|
|
11c0315f9b | ||
|
|
a3fb0cf907 | ||
|
|
ea2384d36e | ||
|
|
e4d4fe3c34 | ||
|
|
5905b2e5fd | ||
|
|
7e2515e87c | ||
|
|
3d2cfdf169 | ||
|
|
6fcfafb742 | ||
|
|
af8ffdfd2b | ||
|
|
b932caba32 | ||
|
|
3eb2619fe5 | ||
|
|
3e11db9a0c | ||
|
|
c6f37d0e4f | ||
|
|
a0a821a4c0 | ||
|
|
49b3b9fb1a | ||
|
|
e7f0ad58c1 | ||
|
|
82c643ff50 | ||
|
|
457831f4bc | ||
|
|
2571929a77 | ||
|
|
81d0912d2d | ||
|
|
d1d9f42119 | ||
|
|
450e18b8b8 | ||
|
|
3db38e87a0 | ||
|
|
379ff53dc9 | ||
|
|
ce93da6ffe | ||
|
|
ee2654ac24 | ||
|
|
354ff22657 | ||
|
|
6b65279459 | ||
|
|
12c28fed49 | ||
|
|
38f0b147a5 | ||
|
|
bec9d989db | ||
|
|
cab84d9844 | ||
|
|
1b039c09fe | ||
|
|
fbf59244b8 | ||
|
|
fa36761d7f | ||
|
|
d08c49aae0 | ||
|
|
fcc941fe20 | ||
|
|
96bcd4f884 | ||
|
|
e3371e62f3 | ||
|
|
82eec0a174 | ||
|
|
933dc6ebc4 | ||
|
|
1e6cae953d | ||
|
|
6d463de2b3 | ||
|
|
bbbc4663d1 | ||
|
|
02e1ec9bc4 | ||
|
|
3df3f6fd7b | ||
|
|
2c6ab8329e | ||
|
|
675376f2b4 | ||
|
|
15a34c6364 | ||
|
|
1bfe856eb2 | ||
|
|
7e71f16f9a | ||
|
|
38a64f9dfe | ||
|
|
e02aa6869e | ||
|
|
7db4eea691 | ||
|
|
b30d4608da | ||
|
|
81ca79911a | ||
|
|
e58d12ed5b | ||
|
|
d549f7d98f | ||
|
|
83fb7adf6c | ||
|
|
1d43a71773 | ||
|
|
ae184e4ab7 | ||
|
|
e6eccb38eb | ||
|
|
9da9886121 | ||
|
|
a1968d7196 | ||
|
|
7f5d44e0ff | ||
|
|
acf5feac80 | ||
|
|
d187d4b218 | ||
|
|
9808745072 | ||
|
|
02ba45c536 | ||
|
|
107db44327 | ||
|
|
dc5d0b3d1b | ||
|
|
7496f5266c | ||
|
|
91d848ebf3 | ||
|
|
e2733d20b2 | ||
|
|
637f6cd735 | ||
|
|
638260eb8e | ||
|
|
30468f786c | ||
|
|
46e50e9d58 | ||
|
|
7c29d0c0cf | ||
|
|
514fb8c10e | ||
|
|
53c862a88e | ||
|
|
b6b8bd1819 | ||
|
|
fd0bbb12c3 | ||
|
|
f2aa58c6f4 | ||
|
|
611493d966 | ||
|
|
e1bb04f740 | ||
|
|
1ade1de223 | ||
|
|
b0bda528c3 | ||
|
|
819e712bfe | ||
|
|
28b9b5af25 | ||
|
|
e9b137c2dd | ||
|
|
95ea3fa19c | ||
|
|
7587cf4401 | ||
|
|
7086749072 | ||
|
|
63b7e03697 | ||
|
|
678f2df60f | ||
|
|
9e57f14d60 | ||
|
|
987c1c6921 | ||
|
|
ffddfee379 | ||
|
|
a2f659ee48 | ||
|
|
d7d02e3c3a | ||
|
|
bb0c6722b6 | ||
|
|
979a54fb20 | ||
|
|
e4f9082b9a | ||
|
|
d95dc32d13 | ||
|
|
53ad66e8c3 | ||
|
|
eba2af633f | ||
|
|
95ce326e5b | ||
|
|
a5448a7de5 | ||
|
|
9231944d96 | ||
|
|
d69d1fa01a | ||
|
|
05efe46eaa | ||
|
|
dbda808a4a | ||
|
|
a049791855 | ||
|
|
ea1c18022e | ||
|
|
516633dc42 | ||
|
|
dc196a57e3 | ||
|
|
2a2820560d | ||
|
|
665656a99b | ||
|
|
658c8bdadc | ||
|
|
5fef40fb4d | ||
|
|
e69390cee7 | ||
|
|
7f647cf68f | ||
|
|
a130a41e69 | ||
|
|
78e127efdb | ||
|
|
ee38b4c813 | ||
|
|
3440557b6d | ||
|
|
d329a6fb22 | ||
|
|
7ebb5e4139 | ||
|
|
57ccbabecb | ||
|
|
eb26db16d7 | ||
|
|
4c8732d71b | ||
|
|
1cc98a5f04 | ||
|
|
de9258a87f | ||
|
|
37f53b4c05 | ||
|
|
a8aa669ba4 | ||
|
|
a5082316e9 | ||
|
|
20ba3ae101 | ||
|
|
4c7634bcb3 | ||
|
|
a21ae81d8a | ||
|
|
aeb3c85f59 | ||
|
|
1f04275ec1 | ||
|
|
4e3e9d0b4d | ||
|
|
358c640721 | ||
|
|
e36f36e15f | ||
|
|
7b17d41e96 | ||
|
|
d6bfa22f72 | ||
|
|
e6e5ad80d8 | ||
|
|
798b0c25cc | ||
|
|
22a56b8a87 | ||
|
|
44bbf73f92 | ||
|
|
00ffa62a91 | ||
|
|
de2200d36d | ||
|
|
710c15a2e9 | ||
|
|
443f1376bc | ||
|
|
a3a91a355b | ||
|
|
ab2572d7ea | ||
|
|
267002cd28 | ||
|
|
63066f4f13 | ||
|
|
caf9a12e9a | ||
|
|
1f62d9383f | ||
|
|
7727994d21 | ||
|
|
8a8696a3c4 | ||
|
|
8998028497 | ||
|
|
a4193c8a4b | ||
|
|
170c6f8705 | ||
|
|
43f493afb4 | ||
|
|
ee22c2f7db | ||
|
|
be3edd9590 | ||
|
|
023fe10d24 | ||
|
|
f66723fab9 | ||
|
|
8cc43feffc | ||
|
|
09a79b4974 | ||
|
|
642012017c | ||
|
|
2444ca413b | ||
|
|
77d4bc349a | ||
|
|
a2a444d6e0 | ||
|
|
4b3686faee | ||
|
|
85c4adf65f | ||
|
|
b69fedff84 | ||
|
|
5fd386f698 | ||
|
|
0ced658970 | ||
|
|
b415a4078d | ||
|
|
f18ac341fe | ||
|
|
63ce9e0a42 | ||
|
|
e1c485be84 | ||
|
|
34e538ae5d | ||
|
|
9995c51ffd | ||
|
|
e58a7c24ac | ||
|
|
777428f2d2 | ||
|
|
5b60212f2a | ||
|
|
a00bad7ed4 | ||
|
|
25b42e9d53 | ||
|
|
8d6c7eb896 | ||
|
|
7bf5be70f7 | ||
|
|
fb9f944458 | ||
|
|
92e873b996 | ||
|
|
9fddaa0c0c | ||
|
|
4a0fb71e67 | ||
|
|
274da6b24b | ||
|
|
15aeac3805 | ||
|
|
28ab0e2edb | ||
|
|
b54ad0498e | ||
|
|
4399059e4d | ||
|
|
43003046cb | ||
|
|
d157e205e9 | ||
|
|
5f21aef2b0 | ||
|
|
829309c70c | ||
|
|
c6a1c22ba6 | ||
|
|
b71e95fc2c | ||
|
|
04a3b84c83 | ||
|
|
86e0c04896 | ||
|
|
5ce276a11a | ||
|
|
5768f5aca6 | ||
|
|
1078f663ae | ||
|
|
0ac32c8375 | ||
|
|
4a9c9687c6 | ||
|
|
73c11f630b | ||
|
|
660de33686 | ||
|
|
69135b5c04 | ||
|
|
69b910399a | ||
|
|
158156d13d | ||
|
|
e63c59cb34 | ||
|
|
13ab5daa86 | ||
|
|
ef792f9ddb | ||
|
|
47cea614a1 | ||
|
|
aedf53821f | ||
|
|
7f5e145212 | ||
|
|
f528bfd45d | ||
|
|
686f3f266b | ||
|
|
5b1214a48e | ||
|
|
3f433d2c87 | ||
|
|
1a084f3d51 | ||
|
|
cd6f11693a | ||
|
|
b939777cec | ||
|
|
d6b86f4d85 | ||
|
|
3a1bc175ea | ||
|
|
db45c29a65 | ||
|
|
7d3505c55a | ||
|
|
fd872598d8 | ||
|
|
8cd0ac2fe1 | ||
|
|
7efa43875d | ||
|
|
44c513c4c9 | ||
|
|
2ee73ac3a8 | ||
|
|
28c3ee3fed | ||
|
|
f929aad6e3 | ||
|
|
e309de25a6 | ||
|
|
10d315a8f2 | ||
|
|
2b64948eb5 | ||
|
|
1f50f8d1d4 | ||
|
|
a1b74fe8fe | ||
|
|
ed5fd2cce4 | ||
|
|
bee3290936 | ||
|
|
beddab753d | ||
|
|
512176dbd8 | ||
|
|
fb6cf1d09c | ||
|
|
66201e2ddf | ||
|
|
ec844b96c0 | ||
|
|
f72e8ff4a6 | ||
|
|
b06eddd39d | ||
|
|
6f51f6b593 | ||
|
|
0294ffb9c8 | ||
|
|
d8d8aa4e2c | ||
|
|
141253b254 | ||
|
|
646be93b4c | ||
|
|
26aa7d72cc | ||
|
|
8e9c4afe70 | ||
|
|
bbc9d34839 | ||
|
|
a8c490cda5 | ||
|
|
4606bb3f06 | ||
|
|
aaaa7df625 | ||
|
|
9d4fb82e3c | ||
|
|
9fafc9eaf0 | ||
|
|
1ef59d0acf | ||
|
|
7fd7b91fac | ||
|
|
d927637dca | ||
|
|
4f2ac23784 | ||
|
|
f658b4db79 | ||
|
|
0c4ad8dc2a | ||
|
|
165c6fc8ce | ||
|
|
2e12669a4c | ||
|
|
52c00a5f15 | ||
|
|
ab6d960ffa | ||
|
|
fbf9eeb34d | ||
|
|
046d6672e2 | ||
|
|
75dfaa1e64 | ||
|
|
1115dde719 | ||
|
|
57206fd42a | ||
|
|
4c27ba27c5 | ||
|
|
ba91cd80d5 | ||
|
|
6f1f31c069 | ||
|
|
5467a72294 | ||
|
|
1b8eb456eb | ||
|
|
d720b93d0b | ||
|
|
eeab3a558f | ||
|
|
658138bcbc | ||
|
|
1190935d98 | ||
|
|
bfbc9133eb | ||
|
|
ad6a4837f8 | ||
|
|
039d3da365 | ||
|
|
a7e61ed446 | ||
|
|
b5075d29a8 | ||
|
|
91caaa612a | ||
|
|
d64477afa1 | ||
|
|
f6bac3809f | ||
|
|
f0cbd3ec9f | ||
|
|
7c1f25b46a | ||
|
|
ee9dbb297d | ||
|
|
c20709aa32 | ||
|
|
92cb7d5423 | ||
|
|
202a456a2b | ||
|
|
3294b949eb | ||
|
|
2aebb3eb2b | ||
|
|
95917e3f57 | ||
|
|
f186904281 | ||
|
|
c5df018e56 | ||
|
|
a541f297a3 | ||
|
|
df475d18d8 | ||
|
|
2f0c934ef9 | ||
|
|
fd836909df | ||
|
|
289e09e77b | ||
|
|
7ae9862745 | ||
|
|
41b9be476c | ||
|
|
52302d7274 | ||
|
|
333190eb97 | ||
|
|
eccabc6ee0 | ||
|
|
bb058620c3 | ||
|
|
9eb153f18f | ||
|
|
aaba6c1516 | ||
|
|
38e205a25b | ||
|
|
e1a237441d | ||
|
|
57c3072482 | ||
|
|
73332e5ccd | ||
|
|
1f673135ac | ||
|
|
aa455485c9 | ||
|
|
9307c4c1d9 | ||
|
|
40c3bac35a | ||
|
|
6eaee46144 | ||
|
|
0f2f112156 | ||
|
|
4721c45750 | ||
|
|
a6e022ad13 | ||
|
|
702c651c4a | ||
|
|
1154e441aa | ||
|
|
e463b581ea | ||
|
|
11d9f695e7 | ||
|
|
0c607d5728 | ||
|
|
67b915a5dd | ||
|
|
bb27c19087 | ||
|
|
8a7ddc38a6 | ||
|
|
b0a21b5334 | ||
|
|
dff38e7b40 | ||
|
|
1f1af9fd7f | ||
|
|
858693c638 | ||
|
|
9b14bb04ca | ||
|
|
c101c49c54 | ||
|
|
4bb2fcc7c9 | ||
|
|
08cea4eef8 | ||
|
|
883da8e219 | ||
|
|
78ebca6e13 | ||
|
|
5e2a644399 | ||
|
|
11774f549e | ||
|
|
d9d849fc5d | ||
|
|
e19e89a5d4 | ||
|
|
f193c7979c | ||
|
|
3035f7ff83 | ||
|
|
4afa64828b | ||
|
|
c45c3d0059 | ||
|
|
baca51faff | ||
|
|
bc51c5c989 | ||
|
|
5069146392 | ||
|
|
59a983b921 | ||
|
|
4e463d8d50 | ||
|
|
94fe4f9fa3 | ||
|
|
0fb48229a7 | ||
|
|
b41a2cd1e4 | ||
|
|
c4b1fcc0f9 | ||
|
|
0f35920cd8 | ||
|
|
7d977de7e1 | ||
|
|
07d898662d | ||
|
|
caed880216 | ||
|
|
b338082b3f | ||
|
|
9dc39cbae3 | ||
|
|
80cabfad16 | ||
|
|
38ca2abc2e | ||
|
|
73bdea1951 | ||
|
|
00af2b2680 | ||
|
|
a735aa3139 | ||
|
|
6b2b6112f8 | ||
|
|
bf08806145 | ||
|
|
f9e92e973f | ||
|
|
8dc75d7535 | ||
|
|
03857e318e | ||
|
|
16f62432c4 | ||
|
|
8b1f24b090 | ||
|
|
b448f2f36c | ||
|
|
97eb5b14dc | ||
|
|
7eee2a509a | ||
|
|
42c3c0cced | ||
|
|
9588b95a08 | ||
|
|
2edcdce334 | ||
|
|
8853f86e1d | ||
|
|
e374bfa35b | ||
|
|
b88e4a9a3b | ||
|
|
537730b956 | ||
|
|
edf779ffcc | ||
|
|
121061dcdf | ||
|
|
83d7396850 | ||
|
|
6b2d3e3c96 | ||
|
|
e98a6e40a9 | ||
|
|
28fbe299c3 | ||
|
|
297d8e6227 | ||
|
|
004bc62c28 | ||
|
|
ed1c0bcb0c | ||
|
|
22a46c55e4 | ||
|
|
ea88812f4f | ||
|
|
58fe2f10f0 | ||
|
|
3a1d9b8bbb | ||
|
|
0e4b179d33 | ||
|
|
77fef8c148 | ||
|
|
59817ccb2c | ||
|
|
bf3e8bf11e | ||
|
|
9acbed0605 | ||
|
|
cf98951b82 | ||
|
|
f72b519c86 | ||
|
|
07ce05eaa9 | ||
|
|
b324e814a9 | ||
|
|
c69810559b | ||
|
|
28c4f361ac | ||
|
|
3d57da2a70 | ||
|
|
00406dff19 | ||
|
|
69de927c6c | ||
|
|
72cbca10e1 | ||
|
|
34f715e754 | ||
|
|
cae61cef89 | ||
|
|
6411cfb6f3 | ||
|
|
898712a85c | ||
|
|
4fa0f5d292 | ||
|
|
1ccde1cb94 | ||
|
|
ad08132319 | ||
|
|
415e561f1d | ||
|
|
ee8b7021da | ||
|
|
625976dac8 | ||
|
|
1ac157da77 | ||
|
|
64a595f26a | ||
|
|
3ad9a57e4f | ||
|
|
7f777bf385 | ||
|
|
56bf1d37a0 | ||
|
|
c92b2e845f | ||
|
|
68e73e391f | ||
|
|
c0637b3794 | ||
|
|
e98c87213e | ||
|
|
3d4b4c0f4b | ||
|
|
8145122b08 | ||
|
|
7399c5a9be | ||
|
|
10f0e412f8 | ||
|
|
4b7aba5173 | ||
|
|
35b66fc4f9 | ||
|
|
13eb76e091 | ||
|
|
3cf1e035ba | ||
|
|
a6b025d37d | ||
|
|
edfcbd9937 | ||
|
|
612b477d48 | ||
|
|
6c9bf8936a | ||
|
|
bb551faa4a | ||
|
|
a136e5a8b1 | ||
|
|
4796f5e9bc | ||
|
|
3504fe171b | ||
|
|
ab1f142ba0 | ||
|
|
4120b61d00 | ||
|
|
7f957d280b | ||
|
|
2be3bc02dd | ||
|
|
c27357906a | ||
|
|
985a19d6d1 | ||
|
|
3f5dcc340c | ||
|
|
f09936ac82 | ||
|
|
d030931173 | ||
|
|
825bd5f8e5 | ||
|
|
bc0b1dc1eb | ||
|
|
630be16f6c | ||
|
|
44a91cae10 | ||
|
|
d575b78aab | ||
|
|
6e44ba7fa2 | ||
|
|
6986f88c3f | ||
|
|
988578886e | ||
|
|
c4c7e3e610 | ||
|
|
b516f85ca8 | ||
|
|
1e4fe7cee2 | ||
|
|
debf7a7c7e | ||
|
|
5a1388b6df | ||
|
|
777aca2fd3 | ||
|
|
69e5bc9068 | ||
|
|
c8135d9af6 | ||
|
|
9e62fd7f26 | ||
|
|
8977f3c107 | ||
|
|
728c9fd5a9 | ||
|
|
bd49793889 | ||
|
|
16d17fdb8e | ||
|
|
4487d0ac49 | ||
|
|
7138fcfbf7 | ||
|
|
c45886db19 | ||
|
|
ffa65c3b70 | ||
|
|
2d603d2216 | ||
|
|
61190b14fc | ||
|
|
9886cc165a | ||
|
|
ce09776be2 | ||
|
|
5be1a8e065 | ||
|
|
09683d3597 | ||
|
|
590b7eed18 | ||
|
|
af5ad10728 | ||
|
|
ac9eb0731a | ||
|
|
9a64fbe4d8 | ||
|
|
efe160c502 | ||
|
|
dc887a4dae | ||
|
|
b9f1950797 | ||
|
|
e16c53fabb | ||
|
|
8351d2d481 | ||
|
|
08785f48b7 | ||
|
|
75c6215f98 | ||
|
|
6b136f9e8f | ||
|
|
2ddbbd10de | ||
|
|
9fa3e85353 | ||
|
|
4390df5107 | ||
|
|
ecd854fdb4 | ||
|
|
513b500f75 | ||
|
|
edf75d592c | ||
|
|
1376847f9f | ||
|
|
17348a7f5e | ||
|
|
4f31916ffb | ||
|
|
943144d91a | ||
|
|
34e01bbf07 | ||
|
|
7f1135b9a4 | ||
|
|
f68dd77007 | ||
|
|
436d8b892a | ||
|
|
dc6f57fd55 | ||
|
|
65262d5738 | ||
|
|
773b93ee06 | ||
|
|
82c7e2a4c6 | ||
|
|
546cdbd77d | ||
|
|
c265508067 | ||
|
|
dd4e27d810 | ||
|
|
a41bc9af8f | ||
|
|
3bfd9da14f | ||
|
|
c573ff6752 | ||
|
|
060366c5ad | ||
|
|
6da41eafc4 | ||
|
|
0124311e00 | ||
|
|
f515528907 | ||
|
|
b453b70bd8 | ||
|
|
0ac4bd56a8 | ||
|
|
3811a291e2 | ||
|
|
3415a4ddb4 | ||
|
|
b7f0f463a5 | ||
|
|
8e682019e3 | ||
|
|
cf495bcf9f | ||
|
|
fb0eaffc6d | ||
|
|
07ad1b93a3 | ||
|
|
d36cd60e6c | ||
|
|
5e809a8095 | ||
|
|
791c2261d6 | ||
|
|
163a7cb620 | ||
|
|
6bb705711b | ||
|
|
4136f33c7e | ||
|
|
6dca2016fc | ||
|
|
678673089d | ||
|
|
28b6751f30 | ||
|
|
79aceca54a | ||
|
|
6a8c397deb | ||
|
|
9cdf757fd5 | ||
|
|
36bdbe5479 | ||
|
|
8004340674 | ||
|
|
a2cc3b2433 | ||
|
|
afa05eb15e | ||
|
|
de5eaa6452 | ||
|
|
7517502475 | ||
|
|
8f2b1fb008 | ||
|
|
891b38e446 | ||
|
|
7dea1da4ae | ||
|
|
4ce900b44c | ||
|
|
f3f2d9be03 | ||
|
|
77729c2445 | ||
|
|
d71b9a8b2f | ||
|
|
181f1558cd | ||
|
|
2750332396 | ||
|
|
f115e911d7 | ||
|
|
3ab493de4c | ||
|
|
3e25f9515a | ||
|
|
246d897f4c | ||
|
|
7e84c2498f | ||
|
|
e670b89e3b | ||
|
|
bd0d90b21d | ||
|
|
1a0636f8d7 | ||
|
|
2e134c9c55 | ||
|
|
5391d80669 | ||
|
|
36b486bb74 | ||
|
|
4ad06a29b2 | ||
|
|
e58143b355 | ||
|
|
1f5476fcce | ||
|
|
461c0471af | ||
|
|
9c3ad57432 | ||
|
|
e748ba4f53 | ||
|
|
b8ed223bfe | ||
|
|
c970a162e7 | ||
|
|
c321f67309 | ||
|
|
7f7f987341 | ||
|
|
aebcb60e55 | ||
|
|
1a18c71b50 | ||
|
|
03a6c5103d | ||
|
|
128b346e0a | ||
|
|
415fa2ea77 | ||
|
|
9951bf39f9 | ||
|
|
8948b5d613 | ||
|
|
5086347239 | ||
|
|
9d4520d0e0 | ||
|
|
5b9f457a89 | ||
|
|
3a4739d651 | ||
|
|
76b62fd001 | ||
|
|
b1f645758a | ||
|
|
bc1b050d85 | ||
|
|
03d5f74aee | ||
|
|
9f05cc34df | ||
|
|
285dc330bd | ||
|
|
baf8ebf01a | ||
|
|
9d16dd550e | ||
|
|
78d6da976c | ||
|
|
dd6ee15c37 | ||
|
|
0db634747e | ||
|
|
6e59c1db89 | ||
|
|
61382a500a | ||
|
|
3a51dee658 | ||
|
|
cc38b844d7 | ||
|
|
c6105c0a04 | ||
|
|
93a40ea926 | ||
|
|
db8d746688 | ||
|
|
997344f303 | ||
|
|
16e9b7de41 | ||
|
|
3486513433 | ||
|
|
0806e3f66f | ||
|
|
39b4da28b3 | ||
|
|
5a67135a0b | ||
|
|
42f1e0e49b | ||
|
|
27c3f2cb9b | ||
|
|
a07cf92aed | ||
|
|
01e3b763a6 | ||
|
|
0ae04d7367 | ||
|
|
ebc054881f | ||
|
|
a20dd508aa | ||
|
|
6180a1818a | ||
|
|
d3eead2eec | ||
|
|
853d6f7a83 | ||
|
|
93ac68bca5 | ||
|
|
1e43adfc89 | ||
|
|
7a3f194486 | ||
|
|
2c0262afa7 | ||
|
|
196ad10903 | ||
|
|
b7dda06abf | ||
|
|
fcf8fcc8e5 | ||
|
|
dbc5594cb6 | ||
|
|
4cbb86e1c4 | ||
|
|
f513a41a3d | ||
|
|
c106152d26 | ||
|
|
facc68be25 | ||
|
|
3ff0631ed9 | ||
|
|
b1ba65744e | ||
|
|
b67d59594e | ||
|
|
2e255c6b9f | ||
|
|
3f33731662 | ||
|
|
d05e66d217 | ||
|
|
2d80ae8987 | ||
|
|
17383a2a2a | ||
|
|
9257a9e49c | ||
|
|
70a194b930 | ||
|
|
2573109866 | ||
|
|
9dfa5b421d | ||
|
|
9da8ba18e6 | ||
|
|
76bc683820 | ||
|
|
3b22c4707d | ||
|
|
96e6e05372 | ||
|
|
e2222c3924 | ||
|
|
31e8f3c894 | ||
|
|
9368caf64d | ||
|
|
38e584a072 | ||
|
|
313aa56710 | ||
|
|
4cbf74b6b8 | ||
|
|
33417e7025 | ||
|
|
4021dab059 | ||
|
|
626df76abb | ||
|
|
abcd5da72e | ||
|
|
97a847bc03 | ||
|
|
ab93bbe2ae | ||
|
|
0f0b726444 | ||
|
|
b92e5a22ec | ||
|
|
17b0018b42 | ||
|
|
39cf780327 | ||
|
|
e89f66eca9 | ||
|
|
b6d78bfa0d | ||
|
|
c33a346edf | ||
|
|
61a2ad53cb | ||
|
|
2c1794c42e | ||
|
|
8a4c1cc411 | ||
|
|
330d0414a5 | ||
|
|
3802ce26a1 | ||
|
|
4abe615b84 | ||
|
|
a412ac572f | ||
|
|
b2b5fb228f | ||
|
|
8f186479e2 | ||
|
|
4c3a88a284 | ||
|
|
d6b4936796 | ||
|
|
9d0fe224f4 | ||
|
|
6e0374f6b5 | ||
|
|
9e5f5284b3 | ||
|
|
c596ed1713 | ||
|
|
91cf4d88fb | ||
|
|
a96fc003bd | ||
|
|
d44b29c21e | ||
|
|
070893f425 | ||
|
|
9621339dca | ||
|
|
ede28208d8 | ||
|
|
7739f36e38 | ||
|
|
f8c8799840 | ||
|
|
43fff2384e | ||
|
|
1b21b62ab4 | ||
|
|
a1516e92b6 | ||
|
|
6fb883e8e3 | ||
|
|
6e295807ac | ||
|
|
f2674e31e0 | ||
|
|
4690764bba | ||
|
|
3c1cf9fa86 | ||
|
|
1f47a9223e | ||
|
|
33e3963e1b | ||
|
|
cd4c3e888a | ||
|
|
7916e2245d | ||
|
|
abd0aaff03 | ||
|
|
c39d5b78f6 | ||
|
|
4d40895f2c | ||
|
|
e477b8b81b | ||
|
|
b118d61e55 | ||
|
|
2f62b397b5 | ||
|
|
907a5b2690 | ||
|
|
0849bf0821 | ||
|
|
305034817d | ||
|
|
ec410fc9ce | ||
|
|
68a7931591 | ||
|
|
c9159e5321 | ||
|
|
8c9b861e74 | ||
|
|
5797fa5d7e | ||
|
|
8ef9a8ece3 | ||
|
|
fc01f7e7f9 | ||
|
|
3b0dca51b0 | ||
|
|
da415d54bf | ||
|
|
b4608c0455 | ||
|
|
d5a0b50c6f | ||
|
|
87858c89ca | ||
|
|
a6f816d697 | ||
|
|
0ad041d476 | ||
|
|
1eb20527c8 | ||
|
|
e3e86d56c4 | ||
|
|
1df912cf9e | ||
|
|
4351832355 | ||
|
|
59faf6d6a6 | ||
|
|
725af7d460 | ||
|
|
a363e34cc5 | ||
|
|
ea041c0e33 | ||
|
|
83479e770d | ||
|
|
e2f2289897 | ||
|
|
844c72eccc | ||
|
|
6b1534cc67 | ||
|
|
e8cd23de30 | ||
|
|
7c2d6a781c | ||
|
|
f1510b2cc3 | ||
|
|
357a94326c | ||
|
|
0824d6fc67 | ||
|
|
6c0372d30b | ||
|
|
92ccca6aa8 | ||
|
|
dd3587f38e | ||
|
|
7d83131cc5 | ||
|
|
66e85a21c7 | ||
|
|
90a9fdae1f | ||
|
|
3fb2ded1d5 | ||
|
|
f76af4b3f3 | ||
|
|
717fc2ad8d | ||
|
|
c05bab779e | ||
|
|
a52c757c9f | ||
|
|
970a87a6bb | ||
|
|
d8bc1fd0ae | ||
|
|
7501267e22 | ||
|
|
13b55754af | ||
|
|
972ddf7840 | ||
|
|
322d0c6657 | ||
|
|
2054396a04 | ||
|
|
039de852ec | ||
|
|
144c345daf | ||
|
|
de83cd02e0 | ||
|
|
6380ab5e26 | ||
|
|
b346ff468e | ||
|
|
5a9fdfec7e | ||
|
|
d19893dab5 | ||
|
|
2521d69883 | ||
|
|
2ab83ea784 | ||
|
|
2437490100 | ||
|
|
e4533c7a8c | ||
|
|
1e5ffbedde | ||
|
|
79638566e5 | ||
|
|
03daf0e361 | ||
|
|
d219f7e7ed | ||
|
|
95cbfc643d | ||
|
|
5898e81684 | ||
|
|
394411ac74 | ||
|
|
ea76864009 | ||
|
|
43ce4dfe9e | ||
|
|
ebc06f87ca | ||
|
|
5bbe92995c | ||
|
|
411bffc41c | ||
|
|
ff1f20a3ee | ||
|
|
9c5d1246c7 | ||
|
|
8dd7cb0621 | ||
|
|
d30329297b | ||
|
|
a95c67907c | ||
|
|
0f533160c7 | ||
|
|
e163bca720 | ||
|
|
8c6939c0b0 | ||
|
|
2d0e9143e2 | ||
|
|
87f4827e1d | ||
|
|
4a585ccb2f | ||
|
|
aa0aa4fa31 | ||
|
|
84fa15d854 | ||
|
|
4add45b4f6 | ||
|
|
4f101ad7ff | ||
|
|
c1db2eb8c3 | ||
|
|
82d19dafe4 | ||
|
|
5286db75a8 | ||
|
|
917f95fd4d | ||
|
|
fe31975692 | ||
|
|
95f7652d65 | ||
|
|
3ec9c4fcc6 | ||
|
|
2f87c60799 | ||
|
|
03bfca946a | ||
|
|
ed2dcdf68e | ||
|
|
4304763ba2 | ||
|
|
c1e42a1397 | ||
|
|
d0a1ffc957 | ||
|
|
df0f11a03b | ||
|
|
2d92f0b8f0 | ||
|
|
aad13cd131 | ||
|
|
a513fe19ac | ||
|
|
f4beb510a4 | ||
|
|
d731dae8e3 | ||
|
|
c9087c2a60 | ||
|
|
14ae3ba7f9 | ||
|
|
5a91de8c90 | ||
|
|
e3b32540df | ||
|
|
a37904dd86 | ||
|
|
cf25629d1e | ||
|
|
0ca790b92e | ||
|
|
d1fe2b2459 | ||
|
|
d4e8164f7e | ||
|
|
08351fb37a | ||
|
|
85e53d4108 | ||
|
|
aa05ae6fec | ||
|
|
1565b7bcd7 | ||
|
|
b409186b8d | ||
|
|
418a97afa1 | ||
|
|
5132455efe | ||
|
|
c0ad5542a8 | ||
|
|
3a27ad0b57 | ||
|
|
2b413144dc | ||
|
|
3ebcc707d2 | ||
|
|
7775e9ecc2 | ||
|
|
03d843ddf2 | ||
|
|
eb51d102bb | ||
|
|
25eb44841e | ||
|
|
b333af0666 | ||
|
|
76c8b7710b | ||
|
|
70e198602b | ||
|
|
206f0fa759 | ||
|
|
fd6ce8f660 | ||
|
|
727d01d4f6 | ||
|
|
ae22853141 | ||
|
|
d418c81eff | ||
|
|
2a29ca73c9 | ||
|
|
54936004fd | ||
|
|
74c95119f2 | ||
|
|
366c1b8bfa | ||
|
|
a993ba85cf | ||
|
|
226c91327d | ||
|
|
b8bf3e3aac | ||
|
|
288426fe3c | ||
|
|
72cc388104 | ||
|
|
378180d8dc | ||
|
|
78c34e98cd | ||
|
|
2792c4f2af | ||
|
|
447db2139a | ||
|
|
564c8f9978 | ||
|
|
c50c0c3fbf | ||
|
|
cabb4d616d | ||
|
|
631271d716 | ||
|
|
9d27abd94f | ||
|
|
148dfc2a8b | ||
|
|
3acace1333 | ||
|
|
0221cfcd71 | ||
|
|
f351077efb | ||
|
|
e84be9dbca | ||
|
|
46ddf5511d | ||
|
|
89e957e7a2 | ||
|
|
982b431579 | ||
|
|
bf7c65bdf4 | ||
|
|
8e5a0667f8 | ||
|
|
19b84f3c35 | ||
|
|
08fc60898b | ||
|
|
082391983e | ||
|
|
504e56ebdc | ||
|
|
455b761956 | ||
|
|
b56dad1c7b | ||
|
|
9ba5695ce5 | ||
|
|
66099dd9af | ||
|
|
b689bc57d6 | ||
|
|
a69d83b60b | ||
|
|
86840ae241 | ||
|
|
3c51961e0e | ||
|
|
d014c98c8d | ||
|
|
a98fd896cd | ||
|
|
d6cdca958e | ||
|
|
efdea7bf19 | ||
|
|
0d3301964d | ||
|
|
fe1e3ce3e9 | ||
|
|
bb326a3749 | ||
|
|
27725c1d74 | ||
|
|
e026db5893 | ||
|
|
43f04c233c | ||
|
|
a8baa8c555 | ||
|
|
728584be27 | ||
|
|
b9adb4a6bc | ||
|
|
ae48a07313 | ||
|
|
956034d7e5 | ||
|
|
6cd9f35b9b | ||
|
|
689f936f7e | ||
|
|
6977fbfd8b | ||
|
|
77e4672d8d | ||
|
|
d34720fd7d | ||
|
|
d9c4d1cc1a | ||
|
|
f644caa51a | ||
|
|
1eb87257da | ||
|
|
32ce63371a | ||
|
|
ec86b0fb3a | ||
|
|
1d346ae63a | ||
|
|
74cd30b811 | ||
|
|
7fb9a24e39 | ||
|
|
afeb6ee377 | ||
|
|
f29042b531 | ||
|
|
09bfb054fb | ||
|
|
2677e107e6 | ||
|
|
66cd58461d | ||
|
|
bb0ebb1f2d | ||
|
|
27c75a9a90 | ||
|
|
d0cd3b8d84 | ||
|
|
9af9eaaa76 | ||
|
|
8c8f42f76c | ||
|
|
51fe68905b | ||
|
|
7fe70ecc56 | ||
|
|
d03cda5923 | ||
|
|
30ac07d4f0 | ||
|
|
8857052055 | ||
|
|
ce11fedc6e | ||
|
|
43d4145a98 | ||
|
|
295defa5f1 | ||
|
|
f801f97e04 | ||
|
|
f48c3dd51a | ||
|
|
62296fe351 | ||
|
|
32f36bcefc | ||
|
|
bc8a22cc30 | ||
|
|
f631ef9bd2 | ||
|
|
f7341ff400 | ||
|
|
fd429f2f6c | ||
|
|
fb3e5849bb | ||
|
|
7854b05654 | ||
|
|
500dab07e8 | ||
|
|
f6630e791b | ||
|
|
168485b75b | ||
|
|
5cd4393b14 | ||
|
|
7ed601b782 | ||
|
|
d1f2367bc0 | ||
|
|
851e67a1b4 | ||
|
|
fc2b4c4879 | ||
|
|
9c605cb135 | ||
|
|
24f9e90b0e | ||
|
|
a4a0ffdb2b | ||
|
|
0ea00c9a3c | ||
|
|
e1d4294a45 | ||
|
|
c3c7c29246 | ||
|
|
31bb950be6 | ||
|
|
8083a3e508 | ||
|
|
644c433cb3 | ||
|
|
d691f66983 | ||
|
|
386405f786 | ||
|
|
3ef693a032 | ||
|
|
b03c60f351 | ||
|
|
9de5e440b9 | ||
|
|
66fb9763af | ||
|
|
1b6b029e40 | ||
|
|
612384d771 | ||
|
|
dab2ed991a | ||
|
|
e591824733 | ||
|
|
04369ff2f5 | ||
|
|
68decc7c7f | ||
|
|
dc99065b5f | ||
|
|
ca735206e0 | ||
|
|
5dd9488c09 | ||
|
|
60cd49d5d7 | ||
|
|
a300e69170 | ||
|
|
1a9353d258 | ||
|
|
6dbad63eef | ||
|
|
27362c82e9 | ||
|
|
55480af80e | ||
|
|
7d13299d07 | ||
|
|
1017ebe9cb | ||
|
|
77f8dd5add | ||
|
|
c5e9815da4 | ||
|
|
9d8e9c0993 | ||
|
|
d57c4e0120 | ||
|
|
4b74fe1f00 | ||
|
|
586314f2aa | ||
|
|
0ecfa9930c | ||
|
|
ba1c6e37fc | ||
|
|
927f621e79 | ||
|
|
367e86e847 | ||
|
|
7bfdb6d18c | ||
|
|
379ca80d34 | ||
|
|
4d1135e486 | ||
|
|
5147f5aac0 | ||
|
|
766a487abf | ||
|
|
72f0390014 | ||
|
|
b17780d521 | ||
|
|
3431395696 | ||
|
|
01ffc75bc9 | ||
|
|
31e31b8a24 | ||
|
|
e63c3dc74b |
+45
@@ -0,0 +1,45 @@
|
||||
arm-linux-user
|
||||
arm-softmmu
|
||||
armeb-linux-user
|
||||
config-host.*
|
||||
dyngen
|
||||
i386
|
||||
i386-softmmu
|
||||
i386-darwin-user
|
||||
i386-linux-user
|
||||
ppc-softmmu
|
||||
ppc64-softmmu
|
||||
ppc-darwin-user
|
||||
ppc-linux-user
|
||||
qemu-doc.html
|
||||
qemu-tech.html
|
||||
qemu-doc.info
|
||||
qemu-tech.info
|
||||
qemu.1
|
||||
qemu.pod
|
||||
qemu-img.1
|
||||
qemu-img.pod
|
||||
sparc-linux-user
|
||||
qemu-img
|
||||
sparc-softmmu
|
||||
x86_64-softmmu
|
||||
sparc64-linux-user
|
||||
sparc64-softmmu
|
||||
mips-softmmu
|
||||
mipsel-softmmu
|
||||
mips-linux-user
|
||||
mipsel-linux-user
|
||||
m68k-linux-user
|
||||
.gdbinit
|
||||
sh4-linux-user
|
||||
sh4-softmmu
|
||||
*.aux
|
||||
*.cp
|
||||
*.dvi
|
||||
*.fn
|
||||
*.ky
|
||||
*.log
|
||||
*.pg
|
||||
*.toc
|
||||
*.tp
|
||||
*.vr
|
||||
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
+111
-111
@@ -1,36 +1,14 @@
|
||||
------------------------------------------------------------------------------
|
||||
NOTE:
|
||||
Some code of the Twin package was modified for DOSEMU by the DOSEMU-team.
|
||||
The original is 'Copyright 1997 Willows Software, Inc.' and generously
|
||||
was put under the GNU Library General Public License.
|
||||
( for more information see http://www.willows.com/ )
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
We make use of section 3 of the GNU Library General Public License
|
||||
('...opt to apply the terms of the ordinary GNU General Public License...'),
|
||||
because the resulting product is an integrated part of DOSEMU and
|
||||
can not be considered to be a 'library' in the terms of Library License.
|
||||
|
||||
Therefore, the below GNU LIBRARY GENERAL PUBLIC LICENSE applies only to the
|
||||
_unchanged_ Twin package from Willows. For the DOSEMU-changed parts the normal
|
||||
GNU GENERAL PUBLIC LICENSE applies. This GPL (file COPYING) can be found in
|
||||
the root directory of the DOSEMU distribution.
|
||||
|
||||
The act of transformation to GPL was indicated to the maintainer of the Twin
|
||||
package (Rob Penrose <rob@Canopy.Com>) and he acknowledge agreement.
|
||||
|
||||
Nov. 1 1997, The DOSEMU team.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
@@ -39,97 +17,109 @@ freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Library General Public License, applies to some
|
||||
specially designated Free Software Foundation software, and to any
|
||||
other libraries whose authors decide to use it. You can use it for
|
||||
your libraries, too.
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the library, or if you modify it.
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link a program with the library, you must provide
|
||||
complete object files to the recipients so that they can relink them
|
||||
with the library, after making changes to the library and recompiling
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright
|
||||
the library, and (2) offer you this license which gives you legal
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, so that any problems introduced by others will not reflect on
|
||||
the original authors' reputations.
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
transforming the program into proprietary software. To prevent this,
|
||||
we have made it clear that any patent must be licensed for everyone's
|
||||
free use or not licensed at all.
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary
|
||||
GNU General Public License, which was designed for utility programs. This
|
||||
license, the GNU Library General Public License, applies to certain
|
||||
designated libraries. This license is quite different from the ordinary
|
||||
one; be sure to read it in full, and don't assume that anything in it is
|
||||
the same as in the ordinary license.
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
The reason we have a separate public license for some libraries is that
|
||||
they blur the distinction we usually make between modifying or adding to a
|
||||
program and simply using it. Linking a program with a library, without
|
||||
changing the library, is in some sense simply using the library, and is
|
||||
analogous to running a utility program or application program. However, in
|
||||
a textual and legal sense, the linked executable is a combined work, a
|
||||
derivative of the original library, and the ordinary General Public License
|
||||
treats it as such.
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General
|
||||
Public License for libraries did not effectively promote software
|
||||
sharing, because most developers did not use the libraries. We
|
||||
concluded that weaker conditions might promote sharing better.
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the
|
||||
users of those programs of all benefit from the free status of the
|
||||
libraries themselves. This Library General Public License is intended to
|
||||
permit developers of non-free programs to use free libraries, while
|
||||
preserving your freedom as a user of such programs to change the free
|
||||
libraries that are incorporated in them. (We have not seen how to achieve
|
||||
this as regards changes in header files, but we have achieved it as regards
|
||||
changes in the actual functions of the Library.) The hope is that this
|
||||
will lead to faster development of free libraries.
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, while the latter only
|
||||
works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Library
|
||||
General Public License (also called "this License"). Each licensee is
|
||||
addressed as "you".
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
@@ -278,7 +268,7 @@ distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
@@ -305,23 +295,31 @@ of these things:
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
d) Verify that the user has already received a copy of these
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the source code distributed need not include anything that is normally
|
||||
distributed (in either source or binary form) with the major
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
@@ -370,7 +368,7 @@ Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
@@ -413,7 +411,7 @@ excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Library General Public License from time to time.
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
@@ -459,7 +457,7 @@ DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Libraries
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
@@ -476,18 +474,18 @@ convey the exclusion of warranty; and each file should have at least the
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -502,3 +500,5 @@ necessary. Here is a sample; alter the names:
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
version 0.9.0:
|
||||
|
||||
- Support for relative paths in backing files for disk images
|
||||
- Async file I/O API
|
||||
- New qcow2 disk image format
|
||||
- Support of multiple VM snapshots
|
||||
- Linux: specific host CDROM and floppy support
|
||||
- SMM support
|
||||
- Moved PCI init, MP table init and ACPI table init to Bochs BIOS
|
||||
- Support for MIPS32 Release 2 instruction set (Thiemo Seufer)
|
||||
- MIPS Malta system emulation (Aurelien Jarno, Stefan Weil)
|
||||
- Darwin userspace emulation (Pierre d'Herbemont)
|
||||
- m68k user support (Paul Brook)
|
||||
- several x86 and x86_64 emulation fixes
|
||||
- Mouse relative offset VNC extension (Anthony Liguori)
|
||||
- PXE boot support (Anthony Liguori)
|
||||
- '-daemonize' option (Anthony Liguori)
|
||||
|
||||
version 0.8.2:
|
||||
|
||||
- ACPI support
|
||||
- PC VGA BIOS fixes
|
||||
- switch to OpenBios for SPARC targets (Blue Swirl)
|
||||
- VNC server fixes
|
||||
- MIPS FPU support (Marius Groeger)
|
||||
- Solaris/SPARC host support (Ben Taylor)
|
||||
- PPC breakpoints and single stepping (Jason Wessel)
|
||||
- USB updates (Paul Brook)
|
||||
- UDP/TCP/telnet character devices (Jason Wessel)
|
||||
- Windows sparse file support (Frediano Ziglio)
|
||||
- RTL8139 NIC TCP segmentation offloading (Igor Kovalenko)
|
||||
- PCNET NIC support (Antony T Curtis)
|
||||
- Support for variable frequency host CPUs
|
||||
- Workaround for win32 SMP hosts
|
||||
- Support for AMD Flash memories (Jocelyn Mayer)
|
||||
- Audio capture to WAV files support (malc)
|
||||
|
||||
version 0.8.1:
|
||||
|
||||
- USB tablet support (Brad Campbell, Anthony Liguori)
|
||||
- win32 host serial support (Kazu)
|
||||
- PC speaker support (Joachim Henke)
|
||||
- IDE LBA48 support (Jens Axboe)
|
||||
- SSE3 support
|
||||
- Solaris port (Ben Taylor)
|
||||
- Preliminary SH4 target (Samuel Tardieu)
|
||||
- VNC server (Anthony Liguori)
|
||||
- slirp fixes (Ed Swierk et al.)
|
||||
- USB fixes
|
||||
- ARM Versatile Platform Baseboard emulation (Paul Brook)
|
||||
|
||||
version 0.8.0:
|
||||
|
||||
- ARM system emulation: Arm Integrator/CP board with an arm1026ej-s
|
||||
cpu (Paul Brook)
|
||||
- SMP support
|
||||
- Mac OS X cocoa improvements (Mike Kronenberg)
|
||||
- Mac OS X CoreAudio driver (Mike Kronenberg)
|
||||
- DirectSound driver (malc)
|
||||
- ALSA audio driver (malc)
|
||||
- new audio options: '-soundhw' and '-audio-help' (malc)
|
||||
- ES1370 PCI audio device (malc)
|
||||
- Initial USB support
|
||||
- Linux host serial port access
|
||||
- Linux host low level parallel port access
|
||||
- New network emulation code supporting VLANs.
|
||||
- MIPS and MIPSel User Linux emulation
|
||||
- MIPS fixes to boot Linux (Daniel Jacobowitz)
|
||||
- NX bit support
|
||||
- Initial SPARC SMP support (Blue Swirl)
|
||||
- Major overhaul of the virtual FAT driver for read/write support
|
||||
(Johannes Schindelin)
|
||||
|
||||
version 0.7.2:
|
||||
|
||||
- x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit)
|
||||
- merge self modifying code handling in dirty ram page mecanism.
|
||||
- MIPS fixes (Ralf Baechle)
|
||||
- better user net performances
|
||||
|
||||
version 0.7.1:
|
||||
|
||||
- read-only Virtual FAT support (Johannes Schindelin)
|
||||
- Windows 2000 install disk full hack (original idea from Vladimir
|
||||
N. Oleynik)
|
||||
- VMDK disk image creation (Filip Navara)
|
||||
- SPARC64 progress (Blue Swirl)
|
||||
- initial MIPS support (Jocelyn mayer)
|
||||
- MIPS improvements (Ralf Baechle)
|
||||
- 64 bit fixes in user networking (initial patch by Gwenole Beauchesne)
|
||||
- IOAPIC support (Filip Navara)
|
||||
|
||||
version 0.7.0:
|
||||
|
||||
- better BIOS translation and HDD geometry auto-detection
|
||||
- user mode networking bug fix
|
||||
- undocumented FPU ops support
|
||||
- Cirrus VGA: support for 1280x1024x[8,15,16] modes
|
||||
- 'pidfile' option
|
||||
- .dmg disk image format support (Johannes Schindelin)
|
||||
- keymaps support (initial patch by Johannes Schindelin)
|
||||
- big endian ARM support (Lennert Buytenhek)
|
||||
- added generic 64 bit target support
|
||||
- x86_64 target support
|
||||
- initial APIC support
|
||||
- MMX/SSE/SSE2/PNI support
|
||||
- PC parallel port support (Mark Jonckheere)
|
||||
- initial SPARC64 support (Blue Swirl)
|
||||
- SPARC target boots Linux (Blue Swirl)
|
||||
- armv5te user mode support (Paul Brook)
|
||||
- ARM VFP support (Paul Brook)
|
||||
- ARM "Angel" semihosting syscalls (Paul Brook)
|
||||
- user mode gdb stub support (Paul Brook)
|
||||
- Samba 3 support
|
||||
- initial Cocoa support (Pierre d'Herbemont)
|
||||
- generic FPU emulation code
|
||||
- Virtual PC read-only disk image support (Alex Beregszaszi)
|
||||
|
||||
version 0.6.1:
|
||||
|
||||
- Mac OS X port (Pierre d'Herbemont)
|
||||
- Virtual console support
|
||||
- Better monitor line edition
|
||||
- New block device layer
|
||||
- New 'qcow' growable disk image support with AES encryption and
|
||||
transparent decompression
|
||||
- VMware 3 and 4 read-only disk image support (untested)
|
||||
- Support for up to 4 serial ports
|
||||
- TFTP server support (Magnus Damm)
|
||||
- Port redirection support in user mode networking
|
||||
- Support for not executable data sections
|
||||
- Compressed loop disk image support (Johannes Schindelin)
|
||||
- Level triggered IRQ fix (aka NE2000 PCI performance fix) (Steve
|
||||
Wormley)
|
||||
- Fixed Fedora Core 2 problems (now you can run qemu without any
|
||||
LD_ASSUME_KERNEL tricks on FC2)
|
||||
- DHCP fix for Windows (accept DHCPREQUEST alone)
|
||||
- SPARC system emulation (Blue Swirl)
|
||||
- Automatic Samba configuration for host file access from Windows.
|
||||
- '-loadvm' and '-full-screen' options
|
||||
- ne2000 savevm support (Johannes Schindelin)
|
||||
- Ctrl-Alt is now the default grab key. Ctrl-Alt-[0-9] switches to
|
||||
the virtual consoles.
|
||||
- BIOS floppy fix for NT4 (Mike Nordell, Derek Fawcus, Volker Ruppert)
|
||||
- Floppy fixes for NT4 and NT5 (Mike Nordell)
|
||||
- NT4 IDE fixes (Ben Pfaf, Mike Nordell)
|
||||
- SDL Audio support and SB16 fixes (malc)
|
||||
- ENTER instruction bug fix (initial patch by Stefan Kisdaroczi)
|
||||
- VGA font change fix
|
||||
- VGA read-only CRTC register fix
|
||||
|
||||
version 0.6.0:
|
||||
|
||||
- minimalist FPU exception support (NetBSD FPU probe fix)
|
||||
- cr0.ET fix (Win95 boot)
|
||||
- *BSD port (Markus Niemisto)
|
||||
- I/O access fix (signaled by Mark Jonckheere)
|
||||
- IDE drives serial number fix (Mike Nordell)
|
||||
- int13 CDROM BIOS fix (aka Solaris x86 install CD fix)
|
||||
- int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix)
|
||||
- BSR/BSF "undefined behaviour" fix
|
||||
- vmdk2raw: convert VMware disk images to raw images
|
||||
- PCI support
|
||||
- NE2K PCI support
|
||||
- dummy VGA PCI support
|
||||
- VGA font selection fix (Daniel Serpell)
|
||||
- PIC reset fix (Hidemi KAWAI)
|
||||
- PIC spurious irq support (aka Solaris install bug)
|
||||
- added '-localtime' option
|
||||
- Cirrus CL-GD54xx VGA support (initial patch by Makoto Suzuki (suzu))
|
||||
- APM and system shutdown support
|
||||
- Fixed system reset
|
||||
- Support for other PC BIOSes
|
||||
- Initial PowerMac hardware emulation
|
||||
- PowerMac/PREP OpenFirmware compatible BIOS (Jocelyn Mayer)
|
||||
- initial IDE BMDMA support (needed for Darwin x86)
|
||||
- Set the default memory size for PC emulation to 128 MB
|
||||
|
||||
version 0.5.5:
|
||||
|
||||
- SDL full screen support (initial patch by malc)
|
||||
- VGA support on PowerPC PREP
|
||||
- VBE fixes (Matthew Mastracci)
|
||||
- PIT fixes (aka Win98 hardware probe and "VGA slowness" bug)
|
||||
- IDE master only fixes (aka Win98 CD-ROM probe bug)
|
||||
- ARM load/store half word fix (Ulrich Hecht)
|
||||
- FDC fixes for Win98
|
||||
|
||||
version 0.5.4:
|
||||
|
||||
- qemu-fast fixes
|
||||
- BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell)
|
||||
- keyboard/mouse fix (Mike Nordell)
|
||||
- IDE fixes (Linux did not recognized slave drivers)
|
||||
- VM86 EIP masking fix (aka NT5 install fix) (Mike Nordell)
|
||||
- QEMU can now boot a PowerPC Linux kernel (Jocelyn Mayer)
|
||||
- User mode network stack
|
||||
- imul imm8 fix + 0x82 opcode support (Hidemi KAWAI)
|
||||
- precise self modifying code (aka BeOS install bug)
|
||||
|
||||
version 0.5.3:
|
||||
|
||||
- added Bochs VESA VBE support
|
||||
- VGA memory map mode 3 access fix (OS/2 install fix)
|
||||
- IDE fixes (Jens Axboe)
|
||||
- CPU interrupt fixes
|
||||
- fixed various TLB invalidation cases (NT install)
|
||||
- fixed cr0.WP semantics (XP install)
|
||||
- direct chaining support for SPARC and PowerPC (faster)
|
||||
- ARM NWFPE support (initial patch by Ulrich Hecht)
|
||||
- added specific x86 to x86 translator (close to native performance
|
||||
in qemu-i386 and qemu-fast)
|
||||
- shm syscalls support (Paul McKerras)
|
||||
- added accurate CR0.MP/ME/TS emulation
|
||||
- fixed DMA memory write access (Win95 boot floppy fix)
|
||||
- graphical x86 linux loader
|
||||
- command line monitor
|
||||
- generic removable device support
|
||||
- support of CD-ROM change
|
||||
- multiple network interface support
|
||||
- initial x86-64 host support (Gwenole Beauchesne)
|
||||
- lret to outer priviledge fix (OS/2 install fix)
|
||||
- task switch fixes (SkyOS boot)
|
||||
- VM save/restore commands
|
||||
- new timer API
|
||||
- more precise RTC emulation (periodic timers + time updates)
|
||||
- Win32 port (initial patch by Kazu)
|
||||
|
||||
version 0.5.2:
|
||||
|
||||
- improved soft MMU speed (assembly functions and specializing)
|
||||
- improved multitasking speed by avoiding flushing TBs when
|
||||
switching tasks
|
||||
- improved qemu-fast speed
|
||||
- improved self modifying code handling (big performance gain in
|
||||
softmmu mode).
|
||||
- fixed IO checking
|
||||
- fixed CD-ROM detection (win98 install CD)
|
||||
- fixed addseg real mode bug (GRUB boot fix)
|
||||
- added ROM memory support (win98 boot)
|
||||
- fixed 'call Ev' in case of paging exception
|
||||
- updated the script 'qemu-binfmt-conf.sh' to use QEMU automagically
|
||||
when launching executables for the supported target CPUs.
|
||||
- PowerPC system emulation update (Jocelyn Mayer)
|
||||
- PC floppy emulation and DMA fixes (Jocelyn Mayer)
|
||||
- polled mode for PIC (Jocelyn Mayer)
|
||||
- fixed PTE dirty bit handling
|
||||
- fixed xadd same reg bug
|
||||
- fixed cmpxchg exception safeness
|
||||
- access to virtual memory in gdb stub
|
||||
- task gate and NT flag fixes
|
||||
- eflags optimisation fix for string operations
|
||||
|
||||
version 0.5.1:
|
||||
|
||||
- float access fixes when using soft mmu
|
||||
- PC emulation support on PowerPC
|
||||
- A20 support
|
||||
- IDE CD-ROM emulation
|
||||
- ARM fixes (Ulrich Hecht)
|
||||
- SB16 emulation (malc)
|
||||
- IRET and INT fixes in VM86 mode with IOPL=3
|
||||
- Port I/Os use TSS io map
|
||||
- Full task switching/task gate support
|
||||
- added verr, verw, arpl, fcmovxx
|
||||
- PowerPC target support (Jocelyn Mayer)
|
||||
- Major SPARC target fixes (dynamically linked programs begin to work)
|
||||
|
||||
version 0.5.0:
|
||||
|
||||
- full hardware level VGA emulation
|
||||
- graphical display with SDL
|
||||
- added PS/2 mouse and keyboard emulation
|
||||
- popw (%esp) fix
|
||||
- mov to/from segment data width fix
|
||||
- added real mode support
|
||||
- added Bochs BIOS and LGPL'ed VGA BIOS loader in qemu
|
||||
- m68k host port (Richard Zidlicky)
|
||||
- partial soft MMU support for memory mapped I/Os
|
||||
- multi-target build
|
||||
- fixed: no error code in hardware interrupts
|
||||
- fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn
|
||||
- correct single stepping thru string operations
|
||||
- preliminary SPARC target support (Thomas M. Ogrisegg)
|
||||
- tun-fd option (Rusty Russell)
|
||||
- automatic IDE geometry detection
|
||||
- renamed 'vl' to qemu[-fast] and user qemu to qemu-{cpu}.
|
||||
- added man page
|
||||
- added full soft mmu mode to launch unpatched OSes.
|
||||
|
||||
version 0.4.3:
|
||||
|
||||
- x86 exception fix in case of nop instruction.
|
||||
- gcc 3.2.2 bug workaround (RedHat 9 fix)
|
||||
- sparc and Alpha host fixes
|
||||
- many ARM target fixes: 'ls' and 'bash' can be launched.
|
||||
|
||||
version 0.4.2:
|
||||
|
||||
- many exception handling fixes (can compile a Linux kernel inside vl)
|
||||
- IDE emulation support
|
||||
- initial GDB stub support
|
||||
- deferred update support for disk images (Rusty Russell)
|
||||
- accept User Mode Linux Copy On Write disk images
|
||||
- SMP kernels can at least be booted
|
||||
|
||||
version 0.4.1:
|
||||
|
||||
- more accurate timer support in vl.
|
||||
- more reliable NE2000 probe in vl.
|
||||
- added 2.5.66 kernel in vl-test.
|
||||
- added VLTMPDIR environment variable in vl.
|
||||
|
||||
version 0.4:
|
||||
|
||||
- initial support for ring 0 x86 processor emulation
|
||||
- fixed signal handling for correct dosemu DPMI emulation
|
||||
- fast x86 MMU emulation with mmap()
|
||||
- fixed popl (%esp) case
|
||||
- Linux kernel can be executed by QEMU with the 'vl' command.
|
||||
|
||||
version 0.3:
|
||||
|
||||
- initial support for ARM emulation
|
||||
- added fnsave, frstor, fnstenv, fldenv FPU instructions
|
||||
- added FPU register save in signal emulation
|
||||
- initial ARM port
|
||||
- Sparc and Alpha ports work on the regression test
|
||||
- generic ioctl number conversion
|
||||
- fixed ioctl type conversion
|
||||
|
||||
version 0.2:
|
||||
|
||||
- PowerPC disassembly and ELF symbols output (Rusty Russell)
|
||||
- flock support (Rusty Russell)
|
||||
- ugetrlimit support (Rusty Russell)
|
||||
- fstat64 fix (Rusty Russell)
|
||||
- initial Alpha port (Falk Hueffner)
|
||||
- initial IA64 port (Matt Wilson)
|
||||
- initial Sparc and Sparc64 port (David S. Miller)
|
||||
- added HLT instruction
|
||||
- LRET instruction fix.
|
||||
- added GPF generation for I/Os.
|
||||
- added INT3 and TF flag support.
|
||||
- SHL instruction C flag fix.
|
||||
- mmap emulation for host page size > 4KB
|
||||
- self-modifying code support
|
||||
- better VM86 support (dosemu works on non trivial programs)
|
||||
- precise exception support (EIP is computed correctly in most cases)
|
||||
- more precise LDT/GDT/IDT emulation
|
||||
- faster segment load in vm86 mode
|
||||
- direct chaining of basic blocks (faster emulation)
|
||||
|
||||
version 0.1.6:
|
||||
|
||||
- automatic library search system. QEMU can now work with unpatched
|
||||
ELF dynamic loader and libc (Rusty Russell).
|
||||
- ISO C warning fixes (Alistair Strachan)
|
||||
- first self-virtualizable version (works only as long as the
|
||||
translation cache is not flushed)
|
||||
- RH9 fixes
|
||||
|
||||
version 0.1.5:
|
||||
|
||||
- ppc64 support + personality() patch (Rusty Russell)
|
||||
- first Alpha CPU patches (Falk Hueffner)
|
||||
- removed bfd.h dependancy
|
||||
- fixed shrd, shld, idivl and divl on PowerPC.
|
||||
- fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC).
|
||||
|
||||
version 0.1.4:
|
||||
|
||||
- more accurate VM86 emulation (can launch small DOS 16 bit
|
||||
executables in wine).
|
||||
- fixed push/pop fs/gs
|
||||
- added iret instruction.
|
||||
- added times() syscall and SIOCATMARK ioctl.
|
||||
|
||||
version 0.1.3:
|
||||
|
||||
- S390 support (Ulrich Weigand)
|
||||
- glibc 2.3.x compile fix (Ulrich Weigand)
|
||||
- socketcall endian fix (Ulrich Weigand)
|
||||
- struct sockaddr endian fix (Ulrich Weigand)
|
||||
- sendmsg/recvmsg endian fix (Ulrich Weigand)
|
||||
- execve endian fix (Ulrich Weigand)
|
||||
- fdset endian fix (Ulrich Weigand)
|
||||
- partial setsockopt syscall support (Ulrich Weigand)
|
||||
- more accurate pushf/popf emulation
|
||||
- first partial vm86() syscall support (can be used with runcom example).
|
||||
- added bound, cmpxchg8b, cpuid instructions
|
||||
- added 16 bit addressing support/override for string operations
|
||||
- poll() fix
|
||||
|
||||
version 0.1.2:
|
||||
|
||||
- compile fixes
|
||||
- xlat instruction
|
||||
- xchg instruction memory lock
|
||||
- added simple vm86 example (not working with QEMU yet). The 54 byte
|
||||
DOS executable 'pi_10.com' program was released by Bertram
|
||||
Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html).
|
||||
|
||||
version 0.1.1:
|
||||
|
||||
- glibc 2.2 compilation fixes
|
||||
- added -s and -L options
|
||||
- binary distribution of x86 glibc and wine
|
||||
- big endian fixes in ELF loader and getdents.
|
||||
|
||||
version 0.1:
|
||||
|
||||
- initial public release.
|
||||
@@ -0,0 +1,15 @@
|
||||
The following points clarify the QEMU license:
|
||||
|
||||
1) QEMU as a whole is released under the GNU General Public License
|
||||
|
||||
2) Parts of QEMU have specific licenses which are compatible with the
|
||||
GNU General Public License. Hence each source file contains its own
|
||||
licensing information.
|
||||
|
||||
In particular, the QEMU virtual CPU core library (libqemu.a) is
|
||||
released under the GNU Lesser General Public License. Many hardware
|
||||
device emulation sources are released under the BSD license.
|
||||
|
||||
3) QEMU is a trademark of Fabrice Bellard.
|
||||
|
||||
Fabrice Bellard.
|
||||
@@ -1,36 +1,174 @@
|
||||
CFLAGS=-Wall -O2 -g
|
||||
LDFLAGS=-g
|
||||
DEFINES=-D_GNU_SOURCE -DGEMU -DDOSEMU #-DNO_TRACE_MSGS
|
||||
# Makefile for QEMU.
|
||||
|
||||
OBJS= i386/fp87.o i386/interp_main.o i386/interp_modrm.o i386/interp_16_32.o \
|
||||
i386/interp_32_16.o i386/interp_32_32.o i386/emu-utils.o \
|
||||
i386/dis8086.o i386/emu-ldt.o
|
||||
OBJS+= elfload.o main.o thunk.o syscall.o
|
||||
include config-host.mak
|
||||
|
||||
SRCS = $(OBJS:.o=.c)
|
||||
.PHONY: all clean distclean dvi info install install-doc tar tarbin \
|
||||
speed test test2 html dvi info
|
||||
|
||||
all: gemu
|
||||
BASE_CFLAGS=
|
||||
BASE_LDFLAGS=
|
||||
|
||||
gemu: $(OBJS)
|
||||
$(CC) -Wl,-T,i386.ld $(LDFLAGS) -o $@ $(OBJS)
|
||||
BASE_CFLAGS += $(OS_CFLAGS)
|
||||
ifeq ($(ARCH),sparc)
|
||||
BASE_CFLAGS += -mcpu=ultrasparc
|
||||
endif
|
||||
CPPFLAGS += -I. -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
LIBS=
|
||||
TOOLS=qemu-img$(EXESUF)
|
||||
ifdef CONFIG_STATIC
|
||||
BASE_LDFLAGS += -static
|
||||
endif
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1
|
||||
else
|
||||
DOCS=
|
||||
endif
|
||||
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $^ 1>.depend
|
||||
ifndef CONFIG_DARWIN
|
||||
ifndef CONFIG_WIN32
|
||||
ifndef CONFIG_SOLARIS
|
||||
LIBS+=-lrt
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
all: $(TOOLS) $(DOCS) recurse-all
|
||||
|
||||
subdir-%: dyngen$(EXESUF)
|
||||
$(MAKE) -C $(subst subdir-,,$@) all
|
||||
|
||||
recurse-all: $(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.c cutils.c block.c block-raw.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c block-qcow2.c
|
||||
$(CC) -DQEMU_TOOL $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $(LDFLAGS) $(BASE_LDFLAGS) -o $@ $^ -lz $(LIBS)
|
||||
|
||||
dyngen$(EXESUF): dyngen.c
|
||||
$(HOST_CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ i386/*.o i386/*~ gemu hello test1 test2 TAGS
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f *.o *.a $(TOOLS) dyngen$(EXESUF) TAGS *.pod *~ */*~
|
||||
$(MAKE) -C tests clean
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
|
||||
hello: hello.c
|
||||
$(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
distclean: clean
|
||||
rm -f config-host.mak config-host.h $(DOCS)
|
||||
rm -f qemu-{doc,tech}.{info,aux,cp,dvi,fn,info,ky,log,pg,toc,tp,vr}
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
|
||||
test1: test1.c
|
||||
$(CC) $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \
|
||||
ar de en-us fi fr-be hr it lv nl pl ru th \
|
||||
common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
|
||||
|
||||
test2: test2.c
|
||||
$(CC) $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
install-doc: $(DOCS)
|
||||
mkdir -p "$(DESTDIR)$(docdir)"
|
||||
$(INSTALL) -m 644 qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)"
|
||||
ifndef CONFIG_WIN32
|
||||
mkdir -p "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
|
||||
install: all $(if $(BUILD_DOCS),install-doc)
|
||||
mkdir -p "$(DESTDIR)$(bindir)"
|
||||
$(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)"
|
||||
mkdir -p "$(DESTDIR)$(datadir)"
|
||||
for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
|
||||
video.x openbios-sparc32 linux_boot.bin pxe-ne2k_pci.bin \
|
||||
pxe-rtl8139.bin pxe-pcnet.bin; do \
|
||||
$(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
|
||||
done
|
||||
ifndef CONFIG_WIN32
|
||||
mkdir -p "$(DESTDIR)$(datadir)/keymaps"
|
||||
for x in $(KEYMAPS); do \
|
||||
$(INSTALL) -m 644 $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
|
||||
done
|
||||
endif
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
|
||||
# various test targets
|
||||
test speed test2: all
|
||||
$(MAKE) -C tests $@
|
||||
|
||||
TAGS:
|
||||
etags *.[ch] tests/*.[ch]
|
||||
|
||||
cscope:
|
||||
rm -f ./cscope.*
|
||||
find . -name "*.[ch]" -print > ./cscope.files
|
||||
cscope -b
|
||||
|
||||
# documentation
|
||||
%.html: %.texi
|
||||
texi2html -monolithic -number $<
|
||||
|
||||
%.info: %.texi
|
||||
makeinfo $< -o $@
|
||||
|
||||
%.dvi: %.texi
|
||||
texi2dvi $<
|
||||
|
||||
qemu.1: qemu-doc.texi
|
||||
$(SRC_PATH)/texi2pod.pl $< qemu.pod
|
||||
pod2man --section=1 --center=" " --release=" " qemu.pod > $@
|
||||
|
||||
qemu-img.1: qemu-img.texi
|
||||
$(SRC_PATH)/texi2pod.pl $< qemu-img.pod
|
||||
pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@
|
||||
|
||||
info: qemu-doc.info qemu-tech.info
|
||||
|
||||
dvi: qemu-doc.dvi qemu-tech.dvi
|
||||
|
||||
html: qemu-doc.html qemu-tech.html
|
||||
|
||||
VERSION ?= $(shell cat VERSION)
|
||||
FILE = qemu-$(VERSION)
|
||||
|
||||
# tar release (use 'make -k tar' on a checkouted tree)
|
||||
tar:
|
||||
rm -rf /tmp/$(FILE)
|
||||
cp -r . /tmp/$(FILE)
|
||||
( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS )
|
||||
rm -rf /tmp/$(FILE)
|
||||
|
||||
# generate a binary distribution
|
||||
tarbin:
|
||||
( cd / ; tar zcvf ~/qemu-$(VERSION)-i386.tar.gz \
|
||||
$(bindir)/qemu \
|
||||
$(bindir)/qemu-system-ppc \
|
||||
$(bindir)/qemu-system-sparc \
|
||||
$(bindir)/qemu-system-x86_64 \
|
||||
$(bindir)/qemu-system-mips \
|
||||
$(bindir)/qemu-system-mipsel \
|
||||
$(bindir)/qemu-system-arm \
|
||||
$(bindir)/qemu-i386 \
|
||||
$(bindir)/qemu-arm \
|
||||
$(bindir)/qemu-armeb \
|
||||
$(bindir)/qemu-sparc \
|
||||
$(bindir)/qemu-ppc \
|
||||
$(bindir)/qemu-mips \
|
||||
$(bindir)/qemu-mipsel \
|
||||
$(bindir)/qemu-img \
|
||||
$(datadir)/bios.bin \
|
||||
$(datadir)/vgabios.bin \
|
||||
$(datadir)/vgabios-cirrus.bin \
|
||||
$(datadir)/ppc_rom.bin \
|
||||
$(datadir)/video.x \
|
||||
$(datadir)/openbios-sparc32 \
|
||||
$(datadir)/linux_boot.bin \
|
||||
$(datadir)/pxe-ne2k_pci.bin \
|
||||
$(datadir)/pxe-rtl8139.bin \
|
||||
$(datadir)/pxe-pcnet.bin \
|
||||
$(docdir)/qemu-doc.html \
|
||||
$(docdir)/qemu-tech.html \
|
||||
$(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 )
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
|
||||
+590
@@ -0,0 +1,590 @@
|
||||
include config.mak
|
||||
|
||||
TARGET_BASE_ARCH:=$(TARGET_ARCH)
|
||||
ifeq ($(TARGET_ARCH), x86_64)
|
||||
TARGET_BASE_ARCH:=i386
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), ppc64)
|
||||
TARGET_BASE_ARCH:=ppc
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
TARGET_BASE_ARCH:=sparc
|
||||
endif
|
||||
TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
|
||||
VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio
|
||||
CPPFLAGS=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH)
|
||||
ifdef CONFIG_DARWIN_USER
|
||||
VPATH+=:$(SRC_PATH)/darwin-user
|
||||
CPPFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH)
|
||||
endif
|
||||
ifdef CONFIG_LINUX_USER
|
||||
VPATH+=:$(SRC_PATH)/linux-user
|
||||
CPPFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH)
|
||||
endif
|
||||
BASE_CFLAGS=
|
||||
BASE_LDFLAGS=
|
||||
#CFLAGS+=-Werror
|
||||
LIBS=
|
||||
HELPER_CFLAGS=$(CFLAGS)
|
||||
DYNGEN=../dyngen$(EXESUF)
|
||||
# user emulator name
|
||||
TARGET_ARCH2=$(TARGET_ARCH)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=armeb
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),sh4)
|
||||
ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=sh4eb
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH),mips)
|
||||
ifneq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
TARGET_ARCH2=mipsel
|
||||
endif
|
||||
endif
|
||||
QEMU_USER=qemu-$(TARGET_ARCH2)
|
||||
# system emulator name
|
||||
ifdef CONFIG_SOFTMMU
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
QEMU_SYSTEM=qemu$(EXESUF)
|
||||
else
|
||||
QEMU_SYSTEM=qemu-system-$(TARGET_ARCH2)$(EXESUF)
|
||||
endif
|
||||
else
|
||||
QEMU_SYSTEM=qemu-fast
|
||||
endif
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
PROGS=$(QEMU_USER)
|
||||
else
|
||||
PROGS+=$(QEMU_SYSTEM)
|
||||
ifndef CONFIG_SOFTMMU
|
||||
CONFIG_STATIC=y
|
||||
endif
|
||||
endif # !CONFIG_USER_ONLY
|
||||
|
||||
ifdef CONFIG_STATIC
|
||||
BASE_LDFLAGS+=-static
|
||||
endif
|
||||
|
||||
# We require -O2 to avoid the stack setup prologue in EXIT_TB
|
||||
OP_CFLAGS = -Wall -O2 -g -fno-strict-aliasing
|
||||
|
||||
ifeq ($(ARCH),i386)
|
||||
HELPER_CFLAGS+=-fomit-frame-pointer
|
||||
OP_CFLAGS+=-mpreferred-stack-boundary=2 -fomit-frame-pointer
|
||||
ifeq ($(HAVE_GCC3_OPTIONS),yes)
|
||||
OP_CFLAGS+= -falign-functions=0 -fno-gcse
|
||||
else
|
||||
OP_CFLAGS+= -malign-functions=0
|
||||
endif
|
||||
ifdef TARGET_GPROF
|
||||
USE_I386_LD=y
|
||||
endif
|
||||
ifdef CONFIG_STATIC
|
||||
USE_I386_LD=y
|
||||
endif
|
||||
ifdef USE_I386_LD
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
else
|
||||
ifdef CONFIG_LINUX_USER
|
||||
# WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object
|
||||
# that the kernel ELF loader considers as an executable. I think this
|
||||
# is the simplest way to make it self virtualizable!
|
||||
BASE_LDFLAGS+=-Wl,-shared
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),x86_64)
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc)
|
||||
CPPFLAGS+= -D__powerpc__
|
||||
ifdef CONFIG_LINUX_USER
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),s390)
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc)
|
||||
ifeq ($(CONFIG_SOLARIS),yes)
|
||||
BASE_CFLAGS+=-mcpu=ultrasparc -m32 -ffixed-g2 -ffixed-g3
|
||||
BASE_LDFLAGS+=-m32
|
||||
OP_CFLAGS+=-fno-delayed-branch -fno-omit-frame-pointer -ffixed-i0
|
||||
else
|
||||
BASE_CFLAGS+=-mcpu=ultrasparc -m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
|
||||
BASE_LDFLAGS+=-m32
|
||||
OP_CFLAGS+=-fno-delayed-branch -ffixed-i0
|
||||
HELPER_CFLAGS=$(CFLAGS) -ffixed-i0 -mflat
|
||||
# -static is used to avoid g1/g3 usage by the dynamic linker
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld -static
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc64)
|
||||
BASE_CFLAGS+=-mcpu=ultrasparc -m64 -ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7
|
||||
BASE_LDFLAGS+=-m64
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
OP_CFLAGS+=-mcpu=ultrasparc -m64 -ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7 -fno-delayed-branch -ffixed-i0
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),alpha)
|
||||
# -msmall-data is not used for OP_CFLAGS because we want two-instruction
|
||||
# relocations for the constant constructions
|
||||
# Ensure there's only a single GP
|
||||
BASE_CFLAGS+=-msmall-data
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
BASE_CFLAGS+=-mno-sdata
|
||||
OP_CFLAGS+=-mno-sdata
|
||||
BASE_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),arm)
|
||||
OP_CFLAGS+=-mno-sched-prolog -fno-omit-frame-pointer
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),m68k)
|
||||
OP_CFLAGS+=-fomit-frame-pointer
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),mips)
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_GCC3_OPTIONS),yes)
|
||||
# very important to generate a return at the end of every operation
|
||||
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DARWIN),yes)
|
||||
LIBS+=-lmx
|
||||
endif
|
||||
|
||||
ifdef CONFIG_DARWIN_USER
|
||||
# Leave some space for the regular program loading zone
|
||||
BASE_LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
|
||||
endif
|
||||
|
||||
OP_CFLAGS+=$(OS_CFLAGS)
|
||||
|
||||
#########################################################
|
||||
|
||||
CPPFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
LIBS+=-lm
|
||||
ifndef CONFIG_USER_ONLY
|
||||
LIBS+=-lz
|
||||
endif
|
||||
ifdef CONFIG_WIN32
|
||||
LIBS+=-lwinmm -lws2_32 -liphlpapi
|
||||
endif
|
||||
ifdef CONFIG_SOLARIS
|
||||
LIBS+=-lsocket -lnsl -lresolv
|
||||
endif
|
||||
|
||||
# profiling code
|
||||
ifdef TARGET_GPROF
|
||||
BASE_LDFLAGS+=-p
|
||||
main.o: BASE_CFLAGS+=-p
|
||||
endif
|
||||
|
||||
ifdef CONFIG_LINUX_USER
|
||||
OBJS= main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o \
|
||||
elfload.o linuxload.o
|
||||
ifdef TARGET_HAS_BFLT
|
||||
OBJS+= flatload.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
OBJS+= vm86.o
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), arm)
|
||||
OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \
|
||||
nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \
|
||||
nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), m68k)
|
||||
OBJS+= m68k-sim.o m68k-semi.o
|
||||
endif
|
||||
endif #CONFIG_LINUX_USER
|
||||
|
||||
ifdef CONFIG_DARWIN_USER
|
||||
OBJS= main.o commpage.o machload.o mmap.o osdep.o signal.o syscall.o thunk.o
|
||||
endif
|
||||
|
||||
SRCS:= $(OBJS:.o=.c)
|
||||
OBJS+= libqemu.a
|
||||
|
||||
# cpu emulator library
|
||||
LIBOBJS=exec.o kqemu.o translate-op.o translate-all.o cpu-exec.o\
|
||||
translate.o op.o
|
||||
ifdef CONFIG_SOFTFLOAT
|
||||
LIBOBJS+=fpu/softfloat.o
|
||||
else
|
||||
LIBOBJS+=fpu/softfloat-native.o
|
||||
endif
|
||||
CPPFLAGS+=-I$(SRC_PATH)/fpu
|
||||
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
LIBOBJS+=helper.o helper2.o
|
||||
ifeq ($(ARCH), i386)
|
||||
LIBOBJS+=translate-copy.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), x86_64)
|
||||
LIBOBJS+=helper.o helper2.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), ppc)
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), mips)
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), arm)
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), sh4)
|
||||
LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), m68k)
|
||||
LIBOBJS+= helper.o
|
||||
endif
|
||||
|
||||
# NOTE: the disassembler code is only needed for debugging
|
||||
LIBOBJS+=disas.o
|
||||
ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386)
|
||||
USE_I386_DIS=y
|
||||
endif
|
||||
ifeq ($(findstring x86_64, $(TARGET_ARCH) $(ARCH)),x86_64)
|
||||
USE_I386_DIS=y
|
||||
endif
|
||||
ifdef USE_I386_DIS
|
||||
LIBOBJS+=i386-dis.o
|
||||
endif
|
||||
ifeq ($(findstring alpha, $(TARGET_ARCH) $(ARCH)),alpha)
|
||||
LIBOBJS+=alpha-dis.o
|
||||
endif
|
||||
ifeq ($(findstring ppc, $(TARGET_BASE_ARCH) $(ARCH)),ppc)
|
||||
LIBOBJS+=ppc-dis.o
|
||||
endif
|
||||
ifeq ($(findstring mips, $(TARGET_ARCH) $(ARCH)),mips)
|
||||
LIBOBJS+=mips-dis.o
|
||||
endif
|
||||
ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc)
|
||||
LIBOBJS+=sparc-dis.o
|
||||
endif
|
||||
ifeq ($(findstring arm, $(TARGET_ARCH) $(ARCH)),arm)
|
||||
LIBOBJS+=arm-dis.o
|
||||
endif
|
||||
ifeq ($(findstring m68k, $(TARGET_ARCH) $(ARCH)),m68k)
|
||||
LIBOBJS+=m68k-dis.o
|
||||
endif
|
||||
ifeq ($(findstring sh4, $(TARGET_ARCH) $(ARCH)),sh4)
|
||||
LIBOBJS+=sh4-dis.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_GDBSTUB
|
||||
OBJS+=gdbstub.o
|
||||
endif
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
$(QEMU_USER): $(OBJS)
|
||||
$(CC) $(LDFLAGS) $(BASE_LDFLAGS) -o $@ $^ $(LIBS)
|
||||
ifeq ($(ARCH),alpha)
|
||||
# Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of
|
||||
# the address space (31 bit so sign extending doesn't matter)
|
||||
echo -ne '\001\000\000\000' | dd of=qemu bs=1 seek=48 count=4 conv=notrunc
|
||||
endif
|
||||
|
||||
# must use static linking to avoid leaving stuff in virtual address space
|
||||
VL_OBJS=vl.o osdep.o readline.o monitor.o pci.o console.o loader.o isa_mmio.o
|
||||
VL_OBJS+=cutils.o
|
||||
VL_OBJS+=block.o block-raw.o
|
||||
VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o block-qcow2.o
|
||||
ifdef CONFIG_WIN32
|
||||
VL_OBJS+=tap-win32.o
|
||||
endif
|
||||
|
||||
SOUND_HW = sb16.o es1370.o
|
||||
AUDIODRV = audio.o noaudio.o wavaudio.o
|
||||
ifdef CONFIG_SDL
|
||||
AUDIODRV += sdlaudio.o
|
||||
endif
|
||||
ifdef CONFIG_OSS
|
||||
AUDIODRV += ossaudio.o
|
||||
endif
|
||||
ifdef CONFIG_COREAUDIO
|
||||
AUDIODRV += coreaudio.o
|
||||
endif
|
||||
ifdef CONFIG_ALSA
|
||||
AUDIODRV += alsaaudio.o
|
||||
LIBS += -lasound
|
||||
endif
|
||||
ifdef CONFIG_DSOUND
|
||||
AUDIODRV += dsoundaudio.o
|
||||
LIBS += -lole32 -ldxguid
|
||||
endif
|
||||
ifdef CONFIG_FMOD
|
||||
AUDIODRV += fmodaudio.o
|
||||
audio.o fmodaudio.o: CPPFLAGS := -I$(CONFIG_FMOD_INC) $(CPPFLAGS)
|
||||
LIBS += $(CONFIG_FMOD_LIB)
|
||||
endif
|
||||
ifdef CONFIG_ADLIB
|
||||
SOUND_HW += fmopl.o adlib.o
|
||||
endif
|
||||
AUDIODRV+= wavcapture.o
|
||||
|
||||
# SCSI layer
|
||||
VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o
|
||||
|
||||
# USB layer
|
||||
VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o
|
||||
|
||||
# PCI network cards
|
||||
VL_OBJS+= ne2000.o rtl8139.o pcnet.o
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
# Hardware support
|
||||
VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
|
||||
VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o acpi.o piix_pci.o
|
||||
VL_OBJS+= usb-uhci.o smbus_eeprom.o
|
||||
CPPFLAGS += -DHAS_AUDIO
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), ppc)
|
||||
VL_OBJS+= ppc.o ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
|
||||
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o
|
||||
VL_OBJS+= grackle_pci.o prep_pci.o unin_pci.o
|
||||
CPPFLAGS += -DHAS_AUDIO
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), mips)
|
||||
VL_OBJS+= mips_r4k.o mips_malta.o mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o
|
||||
VL_OBJS+= ide.o gt64xxx.o pckbd.o ps2.o fdc.o mc146818rtc.o usb-uhci.o acpi.o
|
||||
VL_OBJS+= piix_pci.o parallel.o mixeng.o cirrus_vga.o $(SOUND_HW) $(AUDIODRV)
|
||||
DEFINES += -DHAS_AUDIO
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
ifeq ($(TARGET_ARCH), sparc64)
|
||||
VL_OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o apb_pci.o
|
||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o
|
||||
VL_OBJS+= cirrus_vga.o parallel.o
|
||||
else
|
||||
VL_OBJS+= sun4m.o tcx.o pcnet.o iommu.o m48t59.o slavio_intctl.o
|
||||
VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o sparc32_dma.o
|
||||
VL_OBJS+= cs4231.o
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), arm)
|
||||
VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
|
||||
VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o
|
||||
VL_OBJS+= versatile_pci.o
|
||||
VL_OBJS+= arm_gic.o realview.o arm_sysctl.o
|
||||
VL_OBJS+= arm-semi.o
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), sh4)
|
||||
VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o
|
||||
endif
|
||||
ifdef CONFIG_GDBSTUB
|
||||
VL_OBJS+=gdbstub.o
|
||||
endif
|
||||
ifdef CONFIG_SDL
|
||||
VL_OBJS+=sdl.o x_keymap.o
|
||||
endif
|
||||
VL_OBJS+=vnc.o
|
||||
ifdef CONFIG_COCOA
|
||||
VL_OBJS+=cocoa.o
|
||||
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
|
||||
ifdef CONFIG_COREAUDIO
|
||||
COCOA_LIBS+=-framework CoreAudio
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_SLIRP
|
||||
CPPFLAGS+=-I$(SRC_PATH)/slirp
|
||||
SLIRP_OBJS=cksum.o if.o ip_icmp.o ip_input.o ip_output.o \
|
||||
slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o \
|
||||
tcp_subr.o tcp_timer.o udp.o bootp.o debug.o tftp.o
|
||||
VL_OBJS+=$(addprefix slirp/, $(SLIRP_OBJS))
|
||||
endif
|
||||
|
||||
VL_LDFLAGS=
|
||||
# specific flags are needed for non soft mmu emulator
|
||||
ifdef CONFIG_STATIC
|
||||
VL_LDFLAGS+=-static
|
||||
endif
|
||||
ifndef CONFIG_SOFTMMU
|
||||
VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld
|
||||
endif
|
||||
ifndef CONFIG_DARWIN
|
||||
ifndef CONFIG_WIN32
|
||||
ifndef CONFIG_SOLARIS
|
||||
VL_LIBS=-lutil -lrt
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifdef TARGET_GPROF
|
||||
vl.o: BASE_CFLAGS+=-p
|
||||
VL_LDFLAGS+=-p
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc64)
|
||||
VL_LDFLAGS+=-m64
|
||||
VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc64.ld
|
||||
endif
|
||||
|
||||
ifdef CONFIG_WIN32
|
||||
SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
|
||||
endif
|
||||
|
||||
$(QEMU_SYSTEM): $(VL_OBJS) libqemu.a
|
||||
$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
|
||||
|
||||
cocoa.o: cocoa.m
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
sdl.o: sdl.c keymaps.c sdl_keysym.h
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
sdlaudio.o: sdlaudio.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend
|
||||
|
||||
vldepend: $(VL_OBJS:.o=.c)
|
||||
$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend
|
||||
|
||||
# libqemu
|
||||
|
||||
libqemu.a: $(LIBOBJS)
|
||||
rm -f $@
|
||||
$(AR) rcs $@ $(LIBOBJS)
|
||||
|
||||
translate.o: translate.c gen-op.h opc.h cpu.h
|
||||
|
||||
translate-all.o: translate-all.c opc.h cpu.h
|
||||
|
||||
translate-op.o: translate-all.c op.h opc.h cpu.h
|
||||
|
||||
op.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -o $@ $<
|
||||
|
||||
opc.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -c -o $@ $<
|
||||
|
||||
gen-op.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -g -o $@ $<
|
||||
|
||||
op.o: op.c
|
||||
$(CC) $(OP_CFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
# HELPER_CFLAGS is used for all the code compiled with static register
|
||||
# variables
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
# XXX: rename helper.c to op_helper.c
|
||||
helper.o: helper.c
|
||||
$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
else
|
||||
op_helper.o: op_helper.c
|
||||
$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
endif
|
||||
|
||||
cpu-exec.o: cpu-exec.c
|
||||
$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
# Note: this is a workaround. The real fix is to avoid compiling
|
||||
# cpu_signal_handler() in cpu-exec.c.
|
||||
signal.o: signal.c
|
||||
$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
op.o: op.c opreg_template.h ops_template.h ops_template_mem.h ops_mem.h ops_sse.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), arm)
|
||||
op.o: op.c op_template.h
|
||||
pl110.o: pl110_template.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
op.o: op.c op_template.h op_mem.h fop_template.h fbranch_template.h
|
||||
magic_load.o: elf_op.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), ppc)
|
||||
op.o: op.c op_template.h op_mem.h
|
||||
op_helper.o: op_helper_mem.h
|
||||
translate.o: translate.c translate_init.c
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), mips)
|
||||
op.o: op.c op_template.c fop_template.c op_mem.c
|
||||
op_helper.o: op_helper_mem.c
|
||||
endif
|
||||
|
||||
loader.o: loader.c elf_ops.h
|
||||
|
||||
ifeq ($(TARGET_ARCH), sh4)
|
||||
op.o: op.c op_mem.c cpu.h
|
||||
op_helper.o: op_helper.c exec.h cpu.h
|
||||
helper.o: helper.c exec.h cpu.h
|
||||
sh7750.o: sh7750.c sh7750_regs.h sh7750_regnames.h cpu.h
|
||||
shix.o: shix.c sh7750_regs.h sh7750_regnames.h
|
||||
sh7750_regnames.o: sh7750_regnames.c sh7750_regnames.h sh7750_regs.h
|
||||
tc58128.o: tc58128.c
|
||||
endif
|
||||
|
||||
$(OBJS) $(LIBOBJS) $(VL_OBJS): config.h ../config-host.h
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.S
|
||||
$(CC) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o slirp/*.o fpu/*.o
|
||||
|
||||
install: all
|
||||
ifneq ($(PROGS),)
|
||||
$(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
endif
|
||||
|
||||
ifeq (1, 0)
|
||||
audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \
|
||||
fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \
|
||||
CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare
|
||||
endif
|
||||
@@ -1,2 +1,55 @@
|
||||
- swap all elf paramters
|
||||
- fix printf for doubles (fp87.c bug ?)
|
||||
short term:
|
||||
----------
|
||||
- cycle counter for all archs
|
||||
- cpu_interrupt() win32/SMP fix
|
||||
- support variable tsc freq
|
||||
- USB host async
|
||||
- IDE async
|
||||
- debug option in 'configure' script + disable -fomit-frame-pointer
|
||||
- Precise VGA timings for old games/demos (malc patch)
|
||||
- merge PIC spurious interrupt patch
|
||||
- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?)
|
||||
- config file (at least for windows/Mac OS X)
|
||||
- update doc: PCI infos.
|
||||
- basic VGA optimizations
|
||||
- better code fetch (different exception handling + CS.limit support)
|
||||
- do not resize vga if invalid size.
|
||||
- avoid looping if only exceptions
|
||||
- TLB code protection support for PPC
|
||||
- see openMosix Doc
|
||||
- disable SMC handling for ARM/SPARC/PPC (not finished)
|
||||
- see undefined flags for BTx insn
|
||||
- user/kernel PUSHL/POPL in helper.c
|
||||
- keyboard output buffer filling timing emulation
|
||||
- return UD exception if LOCK prefix incorrectly used
|
||||
- test ldt limit < 7 ?
|
||||
- tests for each target CPU
|
||||
- fix CCOP optimisation
|
||||
- fix all remaining thread lock issues (must put TBs in a specific invalid
|
||||
state, find a solution for tb_flush()).
|
||||
|
||||
ppc specific:
|
||||
------------
|
||||
- TLB invalidate not needed if msr_pr changes
|
||||
- enable shift optimizations ?
|
||||
|
||||
linux-user specific:
|
||||
-------------------
|
||||
- add IPC syscalls
|
||||
- handle rare page fault cases (in particular if page fault in helpers or
|
||||
in syscall emulation code).
|
||||
- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit
|
||||
issues, fix 16 bit uid issues)
|
||||
- use page_unprotect_range in every suitable syscall to handle all
|
||||
cases of self modifying code.
|
||||
- fix thread stack freeing (use kernel 2.5.x CLONE_CHILD_CLEARTID)
|
||||
- use kernel traps for unaligned accesses on ARM ?
|
||||
|
||||
|
||||
lower priority:
|
||||
--------------
|
||||
- int15 ah=86: use better timing
|
||||
- suppress shift_mem ops
|
||||
- fix some 16 bit sp push/pop overflow (pusha/popa, lcall lret)
|
||||
- optimize FPU operations (evaluate x87 stack pointer statically)
|
||||
- use -msoft-float on ARM
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
/* a.out.h
|
||||
|
||||
Copyright 1997, 1998, 1999, 2001 Red Hat, Inc.
|
||||
|
||||
This file is part of Cygwin.
|
||||
|
||||
This software is a copyrighted work licensed under the terms of the
|
||||
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||
details. */
|
||||
|
||||
#ifndef _A_OUT_H_
|
||||
#define _A_OUT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define COFF_IMAGE_WITH_PE
|
||||
#define COFF_LONG_SECTION_NAMES
|
||||
|
||||
/*** coff information for Intel 386/486. */
|
||||
|
||||
|
||||
/********************** FILE HEADER **********************/
|
||||
|
||||
struct external_filehdr {
|
||||
short f_magic; /* magic number */
|
||||
short f_nscns; /* number of sections */
|
||||
unsigned long f_timdat; /* time & date stamp */
|
||||
unsigned long f_symptr; /* file pointer to symtab */
|
||||
unsigned long f_nsyms; /* number of symtab entries */
|
||||
short f_opthdr; /* sizeof(optional hdr) */
|
||||
short f_flags; /* flags */
|
||||
};
|
||||
|
||||
/* Bits for f_flags:
|
||||
* F_RELFLG relocation info stripped from file
|
||||
* F_EXEC file is executable (no unresolved external references)
|
||||
* F_LNNO line numbers stripped from file
|
||||
* F_LSYMS local symbols stripped from file
|
||||
* F_AR32WR file has byte ordering of an AR32WR machine (e.g. vax)
|
||||
*/
|
||||
|
||||
#define F_RELFLG (0x0001)
|
||||
#define F_EXEC (0x0002)
|
||||
#define F_LNNO (0x0004)
|
||||
#define F_LSYMS (0x0008)
|
||||
|
||||
|
||||
|
||||
#define I386MAGIC 0x14c
|
||||
#define I386PTXMAGIC 0x154
|
||||
#define I386AIXMAGIC 0x175
|
||||
|
||||
/* This is Lynx's all-platform magic number for executables. */
|
||||
|
||||
#define LYNXCOFFMAGIC 0415
|
||||
|
||||
#define I386BADMAG(x) (((x).f_magic != I386MAGIC) \
|
||||
&& (x).f_magic != I386AIXMAGIC \
|
||||
&& (x).f_magic != I386PTXMAGIC \
|
||||
&& (x).f_magic != LYNXCOFFMAGIC)
|
||||
|
||||
#define FILHDR struct external_filehdr
|
||||
#define FILHSZ 20
|
||||
|
||||
|
||||
/********************** AOUT "OPTIONAL HEADER"=
|
||||
**********************/
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short magic; /* type of file */
|
||||
unsigned short vstamp; /* version stamp */
|
||||
unsigned long tsize; /* text size in bytes, padded to FW bdry*/
|
||||
unsigned long dsize; /* initialized data " " */
|
||||
unsigned long bsize; /* uninitialized data " " */
|
||||
unsigned long entry; /* entry pt. */
|
||||
unsigned long text_start; /* base of text used for this file */
|
||||
unsigned long data_start; /* base of data used for this file=
|
||||
*/
|
||||
}
|
||||
AOUTHDR;
|
||||
|
||||
#define AOUTSZ 28
|
||||
#define AOUTHDRSZ 28
|
||||
|
||||
#define OMAGIC 0404 /* object files, eg as output */
|
||||
#define ZMAGIC 0413 /* demand load format, eg normal ld output */
|
||||
#define STMAGIC 0401 /* target shlib */
|
||||
#define SHMAGIC 0443 /* host shlib */
|
||||
|
||||
|
||||
/* define some NT default values */
|
||||
/* #define NT_IMAGE_BASE 0x400000 moved to internal.h */
|
||||
#define NT_SECTION_ALIGNMENT 0x1000
|
||||
#define NT_FILE_ALIGNMENT 0x200
|
||||
#define NT_DEF_RESERVE 0x100000
|
||||
#define NT_DEF_COMMIT 0x1000
|
||||
|
||||
/********************** SECTION HEADER **********************/
|
||||
|
||||
|
||||
struct external_scnhdr {
|
||||
char s_name[8]; /* section name */
|
||||
unsigned long s_paddr; /* physical address, offset
|
||||
of last addr in scn */
|
||||
unsigned long s_vaddr; /* virtual address */
|
||||
unsigned long s_size; /* section size */
|
||||
unsigned long s_scnptr; /* file ptr to raw data for section */
|
||||
unsigned long s_relptr; /* file ptr to relocation */
|
||||
unsigned long s_lnnoptr; /* file ptr to line numbers */
|
||||
unsigned short s_nreloc; /* number of relocation entries */
|
||||
unsigned short s_nlnno; /* number of line number entries*/
|
||||
unsigned long s_flags; /* flags */
|
||||
};
|
||||
|
||||
#define SCNHDR struct external_scnhdr
|
||||
#define SCNHSZ 40
|
||||
|
||||
/*
|
||||
* names of "special" sections
|
||||
*/
|
||||
#define _TEXT ".text"
|
||||
#define _DATA ".data"
|
||||
#define _BSS ".bss"
|
||||
#define _COMMENT ".comment"
|
||||
#define _LIB ".lib"
|
||||
|
||||
/********************** LINE NUMBERS **********************/
|
||||
|
||||
/* 1 line number entry for every "breakpointable" source line in a section.
|
||||
* Line numbers are grouped on a per function basis; first entry in a function
|
||||
* grouping will have l_lnno = 0 and in place of physical address will be the
|
||||
* symbol table index of the function name.
|
||||
*/
|
||||
struct external_lineno {
|
||||
union {
|
||||
unsigned long l_symndx; /* function name symbol index, iff l_lnno 0 */
|
||||
unsigned long l_paddr; /* (physical) address of line number */
|
||||
} l_addr;
|
||||
unsigned short l_lnno; /* line number */
|
||||
};
|
||||
|
||||
#define LINENO struct external_lineno
|
||||
#define LINESZ 6
|
||||
|
||||
/********************** SYMBOLS **********************/
|
||||
|
||||
#define E_SYMNMLEN 8 /* # characters in a symbol name */
|
||||
#define E_FILNMLEN 14 /* # characters in a file name */
|
||||
#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */
|
||||
|
||||
struct __attribute__((packed)) external_syment
|
||||
{
|
||||
union {
|
||||
char e_name[E_SYMNMLEN];
|
||||
struct {
|
||||
unsigned long e_zeroes;
|
||||
unsigned long e_offset;
|
||||
} e;
|
||||
} e;
|
||||
unsigned long e_value;
|
||||
unsigned short e_scnum;
|
||||
unsigned short e_type;
|
||||
char e_sclass[1];
|
||||
char e_numaux[1];
|
||||
};
|
||||
|
||||
#define N_BTMASK (0xf)
|
||||
#define N_TMASK (0x30)
|
||||
#define N_BTSHFT (4)
|
||||
#define N_TSHIFT (2)
|
||||
|
||||
union external_auxent {
|
||||
struct {
|
||||
unsigned long x_tagndx; /* str, un, or enum tag indx */
|
||||
union {
|
||||
struct {
|
||||
unsigned short x_lnno; /* declaration line number */
|
||||
unsigned short x_size; /* str/union/array size */
|
||||
} x_lnsz;
|
||||
unsigned long x_fsize; /* size of function */
|
||||
} x_misc;
|
||||
union {
|
||||
struct { /* if ISFCN, tag, or .bb */
|
||||
unsigned long x_lnnoptr;/* ptr to fcn line # */
|
||||
unsigned long x_endndx; /* entry ndx past block end */
|
||||
} x_fcn;
|
||||
struct { /* if ISARY, up to 4 dimen. */
|
||||
char x_dimen[E_DIMNUM][2];
|
||||
} x_ary;
|
||||
} x_fcnary;
|
||||
unsigned short x_tvndx; /* tv index */
|
||||
} x_sym;
|
||||
|
||||
union {
|
||||
char x_fname[E_FILNMLEN];
|
||||
struct {
|
||||
unsigned long x_zeroes;
|
||||
unsigned long x_offset;
|
||||
} x_n;
|
||||
} x_file;
|
||||
|
||||
struct {
|
||||
unsigned long x_scnlen; /* section length */
|
||||
unsigned short x_nreloc; /* # relocation entries */
|
||||
unsigned short x_nlinno; /* # line numbers */
|
||||
unsigned long x_checksum; /* section COMDAT checksum */
|
||||
unsigned short x_associated;/* COMDAT associated section index */
|
||||
char x_comdat[1]; /* COMDAT selection number */
|
||||
} x_scn;
|
||||
|
||||
struct {
|
||||
unsigned long x_tvfill; /* tv fill value */
|
||||
unsigned short x_tvlen; /* length of .tv */
|
||||
char x_tvran[2][2]; /* tv range */
|
||||
} x_tv; /* info about .tv section (in auxent of symbol .tv)) */
|
||||
|
||||
};
|
||||
|
||||
#define SYMENT struct external_syment
|
||||
#define SYMESZ 18
|
||||
#define AUXENT union external_auxent
|
||||
#define AUXESZ 18
|
||||
|
||||
#define _ETEXT "etext"
|
||||
|
||||
/********************** RELOCATION DIRECTIVES **********************/
|
||||
|
||||
struct external_reloc {
|
||||
char r_vaddr[4];
|
||||
char r_symndx[4];
|
||||
char r_type[2];
|
||||
};
|
||||
|
||||
#define RELOC struct external_reloc
|
||||
#define RELSZ 10
|
||||
|
||||
/* end of coff/i386.h */
|
||||
|
||||
/* PE COFF header information */
|
||||
|
||||
#ifndef _PE_H
|
||||
#define _PE_H
|
||||
|
||||
/* NT specific file attributes */
|
||||
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
|
||||
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
|
||||
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
|
||||
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
|
||||
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
|
||||
#define IMAGE_FILE_32BIT_MACHINE 0x0100
|
||||
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
|
||||
#define IMAGE_FILE_SYSTEM 0x1000
|
||||
#define IMAGE_FILE_DLL 0x2000
|
||||
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
|
||||
|
||||
/* additional flags to be set for section headers to allow the NT loader to
|
||||
read and write to the section data (to replace the addresses of data in
|
||||
dlls for one thing); also to execute the section in .text's case=
|
||||
*/
|
||||
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
|
||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000
|
||||
|
||||
/*
|
||||
* Section characteristics added for ppc-nt
|
||||
*/
|
||||
|
||||
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved. */
|
||||
|
||||
#define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */
|
||||
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */
|
||||
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */
|
||||
|
||||
#define IMAGE_SCN_LNK_OTHER 0x00000100 /* Reserved. */
|
||||
#define IMAGE_SCN_LNK_INFO 0x00000200 /* Section contains comments or some other type of information. */
|
||||
#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* Section contents will not become part of image. */
|
||||
#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* Section contents comdat. */
|
||||
|
||||
#define IMAGE_SCN_MEM_FARDATA 0x00008000
|
||||
|
||||
#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
|
||||
#define IMAGE_SCN_MEM_16BIT 0x00020000
|
||||
#define IMAGE_SCN_MEM_LOCKED 0x00040000
|
||||
#define IMAGE_SCN_MEM_PRELOAD 0x00080000
|
||||
|
||||
#define IMAGE_SCN_ALIGN_1BYTES 0x00100000
|
||||
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
|
||||
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
|
||||
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
|
||||
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default alignment if no others are specified. */
|
||||
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
|
||||
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
|
||||
|
||||
|
||||
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* Section contains extended relocations. */
|
||||
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* Section is not cachable. */
|
||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* Section is not pageable. */
|
||||
#define IMAGE_SCN_MEM_SHARED 0x10000000 /* Section is shareable. */
|
||||
|
||||
/* COMDAT selection codes. */
|
||||
|
||||
#define IMAGE_COMDAT_SELECT_NODUPLICATES (1) /* Warn if duplicates. */
|
||||
#define IMAGE_COMDAT_SELECT_ANY (2) /* No warning. */
|
||||
#define IMAGE_COMDAT_SELECT_SAME_SIZE (3) /* Warn if different size. */
|
||||
#define IMAGE_COMDAT_SELECT_EXACT_MATCH (4) /* Warn if different. */
|
||||
#define IMAGE_COMDAT_SELECT_ASSOCIATIVE (5) /* Base on other section. */
|
||||
|
||||
/* Magic values that are true for all dos/nt implementations */
|
||||
#define DOSMAGIC 0x5a4d
|
||||
#define NT_SIGNATURE 0x00004550
|
||||
|
||||
/* NT allows long filenames, we want to accommodate this. This may break
|
||||
some of the bfd functions */
|
||||
#undef FILNMLEN
|
||||
#define FILNMLEN 18 /* # characters in a file name */
|
||||
|
||||
|
||||
#ifdef COFF_IMAGE_WITH_PE
|
||||
/* The filehdr is only weired in images */
|
||||
|
||||
#undef FILHDR
|
||||
struct external_PE_filehdr
|
||||
{
|
||||
/* DOS header fields */
|
||||
unsigned short e_magic; /* Magic number, 0x5a4d */
|
||||
unsigned short e_cblp; /* Bytes on last page of file, 0x90 */
|
||||
unsigned short e_cp; /* Pages in file, 0x3 */
|
||||
unsigned short e_crlc; /* Relocations, 0x0 */
|
||||
unsigned short e_cparhdr; /* Size of header in paragraphs, 0x4 */
|
||||
unsigned short e_minalloc; /* Minimum extra paragraphs needed, 0x0 */
|
||||
unsigned short e_maxalloc; /* Maximum extra paragraphs needed, 0xFFFF */
|
||||
unsigned short e_ss; /* Initial (relative) SS value, 0x0 */
|
||||
unsigned short e_sp; /* Initial SP value, 0xb8 */
|
||||
unsigned short e_csum; /* Checksum, 0x0 */
|
||||
unsigned short e_ip; /* Initial IP value, 0x0 */
|
||||
unsigned short e_cs; /* Initial (relative) CS value, 0x0 */
|
||||
unsigned short e_lfarlc; /* File address of relocation table, 0x40 */
|
||||
unsigned short e_ovno; /* Overlay number, 0x0 */
|
||||
char e_res[4][2]; /* Reserved words, all 0x0 */
|
||||
unsigned short e_oemid; /* OEM identifier (for e_oeminfo), 0x0 */
|
||||
unsigned short e_oeminfo; /* OEM information; e_oemid specific, 0x0 */
|
||||
char e_res2[10][2]; /* Reserved words, all 0x0 */
|
||||
unsigned long e_lfanew; /* File address of new exe header, 0x80 */
|
||||
char dos_message[16][4]; /* other stuff, always follow DOS header */
|
||||
unsigned int nt_signature; /* required NT signature, 0x4550 */
|
||||
|
||||
/* From standard header */
|
||||
|
||||
unsigned short f_magic; /* magic number */
|
||||
unsigned short f_nscns; /* number of sections */
|
||||
unsigned long f_timdat; /* time & date stamp */
|
||||
unsigned long f_symptr; /* file pointer to symtab */
|
||||
unsigned long f_nsyms; /* number of symtab entries */
|
||||
unsigned short f_opthdr; /* sizeof(optional hdr) */
|
||||
unsigned short f_flags; /* flags */
|
||||
};
|
||||
|
||||
|
||||
#define FILHDR struct external_PE_filehdr
|
||||
#undef FILHSZ
|
||||
#define FILHSZ 152
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short magic; /* type of file */
|
||||
unsigned short vstamp; /* version stamp */
|
||||
unsigned long tsize; /* text size in bytes, padded to FW bdry*/
|
||||
unsigned long dsize; /* initialized data " " */
|
||||
unsigned long bsize; /* uninitialized data " " */
|
||||
unsigned long entry; /* entry pt. */
|
||||
unsigned long text_start; /* base of text used for this file */
|
||||
unsigned long data_start; /* base of all data used for this file */
|
||||
|
||||
/* NT extra fields; see internal.h for descriptions */
|
||||
unsigned long ImageBase;
|
||||
unsigned long SectionAlignment;
|
||||
unsigned long FileAlignment;
|
||||
unsigned short MajorOperatingSystemVersion;
|
||||
unsigned short MinorOperatingSystemVersion;
|
||||
unsigned short MajorImageVersion;
|
||||
unsigned short MinorImageVersion;
|
||||
unsigned short MajorSubsystemVersion;
|
||||
unsigned short MinorSubsystemVersion;
|
||||
char Reserved1[4];
|
||||
unsigned long SizeOfImage;
|
||||
unsigned long SizeOfHeaders;
|
||||
unsigned long CheckSum;
|
||||
unsigned short Subsystem;
|
||||
unsigned short DllCharacteristics;
|
||||
unsigned long SizeOfStackReserve;
|
||||
unsigned long SizeOfStackCommit;
|
||||
unsigned long SizeOfHeapReserve;
|
||||
unsigned long SizeOfHeapCommit;
|
||||
unsigned long LoaderFlags;
|
||||
unsigned long NumberOfRvaAndSizes;
|
||||
/* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */
|
||||
char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */
|
||||
|
||||
} PEAOUTHDR;
|
||||
|
||||
|
||||
#undef AOUTSZ
|
||||
#define AOUTSZ (AOUTHDRSZ + 196)
|
||||
|
||||
#undef E_FILNMLEN
|
||||
#define E_FILNMLEN 18 /* # characters in a file name */
|
||||
#endif
|
||||
|
||||
/* end of coff/pe.h */
|
||||
|
||||
#define DT_NON (0) /* no derived type */
|
||||
#define DT_PTR (1) /* pointer */
|
||||
#define DT_FCN (2) /* function */
|
||||
#define DT_ARY (3) /* array */
|
||||
|
||||
#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
|
||||
#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
|
||||
#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _A_OUT_H_ */
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef QEMU_AES_H
|
||||
#define QEMU_AES_H
|
||||
|
||||
#define AES_MAXNR 14
|
||||
#define AES_BLOCK_SIZE 16
|
||||
|
||||
struct aes_key_st {
|
||||
uint32_t rd_key[4 *(AES_MAXNR + 1)];
|
||||
int rounds;
|
||||
};
|
||||
typedef struct aes_key_st AES_KEY;
|
||||
|
||||
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
|
||||
AES_KEY *key);
|
||||
int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
|
||||
AES_KEY *key);
|
||||
|
||||
void AES_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const AES_KEY *key);
|
||||
void AES_decrypt(const unsigned char *in, unsigned char *out,
|
||||
const AES_KEY *key);
|
||||
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const unsigned long length, const AES_KEY *key,
|
||||
unsigned char *ivec, const int enc);
|
||||
|
||||
#endif
|
||||
+1960
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
OUTPUT_FORMAT("elf64-alpha", "elf64-alpha",
|
||||
"elf64-alpha")
|
||||
OUTPUT_ARCH(alpha)
|
||||
ENTRY(__start)
|
||||
SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
. = 0x60000000 + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
.hash : { *(.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.rel.text :
|
||||
{ *(.rel.text) *(.rel.gnu.linkonce.t*) }
|
||||
.rela.text :
|
||||
{ *(.rela.text) *(.rela.gnu.linkonce.t*) }
|
||||
.rel.data :
|
||||
{ *(.rel.data) *(.rel.gnu.linkonce.d*) }
|
||||
.rela.data :
|
||||
{ *(.rela.data) *(.rela.gnu.linkonce.d*) }
|
||||
.rel.rodata :
|
||||
{ *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
|
||||
.rela.rodata :
|
||||
{ *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
|
||||
.rel.got : { *(.rel.got) }
|
||||
.rela.got : { *(.rela.got) }
|
||||
.rel.ctors : { *(.rel.ctors) }
|
||||
.rela.ctors : { *(.rela.ctors) }
|
||||
.rel.dtors : { *(.rel.dtors) }
|
||||
.rela.dtors : { *(.rela.dtors) }
|
||||
.rel.init : { *(.rel.init) }
|
||||
.rela.init : { *(.rela.init) }
|
||||
.rel.fini : { *(.rel.fini) }
|
||||
.rela.fini : { *(.rela.fini) }
|
||||
.rel.bss : { *(.rel.bss) }
|
||||
.rela.bss : { *(.rela.bss) }
|
||||
.rel.plt : { *(.rel.plt) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init : { *(.init) } =0x47ff041f
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.gnu.linkonce.t*)
|
||||
} =0x47ff041f
|
||||
_etext = .;
|
||||
PROVIDE (etext = .);
|
||||
.fini : { *(.fini) } =0x47ff041f
|
||||
.rodata : { *(.rodata) *(.gnu.linkonce.r*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
.reginfo : { *(.reginfo) }
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN(0x100000) + (. & (0x100000 - 1));
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
.ctors :
|
||||
{
|
||||
*(.ctors)
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
*(.dtors)
|
||||
}
|
||||
.plt : { *(.plt) }
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
.sdata : { *(.sdata) }
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
__bss_start = .;
|
||||
.sbss : { *(.sbss) *(.scommon) }
|
||||
.bss :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
_end = . ;
|
||||
PROVIDE (end = .);
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* These must appear regardless of . */
|
||||
}
|
||||
+458
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Arm "Angel" semihosting syscalls
|
||||
*
|
||||
* Copyright (c) 2005, 2007 CodeSourcery.
|
||||
* Written by Paul Brook.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#include "qemu.h"
|
||||
|
||||
#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
|
||||
#else
|
||||
#include "vl.h"
|
||||
#endif
|
||||
|
||||
#define SYS_OPEN 0x01
|
||||
#define SYS_CLOSE 0x02
|
||||
#define SYS_WRITEC 0x03
|
||||
#define SYS_WRITE0 0x04
|
||||
#define SYS_WRITE 0x05
|
||||
#define SYS_READ 0x06
|
||||
#define SYS_READC 0x07
|
||||
#define SYS_ISTTY 0x09
|
||||
#define SYS_SEEK 0x0a
|
||||
#define SYS_FLEN 0x0c
|
||||
#define SYS_TMPNAM 0x0d
|
||||
#define SYS_REMOVE 0x0e
|
||||
#define SYS_RENAME 0x0f
|
||||
#define SYS_CLOCK 0x10
|
||||
#define SYS_TIME 0x11
|
||||
#define SYS_SYSTEM 0x12
|
||||
#define SYS_ERRNO 0x13
|
||||
#define SYS_GET_CMDLINE 0x15
|
||||
#define SYS_HEAPINFO 0x16
|
||||
#define SYS_EXIT 0x18
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#define GDB_O_RDONLY 0x000
|
||||
#define GDB_O_WRONLY 0x001
|
||||
#define GDB_O_RDWR 0x002
|
||||
#define GDB_O_APPEND 0x008
|
||||
#define GDB_O_CREAT 0x200
|
||||
#define GDB_O_TRUNC 0x400
|
||||
#define GDB_O_BINARY 0
|
||||
|
||||
static int gdb_open_modeflags[12] = {
|
||||
GDB_O_RDONLY,
|
||||
GDB_O_RDONLY | GDB_O_BINARY,
|
||||
GDB_O_RDWR,
|
||||
GDB_O_RDWR | GDB_O_BINARY,
|
||||
GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
|
||||
GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
|
||||
GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
|
||||
GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
|
||||
GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
|
||||
GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
|
||||
GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
|
||||
GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
|
||||
};
|
||||
|
||||
static int open_modeflags[12] = {
|
||||
O_RDONLY,
|
||||
O_RDONLY | O_BINARY,
|
||||
O_RDWR,
|
||||
O_RDWR | O_BINARY,
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
O_RDWR | O_CREAT | O_TRUNC,
|
||||
O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
|
||||
O_WRONLY | O_CREAT | O_APPEND,
|
||||
O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
|
||||
O_RDWR | O_CREAT | O_APPEND,
|
||||
O_RDWR | O_CREAT | O_APPEND | O_BINARY
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
|
||||
{
|
||||
if (code == (uint32_t)-1)
|
||||
ts->swi_errno = errno;
|
||||
return code;
|
||||
}
|
||||
#else
|
||||
static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
static uint32_t softmmu_tget32(CPUState *env, uint32_t addr)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
cpu_memory_rw_debug(env, addr, (uint8_t *)&val, 4, 0);
|
||||
return tswap32(val);
|
||||
}
|
||||
static uint32_t softmmu_tget8(CPUState *env, uint32_t addr)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
cpu_memory_rw_debug(env, addr, &val, 1, 0);
|
||||
return val;
|
||||
}
|
||||
#define tget32(p) softmmu_tget32(env, p)
|
||||
#define tget8(p) softmmu_tget8(env, p)
|
||||
|
||||
static void *softmmu_lock_user(CPUState *env, uint32_t addr, uint32_t len,
|
||||
int copy)
|
||||
{
|
||||
char *p;
|
||||
/* TODO: Make this something that isn't fixed size. */
|
||||
p = malloc(len);
|
||||
if (copy)
|
||||
cpu_memory_rw_debug(env, addr, p, len, 0);
|
||||
return p;
|
||||
}
|
||||
#define lock_user(p, len, copy) softmmu_lock_user(env, p, len, copy)
|
||||
static char *softmmu_lock_user_string(CPUState *env, uint32_t addr)
|
||||
{
|
||||
char *p;
|
||||
char *s;
|
||||
uint8_t c;
|
||||
/* TODO: Make this something that isn't fixed size. */
|
||||
s = p = malloc(1024);
|
||||
do {
|
||||
cpu_memory_rw_debug(env, addr, &c, 1, 0);
|
||||
addr++;
|
||||
*(p++) = c;
|
||||
} while (c);
|
||||
return s;
|
||||
}
|
||||
#define lock_user_string(p) softmmu_lock_user_string(env, p)
|
||||
static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr,
|
||||
target_ulong len)
|
||||
{
|
||||
if (len)
|
||||
cpu_memory_rw_debug(env, addr, p, len, 1);
|
||||
free(p);
|
||||
}
|
||||
#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
|
||||
#endif
|
||||
|
||||
static target_ulong arm_semi_syscall_len;
|
||||
|
||||
static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
TaskState *ts = env->opaque;
|
||||
#endif
|
||||
if (ret == (target_ulong)-1) {
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
ts->swi_errno = err;
|
||||
#endif
|
||||
env->regs[0] = ret;
|
||||
} else {
|
||||
/* Fixup syscalls that use nonstardard return conventions. */
|
||||
switch (env->regs[0]) {
|
||||
case SYS_WRITE:
|
||||
case SYS_READ:
|
||||
env->regs[0] = arm_semi_syscall_len - ret;
|
||||
break;
|
||||
case SYS_SEEK:
|
||||
env->regs[0] = 0;
|
||||
break;
|
||||
default:
|
||||
env->regs[0] = ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define ARG(n) tget32(args + (n) * 4)
|
||||
#define SET_ARG(n, val) tput32(args + (n) * 4,val)
|
||||
uint32_t do_arm_semihosting(CPUState *env)
|
||||
{
|
||||
target_ulong args;
|
||||
char * s;
|
||||
int nr;
|
||||
uint32_t ret;
|
||||
uint32_t len;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
TaskState *ts = env->opaque;
|
||||
#else
|
||||
CPUState *ts = env;
|
||||
#endif
|
||||
|
||||
nr = env->regs[0];
|
||||
args = env->regs[1];
|
||||
switch (nr) {
|
||||
case SYS_OPEN:
|
||||
s = lock_user_string(ARG(0));
|
||||
if (ARG(1) >= 12)
|
||||
return (uint32_t)-1;
|
||||
if (strcmp(s, ":tt") == 0) {
|
||||
if (ARG(1) < 4)
|
||||
return STDIN_FILENO;
|
||||
else
|
||||
return STDOUT_FILENO;
|
||||
}
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0), (int)ARG(2),
|
||||
gdb_open_modeflags[ARG(1)]);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
|
||||
}
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return ret;
|
||||
case SYS_CLOSE:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
return set_swi_errno(ts, close(ARG(0)));
|
||||
}
|
||||
case SYS_WRITEC:
|
||||
{
|
||||
char c = tget8(args);
|
||||
/* Write to debug console. stderr is near enough. */
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
return write(STDERR_FILENO, &c, 1);
|
||||
}
|
||||
}
|
||||
case SYS_WRITE0:
|
||||
s = lock_user_string(args);
|
||||
len = strlen(s);
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
|
||||
ret = env->regs[0];
|
||||
} else {
|
||||
ret = write(STDERR_FILENO, s, len);
|
||||
}
|
||||
unlock_user(s, args, 0);
|
||||
return ret;
|
||||
case SYS_WRITE:
|
||||
len = ARG(2);
|
||||
if (use_gdb_syscalls()) {
|
||||
arm_semi_syscall_len = len;
|
||||
gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
s = lock_user(ARG(1), len, 1);
|
||||
ret = set_swi_errno(ts, write(ARG(0), s, len));
|
||||
unlock_user(s, ARG(1), 0);
|
||||
if (ret == (uint32_t)-1)
|
||||
return -1;
|
||||
return len - ret;
|
||||
}
|
||||
case SYS_READ:
|
||||
len = ARG(2);
|
||||
if (use_gdb_syscalls()) {
|
||||
arm_semi_syscall_len = len;
|
||||
gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
|
||||
return env->regs[0];
|
||||
} else {
|
||||
s = lock_user(ARG(1), len, 0);
|
||||
do
|
||||
ret = set_swi_errno(ts, read(ARG(0), s, len));
|
||||
while (ret == -1 && errno == EINTR);
|
||||
unlock_user(s, ARG(1), len);
|
||||
if (ret == (uint32_t)-1)
|
||||
return -1;
|
||||
return len - ret;
|
||||
}
|
||||
case SYS_READC:
|
||||
/* XXX: Read from debug cosole. Not implemented. */
|
||||
return 0;
|
||||
case SYS_ISTTY:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
return isatty(ARG(0));
|
||||
}
|
||||
case SYS_SEEK:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "fseek,%x,%x,0", ARG(0), ARG(1));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
|
||||
if (ret == (uint32_t)-1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
case SYS_FLEN:
|
||||
if (use_gdb_syscalls()) {
|
||||
/* TODO: Use stat syscall. */
|
||||
return -1;
|
||||
} else {
|
||||
struct stat buf;
|
||||
ret = set_swi_errno(ts, fstat(ARG(0), &buf));
|
||||
if (ret == (uint32_t)-1)
|
||||
return -1;
|
||||
return buf.st_size;
|
||||
}
|
||||
case SYS_TMPNAM:
|
||||
/* XXX: Not implemented. */
|
||||
return -1;
|
||||
case SYS_REMOVE:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1));
|
||||
ret = env->regs[0];
|
||||
} else {
|
||||
s = lock_user_string(ARG(0));
|
||||
ret = set_swi_errno(ts, remove(s));
|
||||
unlock_user(s, ARG(0), 0);
|
||||
}
|
||||
return ret;
|
||||
case SYS_RENAME:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
|
||||
ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
char *s2;
|
||||
s = lock_user_string(ARG(0));
|
||||
s2 = lock_user_string(ARG(2));
|
||||
ret = set_swi_errno(ts, rename(s, s2));
|
||||
unlock_user(s2, ARG(2), 0);
|
||||
unlock_user(s, ARG(0), 0);
|
||||
return ret;
|
||||
}
|
||||
case SYS_CLOCK:
|
||||
return clock() / (CLOCKS_PER_SEC / 100);
|
||||
case SYS_TIME:
|
||||
return set_swi_errno(ts, time(NULL));
|
||||
case SYS_SYSTEM:
|
||||
if (use_gdb_syscalls()) {
|
||||
gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1));
|
||||
return env->regs[0];
|
||||
} else {
|
||||
s = lock_user_string(ARG(0));
|
||||
ret = set_swi_errno(ts, system(s));
|
||||
unlock_user(s, ARG(0), 0);
|
||||
}
|
||||
case SYS_ERRNO:
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return ts->swi_errno;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case SYS_GET_CMDLINE:
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* Build a commandline from the original argv. */
|
||||
{
|
||||
char **arg = ts->info->host_argv;
|
||||
int len = ARG(1);
|
||||
/* lock the buffer on the ARM side */
|
||||
char *cmdline_buffer = (char*)lock_user(ARG(0), len, 0);
|
||||
|
||||
s = cmdline_buffer;
|
||||
while (*arg && len > 2) {
|
||||
int n = strlen(*arg);
|
||||
|
||||
if (s != cmdline_buffer) {
|
||||
*(s++) = ' ';
|
||||
len--;
|
||||
}
|
||||
if (n >= len)
|
||||
n = len - 1;
|
||||
memcpy(s, *arg, n);
|
||||
s += n;
|
||||
len -= n;
|
||||
arg++;
|
||||
}
|
||||
/* Null terminate the string. */
|
||||
*s = 0;
|
||||
len = s - cmdline_buffer;
|
||||
|
||||
/* Unlock the buffer on the ARM side. */
|
||||
unlock_user(cmdline_buffer, ARG(0), len);
|
||||
|
||||
/* Adjust the commandline length argument. */
|
||||
SET_ARG(1, len);
|
||||
|
||||
/* Return success if commandline fit into buffer. */
|
||||
return *arg ? -1 : 0;
|
||||
}
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
case SYS_HEAPINFO:
|
||||
{
|
||||
uint32_t *ptr;
|
||||
uint32_t limit;
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* Some C libraries assume the heap immediately follows .bss, so
|
||||
allocate it using sbrk. */
|
||||
if (!ts->heap_limit) {
|
||||
long ret;
|
||||
|
||||
ts->heap_base = do_brk(0);
|
||||
limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
|
||||
/* Try a big heap, and reduce the size if that fails. */
|
||||
for (;;) {
|
||||
ret = do_brk(limit);
|
||||
if (ret != -1)
|
||||
break;
|
||||
limit = (ts->heap_base >> 1) + (limit >> 1);
|
||||
}
|
||||
ts->heap_limit = limit;
|
||||
}
|
||||
|
||||
ptr = lock_user(ARG(0), 16, 0);
|
||||
ptr[0] = tswap32(ts->heap_base);
|
||||
ptr[1] = tswap32(ts->heap_limit);
|
||||
ptr[2] = tswap32(ts->stack_base);
|
||||
ptr[3] = tswap32(0); /* Stack limit. */
|
||||
unlock_user(ptr, ARG(0), 16);
|
||||
#else
|
||||
limit = ram_size;
|
||||
ptr = lock_user(ARG(0), 16, 0);
|
||||
/* TODO: Make this use the limit of the loaded application. */
|
||||
ptr[0] = tswap32(limit / 2);
|
||||
ptr[1] = tswap32(limit);
|
||||
ptr[2] = tswap32(limit); /* Stack base */
|
||||
ptr[3] = tswap32(0); /* Stack limit. */
|
||||
unlock_user(ptr, ARG(0), 16);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
case SYS_EXIT:
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
|
||||
cpu_dump_state(env, stderr, fprintf, 0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm",
|
||||
"elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(_start)
|
||||
SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
. = 0x60000000 + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
.hash : { *(.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.rel.text :
|
||||
{ *(.rel.text) *(.rel.gnu.linkonce.t*) }
|
||||
.rela.text :
|
||||
{ *(.rela.text) *(.rela.gnu.linkonce.t*) }
|
||||
.rel.data :
|
||||
{ *(.rel.data) *(.rel.gnu.linkonce.d*) }
|
||||
.rela.data :
|
||||
{ *(.rela.data) *(.rela.gnu.linkonce.d*) }
|
||||
.rel.rodata :
|
||||
{ *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
|
||||
.rela.rodata :
|
||||
{ *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
|
||||
.rel.got : { *(.rel.got) }
|
||||
.rela.got : { *(.rela.got) }
|
||||
.rel.ctors : { *(.rel.ctors) }
|
||||
.rela.ctors : { *(.rela.ctors) }
|
||||
.rel.dtors : { *(.rel.dtors) }
|
||||
.rela.dtors : { *(.rela.dtors) }
|
||||
.rel.init : { *(.rel.init) }
|
||||
.rela.init : { *(.rela.init) }
|
||||
.rel.fini : { *(.rel.fini) }
|
||||
.rela.fini : { *(.rela.fini) }
|
||||
.rel.bss : { *(.rel.bss) }
|
||||
.rela.bss : { *(.rela.bss) }
|
||||
.rel.plt : { *(.rel.plt) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init : { *(.init) } =0x47ff041f
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.gnu.linkonce.t*)
|
||||
} =0x47ff041f
|
||||
_etext = .;
|
||||
PROVIDE (etext = .);
|
||||
.fini : { *(.fini) } =0x47ff041f
|
||||
.rodata : { *(.rodata) *(.gnu.linkonce.r*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
|
||||
__exidx_start = .;
|
||||
.ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
|
||||
__exidx_end = .;
|
||||
.reginfo : { *(.reginfo) }
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN(0x100000) + (. & (0x100000 - 1));
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
}
|
||||
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
|
||||
.data1 : { *(.data1) }
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
}
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
}
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
}
|
||||
.ctors :
|
||||
{
|
||||
*(.ctors)
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
*(.dtors)
|
||||
}
|
||||
.plt : { *(.plt) }
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
.sdata : { *(.sdata) }
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
__bss_start = .;
|
||||
.sbss : { *(.sbss) *(.scommon) }
|
||||
.bss :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
_end = . ;
|
||||
PROVIDE (end = .);
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* These must appear regardless of . */
|
||||
}
|
||||
@@ -0,0 +1,974 @@
|
||||
/*
|
||||
* QEMU ALSA audio driver
|
||||
*
|
||||
* Copyright (c) 2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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 <alsa/asoundlib.h>
|
||||
#include "vl.h"
|
||||
|
||||
#define AUDIO_CAP "alsa"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct ALSAVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
snd_pcm_t *handle;
|
||||
} ALSAVoiceOut;
|
||||
|
||||
typedef struct ALSAVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
snd_pcm_t *handle;
|
||||
void *pcm_buf;
|
||||
} ALSAVoiceIn;
|
||||
|
||||
static struct {
|
||||
int size_in_usec_in;
|
||||
int size_in_usec_out;
|
||||
const char *pcm_name_in;
|
||||
const char *pcm_name_out;
|
||||
unsigned int buffer_size_in;
|
||||
unsigned int period_size_in;
|
||||
unsigned int buffer_size_out;
|
||||
unsigned int period_size_out;
|
||||
unsigned int threshold;
|
||||
|
||||
int buffer_size_in_overriden;
|
||||
int period_size_in_overriden;
|
||||
|
||||
int buffer_size_out_overriden;
|
||||
int period_size_out_overriden;
|
||||
int verbose;
|
||||
} conf = {
|
||||
#ifdef HIGH_LATENCY
|
||||
.size_in_usec_in = 1,
|
||||
.size_in_usec_out = 1,
|
||||
#endif
|
||||
.pcm_name_out = "default",
|
||||
.pcm_name_in = "default",
|
||||
#ifdef HIGH_LATENCY
|
||||
.buffer_size_in = 400000,
|
||||
.period_size_in = 400000 / 4,
|
||||
.buffer_size_out = 400000,
|
||||
.period_size_out = 400000 / 4,
|
||||
#else
|
||||
#define DEFAULT_BUFFER_SIZE 1024
|
||||
#define DEFAULT_PERIOD_SIZE 256
|
||||
.buffer_size_in = DEFAULT_BUFFER_SIZE * 4,
|
||||
.period_size_in = DEFAULT_PERIOD_SIZE * 4,
|
||||
.buffer_size_out = DEFAULT_BUFFER_SIZE,
|
||||
.period_size_out = DEFAULT_PERIOD_SIZE,
|
||||
.buffer_size_in_overriden = 0,
|
||||
.buffer_size_out_overriden = 0,
|
||||
.period_size_in_overriden = 0,
|
||||
.period_size_out_overriden = 0,
|
||||
#endif
|
||||
.threshold = 0,
|
||||
.verbose = 0
|
||||
};
|
||||
|
||||
struct alsa_params_req {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
unsigned int buffer_size;
|
||||
unsigned int period_size;
|
||||
};
|
||||
|
||||
struct alsa_params_obt {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
snd_pcm_uframes_t samples;
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
|
||||
int err,
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
|
||||
}
|
||||
|
||||
static void alsa_anal_close (snd_pcm_t **handlep)
|
||||
{
|
||||
int err = snd_pcm_close (*handlep);
|
||||
if (err) {
|
||||
alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
|
||||
}
|
||||
*handlep = NULL;
|
||||
}
|
||||
|
||||
static int alsa_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int aud_to_alsafmt (audfmt_e fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
return SND_PCM_FORMAT_S8;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
return SND_PCM_FORMAT_U8;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
return SND_PCM_FORMAT_S16_LE;
|
||||
|
||||
case AUD_FMT_U16:
|
||||
return SND_PCM_FORMAT_U16_LE;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
abort ();
|
||||
#endif
|
||||
return SND_PCM_FORMAT_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
|
||||
{
|
||||
switch (alsafmt) {
|
||||
case SND_PCM_FORMAT_S8:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U8:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_S16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_S16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized audio format %d\n", alsafmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined DEBUG_MISMATCHES || defined DEBUG
|
||||
static void alsa_dump_info (struct alsa_params_req *req,
|
||||
struct alsa_params_obt *obt)
|
||||
{
|
||||
dolog ("parameter | requested value | obtained value\n");
|
||||
dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
|
||||
dolog ("channels | %10d | %10d\n",
|
||||
req->nchannels, obt->nchannels);
|
||||
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
|
||||
dolog ("============================================\n");
|
||||
dolog ("requested: buffer size %d period size %d\n",
|
||||
req->buffer_size, req->period_size);
|
||||
dolog ("obtained: samples %ld\n", obt->samples);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
|
||||
snd_pcm_sw_params_alloca (&sw_params);
|
||||
|
||||
err = snd_pcm_sw_params_current (handle, sw_params);
|
||||
if (err < 0) {
|
||||
dolog ("Could not fully initialize DAC\n");
|
||||
alsa_logerr (err, "Failed to get current software parameters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
|
||||
if (err < 0) {
|
||||
dolog ("Could not fully initialize DAC\n");
|
||||
alsa_logerr (err, "Failed to set software threshold to %ld\n",
|
||||
threshold);
|
||||
return;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params (handle, sw_params);
|
||||
if (err < 0) {
|
||||
dolog ("Could not fully initialize DAC\n");
|
||||
alsa_logerr (err, "Failed to set software parameters\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_open (int in, struct alsa_params_req *req,
|
||||
struct alsa_params_obt *obt, snd_pcm_t **handlep)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
int err, freq, nchannels;
|
||||
const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
|
||||
unsigned int period_size, buffer_size;
|
||||
snd_pcm_uframes_t obt_buffer_size;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
|
||||
freq = req->freq;
|
||||
period_size = req->period_size;
|
||||
buffer_size = req->buffer_size;
|
||||
nchannels = req->nchannels;
|
||||
|
||||
snd_pcm_hw_params_alloca (&hw_params);
|
||||
|
||||
err = snd_pcm_open (
|
||||
&handle,
|
||||
pcm_name,
|
||||
in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
|
||||
SND_PCM_NONBLOCK
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_any (handle, hw_params);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_access (
|
||||
handle,
|
||||
hw_params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set access type\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_channels_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&nchannels
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
|
||||
req->nchannels);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (nchannels != 1 && nchannels != 2) {
|
||||
alsa_logerr2 (err, typ,
|
||||
"Can not handle obtained number of channels %d\n",
|
||||
nchannels);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) {
|
||||
if (!buffer_size) {
|
||||
buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
period_size= DEFAULT_PERIOD_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer_size) {
|
||||
if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) {
|
||||
if (period_size) {
|
||||
err = snd_pcm_hw_params_set_period_time_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&period_size,
|
||||
0
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ,
|
||||
"Failed to set period time %d\n",
|
||||
req->period_size);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_time_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&buffer_size,
|
||||
0
|
||||
);
|
||||
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ,
|
||||
"Failed to set buffer time %d\n",
|
||||
req->buffer_size);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int dir;
|
||||
snd_pcm_uframes_t minval;
|
||||
|
||||
if (period_size) {
|
||||
minval = period_size;
|
||||
dir = 0;
|
||||
|
||||
err = snd_pcm_hw_params_get_period_size_min (
|
||||
hw_params,
|
||||
&minval,
|
||||
&dir
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr (
|
||||
err,
|
||||
"Could not get minmal period size for %s\n",
|
||||
typ
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (period_size < minval) {
|
||||
if ((in && conf.period_size_in_overriden)
|
||||
|| (!in && conf.period_size_out_overriden)) {
|
||||
dolog ("%s period size(%d) is less "
|
||||
"than minmal period size(%ld)\n",
|
||||
typ,
|
||||
period_size,
|
||||
minval);
|
||||
}
|
||||
period_size = minval;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_period_size (
|
||||
handle,
|
||||
hw_params,
|
||||
period_size,
|
||||
0
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set period size %d\n",
|
||||
req->period_size);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
minval = buffer_size;
|
||||
err = snd_pcm_hw_params_get_buffer_size_min (
|
||||
hw_params,
|
||||
&minval
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Could not get minmal buffer size for %s\n",
|
||||
typ);
|
||||
}
|
||||
else {
|
||||
if (buffer_size < minval) {
|
||||
if ((in && conf.buffer_size_in_overriden)
|
||||
|| (!in && conf.buffer_size_out_overriden)) {
|
||||
dolog (
|
||||
"%s buffer size(%d) is less "
|
||||
"than minimal buffer size(%ld)\n",
|
||||
typ,
|
||||
buffer_size,
|
||||
minval
|
||||
);
|
||||
}
|
||||
buffer_size = minval;
|
||||
}
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_size (
|
||||
handle,
|
||||
hw_params,
|
||||
buffer_size
|
||||
);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set buffer size %d\n",
|
||||
req->buffer_size);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Buffer size is not set\n");
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params (handle, hw_params);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to get buffer size\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = snd_pcm_prepare (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!in && conf.threshold) {
|
||||
snd_pcm_uframes_t threshold;
|
||||
int bytes_per_sec;
|
||||
|
||||
bytes_per_sec = freq
|
||||
<< (nchannels == 2)
|
||||
<< (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);
|
||||
|
||||
threshold = (conf.threshold * bytes_per_sec) / 1000;
|
||||
alsa_set_threshold (handle, threshold);
|
||||
}
|
||||
|
||||
obt->fmt = req->fmt;
|
||||
obt->nchannels = nchannels;
|
||||
obt->freq = freq;
|
||||
obt->samples = obt_buffer_size;
|
||||
*handlep = handle;
|
||||
|
||||
#if defined DEBUG_MISMATCHES || defined DEBUG
|
||||
if (obt->fmt != req->fmt ||
|
||||
obt->nchannels != req->nchannels ||
|
||||
obt->freq != req->freq) {
|
||||
dolog ("Audio paramters mismatch for %s\n", typ);
|
||||
alsa_dump_info (req, obt);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
alsa_dump_info (req, obt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
err:
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_recover (snd_pcm_t *handle)
|
||||
{
|
||||
int err = snd_pcm_prepare (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Failed to prepare handle %p\n", handle);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
|
||||
{
|
||||
snd_pcm_sframes_t avail;
|
||||
|
||||
avail = snd_pcm_avail_update (handle);
|
||||
if (avail < 0) {
|
||||
if (avail == -EPIPE) {
|
||||
if (!alsa_recover (handle)) {
|
||||
avail = snd_pcm_avail_update (handle);
|
||||
}
|
||||
}
|
||||
|
||||
if (avail < 0) {
|
||||
alsa_logerr (avail,
|
||||
"Could not obtain number of available frames\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return avail;
|
||||
}
|
||||
|
||||
static int alsa_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
int rpos, live, decr;
|
||||
int samples;
|
||||
uint8_t *dst;
|
||||
st_sample_t *src;
|
||||
snd_pcm_sframes_t avail;
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of available playback frames\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = audio_MIN (live, avail);
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int len = audio_MIN (samples, left_till_end_samples);
|
||||
snd_pcm_sframes_t written;
|
||||
|
||||
src = hw->mix_buf + rpos;
|
||||
dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, len);
|
||||
|
||||
while (len) {
|
||||
written = snd_pcm_writei (alsa->handle, dst, len);
|
||||
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
if (conf.verbose) {
|
||||
dolog ("Failed to write %d frames (wrote zero)\n", len);
|
||||
}
|
||||
goto exit;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
goto exit;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Recovering from playback xrun\n");
|
||||
}
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
goto exit;
|
||||
|
||||
default:
|
||||
alsa_logerr (written, "Failed to write %d frames to %p\n",
|
||||
len, dst);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
rpos = (rpos + written) % hw->samples;
|
||||
samples -= written;
|
||||
len -= written;
|
||||
dst = advance (dst, written << hw->info.shift);
|
||||
src += written;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void alsa_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
|
||||
ldebug ("alsa_fini\n");
|
||||
alsa_anal_close (&alsa->handle);
|
||||
|
||||
if (alsa->pcm_buf) {
|
||||
qemu_free (alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
struct alsa_params_req req;
|
||||
struct alsa_params_obt obt;
|
||||
audfmt_e effective_fmt;
|
||||
int endianness;
|
||||
int err;
|
||||
snd_pcm_t *handle;
|
||||
audsettings_t obt_as;
|
||||
|
||||
req.fmt = aud_to_alsafmt (as->fmt);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.period_size = conf.period_size_out;
|
||||
req.buffer_size = conf.buffer_size_out;
|
||||
|
||||
if (alsa_open (0, &req, &obt, &handle)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
||||
if (err) {
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.nchannels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
obt_as.endianness = endianness;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (pause) {
|
||||
err = snd_pcm_drop (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Could not stop %s\n", typ);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = snd_pcm_prepare (handle);
|
||||
if (err < 0) {
|
||||
alsa_logerr (err, "Could not prepare handle for %s\n", typ);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
ldebug ("enabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "playback", 0);
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "playback", 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
struct alsa_params_req req;
|
||||
struct alsa_params_obt obt;
|
||||
int endianness;
|
||||
int err;
|
||||
audfmt_e effective_fmt;
|
||||
snd_pcm_t *handle;
|
||||
audsettings_t obt_as;
|
||||
|
||||
req.fmt = aud_to_alsafmt (as->fmt);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.period_size = conf.period_size_in;
|
||||
req.buffer_size = conf.buffer_size_in;
|
||||
|
||||
if (alsa_open (1, &req, &obt, &handle)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
||||
if (err) {
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.nchannels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
obt_as.endianness = endianness;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alsa_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
|
||||
alsa_anal_close (&alsa->handle);
|
||||
|
||||
if (alsa->pcm_buf) {
|
||||
qemu_free (alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int i;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
int decr;
|
||||
struct {
|
||||
int add;
|
||||
int len;
|
||||
} bufs[2] = {
|
||||
{ hw->wpos, 0 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
snd_pcm_sframes_t avail;
|
||||
snd_pcm_uframes_t read_samples = 0;
|
||||
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of captured frames\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
|
||||
avail = hw->samples;
|
||||
}
|
||||
|
||||
decr = audio_MIN (dead, avail);
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hw->wpos + decr > hw->samples) {
|
||||
bufs[0].len = (hw->samples - hw->wpos);
|
||||
bufs[1].len = (decr - (hw->samples - hw->wpos));
|
||||
}
|
||||
else {
|
||||
bufs[0].len = decr;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
void *src;
|
||||
st_sample_t *dst;
|
||||
snd_pcm_sframes_t nread;
|
||||
snd_pcm_uframes_t len;
|
||||
|
||||
len = bufs[i].len;
|
||||
|
||||
src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
|
||||
dst = hw->conv_buf + bufs[i].add;
|
||||
|
||||
while (len) {
|
||||
nread = snd_pcm_readi (alsa->handle, src, len);
|
||||
|
||||
if (nread <= 0) {
|
||||
switch (nread) {
|
||||
case 0:
|
||||
if (conf.verbose) {
|
||||
dolog ("Failed to read %ld frames (read zero)\n", len);
|
||||
}
|
||||
goto exit;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (nread, "Failed to read %ld frames\n", len);
|
||||
goto exit;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Recovering from capture xrun\n");
|
||||
}
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
goto exit;
|
||||
|
||||
default:
|
||||
alsa_logerr (
|
||||
nread,
|
||||
"Failed to read %ld frames from %p\n",
|
||||
len,
|
||||
src
|
||||
);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
hw->conv (dst, src, nread, &nominal_volume);
|
||||
|
||||
src = advance (src, nread << hwshift);
|
||||
dst += nread;
|
||||
|
||||
read_samples += nread;
|
||||
len -= nread;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
hw->wpos = (hw->wpos + read_samples) % hw->samples;
|
||||
return read_samples;
|
||||
}
|
||||
|
||||
static int alsa_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
ldebug ("enabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "capture", 0);
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
return alsa_voice_ctl (alsa->handle, "capture", 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *alsa_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void alsa_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_option alsa_options[] = {
|
||||
{"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
|
||||
"DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
|
||||
{"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
|
||||
"DAC period size", &conf.period_size_out_overriden, 0},
|
||||
{"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
|
||||
"DAC buffer size", &conf.buffer_size_out_overriden, 0},
|
||||
|
||||
{"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
|
||||
"ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
|
||||
{"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
|
||||
"ADC period size", &conf.period_size_in_overriden, 0},
|
||||
{"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
|
||||
"ADC buffer size", &conf.buffer_size_in_overriden, 0},
|
||||
|
||||
{"THRESHOLD", AUD_OPT_INT, &conf.threshold,
|
||||
"(undocumented)", NULL, 0},
|
||||
|
||||
{"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
|
||||
"DAC device name (for instance dmix)", NULL, 0},
|
||||
|
||||
{"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
|
||||
"ADC device name", NULL, 0},
|
||||
|
||||
{"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
|
||||
"Behave in a more verbose way", NULL, 0},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops alsa_pcm_ops = {
|
||||
alsa_init_out,
|
||||
alsa_fini_out,
|
||||
alsa_run_out,
|
||||
alsa_write,
|
||||
alsa_ctl_out,
|
||||
|
||||
alsa_init_in,
|
||||
alsa_fini_in,
|
||||
alsa_run_in,
|
||||
alsa_read,
|
||||
alsa_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver alsa_audio_driver = {
|
||||
INIT_FIELD (name = ) "alsa",
|
||||
INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
|
||||
INIT_FIELD (options = ) alsa_options,
|
||||
INIT_FIELD (init = ) alsa_audio_init,
|
||||
INIT_FIELD (fini = ) alsa_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &alsa_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
|
||||
};
|
||||
+1871
File diff suppressed because it is too large
Load Diff
+169
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2003-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef QEMU_AUDIO_H
|
||||
#define QEMU_AUDIO_H
|
||||
|
||||
#include "config.h"
|
||||
#include "sys-queue.h"
|
||||
|
||||
typedef void (*audio_callback_fn_t) (void *opaque, int avail);
|
||||
|
||||
typedef enum {
|
||||
AUD_FMT_U8,
|
||||
AUD_FMT_S8,
|
||||
AUD_FMT_U16,
|
||||
AUD_FMT_S16
|
||||
} audfmt_e;
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define AUDIO_HOST_ENDIANNESS 1
|
||||
#else
|
||||
#define AUDIO_HOST_ENDIANNESS 0
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int freq;
|
||||
int nchannels;
|
||||
audfmt_e fmt;
|
||||
int endianness;
|
||||
} audsettings_t;
|
||||
|
||||
typedef enum {
|
||||
AUD_CNOTIFY_ENABLE,
|
||||
AUD_CNOTIFY_DISABLE
|
||||
} audcnotification_e;
|
||||
|
||||
struct audio_capture_ops {
|
||||
void (*notify) (void *opaque, audcnotification_e cmd);
|
||||
void (*capture) (void *opaque, void *buf, int size);
|
||||
void (*destroy) (void *opaque);
|
||||
};
|
||||
|
||||
struct capture_ops {
|
||||
void (*info) (void *opaque);
|
||||
void (*destroy) (void *opaque);
|
||||
};
|
||||
|
||||
typedef struct CaptureState {
|
||||
void *opaque;
|
||||
struct capture_ops ops;
|
||||
LIST_ENTRY (CaptureState) entries;
|
||||
} CaptureState;
|
||||
|
||||
typedef struct AudioState AudioState;
|
||||
typedef struct SWVoiceOut SWVoiceOut;
|
||||
typedef struct CaptureVoiceOut CaptureVoiceOut;
|
||||
typedef struct SWVoiceIn SWVoiceIn;
|
||||
|
||||
typedef struct QEMUSoundCard {
|
||||
AudioState *audio;
|
||||
char *name;
|
||||
LIST_ENTRY (QEMUSoundCard) entries;
|
||||
} QEMUSoundCard;
|
||||
|
||||
typedef struct QEMUAudioTimeStamp {
|
||||
uint64_t old_ts;
|
||||
} QEMUAudioTimeStamp;
|
||||
|
||||
void AUD_vlog (const char *cap, const char *fmt, va_list ap);
|
||||
void AUD_log (const char *cap, const char *fmt, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
AudioState *AUD_init (void);
|
||||
void AUD_help (void);
|
||||
void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
|
||||
void AUD_remove_card (QEMUSoundCard *card);
|
||||
CaptureVoiceOut *AUD_add_capture (
|
||||
AudioState *s,
|
||||
audsettings_t *as,
|
||||
struct audio_capture_ops *ops,
|
||||
void *opaque
|
||||
);
|
||||
void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
|
||||
|
||||
SWVoiceOut *AUD_open_out (
|
||||
QEMUSoundCard *card,
|
||||
SWVoiceOut *sw,
|
||||
const char *name,
|
||||
void *callback_opaque,
|
||||
audio_callback_fn_t callback_fn,
|
||||
audsettings_t *settings
|
||||
);
|
||||
|
||||
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
|
||||
int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
|
||||
int AUD_get_buffer_size_out (SWVoiceOut *sw);
|
||||
void AUD_set_active_out (SWVoiceOut *sw, int on);
|
||||
int AUD_is_active_out (SWVoiceOut *sw);
|
||||
|
||||
void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
|
||||
uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
|
||||
|
||||
SWVoiceIn *AUD_open_in (
|
||||
QEMUSoundCard *card,
|
||||
SWVoiceIn *sw,
|
||||
const char *name,
|
||||
void *callback_opaque,
|
||||
audio_callback_fn_t callback_fn,
|
||||
audsettings_t *settings
|
||||
);
|
||||
|
||||
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
|
||||
int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
|
||||
void AUD_set_active_in (SWVoiceIn *sw, int on);
|
||||
int AUD_is_active_in (SWVoiceIn *sw);
|
||||
|
||||
void AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
|
||||
uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
|
||||
|
||||
static inline void *advance (void *p, int incr)
|
||||
{
|
||||
uint8_t *d = p;
|
||||
return (d + incr);
|
||||
}
|
||||
|
||||
uint32_t popcount (uint32_t u);
|
||||
uint32_t lsbindex (uint32_t u);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define audio_MIN(a, b) ( __extension__ ({ \
|
||||
__typeof (a) ta = a; \
|
||||
__typeof (b) tb = b; \
|
||||
((ta)>(tb)?(tb):(ta)); \
|
||||
}))
|
||||
|
||||
#define audio_MAX(a, b) ( __extension__ ({ \
|
||||
__typeof (a) ta = a; \
|
||||
__typeof (b) tb = b; \
|
||||
((ta)<(tb)?(tb):(ta)); \
|
||||
}))
|
||||
#else
|
||||
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
|
||||
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
|
||||
#endif
|
||||
|
||||
#endif /* audio.h */
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2003-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef QEMU_AUDIO_INT_H
|
||||
#define QEMU_AUDIO_INT_H
|
||||
|
||||
#ifdef CONFIG_COREAUDIO
|
||||
#define FLOAT_MIXENG
|
||||
/* #define RECIPROCAL */
|
||||
#endif
|
||||
#include "mixeng.h"
|
||||
|
||||
struct audio_pcm_ops;
|
||||
|
||||
typedef enum {
|
||||
AUD_OPT_INT,
|
||||
AUD_OPT_FMT,
|
||||
AUD_OPT_STR,
|
||||
AUD_OPT_BOOL
|
||||
} audio_option_tag_e;
|
||||
|
||||
struct audio_option {
|
||||
const char *name;
|
||||
audio_option_tag_e tag;
|
||||
void *valp;
|
||||
const char *descr;
|
||||
int *overridenp;
|
||||
int overriden;
|
||||
};
|
||||
|
||||
struct audio_callback {
|
||||
void *opaque;
|
||||
audio_callback_fn_t fn;
|
||||
};
|
||||
|
||||
struct audio_pcm_info {
|
||||
int bits;
|
||||
int sign;
|
||||
int freq;
|
||||
int nchannels;
|
||||
int align;
|
||||
int shift;
|
||||
int bytes_per_second;
|
||||
int swap_endianness;
|
||||
};
|
||||
|
||||
typedef struct SWVoiceCap SWVoiceCap;
|
||||
|
||||
typedef struct HWVoiceOut {
|
||||
int enabled;
|
||||
int pending_disable;
|
||||
struct audio_pcm_info info;
|
||||
|
||||
f_sample *clip;
|
||||
|
||||
int rpos;
|
||||
uint64_t ts_helper;
|
||||
|
||||
st_sample_t *mix_buf;
|
||||
|
||||
int samples;
|
||||
LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
|
||||
LIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
LIST_ENTRY (HWVoiceOut) entries;
|
||||
} HWVoiceOut;
|
||||
|
||||
typedef struct HWVoiceIn {
|
||||
int enabled;
|
||||
struct audio_pcm_info info;
|
||||
|
||||
t_sample *conv;
|
||||
|
||||
int wpos;
|
||||
int total_samples_captured;
|
||||
uint64_t ts_helper;
|
||||
|
||||
st_sample_t *conv_buf;
|
||||
|
||||
int samples;
|
||||
LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
LIST_ENTRY (HWVoiceIn) entries;
|
||||
} HWVoiceIn;
|
||||
|
||||
struct SWVoiceOut {
|
||||
struct audio_pcm_info info;
|
||||
t_sample *conv;
|
||||
int64_t ratio;
|
||||
st_sample_t *buf;
|
||||
void *rate;
|
||||
int total_hw_samples_mixed;
|
||||
int active;
|
||||
int empty;
|
||||
HWVoiceOut *hw;
|
||||
char *name;
|
||||
volume_t vol;
|
||||
struct audio_callback callback;
|
||||
LIST_ENTRY (SWVoiceOut) entries;
|
||||
};
|
||||
|
||||
struct SWVoiceIn {
|
||||
int active;
|
||||
struct audio_pcm_info info;
|
||||
int64_t ratio;
|
||||
void *rate;
|
||||
int total_hw_samples_acquired;
|
||||
st_sample_t *buf;
|
||||
f_sample *clip;
|
||||
HWVoiceIn *hw;
|
||||
char *name;
|
||||
volume_t vol;
|
||||
struct audio_callback callback;
|
||||
LIST_ENTRY (SWVoiceIn) entries;
|
||||
};
|
||||
|
||||
struct audio_driver {
|
||||
const char *name;
|
||||
const char *descr;
|
||||
struct audio_option *options;
|
||||
void *(*init) (void);
|
||||
void (*fini) (void *);
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
int can_be_default;
|
||||
int max_voices_out;
|
||||
int max_voices_in;
|
||||
int voice_size_out;
|
||||
int voice_size_in;
|
||||
};
|
||||
|
||||
struct audio_pcm_ops {
|
||||
int (*init_out)(HWVoiceOut *hw, audsettings_t *as);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
int (*run_out) (HWVoiceOut *hw);
|
||||
int (*write) (SWVoiceOut *sw, void *buf, int size);
|
||||
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
|
||||
|
||||
int (*init_in) (HWVoiceIn *hw, audsettings_t *as);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
int (*run_in) (HWVoiceIn *hw);
|
||||
int (*read) (SWVoiceIn *sw, void *buf, int size);
|
||||
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
|
||||
};
|
||||
|
||||
struct capture_callback {
|
||||
struct audio_capture_ops ops;
|
||||
void *opaque;
|
||||
LIST_ENTRY (capture_callback) entries;
|
||||
};
|
||||
|
||||
struct CaptureVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *buf;
|
||||
LIST_HEAD (cb_listhead, capture_callback) cb_head;
|
||||
LIST_ENTRY (CaptureVoiceOut) entries;
|
||||
};
|
||||
|
||||
struct SWVoiceCap {
|
||||
SWVoiceOut sw;
|
||||
CaptureVoiceOut *cap;
|
||||
LIST_ENTRY (SWVoiceCap) entries;
|
||||
};
|
||||
|
||||
struct AudioState {
|
||||
struct audio_driver *drv;
|
||||
void *drv_opaque;
|
||||
|
||||
QEMUTimer *ts;
|
||||
LIST_HEAD (card_listhead, QEMUSoundCard) card_head;
|
||||
LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
|
||||
LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
|
||||
LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
|
||||
int nb_hw_voices_out;
|
||||
int nb_hw_voices_in;
|
||||
};
|
||||
|
||||
extern struct audio_driver no_audio_driver;
|
||||
extern struct audio_driver oss_audio_driver;
|
||||
extern struct audio_driver sdl_audio_driver;
|
||||
extern struct audio_driver wav_audio_driver;
|
||||
extern struct audio_driver fmod_audio_driver;
|
||||
extern struct audio_driver alsa_audio_driver;
|
||||
extern struct audio_driver coreaudio_audio_driver;
|
||||
extern struct audio_driver dsound_audio_driver;
|
||||
extern volume_t nominal_volume;
|
||||
|
||||
void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as);
|
||||
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
|
||||
|
||||
int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
|
||||
int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
|
||||
|
||||
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
|
||||
int audio_pcm_hw_get_live_out (HWVoiceOut *hw);
|
||||
int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live);
|
||||
|
||||
int audio_bug (const char *funcname, int cond);
|
||||
void *audio_calloc (const char *funcname, int nmemb, size_t size);
|
||||
|
||||
#define VOICE_ENABLE 1
|
||||
#define VOICE_DISABLE 2
|
||||
|
||||
static inline int audio_ring_dist (int dst, int src, int len)
|
||||
{
|
||||
return (dst >= src) ? (dst - src) : (len - src + dst);
|
||||
}
|
||||
|
||||
#if defined __GNUC__
|
||||
#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2)))
|
||||
#define INIT_FIELD(f) . f
|
||||
#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m)))
|
||||
#else
|
||||
#define GCC_ATTR /**/
|
||||
#define INIT_FIELD(f) /**/
|
||||
#define GCC_FMT_ATTR(n, m)
|
||||
#endif
|
||||
|
||||
static void GCC_ATTR dolog (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void GCC_ATTR ldebug (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
#else
|
||||
#if defined NDEBUG && defined __GNUC__
|
||||
#define ldebug(...)
|
||||
#elif defined NDEBUG && defined _MSC_VER
|
||||
#define ldebug __noop
|
||||
#else
|
||||
static void GCC_ATTR ldebug (const char *fmt, ...)
|
||||
{
|
||||
(void) fmt;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef GCC_ATTR
|
||||
|
||||
#define AUDIO_STRINGIFY_(n) #n
|
||||
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
|
||||
|
||||
#if defined _MSC_VER || defined __GNUC__
|
||||
#define AUDIO_FUNC __FUNCTION__
|
||||
#else
|
||||
#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__)
|
||||
#endif
|
||||
|
||||
#endif /* audio_int.h */
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef DAC
|
||||
#define NAME "playback"
|
||||
#define HWBUF hw->mix_buf
|
||||
#define TYPE out
|
||||
#define HW HWVoiceOut
|
||||
#define SW SWVoiceOut
|
||||
#else
|
||||
#define NAME "capture"
|
||||
#define TYPE in
|
||||
#define HW HWVoiceIn
|
||||
#define SW SWVoiceIn
|
||||
#define HWBUF hw->conv_buf
|
||||
#endif
|
||||
|
||||
static void glue (audio_init_nb_voices_, TYPE) (
|
||||
AudioState *s,
|
||||
struct audio_driver *drv
|
||||
)
|
||||
{
|
||||
int max_voices = glue (drv->max_voices_, TYPE);
|
||||
int voice_size = glue (drv->voice_size_, TYPE);
|
||||
|
||||
if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
|
||||
if (!max_voices) {
|
||||
#ifdef DAC
|
||||
dolog ("Driver `%s' does not support " NAME "\n", drv->name);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n",
|
||||
drv->name,
|
||||
glue (s->nb_hw_voices_, TYPE),
|
||||
max_voices);
|
||||
}
|
||||
glue (s->nb_hw_voices_, TYPE) = max_voices;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) {
|
||||
dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
|
||||
drv->name, max_voices);
|
||||
glue (s->nb_hw_voices_, TYPE) = 0;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) {
|
||||
dolog ("drv=`%s' voice_size=%d max_voices=0\n",
|
||||
drv->name, voice_size);
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
if (HWBUF) {
|
||||
qemu_free (HWBUF);
|
||||
}
|
||||
|
||||
HWBUF = NULL;
|
||||
}
|
||||
|
||||
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
|
||||
if (!HWBUF) {
|
||||
dolog ("Could not allocate " NAME " buffer (%d samples)\n",
|
||||
hw->samples);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
if (sw->buf) {
|
||||
qemu_free (sw->buf);
|
||||
}
|
||||
|
||||
if (sw->rate) {
|
||||
st_rate_stop (sw->rate);
|
||||
}
|
||||
|
||||
sw->buf = NULL;
|
||||
sw->rate = NULL;
|
||||
}
|
||||
|
||||
static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
int samples;
|
||||
|
||||
#ifdef DAC
|
||||
samples = sw->hw->samples;
|
||||
#else
|
||||
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
|
||||
#endif
|
||||
|
||||
sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t));
|
||||
if (!sw->buf) {
|
||||
dolog ("Could not allocate buffer for `%s' (%d samples)\n",
|
||||
SW_NAME (sw), samples);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DAC
|
||||
sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
|
||||
#else
|
||||
sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
|
||||
#endif
|
||||
if (!sw->rate) {
|
||||
qemu_free (sw->buf);
|
||||
sw->buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glue (audio_pcm_sw_init_, TYPE) (
|
||||
SW *sw,
|
||||
HW *hw,
|
||||
const char *name,
|
||||
audsettings_t *as
|
||||
)
|
||||
{
|
||||
int err;
|
||||
|
||||
audio_pcm_init_info (&sw->info, as);
|
||||
sw->hw = hw;
|
||||
sw->active = 0;
|
||||
#ifdef DAC
|
||||
sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
|
||||
sw->total_hw_samples_mixed = 0;
|
||||
sw->empty = 1;
|
||||
#else
|
||||
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
|
||||
#endif
|
||||
|
||||
#ifdef DAC
|
||||
sw->conv = mixeng_conv
|
||||
#else
|
||||
sw->clip = mixeng_clip
|
||||
#endif
|
||||
[sw->info.nchannels == 2]
|
||||
[sw->info.sign]
|
||||
[sw->info.swap_endianness]
|
||||
[sw->info.bits == 16];
|
||||
|
||||
sw->name = qemu_strdup (name);
|
||||
err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
|
||||
if (err) {
|
||||
qemu_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
|
||||
{
|
||||
glue (audio_pcm_sw_free_resources_, TYPE) (sw);
|
||||
if (sw->name) {
|
||||
qemu_free (sw->name);
|
||||
sw->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
|
||||
{
|
||||
LIST_INSERT_HEAD (&hw->sw_head, sw, entries);
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
|
||||
{
|
||||
LIST_REMOVE (sw, entries);
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
|
||||
{
|
||||
HW *hw = *hwp;
|
||||
|
||||
if (!hw->sw_head.lh_first) {
|
||||
#ifdef DAC
|
||||
audio_detach_capture (hw);
|
||||
#endif
|
||||
LIST_REMOVE (hw, entries);
|
||||
glue (s->nb_hw_voices_, TYPE) += 1;
|
||||
glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
|
||||
glue (hw->pcm_ops->fini_, TYPE) (hw);
|
||||
qemu_free (hw);
|
||||
*hwp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_find_any_, TYPE) (AudioState *s, HW *hw)
|
||||
{
|
||||
return hw ? hw->entries.le_next : s->glue (hw_head_, TYPE).lh_first;
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw)
|
||||
{
|
||||
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
|
||||
if (hw->enabled) {
|
||||
return hw;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
|
||||
AudioState *s,
|
||||
HW *hw,
|
||||
audsettings_t *as
|
||||
)
|
||||
{
|
||||
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
|
||||
if (audio_pcm_info_eq (&hw->info, as)) {
|
||||
return hw;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
|
||||
{
|
||||
HW *hw;
|
||||
struct audio_driver *drv = s->drv;
|
||||
|
||||
if (!glue (s->nb_hw_voices_, TYPE)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !drv)) {
|
||||
dolog ("No host audio driver\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) {
|
||||
dolog ("Host audio driver without pcm_ops\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
|
||||
if (!hw) {
|
||||
dolog ("Can not allocate voice `%s' size %d\n",
|
||||
drv->name, glue (drv->voice_size_, TYPE));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hw->pcm_ops = drv->pcm_ops;
|
||||
LIST_INIT (&hw->sw_head);
|
||||
#ifdef DAC
|
||||
LIST_INIT (&hw->cap_head);
|
||||
#endif
|
||||
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
|
||||
dolog ("hw->samples=%d\n", hw->samples);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
#ifdef DAC
|
||||
hw->clip = mixeng_clip
|
||||
#else
|
||||
hw->conv = mixeng_conv
|
||||
#endif
|
||||
[hw->info.nchannels == 2]
|
||||
[hw->info.sign]
|
||||
[hw->info.swap_endianness]
|
||||
[hw->info.bits == 16];
|
||||
|
||||
if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
|
||||
goto err1;
|
||||
}
|
||||
|
||||
LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
|
||||
glue (s->nb_hw_voices_, TYPE) -= 1;
|
||||
#ifdef DAC
|
||||
audio_attach_capture (s, hw);
|
||||
#endif
|
||||
return hw;
|
||||
|
||||
err1:
|
||||
glue (hw->pcm_ops->fini_, TYPE) (hw);
|
||||
err0:
|
||||
qemu_free (hw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as)
|
||||
{
|
||||
HW *hw;
|
||||
|
||||
if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
|
||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
}
|
||||
|
||||
hw = glue (audio_pcm_hw_find_specific_, TYPE) (s, NULL, as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
|
||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
}
|
||||
|
||||
return glue (audio_pcm_hw_find_any_, TYPE) (s, NULL);
|
||||
}
|
||||
|
||||
static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
|
||||
AudioState *s,
|
||||
const char *sw_name,
|
||||
audsettings_t *as
|
||||
)
|
||||
{
|
||||
SW *sw;
|
||||
HW *hw;
|
||||
audsettings_t hw_as;
|
||||
|
||||
if (glue (conf.fixed_, TYPE).enabled) {
|
||||
hw_as = glue (conf.fixed_, TYPE).settings;
|
||||
}
|
||||
else {
|
||||
hw_as = *as;
|
||||
}
|
||||
|
||||
sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
|
||||
if (!sw) {
|
||||
dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
|
||||
sw_name ? sw_name : "unknown", sizeof (*sw));
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as);
|
||||
if (!hw) {
|
||||
goto err2;
|
||||
}
|
||||
|
||||
glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
|
||||
|
||||
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
|
||||
goto err3;
|
||||
}
|
||||
|
||||
return sw;
|
||||
|
||||
err3:
|
||||
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
|
||||
glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
|
||||
err2:
|
||||
qemu_free (sw);
|
||||
err1:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void glue (audio_close_, TYPE) (AudioState *s, SW *sw)
|
||||
{
|
||||
glue (audio_pcm_sw_fini_, TYPE) (sw);
|
||||
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
|
||||
glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw);
|
||||
qemu_free (sw);
|
||||
}
|
||||
|
||||
void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
|
||||
{
|
||||
if (sw) {
|
||||
if (audio_bug (AUDIO_FUNC, !card || !card->audio)) {
|
||||
dolog ("card=%p card->audio=%p\n",
|
||||
card, card ? card->audio : NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
glue (audio_close_, TYPE) (card->audio, sw);
|
||||
}
|
||||
}
|
||||
|
||||
SW *glue (AUD_open_, TYPE) (
|
||||
QEMUSoundCard *card,
|
||||
SW *sw,
|
||||
const char *name,
|
||||
void *callback_opaque ,
|
||||
audio_callback_fn_t callback_fn,
|
||||
audsettings_t *as
|
||||
)
|
||||
{
|
||||
AudioState *s;
|
||||
#ifdef DAC
|
||||
int live = 0;
|
||||
SW *old_sw = NULL;
|
||||
#endif
|
||||
|
||||
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
|
||||
name, as->freq, as->nchannels, as->fmt);
|
||||
|
||||
if (audio_bug (AUDIO_FUNC,
|
||||
!card || !card->audio || !name || !callback_fn || !as)) {
|
||||
dolog ("card=%p card->audio=%p name=%p callback_fn=%p as=%p\n",
|
||||
card, card ? card->audio : NULL, name, callback_fn, as);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s = card->audio;
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
|
||||
audio_print_settings (as);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !s->drv)) {
|
||||
dolog ("Can not open `%s' (no host audio driver)\n", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sw && audio_pcm_info_eq (&sw->info, as)) {
|
||||
return sw;
|
||||
}
|
||||
|
||||
#ifdef DAC
|
||||
if (conf.plive && sw && (!sw->active && !sw->empty)) {
|
||||
live = sw->total_hw_samples_mixed;
|
||||
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
|
||||
dolog ("Old %s freq %d, bits %d, channels %d\n",
|
||||
SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
|
||||
dolog ("New %s freq %d, bits %d, channels %d\n",
|
||||
name,
|
||||
freq,
|
||||
(fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
|
||||
nchannels);
|
||||
#endif
|
||||
|
||||
if (live) {
|
||||
old_sw = sw;
|
||||
old_sw->callback.fn = NULL;
|
||||
sw = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!glue (conf.fixed_, TYPE).enabled && sw) {
|
||||
glue (AUD_close_, TYPE) (card, sw);
|
||||
sw = NULL;
|
||||
}
|
||||
|
||||
if (sw) {
|
||||
HW *hw = sw->hw;
|
||||
|
||||
if (!hw) {
|
||||
dolog ("Internal logic error voice `%s' has no hardware store\n",
|
||||
SW_NAME (sw));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
glue (audio_pcm_sw_fini_, TYPE) (sw);
|
||||
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as);
|
||||
if (!sw) {
|
||||
dolog ("Failed to create voice `%s'\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (sw) {
|
||||
sw->vol = nominal_volume;
|
||||
sw->callback.fn = callback_fn;
|
||||
sw->callback.opaque = callback_opaque;
|
||||
|
||||
#ifdef DAC
|
||||
if (live) {
|
||||
int mixed =
|
||||
(live << old_sw->info.shift)
|
||||
* old_sw->info.bytes_per_second
|
||||
/ sw->info.bytes_per_second;
|
||||
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Silence will be mixed %d\n", mixed);
|
||||
#endif
|
||||
sw->total_hw_samples_mixed += mixed;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
dolog ("%s\n", name);
|
||||
audio_pcm_print_info ("hw", &sw->hw->info);
|
||||
audio_pcm_print_info ("sw", &sw->info);
|
||||
#endif
|
||||
}
|
||||
|
||||
return sw;
|
||||
|
||||
fail:
|
||||
glue (AUD_close_, TYPE) (card, sw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int glue (AUD_is_active_, TYPE) (SW *sw)
|
||||
{
|
||||
return sw ? sw->active : 0;
|
||||
}
|
||||
|
||||
void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
|
||||
{
|
||||
if (!sw) {
|
||||
return;
|
||||
}
|
||||
|
||||
ts->old_ts = sw->hw->ts_helper;
|
||||
}
|
||||
|
||||
uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
|
||||
{
|
||||
uint64_t delta, cur_ts, old_ts;
|
||||
|
||||
if (!sw) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cur_ts = sw->hw->ts_helper;
|
||||
old_ts = ts->old_ts;
|
||||
/* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
|
||||
|
||||
if (cur_ts >= old_ts) {
|
||||
delta = cur_ts - old_ts;
|
||||
}
|
||||
else {
|
||||
delta = UINT64_MAX - old_ts + cur_ts;
|
||||
}
|
||||
|
||||
if (!delta) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (delta * sw->hw->info.freq) / 1000000;
|
||||
}
|
||||
|
||||
#undef TYPE
|
||||
#undef HW
|
||||
#undef SW
|
||||
#undef HWBUF
|
||||
#undef NAME
|
||||
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
* QEMU OS X CoreAudio audio driver
|
||||
*
|
||||
* Copyright (c) 2005 Mike Kronenberg
|
||||
*
|
||||
* 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 <CoreAudio/CoreAudio.h>
|
||||
#include <string.h> /* strerror */
|
||||
#include <pthread.h> /* pthread_X */
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
#define AUDIO_CAP "coreaudio"
|
||||
#include "audio_int.h"
|
||||
|
||||
struct {
|
||||
int buffer_frames;
|
||||
int nbuffers;
|
||||
int isAtexit;
|
||||
} conf = {
|
||||
.buffer_frames = 512,
|
||||
.nbuffers = 4,
|
||||
.isAtexit = 0
|
||||
};
|
||||
|
||||
typedef struct coreaudioVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
pthread_mutex_t mutex;
|
||||
int isAtexit;
|
||||
AudioDeviceID outputDeviceID;
|
||||
UInt32 audioDevicePropertyBufferFrameSize;
|
||||
AudioStreamBasicDescription outputStreamBasicDescription;
|
||||
int live;
|
||||
int decr;
|
||||
int rpos;
|
||||
} coreaudioVoiceOut;
|
||||
|
||||
static void coreaudio_logstatus (OSStatus status)
|
||||
{
|
||||
char *str = "BUG";
|
||||
|
||||
switch(status) {
|
||||
case kAudioHardwareNoError:
|
||||
str = "kAudioHardwareNoError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareNotRunningError:
|
||||
str = "kAudioHardwareNotRunningError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareUnspecifiedError:
|
||||
str = "kAudioHardwareUnspecifiedError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareUnknownPropertyError:
|
||||
str = "kAudioHardwareUnknownPropertyError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareBadPropertySizeError:
|
||||
str = "kAudioHardwareBadPropertySizeError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareIllegalOperationError:
|
||||
str = "kAudioHardwareIllegalOperationError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareBadDeviceError:
|
||||
str = "kAudioHardwareBadDeviceError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareBadStreamError:
|
||||
str = "kAudioHardwareBadStreamError";
|
||||
break;
|
||||
|
||||
case kAudioHardwareUnsupportedOperationError:
|
||||
str = "kAudioHardwareUnsupportedOperationError";
|
||||
break;
|
||||
|
||||
case kAudioDeviceUnsupportedFormatError:
|
||||
str = "kAudioDeviceUnsupportedFormatError";
|
||||
break;
|
||||
|
||||
case kAudioDevicePermissionsError:
|
||||
str = "kAudioDevicePermissionsError";
|
||||
break;
|
||||
|
||||
default:
|
||||
AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", str);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
|
||||
OSStatus status,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_log (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
coreaudio_logstatus (status);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
|
||||
OSStatus status,
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
coreaudio_logstatus (status);
|
||||
}
|
||||
|
||||
static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
|
||||
{
|
||||
OSStatus status;
|
||||
UInt32 result = 0;
|
||||
UInt32 propertySize = sizeof(outputDeviceID);
|
||||
status = AudioDeviceGetProperty(
|
||||
outputDeviceID, 0, 0,
|
||||
kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr(status,
|
||||
"Could not determine whether Device is playing\n");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void coreaudio_atexit (void)
|
||||
{
|
||||
conf.isAtexit = 1;
|
||||
}
|
||||
|
||||
static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_lock (&core->mutex);
|
||||
if (err) {
|
||||
dolog ("Could not lock voice for %s\nReason: %s\n",
|
||||
fn_name, strerror (err));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_unlock (&core->mutex);
|
||||
if (err) {
|
||||
dolog ("Could not unlock voice for %s\nReason: %s\n",
|
||||
fn_name, strerror (err));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coreaudio_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
int live, decr;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (coreaudio_lock (core, "coreaudio_run_out")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
|
||||
if (core->decr > live) {
|
||||
ldebug ("core->decr %d live %d core->live %d\n",
|
||||
core->decr,
|
||||
live,
|
||||
core->live);
|
||||
}
|
||||
|
||||
decr = audio_MIN (core->decr, live);
|
||||
core->decr -= decr;
|
||||
|
||||
core->live = live - decr;
|
||||
hw->rpos = core->rpos;
|
||||
|
||||
coreaudio_unlock (core, "coreaudio_run_out");
|
||||
return decr;
|
||||
}
|
||||
|
||||
/* callback to feed audiooutput buffer */
|
||||
static OSStatus audioDeviceIOProc(
|
||||
AudioDeviceID inDevice,
|
||||
const AudioTimeStamp* inNow,
|
||||
const AudioBufferList* inInputData,
|
||||
const AudioTimeStamp* inInputTime,
|
||||
AudioBufferList* outOutputData,
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* hwptr)
|
||||
{
|
||||
UInt32 frame, frameCount;
|
||||
float *out = outOutputData->mBuffers[0].mData;
|
||||
HWVoiceOut *hw = hwptr;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
|
||||
int rpos, live;
|
||||
st_sample_t *src;
|
||||
#ifndef FLOAT_MIXENG
|
||||
#ifdef RECIPROCAL
|
||||
const float scale = 1.f / UINT_MAX;
|
||||
#else
|
||||
const float scale = UINT_MAX;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (coreaudio_lock (core, "audioDeviceIOProc")) {
|
||||
inInputTime = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
frameCount = core->audioDevicePropertyBufferFrameSize;
|
||||
live = core->live;
|
||||
|
||||
/* if there are not enough samples, set signal and return */
|
||||
if (live < frameCount) {
|
||||
inInputTime = 0;
|
||||
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rpos = core->rpos;
|
||||
src = hw->mix_buf + rpos;
|
||||
|
||||
/* fill buffer */
|
||||
for (frame = 0; frame < frameCount; frame++) {
|
||||
#ifdef FLOAT_MIXENG
|
||||
*out++ = src[frame].l; /* left channel */
|
||||
*out++ = src[frame].r; /* right channel */
|
||||
#else
|
||||
#ifdef RECIPROCAL
|
||||
*out++ = src[frame].l * scale; /* left channel */
|
||||
*out++ = src[frame].r * scale; /* right channel */
|
||||
#else
|
||||
*out++ = src[frame].l / scale; /* left channel */
|
||||
*out++ = src[frame].r / scale; /* right channel */
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
rpos = (rpos + frameCount) % hw->samples;
|
||||
core->decr += frameCount;
|
||||
core->rpos = rpos;
|
||||
|
||||
coreaudio_unlock (core, "audioDeviceIOProc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
UInt32 propertySize;
|
||||
int err;
|
||||
int bits = 8;
|
||||
const char *typ = "playback";
|
||||
AudioValueRange frameRange;
|
||||
|
||||
/* create mutex */
|
||||
err = pthread_mutex_init(&core->mutex, NULL);
|
||||
if (err) {
|
||||
dolog("Could not create mutex\nReason: %s\n", strerror (err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
|
||||
bits = 16;
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
|
||||
/* open default output device */
|
||||
propertySize = sizeof(core->outputDeviceID);
|
||||
status = AudioHardwareGetProperty(
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
&propertySize,
|
||||
&core->outputDeviceID);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get default output Device\n");
|
||||
return -1;
|
||||
}
|
||||
if (core->outputDeviceID == kAudioDeviceUnknown) {
|
||||
dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get minimum and maximum buffer frame sizes */
|
||||
propertySize = sizeof(frameRange);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&propertySize,
|
||||
&frameRange);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get device buffer frame range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frameRange.mMinimum > conf.buffer_frames) {
|
||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
|
||||
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
|
||||
}
|
||||
else if (frameRange.mMaximum < conf.buffer_frames) {
|
||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
|
||||
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
|
||||
}
|
||||
else {
|
||||
core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
|
||||
}
|
||||
|
||||
/* set Buffer Frame Size */
|
||||
propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
|
||||
status = AudioDeviceSetProperty(
|
||||
core->outputDeviceID,
|
||||
NULL,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
propertySize,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not set device buffer frame size %ld\n",
|
||||
core->audioDevicePropertyBufferFrameSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get Buffer Frame Size */
|
||||
propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
&propertySize,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get device buffer frame size\n");
|
||||
return -1;
|
||||
}
|
||||
hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
|
||||
|
||||
/* get StreamFormat */
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
&propertySize,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get Device Stream properties\n");
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set Samplerate */
|
||||
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
status = AudioDeviceSetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
propertySize,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
|
||||
as->freq);
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set Callback */
|
||||
status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start Playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not start playback\n");
|
||||
AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
OSStatus status;
|
||||
int err;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (!conf.isAtexit) {
|
||||
/* stop playback */
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not stop playback\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* remove callback */
|
||||
status = AudioDeviceRemoveIOProc(core->outputDeviceID,
|
||||
audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not remove IOProc\n");
|
||||
}
|
||||
}
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
|
||||
/* destroy mutex */
|
||||
err = pthread_mutex_destroy(&core->mutex);
|
||||
if (err) {
|
||||
dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
|
||||
}
|
||||
}
|
||||
|
||||
static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
/* start playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not resume playback\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
/* stop playback */
|
||||
if (!conf.isAtexit) {
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not pause playback\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *coreaudio_audio_init (void)
|
||||
{
|
||||
atexit(coreaudio_atexit);
|
||||
return &coreaudio_audio_init;
|
||||
}
|
||||
|
||||
static void coreaudio_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_option coreaudio_options[] = {
|
||||
{"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
|
||||
"Size of the buffer in frames", NULL, 0},
|
||||
{"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers,
|
||||
"Number of buffers", NULL, 0},
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||
coreaudio_init_out,
|
||||
coreaudio_fini_out,
|
||||
coreaudio_run_out,
|
||||
coreaudio_write,
|
||||
coreaudio_ctl_out,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct audio_driver coreaudio_audio_driver = {
|
||||
INIT_FIELD (name = ) "coreaudio",
|
||||
INIT_FIELD (descr = )
|
||||
"CoreAudio http://developer.apple.com/audio/coreaudio.html",
|
||||
INIT_FIELD (options = ) coreaudio_options,
|
||||
INIT_FIELD (init = ) coreaudio_audio_init,
|
||||
INIT_FIELD (fini = ) coreaudio_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) 1,
|
||||
INIT_FIELD (max_voices_in = ) 0,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) 0
|
||||
};
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* QEMU DirectSound audio driver header
|
||||
*
|
||||
* Copyright (c) 2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef DSBTYPE_IN
|
||||
#define NAME "capture buffer"
|
||||
#define TYPE in
|
||||
#define IFACE IDirectSoundCaptureBuffer
|
||||
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
|
||||
#define FIELD dsound_capture_buffer
|
||||
#else
|
||||
#define NAME "playback buffer"
|
||||
#define TYPE out
|
||||
#define IFACE IDirectSoundBuffer
|
||||
#define BUFPTR LPDIRECTSOUNDBUFFER
|
||||
#define FIELD dsound_buffer
|
||||
#endif
|
||||
|
||||
static int glue (dsound_unlock_, TYPE) (
|
||||
BUFPTR buf,
|
||||
LPVOID p1,
|
||||
LPVOID p2,
|
||||
DWORD blen1,
|
||||
DWORD blen2
|
||||
)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not unlock " NAME "\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int glue (dsound_lock_, TYPE) (
|
||||
BUFPTR buf,
|
||||
struct audio_pcm_info *info,
|
||||
DWORD pos,
|
||||
DWORD len,
|
||||
LPVOID *p1p,
|
||||
LPVOID *p2p,
|
||||
DWORD *blen1p,
|
||||
DWORD *blen2p,
|
||||
int entire
|
||||
)
|
||||
{
|
||||
HRESULT hr;
|
||||
int i;
|
||||
LPVOID p1 = NULL, p2 = NULL;
|
||||
DWORD blen1 = 0, blen2 = 0;
|
||||
DWORD flag;
|
||||
|
||||
#ifdef DSBTYPE_IN
|
||||
flag = entire ? DSCBLOCK_ENTIREBUFFER : 0;
|
||||
#else
|
||||
flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
|
||||
#endif
|
||||
for (i = 0; i < conf.lock_retries; ++i) {
|
||||
hr = glue (IFACE, _Lock) (
|
||||
buf,
|
||||
pos,
|
||||
len,
|
||||
&p1,
|
||||
&blen1,
|
||||
&p2,
|
||||
&blen2,
|
||||
flag
|
||||
);
|
||||
|
||||
if (FAILED (hr)) {
|
||||
#ifndef DSBTYPE_IN
|
||||
if (hr == DSERR_BUFFERLOST) {
|
||||
if (glue (dsound_restore_, TYPE) (buf)) {
|
||||
dsound_logerr (hr, "Could not lock " NAME "\n");
|
||||
goto fail;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
dsound_logerr (hr, "Could not lock " NAME "\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == conf.lock_retries) {
|
||||
dolog ("%d attempts to lock " NAME " failed\n", i);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
|
||||
dolog ("DirectSound returned misaligned buffer %ld %ld\n",
|
||||
blen1, blen2);
|
||||
glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!p1 && blen1) {
|
||||
dolog ("warning: !p1 && blen1=%ld\n", blen1);
|
||||
blen1 = 0;
|
||||
}
|
||||
|
||||
if (!p2 && blen2) {
|
||||
dolog ("warning: !p2 && blen2=%ld\n", blen2);
|
||||
blen2 = 0;
|
||||
}
|
||||
|
||||
*p1p = p1;
|
||||
*p2p = p2;
|
||||
*blen1p = blen1;
|
||||
*blen2p = blen2;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
*p1p = NULL - 1;
|
||||
*p2p = NULL - 1;
|
||||
*blen1p = -1;
|
||||
*blen2p = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DSBTYPE_IN
|
||||
static void dsound_fini_in (HWVoiceIn *hw)
|
||||
#else
|
||||
static void dsound_fini_out (HWVoiceOut *hw)
|
||||
#endif
|
||||
{
|
||||
HRESULT hr;
|
||||
#ifdef DSBTYPE_IN
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
#else
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
#endif
|
||||
|
||||
if (ds->FIELD) {
|
||||
hr = glue (IFACE, _Stop) (ds->FIELD);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not stop " NAME "\n");
|
||||
}
|
||||
|
||||
hr = glue (IFACE, _Release) (ds->FIELD);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not release " NAME "\n");
|
||||
}
|
||||
ds->FIELD = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DSBTYPE_IN
|
||||
static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
#else
|
||||
static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
#endif
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
dsound *s = &glob_dsound;
|
||||
WAVEFORMATEX wfx;
|
||||
audsettings_t obt_as;
|
||||
#ifdef DSBTYPE_IN
|
||||
const char *typ = "ADC";
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
DSCBUFFERDESC bd;
|
||||
DSCBCAPS bc;
|
||||
#else
|
||||
const char *typ = "DAC";
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
DSBUFFERDESC bd;
|
||||
DSBCAPS bc;
|
||||
#endif
|
||||
|
||||
err = waveformat_from_audio_settings (&wfx, as);
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset (&bd, 0, sizeof (bd));
|
||||
bd.dwSize = sizeof (bd);
|
||||
bd.lpwfxFormat = &wfx;
|
||||
#ifdef DSBTYPE_IN
|
||||
bd.dwBufferBytes = conf.bufsize_in;
|
||||
hr = IDirectSoundCapture_CreateCaptureBuffer (
|
||||
s->dsound_capture,
|
||||
&bd,
|
||||
&ds->dsound_capture_buffer,
|
||||
NULL
|
||||
);
|
||||
#else
|
||||
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
||||
bd.dwBufferBytes = conf.bufsize_out;
|
||||
hr = IDirectSound_CreateSoundBuffer (
|
||||
s->dsound,
|
||||
&bd,
|
||||
&ds->dsound_buffer,
|
||||
NULL
|
||||
);
|
||||
#endif
|
||||
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr2 (hr, typ, "Could not create " NAME "\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog (NAME "\n");
|
||||
print_wave_format (&wfx);
|
||||
#endif
|
||||
|
||||
memset (&bc, 0, sizeof (bc));
|
||||
bc.dwSize = sizeof (bc);
|
||||
|
||||
hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
err = waveformat_to_audio_settings (&wfx, &obt_as);
|
||||
if (err) {
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
ds->first_time = 1;
|
||||
obt_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
|
||||
if (bc.dwBufferBytes & hw->info.align) {
|
||||
dolog (
|
||||
"GetCaps returned misaligned buffer size %ld, alignment %d\n",
|
||||
bc.dwBufferBytes, hw->info.align + 1
|
||||
);
|
||||
}
|
||||
hw->samples = bc.dwBufferBytes >> hw->info.shift;
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("caps %ld, desc %ld\n",
|
||||
bc.dwBufferBytes, bd.dwBufferBytes);
|
||||
|
||||
dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
|
||||
hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
fail0:
|
||||
glue (dsound_fini_, TYPE) (hw);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef NAME
|
||||
#undef TYPE
|
||||
#undef IFACE
|
||||
#undef BUFPTR
|
||||
#undef FIELD
|
||||
+1080
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,685 @@
|
||||
/*
|
||||
* QEMU FMOD audio driver
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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 <fmod.h>
|
||||
#include <fmod_errors.h>
|
||||
#include "vl.h"
|
||||
|
||||
#define AUDIO_CAP "fmod"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct FMODVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
unsigned int old_pos;
|
||||
FSOUND_SAMPLE *fmod_sample;
|
||||
int channel;
|
||||
} FMODVoiceOut;
|
||||
|
||||
typedef struct FMODVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
FSOUND_SAMPLE *fmod_sample;
|
||||
} FMODVoiceIn;
|
||||
|
||||
static struct {
|
||||
const char *drvname;
|
||||
int nb_samples;
|
||||
int freq;
|
||||
int nb_channels;
|
||||
int bufsize;
|
||||
int threshold;
|
||||
int broken_adc;
|
||||
} conf = {
|
||||
NULL,
|
||||
2048 * 2,
|
||||
44100,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n",
|
||||
FMOD_ErrorString (FSOUND_GetError ()));
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n",
|
||||
FMOD_ErrorString (FSOUND_GetError ()));
|
||||
}
|
||||
|
||||
static int fmod_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static void fmod_clear_sample (FMODVoiceOut *fmd)
|
||||
{
|
||||
HWVoiceOut *hw = &fmd->hw;
|
||||
int status;
|
||||
void *p1 = 0, *p2 = 0;
|
||||
unsigned int len1 = 0, len2 = 0;
|
||||
|
||||
status = FSOUND_Sample_Lock (
|
||||
fmd->fmod_sample,
|
||||
0,
|
||||
hw->samples << hw->info.shift,
|
||||
&p1,
|
||||
&p2,
|
||||
&len1,
|
||||
&len2
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to lock sample\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
|
||||
dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
||||
len1, len2, hw->info.align + 1);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((len1 + len2) - (hw->samples << hw->info.shift)) {
|
||||
dolog ("Lock returned incomplete length %d, %d\n",
|
||||
len1 + len2, hw->samples << hw->info.shift);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
|
||||
|
||||
fail:
|
||||
status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to unlock sample\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
||||
{
|
||||
int src_len1 = dst_len;
|
||||
int src_len2 = 0;
|
||||
int pos = hw->rpos + dst_len;
|
||||
st_sample_t *src1 = hw->mix_buf + hw->rpos;
|
||||
st_sample_t *src2 = NULL;
|
||||
|
||||
if (pos > hw->samples) {
|
||||
src_len1 = hw->samples - hw->rpos;
|
||||
src2 = hw->mix_buf;
|
||||
src_len2 = dst_len - src_len1;
|
||||
pos = src_len2;
|
||||
}
|
||||
|
||||
if (src_len1) {
|
||||
hw->clip (dst, src1, src_len1);
|
||||
}
|
||||
|
||||
if (src_len2) {
|
||||
dst = advance (dst, src_len1 << hw->info.shift);
|
||||
hw->clip (dst, src2, src_len2);
|
||||
}
|
||||
|
||||
hw->rpos = pos % hw->samples;
|
||||
}
|
||||
|
||||
static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
|
||||
unsigned int blen1, unsigned int blen2)
|
||||
{
|
||||
int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to unlock sample\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_lock_sample (
|
||||
FSOUND_SAMPLE *sample,
|
||||
struct audio_pcm_info *info,
|
||||
int pos,
|
||||
int len,
|
||||
void **p1,
|
||||
void **p2,
|
||||
unsigned int *blen1,
|
||||
unsigned int *blen2
|
||||
)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = FSOUND_Sample_Lock (
|
||||
sample,
|
||||
pos << info->shift,
|
||||
len << info->shift,
|
||||
p1,
|
||||
p2,
|
||||
blen1,
|
||||
blen2
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to lock sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((*blen1 & info->align) || (*blen2 & info->align)) {
|
||||
dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
||||
*blen1, *blen2, info->align + 1);
|
||||
|
||||
fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
|
||||
|
||||
*p1 = NULL - 1;
|
||||
*p2 = NULL - 1;
|
||||
*blen1 = ~0U;
|
||||
*blen2 = ~0U;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!*p1 && *blen1) {
|
||||
dolog ("warning: !p1 && blen1=%d\n", *blen1);
|
||||
*blen1 = 0;
|
||||
}
|
||||
|
||||
if (!p2 && *blen2) {
|
||||
dolog ("warning: !p2 && blen2=%d\n", *blen2);
|
||||
*blen2 = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
int live, decr;
|
||||
void *p1 = 0, *p2 = 0;
|
||||
unsigned int blen1 = 0, blen2 = 0;
|
||||
unsigned int len1 = 0, len2 = 0;
|
||||
int nb_live;
|
||||
|
||||
live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!hw->pending_disable
|
||||
&& nb_live
|
||||
&& (conf.threshold && live <= conf.threshold)) {
|
||||
ldebug ("live=%d nb_live=%d\n", live, nb_live);
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = live;
|
||||
|
||||
if (fmd->channel >= 0) {
|
||||
int len = decr;
|
||||
int old_pos = fmd->old_pos;
|
||||
int ppos = FSOUND_GetCurrentPosition (fmd->channel);
|
||||
|
||||
if (ppos == old_pos || !ppos) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
||||
len = ppos - old_pos;
|
||||
}
|
||||
else {
|
||||
if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
|
||||
len = hw->samples - old_pos + ppos;
|
||||
}
|
||||
}
|
||||
decr = len;
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, decr < 0)) {
|
||||
dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
|
||||
decr, live, ppos, old_pos, len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
||||
fmd->old_pos, decr,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hw->info.shift;
|
||||
len2 = blen2 >> hw->info.shift;
|
||||
ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
|
||||
decr = len1 + len2;
|
||||
|
||||
if (p1 && len1) {
|
||||
fmod_write_sample (hw, p1, len1);
|
||||
}
|
||||
|
||||
if (p2 && len2) {
|
||||
fmod_write_sample (hw, p2, len2);
|
||||
}
|
||||
|
||||
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
|
||||
fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
|
||||
{
|
||||
int mode = FSOUND_LOOP_NORMAL;
|
||||
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
mode |= FSOUND_SIGNED | FSOUND_8BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
mode |= FSOUND_SIGNED | FSOUND_16BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_U16:
|
||||
mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_FMOD
|
||||
abort ();
|
||||
#endif
|
||||
mode |= FSOUND_8BITS;
|
||||
}
|
||||
mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void fmod_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
|
||||
if (fmd->fmod_sample) {
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
fmd->fmod_sample = 0;
|
||||
|
||||
if (fmd->channel >= 0) {
|
||||
FSOUND_StopSound (fmd->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
int bits16, mode, channel;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
audsettings_t obt_as = *as;
|
||||
|
||||
mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
|
||||
fmd->fmod_sample = FSOUND_Sample_Alloc (
|
||||
FSOUND_FREE, /* index */
|
||||
conf.nb_samples, /* length */
|
||||
mode, /* mode */
|
||||
as->freq, /* freq */
|
||||
255, /* volume */
|
||||
128, /* pan */
|
||||
255 /* priority */
|
||||
);
|
||||
|
||||
if (!fmd->fmod_sample) {
|
||||
fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
|
||||
if (channel < 0) {
|
||||
fmod_logerr2 ("DAC", "Failed to start playing sound\n");
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
return -1;
|
||||
}
|
||||
fmd->channel = channel;
|
||||
|
||||
/* FMOD always operates on little endian frames? */
|
||||
obt_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
bits16 = (mode & FSOUND_16BITS) != 0;
|
||||
hw->samples = conf.nb_samples;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int status;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
fmod_clear_sample (fmd);
|
||||
status = FSOUND_SetPaused (fmd->channel, 0);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
status = FSOUND_SetPaused (fmd->channel, 1);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
{
|
||||
int bits16, mode;
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
audsettings_t obt_as = *as;
|
||||
|
||||
if (conf.broken_adc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
|
||||
fmd->fmod_sample = FSOUND_Sample_Alloc (
|
||||
FSOUND_FREE, /* index */
|
||||
conf.nb_samples, /* length */
|
||||
mode, /* mode */
|
||||
as->freq, /* freq */
|
||||
255, /* volume */
|
||||
128, /* pan */
|
||||
255 /* priority */
|
||||
);
|
||||
|
||||
if (!fmd->fmod_sample) {
|
||||
fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FMOD always operates on little endian frames? */
|
||||
obt_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
bits16 = (mode & FSOUND_16BITS) != 0;
|
||||
hw->samples = conf.nb_samples;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmod_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
|
||||
if (fmd->fmod_sample) {
|
||||
FSOUND_Record_Stop ();
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
fmd->fmod_sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int live, dead, new_pos, len;
|
||||
unsigned int blen1 = 0, blen2 = 0;
|
||||
unsigned int len1, len2;
|
||||
unsigned int decr;
|
||||
void *p1, *p2;
|
||||
|
||||
live = audio_pcm_hw_get_live_in (hw);
|
||||
dead = hw->samples - live;
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_pos = FSOUND_Record_GetPosition ();
|
||||
if (new_pos < 0) {
|
||||
fmod_logerr ("Could not get recording position\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = audio_ring_dist (new_pos, hw->wpos, hw->samples);
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
len = audio_MIN (len, dead);
|
||||
|
||||
if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
||||
hw->wpos, len,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hwshift;
|
||||
len2 = blen2 >> hwshift;
|
||||
decr = len1 + len2;
|
||||
|
||||
if (p1 && blen1) {
|
||||
hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
|
||||
}
|
||||
if (p2 && len2) {
|
||||
hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
|
||||
}
|
||||
|
||||
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
hw->wpos = (hw->wpos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
int type;
|
||||
} drvtab[] = {
|
||||
{"none", FSOUND_OUTPUT_NOSOUND},
|
||||
#ifdef _WIN32
|
||||
{"winmm", FSOUND_OUTPUT_WINMM},
|
||||
{"dsound", FSOUND_OUTPUT_DSOUND},
|
||||
{"a3d", FSOUND_OUTPUT_A3D},
|
||||
{"asio", FSOUND_OUTPUT_ASIO},
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
{"oss", FSOUND_OUTPUT_OSS},
|
||||
{"alsa", FSOUND_OUTPUT_ALSA},
|
||||
{"esd", FSOUND_OUTPUT_ESD},
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
{"mac", FSOUND_OUTPUT_MAC},
|
||||
#endif
|
||||
#if 0
|
||||
{"xbox", FSOUND_OUTPUT_XBOX},
|
||||
{"ps2", FSOUND_OUTPUT_PS2},
|
||||
{"gcube", FSOUND_OUTPUT_GC},
|
||||
#endif
|
||||
{"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
|
||||
};
|
||||
|
||||
static void *fmod_audio_init (void)
|
||||
{
|
||||
size_t i;
|
||||
double ver;
|
||||
int status;
|
||||
int output_type = -1;
|
||||
const char *drv = conf.drvname;
|
||||
|
||||
ver = FSOUND_GetVersion ();
|
||||
if (ver < FMOD_VERSION) {
|
||||
dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
if (ver < 3.75) {
|
||||
dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
|
||||
"ADC will be disabled.\n");
|
||||
conf.broken_adc = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (drv) {
|
||||
int found = 0;
|
||||
for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
|
||||
if (!strcmp (drv, drvtab[i].name)) {
|
||||
output_type = drvtab[i].type;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
dolog ("Unknown FMOD driver `%s'\n", drv);
|
||||
dolog ("Valid drivers:\n");
|
||||
for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
|
||||
dolog (" %s\n", drvtab[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (output_type != -1) {
|
||||
status = FSOUND_SetOutput (output_type);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.bufsize) {
|
||||
status = FSOUND_SetBufferSize (conf.bufsize);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_Init failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static int fmod_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
int status;
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to start recording\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
status = FSOUND_Record_Stop ();
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to stop recording\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmod_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
FSOUND_Close ();
|
||||
}
|
||||
|
||||
static struct audio_option fmod_options[] = {
|
||||
{"DRV", AUD_OPT_STR, &conf.drvname,
|
||||
"FMOD driver", NULL, 0},
|
||||
{"FREQ", AUD_OPT_INT, &conf.freq,
|
||||
"Default frequency", NULL, 0},
|
||||
{"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
|
||||
"Buffer size in samples", NULL, 0},
|
||||
{"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
|
||||
"Number of default channels (1 - mono, 2 - stereo)", NULL, 0},
|
||||
{"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
|
||||
"(undocumented)", NULL, 0},
|
||||
#if 0
|
||||
{"THRESHOLD", AUD_OPT_INT, &conf.threshold,
|
||||
"(undocumented)"},
|
||||
#endif
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops fmod_pcm_ops = {
|
||||
fmod_init_out,
|
||||
fmod_fini_out,
|
||||
fmod_run_out,
|
||||
fmod_write,
|
||||
fmod_ctl_out,
|
||||
|
||||
fmod_init_in,
|
||||
fmod_fini_in,
|
||||
fmod_run_in,
|
||||
fmod_read,
|
||||
fmod_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver fmod_audio_driver = {
|
||||
INIT_FIELD (name = ) "fmod",
|
||||
INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org",
|
||||
INIT_FIELD (options = ) fmod_options,
|
||||
INIT_FIELD (init = ) fmod_audio_init,
|
||||
INIT_FIELD (fini = ) fmod_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &fmod_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn)
|
||||
};
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
* Copyright (c) 1998 Fabrice Bellard
|
||||
*
|
||||
* 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 "vl.h"
|
||||
|
||||
#define AUDIO_CAP "mixeng"
|
||||
#include "audio_int.h"
|
||||
|
||||
#define NOVOL
|
||||
|
||||
/* 8 bit */
|
||||
#define ENDIAN_CONVERSION natural
|
||||
#define ENDIAN_CONVERT(v) (v)
|
||||
|
||||
/* Signed 8 bit */
|
||||
#define IN_T int8_t
|
||||
#define IN_MIN SCHAR_MIN
|
||||
#define IN_MAX SCHAR_MAX
|
||||
#define SIGNED
|
||||
#define SHIFT 8
|
||||
#include "mixeng_template.h"
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
/* Unsigned 8 bit */
|
||||
#define IN_T uint8_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX UCHAR_MAX
|
||||
#define SHIFT 8
|
||||
#include "mixeng_template.h"
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
|
||||
/* Signed 16 bit */
|
||||
#define IN_T int16_t
|
||||
#define IN_MIN SHRT_MIN
|
||||
#define IN_MAX SHRT_MAX
|
||||
#define SIGNED
|
||||
#define SHIFT 16
|
||||
#define ENDIAN_CONVERSION natural
|
||||
#define ENDIAN_CONVERT(v) (v)
|
||||
#include "mixeng_template.h"
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
#define ENDIAN_CONVERSION swap
|
||||
#define ENDIAN_CONVERT(v) bswap16 (v)
|
||||
#include "mixeng_template.h"
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
#undef SIGNED
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
#define IN_T uint16_t
|
||||
#define IN_MIN 0
|
||||
#define IN_MAX USHRT_MAX
|
||||
#define SHIFT 16
|
||||
#define ENDIAN_CONVERSION natural
|
||||
#define ENDIAN_CONVERT(v) (v)
|
||||
#include "mixeng_template.h"
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
#define ENDIAN_CONVERSION swap
|
||||
#define ENDIAN_CONVERT(v) bswap16 (v)
|
||||
#include "mixeng_template.h"
|
||||
#undef ENDIAN_CONVERT
|
||||
#undef ENDIAN_CONVERSION
|
||||
#undef IN_MAX
|
||||
#undef IN_MIN
|
||||
#undef IN_T
|
||||
#undef SHIFT
|
||||
|
||||
t_sample *mixeng_conv[2][2][2][2] = {
|
||||
{
|
||||
{
|
||||
{
|
||||
conv_natural_uint8_t_to_mono,
|
||||
conv_natural_uint16_t_to_mono
|
||||
},
|
||||
{
|
||||
conv_natural_uint8_t_to_mono,
|
||||
conv_swap_uint16_t_to_mono
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
conv_natural_int8_t_to_mono,
|
||||
conv_natural_int16_t_to_mono
|
||||
},
|
||||
{
|
||||
conv_natural_int8_t_to_mono,
|
||||
conv_swap_int16_t_to_mono
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
{
|
||||
conv_natural_uint8_t_to_stereo,
|
||||
conv_natural_uint16_t_to_stereo
|
||||
},
|
||||
{
|
||||
conv_natural_uint8_t_to_stereo,
|
||||
conv_swap_uint16_t_to_stereo
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
conv_natural_int8_t_to_stereo,
|
||||
conv_natural_int16_t_to_stereo
|
||||
},
|
||||
{
|
||||
conv_natural_int8_t_to_stereo,
|
||||
conv_swap_int16_t_to_stereo
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
f_sample *mixeng_clip[2][2][2][2] = {
|
||||
{
|
||||
{
|
||||
{
|
||||
clip_natural_uint8_t_from_mono,
|
||||
clip_natural_uint16_t_from_mono
|
||||
},
|
||||
{
|
||||
clip_natural_uint8_t_from_mono,
|
||||
clip_swap_uint16_t_from_mono
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
clip_natural_int8_t_from_mono,
|
||||
clip_natural_int16_t_from_mono
|
||||
},
|
||||
{
|
||||
clip_natural_int8_t_from_mono,
|
||||
clip_swap_int16_t_from_mono
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
{
|
||||
clip_natural_uint8_t_from_stereo,
|
||||
clip_natural_uint16_t_from_stereo
|
||||
},
|
||||
{
|
||||
clip_natural_uint8_t_from_stereo,
|
||||
clip_swap_uint16_t_from_stereo
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
clip_natural_int8_t_from_stereo,
|
||||
clip_natural_int16_t_from_stereo
|
||||
},
|
||||
{
|
||||
clip_natural_int8_t_from_stereo,
|
||||
clip_swap_int16_t_from_stereo
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* August 21, 1998
|
||||
* Copyright 1998 Fabrice Bellard.
|
||||
*
|
||||
* [Rewrote completly the code of Lance Norskog And Sundry
|
||||
* Contributors with a more efficient algorithm.]
|
||||
*
|
||||
* This source code is freely redistributable and may be used for
|
||||
* any purpose. This copyright notice must be maintained.
|
||||
* Lance Norskog And Sundry Contributors are not responsible for
|
||||
* the consequences of using this software.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sound Tools rate change effect file.
|
||||
*/
|
||||
/*
|
||||
* Linear Interpolation.
|
||||
*
|
||||
* The use of fractional increment allows us to use no buffer. It
|
||||
* avoid the problems at the end of the buffer we had with the old
|
||||
* method which stored a possibly big buffer of size
|
||||
* lcm(in_rate,out_rate).
|
||||
*
|
||||
* Limited to 16 bit samples and sampling frequency <= 65535 Hz. If
|
||||
* the input & output frequencies are equal, a delay of one sample is
|
||||
* introduced. Limited to processing 32-bit count worth of samples.
|
||||
*
|
||||
* 1 << FRAC_BITS evaluating to zero in several places. Changed with
|
||||
* an (unsigned long) cast to make it safe. MarkMLl 2/1/99
|
||||
*/
|
||||
|
||||
/* Private data */
|
||||
struct rate {
|
||||
uint64_t opos;
|
||||
uint64_t opos_inc;
|
||||
uint32_t ipos; /* position in the input stream (integer) */
|
||||
st_sample_t ilast; /* last sample in the input stream */
|
||||
};
|
||||
|
||||
/*
|
||||
* Prepare processing.
|
||||
*/
|
||||
void *st_rate_start (int inrate, int outrate)
|
||||
{
|
||||
struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
|
||||
|
||||
if (!rate) {
|
||||
dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rate->opos = 0;
|
||||
|
||||
/* increment */
|
||||
rate->opos_inc = ((uint64_t) inrate << 32) / outrate;
|
||||
|
||||
rate->ipos = 0;
|
||||
rate->ilast.l = 0;
|
||||
rate->ilast.r = 0;
|
||||
return rate;
|
||||
}
|
||||
|
||||
#define NAME st_rate_flow_mix
|
||||
#define OP(a, b) a += b
|
||||
#include "rate_template.h"
|
||||
|
||||
#define NAME st_rate_flow
|
||||
#define OP(a, b) a = b
|
||||
#include "rate_template.h"
|
||||
|
||||
void st_rate_stop (void *opaque)
|
||||
{
|
||||
qemu_free (opaque);
|
||||
}
|
||||
|
||||
void mixeng_clear (st_sample_t *buf, int len)
|
||||
{
|
||||
memset (buf, 0, len * sizeof (st_sample_t));
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* QEMU Mixing engine header
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef QEMU_MIXENG_H
|
||||
#define QEMU_MIXENG_H
|
||||
|
||||
#ifdef FLOAT_MIXENG
|
||||
typedef float real_t;
|
||||
typedef struct { int mute; real_t r; real_t l; } volume_t;
|
||||
typedef struct { real_t l; real_t r; } st_sample_t;
|
||||
#else
|
||||
typedef struct { int mute; int64_t r; int64_t l; } volume_t;
|
||||
typedef struct { int64_t l; int64_t r; } st_sample_t;
|
||||
#endif
|
||||
|
||||
typedef void (t_sample) (st_sample_t *dst, const void *src,
|
||||
int samples, volume_t *vol);
|
||||
typedef void (f_sample) (void *dst, const st_sample_t *src, int samples);
|
||||
|
||||
extern t_sample *mixeng_conv[2][2][2][2];
|
||||
extern f_sample *mixeng_clip[2][2][2][2];
|
||||
|
||||
void *st_rate_start (int inrate, int outrate);
|
||||
void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
|
||||
int *isamp, int *osamp);
|
||||
void st_rate_flow_mix (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
|
||||
int *isamp, int *osamp);
|
||||
void st_rate_stop (void *opaque);
|
||||
void mixeng_clear (st_sample_t *buf, int len);
|
||||
|
||||
#endif /* mixeng.h */
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tusen tack till Mike Nordell
|
||||
* dec++'ified by Dscho
|
||||
*/
|
||||
|
||||
#ifndef SIGNED
|
||||
#define HALF (IN_MAX >> 1)
|
||||
#endif
|
||||
|
||||
#ifdef NOVOL
|
||||
#define VOL(a, b) a
|
||||
#else
|
||||
#ifdef FLOAT_MIXENG
|
||||
#define VOL(a, b) ((a) * (b))
|
||||
#else
|
||||
#define VOL(a, b) ((a) * (b)) >> 32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
|
||||
|
||||
#ifdef FLOAT_MIXENG
|
||||
static real_t inline glue (conv_, ET) (IN_T v)
|
||||
{
|
||||
IN_T nv = ENDIAN_CONVERT (v);
|
||||
|
||||
#ifdef RECIPROCAL
|
||||
#ifdef SIGNED
|
||||
return nv * (1.f / (real_t) (IN_MAX - IN_MIN));
|
||||
#else
|
||||
return (nv - HALF) * (1.f / (real_t) IN_MAX);
|
||||
#endif
|
||||
#else /* !RECIPROCAL */
|
||||
#ifdef SIGNED
|
||||
return nv / (real_t) (IN_MAX - IN_MIN);
|
||||
#else
|
||||
return (nv - HALF) / (real_t) IN_MAX;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static IN_T inline glue (clip_, ET) (real_t v)
|
||||
{
|
||||
if (v >= 0.5) {
|
||||
return IN_MAX;
|
||||
}
|
||||
else if (v < -0.5) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
#ifdef SIGNED
|
||||
return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
|
||||
#else
|
||||
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
|
||||
#endif
|
||||
}
|
||||
|
||||
#else /* !FLOAT_MIXENG */
|
||||
|
||||
static inline int64_t glue (conv_, ET) (IN_T v)
|
||||
{
|
||||
IN_T nv = ENDIAN_CONVERT (v);
|
||||
#ifdef SIGNED
|
||||
return ((int64_t) nv) << (32 - SHIFT);
|
||||
#else
|
||||
return ((int64_t) nv - HALF) << (32 - SHIFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline IN_T glue (clip_, ET) (int64_t v)
|
||||
{
|
||||
if (v >= 0x7f000000) {
|
||||
return IN_MAX;
|
||||
}
|
||||
else if (v < -2147483648LL) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
#ifdef SIGNED
|
||||
return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT)));
|
||||
#else
|
||||
return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void glue (glue (conv_, ET), _to_stereo)
|
||||
(st_sample_t *dst, const void *src, int samples, volume_t *vol)
|
||||
{
|
||||
st_sample_t *out = dst;
|
||||
IN_T *in = (IN_T *) src;
|
||||
#ifndef NOVOL
|
||||
if (vol->mute) {
|
||||
mixeng_clear (dst, samples);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
(void) vol;
|
||||
#endif
|
||||
while (samples--) {
|
||||
out->l = VOL (glue (conv_, ET) (*in++), vol->l);
|
||||
out->r = VOL (glue (conv_, ET) (*in++), vol->r);
|
||||
out += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (glue (conv_, ET), _to_mono)
|
||||
(st_sample_t *dst, const void *src, int samples, volume_t *vol)
|
||||
{
|
||||
st_sample_t *out = dst;
|
||||
IN_T *in = (IN_T *) src;
|
||||
#ifndef NOVOL
|
||||
if (vol->mute) {
|
||||
mixeng_clear (dst, samples);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
(void) vol;
|
||||
#endif
|
||||
while (samples--) {
|
||||
out->l = VOL (glue (conv_, ET) (in[0]), vol->l);
|
||||
out->r = out->l;
|
||||
out += 1;
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (glue (clip_, ET), _from_stereo)
|
||||
(void *dst, const st_sample_t *src, int samples)
|
||||
{
|
||||
const st_sample_t *in = src;
|
||||
IN_T *out = (IN_T *) dst;
|
||||
while (samples--) {
|
||||
*out++ = glue (clip_, ET) (in->l);
|
||||
*out++ = glue (clip_, ET) (in->r);
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue (glue (clip_, ET), _from_mono)
|
||||
(void *dst, const st_sample_t *src, int samples)
|
||||
{
|
||||
const st_sample_t *in = src;
|
||||
IN_T *out = (IN_T *) dst;
|
||||
while (samples--) {
|
||||
*out++ = glue (clip_, ET) (in->l + in->r);
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef ET
|
||||
#undef HALF
|
||||
#undef VOL
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* QEMU Timer based audio emulation
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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 "vl.h"
|
||||
|
||||
#define AUDIO_CAP "noaudio"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct NoVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int64_t old_ticks;
|
||||
} NoVoiceOut;
|
||||
|
||||
typedef struct NoVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
int64_t old_ticks;
|
||||
} NoVoiceIn;
|
||||
|
||||
static int no_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
int live, decr, samples;
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
|
||||
live = audio_pcm_hw_get_live_out (&no->hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
now = qemu_get_clock (vm_clock);
|
||||
ticks = now - no->old_ticks;
|
||||
bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||
bytes = audio_MIN (bytes, INT_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
|
||||
no->old_ticks = now;
|
||||
decr = audio_MIN (live, samples);
|
||||
hw->rpos = (hw->rpos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int no_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int no_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void no_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int no_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
{
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void no_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static int no_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
int samples = 0;
|
||||
|
||||
if (dead) {
|
||||
int64_t now = qemu_get_clock (vm_clock);
|
||||
int64_t ticks = now - no->old_ticks;
|
||||
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||
|
||||
no->old_ticks = now;
|
||||
bytes = audio_MIN (bytes, INT_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
samples = audio_MIN (samples, dead);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
static int no_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
int samples = size >> sw->info.shift;
|
||||
int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||
int to_clear = audio_MIN (samples, total);
|
||||
audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
|
||||
return to_clear;
|
||||
}
|
||||
|
||||
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *no_audio_init (void)
|
||||
{
|
||||
return &no_audio_init;
|
||||
}
|
||||
|
||||
static void no_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_pcm_ops no_pcm_ops = {
|
||||
no_init_out,
|
||||
no_fini_out,
|
||||
no_run_out,
|
||||
no_write,
|
||||
no_ctl_out,
|
||||
|
||||
no_init_in,
|
||||
no_fini_in,
|
||||
no_run_in,
|
||||
no_read,
|
||||
no_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver no_audio_driver = {
|
||||
INIT_FIELD (name = ) "none",
|
||||
INIT_FIELD (descr = ) "Timer based audio emulation",
|
||||
INIT_FIELD (options = ) NULL,
|
||||
INIT_FIELD (init = ) no_audio_init,
|
||||
INIT_FIELD (fini = ) no_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &no_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (NoVoiceIn)
|
||||
};
|
||||
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* QEMU OSS audio driver
|
||||
*
|
||||
* Copyright (c) 2003-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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 <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include "vl.h"
|
||||
|
||||
#define AUDIO_CAP "oss"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct OSSVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int mmapped;
|
||||
int old_optr;
|
||||
} OSSVoiceOut;
|
||||
|
||||
typedef struct OSSVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int old_optr;
|
||||
} OSSVoiceIn;
|
||||
|
||||
static struct {
|
||||
int try_mmap;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
const char *devpath_out;
|
||||
const char *devpath_in;
|
||||
int debug;
|
||||
} conf = {
|
||||
.try_mmap = 0,
|
||||
.nfrags = 4,
|
||||
.fragsize = 4096,
|
||||
.devpath_out = "/dev/dsp",
|
||||
.devpath_in = "/dev/dsp",
|
||||
.debug = 0
|
||||
};
|
||||
|
||||
struct oss_params {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
|
||||
int err,
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
static void oss_anal_close (int *fdp)
|
||||
{
|
||||
int err = close (*fdp);
|
||||
if (err) {
|
||||
oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
|
||||
}
|
||||
*fdp = -1;
|
||||
}
|
||||
|
||||
static int oss_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int aud_to_ossfmt (audfmt_e fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
return AFMT_S8;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
return AFMT_U8;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
return AFMT_S16_LE;
|
||||
|
||||
case AUD_FMT_U16:
|
||||
return AFMT_U16_LE;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
abort ();
|
||||
#endif
|
||||
return AFMT_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
|
||||
{
|
||||
switch (ossfmt) {
|
||||
case AFMT_S8:
|
||||
*endianness =0;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case AFMT_U8:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case AFMT_S16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AFMT_U16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case AFMT_S16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AFMT_U16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized audio format %d\n", ossfmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined DEBUG_MISMATCHES || defined DEBUG
|
||||
static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
|
||||
{
|
||||
dolog ("parameter | requested value | obtained value\n");
|
||||
dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
|
||||
dolog ("channels | %10d | %10d\n",
|
||||
req->nchannels, obt->nchannels);
|
||||
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
|
||||
dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags);
|
||||
dolog ("fragsize | %10d | %10d\n",
|
||||
req->fragsize, obt->fragsize);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int oss_open (int in, struct oss_params *req,
|
||||
struct oss_params *obt, int *pfd)
|
||||
{
|
||||
int fd;
|
||||
int mmmmssss;
|
||||
audio_buf_info abinfo;
|
||||
int fmt, freq, nchannels;
|
||||
const char *dspname = in ? conf.devpath_in : conf.devpath_out;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
|
||||
fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
|
||||
if (-1 == fd) {
|
||||
oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freq = req->freq;
|
||||
nchannels = req->nchannels;
|
||||
fmt = req->fmt;
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
|
||||
req->nchannels);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
|
||||
if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
|
||||
req->nfrags, req->fragsize);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
|
||||
oss_logerr2 (errno, typ, "Failed to get buffer length\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
obt->fmt = fmt;
|
||||
obt->nchannels = nchannels;
|
||||
obt->freq = freq;
|
||||
obt->nfrags = abinfo.fragstotal;
|
||||
obt->fragsize = abinfo.fragsize;
|
||||
*pfd = fd;
|
||||
|
||||
#ifdef DEBUG_MISMATCHES
|
||||
if ((req->fmt != obt->fmt) ||
|
||||
(req->nchannels != obt->nchannels) ||
|
||||
(req->freq != obt->freq) ||
|
||||
(req->fragsize != obt->fragsize) ||
|
||||
(req->nfrags != obt->nfrags)) {
|
||||
dolog ("Audio parameters mismatch\n");
|
||||
oss_dump_info (req, obt);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
oss_dump_info (req, obt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
err:
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int oss_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
int err, rpos, live, decr;
|
||||
int samples;
|
||||
uint8_t *dst;
|
||||
st_sample_t *src;
|
||||
struct audio_buf_info abinfo;
|
||||
struct count_info cntinfo;
|
||||
int bufsize;
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bufsize = hw->samples << hw->info.shift;
|
||||
|
||||
if (oss->mmapped) {
|
||||
int bytes;
|
||||
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cntinfo.ptr == oss->old_optr) {
|
||||
if (abs (hw->samples - live) < 64) {
|
||||
dolog ("warning: Overrun\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cntinfo.ptr > oss->old_optr) {
|
||||
bytes = cntinfo.ptr - oss->old_optr;
|
||||
}
|
||||
else {
|
||||
bytes = bufsize + cntinfo.ptr - oss->old_optr;
|
||||
}
|
||||
|
||||
decr = audio_MIN (bytes >> hw->info.shift, live);
|
||||
}
|
||||
else {
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (abinfo.bytes > bufsize) {
|
||||
if (conf.debug) {
|
||||
dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
|
||||
"please report your OS/audio hw to malc@pulsesoft.com\n",
|
||||
abinfo.bytes, bufsize);
|
||||
}
|
||||
abinfo.bytes = bufsize;
|
||||
}
|
||||
|
||||
if (abinfo.bytes < 0) {
|
||||
if (conf.debug) {
|
||||
dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
|
||||
abinfo.bytes, bufsize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
src = hw->mix_buf + rpos;
|
||||
dst = advance (oss->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, convert_samples);
|
||||
if (!oss->mmapped) {
|
||||
int written;
|
||||
|
||||
written = write (oss->fd, dst, convert_samples << hw->info.shift);
|
||||
/* XXX: follow errno recommendations ? */
|
||||
if (written == -1) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"Failed to write %d bytes of audio data from %p\n",
|
||||
convert_samples << hw->info.shift,
|
||||
dst
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (written != convert_samples << hw->info.shift) {
|
||||
int wsamples = written >> hw->info.shift;
|
||||
int wbytes = wsamples << hw->info.shift;
|
||||
if (wbytes != written) {
|
||||
dolog ("warning: Misaligned write %d (requested %d), "
|
||||
"alignment %d\n",
|
||||
wbytes, written, hw->info.align + 1);
|
||||
}
|
||||
decr -= wsamples;
|
||||
rpos = (rpos + wsamples) % hw->samples;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
}
|
||||
if (oss->mmapped) {
|
||||
oss->old_optr = cntinfo.ptr;
|
||||
}
|
||||
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void oss_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
int err;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
ldebug ("oss_fini\n");
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
if (oss->pcm_buf) {
|
||||
if (oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
}
|
||||
}
|
||||
else {
|
||||
qemu_free (oss->pcm_buf);
|
||||
}
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
struct oss_params req, obt;
|
||||
int endianness;
|
||||
int err;
|
||||
int fd;
|
||||
audfmt_e effective_fmt;
|
||||
audsettings_t obt_as;
|
||||
|
||||
oss->fd = -1;
|
||||
|
||||
req.fmt = aud_to_ossfmt (as->fmt);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.fragsize = conf.fragsize;
|
||||
req.nfrags = conf.nfrags;
|
||||
|
||||
if (oss_open (0, &req, &obt, &fd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
||||
if (err) {
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.nchannels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
obt_as.endianness = endianness;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
oss->nfrags = obt.nfrags;
|
||||
oss->fragsize = obt.fragsize;
|
||||
|
||||
if (obt.nfrags * obt.fragsize & hw->info.align) {
|
||||
dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
|
||||
obt.nfrags * obt.fragsize, hw->info.align + 1);
|
||||
}
|
||||
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
|
||||
oss->mmapped = 0;
|
||||
if (conf.try_mmap) {
|
||||
oss->pcm_buf = mmap (
|
||||
0,
|
||||
hw->samples << hw->info.shift,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
0
|
||||
);
|
||||
if (oss->pcm_buf == MAP_FAILED) {
|
||||
oss_logerr (errno, "Failed to map %d bytes of DAC\n",
|
||||
hw->samples << hw->info.shift);
|
||||
} else {
|
||||
int err;
|
||||
int trig = 0;
|
||||
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
|
||||
}
|
||||
else {
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
}
|
||||
else {
|
||||
oss->mmapped = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
oss->pcm_buf = audio_calloc (
|
||||
AUDIO_FUNC,
|
||||
hw->samples,
|
||||
1 << hw->info.shift
|
||||
);
|
||||
if (!oss->pcm_buf) {
|
||||
dolog (
|
||||
"Could not allocate DAC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples,
|
||||
1 << hw->info.shift
|
||||
);
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int trig;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
ldebug ("enabling voice\n");
|
||||
audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
trig = 0;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
struct oss_params req, obt;
|
||||
int endianness;
|
||||
int err;
|
||||
int fd;
|
||||
audfmt_e effective_fmt;
|
||||
audsettings_t obt_as;
|
||||
|
||||
oss->fd = -1;
|
||||
|
||||
req.fmt = aud_to_ossfmt (as->fmt);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.fragsize = conf.fragsize;
|
||||
req.nfrags = conf.nfrags;
|
||||
if (oss_open (1, &req, &obt, &fd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
|
||||
if (err) {
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.nchannels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
obt_as.endianness = endianness;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
oss->nfrags = obt.nfrags;
|
||||
oss->fragsize = obt.fragsize;
|
||||
|
||||
if (obt.nfrags * obt.fragsize & hw->info.align) {
|
||||
dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
|
||||
obt.nfrags * obt.fragsize, hw->info.align + 1);
|
||||
}
|
||||
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
if (!oss->pcm_buf) {
|
||||
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oss_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
if (oss->pcm_buf) {
|
||||
qemu_free (oss->pcm_buf);
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int i;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
size_t read_samples = 0;
|
||||
struct {
|
||||
int add;
|
||||
int len;
|
||||
} bufs[2] = {
|
||||
{ hw->wpos, 0 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hw->wpos + dead > hw->samples) {
|
||||
bufs[0].len = (hw->samples - hw->wpos) << hwshift;
|
||||
bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
|
||||
}
|
||||
else {
|
||||
bufs[0].len = dead << hwshift;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ssize_t nread;
|
||||
|
||||
if (bufs[i].len) {
|
||||
void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
|
||||
nread = read (oss->fd, p, bufs[i].len);
|
||||
|
||||
if (nread > 0) {
|
||||
if (nread & hw->info.align) {
|
||||
dolog ("warning: Misaligned read %zd (requested %d), "
|
||||
"alignment %d\n", nread, bufs[i].add << hwshift,
|
||||
hw->info.align + 1);
|
||||
}
|
||||
read_samples += nread >> hwshift;
|
||||
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
|
||||
&nominal_volume);
|
||||
}
|
||||
|
||||
if (bufs[i].len - nread) {
|
||||
if (nread == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
break;
|
||||
default:
|
||||
oss_logerr (
|
||||
errno,
|
||||
"Failed to read %d bytes of audio (to %p)\n",
|
||||
bufs[i].len, p
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hw->wpos = (hw->wpos + read_samples) % hw->samples;
|
||||
return read_samples;
|
||||
}
|
||||
|
||||
static int oss_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *oss_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void oss_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_option oss_options[] = {
|
||||
{"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
|
||||
"Fragment size in bytes", NULL, 0},
|
||||
{"NFRAGS", AUD_OPT_INT, &conf.nfrags,
|
||||
"Number of fragments", NULL, 0},
|
||||
{"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
|
||||
"Try using memory mapped access", NULL, 0},
|
||||
{"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
|
||||
"Path to DAC device", NULL, 0},
|
||||
{"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
|
||||
"Path to ADC device", NULL, 0},
|
||||
{"DEBUG", AUD_OPT_BOOL, &conf.debug,
|
||||
"Turn on some debugging messages", NULL, 0},
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops oss_pcm_ops = {
|
||||
oss_init_out,
|
||||
oss_fini_out,
|
||||
oss_run_out,
|
||||
oss_write,
|
||||
oss_ctl_out,
|
||||
|
||||
oss_init_in,
|
||||
oss_fini_in,
|
||||
oss_run_in,
|
||||
oss_read,
|
||||
oss_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver oss_audio_driver = {
|
||||
INIT_FIELD (name = ) "oss",
|
||||
INIT_FIELD (descr = ) "OSS http://www.opensound.com",
|
||||
INIT_FIELD (options = ) oss_options,
|
||||
INIT_FIELD (init = ) oss_audio_init,
|
||||
INIT_FIELD (fini = ) oss_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &oss_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) INT_MAX,
|
||||
INIT_FIELD (max_voices_in = ) INT_MAX,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) sizeof (OSSVoiceIn)
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
* Copyright (c) 1998 Fabrice Bellard
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Processed signed long samples from ibuf to obuf.
|
||||
* Return number of samples processed.
|
||||
*/
|
||||
void NAME (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
|
||||
int *isamp, int *osamp)
|
||||
{
|
||||
struct rate *rate = opaque;
|
||||
st_sample_t *istart, *iend;
|
||||
st_sample_t *ostart, *oend;
|
||||
st_sample_t ilast, icur, out;
|
||||
#ifdef FLOAT_MIXENG
|
||||
real_t t;
|
||||
#else
|
||||
int64_t t;
|
||||
#endif
|
||||
|
||||
ilast = rate->ilast;
|
||||
|
||||
istart = ibuf;
|
||||
iend = ibuf + *isamp;
|
||||
|
||||
ostart = obuf;
|
||||
oend = obuf + *osamp;
|
||||
|
||||
if (rate->opos_inc == (1ULL + UINT_MAX)) {
|
||||
int i, n = *isamp > *osamp ? *osamp : *isamp;
|
||||
for (i = 0; i < n; i++) {
|
||||
OP (obuf[i].l, ibuf[i].l);
|
||||
OP (obuf[i].r, ibuf[i].r);
|
||||
}
|
||||
*isamp = n;
|
||||
*osamp = n;
|
||||
return;
|
||||
}
|
||||
|
||||
while (obuf < oend) {
|
||||
|
||||
/* Safety catch to make sure we have input samples. */
|
||||
if (ibuf >= iend) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* read as many input samples so that ipos > opos */
|
||||
|
||||
while (rate->ipos <= (rate->opos >> 32)) {
|
||||
ilast = *ibuf++;
|
||||
rate->ipos++;
|
||||
/* See if we finished the input buffer yet */
|
||||
if (ibuf >= iend) {
|
||||
goto the_end;
|
||||
}
|
||||
}
|
||||
|
||||
icur = *ibuf;
|
||||
|
||||
/* interpolate */
|
||||
#ifdef FLOAT_MIXENG
|
||||
#ifdef RECIPROCAL
|
||||
t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX);
|
||||
#else
|
||||
t = (rate->opos & UINT_MAX) / (real_t) UINT_MAX;
|
||||
#endif
|
||||
out.l = (ilast.l * (1.0 - t)) + icur.l * t;
|
||||
out.r = (ilast.r * (1.0 - t)) + icur.r * t;
|
||||
#else
|
||||
t = rate->opos & 0xffffffff;
|
||||
out.l = (ilast.l * ((int64_t) UINT_MAX - t) + icur.l * t) >> 32;
|
||||
out.r = (ilast.r * ((int64_t) UINT_MAX - t) + icur.r * t) >> 32;
|
||||
#endif
|
||||
|
||||
/* output sample & increment position */
|
||||
OP (obuf->l, out.l);
|
||||
OP (obuf->r, out.r);
|
||||
obuf += 1;
|
||||
rate->opos += rate->opos_inc;
|
||||
}
|
||||
|
||||
the_end:
|
||||
*isamp = ibuf - istart;
|
||||
*osamp = obuf - ostart;
|
||||
rate->ilast = ilast;
|
||||
}
|
||||
|
||||
#undef NAME
|
||||
#undef OP
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* QEMU SDL audio driver
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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 <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
#include "vl.h"
|
||||
|
||||
#define AUDIO_CAP "sdl"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct SDLVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int live;
|
||||
int rpos;
|
||||
int decr;
|
||||
} SDLVoiceOut;
|
||||
|
||||
static struct {
|
||||
int nb_samples;
|
||||
} conf = {
|
||||
1024
|
||||
};
|
||||
|
||||
struct SDLAudioState {
|
||||
int exit;
|
||||
SDL_mutex *mutex;
|
||||
SDL_sem *sem;
|
||||
int initialized;
|
||||
} glob_sdl;
|
||||
typedef struct SDLAudioState SDLAudioState;
|
||||
|
||||
static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
|
||||
}
|
||||
|
||||
static int sdl_lock (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_LockMutex (s->mutex)) {
|
||||
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_UnlockMutex (s->mutex)) {
|
||||
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_post (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_SemPost (s->sem)) {
|
||||
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_wait (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_SemWait (s->sem)) {
|
||||
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (sdl_unlock (s, forfn)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sdl_post (s, forfn);
|
||||
}
|
||||
|
||||
static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
*shift = 0;
|
||||
return AUDIO_S8;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
*shift = 0;
|
||||
return AUDIO_U8;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
*shift = 1;
|
||||
return AUDIO_S16LSB;
|
||||
|
||||
case AUD_FMT_U16:
|
||||
*shift = 1;
|
||||
return AUDIO_U16LSB;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
abort ();
|
||||
#endif
|
||||
return AUDIO_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
|
||||
{
|
||||
switch (sdlfmt) {
|
||||
case AUDIO_S8:
|
||||
*endianess = 0;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case AUDIO_U8:
|
||||
*endianess = 0;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case AUDIO_S16LSB:
|
||||
*endianess = 0;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AUDIO_U16LSB:
|
||||
*endianess = 0;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case AUDIO_S16MSB:
|
||||
*endianess = 1;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AUDIO_U16MSB:
|
||||
*endianess = 1;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = SDL_OpenAudio (req, obt);
|
||||
if (status) {
|
||||
sdl_logerr ("SDL_OpenAudio failed\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void sdl_close (SDLAudioState *s)
|
||||
{
|
||||
if (s->initialized) {
|
||||
sdl_lock (s, "sdl_close");
|
||||
s->exit = 1;
|
||||
sdl_unlock_and_post (s, "sdl_close");
|
||||
SDL_PauseAudio (1);
|
||||
SDL_CloseAudio ();
|
||||
s->initialized = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
{
|
||||
SDLVoiceOut *sdl = opaque;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
HWVoiceOut *hw = &sdl->hw;
|
||||
int samples = len >> hw->info.shift;
|
||||
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (samples) {
|
||||
int to_mix, decr;
|
||||
|
||||
/* dolog ("in callback samples=%d\n", samples); */
|
||||
sdl_wait (s, "sdl_callback");
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sdl_lock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
|
||||
dolog ("sdl->live=%d hw->samples=%d\n",
|
||||
sdl->live, hw->samples);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sdl->live) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* dolog ("in callback live=%d\n", live); */
|
||||
to_mix = audio_MIN (samples, sdl->live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
|
||||
st_sample_t *src = hw->mix_buf + hw->rpos;
|
||||
|
||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||
hw->clip (buf, src, chunk);
|
||||
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->info.shift;
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
again:
|
||||
if (sdl_unlock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* dolog ("done len=%d\n", len); */
|
||||
}
|
||||
|
||||
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int sdl_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
int decr, live;
|
||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
|
||||
if (sdl_lock (s, "sdl_callback")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
|
||||
if (sdl->decr > live) {
|
||||
ldebug ("sdl->decr %d live %d sdl->live %d\n",
|
||||
sdl->decr,
|
||||
live,
|
||||
sdl->live);
|
||||
}
|
||||
|
||||
decr = audio_MIN (sdl->decr, live);
|
||||
sdl->decr -= decr;
|
||||
|
||||
sdl->live = live - decr;
|
||||
hw->rpos = sdl->rpos;
|
||||
|
||||
if (sdl->live > 0) {
|
||||
sdl_unlock_and_post (s, "sdl_callback");
|
||||
}
|
||||
else {
|
||||
sdl_unlock (s, "sdl_callback");
|
||||
}
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void sdl_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
(void) hw;
|
||||
|
||||
sdl_close (&glob_sdl);
|
||||
}
|
||||
|
||||
static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
SDL_AudioSpec req, obt;
|
||||
int shift;
|
||||
int endianess;
|
||||
int err;
|
||||
audfmt_e effective_fmt;
|
||||
audsettings_t obt_as;
|
||||
|
||||
shift <<= as->nchannels == 2;
|
||||
|
||||
req.freq = as->freq;
|
||||
req.format = aud_to_sdlfmt (as->fmt, &shift);
|
||||
req.channels = as->nchannels;
|
||||
req.samples = conf.nb_samples;
|
||||
req.callback = sdl_callback;
|
||||
req.userdata = sdl;
|
||||
|
||||
if (sdl_open (&req, &obt)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
|
||||
if (err) {
|
||||
sdl_close (s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as.freq = obt.freq;
|
||||
obt_as.nchannels = obt.channels;
|
||||
obt_as.fmt = effective_fmt;
|
||||
obt_as.endianness = endianess;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
s->initialized = 1;
|
||||
s->exit = 0;
|
||||
SDL_PauseAudio (0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
SDL_PauseAudio (0);
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
SDL_PauseAudio (1);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *sdl_audio_init (void)
|
||||
{
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
|
||||
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
|
||||
sdl_logerr ("SDL failed to initialize audio subsystem\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->mutex = SDL_CreateMutex ();
|
||||
if (!s->mutex) {
|
||||
sdl_logerr ("Failed to create SDL mutex\n");
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->sem = SDL_CreateSemaphore (0);
|
||||
if (!s->sem) {
|
||||
sdl_logerr ("Failed to create SDL semaphore\n");
|
||||
SDL_DestroyMutex (s->mutex);
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void sdl_audio_fini (void *opaque)
|
||||
{
|
||||
SDLAudioState *s = opaque;
|
||||
sdl_close (s);
|
||||
SDL_DestroySemaphore (s->sem);
|
||||
SDL_DestroyMutex (s->mutex);
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
}
|
||||
|
||||
static struct audio_option sdl_options[] = {
|
||||
{"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
|
||||
"Size of SDL buffer in samples", NULL, 0},
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||
sdl_init_out,
|
||||
sdl_fini_out,
|
||||
sdl_run_out,
|
||||
sdl_write_out,
|
||||
sdl_ctl_out,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct audio_driver sdl_audio_driver = {
|
||||
INIT_FIELD (name = ) "sdl",
|
||||
INIT_FIELD (descr = ) "SDL http://www.libsdl.org",
|
||||
INIT_FIELD (options = ) sdl_options,
|
||||
INIT_FIELD (init = ) sdl_audio_init,
|
||||
INIT_FIELD (fini = ) sdl_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &sdl_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 1,
|
||||
INIT_FIELD (max_voices_out = ) 1,
|
||||
INIT_FIELD (max_voices_in = ) 0,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) 0
|
||||
};
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 4. Neither the name of the University 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 REGENTS AND 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 REGENTS 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.
|
||||
*
|
||||
* @(#)queue.h 8.3 (Berkeley) 12/13/93
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H
|
||||
#define _SYS_QUEUE_H 1
|
||||
|
||||
/*
|
||||
* This file defines three types of data structures: lists, tail queues,
|
||||
* and circular queues.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list after
|
||||
* an existing element or at the head of the list. A list may only be
|
||||
* traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A tail queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) { \
|
||||
(head)->lh_first = NULL; \
|
||||
}
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
}
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
}
|
||||
|
||||
#define LIST_REMOVE(elm, field) { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
}
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
}
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
}
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
}
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) { \
|
||||
(head)->cqh_first = (void *)(head); \
|
||||
(head)->cqh_last = (void *)(head); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = (void *)(head); \
|
||||
if ((head)->cqh_last == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \
|
||||
(elm)->field.cqe_next = (void *)(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) { \
|
||||
if ((elm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
}
|
||||
#endif /* sys/queue.h */
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* QEMU WAV audio driver
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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 "vl.h"
|
||||
|
||||
#define AUDIO_CAP "wav"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct WAVVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
QEMUFile *f;
|
||||
int64_t old_ticks;
|
||||
void *pcm_buf;
|
||||
int total_samples;
|
||||
} WAVVoiceOut;
|
||||
|
||||
static struct {
|
||||
audsettings_t settings;
|
||||
const char *wav_path;
|
||||
} conf = {
|
||||
{
|
||||
44100,
|
||||
2,
|
||||
AUD_FMT_S16
|
||||
},
|
||||
"qemu.wav"
|
||||
};
|
||||
|
||||
static int wav_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
int rpos, live, decr, samples;
|
||||
uint8_t *dst;
|
||||
st_sample_t *src;
|
||||
int64_t now = qemu_get_clock (vm_clock);
|
||||
int64_t ticks = now - wav->old_ticks;
|
||||
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||
|
||||
if (bytes > INT_MAX) {
|
||||
samples = INT_MAX >> hw->info.shift;
|
||||
}
|
||||
else {
|
||||
samples = bytes >> hw->info.shift;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
wav->old_ticks = now;
|
||||
decr = audio_MIN (live, samples);
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
src = hw->mix_buf + rpos;
|
||||
dst = advance (wav->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, convert_samples);
|
||||
qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
wav->total_samples += convert_samples;
|
||||
}
|
||||
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
/* VICE code: Store number as little endian. */
|
||||
static void le_store (uint8_t *buf, uint32_t val, int len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = (uint8_t) (val & 0xff);
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
int bits16 = 0, stereo = 0;
|
||||
uint8_t hdr[] = {
|
||||
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
|
||||
0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
||||
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
audsettings_t wav_as = conf.settings;
|
||||
|
||||
(void) as;
|
||||
|
||||
stereo = wav_as.nchannels == 2;
|
||||
switch (wav_as.fmt) {
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
bits16 = 0;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
bits16 = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
hdr[34] = bits16 ? 0x10 : 0x08;
|
||||
|
||||
wav_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &wav_as);
|
||||
|
||||
hw->samples = 1024;
|
||||
wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
if (!wav->pcm_buf) {
|
||||
dolog ("Could not allocate buffer (%d bytes)\n",
|
||||
hw->samples << hw->info.shift);
|
||||
return -1;
|
||||
}
|
||||
|
||||
le_store (hdr + 22, hw->info.nchannels, 2);
|
||||
le_store (hdr + 24, hw->info.freq, 4);
|
||||
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
|
||||
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
|
||||
|
||||
wav->f = qemu_fopen (conf.wav_path, "wb");
|
||||
if (!wav->f) {
|
||||
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
||||
conf.wav_path, strerror (errno));
|
||||
qemu_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemu_put_buffer (wav->f, hdr, sizeof (hdr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wav_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
uint8_t rlen[4];
|
||||
uint8_t dlen[4];
|
||||
uint32_t datalen = wav->total_samples << hw->info.shift;
|
||||
uint32_t rifflen = datalen + 36;
|
||||
|
||||
if (!wav->f) {
|
||||
return;
|
||||
}
|
||||
|
||||
le_store (rlen, rifflen, 4);
|
||||
le_store (dlen, datalen, 4);
|
||||
|
||||
qemu_fseek (wav->f, 4, SEEK_SET);
|
||||
qemu_put_buffer (wav->f, rlen, 4);
|
||||
|
||||
qemu_fseek (wav->f, 32, SEEK_CUR);
|
||||
qemu_put_buffer (wav->f, dlen, 4);
|
||||
|
||||
qemu_fclose (wav->f);
|
||||
wav->f = NULL;
|
||||
|
||||
qemu_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *wav_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void wav_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
ldebug ("wav_fini");
|
||||
}
|
||||
|
||||
struct audio_option wav_options[] = {
|
||||
{"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
|
||||
"Frequency", NULL, 0},
|
||||
|
||||
{"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
|
||||
"Format", NULL, 0},
|
||||
|
||||
{"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
|
||||
"Number of channels (1 - mono, 2 - stereo)", NULL, 0},
|
||||
|
||||
{"PATH", AUD_OPT_STR, &conf.wav_path,
|
||||
"Path to wave file", NULL, 0},
|
||||
{NULL, 0, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
struct audio_pcm_ops wav_pcm_ops = {
|
||||
wav_init_out,
|
||||
wav_fini_out,
|
||||
wav_run_out,
|
||||
wav_write_out,
|
||||
wav_ctl_out,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct audio_driver wav_audio_driver = {
|
||||
INIT_FIELD (name = ) "wav",
|
||||
INIT_FIELD (descr = )
|
||||
"WAV renderer http://wikipedia.org/wiki/WAV",
|
||||
INIT_FIELD (options = ) wav_options,
|
||||
INIT_FIELD (init = ) wav_audio_init,
|
||||
INIT_FIELD (fini = ) wav_audio_fini,
|
||||
INIT_FIELD (pcm_ops = ) &wav_pcm_ops,
|
||||
INIT_FIELD (can_be_default = ) 0,
|
||||
INIT_FIELD (max_voices_out = ) 1,
|
||||
INIT_FIELD (max_voices_in = ) 0,
|
||||
INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
|
||||
INIT_FIELD (voice_size_in = ) 0
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
#include "vl.h"
|
||||
|
||||
typedef struct {
|
||||
QEMUFile *f;
|
||||
int bytes;
|
||||
char *path;
|
||||
int freq;
|
||||
int bits;
|
||||
int nchannels;
|
||||
CaptureVoiceOut *cap;
|
||||
} WAVState;
|
||||
|
||||
/* VICE code: Store number as little endian. */
|
||||
static void le_store (uint8_t *buf, uint32_t val, int len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = (uint8_t) (val & 0xff);
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void wav_notify (void *opaque, audcnotification_e cmd)
|
||||
{
|
||||
(void) opaque;
|
||||
(void) cmd;
|
||||
}
|
||||
|
||||
static void wav_destroy (void *opaque)
|
||||
{
|
||||
WAVState *wav = opaque;
|
||||
uint8_t rlen[4];
|
||||
uint8_t dlen[4];
|
||||
uint32_t datalen = wav->bytes;
|
||||
uint32_t rifflen = datalen + 36;
|
||||
|
||||
if (wav->f) {
|
||||
le_store (rlen, rifflen, 4);
|
||||
le_store (dlen, datalen, 4);
|
||||
|
||||
qemu_fseek (wav->f, 4, SEEK_SET);
|
||||
qemu_put_buffer (wav->f, rlen, 4);
|
||||
|
||||
qemu_fseek (wav->f, 32, SEEK_CUR);
|
||||
qemu_put_buffer (wav->f, dlen, 4);
|
||||
qemu_fclose (wav->f);
|
||||
}
|
||||
|
||||
qemu_free (wav->path);
|
||||
}
|
||||
|
||||
static void wav_capture (void *opaque, void *buf, int size)
|
||||
{
|
||||
WAVState *wav = opaque;
|
||||
|
||||
qemu_put_buffer (wav->f, buf, size);
|
||||
wav->bytes += size;
|
||||
}
|
||||
|
||||
static void wav_capture_destroy (void *opaque)
|
||||
{
|
||||
WAVState *wav = opaque;
|
||||
|
||||
AUD_del_capture (wav->cap, wav);
|
||||
}
|
||||
|
||||
static void wav_capture_info (void *opaque)
|
||||
{
|
||||
WAVState *wav = opaque;
|
||||
char *path = wav->path;
|
||||
|
||||
term_printf ("Capturing audio(%d,%d,%d) to %s: %d bytes\n",
|
||||
wav->freq, wav->bits, wav->nchannels,
|
||||
path ? path : "<not available>", wav->bytes);
|
||||
}
|
||||
|
||||
static struct capture_ops wav_capture_ops = {
|
||||
.destroy = wav_capture_destroy,
|
||||
.info = wav_capture_info
|
||||
};
|
||||
|
||||
int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
int bits, int nchannels)
|
||||
{
|
||||
WAVState *wav;
|
||||
uint8_t hdr[] = {
|
||||
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
|
||||
0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
||||
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
audsettings_t as;
|
||||
struct audio_capture_ops ops;
|
||||
int stereo, bits16, shift;
|
||||
CaptureVoiceOut *cap;
|
||||
|
||||
if (bits != 8 && bits != 16) {
|
||||
term_printf ("incorrect bit count %d, must be 8 or 16\n", bits);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nchannels != 1 && nchannels != 2) {
|
||||
term_printf ("incorrect channel count %d, must be 1 or 2\n",
|
||||
nchannels);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stereo = nchannels == 2;
|
||||
bits16 = bits == 16;
|
||||
|
||||
as.freq = freq;
|
||||
as.nchannels = 1 << stereo;
|
||||
as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
|
||||
as.endianness = 0;
|
||||
|
||||
ops.notify = wav_notify;
|
||||
ops.capture = wav_capture;
|
||||
ops.destroy = wav_destroy;
|
||||
|
||||
wav = qemu_mallocz (sizeof (*wav));
|
||||
if (!wav) {
|
||||
term_printf ("Could not allocate memory for wav capture (%zu bytes)",
|
||||
sizeof (*wav));
|
||||
return -1;
|
||||
}
|
||||
|
||||
shift = bits16 + stereo;
|
||||
hdr[34] = bits16 ? 0x10 : 0x08;
|
||||
|
||||
le_store (hdr + 22, as.nchannels, 2);
|
||||
le_store (hdr + 24, freq, 4);
|
||||
le_store (hdr + 28, freq << shift, 4);
|
||||
le_store (hdr + 32, 1 << shift, 2);
|
||||
|
||||
wav->f = qemu_fopen (path, "wb");
|
||||
if (!wav->f) {
|
||||
term_printf ("Failed to open wave file `%s'\nReason: %s\n",
|
||||
path, strerror (errno));
|
||||
qemu_free (wav);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wav->path = qemu_strdup (path);
|
||||
wav->bits = bits;
|
||||
wav->nchannels = nchannels;
|
||||
wav->freq = freq;
|
||||
|
||||
qemu_put_buffer (wav->f, hdr, sizeof (hdr));
|
||||
|
||||
cap = AUD_add_capture (NULL, &as, &ops, wav);
|
||||
if (!cap) {
|
||||
term_printf ("Failed to add audio capture\n");
|
||||
qemu_free (wav->path);
|
||||
qemu_fclose (wav->f);
|
||||
qemu_free (wav);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wav->cap = cap;
|
||||
s->opaque = wav;
|
||||
s->ops = wav_capture_ops;
|
||||
return 0;
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Block driver for the various disk image formats used by Bochs
|
||||
* Currently only for "growing" type in read-only mode
|
||||
*
|
||||
* Copyright (c) 2005 Alex Beregszaszi
|
||||
*
|
||||
* 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 "vl.h"
|
||||
#include "block_int.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
#define HEADER_MAGIC "Bochs Virtual HD Image"
|
||||
#define HEADER_VERSION 0x00020000
|
||||
#define HEADER_V1 0x00010000
|
||||
#define HEADER_SIZE 512
|
||||
|
||||
#define REDOLOG_TYPE "Redolog"
|
||||
#define GROWING_TYPE "Growing"
|
||||
|
||||
// not allocated: 0xffffffff
|
||||
|
||||
// always little-endian
|
||||
struct bochs_header_v1 {
|
||||
char magic[32]; // "Bochs Virtual HD Image"
|
||||
char type[16]; // "Redolog"
|
||||
char subtype[16]; // "Undoable" / "Volatile" / "Growing"
|
||||
uint32_t version;
|
||||
uint32_t header; // size of header
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t catalog; // num of entries
|
||||
uint32_t bitmap; // bitmap size
|
||||
uint32_t extent; // extent size
|
||||
uint64_t disk; // disk size
|
||||
char padding[HEADER_SIZE - 64 - 8 - 20];
|
||||
} redolog;
|
||||
char padding[HEADER_SIZE - 64 - 8];
|
||||
} extra;
|
||||
};
|
||||
|
||||
// always little-endian
|
||||
struct bochs_header {
|
||||
char magic[32]; // "Bochs Virtual HD Image"
|
||||
char type[16]; // "Redolog"
|
||||
char subtype[16]; // "Undoable" / "Volatile" / "Growing"
|
||||
uint32_t version;
|
||||
uint32_t header; // size of header
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t catalog; // num of entries
|
||||
uint32_t bitmap; // bitmap size
|
||||
uint32_t extent; // extent size
|
||||
uint32_t reserved; // for ???
|
||||
uint64_t disk; // disk size
|
||||
char padding[HEADER_SIZE - 64 - 8 - 24];
|
||||
} redolog;
|
||||
char padding[HEADER_SIZE - 64 - 8];
|
||||
} extra;
|
||||
};
|
||||
|
||||
typedef struct BDRVBochsState {
|
||||
int fd;
|
||||
|
||||
uint32_t *catalog_bitmap;
|
||||
int catalog_size;
|
||||
|
||||
int data_offset;
|
||||
|
||||
int bitmap_blocks;
|
||||
int extent_blocks;
|
||||
int extent_size;
|
||||
} BDRVBochsState;
|
||||
|
||||
static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const struct bochs_header *bochs = (const void *)buf;
|
||||
|
||||
if (buf_size < HEADER_SIZE)
|
||||
return 0;
|
||||
|
||||
if (!strcmp(bochs->magic, HEADER_MAGIC) &&
|
||||
!strcmp(bochs->type, REDOLOG_TYPE) &&
|
||||
!strcmp(bochs->subtype, GROWING_TYPE) &&
|
||||
((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
|
||||
(le32_to_cpu(bochs->version) == HEADER_V1)))
|
||||
return 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int fd, i;
|
||||
struct bochs_header bochs;
|
||||
struct bochs_header_v1 header_v1;
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
bs->read_only = 1; // no write support yet
|
||||
|
||||
s->fd = fd;
|
||||
|
||||
if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (strcmp(bochs.magic, HEADER_MAGIC) ||
|
||||
strcmp(bochs.type, REDOLOG_TYPE) ||
|
||||
strcmp(bochs.subtype, GROWING_TYPE) ||
|
||||
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
||||
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(bochs.version) == HEADER_V1) {
|
||||
memcpy(&header_v1, &bochs, sizeof(bochs));
|
||||
bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512;
|
||||
} else {
|
||||
bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
|
||||
}
|
||||
|
||||
lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET);
|
||||
|
||||
s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
|
||||
s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
|
||||
if (!s->catalog_bitmap)
|
||||
goto fail;
|
||||
if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) !=
|
||||
s->catalog_size * 4)
|
||||
goto fail;
|
||||
for (i = 0; i < s->catalog_size; i++)
|
||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||
|
||||
s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
|
||||
|
||||
s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
|
||||
s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
|
||||
|
||||
s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int64_t offset = sector_num * 512;
|
||||
int64_t extent_index, extent_offset, bitmap_offset, block_offset;
|
||||
char bitmap_entry;
|
||||
|
||||
// seek to sector
|
||||
extent_index = offset / s->extent_size;
|
||||
extent_offset = (offset % s->extent_size) / 512;
|
||||
|
||||
if (s->catalog_bitmap[extent_index] == 0xffffffff)
|
||||
{
|
||||
// fprintf(stderr, "page not allocated [%x - %x:%x]\n",
|
||||
// sector_num, extent_index, extent_offset);
|
||||
return -1; // not allocated
|
||||
}
|
||||
|
||||
bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
|
||||
(s->extent_blocks + s->bitmap_blocks));
|
||||
block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
|
||||
|
||||
// fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n",
|
||||
// sector_num, extent_index, extent_offset,
|
||||
// le32_to_cpu(s->catalog_bitmap[extent_index]),
|
||||
// bitmap_offset, block_offset);
|
||||
|
||||
// read in bitmap for current extent
|
||||
lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET);
|
||||
|
||||
read(s->fd, &bitmap_entry, 1);
|
||||
|
||||
if (!((bitmap_entry >> (extent_offset % 8)) & 1))
|
||||
{
|
||||
// fprintf(stderr, "sector (%x) in bitmap not allocated\n",
|
||||
// sector_num);
|
||||
return -1; // not allocated
|
||||
}
|
||||
|
||||
lseek(s->fd, block_offset, SEEK_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (!seek_to_sector(bs, sector_num))
|
||||
{
|
||||
ret = read(s->fd, buf, 512);
|
||||
if (ret != 512)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
memset(buf, 0, 512);
|
||||
nb_sectors--;
|
||||
sector_num++;
|
||||
buf += 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bochs_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
qemu_free(s->catalog_bitmap);
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_bochs = {
|
||||
"bochs",
|
||||
sizeof(BDRVBochsState),
|
||||
bochs_probe,
|
||||
bochs_open,
|
||||
bochs_read,
|
||||
NULL,
|
||||
bochs_close,
|
||||
};
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* QEMU Block driver for CLOOP images
|
||||
*
|
||||
* Copyright (c) 2004 Johannes E. Schindelin
|
||||
*
|
||||
* 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 "vl.h"
|
||||
#include "block_int.h"
|
||||
#include <zlib.h>
|
||||
|
||||
typedef struct BDRVCloopState {
|
||||
int fd;
|
||||
uint32_t block_size;
|
||||
uint32_t n_blocks;
|
||||
uint64_t* offsets;
|
||||
uint32_t sectors_per_block;
|
||||
uint32_t current_block;
|
||||
uint8_t *compressed_block;
|
||||
uint8_t *uncompressed_block;
|
||||
z_stream zstream;
|
||||
} BDRVCloopState;
|
||||
|
||||
static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const char* magic_version_2_0="#!/bin/sh\n"
|
||||
"#V2.0 Format\n"
|
||||
"modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n";
|
||||
int length=strlen(magic_version_2_0);
|
||||
if(length>buf_size)
|
||||
length=buf_size;
|
||||
if(!memcmp(magic_version_2_0,buf,length))
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
uint32_t offsets_size,max_compressed_block_size=1,i;
|
||||
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (s->fd < 0)
|
||||
return -errno;
|
||||
bs->read_only = 1;
|
||||
|
||||
/* read header */
|
||||
if(lseek(s->fd,128,SEEK_SET)<0) {
|
||||
cloop_close:
|
||||
close(s->fd);
|
||||
return -1;
|
||||
}
|
||||
if(read(s->fd,&s->block_size,4)<4)
|
||||
goto cloop_close;
|
||||
s->block_size=be32_to_cpu(s->block_size);
|
||||
if(read(s->fd,&s->n_blocks,4)<4)
|
||||
goto cloop_close;
|
||||
s->n_blocks=be32_to_cpu(s->n_blocks);
|
||||
|
||||
/* read offsets */
|
||||
offsets_size=s->n_blocks*sizeof(uint64_t);
|
||||
if(!(s->offsets=(uint64_t*)malloc(offsets_size)))
|
||||
goto cloop_close;
|
||||
if(read(s->fd,s->offsets,offsets_size)<offsets_size)
|
||||
goto cloop_close;
|
||||
for(i=0;i<s->n_blocks;i++) {
|
||||
s->offsets[i]=be64_to_cpu(s->offsets[i]);
|
||||
if(i>0) {
|
||||
uint32_t size=s->offsets[i]-s->offsets[i-1];
|
||||
if(size>max_compressed_block_size)
|
||||
max_compressed_block_size=size;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
if(!(s->compressed_block = malloc(max_compressed_block_size+1)))
|
||||
goto cloop_close;
|
||||
if(!(s->uncompressed_block = malloc(s->block_size)))
|
||||
goto cloop_close;
|
||||
if(inflateInit(&s->zstream) != Z_OK)
|
||||
goto cloop_close;
|
||||
s->current_block=s->n_blocks;
|
||||
|
||||
s->sectors_per_block = s->block_size/512;
|
||||
bs->total_sectors = s->n_blocks*s->sectors_per_block;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cloop_read_block(BDRVCloopState *s,int block_num)
|
||||
{
|
||||
if(s->current_block != block_num) {
|
||||
int ret;
|
||||
uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num];
|
||||
|
||||
lseek(s->fd, s->offsets[block_num], SEEK_SET);
|
||||
ret = read(s->fd, s->compressed_block, bytes);
|
||||
if (ret != bytes)
|
||||
return -1;
|
||||
|
||||
s->zstream.next_in = s->compressed_block;
|
||||
s->zstream.avail_in = bytes;
|
||||
s->zstream.next_out = s->uncompressed_block;
|
||||
s->zstream.avail_out = s->block_size;
|
||||
ret = inflateReset(&s->zstream);
|
||||
if(ret != Z_OK)
|
||||
return -1;
|
||||
ret = inflate(&s->zstream, Z_FINISH);
|
||||
if(ret != Z_STREAM_END || s->zstream.total_out != s->block_size)
|
||||
return -1;
|
||||
|
||||
s->current_block = block_num;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for(i=0;i<nb_sectors;i++) {
|
||||
uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block),
|
||||
block_num=(sector_num+i)/s->sectors_per_block;
|
||||
if(cloop_read_block(s, block_num) != 0)
|
||||
return -1;
|
||||
memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cloop_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
close(s->fd);
|
||||
if(s->n_blocks>0)
|
||||
free(s->offsets);
|
||||
free(s->compressed_block);
|
||||
free(s->uncompressed_block);
|
||||
inflateEnd(&s->zstream);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_cloop = {
|
||||
"cloop",
|
||||
sizeof(BDRVCloopState),
|
||||
cloop_probe,
|
||||
cloop_open,
|
||||
cloop_read,
|
||||
NULL,
|
||||
cloop_close,
|
||||
};
|
||||
|
||||
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Block driver for the COW format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
/**************************************************************/
|
||||
/* COW block driver using file system holes */
|
||||
|
||||
/* user mode linux compatible COW file */
|
||||
#define COW_MAGIC 0x4f4f4f4d /* MOOO */
|
||||
#define COW_VERSION 2
|
||||
|
||||
struct cow_header_v2 {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
char backing_file[1024];
|
||||
int32_t mtime;
|
||||
uint64_t size;
|
||||
uint32_t sectorsize;
|
||||
};
|
||||
|
||||
typedef struct BDRVCowState {
|
||||
int fd;
|
||||
uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */
|
||||
uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */
|
||||
int cow_bitmap_size;
|
||||
int64_t cow_sectors_offset;
|
||||
} BDRVCowState;
|
||||
|
||||
static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const struct cow_header_v2 *cow_header = (const void *)buf;
|
||||
|
||||
if (buf_size >= sizeof(struct cow_header_v2) &&
|
||||
be32_to_cpu(cow_header->magic) == COW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == COW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int fd;
|
||||
struct cow_header_v2 cow_header;
|
||||
int64_t size;
|
||||
|
||||
fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
s->fd = fd;
|
||||
/* see if it is a cow image */
|
||||
if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
|
||||
be32_to_cpu(cow_header.version) != COW_VERSION) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* cow image found */
|
||||
size = be64_to_cpu(cow_header.size);
|
||||
bs->total_sectors = size / 512;
|
||||
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||
cow_header.backing_file);
|
||||
|
||||
/* mmap the bitmap */
|
||||
s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
|
||||
s->cow_bitmap_addr = mmap(get_mmap_addr(s->cow_bitmap_size),
|
||||
s->cow_bitmap_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, s->fd, 0);
|
||||
if (s->cow_bitmap_addr == MAP_FAILED)
|
||||
goto fail;
|
||||
s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header);
|
||||
s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511;
|
||||
return 0;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum)
|
||||
{
|
||||
bitmap[bitnum / 8] |= (1 << (bitnum%8));
|
||||
}
|
||||
|
||||
static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum)
|
||||
{
|
||||
return !!(bitmap[bitnum / 8] & (1 << (bitnum%8)));
|
||||
}
|
||||
|
||||
|
||||
/* Return true if first block has been changed (ie. current version is
|
||||
* in COW file). Set the number of continuous blocks for which that
|
||||
* is true. */
|
||||
static inline int is_changed(uint8_t *bitmap,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int *num_same)
|
||||
{
|
||||
int changed;
|
||||
|
||||
if (!bitmap || nb_sectors == 0) {
|
||||
*num_same = nb_sectors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
changed = is_bit_set(bitmap, sector_num);
|
||||
for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
|
||||
if (is_bit_set(bitmap, sector_num + *num_same) != changed)
|
||||
break;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum);
|
||||
}
|
||||
|
||||
static int cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret, n;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) {
|
||||
lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
|
||||
ret = read(s->fd, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
} else {
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
} else {
|
||||
memset(buf, 0, n * 512);
|
||||
}
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int ret, i;
|
||||
|
||||
lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
|
||||
ret = write(s->fd, buf, nb_sectors * 512);
|
||||
if (ret != nb_sectors * 512)
|
||||
return -1;
|
||||
for (i = 0; i < nb_sectors; i++)
|
||||
cow_set_bit(s->cow_bitmap, sector_num + i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cow_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
munmap(s->cow_bitmap_addr, s->cow_bitmap_size);
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
static int cow_create(const char *filename, int64_t image_sectors,
|
||||
const char *image_filename, int flags)
|
||||
{
|
||||
int fd, cow_fd;
|
||||
struct cow_header_v2 cow_header;
|
||||
struct stat st;
|
||||
|
||||
if (flags)
|
||||
return -ENOTSUP;
|
||||
|
||||
cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (cow_fd < 0)
|
||||
return -1;
|
||||
memset(&cow_header, 0, sizeof(cow_header));
|
||||
cow_header.magic = cpu_to_be32(COW_MAGIC);
|
||||
cow_header.version = cpu_to_be32(COW_VERSION);
|
||||
if (image_filename) {
|
||||
/* Note: if no file, we put a dummy mtime */
|
||||
cow_header.mtime = cpu_to_be32(0);
|
||||
|
||||
fd = open(image_filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0) {
|
||||
close(cow_fd);
|
||||
goto mtime_fail;
|
||||
}
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
goto mtime_fail;
|
||||
}
|
||||
close(fd);
|
||||
cow_header.mtime = cpu_to_be32(st.st_mtime);
|
||||
mtime_fail:
|
||||
pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file),
|
||||
image_filename);
|
||||
}
|
||||
cow_header.sectorsize = cpu_to_be32(512);
|
||||
cow_header.size = cpu_to_be64(image_sectors * 512);
|
||||
write(cow_fd, &cow_header, sizeof(cow_header));
|
||||
/* resize to include at least all the bitmap */
|
||||
ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
|
||||
close(cow_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cow_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
fsync(s->fd);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_cow = {
|
||||
"cow",
|
||||
sizeof(BDRVCowState),
|
||||
cow_probe,
|
||||
cow_open,
|
||||
cow_read,
|
||||
cow_write,
|
||||
cow_close,
|
||||
cow_create,
|
||||
cow_flush,
|
||||
cow_is_allocated,
|
||||
};
|
||||
#endif
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* QEMU Block driver for DMG images
|
||||
*
|
||||
* Copyright (c) 2004 Johannes E. Schindelin
|
||||
*
|
||||
* 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 "vl.h"
|
||||
#include "block_int.h"
|
||||
#include "bswap.h"
|
||||
#include <zlib.h>
|
||||
|
||||
typedef struct BDRVDMGState {
|
||||
int fd;
|
||||
|
||||
/* each chunk contains a certain number of sectors,
|
||||
* offsets[i] is the offset in the .dmg file,
|
||||
* lengths[i] is the length of the compressed chunk,
|
||||
* sectors[i] is the sector beginning at offsets[i],
|
||||
* sectorcounts[i] is the number of sectors in that chunk,
|
||||
* the sectors array is ordered
|
||||
* 0<=i<n_chunks */
|
||||
|
||||
uint32_t n_chunks;
|
||||
uint32_t* types;
|
||||
uint64_t* offsets;
|
||||
uint64_t* lengths;
|
||||
uint64_t* sectors;
|
||||
uint64_t* sectorcounts;
|
||||
uint32_t current_chunk;
|
||||
uint8_t *compressed_chunk;
|
||||
uint8_t *uncompressed_chunk;
|
||||
z_stream zstream;
|
||||
} BDRVDMGState;
|
||||
|
||||
static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
int len=strlen(filename);
|
||||
if(len>4 && !strcmp(filename+len-4,".dmg"))
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static off_t read_off(int fd)
|
||||
{
|
||||
uint64_t buffer;
|
||||
if(read(fd,&buffer,8)<8)
|
||||
return 0;
|
||||
return be64_to_cpu(buffer);
|
||||
}
|
||||
|
||||
static off_t read_uint32(int fd)
|
||||
{
|
||||
uint32_t buffer;
|
||||
if(read(fd,&buffer,4)<4)
|
||||
return 0;
|
||||
return be32_to_cpu(buffer);
|
||||
}
|
||||
|
||||
static int dmg_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
off_t info_begin,info_end,last_in_offset,last_out_offset;
|
||||
uint32_t count;
|
||||
uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
|
||||
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (s->fd < 0)
|
||||
return -errno;
|
||||
bs->read_only = 1;
|
||||
s->n_chunks = 0;
|
||||
s->offsets = s->lengths = s->sectors = s->sectorcounts = 0;
|
||||
|
||||
/* read offset of info blocks */
|
||||
if(lseek(s->fd,-0x1d8,SEEK_END)<0) {
|
||||
dmg_close:
|
||||
close(s->fd);
|
||||
/* open raw instead */
|
||||
bs->drv=&bdrv_raw;
|
||||
return bs->drv->bdrv_open(bs, filename, flags);
|
||||
}
|
||||
info_begin=read_off(s->fd);
|
||||
if(info_begin==0)
|
||||
goto dmg_close;
|
||||
if(lseek(s->fd,info_begin,SEEK_SET)<0)
|
||||
goto dmg_close;
|
||||
if(read_uint32(s->fd)!=0x100)
|
||||
goto dmg_close;
|
||||
if((count = read_uint32(s->fd))==0)
|
||||
goto dmg_close;
|
||||
info_end = info_begin+count;
|
||||
if(lseek(s->fd,0xf8,SEEK_CUR)<0)
|
||||
goto dmg_close;
|
||||
|
||||
/* read offsets */
|
||||
last_in_offset = last_out_offset = 0;
|
||||
while(lseek(s->fd,0,SEEK_CUR)<info_end) {
|
||||
uint32_t type;
|
||||
|
||||
count = read_uint32(s->fd);
|
||||
if(count==0)
|
||||
goto dmg_close;
|
||||
type = read_uint32(s->fd);
|
||||
if(type!=0x6d697368 || count<244)
|
||||
lseek(s->fd,count-4,SEEK_CUR);
|
||||
else {
|
||||
int new_size, chunk_count;
|
||||
if(lseek(s->fd,200,SEEK_CUR)<0)
|
||||
goto dmg_close;
|
||||
chunk_count = (count-204)/40;
|
||||
new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
|
||||
s->types = realloc(s->types, new_size/2);
|
||||
s->offsets = realloc(s->offsets, new_size);
|
||||
s->lengths = realloc(s->lengths, new_size);
|
||||
s->sectors = realloc(s->sectors, new_size);
|
||||
s->sectorcounts = realloc(s->sectorcounts, new_size);
|
||||
|
||||
for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
|
||||
s->types[i] = read_uint32(s->fd);
|
||||
if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
|
||||
if(s->types[i]==0xffffffff) {
|
||||
last_in_offset = s->offsets[i-1]+s->lengths[i-1];
|
||||
last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];
|
||||
}
|
||||
chunk_count--;
|
||||
i--;
|
||||
if(lseek(s->fd,36,SEEK_CUR)<0)
|
||||
goto dmg_close;
|
||||
continue;
|
||||
}
|
||||
read_uint32(s->fd);
|
||||
s->sectors[i] = last_out_offset+read_off(s->fd);
|
||||
s->sectorcounts[i] = read_off(s->fd);
|
||||
s->offsets[i] = last_in_offset+read_off(s->fd);
|
||||
s->lengths[i] = read_off(s->fd);
|
||||
if(s->lengths[i]>max_compressed_size)
|
||||
max_compressed_size = s->lengths[i];
|
||||
if(s->sectorcounts[i]>max_sectors_per_chunk)
|
||||
max_sectors_per_chunk = s->sectorcounts[i];
|
||||
}
|
||||
s->n_chunks+=chunk_count;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
if(!(s->compressed_chunk = malloc(max_compressed_size+1)))
|
||||
goto dmg_close;
|
||||
if(!(s->uncompressed_chunk = malloc(512*max_sectors_per_chunk)))
|
||||
goto dmg_close;
|
||||
if(inflateInit(&s->zstream) != Z_OK)
|
||||
goto dmg_close;
|
||||
|
||||
s->current_chunk = s->n_chunks;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int is_sector_in_chunk(BDRVDMGState* s,
|
||||
uint32_t chunk_num,int sector_num)
|
||||
{
|
||||
if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num ||
|
||||
s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
|
||||
{
|
||||
/* binary search */
|
||||
uint32_t chunk1=0,chunk2=s->n_chunks,chunk3;
|
||||
while(chunk1!=chunk2) {
|
||||
chunk3 = (chunk1+chunk2)/2;
|
||||
if(s->sectors[chunk3]>sector_num)
|
||||
chunk2 = chunk3;
|
||||
else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num)
|
||||
return chunk3;
|
||||
else
|
||||
chunk1 = chunk3;
|
||||
}
|
||||
return s->n_chunks; /* error */
|
||||
}
|
||||
|
||||
static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num)
|
||||
{
|
||||
if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
|
||||
int ret;
|
||||
uint32_t chunk = search_chunk(s,sector_num);
|
||||
|
||||
if(chunk>=s->n_chunks)
|
||||
return -1;
|
||||
|
||||
s->current_chunk = s->n_chunks;
|
||||
switch(s->types[chunk]) {
|
||||
case 0x80000005: { /* zlib compressed */
|
||||
int i;
|
||||
|
||||
ret = lseek(s->fd, s->offsets[chunk], SEEK_SET);
|
||||
if(ret<0)
|
||||
return -1;
|
||||
|
||||
/* we need to buffer, because only the chunk as whole can be
|
||||
* inflated. */
|
||||
i=0;
|
||||
do {
|
||||
ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i);
|
||||
if(ret<0 && errno==EINTR)
|
||||
ret=0;
|
||||
i+=ret;
|
||||
} while(ret>=0 && ret+i<s->lengths[chunk]);
|
||||
|
||||
if (ret != s->lengths[chunk])
|
||||
return -1;
|
||||
|
||||
s->zstream.next_in = s->compressed_chunk;
|
||||
s->zstream.avail_in = s->lengths[chunk];
|
||||
s->zstream.next_out = s->uncompressed_chunk;
|
||||
s->zstream.avail_out = 512*s->sectorcounts[chunk];
|
||||
ret = inflateReset(&s->zstream);
|
||||
if(ret != Z_OK)
|
||||
return -1;
|
||||
ret = inflate(&s->zstream, Z_FINISH);
|
||||
if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk])
|
||||
return -1;
|
||||
break; }
|
||||
case 1: /* copy */
|
||||
ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]);
|
||||
if (ret != s->lengths[chunk])
|
||||
return -1;
|
||||
break;
|
||||
case 2: /* zero */
|
||||
memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]);
|
||||
break;
|
||||
}
|
||||
s->current_chunk = chunk;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmg_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for(i=0;i<nb_sectors;i++) {
|
||||
uint32_t sector_offset_in_chunk;
|
||||
if(dmg_read_chunk(s, sector_num+i) != 0)
|
||||
return -1;
|
||||
sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
|
||||
memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dmg_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
close(s->fd);
|
||||
if(s->n_chunks>0) {
|
||||
free(s->types);
|
||||
free(s->offsets);
|
||||
free(s->lengths);
|
||||
free(s->sectors);
|
||||
free(s->sectorcounts);
|
||||
}
|
||||
free(s->compressed_chunk);
|
||||
free(s->uncompressed_chunk);
|
||||
inflateEnd(&s->zstream);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_dmg = {
|
||||
"dmg",
|
||||
sizeof(BDRVDMGState),
|
||||
dmg_probe,
|
||||
dmg_open,
|
||||
dmg_read,
|
||||
NULL,
|
||||
dmg_close,
|
||||
};
|
||||
|
||||
+905
@@ -0,0 +1,905 @@
|
||||
/*
|
||||
* Block driver for the QCOW format
|
||||
*
|
||||
* Copyright (c) 2004-2006 Fabrice Bellard
|
||||
*
|
||||
* 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 "vl.h"
|
||||
#include "block_int.h"
|
||||
#include <zlib.h>
|
||||
#include "aes.h"
|
||||
|
||||
/**************************************************************/
|
||||
/* QEMU COW block driver with compression and encryption support */
|
||||
|
||||
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
|
||||
#define QCOW_VERSION 1
|
||||
|
||||
#define QCOW_CRYPT_NONE 0
|
||||
#define QCOW_CRYPT_AES 1
|
||||
|
||||
#define QCOW_OFLAG_COMPRESSED (1LL << 63)
|
||||
|
||||
typedef struct QCowHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint64_t backing_file_offset;
|
||||
uint32_t backing_file_size;
|
||||
uint32_t mtime;
|
||||
uint64_t size; /* in bytes */
|
||||
uint8_t cluster_bits;
|
||||
uint8_t l2_bits;
|
||||
uint32_t crypt_method;
|
||||
uint64_t l1_table_offset;
|
||||
} QCowHeader;
|
||||
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct BDRVQcowState {
|
||||
BlockDriverState *hd;
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
int cluster_sectors;
|
||||
int l2_bits;
|
||||
int l2_size;
|
||||
int l1_size;
|
||||
uint64_t cluster_offset_mask;
|
||||
uint64_t l1_table_offset;
|
||||
uint64_t *l1_table;
|
||||
uint64_t *l2_cache;
|
||||
uint64_t l2_cache_offsets[L2_CACHE_SIZE];
|
||||
uint32_t l2_cache_counts[L2_CACHE_SIZE];
|
||||
uint8_t *cluster_cache;
|
||||
uint8_t *cluster_data;
|
||||
uint64_t cluster_cache_offset;
|
||||
uint32_t crypt_method; /* current crypt method, 0 if no key yet */
|
||||
uint32_t crypt_method_header;
|
||||
AES_KEY aes_encrypt_key;
|
||||
AES_KEY aes_decrypt_key;
|
||||
} BDRVQcowState;
|
||||
|
||||
static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset);
|
||||
|
||||
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
const QCowHeader *cow_header = (const void *)buf;
|
||||
|
||||
if (buf_size >= sizeof(QCowHeader) &&
|
||||
be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
|
||||
be32_to_cpu(cow_header->version) == QCOW_VERSION)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int len, i, shift, ret;
|
||||
QCowHeader header;
|
||||
|
||||
ret = bdrv_file_open(&s->hd, filename, flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header))
|
||||
goto fail;
|
||||
be32_to_cpus(&header.magic);
|
||||
be32_to_cpus(&header.version);
|
||||
be64_to_cpus(&header.backing_file_offset);
|
||||
be32_to_cpus(&header.backing_file_size);
|
||||
be32_to_cpus(&header.mtime);
|
||||
be64_to_cpus(&header.size);
|
||||
be32_to_cpus(&header.crypt_method);
|
||||
be64_to_cpus(&header.l1_table_offset);
|
||||
|
||||
if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
|
||||
goto fail;
|
||||
if (header.size <= 1 || header.cluster_bits < 9)
|
||||
goto fail;
|
||||
if (header.crypt_method > QCOW_CRYPT_AES)
|
||||
goto fail;
|
||||
s->crypt_method_header = header.crypt_method;
|
||||
if (s->crypt_method_header)
|
||||
bs->encrypted = 1;
|
||||
s->cluster_bits = header.cluster_bits;
|
||||
s->cluster_size = 1 << s->cluster_bits;
|
||||
s->cluster_sectors = 1 << (s->cluster_bits - 9);
|
||||
s->l2_bits = header.l2_bits;
|
||||
s->l2_size = 1 << s->l2_bits;
|
||||
bs->total_sectors = header.size / 512;
|
||||
s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1;
|
||||
|
||||
/* read the level 1 table */
|
||||
shift = s->cluster_bits + s->l2_bits;
|
||||
s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
|
||||
|
||||
s->l1_table_offset = header.l1_table_offset;
|
||||
s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
|
||||
if (!s->l1_table)
|
||||
goto fail;
|
||||
if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
|
||||
s->l1_size * sizeof(uint64_t))
|
||||
goto fail;
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
/* alloc L2 cache */
|
||||
s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
if (!s->l2_cache)
|
||||
goto fail;
|
||||
s->cluster_cache = qemu_malloc(s->cluster_size);
|
||||
if (!s->cluster_cache)
|
||||
goto fail;
|
||||
s->cluster_data = qemu_malloc(s->cluster_size);
|
||||
if (!s->cluster_data)
|
||||
goto fail;
|
||||
s->cluster_cache_offset = -1;
|
||||
|
||||
/* read the backing file name */
|
||||
if (header.backing_file_offset != 0) {
|
||||
len = header.backing_file_size;
|
||||
if (len > 1023)
|
||||
len = 1023;
|
||||
if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len)
|
||||
goto fail;
|
||||
bs->backing_file[len] = '\0';
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
qemu_free(s->cluster_cache);
|
||||
qemu_free(s->cluster_data);
|
||||
bdrv_delete(s->hd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int qcow_set_key(BlockDriverState *bs, const char *key)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint8_t keybuf[16];
|
||||
int len, i;
|
||||
|
||||
memset(keybuf, 0, 16);
|
||||
len = strlen(key);
|
||||
if (len > 16)
|
||||
len = 16;
|
||||
/* XXX: we could compress the chars to 7 bits to increase
|
||||
entropy */
|
||||
for(i = 0;i < len;i++) {
|
||||
keybuf[i] = key[i];
|
||||
}
|
||||
s->crypt_method = s->crypt_method_header;
|
||||
|
||||
if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
|
||||
return -1;
|
||||
if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
|
||||
return -1;
|
||||
#if 0
|
||||
/* test */
|
||||
{
|
||||
uint8_t in[16];
|
||||
uint8_t out[16];
|
||||
uint8_t tmp[16];
|
||||
for(i=0;i<16;i++)
|
||||
in[i] = i;
|
||||
AES_encrypt(in, tmp, &s->aes_encrypt_key);
|
||||
AES_decrypt(tmp, out, &s->aes_decrypt_key);
|
||||
for(i = 0; i < 16; i++)
|
||||
printf(" %02x", tmp[i]);
|
||||
printf("\n");
|
||||
for(i = 0; i < 16; i++)
|
||||
printf(" %02x", out[i]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The crypt function is compatible with the linux cryptoloop
|
||||
algorithm for < 4 GB images. NOTE: out_buf == in_buf is
|
||||
supported */
|
||||
static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
uint8_t *out_buf, const uint8_t *in_buf,
|
||||
int nb_sectors, int enc,
|
||||
const AES_KEY *key)
|
||||
{
|
||||
union {
|
||||
uint64_t ll[2];
|
||||
uint8_t b[16];
|
||||
} ivec;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nb_sectors; i++) {
|
||||
ivec.ll[0] = cpu_to_le64(sector_num);
|
||||
ivec.ll[1] = 0;
|
||||
AES_cbc_encrypt(in_buf, out_buf, 512, key,
|
||||
ivec.b, enc);
|
||||
sector_num++;
|
||||
in_buf += 512;
|
||||
out_buf += 512;
|
||||
}
|
||||
}
|
||||
|
||||
/* 'allocate' is:
|
||||
*
|
||||
* 0 to not allocate.
|
||||
*
|
||||
* 1 to allocate a normal cluster (for sector indexes 'n_start' to
|
||||
* 'n_end')
|
||||
*
|
||||
* 2 to allocate a compressed cluster of size
|
||||
* 'compressed_size'. 'compressed_size' must be > 0 and <
|
||||
* cluster_size
|
||||
*
|
||||
* return 0 if not allocated.
|
||||
*/
|
||||
static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset, int allocate,
|
||||
int compressed_size,
|
||||
int n_start, int n_end)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int min_index, i, j, l1_index, l2_index;
|
||||
uint64_t l2_offset, *l2_table, cluster_offset, tmp;
|
||||
uint32_t min_count;
|
||||
int new_l2_table;
|
||||
|
||||
l1_index = offset >> (s->l2_bits + s->cluster_bits);
|
||||
l2_offset = s->l1_table[l1_index];
|
||||
new_l2_table = 0;
|
||||
if (!l2_offset) {
|
||||
if (!allocate)
|
||||
return 0;
|
||||
/* allocate a new l2 entry */
|
||||
l2_offset = bdrv_getlength(s->hd);
|
||||
/* round to cluster size */
|
||||
l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1);
|
||||
/* update the L1 entry */
|
||||
s->l1_table[l1_index] = l2_offset;
|
||||
tmp = cpu_to_be64(l2_offset);
|
||||
if (bdrv_pwrite(s->hd, s->l1_table_offset + l1_index * sizeof(tmp),
|
||||
&tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
new_l2_table = 1;
|
||||
}
|
||||
for(i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (l2_offset == s->l2_cache_offsets[i]) {
|
||||
/* increment the hit count */
|
||||
if (++s->l2_cache_counts[i] == 0xffffffff) {
|
||||
for(j = 0; j < L2_CACHE_SIZE; j++) {
|
||||
s->l2_cache_counts[j] >>= 1;
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (i << s->l2_bits);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
/* not found: load a new entry in the least used one */
|
||||
min_index = 0;
|
||||
min_count = 0xffffffff;
|
||||
for(i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (s->l2_cache_counts[i] < min_count) {
|
||||
min_count = s->l2_cache_counts[i];
|
||||
min_index = i;
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (min_index << s->l2_bits);
|
||||
if (new_l2_table) {
|
||||
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
|
||||
if (bdrv_pwrite(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return 0;
|
||||
} else {
|
||||
if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return 0;
|
||||
}
|
||||
s->l2_cache_offsets[min_index] = l2_offset;
|
||||
s->l2_cache_counts[min_index] = 1;
|
||||
found:
|
||||
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||
cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
if (!cluster_offset ||
|
||||
((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) {
|
||||
if (!allocate)
|
||||
return 0;
|
||||
/* allocate a new cluster */
|
||||
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
|
||||
(n_end - n_start) < s->cluster_sectors) {
|
||||
/* if the cluster is already compressed, we must
|
||||
decompress it in the case it is not completely
|
||||
overwritten */
|
||||
if (decompress_cluster(s, cluster_offset) < 0)
|
||||
return 0;
|
||||
cluster_offset = bdrv_getlength(s->hd);
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
~(s->cluster_size - 1);
|
||||
/* write the cluster content */
|
||||
if (bdrv_pwrite(s->hd, cluster_offset, s->cluster_cache, s->cluster_size) !=
|
||||
s->cluster_size)
|
||||
return -1;
|
||||
} else {
|
||||
cluster_offset = bdrv_getlength(s->hd);
|
||||
if (allocate == 1) {
|
||||
/* round to cluster size */
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
~(s->cluster_size - 1);
|
||||
bdrv_truncate(s->hd, cluster_offset + s->cluster_size);
|
||||
/* if encrypted, we must initialize the cluster
|
||||
content which won't be written */
|
||||
if (s->crypt_method &&
|
||||
(n_end - n_start) < s->cluster_sectors) {
|
||||
uint64_t start_sect;
|
||||
start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
|
||||
memset(s->cluster_data + 512, 0x00, 512);
|
||||
for(i = 0; i < s->cluster_sectors; i++) {
|
||||
if (i < n_start || i >= n_end) {
|
||||
encrypt_sectors(s, start_sect + i,
|
||||
s->cluster_data,
|
||||
s->cluster_data + 512, 1, 1,
|
||||
&s->aes_encrypt_key);
|
||||
if (bdrv_pwrite(s->hd, cluster_offset + i * 512,
|
||||
s->cluster_data, 512) != 512)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cluster_offset |= QCOW_OFLAG_COMPRESSED |
|
||||
(uint64_t)compressed_size << (63 - s->cluster_bits);
|
||||
}
|
||||
}
|
||||
/* update L2 table */
|
||||
tmp = cpu_to_be64(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
if (bdrv_pwrite(s->hd,
|
||||
l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
}
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
*pnum = n;
|
||||
return (cluster_offset != 0);
|
||||
}
|
||||
|
||||
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
|
||||
const uint8_t *buf, int buf_size)
|
||||
{
|
||||
z_stream strm1, *strm = &strm1;
|
||||
int ret, out_len;
|
||||
|
||||
memset(strm, 0, sizeof(*strm));
|
||||
|
||||
strm->next_in = (uint8_t *)buf;
|
||||
strm->avail_in = buf_size;
|
||||
strm->next_out = out_buf;
|
||||
strm->avail_out = out_buf_size;
|
||||
|
||||
ret = inflateInit2(strm, -12);
|
||||
if (ret != Z_OK)
|
||||
return -1;
|
||||
ret = inflate(strm, Z_FINISH);
|
||||
out_len = strm->next_out - out_buf;
|
||||
if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
|
||||
out_len != out_buf_size) {
|
||||
inflateEnd(strm);
|
||||
return -1;
|
||||
}
|
||||
inflateEnd(strm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
|
||||
{
|
||||
int ret, csize;
|
||||
uint64_t coffset;
|
||||
|
||||
coffset = cluster_offset & s->cluster_offset_mask;
|
||||
if (s->cluster_cache_offset != coffset) {
|
||||
csize = cluster_offset >> (63 - s->cluster_bits);
|
||||
csize &= (s->cluster_size - 1);
|
||||
ret = bdrv_pread(s->hd, coffset, s->cluster_data, csize);
|
||||
if (ret != csize)
|
||||
return -1;
|
||||
if (decompress_buffer(s->cluster_cache, s->cluster_size,
|
||||
s->cluster_data, csize) < 0) {
|
||||
return -1;
|
||||
}
|
||||
s->cluster_cache_offset = coffset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static int qcow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret, index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
if (!cluster_offset) {
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
} else {
|
||||
memset(buf, 0, 512 * n);
|
||||
}
|
||||
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
if (decompress_cluster(s, cluster_offset) < 0)
|
||||
return -1;
|
||||
memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
|
||||
} else {
|
||||
ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
if (s->crypt_method) {
|
||||
encrypt_sectors(s, sector_num, buf, buf, n, 0,
|
||||
&s->aes_decrypt_key);
|
||||
}
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int qcow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret, index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0,
|
||||
index_in_cluster,
|
||||
index_in_cluster + n);
|
||||
if (!cluster_offset)
|
||||
return -1;
|
||||
if (s->crypt_method) {
|
||||
encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
|
||||
&s->aes_encrypt_key);
|
||||
ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512,
|
||||
s->cluster_data, n * 512);
|
||||
} else {
|
||||
ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
|
||||
}
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct QCowAIOCB {
|
||||
BlockDriverAIOCB common;
|
||||
int64_t sector_num;
|
||||
uint8_t *buf;
|
||||
int nb_sectors;
|
||||
int n;
|
||||
uint64_t cluster_offset;
|
||||
uint8_t *cluster_data;
|
||||
BlockDriverAIOCB *hd_aiocb;
|
||||
} QCowAIOCB;
|
||||
|
||||
static void qcow_aio_read_cb(void *opaque, int ret)
|
||||
{
|
||||
QCowAIOCB *acb = opaque;
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster;
|
||||
|
||||
acb->hd_aiocb = NULL;
|
||||
if (ret < 0) {
|
||||
fail:
|
||||
acb->common.cb(acb->common.opaque, ret);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
redo:
|
||||
/* post process the read buffer */
|
||||
if (!acb->cluster_offset) {
|
||||
/* nothing to do */
|
||||
} else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* nothing to do */
|
||||
} else {
|
||||
if (s->crypt_method) {
|
||||
encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf,
|
||||
acb->n, 0,
|
||||
&s->aes_decrypt_key);
|
||||
}
|
||||
}
|
||||
|
||||
acb->nb_sectors -= acb->n;
|
||||
acb->sector_num += acb->n;
|
||||
acb->buf += acb->n * 512;
|
||||
|
||||
if (acb->nb_sectors == 0) {
|
||||
/* request completed */
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* prepare next AIO request */
|
||||
acb->cluster_offset = get_cluster_offset(bs, acb->sector_num << 9,
|
||||
0, 0, 0, 0);
|
||||
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
|
||||
acb->n = s->cluster_sectors - index_in_cluster;
|
||||
if (acb->n > acb->nb_sectors)
|
||||
acb->n = acb->nb_sectors;
|
||||
|
||||
if (!acb->cluster_offset) {
|
||||
if (bs->backing_hd) {
|
||||
/* read from the base image */
|
||||
acb->hd_aiocb = bdrv_aio_read(bs->backing_hd,
|
||||
acb->sector_num, acb->buf, acb->n, qcow_aio_read_cb, acb);
|
||||
if (acb->hd_aiocb == NULL)
|
||||
goto fail;
|
||||
} else {
|
||||
/* Note: in this case, no need to wait */
|
||||
memset(acb->buf, 0, 512 * acb->n);
|
||||
goto redo;
|
||||
}
|
||||
} else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* add AIO support for compressed blocks ? */
|
||||
if (decompress_cluster(s, acb->cluster_offset) < 0)
|
||||
goto fail;
|
||||
memcpy(acb->buf,
|
||||
s->cluster_cache + index_in_cluster * 512, 512 * acb->n);
|
||||
goto redo;
|
||||
} else {
|
||||
if ((acb->cluster_offset & 511) != 0) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
acb->hd_aiocb = bdrv_aio_read(s->hd,
|
||||
(acb->cluster_offset >> 9) + index_in_cluster,
|
||||
acb->buf, acb->n, qcow_aio_read_cb, acb);
|
||||
if (acb->hd_aiocb == NULL)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qcow_aio_read(BlockDriverState *bs,
|
||||
int64_t sector_num, uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
QCowAIOCB *acb;
|
||||
|
||||
acb = qemu_aio_get(bs, cb, opaque);
|
||||
if (!acb)
|
||||
return NULL;
|
||||
acb->hd_aiocb = NULL;
|
||||
acb->sector_num = sector_num;
|
||||
acb->buf = buf;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->n = 0;
|
||||
acb->cluster_offset = 0;
|
||||
|
||||
qcow_aio_read_cb(acb, 0);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static void qcow_aio_write_cb(void *opaque, int ret)
|
||||
{
|
||||
QCowAIOCB *acb = opaque;
|
||||
BlockDriverState *bs = acb->common.bs;
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster;
|
||||
uint64_t cluster_offset;
|
||||
const uint8_t *src_buf;
|
||||
|
||||
acb->hd_aiocb = NULL;
|
||||
|
||||
if (ret < 0) {
|
||||
fail:
|
||||
acb->common.cb(acb->common.opaque, ret);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
acb->nb_sectors -= acb->n;
|
||||
acb->sector_num += acb->n;
|
||||
acb->buf += acb->n * 512;
|
||||
|
||||
if (acb->nb_sectors == 0) {
|
||||
/* request completed */
|
||||
acb->common.cb(acb->common.opaque, 0);
|
||||
qemu_aio_release(acb);
|
||||
return;
|
||||
}
|
||||
|
||||
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
|
||||
acb->n = s->cluster_sectors - index_in_cluster;
|
||||
if (acb->n > acb->nb_sectors)
|
||||
acb->n = acb->nb_sectors;
|
||||
cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, 1, 0,
|
||||
index_in_cluster,
|
||||
index_in_cluster + acb->n);
|
||||
if (!cluster_offset || (cluster_offset & 511) != 0) {
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
if (s->crypt_method) {
|
||||
if (!acb->cluster_data) {
|
||||
acb->cluster_data = qemu_mallocz(s->cluster_size);
|
||||
if (!acb->cluster_data) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf,
|
||||
acb->n, 1, &s->aes_encrypt_key);
|
||||
src_buf = acb->cluster_data;
|
||||
} else {
|
||||
src_buf = acb->buf;
|
||||
}
|
||||
acb->hd_aiocb = bdrv_aio_write(s->hd,
|
||||
(cluster_offset >> 9) + index_in_cluster,
|
||||
src_buf, acb->n,
|
||||
qcow_aio_write_cb, acb);
|
||||
if (acb->hd_aiocb == NULL)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *qcow_aio_write(BlockDriverState *bs,
|
||||
int64_t sector_num, const uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowAIOCB *acb;
|
||||
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
|
||||
acb = qemu_aio_get(bs, cb, opaque);
|
||||
if (!acb)
|
||||
return NULL;
|
||||
acb->hd_aiocb = NULL;
|
||||
acb->sector_num = sector_num;
|
||||
acb->buf = (uint8_t *)buf;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->n = 0;
|
||||
|
||||
qcow_aio_write_cb(acb, 0);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static void qcow_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
QCowAIOCB *acb = (QCowAIOCB *)blockacb;
|
||||
if (acb->hd_aiocb)
|
||||
bdrv_aio_cancel(acb->hd_aiocb);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static void qcow_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
qemu_free(s->cluster_cache);
|
||||
qemu_free(s->cluster_data);
|
||||
bdrv_delete(s->hd);
|
||||
}
|
||||
|
||||
static int qcow_create(const char *filename, int64_t total_size,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
int fd, header_size, backing_filename_len, l1_size, i, shift;
|
||||
QCowHeader header;
|
||||
uint64_t tmp;
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.magic = cpu_to_be32(QCOW_MAGIC);
|
||||
header.version = cpu_to_be32(QCOW_VERSION);
|
||||
header.size = cpu_to_be64(total_size * 512);
|
||||
header_size = sizeof(header);
|
||||
backing_filename_len = 0;
|
||||
if (backing_file) {
|
||||
header.backing_file_offset = cpu_to_be64(header_size);
|
||||
backing_filename_len = strlen(backing_file);
|
||||
header.backing_file_size = cpu_to_be32(backing_filename_len);
|
||||
header_size += backing_filename_len;
|
||||
header.mtime = cpu_to_be32(0);
|
||||
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
||||
unmodifyed sectors */
|
||||
header.l2_bits = 12; /* 32 KB L2 tables */
|
||||
} else {
|
||||
header.cluster_bits = 12; /* 4 KB clusters */
|
||||
header.l2_bits = 9; /* 4 KB L2 tables */
|
||||
}
|
||||
header_size = (header_size + 7) & ~7;
|
||||
shift = header.cluster_bits + header.l2_bits;
|
||||
l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift;
|
||||
|
||||
header.l1_table_offset = cpu_to_be64(header_size);
|
||||
if (flags) {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
|
||||
} else {
|
||||
header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
|
||||
}
|
||||
|
||||
/* write all the data */
|
||||
write(fd, &header, sizeof(header));
|
||||
if (backing_file) {
|
||||
write(fd, backing_file, backing_filename_len);
|
||||
}
|
||||
lseek(fd, header_size, SEEK_SET);
|
||||
tmp = 0;
|
||||
for(i = 0;i < l1_size; i++) {
|
||||
write(fd, &tmp, sizeof(tmp));
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_make_empty(BlockDriverState *bs)
|
||||
{
|
||||
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(s->hd, s->l1_table_offset, s->l1_table, l1_length) < 0)
|
||||
return -1;
|
||||
ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
|
||||
memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: put compressed sectors first, then all the cluster aligned
|
||||
tables to avoid losing bytes in alignment */
|
||||
static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
z_stream strm;
|
||||
int ret, out_len;
|
||||
uint8_t *out_buf;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
if (nb_sectors != s->cluster_sectors)
|
||||
return -EINVAL;
|
||||
|
||||
out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
|
||||
if (!out_buf)
|
||||
return -1;
|
||||
|
||||
/* best compression, small window, no zlib header */
|
||||
memset(&strm, 0, sizeof(strm));
|
||||
ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
|
||||
Z_DEFLATED, -12,
|
||||
9, Z_DEFAULT_STRATEGY);
|
||||
if (ret != 0) {
|
||||
qemu_free(out_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strm.avail_in = s->cluster_size;
|
||||
strm.next_in = (uint8_t *)buf;
|
||||
strm.avail_out = s->cluster_size;
|
||||
strm.next_out = out_buf;
|
||||
|
||||
ret = deflate(&strm, Z_FINISH);
|
||||
if (ret != Z_STREAM_END && ret != Z_OK) {
|
||||
qemu_free(out_buf);
|
||||
deflateEnd(&strm);
|
||||
return -1;
|
||||
}
|
||||
out_len = strm.next_out - out_buf;
|
||||
|
||||
deflateEnd(&strm);
|
||||
|
||||
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
||||
/* could not compress: write normal cluster */
|
||||
qcow_write(bs, sector_num, buf, s->cluster_sectors);
|
||||
} else {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
|
||||
out_len, 0, 0);
|
||||
cluster_offset &= s->cluster_offset_mask;
|
||||
if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) {
|
||||
qemu_free(out_buf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_free(out_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcow_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
bdrv_flush(s->hd);
|
||||
}
|
||||
|
||||
static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
bdi->cluster_size = s->cluster_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BlockDriver bdrv_qcow = {
|
||||
"qcow",
|
||||
sizeof(BDRVQcowState),
|
||||
qcow_probe,
|
||||
qcow_open,
|
||||
NULL,
|
||||
NULL,
|
||||
qcow_close,
|
||||
qcow_create,
|
||||
qcow_flush,
|
||||
qcow_is_allocated,
|
||||
qcow_set_key,
|
||||
qcow_make_empty,
|
||||
|
||||
.bdrv_aio_read = qcow_aio_read,
|
||||
.bdrv_aio_write = qcow_aio_write,
|
||||
.bdrv_aio_cancel = qcow_aio_cancel,
|
||||
.aiocb_size = sizeof(QCowAIOCB),
|
||||
.bdrv_write_compressed = qcow_write_compressed,
|
||||
.bdrv_get_info = qcow_get_info,
|
||||
};
|
||||
+2246
File diff suppressed because it is too large
Load Diff
+1352
File diff suppressed because it is too large
Load Diff
+754
@@ -0,0 +1,754 @@
|
||||
/*
|
||||
* Block driver for the VMDK format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
* Copyright (c) 2005 Filip Navara
|
||||
*
|
||||
* 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 "vl.h"
|
||||
#include "block_int.h"
|
||||
|
||||
#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
|
||||
#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
uint32_t disk_sectors;
|
||||
uint32_t granularity;
|
||||
uint32_t l1dir_offset;
|
||||
uint32_t l1dir_size;
|
||||
uint32_t file_sectors;
|
||||
uint32_t cylinders;
|
||||
uint32_t heads;
|
||||
uint32_t sectors_per_track;
|
||||
} VMDK3Header;
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
int64_t capacity;
|
||||
int64_t granularity;
|
||||
int64_t desc_offset;
|
||||
int64_t desc_size;
|
||||
int32_t num_gtes_per_gte;
|
||||
int64_t rgd_offset;
|
||||
int64_t gd_offset;
|
||||
int64_t grain_offset;
|
||||
char filler[1];
|
||||
char check_bytes[4];
|
||||
} __attribute__((packed)) VMDK4Header;
|
||||
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct BDRVVmdkState {
|
||||
BlockDriverState *hd;
|
||||
int64_t l1_table_offset;
|
||||
int64_t l1_backup_table_offset;
|
||||
uint32_t *l1_table;
|
||||
uint32_t *l1_backup_table;
|
||||
unsigned int l1_size;
|
||||
uint32_t l1_entry_sectors;
|
||||
|
||||
unsigned int l2_size;
|
||||
uint32_t *l2_cache;
|
||||
uint32_t l2_cache_offsets[L2_CACHE_SIZE];
|
||||
uint32_t l2_cache_counts[L2_CACHE_SIZE];
|
||||
|
||||
unsigned int cluster_sectors;
|
||||
uint32_t parent_cid;
|
||||
} BDRVVmdkState;
|
||||
|
||||
static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
uint32_t magic;
|
||||
|
||||
if (buf_size < 4)
|
||||
return 0;
|
||||
magic = be32_to_cpu(*(uint32_t *)buf);
|
||||
if (magic == VMDK3_MAGIC ||
|
||||
magic == VMDK4_MAGIC)
|
||||
return 100;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CHECK_CID 1
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each
|
||||
#define HEADER_SIZE 512 // first sector of 512 bytes
|
||||
|
||||
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
char desc[DESC_SIZE];
|
||||
uint32_t cid;
|
||||
char *p_name, *cid_str;
|
||||
size_t cid_str_size;
|
||||
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
|
||||
return 0;
|
||||
|
||||
if (parent) {
|
||||
cid_str = "parentCID";
|
||||
cid_str_size = sizeof("parentCID");
|
||||
} else {
|
||||
cid_str = "CID";
|
||||
cid_str_size = sizeof("CID");
|
||||
}
|
||||
|
||||
if ((p_name = strstr(desc,cid_str)) != 0) {
|
||||
p_name += cid_str_size;
|
||||
sscanf(p_name,"%x",&cid);
|
||||
}
|
||||
|
||||
return cid;
|
||||
}
|
||||
|
||||
static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
|
||||
char *p_name, *tmp_str;
|
||||
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
|
||||
return -1;
|
||||
|
||||
tmp_str = strstr(desc,"parentCID");
|
||||
strcpy(tmp_desc, tmp_str);
|
||||
if ((p_name = strstr(desc,"CID")) != 0) {
|
||||
p_name += sizeof("CID");
|
||||
sprintf(p_name,"%x\n",cid);
|
||||
strcat(desc,tmp_desc);
|
||||
}
|
||||
|
||||
if (bdrv_pwrite(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||
{
|
||||
#ifdef CHECK_CID
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
BlockDriverState *p_bs = s->hd->backing_hd;
|
||||
uint32_t cur_pcid;
|
||||
|
||||
if (p_bs) {
|
||||
cur_pcid = vmdk_read_cid(p_bs,0);
|
||||
if (s->parent_cid != cur_pcid)
|
||||
// CID not valid
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
// CID valid
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int vmdk_snapshot_create(const char *filename, const char *backing_file)
|
||||
{
|
||||
int snp_fd, p_fd;
|
||||
uint32_t p_cid;
|
||||
char *p_name, *gd_buf, *rgd_buf;
|
||||
const char *real_filename, *temp_str;
|
||||
VMDK4Header header;
|
||||
uint32_t gde_entries, gd_size;
|
||||
int64_t gd_offset, rgd_offset, capacity, gt_size;
|
||||
char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE];
|
||||
char *desc_template =
|
||||
"# Disk DescriptorFile\n"
|
||||
"version=1\n"
|
||||
"CID=%x\n"
|
||||
"parentCID=%x\n"
|
||||
"createType=\"monolithicSparse\"\n"
|
||||
"parentFileNameHint=\"%s\"\n"
|
||||
"\n"
|
||||
"# Extent description\n"
|
||||
"RW %lu SPARSE \"%s\"\n"
|
||||
"\n"
|
||||
"# The Disk Data Base \n"
|
||||
"#DDB\n"
|
||||
"\n";
|
||||
|
||||
snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644);
|
||||
if (snp_fd < 0)
|
||||
return -1;
|
||||
p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (p_fd < 0) {
|
||||
close(snp_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read the header */
|
||||
if (lseek(p_fd, 0x0, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE)
|
||||
goto fail;
|
||||
|
||||
/* write the header */
|
||||
if (lseek(snp_fd, 0x0, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (write(snp_fd, hdr, HEADER_SIZE) == -1)
|
||||
goto fail;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC
|
||||
|
||||
ftruncate(snp_fd, header.grain_offset << 9);
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (lseek(p_fd, 0x200, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE)
|
||||
goto fail;
|
||||
|
||||
if ((p_name = strstr(p_desc,"CID")) != 0) {
|
||||
p_name += sizeof("CID");
|
||||
sscanf(p_name,"%x",&p_cid);
|
||||
}
|
||||
|
||||
real_filename = filename;
|
||||
if ((temp_str = strrchr(real_filename, '\\')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
if ((temp_str = strrchr(real_filename, '/')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
if ((temp_str = strrchr(real_filename, ':')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
|
||||
sprintf(s_desc, desc_template, p_cid, p_cid, backing_file
|
||||
, (uint32_t)header.capacity, real_filename);
|
||||
|
||||
/* write the descriptor */
|
||||
if (lseek(snp_fd, 0x200, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (write(snp_fd, s_desc, strlen(s_desc)) == -1)
|
||||
goto fail;
|
||||
|
||||
gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table
|
||||
rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table
|
||||
capacity = header.capacity * SECTOR_SIZE; // Extent size
|
||||
/*
|
||||
* Each GDE span 32M disk, means:
|
||||
* 512 GTE per GT, each GTE points to grain
|
||||
*/
|
||||
gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE;
|
||||
if (!gt_size)
|
||||
goto fail;
|
||||
gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde
|
||||
gd_size = gde_entries * sizeof(uint32_t);
|
||||
|
||||
/* write RGD */
|
||||
rgd_buf = qemu_malloc(gd_size);
|
||||
if (!rgd_buf)
|
||||
goto fail;
|
||||
if (lseek(p_fd, rgd_offset, SEEK_SET) == -1)
|
||||
goto fail_rgd;
|
||||
if (read(p_fd, rgd_buf, gd_size) != gd_size)
|
||||
goto fail_rgd;
|
||||
if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1)
|
||||
goto fail_rgd;
|
||||
if (write(snp_fd, rgd_buf, gd_size) == -1)
|
||||
goto fail_rgd;
|
||||
qemu_free(rgd_buf);
|
||||
|
||||
/* write GD */
|
||||
gd_buf = qemu_malloc(gd_size);
|
||||
if (!gd_buf)
|
||||
goto fail_rgd;
|
||||
if (lseek(p_fd, gd_offset, SEEK_SET) == -1)
|
||||
goto fail_gd;
|
||||
if (read(p_fd, gd_buf, gd_size) != gd_size)
|
||||
goto fail_gd;
|
||||
if (lseek(snp_fd, gd_offset, SEEK_SET) == -1)
|
||||
goto fail_gd;
|
||||
if (write(snp_fd, gd_buf, gd_size) == -1)
|
||||
goto fail_gd;
|
||||
qemu_free(gd_buf);
|
||||
|
||||
close(p_fd);
|
||||
close(snp_fd);
|
||||
return 0;
|
||||
|
||||
fail_gd:
|
||||
qemu_free(gd_buf);
|
||||
fail_rgd:
|
||||
qemu_free(rgd_buf);
|
||||
fail:
|
||||
close(p_fd);
|
||||
close(snp_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void vmdk_parent_close(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->backing_hd)
|
||||
bdrv_close(bs->backing_hd);
|
||||
}
|
||||
|
||||
|
||||
static int vmdk_parent_open(BlockDriverState *bs, const char * filename)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
char *p_name;
|
||||
char desc[DESC_SIZE];
|
||||
char parent_img_name[1024];
|
||||
|
||||
/* the descriptor offset = 0x200 */
|
||||
if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
|
||||
return -1;
|
||||
|
||||
if ((p_name = strstr(desc,"parentFileNameHint")) != 0) {
|
||||
char *end_name;
|
||||
struct stat file_buf;
|
||||
|
||||
p_name += sizeof("parentFileNameHint") + 1;
|
||||
if ((end_name = strchr(p_name,'\"')) == 0)
|
||||
return -1;
|
||||
|
||||
strncpy(s->hd->backing_file, p_name, end_name - p_name);
|
||||
if (stat(s->hd->backing_file, &file_buf) != 0) {
|
||||
path_combine(parent_img_name, sizeof(parent_img_name),
|
||||
filename, s->hd->backing_file);
|
||||
} else {
|
||||
strcpy(parent_img_name, s->hd->backing_file);
|
||||
}
|
||||
|
||||
s->hd->backing_hd = bdrv_new("");
|
||||
if (!s->hd->backing_hd) {
|
||||
failure:
|
||||
bdrv_close(s->hd);
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_open(s->hd->backing_hd, parent_img_name, 0) < 0)
|
||||
goto failure;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
uint32_t magic;
|
||||
int l1_size, i, ret;
|
||||
|
||||
ret = bdrv_file_open(&s->hd, filename, flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic))
|
||||
goto fail;
|
||||
|
||||
magic = be32_to_cpu(magic);
|
||||
if (magic == VMDK3_MAGIC) {
|
||||
VMDK3Header header;
|
||||
|
||||
if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header))
|
||||
goto fail;
|
||||
s->cluster_sectors = le32_to_cpu(header.granularity);
|
||||
s->l2_size = 1 << 9;
|
||||
s->l1_size = 1 << 6;
|
||||
bs->total_sectors = le32_to_cpu(header.disk_sectors);
|
||||
s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
|
||||
s->l1_backup_table_offset = 0;
|
||||
s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
||||
} else if (magic == VMDK4_MAGIC) {
|
||||
VMDK4Header header;
|
||||
|
||||
if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header))
|
||||
goto fail;
|
||||
bs->total_sectors = le64_to_cpu(header.capacity);
|
||||
s->cluster_sectors = le64_to_cpu(header.granularity);
|
||||
s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
|
||||
s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
||||
if (s->l1_entry_sectors <= 0)
|
||||
goto fail;
|
||||
s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
|
||||
/ s->l1_entry_sectors;
|
||||
s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
|
||||
s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
|
||||
|
||||
// try to open parent images, if exist
|
||||
if (vmdk_parent_open(bs, filename) != 0)
|
||||
goto fail;
|
||||
// write the CID once after the image creation
|
||||
s->parent_cid = vmdk_read_cid(bs,1);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* read the L1 table */
|
||||
l1_size = s->l1_size * sizeof(uint32_t);
|
||||
s->l1_table = qemu_malloc(l1_size);
|
||||
if (!s->l1_table)
|
||||
goto fail;
|
||||
if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, l1_size) != l1_size)
|
||||
goto fail;
|
||||
for(i = 0; i < s->l1_size; i++) {
|
||||
le32_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
|
||||
if (s->l1_backup_table_offset) {
|
||||
s->l1_backup_table = qemu_malloc(l1_size);
|
||||
if (!s->l1_backup_table)
|
||||
goto fail;
|
||||
if (bdrv_pread(s->hd, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size)
|
||||
goto fail;
|
||||
for(i = 0; i < s->l1_size; i++) {
|
||||
le32_to_cpus(&s->l1_backup_table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
|
||||
if (!s->l2_cache)
|
||||
goto fail;
|
||||
return 0;
|
||||
fail:
|
||||
qemu_free(s->l1_backup_table);
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
bdrv_delete(s->hd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint64_t get_cluster_offset(BlockDriverState *bs, uint64_t offset, int allocate);
|
||||
|
||||
static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
|
||||
uint64_t offset, int allocate)
|
||||
{
|
||||
uint64_t parent_cluster_offset;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
uint8_t whole_grain[s->cluster_sectors*512]; // 128 sectors * 512 bytes each = grain size 64KB
|
||||
|
||||
// we will be here if it's first write on non-exist grain(cluster).
|
||||
// try to read from parent image, if exist
|
||||
if (s->hd->backing_hd) {
|
||||
BDRVVmdkState *ps = s->hd->backing_hd->opaque;
|
||||
|
||||
if (!vmdk_is_cid_valid(bs))
|
||||
return -1;
|
||||
parent_cluster_offset = get_cluster_offset(s->hd->backing_hd, offset, allocate);
|
||||
if (bdrv_pread(ps->hd, parent_cluster_offset, whole_grain, ps->cluster_sectors*512) !=
|
||||
ps->cluster_sectors*512)
|
||||
return -1;
|
||||
|
||||
if (bdrv_pwrite(s->hd, cluster_offset << 9, whole_grain, sizeof(whole_grain)) !=
|
||||
sizeof(whole_grain))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset, int allocate)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
unsigned int l1_index, l2_offset, l2_index;
|
||||
int min_index, i, j;
|
||||
uint32_t min_count, *l2_table, tmp;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
l1_index = (offset >> 9) / s->l1_entry_sectors;
|
||||
if (l1_index >= s->l1_size)
|
||||
return 0;
|
||||
l2_offset = s->l1_table[l1_index];
|
||||
if (!l2_offset)
|
||||
return 0;
|
||||
for(i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (l2_offset == s->l2_cache_offsets[i]) {
|
||||
/* increment the hit count */
|
||||
if (++s->l2_cache_counts[i] == 0xffffffff) {
|
||||
for(j = 0; j < L2_CACHE_SIZE; j++) {
|
||||
s->l2_cache_counts[j] >>= 1;
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (i * s->l2_size);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
/* not found: load a new entry in the least used one */
|
||||
min_index = 0;
|
||||
min_count = 0xffffffff;
|
||||
for(i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (s->l2_cache_counts[i] < min_count) {
|
||||
min_count = s->l2_cache_counts[i];
|
||||
min_index = i;
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (min_index * s->l2_size);
|
||||
if (bdrv_pread(s->hd, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) !=
|
||||
s->l2_size * sizeof(uint32_t))
|
||||
return 0;
|
||||
|
||||
s->l2_cache_offsets[min_index] = l2_offset;
|
||||
s->l2_cache_counts[min_index] = 1;
|
||||
found:
|
||||
l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
|
||||
cluster_offset = le32_to_cpu(l2_table[l2_index]);
|
||||
if (!cluster_offset) {
|
||||
struct stat file_buf;
|
||||
|
||||
if (!allocate)
|
||||
return 0;
|
||||
stat(s->hd->filename, &file_buf);
|
||||
cluster_offset = file_buf.st_size;
|
||||
bdrv_truncate(s->hd, cluster_offset + (s->cluster_sectors << 9));
|
||||
|
||||
cluster_offset >>= 9;
|
||||
/* update L2 table */
|
||||
tmp = cpu_to_le32(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
if (bdrv_pwrite(s->hd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)),
|
||||
&tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
/* update backup L2 table */
|
||||
if (s->l1_backup_table_offset != 0) {
|
||||
l2_offset = s->l1_backup_table[l1_index];
|
||||
if (bdrv_pwrite(s->hd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)),
|
||||
&tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1)
|
||||
return 0;
|
||||
}
|
||||
cluster_offset <<= 9;
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
|
||||
index_in_cluster = sector_num % s->cluster_sectors;
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
*pnum = n;
|
||||
return (cluster_offset != 0);
|
||||
}
|
||||
|
||||
static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int index_in_cluster, n, ret;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
|
||||
index_in_cluster = sector_num % s->cluster_sectors;
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
if (!cluster_offset) {
|
||||
// try to read from parent image, if exist
|
||||
if (s->hd->backing_hd) {
|
||||
if (!vmdk_is_cid_valid(bs))
|
||||
return -1;
|
||||
ret = bdrv_read(s->hd->backing_hd, sector_num, buf, n);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
} else {
|
||||
memset(buf, 0, 512 * n);
|
||||
}
|
||||
} else {
|
||||
if(bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
|
||||
return -1;
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
static int cid_update = 0;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 1);
|
||||
if (!cluster_offset)
|
||||
return -1;
|
||||
if (bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
|
||||
return -1;
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
|
||||
// update CID on the first write every time the virtual disk is opened
|
||||
if (!cid_update) {
|
||||
vmdk_write_cid(bs, time(NULL));
|
||||
cid_update++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_create(const char *filename, int64_t total_size,
|
||||
const char *backing_file, int flags)
|
||||
{
|
||||
int fd, i;
|
||||
VMDK4Header header;
|
||||
uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
|
||||
char *desc_template =
|
||||
"# Disk DescriptorFile\n"
|
||||
"version=1\n"
|
||||
"CID=%x\n"
|
||||
"parentCID=ffffffff\n"
|
||||
"createType=\"monolithicSparse\"\n"
|
||||
"\n"
|
||||
"# Extent description\n"
|
||||
"RW %lu SPARSE \"%s\"\n"
|
||||
"\n"
|
||||
"# The Disk Data Base \n"
|
||||
"#DDB\n"
|
||||
"\n"
|
||||
"ddb.virtualHWVersion = \"4\"\n"
|
||||
"ddb.geometry.cylinders = \"%lu\"\n"
|
||||
"ddb.geometry.heads = \"16\"\n"
|
||||
"ddb.geometry.sectors = \"63\"\n"
|
||||
"ddb.adapterType = \"ide\"\n";
|
||||
char desc[1024];
|
||||
const char *real_filename, *temp_str;
|
||||
|
||||
/* XXX: add support for backing file */
|
||||
if (backing_file) {
|
||||
return vmdk_snapshot_create(filename, backing_file);
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
magic = cpu_to_be32(VMDK4_MAGIC);
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.version = cpu_to_le32(1);
|
||||
header.flags = cpu_to_le32(3); /* ?? */
|
||||
header.capacity = cpu_to_le64(total_size);
|
||||
header.granularity = cpu_to_le64(128);
|
||||
header.num_gtes_per_gte = cpu_to_le32(512);
|
||||
|
||||
grains = (total_size + header.granularity - 1) / header.granularity;
|
||||
gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
|
||||
gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
|
||||
gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
|
||||
|
||||
header.desc_offset = 1;
|
||||
header.desc_size = 20;
|
||||
header.rgd_offset = header.desc_offset + header.desc_size;
|
||||
header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
|
||||
header.grain_offset =
|
||||
((header.gd_offset + gd_size + (gt_size * gt_count) +
|
||||
header.granularity - 1) / header.granularity) *
|
||||
header.granularity;
|
||||
|
||||
header.desc_offset = cpu_to_le64(header.desc_offset);
|
||||
header.desc_size = cpu_to_le64(header.desc_size);
|
||||
header.rgd_offset = cpu_to_le64(header.rgd_offset);
|
||||
header.gd_offset = cpu_to_le64(header.gd_offset);
|
||||
header.grain_offset = cpu_to_le64(header.grain_offset);
|
||||
|
||||
header.check_bytes[0] = 0xa;
|
||||
header.check_bytes[1] = 0x20;
|
||||
header.check_bytes[2] = 0xd;
|
||||
header.check_bytes[3] = 0xa;
|
||||
|
||||
/* write all the data */
|
||||
write(fd, &magic, sizeof(magic));
|
||||
write(fd, &header, sizeof(header));
|
||||
|
||||
ftruncate(fd, header.grain_offset << 9);
|
||||
|
||||
/* write grain directory */
|
||||
lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
|
||||
for (i = 0, tmp = header.rgd_offset + gd_size;
|
||||
i < gt_count; i++, tmp += gt_size)
|
||||
write(fd, &tmp, sizeof(tmp));
|
||||
|
||||
/* write backup grain directory */
|
||||
lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
|
||||
for (i = 0, tmp = header.gd_offset + gd_size;
|
||||
i < gt_count; i++, tmp += gt_size)
|
||||
write(fd, &tmp, sizeof(tmp));
|
||||
|
||||
/* compose the descriptor */
|
||||
real_filename = filename;
|
||||
if ((temp_str = strrchr(real_filename, '\\')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
if ((temp_str = strrchr(real_filename, '/')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
if ((temp_str = strrchr(real_filename, ':')) != NULL)
|
||||
real_filename = temp_str + 1;
|
||||
sprintf(desc, desc_template, time(NULL), (unsigned long)total_size,
|
||||
real_filename, total_size / (63 * 16));
|
||||
|
||||
/* write the descriptor */
|
||||
lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
|
||||
write(fd, desc, strlen(desc));
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmdk_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
bdrv_delete(s->hd);
|
||||
// try to close parent image, if exist
|
||||
vmdk_parent_close(s->hd);
|
||||
}
|
||||
|
||||
static void vmdk_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
bdrv_flush(s->hd);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_vmdk = {
|
||||
"vmdk",
|
||||
sizeof(BDRVVmdkState),
|
||||
vmdk_probe,
|
||||
vmdk_open,
|
||||
vmdk_read,
|
||||
vmdk_write,
|
||||
vmdk_close,
|
||||
vmdk_create,
|
||||
vmdk_flush,
|
||||
vmdk_is_allocated,
|
||||
};
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Block driver for Conectix/Microsoft Virtual PC images
|
||||
*
|
||||
* Copyright (c) 2005 Alex Beregszaszi
|
||||
*
|
||||
* 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 "vl.h"
|
||||
#include "block_int.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
#define HEADER_SIZE 512
|
||||
|
||||
//#define CACHE
|
||||
|
||||
// always big-endian
|
||||
struct vpc_subheader {
|
||||
char magic[8]; // "conectix" / "cxsparse"
|
||||
union {
|
||||
struct {
|
||||
uint32_t unk1[2];
|
||||
uint32_t unk2; // always zero?
|
||||
uint32_t subheader_offset;
|
||||
uint32_t unk3; // some size?
|
||||
char creator[4]; // "vpc "
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
char guest[4]; // "Wi2k"
|
||||
uint32_t unk4[7];
|
||||
uint8_t vnet_id[16]; // virtual network id, purpose unknown
|
||||
// next 16 longs are used, but dunno the purpose
|
||||
// next 6 longs unknown, following 7 long maybe a serial
|
||||
char padding[HEADER_SIZE - 84];
|
||||
} main;
|
||||
struct {
|
||||
uint32_t unk1[2]; // all bits set
|
||||
uint32_t unk2; // always zero?
|
||||
uint32_t pagetable_offset;
|
||||
uint32_t unk3;
|
||||
uint32_t pagetable_entries; // 32bit/entry
|
||||
uint32_t pageentry_size; // 512*8*512
|
||||
uint32_t nb_sectors;
|
||||
char padding[HEADER_SIZE - 40];
|
||||
} sparse;
|
||||
char padding[HEADER_SIZE - 8];
|
||||
} type;
|
||||
};
|
||||
|
||||
typedef struct BDRVVPCState {
|
||||
int fd;
|
||||
|
||||
int pagetable_entries;
|
||||
uint32_t *pagetable;
|
||||
|
||||
uint32_t pageentry_size;
|
||||
#ifdef CACHE
|
||||
uint8_t *pageentry_u8;
|
||||
uint32_t *pageentry_u32;
|
||||
uint16_t *pageentry_u16;
|
||||
|
||||
uint64_t last_bitmap;
|
||||
#endif
|
||||
} BDRVVPCState;
|
||||
|
||||
static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
if (buf_size >= 8 && !strncmp(buf, "conectix", 8))
|
||||
return 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
int fd, i;
|
||||
struct vpc_subheader header;
|
||||
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
bs->read_only = 1; // no write support yet
|
||||
|
||||
s->fd = fd;
|
||||
|
||||
if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
|
||||
goto fail;
|
||||
|
||||
if (strncmp(header.magic, "conectix", 8))
|
||||
goto fail;
|
||||
lseek(s->fd, be32_to_cpu(header.type.main.subheader_offset), SEEK_SET);
|
||||
|
||||
if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
|
||||
goto fail;
|
||||
|
||||
if (strncmp(header.magic, "cxsparse", 8))
|
||||
goto fail;
|
||||
|
||||
bs->total_sectors = ((uint64_t)be32_to_cpu(header.type.sparse.pagetable_entries) *
|
||||
be32_to_cpu(header.type.sparse.pageentry_size)) / 512;
|
||||
|
||||
lseek(s->fd, be32_to_cpu(header.type.sparse.pagetable_offset), SEEK_SET);
|
||||
|
||||
s->pagetable_entries = be32_to_cpu(header.type.sparse.pagetable_entries);
|
||||
s->pagetable = qemu_malloc(s->pagetable_entries * 4);
|
||||
if (!s->pagetable)
|
||||
goto fail;
|
||||
if (read(s->fd, s->pagetable, s->pagetable_entries * 4) !=
|
||||
s->pagetable_entries * 4)
|
||||
goto fail;
|
||||
for (i = 0; i < s->pagetable_entries; i++)
|
||||
be32_to_cpus(&s->pagetable[i]);
|
||||
|
||||
s->pageentry_size = be32_to_cpu(header.type.sparse.pageentry_size);
|
||||
#ifdef CACHE
|
||||
s->pageentry_u8 = qemu_malloc(512);
|
||||
if (!s->pageentry_u8)
|
||||
goto fail;
|
||||
s->pageentry_u32 = s->pageentry_u8;
|
||||
s->pageentry_u16 = s->pageentry_u8;
|
||||
s->last_pagetable = -1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
uint64_t offset = sector_num * 512;
|
||||
uint64_t bitmap_offset, block_offset;
|
||||
uint32_t pagetable_index, pageentry_index;
|
||||
|
||||
pagetable_index = offset / s->pageentry_size;
|
||||
pageentry_index = (offset % s->pageentry_size) / 512;
|
||||
|
||||
if (pagetable_index > s->pagetable_entries || s->pagetable[pagetable_index] == 0xffffffff)
|
||||
return -1; // not allocated
|
||||
|
||||
bitmap_offset = 512 * s->pagetable[pagetable_index];
|
||||
block_offset = bitmap_offset + 512 + (512 * pageentry_index);
|
||||
|
||||
// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
|
||||
// sector_num, pagetable_index, pageentry_index,
|
||||
// bitmap_offset, block_offset);
|
||||
|
||||
// disabled by reason
|
||||
#if 0
|
||||
#ifdef CACHE
|
||||
if (bitmap_offset != s->last_bitmap)
|
||||
{
|
||||
lseek(s->fd, bitmap_offset, SEEK_SET);
|
||||
|
||||
s->last_bitmap = bitmap_offset;
|
||||
|
||||
// Scary! Bitmap is stored as big endian 32bit entries,
|
||||
// while we used to look it up byte by byte
|
||||
read(s->fd, s->pageentry_u8, 512);
|
||||
for (i = 0; i < 128; i++)
|
||||
be32_to_cpus(&s->pageentry_u32[i]);
|
||||
}
|
||||
|
||||
if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
|
||||
return -1;
|
||||
#else
|
||||
lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
|
||||
|
||||
read(s->fd, &bitmap_entry, 1);
|
||||
|
||||
if ((bitmap_entry >> (pageentry_index % 8)) & 1)
|
||||
return -1; // not allocated
|
||||
#endif
|
||||
#endif
|
||||
lseek(s->fd, block_offset, SEEK_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpc_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
if (!seek_to_sector(bs, sector_num))
|
||||
{
|
||||
ret = read(s->fd, buf, 512);
|
||||
if (ret != 512)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
memset(buf, 0, 512);
|
||||
nb_sectors--;
|
||||
sector_num++;
|
||||
buf += 512;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpc_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVPCState *s = bs->opaque;
|
||||
qemu_free(s->pagetable);
|
||||
#ifdef CACHE
|
||||
qemu_free(s->pageentry_u8);
|
||||
#endif
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_vpc = {
|
||||
"vpc",
|
||||
sizeof(BDRVVPCState),
|
||||
vpc_probe,
|
||||
vpc_open,
|
||||
vpc_read,
|
||||
NULL,
|
||||
vpc_close,
|
||||
};
|
||||
+2804
File diff suppressed because it is too large
Load Diff
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* QEMU System Emulator block driver
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef BLOCK_INT_H
|
||||
#define BLOCK_INT_H
|
||||
|
||||
struct BlockDriver {
|
||||
const char *format_name;
|
||||
int instance_size;
|
||||
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
|
||||
int (*bdrv_open)(BlockDriverState *bs, const char *filename, int flags);
|
||||
int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors);
|
||||
int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
void (*bdrv_close)(BlockDriverState *bs);
|
||||
int (*bdrv_create)(const char *filename, int64_t total_sectors,
|
||||
const char *backing_file, int flags);
|
||||
void (*bdrv_flush)(BlockDriverState *bs);
|
||||
int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum);
|
||||
int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
|
||||
int (*bdrv_make_empty)(BlockDriverState *bs);
|
||||
/* aio */
|
||||
BlockDriverAIOCB *(*bdrv_aio_read)(BlockDriverState *bs,
|
||||
int64_t sector_num, uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
BlockDriverAIOCB *(*bdrv_aio_write)(BlockDriverState *bs,
|
||||
int64_t sector_num, const uint8_t *buf, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque);
|
||||
void (*bdrv_aio_cancel)(BlockDriverAIOCB *acb);
|
||||
int aiocb_size;
|
||||
|
||||
const char *protocol_name;
|
||||
int (*bdrv_pread)(BlockDriverState *bs, int64_t offset,
|
||||
uint8_t *buf, int count);
|
||||
int (*bdrv_pwrite)(BlockDriverState *bs, int64_t offset,
|
||||
const uint8_t *buf, int count);
|
||||
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
|
||||
int64_t (*bdrv_getlength)(BlockDriverState *bs);
|
||||
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
|
||||
int (*bdrv_snapshot_create)(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo *sn_info);
|
||||
int (*bdrv_snapshot_goto)(BlockDriverState *bs,
|
||||
const char *snapshot_id);
|
||||
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
|
||||
int (*bdrv_snapshot_list)(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_info);
|
||||
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
|
||||
/* removable device specific */
|
||||
int (*bdrv_is_inserted)(BlockDriverState *bs);
|
||||
int (*bdrv_media_changed)(BlockDriverState *bs);
|
||||
int (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
|
||||
int (*bdrv_set_locked)(BlockDriverState *bs, int locked);
|
||||
|
||||
BlockDriverAIOCB *free_aiocb;
|
||||
struct BlockDriver *next;
|
||||
};
|
||||
|
||||
struct BlockDriverState {
|
||||
int64_t total_sectors; /* if we are reading a disk image, give its
|
||||
size in sectors */
|
||||
int read_only; /* if true, the media is read only */
|
||||
int removable; /* if true, the media can be removed */
|
||||
int locked; /* if true, the media cannot temporarily be ejected */
|
||||
int encrypted; /* if true, the media is encrypted */
|
||||
/* event callback when inserting/removing */
|
||||
void (*change_cb)(void *opaque);
|
||||
void *change_opaque;
|
||||
|
||||
BlockDriver *drv; /* NULL means no media */
|
||||
void *opaque;
|
||||
|
||||
int boot_sector_enabled;
|
||||
uint8_t boot_sector_data[512];
|
||||
|
||||
char filename[1024];
|
||||
char backing_file[1024]; /* if non zero, the image is a diff of
|
||||
this file image */
|
||||
int is_temporary;
|
||||
int media_changed;
|
||||
|
||||
BlockDriverState *backing_hd;
|
||||
/* async read/write emulation */
|
||||
|
||||
void *sync_aiocb;
|
||||
|
||||
/* NOTE: the following infos are only hints for real hardware
|
||||
drivers. They are not used by the block driver */
|
||||
int cyls, heads, secs, translation;
|
||||
int type;
|
||||
char device_name[32];
|
||||
BlockDriverState *next;
|
||||
};
|
||||
|
||||
struct BlockDriverAIOCB {
|
||||
BlockDriverState *bs;
|
||||
BlockDriverCompletionFunc *cb;
|
||||
void *opaque;
|
||||
BlockDriverAIOCB *next;
|
||||
};
|
||||
|
||||
void get_tmp_filename(char *filename, int size);
|
||||
|
||||
void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb,
|
||||
void *opaque);
|
||||
void qemu_aio_release(void *p);
|
||||
|
||||
#endif /* BLOCK_INT_H */
|
||||
@@ -0,0 +1,202 @@
|
||||
#ifndef BSWAP_H
|
||||
#define BSWAP_H
|
||||
|
||||
#include "config-host.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
#include <byteswap.h>
|
||||
#else
|
||||
|
||||
#define bswap_16(x) \
|
||||
({ \
|
||||
uint16_t __x = (x); \
|
||||
((uint16_t)( \
|
||||
(((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \
|
||||
(((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \
|
||||
})
|
||||
|
||||
#define bswap_32(x) \
|
||||
({ \
|
||||
uint32_t __x = (x); \
|
||||
((uint32_t)( \
|
||||
(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
|
||||
})
|
||||
|
||||
#define bswap_64(x) \
|
||||
({ \
|
||||
uint64_t __x = (x); \
|
||||
((uint64_t)( \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
|
||||
})
|
||||
|
||||
#endif /* !HAVE_BYTESWAP_H */
|
||||
|
||||
static inline uint16_t bswap16(uint16_t x)
|
||||
{
|
||||
return bswap_16(x);
|
||||
}
|
||||
|
||||
static inline uint32_t bswap32(uint32_t x)
|
||||
{
|
||||
return bswap_32(x);
|
||||
}
|
||||
|
||||
static inline uint64_t bswap64(uint64_t x)
|
||||
{
|
||||
return bswap_64(x);
|
||||
}
|
||||
|
||||
static inline void bswap16s(uint16_t *s)
|
||||
{
|
||||
*s = bswap16(*s);
|
||||
}
|
||||
|
||||
static inline void bswap32s(uint32_t *s)
|
||||
{
|
||||
*s = bswap32(*s);
|
||||
}
|
||||
|
||||
static inline void bswap64s(uint64_t *s)
|
||||
{
|
||||
*s = bswap64(*s);
|
||||
}
|
||||
|
||||
#if defined(WORDS_BIGENDIAN)
|
||||
#define be_bswap(v, size) (v)
|
||||
#define le_bswap(v, size) bswap ## size(v)
|
||||
#define be_bswaps(v, size)
|
||||
#define le_bswaps(p, size) *p = bswap ## size(*p);
|
||||
#else
|
||||
#define le_bswap(v, size) (v)
|
||||
#define be_bswap(v, size) bswap ## size(v)
|
||||
#define le_bswaps(v, size)
|
||||
#define be_bswaps(p, size) *p = bswap ## size(*p);
|
||||
#endif
|
||||
|
||||
#define CPU_CONVERT(endian, size, type)\
|
||||
static inline type endian ## size ## _to_cpu(type v)\
|
||||
{\
|
||||
return endian ## _bswap(v, size);\
|
||||
}\
|
||||
\
|
||||
static inline type cpu_to_ ## endian ## size(type v)\
|
||||
{\
|
||||
return endian ## _bswap(v, size);\
|
||||
}\
|
||||
\
|
||||
static inline void endian ## size ## _to_cpus(type *p)\
|
||||
{\
|
||||
endian ## _bswaps(p, size)\
|
||||
}\
|
||||
\
|
||||
static inline void cpu_to_ ## endian ## size ## s(type *p)\
|
||||
{\
|
||||
endian ## _bswaps(p, size)\
|
||||
}\
|
||||
\
|
||||
static inline type endian ## size ## _to_cpup(const type *p)\
|
||||
{\
|
||||
return endian ## size ## _to_cpu(*p);\
|
||||
}\
|
||||
\
|
||||
static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\
|
||||
{\
|
||||
*p = cpu_to_ ## endian ## size(v);\
|
||||
}
|
||||
|
||||
CPU_CONVERT(be, 16, uint16_t)
|
||||
CPU_CONVERT(be, 32, uint32_t)
|
||||
CPU_CONVERT(be, 64, uint64_t)
|
||||
|
||||
CPU_CONVERT(le, 16, uint16_t)
|
||||
CPU_CONVERT(le, 32, uint32_t)
|
||||
CPU_CONVERT(le, 64, uint64_t)
|
||||
|
||||
/* unaligned versions (optimized for frequent unaligned accesses)*/
|
||||
|
||||
#if defined(__i386__) || defined(__powerpc__)
|
||||
|
||||
#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v)
|
||||
#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v)
|
||||
#define le16_to_cpupu(p) le16_to_cpup(p)
|
||||
#define le32_to_cpupu(p) le32_to_cpup(p)
|
||||
|
||||
#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v)
|
||||
#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v)
|
||||
|
||||
#else
|
||||
|
||||
static inline void cpu_to_le16wu(uint16_t *p, uint16_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v;
|
||||
p1[1] = v >> 8;
|
||||
}
|
||||
|
||||
static inline void cpu_to_le32wu(uint32_t *p, uint32_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v;
|
||||
p1[1] = v >> 8;
|
||||
p1[2] = v >> 16;
|
||||
p1[3] = v >> 24;
|
||||
}
|
||||
|
||||
static inline uint16_t le16_to_cpupu(const uint16_t *p)
|
||||
{
|
||||
const uint8_t *p1 = (const uint8_t *)p;
|
||||
return p1[0] | (p1[1] << 8);
|
||||
}
|
||||
|
||||
static inline uint32_t le32_to_cpupu(const uint32_t *p)
|
||||
{
|
||||
const uint8_t *p1 = (const uint8_t *)p;
|
||||
return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24);
|
||||
}
|
||||
|
||||
static inline void cpu_to_be16wu(uint16_t *p, uint16_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v >> 8;
|
||||
p1[1] = v;
|
||||
}
|
||||
|
||||
static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v >> 24;
|
||||
p1[1] = v >> 16;
|
||||
p1[2] = v >> 8;
|
||||
p1[3] = v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define cpu_to_32wu cpu_to_be32wu
|
||||
#else
|
||||
#define cpu_to_32wu cpu_to_le32wu
|
||||
#endif
|
||||
|
||||
#undef le_bswap
|
||||
#undef be_bswap
|
||||
#undef le_bswaps
|
||||
#undef be_bswaps
|
||||
|
||||
#endif /* BSWAP_H */
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#! /bin/sh
|
||||
# Script to check for duplicate function prologues in op.o
|
||||
# Typically this indicates missing FORCE_RET();
|
||||
# This script does not detect other errors that may be present.
|
||||
|
||||
# Usage: check_ops.sh [-m machine] [op.o]
|
||||
# machine and op.o are guessed if not specified.
|
||||
|
||||
if [ "x$1" = "x-m" ]; then
|
||||
machine=$2
|
||||
shift 2
|
||||
else
|
||||
machine=`uname -m`
|
||||
fi
|
||||
if [ -z "$1" ]; then
|
||||
for f in `find . -name op.o`; do
|
||||
/bin/sh "$0" -m $machine $f
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case $machine in
|
||||
i?86)
|
||||
ret='\tret'
|
||||
;;
|
||||
x86_64)
|
||||
ret='\tretq'
|
||||
;;
|
||||
arm)
|
||||
ret='\tldm.*pc'
|
||||
;;
|
||||
ppc* | powerpc*)
|
||||
ret='\tblr'
|
||||
;;
|
||||
mips*)
|
||||
ret='\tjr.*ra'
|
||||
;;
|
||||
*)
|
||||
echo "Unknown machine `uname -m`"
|
||||
;;
|
||||
esac
|
||||
echo $1
|
||||
# op_exit_tb causes false positives on some hosts.
|
||||
${CROSS}objdump -dr $1 | \
|
||||
sed -e '/>:$\|'"$ret"'/!d' -e 's/.*<\(.*\)>:/~\1:/' -e 's/.*'"$ret"'.*/!/' | \
|
||||
sed -e ':1;N;s/\n//;t1' | sed -e 's/~/\n/g' | grep -v '^op_exit_tb' | \
|
||||
grep '^op_.*!!'
|
||||
@@ -0,0 +1,927 @@
|
||||
/*
|
||||
* QEMU Cocoa display driver
|
||||
*
|
||||
* Copyright (c) 2005 Pierre d'Herbemont
|
||||
* many code/inspiration from SDL 1.2 code (LGPL)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
Todo : x miniaturize window
|
||||
x center the window
|
||||
- save window position
|
||||
- handle keyboard event
|
||||
- handle mouse event
|
||||
- non 32 bpp support
|
||||
- full screen
|
||||
- mouse focus
|
||||
x simple graphical prompt to demo
|
||||
- better graphical prompt
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
NSWindow *window = NULL;
|
||||
NSQuickDrawView *qd_view = NULL;
|
||||
|
||||
|
||||
int gArgc;
|
||||
char **gArgv;
|
||||
DisplayState current_ds;
|
||||
|
||||
int grab = 0;
|
||||
int modifiers_state[256];
|
||||
|
||||
/* main defined in qemu/vl.c */
|
||||
int qemu_main(int argc, char **argv);
|
||||
|
||||
/* To deal with miniaturization */
|
||||
@interface QemuWindow : NSWindow
|
||||
{ }
|
||||
@end
|
||||
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
Qemu Video Driver
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_update
|
||||
------------------------------------------------------
|
||||
*/
|
||||
static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
|
||||
{
|
||||
//printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
|
||||
|
||||
/* Use QDFlushPortBuffer() to flush content to display */
|
||||
RgnHandle dirty = NewRgn ();
|
||||
RgnHandle temp = NewRgn ();
|
||||
|
||||
SetEmptyRgn (dirty);
|
||||
|
||||
/* Build the region of dirty rectangles */
|
||||
MacSetRectRgn (temp, x, y,
|
||||
x + w, y + h);
|
||||
MacUnionRgn (dirty, temp, dirty);
|
||||
|
||||
/* Flush the dirty region */
|
||||
QDFlushPortBuffer ( [ qd_view qdPort ], dirty );
|
||||
DisposeRgn (dirty);
|
||||
DisposeRgn (temp);
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_resize
|
||||
------------------------------------------------------
|
||||
*/
|
||||
static void cocoa_resize(DisplayState *ds, int w, int h)
|
||||
{
|
||||
const int device_bpp = 32;
|
||||
static void *screen_pixels;
|
||||
static int screen_pitch;
|
||||
NSRect contentRect;
|
||||
|
||||
//printf("resizing to %d %d\n", w, h);
|
||||
|
||||
contentRect = NSMakeRect (0, 0, w, h);
|
||||
if(window)
|
||||
{
|
||||
[window close];
|
||||
[window release];
|
||||
}
|
||||
window = [ [ QemuWindow alloc ] initWithContentRect:contentRect
|
||||
styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
|
||||
backing:NSBackingStoreBuffered defer:NO];
|
||||
if(!window)
|
||||
{
|
||||
fprintf(stderr, "(cocoa) can't create window\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(qd_view)
|
||||
[qd_view release];
|
||||
|
||||
qd_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
|
||||
|
||||
if(!qd_view)
|
||||
{
|
||||
fprintf(stderr, "(cocoa) can't create qd_view\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
[ window setAcceptsMouseMovedEvents:YES ];
|
||||
[ window setTitle:@"Qemu" ];
|
||||
[ window setReleasedWhenClosed:NO ];
|
||||
|
||||
/* Set screen to black */
|
||||
[ window setBackgroundColor: [NSColor blackColor] ];
|
||||
|
||||
/* set window position */
|
||||
[ window center ];
|
||||
|
||||
[ qd_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
|
||||
[ [ window contentView ] addSubview:qd_view ];
|
||||
[ qd_view release ];
|
||||
[ window makeKeyAndOrderFront:nil ];
|
||||
|
||||
/* Careful here, the window seems to have to be onscreen to do that */
|
||||
LockPortBits ( [ qd_view qdPort ] );
|
||||
screen_pixels = GetPixBaseAddr ( GetPortPixMap ( [ qd_view qdPort ] ) );
|
||||
screen_pitch = GetPixRowBytes ( GetPortPixMap ( [ qd_view qdPort ] ) );
|
||||
UnlockPortBits ( [ qd_view qdPort ] );
|
||||
{
|
||||
int vOffset = [ window frame ].size.height -
|
||||
[ qd_view frame ].size.height - [ qd_view frame ].origin.y;
|
||||
|
||||
int hOffset = [ qd_view frame ].origin.x;
|
||||
|
||||
screen_pixels += (vOffset * screen_pitch) + hOffset * (device_bpp/8);
|
||||
}
|
||||
ds->data = screen_pixels;
|
||||
ds->linesize = screen_pitch;
|
||||
ds->depth = device_bpp;
|
||||
ds->width = w;
|
||||
ds->height = h;
|
||||
|
||||
current_ds = *ds;
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
keymap conversion
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
int keymap[] =
|
||||
{
|
||||
// SdlI macI macH SdlH 104xtH 104xtC sdl
|
||||
30, // 0 0x00 0x1e A QZ_a
|
||||
31, // 1 0x01 0x1f S QZ_s
|
||||
32, // 2 0x02 0x20 D QZ_d
|
||||
33, // 3 0x03 0x21 F QZ_f
|
||||
35, // 4 0x04 0x23 H QZ_h
|
||||
34, // 5 0x05 0x22 G QZ_g
|
||||
44, // 6 0x06 0x2c Z QZ_z
|
||||
45, // 7 0x07 0x2d X QZ_x
|
||||
46, // 8 0x08 0x2e C QZ_c
|
||||
47, // 9 0x09 0x2f V QZ_v
|
||||
0, // 10 0x0A Undefined
|
||||
48, // 11 0x0B 0x30 B QZ_b
|
||||
16, // 12 0x0C 0x10 Q QZ_q
|
||||
17, // 13 0x0D 0x11 W QZ_w
|
||||
18, // 14 0x0E 0x12 E QZ_e
|
||||
19, // 15 0x0F 0x13 R QZ_r
|
||||
21, // 16 0x10 0x15 Y QZ_y
|
||||
20, // 17 0x11 0x14 T QZ_t
|
||||
2, // 18 0x12 0x02 1 QZ_1
|
||||
3, // 19 0x13 0x03 2 QZ_2
|
||||
4, // 20 0x14 0x04 3 QZ_3
|
||||
5, // 21 0x15 0x05 4 QZ_4
|
||||
7, // 22 0x16 0x07 6 QZ_6
|
||||
6, // 23 0x17 0x06 5 QZ_5
|
||||
13, // 24 0x18 0x0d = QZ_EQUALS
|
||||
10, // 25 0x19 0x0a 9 QZ_9
|
||||
8, // 26 0x1A 0x08 7 QZ_7
|
||||
12, // 27 0x1B 0x0c - QZ_MINUS
|
||||
9, // 28 0x1C 0x09 8 QZ_8
|
||||
11, // 29 0x1D 0x0b 0 QZ_0
|
||||
27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET
|
||||
24, // 31 0x1F 0x18 O QZ_o
|
||||
22, // 32 0x20 0x16 U QZ_u
|
||||
26, // 33 0x21 0x1a [ QZ_LEFTBRACKET
|
||||
23, // 34 0x22 0x17 I QZ_i
|
||||
25, // 35 0x23 0x19 P QZ_p
|
||||
28, // 36 0x24 0x1c ENTER QZ_RETURN
|
||||
38, // 37 0x25 0x26 L QZ_l
|
||||
36, // 38 0x26 0x24 J QZ_j
|
||||
40, // 39 0x27 0x28 ' QZ_QUOTE
|
||||
37, // 40 0x28 0x25 K QZ_k
|
||||
39, // 41 0x29 0x27 ; QZ_SEMICOLON
|
||||
43, // 42 0x2A 0x2b \ QZ_BACKSLASH
|
||||
51, // 43 0x2B 0x33 , QZ_COMMA
|
||||
53, // 44 0x2C 0x35 / QZ_SLASH
|
||||
49, // 45 0x2D 0x31 N QZ_n
|
||||
50, // 46 0x2E 0x32 M QZ_m
|
||||
52, // 47 0x2F 0x34 . QZ_PERIOD
|
||||
15, // 48 0x30 0x0f TAB QZ_TAB
|
||||
57, // 49 0x31 0x39 SPACE QZ_SPACE
|
||||
41, // 50 0x32 0x29 ` QZ_BACKQUOTE
|
||||
14, // 51 0x33 0x0e BKSP QZ_BACKSPACE
|
||||
0, // 52 0x34 Undefined
|
||||
1, // 53 0x35 0x01 ESC QZ_ESCAPE
|
||||
0, // 54 0x36 QZ_RMETA
|
||||
0, // 55 0x37 QZ_LMETA
|
||||
42, // 56 0x38 0x2a L SHFT QZ_LSHIFT
|
||||
58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK
|
||||
56, // 58 0x3A 0x38 L ALT QZ_LALT
|
||||
29, // 59 0x3B 0x1d L CTRL QZ_LCTRL
|
||||
54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT
|
||||
184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT
|
||||
157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL
|
||||
0, // 63 0x3F Undefined
|
||||
0, // 64 0x40 Undefined
|
||||
0, // 65 0x41 Undefined
|
||||
0, // 66 0x42 Undefined
|
||||
55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY
|
||||
0, // 68 0x44 Undefined
|
||||
78, // 69 0x45 0x4e KP + QZ_KP_PLUS
|
||||
0, // 70 0x46 Undefined
|
||||
69, // 71 0x47 0x45 NUM QZ_NUMLOCK
|
||||
0, // 72 0x48 Undefined
|
||||
0, // 73 0x49 Undefined
|
||||
0, // 74 0x4A Undefined
|
||||
181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE
|
||||
152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER
|
||||
0, // 77 0x4D undefined
|
||||
74, // 78 0x4E 0x4a KP - QZ_KP_MINUS
|
||||
0, // 79 0x4F Undefined
|
||||
0, // 80 0x50 Undefined
|
||||
0, // 81 0x51 QZ_KP_EQUALS
|
||||
82, // 82 0x52 0x52 KP 0 QZ_KP0
|
||||
79, // 83 0x53 0x4f KP 1 QZ_KP1
|
||||
80, // 84 0x54 0x50 KP 2 QZ_KP2
|
||||
81, // 85 0x55 0x51 KP 3 QZ_KP3
|
||||
75, // 86 0x56 0x4b KP 4 QZ_KP4
|
||||
76, // 87 0x57 0x4c KP 5 QZ_KP5
|
||||
77, // 88 0x58 0x4d KP 6 QZ_KP6
|
||||
71, // 89 0x59 0x47 KP 7 QZ_KP7
|
||||
0, // 90 0x5A Undefined
|
||||
72, // 91 0x5B 0x48 KP 8 QZ_KP8
|
||||
73, // 92 0x5C 0x49 KP 9 QZ_KP9
|
||||
0, // 93 0x5D Undefined
|
||||
0, // 94 0x5E Undefined
|
||||
0, // 95 0x5F Undefined
|
||||
63, // 96 0x60 0x3f F5 QZ_F5
|
||||
64, // 97 0x61 0x40 F6 QZ_F6
|
||||
65, // 98 0x62 0x41 F7 QZ_F7
|
||||
61, // 99 0x63 0x3d F3 QZ_F3
|
||||
66, // 100 0x64 0x42 F8 QZ_F8
|
||||
67, // 101 0x65 0x43 F9 QZ_F9
|
||||
0, // 102 0x66 Undefined
|
||||
87, // 103 0x67 0x57 F11 QZ_F11
|
||||
0, // 104 0x68 Undefined
|
||||
183,// 105 0x69 0xb7 QZ_PRINT
|
||||
0, // 106 0x6A Undefined
|
||||
70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK
|
||||
0, // 108 0x6C Undefined
|
||||
68, // 109 0x6D 0x44 F10 QZ_F10
|
||||
0, // 110 0x6E Undefined
|
||||
88, // 111 0x6F 0x58 F12 QZ_F12
|
||||
0, // 112 0x70 Undefined
|
||||
110,// 113 0x71 0x0 QZ_PAUSE
|
||||
210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT
|
||||
199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME
|
||||
201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP
|
||||
211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE
|
||||
62, // 118 0x76 0x3e F4 QZ_F4
|
||||
207,// 119 0x77 0xcf E0,4f END QZ_END
|
||||
60, // 120 0x78 0x3c F2 QZ_F2
|
||||
209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN
|
||||
59, // 122 0x7A 0x3b F1 QZ_F1
|
||||
203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT
|
||||
205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT
|
||||
208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN
|
||||
200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP
|
||||
/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
|
||||
|
||||
/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
|
||||
/*
|
||||
219 // 0xdb e0,5b L GUI
|
||||
220 // 0xdc e0,5c R GUI
|
||||
221 // 0xdd e0,5d APPS
|
||||
// E0,2A,E0,37 PRNT SCRN
|
||||
// E1,1D,45,E1,9D,C5 PAUSE
|
||||
83 // 0x53 0x53 KP .
|
||||
// ACPI Scan Codes
|
||||
222 // 0xde E0, 5E Power
|
||||
223 // 0xdf E0, 5F Sleep
|
||||
227 // 0xe3 E0, 63 Wake
|
||||
// Windows Multimedia Scan Codes
|
||||
153 // 0x99 E0, 19 Next Track
|
||||
144 // 0x90 E0, 10 Previous Track
|
||||
164 // 0xa4 E0, 24 Stop
|
||||
162 // 0xa2 E0, 22 Play/Pause
|
||||
160 // 0xa0 E0, 20 Mute
|
||||
176 // 0xb0 E0, 30 Volume Up
|
||||
174 // 0xae E0, 2E Volume Down
|
||||
237 // 0xed E0, 6D Media Select
|
||||
236 // 0xec E0, 6C E-Mail
|
||||
161 // 0xa1 E0, 21 Calculator
|
||||
235 // 0xeb E0, 6B My Computer
|
||||
229 // 0xe5 E0, 65 WWW Search
|
||||
178 // 0xb2 E0, 32 WWW Home
|
||||
234 // 0xea E0, 6A WWW Back
|
||||
233 // 0xe9 E0, 69 WWW Forward
|
||||
232 // 0xe8 E0, 68 WWW Stop
|
||||
231 // 0xe7 E0, 67 WWW Refresh
|
||||
230 // 0xe6 E0, 66 WWW Favorites
|
||||
*/
|
||||
};
|
||||
|
||||
int cocoa_keycode_to_qemu(int keycode)
|
||||
{
|
||||
if((sizeof(keymap)/sizeof(int)) <= keycode)
|
||||
{
|
||||
printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
|
||||
return 0;
|
||||
}
|
||||
return keymap[keycode];
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_refresh
|
||||
------------------------------------------------------
|
||||
*/
|
||||
static void cocoa_refresh(DisplayState *ds)
|
||||
{
|
||||
//printf("cocoa_refresh \n");
|
||||
NSDate *distantPast;
|
||||
NSEvent *event;
|
||||
NSAutoreleasePool *pool;
|
||||
|
||||
pool = [ [ NSAutoreleasePool alloc ] init ];
|
||||
distantPast = [ NSDate distantPast ];
|
||||
|
||||
vga_hw_update();
|
||||
|
||||
do {
|
||||
event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
|
||||
inMode: NSDefaultRunLoopMode dequeue:YES ];
|
||||
if (event != nil) {
|
||||
switch ([event type]) {
|
||||
case NSFlagsChanged:
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
|
||||
if (keycode)
|
||||
{
|
||||
if (keycode == 58 || keycode == 69) {
|
||||
/* emulate caps lock and num lock keydown and keyup */
|
||||
kbd_put_keycode(keycode);
|
||||
kbd_put_keycode(keycode | 0x80);
|
||||
} else if (is_graphic_console()) {
|
||||
if (keycode & 0x80)
|
||||
kbd_put_keycode(0xe0);
|
||||
if (modifiers_state[keycode] == 0) {
|
||||
/* keydown */
|
||||
kbd_put_keycode(keycode & 0x7f);
|
||||
modifiers_state[keycode] = 1;
|
||||
} else {
|
||||
/* keyup */
|
||||
kbd_put_keycode(keycode | 0x80);
|
||||
modifiers_state[keycode] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* release Mouse grab when pressing ctrl+alt */
|
||||
if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask))
|
||||
{
|
||||
[window setTitle: @"QEMU"];
|
||||
[NSCursor unhide];
|
||||
CGAssociateMouseAndMouseCursorPosition ( TRUE );
|
||||
grab = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NSKeyDown:
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
|
||||
/* handle command Key Combos */
|
||||
if ([event modifierFlags] & NSCommandKeyMask) {
|
||||
switch ([event keyCode]) {
|
||||
/* quit */
|
||||
case 12: /* q key */
|
||||
/* switch to windowed View */
|
||||
exit(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle control + alt Key Combos */
|
||||
if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
|
||||
switch (keycode) {
|
||||
/* toggle Monitor */
|
||||
case 0x02 ... 0x0a: /* '1' to '9' keys */
|
||||
console_select(keycode - 0x02);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* handle standard key events */
|
||||
if (is_graphic_console()) {
|
||||
if (keycode & 0x80) //check bit for e0 in front
|
||||
kbd_put_keycode(0xe0);
|
||||
kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
|
||||
/* handle monitor key events */
|
||||
} else {
|
||||
int keysym = 0;
|
||||
|
||||
switch([event keyCode]) {
|
||||
case 115:
|
||||
keysym = QEMU_KEY_HOME;
|
||||
break;
|
||||
case 117:
|
||||
keysym = QEMU_KEY_DELETE;
|
||||
break;
|
||||
case 119:
|
||||
keysym = QEMU_KEY_END;
|
||||
break;
|
||||
case 123:
|
||||
keysym = QEMU_KEY_LEFT;
|
||||
break;
|
||||
case 124:
|
||||
keysym = QEMU_KEY_RIGHT;
|
||||
break;
|
||||
case 125:
|
||||
keysym = QEMU_KEY_DOWN;
|
||||
break;
|
||||
case 126:
|
||||
keysym = QEMU_KEY_UP;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
NSString *ks = [event characters];
|
||||
|
||||
if ([ks length] > 0)
|
||||
keysym = [ks characterAtIndex:0];
|
||||
}
|
||||
}
|
||||
if (keysym)
|
||||
kbd_put_keysym(keysym);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NSKeyUp:
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
if (is_graphic_console()) {
|
||||
if (keycode & 0x80)
|
||||
kbd_put_keycode(0xe0);
|
||||
kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NSMouseMoved:
|
||||
if (grab) {
|
||||
int dx = [event deltaX];
|
||||
int dy = [event deltaY];
|
||||
int dz = [event deltaZ];
|
||||
int buttons = 0;
|
||||
kbd_mouse_event(dx, dy, dz, buttons);
|
||||
}
|
||||
break;
|
||||
|
||||
case NSLeftMouseDown:
|
||||
if (grab) {
|
||||
int buttons = 0;
|
||||
|
||||
/* leftclick+command simulates rightclick */
|
||||
if ([event modifierFlags] & NSCommandKeyMask) {
|
||||
buttons |= MOUSE_EVENT_RBUTTON;
|
||||
} else {
|
||||
buttons |= MOUSE_EVENT_LBUTTON;
|
||||
}
|
||||
kbd_mouse_event(0, 0, 0, buttons);
|
||||
} else {
|
||||
[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSLeftMouseDragged:
|
||||
if (grab) {
|
||||
int dx = [event deltaX];
|
||||
int dy = [event deltaY];
|
||||
int dz = [event deltaZ];
|
||||
int buttons = 0;
|
||||
if ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) { //leftclick+command simulates rightclick
|
||||
buttons |= MOUSE_EVENT_RBUTTON;
|
||||
} else {
|
||||
buttons |= MOUSE_EVENT_LBUTTON;
|
||||
}
|
||||
kbd_mouse_event(dx, dy, dz, buttons);
|
||||
}
|
||||
break;
|
||||
|
||||
case NSLeftMouseUp:
|
||||
if (grab) {
|
||||
kbd_mouse_event(0, 0, 0, 0);
|
||||
} else {
|
||||
[window setTitle: @"QEMU (Press ctrl + alt to release Mouse)"];
|
||||
[NSCursor hide];
|
||||
CGAssociateMouseAndMouseCursorPosition ( FALSE );
|
||||
grab = 1;
|
||||
//[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSRightMouseDown:
|
||||
if (grab) {
|
||||
int buttons = 0;
|
||||
|
||||
buttons |= MOUSE_EVENT_RBUTTON;
|
||||
kbd_mouse_event(0, 0, 0, buttons);
|
||||
} else {
|
||||
[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSRightMouseDragged:
|
||||
if (grab) {
|
||||
int dx = [event deltaX];
|
||||
int dy = [event deltaY];
|
||||
int dz = [event deltaZ];
|
||||
int buttons = 0;
|
||||
buttons |= MOUSE_EVENT_RBUTTON;
|
||||
kbd_mouse_event(dx, dy, dz, buttons);
|
||||
}
|
||||
break;
|
||||
|
||||
case NSRightMouseUp:
|
||||
if (grab) {
|
||||
kbd_mouse_event(0, 0, 0, 0);
|
||||
} else {
|
||||
[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSOtherMouseDragged:
|
||||
if (grab) {
|
||||
int dx = [event deltaX];
|
||||
int dy = [event deltaY];
|
||||
int dz = [event deltaZ];
|
||||
int buttons = 0;
|
||||
buttons |= MOUSE_EVENT_MBUTTON;
|
||||
kbd_mouse_event(dx, dy, dz, buttons);
|
||||
}
|
||||
break;
|
||||
|
||||
case NSOtherMouseDown:
|
||||
if (grab) {
|
||||
int buttons = 0;
|
||||
buttons |= MOUSE_EVENT_MBUTTON;
|
||||
kbd_mouse_event(0, 0, 0, buttons);
|
||||
} else {
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSOtherMouseUp:
|
||||
if (grab) {
|
||||
kbd_mouse_event(0, 0, 0, 0);
|
||||
} else {
|
||||
[NSApp sendEvent: event];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSScrollWheel:
|
||||
if (grab) {
|
||||
int dz = [event deltaY];
|
||||
kbd_mouse_event(0, 0, -dz, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default: [NSApp sendEvent:event];
|
||||
}
|
||||
}
|
||||
} while(event != nil);
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_cleanup
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void cocoa_cleanup(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
cocoa_display_init
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
void cocoa_display_init(DisplayState *ds, int full_screen)
|
||||
{
|
||||
ds->dpy_update = cocoa_update;
|
||||
ds->dpy_resize = cocoa_resize;
|
||||
ds->dpy_refresh = cocoa_refresh;
|
||||
|
||||
cocoa_resize(ds, 640, 400);
|
||||
|
||||
atexit(cocoa_cleanup);
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
Interface with Cocoa
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
QemuWindow
|
||||
Some trick from SDL to use miniwindow
|
||||
------------------------------------------------------
|
||||
*/
|
||||
static void QZ_SetPortAlphaOpaque ()
|
||||
{
|
||||
/* Assume 32 bit if( bpp == 32 )*/
|
||||
if ( 1 ) {
|
||||
|
||||
uint32_t *pixels = (uint32_t*) current_ds.data;
|
||||
uint32_t rowPixels = current_ds.linesize / 4;
|
||||
uint32_t i, j;
|
||||
|
||||
for (i = 0; i < current_ds.height; i++)
|
||||
for (j = 0; j < current_ds.width; j++) {
|
||||
|
||||
pixels[ (i * rowPixels) + j ] |= 0xFF000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@implementation QemuWindow
|
||||
- (void)miniaturize:(id)sender
|
||||
{
|
||||
|
||||
/* make the alpha channel opaque so anim won't have holes in it */
|
||||
QZ_SetPortAlphaOpaque ();
|
||||
|
||||
[ super miniaturize:sender ];
|
||||
|
||||
}
|
||||
- (void)display
|
||||
{
|
||||
/*
|
||||
This method fires just before the window deminaturizes from the Dock.
|
||||
|
||||
We'll save the current visible surface, let the window manager redraw any
|
||||
UI elements, and restore the SDL surface. This way, no expose event
|
||||
is required, and the deminiaturize works perfectly.
|
||||
*/
|
||||
|
||||
/* make sure pixels are fully opaque */
|
||||
QZ_SetPortAlphaOpaque ();
|
||||
|
||||
/* save current visible SDL surface */
|
||||
[ self cacheImageInRect:[ qd_view frame ] ];
|
||||
|
||||
/* let the window manager redraw controls, border, etc */
|
||||
[ super display ];
|
||||
|
||||
/* restore visible SDL surface */
|
||||
[ self restoreCachedImage ];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
QemuCocoaGUIController
|
||||
NSApp's delegate - indeed main object
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
@interface QemuCocoaGUIController : NSObject
|
||||
{
|
||||
}
|
||||
- (void)applicationDidFinishLaunching: (NSNotification *) note;
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification;
|
||||
|
||||
- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
|
||||
|
||||
- (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
|
||||
@end
|
||||
|
||||
@implementation QemuCocoaGUIController
|
||||
/* Called when the internal event loop has just started running */
|
||||
- (void)applicationDidFinishLaunching: (NSNotification *) note
|
||||
{
|
||||
|
||||
/* Display an open dialog box if no argument were passed or
|
||||
if qemu was launched from the finder ( the Finder passes "-psn" ) */
|
||||
|
||||
if( gArgc <= 1 || strncmp (gArgv[1], "-psn", 4) == 0)
|
||||
{
|
||||
NSOpenPanel *op = [[NSOpenPanel alloc] init];
|
||||
|
||||
cocoa_resize(¤t_ds, 640, 400);
|
||||
|
||||
[op setPrompt:@"Boot image"];
|
||||
|
||||
[op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
|
||||
|
||||
[op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
|
||||
modalForWindow:window modalDelegate:self
|
||||
didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* or Launch Qemu, with the global args */
|
||||
[self startEmulationWithArgc:gArgc argv:gArgv];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
printf("Application will terminate\n");
|
||||
qemu_system_shutdown_request();
|
||||
/* In order to avoid a crash */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
{
|
||||
if(returnCode == NSCancelButton)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(returnCode == NSOKButton)
|
||||
{
|
||||
char *bin = "qemu";
|
||||
char *img = (char*)[ [ sheet filename ] cString];
|
||||
|
||||
char **argv = (char**)malloc( sizeof(char*)*3 );
|
||||
|
||||
asprintf(&argv[0], "%s", bin);
|
||||
asprintf(&argv[1], "-hda");
|
||||
asprintf(&argv[2], "%s", img);
|
||||
|
||||
printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
|
||||
|
||||
[self startEmulationWithArgc:3 argv:(char**)argv];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
|
||||
{
|
||||
int status;
|
||||
/* Launch Qemu */
|
||||
printf("starting qemu...\n");
|
||||
status = qemu_main (argc, argv);
|
||||
exit(status);
|
||||
}
|
||||
@end
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
Application Creation
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* Dock Connection */
|
||||
typedef struct CPSProcessSerNum
|
||||
{
|
||||
UInt32 lo;
|
||||
UInt32 hi;
|
||||
} CPSProcessSerNum;
|
||||
|
||||
extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
|
||||
extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
|
||||
extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
|
||||
|
||||
/* Menu Creation */
|
||||
static void setApplicationMenu(void)
|
||||
{
|
||||
/* warning: this code is very odd */
|
||||
NSMenu *appleMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSString *title;
|
||||
NSString *appName;
|
||||
|
||||
appName = @"Qemu";
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
/* Add menu items */
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||
|
||||
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
|
||||
|
||||
/* Put menu into the menubar */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
/* Tell the application object that this is now the application menu */
|
||||
[NSApp setAppleMenu:appleMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[appleMenu release];
|
||||
[menuItem release];
|
||||
}
|
||||
|
||||
/* Create a window menu */
|
||||
static void setupWindowMenu(void)
|
||||
{
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *windowMenuItem;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
/* "Minimize" item */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||
[windowMenu addItem:menuItem];
|
||||
[menuItem release];
|
||||
|
||||
/* Put menu into the menubar */
|
||||
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
[[NSApp mainMenu] addItem:windowMenuItem];
|
||||
|
||||
/* Tell the application object that this is now the window menu */
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[windowMenu release];
|
||||
[windowMenuItem release];
|
||||
}
|
||||
|
||||
static void CustomApplicationMain(void)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
QemuCocoaGUIController *gui_controller;
|
||||
CPSProcessSerNum PSN;
|
||||
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
if (!CPSGetCurrentProcess(&PSN))
|
||||
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
||||
if (!CPSSetFrontProcess(&PSN))
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
/* Set up the menubar */
|
||||
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||
setApplicationMenu();
|
||||
setupWindowMenu();
|
||||
|
||||
/* Create SDLMain and make it the app delegate */
|
||||
gui_controller = [[QemuCocoaGUIController alloc] init];
|
||||
[NSApp setDelegate:gui_controller];
|
||||
|
||||
/* Start the main event loop */
|
||||
[NSApp run];
|
||||
|
||||
[gui_controller release];
|
||||
[pool release];
|
||||
}
|
||||
|
||||
/* Real main of qemu-cocoa */
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
gArgc = argc;
|
||||
gArgv = argv;
|
||||
|
||||
CustomApplicationMain();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,992 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# qemu configure script (c) 2003 Fabrice Bellard
|
||||
#
|
||||
# set temporary file name
|
||||
if test ! -z "$TMPDIR" ; then
|
||||
TMPDIR1="${TMPDIR}"
|
||||
elif test ! -z "$TEMPDIR" ; then
|
||||
TMPDIR1="${TEMPDIR}"
|
||||
else
|
||||
TMPDIR1="/tmp"
|
||||
fi
|
||||
|
||||
TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
|
||||
TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
|
||||
TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}"
|
||||
TMPS="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.S"
|
||||
|
||||
# default parameters
|
||||
prefix=""
|
||||
interp_prefix="/usr/gnemul/qemu-%M"
|
||||
static="no"
|
||||
cross_prefix=""
|
||||
cc="gcc"
|
||||
gcc3_search="yes"
|
||||
gcc3_list="gcc-3.4 gcc34 gcc-3.3 gcc33 gcc-3.2 gcc32"
|
||||
host_cc="gcc"
|
||||
ar="ar"
|
||||
make="make"
|
||||
install="install"
|
||||
strip="strip"
|
||||
cpu=`uname -m`
|
||||
target_list=""
|
||||
case "$cpu" in
|
||||
i386|i486|i586|i686|i86pc|BePC)
|
||||
cpu="i386"
|
||||
;;
|
||||
armv*b)
|
||||
cpu="armv4b"
|
||||
;;
|
||||
armv*l)
|
||||
cpu="armv4l"
|
||||
;;
|
||||
alpha)
|
||||
cpu="alpha"
|
||||
;;
|
||||
"Power Macintosh"|ppc|ppc64)
|
||||
cpu="powerpc"
|
||||
;;
|
||||
mips)
|
||||
cpu="mips"
|
||||
;;
|
||||
s390)
|
||||
cpu="s390"
|
||||
;;
|
||||
sparc|sun4[muv])
|
||||
cpu="sparc"
|
||||
;;
|
||||
sparc64)
|
||||
cpu="sparc64"
|
||||
;;
|
||||
ia64)
|
||||
cpu="ia64"
|
||||
;;
|
||||
m68k)
|
||||
cpu="m68k"
|
||||
;;
|
||||
x86_64|amd64)
|
||||
cpu="x86_64"
|
||||
;;
|
||||
*)
|
||||
cpu="unknown"
|
||||
;;
|
||||
esac
|
||||
gprof="no"
|
||||
bigendian="no"
|
||||
mingw32="no"
|
||||
EXESUF=""
|
||||
gdbstub="yes"
|
||||
slirp="yes"
|
||||
adlib="no"
|
||||
oss="no"
|
||||
dsound="no"
|
||||
coreaudio="no"
|
||||
alsa="no"
|
||||
fmod="no"
|
||||
fmod_lib=""
|
||||
fmod_inc=""
|
||||
bsd="no"
|
||||
linux="no"
|
||||
kqemu="no"
|
||||
profiler="no"
|
||||
cocoa="no"
|
||||
check_gfx="yes"
|
||||
check_gcc="yes"
|
||||
softmmu="yes"
|
||||
linux_user="no"
|
||||
darwin_user="no"
|
||||
build_docs="no"
|
||||
uname_release=""
|
||||
|
||||
# OS specific
|
||||
targetos=`uname -s`
|
||||
case $targetos in
|
||||
CYGWIN*)
|
||||
mingw32="yes"
|
||||
OS_CFLAGS="-mno-cygwin"
|
||||
;;
|
||||
MINGW32*)
|
||||
mingw32="yes"
|
||||
;;
|
||||
FreeBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
;;
|
||||
NetBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
;;
|
||||
OpenBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
;;
|
||||
Darwin)
|
||||
bsd="yes"
|
||||
darwin="yes"
|
||||
darwin_user="yes"
|
||||
cocoa="yes"
|
||||
coreaudio="yes"
|
||||
OS_CFLAGS="-mdynamic-no-pic"
|
||||
;;
|
||||
SunOS)
|
||||
solaris="yes"
|
||||
;;
|
||||
*)
|
||||
oss="yes"
|
||||
linux="yes"
|
||||
linux_user="yes"
|
||||
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$bsd" = "yes" ] ; then
|
||||
if [ "$darwin" != "yes" ] ; then
|
||||
make="gmake"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$solaris" = "yes" ] ; then
|
||||
make="gmake"
|
||||
install="ginstall"
|
||||
solarisrev=`uname -r | cut -f2 -d.`
|
||||
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
|
||||
if test "$solarisrev" -gt 10 ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# find source path
|
||||
source_path=`dirname "$0"`
|
||||
if [ -z "$source_path" ]; then
|
||||
source_path=`pwd`
|
||||
else
|
||||
source_path=`cd "$source_path"; pwd`
|
||||
fi
|
||||
if test "$source_path" = `pwd` ; then
|
||||
source_path_used="no"
|
||||
else
|
||||
source_path_used="yes"
|
||||
fi
|
||||
|
||||
for opt do
|
||||
optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
|
||||
case "$opt" in
|
||||
--help|-h) show_help=yes
|
||||
;;
|
||||
--prefix=*) prefix="$optarg"
|
||||
;;
|
||||
--interp-prefix=*) interp_prefix="$optarg"
|
||||
;;
|
||||
--source-path=*) source_path="$optarg"
|
||||
source_path_used="yes"
|
||||
;;
|
||||
--cross-prefix=*) cross_prefix="$optarg"
|
||||
;;
|
||||
--cc=*) cc="$optarg"
|
||||
gcc3_search="no"
|
||||
;;
|
||||
--host-cc=*) host_cc="$optarg"
|
||||
;;
|
||||
--make=*) make="$optarg"
|
||||
;;
|
||||
--install=*) install="$optarg"
|
||||
;;
|
||||
--extra-cflags=*) CFLAGS="$optarg"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="$optarg"
|
||||
;;
|
||||
--cpu=*) cpu="$optarg"
|
||||
;;
|
||||
--target-list=*) target_list="$optarg"
|
||||
;;
|
||||
--enable-gprof) gprof="yes"
|
||||
;;
|
||||
--static) static="yes"
|
||||
;;
|
||||
--disable-sdl) sdl="no"
|
||||
;;
|
||||
--enable-coreaudio) coreaudio="yes"
|
||||
;;
|
||||
--enable-alsa) alsa="yes"
|
||||
;;
|
||||
--enable-dsound) dsound="yes"
|
||||
;;
|
||||
--enable-fmod) fmod="yes"
|
||||
;;
|
||||
--fmod-lib=*) fmod_lib="$optarg"
|
||||
;;
|
||||
--fmod-inc=*) fmod_inc="$optarg"
|
||||
;;
|
||||
--enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" ; user="no"
|
||||
;;
|
||||
--disable-slirp) slirp="no"
|
||||
;;
|
||||
--enable-adlib) adlib="yes"
|
||||
;;
|
||||
--disable-kqemu) kqemu="no"
|
||||
;;
|
||||
--enable-profiler) profiler="yes"
|
||||
;;
|
||||
--enable-cocoa) cocoa="yes" ; coreaudio="yes" ; sdl="no"
|
||||
;;
|
||||
--disable-gfx-check) check_gfx="no"
|
||||
;;
|
||||
--disable-gcc-check) check_gcc="no"
|
||||
;;
|
||||
--disable-system) softmmu="no"
|
||||
;;
|
||||
--enable-system) softmmu="yes"
|
||||
;;
|
||||
--disable-linux-user) linux_user="no"
|
||||
;;
|
||||
--enable-linux-user) linux_user="yes"
|
||||
;;
|
||||
--disable-darwin-user) darwin_user="no"
|
||||
;;
|
||||
--enable-darwin-user) darwin_user="yes"
|
||||
;;
|
||||
--enable-uname-release=*) uname_release="$optarg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# default flags for all hosts
|
||||
CFLAGS="$CFLAGS -Wall -O2 -g -fno-strict-aliasing"
|
||||
LDFLAGS="$LDFLAGS -g"
|
||||
|
||||
if test x"$show_help" = x"yes" ; then
|
||||
cat << EOF
|
||||
|
||||
Usage: configure [options]
|
||||
Options: [defaults in brackets after descriptions]
|
||||
|
||||
EOF
|
||||
echo "Standard options:"
|
||||
echo " --help print this message"
|
||||
echo " --prefix=PREFIX install in PREFIX [$prefix]"
|
||||
echo " --interp-prefix=PREFIX where to find shared libraries, etc."
|
||||
echo " use %M for cpu name [$interp_prefix]"
|
||||
echo " --target-list=LIST set target list [$target_list]"
|
||||
echo ""
|
||||
echo "kqemu kernel acceleration support:"
|
||||
echo " --disable-kqemu disable kqemu support"
|
||||
echo ""
|
||||
echo "Advanced options (experts only):"
|
||||
echo " --source-path=PATH path of source code [$source_path]"
|
||||
echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]"
|
||||
echo " --cc=CC use C compiler CC [$cc]"
|
||||
echo " --host-cc=CC use C compiler CC [$host_cc] for dyngen etc."
|
||||
echo " --make=MAKE use specified make [$make]"
|
||||
echo " --install=INSTALL use specified install [$install]"
|
||||
echo " --static enable static build [$static]"
|
||||
echo " --enable-cocoa enable COCOA (Mac OS X only)"
|
||||
echo " --enable-mingw32 enable Win32 cross compilation with mingw32"
|
||||
echo " --enable-adlib enable Adlib emulation"
|
||||
echo " --enable-coreaudio enable Coreaudio audio driver"
|
||||
echo " --enable-alsa enable ALSA audio driver"
|
||||
echo " --enable-fmod enable FMOD audio driver"
|
||||
echo " --enabled-dsound enable DirectSound audio driver"
|
||||
echo " --enable-system enable all system emulation targets"
|
||||
echo " --disable-system disable all system emulation targets"
|
||||
echo " --enable-linux-user enable all linux usermode emulation targets"
|
||||
echo " --disable-linux-user disable all linux usermode emulation targets"
|
||||
echo " --enable-darwin-user enable all darwin usermode emulation targets"
|
||||
echo " --disable-darwin-user disable all darwin usermode emulation targets"
|
||||
echo " --fmod-lib path to FMOD library"
|
||||
echo " --fmod-inc path to FMOD includes"
|
||||
echo " --enable-uname-release=R Return R for uname -r in usermode emulation"
|
||||
echo ""
|
||||
echo "NOTE: The object files are built at the place where configure is launched"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cc="${cross_prefix}${cc}"
|
||||
ar="${cross_prefix}${ar}"
|
||||
strip="${cross_prefix}${strip}"
|
||||
|
||||
# check that the C compiler works.
|
||||
cat > $TMPC <<EOF
|
||||
int main(void) {}
|
||||
EOF
|
||||
|
||||
if $cc -c -o $TMPO $TMPC 2>/dev/null ; then
|
||||
: C compiler works ok
|
||||
else
|
||||
echo "ERROR: \"$cc\" either does not exist or does not work"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test "$mingw32" = "yes" ; then
|
||||
linux="no"
|
||||
EXESUF=".exe"
|
||||
oss="no"
|
||||
if [ "$cpu" = "i386" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for gcc4, error if pre-gcc4
|
||||
if test "$check_gcc" = "yes" ; then
|
||||
cat > $TMPC <<EOF
|
||||
#if __GNUC__ < 4
|
||||
#error gcc3
|
||||
#endif
|
||||
int main(){return 0;}
|
||||
EOF
|
||||
check_cc() {
|
||||
which "$1" >&/dev/null
|
||||
return $?
|
||||
}
|
||||
|
||||
if "$cc" -o $TMPE $TMPC 2>/dev/null ; then
|
||||
echo "WARNING: \"$cc\" looks like gcc 4.x"
|
||||
found_compat_cc="no"
|
||||
if test "$gcc3_search" = "yes" ; then
|
||||
echo "Looking for gcc 3.x"
|
||||
for compat_cc in $gcc3_list ; do
|
||||
if check_cc "$compat_cc" ; then
|
||||
echo "Found \"$compat_cc\""
|
||||
cc="$compat_cc"
|
||||
found_compat_cc="yes"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test "$found_compat_cc" = "no" ; then
|
||||
echo "gcc 3.x not found!"
|
||||
fi
|
||||
fi
|
||||
if test "$found_compat_cc" = "no" ; then
|
||||
echo "QEMU is known to have problems when compiled with gcc 4.x"
|
||||
echo "It is recommended that you use gcc 3.x to build QEMU"
|
||||
echo "To use this compiler anyway, configure with --disable-gcc-check"
|
||||
exit 1;
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Solaris specific configure tool chain decisions
|
||||
#
|
||||
if test "$solaris" = "yes" ; then
|
||||
#
|
||||
# gcc for solaris 10/fcs in /usr/sfw/bin doesn't compile qemu correctly
|
||||
# override the check with --disable-gcc-check
|
||||
#
|
||||
if test "$solarisrev" -eq 10 -a "$check_gcc" = "yes" ; then
|
||||
solgcc=`which $cc`
|
||||
if test "$solgcc" = "/usr/sfw/bin/gcc" ; then
|
||||
echo "Solaris 10/FCS gcc in /usr/sfw/bin will not compiled qemu correctly."
|
||||
echo "please get gcc-3.4.3 or later, from www.blastwave.org using pkg-get -i gcc3"
|
||||
echo "or get the latest patch from SunSolve for gcc"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
solinst=`which $install 2> /dev/null | /usr/bin/grep -v "no $install in"`
|
||||
if test -z "$solinst" ; then
|
||||
echo "Solaris install program not found. Use --install=/usr/ucb/install or"
|
||||
echo "install fileutils from www.blastwave.org using pkg-get -i fileutils"
|
||||
echo "to get ginstall which is used by default (which lives in /opt/csw/bin)"
|
||||
exit 1
|
||||
fi
|
||||
if test "$solinst" = "/usr/sbin/install" ; then
|
||||
echo "Error: Solaris /usr/sbin/install is not an appropriate install program."
|
||||
echo "try ginstall from the GNU fileutils available from www.blastwave.org"
|
||||
echo "using pkg-get -i fileutils, or use --install=/usr/ucb/install"
|
||||
exit 1
|
||||
fi
|
||||
sol_ar=`which ar 2> /dev/null | /usr/bin/grep -v "no ar in"`
|
||||
if test -z "$sol_ar" ; then
|
||||
echo "Error: No path includes ar"
|
||||
if test -f /usr/ccs/bin/ar ; then
|
||||
echo "Add /usr/ccs/bin to your path and rerun configure"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if test -z "$target_list" ; then
|
||||
# these targets are portable
|
||||
if [ "$softmmu" = "yes" ] ; then
|
||||
target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu mipsel-softmmu arm-softmmu"
|
||||
fi
|
||||
# the following are Linux specific
|
||||
if [ "$linux_user" = "yes" ] ; then
|
||||
target_list="i386-linux-user arm-linux-user armeb-linux-user sparc-linux-user ppc-linux-user mips-linux-user mipsel-linux-user m68k-linux-user $target_list"
|
||||
fi
|
||||
# the following are Darwin specific
|
||||
if [ "$darwin_user" = "yes" ] ; then
|
||||
target_list="i386-darwin-user ppc-darwin-user $target_list"
|
||||
fi
|
||||
else
|
||||
target_list=`echo "$target_list" | sed -e 's/,/ /g'`
|
||||
fi
|
||||
if test -z "$target_list" ; then
|
||||
echo "No targets enabled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$cross_prefix" ; then
|
||||
|
||||
# ---
|
||||
# big/little endian test
|
||||
cat > $TMPC << EOF
|
||||
#include <inttypes.h>
|
||||
int main(int argc, char ** argv){
|
||||
volatile uint32_t i=0x01234567;
|
||||
return (*((uint8_t*)(&i))) == 0x67;
|
||||
}
|
||||
EOF
|
||||
|
||||
if $cc -o $TMPE $TMPC 2>/dev/null ; then
|
||||
$TMPE && bigendian="yes"
|
||||
else
|
||||
echo big/little test failed
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
# if cross compiling, cannot launch a program, so make a static guess
|
||||
if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" -o "$cpu" = "sparc" -o "$cpu" = "sparc64" -o "$cpu" = "m68k" -o "$cpu" = "armv4b"; then
|
||||
bigendian="yes"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# host long bits test
|
||||
hostlongbits="32"
|
||||
if test "$cpu" = "sparc64" -o "$cpu" = "ia64" -o "$cpu" = "x86_64" -o "$cpu" = "alpha"; then
|
||||
hostlongbits="64"
|
||||
fi
|
||||
|
||||
# check gcc options support
|
||||
cat > $TMPC <<EOF
|
||||
int main(void) {
|
||||
}
|
||||
EOF
|
||||
|
||||
have_gcc3_options="no"
|
||||
if $cc -fno-reorder-blocks -fno-optimize-sibling-calls -o $TMPO $TMPC 2> /dev/null ; then
|
||||
have_gcc3_options="yes"
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# SDL probe
|
||||
|
||||
sdl_too_old=no
|
||||
|
||||
if test -z "$sdl" ; then
|
||||
|
||||
sdl_config="sdl-config"
|
||||
sdl=no
|
||||
sdl_static=no
|
||||
|
||||
if test "$mingw32" = "yes" -a ! -z "$cross_prefix" ; then
|
||||
# win32 cross compilation case
|
||||
sdl_config="i386-mingw32msvc-sdl-config"
|
||||
sdl=yes
|
||||
else
|
||||
# normal SDL probe
|
||||
cat > $TMPC << EOF
|
||||
#include <SDL.h>
|
||||
#undef main /* We don't want SDL to override our main() */
|
||||
int main( void ) { return SDL_Init (SDL_INIT_VIDEO); }
|
||||
EOF
|
||||
|
||||
if $cc -o $TMPE `$sdl_config --cflags 2> /dev/null` $TMPC `$sdl_config --libs 2> /dev/null` 2> /dev/null ; then
|
||||
_sdlversion=`$sdl_config --version | sed 's/[^0-9]//g'`
|
||||
if test "$_sdlversion" -lt 121 ; then
|
||||
sdl_too_old=yes
|
||||
else
|
||||
if test "$cocoa" = "no" ; then
|
||||
sdl=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
# static link with sdl ?
|
||||
if test "$sdl" = "yes" ; then
|
||||
aa="no"
|
||||
`$sdl_config --static-libs | grep \\\-laa > /dev/null` && aa="yes"
|
||||
sdl_static_libs=`$sdl_config --static-libs`
|
||||
if [ "$aa" = "yes" ] ; then
|
||||
sdl_static_libs="$sdl_static_libs `aalib-config --static-libs`"
|
||||
fi
|
||||
|
||||
if $cc -o $TMPE `$sdl_config --cflags 2> /dev/null` $TMPC $sdl_static_libs 2> /dev/null; then
|
||||
sdl_static=yes
|
||||
fi
|
||||
|
||||
fi # static link
|
||||
|
||||
fi # sdl compile test
|
||||
|
||||
fi # cross compilation
|
||||
|
||||
else
|
||||
# Make sure to disable cocoa if sdl was set
|
||||
if test "$sdl" = "yes" ; then
|
||||
cocoa="no"
|
||||
coreaudio="no"
|
||||
fi
|
||||
fi # -z $sdl
|
||||
|
||||
##########################################
|
||||
# alsa sound support libraries
|
||||
|
||||
if test "$alsa" = "yes" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <alsa/asoundlib.h>
|
||||
int main(void) { snd_pcm_t **handle; return snd_pcm_close(*handle); }
|
||||
EOF
|
||||
if $cc -o $TMPE $TMPC -lasound 2> /dev/null ; then
|
||||
:
|
||||
else
|
||||
echo
|
||||
echo "Error: Could not find alsa"
|
||||
echo "Make sure to have the alsa libs and headers installed."
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if tools are available to build documentation.
|
||||
if [ -x "`which texi2html`" ] && [ -x "`which pod2man`" ]; then
|
||||
build_docs="yes"
|
||||
fi
|
||||
|
||||
if test "$mingw32" = "yes" ; then
|
||||
if test -z "$prefix" ; then
|
||||
prefix="/c/Program Files/Qemu"
|
||||
fi
|
||||
mandir="$prefix"
|
||||
datadir="$prefix"
|
||||
docdir="$prefix"
|
||||
bindir="$prefix"
|
||||
else
|
||||
if test -z "$prefix" ; then
|
||||
prefix="/usr/local"
|
||||
fi
|
||||
mandir="$prefix/share/man"
|
||||
datadir="$prefix/share/qemu"
|
||||
docdir="$prefix/share/doc/qemu"
|
||||
bindir="$prefix/bin"
|
||||
fi
|
||||
|
||||
echo "Install prefix $prefix"
|
||||
echo "BIOS directory $datadir"
|
||||
echo "binary directory $bindir"
|
||||
if test "$mingw32" = "no" ; then
|
||||
echo "Manual directory $mandir"
|
||||
echo "ELF interp prefix $interp_prefix"
|
||||
fi
|
||||
echo "Source path $source_path"
|
||||
echo "C compiler $cc"
|
||||
echo "Host C compiler $host_cc"
|
||||
echo "make $make"
|
||||
echo "install $install"
|
||||
echo "host CPU $cpu"
|
||||
echo "host big endian $bigendian"
|
||||
echo "target list $target_list"
|
||||
echo "gprof enabled $gprof"
|
||||
echo "profiler $profiler"
|
||||
echo "static build $static"
|
||||
if test "$darwin" = "yes" ; then
|
||||
echo "Cocoa support $cocoa"
|
||||
fi
|
||||
echo "SDL support $sdl"
|
||||
if test "$sdl" != "no" ; then
|
||||
echo "SDL static link $sdl_static"
|
||||
fi
|
||||
echo "mingw32 support $mingw32"
|
||||
echo "Adlib support $adlib"
|
||||
echo "CoreAudio support $coreaudio"
|
||||
echo "ALSA support $alsa"
|
||||
echo "DSound support $dsound"
|
||||
if test "$fmod" = "yes"; then
|
||||
if test -z $fmod_lib || test -z $fmod_inc; then
|
||||
echo
|
||||
echo "Error: You must specify path to FMOD library and headers"
|
||||
echo "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
fmod_support=" (lib='$fmod_lib' include='$fmod_inc')"
|
||||
else
|
||||
fmod_support=""
|
||||
fi
|
||||
echo "FMOD support $fmod $fmod_support"
|
||||
echo "kqemu support $kqemu"
|
||||
echo "Documentation $build_docs"
|
||||
[ ! -z "$uname_release" ] && \
|
||||
echo "uname -r $uname_release"
|
||||
|
||||
if test $sdl_too_old = "yes"; then
|
||||
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
||||
fi
|
||||
#if test "$sdl_static" = "no"; then
|
||||
# echo "WARNING: cannot compile statically with SDL - qemu-fast won't have a graphical output"
|
||||
#fi
|
||||
config_mak="config-host.mak"
|
||||
config_h="config-host.h"
|
||||
|
||||
#echo "Creating $config_mak and $config_h"
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > $config_mak
|
||||
echo "# Configured with: $0 $@" >> $config_mak
|
||||
echo "/* Automatically generated by configure - do not modify */" > $config_h
|
||||
|
||||
echo "prefix=$prefix" >> $config_mak
|
||||
echo "bindir=$bindir" >> $config_mak
|
||||
echo "mandir=$mandir" >> $config_mak
|
||||
echo "datadir=$datadir" >> $config_mak
|
||||
echo "docdir=$docdir" >> $config_mak
|
||||
echo "#define CONFIG_QEMU_SHAREDIR \"$datadir\"" >> $config_h
|
||||
echo "MAKE=$make" >> $config_mak
|
||||
echo "INSTALL=$install" >> $config_mak
|
||||
echo "CC=$cc" >> $config_mak
|
||||
if test "$have_gcc3_options" = "yes" ; then
|
||||
echo "HAVE_GCC3_OPTIONS=yes" >> $config_mak
|
||||
fi
|
||||
echo "HOST_CC=$host_cc" >> $config_mak
|
||||
echo "AR=$ar" >> $config_mak
|
||||
echo "STRIP=$strip -s -R .comment -R .note" >> $config_mak
|
||||
echo "OS_CFLAGS=$OS_CFLAGS" >> $config_mak
|
||||
echo "CFLAGS=$CFLAGS" >> $config_mak
|
||||
echo "LDFLAGS=$LDFLAGS" >> $config_mak
|
||||
echo "EXESUF=$EXESUF" >> $config_mak
|
||||
if test "$cpu" = "i386" ; then
|
||||
echo "ARCH=i386" >> $config_mak
|
||||
echo "#define HOST_I386 1" >> $config_h
|
||||
elif test "$cpu" = "x86_64" ; then
|
||||
echo "ARCH=x86_64" >> $config_mak
|
||||
echo "#define HOST_X86_64 1" >> $config_h
|
||||
elif test "$cpu" = "armv4b" ; then
|
||||
echo "ARCH=arm" >> $config_mak
|
||||
echo "#define HOST_ARM 1" >> $config_h
|
||||
elif test "$cpu" = "armv4l" ; then
|
||||
echo "ARCH=arm" >> $config_mak
|
||||
echo "#define HOST_ARM 1" >> $config_h
|
||||
elif test "$cpu" = "powerpc" ; then
|
||||
echo "ARCH=ppc" >> $config_mak
|
||||
echo "#define HOST_PPC 1" >> $config_h
|
||||
elif test "$cpu" = "mips" ; then
|
||||
echo "ARCH=mips" >> $config_mak
|
||||
echo "#define HOST_MIPS 1" >> $config_h
|
||||
elif test "$cpu" = "s390" ; then
|
||||
echo "ARCH=s390" >> $config_mak
|
||||
echo "#define HOST_S390 1" >> $config_h
|
||||
elif test "$cpu" = "alpha" ; then
|
||||
echo "ARCH=alpha" >> $config_mak
|
||||
echo "#define HOST_ALPHA 1" >> $config_h
|
||||
elif test "$cpu" = "sparc" ; then
|
||||
echo "ARCH=sparc" >> $config_mak
|
||||
echo "#define HOST_SPARC 1" >> $config_h
|
||||
elif test "$cpu" = "sparc64" ; then
|
||||
echo "ARCH=sparc64" >> $config_mak
|
||||
echo "#define HOST_SPARC64 1" >> $config_h
|
||||
elif test "$cpu" = "ia64" ; then
|
||||
echo "ARCH=ia64" >> $config_mak
|
||||
echo "#define HOST_IA64 1" >> $config_h
|
||||
elif test "$cpu" = "m68k" ; then
|
||||
echo "ARCH=m68k" >> $config_mak
|
||||
echo "#define HOST_M68K 1" >> $config_h
|
||||
else
|
||||
echo "Unsupported CPU"
|
||||
exit 1
|
||||
fi
|
||||
if test "$bigendian" = "yes" ; then
|
||||
echo "WORDS_BIGENDIAN=yes" >> $config_mak
|
||||
echo "#define WORDS_BIGENDIAN 1" >> $config_h
|
||||
fi
|
||||
echo "#define HOST_LONG_BITS $hostlongbits" >> $config_h
|
||||
if test "$mingw32" = "yes" ; then
|
||||
echo "CONFIG_WIN32=yes" >> $config_mak
|
||||
echo "#define CONFIG_WIN32 1" >> $config_h
|
||||
elif test -f "/usr/include/byteswap.h" ; then
|
||||
echo "#define HAVE_BYTESWAP_H 1" >> $config_h
|
||||
fi
|
||||
if test "$darwin" = "yes" ; then
|
||||
echo "CONFIG_DARWIN=yes" >> $config_mak
|
||||
echo "#define CONFIG_DARWIN 1" >> $config_h
|
||||
fi
|
||||
if test "$solaris" = "yes" ; then
|
||||
echo "CONFIG_SOLARIS=yes" >> $config_mak
|
||||
echo "#define HOST_SOLARIS $solarisrev" >> $config_h
|
||||
fi
|
||||
if test "$gdbstub" = "yes" ; then
|
||||
echo "CONFIG_GDBSTUB=yes" >> $config_mak
|
||||
echo "#define CONFIG_GDBSTUB 1" >> $config_h
|
||||
fi
|
||||
if test "$gprof" = "yes" ; then
|
||||
echo "TARGET_GPROF=yes" >> $config_mak
|
||||
echo "#define HAVE_GPROF 1" >> $config_h
|
||||
fi
|
||||
if test "$static" = "yes" ; then
|
||||
echo "CONFIG_STATIC=yes" >> $config_mak
|
||||
echo "#define CONFIG_STATIC 1" >> $config_h
|
||||
fi
|
||||
if test $profiler = "yes" ; then
|
||||
echo "#define CONFIG_PROFILER 1" >> $config_h
|
||||
fi
|
||||
if test "$slirp" = "yes" ; then
|
||||
echo "CONFIG_SLIRP=yes" >> $config_mak
|
||||
echo "#define CONFIG_SLIRP 1" >> $config_h
|
||||
fi
|
||||
if test "$adlib" = "yes" ; then
|
||||
echo "CONFIG_ADLIB=yes" >> $config_mak
|
||||
echo "#define CONFIG_ADLIB 1" >> $config_h
|
||||
fi
|
||||
if test "$oss" = "yes" ; then
|
||||
echo "CONFIG_OSS=yes" >> $config_mak
|
||||
echo "#define CONFIG_OSS 1" >> $config_h
|
||||
fi
|
||||
if test "$coreaudio" = "yes" ; then
|
||||
echo "CONFIG_COREAUDIO=yes" >> $config_mak
|
||||
echo "#define CONFIG_COREAUDIO 1" >> $config_h
|
||||
fi
|
||||
if test "$alsa" = "yes" ; then
|
||||
echo "CONFIG_ALSA=yes" >> $config_mak
|
||||
echo "#define CONFIG_ALSA 1" >> $config_h
|
||||
fi
|
||||
if test "$dsound" = "yes" ; then
|
||||
echo "CONFIG_DSOUND=yes" >> $config_mak
|
||||
echo "#define CONFIG_DSOUND 1" >> $config_h
|
||||
fi
|
||||
if test "$fmod" = "yes" ; then
|
||||
echo "CONFIG_FMOD=yes" >> $config_mak
|
||||
echo "CONFIG_FMOD_LIB=$fmod_lib" >> $config_mak
|
||||
echo "CONFIG_FMOD_INC=$fmod_inc" >> $config_mak
|
||||
echo "#define CONFIG_FMOD 1" >> $config_h
|
||||
fi
|
||||
qemu_version=`head $source_path/VERSION`
|
||||
echo "VERSION=$qemu_version" >>$config_mak
|
||||
echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
|
||||
|
||||
echo "SRC_PATH=$source_path" >> $config_mak
|
||||
if [ "$source_path_used" = "yes" ]; then
|
||||
echo "VPATH=$source_path" >> $config_mak
|
||||
fi
|
||||
echo "TARGET_DIRS=$target_list" >> $config_mak
|
||||
if [ "$build_docs" = "yes" ] ; then
|
||||
echo "BUILD_DOCS=yes" >> $config_mak
|
||||
fi
|
||||
|
||||
# XXX: suppress that
|
||||
if [ "$bsd" = "yes" ] ; then
|
||||
echo "#define O_LARGEFILE 0" >> $config_h
|
||||
echo "#define MAP_ANONYMOUS MAP_ANON" >> $config_h
|
||||
echo "#define _BSD 1" >> $config_h
|
||||
fi
|
||||
|
||||
echo "#define CONFIG_UNAME_RELEASE \"$uname_release\"" >> $config_h
|
||||
|
||||
for target in $target_list; do
|
||||
target_dir="$target"
|
||||
config_mak=$target_dir/config.mak
|
||||
config_h=$target_dir/config.h
|
||||
target_cpu=`echo $target | cut -d '-' -f 1`
|
||||
target_bigendian="no"
|
||||
[ "$target_cpu" = "armeb" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "sparc" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "sparc64" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "ppc" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "ppc64" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "mips" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "sh4eb" ] && target_bigendian=yes
|
||||
[ "$target_cpu" = "m68k" ] && target_bigendian=yes
|
||||
target_softmmu="no"
|
||||
if expr $target : '.*-softmmu' > /dev/null ; then
|
||||
target_softmmu="yes"
|
||||
fi
|
||||
target_user_only="no"
|
||||
if expr $target : '.*-user' > /dev/null ; then
|
||||
target_user_only="yes"
|
||||
fi
|
||||
|
||||
target_linux_user="no"
|
||||
if expr $target : '.*-linux-user' > /dev/null ; then
|
||||
target_linux_user="yes"
|
||||
fi
|
||||
|
||||
target_darwin_user="no"
|
||||
if expr $target : '.*-darwin-user' > /dev/null ; then
|
||||
target_darwin_user="yes"
|
||||
fi
|
||||
|
||||
if test "$target_user_only" = "no" -a "$check_gfx" = "yes" \
|
||||
-a "$sdl" = "no" -a "$cocoa" = "no" ; then
|
||||
echo "ERROR: QEMU requires SDL or Cocoa for graphical output"
|
||||
echo "To build QEMU without graphical output configure with --disable-gfx-check"
|
||||
echo "Note that this will disable all output from the virtual graphics card."
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
#echo "Creating $config_mak, $config_h and $target_dir/Makefile"
|
||||
|
||||
mkdir -p $target_dir
|
||||
mkdir -p $target_dir/fpu
|
||||
if test "$target" = "arm-linux-user" -o "$target" = "armeb-linux-user" ; then
|
||||
mkdir -p $target_dir/nwfpe
|
||||
fi
|
||||
if test "$target_user_only" = "no" ; then
|
||||
mkdir -p $target_dir/slirp
|
||||
fi
|
||||
|
||||
#
|
||||
# don't use ln -sf as not all "ln -sf" over write the file/link
|
||||
#
|
||||
rm -f $target_dir/Makefile
|
||||
ln -s $source_path/Makefile.target $target_dir/Makefile
|
||||
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > $config_mak
|
||||
echo "/* Automatically generated by configure - do not modify */" > $config_h
|
||||
|
||||
|
||||
echo "include ../config-host.mak" >> $config_mak
|
||||
echo "#include \"../config-host.h\"" >> $config_h
|
||||
|
||||
bflt="no"
|
||||
interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"`
|
||||
echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h
|
||||
|
||||
if test "$target_cpu" = "i386" ; then
|
||||
echo "TARGET_ARCH=i386" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"i386\"" >> $config_h
|
||||
echo "#define TARGET_I386 1" >> $config_h
|
||||
if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "i386" ; then
|
||||
echo "#define USE_KQEMU 1" >> $config_h
|
||||
fi
|
||||
elif test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then
|
||||
echo "TARGET_ARCH=arm" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"arm\"" >> $config_h
|
||||
echo "#define TARGET_ARM 1" >> $config_h
|
||||
bflt="yes"
|
||||
elif test "$target_cpu" = "sparc" ; then
|
||||
echo "TARGET_ARCH=sparc" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"sparc\"" >> $config_h
|
||||
echo "#define TARGET_SPARC 1" >> $config_h
|
||||
elif test "$target_cpu" = "sparc64" ; then
|
||||
echo "TARGET_ARCH=sparc64" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"sparc64\"" >> $config_h
|
||||
echo "#define TARGET_SPARC 1" >> $config_h
|
||||
echo "#define TARGET_SPARC64 1" >> $config_h
|
||||
elif test "$target_cpu" = "ppc" ; then
|
||||
echo "TARGET_ARCH=ppc" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"ppc\"" >> $config_h
|
||||
echo "#define TARGET_PPC 1" >> $config_h
|
||||
elif test "$target_cpu" = "ppc64" ; then
|
||||
echo "TARGET_ARCH=ppc64" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"ppc64\"" >> $config_h
|
||||
echo "#define TARGET_PPC 1" >> $config_h
|
||||
echo "#define TARGET_PPC64 1" >> $config_h
|
||||
elif test "$target_cpu" = "x86_64" ; then
|
||||
echo "TARGET_ARCH=x86_64" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"x86_64\"" >> $config_h
|
||||
echo "#define TARGET_I386 1" >> $config_h
|
||||
echo "#define TARGET_X86_64 1" >> $config_h
|
||||
if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "x86_64" ; then
|
||||
echo "#define USE_KQEMU 1" >> $config_h
|
||||
fi
|
||||
elif test "$target_cpu" = "mips" -o "$target_cpu" = "mipsel" ; then
|
||||
echo "TARGET_ARCH=mips" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"mips\"" >> $config_h
|
||||
echo "#define TARGET_MIPS 1" >> $config_h
|
||||
echo "CONFIG_SOFTFLOAT=yes" >> $config_mak
|
||||
echo "#define CONFIG_SOFTFLOAT 1" >> $config_h
|
||||
elif test "$target_cpu" = "sh4" -o "$target_cpu" = "sh4eb" ; then
|
||||
echo "TARGET_ARCH=sh4" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"sh4\"" >> $config_h
|
||||
echo "#define TARGET_SH4 1" >> $config_h
|
||||
bflt="yes"
|
||||
elif test "$target_cpu" = "m68k" ; then
|
||||
echo "TARGET_ARCH=m68k" >> $config_mak
|
||||
echo "#define TARGET_ARCH \"m68k\"" >> $config_h
|
||||
echo "#define TARGET_M68K 1" >> $config_h
|
||||
bflt="yes"
|
||||
else
|
||||
echo "Unsupported target CPU"
|
||||
exit 1
|
||||
fi
|
||||
if test "$target_bigendian" = "yes" ; then
|
||||
echo "TARGET_WORDS_BIGENDIAN=yes" >> $config_mak
|
||||
echo "#define TARGET_WORDS_BIGENDIAN 1" >> $config_h
|
||||
fi
|
||||
if test "$target_softmmu" = "yes" ; then
|
||||
echo "CONFIG_SOFTMMU=yes" >> $config_mak
|
||||
echo "#define CONFIG_SOFTMMU 1" >> $config_h
|
||||
fi
|
||||
if test "$target_user_only" = "yes" ; then
|
||||
echo "CONFIG_USER_ONLY=yes" >> $config_mak
|
||||
echo "#define CONFIG_USER_ONLY 1" >> $config_h
|
||||
fi
|
||||
if test "$target_linux_user" = "yes" ; then
|
||||
echo "CONFIG_LINUX_USER=yes" >> $config_mak
|
||||
echo "#define CONFIG_LINUX_USER 1" >> $config_h
|
||||
fi
|
||||
if test "$target_darwin_user" = "yes" ; then
|
||||
echo "CONFIG_DARWIN_USER=yes" >> $config_mak
|
||||
echo "#define CONFIG_DARWIN_USER 1" >> $config_h
|
||||
fi
|
||||
|
||||
if test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" -o "$target_cpu" = "sparc" -o "$target_cpu" = "sparc64" -o "$target_cpu" = "m68k"; then
|
||||
echo "CONFIG_SOFTFLOAT=yes" >> $config_mak
|
||||
echo "#define CONFIG_SOFTFLOAT 1" >> $config_h
|
||||
fi
|
||||
if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then
|
||||
echo "TARGET_HAS_BFLT=yes" >> $config_mak
|
||||
echo "#define TARGET_HAS_BFLT 1" >> $config_h
|
||||
fi
|
||||
# sdl defines
|
||||
|
||||
if test "$target_user_only" = "no"; then
|
||||
if test "$target_softmmu" = "no" -o "$static" = "yes"; then
|
||||
sdl1=$sdl_static
|
||||
else
|
||||
sdl1=$sdl
|
||||
fi
|
||||
if test "$sdl1" = "yes" ; then
|
||||
echo "#define CONFIG_SDL 1" >> $config_h
|
||||
echo "CONFIG_SDL=yes" >> $config_mak
|
||||
if test "$target_softmmu" = "no" -o "$static" = "yes"; then
|
||||
echo "SDL_LIBS=$sdl_static_libs" >> $config_mak
|
||||
else
|
||||
echo "SDL_LIBS=`$sdl_config --libs`" >> $config_mak
|
||||
fi
|
||||
if [ "${aa}" = "yes" ] ; then
|
||||
echo "SDL_CFLAGS=`$sdl_config --cflags` `aalib-config --cflags`" >> $config_mak
|
||||
else
|
||||
echo "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$cocoa" = "yes" ; then
|
||||
echo "#define CONFIG_COCOA 1" >> $config_h
|
||||
echo "CONFIG_COCOA=yes" >> $config_mak
|
||||
fi
|
||||
|
||||
done # for target in $targets
|
||||
|
||||
# build tree in object directory if source path is different from current one
|
||||
if test "$source_path_used" = "yes" ; then
|
||||
DIRS="tests"
|
||||
FILES="Makefile tests/Makefile"
|
||||
for dir in $DIRS ; do
|
||||
mkdir -p $dir
|
||||
done
|
||||
# remove the link and recreate it, as not all "ln -sf" overwrite the link
|
||||
for f in $FILES ; do
|
||||
rm -f $f
|
||||
ln -s $source_path/$f $f
|
||||
done
|
||||
fi
|
||||
|
||||
rm -f $TMPO $TMPC $TMPE $TMPS
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* common defines for all CPUs
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef CPU_DEFS_H
|
||||
#define CPU_DEFS_H
|
||||
|
||||
#include "config.h"
|
||||
#include <setjmp.h>
|
||||
#include <inttypes.h>
|
||||
#include "osdep.h"
|
||||
|
||||
#ifndef TARGET_LONG_BITS
|
||||
#error TARGET_LONG_BITS must be defined before including this header
|
||||
#endif
|
||||
|
||||
#ifndef TARGET_PHYS_ADDR_BITS
|
||||
#if TARGET_LONG_BITS >= HOST_LONG_BITS
|
||||
#define TARGET_PHYS_ADDR_BITS TARGET_LONG_BITS
|
||||
#else
|
||||
#define TARGET_PHYS_ADDR_BITS HOST_LONG_BITS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
|
||||
|
||||
/* target_ulong is the type of a virtual address */
|
||||
#if TARGET_LONG_SIZE == 4
|
||||
typedef int32_t target_long;
|
||||
typedef uint32_t target_ulong;
|
||||
#define TARGET_FMT_lx "%08x"
|
||||
#elif TARGET_LONG_SIZE == 8
|
||||
typedef int64_t target_long;
|
||||
typedef uint64_t target_ulong;
|
||||
#define TARGET_FMT_lx "%016" PRIx64
|
||||
#else
|
||||
#error TARGET_LONG_SIZE undefined
|
||||
#endif
|
||||
|
||||
/* target_phys_addr_t is the type of a physical address (its size can
|
||||
be different from 'target_ulong'). We have sizeof(target_phys_addr)
|
||||
= max(sizeof(unsigned long),
|
||||
sizeof(size_of_target_physical_address)) because we must pass a
|
||||
host pointer to memory operations in some cases */
|
||||
|
||||
#if TARGET_PHYS_ADDR_BITS == 32
|
||||
typedef uint32_t target_phys_addr_t;
|
||||
#elif TARGET_PHYS_ADDR_BITS == 64
|
||||
typedef uint64_t target_phys_addr_t;
|
||||
#else
|
||||
#error TARGET_PHYS_ADDR_BITS undefined
|
||||
#endif
|
||||
|
||||
/* address in the RAM (different from a physical address) */
|
||||
typedef unsigned long ram_addr_t;
|
||||
|
||||
#define HOST_LONG_SIZE (HOST_LONG_BITS / 8)
|
||||
|
||||
#define EXCP_INTERRUPT 0x10000 /* async interruption */
|
||||
#define EXCP_HLT 0x10001 /* hlt instruction reached */
|
||||
#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
|
||||
#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */
|
||||
#define MAX_BREAKPOINTS 32
|
||||
|
||||
#define TB_JMP_CACHE_BITS 12
|
||||
#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
|
||||
|
||||
/* Only the bottom TB_JMP_PAGE_BITS of the jump cache hash bits vary for
|
||||
addresses on the same page. The top bits are the same. This allows
|
||||
TLB invalidation to quickly clear a subset of the hash table. */
|
||||
#define TB_JMP_PAGE_BITS (TB_JMP_CACHE_BITS / 2)
|
||||
#define TB_JMP_PAGE_SIZE (1 << TB_JMP_PAGE_BITS)
|
||||
#define TB_JMP_ADDR_MASK (TB_JMP_PAGE_SIZE - 1)
|
||||
#define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE)
|
||||
|
||||
#define CPU_TLB_BITS 8
|
||||
#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
|
||||
|
||||
typedef struct CPUTLBEntry {
|
||||
/* bit 31 to TARGET_PAGE_BITS : virtual address
|
||||
bit TARGET_PAGE_BITS-1..IO_MEM_SHIFT : if non zero, memory io
|
||||
zone number
|
||||
bit 3 : indicates that the entry is invalid
|
||||
bit 2..0 : zero
|
||||
*/
|
||||
target_ulong addr_read;
|
||||
target_ulong addr_write;
|
||||
target_ulong addr_code;
|
||||
/* addend to virtual address to get physical address */
|
||||
target_phys_addr_t addend;
|
||||
} CPUTLBEntry;
|
||||
|
||||
#define CPU_COMMON \
|
||||
struct TranslationBlock *current_tb; /* currently executing TB */ \
|
||||
/* soft mmu support */ \
|
||||
/* in order to avoid passing too many arguments to the memory \
|
||||
write helpers, we store some rarely used information in the CPU \
|
||||
context) */ \
|
||||
unsigned long mem_write_pc; /* host pc at which the memory was \
|
||||
written */ \
|
||||
target_ulong mem_write_vaddr; /* target virtual addr at which the \
|
||||
memory was written */ \
|
||||
/* 0 = kernel, 1 = user */ \
|
||||
CPUTLBEntry tlb_table[2][CPU_TLB_SIZE]; \
|
||||
struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE]; \
|
||||
\
|
||||
/* from this point: preserved by CPU reset */ \
|
||||
/* ice debug support */ \
|
||||
target_ulong breakpoints[MAX_BREAKPOINTS]; \
|
||||
int nb_breakpoints; \
|
||||
int singlestep_enabled; \
|
||||
\
|
||||
void *next_cpu; /* next CPU sharing TB cache */ \
|
||||
int cpu_index; /* CPU index (informative) */ \
|
||||
/* user data */ \
|
||||
void *opaque;
|
||||
|
||||
#endif
|
||||
+1487
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Simple C functions to supplement the C library
|
||||
*
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
*
|
||||
* 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 "vl.h"
|
||||
|
||||
void pstrcpy(char *buf, int buf_size, const char *str)
|
||||
{
|
||||
int c;
|
||||
char *q = buf;
|
||||
|
||||
if (buf_size <= 0)
|
||||
return;
|
||||
|
||||
for(;;) {
|
||||
c = *str++;
|
||||
if (c == 0 || q >= buf + buf_size - 1)
|
||||
break;
|
||||
*q++ = c;
|
||||
}
|
||||
*q = '\0';
|
||||
}
|
||||
|
||||
/* strcat and truncate. */
|
||||
char *pstrcat(char *buf, int buf_size, const char *s)
|
||||
{
|
||||
int len;
|
||||
len = strlen(buf);
|
||||
if (len < buf_size)
|
||||
pstrcpy(buf + len, buf_size - len, s);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int strstart(const char *str, const char *val, const char **ptr)
|
||||
{
|
||||
const char *p, *q;
|
||||
p = str;
|
||||
q = val;
|
||||
while (*q != '\0') {
|
||||
if (*p != *q)
|
||||
return 0;
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if (ptr)
|
||||
*ptr = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int stristart(const char *str, const char *val, const char **ptr)
|
||||
{
|
||||
const char *p, *q;
|
||||
p = str;
|
||||
q = val;
|
||||
while (*q != '\0') {
|
||||
if (toupper(*p) != toupper(*q))
|
||||
return 0;
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if (ptr)
|
||||
*ptr = p;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Commpage syscalls
|
||||
*
|
||||
* Copyright (c) 2006 Pierre d'Herbemont
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <mach/message.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <libkern/OSAtomic.h>
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
//#define DEBUG_COMMPAGE
|
||||
|
||||
#ifdef DEBUG_COMMPAGE
|
||||
# define DPRINTF(...) do { if(loglevel) fprintf(logfile, __VA_ARGS__); printf(__VA_ARGS__); } while(0)
|
||||
#else
|
||||
# define DPRINTF(...) do { if(loglevel) fprintf(logfile, __VA_ARGS__); } while(0)
|
||||
#endif
|
||||
|
||||
/********************************************************************
|
||||
* Commpage definitions
|
||||
*/
|
||||
#ifdef TARGET_I386
|
||||
/* Reserve space for the commpage see xnu/osfmk/i386/cpu_capabilities.h */
|
||||
# define COMMPAGE_START (-16 * 4096) /* base address is -20 * 4096 */
|
||||
# define COMMPAGE_SIZE (0x1240) /* _COMM_PAGE_AREA_LENGTH is 19 * 4096 */
|
||||
#elif defined(TARGET_PPC)
|
||||
/* Reserve space for the commpage see xnu/osfmk/ppc/cpu_capabilities.h */
|
||||
# define COMMPAGE_START (-8*4096)
|
||||
# define COMMPAGE_SIZE (2*4096) /* its _COMM_PAGE_AREA_USED but _COMM_PAGE_AREA_LENGTH is 7*4096 */
|
||||
#endif
|
||||
|
||||
void do_compare_and_swap32(void *cpu_env, int num);
|
||||
void do_compare_and_swap64(void *cpu_env, int num);
|
||||
void do_add_atomic_word32(void *cpu_env, int num);
|
||||
void do_cgettimeofday(void *cpu_env, int num, uint32_t arg1);
|
||||
void do_nanotime(void *cpu_env, int num);
|
||||
|
||||
void unimpl_commpage(void *cpu_env, int num);
|
||||
|
||||
typedef void (*commpage_8args_function_t)(uint32_t arg1, uint32_t arg2, uint32_t arg3,
|
||||
uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7,
|
||||
uint32_t arg8);
|
||||
typedef void (*commpage_indirect_function_t)(void *cpu_env, int num, uint32_t arg1,
|
||||
uint32_t arg2, uint32_t arg3, uint32_t arg4, uint32_t arg5,
|
||||
uint32_t arg6, uint32_t arg7, uint32_t arg8);
|
||||
|
||||
#define HAS_PTR 0x10
|
||||
#define NO_PTR 0x20
|
||||
#define CALL_DIRECT 0x1
|
||||
#define CALL_INDIRECT 0x2
|
||||
|
||||
#define COMMPAGE_ENTRY(name, nargs, offset, func, options) \
|
||||
{ #name, offset, nargs, options, (commpage_8args_function_t)func }
|
||||
|
||||
struct commpage_entry {
|
||||
char * name;
|
||||
int offset;
|
||||
int nargs;
|
||||
char options;
|
||||
commpage_8args_function_t function;
|
||||
};
|
||||
|
||||
static inline int commpage_code_num(struct commpage_entry *entry)
|
||||
{
|
||||
if((entry->options & HAS_PTR))
|
||||
return entry->offset + 4;
|
||||
else
|
||||
return entry->offset;
|
||||
}
|
||||
|
||||
static inline int commpage_is_indirect(struct commpage_entry *entry)
|
||||
{
|
||||
return !(entry->options & CALL_DIRECT);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Commpage entry
|
||||
*/
|
||||
static struct commpage_entry commpage_entries[] =
|
||||
{
|
||||
COMMPAGE_ENTRY(compare_and_swap32, 0, 0x080, do_compare_and_swap32, CALL_INDIRECT | HAS_PTR),
|
||||
COMMPAGE_ENTRY(compare_and_swap64, 0, 0x0c0, do_compare_and_swap64, CALL_INDIRECT | HAS_PTR),
|
||||
COMMPAGE_ENTRY(enqueue, 0, 0x100, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(dequeue, 0, 0x140, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(memory_barrier, 0, 0x180, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(add_atomic_word32, 0, 0x1a0, do_add_atomic_word32, CALL_INDIRECT | HAS_PTR),
|
||||
COMMPAGE_ENTRY(add_atomic_word64, 0, 0x1c0, unimpl_commpage, CALL_INDIRECT | HAS_PTR),
|
||||
|
||||
COMMPAGE_ENTRY(mach_absolute_time, 0, 0x200, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(spinlock_try, 1, 0x220, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(spinlock_lock, 1, 0x260, OSSpinLockLock, CALL_DIRECT),
|
||||
COMMPAGE_ENTRY(spinlock_unlock, 1, 0x2a0, OSSpinLockUnlock, CALL_DIRECT),
|
||||
COMMPAGE_ENTRY(pthread_getspecific, 0, 0x2c0, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(gettimeofday, 1, 0x2e0, do_cgettimeofday, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(sys_dcache_flush, 0, 0x4e0, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(sys_icache_invalidate, 0, 0x520, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(pthread_self, 0, 0x580, unimpl_commpage, CALL_INDIRECT),
|
||||
|
||||
COMMPAGE_ENTRY(relinquish, 0, 0x5c0, unimpl_commpage, CALL_INDIRECT),
|
||||
|
||||
#ifdef TARGET_I386
|
||||
COMMPAGE_ENTRY(bts, 0, 0x5e0, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(btc, 0, 0x5f0, unimpl_commpage, CALL_INDIRECT),
|
||||
#endif
|
||||
|
||||
COMMPAGE_ENTRY(bzero, 2, 0x600, bzero, CALL_DIRECT),
|
||||
COMMPAGE_ENTRY(bcopy, 3, 0x780, bcopy, CALL_DIRECT),
|
||||
COMMPAGE_ENTRY(memcpy, 3, 0x7a0, memcpy, CALL_DIRECT),
|
||||
|
||||
#ifdef TARGET_I386
|
||||
COMMPAGE_ENTRY(old_nanotime, 0, 0xf80, do_nanotime, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(memset_pattern, 0, 0xf80, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(long_copy, 0, 0x1200, unimpl_commpage, CALL_INDIRECT),
|
||||
|
||||
COMMPAGE_ENTRY(sysintegrity, 0, 0x1600, unimpl_commpage, CALL_INDIRECT),
|
||||
|
||||
COMMPAGE_ENTRY(nanotime, 0, 0x1700, do_nanotime, CALL_INDIRECT),
|
||||
#elif TARGET_PPC
|
||||
COMMPAGE_ENTRY(compare_and_swap32b, 0, 0xf80, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(compare_and_swap64b, 0, 0xfc0, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(memset_pattern, 0, 0x1000, unimpl_commpage, CALL_INDIRECT),
|
||||
COMMPAGE_ENTRY(bigcopy, 0, 0x1140, unimpl_commpage, CALL_INDIRECT),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Commpage backdoor
|
||||
*/
|
||||
static inline void print_commpage_entry(struct commpage_entry entry)
|
||||
{
|
||||
printf("@0x%x %s\n", entry.offset, entry.name);
|
||||
}
|
||||
|
||||
static inline void install_commpage_backdoor_for_entry(struct commpage_entry entry)
|
||||
{
|
||||
#ifdef TARGET_I386
|
||||
char * commpage = (char*)(COMMPAGE_START+entry.offset);
|
||||
int c = 0;
|
||||
if(entry.options & HAS_PTR)
|
||||
{
|
||||
commpage[c++] = (COMMPAGE_START+entry.offset+4) & 0xff;
|
||||
commpage[c++] = ((COMMPAGE_START+entry.offset+4) >> 8) & 0xff;
|
||||
commpage[c++] = ((COMMPAGE_START+entry.offset+4) >> 16) & 0xff;
|
||||
commpage[c++] = ((COMMPAGE_START+entry.offset+4) >> 24) & 0xff;
|
||||
}
|
||||
commpage[c++] = 0xcd;
|
||||
commpage[c++] = 0x79; /* int 0x79 */
|
||||
commpage[c++] = 0xc3; /* ret */
|
||||
#else
|
||||
qerror("can't install the commpage on this arch\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Commpage initialization
|
||||
*/
|
||||
void commpage_init(void)
|
||||
{
|
||||
#if (defined(__i386__) ^ defined(TARGET_I386)) || (defined(__powerpc__) ^ defined(TARGET_PPC))
|
||||
int i;
|
||||
void * commpage = (void *)target_mmap( COMMPAGE_START, COMMPAGE_SIZE,
|
||||
PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
if((int)commpage != COMMPAGE_START)
|
||||
qerror("can't allocate the commpage\n");
|
||||
|
||||
bzero(commpage, COMMPAGE_SIZE);
|
||||
|
||||
/* XXX: commpage data not handled */
|
||||
|
||||
for(i = 0; i < sizeof(commpage_entries)/sizeof(commpage_entries[0]); i++)
|
||||
install_commpage_backdoor_for_entry(commpage_entries[i]);
|
||||
#else
|
||||
/* simply map our pages so they can be executed
|
||||
XXX: we don't really want to do that since in the ppc on ppc situation we may
|
||||
not able to run commpages host optimized instructions (like G5's on a G5),
|
||||
hence this is sometimes a broken fix. */
|
||||
page_set_flags(COMMPAGE_START, COMMPAGE_START+COMMPAGE_SIZE, PROT_EXEC | PROT_READ | PAGE_VALID);
|
||||
#endif
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Commpage implementation
|
||||
*/
|
||||
void do_compare_and_swap32(void *cpu_env, int num)
|
||||
{
|
||||
#ifdef TARGET_I386
|
||||
uint32_t old = ((CPUX86State*)cpu_env)->regs[R_EAX];
|
||||
uint32_t *value = (uint32_t*)((CPUX86State*)cpu_env)->regs[R_ECX];
|
||||
DPRINTF("commpage: compare_and_swap32(%x,new,%p)\n", old, value);
|
||||
|
||||
if(value && old == tswap32(*value))
|
||||
{
|
||||
uint32_t new = ((CPUX86State*)cpu_env)->regs[R_EDX];
|
||||
*value = tswap32(new);
|
||||
/* set zf flag */
|
||||
((CPUX86State*)cpu_env)->eflags |= 0x40;
|
||||
}
|
||||
else
|
||||
{
|
||||
((CPUX86State*)cpu_env)->regs[R_EAX] = tswap32(*value);
|
||||
/* unset zf flag */
|
||||
((CPUX86State*)cpu_env)->eflags &= ~0x40;
|
||||
}
|
||||
#else
|
||||
qerror("do_compare_and_swap32 unimplemented");
|
||||
#endif
|
||||
}
|
||||
|
||||
void do_compare_and_swap64(void *cpu_env, int num)
|
||||
{
|
||||
#ifdef TARGET_I386
|
||||
/* OSAtomicCompareAndSwap64 is not available on non 64 bits ppc, here is a raw implementation */
|
||||
uint64_t old, new, swapped_val;
|
||||
uint64_t *value = (uint64_t*)((CPUX86State*)cpu_env)->regs[R_ESI];
|
||||
old = (uint64_t)((uint64_t)((CPUX86State*)cpu_env)->regs[R_EDX]) << 32 | (uint64_t)((CPUX86State*)cpu_env)->regs[R_EAX];
|
||||
|
||||
DPRINTF("commpage: compare_and_swap64(%llx,new,%p)\n", old, value);
|
||||
swapped_val = tswap64(*value);
|
||||
|
||||
if(old == swapped_val)
|
||||
{
|
||||
new = (uint64_t)((uint64_t)((CPUX86State*)cpu_env)->regs[R_ECX]) << 32 | (uint64_t)((CPUX86State*)cpu_env)->regs[R_EBX];
|
||||
*value = tswap64(new);
|
||||
/* set zf flag */
|
||||
((CPUX86State*)cpu_env)->eflags |= 0x40;
|
||||
}
|
||||
else
|
||||
{
|
||||
((CPUX86State*)cpu_env)->regs[R_EAX] = (uint32_t)(swapped_val);
|
||||
((CPUX86State*)cpu_env)->regs[R_EDX] = (uint32_t)(swapped_val >> 32);
|
||||
/* unset zf flag */
|
||||
((CPUX86State*)cpu_env)->eflags &= ~0x40;
|
||||
}
|
||||
#else
|
||||
qerror("do_compare_and_swap64 unimplemented");
|
||||
#endif
|
||||
}
|
||||
|
||||
void do_add_atomic_word32(void *cpu_env, int num)
|
||||
{
|
||||
#ifdef TARGET_I386
|
||||
uint32_t amt = ((CPUX86State*)cpu_env)->regs[R_EAX];
|
||||
uint32_t *value = (uint32_t*)((CPUX86State*)cpu_env)->regs[R_EDX];
|
||||
uint32_t swapped_value = tswap32(*value);
|
||||
|
||||
DPRINTF("commpage: add_atomic_word32(%x,%p)\n", amt, value);
|
||||
|
||||
/* old value in EAX */
|
||||
((CPUX86State*)cpu_env)->regs[R_EAX] = swapped_value;
|
||||
*value = tswap32(swapped_value + amt);
|
||||
#else
|
||||
qerror("do_add_atomic_word32 unimplemented");
|
||||
#endif
|
||||
}
|
||||
|
||||
void do_cgettimeofday(void *cpu_env, int num, uint32_t arg1)
|
||||
{
|
||||
#ifdef TARGET_I386
|
||||
extern int __commpage_gettimeofday(struct timeval *);
|
||||
DPRINTF("commpage: gettimeofday(0x%x)\n", arg1);
|
||||
struct timeval *time = (struct timeval *)arg1;
|
||||
int ret = __commpage_gettimeofday(time);
|
||||
tswap32s((uint32_t*)&time->tv_sec);
|
||||
tswap32s((uint32_t*)&time->tv_usec);
|
||||
((CPUX86State*)cpu_env)->regs[R_EAX] = ret; /* Success */
|
||||
#else
|
||||
qerror("do_gettimeofday unimplemented");
|
||||
#endif
|
||||
}
|
||||
|
||||
void do_nanotime(void *cpu_env, int num)
|
||||
{
|
||||
#ifdef TARGET_I386
|
||||
uint64_t t = mach_absolute_time();
|
||||
((CPUX86State*)cpu_env)->regs[R_EAX] = (int)(t & 0xffffffff);
|
||||
((CPUX86State*)cpu_env)->regs[R_EDX] = (int)((t >> 32) & 0xffffffff);
|
||||
#else
|
||||
qerror("do_nanotime unimplemented");
|
||||
#endif
|
||||
}
|
||||
|
||||
void unimpl_commpage(void *cpu_env, int num)
|
||||
{
|
||||
qerror("qemu: commpage function 0x%x not implemented\n", num);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* do_commpage - called by the main cpu loop
|
||||
*/
|
||||
void
|
||||
do_commpage(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
|
||||
uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7,
|
||||
uint32_t arg8)
|
||||
{
|
||||
int i, found = 0;
|
||||
|
||||
arg1 = tswap32(arg1);
|
||||
arg2 = tswap32(arg2);
|
||||
arg3 = tswap32(arg3);
|
||||
arg4 = tswap32(arg4);
|
||||
arg5 = tswap32(arg5);
|
||||
arg6 = tswap32(arg6);
|
||||
arg7 = tswap32(arg7);
|
||||
arg8 = tswap32(arg8);
|
||||
|
||||
num = num-COMMPAGE_START-2;
|
||||
|
||||
for(i = 0; i < sizeof(commpage_entries)/sizeof(commpage_entries[0]); i++) {
|
||||
if( num == commpage_code_num(&commpage_entries[i]) )
|
||||
{
|
||||
DPRINTF("commpage: %s %s\n", commpage_entries[i].name, commpage_is_indirect(&commpage_entries[i]) ? "[indirect]" : "[direct]");
|
||||
found = 1;
|
||||
if(commpage_is_indirect(&commpage_entries[i]))
|
||||
{
|
||||
commpage_indirect_function_t function = (commpage_indirect_function_t)commpage_entries[i].function;
|
||||
function(cpu_env, num, arg1, arg2, arg3,
|
||||
arg4, arg5, arg6, arg7, arg8);
|
||||
}
|
||||
else
|
||||
{
|
||||
commpage_entries[i].function(arg1, arg2, arg3,
|
||||
arg4, arg5, arg6, arg7, arg8);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
gemu_log("qemu: commpage function 0x%x not defined\n", num);
|
||||
gdb_handlesig (cpu_env, SIGTRAP);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* emulated ioctl list */
|
||||
|
||||
IOCTL(TIOCGETA, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
|
||||
IOCTL(TIOCSETA, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
|
||||
@@ -0,0 +1 @@
|
||||
STRUCT(termios, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, MK_ARRAY(TYPE_CHAR, 20), TYPE_INT, TYPE_INT)
|
||||
@@ -0,0 +1,903 @@
|
||||
/*
|
||||
* Mach-O object file loading
|
||||
*
|
||||
* Copyright (c) 2006 Pierre d'Herbemont
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qemu.h"
|
||||
#include "disas.h"
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <mach-o/reloc.h>
|
||||
#include <mach-o/ppc/reloc.h>
|
||||
|
||||
//#define DEBUG_MACHLOAD
|
||||
|
||||
#ifdef DEBUG_MACHLOAD
|
||||
# define DPRINTF(...) do { if(loglevel) fprintf(logfile, __VA_ARGS__); printf(__VA_ARGS__); } while(0)
|
||||
#else
|
||||
# define DPRINTF(...) do { if(loglevel) fprintf(logfile, __VA_ARGS__); } while(0)
|
||||
#endif
|
||||
|
||||
# define check_mach_header(x) (x.magic == MH_CIGAM)
|
||||
|
||||
extern const char *interp_prefix;
|
||||
|
||||
/* we don't have a good implementation for this */
|
||||
#define DONT_USE_DYLD_SHARED_MAP
|
||||
|
||||
/* Pass extra arg to DYLD for debug */
|
||||
//#define ACTIVATE_DYLD_TRACE
|
||||
|
||||
//#define OVERRIDE_DYLINKER
|
||||
|
||||
#ifdef OVERRIDE_DYLINKER
|
||||
# ifdef TARGET_I386
|
||||
# define DYLINKER_NAME "/Users/steg/qemu/tests/i386-darwin-env/usr/lib/dyld"
|
||||
# else
|
||||
# define DYLINKER_NAME "/usr/lib/dyld"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* XXX: in an include */
|
||||
struct nlist_extended
|
||||
{
|
||||
union {
|
||||
char *n_name;
|
||||
long n_strx;
|
||||
} n_un;
|
||||
unsigned char n_type;
|
||||
unsigned char n_sect;
|
||||
short st_desc;
|
||||
unsigned long st_value;
|
||||
unsigned long st_size;
|
||||
};
|
||||
|
||||
/* Print symbols in gdb */
|
||||
void *macho_text_sect = 0;
|
||||
int macho_offset = 0;
|
||||
|
||||
int load_object(const char *filename, struct target_pt_regs * regs, void ** mh);
|
||||
void qerror(const char *format, ...);
|
||||
#ifdef TARGET_I386
|
||||
typedef struct mach_i386_thread_state {
|
||||
unsigned int eax;
|
||||
unsigned int ebx;
|
||||
unsigned int ecx;
|
||||
unsigned int edx;
|
||||
unsigned int edi;
|
||||
unsigned int esi;
|
||||
unsigned int ebp;
|
||||
unsigned int esp;
|
||||
unsigned int ss;
|
||||
unsigned int eflags;
|
||||
unsigned int eip;
|
||||
unsigned int cs;
|
||||
unsigned int ds;
|
||||
unsigned int es;
|
||||
unsigned int fs;
|
||||
unsigned int gs;
|
||||
} mach_i386_thread_state_t;
|
||||
|
||||
void bswap_i386_thread_state(struct mach_i386_thread_state *ts)
|
||||
{
|
||||
bswap32s((uint32_t*)&ts->eax);
|
||||
bswap32s((uint32_t*)&ts->ebx);
|
||||
bswap32s((uint32_t*)&ts->ecx);
|
||||
bswap32s((uint32_t*)&ts->edx);
|
||||
bswap32s((uint32_t*)&ts->edi);
|
||||
bswap32s((uint32_t*)&ts->esi);
|
||||
bswap32s((uint32_t*)&ts->ebp);
|
||||
bswap32s((uint32_t*)&ts->esp);
|
||||
bswap32s((uint32_t*)&ts->ss);
|
||||
bswap32s((uint32_t*)&ts->eflags);
|
||||
bswap32s((uint32_t*)&ts->eip);
|
||||
bswap32s((uint32_t*)&ts->cs);
|
||||
bswap32s((uint32_t*)&ts->ds);
|
||||
bswap32s((uint32_t*)&ts->es);
|
||||
bswap32s((uint32_t*)&ts->fs);
|
||||
bswap32s((uint32_t*)&ts->gs);
|
||||
}
|
||||
#define target_thread_state mach_i386_thread_state
|
||||
#define TARGET_CPU_TYPE CPU_TYPE_I386
|
||||
#define TARGET_CPU_NAME "i386"
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_PPC
|
||||
struct mach_ppc_thread_state {
|
||||
unsigned int srr0; /* Instruction address register (PC) */
|
||||
unsigned int srr1; /* Machine state register (supervisor) */
|
||||
unsigned int r0;
|
||||
unsigned int r1;
|
||||
unsigned int r2;
|
||||
unsigned int r3;
|
||||
unsigned int r4;
|
||||
unsigned int r5;
|
||||
unsigned int r6;
|
||||
unsigned int r7;
|
||||
unsigned int r8;
|
||||
unsigned int r9;
|
||||
unsigned int r10;
|
||||
unsigned int r11;
|
||||
unsigned int r12;
|
||||
unsigned int r13;
|
||||
unsigned int r14;
|
||||
unsigned int r15;
|
||||
unsigned int r16;
|
||||
unsigned int r17;
|
||||
unsigned int r18;
|
||||
unsigned int r19;
|
||||
unsigned int r20;
|
||||
unsigned int r21;
|
||||
unsigned int r22;
|
||||
unsigned int r23;
|
||||
unsigned int r24;
|
||||
unsigned int r25;
|
||||
unsigned int r26;
|
||||
unsigned int r27;
|
||||
unsigned int r28;
|
||||
unsigned int r29;
|
||||
unsigned int r30;
|
||||
unsigned int r31;
|
||||
|
||||
unsigned int cr; /* Condition register */
|
||||
unsigned int xer; /* User's integer exception register */
|
||||
unsigned int lr; /* Link register */
|
||||
unsigned int ctr; /* Count register */
|
||||
unsigned int mq; /* MQ register (601 only) */
|
||||
|
||||
unsigned int vrsave; /* Vector Save Register */
|
||||
};
|
||||
|
||||
void bswap_ppc_thread_state(struct mach_ppc_thread_state *ts)
|
||||
{
|
||||
bswap32s((uint32_t*)&ts->srr0);
|
||||
bswap32s((uint32_t*)&ts->srr1);
|
||||
bswap32s((uint32_t*)&ts->r0);
|
||||
bswap32s((uint32_t*)&ts->r1);
|
||||
bswap32s((uint32_t*)&ts->r2);
|
||||
bswap32s((uint32_t*)&ts->r3);
|
||||
bswap32s((uint32_t*)&ts->r4);
|
||||
bswap32s((uint32_t*)&ts->r5);
|
||||
bswap32s((uint32_t*)&ts->r6);
|
||||
bswap32s((uint32_t*)&ts->r7);
|
||||
bswap32s((uint32_t*)&ts->r8);
|
||||
bswap32s((uint32_t*)&ts->r9);
|
||||
bswap32s((uint32_t*)&ts->r10);
|
||||
bswap32s((uint32_t*)&ts->r11);
|
||||
bswap32s((uint32_t*)&ts->r12);
|
||||
bswap32s((uint32_t*)&ts->r13);
|
||||
bswap32s((uint32_t*)&ts->r14);
|
||||
bswap32s((uint32_t*)&ts->r15);
|
||||
bswap32s((uint32_t*)&ts->r16);
|
||||
bswap32s((uint32_t*)&ts->r17);
|
||||
bswap32s((uint32_t*)&ts->r18);
|
||||
bswap32s((uint32_t*)&ts->r19);
|
||||
bswap32s((uint32_t*)&ts->r20);
|
||||
bswap32s((uint32_t*)&ts->r21);
|
||||
bswap32s((uint32_t*)&ts->r22);
|
||||
bswap32s((uint32_t*)&ts->r23);
|
||||
bswap32s((uint32_t*)&ts->r24);
|
||||
bswap32s((uint32_t*)&ts->r25);
|
||||
bswap32s((uint32_t*)&ts->r26);
|
||||
bswap32s((uint32_t*)&ts->r27);
|
||||
bswap32s((uint32_t*)&ts->r28);
|
||||
bswap32s((uint32_t*)&ts->r29);
|
||||
bswap32s((uint32_t*)&ts->r30);
|
||||
bswap32s((uint32_t*)&ts->r31);
|
||||
|
||||
bswap32s((uint32_t*)&ts->cr);
|
||||
bswap32s((uint32_t*)&ts->xer);
|
||||
bswap32s((uint32_t*)&ts->lr);
|
||||
bswap32s((uint32_t*)&ts->ctr);
|
||||
bswap32s((uint32_t*)&ts->mq);
|
||||
|
||||
bswap32s((uint32_t*)&ts->vrsave);
|
||||
}
|
||||
|
||||
#define target_thread_state mach_ppc_thread_state
|
||||
#define TARGET_CPU_TYPE CPU_TYPE_POWERPC
|
||||
#define TARGET_CPU_NAME "PowerPC"
|
||||
#endif
|
||||
|
||||
struct target_thread_command {
|
||||
unsigned long cmd; /* LC_THREAD or LC_UNIXTHREAD */
|
||||
unsigned long cmdsize; /* total size of this command */
|
||||
unsigned long flavor; /* flavor of thread state */
|
||||
unsigned long count; /* count of longs in thread state */
|
||||
struct target_thread_state state; /* thread state for this flavor */
|
||||
};
|
||||
|
||||
void bswap_tc(struct target_thread_command *tc)
|
||||
{
|
||||
bswap32s((uint32_t*)(&tc->flavor));
|
||||
bswap32s((uint32_t*)&tc->count);
|
||||
#if defined(TARGET_I386)
|
||||
bswap_i386_thread_state(&tc->state);
|
||||
#elif defined(TARGET_PPC)
|
||||
bswap_ppc_thread_state(&tc->state);
|
||||
#else
|
||||
# error unknown TARGET_CPU_TYPE
|
||||
#endif
|
||||
}
|
||||
|
||||
void bswap_mh(struct mach_header *mh)
|
||||
{
|
||||
bswap32s((uint32_t*)(&mh->magic));
|
||||
bswap32s((uint32_t*)&mh->cputype);
|
||||
bswap32s((uint32_t*)&mh->cpusubtype);
|
||||
bswap32s((uint32_t*)&mh->filetype);
|
||||
bswap32s((uint32_t*)&mh->ncmds);
|
||||
bswap32s((uint32_t*)&mh->sizeofcmds);
|
||||
bswap32s((uint32_t*)&mh->flags);
|
||||
}
|
||||
|
||||
void bswap_lc(struct load_command *lc)
|
||||
{
|
||||
bswap32s((uint32_t*)&lc->cmd);
|
||||
bswap32s((uint32_t*)&lc->cmdsize);
|
||||
}
|
||||
|
||||
|
||||
void bswap_fh(struct fat_header *fh)
|
||||
{
|
||||
bswap32s((uint32_t*)&fh->magic);
|
||||
bswap32s((uint32_t*)&fh->nfat_arch);
|
||||
}
|
||||
|
||||
void bswap_fa(struct fat_arch *fa)
|
||||
{
|
||||
bswap32s((uint32_t*)&fa->cputype);
|
||||
bswap32s((uint32_t*)&fa->cpusubtype);
|
||||
bswap32s((uint32_t*)&fa->offset);
|
||||
bswap32s((uint32_t*)&fa->size);
|
||||
bswap32s((uint32_t*)&fa->align);
|
||||
}
|
||||
|
||||
void bswap_segcmd(struct segment_command *sc)
|
||||
{
|
||||
bswap32s((uint32_t*)&sc->vmaddr);
|
||||
bswap32s((uint32_t*)&sc->vmsize);
|
||||
bswap32s((uint32_t*)&sc->fileoff);
|
||||
bswap32s((uint32_t*)&sc->filesize);
|
||||
bswap32s((uint32_t*)&sc->maxprot);
|
||||
bswap32s((uint32_t*)&sc->initprot);
|
||||
bswap32s((uint32_t*)&sc->nsects);
|
||||
bswap32s((uint32_t*)&sc->flags);
|
||||
}
|
||||
|
||||
void bswap_symtabcmd(struct symtab_command *stc)
|
||||
{
|
||||
bswap32s((uint32_t*)&stc->cmd);
|
||||
bswap32s((uint32_t*)&stc->cmdsize);
|
||||
bswap32s((uint32_t*)&stc->symoff);
|
||||
bswap32s((uint32_t*)&stc->nsyms);
|
||||
bswap32s((uint32_t*)&stc->stroff);
|
||||
bswap32s((uint32_t*)&stc->strsize);
|
||||
}
|
||||
|
||||
void bswap_sym(struct nlist *n)
|
||||
{
|
||||
bswap32s((uint32_t*)&n->n_un.n_strx);
|
||||
bswap16s((uint16_t*)&n->n_desc);
|
||||
bswap32s((uint32_t*)&n->n_value);
|
||||
}
|
||||
|
||||
int load_thread(struct mach_header *mh, struct target_thread_command *tc, struct target_pt_regs * regs, int fd, int mh_pos, int need_bswap)
|
||||
{
|
||||
int entry;
|
||||
if(need_bswap)
|
||||
bswap_tc(tc);
|
||||
#if defined(TARGET_I386)
|
||||
entry = tc->state.eip;
|
||||
DPRINTF(" eax 0x%.8x\n ebx 0x%.8x\n ecx 0x%.8x\n edx 0x%.8x\n edi 0x%.8x\n esi 0x%.8x\n ebp 0x%.8x\n esp 0x%.8x\n ss 0x%.8x\n eflags 0x%.8x\n eip 0x%.8x\n cs 0x%.8x\n ds 0x%.8x\n es 0x%.8x\n fs 0x%.8x\n gs 0x%.8x\n",
|
||||
tc->state.eax, tc->state.ebx, tc->state.ecx, tc->state.edx, tc->state.edi, tc->state.esi, tc->state.ebp,
|
||||
tc->state.esp, tc->state.ss, tc->state.eflags, tc->state.eip, tc->state.cs, tc->state.ds, tc->state.es,
|
||||
tc->state.fs, tc->state.gs );
|
||||
#define reg_copy(reg) regs->reg = tc->state.reg
|
||||
if(regs)
|
||||
{
|
||||
reg_copy(eax);
|
||||
reg_copy(ebx);
|
||||
reg_copy(ecx);
|
||||
reg_copy(edx);
|
||||
|
||||
reg_copy(edi);
|
||||
reg_copy(esi);
|
||||
|
||||
reg_copy(ebp);
|
||||
reg_copy(esp);
|
||||
|
||||
reg_copy(eflags);
|
||||
reg_copy(eip);
|
||||
/*
|
||||
reg_copy(ss);
|
||||
reg_copy(cs);
|
||||
reg_copy(ds);
|
||||
reg_copy(es);
|
||||
reg_copy(fs);
|
||||
reg_copy(gs);*/
|
||||
}
|
||||
#undef reg_copy
|
||||
#elif defined(TARGET_PPC)
|
||||
entry = tc->state.srr0;
|
||||
#endif
|
||||
DPRINTF("load_thread: entry 0x%x\n", entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
int load_dylinker(struct mach_header *mh, struct dylinker_command *dc, int fd, int mh_pos, int need_bswap)
|
||||
{
|
||||
int size;
|
||||
char * dylinker_name;
|
||||
size = dc->cmdsize - sizeof(struct dylinker_command);
|
||||
|
||||
if(need_bswap)
|
||||
dylinker_name = (char*)(bswap_32(dc->name.offset)+(int)dc);
|
||||
else
|
||||
dylinker_name = (char*)((dc->name.offset)+(int)dc);
|
||||
|
||||
#ifdef OVERRIDE_DYLINKER
|
||||
dylinker_name = DYLINKER_NAME;
|
||||
#else
|
||||
if(asprintf(&dylinker_name, "%s%s", interp_prefix, dylinker_name) == -1)
|
||||
qerror("can't allocate the new dylinker name\n");
|
||||
#endif
|
||||
|
||||
DPRINTF("dylinker_name %s\n", dylinker_name);
|
||||
return load_object(dylinker_name, NULL, NULL);
|
||||
}
|
||||
|
||||
int load_segment(struct mach_header *mh, struct segment_command *sc, int fd, int mh_pos, int need_bswap, int fixed, int slide)
|
||||
{
|
||||
unsigned long addr = sc->vmaddr;
|
||||
unsigned long size = sc->filesize;
|
||||
unsigned long error = 0;
|
||||
|
||||
if(need_bswap)
|
||||
bswap_segcmd(sc);
|
||||
|
||||
if(sc->vmaddr == 0)
|
||||
{
|
||||
DPRINTF("load_segment: sc->vmaddr == 0 returning\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(sc->segname, "__PAGEZERO") == 0)
|
||||
{
|
||||
DPRINTF("load_segment: __PAGEZERO returning\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Right now mmap memory */
|
||||
/* XXX: should check to see that the space is free, because MAP_FIXED is dangerous */
|
||||
DPRINTF("load_segment: mmaping %s to 0x%x-(0x%x|0x%x) + 0x%x\n", sc->segname, sc->vmaddr, sc->filesize, sc->vmsize, slide);
|
||||
|
||||
if(sc->filesize > 0)
|
||||
{
|
||||
int opt = 0;
|
||||
|
||||
if(fixed)
|
||||
opt |= MAP_FIXED;
|
||||
|
||||
DPRINTF("sc->vmaddr 0x%x slide 0x%x add 0x%x\n", slide, sc->vmaddr, sc->vmaddr+slide);
|
||||
|
||||
addr = target_mmap(sc->vmaddr+slide, sc->filesize, sc->initprot, opt, fd, mh_pos + sc->fileoff);
|
||||
|
||||
if(addr==-1)
|
||||
qerror("load_segment: can't mmap at 0x%x\n", sc->vmaddr+slide);
|
||||
|
||||
error = addr-sc->vmaddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = sc->vmaddr+slide;
|
||||
error = slide;
|
||||
}
|
||||
|
||||
if(sc->vmsize > sc->filesize)
|
||||
{
|
||||
addr += sc->filesize;
|
||||
size = sc->vmsize-sc->filesize;
|
||||
addr = target_mmap(addr, size, sc->initprot, MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
if(addr==-1)
|
||||
qerror("load_segment: can't mmap at 0x%x\n", sc->vmaddr+slide);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void *load_data(int fd, long offset, unsigned int size)
|
||||
{
|
||||
char *data;
|
||||
|
||||
data = malloc(size);
|
||||
if (!data)
|
||||
return NULL;
|
||||
lseek(fd, offset, SEEK_SET);
|
||||
if (read(fd, data, size) != size) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/* load a mach-o object file */
|
||||
int load_object(const char *filename, struct target_pt_regs * regs, void ** mh)
|
||||
{
|
||||
int need_bswap = 0;
|
||||
int entry_point = 0;
|
||||
int dyld_entry_point = 0;
|
||||
int slide, mmapfixed;
|
||||
int fd;
|
||||
struct load_command *lcmds, *lc;
|
||||
int is_fat = 0;
|
||||
unsigned int i, magic;
|
||||
int mach_hdr_pos = 0;
|
||||
struct mach_header mach_hdr;
|
||||
|
||||
/* for symbol lookup whith -d flag. */
|
||||
struct symtab_command * symtabcmd = 0;
|
||||
struct nlist_extended *symtab, *sym;
|
||||
struct nlist *symtab_std, *syment;
|
||||
char *strtab;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
qerror("can't open file '%s'", filename);
|
||||
|
||||
/* Read magic header. */
|
||||
if (read(fd, &magic, sizeof (magic)) != sizeof (magic))
|
||||
qerror("unable to read Magic of '%s'", filename);
|
||||
|
||||
/* Check Mach identification. */
|
||||
if(magic == MH_MAGIC)
|
||||
{
|
||||
is_fat = 0;
|
||||
need_bswap = 0;
|
||||
} else if (magic == MH_CIGAM)
|
||||
{
|
||||
is_fat = 0;
|
||||
need_bswap = 1;
|
||||
} else if (magic == FAT_MAGIC)
|
||||
{
|
||||
is_fat = 1;
|
||||
need_bswap = 0;
|
||||
} else if (magic == FAT_CIGAM)
|
||||
{
|
||||
is_fat = 1;
|
||||
need_bswap = 1;
|
||||
}
|
||||
else
|
||||
qerror("Not a Mach-O file.", filename);
|
||||
|
||||
DPRINTF("loading %s %s...\n", filename, is_fat ? "[FAT]": "[REGULAR]");
|
||||
if(is_fat)
|
||||
{
|
||||
int found = 0;
|
||||
struct fat_header fh;
|
||||
struct fat_arch *fa;
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
/* Read Fat header. */
|
||||
if (read(fd, &fh, sizeof (fh)) != sizeof (fh))
|
||||
qerror("unable to read file header");
|
||||
|
||||
if(need_bswap)
|
||||
bswap_fh(&fh);
|
||||
|
||||
/* Read Fat Arch. */
|
||||
fa = malloc(sizeof(struct fat_arch)*fh.nfat_arch);
|
||||
|
||||
if (read(fd, fa, sizeof(struct fat_arch)*fh.nfat_arch) != sizeof(struct fat_arch)*fh.nfat_arch)
|
||||
qerror("unable to read file header");
|
||||
|
||||
for( i = 0; i < fh.nfat_arch; i++, fa++)
|
||||
{
|
||||
if(need_bswap)
|
||||
bswap_fa(fa);
|
||||
if(fa->cputype == TARGET_CPU_TYPE)
|
||||
{
|
||||
mach_hdr_pos = fa->offset;
|
||||
lseek(fd, mach_hdr_pos, SEEK_SET);
|
||||
|
||||
/* Read Mach header. */
|
||||
|
||||
if (read(fd, &mach_hdr, sizeof(struct mach_header)) != sizeof (struct mach_header))
|
||||
qerror("unable to read file header");
|
||||
|
||||
if(mach_hdr.magic == MH_MAGIC)
|
||||
need_bswap = 0;
|
||||
else if (mach_hdr.magic == MH_CIGAM)
|
||||
need_bswap = 1;
|
||||
else
|
||||
qerror("Invalid mach header in Fat Mach-O File");
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
qerror("%s: No %s CPU found in FAT Header", filename, TARGET_CPU_NAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
/* Read Mach header */
|
||||
if (read(fd, &mach_hdr, sizeof (mach_hdr)) != sizeof (mach_hdr))
|
||||
qerror("%s: unable to read file header", filename);
|
||||
}
|
||||
|
||||
if(need_bswap)
|
||||
bswap_mh(&mach_hdr);
|
||||
|
||||
if ((mach_hdr.cputype) != TARGET_CPU_TYPE)
|
||||
qerror("%s: Unsupported CPU 0x%x (only 0x%x(%s) supported)", filename, mach_hdr.cputype, TARGET_CPU_TYPE, TARGET_CPU_NAME);
|
||||
|
||||
|
||||
switch(mach_hdr.filetype)
|
||||
{
|
||||
case MH_EXECUTE: break;
|
||||
case MH_FVMLIB:
|
||||
case MH_DYLIB:
|
||||
case MH_DYLINKER: break;
|
||||
default:
|
||||
qerror("%s: Unsupported Mach type (0x%x)", filename, mach_hdr.filetype);
|
||||
}
|
||||
|
||||
/* read segment headers */
|
||||
lcmds = malloc(mach_hdr.sizeofcmds);
|
||||
|
||||
if(read(fd, lcmds, mach_hdr.sizeofcmds) != mach_hdr.sizeofcmds)
|
||||
qerror("%s: unable to read load_command", filename);
|
||||
slide = 0;
|
||||
mmapfixed = 0;
|
||||
for(i=0, lc = lcmds; i < (mach_hdr.ncmds) ; i++)
|
||||
{
|
||||
|
||||
if(need_bswap)
|
||||
bswap_lc(lc);
|
||||
switch(lc->cmd)
|
||||
{
|
||||
case LC_SEGMENT:
|
||||
/* The main_exe can't be relocated */
|
||||
if(mach_hdr.filetype == MH_EXECUTE)
|
||||
mmapfixed = 1;
|
||||
|
||||
slide = load_segment(&mach_hdr, (struct segment_command*)lc, fd, mach_hdr_pos, need_bswap, mmapfixed, slide);
|
||||
|
||||
/* other segment must be mapped according to slide exactly, if load_segment did something */
|
||||
if(slide != -1)
|
||||
mmapfixed = 1;
|
||||
else
|
||||
slide = 0; /* load_segment didn't map the segment */
|
||||
|
||||
if(mach_hdr.filetype == MH_EXECUTE && slide != 0)
|
||||
qerror("%s: Warning executable can't be mapped at the right address (offset: 0x%x)\n", filename, slide);
|
||||
|
||||
if(strcmp(((struct segment_command*)(lc))->segname, "__TEXT") == 0)
|
||||
{
|
||||
/* Text section */
|
||||
if(mach_hdr.filetype == MH_EXECUTE)
|
||||
{
|
||||
/* return the mach_header */
|
||||
*mh = (void*)(((struct segment_command*)(lc))->vmaddr + slide);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* it is dyld save the section for gdb, we will be interested in dyld symbol
|
||||
while debuging */
|
||||
macho_text_sect = (void*)(((struct segment_command*)(lc))->vmaddr + slide);
|
||||
macho_offset = slide;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LC_LOAD_DYLINKER:
|
||||
dyld_entry_point = load_dylinker( &mach_hdr, (struct dylinker_command*)lc, fd, mach_hdr_pos, need_bswap );
|
||||
break;
|
||||
case LC_LOAD_DYLIB:
|
||||
/* dyld will do that for us */
|
||||
break;
|
||||
case LC_THREAD:
|
||||
case LC_UNIXTHREAD:
|
||||
{
|
||||
struct target_pt_regs * _regs;
|
||||
if(mach_hdr.filetype == MH_DYLINKER)
|
||||
_regs = regs;
|
||||
else
|
||||
_regs = 0;
|
||||
entry_point = load_thread( &mach_hdr, (struct target_thread_command*)lc, _regs, fd, mach_hdr_pos, need_bswap );
|
||||
}
|
||||
break;
|
||||
case LC_SYMTAB:
|
||||
/* Save the symtab and strtab */
|
||||
symtabcmd = (struct symtab_command *)lc;
|
||||
break;
|
||||
case LC_ID_DYLINKER:
|
||||
case LC_ID_DYLIB:
|
||||
case LC_UUID:
|
||||
case LC_DYSYMTAB:
|
||||
case LC_TWOLEVEL_HINTS:
|
||||
case LC_PREBIND_CKSUM:
|
||||
case LC_SUB_LIBRARY:
|
||||
break;
|
||||
default: fprintf(stderr, "warning: unkown command 0x%x in '%s'\n", lc->cmd, filename);
|
||||
}
|
||||
lc = (struct load_command*)((int)(lc)+(lc->cmdsize));
|
||||
}
|
||||
|
||||
if(symtabcmd)
|
||||
{
|
||||
if(need_bswap)
|
||||
bswap_symtabcmd(symtabcmd);
|
||||
|
||||
symtab_std = load_data(fd, symtabcmd->symoff+mach_hdr_pos, symtabcmd->nsyms * sizeof(struct nlist));
|
||||
strtab = load_data(fd, symtabcmd->stroff+mach_hdr_pos, symtabcmd->strsize);
|
||||
|
||||
symtab = malloc(sizeof(struct nlist_extended) * symtabcmd->nsyms);
|
||||
|
||||
if(need_bswap)
|
||||
{
|
||||
for(i = 0, syment = symtab_std; i < symtabcmd->nsyms; i++, syment++)
|
||||
bswap_sym(syment);
|
||||
}
|
||||
|
||||
for(i = 0, sym = symtab, syment = symtab_std; i < symtabcmd->nsyms; i++, sym++, syment++)
|
||||
{
|
||||
struct nlist *sym_follow, *sym_next = 0;
|
||||
unsigned int j;
|
||||
memset(sym, 0, sizeof(*sym));
|
||||
|
||||
sym->n_type = syment->n_type;
|
||||
if ( syment->n_type & N_STAB ) /* Debug symbols are skipped */
|
||||
continue;
|
||||
|
||||
memcpy(sym, syment, sizeof(*syment));
|
||||
|
||||
/* Find the following symbol in order to get the current symbol size */
|
||||
for(j = 0, sym_follow = symtab_std; j < symtabcmd->nsyms; j++, sym_follow++) {
|
||||
if ( sym_follow->n_type & N_STAB || !(sym_follow->n_value > sym->st_value))
|
||||
continue;
|
||||
if(!sym_next) {
|
||||
sym_next = sym_follow;
|
||||
continue;
|
||||
}
|
||||
if(!(sym_next->n_value > sym_follow->n_value))
|
||||
continue;
|
||||
sym_next = sym_follow;
|
||||
}
|
||||
if(sym_next)
|
||||
sym->st_size = sym_next->n_value - sym->st_value;
|
||||
else
|
||||
sym->st_size = 10; /* XXX: text_sec_hdr->size + text_sec_hdr->offset - sym->st_value; */
|
||||
|
||||
sym->st_value += slide;
|
||||
}
|
||||
|
||||
free((void*)symtab_std);
|
||||
|
||||
{
|
||||
DPRINTF("saving symtab of %s (%d symbol(s))\n", filename, symtabcmd->nsyms);
|
||||
struct syminfo *s;
|
||||
s = malloc(sizeof(*s));
|
||||
s->disas_symtab = symtab;
|
||||
s->disas_strtab = strtab;
|
||||
s->disas_num_syms = symtabcmd->nsyms;
|
||||
s->next = syminfos;
|
||||
syminfos = s;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
if(mach_hdr.filetype == MH_EXECUTE && dyld_entry_point)
|
||||
return dyld_entry_point;
|
||||
else
|
||||
return entry_point+slide;
|
||||
}
|
||||
|
||||
extern unsigned long stack_size;
|
||||
|
||||
unsigned long setup_arg_pages(void * mh, char ** argv, char ** env)
|
||||
{
|
||||
unsigned long stack_base, error, size;
|
||||
int i;
|
||||
int * stack;
|
||||
int argc, envc;
|
||||
|
||||
/* Create enough stack to hold everything. If we don't use
|
||||
* it for args, we'll use it for something else...
|
||||
*/
|
||||
size = stack_size;
|
||||
|
||||
error = target_mmap(0,
|
||||
size + qemu_host_page_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
if (error == -1)
|
||||
qerror("stk mmap");
|
||||
|
||||
/* we reserve one extra page at the top of the stack as guard */
|
||||
target_mprotect(error + size, qemu_host_page_size, PROT_NONE);
|
||||
|
||||
stack_base = error + size;
|
||||
stack = (void*)stack_base;
|
||||
/*
|
||||
* | STRING AREA |
|
||||
* +-------------+
|
||||
* | 0 |
|
||||
* +-------------+
|
||||
* | apple[n] |
|
||||
* +-------------+
|
||||
* :
|
||||
* +-------------+
|
||||
* | apple[0] |
|
||||
* +-------------+
|
||||
* | 0 |
|
||||
* +-------------+
|
||||
* | env[n] |
|
||||
* +-------------+
|
||||
* :
|
||||
* :
|
||||
* +-------------+
|
||||
* | env[0] |
|
||||
* +-------------+
|
||||
* | 0 |
|
||||
* +-------------+
|
||||
* | arg[argc-1] |
|
||||
* +-------------+
|
||||
* :
|
||||
* :
|
||||
* +-------------+
|
||||
* | arg[0] |
|
||||
* +-------------+
|
||||
* | argc |
|
||||
* +-------------+
|
||||
* sp-> | mh | address of where the a.out's file offset 0 is in memory
|
||||
* +-------------+
|
||||
*/
|
||||
/* Construct the stack Stack grows down */
|
||||
stack--;
|
||||
|
||||
/* XXX: string should go up there */
|
||||
|
||||
*stack = 0;
|
||||
stack--;
|
||||
|
||||
/* Push the absolute path of our executable */
|
||||
DPRINTF("pushing apple %s (0x%x)\n", (char*)argv[0], (int)argv[0]);
|
||||
stl(stack, (int) argv[0]);
|
||||
|
||||
stack--;
|
||||
|
||||
stl(stack, 0);
|
||||
stack--;
|
||||
|
||||
/* Get envc */
|
||||
for(envc = 0; env[envc]; envc++);
|
||||
|
||||
for(i = envc-1; i >= 0; i--)
|
||||
{
|
||||
DPRINTF("pushing env %s (0x%x)\n", (char*)env[i], (int)env[i]);
|
||||
stl(stack, (int)env[i]);
|
||||
stack--;
|
||||
|
||||
/* XXX: remove that when string will be on top of the stack */
|
||||
page_set_flags((int)env[i], (int)(env[i]+strlen(env[i])), PROT_READ | PAGE_VALID);
|
||||
}
|
||||
|
||||
/* Add on the stack the interp_prefix choosen if so */
|
||||
if(interp_prefix[0])
|
||||
{
|
||||
char *dyld_root;
|
||||
asprintf(&dyld_root, "DYLD_ROOT_PATH=%s", interp_prefix);
|
||||
page_set_flags((int)dyld_root, (int)(dyld_root+strlen(interp_prefix)+1), PROT_READ | PAGE_VALID);
|
||||
|
||||
stl(stack, (int)dyld_root);
|
||||
stack--;
|
||||
}
|
||||
|
||||
#ifdef DONT_USE_DYLD_SHARED_MAP
|
||||
{
|
||||
char *shared_map_mode;
|
||||
asprintf(&shared_map_mode, "DYLD_SHARED_REGION=avoid");
|
||||
page_set_flags((int)shared_map_mode, (int)(shared_map_mode+strlen(shared_map_mode)+1), PROT_READ | PAGE_VALID);
|
||||
|
||||
stl(stack, (int)shared_map_mode);
|
||||
stack--;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ACTIVATE_DYLD_TRACE
|
||||
char * extra_env_static[] = {"DYLD_DEBUG_TRACE=yes",
|
||||
"DYLD_PREBIND_DEBUG=3", "DYLD_UNKNOW_TRACE=yes",
|
||||
"DYLD_PRINT_INITIALIZERS=yes",
|
||||
"DYLD_PRINT_SEGMENTS=yes", "DYLD_PRINT_REBASINGS=yes", "DYLD_PRINT_BINDINGS=yes", "DYLD_PRINT_INITIALIZERS=yes", "DYLD_PRINT_WARNINGS=yes" };
|
||||
|
||||
char ** extra_env = malloc(sizeof(extra_env_static));
|
||||
bcopy(extra_env_static, extra_env, sizeof(extra_env_static));
|
||||
page_set_flags((int)extra_env, (int)((void*)extra_env+sizeof(extra_env_static)), PROT_READ | PAGE_VALID);
|
||||
|
||||
for(i = 0; i<9; i++)
|
||||
{
|
||||
DPRINTF("pushing (extra) env %s (0x%x)\n", (char*)extra_env[i], (int)extra_env[i]);
|
||||
stl(stack, (int) extra_env[i]);
|
||||
stack--;
|
||||
}
|
||||
#endif
|
||||
|
||||
stl(stack, 0);
|
||||
stack--;
|
||||
|
||||
/* Get argc */
|
||||
for(argc = 0; argv[argc]; argc++);
|
||||
|
||||
for(i = argc-1; i >= 0; i--)
|
||||
{
|
||||
DPRINTF("pushing arg %s (0x%x)\n", (char*)argv[i], (int)argv[i]);
|
||||
stl(stack, (int) argv[i]);
|
||||
stack--;
|
||||
|
||||
/* XXX: remove that when string will be on top of the stack */
|
||||
page_set_flags((int)argv[i], (int)(argv[i]+strlen(argv[i])), PROT_READ | PAGE_VALID);
|
||||
}
|
||||
|
||||
DPRINTF("pushing argc %d \n", argc);
|
||||
stl(stack, argc);
|
||||
stack--;
|
||||
|
||||
DPRINTF("pushing mh 0x%x \n", (int)mh);
|
||||
stl(stack, (int) mh);
|
||||
|
||||
/* Stack points on the mh */
|
||||
return (unsigned long)stack;
|
||||
}
|
||||
|
||||
int mach_exec(const char * filename, char ** argv, char ** envp,
|
||||
struct target_pt_regs * regs)
|
||||
{
|
||||
int entrypoint, stack;
|
||||
void * mh; /* the Mach Header that will be used by dyld */
|
||||
|
||||
DPRINTF("mach_exec at 0x%x\n", (int)mach_exec);
|
||||
|
||||
entrypoint = load_object(filename, regs, &mh);
|
||||
stack = setup_arg_pages(mh, argv, envp);
|
||||
#if defined(TARGET_I386)
|
||||
regs->eip = entrypoint;
|
||||
regs->esp = stack;
|
||||
#elif defined(TARGET_PPC)
|
||||
regs->nip = entrypoint;
|
||||
regs->gpr[1] = stack;
|
||||
#endif
|
||||
DPRINTF("mach_exec returns eip set to 0x%x esp 0x%x mh 0x%x\n", entrypoint, stack, (int)mh);
|
||||
|
||||
if(!entrypoint)
|
||||
qerror("%s: no entry point!\n", filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,918 @@
|
||||
/*
|
||||
* qemu user main
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
* Copyright (c) 2006 Pierre d'Herbemont
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
#define DEBUG_LOGFILE "/tmp/qemu.log"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <crt_externs.h>
|
||||
# define environ (*_NSGetEnviron())
|
||||
#endif
|
||||
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/vm_map.h>
|
||||
|
||||
const char *interp_prefix = "";
|
||||
|
||||
asm(".zerofill __STD_PROG_ZONE, __STD_PROG_ZONE, __std_prog_zone, 0x0dfff000");
|
||||
|
||||
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
|
||||
we allocate a bigger stack. Need a better solution, for example
|
||||
by remapping the process stack directly at the right place */
|
||||
unsigned long stack_size = 512 * 1024;
|
||||
|
||||
void qerror(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void gemu_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void cpu_outb(CPUState *env, int addr, int val)
|
||||
{
|
||||
fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val);
|
||||
}
|
||||
|
||||
void cpu_outw(CPUState *env, int addr, int val)
|
||||
{
|
||||
fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val);
|
||||
}
|
||||
|
||||
void cpu_outl(CPUState *env, int addr, int val)
|
||||
{
|
||||
fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val);
|
||||
}
|
||||
|
||||
int cpu_inb(CPUState *env, int addr)
|
||||
{
|
||||
fprintf(stderr, "inb: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_inw(CPUState *env, int addr)
|
||||
{
|
||||
fprintf(stderr, "inw: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_inl(CPUState *env, int addr)
|
||||
{
|
||||
fprintf(stderr, "inl: port=0x%04x\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_get_pic_interrupt(CPUState *env)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#ifdef TARGET_PPC
|
||||
|
||||
static inline uint64_t cpu_ppc_get_tb (CPUState *env)
|
||||
{
|
||||
/* TO FIX */
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t cpu_ppc_load_tbl (CPUState *env)
|
||||
{
|
||||
return cpu_ppc_get_tb(env) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
uint32_t cpu_ppc_load_tbu (CPUState *env)
|
||||
{
|
||||
return cpu_ppc_get_tb(env) >> 32;
|
||||
}
|
||||
|
||||
static void cpu_ppc_store_tb (CPUState *env, uint64_t value)
|
||||
{
|
||||
/* TO FIX */
|
||||
}
|
||||
|
||||
void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
|
||||
{
|
||||
cpu_ppc_store_tb(env, ((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
|
||||
}
|
||||
|
||||
void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
|
||||
{
|
||||
cpu_ppc_store_tb(env, ((uint64_t)cpu_ppc_load_tbl(env) << 32) | value);
|
||||
}
|
||||
|
||||
uint32_t cpu_ppc_load_decr (CPUState *env)
|
||||
{
|
||||
/* TO FIX */
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cpu_ppc_store_decr (CPUState *env, uint32_t value)
|
||||
{
|
||||
/* TO FIX */
|
||||
}
|
||||
|
||||
void cpu_loop(CPUPPCState *env)
|
||||
{
|
||||
int trapnr;
|
||||
uint32_t ret;
|
||||
target_siginfo_t info;
|
||||
|
||||
for(;;) {
|
||||
trapnr = cpu_ppc_exec(env);
|
||||
if (trapnr != EXCP_SYSCALL_USER && trapnr != EXCP_BRANCH &&
|
||||
trapnr != EXCP_TRACE) {
|
||||
if (loglevel > 0) {
|
||||
cpu_dump_state(env, logfile, fprintf, 0);
|
||||
}
|
||||
}
|
||||
switch(trapnr) {
|
||||
case EXCP_NONE:
|
||||
break;
|
||||
case EXCP_SYSCALL_USER:
|
||||
/* system call */
|
||||
if(((int)env->gpr[0]) <= SYS_MAXSYSCALL && ((int)env->gpr[0])>0)
|
||||
ret = do_unix_syscall(env, env->gpr[0]/*, env->gpr[3], env->gpr[4],
|
||||
env->gpr[5], env->gpr[6], env->gpr[7],
|
||||
env->gpr[8], env->gpr[9], env->gpr[10]*/);
|
||||
else if(((int)env->gpr[0])<0)
|
||||
ret = do_mach_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4],
|
||||
env->gpr[5], env->gpr[6], env->gpr[7],
|
||||
env->gpr[8], env->gpr[9], env->gpr[10]);
|
||||
else
|
||||
ret = do_thread_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4],
|
||||
env->gpr[5], env->gpr[6], env->gpr[7],
|
||||
env->gpr[8], env->gpr[9], env->gpr[10]);
|
||||
|
||||
/* Unix syscall error signaling */
|
||||
if(((int)env->gpr[0]) <= SYS_MAXSYSCALL && ((int)env->gpr[0])>0)
|
||||
{
|
||||
if( (int)ret < 0 )
|
||||
env->nip += 0;
|
||||
else
|
||||
env->nip += 4;
|
||||
}
|
||||
|
||||
/* Return value */
|
||||
env->gpr[3] = ret;
|
||||
break;
|
||||
case EXCP_RESET:
|
||||
/* Should not happen ! */
|
||||
fprintf(stderr, "RESET asked... Stop emulation\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "RESET asked... Stop emulation\n");
|
||||
abort();
|
||||
case EXCP_MACHINE_CHECK:
|
||||
fprintf(stderr, "Machine check exeption... Stop emulation\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "RESET asked... Stop emulation\n");
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = BUS_OBJERR;
|
||||
info.si_addr = (void*)(env->nip - 4);
|
||||
queue_signal(info.si_signo, &info);
|
||||
case EXCP_DSI:
|
||||
#ifndef DAR
|
||||
/* To deal with multiple qemu header version as host for the darwin-user code */
|
||||
# define DAR SPR_DAR
|
||||
#endif
|
||||
fprintf(stderr, "Invalid data memory access: 0x%08x\n", env->spr[DAR]);
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "Invalid data memory access: 0x%08x\n",
|
||||
env->spr[DAR]);
|
||||
}
|
||||
/* Handle this via the gdb */
|
||||
gdb_handlesig (env, SIGSEGV);
|
||||
|
||||
info.si_addr = (void*)env->nip;
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP_ISI:
|
||||
fprintf(stderr, "Invalid instruction fetch\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Invalid instruction fetch\n");
|
||||
/* Handle this via the gdb */
|
||||
gdb_handlesig (env, SIGSEGV);
|
||||
|
||||
info.si_addr = (void*)(env->nip - 4);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP_EXTERNAL:
|
||||
/* Should not happen ! */
|
||||
fprintf(stderr, "External interruption... Stop emulation\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "External interruption... Stop emulation\n");
|
||||
abort();
|
||||
case EXCP_ALIGN:
|
||||
fprintf(stderr, "Invalid unaligned memory access\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Invalid unaligned memory access\n");
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = BUS_ADRALN;
|
||||
info.si_addr = (void*)(env->nip - 4);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP_PROGRAM:
|
||||
switch (env->error_code & ~0xF) {
|
||||
case EXCP_FP:
|
||||
fprintf(stderr, "Program exception\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Program exception\n");
|
||||
/* Set FX */
|
||||
env->fpscr[7] |= 0x8;
|
||||
/* Finally, update FEX */
|
||||
if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
|
||||
((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
|
||||
env->fpscr[7] |= 0x4;
|
||||
info.si_signo = SIGFPE;
|
||||
info.si_errno = 0;
|
||||
switch (env->error_code & 0xF) {
|
||||
case EXCP_FP_OX:
|
||||
info.si_code = FPE_FLTOVF;
|
||||
break;
|
||||
case EXCP_FP_UX:
|
||||
info.si_code = FPE_FLTUND;
|
||||
break;
|
||||
case EXCP_FP_ZX:
|
||||
case EXCP_FP_VXZDZ:
|
||||
info.si_code = FPE_FLTDIV;
|
||||
break;
|
||||
case EXCP_FP_XX:
|
||||
info.si_code = FPE_FLTRES;
|
||||
break;
|
||||
case EXCP_FP_VXSOFT:
|
||||
info.si_code = FPE_FLTINV;
|
||||
break;
|
||||
case EXCP_FP_VXNAN:
|
||||
case EXCP_FP_VXISI:
|
||||
case EXCP_FP_VXIDI:
|
||||
case EXCP_FP_VXIMZ:
|
||||
case EXCP_FP_VXVC:
|
||||
case EXCP_FP_VXSQRT:
|
||||
case EXCP_FP_VXCVI:
|
||||
info.si_code = FPE_FLTSUB;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown floating point exception "
|
||||
"(%02x)\n", env->error_code);
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "Unknown floating point exception "
|
||||
"(%02x)\n", env->error_code & 0xF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXCP_INVAL:
|
||||
fprintf(stderr, "Invalid instruction\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Invalid instruction\n");
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
switch (env->error_code & 0xF) {
|
||||
case EXCP_INVAL_INVAL:
|
||||
info.si_code = ILL_ILLOPC;
|
||||
break;
|
||||
case EXCP_INVAL_LSWX:
|
||||
info.si_code = ILL_ILLOPN;
|
||||
break;
|
||||
case EXCP_INVAL_SPR:
|
||||
info.si_code = ILL_PRVREG;
|
||||
break;
|
||||
case EXCP_INVAL_FP:
|
||||
info.si_code = ILL_COPROC;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown invalid operation (%02x)\n",
|
||||
env->error_code & 0xF);
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "Unknown invalid operation (%02x)\n",
|
||||
env->error_code & 0xF);
|
||||
}
|
||||
info.si_code = ILL_ILLADR;
|
||||
break;
|
||||
}
|
||||
/* Handle this via the gdb */
|
||||
gdb_handlesig (env, SIGSEGV);
|
||||
break;
|
||||
case EXCP_PRIV:
|
||||
fprintf(stderr, "Privilege violation\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Privilege violation\n");
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
switch (env->error_code & 0xF) {
|
||||
case EXCP_PRIV_OPC:
|
||||
info.si_code = ILL_PRVOPC;
|
||||
break;
|
||||
case EXCP_PRIV_REG:
|
||||
info.si_code = ILL_PRVREG;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown privilege violation (%02x)\n",
|
||||
env->error_code & 0xF);
|
||||
info.si_code = ILL_PRVOPC;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EXCP_TRAP:
|
||||
fprintf(stderr, "Tried to call a TRAP\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Tried to call a TRAP\n");
|
||||
abort();
|
||||
default:
|
||||
/* Should not happen ! */
|
||||
fprintf(stderr, "Unknown program exception (%02x)\n",
|
||||
env->error_code);
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "Unknwon program exception (%02x)\n",
|
||||
env->error_code);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
info.si_addr = (void*)(env->nip - 4);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP_NO_FP:
|
||||
fprintf(stderr, "No floating point allowed\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "No floating point allowed\n");
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_COPROC;
|
||||
info.si_addr = (void*)(env->nip - 4);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP_DECR:
|
||||
/* Should not happen ! */
|
||||
fprintf(stderr, "Decrementer exception\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Decrementer exception\n");
|
||||
abort();
|
||||
case EXCP_TRACE:
|
||||
/* Pass to gdb: we use this to trace execution */
|
||||
gdb_handlesig (env, SIGTRAP);
|
||||
break;
|
||||
case EXCP_FP_ASSIST:
|
||||
/* Should not happen ! */
|
||||
fprintf(stderr, "Floating point assist exception\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Floating point assist exception\n");
|
||||
abort();
|
||||
case EXCP_MTMSR:
|
||||
/* We reloaded the msr, just go on */
|
||||
if (msr_pr == 0) {
|
||||
fprintf(stderr, "Tried to go into supervisor mode !\n");
|
||||
if (loglevel)
|
||||
fprintf(logfile, "Tried to go into supervisor mode !\n");
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
case EXCP_BRANCH:
|
||||
/* We stopped because of a jump... */
|
||||
break;
|
||||
case EXCP_INTERRUPT:
|
||||
/* Don't know why this should ever happen... */
|
||||
fprintf(stderr, "EXCP_INTERRUPT\n");
|
||||
break;
|
||||
case EXCP_DEBUG:
|
||||
gdb_handlesig (env, SIGTRAP);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
|
||||
trapnr);
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "qemu: unhandled CPU exception 0x%02x - "
|
||||
"0x%02x - aborting\n", trapnr, env->error_code);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
process_pending_signals(env);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TARGET_I386
|
||||
|
||||
/***********************************************************/
|
||||
/* CPUX86 core interface */
|
||||
|
||||
uint64_t cpu_get_tsc(CPUX86State *env)
|
||||
{
|
||||
return cpu_get_real_ticks();
|
||||
}
|
||||
|
||||
void
|
||||
write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
int flags)
|
||||
{
|
||||
unsigned int e1, e2;
|
||||
e1 = (addr << 16) | (limit & 0xffff);
|
||||
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
|
||||
e2 |= flags;
|
||||
stl((uint8_t *)ptr, e1);
|
||||
stl((uint8_t *)ptr + 4, e2);
|
||||
}
|
||||
|
||||
static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
|
||||
unsigned long addr, unsigned int sel)
|
||||
{
|
||||
unsigned int e1, e2;
|
||||
e1 = (addr & 0xffff) | (sel << 16);
|
||||
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
|
||||
stl((uint8_t *)ptr, e1);
|
||||
stl((uint8_t *)ptr + 4, e2);
|
||||
}
|
||||
|
||||
#define GDT_TABLE_SIZE 14
|
||||
#define LDT_TABLE_SIZE 15
|
||||
#define IDT_TABLE_SIZE 256
|
||||
#define TSS_SIZE 104
|
||||
uint64_t gdt_table[GDT_TABLE_SIZE];
|
||||
uint64_t ldt_table[LDT_TABLE_SIZE];
|
||||
uint64_t idt_table[IDT_TABLE_SIZE];
|
||||
uint32_t tss[TSS_SIZE];
|
||||
|
||||
/* only dpl matters as we do only user space emulation */
|
||||
static void set_idt(int n, unsigned int dpl)
|
||||
{
|
||||
set_gate(idt_table + n, 0, dpl, 0, 0);
|
||||
}
|
||||
|
||||
/* ABI convention: after a syscall if there was an error the CF flag is set */
|
||||
static inline void set_error(CPUX86State *env, int ret)
|
||||
{
|
||||
if(ret<0)
|
||||
env->eflags = env->eflags | 0x1;
|
||||
else
|
||||
env->eflags &= ~0x1;
|
||||
env->regs[R_EAX] = ret;
|
||||
}
|
||||
|
||||
void cpu_loop(CPUX86State *env)
|
||||
{
|
||||
int trapnr;
|
||||
int ret;
|
||||
uint8_t *pc;
|
||||
target_siginfo_t info;
|
||||
|
||||
for(;;) {
|
||||
trapnr = cpu_x86_exec(env);
|
||||
uint32_t *params = (uint32_t *)env->regs[R_ESP];
|
||||
switch(trapnr) {
|
||||
case 0x79: /* Our commpage hack back door exit is here */
|
||||
do_commpage(env, env->eip, *(params + 1), *(params + 2),
|
||||
*(params + 3), *(params + 4),
|
||||
*(params + 5), *(params + 6),
|
||||
*(params + 7), *(params + 8));
|
||||
break;
|
||||
case 0x81: /* mach syscall */
|
||||
{
|
||||
ret = do_mach_syscall(env, env->regs[R_EAX],
|
||||
*(params + 1), *(params + 2),
|
||||
*(params + 3), *(params + 4),
|
||||
*(params + 5), *(params + 6),
|
||||
*(params + 7), *(params + 8));
|
||||
set_error(env, ret);
|
||||
break;
|
||||
}
|
||||
case 0x90: /* unix backdoor */
|
||||
{
|
||||
/* after sysenter, stack is in R_ECX, new eip in R_EDX (sysexit will flip them back)*/
|
||||
int saved_stack = env->regs[R_ESP];
|
||||
env->regs[R_ESP] = env->regs[R_ECX];
|
||||
|
||||
ret = do_unix_syscall(env, env->regs[R_EAX]);
|
||||
|
||||
env->regs[R_ECX] = env->regs[R_ESP];
|
||||
env->regs[R_ESP] = saved_stack;
|
||||
|
||||
set_error(env, ret);
|
||||
break;
|
||||
}
|
||||
case 0x80: /* unix syscall */
|
||||
{
|
||||
ret = do_unix_syscall(env, env->regs[R_EAX]/*,
|
||||
*(params + 1), *(params + 2),
|
||||
*(params + 3), *(params + 4),
|
||||
*(params + 5), *(params + 6),
|
||||
*(params + 7), *(params + 8)*/);
|
||||
set_error(env, ret);
|
||||
break;
|
||||
}
|
||||
case 0x82: /* thread syscall */
|
||||
{
|
||||
ret = do_thread_syscall(env, env->regs[R_EAX],
|
||||
*(params + 1), *(params + 2),
|
||||
*(params + 3), *(params + 4),
|
||||
*(params + 5), *(params + 6),
|
||||
*(params + 7), *(params + 8));
|
||||
set_error(env, ret);
|
||||
break;
|
||||
}
|
||||
case EXCP0B_NOSEG:
|
||||
case EXCP0C_STACK:
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = BUS_NOOP;
|
||||
info.si_addr = 0;
|
||||
gdb_handlesig (env, SIGBUS);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP0D_GPF:
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = SEGV_NOOP;
|
||||
info.si_addr = 0;
|
||||
gdb_handlesig (env, SIGSEGV);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP0E_PAGE:
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
if (!(env->error_code & 1))
|
||||
info.si_code = SEGV_MAPERR;
|
||||
else
|
||||
info.si_code = SEGV_ACCERR;
|
||||
info.si_addr = (void*)env->cr[2];
|
||||
gdb_handlesig (env, SIGSEGV);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP00_DIVZ:
|
||||
/* division by zero */
|
||||
info.si_signo = SIGFPE;
|
||||
info.si_errno = 0;
|
||||
info.si_code = FPE_INTDIV;
|
||||
info.si_addr = (void*)env->eip;
|
||||
gdb_handlesig (env, SIGFPE);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP01_SSTP:
|
||||
case EXCP03_INT3:
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TRAP_BRKPT;
|
||||
info.si_addr = (void*)env->eip;
|
||||
gdb_handlesig (env, SIGTRAP);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP04_INTO:
|
||||
case EXCP05_BOUND:
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = SEGV_NOOP;
|
||||
info.si_addr = 0;
|
||||
gdb_handlesig (env, SIGSEGV);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP06_ILLOP:
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLOPN;
|
||||
info.si_addr = (void*)env->eip;
|
||||
gdb_handlesig (env, SIGILL);
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP_INTERRUPT:
|
||||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
case EXCP_DEBUG:
|
||||
{
|
||||
int sig;
|
||||
|
||||
sig = gdb_handlesig (env, SIGTRAP);
|
||||
if (sig)
|
||||
{
|
||||
info.si_signo = sig;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TRAP_BRKPT;
|
||||
queue_signal(info.si_signo, &info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pc = (void*)(env->segs[R_CS].base + env->eip);
|
||||
fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
|
||||
(long)pc, trapnr);
|
||||
abort();
|
||||
}
|
||||
process_pending_signals(env);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2004 Fabrice Bellard\n"
|
||||
"usage: qemu-" TARGET_ARCH " [-h] [-d opts] [-L path] [-s size] program [arguments...]\n"
|
||||
"Darwin CPU emulator (compiled for %s emulation)\n"
|
||||
"\n"
|
||||
"-h print this help\n"
|
||||
"-L path set the %s library path (default='%s')\n"
|
||||
"-s size set the stack size in bytes (default=%ld)\n"
|
||||
"\n"
|
||||
"debug options:\n"
|
||||
#ifdef USE_CODE_COPY
|
||||
"-no-code-copy disable code copy acceleration\n"
|
||||
#endif
|
||||
"-d options activate log (logfile='%s')\n"
|
||||
"-g wait for gdb on port 1234\n"
|
||||
"-p pagesize set the host page size to 'pagesize'\n",
|
||||
TARGET_ARCH,
|
||||
TARGET_ARCH,
|
||||
interp_prefix,
|
||||
stack_size,
|
||||
DEBUG_LOGFILE);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
/* XXX: currently only used for async signals (see signal.c) */
|
||||
CPUState *global_env;
|
||||
/* used only if single thread */
|
||||
CPUState *cpu_single_env = NULL;
|
||||
|
||||
/* used to free thread contexts */
|
||||
TaskState *first_task_state;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *filename;
|
||||
struct target_pt_regs regs1, *regs = ®s1;
|
||||
TaskState ts1, *ts = &ts1;
|
||||
CPUState *env;
|
||||
int optind;
|
||||
short use_gdbstub = 0;
|
||||
const char *r;
|
||||
|
||||
if (argc <= 1)
|
||||
usage();
|
||||
|
||||
/* init debug */
|
||||
cpu_set_log_filename(DEBUG_LOGFILE);
|
||||
|
||||
optind = 1;
|
||||
for(;;) {
|
||||
if (optind >= argc)
|
||||
break;
|
||||
r = argv[optind];
|
||||
if (r[0] != '-')
|
||||
break;
|
||||
optind++;
|
||||
r++;
|
||||
if (!strcmp(r, "-")) {
|
||||
break;
|
||||
} else if (!strcmp(r, "d")) {
|
||||
int mask;
|
||||
CPULogItem *item;
|
||||
|
||||
if (optind >= argc)
|
||||
break;
|
||||
|
||||
r = argv[optind++];
|
||||
mask = cpu_str_to_log_mask(r);
|
||||
if (!mask) {
|
||||
printf("Log items (comma separated):\n");
|
||||
for(item = cpu_log_items; item->mask != 0; item++) {
|
||||
printf("%-10s %s\n", item->name, item->help);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
cpu_set_log(mask);
|
||||
} else if (!strcmp(r, "s")) {
|
||||
r = argv[optind++];
|
||||
stack_size = strtol(r, (char **)&r, 0);
|
||||
if (stack_size <= 0)
|
||||
usage();
|
||||
if (*r == 'M')
|
||||
stack_size *= 1024 * 1024;
|
||||
else if (*r == 'k' || *r == 'K')
|
||||
stack_size *= 1024;
|
||||
} else if (!strcmp(r, "L")) {
|
||||
interp_prefix = argv[optind++];
|
||||
} else if (!strcmp(r, "p")) {
|
||||
qemu_host_page_size = atoi(argv[optind++]);
|
||||
if (qemu_host_page_size == 0 ||
|
||||
(qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
|
||||
fprintf(stderr, "page size must be a power of two\n");
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
if (!strcmp(r, "g")) {
|
||||
use_gdbstub = 1;
|
||||
} else
|
||||
#ifdef USE_CODE_COPY
|
||||
if (!strcmp(r, "no-code-copy")) {
|
||||
code_copy_enabled = 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
usage();
|
||||
}
|
||||
}
|
||||
if (optind >= argc)
|
||||
usage();
|
||||
filename = argv[optind];
|
||||
|
||||
/* Zero out regs */
|
||||
memset(regs, 0, sizeof(struct target_pt_regs));
|
||||
|
||||
/* NOTE: we need to init the CPU at this stage to get
|
||||
qemu_host_page_size */
|
||||
env = cpu_init();
|
||||
|
||||
printf("Starting %s with qemu\n----------------\n", filename);
|
||||
|
||||
commpage_init();
|
||||
|
||||
if (mach_exec(filename, argv+optind, environ, regs) != 0) {
|
||||
printf("Error loading %s\n", filename);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
syscall_init();
|
||||
signal_init();
|
||||
global_env = env;
|
||||
|
||||
/* build Task State */
|
||||
memset(ts, 0, sizeof(TaskState));
|
||||
env->opaque = ts;
|
||||
ts->used = 1;
|
||||
env->user_mode_only = 1;
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
cpu_x86_set_cpl(env, 3);
|
||||
|
||||
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
|
||||
env->hflags |= HF_PE_MASK;
|
||||
|
||||
if (env->cpuid_features & CPUID_SSE) {
|
||||
env->cr[4] |= CR4_OSFXSR_MASK;
|
||||
env->hflags |= HF_OSFXSR_MASK;
|
||||
}
|
||||
|
||||
/* flags setup : we activate the IRQs by default as in user mode */
|
||||
env->eflags |= IF_MASK;
|
||||
|
||||
/* darwin register setup */
|
||||
env->regs[R_EAX] = regs->eax;
|
||||
env->regs[R_EBX] = regs->ebx;
|
||||
env->regs[R_ECX] = regs->ecx;
|
||||
env->regs[R_EDX] = regs->edx;
|
||||
env->regs[R_ESI] = regs->esi;
|
||||
env->regs[R_EDI] = regs->edi;
|
||||
env->regs[R_EBP] = regs->ebp;
|
||||
env->regs[R_ESP] = regs->esp;
|
||||
env->eip = regs->eip;
|
||||
|
||||
/* Darwin LDT setup */
|
||||
/* 2 - User code segment
|
||||
3 - User data segment
|
||||
4 - User cthread */
|
||||
bzero(ldt_table, LDT_TABLE_SIZE * sizeof(ldt_table[0]));
|
||||
env->ldt.base = (uint32_t) ldt_table;
|
||||
env->ldt.limit = sizeof(ldt_table) - 1;
|
||||
|
||||
write_dt(ldt_table + 2, 0, 0xfffff,
|
||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
|
||||
write_dt(ldt_table + 3, 0, 0xfffff,
|
||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
|
||||
write_dt(ldt_table + 4, 0, 0xfffff,
|
||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
|
||||
|
||||
/* Darwin GDT setup.
|
||||
* has changed a lot between old Darwin/x86 (pre-Mac Intel) and Mac OS X/x86,
|
||||
now everything is done via int 0x81(mach) int 0x82 (thread) and sysenter/sysexit(unix) */
|
||||
bzero(gdt_table, sizeof(gdt_table));
|
||||
env->gdt.base = (uint32_t)gdt_table;
|
||||
env->gdt.limit = sizeof(gdt_table) - 1;
|
||||
|
||||
/* Set up a back door to handle sysenter syscalls (unix) */
|
||||
char * syscallbackdoor = malloc(64);
|
||||
page_set_flags((int)syscallbackdoor, (int)syscallbackdoor + 64, PROT_EXEC | PROT_READ | PAGE_VALID);
|
||||
|
||||
int i = 0;
|
||||
syscallbackdoor[i++] = 0xcd;
|
||||
syscallbackdoor[i++] = 0x90; /* int 0x90 */
|
||||
syscallbackdoor[i++] = 0x0F;
|
||||
syscallbackdoor[i++] = 0x35; /* sysexit */
|
||||
|
||||
/* Darwin sysenter/sysexit setup */
|
||||
env->sysenter_cs = 0x1; //XXX
|
||||
env->sysenter_eip = (int)syscallbackdoor;
|
||||
env->sysenter_esp = (int)malloc(64);
|
||||
|
||||
/* Darwin TSS setup
|
||||
This must match up with GDT[4] */
|
||||
env->tr.base = (uint32_t) tss;
|
||||
env->tr.limit = sizeof(tss) - 1;
|
||||
env->tr.flags = DESC_P_MASK | (0x9 << DESC_TYPE_SHIFT);
|
||||
stw(tss + 2, 0x10); // ss0 = 0x10 = GDT[2] = Kernel Data Segment
|
||||
|
||||
/* Darwin interrupt setup */
|
||||
bzero(idt_table, sizeof(idt_table));
|
||||
env->idt.base = (uint32_t) idt_table;
|
||||
env->idt.limit = sizeof(idt_table) - 1;
|
||||
set_idt(0, 0);
|
||||
set_idt(1, 0);
|
||||
set_idt(2, 0);
|
||||
set_idt(3, 3);
|
||||
set_idt(4, 3);
|
||||
set_idt(5, 3);
|
||||
set_idt(6, 0);
|
||||
set_idt(7, 0);
|
||||
set_idt(8, 0);
|
||||
set_idt(9, 0);
|
||||
set_idt(10, 0);
|
||||
set_idt(11, 0);
|
||||
set_idt(12, 0);
|
||||
set_idt(13, 0);
|
||||
set_idt(14, 0);
|
||||
set_idt(15, 0);
|
||||
set_idt(16, 0);
|
||||
set_idt(17, 0);
|
||||
set_idt(18, 0);
|
||||
set_idt(19, 0);
|
||||
/* Syscalls are done via
|
||||
int 0x80 (unix) (rarely used)
|
||||
int 0x81 (mach)
|
||||
int 0x82 (thread)
|
||||
int 0x83 (diag) (not handled here)
|
||||
sysenter/sysexit (unix) -> we redirect that to int 0x90 */
|
||||
set_idt(0x79, 3); /* Commpage hack, here is our backdoor interrupt */
|
||||
set_idt(0x80, 3); /* Unix Syscall */
|
||||
set_idt(0x81, 3); /* Mach Syscalls */
|
||||
set_idt(0x82, 3); /* thread Syscalls */
|
||||
|
||||
set_idt(0x90, 3); /* qemu-darwin-user's Unix syscalls backdoor */
|
||||
|
||||
|
||||
cpu_x86_load_seg(env, R_CS, __USER_CS);
|
||||
cpu_x86_load_seg(env, R_DS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_ES, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_SS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_FS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_GS, __USER_DS);
|
||||
|
||||
#elif defined(TARGET_PPC)
|
||||
{
|
||||
int i;
|
||||
env->nip = regs->nip;
|
||||
for(i = 0; i < 32; i++) {
|
||||
env->gpr[i] = regs->gpr[i];
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error unsupported target CPU
|
||||
#endif
|
||||
|
||||
if (use_gdbstub) {
|
||||
printf("Waiting for gdb Connection on port 1234...\n");
|
||||
gdbserver_start (1234);
|
||||
gdb_handlesig(env, 0);
|
||||
}
|
||||
|
||||
cpu_loop(env);
|
||||
/* never exits */
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* mmap support for qemu
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
//#define DEBUG_MMAP
|
||||
|
||||
/* NOTE: all the constants are the HOST ones */
|
||||
int target_mprotect(unsigned long start, unsigned long len, int prot)
|
||||
{
|
||||
unsigned long end, host_start, host_end, addr;
|
||||
int prot1, ret;
|
||||
|
||||
#ifdef DEBUG_MMAP
|
||||
printf("mprotect: start=0x%lx len=0x%lx prot=%c%c%c\n", start, len,
|
||||
prot & PROT_READ ? 'r' : '-',
|
||||
prot & PROT_WRITE ? 'w' : '-',
|
||||
prot & PROT_EXEC ? 'x' : '-');
|
||||
#endif
|
||||
|
||||
if ((start & ~TARGET_PAGE_MASK) != 0)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
end = start + len;
|
||||
if (end < start)
|
||||
return -EINVAL;
|
||||
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
|
||||
return -EINVAL;
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
host_start = start & qemu_host_page_mask;
|
||||
host_end = HOST_PAGE_ALIGN(end);
|
||||
if (start > host_start) {
|
||||
/* handle host page containing start */
|
||||
prot1 = prot;
|
||||
for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
if (host_end == host_start + qemu_host_page_size) {
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
end = host_end;
|
||||
}
|
||||
ret = mprotect((void *)host_start, qemu_host_page_size, prot1 & PAGE_BITS);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
host_start += qemu_host_page_size;
|
||||
}
|
||||
if (end < host_end) {
|
||||
prot1 = prot;
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
ret = mprotect((void *)(host_end - qemu_host_page_size), qemu_host_page_size,
|
||||
prot1 & PAGE_BITS);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
host_end -= qemu_host_page_size;
|
||||
}
|
||||
|
||||
/* handle the pages in the middle */
|
||||
if (host_start < host_end) {
|
||||
ret = mprotect((void *)host_start, host_end - host_start, prot);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
page_set_flags(start, start + len, prot | PAGE_VALID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* map an incomplete host page */
|
||||
int mmap_frag(unsigned long host_start,
|
||||
unsigned long start, unsigned long end,
|
||||
int prot, int flags, int fd, unsigned long offset)
|
||||
{
|
||||
unsigned long host_end, ret, addr;
|
||||
int prot1, prot_new;
|
||||
|
||||
host_end = host_start + qemu_host_page_size;
|
||||
|
||||
/* get the protection of the target pages outside the mapping */
|
||||
prot1 = 0;
|
||||
for(addr = host_start; addr < host_end; addr++) {
|
||||
if (addr < start || addr >= end)
|
||||
prot1 |= page_get_flags(addr);
|
||||
}
|
||||
|
||||
if (prot1 == 0) {
|
||||
/* no page was there, so we allocate one */
|
||||
ret = (long)mmap((void *)host_start, qemu_host_page_size, prot,
|
||||
flags | MAP_ANONYMOUS, -1, 0);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
}
|
||||
prot1 &= PAGE_BITS;
|
||||
|
||||
prot_new = prot | prot1;
|
||||
if (!(flags & MAP_ANONYMOUS)) {
|
||||
/* msync() won't work here, so we return an error if write is
|
||||
possible while it is a shared mapping */
|
||||
#ifndef __APPLE__
|
||||
if ((flags & MAP_TYPE) == MAP_SHARED &&
|
||||
#else
|
||||
if ((flags & MAP_SHARED) &&
|
||||
#endif
|
||||
(prot & PROT_WRITE))
|
||||
return -EINVAL;
|
||||
|
||||
/* adjust protection to be able to read */
|
||||
if (!(prot1 & PROT_WRITE))
|
||||
mprotect((void *)host_start, qemu_host_page_size, prot1 | PROT_WRITE);
|
||||
|
||||
/* read the corresponding file data */
|
||||
pread(fd, (void *)start, end - start, offset);
|
||||
|
||||
/* put final protection */
|
||||
if (prot_new != (prot1 | PROT_WRITE))
|
||||
mprotect((void *)host_start, qemu_host_page_size, prot_new);
|
||||
} else {
|
||||
/* just update the protection */
|
||||
if (prot_new != prot1) {
|
||||
mprotect((void *)host_start, qemu_host_page_size, prot_new);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NOTE: all the constants are the HOST ones */
|
||||
long target_mmap(unsigned long start, unsigned long len, int prot,
|
||||
int flags, int fd, unsigned long offset)
|
||||
{
|
||||
unsigned long ret, end, host_start, host_end, retaddr, host_offset, host_len;
|
||||
#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__)
|
||||
static unsigned long last_start = 0x40000000;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_MMAP
|
||||
{
|
||||
printf("mmap: start=0x%lx len=0x%lx prot=%c%c%c flags=",
|
||||
start, len,
|
||||
prot & PROT_READ ? 'r' : '-',
|
||||
prot & PROT_WRITE ? 'w' : '-',
|
||||
prot & PROT_EXEC ? 'x' : '-');
|
||||
if (flags & MAP_FIXED)
|
||||
printf("MAP_FIXED ");
|
||||
if (flags & MAP_ANONYMOUS)
|
||||
printf("MAP_ANON ");
|
||||
#ifndef MAP_TYPE
|
||||
# define MAP_TYPE 0x3
|
||||
#endif
|
||||
switch(flags & MAP_TYPE) {
|
||||
case MAP_PRIVATE:
|
||||
printf("MAP_PRIVATE ");
|
||||
break;
|
||||
case MAP_SHARED:
|
||||
printf("MAP_SHARED ");
|
||||
break;
|
||||
default:
|
||||
printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
|
||||
break;
|
||||
}
|
||||
printf("fd=%d offset=%lx\n", fd, offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (offset & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
if (len == 0)
|
||||
return start;
|
||||
host_start = start & qemu_host_page_mask;
|
||||
|
||||
if (!(flags & MAP_FIXED)) {
|
||||
#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__)
|
||||
/* tell the kenel to search at the same place as i386 */
|
||||
if (host_start == 0) {
|
||||
host_start = last_start;
|
||||
last_start += HOST_PAGE_ALIGN(len);
|
||||
}
|
||||
#endif
|
||||
if (qemu_host_page_size != qemu_real_host_page_size) {
|
||||
/* NOTE: this code is only for debugging with '-p' option */
|
||||
/* reserve a memory area */
|
||||
host_len = HOST_PAGE_ALIGN(len) + qemu_host_page_size - TARGET_PAGE_SIZE;
|
||||
host_start = (long)mmap((void *)host_start, host_len, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (host_start == -1)
|
||||
return host_start;
|
||||
host_end = host_start + host_len;
|
||||
start = HOST_PAGE_ALIGN(host_start);
|
||||
end = start + HOST_PAGE_ALIGN(len);
|
||||
if (start > host_start)
|
||||
munmap((void *)host_start, start - host_start);
|
||||
if (end < host_end)
|
||||
munmap((void *)end, host_end - end);
|
||||
/* use it as a fixed mapping */
|
||||
flags |= MAP_FIXED;
|
||||
} else {
|
||||
/* if not fixed, no need to do anything */
|
||||
host_offset = offset & qemu_host_page_mask;
|
||||
host_len = len + offset - host_offset;
|
||||
start = (long)mmap((void *)host_start, host_len,
|
||||
prot, flags, fd, host_offset);
|
||||
if (start == -1)
|
||||
return start;
|
||||
/* update start so that it points to the file position at 'offset' */
|
||||
if (!(flags & MAP_ANONYMOUS))
|
||||
start += offset - host_offset;
|
||||
goto the_end1;
|
||||
}
|
||||
}
|
||||
|
||||
if (start & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
end = start + len;
|
||||
host_end = HOST_PAGE_ALIGN(end);
|
||||
|
||||
/* worst case: we cannot map the file because the offset is not
|
||||
aligned, so we read it */
|
||||
if (!(flags & MAP_ANONYMOUS) &&
|
||||
(offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
|
||||
/* msync() won't work here, so we return an error if write is
|
||||
possible while it is a shared mapping */
|
||||
#ifndef __APPLE__
|
||||
if ((flags & MAP_TYPE) == MAP_SHARED &&
|
||||
#else
|
||||
if ((flags & MAP_SHARED) &&
|
||||
#endif
|
||||
(prot & PROT_WRITE))
|
||||
return -EINVAL;
|
||||
retaddr = target_mmap(start, len, prot | PROT_WRITE,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
if (retaddr == -1)
|
||||
return retaddr;
|
||||
pread(fd, (void *)start, len, offset);
|
||||
if (!(prot & PROT_WRITE)) {
|
||||
ret = target_mprotect(start, len, prot);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
goto the_end;
|
||||
}
|
||||
|
||||
/* handle the start of the mapping */
|
||||
if (start > host_start) {
|
||||
if (host_end == host_start + qemu_host_page_size) {
|
||||
/* one single host page */
|
||||
ret = mmap_frag(host_start, start, end,
|
||||
prot, flags, fd, offset);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
goto the_end1;
|
||||
}
|
||||
ret = mmap_frag(host_start, start, host_start + qemu_host_page_size,
|
||||
prot, flags, fd, offset);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
host_start += qemu_host_page_size;
|
||||
}
|
||||
/* handle the end of the mapping */
|
||||
if (end < host_end) {
|
||||
ret = mmap_frag(host_end - qemu_host_page_size,
|
||||
host_end - qemu_host_page_size, host_end,
|
||||
prot, flags, fd,
|
||||
offset + host_end - qemu_host_page_size - start);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
host_end -= qemu_host_page_size;
|
||||
}
|
||||
|
||||
/* map the middle (easier) */
|
||||
if (host_start < host_end) {
|
||||
unsigned long offset1;
|
||||
if (flags & MAP_ANONYMOUS)
|
||||
offset1 = 0;
|
||||
else
|
||||
offset1 = offset + host_start - start;
|
||||
ret = (long)mmap((void *)host_start, host_end - host_start,
|
||||
prot, flags, fd, offset1);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
}
|
||||
the_end1:
|
||||
page_set_flags(start, start + len, prot | PAGE_VALID);
|
||||
the_end:
|
||||
#ifdef DEBUG_MMAP
|
||||
printf("target_mmap: ret=0x%lx\n", (long)start);
|
||||
page_dump(stdout);
|
||||
printf("\n");
|
||||
#endif
|
||||
return start;
|
||||
}
|
||||
|
||||
int target_munmap(unsigned long start, unsigned long len)
|
||||
{
|
||||
unsigned long end, host_start, host_end, addr;
|
||||
int prot, ret;
|
||||
|
||||
#ifdef DEBUG_MMAP
|
||||
printf("munmap: start=0x%lx len=0x%lx\n", start, len);
|
||||
#endif
|
||||
if (start & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
end = start + len;
|
||||
host_start = start & qemu_host_page_mask;
|
||||
host_end = HOST_PAGE_ALIGN(end);
|
||||
|
||||
if (start > host_start) {
|
||||
/* handle host page containing start */
|
||||
prot = 0;
|
||||
for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
if (host_end == host_start + qemu_host_page_size) {
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
end = host_end;
|
||||
}
|
||||
if (prot != 0)
|
||||
host_start += qemu_host_page_size;
|
||||
}
|
||||
if (end < host_end) {
|
||||
prot = 0;
|
||||
for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
|
||||
prot |= page_get_flags(addr);
|
||||
}
|
||||
if (prot != 0)
|
||||
host_end -= qemu_host_page_size;
|
||||
}
|
||||
|
||||
/* unmap what we can */
|
||||
if (host_start < host_end) {
|
||||
ret = munmap((void *)host_start, host_end - host_start);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
page_set_flags(start, start + len, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
|
||||
blocks which have been allocated starting on a host page */
|
||||
long target_mremap(unsigned long old_addr, unsigned long old_size,
|
||||
unsigned long new_size, unsigned long flags,
|
||||
unsigned long new_addr)
|
||||
{
|
||||
#ifndef __APPLE__
|
||||
/* XXX: use 5 args syscall */
|
||||
new_addr = (long)mremap((void *)old_addr, old_size, new_size, flags);
|
||||
if (new_addr == -1)
|
||||
return new_addr;
|
||||
prot = page_get_flags(old_addr);
|
||||
page_set_flags(old_addr, old_addr + old_size, 0);
|
||||
page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
|
||||
return new_addr;
|
||||
#else
|
||||
qerror("target_mremap: unsupported\n");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int target_msync(unsigned long start, unsigned long len, int flags)
|
||||
{
|
||||
unsigned long end;
|
||||
|
||||
if (start & ~TARGET_PAGE_MASK)
|
||||
return -EINVAL;
|
||||
len = TARGET_PAGE_ALIGN(len);
|
||||
end = start + len;
|
||||
if (end < start)
|
||||
return -EINVAL;
|
||||
if (end == start)
|
||||
return 0;
|
||||
|
||||
start &= qemu_host_page_mask;
|
||||
return msync((void *)start, end - start, flags);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
#ifndef GEMU_H
|
||||
#define GEMU_H
|
||||
|
||||
#include "thunk.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#include "gdbstub.h"
|
||||
|
||||
typedef siginfo_t target_siginfo_t;
|
||||
#define target_sigaction sigaction
|
||||
#ifdef TARGET_I386
|
||||
struct target_pt_regs {
|
||||
long ebx;
|
||||
long ecx;
|
||||
long edx;
|
||||
long esi;
|
||||
long edi;
|
||||
long ebp;
|
||||
long eax;
|
||||
int xds;
|
||||
int xes;
|
||||
long orig_eax;
|
||||
long eip;
|
||||
int xcs;
|
||||
long eflags;
|
||||
long esp;
|
||||
int xss;
|
||||
};
|
||||
struct target_sigcontext {
|
||||
int sc_onstack;
|
||||
int sc_mask;
|
||||
int sc_eax;
|
||||
int sc_ebx;
|
||||
int sc_ecx;
|
||||
int sc_edx;
|
||||
int sc_edi;
|
||||
int sc_esi;
|
||||
int sc_ebp;
|
||||
int sc_esp;
|
||||
int sc_ss;
|
||||
int sc_eflags;
|
||||
int sc_eip;
|
||||
int sc_cs;
|
||||
int sc_ds;
|
||||
int sc_es;
|
||||
int sc_fs;
|
||||
int sc_gs;
|
||||
};
|
||||
|
||||
#define __USER_CS (0x17)
|
||||
#define __USER_DS (0x1F)
|
||||
|
||||
#elif defined(TARGET_PPC)
|
||||
struct target_pt_regs {
|
||||
unsigned long gpr[32];
|
||||
unsigned long nip;
|
||||
unsigned long msr;
|
||||
unsigned long orig_gpr3; /* Used for restarting system calls */
|
||||
unsigned long ctr;
|
||||
unsigned long link;
|
||||
unsigned long xer;
|
||||
unsigned long ccr;
|
||||
unsigned long mq; /* 601 only (not used at present) */
|
||||
/* Used on APUS to hold IPL value. */
|
||||
unsigned long trap; /* Reason for being here */
|
||||
unsigned long dar; /* Fault registers */
|
||||
unsigned long dsisr;
|
||||
unsigned long result; /* Result of a system call */
|
||||
};
|
||||
|
||||
struct target_sigcontext {
|
||||
int sc_onstack; /* sigstack state to restore */
|
||||
int sc_mask; /* signal mask to restore */
|
||||
int sc_ir; /* pc */
|
||||
int sc_psw; /* processor status word */
|
||||
int sc_sp; /* stack pointer if sc_regs == NULL */
|
||||
void *sc_regs; /* (kernel private) saved state */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct TaskState {
|
||||
struct TaskState *next;
|
||||
int used; /* non zero if used */
|
||||
uint8_t stack[0];
|
||||
} __attribute__((aligned(16))) TaskState;
|
||||
|
||||
void syscall_init(void);
|
||||
long do_mach_syscall(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
|
||||
uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8);
|
||||
long do_thread_syscall(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
|
||||
uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8);
|
||||
long do_unix_syscall(void *cpu_env, int num);
|
||||
int do_sigaction(int sig, const struct sigaction *act,
|
||||
struct sigaction *oact);
|
||||
int do_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss);
|
||||
|
||||
void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
|
||||
void qerror(const char *fmt, ...);
|
||||
|
||||
void write_dt(void *ptr, unsigned long addr, unsigned long limit, int flags);
|
||||
|
||||
extern CPUState *global_env;
|
||||
void cpu_loop(CPUState *env);
|
||||
void init_paths(const char *prefix);
|
||||
const char *path(const char *pathname);
|
||||
|
||||
extern int loglevel;
|
||||
extern FILE *logfile;
|
||||
|
||||
/* commpage.c */
|
||||
void commpage_init();
|
||||
void do_commpage(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
|
||||
uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8);
|
||||
|
||||
/* signal.c */
|
||||
void process_pending_signals(void *cpu_env);
|
||||
void signal_init(void);
|
||||
int queue_signal(int sig, target_siginfo_t *info);
|
||||
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
|
||||
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
|
||||
long do_sigreturn(CPUState *env, int num);
|
||||
|
||||
/* machload.c */
|
||||
int mach_exec(const char * filename, char ** argv, char ** envp,
|
||||
struct target_pt_regs * regs);
|
||||
|
||||
/* mmap.c */
|
||||
int target_mprotect(unsigned long start, unsigned long len, int prot);
|
||||
long target_mmap(unsigned long start, unsigned long len, int prot,
|
||||
int flags, int fd, unsigned long offset);
|
||||
int target_munmap(unsigned long start, unsigned long len);
|
||||
long target_mremap(unsigned long old_addr, unsigned long old_size,
|
||||
unsigned long new_size, unsigned long flags,
|
||||
unsigned long new_addr);
|
||||
int target_msync(unsigned long start, unsigned long len, int flags);
|
||||
|
||||
/* user access */
|
||||
|
||||
/* XXX: todo protect every memory access */
|
||||
#define lock_user(x,y,z) (void*)(x)
|
||||
#define unlock_user(x,y,z)
|
||||
|
||||
/* Mac OS X ABI arguments processing */
|
||||
#ifdef TARGET_I386
|
||||
static inline uint32_t get_int_arg(int *i, CPUX86State *cpu_env)
|
||||
{
|
||||
uint32_t *args = (uint32_t*)(cpu_env->regs[R_ESP] + 4 + *i);
|
||||
*i+=4;
|
||||
return tswap32(*args);
|
||||
}
|
||||
static inline uint64_t get_int64_arg(int *i, CPUX86State *cpu_env)
|
||||
{
|
||||
uint64_t *args = (uint64_t*)(cpu_env->regs[R_ESP] + 4 + *i);
|
||||
*i+=8;
|
||||
return tswap64(*args);
|
||||
}
|
||||
#elif defined(TARGET_PPC)
|
||||
static inline uint32_t get_int_arg(int *i, CPUPPCState *cpu_env)
|
||||
{
|
||||
/* XXX: won't work when args goes on stack after gpr10 */
|
||||
uint32_t args = (uint32_t)(cpu_env->gpr[3+(*i & 0xff)/4]);
|
||||
*i+=4;
|
||||
return tswap32(args);
|
||||
}
|
||||
static inline uint64_t get_int64_arg(int *i, CPUPPCState *cpu_env)
|
||||
{
|
||||
/* XXX: won't work when args goes on stack after gpr10 */
|
||||
uint64_t args = (uint64_t)(cpu_env->fpr[1+(*i >> 8)/8]);
|
||||
*i+=(8 << 8) + 8;
|
||||
return tswap64(args);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Emulation of Linux signals
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#ifdef __ia64__
|
||||
#undef uc_mcontext
|
||||
#undef uc_sigmask
|
||||
#undef uc_stack
|
||||
#undef uc_link
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
#define DEBUG_SIGNAL
|
||||
|
||||
#define MAX_SIGQUEUE_SIZE 1024
|
||||
|
||||
struct sigqueue {
|
||||
struct sigqueue *next;
|
||||
target_siginfo_t info;
|
||||
};
|
||||
|
||||
struct emulated_sigaction {
|
||||
struct target_sigaction sa;
|
||||
int pending; /* true if signal is pending */
|
||||
struct sigqueue *first;
|
||||
struct sigqueue info; /* in order to always have memory for the
|
||||
first signal, we put it here */
|
||||
};
|
||||
|
||||
struct sigaltstack target_sigaltstack_used = {
|
||||
0, 0, SA_DISABLE
|
||||
};
|
||||
|
||||
static struct emulated_sigaction sigact_table[NSIG];
|
||||
static struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
|
||||
static struct sigqueue *first_free; /* first free siginfo queue entry */
|
||||
static int signal_pending; /* non zero if a signal may be pending */
|
||||
|
||||
static void host_signal_handler(int host_signum, siginfo_t *info,
|
||||
void *puc);
|
||||
|
||||
|
||||
static inline int host_to_target_signal(int sig)
|
||||
{
|
||||
return sig;
|
||||
}
|
||||
|
||||
static inline int target_to_host_signal(int sig)
|
||||
{
|
||||
return sig;
|
||||
}
|
||||
|
||||
/* siginfo conversion */
|
||||
|
||||
|
||||
|
||||
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void signal_init(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
int i;
|
||||
|
||||
/* set all host signal handlers. ALL signals are blocked during
|
||||
the handlers to serialize them. */
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_sigaction = host_signal_handler;
|
||||
for(i = 1; i < NSIG; i++) {
|
||||
sigaction(i, &act, NULL);
|
||||
}
|
||||
|
||||
memset(sigact_table, 0, sizeof(sigact_table));
|
||||
|
||||
first_free = &sigqueue_table[0];
|
||||
for(i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++)
|
||||
sigqueue_table[i].next = &sigqueue_table[i + 1];
|
||||
sigqueue_table[MAX_SIGQUEUE_SIZE - 1].next = NULL;
|
||||
}
|
||||
|
||||
/* signal queue handling */
|
||||
|
||||
static inline struct sigqueue *alloc_sigqueue(void)
|
||||
{
|
||||
struct sigqueue *q = first_free;
|
||||
if (!q)
|
||||
return NULL;
|
||||
first_free = q->next;
|
||||
return q;
|
||||
}
|
||||
|
||||
static inline void free_sigqueue(struct sigqueue *q)
|
||||
{
|
||||
q->next = first_free;
|
||||
first_free = q;
|
||||
}
|
||||
|
||||
/* abort execution with signal */
|
||||
void __attribute((noreturn)) force_sig(int sig)
|
||||
{
|
||||
int host_sig;
|
||||
host_sig = target_to_host_signal(sig);
|
||||
fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n",
|
||||
sig, strsignal(host_sig));
|
||||
_exit(-host_sig);
|
||||
}
|
||||
|
||||
/* queue a signal so that it will be send to the virtual CPU as soon
|
||||
as possible */
|
||||
int queue_signal(int sig, target_siginfo_t *info)
|
||||
{
|
||||
struct emulated_sigaction *k;
|
||||
struct sigqueue *q, **pq;
|
||||
target_ulong handler;
|
||||
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "queue_signal: sig=%d\n",
|
||||
sig);
|
||||
#endif
|
||||
k = &sigact_table[sig - 1];
|
||||
handler = (target_ulong)k->sa.sa_handler;
|
||||
if (handler == SIG_DFL) {
|
||||
/* default handler : ignore some signal. The other are fatal */
|
||||
if (sig != SIGCHLD &&
|
||||
sig != SIGURG &&
|
||||
sig != SIGWINCH) {
|
||||
force_sig(sig);
|
||||
} else {
|
||||
return 0; /* indicate ignored */
|
||||
}
|
||||
} else if (handler == host_to_target_signal(SIG_IGN)) {
|
||||
/* ignore signal */
|
||||
return 0;
|
||||
} else if (handler == host_to_target_signal(SIG_ERR)) {
|
||||
force_sig(sig);
|
||||
} else {
|
||||
pq = &k->first;
|
||||
if (!k->pending) {
|
||||
/* first signal */
|
||||
q = &k->info;
|
||||
} else {
|
||||
q = alloc_sigqueue();
|
||||
if (!q)
|
||||
return -EAGAIN;
|
||||
while (*pq != NULL)
|
||||
pq = &(*pq)->next;
|
||||
}
|
||||
*pq = q;
|
||||
q->info = *info;
|
||||
q->next = NULL;
|
||||
k->pending = 1;
|
||||
/* signal that a new signal is pending */
|
||||
signal_pending = 1;
|
||||
return 1; /* indicates that the signal was queued */
|
||||
}
|
||||
}
|
||||
|
||||
static void host_signal_handler(int host_signum, siginfo_t *info,
|
||||
void *puc)
|
||||
{
|
||||
int sig;
|
||||
target_siginfo_t tinfo;
|
||||
|
||||
/* the CPU emulator uses some host signals to detect exceptions,
|
||||
we we forward to it some signals */
|
||||
if (host_signum == SIGSEGV || host_signum == SIGBUS
|
||||
#if defined(TARGET_I386) && defined(USE_CODE_COPY)
|
||||
|| host_signum == SIGFPE
|
||||
#endif
|
||||
) {
|
||||
if (cpu_signal_handler(host_signum, (void*)info, puc))
|
||||
return;
|
||||
}
|
||||
|
||||
/* get target signal number */
|
||||
sig = host_to_target_signal(host_signum);
|
||||
if (sig < 1 || sig > NSIG)
|
||||
return;
|
||||
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "qemu: got signal %d\n", sig);
|
||||
#endif
|
||||
if (queue_signal(sig, &tinfo) == 1) {
|
||||
/* interrupt the virtual CPU as soon as possible */
|
||||
cpu_interrupt(global_env, CPU_INTERRUPT_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
int do_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss)
|
||||
{
|
||||
/* XXX: test errors */
|
||||
if(oss)
|
||||
{
|
||||
oss->ss_sp = tswap32(target_sigaltstack_used.ss_sp);
|
||||
oss->ss_size = tswap32(target_sigaltstack_used.ss_size);
|
||||
oss->ss_flags = tswap32(target_sigaltstack_used.ss_flags);
|
||||
}
|
||||
if(ss)
|
||||
{
|
||||
target_sigaltstack_used.ss_sp = tswap32(ss->ss_sp);
|
||||
target_sigaltstack_used.ss_size = tswap32(ss->ss_size);
|
||||
target_sigaltstack_used.ss_flags = tswap32(ss->ss_flags);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_sigaction(int sig, const struct sigaction *act,
|
||||
struct sigaction *oact)
|
||||
{
|
||||
struct emulated_sigaction *k;
|
||||
struct sigaction act1;
|
||||
int host_sig;
|
||||
|
||||
if (sig < 1 || sig > NSIG)
|
||||
return -EINVAL;
|
||||
|
||||
k = &sigact_table[sig - 1];
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "sigaction 1 sig=%d act=0x%08x, oact=0x%08x\n",
|
||||
sig, (int)act, (int)oact);
|
||||
#endif
|
||||
if (oact) {
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "sigaction 1 sig=%d act=0x%08x, oact=0x%08x\n",
|
||||
sig, (int)act, (int)oact);
|
||||
#endif
|
||||
|
||||
oact->sa_handler = tswapl(k->sa.sa_handler);
|
||||
oact->sa_flags = tswapl(k->sa.sa_flags);
|
||||
oact->sa_mask = tswapl(k->sa.sa_mask);
|
||||
}
|
||||
if (act) {
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "sigaction handler 0x%x flag 0x%x mask 0x%x\n",
|
||||
act->sa_handler, act->sa_flags, act->sa_mask);
|
||||
#endif
|
||||
|
||||
k->sa.sa_handler = tswapl(act->sa_handler);
|
||||
k->sa.sa_flags = tswapl(act->sa_flags);
|
||||
k->sa.sa_mask = tswapl(act->sa_mask);
|
||||
/* we update the host signal state */
|
||||
host_sig = target_to_host_signal(sig);
|
||||
if (host_sig != SIGSEGV && host_sig != SIGBUS) {
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
fprintf(stderr, "sigaction handler going to call sigaction\n",
|
||||
act->sa_handler, act->sa_flags, act->sa_mask);
|
||||
#endif
|
||||
|
||||
sigfillset(&act1.sa_mask);
|
||||
act1.sa_flags = SA_SIGINFO;
|
||||
if (k->sa.sa_flags & SA_RESTART)
|
||||
act1.sa_flags |= SA_RESTART;
|
||||
/* NOTE: it is important to update the host kernel signal
|
||||
ignore state to avoid getting unexpected interrupted
|
||||
syscalls */
|
||||
if (k->sa.sa_handler == SIG_IGN) {
|
||||
act1.sa_sigaction = (void *)SIG_IGN;
|
||||
} else if (k->sa.sa_handler == SIG_DFL) {
|
||||
act1.sa_sigaction = (void *)SIG_DFL;
|
||||
} else {
|
||||
act1.sa_sigaction = host_signal_handler;
|
||||
}
|
||||
sigaction(host_sig, &act1, NULL);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef TARGET_I386
|
||||
|
||||
static inline void *
|
||||
get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size)
|
||||
{
|
||||
/* XXX Fix that */
|
||||
if(target_sigaltstack_used.ss_flags & SA_DISABLE)
|
||||
{
|
||||
int esp;
|
||||
/* Default to using normal stack */
|
||||
esp = env->regs[R_ESP];
|
||||
|
||||
return (void *)((esp - frame_size) & -8ul);
|
||||
}
|
||||
else
|
||||
{
|
||||
return target_sigaltstack_used.ss_sp;
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_frame(int sig, struct emulated_sigaction *ka,
|
||||
void *set, CPUState *env)
|
||||
{
|
||||
void *frame;
|
||||
int i, err = 0;
|
||||
|
||||
fprintf(stderr, "setup_frame %d\n", sig);
|
||||
frame = get_sigframe(ka, env, sizeof(*frame));
|
||||
|
||||
/* Set up registers for signal handler */
|
||||
env->regs[R_ESP] = (unsigned long) frame;
|
||||
env->eip = (unsigned long) ka->sa.sa_handler;
|
||||
|
||||
env->eflags &= ~TF_MASK;
|
||||
|
||||
return;
|
||||
|
||||
give_sigsegv:
|
||||
if (sig == SIGSEGV)
|
||||
ka->sa.sa_handler = SIG_DFL;
|
||||
force_sig(SIGSEGV /* , current */);
|
||||
}
|
||||
|
||||
long do_sigreturn(CPUState *env, int num)
|
||||
{
|
||||
int i = 0;
|
||||
struct target_sigcontext *scp = get_int_arg(&i, env);
|
||||
/* XXX Get current signal number */
|
||||
/* XXX Adjust accordin to sc_onstack, sc_mask */
|
||||
if(tswapl(scp->sc_onstack) & 0x1)
|
||||
target_sigaltstack_used.ss_flags |= ~SA_DISABLE;
|
||||
else
|
||||
target_sigaltstack_used.ss_flags &= SA_DISABLE;
|
||||
int set = tswapl(scp->sc_eax);
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
|
||||
fprintf(stderr, "do_sigreturn: partially implemented %x EAX:%x EBX:%x\n", scp->sc_mask, tswapl(scp->sc_eax), tswapl(scp->sc_ebx));
|
||||
fprintf(stderr, "ECX:%x EDX:%x EDI:%x\n", scp->sc_ecx, tswapl(scp->sc_edx), tswapl(scp->sc_edi));
|
||||
fprintf(stderr, "EIP:%x\n", tswapl(scp->sc_eip));
|
||||
|
||||
env->regs[R_EAX] = tswapl(scp->sc_eax);
|
||||
env->regs[R_EBX] = tswapl(scp->sc_ebx);
|
||||
env->regs[R_ECX] = tswapl(scp->sc_ecx);
|
||||
env->regs[R_EDX] = tswapl(scp->sc_edx);
|
||||
env->regs[R_EDI] = tswapl(scp->sc_edi);
|
||||
env->regs[R_ESI] = tswapl(scp->sc_esi);
|
||||
env->regs[R_EBP] = tswapl(scp->sc_ebp);
|
||||
env->regs[R_ESP] = tswapl(scp->sc_esp);
|
||||
env->segs[R_SS].selector = (void*)tswapl(scp->sc_ss);
|
||||
env->eflags = tswapl(scp->sc_eflags);
|
||||
env->eip = tswapl(scp->sc_eip);
|
||||
env->segs[R_CS].selector = (void*)tswapl(scp->sc_cs);
|
||||
env->segs[R_DS].selector = (void*)tswapl(scp->sc_ds);
|
||||
env->segs[R_ES].selector = (void*)tswapl(scp->sc_es);
|
||||
env->segs[R_FS].selector = (void*)tswapl(scp->sc_fs);
|
||||
env->segs[R_GS].selector = (void*)tswapl(scp->sc_gs);
|
||||
|
||||
/* Again, because our caller's caller will reset EAX */
|
||||
return env->regs[R_EAX];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void setup_frame(int sig, struct emulated_sigaction *ka,
|
||||
void *set, CPUState *env)
|
||||
{
|
||||
fprintf(stderr, "setup_frame: not implemented\n");
|
||||
}
|
||||
|
||||
long do_sigreturn(CPUState *env, int num)
|
||||
{
|
||||
int i = 0;
|
||||
struct target_sigcontext *scp = get_int_arg(&i, env);
|
||||
fprintf(stderr, "do_sigreturn: not implemented\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void process_pending_signals(void *cpu_env)
|
||||
{
|
||||
struct emulated_sigaction *k;
|
||||
struct sigqueue *q;
|
||||
target_ulong handler;
|
||||
int sig;
|
||||
|
||||
if (!signal_pending)
|
||||
return;
|
||||
|
||||
k = sigact_table;
|
||||
|
||||
for(sig = 1; sig <= NSIG; sig++) {
|
||||
if (k->pending)
|
||||
goto handle_signal;
|
||||
k++;
|
||||
}
|
||||
|
||||
/* if no signal is pending, just return */
|
||||
signal_pending = 0;
|
||||
return;
|
||||
handle_signal:
|
||||
#ifdef DEBUG_SIGNAL
|
||||
fprintf(stderr, "qemu: process signal %d\n", sig);
|
||||
#endif
|
||||
/* dequeue signal */
|
||||
q = k->first;
|
||||
k->first = q->next;
|
||||
if (!k->first)
|
||||
k->pending = 0;
|
||||
|
||||
sig = gdb_handlesig (cpu_env, sig);
|
||||
if (!sig) {
|
||||
fprintf (stderr, "Lost signal\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
handler = k->sa.sa_handler;
|
||||
if (handler == SIG_DFL) {
|
||||
/* default handler : ignore some signal. The other are fatal */
|
||||
if (sig != SIGCHLD &&
|
||||
sig != SIGURG &&
|
||||
sig != SIGWINCH) {
|
||||
force_sig(sig);
|
||||
}
|
||||
} else if (handler == SIG_IGN) {
|
||||
/* ignore sig */
|
||||
} else if (handler == SIG_ERR) {
|
||||
force_sig(sig);
|
||||
} else {
|
||||
|
||||
setup_frame(sig, k, 0, cpu_env);
|
||||
if (k->sa.sa_flags & SA_RESETHAND)
|
||||
k->sa.sa_handler = SIG_DFL;
|
||||
}
|
||||
if (q != &k->info)
|
||||
free_sigqueue(q);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,384 @@
|
||||
/* generated from xnu/bsd/kern/syscalls.master */
|
||||
|
||||
ENTRY("syscall", SYS_syscall, do_unix_syscall_indirect, 0, CALL_INDIRECT, VOID) /* 0 indirect syscall */
|
||||
ENTRY("exit", SYS_exit, do_exit, 1, CALL_DIRECT, INT) /* 1 */
|
||||
ENTRY("fork", SYS_fork, fork, 0, CALL_NOERRNO, VOID) /* 2 */
|
||||
ENTRY("read", SYS_read, do_read, 3, CALL_DIRECT, INT, PTR, SIZE) /* 3 */
|
||||
ENTRY("write", SYS_write, write, 3, CALL_DIRECT, INT, PTR, SIZE) /* 4 */
|
||||
ENTRY("open", SYS_open, do_open, 3, CALL_DIRECT, PTR, INT, INT) /* 5 */
|
||||
ENTRY("close", SYS_close, close, 1, CALL_DIRECT, INT) /* 6 */
|
||||
ENTRY("wait4", SYS_wait4, wait4, 4, CALL_DIRECT, INT, PTR, INT, PTR) /* 7 */
|
||||
ENTRY("", 8, no_syscall, 0, CALL_INDIRECT, VOID) /* 8 old creat */
|
||||
ENTRY("link", SYS_link, link, 2, CALL_DIRECT, PTR, PTR) /* 9 */
|
||||
ENTRY("unlink", SYS_unlink, unlink, 1, CALL_DIRECT, PTR) /* 10 */
|
||||
ENTRY("", 11, no_syscall, 0, CALL_INDIRECT, VOID) /* 11 old execv */
|
||||
ENTRY("chdir", SYS_chdir, chdir, 1, CALL_DIRECT, PTR) /* 12 */
|
||||
ENTRY("fchdir", SYS_fchdir, fchdir, 1, CALL_DIRECT, INT) /* 13 */
|
||||
ENTRY("mknod", SYS_mknod, mknod, 3, CALL_DIRECT, PTR, INT, INT) /* 14 */
|
||||
ENTRY("chmod", SYS_chmod, chmod, 2, CALL_DIRECT, PTR, INT) /* 15 */
|
||||
ENTRY("chown", SYS_chown, chown, 3, CALL_DIRECT, PTR, INT, INT) /* 16 */
|
||||
ENTRY("obreak", SYS_obreak, no_syscall, 1, CALL_INDIRECT, VOID) /* 17 old break */
|
||||
ENTRY("ogetfsstat", 18, unimpl_unix_syscall, 3, CALL_INDIRECT, PTR, INT, INT) /* 18 */
|
||||
ENTRY("", 19, no_syscall, 0, CALL_INDIRECT, VOID) /* 19 old lseek */
|
||||
ENTRY("getpid", SYS_getpid, getpid, 0, CALL_NOERRNO, VOID) /* 20 */
|
||||
ENTRY("", 21, no_syscall, 0, CALL_INDIRECT, VOID) /* 21 old mount */
|
||||
ENTRY("", 22, no_syscall, 0, CALL_INDIRECT, VOID) /* 22 old umount */
|
||||
ENTRY("setuid", SYS_setuid, setuid, 1, CALL_DIRECT, INT) /* 23 */
|
||||
ENTRY("getuid", SYS_getuid, getuid, 0, CALL_NOERRNO, VOID) /* 24 */
|
||||
ENTRY("geteuid", SYS_geteuid, geteuid, 0, CALL_NOERRNO, VOID) /* 25 */
|
||||
ENTRY("ptrace", SYS_ptrace, ptrace, 4, CALL_DIRECT, INT, INT, PTR, INT) /* 26 */
|
||||
ENTRY("recvmsg", SYS_recvmsg, recvmsg, 3, CALL_DIRECT, INT, PTR, INT) /* 27 */
|
||||
ENTRY("sendmsg", SYS_sendmsg, sendmsg, 3, CALL_DIRECT, INT, PTR, INT) /* 28 */
|
||||
ENTRY("recvfrom", SYS_recvfrom, recvfrom, 6, CALL_DIRECT, INT, PTR, INT, INT, PTR, PTR) /* 29 */
|
||||
ENTRY("accept", SYS_accept, accept, 3, CALL_DIRECT, INT, PTR, PTR) /* 30 */
|
||||
ENTRY("getpeername", SYS_getpeername, getpeername, 3, CALL_DIRECT, INT, PTR, PTR) /* 31 */
|
||||
ENTRY("getsockname", SYS_getsockname, getsockname, 3, CALL_DIRECT, INT, PTR, PTR) /* 32 */
|
||||
ENTRY("access", SYS_access, access, 2, CALL_DIRECT, PTR, INT) /* 33 */
|
||||
ENTRY("chflags", SYS_chflags, chflags, 2, CALL_DIRECT, PTR, INT) /* 34 */
|
||||
ENTRY("fchflags", SYS_fchflags, fchflags, 2, CALL_DIRECT, INT, INT) /* 35 */
|
||||
ENTRY("sync", SYS_sync, do_sync, 0, CALL_INDIRECT, VOID) /* 36 */
|
||||
ENTRY("kill", SYS_kill, kill, 2, CALL_DIRECT, INT, INT) /* 37 */
|
||||
ENTRY("", 38, no_syscall, 0, CALL_INDIRECT, VOID) /* 38 old stat */
|
||||
ENTRY("getppid", SYS_getppid, getppid, 0, CALL_DIRECT, VOID) /* 39 */
|
||||
ENTRY("", 40, no_syscall, 0, CALL_INDIRECT, VOID) /* 40 old lstat */
|
||||
ENTRY("dup", SYS_dup, dup, 1, CALL_DIRECT, INT) /* 41 */
|
||||
ENTRY("pipe", SYS_pipe, unimpl_unix_syscall, 0, CALL_INDIRECT, PTR) /* 42 */
|
||||
ENTRY("getegid", SYS_getegid, getegid, 0, CALL_NOERRNO, VOID) /* 43 */
|
||||
ENTRY("profil", SYS_profil, profil, 4, CALL_DIRECT, PTR, SIZE, INT, INT) /* 44 */
|
||||
ENTRY("ktrace", SYS_ktrace, no_syscall, 4, CALL_INDIRECT, VOID) /* 45 */
|
||||
ENTRY("sigaction", SYS_sigaction, do_sigaction, 3, CALL_DIRECT, INT, PTR, PTR) /* 46 */
|
||||
ENTRY("getgid", SYS_getgid, getgid, 0, CALL_NOERRNO, VOID) /* 47 */
|
||||
ENTRY("sigprocmask", SYS_sigprocmask, do_sigprocmask, 3, CALL_DIRECT, INT, PTR, PTR) /* 48 */
|
||||
ENTRY("getlogin", SYS_getlogin, do_getlogin, 2, CALL_DIRECT, PTR, UINT) /* 49 XXX */
|
||||
ENTRY("setlogin", SYS_setlogin, setlogin, 1, CALL_DIRECT, PTR) /* 50 */
|
||||
ENTRY("acct", SYS_acct, acct, 1, CALL_DIRECT, PTR) /* 51 */
|
||||
ENTRY("sigpending", SYS_sigpending, sigpending, 1, CALL_DIRECT, PTR) /* 52 */
|
||||
ENTRY("sigaltstack", SYS_sigaltstack, do_sigaltstack, 2, CALL_DIRECT, PTR, PTR) /* 53 */
|
||||
ENTRY("ioctl", SYS_ioctl, do_ioctl, 3, CALL_DIRECT, INT, INT, INT) /* 54 */
|
||||
ENTRY("reboot", SYS_reboot, unimpl_unix_syscall, 2, CALL_INDIRECT, INT, PTR) /* 55 */
|
||||
ENTRY("revoke", SYS_revoke, revoke, 1, CALL_DIRECT, PTR) /* 56 */
|
||||
ENTRY("symlink", SYS_symlink, symlink, 2, CALL_DIRECT, PTR, PTR) /* 57 */
|
||||
ENTRY("readlink", SYS_readlink, readlink, 3, CALL_DIRECT, PTR, PTR, INT) /* 58 */
|
||||
ENTRY("execve", SYS_execve, do_execve, 3, CALL_DIRECT, PTR, PTR, PTR) /* 59 */
|
||||
ENTRY("umask", SYS_umask, umask, 1, CALL_DIRECT, INT) /* 60 */
|
||||
ENTRY("chroot", SYS_chroot, chroot, 1, CALL_DIRECT, PTR) /* 61 */
|
||||
ENTRY("", 62, no_syscall, 0, CALL_INDIRECT, VOID) /* 62 old fstat */
|
||||
ENTRY("", 63, no_syscall, 0, CALL_INDIRECT, VOID) /* 63 used internally , reserved */
|
||||
ENTRY("", 64, no_syscall, 0, CALL_INDIRECT, VOID) /* 64 old getpagesize */
|
||||
ENTRY("msync", SYS_msync, target_msync, 3, CALL_DIRECT, UINT /*PTR*/, SIZE, INT) /* 65 */
|
||||
ENTRY("vfork", SYS_vfork, vfork, 0, CALL_DIRECT, VOID) /* 66 */
|
||||
ENTRY("", 67, no_syscall, 0, CALL_INDIRECT, VOID) /* 67 old vread */
|
||||
ENTRY("", 68, no_syscall, 0, CALL_INDIRECT, VOID) /* 68 old vwrite */
|
||||
ENTRY("sbrk", SYS_sbrk, sbrk, 1, CALL_DIRECT, INT) /* 69 */
|
||||
ENTRY("sstk", SYS_sstk, no_syscall, 1, CALL_INDIRECT, VOID) /* 70 */
|
||||
ENTRY("", 71, no_syscall, 0, CALL_INDIRECT, VOID) /* 71 old mmap */
|
||||
ENTRY("ovadvise", SYS_ovadvise, no_syscall, 0, CALL_INDIRECT, VOID) /* 72 old vadvise */
|
||||
ENTRY("munmap", SYS_munmap, target_munmap, 2, CALL_DIRECT, UINT /* PTR */, SIZE) /* 73 */
|
||||
ENTRY("mprotect", SYS_mprotect, mprotect, 3, CALL_DIRECT, PTR, SIZE, INT) /* 74 */
|
||||
ENTRY("madvise", SYS_madvise, madvise, 3, CALL_DIRECT, PTR, SIZE, INT) /* 75 */
|
||||
ENTRY("", 76, no_syscall, 0, CALL_INDIRECT, VOID) /* 76 old vhangup */
|
||||
ENTRY("", 77, no_syscall, 0, CALL_INDIRECT, VOID) /* 77 old vlimit */
|
||||
ENTRY("mincore", SYS_mincore, mincore, 3, CALL_DIRECT, PTR, SIZE, PTR) /* 78 */
|
||||
ENTRY("getgroups", SYS_getgroups, do_getgroups, 2, CALL_DIRECT, UINT, PTR) /* 79 */
|
||||
ENTRY("setgroups", SYS_setgroups, setgroups, 2, CALL_DIRECT, UINT, PTR) /* 80 */
|
||||
ENTRY("getpgrp", SYS_getpgrp, getpgrp, 0, CALL_DIRECT, VOID) /* 81 */
|
||||
ENTRY("setpgid", SYS_setpgid, setpgid, 2, CALL_DIRECT, INT, INT) /* 82 */
|
||||
ENTRY("setitimer", SYS_setitimer, setitimer, 3, CALL_DIRECT, INT, PTR, PTR) /* 83 */
|
||||
ENTRY("", 84, no_syscall, 0, CALL_INDIRECT, VOID) /* 84 old wait */
|
||||
ENTRY("swapon", SYS_swapon, unimpl_unix_syscall, 0, CALL_INDIRECT, VOID) /* 85 */
|
||||
ENTRY("getitimer", SYS_getitimer, getitimer, 2, CALL_DIRECT, INT, PTR) /* 86 */
|
||||
ENTRY("", 87, no_syscall, 0, CALL_INDIRECT, VOID) /* 87 old gethostname */
|
||||
ENTRY("", 88, no_syscall, 0, CALL_INDIRECT, VOID) /* 88 old sethostname */
|
||||
ENTRY("getdtablesize", SYS_getdtablesize, getdtablesize, 0, CALL_DIRECT, VOID) /* 89 */
|
||||
ENTRY("dup2", SYS_dup2, dup2, 2, CALL_DIRECT, INT, INT) /* 90 */
|
||||
ENTRY("", 91, no_syscall, 0, CALL_INDIRECT, VOID) /* 91 old getdopt */
|
||||
ENTRY("fcntl", SYS_fcntl, do_fcntl, 3, CALL_DIRECT, INT, INT, INT) /* 92 */
|
||||
ENTRY("select", SYS_select, select, 5, CALL_DIRECT, INT, PTR, PTR, PTR, PTR) /* 93 */
|
||||
ENTRY("", 94, no_syscall, 0, CALL_INDIRECT, VOID) /* 94 old setdopt */
|
||||
ENTRY("fsync", SYS_fsync, fsync, 1, CALL_DIRECT, INT) /* 95 */
|
||||
ENTRY("setpriority", SYS_setpriority, setpriority, 3, CALL_DIRECT, INT, INT, INT) /* 96 */
|
||||
ENTRY("socket", SYS_socket, socket, 3, CALL_DIRECT, INT, INT, INT) /* 97 */
|
||||
ENTRY("connect", SYS_connect, connect, 3, CALL_DIRECT, INT, PTR, INT) /* 98 */
|
||||
ENTRY("", 99, no_syscall, 0, CALL_INDIRECT, VOID) /* 99 old accept */
|
||||
ENTRY("getpriority", SYS_getpriority, getpriority, 2, CALL_DIRECT, INT, INT) /* 100 */
|
||||
ENTRY("", 101, no_syscall, 0, CALL_INDIRECT, VOID) /* 101 old send */
|
||||
ENTRY("", 102, no_syscall, 0, CALL_INDIRECT, VOID) /* 102 old recv */
|
||||
ENTRY("", 103, no_syscall, 0, CALL_INDIRECT, VOID) /* 103 old sigreturn */
|
||||
ENTRY("bind", SYS_bind, bind, 3, CALL_DIRECT, INT, PTR, INT) /* 104 */
|
||||
ENTRY("setsockopt", SYS_setsockopt, setsockopt, 5, CALL_DIRECT, INT, INT, INT, PTR, INT) /* 105 */
|
||||
ENTRY("listen", SYS_listen, listen, 2, CALL_DIRECT, INT, INT) /* 106 */
|
||||
ENTRY("", 107, no_syscall, 0, CALL_INDIRECT, VOID) /* 107 old vtimes */
|
||||
ENTRY("", 108, no_syscall, 0, CALL_INDIRECT, VOID) /* 108 old sigvec */
|
||||
ENTRY("", 109, no_syscall, 0, CALL_INDIRECT, VOID) /* 109 old sigblock */
|
||||
ENTRY("", 110, no_syscall, 0, CALL_INDIRECT, VOID) /* 110 old sigsetmask */
|
||||
ENTRY("sigsuspend", SYS_sigsuspend, unimpl_unix_syscall, 1, CALL_INDIRECT, INT) /* 111 */
|
||||
ENTRY("", 112, no_syscall, 0, CALL_INDIRECT, VOID) /* 112 old sigstack */
|
||||
ENTRY("", 113, no_syscall, 0, CALL_INDIRECT, VOID) /* 113 old recvmsg */
|
||||
ENTRY("", 114, no_syscall, 0, CALL_INDIRECT, VOID) /* 114 old sendmsg */
|
||||
ENTRY("", 115, no_syscall, 0, CALL_INDIRECT, VOID) /* 115 old vtrace */
|
||||
ENTRY("gettimeofday", SYS_gettimeofday, do_gettimeofday, 2, CALL_DIRECT, PTR, PTR) /* 116 */
|
||||
ENTRY("getrusage", SYS_getrusage, getrusage, 2, CALL_DIRECT, INT, PTR) /* 117 */
|
||||
ENTRY("getsockopt", SYS_getsockopt, getsockopt, 5, CALL_DIRECT, INT, INT, INT, PTR, PTR) /* 118 */
|
||||
ENTRY("", 119, no_syscall, 0, CALL_INDIRECT, VOID) /* 119 old resuba */
|
||||
ENTRY("readv", SYS_readv, do_readv, 3, CALL_DIRECT, INT, PTR, UINT) /* 120 */
|
||||
ENTRY("writev", SYS_writev, do_writev, 3, CALL_DIRECT, INT, PTR, UINT) /* 121 */
|
||||
ENTRY("settimeofday", SYS_settimeofday, settimeofday, 2, CALL_DIRECT, PTR, PTR) /* 122 */
|
||||
ENTRY("fchown", SYS_fchown, fchown, 3, CALL_DIRECT, INT, INT, INT) /* 123 */
|
||||
ENTRY("fchmod", SYS_fchmod, fchmod, 2, CALL_DIRECT, INT, INT) /* 124 */
|
||||
ENTRY("", 125, no_syscall, 0, CALL_INDIRECT, VOID) /* 125 old recvfrom */
|
||||
ENTRY("", 126, no_syscall, 0, CALL_INDIRECT, VOID) /* 126 old setreuid */
|
||||
ENTRY("", 127, no_syscall, 0, CALL_INDIRECT, VOID) /* 127 old setregid */
|
||||
ENTRY("rename", SYS_rename, rename, 2, CALL_DIRECT, PTR, PTR) /* 128 */
|
||||
ENTRY("", 129, no_syscall, 0, CALL_INDIRECT, VOID) /* 129 old truncate */
|
||||
ENTRY("", 130, no_syscall, 0, CALL_INDIRECT, VOID) /* 130 old ftruncate */
|
||||
ENTRY("flock", SYS_flock, flock, 2, CALL_DIRECT, INT, INT) /* 131 */
|
||||
ENTRY("mkfifo", SYS_mkfifo, mkfifo, 2, CALL_DIRECT, PTR, INT) /* 132 */
|
||||
ENTRY("sendto", SYS_sendto, sendto, 6, CALL_DIRECT, INT, PTR, SIZE, INT, PTR, INT) /* 133 */
|
||||
ENTRY("shutdown", SYS_shutdown, shutdown, 2, CALL_DIRECT, INT, INT) /* 134 */
|
||||
ENTRY("socketpair", SYS_socketpair, socketpair, 4, CALL_DIRECT, INT, INT, INT, PTR) /* 135 */
|
||||
ENTRY("mkdir", SYS_mkdir, mkdir, 2, CALL_DIRECT, PTR, INT) /* 136 */
|
||||
ENTRY("rmdir", SYS_rmdir, rmdir, 1, CALL_DIRECT, PTR) /* 137 */
|
||||
ENTRY("utimes", SYS_utimes, do_utimes, 2, CALL_DIRECT, PTR, PTR) /* 138 */
|
||||
ENTRY("futimes", SYS_futimes, do_futimes, 2, CALL_DIRECT, INT, PTR) /* 139 */
|
||||
ENTRY("adjtime", SYS_adjtime, adjtime, 2, CALL_DIRECT, PTR, PTR) /* 140 */
|
||||
ENTRY("", 141, no_syscall, 0, CALL_INDIRECT, VOID) /* 141 old getpeername */
|
||||
ENTRY("", 142, no_syscall, 0, CALL_INDIRECT, VOID) /* 142 old gethostid */
|
||||
ENTRY("", 143, no_syscall, 0, CALL_INDIRECT, VOID) /* 143 old sethostid */
|
||||
ENTRY("", 144, no_syscall, 0, CALL_INDIRECT, VOID) /* 144 old getrlimit */
|
||||
ENTRY("", 145, no_syscall, 0, CALL_INDIRECT, VOID) /* 145 old setrlimit */
|
||||
ENTRY("", 146, no_syscall, 0, CALL_INDIRECT, VOID) /* 146 old killpg */
|
||||
ENTRY("setsid", SYS_setsid, setsid, 0, CALL_DIRECT, VOID) /* 147 */
|
||||
ENTRY("", 148, no_syscall, 0, CALL_INDIRECT, VOID) /* 148 old setquota */
|
||||
ENTRY("", 149, no_syscall, 0, CALL_INDIRECT, VOID) /* 149 old qquota */
|
||||
ENTRY("", 150, no_syscall, 0, CALL_INDIRECT, VOID) /* 150 old getsockname */
|
||||
ENTRY("getpgid", SYS_getpgid, getpgid, 1, CALL_DIRECT, INT) /* 151 */
|
||||
ENTRY("setprivexec", SYS_setprivexec, no_syscall, 1, CALL_INDIRECT, VOID) /* 152 */
|
||||
ENTRY("pread", SYS_pread, do_pread, 4, CALL_DIRECT, INT, PTR, SIZE, OFFSET) /* 153 */
|
||||
ENTRY("pwrite", SYS_pwrite, pwrite, 4, CALL_DIRECT, INT, PTR, SIZE, OFFSET) /* 154 */
|
||||
#ifdef SYS_nfssvc
|
||||
ENTRY("nfssvc", SYS_nfssvc, nfssvc, 2, CALL_DIRECT, INT, PTR) /* 155 */
|
||||
#else
|
||||
ENTRY("nfssvc", 155, no_syscall, 2, CALL_INDIRECT, VOID) /* 155 */
|
||||
#endif
|
||||
ENTRY("", 155, no_syscall, 0, CALL_INDIRECT, VOID) /* 155 */
|
||||
ENTRY("", 156, no_syscall, 0, CALL_INDIRECT, VOID) /* 156 old getdirentries */
|
||||
ENTRY("statfs", SYS_statfs, do_statfs, 2, CALL_DIRECT, PTR, PTR) /* 157 */
|
||||
ENTRY("fstatfs", SYS_fstatfs, do_fstatfs, 2, CALL_DIRECT, INT, PTR) /* 158 */
|
||||
ENTRY("unmount", SYS_unmount, unmount, 2, CALL_DIRECT, PTR, INT) /* 159 */
|
||||
ENTRY("", 160, no_syscall, 0, CALL_INDIRECT, VOID) /* 160 old async_daemon */
|
||||
ENTRY("", 161, no_syscall, 0, CALL_INDIRECT, VOID) /* 161 */
|
||||
ENTRY("", 162, no_syscall, 0, CALL_INDIRECT, VOID) /* 162 old getdomainname */
|
||||
ENTRY("", 163, no_syscall, 0, CALL_INDIRECT, VOID) /* 163 old setdomainname */
|
||||
ENTRY("", 164, no_syscall, 0, CALL_INDIRECT, VOID) /* 164 */
|
||||
ENTRY("quotactl", SYS_quotactl, no_syscall, 4, CALL_INDIRECT, VOID) /* 165 */
|
||||
ENTRY("", 166, no_syscall, 0, CALL_INDIRECT, VOID) /* 166 old exportfs */
|
||||
ENTRY("mount", SYS_mount, mount, 4, CALL_DIRECT, PTR, PTR, INT, PTR) /* 167 */
|
||||
ENTRY("", 168, no_syscall, 0, CALL_INDIRECT, VOID) /* 168 old ustat */
|
||||
ENTRY("", 169, no_syscall, 0, CALL_INDIRECT, VOID) /* 169 */
|
||||
ENTRY("table", SYS_table, no_syscall, 0, CALL_INDIRECT, VOID) /* 170 old table */
|
||||
ENTRY("", 171, no_syscall, 0, CALL_INDIRECT, VOID) /* 171 old wait3 */
|
||||
ENTRY("", 172, no_syscall, 0, CALL_INDIRECT, VOID) /* 172 old rpause */
|
||||
ENTRY("waitid", SYS_waitid, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 173 */
|
||||
ENTRY("", 174, no_syscall, 0, CALL_INDIRECT, VOID) /* 174 old getdents */
|
||||
ENTRY("", 175, no_syscall, 0, CALL_INDIRECT, VOID) /* 175 old gc_control */
|
||||
ENTRY("add_profil", SYS_add_profil, add_profil, 4, CALL_DIRECT, PTR, SIZE, UINT, UINT) /* 176 */
|
||||
ENTRY("", 177, no_syscall, 0, CALL_INDIRECT, VOID) /* 177 */
|
||||
ENTRY("", 178, no_syscall, 0, CALL_INDIRECT, VOID) /* 178 */
|
||||
ENTRY("", 179, no_syscall, 0, CALL_INDIRECT, VOID) /* 179 */
|
||||
ENTRY("kdebug_trace", SYS_kdebug_trace, no_syscall, 6, CALL_INDIRECT, VOID) /* 180 */
|
||||
ENTRY("setgid", SYS_setgid, setgid, 1, CALL_DIRECT, INT) /* 181 */
|
||||
ENTRY("setegid", SYS_setegid, setegid, 1, CALL_DIRECT, INT) /* 182 */
|
||||
ENTRY("seteuid", SYS_seteuid, seteuid, 1, CALL_DIRECT, INT) /* 183 */
|
||||
ENTRY("sigreturn", SYS_sigreturn, do_sigreturn, 2, CALL_INDIRECT, PTR, INT) /* 184 */
|
||||
ENTRY("chud", SYS_chud, unimpl_unix_syscall, 6, CALL_INDIRECT, VOID) /* 185 */
|
||||
ENTRY("", 186, no_syscall, 0, CALL_INDIRECT, VOID) /* 186 */
|
||||
ENTRY("", 187, no_syscall, 0, CALL_INDIRECT, VOID) /* 187 */
|
||||
ENTRY("stat", SYS_stat, do_stat, 2, CALL_DIRECT, PTR, PTR) /* 188 */
|
||||
ENTRY("fstat", SYS_fstat, do_fstat, 2, CALL_DIRECT, INT, PTR) /* 189 */
|
||||
ENTRY("lstat", SYS_lstat, do_lstat, 2, CALL_DIRECT, PTR, PTR) /* 190 */
|
||||
ENTRY("pathconf", SYS_pathconf, pathconf, 2, CALL_DIRECT, PTR, INT) /* 191 */
|
||||
ENTRY("fpathconf", SYS_fpathconf, fpathconf, 2, CALL_DIRECT, INT, INT) /* 192 */
|
||||
ENTRY("getfsstat", SYS_getfsstat, do_getfsstat, 3, CALL_DIRECT, PTR, INT, INT) /* 193 */
|
||||
ENTRY("", 193, no_syscall, 0, CALL_INDIRECT, VOID) /* 193 */
|
||||
ENTRY("getrlimit", SYS_getrlimit, getrlimit, 2, CALL_DIRECT, UINT, PTR) /* 194 */
|
||||
ENTRY("setrlimit", SYS_setrlimit, setrlimit, 2, CALL_DIRECT, UINT, PTR) /* 195 */
|
||||
ENTRY("getdirentries", SYS_getdirentries, do_getdirentries, 4, CALL_DIRECT, INT, PTR, UINT, PTR) /* 196 */
|
||||
ENTRY("mmap", SYS_mmap, target_mmap, 6, CALL_DIRECT, UINT /*PTR*/, SIZE, INT, INT, INT, OFFSET) /* 197 */
|
||||
ENTRY("", 198, no_syscall, 0, CALL_INDIRECT, VOID) /* 198 __syscall */
|
||||
ENTRY("lseek", SYS_lseek, do_lseek, 3, CALL_INDIRECT, INT, OFFSET, INT) /* 199 */
|
||||
ENTRY("truncate", SYS_truncate, truncate, 2, CALL_DIRECT, PTR, OFFSET) /* 200 */
|
||||
ENTRY("ftruncate", SYS_ftruncate, ftruncate, 2, CALL_DIRECT, INT, OFFSET) /* 201 */
|
||||
ENTRY("__sysctl", SYS___sysctl, do___sysctl, 6, CALL_DIRECT, PTR, INT, PTR, PTR, PTR, SIZE) /* 202 */
|
||||
ENTRY("mlock", SYS_mlock, mlock, 2, CALL_DIRECT, PTR, SIZE) /* 203 */
|
||||
ENTRY("munlock", SYS_munlock, munlock, 2, CALL_DIRECT, PTR, SIZE) /* 204 */
|
||||
ENTRY("undelete", SYS_undelete, undelete, 1, CALL_DIRECT, PTR) /* 205 */
|
||||
ENTRY("ATsocket", SYS_ATsocket, no_syscall, 1, CALL_INDIRECT, VOID) /* 206 */
|
||||
ENTRY("ATgetmsg", SYS_ATgetmsg, no_syscall, 4, CALL_INDIRECT, VOID) /* 207 */
|
||||
ENTRY("ATputmsg", SYS_ATputmsg, no_syscall, 4, CALL_INDIRECT, VOID) /* 208 */
|
||||
ENTRY("ATPsndreq", SYS_ATPsndreq, no_syscall, 4, CALL_INDIRECT, VOID) /* 209 */
|
||||
ENTRY("ATPsndrsp", SYS_ATPsndrsp, no_syscall, 4, CALL_INDIRECT, VOID) /* 210 */
|
||||
ENTRY("ATPgetreq", SYS_ATPgetreq, no_syscall, 3, CALL_INDIRECT, VOID) /* 211 */
|
||||
ENTRY("ATPgetrsp", SYS_ATPgetrsp, no_syscall, 2, CALL_INDIRECT, VOID) /* 212 */
|
||||
ENTRY("", 213, no_syscall, 0, CALL_INDIRECT, VOID) /* 213 Reserved for AppleTalk */
|
||||
ENTRY("kqueue_from_portset_np", SYS_kqueue_from_portset_np, no_syscall, 1, CALL_INDIRECT, VOID) /* 214 */
|
||||
ENTRY("kqueue_portset_np", SYS_kqueue_portset_np, no_syscall, 1, CALL_INDIRECT, VOID) /* 215 */
|
||||
ENTRY("mkcomplex", SYS_mkcomplex, no_syscall, 3, CALL_INDIRECT, VOID) /* 216 soon to be obsolete */
|
||||
ENTRY("statv", SYS_statv, no_syscall, 2, CALL_INDIRECT, VOID) /* 217 soon to be obsolete */
|
||||
ENTRY("lstatv", SYS_lstatv, no_syscall, 2, CALL_INDIRECT, VOID) /* 218 soon to be obsolete */
|
||||
ENTRY("fstatv", SYS_fstatv, no_syscall, 2, CALL_INDIRECT, VOID) /* 219 soon to be obsolete */
|
||||
ENTRY("getattrlist", SYS_getattrlist, do_getattrlist, 5, CALL_DIRECT, PTR, PTR, PTR, SIZE, UINT) /* 220 */
|
||||
ENTRY("setattrlist", SYS_setattrlist, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 221 */
|
||||
ENTRY("getdirentriesattr", SYS_getdirentriesattr, do_getdirentriesattr, 8, CALL_DIRECT, INT, PTR, PTR, SIZE, PTR, PTR, PTR, UINT) /* 222 */
|
||||
ENTRY("exchangedata", SYS_exchangedata, exchangedata, 3, CALL_DIRECT, PTR, PTR, UINT) /* 223 */
|
||||
ENTRY("checkuseraccess", SYS_checkuseraccess, checkuseraccess, 6, CALL_DIRECT, PTR, INT, PTR, INT, INT, UINT) /* 224 */
|
||||
ENTRY("", 224, no_syscall, 0, CALL_INDIRECT, VOID) /* 224 HFS checkuseraccess check access to a file */
|
||||
ENTRY("searchfs", SYS_searchfs, searchfs, 6, CALL_DIRECT, PTR, PTR, PTR, UINT, UINT, PTR) /* 225 */
|
||||
ENTRY("delete", SYS_delete, no_syscall, 1, CALL_INDIRECT, VOID) /* 226 private delete ( Carbon semantics ) */
|
||||
ENTRY("copyfile", SYS_copyfile, no_syscall, 4, CALL_INDIRECT, VOID) /* 227 */
|
||||
ENTRY("", 228, no_syscall, 0, CALL_INDIRECT, VOID) /* 228 */
|
||||
ENTRY("", 229, no_syscall, 0, CALL_INDIRECT, VOID) /* 229 */
|
||||
ENTRY("poll", SYS_poll, no_syscall, 3, CALL_INDIRECT, VOID) /* 230 */
|
||||
ENTRY("watchevent", SYS_watchevent, no_syscall, 2, CALL_INDIRECT, VOID) /* 231 */
|
||||
ENTRY("waitevent", SYS_waitevent, no_syscall, 2, CALL_INDIRECT, VOID) /* 232 */
|
||||
ENTRY("modwatch", SYS_modwatch, no_syscall, 2, CALL_INDIRECT, VOID) /* 233 */
|
||||
ENTRY("getxattr", SYS_getxattr, no_syscall, 6, CALL_INDIRECT, VOID) /* 234 */
|
||||
ENTRY("fgetxattr", SYS_fgetxattr, no_syscall, 6, CALL_INDIRECT, VOID) /* 235 */
|
||||
ENTRY("setxattr", SYS_setxattr, no_syscall, 6, CALL_INDIRECT, VOID) /* 236 */
|
||||
ENTRY("fsetxattr", SYS_fsetxattr, no_syscall, 6, CALL_INDIRECT, VOID) /* 237 */
|
||||
ENTRY("removexattr", SYS_removexattr, no_syscall, 3, CALL_INDIRECT, VOID) /* 238 */
|
||||
ENTRY("fremovexattr", SYS_fremovexattr, no_syscall, 3, CALL_INDIRECT, VOID) /* 239 */
|
||||
ENTRY("listxattr", SYS_listxattr, no_syscall, 4, CALL_INDIRECT, VOID) /* 240 */
|
||||
ENTRY("flistxattr", SYS_flistxattr, no_syscall, 4, CALL_INDIRECT, VOID) /* 241 */
|
||||
ENTRY("fsctl", SYS_fsctl, fsctl, 4, CALL_DIRECT, PTR, UINT, PTR, UINT) /* 242 */
|
||||
ENTRY("initgroups", SYS_initgroups, unimpl_unix_syscall, 3, CALL_INDIRECT, UINT, PTR, INT) /* 243 */
|
||||
ENTRY("", 244, no_syscall, 0, CALL_INDIRECT, VOID) /* 244 */
|
||||
ENTRY("", 245, no_syscall, 0, CALL_INDIRECT, VOID) /* 245 */
|
||||
ENTRY("", 246, no_syscall, 0, CALL_INDIRECT, VOID) /* 246 */
|
||||
#ifdef SYS_nfsclnt
|
||||
ENTRY("nfsclnt", SYS_nfsclnt, nfsclnt, 2, CALL_DIRECT, INT, PTR) /* 247 */
|
||||
#else
|
||||
ENTRY("nfsclnt", 247, no_syscall, 2, CALL_INDIRECT, VOID) /* 247 */
|
||||
#endif
|
||||
ENTRY("", 247, no_syscall, 0, CALL_INDIRECT, VOID) /* 247 */
|
||||
ENTRY("", 248, no_syscall, 0, CALL_INDIRECT, VOID) /* 248 */
|
||||
ENTRY("", 249, no_syscall, 0, CALL_INDIRECT, VOID) /* 249 */
|
||||
ENTRY("minherit", SYS_minherit, minherit, 3, CALL_DIRECT, PTR, INT, INT) /* 250 */
|
||||
ENTRY("semsys", SYS_semsys, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 251 */
|
||||
ENTRY("msgsys", SYS_msgsys, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 252 */
|
||||
ENTRY("shmsys", SYS_shmsys, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 253 */
|
||||
ENTRY("semctl", SYS_semctl, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 254 */
|
||||
ENTRY("semget", SYS_semget, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 255 */
|
||||
ENTRY("semop", SYS_semop, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 256 */
|
||||
ENTRY("", 257, no_syscall, 0, CALL_INDIRECT, VOID) /* 257 */
|
||||
ENTRY("msgctl", SYS_msgctl, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 258 */
|
||||
ENTRY("msgget", SYS_msgget, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 259 */
|
||||
ENTRY("msgsnd", SYS_msgsnd, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 260 */
|
||||
ENTRY("msgrcv", SYS_msgrcv, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 261 */
|
||||
ENTRY("shmat", SYS_shmat, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 262 */
|
||||
ENTRY("shmctl", SYS_shmctl, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 263 */
|
||||
ENTRY("shmdt", SYS_shmdt, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 264 */
|
||||
ENTRY("shmget", SYS_shmget, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 265 */
|
||||
ENTRY("shm_open", SYS_shm_open, shm_open, 3, CALL_DIRECT, PTR, INT, INT) /* 266 */
|
||||
ENTRY("shm_unlink", SYS_shm_unlink, shm_unlink, 1, CALL_DIRECT, PTR) /* 267 */
|
||||
ENTRY("sem_open", SYS_sem_open, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 268 */
|
||||
ENTRY("sem_close", SYS_sem_close, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 269 */
|
||||
ENTRY("sem_unlink", SYS_sem_unlink, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 270 */
|
||||
ENTRY("sem_wait", SYS_sem_wait, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 271 */
|
||||
ENTRY("sem_trywait", SYS_sem_trywait, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 272 */
|
||||
ENTRY("sem_post", SYS_sem_post, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 273 */
|
||||
ENTRY("sem_getvalue", SYS_sem_getvalue, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 274 */
|
||||
ENTRY("sem_init", SYS_sem_init, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 275 */
|
||||
ENTRY("sem_destroy", SYS_sem_destroy, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 276 */
|
||||
ENTRY("open_extended", SYS_open_extended, unimpl_unix_syscall, 6, CALL_INDIRECT, VOID) /* 277 */
|
||||
ENTRY("umask_extended", SYS_umask_extended, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 278 */
|
||||
ENTRY("stat_extended", SYS_stat_extended, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 279 */
|
||||
ENTRY("lstat_extended", SYS_lstat_extended, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 280 */
|
||||
ENTRY("fstat_extended", SYS_fstat_extended, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 281 */
|
||||
ENTRY("chmod_extended", SYS_chmod_extended, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 282 */
|
||||
ENTRY("fchmod_extended", SYS_fchmod_extended, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 283 */
|
||||
ENTRY("access_extended", SYS_access_extended, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 284 */
|
||||
ENTRY("settid", SYS_settid, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 285 */
|
||||
ENTRY("gettid", SYS_gettid, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 286 */
|
||||
ENTRY("setsgroups", SYS_setsgroups, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 287 */
|
||||
ENTRY("getsgroups", SYS_getsgroups, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 288 */
|
||||
ENTRY("setwgroups", SYS_setwgroups, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 289 */
|
||||
ENTRY("getwgroups", SYS_getwgroups, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 290 */
|
||||
ENTRY("mkfifo_extended", SYS_mkfifo_extended, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 291 */
|
||||
ENTRY("mkdir_extended", SYS_mkdir_extended, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 292 */
|
||||
ENTRY("identitysvc", SYS_identitysvc, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 293 */
|
||||
ENTRY("", 294, no_syscall, 0, CALL_INDIRECT, VOID) /* 294 */
|
||||
ENTRY("", 295, no_syscall, 0, CALL_INDIRECT, VOID) /* 295 */
|
||||
ENTRY("load_shared_file", SYS_load_shared_file, unimpl_unix_syscall, 7, CALL_INDIRECT, VOID) /* 296 */
|
||||
ENTRY("reset_shared_file", SYS_reset_shared_file, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 297 */
|
||||
ENTRY("new_system_shared_regions", SYS_new_system_shared_regions, unimpl_unix_syscall, 0, CALL_INDIRECT, VOID) /* 298 */
|
||||
ENTRY("shared_region_map_file_np", SYS_shared_region_map_file_np, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 299 */
|
||||
ENTRY("shared_region_make_private_np", SYS_shared_region_make_private_np, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 300 */
|
||||
ENTRY("", 301, no_syscall, 0, CALL_INDIRECT, VOID) /* 301 */
|
||||
ENTRY("", 302, no_syscall, 0, CALL_INDIRECT, VOID) /* 302 */
|
||||
ENTRY("", 303, no_syscall, 0, CALL_INDIRECT, VOID) /* 303 */
|
||||
ENTRY("", 304, no_syscall, 0, CALL_INDIRECT, VOID) /* 304 */
|
||||
ENTRY("", 305, no_syscall, 0, CALL_INDIRECT, VOID) /* 305 */
|
||||
ENTRY("", 306, no_syscall, 0, CALL_INDIRECT, VOID) /* 306 */
|
||||
ENTRY("", 307, no_syscall, 0, CALL_INDIRECT, VOID) /* 307 */
|
||||
ENTRY("", 308, no_syscall, 0, CALL_INDIRECT, VOID) /* 308 */
|
||||
ENTRY("", 309, no_syscall, 0, CALL_INDIRECT, VOID) /* 309 */
|
||||
ENTRY("getsid", SYS_getsid, getsid, 1, CALL_DIRECT, INT) /* 310 */
|
||||
ENTRY("settid_with_pid", SYS_settid_with_pid, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 311 */
|
||||
ENTRY("", 312, no_syscall, 0, CALL_INDIRECT, VOID) /* 312 */
|
||||
ENTRY("aio_fsync", SYS_aio_fsync, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 313 */
|
||||
ENTRY("aio_return", SYS_aio_return, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 314 */
|
||||
ENTRY("aio_suspend", SYS_aio_suspend, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 315 */
|
||||
ENTRY("aio_cancel", SYS_aio_cancel, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 316 */
|
||||
ENTRY("aio_error", SYS_aio_error, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 317 */
|
||||
ENTRY("aio_read", SYS_aio_read, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 318 */
|
||||
ENTRY("aio_write", SYS_aio_write, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 319 */
|
||||
ENTRY("lio_listio", SYS_lio_listio, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 320 */
|
||||
ENTRY("", 321, no_syscall, 0, CALL_INDIRECT, VOID) /* 321 */
|
||||
ENTRY("", 322, no_syscall, 0, CALL_INDIRECT, VOID) /* 322 */
|
||||
ENTRY("", 323, no_syscall, 0, CALL_INDIRECT, VOID) /* 323 */
|
||||
ENTRY("mlockall", SYS_mlockall, mlockall, 1, CALL_DIRECT, INT) /* 324 */
|
||||
ENTRY("munlockall", SYS_munlockall, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 325 */
|
||||
ENTRY("", 326, no_syscall, 0, CALL_INDIRECT, VOID) /* 326 */
|
||||
ENTRY("issetugid", SYS_issetugid, issetugid, 0, CALL_DIRECT, VOID) /* 327 */
|
||||
ENTRY("__pthread_kill", SYS___pthread_kill, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 328 */
|
||||
ENTRY("pthread_sigmask", SYS_pthread_sigmask, pthread_sigmask, 3, CALL_DIRECT, INT, PTR, PTR) /* 329 */
|
||||
ENTRY("sigwait", SYS_sigwait, sigwait, 2, CALL_DIRECT, PTR, PTR) /* 330 */
|
||||
ENTRY("__disable_threadsignal", SYS___disable_threadsignal, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 331 */
|
||||
ENTRY("__pthread_markcancel", SYS___pthread_markcancel, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 332 */
|
||||
ENTRY("__pthread_canceled", SYS___pthread_canceled, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 333 */
|
||||
ENTRY("__semwait_signal", SYS___semwait_signal, unimpl_unix_syscall, 6, CALL_INDIRECT, VOID) /* 334 */
|
||||
ENTRY("utrace", SYS_utrace, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 335 */
|
||||
ENTRY("proc_info", SYS_proc_info, unimpl_unix_syscall, 6, CALL_INDIRECT, VOID) /* 336 */
|
||||
ENTRY("", 337, no_syscall, 0, CALL_INDIRECT, VOID) /* 337 */
|
||||
ENTRY("", 338, no_syscall, 0, CALL_INDIRECT, VOID) /* 338 */
|
||||
ENTRY("", 339, no_syscall, 0, CALL_INDIRECT, VOID) /* 339 */
|
||||
ENTRY("", 340, no_syscall, 0, CALL_INDIRECT, VOID) /* 340 */
|
||||
ENTRY("", 341, no_syscall, 0, CALL_INDIRECT, VOID) /* 341 */
|
||||
ENTRY("", 342, no_syscall, 0, CALL_INDIRECT, VOID) /* 342 */
|
||||
ENTRY("", 343, no_syscall, 0, CALL_INDIRECT, VOID) /* 343 */
|
||||
ENTRY("", 344, no_syscall, 0, CALL_INDIRECT, VOID) /* 344 */
|
||||
ENTRY("", 345, no_syscall, 0, CALL_INDIRECT, VOID) /* 345 */
|
||||
ENTRY("", 346, no_syscall, 0, CALL_INDIRECT, VOID) /* 346 */
|
||||
ENTRY("", 347, no_syscall, 0, CALL_INDIRECT, VOID) /* 347 */
|
||||
ENTRY("", 348, no_syscall, 0, CALL_INDIRECT, VOID) /* 348 */
|
||||
ENTRY("", 349, no_syscall, 0, CALL_INDIRECT, VOID) /* 349 */
|
||||
ENTRY("audit", SYS_audit, audit, 2, CALL_DIRECT, PTR, INT) /* 350 */
|
||||
ENTRY("auditon", SYS_auditon, auditon, 3, CALL_DIRECT, INT, PTR, INT) /* 351 */
|
||||
ENTRY("", 352, no_syscall, 0, CALL_INDIRECT, VOID) /* 352 */
|
||||
ENTRY("getauid", SYS_getauid, getauid, 1, CALL_DIRECT, PTR) /* 353 */
|
||||
ENTRY("setauid", SYS_setauid, setauid, 1, CALL_DIRECT, PTR) /* 354 */
|
||||
ENTRY("getaudit", SYS_getaudit, getaudit, 1, CALL_DIRECT, PTR) /* 355 */
|
||||
ENTRY("setaudit", SYS_setaudit, setaudit, 1, CALL_DIRECT, PTR) /* 356 */
|
||||
ENTRY("getaudit_addr", SYS_getaudit_addr, getaudit_addr, 2, CALL_DIRECT, PTR, INT) /* 357 */
|
||||
ENTRY("setaudit_addr", SYS_setaudit_addr, setaudit_addr, 2, CALL_DIRECT, PTR, INT) /* 358 */
|
||||
ENTRY("auditctl", SYS_auditctl, auditctl, 1, CALL_DIRECT, PTR) /* 359 */
|
||||
ENTRY("", 360, no_syscall, 0, CALL_INDIRECT, VOID) /* 360 */
|
||||
ENTRY("", 361, no_syscall, 0, CALL_INDIRECT, VOID) /* 361 */
|
||||
ENTRY("kqueue", SYS_kqueue, kqueue, 0, CALL_DIRECT, VOID) /* 362 */
|
||||
ENTRY("kevent", SYS_kevent, kevent, 6, CALL_DIRECT, INT, PTR, INT, PTR, INT, PTR) /* 363 */
|
||||
ENTRY("lchown", SYS_lchown, lchown, 3, CALL_DIRECT, PTR, INT , INT) /* 364 */
|
||||
ENTRY("stack_snapshot", SYS_stack_snapshot, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 365 */
|
||||
ENTRY("", 366, no_syscall, 0, CALL_INDIRECT, VOID) /* 366 */
|
||||
ENTRY("", 367, no_syscall, 0, CALL_INDIRECT, VOID) /* 367 */
|
||||
ENTRY("", 368, no_syscall, 0, CALL_INDIRECT, VOID) /* 368 */
|
||||
ENTRY("", 369, no_syscall, 0, CALL_INDIRECT, VOID) /* 369 */
|
||||
@@ -0,0 +1,455 @@
|
||||
/* Interface between the opcode library and its callers.
|
||||
Written by Cygnus Support, 1993.
|
||||
|
||||
The opcode library (libopcodes.a) provides instruction decoders for
|
||||
a large variety of instruction sets, callable with an identical
|
||||
interface, for making instruction-processing programs more independent
|
||||
of the instruction set being processed. */
|
||||
|
||||
#ifndef DIS_ASM_H
|
||||
#define DIS_ASM_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define PARAMS(x) x
|
||||
typedef void *PTR;
|
||||
typedef uint64_t bfd_vma;
|
||||
typedef int64_t bfd_signed_vma;
|
||||
typedef uint8_t bfd_byte;
|
||||
#define sprintf_vma(s,x) sprintf (s, "%0" PRIx64, x)
|
||||
|
||||
#define BFD64
|
||||
|
||||
enum bfd_flavour {
|
||||
bfd_target_unknown_flavour,
|
||||
bfd_target_aout_flavour,
|
||||
bfd_target_coff_flavour,
|
||||
bfd_target_ecoff_flavour,
|
||||
bfd_target_elf_flavour,
|
||||
bfd_target_ieee_flavour,
|
||||
bfd_target_nlm_flavour,
|
||||
bfd_target_oasys_flavour,
|
||||
bfd_target_tekhex_flavour,
|
||||
bfd_target_srec_flavour,
|
||||
bfd_target_ihex_flavour,
|
||||
bfd_target_som_flavour,
|
||||
bfd_target_os9k_flavour,
|
||||
bfd_target_versados_flavour,
|
||||
bfd_target_msdos_flavour,
|
||||
bfd_target_evax_flavour
|
||||
};
|
||||
|
||||
enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
|
||||
|
||||
enum bfd_architecture
|
||||
{
|
||||
bfd_arch_unknown, /* File arch not known */
|
||||
bfd_arch_obscure, /* Arch known, not one of these */
|
||||
bfd_arch_m68k, /* Motorola 68xxx */
|
||||
#define bfd_mach_m68000 1
|
||||
#define bfd_mach_m68008 2
|
||||
#define bfd_mach_m68010 3
|
||||
#define bfd_mach_m68020 4
|
||||
#define bfd_mach_m68030 5
|
||||
#define bfd_mach_m68040 6
|
||||
#define bfd_mach_m68060 7
|
||||
#define bfd_mach_cpu32 8
|
||||
#define bfd_mach_mcf5200 9
|
||||
#define bfd_mach_mcf5206e 10
|
||||
#define bfd_mach_mcf5307 11
|
||||
#define bfd_mach_mcf5407 12
|
||||
#define bfd_mach_mcf528x 13
|
||||
#define bfd_mach_mcfv4e 14
|
||||
#define bfd_mach_mcf521x 15
|
||||
#define bfd_mach_mcf5249 16
|
||||
#define bfd_mach_mcf547x 17
|
||||
#define bfd_mach_mcf548x 18
|
||||
bfd_arch_vax, /* DEC Vax */
|
||||
bfd_arch_i960, /* Intel 960 */
|
||||
/* The order of the following is important.
|
||||
lower number indicates a machine type that
|
||||
only accepts a subset of the instructions
|
||||
available to machines with higher numbers.
|
||||
The exception is the "ca", which is
|
||||
incompatible with all other machines except
|
||||
"core". */
|
||||
|
||||
#define bfd_mach_i960_core 1
|
||||
#define bfd_mach_i960_ka_sa 2
|
||||
#define bfd_mach_i960_kb_sb 3
|
||||
#define bfd_mach_i960_mc 4
|
||||
#define bfd_mach_i960_xa 5
|
||||
#define bfd_mach_i960_ca 6
|
||||
#define bfd_mach_i960_jx 7
|
||||
#define bfd_mach_i960_hx 8
|
||||
|
||||
bfd_arch_a29k, /* AMD 29000 */
|
||||
bfd_arch_sparc, /* SPARC */
|
||||
#define bfd_mach_sparc 1
|
||||
/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */
|
||||
#define bfd_mach_sparc_sparclet 2
|
||||
#define bfd_mach_sparc_sparclite 3
|
||||
#define bfd_mach_sparc_v8plus 4
|
||||
#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */
|
||||
#define bfd_mach_sparc_sparclite_le 6
|
||||
#define bfd_mach_sparc_v9 7
|
||||
#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */
|
||||
#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */
|
||||
#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */
|
||||
/* Nonzero if MACH has the v9 instruction set. */
|
||||
#define bfd_mach_sparc_v9_p(mach) \
|
||||
((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \
|
||||
&& (mach) != bfd_mach_sparc_sparclite_le)
|
||||
bfd_arch_mips, /* MIPS Rxxxx */
|
||||
#define bfd_mach_mips3000 3000
|
||||
#define bfd_mach_mips3900 3900
|
||||
#define bfd_mach_mips4000 4000
|
||||
#define bfd_mach_mips4010 4010
|
||||
#define bfd_mach_mips4100 4100
|
||||
#define bfd_mach_mips4300 4300
|
||||
#define bfd_mach_mips4400 4400
|
||||
#define bfd_mach_mips4600 4600
|
||||
#define bfd_mach_mips4650 4650
|
||||
#define bfd_mach_mips5000 5000
|
||||
#define bfd_mach_mips6000 6000
|
||||
#define bfd_mach_mips8000 8000
|
||||
#define bfd_mach_mips10000 10000
|
||||
#define bfd_mach_mips16 16
|
||||
bfd_arch_i386, /* Intel 386 */
|
||||
#define bfd_mach_i386_i386 0
|
||||
#define bfd_mach_i386_i8086 1
|
||||
#define bfd_mach_i386_i386_intel_syntax 2
|
||||
#define bfd_mach_x86_64 3
|
||||
#define bfd_mach_x86_64_intel_syntax 4
|
||||
bfd_arch_we32k, /* AT&T WE32xxx */
|
||||
bfd_arch_tahoe, /* CCI/Harris Tahoe */
|
||||
bfd_arch_i860, /* Intel 860 */
|
||||
bfd_arch_romp, /* IBM ROMP PC/RT */
|
||||
bfd_arch_alliant, /* Alliant */
|
||||
bfd_arch_convex, /* Convex */
|
||||
bfd_arch_m88k, /* Motorola 88xxx */
|
||||
bfd_arch_pyramid, /* Pyramid Technology */
|
||||
bfd_arch_h8300, /* Hitachi H8/300 */
|
||||
#define bfd_mach_h8300 1
|
||||
#define bfd_mach_h8300h 2
|
||||
#define bfd_mach_h8300s 3
|
||||
bfd_arch_powerpc, /* PowerPC */
|
||||
#define bfd_mach_ppc 0
|
||||
#define bfd_mach_ppc64 1
|
||||
#define bfd_mach_ppc_403 403
|
||||
#define bfd_mach_ppc_403gc 4030
|
||||
#define bfd_mach_ppc_505 505
|
||||
#define bfd_mach_ppc_601 601
|
||||
#define bfd_mach_ppc_602 602
|
||||
#define bfd_mach_ppc_603 603
|
||||
#define bfd_mach_ppc_ec603e 6031
|
||||
#define bfd_mach_ppc_604 604
|
||||
#define bfd_mach_ppc_620 620
|
||||
#define bfd_mach_ppc_630 630
|
||||
#define bfd_mach_ppc_750 750
|
||||
#define bfd_mach_ppc_860 860
|
||||
#define bfd_mach_ppc_a35 35
|
||||
#define bfd_mach_ppc_rs64ii 642
|
||||
#define bfd_mach_ppc_rs64iii 643
|
||||
#define bfd_mach_ppc_7400 7400
|
||||
bfd_arch_rs6000, /* IBM RS/6000 */
|
||||
bfd_arch_hppa, /* HP PA RISC */
|
||||
bfd_arch_d10v, /* Mitsubishi D10V */
|
||||
bfd_arch_z8k, /* Zilog Z8000 */
|
||||
#define bfd_mach_z8001 1
|
||||
#define bfd_mach_z8002 2
|
||||
bfd_arch_h8500, /* Hitachi H8/500 */
|
||||
bfd_arch_sh, /* Hitachi SH */
|
||||
#define bfd_mach_sh 1
|
||||
#define bfd_mach_sh2 0x20
|
||||
#define bfd_mach_sh_dsp 0x2d
|
||||
#define bfd_mach_sh2a 0x2a
|
||||
#define bfd_mach_sh2a_nofpu 0x2b
|
||||
#define bfd_mach_sh2e 0x2e
|
||||
#define bfd_mach_sh3 0x30
|
||||
#define bfd_mach_sh3_nommu 0x31
|
||||
#define bfd_mach_sh3_dsp 0x3d
|
||||
#define bfd_mach_sh3e 0x3e
|
||||
#define bfd_mach_sh4 0x40
|
||||
#define bfd_mach_sh4_nofpu 0x41
|
||||
#define bfd_mach_sh4_nommu_nofpu 0x42
|
||||
#define bfd_mach_sh4a 0x4a
|
||||
#define bfd_mach_sh4a_nofpu 0x4b
|
||||
#define bfd_mach_sh4al_dsp 0x4d
|
||||
#define bfd_mach_sh5 0x50
|
||||
bfd_arch_alpha, /* Dec Alpha */
|
||||
bfd_arch_arm, /* Advanced Risc Machines ARM */
|
||||
#define bfd_mach_arm_2 1
|
||||
#define bfd_mach_arm_2a 2
|
||||
#define bfd_mach_arm_3 3
|
||||
#define bfd_mach_arm_3M 4
|
||||
#define bfd_mach_arm_4 5
|
||||
#define bfd_mach_arm_4T 6
|
||||
bfd_arch_ns32k, /* National Semiconductors ns32000 */
|
||||
bfd_arch_w65, /* WDC 65816 */
|
||||
bfd_arch_tic30, /* Texas Instruments TMS320C30 */
|
||||
bfd_arch_v850, /* NEC V850 */
|
||||
#define bfd_mach_v850 0
|
||||
bfd_arch_arc, /* Argonaut RISC Core */
|
||||
#define bfd_mach_arc_base 0
|
||||
bfd_arch_m32r, /* Mitsubishi M32R/D */
|
||||
#define bfd_mach_m32r 0 /* backwards compatibility */
|
||||
bfd_arch_mn10200, /* Matsushita MN10200 */
|
||||
bfd_arch_mn10300, /* Matsushita MN10300 */
|
||||
bfd_arch_last
|
||||
};
|
||||
|
||||
typedef struct symbol_cache_entry
|
||||
{
|
||||
const char *name;
|
||||
union
|
||||
{
|
||||
PTR p;
|
||||
bfd_vma i;
|
||||
} udata;
|
||||
} asymbol;
|
||||
|
||||
typedef int (*fprintf_ftype) PARAMS((FILE*, const char*, ...));
|
||||
|
||||
enum dis_insn_type {
|
||||
dis_noninsn, /* Not a valid instruction */
|
||||
dis_nonbranch, /* Not a branch instruction */
|
||||
dis_branch, /* Unconditional branch */
|
||||
dis_condbranch, /* Conditional branch */
|
||||
dis_jsr, /* Jump to subroutine */
|
||||
dis_condjsr, /* Conditional jump to subroutine */
|
||||
dis_dref, /* Data reference instruction */
|
||||
dis_dref2 /* Two data references in instruction */
|
||||
};
|
||||
|
||||
/* This struct is passed into the instruction decoding routine,
|
||||
and is passed back out into each callback. The various fields are used
|
||||
for conveying information from your main routine into your callbacks,
|
||||
for passing information into the instruction decoders (such as the
|
||||
addresses of the callback functions), or for passing information
|
||||
back from the instruction decoders to their callers.
|
||||
|
||||
It must be initialized before it is first passed; this can be done
|
||||
by hand, or using one of the initialization macros below. */
|
||||
|
||||
typedef struct disassemble_info {
|
||||
fprintf_ftype fprintf_func;
|
||||
FILE *stream;
|
||||
PTR application_data;
|
||||
|
||||
/* Target description. We could replace this with a pointer to the bfd,
|
||||
but that would require one. There currently isn't any such requirement
|
||||
so to avoid introducing one we record these explicitly. */
|
||||
/* The bfd_flavour. This can be bfd_target_unknown_flavour. */
|
||||
enum bfd_flavour flavour;
|
||||
/* The bfd_arch value. */
|
||||
enum bfd_architecture arch;
|
||||
/* The bfd_mach value. */
|
||||
unsigned long mach;
|
||||
/* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */
|
||||
enum bfd_endian endian;
|
||||
|
||||
/* An array of pointers to symbols either at the location being disassembled
|
||||
or at the start of the function being disassembled. The array is sorted
|
||||
so that the first symbol is intended to be the one used. The others are
|
||||
present for any misc. purposes. This is not set reliably, but if it is
|
||||
not NULL, it is correct. */
|
||||
asymbol **symbols;
|
||||
/* Number of symbols in array. */
|
||||
int num_symbols;
|
||||
|
||||
/* For use by the disassembler.
|
||||
The top 16 bits are reserved for public use (and are documented here).
|
||||
The bottom 16 bits are for the internal use of the disassembler. */
|
||||
unsigned long flags;
|
||||
#define INSN_HAS_RELOC 0x80000000
|
||||
PTR private_data;
|
||||
|
||||
/* Function used to get bytes to disassemble. MEMADDR is the
|
||||
address of the stuff to be disassembled, MYADDR is the address to
|
||||
put the bytes in, and LENGTH is the number of bytes to read.
|
||||
INFO is a pointer to this struct.
|
||||
Returns an errno value or 0 for success. */
|
||||
int (*read_memory_func)
|
||||
PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, int length,
|
||||
struct disassemble_info *info));
|
||||
|
||||
/* Function which should be called if we get an error that we can't
|
||||
recover from. STATUS is the errno value from read_memory_func and
|
||||
MEMADDR is the address that we were trying to read. INFO is a
|
||||
pointer to this struct. */
|
||||
void (*memory_error_func)
|
||||
PARAMS ((int status, bfd_vma memaddr, struct disassemble_info *info));
|
||||
|
||||
/* Function called to print ADDR. */
|
||||
void (*print_address_func)
|
||||
PARAMS ((bfd_vma addr, struct disassemble_info *info));
|
||||
|
||||
/* Function called to determine if there is a symbol at the given ADDR.
|
||||
If there is, the function returns 1, otherwise it returns 0.
|
||||
This is used by ports which support an overlay manager where
|
||||
the overlay number is held in the top part of an address. In
|
||||
some circumstances we want to include the overlay number in the
|
||||
address, (normally because there is a symbol associated with
|
||||
that address), but sometimes we want to mask out the overlay bits. */
|
||||
int (* symbol_at_address_func)
|
||||
PARAMS ((bfd_vma addr, struct disassemble_info * info));
|
||||
|
||||
/* These are for buffer_read_memory. */
|
||||
bfd_byte *buffer;
|
||||
bfd_vma buffer_vma;
|
||||
int buffer_length;
|
||||
|
||||
/* This variable may be set by the instruction decoder. It suggests
|
||||
the number of bytes objdump should display on a single line. If
|
||||
the instruction decoder sets this, it should always set it to
|
||||
the same value in order to get reasonable looking output. */
|
||||
int bytes_per_line;
|
||||
|
||||
/* the next two variables control the way objdump displays the raw data */
|
||||
/* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */
|
||||
/* output will look like this:
|
||||
00: 00000000 00000000
|
||||
with the chunks displayed according to "display_endian". */
|
||||
int bytes_per_chunk;
|
||||
enum bfd_endian display_endian;
|
||||
|
||||
/* Results from instruction decoders. Not all decoders yet support
|
||||
this information. This info is set each time an instruction is
|
||||
decoded, and is only valid for the last such instruction.
|
||||
|
||||
To determine whether this decoder supports this information, set
|
||||
insn_info_valid to 0, decode an instruction, then check it. */
|
||||
|
||||
char insn_info_valid; /* Branch info has been set. */
|
||||
char branch_delay_insns; /* How many sequential insn's will run before
|
||||
a branch takes effect. (0 = normal) */
|
||||
char data_size; /* Size of data reference in insn, in bytes */
|
||||
enum dis_insn_type insn_type; /* Type of instruction */
|
||||
bfd_vma target; /* Target address of branch or dref, if known;
|
||||
zero if unknown. */
|
||||
bfd_vma target2; /* Second target address for dref2 */
|
||||
|
||||
/* Command line options specific to the target disassembler. */
|
||||
char * disassembler_options;
|
||||
|
||||
} disassemble_info;
|
||||
|
||||
|
||||
/* Standard disassemblers. Disassemble one instruction at the given
|
||||
target address. Return number of bytes processed. */
|
||||
typedef int (*disassembler_ftype)
|
||||
PARAMS((bfd_vma, disassemble_info *));
|
||||
|
||||
extern int print_insn_big_mips PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_mips PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_i386 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_m68k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_z8001 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_z8002 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8300 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8300h PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8300s PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8500 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_alpha PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern disassembler_ftype arc_get_disassembler PARAMS ((int, int));
|
||||
extern int print_insn_arm PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_sparc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_big_a29k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_a29k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_i960 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_sh PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_shl PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_hppa PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_m32r PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_m88k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_mn10200 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_mn10300 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_ns32k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_big_powerpc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_powerpc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_rs6000 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_w65 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_d10v PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_v850 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_tic30 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_ppc PARAMS ((bfd_vma, disassemble_info*));
|
||||
|
||||
#if 0
|
||||
/* Fetch the disassembler for a given BFD, if that support is available. */
|
||||
extern disassembler_ftype disassembler PARAMS ((bfd *));
|
||||
#endif
|
||||
|
||||
|
||||
/* This block of definitions is for particular callers who read instructions
|
||||
into a buffer before calling the instruction decoder. */
|
||||
|
||||
/* Here is a function which callers may wish to use for read_memory_func.
|
||||
It gets bytes from a buffer. */
|
||||
extern int buffer_read_memory
|
||||
PARAMS ((bfd_vma, bfd_byte *, int, struct disassemble_info *));
|
||||
|
||||
/* This function goes with buffer_read_memory.
|
||||
It prints a message using info->fprintf_func and info->stream. */
|
||||
extern void perror_memory PARAMS ((int, bfd_vma, struct disassemble_info *));
|
||||
|
||||
|
||||
/* Just print the address in hex. This is included for completeness even
|
||||
though both GDB and objdump provide their own (to print symbolic
|
||||
addresses). */
|
||||
extern void generic_print_address
|
||||
PARAMS ((bfd_vma, struct disassemble_info *));
|
||||
|
||||
/* Always true. */
|
||||
extern int generic_symbol_at_address
|
||||
PARAMS ((bfd_vma, struct disassemble_info *));
|
||||
|
||||
/* Macro to initialize a disassemble_info struct. This should be called
|
||||
by all applications creating such a struct. */
|
||||
#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \
|
||||
(INFO).flavour = bfd_target_unknown_flavour, \
|
||||
(INFO).arch = bfd_arch_unknown, \
|
||||
(INFO).mach = 0, \
|
||||
(INFO).endian = BFD_ENDIAN_UNKNOWN, \
|
||||
INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC)
|
||||
|
||||
/* Call this macro to initialize only the internal variables for the
|
||||
disassembler. Architecture dependent things such as byte order, or machine
|
||||
variant are not touched by this macro. This makes things much easier for
|
||||
GDB which must initialize these things seperatly. */
|
||||
|
||||
#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \
|
||||
(INFO).fprintf_func = (FPRINTF_FUNC), \
|
||||
(INFO).stream = (STREAM), \
|
||||
(INFO).symbols = NULL, \
|
||||
(INFO).num_symbols = 0, \
|
||||
(INFO).buffer = NULL, \
|
||||
(INFO).buffer_vma = 0, \
|
||||
(INFO).buffer_length = 0, \
|
||||
(INFO).read_memory_func = buffer_read_memory, \
|
||||
(INFO).memory_error_func = perror_memory, \
|
||||
(INFO).print_address_func = generic_print_address, \
|
||||
(INFO).symbol_at_address_func = generic_symbol_at_address, \
|
||||
(INFO).flags = 0, \
|
||||
(INFO).bytes_per_line = 0, \
|
||||
(INFO).bytes_per_chunk = 0, \
|
||||
(INFO).display_endian = BFD_ENDIAN_UNKNOWN, \
|
||||
(INFO).disassembler_options = NULL, \
|
||||
(INFO).insn_info_valid = 0
|
||||
|
||||
#define _(x) x
|
||||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||
|
||||
/* from libbfd */
|
||||
|
||||
bfd_vma bfd_getl32 (const bfd_byte *addr);
|
||||
bfd_vma bfd_getb32 (const bfd_byte *addr);
|
||||
bfd_vma bfd_getl16 (const bfd_byte *addr);
|
||||
bfd_vma bfd_getb16 (const bfd_byte *addr);
|
||||
typedef enum bfd_boolean {false, true} boolean;
|
||||
typedef boolean bfd_boolean;
|
||||
|
||||
#endif /* ! defined (DIS_ASM_H) */
|
||||
@@ -0,0 +1,411 @@
|
||||
/* General "disassemble this chunk" code. Used for debugging. */
|
||||
#include "config.h"
|
||||
#include "dis-asm.h"
|
||||
#include "elf.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec-all.h"
|
||||
#include "disas.h"
|
||||
|
||||
/* Filled in by elfload.c. Simplistic, but will do for now. */
|
||||
struct syminfo *syminfos = NULL;
|
||||
|
||||
/* Get LENGTH bytes from info's buffer, at target address memaddr.
|
||||
Transfer them to myaddr. */
|
||||
int
|
||||
buffer_read_memory (memaddr, myaddr, length, info)
|
||||
bfd_vma memaddr;
|
||||
bfd_byte *myaddr;
|
||||
int length;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (memaddr < info->buffer_vma
|
||||
|| memaddr + length > info->buffer_vma + info->buffer_length)
|
||||
/* Out of bounds. Use EIO because GDB uses it. */
|
||||
return EIO;
|
||||
memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get LENGTH bytes from info's buffer, at target address memaddr.
|
||||
Transfer them to myaddr. */
|
||||
static int
|
||||
target_read_memory (bfd_vma memaddr,
|
||||
bfd_byte *myaddr,
|
||||
int length,
|
||||
struct disassemble_info *info)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < length; i++) {
|
||||
myaddr[i] = ldub_code(memaddr + i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print an error message. We can assume that this is in response to
|
||||
an error return from buffer_read_memory. */
|
||||
void
|
||||
perror_memory (status, memaddr, info)
|
||||
int status;
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (status != EIO)
|
||||
/* Can't happen. */
|
||||
(*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
|
||||
else
|
||||
/* Actually, address between memaddr and memaddr + len was
|
||||
out of bounds. */
|
||||
(*info->fprintf_func) (info->stream,
|
||||
"Address 0x%" PRIx64 " is out of bounds.\n", memaddr);
|
||||
}
|
||||
|
||||
/* This could be in a separate file, to save miniscule amounts of space
|
||||
in statically linked executables. */
|
||||
|
||||
/* Just print the address is hex. This is included for completeness even
|
||||
though both GDB and objdump provide their own (to print symbolic
|
||||
addresses). */
|
||||
|
||||
void
|
||||
generic_print_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "0x%" PRIx64, addr);
|
||||
}
|
||||
|
||||
/* Just return the given address. */
|
||||
|
||||
int
|
||||
generic_symbol_at_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info * info;
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bfd_vma bfd_getl32 (const bfd_byte *addr)
|
||||
{
|
||||
unsigned long v;
|
||||
|
||||
v = (unsigned long) addr[0];
|
||||
v |= (unsigned long) addr[1] << 8;
|
||||
v |= (unsigned long) addr[2] << 16;
|
||||
v |= (unsigned long) addr[3] << 24;
|
||||
return (bfd_vma) v;
|
||||
}
|
||||
|
||||
bfd_vma bfd_getb32 (const bfd_byte *addr)
|
||||
{
|
||||
unsigned long v;
|
||||
|
||||
v = (unsigned long) addr[0] << 24;
|
||||
v |= (unsigned long) addr[1] << 16;
|
||||
v |= (unsigned long) addr[2] << 8;
|
||||
v |= (unsigned long) addr[3];
|
||||
return (bfd_vma) v;
|
||||
}
|
||||
|
||||
bfd_vma bfd_getl16 (const bfd_byte *addr)
|
||||
{
|
||||
unsigned long v;
|
||||
|
||||
v = (unsigned long) addr[0];
|
||||
v |= (unsigned long) addr[1] << 8;
|
||||
return (bfd_vma) v;
|
||||
}
|
||||
|
||||
bfd_vma bfd_getb16 (const bfd_byte *addr)
|
||||
{
|
||||
unsigned long v;
|
||||
|
||||
v = (unsigned long) addr[0] << 24;
|
||||
v |= (unsigned long) addr[1] << 16;
|
||||
return (bfd_vma) v;
|
||||
}
|
||||
|
||||
#ifdef TARGET_ARM
|
||||
static int
|
||||
print_insn_thumb1(bfd_vma pc, disassemble_info *info)
|
||||
{
|
||||
return print_insn_arm(pc | 1, info);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Disassemble this for me please... (debugging). 'flags' has teh following
|
||||
values:
|
||||
i386 - nonzero means 16 bit code
|
||||
arm - nonzero means thumb code
|
||||
ppc - nonzero means little endian
|
||||
other targets - unused
|
||||
*/
|
||||
void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
|
||||
{
|
||||
target_ulong pc;
|
||||
int count;
|
||||
struct disassemble_info disasm_info;
|
||||
int (*print_insn)(bfd_vma pc, disassemble_info *info);
|
||||
|
||||
INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
|
||||
|
||||
disasm_info.read_memory_func = target_read_memory;
|
||||
disasm_info.buffer_vma = code;
|
||||
disasm_info.buffer_length = size;
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
disasm_info.endian = BFD_ENDIAN_BIG;
|
||||
#else
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#endif
|
||||
#if defined(TARGET_I386)
|
||||
if (flags == 2)
|
||||
disasm_info.mach = bfd_mach_x86_64;
|
||||
else if (flags == 1)
|
||||
disasm_info.mach = bfd_mach_i386_i8086;
|
||||
else
|
||||
disasm_info.mach = bfd_mach_i386_i386;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(TARGET_ARM)
|
||||
if (flags)
|
||||
print_insn = print_insn_thumb1;
|
||||
else
|
||||
print_insn = print_insn_arm;
|
||||
#elif defined(TARGET_SPARC)
|
||||
print_insn = print_insn_sparc;
|
||||
#ifdef TARGET_SPARC64
|
||||
disasm_info.mach = bfd_mach_sparc_v9b;
|
||||
#endif
|
||||
#elif defined(TARGET_PPC)
|
||||
if (flags)
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#ifdef TARGET_PPC64
|
||||
disasm_info.mach = bfd_mach_ppc64;
|
||||
#else
|
||||
disasm_info.mach = bfd_mach_ppc;
|
||||
#endif
|
||||
print_insn = print_insn_ppc;
|
||||
#elif defined(TARGET_M68K)
|
||||
print_insn = print_insn_m68k;
|
||||
#elif defined(TARGET_MIPS)
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
print_insn = print_insn_big_mips;
|
||||
#else
|
||||
print_insn = print_insn_little_mips;
|
||||
#endif
|
||||
#elif defined(TARGET_SH4)
|
||||
disasm_info.mach = bfd_mach_sh4;
|
||||
print_insn = print_insn_sh;
|
||||
#else
|
||||
fprintf(out, "0x" TARGET_FMT_lx
|
||||
": Asm output not supported on this arch\n", code);
|
||||
return;
|
||||
#endif
|
||||
|
||||
for (pc = code; pc < code + size; pc += count) {
|
||||
fprintf(out, "0x" TARGET_FMT_lx ": ", pc);
|
||||
count = print_insn(pc, &disasm_info);
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
uint8_t b;
|
||||
fprintf(out, " {");
|
||||
for(i = 0; i < count; i++) {
|
||||
target_read_memory(pc + i, &b, 1, &disasm_info);
|
||||
fprintf(out, " %02x", b);
|
||||
}
|
||||
fprintf(out, " }");
|
||||
}
|
||||
#endif
|
||||
fprintf(out, "\n");
|
||||
if (count < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disassemble this for me please... (debugging). */
|
||||
void disas(FILE *out, void *code, unsigned long size)
|
||||
{
|
||||
unsigned long pc;
|
||||
int count;
|
||||
struct disassemble_info disasm_info;
|
||||
int (*print_insn)(bfd_vma pc, disassemble_info *info);
|
||||
|
||||
INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
|
||||
|
||||
disasm_info.buffer = code;
|
||||
disasm_info.buffer_vma = (unsigned long)code;
|
||||
disasm_info.buffer_length = size;
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
disasm_info.endian = BFD_ENDIAN_BIG;
|
||||
#else
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#endif
|
||||
#if defined(__i386__)
|
||||
disasm_info.mach = bfd_mach_i386_i386;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(__x86_64__)
|
||||
disasm_info.mach = bfd_mach_x86_64;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(__powerpc__)
|
||||
print_insn = print_insn_ppc;
|
||||
#elif defined(__alpha__)
|
||||
print_insn = print_insn_alpha;
|
||||
#elif defined(__sparc__)
|
||||
print_insn = print_insn_sparc;
|
||||
#elif defined(__arm__)
|
||||
print_insn = print_insn_arm;
|
||||
#elif defined(__MIPSEB__)
|
||||
print_insn = print_insn_big_mips;
|
||||
#elif defined(__MIPSEL__)
|
||||
print_insn = print_insn_little_mips;
|
||||
#elif defined(__m68k__)
|
||||
print_insn = print_insn_m68k;
|
||||
#else
|
||||
fprintf(out, "0x%lx: Asm output not supported on this arch\n",
|
||||
(long) code);
|
||||
return;
|
||||
#endif
|
||||
for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) {
|
||||
fprintf(out, "0x%08lx: ", pc);
|
||||
#ifdef __arm__
|
||||
/* since data is included in the code, it is better to
|
||||
display code data too */
|
||||
fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc));
|
||||
#endif
|
||||
count = print_insn(pc, &disasm_info);
|
||||
fprintf(out, "\n");
|
||||
if (count < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||
const char *lookup_symbol(target_ulong orig_addr)
|
||||
{
|
||||
unsigned int i;
|
||||
/* Hack, because we know this is x86. */
|
||||
Elf32_Sym *sym;
|
||||
struct syminfo *s;
|
||||
target_ulong addr;
|
||||
|
||||
for (s = syminfos; s; s = s->next) {
|
||||
sym = s->disas_symtab;
|
||||
for (i = 0; i < s->disas_num_syms; i++) {
|
||||
if (sym[i].st_shndx == SHN_UNDEF
|
||||
|| sym[i].st_shndx >= SHN_LORESERVE)
|
||||
continue;
|
||||
|
||||
if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC)
|
||||
continue;
|
||||
|
||||
addr = sym[i].st_value;
|
||||
#ifdef TARGET_ARM
|
||||
/* The bottom address bit marks a Thumb symbol. */
|
||||
addr &= ~(target_ulong)1;
|
||||
#endif
|
||||
if (orig_addr >= addr
|
||||
&& orig_addr < addr + sym[i].st_size)
|
||||
return s->disas_strtab + sym[i].st_name;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
void term_vprintf(const char *fmt, va_list ap);
|
||||
void term_printf(const char *fmt, ...);
|
||||
|
||||
static int monitor_disas_is_physical;
|
||||
static CPUState *monitor_disas_env;
|
||||
|
||||
static int
|
||||
monitor_read_memory (memaddr, myaddr, length, info)
|
||||
bfd_vma memaddr;
|
||||
bfd_byte *myaddr;
|
||||
int length;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (monitor_disas_is_physical) {
|
||||
cpu_physical_memory_rw(memaddr, myaddr, length, 0);
|
||||
} else {
|
||||
cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int monitor_fprintf(FILE *stream, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
term_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void monitor_disas(CPUState *env,
|
||||
target_ulong pc, int nb_insn, int is_physical, int flags)
|
||||
{
|
||||
int count, i;
|
||||
struct disassemble_info disasm_info;
|
||||
int (*print_insn)(bfd_vma pc, disassemble_info *info);
|
||||
|
||||
INIT_DISASSEMBLE_INFO(disasm_info, NULL, monitor_fprintf);
|
||||
|
||||
monitor_disas_env = env;
|
||||
monitor_disas_is_physical = is_physical;
|
||||
disasm_info.read_memory_func = monitor_read_memory;
|
||||
|
||||
disasm_info.buffer_vma = pc;
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
disasm_info.endian = BFD_ENDIAN_BIG;
|
||||
#else
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#endif
|
||||
#if defined(TARGET_I386)
|
||||
if (flags == 2)
|
||||
disasm_info.mach = bfd_mach_x86_64;
|
||||
else if (flags == 1)
|
||||
disasm_info.mach = bfd_mach_i386_i8086;
|
||||
else
|
||||
disasm_info.mach = bfd_mach_i386_i386;
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(TARGET_ARM)
|
||||
print_insn = print_insn_arm;
|
||||
#elif defined(TARGET_SPARC)
|
||||
print_insn = print_insn_sparc;
|
||||
#elif defined(TARGET_PPC)
|
||||
#ifdef TARGET_PPC64
|
||||
disasm_info.mach = bfd_mach_ppc64;
|
||||
#else
|
||||
disasm_info.mach = bfd_mach_ppc;
|
||||
#endif
|
||||
print_insn = print_insn_ppc;
|
||||
#elif defined(TARGET_M68K)
|
||||
print_insn = print_insn_m68k;
|
||||
#elif defined(TARGET_MIPS)
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
print_insn = print_insn_big_mips;
|
||||
#else
|
||||
print_insn = print_insn_little_mips;
|
||||
#endif
|
||||
#else
|
||||
term_printf("0x" TARGET_FMT_lx
|
||||
": Asm output not supported on this arch\n", pc);
|
||||
return;
|
||||
#endif
|
||||
|
||||
for(i = 0; i < nb_insn; i++) {
|
||||
term_printf("0x" TARGET_FMT_lx ": ", pc);
|
||||
count = print_insn(pc, &disasm_info);
|
||||
term_printf("\n");
|
||||
if (count < 0)
|
||||
break;
|
||||
pc += count;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef _QEMU_DISAS_H
|
||||
#define _QEMU_DISAS_H
|
||||
|
||||
/* Disassemble this for me please... (debugging). */
|
||||
void disas(FILE *out, void *code, unsigned long size);
|
||||
void target_disas(FILE *out, target_ulong code, target_ulong size, int flags);
|
||||
void monitor_disas(CPUState *env,
|
||||
target_ulong pc, int nb_insn, int is_physical, int flags);
|
||||
|
||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||
const char *lookup_symbol(target_ulong orig_addr);
|
||||
|
||||
/* Filled in by elfload.c. Simplistic, but will do for now. */
|
||||
extern struct syminfo {
|
||||
unsigned int disas_num_syms;
|
||||
void *disas_symtab;
|
||||
const char *disas_strtab;
|
||||
struct syminfo *next;
|
||||
} *syminfos;
|
||||
|
||||
#endif /* _QEMU_DISAS_H */
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* dyngen defines for micro operation code
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#if !defined(__DYNGEN_EXEC_H__)
|
||||
#define __DYNGEN_EXEC_H__
|
||||
|
||||
/* prevent Solaris from trying to typedef FILE in gcc's
|
||||
include/floatingpoint.h which will conflict with the
|
||||
definition down below */
|
||||
#ifdef __sun__
|
||||
#define _FILEDEFED
|
||||
#endif
|
||||
|
||||
/* NOTE: standard headers should be used with special care at this
|
||||
point because host CPU registers are used as global variables. Some
|
||||
host headers do not allow that. */
|
||||
#include <stddef.h>
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
// Linux/Sparc64 defines uint64_t
|
||||
#if !(defined (__sparc_v9__) && defined(__linux__))
|
||||
/* XXX may be done for all 64 bits targets ? */
|
||||
#if defined (__x86_64__) || defined(__ia64)
|
||||
typedef unsigned long uint64_t;
|
||||
#else
|
||||
typedef unsigned long long uint64_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* if Solaris/__sun__, don't typedef int8_t, as it will be typedef'd
|
||||
prior to this and will cause an error in compliation, conflicting
|
||||
with /usr/include/sys/int_types.h, line 75 */
|
||||
#ifndef __sun__
|
||||
typedef signed char int8_t;
|
||||
#endif
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
// Linux/Sparc64 defines int64_t
|
||||
#if !(defined (__sparc_v9__) && defined(__linux__))
|
||||
#if defined (__x86_64__) || defined(__ia64)
|
||||
typedef signed long int64_t;
|
||||
#else
|
||||
typedef signed long long int64_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* XXX: This may be wrong for 64-bit ILP32 hosts. */
|
||||
typedef void * host_reg_t;
|
||||
|
||||
#define INT8_MIN (-128)
|
||||
#define INT16_MIN (-32767-1)
|
||||
#define INT32_MIN (-2147483647-1)
|
||||
#define INT64_MIN (-(int64_t)(9223372036854775807)-1)
|
||||
#define INT8_MAX (127)
|
||||
#define INT16_MAX (32767)
|
||||
#define INT32_MAX (2147483647)
|
||||
#define INT64_MAX ((int64_t)(9223372036854775807))
|
||||
#define UINT8_MAX (255)
|
||||
#define UINT16_MAX (65535)
|
||||
#define UINT32_MAX (4294967295U)
|
||||
#define UINT64_MAX ((uint64_t)(18446744073709551615))
|
||||
|
||||
typedef struct FILE FILE;
|
||||
extern int fprintf(FILE *, const char *, ...);
|
||||
extern int printf(const char *, ...);
|
||||
#undef NULL
|
||||
#define NULL 0
|
||||
|
||||
#ifdef __i386__
|
||||
#define AREG0 "ebp"
|
||||
#define AREG1 "ebx"
|
||||
#define AREG2 "esi"
|
||||
#define AREG3 "edi"
|
||||
#endif
|
||||
#ifdef __x86_64__
|
||||
#define AREG0 "rbp"
|
||||
#define AREG1 "rbx"
|
||||
#define AREG2 "r12"
|
||||
#define AREG3 "r13"
|
||||
//#define AREG4 "r14"
|
||||
//#define AREG5 "r15"
|
||||
#endif
|
||||
#ifdef __powerpc__
|
||||
#define AREG0 "r27"
|
||||
#define AREG1 "r24"
|
||||
#define AREG2 "r25"
|
||||
#define AREG3 "r26"
|
||||
/* XXX: suppress this hack */
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#define AREG4 "r16"
|
||||
#define AREG5 "r17"
|
||||
#define AREG6 "r18"
|
||||
#define AREG7 "r19"
|
||||
#define AREG8 "r20"
|
||||
#define AREG9 "r21"
|
||||
#define AREG10 "r22"
|
||||
#define AREG11 "r23"
|
||||
#endif
|
||||
#define USE_INT_TO_FLOAT_HELPERS
|
||||
#define BUGGY_GCC_DIV64
|
||||
#endif
|
||||
#ifdef __arm__
|
||||
#define AREG0 "r7"
|
||||
#define AREG1 "r4"
|
||||
#define AREG2 "r5"
|
||||
#define AREG3 "r6"
|
||||
#endif
|
||||
#ifdef __mips__
|
||||
#define AREG0 "s3"
|
||||
#define AREG1 "s0"
|
||||
#define AREG2 "s1"
|
||||
#define AREG3 "s2"
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
#ifdef HOST_SOLARIS
|
||||
#define AREG0 "g2"
|
||||
#define AREG1 "g3"
|
||||
#define AREG2 "g4"
|
||||
#define AREG3 "g5"
|
||||
#define AREG4 "g6"
|
||||
#else
|
||||
#ifdef __sparc_v9__
|
||||
#define AREG0 "g1"
|
||||
#define AREG1 "g4"
|
||||
#define AREG2 "g5"
|
||||
#define AREG3 "g7"
|
||||
#else
|
||||
#define AREG0 "g6"
|
||||
#define AREG1 "g1"
|
||||
#define AREG2 "g2"
|
||||
#define AREG3 "g3"
|
||||
#define AREG4 "l0"
|
||||
#define AREG5 "l1"
|
||||
#define AREG6 "l2"
|
||||
#define AREG7 "l3"
|
||||
#define AREG8 "l4"
|
||||
#define AREG9 "l5"
|
||||
#define AREG10 "l6"
|
||||
#define AREG11 "l7"
|
||||
#endif
|
||||
#endif
|
||||
#define USE_FP_CONVERT
|
||||
#endif
|
||||
#ifdef __s390__
|
||||
#define AREG0 "r10"
|
||||
#define AREG1 "r7"
|
||||
#define AREG2 "r8"
|
||||
#define AREG3 "r9"
|
||||
#endif
|
||||
#ifdef __alpha__
|
||||
/* Note $15 is the frame pointer, so anything in op-i386.c that would
|
||||
require a frame pointer, like alloca, would probably loose. */
|
||||
#define AREG0 "$15"
|
||||
#define AREG1 "$9"
|
||||
#define AREG2 "$10"
|
||||
#define AREG3 "$11"
|
||||
#define AREG4 "$12"
|
||||
#define AREG5 "$13"
|
||||
#define AREG6 "$14"
|
||||
#endif
|
||||
#ifdef __mc68000
|
||||
#define AREG0 "%a5"
|
||||
#define AREG1 "%a4"
|
||||
#define AREG2 "%d7"
|
||||
#define AREG3 "%d6"
|
||||
#define AREG4 "%d5"
|
||||
#endif
|
||||
#ifdef __ia64__
|
||||
#define AREG0 "r7"
|
||||
#define AREG1 "r4"
|
||||
#define AREG2 "r5"
|
||||
#define AREG3 "r6"
|
||||
#endif
|
||||
|
||||
/* force GCC to generate only one epilog at the end of the function */
|
||||
#define FORCE_RET() __asm__ __volatile__("" : : : "memory");
|
||||
|
||||
#ifndef OPPROTO
|
||||
#define OPPROTO
|
||||
#endif
|
||||
|
||||
#define xglue(x, y) x ## y
|
||||
#define glue(x, y) xglue(x, y)
|
||||
#define stringify(s) tostring(s)
|
||||
#define tostring(s) #s
|
||||
|
||||
#ifdef __alpha__
|
||||
/* the symbols are considered non exported so a br immediate is generated */
|
||||
#define __hidden __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define __hidden
|
||||
#endif
|
||||
|
||||
#if defined(__alpha__)
|
||||
/* Suggested by Richard Henderson. This will result in code like
|
||||
ldah $0,__op_param1($29) !gprelhigh
|
||||
lda $0,__op_param1($0) !gprellow
|
||||
We can then conveniently change $29 to $31 and adapt the offsets to
|
||||
emit the appropriate constant. */
|
||||
extern int __op_param1 __hidden;
|
||||
extern int __op_param2 __hidden;
|
||||
extern int __op_param3 __hidden;
|
||||
#define PARAM1 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param1)); _r; })
|
||||
#define PARAM2 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param2)); _r; })
|
||||
#define PARAM3 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param3)); _r; })
|
||||
#else
|
||||
#if defined(__APPLE__)
|
||||
static int __op_param1, __op_param2, __op_param3;
|
||||
#else
|
||||
extern int __op_param1, __op_param2, __op_param3;
|
||||
#endif
|
||||
#define PARAM1 ((long)(&__op_param1))
|
||||
#define PARAM2 ((long)(&__op_param2))
|
||||
#define PARAM3 ((long)(&__op_param3))
|
||||
#endif /* !defined(__alpha__) */
|
||||
|
||||
extern int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
#define ASM_NAME(x) "_" #x
|
||||
#else
|
||||
#define ASM_NAME(x) #x
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
#define EXIT_TB() asm volatile ("ret")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __x86_64__
|
||||
#define EXIT_TB() asm volatile ("ret")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __powerpc__
|
||||
#define EXIT_TB() asm volatile ("blr")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __s390__
|
||||
#define EXIT_TB() asm volatile ("br %r14")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __alpha__
|
||||
#define EXIT_TB() asm volatile ("ret")
|
||||
#endif
|
||||
#ifdef __ia64__
|
||||
#define EXIT_TB() asm volatile ("br.ret.sptk.many b0;;")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("br.sptk.many " \
|
||||
ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
#define EXIT_TB() asm volatile ("jmpl %i0 + 8, %g0; nop")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("ba " ASM_NAME(__op_gen_label) #n ";nop")
|
||||
#endif
|
||||
#ifdef __arm__
|
||||
#define EXIT_TB() asm volatile ("b exec_loop")
|
||||
#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __mc68000
|
||||
#define EXIT_TB() asm volatile ("rts")
|
||||
#endif
|
||||
|
||||
#endif /* !defined(__DYNGEN_EXEC_H__) */
|
||||
@@ -0,0 +1,9 @@
|
||||
static inline int gen_new_label(void)
|
||||
{
|
||||
return nb_gen_labels++;
|
||||
}
|
||||
|
||||
static inline void gen_set_label(int n)
|
||||
{
|
||||
gen_labels[n] = gen_opc_ptr - gen_opc_buf;
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* dyngen helpers
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
int __op_param1, __op_param2, __op_param3;
|
||||
#if defined(__sparc__) || defined(__arm__)
|
||||
void __op_gen_label1(){}
|
||||
void __op_gen_label2(){}
|
||||
void __op_gen_label3(){}
|
||||
#else
|
||||
int __op_gen_label1, __op_gen_label2, __op_gen_label3;
|
||||
#endif
|
||||
int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
|
||||
|
||||
#ifdef __i386__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __s390__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ia64__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
while (start < stop) {
|
||||
asm volatile ("fc %0" :: "r"(start));
|
||||
start += 32;
|
||||
}
|
||||
asm volatile (";;sync.i;;srlz.i;;");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc__
|
||||
|
||||
#define MIN_CACHE_LINE_SIZE 8 /* conservative value */
|
||||
|
||||
static void inline flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
unsigned long p;
|
||||
|
||||
start &= ~(MIN_CACHE_LINE_SIZE - 1);
|
||||
stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1);
|
||||
|
||||
for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
|
||||
asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
|
||||
}
|
||||
asm volatile ("sync" : : : "memory");
|
||||
for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
|
||||
asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
|
||||
}
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("isync" : : : "memory");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
asm ("imb");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __sparc__
|
||||
|
||||
static void inline flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
unsigned long p;
|
||||
|
||||
p = start & ~(8UL - 1UL);
|
||||
stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL);
|
||||
|
||||
for (; p < stop; p += 8)
|
||||
__asm__ __volatile__("flush\t%0" : : "r" (p));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __arm__
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
register unsigned long _beg __asm ("a1") = start;
|
||||
register unsigned long _end __asm ("a2") = stop;
|
||||
register unsigned long _flg __asm ("a3") = 0;
|
||||
__asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __mc68000
|
||||
#include <asm/cachectl.h>
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
cacheflush(start,FLUSH_SCOPE_LINE,FLUSH_CACHE_BOTH,stop-start+16);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
|
||||
register int gp asm("$29");
|
||||
|
||||
static inline void immediate_ldah(void *p, int val) {
|
||||
uint32_t *dest = p;
|
||||
long high = ((val >> 16) + ((val >> 15) & 1)) & 0xffff;
|
||||
|
||||
*dest &= ~0xffff;
|
||||
*dest |= high;
|
||||
*dest |= 31 << 16;
|
||||
}
|
||||
static inline void immediate_lda(void *dest, int val) {
|
||||
*(uint16_t *) dest = val;
|
||||
}
|
||||
void fix_bsr(void *p, int offset) {
|
||||
uint32_t *dest = p;
|
||||
*dest &= ~((1 << 21) - 1);
|
||||
*dest |= (offset >> 2) & ((1 << 21) - 1);
|
||||
}
|
||||
|
||||
#endif /* __alpha__ */
|
||||
|
||||
#ifdef __arm__
|
||||
|
||||
#define ARM_LDR_TABLE_SIZE 1024
|
||||
|
||||
typedef struct LDREntry {
|
||||
uint8_t *ptr;
|
||||
uint32_t *data_ptr;
|
||||
unsigned type:2;
|
||||
} LDREntry;
|
||||
|
||||
static LDREntry arm_ldr_table[1024];
|
||||
static uint32_t arm_data_table[ARM_LDR_TABLE_SIZE];
|
||||
|
||||
extern char exec_loop;
|
||||
|
||||
static inline void arm_reloc_pc24(uint32_t *ptr, uint32_t insn, int val)
|
||||
{
|
||||
*ptr = (insn & ~0xffffff) | ((insn + ((val - (int)ptr) >> 2)) & 0xffffff);
|
||||
}
|
||||
|
||||
static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr,
|
||||
LDREntry *ldr_start, LDREntry *ldr_end,
|
||||
uint32_t *data_start, uint32_t *data_end,
|
||||
int gen_jmp)
|
||||
{
|
||||
LDREntry *le;
|
||||
uint32_t *ptr;
|
||||
int offset, data_size, target;
|
||||
uint8_t *data_ptr;
|
||||
uint32_t insn;
|
||||
uint32_t mask;
|
||||
|
||||
data_size = (data_end - data_start) << 2;
|
||||
|
||||
if (gen_jmp) {
|
||||
/* generate branch to skip the data */
|
||||
if (data_size == 0)
|
||||
return gen_code_ptr;
|
||||
target = (long)gen_code_ptr + data_size + 4;
|
||||
arm_reloc_pc24((uint32_t *)gen_code_ptr, 0xeafffffe, target);
|
||||
gen_code_ptr += 4;
|
||||
}
|
||||
|
||||
/* copy the data */
|
||||
data_ptr = gen_code_ptr;
|
||||
memcpy(gen_code_ptr, data_start, data_size);
|
||||
gen_code_ptr += data_size;
|
||||
|
||||
/* patch the ldr to point to the data */
|
||||
for(le = ldr_start; le < ldr_end; le++) {
|
||||
ptr = (uint32_t *)le->ptr;
|
||||
offset = ((unsigned long)(le->data_ptr) - (unsigned long)data_start) +
|
||||
(unsigned long)data_ptr -
|
||||
(unsigned long)ptr - 8;
|
||||
if (offset < 0) {
|
||||
fprintf(stderr, "Negative constant pool offset\n");
|
||||
abort();
|
||||
}
|
||||
switch (le->type) {
|
||||
case 0: /* ldr */
|
||||
mask = ~0x00800fff;
|
||||
if (offset >= 4096) {
|
||||
fprintf(stderr, "Bad ldr offset\n");
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
case 1: /* ldc */
|
||||
mask = ~0x008000ff;
|
||||
if (offset >= 1024 ) {
|
||||
fprintf(stderr, "Bad ldc offset\n");
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
case 2: /* add */
|
||||
mask = ~0xfff;
|
||||
if (offset >= 1024 ) {
|
||||
fprintf(stderr, "Bad add offset\n");
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Bad pc relative fixup\n");
|
||||
abort();
|
||||
}
|
||||
insn = *ptr & mask;
|
||||
switch (le->type) {
|
||||
case 0: /* ldr */
|
||||
insn |= offset | 0x00800000;
|
||||
break;
|
||||
case 1: /* ldc */
|
||||
insn |= (offset >> 2) | 0x00800000;
|
||||
break;
|
||||
case 2: /* add */
|
||||
insn |= (offset >> 2) | 0xf00;
|
||||
break;
|
||||
}
|
||||
*ptr = insn;
|
||||
}
|
||||
return gen_code_ptr;
|
||||
}
|
||||
|
||||
#endif /* __arm__ */
|
||||
|
||||
#ifdef __ia64
|
||||
|
||||
|
||||
/* Patch instruction with "val" where "mask" has 1 bits. */
|
||||
static inline void ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val)
|
||||
{
|
||||
uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16);
|
||||
# define insn_mask ((1UL << 41) - 1)
|
||||
unsigned long shift;
|
||||
|
||||
b0 = b[0]; b1 = b[1];
|
||||
shift = 5 + 41 * (insn_addr % 16); /* 5 template, 3 x 41-bit insns */
|
||||
if (shift >= 64) {
|
||||
m1 = mask << (shift - 64);
|
||||
v1 = val << (shift - 64);
|
||||
} else {
|
||||
m0 = mask << shift; m1 = mask >> (64 - shift);
|
||||
v0 = val << shift; v1 = val >> (64 - shift);
|
||||
b[0] = (b0 & ~m0) | (v0 & m0);
|
||||
}
|
||||
b[1] = (b1 & ~m1) | (v1 & m1);
|
||||
}
|
||||
|
||||
static inline void ia64_patch_imm60 (uint64_t insn_addr, uint64_t val)
|
||||
{
|
||||
ia64_patch(insn_addr,
|
||||
0x011ffffe000UL,
|
||||
( ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */
|
||||
| ((val & 0x00000000000fffffUL) << 13) /* bit 0 -> 13 */));
|
||||
ia64_patch(insn_addr - 1, 0x1fffffffffcUL, val >> 18);
|
||||
}
|
||||
|
||||
static inline void ia64_imm64 (void *insn, uint64_t val)
|
||||
{
|
||||
/* Ignore the slot number of the relocation; GCC and Intel
|
||||
toolchains differed for some time on whether IMM64 relocs are
|
||||
against slot 1 (Intel) or slot 2 (GCC). */
|
||||
uint64_t insn_addr = (uint64_t) insn & ~3UL;
|
||||
|
||||
ia64_patch(insn_addr + 2,
|
||||
0x01fffefe000UL,
|
||||
( ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */
|
||||
| ((val & 0x0000000000200000UL) << 0) /* bit 21 -> 21 */
|
||||
| ((val & 0x00000000001f0000UL) << 6) /* bit 16 -> 22 */
|
||||
| ((val & 0x000000000000ff80UL) << 20) /* bit 7 -> 27 */
|
||||
| ((val & 0x000000000000007fUL) << 13) /* bit 0 -> 13 */)
|
||||
);
|
||||
ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22);
|
||||
}
|
||||
|
||||
static inline void ia64_imm60b (void *insn, uint64_t val)
|
||||
{
|
||||
/* Ignore the slot number of the relocation; GCC and Intel
|
||||
toolchains differed for some time on whether IMM64 relocs are
|
||||
against slot 1 (Intel) or slot 2 (GCC). */
|
||||
uint64_t insn_addr = (uint64_t) insn & ~3UL;
|
||||
|
||||
if (val + ((uint64_t) 1 << 59) >= (1UL << 60))
|
||||
fprintf(stderr, "%s: value %ld out of IMM60 range\n",
|
||||
__FUNCTION__, (int64_t) val);
|
||||
ia64_patch_imm60(insn_addr + 2, val);
|
||||
}
|
||||
|
||||
static inline void ia64_imm22 (void *insn, uint64_t val)
|
||||
{
|
||||
if (val + (1 << 21) >= (1 << 22))
|
||||
fprintf(stderr, "%s: value %li out of IMM22 range\n",
|
||||
__FUNCTION__, (int64_t)val);
|
||||
ia64_patch((uint64_t) insn, 0x01fffcfe000UL,
|
||||
( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
|
||||
| ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */
|
||||
| ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */
|
||||
| ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */));
|
||||
}
|
||||
|
||||
/* Like ia64_imm22(), but also clear bits 20-21. For addl, this has
|
||||
the effect of turning "addl rX=imm22,rY" into "addl
|
||||
rX=imm22,r0". */
|
||||
static inline void ia64_imm22_r0 (void *insn, uint64_t val)
|
||||
{
|
||||
if (val + (1 << 21) >= (1 << 22))
|
||||
fprintf(stderr, "%s: value %li out of IMM22 range\n",
|
||||
__FUNCTION__, (int64_t)val);
|
||||
ia64_patch((uint64_t) insn, 0x01fffcfe000UL | (0x3UL << 20),
|
||||
( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
|
||||
| ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */
|
||||
| ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */
|
||||
| ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */));
|
||||
}
|
||||
|
||||
static inline void ia64_imm21b (void *insn, uint64_t val)
|
||||
{
|
||||
if (val + (1 << 20) >= (1 << 21))
|
||||
fprintf(stderr, "%s: value %li out of IMM21b range\n",
|
||||
__FUNCTION__, (int64_t)val);
|
||||
ia64_patch((uint64_t) insn, 0x11ffffe000UL,
|
||||
( ((val & 0x100000UL) << 16) /* bit 20 -> 36 */
|
||||
| ((val & 0x0fffffUL) << 13) /* bit 0 -> 13 */));
|
||||
}
|
||||
|
||||
static inline void ia64_nop_b (void *insn)
|
||||
{
|
||||
ia64_patch((uint64_t) insn, (1UL << 41) - 1, 2UL << 37);
|
||||
}
|
||||
|
||||
static inline void ia64_ldxmov(void *insn, uint64_t val)
|
||||
{
|
||||
if (val + (1 << 21) < (1 << 22))
|
||||
ia64_patch((uint64_t) insn, 0x1fff80fe000UL, 8UL << 37);
|
||||
}
|
||||
|
||||
static inline int ia64_patch_ltoff(void *insn, uint64_t val,
|
||||
int relaxable)
|
||||
{
|
||||
if (relaxable && (val + (1 << 21) < (1 << 22))) {
|
||||
ia64_imm22_r0(insn, val);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ia64_fixup {
|
||||
struct ia64_fixup *next;
|
||||
void *addr; /* address that needs to be patched */
|
||||
long value;
|
||||
};
|
||||
|
||||
#define IA64_PLT(insn, plt_index) \
|
||||
do { \
|
||||
struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \
|
||||
fixup->next = plt_fixes; \
|
||||
plt_fixes = fixup; \
|
||||
fixup->addr = (insn); \
|
||||
fixup->value = (plt_index); \
|
||||
plt_offset[(plt_index)] = 1; \
|
||||
} while (0)
|
||||
|
||||
#define IA64_LTOFF(insn, val, relaxable) \
|
||||
do { \
|
||||
if (ia64_patch_ltoff(insn, val, relaxable)) { \
|
||||
struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \
|
||||
fixup->next = ltoff_fixes; \
|
||||
ltoff_fixes = fixup; \
|
||||
fixup->addr = (insn); \
|
||||
fixup->value = (val); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static inline void ia64_apply_fixes (uint8_t **gen_code_pp,
|
||||
struct ia64_fixup *ltoff_fixes,
|
||||
uint64_t gp,
|
||||
struct ia64_fixup *plt_fixes,
|
||||
int num_plts,
|
||||
unsigned long *plt_target,
|
||||
unsigned int *plt_offset)
|
||||
{
|
||||
static const uint8_t plt_bundle[] = {
|
||||
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; movl r1=GP */
|
||||
0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60,
|
||||
|
||||
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; brl IP */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0
|
||||
};
|
||||
uint8_t *gen_code_ptr = *gen_code_pp, *plt_start, *got_start, *vp;
|
||||
struct ia64_fixup *fixup;
|
||||
unsigned int offset = 0;
|
||||
struct fdesc {
|
||||
long ip;
|
||||
long gp;
|
||||
} *fdesc;
|
||||
int i;
|
||||
|
||||
if (plt_fixes) {
|
||||
plt_start = gen_code_ptr;
|
||||
|
||||
for (i = 0; i < num_plts; ++i) {
|
||||
if (plt_offset[i]) {
|
||||
plt_offset[i] = offset;
|
||||
offset += sizeof(plt_bundle);
|
||||
|
||||
fdesc = (struct fdesc *) plt_target[i];
|
||||
memcpy(gen_code_ptr, plt_bundle, sizeof(plt_bundle));
|
||||
ia64_imm64 (gen_code_ptr + 0x02, fdesc->gp);
|
||||
ia64_imm60b(gen_code_ptr + 0x12,
|
||||
(fdesc->ip - (long) (gen_code_ptr + 0x10)) >> 4);
|
||||
gen_code_ptr += sizeof(plt_bundle);
|
||||
}
|
||||
}
|
||||
|
||||
for (fixup = plt_fixes; fixup; fixup = fixup->next)
|
||||
ia64_imm21b(fixup->addr,
|
||||
((long) plt_start + plt_offset[fixup->value]
|
||||
- ((long) fixup->addr & ~0xf)) >> 4);
|
||||
}
|
||||
|
||||
got_start = gen_code_ptr;
|
||||
|
||||
/* First, create the GOT: */
|
||||
for (fixup = ltoff_fixes; fixup; fixup = fixup->next) {
|
||||
/* first check if we already have this value in the GOT: */
|
||||
for (vp = got_start; vp < gen_code_ptr; ++vp)
|
||||
if (*(uint64_t *) vp == fixup->value)
|
||||
break;
|
||||
if (vp == gen_code_ptr) {
|
||||
/* Nope, we need to put the value in the GOT: */
|
||||
*(uint64_t *) vp = fixup->value;
|
||||
gen_code_ptr += 8;
|
||||
}
|
||||
ia64_imm22(fixup->addr, (long) vp - gp);
|
||||
}
|
||||
/* Keep code ptr aligned. */
|
||||
if ((long) gen_code_ptr & 15)
|
||||
gen_code_ptr += 8;
|
||||
*gen_code_pp = gen_code_ptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,207 @@
|
||||
static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr)
|
||||
{
|
||||
bswap16s(&ehdr->e_type); /* Object file type */
|
||||
bswap16s(&ehdr->e_machine); /* Architecture */
|
||||
bswap32s(&ehdr->e_version); /* Object file version */
|
||||
bswapSZs(&ehdr->e_entry); /* Entry point virtual address */
|
||||
bswapSZs(&ehdr->e_phoff); /* Program header table file offset */
|
||||
bswapSZs(&ehdr->e_shoff); /* Section header table file offset */
|
||||
bswap32s(&ehdr->e_flags); /* Processor-specific flags */
|
||||
bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
|
||||
bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
|
||||
bswap16s(&ehdr->e_phnum); /* Program header table entry count */
|
||||
bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
|
||||
bswap16s(&ehdr->e_shnum); /* Section header table entry count */
|
||||
bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
|
||||
}
|
||||
|
||||
static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr)
|
||||
{
|
||||
bswap32s(&phdr->p_type); /* Segment type */
|
||||
bswapSZs(&phdr->p_offset); /* Segment file offset */
|
||||
bswapSZs(&phdr->p_vaddr); /* Segment virtual address */
|
||||
bswapSZs(&phdr->p_paddr); /* Segment physical address */
|
||||
bswapSZs(&phdr->p_filesz); /* Segment size in file */
|
||||
bswapSZs(&phdr->p_memsz); /* Segment size in memory */
|
||||
bswap32s(&phdr->p_flags); /* Segment flags */
|
||||
bswapSZs(&phdr->p_align); /* Segment alignment */
|
||||
}
|
||||
|
||||
static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr)
|
||||
{
|
||||
bswap32s(&shdr->sh_name);
|
||||
bswap32s(&shdr->sh_type);
|
||||
bswapSZs(&shdr->sh_flags);
|
||||
bswapSZs(&shdr->sh_addr);
|
||||
bswapSZs(&shdr->sh_offset);
|
||||
bswapSZs(&shdr->sh_size);
|
||||
bswap32s(&shdr->sh_link);
|
||||
bswap32s(&shdr->sh_info);
|
||||
bswapSZs(&shdr->sh_addralign);
|
||||
bswapSZs(&shdr->sh_entsize);
|
||||
}
|
||||
|
||||
static void glue(bswap_sym, SZ)(struct elf_sym *sym)
|
||||
{
|
||||
bswap32s(&sym->st_name);
|
||||
bswapSZs(&sym->st_value);
|
||||
bswapSZs(&sym->st_size);
|
||||
bswap16s(&sym->st_shndx);
|
||||
}
|
||||
|
||||
static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table,
|
||||
int n, int type)
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<n;i++) {
|
||||
if (shdr_table[i].sh_type == type)
|
||||
return shdr_table + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
|
||||
{
|
||||
struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
|
||||
struct elf_sym *syms = NULL;
|
||||
#if (SZ == 64)
|
||||
struct elf32_sym *syms32 = NULL;
|
||||
#endif
|
||||
struct syminfo *s;
|
||||
int nsyms, i;
|
||||
char *str = NULL;
|
||||
|
||||
shdr_table = load_at(fd, ehdr->e_shoff,
|
||||
sizeof(struct elf_shdr) * ehdr->e_shnum);
|
||||
if (!shdr_table)
|
||||
return -1;
|
||||
|
||||
if (must_swab) {
|
||||
for (i = 0; i < ehdr->e_shnum; i++) {
|
||||
glue(bswap_shdr, SZ)(shdr_table + i);
|
||||
}
|
||||
}
|
||||
|
||||
symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB);
|
||||
if (!symtab)
|
||||
goto fail;
|
||||
syms = load_at(fd, symtab->sh_offset, symtab->sh_size);
|
||||
if (!syms)
|
||||
goto fail;
|
||||
|
||||
nsyms = symtab->sh_size / sizeof(struct elf_sym);
|
||||
#if (SZ == 64)
|
||||
syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym));
|
||||
#endif
|
||||
for (i = 0; i < nsyms; i++) {
|
||||
if (must_swab)
|
||||
glue(bswap_sym, SZ)(&syms[i]);
|
||||
#if (SZ == 64)
|
||||
syms32[i].st_name = syms[i].st_name;
|
||||
syms32[i].st_info = syms[i].st_info;
|
||||
syms32[i].st_other = syms[i].st_other;
|
||||
syms32[i].st_shndx = syms[i].st_shndx;
|
||||
syms32[i].st_value = syms[i].st_value & 0xffffffff;
|
||||
syms32[i].st_size = syms[i].st_size & 0xffffffff;
|
||||
#endif
|
||||
}
|
||||
/* String table */
|
||||
if (symtab->sh_link >= ehdr->e_shnum)
|
||||
goto fail;
|
||||
strtab = &shdr_table[symtab->sh_link];
|
||||
|
||||
str = load_at(fd, strtab->sh_offset, strtab->sh_size);
|
||||
if (!str)
|
||||
goto fail;
|
||||
|
||||
/* Commit */
|
||||
s = qemu_mallocz(sizeof(*s));
|
||||
#if (SZ == 64)
|
||||
s->disas_symtab = syms32;
|
||||
qemu_free(syms);
|
||||
#else
|
||||
s->disas_symtab = syms;
|
||||
#endif
|
||||
s->disas_num_syms = nsyms;
|
||||
s->disas_strtab = str;
|
||||
s->next = syminfos;
|
||||
syminfos = s;
|
||||
qemu_free(shdr_table);
|
||||
return 0;
|
||||
fail:
|
||||
#if (SZ == 64)
|
||||
qemu_free(syms32);
|
||||
#endif
|
||||
qemu_free(syms);
|
||||
qemu_free(str);
|
||||
qemu_free(shdr_table);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend,
|
||||
int must_swab, uint64_t *pentry)
|
||||
{
|
||||
struct elfhdr ehdr;
|
||||
struct elf_phdr *phdr = NULL, *ph;
|
||||
int size, i, total_size;
|
||||
elf_word mem_size, addr;
|
||||
uint8_t *data = NULL;
|
||||
|
||||
if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
|
||||
goto fail;
|
||||
if (must_swab) {
|
||||
glue(bswap_ehdr, SZ)(&ehdr);
|
||||
}
|
||||
|
||||
if (ELF_MACHINE != ehdr.e_machine)
|
||||
goto fail;
|
||||
|
||||
if (pentry)
|
||||
*pentry = (uint64_t)ehdr.e_entry;
|
||||
|
||||
glue(load_symbols, SZ)(&ehdr, fd, must_swab);
|
||||
|
||||
size = ehdr.e_phnum * sizeof(phdr[0]);
|
||||
lseek(fd, ehdr.e_phoff, SEEK_SET);
|
||||
phdr = qemu_mallocz(size);
|
||||
if (!phdr)
|
||||
goto fail;
|
||||
if (read(fd, phdr, size) != size)
|
||||
goto fail;
|
||||
if (must_swab) {
|
||||
for(i = 0; i < ehdr.e_phnum; i++) {
|
||||
ph = &phdr[i];
|
||||
glue(bswap_phdr, SZ)(ph);
|
||||
}
|
||||
}
|
||||
|
||||
total_size = 0;
|
||||
for(i = 0; i < ehdr.e_phnum; i++) {
|
||||
ph = &phdr[i];
|
||||
if (ph->p_type == PT_LOAD) {
|
||||
mem_size = ph->p_memsz;
|
||||
/* XXX: avoid allocating */
|
||||
data = qemu_mallocz(mem_size);
|
||||
if (ph->p_filesz > 0) {
|
||||
if (lseek(fd, ph->p_offset, SEEK_SET) < 0)
|
||||
goto fail;
|
||||
if (read(fd, data, ph->p_filesz) != ph->p_filesz)
|
||||
goto fail;
|
||||
}
|
||||
addr = ph->p_vaddr + virt_to_phys_addend;
|
||||
|
||||
cpu_physical_memory_write_rom(addr, data, mem_size);
|
||||
|
||||
total_size += mem_size;
|
||||
|
||||
qemu_free(data);
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
qemu_free(phdr);
|
||||
return total_size;
|
||||
fail:
|
||||
qemu_free(data);
|
||||
qemu_free(phdr);
|
||||
return -1;
|
||||
}
|
||||
+615
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* internal execution defines for qemu
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* allow to see translation results - the slowdown should be negligible, so we leave it */
|
||||
#define DEBUG_DISAS
|
||||
|
||||
#ifndef glue
|
||||
#define xglue(x, y) x ## y
|
||||
#define glue(x, y) xglue(x, y)
|
||||
#define stringify(s) tostring(s)
|
||||
#define tostring(s) #s
|
||||
#endif
|
||||
|
||||
#if __GNUC__ < 3
|
||||
#define __builtin_expect(x, n) (x)
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
#define REGPARM(n) __attribute((regparm(n)))
|
||||
#else
|
||||
#define REGPARM(n)
|
||||
#endif
|
||||
|
||||
/* is_jmp field values */
|
||||
#define DISAS_NEXT 0 /* next instruction can be analyzed */
|
||||
#define DISAS_JUMP 1 /* only pc was modified dynamically */
|
||||
#define DISAS_UPDATE 2 /* cpu state was modified dynamically */
|
||||
#define DISAS_TB_JUMP 3 /* only pc was modified statically */
|
||||
|
||||
struct TranslationBlock;
|
||||
|
||||
/* XXX: make safe guess about sizes */
|
||||
#define MAX_OP_PER_INSTR 32
|
||||
#define OPC_BUF_SIZE 512
|
||||
#define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR)
|
||||
|
||||
#define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * 3)
|
||||
|
||||
extern uint16_t gen_opc_buf[OPC_BUF_SIZE];
|
||||
extern uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE];
|
||||
extern long gen_labels[OPC_BUF_SIZE];
|
||||
extern int nb_gen_labels;
|
||||
extern target_ulong gen_opc_pc[OPC_BUF_SIZE];
|
||||
extern target_ulong gen_opc_npc[OPC_BUF_SIZE];
|
||||
extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
|
||||
extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
|
||||
extern target_ulong gen_opc_jump_pc[2];
|
||||
extern uint32_t gen_opc_hflags[OPC_BUF_SIZE];
|
||||
|
||||
typedef void (GenOpFunc)(void);
|
||||
typedef void (GenOpFunc1)(long);
|
||||
typedef void (GenOpFunc2)(long, long);
|
||||
typedef void (GenOpFunc3)(long, long, long);
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
|
||||
void optimize_flags_init(void);
|
||||
|
||||
#endif
|
||||
|
||||
extern FILE *logfile;
|
||||
extern int loglevel;
|
||||
|
||||
int gen_intermediate_code(CPUState *env, struct TranslationBlock *tb);
|
||||
int gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb);
|
||||
void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf);
|
||||
int cpu_gen_code(CPUState *env, struct TranslationBlock *tb,
|
||||
int max_code_size, int *gen_code_size_ptr);
|
||||
int cpu_restore_state(struct TranslationBlock *tb,
|
||||
CPUState *env, unsigned long searched_pc,
|
||||
void *puc);
|
||||
int cpu_gen_code_copy(CPUState *env, struct TranslationBlock *tb,
|
||||
int max_code_size, int *gen_code_size_ptr);
|
||||
int cpu_restore_state_copy(struct TranslationBlock *tb,
|
||||
CPUState *env, unsigned long searched_pc,
|
||||
void *puc);
|
||||
void cpu_resume_from_signal(CPUState *env1, void *puc);
|
||||
void cpu_exec_init(CPUState *env);
|
||||
int page_unprotect(target_ulong address, unsigned long pc, void *puc);
|
||||
void tb_invalidate_phys_page_range(target_ulong start, target_ulong end,
|
||||
int is_cpu_write_access);
|
||||
void tb_invalidate_page_range(target_ulong start, target_ulong end);
|
||||
void tlb_flush_page(CPUState *env, target_ulong addr);
|
||||
void tlb_flush(CPUState *env, int flush_global);
|
||||
int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
|
||||
target_phys_addr_t paddr, int prot,
|
||||
int is_user, int is_softmmu);
|
||||
static inline int tlb_set_page(CPUState *env, target_ulong vaddr,
|
||||
target_phys_addr_t paddr, int prot,
|
||||
int is_user, int is_softmmu)
|
||||
{
|
||||
if (prot & PAGE_READ)
|
||||
prot |= PAGE_EXEC;
|
||||
return tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu);
|
||||
}
|
||||
|
||||
#define CODE_GEN_MAX_SIZE 65536
|
||||
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
|
||||
|
||||
#define CODE_GEN_PHYS_HASH_BITS 15
|
||||
#define CODE_GEN_PHYS_HASH_SIZE (1 << CODE_GEN_PHYS_HASH_BITS)
|
||||
|
||||
/* maximum total translate dcode allocated */
|
||||
|
||||
/* NOTE: the translated code area cannot be too big because on some
|
||||
archs the range of "fast" function calls is limited. Here is a
|
||||
summary of the ranges:
|
||||
|
||||
i386 : signed 32 bits
|
||||
arm : signed 26 bits
|
||||
ppc : signed 24 bits
|
||||
sparc : signed 32 bits
|
||||
alpha : signed 23 bits
|
||||
*/
|
||||
|
||||
#if defined(__alpha__)
|
||||
#define CODE_GEN_BUFFER_SIZE (2 * 1024 * 1024)
|
||||
#elif defined(__ia64)
|
||||
#define CODE_GEN_BUFFER_SIZE (4 * 1024 * 1024) /* range of addl */
|
||||
#elif defined(__powerpc__)
|
||||
#define CODE_GEN_BUFFER_SIZE (6 * 1024 * 1024)
|
||||
#else
|
||||
#define CODE_GEN_BUFFER_SIZE (16 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
//#define CODE_GEN_BUFFER_SIZE (128 * 1024)
|
||||
|
||||
/* estimated block size for TB allocation */
|
||||
/* XXX: use a per code average code fragment size and modulate it
|
||||
according to the host CPU */
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
#define CODE_GEN_AVG_BLOCK_SIZE 128
|
||||
#else
|
||||
#define CODE_GEN_AVG_BLOCK_SIZE 64
|
||||
#endif
|
||||
|
||||
#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / CODE_GEN_AVG_BLOCK_SIZE)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
#define USE_DIRECT_JUMP
|
||||
#endif
|
||||
#if defined(__i386__) && !defined(_WIN32)
|
||||
#define USE_DIRECT_JUMP
|
||||
#endif
|
||||
|
||||
typedef struct TranslationBlock {
|
||||
target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */
|
||||
target_ulong cs_base; /* CS base for this block */
|
||||
unsigned int flags; /* flags defining in which context the code was generated */
|
||||
uint16_t size; /* size of target code for this block (1 <=
|
||||
size <= TARGET_PAGE_SIZE) */
|
||||
uint16_t cflags; /* compile flags */
|
||||
#define CF_CODE_COPY 0x0001 /* block was generated in code copy mode */
|
||||
#define CF_TB_FP_USED 0x0002 /* fp ops are used in the TB */
|
||||
#define CF_FP_USED 0x0004 /* fp ops are used in the TB or in a chained TB */
|
||||
#define CF_SINGLE_INSN 0x0008 /* compile only a single instruction */
|
||||
|
||||
uint8_t *tc_ptr; /* pointer to the translated code */
|
||||
/* next matching tb for physical address. */
|
||||
struct TranslationBlock *phys_hash_next;
|
||||
/* first and second physical page containing code. The lower bit
|
||||
of the pointer tells the index in page_next[] */
|
||||
struct TranslationBlock *page_next[2];
|
||||
target_ulong page_addr[2];
|
||||
|
||||
/* the following data are used to directly call another TB from
|
||||
the code of this one. */
|
||||
uint16_t tb_next_offset[2]; /* offset of original jump target */
|
||||
#ifdef USE_DIRECT_JUMP
|
||||
uint16_t tb_jmp_offset[4]; /* offset of jump instruction */
|
||||
#else
|
||||
uint32_t tb_next[2]; /* address of jump generated code */
|
||||
#endif
|
||||
/* list of TBs jumping to this one. This is a circular list using
|
||||
the two least significant bits of the pointers to tell what is
|
||||
the next pointer: 0 = jmp_next[0], 1 = jmp_next[1], 2 =
|
||||
jmp_first */
|
||||
struct TranslationBlock *jmp_next[2];
|
||||
struct TranslationBlock *jmp_first;
|
||||
} TranslationBlock;
|
||||
|
||||
static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc)
|
||||
{
|
||||
target_ulong tmp;
|
||||
tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
|
||||
return (tmp >> TB_JMP_PAGE_BITS) & TB_JMP_PAGE_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
|
||||
{
|
||||
target_ulong tmp;
|
||||
tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
|
||||
return (((tmp >> TB_JMP_PAGE_BITS) & TB_JMP_PAGE_MASK) |
|
||||
(tmp & TB_JMP_ADDR_MASK));
|
||||
}
|
||||
|
||||
static inline unsigned int tb_phys_hash_func(unsigned long pc)
|
||||
{
|
||||
return pc & (CODE_GEN_PHYS_HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
TranslationBlock *tb_alloc(target_ulong pc);
|
||||
void tb_flush(CPUState *env);
|
||||
void tb_link_phys(TranslationBlock *tb,
|
||||
target_ulong phys_pc, target_ulong phys_page2);
|
||||
|
||||
extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
|
||||
|
||||
extern uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
|
||||
extern uint8_t *code_gen_ptr;
|
||||
|
||||
#if defined(USE_DIRECT_JUMP)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
|
||||
{
|
||||
uint32_t val, *ptr;
|
||||
|
||||
/* patch the branch destination */
|
||||
ptr = (uint32_t *)jmp_addr;
|
||||
val = *ptr;
|
||||
val = (val & ~0x03fffffc) | ((addr - jmp_addr) & 0x03fffffc);
|
||||
*ptr = val;
|
||||
/* flush icache */
|
||||
asm volatile ("dcbst 0,%0" : : "r"(ptr) : "memory");
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("icbi 0,%0" : : "r"(ptr) : "memory");
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("isync" : : : "memory");
|
||||
}
|
||||
#elif defined(__i386__)
|
||||
static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
|
||||
{
|
||||
/* patch the branch destination */
|
||||
*(uint32_t *)jmp_addr = addr - (jmp_addr + 4);
|
||||
/* no need to flush icache explicitely */
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void tb_set_jmp_target(TranslationBlock *tb,
|
||||
int n, unsigned long addr)
|
||||
{
|
||||
unsigned long offset;
|
||||
|
||||
offset = tb->tb_jmp_offset[n];
|
||||
tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
|
||||
offset = tb->tb_jmp_offset[n + 2];
|
||||
if (offset != 0xffff)
|
||||
tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* set the jump target */
|
||||
static inline void tb_set_jmp_target(TranslationBlock *tb,
|
||||
int n, unsigned long addr)
|
||||
{
|
||||
tb->tb_next[n] = addr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||
TranslationBlock *tb_next)
|
||||
{
|
||||
/* NOTE: this test is only needed for thread safety */
|
||||
if (!tb->jmp_next[n]) {
|
||||
/* patch the native jump address */
|
||||
tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr);
|
||||
|
||||
/* add in TB jmp circular list */
|
||||
tb->jmp_next[n] = tb_next->jmp_first;
|
||||
tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n));
|
||||
}
|
||||
}
|
||||
|
||||
TranslationBlock *tb_find_pc(unsigned long pc_ptr);
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(type, field) ((size_t) &((type *)0)->field)
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define ASM_DATA_SECTION ".section \".data\"\n"
|
||||
#define ASM_PREVIOUS_SECTION ".section .text\n"
|
||||
#elif defined(__APPLE__)
|
||||
#define ASM_DATA_SECTION ".data\n"
|
||||
#define ASM_PREVIOUS_SECTION ".text\n"
|
||||
#else
|
||||
#define ASM_DATA_SECTION ".section \".data\"\n"
|
||||
#define ASM_PREVIOUS_SECTION ".previous\n"
|
||||
#endif
|
||||
|
||||
#define ASM_OP_LABEL_NAME(n, opname) \
|
||||
ASM_NAME(__op_label) #n "." ASM_NAME(opname)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
|
||||
/* we patch the jump instruction directly */
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
asm volatile (ASM_DATA_SECTION\
|
||||
ASM_OP_LABEL_NAME(n, opname) ":\n"\
|
||||
".long 1f\n"\
|
||||
ASM_PREVIOUS_SECTION \
|
||||
"b " ASM_NAME(__op_jmp) #n "\n"\
|
||||
"1:\n");\
|
||||
} while (0)
|
||||
|
||||
#elif defined(__i386__) && defined(USE_DIRECT_JUMP)
|
||||
|
||||
/* we patch the jump instruction directly */
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
asm volatile (".section .data\n"\
|
||||
ASM_OP_LABEL_NAME(n, opname) ":\n"\
|
||||
".long 1f\n"\
|
||||
ASM_PREVIOUS_SECTION \
|
||||
"jmp " ASM_NAME(__op_jmp) #n "\n"\
|
||||
"1:\n");\
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
/* jump to next block operations (more portable code, does not need
|
||||
cache flushing, but slower because of indirect jump) */
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
static void __attribute__((unused)) *dummy ## n = &&dummy_label ## n;\
|
||||
static void __attribute__((unused)) *__op_label ## n \
|
||||
__asm__(ASM_OP_LABEL_NAME(n, opname)) = &&label ## n;\
|
||||
goto *(void *)(((TranslationBlock *)tbparam)->tb_next[n]);\
|
||||
label ## n: ;\
|
||||
dummy_label ## n: ;\
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
|
||||
extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
|
||||
extern void *io_mem_opaque[IO_MEM_NB_ENTRIES];
|
||||
|
||||
#ifdef __powerpc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
__asm__ __volatile__ (
|
||||
"0: lwarx %0,0,%1\n"
|
||||
" xor. %0,%3,%0\n"
|
||||
" bne 1f\n"
|
||||
" stwcx. %2,0,%1\n"
|
||||
" bne- 0b\n"
|
||||
"1: "
|
||||
: "=&r" (ret)
|
||||
: "r" (p), "r" (1), "r" (0)
|
||||
: "cr0", "memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
long int readval = 0;
|
||||
|
||||
__asm__ __volatile__ ("lock; cmpxchgl %2, %0"
|
||||
: "+m" (*p), "+a" (readval)
|
||||
: "r" (1)
|
||||
: "cc");
|
||||
return readval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
long int readval = 0;
|
||||
|
||||
__asm__ __volatile__ ("lock; cmpxchgl %2, %0"
|
||||
: "+m" (*p), "+a" (readval)
|
||||
: "r" (1)
|
||||
: "cc");
|
||||
return readval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __s390__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__ __volatile__ ("0: cs %0,%1,0(%2)\n"
|
||||
" jl 0b"
|
||||
: "=&d" (ret)
|
||||
: "r" (1), "a" (p), "0" (*p)
|
||||
: "cc", "memory" );
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
unsigned long one;
|
||||
|
||||
__asm__ __volatile__ ("0: mov 1,%2\n"
|
||||
" ldl_l %0,%1\n"
|
||||
" stl_c %2,%1\n"
|
||||
" beq %2,1f\n"
|
||||
".subsection 2\n"
|
||||
"1: br 0b\n"
|
||||
".previous"
|
||||
: "=r" (ret), "=m" (*p), "=r" (one)
|
||||
: "m" (*p));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __sparc__
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__ __volatile__("ldstub [%1], %0"
|
||||
: "=r" (ret)
|
||||
: "r" (p)
|
||||
: "memory");
|
||||
|
||||
return (ret ? 1 : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __arm__
|
||||
static inline int testandset (int *spinlock)
|
||||
{
|
||||
register unsigned int ret;
|
||||
__asm__ __volatile__("swp %0, %1, [%2]"
|
||||
: "=r"(ret)
|
||||
: "0"(1), "r"(spinlock));
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __mc68000
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
char ret;
|
||||
__asm__ __volatile__("tas %1; sne %0"
|
||||
: "=r" (ret)
|
||||
: "m" (p)
|
||||
: "cc","memory");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ia64
|
||||
#include <ia64intrin.h>
|
||||
|
||||
static inline int testandset (int *p)
|
||||
{
|
||||
return __sync_lock_test_and_set (p, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef int spinlock_t;
|
||||
|
||||
#define SPIN_LOCK_UNLOCKED 0
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
static inline void spin_lock(spinlock_t *lock)
|
||||
{
|
||||
while (testandset(lock));
|
||||
}
|
||||
|
||||
static inline void spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline int spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
return !testandset(lock);
|
||||
}
|
||||
#else
|
||||
static inline void spin_lock(spinlock_t *lock)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern spinlock_t tb_lock;
|
||||
|
||||
extern int tb_invalidated_flag;
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
void tlb_fill(target_ulong addr, int is_write, int is_user,
|
||||
void *retaddr);
|
||||
|
||||
#define ACCESS_TYPE 3
|
||||
#define MEMSUFFIX _code
|
||||
#define env cpu_single_env
|
||||
|
||||
#define DATA_SIZE 1
|
||||
#include "softmmu_header.h"
|
||||
|
||||
#define DATA_SIZE 2
|
||||
#include "softmmu_header.h"
|
||||
|
||||
#define DATA_SIZE 4
|
||||
#include "softmmu_header.h"
|
||||
|
||||
#define DATA_SIZE 8
|
||||
#include "softmmu_header.h"
|
||||
|
||||
#undef ACCESS_TYPE
|
||||
#undef MEMSUFFIX
|
||||
#undef env
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
#else
|
||||
/* NOTE: this function can trigger an exception */
|
||||
/* NOTE2: the returned address is not exactly the physical address: it
|
||||
is the offset relative to phys_ram_base */
|
||||
static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
|
||||
{
|
||||
int is_user, index, pd;
|
||||
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
#if defined(TARGET_I386)
|
||||
is_user = ((env->hflags & HF_CPL_MASK) == 3);
|
||||
#elif defined (TARGET_PPC)
|
||||
is_user = msr_pr;
|
||||
#elif defined (TARGET_MIPS)
|
||||
is_user = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM);
|
||||
#elif defined (TARGET_SPARC)
|
||||
is_user = (env->psrs == 0);
|
||||
#elif defined (TARGET_ARM)
|
||||
is_user = ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR);
|
||||
#elif defined (TARGET_SH4)
|
||||
is_user = ((env->sr & SR_MD) == 0);
|
||||
#else
|
||||
#error unimplemented CPU
|
||||
#endif
|
||||
if (__builtin_expect(env->tlb_table[is_user][index].addr_code !=
|
||||
(addr & TARGET_PAGE_MASK), 0)) {
|
||||
ldub_code(addr);
|
||||
}
|
||||
pd = env->tlb_table[is_user][index].addr_code & ~TARGET_PAGE_MASK;
|
||||
if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
|
||||
cpu_abort(env, "Trying to execute code outside RAM or ROM at 0x%08lx\n", addr);
|
||||
}
|
||||
return addr + env->tlb_table[is_user][index].addend - (unsigned long)phys_ram_base;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_KQEMU
|
||||
#define KQEMU_MODIFY_PAGE_MASK (0xff & ~(VGA_DIRTY_FLAG | CODE_DIRTY_FLAG))
|
||||
|
||||
int kqemu_init(CPUState *env);
|
||||
int kqemu_cpu_exec(CPUState *env);
|
||||
void kqemu_flush_page(CPUState *env, target_ulong addr);
|
||||
void kqemu_flush(CPUState *env, int global);
|
||||
void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr);
|
||||
void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr);
|
||||
void kqemu_cpu_interrupt(CPUState *env);
|
||||
void kqemu_record_dump(void);
|
||||
|
||||
static inline int kqemu_is_ok(CPUState *env)
|
||||
{
|
||||
return(env->kqemu_enabled &&
|
||||
(env->cr[0] & CR0_PE_MASK) &&
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK) &&
|
||||
(env->eflags & IF_MASK) &&
|
||||
!(env->eflags & VM_MASK) &&
|
||||
(env->kqemu_enabled == 2 ||
|
||||
((env->hflags & HF_CPL_MASK) == 3 &&
|
||||
(env->eflags & IOPL_MASK) != IOPL_MASK)));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,720 @@
|
||||
|
||||
/*============================================================================
|
||||
|
||||
This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
|
||||
Arithmetic Package, Release 2b.
|
||||
|
||||
Written by John R. Hauser. This work was made possible in part by the
|
||||
International Computer Science Institute, located at Suite 600, 1947 Center
|
||||
Street, Berkeley, California 94704. Funding was partially provided by the
|
||||
National Science Foundation under grant MIP-9311980. The original version
|
||||
of this code was written as part of a project to build a fixed-point vector
|
||||
processor in collaboration with the University of California at Berkeley,
|
||||
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
|
||||
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
|
||||
arithmetic/SoftFloat.html'.
|
||||
|
||||
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
|
||||
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
|
||||
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
|
||||
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
|
||||
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
|
||||
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
|
||||
INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR
|
||||
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
|
||||
|
||||
Derivative works are acceptable, even for commercial purposes, so long as
|
||||
(1) the source code for the derivative work includes prominent notice that
|
||||
the work is derivative, and (2) the source code includes prominent notice with
|
||||
these four paragraphs for those parts of this code that are retained.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts `a' right by the number of bits given in `count'. If any nonzero
|
||||
| bits are shifted off, they are ``jammed'' into the least significant bit of
|
||||
| the result by setting the least significant bit to 1. The value of `count'
|
||||
| can be arbitrarily large; in particular, if `count' is greater than 32, the
|
||||
| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
|
||||
| The result is stored in the location pointed to by `zPtr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
|
||||
{
|
||||
bits32 z;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z = a;
|
||||
}
|
||||
else if ( count < 32 ) {
|
||||
z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
|
||||
}
|
||||
else {
|
||||
z = ( a != 0 );
|
||||
}
|
||||
*zPtr = z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts `a' right by the number of bits given in `count'. If any nonzero
|
||||
| bits are shifted off, they are ``jammed'' into the least significant bit of
|
||||
| the result by setting the least significant bit to 1. The value of `count'
|
||||
| can be arbitrarily large; in particular, if `count' is greater than 64, the
|
||||
| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
|
||||
| The result is stored in the location pointed to by `zPtr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
|
||||
{
|
||||
bits64 z;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z = a;
|
||||
}
|
||||
else if ( count < 64 ) {
|
||||
z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
|
||||
}
|
||||
else {
|
||||
z = ( a != 0 );
|
||||
}
|
||||
*zPtr = z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
|
||||
| _plus_ the number of bits given in `count'. The shifted result is at most
|
||||
| 64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The
|
||||
| bits shifted off form a second 64-bit result as follows: The _last_ bit
|
||||
| shifted off is the most-significant bit of the extra result, and the other
|
||||
| 63 bits of the extra result are all zero if and only if _all_but_the_last_
|
||||
| bits shifted off were all zero. This extra result is stored in the location
|
||||
| pointed to by `z1Ptr'. The value of `count' can be arbitrarily large.
|
||||
| (This routine makes more sense if `a0' and `a1' are considered to form
|
||||
| a fixed-point value with binary point between `a0' and `a1'. This fixed-
|
||||
| point value is shifted right by the number of bits given in `count', and
|
||||
| the integer part of the result is returned at the location pointed to by
|
||||
| `z0Ptr'. The fractional part of the result may be slightly corrupted as
|
||||
| described above, and is returned at the location pointed to by `z1Ptr'.)
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shift64ExtraRightJamming(
|
||||
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits64 z0, z1;
|
||||
int8 negCount = ( - count ) & 63;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z1 = a1;
|
||||
z0 = a0;
|
||||
}
|
||||
else if ( count < 64 ) {
|
||||
z1 = ( a0<<negCount ) | ( a1 != 0 );
|
||||
z0 = a0>>count;
|
||||
}
|
||||
else {
|
||||
if ( count == 64 ) {
|
||||
z1 = a0 | ( a1 != 0 );
|
||||
}
|
||||
else {
|
||||
z1 = ( ( a0 | a1 ) != 0 );
|
||||
}
|
||||
z0 = 0;
|
||||
}
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
|
||||
| number of bits given in `count'. Any bits shifted off are lost. The value
|
||||
| of `count' can be arbitrarily large; in particular, if `count' is greater
|
||||
| than 128, the result will be 0. The result is broken into two 64-bit pieces
|
||||
| which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shift128Right(
|
||||
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits64 z0, z1;
|
||||
int8 negCount = ( - count ) & 63;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z1 = a1;
|
||||
z0 = a0;
|
||||
}
|
||||
else if ( count < 64 ) {
|
||||
z1 = ( a0<<negCount ) | ( a1>>count );
|
||||
z0 = a0>>count;
|
||||
}
|
||||
else {
|
||||
z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
|
||||
z0 = 0;
|
||||
}
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
|
||||
| number of bits given in `count'. If any nonzero bits are shifted off, they
|
||||
| are ``jammed'' into the least significant bit of the result by setting the
|
||||
| least significant bit to 1. The value of `count' can be arbitrarily large;
|
||||
| in particular, if `count' is greater than 128, the result will be either
|
||||
| 0 or 1, depending on whether the concatenation of `a0' and `a1' is zero or
|
||||
| nonzero. The result is broken into two 64-bit pieces which are stored at
|
||||
| the locations pointed to by `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shift128RightJamming(
|
||||
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits64 z0, z1;
|
||||
int8 negCount = ( - count ) & 63;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z1 = a1;
|
||||
z0 = a0;
|
||||
}
|
||||
else if ( count < 64 ) {
|
||||
z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
|
||||
z0 = a0>>count;
|
||||
}
|
||||
else {
|
||||
if ( count == 64 ) {
|
||||
z1 = a0 | ( a1 != 0 );
|
||||
}
|
||||
else if ( count < 128 ) {
|
||||
z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
|
||||
}
|
||||
else {
|
||||
z1 = ( ( a0 | a1 ) != 0 );
|
||||
}
|
||||
z0 = 0;
|
||||
}
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
|
||||
| by 64 _plus_ the number of bits given in `count'. The shifted result is
|
||||
| at most 128 nonzero bits; these are broken into two 64-bit pieces which are
|
||||
| stored at the locations pointed to by `z0Ptr' and `z1Ptr'. The bits shifted
|
||||
| off form a third 64-bit result as follows: The _last_ bit shifted off is
|
||||
| the most-significant bit of the extra result, and the other 63 bits of the
|
||||
| extra result are all zero if and only if _all_but_the_last_ bits shifted off
|
||||
| were all zero. This extra result is stored in the location pointed to by
|
||||
| `z2Ptr'. The value of `count' can be arbitrarily large.
|
||||
| (This routine makes more sense if `a0', `a1', and `a2' are considered
|
||||
| to form a fixed-point value with binary point between `a1' and `a2'. This
|
||||
| fixed-point value is shifted right by the number of bits given in `count',
|
||||
| and the integer part of the result is returned at the locations pointed to
|
||||
| by `z0Ptr' and `z1Ptr'. The fractional part of the result may be slightly
|
||||
| corrupted as described above, and is returned at the location pointed to by
|
||||
| `z2Ptr'.)
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shift128ExtraRightJamming(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 a2,
|
||||
int16 count,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2;
|
||||
int8 negCount = ( - count ) & 63;
|
||||
|
||||
if ( count == 0 ) {
|
||||
z2 = a2;
|
||||
z1 = a1;
|
||||
z0 = a0;
|
||||
}
|
||||
else {
|
||||
if ( count < 64 ) {
|
||||
z2 = a1<<negCount;
|
||||
z1 = ( a0<<negCount ) | ( a1>>count );
|
||||
z0 = a0>>count;
|
||||
}
|
||||
else {
|
||||
if ( count == 64 ) {
|
||||
z2 = a1;
|
||||
z1 = a0;
|
||||
}
|
||||
else {
|
||||
a2 |= a1;
|
||||
if ( count < 128 ) {
|
||||
z2 = a0<<negCount;
|
||||
z1 = a0>>( count & 63 );
|
||||
}
|
||||
else {
|
||||
z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
|
||||
z1 = 0;
|
||||
}
|
||||
}
|
||||
z0 = 0;
|
||||
}
|
||||
z2 |= ( a2 != 0 );
|
||||
}
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
|
||||
| number of bits given in `count'. Any bits shifted off are lost. The value
|
||||
| of `count' must be less than 64. The result is broken into two 64-bit
|
||||
| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shortShift128Left(
|
||||
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
|
||||
*z1Ptr = a1<<count;
|
||||
*z0Ptr =
|
||||
( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
|
||||
| by the number of bits given in `count'. Any bits shifted off are lost.
|
||||
| The value of `count' must be less than 64. The result is broken into three
|
||||
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
|
||||
| `z1Ptr', and `z2Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
shortShift192Left(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 a2,
|
||||
int16 count,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2;
|
||||
int8 negCount;
|
||||
|
||||
z2 = a2<<count;
|
||||
z1 = a1<<count;
|
||||
z0 = a0<<count;
|
||||
if ( 0 < count ) {
|
||||
negCount = ( ( - count ) & 63 );
|
||||
z1 |= a2>>negCount;
|
||||
z0 |= a1>>negCount;
|
||||
}
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
|
||||
| value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so
|
||||
| any carry out is lost. The result is broken into two 64-bit pieces which
|
||||
| are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
add128(
|
||||
bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits64 z1;
|
||||
|
||||
z1 = a1 + b1;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = a0 + b0 + ( z1 < a1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
|
||||
| 192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is
|
||||
| modulo 2^192, so any carry out is lost. The result is broken into three
|
||||
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
|
||||
| `z1Ptr', and `z2Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
add192(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 a2,
|
||||
bits64 b0,
|
||||
bits64 b1,
|
||||
bits64 b2,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2;
|
||||
int8 carry0, carry1;
|
||||
|
||||
z2 = a2 + b2;
|
||||
carry1 = ( z2 < a2 );
|
||||
z1 = a1 + b1;
|
||||
carry0 = ( z1 < a1 );
|
||||
z0 = a0 + b0;
|
||||
z1 += carry1;
|
||||
z0 += ( z1 < carry1 );
|
||||
z0 += carry0;
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
|
||||
| 128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo
|
||||
| 2^128, so any borrow out (carry out) is lost. The result is broken into two
|
||||
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
|
||||
| `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
sub128(
|
||||
bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
|
||||
*z1Ptr = a1 - b1;
|
||||
*z0Ptr = a0 - b0 - ( a1 < b1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
|
||||
| from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
|
||||
| Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The
|
||||
| result is broken into three 64-bit pieces which are stored at the locations
|
||||
| pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
sub192(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 a2,
|
||||
bits64 b0,
|
||||
bits64 b1,
|
||||
bits64 b2,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2;
|
||||
int8 borrow0, borrow1;
|
||||
|
||||
z2 = a2 - b2;
|
||||
borrow1 = ( a2 < b2 );
|
||||
z1 = a1 - b1;
|
||||
borrow0 = ( a1 < b1 );
|
||||
z0 = a0 - b0;
|
||||
z0 -= ( z1 < borrow1 );
|
||||
z1 -= borrow1;
|
||||
z0 -= borrow0;
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Multiplies `a' by `b' to obtain a 128-bit product. The product is broken
|
||||
| into two 64-bit pieces which are stored at the locations pointed to by
|
||||
| `z0Ptr' and `z1Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
|
||||
{
|
||||
bits32 aHigh, aLow, bHigh, bLow;
|
||||
bits64 z0, zMiddleA, zMiddleB, z1;
|
||||
|
||||
aLow = a;
|
||||
aHigh = a>>32;
|
||||
bLow = b;
|
||||
bHigh = b>>32;
|
||||
z1 = ( (bits64) aLow ) * bLow;
|
||||
zMiddleA = ( (bits64) aLow ) * bHigh;
|
||||
zMiddleB = ( (bits64) aHigh ) * bLow;
|
||||
z0 = ( (bits64) aHigh ) * bHigh;
|
||||
zMiddleA += zMiddleB;
|
||||
z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
|
||||
zMiddleA <<= 32;
|
||||
z1 += zMiddleA;
|
||||
z0 += ( z1 < zMiddleA );
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Multiplies the 128-bit value formed by concatenating `a0' and `a1' by
|
||||
| `b' to obtain a 192-bit product. The product is broken into three 64-bit
|
||||
| pieces which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
|
||||
| `z2Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
mul128By64To192(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 b,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2, more1;
|
||||
|
||||
mul64To128( a1, b, &z1, &z2 );
|
||||
mul64To128( a0, b, &z0, &more1 );
|
||||
add128( z0, more1, 0, z1, &z0, &z1 );
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
|
||||
| 128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
|
||||
| product. The product is broken into four 64-bit pieces which are stored at
|
||||
| the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE void
|
||||
mul128To256(
|
||||
bits64 a0,
|
||||
bits64 a1,
|
||||
bits64 b0,
|
||||
bits64 b1,
|
||||
bits64 *z0Ptr,
|
||||
bits64 *z1Ptr,
|
||||
bits64 *z2Ptr,
|
||||
bits64 *z3Ptr
|
||||
)
|
||||
{
|
||||
bits64 z0, z1, z2, z3;
|
||||
bits64 more1, more2;
|
||||
|
||||
mul64To128( a1, b1, &z2, &z3 );
|
||||
mul64To128( a1, b0, &z1, &more2 );
|
||||
add128( z1, more2, 0, z2, &z1, &z2 );
|
||||
mul64To128( a0, b0, &z0, &more1 );
|
||||
add128( z0, more1, 0, z1, &z0, &z1 );
|
||||
mul64To128( a0, b1, &more1, &more2 );
|
||||
add128( more1, more2, 0, z2, &more1, &z2 );
|
||||
add128( z0, z1, 0, more1, &z0, &z1 );
|
||||
*z3Ptr = z3;
|
||||
*z2Ptr = z2;
|
||||
*z1Ptr = z1;
|
||||
*z0Ptr = z0;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns an approximation to the 64-bit integer quotient obtained by dividing
|
||||
| `b' into the 128-bit value formed by concatenating `a0' and `a1'. The
|
||||
| divisor `b' must be at least 2^63. If q is the exact quotient truncated
|
||||
| toward zero, the approximation returned lies between q and q + 2 inclusive.
|
||||
| If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
|
||||
| unsigned integer is returned.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
|
||||
{
|
||||
bits64 b0, b1;
|
||||
bits64 rem0, rem1, term0, term1;
|
||||
bits64 z;
|
||||
|
||||
if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
|
||||
b0 = b>>32;
|
||||
z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
|
||||
mul64To128( b, z, &term0, &term1 );
|
||||
sub128( a0, a1, term0, term1, &rem0, &rem1 );
|
||||
while ( ( (sbits64) rem0 ) < 0 ) {
|
||||
z -= LIT64( 0x100000000 );
|
||||
b1 = b<<32;
|
||||
add128( rem0, rem1, b0, b1, &rem0, &rem1 );
|
||||
}
|
||||
rem0 = ( rem0<<32 ) | ( rem1>>32 );
|
||||
z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns an approximation to the square root of the 32-bit significand given
|
||||
| by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of
|
||||
| `aExp' (the least significant bit) is 1, the integer returned approximates
|
||||
| 2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp'
|
||||
| is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either
|
||||
| case, the approximation returned lies strictly within +/-2 of the exact
|
||||
| value.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static bits32 estimateSqrt32( int16 aExp, bits32 a )
|
||||
{
|
||||
static const bits16 sqrtOddAdjustments[] = {
|
||||
0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
|
||||
0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
|
||||
};
|
||||
static const bits16 sqrtEvenAdjustments[] = {
|
||||
0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
|
||||
0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
|
||||
};
|
||||
int8 index;
|
||||
bits32 z;
|
||||
|
||||
index = ( a>>27 ) & 15;
|
||||
if ( aExp & 1 ) {
|
||||
z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
|
||||
z = ( ( a / z )<<14 ) + ( z<<15 );
|
||||
a >>= 1;
|
||||
}
|
||||
else {
|
||||
z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
|
||||
z = a / z + z;
|
||||
z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
|
||||
if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
|
||||
}
|
||||
return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the number of leading 0 bits before the most-significant 1 bit of
|
||||
| `a'. If `a' is zero, 32 is returned.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static int8 countLeadingZeros32( bits32 a )
|
||||
{
|
||||
static const int8 countLeadingZerosHigh[] = {
|
||||
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
int8 shiftCount;
|
||||
|
||||
shiftCount = 0;
|
||||
if ( a < 0x10000 ) {
|
||||
shiftCount += 16;
|
||||
a <<= 16;
|
||||
}
|
||||
if ( a < 0x1000000 ) {
|
||||
shiftCount += 8;
|
||||
a <<= 8;
|
||||
}
|
||||
shiftCount += countLeadingZerosHigh[ a>>24 ];
|
||||
return shiftCount;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the number of leading 0 bits before the most-significant 1 bit of
|
||||
| `a'. If `a' is zero, 64 is returned.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static int8 countLeadingZeros64( bits64 a )
|
||||
{
|
||||
int8 shiftCount;
|
||||
|
||||
shiftCount = 0;
|
||||
if ( a < ( (bits64) 1 )<<32 ) {
|
||||
shiftCount += 32;
|
||||
}
|
||||
else {
|
||||
a >>= 32;
|
||||
}
|
||||
shiftCount += countLeadingZeros32( a );
|
||||
return shiftCount;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
|
||||
| is equal to the 128-bit value formed by concatenating `b0' and `b1'.
|
||||
| Otherwise, returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
|
||||
{
|
||||
|
||||
return ( a0 == b0 ) && ( a1 == b1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
|
||||
| than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
|
||||
| Otherwise, returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
|
||||
{
|
||||
|
||||
return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
|
||||
| than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise,
|
||||
| returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
|
||||
{
|
||||
|
||||
return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
|
||||
| not equal to the 128-bit value formed by concatenating `b0' and `b1'.
|
||||
| Otherwise, returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
|
||||
{
|
||||
|
||||
return ( a0 != b0 ) || ( a1 != b1 );
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
/* Native implementation of soft float functions. Only a single status
|
||||
context is supported */
|
||||
#include "softfloat.h"
|
||||
#include <math.h>
|
||||
|
||||
void set_float_rounding_mode(int val STATUS_PARAM)
|
||||
{
|
||||
STATUS(float_rounding_mode) = val;
|
||||
#if defined(_BSD) && !defined(__APPLE__) || (defined(HOST_SOLARIS) && HOST_SOLARIS < 10)
|
||||
fpsetround(val);
|
||||
#elif defined(__arm__)
|
||||
/* nothing to do */
|
||||
#else
|
||||
fesetround(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
void set_floatx80_rounding_precision(int val STATUS_PARAM)
|
||||
{
|
||||
STATUS(floatx80_rounding_precision) = val;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_BSD) || (defined(HOST_SOLARIS) && HOST_SOLARIS < 10)
|
||||
#define lrint(d) ((int32_t)rint(d))
|
||||
#define llrint(d) ((int64_t)rint(d))
|
||||
#define lrintf(f) ((int32_t)rint(f))
|
||||
#define llrintf(f) ((int64_t)rint(f))
|
||||
#define sqrtf(f) ((float)sqrt(f))
|
||||
#define remainderf(fa, fb) ((float)remainder(fa, fb))
|
||||
#define rintf(f) ((float)rint(f))
|
||||
#endif
|
||||
|
||||
#if defined(__powerpc__)
|
||||
|
||||
/* correct (but slow) PowerPC rint() (glibc version is incorrect) */
|
||||
double qemu_rint(double x)
|
||||
{
|
||||
double y = 4503599627370496.0;
|
||||
if (fabs(x) >= y)
|
||||
return x;
|
||||
if (x < 0)
|
||||
y = -y;
|
||||
y = (x + y) - y;
|
||||
if (y == 0.0)
|
||||
y = copysign(y, x);
|
||||
return y;
|
||||
}
|
||||
|
||||
#define rint qemu_rint
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE integer-to-floating-point conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 int32_to_float32(int v STATUS_PARAM)
|
||||
{
|
||||
return (float32)v;
|
||||
}
|
||||
|
||||
float64 int32_to_float64(int v STATUS_PARAM)
|
||||
{
|
||||
return (float64)v;
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
floatx80 int32_to_floatx80(int v STATUS_PARAM)
|
||||
{
|
||||
return (floatx80)v;
|
||||
}
|
||||
#endif
|
||||
float32 int64_to_float32( int64_t v STATUS_PARAM)
|
||||
{
|
||||
return (float32)v;
|
||||
}
|
||||
float64 int64_to_float64( int64_t v STATUS_PARAM)
|
||||
{
|
||||
return (float64)v;
|
||||
}
|
||||
#ifdef FLOATX80
|
||||
floatx80 int64_to_floatx80( int64_t v STATUS_PARAM)
|
||||
{
|
||||
return (floatx80)v;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: this code implements the x86 behaviour, not the IEEE one. */
|
||||
#if HOST_LONG_BITS == 32
|
||||
static inline int long_to_int32(long a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#else
|
||||
static inline int long_to_int32(long a)
|
||||
{
|
||||
if (a != (int32_t)a)
|
||||
a = 0x80000000;
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float32_to_int32( float32 a STATUS_PARAM)
|
||||
{
|
||||
return long_to_int32(lrintf(a));
|
||||
}
|
||||
int float32_to_int32_round_to_zero( float32 a STATUS_PARAM)
|
||||
{
|
||||
return (int)a;
|
||||
}
|
||||
int64_t float32_to_int64( float32 a STATUS_PARAM)
|
||||
{
|
||||
return llrintf(a);
|
||||
}
|
||||
|
||||
int64_t float32_to_int64_round_to_zero( float32 a STATUS_PARAM)
|
||||
{
|
||||
return (int64_t)a;
|
||||
}
|
||||
|
||||
float64 float32_to_float64( float32 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#ifdef FLOATX80
|
||||
floatx80 float32_to_floatx80( float32 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 float32_round_to_int( float32 a STATUS_PARAM)
|
||||
{
|
||||
return rintf(a);
|
||||
}
|
||||
|
||||
float32 float32_rem( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return remainderf(a, b);
|
||||
}
|
||||
|
||||
float32 float32_sqrt( float32 a STATUS_PARAM)
|
||||
{
|
||||
return sqrtf(a);
|
||||
}
|
||||
int float32_compare( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
int float32_compare_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (isgreater(a, b)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
int float32_is_signaling_nan( float32 a1)
|
||||
{
|
||||
float32u u;
|
||||
uint32_t a;
|
||||
u.f = a1;
|
||||
a = u.i;
|
||||
return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float64_to_int32( float64 a STATUS_PARAM)
|
||||
{
|
||||
return long_to_int32(lrint(a));
|
||||
}
|
||||
int float64_to_int32_round_to_zero( float64 a STATUS_PARAM)
|
||||
{
|
||||
return (int)a;
|
||||
}
|
||||
int64_t float64_to_int64( float64 a STATUS_PARAM)
|
||||
{
|
||||
return llrint(a);
|
||||
}
|
||||
int64_t float64_to_int64_round_to_zero( float64 a STATUS_PARAM)
|
||||
{
|
||||
return (int64_t)a;
|
||||
}
|
||||
float32 float64_to_float32( float64 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#ifdef FLOATX80
|
||||
floatx80 float64_to_floatx80( float64 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float64_to_float128( float64 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float64 float64_trunc_to_int( float64 a STATUS_PARAM )
|
||||
{
|
||||
return trunc(a);
|
||||
}
|
||||
|
||||
float64 float64_round_to_int( float64 a STATUS_PARAM )
|
||||
{
|
||||
#if defined(__arm__)
|
||||
switch(STATUS(float_rounding_mode)) {
|
||||
default:
|
||||
case float_round_nearest_even:
|
||||
asm("rndd %0, %1" : "=f" (a) : "f"(a));
|
||||
break;
|
||||
case float_round_down:
|
||||
asm("rnddm %0, %1" : "=f" (a) : "f"(a));
|
||||
break;
|
||||
case float_round_up:
|
||||
asm("rnddp %0, %1" : "=f" (a) : "f"(a));
|
||||
break;
|
||||
case float_round_to_zero:
|
||||
asm("rnddz %0, %1" : "=f" (a) : "f"(a));
|
||||
break;
|
||||
}
|
||||
#else
|
||||
return rint(a);
|
||||
#endif
|
||||
}
|
||||
|
||||
float64 float64_rem( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return remainder(a, b);
|
||||
}
|
||||
|
||||
float64 float64_sqrt( float64 a STATUS_PARAM)
|
||||
{
|
||||
return sqrt(a);
|
||||
}
|
||||
int float64_compare( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
int float64_compare_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (isgreater(a, b)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
int float64_is_signaling_nan( float64 a1)
|
||||
{
|
||||
float64u u;
|
||||
uint64_t a;
|
||||
u.f = a1;
|
||||
a = u.i;
|
||||
return
|
||||
( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
|
||||
&& ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
|
||||
|
||||
}
|
||||
|
||||
int float64_is_nan( float64 a1 )
|
||||
{
|
||||
float64u u;
|
||||
uint64_t a;
|
||||
u.f = a1;
|
||||
a = u.i;
|
||||
|
||||
return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
|
||||
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int floatx80_to_int32( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return long_to_int32(lrintl(a));
|
||||
}
|
||||
int floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return (int)a;
|
||||
}
|
||||
int64_t floatx80_to_int64( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return llrintl(a);
|
||||
}
|
||||
int64_t floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return (int64_t)a;
|
||||
}
|
||||
float32 floatx80_to_float32( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
float64 floatx80_to_float64( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return rintl(a);
|
||||
}
|
||||
floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return remainderl(a, b);
|
||||
}
|
||||
floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return sqrtl(a);
|
||||
}
|
||||
int floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
} else if (a == b) {
|
||||
return 0;
|
||||
} else if (isgreater(a, b)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
int floatx80_is_signaling_nan( floatx80 a1)
|
||||
{
|
||||
floatx80u u;
|
||||
u.f = a1;
|
||||
return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( u.i.low<<1 );
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,361 @@
|
||||
/* Native implementation of soft float functions */
|
||||
#include <math.h>
|
||||
|
||||
#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS)
|
||||
#include <ieeefp.h>
|
||||
#define fabsf(f) ((float)fabs(f))
|
||||
#else
|
||||
#include <fenv.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define some C99-7.12.3 classification macros and
|
||||
* some C99-.12.4 for Solaris systems OS less than 10,
|
||||
* or Solaris 10 systems running GCC 3.x or less.
|
||||
* Solaris 10 with GCC4 does not need these macros as they
|
||||
* are defined in <iso/math_c99.h> with a compiler directive
|
||||
*/
|
||||
#if defined(HOST_SOLARIS) && (( HOST_SOLARIS <= 9 ) || ( ( HOST_SOLARIS >= 10 ) && ( __GNUC__ <= 4) ))
|
||||
/*
|
||||
* C99 7.12.3 classification macros
|
||||
* and
|
||||
* C99 7.12.14 comparison macros
|
||||
*
|
||||
* ... do not work on Solaris 10 using GNU CC 3.4.x.
|
||||
* Try to workaround the missing / broken C99 math macros.
|
||||
*/
|
||||
|
||||
#define isnormal(x) (fpclass(x) >= FP_NZERO)
|
||||
#define isgreater(x, y) ((!unordered(x, y)) && ((x) > (y)))
|
||||
#define isgreaterequal(x, y) ((!unordered(x, y)) && ((x) >= (y)))
|
||||
#define isless(x, y) ((!unordered(x, y)) && ((x) < (y)))
|
||||
#define islessequal(x, y) ((!unordered(x, y)) && ((x) <= (y)))
|
||||
#define isunordered(x,y) unordered(x, y)
|
||||
#endif
|
||||
|
||||
typedef float float32;
|
||||
typedef double float64;
|
||||
#ifdef FLOATX80
|
||||
typedef long double floatx80;
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} float32u;
|
||||
typedef union {
|
||||
float64 f;
|
||||
uint64_t i;
|
||||
} float64u;
|
||||
#ifdef FLOATX80
|
||||
typedef union {
|
||||
floatx80 f;
|
||||
struct {
|
||||
uint64_t low;
|
||||
uint16_t high;
|
||||
} i;
|
||||
} floatx80u;
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point rounding mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS)
|
||||
enum {
|
||||
float_round_nearest_even = FP_RN,
|
||||
float_round_down = FP_RM,
|
||||
float_round_up = FP_RP,
|
||||
float_round_to_zero = FP_RZ
|
||||
};
|
||||
#elif defined(__arm__)
|
||||
enum {
|
||||
float_round_nearest_even = 0,
|
||||
float_round_down = 1,
|
||||
float_round_up = 2,
|
||||
float_round_to_zero = 3
|
||||
};
|
||||
#else
|
||||
enum {
|
||||
float_round_nearest_even = FE_TONEAREST,
|
||||
float_round_down = FE_DOWNWARD,
|
||||
float_round_up = FE_UPWARD,
|
||||
float_round_to_zero = FE_TOWARDZERO
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct float_status {
|
||||
signed char float_rounding_mode;
|
||||
#ifdef FLOATX80
|
||||
signed char floatx80_rounding_precision;
|
||||
#endif
|
||||
} float_status;
|
||||
|
||||
void set_float_rounding_mode(int val STATUS_PARAM);
|
||||
#ifdef FLOATX80
|
||||
void set_floatx80_rounding_precision(int val STATUS_PARAM);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE integer-to-floating-point conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 int32_to_float32( int STATUS_PARAM);
|
||||
float64 int32_to_float64( int STATUS_PARAM);
|
||||
#ifdef FLOATX80
|
||||
floatx80 int32_to_floatx80( int STATUS_PARAM);
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 int32_to_float128( int STATUS_PARAM);
|
||||
#endif
|
||||
float32 int64_to_float32( int64_t STATUS_PARAM);
|
||||
float64 int64_to_float64( int64_t STATUS_PARAM);
|
||||
#ifdef FLOATX80
|
||||
floatx80 int64_to_floatx80( int64_t STATUS_PARAM);
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 int64_to_float128( int64_t STATUS_PARAM);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float32_to_int32( float32 STATUS_PARAM);
|
||||
int float32_to_int32_round_to_zero( float32 STATUS_PARAM);
|
||||
int64_t float32_to_int64( float32 STATUS_PARAM);
|
||||
int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM);
|
||||
float64 float32_to_float64( float32 STATUS_PARAM);
|
||||
#ifdef FLOATX80
|
||||
floatx80 float32_to_floatx80( float32 STATUS_PARAM);
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float32_to_float128( float32 STATUS_PARAM);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 float32_round_to_int( float32 STATUS_PARAM);
|
||||
INLINE float32 float32_add( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
INLINE float32 float32_sub( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
INLINE float32 float32_mul( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
float32 float32_rem( float32, float32 STATUS_PARAM);
|
||||
float32 float32_sqrt( float32 STATUS_PARAM);
|
||||
INLINE int float32_eq( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE int float32_le( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE int float32_lt( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE int float32_eq_signaling( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE int float32_le_quiet( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE int float32_lt_quiet( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
}
|
||||
INLINE int float32_unordered( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
int float32_compare( float32, float32 STATUS_PARAM );
|
||||
int float32_compare_quiet( float32, float32 STATUS_PARAM );
|
||||
int float32_is_signaling_nan( float32 );
|
||||
|
||||
INLINE float32 float32_abs(float32 a)
|
||||
{
|
||||
return fabsf(a);
|
||||
}
|
||||
|
||||
INLINE float32 float32_chs(float32 a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float64_to_int32( float64 STATUS_PARAM );
|
||||
int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
|
||||
int64_t float64_to_int64( float64 STATUS_PARAM );
|
||||
int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
|
||||
float32 float64_to_float32( float64 STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 float64_to_floatx80( float64 STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float64_to_float128( float64 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float64 float64_round_to_int( float64 STATUS_PARAM );
|
||||
float64 float64_trunc_to_int( float64 STATUS_PARAM );
|
||||
INLINE float64 float64_add( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
INLINE float64 float64_sub( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
INLINE float64 float64_mul( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
float64 float64_rem( float64, float64 STATUS_PARAM );
|
||||
float64 float64_sqrt( float64 STATUS_PARAM );
|
||||
INLINE int float64_eq( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE int float64_le( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE int float64_lt( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE int float64_eq_signaling( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE int float64_le_quiet( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE int float64_lt_quiet( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
|
||||
}
|
||||
INLINE int float64_unordered( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
int float64_compare( float64, float64 STATUS_PARAM );
|
||||
int float64_compare_quiet( float64, float64 STATUS_PARAM );
|
||||
int float64_is_signaling_nan( float64 );
|
||||
int float64_is_nan( float64 );
|
||||
|
||||
INLINE float64 float64_abs(float64 a)
|
||||
{
|
||||
return fabs(a);
|
||||
}
|
||||
|
||||
INLINE float64 float64_chs(float64 a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int floatx80_to_int32( floatx80 STATUS_PARAM );
|
||||
int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
|
||||
int64_t floatx80_to_int64( floatx80 STATUS_PARAM);
|
||||
int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM);
|
||||
float32 floatx80_to_float32( floatx80 STATUS_PARAM );
|
||||
float64 floatx80_to_float64( floatx80 STATUS_PARAM );
|
||||
#ifdef FLOAT128
|
||||
float128 floatx80_to_float128( floatx80 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
|
||||
INLINE floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
INLINE floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
INLINE floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
|
||||
INLINE int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
|
||||
}
|
||||
INLINE int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
int floatx80_compare( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_is_signaling_nan( floatx80 );
|
||||
|
||||
INLINE floatx80 floatx80_abs(floatx80 a)
|
||||
{
|
||||
return fabsl(a);
|
||||
}
|
||||
|
||||
INLINE floatx80 floatx80_chs(floatx80 a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,464 @@
|
||||
|
||||
/*============================================================================
|
||||
|
||||
This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
|
||||
Arithmetic Package, Release 2b.
|
||||
|
||||
Written by John R. Hauser. This work was made possible in part by the
|
||||
International Computer Science Institute, located at Suite 600, 1947 Center
|
||||
Street, Berkeley, California 94704. Funding was partially provided by the
|
||||
National Science Foundation under grant MIP-9311980. The original version
|
||||
of this code was written as part of a project to build a fixed-point vector
|
||||
processor in collaboration with the University of California at Berkeley,
|
||||
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
|
||||
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
|
||||
arithmetic/SoftFloat.html'.
|
||||
|
||||
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
|
||||
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
|
||||
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
|
||||
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
|
||||
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
|
||||
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
|
||||
INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
|
||||
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
|
||||
|
||||
Derivative works are acceptable, even for commercial purposes, so long as
|
||||
(1) the source code for the derivative work includes prominent notice that
|
||||
the work is derivative, and (2) the source code includes prominent notice with
|
||||
these four paragraphs for those parts of this code that are retained.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Underflow tininess-detection mode, statically initialized to default value.
|
||||
| (The declaration in `softfloat.h' must match the `int8' type here.)
|
||||
*----------------------------------------------------------------------------*/
|
||||
int8 float_detect_tininess = float_tininess_after_rounding;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Raises the exceptions specified by `flags'. Floating-point traps can be
|
||||
| defined here if desired. It is currently not possible for such a trap
|
||||
| to substitute a result value. If traps are not implemented, this routine
|
||||
| should be simply `float_exception_flags |= flags;'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
void float_raise( int8 flags STATUS_PARAM )
|
||||
{
|
||||
|
||||
STATUS(float_exception_flags) |= flags;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Internal canonical NaN format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef struct {
|
||||
flag sign;
|
||||
bits64 high, low;
|
||||
} commonNaNT;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated single-precision NaN.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define float32_default_nan 0xFFC00000
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the single-precision floating-point value `a' is a NaN;
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
int float32_is_nan( float32 a )
|
||||
{
|
||||
|
||||
return ( 0xFF000000 < (bits32) ( a<<1 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the single-precision floating-point value `a' is a signaling
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
int float32_is_signaling_nan( float32 a )
|
||||
{
|
||||
|
||||
return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the single-precision floating-point NaN
|
||||
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM )
|
||||
{
|
||||
commonNaNT z;
|
||||
|
||||
if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR );
|
||||
z.sign = a>>31;
|
||||
z.low = 0;
|
||||
z.high = ( (bits64) a )<<41;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the single-
|
||||
| precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float32 commonNaNToFloat32( commonNaNT a )
|
||||
{
|
||||
|
||||
return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Takes two single-precision floating-point values `a' and `b', one of which
|
||||
| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
|
||||
| signaling NaN, the invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
|
||||
|
||||
aIsNaN = float32_is_nan( a );
|
||||
aIsSignalingNaN = float32_is_signaling_nan( a );
|
||||
bIsNaN = float32_is_nan( b );
|
||||
bIsSignalingNaN = float32_is_signaling_nan( b );
|
||||
a |= 0x00400000;
|
||||
b |= 0x00400000;
|
||||
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
if ( aIsSignalingNaN ) {
|
||||
if ( bIsSignalingNaN ) goto returnLargerSignificand;
|
||||
return bIsNaN ? b : a;
|
||||
}
|
||||
else if ( aIsNaN ) {
|
||||
if ( bIsSignalingNaN | ! bIsNaN ) return a;
|
||||
returnLargerSignificand:
|
||||
if ( (bits32) ( a<<1 ) < (bits32) ( b<<1 ) ) return b;
|
||||
if ( (bits32) ( b<<1 ) < (bits32) ( a<<1 ) ) return a;
|
||||
return ( a < b ) ? a : b;
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated double-precision NaN.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define float64_default_nan LIT64( 0xFFF8000000000000 )
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the double-precision floating-point value `a' is a NaN;
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
int float64_is_nan( float64 a )
|
||||
{
|
||||
|
||||
return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the double-precision floating-point value `a' is a signaling
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
int float64_is_signaling_nan( float64 a )
|
||||
{
|
||||
|
||||
return
|
||||
( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
|
||||
&& ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the double-precision floating-point NaN
|
||||
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM)
|
||||
{
|
||||
commonNaNT z;
|
||||
|
||||
if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
z.sign = a>>63;
|
||||
z.low = 0;
|
||||
z.high = a<<12;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the double-
|
||||
| precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float64 commonNaNToFloat64( commonNaNT a )
|
||||
{
|
||||
|
||||
return
|
||||
( ( (bits64) a.sign )<<63 )
|
||||
| LIT64( 0x7FF8000000000000 )
|
||||
| ( a.high>>12 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Takes two double-precision floating-point values `a' and `b', one of which
|
||||
| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
|
||||
| signaling NaN, the invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
|
||||
|
||||
aIsNaN = float64_is_nan( a );
|
||||
aIsSignalingNaN = float64_is_signaling_nan( a );
|
||||
bIsNaN = float64_is_nan( b );
|
||||
bIsSignalingNaN = float64_is_signaling_nan( b );
|
||||
a |= LIT64( 0x0008000000000000 );
|
||||
b |= LIT64( 0x0008000000000000 );
|
||||
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
if ( aIsSignalingNaN ) {
|
||||
if ( bIsSignalingNaN ) goto returnLargerSignificand;
|
||||
return bIsNaN ? b : a;
|
||||
}
|
||||
else if ( aIsNaN ) {
|
||||
if ( bIsSignalingNaN | ! bIsNaN ) return a;
|
||||
returnLargerSignificand:
|
||||
if ( (bits64) ( a<<1 ) < (bits64) ( b<<1 ) ) return b;
|
||||
if ( (bits64) ( b<<1 ) < (bits64) ( a<<1 ) ) return a;
|
||||
return ( a < b ) ? a : b;
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated extended double-precision NaN. The
|
||||
| `high' and `low' values hold the most- and least-significant bits,
|
||||
| respectively.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define floatx80_default_nan_high 0xFFFF
|
||||
#define floatx80_default_nan_low LIT64( 0xC000000000000000 )
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the extended double-precision floating-point value `a' is a
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
int floatx80_is_nan( floatx80 a )
|
||||
{
|
||||
|
||||
return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the extended double-precision floating-point value `a' is a
|
||||
| signaling NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
int floatx80_is_signaling_nan( floatx80 a )
|
||||
{
|
||||
bits64 aLow;
|
||||
|
||||
aLow = a.low & ~ LIT64( 0x4000000000000000 );
|
||||
return
|
||||
( ( a.high & 0x7FFF ) == 0x7FFF )
|
||||
&& (bits64) ( aLow<<1 )
|
||||
&& ( a.low == aLow );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the extended double-precision floating-
|
||||
| point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the
|
||||
| invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
commonNaNT z;
|
||||
|
||||
if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
z.sign = a.high>>15;
|
||||
z.low = 0;
|
||||
z.high = a.low<<1;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the extended
|
||||
| double-precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static floatx80 commonNaNToFloatx80( commonNaNT a )
|
||||
{
|
||||
floatx80 z;
|
||||
|
||||
z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 );
|
||||
z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Takes two extended double-precision floating-point values `a' and `b', one
|
||||
| of which is a NaN, and returns the appropriate NaN result. If either `a' or
|
||||
| `b' is a signaling NaN, the invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
|
||||
|
||||
aIsNaN = floatx80_is_nan( a );
|
||||
aIsSignalingNaN = floatx80_is_signaling_nan( a );
|
||||
bIsNaN = floatx80_is_nan( b );
|
||||
bIsSignalingNaN = floatx80_is_signaling_nan( b );
|
||||
a.low |= LIT64( 0xC000000000000000 );
|
||||
b.low |= LIT64( 0xC000000000000000 );
|
||||
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
if ( aIsSignalingNaN ) {
|
||||
if ( bIsSignalingNaN ) goto returnLargerSignificand;
|
||||
return bIsNaN ? b : a;
|
||||
}
|
||||
else if ( aIsNaN ) {
|
||||
if ( bIsSignalingNaN | ! bIsNaN ) return a;
|
||||
returnLargerSignificand:
|
||||
if ( a.low < b.low ) return b;
|
||||
if ( b.low < a.low ) return a;
|
||||
return ( a.high < b.high ) ? a : b;
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FLOAT128
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated quadruple-precision NaN. The `high' and
|
||||
| `low' values hold the most- and least-significant bits, respectively.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
|
||||
#define float128_default_nan_low LIT64( 0x0000000000000000 )
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the quadruple-precision floating-point value `a' is a NaN;
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
int float128_is_nan( float128 a )
|
||||
{
|
||||
|
||||
return
|
||||
( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
|
||||
&& ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns 1 if the quadruple-precision floating-point value `a' is a
|
||||
| signaling NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
int float128_is_signaling_nan( float128 a )
|
||||
{
|
||||
|
||||
return
|
||||
( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
|
||||
&& ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the quadruple-precision floating-point NaN
|
||||
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
|
||||
| exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM)
|
||||
{
|
||||
commonNaNT z;
|
||||
|
||||
if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
z.sign = a.high>>63;
|
||||
shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the quadruple-
|
||||
| precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float128 commonNaNToFloat128( commonNaNT a )
|
||||
{
|
||||
float128 z;
|
||||
|
||||
shift128Right( a.high, a.low, 16, &z.high, &z.low );
|
||||
z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 );
|
||||
return z;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Takes two quadruple-precision floating-point values `a' and `b', one of
|
||||
| which is a NaN, and returns the appropriate NaN result. If either `a' or
|
||||
| `b' is a signaling NaN, the invalid exception is raised.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM)
|
||||
{
|
||||
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
|
||||
|
||||
aIsNaN = float128_is_nan( a );
|
||||
aIsSignalingNaN = float128_is_signaling_nan( a );
|
||||
bIsNaN = float128_is_nan( b );
|
||||
bIsSignalingNaN = float128_is_signaling_nan( b );
|
||||
a.high |= LIT64( 0x0000800000000000 );
|
||||
b.high |= LIT64( 0x0000800000000000 );
|
||||
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
|
||||
if ( aIsSignalingNaN ) {
|
||||
if ( bIsSignalingNaN ) goto returnLargerSignificand;
|
||||
return bIsNaN ? b : a;
|
||||
}
|
||||
else if ( aIsNaN ) {
|
||||
if ( bIsSignalingNaN | ! bIsNaN ) return a;
|
||||
returnLargerSignificand:
|
||||
if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b;
|
||||
if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a;
|
||||
return ( a.high < b.high ) ? a : b;
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+5331
File diff suppressed because it is too large
Load Diff
+400
@@ -0,0 +1,400 @@
|
||||
/*============================================================================
|
||||
|
||||
This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
|
||||
Package, Release 2b.
|
||||
|
||||
Written by John R. Hauser. This work was made possible in part by the
|
||||
International Computer Science Institute, located at Suite 600, 1947 Center
|
||||
Street, Berkeley, California 94704. Funding was partially provided by the
|
||||
National Science Foundation under grant MIP-9311980. The original version
|
||||
of this code was written as part of a project to build a fixed-point vector
|
||||
processor in collaboration with the University of California at Berkeley,
|
||||
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
|
||||
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
|
||||
arithmetic/SoftFloat.html'.
|
||||
|
||||
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
|
||||
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
|
||||
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
|
||||
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
|
||||
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
|
||||
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
|
||||
INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
|
||||
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
|
||||
|
||||
Derivative works are acceptable, even for commercial purposes, so long as
|
||||
(1) the source code for the derivative work includes prominent notice that
|
||||
the work is derivative, and (2) the source code includes prominent notice with
|
||||
these four paragraphs for those parts of this code that are retained.
|
||||
|
||||
=============================================================================*/
|
||||
|
||||
#ifndef SOFTFLOAT_H
|
||||
#define SOFTFLOAT_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "config.h"
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Each of the following `typedef's defines the most convenient type that holds
|
||||
| integers of at least as many bits as specified. For example, `uint8' should
|
||||
| be the most convenient type that can hold unsigned integers of as many as
|
||||
| 8 bits. The `flag' type must be able to hold either a 0 or 1. For most
|
||||
| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
|
||||
| to the same as `int'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef uint8_t flag;
|
||||
typedef uint8_t uint8;
|
||||
typedef int8_t int8;
|
||||
typedef int uint16;
|
||||
typedef int int16;
|
||||
typedef unsigned int uint32;
|
||||
typedef signed int int32;
|
||||
typedef uint64_t uint64;
|
||||
typedef int64_t int64;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Each of the following `typedef's defines a type that holds integers
|
||||
| of _exactly_ the number of bits specified. For instance, for most
|
||||
| implementation of C, `bits16' and `sbits16' should be `typedef'ed to
|
||||
| `unsigned short int' and `signed short int' (or `short int'), respectively.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef uint8_t bits8;
|
||||
typedef int8_t sbits8;
|
||||
typedef uint16_t bits16;
|
||||
typedef int16_t sbits16;
|
||||
typedef uint32_t bits32;
|
||||
typedef int32_t sbits32;
|
||||
typedef uint64_t bits64;
|
||||
typedef int64_t sbits64;
|
||||
|
||||
#define LIT64( a ) a##LL
|
||||
#define INLINE static inline
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The macro `FLOATX80' must be defined to enable the extended double-precision
|
||||
| floating-point format `floatx80'. If this macro is not defined, the
|
||||
| `floatx80' type will not be defined, and none of the functions that either
|
||||
| input or output the `floatx80' type will be defined. The same applies to
|
||||
| the `FLOAT128' macro and the quadruple-precision format `float128'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#ifdef CONFIG_SOFTFLOAT
|
||||
/* bit exact soft float support */
|
||||
#define FLOATX80
|
||||
#define FLOAT128
|
||||
#else
|
||||
/* native float support */
|
||||
#if (defined(__i386__) || defined(__x86_64__)) && !defined(_BSD)
|
||||
#define FLOATX80
|
||||
#endif
|
||||
#endif /* !CONFIG_SOFTFLOAT */
|
||||
|
||||
#define STATUS_PARAM , float_status *status
|
||||
#define STATUS(field) status->field
|
||||
#define STATUS_VAR , status
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point ordering relations
|
||||
*----------------------------------------------------------------------------*/
|
||||
enum {
|
||||
float_relation_less = -1,
|
||||
float_relation_equal = 0,
|
||||
float_relation_greater = 1,
|
||||
float_relation_unordered = 2
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SOFTFLOAT
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point types.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef uint32_t float32;
|
||||
typedef uint64_t float64;
|
||||
#ifdef FLOATX80
|
||||
typedef struct {
|
||||
uint64_t low;
|
||||
uint16_t high;
|
||||
} floatx80;
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
typedef struct {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
uint64_t high, low;
|
||||
#else
|
||||
uint64_t low, high;
|
||||
#endif
|
||||
} float128;
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point underflow tininess-detection mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
enum {
|
||||
float_tininess_after_rounding = 0,
|
||||
float_tininess_before_rounding = 1
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point rounding mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
enum {
|
||||
float_round_nearest_even = 0,
|
||||
float_round_down = 1,
|
||||
float_round_up = 2,
|
||||
float_round_to_zero = 3
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
enum {
|
||||
float_flag_invalid = 1,
|
||||
float_flag_divbyzero = 4,
|
||||
float_flag_overflow = 8,
|
||||
float_flag_underflow = 16,
|
||||
float_flag_inexact = 32
|
||||
};
|
||||
|
||||
typedef struct float_status {
|
||||
signed char float_detect_tininess;
|
||||
signed char float_rounding_mode;
|
||||
signed char float_exception_flags;
|
||||
#ifdef FLOATX80
|
||||
signed char floatx80_rounding_precision;
|
||||
#endif
|
||||
} float_status;
|
||||
|
||||
void set_float_rounding_mode(int val STATUS_PARAM);
|
||||
void set_float_exception_flags(int val STATUS_PARAM);
|
||||
INLINE int get_float_exception_flags(float_status *status)
|
||||
{
|
||||
return STATUS(float_exception_flags);
|
||||
}
|
||||
#ifdef FLOATX80
|
||||
void set_floatx80_rounding_precision(int val STATUS_PARAM);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Routine to raise any or all of the software IEC/IEEE floating-point
|
||||
| exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
void float_raise( int8 flags STATUS_PARAM);
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE integer-to-floating-point conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 int32_to_float32( int STATUS_PARAM );
|
||||
float64 int32_to_float64( int STATUS_PARAM );
|
||||
float32 uint32_to_float32( unsigned int STATUS_PARAM );
|
||||
float64 uint32_to_float64( unsigned int STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 int32_to_floatx80( int STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 int32_to_float128( int STATUS_PARAM );
|
||||
#endif
|
||||
float32 int64_to_float32( int64_t STATUS_PARAM );
|
||||
float64 int64_to_float64( int64_t STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 int64_to_floatx80( int64_t STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 int64_to_float128( int64_t STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float32_to_int32( float32 STATUS_PARAM );
|
||||
int float32_to_int32_round_to_zero( float32 STATUS_PARAM );
|
||||
unsigned int float32_to_uint32( float32 STATUS_PARAM );
|
||||
unsigned int float32_to_uint32_round_to_zero( float32 STATUS_PARAM );
|
||||
int64_t float32_to_int64( float32 STATUS_PARAM );
|
||||
int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM );
|
||||
float64 float32_to_float64( float32 STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 float32_to_floatx80( float32 STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float32_to_float128( float32 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE single-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float32 float32_round_to_int( float32 STATUS_PARAM );
|
||||
float32 float32_add( float32, float32 STATUS_PARAM );
|
||||
float32 float32_sub( float32, float32 STATUS_PARAM );
|
||||
float32 float32_mul( float32, float32 STATUS_PARAM );
|
||||
float32 float32_div( float32, float32 STATUS_PARAM );
|
||||
float32 float32_rem( float32, float32 STATUS_PARAM );
|
||||
float32 float32_sqrt( float32 STATUS_PARAM );
|
||||
int float32_eq( float32, float32 STATUS_PARAM );
|
||||
int float32_le( float32, float32 STATUS_PARAM );
|
||||
int float32_lt( float32, float32 STATUS_PARAM );
|
||||
int float32_eq_signaling( float32, float32 STATUS_PARAM );
|
||||
int float32_le_quiet( float32, float32 STATUS_PARAM );
|
||||
int float32_lt_quiet( float32, float32 STATUS_PARAM );
|
||||
int float32_compare( float32, float32 STATUS_PARAM );
|
||||
int float32_compare_quiet( float32, float32 STATUS_PARAM );
|
||||
int float32_is_signaling_nan( float32 );
|
||||
int float64_is_nan( float64 a );
|
||||
|
||||
INLINE float32 float32_abs(float32 a)
|
||||
{
|
||||
return a & 0x7fffffff;
|
||||
}
|
||||
|
||||
INLINE float32 float32_chs(float32 a)
|
||||
{
|
||||
return a ^ 0x80000000;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float64_to_int32( float64 STATUS_PARAM );
|
||||
int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
|
||||
unsigned int float64_to_uint32( float64 STATUS_PARAM );
|
||||
unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM );
|
||||
int64_t float64_to_int64( float64 STATUS_PARAM );
|
||||
int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
|
||||
float32 float64_to_float32( float64 STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 float64_to_floatx80( float64 STATUS_PARAM );
|
||||
#endif
|
||||
#ifdef FLOAT128
|
||||
float128 float64_to_float128( float64 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float64 float64_round_to_int( float64 STATUS_PARAM );
|
||||
float64 float64_trunc_to_int( float64 STATUS_PARAM );
|
||||
float64 float64_add( float64, float64 STATUS_PARAM );
|
||||
float64 float64_sub( float64, float64 STATUS_PARAM );
|
||||
float64 float64_mul( float64, float64 STATUS_PARAM );
|
||||
float64 float64_div( float64, float64 STATUS_PARAM );
|
||||
float64 float64_rem( float64, float64 STATUS_PARAM );
|
||||
float64 float64_sqrt( float64 STATUS_PARAM );
|
||||
int float64_eq( float64, float64 STATUS_PARAM );
|
||||
int float64_le( float64, float64 STATUS_PARAM );
|
||||
int float64_lt( float64, float64 STATUS_PARAM );
|
||||
int float64_eq_signaling( float64, float64 STATUS_PARAM );
|
||||
int float64_le_quiet( float64, float64 STATUS_PARAM );
|
||||
int float64_lt_quiet( float64, float64 STATUS_PARAM );
|
||||
int float64_compare( float64, float64 STATUS_PARAM );
|
||||
int float64_compare_quiet( float64, float64 STATUS_PARAM );
|
||||
int float64_is_signaling_nan( float64 );
|
||||
|
||||
INLINE float64 float64_abs(float64 a)
|
||||
{
|
||||
return a & 0x7fffffffffffffffLL;
|
||||
}
|
||||
|
||||
INLINE float64 float64_chs(float64 a)
|
||||
{
|
||||
return a ^ 0x8000000000000000LL;
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int floatx80_to_int32( floatx80 STATUS_PARAM );
|
||||
int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
|
||||
int64_t floatx80_to_int64( floatx80 STATUS_PARAM );
|
||||
int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM );
|
||||
float32 floatx80_to_float32( floatx80 STATUS_PARAM );
|
||||
float64 floatx80_to_float64( floatx80 STATUS_PARAM );
|
||||
#ifdef FLOAT128
|
||||
float128 floatx80_to_float128( floatx80 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE extended double-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_add( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_sub( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_mul( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_div( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
|
||||
int floatx80_eq( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_le( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_lt( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_is_signaling_nan( floatx80 );
|
||||
|
||||
INLINE floatx80 floatx80_abs(floatx80 a)
|
||||
{
|
||||
a.high &= 0x7fff;
|
||||
return a;
|
||||
}
|
||||
|
||||
INLINE floatx80 floatx80_chs(floatx80 a)
|
||||
{
|
||||
a.high ^= 0x8000;
|
||||
return a;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FLOAT128
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE quadruple-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float128_to_int32( float128 STATUS_PARAM );
|
||||
int float128_to_int32_round_to_zero( float128 STATUS_PARAM );
|
||||
int64_t float128_to_int64( float128 STATUS_PARAM );
|
||||
int64_t float128_to_int64_round_to_zero( float128 STATUS_PARAM );
|
||||
float32 float128_to_float32( float128 STATUS_PARAM );
|
||||
float64 float128_to_float64( float128 STATUS_PARAM );
|
||||
#ifdef FLOATX80
|
||||
floatx80 float128_to_floatx80( float128 STATUS_PARAM );
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE quadruple-precision operations.
|
||||
*----------------------------------------------------------------------------*/
|
||||
float128 float128_round_to_int( float128 STATUS_PARAM );
|
||||
float128 float128_add( float128, float128 STATUS_PARAM );
|
||||
float128 float128_sub( float128, float128 STATUS_PARAM );
|
||||
float128 float128_mul( float128, float128 STATUS_PARAM );
|
||||
float128 float128_div( float128, float128 STATUS_PARAM );
|
||||
float128 float128_rem( float128, float128 STATUS_PARAM );
|
||||
float128 float128_sqrt( float128 STATUS_PARAM );
|
||||
int float128_eq( float128, float128 STATUS_PARAM );
|
||||
int float128_le( float128, float128 STATUS_PARAM );
|
||||
int float128_lt( float128, float128 STATUS_PARAM );
|
||||
int float128_eq_signaling( float128, float128 STATUS_PARAM );
|
||||
int float128_le_quiet( float128, float128 STATUS_PARAM );
|
||||
int float128_lt_quiet( float128, float128 STATUS_PARAM );
|
||||
int float128_is_signaling_nan( float128 );
|
||||
|
||||
INLINE float128 float128_abs(float128 a)
|
||||
{
|
||||
a.high &= 0x7fffffffffffffffLL;
|
||||
return a;
|
||||
}
|
||||
|
||||
INLINE float128 float128_chs(float128 a)
|
||||
{
|
||||
a.high ^= 0x8000000000000000LL;
|
||||
return a;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else /* CONFIG_SOFTFLOAT */
|
||||
|
||||
#include "softfloat-native.h"
|
||||
|
||||
#endif /* !CONFIG_SOFTFLOAT */
|
||||
|
||||
#endif /* !SOFTFLOAT_H */
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef GDBSTUB_H
|
||||
#define GDBSTUB_H
|
||||
|
||||
#define DEFAULT_GDBSTUB_PORT 1234
|
||||
|
||||
typedef void (*gdb_syscall_complete_cb)(CPUState *env,
|
||||
target_ulong ret, target_ulong err);
|
||||
|
||||
void gdb_do_syscall(gdb_syscall_complete_cb cb, char *fmt, ...);
|
||||
int use_gdb_syscalls(void);
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int gdb_handlesig (CPUState *, int);
|
||||
void gdb_exit(CPUState *, int);
|
||||
int gdbserver_start(int);
|
||||
#else
|
||||
int gdbserver_start(CharDriverState *chr);
|
||||
int gdbserver_start_port(int port);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Save/restore host registrs.
|
||||
*
|
||||
* Copyright (c) 2007 CodeSourcery
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* The GCC global register vairable extension is used to reserve some
|
||||
host registers for use by dyngen. However only the core parts of the
|
||||
translation engine are compiled with these settings. We must manually
|
||||
save/restore these registers when called from regular code.
|
||||
It is not sufficient to save/restore T0 et. al. as these may be declared
|
||||
with a datatype smaller than the actual register. */
|
||||
|
||||
#if defined(DECLARE_HOST_REGS)
|
||||
|
||||
#define DO_REG(REG) \
|
||||
register host_reg_t reg_AREG##REG asm(AREG##REG); \
|
||||
volatile host_reg_t saved_AREG##REG;
|
||||
|
||||
#elif defined(SAVE_HOST_REGS)
|
||||
|
||||
#define DO_REG(REG) \
|
||||
__asm__ __volatile__ ("" : "=r" (reg_AREG##REG)); \
|
||||
saved_AREG##REG = reg_AREG##REG;
|
||||
|
||||
#else
|
||||
|
||||
#define DO_REG(REG) \
|
||||
reg_AREG##REG = saved_AREG##REG; \
|
||||
__asm__ __volatile__ ("" : : "r" (reg_AREG##REG));
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef AREG0
|
||||
DO_REG(0)
|
||||
#endif
|
||||
|
||||
#ifdef AREG1
|
||||
DO_REG(1)
|
||||
#endif
|
||||
|
||||
#ifdef AREG2
|
||||
DO_REG(2)
|
||||
#endif
|
||||
|
||||
#ifdef AREG3
|
||||
DO_REG(3)
|
||||
#endif
|
||||
|
||||
#ifdef AREG4
|
||||
DO_REG(4)
|
||||
#endif
|
||||
|
||||
#ifdef AREG5
|
||||
DO_REG(5)
|
||||
#endif
|
||||
|
||||
#ifdef AREG6
|
||||
DO_REG(6)
|
||||
#endif
|
||||
|
||||
#ifdef AREG7
|
||||
DO_REG(7)
|
||||
#endif
|
||||
|
||||
#ifdef AREG8
|
||||
DO_REG(8)
|
||||
#endif
|
||||
|
||||
#ifdef AREG9
|
||||
DO_REG(9)
|
||||
#endif
|
||||
|
||||
#ifdef AREG10
|
||||
DO_REG(10)
|
||||
#endif
|
||||
|
||||
#ifdef AREG11
|
||||
DO_REG(11)
|
||||
#endif
|
||||
|
||||
#undef SAVE_HOST_REGS
|
||||
#undef DECLARE_HOST_REGS
|
||||
#undef DO_REG
|
||||
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* ACPI implementation
|
||||
*
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
/* i82731AB (PIIX4) compatible power management function */
|
||||
#define PM_FREQ 3579545
|
||||
|
||||
#define ACPI_DBG_IO_ADDR 0xb044
|
||||
#define SMB_IO_BASE 0xb100
|
||||
|
||||
typedef struct PIIX4PMState {
|
||||
PCIDevice dev;
|
||||
uint16_t pmsts;
|
||||
uint16_t pmen;
|
||||
uint16_t pmcntrl;
|
||||
uint8_t apmc;
|
||||
uint8_t apms;
|
||||
QEMUTimer *tmr_timer;
|
||||
int64_t tmr_overflow_time;
|
||||
SMBusDevice *smb_dev[128];
|
||||
uint8_t smb_stat;
|
||||
uint8_t smb_ctl;
|
||||
uint8_t smb_cmd;
|
||||
uint8_t smb_addr;
|
||||
uint8_t smb_data0;
|
||||
uint8_t smb_data1;
|
||||
uint8_t smb_data[32];
|
||||
uint8_t smb_index;
|
||||
} PIIX4PMState;
|
||||
|
||||
#define RTC_EN (1 << 10)
|
||||
#define PWRBTN_EN (1 << 8)
|
||||
#define GBL_EN (1 << 5)
|
||||
#define TMROF_EN (1 << 0)
|
||||
|
||||
#define SCI_EN (1 << 0)
|
||||
|
||||
#define SUS_EN (1 << 13)
|
||||
|
||||
#define SMBHSTSTS 0x00
|
||||
#define SMBHSTCNT 0x02
|
||||
#define SMBHSTCMD 0x03
|
||||
#define SMBHSTADD 0x04
|
||||
#define SMBHSTDAT0 0x05
|
||||
#define SMBHSTDAT1 0x06
|
||||
#define SMBBLKDAT 0x07
|
||||
|
||||
/* Note: only used for piix4_smbus_register_device */
|
||||
static PIIX4PMState *piix4_pm_state;
|
||||
|
||||
static uint32_t get_pmtmr(PIIX4PMState *s)
|
||||
{
|
||||
uint32_t d;
|
||||
d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
|
||||
return d & 0xffffff;
|
||||
}
|
||||
|
||||
static int get_pmsts(PIIX4PMState *s)
|
||||
{
|
||||
int64_t d;
|
||||
int pmsts;
|
||||
pmsts = s->pmsts;
|
||||
d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
|
||||
if (d >= s->tmr_overflow_time)
|
||||
s->pmsts |= TMROF_EN;
|
||||
return pmsts;
|
||||
}
|
||||
|
||||
static void pm_update_sci(PIIX4PMState *s)
|
||||
{
|
||||
int sci_level, pmsts;
|
||||
int64_t expire_time;
|
||||
|
||||
pmsts = get_pmsts(s);
|
||||
sci_level = (((pmsts & s->pmen) &
|
||||
(RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0);
|
||||
pci_set_irq(&s->dev, 0, sci_level);
|
||||
/* schedule a timer interruption if needed */
|
||||
if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) {
|
||||
expire_time = muldiv64(s->tmr_overflow_time, ticks_per_sec, PM_FREQ);
|
||||
qemu_mod_timer(s->tmr_timer, expire_time);
|
||||
} else {
|
||||
qemu_del_timer(s->tmr_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void pm_tmr_timer(void *opaque)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
pm_update_sci(s);
|
||||
}
|
||||
|
||||
static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
addr &= 0x3f;
|
||||
switch(addr) {
|
||||
case 0x00:
|
||||
{
|
||||
int64_t d;
|
||||
int pmsts;
|
||||
pmsts = get_pmsts(s);
|
||||
if (pmsts & val & TMROF_EN) {
|
||||
/* if TMRSTS is reset, then compute the new overflow time */
|
||||
d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
|
||||
s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
|
||||
}
|
||||
s->pmsts &= ~val;
|
||||
pm_update_sci(s);
|
||||
}
|
||||
break;
|
||||
case 0x02:
|
||||
s->pmen = val;
|
||||
pm_update_sci(s);
|
||||
break;
|
||||
case 0x04:
|
||||
{
|
||||
int sus_typ;
|
||||
s->pmcntrl = val & ~(SUS_EN);
|
||||
if (val & SUS_EN) {
|
||||
/* change suspend type */
|
||||
sus_typ = (val >> 10) & 3;
|
||||
switch(sus_typ) {
|
||||
case 0: /* soft power off */
|
||||
qemu_system_shutdown_request();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("PM writew port=0x%04x val=0x%04x\n", addr, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t pm_ioport_readw(void *opaque, uint32_t addr)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
addr &= 0x3f;
|
||||
switch(addr) {
|
||||
case 0x00:
|
||||
val = get_pmsts(s);
|
||||
break;
|
||||
case 0x02:
|
||||
val = s->pmen;
|
||||
break;
|
||||
case 0x04:
|
||||
val = s->pmcntrl;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("PM readw port=0x%04x val=0x%04x\n", addr, val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
// PIIX4PMState *s = opaque;
|
||||
addr &= 0x3f;
|
||||
#ifdef DEBUG
|
||||
printf("PM writel port=0x%04x val=0x%08x\n", addr, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t pm_ioport_readl(void *opaque, uint32_t addr)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
addr &= 0x3f;
|
||||
switch(addr) {
|
||||
case 0x08:
|
||||
val = get_pmtmr(s);
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("PM readl port=0x%04x val=0x%08x\n", addr, val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pm_smi_writeb(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
addr &= 1;
|
||||
#ifdef DEBUG
|
||||
printf("pm_smi_writeb addr=0x%x val=0x%02x\n", addr, val);
|
||||
#endif
|
||||
if (addr == 0) {
|
||||
s->apmc = val;
|
||||
if (s->dev.config[0x5b] & (1 << 1)) {
|
||||
cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
|
||||
}
|
||||
} else {
|
||||
s->apms = val;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pm_smi_readb(void *opaque, uint32_t addr)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
addr &= 1;
|
||||
if (addr == 0) {
|
||||
val = s->apmc;
|
||||
} else {
|
||||
val = s->apms;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("pm_smi_readb addr=0x%x val=0x%02x\n", addr, val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
printf("ACPI: DBG: 0x%08x\n", val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void smb_transaction(PIIX4PMState *s)
|
||||
{
|
||||
uint8_t prot = (s->smb_ctl >> 2) & 0x07;
|
||||
uint8_t read = s->smb_addr & 0x01;
|
||||
uint8_t cmd = s->smb_cmd;
|
||||
uint8_t addr = s->smb_addr >> 1;
|
||||
SMBusDevice *dev = s->smb_dev[addr];
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
|
||||
#endif
|
||||
if (!dev) goto error;
|
||||
|
||||
switch(prot) {
|
||||
case 0x0:
|
||||
if (!dev->quick_cmd) goto error;
|
||||
(*dev->quick_cmd)(dev, read);
|
||||
break;
|
||||
case 0x1:
|
||||
if (read) {
|
||||
if (!dev->receive_byte) goto error;
|
||||
s->smb_data0 = (*dev->receive_byte)(dev);
|
||||
}
|
||||
else {
|
||||
if (!dev->send_byte) goto error;
|
||||
(*dev->send_byte)(dev, cmd);
|
||||
}
|
||||
break;
|
||||
case 0x2:
|
||||
if (read) {
|
||||
if (!dev->read_byte) goto error;
|
||||
s->smb_data0 = (*dev->read_byte)(dev, cmd);
|
||||
}
|
||||
else {
|
||||
if (!dev->write_byte) goto error;
|
||||
(*dev->write_byte)(dev, cmd, s->smb_data0);
|
||||
}
|
||||
break;
|
||||
case 0x3:
|
||||
if (read) {
|
||||
uint16_t val;
|
||||
if (!dev->read_word) goto error;
|
||||
val = (*dev->read_word)(dev, cmd);
|
||||
s->smb_data0 = val;
|
||||
s->smb_data1 = val >> 8;
|
||||
}
|
||||
else {
|
||||
if (!dev->write_word) goto error;
|
||||
(*dev->write_word)(dev, cmd, (s->smb_data1 << 8) | s->smb_data0);
|
||||
}
|
||||
break;
|
||||
case 0x5:
|
||||
if (read) {
|
||||
if (!dev->read_block) goto error;
|
||||
s->smb_data0 = (*dev->read_block)(dev, cmd, s->smb_data);
|
||||
}
|
||||
else {
|
||||
if (!dev->write_block) goto error;
|
||||
(*dev->write_block)(dev, cmd, s->smb_data0, s->smb_data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
return;
|
||||
|
||||
error:
|
||||
s->smb_stat |= 0x04;
|
||||
}
|
||||
|
||||
static void smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
addr &= 0x3f;
|
||||
#ifdef DEBUG
|
||||
printf("SMB writeb port=0x%04x val=0x%02x\n", addr, val);
|
||||
#endif
|
||||
switch(addr) {
|
||||
case SMBHSTSTS:
|
||||
s->smb_stat = 0;
|
||||
s->smb_index = 0;
|
||||
break;
|
||||
case SMBHSTCNT:
|
||||
s->smb_ctl = val;
|
||||
if (val & 0x40)
|
||||
smb_transaction(s);
|
||||
break;
|
||||
case SMBHSTCMD:
|
||||
s->smb_cmd = val;
|
||||
break;
|
||||
case SMBHSTADD:
|
||||
s->smb_addr = val;
|
||||
break;
|
||||
case SMBHSTDAT0:
|
||||
s->smb_data0 = val;
|
||||
break;
|
||||
case SMBHSTDAT1:
|
||||
s->smb_data1 = val;
|
||||
break;
|
||||
case SMBBLKDAT:
|
||||
s->smb_data[s->smb_index++] = val;
|
||||
if (s->smb_index > 31)
|
||||
s->smb_index = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t smb_ioport_readb(void *opaque, uint32_t addr)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
addr &= 0x3f;
|
||||
switch(addr) {
|
||||
case SMBHSTSTS:
|
||||
val = s->smb_stat;
|
||||
break;
|
||||
case SMBHSTCNT:
|
||||
s->smb_index = 0;
|
||||
val = s->smb_ctl & 0x1f;
|
||||
break;
|
||||
case SMBHSTCMD:
|
||||
val = s->smb_cmd;
|
||||
break;
|
||||
case SMBHSTADD:
|
||||
val = s->smb_addr;
|
||||
break;
|
||||
case SMBHSTDAT0:
|
||||
val = s->smb_data0;
|
||||
break;
|
||||
case SMBHSTDAT1:
|
||||
val = s->smb_data1;
|
||||
break;
|
||||
case SMBBLKDAT:
|
||||
val = s->smb_data[s->smb_index++];
|
||||
if (s->smb_index > 31)
|
||||
s->smb_index = 0;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("SMB readb port=0x%04x val=0x%02x\n", addr, val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pm_io_space_update(PIIX4PMState *s)
|
||||
{
|
||||
uint32_t pm_io_base;
|
||||
|
||||
if (s->dev.config[0x80] & 1) {
|
||||
pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40));
|
||||
pm_io_base &= 0xfffe;
|
||||
|
||||
/* XXX: need to improve memory and ioport allocation */
|
||||
#if defined(DEBUG)
|
||||
printf("PM: mapping to 0x%x\n", pm_io_base);
|
||||
#endif
|
||||
register_ioport_write(pm_io_base, 64, 2, pm_ioport_writew, s);
|
||||
register_ioport_read(pm_io_base, 64, 2, pm_ioport_readw, s);
|
||||
register_ioport_write(pm_io_base, 64, 4, pm_ioport_writel, s);
|
||||
register_ioport_read(pm_io_base, 64, 4, pm_ioport_readl, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void pm_write_config(PCIDevice *d,
|
||||
uint32_t address, uint32_t val, int len)
|
||||
{
|
||||
pci_default_write_config(d, address, val, len);
|
||||
if (address == 0x80)
|
||||
pm_io_space_update((PIIX4PMState *)d);
|
||||
}
|
||||
|
||||
static void pm_save(QEMUFile* f,void *opaque)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
|
||||
pci_device_save(&s->dev, f);
|
||||
|
||||
qemu_put_be16s(f, &s->pmsts);
|
||||
qemu_put_be16s(f, &s->pmen);
|
||||
qemu_put_be16s(f, &s->pmcntrl);
|
||||
qemu_put_8s(f, &s->apmc);
|
||||
qemu_put_8s(f, &s->apms);
|
||||
qemu_put_timer(f, s->tmr_timer);
|
||||
qemu_put_be64s(f, &s->tmr_overflow_time);
|
||||
}
|
||||
|
||||
static int pm_load(QEMUFile* f,void* opaque,int version_id)
|
||||
{
|
||||
PIIX4PMState *s = opaque;
|
||||
int ret;
|
||||
|
||||
if (version_id > 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = pci_device_load(&s->dev, f);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
qemu_get_be16s(f, &s->pmsts);
|
||||
qemu_get_be16s(f, &s->pmen);
|
||||
qemu_get_be16s(f, &s->pmcntrl);
|
||||
qemu_get_8s(f, &s->apmc);
|
||||
qemu_get_8s(f, &s->apms);
|
||||
qemu_get_timer(f, s->tmr_timer);
|
||||
qemu_get_be64s(f, &s->tmr_overflow_time);
|
||||
|
||||
pm_io_space_update(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void piix4_pm_init(PCIBus *bus, int devfn)
|
||||
{
|
||||
PIIX4PMState *s;
|
||||
uint8_t *pci_conf;
|
||||
uint32_t pm_io_base, smb_io_base;
|
||||
|
||||
s = (PIIX4PMState *)pci_register_device(bus,
|
||||
"PM", sizeof(PIIX4PMState),
|
||||
devfn, NULL, pm_write_config);
|
||||
pci_conf = s->dev.config;
|
||||
pci_conf[0x00] = 0x86;
|
||||
pci_conf[0x01] = 0x80;
|
||||
pci_conf[0x02] = 0x13;
|
||||
pci_conf[0x03] = 0x71;
|
||||
pci_conf[0x08] = 0x00; // revision number
|
||||
pci_conf[0x09] = 0x00;
|
||||
pci_conf[0x0a] = 0x80; // other bridge device
|
||||
pci_conf[0x0b] = 0x06; // bridge device
|
||||
pci_conf[0x0e] = 0x00; // header_type
|
||||
pci_conf[0x3d] = 0x01; // interrupt pin 1
|
||||
|
||||
pci_conf[0x40] = 0x01; /* PM io base read only bit */
|
||||
|
||||
register_ioport_write(0xb2, 2, 1, pm_smi_writeb, s);
|
||||
register_ioport_read(0xb2, 2, 1, pm_smi_readb, s);
|
||||
|
||||
register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s);
|
||||
|
||||
/* XXX: which specification is used ? The i82731AB has different
|
||||
mappings */
|
||||
pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10;
|
||||
pci_conf[0x63] = 0x60;
|
||||
pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) |
|
||||
(serial_hds[1] != NULL ? 0x90 : 0);
|
||||
|
||||
smb_io_base = SMB_IO_BASE;
|
||||
pci_conf[0x90] = smb_io_base | 1;
|
||||
pci_conf[0x91] = smb_io_base >> 8;
|
||||
pci_conf[0xd2] = 0x09;
|
||||
register_ioport_write(smb_io_base, 64, 1, smb_ioport_writeb, s);
|
||||
register_ioport_read(smb_io_base, 64, 1, smb_ioport_readb, s);
|
||||
|
||||
s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s);
|
||||
|
||||
register_savevm("piix4_pm", 0, 1, pm_save, pm_load, s);
|
||||
piix4_pm_state = s;
|
||||
}
|
||||
|
||||
void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr)
|
||||
{
|
||||
piix4_pm_state->smb_dev[addr] = dev;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* QEMU ADB support
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
*
|
||||
* 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 "vl.h"
|
||||
|
||||
/* ADB commands */
|
||||
#define ADB_BUSRESET 0x00
|
||||
#define ADB_FLUSH 0x01
|
||||
#define ADB_WRITEREG 0x08
|
||||
#define ADB_READREG 0x0c
|
||||
|
||||
/* ADB device commands */
|
||||
#define ADB_CMD_SELF_TEST 0xff
|
||||
#define ADB_CMD_CHANGE_ID 0xfe
|
||||
#define ADB_CMD_CHANGE_ID_AND_ACT 0xfd
|
||||
#define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00
|
||||
|
||||
/* ADB default device IDs (upper 4 bits of ADB command byte) */
|
||||
#define ADB_DONGLE 1
|
||||
#define ADB_KEYBOARD 2
|
||||
#define ADB_MOUSE 3
|
||||
#define ADB_TABLET 4
|
||||
#define ADB_MODEM 5
|
||||
#define ADB_MISC 7
|
||||
|
||||
/* error codes */
|
||||
#define ADB_RET_NOTPRESENT (-2)
|
||||
|
||||
int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
|
||||
{
|
||||
ADBDevice *d;
|
||||
int devaddr, cmd, i;
|
||||
|
||||
cmd = buf[0] & 0xf;
|
||||
if (cmd == ADB_BUSRESET) {
|
||||
for(i = 0; i < s->nb_devices; i++) {
|
||||
d = &s->devices[i];
|
||||
if (d->devreset) {
|
||||
d->devreset(d);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
devaddr = buf[0] >> 4;
|
||||
for(i = 0; i < s->nb_devices; i++) {
|
||||
d = &s->devices[i];
|
||||
if (d->devaddr == devaddr) {
|
||||
return d->devreq(d, obuf, buf, len);
|
||||
}
|
||||
}
|
||||
return ADB_RET_NOTPRESENT;
|
||||
}
|
||||
|
||||
/* XXX: move that to cuda ? */
|
||||
int adb_poll(ADBBusState *s, uint8_t *obuf)
|
||||
{
|
||||
ADBDevice *d;
|
||||
int olen, i;
|
||||
uint8_t buf[1];
|
||||
|
||||
olen = 0;
|
||||
for(i = 0; i < s->nb_devices; i++) {
|
||||
if (s->poll_index >= s->nb_devices)
|
||||
s->poll_index = 0;
|
||||
d = &s->devices[s->poll_index];
|
||||
buf[0] = ADB_READREG | (d->devaddr << 4);
|
||||
olen = adb_request(s, obuf + 1, buf, 1);
|
||||
/* if there is data, we poll again the same device */
|
||||
if (olen > 0) {
|
||||
obuf[0] = buf[0];
|
||||
olen++;
|
||||
break;
|
||||
}
|
||||
s->poll_index++;
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
|
||||
ADBDeviceRequest *devreq,
|
||||
ADBDeviceReset *devreset,
|
||||
void *opaque)
|
||||
{
|
||||
ADBDevice *d;
|
||||
if (s->nb_devices >= MAX_ADB_DEVICES)
|
||||
return NULL;
|
||||
d = &s->devices[s->nb_devices++];
|
||||
d->bus = s;
|
||||
d->devaddr = devaddr;
|
||||
d->devreq = devreq;
|
||||
d->devreset = devreset;
|
||||
d->opaque = opaque;
|
||||
return d;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* Keyboard ADB device */
|
||||
|
||||
typedef struct KBDState {
|
||||
uint8_t data[128];
|
||||
int rptr, wptr, count;
|
||||
} KBDState;
|
||||
|
||||
static const uint8_t pc_to_adb_keycode[256] = {
|
||||
0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
|
||||
12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54, 0, 1,
|
||||
2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9,
|
||||
11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
|
||||
97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
|
||||
84, 85, 82, 65, 0, 0, 10,103,111, 0, 0,110, 81, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 94, 0, 93, 0, 0, 0, 0, 0, 0,104,102, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76,125, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 75, 0, 0,124, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0,115, 62,116, 0, 59, 0, 60, 0,119,
|
||||
61,121,114,117, 0, 0, 0, 0, 0, 0, 0, 55,126, 0,127, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static void adb_kbd_put_keycode(void *opaque, int keycode)
|
||||
{
|
||||
ADBDevice *d = opaque;
|
||||
KBDState *s = d->opaque;
|
||||
|
||||
if (s->count < sizeof(s->data)) {
|
||||
s->data[s->wptr] = keycode;
|
||||
if (++s->wptr == sizeof(s->data))
|
||||
s->wptr = 0;
|
||||
s->count++;
|
||||
}
|
||||
}
|
||||
|
||||
static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
|
||||
{
|
||||
static int ext_keycode;
|
||||
KBDState *s = d->opaque;
|
||||
int adb_keycode, keycode;
|
||||
int olen;
|
||||
|
||||
olen = 0;
|
||||
for(;;) {
|
||||
if (s->count == 0)
|
||||
break;
|
||||
keycode = s->data[s->rptr];
|
||||
if (++s->rptr == sizeof(s->data))
|
||||
s->rptr = 0;
|
||||
s->count--;
|
||||
|
||||
if (keycode == 0xe0) {
|
||||
ext_keycode = 1;
|
||||
} else {
|
||||
if (ext_keycode)
|
||||
adb_keycode = pc_to_adb_keycode[keycode | 0x80];
|
||||
else
|
||||
adb_keycode = pc_to_adb_keycode[keycode & 0x7f];
|
||||
obuf[0] = adb_keycode | (keycode & 0x80);
|
||||
/* NOTE: could put a second keycode if needed */
|
||||
obuf[1] = 0xff;
|
||||
olen = 2;
|
||||
ext_keycode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
|
||||
const uint8_t *buf, int len)
|
||||
{
|
||||
KBDState *s = d->opaque;
|
||||
int cmd, reg, olen;
|
||||
|
||||
if ((buf[0] & 0x0f) == ADB_FLUSH) {
|
||||
/* flush keyboard fifo */
|
||||
s->wptr = s->rptr = s->count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd = buf[0] & 0xc;
|
||||
reg = buf[0] & 0x3;
|
||||
olen = 0;
|
||||
switch(cmd) {
|
||||
case ADB_WRITEREG:
|
||||
switch(reg) {
|
||||
case 2:
|
||||
/* LED status */
|
||||
break;
|
||||
case 3:
|
||||
switch(buf[2]) {
|
||||
case ADB_CMD_SELF_TEST:
|
||||
break;
|
||||
case ADB_CMD_CHANGE_ID:
|
||||
case ADB_CMD_CHANGE_ID_AND_ACT:
|
||||
case ADB_CMD_CHANGE_ID_AND_ENABLE:
|
||||
d->devaddr = buf[1] & 0xf;
|
||||
break;
|
||||
default:
|
||||
/* XXX: check this */
|
||||
d->devaddr = buf[1] & 0xf;
|
||||
d->handler = buf[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ADB_READREG:
|
||||
switch(reg) {
|
||||
case 0:
|
||||
olen = adb_kbd_poll(d, obuf);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
obuf[0] = 0x00; /* XXX: check this */
|
||||
obuf[1] = 0x07; /* led status */
|
||||
olen = 2;
|
||||
break;
|
||||
case 3:
|
||||
obuf[0] = d->handler;
|
||||
obuf[1] = d->devaddr;
|
||||
olen = 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
static int adb_kbd_reset(ADBDevice *d)
|
||||
{
|
||||
KBDState *s = d->opaque;
|
||||
|
||||
d->handler = 1;
|
||||
d->devaddr = ADB_KEYBOARD;
|
||||
memset(s, 0, sizeof(KBDState));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adb_kbd_init(ADBBusState *bus)
|
||||
{
|
||||
ADBDevice *d;
|
||||
KBDState *s;
|
||||
s = qemu_mallocz(sizeof(KBDState));
|
||||
d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request,
|
||||
adb_kbd_reset, s);
|
||||
adb_kbd_reset(d);
|
||||
qemu_add_kbd_event_handler(adb_kbd_put_keycode, d);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* Mouse ADB device */
|
||||
|
||||
typedef struct MouseState {
|
||||
int buttons_state, last_buttons_state;
|
||||
int dx, dy, dz;
|
||||
} MouseState;
|
||||
|
||||
static void adb_mouse_event(void *opaque,
|
||||
int dx1, int dy1, int dz1, int buttons_state)
|
||||
{
|
||||
ADBDevice *d = opaque;
|
||||
MouseState *s = d->opaque;
|
||||
|
||||
s->dx += dx1;
|
||||
s->dy += dy1;
|
||||
s->dz += dz1;
|
||||
s->buttons_state = buttons_state;
|
||||
}
|
||||
|
||||
|
||||
static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf)
|
||||
{
|
||||
MouseState *s = d->opaque;
|
||||
int dx, dy;
|
||||
|
||||
if (s->last_buttons_state == s->buttons_state &&
|
||||
s->dx == 0 && s->dy == 0)
|
||||
return 0;
|
||||
|
||||
dx = s->dx;
|
||||
if (dx < -63)
|
||||
dx = -63;
|
||||
else if (dx > 63)
|
||||
dx = 63;
|
||||
|
||||
dy = s->dy;
|
||||
if (dy < -63)
|
||||
dy = -63;
|
||||
else if (dy > 63)
|
||||
dy = 63;
|
||||
|
||||
s->dx -= dx;
|
||||
s->dy -= dy;
|
||||
s->last_buttons_state = s->buttons_state;
|
||||
|
||||
dx &= 0x7f;
|
||||
dy &= 0x7f;
|
||||
|
||||
if (!(s->buttons_state & MOUSE_EVENT_LBUTTON))
|
||||
dy |= 0x80;
|
||||
if (!(s->buttons_state & MOUSE_EVENT_RBUTTON))
|
||||
dx |= 0x80;
|
||||
|
||||
obuf[0] = dy;
|
||||
obuf[1] = dx;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
|
||||
const uint8_t *buf, int len)
|
||||
{
|
||||
MouseState *s = d->opaque;
|
||||
int cmd, reg, olen;
|
||||
|
||||
if ((buf[0] & 0x0f) == ADB_FLUSH) {
|
||||
/* flush mouse fifo */
|
||||
s->buttons_state = s->last_buttons_state;
|
||||
s->dx = 0;
|
||||
s->dy = 0;
|
||||
s->dz = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd = buf[0] & 0xc;
|
||||
reg = buf[0] & 0x3;
|
||||
olen = 0;
|
||||
switch(cmd) {
|
||||
case ADB_WRITEREG:
|
||||
switch(reg) {
|
||||
case 2:
|
||||
break;
|
||||
case 3:
|
||||
switch(buf[2]) {
|
||||
case ADB_CMD_SELF_TEST:
|
||||
break;
|
||||
case ADB_CMD_CHANGE_ID:
|
||||
case ADB_CMD_CHANGE_ID_AND_ACT:
|
||||
case ADB_CMD_CHANGE_ID_AND_ENABLE:
|
||||
d->devaddr = buf[1] & 0xf;
|
||||
break;
|
||||
default:
|
||||
/* XXX: check this */
|
||||
d->devaddr = buf[1] & 0xf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ADB_READREG:
|
||||
switch(reg) {
|
||||
case 0:
|
||||
olen = adb_mouse_poll(d, obuf);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 3:
|
||||
obuf[0] = d->handler;
|
||||
obuf[1] = d->devaddr;
|
||||
olen = 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
static int adb_mouse_reset(ADBDevice *d)
|
||||
{
|
||||
MouseState *s = d->opaque;
|
||||
|
||||
d->handler = 2;
|
||||
d->devaddr = ADB_MOUSE;
|
||||
memset(s, 0, sizeof(MouseState));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adb_mouse_init(ADBBusState *bus)
|
||||
{
|
||||
ADBDevice *d;
|
||||
MouseState *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(MouseState));
|
||||
d = adb_register_device(bus, ADB_MOUSE, adb_mouse_request,
|
||||
adb_mouse_reset, s);
|
||||
adb_mouse_reset(d);
|
||||
qemu_add_mouse_event_handler(adb_mouse_event, d, 0, "QEMU ADB Mouse");
|
||||
}
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* QEMU Proxy for OPL2/3 emulation by MAME team
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include "vl.h"
|
||||
|
||||
#define ADLIB_KILL_TIMERS 1
|
||||
|
||||
#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
#include "ymf262.h"
|
||||
void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
|
||||
#define SHIFT 2
|
||||
#else
|
||||
#include "fmopl.h"
|
||||
#define SHIFT 1
|
||||
#endif
|
||||
|
||||
#define IO_READ_PROTO(name) \
|
||||
uint32_t name (void *opaque, uint32_t nport)
|
||||
#define IO_WRITE_PROTO(name) \
|
||||
void name (void *opaque, uint32_t nport, uint32_t val)
|
||||
|
||||
static struct {
|
||||
int port;
|
||||
int freq;
|
||||
} conf = {0x220, 44100};
|
||||
|
||||
typedef struct {
|
||||
QEMUSoundCard card;
|
||||
int ticking[2];
|
||||
int enabled;
|
||||
int active;
|
||||
int bufpos;
|
||||
#ifdef DEBUG
|
||||
int64_t exp[2];
|
||||
#endif
|
||||
int16_t *mixbuf;
|
||||
uint64_t dexp[2];
|
||||
SWVoiceOut *voice;
|
||||
int left, pos, samples;
|
||||
QEMUAudioTimeStamp ats;
|
||||
#ifndef HAS_YMF262
|
||||
FM_OPL *opl;
|
||||
#endif
|
||||
} AdlibState;
|
||||
|
||||
static AdlibState glob_adlib;
|
||||
|
||||
static void adlib_stop_opl_timer (AdlibState *s, size_t n)
|
||||
{
|
||||
#ifdef HAS_YMF262
|
||||
YMF262TimerOver (0, n);
|
||||
#else
|
||||
OPLTimerOver (s->opl, n);
|
||||
#endif
|
||||
s->ticking[n] = 0;
|
||||
}
|
||||
|
||||
static void adlib_kill_timers (AdlibState *s)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
if (s->ticking[i]) {
|
||||
uint64_t delta;
|
||||
|
||||
delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
|
||||
ldebug (
|
||||
"delta = %f dexp = %f expired => %d\n",
|
||||
delta / 1000000.0,
|
||||
s->dexp[i] / 1000000.0,
|
||||
delta >= s->dexp[i]
|
||||
);
|
||||
if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
|
||||
adlib_stop_opl_timer (s, i);
|
||||
AUD_init_time_stamp_out (s->voice, &s->ats);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static IO_WRITE_PROTO(adlib_write)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int a = nport & 3;
|
||||
int status;
|
||||
|
||||
s->active = 1;
|
||||
AUD_set_active_out (s->voice, 1);
|
||||
|
||||
adlib_kill_timers (s);
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
status = YMF262Write (0, a, val);
|
||||
#else
|
||||
status = OPLWrite (s->opl, a, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static IO_READ_PROTO(adlib_read)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
uint8_t data;
|
||||
int a = nport & 3;
|
||||
|
||||
adlib_kill_timers (s);
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
data = YMF262Read (0, a);
|
||||
#else
|
||||
data = OPLRead (s->opl, a);
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
|
||||
static void timer_handler (int c, double interval_Sec)
|
||||
{
|
||||
AdlibState *s = &glob_adlib;
|
||||
unsigned n = c & 1;
|
||||
#ifdef DEBUG
|
||||
double interval;
|
||||
int64_t exp;
|
||||
#endif
|
||||
|
||||
if (interval_Sec == 0.0) {
|
||||
s->ticking[n] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
s->ticking[n] = 1;
|
||||
#ifdef DEBUG
|
||||
interval = ticks_per_sec * interval_Sec;
|
||||
exp = qemu_get_clock (vm_clock) + interval;
|
||||
s->exp[n] = exp;
|
||||
#endif
|
||||
|
||||
s->dexp[n] = interval_Sec * 1000000.0;
|
||||
AUD_init_time_stamp_out (s->voice, &s->ats);
|
||||
}
|
||||
|
||||
static int write_audio (AdlibState *s, int samples)
|
||||
{
|
||||
int net = 0;
|
||||
int pos = s->pos;
|
||||
|
||||
while (samples) {
|
||||
int nbytes, wbytes, wsampl;
|
||||
|
||||
nbytes = samples << SHIFT;
|
||||
wbytes = AUD_write (
|
||||
s->voice,
|
||||
s->mixbuf + (pos << (SHIFT - 1)),
|
||||
nbytes
|
||||
);
|
||||
|
||||
if (wbytes) {
|
||||
wsampl = wbytes >> SHIFT;
|
||||
|
||||
samples -= wsampl;
|
||||
pos = (pos + wsampl) % s->samples;
|
||||
|
||||
net += wsampl;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return net;
|
||||
}
|
||||
|
||||
static void adlib_callback (void *opaque, int free)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int samples, net = 0, to_play, written;
|
||||
|
||||
samples = free >> SHIFT;
|
||||
if (!(s->active && s->enabled) || !samples) {
|
||||
return;
|
||||
}
|
||||
|
||||
to_play = audio_MIN (s->left, samples);
|
||||
while (to_play) {
|
||||
written = write_audio (s, to_play);
|
||||
|
||||
if (written) {
|
||||
s->left -= written;
|
||||
samples -= written;
|
||||
to_play -= written;
|
||||
s->pos = (s->pos + written) % s->samples;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
samples = audio_MIN (samples, s->samples - s->pos);
|
||||
if (!samples) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
|
||||
#else
|
||||
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
|
||||
#endif
|
||||
|
||||
while (samples) {
|
||||
written = write_audio (s, samples);
|
||||
|
||||
if (written) {
|
||||
net += written;
|
||||
samples -= written;
|
||||
s->pos = (s->pos + written) % s->samples;
|
||||
}
|
||||
else {
|
||||
s->left = samples;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Adlib_fini (AdlibState *s)
|
||||
{
|
||||
#ifdef HAS_YMF262
|
||||
YMF262Shutdown ();
|
||||
#else
|
||||
if (s->opl) {
|
||||
OPLDestroy (s->opl);
|
||||
s->opl = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s->mixbuf) {
|
||||
qemu_free (s->mixbuf);
|
||||
}
|
||||
|
||||
s->active = 0;
|
||||
s->enabled = 0;
|
||||
AUD_remove_card (&s->card);
|
||||
}
|
||||
|
||||
int Adlib_init (AudioState *audio)
|
||||
{
|
||||
AdlibState *s = &glob_adlib;
|
||||
audsettings_t as;
|
||||
|
||||
if (!audio) {
|
||||
dolog ("No audio state\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
if (YMF262Init (1, 14318180, conf.freq)) {
|
||||
dolog ("YMF262Init %d failed\n", conf.freq);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
YMF262SetTimerHandler (0, timer_handler, 0);
|
||||
s->enabled = 1;
|
||||
}
|
||||
#else
|
||||
s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
|
||||
if (!s->opl) {
|
||||
dolog ("OPLCreate %d failed\n", conf.freq);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
OPLSetTimerHandler (s->opl, timer_handler, 0);
|
||||
s->enabled = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
as.freq = conf.freq;
|
||||
as.nchannels = SHIFT;
|
||||
as.fmt = AUD_FMT_S16;
|
||||
as.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
|
||||
AUD_register_card (audio, "adlib", &s->card);
|
||||
|
||||
s->voice = AUD_open_out (
|
||||
&s->card,
|
||||
s->voice,
|
||||
"adlib",
|
||||
s,
|
||||
adlib_callback,
|
||||
&as
|
||||
);
|
||||
if (!s->voice) {
|
||||
Adlib_fini (s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
|
||||
s->mixbuf = qemu_mallocz (s->samples << SHIFT);
|
||||
|
||||
if (!s->mixbuf) {
|
||||
dolog ("Could not allocate mixing buffer, %d samples (each %d bytes)\n",
|
||||
s->samples, 1 << SHIFT);
|
||||
Adlib_fini (s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
register_ioport_read (0x388, 4, 1, adlib_read, s);
|
||||
register_ioport_write (0x388, 4, 1, adlib_write, s);
|
||||
|
||||
register_ioport_read (conf.port, 4, 1, adlib_read, s);
|
||||
register_ioport_write (conf.port, 4, 1, adlib_write, s);
|
||||
|
||||
register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
|
||||
register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* QEMU Ultrasparc APB PCI host
|
||||
*
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* XXX This file and most of its contests are somewhat misnamed. The
|
||||
Ultrasparc PCI host is called the PCI Bus Module (PBM). The APB is
|
||||
the secondary PCI bridge. */
|
||||
|
||||
#include "vl.h"
|
||||
typedef target_phys_addr_t pci_addr_t;
|
||||
#include "pci_host.h"
|
||||
|
||||
typedef PCIHostState APBState;
|
||||
|
||||
static void pci_apb_config_writel (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
APBState *s = opaque;
|
||||
int i;
|
||||
|
||||
for (i = 11; i < 32; i++) {
|
||||
if ((val & (1 << i)) != 0)
|
||||
break;
|
||||
}
|
||||
s->config_reg = (1 << 16) | (val & 0x7FC) | (i << 11);
|
||||
}
|
||||
|
||||
static uint32_t pci_apb_config_readl (void *opaque,
|
||||
target_phys_addr_t addr)
|
||||
{
|
||||
APBState *s = opaque;
|
||||
uint32_t val;
|
||||
int devfn;
|
||||
|
||||
devfn = (s->config_reg >> 8) & 0xFF;
|
||||
val = (1 << (devfn >> 3)) | ((devfn & 0x07) << 8) | (s->config_reg & 0xFC);
|
||||
return val;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *pci_apb_config_write[] = {
|
||||
&pci_apb_config_writel,
|
||||
&pci_apb_config_writel,
|
||||
&pci_apb_config_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *pci_apb_config_read[] = {
|
||||
&pci_apb_config_readl,
|
||||
&pci_apb_config_readl,
|
||||
&pci_apb_config_readl,
|
||||
};
|
||||
|
||||
static void apb_config_writel (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
//PCIBus *s = opaque;
|
||||
|
||||
switch (addr & 0x3f) {
|
||||
case 0x00: // Control/Status
|
||||
case 0x10: // AFSR
|
||||
case 0x18: // AFAR
|
||||
case 0x20: // Diagnostic
|
||||
case 0x28: // Target address space
|
||||
// XXX
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t apb_config_readl (void *opaque,
|
||||
target_phys_addr_t addr)
|
||||
{
|
||||
//PCIBus *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
switch (addr & 0x3f) {
|
||||
case 0x00: // Control/Status
|
||||
case 0x10: // AFSR
|
||||
case 0x18: // AFAR
|
||||
case 0x20: // Diagnostic
|
||||
case 0x28: // Target address space
|
||||
// XXX
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *apb_config_write[] = {
|
||||
&apb_config_writel,
|
||||
&apb_config_writel,
|
||||
&apb_config_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *apb_config_read[] = {
|
||||
&apb_config_readl,
|
||||
&apb_config_readl,
|
||||
&apb_config_readl,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *pci_apb_write[] = {
|
||||
&pci_host_data_writeb,
|
||||
&pci_host_data_writew,
|
||||
&pci_host_data_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *pci_apb_read[] = {
|
||||
&pci_host_data_readb,
|
||||
&pci_host_data_readw,
|
||||
&pci_host_data_readl,
|
||||
};
|
||||
|
||||
static void pci_apb_iowriteb (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
cpu_outb(NULL, addr & 0xffff, val);
|
||||
}
|
||||
|
||||
static void pci_apb_iowritew (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
cpu_outw(NULL, addr & 0xffff, val);
|
||||
}
|
||||
|
||||
static void pci_apb_iowritel (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
cpu_outl(NULL, addr & 0xffff, val);
|
||||
}
|
||||
|
||||
static uint32_t pci_apb_ioreadb (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = cpu_inb(NULL, addr & 0xffff);
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t pci_apb_ioreadw (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = cpu_inw(NULL, addr & 0xffff);
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t pci_apb_ioreadl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = cpu_inl(NULL, addr & 0xffff);
|
||||
return val;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *pci_apb_iowrite[] = {
|
||||
&pci_apb_iowriteb,
|
||||
&pci_apb_iowritew,
|
||||
&pci_apb_iowritel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *pci_apb_ioread[] = {
|
||||
&pci_apb_ioreadb,
|
||||
&pci_apb_ioreadw,
|
||||
&pci_apb_ioreadl,
|
||||
};
|
||||
|
||||
/* The APB host has an IRQ line for each IRQ line of each slot. */
|
||||
static int pci_apb_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||
{
|
||||
return ((pci_dev->devfn & 0x18) >> 1) + irq_num;
|
||||
}
|
||||
|
||||
static int pci_pbm_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||
{
|
||||
int bus_offset;
|
||||
if (pci_dev->devfn & 1)
|
||||
bus_offset = 16;
|
||||
else
|
||||
bus_offset = 0;
|
||||
return bus_offset + irq_num;
|
||||
}
|
||||
|
||||
static void pci_apb_set_irq(void *pic, int irq_num, int level)
|
||||
{
|
||||
/* PCI IRQ map onto the first 32 INO. */
|
||||
pic_set_irq_new(pic, irq_num, level);
|
||||
}
|
||||
|
||||
PCIBus *pci_apb_init(target_ulong special_base, target_ulong mem_base,
|
||||
void *pic)
|
||||
{
|
||||
APBState *s;
|
||||
PCIDevice *d;
|
||||
int pci_mem_config, pci_mem_data, apb_config, pci_ioport;
|
||||
PCIDevice *apb;
|
||||
PCIBus *secondary;
|
||||
|
||||
s = qemu_mallocz(sizeof(APBState));
|
||||
/* Ultrasparc PBM main bus */
|
||||
s->bus = pci_register_bus(pci_apb_set_irq, pci_pbm_map_irq, pic, 0, 32);
|
||||
|
||||
pci_mem_config = cpu_register_io_memory(0, pci_apb_config_read,
|
||||
pci_apb_config_write, s);
|
||||
apb_config = cpu_register_io_memory(0, apb_config_read,
|
||||
apb_config_write, s);
|
||||
pci_mem_data = cpu_register_io_memory(0, pci_apb_read,
|
||||
pci_apb_write, s);
|
||||
pci_ioport = cpu_register_io_memory(0, pci_apb_ioread,
|
||||
pci_apb_iowrite, s);
|
||||
|
||||
cpu_register_physical_memory(special_base + 0x2000ULL, 0x40, apb_config);
|
||||
cpu_register_physical_memory(special_base + 0x1000000ULL, 0x10, pci_mem_config);
|
||||
cpu_register_physical_memory(special_base + 0x2000000ULL, 0x10000, pci_ioport);
|
||||
cpu_register_physical_memory(mem_base, 0x10000000, pci_mem_data); // XXX size should be 4G-prom
|
||||
|
||||
d = pci_register_device(s->bus, "Advanced PCI Bus", sizeof(PCIDevice),
|
||||
0, NULL, NULL);
|
||||
d->config[0x00] = 0x8e; // vendor_id : Sun
|
||||
d->config[0x01] = 0x10;
|
||||
d->config[0x02] = 0x00; // device_id
|
||||
d->config[0x03] = 0xa0;
|
||||
d->config[0x04] = 0x06; // command = bus master, pci mem
|
||||
d->config[0x05] = 0x00;
|
||||
d->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error
|
||||
d->config[0x07] = 0x03; // status = medium devsel
|
||||
d->config[0x08] = 0x00; // revision
|
||||
d->config[0x09] = 0x00; // programming i/f
|
||||
d->config[0x0A] = 0x00; // class_sub = pci host
|
||||
d->config[0x0B] = 0x06; // class_base = PCI_bridge
|
||||
d->config[0x0D] = 0x10; // latency_timer
|
||||
d->config[0x0E] = 0x00; // header_type
|
||||
|
||||
/* APB secondary busses */
|
||||
secondary = pci_bridge_init(s->bus, 8, 0x108e5000, pci_apb_map_irq, "Advanced PCI Bus secondary bridge 1");
|
||||
pci_bridge_init(s->bus, 9, 0x108e5000, pci_apb_map_irq, "Advanced PCI Bus secondary bridge 2");
|
||||
return secondary;
|
||||
}
|
||||
|
||||
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* ARM kernel loader.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
#define KERNEL_ARGS_ADDR 0x100
|
||||
#define KERNEL_LOAD_ADDR 0x00010000
|
||||
#define INITRD_LOAD_ADDR 0x00800000
|
||||
|
||||
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
|
||||
static uint32_t bootloader[] = {
|
||||
0xe3a00000, /* mov r0, #0 */
|
||||
0xe3a01000, /* mov r1, #0x?? */
|
||||
0xe3811c00, /* orr r1, r1, #0x??00 */
|
||||
0xe59f2000, /* ldr r2, [pc, #0] */
|
||||
0xe59ff000, /* ldr pc, [pc, #0] */
|
||||
0, /* Address of kernel args. Set by integratorcp_init. */
|
||||
0 /* Kernel entry point. Set by integratorcp_init. */
|
||||
};
|
||||
|
||||
static void set_kernel_args(uint32_t ram_size, int initrd_size,
|
||||
const char *kernel_cmdline)
|
||||
{
|
||||
uint32_t *p;
|
||||
|
||||
p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR);
|
||||
/* ATAG_CORE */
|
||||
stl_raw(p++, 5);
|
||||
stl_raw(p++, 0x54410001);
|
||||
stl_raw(p++, 1);
|
||||
stl_raw(p++, 0x1000);
|
||||
stl_raw(p++, 0);
|
||||
/* ATAG_MEM */
|
||||
stl_raw(p++, 4);
|
||||
stl_raw(p++, 0x54410002);
|
||||
stl_raw(p++, ram_size);
|
||||
stl_raw(p++, 0);
|
||||
if (initrd_size) {
|
||||
/* ATAG_INITRD2 */
|
||||
stl_raw(p++, 4);
|
||||
stl_raw(p++, 0x54420005);
|
||||
stl_raw(p++, INITRD_LOAD_ADDR);
|
||||
stl_raw(p++, initrd_size);
|
||||
}
|
||||
if (kernel_cmdline && *kernel_cmdline) {
|
||||
/* ATAG_CMDLINE */
|
||||
int cmdline_size;
|
||||
|
||||
cmdline_size = strlen(kernel_cmdline);
|
||||
memcpy (p + 2, kernel_cmdline, cmdline_size + 1);
|
||||
cmdline_size = (cmdline_size >> 2) + 1;
|
||||
stl_raw(p++, cmdline_size + 2);
|
||||
stl_raw(p++, 0x54410009);
|
||||
p += cmdline_size;
|
||||
}
|
||||
/* ATAG_END */
|
||||
stl_raw(p++, 0);
|
||||
stl_raw(p++, 0);
|
||||
}
|
||||
|
||||
void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
|
||||
const char *kernel_cmdline, const char *initrd_filename,
|
||||
int board_id)
|
||||
{
|
||||
int kernel_size;
|
||||
int initrd_size;
|
||||
int n;
|
||||
uint64_t entry;
|
||||
|
||||
/* Load the kernel. */
|
||||
if (!kernel_filename) {
|
||||
fprintf(stderr, "Kernel image must be specified\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
kernel_size = load_elf(kernel_filename, 0, &entry);
|
||||
if (kernel_size >= 0) {
|
||||
/* An ELF image. Jump to the entry point. */
|
||||
env->regs[15] = entry & 0xfffffffe;
|
||||
env->thumb = entry & 1;
|
||||
} else {
|
||||
/* Raw binary image. Assume it is a Linux zImage. */
|
||||
kernel_size = load_image(kernel_filename,
|
||||
phys_ram_base + KERNEL_LOAD_ADDR);
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
if (initrd_filename) {
|
||||
initrd_size = load_image(initrd_filename,
|
||||
phys_ram_base + INITRD_LOAD_ADDR);
|
||||
if (initrd_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load initrd '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_size = 0;
|
||||
}
|
||||
bootloader[1] |= board_id & 0xff;
|
||||
bootloader[2] |= (board_id >> 8) & 0xff;
|
||||
bootloader[5] = KERNEL_ARGS_ADDR;
|
||||
bootloader[6] = KERNEL_LOAD_ADDR;
|
||||
for (n = 0; n < sizeof(bootloader) / 4; n++)
|
||||
stl_raw(phys_ram_base + (n * 4), bootloader[n]);
|
||||
set_kernel_args(ram_size, initrd_size, kernel_cmdline);
|
||||
}
|
||||
}
|
||||
|
||||
+547
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
* ARM AMBA Generic/Distributed Interrupt Controller
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
/* TODO: Some variants of this controller can handle multiple CPUs.
|
||||
Currently only single CPU operation is implemented. */
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
//#define DEBUG_GIC
|
||||
|
||||
#ifdef DEBUG_GIC
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("arm_gic: " fmt , ##args); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/* Distributed interrupt controller. */
|
||||
|
||||
static const uint8_t gic_id[] =
|
||||
{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
|
||||
#define GIC_NIRQ 96
|
||||
|
||||
typedef struct gic_irq_state
|
||||
{
|
||||
unsigned enabled:1;
|
||||
unsigned pending:1;
|
||||
unsigned active:1;
|
||||
unsigned level:1;
|
||||
unsigned model:1; /* 0 = 1:N, 1 = N:N */
|
||||
unsigned trigger:1; /* nonzero = edge triggered. */
|
||||
} gic_irq_state;
|
||||
|
||||
#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
|
||||
#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
|
||||
#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
|
||||
#define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1
|
||||
#define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0
|
||||
#define GIC_TEST_PENDING(irq) s->irq_state[irq].pending
|
||||
#define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1
|
||||
#define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0
|
||||
#define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active
|
||||
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
|
||||
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
|
||||
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
|
||||
#define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1
|
||||
#define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0
|
||||
#define GIC_TEST_LEVEL(irq) s->irq_state[irq].level
|
||||
#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
|
||||
#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
|
||||
#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
|
||||
|
||||
typedef struct gic_state
|
||||
{
|
||||
arm_pic_handler handler;
|
||||
uint32_t base;
|
||||
void *parent;
|
||||
int parent_irq;
|
||||
int enabled;
|
||||
int cpu_enabled;
|
||||
|
||||
gic_irq_state irq_state[GIC_NIRQ];
|
||||
int irq_target[GIC_NIRQ];
|
||||
int priority[GIC_NIRQ];
|
||||
int last_active[GIC_NIRQ];
|
||||
|
||||
int priority_mask;
|
||||
int running_irq;
|
||||
int running_priority;
|
||||
int current_pending;
|
||||
} gic_state;
|
||||
|
||||
/* TODO: Many places that call this routine could be optimized. */
|
||||
/* Update interrupt status after enabled or pending bits have been changed. */
|
||||
static void gic_update(gic_state *s)
|
||||
{
|
||||
int best_irq;
|
||||
int best_prio;
|
||||
int irq;
|
||||
|
||||
s->current_pending = 1023;
|
||||
if (!s->enabled || !s->cpu_enabled) {
|
||||
pic_set_irq_new(s->parent, s->parent_irq, 0);
|
||||
return;
|
||||
}
|
||||
best_prio = 0x100;
|
||||
best_irq = 1023;
|
||||
for (irq = 0; irq < 96; irq++) {
|
||||
if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) {
|
||||
if (s->priority[irq] < best_prio) {
|
||||
best_prio = s->priority[irq];
|
||||
best_irq = irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best_prio > s->priority_mask) {
|
||||
pic_set_irq_new(s->parent, s->parent_irq, 0);
|
||||
} else {
|
||||
s->current_pending = best_irq;
|
||||
if (best_prio < s->running_priority) {
|
||||
DPRINTF("Raised pending IRQ %d\n", best_irq);
|
||||
pic_set_irq_new(s->parent, s->parent_irq, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
/* The first external input line is internal interrupt 32. */
|
||||
irq += 32;
|
||||
if (level == GIC_TEST_LEVEL(irq))
|
||||
return;
|
||||
|
||||
if (level) {
|
||||
GIC_SET_LEVEL(irq);
|
||||
if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
|
||||
DPRINTF("Set %d pending\n", irq);
|
||||
GIC_SET_PENDING(irq);
|
||||
}
|
||||
} else {
|
||||
GIC_CLEAR_LEVEL(irq);
|
||||
}
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
static void gic_set_running_irq(gic_state *s, int irq)
|
||||
{
|
||||
s->running_irq = irq;
|
||||
if (irq == 1023)
|
||||
s->running_priority = 0x100;
|
||||
else
|
||||
s->running_priority = s->priority[irq];
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
static uint32_t gic_acknowledge_irq(gic_state *s)
|
||||
{
|
||||
int new_irq;
|
||||
new_irq = s->current_pending;
|
||||
if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) {
|
||||
DPRINTF("ACK no pending IRQ\n");
|
||||
return 1023;
|
||||
}
|
||||
pic_set_irq_new(s->parent, s->parent_irq, 0);
|
||||
s->last_active[new_irq] = s->running_irq;
|
||||
/* For level triggered interrupts we clear the pending bit while
|
||||
the interrupt is active. */
|
||||
GIC_CLEAR_PENDING(new_irq);
|
||||
gic_set_running_irq(s, new_irq);
|
||||
DPRINTF("ACK %d\n", new_irq);
|
||||
return new_irq;
|
||||
}
|
||||
|
||||
static void gic_complete_irq(gic_state * s, int irq)
|
||||
{
|
||||
int update = 0;
|
||||
DPRINTF("EOI %d\n", irq);
|
||||
if (s->running_irq == 1023)
|
||||
return; /* No active IRQ. */
|
||||
if (irq != 1023) {
|
||||
/* Mark level triggered interrupts as pending if they are still
|
||||
raised. */
|
||||
if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
|
||||
&& GIC_TEST_LEVEL(irq)) {
|
||||
GIC_SET_PENDING(irq);
|
||||
update = 1;
|
||||
}
|
||||
}
|
||||
if (irq != s->running_irq) {
|
||||
/* Complete an IRQ that is not currently running. */
|
||||
int tmp = s->running_irq;
|
||||
while (s->last_active[tmp] != 1023) {
|
||||
if (s->last_active[tmp] == irq) {
|
||||
s->last_active[tmp] = s->last_active[irq];
|
||||
break;
|
||||
}
|
||||
tmp = s->last_active[tmp];
|
||||
}
|
||||
if (update) {
|
||||
gic_update(s);
|
||||
}
|
||||
} else {
|
||||
/* Complete the current running IRQ. */
|
||||
gic_set_running_irq(s, s->last_active[s->running_irq]);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
uint32_t res;
|
||||
int irq;
|
||||
int i;
|
||||
|
||||
offset -= s->base + 0x1000;
|
||||
if (offset < 0x100) {
|
||||
if (offset == 0)
|
||||
return s->enabled;
|
||||
if (offset == 4)
|
||||
return (GIC_NIRQ / 32) - 1;
|
||||
if (offset < 0x08)
|
||||
return 0;
|
||||
goto bad_reg;
|
||||
} else if (offset < 0x200) {
|
||||
/* Interrupt Set/Clear Enable. */
|
||||
if (offset < 0x180)
|
||||
irq = (offset - 0x100) * 8;
|
||||
else
|
||||
irq = (offset - 0x180) * 8;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (GIC_TEST_ENABLED(irq + i)) {
|
||||
res |= (1 << i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x300) {
|
||||
/* Interrupt Set/Clear Pending. */
|
||||
if (offset < 0x280)
|
||||
irq = (offset - 0x200) * 8;
|
||||
else
|
||||
irq = (offset - 0x280) * 8;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (GIC_TEST_PENDING(irq + i)) {
|
||||
res |= (1 << i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x400) {
|
||||
/* Interrupt Active. */
|
||||
irq = (offset - 0x300) * 8;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (GIC_TEST_ACTIVE(irq + i)) {
|
||||
res |= (1 << i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x800) {
|
||||
/* Interrupt Priority. */
|
||||
irq = offset - 0x400;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = s->priority[irq];
|
||||
} else if (offset < 0xc00) {
|
||||
/* Interrupt CPU Target. */
|
||||
irq = offset - 0x800;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = s->irq_target[irq];
|
||||
} else if (offset < 0xf00) {
|
||||
/* Interrupt Configuration. */
|
||||
irq = (offset - 0xc00) * 2;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (GIC_TEST_MODEL(irq + i))
|
||||
res |= (1 << (i * 2));
|
||||
if (GIC_TEST_TRIGGER(irq + i))
|
||||
res |= (2 << (i * 2));
|
||||
}
|
||||
} else if (offset < 0xfe0) {
|
||||
goto bad_reg;
|
||||
} else /* offset >= 0xfe0 */ {
|
||||
if (offset & 3) {
|
||||
res = 0;
|
||||
} else {
|
||||
res = gic_id[(offset - 0xfe0) >> 2];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
bad_reg:
|
||||
cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
uint32_t val;
|
||||
val = gic_dist_readb(opaque, offset);
|
||||
val |= gic_dist_readb(opaque, offset + 1) << 8;
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
uint32_t val;
|
||||
val = gic_dist_readw(opaque, offset);
|
||||
val |= gic_dist_readw(opaque, offset + 2) << 16;
|
||||
return val;
|
||||
}
|
||||
|
||||
static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
int irq;
|
||||
int i;
|
||||
|
||||
offset -= s->base + 0x1000;
|
||||
if (offset < 0x100) {
|
||||
if (offset == 0) {
|
||||
s->enabled = (value & 1);
|
||||
DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
|
||||
} else if (offset < 4) {
|
||||
/* ignored. */
|
||||
} else {
|
||||
goto bad_reg;
|
||||
}
|
||||
} else if (offset < 0x180) {
|
||||
/* Interrupt Set Enable. */
|
||||
irq = (offset - 0x100) * 8;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (value & (1 << i)) {
|
||||
if (!GIC_TEST_ENABLED(irq + i))
|
||||
DPRINTF("Enabled IRQ %d\n", irq + i);
|
||||
GIC_SET_ENABLED(irq + i);
|
||||
/* If a raised level triggered IRQ enabled then mark
|
||||
is as pending. */
|
||||
if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i))
|
||||
GIC_SET_PENDING(irq + i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x200) {
|
||||
/* Interrupt Clear Enable. */
|
||||
irq = (offset - 0x180) * 8;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (value & (1 << i)) {
|
||||
if (GIC_TEST_ENABLED(irq + i))
|
||||
DPRINTF("Disabled IRQ %d\n", irq + i);
|
||||
GIC_CLEAR_ENABLED(irq + i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x280) {
|
||||
/* Interrupt Set Pending. */
|
||||
irq = (offset - 0x200) * 8;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (value & (1 << i)) {
|
||||
GIC_SET_PENDING(irq + i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x300) {
|
||||
/* Interrupt Clear Pending. */
|
||||
irq = (offset - 0x280) * 8;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (value & (1 << i)) {
|
||||
GIC_CLEAR_PENDING(irq + i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x400) {
|
||||
/* Interrupt Active. */
|
||||
goto bad_reg;
|
||||
} else if (offset < 0x800) {
|
||||
/* Interrupt Priority. */
|
||||
irq = offset - 0x400;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
s->priority[irq] = value;
|
||||
} else if (offset < 0xc00) {
|
||||
/* Interrupt CPU Target. */
|
||||
irq = offset - 0x800;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
s->irq_target[irq] = value;
|
||||
} else if (offset < 0xf00) {
|
||||
/* Interrupt Configuration. */
|
||||
irq = (offset - 0xc00) * 4;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (value & (1 << (i * 2))) {
|
||||
GIC_SET_MODEL(irq + i);
|
||||
} else {
|
||||
GIC_CLEAR_MODEL(irq + i);
|
||||
}
|
||||
if (value & (2 << (i * 2))) {
|
||||
GIC_SET_TRIGGER(irq + i);
|
||||
} else {
|
||||
GIC_CLEAR_TRIGGER(irq + i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* 0xf00 is only handled for word writes. */
|
||||
goto bad_reg;
|
||||
}
|
||||
gic_update(s);
|
||||
return;
|
||||
bad_reg:
|
||||
cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset);
|
||||
}
|
||||
|
||||
static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
if (offset - s->base == 0xf00) {
|
||||
GIC_SET_PENDING(value & 0x3ff);
|
||||
gic_update(s);
|
||||
return;
|
||||
}
|
||||
gic_dist_writeb(opaque, offset, value & 0xff);
|
||||
gic_dist_writeb(opaque, offset + 1, value >> 8);
|
||||
}
|
||||
|
||||
static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
gic_dist_writew(opaque, offset, value & 0xffff);
|
||||
gic_dist_writew(opaque, offset + 2, value >> 16);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *gic_dist_readfn[] = {
|
||||
gic_dist_readb,
|
||||
gic_dist_readw,
|
||||
gic_dist_readl
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *gic_dist_writefn[] = {
|
||||
gic_dist_writeb,
|
||||
gic_dist_writew,
|
||||
gic_dist_writel
|
||||
};
|
||||
|
||||
static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
offset -= s->base;
|
||||
switch (offset) {
|
||||
case 0x00: /* Control */
|
||||
return s->cpu_enabled;
|
||||
case 0x04: /* Priority mask */
|
||||
return s->priority_mask;
|
||||
case 0x08: /* Binary Point */
|
||||
/* ??? Not implemented. */
|
||||
return 0;
|
||||
case 0x0c: /* Acknowledge */
|
||||
return gic_acknowledge_irq(s);
|
||||
case 0x14: /* Runing Priority */
|
||||
return s->running_priority;
|
||||
case 0x18: /* Highest Pending Interrupt */
|
||||
return s->current_pending;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "gic_cpu_writeb: Bad offset %x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void gic_cpu_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
offset -= s->base;
|
||||
switch (offset) {
|
||||
case 0x00: /* Control */
|
||||
s->cpu_enabled = (value & 1);
|
||||
DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
|
||||
break;
|
||||
case 0x04: /* Priority mask */
|
||||
s->priority_mask = (value & 0x3ff);
|
||||
break;
|
||||
case 0x08: /* Binary Point */
|
||||
/* ??? Not implemented. */
|
||||
break;
|
||||
case 0x10: /* End Of Interrupt */
|
||||
return gic_complete_irq(s, value & 0x3ff);
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "gic_cpu_writeb: Bad offset %x\n", offset);
|
||||
return;
|
||||
}
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *gic_cpu_readfn[] = {
|
||||
gic_cpu_read,
|
||||
gic_cpu_read,
|
||||
gic_cpu_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *gic_cpu_writefn[] = {
|
||||
gic_cpu_write,
|
||||
gic_cpu_write,
|
||||
gic_cpu_write
|
||||
};
|
||||
|
||||
static void gic_reset(gic_state *s)
|
||||
{
|
||||
int i;
|
||||
memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
|
||||
s->priority_mask = 0xf0;
|
||||
s->current_pending = 1023;
|
||||
s->running_irq = 1023;
|
||||
s->running_priority = 0x100;
|
||||
for (i = 0; i < 15; i++) {
|
||||
GIC_SET_ENABLED(i);
|
||||
GIC_SET_TRIGGER(i);
|
||||
}
|
||||
s->enabled = 0;
|
||||
s->cpu_enabled = 0;
|
||||
}
|
||||
|
||||
void *arm_gic_init(uint32_t base, void *parent, int parent_irq)
|
||||
{
|
||||
gic_state *s;
|
||||
int iomemtype;
|
||||
|
||||
s = (gic_state *)qemu_mallocz(sizeof(gic_state));
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->handler = gic_set_irq;
|
||||
s->parent = parent;
|
||||
s->parent_irq = parent_irq;
|
||||
if (base != 0xffffffff) {
|
||||
iomemtype = cpu_register_io_memory(0, gic_cpu_readfn,
|
||||
gic_cpu_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
|
||||
iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
|
||||
gic_dist_writefn, s);
|
||||
cpu_register_physical_memory(base + 0x1000, 0x00000fff, iomemtype);
|
||||
s->base = base;
|
||||
} else {
|
||||
s->base = 0;
|
||||
}
|
||||
gic_reset(s);
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Generic ARM Programmable Interrupt Controller support.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the LGPL
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
/* Stub functions for hardware that doesn't exist. */
|
||||
void pic_set_irq(int irq, int level)
|
||||
{
|
||||
cpu_abort(cpu_single_env, "pic_set_irq");
|
||||
}
|
||||
|
||||
void pic_info(void)
|
||||
{
|
||||
}
|
||||
|
||||
void irq_info(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void pic_set_irq_new(void *opaque, int irq, int level)
|
||||
{
|
||||
arm_pic_handler *p = (arm_pic_handler *)opaque;
|
||||
/* Call the real handler. */
|
||||
(*p)(opaque, irq, level);
|
||||
}
|
||||
|
||||
/* Model the IRQ/FIQ CPU interrupt lines as a two input interrupt controller.
|
||||
Input 0 is IRQ and input 1 is FIQ. */
|
||||
typedef struct
|
||||
{
|
||||
arm_pic_handler handler;
|
||||
CPUState *cpu_env;
|
||||
} arm_pic_cpu_state;
|
||||
|
||||
static void arm_pic_cpu_handler(void *opaque, int irq, int level)
|
||||
{
|
||||
arm_pic_cpu_state *s = (arm_pic_cpu_state *)opaque;
|
||||
switch (irq) {
|
||||
case ARM_PIC_CPU_IRQ:
|
||||
if (level)
|
||||
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
|
||||
else
|
||||
cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
|
||||
break;
|
||||
case ARM_PIC_CPU_FIQ:
|
||||
if (level)
|
||||
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
|
||||
else
|
||||
cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
|
||||
break;
|
||||
default:
|
||||
cpu_abort(s->cpu_env, "arm_pic_cpu_handler: Bad interrput line %d\n",
|
||||
irq);
|
||||
}
|
||||
}
|
||||
|
||||
void *arm_pic_init_cpu(CPUState *env)
|
||||
{
|
||||
arm_pic_cpu_state *s;
|
||||
|
||||
s = (arm_pic_cpu_state *)malloc(sizeof(arm_pic_cpu_state));
|
||||
s->handler = arm_pic_cpu_handler;
|
||||
s->cpu_env = env;
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Generic ARM Programmable Interrupt Controller support.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the LGPL.
|
||||
*
|
||||
* Arm hardware uses a wide variety of interrupt handling hardware.
|
||||
* This provides a generic framework for connecting interrupt sources and
|
||||
* inputs.
|
||||
*/
|
||||
|
||||
#ifndef ARM_INTERRUPT_H
|
||||
#define ARM_INTERRUPT_H 1
|
||||
|
||||
/* The first element of an individual PIC state structures should
|
||||
be a pointer to the handler routine. */
|
||||
typedef void (*arm_pic_handler)(void *opaque, int irq, int level);
|
||||
|
||||
/* The CPU is also modeled as an interrupt controller. */
|
||||
#define ARM_PIC_CPU_IRQ 0
|
||||
#define ARM_PIC_CPU_FIQ 1
|
||||
void *arm_pic_init_cpu(CPUState *env);
|
||||
|
||||
#endif /* !ARM_INTERRUPT_H */
|
||||
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Status and system control registers for ARM RealView/Versatile boards.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
#define LOCK_VALUE 0xa05f
|
||||
|
||||
typedef struct {
|
||||
uint32_t base;
|
||||
uint32_t sys_id;
|
||||
uint32_t leds;
|
||||
uint16_t lockval;
|
||||
uint32_t cfgdata1;
|
||||
uint32_t cfgdata2;
|
||||
uint32_t flags;
|
||||
uint32_t nvflags;
|
||||
uint32_t resetlevel;
|
||||
} arm_sysctl_state;
|
||||
|
||||
static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
arm_sysctl_state *s = (arm_sysctl_state *)opaque;
|
||||
|
||||
offset -= s->base;
|
||||
switch (offset) {
|
||||
case 0x00: /* ID */
|
||||
return s->sys_id;
|
||||
case 0x04: /* SW */
|
||||
/* General purpose hardware switches.
|
||||
We don't have a useful way of exposing these to the user. */
|
||||
return 0;
|
||||
case 0x08: /* LED */
|
||||
return s->leds;
|
||||
case 0x20: /* LOCK */
|
||||
return s->lockval;
|
||||
case 0x0c: /* OSC0 */
|
||||
case 0x10: /* OSC1 */
|
||||
case 0x14: /* OSC2 */
|
||||
case 0x18: /* OSC3 */
|
||||
case 0x1c: /* OSC4 */
|
||||
case 0x24: /* 100HZ */
|
||||
/* ??? Implement these. */
|
||||
return 0;
|
||||
case 0x28: /* CFGDATA1 */
|
||||
return s->cfgdata1;
|
||||
case 0x2c: /* CFGDATA2 */
|
||||
return s->cfgdata2;
|
||||
case 0x30: /* FLAGS */
|
||||
return s->flags;
|
||||
case 0x38: /* NVFLAGS */
|
||||
return s->nvflags;
|
||||
case 0x40: /* RESETCTL */
|
||||
return s->resetlevel;
|
||||
case 0x44: /* PCICTL */
|
||||
return 1;
|
||||
case 0x48: /* MCI */
|
||||
return 0;
|
||||
case 0x4c: /* FLASH */
|
||||
return 0;
|
||||
case 0x50: /* CLCD */
|
||||
return 0x1000;
|
||||
case 0x54: /* CLCDSER */
|
||||
return 0;
|
||||
case 0x58: /* BOOTCS */
|
||||
return 0;
|
||||
case 0x5c: /* 24MHz */
|
||||
/* ??? not implemented. */
|
||||
return 0;
|
||||
case 0x60: /* MISC */
|
||||
return 0;
|
||||
case 0x84: /* PROCID0 */
|
||||
/* ??? Don't know what the proper value for the core tile ID is. */
|
||||
return 0x02000000;
|
||||
case 0x88: /* PROCID1 */
|
||||
return 0xff000000;
|
||||
case 0x64: /* DMAPSR0 */
|
||||
case 0x68: /* DMAPSR1 */
|
||||
case 0x6c: /* DMAPSR2 */
|
||||
case 0x70: /* IOSEL */
|
||||
case 0x74: /* PLDCTL */
|
||||
case 0x80: /* BUSID */
|
||||
case 0x8c: /* OSCRESET0 */
|
||||
case 0x90: /* OSCRESET1 */
|
||||
case 0x94: /* OSCRESET2 */
|
||||
case 0x98: /* OSCRESET3 */
|
||||
case 0x9c: /* OSCRESET4 */
|
||||
case 0xc0: /* SYS_TEST_OSC0 */
|
||||
case 0xc4: /* SYS_TEST_OSC1 */
|
||||
case 0xc8: /* SYS_TEST_OSC2 */
|
||||
case 0xcc: /* SYS_TEST_OSC3 */
|
||||
case 0xd0: /* SYS_TEST_OSC4 */
|
||||
return 0;
|
||||
default:
|
||||
printf ("arm_sysctl_read: Bad register offset 0x%x\n", (int)offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_sysctl_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t val)
|
||||
{
|
||||
arm_sysctl_state *s = (arm_sysctl_state *)opaque;
|
||||
offset -= s->base;
|
||||
|
||||
switch (offset) {
|
||||
case 0x08: /* LED */
|
||||
s->leds = val;
|
||||
case 0x0c: /* OSC0 */
|
||||
case 0x10: /* OSC1 */
|
||||
case 0x14: /* OSC2 */
|
||||
case 0x18: /* OSC3 */
|
||||
case 0x1c: /* OSC4 */
|
||||
/* ??? */
|
||||
break;
|
||||
case 0x20: /* LOCK */
|
||||
if (val == LOCK_VALUE)
|
||||
s->lockval = val;
|
||||
else
|
||||
s->lockval = val & 0x7fff;
|
||||
break;
|
||||
case 0x28: /* CFGDATA1 */
|
||||
/* ??? Need to implement this. */
|
||||
s->cfgdata1 = val;
|
||||
break;
|
||||
case 0x2c: /* CFGDATA2 */
|
||||
/* ??? Need to implement this. */
|
||||
s->cfgdata2 = val;
|
||||
break;
|
||||
case 0x30: /* FLAGSSET */
|
||||
s->flags |= val;
|
||||
break;
|
||||
case 0x34: /* FLAGSCLR */
|
||||
s->flags &= ~val;
|
||||
break;
|
||||
case 0x38: /* NVFLAGSSET */
|
||||
s->nvflags |= val;
|
||||
break;
|
||||
case 0x3c: /* NVFLAGSCLR */
|
||||
s->nvflags &= ~val;
|
||||
break;
|
||||
case 0x40: /* RESETCTL */
|
||||
if (s->lockval == LOCK_VALUE) {
|
||||
s->resetlevel = val;
|
||||
if (val & 0x100)
|
||||
cpu_abort(cpu_single_env, "Board reset\n");
|
||||
}
|
||||
break;
|
||||
case 0x44: /* PCICTL */
|
||||
/* nothing to do. */
|
||||
break;
|
||||
case 0x4c: /* FLASH */
|
||||
case 0x50: /* CLCD */
|
||||
case 0x54: /* CLCDSER */
|
||||
case 0x64: /* DMAPSR0 */
|
||||
case 0x68: /* DMAPSR1 */
|
||||
case 0x6c: /* DMAPSR2 */
|
||||
case 0x70: /* IOSEL */
|
||||
case 0x74: /* PLDCTL */
|
||||
case 0x80: /* BUSID */
|
||||
case 0x84: /* PROCID0 */
|
||||
case 0x88: /* PROCID1 */
|
||||
case 0x8c: /* OSCRESET0 */
|
||||
case 0x90: /* OSCRESET1 */
|
||||
case 0x94: /* OSCRESET2 */
|
||||
case 0x98: /* OSCRESET3 */
|
||||
case 0x9c: /* OSCRESET4 */
|
||||
break;
|
||||
default:
|
||||
printf ("arm_sysctl_write: Bad register offset 0x%x\n", (int)offset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *arm_sysctl_readfn[] = {
|
||||
arm_sysctl_read,
|
||||
arm_sysctl_read,
|
||||
arm_sysctl_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *arm_sysctl_writefn[] = {
|
||||
arm_sysctl_write,
|
||||
arm_sysctl_write,
|
||||
arm_sysctl_write
|
||||
};
|
||||
|
||||
void arm_sysctl_init(uint32_t base, uint32_t sys_id)
|
||||
{
|
||||
arm_sysctl_state *s;
|
||||
int iomemtype;
|
||||
|
||||
s = (arm_sysctl_state *)qemu_mallocz(sizeof(arm_sysctl_state));
|
||||
if (!s)
|
||||
return;
|
||||
s->base = base;
|
||||
s->sys_id = sys_id;
|
||||
iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn,
|
||||
arm_sysctl_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* ARM PrimeCell Timer modules.
|
||||
*
|
||||
* Copyright (c) 2005-2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
/* Common timer implementation. */
|
||||
|
||||
#define TIMER_CTRL_ONESHOT (1 << 0)
|
||||
#define TIMER_CTRL_32BIT (1 << 1)
|
||||
#define TIMER_CTRL_DIV1 (0 << 2)
|
||||
#define TIMER_CTRL_DIV16 (1 << 2)
|
||||
#define TIMER_CTRL_DIV256 (2 << 2)
|
||||
#define TIMER_CTRL_IE (1 << 5)
|
||||
#define TIMER_CTRL_PERIODIC (1 << 6)
|
||||
#define TIMER_CTRL_ENABLE (1 << 7)
|
||||
|
||||
typedef struct {
|
||||
int64_t next_time;
|
||||
int64_t expires;
|
||||
int64_t loaded;
|
||||
QEMUTimer *timer;
|
||||
uint32_t control;
|
||||
uint32_t count;
|
||||
uint32_t limit;
|
||||
int raw_freq;
|
||||
int freq;
|
||||
int int_level;
|
||||
void *pic;
|
||||
int irq;
|
||||
} arm_timer_state;
|
||||
|
||||
/* Calculate the new expiry time of the given timer. */
|
||||
|
||||
static void arm_timer_reload(arm_timer_state *s)
|
||||
{
|
||||
int64_t delay;
|
||||
|
||||
s->loaded = s->expires;
|
||||
delay = muldiv64(s->count, ticks_per_sec, s->freq);
|
||||
if (delay == 0)
|
||||
delay = 1;
|
||||
s->expires += delay;
|
||||
}
|
||||
|
||||
/* Check all active timers, and schedule the next timer interrupt. */
|
||||
|
||||
static void arm_timer_update(arm_timer_state *s, int64_t now)
|
||||
{
|
||||
int64_t next;
|
||||
|
||||
/* Ignore disabled timers. */
|
||||
if ((s->control & TIMER_CTRL_ENABLE) == 0)
|
||||
return;
|
||||
/* Ignore expired one-shot timers. */
|
||||
if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT))
|
||||
return;
|
||||
if (s->expires - now <= 0) {
|
||||
/* Timer has expired. */
|
||||
s->int_level = 1;
|
||||
if (s->control & TIMER_CTRL_ONESHOT) {
|
||||
/* One-shot. */
|
||||
s->count = 0;
|
||||
} else {
|
||||
if ((s->control & TIMER_CTRL_PERIODIC) == 0) {
|
||||
/* Free running. */
|
||||
if (s->control & TIMER_CTRL_32BIT)
|
||||
s->count = 0xffffffff;
|
||||
else
|
||||
s->count = 0xffff;
|
||||
} else {
|
||||
/* Periodic. */
|
||||
s->count = s->limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (s->expires - now <= 0) {
|
||||
arm_timer_reload(s);
|
||||
}
|
||||
/* Update interrupts. */
|
||||
if (s->int_level && (s->control & TIMER_CTRL_IE)) {
|
||||
pic_set_irq_new(s->pic, s->irq, 1);
|
||||
} else {
|
||||
pic_set_irq_new(s->pic, s->irq, 0);
|
||||
}
|
||||
|
||||
next = now;
|
||||
if (next - s->expires < 0)
|
||||
next = s->expires;
|
||||
|
||||
/* Schedule the next timer interrupt. */
|
||||
if (next == now) {
|
||||
qemu_del_timer(s->timer);
|
||||
s->next_time = 0;
|
||||
} else if (next != s->next_time) {
|
||||
qemu_mod_timer(s->timer, next);
|
||||
s->next_time = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the current value of the timer. */
|
||||
static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now)
|
||||
{
|
||||
int64_t left;
|
||||
int64_t period;
|
||||
|
||||
if (s->count == 0)
|
||||
return 0;
|
||||
if ((s->control & TIMER_CTRL_ENABLE) == 0)
|
||||
return s->count;
|
||||
left = s->expires - now;
|
||||
period = s->expires - s->loaded;
|
||||
/* If the timer should have expired then return 0. This can happen
|
||||
when the host timer signal doesnt occur immediately. It's better to
|
||||
have a timer appear to sit at zero for a while than have it wrap
|
||||
around before the guest interrupt is raised. */
|
||||
/* ??? Could we trigger the interrupt here? */
|
||||
if (left < 0)
|
||||
return 0;
|
||||
/* We need to calculate count * elapsed / period without overfowing.
|
||||
Scale both elapsed and period so they fit in a 32-bit int. */
|
||||
while (period != (int32_t)period) {
|
||||
period >>= 1;
|
||||
left >>= 1;
|
||||
}
|
||||
return ((uint64_t)s->count * (uint64_t)(int32_t)left)
|
||||
/ (int32_t)period;
|
||||
}
|
||||
|
||||
uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
arm_timer_state *s = (arm_timer_state *)opaque;
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 0: /* TimerLoad */
|
||||
case 6: /* TimerBGLoad */
|
||||
return s->limit;
|
||||
case 1: /* TimerValue */
|
||||
return arm_timer_getcount(s, qemu_get_clock(vm_clock));
|
||||
case 2: /* TimerControl */
|
||||
return s->control;
|
||||
case 4: /* TimerRIS */
|
||||
return s->int_level;
|
||||
case 5: /* TimerMIS */
|
||||
if ((s->control & TIMER_CTRL_IE) == 0)
|
||||
return 0;
|
||||
return s->int_level;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_timer_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
arm_timer_state *s = (arm_timer_state *)opaque;
|
||||
int64_t now;
|
||||
|
||||
now = qemu_get_clock(vm_clock);
|
||||
switch (offset >> 2) {
|
||||
case 0: /* TimerLoad */
|
||||
s->limit = value;
|
||||
s->count = value;
|
||||
s->expires = now;
|
||||
arm_timer_reload(s);
|
||||
break;
|
||||
case 1: /* TimerValue */
|
||||
/* ??? Linux seems to want to write to this readonly register.
|
||||
Ignore it. */
|
||||
break;
|
||||
case 2: /* TimerControl */
|
||||
if (s->control & TIMER_CTRL_ENABLE) {
|
||||
/* Pause the timer if it is running. This may cause some
|
||||
inaccuracy dure to rounding, but avoids a whole lot of other
|
||||
messyness. */
|
||||
s->count = arm_timer_getcount(s, now);
|
||||
}
|
||||
s->control = value;
|
||||
s->freq = s->raw_freq;
|
||||
/* ??? Need to recalculate expiry time after changing divisor. */
|
||||
switch ((value >> 2) & 3) {
|
||||
case 1: s->freq >>= 4; break;
|
||||
case 2: s->freq >>= 8; break;
|
||||
}
|
||||
if (s->control & TIMER_CTRL_ENABLE) {
|
||||
/* Restart the timer if still enabled. */
|
||||
s->expires = now;
|
||||
arm_timer_reload(s);
|
||||
}
|
||||
break;
|
||||
case 3: /* TimerIntClr */
|
||||
s->int_level = 0;
|
||||
break;
|
||||
case 6: /* TimerBGLoad */
|
||||
s->limit = value;
|
||||
break;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset);
|
||||
}
|
||||
arm_timer_update(s, now);
|
||||
}
|
||||
|
||||
static void arm_timer_tick(void *opaque)
|
||||
{
|
||||
int64_t now;
|
||||
|
||||
now = qemu_get_clock(vm_clock);
|
||||
arm_timer_update((arm_timer_state *)opaque, now);
|
||||
}
|
||||
|
||||
static void *arm_timer_init(uint32_t freq, void *pic, int irq)
|
||||
{
|
||||
arm_timer_state *s;
|
||||
|
||||
s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
|
||||
s->pic = pic;
|
||||
s->irq = irq;
|
||||
s->raw_freq = s->freq = 1000000;
|
||||
s->control = TIMER_CTRL_IE;
|
||||
s->count = 0xffffffff;
|
||||
|
||||
s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s);
|
||||
/* ??? Save/restore. */
|
||||
return s;
|
||||
}
|
||||
|
||||
/* ARM PrimeCell SP804 dual timer module.
|
||||
Docs for this device don't seem to be publicly available. This
|
||||
implementation is based on gueswork, the linux kernel sources and the
|
||||
Integrator/CP timer modules. */
|
||||
|
||||
typedef struct {
|
||||
/* Include a pseudo-PIC device to merge the two interrupt sources. */
|
||||
arm_pic_handler handler;
|
||||
void *timer[2];
|
||||
int level[2];
|
||||
uint32_t base;
|
||||
/* The output PIC device. */
|
||||
void *pic;
|
||||
int irq;
|
||||
} sp804_state;
|
||||
|
||||
static void sp804_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
sp804_state *s = (sp804_state *)opaque;
|
||||
|
||||
s->level[irq] = level;
|
||||
pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]);
|
||||
}
|
||||
|
||||
static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
sp804_state *s = (sp804_state *)opaque;
|
||||
|
||||
/* ??? Don't know the PrimeCell ID for this device. */
|
||||
offset -= s->base;
|
||||
if (offset < 0x20) {
|
||||
return arm_timer_read(s->timer[0], offset);
|
||||
} else {
|
||||
return arm_timer_read(s->timer[1], offset - 0x20);
|
||||
}
|
||||
}
|
||||
|
||||
static void sp804_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
sp804_state *s = (sp804_state *)opaque;
|
||||
|
||||
offset -= s->base;
|
||||
if (offset < 0x20) {
|
||||
arm_timer_write(s->timer[0], offset, value);
|
||||
} else {
|
||||
arm_timer_write(s->timer[1], offset - 0x20, value);
|
||||
}
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *sp804_readfn[] = {
|
||||
sp804_read,
|
||||
sp804_read,
|
||||
sp804_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *sp804_writefn[] = {
|
||||
sp804_write,
|
||||
sp804_write,
|
||||
sp804_write
|
||||
};
|
||||
|
||||
void sp804_init(uint32_t base, void *pic, int irq)
|
||||
{
|
||||
int iomemtype;
|
||||
sp804_state *s;
|
||||
|
||||
s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
|
||||
s->handler = sp804_set_irq;
|
||||
s->base = base;
|
||||
s->pic = pic;
|
||||
s->irq = irq;
|
||||
/* ??? The timers are actually configurable between 32kHz and 1MHz, but
|
||||
we don't implement that. */
|
||||
s->timer[0] = arm_timer_init(1000000, s, 0);
|
||||
s->timer[1] = arm_timer_init(1000000, s, 1);
|
||||
iomemtype = cpu_register_io_memory(0, sp804_readfn,
|
||||
sp804_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
|
||||
/* Integrator/CP timer module. */
|
||||
|
||||
typedef struct {
|
||||
void *timer[3];
|
||||
uint32_t base;
|
||||
} icp_pit_state;
|
||||
|
||||
static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
icp_pit_state *s = (icp_pit_state *)opaque;
|
||||
int n;
|
||||
|
||||
/* ??? Don't know the PrimeCell ID for this device. */
|
||||
offset -= s->base;
|
||||
n = offset >> 8;
|
||||
if (n > 3)
|
||||
cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n);
|
||||
|
||||
return arm_timer_read(s->timer[n], offset & 0xff);
|
||||
}
|
||||
|
||||
static void icp_pit_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
icp_pit_state *s = (icp_pit_state *)opaque;
|
||||
int n;
|
||||
|
||||
offset -= s->base;
|
||||
n = offset >> 8;
|
||||
if (n > 3)
|
||||
cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n);
|
||||
|
||||
arm_timer_write(s->timer[n], offset & 0xff, value);
|
||||
}
|
||||
|
||||
|
||||
static CPUReadMemoryFunc *icp_pit_readfn[] = {
|
||||
icp_pit_read,
|
||||
icp_pit_read,
|
||||
icp_pit_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *icp_pit_writefn[] = {
|
||||
icp_pit_write,
|
||||
icp_pit_write,
|
||||
icp_pit_write
|
||||
};
|
||||
|
||||
void icp_pit_init(uint32_t base, void *pic, int irq)
|
||||
{
|
||||
int iomemtype;
|
||||
icp_pit_state *s;
|
||||
|
||||
s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
|
||||
s->base = base;
|
||||
/* Timer 0 runs at the system clock speed (40MHz). */
|
||||
s->timer[0] = arm_timer_init(40000000, pic, irq);
|
||||
/* The other two timers run at 1MHz. */
|
||||
s->timer[1] = arm_timer_init(1000000, pic, irq + 1);
|
||||
s->timer[2] = arm_timer_init(1000000, pic, irq + 2);
|
||||
|
||||
iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
|
||||
icp_pit_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00000fff, iomemtype);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* QEMU ATAPI CD-ROM Emulator
|
||||
*
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved
|
||||
here. */
|
||||
|
||||
#include <vl.h>
|
||||
|
||||
static void lba_to_msf(uint8_t *buf, int lba)
|
||||
{
|
||||
lba += 150;
|
||||
buf[0] = (lba / 75) / 60;
|
||||
buf[1] = (lba / 75) % 60;
|
||||
buf[2] = lba % 75;
|
||||
}
|
||||
|
||||
/* same toc as bochs. Return -1 if error or the toc length */
|
||||
/* XXX: check this */
|
||||
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
|
||||
{
|
||||
uint8_t *q;
|
||||
int len;
|
||||
|
||||
if (start_track > 1 && start_track != 0xaa)
|
||||
return -1;
|
||||
q = buf + 2;
|
||||
*q++ = 1; /* first session */
|
||||
*q++ = 1; /* last session */
|
||||
if (start_track <= 1) {
|
||||
*q++ = 0; /* reserved */
|
||||
*q++ = 0x14; /* ADR, control */
|
||||
*q++ = 1; /* track number */
|
||||
*q++ = 0; /* reserved */
|
||||
if (msf) {
|
||||
*q++ = 0; /* reserved */
|
||||
lba_to_msf(q, 0);
|
||||
q += 3;
|
||||
} else {
|
||||
/* sector 0 */
|
||||
cpu_to_be32wu((uint32_t *)q, 0);
|
||||
q += 4;
|
||||
}
|
||||
}
|
||||
/* lead out track */
|
||||
*q++ = 0; /* reserved */
|
||||
*q++ = 0x16; /* ADR, control */
|
||||
*q++ = 0xaa; /* track number */
|
||||
*q++ = 0; /* reserved */
|
||||
if (msf) {
|
||||
*q++ = 0; /* reserved */
|
||||
lba_to_msf(q, nb_sectors);
|
||||
q += 3;
|
||||
} else {
|
||||
cpu_to_be32wu((uint32_t *)q, nb_sectors);
|
||||
q += 4;
|
||||
}
|
||||
len = q - buf;
|
||||
cpu_to_be16wu((uint16_t *)buf, len - 2);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* mostly same info as PearPc */
|
||||
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
|
||||
{
|
||||
uint8_t *q;
|
||||
int len;
|
||||
|
||||
q = buf + 2;
|
||||
*q++ = 1; /* first session */
|
||||
*q++ = 1; /* last session */
|
||||
|
||||
*q++ = 1; /* session number */
|
||||
*q++ = 0x14; /* data track */
|
||||
*q++ = 0; /* track number */
|
||||
*q++ = 0xa0; /* lead-in */
|
||||
*q++ = 0; /* min */
|
||||
*q++ = 0; /* sec */
|
||||
*q++ = 0; /* frame */
|
||||
*q++ = 0;
|
||||
*q++ = 1; /* first track */
|
||||
*q++ = 0x00; /* disk type */
|
||||
*q++ = 0x00;
|
||||
|
||||
*q++ = 1; /* session number */
|
||||
*q++ = 0x14; /* data track */
|
||||
*q++ = 0; /* track number */
|
||||
*q++ = 0xa1;
|
||||
*q++ = 0; /* min */
|
||||
*q++ = 0; /* sec */
|
||||
*q++ = 0; /* frame */
|
||||
*q++ = 0;
|
||||
*q++ = 1; /* last track */
|
||||
*q++ = 0x00;
|
||||
*q++ = 0x00;
|
||||
|
||||
*q++ = 1; /* session number */
|
||||
*q++ = 0x14; /* data track */
|
||||
*q++ = 0; /* track number */
|
||||
*q++ = 0xa2; /* lead-out */
|
||||
*q++ = 0; /* min */
|
||||
*q++ = 0; /* sec */
|
||||
*q++ = 0; /* frame */
|
||||
if (msf) {
|
||||
*q++ = 0; /* reserved */
|
||||
lba_to_msf(q, nb_sectors);
|
||||
q += 3;
|
||||
} else {
|
||||
cpu_to_be32wu((uint32_t *)q, nb_sectors);
|
||||
q += 4;
|
||||
}
|
||||
|
||||
*q++ = 1; /* session number */
|
||||
*q++ = 0x14; /* ADR, control */
|
||||
*q++ = 0; /* track number */
|
||||
*q++ = 1; /* point */
|
||||
*q++ = 0; /* min */
|
||||
*q++ = 0; /* sec */
|
||||
*q++ = 0; /* frame */
|
||||
if (msf) {
|
||||
*q++ = 0;
|
||||
lba_to_msf(q, 0);
|
||||
q += 3;
|
||||
} else {
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
*q++ = 0;
|
||||
}
|
||||
|
||||
len = q - buf;
|
||||
cpu_to_be16wu((uint16_t *)buf, len - 2);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user