Compare commits
865
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 |
No files matched your search
+28
-6
@@ -1,23 +1,45 @@
|
||||
arm-user
|
||||
armeb-user
|
||||
arm-linux-user
|
||||
arm-softmmu
|
||||
armeb-linux-user
|
||||
config-host.*
|
||||
dyngen
|
||||
i386
|
||||
i386-softmmu
|
||||
i386-user
|
||||
i386-darwin-user
|
||||
i386-linux-user
|
||||
ppc-softmmu
|
||||
ppc64-softmmu
|
||||
ppc-user
|
||||
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-user
|
||||
sparc-linux-user
|
||||
qemu-img
|
||||
sparc-softmmu
|
||||
x86_64-softmmu
|
||||
sparc64-user
|
||||
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
|
||||
@@ -1,3 +1,83 @@
|
||||
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)
|
||||
@@ -8,6 +88,7 @@ version 0.7.1:
|
||||
- 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:
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
The following points clarify the QEMU licenses:
|
||||
The following points clarify the QEMU license:
|
||||
|
||||
1) The QEMU virtual CPU core library (libqemu.a) and the QEMU PC
|
||||
system emulator are released under the GNU Lesser General Public
|
||||
License.
|
||||
1) QEMU as a whole is released under the GNU General Public License
|
||||
|
||||
2) The Linux user mode QEMU emulator 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.
|
||||
|
||||
|
||||
@@ -1,35 +1,49 @@
|
||||
-include config-host.mak
|
||||
# Makefile for QEMU.
|
||||
|
||||
CFLAGS=-Wall -O2 -g -fno-strict-aliasing
|
||||
ifdef CONFIG_DARWIN
|
||||
CFLAGS+= -mdynamic-no-pic
|
||||
include config-host.mak
|
||||
|
||||
.PHONY: all clean distclean dvi info install install-doc tar tarbin \
|
||||
speed test test2 html dvi info
|
||||
|
||||
BASE_CFLAGS=
|
||||
BASE_LDFLAGS=
|
||||
|
||||
BASE_CFLAGS += $(OS_CFLAGS)
|
||||
ifeq ($(ARCH),sparc)
|
||||
BASE_CFLAGS += -mcpu=ultrasparc
|
||||
endif
|
||||
LDFLAGS=-g
|
||||
CPPFLAGS += -I. -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
LIBS=
|
||||
DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
TOOLS=qemu-img$(EXESUF)
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-static
|
||||
BASE_LDFLAGS += -static
|
||||
endif
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1
|
||||
|
||||
all: dyngen$(EXESUF) $(TOOLS) $(DOCS)
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
ifdef CONFIG_KQEMU
|
||||
ifdef CONFIG_WIN32
|
||||
$(MAKE) -C kqemu -f Makefile.winnt
|
||||
else
|
||||
$(MAKE) -C kqemu
|
||||
DOCS=
|
||||
endif
|
||||
|
||||
ifndef CONFIG_DARWIN
|
||||
ifndef CONFIG_WIN32
|
||||
ifndef CONFIG_SOLARIS
|
||||
LIBS+=-lrt
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.c block.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
|
||||
$(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS)
|
||||
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) $(DEFINES) -o $@ $^
|
||||
$(HOST_CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
@@ -39,12 +53,10 @@ clean:
|
||||
for d in $(TARGET_DIRS); do \
|
||||
$(MAKE) -C $$d $@ || exit 1 ; \
|
||||
done
|
||||
ifdef CONFIG_KQEMU
|
||||
$(MAKE) -C kqemu clean
|
||||
endif
|
||||
|
||||
distclean: clean
|
||||
rm -f config-host.mak config-host.h
|
||||
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
|
||||
@@ -53,29 +65,32 @@ 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
|
||||
|
||||
install: all
|
||||
mkdir -p "$(bindir)"
|
||||
install -m 755 -s $(TOOLS) "$(bindir)"
|
||||
mkdir -p "$(datadir)"
|
||||
install -m 644 pc-bios/bios.bin pc-bios/vgabios.bin \
|
||||
pc-bios/vgabios-cirrus.bin \
|
||||
pc-bios/ppc_rom.bin pc-bios/video.x \
|
||||
pc-bios/proll.elf \
|
||||
pc-bios/linux_boot.bin "$(datadir)"
|
||||
mkdir -p "$(docdir)"
|
||||
install -m 644 qemu-doc.html qemu-tech.html "$(docdir)"
|
||||
install-doc: $(DOCS)
|
||||
mkdir -p "$(DESTDIR)$(docdir)"
|
||||
$(INSTALL) -m 644 qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)"
|
||||
ifndef CONFIG_WIN32
|
||||
mkdir -p "$(mandir)/man1"
|
||||
install qemu.1 qemu-img.1 "$(mandir)/man1"
|
||||
mkdir -p "$(datadir)/keymaps"
|
||||
install -m 644 $(addprefix keymaps/,$(KEYMAPS)) "$(datadir)/keymaps"
|
||||
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
|
||||
ifdef CONFIG_KQEMU
|
||||
cd kqemu ; ./install.sh
|
||||
endif
|
||||
|
||||
# various test targets
|
||||
test speed test2: all
|
||||
@@ -84,19 +99,37 @@ test speed test2: all
|
||||
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
|
||||
./texi2pod.pl $< qemu.pod
|
||||
$(SRC_PATH)/texi2pod.pl $< qemu.pod
|
||||
pod2man --section=1 --center=" " --release=" " qemu.pod > $@
|
||||
|
||||
qemu-img.1: qemu-img.texi
|
||||
./texi2pod.pl $< qemu-img.pod
|
||||
$(SRC_PATH)/texi2pod.pl $< qemu-img.pod
|
||||
pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@
|
||||
|
||||
FILE=qemu-$(shell cat VERSION)
|
||||
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:
|
||||
@@ -113,18 +146,26 @@ tarbin:
|
||||
$(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)/proll.elf \
|
||||
$(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 )
|
||||
|
||||
+246
-91
@@ -12,33 +12,45 @@ TARGET_BASE_ARCH:=sparc
|
||||
endif
|
||||
TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
|
||||
VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio
|
||||
DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH)
|
||||
ifdef CONFIG_USER_ONLY
|
||||
VPATH+=:$(SRC_PATH)/linux-user
|
||||
DEFINES+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH)
|
||||
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
|
||||
CFLAGS=-Wall -O2 -g -fno-strict-aliasing
|
||||
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
|
||||
LDFLAGS=-g
|
||||
LIBS=
|
||||
HELPER_CFLAGS=$(CFLAGS)
|
||||
DYNGEN=../dyngen$(EXESUF)
|
||||
# user emulator name
|
||||
TARGET_ARCH2=$(TARGET_ARCH)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
ifeq ($(TARGET_WORDS_BIGENDIAN),yes)
|
||||
QEMU_USER=qemu-armeb
|
||||
else
|
||||
QEMU_USER=qemu-arm
|
||||
TARGET_ARCH2=armeb
|
||||
endif
|
||||
else
|
||||
QEMU_USER=qemu-$(TARGET_ARCH)
|
||||
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_ARCH)$(EXESUF)
|
||||
QEMU_SYSTEM=qemu-system-$(TARGET_ARCH2)$(EXESUF)
|
||||
endif
|
||||
else
|
||||
QEMU_SYSTEM=qemu-fast
|
||||
@@ -54,18 +66,20 @@ endif
|
||||
endif # !CONFIG_USER_ONLY
|
||||
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-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)
|
||||
CFLAGS+=-fomit-frame-pointer
|
||||
OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2
|
||||
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
|
||||
@@ -73,69 +87,80 @@ ifdef CONFIG_STATIC
|
||||
USE_I386_LD=y
|
||||
endif
|
||||
ifdef USE_I386_LD
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/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!
|
||||
LDFLAGS+=-Wl,-shared
|
||||
BASE_LDFLAGS+=-Wl,-shared
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),x86_64)
|
||||
OP_CFLAGS=$(CFLAGS) -falign-functions=0
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/x86_64.ld
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc)
|
||||
CFLAGS+= -D__powerpc__
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/ppc.ld
|
||||
CPPFLAGS+= -D__powerpc__
|
||||
ifdef CONFIG_LINUX_USER
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),s390)
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/s390.ld
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc)
|
||||
CFLAGS+=-m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
|
||||
LDFLAGS+=-m32
|
||||
OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
|
||||
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
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc.ld -static
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld -static
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),sparc64)
|
||||
CFLAGS+=-m64 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6
|
||||
LDFLAGS+=-m64
|
||||
OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0
|
||||
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 because we want two-instruction relocations
|
||||
# for the constant constructions
|
||||
OP_CFLAGS=-Wall -O2 -g
|
||||
# -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
|
||||
CFLAGS += -msmall-data
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/alpha.ld
|
||||
BASE_CFLAGS+=-msmall-data
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
CFLAGS += -mno-sdata
|
||||
OP_CFLAGS=$(CFLAGS)
|
||||
LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
|
||||
BASE_CFLAGS+=-mno-sdata
|
||||
OP_CFLAGS+=-mno-sdata
|
||||
BASE_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),arm)
|
||||
OP_CFLAGS=$(CFLAGS) -mno-sched-prolog -fno-omit-frame-pointer
|
||||
LDFLAGS+=-Wl,-T,$(SRC_PATH)/arm.ld
|
||||
OP_CFLAGS+=-mno-sched-prolog -fno-omit-frame-pointer
|
||||
BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),m68k)
|
||||
OP_CFLAGS=$(CFLAGS) -fomit-frame-pointer
|
||||
LDFLAGS+=-Wl,-T,m68k.ld
|
||||
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)
|
||||
@@ -144,13 +169,19 @@ OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DARWIN),yes)
|
||||
OP_CFLAGS+= -mdynamic-no-pic
|
||||
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)
|
||||
|
||||
#########################################################
|
||||
|
||||
DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
CPPFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
LIBS+=-lm
|
||||
ifndef CONFIG_USER_ONLY
|
||||
LIBS+=-lz
|
||||
@@ -158,14 +189,23 @@ endif
|
||||
ifdef CONFIG_WIN32
|
||||
LIBS+=-lwinmm -lws2_32 -liphlpapi
|
||||
endif
|
||||
ifdef CONFIG_SOLARIS
|
||||
LIBS+=-lsocket -lnsl -lresolv
|
||||
endif
|
||||
|
||||
# profiling code
|
||||
ifdef TARGET_GPROF
|
||||
LDFLAGS+=-p
|
||||
main.o: CFLAGS+=-p
|
||||
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
|
||||
|
||||
OBJS= elfload.o main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
OBJS+= vm86.o
|
||||
endif
|
||||
@@ -174,6 +214,15 @@ 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
|
||||
|
||||
@@ -185,7 +234,7 @@ LIBOBJS+=fpu/softfloat.o
|
||||
else
|
||||
LIBOBJS+=fpu/softfloat-native.o
|
||||
endif
|
||||
DEFINES+=-I$(SRC_PATH)/fpu
|
||||
CPPFLAGS+=-I$(SRC_PATH)/fpu
|
||||
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
LIBOBJS+=helper.o helper2.o
|
||||
@@ -211,7 +260,15 @@ LIBOBJS+= op_helper.o helper.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), arm)
|
||||
LIBOBJS+= op_helper.o
|
||||
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
|
||||
@@ -240,10 +297,13 @@ endif
|
||||
ifeq ($(findstring arm, $(TARGET_ARCH) $(ARCH)),arm)
|
||||
LIBOBJS+=arm-dis.o
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ia64)
|
||||
OBJS += ia64-syscall.o
|
||||
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
|
||||
@@ -251,7 +311,7 @@ endif
|
||||
all: $(PROGS)
|
||||
|
||||
$(QEMU_USER): $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
$(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)
|
||||
@@ -259,10 +319,15 @@ ifeq ($(ARCH),alpha)
|
||||
endif
|
||||
|
||||
# must use static linking to avoid leaving stuff in virtual address space
|
||||
VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.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
|
||||
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
|
||||
SOUND_HW = sb16.o es1370.o
|
||||
AUDIODRV = audio.o noaudio.o wavaudio.o
|
||||
ifdef CONFIG_SDL
|
||||
AUDIODRV += sdlaudio.o
|
||||
@@ -270,56 +335,94 @@ endif
|
||||
ifdef CONFIG_OSS
|
||||
AUDIODRV += ossaudio.o
|
||||
endif
|
||||
|
||||
pc.o: DEFINES := -DUSE_SB16 $(DEFINES)
|
||||
|
||||
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
|
||||
|
||||
ifdef CONFIG_FMOD
|
||||
AUDIODRV += fmodaudio.o
|
||||
audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES)
|
||||
LIBS += $(CONFIG_FMOD_LIB)
|
||||
endif
|
||||
# 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 ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
|
||||
VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o
|
||||
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 ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||
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 dma.o vga.o serial.o ne2000.o i8259.o
|
||||
#VL_OBJS+= #ide.o pckbd.o i8254.o fdc.o m48t59.o
|
||||
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 ne2000.o pckbd.o vga.o
|
||||
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
|
||||
VL_OBJS+= magic-load.o
|
||||
else
|
||||
VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t08.o magic-load.o slavio_intctl.o slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o
|
||||
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
|
||||
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
|
||||
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
|
||||
ifdef CONFIG_COREAUDIO
|
||||
COCOA_LIBS+=-framework CoreAudio
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_SLIRP
|
||||
DEFINES+=-I$(SRC_PATH)/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
|
||||
@@ -336,11 +439,13 @@ VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld
|
||||
endif
|
||||
ifndef CONFIG_DARWIN
|
||||
ifndef CONFIG_WIN32
|
||||
VL_LIBS=-lutil
|
||||
ifndef CONFIG_SOLARIS
|
||||
VL_LIBS=-lutil -lrt
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifdef TARGET_GPROF
|
||||
vl.o: CFLAGS+=-p
|
||||
vl.o: BASE_CFLAGS+=-p
|
||||
VL_LDFLAGS+=-p
|
||||
endif
|
||||
|
||||
@@ -348,20 +453,35 @@ 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) $(DEFINES) -c -o $@ $<
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
sdl.o: sdl.c keymaps.c sdl_keysym.h
|
||||
$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
|
||||
$(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) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
|
||||
$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend
|
||||
|
||||
vldepend: $(VL_OBJS:.o=.c)
|
||||
$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend
|
||||
|
||||
# libqemu
|
||||
|
||||
@@ -385,10 +505,26 @@ gen-op.h: op.o $(DYNGEN)
|
||||
$(DYNGEN) -g -o $@ $<
|
||||
|
||||
op.o: op.c
|
||||
$(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
$(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) $(DEFINES) -c -o $@ $<
|
||||
$(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
|
||||
@@ -396,6 +532,7 @@ endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), arm)
|
||||
op.o: op.c op_template.h
|
||||
pl110.o: pl110_template.h
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||
@@ -410,26 +547,44 @@ translate.o: translate.c translate_init.c
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH), mips)
|
||||
op.o: op.c op_template.c op_mem.c
|
||||
op.o: op.c op_template.c fop_template.c op_mem.c
|
||||
op_helper.o: op_helper_mem.c
|
||||
endif
|
||||
|
||||
mixeng.o: mixeng.c mixeng.h mixeng_template.h
|
||||
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) $(DEFINES) -c -o $@ $<
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.S
|
||||
$(CC) $(DEFINES) -c -o $@ $<
|
||||
$(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) "$(bindir)"
|
||||
$(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,16 +0,0 @@
|
||||
Information about the various packages used to build the current qemu
|
||||
x86 binary distribution:
|
||||
|
||||
* gcc 2.95.2 was used for the build. A glibc 2.1.3 Debian distribution
|
||||
was used to get most of the binary packages.
|
||||
|
||||
* wine-20020411 tarball
|
||||
|
||||
./configure --prefix=/usr/local/wine-i386
|
||||
|
||||
All exe and libs were stripped. Some compile time tools and the
|
||||
includes were deleted.
|
||||
|
||||
* ldconfig was launched to build the library links:
|
||||
|
||||
qemu-i386 /usr/gnemul/qemu-i386/bin/ldconfig-i386 -C /usr/gnemul/qemu-i386/etc/ld.so.cache
|
||||
@@ -1,20 +1,20 @@
|
||||
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
|
||||
- merge Solaris patch
|
||||
- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?)
|
||||
- config file (at least for windows/Mac OS X)
|
||||
- commit message if execution of code in IO memory
|
||||
- update doc: PCI infos.
|
||||
- VNC patch + Synaptic patch.
|
||||
- basic VGA optimizations
|
||||
- physical memory cache (reduce qemu-fast address space size to about 32 MB)
|
||||
- better code fetch (different exception handling + CS.limit support)
|
||||
- do not resize vga if invalid size.
|
||||
- avoid looping if only exceptions
|
||||
- cycle counter for all archs
|
||||
- TLB code protection support for PPC
|
||||
- see openMosix Doc
|
||||
- disable SMC handling for ARM/SPARC/PPC (not finished)
|
||||
@@ -27,31 +27,29 @@ short term:
|
||||
- fix CCOP optimisation
|
||||
- fix all remaining thread lock issues (must put TBs in a specific invalid
|
||||
state, find a solution for tb_flush()).
|
||||
- fix arm fpu rounding (at least for float->integer conversions)
|
||||
- SMP support
|
||||
|
||||
ppc specific:
|
||||
------------
|
||||
- TLB invalidate not needed if msr_pr changes
|
||||
- SPR_ENCODE() not useful
|
||||
- enable shift optimizations ?
|
||||
|
||||
lower priority:
|
||||
--------------
|
||||
- more friendly BIOS (logo)
|
||||
- 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)
|
||||
linux-user specific:
|
||||
-------------------
|
||||
- add IPC syscalls
|
||||
- use -msoft-float on ARM
|
||||
- use kernel traps for unaligned accesses on ARM ?
|
||||
- handle rare page fault cases (in particular if page fault in helpers or
|
||||
in syscall emulation code).
|
||||
- fix thread stack freeing (use kernel 2.5.x CLONE_CHILD_CLEARTID)
|
||||
- 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.
|
||||
- use gcc as a backend to generate better code (easy to do by using
|
||||
op-i386.c operations as local inline functions).
|
||||
- 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
|
||||
@@ -23,9 +23,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
#include <stdio.h>
|
||||
#include "dis-asm.h"
|
||||
|
||||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||
#define _(x) x
|
||||
|
||||
/* The opcode table is an array of struct alpha_opcode. */
|
||||
|
||||
struct alpha_opcode
|
||||
|
||||
+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();
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,10 @@ SECTIONS
|
||||
.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. */
|
||||
@@ -63,7 +67,28 @@ SECTIONS
|
||||
*(.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)
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
+1628
-668
File diff suppressed because it is too large.
Load diff
+128
-24
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -24,31 +24,121 @@
|
||||
#ifndef QEMU_AUDIO_H
|
||||
#define QEMU_AUDIO_H
|
||||
|
||||
#include "mixeng.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
|
||||
AUD_FMT_U8,
|
||||
AUD_FMT_S8,
|
||||
AUD_FMT_U16,
|
||||
AUD_FMT_S16
|
||||
} audfmt_e;
|
||||
|
||||
typedef struct SWVoice SWVoice;
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define AUDIO_HOST_ENDIANNESS 1
|
||||
#else
|
||||
#define AUDIO_HOST_ENDIANNESS 0
|
||||
#endif
|
||||
|
||||
SWVoice * AUD_open (SWVoice *sw, const char *name, int freq,
|
||||
int nchannels, audfmt_e fmt);
|
||||
void AUD_init (void);
|
||||
void AUD_log (const char *cap, const char *fmt, ...)
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)));;
|
||||
void AUD_close (SWVoice *sw);
|
||||
int AUD_write (SWVoice *sw, void *pcm_buf, int size);
|
||||
void AUD_adjust (SWVoice *sw, int leftover);
|
||||
void AUD_reset (SWVoice *sw);
|
||||
int AUD_get_free (SWVoice *sw);
|
||||
int AUD_get_buffer_size (SWVoice *sw);
|
||||
void AUD_run (void);
|
||||
void AUD_enable (SWVoice *sw, int on);
|
||||
int AUD_calc_elapsed (SWVoice *sw);
|
||||
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)
|
||||
{
|
||||
@@ -57,9 +147,23 @@ static inline void *advance (void *p, int incr)
|
||||
}
|
||||
|
||||
uint32_t popcount (uint32_t u);
|
||||
inline uint32_t lsbindex (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 */
|
||||
+223
-106
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU Audio subsystem header
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -24,140 +24,257 @@
|
||||
#ifndef QEMU_AUDIO_INT_H
|
||||
#define QEMU_AUDIO_INT_H
|
||||
|
||||
#include "vl.h"
|
||||
#ifdef CONFIG_COREAUDIO
|
||||
#define FLOAT_MIXENG
|
||||
/* #define RECIPROCAL */
|
||||
#endif
|
||||
#include "mixeng.h"
|
||||
|
||||
struct pcm_ops;
|
||||
struct audio_pcm_ops;
|
||||
|
||||
typedef struct HWVoice {
|
||||
int active;
|
||||
int enabled;
|
||||
int pending_disable;
|
||||
int valid;
|
||||
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;
|
||||
|
||||
f_sample *clip;
|
||||
audfmt_e fmt;
|
||||
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;
|
||||
int bufsize;
|
||||
uint64_t ts_helper;
|
||||
|
||||
int bytes_per_second;
|
||||
st_sample_t *mix_buf;
|
||||
|
||||
int samples;
|
||||
int64_t old_ticks;
|
||||
int nb_voices;
|
||||
struct SWVoice **pvoice;
|
||||
struct pcm_ops *pcm_ops;
|
||||
} HWVoice;
|
||||
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;
|
||||
|
||||
extern struct pcm_ops no_pcm_ops;
|
||||
extern struct audio_output_driver no_output_driver;
|
||||
|
||||
extern struct pcm_ops oss_pcm_ops;
|
||||
extern struct audio_output_driver oss_output_driver;
|
||||
|
||||
extern struct pcm_ops sdl_pcm_ops;
|
||||
extern struct audio_output_driver sdl_output_driver;
|
||||
|
||||
extern struct pcm_ops wav_pcm_ops;
|
||||
extern struct audio_output_driver wav_output_driver;
|
||||
|
||||
extern struct pcm_ops fmod_pcm_ops;
|
||||
extern struct audio_output_driver fmod_output_driver;
|
||||
|
||||
struct audio_output_driver {
|
||||
const char *name;
|
||||
void *(*init) (void);
|
||||
void (*fini) (void *);
|
||||
struct pcm_ops *pcm_ops;
|
||||
int can_be_default;
|
||||
int max_voices;
|
||||
int voice_size;
|
||||
};
|
||||
|
||||
typedef struct AudioState {
|
||||
int fixed_format;
|
||||
int fixed_freq;
|
||||
int fixed_channels;
|
||||
int fixed_fmt;
|
||||
int nb_hw_voices;
|
||||
int64_t ticks_threshold;
|
||||
int freq_threshold;
|
||||
void *opaque;
|
||||
struct audio_output_driver *drv;
|
||||
} AudioState;
|
||||
extern AudioState audio_state;
|
||||
|
||||
struct SWVoice {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
|
||||
int shift;
|
||||
int align;
|
||||
typedef struct HWVoiceIn {
|
||||
int enabled;
|
||||
struct audio_pcm_info info;
|
||||
|
||||
t_sample *conv;
|
||||
|
||||
int left;
|
||||
int pos;
|
||||
int bytes_per_second;
|
||||
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 wpos;
|
||||
int live;
|
||||
int total_hw_samples_mixed;
|
||||
int active;
|
||||
int64_t old_ticks;
|
||||
HWVoice *hw;
|
||||
int empty;
|
||||
HWVoiceOut *hw;
|
||||
char *name;
|
||||
volume_t vol;
|
||||
struct audio_callback callback;
|
||||
LIST_ENTRY (SWVoiceOut) entries;
|
||||
};
|
||||
|
||||
struct pcm_ops {
|
||||
int (*init) (HWVoice *hw, int freq, int nchannels, audfmt_e fmt);
|
||||
void (*fini) (HWVoice *hw);
|
||||
void (*run) (HWVoice *hw);
|
||||
int (*write) (SWVoice *sw, void *buf, int size);
|
||||
int (*ctl) (HWVoice *hw, int cmd, ...);
|
||||
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;
|
||||
};
|
||||
|
||||
void pcm_sw_free_resources (SWVoice *sw);
|
||||
int pcm_sw_alloc_resources (SWVoice *sw);
|
||||
void pcm_sw_fini (SWVoice *sw);
|
||||
int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq,
|
||||
int nchannels, audfmt_e fmt);
|
||||
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;
|
||||
};
|
||||
|
||||
void pcm_hw_clear (HWVoice *hw, void *buf, int len);
|
||||
HWVoice * pcm_hw_find_any (HWVoice *hw);
|
||||
HWVoice * pcm_hw_find_any_active (HWVoice *hw);
|
||||
HWVoice * pcm_hw_find_any_passive (HWVoice *hw);
|
||||
HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq,
|
||||
int nchannels, audfmt_e fmt);
|
||||
HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt);
|
||||
int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw);
|
||||
int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw);
|
||||
SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt);
|
||||
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, ...);
|
||||
|
||||
void pcm_hw_free_resources (HWVoice *hw);
|
||||
int pcm_hw_alloc_resources (HWVoice *hw);
|
||||
void pcm_hw_fini (HWVoice *hw);
|
||||
void pcm_hw_gc (HWVoice *hw);
|
||||
int pcm_hw_get_live (HWVoice *hw);
|
||||
int pcm_hw_get_live2 (HWVoice *hw, int *nb_active);
|
||||
void pcm_hw_dec_live (HWVoice *hw, int decr);
|
||||
int pcm_hw_write (SWVoice *sw, void *buf, int len);
|
||||
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, ...);
|
||||
};
|
||||
|
||||
int audio_get_conf_int (const char *key, int defval);
|
||||
const char *audio_get_conf_str (const char *key, const char *defval);
|
||||
struct capture_callback {
|
||||
struct audio_capture_ops ops;
|
||||
void *opaque;
|
||||
LIST_ENTRY (capture_callback) entries;
|
||||
};
|
||||
|
||||
struct audio_output_driver;
|
||||
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
+374
-158
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU FMOD audio output driver
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* 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
|
||||
@@ -25,53 +25,77 @@
|
||||
#include <fmod_errors.h>
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
#define AUDIO_CAP "fmod"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct FMODVoice {
|
||||
HWVoice hw;
|
||||
typedef struct FMODVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
unsigned int old_pos;
|
||||
FSOUND_SAMPLE *fmod_sample;
|
||||
int channel;
|
||||
} FMODVoice;
|
||||
} FMODVoiceOut;
|
||||
|
||||
#define dolog(...) AUD_log ("fmod", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#define QC_FMOD_DRV "QEMU_FMOD_DRV"
|
||||
#define QC_FMOD_FREQ "QEMU_FMOD_FREQ"
|
||||
#define QC_FMOD_SAMPLES "QEMU_FMOD_SAMPLES"
|
||||
#define QC_FMOD_CHANNELS "QEMU_FMOD_CHANNELS"
|
||||
#define QC_FMOD_BUFSIZE "QEMU_FMOD_BUFSIZE"
|
||||
#define QC_FMOD_THRESHOLD "QEMU_FMOD_THRESHOLD"
|
||||
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 = {
|
||||
2048,
|
||||
NULL,
|
||||
2048 * 2,
|
||||
44100,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
128
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
#define errstr() FMOD_ErrorString (FSOUND_GetError ())
|
||||
|
||||
static int fmod_hw_write (SWVoice *sw, void *buf, int len)
|
||||
static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
|
||||
{
|
||||
return pcm_hw_write (sw, buf, len);
|
||||
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 fmod_clear_sample (FMODVoice *fmd)
|
||||
static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
HWVoice *hw = &fmd->hw;
|
||||
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;
|
||||
@@ -79,7 +103,7 @@ static void fmod_clear_sample (FMODVoice *fmd)
|
||||
status = FSOUND_Sample_Lock (
|
||||
fmd->fmod_sample,
|
||||
0,
|
||||
hw->samples << hw->shift,
|
||||
hw->samples << hw->info.shift,
|
||||
&p1,
|
||||
&p2,
|
||||
&len1,
|
||||
@@ -87,78 +111,86 @@ static void fmod_clear_sample (FMODVoice *fmd)
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
dolog ("Failed to lock sample\nReason: %s\n", errstr ());
|
||||
fmod_logerr ("Failed to lock sample\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((len1 & hw->align) || (len2 & hw->align)) {
|
||||
dolog ("Locking sample returned unaligned length %d, %d\n",
|
||||
len1, len2);
|
||||
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->shift) {
|
||||
dolog ("Locking sample returned incomplete length %d, %d\n",
|
||||
len1 + len2, hw->samples << hw->shift);
|
||||
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;
|
||||
}
|
||||
pcm_hw_clear (hw, p1, hw->samples);
|
||||
|
||||
audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
|
||||
|
||||
fail:
|
||||
status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
|
||||
if (!status) {
|
||||
dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
|
||||
fmod_logerr ("Failed to unlock sample\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_write_sample (HWVoice *hw, uint8_t *dst, st_sample_t *src,
|
||||
int src_size, int src_pos, int dst_len)
|
||||
static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
||||
{
|
||||
int src_len1 = dst_len, src_len2 = 0, pos = src_pos + dst_len;
|
||||
st_sample_t *src1 = src + src_pos, *src2 = 0;
|
||||
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 (src_pos + dst_len > src_size) {
|
||||
src_len1 = src_size - src_pos;
|
||||
src2 = src;
|
||||
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);
|
||||
memset (src1, 0, src_len1 * sizeof (st_sample_t));
|
||||
advance (dst, src_len1);
|
||||
}
|
||||
|
||||
if (src_len2) {
|
||||
dst = advance (dst, src_len1 << hw->info.shift);
|
||||
hw->clip (dst, src2, src_len2);
|
||||
memset (src2, 0, src_len2 * sizeof (st_sample_t));
|
||||
}
|
||||
return pos;
|
||||
|
||||
hw->rpos = pos % hw->samples;
|
||||
}
|
||||
|
||||
static int fmod_unlock_sample (FMODVoice *fmd, void *p1, void *p2,
|
||||
static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
|
||||
unsigned int blen1, unsigned int blen2)
|
||||
{
|
||||
int status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
|
||||
if (!status) {
|
||||
dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
|
||||
fmod_logerr ("Failed to unlock sample\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_lock_sample (FMODVoice *fmd, int pos, int len,
|
||||
void **p1, void **p2,
|
||||
unsigned int *blen1, unsigned int *blen2)
|
||||
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
|
||||
)
|
||||
{
|
||||
HWVoice *hw = &fmd->hw;
|
||||
int status;
|
||||
|
||||
status = FSOUND_Sample_Lock (
|
||||
fmd->fmod_sample,
|
||||
pos << hw->shift,
|
||||
len << hw->shift,
|
||||
sample,
|
||||
pos << info->shift,
|
||||
len << info->shift,
|
||||
p1,
|
||||
p2,
|
||||
blen1,
|
||||
@@ -166,89 +198,117 @@ static int fmod_lock_sample (FMODVoice *fmd, int pos, int len,
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
dolog ("Failed to lock sample\nReason: %s\n", errstr ());
|
||||
fmod_logerr ("Failed to lock sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((*blen1 & hw->align) || (*blen2 & hw->align)) {
|
||||
dolog ("Locking sample returned unaligned length %d, %d\n",
|
||||
*blen1, *blen2);
|
||||
fmod_unlock_sample (fmd, *p1, *p2, *blen1, *blen2);
|
||||
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 void fmod_hw_run (HWVoice *hw)
|
||||
static int fmod_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
FMODVoice *fmd = (FMODVoice *) hw;
|
||||
int rpos, live, decr;
|
||||
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_active;
|
||||
int nb_live;
|
||||
|
||||
live = pcm_hw_get_live2 (hw, &nb_active);
|
||||
if (live <= 0) {
|
||||
return;
|
||||
live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!hw->pending_disable
|
||||
&& nb_active
|
||||
&& conf.threshold
|
||||
&& live <= conf.threshold) {
|
||||
ldebug ("live=%d nb_active=%d\n", live, nb_active);
|
||||
return;
|
||||
&& nb_live
|
||||
&& (conf.threshold && live <= conf.threshold)) {
|
||||
ldebug ("live=%d nb_live=%d\n", live, nb_live);
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = live;
|
||||
|
||||
#if 1
|
||||
if (fmd->channel >= 0) {
|
||||
int pos2 = (fmd->old_pos + decr) % hw->samples;
|
||||
int pos = FSOUND_GetCurrentPosition (fmd->channel);
|
||||
int len = decr;
|
||||
int old_pos = fmd->old_pos;
|
||||
int ppos = FSOUND_GetCurrentPosition (fmd->channel);
|
||||
|
||||
if (fmd->old_pos < pos && pos2 >= pos) {
|
||||
decr = pos - fmd->old_pos - (pos2 == pos) - 1;
|
||||
if (ppos == old_pos || !ppos) {
|
||||
return 0;
|
||||
}
|
||||
else if (fmd->old_pos > pos && pos2 >= pos && pos2 < fmd->old_pos) {
|
||||
decr = (hw->samples - fmd->old_pos) + pos - (pos2 == pos) - 1;
|
||||
|
||||
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
||||
len = ppos - old_pos;
|
||||
}
|
||||
/* ldebug ("pos=%d pos2=%d old=%d live=%d decr=%d\n", */
|
||||
/* pos, pos2, fmd->old_pos, live, decr); */
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
|
||||
len = hw->samples - old_pos + ppos;
|
||||
}
|
||||
}
|
||||
decr = len;
|
||||
|
||||
if (decr <= 0) {
|
||||
return;
|
||||
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 (fmod_lock_sample (fmd, fmd->old_pos, decr, &p1, &p2, &blen1, &blen2)) {
|
||||
return;
|
||||
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hw->shift;
|
||||
len2 = blen2 >> hw->shift;
|
||||
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;
|
||||
rpos = hw->rpos;
|
||||
|
||||
if (len1) {
|
||||
rpos = fmod_write_sample (hw, p1, hw->mix_buf, hw->samples, rpos, len1);
|
||||
if (p1 && len1) {
|
||||
fmod_write_sample (hw, p1, len1);
|
||||
}
|
||||
|
||||
if (len2) {
|
||||
rpos = fmod_write_sample (hw, p2, hw->mix_buf, hw->samples, rpos, len2);
|
||||
if (p2 && len2) {
|
||||
fmod_write_sample (hw, p2, len2);
|
||||
}
|
||||
|
||||
fmod_unlock_sample (fmd, p1, p2, blen1, blen2);
|
||||
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
hw->rpos = rpos % hw->samples;
|
||||
fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int AUD_to_fmodfmt (audfmt_e fmt, int stereo)
|
||||
static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
|
||||
{
|
||||
int mode = FSOUND_LOOP_NORMAL;
|
||||
|
||||
@@ -270,16 +330,19 @@ static int AUD_to_fmodfmt (audfmt_e fmt, int stereo)
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
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_hw_fini (HWVoice *hw)
|
||||
static void fmod_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
FMODVoice *fmd = (FMODVoice *) hw;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
|
||||
if (fmd->fmod_sample) {
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
@@ -291,69 +354,164 @@ static void fmod_hw_fini (HWVoice *hw)
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
int bits16, mode, channel;
|
||||
FMODVoice *fmd = (FMODVoice *) hw;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
audsettings_t obt_as = *as;
|
||||
|
||||
mode = AUD_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0);
|
||||
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 */
|
||||
freq, /* freq */
|
||||
as->freq, /* freq */
|
||||
255, /* volume */
|
||||
128, /* pan */
|
||||
255 /* priority */
|
||||
);
|
||||
|
||||
if (!fmd->fmod_sample) {
|
||||
dolog ("Failed to allocate FMOD sample\nReason: %s\n", errstr ());
|
||||
fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
|
||||
if (channel < 0) {
|
||||
dolog ("Failed to start playing sound\nReason: %s\n", errstr ());
|
||||
fmod_logerr2 ("DAC", "Failed to start playing sound\n");
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
return -1;
|
||||
}
|
||||
fmd->channel = channel;
|
||||
|
||||
hw->freq = freq;
|
||||
hw->fmt = fmt;
|
||||
hw->nchannels = nchannels;
|
||||
bits16 = fmt == AUD_FMT_U16 || fmt == AUD_FMT_S16;
|
||||
hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16;
|
||||
/* 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_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int status;
|
||||
FMODVoice *fmd = (FMODVoice *) hw;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
fmod_clear_sample (fmd);
|
||||
status = FSOUND_SetPaused (fmd->channel, 0);
|
||||
if (!status) {
|
||||
dolog ("Failed to resume channel %d\nReason: %s\n",
|
||||
fmd->channel, errstr ());
|
||||
fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
status = FSOUND_SetPaused (fmd->channel, 1);
|
||||
if (!status) {
|
||||
dolog ("Failed to pause channel %d\nReason: %s\n",
|
||||
fmd->channel, errstr ());
|
||||
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;
|
||||
@@ -378,16 +536,16 @@ static struct {
|
||||
{"ps2", FSOUND_OUTPUT_PS2},
|
||||
{"gcube", FSOUND_OUTPUT_GC},
|
||||
#endif
|
||||
{"nort", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
|
||||
{"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
|
||||
};
|
||||
|
||||
static void *fmod_audio_init (void)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
double ver;
|
||||
int status;
|
||||
int output_type = -1;
|
||||
const char *drv = audio_get_conf_str (QC_FMOD_DRV, NULL);
|
||||
const char *drv = conf.drvname;
|
||||
|
||||
ver = FSOUND_GetVersion ();
|
||||
if (ver < FMOD_VERSION) {
|
||||
@@ -395,6 +553,14 @@ static void *fmod_audio_init (void)
|
||||
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++) {
|
||||
@@ -405,65 +571,115 @@ static void *fmod_audio_init (void)
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
dolog ("Unknown FMOD output driver `%s'\n", drv);
|
||||
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) {
|
||||
dolog ("FSOUND_SetOutput(%d) failed\nReason: %s\n",
|
||||
output_type, errstr ());
|
||||
fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
conf.freq = audio_get_conf_int (QC_FMOD_FREQ, conf.freq);
|
||||
conf.nb_samples = audio_get_conf_int (QC_FMOD_SAMPLES, conf.nb_samples);
|
||||
conf.nb_channels =
|
||||
audio_get_conf_int (QC_FMOD_CHANNELS,
|
||||
(audio_state.nb_hw_voices > 1
|
||||
? audio_state.nb_hw_voices
|
||||
: conf.nb_channels));
|
||||
conf.bufsize = audio_get_conf_int (QC_FMOD_BUFSIZE, conf.bufsize);
|
||||
conf.threshold = audio_get_conf_int (QC_FMOD_THRESHOLD, conf.threshold);
|
||||
|
||||
if (conf.bufsize) {
|
||||
status = FSOUND_SetBufferSize (conf.bufsize);
|
||||
if (!status) {
|
||||
dolog ("FSOUND_SetBufferSize (%d) failed\nReason: %s\n",
|
||||
conf.bufsize, errstr ());
|
||||
fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
|
||||
if (!status) {
|
||||
dolog ("FSOUND_Init failed\nReason: %s\n", errstr ());
|
||||
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 ();
|
||||
}
|
||||
|
||||
struct pcm_ops fmod_pcm_ops = {
|
||||
fmod_hw_init,
|
||||
fmod_hw_fini,
|
||||
fmod_hw_run,
|
||||
fmod_hw_write,
|
||||
fmod_hw_ctl
|
||||
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}
|
||||
};
|
||||
|
||||
struct audio_output_driver fmod_output_driver = {
|
||||
"fmod",
|
||||
fmod_audio_init,
|
||||
fmod_audio_fini,
|
||||
&fmod_pcm_ops,
|
||||
1,
|
||||
INT_MAX,
|
||||
sizeof (FMODVoice)
|
||||
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)
|
||||
};
|
||||
+130
-108
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
* 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
|
||||
@@ -23,87 +23,174 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
//#define DEBUG_FP
|
||||
#include "audio/mixeng.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 CHAR_MIN
|
||||
#define IN_MAX CHAR_MAX
|
||||
#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] = {
|
||||
t_sample *mixeng_conv[2][2][2][2] = {
|
||||
{
|
||||
{
|
||||
conv_uint8_t_to_mono,
|
||||
conv_uint16_t_to_mono
|
||||
{
|
||||
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_int8_t_to_mono,
|
||||
conv_int16_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_uint8_t_to_stereo,
|
||||
conv_uint16_t_to_stereo
|
||||
{
|
||||
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_int8_t_to_stereo,
|
||||
conv_int16_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] = {
|
||||
f_sample *mixeng_clip[2][2][2][2] = {
|
||||
{
|
||||
{
|
||||
clip_uint8_t_from_mono,
|
||||
clip_uint16_t_from_mono
|
||||
{
|
||||
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_int8_t_from_mono,
|
||||
clip_int16_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_uint8_t_from_stereo,
|
||||
clip_uint16_t_from_stereo
|
||||
{
|
||||
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_int8_t_from_stereo,
|
||||
clip_int16_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
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -116,9 +203,9 @@ f_sample *mixeng_clip[2][2][2] = {
|
||||
* 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.
|
||||
* any purpose. This copyright notice must be maintained.
|
||||
* Lance Norskog And Sundry Contributors are not responsible for
|
||||
* the consequences of using this software.
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -141,36 +228,29 @@ f_sample *mixeng_clip[2][2][2] = {
|
||||
*/
|
||||
|
||||
/* Private data */
|
||||
typedef struct ratestuff {
|
||||
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 */
|
||||
} *rate_t;
|
||||
};
|
||||
|
||||
/*
|
||||
* Prepare processing.
|
||||
*/
|
||||
void *st_rate_start (int inrate, int outrate)
|
||||
{
|
||||
rate_t rate = (rate_t) qemu_mallocz (sizeof (struct ratestuff));
|
||||
struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
|
||||
|
||||
if (!rate) {
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (inrate == outrate) {
|
||||
// exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (inrate >= 65535 || outrate >= 65535) {
|
||||
// exit (EXIT_FAILURE);
|
||||
dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rate->opos = 0;
|
||||
|
||||
/* increment */
|
||||
rate->opos_inc = (inrate * ((int64_t) UINT_MAX)) / outrate;
|
||||
rate->opos_inc = ((uint64_t) inrate << 32) / outrate;
|
||||
|
||||
rate->ipos = 0;
|
||||
rate->ilast.l = 0;
|
||||
@@ -178,78 +258,20 @@ void *st_rate_start (int inrate, int outrate)
|
||||
return rate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processed signed long samples from ibuf to obuf.
|
||||
* Return number of samples processed.
|
||||
*/
|
||||
void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
|
||||
int *isamp, int *osamp)
|
||||
{
|
||||
rate_t rate = (rate_t) opaque;
|
||||
st_sample_t *istart, *iend;
|
||||
st_sample_t *ostart, *oend;
|
||||
st_sample_t ilast, icur, out;
|
||||
int64_t t;
|
||||
#define NAME st_rate_flow_mix
|
||||
#define OP(a, b) a += b
|
||||
#include "rate_template.h"
|
||||
|
||||
ilast = rate->ilast;
|
||||
|
||||
istart = ibuf;
|
||||
iend = ibuf + *isamp;
|
||||
|
||||
ostart = obuf;
|
||||
oend = obuf + *osamp;
|
||||
|
||||
if (rate->opos_inc == 1ULL << 32) {
|
||||
int i, n = *isamp > *osamp ? *osamp : *isamp;
|
||||
for (i = 0; i < n; i++) {
|
||||
obuf[i].l += ibuf[i].r;
|
||||
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 */
|
||||
t = rate->opos & 0xffffffff;
|
||||
out.l = (ilast.l * (INT_MAX - t) + icur.l * t) / INT_MAX;
|
||||
out.r = (ilast.r * (INT_MAX - t) + icur.r * t) / INT_MAX;
|
||||
|
||||
/* output sample & increment position */
|
||||
#if 0
|
||||
*obuf++ = out;
|
||||
#else
|
||||
obuf->l += out.l;
|
||||
obuf->r += out.r;
|
||||
obuf += 1;
|
||||
#endif
|
||||
rate->opos += rate->opos_inc;
|
||||
}
|
||||
|
||||
the_end:
|
||||
*isamp = ibuf - istart;
|
||||
*osamp = obuf - ostart;
|
||||
rate->ilast = ilast;
|
||||
}
|
||||
#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));
|
||||
}
|
||||
+19
-7
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU Mixing engine header
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -24,16 +24,28 @@
|
||||
#ifndef QEMU_MIXENG_H
|
||||
#define QEMU_MIXENG_H
|
||||
|
||||
typedef void (t_sample) (void *dst, const void *src, int samples);
|
||||
typedef void (f_sample) (void *dst, const void *src, int samples);
|
||||
#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
|
||||
|
||||
extern t_sample *mixeng_conv[2][2][2];
|
||||
extern f_sample *mixeng_clip[2][2][2];
|
||||
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 */
|
||||
+105
-39
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU Mixing engine
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -27,85 +27,151 @@
|
||||
* dec++'ified by Dscho
|
||||
*/
|
||||
|
||||
#ifdef SIGNED
|
||||
#define HALFT IN_MAX
|
||||
#define HALF IN_MAX
|
||||
#else
|
||||
#define HALFT ((IN_MAX)>>1)
|
||||
#define HALF HALFT
|
||||
#ifndef SIGNED
|
||||
#define HALF (IN_MAX >> 1)
|
||||
#endif
|
||||
|
||||
static int64_t inline glue(conv_,IN_T) (IN_T v)
|
||||
{
|
||||
#ifdef SIGNED
|
||||
return (INT_MAX*(int64_t)v)/HALF;
|
||||
#ifdef NOVOL
|
||||
#define VOL(a, b) a
|
||||
#else
|
||||
return (INT_MAX*((int64_t)v-HALFT))/HALF;
|
||||
#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_,IN_T) (int64_t v)
|
||||
static IN_T inline glue (clip_, ET) (real_t v)
|
||||
{
|
||||
if (v >= INT_MAX)
|
||||
if (v >= 0.5) {
|
||||
return IN_MAX;
|
||||
else if (v < -INT_MAX)
|
||||
}
|
||||
else if (v < -0.5) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
#ifdef SIGNED
|
||||
return (IN_T) (v*HALF/INT_MAX);
|
||||
return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
|
||||
#else
|
||||
return (IN_T) (v+INT_MAX/2)*HALF/INT_MAX;
|
||||
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void glue(glue(conv_,IN_T),_to_stereo) (void *dst, const void *src,
|
||||
int samples)
|
||||
#else /* !FLOAT_MIXENG */
|
||||
|
||||
static inline int64_t glue (conv_, ET) (IN_T v)
|
||||
{
|
||||
st_sample_t *out = (st_sample_t *) dst;
|
||||
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 = glue(conv_,IN_T) (*in++);
|
||||
out->r = glue(conv_,IN_T) (*in++);
|
||||
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_,IN_T),_to_mono) (void *dst, const void *src,
|
||||
int samples)
|
||||
static void glue (glue (conv_, ET), _to_mono)
|
||||
(st_sample_t *dst, const void *src, int samples, volume_t *vol)
|
||||
{
|
||||
st_sample_t *out = (st_sample_t *) dst;
|
||||
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 = glue(conv_,IN_T) (in[0]);
|
||||
out->l = VOL (glue (conv_, ET) (in[0]), vol->l);
|
||||
out->r = out->l;
|
||||
out += 1;
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue(glue(clip_,IN_T),_from_stereo) (void *dst, const void *src,
|
||||
int samples)
|
||||
static void glue (glue (clip_, ET), _from_stereo)
|
||||
(void *dst, const st_sample_t *src, int samples)
|
||||
{
|
||||
st_sample_t *in = (st_sample_t *) src;
|
||||
const st_sample_t *in = src;
|
||||
IN_T *out = (IN_T *) dst;
|
||||
while (samples--) {
|
||||
*out++ = glue(clip_,IN_T) (in->l);
|
||||
*out++ = glue(clip_,IN_T) (in->r);
|
||||
*out++ = glue (clip_, ET) (in->l);
|
||||
*out++ = glue (clip_, ET) (in->r);
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void glue(glue(clip_,IN_T),_from_mono) (void *dst, const void *src,
|
||||
int samples)
|
||||
static void glue (glue (clip_, ET), _from_mono)
|
||||
(void *dst, const st_sample_t *src, int samples)
|
||||
{
|
||||
st_sample_t *in = (st_sample_t *) src;
|
||||
const st_sample_t *in = src;
|
||||
IN_T *out = (IN_T *) dst;
|
||||
while (samples--) {
|
||||
*out++ = glue(clip_,IN_T) (in->l + in->r);
|
||||
*out++ = glue (clip_, ET) (in->l + in->r);
|
||||
in += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef ET
|
||||
#undef HALF
|
||||
#undef HALFT
|
||||
|
||||
#undef VOL
|
||||
+110
-66
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU NULL audio output driver
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* 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
|
||||
@@ -23,77 +23,110 @@
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
#define AUDIO_CAP "noaudio"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct NoVoice {
|
||||
HWVoice hw;
|
||||
typedef struct NoVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int64_t old_ticks;
|
||||
} NoVoice;
|
||||
} NoVoiceOut;
|
||||
|
||||
#define dolog(...) AUD_log ("noaudio", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
typedef struct NoVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
int64_t old_ticks;
|
||||
} NoVoiceIn;
|
||||
|
||||
static void no_hw_run (HWVoice *hw)
|
||||
static int no_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
NoVoice *no = (NoVoice *) hw;
|
||||
int rpos, live, decr, samples;
|
||||
st_sample_t *src;
|
||||
int64_t now = qemu_get_clock (vm_clock);
|
||||
int64_t ticks = now - no->old_ticks;
|
||||
int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
int live, decr, samples;
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
|
||||
if (bytes > INT_MAX)
|
||||
samples = INT_MAX >> hw->shift;
|
||||
else
|
||||
samples = bytes >> hw->shift;
|
||||
live = audio_pcm_hw_get_live_out (&no->hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = pcm_hw_get_live (hw);
|
||||
if (live <= 0)
|
||||
return;
|
||||
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);
|
||||
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 = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
|
||||
memset (src, 0, convert_samples * sizeof (st_sample_t));
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
}
|
||||
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
hw->rpos = rpos;
|
||||
hw->rpos = (hw->rpos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int no_hw_write (SWVoice *sw, void *buf, int len)
|
||||
static int no_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return pcm_hw_write (sw, buf, len);
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int no_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
static int no_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
hw->freq = freq;
|
||||
hw->nchannels = nchannels;
|
||||
hw->fmt = fmt;
|
||||
hw->bufsize = 4096;
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void no_hw_fini (HWVoice *hw)
|
||||
static void no_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static int no_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
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;
|
||||
@@ -107,22 +140,33 @@ static void *no_audio_init (void)
|
||||
|
||||
static void no_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
struct pcm_ops no_pcm_ops = {
|
||||
no_hw_init,
|
||||
no_hw_fini,
|
||||
no_hw_run,
|
||||
no_hw_write,
|
||||
no_hw_ctl
|
||||
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_output_driver no_output_driver = {
|
||||
"none",
|
||||
no_audio_init,
|
||||
no_audio_fini,
|
||||
&no_pcm_ops,
|
||||
1,
|
||||
1,
|
||||
sizeof (NoVoice)
|
||||
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)
|
||||
};
|
||||
+473
-180
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU OSS audio output driver
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
* 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
|
||||
@@ -25,45 +25,44 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <assert.h>
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
#define AUDIO_CAP "oss"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct OSSVoice {
|
||||
HWVoice hw;
|
||||
typedef struct OSSVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int mmapped;
|
||||
int old_optr;
|
||||
} OSSVoice;
|
||||
} OSSVoiceOut;
|
||||
|
||||
#define dolog(...) AUD_log ("oss", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
|
||||
#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS"
|
||||
#define QC_OSS_MMAP "QEMU_OSS_MMAP"
|
||||
#define QC_OSS_DEV "QEMU_OSS_DEV"
|
||||
|
||||
#define errstr() strerror (errno)
|
||||
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 *dspname;
|
||||
const char *devpath_out;
|
||||
const char *devpath_in;
|
||||
int debug;
|
||||
} conf = {
|
||||
.try_mmap = 0,
|
||||
.nfrags = 4,
|
||||
.fragsize = 4096,
|
||||
.dspname = "/dev/dsp"
|
||||
.devpath_out = "/dev/dsp",
|
||||
.devpath_in = "/dev/dsp",
|
||||
.debug = 0
|
||||
};
|
||||
|
||||
struct oss_params {
|
||||
@@ -74,65 +73,141 @@ struct oss_params {
|
||||
int fragsize;
|
||||
};
|
||||
|
||||
static int oss_hw_write (SWVoice *sw, void *buf, int len)
|
||||
static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
|
||||
{
|
||||
return pcm_hw_write (sw, buf, len);
|
||||
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 int AUD_to_ossfmt (audfmt_e fmt)
|
||||
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;
|
||||
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\nAborting\n", fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
abort ();
|
||||
#endif
|
||||
return AFMT_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_to_audfmt (int fmt)
|
||||
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AFMT_S8: return AUD_FMT_S8;
|
||||
case AFMT_U8: return AUD_FMT_U8;
|
||||
case AFMT_S16_LE: return AUD_FMT_S16;
|
||||
case AFMT_U16_LE: return AUD_FMT_U16;
|
||||
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 ("Internal logic error: Unrecognized OSS audio format %d\n"
|
||||
"Aborting\n",
|
||||
fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
dolog ("Unrecognized audio format %d\n", ossfmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PCM
|
||||
static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt)
|
||||
#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 ("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);
|
||||
dolog ("fragsize | %10d | %10d\n",
|
||||
req->fragsize, obt->fragsize);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
|
||||
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 = conf.dspname;
|
||||
const char *dspname = in ? conf.devpath_in : conf.devpath_out;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
|
||||
fd = open (dspname, O_WRONLY | O_NONBLOCK);
|
||||
fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
|
||||
if (-1 == fd) {
|
||||
dolog ("Could not initialize audio hardware. Failed to open `%s':\n"
|
||||
"Reason:%s\n",
|
||||
dspname,
|
||||
errstr ());
|
||||
oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -141,52 +216,35 @@ static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
|
||||
fmt = req->fmt;
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set sample size\n"
|
||||
"Reason: %s\n",
|
||||
errstr ());
|
||||
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set number of channels\n"
|
||||
"Reason: %s\n",
|
||||
errstr ());
|
||||
oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
|
||||
req->nchannels);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set frequency\n"
|
||||
"Reason: %s\n",
|
||||
errstr ());
|
||||
oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set non-blocking mode\n"
|
||||
"Reason: %s\n",
|
||||
errstr ());
|
||||
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)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to set buffer length (%d, %d)\n"
|
||||
"Reason:%s\n",
|
||||
conf.nfrags, conf.fragsize,
|
||||
errstr ());
|
||||
oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
|
||||
req->nfrags, req->fragsize);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) {
|
||||
dolog ("Could not initialize audio hardware\n"
|
||||
"Failed to get buffer length\n"
|
||||
"Reason:%s\n",
|
||||
errstr ());
|
||||
if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
|
||||
oss_logerr2 (errno, typ, "Failed to get buffer length\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -197,75 +255,98 @@ static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
|
||||
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)) {
|
||||
#ifdef DEBUG_PCM
|
||||
dolog ("Audio parameters mismatch\n");
|
||||
oss_dump_pcm_info (req, obt);
|
||||
#endif
|
||||
oss_dump_info (req, obt);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_PCM
|
||||
oss_dump_pcm_info (req, obt);
|
||||
#ifdef DEBUG
|
||||
oss_dump_info (req, obt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
err:
|
||||
close (fd);
|
||||
err:
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void oss_hw_run (HWVoice *hw)
|
||||
static int oss_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoice *oss = (OSSVoice *) 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 = pcm_hw_get_live (hw);
|
||||
if (live <= 0)
|
||||
return;
|
||||
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) {
|
||||
dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ());
|
||||
return;
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cntinfo.ptr == oss->old_optr) {
|
||||
if (abs (hw->samples - live) < 64)
|
||||
dolog ("overrun\n");
|
||||
return;
|
||||
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 = hw->bufsize + cntinfo.ptr - oss->old_optr;
|
||||
bytes = bufsize + cntinfo.ptr - oss->old_optr;
|
||||
}
|
||||
|
||||
decr = audio_MIN (bytes >> hw->shift, live);
|
||||
decr = audio_MIN (bytes >> hw->info.shift, live);
|
||||
}
|
||||
else {
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
|
||||
if (err < 0) {
|
||||
dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ());
|
||||
return;
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = audio_MIN (abinfo.bytes >> hw->shift, live);
|
||||
if (decr <= 0)
|
||||
return;
|
||||
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;
|
||||
@@ -274,33 +355,38 @@ static void oss_hw_run (HWVoice *hw)
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
|
||||
dst = advance (oss->pcm_buf, rpos << hw->shift);
|
||||
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->shift);
|
||||
written = write (oss->fd, dst, convert_samples << hw->info.shift);
|
||||
/* XXX: follow errno recommendations ? */
|
||||
if (written == -1) {
|
||||
dolog ("Failed to write audio\nReason: %s\n", errstr ());
|
||||
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->shift) {
|
||||
int wsamples = written >> hw->shift;
|
||||
int wbytes = wsamples << hw->shift;
|
||||
if (written != convert_samples << hw->info.shift) {
|
||||
int wsamples = written >> hw->info.shift;
|
||||
int wbytes = wsamples << hw->info.shift;
|
||||
if (wbytes != written) {
|
||||
dolog ("Unaligned write %d, %d\n", wbytes, written);
|
||||
dolog ("warning: Misaligned write %d (requested %d), "
|
||||
"alignment %d\n",
|
||||
wbytes, written, hw->info.align + 1);
|
||||
}
|
||||
memset (src, 0, wbytes);
|
||||
decr -= samples;
|
||||
decr -= wsamples;
|
||||
rpos = (rpos + wsamples) % hw->samples;
|
||||
break;
|
||||
}
|
||||
}
|
||||
memset (src, 0, convert_samples * sizeof (st_sample_t));
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
@@ -309,28 +395,24 @@ static void oss_hw_run (HWVoice *hw)
|
||||
oss->old_optr = cntinfo.ptr;
|
||||
}
|
||||
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void oss_hw_fini (HWVoice *hw)
|
||||
static void oss_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
int err;
|
||||
OSSVoice *oss = (OSSVoice *) hw;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
ldebug ("oss_hw_fini\n");
|
||||
err = close (oss->fd);
|
||||
if (err) {
|
||||
dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ());
|
||||
}
|
||||
oss->fd = -1;
|
||||
ldebug ("oss_fini\n");
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
if (oss->pcm_buf) {
|
||||
if (oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->bufsize);
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
dolog ("Failed to unmap OSS buffer\nReason: %s\n",
|
||||
errstr ());
|
||||
oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -340,48 +422,76 @@ static void oss_hw_fini (HWVoice *hw)
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
OSSVoice *oss = (OSSVoice *) hw;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
struct oss_params req, obt;
|
||||
int endianness;
|
||||
int err;
|
||||
int fd;
|
||||
audfmt_e effective_fmt;
|
||||
audsettings_t obt_as;
|
||||
|
||||
assert (!oss->fd);
|
||||
req.fmt = AUD_to_ossfmt (fmt);
|
||||
req.freq = freq;
|
||||
req.nchannels = nchannels;
|
||||
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 (&req, &obt, &oss->fd))
|
||||
if (oss_open (0, &req, &obt, &fd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
hw->freq = obt.freq;
|
||||
hw->fmt = oss_to_audfmt (obt.fmt);
|
||||
hw->nchannels = obt.nchannels;
|
||||
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;
|
||||
hw->bufsize = obt.nfrags * 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->bufsize, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, oss->fd, 0);
|
||||
oss->pcm_buf = mmap (
|
||||
0,
|
||||
hw->samples << hw->info.shift,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
0
|
||||
);
|
||||
if (oss->pcm_buf == MAP_FAILED) {
|
||||
dolog ("Failed to mmap OSS device\nReason: %s\n",
|
||||
errstr ());
|
||||
oss_logerr (errno, "Failed to map %d bytes of DAC\n",
|
||||
hw->samples << hw->info.shift);
|
||||
} else {
|
||||
int err;
|
||||
int trig = 0;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
|
||||
errstr ());
|
||||
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
|
||||
}
|
||||
else {
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
"Reason: %s\n", errstr ());
|
||||
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
}
|
||||
else {
|
||||
oss->mmapped = 1;
|
||||
@@ -389,43 +499,55 @@ static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->bufsize);
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
dolog ("Failed to unmap OSS device\nReason: %s\n",
|
||||
errstr ());
|
||||
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 = qemu_mallocz (hw->bufsize);
|
||||
oss->pcm_buf = audio_calloc (
|
||||
AUDIO_FUNC,
|
||||
hw->samples,
|
||||
1 << hw->info.shift
|
||||
);
|
||||
if (!oss->pcm_buf) {
|
||||
close (oss->fd);
|
||||
oss->fd = -1;
|
||||
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_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int trig;
|
||||
OSSVoice *oss = (OSSVoice *) hw;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
if (!oss->mmapped)
|
||||
if (!oss->mmapped) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
ldebug ("enabling voice\n");
|
||||
pcm_hw_clear (hw, oss->pcm_buf, hw->samples);
|
||||
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) {
|
||||
dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
"Reason: %s\n", errstr ());
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
@@ -434,8 +556,7 @@ static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
ldebug ("disabling voice\n");
|
||||
trig = 0;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
|
||||
errstr ());
|
||||
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
@@ -443,33 +564,205 @@ static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
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)
|
||||
{
|
||||
conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize);
|
||||
conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags);
|
||||
conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap);
|
||||
conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname);
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void oss_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
struct pcm_ops oss_pcm_ops = {
|
||||
oss_hw_init,
|
||||
oss_hw_fini,
|
||||
oss_hw_run,
|
||||
oss_hw_write,
|
||||
oss_hw_ctl
|
||||
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}
|
||||
};
|
||||
|
||||
struct audio_output_driver oss_output_driver = {
|
||||
"oss",
|
||||
oss_audio_init,
|
||||
oss_audio_fini,
|
||||
&oss_pcm_ops,
|
||||
1,
|
||||
INT_MAX,
|
||||
sizeof (OSSVoice)
|
||||
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
|
||||
+219
-118
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU SDL audio output driver
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* 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
|
||||
@@ -25,22 +25,15 @@
|
||||
#include <SDL_thread.h>
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
#define AUDIO_CAP "sdl"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct SDLVoice {
|
||||
HWVoice hw;
|
||||
} SDLVoice;
|
||||
|
||||
#define dolog(...) AUD_log ("sdl", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES"
|
||||
|
||||
#define errstr() SDL_GetError ()
|
||||
typedef struct SDLVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int live;
|
||||
int rpos;
|
||||
int decr;
|
||||
} SDLVoiceOut;
|
||||
|
||||
static struct {
|
||||
int nb_samples;
|
||||
@@ -56,91 +49,129 @@ struct SDLAudioState {
|
||||
} glob_sdl;
|
||||
typedef struct SDLAudioState SDLAudioState;
|
||||
|
||||
static void sdl_hw_run (HWVoice *hw)
|
||||
static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
|
||||
{
|
||||
(void) hw;
|
||||
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)
|
||||
static int sdl_lock (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_LockMutex (s->mutex)) {
|
||||
dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ());
|
||||
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock (SDLAudioState *s)
|
||||
static int sdl_unlock (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_UnlockMutex (s->mutex)) {
|
||||
dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ());
|
||||
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_post (SDLAudioState *s)
|
||||
static int sdl_post (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_SemPost (s->sem)) {
|
||||
dolog ("SDL_SemPost failed\nReason: %s\n", errstr ());
|
||||
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_wait (SDLAudioState *s)
|
||||
static int sdl_wait (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_SemWait (s->sem)) {
|
||||
dolog ("SDL_SemWait failed\nReason: %s\n", errstr ());
|
||||
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock_and_post (SDLAudioState *s)
|
||||
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (sdl_unlock (s))
|
||||
if (sdl_unlock (s, forfn)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sdl_post (s);
|
||||
return sdl_post (s, forfn);
|
||||
}
|
||||
|
||||
static int sdl_hw_write (SWVoice *sw, void *buf, int len)
|
||||
static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
|
||||
{
|
||||
int ret;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
sdl_lock (s);
|
||||
ret = pcm_hw_write (sw, buf, len);
|
||||
sdl_unlock_and_post (s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int AUD_to_sdlfmt (audfmt_e fmt, int *shift)
|
||||
{
|
||||
*shift = 0;
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8: return AUDIO_S8;
|
||||
case AUD_FMT_U8: return AUDIO_U8;
|
||||
case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB;
|
||||
case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB;
|
||||
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\nAborting\n", fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
abort ();
|
||||
#endif
|
||||
return AUDIO_U8;
|
||||
}
|
||||
}
|
||||
|
||||
static int sdl_to_audfmt (int fmt)
|
||||
static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUDIO_S8: return AUD_FMT_S8;
|
||||
case AUDIO_U8: return AUD_FMT_U8;
|
||||
case AUDIO_S16LSB: return AUD_FMT_S16;
|
||||
case AUDIO_U16LSB: return AUD_FMT_U16;
|
||||
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 ("Internal logic error: Unrecognized SDL audio format %d\n"
|
||||
"Aborting\n", fmt);
|
||||
exit (EXIT_FAILURE);
|
||||
dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||
@@ -149,7 +180,7 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||
|
||||
status = SDL_OpenAudio (req, obt);
|
||||
if (status) {
|
||||
dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ());
|
||||
sdl_logerr ("SDL_OpenAudio failed\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -157,9 +188,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||
static void sdl_close (SDLAudioState *s)
|
||||
{
|
||||
if (s->initialized) {
|
||||
sdl_lock (s);
|
||||
sdl_lock (s, "sdl_close");
|
||||
s->exit = 1;
|
||||
sdl_unlock_and_post (s);
|
||||
sdl_unlock_and_post (s, "sdl_close");
|
||||
SDL_PauseAudio (1);
|
||||
SDL_CloseAudio ();
|
||||
s->initialized = 0;
|
||||
@@ -168,31 +199,40 @@ static void sdl_close (SDLAudioState *s)
|
||||
|
||||
static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
{
|
||||
SDLVoice *sdl = opaque;
|
||||
SDLVoiceOut *sdl = opaque;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
HWVoice *hw = &sdl->hw;
|
||||
int samples = len >> hw->shift;
|
||||
HWVoiceOut *hw = &sdl->hw;
|
||||
int samples = len >> hw->info.shift;
|
||||
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (samples) {
|
||||
int to_mix, live, decr;
|
||||
int to_mix, decr;
|
||||
|
||||
/* dolog ("in callback samples=%d\n", samples); */
|
||||
sdl_wait (s);
|
||||
sdl_wait (s, "sdl_callback");
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
sdl_lock (s);
|
||||
live = pcm_hw_get_live (hw);
|
||||
if (live <= 0)
|
||||
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, live);
|
||||
to_mix = audio_MIN (samples, sdl->live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
|
||||
@@ -200,58 +240,105 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
|
||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||
hw->clip (buf, src, chunk);
|
||||
memset (src, 0, chunk * sizeof (st_sample_t));
|
||||
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
||||
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->shift;
|
||||
buf += chunk << hw->info.shift;
|
||||
}
|
||||
samples -= decr;
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
again:
|
||||
sdl_unlock (s);
|
||||
if (sdl_unlock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* dolog ("done len=%d\n", len); */
|
||||
}
|
||||
|
||||
static void sdl_hw_fini (HWVoice *hw)
|
||||
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
ldebug ("sdl_hw_fini %d fixed=%d\n",
|
||||
glob_sdl.initialized, audio_state.fixed_format);
|
||||
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_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
SDLVoice *sdl = (SDLVoice *) hw;
|
||||
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;
|
||||
|
||||
ldebug ("sdl_hw_init %d freq=%d fixed=%d\n",
|
||||
s->initialized, freq, audio_state.fixed_format);
|
||||
shift <<= as->nchannels == 2;
|
||||
|
||||
if (nchannels != 2) {
|
||||
dolog ("Bogus channel count %d\n", nchannels);
|
||||
return -1;
|
||||
}
|
||||
|
||||
req.freq = freq;
|
||||
req.format = AUD_to_sdlfmt (fmt, &shift);
|
||||
req.channels = nchannels;
|
||||
req.freq = as->freq;
|
||||
req.format = aud_to_sdlfmt (as->fmt, &shift);
|
||||
req.channels = as->nchannels;
|
||||
req.samples = conf.nb_samples;
|
||||
shift <<= nchannels == 2;
|
||||
|
||||
req.callback = sdl_callback;
|
||||
req.userdata = sdl;
|
||||
|
||||
if (sdl_open (&req, &obt))
|
||||
if (sdl_open (&req, &obt)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
hw->freq = obt.freq;
|
||||
hw->fmt = sdl_to_audfmt (obt.format);
|
||||
hw->nchannels = obt.channels;
|
||||
hw->bufsize = obt.samples << shift;
|
||||
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;
|
||||
@@ -259,7 +346,7 @@ static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
|
||||
@@ -278,24 +365,22 @@ static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
static void *sdl_audio_init (void)
|
||||
{
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples);
|
||||
|
||||
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
|
||||
dolog ("SDL failed to initialize audio subsystem\nReason: %s\n",
|
||||
errstr ());
|
||||
sdl_logerr ("SDL failed to initialize audio subsystem\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->mutex = SDL_CreateMutex ();
|
||||
if (!s->mutex) {
|
||||
dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ());
|
||||
sdl_logerr ("Failed to create SDL mutex\n");
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->sem = SDL_CreateSemaphore (0);
|
||||
if (!s->sem) {
|
||||
dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ());
|
||||
sdl_logerr ("Failed to create SDL semaphore\n");
|
||||
SDL_DestroyMutex (s->mutex);
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
@@ -313,20 +398,36 @@ static void sdl_audio_fini (void *opaque)
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
}
|
||||
|
||||
struct pcm_ops sdl_pcm_ops = {
|
||||
sdl_hw_init,
|
||||
sdl_hw_fini,
|
||||
sdl_hw_run,
|
||||
sdl_hw_write,
|
||||
sdl_hw_ctl
|
||||
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}
|
||||
};
|
||||
|
||||
struct audio_output_driver sdl_output_driver = {
|
||||
"sdl",
|
||||
sdl_audio_init,
|
||||
sdl_audio_fini,
|
||||
&sdl_pcm_ops,
|
||||
1,
|
||||
1,
|
||||
sizeof (SDLVoice)
|
||||
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 */
|
||||
+109
-71
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU WAV audio output driver
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* 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
|
||||
@@ -23,47 +23,50 @@
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#include "audio/audio_int.h"
|
||||
#define AUDIO_CAP "wav"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct WAVVoice {
|
||||
HWVoice hw;
|
||||
typedef struct WAVVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
QEMUFile *f;
|
||||
int64_t old_ticks;
|
||||
void *pcm_buf;
|
||||
int total_samples;
|
||||
} WAVVoice;
|
||||
|
||||
#define dolog(...) AUD_log ("wav", __VA_ARGS__)
|
||||
#ifdef DEBUG
|
||||
#define ldebug(...) dolog (__VA_ARGS__)
|
||||
#else
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
} WAVVoiceOut;
|
||||
|
||||
static struct {
|
||||
audsettings_t settings;
|
||||
const char *wav_path;
|
||||
} conf = {
|
||||
.wav_path = "qemu.wav"
|
||||
{
|
||||
44100,
|
||||
2,
|
||||
AUD_FMT_S16
|
||||
},
|
||||
"qemu.wav"
|
||||
};
|
||||
|
||||
static void wav_hw_run (HWVoice *hw)
|
||||
static int wav_run_out (HWVoiceOut *hw)
|
||||
{
|
||||
WAVVoice *wav = (WAVVoice *) 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->bytes_per_second) / ticks_per_sec;
|
||||
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||
|
||||
if (bytes > INT_MAX)
|
||||
samples = INT_MAX >> hw->shift;
|
||||
else
|
||||
samples = bytes >> hw->shift;
|
||||
if (bytes > INT_MAX) {
|
||||
samples = INT_MAX >> hw->info.shift;
|
||||
}
|
||||
else {
|
||||
samples = bytes >> hw->info.shift;
|
||||
}
|
||||
|
||||
live = pcm_hw_get_live (hw);
|
||||
if (live <= 0)
|
||||
return;
|
||||
live = audio_pcm_hw_get_live_out (hw);
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
wav->old_ticks = now;
|
||||
decr = audio_MIN (live, samples);
|
||||
@@ -73,25 +76,24 @@ static void wav_hw_run (HWVoice *hw)
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = audio_MIN (samples, left_till_end_samples);
|
||||
|
||||
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
|
||||
dst = advance (wav->pcm_buf, rpos << hw->shift);
|
||||
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->shift);
|
||||
memset (src, 0, convert_samples * sizeof (st_sample_t));
|
||||
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;
|
||||
}
|
||||
|
||||
pcm_hw_dec_live (hw, decr);
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int wav_hw_write (SWVoice *sw, void *buf, int len)
|
||||
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return pcm_hw_write (sw, buf, len);
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
/* VICE code: Store number as little endian. */
|
||||
@@ -104,20 +106,25 @@ static void le_store (uint8_t *buf, uint32_t val, int len)
|
||||
}
|
||||
}
|
||||
|
||||
static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
|
||||
{
|
||||
WAVVoice *wav = (WAVVoice *) hw;
|
||||
int bits16 = 0, stereo = audio_state.fixed_channels == 2;
|
||||
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;
|
||||
|
||||
switch (audio_state.fixed_fmt) {
|
||||
(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:
|
||||
@@ -127,22 +134,26 @@ static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
}
|
||||
|
||||
hdr[34] = bits16 ? 0x10 : 0x08;
|
||||
hw->freq = 44100;
|
||||
hw->nchannels = stereo ? 2 : 1;
|
||||
hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
|
||||
hw->bufsize = 4096;
|
||||
wav->pcm_buf = qemu_mallocz (hw->bufsize);
|
||||
if (!wav->pcm_buf)
|
||||
return -1;
|
||||
|
||||
le_store (hdr + 22, hw->nchannels, 2);
|
||||
le_store (hdr + 24, hw->freq, 4);
|
||||
le_store (hdr + 28, hw->freq << (bits16 + stereo), 4);
|
||||
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 = fopen (conf.wav_path, "wb");
|
||||
wav->f = qemu_fopen (conf.wav_path, "wb");
|
||||
if (!wav->f) {
|
||||
dolog ("failed to open wave file `%s'\nReason: %s\n",
|
||||
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
||||
conf.wav_path, strerror (errno));
|
||||
qemu_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
@@ -153,17 +164,17 @@ static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wav_hw_fini (HWVoice *hw)
|
||||
static void wav_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
WAVVoice *wav = (WAVVoice *) hw;
|
||||
int stereo = hw->nchannels == 2;
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
uint8_t rlen[4];
|
||||
uint8_t dlen[4];
|
||||
uint32_t rifflen = (wav->total_samples << stereo) + 36;
|
||||
uint32_t datalen = wav->total_samples << stereo;
|
||||
uint32_t datalen = wav->total_samples << hw->info.shift;
|
||||
uint32_t rifflen = datalen + 36;
|
||||
|
||||
if (!wav->f || !hw->active)
|
||||
if (!wav->f) {
|
||||
return;
|
||||
}
|
||||
|
||||
le_store (rlen, rifflen, 4);
|
||||
le_store (dlen, datalen, 4);
|
||||
@@ -174,14 +185,14 @@ static void wav_hw_fini (HWVoice *hw)
|
||||
qemu_fseek (wav->f, 32, SEEK_CUR);
|
||||
qemu_put_buffer (wav->f, dlen, 4);
|
||||
|
||||
fclose (wav->f);
|
||||
qemu_fclose (wav->f);
|
||||
wav->f = NULL;
|
||||
|
||||
qemu_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int wav_hw_ctl (HWVoice *hw, int cmd, ...)
|
||||
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
@@ -195,23 +206,50 @@ static void *wav_audio_init (void)
|
||||
|
||||
static void wav_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
ldebug ("wav_fini");
|
||||
}
|
||||
|
||||
struct pcm_ops wav_pcm_ops = {
|
||||
wav_hw_init,
|
||||
wav_hw_fini,
|
||||
wav_hw_run,
|
||||
wav_hw_write,
|
||||
wav_hw_ctl
|
||||
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_output_driver wav_output_driver = {
|
||||
"wav",
|
||||
wav_audio_init,
|
||||
wav_audio_fini,
|
||||
&wav_pcm_ops,
|
||||
1,
|
||||
1,
|
||||
sizeof (WAVVoice)
|
||||
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;
|
||||
}
|
||||
+38
-8
@@ -28,7 +28,8 @@
|
||||
/**************************************************************/
|
||||
|
||||
#define HEADER_MAGIC "Bochs Virtual HD Image"
|
||||
#define HEADER_VERSION 0x00010000
|
||||
#define HEADER_VERSION 0x00020000
|
||||
#define HEADER_V1 0x00010000
|
||||
#define HEADER_SIZE 512
|
||||
|
||||
#define REDOLOG_TYPE "Redolog"
|
||||
@@ -36,6 +37,26 @@
|
||||
|
||||
// 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"
|
||||
@@ -49,8 +70,9 @@ struct bochs_header {
|
||||
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 - 20];
|
||||
char padding[HEADER_SIZE - 64 - 8 - 24];
|
||||
} redolog;
|
||||
char padding[HEADER_SIZE - 64 - 8];
|
||||
} extra;
|
||||
@@ -79,21 +101,23 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
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_VERSION) ||
|
||||
(le32_to_cpu(bochs->version) == HEADER_V1)))
|
||||
return 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_open(BlockDriverState *bs, const char *filename)
|
||||
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 | O_LARGEFILE);
|
||||
fd = open(filename, O_RDWR | O_BINARY);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
@@ -109,11 +133,17 @@ static int bochs_open(BlockDriverState *bs, const char *filename)
|
||||
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_VERSION) &&
|
||||
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
|
||||
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);
|
||||
|
||||
|
||||
+7
-7
@@ -32,8 +32,8 @@ typedef struct BDRVCloopState {
|
||||
uint64_t* offsets;
|
||||
uint32_t sectors_per_block;
|
||||
uint32_t current_block;
|
||||
char* compressed_block;
|
||||
char* uncompressed_block;
|
||||
uint8_t *compressed_block;
|
||||
uint8_t *uncompressed_block;
|
||||
z_stream zstream;
|
||||
} BDRVCloopState;
|
||||
|
||||
@@ -50,14 +50,14 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_open(BlockDriverState *bs, const char *filename)
|
||||
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 | O_LARGEFILE);
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (s->fd < 0)
|
||||
return -1;
|
||||
return -errno;
|
||||
bs->read_only = 1;
|
||||
|
||||
/* read header */
|
||||
@@ -89,9 +89,9 @@ cloop_close:
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
if(!(s->compressed_block=(char*)malloc(max_compressed_block_size+1)))
|
||||
if(!(s->compressed_block = malloc(max_compressed_block_size+1)))
|
||||
goto cloop_close;
|
||||
if(!(s->uncompressed_block=(char*)malloc(s->block_size)))
|
||||
if(!(s->uncompressed_block = malloc(s->block_size)))
|
||||
goto cloop_close;
|
||||
if(inflateInit(&s->zstream) != Z_OK)
|
||||
goto cloop_close;
|
||||
|
||||
+24
-21
@@ -62,7 +62,7 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_open(BlockDriverState *bs, const char *filename)
|
||||
static int cow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
int fd;
|
||||
@@ -93,22 +93,6 @@ static int cow_open(BlockDriverState *bs, const char *filename)
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||
cow_header.backing_file);
|
||||
|
||||
#if 0
|
||||
if (cow_header.backing_file[0] != '\0') {
|
||||
if (stat(cow_header.backing_file, &st) != 0) {
|
||||
fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file);
|
||||
goto fail;
|
||||
}
|
||||
if (st.st_mtime != be32_to_cpu(cow_header.mtime)) {
|
||||
fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file);
|
||||
goto fail;
|
||||
}
|
||||
fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
bs->fd = fd;
|
||||
}
|
||||
#endif
|
||||
/* 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),
|
||||
@@ -179,8 +163,15 @@ static int cow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
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;
|
||||
@@ -220,7 +211,7 @@ static int cow_create(const char *filename, int64_t image_sectors,
|
||||
if (flags)
|
||||
return -ENOTSUP;
|
||||
|
||||
cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
0644);
|
||||
if (cow_fd < 0)
|
||||
return -1;
|
||||
@@ -228,18 +219,23 @@ static int cow_create(const char *filename, int64_t image_sectors,
|
||||
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);
|
||||
return -1;
|
||||
goto mtime_fail;
|
||||
}
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
goto mtime_fail;
|
||||
}
|
||||
close(fd);
|
||||
cow_header.mtime = cpu_to_be32(st.st_mtime);
|
||||
realpath(image_filename, cow_header.backing_file);
|
||||
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);
|
||||
@@ -250,6 +246,12 @@ static int cow_create(const char *filename, int64_t image_sectors,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cow_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
fsync(s->fd);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_cow = {
|
||||
"cow",
|
||||
sizeof(BDRVCowState),
|
||||
@@ -259,6 +261,7 @@ BlockDriver bdrv_cow = {
|
||||
cow_write,
|
||||
cow_close,
|
||||
cow_create,
|
||||
cow_flush,
|
||||
cow_is_allocated,
|
||||
};
|
||||
#endif
|
||||
+8
-8
@@ -44,8 +44,8 @@ typedef struct BDRVDMGState {
|
||||
uint64_t* sectors;
|
||||
uint64_t* sectorcounts;
|
||||
uint32_t current_chunk;
|
||||
char* compressed_chunk;
|
||||
char* uncompressed_chunk;
|
||||
uint8_t *compressed_chunk;
|
||||
uint8_t *uncompressed_chunk;
|
||||
z_stream zstream;
|
||||
} BDRVDMGState;
|
||||
|
||||
@@ -73,16 +73,16 @@ static off_t read_uint32(int fd)
|
||||
return be32_to_cpu(buffer);
|
||||
}
|
||||
|
||||
static int dmg_open(BlockDriverState *bs, const char *filename)
|
||||
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 | O_LARGEFILE);
|
||||
s->fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (s->fd < 0)
|
||||
return -1;
|
||||
return -errno;
|
||||
bs->read_only = 1;
|
||||
s->n_chunks = 0;
|
||||
s->offsets = s->lengths = s->sectors = s->sectorcounts = 0;
|
||||
@@ -93,7 +93,7 @@ dmg_close:
|
||||
close(s->fd);
|
||||
/* open raw instead */
|
||||
bs->drv=&bdrv_raw;
|
||||
return bs->drv->bdrv_open(bs,filename);
|
||||
return bs->drv->bdrv_open(bs, filename, flags);
|
||||
}
|
||||
info_begin=read_off(s->fd);
|
||||
if(info_begin==0)
|
||||
@@ -159,9 +159,9 @@ dmg_close:
|
||||
}
|
||||
|
||||
/* initialize zlib engine */
|
||||
if(!(s->compressed_chunk=(char*)malloc(max_compressed_size+1)))
|
||||
if(!(s->compressed_chunk = malloc(max_compressed_size+1)))
|
||||
goto dmg_close;
|
||||
if(!(s->uncompressed_chunk=(char*)malloc(512*max_sectors_per_chunk)))
|
||||
if(!(s->uncompressed_chunk = malloc(512*max_sectors_per_chunk)))
|
||||
goto dmg_close;
|
||||
if(inflateInit(&s->zstream) != Z_OK)
|
||||
goto dmg_close;
|
||||
|
||||
+295
-78
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Block driver for the QCOW format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
* 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
|
||||
@@ -53,7 +53,7 @@ typedef struct QCowHeader {
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct BDRVQcowState {
|
||||
int fd;
|
||||
BlockDriverState *hd;
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
int cluster_sectors;
|
||||
@@ -89,20 +89,16 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_open(BlockDriverState *bs, const char *filename)
|
||||
static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int fd, len, i, shift;
|
||||
int len, i, shift, ret;
|
||||
QCowHeader header;
|
||||
|
||||
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;
|
||||
if (read(fd, &header, sizeof(header)) != sizeof(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);
|
||||
@@ -138,8 +134,7 @@ static int qcow_open(BlockDriverState *bs, const char *filename)
|
||||
s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
|
||||
if (!s->l1_table)
|
||||
goto fail;
|
||||
lseek(fd, s->l1_table_offset, SEEK_SET);
|
||||
if (read(fd, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
|
||||
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++) {
|
||||
@@ -162,8 +157,7 @@ static int qcow_open(BlockDriverState *bs, const char *filename)
|
||||
len = header.backing_file_size;
|
||||
if (len > 1023)
|
||||
len = 1023;
|
||||
lseek(fd, header.backing_file_offset, SEEK_SET);
|
||||
if (read(fd, bs->backing_file, len) != len)
|
||||
if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len)
|
||||
goto fail;
|
||||
bs->backing_file[len] = '\0';
|
||||
}
|
||||
@@ -174,7 +168,7 @@ static int qcow_open(BlockDriverState *bs, const char *filename)
|
||||
qemu_free(s->l2_cache);
|
||||
qemu_free(s->cluster_cache);
|
||||
qemu_free(s->cluster_data);
|
||||
close(fd);
|
||||
bdrv_delete(s->hd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -276,14 +270,14 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
if (!allocate)
|
||||
return 0;
|
||||
/* allocate a new l2 entry */
|
||||
l2_offset = lseek(s->fd, 0, SEEK_END);
|
||||
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);
|
||||
lseek(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), SEEK_SET);
|
||||
if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
if (bdrv_pwrite(s->hd, s->l1_table_offset + l1_index * sizeof(tmp),
|
||||
&tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
new_l2_table = 1;
|
||||
}
|
||||
@@ -309,14 +303,13 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (min_index << s->l2_bits);
|
||||
lseek(s->fd, l2_offset, SEEK_SET);
|
||||
if (new_l2_table) {
|
||||
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
|
||||
if (write(s->fd, l2_table, 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 (read(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
||||
s->l2_size * sizeof(uint64_t))
|
||||
return 0;
|
||||
}
|
||||
@@ -337,36 +330,35 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
overwritten */
|
||||
if (decompress_cluster(s, cluster_offset) < 0)
|
||||
return 0;
|
||||
cluster_offset = lseek(s->fd, 0, SEEK_END);
|
||||
cluster_offset = bdrv_getlength(s->hd);
|
||||
cluster_offset = (cluster_offset + s->cluster_size - 1) &
|
||||
~(s->cluster_size - 1);
|
||||
/* write the cluster content */
|
||||
lseek(s->fd, cluster_offset, SEEK_SET);
|
||||
if (write(s->fd, s->cluster_cache, s->cluster_size) !=
|
||||
if (bdrv_pwrite(s->hd, cluster_offset, s->cluster_cache, s->cluster_size) !=
|
||||
s->cluster_size)
|
||||
return -1;
|
||||
} else {
|
||||
cluster_offset = lseek(s->fd, 0, SEEK_END);
|
||||
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);
|
||||
ftruncate(s->fd, cluster_offset + s->cluster_size);
|
||||
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, 0xaa, 512);
|
||||
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);
|
||||
lseek(s->fd, cluster_offset + i * 512, SEEK_SET);
|
||||
if (write(s->fd, s->cluster_data, 512) != 512)
|
||||
if (bdrv_pwrite(s->hd, cluster_offset + i * 512,
|
||||
s->cluster_data, 512) != 512)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -379,8 +371,8 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
/* update L2 table */
|
||||
tmp = cpu_to_be64(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
lseek(s->fd, l2_offset + l2_index * sizeof(tmp), SEEK_SET);
|
||||
if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
if (bdrv_pwrite(s->hd,
|
||||
l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
}
|
||||
return cluster_offset;
|
||||
@@ -438,8 +430,7 @@ static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
|
||||
if (s->cluster_cache_offset != coffset) {
|
||||
csize = cluster_offset >> (63 - s->cluster_bits);
|
||||
csize &= (s->cluster_size - 1);
|
||||
lseek(s->fd, coffset, SEEK_SET);
|
||||
ret = read(s->fd, s->cluster_data, csize);
|
||||
ret = bdrv_pread(s->hd, coffset, s->cluster_data, csize);
|
||||
if (ret != csize)
|
||||
return -1;
|
||||
if (decompress_buffer(s->cluster_cache, s->cluster_size,
|
||||
@@ -451,6 +442,8 @@ static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static int qcow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
@@ -465,14 +458,20 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
if (!cluster_offset) {
|
||||
memset(buf, 0, 512 * n);
|
||||
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 {
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
ret = read(s->fd, buf, n * 512);
|
||||
ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
if (s->crypt_method) {
|
||||
@@ -486,6 +485,7 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int qcow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
@@ -504,13 +504,13 @@ static int qcow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
index_in_cluster + n);
|
||||
if (!cluster_offset)
|
||||
return -1;
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
if (s->crypt_method) {
|
||||
encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
|
||||
&s->aes_encrypt_key);
|
||||
ret = write(s->fd, s->cluster_data, n * 512);
|
||||
ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512,
|
||||
s->cluster_data, n * 512);
|
||||
} else {
|
||||
ret = write(s->fd, buf, n * 512);
|
||||
ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
|
||||
}
|
||||
if (ret != n * 512)
|
||||
return -1;
|
||||
@@ -522,6 +522,209 @@ static int qcow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
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;
|
||||
@@ -529,7 +732,7 @@ static void qcow_close(BlockDriverState *bs)
|
||||
qemu_free(s->l2_cache);
|
||||
qemu_free(s->cluster_cache);
|
||||
qemu_free(s->cluster_data);
|
||||
close(s->fd);
|
||||
bdrv_delete(s->hd);
|
||||
}
|
||||
|
||||
static int qcow_create(const char *filename, int64_t total_size,
|
||||
@@ -537,12 +740,9 @@ static int qcow_create(const char *filename, int64_t total_size,
|
||||
{
|
||||
int fd, header_size, backing_filename_len, l1_size, i, shift;
|
||||
QCowHeader header;
|
||||
char backing_filename[1024];
|
||||
uint64_t tmp;
|
||||
struct stat st;
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||
0644);
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
memset(&header, 0, sizeof(header));
|
||||
@@ -552,25 +752,11 @@ static int qcow_create(const char *filename, int64_t total_size,
|
||||
header_size = sizeof(header);
|
||||
backing_filename_len = 0;
|
||||
if (backing_file) {
|
||||
const char *p;
|
||||
/* XXX: this is a hack: we do not attempt to check for URL
|
||||
like syntax */
|
||||
p = strchr(backing_file, ':');
|
||||
if (p && (p - backing_file) >= 2) {
|
||||
/* URL like but exclude "c:" like filenames */
|
||||
pstrcpy(backing_filename, sizeof(backing_filename),
|
||||
backing_file);
|
||||
} else {
|
||||
realpath(backing_file, backing_filename);
|
||||
if (stat(backing_filename, &st) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
header.mtime = cpu_to_be32(st.st_mtime);
|
||||
header.backing_file_offset = cpu_to_be64(header_size);
|
||||
backing_filename_len = strlen(backing_filename);
|
||||
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 */
|
||||
@@ -592,7 +778,7 @@ static int qcow_create(const char *filename, int64_t total_size,
|
||||
/* write all the data */
|
||||
write(fd, &header, sizeof(header));
|
||||
if (backing_file) {
|
||||
write(fd, backing_filename, backing_filename_len);
|
||||
write(fd, backing_file, backing_filename_len);
|
||||
}
|
||||
lseek(fd, header_size, SEEK_SET);
|
||||
tmp = 0;
|
||||
@@ -603,18 +789,30 @@ static int qcow_create(const char *filename, int64_t total_size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcow_get_cluster_size(BlockDriverState *bs)
|
||||
static int qcow_make_empty(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
if (bs->drv != &bdrv_qcow)
|
||||
return -1;
|
||||
return s->cluster_size;
|
||||
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 */
|
||||
int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf)
|
||||
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;
|
||||
@@ -622,8 +820,8 @@ int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *out_buf;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
if (bs->drv != &bdrv_qcow)
|
||||
return -1;
|
||||
if (nb_sectors != s->cluster_sectors)
|
||||
return -EINVAL;
|
||||
|
||||
out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
|
||||
if (!out_buf)
|
||||
@@ -661,8 +859,7 @@ int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num,
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
|
||||
out_len, 0, 0);
|
||||
cluster_offset &= s->cluster_offset_mask;
|
||||
lseek(s->fd, cluster_offset, SEEK_SET);
|
||||
if (write(s->fd, out_buf, out_len) != out_len) {
|
||||
if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) {
|
||||
qemu_free(out_buf);
|
||||
return -1;
|
||||
}
|
||||
@@ -672,17 +869,37 @@ int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num,
|
||||
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,
|
||||
qcow_read,
|
||||
qcow_write,
|
||||
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
+362
-47
@@ -22,6 +22,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "block_int.h"
|
||||
|
||||
@@ -59,7 +60,7 @@ typedef struct {
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
typedef struct BDRVVmdkState {
|
||||
int fd;
|
||||
BlockDriverState *hd;
|
||||
int64_t l1_table_offset;
|
||||
int64_t l1_backup_table_offset;
|
||||
uint32_t *l1_table;
|
||||
@@ -73,6 +74,7 @@ typedef struct BDRVVmdkState {
|
||||
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)
|
||||
@@ -89,27 +91,278 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_open(BlockDriverState *bs, const char *filename)
|
||||
#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;
|
||||
int fd, i;
|
||||
uint32_t magic;
|
||||
int l1_size;
|
||||
char desc[DESC_SIZE];
|
||||
uint32_t cid;
|
||||
char *p_name, *cid_str;
|
||||
size_t cid_str_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;
|
||||
bs->read_only = 1;
|
||||
/* 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 (read(fd, &magic, sizeof(magic)) != sizeof(magic))
|
||||
|
||||
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 (read(fd, &header, sizeof(header)) !=
|
||||
sizeof(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;
|
||||
@@ -120,11 +373,11 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
|
||||
s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
||||
} else if (magic == VMDK4_MAGIC) {
|
||||
VMDK4Header header;
|
||||
|
||||
if (read(fd, &header, sizeof(header)) != sizeof(header))
|
||||
|
||||
if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header))
|
||||
goto fail;
|
||||
bs->total_sectors = le32_to_cpu(header.capacity);
|
||||
s->cluster_sectors = le32_to_cpu(header.granularity);
|
||||
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)
|
||||
@@ -133,17 +386,22 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
|
||||
/ 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 (lseek(fd, s->l1_table_offset, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(fd, s->l1_table, l1_size) != l1_size)
|
||||
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]);
|
||||
@@ -153,9 +411,7 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
|
||||
s->l1_backup_table = qemu_malloc(l1_size);
|
||||
if (!s->l1_backup_table)
|
||||
goto fail;
|
||||
if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(fd, s->l1_backup_table, l1_size) != l1_size)
|
||||
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]);
|
||||
@@ -165,16 +421,43 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
|
||||
s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
|
||||
if (!s->l2_cache)
|
||||
goto fail;
|
||||
s->fd = fd;
|
||||
return 0;
|
||||
fail:
|
||||
qemu_free(s->l1_backup_table);
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
close(fd);
|
||||
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)
|
||||
{
|
||||
@@ -212,34 +495,41 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
l2_table = s->l2_cache + (min_index * s->l2_size);
|
||||
lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET);
|
||||
if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) !=
|
||||
s->l2_size * sizeof(uint32_t))
|
||||
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;
|
||||
cluster_offset = lseek(s->fd, 0, SEEK_END);
|
||||
ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9));
|
||||
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;
|
||||
lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
|
||||
if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(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];
|
||||
lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
|
||||
if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
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;
|
||||
@@ -265,9 +555,9 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret, index_in_cluster, n;
|
||||
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;
|
||||
@@ -275,11 +565,18 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
if (!cluster_offset) {
|
||||
memset(buf, 0, 512 * n);
|
||||
// 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 {
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
ret = read(s->fd, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
if(bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
|
||||
return -1;
|
||||
}
|
||||
nb_sectors -= n;
|
||||
@@ -293,8 +590,9 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret, index_in_cluster, n;
|
||||
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);
|
||||
@@ -304,13 +602,17 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
cluster_offset = get_cluster_offset(bs, sector_num << 9, 1);
|
||||
if (!cluster_offset)
|
||||
return -1;
|
||||
lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
||||
ret = write(s->fd, buf, n * 512);
|
||||
if (ret != n * 512)
|
||||
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;
|
||||
}
|
||||
@@ -334,7 +636,7 @@ static int vmdk_create(const char *filename, int64_t total_size,
|
||||
"# The Disk Data Base \n"
|
||||
"#DDB\n"
|
||||
"\n"
|
||||
"ddb.virtualHWVersion = \"3\"\n"
|
||||
"ddb.virtualHWVersion = \"4\"\n"
|
||||
"ddb.geometry.cylinders = \"%lu\"\n"
|
||||
"ddb.geometry.heads = \"16\"\n"
|
||||
"ddb.geometry.sectors = \"63\"\n"
|
||||
@@ -343,6 +645,9 @@ static int vmdk_create(const char *filename, int64_t total_size,
|
||||
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);
|
||||
@@ -421,9 +726,18 @@ static int vmdk_create(const char *filename, int64_t total_size,
|
||||
static void vmdk_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
|
||||
qemu_free(s->l1_table);
|
||||
qemu_free(s->l2_cache);
|
||||
close(s->fd);
|
||||
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 = {
|
||||
@@ -435,5 +749,6 @@ BlockDriver bdrv_vmdk = {
|
||||
vmdk_write,
|
||||
vmdk_close,
|
||||
vmdk_create,
|
||||
vmdk_flush,
|
||||
vmdk_is_allocated,
|
||||
};
|
||||
+6
-9
@@ -86,19 +86,16 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpc_open(BlockDriverState *bs, const char *filename)
|
||||
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_RDWR | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
bs->read_only = 1; // no write support yet
|
||||
|
||||
s->fd = fd;
|
||||
@@ -163,7 +160,7 @@ static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
bitmap_offset = 512 * s->pagetable[pagetable_index];
|
||||
block_offset = bitmap_offset + 512 + (512 * pageentry_index);
|
||||
|
||||
// printf("sector: %llx, index: %x, offset: %x, bioff: %llx, bloff: %llx\n",
|
||||
// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
|
||||
// sector_num, pagetable_index, pageentry_index,
|
||||
// bitmap_offset, block_offset);
|
||||
|
||||
|
||||
+2072
-1010
File diff suppressed because it is too large.
Load diff
+60
-5
@@ -28,7 +28,7 @@ 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 (*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,
|
||||
@@ -36,16 +36,54 @@ struct BlockDriver {
|
||||
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;
|
||||
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 inserted; /* if true, the media is present */
|
||||
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 */
|
||||
@@ -53,7 +91,7 @@ struct BlockDriverState {
|
||||
void (*change_cb)(void *opaque);
|
||||
void *change_opaque;
|
||||
|
||||
BlockDriver *drv;
|
||||
BlockDriver *drv; /* NULL means no media */
|
||||
void *opaque;
|
||||
|
||||
int boot_sector_enabled;
|
||||
@@ -63,8 +101,12 @@ struct BlockDriverState {
|
||||
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 */
|
||||
@@ -74,4 +116,17 @@ struct BlockDriverState {
|
||||
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 */
|
||||
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_.*!!'
|
||||
@@ -47,6 +47,9 @@ 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);
|
||||
|
||||
@@ -171,63 +174,175 @@ static void cocoa_resize(DisplayState *ds, int w, int h)
|
||||
------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int keymap[] =
|
||||
int keymap[] =
|
||||
{
|
||||
30, //'a' 0x0
|
||||
31, //'s'
|
||||
32, //'d'
|
||||
33, //'f'
|
||||
35, //'h'
|
||||
34, //'g'
|
||||
44, //'z'
|
||||
45, //'x'
|
||||
46, //'c'
|
||||
47, //'v'
|
||||
0, // 0 0x0a
|
||||
48, //'b'
|
||||
16, //'q'
|
||||
17, //'w'
|
||||
18, //'e'
|
||||
19, //'r'
|
||||
21, //'y' 0x10
|
||||
20, //'t'
|
||||
2, //'1'
|
||||
3, //'2'
|
||||
4, //'3'
|
||||
5, //'4'
|
||||
7, //'6'
|
||||
6, //'5'
|
||||
0, //'='
|
||||
10, //'9'
|
||||
8, //'7' 0x1A
|
||||
0, //'-'
|
||||
9, //'8'
|
||||
11, //'0'
|
||||
27, //']'
|
||||
24, //'o'
|
||||
22, //'u' 0x20
|
||||
26, //'['
|
||||
23, //'i'
|
||||
25, //'p'
|
||||
28, //'\n'
|
||||
38, //'l'
|
||||
36, //'j'
|
||||
40, //'"'
|
||||
37, //'k'
|
||||
39, //';'
|
||||
15, //'\t' 0x30
|
||||
0, //' '
|
||||
0, //'`'
|
||||
14, //'<backspace>'
|
||||
0, //'' 0x34
|
||||
0, //'<esc>'
|
||||
0, //'<esc>'
|
||||
/* Not completed to finish see http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
|
||||
// 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
|
||||
*/
|
||||
};
|
||||
|
||||
static int cocoa_keycode_to_qemu(int keycode)
|
||||
int cocoa_keycode_to_qemu(int keycode)
|
||||
{
|
||||
if(sizeof(keymap) <= keycode)
|
||||
if((sizeof(keymap)/sizeof(int)) <= keycode)
|
||||
{
|
||||
printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
|
||||
return 0;
|
||||
@@ -246,53 +361,252 @@ static void cocoa_refresh(DisplayState *ds)
|
||||
NSDate *distantPast;
|
||||
NSEvent *event;
|
||||
NSAutoreleasePool *pool;
|
||||
int grab = 1;
|
||||
|
||||
pool = [ [ NSAutoreleasePool alloc ] init ];
|
||||
distantPast = [ NSDate distantPast ];
|
||||
|
||||
if (is_active_console(vga_console))
|
||||
vga_update_display();
|
||||
vga_hw_update();
|
||||
|
||||
do {
|
||||
event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
|
||||
inMode: NSDefaultRunLoopMode dequeue:YES ];
|
||||
if (event != nil) {
|
||||
switch ([event type]) {
|
||||
case NSKeyDown:
|
||||
if(grab)
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
|
||||
if (keycode & 0x80)
|
||||
kbd_put_keycode(0xe0);
|
||||
kbd_put_keycode(keycode & 0x7f);
|
||||
}
|
||||
break;
|
||||
case NSKeyUp:
|
||||
if(grab)
|
||||
case NSFlagsChanged:
|
||||
{
|
||||
int keycode = cocoa_keycode_to_qemu([event keyCode]);
|
||||
|
||||
if (keycode & 0x80)
|
||||
kbd_put_keycode(0xe0);
|
||||
kbd_put_keycode(keycode | 0x80);
|
||||
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 NSScrollWheel:
|
||||
|
||||
case NSLeftMouseDown:
|
||||
case NSLeftMouseUp:
|
||||
|
||||
case NSOtherMouseDown:
|
||||
case NSRightMouseDown:
|
||||
|
||||
case NSOtherMouseUp:
|
||||
case NSRightMouseUp:
|
||||
|
||||
|
||||
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:
|
||||
case NSOtherMouseDragged:
|
||||
case NSRightMouseDragged:
|
||||
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];
|
||||
}
|
||||
@@ -570,10 +884,9 @@ static void setupWindowMenu(void)
|
||||
/* Finally give up our references to the objects */
|
||||
[windowMenu release];
|
||||
[windowMenuItem release];
|
||||
|
||||
}
|
||||
|
||||
static void CustomApplicationMain (argc, argv)
|
||||
static void CustomApplicationMain(void)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
QemuCocoaGUIController *gui_controller;
|
||||
@@ -607,8 +920,8 @@ int main(int argc, char **argv)
|
||||
{
|
||||
gArgc = argc;
|
||||
gArgv = argv;
|
||||
|
||||
CustomApplicationMain (argc, argv);
|
||||
|
||||
|
||||
CustomApplicationMain();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -22,9 +22,12 @@ 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=""
|
||||
@@ -50,7 +53,7 @@ case "$cpu" in
|
||||
s390)
|
||||
cpu="s390"
|
||||
;;
|
||||
sparc)
|
||||
sparc|sun4[muv])
|
||||
cpu="sparc"
|
||||
;;
|
||||
sparc64)
|
||||
@@ -77,21 +80,31 @@ 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"
|
||||
kernel_path=""
|
||||
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"
|
||||
CFLAGS="-O2 -mno-cygwin"
|
||||
OS_CFLAGS="-mno-cygwin"
|
||||
;;
|
||||
MINGW32*)
|
||||
mingw32="yes"
|
||||
@@ -99,7 +112,7 @@ mingw32="yes"
|
||||
FreeBSD)
|
||||
bsd="yes"
|
||||
oss="yes"
|
||||
if [ "$cpu" = "i386" ] ; then
|
||||
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
|
||||
kqemu="yes"
|
||||
fi
|
||||
;;
|
||||
@@ -114,10 +127,18 @@ 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
|
||||
@@ -125,45 +146,65 @@ fi
|
||||
esac
|
||||
|
||||
if [ "$bsd" = "yes" ] ; then
|
||||
if [ ! "$darwin" = "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
|
||||
# XXX: we assume an absolute path is given when launching configure,
|
||||
# except in './configure' case.
|
||||
source_path=${0%configure}
|
||||
source_path=${source_path%/}
|
||||
source_path_used="yes"
|
||||
if test -z "$source_path" -o "$source_path" = "." ; then
|
||||
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
|
||||
--prefix=*) prefix=`echo $opt | cut -d '=' -f 2`
|
||||
--help|-h) show_help=yes
|
||||
;;
|
||||
--interp-prefix=*) interp_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
--prefix=*) prefix="$optarg"
|
||||
;;
|
||||
--source-path=*) source_path=`echo $opt | cut -d '=' -f 2`
|
||||
--interp-prefix=*) interp_prefix="$optarg"
|
||||
;;
|
||||
--cross-prefix=*) cross_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
--source-path=*) source_path="$optarg"
|
||||
source_path_used="yes"
|
||||
;;
|
||||
--cc=*) cc=`echo $opt | cut -d '=' -f 2`
|
||||
--cross-prefix=*) cross_prefix="$optarg"
|
||||
;;
|
||||
--host-cc=*) host_cc=`echo $opt | cut -d '=' -f 2`
|
||||
--cc=*) cc="$optarg"
|
||||
gcc3_search="no"
|
||||
;;
|
||||
--make=*) make=`echo $opt | cut -d '=' -f 2`
|
||||
--host-cc=*) host_cc="$optarg"
|
||||
;;
|
||||
--extra-cflags=*) CFLAGS="${opt#--extra-cflags=}"
|
||||
--make=*) make="$optarg"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="${opt#--extra-ldflags=}"
|
||||
--install=*) install="$optarg"
|
||||
;;
|
||||
--cpu=*) cpu=`echo $opt | cut -d '=' -f 2`
|
||||
--extra-cflags=*) CFLAGS="$optarg"
|
||||
;;
|
||||
--target-list=*) target_list=${opt#--target-list=}
|
||||
--extra-ldflags=*) LDFLAGS="$optarg"
|
||||
;;
|
||||
--cpu=*) cpu="$optarg"
|
||||
;;
|
||||
--target-list=*) target_list="$optarg"
|
||||
;;
|
||||
--enable-gprof) gprof="yes"
|
||||
;;
|
||||
@@ -171,57 +212,225 @@ for opt do
|
||||
;;
|
||||
--disable-sdl) sdl="no"
|
||||
;;
|
||||
--enable-coreaudio) coreaudio="yes"
|
||||
;;
|
||||
--enable-alsa) alsa="yes"
|
||||
;;
|
||||
--enable-dsound) dsound="yes"
|
||||
;;
|
||||
--enable-fmod) fmod="yes"
|
||||
;;
|
||||
--fmod-lib=*) fmod_lib=${opt#--fmod-lib=}
|
||||
--fmod-lib=*) fmod_lib="$optarg"
|
||||
;;
|
||||
--fmod-inc=*) fmod_inc=${opt#--fmod-inc=}
|
||||
--fmod-inc=*) fmod_inc="$optarg"
|
||||
;;
|
||||
--enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" ; user="no"
|
||||
;;
|
||||
--enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-"
|
||||
;;
|
||||
--disable-slirp) slirp="no"
|
||||
;;
|
||||
;;
|
||||
--enable-adlib) adlib="yes"
|
||||
;;
|
||||
;;
|
||||
--disable-kqemu) kqemu="no"
|
||||
;;
|
||||
--kernel-path=*) kernel_path=${opt#--kernel-path=}
|
||||
;;
|
||||
--enable-cocoa) cocoa="yes" ; sdl="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
|
||||
|
||||
# Checking for CFLAGS
|
||||
if test -z "$CFLAGS"; then
|
||||
CFLAGS="-O2"
|
||||
# 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"
|
||||
gdbstub="no"
|
||||
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
|
||||
target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu"
|
||||
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" = "yes" ] ; then
|
||||
target_list="i386-user arm-user armeb-user sparc-user ppc-user $target_list"
|
||||
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')
|
||||
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
|
||||
@@ -231,8 +440,8 @@ if test -z "$cross_prefix" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <inttypes.h>
|
||||
int main(int argc, char ** argv){
|
||||
volatile uint32_t i=0x01234567;
|
||||
return (*((uint8_t*)(&i))) == 0x67;
|
||||
volatile uint32_t i=0x01234567;
|
||||
return (*((uint8_t*)(&i))) == 0x67;
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -296,7 +505,9 @@ _sdlversion=`$sdl_config --version | sed 's/[^0-9]//g'`
|
||||
if test "$_sdlversion" -lt 121 ; then
|
||||
sdl_too_old=yes
|
||||
else
|
||||
sdl=yes
|
||||
if test "$cocoa" = "no" ; then
|
||||
sdl=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
# static link with sdl ?
|
||||
@@ -317,41 +528,37 @@ 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
|
||||
|
||||
if test x"$1" = x"-h" -o x"$1" = x"--help" ; then
|
||||
cat << EOF
|
||||
|
||||
Usage: configure [options]
|
||||
Options: [defaults in brackets after descriptions]
|
||||
##########################################
|
||||
# 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
|
||||
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 build"
|
||||
echo " --kernel-path=PATH set the kernel path (configure probes it)"
|
||||
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 [$cc] for dyngen etc."
|
||||
echo " --make=MAKE use specified make [$make]"
|
||||
echo " --static enable static build [$static]"
|
||||
echo " --enable-mingw32 enable Win32 cross compilation with mingw32"
|
||||
echo " --enable-adlib enable Adlib emulation"
|
||||
echo " --enable-fmod enable FMOD audio output driver"
|
||||
echo " --fmod-lib path to FMOD library"
|
||||
echo " --fmod-inc path to FMOD includes"
|
||||
echo ""
|
||||
echo "NOTE: The object files are build at the place where configure is launched"
|
||||
exit 1
|
||||
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
|
||||
@@ -372,48 +579,6 @@ docdir="$prefix/share/doc/qemu"
|
||||
bindir="$prefix/bin"
|
||||
fi
|
||||
|
||||
# kqemu support
|
||||
if test $kqemu = "yes" ; then
|
||||
# test if the source code is installed
|
||||
if test '!' -f "kqemu/Makefile" ; then
|
||||
kqemu="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Linux specific kqemu configuration
|
||||
if test $kqemu = "yes" -a $linux = "yes" ; then
|
||||
# find the kernel path
|
||||
if test -z "$kernel_path" ; then
|
||||
kernel_version=`uname -r`
|
||||
kernel_path="/lib/modules/$kernel_version/build"
|
||||
if test '!' -d "$kernel_path/include" ; then
|
||||
kernel_path="/usr/src/linux"
|
||||
if test '!' -d "$kernel_path/include" ; then
|
||||
echo "Could not find kernel includes in /lib/modules or /usr/src/linux - cannot build the kqemu module"
|
||||
kqemu="no"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $kqemu = "yes" ; then
|
||||
|
||||
# test that the kernel config is present
|
||||
if test '!' -f "$kernel_path/Makefile" ; then
|
||||
echo "No Makefile file present in $kernel_path - kqemu cannot be built"
|
||||
kqemu="no"
|
||||
fi
|
||||
|
||||
# find build system (2.6 or legacy)
|
||||
kbuild26="yes"
|
||||
if grep -q "PATCHLEVEL = 4" $kernel_path/Makefile ; then
|
||||
kbuild26="no"
|
||||
fi
|
||||
|
||||
fi # kqemu
|
||||
|
||||
fi # kqemu and linux
|
||||
|
||||
|
||||
echo "Install prefix $prefix"
|
||||
echo "BIOS directory $datadir"
|
||||
echo "binary directory $bindir"
|
||||
@@ -425,10 +590,12 @@ 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"
|
||||
@@ -439,23 +606,26 @@ if test "$sdl" != "no" ; then
|
||||
fi
|
||||
echo "mingw32 support $mingw32"
|
||||
echo "Adlib support $adlib"
|
||||
echo -n "FMOD support $fmod"
|
||||
if test $fmod = "yes"; then
|
||||
echo -n " (lib='$fmod_lib' include='$fmod_inc')"
|
||||
fi
|
||||
echo ""
|
||||
echo "kqemu support $kqemu"
|
||||
if test $kqemu = "yes" -a $linux = "yes" ; then
|
||||
echo ""
|
||||
echo "KQEMU Linux module configuration:"
|
||||
echo "kernel sources $kernel_path"
|
||||
echo -n "kbuild type "
|
||||
if test $kbuild26 = "yes"; then
|
||||
echo "2.6"
|
||||
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
|
||||
echo "2.4"
|
||||
fi
|
||||
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"
|
||||
@@ -463,13 +633,13 @@ 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
|
||||
@@ -479,6 +649,7 @@ 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
|
||||
@@ -486,6 +657,7 @@ 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
|
||||
@@ -544,6 +716,10 @@ 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
|
||||
@@ -556,6 +732,9 @@ 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
|
||||
@@ -568,30 +747,36 @@ 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
|
||||
echo -n "VERSION=" >>$config_mak
|
||||
head $source_path/VERSION >>$config_mak
|
||||
echo "" >>$config_mak
|
||||
echo -n "#define QEMU_VERSION \"" >> $config_h
|
||||
head $source_path/VERSION >> $config_h
|
||||
echo "\"" >> $config_h
|
||||
qemu_version=`head $source_path/VERSION`
|
||||
echo "VERSION=$qemu_version" >>$config_mak
|
||||
echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
|
||||
|
||||
if test $kqemu = "yes" ; then
|
||||
echo "CONFIG_KQEMU=yes" >> $config_mak
|
||||
if test $linux = "yes" ; then
|
||||
echo "KERNEL_PATH=$kernel_path" >> $config_mak
|
||||
if test $kbuild26 = "yes" ; then
|
||||
echo "CONFIG_KBUILD26=yes" >> $config_mak
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
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
|
||||
@@ -600,8 +785,9 @@ if [ "$bsd" = "yes" ] ; then
|
||||
echo "#define _BSD 1" >> $config_h
|
||||
fi
|
||||
|
||||
for target in $target_list; do
|
||||
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
|
||||
@@ -613,6 +799,8 @@ target_bigendian="no"
|
||||
[ "$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"
|
||||
@@ -622,10 +810,20 @@ 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
|
||||
-a "$sdl" = "no" -a "$cocoa" = "no" ; then
|
||||
echo "ERROR: QEMU requires SDL or Cocoa for graphical output"
|
||||
echo "To build QEMU with graphical output configure with --disable-gfx-check"
|
||||
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
|
||||
@@ -634,14 +832,19 @@ fi
|
||||
|
||||
mkdir -p $target_dir
|
||||
mkdir -p $target_dir/fpu
|
||||
if test "$target" = "arm-user" -o "$target" = "armeb-user" ; then
|
||||
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
|
||||
|
||||
ln -sf $source_path/Makefile.target $target_dir/Makefile
|
||||
#
|
||||
# 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
|
||||
@@ -650,6 +853,7 @@ 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
|
||||
|
||||
@@ -664,6 +868,7 @@ 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
|
||||
@@ -690,10 +895,22 @@ elif test "$target_cpu" = "x86_64" ; then
|
||||
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" ; then
|
||||
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
|
||||
@@ -710,11 +927,23 @@ 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" ; then
|
||||
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
|
||||
@@ -731,11 +960,11 @@ if test "$target_user_only" = "no"; then
|
||||
else
|
||||
echo "SDL_LIBS=`$sdl_config --libs`" >> $config_mak
|
||||
fi
|
||||
echo -n "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak
|
||||
if [ "${aa}" = "yes" ] ; then
|
||||
echo -n " `aalib-config --cflags`" >> $config_mak ;
|
||||
echo "SDL_CFLAGS=`$sdl_config --cflags` `aalib-config --cflags`" >> $config_mak
|
||||
else
|
||||
echo "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak
|
||||
fi
|
||||
echo "" >> $config_mak
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -753,8 +982,10 @@ if test "$source_path_used" = "yes" ; then
|
||||
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
|
||||
ln -sf $source_path/$f $f
|
||||
rm -f $f
|
||||
ln -s $source_path/$f $f
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
@@ -23,16 +23,26 @@
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_CONSOLE
|
||||
#define DEFAULT_BACKSCROLL 512
|
||||
#define MAX_CONSOLES 12
|
||||
|
||||
#define RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
|
||||
#define RGB(r, g, b) RGBA(r, g, b, 0xff)
|
||||
#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
|
||||
#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
|
||||
|
||||
typedef struct TextAttributes {
|
||||
uint8_t fgcol:4;
|
||||
uint8_t bgcol:4;
|
||||
uint8_t bold:1;
|
||||
uint8_t uline:1;
|
||||
uint8_t blink:1;
|
||||
uint8_t invers:1;
|
||||
uint8_t unvisible:1;
|
||||
} TextAttributes;
|
||||
|
||||
typedef struct TextCell {
|
||||
uint8_t ch;
|
||||
uint8_t bgcol:4;
|
||||
uint8_t fgcol:4;
|
||||
TextAttributes t_attrib;
|
||||
} TextCell;
|
||||
|
||||
#define MAX_ESC_PARAMS 3
|
||||
@@ -43,34 +53,116 @@ enum TTYState {
|
||||
TTY_STATE_CSI,
|
||||
};
|
||||
|
||||
typedef struct QEMUFIFO {
|
||||
uint8_t *buf;
|
||||
int buf_size;
|
||||
int count, wptr, rptr;
|
||||
} QEMUFIFO;
|
||||
|
||||
int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
|
||||
{
|
||||
int l, len;
|
||||
|
||||
l = f->buf_size - f->count;
|
||||
if (len1 > l)
|
||||
len1 = l;
|
||||
len = len1;
|
||||
while (len > 0) {
|
||||
l = f->buf_size - f->wptr;
|
||||
if (l > len)
|
||||
l = len;
|
||||
memcpy(f->buf + f->wptr, buf, l);
|
||||
f->wptr += l;
|
||||
if (f->wptr >= f->buf_size)
|
||||
f->wptr = 0;
|
||||
buf += l;
|
||||
len -= l;
|
||||
}
|
||||
f->count += len1;
|
||||
return len1;
|
||||
}
|
||||
|
||||
int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
|
||||
{
|
||||
int l, len;
|
||||
|
||||
if (len1 > f->count)
|
||||
len1 = f->count;
|
||||
len = len1;
|
||||
while (len > 0) {
|
||||
l = f->buf_size - f->rptr;
|
||||
if (l > len)
|
||||
l = len;
|
||||
memcpy(buf, f->buf + f->rptr, l);
|
||||
f->rptr += l;
|
||||
if (f->rptr >= f->buf_size)
|
||||
f->rptr = 0;
|
||||
buf += l;
|
||||
len -= l;
|
||||
}
|
||||
f->count -= len1;
|
||||
return len1;
|
||||
}
|
||||
|
||||
/* ??? This is mis-named.
|
||||
It is used for both text and graphical consoles. */
|
||||
struct TextConsole {
|
||||
int text_console; /* true if text console */
|
||||
DisplayState *ds;
|
||||
/* Graphic console state. */
|
||||
vga_hw_update_ptr hw_update;
|
||||
vga_hw_invalidate_ptr hw_invalidate;
|
||||
vga_hw_screen_dump_ptr hw_screen_dump;
|
||||
void *hw;
|
||||
|
||||
int g_width, g_height;
|
||||
int width;
|
||||
int height;
|
||||
int total_height;
|
||||
int backscroll_height;
|
||||
int fgcol;
|
||||
int bgcol;
|
||||
int x, y;
|
||||
int x_saved, y_saved;
|
||||
int y_displayed;
|
||||
int y_base;
|
||||
TextAttributes t_attrib_default; /* default text attributes */
|
||||
TextAttributes t_attrib; /* currently active text attributes */
|
||||
TextCell *cells;
|
||||
|
||||
enum TTYState state;
|
||||
int esc_params[MAX_ESC_PARAMS];
|
||||
int nb_esc_params;
|
||||
|
||||
/* kbd read handler */
|
||||
IOReadHandler *fd_read;
|
||||
void *fd_opaque;
|
||||
CharDriverState *chr;
|
||||
/* fifo for key pressed */
|
||||
QEMUFIFO out_fifo;
|
||||
uint8_t out_fifo_buf[16];
|
||||
QEMUTimer *kbd_timer;
|
||||
};
|
||||
|
||||
static TextConsole *active_console;
|
||||
static TextConsole *consoles[MAX_CONSOLES];
|
||||
static int nb_consoles = 0;
|
||||
|
||||
void vga_hw_update(void)
|
||||
{
|
||||
if (active_console && active_console->hw_update)
|
||||
active_console->hw_update(active_console->hw);
|
||||
}
|
||||
|
||||
void vga_hw_invalidate(void)
|
||||
{
|
||||
if (active_console->hw_invalidate)
|
||||
active_console->hw_invalidate(active_console->hw);
|
||||
}
|
||||
|
||||
void vga_hw_screen_dump(const char *filename)
|
||||
{
|
||||
/* There is currently no was of specifying which screen we want to dump,
|
||||
so always dump the dirst one. */
|
||||
if (consoles[0]->hw_screen_dump)
|
||||
consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
|
||||
}
|
||||
|
||||
/* convert a RGBA color to a color index usable in graphic primitives */
|
||||
static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
|
||||
{
|
||||
@@ -221,17 +313,40 @@ static const uint32_t dmask4[4] = {
|
||||
PAT(0xffffffff),
|
||||
};
|
||||
|
||||
static uint32_t color_table[8];
|
||||
static uint32_t color_table[2][8];
|
||||
|
||||
static const uint32_t color_table_rgb[8] = {
|
||||
RGB(0x00, 0x00, 0x00),
|
||||
RGB(0xff, 0x00, 0x00),
|
||||
RGB(0x00, 0xff, 0x00),
|
||||
RGB(0xff, 0xff, 0x00),
|
||||
RGB(0x00, 0x00, 0xff),
|
||||
RGB(0xff, 0x00, 0xff),
|
||||
RGB(0x00, 0xff, 0xff),
|
||||
RGB(0xff, 0xff, 0xff),
|
||||
enum color_names {
|
||||
COLOR_BLACK = 0,
|
||||
COLOR_RED = 1,
|
||||
COLOR_GREEN = 2,
|
||||
COLOR_YELLOW = 3,
|
||||
COLOR_BLUE = 4,
|
||||
COLOR_MAGENTA = 5,
|
||||
COLOR_CYAN = 6,
|
||||
COLOR_WHITE = 7
|
||||
};
|
||||
|
||||
static const uint32_t color_table_rgb[2][8] = {
|
||||
{ /* dark */
|
||||
QEMU_RGB(0x00, 0x00, 0x00), /* black */
|
||||
QEMU_RGB(0xaa, 0x00, 0x00), /* red */
|
||||
QEMU_RGB(0x00, 0xaa, 0x00), /* green */
|
||||
QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
|
||||
QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
|
||||
QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
|
||||
QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
|
||||
QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
|
||||
},
|
||||
{ /* bright */
|
||||
QEMU_RGB(0x00, 0x00, 0x00), /* black */
|
||||
QEMU_RGB(0xff, 0x00, 0x00), /* red */
|
||||
QEMU_RGB(0x00, 0xff, 0x00), /* green */
|
||||
QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
|
||||
QEMU_RGB(0x00, 0x00, 0xff), /* blue */
|
||||
QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
|
||||
QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
|
||||
QEMU_RGB(0xff, 0xff, 0xff), /* white */
|
||||
}
|
||||
};
|
||||
|
||||
static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
|
||||
@@ -251,14 +366,60 @@ static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
|
||||
|
||||
return col;
|
||||
}
|
||||
#ifdef DEBUG_CONSOLE
|
||||
static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
|
||||
{
|
||||
if (t_attrib->bold) {
|
||||
printf("b");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
if (t_attrib->uline) {
|
||||
printf("u");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
if (t_attrib->blink) {
|
||||
printf("l");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
if (t_attrib->invers) {
|
||||
printf("i");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
if (t_attrib->unvisible) {
|
||||
printf("n");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
|
||||
unsigned int fgcol, unsigned int bgcol)
|
||||
TextAttributes *t_attrib)
|
||||
{
|
||||
uint8_t *d;
|
||||
const uint8_t *font_ptr;
|
||||
unsigned int font_data, linesize, xorcol, bpp;
|
||||
int i;
|
||||
unsigned int fgcol, bgcol;
|
||||
|
||||
#ifdef DEBUG_CONSOLE
|
||||
printf("x: %2i y: %2i", x, y);
|
||||
console_print_text_attributes(t_attrib, ch);
|
||||
#endif
|
||||
|
||||
if (t_attrib->invers) {
|
||||
bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
|
||||
fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
|
||||
} else {
|
||||
fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
|
||||
bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
|
||||
}
|
||||
|
||||
bpp = (ds->depth + 7) >> 3;
|
||||
d = ds->data +
|
||||
@@ -270,6 +431,10 @@ static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
|
||||
case 8:
|
||||
for(i = 0; i < FONT_HEIGHT; i++) {
|
||||
font_data = *font_ptr++;
|
||||
if (t_attrib->uline
|
||||
&& ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
|
||||
font_data = 0xFFFF;
|
||||
}
|
||||
((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
|
||||
d += linesize;
|
||||
@@ -279,6 +444,10 @@ static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
|
||||
case 15:
|
||||
for(i = 0; i < FONT_HEIGHT; i++) {
|
||||
font_data = *font_ptr++;
|
||||
if (t_attrib->uline
|
||||
&& ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
|
||||
font_data = 0xFFFF;
|
||||
}
|
||||
((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
|
||||
@@ -289,6 +458,9 @@ static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
|
||||
case 32:
|
||||
for(i = 0; i < FONT_HEIGHT; i++) {
|
||||
font_data = *font_ptr++;
|
||||
if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
|
||||
font_data = 0xFFFF;
|
||||
}
|
||||
((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
|
||||
((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
|
||||
@@ -327,8 +499,7 @@ static void text_console_resize(TextConsole *s)
|
||||
}
|
||||
for(x = w1; x < s->width; x++) {
|
||||
c->ch = ' ';
|
||||
c->fgcol = 7;
|
||||
c->bgcol = 0;
|
||||
c->t_attrib = s->t_attrib_default;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
@@ -349,7 +520,7 @@ static void update_xy(TextConsole *s, int x, int y)
|
||||
if (y2 < s->height) {
|
||||
c = &s->cells[y1 * s->width + x];
|
||||
vga_putcharxy(s->ds, x, y2, c->ch,
|
||||
color_table[c->fgcol], color_table[c->bgcol]);
|
||||
&(c->t_attrib));
|
||||
dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
|
||||
FONT_WIDTH, FONT_HEIGHT);
|
||||
}
|
||||
@@ -369,11 +540,12 @@ static void console_show_cursor(TextConsole *s, int show)
|
||||
if (y < s->height) {
|
||||
c = &s->cells[y1 * s->width + s->x];
|
||||
if (show) {
|
||||
vga_putcharxy(s->ds, s->x, y, c->ch,
|
||||
color_table[0], color_table[7]);
|
||||
TextAttributes t_attrib = s->t_attrib_default;
|
||||
t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
|
||||
vga_putcharxy(s->ds, s->x, y, c->ch, &t_attrib);
|
||||
} else {
|
||||
vga_putcharxy(s->ds, s->x, y, c->ch,
|
||||
color_table[c->fgcol], color_table[c->bgcol]);
|
||||
&(c->t_attrib));
|
||||
}
|
||||
dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
|
||||
FONT_WIDTH, FONT_HEIGHT);
|
||||
@@ -390,13 +562,13 @@ static void console_refresh(TextConsole *s)
|
||||
return;
|
||||
|
||||
vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
|
||||
color_table[0]);
|
||||
color_table[0][COLOR_BLACK]);
|
||||
y1 = s->y_displayed;
|
||||
for(y = 0; y < s->height; y++) {
|
||||
c = s->cells + y1 * s->width;
|
||||
for(x = 0; x < s->width; x++) {
|
||||
vga_putcharxy(s->ds, x, y, c->ch,
|
||||
color_table[c->fgcol], color_table[c->bgcol]);
|
||||
&(c->t_attrib));
|
||||
c++;
|
||||
}
|
||||
if (++y1 == s->total_height)
|
||||
@@ -445,11 +617,10 @@ static void console_put_lf(TextConsole *s)
|
||||
TextCell *c;
|
||||
int x, y1;
|
||||
|
||||
s->x = 0;
|
||||
s->y++;
|
||||
if (s->y >= s->height) {
|
||||
s->y = s->height - 1;
|
||||
|
||||
|
||||
if (s->y_displayed == s->y_base) {
|
||||
if (++s->y_displayed == s->total_height)
|
||||
s->y_displayed = 0;
|
||||
@@ -462,8 +633,7 @@ static void console_put_lf(TextConsole *s)
|
||||
c = &s->cells[y1 * s->width];
|
||||
for(x = 0; x < s->width; x++) {
|
||||
c->ch = ' ';
|
||||
c->fgcol = s->fgcol;
|
||||
c->bgcol = s->bgcol;
|
||||
c->t_attrib = s->t_attrib_default;
|
||||
c++;
|
||||
}
|
||||
if (s == active_console && s->y_displayed == s->y_base) {
|
||||
@@ -472,44 +642,179 @@ static void console_put_lf(TextConsole *s)
|
||||
(s->height - 1) * FONT_HEIGHT);
|
||||
vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
|
||||
s->width * FONT_WIDTH, FONT_HEIGHT,
|
||||
color_table[s->bgcol]);
|
||||
color_table[0][s->t_attrib_default.bgcol]);
|
||||
dpy_update(s->ds, 0, 0,
|
||||
s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set console attributes depending on the current escape codes.
|
||||
* NOTE: I know this code is not very efficient (checking every color for it
|
||||
* self) but it is more readable and better maintainable.
|
||||
*/
|
||||
static void console_handle_escape(TextConsole *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<s->nb_esc_params; i++) {
|
||||
switch (s->esc_params[i]) {
|
||||
case 0: /* reset all console attributes to default */
|
||||
s->t_attrib = s->t_attrib_default;
|
||||
break;
|
||||
case 1:
|
||||
s->t_attrib.bold = 1;
|
||||
break;
|
||||
case 4:
|
||||
s->t_attrib.uline = 1;
|
||||
break;
|
||||
case 5:
|
||||
s->t_attrib.blink = 1;
|
||||
break;
|
||||
case 7:
|
||||
s->t_attrib.invers = 1;
|
||||
break;
|
||||
case 8:
|
||||
s->t_attrib.unvisible = 1;
|
||||
break;
|
||||
case 22:
|
||||
s->t_attrib.bold = 0;
|
||||
break;
|
||||
case 24:
|
||||
s->t_attrib.uline = 0;
|
||||
break;
|
||||
case 25:
|
||||
s->t_attrib.blink = 0;
|
||||
break;
|
||||
case 27:
|
||||
s->t_attrib.invers = 0;
|
||||
break;
|
||||
case 28:
|
||||
s->t_attrib.unvisible = 0;
|
||||
break;
|
||||
/* set foreground color */
|
||||
case 30:
|
||||
s->t_attrib.fgcol=COLOR_BLACK;
|
||||
break;
|
||||
case 31:
|
||||
s->t_attrib.fgcol=COLOR_RED;
|
||||
break;
|
||||
case 32:
|
||||
s->t_attrib.fgcol=COLOR_GREEN;
|
||||
break;
|
||||
case 33:
|
||||
s->t_attrib.fgcol=COLOR_YELLOW;
|
||||
break;
|
||||
case 34:
|
||||
s->t_attrib.fgcol=COLOR_BLUE;
|
||||
break;
|
||||
case 35:
|
||||
s->t_attrib.fgcol=COLOR_MAGENTA;
|
||||
break;
|
||||
case 36:
|
||||
s->t_attrib.fgcol=COLOR_CYAN;
|
||||
break;
|
||||
case 37:
|
||||
s->t_attrib.fgcol=COLOR_WHITE;
|
||||
break;
|
||||
/* set background color */
|
||||
case 40:
|
||||
s->t_attrib.bgcol=COLOR_BLACK;
|
||||
break;
|
||||
case 41:
|
||||
s->t_attrib.bgcol=COLOR_RED;
|
||||
break;
|
||||
case 42:
|
||||
s->t_attrib.bgcol=COLOR_GREEN;
|
||||
break;
|
||||
case 43:
|
||||
s->t_attrib.bgcol=COLOR_YELLOW;
|
||||
break;
|
||||
case 44:
|
||||
s->t_attrib.bgcol=COLOR_BLUE;
|
||||
break;
|
||||
case 45:
|
||||
s->t_attrib.bgcol=COLOR_MAGENTA;
|
||||
break;
|
||||
case 46:
|
||||
s->t_attrib.bgcol=COLOR_CYAN;
|
||||
break;
|
||||
case 47:
|
||||
s->t_attrib.bgcol=COLOR_WHITE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void console_clear_xy(TextConsole *s, int x, int y)
|
||||
{
|
||||
int y1 = (s->y_base + y) % s->total_height;
|
||||
TextCell *c = &s->cells[y1 * s->width + x];
|
||||
c->ch = ' ';
|
||||
c->t_attrib = s->t_attrib_default;
|
||||
c++;
|
||||
update_xy(s, x, y);
|
||||
}
|
||||
|
||||
static void console_putchar(TextConsole *s, int ch)
|
||||
{
|
||||
TextCell *c;
|
||||
int y1, i, x;
|
||||
int y1, i;
|
||||
int x, y;
|
||||
|
||||
switch(s->state) {
|
||||
case TTY_STATE_NORM:
|
||||
switch(ch) {
|
||||
case '\r':
|
||||
case '\r': /* carriage return */
|
||||
s->x = 0;
|
||||
break;
|
||||
case '\n':
|
||||
case '\n': /* newline */
|
||||
console_put_lf(s);
|
||||
break;
|
||||
case 27:
|
||||
case '\b': /* backspace */
|
||||
if (s->x > 0)
|
||||
s->x--;
|
||||
break;
|
||||
case '\t': /* tabspace */
|
||||
if (s->x + (8 - (s->x % 8)) > s->width) {
|
||||
s->x = 0;
|
||||
console_put_lf(s);
|
||||
} else {
|
||||
s->x = s->x + (8 - (s->x % 8));
|
||||
}
|
||||
break;
|
||||
case '\a': /* alert aka. bell */
|
||||
/* TODO: has to be implemented */
|
||||
break;
|
||||
case 14:
|
||||
/* SI (shift in), character set 0 (ignored) */
|
||||
break;
|
||||
case 15:
|
||||
/* SO (shift out), character set 1 (ignored) */
|
||||
break;
|
||||
case 27: /* esc (introducing an escape sequence) */
|
||||
s->state = TTY_STATE_ESC;
|
||||
break;
|
||||
default:
|
||||
if (s->x >= s->width - 1) {
|
||||
break;
|
||||
}
|
||||
y1 = (s->y_base + s->y) % s->total_height;
|
||||
c = &s->cells[y1 * s->width + s->x];
|
||||
c->ch = ch;
|
||||
c->fgcol = s->fgcol;
|
||||
c->bgcol = s->bgcol;
|
||||
c->t_attrib = s->t_attrib;
|
||||
update_xy(s, s->x, s->y);
|
||||
s->x++;
|
||||
if (s->x >= s->width)
|
||||
#if 0 /* line wrap disabled */
|
||||
if (s->x >= s->width) {
|
||||
s->x = 0;
|
||||
console_put_lf(s);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TTY_STATE_ESC:
|
||||
case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
|
||||
if (ch == '[') {
|
||||
for(i=0;i<MAX_ESC_PARAMS;i++)
|
||||
s->esc_params[i] = 0;
|
||||
@@ -519,7 +824,7 @@ static void console_putchar(TextConsole *s, int ch)
|
||||
s->state = TTY_STATE_NORM;
|
||||
}
|
||||
break;
|
||||
case TTY_STATE_CSI:
|
||||
case TTY_STATE_CSI: /* handle escape sequence parameters */
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
if (s->nb_esc_params < MAX_ESC_PARAMS) {
|
||||
s->esc_params[s->nb_esc_params] =
|
||||
@@ -529,29 +834,147 @@ static void console_putchar(TextConsole *s, int ch)
|
||||
s->nb_esc_params++;
|
||||
if (ch == ';')
|
||||
break;
|
||||
#ifdef DEBUG_CONSOLE
|
||||
fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
|
||||
s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
|
||||
#endif
|
||||
s->state = TTY_STATE_NORM;
|
||||
switch(ch) {
|
||||
case 'D':
|
||||
if (s->x > 0)
|
||||
s->x--;
|
||||
break;
|
||||
case 'C':
|
||||
if (s->x < (s->width - 1))
|
||||
s->x++;
|
||||
break;
|
||||
case 'K':
|
||||
/* clear to eol */
|
||||
y1 = (s->y_base + s->y) % s->total_height;
|
||||
for(x = s->x; x < s->width; x++) {
|
||||
c = &s->cells[y1 * s->width + x];
|
||||
c->ch = ' ';
|
||||
c->fgcol = s->fgcol;
|
||||
c->bgcol = s->bgcol;
|
||||
c++;
|
||||
update_xy(s, x, s->y);
|
||||
case 'A':
|
||||
/* move cursor up */
|
||||
if (s->esc_params[0] == 0) {
|
||||
s->esc_params[0] = 1;
|
||||
}
|
||||
s->y -= s->esc_params[0];
|
||||
if (s->y < 0) {
|
||||
s->y = 0;
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
/* move cursor down */
|
||||
if (s->esc_params[0] == 0) {
|
||||
s->esc_params[0] = 1;
|
||||
}
|
||||
s->y += s->esc_params[0];
|
||||
if (s->y >= s->height) {
|
||||
s->y = s->height - 1;
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
/* move cursor right */
|
||||
if (s->esc_params[0] == 0) {
|
||||
s->esc_params[0] = 1;
|
||||
}
|
||||
s->x += s->esc_params[0];
|
||||
if (s->x >= s->width) {
|
||||
s->x = s->width - 1;
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
/* move cursor left */
|
||||
if (s->esc_params[0] == 0) {
|
||||
s->esc_params[0] = 1;
|
||||
}
|
||||
s->x -= s->esc_params[0];
|
||||
if (s->x < 0) {
|
||||
s->x = 0;
|
||||
}
|
||||
break;
|
||||
case 'G':
|
||||
/* move cursor to column */
|
||||
s->x = s->esc_params[0] - 1;
|
||||
if (s->x < 0) {
|
||||
s->x = 0;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
case 'H':
|
||||
/* move cursor to row, column */
|
||||
s->x = s->esc_params[1] - 1;
|
||||
if (s->x < 0) {
|
||||
s->x = 0;
|
||||
}
|
||||
s->y = s->esc_params[0] - 1;
|
||||
if (s->y < 0) {
|
||||
s->y = 0;
|
||||
}
|
||||
break;
|
||||
case 'J':
|
||||
switch (s->esc_params[0]) {
|
||||
case 0:
|
||||
/* clear to end of screen */
|
||||
for (y = s->y; y < s->height; y++) {
|
||||
for (x = 0; x < s->width; x++) {
|
||||
if (y == s->y && x < s->x) {
|
||||
continue;
|
||||
}
|
||||
console_clear_xy(s, x, y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* clear from beginning of screen */
|
||||
for (y = 0; y <= s->y; y++) {
|
||||
for (x = 0; x < s->width; x++) {
|
||||
if (y == s->y && x > s->x) {
|
||||
break;
|
||||
}
|
||||
console_clear_xy(s, x, y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* clear entire screen */
|
||||
for (y = 0; y <= s->height; y++) {
|
||||
for (x = 0; x < s->width; x++) {
|
||||
console_clear_xy(s, x, y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'K':
|
||||
switch (s->esc_params[0]) {
|
||||
case 0:
|
||||
/* clear to eol */
|
||||
for(x = s->x; x < s->width; x++) {
|
||||
console_clear_xy(s, x, s->y);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* clear from beginning of line */
|
||||
for (x = 0; x <= s->x; x++) {
|
||||
console_clear_xy(s, x, s->y);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* clear entire line */
|
||||
for(x = 0; x < s->width; x++) {
|
||||
console_clear_xy(s, x, s->y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
console_handle_escape(s);
|
||||
break;
|
||||
case 'n':
|
||||
/* report cursor position */
|
||||
/* TODO: send ESC[row;colR */
|
||||
break;
|
||||
case 's':
|
||||
/* save cursor position */
|
||||
s->x_saved = s->x;
|
||||
s->y_saved = s->y;
|
||||
break;
|
||||
case 'u':
|
||||
/* restore cursor position */
|
||||
s->x = s->x_saved;
|
||||
s->y = s->y_saved;
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_CONSOLE
|
||||
fprintf(stderr, "unhandled escape character '%c'\n", ch);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -562,7 +985,7 @@ static void console_putchar(TextConsole *s, int ch)
|
||||
void console_select(unsigned int index)
|
||||
{
|
||||
TextConsole *s;
|
||||
|
||||
|
||||
if (index >= MAX_CONSOLES)
|
||||
return;
|
||||
s = consoles[index];
|
||||
@@ -571,11 +994,13 @@ void console_select(unsigned int index)
|
||||
if (s->text_console) {
|
||||
if (s->g_width != s->ds->width ||
|
||||
s->g_height != s->ds->height) {
|
||||
s->g_width = s->ds->width;
|
||||
s->g_height = s->ds->height;
|
||||
s->g_width = s->ds->width;
|
||||
s->g_height = s->ds->height;
|
||||
text_console_resize(s);
|
||||
}
|
||||
}
|
||||
console_refresh(s);
|
||||
} else {
|
||||
vga_hw_invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -593,15 +1018,6 @@ static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
|
||||
return len;
|
||||
}
|
||||
|
||||
static void console_chr_add_read_handler(CharDriverState *chr,
|
||||
IOCanRWHandler *fd_can_read,
|
||||
IOReadHandler *fd_read, void *opaque)
|
||||
{
|
||||
TextConsole *s = chr->opaque;
|
||||
s->fd_read = fd_read;
|
||||
s->fd_opaque = opaque;
|
||||
}
|
||||
|
||||
static void console_send_event(CharDriverState *chr, int event)
|
||||
{
|
||||
TextConsole *s = chr->opaque;
|
||||
@@ -617,6 +1033,28 @@ static void console_send_event(CharDriverState *chr, int event)
|
||||
}
|
||||
}
|
||||
|
||||
static void kbd_send_chars(void *opaque)
|
||||
{
|
||||
TextConsole *s = opaque;
|
||||
int len;
|
||||
uint8_t buf[16];
|
||||
|
||||
len = qemu_chr_can_read(s->chr);
|
||||
if (len > s->out_fifo.count)
|
||||
len = s->out_fifo.count;
|
||||
if (len > 0) {
|
||||
if (len > sizeof(buf))
|
||||
len = sizeof(buf);
|
||||
qemu_fifo_read(&s->out_fifo, buf, len);
|
||||
qemu_chr_read(s->chr, buf, len);
|
||||
}
|
||||
/* characters are pending: we send them a bit later (XXX:
|
||||
horrible, should change char device API) */
|
||||
if (s->out_fifo.count > 0) {
|
||||
qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* called when an ascii key is pressed */
|
||||
void kbd_put_keysym(int keysym)
|
||||
{
|
||||
@@ -642,33 +1080,35 @@ void kbd_put_keysym(int keysym)
|
||||
console_scroll(10);
|
||||
break;
|
||||
default:
|
||||
if (s->fd_read) {
|
||||
/* convert the QEMU keysym to VT100 key string */
|
||||
q = buf;
|
||||
if (keysym >= 0xe100 && keysym <= 0xe11f) {
|
||||
*q++ = '\033';
|
||||
*q++ = '[';
|
||||
c = keysym - 0xe100;
|
||||
if (c >= 10)
|
||||
*q++ = '0' + (c / 10);
|
||||
*q++ = '0' + (c % 10);
|
||||
*q++ = '~';
|
||||
} else if (keysym >= 0xe120 && keysym <= 0xe17f) {
|
||||
*q++ = '\033';
|
||||
*q++ = '[';
|
||||
*q++ = keysym & 0xff;
|
||||
} else {
|
||||
/* convert the QEMU keysym to VT100 key string */
|
||||
q = buf;
|
||||
if (keysym >= 0xe100 && keysym <= 0xe11f) {
|
||||
*q++ = '\033';
|
||||
*q++ = '[';
|
||||
c = keysym - 0xe100;
|
||||
if (c >= 10)
|
||||
*q++ = '0' + (c / 10);
|
||||
*q++ = '0' + (c % 10);
|
||||
*q++ = '~';
|
||||
} else if (keysym >= 0xe120 && keysym <= 0xe17f) {
|
||||
*q++ = '\033';
|
||||
*q++ = '[';
|
||||
*q++ = keysym & 0xff;
|
||||
} else {
|
||||
*q++ = keysym;
|
||||
}
|
||||
s->fd_read(s->fd_opaque, buf, q - buf);
|
||||
}
|
||||
if (s->chr->chr_read) {
|
||||
qemu_fifo_write(&s->out_fifo, buf, q - buf);
|
||||
kbd_send_chars(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TextConsole *graphic_console_init(DisplayState *ds)
|
||||
static TextConsole *new_console(DisplayState *ds, int text)
|
||||
{
|
||||
TextConsole *s;
|
||||
int i;
|
||||
|
||||
if (nb_consoles >= MAX_CONSOLES)
|
||||
return NULL;
|
||||
@@ -676,44 +1116,77 @@ TextConsole *graphic_console_init(DisplayState *ds)
|
||||
if (!s) {
|
||||
return NULL;
|
||||
}
|
||||
if (!active_console)
|
||||
if (!active_console || (active_console->text_console && !text))
|
||||
active_console = s;
|
||||
s->ds = ds;
|
||||
consoles[nb_consoles++] = s;
|
||||
s->text_console = text;
|
||||
if (text) {
|
||||
consoles[nb_consoles++] = s;
|
||||
} else {
|
||||
/* HACK: Put graphical consoles before text consoles. */
|
||||
for (i = nb_consoles; i > 0; i--) {
|
||||
if (!consoles[i - 1]->text_console)
|
||||
break;
|
||||
consoles[i] = consoles[i - 1];
|
||||
}
|
||||
consoles[i] = s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int is_active_console(TextConsole *s)
|
||||
TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
|
||||
vga_hw_invalidate_ptr invalidate,
|
||||
vga_hw_screen_dump_ptr screen_dump,
|
||||
void *opaque)
|
||||
{
|
||||
return s == active_console;
|
||||
TextConsole *s;
|
||||
|
||||
s = new_console(ds, 0);
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->hw_update = update;
|
||||
s->hw_invalidate = invalidate;
|
||||
s->hw_screen_dump = screen_dump;
|
||||
s->hw = opaque;
|
||||
return s;
|
||||
}
|
||||
|
||||
int is_graphic_console(void)
|
||||
{
|
||||
return !active_console->text_console;
|
||||
}
|
||||
|
||||
CharDriverState *text_console_init(DisplayState *ds)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
TextConsole *s;
|
||||
int i;
|
||||
int i,j;
|
||||
static int color_inited;
|
||||
|
||||
|
||||
chr = qemu_mallocz(sizeof(CharDriverState));
|
||||
if (!chr)
|
||||
return NULL;
|
||||
s = graphic_console_init(ds);
|
||||
s = new_console(ds, 1);
|
||||
if (!s) {
|
||||
free(chr);
|
||||
return NULL;
|
||||
}
|
||||
s->text_console = 1;
|
||||
chr->opaque = s;
|
||||
chr->chr_write = console_puts;
|
||||
chr->chr_add_read_handler = console_chr_add_read_handler;
|
||||
chr->chr_send_event = console_send_event;
|
||||
|
||||
s->chr = chr;
|
||||
s->out_fifo.buf = s->out_fifo_buf;
|
||||
s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
|
||||
s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
|
||||
|
||||
if (!color_inited) {
|
||||
color_inited = 1;
|
||||
for(i = 0; i < 8; i++) {
|
||||
color_table[i] = col_expand(s->ds,
|
||||
vga_get_color(s->ds, color_table_rgb[i]));
|
||||
for(j = 0; j < 2; j++) {
|
||||
for(i = 0; i < 8; i++) {
|
||||
color_table[j][i] = col_expand(s->ds,
|
||||
vga_get_color(s->ds, color_table_rgb[j][i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
s->y_displayed = 0;
|
||||
@@ -721,11 +1194,23 @@ CharDriverState *text_console_init(DisplayState *ds)
|
||||
s->total_height = DEFAULT_BACKSCROLL;
|
||||
s->x = 0;
|
||||
s->y = 0;
|
||||
s->fgcol = 7;
|
||||
s->bgcol = 0;
|
||||
s->g_width = s->ds->width;
|
||||
s->g_height = s->ds->height;
|
||||
|
||||
/* Set text attribute defaults */
|
||||
s->t_attrib_default.bold = 0;
|
||||
s->t_attrib_default.uline = 0;
|
||||
s->t_attrib_default.blink = 0;
|
||||
s->t_attrib_default.invers = 0;
|
||||
s->t_attrib_default.unvisible = 0;
|
||||
s->t_attrib_default.fgcol = COLOR_WHITE;
|
||||
s->t_attrib_default.bgcol = COLOR_BLACK;
|
||||
|
||||
/* set current text attributes to default */
|
||||
s->t_attrib = s->t_attrib_default;
|
||||
text_console_resize(s);
|
||||
|
||||
qemu_chr_reset(chr);
|
||||
|
||||
return chr;
|
||||
}
|
||||
@@ -188,10 +188,10 @@ static inline void stb_p(void *ptr, int v)
|
||||
/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the
|
||||
kernel handles unaligned load/stores may give better results, but
|
||||
it is a system wide setting : bad */
|
||||
#if !defined(TARGET_WORDS_BIGENDIAN) && (defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED))
|
||||
#if defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
|
||||
|
||||
/* conservative code for little endian unaligned accesses */
|
||||
static inline int lduw_p(void *ptr)
|
||||
static inline int lduw_le_p(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
@@ -203,7 +203,7 @@ static inline int lduw_p(void *ptr)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldsw_p(void *ptr)
|
||||
static inline int ldsw_le_p(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
@@ -215,7 +215,7 @@ static inline int ldsw_p(void *ptr)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldl_p(void *ptr)
|
||||
static inline int ldl_le_p(void *ptr)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
int val;
|
||||
@@ -227,16 +227,16 @@ static inline int ldl_p(void *ptr)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_p(void *ptr)
|
||||
static inline uint64_t ldq_le_p(void *ptr)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
uint32_t v1, v2;
|
||||
v1 = ldl_p(p);
|
||||
v2 = ldl_p(p + 4);
|
||||
v1 = ldl_le_p(p);
|
||||
v2 = ldl_le_p(p + 4);
|
||||
return v1 | ((uint64_t)v2 << 32);
|
||||
}
|
||||
|
||||
static inline void stw_p(void *ptr, int v)
|
||||
static inline void stw_le_p(void *ptr, int v)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
__asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
|
||||
@@ -247,7 +247,7 @@ static inline void stw_p(void *ptr, int v)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stl_p(void *ptr, int v)
|
||||
static inline void stl_le_p(void *ptr, int v)
|
||||
{
|
||||
#ifdef __powerpc__
|
||||
__asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
|
||||
@@ -260,54 +260,114 @@ static inline void stl_p(void *ptr, int v)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stq_p(void *ptr, uint64_t v)
|
||||
static inline void stq_le_p(void *ptr, uint64_t v)
|
||||
{
|
||||
uint8_t *p = ptr;
|
||||
stl_p(p, (uint32_t)v);
|
||||
stl_p(p + 4, v >> 32);
|
||||
stl_le_p(p, (uint32_t)v);
|
||||
stl_le_p(p + 4, v >> 32);
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_p(void *ptr)
|
||||
static inline float32 ldfl_le_p(void *ptr)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.i = ldl_p(ptr);
|
||||
u.i = ldl_le_p(ptr);
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline void stfl_p(void *ptr, float32 v)
|
||||
static inline void stfl_le_p(void *ptr, float32 v)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = v;
|
||||
stl_p(ptr, u.i);
|
||||
stl_le_p(ptr, u.i);
|
||||
}
|
||||
|
||||
static inline float64 ldfq_p(void *ptr)
|
||||
static inline float64 ldfq_le_p(void *ptr)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.l.lower = ldl_p(ptr);
|
||||
u.l.upper = ldl_p(ptr + 4);
|
||||
u.l.lower = ldl_le_p(ptr);
|
||||
u.l.upper = ldl_le_p(ptr + 4);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline void stfq_p(void *ptr, float64 v)
|
||||
static inline void stfq_le_p(void *ptr, float64 v)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.d = v;
|
||||
stl_p(ptr, u.l.lower);
|
||||
stl_p(ptr + 4, u.l.upper);
|
||||
stl_le_p(ptr, u.l.lower);
|
||||
stl_le_p(ptr + 4, u.l.upper);
|
||||
}
|
||||
|
||||
#elif defined(TARGET_WORDS_BIGENDIAN) && (!defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED))
|
||||
#else
|
||||
|
||||
static inline int lduw_p(void *ptr)
|
||||
static inline int lduw_le_p(void *ptr)
|
||||
{
|
||||
return *(uint16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsw_le_p(void *ptr)
|
||||
{
|
||||
return *(int16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldl_le_p(void *ptr)
|
||||
{
|
||||
return *(uint32_t *)ptr;
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_le_p(void *ptr)
|
||||
{
|
||||
return *(uint64_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stw_le_p(void *ptr, int v)
|
||||
{
|
||||
*(uint16_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stl_le_p(void *ptr, int v)
|
||||
{
|
||||
*(uint32_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stq_le_p(void *ptr, uint64_t v)
|
||||
{
|
||||
*(uint64_t *)ptr = v;
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_le_p(void *ptr)
|
||||
{
|
||||
return *(float32 *)ptr;
|
||||
}
|
||||
|
||||
static inline float64 ldfq_le_p(void *ptr)
|
||||
{
|
||||
return *(float64 *)ptr;
|
||||
}
|
||||
|
||||
static inline void stfl_le_p(void *ptr, float32 v)
|
||||
{
|
||||
*(float32 *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stfq_le_p(void *ptr, float64 v)
|
||||
{
|
||||
*(float64 *)ptr = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
|
||||
|
||||
static inline int lduw_be_p(void *ptr)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
int val;
|
||||
@@ -322,7 +382,7 @@ static inline int lduw_p(void *ptr)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldsw_p(void *ptr)
|
||||
static inline int ldsw_be_p(void *ptr)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
int val;
|
||||
@@ -337,7 +397,7 @@ static inline int ldsw_p(void *ptr)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int ldl_p(void *ptr)
|
||||
static inline int ldl_be_p(void *ptr)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
int val;
|
||||
@@ -352,15 +412,15 @@ static inline int ldl_p(void *ptr)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_p(void *ptr)
|
||||
static inline uint64_t ldq_be_p(void *ptr)
|
||||
{
|
||||
uint32_t a,b;
|
||||
a = ldl_p(ptr);
|
||||
b = ldl_p(ptr+4);
|
||||
a = ldl_be_p(ptr);
|
||||
b = ldl_be_p(ptr+4);
|
||||
return (((uint64_t)a<<32)|b);
|
||||
}
|
||||
|
||||
static inline void stw_p(void *ptr, int v)
|
||||
static inline void stw_be_p(void *ptr, int v)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
asm volatile ("xchgb %b0, %h0\n"
|
||||
@@ -374,7 +434,7 @@ static inline void stw_p(void *ptr, int v)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stl_p(void *ptr, int v)
|
||||
static inline void stl_be_p(void *ptr, int v)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
asm volatile ("bswap %0\n"
|
||||
@@ -390,128 +450,175 @@ static inline void stl_p(void *ptr, int v)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void stq_p(void *ptr, uint64_t v)
|
||||
static inline void stq_be_p(void *ptr, uint64_t v)
|
||||
{
|
||||
stl_p(ptr, v >> 32);
|
||||
stl_p(ptr + 4, v);
|
||||
stl_be_p(ptr, v >> 32);
|
||||
stl_be_p(ptr + 4, v);
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_p(void *ptr)
|
||||
static inline float32 ldfl_be_p(void *ptr)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.i = ldl_p(ptr);
|
||||
u.i = ldl_be_p(ptr);
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline void stfl_p(void *ptr, float32 v)
|
||||
static inline void stfl_be_p(void *ptr, float32 v)
|
||||
{
|
||||
union {
|
||||
float32 f;
|
||||
uint32_t i;
|
||||
} u;
|
||||
u.f = v;
|
||||
stl_p(ptr, u.i);
|
||||
stl_be_p(ptr, u.i);
|
||||
}
|
||||
|
||||
static inline float64 ldfq_p(void *ptr)
|
||||
static inline float64 ldfq_be_p(void *ptr)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.l.upper = ldl_p(ptr);
|
||||
u.l.lower = ldl_p(ptr + 4);
|
||||
u.l.upper = ldl_be_p(ptr);
|
||||
u.l.lower = ldl_be_p(ptr + 4);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static inline void stfq_p(void *ptr, float64 v)
|
||||
static inline void stfq_be_p(void *ptr, float64 v)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.d = v;
|
||||
stl_p(ptr, u.l.upper);
|
||||
stl_p(ptr + 4, u.l.lower);
|
||||
stl_be_p(ptr, u.l.upper);
|
||||
stl_be_p(ptr + 4, u.l.lower);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int lduw_p(void *ptr)
|
||||
static inline int lduw_be_p(void *ptr)
|
||||
{
|
||||
return *(uint16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldsw_p(void *ptr)
|
||||
static inline int ldsw_be_p(void *ptr)
|
||||
{
|
||||
return *(int16_t *)ptr;
|
||||
}
|
||||
|
||||
static inline int ldl_p(void *ptr)
|
||||
static inline int ldl_be_p(void *ptr)
|
||||
{
|
||||
return *(uint32_t *)ptr;
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_p(void *ptr)
|
||||
static inline uint64_t ldq_be_p(void *ptr)
|
||||
{
|
||||
return *(uint64_t *)ptr;
|
||||
}
|
||||
|
||||
static inline void stw_p(void *ptr, int v)
|
||||
static inline void stw_be_p(void *ptr, int v)
|
||||
{
|
||||
*(uint16_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stl_p(void *ptr, int v)
|
||||
static inline void stl_be_p(void *ptr, int v)
|
||||
{
|
||||
*(uint32_t *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stq_p(void *ptr, uint64_t v)
|
||||
static inline void stq_be_p(void *ptr, uint64_t v)
|
||||
{
|
||||
*(uint64_t *)ptr = v;
|
||||
}
|
||||
|
||||
/* float access */
|
||||
|
||||
static inline float32 ldfl_p(void *ptr)
|
||||
static inline float32 ldfl_be_p(void *ptr)
|
||||
{
|
||||
return *(float32 *)ptr;
|
||||
}
|
||||
|
||||
static inline float64 ldfq_p(void *ptr)
|
||||
static inline float64 ldfq_be_p(void *ptr)
|
||||
{
|
||||
return *(float64 *)ptr;
|
||||
}
|
||||
|
||||
static inline void stfl_p(void *ptr, float32 v)
|
||||
static inline void stfl_be_p(void *ptr, float32 v)
|
||||
{
|
||||
*(float32 *)ptr = v;
|
||||
}
|
||||
|
||||
static inline void stfq_p(void *ptr, float64 v)
|
||||
static inline void stfq_be_p(void *ptr, float64 v)
|
||||
{
|
||||
*(float64 *)ptr = v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* target CPU memory access functions */
|
||||
#if defined(TARGET_WORDS_BIGENDIAN)
|
||||
#define lduw_p(p) lduw_be_p(p)
|
||||
#define ldsw_p(p) ldsw_be_p(p)
|
||||
#define ldl_p(p) ldl_be_p(p)
|
||||
#define ldq_p(p) ldq_be_p(p)
|
||||
#define ldfl_p(p) ldfl_be_p(p)
|
||||
#define ldfq_p(p) ldfq_be_p(p)
|
||||
#define stw_p(p, v) stw_be_p(p, v)
|
||||
#define stl_p(p, v) stl_be_p(p, v)
|
||||
#define stq_p(p, v) stq_be_p(p, v)
|
||||
#define stfl_p(p, v) stfl_be_p(p, v)
|
||||
#define stfq_p(p, v) stfq_be_p(p, v)
|
||||
#else
|
||||
#define lduw_p(p) lduw_le_p(p)
|
||||
#define ldsw_p(p) ldsw_le_p(p)
|
||||
#define ldl_p(p) ldl_le_p(p)
|
||||
#define ldq_p(p) ldq_le_p(p)
|
||||
#define ldfl_p(p) ldfl_le_p(p)
|
||||
#define ldfq_p(p) ldfq_le_p(p)
|
||||
#define stw_p(p, v) stw_le_p(p, v)
|
||||
#define stl_p(p, v) stl_le_p(p, v)
|
||||
#define stq_p(p, v) stq_le_p(p, v)
|
||||
#define stfl_p(p, v) stfl_le_p(p, v)
|
||||
#define stfq_p(p, v) stfq_le_p(p, v)
|
||||
#endif
|
||||
|
||||
/* MMU memory access macros */
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
/* On some host systems the guest address space is reserved on the host.
|
||||
* This allows the guest address space to be offset to a convenient location.
|
||||
*/
|
||||
//#define GUEST_BASE 0x20000000
|
||||
#define GUEST_BASE 0
|
||||
|
||||
/* All direct uses of g2h and h2g need to go away for usermode softmmu. */
|
||||
#define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE))
|
||||
#define h2g(x) ((target_ulong)(x - GUEST_BASE))
|
||||
|
||||
#define saddr(x) g2h(x)
|
||||
#define laddr(x) g2h(x)
|
||||
|
||||
#else /* !CONFIG_USER_ONLY */
|
||||
/* NOTE: we use double casts if pointers and target_ulong have
|
||||
different sizes */
|
||||
#define ldub_raw(p) ldub_p((uint8_t *)(long)(p))
|
||||
#define ldsb_raw(p) ldsb_p((uint8_t *)(long)(p))
|
||||
#define lduw_raw(p) lduw_p((uint8_t *)(long)(p))
|
||||
#define ldsw_raw(p) ldsw_p((uint8_t *)(long)(p))
|
||||
#define ldl_raw(p) ldl_p((uint8_t *)(long)(p))
|
||||
#define ldq_raw(p) ldq_p((uint8_t *)(long)(p))
|
||||
#define ldfl_raw(p) ldfl_p((uint8_t *)(long)(p))
|
||||
#define ldfq_raw(p) ldfq_p((uint8_t *)(long)(p))
|
||||
#define stb_raw(p, v) stb_p((uint8_t *)(long)(p), v)
|
||||
#define stw_raw(p, v) stw_p((uint8_t *)(long)(p), v)
|
||||
#define stl_raw(p, v) stl_p((uint8_t *)(long)(p), v)
|
||||
#define stq_raw(p, v) stq_p((uint8_t *)(long)(p), v)
|
||||
#define stfl_raw(p, v) stfl_p((uint8_t *)(long)(p), v)
|
||||
#define stfq_raw(p, v) stfq_p((uint8_t *)(long)(p), v)
|
||||
#define saddr(x) (uint8_t *)(long)(x)
|
||||
#define laddr(x) (uint8_t *)(long)(x)
|
||||
#endif
|
||||
|
||||
#define ldub_raw(p) ldub_p(laddr((p)))
|
||||
#define ldsb_raw(p) ldsb_p(laddr((p)))
|
||||
#define lduw_raw(p) lduw_p(laddr((p)))
|
||||
#define ldsw_raw(p) ldsw_p(laddr((p)))
|
||||
#define ldl_raw(p) ldl_p(laddr((p)))
|
||||
#define ldq_raw(p) ldq_p(laddr((p)))
|
||||
#define ldfl_raw(p) ldfl_p(laddr((p)))
|
||||
#define ldfq_raw(p) ldfq_p(laddr((p)))
|
||||
#define stb_raw(p, v) stb_p(saddr((p)), v)
|
||||
#define stw_raw(p, v) stw_p(saddr((p)), v)
|
||||
#define stl_raw(p, v) stl_p(saddr((p)), v)
|
||||
#define stq_raw(p, v) stq_p(saddr((p)), v)
|
||||
#define stfl_raw(p, v) stfl_p(saddr((p)), v)
|
||||
#define stfq_raw(p, v) stfq_p(saddr((p)), v)
|
||||
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
@@ -560,6 +667,7 @@ static inline void stfq_p(void *ptr, float64 v)
|
||||
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
|
||||
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
|
||||
|
||||
/* ??? These should be the larger of unsigned long and target_ulong. */
|
||||
extern unsigned long qemu_real_host_page_size;
|
||||
extern unsigned long qemu_host_page_bits;
|
||||
extern unsigned long qemu_host_page_size;
|
||||
@@ -578,9 +686,9 @@ extern unsigned long qemu_host_page_mask;
|
||||
#define PAGE_WRITE_ORG 0x0010
|
||||
|
||||
void page_dump(FILE *f);
|
||||
int page_get_flags(unsigned long address);
|
||||
void page_set_flags(unsigned long start, unsigned long end, int flags);
|
||||
void page_unprotect_range(uint8_t *data, unsigned long data_size);
|
||||
int page_get_flags(target_ulong address);
|
||||
void page_set_flags(target_ulong start, target_ulong end, int flags);
|
||||
void page_unprotect_range(target_ulong data, target_ulong data_size);
|
||||
|
||||
#define SINGLE_CPU_DEFINES
|
||||
#ifdef SINGLE_CPU_DEFINES
|
||||
@@ -617,6 +725,13 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size);
|
||||
#define cpu_gen_code cpu_ppc_gen_code
|
||||
#define cpu_signal_handler cpu_ppc_signal_handler
|
||||
|
||||
#elif defined(TARGET_M68K)
|
||||
#define CPUState CPUM68KState
|
||||
#define cpu_init cpu_m68k_init
|
||||
#define cpu_exec cpu_m68k_exec
|
||||
#define cpu_gen_code cpu_m68k_gen_code
|
||||
#define cpu_signal_handler cpu_m68k_signal_handler
|
||||
|
||||
#elif defined(TARGET_MIPS)
|
||||
#define CPUState CPUMIPSState
|
||||
#define cpu_init cpu_mips_init
|
||||
@@ -624,6 +739,13 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size);
|
||||
#define cpu_gen_code cpu_mips_gen_code
|
||||
#define cpu_signal_handler cpu_mips_signal_handler
|
||||
|
||||
#elif defined(TARGET_SH4)
|
||||
#define CPUState CPUSH4State
|
||||
#define cpu_init cpu_sh4_init
|
||||
#define cpu_exec cpu_sh4_exec
|
||||
#define cpu_gen_code cpu_sh4_gen_code
|
||||
#define cpu_signal_handler cpu_sh4_signal_handler
|
||||
|
||||
#else
|
||||
|
||||
#error unsupported target CPU
|
||||
@@ -637,6 +759,7 @@ void cpu_dump_state(CPUState *env, FILE *f,
|
||||
int flags);
|
||||
|
||||
void cpu_abort(CPUState *env, const char *fmt, ...);
|
||||
extern CPUState *first_cpu;
|
||||
extern CPUState *cpu_single_env;
|
||||
extern int code_copy_enabled;
|
||||
|
||||
@@ -644,6 +767,10 @@ extern int code_copy_enabled;
|
||||
#define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */
|
||||
#define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */
|
||||
#define CPU_INTERRUPT_TIMER 0x08 /* internal timer exception pending */
|
||||
#define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */
|
||||
#define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */
|
||||
#define CPU_INTERRUPT_SMI 0x40 /* (x86 only) SMI interrupt pending */
|
||||
|
||||
void cpu_interrupt(CPUState *s, int mask);
|
||||
void cpu_reset_interrupt(CPUState *env, int mask);
|
||||
|
||||
@@ -701,15 +828,18 @@ extern uint8_t *phys_ram_base;
|
||||
extern uint8_t *phys_ram_dirty;
|
||||
|
||||
/* physical memory access */
|
||||
#define IO_MEM_NB_ENTRIES 256
|
||||
#define TLB_INVALID_MASK (1 << 3)
|
||||
#define IO_MEM_SHIFT 4
|
||||
#define IO_MEM_NB_ENTRIES (1 << (TARGET_PAGE_BITS - IO_MEM_SHIFT))
|
||||
|
||||
#define IO_MEM_RAM (0 << IO_MEM_SHIFT) /* hardcoded offset */
|
||||
#define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */
|
||||
#define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT)
|
||||
#define IO_MEM_CODE (3 << IO_MEM_SHIFT) /* used internally, never use directly */
|
||||
#define IO_MEM_NOTDIRTY (4 << IO_MEM_SHIFT) /* used internally, never use directly */
|
||||
/* acts like a ROM when read and like a device when written. As an
|
||||
exception, the write memory callback gets the ram offset instead of
|
||||
the physical address */
|
||||
#define IO_MEM_ROMD (1)
|
||||
|
||||
typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
|
||||
typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
|
||||
@@ -717,6 +847,7 @@ typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
|
||||
void cpu_register_physical_memory(target_phys_addr_t start_addr,
|
||||
unsigned long size,
|
||||
unsigned long phys_offset);
|
||||
uint32_t cpu_get_physical_page_desc(target_phys_addr_t addr);
|
||||
int cpu_register_io_memory(int io_index,
|
||||
CPUReadMemoryFunc **mem_read,
|
||||
CPUWriteMemoryFunc **mem_write,
|
||||
@@ -736,36 +867,167 @@ static inline void cpu_physical_memory_write(target_phys_addr_t addr,
|
||||
{
|
||||
cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1);
|
||||
}
|
||||
uint32_t ldub_phys(target_phys_addr_t addr);
|
||||
uint32_t lduw_phys(target_phys_addr_t addr);
|
||||
uint32_t ldl_phys(target_phys_addr_t addr);
|
||||
uint64_t ldq_phys(target_phys_addr_t addr);
|
||||
void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
|
||||
void stb_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stw_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stl_phys(target_phys_addr_t addr, uint32_t val);
|
||||
void stq_phys(target_phys_addr_t addr, uint64_t val);
|
||||
|
||||
void cpu_physical_memory_write_rom(target_phys_addr_t addr,
|
||||
const uint8_t *buf, int len);
|
||||
int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
|
||||
uint8_t *buf, int len, int is_write);
|
||||
|
||||
#define VGA_DIRTY_FLAG 0x01
|
||||
#define VGA_DIRTY_FLAG 0x01
|
||||
#define CODE_DIRTY_FLAG 0x02
|
||||
|
||||
/* read dirty bit (return 0 or 1) */
|
||||
static inline int cpu_physical_memory_is_dirty(target_ulong addr)
|
||||
static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
|
||||
{
|
||||
return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
|
||||
}
|
||||
|
||||
static inline int cpu_physical_memory_get_dirty(target_ulong addr,
|
||||
static inline int cpu_physical_memory_get_dirty(ram_addr_t addr,
|
||||
int dirty_flags)
|
||||
{
|
||||
return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_set_dirty(target_ulong addr)
|
||||
static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
|
||||
{
|
||||
phys_ram_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
|
||||
}
|
||||
|
||||
void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end,
|
||||
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
|
||||
int dirty_flags);
|
||||
void cpu_tlb_update_dirty(CPUState *env);
|
||||
|
||||
void dump_exec_info(FILE *f,
|
||||
int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
|
||||
|
||||
/*******************************************/
|
||||
/* host CPU ticks (if available) */
|
||||
|
||||
#if defined(__powerpc__)
|
||||
|
||||
static inline uint32_t get_tbl(void)
|
||||
{
|
||||
uint32_t tbl;
|
||||
asm volatile("mftb %0" : "=r" (tbl));
|
||||
return tbl;
|
||||
}
|
||||
|
||||
static inline uint32_t get_tbu(void)
|
||||
{
|
||||
uint32_t tbl;
|
||||
asm volatile("mftbu %0" : "=r" (tbl));
|
||||
return tbl;
|
||||
}
|
||||
|
||||
static inline int64_t cpu_get_real_ticks(void)
|
||||
{
|
||||
uint32_t l, h, h1;
|
||||
/* NOTE: we test if wrapping has occurred */
|
||||
do {
|
||||
h = get_tbu();
|
||||
l = get_tbl();
|
||||
h1 = get_tbu();
|
||||
} while (h != h1);
|
||||
return ((int64_t)h << 32) | l;
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
static inline int64_t cpu_get_real_ticks(void)
|
||||
{
|
||||
int64_t val;
|
||||
asm volatile ("rdtsc" : "=A" (val));
|
||||
return val;
|
||||
}
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
static inline int64_t cpu_get_real_ticks(void)
|
||||
{
|
||||
uint32_t low,high;
|
||||
int64_t val;
|
||||
asm volatile("rdtsc" : "=a" (low), "=d" (high));
|
||||
val = high;
|
||||
val <<= 32;
|
||||
val |= low;
|
||||
return val;
|
||||
}
|
||||
|
||||
#elif defined(__ia64)
|
||||
|
||||
static inline int64_t cpu_get_real_ticks(void)
|
||||
{
|
||||
int64_t val;
|
||||
asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory");
|
||||
return val;
|
||||
}
|
||||
|
||||
#elif defined(__s390__)
|
||||
|
||||
static inline int64_t cpu_get_real_ticks(void)
|
||||
{
|
||||
int64_t val;
|
||||
asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc");
|
||||
return val;
|
||||
}
|
||||
|
||||
#elif defined(__sparc_v9__)
|
||||
|
||||
static inline int64_t cpu_get_real_ticks (void)
|
||||
{
|
||||
#if defined(_LP64)
|
||||
uint64_t rval;
|
||||
asm volatile("rd %%tick,%0" : "=r"(rval));
|
||||
return rval;
|
||||
#else
|
||||
union {
|
||||
uint64_t i64;
|
||||
struct {
|
||||
uint32_t high;
|
||||
uint32_t low;
|
||||
} i32;
|
||||
} rval;
|
||||
asm volatile("rd %%tick,%1; srlx %1,32,%0"
|
||||
: "=r"(rval.i32.high), "=r"(rval.i32.low));
|
||||
return rval.i64;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
/* The host CPU doesn't have an easily accessible cycle counter.
|
||||
Just return a monotonically increasing vlue. This will be totally wrong,
|
||||
but hopefully better than nothing. */
|
||||
static inline int64_t cpu_get_real_ticks (void)
|
||||
{
|
||||
static int64_t ticks = 0;
|
||||
return ticks++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* profiling */
|
||||
#ifdef CONFIG_PROFILER
|
||||
static inline int64_t profile_getclock(void)
|
||||
{
|
||||
return cpu_get_real_ticks();
|
||||
}
|
||||
|
||||
extern int64_t kqemu_time, kqemu_time_start;
|
||||
extern int64_t qemu_time, qemu_time_start;
|
||||
extern int64_t tlb_flush_time;
|
||||
extern int64_t kqemu_exec_count;
|
||||
extern int64_t dev_time;
|
||||
extern int64_t kqemu_ret_int_count;
|
||||
extern int64_t kqemu_ret_excp_count;
|
||||
extern int64_t kqemu_ret_intr_count;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* CPU_ALL_H */
|
||||
+46
-4
@@ -47,7 +47,7 @@ typedef uint32_t target_ulong;
|
||||
#elif TARGET_LONG_SIZE == 8
|
||||
typedef int64_t target_long;
|
||||
typedef uint64_t target_ulong;
|
||||
#define TARGET_FMT_lx "%016llx"
|
||||
#define TARGET_FMT_lx "%016" PRIx64
|
||||
#else
|
||||
#error TARGET_LONG_SIZE undefined
|
||||
#endif
|
||||
@@ -66,15 +66,30 @@ typedef uint64_t target_phys_addr_t;
|
||||
#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 CPU_TLB_SIZE 256
|
||||
#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
|
||||
@@ -83,9 +98,36 @@ typedef struct CPUTLBEntry {
|
||||
bit 3 : indicates that the entry is invalid
|
||||
bit 2..0 : zero
|
||||
*/
|
||||
target_ulong address;
|
||||
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
|
||||
+459
-278
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 */
|
||||
@@ -56,6 +56,17 @@ enum bfd_architecture
|
||||
#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.
|
||||
@@ -152,10 +163,23 @@ enum bfd_architecture
|
||||
#define bfd_mach_z8002 2
|
||||
bfd_arch_h8500, /* Hitachi H8/500 */
|
||||
bfd_arch_sh, /* Hitachi SH */
|
||||
#define bfd_mach_sh 0
|
||||
#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
|
||||
@@ -417,6 +441,7 @@ extern int generic_symbol_at_address
|
||||
(INFO).insn_info_valid = 0
|
||||
|
||||
#define _(x) x
|
||||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||
|
||||
/* from libbfd */
|
||||
|
||||
@@ -425,5 +450,6 @@ 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) */
|
||||
@@ -58,7 +58,7 @@ perror_memory (status, memaddr, info)
|
||||
/* Actually, address between memaddr and memaddr + len was
|
||||
out of bounds. */
|
||||
(*info->fprintf_func) (info->stream,
|
||||
"Address 0x%llx is out of bounds.\n", memaddr);
|
||||
"Address 0x%" PRIx64 " is out of bounds.\n", memaddr);
|
||||
}
|
||||
|
||||
/* This could be in a separate file, to save miniscule amounts of space
|
||||
@@ -73,7 +73,7 @@ generic_print_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "0x%llx", addr);
|
||||
(*info->fprintf_func) (info->stream, "0x%" PRIx64, addr);
|
||||
}
|
||||
|
||||
/* Just return the given address. */
|
||||
@@ -138,6 +138,7 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info)
|
||||
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)
|
||||
@@ -177,7 +178,7 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
|
||||
disasm_info.mach = bfd_mach_sparc_v9b;
|
||||
#endif
|
||||
#elif defined(TARGET_PPC)
|
||||
if (cpu_single_env->msr[MSR_LE])
|
||||
if (flags)
|
||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||
#ifdef TARGET_PPC64
|
||||
disasm_info.mach = bfd_mach_ppc64;
|
||||
@@ -185,8 +186,17 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
|
||||
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);
|
||||
@@ -251,6 +261,8 @@ void disas(FILE *out, void *code, unsigned long size)
|
||||
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);
|
||||
@@ -259,11 +271,9 @@ void disas(FILE *out, void *code, unsigned long size)
|
||||
for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) {
|
||||
fprintf(out, "0x%08lx: ", pc);
|
||||
#ifdef __arm__
|
||||
/* since data are included in the code, it is better to
|
||||
/* since data is included in the code, it is better to
|
||||
display code data too */
|
||||
if (is_host) {
|
||||
fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc));
|
||||
}
|
||||
fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc));
|
||||
#endif
|
||||
count = print_insn(pc, &disasm_info);
|
||||
fprintf(out, "\n");
|
||||
@@ -279,6 +289,7 @@ const char *lookup_symbol(target_ulong orig_addr)
|
||||
/* 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;
|
||||
@@ -290,8 +301,13 @@ const char *lookup_symbol(target_ulong orig_addr)
|
||||
if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC)
|
||||
continue;
|
||||
|
||||
if (orig_addr >= sym[i].st_value
|
||||
&& orig_addr < sym[i].st_value + sym[i].st_size)
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -304,6 +320,7 @@ 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)
|
||||
@@ -315,7 +332,7 @@ monitor_read_memory (memaddr, myaddr, length, info)
|
||||
if (monitor_disas_is_physical) {
|
||||
cpu_physical_memory_rw(memaddr, myaddr, length, 0);
|
||||
} else {
|
||||
cpu_memory_rw_debug(cpu_single_env, memaddr,myaddr, length, 0);
|
||||
cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -329,7 +346,8 @@ static int monitor_fprintf(FILE *stream, const char *fmt, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags)
|
||||
void monitor_disas(CPUState *env,
|
||||
target_ulong pc, int nb_insn, int is_physical, int flags)
|
||||
{
|
||||
int count, i;
|
||||
struct disassemble_info disasm_info;
|
||||
@@ -337,6 +355,7 @@ void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags)
|
||||
|
||||
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;
|
||||
|
||||
@@ -366,8 +385,14 @@ void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags)
|
||||
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);
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
/* 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(target_ulong pc, int nb_insn, int is_physical, 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);
|
||||
|
||||
+39
-3
@@ -20,6 +20,13 @@
|
||||
#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. */
|
||||
@@ -28,21 +35,35 @@
|
||||
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)
|
||||
@@ -109,6 +130,19 @@ extern int printf(const char *, ...);
|
||||
#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"
|
||||
@@ -121,6 +155,8 @@ extern int printf(const char *, ...);
|
||||
#define AREG9 "l5"
|
||||
#define AREG10 "l6"
|
||||
#define AREG11 "l7"
|
||||
#endif
|
||||
#endif
|
||||
#define USE_FP_CONVERT
|
||||
#endif
|
||||
#ifdef __s390__
|
||||
@@ -155,7 +191,7 @@ extern int printf(const char *, ...);
|
||||
#endif
|
||||
|
||||
/* force GCC to generate only one epilog at the end of the function */
|
||||
#define FORCE_RET() asm volatile ("");
|
||||
#define FORCE_RET() __asm__ __volatile__("" : : : "memory");
|
||||
|
||||
#ifndef OPPROTO
|
||||
#define OPPROTO
|
||||
@@ -229,8 +265,8 @@ extern int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
|
||||
ASM_NAME(__op_gen_label) #n)
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
#define EXIT_TB() asm volatile ("jmpl %i0 + 8, %g0\n" \
|
||||
"nop")
|
||||
#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")
|
||||
|
||||
@@ -127,10 +127,12 @@
|
||||
typedef int32_t host_long;
|
||||
typedef uint32_t host_ulong;
|
||||
#define swabls(x) swab32s(x)
|
||||
#define swablss(x) swab32ss(x)
|
||||
#else
|
||||
typedef int64_t host_long;
|
||||
typedef uint64_t host_ulong;
|
||||
#define swabls(x) swab64s(x)
|
||||
#define swablss(x) swab64ss(x)
|
||||
#endif
|
||||
|
||||
#ifdef ELF_USES_RELOCA
|
||||
@@ -284,11 +286,21 @@ void swab32s(uint32_t *p)
|
||||
*p = bswap32(*p);
|
||||
}
|
||||
|
||||
void swab32ss(int32_t *p)
|
||||
{
|
||||
*p = bswap32(*p);
|
||||
}
|
||||
|
||||
void swab64s(uint64_t *p)
|
||||
{
|
||||
*p = bswap64(*p);
|
||||
}
|
||||
|
||||
void swab64ss(int64_t *p)
|
||||
{
|
||||
*p = bswap64(*p);
|
||||
}
|
||||
|
||||
uint16_t get16(uint16_t *p)
|
||||
{
|
||||
uint16_t val;
|
||||
@@ -397,7 +409,7 @@ void elf_swap_rel(ELF_RELOC *rel)
|
||||
swabls(&rel->r_offset);
|
||||
swabls(&rel->r_info);
|
||||
#ifdef ELF_USES_RELOCA
|
||||
swabls(&rel->r_addend);
|
||||
swablss(&rel->r_addend);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -505,7 +517,7 @@ int load_object(const char *filename)
|
||||
}
|
||||
|
||||
sec = &shdr[ehdr.e_shstrndx];
|
||||
shstr = sdata[ehdr.e_shstrndx];
|
||||
shstr = (char *)sdata[ehdr.e_shstrndx];
|
||||
|
||||
/* swap relocations */
|
||||
for(i = 0; i < ehdr.e_shnum; i++) {
|
||||
@@ -541,7 +553,7 @@ int load_object(const char *filename)
|
||||
strtab_sec = &shdr[symtab_sec->sh_link];
|
||||
|
||||
symtab = (ElfW(Sym) *)sdata[symtab_sec - shdr];
|
||||
strtab = sdata[symtab_sec->sh_link];
|
||||
strtab = (char *)sdata[symtab_sec->sh_link];
|
||||
|
||||
nb_syms = symtab_sec->sh_size / sizeof(ElfW(Sym));
|
||||
if (do_swap) {
|
||||
@@ -1196,7 +1208,7 @@ void get_reloc_expr(char *name, int name_size, const char *sym_name)
|
||||
} else {
|
||||
#ifdef HOST_SPARC
|
||||
if (sym_name[0] == '.')
|
||||
snprintf(name, sizeof(name),
|
||||
snprintf(name, name_size,
|
||||
"(long)(&__dot_%s)",
|
||||
sym_name + 1);
|
||||
else
|
||||
@@ -1255,90 +1267,149 @@ int arm_emit_ldr_info(const char *name, unsigned long start_offset,
|
||||
{
|
||||
uint8_t *p;
|
||||
uint32_t insn;
|
||||
int offset, min_offset, pc_offset, data_size;
|
||||
int offset, min_offset, pc_offset, data_size, spare, max_pool;
|
||||
uint8_t data_allocated[1024];
|
||||
unsigned int data_index;
|
||||
int type;
|
||||
|
||||
memset(data_allocated, 0, sizeof(data_allocated));
|
||||
|
||||
p = p_start;
|
||||
min_offset = p_end - p_start;
|
||||
spare = 0x7fffffff;
|
||||
while (p < p_start + min_offset) {
|
||||
insn = get32((uint32_t *)p);
|
||||
/* TODO: Armv5e ldrd. */
|
||||
/* TODO: VFP load. */
|
||||
if ((insn & 0x0d5f0000) == 0x051f0000) {
|
||||
/* ldr reg, [pc, #im] */
|
||||
offset = insn & 0xfff;
|
||||
if (!(insn & 0x00800000))
|
||||
offset = -offset;
|
||||
offset = -offset;
|
||||
max_pool = 4096;
|
||||
type = 0;
|
||||
} else if ((insn & 0x0e5f0f00) == 0x0c1f0100) {
|
||||
/* FPA ldf. */
|
||||
offset = (insn & 0xff) << 2;
|
||||
if (!(insn & 0x00800000))
|
||||
offset = -offset;
|
||||
max_pool = 1024;
|
||||
type = 1;
|
||||
} else if ((insn & 0x0fff0000) == 0x028f0000) {
|
||||
/* Some gcc load a doubleword immediate with
|
||||
add regN, pc, #imm
|
||||
ldmia regN, {regN, regM}
|
||||
Hope and pray the compiler never generates somethin like
|
||||
add reg, pc, #imm1; ldr reg, [reg, #-imm2]; */
|
||||
int r;
|
||||
|
||||
r = (insn & 0xf00) >> 7;
|
||||
offset = ((insn & 0xff) >> r) | ((insn & 0xff) << (32 - r));
|
||||
max_pool = 1024;
|
||||
type = 2;
|
||||
} else {
|
||||
max_pool = 0;
|
||||
type = -1;
|
||||
}
|
||||
if (type >= 0) {
|
||||
/* PC-relative load needs fixing up. */
|
||||
if (spare > max_pool - offset)
|
||||
spare = max_pool - offset;
|
||||
if ((offset & 3) !=0)
|
||||
error("%s:%04x: ldr pc offset must be 32 bit aligned",
|
||||
error("%s:%04x: pc offset must be 32 bit aligned",
|
||||
name, start_offset + p - p_start);
|
||||
if (offset < 0)
|
||||
error("%s:%04x: Embedded literal value",
|
||||
name, start_offset + p - p_start);
|
||||
pc_offset = p - p_start + offset + 8;
|
||||
if (pc_offset <= (p - p_start) ||
|
||||
pc_offset >= (p_end - p_start))
|
||||
error("%s:%04x: ldr pc offset must point inside the function code",
|
||||
error("%s:%04x: pc offset must point inside the function code",
|
||||
name, start_offset + p - p_start);
|
||||
if (pc_offset < min_offset)
|
||||
min_offset = pc_offset;
|
||||
if (outfile) {
|
||||
/* ldr position */
|
||||
/* The intruction position */
|
||||
fprintf(outfile, " arm_ldr_ptr->ptr = gen_code_ptr + %d;\n",
|
||||
p - p_start);
|
||||
/* ldr data index */
|
||||
data_index = ((p_end - p_start) - pc_offset - 4) >> 2;
|
||||
fprintf(outfile, " arm_ldr_ptr->data_ptr = arm_data_ptr + %d;\n",
|
||||
/* The position of the constant pool data. */
|
||||
data_index = ((p_end - p_start) - pc_offset) >> 2;
|
||||
fprintf(outfile, " arm_ldr_ptr->data_ptr = arm_data_ptr - %d;\n",
|
||||
data_index);
|
||||
fprintf(outfile, " arm_ldr_ptr->type = %d;\n", type);
|
||||
fprintf(outfile, " arm_ldr_ptr++;\n");
|
||||
if (data_index >= sizeof(data_allocated))
|
||||
error("%s: too many data", name);
|
||||
if (!data_allocated[data_index]) {
|
||||
ELF_RELOC *rel;
|
||||
int i, addend, type;
|
||||
const char *sym_name, *p;
|
||||
char relname[1024];
|
||||
|
||||
data_allocated[data_index] = 1;
|
||||
|
||||
/* data value */
|
||||
addend = get32((uint32_t *)(p_start + pc_offset));
|
||||
relname[0] = '\0';
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset == (pc_offset + start_offset)) {
|
||||
sym_name = get_rel_sym_name(rel);
|
||||
/* the compiler leave some unnecessary references to the code */
|
||||
get_reloc_expr(relname, sizeof(relname), sym_name);
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
if (type != R_ARM_ABS32)
|
||||
error("%s: unsupported data relocation", name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(outfile, " arm_data_ptr[%d] = 0x%x",
|
||||
data_index, addend);
|
||||
if (relname[0] != '\0')
|
||||
fprintf(outfile, " + %s", relname);
|
||||
fprintf(outfile, ";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
p += 4;
|
||||
}
|
||||
|
||||
/* Copy and relocate the constant pool data. */
|
||||
data_size = (p_end - p_start) - min_offset;
|
||||
if (data_size > 0 && outfile) {
|
||||
fprintf(outfile, " arm_data_ptr += %d;\n", data_size >> 2);
|
||||
spare += min_offset;
|
||||
fprintf(outfile, " arm_data_ptr -= %d;\n", data_size >> 2);
|
||||
fprintf(outfile, " arm_pool_ptr -= %d;\n", data_size);
|
||||
fprintf(outfile, " if (arm_pool_ptr > gen_code_ptr + %d)\n"
|
||||
" arm_pool_ptr = gen_code_ptr + %d;\n",
|
||||
spare, spare);
|
||||
|
||||
data_index = 0;
|
||||
for (pc_offset = min_offset;
|
||||
pc_offset < p_end - p_start;
|
||||
pc_offset += 4) {
|
||||
|
||||
ELF_RELOC *rel;
|
||||
int i, addend, type;
|
||||
const char *sym_name;
|
||||
char relname[1024];
|
||||
|
||||
/* data value */
|
||||
addend = get32((uint32_t *)(p_start + pc_offset));
|
||||
relname[0] = '\0';
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset == (pc_offset + start_offset)) {
|
||||
sym_name = get_rel_sym_name(rel);
|
||||
/* the compiler leave some unnecessary references to the code */
|
||||
get_reloc_expr(relname, sizeof(relname), sym_name);
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
if (type != R_ARM_ABS32)
|
||||
error("%s: unsupported data relocation", name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(outfile, " arm_data_ptr[%d] = 0x%x",
|
||||
data_index, addend);
|
||||
if (relname[0] != '\0')
|
||||
fprintf(outfile, " + %s", relname);
|
||||
fprintf(outfile, ";\n");
|
||||
|
||||
data_index++;
|
||||
}
|
||||
}
|
||||
|
||||
/* the last instruction must be a mov pc, lr */
|
||||
if (p == p_start)
|
||||
goto arm_ret_error;
|
||||
p -= 4;
|
||||
insn = get32((uint32_t *)p);
|
||||
if ((insn & 0xffff0000) != 0xe91b0000) {
|
||||
/* The last instruction must be an ldm instruction. There are several
|
||||
forms generated by gcc:
|
||||
ldmib sp, {..., pc} (implies a sp adjustment of +4)
|
||||
ldmia sp, {..., pc}
|
||||
ldmea fp, {..., pc} */
|
||||
if ((insn & 0xffff8000) == 0xe99d8000) {
|
||||
if (outfile) {
|
||||
fprintf(outfile,
|
||||
" *(uint32_t *)(gen_code_ptr + %d) = 0xe28dd004;\n",
|
||||
p - p_start);
|
||||
}
|
||||
p += 4;
|
||||
} else if ((insn & 0xffff8000) != 0xe89d8000
|
||||
&& (insn & 0xffff8000) != 0xe91b8000) {
|
||||
arm_ret_error:
|
||||
if (!outfile)
|
||||
printf("%s: invalid epilog\n", name);
|
||||
}
|
||||
return p - p_start;
|
||||
return p - p_start;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1440,6 +1511,15 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
}
|
||||
#elif defined(HOST_SPARC)
|
||||
{
|
||||
#define INSN_SAVE 0x9de3a000
|
||||
#define INSN_RET 0x81c7e008
|
||||
#define INSN_RETL 0x81c3e008
|
||||
#define INSN_RESTORE 0x81e80000
|
||||
#define INSN_RETURN 0x81cfe008
|
||||
#define INSN_NOP 0x01000000
|
||||
#define INSN_ADD_SP 0x9c03a000 // add %sp, nn, %sp
|
||||
#define INSN_SUB_SP 0x9c23a000 // sub %sp, nn, %sp
|
||||
|
||||
uint32_t start_insn, end_insn1, end_insn2;
|
||||
uint8_t *p;
|
||||
p = (void *)(p_end - 8);
|
||||
@@ -1448,13 +1528,21 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
start_insn = get32((uint32_t *)(p_start + 0x0));
|
||||
end_insn1 = get32((uint32_t *)(p + 0x0));
|
||||
end_insn2 = get32((uint32_t *)(p + 0x4));
|
||||
if ((start_insn & ~0x1fff) == 0x9de3a000) {
|
||||
if (((start_insn & ~0x1fff) == INSN_SAVE) ||
|
||||
(start_insn & ~0x1fff) == INSN_ADD_SP) {
|
||||
p_start += 0x4;
|
||||
start_offset += 0x4;
|
||||
if ((int)(start_insn | ~0x1fff) < -128)
|
||||
error("Found bogus save at the start of %s", name);
|
||||
if (end_insn1 != 0x81c7e008 || end_insn2 != 0x81e80000)
|
||||
if (end_insn1 == INSN_RET && end_insn2 == INSN_RESTORE)
|
||||
/* SPARC v7: ret; restore; */ ;
|
||||
else if (end_insn1 == INSN_RETURN && end_insn2 == INSN_NOP)
|
||||
/* SPARC v9: return; nop; */ ;
|
||||
else if (end_insn1 == INSN_RETL && (end_insn2 & ~0x1fff) == INSN_SUB_SP)
|
||||
/* SPARC v7: retl; sub %sp, nn, %sp; */ ;
|
||||
else
|
||||
|
||||
error("ret; restore; not found at end of %s", name);
|
||||
} else if (end_insn1 == INSN_RETL && end_insn2 == INSN_NOP) {
|
||||
;
|
||||
} else {
|
||||
error("No save at the beginning of %s", name);
|
||||
}
|
||||
@@ -1462,7 +1550,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
/* Skip a preceeding nop, if present. */
|
||||
if (p > p_start) {
|
||||
skip_insn = get32((uint32_t *)(p - 0x4));
|
||||
if (skip_insn == 0x01000000)
|
||||
if (skip_insn == INSN_NOP)
|
||||
p -= 4;
|
||||
}
|
||||
#endif
|
||||
@@ -1470,21 +1558,41 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
}
|
||||
#elif defined(HOST_SPARC64)
|
||||
{
|
||||
#define INSN_SAVE 0x9de3a000
|
||||
#define INSN_RET 0x81c7e008
|
||||
#define INSN_RETL 0x81c3e008
|
||||
#define INSN_RESTORE 0x81e80000
|
||||
#define INSN_RETURN 0x81cfe008
|
||||
#define INSN_NOP 0x01000000
|
||||
#define INSN_ADD_SP 0x9c03a000 // add %sp, nn, %sp
|
||||
#define INSN_SUB_SP 0x9c23a000 // sub %sp, nn, %sp
|
||||
|
||||
uint32_t start_insn, end_insn1, end_insn2, skip_insn;
|
||||
uint8_t *p;
|
||||
p = (void *)(p_end - 8);
|
||||
#if 0
|
||||
/* XXX: check why it occurs */
|
||||
if (p <= p_start)
|
||||
error("empty code for %s", name);
|
||||
#endif
|
||||
start_insn = get32((uint32_t *)(p_start + 0x0));
|
||||
end_insn1 = get32((uint32_t *)(p + 0x0));
|
||||
end_insn2 = get32((uint32_t *)(p + 0x4));
|
||||
if ((start_insn & ~0x1fff) == 0x9de3a000) {
|
||||
if (((start_insn & ~0x1fff) == INSN_SAVE) ||
|
||||
(start_insn & ~0x1fff) == INSN_ADD_SP) {
|
||||
p_start += 0x4;
|
||||
start_offset += 0x4;
|
||||
if ((int)(start_insn | ~0x1fff) < -256)
|
||||
error("Found bogus save at the start of %s", name);
|
||||
if (end_insn1 != 0x81c7e008 || end_insn2 != 0x81e80000)
|
||||
if (end_insn1 == INSN_RET && end_insn2 == INSN_RESTORE)
|
||||
/* SPARC v7: ret; restore; */ ;
|
||||
else if (end_insn1 == INSN_RETURN && end_insn2 == INSN_NOP)
|
||||
/* SPARC v9: return; nop; */ ;
|
||||
else if (end_insn1 == INSN_RETL && (end_insn2 & ~0x1fff) == INSN_SUB_SP)
|
||||
/* SPARC v7: retl; sub %sp, nn, %sp; */ ;
|
||||
else
|
||||
|
||||
error("ret; restore; not found at end of %s", name);
|
||||
} else if (end_insn1 == INSN_RETL && end_insn2 == INSN_NOP) {
|
||||
;
|
||||
} else {
|
||||
error("No save at the beginning of %s", name);
|
||||
}
|
||||
@@ -1500,6 +1608,8 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
}
|
||||
#elif defined(HOST_ARM)
|
||||
{
|
||||
uint32_t insn;
|
||||
|
||||
if ((p_end - p_start) <= 16)
|
||||
error("%s: function too small", name);
|
||||
if (get32((uint32_t *)p_start) != 0xe1a0c00d ||
|
||||
@@ -1508,6 +1618,12 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
error("%s: invalid prolog", name);
|
||||
p_start += 12;
|
||||
start_offset += 12;
|
||||
insn = get32((uint32_t *)p_start);
|
||||
if ((insn & 0xffffff00) == 0xe24dd000) {
|
||||
/* Stack adjustment. Assume op uses the frame pointer. */
|
||||
p_start -= 4;
|
||||
start_offset -= 4;
|
||||
}
|
||||
copy_size = arm_emit_ldr_info(name, start_offset, NULL, p_start, p_end,
|
||||
relocs, nb_relocs);
|
||||
}
|
||||
@@ -1679,7 +1795,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
#endif
|
||||
if (val >= start_offset && val <= start_offset + copy_size) {
|
||||
n = strtol(p, NULL, 10);
|
||||
fprintf(outfile, " label_offsets[%d] = %ld + (gen_code_ptr - gen_code_buf);\n", n, val - start_offset);
|
||||
fprintf(outfile, " label_offsets[%d] = %ld + (gen_code_ptr - gen_code_buf);\n", n, (long)(val - start_offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1696,12 +1812,14 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
char name[256];
|
||||
int type;
|
||||
int addend;
|
||||
int reloc_offset;
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset >= start_offset &&
|
||||
rel->r_offset < start_offset + copy_size) {
|
||||
sym_name = get_rel_sym_name(rel);
|
||||
if (!sym_name)
|
||||
continue;
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
if (strstart(sym_name, "__op_jmp", &p)) {
|
||||
int n;
|
||||
n = strtol(p, NULL, 10);
|
||||
@@ -1710,10 +1828,10 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
chaining: the offset of the instruction
|
||||
needs to be stored */
|
||||
fprintf(outfile, " jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n",
|
||||
n, rel->r_offset - start_offset);
|
||||
n, reloc_offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
get_reloc_expr(name, sizeof(name), sym_name);
|
||||
addend = get32((uint32_t *)(text + rel->r_offset));
|
||||
#ifdef CONFIG_FORMAT_ELF
|
||||
@@ -1721,11 +1839,11 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
switch(type) {
|
||||
case R_386_32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_386_PC32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d;\n",
|
||||
rel->r_offset - start_offset, name, rel->r_offset - start_offset, addend);
|
||||
reloc_offset, name, reloc_offset, addend);
|
||||
break;
|
||||
default:
|
||||
error("unsupported i386 relocation (%d)", type);
|
||||
@@ -1748,11 +1866,11 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
switch(type) {
|
||||
case DIR32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case DISP32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d -4;\n",
|
||||
rel->r_offset - start_offset, name, rel->r_offset - start_offset, addend);
|
||||
reloc_offset, name, reloc_offset, addend);
|
||||
break;
|
||||
default:
|
||||
error("unsupported i386 relocation (%d)", type);
|
||||
@@ -1768,6 +1886,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
char name[256];
|
||||
int type;
|
||||
int addend;
|
||||
int reloc_offset;
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset >= start_offset &&
|
||||
rel->r_offset < start_offset + copy_size) {
|
||||
@@ -1775,18 +1894,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
get_reloc_expr(name, sizeof(name), sym_name);
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
addend = rel->r_addend;
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
switch(type) {
|
||||
case R_X86_64_32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (uint32_t)%s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_X86_64_32S:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (int32_t)%s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_X86_64_PC32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d;\n",
|
||||
rel->r_offset - start_offset, name, rel->r_offset - start_offset, addend);
|
||||
reloc_offset, name, reloc_offset, addend);
|
||||
break;
|
||||
default:
|
||||
error("unsupported X86_64 relocation (%d)", type);
|
||||
@@ -1800,10 +1920,12 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
char name[256];
|
||||
int type;
|
||||
int addend;
|
||||
int reloc_offset;
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset >= start_offset &&
|
||||
rel->r_offset < start_offset + copy_size) {
|
||||
sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
if (strstart(sym_name, "__op_jmp", &p)) {
|
||||
int n;
|
||||
n = strtol(p, NULL, 10);
|
||||
@@ -1812,7 +1934,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
chaining: the offset of the instruction
|
||||
needs to be stored */
|
||||
fprintf(outfile, " jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n",
|
||||
n, rel->r_offset - start_offset);
|
||||
n, reloc_offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1822,24 +1944,24 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
switch(type) {
|
||||
case R_PPC_ADDR32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_PPC_ADDR16_LO:
|
||||
fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d);\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_PPC_ADDR16_HI:
|
||||
fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d) >> 16;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_PPC_ADDR16_HA:
|
||||
fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d + 0x8000) >> 16;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_PPC_REL24:
|
||||
/* warning: must be at 32 MB distancy */
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (*(uint32_t *)(gen_code_ptr + %d) & ~0x03fffffc) | ((%s - (long)(gen_code_ptr + %d) + %d) & 0x03fffffc);\n",
|
||||
rel->r_offset - start_offset, rel->r_offset - start_offset, name, rel->r_offset - start_offset, addend);
|
||||
reloc_offset, reloc_offset, name, reloc_offset, addend);
|
||||
break;
|
||||
default:
|
||||
error("unsupported powerpc relocation (%d)", type);
|
||||
@@ -1941,6 +2063,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
char name[256];
|
||||
int type;
|
||||
int addend;
|
||||
int reloc_offset;
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset >= start_offset &&
|
||||
rel->r_offset < start_offset + copy_size) {
|
||||
@@ -1948,18 +2071,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
get_reloc_expr(name, sizeof(name), sym_name);
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
addend = rel->r_addend;
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
switch(type) {
|
||||
case R_390_32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_390_16:
|
||||
fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_390_8:
|
||||
fprintf(outfile, " *(uint8_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
default:
|
||||
error("unsupported s390 relocation (%d)", type);
|
||||
@@ -1972,17 +2096,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
for (i = 0, rel = relocs; i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset >= start_offset && rel->r_offset < start_offset + copy_size) {
|
||||
int type;
|
||||
long reloc_offset;
|
||||
|
||||
type = ELF64_R_TYPE(rel->r_info);
|
||||
sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name;
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
switch (type) {
|
||||
case R_ALPHA_GPDISP:
|
||||
/* The gp is just 32 bit, and never changes, so it's easiest to emit it
|
||||
as an immediate instead of constructing it from the pv or ra. */
|
||||
fprintf(outfile, " immediate_ldah(gen_code_ptr + %ld, gp);\n",
|
||||
rel->r_offset - start_offset);
|
||||
reloc_offset);
|
||||
fprintf(outfile, " immediate_lda(gen_code_ptr + %ld, gp);\n",
|
||||
rel->r_offset - start_offset + rel->r_addend);
|
||||
reloc_offset + (int)rel->r_addend);
|
||||
break;
|
||||
case R_ALPHA_LITUSE:
|
||||
/* jsr to literal hint. Could be used to optimize to bsr. Ignore for
|
||||
@@ -2002,18 +2128,18 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
special treatment. */
|
||||
if (strstart(sym_name, "__op_param", &p))
|
||||
fprintf(outfile, " immediate_ldah(gen_code_ptr + %ld, param%s);\n",
|
||||
rel->r_offset - start_offset, p);
|
||||
reloc_offset, p);
|
||||
break;
|
||||
case R_ALPHA_GPRELLOW:
|
||||
if (strstart(sym_name, "__op_param", &p))
|
||||
fprintf(outfile, " immediate_lda(gen_code_ptr + %ld, param%s);\n",
|
||||
rel->r_offset - start_offset, p);
|
||||
reloc_offset, p);
|
||||
break;
|
||||
case R_ALPHA_BRSGP:
|
||||
/* PC-relative jump. Tweak offset to skip the two instructions that try to
|
||||
set up the gp from the pv. */
|
||||
fprintf(outfile, " fix_bsr(gen_code_ptr + %ld, (uint8_t *) &%s - (gen_code_ptr + %ld + 4) + 8);\n",
|
||||
rel->r_offset - start_offset, sym_name, rel->r_offset - start_offset);
|
||||
reloc_offset, sym_name, reloc_offset);
|
||||
break;
|
||||
default:
|
||||
error("unsupported Alpha relocation (%d)", type);
|
||||
@@ -2035,6 +2161,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
|| rel->r_offset >= start_offset + copy_size)
|
||||
continue;
|
||||
sym_name = (strtab + symtab[sym_idx].st_name);
|
||||
code_offset = rel->r_offset - start_offset;
|
||||
if (strstart(sym_name, "__op_jmp", &p)) {
|
||||
int n;
|
||||
n = strtol(p, NULL, 10);
|
||||
@@ -2044,13 +2171,12 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
needs to be stored */
|
||||
fprintf(outfile, " jmp_offsets[%d] ="
|
||||
"%ld + (gen_code_ptr - gen_code_buf);\n",
|
||||
n, rel->r_offset - start_offset);
|
||||
n, code_offset);
|
||||
continue;
|
||||
}
|
||||
get_reloc_expr(name, sizeof(name), sym_name);
|
||||
type = ELF64_R_TYPE(rel->r_info);
|
||||
addend = rel->r_addend;
|
||||
code_offset = rel->r_offset - start_offset;
|
||||
switch(type) {
|
||||
case R_IA64_IMM64:
|
||||
fprintf(outfile,
|
||||
@@ -2101,6 +2227,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
char name[256];
|
||||
int type;
|
||||
int addend;
|
||||
int reloc_offset;
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset >= start_offset &&
|
||||
rel->r_offset < start_offset + copy_size) {
|
||||
@@ -2108,10 +2235,11 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
get_reloc_expr(name, sizeof(name), sym_name);
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
addend = rel->r_addend;
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
switch(type) {
|
||||
case R_SPARC_32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_SPARC_HI22:
|
||||
fprintf(outfile,
|
||||
@@ -2119,9 +2247,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
"((*(uint32_t *)(gen_code_ptr + %d)) "
|
||||
" & ~0x3fffff) "
|
||||
" | (((%s + %d) >> 10) & 0x3fffff);\n",
|
||||
rel->r_offset - start_offset,
|
||||
rel->r_offset - start_offset,
|
||||
name, addend);
|
||||
reloc_offset, reloc_offset, name, addend);
|
||||
break;
|
||||
case R_SPARC_LO10:
|
||||
fprintf(outfile,
|
||||
@@ -2129,9 +2255,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
"((*(uint32_t *)(gen_code_ptr + %d)) "
|
||||
" & ~0x3ff) "
|
||||
" | ((%s + %d) & 0x3ff);\n",
|
||||
rel->r_offset - start_offset,
|
||||
rel->r_offset - start_offset,
|
||||
name, addend);
|
||||
reloc_offset, reloc_offset, name, addend);
|
||||
break;
|
||||
case R_SPARC_WDISP30:
|
||||
fprintf(outfile,
|
||||
@@ -2140,11 +2264,21 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
" & ~0x3fffffff) "
|
||||
" | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
|
||||
" & 0x3fffffff);\n",
|
||||
rel->r_offset - start_offset,
|
||||
rel->r_offset - start_offset,
|
||||
name, addend,
|
||||
rel->r_offset - start_offset);
|
||||
reloc_offset, reloc_offset, name, addend,
|
||||
reloc_offset);
|
||||
break;
|
||||
case R_SPARC_WDISP22:
|
||||
fprintf(outfile,
|
||||
" *(uint32_t *)(gen_code_ptr + %d) = "
|
||||
"((*(uint32_t *)(gen_code_ptr + %d)) "
|
||||
" & ~0x3fffff) "
|
||||
" | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
|
||||
" & 0x3fffff);\n",
|
||||
rel->r_offset - start_offset,
|
||||
rel->r_offset - start_offset,
|
||||
name, addend,
|
||||
rel->r_offset - start_offset);
|
||||
break;
|
||||
default:
|
||||
error("unsupported sparc relocation (%d)", type);
|
||||
}
|
||||
@@ -2156,17 +2290,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
char name[256];
|
||||
int type;
|
||||
int addend;
|
||||
int reloc_offset;
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset >= start_offset &&
|
||||
rel->r_offset < start_offset + copy_size) {
|
||||
sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name;
|
||||
get_reloc_expr(name, sizeof(name), sym_name);
|
||||
type = ELF64_R_TYPE(rel->r_info);
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
addend = rel->r_addend;
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
switch(type) {
|
||||
case R_SPARC_32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_SPARC_HI22:
|
||||
fprintf(outfile,
|
||||
@@ -2174,9 +2310,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
"((*(uint32_t *)(gen_code_ptr + %d)) "
|
||||
" & ~0x3fffff) "
|
||||
" | (((%s + %d) >> 10) & 0x3fffff);\n",
|
||||
rel->r_offset - start_offset,
|
||||
rel->r_offset - start_offset,
|
||||
name, addend);
|
||||
reloc_offset, reloc_offset, name, addend);
|
||||
break;
|
||||
case R_SPARC_LO10:
|
||||
fprintf(outfile,
|
||||
@@ -2184,9 +2318,16 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
"((*(uint32_t *)(gen_code_ptr + %d)) "
|
||||
" & ~0x3ff) "
|
||||
" | ((%s + %d) & 0x3ff);\n",
|
||||
rel->r_offset - start_offset,
|
||||
rel->r_offset - start_offset,
|
||||
name, addend);
|
||||
reloc_offset, reloc_offset, name, addend);
|
||||
break;
|
||||
case R_SPARC_OLO10:
|
||||
addend += ELF64_R_TYPE_DATA (rel->r_info);
|
||||
fprintf(outfile,
|
||||
" *(uint32_t *)(gen_code_ptr + %d) = "
|
||||
"((*(uint32_t *)(gen_code_ptr + %d)) "
|
||||
" & ~0x3ff) "
|
||||
" | ((%s + %d) & 0x3ff);\n",
|
||||
reloc_offset, reloc_offset, name, addend);
|
||||
break;
|
||||
case R_SPARC_WDISP30:
|
||||
fprintf(outfile,
|
||||
@@ -2195,13 +2336,21 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
" & ~0x3fffffff) "
|
||||
" | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
|
||||
" & 0x3fffffff);\n",
|
||||
rel->r_offset - start_offset,
|
||||
rel->r_offset - start_offset,
|
||||
name, addend,
|
||||
rel->r_offset - start_offset);
|
||||
reloc_offset, reloc_offset, name, addend,
|
||||
reloc_offset);
|
||||
break;
|
||||
case R_SPARC_WDISP22:
|
||||
fprintf(outfile,
|
||||
" *(uint32_t *)(gen_code_ptr + %d) = "
|
||||
"((*(uint32_t *)(gen_code_ptr + %d)) "
|
||||
" & ~0x3fffff) "
|
||||
" | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
|
||||
" & 0x3fffff);\n",
|
||||
reloc_offset, reloc_offset, name, addend,
|
||||
reloc_offset);
|
||||
break;
|
||||
default:
|
||||
error("unsupported sparc64 relocation (%d)", type);
|
||||
error("unsupported sparc64 relocation (%d) for symbol %s", type, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2211,7 +2360,38 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
char name[256];
|
||||
int type;
|
||||
int addend;
|
||||
int reloc_offset;
|
||||
uint32_t insn;
|
||||
|
||||
insn = get32((uint32_t *)(p_start + 4));
|
||||
/* If prologue ends in sub sp, sp, #const then assume
|
||||
op has a stack frame and needs the frame pointer. */
|
||||
if ((insn & 0xffffff00) == 0xe24dd000) {
|
||||
int i;
|
||||
uint32_t opcode;
|
||||
opcode = 0xe28db000; /* add fp, sp, #0. */
|
||||
#if 0
|
||||
/* ??? Need to undo the extra stack adjustment at the end of the op.
|
||||
For now just leave the stack misaligned and hope it doesn't break anything
|
||||
too important. */
|
||||
if ((insn & 4) != 0) {
|
||||
/* Preserve doubleword stack alignment. */
|
||||
fprintf(outfile,
|
||||
" *(uint32_t *)(gen_code_ptr + 4)= 0x%x;\n",
|
||||
insn + 4);
|
||||
opcode -= 4;
|
||||
}
|
||||
#endif
|
||||
insn = get32((uint32_t *)(p_start - 4));
|
||||
/* Calculate the size of the saved registers,
|
||||
excluding pc. */
|
||||
for (i = 0; i < 15; i++) {
|
||||
if (insn & (1 << i))
|
||||
opcode += 4;
|
||||
}
|
||||
fprintf(outfile,
|
||||
" *(uint32_t *)gen_code_ptr = 0x%x;\n", opcode);
|
||||
}
|
||||
arm_emit_ldr_info(name, start_offset, outfile, p_start, p_end,
|
||||
relocs, nb_relocs);
|
||||
|
||||
@@ -2225,14 +2405,17 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
get_reloc_expr(name, sizeof(name), sym_name);
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
addend = get32((uint32_t *)(text + rel->r_offset));
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
switch(type) {
|
||||
case R_ARM_ABS32:
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
|
||||
rel->r_offset - start_offset, name, addend);
|
||||
reloc_offset, name, addend);
|
||||
break;
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_CALL:
|
||||
fprintf(outfile, " arm_reloc_pc24((uint32_t *)(gen_code_ptr + %d), 0x%x, %s);\n",
|
||||
rel->r_offset - start_offset, addend, name);
|
||||
reloc_offset, addend, name);
|
||||
break;
|
||||
default:
|
||||
error("unsupported arm relocation (%d)", type);
|
||||
@@ -2245,6 +2428,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
char name[256];
|
||||
int type;
|
||||
int addend;
|
||||
int reloc_offset;
|
||||
Elf32_Sym *sym;
|
||||
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
|
||||
if (rel->r_offset >= start_offset &&
|
||||
@@ -2254,16 +2438,17 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
get_reloc_expr(name, sizeof(name), sym_name);
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
addend = get32((uint32_t *)(text + rel->r_offset)) + rel->r_addend;
|
||||
reloc_offset = rel->r_offset - start_offset;
|
||||
switch(type) {
|
||||
case R_68K_32:
|
||||
fprintf(outfile, " /* R_68K_32 RELOC, offset %x */\n", rel->r_offset) ;
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %#x;\n",
|
||||
rel->r_offset - start_offset, name, addend );
|
||||
reloc_offset, name, addend );
|
||||
break;
|
||||
case R_68K_PC32:
|
||||
fprintf(outfile, " /* R_68K_PC32 RELOC, offset %x */\n", rel->r_offset);
|
||||
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %#x) + %#x;\n",
|
||||
rel->r_offset - start_offset, name, rel->r_offset - start_offset, /*sym->st_value+*/ addend);
|
||||
reloc_offset, name, reloc_offset, /*sym->st_value+*/ addend);
|
||||
break;
|
||||
default:
|
||||
error("unsupported m68k relocation (%d)", type);
|
||||
@@ -2333,6 +2518,28 @@ int gen_file(FILE *outfile, int out_type)
|
||||
|
||||
} else {
|
||||
/* generate big code generation switch */
|
||||
|
||||
#ifdef HOST_ARM
|
||||
/* We need to know the size of all the ops so we can figure out when
|
||||
to emit constant pools. This must be consistent with opc.h. */
|
||||
fprintf(outfile,
|
||||
"static const uint32_t arm_opc_size[] = {\n"
|
||||
" 0,\n" /* end */
|
||||
" 0,\n" /* nop */
|
||||
" 0,\n" /* nop1 */
|
||||
" 0,\n" /* nop2 */
|
||||
" 0,\n"); /* nop3 */
|
||||
for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
|
||||
const char *name;
|
||||
name = get_sym_name(sym);
|
||||
if (strstart(name, OP_PREFIX, NULL)) {
|
||||
fprintf(outfile, " %d,\n", sym->st_size);
|
||||
}
|
||||
}
|
||||
fprintf(outfile,
|
||||
"};\n");
|
||||
#endif
|
||||
|
||||
fprintf(outfile,
|
||||
"int dyngen_code(uint8_t *gen_code_buf,\n"
|
||||
" uint16_t *label_offsets, uint16_t *jmp_offsets,\n"
|
||||
@@ -2343,10 +2550,36 @@ fprintf(outfile,
|
||||
" const uint32_t *opparam_ptr;\n");
|
||||
|
||||
#ifdef HOST_ARM
|
||||
/* Arm is tricky because it uses constant pools for loading immediate values.
|
||||
We assume (and require) each function is code followed by a constant pool.
|
||||
All the ops are small so this should be ok. For each op we figure
|
||||
out how much "spare" range we have in the load instructions. This allows
|
||||
us to insert subsequent ops in between the op and the constant pool,
|
||||
eliminating the neeed to jump around the pool.
|
||||
|
||||
We currently generate:
|
||||
|
||||
[ For this example we assume merging would move op1_pool out of range.
|
||||
In practice we should be able to combine many ops before the offset
|
||||
limits are reached. ]
|
||||
op1_code;
|
||||
op2_code;
|
||||
goto op3;
|
||||
op2_pool;
|
||||
op1_pool;
|
||||
op3:
|
||||
op3_code;
|
||||
ret;
|
||||
op3_pool;
|
||||
|
||||
Ideally we'd put op1_pool before op2_pool, but that requires two passes.
|
||||
*/
|
||||
fprintf(outfile,
|
||||
" uint8_t *last_gen_code_ptr = gen_code_buf;\n"
|
||||
" LDREntry *arm_ldr_ptr = arm_ldr_table;\n"
|
||||
" uint32_t *arm_data_ptr = arm_data_table;\n");
|
||||
" uint32_t *arm_data_ptr = arm_data_table + ARM_LDR_TABLE_SIZE;\n"
|
||||
/* Initialise the parmissible pool offset to an arbitary large value. */
|
||||
" uint8_t *arm_pool_ptr = gen_code_buf + 0x1000000;\n");
|
||||
#endif
|
||||
#ifdef HOST_IA64
|
||||
{
|
||||
@@ -2415,9 +2648,23 @@ fprintf(outfile,
|
||||
/* Generate prologue, if needed. */
|
||||
|
||||
fprintf(outfile,
|
||||
" for(;;) {\n"
|
||||
" switch(*opc_ptr++) {\n"
|
||||
);
|
||||
" for(;;) {\n");
|
||||
|
||||
#ifdef HOST_ARM
|
||||
/* Generate constant pool if needed */
|
||||
fprintf(outfile,
|
||||
" if (gen_code_ptr + arm_opc_size[*opc_ptr] >= arm_pool_ptr) {\n"
|
||||
" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, "
|
||||
"arm_ldr_ptr, arm_data_ptr, arm_data_table + ARM_LDR_TABLE_SIZE, 1);\n"
|
||||
" last_gen_code_ptr = gen_code_ptr;\n"
|
||||
" arm_ldr_ptr = arm_ldr_table;\n"
|
||||
" arm_data_ptr = arm_data_table + ARM_LDR_TABLE_SIZE;\n"
|
||||
" arm_pool_ptr = gen_code_ptr + 0x1000000;\n"
|
||||
" }\n");
|
||||
#endif
|
||||
|
||||
fprintf(outfile,
|
||||
" switch(*opc_ptr++) {\n");
|
||||
|
||||
for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
|
||||
const char *name;
|
||||
@@ -2451,17 +2698,6 @@ fprintf(outfile,
|
||||
" goto the_end;\n"
|
||||
" }\n");
|
||||
|
||||
#ifdef HOST_ARM
|
||||
/* generate constant table if needed */
|
||||
fprintf(outfile,
|
||||
" if ((gen_code_ptr - last_gen_code_ptr) >= (MAX_FRAG_SIZE - MAX_OP_SIZE)) {\n"
|
||||
" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 1);\n"
|
||||
" last_gen_code_ptr = gen_code_ptr;\n"
|
||||
" arm_ldr_ptr = arm_ldr_table;\n"
|
||||
" arm_data_ptr = arm_data_table;\n"
|
||||
" }\n");
|
||||
#endif
|
||||
|
||||
|
||||
fprintf(outfile,
|
||||
" }\n"
|
||||
@@ -2469,15 +2705,20 @@ fprintf(outfile,
|
||||
);
|
||||
#ifdef HOST_IA64
|
||||
fprintf(outfile,
|
||||
" ia64_apply_fixes(&gen_code_ptr, ltoff_fixes, "
|
||||
" {\n"
|
||||
" extern char code_gen_buffer[];\n"
|
||||
" ia64_apply_fixes(&gen_code_ptr, ltoff_fixes, "
|
||||
"(uint64_t) code_gen_buffer + 2*(1<<20), plt_fixes,\n\t\t\t"
|
||||
"sizeof(plt_target)/sizeof(plt_target[0]),\n\t\t\t"
|
||||
"plt_target, plt_offset);\n");
|
||||
"plt_target, plt_offset);\n }\n");
|
||||
#endif
|
||||
|
||||
/* generate some code patching */
|
||||
#ifdef HOST_ARM
|
||||
fprintf(outfile, "gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 0);\n");
|
||||
fprintf(outfile,
|
||||
"if (arm_data_ptr != arm_data_table + ARM_LDR_TABLE_SIZE)\n"
|
||||
" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, "
|
||||
"arm_ldr_ptr, arm_data_ptr, arm_data_table + ARM_LDR_TABLE_SIZE, 0);\n");
|
||||
#endif
|
||||
/* flush instruction cache */
|
||||
fprintf(outfile, "flush_icache_range((unsigned long)gen_code_buf, (unsigned long)gen_code_ptr);\n");
|
||||
|
||||
@@ -19,7 +19,13 @@
|
||||
*/
|
||||
|
||||
int __op_param1, __op_param2, __op_param3;
|
||||
int __op_gen_label1, __op_gen_label2, __op_gen_label3;
|
||||
#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__
|
||||
@@ -59,7 +65,7 @@ static void inline flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
unsigned long p;
|
||||
|
||||
p = start & ~(MIN_CACHE_LINE_SIZE - 1);
|
||||
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) {
|
||||
@@ -139,18 +145,16 @@ void fix_bsr(void *p, int offset) {
|
||||
|
||||
#ifdef __arm__
|
||||
|
||||
#define MAX_OP_SIZE (128 * 4) /* in bytes */
|
||||
/* max size of the code that can be generated without calling arm_flush_ldr */
|
||||
#define MAX_FRAG_SIZE (1024 * 4)
|
||||
//#define MAX_FRAG_SIZE (135 * 4) /* for testing */
|
||||
#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[1024];
|
||||
static uint32_t arm_data_table[ARM_LDR_TABLE_SIZE];
|
||||
|
||||
extern char exec_loop;
|
||||
|
||||
@@ -169,8 +173,9 @@ static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr,
|
||||
int offset, data_size, target;
|
||||
uint8_t *data_ptr;
|
||||
uint32_t insn;
|
||||
uint32_t mask;
|
||||
|
||||
data_size = (uint8_t *)data_end - (uint8_t *)data_start;
|
||||
data_size = (data_end - data_start) << 2;
|
||||
|
||||
if (gen_jmp) {
|
||||
/* generate branch to skip the data */
|
||||
@@ -192,17 +197,48 @@ static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr,
|
||||
offset = ((unsigned long)(le->data_ptr) - (unsigned long)data_start) +
|
||||
(unsigned long)data_ptr -
|
||||
(unsigned long)ptr - 8;
|
||||
insn = *ptr & ~(0xfff | 0x00800000);
|
||||
if (offset < 0) {
|
||||
offset = - offset;
|
||||
} else {
|
||||
insn |= 0x00800000;
|
||||
}
|
||||
if (offset > 0xfff) {
|
||||
fprintf(stderr, "Error ldr offset\n");
|
||||
fprintf(stderr, "Negative constant pool offset\n");
|
||||
abort();
|
||||
}
|
||||
insn |= offset;
|
||||
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;
|
||||
@@ -420,6 +456,9 @@ static inline void ia64_apply_fixes (uint8_t **gen_code_pp,
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +227,7 @@ typedef struct {
|
||||
|
||||
#define ELF64_R_SYM(i) ((i) >> 32)
|
||||
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
|
||||
#define ELF64_R_TYPE_DATA(i) (((ELF64_R_TYPE(i) >> 8) ^ 0x00800000) - 0x00800000)
|
||||
|
||||
#define R_386_NONE 0
|
||||
#define R_386_32 1
|
||||
@@ -326,6 +327,7 @@ typedef struct {
|
||||
#define R_SPARC_10 30
|
||||
#define R_SPARC_11 31
|
||||
#define R_SPARC_64 32
|
||||
#define R_SPARC_OLO10 33
|
||||
#define R_SPARC_WDISP16 40
|
||||
#define R_SPARC_WDISP19 41
|
||||
#define R_SPARC_7 43
|
||||
@@ -500,6 +502,8 @@ typedef struct {
|
||||
#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
|
||||
#define R_ARM_GOT32 26 /* 32 bit GOT entry */
|
||||
#define R_ARM_PLT32 27 /* 32 bit PLT address */
|
||||
#define R_ARM_CALL 28
|
||||
#define R_ARM_JUMP24 29
|
||||
#define R_ARM_GNU_VTENTRY 100
|
||||
#define R_ARM_GNU_VTINHERIT 101
|
||||
#define R_ARM_THM_PC11 102 /* thumb unconditional branch */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+52
-59
@@ -62,6 +62,7 @@ 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);
|
||||
@@ -91,23 +92,28 @@ 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(void);
|
||||
int page_unprotect(unsigned long address, unsigned long pc, 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(CPUState *env, target_ulong vaddr,
|
||||
target_phys_addr_t paddr, int prot,
|
||||
int is_user, int is_softmmu);
|
||||
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_HASH_BITS 15
|
||||
#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS)
|
||||
|
||||
#define CODE_GEN_PHYS_HASH_BITS 15
|
||||
#define CODE_GEN_PHYS_HASH_SIZE (1 << CODE_GEN_PHYS_HASH_BITS)
|
||||
|
||||
@@ -167,7 +173,6 @@ typedef struct TranslationBlock {
|
||||
#define CF_SINGLE_INSN 0x0008 /* compile only a single instruction */
|
||||
|
||||
uint8_t *tc_ptr; /* pointer to the translated code */
|
||||
struct TranslationBlock *hash_next; /* next matching tb for virtual address */
|
||||
/* next matching tb for physical address. */
|
||||
struct TranslationBlock *phys_hash_next;
|
||||
/* first and second physical page containing code. The lower bit
|
||||
@@ -191,9 +196,19 @@ typedef struct TranslationBlock {
|
||||
struct TranslationBlock *jmp_first;
|
||||
} TranslationBlock;
|
||||
|
||||
static inline unsigned int tb_hash_func(target_ulong pc)
|
||||
static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc)
|
||||
{
|
||||
return pc & (CODE_GEN_HASH_SIZE - 1);
|
||||
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)
|
||||
@@ -203,41 +218,14 @@ static inline unsigned int tb_phys_hash_func(unsigned long pc)
|
||||
|
||||
TranslationBlock *tb_alloc(target_ulong pc);
|
||||
void tb_flush(CPUState *env);
|
||||
void tb_link(TranslationBlock *tb);
|
||||
void tb_link_phys(TranslationBlock *tb,
|
||||
target_ulong phys_pc, target_ulong phys_page2);
|
||||
|
||||
extern TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
|
||||
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;
|
||||
|
||||
/* find a translation block in the translation cache. If not found,
|
||||
return NULL and the pointer to the last element of the list in pptb */
|
||||
static inline TranslationBlock *tb_find(TranslationBlock ***pptb,
|
||||
target_ulong pc,
|
||||
target_ulong cs_base,
|
||||
unsigned int flags)
|
||||
{
|
||||
TranslationBlock **ptb, *tb;
|
||||
unsigned int h;
|
||||
|
||||
h = tb_hash_func(pc);
|
||||
ptb = &tb_hash[h];
|
||||
for(;;) {
|
||||
tb = *ptb;
|
||||
if (!tb)
|
||||
break;
|
||||
if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags)
|
||||
return tb;
|
||||
ptb = &tb->hash_next;
|
||||
}
|
||||
*pptb = ptb;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#if defined(USE_DIRECT_JUMP)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
@@ -320,13 +308,16 @@ TranslationBlock *tb_find_pc(unsigned long pc_ptr);
|
||||
#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_NAME(__op_label) #n "." ASM_NAME(opname) ":\n"\
|
||||
ASM_OP_LABEL_NAME(n, opname) ":\n"\
|
||||
".long 1f\n"\
|
||||
ASM_PREVIOUS_SECTION \
|
||||
"b " ASM_NAME(__op_jmp) #n "\n"\
|
||||
@@ -339,7 +330,7 @@ do {\
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
asm volatile (".section .data\n"\
|
||||
ASM_NAME(__op_label) #n "." ASM_NAME(opname) ":\n"\
|
||||
ASM_OP_LABEL_NAME(n, opname) ":\n"\
|
||||
".long 1f\n"\
|
||||
ASM_PREVIOUS_SECTION \
|
||||
"jmp " ASM_NAME(__op_jmp) #n "\n"\
|
||||
@@ -353,7 +344,8 @@ do {\
|
||||
#define GOTO_TB(opname, tbparam, n)\
|
||||
do {\
|
||||
static void __attribute__((unused)) *dummy ## n = &&dummy_label ## n;\
|
||||
static void __attribute__((unused)) *__op_label ## n = &&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: ;\
|
||||
@@ -361,15 +353,6 @@ dummy_label ## n: ;\
|
||||
|
||||
#endif
|
||||
|
||||
/* XXX: will be suppressed */
|
||||
#define JUMP_TB(opname, tbparam, n, eip)\
|
||||
do {\
|
||||
GOTO_TB(opname, tbparam, n);\
|
||||
T0 = (long)(tbparam) + (n);\
|
||||
EIP = (int32_t)eip;\
|
||||
EXIT_TB();\
|
||||
} while (0)
|
||||
|
||||
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];
|
||||
@@ -572,7 +555,6 @@ static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
|
||||
/* 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 */
|
||||
/* XXX: i386 target specific */
|
||||
static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
|
||||
{
|
||||
int is_user, index, pd;
|
||||
@@ -586,37 +568,48 @@ static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
|
||||
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 !"
|
||||
#error unimplemented CPU
|
||||
#endif
|
||||
if (__builtin_expect(env->tlb_read[is_user][index].address !=
|
||||
if (__builtin_expect(env->tlb_table[is_user][index].addr_code !=
|
||||
(addr & TARGET_PAGE_MASK), 0)) {
|
||||
ldub_code(addr);
|
||||
}
|
||||
pd = env->tlb_read[is_user][index].address & ~TARGET_PAGE_MASK;
|
||||
if (pd > IO_MEM_ROM) {
|
||||
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_read[is_user][index].addend - (unsigned long)phys_ram_base;
|
||||
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->hflags & HF_CPL_MASK) == 3 &&
|
||||
(env->eflags & IOPL_MASK) != IOPL_MASK &&
|
||||
(env->cr[0] & CR0_PE_MASK) &&
|
||||
!(env->hflags & HF_INHIBIT_IRQ_MASK) &&
|
||||
(env->eflags & IF_MASK) &&
|
||||
!(env->eflags & VM_MASK) &&
|
||||
(env->ldt.limit == 0 || env->ldt.limit == 0x27));
|
||||
(env->kqemu_enabled == 2 ||
|
||||
((env->hflags & HF_CPL_MASK) == 3 &&
|
||||
(env->eflags & IOPL_MASK) != IOPL_MASK)));
|
||||
}
|
||||
|
||||
#endif
|
||||
+50
-14
@@ -6,7 +6,7 @@
|
||||
void set_float_rounding_mode(int val STATUS_PARAM)
|
||||
{
|
||||
STATUS(float_rounding_mode) = val;
|
||||
#if defined(_BSD) && !defined(__APPLE__)
|
||||
#if defined(_BSD) && !defined(__APPLE__) || (defined(HOST_SOLARIS) && HOST_SOLARIS < 10)
|
||||
fpsetround(val);
|
||||
#elif defined(__arm__)
|
||||
/* nothing to do */
|
||||
@@ -22,9 +22,14 @@ void set_floatx80_rounding_precision(int val STATUS_PARAM)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_BSD)
|
||||
#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__)
|
||||
@@ -80,12 +85,27 @@ floatx80 int64_to_floatx80( int64_t v STATUS_PARAM)
|
||||
}
|
||||
#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 lrintf(a);
|
||||
return long_to_int32(lrintf(a));
|
||||
}
|
||||
int float32_to_int32_round_to_zero( float32 a STATUS_PARAM)
|
||||
{
|
||||
@@ -129,7 +149,7 @@ float32 float32_sqrt( float32 a STATUS_PARAM)
|
||||
{
|
||||
return sqrtf(a);
|
||||
}
|
||||
char float32_compare( float32 a, float32 b STATUS_PARAM )
|
||||
int float32_compare( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
@@ -141,7 +161,7 @@ char float32_compare( float32 a, float32 b STATUS_PARAM )
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char float32_compare_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
int float32_compare_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
@@ -153,7 +173,7 @@ char float32_compare_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char float32_is_signaling_nan( float32 a1)
|
||||
int float32_is_signaling_nan( float32 a1)
|
||||
{
|
||||
float32u u;
|
||||
uint32_t a;
|
||||
@@ -167,7 +187,7 @@ char float32_is_signaling_nan( float32 a1)
|
||||
*----------------------------------------------------------------------------*/
|
||||
int float64_to_int32( float64 a STATUS_PARAM)
|
||||
{
|
||||
return lrint(a);
|
||||
return long_to_int32(lrint(a));
|
||||
}
|
||||
int float64_to_int32_round_to_zero( float64 a STATUS_PARAM)
|
||||
{
|
||||
@@ -201,6 +221,11 @@ float128 float64_to_float128( float64 a STATUS_PARAM)
|
||||
/*----------------------------------------------------------------------------
|
||||
| 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__)
|
||||
@@ -233,7 +258,7 @@ float64 float64_sqrt( float64 a STATUS_PARAM)
|
||||
{
|
||||
return sqrt(a);
|
||||
}
|
||||
char float64_compare( float64 a, float64 b STATUS_PARAM )
|
||||
int float64_compare( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
@@ -245,7 +270,7 @@ char float64_compare( float64 a, float64 b STATUS_PARAM )
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char float64_compare_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
int float64_compare_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
@@ -257,7 +282,7 @@ char float64_compare_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char float64_is_signaling_nan( float64 a1)
|
||||
int float64_is_signaling_nan( float64 a1)
|
||||
{
|
||||
float64u u;
|
||||
uint64_t a;
|
||||
@@ -269,6 +294,17 @@ char float64_is_signaling_nan( float64 a1)
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
@@ -276,7 +312,7 @@ char float64_is_signaling_nan( float64 a1)
|
||||
*----------------------------------------------------------------------------*/
|
||||
int floatx80_to_int32( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return lrintl(a);
|
||||
return long_to_int32(lrintl(a));
|
||||
}
|
||||
int floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
@@ -314,7 +350,7 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM)
|
||||
{
|
||||
return sqrtl(a);
|
||||
}
|
||||
char floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
int floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
if (a < b) {
|
||||
return -1;
|
||||
@@ -326,7 +362,7 @@ char floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
if (isless(a, b)) {
|
||||
return -1;
|
||||
@@ -338,7 +374,7 @@ char floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
char floatx80_is_signaling_nan( floatx80 a1)
|
||||
int floatx80_is_signaling_nan( floatx80 a1)
|
||||
{
|
||||
floatx80u u;
|
||||
u.f = a1;
|
||||
|
||||
+64
-35
@@ -1,11 +1,38 @@
|
||||
/* Native implementation of soft float functions */
|
||||
#include <math.h>
|
||||
#if defined(_BSD) && !defined(__APPLE__)
|
||||
|
||||
#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
|
||||
@@ -33,12 +60,12 @@ typedef union {
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE floating-point rounding mode.
|
||||
*----------------------------------------------------------------------------*/
|
||||
#if defined(_BSD) && !defined(__APPLE__)
|
||||
#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS)
|
||||
enum {
|
||||
float_round_nearest_even = FP_RN,
|
||||
float_round_down = FE_RM,
|
||||
float_round_up = FE_RP,
|
||||
float_round_to_zero = FE_RZ
|
||||
float_round_down = FP_RM,
|
||||
float_round_up = FP_RP,
|
||||
float_round_to_zero = FP_RZ
|
||||
};
|
||||
#elif defined(__arm__)
|
||||
enum {
|
||||
@@ -125,38 +152,38 @@ INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM)
|
||||
}
|
||||
float32 float32_rem( float32, float32 STATUS_PARAM);
|
||||
float32 float32_sqrt( float32 STATUS_PARAM);
|
||||
INLINE char float32_eq( float32 a, float32 b STATUS_PARAM)
|
||||
INLINE int float32_eq( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE char float32_le( float32 a, float32 b STATUS_PARAM)
|
||||
INLINE int float32_le( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE char float32_lt( float32 a, float32 b STATUS_PARAM)
|
||||
INLINE int float32_lt( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE char float32_eq_signaling( float32 a, float32 b STATUS_PARAM)
|
||||
INLINE int float32_eq_signaling( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE char float32_le_quiet( float32 a, float32 b STATUS_PARAM)
|
||||
INLINE int float32_le_quiet( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE char float32_lt_quiet( float32 a, float32 b STATUS_PARAM)
|
||||
INLINE int float32_lt_quiet( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
}
|
||||
INLINE char float32_unordered( float32 a, float32 b STATUS_PARAM)
|
||||
INLINE int float32_unordered( float32 a, float32 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
char float32_compare( float32, float32 STATUS_PARAM );
|
||||
char float32_compare_quiet( float32, float32 STATUS_PARAM );
|
||||
char float32_is_signaling_nan( float32 );
|
||||
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)
|
||||
{
|
||||
@@ -187,6 +214,7 @@ float128 float64_to_float128( float64 STATUS_PARAM );
|
||||
| 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;
|
||||
@@ -205,39 +233,40 @@ INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM)
|
||||
}
|
||||
float64 float64_rem( float64, float64 STATUS_PARAM );
|
||||
float64 float64_sqrt( float64 STATUS_PARAM );
|
||||
INLINE char float64_eq( float64 a, float64 b STATUS_PARAM)
|
||||
INLINE int float64_eq( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE char float64_le( float64 a, float64 b STATUS_PARAM)
|
||||
INLINE int float64_le( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE char float64_lt( float64 a, float64 b STATUS_PARAM)
|
||||
INLINE int float64_lt( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE char float64_eq_signaling( float64 a, float64 b STATUS_PARAM)
|
||||
INLINE int float64_eq_signaling( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE char float64_le_quiet( float64 a, float64 b STATUS_PARAM)
|
||||
INLINE int float64_le_quiet( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE char float64_lt_quiet( float64 a, float64 b STATUS_PARAM)
|
||||
INLINE int float64_lt_quiet( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
|
||||
}
|
||||
INLINE char float64_unordered( float64 a, float64 b STATUS_PARAM)
|
||||
INLINE int float64_unordered( float64 a, float64 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
char float64_compare( float64, float64 STATUS_PARAM );
|
||||
char float64_compare_quiet( float64, float64 STATUS_PARAM );
|
||||
char float64_is_signaling_nan( float64 );
|
||||
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)
|
||||
{
|
||||
@@ -286,39 +315,39 @@ INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
}
|
||||
floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
|
||||
floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
|
||||
INLINE char floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
INLINE int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
INLINE char floatx80_le( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
INLINE int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b;
|
||||
}
|
||||
INLINE char floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
INLINE int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
INLINE char floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
INLINE int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return a <= b && a >= b;
|
||||
}
|
||||
INLINE char floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
INLINE int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return islessequal(a, b);
|
||||
}
|
||||
INLINE char floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
INLINE int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return isless(a, b);
|
||||
|
||||
}
|
||||
INLINE char floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
INLINE int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
{
|
||||
return isunordered(a, b);
|
||||
|
||||
}
|
||||
char floatx80_compare( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_is_signaling_nan( floatx80 );
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ typedef struct {
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_is_nan( float32 a )
|
||||
int float32_is_nan( float32 a )
|
||||
{
|
||||
|
||||
return ( 0xFF000000 < (bits32) ( a<<1 ) );
|
||||
@@ -80,7 +80,7 @@ flag float32_is_nan( float32 a )
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_is_signaling_nan( float32 a )
|
||||
int float32_is_signaling_nan( float32 a )
|
||||
{
|
||||
|
||||
return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
|
||||
@@ -161,7 +161,7 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM)
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_is_nan( float64 a )
|
||||
int float64_is_nan( float64 a )
|
||||
{
|
||||
|
||||
return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
|
||||
@@ -173,7 +173,7 @@ flag float64_is_nan( float64 a )
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_is_signaling_nan( float64 a )
|
||||
int float64_is_signaling_nan( float64 a )
|
||||
{
|
||||
|
||||
return
|
||||
@@ -264,7 +264,7 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM)
|
||||
| NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_is_nan( floatx80 a )
|
||||
int floatx80_is_nan( floatx80 a )
|
||||
{
|
||||
|
||||
return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
|
||||
@@ -276,7 +276,7 @@ flag floatx80_is_nan( floatx80 a )
|
||||
| signaling NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_is_signaling_nan( floatx80 a )
|
||||
int floatx80_is_signaling_nan( floatx80 a )
|
||||
{
|
||||
bits64 aLow;
|
||||
|
||||
@@ -371,7 +371,7 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM)
|
||||
| otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_is_nan( float128 a )
|
||||
int float128_is_nan( float128 a )
|
||||
{
|
||||
|
||||
return
|
||||
@@ -385,7 +385,7 @@ flag float128_is_nan( float128 a )
|
||||
| signaling NaN; otherwise returns 0.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_is_signaling_nan( float128 a )
|
||||
int float128_is_signaling_nan( float128 a )
|
||||
{
|
||||
|
||||
return
|
||||
|
||||
+38
-27
@@ -2023,7 +2023,7 @@ float32 float32_sqrt( float32 a STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_eq( float32 a, float32 b STATUS_PARAM )
|
||||
int float32_eq( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
|
||||
if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
|
||||
@@ -2045,7 +2045,7 @@ flag float32_eq( float32 a, float32 b STATUS_PARAM )
|
||||
| Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_le( float32 a, float32 b STATUS_PARAM )
|
||||
int float32_le( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -2068,7 +2068,7 @@ flag float32_le( float32 a, float32 b STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_lt( float32 a, float32 b STATUS_PARAM )
|
||||
int float32_lt( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -2092,7 +2092,7 @@ flag float32_lt( float32 a, float32 b STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_eq_signaling( float32 a, float32 b STATUS_PARAM )
|
||||
int float32_eq_signaling( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
|
||||
if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
|
||||
@@ -2112,7 +2112,7 @@ flag float32_eq_signaling( float32 a, float32 b STATUS_PARAM )
|
||||
| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_le_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
int float32_le_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -2138,7 +2138,7 @@ flag float32_le_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
| Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float32_lt_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
int float32_lt_quiet( float32 a, float32 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -2483,6 +2483,17 @@ float64 float64_round_to_int( float64 a STATUS_PARAM )
|
||||
|
||||
}
|
||||
|
||||
float64 float64_trunc_to_int( float64 a STATUS_PARAM)
|
||||
{
|
||||
int oldmode;
|
||||
float64 res;
|
||||
oldmode = STATUS(float_rounding_mode);
|
||||
STATUS(float_rounding_mode) = float_round_to_zero;
|
||||
res = float64_round_to_int(a STATUS_VAR);
|
||||
STATUS(float_rounding_mode) = oldmode;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of adding the absolute values of the double-precision
|
||||
| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated
|
||||
@@ -2941,7 +2952,7 @@ float64 float64_sqrt( float64 a STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_eq( float64 a, float64 b STATUS_PARAM )
|
||||
int float64_eq( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
|
||||
if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
|
||||
@@ -2963,7 +2974,7 @@ flag float64_eq( float64 a, float64 b STATUS_PARAM )
|
||||
| Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_le( float64 a, float64 b STATUS_PARAM )
|
||||
int float64_le( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -2986,7 +2997,7 @@ flag float64_le( float64 a, float64 b STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_lt( float64 a, float64 b STATUS_PARAM )
|
||||
int float64_lt( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -3010,7 +3021,7 @@ flag float64_lt( float64 a, float64 b STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_eq_signaling( float64 a, float64 b STATUS_PARAM )
|
||||
int float64_eq_signaling( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
|
||||
if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
|
||||
@@ -3030,7 +3041,7 @@ flag float64_eq_signaling( float64 a, float64 b STATUS_PARAM )
|
||||
| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_le_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
int float64_le_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -3056,7 +3067,7 @@ flag float64_le_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
| Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float64_lt_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
int float64_lt_quiet( float64 a, float64 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -3879,7 +3890,7 @@ floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM )
|
||||
| Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
|
||||
if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
|
||||
@@ -3909,7 +3920,7 @@ flag floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
| Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_le( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -3942,7 +3953,7 @@ flag floatx80_le( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
| Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -3975,7 +3986,7 @@ flag floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
|
||||
if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
|
||||
@@ -4002,7 +4013,7 @@ flag floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -4038,7 +4049,7 @@ flag floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -4999,7 +5010,7 @@ float128 float128_sqrt( float128 a STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_eq( float128 a, float128 b STATUS_PARAM )
|
||||
int float128_eq( float128 a, float128 b STATUS_PARAM )
|
||||
{
|
||||
|
||||
if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
|
||||
@@ -5029,7 +5040,7 @@ flag float128_eq( float128 a, float128 b STATUS_PARAM )
|
||||
| Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_le( float128 a, float128 b STATUS_PARAM )
|
||||
int float128_le( float128 a, float128 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -5061,7 +5072,7 @@ flag float128_le( float128 a, float128 b STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_lt( float128 a, float128 b STATUS_PARAM )
|
||||
int float128_lt( float128 a, float128 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -5094,7 +5105,7 @@ flag float128_lt( float128 a, float128 b STATUS_PARAM )
|
||||
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_eq_signaling( float128 a, float128 b STATUS_PARAM )
|
||||
int float128_eq_signaling( float128 a, float128 b STATUS_PARAM )
|
||||
{
|
||||
|
||||
if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
|
||||
@@ -5121,7 +5132,7 @@ flag float128_eq_signaling( float128 a, float128 b STATUS_PARAM )
|
||||
| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_le_quiet( float128 a, float128 b STATUS_PARAM )
|
||||
int float128_le_quiet( float128 a, float128 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -5157,7 +5168,7 @@ flag float128_le_quiet( float128 a, float128 b STATUS_PARAM )
|
||||
| Standard for Binary Floating-Point Arithmetic.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
flag float128_lt_quiet( float128 a, float128 b STATUS_PARAM )
|
||||
int float128_lt_quiet( float128 a, float128 b STATUS_PARAM )
|
||||
{
|
||||
flag aSign, bSign;
|
||||
|
||||
@@ -5272,7 +5283,7 @@ unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM )
|
||||
}
|
||||
|
||||
#define COMPARE(s, nan_exp) \
|
||||
INLINE char float ## s ## _compare_internal( float ## s a, float ## s b, \
|
||||
INLINE int float ## s ## _compare_internal( float ## s a, float ## s b, \
|
||||
int is_quiet STATUS_PARAM ) \
|
||||
{ \
|
||||
flag aSign, bSign; \
|
||||
@@ -5306,12 +5317,12 @@ INLINE char float ## s ## _compare_internal( float ## s a, float ## s b, \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
char float ## s ## _compare( float ## s a, float ## s b STATUS_PARAM ) \
|
||||
int float ## s ## _compare( float ## s a, float ## s b STATUS_PARAM ) \
|
||||
{ \
|
||||
return float ## s ## _compare_internal(a, b, 0 STATUS_VAR); \
|
||||
} \
|
||||
\
|
||||
char float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \
|
||||
int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \
|
||||
{ \
|
||||
return float ## s ## _compare_internal(a, b, 1 STATUS_VAR); \
|
||||
}
|
||||
|
||||
+36
-34
@@ -43,7 +43,7 @@ these four paragraphs for those parts of this code that are retained.
|
||||
| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
|
||||
| to the same as `int'.
|
||||
*----------------------------------------------------------------------------*/
|
||||
typedef char flag;
|
||||
typedef uint8_t flag;
|
||||
typedef uint8_t uint8;
|
||||
typedef int8_t int8;
|
||||
typedef int uint16;
|
||||
@@ -177,7 +177,7 @@ void set_floatx80_rounding_precision(int val STATUS_PARAM);
|
||||
| Routine to raise any or all of the software IEC/IEEE floating-point
|
||||
| exception flags.
|
||||
*----------------------------------------------------------------------------*/
|
||||
void float_raise( signed char STATUS_PARAM);
|
||||
void float_raise( int8 flags STATUS_PARAM);
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE integer-to-floating-point conversion routines.
|
||||
@@ -228,15 +228,16 @@ 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 );
|
||||
char float32_eq( float32, float32 STATUS_PARAM );
|
||||
char float32_le( float32, float32 STATUS_PARAM );
|
||||
char float32_lt( float32, float32 STATUS_PARAM );
|
||||
char float32_eq_signaling( float32, float32 STATUS_PARAM );
|
||||
char float32_le_quiet( float32, float32 STATUS_PARAM );
|
||||
char float32_lt_quiet( float32, float32 STATUS_PARAM );
|
||||
char float32_compare( float32, float32 STATUS_PARAM );
|
||||
char float32_compare_quiet( float32, float32 STATUS_PARAM );
|
||||
char float32_is_signaling_nan( float32 );
|
||||
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)
|
||||
{
|
||||
@@ -269,21 +270,22 @@ float128 float64_to_float128( float64 STATUS_PARAM );
|
||||
| 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 );
|
||||
char float64_eq( float64, float64 STATUS_PARAM );
|
||||
char float64_le( float64, float64 STATUS_PARAM );
|
||||
char float64_lt( float64, float64 STATUS_PARAM );
|
||||
char float64_eq_signaling( float64, float64 STATUS_PARAM );
|
||||
char float64_le_quiet( float64, float64 STATUS_PARAM );
|
||||
char float64_lt_quiet( float64, float64 STATUS_PARAM );
|
||||
char float64_compare( float64, float64 STATUS_PARAM );
|
||||
char float64_compare_quiet( float64, float64 STATUS_PARAM );
|
||||
char float64_is_signaling_nan( float64 );
|
||||
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)
|
||||
{
|
||||
@@ -320,13 +322,13 @@ 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 );
|
||||
char floatx80_eq( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_le( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_lt( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
char floatx80_is_signaling_nan( floatx80 );
|
||||
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)
|
||||
{
|
||||
@@ -367,13 +369,13 @@ 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 );
|
||||
char float128_eq( float128, float128 STATUS_PARAM );
|
||||
char float128_le( float128, float128 STATUS_PARAM );
|
||||
char float128_lt( float128, float128 STATUS_PARAM );
|
||||
char float128_eq_signaling( float128, float128 STATUS_PARAM );
|
||||
char float128_le_quiet( float128, float128 STATUS_PARAM );
|
||||
char float128_lt_quiet( float128, float128 STATUS_PARAM );
|
||||
char float128_is_signaling_nan( float128 );
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* 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 "config.h"
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -24,16 +25,25 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "qemu.h"
|
||||
#else
|
||||
#include "vl.h"
|
||||
#endif
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include "qemu_socket.h"
|
||||
#ifdef _WIN32
|
||||
/* XXX: these constants may be independent of the host ones even for Unix */
|
||||
#ifndef SIGTRAP
|
||||
#define SIGTRAP 5
|
||||
#endif
|
||||
#ifndef SIGINT
|
||||
#define SIGINT 2
|
||||
#endif
|
||||
#else
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
//#define DEBUG_GDB
|
||||
|
||||
@@ -42,25 +52,30 @@ enum RSState {
|
||||
RS_GETLINE,
|
||||
RS_CHKSUM1,
|
||||
RS_CHKSUM2,
|
||||
RS_SYSCALL,
|
||||
};
|
||||
/* XXX: This is not thread safe. Do we care? */
|
||||
static int gdbserver_fd = -1;
|
||||
|
||||
typedef struct GDBState {
|
||||
CPUState *env; /* current CPU */
|
||||
enum RSState state; /* parsing state */
|
||||
int fd;
|
||||
char line_buf[4096];
|
||||
int line_buf_index;
|
||||
int line_csum;
|
||||
char last_packet[4100];
|
||||
int last_packet_len;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int fd;
|
||||
int running_state;
|
||||
#else
|
||||
CharDriverState *chr;
|
||||
#endif
|
||||
} GDBState;
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* XXX: This is not thread safe. Do we care? */
|
||||
static int gdbserver_fd = -1;
|
||||
|
||||
/* XXX: remove this hack. */
|
||||
static GDBState gdbserver_state;
|
||||
#endif
|
||||
|
||||
static int get_char(GDBState *s)
|
||||
{
|
||||
@@ -68,7 +83,7 @@ static int get_char(GDBState *s)
|
||||
int ret;
|
||||
|
||||
for(;;) {
|
||||
ret = read(s->fd, &ch, 1);
|
||||
ret = recv(s->fd, &ch, 1, 0);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
return -1;
|
||||
@@ -80,13 +95,36 @@ static int get_char(GDBState *s)
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* GDB stub state for use by semihosting syscalls. */
|
||||
static GDBState *gdb_syscall_state;
|
||||
static gdb_syscall_complete_cb gdb_current_syscall_cb;
|
||||
|
||||
enum {
|
||||
GDB_SYS_UNKNOWN,
|
||||
GDB_SYS_ENABLED,
|
||||
GDB_SYS_DISABLED,
|
||||
} gdb_syscall_mode;
|
||||
|
||||
/* If gdb is connected when the first semihosting syscall occurs then use
|
||||
remote gdb syscalls. Otherwise use native file IO. */
|
||||
int use_gdb_syscalls(void)
|
||||
{
|
||||
if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
|
||||
gdb_syscall_mode = (gdb_syscall_state ? GDB_SYS_ENABLED
|
||||
: GDB_SYS_DISABLED);
|
||||
}
|
||||
return gdb_syscall_mode == GDB_SYS_ENABLED;
|
||||
}
|
||||
|
||||
static void put_buffer(GDBState *s, const uint8_t *buf, int len)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int ret;
|
||||
|
||||
while (len > 0) {
|
||||
ret = write(s->fd, buf, len);
|
||||
ret = send(s->fd, buf, len, 0);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
return;
|
||||
@@ -95,6 +133,9 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len)
|
||||
len -= ret;
|
||||
}
|
||||
}
|
||||
#else
|
||||
qemu_chr_write(s->chr, buf, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int fromhex(int v)
|
||||
@@ -143,33 +184,39 @@ static void hextomem(uint8_t *mem, const char *buf, int len)
|
||||
/* return -1 if error, 0 if OK */
|
||||
static int put_packet(GDBState *s, char *buf)
|
||||
{
|
||||
char buf1[3];
|
||||
int len, csum, ch, i;
|
||||
int len, csum, i;
|
||||
char *p;
|
||||
|
||||
#ifdef DEBUG_GDB
|
||||
printf("reply='%s'\n", buf);
|
||||
#endif
|
||||
|
||||
for(;;) {
|
||||
buf1[0] = '$';
|
||||
put_buffer(s, buf1, 1);
|
||||
p = s->last_packet;
|
||||
*(p++) = '$';
|
||||
len = strlen(buf);
|
||||
put_buffer(s, buf, len);
|
||||
memcpy(p, buf, len);
|
||||
p += len;
|
||||
csum = 0;
|
||||
for(i = 0; i < len; i++) {
|
||||
csum += buf[i];
|
||||
}
|
||||
buf1[0] = '#';
|
||||
buf1[1] = tohex((csum >> 4) & 0xf);
|
||||
buf1[2] = tohex((csum) & 0xf);
|
||||
*(p++) = '#';
|
||||
*(p++) = tohex((csum >> 4) & 0xf);
|
||||
*(p++) = tohex((csum) & 0xf);
|
||||
|
||||
put_buffer(s, buf1, 3);
|
||||
s->last_packet_len = p - s->last_packet;
|
||||
put_buffer(s, s->last_packet, s->last_packet_len);
|
||||
|
||||
ch = get_char(s);
|
||||
if (ch < 0)
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
i = get_char(s);
|
||||
if (i < 0)
|
||||
return -1;
|
||||
if (ch == '+')
|
||||
if (i == '+')
|
||||
break;
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -297,18 +344,18 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
int i;
|
||||
|
||||
/* fill in g0..g7 */
|
||||
for(i = 0; i < 7; i++) {
|
||||
for(i = 0; i < 8; i++) {
|
||||
registers[i] = tswapl(env->gregs[i]);
|
||||
}
|
||||
/* fill in register window */
|
||||
for(i = 0; i < 24; i++) {
|
||||
registers[i + 8] = tswapl(env->regwptr[i]);
|
||||
}
|
||||
#ifndef TARGET_SPARC64
|
||||
/* fill in fprs */
|
||||
for (i = 0; i < 32; i++) {
|
||||
registers[i + 32] = tswapl(*((uint32_t *)&env->fpr[i]));
|
||||
}
|
||||
#ifndef TARGET_SPARC64
|
||||
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
|
||||
registers[64] = tswapl(env->y);
|
||||
{
|
||||
@@ -326,16 +373,21 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
registers[72] = 0;
|
||||
return 73 * sizeof(target_ulong);
|
||||
#else
|
||||
for (i = 0; i < 32; i += 2) {
|
||||
registers[i/2 + 64] = tswapl(*((uint64_t *)&env->fpr[i]));
|
||||
/* fill in fprs */
|
||||
for (i = 0; i < 64; i += 2) {
|
||||
uint64_t tmp;
|
||||
|
||||
tmp = (uint64_t)tswap32(*((uint32_t *)&env->fpr[i])) << 32;
|
||||
tmp |= tswap32(*((uint32_t *)&env->fpr[i + 1]));
|
||||
registers[i/2 + 32] = tmp;
|
||||
}
|
||||
registers[81] = tswapl(env->pc);
|
||||
registers[82] = tswapl(env->npc);
|
||||
registers[83] = tswapl(env->tstate[env->tl]);
|
||||
registers[84] = tswapl(env->fsr);
|
||||
registers[85] = tswapl(env->fprs);
|
||||
registers[86] = tswapl(env->y);
|
||||
return 87 * sizeof(target_ulong);
|
||||
registers[64] = tswapl(env->pc);
|
||||
registers[65] = tswapl(env->npc);
|
||||
registers[66] = tswapl(env->tstate[env->tl]);
|
||||
registers[67] = tswapl(env->fsr);
|
||||
registers[68] = tswapl(env->fprs);
|
||||
registers[69] = tswapl(env->y);
|
||||
return 70 * sizeof(target_ulong);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -352,11 +404,11 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
for(i = 0; i < 24; i++) {
|
||||
env->regwptr[i] = tswapl(registers[i + 8]);
|
||||
}
|
||||
#ifndef TARGET_SPARC64
|
||||
/* fill in fprs */
|
||||
for (i = 0; i < 32; i++) {
|
||||
*((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]);
|
||||
}
|
||||
#ifndef TARGET_SPARC64
|
||||
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
|
||||
env->y = tswapl(registers[64]);
|
||||
PUT_PSR(env, tswapl(registers[65]));
|
||||
@@ -366,18 +418,16 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
env->npc = tswapl(registers[69]);
|
||||
env->fsr = tswapl(registers[70]);
|
||||
#else
|
||||
for (i = 0; i < 32; i += 2) {
|
||||
uint64_t tmp;
|
||||
tmp = tswapl(registers[i/2 + 64]) << 32;
|
||||
tmp |= tswapl(registers[i/2 + 64 + 1]);
|
||||
*((uint64_t *)&env->fpr[i]) = tmp;
|
||||
for (i = 0; i < 64; i += 2) {
|
||||
*((uint32_t *)&env->fpr[i]) = tswap32(registers[i/2 + 32] >> 32);
|
||||
*((uint32_t *)&env->fpr[i + 1]) = tswap32(registers[i/2 + 32] & 0xffffffff);
|
||||
}
|
||||
env->pc = tswapl(registers[81]);
|
||||
env->npc = tswapl(registers[82]);
|
||||
env->tstate[env->tl] = tswapl(registers[83]);
|
||||
env->fsr = tswapl(registers[84]);
|
||||
env->fprs = tswapl(registers[85]);
|
||||
env->y = tswapl(registers[86]);
|
||||
env->pc = tswapl(registers[64]);
|
||||
env->npc = tswapl(registers[65]);
|
||||
env->tstate[env->tl] = tswapl(registers[66]);
|
||||
env->fsr = tswapl(registers[67]);
|
||||
env->fprs = tswapl(registers[68]);
|
||||
env->y = tswapl(registers[69]);
|
||||
#endif
|
||||
}
|
||||
#elif defined (TARGET_ARM)
|
||||
@@ -398,7 +448,7 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
memset (ptr, 0, 8 * 12 + 4);
|
||||
ptr += 8 * 12 + 4;
|
||||
/* CPSR (4 bytes). */
|
||||
*(uint32_t *)ptr = tswapl (env->cpsr);
|
||||
*(uint32_t *)ptr = tswapl (cpsr_read(env));
|
||||
ptr += 4;
|
||||
|
||||
return ptr - mem_buf;
|
||||
@@ -418,7 +468,237 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
}
|
||||
/* Ignore FPA regs and scr. */
|
||||
ptr += 8 * 12 + 4;
|
||||
env->cpsr = tswapl(*(uint32_t *)ptr);
|
||||
cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff);
|
||||
}
|
||||
#elif defined (TARGET_M68K)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ptr;
|
||||
CPU_DoubleU u;
|
||||
|
||||
ptr = mem_buf;
|
||||
/* D0-D7 */
|
||||
for (i = 0; i < 8; i++) {
|
||||
*(uint32_t *)ptr = tswapl(env->dregs[i]);
|
||||
ptr += 4;
|
||||
}
|
||||
/* A0-A7 */
|
||||
for (i = 0; i < 8; i++) {
|
||||
*(uint32_t *)ptr = tswapl(env->aregs[i]);
|
||||
ptr += 4;
|
||||
}
|
||||
*(uint32_t *)ptr = tswapl(env->sr);
|
||||
ptr += 4;
|
||||
*(uint32_t *)ptr = tswapl(env->pc);
|
||||
ptr += 4;
|
||||
/* F0-F7. The 68881/68040 have 12-bit extended precision registers.
|
||||
ColdFire has 8-bit double precision registers. */
|
||||
for (i = 0; i < 8; i++) {
|
||||
u.d = env->fregs[i];
|
||||
*(uint32_t *)ptr = tswap32(u.l.upper);
|
||||
*(uint32_t *)ptr = tswap32(u.l.lower);
|
||||
}
|
||||
/* FP control regs (not implemented). */
|
||||
memset (ptr, 0, 3 * 4);
|
||||
ptr += 3 * 4;
|
||||
|
||||
return ptr - mem_buf;
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ptr;
|
||||
CPU_DoubleU u;
|
||||
|
||||
ptr = mem_buf;
|
||||
/* D0-D7 */
|
||||
for (i = 0; i < 8; i++) {
|
||||
env->dregs[i] = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
}
|
||||
/* A0-A7 */
|
||||
for (i = 0; i < 8; i++) {
|
||||
env->aregs[i] = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
}
|
||||
env->sr = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
env->pc = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
/* F0-F7. The 68881/68040 have 12-bit extended precision registers.
|
||||
ColdFire has 8-bit double precision registers. */
|
||||
for (i = 0; i < 8; i++) {
|
||||
u.l.upper = tswap32(*(uint32_t *)ptr);
|
||||
u.l.lower = tswap32(*(uint32_t *)ptr);
|
||||
env->fregs[i] = u.d;
|
||||
}
|
||||
/* FP control regs (not implemented). */
|
||||
ptr += 3 * 4;
|
||||
}
|
||||
#elif defined (TARGET_MIPS)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ptr;
|
||||
|
||||
ptr = mem_buf;
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
*(uint32_t *)ptr = tswapl(env->gpr[i]);
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->CP0_Status);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->LO);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->HI);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->CP0_BadVAddr);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->CP0_Cause);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->PC);
|
||||
ptr += 4;
|
||||
|
||||
#ifdef MIPS_USES_FPU
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
*(uint32_t *)ptr = tswapl(FPR_W (env, i));
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->fcr31);
|
||||
ptr += 4;
|
||||
|
||||
*(uint32_t *)ptr = tswapl(env->fcr0);
|
||||
ptr += 4;
|
||||
#endif
|
||||
|
||||
/* 32 FP registers, fsr, fir, fp. Not yet implemented. */
|
||||
/* what's 'fp' mean here? */
|
||||
|
||||
return ptr - mem_buf;
|
||||
}
|
||||
|
||||
/* convert MIPS rounding mode in FCR31 to IEEE library */
|
||||
static unsigned int ieee_rm[] =
|
||||
{
|
||||
float_round_nearest_even,
|
||||
float_round_to_zero,
|
||||
float_round_up,
|
||||
float_round_down
|
||||
};
|
||||
#define RESTORE_ROUNDING_MODE \
|
||||
set_float_rounding_mode(ieee_rm[env->fcr31 & 3], &env->fp_status)
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ptr;
|
||||
|
||||
ptr = mem_buf;
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
env->gpr[i] = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
env->CP0_Status = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->LO = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->HI = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->CP0_BadVAddr = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->CP0_Cause = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
env->PC = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
#ifdef MIPS_USES_FPU
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
FPR_W (env, i) = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
env->fcr31 = tswapl(*(uint32_t *)ptr) & 0x0183FFFF;
|
||||
ptr += 4;
|
||||
|
||||
env->fcr0 = tswapl(*(uint32_t *)ptr);
|
||||
ptr += 4;
|
||||
|
||||
/* set rounding mode */
|
||||
RESTORE_ROUNDING_MODE;
|
||||
|
||||
#ifndef CONFIG_SOFTFLOAT
|
||||
/* no floating point exception for native float */
|
||||
SET_FP_ENABLE(env->fcr31, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#elif defined (TARGET_SH4)
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
{
|
||||
uint32_t *ptr = (uint32_t *)mem_buf;
|
||||
int i;
|
||||
|
||||
#define SAVE(x) *ptr++=tswapl(x)
|
||||
if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
|
||||
for (i = 0; i < 8; i++) SAVE(env->gregs[i + 16]);
|
||||
} else {
|
||||
for (i = 0; i < 8; i++) SAVE(env->gregs[i]);
|
||||
}
|
||||
for (i = 8; i < 16; i++) SAVE(env->gregs[i]);
|
||||
SAVE (env->pc);
|
||||
SAVE (env->pr);
|
||||
SAVE (env->gbr);
|
||||
SAVE (env->vbr);
|
||||
SAVE (env->mach);
|
||||
SAVE (env->macl);
|
||||
SAVE (env->sr);
|
||||
SAVE (0); /* TICKS */
|
||||
SAVE (0); /* STALLS */
|
||||
SAVE (0); /* CYCLES */
|
||||
SAVE (0); /* INSTS */
|
||||
SAVE (0); /* PLR */
|
||||
|
||||
return ((uint8_t *)ptr - mem_buf);
|
||||
}
|
||||
|
||||
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
|
||||
{
|
||||
uint32_t *ptr = (uint32_t *)mem_buf;
|
||||
int i;
|
||||
|
||||
#define LOAD(x) (x)=*ptr++;
|
||||
if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
|
||||
for (i = 0; i < 8; i++) LOAD(env->gregs[i + 16]);
|
||||
} else {
|
||||
for (i = 0; i < 8; i++) LOAD(env->gregs[i]);
|
||||
}
|
||||
for (i = 8; i < 16; i++) LOAD(env->gregs[i]);
|
||||
LOAD (env->pc);
|
||||
LOAD (env->pr);
|
||||
LOAD (env->gbr);
|
||||
LOAD (env->vbr);
|
||||
LOAD (env->mach);
|
||||
LOAD (env->macl);
|
||||
LOAD (env->sr);
|
||||
}
|
||||
#else
|
||||
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
|
||||
@@ -439,7 +719,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
char buf[4096];
|
||||
uint8_t mem_buf[2000];
|
||||
uint32_t *registers;
|
||||
uint32_t addr, len;
|
||||
target_ulong addr, len;
|
||||
|
||||
#ifdef DEBUG_GDB
|
||||
printf("command='%s'\n", line_buf);
|
||||
@@ -454,7 +734,7 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
break;
|
||||
case 'c':
|
||||
if (*p != '\0') {
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
addr = strtoull(p, (char **)&p, 16);
|
||||
#if defined(TARGET_I386)
|
||||
env->eip = addr;
|
||||
#elif defined (TARGET_PPC)
|
||||
@@ -462,6 +742,10 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
#elif defined (TARGET_SPARC)
|
||||
env->pc = addr;
|
||||
env->npc = addr + 4;
|
||||
#elif defined (TARGET_ARM)
|
||||
env->regs[15] = addr;
|
||||
#elif defined (TARGET_SH4)
|
||||
env->pc = addr;
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
@@ -480,6 +764,10 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
#elif defined (TARGET_SPARC)
|
||||
env->pc = addr;
|
||||
env->npc = addr + 4;
|
||||
#elif defined (TARGET_ARM)
|
||||
env->regs[15] = addr;
|
||||
#elif defined (TARGET_SH4)
|
||||
env->pc = addr;
|
||||
#endif
|
||||
}
|
||||
cpu_single_step(env, 1);
|
||||
@@ -489,6 +777,34 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
vm_start();
|
||||
#endif
|
||||
return RS_IDLE;
|
||||
case 'F':
|
||||
{
|
||||
target_ulong ret;
|
||||
target_ulong err;
|
||||
|
||||
ret = strtoull(p, (char **)&p, 16);
|
||||
if (*p == ',') {
|
||||
p++;
|
||||
err = strtoull(p, (char **)&p, 16);
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
if (*p == ',')
|
||||
p++;
|
||||
type = *p;
|
||||
if (gdb_current_syscall_cb)
|
||||
gdb_current_syscall_cb(s->env, ret, err);
|
||||
if (type == 'C') {
|
||||
put_packet(s, "T02");
|
||||
} else {
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
s->running_state = 1;
|
||||
#else
|
||||
vm_start();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
reg_size = cpu_gdb_read_registers(env, mem_buf);
|
||||
memtohex(buf, mem_buf, reg_size);
|
||||
@@ -502,20 +818,22 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
put_packet(s, "OK");
|
||||
break;
|
||||
case 'm':
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
addr = strtoull(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtoul(p, NULL, 16);
|
||||
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0)
|
||||
memset(mem_buf, 0, len);
|
||||
memtohex(buf, mem_buf, len);
|
||||
put_packet(s, buf);
|
||||
len = strtoull(p, NULL, 16);
|
||||
if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0) {
|
||||
put_packet (s, "E14");
|
||||
} else {
|
||||
memtohex(buf, mem_buf, len);
|
||||
put_packet(s, buf);
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
addr = strtoull(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtoul(p, (char **)&p, 16);
|
||||
len = strtoull(p, (char **)&p, 16);
|
||||
if (*p == ':')
|
||||
p++;
|
||||
hextomem(mem_buf, p, len);
|
||||
@@ -528,10 +846,10 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
type = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
addr = strtoull(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtoul(p, (char **)&p, 16);
|
||||
len = strtoull(p, (char **)&p, 16);
|
||||
if (type == 0 || type == 1) {
|
||||
if (cpu_breakpoint_insert(env, addr) < 0)
|
||||
goto breakpoint_error;
|
||||
@@ -545,10 +863,10 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
type = strtoul(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
addr = strtoul(p, (char **)&p, 16);
|
||||
addr = strtoull(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtoul(p, (char **)&p, 16);
|
||||
len = strtoull(p, (char **)&p, 16);
|
||||
if (type == 0 || type == 1) {
|
||||
cpu_breakpoint_remove(env, addr);
|
||||
put_packet(s, "OK");
|
||||
@@ -556,6 +874,18 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
||||
goto breakpoint_error;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_LINUX_USER
|
||||
case 'q':
|
||||
if (strncmp(p, "Offsets", 7) == 0) {
|
||||
TaskState *ts = env->opaque;
|
||||
|
||||
sprintf(buf, "Text=%x;Data=%x;Bss=%x", ts->info->code_offset,
|
||||
ts->info->data_offset, ts->info->data_offset);
|
||||
put_packet(s, buf);
|
||||
break;
|
||||
}
|
||||
/* Fall through. */
|
||||
#endif
|
||||
default:
|
||||
// unknown_command:
|
||||
/* put empty packet */
|
||||
@@ -575,26 +905,106 @@ static void gdb_vm_stopped(void *opaque, int reason)
|
||||
char buf[256];
|
||||
int ret;
|
||||
|
||||
if (s->state == RS_SYSCALL)
|
||||
return;
|
||||
|
||||
/* disable single step if it was enable */
|
||||
cpu_single_step(cpu_single_env, 0);
|
||||
cpu_single_step(s->env, 0);
|
||||
|
||||
if (reason == EXCP_DEBUG) {
|
||||
tb_flush(cpu_single_env);
|
||||
tb_flush(s->env);
|
||||
ret = SIGTRAP;
|
||||
}
|
||||
else
|
||||
} else if (reason == EXCP_INTERRUPT) {
|
||||
ret = SIGINT;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "S%02x", ret);
|
||||
put_packet(s, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gdb_read_byte(GDBState *s, CPUState *env, int ch)
|
||||
/* Send a gdb syscall request.
|
||||
This accepts limited printf-style format specifiers, specifically:
|
||||
%x - target_ulong argument printed in hex.
|
||||
%s - string pointer (target_ulong) and length (int) pair. */
|
||||
void gdb_do_syscall(gdb_syscall_complete_cb cb, char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
char buf[256];
|
||||
char *p;
|
||||
target_ulong addr;
|
||||
GDBState *s;
|
||||
|
||||
s = gdb_syscall_state;
|
||||
if (!s)
|
||||
return;
|
||||
gdb_current_syscall_cb = cb;
|
||||
s->state = RS_SYSCALL;
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
vm_stop(EXCP_DEBUG);
|
||||
#endif
|
||||
s->state = RS_IDLE;
|
||||
va_start(va, fmt);
|
||||
p = buf;
|
||||
*(p++) = 'F';
|
||||
while (*fmt) {
|
||||
if (*fmt == '%') {
|
||||
fmt++;
|
||||
switch (*fmt++) {
|
||||
case 'x':
|
||||
addr = va_arg(va, target_ulong);
|
||||
p += sprintf(p, TARGET_FMT_lx, addr);
|
||||
break;
|
||||
case 's':
|
||||
addr = va_arg(va, target_ulong);
|
||||
p += sprintf(p, TARGET_FMT_lx "/%x", addr, va_arg(va, int));
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "gdbstub: Bad syscall format string '%s'\n",
|
||||
fmt - 1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
*(p++) = *(fmt++);
|
||||
}
|
||||
}
|
||||
va_end(va);
|
||||
put_packet(s, buf);
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
gdb_handlesig(s->env, 0);
|
||||
#else
|
||||
cpu_interrupt(s->env, CPU_INTERRUPT_EXIT);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gdb_read_byte(GDBState *s, int ch)
|
||||
{
|
||||
CPUState *env = s->env;
|
||||
int i, csum;
|
||||
char reply[1];
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (s->last_packet_len) {
|
||||
/* Waiting for a response to the last packet. If we see the start
|
||||
of a new command then abandon the previous response. */
|
||||
if (ch == '-') {
|
||||
#ifdef DEBUG_GDB
|
||||
printf("Got NACK, retransmitting\n");
|
||||
#endif
|
||||
put_buffer(s, s->last_packet, s->last_packet_len);
|
||||
}
|
||||
#ifdef DEBUG_GDB
|
||||
else if (ch == '+')
|
||||
printf("Got ACK\n");
|
||||
else
|
||||
printf("Got '%c' when expecting ACK/NACK\n", ch);
|
||||
#endif
|
||||
if (ch == '+' || ch == '$')
|
||||
s->last_packet_len = 0;
|
||||
if (ch != '$')
|
||||
return;
|
||||
}
|
||||
if (vm_running) {
|
||||
/* when the CPU is running, we cannot do anything except stop
|
||||
it when receiving a char */
|
||||
@@ -639,6 +1049,8 @@ static void gdb_read_byte(GDBState *s, CPUState *env, int ch)
|
||||
s->state = gdb_handle_packet(s, env, s->line_buf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -676,7 +1088,7 @@ gdb_handlesig (CPUState *env, int sig)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
gdb_read_byte (s, env, buf[i]);
|
||||
gdb_read_byte (s, buf[i]);
|
||||
}
|
||||
else if (n == 0 || errno != EAGAIN)
|
||||
{
|
||||
@@ -703,31 +1115,8 @@ void gdb_exit(CPUState *env, int code)
|
||||
put_packet(s, buf);
|
||||
}
|
||||
|
||||
#else
|
||||
static int gdb_can_read(void *opaque)
|
||||
{
|
||||
return 256;
|
||||
}
|
||||
|
||||
static void gdb_read(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
GDBState *s = opaque;
|
||||
int i;
|
||||
if (size == 0) {
|
||||
/* end of connection */
|
||||
qemu_del_vm_stop_handler(gdb_vm_stopped, s);
|
||||
qemu_del_fd_read_handler(s->fd);
|
||||
qemu_free(s);
|
||||
vm_start();
|
||||
} else {
|
||||
for(i = 0; i < size; i++)
|
||||
gdb_read_byte(s, cpu_single_env, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void gdb_accept(void *opaque, const uint8_t *buf, int size)
|
||||
static void gdb_accept(void *opaque)
|
||||
{
|
||||
GDBState *s;
|
||||
struct sockaddr_in sockaddr;
|
||||
@@ -747,31 +1136,16 @@ static void gdb_accept(void *opaque, const uint8_t *buf, int size)
|
||||
|
||||
/* set short latency */
|
||||
val = 1;
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
s = &gdbserver_state;
|
||||
memset (s, 0, sizeof (GDBState));
|
||||
#else
|
||||
s = qemu_mallocz(sizeof(GDBState));
|
||||
if (!s) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
s->env = first_cpu; /* XXX: allow to change CPU */
|
||||
s->fd = fd;
|
||||
|
||||
gdb_syscall_state = s;
|
||||
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* stop the VM */
|
||||
vm_stop(EXCP_INTERRUPT);
|
||||
|
||||
/* start handling I/O */
|
||||
qemu_add_fd_read_handler(s->fd, gdb_can_read, gdb_read, s);
|
||||
/* when the VM is stopped, the following callback is called */
|
||||
qemu_add_vm_stop_handler(gdb_vm_stopped, s);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int gdbserver_open(int port)
|
||||
@@ -787,7 +1161,7 @@ static int gdbserver_open(int port)
|
||||
|
||||
/* allow fast reuse */
|
||||
val = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
|
||||
|
||||
sockaddr.sin_family = AF_INET;
|
||||
sockaddr.sin_port = htons(port);
|
||||
@@ -802,9 +1176,6 @@ static int gdbserver_open(int port)
|
||||
perror("listen");
|
||||
return -1;
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
return fd;
|
||||
}
|
||||
|
||||
@@ -814,10 +1185,67 @@ int gdbserver_start(int port)
|
||||
if (gdbserver_fd < 0)
|
||||
return -1;
|
||||
/* accept connections */
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
gdb_accept (NULL, NULL, 0);
|
||||
#else
|
||||
qemu_add_fd_read_handler(gdbserver_fd, NULL, gdb_accept, NULL);
|
||||
#endif
|
||||
gdb_accept (NULL);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int gdb_chr_can_recieve(void *opaque)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void gdb_chr_recieve(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
GDBState *s = opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
gdb_read_byte(s, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void gdb_chr_event(void *opaque, int event)
|
||||
{
|
||||
switch (event) {
|
||||
case CHR_EVENT_RESET:
|
||||
vm_stop(EXCP_INTERRUPT);
|
||||
gdb_syscall_state = opaque;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int gdbserver_start(CharDriverState *chr)
|
||||
{
|
||||
GDBState *s;
|
||||
|
||||
if (!chr)
|
||||
return -1;
|
||||
|
||||
s = qemu_mallocz(sizeof(GDBState));
|
||||
if (!s) {
|
||||
return -1;
|
||||
}
|
||||
s->env = first_cpu; /* XXX: allow to change CPU */
|
||||
s->chr = chr;
|
||||
qemu_chr_add_handlers(chr, gdb_chr_can_recieve, gdb_chr_recieve,
|
||||
gdb_chr_event, s);
|
||||
qemu_add_vm_stop_handler(gdb_vm_stopped, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gdbserver_start_port(int port)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
char gdbstub_port_name[128];
|
||||
|
||||
snprintf(gdbstub_port_name, sizeof(gdbstub_port_name),
|
||||
"tcp::%d,nowait,nodelay,server", port);
|
||||
chr = qemu_chr_open(gdbstub_port_name);
|
||||
if (!chr)
|
||||
return -EIO;
|
||||
return gdbserver_start(chr);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -3,10 +3,18 @@
|
||||
|
||||
#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);
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
@@ -406,5 +406,5 @@ void adb_mouse_init(ADBBusState *bus)
|
||||
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);
|
||||
qemu_add_mouse_event_handler(adb_mouse_event, d, 0, "QEMU ADB Mouse");
|
||||
}
|
||||
+169
-141
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* QEMU Adlib emulation
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* 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
|
||||
@@ -21,8 +21,11 @@
|
||||
* 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__)
|
||||
@@ -30,23 +33,15 @@
|
||||
#define ldebug(...)
|
||||
#endif
|
||||
|
||||
#ifdef USE_YMF262
|
||||
#define HAS_YMF262 1
|
||||
#ifdef HAS_YMF262
|
||||
#include "ymf262.h"
|
||||
void YMF262UpdateOneQEMU(int which, INT16 *dst, int length);
|
||||
void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
|
||||
#define SHIFT 2
|
||||
#else
|
||||
#include "fmopl.h"
|
||||
#define SHIFT 1
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define small_delay() Sleep (1)
|
||||
#else
|
||||
#define small_delay() usleep (1)
|
||||
#endif
|
||||
|
||||
#define IO_READ_PROTO(name) \
|
||||
uint32_t name (void *opaque, uint32_t nport)
|
||||
#define IO_WRITE_PROTO(name) \
|
||||
@@ -58,23 +53,58 @@ static struct {
|
||||
} conf = {0x220, 44100};
|
||||
|
||||
typedef struct {
|
||||
QEMUSoundCard card;
|
||||
int ticking[2];
|
||||
int enabled;
|
||||
int active;
|
||||
int cparam;
|
||||
int64_t ticks;
|
||||
int bufpos;
|
||||
#ifdef DEBUG
|
||||
int64_t exp[2];
|
||||
#endif
|
||||
int16_t *mixbuf;
|
||||
double interval;
|
||||
QEMUTimer *ts, *opl_ts;
|
||||
SWVoice *voice;
|
||||
int left, pos, samples, bytes_per_second, old_free;
|
||||
int refcount;
|
||||
#ifndef USE_YMF262
|
||||
uint64_t dexp[2];
|
||||
SWVoiceOut *voice;
|
||||
int left, pos, samples;
|
||||
QEMUAudioTimeStamp ats;
|
||||
#ifndef HAS_YMF262
|
||||
FM_OPL *opl;
|
||||
#endif
|
||||
} AdlibState;
|
||||
|
||||
static AdlibState adlib;
|
||||
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)
|
||||
{
|
||||
@@ -82,11 +112,12 @@ static IO_WRITE_PROTO(adlib_write)
|
||||
int a = nport & 3;
|
||||
int status;
|
||||
|
||||
s->ticks = qemu_get_clock (vm_clock);
|
||||
s->active = 1;
|
||||
AUD_enable (s->voice, 1);
|
||||
AUD_set_active_out (s->voice, 1);
|
||||
|
||||
#ifdef USE_YMF262
|
||||
adlib_kill_timers (s);
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
status = YMF262Write (0, a, val);
|
||||
#else
|
||||
status = OPLWrite (s->opl, a, val);
|
||||
@@ -99,8 +130,9 @@ static IO_READ_PROTO(adlib_read)
|
||||
uint8_t data;
|
||||
int a = nport & 3;
|
||||
|
||||
#ifdef USE_YMF262
|
||||
(void) s;
|
||||
adlib_kill_timers (s);
|
||||
|
||||
#ifdef HAS_YMF262
|
||||
data = YMF262Read (0, a);
|
||||
#else
|
||||
data = OPLRead (s->opl, a);
|
||||
@@ -108,119 +140,116 @@ static IO_READ_PROTO(adlib_read)
|
||||
return data;
|
||||
}
|
||||
|
||||
static void OPL_timer (void *opaque)
|
||||
static void timer_handler (int c, double interval_Sec)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
#ifdef USE_YMF262
|
||||
YMF262TimerOver (s->cparam >> 1, s->cparam & 1);
|
||||
#else
|
||||
OPLTimerOver (s->opl, s->cparam);
|
||||
AdlibState *s = &glob_adlib;
|
||||
unsigned n = c & 1;
|
||||
#ifdef DEBUG
|
||||
double interval;
|
||||
int64_t exp;
|
||||
#endif
|
||||
qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
|
||||
}
|
||||
|
||||
static void YMF262TimerHandler (int c, double interval_Sec)
|
||||
{
|
||||
AdlibState *s = &adlib;
|
||||
if (interval_Sec == 0.0) {
|
||||
qemu_del_timer (s->opl_ts);
|
||||
s->ticking[n] = 0;
|
||||
return;
|
||||
}
|
||||
s->cparam = c;
|
||||
s->interval = ticks_per_sec * interval_Sec;
|
||||
qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
|
||||
small_delay ();
|
||||
|
||||
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 ss = samples;
|
||||
int pos = s->pos;
|
||||
|
||||
while (samples) {
|
||||
int nbytes = samples << SHIFT;
|
||||
int wbytes = AUD_write (s->voice,
|
||||
s->mixbuf + (s->pos << (SHIFT - 1)),
|
||||
nbytes);
|
||||
int wsampl = wbytes >> SHIFT;
|
||||
samples -= wsampl;
|
||||
s->pos = (s->pos + wsampl) % s->samples;
|
||||
net += wsampl;
|
||||
if (!wbytes)
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (net > ss) {
|
||||
dolog ("WARNING: net > ss\n");
|
||||
}
|
||||
|
||||
return net;
|
||||
}
|
||||
|
||||
static void timer (void *opaque)
|
||||
static void adlib_callback (void *opaque, int free)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int elapsed, samples, net = 0;
|
||||
int samples, net = 0, to_play, written;
|
||||
|
||||
if (s->refcount)
|
||||
dolog ("refcount=%d\n", s->refcount);
|
||||
|
||||
s->refcount += 1;
|
||||
if (!(s->active && s->enabled))
|
||||
goto reset;
|
||||
|
||||
AUD_run ();
|
||||
|
||||
while (s->left) {
|
||||
int written = write_audio (s, s->left);
|
||||
net += written;
|
||||
if (!written)
|
||||
goto reset2;
|
||||
s->left -= written;
|
||||
samples = free >> SHIFT;
|
||||
if (!(s->active && s->enabled) || !samples) {
|
||||
return;
|
||||
}
|
||||
s->pos = 0;
|
||||
|
||||
elapsed = AUD_calc_elapsed (s->voice);
|
||||
if (!elapsed)
|
||||
goto reset2;
|
||||
to_play = audio_MIN (s->left, samples);
|
||||
while (to_play) {
|
||||
written = write_audio (s, to_play);
|
||||
|
||||
/* elapsed = AUD_get_free (s->voice); */
|
||||
samples = elapsed >> SHIFT;
|
||||
if (!samples)
|
||||
goto reset2;
|
||||
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 (s->left)
|
||||
dolog ("left=%d samples=%d elapsed=%d free=%d\n",
|
||||
s->left, samples, elapsed, AUD_get_free (s->voice));
|
||||
if (!samples) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!samples)
|
||||
goto reset2;
|
||||
|
||||
#ifdef USE_YMF262
|
||||
#ifdef HAS_YMF262
|
||||
YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
|
||||
#else
|
||||
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
|
||||
#endif
|
||||
|
||||
while (samples) {
|
||||
int written = write_audio (s, samples);
|
||||
net += written;
|
||||
if (!written)
|
||||
break;
|
||||
samples -= written;
|
||||
}
|
||||
if (!samples)
|
||||
s->pos = 0;
|
||||
s->left = samples;
|
||||
written = write_audio (s, samples);
|
||||
|
||||
reset2:
|
||||
AUD_adjust (s->voice, net << SHIFT);
|
||||
reset:
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024);
|
||||
s->refcount -= 1;
|
||||
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 USE_YMF262
|
||||
#ifdef HAS_YMF262
|
||||
YMF262Shutdown ();
|
||||
#else
|
||||
if (s->opl) {
|
||||
@@ -229,77 +258,76 @@ static void Adlib_fini (AdlibState *s)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s->opl_ts)
|
||||
qemu_free_timer (s->opl_ts);
|
||||
|
||||
if (s->ts)
|
||||
qemu_free_timer (s->ts);
|
||||
|
||||
#define maybe_free(p) if (p) qemu_free (p)
|
||||
maybe_free (s->mixbuf);
|
||||
#undef maybe_free
|
||||
if (s->mixbuf) {
|
||||
qemu_free (s->mixbuf);
|
||||
}
|
||||
|
||||
s->active = 0;
|
||||
s->enabled = 0;
|
||||
AUD_remove_card (&s->card);
|
||||
}
|
||||
|
||||
void Adlib_init (void)
|
||||
int Adlib_init (AudioState *audio)
|
||||
{
|
||||
AdlibState *s = &adlib;
|
||||
AdlibState *s = &glob_adlib;
|
||||
audsettings_t as;
|
||||
|
||||
memset (s, 0, sizeof (*s));
|
||||
if (!audio) {
|
||||
dolog ("No audio state\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef USE_YMF262
|
||||
#ifdef HAS_YMF262
|
||||
if (YMF262Init (1, 14318180, conf.freq)) {
|
||||
dolog ("YMF262Init %d failed\n", conf.freq);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
YMF262SetTimerHandler (0, YMF262TimerHandler, 0);
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0);
|
||||
OPLSetTimerHandler (s->opl, timer_handler, 0);
|
||||
s->enabled = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s);
|
||||
if (!s->opl_ts) {
|
||||
dolog ("Can not get timer for adlib emulation\n");
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
as.freq = conf.freq;
|
||||
as.nchannels = SHIFT;
|
||||
as.fmt = AUD_FMT_S16;
|
||||
as.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
|
||||
s->ts = qemu_new_timer (vm_clock, timer, s);
|
||||
if (!s->opl_ts) {
|
||||
dolog ("Can not get timer for adlib emulation\n");
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
AUD_register_card (audio, "adlib", &s->card);
|
||||
|
||||
s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16);
|
||||
s->voice = AUD_open_out (
|
||||
&s->card,
|
||||
s->voice,
|
||||
"adlib",
|
||||
s,
|
||||
adlib_callback,
|
||||
&as
|
||||
);
|
||||
if (!s->voice) {
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->bytes_per_second = conf.freq << SHIFT;
|
||||
s->samples = AUD_get_buffer_size (s->voice) >> SHIFT;
|
||||
s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
|
||||
s->mixbuf = qemu_mallocz (s->samples << SHIFT);
|
||||
|
||||
if (!s->mixbuf) {
|
||||
dolog ("not enough memory for adlib mixing buffer (%d)\n",
|
||||
s->samples << SHIFT);
|
||||
dolog ("Could not allocate mixing buffer, %d samples (each %d bytes)\n",
|
||||
s->samples, 1 << SHIFT);
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
register_ioport_read (0x388, 4, 1, adlib_read, s);
|
||||
register_ioport_write (0x388, 4, 1, adlib_write, s);
|
||||
|
||||
@@ -309,5 +337,5 @@ void Adlib_init (void)
|
||||
register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
|
||||
register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
|
||||
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,9 @@
|
||||
|
||||
#define APIC_SV_ENABLE (1 << 8)
|
||||
|
||||
#define MAX_APICS 255
|
||||
#define MAX_APIC_WORDS 8
|
||||
|
||||
typedef struct APICState {
|
||||
CPUState *cpu_env;
|
||||
uint32_t apicbase;
|
||||
@@ -81,8 +84,6 @@ typedef struct APICState {
|
||||
uint32_t initial_count;
|
||||
int64_t initial_count_load_time, next_time;
|
||||
QEMUTimer *timer;
|
||||
|
||||
struct APICState *next_apic;
|
||||
} APICState;
|
||||
|
||||
struct IOAPICState {
|
||||
@@ -94,95 +95,19 @@ struct IOAPICState {
|
||||
};
|
||||
|
||||
static int apic_io_memory;
|
||||
static APICState *first_local_apic = NULL;
|
||||
static APICState *local_apics[MAX_APICS + 1];
|
||||
static int last_apic_id = 0;
|
||||
|
||||
static void apic_init_ipi(APICState *s);
|
||||
static void apic_set_irq(APICState *s, int vector_num, int trigger_mode);
|
||||
static void apic_update_irq(APICState *s);
|
||||
|
||||
static void apic_bus_deliver(uint32_t deliver_bitmask, uint8_t delivery_mode,
|
||||
uint8_t vector_num, uint8_t polarity,
|
||||
uint8_t trigger_mode)
|
||||
{
|
||||
APICState *apic_iter;
|
||||
|
||||
switch (delivery_mode) {
|
||||
case APIC_DM_LOWPRI:
|
||||
case APIC_DM_FIXED:
|
||||
/* XXX: arbitration */
|
||||
break;
|
||||
|
||||
case APIC_DM_SMI:
|
||||
case APIC_DM_NMI:
|
||||
break;
|
||||
|
||||
case APIC_DM_INIT:
|
||||
/* normal INIT IPI sent to processors */
|
||||
for (apic_iter = first_local_apic; apic_iter != NULL;
|
||||
apic_iter = apic_iter->next_apic) {
|
||||
apic_init_ipi(apic_iter);
|
||||
}
|
||||
return;
|
||||
|
||||
case APIC_DM_EXTINT:
|
||||
/* handled in I/O APIC code */
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
for (apic_iter = first_local_apic; apic_iter != NULL;
|
||||
apic_iter = apic_iter->next_apic) {
|
||||
if (deliver_bitmask & (1 << apic_iter->id))
|
||||
apic_set_irq(apic_iter, vector_num, trigger_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_set_apic_base(CPUState *env, uint64_t val)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
#ifdef DEBUG_APIC
|
||||
printf("cpu_set_apic_base: %016llx\n", val);
|
||||
#endif
|
||||
s->apicbase = (val & 0xfffff000) |
|
||||
(s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
|
||||
/* if disabled, cannot be enabled again */
|
||||
if (!(val & MSR_IA32_APICBASE_ENABLE)) {
|
||||
s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
|
||||
env->cpuid_features &= ~CPUID_APIC;
|
||||
s->spurious_vec &= ~APIC_SV_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t cpu_get_apic_base(CPUState *env)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
#ifdef DEBUG_APIC
|
||||
printf("cpu_get_apic_base: %016llx\n", (uint64_t)s->apicbase);
|
||||
#endif
|
||||
return s->apicbase;
|
||||
}
|
||||
|
||||
void cpu_set_apic_tpr(CPUX86State *env, uint8_t val)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
s->tpr = (val & 0x0f) << 4;
|
||||
apic_update_irq(s);
|
||||
}
|
||||
|
||||
uint8_t cpu_get_apic_tpr(CPUX86State *env)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
return s->tpr >> 4;
|
||||
}
|
||||
|
||||
static int fls_bit(int value)
|
||||
/* Find first bit starting from msb. Return 0 if value = 0 */
|
||||
static int fls_bit(uint32_t value)
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
|
||||
#ifdef HOST_I386
|
||||
#if defined(HOST_I386)
|
||||
__asm__ __volatile__ ("bsr %1, %0\n" : "+r" (ret) : "rm" (value));
|
||||
return ret;
|
||||
#else
|
||||
@@ -198,6 +123,31 @@ static int fls_bit(int value)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Find first bit starting from lsb. Return 0 if value = 0 */
|
||||
static int ffs_bit(uint32_t value)
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
|
||||
#if defined(HOST_I386)
|
||||
__asm__ __volatile__ ("bsf %1, %0\n" : "+r" (ret) : "rm" (value));
|
||||
return ret;
|
||||
#else
|
||||
if (!value)
|
||||
return 0;
|
||||
if (!(value & 0xffff))
|
||||
value >>= 16, ret = 16;
|
||||
if (!(value & 0xff))
|
||||
value >>= 8, ret += 8;
|
||||
if (!(value & 0xf))
|
||||
value >>= 4, ret += 4;
|
||||
if (!(value & 0x3))
|
||||
value >>= 2, ret += 2;
|
||||
if (!(value & 0x1))
|
||||
ret++;
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void set_bit(uint32_t *tab, int index)
|
||||
{
|
||||
int i, mask;
|
||||
@@ -214,6 +164,115 @@ static inline void reset_bit(uint32_t *tab, int index)
|
||||
tab[i] &= ~mask;
|
||||
}
|
||||
|
||||
#define foreach_apic(apic, deliver_bitmask, code) \
|
||||
{\
|
||||
int __i, __j, __mask;\
|
||||
for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\
|
||||
__mask = deliver_bitmask[__i];\
|
||||
if (__mask) {\
|
||||
for(__j = 0; __j < 32; __j++) {\
|
||||
if (__mask & (1 << __j)) {\
|
||||
apic = local_apics[__i * 32 + __j];\
|
||||
if (apic) {\
|
||||
code;\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
}
|
||||
|
||||
static void apic_bus_deliver(const uint32_t *deliver_bitmask,
|
||||
uint8_t delivery_mode,
|
||||
uint8_t vector_num, uint8_t polarity,
|
||||
uint8_t trigger_mode)
|
||||
{
|
||||
APICState *apic_iter;
|
||||
|
||||
switch (delivery_mode) {
|
||||
case APIC_DM_LOWPRI:
|
||||
/* XXX: search for focus processor, arbitration */
|
||||
{
|
||||
int i, d;
|
||||
d = -1;
|
||||
for(i = 0; i < MAX_APIC_WORDS; i++) {
|
||||
if (deliver_bitmask[i]) {
|
||||
d = i * 32 + ffs_bit(deliver_bitmask[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (d >= 0) {
|
||||
apic_iter = local_apics[d];
|
||||
if (apic_iter) {
|
||||
apic_set_irq(apic_iter, vector_num, trigger_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
case APIC_DM_FIXED:
|
||||
break;
|
||||
|
||||
case APIC_DM_SMI:
|
||||
case APIC_DM_NMI:
|
||||
break;
|
||||
|
||||
case APIC_DM_INIT:
|
||||
/* normal INIT IPI sent to processors */
|
||||
foreach_apic(apic_iter, deliver_bitmask,
|
||||
apic_init_ipi(apic_iter) );
|
||||
return;
|
||||
|
||||
case APIC_DM_EXTINT:
|
||||
/* handled in I/O APIC code */
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
foreach_apic(apic_iter, deliver_bitmask,
|
||||
apic_set_irq(apic_iter, vector_num, trigger_mode) );
|
||||
}
|
||||
|
||||
void cpu_set_apic_base(CPUState *env, uint64_t val)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
#ifdef DEBUG_APIC
|
||||
printf("cpu_set_apic_base: %016" PRIx64 "\n", val);
|
||||
#endif
|
||||
s->apicbase = (val & 0xfffff000) |
|
||||
(s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
|
||||
/* if disabled, cannot be enabled again */
|
||||
if (!(val & MSR_IA32_APICBASE_ENABLE)) {
|
||||
s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
|
||||
env->cpuid_features &= ~CPUID_APIC;
|
||||
s->spurious_vec &= ~APIC_SV_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t cpu_get_apic_base(CPUState *env)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
#ifdef DEBUG_APIC
|
||||
printf("cpu_get_apic_base: %016" PRIx64 "\n", (uint64_t)s->apicbase);
|
||||
#endif
|
||||
return s->apicbase;
|
||||
}
|
||||
|
||||
void cpu_set_apic_tpr(CPUX86State *env, uint8_t val)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
s->tpr = (val & 0x0f) << 4;
|
||||
apic_update_irq(s);
|
||||
}
|
||||
|
||||
uint8_t cpu_get_apic_tpr(CPUX86State *env)
|
||||
{
|
||||
APICState *s = env->apic_state;
|
||||
return s->tpr >> 4;
|
||||
}
|
||||
|
||||
/* return -1 if no bit is set */
|
||||
static int get_highest_priority_int(uint32_t *tab)
|
||||
{
|
||||
@@ -285,26 +344,37 @@ static void apic_eoi(APICState *s)
|
||||
apic_update_irq(s);
|
||||
}
|
||||
|
||||
static uint32_t apic_get_delivery_bitmask(uint8_t dest, uint8_t dest_mode)
|
||||
static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
|
||||
uint8_t dest, uint8_t dest_mode)
|
||||
{
|
||||
uint32_t mask = 0;
|
||||
APICState *apic_iter;
|
||||
int i;
|
||||
|
||||
if (dest_mode == 0) {
|
||||
if (dest == 0xff)
|
||||
mask = 0xff;
|
||||
else
|
||||
mask = 1 << dest;
|
||||
if (dest == 0xff) {
|
||||
memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t));
|
||||
} else {
|
||||
memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
|
||||
set_bit(deliver_bitmask, dest);
|
||||
}
|
||||
} else {
|
||||
/* XXX: cluster mode */
|
||||
for (apic_iter = first_local_apic; apic_iter != NULL;
|
||||
apic_iter = apic_iter->next_apic) {
|
||||
if (dest & apic_iter->log_dest)
|
||||
mask |= (1 << apic_iter->id);
|
||||
memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
|
||||
for(i = 0; i < MAX_APICS; i++) {
|
||||
apic_iter = local_apics[i];
|
||||
if (apic_iter) {
|
||||
if (apic_iter->dest_mode == 0xf) {
|
||||
if (dest & apic_iter->log_dest)
|
||||
set_bit(deliver_bitmask, i);
|
||||
} else if (apic_iter->dest_mode == 0x0) {
|
||||
if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) &&
|
||||
(dest & apic_iter->log_dest & 0x0f)) {
|
||||
set_bit(deliver_bitmask, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
@@ -312,16 +382,15 @@ static void apic_init_ipi(APICState *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < APIC_LVT_NB; i++)
|
||||
s->lvt[i] = 1 << 16; /* mask LVT */
|
||||
s->tpr = 0;
|
||||
s->spurious_vec = 0xff;
|
||||
s->log_dest = 0;
|
||||
s->dest_mode = 0;
|
||||
s->dest_mode = 0xf;
|
||||
memset(s->isr, 0, sizeof(s->isr));
|
||||
memset(s->tmr, 0, sizeof(s->tmr));
|
||||
memset(s->irr, 0, sizeof(s->irr));
|
||||
memset(s->lvt, 0, sizeof(s->lvt));
|
||||
for(i = 0; i < APIC_LVT_NB; i++)
|
||||
s->lvt[i] = 1 << 16; /* mask LVT */
|
||||
s->esr = 0;
|
||||
memset(s->icr, 0, sizeof(s->icr));
|
||||
s->divide_conf = 0;
|
||||
@@ -331,61 +400,62 @@ static void apic_init_ipi(APICState *s)
|
||||
s->next_time = 0;
|
||||
}
|
||||
|
||||
/* send a SIPI message to the CPU to start it */
|
||||
static void apic_startup(APICState *s, int vector_num)
|
||||
{
|
||||
CPUState *env = s->cpu_env;
|
||||
if (!(env->hflags & HF_HALTED_MASK))
|
||||
return;
|
||||
env->eip = 0;
|
||||
cpu_x86_load_seg_cache(env, R_CS, vector_num << 8, vector_num << 12,
|
||||
0xffff, 0);
|
||||
env->hflags &= ~HF_HALTED_MASK;
|
||||
}
|
||||
|
||||
static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode,
|
||||
uint8_t delivery_mode, uint8_t vector_num,
|
||||
uint8_t polarity, uint8_t trigger_mode)
|
||||
{
|
||||
uint32_t deliver_bitmask = 0;
|
||||
uint32_t deliver_bitmask[MAX_APIC_WORDS];
|
||||
int dest_shorthand = (s->icr[0] >> 18) & 3;
|
||||
APICState *apic_iter;
|
||||
|
||||
switch (delivery_mode) {
|
||||
case APIC_DM_LOWPRI:
|
||||
/* XXX: serch for focus processor, arbitration */
|
||||
dest = s->id;
|
||||
switch (dest_shorthand) {
|
||||
case 0:
|
||||
apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
|
||||
break;
|
||||
case 1:
|
||||
memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask));
|
||||
set_bit(deliver_bitmask, s->id);
|
||||
break;
|
||||
case 2:
|
||||
memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
|
||||
break;
|
||||
case 3:
|
||||
memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
|
||||
reset_bit(deliver_bitmask, s->id);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (delivery_mode) {
|
||||
case APIC_DM_INIT:
|
||||
{
|
||||
int trig_mode = (s->icr[0] >> 15) & 1;
|
||||
int level = (s->icr[0] >> 14) & 1;
|
||||
if (level == 0 && trig_mode == 1) {
|
||||
for (apic_iter = first_local_apic; apic_iter != NULL;
|
||||
apic_iter = apic_iter->next_apic) {
|
||||
if (deliver_bitmask & (1 << apic_iter->id)) {
|
||||
apic_iter->arb_id = apic_iter->id;
|
||||
}
|
||||
}
|
||||
foreach_apic(apic_iter, deliver_bitmask,
|
||||
apic_iter->arb_id = apic_iter->id );
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case APIC_DM_SIPI:
|
||||
for (apic_iter = first_local_apic; apic_iter != NULL;
|
||||
apic_iter = apic_iter->next_apic) {
|
||||
if (deliver_bitmask & (1 << apic_iter->id)) {
|
||||
/* XXX: SMP support */
|
||||
/* apic_startup(apic_iter); */
|
||||
}
|
||||
}
|
||||
foreach_apic(apic_iter, deliver_bitmask,
|
||||
apic_startup(apic_iter, vector_num) );
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dest_shorthand) {
|
||||
case 0:
|
||||
deliver_bitmask = apic_get_delivery_bitmask(dest, dest_mode);
|
||||
break;
|
||||
case 1:
|
||||
deliver_bitmask = (1 << s->id);
|
||||
break;
|
||||
case 2:
|
||||
deliver_bitmask = 0xffffffff;
|
||||
break;
|
||||
case 3:
|
||||
deliver_bitmask = 0xffffffff & ~(1 << s->id);
|
||||
break;
|
||||
}
|
||||
|
||||
apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity,
|
||||
trigger_mode);
|
||||
}
|
||||
@@ -406,9 +476,9 @@ int apic_get_interrupt(CPUState *env)
|
||||
intno = get_highest_priority_int(s->irr);
|
||||
if (intno < 0)
|
||||
return -1;
|
||||
reset_bit(s->irr, intno);
|
||||
if (s->tpr && intno <= s->tpr)
|
||||
return s->spurious_vec & 0xff;
|
||||
reset_bit(s->irr, intno);
|
||||
set_bit(s->isr, intno);
|
||||
apic_update_irq(s);
|
||||
return intno;
|
||||
@@ -534,13 +604,13 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
|
||||
case 0x28:
|
||||
val = s->esr;
|
||||
break;
|
||||
case 0x32 ... 0x37:
|
||||
val = s->lvt[index - 0x32];
|
||||
break;
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
val = s->icr[index & 1];
|
||||
break;
|
||||
case 0x32 ... 0x37:
|
||||
val = s->lvt[index - 0x32];
|
||||
break;
|
||||
case 0x38:
|
||||
val = s->initial_count;
|
||||
break;
|
||||
@@ -581,10 +651,15 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
case 0x02:
|
||||
s->id = (val >> 24);
|
||||
break;
|
||||
case 0x03:
|
||||
break;
|
||||
case 0x08:
|
||||
s->tpr = val;
|
||||
apic_update_irq(s);
|
||||
break;
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
break;
|
||||
case 0x0b: /* EOI */
|
||||
apic_eoi(s);
|
||||
break;
|
||||
@@ -598,6 +673,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
s->spurious_vec = val & 0x1ff;
|
||||
apic_update_irq(s);
|
||||
break;
|
||||
case 0x10 ... 0x17:
|
||||
case 0x18 ... 0x1f:
|
||||
case 0x20 ... 0x27:
|
||||
case 0x28:
|
||||
break;
|
||||
case 0x30:
|
||||
s->icr[0] = val;
|
||||
apic_deliver(s, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1,
|
||||
@@ -620,6 +700,8 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
s->initial_count_load_time = qemu_get_clock(vm_clock);
|
||||
apic_timer_update(s, s->initial_count_load_time);
|
||||
break;
|
||||
case 0x39:
|
||||
break;
|
||||
case 0x3e:
|
||||
{
|
||||
int v;
|
||||
@@ -662,6 +744,8 @@ static void apic_save(QEMUFile *f, void *opaque)
|
||||
qemu_put_be32s(f, &s->initial_count);
|
||||
qemu_put_be64s(f, &s->initial_count_load_time);
|
||||
qemu_put_be64s(f, &s->next_time);
|
||||
|
||||
qemu_put_timer(f, s->timer);
|
||||
}
|
||||
|
||||
static int apic_load(QEMUFile *f, void *opaque, int version_id)
|
||||
@@ -669,7 +753,7 @@ static int apic_load(QEMUFile *f, void *opaque, int version_id)
|
||||
APICState *s = opaque;
|
||||
int i;
|
||||
|
||||
if (version_id != 1)
|
||||
if (version_id > 2)
|
||||
return -EINVAL;
|
||||
|
||||
/* XXX: what if the base changes? (registered memory regions) */
|
||||
@@ -696,6 +780,9 @@ static int apic_load(QEMUFile *f, void *opaque, int version_id)
|
||||
qemu_get_be32s(f, &s->initial_count);
|
||||
qemu_get_be64s(f, &s->initial_count_load_time);
|
||||
qemu_get_be64s(f, &s->next_time);
|
||||
|
||||
if (version_id >= 2)
|
||||
qemu_get_timer(f, s->timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -721,6 +808,8 @@ int apic_init(CPUState *env)
|
||||
{
|
||||
APICState *s;
|
||||
|
||||
if (last_apic_id >= MAX_APICS)
|
||||
return -1;
|
||||
s = qemu_mallocz(sizeof(APICState));
|
||||
if (!s)
|
||||
return -1;
|
||||
@@ -742,12 +831,10 @@ int apic_init(CPUState *env)
|
||||
}
|
||||
s->timer = qemu_new_timer(vm_clock, apic_timer, s);
|
||||
|
||||
register_savevm("apic", 0, 1, apic_save, apic_load, s);
|
||||
register_savevm("apic", 0, 2, apic_save, apic_load, s);
|
||||
qemu_register_reset(apic_reset, s);
|
||||
|
||||
s->next_apic = first_local_apic;
|
||||
first_local_apic = s;
|
||||
|
||||
local_apics[s->id] = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -762,6 +849,7 @@ static void ioapic_service(IOAPICState *s)
|
||||
uint8_t dest;
|
||||
uint8_t dest_mode;
|
||||
uint8_t polarity;
|
||||
uint32_t deliver_bitmask[MAX_APIC_WORDS];
|
||||
|
||||
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
|
||||
mask = 1 << i;
|
||||
@@ -779,8 +867,10 @@ static void ioapic_service(IOAPICState *s)
|
||||
vector = pic_read_irq(isa_pic);
|
||||
else
|
||||
vector = entry & 0xff;
|
||||
apic_bus_deliver(apic_get_delivery_bitmask(dest, dest_mode),
|
||||
delivery_mode, vector, polarity, trig_mode);
|
||||
|
||||
apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
|
||||
apic_bus_deliver(deliver_bitmask, delivery_mode,
|
||||
vector, polarity, trig_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
|
||||
+101
-10
@@ -644,15 +644,90 @@ static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s)
|
||||
(s->cirrus_blt_srcaddr & ~7));
|
||||
}
|
||||
|
||||
static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
|
||||
static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
|
||||
{
|
||||
int sx, sy;
|
||||
int dx, dy;
|
||||
int width, height;
|
||||
int depth;
|
||||
int notify = 0;
|
||||
|
||||
depth = s->get_bpp((VGAState *)s) / 8;
|
||||
s->get_resolution((VGAState *)s, &width, &height);
|
||||
|
||||
/* extra x, y */
|
||||
sx = (src % (width * depth)) / depth;
|
||||
sy = (src / (width * depth));
|
||||
dx = (dst % (width *depth)) / depth;
|
||||
dy = (dst / (width * depth));
|
||||
|
||||
/* normalize width */
|
||||
w /= depth;
|
||||
|
||||
/* if we're doing a backward copy, we have to adjust
|
||||
our x/y to be the upper left corner (instead of the lower
|
||||
right corner) */
|
||||
if (s->cirrus_blt_dstpitch < 0) {
|
||||
sx -= (s->cirrus_blt_width / depth) - 1;
|
||||
dx -= (s->cirrus_blt_width / depth) - 1;
|
||||
sy -= s->cirrus_blt_height - 1;
|
||||
dy -= s->cirrus_blt_height - 1;
|
||||
}
|
||||
|
||||
/* are we in the visible portion of memory? */
|
||||
if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 &&
|
||||
(sx + w) <= width && (sy + h) <= height &&
|
||||
(dx + w) <= width && (dy + h) <= height) {
|
||||
notify = 1;
|
||||
}
|
||||
|
||||
/* make to sure only copy if it's a plain copy ROP */
|
||||
if (*s->cirrus_rop != cirrus_bitblt_rop_fwd_src &&
|
||||
*s->cirrus_rop != cirrus_bitblt_rop_bkwd_src)
|
||||
notify = 0;
|
||||
|
||||
/* we have to flush all pending changes so that the copy
|
||||
is generated at the appropriate moment in time */
|
||||
if (notify)
|
||||
vga_hw_update();
|
||||
|
||||
(*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr,
|
||||
s->vram_ptr + s->cirrus_blt_srcaddr,
|
||||
s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
|
||||
s->cirrus_blt_width, s->cirrus_blt_height);
|
||||
cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
|
||||
s->cirrus_blt_dstpitch, s->cirrus_blt_width,
|
||||
s->cirrus_blt_height);
|
||||
|
||||
if (notify)
|
||||
s->ds->dpy_copy(s->ds,
|
||||
sx, sy, dx, dy,
|
||||
s->cirrus_blt_width / depth,
|
||||
s->cirrus_blt_height);
|
||||
|
||||
/* we don't have to notify the display that this portion has
|
||||
changed since dpy_copy implies this */
|
||||
|
||||
if (!notify)
|
||||
cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
|
||||
s->cirrus_blt_dstpitch, s->cirrus_blt_width,
|
||||
s->cirrus_blt_height);
|
||||
}
|
||||
|
||||
static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
|
||||
{
|
||||
if (s->ds->dpy_copy) {
|
||||
cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr,
|
||||
s->cirrus_blt_srcaddr - s->start_addr,
|
||||
s->cirrus_blt_width, s->cirrus_blt_height);
|
||||
} else {
|
||||
(*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr,
|
||||
s->vram_ptr + s->cirrus_blt_srcaddr,
|
||||
s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
|
||||
s->cirrus_blt_width, s->cirrus_blt_height);
|
||||
|
||||
cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
|
||||
s->cirrus_blt_dstpitch, s->cirrus_blt_width,
|
||||
s->cirrus_blt_height);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -925,12 +1000,12 @@ static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value)
|
||||
***************************************/
|
||||
|
||||
static void cirrus_get_offsets(VGAState *s1,
|
||||
uint32_t *pline_offset,
|
||||
uint32_t *pstart_addr)
|
||||
uint32_t *pline_offset,
|
||||
uint32_t *pstart_addr,
|
||||
uint32_t *pline_compare)
|
||||
{
|
||||
CirrusVGAState * s = (CirrusVGAState *)s1;
|
||||
uint32_t start_addr;
|
||||
uint32_t line_offset;
|
||||
uint32_t start_addr, line_offset, line_compare;
|
||||
|
||||
line_offset = s->cr[0x13]
|
||||
| ((s->cr[0x1b] & 0x10) << 4);
|
||||
@@ -943,6 +1018,11 @@ static void cirrus_get_offsets(VGAState *s1,
|
||||
| ((s->cr[0x1b] & 0x0c) << 15)
|
||||
| ((s->cr[0x1d] & 0x80) << 12);
|
||||
*pstart_addr = start_addr;
|
||||
|
||||
line_compare = s->cr[0x18] |
|
||||
((s->cr[0x07] & 0x10) << 4) |
|
||||
((s->cr[0x09] & 0x40) << 3);
|
||||
*pline_compare = line_compare;
|
||||
}
|
||||
|
||||
static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s)
|
||||
@@ -2830,6 +2910,9 @@ static void cirrus_vga_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
CirrusVGAState *s = opaque;
|
||||
|
||||
if (s->pci_dev)
|
||||
pci_device_save(s->pci_dev, f);
|
||||
|
||||
qemu_put_be32s(f, &s->latch);
|
||||
qemu_put_8s(f, &s->sr_index);
|
||||
qemu_put_buffer(f, s->sr, 256);
|
||||
@@ -2868,10 +2951,17 @@ static void cirrus_vga_save(QEMUFile *f, void *opaque)
|
||||
static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
CirrusVGAState *s = opaque;
|
||||
int ret;
|
||||
|
||||
if (version_id != 1)
|
||||
if (version_id > 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (s->pci_dev && version_id >= 2) {
|
||||
ret = pci_device_load(s->pci_dev, f);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_get_be32s(f, &s->latch);
|
||||
qemu_get_8s(f, &s->sr_index);
|
||||
qemu_get_buffer(f, s->sr, 256);
|
||||
@@ -3025,7 +3115,7 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
|
||||
s->cursor_invalidate = cirrus_cursor_invalidate;
|
||||
s->cursor_draw_line = cirrus_cursor_draw_line;
|
||||
|
||||
register_savevm("cirrus_vga", 0, 1, cirrus_vga_save, cirrus_vga_load, s);
|
||||
register_savevm("cirrus_vga", 0, 2, cirrus_vga_save, cirrus_vga_load, s);
|
||||
}
|
||||
|
||||
/***************************************
|
||||
@@ -3103,6 +3193,7 @@ void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
|
||||
vga_common_init((VGAState *)s,
|
||||
ds, vga_ram_base, vga_ram_offset, vga_ram_size);
|
||||
cirrus_init_common(s, device_id, 1);
|
||||
s->pci_dev = (PCIDevice *)d;
|
||||
|
||||
/* setup memory space */
|
||||
/* memory #0 LFB */
|
||||
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* QEMU Crystal CS4231 audio chip emulation
|
||||
*
|
||||
* 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"
|
||||
|
||||
/* debug CS4231 */
|
||||
//#define DEBUG_CS
|
||||
|
||||
/*
|
||||
* In addition to Crystal CS4231 there is a DMA controller on Sparc.
|
||||
*/
|
||||
#define CS_MAXADDR 0x3f
|
||||
#define CS_REGS 16
|
||||
#define CS_DREGS 32
|
||||
#define CS_MAXDREG (CS_DREGS - 1)
|
||||
|
||||
typedef struct CSState {
|
||||
uint32_t regs[CS_REGS];
|
||||
uint8_t dregs[CS_DREGS];
|
||||
void *intctl;
|
||||
} CSState;
|
||||
|
||||
#define CS_RAP(s) ((s)->regs[0] & CS_MAXDREG)
|
||||
#define CS_VER 0xa0
|
||||
#define CS_CDC_VER 0x8a
|
||||
|
||||
#ifdef DEBUG_CS
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("CS: " fmt , ##args); } while (0)
|
||||
#define pic_set_irq_new(intctl, irq, level) \
|
||||
do { printf("CS: set_irq(%d): %d\n", (irq), (level)); \
|
||||
pic_set_irq_new((intctl), (irq),(level));} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...)
|
||||
#endif
|
||||
|
||||
static void cs_reset(void *opaque)
|
||||
{
|
||||
CSState *s = opaque;
|
||||
|
||||
memset(s->regs, 0, CS_REGS * 4);
|
||||
memset(s->dregs, 0, CS_DREGS);
|
||||
s->dregs[12] = CS_CDC_VER;
|
||||
s->dregs[25] = CS_VER;
|
||||
}
|
||||
|
||||
static uint32_t cs_mem_readl(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
CSState *s = opaque;
|
||||
uint32_t saddr, ret;
|
||||
|
||||
saddr = (addr & CS_MAXADDR) >> 2;
|
||||
switch (saddr) {
|
||||
case 1:
|
||||
switch (CS_RAP(s)) {
|
||||
case 3: // Write only
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = s->dregs[CS_RAP(s)];
|
||||
break;
|
||||
}
|
||||
DPRINTF("read dreg[%d]: 0x%8.8x\n", CS_RAP(s), ret);
|
||||
break;
|
||||
default:
|
||||
ret = s->regs[saddr];
|
||||
DPRINTF("read reg[%d]: 0x%8.8x\n", saddr, ret);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cs_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
CSState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & CS_MAXADDR) >> 2;
|
||||
DPRINTF("write reg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->regs[saddr], val);
|
||||
switch (saddr) {
|
||||
case 1:
|
||||
DPRINTF("write dreg[%d]: 0x%2.2x -> 0x%2.2x\n", CS_RAP(s), s->dregs[CS_RAP(s)], val);
|
||||
switch(CS_RAP(s)) {
|
||||
case 11:
|
||||
case 25: // Read only
|
||||
break;
|
||||
case 12:
|
||||
val &= 0x40;
|
||||
val |= CS_CDC_VER; // Codec version
|
||||
s->dregs[CS_RAP(s)] = val;
|
||||
break;
|
||||
default:
|
||||
s->dregs[CS_RAP(s)] = val;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2: // Read only
|
||||
break;
|
||||
case 4:
|
||||
if (val & 1)
|
||||
cs_reset(s);
|
||||
val &= 0x7f;
|
||||
s->regs[saddr] = val;
|
||||
break;
|
||||
default:
|
||||
s->regs[saddr] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *cs_mem_read[3] = {
|
||||
cs_mem_readl,
|
||||
cs_mem_readl,
|
||||
cs_mem_readl,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *cs_mem_write[3] = {
|
||||
cs_mem_writel,
|
||||
cs_mem_writel,
|
||||
cs_mem_writel,
|
||||
};
|
||||
|
||||
static void cs_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
CSState *s = opaque;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < CS_REGS; i++)
|
||||
qemu_put_be32s(f, &s->regs[i]);
|
||||
|
||||
qemu_put_buffer(f, s->dregs, CS_DREGS);
|
||||
}
|
||||
|
||||
static int cs_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
CSState *s = opaque;
|
||||
unsigned int i;
|
||||
|
||||
if (version_id > 1)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < CS_REGS; i++)
|
||||
qemu_get_be32s(f, &s->regs[i]);
|
||||
|
||||
qemu_get_buffer(f, s->dregs, CS_DREGS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cs_init(target_phys_addr_t base, int irq, void *intctl)
|
||||
{
|
||||
int cs_io_memory;
|
||||
CSState *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(CSState));
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
cs_io_memory = cpu_register_io_memory(0, cs_mem_read, cs_mem_write, s);
|
||||
cpu_register_physical_memory(base, CS_MAXADDR, cs_io_memory);
|
||||
register_savevm("cs4231", base, 1, cs_save, cs_load, s);
|
||||
qemu_register_reset(cs_reset, s);
|
||||
cs_reset(s);
|
||||
}
|
||||
@@ -209,7 +209,7 @@ static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
|
||||
}
|
||||
#if 0
|
||||
#ifdef DEBUG_CUDA
|
||||
printf("latch=%d counter=%lld delta_next=%lld\n",
|
||||
printf("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n",
|
||||
s->latch, d, next_time - d);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -427,7 +427,9 @@ int DMA_write_memory (int nchan, void *buf, int pos, int len)
|
||||
/* request the emulator to transfer a new DMA memory block ASAP */
|
||||
void DMA_schedule(int nchan)
|
||||
{
|
||||
cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
|
||||
CPUState *env = cpu_single_env;
|
||||
if (env)
|
||||
cpu_interrupt(env, CPU_INTERRUPT_EXIT);
|
||||
}
|
||||
|
||||
static void dma_reset(void *opaque)
|
||||
|
||||
-218
@@ -1,218 +0,0 @@
|
||||
#ifdef BSWAP_NEEDED
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int glue(find_phdr, SZ)(struct elfhdr *ehdr, int fd, struct elf_phdr *phdr, elf_word type)
|
||||
{
|
||||
int i, retval;
|
||||
|
||||
retval = lseek(fd, ehdr->e_phoff, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
retval = read(fd, phdr, sizeof(*phdr));
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
glue(bswap_phdr, SZ)(phdr);
|
||||
if (phdr->p_type == type)
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void * glue(find_shdr, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, elf_word type)
|
||||
{
|
||||
int i, retval;
|
||||
|
||||
retval = lseek(fd, ehdr->e_shoff, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < ehdr->e_shnum; i++) {
|
||||
retval = read(fd, shdr, sizeof(*shdr));
|
||||
if (retval < 0)
|
||||
return NULL;
|
||||
glue(bswap_shdr, SZ)(shdr);
|
||||
if (shdr->sh_type == type)
|
||||
return qemu_malloc(shdr->sh_size);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void * glue(find_strtab, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = lseek(fd, ehdr->e_shoff + sizeof(struct elf_shdr) * symtab->sh_link, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return NULL;
|
||||
|
||||
retval = read(fd, shdr, sizeof(*shdr));
|
||||
if (retval < 0)
|
||||
return NULL;
|
||||
glue(bswap_shdr, SZ)(shdr);
|
||||
if (shdr->sh_type == SHT_STRTAB)
|
||||
return qemu_malloc(shdr->sh_size);;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int glue(read_program, SZ)(int fd, struct elf_phdr *phdr, void *dst, elf_word entry)
|
||||
{
|
||||
int retval;
|
||||
retval = lseek(fd, phdr->p_offset + entry - phdr->p_vaddr, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
return read(fd, dst, phdr->p_filesz);
|
||||
}
|
||||
|
||||
static int glue(read_section, SZ)(int fd, struct elf_shdr *s, void *dst)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = lseek(fd, s->sh_offset, SEEK_SET);
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
retval = read(fd, dst, s->sh_size);
|
||||
if (retval < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void * glue(process_section, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, elf_word type)
|
||||
{
|
||||
void *dst;
|
||||
|
||||
dst = glue(find_shdr, SZ)(ehdr, fd, shdr, type);
|
||||
if (!dst)
|
||||
goto error;
|
||||
|
||||
if (glue(read_section, SZ)(fd, shdr, dst))
|
||||
goto error;
|
||||
return dst;
|
||||
error:
|
||||
qemu_free(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void * glue(process_strtab, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab)
|
||||
{
|
||||
void *dst;
|
||||
|
||||
dst = glue(find_strtab, SZ)(ehdr, fd, shdr, symtab);
|
||||
if (!dst)
|
||||
goto error;
|
||||
|
||||
if (glue(read_section, SZ)(fd, shdr, dst))
|
||||
goto error;
|
||||
return dst;
|
||||
error:
|
||||
qemu_free(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd)
|
||||
{
|
||||
struct elf_shdr symtab, strtab;
|
||||
struct elf_sym *syms;
|
||||
#if (SZ == 64)
|
||||
struct elf32_sym *syms32;
|
||||
#endif
|
||||
struct syminfo *s;
|
||||
int nsyms, i;
|
||||
char *str;
|
||||
|
||||
/* Symbol table */
|
||||
syms = glue(process_section, SZ)(ehdr, fd, &symtab, SHT_SYMTAB);
|
||||
if (!syms)
|
||||
return;
|
||||
|
||||
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++) {
|
||||
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 */
|
||||
str = glue(process_strtab, SZ)(ehdr, fd, &strtab, &symtab);
|
||||
if (!str)
|
||||
goto error_freesyms;
|
||||
|
||||
/* 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;
|
||||
return;
|
||||
error_freesyms:
|
||||
#if (SZ == 64)
|
||||
qemu_free(syms32);
|
||||
#endif
|
||||
qemu_free(syms);
|
||||
return;
|
||||
}
|
||||
+1062
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* QEMU ESP emulation
|
||||
* QEMU ESP/NCR53C9x emulation
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
* Copyright (c) 2005-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
|
||||
@@ -26,6 +26,14 @@
|
||||
/* debug ESP card */
|
||||
//#define DEBUG_ESP
|
||||
|
||||
/*
|
||||
* On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O), also
|
||||
* produced as NCR89C100. See
|
||||
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
|
||||
* and
|
||||
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_ESP
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("ESP: " fmt , ##args); } while (0)
|
||||
@@ -33,20 +41,37 @@ do { printf("ESP: " fmt , ##args); } while (0)
|
||||
#define DPRINTF(fmt, args...)
|
||||
#endif
|
||||
|
||||
#define ESPDMA_REGS 4
|
||||
#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
|
||||
#define ESP_MAXREG 0x3f
|
||||
#define TI_BUFSZ 32
|
||||
/* The HBA is ID 7, so for simplicitly limit to 7 devices. */
|
||||
#define ESP_MAX_DEVS 7
|
||||
|
||||
typedef struct ESPState {
|
||||
typedef struct ESPState ESPState;
|
||||
|
||||
struct ESPState {
|
||||
BlockDriverState **bd;
|
||||
uint8_t rregs[ESP_MAXREG];
|
||||
uint8_t wregs[ESP_MAXREG];
|
||||
int irq;
|
||||
uint32_t espdmaregs[ESPDMA_REGS];
|
||||
uint32_t ti_size;
|
||||
int ti_dir;
|
||||
uint8_t ti_buf[65536];
|
||||
} ESPState;
|
||||
int32_t ti_size;
|
||||
uint32_t ti_rptr, ti_wptr;
|
||||
uint8_t ti_buf[TI_BUFSZ];
|
||||
int sense;
|
||||
int dma;
|
||||
SCSIDevice *scsi_dev[MAX_DISKS];
|
||||
SCSIDevice *current_dev;
|
||||
uint8_t cmdbuf[TI_BUFSZ];
|
||||
int cmdlen;
|
||||
int do_cmd;
|
||||
|
||||
/* The amount of data left in the current DMA transfer. */
|
||||
uint32_t dma_left;
|
||||
/* The size of the current DMA transfer. Zero if no transfer is in
|
||||
progress. */
|
||||
uint32_t dma_counter;
|
||||
uint8_t *async_buf;
|
||||
uint32_t async_len;
|
||||
void *dma_opaque;
|
||||
};
|
||||
|
||||
#define STAT_DO 0x00
|
||||
#define STAT_DI 0x01
|
||||
@@ -56,162 +81,264 @@ typedef struct ESPState {
|
||||
#define STAT_MO 0x07
|
||||
|
||||
#define STAT_TC 0x10
|
||||
#define STAT_PE 0x20
|
||||
#define STAT_GE 0x40
|
||||
#define STAT_IN 0x80
|
||||
|
||||
#define INTR_FC 0x08
|
||||
#define INTR_BS 0x10
|
||||
#define INTR_DC 0x20
|
||||
#define INTR_RST 0x80
|
||||
|
||||
#define SEQ_0 0x0
|
||||
#define SEQ_CD 0x4
|
||||
|
||||
static void handle_satn(ESPState *s)
|
||||
static int get_cmd(ESPState *s, uint8_t *buf)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
uint32_t dmaptr, dmalen;
|
||||
unsigned int i;
|
||||
int64_t nb_sectors;
|
||||
uint32_t dmalen;
|
||||
int target;
|
||||
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
DPRINTF("Select with ATN at %8.8x len %d\n", dmaptr, dmalen);
|
||||
DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
|
||||
cpu_physical_memory_read(dmaptr, buf, dmalen);
|
||||
for (i = 0; i < dmalen; i++) {
|
||||
DPRINTF("Command %2.2x\n", buf[i]);
|
||||
}
|
||||
s->ti_dir = 0;
|
||||
s->ti_size = 0;
|
||||
dmalen = s->rregs[0] | (s->rregs[1] << 8);
|
||||
target = s->wregs[4] & 7;
|
||||
DPRINTF("get_cmd: len %d target %d\n", dmalen, target);
|
||||
if (s->dma) {
|
||||
espdma_memory_read(s->dma_opaque, buf, dmalen);
|
||||
} else {
|
||||
buf[0] = 0;
|
||||
memcpy(&buf[1], s->ti_buf, dmalen);
|
||||
dmalen++;
|
||||
}
|
||||
|
||||
if (target > 4 || !s->bd[target]) { // No such drive
|
||||
s->ti_size = 0;
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
|
||||
if (s->current_dev) {
|
||||
/* Started a new command before the old one finished. Cancel it. */
|
||||
scsi_cancel_io(s->current_dev, 0);
|
||||
s->async_len = 0;
|
||||
}
|
||||
|
||||
if (target >= MAX_DISKS || !s->scsi_dev[target]) {
|
||||
// No such drive
|
||||
s->rregs[4] = STAT_IN;
|
||||
s->rregs[5] = INTR_DC;
|
||||
s->rregs[6] = SEQ_0;
|
||||
s->espdmaregs[0] |= 1;
|
||||
pic_set_irq(s->irq, 1);
|
||||
return;
|
||||
espdma_raise_irq(s->dma_opaque);
|
||||
return 0;
|
||||
}
|
||||
switch (buf[1]) {
|
||||
case 0x0:
|
||||
DPRINTF("Test Unit Ready (len %d)\n", buf[5]);
|
||||
break;
|
||||
case 0x12:
|
||||
DPRINTF("Inquiry (len %d)\n", buf[5]);
|
||||
memset(s->ti_buf, 0, 36);
|
||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
|
||||
s->ti_buf[0] = 5;
|
||||
memcpy(&s->ti_buf[16], "QEMU CDROM ", 16);
|
||||
} else {
|
||||
s->ti_buf[0] = 0;
|
||||
memcpy(&s->ti_buf[16], "QEMU HARDDISK ", 16);
|
||||
}
|
||||
memcpy(&s->ti_buf[8], "QEMU ", 8);
|
||||
s->ti_buf[2] = 1;
|
||||
s->ti_buf[3] = 2;
|
||||
s->ti_dir = 1;
|
||||
s->ti_size = 36;
|
||||
break;
|
||||
case 0x1a:
|
||||
DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[3], buf[5]);
|
||||
break;
|
||||
case 0x25:
|
||||
DPRINTF("Read Capacity (len %d)\n", buf[5]);
|
||||
memset(s->ti_buf, 0, 8);
|
||||
bdrv_get_geometry(s->bd[target], &nb_sectors);
|
||||
s->ti_buf[0] = (nb_sectors >> 24) & 0xff;
|
||||
s->ti_buf[1] = (nb_sectors >> 16) & 0xff;
|
||||
s->ti_buf[2] = (nb_sectors >> 8) & 0xff;
|
||||
s->ti_buf[3] = nb_sectors & 0xff;
|
||||
s->ti_buf[4] = 0;
|
||||
s->ti_buf[5] = 0;
|
||||
s->ti_buf[6] = 2;
|
||||
s->ti_buf[7] = 0;
|
||||
s->ti_dir = 1;
|
||||
s->ti_size = 8;
|
||||
break;
|
||||
case 0x28:
|
||||
{
|
||||
int64_t offset, len;
|
||||
|
||||
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
|
||||
len = (buf[8] << 8) | buf[9];
|
||||
DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len);
|
||||
bdrv_read(s->bd[target], offset, s->ti_buf, len);
|
||||
s->ti_dir = 1;
|
||||
s->ti_size = len * 512;
|
||||
break;
|
||||
}
|
||||
case 0x2a:
|
||||
{
|
||||
int64_t offset, len;
|
||||
|
||||
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
|
||||
len = (buf[8] << 8) | buf[9];
|
||||
DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len);
|
||||
bdrv_write(s->bd[target], offset, s->ti_buf, len);
|
||||
s->ti_dir = 0;
|
||||
s->ti_size = len * 512;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DPRINTF("Unknown command (%2.2x)\n", buf[1]);
|
||||
break;
|
||||
}
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_DI;
|
||||
s->rregs[5] = INTR_BS | INTR_FC;
|
||||
s->rregs[6] = SEQ_CD;
|
||||
s->espdmaregs[0] |= 1;
|
||||
pic_set_irq(s->irq, 1);
|
||||
s->current_dev = s->scsi_dev[target];
|
||||
return dmalen;
|
||||
}
|
||||
|
||||
static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len)
|
||||
static void do_cmd(ESPState *s, uint8_t *buf)
|
||||
{
|
||||
uint32_t dmaptr, dmalen;
|
||||
int32_t datalen;
|
||||
int lun;
|
||||
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
|
||||
cpu_physical_memory_write(dmaptr, buf, len);
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||
DPRINTF("do_cmd: busid 0x%x\n", buf[0]);
|
||||
lun = buf[0] & 7;
|
||||
datalen = scsi_send_command(s->current_dev, 0, &buf[1], lun);
|
||||
s->ti_size = datalen;
|
||||
if (datalen != 0) {
|
||||
s->rregs[4] = STAT_IN | STAT_TC;
|
||||
s->dma_left = 0;
|
||||
s->dma_counter = 0;
|
||||
if (datalen > 0) {
|
||||
s->rregs[4] |= STAT_DI;
|
||||
scsi_read_data(s->current_dev, 0);
|
||||
} else {
|
||||
s->rregs[4] |= STAT_DO;
|
||||
scsi_write_data(s->current_dev, 0);
|
||||
}
|
||||
}
|
||||
s->rregs[5] = INTR_BS | INTR_FC;
|
||||
s->rregs[6] = SEQ_CD;
|
||||
s->espdmaregs[0] |= 1;
|
||||
pic_set_irq(s->irq, 1);
|
||||
espdma_raise_irq(s->dma_opaque);
|
||||
}
|
||||
|
||||
static void handle_satn(ESPState *s)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
int len;
|
||||
|
||||
len = get_cmd(s, buf);
|
||||
if (len)
|
||||
do_cmd(s, buf);
|
||||
}
|
||||
|
||||
static void handle_satn_stop(ESPState *s)
|
||||
{
|
||||
s->cmdlen = get_cmd(s, s->cmdbuf);
|
||||
if (s->cmdlen) {
|
||||
DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen);
|
||||
s->do_cmd = 1;
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_CD;
|
||||
s->rregs[5] = INTR_BS | INTR_FC;
|
||||
s->rregs[6] = SEQ_CD;
|
||||
espdma_raise_irq(s->dma_opaque);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_response(ESPState *s)
|
||||
{
|
||||
DPRINTF("Transfer status (sense=%d)\n", s->sense);
|
||||
s->ti_buf[0] = s->sense;
|
||||
s->ti_buf[1] = 0;
|
||||
if (s->dma) {
|
||||
espdma_memory_write(s->dma_opaque, s->ti_buf, 2);
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||
s->rregs[5] = INTR_BS | INTR_FC;
|
||||
s->rregs[6] = SEQ_CD;
|
||||
} else {
|
||||
s->ti_size = 2;
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
s->rregs[7] = 2;
|
||||
}
|
||||
espdma_raise_irq(s->dma_opaque);
|
||||
}
|
||||
|
||||
static void esp_dma_done(ESPState *s)
|
||||
{
|
||||
s->rregs[4] |= STAT_IN | STAT_TC;
|
||||
s->rregs[5] = INTR_BS;
|
||||
s->rregs[6] = 0;
|
||||
s->rregs[7] = 0;
|
||||
s->rregs[0] = 0;
|
||||
s->rregs[1] = 0;
|
||||
espdma_raise_irq(s->dma_opaque);
|
||||
}
|
||||
|
||||
static void esp_do_dma(ESPState *s)
|
||||
{
|
||||
uint32_t len;
|
||||
int to_device;
|
||||
|
||||
to_device = (s->ti_size < 0);
|
||||
len = s->dma_left;
|
||||
if (s->do_cmd) {
|
||||
DPRINTF("command len %d + %d\n", s->cmdlen, len);
|
||||
espdma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len);
|
||||
s->ti_size = 0;
|
||||
s->cmdlen = 0;
|
||||
s->do_cmd = 0;
|
||||
do_cmd(s, s->cmdbuf);
|
||||
return;
|
||||
}
|
||||
if (s->async_len == 0) {
|
||||
/* Defer until data is available. */
|
||||
return;
|
||||
}
|
||||
if (len > s->async_len) {
|
||||
len = s->async_len;
|
||||
}
|
||||
if (to_device) {
|
||||
espdma_memory_read(s->dma_opaque, s->async_buf, len);
|
||||
} else {
|
||||
espdma_memory_write(s->dma_opaque, s->async_buf, len);
|
||||
}
|
||||
s->dma_left -= len;
|
||||
s->async_buf += len;
|
||||
s->async_len -= len;
|
||||
if (to_device)
|
||||
s->ti_size += len;
|
||||
else
|
||||
s->ti_size -= len;
|
||||
if (s->async_len == 0) {
|
||||
if (to_device) {
|
||||
// ti_size is negative
|
||||
scsi_write_data(s->current_dev, 0);
|
||||
} else {
|
||||
scsi_read_data(s->current_dev, 0);
|
||||
/* If there is still data to be read from the device then
|
||||
complete the DMA operation immeriately. Otherwise defer
|
||||
until the scsi layer has completed. */
|
||||
if (s->dma_left == 0 && s->ti_size > 0) {
|
||||
esp_dma_done(s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Partially filled a scsi buffer. Complete immediately. */
|
||||
esp_dma_done(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_command_complete(void *opaque, int reason, uint32_t tag,
|
||||
uint32_t arg)
|
||||
{
|
||||
ESPState *s = (ESPState *)opaque;
|
||||
|
||||
if (reason == SCSI_REASON_DONE) {
|
||||
DPRINTF("SCSI Command complete\n");
|
||||
if (s->ti_size != 0)
|
||||
DPRINTF("SCSI command completed unexpectedly\n");
|
||||
s->ti_size = 0;
|
||||
s->dma_left = 0;
|
||||
s->async_len = 0;
|
||||
if (arg)
|
||||
DPRINTF("Command failed\n");
|
||||
s->sense = arg;
|
||||
s->rregs[4] = STAT_ST;
|
||||
esp_dma_done(s);
|
||||
s->current_dev = NULL;
|
||||
} else {
|
||||
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
|
||||
s->async_len = arg;
|
||||
s->async_buf = scsi_get_buf(s->current_dev, 0);
|
||||
if (s->dma_left) {
|
||||
esp_do_dma(s);
|
||||
} else if (s->dma_counter != 0 && s->ti_size <= 0) {
|
||||
/* If this was the last part of a DMA transfer then the
|
||||
completion interrupt is deferred to here. */
|
||||
esp_dma_done(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
static const uint8_t okbuf[] = {0, 0};
|
||||
|
||||
static void handle_ti(ESPState *s)
|
||||
{
|
||||
uint32_t dmaptr, dmalen;
|
||||
unsigned int i;
|
||||
uint32_t dmalen, minlen;
|
||||
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
DPRINTF("Transfer Information at %8.8x len %d\n", dmaptr, dmalen);
|
||||
DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
|
||||
for (i = 0; i < s->ti_size; i++) {
|
||||
dmaptr = iommu_translate(s->espdmaregs[1] + i);
|
||||
if (s->ti_dir)
|
||||
cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1);
|
||||
else
|
||||
cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1);
|
||||
dmalen = s->rregs[0] | (s->rregs[1] << 8);
|
||||
if (dmalen==0) {
|
||||
dmalen=0x10000;
|
||||
}
|
||||
s->dma_counter = dmalen;
|
||||
|
||||
if (s->do_cmd)
|
||||
minlen = (dmalen < 32) ? dmalen : 32;
|
||||
else if (s->ti_size < 0)
|
||||
minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size;
|
||||
else
|
||||
minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
|
||||
DPRINTF("Transfer Information len %d\n", minlen);
|
||||
if (s->dma) {
|
||||
s->dma_left = minlen;
|
||||
s->rregs[4] &= ~STAT_TC;
|
||||
esp_do_dma(s);
|
||||
} else if (s->do_cmd) {
|
||||
DPRINTF("command len %d\n", s->cmdlen);
|
||||
s->ti_size = 0;
|
||||
s->cmdlen = 0;
|
||||
s->do_cmd = 0;
|
||||
do_cmd(s, s->cmdbuf);
|
||||
return;
|
||||
}
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||
s->rregs[5] = INTR_BS;
|
||||
s->rregs[6] = 0;
|
||||
s->espdmaregs[0] |= 1;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
static void esp_reset(void *opaque)
|
||||
void esp_reset(void *opaque)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
|
||||
memset(s->rregs, 0, ESP_MAXREG);
|
||||
memset(s->wregs, 0, ESP_MAXREG);
|
||||
s->rregs[0x0e] = 0x4; // Indicate fas100a
|
||||
memset(s->espdmaregs, 0, ESPDMA_REGS * 4);
|
||||
s->ti_size = 0;
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
s->dma = 0;
|
||||
s->do_cmd = 0;
|
||||
}
|
||||
|
||||
static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
|
||||
@@ -220,11 +347,35 @@ static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & ESP_MAXREG) >> 2;
|
||||
DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
|
||||
switch (saddr) {
|
||||
case 2:
|
||||
// FIFO
|
||||
if (s->ti_size > 0) {
|
||||
s->ti_size--;
|
||||
if ((s->rregs[4] & 6) == 0) {
|
||||
/* Data in/out. */
|
||||
fprintf(stderr, "esp: PIO data read not implemented\n");
|
||||
s->rregs[2] = 0;
|
||||
} else {
|
||||
s->rregs[2] = s->ti_buf[s->ti_rptr++];
|
||||
}
|
||||
espdma_raise_irq(s->dma_opaque);
|
||||
}
|
||||
if (s->ti_size == 0) {
|
||||
s->ti_rptr = 0;
|
||||
s->ti_wptr = 0;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
// interrupt
|
||||
// Clear interrupt/error status bits
|
||||
s->rregs[4] &= ~(STAT_IN | STAT_GE | STAT_PE);
|
||||
espdma_clear_irq(s->dma_opaque);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
|
||||
return s->rregs[saddr];
|
||||
}
|
||||
|
||||
@@ -236,16 +387,44 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
saddr = (addr & ESP_MAXREG) >> 2;
|
||||
DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val);
|
||||
switch (saddr) {
|
||||
case 0:
|
||||
case 1:
|
||||
s->rregs[4] &= ~STAT_TC;
|
||||
break;
|
||||
case 2:
|
||||
// FIFO
|
||||
if (s->do_cmd) {
|
||||
s->cmdbuf[s->cmdlen++] = val & 0xff;
|
||||
} else if ((s->rregs[4] & 6) == 0) {
|
||||
uint8_t buf;
|
||||
buf = val & 0xff;
|
||||
s->ti_size--;
|
||||
fprintf(stderr, "esp: PIO data write not implemented\n");
|
||||
} else {
|
||||
s->ti_size++;
|
||||
s->ti_buf[s->ti_wptr++] = val & 0xff;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
s->rregs[saddr] = val;
|
||||
// Command
|
||||
if (val & 0x80) {
|
||||
s->dma = 1;
|
||||
/* Reload DMA counter. */
|
||||
s->rregs[0] = s->wregs[0];
|
||||
s->rregs[1] = s->wregs[1];
|
||||
} else {
|
||||
s->dma = 0;
|
||||
}
|
||||
switch(val & 0x7f) {
|
||||
case 0:
|
||||
DPRINTF("NOP (%2.2x)\n", val);
|
||||
break;
|
||||
case 1:
|
||||
DPRINTF("Flush FIFO (%2.2x)\n", val);
|
||||
s->rregs[6] = 0;
|
||||
//s->ti_size = 0;
|
||||
s->rregs[5] = INTR_FC;
|
||||
s->rregs[6] = 0;
|
||||
break;
|
||||
case 2:
|
||||
DPRINTF("Chip reset (%2.2x)\n", val);
|
||||
@@ -253,17 +432,21 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
break;
|
||||
case 3:
|
||||
DPRINTF("Bus reset (%2.2x)\n", val);
|
||||
s->rregs[5] = INTR_RST;
|
||||
if (!(s->wregs[8] & 0x40)) {
|
||||
espdma_raise_irq(s->dma_opaque);
|
||||
}
|
||||
break;
|
||||
case 0x10:
|
||||
handle_ti(s);
|
||||
break;
|
||||
case 0x11:
|
||||
DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val);
|
||||
dma_write(s, okbuf, 2);
|
||||
write_response(s);
|
||||
break;
|
||||
case 0x12:
|
||||
DPRINTF("Message Accepted (%2.2x)\n", val);
|
||||
dma_write(s, okbuf, 2);
|
||||
write_response(s);
|
||||
s->rregs[5] = INTR_DC;
|
||||
s->rregs[6] = 0;
|
||||
break;
|
||||
@@ -271,20 +454,31 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
DPRINTF("Set ATN (%2.2x)\n", val);
|
||||
break;
|
||||
case 0x42:
|
||||
DPRINTF("Set ATN (%2.2x)\n", val);
|
||||
handle_satn(s);
|
||||
break;
|
||||
case 0x43:
|
||||
DPRINTF("Set ATN & stop (%2.2x)\n", val);
|
||||
handle_satn(s);
|
||||
handle_satn_stop(s);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Unhandled command (%2.2x)\n", val);
|
||||
DPRINTF("Unhandled ESP command (%2.2x)\n", val);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4 ... 7:
|
||||
case 9 ... 0xf:
|
||||
break;
|
||||
case 8:
|
||||
s->rregs[saddr] = val;
|
||||
break;
|
||||
case 9 ... 10:
|
||||
break;
|
||||
case 11:
|
||||
s->rregs[saddr] = val & 0x15;
|
||||
break;
|
||||
case 12 ... 15:
|
||||
s->rregs[saddr] = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -303,96 +497,79 @@ static CPUWriteMemoryFunc *esp_mem_write[3] = {
|
||||
esp_mem_writeb,
|
||||
};
|
||||
|
||||
static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & ESPDMA_MAXADDR) >> 2;
|
||||
DPRINTF("read dmareg[%d]: 0x%2.2x\n", saddr, s->espdmaregs[saddr]);
|
||||
return s->espdmaregs[saddr];
|
||||
}
|
||||
|
||||
static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
saddr = (addr & ESPDMA_MAXADDR) >> 2;
|
||||
DPRINTF("write dmareg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->espdmaregs[saddr], val);
|
||||
switch (saddr) {
|
||||
case 0:
|
||||
if (!(val & 0x10))
|
||||
pic_set_irq(s->irq, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
s->espdmaregs[saddr] = val;
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *espdma_mem_read[3] = {
|
||||
espdma_mem_readl,
|
||||
espdma_mem_readl,
|
||||
espdma_mem_readl,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *espdma_mem_write[3] = {
|
||||
espdma_mem_writel,
|
||||
espdma_mem_writel,
|
||||
espdma_mem_writel,
|
||||
};
|
||||
|
||||
static void esp_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
unsigned int i;
|
||||
|
||||
qemu_put_buffer(f, s->rregs, ESP_MAXREG);
|
||||
qemu_put_buffer(f, s->wregs, ESP_MAXREG);
|
||||
qemu_put_be32s(f, &s->irq);
|
||||
for (i = 0; i < ESPDMA_REGS; i++)
|
||||
qemu_put_be32s(f, &s->espdmaregs[i]);
|
||||
qemu_put_be32s(f, &s->ti_size);
|
||||
qemu_put_be32s(f, &s->ti_rptr);
|
||||
qemu_put_be32s(f, &s->ti_wptr);
|
||||
qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
|
||||
qemu_put_be32s(f, &s->dma);
|
||||
}
|
||||
|
||||
static int esp_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
ESPState *s = opaque;
|
||||
unsigned int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
if (version_id != 2)
|
||||
return -EINVAL; // Cannot emulate 1
|
||||
|
||||
qemu_get_buffer(f, s->rregs, ESP_MAXREG);
|
||||
qemu_get_buffer(f, s->wregs, ESP_MAXREG);
|
||||
qemu_get_be32s(f, &s->irq);
|
||||
for (i = 0; i < ESPDMA_REGS; i++)
|
||||
qemu_get_be32s(f, &s->espdmaregs[i]);
|
||||
qemu_get_be32s(f, &s->ti_size);
|
||||
qemu_get_be32s(f, &s->ti_rptr);
|
||||
qemu_get_be32s(f, &s->ti_wptr);
|
||||
qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
|
||||
qemu_get_be32s(f, &s->dma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr)
|
||||
void esp_scsi_attach(void *opaque, BlockDriverState *bd, int id)
|
||||
{
|
||||
ESPState *s = (ESPState *)opaque;
|
||||
|
||||
if (id < 0) {
|
||||
for (id = 0; id < ESP_MAX_DEVS; id++) {
|
||||
if (s->scsi_dev[id] == NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id >= ESP_MAX_DEVS) {
|
||||
DPRINTF("Bad Device ID %d\n", id);
|
||||
return;
|
||||
}
|
||||
if (s->scsi_dev[id]) {
|
||||
DPRINTF("Destroying device %d\n", id);
|
||||
scsi_disk_destroy(s->scsi_dev[id]);
|
||||
}
|
||||
DPRINTF("Attaching block device %d\n", id);
|
||||
/* Command queueing is not implemented. */
|
||||
s->scsi_dev[id] = scsi_disk_init(bd, 0, esp_command_complete, s);
|
||||
}
|
||||
|
||||
void *esp_init(BlockDriverState **bd, uint32_t espaddr, void *dma_opaque)
|
||||
{
|
||||
ESPState *s;
|
||||
int esp_io_memory, espdma_io_memory;
|
||||
int esp_io_memory;
|
||||
|
||||
s = qemu_mallocz(sizeof(ESPState));
|
||||
if (!s)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
s->bd = bd;
|
||||
s->irq = irq;
|
||||
s->dma_opaque = dma_opaque;
|
||||
|
||||
esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s);
|
||||
cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory);
|
||||
|
||||
espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s);
|
||||
cpu_register_physical_memory(espdaddr, 16, espdma_io_memory);
|
||||
|
||||
esp_reset(s);
|
||||
|
||||
register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
|
||||
register_savevm("esp", espaddr, 2, esp_save, esp_load, s);
|
||||
qemu_register_reset(esp_reset, s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -66,7 +66,6 @@ typedef enum fdrive_type_t {
|
||||
|
||||
typedef enum fdrive_flags_t {
|
||||
FDRIVE_MOTOR_ON = 0x01, /* motor on/off */
|
||||
FDRIVE_REVALIDATE = 0x02, /* Revalidated */
|
||||
} fdrive_flags_t;
|
||||
|
||||
typedef enum fdisk_flags_t {
|
||||
@@ -236,7 +235,6 @@ static void fd_revalidate (fdrive_t *drv)
|
||||
int nb_heads, max_track, last_sect, ro;
|
||||
|
||||
FLOPPY_DPRINTF("revalidate\n");
|
||||
drv->drflags &= ~FDRIVE_REVALIDATE;
|
||||
if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
|
||||
ro = bdrv_is_read_only(drv->bs);
|
||||
bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
|
||||
@@ -291,7 +289,6 @@ static void fd_revalidate (fdrive_t *drv)
|
||||
drv->max_track = 0;
|
||||
drv->flags &= ~FDISK_DBL_SIDES;
|
||||
}
|
||||
drv->drflags |= FDRIVE_REVALIDATE;
|
||||
}
|
||||
|
||||
/* Motor control */
|
||||
@@ -488,19 +485,6 @@ static CPUWriteMemoryFunc *fdctrl_mem_write[3] = {
|
||||
fdctrl_write_mem,
|
||||
};
|
||||
|
||||
static void fd_change_cb (void *opaque)
|
||||
{
|
||||
fdrive_t *drv = opaque;
|
||||
|
||||
FLOPPY_DPRINTF("disk change\n");
|
||||
fd_revalidate(drv);
|
||||
#if 0
|
||||
fd_recalibrate(drv);
|
||||
fdctrl_reset_fifo(drv->fdctrl);
|
||||
fdctrl_raise_irq(drv->fdctrl, 0x20);
|
||||
#endif
|
||||
}
|
||||
|
||||
fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped,
|
||||
uint32_t io_base,
|
||||
BlockDriverState **fds)
|
||||
@@ -529,10 +513,6 @@ fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped,
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
fd_init(&fdctrl->drives[i], fds[i]);
|
||||
if (fds[i]) {
|
||||
bdrv_set_change_cb(fds[i],
|
||||
&fd_change_cb, &fdctrl->drives[i]);
|
||||
}
|
||||
}
|
||||
fdctrl_reset(fdctrl, 0);
|
||||
fdctrl->state = FD_CTRL_ACTIVE;
|
||||
@@ -760,18 +740,28 @@ static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value)
|
||||
// fdctrl.precomp = (value >> 2) & 0x07;
|
||||
}
|
||||
|
||||
static int fdctrl_media_changed(fdrive_t *drv)
|
||||
{
|
||||
int ret;
|
||||
if (!drv->bs)
|
||||
return 0;
|
||||
ret = bdrv_media_changed(drv->bs);
|
||||
if (ret) {
|
||||
fd_revalidate(drv);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Digital input register : 0x07 (read-only) */
|
||||
static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
|
||||
{
|
||||
uint32_t retval = 0;
|
||||
|
||||
if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE ||
|
||||
drv1(fdctrl)->drflags & FDRIVE_REVALIDATE)
|
||||
if (fdctrl_media_changed(drv0(fdctrl)) ||
|
||||
fdctrl_media_changed(drv1(fdctrl)))
|
||||
retval |= 0x80;
|
||||
if (retval != 0)
|
||||
FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
|
||||
drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
|
||||
drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -898,7 +888,7 @@ static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction)
|
||||
fdctrl->data_len = fdctrl->fifo[8];
|
||||
} else {
|
||||
int tmp;
|
||||
fdctrl->data_len = 128 << fdctrl->fifo[5];
|
||||
fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
|
||||
tmp = (cur_drv->last_sect - ks + 1);
|
||||
if (fdctrl->fifo[0] & 0x80)
|
||||
tmp += cur_drv->last_sect;
|
||||
@@ -1683,8 +1673,9 @@ enqueue:
|
||||
#else
|
||||
cur_drv->last_sect = fdctrl->fifo[3];
|
||||
#endif
|
||||
/* Bochs BIOS is buggy and don't send format informations
|
||||
* for each sector. So, pretend all's done right now...
|
||||
/* TODO: implement format using DMA expected by the Bochs BIOS
|
||||
* and Linux fdformat (read 3 bytes per sector via DMA and fill
|
||||
* the sector with the specified fill byte
|
||||
*/
|
||||
fdctrl->data_state &= ~FD_STATE_FORMAT;
|
||||
fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* QEMU Grackle (heathrow PPC) 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.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
typedef target_phys_addr_t pci_addr_t;
|
||||
#include "pci_host.h"
|
||||
|
||||
typedef PCIHostState GrackleState;
|
||||
|
||||
static void pci_grackle_config_writel (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
GrackleState *s = opaque;
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
val = bswap32(val);
|
||||
#endif
|
||||
s->config_reg = val;
|
||||
}
|
||||
|
||||
static uint32_t pci_grackle_config_readl (void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
GrackleState *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
val = s->config_reg;
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
val = bswap32(val);
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *pci_grackle_config_write[] = {
|
||||
&pci_grackle_config_writel,
|
||||
&pci_grackle_config_writel,
|
||||
&pci_grackle_config_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *pci_grackle_config_read[] = {
|
||||
&pci_grackle_config_readl,
|
||||
&pci_grackle_config_readl,
|
||||
&pci_grackle_config_readl,
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *pci_grackle_write[] = {
|
||||
&pci_host_data_writeb,
|
||||
&pci_host_data_writew,
|
||||
&pci_host_data_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *pci_grackle_read[] = {
|
||||
&pci_host_data_readb,
|
||||
&pci_host_data_readw,
|
||||
&pci_host_data_readl,
|
||||
};
|
||||
|
||||
/* Don't know if this matches real hardware, but it agrees with OHW. */
|
||||
static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||
{
|
||||
return (irq_num + (pci_dev->devfn >> 3)) & 3;
|
||||
}
|
||||
|
||||
static void pci_grackle_set_irq(void *pic, int irq_num, int level)
|
||||
{
|
||||
heathrow_pic_set_irq(pic, irq_num + 8, level);
|
||||
}
|
||||
|
||||
PCIBus *pci_grackle_init(uint32_t base, void *pic)
|
||||
{
|
||||
GrackleState *s;
|
||||
PCIDevice *d;
|
||||
int pci_mem_config, pci_mem_data;
|
||||
|
||||
s = qemu_mallocz(sizeof(GrackleState));
|
||||
s->bus = pci_register_bus(pci_grackle_set_irq, pci_grackle_map_irq,
|
||||
pic, 0, 0);
|
||||
|
||||
pci_mem_config = cpu_register_io_memory(0, pci_grackle_config_read,
|
||||
pci_grackle_config_write, s);
|
||||
pci_mem_data = cpu_register_io_memory(0, pci_grackle_read,
|
||||
pci_grackle_write, s);
|
||||
cpu_register_physical_memory(base, 0x1000, pci_mem_config);
|
||||
cpu_register_physical_memory(base + 0x00200000, 0x1000, pci_mem_data);
|
||||
d = pci_register_device(s->bus, "Grackle host bridge", sizeof(PCIDevice),
|
||||
0, NULL, NULL);
|
||||
d->config[0x00] = 0x57; // vendor_id
|
||||
d->config[0x01] = 0x10;
|
||||
d->config[0x02] = 0x02; // device_id
|
||||
d->config[0x03] = 0x00;
|
||||
d->config[0x08] = 0x00; // revision
|
||||
d->config[0x09] = 0x01;
|
||||
d->config[0x0a] = 0x00; // class_sub = host
|
||||
d->config[0x0b] = 0x06; // class_base = PCI_bridge
|
||||
d->config[0x0e] = 0x00; // header_type
|
||||
|
||||
d->config[0x18] = 0x00; // primary_bus
|
||||
d->config[0x19] = 0x01; // secondary_bus
|
||||
d->config[0x1a] = 0x00; // subordinate_bus
|
||||
d->config[0x1c] = 0x00;
|
||||
d->config[0x1d] = 0x00;
|
||||
|
||||
d->config[0x20] = 0x00; // memory_base
|
||||
d->config[0x21] = 0x00;
|
||||
d->config[0x22] = 0x01; // memory_limit
|
||||
d->config[0x23] = 0x00;
|
||||
|
||||
d->config[0x24] = 0x00; // prefetchable_memory_base
|
||||
d->config[0x25] = 0x00;
|
||||
d->config[0x26] = 0x00; // prefetchable_memory_limit
|
||||
d->config[0x27] = 0x00;
|
||||
|
||||
#if 0
|
||||
/* PCI2PCI bridge same values as PearPC - check this */
|
||||
d->config[0x00] = 0x11; // vendor_id
|
||||
d->config[0x01] = 0x10;
|
||||
d->config[0x02] = 0x26; // device_id
|
||||
d->config[0x03] = 0x00;
|
||||
d->config[0x08] = 0x02; // revision
|
||||
d->config[0x0a] = 0x04; // class_sub = pci2pci
|
||||
d->config[0x0b] = 0x06; // class_base = PCI_bridge
|
||||
d->config[0x0e] = 0x01; // header_type
|
||||
|
||||
d->config[0x18] = 0x0; // primary_bus
|
||||
d->config[0x19] = 0x1; // secondary_bus
|
||||
d->config[0x1a] = 0x1; // subordinate_bus
|
||||
d->config[0x1c] = 0x10; // io_base
|
||||
d->config[0x1d] = 0x20; // io_limit
|
||||
|
||||
d->config[0x20] = 0x80; // memory_base
|
||||
d->config[0x21] = 0x80;
|
||||
d->config[0x22] = 0x90; // memory_limit
|
||||
d->config[0x23] = 0x80;
|
||||
|
||||
d->config[0x24] = 0x00; // prefetchable_memory_base
|
||||
d->config[0x25] = 0x84;
|
||||
d->config[0x26] = 0x00; // prefetchable_memory_limit
|
||||
d->config[0x27] = 0x85;
|
||||
#endif
|
||||
return s->bus;
|
||||
}
|
||||
|
||||
+648
@@ -0,0 +1,648 @@
|
||||
/*
|
||||
* QEMU GT64120 PCI host
|
||||
*
|
||||
* Copyright (c) 2006,2007 Aurelien Jarno
|
||||
*
|
||||
* 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"
|
||||
typedef target_phys_addr_t pci_addr_t;
|
||||
#include "pci_host.h"
|
||||
|
||||
#define GT_REGS (0x1000 >> 2)
|
||||
|
||||
/* CPU Configuration */
|
||||
#define GT_CPU (0x000 >> 2)
|
||||
#define GT_MULTI (0x120 >> 2)
|
||||
|
||||
/* CPU Address Decode */
|
||||
#define GT_SCS10LD (0x008 >> 2)
|
||||
#define GT_SCS10HD (0x010 >> 2)
|
||||
#define GT_SCS32LD (0x018 >> 2)
|
||||
#define GT_SCS32HD (0x020 >> 2)
|
||||
#define GT_CS20LD (0x028 >> 2)
|
||||
#define GT_CS20HD (0x030 >> 2)
|
||||
#define GT_CS3BOOTLD (0x038 >> 2)
|
||||
#define GT_CS3BOOTHD (0x040 >> 2)
|
||||
#define GT_PCI0IOLD (0x048 >> 2)
|
||||
#define GT_PCI0IOHD (0x050 >> 2)
|
||||
#define GT_PCI0M0LD (0x058 >> 2)
|
||||
#define GT_PCI0M0HD (0x060 >> 2)
|
||||
#define GT_ISD (0x068 >> 2)
|
||||
|
||||
#define GT_PCI0M1LD (0x080 >> 2)
|
||||
#define GT_PCI0M1HD (0x088 >> 2)
|
||||
#define GT_PCI1IOLD (0x090 >> 2)
|
||||
#define GT_PCI1IOHD (0x098 >> 2)
|
||||
#define GT_PCI1M0LD (0x0a0 >> 2)
|
||||
#define GT_PCI1M0HD (0x0a8 >> 2)
|
||||
#define GT_PCI1M1LD (0x0b0 >> 2)
|
||||
#define GT_PCI1M1HD (0x0b8 >> 2)
|
||||
#define GT_PCI1M1LD (0x0b0 >> 2)
|
||||
#define GT_PCI1M1HD (0x0b8 >> 2)
|
||||
|
||||
#define GT_SCS10AR (0x0d0 >> 2)
|
||||
#define GT_SCS32AR (0x0d8 >> 2)
|
||||
#define GT_CS20R (0x0e0 >> 2)
|
||||
#define GT_CS3BOOTR (0x0e8 >> 2)
|
||||
|
||||
#define GT_PCI0IOREMAP (0x0f0 >> 2)
|
||||
#define GT_PCI0M0REMAP (0x0f8 >> 2)
|
||||
#define GT_PCI0M1REMAP (0x100 >> 2)
|
||||
#define GT_PCI1IOREMAP (0x108 >> 2)
|
||||
#define GT_PCI1M0REMAP (0x110 >> 2)
|
||||
#define GT_PCI1M1REMAP (0x118 >> 2)
|
||||
|
||||
/* CPU Error Report */
|
||||
#define GT_CPUERR_ADDRLO (0x070 >> 2)
|
||||
#define GT_CPUERR_ADDRHI (0x078 >> 2)
|
||||
#define GT_CPUERR_DATALO (0x128 >> 2) /* GT-64120A only */
|
||||
#define GT_CPUERR_DATAHI (0x130 >> 2) /* GT-64120A only */
|
||||
#define GT_CPUERR_PARITY (0x138 >> 2) /* GT-64120A only */
|
||||
|
||||
/* CPU Sync Barrier */
|
||||
#define GT_PCI0SYNC (0x0c0 >> 2)
|
||||
#define GT_PCI1SYNC (0x0c8 >> 2)
|
||||
|
||||
/* SDRAM and Device Address Decode */
|
||||
#define GT_SCS0LD (0x400 >> 2)
|
||||
#define GT_SCS0HD (0x404 >> 2)
|
||||
#define GT_SCS1LD (0x408 >> 2)
|
||||
#define GT_SCS1HD (0x40c >> 2)
|
||||
#define GT_SCS2LD (0x410 >> 2)
|
||||
#define GT_SCS2HD (0x414 >> 2)
|
||||
#define GT_SCS3LD (0x418 >> 2)
|
||||
#define GT_SCS3HD (0x41c >> 2)
|
||||
#define GT_CS0LD (0x420 >> 2)
|
||||
#define GT_CS0HD (0x424 >> 2)
|
||||
#define GT_CS1LD (0x428 >> 2)
|
||||
#define GT_CS1HD (0x42c >> 2)
|
||||
#define GT_CS2LD (0x430 >> 2)
|
||||
#define GT_CS2HD (0x434 >> 2)
|
||||
#define GT_CS3LD (0x438 >> 2)
|
||||
#define GT_CS3HD (0x43c >> 2)
|
||||
#define GT_BOOTLD (0x440 >> 2)
|
||||
#define GT_BOOTHD (0x444 >> 2)
|
||||
#define GT_ADERR (0x470 >> 2)
|
||||
|
||||
/* SDRAM Configuration */
|
||||
#define GT_SDRAM_CFG (0x448 >> 2)
|
||||
#define GT_SDRAM_OPMODE (0x474 >> 2)
|
||||
#define GT_SDRAM_BM (0x478 >> 2)
|
||||
#define GT_SDRAM_ADDRDECODE (0x47c >> 2)
|
||||
|
||||
/* SDRAM Parameters */
|
||||
#define GT_SDRAM_B0 (0x44c >> 2)
|
||||
#define GT_SDRAM_B1 (0x450 >> 2)
|
||||
#define GT_SDRAM_B2 (0x454 >> 2)
|
||||
#define GT_SDRAM_B3 (0x458 >> 2)
|
||||
|
||||
/* Device Parameters */
|
||||
#define GT_DEV_B0 (0x45c >> 2)
|
||||
#define GT_DEV_B1 (0x460 >> 2)
|
||||
#define GT_DEV_B2 (0x464 >> 2)
|
||||
#define GT_DEV_B3 (0x468 >> 2)
|
||||
#define GT_DEV_BOOT (0x46c >> 2)
|
||||
|
||||
/* ECC */
|
||||
#define GT_ECC_ERRDATALO (0x480 >> 2) /* GT-64120A only */
|
||||
#define GT_ECC_ERRDATAHI (0x484 >> 2) /* GT-64120A only */
|
||||
#define GT_ECC_MEM (0x488 >> 2) /* GT-64120A only */
|
||||
#define GT_ECC_CALC (0x48c >> 2) /* GT-64120A only */
|
||||
#define GT_ECC_ERRADDR (0x490 >> 2) /* GT-64120A only */
|
||||
|
||||
/* DMA Record */
|
||||
#define GT_DMA0_CNT (0x800 >> 2)
|
||||
#define GT_DMA1_CNT (0x804 >> 2)
|
||||
#define GT_DMA2_CNT (0x808 >> 2)
|
||||
#define GT_DMA3_CNT (0x80c >> 2)
|
||||
#define GT_DMA0_SA (0x810 >> 2)
|
||||
#define GT_DMA1_SA (0x814 >> 2)
|
||||
#define GT_DMA2_SA (0x818 >> 2)
|
||||
#define GT_DMA3_SA (0x81c >> 2)
|
||||
#define GT_DMA0_DA (0x820 >> 2)
|
||||
#define GT_DMA1_DA (0x824 >> 2)
|
||||
#define GT_DMA2_DA (0x828 >> 2)
|
||||
#define GT_DMA3_DA (0x82c >> 2)
|
||||
#define GT_DMA0_NEXT (0x830 >> 2)
|
||||
#define GT_DMA1_NEXT (0x834 >> 2)
|
||||
#define GT_DMA2_NEXT (0x838 >> 2)
|
||||
#define GT_DMA3_NEXT (0x83c >> 2)
|
||||
#define GT_DMA0_CUR (0x870 >> 2)
|
||||
#define GT_DMA1_CUR (0x874 >> 2)
|
||||
#define GT_DMA2_CUR (0x878 >> 2)
|
||||
#define GT_DMA3_CUR (0x87c >> 2)
|
||||
|
||||
/* DMA Channel Control */
|
||||
#define GT_DMA0_CTRL (0x840 >> 2)
|
||||
#define GT_DMA1_CTRL (0x844 >> 2)
|
||||
#define GT_DMA2_CTRL (0x848 >> 2)
|
||||
#define GT_DMA3_CTRL (0x84c >> 2)
|
||||
|
||||
/* DMA Arbiter */
|
||||
#define GT_DMA_ARB (0x860 >> 2)
|
||||
|
||||
/* Timer/Counter */
|
||||
#define GT_TC0 (0x850 >> 2)
|
||||
#define GT_TC1 (0x854 >> 2)
|
||||
#define GT_TC2 (0x858 >> 2)
|
||||
#define GT_TC3 (0x85c >> 2)
|
||||
#define GT_TC_CONTROL (0x864 >> 2)
|
||||
|
||||
/* PCI Internal */
|
||||
#define GT_PCI0_CMD (0xc00 >> 2)
|
||||
#define GT_PCI0_TOR (0xc04 >> 2)
|
||||
#define GT_PCI0_BS_SCS10 (0xc08 >> 2)
|
||||
#define GT_PCI0_BS_SCS32 (0xc0c >> 2)
|
||||
#define GT_PCI0_BS_CS20 (0xc10 >> 2)
|
||||
#define GT_PCI0_BS_CS3BT (0xc14 >> 2)
|
||||
#define GT_PCI1_IACK (0xc30 >> 2)
|
||||
#define GT_PCI0_IACK (0xc34 >> 2)
|
||||
#define GT_PCI0_BARE (0xc3c >> 2)
|
||||
#define GT_PCI0_PREFMBR (0xc40 >> 2)
|
||||
#define GT_PCI0_SCS10_BAR (0xc48 >> 2)
|
||||
#define GT_PCI0_SCS32_BAR (0xc4c >> 2)
|
||||
#define GT_PCI0_CS20_BAR (0xc50 >> 2)
|
||||
#define GT_PCI0_CS3BT_BAR (0xc54 >> 2)
|
||||
#define GT_PCI0_SSCS10_BAR (0xc58 >> 2)
|
||||
#define GT_PCI0_SSCS32_BAR (0xc5c >> 2)
|
||||
#define GT_PCI0_SCS3BT_BAR (0xc64 >> 2)
|
||||
#define GT_PCI1_CMD (0xc80 >> 2)
|
||||
#define GT_PCI1_TOR (0xc84 >> 2)
|
||||
#define GT_PCI1_BS_SCS10 (0xc88 >> 2)
|
||||
#define GT_PCI1_BS_SCS32 (0xc8c >> 2)
|
||||
#define GT_PCI1_BS_CS20 (0xc90 >> 2)
|
||||
#define GT_PCI1_BS_CS3BT (0xc94 >> 2)
|
||||
#define GT_PCI1_BARE (0xcbc >> 2)
|
||||
#define GT_PCI1_PREFMBR (0xcc0 >> 2)
|
||||
#define GT_PCI1_SCS10_BAR (0xcc8 >> 2)
|
||||
#define GT_PCI1_SCS32_BAR (0xccc >> 2)
|
||||
#define GT_PCI1_CS20_BAR (0xcd0 >> 2)
|
||||
#define GT_PCI1_CS3BT_BAR (0xcd4 >> 2)
|
||||
#define GT_PCI1_SSCS10_BAR (0xcd8 >> 2)
|
||||
#define GT_PCI1_SSCS32_BAR (0xcdc >> 2)
|
||||
#define GT_PCI1_SCS3BT_BAR (0xce4 >> 2)
|
||||
#define GT_PCI1_CFGADDR (0xcf0 >> 2)
|
||||
#define GT_PCI1_CFGDATA (0xcf4 >> 2)
|
||||
#define GT_PCI0_CFGADDR (0xcf8 >> 2)
|
||||
#define GT_PCI0_CFGDATA (0xcfc >> 2)
|
||||
|
||||
/* Interrupts */
|
||||
#define GT_INTRCAUSE (0xc18 >> 2)
|
||||
#define GT_INTRMASK (0xc1c >> 2)
|
||||
#define GT_PCI0_ICMASK (0xc24 >> 2)
|
||||
#define GT_PCI0_SERR0MASK (0xc28 >> 2)
|
||||
#define GT_CPU_INTSEL (0xc70 >> 2)
|
||||
#define GT_PCI0_INTSEL (0xc74 >> 2)
|
||||
#define GT_HINTRCAUSE (0xc98 >> 2)
|
||||
#define GT_HINTRMASK (0xc9c >> 2)
|
||||
#define GT_PCI0_HICMASK (0xca4 >> 2)
|
||||
#define GT_PCI1_SERR1MASK (0xca8 >> 2)
|
||||
|
||||
|
||||
typedef PCIHostState GT64120PCIState;
|
||||
|
||||
typedef struct GT64120State {
|
||||
GT64120PCIState *pci;
|
||||
uint32_t regs[GT_REGS];
|
||||
} GT64120State;
|
||||
|
||||
static void gt64120_pci_mapping(GT64120State *s)
|
||||
{
|
||||
target_phys_addr_t start, length;
|
||||
|
||||
/* Update IO mapping */
|
||||
if ((s->regs[GT_PCI0IOLD] & 0x7f) <= s->regs[GT_PCI0IOHD])
|
||||
{
|
||||
start = s->regs[GT_PCI0IOLD] << 21;
|
||||
length = ((s->regs[GT_PCI0IOHD] + 1) - (s->regs[GT_PCI0IOLD] & 0x7f)) << 21;
|
||||
isa_mmio_init(start, length);
|
||||
}
|
||||
}
|
||||
|
||||
static void gt64120_writel (void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val)
|
||||
{
|
||||
GT64120State *s = opaque;
|
||||
uint32_t saddr;
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
val = bswap32(val);
|
||||
#endif
|
||||
|
||||
saddr = (addr & 0xfff) >> 2;
|
||||
switch (saddr) {
|
||||
|
||||
/* CPU Configuration */
|
||||
case GT_CPU:
|
||||
s->regs[GT_CPU] = val;
|
||||
break;
|
||||
case GT_MULTI:
|
||||
/* Read-only register as only one GT64xxx is present on the CPU bus */
|
||||
break;
|
||||
|
||||
/* CPU Address Decode */
|
||||
case GT_PCI0IOLD:
|
||||
s->regs[GT_PCI0IOLD] = val & 0x00007fff;
|
||||
s->regs[GT_PCI0IOREMAP] = val & 0x000007ff;
|
||||
gt64120_pci_mapping(s);
|
||||
break;
|
||||
case GT_PCI0M0LD:
|
||||
s->regs[GT_PCI0M0LD] = val & 0x00007fff;
|
||||
s->regs[GT_PCI0M0REMAP] = val & 0x000007ff;
|
||||
gt64120_pci_mapping(s);
|
||||
break;
|
||||
case GT_PCI0M1LD:
|
||||
s->regs[GT_PCI0M1LD] = val & 0x00007fff;
|
||||
s->regs[GT_PCI0M1REMAP] = val & 0x000007ff;
|
||||
gt64120_pci_mapping(s);
|
||||
break;
|
||||
case GT_PCI1IOLD:
|
||||
s->regs[GT_PCI1IOLD] = val & 0x00007fff;
|
||||
s->regs[GT_PCI1IOREMAP] = val & 0x000007ff;
|
||||
gt64120_pci_mapping(s);
|
||||
break;
|
||||
case GT_PCI1M0LD:
|
||||
s->regs[GT_PCI1M0LD] = val & 0x00007fff;
|
||||
s->regs[GT_PCI1M0REMAP] = val & 0x000007ff;
|
||||
gt64120_pci_mapping(s);
|
||||
break;
|
||||
case GT_PCI1M1LD:
|
||||
s->regs[GT_PCI1M1LD] = val & 0x00007fff;
|
||||
s->regs[GT_PCI1M1REMAP] = val & 0x000007ff;
|
||||
gt64120_pci_mapping(s);
|
||||
break;
|
||||
case GT_PCI0IOHD:
|
||||
case GT_PCI0M0HD:
|
||||
case GT_PCI0M1HD:
|
||||
case GT_PCI1IOHD:
|
||||
case GT_PCI1M0HD:
|
||||
case GT_PCI1M1HD:
|
||||
s->regs[saddr] = val & 0x0000007f;
|
||||
gt64120_pci_mapping(s);
|
||||
break;
|
||||
case GT_PCI0IOREMAP:
|
||||
case GT_PCI0M0REMAP:
|
||||
case GT_PCI0M1REMAP:
|
||||
case GT_PCI1IOREMAP:
|
||||
case GT_PCI1M0REMAP:
|
||||
case GT_PCI1M1REMAP:
|
||||
s->regs[saddr] = val & 0x000007ff;
|
||||
gt64120_pci_mapping(s);
|
||||
break;
|
||||
|
||||
/* CPU Error Report */
|
||||
case GT_CPUERR_ADDRLO:
|
||||
case GT_CPUERR_ADDRHI:
|
||||
case GT_CPUERR_DATALO:
|
||||
case GT_CPUERR_DATAHI:
|
||||
case GT_CPUERR_PARITY:
|
||||
/* Read-only registers, do nothing */
|
||||
break;
|
||||
|
||||
/* CPU Sync Barrier */
|
||||
case GT_PCI0SYNC:
|
||||
case GT_PCI1SYNC:
|
||||
/* Read-only registers, do nothing */
|
||||
break;
|
||||
|
||||
/* ECC */
|
||||
case GT_ECC_ERRDATALO:
|
||||
case GT_ECC_ERRDATAHI:
|
||||
case GT_ECC_MEM:
|
||||
case GT_ECC_CALC:
|
||||
case GT_ECC_ERRADDR:
|
||||
/* Read-only registers, do nothing */
|
||||
break;
|
||||
|
||||
/* PCI Internal */
|
||||
case GT_PCI0_CMD:
|
||||
case GT_PCI1_CMD:
|
||||
s->regs[saddr] = val & 0x0401fc0f;
|
||||
break;
|
||||
case GT_PCI0_CFGADDR:
|
||||
s->pci->config_reg = val & 0x80fffffc;
|
||||
break;
|
||||
case GT_PCI0_CFGDATA:
|
||||
pci_host_data_writel(s->pci, 0, val);
|
||||
break;
|
||||
|
||||
/* SDRAM Parameters */
|
||||
case GT_SDRAM_B0:
|
||||
case GT_SDRAM_B1:
|
||||
case GT_SDRAM_B2:
|
||||
case GT_SDRAM_B3:
|
||||
/* We don't simulate electrical parameters of the SDRAM.
|
||||
Accept, but ignore the values. */
|
||||
s->regs[saddr] = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
#if 0
|
||||
printf ("gt64120_writel: Bad register offset 0x%x\n", (int)addr);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t gt64120_readl (void *opaque,
|
||||
target_phys_addr_t addr)
|
||||
{
|
||||
GT64120State *s = opaque;
|
||||
uint32_t val;
|
||||
uint32_t saddr;
|
||||
|
||||
val = 0;
|
||||
saddr = (addr & 0xfff) >> 2;
|
||||
|
||||
switch (saddr) {
|
||||
|
||||
/* CPU Configuration */
|
||||
case GT_MULTI:
|
||||
/* Only one GT64xxx is present on the CPU bus, return
|
||||
the initial value */
|
||||
val = s->regs[saddr];
|
||||
break;
|
||||
|
||||
/* CPU Error Report */
|
||||
case GT_CPUERR_ADDRLO:
|
||||
case GT_CPUERR_ADDRHI:
|
||||
case GT_CPUERR_DATALO:
|
||||
case GT_CPUERR_DATAHI:
|
||||
case GT_CPUERR_PARITY:
|
||||
/* Emulated memory has no error, always return the initial
|
||||
values */
|
||||
val = s->regs[saddr];
|
||||
break;
|
||||
|
||||
/* CPU Sync Barrier */
|
||||
case GT_PCI0SYNC:
|
||||
case GT_PCI1SYNC:
|
||||
/* Reading those register should empty all FIFO on the PCI
|
||||
bus, which are not emulated. The return value should be
|
||||
a random value that should be ignored. */
|
||||
val = 0xc000ffee;
|
||||
break;
|
||||
|
||||
/* ECC */
|
||||
case GT_ECC_ERRDATALO:
|
||||
case GT_ECC_ERRDATAHI:
|
||||
case GT_ECC_MEM:
|
||||
case GT_ECC_CALC:
|
||||
case GT_ECC_ERRADDR:
|
||||
/* Emulated memory has no error, always return the initial
|
||||
values */
|
||||
val = s->regs[saddr];
|
||||
break;
|
||||
|
||||
case GT_CPU:
|
||||
case GT_PCI0IOLD:
|
||||
case GT_PCI0M0LD:
|
||||
case GT_PCI0M1LD:
|
||||
case GT_PCI1IOLD:
|
||||
case GT_PCI1M0LD:
|
||||
case GT_PCI1M1LD:
|
||||
case GT_PCI0IOHD:
|
||||
case GT_PCI0M0HD:
|
||||
case GT_PCI0M1HD:
|
||||
case GT_PCI1IOHD:
|
||||
case GT_PCI1M0HD:
|
||||
case GT_PCI1M1HD:
|
||||
case GT_PCI0_CMD:
|
||||
case GT_PCI1_CMD:
|
||||
case GT_PCI0IOREMAP:
|
||||
case GT_PCI0M0REMAP:
|
||||
case GT_PCI0M1REMAP:
|
||||
case GT_PCI1IOREMAP:
|
||||
case GT_PCI1M0REMAP:
|
||||
case GT_PCI1M1REMAP:
|
||||
val = s->regs[saddr];
|
||||
break;
|
||||
case GT_PCI0_IACK:
|
||||
/* Read the IRQ number */
|
||||
val = pic_read_irq(isa_pic);
|
||||
break;
|
||||
|
||||
/* SDRAM Parameters */
|
||||
case GT_SDRAM_B0:
|
||||
case GT_SDRAM_B1:
|
||||
case GT_SDRAM_B2:
|
||||
case GT_SDRAM_B3:
|
||||
/* We don't simulate electrical parameters of the SDRAM.
|
||||
Just return the last written value. */
|
||||
val = s->regs[saddr];
|
||||
break;
|
||||
|
||||
/* PCI Internal */
|
||||
case GT_PCI0_CFGADDR:
|
||||
val = s->pci->config_reg;
|
||||
break;
|
||||
case GT_PCI0_CFGDATA:
|
||||
val = pci_host_data_readl(s->pci, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
val = s->regs[saddr];
|
||||
#if 0
|
||||
printf ("gt64120_readl: Bad register offset 0x%x\n", (int)addr);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
return bswap32(val);
|
||||
#else
|
||||
return val;
|
||||
#endif
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc *gt64120_write[] = {
|
||||
>64120_writel,
|
||||
>64120_writel,
|
||||
>64120_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc *gt64120_read[] = {
|
||||
>64120_readl,
|
||||
>64120_readl,
|
||||
>64120_readl,
|
||||
};
|
||||
|
||||
static int pci_gt64120_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||
{
|
||||
int slot;
|
||||
|
||||
slot = (pci_dev->devfn >> 3);
|
||||
|
||||
switch (slot) {
|
||||
/* PIIX4 USB */
|
||||
case 10:
|
||||
return 3;
|
||||
/* AMD 79C973 Ethernet */
|
||||
case 11:
|
||||
return 0;
|
||||
/* Crystal 4281 Sound */
|
||||
case 12:
|
||||
return 0;
|
||||
/* PCI slot 1 to 4 */
|
||||
case 18 ... 21:
|
||||
return ((slot - 18) + irq_num) & 0x03;
|
||||
/* Unknown device, don't do any translation */
|
||||
default:
|
||||
return irq_num;
|
||||
}
|
||||
}
|
||||
|
||||
extern PCIDevice *piix4_dev;
|
||||
static int pci_irq_levels[4];
|
||||
|
||||
static void pci_gt64120_set_irq(void *pic, int irq_num, int level)
|
||||
{
|
||||
int i, pic_irq, pic_level;
|
||||
|
||||
pci_irq_levels[irq_num] = level;
|
||||
|
||||
/* now we change the pic irq level according to the piix irq mappings */
|
||||
/* XXX: optimize */
|
||||
pic_irq = piix4_dev->config[0x60 + irq_num];
|
||||
if (pic_irq < 16) {
|
||||
/* The pic level is the logical OR of all the PCI irqs mapped
|
||||
to it */
|
||||
pic_level = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (pic_irq == piix4_dev->config[0x60 + i])
|
||||
pic_level |= pci_irq_levels[i];
|
||||
}
|
||||
pic_set_irq(pic_irq, pic_level);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gt64120_reset(void *opaque)
|
||||
{
|
||||
GT64120State *s = opaque;
|
||||
|
||||
/* CPU Configuration */
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
s->regs[GT_CPU] = 0x00000000;
|
||||
#else
|
||||
s->regs[GT_CPU] = 0x00000800;
|
||||
#endif
|
||||
s->regs[GT_MULTI] = 0x00000000;
|
||||
|
||||
/* CPU Address decode FIXME: not complete*/
|
||||
s->regs[GT_PCI0IOLD] = 0x00000080;
|
||||
s->regs[GT_PCI0IOHD] = 0x0000000f;
|
||||
s->regs[GT_PCI0M0LD] = 0x00000090;
|
||||
s->regs[GT_PCI0M0HD] = 0x0000001f;
|
||||
s->regs[GT_PCI0M1LD] = 0x00000790;
|
||||
s->regs[GT_PCI0M1HD] = 0x0000001f;
|
||||
s->regs[GT_PCI1IOLD] = 0x00000100;
|
||||
s->regs[GT_PCI1IOHD] = 0x0000000f;
|
||||
s->regs[GT_PCI1M0LD] = 0x00000110;
|
||||
s->regs[GT_PCI1M0HD] = 0x0000001f;
|
||||
s->regs[GT_PCI1M1LD] = 0x00000120;
|
||||
s->regs[GT_PCI1M1HD] = 0x0000002f;
|
||||
s->regs[GT_PCI0IOREMAP] = 0x00000080;
|
||||
s->regs[GT_PCI0M0REMAP] = 0x00000090;
|
||||
s->regs[GT_PCI0M1REMAP] = 0x00000790;
|
||||
s->regs[GT_PCI1IOREMAP] = 0x00000100;
|
||||
s->regs[GT_PCI1M0REMAP] = 0x00000110;
|
||||
s->regs[GT_PCI1M1REMAP] = 0x00000120;
|
||||
|
||||
/* CPU Error Report */
|
||||
s->regs[GT_CPUERR_ADDRLO] = 0x00000000;
|
||||
s->regs[GT_CPUERR_ADDRHI] = 0x00000000;
|
||||
s->regs[GT_CPUERR_DATALO] = 0xffffffff;
|
||||
s->regs[GT_CPUERR_DATAHI] = 0xffffffff;
|
||||
s->regs[GT_CPUERR_PARITY] = 0x000000ff;
|
||||
|
||||
/* ECC */
|
||||
s->regs[GT_ECC_ERRDATALO] = 0x00000000;
|
||||
s->regs[GT_ECC_ERRDATAHI] = 0x00000000;
|
||||
s->regs[GT_ECC_MEM] = 0x00000000;
|
||||
s->regs[GT_ECC_CALC] = 0x00000000;
|
||||
s->regs[GT_ECC_ERRADDR] = 0x00000000;
|
||||
|
||||
/* SDRAM Parameters */
|
||||
s->regs[GT_SDRAM_B0] = 0x00000005;
|
||||
s->regs[GT_SDRAM_B1] = 0x00000005;
|
||||
s->regs[GT_SDRAM_B2] = 0x00000005;
|
||||
s->regs[GT_SDRAM_B3] = 0x00000005;
|
||||
|
||||
/* PCI Internal FIXME: not complete*/
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
s->regs[GT_PCI0_CMD] = 0x00000000;
|
||||
s->regs[GT_PCI1_CMD] = 0x00000000;
|
||||
#else
|
||||
s->regs[GT_PCI0_CMD] = 0x00010001;
|
||||
s->regs[GT_PCI1_CMD] = 0x00010001;
|
||||
#endif
|
||||
s->regs[GT_PCI0_IACK] = 0x00000000;
|
||||
s->regs[GT_PCI1_IACK] = 0x00000000;
|
||||
|
||||
gt64120_pci_mapping(s);
|
||||
}
|
||||
|
||||
PCIBus *pci_gt64120_init(void *pic)
|
||||
{
|
||||
GT64120State *s;
|
||||
PCIDevice *d;
|
||||
int gt64120;
|
||||
|
||||
s = qemu_mallocz(sizeof(GT64120State));
|
||||
s->pci = qemu_mallocz(sizeof(GT64120PCIState));
|
||||
gt64120_reset(s);
|
||||
|
||||
s->pci->bus = pci_register_bus(pci_gt64120_set_irq, pci_gt64120_map_irq,
|
||||
pic, 144, 4);
|
||||
|
||||
gt64120 = cpu_register_io_memory(0, gt64120_read,
|
||||
gt64120_write, s);
|
||||
cpu_register_physical_memory(0x1be00000LL, 0x1000, gt64120);
|
||||
|
||||
d = pci_register_device(s->pci->bus, "GT64120 PCI Bus", sizeof(PCIDevice),
|
||||
0, NULL, NULL);
|
||||
|
||||
d->config[0x00] = 0xab; // vendor_id
|
||||
d->config[0x01] = 0x11;
|
||||
d->config[0x02] = 0x46; // device_id
|
||||
d->config[0x03] = 0x20;
|
||||
d->config[0x04] = 0x06;
|
||||
d->config[0x05] = 0x00;
|
||||
d->config[0x06] = 0x80;
|
||||
d->config[0x07] = 0xa2;
|
||||
d->config[0x08] = 0x10;
|
||||
d->config[0x09] = 0x00;
|
||||
d->config[0x0A] = 0x80;
|
||||
d->config[0x0B] = 0x05;
|
||||
d->config[0x0C] = 0x08;
|
||||
d->config[0x0D] = 0x40;
|
||||
d->config[0x0E] = 0x00;
|
||||
d->config[0x0F] = 0x00;
|
||||
d->config[0x17] = 0x08;
|
||||
d->config[0x1B] = 0x1c;
|
||||
d->config[0x1F] = 0x1f;
|
||||
d->config[0x23] = 0x14;
|
||||
d->config[0x27] = 0x14;
|
||||
d->config[0x3D] = 0x01;
|
||||
|
||||
return s->pci->bus;
|
||||
}
|
||||
Loaded 100 of 331 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user