Compare commits
2444 Commits
catching_e
...
b3e0b5e139
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3e0b5e139 | ||
|
|
33b7979468 | ||
|
|
a539c3b814 | ||
|
|
1338660a81 | ||
|
|
21b2c1e198 | ||
|
|
f2966a5348 | ||
|
|
687226e9c1 | ||
|
|
7b54d2cde1 | ||
|
|
b45bd8fada | ||
|
|
de8054095c | ||
|
|
7c795ebdd1 | ||
|
|
4e4c30deaa | ||
|
|
fcd3eca133 | ||
|
|
a03eaffe98 | ||
|
|
ccfa6f500d | ||
|
|
67b3ac65dc | ||
|
|
7262bf0f90 | ||
|
|
9894147b30 | ||
|
|
5b4cb532e8 | ||
|
|
9e325de9c7 | ||
|
|
ad888bcb9b | ||
|
|
7b9891b2b5 | ||
|
|
359f457e53 | ||
|
|
336391b9c9 | ||
|
|
2de7654f05 | ||
|
|
1b0c9752e6 | ||
|
|
30bbcb94cf | ||
|
|
187839ab53 | ||
|
|
4e51284661 | ||
|
|
3c8d567c1e | ||
|
|
8b702b732e | ||
|
|
02adcf94ee | ||
|
|
d5e981bca9 | ||
|
|
2edf0221a3 | ||
|
|
32f629fb2d | ||
|
|
98f616964d | ||
|
|
557da037c7 | ||
|
|
5a849b05d6 | ||
|
|
9da248032f | ||
|
|
52b31ded4d | ||
|
|
88fd5aabdd | ||
|
|
411d42ac8d | ||
|
|
ae810ce83d | ||
|
|
d4daf93050 | ||
|
|
c0fc0bed9e | ||
|
|
9753695744 | ||
|
|
725085abea | ||
|
|
98cf0d4850 | ||
|
|
b9b1ae8c08 | ||
|
|
cf91244de6 | ||
|
|
ffd0555a5a | ||
|
|
0e9f50d773 | ||
|
|
9345886d44 | ||
|
|
b1bf2e51d6 | ||
|
|
176d4a7783 | ||
|
|
b69f96e0ae | ||
|
|
450afac4a1 | ||
|
|
fba3bedf39 | ||
|
|
8b341c96c1 | ||
|
|
7f1f658358 | ||
|
|
0417fd22ef | ||
|
|
69a498c6a3 | ||
|
|
c9307a422f | ||
|
|
d9de86f6df | ||
|
|
8971334f57 | ||
|
|
ab232d3051 | ||
|
|
4de9527ef8 | ||
|
|
c46922678e | ||
|
|
146e694d08 | ||
|
|
b797cc805f | ||
|
|
63cd95b612 | ||
|
|
28753d6a18 | ||
|
|
68698fde20 | ||
|
|
3bb6671dd9 | ||
|
|
a16900efe4 | ||
|
|
55b5cbaba3 | ||
|
|
e0ab3f257d | ||
|
|
2b6fc68ad1 | ||
|
|
e2f2f4c533 | ||
|
|
6a27ab2c2d | ||
|
|
4d3b246ef6 | ||
|
|
e037dcfdae | ||
|
|
c87cff0cdd | ||
|
|
e4dd739d72 | ||
|
|
9a6039237d | ||
|
|
94f1c4503a | ||
|
|
7095876dca | ||
|
|
6d4c5c5c4e | ||
|
|
134e938c9d | ||
|
|
d10dbc19a4 | ||
|
|
64cc41f49c | ||
|
|
33d73cbdbc | ||
|
|
807fc91bf8 | ||
|
|
d67f9ee8ce | ||
|
|
0a9024f1bd | ||
|
|
b991afebac | ||
|
|
7927beeffa | ||
|
|
cd348bbe3c | ||
|
|
d2ee259bfd | ||
|
|
9150885e82 | ||
|
|
fcf63e42aa | ||
|
|
087a464ed7 | ||
|
|
96dc9ed668 | ||
|
|
e6486aad6d | ||
|
|
f578336cd7 | ||
|
|
1ba8a4b012 | ||
|
|
69a0f31747 | ||
|
|
e4c25a3612 | ||
|
|
978fc6b174 | ||
|
|
f192d8ae4e | ||
|
|
1e108fba64 | ||
|
|
9c05cc9bd5 | ||
|
|
4d7b78069f | ||
|
|
56d66b6b24 | ||
|
|
ed38ea1aa0 | ||
|
|
d8204b05cc | ||
|
|
5f00ca206d | ||
|
|
52856ea30e | ||
|
|
a611ca7bfe | ||
|
|
9b0a0a3fbb | ||
|
|
54010c3653 | ||
|
|
b8be0c5a5d | ||
|
|
9271bb104c | ||
|
|
5b5e541b1e | ||
|
|
6d20ba47fa | ||
|
|
a3bbd5ab55 | ||
|
|
88e473433b | ||
|
|
3527693aeb | ||
|
|
4376233ec3 | ||
|
|
1f6402f746 | ||
|
|
3556211904 | ||
|
|
c31dcb48e5 | ||
|
|
1a0254b977 | ||
|
|
05dadc1302 | ||
|
|
6041d119db | ||
|
|
258426b787 | ||
|
|
8e6c4caffc | ||
|
|
59f84c2a45 | ||
|
|
6bc593be79 | ||
|
|
3d337e5f80 | ||
|
|
9691bd5dc8 | ||
|
|
c0fbb10907 | ||
|
|
f9f1b8e7b9 | ||
|
|
bde9fd0262 | ||
|
|
bd361ee25a | ||
|
|
f13ccee48b | ||
|
|
e4316a8a16 | ||
|
|
3eeb25c946 | ||
|
|
813f434ecd | ||
|
|
71f5d220ee | ||
|
|
71e5ea0268 | ||
|
|
da82fe8a0f | ||
|
|
2b444d3262 | ||
|
|
b118828956 | ||
|
|
4595872c6b | ||
|
|
1dced13b29 | ||
|
|
4b023bb432 | ||
|
|
b1b35833d6 | ||
|
|
34120a0409 | ||
|
|
6367e931e5 | ||
|
|
02fe09d56f | ||
|
|
f4ab296b8b | ||
|
|
db4068826b | ||
|
|
9b1791a48d | ||
|
|
61d074cc6c | ||
|
|
417c22556d | ||
|
|
5b4d04e337 | ||
|
|
684ef64767 | ||
|
|
87d0ce5fa2 | ||
|
|
5b67cb05ff | ||
|
|
81b7829657 | ||
|
|
1ddeb129ac | ||
|
|
53d181d57b | ||
|
|
b634a24f4b | ||
|
|
4ef59648be | ||
|
|
73628c1d05 | ||
|
|
2e3230eca2 | ||
|
|
63dc179cc7 | ||
|
|
211544af00 | ||
|
|
1ff26bb4b6 | ||
|
|
e2a4464835 | ||
|
|
f0fe3929ab | ||
|
|
c728cf8225 | ||
|
|
f38c11ae8e | ||
|
|
a1b195ede0 | ||
|
|
cfd31a977d | ||
|
|
06dd5edb3f | ||
|
|
685f55dd31 | ||
|
|
0b6b053c54 | ||
|
|
e0464fbd44 | ||
|
|
5e88b86d1e | ||
|
|
e6b507e6d2 | ||
|
|
5baf50a5b4 | ||
|
|
5d63300512 | ||
|
|
739d9e1a3c | ||
|
|
8a1063df5f | ||
|
|
d2c6b281c8 | ||
|
|
3900ddeb2c | ||
|
|
a7f4647e73 | ||
|
|
a7cfc562e9 | ||
|
|
5f5320f8e0 | ||
|
|
6f6e6698c4 | ||
|
|
1d0e7950fd | ||
|
|
dd4e872f82 | ||
|
|
d8ad83528d | ||
|
|
7c1b57a663 | ||
|
|
559b2a195d | ||
|
|
e56c635c13 | ||
|
|
6cf6e32ee9 | ||
|
|
ac94309114 | ||
|
|
3183dc92e9 | ||
|
|
19150682df | ||
|
|
912566167c | ||
|
|
82f205956a | ||
|
|
d456410da2 | ||
|
|
48c3532c40 | ||
|
|
0c79f42e39 | ||
|
|
399eee2e19 | ||
|
|
2a338cf045 | ||
|
|
31eb517b4d | ||
|
|
2844d0fc8c | ||
|
|
b63ce2dc3c | ||
|
|
184db36e7c | ||
|
|
a108367d71 | ||
|
|
c1639cb9c2 | ||
|
|
2f1918bf71 | ||
|
|
7a44cd1f60 | ||
|
|
15421d988a | ||
|
|
1e2ae0e2ee | ||
|
|
56ec24cbe1 | ||
|
|
c786f14b8a | ||
|
|
4abdc3ea43 | ||
|
|
f6546321a3 | ||
|
|
98514773d0 | ||
|
|
b0884bd879 | ||
|
|
633bc2e032 | ||
|
|
745d12d7f5 | ||
|
|
3fef39211e | ||
|
|
f905e7aa2b | ||
|
|
d17048e054 | ||
|
|
e0db01a59d | ||
|
|
cca2886907 | ||
|
|
77221b833b | ||
|
|
db64d2876d | ||
|
|
bde26a617e | ||
|
|
d70afb7ecc | ||
|
|
cc100920d8 | ||
|
|
209fd7c312 | ||
|
|
a86304d932 | ||
|
|
7f5859f8ee | ||
|
|
09aa0347d1 | ||
|
|
bbc59d23e8 | ||
|
|
c2c6d64452 | ||
|
|
1c66190ac2 | ||
|
|
90a92f04a6 | ||
|
|
7de8c2aa37 | ||
|
|
3f5c404609 | ||
|
|
44eaeca650 | ||
|
|
d52116d446 | ||
|
|
0268d6b9c6 | ||
|
|
001231b53f | ||
|
|
a366cdf277 | ||
|
|
80be2f0a9e | ||
|
|
4337df6236 | ||
|
|
f0ab5e01c7 | ||
|
|
f339bca0bf | ||
|
|
a95c6d1745 | ||
|
|
1030e82621 | ||
|
|
ea70ed128c | ||
|
|
5b5e72425e | ||
|
|
7332d3d332 | ||
|
|
20b306d7a4 | ||
|
|
dee0ee61fa | ||
|
|
c689ecf810 | ||
|
|
a9c8b51c0e | ||
|
|
a9aa4219e6 | ||
|
|
fe387b5fa6 | ||
|
|
567ace4f8d | ||
|
|
00b068a6eb | ||
|
|
cfe743ad9e | ||
|
|
81d4f49f15 | ||
|
|
9440629176 | ||
|
|
096c044b4f | ||
|
|
987f8e7f17 | ||
|
|
6060898753 | ||
|
|
c8688719ee | ||
|
|
fcc20806a7 | ||
|
|
3b6064bbe0 | ||
|
|
ba7ec89c35 | ||
|
|
6680ecbfeb | ||
|
|
9c7c62754b | ||
|
|
9c5890d409 | ||
|
|
f927a863f2 | ||
|
|
4d178a2a35 | ||
|
|
dabc17bc39 | ||
|
|
1dd8ea1dba | ||
|
|
9d079e6d3e | ||
|
|
d07b3d089e | ||
|
|
cc68aaf453 | ||
|
|
410f1df3da | ||
|
|
d915443930 | ||
|
|
ff512edb54 | ||
|
|
aa389dae4b | ||
|
|
4905a41af0 | ||
|
|
ad89055521 | ||
|
|
5d0f200b61 | ||
|
|
2f9836b407 | ||
|
|
6dc78627f7 | ||
|
|
ef19dbce1d | ||
|
|
c0f111aaee | ||
|
|
1662aaaea7 | ||
|
|
1f27d18427 | ||
|
|
3c3a37a48c | ||
|
|
87794f2240 | ||
|
|
341e05b843 | ||
|
|
01975be7ac | ||
|
|
eeac9e880c | ||
|
|
2eb5147675 | ||
|
|
b05c07d21c | ||
|
|
385d0e1306 | ||
|
|
1c9b5edd6a | ||
|
|
eab9b70c68 | ||
|
|
ad8e0b4783 | ||
|
|
c2d3f2704c | ||
|
|
9025695ebf | ||
|
|
5a1b841711 | ||
|
|
21c5b62c38 | ||
|
|
884247cb1d | ||
|
|
d78dd1582b | ||
|
|
1a750f5374 | ||
|
|
afeed7477b | ||
|
|
611698ec5f | ||
|
|
c34db52262 | ||
|
|
71870a05b0 | ||
|
|
1519a17803 | ||
|
|
d8dc4f5395 | ||
|
|
b13f261770 | ||
|
|
5217bc27b7 | ||
|
|
f365ade976 | ||
|
|
f6777e621d | ||
|
|
ee893a7ba0 | ||
|
|
58eca98ae1 | ||
|
|
f56b4a0a32 | ||
|
|
c393e4442d | ||
|
|
7954a9f424 | ||
|
|
0873c3ae1d | ||
|
|
daafb244d9 | ||
|
|
b8272b53cf | ||
|
|
7ce356f99d | ||
|
|
5b2c60ece3 | ||
|
|
c1af4e31b1 | ||
|
|
c7eced8b49 | ||
|
|
6b241de81b | ||
|
|
09e0c5915b | ||
|
|
64c1130908 | ||
|
|
b24122a604 | ||
|
|
a26eeae82e | ||
|
|
0d42178666 | ||
|
|
05acd376e9 | ||
|
|
0d40ee3750 | ||
|
|
ce51e833a8 | ||
|
|
c4f8477310 | ||
|
|
44f51402f7 | ||
|
|
5c750985a6 | ||
|
|
d6bf1b2271 | ||
|
|
7eb1426e1f | ||
|
|
ebbc6381d7 | ||
|
|
456ed398e8 | ||
|
|
195d40080a | ||
|
|
18f7cb16b9 | ||
|
|
1095dc33f4 | ||
|
|
a41d3236b8 | ||
|
|
53e3578e34 | ||
|
|
ac8f0175a7 | ||
|
|
0fa31a0e15 | ||
|
|
5ff1aab7b5 | ||
|
|
76b6c445e4 | ||
|
|
69917e234c | ||
|
|
3281df3a41 | ||
|
|
96ab77fd1e | ||
|
|
9d7c0ead07 | ||
|
|
adaaa3238a | ||
|
|
d8c27c7fcc | ||
|
|
990169b631 | ||
|
|
9b02a5e77a | ||
|
|
874f79a8f9 | ||
|
|
faae44a477 | ||
|
|
b16c0c861f | ||
|
|
a8a111c6ee | ||
|
|
5155e18ba1 | ||
|
|
ff5ac93908 | ||
|
|
d3e8714a80 | ||
|
|
5654201a09 | ||
|
|
279ca5dad7 | ||
|
|
ff10c51d11 | ||
|
|
daf15c993e | ||
|
|
fbff83e498 | ||
|
|
1bc5422049 | ||
|
|
f5c111f649 | ||
|
|
9dd6588edf | ||
|
|
526c7a3dca | ||
|
|
893c1cf447 | ||
|
|
f57bded691 | ||
|
|
f30ebe878c | ||
|
|
c987148dc2 | ||
|
|
c430d1d113 | ||
|
|
68212e8bc4 | ||
|
|
ae72448f89 | ||
|
|
6622e583da | ||
|
|
8e506fc34a | ||
|
|
38027670fd | ||
|
|
7d7130bc0f | ||
|
|
f881645991 | ||
|
|
3e87916b6c | ||
|
|
dbe43c203d | ||
|
|
4472eed23c | ||
|
|
0fe6d6aaec | ||
|
|
282ed23a99 | ||
|
|
559f1fbecd | ||
|
|
575547963d | ||
|
|
803d2e6c7a | ||
|
|
ed14ee779d | ||
|
|
fcabba0751 | ||
|
|
21516fe821 | ||
|
|
841b9deea3 | ||
|
|
27ab4518ac | ||
|
|
c1e6a66e35 | ||
|
|
d8eff6ba3e | ||
|
|
eb1e22582b | ||
|
|
f9fb942ba8 | ||
|
|
0edd05ccac | ||
|
|
6fbcd8527a | ||
|
|
906a0580d0 | ||
|
|
6ac7b5bb9d | ||
|
|
dcef701d04 | ||
|
|
3c0060dacf | ||
|
|
4653711620 | ||
|
|
7f36fe6693 | ||
|
|
8270e1e12e | ||
|
|
ff9a7c10d3 | ||
|
|
c2d6645392 | ||
|
|
8b2ee6a4b8 | ||
|
|
4942a8e50e | ||
|
|
782d5d6d5e | ||
|
|
218a2754c7 | ||
|
|
c7743a23ed | ||
|
|
afb20aae74 | ||
|
|
27b1528a65 | ||
|
|
03853d15f4 | ||
|
|
963eb3e659 | ||
|
|
8eba8dab7c | ||
|
|
47292beaad | ||
|
|
90c4e2bb93 | ||
|
|
a46c20ef93 | ||
|
|
762beaa59e | ||
|
|
b0f0a4c5e2 | ||
|
|
b956c0c591 | ||
|
|
8cb4cf16df | ||
|
|
7fb46ee069 | ||
|
|
54ca8bbe67 | ||
|
|
bbf3c03cce | ||
|
|
732014c148 | ||
|
|
7992ce6a21 | ||
|
|
34a3066cae | ||
|
|
248bc1cf05 | ||
|
|
5ef39591f6 | ||
|
|
b070fa8ee6 | ||
|
|
01e254db8c | ||
|
|
c85e40b9ab | ||
|
|
c7deb06fe8 | ||
|
|
178c90ad74 | ||
|
|
bf4f881079 | ||
|
|
5d5e51400e | ||
|
|
6d02874049 | ||
|
|
d3f5bb5fcb | ||
|
|
f2ca9f282a | ||
|
|
fc81c27fec | ||
|
|
02b80acbd7 | ||
|
|
2d6108dc2f | ||
|
|
ff43048585 | ||
|
|
3f1a451e2b | ||
|
|
51f401a56c | ||
|
|
8bcfc7043f | ||
|
|
c515590c1b | ||
|
|
3315bfe2f6 | ||
|
|
ec4863ff93 | ||
|
|
5577f19859 | ||
|
|
6a17e8a7f4 | ||
|
|
7ac5fc903b | ||
|
|
05b22bfa69 | ||
|
|
bdf6f14b9e | ||
|
|
f87cc129fc | ||
|
|
3f29b7b582 | ||
|
|
42c550fcde | ||
|
|
e2ea67a826 | ||
|
|
a9df1f2ec2 | ||
|
|
a0368be408 | ||
|
|
5dbdd8c11d | ||
|
|
2ce8169e64 | ||
|
|
be2e130de9 | ||
|
|
616376fbd7 | ||
|
|
0779767355 | ||
|
|
987d695ed2 | ||
|
|
a8a6b725cf | ||
|
|
15ae425a6b | ||
|
|
9d381723ed | ||
|
|
88f32ffcb5 | ||
|
|
54a0c2b0ad | ||
|
|
16cc7048b0 | ||
|
|
d4034ee3d9 | ||
|
|
6e6d9cbe05 | ||
|
|
3c75f84581 | ||
|
|
563689dba5 | ||
|
|
6d648457b1 | ||
|
|
00986e8876 | ||
|
|
c5eeea29e2 | ||
|
|
18930ccad7 | ||
|
|
cd8e139ab0 | ||
|
|
27ac0e1acc | ||
|
|
c242563a13 | ||
|
|
27b5f5ee83 | ||
|
|
749d9d3793 | ||
|
|
904d203f36 | ||
|
|
611bb91141 | ||
|
|
1839f98be2 | ||
|
|
ec0d60e6aa | ||
|
|
0cb7977025 | ||
|
|
a1b496fab5 | ||
|
|
16b9e30d5f | ||
|
|
3efca6dbbc | ||
|
|
23e8cb3b5d | ||
|
|
70fa363658 | ||
|
|
d697c6caf2 | ||
|
|
b7416af316 | ||
|
|
aa33626a03 | ||
|
|
761b5de93d | ||
|
|
bb39724f72 | ||
|
|
a28ad5eba2 | ||
|
|
247965c1b7 | ||
|
|
f60883071e | ||
|
|
3ca2ef760f | ||
|
|
f779303472 | ||
|
|
fad609f744 | ||
|
|
478b4cb808 | ||
|
|
63d9828213 | ||
|
|
c20d7b534d | ||
|
|
b9cc0a47c6 | ||
|
|
4d4a2e5ae8 | ||
|
|
36c4e6a7fe | ||
|
|
d2daf9dc48 | ||
|
|
5a86a715c9 | ||
|
|
aa227c7dc6 | ||
|
|
ee1ca9fa61 | ||
|
|
22255d12a9 | ||
|
|
68d12a7839 | ||
|
|
a3bb0d3469 | ||
|
|
924da318c0 | ||
|
|
ceb52ac411 | ||
|
|
b6ff79ac32 | ||
|
|
85d97bd362 | ||
|
|
d9f3558b34 | ||
|
|
3ef243f342 | ||
|
|
5f5fdaf902 | ||
|
|
009d0ceaa5 | ||
|
|
dce5c9825b | ||
|
|
b5a1360a72 | ||
|
|
183872e362 | ||
|
|
9279c75160 | ||
|
|
f692c5b377 | ||
|
|
4a9ea6c503 | ||
|
|
e6c510cba6 | ||
|
|
d36c041a9c | ||
|
|
f6416c1e6b | ||
|
|
e8f5ee95c6 | ||
|
|
a081faf3cc | ||
|
|
8f023f8e70 | ||
|
|
165a83efae | ||
|
|
a6be039bdb | ||
|
|
6b0f9290ea | ||
|
|
1ba70ef398 | ||
|
|
c39b1e2b3c | ||
|
|
a1bb75850d | ||
|
|
19bf93a59d | ||
|
|
106be64a1a | ||
|
|
8fd8e42490 | ||
|
|
656b1c1ab2 | ||
|
|
668c903c02 | ||
|
|
307916d1fe | ||
|
|
caecc2c98e | ||
|
|
b17b3d40da | ||
|
|
85b7d4b5c1 | ||
|
|
c9fae31766 | ||
|
|
7212ffaa83 | ||
|
|
a65bf32d57 | ||
|
|
5184a2f7ea | ||
|
|
fb8b03e82d | ||
|
|
4213609b1e | ||
|
|
114140ab7c | ||
|
|
d007af4811 | ||
|
|
1a63bbe28d | ||
|
|
80136cc047 | ||
|
|
68d59147e7 | ||
|
|
3ac8291712 | ||
|
|
8ff1aeb96d | ||
|
|
685b8a94ac | ||
|
|
00ebabddc2 | ||
|
|
3c0e8dc56a | ||
|
|
b978c72e03 | ||
|
|
f198d45a59 | ||
|
|
10d84faa92 | ||
|
|
9b61e061a0 | ||
|
|
e159804492 | ||
|
|
0425bd3c81 | ||
|
|
e80864b64d | ||
|
|
90b5a958d3 | ||
|
|
30650ab52b | ||
|
|
3cdf51aaca | ||
|
|
bff9944b02 | ||
|
|
3650a48c51 | ||
|
|
57855e23cb | ||
|
|
277368537b | ||
|
|
3c070c6dc0 | ||
|
|
6233747299 | ||
|
|
96edb8909f | ||
|
|
4717834ad0 | ||
|
|
06d235f8af | ||
|
|
36a0ae4737 | ||
|
|
4cde35b674 | ||
|
|
68b2b7737b | ||
|
|
3096baf462 | ||
|
|
f0443b7bfe | ||
|
|
e407ec0ee0 | ||
|
|
ed339ee2ce | ||
|
|
1a52165e25 | ||
|
|
de07416085 | ||
|
|
3c723b428e | ||
|
|
580f58f7ab | ||
|
|
6e2edf03a9 | ||
|
|
6f1f872158 | ||
|
|
b7eb093b42 | ||
|
|
797e87e169 | ||
|
|
9985e4a699 | ||
|
|
64501e46f5 | ||
|
|
ee33868068 | ||
|
|
4540a2e725 | ||
|
|
0b15229a5e | ||
|
|
7f9ff717b4 | ||
|
|
aa895e271d | ||
|
|
6194a43724 | ||
|
|
ec90a6a958 | ||
|
|
9980989f7f | ||
|
|
e1228d8708 | ||
|
|
c081c3f51f | ||
|
|
0fa30e24c7 | ||
|
|
a3dcc3095e | ||
|
|
433fd26173 | ||
|
|
59a7f96ae8 | ||
|
|
788d6a7e22 | ||
|
|
1f8a388bdc | ||
|
|
524fa5330f | ||
|
|
c7c7f64f79 | ||
|
|
fddab6967d | ||
|
|
5e37a0ed5d | ||
|
|
5caf136bc2 | ||
|
|
2cb6a0ffe7 | ||
|
|
fbaa641841 | ||
|
|
1a5d91be4b | ||
|
|
ee09a70d40 | ||
|
|
a8c85afeb0 | ||
|
|
70b2f07694 | ||
|
|
a79cea1cd6 | ||
|
|
052fc405ad | ||
|
|
36e8c16a2c | ||
|
|
199c3b467c | ||
|
|
516121b698 | ||
|
|
389b97f13e | ||
|
|
1adfacf21d | ||
|
|
a7a57578e9 | ||
|
|
0b957688b7 | ||
|
|
ed3eaacb44 | ||
|
|
355d3f6681 | ||
|
|
e46d1f3455 | ||
|
|
cf1e447d9c | ||
|
|
b2b58278be | ||
|
|
a947956616 | ||
|
|
9b3ab1bba5 | ||
|
|
cbfd467011 | ||
|
|
a237cd2777 | ||
|
|
f13fc3062a | ||
|
|
392cbc2d41 | ||
|
|
f8f9a02d56 | ||
|
|
47969a618e | ||
|
|
a96a5ca8d7 | ||
|
|
b6d09c8a70 | ||
|
|
a98de2a76c | ||
|
|
8aea96a4e3 | ||
|
|
7cf7646ed0 | ||
|
|
5c617f311a | ||
|
|
023a18014e | ||
|
|
c67da817b7 | ||
|
|
0512a65c42 | ||
|
|
101a0c8648 | ||
|
|
2966752b73 | ||
|
|
a04926ae4d | ||
|
|
b82d0bdefb | ||
|
|
f32ee7fbbb | ||
|
|
a83a946cda | ||
|
|
84f726edd6 | ||
|
|
1aa1a0c0d5 | ||
|
|
e7a35086f7 | ||
|
|
8a7e8665d2 | ||
|
|
74969cd1be | ||
|
|
6163821401 | ||
|
|
4f8858f75d | ||
|
|
fb2b6f3685 | ||
|
|
5286828cb8 | ||
|
|
d29a28591e | ||
|
|
da58c31ed4 | ||
|
|
55bfb1d550 | ||
|
|
4d326ef806 | ||
|
|
117fcbddd4 | ||
|
|
46fbd2454c | ||
|
|
fe0c8ccb0c | ||
|
|
e76e71f285 | ||
|
|
358a05c0fa | ||
|
|
1264a44aa0 | ||
|
|
9c96651e70 | ||
|
|
8e758c383c | ||
|
|
886d7411ae | ||
|
|
7600a763a2 | ||
|
|
8d3cdf62e3 | ||
|
|
9e5e993a1b | ||
|
|
5a879058c9 | ||
|
|
c26d36ebce | ||
|
|
89f5350ac4 | ||
|
|
c423363266 | ||
|
|
000adfb2be | ||
|
|
4f29fdea72 | ||
|
|
c3648e4b20 | ||
|
|
dd83feec2d | ||
|
|
45afd2032b | ||
|
|
744314cb3a | ||
|
|
1907e5d3ce | ||
|
|
50db561774 | ||
|
|
326a35939a | ||
|
|
ae167faee5 | ||
|
|
fb0f30b9f0 | ||
|
|
75e2626dc0 | ||
|
|
def0e6762d | ||
|
|
ba6452c5b0 | ||
|
|
817c0c56ab | ||
|
|
81f71982f4 | ||
|
|
bf07f26e73 | ||
|
|
642ff0f27f | ||
|
|
58faf5adf0 | ||
|
|
868a6f03ec | ||
|
|
2ff011ffba | ||
|
|
7ce491df53 | ||
|
|
3d89841a51 | ||
|
|
3cff5d0961 | ||
|
|
51a02a4064 | ||
|
|
752accdd33 | ||
|
|
bdcd392dbf | ||
|
|
6f7c5a35dd | ||
|
|
4feaffe102 | ||
|
|
e0686209f4 | ||
|
|
e061557eea | ||
|
|
aeafe57c5d | ||
|
|
e84b6f4084 | ||
|
|
82c6a5dd60 | ||
|
|
f9002f9f9b | ||
|
|
bf4c928214 | ||
|
|
bdf161aa7d | ||
|
|
bfdc1e96a6 | ||
|
|
368b5445e4 | ||
|
|
6b76338407 | ||
|
|
6beedbdba9 | ||
|
|
fb920af5dc | ||
|
|
acea700708 | ||
|
|
0619f3a9e7 | ||
|
|
361108b88e | ||
|
|
b03a368191 | ||
|
|
516bc38b2b | ||
|
|
b532c052ad | ||
|
|
a392065ca8 | ||
|
|
0195d2ec43 | ||
|
|
cc27cf5c24 | ||
|
|
fa5b500751 | ||
|
|
2c9226e4b1 | ||
|
|
6908e297f6 | ||
|
|
8ff09b3a62 | ||
|
|
ea3e89dd00 | ||
|
|
b8e8370a4c | ||
|
|
2cb6d2f3d0 | ||
|
|
71dee66573 | ||
|
|
65da18d143 | ||
|
|
f21bef93b1 | ||
|
|
3f43994766 | ||
|
|
06862d354e | ||
|
|
06aded3ce4 | ||
|
|
cdb6aabdbf | ||
|
|
c23e8f8c65 | ||
|
|
db69d016a9 | ||
|
|
9e5e3fba16 | ||
|
|
135d6e510e | ||
|
|
0e3635f98b | ||
|
|
127f446856 | ||
|
|
0ae8c972bb | ||
|
|
03d4cc43ec | ||
|
|
04fae1194f | ||
|
|
f6867a914e | ||
|
|
0f8bd7fab8 | ||
|
|
7124ce742f | ||
|
|
1ea72a0080 | ||
|
|
2b20528eee | ||
|
|
a3d73fa5ff | ||
|
|
9547b0fc72 | ||
|
|
bccfef77c9 | ||
|
|
b01c6b68b6 | ||
|
|
9924ed0b13 | ||
|
|
78f55a701f | ||
|
|
7d27a2c4a4 | ||
|
|
c1319ba34f | ||
|
|
a3eeb1ded7 | ||
|
|
536a37aee9 | ||
|
|
4707ea617f | ||
|
|
83239394dd | ||
|
|
5276972c67 | ||
|
|
174d695512 | ||
|
|
e134914ef8 | ||
|
|
e91567f637 | ||
|
|
423ab3fa68 | ||
|
|
b569fcbc25 | ||
|
|
728620b527 | ||
|
|
e2ea963d5d | ||
|
|
9c6cdff785 | ||
|
|
670ac60e1b | ||
|
|
4f42826399 | ||
|
|
5f7437df8d | ||
|
|
6d4f1d6c43 | ||
|
|
409c6de44a | ||
|
|
571c66c1b2 | ||
|
|
6a56ea242f | ||
|
|
c7af79f452 | ||
|
|
578b03efcb | ||
|
|
d351e48602 | ||
|
|
f710404f10 | ||
|
|
007bf8a937 | ||
|
|
0c67111e68 | ||
|
|
16a6aca76a | ||
|
|
c0399b04db | ||
|
|
4b8c902354 | ||
|
|
7070bb608d | ||
|
|
3a6aa9834a | ||
|
|
0346ec3111 | ||
|
|
2f926b601c | ||
|
|
d6da1c8485 | ||
|
|
6ecf2998ee | ||
|
|
13d65e64b5 | ||
|
|
0b9a80684a | ||
|
|
bd94c52c36 | ||
|
|
9d25a18f9e | ||
|
|
987138f5bf | ||
|
|
dc8d0a833b | ||
|
|
6368938b9e | ||
|
|
8500b480a9 | ||
|
|
deca65eb1f | ||
|
|
2cfa13a48f | ||
|
|
687c81eedb | ||
|
|
658212c1f5 | ||
|
|
04857a063d | ||
|
|
5d14722601 | ||
|
|
db20a40745 | ||
|
|
07b1ee0199 | ||
|
|
ad671a3a92 | ||
|
|
f042761ada | ||
|
|
64e5b67f35 | ||
|
|
c92b0d46dc | ||
|
|
1915e6feb4 | ||
|
|
8f80378e65 | ||
|
|
b34cad7c61 | ||
|
|
e4c07e0356 | ||
|
|
6a4fdf94fc | ||
|
|
14c0cc7ece | ||
|
|
231b5d587b | ||
|
|
6cc3449183 | ||
|
|
dda99166d9 | ||
|
|
f19b93eb34 | ||
|
|
e4652090a8 | ||
|
|
8fb0a7c405 | ||
|
|
192923bd2a | ||
|
|
851460fe12 | ||
|
|
37aa01958a | ||
|
|
c3feb6a9e6 | ||
|
|
ed157c8a75 | ||
|
|
26fc3ba626 | ||
|
|
06ea0caece | ||
|
|
620958a8a2 | ||
|
|
5937ec2e04 | ||
|
|
4e5a757396 | ||
|
|
581849098f | ||
|
|
dd5baca85d | ||
|
|
1bde324162 | ||
|
|
05ac04d30d | ||
|
|
4784ec9915 | ||
|
|
f1b13d7ed3 | ||
|
|
55f19fdcdc | ||
|
|
6db4e34c48 | ||
|
|
d9d204a6cd | ||
|
|
fdccda6b54 | ||
|
|
60ce0e0cd4 | ||
|
|
001e041855 | ||
|
|
b31b2af58b | ||
|
|
6e04648284 | ||
|
|
bf5e2cf4dc | ||
|
|
59fe01cef6 | ||
|
|
e0a823bf58 | ||
|
|
f8a50cb752 | ||
|
|
44cefeec28 | ||
|
|
2eeec2bfc6 | ||
|
|
f53f09cea5 | ||
|
|
7205b5f942 | ||
|
|
5b7129acb7 | ||
|
|
a7ae6519c7 | ||
|
|
9406eb32f6 | ||
|
|
ef1885d63e | ||
|
|
6d091df30c | ||
|
|
b0317d0e4a | ||
|
|
b65825e99a | ||
|
|
5ad8df595c | ||
|
|
130fc3e075 | ||
|
|
f02e3a23ee | ||
|
|
3ec59540bd | ||
|
|
01c887530b | ||
|
|
5824c9de52 | ||
|
|
c3805b6b2f | ||
|
|
78ce5f3e6e | ||
|
|
3d25c27b11 | ||
|
|
469add4b6b | ||
|
|
a66fe41977 | ||
|
|
63e53724c4 | ||
|
|
85bd909af2 | ||
|
|
94a19f627b | ||
|
|
e0d25951a2 | ||
|
|
c14adf700a | ||
|
|
8e2e4e7c30 | ||
|
|
a1020cde7d | ||
|
|
d1da7f4a47 | ||
|
|
55a45c7673 | ||
|
|
e9361e74e8 | ||
|
|
c33c08392c | ||
|
|
bb2ac9c259 | ||
|
|
6d95caec31 | ||
|
|
555b5aece1 | ||
|
|
537a070238 | ||
|
|
dcacc7969f | ||
|
|
63110b204d | ||
|
|
a439f96384 | ||
|
|
8e0d70da8b | ||
|
|
18ce0f4219 | ||
|
|
7f4fc340ad | ||
|
|
50aaa206bf | ||
|
|
cbed5dae46 | ||
|
|
fd64bb9d36 | ||
|
|
945aaf02d8 | ||
|
|
a93c0e424d | ||
|
|
7ae2489153 | ||
|
|
78f014e74b | ||
|
|
dddabe2243 | ||
|
|
0caf162825 | ||
|
|
24d04e0e39 | ||
|
|
d7ccea9cdb | ||
|
|
69bfe311f9 | ||
|
|
f5ef5d3222 | ||
|
|
30752f53b8 | ||
|
|
76f00c3a08 | ||
|
|
3ffda44c39 | ||
|
|
fac3e7d3c7 | ||
|
|
46386c71a3 | ||
|
|
ffdd1abd47 | ||
|
|
032ea13c65 | ||
|
|
c76516db75 | ||
|
|
e5b4d501ed | ||
|
|
a133ea82cf | ||
|
|
0b971c9128 | ||
|
|
9877e4c84d | ||
|
|
94e2b7c351 | ||
|
|
d4d853c26d | ||
|
|
841bf664fa | ||
|
|
820bc0cab3 | ||
|
|
7104014c27 | ||
|
|
4546d40be1 | ||
|
|
553b9723b3 | ||
|
|
b0aff929f5 | ||
|
|
a2364f9e59 | ||
|
|
9136f81f78 | ||
|
|
9d8b55f6ee | ||
|
|
b9005f28f7 | ||
|
|
ea1711fe06 | ||
|
|
bc738c8afc | ||
|
|
1fc00669a6 | ||
|
|
19f585d517 | ||
|
|
0744cc4471 | ||
|
|
c4546f1e30 | ||
|
|
f124f2fc4d | ||
|
|
ff39b77c18 | ||
|
|
a640376d84 | ||
|
|
529f59264c | ||
|
|
fa97ae36b5 | ||
|
|
c0af518121 | ||
|
|
fdd8fa3f2a | ||
|
|
afdc269323 | ||
|
|
54ad8ae792 | ||
|
|
c8b3583465 | ||
|
|
c88d4d674f | ||
|
|
cd5dd17a99 | ||
|
|
f4123dc011 | ||
|
|
4b3fbd27cc | ||
|
|
87c64cf255 | ||
|
|
f92f514254 | ||
|
|
8ccb4bd6fe | ||
|
|
1846f75f81 | ||
|
|
3d015be1fc | ||
|
|
420d67f092 | ||
|
|
f57f717f92 | ||
|
|
af138542b7 | ||
|
|
f67e7bb128 | ||
|
|
48974ec63e | ||
|
|
f8afce46a1 | ||
|
|
c4ccf7303a | ||
|
|
545b87b10e | ||
|
|
323c0ff611 | ||
|
|
7294471223 | ||
|
|
a74c65f8dc | ||
|
|
304eb179f1 | ||
|
|
bc5631d9a8 | ||
|
|
265f9f9bd5 | ||
|
|
67e780691c | ||
|
|
85b83aed32 | ||
|
|
41c9efd734 | ||
|
|
7aec521fa8 | ||
|
|
a40b65a77d | ||
|
|
9c9ad4a172 | ||
|
|
fc2a0cd162 | ||
|
|
2209853abe | ||
|
|
fc33c079a1 | ||
|
|
3178be619e | ||
|
|
7abd49dc56 | ||
|
|
433cf1a974 | ||
|
|
bb612298b9 | ||
|
|
597bf6793d | ||
|
|
2acb3e1b65 | ||
|
|
55446928c2 | ||
|
|
acada272c5 | ||
|
|
029d77ef21 | ||
|
|
5bc1e3cc72 | ||
|
|
8acf687a97 | ||
|
|
55b96f21b6 | ||
|
|
5f1d6aed2e | ||
|
|
0cb439b450 | ||
|
|
925d228c7f | ||
|
|
9842ede61c | ||
|
|
2a0e5e4696 | ||
|
|
a09357a484 | ||
|
|
1022c3b6fa | ||
|
|
8282608d3a | ||
|
|
2b78ad44be | ||
|
|
2d16cce255 | ||
|
|
518f5247c5 | ||
|
|
63b8b2c31d | ||
|
|
bfab946078 | ||
|
|
5d2f27f870 | ||
|
|
79c827006d | ||
|
|
5f4153c365 | ||
|
|
f6d2ac229f | ||
|
|
572ff8fff6 | ||
|
|
3b86f8de2a | ||
|
|
684e21fcec | ||
|
|
6e1e677316 | ||
|
|
608ec767f0 | ||
|
|
4f6d62779d | ||
|
|
bdb87289d8 | ||
|
|
6108c01fb5 | ||
|
|
015ef5b960 | ||
|
|
361ba0c32b | ||
|
|
00978bd6c9 | ||
|
|
413ed1d914 | ||
|
|
a61bcc62ed | ||
|
|
bd6fbcb1c3 | ||
|
|
300510869b | ||
|
|
ba0a610749 | ||
|
|
0e185670ab | ||
|
|
2adea53ae8 | ||
|
|
65e8b354d3 | ||
|
|
bd95ade626 | ||
|
|
a8908acecc | ||
|
|
99f8d2cfe7 | ||
|
|
6db5ad78ab | ||
|
|
1ebe869e0e | ||
|
|
a770d09c0a | ||
|
|
f845b608c2 | ||
|
|
0632a0de80 | ||
|
|
e2f0881701 | ||
|
|
244d05ebff | ||
|
|
c1994c943f | ||
|
|
4736ad27fd | ||
|
|
faf574621a | ||
|
|
af663942b6 | ||
|
|
9fc786beca | ||
|
|
aac8e351f6 | ||
|
|
a9bdb5c56b | ||
|
|
d4ef8cb9ce | ||
|
|
4f557713ea | ||
|
|
d10531f65d | ||
|
|
eb76c67a44 | ||
|
|
11e1f0d787 | ||
|
|
b8d3aa32e9 | ||
|
|
41c54f54da | ||
|
|
1f9710308f | ||
|
|
b7e98fa04a | ||
|
|
7cbd985756 | ||
|
|
3bcb2eecd8 | ||
|
|
a8865e2277 | ||
|
|
9995c69cc6 | ||
|
|
e677e5d569 | ||
|
|
2286569694 | ||
|
|
c337840584 | ||
|
|
3307cc6f66 | ||
|
|
0509307d8f | ||
|
|
57a506e0fc | ||
|
|
2d61a463fc | ||
|
|
8d0293e647 | ||
|
|
74783de318 | ||
|
|
dce7adb210 | ||
|
|
8f14013e0a | ||
|
|
8db60d8e79 | ||
|
|
34bce0513c | ||
|
|
cd6223c0ab | ||
|
|
8735bbefd4 | ||
|
|
fd8fb3e581 | ||
|
|
fad4618de3 | ||
|
|
e8901719fa | ||
|
|
992b958414 | ||
|
|
c10d2ef753 | ||
|
|
bda362a666 | ||
|
|
701542b40f | ||
|
|
9437656d5c | ||
|
|
97bd58720e | ||
|
|
38b904c6fb | ||
|
|
6f4383d004 | ||
|
|
077b583eff | ||
|
|
22261d8303 | ||
|
|
00b3ded7d6 | ||
|
|
028d31cc5b | ||
|
|
6257baaea5 | ||
|
|
10718f2996 | ||
|
|
736160a910 | ||
|
|
ade3e36856 | ||
|
|
441ef21937 | ||
|
|
f0c5326b19 | ||
|
|
66102b3aba | ||
|
|
ae46a98cdb | ||
|
|
79ce3bd883 | ||
|
|
005dd7d951 | ||
|
|
5808aafb49 | ||
|
|
82c499a8b9 | ||
|
|
52cbb1e756 | ||
|
|
22bc414972 | ||
|
|
2377c0a579 | ||
|
|
24e06d8978 | ||
|
|
3a3a76466c | ||
|
|
719a8ef964 | ||
|
|
646faffb4e | ||
|
|
ffb17617a1 | ||
|
|
65f48be964 | ||
|
|
e4eee8c37f | ||
|
|
c1d7a7933b | ||
|
|
e2504b1366 | ||
|
|
b17c06b47d | ||
|
|
5daa7269d6 | ||
|
|
5995948310 | ||
|
|
dcac2d118d | ||
|
|
a6b8252946 | ||
|
|
fd2ac6ef18 | ||
|
|
1ff397f568 | ||
|
|
8e6e5f7dcc | ||
|
|
f2335e8e72 | ||
|
|
ac16f2ed55 | ||
|
|
0b9ca84735 | ||
|
|
4c01a96eb1 | ||
|
|
6015feb511 | ||
|
|
b8667c45dd | ||
|
|
beb1306a1d | ||
|
|
042c934c6e | ||
|
|
6eb9c83345 | ||
|
|
bbe2fe000d | ||
|
|
84c539df67 | ||
|
|
8397cae2be | ||
|
|
11ae72b2e2 | ||
|
|
6a07fea2d7 | ||
|
|
5cd435b755 | ||
|
|
0605fc54df | ||
|
|
48d96243ea | ||
|
|
6055a2e342 | ||
|
|
d0c0cd8ed7 | ||
|
|
fe19af668d | ||
|
|
06ec0dbf25 | ||
|
|
06b334396a | ||
|
|
cc0f68448b | ||
|
|
fc5fd7f86b | ||
|
|
82256aaa2f | ||
|
|
c655a61290 | ||
|
|
7b88ebcaf6 | ||
|
|
2e9e8d7f45 | ||
|
|
b496f835dc | ||
|
|
5ce8eabffb | ||
|
|
c069cdde69 | ||
|
|
c734bc472f | ||
|
|
42d94c3d50 | ||
|
|
154f10c3a0 | ||
|
|
1041754c71 | ||
|
|
b8a3bb44df | ||
|
|
23e70e0e9c | ||
|
|
f34725de85 | ||
|
|
15668849d2 | ||
|
|
8efa53761e | ||
|
|
a72810e1ab | ||
|
|
409527caca | ||
|
|
bf66b29225 | ||
|
|
96f543b2d4 | ||
|
|
053b2d3c23 | ||
|
|
d09dd7ff1f | ||
|
|
4197382855 | ||
|
|
d0e995d221 | ||
|
|
cb6d46a582 | ||
|
|
fee3e34378 | ||
|
|
c3abefd745 | ||
|
|
b19e350dc4 | ||
|
|
7e4c8285dd | ||
|
|
8d54b92ded | ||
|
|
aa0a876e19 | ||
|
|
ddbe36c0dc | ||
|
|
0b6564895e | ||
|
|
9011a8a123 | ||
|
|
4c68f8a541 | ||
|
|
8ea576263c | ||
|
|
d2852ce966 | ||
|
|
b95ec88e4f | ||
|
|
c14d1db833 | ||
|
|
3a3bb7fb38 | ||
|
|
23e2136e47 | ||
|
|
715318707e | ||
|
|
4fc9f569e2 | ||
|
|
c1428d92d5 | ||
|
|
fb02b0c340 | ||
|
|
a7f9477e95 | ||
|
|
58165264c6 | ||
|
|
746591430f | ||
|
|
b0d0dcad2c | ||
|
|
9e090175a5 | ||
|
|
345726265b | ||
|
|
2c53bb0eb8 | ||
|
|
51ef14c71a | ||
|
|
df4b29acaf | ||
|
|
3b23155fa4 | ||
|
|
59b99fa6b0 | ||
|
|
381627bbbb | ||
|
|
aa09d2f719 | ||
|
|
1f27ca5352 | ||
|
|
131be53b1a | ||
|
|
cdc9448e93 | ||
|
|
47092acf8c | ||
|
|
87d542f7a1 | ||
|
|
403e67a795 | ||
|
|
c8ee869941 | ||
|
|
82f249ca6f | ||
|
|
426cd9b6a6 | ||
|
|
6fe331397c | ||
|
|
8306f3e2da | ||
|
|
0c5e303175 | ||
|
|
b9ff59fd75 | ||
|
|
8b279bf34b | ||
|
|
a238fe99a9 | ||
|
|
2905fbe647 | ||
|
|
e349388372 | ||
|
|
e5298ea8e6 | ||
|
|
cc36ae536b | ||
|
|
7744acd69c | ||
|
|
fa40f1a83f | ||
|
|
196e5c4419 | ||
|
|
ffa25d1d9c | ||
|
|
ed649f0516 | ||
|
|
df84c2b28d | ||
|
|
e8b5faf1d5 | ||
|
|
8c3b9e6508 | ||
|
|
5e756c9fd3 | ||
|
|
a3e7ad1fa8 | ||
|
|
bcc590e65f | ||
|
|
7cb62ee7fe | ||
|
|
660528bb52 | ||
|
|
4cec9642ab | ||
|
|
257125b8c8 | ||
|
|
e46f8c5187 | ||
|
|
41b21914be | ||
|
|
1ff102dc27 | ||
|
|
47915fd4a0 | ||
|
|
6d5ebf56a4 | ||
|
|
c285ac7c4f | ||
|
|
7bb7efc705 | ||
|
|
b570cad138 | ||
|
|
abd5082c56 | ||
|
|
d87c41fa6c | ||
|
|
2ad8de51de | ||
|
|
14f7e910cb | ||
|
|
55d86b8d42 | ||
|
|
1034c884ac | ||
|
|
04d3c134e2 | ||
|
|
2cbe44ecef | ||
|
|
dc86c88be5 | ||
|
|
bc2b3891d6 | ||
|
|
9cd801fd1f | ||
|
|
c677103c5e | ||
|
|
d84127a7a8 | ||
|
|
db2290e47a | ||
|
|
441d2df46f | ||
|
|
0e4c13937b | ||
|
|
1d0bdad7d0 | ||
|
|
c0e0136349 | ||
|
|
90ac6bf3b4 | ||
|
|
d83acc0fc8 | ||
|
|
53e0749906 | ||
|
|
ccc36b7d45 | ||
|
|
a62ee6f35e | ||
|
|
a6eb3f25ac | ||
|
|
b96f73278d | ||
|
|
ea7f4d74f6 | ||
|
|
40806ceda0 | ||
|
|
b4238e93af | ||
|
|
19b7fbc350 | ||
|
|
ef2bf0ca0f | ||
|
|
cdbcb6b981 | ||
|
|
09789b5327 | ||
|
|
9673c9dc2c | ||
|
|
aa72396013 | ||
|
|
ed3c4cced9 | ||
|
|
450099c13a | ||
|
|
2e16cc492a | ||
|
|
f03463bd85 | ||
|
|
205cbce425 | ||
|
|
a147009dcd | ||
|
|
7e5757e317 | ||
|
|
0dd17a2603 | ||
|
|
f423c068fe | ||
|
|
f28bed6436 | ||
|
|
c478c7a90e | ||
|
|
6bf0bbfdfd | ||
|
|
d9f0baa987 | ||
|
|
9037941ddc | ||
|
|
bb7b599151 | ||
|
|
4e54bb9841 | ||
|
|
dec8ec8a60 | ||
|
|
8d6eab6b33 | ||
|
|
c1009715de | ||
|
|
62f7cfb82b | ||
|
|
b35fa0adc7 | ||
|
|
e8c7ec339e | ||
|
|
2f1616fa40 | ||
|
|
c4e3f86ebf | ||
|
|
0c21ed01c2 | ||
|
|
d158d39e1f | ||
|
|
aa55d2639a | ||
|
|
7fbbaa6d60 | ||
|
|
c47fff7bf5 | ||
|
|
b4d53f3aed | ||
|
|
cdd0328f03 | ||
|
|
60e84bd659 | ||
|
|
164ae2ba32 | ||
|
|
383ff21823 | ||
|
|
78a936d21f | ||
|
|
c7c7a04e89 | ||
|
|
e9e4984643 | ||
|
|
ec377baa37 | ||
|
|
5a90015dc7 | ||
|
|
0554c5c5fb | ||
|
|
37293b3f1b | ||
|
|
651d4799d8 | ||
|
|
2cec6ecbc0 | ||
|
|
42f1ef0d6b | ||
|
|
ed062f2c64 | ||
|
|
5aaee100c8 | ||
|
|
517e39f34a | ||
|
|
4a257c6bad | ||
|
|
4fd5545e2d | ||
|
|
08a40e10e7 | ||
|
|
af61d11cc1 | ||
|
|
c9aa0684bf | ||
|
|
eda4c08fc0 | ||
|
|
3aa39bcd9a | ||
|
|
ad7c11c0a3 | ||
|
|
dbfb732325 | ||
|
|
8e292cc593 | ||
|
|
0b5e89fbb7 | ||
|
|
1d4cbdbe57 | ||
|
|
f5aea8f015 | ||
|
|
bf413d3baa | ||
|
|
ba31b21661 | ||
|
|
767fd53d5f | ||
|
|
24e7974d04 | ||
|
|
fc4542463b | ||
|
|
6e5ebc4bd9 | ||
|
|
5091491a1f | ||
|
|
231888eb4c | ||
|
|
264a32f747 | ||
|
|
6322bcce2a | ||
|
|
0823c9f6c5 | ||
|
|
e7d4012653 | ||
|
|
21f3152dc0 | ||
|
|
298e71401b | ||
|
|
d9f3b3d155 | ||
|
|
f5b6b7a240 | ||
|
|
a56112e789 | ||
|
|
b5b39d1b6e | ||
|
|
442da8c9eb | ||
|
|
24c6a85f18 | ||
|
|
68f8d238d8 | ||
|
|
ead49217c0 | ||
|
|
8dbdefdfad | ||
|
|
7f36c84c0f | ||
|
|
c9f721d4c9 | ||
|
|
768f81aedc | ||
|
|
070e81964e | ||
|
|
92c27c6545 | ||
|
|
b8206b895f | ||
|
|
d802763867 | ||
|
|
b1c8df2a7f | ||
|
|
1b52ff1acf | ||
|
|
250a80dd77 | ||
|
|
d6c812ea28 | ||
|
|
1ed19359b5 | ||
|
|
7df441c014 | ||
|
|
8330447463 | ||
|
|
ca9279e3f9 | ||
|
|
bf59190406 | ||
|
|
cefb137e1e | ||
|
|
4d85bde8d5 | ||
|
|
f6196a73be | ||
|
|
005551efc8 | ||
|
|
6465d6e68e | ||
|
|
cd44531822 | ||
|
|
0aa59f3ab8 | ||
|
|
9eef331df6 | ||
|
|
e34804d23b | ||
|
|
dd2262fc88 | ||
|
|
75a7359ba5 | ||
|
|
fb489a30ee | ||
|
|
678ea47cbe | ||
|
|
b40e5bd7b2 | ||
|
|
5035872ab6 | ||
|
|
4583936f0b | ||
|
|
16a727b3cc | ||
|
|
66d940559f | ||
|
|
68ffc3cd59 | ||
|
|
ac9582bd8b | ||
|
|
5243ef874d | ||
|
|
5b308be891 | ||
|
|
7ad0ed9254 | ||
|
|
7b95ded577 | ||
|
|
00d869952d | ||
|
|
15c3925b1e | ||
|
|
a1dd6b2484 | ||
|
|
48f9e9c3f0 | ||
|
|
4f2bb34218 | ||
|
|
a80ae0d06a | ||
|
|
da09ad3362 | ||
|
|
168e2b3d89 | ||
|
|
e3d742c928 | ||
|
|
61397dbb07 | ||
|
|
206fb7cc8a | ||
|
|
a198de6d1f | ||
|
|
36d56438a2 | ||
|
|
b30c3f653e | ||
|
|
87f6e734a9 | ||
|
|
a619b8908d | ||
|
|
9effe2b27b | ||
|
|
fa426fc633 | ||
|
|
4e82b63816 | ||
|
|
4538b2fb21 | ||
|
|
23e35b8d9b | ||
|
|
6f48a17ba4 | ||
|
|
2dd925d34d | ||
|
|
2a9075d642 | ||
|
|
0bd4fcd99e | ||
|
|
91001d9158 | ||
|
|
81d3001c07 | ||
|
|
812c981f9b | ||
|
|
06011a6317 | ||
|
|
ee7f0cecf8 | ||
|
|
2209b580b5 | ||
|
|
a2d99bb52e | ||
|
|
f551116714 | ||
|
|
71e35cef4d | ||
|
|
f2d2c476f3 | ||
|
|
c198d86538 | ||
|
|
6eabcf1704 | ||
|
|
42bfe4b357 | ||
|
|
d2973e27da | ||
|
|
934c7f3413 | ||
|
|
0f07341dea | ||
|
|
3faa0cfcad | ||
|
|
841684593f | ||
|
|
0b98799673 | ||
|
|
b0d18c735b | ||
|
|
9f6966d7f2 | ||
|
|
bb85705fe9 | ||
|
|
b4dd2de117 | ||
|
|
dbb8575692 | ||
|
|
41bd05586e | ||
|
|
3e570a8cfb | ||
|
|
3365a4f9ff | ||
|
|
795b84a447 | ||
|
|
0af14e1d51 | ||
|
|
e8b6801de1 | ||
|
|
22470e7c87 | ||
|
|
56c9b0e41a | ||
|
|
83873ab54b | ||
|
|
0bc3ebe3a5 | ||
|
|
275ba7509f | ||
|
|
799e96c317 | ||
|
|
6a3e391f17 | ||
|
|
deaf48e319 | ||
|
|
22bf95ac19 | ||
|
|
0b6174ace6 | ||
|
|
1ed7a4ceb8 | ||
|
|
233e32b570 | ||
|
|
18fadda280 | ||
|
|
99c0fa5ebf | ||
|
|
5499471279 | ||
|
|
803eafe632 | ||
|
|
521ff876b9 | ||
|
|
97a87728f7 | ||
|
|
c39835e61e | ||
|
|
01ac5b5ea0 | ||
|
|
837d1502e9 | ||
|
|
30493e763b | ||
|
|
faf8521e9d | ||
|
|
e585d72216 | ||
|
|
2ed8d94c67 | ||
|
|
3372966664 | ||
|
|
34f5733114 | ||
|
|
76ff956eb9 | ||
|
|
6529cbc674 | ||
|
|
8b5be6ebc0 | ||
|
|
40eb19b613 | ||
|
|
955fc795da | ||
|
|
4fd26d7784 | ||
|
|
85e199b27b | ||
|
|
79211ac4a9 | ||
|
|
99c3d86fa5 | ||
|
|
1f03081002 | ||
|
|
84001f848f | ||
|
|
66b49bcf31 | ||
|
|
fb0dd53ba5 | ||
|
|
15c0e7551e | ||
|
|
38fe338029 | ||
|
|
6b5076bbfe | ||
|
|
a180d71e72 | ||
|
|
0c9d4386fb | ||
|
|
591eb5411f | ||
|
|
5cffdc4d8c | ||
|
|
e293fbdb07 | ||
|
|
97ce17925e | ||
|
|
3e3778d982 | ||
|
|
cc6396ec56 | ||
|
|
c33811ae3d | ||
|
|
ee07901388 | ||
|
|
d9d836fe2c | ||
|
|
3f61050387 | ||
|
|
3b8f9c7d36 | ||
|
|
9b94bdffed | ||
|
|
c566ad8456 | ||
|
|
40cfef3115 | ||
|
|
a860ca76b5 | ||
|
|
b613c8e0ff | ||
|
|
3de9c85065 | ||
|
|
92f4969cd9 | ||
|
|
1b9ec290a7 | ||
|
|
ab8a224b06 | ||
|
|
65211f85a5 | ||
|
|
cb1fa395b1 | ||
|
|
ba266f31c5 | ||
|
|
0aaeb52581 | ||
|
|
1242eabb55 | ||
|
|
94c3b63ff6 | ||
|
|
547943bb94 | ||
|
|
c1961ec61c | ||
|
|
b278b9ec76 | ||
|
|
573e1ed40c | ||
|
|
44ffa1d046 | ||
|
|
d7efdf013a | ||
|
|
5f12cd8fea | ||
|
|
2a00afaab5 | ||
|
|
7a1c38a4ad | ||
|
|
8a5ad66616 | ||
|
|
81ed946515 | ||
|
|
dcf8b7767c | ||
|
|
6d0f103c16 | ||
|
|
21fb890328 | ||
|
|
5d0503e250 | ||
|
|
549dfd362b | ||
|
|
0bd0c70c8f | ||
|
|
e1338bb53e | ||
|
|
5f195a869c | ||
|
|
9a108026e6 | ||
|
|
05b68e21f4 | ||
|
|
6599a6935f | ||
|
|
8d312293b1 | ||
|
|
ed06b77864 | ||
|
|
8a83c611eb | ||
|
|
78bd186003 | ||
|
|
7a14fe6a90 | ||
|
|
424e9c4aaa | ||
|
|
59f5e4fe17 | ||
|
|
af151a2d45 | ||
|
|
77522e79c6 | ||
|
|
ace131cd4e | ||
|
|
b7b8c5ff61 | ||
|
|
558e9237fd | ||
|
|
f4acd176b8 | ||
|
|
2c2bd0a973 | ||
|
|
ffbf030a3d | ||
|
|
f82e46ed1d | ||
|
|
b0a7795cf0 | ||
|
|
7d2b5b6c07 | ||
|
|
a02a6b0b92 | ||
|
|
4c6edec787 | ||
|
|
fcb074bbd8 | ||
|
|
e120e49efb | ||
|
|
b106ed8fc0 | ||
|
|
844f0f0051 | ||
|
|
02941fe5d3 | ||
|
|
8bd66bf06f | ||
|
|
ae085404d3 | ||
|
|
1c4e6ecac6 | ||
|
|
f24ba8b799 | ||
|
|
c810448fc8 | ||
|
|
db812fe7c8 | ||
|
|
d7e17b2f56 | ||
|
|
033144fd9c | ||
|
|
4c909d5cfc | ||
|
|
70889544d1 | ||
|
|
557b869aa4 | ||
|
|
2f30a0df56 | ||
|
|
fab439d6a3 | ||
|
|
73663150f8 | ||
|
|
21f409625b | ||
|
|
fe472d3412 | ||
|
|
2671fac8c2 | ||
|
|
c157c91075 | ||
|
|
9208ae18ce | ||
|
|
4281f0e261 | ||
|
|
7d04bdea1b | ||
|
|
bd1cf20f05 | ||
|
|
1cdc010446 | ||
|
|
07553ed219 | ||
|
|
3a8de6cdec | ||
|
|
71778d7a42 | ||
|
|
384f6c2420 | ||
|
|
65c63a77b2 | ||
|
|
325f1f2756 | ||
|
|
8848f4c139 | ||
|
|
00b3adfe82 | ||
|
|
b2285f762c | ||
|
|
dc50747c78 | ||
|
|
56be17ab80 | ||
|
|
254d5476b1 | ||
|
|
5e96335c23 | ||
|
|
7be59b571d | ||
|
|
cef44163bc | ||
|
|
7d55dda917 | ||
|
|
8a75c2828e | ||
|
|
9bb456ed2f | ||
|
|
bff0c4401c | ||
|
|
9fc0a07453 | ||
|
|
eb688962a1 | ||
|
|
c266193a34 | ||
|
|
244dd16819 | ||
|
|
7d7b94804b | ||
|
|
8f69dfa6a2 | ||
|
|
734310ff0d | ||
|
|
5823bcb7ab | ||
|
|
2629945c72 | ||
|
|
f5797b7fd2 | ||
|
|
082c9f0f4f | ||
|
|
7f3de8dcb4 | ||
|
|
abc9f350d2 | ||
|
|
d38e97ee0b | ||
|
|
25441a8773 | ||
|
|
7a263355f3 | ||
|
|
75b4e09c4e | ||
|
|
9d32d49488 | ||
|
|
3f4a4bf2e1 | ||
|
|
8ce22720c6 | ||
|
|
651a33a98d | ||
|
|
b188e8a617 | ||
|
|
3523fdfbb5 | ||
|
|
f7868a1bd8 | ||
|
|
5e937e6e20 | ||
|
|
2e8da22b32 | ||
|
|
cf2c5550aa | ||
|
|
0a583ca73b | ||
|
|
5ad167c45d | ||
|
|
e89e4f5277 | ||
|
|
8fb813a09d | ||
|
|
bed57b03ce | ||
|
|
3e7aea0d51 | ||
|
|
30fcae9dce | ||
|
|
f499b6378c | ||
|
|
e23479bd9e | ||
|
|
087371d7b0 | ||
|
|
ff240ed264 | ||
|
|
7590050ffe | ||
|
|
3c6b0b89ac | ||
|
|
f1ffa242c2 | ||
|
|
36eccdd102 | ||
|
|
1c9c20c810 | ||
|
|
dae2edfbf5 | ||
|
|
bba3c52ff3 | ||
|
|
a85794df1b | ||
|
|
df0a6fa3ab | ||
|
|
c5cde79130 | ||
|
|
b58ee1e52b | ||
|
|
82c4a6f0a0 | ||
|
|
0175e83387 | ||
|
|
2ebd4ed954 | ||
|
|
e54cfa4378 | ||
|
|
7b04934cab | ||
|
|
970aeb1e20 | ||
|
|
54ab56aec5 | ||
|
|
d5fe84e1c6 | ||
|
|
bfe0d12a60 | ||
|
|
a06f074bc8 | ||
|
|
1f0e1f0e21 | ||
|
|
ab8b40a9d1 | ||
|
|
557c6a58ea | ||
|
|
3263f412fd | ||
|
|
559a90ad8b | ||
|
|
ff8e8e0f8b | ||
|
|
7d584818ff | ||
|
|
31806c7e92 | ||
|
|
ee6a4ecc53 | ||
|
|
b5a271191d | ||
|
|
076b4e132f | ||
|
|
4df59060fb | ||
|
|
0048d3bcd3 | ||
|
|
f807eaa95e | ||
|
|
ab0d27e398 | ||
|
|
487e4c18fc | ||
|
|
1794a94fa9 | ||
|
|
e84c8ac638 | ||
|
|
6f8999ada5 | ||
|
|
ca96cae9cc | ||
|
|
1530cfe55f | ||
|
|
918f9045d3 | ||
|
|
d83dc719cf | ||
|
|
a1ed273f7f | ||
|
|
c104445880 | ||
|
|
e11d838189 | ||
|
|
8140eb0e7c | ||
|
|
4c09e9792b | ||
|
|
6f14cc83af | ||
|
|
88d81461e2 | ||
|
|
f1ce61263c | ||
|
|
59594b8622 | ||
|
|
945a766f0c | ||
|
|
66e10facc1 | ||
|
|
11de9b4c07 | ||
|
|
d5a3b48d62 | ||
|
|
8289bc7d5b | ||
|
|
c6cd132de9 | ||
|
|
a80017cf6c | ||
|
|
c2dcd29d81 | ||
|
|
3f51f3c61d | ||
|
|
7744a08212 | ||
|
|
7e63bf08ff | ||
|
|
2ebc323cdb | ||
|
|
c4f9b28b41 | ||
|
|
c5622c864e | ||
|
|
76c0c4f1da | ||
|
|
186ce23e11 | ||
|
|
68453f7374 | ||
|
|
168b598901 | ||
|
|
d109912798 | ||
|
|
55b3378e8c | ||
|
|
1f5cca2779 | ||
|
|
f525541969 | ||
|
|
9940a598ab | ||
|
|
4f0fa4e1b3 | ||
|
|
b7ca881bed | ||
|
|
03fc9d2e4e | ||
|
|
f3829346bc | ||
|
|
160b986322 | ||
|
|
6ef85ecd69 | ||
|
|
fd11d71f08 | ||
|
|
4634149084 | ||
|
|
92566fcdc4 | ||
|
|
44758f46fb | ||
|
|
aac958a119 | ||
|
|
559e85a597 | ||
|
|
f94062a9d5 | ||
|
|
f5d9766868 | ||
|
|
538cc32766 | ||
|
|
f2c8b792d6 | ||
|
|
1ad30d488c | ||
|
|
493dede919 | ||
|
|
9f0995c4b7 | ||
|
|
df048c9922 | ||
|
|
e48aa7e68e | ||
|
|
d0e3a93672 | ||
|
|
c202937302 | ||
|
|
239e96e8f2 | ||
|
|
f72cfbff34 | ||
|
|
388f014fc3 | ||
|
|
42c6ad56bb | ||
|
|
04dcedbc38 | ||
|
|
f58ec094ab | ||
|
|
553fac369e | ||
|
|
47455021ee | ||
|
|
5cb96ce440 | ||
|
|
b7b9097557 | ||
|
|
657f34ae5e | ||
|
|
38c6561252 | ||
|
|
de45b55cf8 | ||
|
|
d02b89c626 | ||
|
|
9e79c6bd97 | ||
|
|
1cc5f055f7 | ||
|
|
67739e4020 | ||
|
|
97ef518895 | ||
|
|
260b416ae4 | ||
|
|
26417cc670 | ||
|
|
43a41233b2 | ||
|
|
3e5eceb1b3 | ||
|
|
839cf7fba4 | ||
|
|
d3a0684014 | ||
|
|
3b1f795714 | ||
|
|
129ce4ca25 | ||
|
|
09b5e7b136 | ||
|
|
531a069a6e | ||
|
|
0cb9be7c08 | ||
|
|
e20a48712b | ||
|
|
2afa815b2f | ||
|
|
3765d2337b | ||
|
|
37911358ac | ||
|
|
31aab55ace | ||
|
|
4853baaabc | ||
|
|
02eaeb7af1 | ||
|
|
7ef97adb9c | ||
|
|
24a4c57deb | ||
|
|
cfa93f1c46 | ||
|
|
0fc98f2a92 | ||
|
|
74f84c16fa | ||
|
|
71d5a1d9dc | ||
|
|
fc4d296dc7 | ||
|
|
cb84c5c152 | ||
|
|
495bee0480 | ||
|
|
7dbc7dc885 | ||
|
|
ce36e25da9 | ||
|
|
68dc7447f4 | ||
|
|
844d739379 | ||
|
|
cb98f1a4de | ||
|
|
419d000897 | ||
|
|
8b807a7d25 | ||
|
|
46100ba24b | ||
|
|
fce85e68ee | ||
|
|
0ec93fc6fe | ||
|
|
ebb3e413dd | ||
|
|
8f8b46a9b6 | ||
|
|
e179dadf70 | ||
|
|
a8847c22ef | ||
|
|
ae1c9b3739 | ||
|
|
64cbabd1c2 | ||
|
|
1be2f42791 | ||
|
|
f6a1faab96 | ||
|
|
1549e318a5 | ||
|
|
c39070da4a | ||
|
|
a43b23b619 | ||
|
|
006619920e | ||
|
|
c0d7206249 | ||
|
|
31ffe60052 | ||
|
|
d330a67659 | ||
|
|
e08bd375ae | ||
|
|
8ad76ba496 | ||
|
|
4347ff235f | ||
|
|
47755d541d | ||
|
|
dfa1cc48c9 | ||
|
|
9090bf0c06 | ||
|
|
7db15cf892 | ||
|
|
055c534b4e | ||
|
|
80354c8a72 | ||
|
|
02b3ebcd27 | ||
|
|
cce40a1d67 | ||
|
|
e121edced2 | ||
|
|
1e45ddd79c | ||
|
|
ec358321da | ||
|
|
c3bfcbcd8d | ||
|
|
58e7cc77d0 | ||
|
|
0b03c18b06 | ||
|
|
3e6b1a8a35 | ||
|
|
ceb44d9c2e | ||
|
|
d1fb1516fa | ||
|
|
ef786e1fe8 | ||
|
|
038fd097b6 | ||
|
|
5f8de6e871 | ||
|
|
b4bc47d5d9 | ||
|
|
0cd2577abe | ||
|
|
8eba47816f | ||
|
|
c33f01d8f7 | ||
|
|
89abbf1038 | ||
|
|
14babd8c2a | ||
|
|
8685cc602f | ||
|
|
02697891e2 | ||
|
|
a7a9aabce7 | ||
|
|
fcd453ac29 | ||
|
|
00452c2302 | ||
|
|
c5dd983949 | ||
|
|
f4ff2b0000 | ||
|
|
9583cdea28 | ||
|
|
bef61116fd | ||
|
|
c76f7b8b98 | ||
|
|
f4c9c12047 | ||
|
|
e9735738f7 | ||
|
|
c7268cff3b | ||
|
|
a1fccc12b6 | ||
|
|
e304b8ee21 | ||
|
|
571f1d2fbf | ||
|
|
9963961f35 | ||
|
|
67857bde2f | ||
|
|
e129da8cc2 | ||
|
|
15feb9a120 | ||
|
|
f737c302e7 | ||
|
|
7a0bb1ff7b | ||
|
|
a6d52f8244 | ||
|
|
7254528d53 | ||
|
|
f300afe139 | ||
|
|
3bfa5a8178 | ||
|
|
25fca59248 | ||
|
|
f14baa218e | ||
|
|
ca522db7bf | ||
|
|
9e7ea0b65f | ||
|
|
beba773e52 | ||
|
|
41c87636e0 | ||
|
|
cfccffca39 | ||
|
|
c69f73b9b2 | ||
|
|
9b83b2853e | ||
|
|
212dcfa592 | ||
|
|
d96df500a7 | ||
|
|
1fe9e87ead | ||
|
|
f35ea9430c | ||
|
|
92b04dfba7 | ||
|
|
941fca8b0d | ||
|
|
8bec3b42b0 | ||
|
|
7db50a1a62 | ||
|
|
895cab981b | ||
|
|
d7a445b1bb | ||
|
|
a55a144a26 | ||
|
|
83ce5d2d4d | ||
|
|
1829509800 | ||
|
|
6a5b3c9d1a | ||
|
|
f10982aa7d | ||
|
|
9ec3914aa3 | ||
|
|
b7351b0f69 | ||
|
|
12b66968dc | ||
|
|
eef00ff35c | ||
|
|
9d5728cdcd | ||
|
|
b22c1f8113 | ||
|
|
620c4858a3 | ||
|
|
cd93924159 | ||
|
|
0f4d1ce303 | ||
|
|
a04924955a | ||
|
|
51e19d104b | ||
|
|
2b07d88b62 | ||
|
|
c6b1346956 | ||
|
|
05c537d1e7 | ||
|
|
4db15a7e80 | ||
|
|
2639a46a5e | ||
|
|
6eba03dd58 | ||
|
|
f73006a24f | ||
|
|
3315f54978 | ||
|
|
5b9a45674d | ||
|
|
54e02fd6b5 | ||
|
|
379ca660ae | ||
|
|
901ca7f2ed | ||
|
|
2e013397e3 | ||
|
|
fce2da691d | ||
|
|
f8151580de | ||
|
|
3a82be2c1f | ||
|
|
9f7994772b | ||
|
|
bd285b1731 | ||
|
|
43ed189b35 | ||
|
|
5c05f7c438 | ||
|
|
e9128e8c54 | ||
|
|
00701ca474 | ||
|
|
a4054c478c | ||
|
|
f38805c3d3 | ||
|
|
706c48a410 | ||
|
|
5a00bddd7c | ||
|
|
626763d452 | ||
|
|
bdebc1525f | ||
|
|
e75881c501 | ||
|
|
e4ae45e863 | ||
|
|
0bb7f1a53e | ||
|
|
8cee4161de | ||
|
|
9b842e9578 | ||
|
|
3d8dca0588 | ||
|
|
0b08caf16d | ||
|
|
5cd4ff8641 | ||
|
|
f948f8cb98 | ||
|
|
113ad1c6f9 | ||
|
|
555d24698b | ||
|
|
917eb78255 | ||
|
|
ee48c2ee3b | ||
|
|
9830f4ef48 | ||
|
|
68921b625d | ||
|
|
18d02e01e1 | ||
|
|
30a8c06295 | ||
|
|
e56df793d4 | ||
|
|
02ef52c677 | ||
|
|
a33bf9c889 | ||
|
|
7c0082ecb4 | ||
|
|
c6f35a953a | ||
|
|
98c3b2a4a1 | ||
|
|
462a38d0ca | ||
|
|
72199bb7d7 | ||
|
|
86efbda8d8 | ||
|
|
a5acfee7c3 | ||
|
|
9603ac1ccc | ||
|
|
a01034cbfc | ||
|
|
4a1fa3a65a | ||
|
|
3bd5e56e3f | ||
|
|
74cadb15a3 | ||
|
|
9ec1860f02 | ||
|
|
46d41d9c3d | ||
|
|
cde3f535c7 | ||
|
|
3a62b42414 | ||
|
|
ca925c49e0 | ||
|
|
5e6a8cb69b | ||
|
|
88fe633b87 | ||
|
|
3312478f71 | ||
|
|
60a58a8c9c | ||
|
|
2b629415a4 | ||
|
|
d13460812d | ||
|
|
a9319ea83e | ||
|
|
69f1b58bb0 | ||
|
|
df1e39edb2 | ||
|
|
71ebb23ec3 | ||
|
|
e7ee1faaba | ||
|
|
d97a8a3210 | ||
|
|
297f8442b9 | ||
|
|
398ba7a424 | ||
|
|
261f92e2e5 | ||
|
|
1ba595aab3 | ||
|
|
710cebe695 | ||
|
|
e9d2d594ab | ||
|
|
f14876dfff | ||
|
|
b1de7a9100 | ||
|
|
40b565947b | ||
|
|
e8220b3781 | ||
|
|
2e0f4f9161 | ||
|
|
8d27acd15a | ||
|
|
95c0452bcb | ||
|
|
68e2495d9c | ||
|
|
3b515229bb | ||
|
|
439aa861e7 | ||
|
|
35c083122e | ||
|
|
46bb485621 | ||
|
|
226c0ccdfc | ||
|
|
fda5851e7c | ||
|
|
7e2878aba7 | ||
|
|
9469dc5891 | ||
|
|
6b2f1281ae | ||
|
|
826e303ed0 | ||
|
|
d4e82b0aa4 | ||
|
|
bc6f496eb5 | ||
|
|
05d74786f9 | ||
|
|
0f72384ed9 | ||
|
|
f4b84d55e2 | ||
|
|
4000c990f0 | ||
|
|
62dd13b2e7 | ||
|
|
68c1e42356 | ||
|
|
2445b47661 | ||
|
|
a8534a7bd7 | ||
|
|
03bdf35dd3 | ||
|
|
d64e4ba2fc | ||
|
|
48e7faa521 | ||
|
|
8ca12286f6 | ||
|
|
f8c5bc5dec | ||
|
|
ca438d9740 | ||
|
|
702e49eaf7 | ||
|
|
e8b3a1fff1 | ||
|
|
c9d8afe434 | ||
|
|
2f9e6cc0d0 | ||
|
|
84ba085a0a | ||
|
|
b7a47ee001 | ||
|
|
e4cc5b84f1 | ||
|
|
f00360d553 | ||
|
|
952f7ed5e3 | ||
|
|
7bd6030185 | ||
|
|
76b83c623e | ||
|
|
20d9037740 | ||
|
|
1fb6369f61 | ||
|
|
1083ac9bac | ||
|
|
793eeebfb3 | ||
|
|
1183f5d1e1 | ||
|
|
bddf35f365 | ||
|
|
bf0e70746d | ||
|
|
c22e1e8bb1 | ||
|
|
4fae6e4731 | ||
|
|
c52ba54077 | ||
|
|
c2336ceece | ||
|
|
01f8d382e4 | ||
|
|
2f0a3459ad | ||
|
|
0f5ec6f129 | ||
|
|
5d13ff7c3a | ||
|
|
d7b05bc6b4 | ||
|
|
f4eb589f19 | ||
|
|
725c452157 | ||
|
|
01f9f1056c | ||
|
|
9ea7ee6f57 | ||
|
|
480265872d | ||
|
|
929e72c8a7 | ||
|
|
165bf096a7 | ||
|
|
11cc57b59d | ||
|
|
8d7f3ab284 | ||
|
|
4bb23e9687 | ||
|
|
01f32ba33e | ||
|
|
5ec5a5828d | ||
|
|
e30f50019d | ||
|
|
2d463d103a | ||
|
|
2dfbf77de9 | ||
|
|
5f62015c2d | ||
|
|
3004d8d43d | ||
|
|
b2e1f9bfdc | ||
|
|
a5ed209fe9 | ||
|
|
6a4dbe8446 | ||
|
|
a5c1a2da88 | ||
|
|
a4b623d734 | ||
|
|
66a588efd6 | ||
|
|
e0ce3a6774 | ||
|
|
4c42c961ac | ||
|
|
1c01e74d3a | ||
|
|
2d128620c6 | ||
|
|
4e0ce25794 | ||
|
|
87e21bf6e7 | ||
|
|
885b39acf0 | ||
|
|
d447d2cad9 | ||
|
|
cba4c16fb6 | ||
|
|
7343fc7a50 | ||
|
|
60c7f51747 | ||
|
|
cb3ed425c8 | ||
|
|
7e05690ad8 | ||
|
|
aa3d53f800 | ||
|
|
fc3d445c3f | ||
|
|
e8520b665c | ||
|
|
97c3680013 | ||
|
|
ea7f21a392 | ||
|
|
c65e98fd86 | ||
|
|
e0d966ffdd | ||
|
|
3fa7a72e9d | ||
|
|
50f4657c0a | ||
|
|
d905f5209b | ||
|
|
dc099ae621 | ||
|
|
5f3ff4904e | ||
|
|
f6d3494d66 | ||
|
|
05ae323d0f | ||
|
|
93bed0a54a | ||
|
|
24a65dc8ff | ||
|
|
269fd36df4 | ||
|
|
a708ffd037 | ||
|
|
5aaab11022 | ||
|
|
6c01e8039c | ||
|
|
6c5b577b88 | ||
|
|
6be428b248 | ||
|
|
840ed0097a | ||
|
|
fc6653d780 | ||
|
|
bccf9f9166 | ||
|
|
f740789dba | ||
|
|
7059b2f6a6 | ||
|
|
298c9dc694 | ||
|
|
c1f1121512 | ||
|
|
5cb498856b | ||
|
|
08f2fee638 | ||
|
|
236f120cba | ||
|
|
00a6697680 | ||
|
|
adc401206d | ||
|
|
54c4f674c8 | ||
|
|
b32aab82db | ||
|
|
a6d9988151 | ||
|
|
5c12fc6cde | ||
|
|
21708b3a15 | ||
|
|
79ec86489b | ||
|
|
d127ee9954 | ||
|
|
21f862b325 | ||
|
|
e431e59d78 | ||
|
|
5221d4a4cf | ||
|
|
ed41966300 | ||
|
|
f61e070d88 | ||
|
|
ca3a71a4dc | ||
|
|
38e0eedcc6 | ||
|
|
230668fa05 | ||
|
|
771cfd76b1 | ||
|
|
5a4e5288f0 | ||
|
|
d47af70e51 | ||
|
|
fc95bf1ac9 | ||
|
|
0f334f5da2 | ||
|
|
23523189cb | ||
|
|
1d7b913a1a | ||
|
|
82e230c257 | ||
|
|
f3f5a901a5 | ||
|
|
9c5773d94d | ||
|
|
618866234e | ||
|
|
8d00795640 | ||
|
|
90aded2bad | ||
|
|
2f72b0d173 | ||
|
|
aa91bf7f2d | ||
|
|
6c81a67a65 | ||
|
|
15873b8448 | ||
|
|
cf7a19adef | ||
|
|
6ad00195f5 | ||
|
|
262aec97ae | ||
|
|
66417a1b72 | ||
|
|
9c193d432c | ||
|
|
4df4fab08f | ||
|
|
30414dccf5 | ||
|
|
b02b6144ac | ||
|
|
0ba1cab5a0 | ||
|
|
790717db9a | ||
|
|
7bcd730c5f | ||
|
|
af2897fe12 | ||
|
|
935126b14c | ||
|
|
64f45b3743 | ||
|
|
cf6b6ca787 | ||
|
|
606211a8d5 | ||
|
|
ec215ff4df | ||
|
|
d162a1a930 | ||
|
|
a8f3712d12 | ||
|
|
0bdc4795a8 | ||
|
|
1eb62e8380 | ||
|
|
653c87e145 | ||
|
|
aa06cd8bbd | ||
|
|
d137b1e5fa | ||
|
|
99e026a7e0 | ||
|
|
6cccace846 | ||
|
|
5f8b058cd3 | ||
|
|
f0cf691229 | ||
|
|
2f5f122053 | ||
|
|
ff38d2aec9 | ||
|
|
5ee4fd1e4b | ||
|
|
34b126b370 | ||
|
|
2c6b8aed4f | ||
|
|
5e94142e7c | ||
|
|
14f2f1a967 | ||
|
|
2f637887e3 | ||
|
|
fb4669eaca | ||
|
|
6f62dc6709 | ||
|
|
9c76819466 | ||
|
|
a605b9f822 | ||
|
|
4461e9c3c7 | ||
|
|
d07fd8798b | ||
|
|
3e0a08c2ee | ||
|
|
b7b8fff4d5 | ||
|
|
d904b31783 | ||
|
|
d1d9eaa14e | ||
|
|
dd6db1a571 | ||
|
|
4d6613736d | ||
|
|
67146f2096 | ||
|
|
53d53bb648 | ||
|
|
f615b75d6b | ||
|
|
1e4beda09a | ||
|
|
02a1eb4e27 | ||
|
|
66d7cf802e | ||
|
|
9def164db3 | ||
|
|
7f5e356132 | ||
|
|
cd83d97091 | ||
|
|
8a75b88abd | ||
|
|
3468c2378b | ||
|
|
ec6b02ab95 | ||
|
|
eab586b73b | ||
|
|
7c952d83ac | ||
|
|
7c2bd760dd | ||
|
|
4e2e94770c | ||
|
|
ce170cad74 | ||
|
|
0d15f92487 | ||
|
|
ef8f4e2dac | ||
|
|
f7e3136845 | ||
|
|
1d35df58eb | ||
|
|
cb85eca708 | ||
|
|
2089c893fe | ||
|
|
835601fd48 | ||
|
|
4666e25404 | ||
|
|
7b235adab2 | ||
|
|
cb463cc70f | ||
|
|
ef0bdc6986 | ||
|
|
8a4ae0751d | ||
|
|
237b0e8e6a | ||
|
|
7ffaf1cac8 | ||
|
|
ba5550aebb | ||
|
|
870d858018 | ||
|
|
514bad2cbd | ||
|
|
dbce597de7 | ||
|
|
765e22db47 | ||
|
|
175ed545b1 | ||
|
|
7cfa96b72d | ||
|
|
44a8f72c28 | ||
|
|
243abbe280 | ||
|
|
8c45ffff6a | ||
|
|
3453fbfa2f | ||
|
|
8ff462f831 | ||
|
|
cb2a90ef2b | ||
|
|
99be56f9b8 | ||
|
|
5dde1afc38 | ||
|
|
77b766d669 | ||
|
|
cc10ea92c1 | ||
|
|
934e8369b6 | ||
|
|
5e60f6708e | ||
|
|
c5d459bd10 | ||
|
|
e0955c6019 | ||
|
|
f9325f5811 | ||
|
|
592bae3f87 | ||
|
|
ba938a2aeb | ||
|
|
3e59075d53 | ||
|
|
0a165becfe | ||
|
|
93f042da4c | ||
|
|
1b6d39f998 | ||
|
|
daa2bcc1fe | ||
|
|
07fdd581d8 | ||
|
|
d017999722 | ||
|
|
8aaa0105c3 | ||
|
|
d8746e116d | ||
|
|
a2cebb7fcb | ||
|
|
f48ebb3961 | ||
|
|
76ea95acf5 | ||
|
|
ca9c3fdcf4 | ||
|
|
145c7a2285 | ||
|
|
955f30a2a4 | ||
|
|
edfe60d416 | ||
|
|
76372f82f4 | ||
|
|
a57d769687 | ||
|
|
01798b717f | ||
|
|
710fa81c27 | ||
|
|
2f64af811e | ||
|
|
9497fc853e | ||
|
|
c223ae604a | ||
|
|
cad0065e74 | ||
|
|
29fa352663 | ||
|
|
4efb3c1eae | ||
|
|
44f512a90c | ||
|
|
9b61ace59d | ||
|
|
47ffda304d | ||
|
|
cc73673305 | ||
|
|
6602332094 | ||
|
|
8d90933c01 | ||
|
|
fd889d8613 | ||
|
|
4fd71fc295 | ||
|
|
ec053b2586 | ||
|
|
61908ce94f | ||
|
|
282206e3ff | ||
|
|
99a3979c68 | ||
|
|
7ddb202d70 | ||
|
|
ae339d3c59 | ||
|
|
db18395f48 | ||
|
|
bc30771243 | ||
|
|
427ad8dcf1 | ||
|
|
e24f30d86c | ||
|
|
445b50ce77 | ||
|
|
0c7100ca7d | ||
|
|
0324241ea1 | ||
|
|
5c4bb91592 | ||
|
|
4bfd933522 | ||
|
|
93a7bb6333 | ||
|
|
517c5eab0d | ||
|
|
9d34da7c13 | ||
|
|
6b29a31ae9 | ||
|
|
4523352e05 | ||
|
|
d34c7a5730 | ||
|
|
faf04f65c6 | ||
|
|
76d21f0e99 | ||
|
|
b021fe49bf | ||
|
|
5aabcfa38d | ||
|
|
d0c7d4d3d1 | ||
|
|
47b21253ec | ||
|
|
73377ca504 | ||
|
|
ce16d32e81 | ||
|
|
a4bcdefbd5 | ||
|
|
41d04687d9 | ||
|
|
d83a2eb130 | ||
|
|
589f540b28 | ||
|
|
87f36584c2 | ||
|
|
1e831e2266 | ||
|
|
79ac1effd6 | ||
|
|
2a58061641 | ||
|
|
ce26218d1f | ||
|
|
997c8ef8f9 | ||
|
|
0503c59198 | ||
|
|
5b09bda567 | ||
|
|
e4c1acec4e | ||
|
|
9198e224ef | ||
|
|
57a156d9bd | ||
|
|
952452aa38 | ||
|
|
08d9c7da5b | ||
|
|
9447a054a3 | ||
|
|
003b9d347c | ||
|
|
ed5ace118b | ||
|
|
726b8b65b6 | ||
|
|
f064d4415f | ||
|
|
f3f78697f5 | ||
|
|
333c95faac | ||
|
|
2e0fa08605 | ||
|
|
e38f3cfe52 | ||
|
|
002c97402c | ||
|
|
826e489fe1 | ||
|
|
fdb6ef2edf | ||
|
|
4bf8f08a55 | ||
|
|
6895314ae0 | ||
|
|
6ee4267be6 | ||
|
|
de7113b428 | ||
|
|
3aea2c209f | ||
|
|
fe9e7e9eae | ||
|
|
5b0a8cf468 | ||
|
|
ee38c185ce | ||
|
|
0e73acda14 | ||
|
|
9588bbe5ff | ||
|
|
72f8e122fa | ||
|
|
ea4d8f0056 | ||
|
|
c8d4693fea | ||
|
|
5004c2cbe3 | ||
|
|
5afd95a22d | ||
|
|
a1368cd51a | ||
|
|
4d618ac374 | ||
|
|
8cf1bd68bc | ||
|
|
ba22982704 | ||
|
|
c1b8aa2161 | ||
|
|
bb304bb66f | ||
|
|
4d1f47308b | ||
|
|
2afe2add5f | ||
|
|
34edd3497e |
13
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [phil-opp] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
custom: ['https://donorbox.org/phil-opp'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
patreon: phil_opp # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: phil-opp # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
|
||||
107
.github/workflows/blog.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
name: Blog
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
- '!staging.tmp'
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 0 1/4 * *' # every 4 days
|
||||
|
||||
jobs:
|
||||
build_site:
|
||||
name: "Zola Build"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: 'Download Zola'
|
||||
run: curl -sL https://github.com/getzola/zola/releases/download/v0.19.0/zola-v0.19.0-x86_64-unknown-linux-gnu.tar.gz | tar zxv
|
||||
- name: 'Install Python Libraries'
|
||||
run: python -m pip install --user -r requirements.txt
|
||||
working-directory: "blog"
|
||||
|
||||
- name: "Run before_build.py script"
|
||||
run: python before_build.py
|
||||
working-directory: "blog"
|
||||
- name: "Build Site"
|
||||
run: ../zola build
|
||||
working-directory: "blog"
|
||||
|
||||
- name: Upload Generated Site
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: generated_site
|
||||
path: blog/public
|
||||
|
||||
check_spelling:
|
||||
name: "Check Spelling"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Typo Check
|
||||
uses: crate-ci/typos@v1.1.9
|
||||
with:
|
||||
files: blog
|
||||
|
||||
deploy_site:
|
||||
name: "Deploy Generated Site"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_site, check_spelling]
|
||||
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'schedule')
|
||||
|
||||
steps:
|
||||
- name: "Download Generated Site"
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: generated_site
|
||||
path: generated_site
|
||||
|
||||
- name: Setup SSH Keys and known_hosts
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
ssh-agent -a $SSH_AUTH_SOCK > /dev/null
|
||||
ssh-add - <<< "$deploy_key"
|
||||
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> $GITHUB_ENV
|
||||
env:
|
||||
SSH_AUTH_SOCK: /tmp/ssh_agent.sock
|
||||
deploy_key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
|
||||
- name: "Clone blog_os_deploy Repo"
|
||||
run: git clone git@github.com:phil-opp/blog_os_deploy.git --branch gh-pages
|
||||
|
||||
- name: "Set Up Git Identity"
|
||||
run: |
|
||||
git config --local user.name "GitHub Actions Deploy"
|
||||
git config --local user.email "github-actions-deploy@phil-opp.com"
|
||||
working-directory: "blog_os_deploy"
|
||||
|
||||
- name: "Delete Old Content"
|
||||
run: "rm -r ./*"
|
||||
working-directory: "blog_os_deploy"
|
||||
|
||||
- name: "Add New Content"
|
||||
run: cp -r generated_site/* blog_os_deploy
|
||||
|
||||
- name: "Commit New Content"
|
||||
run: |
|
||||
git add .
|
||||
git commit --allow-empty -m "Deploy ${GITHUB_SHA}
|
||||
|
||||
Deploy of commit https://github.com/phil-opp/blog_os/commit/${GITHUB_SHA}"
|
||||
working-directory: "blog_os_deploy"
|
||||
|
||||
- name: "Show Changes"
|
||||
run: "git show"
|
||||
working-directory: "blog_os_deploy"
|
||||
|
||||
- name: "Push Changes"
|
||||
run: "git push"
|
||||
working-directory: "blog_os_deploy"
|
||||
27
.github/workflows/check-links.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Check Links
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "*"
|
||||
- "!staging.tmp"
|
||||
tags:
|
||||
- "*"
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: "0 0 1/4 * *" # every 4 days
|
||||
|
||||
jobs:
|
||||
zola_check:
|
||||
name: "Zola Link Check"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: "Download Zola"
|
||||
run: curl -sL https://github.com/getzola/zola/releases/download/v0.19.0/zola-v0.19.0-x86_64-unknown-linux-gnu.tar.gz | tar zxv
|
||||
|
||||
- name: "Run zola check"
|
||||
run: ../zola check
|
||||
working-directory: "blog"
|
||||
33
.github/workflows/scheduled-builds.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Build code on schedule
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '40 1 * * *' # every day at 1:40
|
||||
|
||||
jobs:
|
||||
trigger-build:
|
||||
name: Trigger Build
|
||||
strategy:
|
||||
matrix:
|
||||
branch: [
|
||||
post-01,
|
||||
post-02,
|
||||
post-03,
|
||||
post-04,
|
||||
post-05,
|
||||
post-06,
|
||||
post-07,
|
||||
post-08,
|
||||
post-09,
|
||||
post-10,
|
||||
post-11,
|
||||
post-12,
|
||||
]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Invoke workflow
|
||||
uses: benc-uk/workflow-dispatch@v1.1
|
||||
with:
|
||||
workflow: Code
|
||||
token: ${{ secrets.SCHEDULED_BUILDS_TOKEN }}
|
||||
ref: ${{ matrix.branch }}
|
||||
15
.gitignore
vendored
@@ -1,14 +1 @@
|
||||
# Compiled files
|
||||
*.o
|
||||
*.so
|
||||
*.rlib
|
||||
*.dll
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
|
||||
# Generated by Cargo
|
||||
/target/
|
||||
|
||||
# Build directory
|
||||
/build/
|
||||
code
|
||||
|
||||
24
.travis.yml
@@ -1,24 +0,0 @@
|
||||
language: rust
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
|
||||
sudo: false
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: change
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- nasm
|
||||
|
||||
script: bash scripts/travis-build.sh
|
||||
|
||||
after_success: bash scripts/travis-trigger-hugo-build.sh
|
||||
|
||||
env:
|
||||
global:
|
||||
secure: YQvZpLe32k8N+vLMa8PB80LKJdiD4WEUuw2lPFk3AJLSK6dojWq8MKjGuedxLvtmjURu401m7D06NFCjwd6j0dpR4L2xmVJ/EgufP2Wc62BUa2XvIIU/zURv0dFpGvaOTyHKl4j46SIk2LDugIzB1WyYRqeYTCpiN0xHuWsywXjPpCCJ04ftxHwrjYjzA8vvtyehjMeN4HC3J9r6anFlN9Ka7RFVSQ0Bun79pd6Xa/OPIxTsZuw24Ru4v458e4QxVh0atJif0lqmu5tZAeR/S0FTnG3XlKqfIYDmDWKJVKUwk6AHnsgaYAFASQhi9XOcr+cMir38/8k8FQpx80hjqkWXCZafUWuypgQnZOKS8K1oZLmMAQnrssM8HbMZEIYH40I8G5MqcpgPUvdwFO2PMGRNimMk6bEvrtheNwZn1XBTxgNOW4huCBoR0/E95FFGiTH5HL2kZi5N5+1EGJMdhPGjybIdJgawsAY2SOdw6rvHgf+ZMkJFBU2R9ftS88DHygMhkOifLDUq3GFPiFePNrE97LBFH3WN48fxPTcske00tAPTMcMGvw8zDBH4tZAHQikH6oMZFzKNQ0GvBTiKNvLMl1HaCcM/h1QmGXho4HtVD6PNrgOocdVxxXBcntQMVewkcr3hwDwaAZ/7+AqjeG9M+V2wEkI20Y4XYiq24HY=
|
||||
24
Cargo.toml
@@ -1,24 +0,0 @@
|
||||
[package]
|
||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||
name = "blog_os"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
bit_field = "0.1.0"
|
||||
bitflags = "0.7.0"
|
||||
once = "0.2.1"
|
||||
rlibc = "0.1.4"
|
||||
spin = "0.3.4"
|
||||
|
||||
[dependencies.hole_list_allocator]
|
||||
path = "libs/hole_list_allocator"
|
||||
|
||||
[dependencies.multiboot2]
|
||||
git = "https://github.com/phil-opp/multiboot2-elf64"
|
||||
|
||||
[dependencies.x86]
|
||||
default-features = false
|
||||
version = "0.7.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
@@ -1,6 +1,6 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
@@ -174,28 +174,3 @@
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Philipp Oppermann
|
||||
Copyright (c) 2019 Philipp Oppermann
|
||||
|
||||
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:
|
||||
|
||||
|
||||
57
Makefile
@@ -1,57 +0,0 @@
|
||||
# Copyright 2015 Philipp Oppermann. See the README.md
|
||||
# file at the top-level directory of this distribution.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
arch ?= x86_64
|
||||
target ?= $(arch)-unknown-linux-gnu
|
||||
kernel := build/kernel-$(arch).bin
|
||||
iso := build/os-$(arch).iso
|
||||
|
||||
rust_os := target/$(target)/debug/libblog_os.a
|
||||
linker_script := src/arch/$(arch)/linker.ld
|
||||
grub_cfg := src/arch/$(arch)/grub.cfg
|
||||
assembly_source_files := $(wildcard src/arch/$(arch)/*.asm)
|
||||
assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \
|
||||
build/arch/$(arch)/%.o, $(assembly_source_files))
|
||||
|
||||
.PHONY: all clean run debug iso cargo gdb
|
||||
|
||||
all: $(kernel)
|
||||
|
||||
clean:
|
||||
@cargo clean
|
||||
@rm -rf build
|
||||
|
||||
run: $(iso)
|
||||
@qemu-system-x86_64 -cdrom $(iso) -s
|
||||
|
||||
debug: $(iso)
|
||||
@qemu-system-x86_64 -cdrom $(iso) -s -S
|
||||
|
||||
gdb:
|
||||
@rust-os-gdb/bin/rust-gdb "build/kernel-x86_64.bin" -ex "target remote :1234"
|
||||
|
||||
iso: $(iso)
|
||||
|
||||
$(iso): $(kernel) $(grub_cfg)
|
||||
@mkdir -p build/isofiles/boot/grub
|
||||
@cp $(kernel) build/isofiles/boot/kernel.bin
|
||||
@cp $(grub_cfg) build/isofiles/boot/grub
|
||||
@grub-mkrescue -o $(iso) build/isofiles 2> /dev/null
|
||||
@rm -r build/isofiles
|
||||
|
||||
$(kernel): cargo $(rust_os) $(assembly_object_files) $(linker_script)
|
||||
@ld -n --gc-sections -T $(linker_script) -o $(kernel) $(assembly_object_files) $(rust_os)
|
||||
|
||||
cargo:
|
||||
@cargo rustc --target $(target) -- -Z no-landing-pads
|
||||
|
||||
# compile assembly files
|
||||
build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm
|
||||
@mkdir -p $(shell dirname $@)
|
||||
@nasm -felf64 $< -o $@
|
||||
152
README.md
@@ -1,41 +1,131 @@
|
||||
# Blog OS
|
||||
[](https://travis-ci.org/phil-opp/blog_os)
|
||||
|
||||
This repository contains the source code for the _Writing an OS in Rust_ series at [os.phil-opp.com](http://os.phil-opp.com).
|
||||
This repository contains the source code for the _Writing an OS in Rust_ series at [os.phil-opp.com](https://os.phil-opp.com).
|
||||
|
||||
## Bare Bones
|
||||
- [A Minimal x86 Kernel](http://os.phil-opp.com/multiboot-kernel.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/multiboot_bootstrap))
|
||||
- [Entering Long Mode](http://os.phil-opp.com/entering-longmode.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/entering_longmode))
|
||||
- [Set Up Rust](http://os.phil-opp.com/set-up-rust.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/set_up_rust))
|
||||
- [Printing to Screen](http://os.phil-opp.com/printing-to-screen.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/printing_to_screen))
|
||||
If you have questions, open an issue or chat with us [on Gitter](https://gitter.im/phil-opp/blog_os).
|
||||
|
||||
## Memory Management
|
||||
- [Allocating Frames](http://os.phil-opp.com/allocating-frames.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/allocating_frames))
|
||||
- [Page Tables](http://os.phil-opp.com/modifying-page-tables.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/page_tables))
|
||||
- [Remap the Kernel](http://os.phil-opp.com/remap-the-kernel.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/remap_the_kernel))
|
||||
- [Kernel Heap](http://os.phil-opp.com/kernel-heap.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/kernel_heap))
|
||||
## Where is the code?
|
||||
|
||||
## Interrupts
|
||||
- [Catching Exceptions](http://os.phil-opp.com/catching-exceptions.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/catching_exceptions))
|
||||
The code for each post lives in a separate git branch. This makes it possible to see the intermediate state after each post.
|
||||
|
||||
## Additional Resources
|
||||
- [Cross Compile Binutils](http://os.phil-opp.com/cross-compile-binutils.html)
|
||||
- [Cross Compile libcore](http://os.phil-opp.com/cross-compile-libcore.html)
|
||||
- [Set Up GDB](http://os.phil-opp.com/set-up-gdb.html)
|
||||
**The code for the latest post is available [here][latest-post].**
|
||||
|
||||
## Building
|
||||
You need to have `nasm`, `grub-mkrescue`, `xorriso`, `qemu` and a nighly Rust compiler installed. Then you can run it using `make run`.
|
||||
[latest-post]: https://github.com/phil-opp/blog_os/tree/post-12
|
||||
|
||||
Please file an issue if you run into any problems.
|
||||
You can find the branch for each post by following the `(source code)` link in the [post list](#posts) below. The branches are named `post-XX` where `XX` is the post number, for example `post-03` for the _VGA Text Mode_ post or `post-07` for the _Hardware Interrupts_ post. For build instructions, see the Readme of the respective branch.
|
||||
|
||||
You can check out a branch in a subdirectory using [git worktree]:
|
||||
|
||||
[git worktree]: https://git-scm.com/docs/git-worktree
|
||||
|
||||
```
|
||||
git worktree add code post-10
|
||||
```
|
||||
|
||||
The above command creates a subdirectory named `code` that contains the code for the 10th post ("Heap Allocation").
|
||||
|
||||
## Posts
|
||||
|
||||
The goal of this project is to provide step-by-step tutorials in individual blog posts. We currently have the following set of posts:
|
||||
|
||||
**Bare Bones:**
|
||||
|
||||
- [A Freestanding Rust Binary](https://os.phil-opp.com/freestanding-rust-binary/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-01))
|
||||
- [A Minimal Rust Kernel](https://os.phil-opp.com/minimal-rust-kernel/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-02))
|
||||
- [VGA Text Mode](https://os.phil-opp.com/vga-text-mode/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-03))
|
||||
- [Testing](https://os.phil-opp.com/testing/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-04))
|
||||
|
||||
**Interrupts:**
|
||||
|
||||
- [CPU Exceptions](https://os.phil-opp.com/cpu-exceptions/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-05))
|
||||
- [Double Faults](https://os.phil-opp.com/double-fault-exceptions/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-06))
|
||||
- [Hardware Interrupts](https://os.phil-opp.com/hardware-interrupts/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-07))
|
||||
|
||||
**Memory Management:**
|
||||
|
||||
- [Introduction to Paging](https://os.phil-opp.com/paging-introduction/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-08))
|
||||
- [Paging Implementation](https://os.phil-opp.com/paging-implementation/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-09))
|
||||
- [Heap Allocation](https://os.phil-opp.com/heap-allocation/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-10))
|
||||
- [Allocator Designs](https://os.phil-opp.com/allocator-designs/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-11))
|
||||
|
||||
**Multitasking**:
|
||||
|
||||
- [Async/Await](https://os.phil-opp.com/async-await/)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/post-12))
|
||||
|
||||
## First Edition Posts
|
||||
|
||||
The current version of the blog is already the second edition. The first edition is outdated and no longer maintained, but might still be useful. The posts of the first edition are:
|
||||
|
||||
<details><summary><i>Click to expand</i></summary>
|
||||
|
||||
**Bare Bones:**
|
||||
|
||||
- [A Minimal x86 Kernel](https://os.phil-opp.com/multiboot-kernel.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_1))
|
||||
- [Entering Long Mode](https://os.phil-opp.com/entering-longmode.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_2))
|
||||
- [Set Up Rust](https://os.phil-opp.com/set-up-rust.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_3))
|
||||
- [Printing to Screen](https://os.phil-opp.com/printing-to-screen.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_4))
|
||||
|
||||
**Memory Management:**
|
||||
|
||||
- [Allocating Frames](https://os.phil-opp.com/allocating-frames.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_5))
|
||||
- [Page Tables](https://os.phil-opp.com/modifying-page-tables.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_6))
|
||||
- [Remap the Kernel](https://os.phil-opp.com/remap-the-kernel.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_7))
|
||||
- [Kernel Heap](https://os.phil-opp.com/kernel-heap.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_8))
|
||||
|
||||
**Exceptions:**
|
||||
|
||||
- [Handling Exceptions](https://os.phil-opp.com/handling-exceptions.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_9))
|
||||
- [Double Faults](https://os.phil-opp.com/double-faults.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/first_edition_post_10))
|
||||
|
||||
**Additional Resources:**
|
||||
|
||||
- [Cross Compile Binutils](https://os.phil-opp.com/cross-compile-binutils.html)
|
||||
- [Cross Compile libcore](https://os.phil-opp.com/cross-compile-libcore.html)
|
||||
- [Set Up GDB](https://os.phil-opp.com/set-up-gdb)
|
||||
- [Handling Exceptions using Naked Functions](https://os.phil-opp.com/handling-exceptions-with-naked-fns.html)
|
||||
- [Catching Exceptions](https://os.phil-opp.com/catching-exceptions.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/catching_exceptions))
|
||||
- [Better Exception Messages](https://os.phil-opp.com/better-exception-messages.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/better_exception_messages))
|
||||
- [Returning from Exceptions](https://os.phil-opp.com/returning-from-exceptions.html)
|
||||
([source code](https://github.com/phil-opp/blog_os/tree/returning_from_exceptions))
|
||||
|
||||
</details>
|
||||
|
||||
## License
|
||||
The source code is dual-licensed under MIT or the Apache License (Version 2.0). This excludes the `blog` directory.
|
||||
|
||||
This project, with exception of the `blog/content` folder, is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
https://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
For licensing of the `blog/content` folder, see the [`blog/content/README.md`](blog/content/README.md).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
2
blog/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/public
|
||||
zola
|
||||
89
blog/before_build.py
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import io
|
||||
import urllib
|
||||
import datetime
|
||||
from github import Github
|
||||
|
||||
g = Github()
|
||||
|
||||
one_month_ago = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=32)
|
||||
|
||||
def filter_date(issue):
|
||||
return issue.closed_at > one_month_ago
|
||||
|
||||
def format_number(number):
|
||||
if number > 1000:
|
||||
return u"{:.1f}k".format(float(number) / 1000)
|
||||
else:
|
||||
return u"{}".format(number)
|
||||
|
||||
with io.open("templates/auto/recent-updates.html", 'w', encoding='utf8') as recent_updates:
|
||||
recent_updates.truncate()
|
||||
|
||||
relnotes_issues = g.search_issues("is:merged", repo="phil-opp/blog_os", type="pr", label="relnotes")[:100]
|
||||
recent_relnotes_issues = list(filter(filter_date, relnotes_issues))
|
||||
|
||||
if len(recent_relnotes_issues) == 0:
|
||||
recent_updates.write(u"No notable updates recently.")
|
||||
else:
|
||||
recent_updates.write(u"<ul>\n")
|
||||
|
||||
for pr in sorted(recent_relnotes_issues, key=lambda issue: issue.closed_at, reverse=True):
|
||||
link = '<a href="' + pr.html_url + '">' + pr.title + "</a> "
|
||||
iso_date = pr.closed_at.isoformat()
|
||||
readable_date = pr.closed_at.strftime("%b %d")
|
||||
datetime_str = '<time datetime="' + iso_date + '">' + readable_date + '</time>'
|
||||
recent_updates.write(u" <li>" + link + datetime_str + "</li>\n")
|
||||
|
||||
recent_updates.write(u"</ul>")
|
||||
|
||||
repo = g.get_repo("phil-opp/blog_os")
|
||||
|
||||
with io.open("templates/auto/stars.html", 'w', encoding='utf8') as stars:
|
||||
stars.truncate()
|
||||
stars.write(format_number(repo.stargazers_count))
|
||||
|
||||
with io.open("templates/auto/forks.html", 'w', encoding='utf8') as forks:
|
||||
forks.truncate()
|
||||
forks.write(format_number(repo.forks_count))
|
||||
|
||||
|
||||
# query "This week in Rust OSDev posts"
|
||||
|
||||
lines = []
|
||||
year = 2020
|
||||
month = 4
|
||||
while True:
|
||||
url = "https://rust-osdev.com/this-month/" + str(year) + "-" + str(month).zfill(2) + "/"
|
||||
try:
|
||||
urllib.request.urlopen(url)
|
||||
except urllib.error.HTTPError as e:
|
||||
break
|
||||
|
||||
month_str = datetime.date(1900, month, 1).strftime('%B')
|
||||
|
||||
link = '<a href="' + url + '">This Month in Rust OSDev (' + month_str + " " + str(year) + ")</a> "
|
||||
lines.append(u" <li><b>" + link + "</b></li>\n")
|
||||
|
||||
month = month + 1
|
||||
if month > 12:
|
||||
month = 1
|
||||
year = year + 1
|
||||
|
||||
lines.reverse()
|
||||
|
||||
with io.open("templates/auto/status-updates.html", 'w', encoding='utf8') as status_updates:
|
||||
status_updates.truncate()
|
||||
|
||||
for line in lines:
|
||||
status_updates.write(line)
|
||||
|
||||
with io.open("templates/auto/status-updates-truncated.html", 'w', encoding='utf8') as status_updates:
|
||||
status_updates.truncate()
|
||||
|
||||
for index, line in enumerate(lines):
|
||||
if index == 5:
|
||||
break
|
||||
status_updates.write(line)
|
||||
281
blog/config.toml
Normal file
@@ -0,0 +1,281 @@
|
||||
base_url = "https://os.phil-opp.com"
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
|
||||
generate_feeds = true
|
||||
feed_filenames = ["rss.xml"]
|
||||
compile_sass = true
|
||||
minify_html = false
|
||||
|
||||
ignored_content = ["*/README.md", "*/LICENSE-CC-BY-NC"]
|
||||
|
||||
[markdown]
|
||||
highlight_code = true
|
||||
highlight_theme = "visual-studio-dark"
|
||||
smart_punctuation = true
|
||||
|
||||
[link_checker]
|
||||
skip_prefixes = [
|
||||
"https://crates.io/crates", # see https://github.com/rust-lang/crates.io/issues/788
|
||||
"https://www.amd.com/system/files/TechDocs/", # seems to have problems with PDFs
|
||||
"https://developer.apple.com/library/archive/qa/qa1118/_index.html", # results in a 401 (I don't know why)
|
||||
"https://github.com", # rate limiting often leads to "Error 429 Too Many Requests"
|
||||
"https://www.linkedin.com/", # seems to send invalid HTTP status codes
|
||||
]
|
||||
skip_anchor_prefixes = [
|
||||
"https://github.com/", # see https://github.com/getzola/zola/issues/805
|
||||
"https://docs.rs/x86_64/0.1.2/src/", # source code highlight
|
||||
"https://doc.rust-jp.rs/book-ja/", # seems like Zola has problems with Japanese anchor names
|
||||
"https://doc.rust-jp.rs/edition-guide/rust-2018", # seems like Zola has problems with Japanese anchor names
|
||||
"https://doc.rust-jp.rs/rust-nomicon-ja/", # seems like Zola has problems with Japanese anchor names
|
||||
]
|
||||
|
||||
[extra]
|
||||
subtitle = "Philipp Oppermann's blog"
|
||||
author = { name = "Philipp Oppermann" }
|
||||
default_language = "en"
|
||||
languages = ["en", "zh-CN", "zh-TW", "fr", "ja", "fa", "ru", "ko", "ar", "es"]
|
||||
|
||||
[translations]
|
||||
lang_name = "English (original)"
|
||||
toc = "Table of Contents"
|
||||
all_posts = "« All Posts"
|
||||
comments = "Comments"
|
||||
comments_notice = "Please leave your comments in English if possible."
|
||||
readmore = "read more »"
|
||||
not_translated = "(This post is not translated yet.)"
|
||||
translated_content = "Translated Content:"
|
||||
translated_content_notice = "This is a community translation of the <strong><a href=\"_original.permalink_\">_original.title_</a></strong> post. It might be incomplete, outdated or contain errors. Please report any issues!"
|
||||
translated_by = "Translation by"
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "and"
|
||||
support_me = """
|
||||
<h2>Support Me</h2>
|
||||
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
|
||||
"""
|
||||
|
||||
# Chinese (simplified)
|
||||
[languages.zh-CN]
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
[languages.zh-CN.translations]
|
||||
lang_name = "Chinese (simplified)"
|
||||
toc = "目录"
|
||||
all_posts = "« 所有文章"
|
||||
comments = "评论"
|
||||
comments_notice = "请尽可能使用英语评论。"
|
||||
readmore = "更多 »"
|
||||
not_translated = "(该文章还没有被翻译。)"
|
||||
translated_content = "翻译内容:"
|
||||
translated_content_notice = "这是对原文章 <strong><a href=\"_original.permalink_\">_original.title_</a></strong> 的社区中文翻译。它可能不完整,过时或者包含错误。可以在 <a href=\"https://github.com/phil-opp/blog_os/issues/961\">这个 Issue</a> 上评论和提问!"
|
||||
translated_by = "翻译者:"
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "和"
|
||||
support_me = """
|
||||
<h2>支持我</h2>
|
||||
<p>创建和维护这个博客以及相关的库带来了十分庞大的工作量,即便我十分热爱它们,仍然需要你们的支持。通过赞助我,可以让我有能投入更多时间与精力在创造新内容,开发新功能上。赞助我最好的办法是通过<a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. 十分感谢各位!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
你有问题需要解决,想要分享反馈,或者讨论更多的想法吗?请随时在这里留下评论!请使用尽量使用英文并遵循 Rust 的 <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. 这个讨论串将与 <a href="_discussion_url_"><em>discussion on GitHub</em></a> 直接连接,所以你也可以直接在那边发表评论
|
||||
"""
|
||||
|
||||
# Chinese (traditional)
|
||||
[languages.zh-TW]
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
[languages.zh-TW.translations]
|
||||
lang_name = "Chinese (traditional)"
|
||||
toc = "目錄"
|
||||
all_posts = "« 所有文章"
|
||||
comments = "評論"
|
||||
comments_notice = "請儘可能使用英語評論。"
|
||||
readmore = "更多 »"
|
||||
not_translated = "(該文章還沒有被翻譯。)"
|
||||
translated_content = "翻譯內容:"
|
||||
translated_content_notice = "這是對原文章 <strong><a href=\"_original.permalink_\">_original.title_</a></strong> 的社區中文翻譯。它可能不完整,過時或者包含錯誤。可以在 <a href=\"https://github.com/phil-opp/blog_os/issues/961\">這個 Issue</a> 上評論和提問!"
|
||||
translated_by = "翻譯者:"
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "和"
|
||||
support_me = """
|
||||
<h2>Support Me</h2>
|
||||
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
|
||||
"""
|
||||
|
||||
# Japanese
|
||||
[languages.ja]
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
[languages.ja.translations]
|
||||
lang_name = "Japanese"
|
||||
toc = "目次"
|
||||
all_posts = "« すべての記事へ"
|
||||
comments = "コメント"
|
||||
comments_notice = "可能な限りコメントは英語で残すようにしてください。"
|
||||
readmore = "もっと読む »"
|
||||
not_translated = "(この記事はまだ翻訳されていません。)"
|
||||
translated_content = "この記事は翻訳されたものです:"
|
||||
translated_content_notice = "この記事は<strong><a href=\"_original.permalink_\">_original.title_</a></strong>をコミュニティの手により翻訳したものです。そのため、翻訳が完全・最新でなかったり、原文にない誤りを含んでいる可能性があります。問題があれば<a href=\"https://github.com/phil-opp/blog_os/issues/906\">このissue</a>上で報告してください!"
|
||||
translated_by = "翻訳者:"
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "及び"
|
||||
support_me = """
|
||||
<h2>Support Me</h2>
|
||||
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
|
||||
"""
|
||||
|
||||
# Persian
|
||||
[languages.fa]
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
[languages.fa.translations]
|
||||
lang_name = "Persian"
|
||||
toc = "فهرست مطالب"
|
||||
all_posts = "« همه پستها"
|
||||
comments = "نظرات"
|
||||
comments_notice = "لطفا نظرات خود را در صورت امکان به انگلیسی بنویسید."
|
||||
readmore = "ادامهمطلب»"
|
||||
not_translated = "(.این پست هنوز ترجمه نشده است)"
|
||||
translated_content = "محتوای ترجمه شده:"
|
||||
translated_content_notice = "این یک ترجمه از جامعه کاربران برای پست <strong><a href=\"_original.permalink_\">_original.title_</a></strong> است. ممکن است ناقص، منسوخ شده یا دارای خطا باشد. لطفا هر گونه مشکل را در <a href=\"https://github.com/phil-opp/blog_os/issues/908\">این ایشو</a> گزارش دهید!"
|
||||
translated_by = "ترجمه توسط"
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "و"
|
||||
support_me = """
|
||||
<h2>Support Me</h2>
|
||||
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
|
||||
"""
|
||||
|
||||
# Russian
|
||||
[languages.ru]
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
[languages.ru.translations]
|
||||
lang_name = "Russian"
|
||||
toc = "Содержание"
|
||||
all_posts = "« Все посты"
|
||||
comments = "Комментарии"
|
||||
comments_notice = "Пожалуйста, оставляйте комментарии на английском по возможности."
|
||||
readmore = "читать дальше »"
|
||||
not_translated = "(Этот пост еще не переведен.)"
|
||||
translated_content = "Переведенное содержание:"
|
||||
translated_content_notice = "Это перевод сообщества поста <strong><a href=\"_original.permalink_\">_original.title_</a></strong>. Он может быть неполным, устаревшим или содержать ошибки. Пожалуйста, сообщайте о любых проблемах!"
|
||||
translated_by = "Перевод сделан"
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "и"
|
||||
support_me = """
|
||||
<h2>Support Me</h2>
|
||||
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
|
||||
"""
|
||||
|
||||
# French
|
||||
[languages.fr]
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
[languages.fr.translations]
|
||||
lang_name = "French"
|
||||
toc = "Table des matières"
|
||||
all_posts = "« Tous les articles"
|
||||
comments = "Commentaires"
|
||||
comments_notice = "Veuillez commenter en Anglais si possible."
|
||||
readmore = "Voir plus »"
|
||||
not_translated = "(Cet article n'est pas encore traduit.)"
|
||||
translated_content = "Contenu traduit : "
|
||||
translated_content_notice = "Ceci est une traduction communautaire de l'article <strong><a href=\"_original.permalink_\">_original.title_</a></strong>. Il peut être incomplet, obsolète ou contenir des erreurs. Veuillez signaler les quelconques problèmes !"
|
||||
translated_by = "Traduit par : "
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "et"
|
||||
support_me = """
|
||||
<h2>Support Me</h2>
|
||||
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
|
||||
"""
|
||||
|
||||
# Korean
|
||||
[languages.ko]
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
[languages.ko.translations]
|
||||
lang_name = "Korean"
|
||||
toc = "목차"
|
||||
all_posts = "« 모든 게시글"
|
||||
comments = "댓글"
|
||||
comments_notice = "댓글은 가능하면 영어로 작성해주세요."
|
||||
readmore = "더 읽기 »"
|
||||
not_translated = "(아직 번역이 완료되지 않은 게시글입니다)"
|
||||
translated_content = "번역된 내용 : "
|
||||
translated_content_notice = "이것은 커뮤니티 멤버가 <strong><a href=\"_original.permalink_\">_original.title_</a></strong> 포스트를 번역한 글입니다. 부족한 설명이나 오류, 혹은 시간이 지나 더 이상 유효하지 않은 정보를 발견하시면 제보해주세요!"
|
||||
translated_by = "번역한 사람 : "
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "와"
|
||||
support_me = """
|
||||
<h2>Support Me</h2>
|
||||
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
|
||||
"""
|
||||
|
||||
[languages.ar]
|
||||
title = "Writing an OS in Rust"
|
||||
[languages.ar.translations]
|
||||
lang_name = "Arabic"
|
||||
toc = "Table of Contents"
|
||||
all_posts = "« All Posts"
|
||||
comments = "Comments"
|
||||
comments_notice = "Please leave your comments in English if possible."
|
||||
readmore = "read more »"
|
||||
not_translated = "(This post is not translated yet.)"
|
||||
translated_content = "Translated Content:"
|
||||
translated_content_notice = "This is a community translation of the <strong><a href=\"_original.permalink_\">_original.title_</a></strong> post. It might be incomplete, outdated or contain errors. Please report any issues!"
|
||||
translated_by = "Translation by"
|
||||
translation_contributors = "With contributions from"
|
||||
word_separator = "and"
|
||||
support_me = """
|
||||
<h2>Support Me</h2>
|
||||
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
|
||||
"""
|
||||
|
||||
# Spanish
|
||||
[languages.es]
|
||||
title = "Writing an OS in Rust"
|
||||
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
|
||||
[languages.es.translations]
|
||||
lang_name = "Spanish"
|
||||
toc = "Tabla de Contenidos"
|
||||
all_posts = "« Todos los Posts"
|
||||
comments = "Comentarios"
|
||||
comments_notice = "Por favor deja tus comentarios en inglés si es posible."
|
||||
readmore = "leer más »"
|
||||
not_translated = "(Este post aún no está traducido.)"
|
||||
translated_content = "Contenido Traducido:"
|
||||
translated_content_notice = "Esta es una traducción comunitaria del post <strong><a href=\"_original.permalink_\">_original.title_</a></strong>. Puede estar incompleta, desactualizada o contener errores. ¡Por favor reporta cualquier problema!"
|
||||
translated_by = "Traducción por"
|
||||
translation_contributors = "Con contribuciones de"
|
||||
word_separator = "y"
|
||||
support_me = """
|
||||
<h2>Apóyame</h2>
|
||||
<p>Crear y mantener este blog y las bibliotecas asociadas es mucho trabajo, pero realmente disfruto haciéndolo. Al apoyarme, me permites invertir más tiempo en nuevo contenido, nuevas características y mantenimiento continuo. La mejor manera de apoyarme es <a href=\"https://github.com/sponsors/phil-opp\"><em>patrocinarme en GitHub</em></a>. ¡Gracias!</p>
|
||||
"""
|
||||
comment_note = """
|
||||
¿Tienes algún problema, quieres compartir comentarios o discutir más ideas? ¡No dudes en dejar un comentario aquí! Por favor, utiliza inglés y sigue el <a href=\"https://www.rust-lang.org/policies/code-of-conduct\">código de conducta</a> de Rust. Este hilo de comentarios se vincula directamente con una <a href=\"_discussion_url_\"><em>discusión en GitHub</em></a>, así que también puedes comentar allí si lo prefieres.
|
||||
"""
|
||||
96
blog/content/LICENSE-CC-BY-NC
Normal file
@@ -0,0 +1,96 @@
|
||||
Creative Commons Attribution-NonCommercial 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
|
||||
|
||||
Section 1 – Definitions.
|
||||
|
||||
Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
|
||||
Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
|
||||
Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
|
||||
Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
|
||||
Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
|
||||
Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
|
||||
Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
|
||||
Licensor means the individual(s) or entity(ies) granting rights under this Public License.
|
||||
NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
|
||||
Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
|
||||
Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
|
||||
You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
|
||||
|
||||
Section 2 – Scope.
|
||||
|
||||
License grant.
|
||||
Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
|
||||
reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
|
||||
produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
|
||||
Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
|
||||
Term. The term of this Public License is specified in Section 6(a).
|
||||
Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
|
||||
Downstream recipients.
|
||||
Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
|
||||
No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
|
||||
No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
Other rights.
|
||||
Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
|
||||
Patent and trademark rights are not licensed under this Public License.
|
||||
To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
|
||||
|
||||
Section 3 – License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the following conditions.
|
||||
|
||||
Attribution.
|
||||
|
||||
If You Share the Licensed Material (including in modified form), You must:
|
||||
retain the following if it is supplied by the Licensor with the Licensed Material:
|
||||
identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
|
||||
a copyright notice;
|
||||
a notice that refers to this Public License;
|
||||
a notice that refers to the disclaimer of warranties;
|
||||
a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
|
||||
indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
|
||||
indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
|
||||
You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
|
||||
If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
|
||||
If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
|
||||
|
||||
Section 4 – Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
|
||||
|
||||
for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
|
||||
if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
|
||||
You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
|
||||
|
||||
Section 5 – Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
|
||||
To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
|
||||
|
||||
The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
|
||||
|
||||
Section 6 – Term and Termination.
|
||||
|
||||
This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
|
||||
|
||||
Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
|
||||
automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
|
||||
upon express reinstatement by the Licensor.
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
|
||||
For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
|
||||
Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
|
||||
|
||||
Section 7 – Other Terms and Conditions.
|
||||
|
||||
The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
|
||||
Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
|
||||
|
||||
Section 8 – Interpretation.
|
||||
|
||||
For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
|
||||
To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
|
||||
No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
|
||||
Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
|
||||
19
blog/content/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Blog Content
|
||||
|
||||
This folder contains the content for the _"Writing an OS in Rust"_ blog.
|
||||
|
||||
## License
|
||||
|
||||
This folder is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License, available in [LICENSE-CC-BY-NC](LICENSE-CC-BY-NC) or under <https://creativecommons.org/licenses/by-nc/4.0/>.
|
||||
|
||||
All _code examples_ between markdown code blocks denoted by three backticks (<code>\`\`\`</code>) are additionally licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../../LICENSE-APACHE) or
|
||||
https://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](../../LICENSE-MIT) or https://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions.
|
||||
12
blog/content/_index.ar.md
Normal file
@@ -0,0 +1,12 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">كتابة نظام تشغيل بلغة Rust </h1>
|
||||
<div class="front-page-introduction">
|
||||
|
||||
تنشئ سلسلة المدونات هذه نظام تشغيل صغير بلغة البرمجة [Rust ](https://www.rust-lang.org/). كل منشور هو عبارة عن برنامج تعليمي صغير ويتضمن كل الشيفرة المطلوبة، لذا يمكنك المتابعة إذا أردت. الكود المصدري متاح أيضًا في مستودع [Github ](https://github.com/phil-opp/blog_os) المقابل.
|
||||
|
||||
آخر منشور: <!-- latest-post -->
|
||||
</div>
|
||||
13
blog/content/_index.es.md
Normal file
@@ -0,0 +1,13 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Escribiendo un sistema operativo en Rust</h1>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
Esta serie de blogs crea un pequeño sistema operativo en el [lenguaje de programación Rust](https://www.rust-lang.org/). Cada publicación es un pequeño tutorial e incluye todo el código necesario, para que puedas seguir los pasos si lo deseas. El código fuente también está disponible en el [repositorio correspondiente de Github](https://github.com/phil-opp/blog_os).
|
||||
|
||||
Última publicación: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
13
blog/content/_index.fa.md
Normal file
@@ -0,0 +1,13 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">نوشتن یک سیستم عامل با راست</h1>
|
||||
|
||||
<div class="front-page-introduction right-to-left">
|
||||
|
||||
این مجموعه بلاگ یک سیستم عامل کوچک در [زبان برنامه نویسی Rust](https://www.rust-lang.org/) ایجاد می کند. هر پست یک آموزش کوچک است و شامل تمام کدهای مورد نیاز است ، بنابراین اگر دوست دارید می توانید آن را دنبال کنید. کد منبع نیز در [مخزن گیتهاب](https://github.com/phil-opp/blog_os) مربوطه موجود است.
|
||||
|
||||
اخرین پست: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
13
blog/content/_index.fr.md
Normal file
@@ -0,0 +1,13 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Écrire un OS en Rust</h1>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
L'objectif de ce blog est de créer un petit système d'exploitation avec le [langage de programmation Rust](https://www.rust-lang.org/). Chaque article est un petit tutoriel et comprend tout le code nécessaire, vous pouvez donc essayer en même temps si vous le souhaitez. Le code source est aussi disponible dans le [dépôt GitHub](https://github.com/phil-opp/blog_os) correspondant.
|
||||
|
||||
Dernier article : <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
13
blog/content/_index.ja.md
Normal file
@@ -0,0 +1,13 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">RustでOSを書く</h1>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
このブログシリーズでは、ちょっとしたオペレーティングシステムを[Rustプログラミング言語](https://www.rust-lang.org/)を使って作ります。それぞれの記事が小さなチュートリアルになっており、必要なコードも全て記事内に記されているので、一つずつ読み進めて行けば理解できるでしょう。対応した[Githubリポジトリ](https://github.com/phil-opp/blog_os)でソースコードを見ることもできます。
|
||||
|
||||
最新記事: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
14
blog/content/_index.ko.md
Normal file
@@ -0,0 +1,14 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Rust로 OS 구현하기</h1>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
이 블로그 시리즈는 [Rust 프로그래밍 언어](https://www.rust-lang.org/)로 작은 OS를 구현하는 것을 주제로 합니다.
|
||||
각 포스트는 구현에 필요한 소스 코드를 포함한 작은 튜토리얼 형식으로 구성되어 있습니다. 소스 코드는 이 블로그의 [Github 저장소](https://github.com/phil-opp/blog_os)에서도 확인하실 수 있습니다.
|
||||
|
||||
최신 포스트: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
13
blog/content/_index.md
Normal file
@@ -0,0 +1,13 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Writing an OS in Rust</h1>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
|
||||
|
||||
Latest post: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
13
blog/content/_index.ru.md
Normal file
@@ -0,0 +1,13 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Собственная операционная система на Rust</h1>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
Этот блог посвящен написанию маленькой операционной системы на [языке программирования Rust](https://www.rust-lang.org/). Каждый пост — это маленькое руководство, включающее в себя весь необходимый код, — вы сможете следовать ему, если пожелаете. Исходный код также доступен в соотвестующем [репозитории на Github](https://github.com/phil-opp/blog_os).
|
||||
|
||||
Последний пост: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
13
blog/content/_index.zh-CN.md
Normal file
@@ -0,0 +1,13 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">用Rust写一个操作系统</h1>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
这个博客系列用[Rust编程语言](https://www.rust-lang.org/)编写了一个小操作系统。每篇文章都是一个小教程,并且包含了所有代码,你可以跟着一起学习。源代码也放在了[Github 仓库](https://github.com/phil-opp/blog_os)。
|
||||
|
||||
最新文章: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
13
blog/content/_index.zh-TW.md
Normal file
@@ -0,0 +1,13 @@
|
||||
+++
|
||||
template = "edition-2/index.html"
|
||||
+++
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Writing an OS in Rust</h1>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
|
||||
|
||||
Latest post: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
5
blog/content/edition-1/_index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
+++
|
||||
title = "First Edition"
|
||||
template = "edition-1/index.html"
|
||||
aliases = ["first-edition/index.html"]
|
||||
+++
|
||||
6
blog/content/edition-1/extra/_index.md
Normal file
@@ -0,0 +1,6 @@
|
||||
+++
|
||||
title = "Extra Content"
|
||||
insert_anchor_links = "left"
|
||||
render = false
|
||||
sort_by = "weight"
|
||||
+++
|
||||
@@ -1,12 +1,17 @@
|
||||
+++
|
||||
title = "Cross Compile Binutils"
|
||||
template = "plain.html"
|
||||
path = "cross-compile-binutils"
|
||||
weight = 2
|
||||
+++
|
||||
|
||||
The [GNU Binutils] are a collection of various binary tools such as `ld`, `as`, `objdump`, or `readelf`. These tools are platform-specific, so you need to compile them again if your host system and target system are different. In our case, we need `ld` and `objdump` for the x86_64 architecture.
|
||||
|
||||
[GNU Binutils]: https://www.gnu.org/software/binutils/
|
||||
|
||||
## Building Setup
|
||||
First, you need to download a current binutils version from [here][download] \(the latest one is near the bottom). After extracting, you should have a folder named `binutils-2.X` where `X` is for example `25.1`. Now can create and switch to a new folder for building (recommended):
|
||||
|
||||
[download]: ftp://sourceware.org/pub/binutils/snapshots
|
||||
|
||||
```bash
|
||||
36
blog/content/edition-1/extra/cross-compile-libcore.md
Normal file
@@ -0,0 +1,36 @@
|
||||
+++
|
||||
title = "Cross Compiling: libcore"
|
||||
template = "plain.html"
|
||||
path = "cross-compile-libcore"
|
||||
weight = 3
|
||||
+++
|
||||
|
||||
If you get an `error: can't find crate for 'core'`, you're probably compiling for a different target (e.g. you're passing the `target` option to `cargo build`). Now the compiler complains that it can't find the `core` library. This document gives a quick overview how to fix this problem. For more details, see the [rust-cross] project.
|
||||
|
||||
[rust-cross]: https://github.com/japaric/rust-cross
|
||||
|
||||
## Libcore
|
||||
The core library is a dependency-free library that is added implicitly when using `#![no_std]`. It provides basic standard library features like Option or Iterator. The core library is installed together with the rust compiler (just like the std library). But the installed libcore is specific to your architecture. If you aren't working on x86_64 Linux and pass `‑‑target x86_64‑unknown‑linux‑gnu` to cargo, it can't find a x86_64 libcore. To fix this, you can either use `rustup` or `xargo`.
|
||||
|
||||
## rustup
|
||||
Thanks to [rustup], cross-compilation for [official target triples] is pretty easy today: Just execute `rustup target add x86_64-unknown-linux-gnu`.
|
||||
|
||||
[rustup]: https://rustup.rs
|
||||
[official target triples]: https://github.com/japaric/rust-cross#the-target-triple
|
||||
|
||||
## xargo
|
||||
If you're using a _custom target specification_, the `rustup` method doesn't work. Instead, you can use [xargo]. Xargo is a wrapper for cargo that eases cross compilation. We can install it by executing:
|
||||
|
||||
```
|
||||
cargo install xargo
|
||||
```
|
||||
If the installation fails, make sure that you have `cmake` and the OpenSSL headers installed. For more details, see the xargo's [dependency section].
|
||||
|
||||
[xargo]: https://github.com/japaric/xargo
|
||||
[dependency section]: https://github.com/japaric/xargo#dependencies
|
||||
|
||||
Xargo is “a drop-in replacement for cargo”, so every cargo command also works with `xargo`. You can do e.g. `xargo --help`, `xargo clean`, or `xargo doc`. However, the `build` command gains additional functionality: `xargo build` will automatically cross compile the `core` library (and a few other libraries such as `alloc`) when compiling for custom targets.
|
||||
|
||||
[xargo]: https://github.com/japaric/xargo
|
||||
|
||||
So if your custom target file is named `your-cool-target.json`, you can compile your code using xargo through `xargo build --target your-cool-target` (note the omitted extension).
|
||||
@@ -1,17 +1,28 @@
|
||||
+++
|
||||
title = "Catching Exceptions"
|
||||
date = "2016-05-28"
|
||||
weight = 1
|
||||
path = "catching-exceptions"
|
||||
aliases = ["catching-exceptions.html"]
|
||||
date = 2016-05-28
|
||||
template = "edition-1/page.html"
|
||||
[extra]
|
||||
updated = "2016-06-25"
|
||||
+++
|
||||
|
||||
In this post, we start exploring exceptions. We set up an interrupt descriptor table and add handler functions. At the end of this post, our kernel will be able to catch page faults.
|
||||
In this post, we start exploring exceptions. We set up an interrupt descriptor table and add handler functions. At the end of this post, our kernel will be able to catch divide-by-zero faults.
|
||||
|
||||
<!--more-->
|
||||
<!-- more -->
|
||||
|
||||
As always, the complete source code is on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
||||
As always, the complete source code is on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
||||
|
||||
[Github]: https://github.com/phil-opp/blog_os/tree/catching_exceptions
|
||||
[GitHub]: https://github.com/phil-opp/blog_os/tree/catching_exceptions
|
||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||
|
||||
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||
|
||||
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||
|
||||
## Exceptions
|
||||
An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type.
|
||||
|
||||
@@ -24,7 +35,7 @@ We've already seen several types of exceptions in our kernel:
|
||||
|
||||
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
||||
|
||||
[exceptions]: http://wiki.osdev.org/Exceptions
|
||||
[exceptions]: https://wiki.osdev.org/Exceptions
|
||||
|
||||
### The Interrupt Descriptor Table
|
||||
In order to catch and handle exceptions, we have to set up a so-called _Interrupt Descriptor Table_ (IDT). In this table we can specify a handler function for each CPU exception. The hardware uses this table directly, so we need to follow a predefined format. Each entry must have the following 16-byte structure:
|
||||
@@ -83,7 +94,9 @@ Now we create types for the IDT and its entries:
|
||||
```rust
|
||||
// src/interrupts/idt.rs
|
||||
|
||||
use x86::segmentation::{self, SegmentSelector};
|
||||
use x86_64::instructions::segmentation;
|
||||
use x86_64::structures::gdt::SegmentSelector;
|
||||
use x86_64::PrivilegeLevel;
|
||||
|
||||
pub struct Idt([Entry; 16]);
|
||||
|
||||
@@ -113,13 +126,13 @@ pub struct EntryOptions(u16);
|
||||
impl EntryOptions {
|
||||
fn new() -> Self {...}
|
||||
|
||||
fn set_present(&mut self, present: bool) {...}
|
||||
pub fn set_present(&mut self, present: bool) {...}
|
||||
|
||||
fn disable_interrupts(&mut self, disable: bool) {...}
|
||||
pub fn disable_interrupts(&mut self, disable: bool) {...}
|
||||
|
||||
fn set_privilege_level(&mut self, dpl: u16) {...}
|
||||
pub fn set_privilege_level(&mut self, dpl: u16) {...}
|
||||
|
||||
fn set_stack_index(&mut self, index: u16) {...}
|
||||
pub fn set_stack_index(&mut self, index: u16) {...}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -141,17 +154,19 @@ Or:
|
||||
self.0 = ((self.0 >> 3) << 3) | stack_index;
|
||||
```
|
||||
|
||||
Well, none of these variants is really _readable_ and it's very easy to make mistakes somewhere. Therefore I created a `BitField` type with the following API:
|
||||
Well, none of these variants is really _readable_ and it's very easy to make mistakes somewhere. Therefore I created a `BitField` trait that provides the following [Range]-based API:
|
||||
|
||||
[Range]: https://doc.rust-lang.org/nightly/core/ops/struct.Range.html
|
||||
|
||||
``` rust
|
||||
self.0.set_range(0..3, stack_index);
|
||||
self.0.set_bits(0..3, stack_index);
|
||||
```
|
||||
|
||||
I think it is much more readable, since we abstracted away all bit-masking details. The `BitField` type is contained in the [bit_field] crate. (It's pretty new, so it might still contain bugs.) To add it as dependency, we run `cargo add bit_field` and add `extern crate bit_field;` to our `src/lib.rs`.
|
||||
I think it is much more readable, since we abstracted away all bit-masking details. The `BitField` trait is contained in the [bit_field] crate. (It's pretty new, so it might still contain bugs.) To add it as dependency, we run `cargo add bit_field` and add `extern crate bit_field;` to our `src/lib.rs`.
|
||||
|
||||
[bit_field]: TODO
|
||||
[bit_field]: https://crates.io/crates/bit_field
|
||||
|
||||
Now we can use the crate to implement the methods of `EntryOptions`:
|
||||
Now we can use the trait to implement the methods of `EntryOptions`:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/idt.rs
|
||||
@@ -159,12 +174,12 @@ Now we can use the crate to implement the methods of `EntryOptions`:
|
||||
use bit_field::BitField;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct EntryOptions(BitField<u16>);
|
||||
pub struct EntryOptions(u16);
|
||||
|
||||
impl EntryOptions {
|
||||
fn minimal() -> Self {
|
||||
let mut options = BitField::new(0);
|
||||
options.set_range(9..12, 0b111); // 'must-be-one' bits
|
||||
let mut options = 0;
|
||||
options.set_bits(9..12, 0b111); // 'must-be-one' bits
|
||||
EntryOptions(options)
|
||||
}
|
||||
|
||||
@@ -174,23 +189,23 @@ impl EntryOptions {
|
||||
options
|
||||
}
|
||||
|
||||
fn set_present(&mut self, present: bool) -> &mut Self {
|
||||
pub fn set_present(&mut self, present: bool) -> &mut Self {
|
||||
self.0.set_bit(15, present);
|
||||
self
|
||||
}
|
||||
|
||||
fn disable_interrupts(&mut self, disable: bool) -> &mut Self {
|
||||
pub fn disable_interrupts(&mut self, disable: bool) -> &mut Self {
|
||||
self.0.set_bit(8, !disable);
|
||||
self
|
||||
}
|
||||
|
||||
fn set_privilege_level(&mut self, dpl: u16) -> &mut Self {
|
||||
self.0.set_range(13..15, dpl);
|
||||
pub fn set_privilege_level(&mut self, dpl: u16) -> &mut Self {
|
||||
self.0.set_bits(13..15, dpl);
|
||||
self
|
||||
}
|
||||
|
||||
fn set_stack_index(&mut self, index: u16) -> &mut Self {
|
||||
self.0.set_range(0..3, index);
|
||||
pub fn set_stack_index(&mut self, index: u16) -> &mut Self {
|
||||
self.0.set_bits(0..3, index);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -230,9 +245,9 @@ It needs to be a function with a defined [calling convention], as it called dire
|
||||
|
||||
It is important that the function is [diverging], i.e. it must never return. The reason is that the hardware doesn't _call_ the handler functions, it just _jumps_ to them after pushing some values to the stack. So our stack might look different:
|
||||
|
||||
[diverging]: https://doc.rust-lang.org/book/functions.html#diverging-functions
|
||||
[diverging]: https://doc.rust-lang.org/rust-by-example/fn/diverging.html
|
||||
|
||||

|
||||

|
||||
|
||||
If our handler function returned normally, it would try to pop the return address from the stack. But it might get some completely different value then. For example, the CPU pushes an error code for some exceptions. Bad things would happen if we interpreted this error code as return address and jumped to it. Therefore interrupt handler functions must diverge[^fn-must-diverge].
|
||||
|
||||
@@ -251,7 +266,7 @@ impl Idt {
|
||||
impl Entry {
|
||||
fn missing() -> Self {
|
||||
Entry {
|
||||
gdt_selector: SegmentSelector::new(0),
|
||||
gdt_selector: SegmentSelector::new(0, PrivilegeLevel::Ring0),
|
||||
pointer_low: 0,
|
||||
pointer_middle: 0,
|
||||
pointer_high: 0,
|
||||
@@ -275,17 +290,16 @@ impl Idt {
|
||||
}
|
||||
}
|
||||
```
|
||||
The method overwrites the specified entry with the given handler function. We use the `segmentation::cs`[^fn-segmentation-cs] function of the [x86 crate] to get the current code segment descriptor. There's no need for different kernel code segments in long mode, so the current `cs` value should be always the right choice.
|
||||
The method overwrites the specified entry with the given handler function. We use the `segmentation::cs` function of the [x86_64 crate] to get the current code segment descriptor. There's no need for different kernel code segments in long mode, so the current `cs` value should be always the right choice.
|
||||
|
||||
[x86 crate]: https://github.com/gz/rust-x86
|
||||
[^fn-segmentation-cs]: The `segmentation::cs` function was [added](https://github.com/gz/rust-x86/pull/12) in version 0.7.0, so you might need to update your `x86` version in your `Cargo.toml`.
|
||||
[x86_64 crate]: https://docs.rs/x86_64
|
||||
|
||||
By returning a mutual reference to the entry's options, we allow the caller to override the default settings. For example, the caller could add a non-present entry by executing: `idt.set_handler(11, handler_fn).set_present(false)`.
|
||||
|
||||
### Loading the IDT
|
||||
Now we're able to create new interrupt descriptor tables with registered handler functions. We just need a way to load an IDT, so that the CPU uses it. The x86 architecture uses a special register to store the active IDT and its length. In order to load a new IDT we need to update this register through the [lidt] instruction.
|
||||
|
||||
[lidt]: http://x86.renejeschke.de/html/file_module_x86_id_156.html
|
||||
[lidt]: https://www.felixcloutier.com/x86/lgdt:lidt
|
||||
|
||||
The `lidt` instruction expects a pointer to a special data structure, which specifies the start address of the IDT and its length:
|
||||
|
||||
@@ -295,15 +309,15 @@ Type | Name | Description
|
||||
u16 | Limit | The maximum addressable byte in the table. Equal to the table size in bytes minus 1.
|
||||
u64 | Offset | Virtual start address of the table.
|
||||
|
||||
This structure is already contained [in the x86 crate], so we don't need to create it ourselves. The same is true for the [lidt function]. So we just need to put the pieces together to create a `load` method:
|
||||
This structure is already contained [in the x86_64 crate], so we don't need to create it ourselves. The same is true for the [lidt function]. So we just need to put the pieces together to create a `load` method:
|
||||
|
||||
[in the x86 crate]: http://gz.github.io/rust-x86/x86/dtables/struct.DescriptorTablePointer.html
|
||||
[lidt function]: http://gz.github.io/rust-x86/x86/dtables/fn.lidt.html
|
||||
[in the x86_64 crate]: https://docs.rs/x86_64/0.1.0/x86_64/instructions/tables/struct.DescriptorTablePointer.html
|
||||
[lidt function]: https://docs.rs/x86_64/0.1.0/x86_64/instructions/tables/fn.lidt.html
|
||||
|
||||
```rust
|
||||
impl Idt {
|
||||
pub fn load(&self) {
|
||||
use x86::dtables::{DescriptorTablePointer, lidt};
|
||||
use x86_64::instructions::tables::{DescriptorTablePointer, lidt};
|
||||
use core::mem::size_of;
|
||||
|
||||
let ptr = DescriptorTablePointer {
|
||||
@@ -315,13 +329,11 @@ impl Idt {
|
||||
}
|
||||
}
|
||||
```
|
||||
The method does not need to modify the IDT, so it takes `self` by immutable reference. We convert this reference to an u64 and calculate the table size using [mem::size_of]. The additional `-1` is needed because the limit field has to be the maximum addressable byte.
|
||||
The method does not need to modify the IDT, so it takes `self` by immutable reference. First, we create a `DescriptorTablePointer` and then we pass it to `lidt`. The `lidt` function expects that the `base` field has the type `u64`, therefore we need to cast the `self` pointer. For calculating the `limit` we use [mem::size_of]. The additional `-1` is needed because the limit field has to be the maximum addressable byte (inclusive bound). We need an unsafe block around `lidt`, because the function assumes that the specified handler addresses are valid.
|
||||
|
||||
[mem::size_of]: https://doc.rust-lang.org/nightly/core/mem/fn.size_of.html
|
||||
|
||||
Then we pass a pointer to our `ptr` structure to the `lidt` function, which calls the `lidt` assembly instruction in order to reload the IDT register. We need an unsafe block here, because the `lidt` assumes that the specified handler addresses are valid.
|
||||
|
||||
### Safety
|
||||
#### Safety
|
||||
But can we really guarantee that handler addresses are always valid? Let's see:
|
||||
|
||||
- The `Idt::new` function creates a new table populated with non-present entries. There's no way to set these entries to present from outside of this module, so this function is fine.
|
||||
@@ -354,7 +366,7 @@ Well, we construct an IDT _on the stack_ and load it. It is perfectly valid unti
|
||||
|
||||
Now imagine that the `cause_page_fault` function declared an array of pointers instead. If the present was coincidentally set, the CPU would jump to some random pointer and interpret random memory as code. This would be a clear violation of memory safety.
|
||||
|
||||
### Fixing the load method
|
||||
#### Fixing the load method
|
||||
So how do we fix it? We could make the load function itself `unsafe` and push the unsafety to the caller. However, there is a much better solution in this case. In order to see it, we formulate the requirement for the `load` method:
|
||||
|
||||
> The referenced IDT must be valid until a new IDT is loaded.
|
||||
@@ -365,7 +377,7 @@ We can't know when the next IDT will be loaded. Maybe never. So in the worst cas
|
||||
|
||||
This is exactly the definition of a [static lifetime]. So we can easily ensure that the IDT lives long enough by adding a `'static` requirement to the signature of the `load` function:
|
||||
|
||||
[static lifetime]: http://rustbyexample.com/scope/lifetime/static_lifetime.html
|
||||
[static lifetime]: https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html
|
||||
|
||||
```rust
|
||||
pub fn load(&'static self) {...}
|
||||
@@ -398,17 +410,21 @@ So a valid IDT needs to have the `'static` lifetime. We can either create a `sta
|
||||
static IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
|
||||
idt.set_handler(14, page_fault_handler);
|
||||
idt.set_handler(0, divide_by_zero_handler);
|
||||
|
||||
idt
|
||||
};
|
||||
|
||||
extern "C" fn page_fault_handler() -> ! {
|
||||
println!("EXCEPTION: PAGE FAULT");
|
||||
extern "C" fn divide_by_zero_handler() -> ! {
|
||||
println!("EXCEPTION: DIVIDE BY ZERO");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
We register a single handler function for a page fault (index 14). The handler function just prints an error message and enters a `loop`. However, it doesn't work this way:
|
||||
We register a single handler function for a [divide by zero error] \(index 0). Like the name says, this exception occurs when dividing a number by 0. Thus we have an easy way to test our new exception handler.
|
||||
|
||||
[divide by zero error]: https://wiki.osdev.org/Exceptions#Division_Error
|
||||
|
||||
However, it doesn't work this way:
|
||||
|
||||
```
|
||||
error: calls in statics are limited to constant functions, struct and enum
|
||||
@@ -421,9 +437,27 @@ error: references in statics may only refer to immutable values [E0017]
|
||||
```
|
||||
The reason is that the Rust compiler is not able to evaluate the value of the `static` at compile time. Maybe it will work someday when `const` functions become more powerful. But until then, we have to find another solution.
|
||||
|
||||
### Lazy Statics to the Rescue
|
||||
#### Lazy Statics to the Rescue
|
||||
Fortunately the `lazy_static` macro exists. Instead of evaluating a `static` at compile time, the macro performs the initialization when the `static` is referenced the first time. Thus, we can do almost everything in the initialization block and are even able to read runtime values.
|
||||
|
||||
Let's add the `lazy_static` crate to our project:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
```
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "0.2.1"
|
||||
features = ["spin_no_std"]
|
||||
```
|
||||
We need the `spin_no_std` feature, since we don't link the standard library.
|
||||
|
||||
With `lazy_static`, we can define our IDT without problems:
|
||||
|
||||
```rust
|
||||
@@ -433,7 +467,7 @@ lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
|
||||
idt.set_handler(14, page_fault_handler);
|
||||
idt.set_handler(0, divide_by_zero_handler);
|
||||
|
||||
idt
|
||||
};
|
||||
@@ -464,157 +498,65 @@ pub extern "C" fn rust_main(...) {
|
||||
// initialize our IDT
|
||||
interrupts::init();
|
||||
|
||||
// provoke a page fault by writing to some random address
|
||||
unsafe{ *(0xdeadbeaf as *mut u64) = 42 };
|
||||
// provoke a divide-by-zero fault
|
||||
42 / 0;
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
It works! We see a `EXCEPTION: PAGE FAULT` message at the bottom of our screen:
|
||||
When we run it, we get a runtime panic:
|
||||
|
||||

|
||||
```
|
||||
PANIC in src/lib.rs at line 57:
|
||||
attempted to divide by zero
|
||||
```
|
||||
|
||||
Let's try something else:
|
||||
That's a not our exception handler. The reason is that Rust itself checks for a possible division by zero and panics in that case. So in order to raise a divide-by-zero error in the CPU, we need to bypass the Rust compiler somehow.
|
||||
|
||||
### Inline Assembly
|
||||
In order to cause a divide-by-zero exception, we need to execute a [div] or [idiv] assembly instruction with operand 0. We could write a small assembly function and call it from our Rust code. An easier way is to use Rust's [inline assembly] macro.
|
||||
|
||||
[div]: https://www.felixcloutier.com/x86/div
|
||||
[idiv]: https://www.felixcloutier.com/x86/idiv
|
||||
[inline assembly]: https://doc.rust-lang.org/1.10.0/book/inline-assembly.html
|
||||
|
||||
Inline assembly allows us to write raw x86 assembly within a Rust function. The feature is unstable, so we need to add `#![feature(asm)]` to our `src/lib.rs`. Then we're able to write a `divide_by_zero` function:
|
||||
|
||||
```rust
|
||||
fn divide_by_zero() {
|
||||
unsafe {
|
||||
asm!("mov dx, 0; div dx" ::: "ax", "dx" : "volatile", "intel")
|
||||
}
|
||||
}
|
||||
```
|
||||
Let's try to decode it:
|
||||
|
||||
- The `asm!` macro emits raw assembly instructions, so it's `unsafe` to use it.
|
||||
- We insert two assembly instructions here: `mov dx, 0` and `div dx`. The former loads a 0 into the `dx` register (a subset of `rdx`) and the latter divides the `ax` register by `dx`. (The `div` instruction always implicitly operates on the `ax` register).
|
||||
- The colons are separators. After the first `:` we could specify output operands and after the second `:` we could specify input operands. We need neither, so we leave these areas empty.
|
||||
- After the third colon, we specify the so-called _clobbers_. These tell the compiler that our assembly modifies the values of some registers. Otherwise, the compiler assumes that the registers preserve their value. In our case, we clobber `dx` (we load 0 to it) and `ax` (the `div` instruction places the result in it).
|
||||
- The last block (after the 4th colon) specifies some options. The `volatile` option tells the compiler: “This code has side effects. Do not delete it and do not move it elsewhere”. In our case, the “side effect” is the divide-by-zero exception. Finally, the `intel` option allows us to use the Intel assembly syntax instead of the default AT&T syntax.
|
||||
|
||||
Let's use our new `divide_by_zero` function to raise a CPU exception:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
pub extern "C" fn rust_main(...) {
|
||||
...
|
||||
interrupts::init();
|
||||
|
||||
// provoke a page fault inside println
|
||||
println!("{:?}", unsafe{ *(0xdeadbeaf as *mut u64) = 42 });
|
||||
// provoke a divide-by-zero fault
|
||||
divide_by_zero();
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
Now the output ends on the `guard page` line. No `EXCEPTION` message and no `It did not crash` message either. What's happening?
|
||||
|
||||
### Debugging
|
||||
Let's debug it using [GDB]. It is a console debugger and works with nearly everything, including QEMU. To make QEMU listen for a debugger connection, we start it with the `-s` flag:
|
||||
It works! We see a `EXCEPTION: DIVIDE BY ZERO` message at the bottom of our screen:
|
||||
|
||||
[GDB]: https://www.gnu.org/software/gdb/
|
||||
|
||||
```Makefile
|
||||
# in `Makefile`
|
||||
|
||||
run: $(iso)
|
||||
@qemu-system-x86_64 -cdrom $(iso) -s
|
||||
```
|
||||
|
||||
Then we can launch GDB in another console window:
|
||||
|
||||
```
|
||||
> gdb build/kernel-x86_64.bin
|
||||
[some version, copyright, and usage information]
|
||||
Reading symbols from build/kernel-x86_64.bin...done.
|
||||
(gdb)
|
||||
```
|
||||
Now we can connect to our running QEMU instance on port `1234`:
|
||||
|
||||
```
|
||||
(gdb) target remote :1234
|
||||
Remote debugging using :1234
|
||||
0x00000000001031bd in spin::mutex::cpu_relax ()
|
||||
at /home/.../spin-0.3.5/src/mutex.rs:102
|
||||
102 unsafe { asm!("pause" :::: "volatile"); }
|
||||
```
|
||||
So we're locked in a function named `mutex::cpu_relax` inside the `spin` crate. Let's try a backtrace:
|
||||
|
||||
```
|
||||
(gdb) backtrace
|
||||
#0 0x00000000001031bd in spin::mutex::cpu_relax ()
|
||||
at /home/.../spin-0.3.5/src/mutex.rs:102
|
||||
#1 spin::mutex::{{impl}}::obtain_lock<blog_os::vga_buffer::Writer> (
|
||||
self=0x111230 <blog_os::vga_buffer::WRITER::h702c3f466147ac3b>)
|
||||
at /home/.../spin-0.3.5/src/mutex.rs:142
|
||||
#2 0x0000000000103143 in spin::mutex::{{impl}}::lock<blog_os::vga_buffer::
|
||||
Writer> (
|
||||
self=0x111230 <blog_os::vga_buffer::WRITER::h702c3f466147ac3b>)
|
||||
at /home/.../spin-0.3.5/src/mutex.rs:163
|
||||
#3 0x000000000010da59 in blog_os::interrupts::page_fault_handler ()
|
||||
at src/vga_buffer.rs:31
|
||||
...
|
||||
```
|
||||
Pretty verbose… but very useful. Let's clean it up a bit:
|
||||
|
||||
- `spin::mutex::cpu_relax`
|
||||
- `spin::mutex::obtain_lock<vga_buffer::Writer>`
|
||||
- `spin::mutex::lock<vga_buffer::Writer>`
|
||||
- `blog_os::interrupts::page_fault_handler`
|
||||
- ...
|
||||
|
||||
It's a _back_-trace, so it goes from the innermost function to the outermost function. We see that our page fault handler was called successfully. It then tried to write its error message. Therefore, it tried to `lock` the static `WRITER`, which in turn called `obtain_lock` and `cpu_relax`.
|
||||
|
||||
So our kernel tries to lock the output `WRITER`, which is already locked by the interrupted `println`. Thus, our exception handler waits forever and we don't see what error occurred. Yay, that's our first deadlock! :)
|
||||
|
||||
(As you see, GDB can be very useful sometimes. For more GDB information check out our [Set Up GDB] page.)
|
||||
|
||||
[Set Up GDB]: {{% relref "set-up-gdb.md" %}}
|
||||
|
||||
## Printing Errors Reliably
|
||||
In order to guarantee that we always see error messages, we add a `print_error` function to our `vga_buffer` module:
|
||||
|
||||
```rust
|
||||
// in src/vga_buffer.rs
|
||||
|
||||
pub unsafe fn print_error(fmt: fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
|
||||
let mut writer = Writer {
|
||||
column_position: 0,
|
||||
color_code: ColorCode::new(Color::Red, Color::Black),
|
||||
buffer: Unique::new(0xb8000 as *mut _),
|
||||
};
|
||||
writer.new_line();
|
||||
writer.write_fmt(fmt);
|
||||
}
|
||||
```
|
||||
|
||||
Instead of using the static `WRITER`, this function creates a new `Writer` on each invocation. Thereby it ignores the mutex and is always able to print to the screen without deadlocking. We print in red to highlight the error and add a newline to avoid overwriting unfinished lines.
|
||||
|
||||
### Safety
|
||||
This function clearly violates the invariants of the `vga_buffer` module, as it creates another `Unique` pointing to `0xb8000`. Thus, we deliberately introduce a data race on the VGA buffer. For this reason, the function is marked as `unsafe` and should only be used if absolutely necessary.
|
||||
|
||||
However, the situation is not _that_ bad. The VGA buffer only stores characters (no pointers) and we never rely on the buffer's values. So the function might cause mangled output, but should never be able to violate memory safety.
|
||||
|
||||
### Using print_error
|
||||
Let's use the new `print_error` function to print the page fault error:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
use vga_buffer::print_error;
|
||||
|
||||
extern "C" fn page_fault_handler() -> ! {
|
||||
unsafe { print_error(format_args!("EXCEPTION: PAGE FAULT")) };
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
We use the built-in [format_args] macro to translate the error string to a `fmt::Arguments` type. Now we should always see the error message, even if the exception occurred inside `println`:
|
||||
|
||||
[format_args]: https://doc.rust-lang.org/nightly/std/macro.format_args!.html
|
||||
|
||||

|
||||

|
||||
|
||||
## What's next?
|
||||
Now we're able to catch _almost_ all page faults. However, some page faults still cause a triple fault and a bootloop. For example, try the following code:
|
||||
|
||||
```rust
|
||||
pub extern "C" fn rust_main(...) {
|
||||
...
|
||||
interrupts::init();
|
||||
|
||||
// provoke a kernel stack overflow, which hits the guard page
|
||||
fn recursive() {
|
||||
recursive();
|
||||
}
|
||||
recursive();
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
The next post will explore and fix this triple fault by creating a double fault handler. After that, we should never again experience a triple fault in our kernel.
|
||||
We've successfully caught our first exception! However, our `EXCEPTION: DIVIDE BY ZERO` message doesn't contain much information about the cause of the exception. The next post improves the situation by printing i.a. the current stack pointer and address of the causing instruction. We will also explore other exceptions such as page faults, for which the CPU pushes an _error code_ on the stack.
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,664 @@
|
||||
+++
|
||||
title = "Better Exception Messages"
|
||||
weight = 2
|
||||
path = "better-exception-messages"
|
||||
aliases = ["better-exception-messages.html"]
|
||||
date = 2016-08-03
|
||||
template = "edition-1/page.html"
|
||||
[extra]
|
||||
updated = "2016-11-01"
|
||||
+++
|
||||
|
||||
In this post, we explore exceptions in more detail. Our goal is to print additional information when an exception occurs, for example the values of the instruction and stack pointer. In the course of this, we will explore inline assembly and naked functions. We will also add a handler function for page faults and read the associated error code.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
As always, the complete source code is on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
|
||||
|
||||
[GitHub]: https://github.com/phil-opp/blog_os/tree/better_exception_messages
|
||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
||||
|
||||
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||
|
||||
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||
|
||||
## Exceptions in Detail
|
||||
An exception signals that something is wrong with the currently-executed instruction. Whenever an exception occurs, the CPU interrupts its current work and starts an internal exception routine.
|
||||
|
||||
This routine involves reading the interrupt descriptor table and invoking the registered handler function. But first, the CPU pushes various information onto the stack, which describe the current state and provide information about the cause of the exception:
|
||||
|
||||

|
||||
|
||||
The pushed information contain the instruction and stack pointer, the current CPU flags, and (for some exceptions) an error code, which contains further information about the cause of the exception. Let's look at the fields in detail:
|
||||
|
||||
- First, the CPU aligns the stack pointer on a 16-byte boundary. This allows the handler function to use SSE instructions, which partly expect such an alignment.
|
||||
- After that, the CPU pushes the stack segment descriptor (SS) and the old stack pointer (from before the alignment) onto the stack. This allows us to restore the previous stack pointer when we want to resume the interrupted program.
|
||||
- Then the CPU pushes the contents of the [RFLAGS] register. This register contains various state information of the interrupted program. For example, it indicates if interrupts were enabled and whether the last executed instruction returned zero.
|
||||
- Next the CPU pushes the instruction pointer and its code segment descriptor onto the stack. This tells us the address of the last executed instruction, which caused the exception.
|
||||
- Finally, the CPU pushes an error code for some exceptions. This error code only exists for exceptions such as page faults or general protection faults and provides additional information. For example, it tells us whether a page fault was caused by a read or a write request.
|
||||
|
||||
[RFLAGS]: https://en.wikipedia.org/wiki/FLAGS_register
|
||||
|
||||
## Printing the Exception Stack Frame
|
||||
Let's create a struct that represents the exception stack frame:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct ExceptionStackFrame {
|
||||
instruction_pointer: u64,
|
||||
code_segment: u64,
|
||||
cpu_flags: u64,
|
||||
stack_pointer: u64,
|
||||
stack_segment: u64,
|
||||
}
|
||||
```
|
||||
The divide-by-zero fault pushes no error code, so we leave it out for now. Note that the stack grows downwards in memory, so we need to declare the fields in reverse order (compared to the figure above).
|
||||
|
||||
Now we need a way to find the memory address of this stack frame. When we look at the above graphic again, we see that the start address of the exception stack frame is the new stack pointer. So we just need to read the value of `rsp` at the very beginning of our handler function:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
extern "C" fn divide_by_zero_handler() -> ! {
|
||||
let stack_frame: &ExceptionStackFrame;
|
||||
unsafe {
|
||||
asm!("mov $0, rsp" : "=r"(stack_frame) ::: "intel");
|
||||
}
|
||||
println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame);
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
We're using [inline assembly] here to load the value from the `rsp` register into `stack_frame`. The syntax is a bit strange, so here's a quick explanation:
|
||||
|
||||
[inline assembly]: https://doc.rust-lang.org/1.10.0/book/inline-assembly.html
|
||||
|
||||
- The `asm!` macro emits raw assembly instructions. This is the only way to read raw register values in Rust.
|
||||
- We insert a single assembly instruction: `mov $0, rsp`. It moves the value of `rsp` to some register (the `$0` is a placeholder for an arbitrary register, which gets filled by the compiler).
|
||||
- The colons are separators. After the first colon, the `asm!` macro expects output operands. We're specifying our `stack_frame` variable as a single output operand here. The `=r` tells the compiler that it should use any register for the first placeholder `$0`.
|
||||
- After the second colon, we can specify input operands. We don't need any, therefore we leave it empty.
|
||||
- After the third colon, the macro expects so called [clobbers]. We don't change any register values, so we leave it empty too.
|
||||
- The last block (after the 4th colon) specifies options. The `intel` option tells the compiler that our code is in Intel assembly syntax (instead of the default AT&T syntax).
|
||||
|
||||
[clobbers]: https://doc.rust-lang.org/1.10.0/book/inline-assembly.html#clobbers
|
||||
|
||||
So the inline assembly loads the stack pointer value to `stack_frame` at the very beginning of our function. Thus we have a pointer to the exception stack frame and are able to pretty-print its `Debug` formatting through the `{:#?}` argument.
|
||||
|
||||
### Testing it
|
||||
Let's try it by executing `make run`:
|
||||
|
||||

|
||||
|
||||
Those `ExceptionStackFrame` values look very wrong. The instruction pointer definitely shouldn't be 1 and the code segment should be `0x8` instead of some big number. So what's going on here?
|
||||
|
||||
### Debugging
|
||||
It seems like we somehow got the pointer wrong. The `ExceptionStackFrame` type and our inline assembly seem correct, so something must be modifying `rsp` before we load it into `stack_frame`.
|
||||
|
||||
Let's see what's happening by looking at the disassembly of our function:
|
||||
|
||||
```
|
||||
> objdump -d build/kernel-x86_64.bin | grep -A20 "divide_by_zero_handler"
|
||||
|
||||
[...]
|
||||
000000000010ced0 <_ZN7blog_os10interrupts22divide_by_zero_handler17h62189e8E>:
|
||||
10ced0: 55 push %rbp
|
||||
10ced1: 48 89 e5 mov %rsp,%rbp
|
||||
10ced4: 48 81 ec b0 00 00 00 sub $0xb0,%rsp
|
||||
10cedb: 48 8d 45 98 lea -0x68(%rbp),%rax
|
||||
10cedf: 48 b9 1d 1d 1d 1d 1d movabs $0x1d1d1d1d1d1d1d1d,%rcx
|
||||
10cee6: 1d 1d 1d
|
||||
10cee9: 48 89 4d 98 mov %rcx,-0x68(%rbp)
|
||||
10ceed: 48 89 4d f8 mov %rcx,-0x8(%rbp)
|
||||
10cef1: 48 89 e1 mov %rsp,%rcx
|
||||
10cef4: 48 89 4d f8 mov %rcx,-0x8(%rbp)
|
||||
10cef8: ...
|
||||
[...]
|
||||
```
|
||||
Our `divide_by_zero_handler` starts at address `0x10ced0`. Let's look at the instruction at address `0x10cef1`:
|
||||
|
||||
```
|
||||
mov %rsp,%rcx
|
||||
```
|
||||
This is our inline assembly instruction, which loads the stack pointer into the `stack_frame` variable. It just looks a bit different, since it's in AT&T syntax and contains `rcx` instead of our `$0` placeholder. It moves `rsp` to `rcx`, and then the next instruction (`mov %rcx,-0x8(%rbp)`) moves `rcx` to the variable on the stack.
|
||||
|
||||
We can clearly see the problem here: The compiler inserted various other instructions before our inline assembly. These instructions modify the stack pointer so that we don't read the original `rsp` value and get a wrong pointer. But why is the compiler doing this?
|
||||
|
||||
The reason is that we need some place on the stack to store things like variables. Therefore the compiler inserts a so-called _[function prologue]_, which prepares the stack and reserves space for all variables. In our case, the compiler subtracts from the stack pointer to make room for i.a. our `stack_frame` variable. This prologue is the first thing in every function and comes before every other code.
|
||||
|
||||
So in order to correctly load the exception frame pointer, we need some way to circumvent the automatic prologue generation.
|
||||
|
||||
[function prologue]: https://en.wikipedia.org/wiki/Function_prologue
|
||||
|
||||
### Naked Functions
|
||||
Fortunately there is a way to disable the prologue: [naked functions]. A naked function has no prologue and immediately starts with the first instruction of its body. However, most Rust code requires the prologue. Therefore naked functions should only contain inline assembly.
|
||||
|
||||
[naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
||||
|
||||
A naked function looks like this (note the `#[naked]` attribute):
|
||||
|
||||
```rust
|
||||
#[naked]
|
||||
extern "C" fn naked_function_example() {
|
||||
unsafe {
|
||||
asm!("mov rax, 0x42" ::: "rax" : "intel");
|
||||
};
|
||||
}
|
||||
```
|
||||
Naked functions are highly unstable, so we need to add `#![feature(naked_functions)]` to our `src/lib.rs`.
|
||||
|
||||
If you want to try it, insert it in `src/lib.rs` and call it from `rust_main`. When we inspect the disassembly, we see that the function prologue is missing:
|
||||
|
||||
```
|
||||
> objdump -d build/kernel-x86_64.bin | grep -A5 "naked_function_example"
|
||||
[...]
|
||||
000000000010df90 <_ZN7blog_os22naked_function_example17ha9f733dfe42b595dE>:
|
||||
10df90: 48 c7 c0 2a 00 00 00 mov $0x42,%rax
|
||||
10df97: c3 retq
|
||||
10df98: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
||||
10df9f: 00
|
||||
```
|
||||
It contains just the specified inline assembly and a return instruction (you can ignore the junk values after the return statement). So let's try to use a naked function to retrieve the exception frame pointer.
|
||||
|
||||
### A Naked Exception Handler
|
||||
We can't use Rust code in naked functions, but we still want to use Rust in our exception handler. Therefore we split our handler function in two parts. A main exception handler in Rust and a small naked wrapper function, which just loads the exception frame pointer and then calls the main handler.
|
||||
|
||||
Our new two-stage exception handler looks like this:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
#[naked]
|
||||
extern "C" fn divide_by_zero_wrapper() -> ! {
|
||||
unsafe {
|
||||
asm!(/* load exception frame pointer and call main handler */);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn divide_by_zero_handler(stack_frame: &ExceptionStackFrame)
|
||||
-> !
|
||||
{
|
||||
println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}",
|
||||
unsafe { &*stack_frame });
|
||||
loop {}
|
||||
}
|
||||
|
||||
```
|
||||
The naked wrapper function retrieves the exception stack frame pointer and then calls the `divide_by_zero_handler` with the pointer as argument. We can't use Rust code in naked functions, so we need to do both things in inline assembly.
|
||||
|
||||
Retrieving the pointer to the exception stack frame is easy: We just need to load it from the `rsp` register. Our wrapper function has no prologue (it's naked), so we can be sure that nothing modifies the register before.
|
||||
|
||||
Calling the main handler is a bit more complicated, since we need to pass the argument correctly. Our main handler uses the C calling convention, which specifies that the the first argument is passed in the `rdi` register. So we need to load the pointer value into `rdi` and then use the `call` instruction to call `divide_by_zero_handler`.
|
||||
|
||||
Translated to assembly, it looks like this:
|
||||
|
||||
```nasm
|
||||
mov rdi, rsp
|
||||
call divide_by_zero_handler
|
||||
```
|
||||
It moves the exception stack frame pointer from `rsp` to `rdi`, where the first argument is expected, and then calls the main handler. Let's create the corresponding inline assembly to complete our wrapper function:
|
||||
|
||||
```rust
|
||||
#[naked]
|
||||
extern "C" fn divide_by_zero_wrapper() -> ! {
|
||||
unsafe {
|
||||
asm!("mov rdi, rsp; call $0"
|
||||
:: "i"(divide_by_zero_handler as extern "C" fn(_) -> !)
|
||||
: "rdi" : "intel");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Instead of `call divide_by_zero_handler`, we use a placeholder again. The reason is Rust's name mangling, which changes the name of the `divide_by_zero_handler` function. To circumvent this, we pass a function pointer as input parameter (after the second colon). The `"i"` tells the compiler that it is an immediate value, which can be directly inserted for the placeholder. We also specify a clobber after the third colon, which tells the compiler that we change the value of the `rdi` register.
|
||||
|
||||
### Intrinsics::Unreachable
|
||||
When we try to compile it, we get the following error:
|
||||
|
||||
```
|
||||
error: computation may converge in a function marked as diverging
|
||||
--> src/interrupts/mod.rs:23:1
|
||||
|>
|
||||
23 |> extern "C" fn divide_by_zero_wrapper() -> ! {
|
||||
|> ^
|
||||
```
|
||||
The reason is that we marked our `divide_by_zero_wrapper` function as diverging (the `!`). We call another diverging function in inline assembly, so it is clear that the function diverges. However, the Rust compiler doesn't understand inline assembly, so it doesn't know that. To fix this, we tell the compiler that all code after the `asm!` macro is unreachable:
|
||||
|
||||
```rust
|
||||
#[naked]
|
||||
extern "C" fn divide_by_zero_wrapper() -> ! {
|
||||
unsafe {
|
||||
asm!("mov rdi, rsp; call $0"
|
||||
:: "i"(divide_by_zero_handler as extern "C" fn(_) -> !)
|
||||
: "rdi" : "intel");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The [intrinsics::unreachable] function is unstable, so we need to add `#![feature(core_intrinsics)]` to our `src/lib.rs`. It is just an annotation for the compiler and produces no real code. (Not to be confused with the [unreachable!] macro, which is completely different!)
|
||||
|
||||
[intrinsics::unreachable]: https://doc.rust-lang.org/nightly/core/intrinsics/fn.unreachable.html
|
||||
[unreachable!]: https://doc.rust-lang.org/nightly/core/macro.unreachable!.html
|
||||
|
||||
### It works!
|
||||
The last step is to update the interrupt descriptor table (IDT) to use our new wrapper function:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
idt.set_handler(0, divide_by_zero_wrapper); // changed
|
||||
idt
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Now we see a correct exception stack frame when we execute `make run`:
|
||||
|
||||

|
||||
|
||||
## Testing on real Hardware
|
||||
Virtual machines such as QEMU are very convenient to quickly test our kernel. However, they might behave a bit different than real hardware in some situations. So we should test our kernel on real hardware, too.
|
||||
|
||||
Let's do it by burning it to an USB stick:
|
||||
|
||||
```
|
||||
> sudo dd if=build/os-x86_64.iso of=/dev/sdX; and sync
|
||||
```
|
||||
|
||||
Replace `sdX` by the device name of your USB stick. But **be careful**! The command will erase everything on that device.
|
||||
|
||||
Now we should be able to boot from this USB stick. When we do it, we see that it works fine on real hardware, too. Great!
|
||||
|
||||
However, this section wouldn't exist if there weren't a problem. To trigger this problem, we add some example code to the start of our `divide_by_zero_handler`:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
extern "C" fn divide_by_zero_handler(...) {
|
||||
let x = (1u64, 2u64, 3u64);
|
||||
let y = Some(x);
|
||||
for i in (0..100).map(|z| (z, z - 1)) {}
|
||||
|
||||
println!(...);
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
This is just some garbage code that doesn't do anything useful. When we try it in QEMU using `make run`, it still works fine. However, when we burn it to an USB stick again and boot from it on real hardware, we see that our computer reboots just before printing the exception message.
|
||||
|
||||
So our code, which worked well in QEMU, _causes a triple fault_ on real hardware. What's happening?
|
||||
|
||||
### Reproducing the Bug in QEMU
|
||||
Debugging on a real machine is difficult. Fortunately there is a way to reproduce this bug in QEMU: We use Linux's [Kernel-based Virtual Machine] \(KVM) by passing the `‑enable-kvm` flag:
|
||||
|
||||
[Kernel-based Virtual Machine]: https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine
|
||||
|
||||
```
|
||||
> qemu-system-x86_64 -cdrom build/os-x86_64.iso -enable-kvm
|
||||
```
|
||||
|
||||
Now QEMU triple faults as well. This should make debugging much easier.
|
||||
|
||||
### Debugging
|
||||
|
||||
QEMU's `-d int`, which prints every exception, doesn't seem to work in KVM mode. However `-d cpu_reset` still works. It prints the complete CPU state whenever the CPU resets. Let's try it:
|
||||
|
||||
```
|
||||
> qemu-system-x86_64 -cdrom build/os-x86_64.iso -enable-kvm -d cpu_reset
|
||||
CPU Reset (CPU 0)
|
||||
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000
|
||||
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
|
||||
EIP=00000000 EFL=00000000 [-------] CPL=0 II=0 A20=0 SMM=0 HLT=0
|
||||
[...]
|
||||
CPU Reset (CPU 0)
|
||||
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000663
|
||||
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
|
||||
EIP=0000fff0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
||||
[...]
|
||||
CPU Reset (CPU 0)
|
||||
RAX=0000000000118cb8 RBX=0000000000000800 RCX=1d1d1d1d1d1d1d1d RDX=0..0000000
|
||||
RSI=0000000000112cd0 RDI=0000000000118d38 RBP=0000000000118d28 RSP=0..0118c68
|
||||
R8 =0000000000000000 R9 =0000000000000100 R10=0000000000118700 R11=0..0118a00
|
||||
R12=0000000000000000 R13=0000000000000000 R14=0000000000000000 R15=0..0000000
|
||||
RIP=000000000010cf08 RFL=00210002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
||||
[...]
|
||||
```
|
||||
The first two resets occur while the CPU is still in 32-bit mode (`EAX` instead of `RAX`), so we ignore them. The third reset is the interesting one, because it occurs in 64-bit mode. The register dump tells us that the instruction pointer (`rip`) was `0x10cf08` just before the reset. This might be the address of the instruction that caused the triple fault.
|
||||
|
||||
We can find the corresponding instruction by disassembling our kernel:
|
||||
|
||||
```
|
||||
objdump -d build/kernel-x86_64.bin | grep "10cf08:"
|
||||
10cf08: 0f 29 45 b0 movaps %xmm0,-0x50(%rbp)
|
||||
```
|
||||
The [movaps] instruction is an [SSE] instruction that moves aligned 128bit values. It can fail for a number of reasons:
|
||||
|
||||
[movaps]: https://www.felixcloutier.com/x86/movaps
|
||||
[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
|
||||
|
||||
1. For an illegal memory operand effective address in the CS, DS, ES, FS or GS segments.
|
||||
2. For an illegal address in the SS segment.
|
||||
3. If a memory operand is not aligned on a 16-byte boundary.
|
||||
4. For a page fault.
|
||||
5. If TS in CR0 is set.
|
||||
|
||||
The segment registers contain no meaningful values in long mode, so they can't contain illegal addresses. We did not change the TS bit in [CR0] and there is no reason for a page fault either. So it has to be option 3.
|
||||
|
||||
[CR0]: https://en.wikipedia.org/wiki/Control_register#CR0
|
||||
|
||||
### 16-byte Alignment
|
||||
Some SSE instructions such as `movaps` require that memory operands are 16-byte aligned. In our case, the instruction is `movaps %xmm0,-0x50(%rbp)`, which writes to address `rbp - 0x50`. Therefore `rbp` needs to be 16-byte aligned.
|
||||
|
||||
Let's look at the above `-d cpu_reset` dump again and check the value of `rbp`:
|
||||
|
||||
```
|
||||
CPU Reset (CPU 0)
|
||||
RAX=[...] RBX=[...] RCX=[...] RDX=[...]
|
||||
RSI=[...] RDI=[...] RBP=0000000000118d28 RSP=[...]
|
||||
...
|
||||
```
|
||||
`RBP` is `0x118d28`, which is _not_ 16-byte aligned. So this is the reason for the triple fault. (It seems like QEMU doesn't check the alignment for `movaps`, but real hardware of course does.)
|
||||
|
||||
But how did we end up with a misaligned `rbp` register?
|
||||
|
||||
### The Base Pointer
|
||||
In order to solve this mystery, we need to look at the disassembly of the preceding code:
|
||||
|
||||
```
|
||||
> objdump -d build/kernel-x86_64.bin | grep -B10 "10cf08:"
|
||||
000000000010cee0 <_ZN7blog_os10interrupts22divide_by_zero_handler17hE>:
|
||||
10cee0: 55 push %rbp
|
||||
10cee1: 48 89 e5 mov %rsp,%rbp
|
||||
10cee4: 48 81 ec c0 00 00 00 sub $0xc0,%rsp
|
||||
10ceeb: 48 8d 45 90 lea -0x70(%rbp),%rax
|
||||
10ceef: 48 b9 1d 1d 1d 1d 1d movabs $0x1d1d1d1d1d1d1d1d,%rcx
|
||||
10cef6: 1d 1d 1d
|
||||
10cef9: 48 89 4d 90 mov %rcx,-0x70(%rbp)
|
||||
10cefd: 48 89 7d f8 mov %rdi,-0x8(%rbp)
|
||||
10cf01: 0f 10 05 a8 51 00 00 movups 0x51a8(%rip),%xmm0
|
||||
10cf08: 0f 29 45 b0 movaps %xmm0,-0x50(%rbp)
|
||||
```
|
||||
At the last line we have the `movaps` instruction, which caused the triple fault. The exception occurs inside our `divide_by_zero_handler` function. We see that `rbp` is loaded with the value of `rsp` at the beginning (at `0x10cee1`). The `rbp` register holds the so-called _base pointer_, which points to the beginning of the stack frame. It is used in the rest of the function to address variables and other values on the stack.
|
||||
|
||||
The base pointer is initialized directly from the stack pointer (`rsp`) after pushing the old base pointer. There is no special alignment code, so the compiler blindly assumes that `(rsp - 8)`[^fn-rsp-8] is always 16-byte aligned. This seems to be wrong in our case. But why does the compiler assume this?
|
||||
|
||||
[^fn-rsp-8]: By pushing the old base pointer, `rsp` is updated to `rsp-8`.
|
||||
|
||||
### Calling Conventions
|
||||
The reason is that our exception handler is defined as `extern "C" function`, which specifies that it's using the C [calling convention]. On x86_64 Linux, the C calling convention is specified by the System V AMD64 ABI ([PDF][system v abi]). Section 3.2.2 defines the following:
|
||||
|
||||
[calling convention]: https://en.wikipedia.org/wiki/X86_calling_conventions
|
||||
[system v abi]: https://web.archive.org/web/20160801075139/https://www.x86-64.org/documentation/abi.pdf
|
||||
|
||||
> The end of the input argument area shall be aligned on a 16 byte boundary. In other words, the value (%rsp + 8) is always a multiple of 16 when control is transferred to the function entry point.
|
||||
|
||||
The “end of the input argument area” refers to the last stack-passed argument (in our case there aren't any). So the stack pointer must be 16 byte aligned whenever we `call` a C-compatible function. The `call` instruction then pushes the return value on the stack so that “the value (%rsp + 8) is a multiple of 16 when control is transferred to the function entry point”.
|
||||
|
||||
_Summary_: The calling convention requires a 16 byte aligned stack pointer before `call` instructions. The compiler relies on this requirement, but we broke it somehow. Thus the generated code triple faults due to a misaligned memory address in the `movaps` instruction.
|
||||
|
||||
### Fixing the Alignment
|
||||
In order to fix this bug, we need to make sure that the stack pointer is correctly aligned before calling `extern "C"` functions. Let's summarize the stack pointer modifications that occur before the exception handler is called:
|
||||
|
||||
1. The CPU aligns the stack pointer to a 16 byte boundary.
|
||||
2. The CPU pushes `ss`, `rsp`, `rflags`, `cs`, and `rip`. So it pushes five 8 byte registers, which makes `rsp` misaligned.
|
||||
3. The wrapper function calls `divide_by_zero_handler` with a misaligned stack pointer.
|
||||
|
||||
The problem is that we're pushing an uneven number of 8 byte registers. Thus we need to align the stack pointer again before the `call` instruction:
|
||||
|
||||
```rust
|
||||
#[naked]
|
||||
extern "C" fn divide_by_zero_wrapper() -> ! {
|
||||
unsafe {
|
||||
asm!("mov rdi, rsp
|
||||
sub rsp, 8 // align the stack pointer
|
||||
call $0"
|
||||
:: "i"(divide_by_zero_handler as extern "C" fn(_) -> !)
|
||||
: "rdi" : "intel");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The additional `sub rsp, 8` instruction aligns the stack pointer to a 16 byte boundary. Now it should work on real hardware (and in QEMU KVM mode) again.
|
||||
|
||||
## A Handler Macro
|
||||
The next step is to add handlers for other exceptions. However, we would need wrapper functions for them too. To avoid this code duplication, we create a `handler` macro that creates the wrapper functions for us:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
macro_rules! handler {
|
||||
($name: ident) => {{
|
||||
#[naked]
|
||||
extern "C" fn wrapper() -> ! {
|
||||
unsafe {
|
||||
asm!("mov rdi, rsp
|
||||
sub rsp, 8 // align the stack pointer
|
||||
call $0"
|
||||
:: "i"($name as extern "C" fn(
|
||||
&ExceptionStackFrame) -> !)
|
||||
: "rdi" : "intel");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
wrapper
|
||||
}}
|
||||
}
|
||||
```
|
||||
The macro takes a single Rust identifier (`ident`) as argument and expands to a `{}` block (hence the double braces). The block defines a new wrapper function that calls the function `$name` and passes a pointer to the exception stack frame. Note that we're fixing the argument type to `&ExceptionStackFrame`. If we used a `_` like before, the passed function could accept an arbitrary argument, which would lead to ugly bugs at runtime.
|
||||
|
||||
Now we can remove the `divide_by_zero_wrapper` and use our new `handler!` macro instead:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
idt.set_handler(0, handler!(divide_by_zero_handler)); // new
|
||||
idt
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Note that the `handler!` macro needs to be defined above the static `IDT`, because macros are only available after their definition.
|
||||
|
||||
### Invalid Opcode Exception
|
||||
With the `handler!` macro we can create new handler functions easily. For example, we can add a handler for the invalid opcode exception as follows:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
idt.set_handler(0, handler!(divide_by_zero_handler));
|
||||
idt.set_handler(6, handler!(invalid_opcode_handler)); // new
|
||||
idt
|
||||
};
|
||||
}
|
||||
|
||||
extern "C" fn invalid_opcode_handler(stack_frame: &ExceptionStackFrame)
|
||||
-> !
|
||||
{
|
||||
let stack_frame = unsafe { &*stack_frame };
|
||||
println!("\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}",
|
||||
stack_frame.instruction_pointer, stack_frame);
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
Invalid opcode faults have the vector number 6, so we set the 6th IDT entry. This time we additionally print the address of the invalid instruction.
|
||||
|
||||
We can test our new handler with the special [ud2] instruction, which generates a invalid opcode:
|
||||
|
||||
[ud2]: https://www.felixcloutier.com/x86/ud
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
...
|
||||
|
||||
// initialize our IDT
|
||||
interrupts::init();
|
||||
|
||||
// provoke a invalid opcode exception
|
||||
unsafe { asm!("ud2") };
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
## Exceptions with Error Codes
|
||||
When a divide-by-zero exception occurs, we immediately know the reason: Someone tried to divide by zero. In contrast, there are faults with many possible causes. For example, a page fault occurs in many occasions: When accessing a non-present page, when writing to a read-only page, when the page table is malformed, etc. In order to differentiate these causes, the CPU pushes an additional error code onto the stack for such exceptions, which gives additional information.
|
||||
|
||||
### A new Macro
|
||||
Since the CPU pushes an additional error code, the stack frame is different and our `handler!` macro is not applicable. Therefore we create a new `handler_with_error_code!` macro for them:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
macro_rules! handler_with_error_code {
|
||||
($name: ident) => {{
|
||||
#[naked]
|
||||
extern "C" fn wrapper() -> ! {
|
||||
unsafe {
|
||||
asm!("pop rsi // pop error code into rsi
|
||||
mov rdi, rsp
|
||||
sub rsp, 8 // align the stack pointer
|
||||
call $0"
|
||||
:: "i"($name as extern "C" fn(
|
||||
&ExceptionStackFrame, u64) -> !)
|
||||
: "rdi","rsi" : "intel");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
wrapper
|
||||
}}
|
||||
}
|
||||
```
|
||||
The difference to the `handler!` macro is the additional error code argument. The CPU pushes the error code last, so we pop it right at the beginning of the wrapper function. We pop it into `rsi` because the C calling convention expects the second argument in it.
|
||||
|
||||
### A Page Fault Handler
|
||||
Let's write a page fault handler which analyzes and prints the error code:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame,
|
||||
error_code: u64) -> !
|
||||
{
|
||||
println!(
|
||||
"\nEXCEPTION: PAGE FAULT with error code {:?}\n{:#?}",
|
||||
error_code, unsafe { &*stack_frame });
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
We need to register our new handler function in the static interrupt descriptor table (IDT):
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
|
||||
idt.set_handler(0, handler!(divide_by_zero_handler));
|
||||
idt.set_handler(6, handler!(invalid_opcode_handler));
|
||||
// new
|
||||
idt.set_handler(14, handler_with_error_code!(page_fault_handler));
|
||||
|
||||
idt
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Page faults have the vector number 14, so we set the 14th IDT entry.
|
||||
|
||||
#### Testing it
|
||||
Let's test our new page fault handler by provoking a page fault in our main function:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
...
|
||||
|
||||
// initialize our IDT
|
||||
interrupts::init();
|
||||
|
||||
// provoke a page fault
|
||||
unsafe { *(0xdeadbeaf as *mut u64) = 42 };
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
We get the following output:
|
||||
|
||||

|
||||
|
||||
### The Page Fault Error Code
|
||||
“Error code 2” is not really an useful error message. Let's improve this by creating a `PageFaultErrorCode` type:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
bitflags! {
|
||||
struct PageFaultErrorCode: u64 {
|
||||
const PROTECTION_VIOLATION = 1 << 0;
|
||||
const CAUSED_BY_WRITE = 1 << 1;
|
||||
const USER_MODE = 1 << 2;
|
||||
const MALFORMED_TABLE = 1 << 3;
|
||||
const INSTRUCTION_FETCH = 1 << 4;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- When the `PROTECTION_VIOLATION` flag is set, the page fault was caused e.g. by a write to a read-only page. If it's not set, it was caused by accessing a non-present page.
|
||||
- The `CAUSED_BY_WRITE` flag specifies if the fault was caused by a write (if set) or a read (if not set).
|
||||
- The `USER_MODE` flag is set when the fault occurred in non-privileged mode.
|
||||
- The `MALFORMED_TABLE` flag is set when the page table entry has a 1 in a reserved field.
|
||||
- When the `INSTRUCTION_FETCH` flag is set, the page fault occurred while fetching the next instruction.
|
||||
|
||||
Now we can improve our page fault error message by using the new `PageFaultErrorCode`. We also print the accessed memory address:
|
||||
|
||||
```rust
|
||||
extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame,
|
||||
error_code: u64) -> !
|
||||
{
|
||||
use x86_64::registers::control_regs;
|
||||
println!(
|
||||
"\nEXCEPTION: PAGE FAULT while accessing {:#x}\
|
||||
\nerror code: {:?}\n{:#?}",
|
||||
unsafe { control_regs::cr2() },
|
||||
PageFaultErrorCode::from_bits(error_code).unwrap(),
|
||||
unsafe { &*stack_frame });
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
The `from_bits` function tries to convert the `u64` into a `PageFaultErrorCode`. We use `unwrap` to panic if the error code has invalid bits set, since this indicates an error in our `PageFaultErrorCode` definition or a stack corruption. We also print the contents of the `cr2` register. It contains the accessed memory address, which was the cause of the page fault.
|
||||
|
||||
Now we get a useful error message when a page fault occurs, which allows us to debug it more easily:
|
||||
|
||||

|
||||
|
||||
As expected, the page fault was caused by write to `0xdeadbeaf`. The `PROTECTION_VIOLATION` flag is not set, so the accessed page was not present.
|
||||
|
||||
## What's next?
|
||||
Now we're able to catch and analyze various exceptions. The next step is to _resolve_ exceptions, if possible. An example is [demand paging]: The OS swaps out memory pages to disk so that a page fault occurs when the page is accessed the next time. In that case, the OS can resolve the exception by bringing the page back into memory. Afterwards, the OS resumes the interrupted program as if nothing had happened.
|
||||
|
||||
[demand paging]: https://en.wikipedia.org/wiki/Demand_paging
|
||||
|
||||
The next post will implement the first portion of demand paging: saving and restoring the complete state of an program. This will allow us to transparently interrupt and resume programs in the future.
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
@@ -0,0 +1,905 @@
|
||||
+++
|
||||
title = "Returning from Exceptions"
|
||||
weight = 3
|
||||
path = "returning-from-exceptions"
|
||||
aliases = ["returning-from-exceptions.html"]
|
||||
date = 2016-09-21
|
||||
template = "edition-1/page.html"
|
||||
[extra]
|
||||
updated = "2016-11-01"
|
||||
+++
|
||||
|
||||
In this post, we learn how to return from exceptions correctly. In the course of this, we will explore the `iretq` instruction, the C calling convention, multimedia registers, and the red zone.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
As always, the complete source code is on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
|
||||
|
||||
[GitHub]: https://github.com/phil-opp/blog_os/tree/returning_from_exceptions
|
||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
||||
|
||||
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||
|
||||
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||
|
||||
## Introduction
|
||||
Most exceptions are fatal and can't be resolved. For example, we can't return from a divide-by-zero exception in a reasonable way. However, there are some exceptions that we can resolve:
|
||||
|
||||
Imagine a system that uses [memory mapped files]: We map a file into the virtual address space without loading it into memory. Whenever we access a part of the file for the first time, a page fault occurs. However, this page fault is not fatal. We can resolve it by loading the corresponding page from disk into memory and setting the `present` flag in the page table. Then we can return from the page fault handler and restart the failed instruction, which now successfully accesses the file data.
|
||||
|
||||
[memory mapped files]: https://en.wikipedia.org/wiki/Memory-mapped_file
|
||||
|
||||
Memory mapped files are completely out of scope for us right now (we have neither a file concept nor a hard disk driver). So we need an exception that we can resolve easily so that we can return from it in a reasonable way. Fortunately, there is an exception that needs no resolution at all: the breakpoint exception.
|
||||
|
||||
## The Breakpoint Exception
|
||||
The breakpoint exception is the perfect exception to test our upcoming return-from-exception logic. Its only purpose is to temporary pause a program when the breakpoint instruction `int3` is executed.
|
||||
|
||||
The breakpoint exception is commonly used in debuggers: When the user sets a breakpoint, the debugger overwrites the corresponding instruction with the `int3` instruction so that the CPU throws the breakpoint exception when it reaches that line. When the user wants to continue the program, the debugger replaces the `int3` instruction with the original instruction again and continues the program. For more details, see the [How debuggers work] series.
|
||||
|
||||
[How debuggers work]: https://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints
|
||||
|
||||
For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.
|
||||
|
||||
[set the page table flags]: @/edition-1/posts/07-remap-the-kernel/index.md#using-the-correct-flags
|
||||
|
||||
### Catching Breakpoints
|
||||
Let's start by defining a handler function for the breakpoint exception:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
extern "C" fn breakpoint_handler(stack_frame: &ExceptionStackFrame) -> !
|
||||
{
|
||||
let stack_frame = unsafe { &*stack_frame };
|
||||
println!("\nEXCEPTION: BREAKPOINT at {:#x}\n{:#?}",
|
||||
stack_frame.instruction_pointer, stack_frame);
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
We print an error message and also output the instruction pointer and the rest of the stack frame. Note that this function does _not_ return yet, since our `handler!` macro still requires a diverging function.
|
||||
|
||||
We need to register our new handler function in the interrupt descriptor table (IDT):
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
|
||||
idt.set_handler(0, handler!(divide_by_zero_handler));
|
||||
idt.set_handler(3, handler!(breakpoint_handler)); // new
|
||||
idt.set_handler(6, handler!(invalid_opcode_handler));
|
||||
idt.set_handler(14, handler_with_error_code!(page_fault_handler));
|
||||
|
||||
idt
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
We set the IDT entry with number 3 since it's the vector number of the breakpoint exception.
|
||||
|
||||
#### Testing it
|
||||
In order to test it, we insert an `int3` instruction in our `rust_main`:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
...
|
||||
#[macro_use] // needed for the `int!` macro
|
||||
extern crate x86_64;
|
||||
...
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main(...) {
|
||||
...
|
||||
interrupts::init();
|
||||
|
||||
// trigger a breakpoint exception
|
||||
unsafe { int!(3) };
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
When we execute `make run`, we see the following:
|
||||
|
||||

|
||||
|
||||
It works! Now we “just” need to return from the breakpoint handler somehow so that we see the `It did not crash` message again.
|
||||
|
||||
## Returning from Exceptions
|
||||
So how do we return from exceptions? To make it easier, we look at a normal function return first:
|
||||
|
||||

|
||||
|
||||
When calling a function, the `call` instruction pushes the return address on the stack. When the called function is finished, it can return to the parent function through the `ret` instruction, which pops the return address from the stack and then jumps to it.
|
||||
|
||||
The exception stack frame, in contrast, looks a bit different:
|
||||
|
||||

|
||||
|
||||
Instead of pushing a return address, the CPU pushes the stack and instruction pointers (with their segment descriptors), the RFLAGS register, and an optional error code. It also aligns the stack pointer to a 16 byte boundary before pushing values.
|
||||
|
||||
So we can't use a normal `ret` instruction, since it expects a different stack frame layout. Instead, there is a special instruction for returning from exceptions: `iretq`.
|
||||
|
||||
### The `iretq` Instruction
|
||||
The `iretq` instruction is the one and only way to return from exceptions and is specifically designed for this purpose. The AMD64 instruction manual ([PDF][amd-manual]) even demands that `iretq` “_must_ be used to terminate the exception or interrupt handler associated with the exception”.
|
||||
|
||||
[amd-manual]: https://www.amd.com/system/files/TechDocs/24594.pdf
|
||||
|
||||
IRETQ restores `rip`, `cs`, `rflags`, `rsp`, and `ss` from the values saved on the stack and thus continues the interrupted program. The instruction does not handle the optional error code, so it must be popped from the stack before.
|
||||
|
||||
We see that `iretq` treats the stored instruction pointer as return address. For most exceptions, the stored `rip` points to the instruction that caused the fault. So by executing `iretq`, we restart the failing instruction. This makes sense because we should have resolved the exception when returning from it, so the instruction should no longer fail (e.g. the accessed part of the memory mapped file is now present in memory).
|
||||
|
||||
The situation is a bit different for the breakpoint exception, since it needs no resolution. Restarting the `int3` instruction wouldn't make sense, since it would cause a new breakpoint exception and we would enter an endless loop. For this reason the hardware designers decided that the stored `rip` should point to the next instruction after the `int3` instruction.
|
||||
|
||||
Let's check this for our breakpoint handler. Remember, the handler printed the following message (see the image above):
|
||||
|
||||
```
|
||||
EXCEPTION: BREAKPOINT at 0x110970
|
||||
```
|
||||
|
||||
So let's disassemble the instruction at `0x110970` and its predecessor:
|
||||
|
||||
```bash
|
||||
> objdump -d build/kernel-x86_64.bin | grep -B1 "110970:"
|
||||
11096f: cc int3
|
||||
110970: 48 c7 01 2a 00 00 00 movq $0x2a,(%rcx)
|
||||
```
|
||||
|
||||
We see that `0x110970` indeed points to the next instruction after `int3`. So we can simply jump to the stored instruction pointer when we want to return from the breakpoint exception.
|
||||
|
||||
### Implementation
|
||||
Let's update our `handler!` macro to support non-diverging exception handlers:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
macro_rules! handler {
|
||||
($name: ident) => {{
|
||||
#[naked]
|
||||
extern "C" fn wrapper() -> ! {
|
||||
unsafe {
|
||||
asm!("mov rdi, rsp
|
||||
sub rsp, 8 // align the stack pointer
|
||||
call $0"
|
||||
:: "i"($name as extern "C" fn(
|
||||
&ExceptionStackFrame)) // no longer diverging
|
||||
: "rdi" : "intel", "volatile");
|
||||
|
||||
// new
|
||||
asm!("add rsp, 8 // undo stack pointer alignment
|
||||
iretq"
|
||||
:::: "intel", "volatile");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
wrapper
|
||||
}}
|
||||
}
|
||||
```
|
||||
|
||||
When an exception handler returns from the `call` instruction, we use the `iretq` instruction to continue the interrupted program. Note that we need to undo the stack pointer alignment before, so that `rsp` points to the end of the exception stack frame again.
|
||||
|
||||
We've changed the handler function type, so we need to adjust our existing exception handlers:
|
||||
|
||||
```diff
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
extern "C" fn divide_by_zero_handler(
|
||||
- stack_frame: &ExceptionStackFrame) -> ! {...}
|
||||
+ stack_frame: &ExceptionStackFrame) {...}
|
||||
|
||||
extern "C" fn invalid_opcode_handler(
|
||||
- stack_frame: &ExceptionStackFrame) -> ! {...}
|
||||
+ stack_frame: &ExceptionStackFrame) {...}
|
||||
|
||||
extern "C" fn breakpoint_handler(
|
||||
- stack_frame: &ExceptionStackFrame) -> ! {
|
||||
+ stack_frame: &ExceptionStackFrame) {
|
||||
println!(...);
|
||||
- loop {}
|
||||
}
|
||||
```
|
||||
Note that we also removed the `loop {}` at the end of our `breakpoint_handler` so that it no longer diverges. The `divide_by_zero_handler` and the `invalid_opcode_handler` still diverge (albeit the new function type would allow a return).
|
||||
|
||||
### Testing
|
||||
Let's try our new `iretq` logic:
|
||||
|
||||

|
||||
|
||||
Instead of the expected _“It did not crash”_ message after the breakpoint exception, we get a page fault. The strange thing is that our kernel tried to access address `0x1`, which should never happen. So it seems like we messed up something important.
|
||||
|
||||
### Debugging
|
||||
Let's debug it using GDB. For that we execute `make debug` in one terminal (which starts QEMU with the `-s -S` flags) and then `make gdb` (which starts and connects GDB) in a second terminal. For more information about GDB debugging, check out our [Set Up GDB] guide.
|
||||
|
||||
[Set Up GDB]: @/edition-1/extra/set-up-gdb/index.md
|
||||
|
||||
First we want to check if our `iretq` was successful. Therefore we set a breakpoint on the `println!("It did not crash line!")` statement in `src/lib.rs`. Let's assume that it's on line 61:
|
||||
|
||||
```
|
||||
(gdb) break blog_os/src/lib.rs:61
|
||||
Breakpoint 1 at 0x110a95: file /home/.../blog_os/src/lib.rs, line 61.
|
||||
```
|
||||
|
||||
This line is after the `int3` instruction, so we know that the `iretq` succeeded when the breakpoint is hit. To test this, we continue the execution:
|
||||
|
||||
```
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, blog_os::rust_main (multiboot_information_address=1539136)
|
||||
at /home/.../blog_os/src/lib.rs:61
|
||||
61 println!("It did not crash!");
|
||||
|
||||
```
|
||||
It worked! So our kernel successfully returned from the `int3` instruction, which means that the `iretq` itself works.
|
||||
|
||||
However, when we `continue` the execution again, we get the page fault. So the exception occurs somewhere in the `println` logic. This means that it occurs in code generated by the compiler (and not e.g. in inline assembly). But the compiler should never access `0x1`, so how is this happening?
|
||||
|
||||
The answer is that we've used the wrong _calling convention_ for our exception handlers. Thus, we violate some compiler invariants so that the code that works fine without intermediate exceptions starts to violate memory safety when it's executed after a breakpoint exception.
|
||||
|
||||
## Calling Conventions
|
||||
Exceptions are quite similar to function calls: The CPU jumps to the first instruction of the (handler) function and executes the function. Afterwards, if the function is not diverging, the CPU jumps to the return address and continues the execution of the parent function.
|
||||
|
||||
However, there is a major difference between exceptions and function calls: A function call is invoked voluntary by a compiler inserted `call` instruction, while an exception might occur at _any_ instruction. In order to understand the consequences of this difference, we need to examine function calls in more detail.
|
||||
|
||||
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
|
||||
|
||||
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
|
||||
[System V ABI]: https://refspecs.linuxbase.org/elf/gabi41.pdf
|
||||
|
||||
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
||||
- additional arguments are passed on the stack
|
||||
- results are returned in `rax` and `rdx`
|
||||
|
||||
Note that Rust does not follow the C ABI (in fact, [there isn't even a Rust ABI yet][rust abi]). So these rules apply only to functions declared as `extern "C" fn`.
|
||||
|
||||
[rust abi]: https://github.com/rust-lang/rfcs/issues/600
|
||||
|
||||
### Preserved and Scratch Registers
|
||||
The calling convention divides the registers in two parts: _preserved_ and _scratch_ registers.
|
||||
|
||||
The values of the preserved register must remain unchanged across function calls. So a called function (the _“callee”_) is only allowed to overwrite these registers if it restores their original values before returning. Therefore these registers are called _“callee-saved”_. A common pattern is to save these registers to the stack at the function's beginning and restore them just before returning.
|
||||
|
||||
In contrast, a called function is allowed to overwrite scratch registers without restrictions. If the caller wants to preserve the value of a scratch register across a function call, it needs to backup and restore it (e.g. by pushing it to the stack before the function call). So the scratch registers are _caller-saved_.
|
||||
|
||||
On x86_64, the C calling convention specifies the following preserved and scratch registers:
|
||||
|
||||
preserved registers | scratch registers
|
||||
---|---
|
||||
`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11`
|
||||
_callee-saved_ | _caller-saved_
|
||||
|
||||
The compiler knows these rules, so it generates the code accordingly. For example, most functions begin with a `push rbp`, which backups `rbp` on the stack (because it's a callee-saved register).
|
||||
|
||||
### The Exception Calling Convention
|
||||
In contrast to function calls, exceptions can occur on _any_ instruction. In most cases we don't even know at compile time if the generated code will cause an exception. For example, the compiler can't know if an instruction causes a stack overflow or an other page fault.
|
||||
|
||||
Since we don't know when an exception occurs, we can't backup any registers before. This means that we can't use a calling convention that relies on caller-saved registers for our exception handlers. But we do so at the moment: Our exception handlers are declared as `extern "C" fn` and thus use the C calling convention.
|
||||
|
||||
So here is what happens:
|
||||
|
||||
- `rust_main` is executing; it writes some memory address into `rax`.
|
||||
- The `int3` instruction causes a breakpoint exception.
|
||||
- Our `breakpoint_handler` prints to the screen and assumes that it can overwrite `rax` freely (since it's a scratch register). Somehow the value `0` ends up in `rax`.
|
||||
- We return from the breakpoint exception using `iretq`.
|
||||
- `rust_main` continues and accesses the memory address in `rax`.
|
||||
- The CPU tries to access address `0x1`, which causes a page fault.
|
||||
|
||||
So our exception handler erroneously assumes that the scratch registers were saved by the caller. But the caller (`rust_main`) couldn't save any registers since it didn't know that an exception occurs. So nobody saves `rax` and the other scratch registers, which leads to the page fault.
|
||||
|
||||
The problem is that we use a calling convention with caller-saved registers for our exception handlers. Instead, we need a calling convention means that preserves _all registers_. In other words, all registers must be callee-saved:
|
||||
|
||||
```rust
|
||||
extern "all-registers-callee-saved" fn exception_handler() {...}
|
||||
```
|
||||
|
||||
Unfortunately, Rust does not support such a calling convention. It was [proposed once][interrupt calling conventions], but did not get accepted for various reasons. The primary reason was that such calling conventions can be simulated by writing a naked wrapper function.
|
||||
|
||||
(Remember: [Naked functions] are functions without prologue and can contain only inline assembly. They were discussed in the [previous post][naked fn post].)
|
||||
|
||||
[interrupt calling conventions]: https://github.com/rust-lang/rfcs/pull/1275
|
||||
[Naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
||||
[naked fn post]: @/edition-1/extra/naked-exceptions/02-better-exception-messages/index.md#naked-functions
|
||||
|
||||
### A naked wrapper function
|
||||
|
||||
Such a naked wrapper function might look like this:
|
||||
|
||||
```rust
|
||||
#[naked]
|
||||
extern "C" fn calling_convention_wrapper() {
|
||||
unsafe {
|
||||
asm!("
|
||||
push rax
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
// TODO: call exception handler with C calling convention
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rax
|
||||
" :::: "intel", "volatile");
|
||||
}
|
||||
}
|
||||
```
|
||||
This wrapper function saves all _scratch_ registers to the stack before calling the exception handler and restores them afterwards. Note that we `pop` the registers in reverse order.
|
||||
|
||||
We don't need to backup _preserved_ registers since they are callee-saved in the C calling convention. Thus, the compiler already takes care of preserving their values.
|
||||
|
||||
### Fixing our Handler Macro
|
||||
Let's update our handler macro to fix the calling convention problem. Therefore we need to backup and restore all scratch registers. For that we create two new macros:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
macro_rules! save_scratch_registers {
|
||||
() => {
|
||||
asm!("push rax
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
" :::: "intel", "volatile");
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! restore_scratch_registers {
|
||||
() => {
|
||||
asm!("pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rax
|
||||
" :::: "intel", "volatile");
|
||||
}
|
||||
}
|
||||
```
|
||||
We need to declare these macros _above_ our `handler` macro, since macros are only available after their declaration.
|
||||
|
||||
Now we can use these macros to fix our `handler!` macro:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
macro_rules! handler {
|
||||
($name: ident) => {{
|
||||
#[naked]
|
||||
extern "C" fn wrapper() -> ! {
|
||||
unsafe {
|
||||
save_scratch_registers!();
|
||||
asm!("mov rdi, rsp
|
||||
add rdi, 9*8 // calculate exception stack frame pointer
|
||||
// sub rsp, 8 (stack is aligned already)
|
||||
call $0"
|
||||
:: "i"($name as
|
||||
extern "C" fn(&ExceptionStackFrame))
|
||||
: "rdi" : "intel", "volatile");
|
||||
|
||||
restore_scratch_registers!();
|
||||
asm!("
|
||||
// add rsp, 8 (undo stack alignment; not needed anymore)
|
||||
iretq"
|
||||
:::: "intel", "volatile");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
wrapper
|
||||
}}
|
||||
}
|
||||
```
|
||||
|
||||
It's important that we save the registers first, before we modify any of them. After the `call` instruction (but before `iretq`) we restore the registers again. Because we're now changing `rsp` (by pushing the register values) before we load it into `rdi`, we would get a wrong exception stack frame pointer. Therefore we need to adjust it by adding the number of bytes we push. We push 9 registers that are 8 bytes each, so `9 * 8` bytes in total.
|
||||
|
||||
Note that we no longer need to manually align the stack pointer, because we're pushing an uneven number of registers in `save_scratch_registers`. Thus the stack pointer already has the required 16-byte alignment.
|
||||
|
||||
### Testing it again
|
||||
Let's test it again with our corrected `handler!` macro:
|
||||
|
||||

|
||||
|
||||
The page fault is gone and we see the _“It did not crash”_ message again!
|
||||
|
||||
So the page fault occurred because our exception handler didn't preserve the scratch register `rax`. Our new `handler!` macro fixes this problem by saving all scratch registers (including `rax`) before calling exception handlers. Thus, `rax` still contains the valid memory address when `rust-main` continues execution.
|
||||
|
||||
## Multimedia Registers
|
||||
When we discussed calling conventions above, we assumed that a x86_64 CPU only has the following 16 registers: `rax`, `rbx`, `rcx`, `rdx`, `rsi`, `rdi`, `rsp`, `rbp`, `r8`, `r9`, `r10`, `r11`.`r12`, `r13`, `r14`, and `r15`. These registers are called _general purpose registers_ since each of them can be used for arithmetic and load/store instructions.
|
||||
|
||||
However, modern CPUs also have a set of _special purpose registers_, which can be used to improve performance in several use cases. On x86_64, the most important set of special purpose registers are the _multimedia registers_. These registers are larger than the general purpose registers and can be used to speed up audio/video processing or matrix calculations. For example, we could use them to add two 4-dimensional vectors _in a single CPU instruction_:
|
||||
|
||||

|
||||
|
||||
Such multimedia instructions are called [Single Instruction Multiple Data (SIMD)] instructions, because they simultaneously perform an operation (e.g. addition) on multiple data words. Good compilers are able to transform normal loops into such SIMD code automatically. This process is called [auto-vectorization] and can lead to huge performance improvements.
|
||||
|
||||
[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
|
||||
[auto-vectorization]: https://en.wikipedia.org/wiki/Automatic_vectorization
|
||||
|
||||
However, auto-vectorization causes a problem for us: Most of the multimedia registers are caller-saved. According to our discussion of calling conventions above, this means that our exception handlers erroneously assume that they are allowed to overwrite them without preserving their values.
|
||||
|
||||
We don't use any multimedia registers explicitly, but the Rust compiler might auto-vectorize our code (including the exception handlers). Thus we could silently clobber the multimedia registers, which leads to the same problems as above:
|
||||
|
||||

|
||||
|
||||
This example shows a program that is using the first three multimedia registers (`mm0` to `mm2`). At some point, an exception occurs and control is transferred to the exception handler. The exception handler uses `mm1` for its own data and thus overwrites the previous value. When the exception is resolved, the CPU continues the interrupted program again. However, the program is now corrupt since it relies on the original `mm1` value.
|
||||
|
||||
### Saving and Restoring Multimedia Registers
|
||||
In order to fix this problem, we need to backup all caller-saved multimedia registers before we call the exception handler. The problem is that the set of multimedia registers varies between CPUs. There are different standards:
|
||||
|
||||
- [MMX]: The MMX instruction set was introduced in 1997 and defines eight 64 bit registers called `mm0` through `mm7`. These registers are just aliases for the registers of the [x87 floating point unit].
|
||||
- [SSE]: The _Streaming SIMD Extensions_ instruction set was introduced in 1999. Instead of re-using the floating point registers, it adds a completely new register set. The sixteen new registers are called `xmm0` through `xmm15` and are 128 bits each.
|
||||
- [AVX]: The _Advanced Vector Extensions_ are extensions that further increase the size of the multimedia registers. The new registers are called `ymm0` through `ymm15` and are 256 bits each. They extend the `xmm` registers, so e.g. `xmm0` is the lower (or upper?) half of `ymm0`.
|
||||
|
||||
[MMX]: https://en.wikipedia.org/wiki/MMX_(instruction_set)
|
||||
[x87 floating point unit]: https://en.wikipedia.org/wiki/X87
|
||||
[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
|
||||
[AVX]: https://en.wikipedia.org/wiki/Advanced_Vector_Extensions
|
||||
|
||||
The Rust compiler (and LLVM) assume that the `x86_64-unknown-linux-gnu` target supports only MMX and SSE, so we don't need to save the `ymm0` through `ymm15`. But we need to save `xmm0` through `xmm15` and also `mm0` through `mm7`. There is a special instruction to do this: [fxsave]. This instruction saves the floating point and multimedia state to a given address. It needs _512 bytes_ to store that state.
|
||||
|
||||
[fxsave]: https://www.felixcloutier.com/x86/fxsave
|
||||
|
||||
In order to save/restore the multimedia registers, we _could_ add new macros:
|
||||
|
||||
```rust
|
||||
macro_rules! save_multimedia_registers {
|
||||
() => {
|
||||
asm!("sub rsp, 512
|
||||
fxsave [rsp]
|
||||
" :::: "intel", "volatile");
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! restore_multimedia_registers {
|
||||
() => {
|
||||
asm!("fxrstor [rsp]
|
||||
add rsp, 512
|
||||
" :::: "intel", "volatile");
|
||||
}
|
||||
}
|
||||
```
|
||||
First, we reserve the 512 bytes on the stack and then we use `fxsave` to backup the multimedia registers. In order to restore them later, we use the [fxrstor] instruction. Note that `fxsave` and `fxrstor` require a 16 byte aligned memory address.
|
||||
|
||||
[fxrstor]: https://www.felixcloutier.com/x86/fxrstor
|
||||
|
||||
However, _we won't do it that way_. The problem is the large amount of memory required. We will reuse the same code when we handle hardware interrupts in a future post. So for each mouse click, pressed key, or arrived network package we need to write 512 bytes to memory. This would be a huge performance problem.
|
||||
|
||||
Fortunately, there exists an alternative solution.
|
||||
|
||||
### Disabling Multimedia Extensions
|
||||
We just disable MMX, SSE, and all the other fancy multimedia extensions in our kernel[^fn-userspace-sse]. This way, our exception handlers won't clobber the multimedia registers because they won't use them at all.
|
||||
|
||||
[^fn-userspace-sse]: Userspace programs will still be able to use the multimedia registers.
|
||||
|
||||
This solution has its own disadvantages, of course. For example, it leads to slower kernel code because the compiler can't perform any auto-vectorization optimizations. But it's still the faster solution (since we save many memory accesses) and most kernels do it this way (including Linux).
|
||||
|
||||
So how do we disable MMX and SSE? Well, we just tell the compiler that our target system doesn't support it. Since the very beginning, we're compiling our kernel for the `x86_64-unknown-linux-gnu` target. This worked fine so far, but now we want a different target without support for multimedia extensions. We can do so by creating a _target configuration file_.
|
||||
|
||||
### Target Specifications
|
||||
In order to disable the multimedia extensions for our kernel, we need to compile for a custom target. We want a target that is equal to `x86_64-unknown-linux-gnu`, but without MMX and SSE support. Rust allows us to specify such a target using a JSON configuration file.
|
||||
|
||||
A minimal target specification that describes the `x86_64-unknown-linux-gnu` target looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "64",
|
||||
"target-c-int-width": "32",
|
||||
"arch": "x86_64",
|
||||
"os": "none"
|
||||
}
|
||||
```
|
||||
|
||||
The `llvm-target` field specifies the target triple that is passed to LLVM. We want to derive a 64-bit Linux target, so we choose `x86_64-unknown-linux-gnu`. The `data-layout` field is also passed to LLVM and specifies how data should be laid out in memory. It consists of various specifications separated by a `-` character. For example, the `e` means little endian and `S128` specifies that the stack should be 128 bits (= 16 byte) aligned. The format is described in detail in the [LLVM documentation][data layout] but there shouldn't be a reason to change this string.
|
||||
|
||||
The other fields are used for conditional compilation. This allows crate authors to use `cfg` variables to write special code for depending on the OS or the architecture. There isn't any up-to-date documentation about these fields but the [corresponding source code][target specification] is quite readable.
|
||||
|
||||
[data layout]: https://llvm.org/docs/LangRef.html#data-layout
|
||||
[target specification]: https://github.com/rust-lang/rust/blob/c772948b687488a087356cb91432425662e034b9/src/librustc_back/target/mod.rs#L194-L214
|
||||
|
||||
#### Disabling MMX and SSE
|
||||
In order to disable the multimedia extensions, we create a new target named `x86_64-blog_os`. To describe this target, we create a file named `x86_64-blog_os.json` in the project root with the following content:
|
||||
|
||||
```json
|
||||
{
|
||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "64",
|
||||
"target-c-int-width": "32",
|
||||
"arch": "x86_64",
|
||||
"os": "none",
|
||||
"features": "-mmx,-sse"
|
||||
}
|
||||
```
|
||||
|
||||
It's equal to `x86_64-unknown-linux-gnu` target but has one additional option: `"features": "-mmx,-sse"`. So we added two target _features_: `-mmx` and `-sse`. The minus prefix defines that our target does _not_ support this feature. So by specifying `-mmx` and `-sse`, we disable the default `mmx` and `sse` features.
|
||||
|
||||
In order to compile for the new target, we need to adjust our Makefile:
|
||||
|
||||
```diff
|
||||
# in `Makefile`
|
||||
|
||||
arch ?= x86_64
|
||||
-target ?= $(arch)-unknown-linux-gnu
|
||||
+target ?= $(arch)-blog_os
|
||||
...
|
||||
```
|
||||
The new target name (`x86_64-blog_os`) is the file name of the JSON configuration file without the `.json` extension.
|
||||
|
||||
### Cross compilation
|
||||
Let's try if our kernel still works with the new target:
|
||||
|
||||
```
|
||||
> make run
|
||||
Compiling raw-cpuid v2.0.1
|
||||
Compiling rlibc v0.1.5
|
||||
Compiling x86 v0.7.1
|
||||
Compiling spin v0.3.5
|
||||
error[E0463]: can't find crate for `core`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
Build failed, waiting for other jobs to finish...
|
||||
...
|
||||
Makefile:52: recipe for target 'cargo' failed
|
||||
make: *** [cargo] Error 101
|
||||
```
|
||||
It doesn't compile anymore. The error tells us that the Rust compiler no longer finds the core library.
|
||||
|
||||
The [core library] is implicitly linked to all `no_std` crates and contains things such as `Result`, `Option`, and iterators. We've used that library without problems since [the very beginning], so why is it no longer available?
|
||||
|
||||
[core library]: https://doc.rust-lang.org/nightly/core/index.html
|
||||
[the very beginning]: @/edition-1/posts/03-set-up-rust/index.md
|
||||
|
||||
The problem is that the core library is distributed together with the Rust compiler as a _precompiled_ library. So it is only valid for the host triple, which is `x86_64-unknown-linux-gnu` in our case. If we want to compile code for other targets, we need to recompile `core` for these targets first.
|
||||
|
||||
#### Xargo
|
||||
That's where [xargo] comes in. It is a wrapper for cargo that eases cross compilation. We can install it by executing:
|
||||
|
||||
[xargo]: https://github.com/japaric/xargo
|
||||
|
||||
```
|
||||
cargo install xargo
|
||||
```
|
||||
|
||||
Xargo depends on the rust source code, which we can install with `rustup component add rust-src`.
|
||||
|
||||
Xargo is “a drop-in replacement for cargo”, so every cargo command also works with `xargo`. You can do e.g. `xargo --help`, `xargo clean`, or `xargo doc`. However, the `build` command gains additional functionality: `xargo build` will automatically cross compile the `core` library when compiling for custom targets.
|
||||
|
||||
That's exactly what we want, so we change one letter in our Makefile:
|
||||
|
||||
```diff
|
||||
# in `Makefile`
|
||||
...
|
||||
|
||||
cargo:
|
||||
- @cargo build --target $(target)
|
||||
+ @xargo build --target $(target)
|
||||
...
|
||||
```
|
||||
|
||||
Now the build goes through `xargo`, which should fix the compilation error. Let's try it out:
|
||||
|
||||
```
|
||||
> make run
|
||||
Compiling core v0.0.0 (file:///home/…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore)
|
||||
LLVM ERROR: SSE register return with SSE disabled
|
||||
error: Could not compile `core`.
|
||||
```
|
||||
Well, we get a different error now, so it seems like we're making progress :). It seems like there is a “SSE register return” although SSE is disabled. But what's an “SSE register return”?
|
||||
|
||||
### SSE Register Return
|
||||
Remember when we discussed calling conventions above? The calling convention defines which registers are used for return values. Well, the [System V ABI] defines that `xmm0` should be used for returning floating point values. So somewhere in the `core` library a function returns a float and LLVM doesn't know what to do. The ABI says “use `xmm0`” but the target specification says “don't use `xmm` registers”.
|
||||
|
||||
In order to fix this problem, we need to change our float ABI. The idea is to avoid normal hardware-supported floats and use a pure software implementation instead. We can do so by enabling the `soft-float` feature for our target. For that, we edit `x86_64-blog_os.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||
...
|
||||
"features": "-mmx,-sse,+soft-float"
|
||||
}
|
||||
```
|
||||
|
||||
The plus prefix tells LLVM to enable the `soft-float` feature.
|
||||
|
||||
Let's try `make run` again:
|
||||
|
||||
```
|
||||
> make run
|
||||
Compiling core v0.0.0 (file:///…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore)
|
||||
Finished release [optimized] target(s) in 21.95 secs
|
||||
Compiling spin v0.4.5
|
||||
Compiling once v0.3.2
|
||||
Compiling x86 v0.8.0
|
||||
Compiling bitflags v0.9.1
|
||||
Compiling raw-cpuid v2.0.1
|
||||
Compiling rlibc v0.1.5
|
||||
Compiling linked_list_allocator v0.2.3
|
||||
Compiling volatile v0.1.0
|
||||
Compiling bitflags v0.4.0
|
||||
Compiling bit_field v0.5.0
|
||||
Compiling spin v0.3.5
|
||||
Compiling multiboot2 v0.1.0
|
||||
Compiling lazy_static v0.2.2
|
||||
Compiling hole_list_allocator v0.1.0 (file:///…/libs/hole_list_allocator)
|
||||
Compiling blog_os v0.1.0 (file:///…)
|
||||
error[E0463]: can't find crate for `alloc`
|
||||
--> src/lib.rs:33:1
|
||||
|
|
||||
33 | extern crate alloc;
|
||||
| ^^^^^^^^^^^^^^^^^^^ can't find crate
|
||||
|
||||
error: aborting due to previous error
|
||||
```
|
||||
We see that `xargo` now compiles the `core` crate in release mode. Then it starts the normal cargo build. Cargo then recompiles all dependencies, since it needs to generate different code for the new target.
|
||||
|
||||
However, the build still fails. The reason is that xargo only installs `core` by default, but we also need the `alloc` crate. We can enable it by creating a file named `Xargo.toml` with the following contents:
|
||||
|
||||
```toml
|
||||
# Xargo.toml
|
||||
|
||||
[target.x86_64-blog_os.dependencies]
|
||||
alloc = {}
|
||||
```
|
||||
|
||||
Now xargo compiles `alloc`, too:
|
||||
|
||||
```
|
||||
> make run
|
||||
Compiling core v0.0.0 (file:///…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore)
|
||||
Compiling std_unicode v0.0.0 (file:///…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd_unicode)
|
||||
Compiling alloc v0.0.0 (file:///…/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/liballoc)
|
||||
Finished release [optimized] target(s) in 28.84 secs
|
||||
Compiling blog_os v0.1.0 (file:///…/Documents/blog_os/master)
|
||||
warning: unused variable: `allocator` […]
|
||||
warning: unused variable: `frame` […]
|
||||
|
||||
Finished debug [unoptimized + debuginfo] target(s) in 1.75 secs
|
||||
```
|
||||
|
||||
It worked! Now we have a kernel that never touches the multimedia registers! We can verify this by executing:
|
||||
|
||||
```
|
||||
> objdump -d build/kernel-x86_64.bin | grep "mm[0-9]"
|
||||
```
|
||||
If the command produces no output, our kernel uses neither MMX (`mm0` – `mm7`) nor SSE (`xmm0` – `xmm15`) registers.
|
||||
|
||||
So now our return-from-exception logic works without problems in _most_ cases. However, there is still a pitfall hidden in the C calling convention, which might cause hideous bugs in some rare cases.
|
||||
|
||||
## The Red Zone
|
||||
The [red zone] is an optimization of the [System V ABI] that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:
|
||||
|
||||
[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||
|
||||

|
||||
|
||||
The image shows the stack frame of a function with `n` local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.
|
||||
|
||||
The red zone is defined as the 128 bytes below the adjusted stack pointer. The function can use this area for temporary data that's not needed across function calls. Thus, the two instructions for adjusting the stack pointer can be avoided in some cases (e.g. in small leaf functions).
|
||||
|
||||
However, this optimization leads to huge problems with exceptions. Let's assume that an exception occurs while a function uses the red zone:
|
||||
|
||||

|
||||
|
||||
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. It might fail or cause another exception, but it could also lead to strange bugs that [take weeks to debug].
|
||||
|
||||
[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
|
||||
|
||||
### Adjusting our Exception Handler?
|
||||
The problem is that the [System V ABI] demands that the red zone _“shall not be modified by signal or interrupt handlers.”_ Our current exception handlers do not respect this. We could try to fix it by subtracting 128 from the stack pointer before pushing anything:
|
||||
|
||||
```nasm
|
||||
sub rsp, 128
|
||||
save_scratch_registers()
|
||||
...
|
||||
call ...
|
||||
...
|
||||
restore_scratch_registers()
|
||||
add rsp, 128
|
||||
|
||||
iretq
|
||||
```
|
||||
_This will not work._ The problem is that the CPU pushes the exception stack frame before even calling our handler function. So the CPU itself will clobber the red zone and there is nothing we can do about that. So our only chance is to disable the red zone.
|
||||
|
||||
### Disabling the Red Zone
|
||||
The red zone is a property of our target, so in order to disable it we edit our `x86_64-blog_os.json` a last time:
|
||||
|
||||
```json
|
||||
{
|
||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||
...
|
||||
"features": "-mmx,-sse,+soft-float",
|
||||
"disable-redzone": true
|
||||
}
|
||||
```
|
||||
|
||||
We add one additional option at the end: `"disable-redzone": true`. As you might guess, this option disables the red zone optimization.
|
||||
|
||||
Now we have a red zone free kernel!
|
||||
|
||||
## Exceptions with Error Codes
|
||||
We're now able to correctly return from exceptions without error codes. However, we still can't return from exceptions that push an error code (e.g. page faults). Let's fix that by updating our `handler_with_error_code` macro:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
macro_rules! handler_with_error_code {
|
||||
($name: ident) => {{
|
||||
#[naked]
|
||||
extern "C" fn wrapper() -> ! {
|
||||
unsafe {
|
||||
asm!("pop rsi // pop error code into rsi
|
||||
mov rdi, rsp
|
||||
sub rsp, 8 // align the stack pointer
|
||||
call $0"
|
||||
:: "i"($name as extern "C" fn(
|
||||
&ExceptionStackFrame, u64))
|
||||
: "rdi","rsi" : "intel");
|
||||
asm!("iretq" :::: "intel", "volatile");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
wrapper
|
||||
}}
|
||||
}
|
||||
```
|
||||
|
||||
First, we change the type of the handler function: no more `-> !`, so it no longer needs to diverge. We also add an `iretq` instruction at the end.
|
||||
|
||||
Now we can make our `page_fault_handler` non-diverging:
|
||||
|
||||
```diff
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame,
|
||||
- error_code: u64) -> ! { ... }
|
||||
+ error_code: u64) { ... }
|
||||
```
|
||||
|
||||
However, now we have the same problem as above: The handler function will overwrite the scratch registers and cause bugs when returning. Let's fix this by invoking `save_scratch_registers` at the beginning:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
macro_rules! handler_with_error_code {
|
||||
($name: ident) => {{
|
||||
#[naked]
|
||||
extern "C" fn wrapper() -> ! {
|
||||
unsafe {
|
||||
save_scratch_registers!();
|
||||
asm!("pop rsi // pop error code into rsi
|
||||
mov rdi, rsp
|
||||
add rdi, 10*8 // calculate exception stack frame pointer
|
||||
sub rsp, 8 // align the stack pointer
|
||||
call $0
|
||||
add rsp, 8 // undo stack pointer alignment
|
||||
" :: "i"($name as extern "C" fn(
|
||||
&ExceptionStackFrame, u64))
|
||||
: "rdi","rsi" : "intel");
|
||||
restore_scratch_registers!();
|
||||
asm!("iretq" :::: "intel", "volatile");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
wrapper
|
||||
}}
|
||||
}
|
||||
```
|
||||
|
||||
Now we backup the scratch registers to the stack right at the beginning and restore them just before the `iretq`. Like in the `handler` macro, we now need to add `10*8` to `rdi` in order to get the correct exception stack frame pointer (`save_scratch_registers` pushes nine 8 byte registers, plus the error code). We also need to undo the stack pointer alignment after the `call` [^fn-stack-alignment].
|
||||
|
||||
[^fn-stack-alignment]: The stack alignment is actually wrong here, since we additionally pushed an uneven number of registers. However, the `pop rsi` is wrong too, since the error code is no longer at the top of the stack. When we fix that problem, the stack alignment becomes correct again. So I left it in to keep things simple.
|
||||
|
||||
Now we have one last bug: We `pop` the error code into `rsi`, but the error code is no longer at the top of the stack (since `save_scratch_registers` pushed 9 registers on top of it). So we need to do it differently:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
macro_rules! handler_with_error_code {
|
||||
($name: ident) => {{
|
||||
#[naked]
|
||||
extern "C" fn wrapper() -> ! {
|
||||
unsafe {
|
||||
save_scratch_registers!();
|
||||
asm!("mov rsi, [rsp + 9*8] // load error code into rsi
|
||||
mov rdi, rsp
|
||||
add rdi, 10*8 // calculate exception stack frame pointer
|
||||
sub rsp, 8 // align the stack pointer
|
||||
call $0
|
||||
add rsp, 8 // undo stack pointer alignment
|
||||
" :: "i"($name as extern "C" fn(
|
||||
&ExceptionStackFrame, u64))
|
||||
: "rdi","rsi" : "intel");
|
||||
restore_scratch_registers!();
|
||||
asm!("add rsp, 8 // pop error code
|
||||
iretq" :::: "intel", "volatile");
|
||||
::core::intrinsics::unreachable();
|
||||
}
|
||||
}
|
||||
wrapper
|
||||
}}
|
||||
}
|
||||
```
|
||||
|
||||
Instead of using `pop`, we're calculating the error code address manually (`save_scratch_registers` pushes nine 8 byte registers) and load it into `rsi` using a `mov`. So now the error code stays on the stack. But `iretq` doesn't handle the error code, so we need to pop it before invoking `iretq`.
|
||||
|
||||
Phew! That was a lot of fiddling with assembly. Let's test if it still works.
|
||||
|
||||
### Testing
|
||||
First, we test if the exception stack frame pointer and the error code are still correct:
|
||||
|
||||
```rust
|
||||
// in rust_main in src/lib.rs
|
||||
|
||||
...
|
||||
unsafe { int!(3) };
|
||||
|
||||
// provoke a page fault
|
||||
unsafe { *(0xdeadbeaf as *mut u64) = 42; }
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
```
|
||||
|
||||
This should cause the following error message:
|
||||
|
||||
```
|
||||
EXCEPTION: PAGE FAULT while accessing 0xdeadbeaf
|
||||
error code: CAUSED_BY_WRITE
|
||||
ExceptionStackFrame {
|
||||
instruction_pointer: 1114753,
|
||||
code_segment: 8,
|
||||
cpu_flags: 2097158,
|
||||
stack_pointer: 1171104,
|
||||
stack_segment: 16
|
||||
}
|
||||
```
|
||||
The error code should still be `CAUSED_BY_WRITE` and the exception stack frame values should also be correct (e.g. `code_segment` should be 8 and `stack_segment` should be 16).
|
||||
|
||||
#### Returning from Page Faults
|
||||
Let's see what happens if we comment out the trailing `loop` in our page fault handler:
|
||||
|
||||

|
||||
|
||||
We see that the same error message is printed over and over again. Here is what happens:
|
||||
|
||||
- The CPU executes `rust_main` and tries to access `0xdeadbeaf`. This causes a page fault.
|
||||
- The page fault handler prints an error message and returns without fixing the cause of the exception (`0xdeadbeaf` is still unaccessible).
|
||||
- The CPU restarts the instruction that caused the page fault and thus tries to access `0xdeadbeaf` again. Of course, this causes a page fault again.
|
||||
- The page fault handler prints the error message and returns.
|
||||
|
||||
… and so on. Thus, our code indefinitely jumps between the page fault handler and the instruction that accesses `0xdeadbeaf`.
|
||||
|
||||
This is a good thing! It means that our `iretq` logic is working correctly, since it returns to the correct instruction every time. So our `handler_with_error_code` macro seems to be correct.
|
||||
|
||||
## What's next?
|
||||
We are now able to catch exceptions and to return from them. However, there are still exceptions that completely crash our kernel by causing a [triple fault]. In the next post, we will fix this issue by handling a special type of exception: the [double fault]. Thus, we will be able to avoid random reboots in our kernel.
|
||||
|
||||
[triple fault]: https://en.wikipedia.org/wiki/Triple_fault
|
||||
[double fault]: https://en.wikipedia.org/wiki/Double_fault
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
|
||||
<svg width="17cm" height="11cm" viewBox="-60 -21 340 212" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
|
||||
<tspan x="90" y="65.9389"></tspan>
|
||||
</text>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="70" x2="176.236" y2="70"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,65 175.118,70 185.118,75 "/>
|
||||
</g>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="72.75">
|
||||
<tspan x="208" y="72.75">Old Stack Pointer</tspan>
|
||||
</text>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
|
||||
<tspan x="75" y="230"></tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="190" x2="-4" y2="190"/>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="187.764"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,178.882 -12,188.882 -7,178.882 "/>
|
||||
</g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="170" y1="-20" x2="170" y2="0"/>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="0" y="0" width="170" height="20"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="170" height="20"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="13.125">
|
||||
<tspan x="85" y="13.125">Return Address</tspan>
|
||||
</text>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
|
||||
<tspan x="84" y="4"></tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #dddddd" x="0" y="20" width="170" height="20"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="170" height="20"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="33.125">
|
||||
<tspan x="85" y="33.125">Local Variable 1</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #cccccc" x="0" y="40" width="170" height="32"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="170" height="32"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="59.125">
|
||||
<tspan x="85" y="59.125">Local Variables 2..n</tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="170" y2="40"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
|
||||
<g>
|
||||
<rect style="fill: #ff3333" x="0" y="70" width="170" height="120"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="170" height="120"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="40" y="179.125">
|
||||
<tspan x="40" y="179.125">Red Zone</tspan>
|
||||
</text>
|
||||
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="132.75">
|
||||
<tspan x="-20" y="132.75">128 bytes</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #ffc200" x="30" y="70" width="140" height="30"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="70" width="140" height="30"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="88.125">
|
||||
<tspan x="100" y="88.125">Exception Stack Frame</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #ffe000" x="30" y="100" width="140" height="30"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="100" width="140" height="30"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="118.125">
|
||||
<tspan x="100" y="118.125">Register Backup</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #c6db97" x="30" y="130" width="140" height="30"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="130" width="140" height="30"/>
|
||||
</g>
|
||||
<text font-size="8.46654" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="147.925">
|
||||
<tspan x="100" y="147.925">Handler Function Stack Frame</tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="70" x2="30" y2="160"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="160" x2="170" y2="160"/>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="160" x2="176.236" y2="160"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,155 175.118,160 185.118,165 "/>
|
||||
</g>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="162.635">
|
||||
<tspan x="208" y="162.635">New Stack Pointer</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
|
||||
<svg width="14cm" height="9cm" viewBox="-60 -21 270 172" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
|
||||
<tspan x="90" y="65.9389"></tspan>
|
||||
</text>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="154" y1="70" x2="126.236" y2="70"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="135.118,65 125.118,70 135.118,75 "/>
|
||||
</g>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="158" y="72.75">
|
||||
<tspan x="158" y="72.75">Stack Pointer</tspan>
|
||||
</text>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
|
||||
<tspan x="75" y="230"></tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="150" x2="-4" y2="150"/>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="147.764"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,138.882 -12,148.882 -7,138.882 "/>
|
||||
</g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="120" y1="-20" x2="120" y2="0"/>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="0" y="0" width="120" height="20"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="120" height="20"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="13.125">
|
||||
<tspan x="60" y="13.125">Return Address</tspan>
|
||||
</text>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
|
||||
<tspan x="84" y="4"></tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #dddddd" x="0" y="20" width="120" height="20"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="120" height="20"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="33.125">
|
||||
<tspan x="60" y="33.125">Local Variable 1</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #cccccc" x="0" y="40" width="120" height="32"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="120" height="32"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="59.125">
|
||||
<tspan x="60" y="59.125">Local Variables 2..n</tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="120" y2="40"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
|
||||
<g>
|
||||
<rect style="fill: #ff3333" x="0" y="70" width="120" height="80"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="120" height="80"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="113.125">
|
||||
<tspan x="60" y="113.125">Red Zone</tspan>
|
||||
</text>
|
||||
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="112.75">
|
||||
<tspan x="-20" y="112.75">128 bytes</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
|
||||
<svg width="13cm" height="4cm" viewBox="-1 -23 252 63" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="0" y="0" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="50" height="10"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="8.125">
|
||||
<tspan x="25" y="8.125">mm0</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="0" y="10" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="10" width="50" height="10"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="0" y="20" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="50" height="10"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect style="fill: #dddddd" x="0" y="30" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="30" width="50" height="10"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="17.9931">
|
||||
<tspan x="25" y="17.9931">mm1</tspan>
|
||||
</text>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="28.125">
|
||||
<tspan x="25" y="28.125">mm2</tspan>
|
||||
</text>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="38.125">
|
||||
<tspan x="25" y="38.125">mm3</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="100" y="-4.44089e-15" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="100" y="-4.44089e-15" width="50" height="10"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="8.125">
|
||||
<tspan x="125" y="8.125">mm0</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #ff0000" x="100" y="10" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="100" y="10" width="50" height="10"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="100" y="20" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="100" y="20" width="50" height="10"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect style="fill: #dddddd" x="100" y="30" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="100" y="30" width="50" height="10"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="18.125">
|
||||
<tspan x="125" y="18.125">mm1</tspan>
|
||||
</text>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="28.125">
|
||||
<tspan x="125" y="28.125">mm2</tspan>
|
||||
</text>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="38.125">
|
||||
<tspan x="125" y="38.125">mm3</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="200" y="0" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="200" y="0" width="50" height="10"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="8.125">
|
||||
<tspan x="225" y="8.125">mm0</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #ff0000" x="200" y="10" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="200" y="10" width="50" height="10"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="200" y="20" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="200" y="20" width="50" height="10"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect style="fill: #dddddd" x="200" y="30" width="50" height="10"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="200" y="30" width="50" height="10"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="18.125">
|
||||
<tspan x="225" y="18.125">mm1</tspan>
|
||||
</text>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="28.125">
|
||||
<tspan x="225" y="28.125">mm2</tspan>
|
||||
</text>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="38.125">
|
||||
<tspan x="225" y="38.125">mm3</tspan>
|
||||
</text>
|
||||
<text font-size="7.9021" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="25" y="-10.0787">
|
||||
<tspan x="25" y="-10.0787">Program</tspan>
|
||||
</text>
|
||||
<text font-size="7.9021" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="125" y="-15.0175">
|
||||
<tspan x="125" y="-15.0175">Exception</tspan>
|
||||
<tspan x="125" y="-5.13977">Handler</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="50" y1="-12.8287" x2="97.7639" y2="-12.8287"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="94.882,-10.8287 98.882,-12.8287 94.882,-14.8287 "/>
|
||||
</g>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="150" y1="-12.8287" x2="197.764" y2="-12.8287"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="194.882,-10.8287 198.882,-12.8287 194.882,-14.8287 "/>
|
||||
</g>
|
||||
<text font-size="7.9021" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="225" y="-10.0787">
|
||||
<tspan x="225" y="-10.0787">Program</tspan>
|
||||
</text>
|
||||
<text font-size="6.77323" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="-14.737">
|
||||
<tspan x="75" y="-14.737">Exception</tspan>
|
||||
<tspan x="75" y="-6.27032">occurs</tspan>
|
||||
</text>
|
||||
<text font-size="6.77323" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="175" y="-14.737">
|
||||
<tspan x="175" y="-14.737">Exception</tspan>
|
||||
<tspan x="175" y="-6.27032">resolved</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
7
blog/content/edition-1/extra/naked-exceptions/_index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
+++
|
||||
title = "Handling Exceptions using naked Functions"
|
||||
sort_by = "weight"
|
||||
template = "edition-1/handling-exceptions-with-naked-fns.html"
|
||||
insert_anchor_links = "left"
|
||||
aliases = ["first-edition/extra/naked-exceptions/index.html"]
|
||||
+++
|
||||
BIN
blog/content/edition-1/extra/set-up-gdb/gdb-tui-screenshot.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
@@ -1,5 +1,9 @@
|
||||
+++
|
||||
title = "Set Up GDB"
|
||||
template = "plain.html"
|
||||
path = "set-up-gdb"
|
||||
aliases = ["set-up-gdb.html"]
|
||||
weight = 4
|
||||
+++
|
||||
|
||||
There are a lot of things that can go wrong when developing an OS. So it's a good idea to add a debugger to our toolset, which allows us to set breakpoints and examine variables. We will use [GDB](https://www.gnu.org/software/gdb/) as QEMU supports it out of the box.
|
||||
@@ -29,7 +33,7 @@ Remote 'g' packet reply is too long: [a very long number]
|
||||
```
|
||||
This issue is known [since 2012][gdb issue patch] but it is still not fixed. Maybe we find the reason in the [issue thread][gdb issue thread]:
|
||||
|
||||
[gdb issue patch]: http://www.cygwin.com/ml/gdb-patches/2012-03/msg00116.html
|
||||
[gdb issue patch]: https://web.archive.org/web/20190114181420/https://www.cygwin.com/ml/gdb-patches/2012-03/msg00116.html
|
||||
[gdb issue thread]: https://sourceware.org/bugzilla/show_bug.cgi?id=13984#c11
|
||||
|
||||
> from my (limited) experience, unless you ping the gdb-patches list weekly, this patch is more likely to remain forgotten :-)
|
||||
@@ -66,9 +70,9 @@ After connecting to QEMU, you can use various gdb commands to control execution
|
||||
- `print` or `p`: Prints the value of a variable. You can use Cs `*` and `&` operators. To print in hexadecimal, use `p/x`.
|
||||
- `tui enable`: Enables the text user interface, which provides a graphical interface (see below). To disable it again, run `tui disable`.
|
||||
|
||||

|
||||

|
||||
|
||||
Of course there are many more commands. Feel free to send a PR if you think this list is missing something important. For a more complete GDB overview, check out [Beej's Quick Guide][bggdb] or the [website for Harvard's CS161 course][CS161].
|
||||
|
||||
[bggdb]: http://beej.us/guide/bggdb/
|
||||
[CS161]: http://www.eecs.harvard.edu/~cs161/resources/gdb.html
|
||||
[bggdb]: https://beej.us/guide/bggdb/
|
||||
[CS161]: https://www.eecs.harvard.edu/~cs161/resources/gdb.html
|
||||
14
blog/content/edition-1/extra/talks.md
Normal file
@@ -0,0 +1,14 @@
|
||||
+++
|
||||
title = "Talks"
|
||||
path = "talks"
|
||||
template = "plain.html"
|
||||
weight = 1
|
||||
+++
|
||||
|
||||
## 2018
|
||||
|
||||
- “The Rust Way Of OS Development” at HTWG Konstanz, May 30, 2018: [slides](https://phil-opp.github.io/talk-konstanz-may-2018/) [pdf](https://phil-opp.github.io/talk-konstanz-may-2018/talk.pdf)
|
||||
|
||||
## 2017
|
||||
|
||||
- “Open Source OS Development in Rust” at HTWG Konstanz, May 22, 2017: [slides](https://phil-opp.github.io/talk-konstanz-may-2017/)
|
||||
@@ -1,23 +1,22 @@
|
||||
+++
|
||||
title = "A minimal x86 kernel"
|
||||
slug = "multiboot-kernel"
|
||||
date = "2015-08-18"
|
||||
aliases = [
|
||||
"/2015/08/18/multiboot-kernel/",
|
||||
"/rust-os/multiboot-kernel.html",
|
||||
]
|
||||
title = "A minimal Multiboot Kernel"
|
||||
weight = 1
|
||||
path = "multiboot-kernel"
|
||||
aliases = ["multiboot-kernel.html", "/2015/08/18/multiboot-kernel/", "/rust-os/multiboot-kernel.html"]
|
||||
date = 2015-08-18
|
||||
template = "edition-1/page.html"
|
||||
+++
|
||||
|
||||
This post explains how to create a minimal x86 operating system kernel. In fact, it will just boot and print `OK` to the screen. The following blog posts we will extend it using the [Rust] programming language.
|
||||
This post explains how to create a minimal x86 operating system kernel using the Multiboot standard. In fact, it will just boot and print `OK` to the screen. In subsequent blog posts we will extend it using the [Rust] programming language.
|
||||
|
||||
[Rust]: http://www.rust-lang.org/
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
|
||||
<!--more-->
|
||||
<!-- more -->
|
||||
|
||||
I tried to explain everything in detail and to keep the code as simple as possible. If you have any questions, suggestions or other issues, please leave a comment or [create an issue] on Github. The source code is available in a [repository][source code], too.
|
||||
|
||||
[create an issue]: https://github.com/phil-opp/blog_os/issues
|
||||
[source code]: https://github.com/phil-opp/blog_os/tree/multiboot_bootstrap/src/arch/x86_64
|
||||
[source code]: https://github.com/phil-opp/blog_os/tree/first_edition_post_1/src/arch/x86_64
|
||||
|
||||
Note that this tutorial is written mainly for Linux. For some known problems on OS X see the comment section and [this issue][mac os issue]. If you want to use a virtual Linux machine, you can find instructions and a Vagrantfile in Ashley Willams's [x86-kernel repository].
|
||||
|
||||
@@ -29,19 +28,19 @@ When you turn on a computer, it loads the [BIOS] from some special flash memory.
|
||||
|
||||
[BIOS]: https://en.wikipedia.org/wiki/BIOS
|
||||
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
||||
[real mode]: http://wiki.osdev.org/Real_Mode
|
||||
[real mode]: https://wiki.osdev.org/Real_Mode
|
||||
|
||||
We won't write a bootloader because that would be a complex project on its own (if you really want to do it, check out [_Rolling Your Own Bootloader_]). Instead we will use one of the [many well-tested bootloaders][bootloader comparison] out there. But which one?
|
||||
We won't write a bootloader because that would be a complex project on its own (if you really want to do it, check out [_Rolling Your Own Bootloader_]). Instead we will use one of the [many well-tested bootloaders][bootloader comparison] out there to boot our kernel from a CD-ROM. But which one?
|
||||
|
||||
[_Rolling Your Own Bootloader_]: http://wiki.osdev.org/Rolling_Your_Own_Bootloader
|
||||
[_Rolling Your Own Bootloader_]: https://wiki.osdev.org/Rolling_Your_Own_Bootloader
|
||||
[bootloader comparison]: https://en.wikipedia.org/wiki/Comparison_of_boot_loaders
|
||||
|
||||
## Multiboot
|
||||
Fortunately there is a bootloader standard: the [Multiboot Specification][multiboot]. Our kernel just needs to indicate that it supports Multiboot and every Multiboot-compliant bootloader can boot it. We will use the Multiboot 2 specification ([PDF][Multiboot 2]) together with the well-known [GRUB 2] bootloader.
|
||||
|
||||
[multiboot]: https://en.wikipedia.org/wiki/Multiboot_Specification
|
||||
[multiboot 2]: http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||
[grub 2]: http://wiki.osdev.org/GRUB_2
|
||||
[multiboot 2]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||
[grub 2]: https://wiki.osdev.org/GRUB_2
|
||||
|
||||
To indicate our Multiboot 2 support to the bootloader, our kernel must start with a _Multiboot Header_, which has the following format:
|
||||
|
||||
@@ -80,8 +79,6 @@ If you don't know x86 assembly, here is some quick guide:
|
||||
- `dd` stands for `define double` (32bit) and `dw` stands for `define word` (16bit). They just output the specified 32bit/16bit constant.
|
||||
- the additional `0x100000000` in the checksum calculation is a small hack[^fn-checksum_hack] to avoid a compiler warning
|
||||
|
||||
[^fn-checksum_hack]: The formula from the table, `-(magic + architecture + header_length)`, creates a negative value that doesn't fit into 32bit. By subtracting from `0x100000000` (= 2^(32)) instead, we keep the value positive without changing its truncated value. Without the additional sign bit(s) the result fits into 32bit and the compiler is happy :).
|
||||
|
||||
We can already _assemble_ this file (which I called `multiboot_header.asm`) using `nasm`. It produces a flat binary by default, so the resulting file just contains our 24 bytes (in little endian if you work on a x86 machine):
|
||||
|
||||
```
|
||||
@@ -110,7 +107,7 @@ There are some new commands:
|
||||
- `global` exports a label (makes it public). As `start` will be the entry point of our kernel, it needs to be public.
|
||||
- the `.text` section is the default section for executable code
|
||||
- `bits 32` specifies that the following lines are 32-bit instructions. It's needed because the CPU is still in [Protected mode] when GRUB starts our kernel. When we switch to [Long mode] in the [next post] we can use `bits 64` (64-bit instructions).
|
||||
- the `mov dword` instruction moves the 32bit constant `0x2f4f2f4b` to the memory at address `b8000` (it prints `OK` to the screen, an explanation follows in the next posts)
|
||||
- the `mov dword` instruction moves the 32bit constant `0x2f4b2f4f` to the memory at address `b8000` (it prints `OK` to the screen, an explanation follows in the next posts)
|
||||
- `hlt` is the halt instruction and causes the CPU to stop
|
||||
|
||||
Through assembling, viewing and disassembling we can see the CPU [Opcodes] in action:
|
||||
@@ -133,14 +130,14 @@ Through assembling, viewing and disassembling we can see the CPU [Opcodes] in ac
|
||||
To boot our executable later through GRUB, it should be an [ELF] executable. So we want `nasm` to create ELF [object files] instead of plain binaries. To do that, we simply pass the `‑f elf64` argument to it.
|
||||
|
||||
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||
[object files]: http://wiki.osdev.org/Object_Files
|
||||
[object files]: https://wiki.osdev.org/Object_Files
|
||||
|
||||
To create the ELF _executable_, we need to [link] the object files together. We use a custom [linker script] named `linker.ld`:
|
||||
|
||||
[link]: https://en.wikipedia.org/wiki/Linker_(computing)
|
||||
[linker script]: https://sourceware.org/binutils/docs/ld/Scripts.html
|
||||
|
||||
```
|
||||
```ld
|
||||
ENTRY(start)
|
||||
|
||||
SECTIONS {
|
||||
@@ -166,8 +163,6 @@ Let's translate it:
|
||||
- the `.text` output section contains all input sections named `.text`
|
||||
- Sections named `.multiboot_header` are added to the first output section (`.boot`) to ensure they are at the beginning of the executable. This is necessary because GRUB expects to find the Multiboot header very early in the file.
|
||||
|
||||
[^Linker 1M]: We don't want to load the kernel to e.g. `0x0` because there are many special memory areas below the 1MB mark (for example the so-called VGA buffer at `0xb8000`, that we use to print `OK` to the screen).
|
||||
|
||||
So let's create the ELF object files and link them using our new linker script:
|
||||
|
||||
```
|
||||
@@ -191,10 +186,11 @@ Idx Name Size VMA LMA File off Algn
|
||||
CONTENTS, ALLOC, LOAD, READONLY, CODE
|
||||
```
|
||||
_Note_: The `ld` and `objdump` commands are platform specific. If you're _not_ working on x86_64 architecture, you will need to [cross compile binutils]. Then use `x86_64‑elf‑ld` and `x86_64‑elf‑objdump` instead of `ld` and `objdump`.
|
||||
[cross compile binutils]: {{% relref "cross-compile-binutils.md" %}}
|
||||
|
||||
[cross compile binutils]: @/edition-1/extra/cross-compile-binutils.md
|
||||
|
||||
## Creating the ISO
|
||||
The last step is to create a bootable ISO image with GRUB. We need to create the following directory structure and copy the `kernel.bin` to the right place:
|
||||
All PC BIOSes know how to boot from a CD-ROM, so we want to create a bootable CD-ROM image, containing our kernel and the GRUB bootloader's files, in a single file called an [ISO](https://en.wikipedia.org/wiki/ISO_image). Make the following directory structure and copy the `kernel.bin` to the right place:
|
||||
|
||||
```
|
||||
isofiles
|
||||
@@ -235,13 +231,13 @@ Now it's time to boot our OS. We will use [QEMU]:
|
||||
```
|
||||
qemu-system-x86_64 -cdrom os.iso
|
||||
```
|
||||

|
||||

|
||||
|
||||
Notice the green `OK` in the upper left corner. If it does not work for you, take a look at the comment section.
|
||||
|
||||
Let's summarize what happens:
|
||||
|
||||
1. the BIOS loads the bootloader (GRUB) from the virtual hard drive (the ISO)
|
||||
1. the BIOS loads the bootloader (GRUB) from the virtual CD-ROM (the ISO)
|
||||
2. the bootloader reads the kernel executable and finds the Multiboot header
|
||||
3. it copies the `.boot` and `.text` sections to memory (to addresses `0x100000` and `0x100020`)
|
||||
4. it jumps to the entry point (`0x100020`, you can obtain it through `objdump -f`)
|
||||
@@ -251,9 +247,7 @@ You can test it on real hardware, too. Just burn the ISO to a disk or USB stick
|
||||
|
||||
## Build Automation
|
||||
|
||||
Right now we need to execute 4 commands in the right order everytime we change a file. That's bad. So let's automate the build using a [Makefile][Makefile tutorial]. But first we should create some clean directory structure for our source files to separate the architecture specific files:
|
||||
|
||||
[Makefile tutorial]: http://mrbook.org/blog/tutorials/make/
|
||||
Right now we need to execute 4 commands in the right order every time we change a file. That's bad. So let's automate the build using a `Makefile`. But first we should create some clean directory structure for our source files to separate the architecture specific files:
|
||||
|
||||
```
|
||||
…
|
||||
@@ -321,5 +315,10 @@ Now we can invoke `make` and all updated assembly files are compiled and linked.
|
||||
|
||||
In the [next post] we will create a page table and do some CPU configuration to switch to the 64-bit [long mode].
|
||||
|
||||
[next post]: {{% relref "2015-08-25-entering-longmode.md" %}}
|
||||
[next post]: @/edition-1/posts/02-entering-longmode/index.md
|
||||
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
||||
|
||||
## Footnotes
|
||||
[^fn-checksum_hack]: The formula from the table, `-(magic + architecture + header_length)`, creates a negative value that doesn't fit into 32bit. By subtracting from `0x100000000` (= 2^(32)) instead, we keep the value positive without changing its truncated value. Without the additional sign bit(s) the result fits into 32bit and the compiler is happy :).
|
||||
|
||||
[^Linker 1M]: We don't want to load the kernel to e.g. `0x0` because there are many special memory areas below the 1MB mark (for example the so-called VGA buffer at `0xb8000`, that we use to print `OK` to the screen).
|
||||
BIN
blog/content/edition-1/posts/01-multiboot-kernel/qemu-ok.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,929 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="7.2973523in"
|
||||
height="4.1279249in"
|
||||
viewBox="-2141 2141 8777.1352 4929.2808"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="X86_Paging_64bit.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1080"
|
||||
inkscape:window-height="1868"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="1.4404005"
|
||||
inkscape:cx="383.13253"
|
||||
inkscape:cy="73.778068"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(-2845,-286.64442)">
|
||||
<rect
|
||||
x="8503"
|
||||
y="4015"
|
||||
width="708"
|
||||
height="3307"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect8" />
|
||||
<polyline
|
||||
points="3365,2598 3365,3070"
|
||||
id="polyline10" />
|
||||
<polyline
|
||||
points="3189,2598 3189,3070"
|
||||
id="polyline12" />
|
||||
<polyline
|
||||
points="3012,2598 3012,3070"
|
||||
id="polyline14" />
|
||||
<polyline
|
||||
points="2834,2598 2834,3070"
|
||||
id="polyline16" />
|
||||
<polyline
|
||||
points="2657,2598 2657,3070"
|
||||
id="polyline18" />
|
||||
<polyline
|
||||
points="2480,2598 2480,3070"
|
||||
id="polyline20" />
|
||||
<polyline
|
||||
points="2303,2598 2303,3070"
|
||||
id="polyline22" />
|
||||
<polyline
|
||||
points="2125,2456 2125,3070"
|
||||
id="polyline24" />
|
||||
<polyline
|
||||
points="1948,2598 1948,3070"
|
||||
id="polyline26" />
|
||||
<polyline
|
||||
points="1772,2598 1772,3070"
|
||||
id="polyline28" />
|
||||
<polyline
|
||||
points="1594,2598 1594,3070"
|
||||
id="polyline30" />
|
||||
<polyline
|
||||
points="1417,2598 1417,3070"
|
||||
id="polyline32" />
|
||||
<polyline
|
||||
points="1239,2598 1239,3070"
|
||||
id="polyline34" />
|
||||
<polyline
|
||||
points="1063,2598 1063,3070"
|
||||
id="polyline36" />
|
||||
<polyline
|
||||
points="886,2598 886,3070"
|
||||
id="polyline38" />
|
||||
<polyline
|
||||
points="708,2456 708,3070"
|
||||
id="polyline40" />
|
||||
<polyline
|
||||
points="3543,2456 3543,3070"
|
||||
id="polyline74" />
|
||||
<polyline
|
||||
points="-2125,2598 3543,2598"
|
||||
id="polyline76"
|
||||
transform="matrix(0.49978943,0,0,1,1772.2461,0)" />
|
||||
<polyline
|
||||
points="3543,3070 -1948,3070 -2125,3070"
|
||||
id="polyline78"
|
||||
transform="matrix(0.49786923,0,0,1,1779.0493,0)" />
|
||||
<polyline
|
||||
points="9035,2598 9035,3070"
|
||||
id="polyline80" />
|
||||
<polyline
|
||||
points="8858,2598 8858,3070"
|
||||
id="polyline82" />
|
||||
<polyline
|
||||
points="8681,2598 8681,3070"
|
||||
id="polyline84" />
|
||||
<polyline
|
||||
points="8503,2598 8503,3070"
|
||||
id="polyline86" />
|
||||
<polyline
|
||||
points="8326,2598 8326,3070"
|
||||
id="polyline88" />
|
||||
<polyline
|
||||
points="8150,2598 8150,3070"
|
||||
id="polyline90" />
|
||||
<polyline
|
||||
points="7972,2598 7972,3070"
|
||||
id="polyline92" />
|
||||
<polyline
|
||||
points="7795,2456 7795,3070"
|
||||
id="polyline94" />
|
||||
<polyline
|
||||
points="7617,2598 7617,3070"
|
||||
id="polyline96" />
|
||||
<polyline
|
||||
points="7441,2598 7441,3070"
|
||||
id="polyline98" />
|
||||
<polyline
|
||||
points="7264,2598 7264,3070"
|
||||
id="polyline100" />
|
||||
<polyline
|
||||
points="7086,2598 7086,3070"
|
||||
id="polyline102" />
|
||||
<polyline
|
||||
points="6909,2598 6909,3070"
|
||||
id="polyline104" />
|
||||
<polyline
|
||||
points="6732,2598 6732,3070"
|
||||
id="polyline106" />
|
||||
<polyline
|
||||
points="6555,2598 6555,3070"
|
||||
id="polyline108" />
|
||||
<polyline
|
||||
points="6377,2456 6377,3070"
|
||||
id="polyline110" />
|
||||
<polyline
|
||||
points="6200,2598 6200,3070"
|
||||
id="polyline112" />
|
||||
<polyline
|
||||
points="6024,2598 6024,3070"
|
||||
id="polyline114" />
|
||||
<polyline
|
||||
points="5846,2598 5846,3070"
|
||||
id="polyline116" />
|
||||
<polyline
|
||||
points="5669,2598 5669,3070"
|
||||
id="polyline118" />
|
||||
<polyline
|
||||
points="5491,2598 5491,3070"
|
||||
id="polyline120" />
|
||||
<polyline
|
||||
points="5315,2598 5315,3070"
|
||||
id="polyline122" />
|
||||
<polyline
|
||||
points="5138,2598 5138,3070"
|
||||
id="polyline124" />
|
||||
<polyline
|
||||
points="4960,2409 4960,3070"
|
||||
id="polyline126" />
|
||||
<polyline
|
||||
points="4783,2598 4783,3070"
|
||||
id="polyline128" />
|
||||
<polyline
|
||||
points="4606,2598 4606,3070"
|
||||
id="polyline130" />
|
||||
<polyline
|
||||
points="4429,2598 4429,3070"
|
||||
id="polyline132" />
|
||||
<polyline
|
||||
points="4251,2598 4251,3070"
|
||||
id="polyline134" />
|
||||
<polyline
|
||||
points="4074,2598 4074,3070"
|
||||
id="polyline136" />
|
||||
<polyline
|
||||
points="3898,2598 3898,3070"
|
||||
id="polyline138" />
|
||||
<polyline
|
||||
points="3720,2598 3720,3070"
|
||||
id="polyline140" />
|
||||
<polyline
|
||||
points="9212,2456 9212,3070"
|
||||
id="polyline142" />
|
||||
<polyline
|
||||
points="3543,2598 9212,2598"
|
||||
id="polyline144" />
|
||||
<polyline
|
||||
points="9212,3070 3720,3070 0,3070"
|
||||
id="polyline146"
|
||||
transform="matrix(0.92335242,0,0,1,706.07747,0)" />
|
||||
<rect
|
||||
x="5102"
|
||||
y="4488"
|
||||
width="1181"
|
||||
height="2362"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect148" />
|
||||
<rect
|
||||
x="3307"
|
||||
y="4251"
|
||||
width="1181"
|
||||
height="2362"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect150" />
|
||||
<rect
|
||||
x="6897"
|
||||
y="4724"
|
||||
width="1181"
|
||||
height="2362"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect152" />
|
||||
<rect
|
||||
x="1511"
|
||||
y="4015"
|
||||
width="1181"
|
||||
height="2362"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect154" />
|
||||
<rect
|
||||
x="1417"
|
||||
y="6850"
|
||||
width="1417"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect156" />
|
||||
<rect
|
||||
x="8503"
|
||||
y="5433"
|
||||
width="708"
|
||||
height="236"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect158" />
|
||||
<rect
|
||||
x="5102"
|
||||
y="5669"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect160" />
|
||||
<rect
|
||||
x="3307"
|
||||
y="5433"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect162" />
|
||||
<rect
|
||||
x="6897"
|
||||
y="5905"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect164" />
|
||||
<rect
|
||||
x="1511"
|
||||
y="5196"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect166" />
|
||||
<circle
|
||||
cx="6141"
|
||||
cy="5905"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle170" />
|
||||
<circle
|
||||
cx="4346"
|
||||
cy="5669"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle172" />
|
||||
<circle
|
||||
cx="7937"
|
||||
cy="6141"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle174" />
|
||||
<circle
|
||||
cx="2551"
|
||||
cy="5433"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle176" />
|
||||
<polyline
|
||||
points="3118,3590 2834,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline178" />
|
||||
<polyline
|
||||
points="1322,3590 1039,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline180" />
|
||||
<polyline
|
||||
points="4913,3590 4629,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline184" />
|
||||
<polyline
|
||||
points="6519,3590 6236,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline186" />
|
||||
<polyline
|
||||
points="8314,3590 8031,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline188" />
|
||||
<circle
|
||||
cx="1653"
|
||||
cy="7086"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle190" />
|
||||
<polyline
|
||||
points="1653,7086 1181,7086 1181,6377 1354,6377"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline192" />
|
||||
<polygon
|
||||
points="1512,6377 1355,6330 1355,6425 1355,6425 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon194" />
|
||||
<polyline
|
||||
points="7937,6141 8173,6141 8173,7322 8346,7322"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline196" />
|
||||
<polygon
|
||||
points="8504,7322 8347,7275 8347,7370 8347,7370 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon198" />
|
||||
<polyline
|
||||
points="8173,3543 8173,5669 8346,5669"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline200" />
|
||||
<polygon
|
||||
points="8504,5669 8347,5622 8347,5716 8347,5716 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon202" />
|
||||
<polyline
|
||||
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline204" />
|
||||
<polyline
|
||||
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline206" />
|
||||
<polyline
|
||||
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline208" />
|
||||
<polyline
|
||||
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline210" />
|
||||
<polyline
|
||||
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline212" />
|
||||
<polyline
|
||||
points="4771,3543 4771,6141 4944,6141"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline214" />
|
||||
<polygon
|
||||
points="5103,6141 4945,6094 4945,6188 4945,6188 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon216" />
|
||||
<polyline
|
||||
points="6377,3543 6377,4015 6519,4157 6519,6377 6740,6377"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline218" />
|
||||
<polygon
|
||||
points="6898,6377 6741,6330 6741,6425 6741,6425 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon220" />
|
||||
<polyline
|
||||
points="6141,5905 6377,5905 6377,7086 6740,7086"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline222" />
|
||||
<polygon
|
||||
points="6898,7086 6741,7039 6741,7133 6741,7133 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon224" />
|
||||
<polyline
|
||||
points="4346,5669 4629,5669 4629,6850 4944,6850"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline226" />
|
||||
<polygon
|
||||
points="5103,6850 4945,6803 4945,6897 4945,6897 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon228" />
|
||||
<polyline
|
||||
points="2598,5433 2834,5433 2834,6614 3149,6614"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline230" />
|
||||
<polygon
|
||||
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon232" />
|
||||
<polyline
|
||||
points="1181,3543 1181,5669 1401,5669"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline234" />
|
||||
<polygon
|
||||
points="1560,5669 1402,5622 1402,5716 1402,5716 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon236" />
|
||||
<polyline
|
||||
points="2976,3543 2976,5905 3149,5905"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline238" />
|
||||
<polygon
|
||||
points="3308,5905 3150,5858 3150,5952 3150,5952 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon240" />
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2281.8145"
|
||||
y="7165"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text244"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">CR3 register</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3507"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text246"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2161"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text248"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text250"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="9176"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text262"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7759"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text264"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6342"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text266"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4924"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text268"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3579"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text270"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6413"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text272"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7830"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text274"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4996"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text276"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,4724)"
|
||||
id="g278">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text280"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,6614)"
|
||||
id="g282">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text284"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,9448,5622)"
|
||||
id="g286">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text288"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3509">4K memory page</tspan></text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5622"
|
||||
y="5958.457"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text292"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3497">P2 entry</tspan></text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,5723,6614)"
|
||||
id="g296">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text298"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,5723,5196)"
|
||||
id="g300">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text302"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5692"
|
||||
y="4393"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text304"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3505">P2 table</tspan></text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,3928,6377)"
|
||||
id="g306">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,3928,4960)"
|
||||
id="g310">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text312"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3826"
|
||||
y="5722.457"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3495">P3 entry</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="4157"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3503">P3 table</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7417"
|
||||
y="6194.457"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text322"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3499">P1 entry </tspan></text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,7519,6850)"
|
||||
id="g326">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text328"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,7519,5433)"
|
||||
id="g330">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text332"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7487"
|
||||
y="4629"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text334"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3507">P1 table</tspan></text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,2133,6141)"
|
||||
id="g336">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text338"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,2133,4724)"
|
||||
id="g340">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text342"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
x="2004.2715"
|
||||
y="5486.457"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text344"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
|
||||
<tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3493">P4 entry</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="2078"
|
||||
y="3941.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text348"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
|
||||
<tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3501">P4 table</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="3165"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text350"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text352"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="4960"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text356"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="6566"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text358"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="8362"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text360"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 27 KiB |
@@ -1,30 +1,27 @@
|
||||
+++
|
||||
title = "Entering Long Mode"
|
||||
slug = "entering-longmode"
|
||||
date = "2015-08-25"
|
||||
weight = 2
|
||||
path = "entering-longmode"
|
||||
aliases = ["entering-longmode.html", "/2015/08/25/entering-longmode/", "/rust-os/entering-longmode.html"]
|
||||
date = 2015-08-25
|
||||
template = "edition-1/page.html"
|
||||
[extra]
|
||||
updated = "2015-10-29"
|
||||
aliases = [
|
||||
"/2015/08/25/entering-longmode/",
|
||||
"/rust-os/entering-longmode.html",
|
||||
]
|
||||
+++
|
||||
|
||||
In the [previous post] we created a minimal multiboot kernel. It just prints `OK` and hangs. The goal is to extend it and call 64-bit [Rust] code. But the CPU is currently in [protected mode] and allows only 32-bit instructions and up to 4GiB memory. So we need to set up _Paging_ and switch to the 64-bit [long mode] first.
|
||||
|
||||
[previous post]: {{% relref "2015-08-18-multiboot-kernel.md" %}}
|
||||
[Rust]: http://www.rust-lang.org/
|
||||
[previous post]: @/edition-1/posts/01-multiboot-kernel/index.md
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
||||
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
||||
|
||||
<!--more-->
|
||||
<!-- more -->
|
||||
|
||||
I tried to explain everything in detail and to keep the code as simple as possible. If you have any questions, suggestions, or issues, please leave a comment or [create an issue] on Github. The source code is available in a [repository][source code], too.
|
||||
|
||||
[create an issue]: https://github.com/phil-opp/blog_os/issues
|
||||
[source code]: https://github.com/phil-opp/blog_os/tree/entering_longmode/src/arch/x86_64
|
||||
|
||||
_Notable Changes_: We don't use 1GiB pages anymore, since they have [compatibility problems][1GiB page problems]. The identity mapping is now done through 2MiB pages.
|
||||
[1GiB page problems]: https://github.com/phil-opp/blog_os/issues/17
|
||||
[source code]: https://github.com/phil-opp/blog_os/tree/first_edition_post_2/src/arch/x86_64
|
||||
|
||||
## Some Tests
|
||||
To avoid bugs and strange errors on old CPUs we should check if the processor supports every needed feature. If not, the kernel should abort and display an error message. To handle errors easily, we create an error procedure in `boot.asm`. It prints a rudimentary `ERR: X` message, where X is an error code letter, and hangs:
|
||||
@@ -42,7 +39,7 @@ error:
|
||||
At address `0xb8000` begins the so-called [VGA text buffer]. It's an array of screen characters that are displayed by the graphics card. A [future post] will cover the VGA buffer in detail and create a Rust interface to it. But for now, manual bit-fiddling is the easiest option.
|
||||
|
||||
[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
|
||||
[future post]: {{% relref "2015-10-23-printing-to-screen.md" %}}
|
||||
[future post]: @/edition-1/posts/04-printing-to-screen/index.md
|
||||
|
||||
A screen character consists of a 8 bit color code and a 8 bit [ASCII] character. We used the color code `4f` for all characters, which means white text on red background. `0x52` is an ASCII `R`, `0x45` is an `E`, `0x3a` is a `:`, and `0x20` is a space. The second space is overwritten by the given ASCII byte. Finally the CPU is stopped with the `hlt` instruction.
|
||||
|
||||
@@ -50,7 +47,7 @@ A screen character consists of a 8 bit color code and a 8 bit [ASCII] character.
|
||||
|
||||
Now we can add some check _functions_. A function is just a normal label with an `ret` (return) instruction at the end. The `call` instruction can be used to call it. Unlike the `jmp` instruction that just jumps to a memory address, the `call` instruction will push a return address to the stack (and the `ret` will jump to this address). But we don't have a stack yet. The [stack pointer] in the esp register could point to some important data or even invalid memory. So we need to update it and point it to some valid stack memory.
|
||||
|
||||
[stack pointer]: http://stackoverflow.com/a/1464052/866447
|
||||
[stack pointer]: https://stackoverflow.com/a/1464052/866447
|
||||
|
||||
### Creating a Stack
|
||||
To create stack memory we reserve some bytes at the end of our `boot.asm`:
|
||||
@@ -99,14 +96,14 @@ We use the `cmp` instruction to compare the value in `eax` to the magic value. I
|
||||
|
||||
In `no_multiboot`, we use the `jmp` (“jump”) instruction to jump to our error function. We could just as well use the `call` instruction, which additionally pushes the return address. But the return address is not needed because `error` never returns. To pass `0` as error code to the `error` function, we move it into `al` before the jump (`error` will read it from there).
|
||||
|
||||
[Multiboot specification]: http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||
[Multiboot specification]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||
[FLAGS register]: https://en.wikipedia.org/wiki/FLAGS_register
|
||||
|
||||
### CPUID check
|
||||
[CPUID] is a CPU instruction that can be used to get various information about the CPU. But not every processor supports it. CPUID detection is quite laborious, so we just copy a detection function from the [OSDev wiki][CPUID detection]:
|
||||
|
||||
[CPUID]: http://wiki.osdev.org/CPUID
|
||||
[CPUID detection]: http://wiki.osdev.org/Setting_Up_Long_Mode#Detection_of_CPUID
|
||||
[CPUID]: https://wiki.osdev.org/CPUID
|
||||
[CPUID detection]: https://wiki.osdev.org/Setting_Up_Long_Mode#Detection_of_CPUID
|
||||
|
||||
```nasm
|
||||
check_cpuid:
|
||||
@@ -154,7 +151,7 @@ Don't worry, you don't need to understand the details.
|
||||
### Long Mode check
|
||||
Now we can use CPUID to detect whether long mode can be used. I use code from [OSDev][long mode detection] again:
|
||||
|
||||
[long mode detection]: http://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64
|
||||
[long mode detection]: https://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64
|
||||
|
||||
```nasm
|
||||
check_long_mode:
|
||||
@@ -174,7 +171,7 @@ check_long_mode:
|
||||
mov al, "2"
|
||||
jmp error
|
||||
```
|
||||
Like many low-level things, CPUID is a bit strange. Instead of taking a parameter, the `cpuid` instruction implicitely uses the `eax` register as argument. To test if long mode is available, we need to call `cpuid` with `0x80000001` in `eax`. This loads some information to the `ecx` and `edx` registers. Long mode is supported if the 29th bit in `edx` is set. [Wikipedia][cpuid long mode] has detailed information.
|
||||
Like many low-level things, CPUID is a bit strange. Instead of taking a parameter, the `cpuid` instruction implicitly uses the `eax` register as argument. To test if long mode is available, we need to call `cpuid` with `0x80000001` in `eax`. This loads some information to the `ecx` and `edx` registers. Long mode is supported if the 29th bit in `edx` is set. [Wikipedia][cpuid long mode] has detailed information.
|
||||
|
||||
[cpuid long mode]: https://en.wikipedia.org/wiki/CPUID#EAX.3D80000001h:_Extended_Processor_Info_and_Feature_Bits
|
||||
|
||||
@@ -184,11 +181,11 @@ If you look at the assembly above, you'll probably notice that we call `cpuid` t
|
||||
We just call these check functions right after start:
|
||||
|
||||
```nasm
|
||||
global _start
|
||||
global start
|
||||
|
||||
section .text
|
||||
bits 32
|
||||
_start:
|
||||
start:
|
||||
mov esp, stack_top
|
||||
|
||||
call check_multiboot
|
||||
@@ -217,11 +214,7 @@ As I don't like these names, I will call them P4, P3, P2, and P1 from now on.
|
||||
|
||||
Each page table contains 512 entries and one entry is 8 bytes, so they fit exactly in one page (`512*8 = 4096`). To translate a virtual address to a physical address the CPU[^hardware_lookup] will do the following[^virtual_physical_translation_source]:
|
||||
|
||||
[^hardware_lookup]: In the x86 architecture, the page tables are _hardware walked_, so the CPU will look at the table on its own when it needs a translation. Other architectures, for example MIPS, just throw an exception and let the OS translate the virtual address.
|
||||
|
||||
[^virtual_physical_translation_source]: Image source: [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg), with modified font size, page table naming, and removed sign extended bits. The modified file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
|
||||
|
||||

|
||||

|
||||
|
||||
1. Get the address of the P4 table from the CR3 register
|
||||
2. Use bits 39-47 (9 bits) as an index into P4 (`2^9 = 512 = number of entries`)
|
||||
@@ -255,14 +248,13 @@ Bit(s) | Name | Meaning
|
||||
When we switch to long mode, paging will be activated automatically. The CPU will then try to read the instruction at the following address, but this address is now a virtual address. So we need to do _identity mapping_, i.e. map a physical address to the same virtual address.
|
||||
|
||||
The `huge page` bit is now very useful to us. It creates a 2MiB (when used in P2) or even a 1GiB page (when used in P3). So we could map the first _gigabytes_ of the kernel with only one P4 and one P3 table by using 1GiB pages. Unfortunately 1GiB pages are relatively new feature, for example Intel introduced it 2010 in the [Westmere architecture]. Therefore we will use 2MiB pages instead to make our kernel compatible to older computers, too.
|
||||
|
||||
[Westmere architecture]: https://en.wikipedia.org/wiki/Westmere_(microarchitecture)#Technology
|
||||
|
||||
To identity map the first gigabyte of our kernel with 512 2MiB pages, we need one P4, one P3, and one P2 table. Of course we will replace them with finer-grained tables later. But now that we're stuck with assembly, we choose the easiest way.
|
||||
|
||||
We can add these two tables at the beginning[^page_table_alignment] of the `.bss` section:
|
||||
|
||||
[^page_table_alignment]: Page tables need to be page-aligned as the bits 0-11 are used for flags. By putting these tables at the beginning of `.bss`, the linker can just page align the whole section and we don't have unused padding bytes in between.
|
||||
|
||||
```nasm
|
||||
...
|
||||
|
||||
@@ -320,7 +312,7 @@ set_up_page_tables:
|
||||
|
||||
ret
|
||||
```
|
||||
Maybe I first explain how an assembly loop works. We use the `ecx` register as a counter variable, just like `i` in a for loop. After mapping the `ecx-th` entry, we increase `ecx` by one and jump to `.map_p2_table` again if it's still smaller 512.
|
||||
Maybe I should first explain how an assembly loop works. We use the `ecx` register as a counter variable, just like `i` in a for loop. After mapping the `ecx-th` entry, we increase `ecx` by one and jump to `.map_p2_table` again if it's still smaller than 512.
|
||||
|
||||
To map a P2 entry we first calculate the start address of its page in `eax`: The `ecx-th` entry needs to be mapped to `ecx * 2MiB`. We use the `mul` operation for that, which multiplies `eax` with the given register and stores the result in `eax`. Then we set the `present`, `writable`, and `huge page` bits and write it to the P2 entry. The address of the `ecx-th` entry in P2 is `p2_table + ecx * 8`, because each entry is 8 bytes large.
|
||||
|
||||
@@ -386,55 +378,47 @@ start:
|
||||
To test it we execute `make run`. If the green OK is still printed, we have successfully enabled paging!
|
||||
|
||||
## The Global Descriptor Table
|
||||
After enabling Paging, the processor is in long mode. So we can use 64-bit instructions now, right? Wrong. The processor is still in some 32-bit compatibility submode. To actually execute 64-bit code, we need to set up a new Global Descriptor Table.
|
||||
After enabling Paging, the processor is in long mode. So we can use 64-bit instructions now, right? Wrong. The processor is still in a 32-bit compatibility submode. To actually execute 64-bit code, we need to set up a new Global Descriptor Table.
|
||||
The Global Descriptor Table (GDT) was used for _Segmentation_ in old operating systems. I won't explain Segmentation but the [Three Easy Pieces] OS book has good introduction ([PDF][Segmentation chapter]) again.
|
||||
|
||||
[Segmentation chapter]: http://pages.cs.wisc.edu/~remzi/OSTEP/vm-segmentation.pdf
|
||||
|
||||
Today almost everyone uses Paging instead of Segmentation (and so do we). But on x86, a GDT is always required, even when you're not using Segmentation. GRUB has set up a valid 32-bit GDT for us but now we need to switch to a long mode GDT.
|
||||
|
||||
A GDT always starts with a 0-entry and contains an arbitrary number of segment entries afterwards. An entry has the following format:
|
||||
A GDT always starts with a 0-entry and contains an arbitrary number of segment entries afterwards. A 64-bit entry has the following format:
|
||||
|
||||
Bit(s) | Name | Meaning
|
||||
--------------------- | ------ | ----------------------------------
|
||||
0-15 | limit 0-15 | the first 2 byte of the segment's limit
|
||||
16-39 | base 0-23 | the first 3 byte of the segment's base address
|
||||
40 | accessed | set by the CPU when the segment is accessed
|
||||
41 | read/write | reads allowed for code segments / writes allowed for data segments
|
||||
42 | direction/conforming | the segment grows down (i.e. base>limit) for data segments / the current privilege level can be higher than the specified level for code segments (else it must match exactly)
|
||||
0-41 | ignored | ignored in 64-bit mode
|
||||
42 | conforming | the current privilege level can be higher than the specified level for code segments (else it must match exactly)
|
||||
43 | executable | if set, it's a code segment, else it's a data segment
|
||||
44 | descriptor type | should be 1 for code and data segments
|
||||
45-46 | privilege | the [ring level]: 0 for kernel, 3 for user
|
||||
47 | present | must be 1 for valid selectors
|
||||
48-51 | limit 16-19 | bits 16 to 19 of the segment's limit
|
||||
52 | available | freely available to the OS
|
||||
48-52 | ignored | ignored in 64-bit mode
|
||||
53 | 64-bit | should be set for 64-bit code segments
|
||||
54 | 32-bit | should be set for 32-bit segments
|
||||
55 | granularity | if it's set, the limit is the number of pages, else it's a byte number
|
||||
56-63 | base 24-31 | the last byte of the base address
|
||||
54 | 32-bit | must be 0 for 64-bit segments
|
||||
55-63 | ignored | ignored in 64-bit mode
|
||||
|
||||
[ring level]: http://wiki.osdev.org/Security#Rings
|
||||
[ring level]: https://wiki.osdev.org/Security#Rings
|
||||
|
||||
We need one code and one data segment. They have the following bits set: _descriptor type_, _present_, and _read/write_. The code segment has additionally the _executable_ and the _64-bit_ flag. In Long mode, it's not possible to actually use the GDT entries for Segmentation and thus the base and limit fields must be 0. Translated to assembly the long mode GDT looks like this:
|
||||
We need one code segment, a data segment is not necessary in 64-bit mode. Code segments have the following bits set: _descriptor type_, _present_, _executable_ and the _64-bit_ flag. Translated to assembly the long mode GDT looks like this:
|
||||
|
||||
```nasm
|
||||
section .rodata
|
||||
gdt64:
|
||||
dq 0 ; zero entry
|
||||
dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) ; code segment
|
||||
dq (1<<44) | (1<<47) | (1<<41) ; data segment
|
||||
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
|
||||
```
|
||||
We chose the `.rodata` section here because it's initialized read-only data. The `dq` command stands for `define quad` and outputs a 64-bit constant (similar to `dw` and `dd`). And the `(1<<44)` is a [bit shift] that sets bit 44.
|
||||
|
||||
[bit shift]: http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/bitshift.html
|
||||
We chose the `.rodata` section here because it's initialized read-only data. The `dq` command stands for `define quad` and outputs a 64-bit constant (similar to `dw` and `dd`). And the `(1<<43)` is a bit shift that sets bit 43.
|
||||
|
||||
### Loading the GDT
|
||||
To load our new 64-bit GDT, we have to tell the CPU its address and length. We do this by passing the memory location of a special pointer structure to the `lgdt` (load GDT) instruction. The pointer structure looks like this:
|
||||
|
||||
```nasm
|
||||
gdt64:
|
||||
...
|
||||
dq (1<<44) | (1<<47) | (1<<41) ; data segment
|
||||
dq 0 ; zero entry
|
||||
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
|
||||
.pointer:
|
||||
dw $ - gdt64 - 1
|
||||
dq gdt64
|
||||
@@ -454,39 +438,22 @@ start:
|
||||
; print `OK` to screen
|
||||
...
|
||||
```
|
||||
When you still see the green `OK`, everything went fine and the new GDT is loaded. But we still can't execute 64-bit code: The selector registers such as the code selector `cs` and the data selector `ds` still have the values from the old GDT. To update them, we need to load them with the GDT offset (in bytes) of the desired segment. In our case the code segment starts at byte 8 of the GDT and the data segment at byte 16. Let's try it:
|
||||
|
||||
```nasm
|
||||
...
|
||||
lgdt [gdt64.pointer]
|
||||
|
||||
; update selectors
|
||||
mov ax, 16
|
||||
mov ss, ax ; stack selector
|
||||
mov ds, ax ; data selector
|
||||
mov es, ax ; extra selector
|
||||
|
||||
; print `OK` to screen
|
||||
...
|
||||
```
|
||||
It should still work. The segment selectors are only 16-bits large, so we use the 16-bit `ax` subregister. Notice that we didn't update the code selector `cs`. We will do that later. First we should replace this hardcoded `16` by adding some labels to our GDT:
|
||||
When you still see the green `OK`, everything went fine and the new GDT is loaded. But we still can't execute 64-bit code: The code selector register `cs` still has the values from the old GDT. To update it, we need to load it with the GDT offset (in bytes) of the desired segment. In our case the code segment starts at byte 8 of the GDT, but we don't want to hardcode that 8 (in case we modify our GDT later). Instead, we add a `.code` label to our GDT, that calculates the offset directly from the GDT:
|
||||
|
||||
```nasm
|
||||
section .rodata
|
||||
gdt64:
|
||||
dq 0 ; zero entry
|
||||
.code: equ $ - gdt64 ; new
|
||||
dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) ; code segment
|
||||
.data: equ $ - gdt64 ; new
|
||||
dq (1<<44) | (1<<47) | (1<<41) ; data segment
|
||||
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
|
||||
.pointer:
|
||||
...
|
||||
```
|
||||
We can't just use normal labels here, as we need the table offset. We calculate this offset using the current address `$` and set the labels to this value using [equ]. Now we can use `gdt64.data` instead of 16 and `gdt64.code` instead of 8 and these labels will still work if we modify the GDT.
|
||||
We can't just use a normal label here, since we need the table _offset_. We calculate this offset using the current address `$` and set the label to this value using [equ]. Now we can use `gdt64.code` instead of 8 and this label will still work if we modify the GDT.
|
||||
|
||||
[equ]: http://www.nasm.us/doc/nasmdoc3.html#section-3.2.4
|
||||
[equ]: https://www.nasm.us/doc/nasmdoc3.html#section-3.2.4
|
||||
|
||||
Now there is just one last step left to enter the true 64-bit mode: We need to load `cs` with `gdt64.code`. But we can't do it through `mov`. The only way to reload the code selector is a _far jump_ or a _far return_. These instructions work like a normal jump/return but change the code selector. We use a far jump to a long mode label:
|
||||
In order to finally enter the true 64-bit mode, we need to load `cs` with `gdt64.code`. But we can't do it through `mov`. The only way to reload the code selector is a _far jump_ or a _far return_. These instructions work like a normal jump/return but change the code selector. We use a far jump to a long mode label:
|
||||
|
||||
```nasm
|
||||
global start
|
||||
@@ -496,12 +463,6 @@ start:
|
||||
...
|
||||
lgdt [gdt64.pointer]
|
||||
|
||||
; update selectors
|
||||
mov ax, gdt64.data
|
||||
mov ss, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
|
||||
jmp gdt64.code:long_mode_start
|
||||
...
|
||||
```
|
||||
@@ -528,10 +489,37 @@ You should see a green `OKAY` on the screen. Some notes on this last step:
|
||||
|
||||
_Congratulations_! You have successfully wrestled through this CPU configuration and compatibility mode mess :).
|
||||
|
||||
## What's next?
|
||||
It's time to finally leave assembly behind[^leave_assembly_behind] and switch to some higher level language. We won't use C or C++ (not even a single line). Instead we will use the relatively new [Rust] language. It's a systems language without garbage collections but with guaranteed memory safety. Through a real type system and many abstractions it feels like a high-level language but can still be low-level enough for OS development. The [next post] describes the Rust setup.
|
||||
#### One Last Thing
|
||||
Above, we reloaded the code segment register `cs` with the new GDT offset. However, the data segment registers `ss`, `ds`, `es`, `fs`, and `gs` still contain the data segment offsets of the old GDT. This isn't necessarily bad, since they're ignored by almost all instructions in 64-bit mode. However, there are a few instructions that expect a valid data segment descriptor _or the null descriptor_ in those registers. An example is the the [iretq] instruction that we'll need in the [_Returning from Exceptions_] post.
|
||||
|
||||
[^leave_assembly_behind]: Actually we will still need some assembly in the future, but I'll try to minimize it.
|
||||
[iretq]: @/edition-1/extra/naked-exceptions/03-returning-from-exceptions/index.md#the-iretq-instruction
|
||||
[_Returning from Exceptions_]: @/edition-1/extra/naked-exceptions/03-returning-from-exceptions/index.md
|
||||
|
||||
To avoid future problems, we reload all data segment registers with null:
|
||||
|
||||
```nasm
|
||||
long_mode_start:
|
||||
; load 0 into all data segment registers
|
||||
mov ax, 0
|
||||
mov ss, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
; print `OKAY` to screen
|
||||
...
|
||||
```
|
||||
|
||||
## What's next?
|
||||
It's time to finally leave assembly behind and switch to [Rust]. Rust is a systems language without garbage collections that guarantees memory safety. Through a real type system and many abstractions it feels like a high-level language but can still be low-level enough for OS development. The [next post] describes the Rust setup.
|
||||
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
[next post]: {{% relref "2015-09-02-set-up-rust.md" %}}
|
||||
[next post]: @/edition-1/posts/03-set-up-rust/index.md
|
||||
|
||||
## Footnotes
|
||||
[^hardware_lookup]: In the x86 architecture, the page tables are _hardware walked_, so the CPU will look at the table on its own when it needs a translation. Other architectures, for example MIPS, just throw an exception and let the OS translate the virtual address.
|
||||
|
||||
[^virtual_physical_translation_source]: Image source: [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg), with modified font size, page table naming, and removed sign extended bits. The modified file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
|
||||
|
||||
[^page_table_alignment]: Page tables need to be page-aligned as the bits 0-11 are used for flags. By putting these tables at the beginning of `.bss`, the linker can just page align the whole section and we don't have unused padding bytes in between.
|
||||
491
blog/content/edition-1/posts/03-set-up-rust/index.md
Normal file
@@ -0,0 +1,491 @@
|
||||
+++
|
||||
title = "Set Up Rust"
|
||||
weight = 3
|
||||
path = "set-up-rust"
|
||||
aliases = ["set-up-rust.html", "setup-rust.html", "/2015/09/02/setup-rust/", "/rust-os/setup-rust.html"]
|
||||
date = 2015-09-02
|
||||
template = "edition-1/page.html"
|
||||
[extra]
|
||||
updated = "2017-04-12"
|
||||
+++
|
||||
|
||||
In the previous posts we created a [minimal Multiboot kernel][multiboot post] and [switched to Long Mode][long mode post]. Now we can finally switch to [Rust] code. Rust is a high-level language without runtime. It allows us to not link the standard library and write bare metal code. Unfortunately the setup is not quite hassle-free yet.
|
||||
|
||||
[multiboot post]: @/edition-1/posts/01-multiboot-kernel/index.md
|
||||
[long mode post]: @/edition-1/posts/02-entering-longmode/index.md
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
|
||||
<!-- more -->
|
||||
|
||||
This blog post tries to set up Rust step-by-step and point out the different problems. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is in a [Github repository], too.
|
||||
|
||||
[file an issue]: https://github.com/phil-opp/blog_os/issues
|
||||
[Github repository]: https://github.com/phil-opp/blog_os/tree/first_edition_post_3
|
||||
|
||||
## Installing Rust
|
||||
We need a nightly compiler, as we will use many unstable features. To manage Rust installations I highly recommend [rustup]. It allows you to install nightly, beta, and stable compilers side-by-side and makes it easy to update them. To use a nightly compiler for the current directory, you can run `rustup override add nightly`. Alternatively, you can add a file called `rust-toolchain` to the project's root directory:
|
||||
|
||||
```
|
||||
nightly
|
||||
```
|
||||
|
||||
[rustup]: https://www.rustup.rs/
|
||||
|
||||
## Creating a Cargo project
|
||||
[Cargo] is Rust's excellent package manager. Normally you would call `cargo new` when you want to create a new project folder. We can't use it because our folder already exists, so we need to do it manually. Fortunately we only need to add a cargo configuration file named `Cargo.toml`:
|
||||
|
||||
[Cargo]: https://doc.crates.io/guide.html
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "blog_os"
|
||||
version = "0.1.0"
|
||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
```
|
||||
The `package` section contains required project metadata such as the [semantic crate version]. The `lib` section specifies that we want to build a static library, i.e. a library that contains all of its dependencies. This is required to link the Rust project with our kernel.
|
||||
|
||||
[semantic crate version]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-package-section
|
||||
|
||||
Now we place our root source file in `src/lib.rs`:
|
||||
|
||||
```rust
|
||||
#![feature(lang_items)]
|
||||
#![no_std]
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn rust_main() {}
|
||||
|
||||
#[lang = "eh_personality"] #[no_mangle] pub extern fn eh_personality() {}
|
||||
#[lang = "panic_fmt"] #[no_mangle] pub extern fn panic_fmt() -> ! {loop{}}
|
||||
```
|
||||
Let's break it down:
|
||||
|
||||
- `#!` defines an [attribute] of the current module. Since we are at the root module, the attributes apply to the crate itself.
|
||||
- The `feature` attribute is used to allow the specified _feature-gated_ attributes in this crate. You can't do that in a stable/beta compiler, so this is one reason we need a Rust nighly.
|
||||
- The `no_std` attribute prevents the automatic linking of the standard library. We can't use `std` because it relies on operating system features like files, system calls, and various device drivers. Remember that currently the only “feature” of our OS is printing `OKAY` :).
|
||||
- A `#` without a `!` afterwards defines an attribute for the _following_ item (a function in our case).
|
||||
- The `no_mangle` attribute disables the automatic [name mangling] that Rust uses to get unique function names. We want to do a `call rust_main` from our assembly code, so this function name must stay as it is.
|
||||
- We mark our main function as `extern` to make it compatible to the standard C [calling convention].
|
||||
- The `lang` attribute defines a Rust [language item].
|
||||
- The `eh_personality` function is used for Rust's [unwinding] on `panic!`. We can leave it empty since we don't have any unwinding support in our OS yet.
|
||||
- The `panic_fmt` function is the entry point on panic. Right now we can't do anything useful, so we just make sure that it doesn't return (required by the `!` return type).
|
||||
|
||||
[attribute]: https://doc.rust-lang.org/book/attributes.html
|
||||
[name mangling]: https://en.wikipedia.org/wiki/Name_mangling
|
||||
[calling convention]: https://en.wikipedia.org/wiki/Calling_convention
|
||||
[language item]: https://doc.rust-lang.org/1.10.0/book/lang-items.html
|
||||
[unwinding]: https://doc.rust-lang.org/nomicon/unwinding.html
|
||||
|
||||
## Building Rust
|
||||
We can now build it using `cargo build`, which creates a static library at `target/debug/libblog_os.a`. However, the resulting library is specific to our _host_ operating system. This is undesirable, because our target system might be different.
|
||||
|
||||
Let's define some properties of our target system:
|
||||
|
||||
- **x86_64**: Our target CPU is a recent `x86_64` CPU.
|
||||
- **No operating system**: Our target does not run any operating system (we're currently writing it), so the compiler should not assume any OS-specific functionality.
|
||||
- **Handles hardware interrupts**: We're writing a kernel, so we'll need to handle asynchronous hardware interrupts at some point. This means that we have to disable a certain stack pointer optimization (the so-called [red zone]), because it would cause stack corruptions otherwise.
|
||||
- **No SSE**: Our target might not have [SSE] support. Even if it does, we probably don't want to use SSE instructions in our kernel, because it makes interrupt handling much slower. We will explain this in detail in the [“Handling Exceptions”] post.
|
||||
- **No hardware floats**: The `x86_64` architecture uses SSE instructions for floating point operations, which we don't want to use (see the previous point). So we also need to avoid hardware floating point operations in our kernel. Instead, we will use _soft floats_, which are basically software functions that emulate floating point operations using normal integers.
|
||||
|
||||
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||
|
||||
### Target Specifications
|
||||
Rust allows us to define [custom targets] through a JSON configuration file. A minimal target specification equal to `x86_64-unknown-linux-gnu` (the default 64-bit Linux target) looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
||||
"linker-flavor": "gcc",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "64",
|
||||
"target-c-int-width": "32",
|
||||
"arch": "x86_64",
|
||||
"os": "linux"
|
||||
}
|
||||
```
|
||||
|
||||
[custom targets]: https://doc.rust-lang.org/1.1.0/rustc_back/target/
|
||||
|
||||
The `llvm-target` field specifies the target triple that is passed to LLVM. [Target triples] are a naming convention that define the CPU architecture (e.g., `x86_64` or `arm`), the vendor (e.g., `apple` or `unknown`), the operating system (e.g., `windows` or `linux`), and the [ABI] \(e.g., `gnu` or `msvc`). For example, the target triple for 64-bit Linux is `x86_64-unknown-linux-gnu` and for 32-bit Windows the target triple is `i686-pc-windows-msvc`.
|
||||
|
||||
[Target triples]: https://llvm.org/docs/LangRef.html#target-triple
|
||||
[ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
|
||||
|
||||
The `data-layout` field is also passed to LLVM and specifies how data should be laid out in memory. It consists of various specifications separated by a `-` character. For example, the `e` means little endian and `S128` specifies that the stack should be 128 bits (= 16 byte) aligned. The format is described in detail in the [LLVM documentation][data layout] but there shouldn't be a reason to change this string.
|
||||
|
||||
The `linker-flavor` field was recently introduced in [#40018] with the intention to add support for the LLVM linker [LLD], which is platform independent. In the future, this might allow easy cross compilation without the need to install a gcc cross compiler for linking.
|
||||
|
||||
[#40018]: https://github.com/rust-lang/rust/pull/40018
|
||||
[LLD]: https://lld.llvm.org/
|
||||
|
||||
The other fields are used for conditional compilation. This allows crate authors to use `cfg` variables to write special code for depending on the OS or the architecture. There isn't any up-to-date documentation about these fields but the [corresponding source code][target specification] is quite readable.
|
||||
|
||||
[data layout]: https://llvm.org/docs/LangRef.html#data-layout
|
||||
[target specification]: https://github.com/rust-lang/rust/blob/c772948b687488a087356cb91432425662e034b9/src/librustc_back/target/mod.rs#L194-L214
|
||||
|
||||
### A Kernel Target Specification
|
||||
For our target system, we define the following JSON configuration in a file named `x86_64-blog_os.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"llvm-target": "x86_64-unknown-none",
|
||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
||||
"linker-flavor": "gcc",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "64",
|
||||
"target-c-int-width": "32",
|
||||
"arch": "x86_64",
|
||||
"os": "none",
|
||||
"disable-redzone": true,
|
||||
"features": "-mmx,-sse,+soft-float"
|
||||
}
|
||||
```
|
||||
|
||||
As `llvm-target` we use `x86_64-unknown-none`, which defines the `x86_64` architecture, an `unknown` vendor, and no operating system (`none`). The ABI doesn't matter for us, so we just leave it off. The `data-layout` field is just copied from the `x86_64-unknown-linux-gnu` target. We also use the same values for the `target-endian`, `target-pointer-width`, `target-c-int-width`, and `arch` fields. For the `os` field we choose `none`, since our kernel runs on bare metal.
|
||||
|
||||
#### The Red Zone
|
||||
The [red zone] is an optimization of the [System V ABI] that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:
|
||||
|
||||
[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||
[System V ABI]: https://wiki.osdev.org/System_V_ABI
|
||||
|
||||

|
||||
|
||||
The image shows the stack frame of a function with `n` local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.
|
||||
|
||||
The red zone is defined as the 128 bytes below the adjusted stack pointer. The function can use this area for temporary data that's not needed across function calls. Thus, the two instructions for adjusting the stack pointer can be avoided in some cases (e.g. in small leaf functions).
|
||||
|
||||
However, this optimization leads to huge problems with exceptions or hardware interrupts. Let's assume that an exception occurs while a function uses the red zone:
|
||||
|
||||

|
||||
|
||||
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
|
||||
|
||||
[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
|
||||
|
||||
To avoid such bugs when we implement exception handling in the future, we disable the red zone right from the beginning. This is achieved by adding the `"disable-redzone": true` line to our target configuration file.
|
||||
|
||||
#### SIMD Extensions
|
||||
The `features` field enables/disables target features. We disable the `mmx` and `sse` features by prefixing them with a minus and enable the `soft-float` feature by prefixing it with a plus. The `mmx` and `sse` features determine support for [Single Instruction Multiple Data (SIMD)] instructions, which simultaneously perform an operation (e.g. addition) on multiple data words. The `x86` architecture supports the following standards:
|
||||
|
||||
[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
|
||||
|
||||
- [MMX]: The _Multi Media Extension_ instruction set was introduced in 1997 and defines eight 64 bit registers called `mm0` through `mm7`. These registers are just aliases for the registers of the [x87 floating point unit].
|
||||
- [SSE]: The _Streaming SIMD Extensions_ instruction set was introduced in 1999. Instead of re-using the floating point registers, it adds a completely new register set. The sixteen new registers are called `xmm0` through `xmm15` and are 128 bits each.
|
||||
- [AVX]: The _Advanced Vector Extensions_ are extensions that further increase the size of the multimedia registers. The new registers are called `ymm0` through `ymm15` and are 256 bits each. They extend the `xmm` registers, so e.g. `xmm0` is the lower half of `ymm0`.
|
||||
|
||||
[MMX]: https://en.wikipedia.org/wiki/MMX_(instruction_set)
|
||||
[x87 floating point unit]: https://en.wikipedia.org/wiki/X87
|
||||
[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
|
||||
[AVX]: https://en.wikipedia.org/wiki/Advanced_Vector_Extensions
|
||||
|
||||
By using such SIMD standards, programs can often speed up significantly. Good compilers are able to transform normal loops into such SIMD code automatically through a process called [auto-vectorization].
|
||||
|
||||
[auto-vectorization]: https://en.wikipedia.org/wiki/Automatic_vectorization
|
||||
|
||||
However, the large SIMD registers lead to problems in OS kernels. The reason is that the kernel has to backup all registers that it uses on each hardware interrupt (we will look into this in the [“Handling Exceptions”] post). So if the kernel uses SIMD registers, it has to backup a lot more data, which noticeably decreases performance. To avoid this performance loss, we disable the `sse` and `mmx` features (the `avx` feature is disabled by default).
|
||||
|
||||
As noted above, floating point operations on `x86_64` use SSE registers, so floats are no longer usable without SSE. Unfortunately, the Rust core library already uses floats (e.g., it implements traits for `f32` and `f64`), so we need an alternative way to implement float operations. The `soft-float` feature solves this problem by emulating all floating point operations through software functions based on normal integers.
|
||||
|
||||
### Compiling
|
||||
To build our kernel for our new target, we pass the configuration file's name as `--target` argument. There is currently an [open bug][custom-target-bug] for custom target specifications, so you also need to set the `RUST_TARGET_PATH` environment variable to the current directory, otherwise Rust doesn't find your target. The full command is:
|
||||
|
||||
[custom-target-bug]: https://github.com/rust-lang/cargo/issues/4905
|
||||
|
||||
```
|
||||
RUST_TARGET_PATH=$(pwd) cargo build --target x86_64-blog_os
|
||||
```
|
||||
|
||||
However, the following error occurs:
|
||||
|
||||
```
|
||||
error[E0463]: can't find crate for `core`
|
||||
|
|
||||
= note: the `x86_64-blog_os` target may not be installed
|
||||
```
|
||||
|
||||
The error tells us that the Rust compiler no longer finds the core library. The [core library] is implicitly linked to all `no_std` crates and contains things such as `Result`, `Option`, and iterators.
|
||||
|
||||
[core library]: https://doc.rust-lang.org/nightly/core/index.html
|
||||
|
||||
The problem is that the core library is distributed together with the Rust compiler as a _precompiled_ library. So it is only valid for the host triple (e.g., `x86_64-unknown-linux-gnu`) but not for our custom target. If we want to compile code for other targets, we need to recompile `core` for these targets first.
|
||||
|
||||
#### Xargo
|
||||
That's where [xargo] comes in. It is a wrapper for cargo that eases cross compilation. We can install it by executing:
|
||||
|
||||
[xargo]: https://github.com/japaric/xargo
|
||||
|
||||
```
|
||||
cargo install xargo
|
||||
```
|
||||
|
||||
Xargo depends on the rust source code, which we can install with `rustup component add rust-src`.
|
||||
|
||||
Xargo is “a drop-in replacement for cargo”, so every cargo command also works with `xargo`. You can do e.g. `xargo --help`, `xargo clean`, or `xargo doc`. However, the `build` command gains additional functionality: `xargo build` will automatically cross compile the `core` library when compiling for custom targets.
|
||||
|
||||
Let's try it:
|
||||
|
||||
```bash
|
||||
> RUST_TARGET_PATH=$(pwd) xargo build --target=x86_64-blog_os
|
||||
Compiling core v0.0.0 (file:///…/rust/src/libcore)
|
||||
Finished release [optimized] target(s) in 22.87 secs
|
||||
Compiling blog_os v0.1.0 (file:///…/blog_os/tags)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
|
||||
```
|
||||
|
||||
It worked! We see that `xargo` cross-compiled the `core` library for our new custom target and then continued to compile our `blog_os` crate. After compilation, we can find a static library at `target/x86_64-blog_os/debug/libblog_os.a`, which can be linked with our assembly kernel.
|
||||
|
||||
## Integrating Rust
|
||||
Let's try to integrate our Rust library into our assembly kernel so that we can call the `rust_main` function. For that we need to pass the `libblog_os.a` file to the linker, together with the assembly object files.
|
||||
|
||||
### Adjusting the Makefile
|
||||
To build and link the rust library on `make`, we extend our `Makefile`([full file][github makefile]):
|
||||
|
||||
```make
|
||||
# ...
|
||||
target ?= $(arch)-blog_os
|
||||
rust_os := target/$(target)/debug/libblog_os.a
|
||||
# ...
|
||||
.PHONY: all clean run iso kernel
|
||||
# ...
|
||||
$(kernel): kernel $(rust_os) $(assembly_object_files) $(linker_script)
|
||||
@ld -n -T $(linker_script) -o $(kernel) \
|
||||
$(assembly_object_files) $(rust_os)
|
||||
|
||||
kernel:
|
||||
@RUST_TARGET_PATH=$(shell pwd) xargo build --target $(target)
|
||||
```
|
||||
We add a new `kernel` target that just executes `xargo build` and modify the `$(kernel)` target to link the created static lib. We also add the new `kernel` target to the `.PHONY` list, since it does not belong to a file with that name.
|
||||
|
||||
But now `xargo build` is executed on every `make`, even if no source file was changed. And the ISO is recreated on every `make iso`/`make run`, too. We could try to avoid this by adding dependencies on all rust source and cargo configuration files to the `kernel` target, but the ISO creation takes only half a second on my machine and most of the time we will have changed a Rust file when we run `make`. So we keep it simple for now and let cargo do the bookkeeping of changed files (it does it anyway).
|
||||
|
||||
[github makefile]: https://github.com/phil-opp/blog_os/blob/first_edition_post_3/Makefile
|
||||
|
||||
### Calling Rust
|
||||
Now we can call the main method in `long_mode_start`:
|
||||
|
||||
```nasm
|
||||
bits 64
|
||||
long_mode_start:
|
||||
...
|
||||
|
||||
; call the rust main
|
||||
extern rust_main ; new
|
||||
call rust_main ; new
|
||||
|
||||
; print `OKAY` to screen
|
||||
mov rax, 0x2f592f412f4b2f4f
|
||||
mov qword [0xb8000], rax
|
||||
hlt
|
||||
```
|
||||
By defining `rust_main` as `extern` we tell nasm that the function is defined in another file. As the linker takes care of linking them together, we'll get a linker error if we have a typo in the name or forget to mark the rust function as `pub extern`.
|
||||
|
||||
If we've done everything right, we should still see the green `OKAY` when executing `make run`. That means that we successfully called the Rust function and returned back to assembly.
|
||||
|
||||
### Fixing Linker Errors
|
||||
Now we can try some Rust code:
|
||||
|
||||
```rust
|
||||
pub extern fn rust_main() {
|
||||
let x = ["Hello", "World", "!"];
|
||||
let y = x;
|
||||
}
|
||||
```
|
||||
When we test it using `make run`, it fails with `undefined reference to 'memcpy'`. The `memcpy` function is one of the basic functions of the C library (`libc`). Usually the `libc` crate is linked to every Rust program together with the standard library, but we opted out through `#![no_std]`. We could try to fix this by adding the [libc crate] as `extern crate`. But `libc` is just a wrapper for the system `libc`, for example `glibc` on Linux, so this won't work for us. Instead we need to recreate the basic `libc` functions such as `memcpy`, `memmove`, `memset`, and `memcmp` in Rust.
|
||||
|
||||
[libc crate]: https://doc.rust-lang.org/1.10.0/libc/index.html
|
||||
|
||||
#### rlibc
|
||||
Fortunately there already is a crate for that: [rlibc]. When we look at its [source code][rlibc source] we see that it contains no magic, just some [raw pointer] operations in a while loop. To add `rlibc` as a dependency we just need to add two lines to the `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
...
|
||||
[dependencies]
|
||||
rlibc = "1.0"
|
||||
```
|
||||
and an `extern crate` definition in our `src/lib.rs`:
|
||||
|
||||
```rust
|
||||
...
|
||||
extern crate rlibc;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn rust_main() {
|
||||
...
|
||||
```
|
||||
Now `make run` doesn't complain about `memcpy` anymore. Instead it will show a pile of new ugly linker errors:
|
||||
|
||||
```
|
||||
target/x86_64-blog_os/debug/libblog_os.a(core-92335f822fa6c9a6.0.o):
|
||||
In function `_$LT$f32$u20$as$u20$core..num..dec2flt..
|
||||
rawfp..RawFloat$GT$::from_int::h50f7952efac3fdca':
|
||||
core.cgu-0.rs:(.text._ZN59_$LT$f32$u20$as$u20$core..num..dec2flt..
|
||||
rawfp..RawFloat$GT$8from_int17h50f7952efac3fdcaE+0x2):
|
||||
undefined reference to `__floatundisf'
|
||||
target/x86_64-blog_os/debug/libblog_os.a(core-92335f822fa6c9a6.0.o):
|
||||
In function `_$LT$f64$u20$as$u20$core..num..dec2flt..rawfp..
|
||||
RawFloat$GT$::from_int::h12a81f175246914a':
|
||||
core.cgu-0.rs:(.text._ZN59_$LT$f64$u20$as$u20$core..num..dec2flt..rawfp..
|
||||
RawFloat$GT$8from_int17h12a81f175246914aE+0x2):
|
||||
undefined reference to `__floatundidf'
|
||||
target/x86_64-blog_os/debug/libblog_os.a(core-92335f822fa6c9a6.0.o):
|
||||
In function `core::num::from_str_radix::h09b12650704e0508':
|
||||
core.cgu-0.rs:(.text._ZN4core3num14from_str_radix
|
||||
17h09b12650704e0508E+0xcf):
|
||||
undefined reference to `__muloti4'
|
||||
...
|
||||
```
|
||||
|
||||
[rlibc]: https://crates.io/crates/rlibc
|
||||
[rlibc source]: https://github.com/alexcrichton/rlibc/blob/defb486e765846417a8e73329e8c5196f1dca49a/src/lib.rs
|
||||
[raw pointer]: https://doc.rust-lang.org/book/raw-pointers.html
|
||||
[crates.io]: https://crates.io
|
||||
|
||||
#### --gc-sections
|
||||
The new errors are linker errors about various missing functions such as `__floatundisf` or `__muloti4`. These functions are part of LLVM's [`compiler-rt` builtins] and are normally linked by the standard library. For `no_std` crates like ours, one has to link the `compiler-rt` library manually. Unfortunately, this library is implemented in C and the build process is a bit cumbersome. Alternatively, there is the [compiler-builtins] crate that tries to port the library to Rust, but it isn't complete yet.
|
||||
|
||||
[`compiler-rt` builtins]: https://compiler-rt.llvm.org/
|
||||
[compiler-builtins]: https://github.com/rust-lang-nursery/compiler-builtins
|
||||
|
||||
In our case, there is a much simpler solution, since our kernel doesn't really need any of those functions yet. So we can just tell the linker to remove unused program sections and hopefully all references to these functions will disappear. Removing unused sections is generally a good idea as it reduces kernel size. The magic linker flag for this is `--gc-sections`, which stands for “garbage collect sections”. Let's add it to the `$(kernel)` target in our `Makefile`:
|
||||
|
||||
```make
|
||||
$(kernel): xargo $(rust_os) $(assembly_object_files) $(linker_script)
|
||||
@ld -n --gc-sections -T $(linker_script) -o $(kernel) \
|
||||
$(assembly_object_files) $(rust_os)
|
||||
```
|
||||
Now we can do a `make run` again and it compiles without errors again. However, it doesn't boot anymore:
|
||||
|
||||
```
|
||||
GRUB error: no multiboot header found.
|
||||
```
|
||||
What happened? Well, the linker removed unused sections. And since we don't use the Multiboot section anywhere, `ld` removes it, too. So we need to tell the linker explicitly that it should keep this section. The `KEEP` command does exactly that, so we add it to the linker script (`linker.ld`):
|
||||
|
||||
```
|
||||
.boot :
|
||||
{
|
||||
/* ensure that the multiboot header is at the beginning */
|
||||
KEEP(*(.multiboot_header))
|
||||
}
|
||||
```
|
||||
Now everything should work again (the green `OKAY`). But there is another linking issue, which is triggered by some other example code.
|
||||
|
||||
#### panic = "abort"
|
||||
|
||||
The following snippet still fails:
|
||||
|
||||
```rust
|
||||
...
|
||||
let test = (0..3).flat_map(|x| 0..x).zip(0..);
|
||||
```
|
||||
The error is a linker error again (hence the ugly error message):
|
||||
|
||||
```
|
||||
target/x86_64-blog_os/debug/libblog_os.a(blog_os-b5a29f28b14f1f1f.0.o):
|
||||
In function `core::ptr::drop_in_place<core::iter::Zip<
|
||||
core::iter::FlatMap<core::ops::Range<i32>, core::ops::Range<i32>,
|
||||
closure>, core::ops::RangeFrom<i32>>>':
|
||||
/…/rust/src/libcore/ptr.rs:66:
|
||||
undefined reference to `_Unwind_Resume'
|
||||
target/x86_64-blog_os/debug/libblog_os.a(blog_os-b5a29f28b14f1f1f.0.o):
|
||||
In function `core::iter::iterator::Iterator::zip<core::iter::FlatMap<
|
||||
core::ops::Range<i32>, core::ops::Range<i32>, closure>,
|
||||
core::ops::RangeFrom<i32>>':
|
||||
/…/rust/src/libcore/iter/iterator.rs:389:
|
||||
undefined reference to `_Unwind_Resume'
|
||||
...
|
||||
```
|
||||
|
||||
So the linker can't find a function named `_Unwind_Resume` that is referenced e.g. in `iter/iterator.rs:389` in libcore. This reference is not really there at [line 389][iterator.rs:389] of libcore's `iterator.rs`. Instead, it is a compiler inserted _landing pad_, which is used for panic handling.
|
||||
|
||||
[iterator.rs:389]: https://github.com/rust-lang/rust/blob/c58c928e658d2e45f816fd05796a964aa83759da/src/libcore/iter/iterator.rs#L389
|
||||
|
||||
By default, the destructors of all stack variables are run when a `panic` occurs. This is called _unwinding_ and allows parent threads to recover from panics. However, it requires a platform specific gcc library, which isn't available in our kernel.
|
||||
|
||||
Fortunately, Rust allows us to disable unwinding for our target. For that we add the following line to our `x86_64-blog_os.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"...",
|
||||
"panic-strategy": "abort"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
By setting the [panic strategy] to `abort` instead of the default `unwind`, we disable all unwinding in our kernel. Let's try `make run` again:
|
||||
|
||||
[panic strategy]: https://github.com/nox/rust-rfcs/blob/master/text/1513-less-unwinding.md
|
||||
|
||||
```
|
||||
Compiling core v0.0.0 (file:///…/rust/src/libcore)
|
||||
Finished release [optimized] target(s) in 22.24 secs
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.5 secs
|
||||
target/x86_64-blog_os/debug/libblog_os.a(blog_os-b5a29f28b14f1f1f.0.o):
|
||||
In function `core::ptr::drop_in_place<…>':
|
||||
/…/src/libcore/ptr.rs:66:
|
||||
undefined reference to `_Unwind_Resume'
|
||||
...
|
||||
```
|
||||
|
||||
We see that `xargo` recompiles the `core` crate, but the `_Unwind_Resume` error still occurs. This is because our `blog_os` crate was not recompiled somehow and thus still references the unwinding function. To fix this, we need to force a recompile using `cargo clean`:
|
||||
|
||||
```
|
||||
> cargo clean
|
||||
> make run
|
||||
Compiling rlibc v1.0.0
|
||||
Compiling blog_os v0.1.0 (file:///home/philipp/Documents/blog_os/tags)
|
||||
warning: unused variable: `test` […]
|
||||
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs
|
||||
```
|
||||
|
||||
It worked! We no longer see linker errors and our kernel prints `OKAY` again.
|
||||
|
||||
## Hello World!
|
||||
Finally, it's time for a `Hello World!` from Rust:
|
||||
|
||||
```rust
|
||||
#[no_mangle]
|
||||
pub extern fn rust_main() {
|
||||
// ATTENTION: we have a very small stack and no guard page
|
||||
|
||||
let hello = b"Hello World!";
|
||||
let color_byte = 0x1f; // white foreground, blue background
|
||||
|
||||
let mut hello_colored = [color_byte; 24];
|
||||
for (i, char_byte) in hello.into_iter().enumerate() {
|
||||
hello_colored[i*2] = *char_byte;
|
||||
}
|
||||
|
||||
// write `Hello World!` to the center of the VGA text buffer
|
||||
let buffer_ptr = (0xb8000 + 1988) as *mut _;
|
||||
unsafe { *buffer_ptr = hello_colored };
|
||||
|
||||
loop{}
|
||||
}
|
||||
```
|
||||
Some notes:
|
||||
|
||||
- The `b` prefix creates a [byte string], which is just an array of `u8`
|
||||
- [enumerate] is an `Iterator` method that adds the current index `i` to elements
|
||||
- `buffer_ptr` is a [raw pointer] that points to the center of the VGA text buffer
|
||||
- Rust doesn't know the VGA buffer and thus can't guarantee that writing to the `buffer_ptr` is safe (it could point to important data). So we need to tell Rust that we know what we are doing by using an [unsafe block].
|
||||
|
||||
[byte string]: https://doc.rust-lang.org/reference/tokens.html#characters-and-strings
|
||||
[enumerate]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.enumerate
|
||||
[unsafe block]: https://doc.rust-lang.org/book/unsafe.html
|
||||
|
||||
### Stack Overflows
|
||||
Since we still use the small 64 byte [stack from the last post], we must be careful not to [overflow] it. Normally, Rust tries to avoid stack overflows through _guard pages_: The page below the stack isn't mapped and such a stack overflow triggers a page fault (instead of silently overwriting random memory). But we can't unmap the page below our stack right now since we currently use only a single big page. Fortunately the stack is located just above the page tables. So some important page table entry would probably get overwritten on stack overflow and then a page fault occurs, too.
|
||||
|
||||
[stack from the last post]: @/edition-1/posts/02-entering-longmode/index.md#creating-a-stack
|
||||
[overflow]: https://en.wikipedia.org/wiki/Stack_overflow
|
||||
|
||||
## What's next?
|
||||
Until now we write magic bits to some memory location when we want to print something to screen. In the [next post] we create a abstraction for the VGA text buffer that allows us to print strings in different colors and provides a simple interface.
|
||||
|
||||
[next post]: @/edition-1/posts/04-printing-to-screen/index.md
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
|
||||
<svg width="17cm" height="11cm" viewBox="-60 -21 340 212" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
|
||||
<tspan x="90" y="65.9389"></tspan>
|
||||
</text>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="70" x2="176.236" y2="70"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,65 175.118,70 185.118,75 "/>
|
||||
</g>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="72.75">
|
||||
<tspan x="208" y="72.75">Old Stack Pointer</tspan>
|
||||
</text>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
|
||||
<tspan x="75" y="230"></tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="190" x2="-4" y2="190"/>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="187.764"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,178.882 -12,188.882 -7,178.882 "/>
|
||||
</g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="170" y1="-20" x2="170" y2="0"/>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="0" y="0" width="170" height="20"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="170" height="20"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="13.125">
|
||||
<tspan x="85" y="13.125">Return Address</tspan>
|
||||
</text>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
|
||||
<tspan x="84" y="4"></tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #dddddd" x="0" y="20" width="170" height="20"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="170" height="20"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="33.125">
|
||||
<tspan x="85" y="33.125">Local Variable 1</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #cccccc" x="0" y="40" width="170" height="32"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="170" height="32"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="59.125">
|
||||
<tspan x="85" y="59.125">Local Variables 2..n</tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="170" y2="40"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
|
||||
<g>
|
||||
<rect style="fill: #ff3333" x="0" y="70" width="170" height="120"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="170" height="120"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="40" y="179.125">
|
||||
<tspan x="40" y="179.125">Red Zone</tspan>
|
||||
</text>
|
||||
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="132.75">
|
||||
<tspan x="-20" y="132.75">128 bytes</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #ffc200" x="30" y="70" width="140" height="30"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="70" width="140" height="30"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="88.125">
|
||||
<tspan x="100" y="88.125">Exception Stack Frame</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #ffe000" x="30" y="100" width="140" height="30"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="100" width="140" height="30"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="118.125">
|
||||
<tspan x="100" y="118.125">Register Backup</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #c6db97" x="30" y="130" width="140" height="30"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="130" width="140" height="30"/>
|
||||
</g>
|
||||
<text font-size="8.46654" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="147.925">
|
||||
<tspan x="100" y="147.925">Handler Function Stack Frame</tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="70" x2="30" y2="160"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="160" x2="170" y2="160"/>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="160" x2="176.236" y2="160"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,155 175.118,160 185.118,165 "/>
|
||||
</g>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="162.635">
|
||||
<tspan x="208" y="162.635">New Stack Pointer</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
62
blog/content/edition-1/posts/03-set-up-rust/red-zone.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
|
||||
<svg width="14cm" height="9cm" viewBox="-60 -21 270 172" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
|
||||
<tspan x="90" y="65.9389"></tspan>
|
||||
</text>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="154" y1="70" x2="126.236" y2="70"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="135.118,65 125.118,70 135.118,75 "/>
|
||||
</g>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="158" y="72.75">
|
||||
<tspan x="158" y="72.75">Stack Pointer</tspan>
|
||||
</text>
|
||||
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
|
||||
<tspan x="75" y="230"></tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="150" x2="-4" y2="150"/>
|
||||
<g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="147.764"/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
|
||||
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,138.882 -12,148.882 -7,138.882 "/>
|
||||
</g>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="120" y1="-20" x2="120" y2="0"/>
|
||||
<g>
|
||||
<rect style="fill: #00ff00" x="0" y="0" width="120" height="20"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="120" height="20"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="13.125">
|
||||
<tspan x="60" y="13.125">Return Address</tspan>
|
||||
</text>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
|
||||
<tspan x="84" y="4"></tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #dddddd" x="0" y="20" width="120" height="20"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="120" height="20"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="33.125">
|
||||
<tspan x="60" y="33.125">Local Variable 1</tspan>
|
||||
</text>
|
||||
<g>
|
||||
<rect style="fill: #cccccc" x="0" y="40" width="120" height="32"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="120" height="32"/>
|
||||
</g>
|
||||
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="59.125">
|
||||
<tspan x="60" y="59.125">Local Variables 2..n</tspan>
|
||||
</text>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="120" y2="40"/>
|
||||
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
|
||||
<g>
|
||||
<rect style="fill: #ff3333" x="0" y="70" width="120" height="80"/>
|
||||
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="120" height="80"/>
|
||||
</g>
|
||||
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="113.125">
|
||||
<tspan x="60" y="113.125">Red Zone</tspan>
|
||||
</text>
|
||||
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="112.75">
|
||||
<tspan x="-20" y="112.75">128 bytes</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
@@ -1,25 +1,27 @@
|
||||
+++
|
||||
title = "Printing to Screen"
|
||||
date = "2015-10-23"
|
||||
aliases = [
|
||||
"/2015/10/23/printing-to-screen/",
|
||||
"/rust-os/printing-to-screen.html",
|
||||
]
|
||||
weight = 4
|
||||
path = "printing-to-screen"
|
||||
aliases = ["printing-to-screen.html", "/2015/10/23/printing-to-screen/", "/rust-os/printing-to-screen.html"]
|
||||
date = 2015-10-23
|
||||
template = "edition-1/page.html"
|
||||
[extra]
|
||||
updated = "2016-10-31"
|
||||
+++
|
||||
|
||||
In the [previous post] we switched from assembly to [Rust], a systems programming language that provides great safety. But so far we are using unsafe features like [raw pointers] whenever we want to print to screen. In this post we will create a Rust module that provides a safe and easy-to-use interface for the VGA text buffer. It will support Rust's [formatting macros], too.
|
||||
|
||||
[previous post]: {{% relref "2015-09-02-set-up-rust.md" %}}
|
||||
[previous post]: @/edition-1/posts/03-set-up-rust/index.md
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
[raw pointers]: https://doc.rust-lang.org/book/raw-pointers.html
|
||||
[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
|
||||
|
||||
<!--more-->
|
||||
<!-- more -->
|
||||
|
||||
This post uses recent unstable features, so you need an up-to-date nighly compiler. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is also available on [Github][code repository].
|
||||
This post uses recent unstable features, so you need an up-to-date nighly compiler. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is also available on [GitHub][code repository].
|
||||
|
||||
[file an issue]: https://github.com/phil-opp/blog_os/issues
|
||||
[code repository]: https://github.com/phil-opp/blog_os/tree/printing_to_screen
|
||||
[code repository]: https://github.com/phil-opp/blog_os/tree/first_edition_post_4
|
||||
|
||||
## The VGA Text Buffer
|
||||
The text buffer starts at physical address `0xb8000` and contains the characters displayed on screen. It has 25 rows and 80 columns. Each screen character has the following format:
|
||||
@@ -87,13 +89,11 @@ pub enum Color {
|
||||
```
|
||||
We use a [C-like enum] here to explicitly specify the number for each color. Because of the `repr(u8)` attribute each enum variant is stored as an `u8`. Actually 4 bits would be sufficient, but Rust doesn't have an `u4` type.
|
||||
|
||||
[C-like enum]: http://rustbyexample.com/custom_types/enum/c_like.html
|
||||
[C-like enum]: https://doc.rust-lang.org/rust-by-example/custom_types/enum/c_like.html
|
||||
|
||||
Normally the compiler would issue a warning for each unused variant. By using the `#[allow(dead_code)]` attribute we disable these warnings for the `Color` enum.
|
||||
|
||||
To represent a full color code that specifies foreground and background color, we create a [newtype] on top of `u8`:
|
||||
|
||||
[newtype]: https://aturon.github.io/features/types/newtype.html
|
||||
To represent a full color code that specifies foreground and background color, we create a newtype on top of `u8`:
|
||||
|
||||
```rust
|
||||
struct ColorCode(u8);
|
||||
@@ -142,14 +142,14 @@ pub struct Writer {
|
||||
```
|
||||
The writer will always write to the last line and shift lines up when a line is full (or on `\n`). The `column_position` field keeps track of the current position in the last row. The current foreground and background colors are specified by `color_code` and a pointer to the VGA buffer is stored in `buffer`. To make it possible to create a `static` Writer later, the `buffer` field stores an `Unique<Buffer>` instead of a plain `*mut Buffer`. [Unique] is a wrapper that implements Send/Sync and is thus usable as a `static`. Since it's unstable, you may need to add the `unique` feature to `lib.rs`:
|
||||
|
||||
[Unique]: https://doc.rust-lang.org/nightly/core/ptr/struct.Unique.html
|
||||
[Unique]: https://doc.rust-lang.org/1.10.0/core/ptr/struct.Unique.html
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
#![feature(unique)]
|
||||
```
|
||||
|
||||
## Printing to Screen
|
||||
## Printing Characters
|
||||
Now we can use the `Writer` to modify the buffer's characters. First we create a method to write a single ASCII byte (it doesn't compile yet):
|
||||
|
||||
```rust
|
||||
@@ -165,9 +165,10 @@ impl Writer {
|
||||
let row = BUFFER_HEIGHT - 1;
|
||||
let col = self.column_position;
|
||||
|
||||
let color_code = self.color_code;
|
||||
self.buffer().chars[row][col] = ScreenChar {
|
||||
ascii_character: byte,
|
||||
color_code: self.color_code,
|
||||
color_code: color_code,
|
||||
};
|
||||
self.column_position += 1;
|
||||
}
|
||||
@@ -175,7 +176,7 @@ impl Writer {
|
||||
}
|
||||
|
||||
fn buffer(&mut self) -> &mut Buffer {
|
||||
unsafe{ self.buffer.get_mut() }
|
||||
unsafe{ self.buffer.as_mut() }
|
||||
}
|
||||
|
||||
fn new_line(&mut self) {/* TODO */}
|
||||
@@ -187,20 +188,56 @@ If the byte is the [newline] byte `\n`, the writer does not print anything. Inst
|
||||
|
||||
When printing a byte, the writer checks if the current line is full. In that case, a `new_line` call is required before to wrap the line. Then it writes a new `ScreenChar` to the buffer at the current position. Finally, the current column position is advanced.
|
||||
|
||||
The `buffer()` auxiliary method converts the raw pointer in the `buffer` field into a safe mutable buffer reference. The unsafe block is needed because the [get_mut()] method of `Unique` is unsafe. But our `buffer()` method itself isn't marked as unsafe, so it must not introduce any unsafety (e.g. cause segfaults). To guarantee that, it's very important that the `buffer` field always points to a valid `Buffer`. It's like a contract that we must stand to every time we create a `Writer`. To ensure that it's not possible to create an invalid `Writer` from outside of the module, the struct must have at least one private field and public creation functions are not allowed either.
|
||||
[get_mut()]: https://doc.rust-lang.org/nightly/core/ptr/struct.Unique.html#method.get_mut
|
||||
The `buffer()` auxiliary method converts the raw pointer in the `buffer` field into a safe mutable buffer reference. The unsafe block is needed because the [as_mut()] method of `Unique` is unsafe. But our `buffer()` method itself isn't marked as unsafe, so it must not introduce any unsafety (e.g. cause segfaults). To guarantee that, it's very important that the `buffer` field always points to a valid `Buffer`. It's like a contract that we must stand to every time we create a `Writer`. To ensure that it's not possible to create an invalid `Writer` from outside of the module, the struct must have at least one private field and public creation functions are not allowed either.
|
||||
|
||||
[as_mut()]: https://doc.rust-lang.org/1.26.0/core/ptr/struct.Unique.html#method.as_mut
|
||||
|
||||
### Cannot Move out of Borrowed Content
|
||||
When we try to compile it, we get the following error:
|
||||
|
||||
```
|
||||
error: cannot move out of borrowed content [E0507]
|
||||
color_code: self.color_code,
|
||||
^~~~
|
||||
error[E0507]: cannot move out of borrowed content
|
||||
--> src/vga_buffer.rs:79:34
|
||||
|
|
||||
79 | let color_code = self.color_code;
|
||||
| ^^^^ cannot move out of borrowed content
|
||||
```
|
||||
The reason it that Rust _moves_ values by default instead of copying them like other languages. And we cannot move `color_code` out of `self` because we only borrowed `self`. For more information check out the [ownership section] in the Rust book. To fix it, we can implement the [Copy trait] for the `ColorCode` type by adding `#[derive(Clone, Copy)]` to its struct.
|
||||
The reason it that Rust _moves_ values by default instead of copying them like other languages. And we cannot move `color_code` out of `self` because we only borrowed `self`. For more information check out the [ownership section] in the Rust book.
|
||||
|
||||
[ownership section]: https://doc.rust-lang.org/book/ownership.html
|
||||
[Copy trait]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
|
||||
[by reference]: https://rust-lang.github.io/book/ch04-02-references-and-borrowing.html
|
||||
|
||||
To fix it, we can implement the [Copy] trait for the `ColorCode` type. The easiest way to do this is to use the built-in [derive macro]:
|
||||
|
||||
[Copy]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
|
||||
[derive macro]: https://doc.rust-lang.org/rust-by-example/custom_types/enum/c_like.html
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ColorCode(u8);
|
||||
```
|
||||
|
||||
We also derive the [Clone] trait, since it's a requirement for `Copy`, and the [Debug] trait, which allows us to print this field for debugging purposes.
|
||||
|
||||
[Clone]: https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html
|
||||
[Debug]: https://doc.rust-lang.org/nightly/core/fmt/trait.Debug.html
|
||||
|
||||
Now our project should compile again.
|
||||
|
||||
However, the [documentation for Copy] says: _“if your type can implement Copy, it should”_. Therefore we also derive Copy for `Color` and `ScreenChar`:
|
||||
|
||||
[documentation for Copy]: https://doc.rust-lang.org/core/marker/trait.Copy.html#when-should-my-type-be-copy
|
||||
|
||||
```rust
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum Color {...}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
struct ScreenChar {...}
|
||||
```
|
||||
|
||||
### Try it out!
|
||||
To write some characters to the screen, you can create a temporary function:
|
||||
@@ -210,17 +247,97 @@ pub fn print_something() {
|
||||
let mut writer = Writer {
|
||||
column_position: 0,
|
||||
color_code: ColorCode::new(Color::LightGreen, Color::Black),
|
||||
buffer: unsafe { Unique::new(0xb8000 as *mut _) },
|
||||
buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) },
|
||||
};
|
||||
|
||||
writer.write_byte(b'H');
|
||||
}
|
||||
```
|
||||
It just creates a new Writer that points to the VGA buffer at `0xb8000`. Then it writes the byte `b'H'` to it. The `b` prefix creates a [byte character], which represents an ASCII code point. When we call `vga_buffer::print_something` in main, a `H` should be printed in the _lower_ left corner of the screen in light green.
|
||||
It just creates a new Writer that points to the VGA buffer at `0xb8000`. To use the unstable `Unique::new_unchecked` function, we need to add the feature flag `#![feature(const_unique_new)]` to the top of our `src/lib.rs`.
|
||||
|
||||
[byte character]: https://doc.rust-lang.org/reference.html#characters-and-strings
|
||||
Then it writes the byte `b'H'` to it. The `b` prefix creates a [byte character], which represents an ASCII code point. When we call `vga_buffer::print_something` in main, a `H` should be printed in the _lower_ left corner of the screen in light green:
|
||||
|
||||
### Printing Strings
|
||||
[byte character]: https://doc.rust-lang.org/reference/tokens.html#characters-and-strings
|
||||
|
||||

|
||||
|
||||
### Volatile
|
||||
We just saw that our `H` was printed correctly. However, it might not work with future Rust compilers that optimize more aggressively.
|
||||
|
||||
The problem is that we only write to the `Buffer` and never read from it again. The compiler doesn't know about the side effect that some characters appear on the screen. So it might decide that these writes are unnecessary and can be omitted.
|
||||
|
||||
To avoid this erroneous optimization, we need to specify these writes as _[volatile]_. This tells the compiler that the write has side effects and should not be optimized away.
|
||||
|
||||
[volatile]: https://en.wikipedia.org/wiki/Volatile_(computer_programming)
|
||||
|
||||
In order to use volatile writes for the VGA buffer, we use the [volatile][volatile crate] library. This _crate_ (this is how packages are called in the Rust world) provides a `Volatile` wrapper type with `read` and `write` methods. These methods internally use the [read_volatile] and [write_volatile] functions of the standard library and thus guarantee that the reads/writes are not optimized away.
|
||||
|
||||
[volatile crate]: https://docs.rs/volatile
|
||||
[read_volatile]: https://doc.rust-lang.org/nightly/core/ptr/fn.read_volatile.html
|
||||
[write_volatile]: https://doc.rust-lang.org/nightly/core/ptr/fn.write_volatile.html
|
||||
|
||||
We can add a dependency on the `volatile` crate by adding it to the `dependencies` section of our `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
[dependencies]
|
||||
volatile = "0.1.0"
|
||||
```
|
||||
|
||||
The `0.1.0` is the [semantic] version number. For more information, see the [Specifying Dependencies] guide of the cargo documentation.
|
||||
|
||||
[semantic]: https://semver.org/
|
||||
[Specifying Dependencies]: https://doc.crates.io/specifying-dependencies.html
|
||||
|
||||
Now we've declared that our project depends on the `volatile` crate and are able to import it in `src/lib.rs`:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
extern crate volatile;
|
||||
```
|
||||
|
||||
Let's use it to make writes to the VGA buffer volatile. We update our `Buffer` type as follows:
|
||||
|
||||
```rust
|
||||
// in src/vga_buffer.rs
|
||||
|
||||
use volatile::Volatile;
|
||||
|
||||
struct Buffer {
|
||||
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
}
|
||||
```
|
||||
Instead of a `ScreenChar`, we're now using a `Volatile<ScreenChar>`. (The `Volatile` type is [generic] and can wrap (almost) any type). This ensures that we can't accidentally write to it through a “normal” write. Instead, we have to use the `write` method now.
|
||||
|
||||
[generic]: https://doc.rust-lang.org/book/second-edition/ch10-00-generics.html
|
||||
|
||||
This means that we have to update our `Writer::write_byte` method:
|
||||
|
||||
```rust
|
||||
impl Writer {
|
||||
pub fn write_byte(&mut self, byte: u8) {
|
||||
match byte {
|
||||
b'\n' => self.new_line(),
|
||||
byte => {
|
||||
...
|
||||
|
||||
self.buffer().chars[row][col].write(ScreenChar {
|
||||
ascii_character: byte,
|
||||
color_code: color_code,
|
||||
});
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Instead of a normal assignment using `=`, we're now using the `write` method. This guarantees that the compiler will never optimize away this write.
|
||||
|
||||
## Printing Strings
|
||||
|
||||
To print whole strings, we can convert them to bytes and print them one-by-one:
|
||||
|
||||
@@ -237,14 +354,16 @@ You can try it yourself in the `print_something` function.
|
||||
When you print strings with some special characters like `ä` or `λ`, you'll notice that they cause weird symbols on screen. That's because they are represented by multiple bytes in [UTF-8]. By converting them to bytes, we of course get strange results. But since the VGA buffer doesn't support UTF-8, it's not possible to display these characters anyway.
|
||||
|
||||
[core tracking issue]: https://github.com/rust-lang/rust/issues/27701
|
||||
[UTF-8]: http://www.fileformat.info/info/unicode/utf8.htm
|
||||
[UTF-8]: https://www.fileformat.info/info/unicode/utf8.htm
|
||||
|
||||
### Support Formatting Macros
|
||||
It would be nice to support Rust's formatting macros, too. That way, we can easily print different types like integers or floats. To support them, we need to implement the [core::fmt::Write] trait. The only required method of this trait is `write_str` that looks quite similar to our `write_str` method. To implement the trait, we just need to move it into an `impl ::core::fmt::Write for Writer` block and add a return type:
|
||||
It would be nice to support Rust's formatting macros, too. That way, we can easily print different types like integers or floats. To support them, we need to implement the [core::fmt::Write] trait. The only required method of this trait is `write_str` that looks quite similar to our `write_str` method. To implement the trait, we just need to move it into an `impl fmt::Write for Writer` block and add a return type:
|
||||
|
||||
```rust
|
||||
impl ::core::fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
|
||||
use core::fmt;
|
||||
|
||||
impl fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for byte in s.bytes() {
|
||||
self.write_byte(byte)
|
||||
}
|
||||
@@ -264,7 +383,8 @@ writer.write_byte(b'H');
|
||||
writer.write_str("ello! ");
|
||||
write!(writer, "The numbers are {} and {}", 42, 1.0/3.0);
|
||||
```
|
||||
Now you should see a `Hello! The numbers are 42 and 0.3333333333333333` in strange colors at the bottom of the screen.
|
||||
|
||||
Now you should see a `Hello! The numbers are 42 and 0.3333333333333333` at the bottom of the screen.
|
||||
|
||||
[core::fmt::Write]: https://doc.rust-lang.org/nightly/core/fmt/trait.Write.html
|
||||
|
||||
@@ -275,9 +395,12 @@ Right now, we just ignore newlines and characters that don't fit into the line a
|
||||
// in `impl Writer`
|
||||
|
||||
fn new_line(&mut self) {
|
||||
for row in 0..(BUFFER_HEIGHT-1) {
|
||||
let buffer = self.buffer();
|
||||
buffer.chars[row] = buffer.chars[row + 1]
|
||||
for row in 1..BUFFER_HEIGHT {
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
let buffer = self.buffer();
|
||||
let character = buffer.chars[row][col].read();
|
||||
buffer.chars[row - 1][col].write(character);
|
||||
}
|
||||
}
|
||||
self.clear_row(BUFFER_HEIGHT-1);
|
||||
self.column_position = 0;
|
||||
@@ -285,14 +408,7 @@ fn new_line(&mut self) {
|
||||
|
||||
fn clear_row(&mut self, row: usize) {/* TODO */}
|
||||
```
|
||||
We just move each line to the line above. Notice that the range notation (`..`) is exclusive the upper bound. But when we try to compile it, we get an borrow checker error again:
|
||||
|
||||
```
|
||||
error: cannot move out of indexed content [E0507]
|
||||
buffer.chars[row] = buffer.chars[row + 1]
|
||||
^~~~~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
It's because of Rust's move semantics again: We try to move out the `ScreenChar` at `row + 1`. If Rust would allow that, the array would become invalid as it would contain some valid and some moved out values. Fortunately, the `ScreenChar` type meets all criteria for the [Copy trait], so we can fix the problem by adding `#[derive(Clone, Copy)]` to `ScreenChar`.
|
||||
We iterate over all screen characters and move each characters one row up. Note that the range notation (`..`) is exclusive the upper bound. We also omit the 0th row (the first range starts at `1`) because it's the row that is shifted off screen.
|
||||
|
||||
Now we only need to implement the `clear_row` method to finish the newline code:
|
||||
|
||||
@@ -303,9 +419,12 @@ fn clear_row(&mut self, row: usize) {
|
||||
ascii_character: b' ',
|
||||
color_code: self.color_code,
|
||||
};
|
||||
self.buffer().chars[row] = [blank; BUFFER_WIDTH];
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
self.buffer().chars[row][col].write(blank);
|
||||
}
|
||||
}
|
||||
```
|
||||
This method clears a row by overwriting all of its characters with a space character.
|
||||
|
||||
## Providing an Interface
|
||||
To provide a global writer that can used as an interface from other modules, we can add a `static` writer:
|
||||
@@ -314,7 +433,7 @@ To provide a global writer that can used as an interface from other modules, we
|
||||
pub static WRITER: Writer = Writer {
|
||||
column_position: 0,
|
||||
color_code: ColorCode::new(Color::LightGreen, Color::Black),
|
||||
buffer: unsafe { Unique::new(0xb8000 as *mut _) },
|
||||
buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) },
|
||||
};
|
||||
```
|
||||
|
||||
@@ -322,14 +441,14 @@ But we can't use it to print anything! You can try it yourself in the `print_som
|
||||
|
||||
To resolve it, we could use a [mutable static]. But then every read and write to it would be unsafe since it could easily introduce data races and other bad things. Using `static mut` is highly discouraged, there are even proposals to [remove it][remove static mut].
|
||||
|
||||
[mutable static]: https://doc.rust-lang.org/book/const-and-static.html#mutability
|
||||
[mutable static]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
|
||||
[remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437
|
||||
|
||||
But what are the alternatives? We could try to use a cell type like [RefCell] or even [UnsafeCell] to provide [interior mutability]. But these types aren't [Sync] \(with good reason), so we can't use them in statics.
|
||||
|
||||
[RefCell]: https://doc.rust-lang.org/nightly/core/cell/struct.RefCell.html
|
||||
[UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html
|
||||
[interior mutability]: https://doc.rust-lang.org/book/mutability.html#interior-vs.-exterior-mutability
|
||||
[interior mutability]: https://doc.rust-lang.org/1.30.0/book/first-edition/mutability.html#interior-vs-exterior-mutability
|
||||
[Sync]: https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html
|
||||
|
||||
To get synchronized interior mutability, users of the standard library can use [Mutex]. It provides mutual exclusion by blocking threads when the resource is already locked. But our basic kernel does not have any blocking support or even a concept of threads, so we can't use it either. However there is a really basic kind of mutex in computer science that requires no operating system features: the [spinlock]. Instead of blocking, the threads simply try to lock it again and again in a tight loop and thus burn CPU time until the mutex is free again.
|
||||
@@ -345,7 +464,7 @@ To use a spinning mutex, we can add the [spin crate] as a dependency:
|
||||
# in Cargo.toml
|
||||
[dependencies]
|
||||
rlibc = "0.1.4"
|
||||
spin = "0.3.4"
|
||||
spin = "0.4.5"
|
||||
```
|
||||
|
||||
```rust
|
||||
@@ -362,14 +481,14 @@ use spin::Mutex;
|
||||
pub static WRITER: Mutex<Writer> = Mutex::new(Writer {
|
||||
column_position: 0,
|
||||
color_code: ColorCode::new(Color::LightGreen, Color::Black),
|
||||
buffer: unsafe { Unique::new(0xb8000 as *mut _) },
|
||||
buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) },
|
||||
});
|
||||
```
|
||||
[Mutex::new] is a const function, too, so it can be used in statics.
|
||||
|
||||
Now we can easily print from our main function:
|
||||
|
||||
[Mutex::new]: https://mvdnes.github.io/rust-docs/spinlock-rs/spin/struct.Mutex.html#method.new
|
||||
[Mutex::new]: https://docs.rs/spin/0.4.5/spin/struct.Mutex.html#method.new
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
@@ -385,7 +504,7 @@ Note that we need to import the `Write` trait if we want to use its functions.
|
||||
## A println macro
|
||||
Rust's [macro syntax] is a bit strange, so we won't try to write a macro from scratch. Instead we look at the source of the [`println!` macro] in the standard library:
|
||||
|
||||
[macro syntax]: https://doc.rust-lang.org/nightly/book/macros.html
|
||||
[macro syntax]: https://doc.rust-lang.org/nightly/book/second-edition/appendix-04-macros.html
|
||||
[`println!` macro]: https://doc.rust-lang.org/nightly/std/macro.println!.html
|
||||
|
||||
```rust
|
||||
@@ -394,7 +513,9 @@ macro_rules! println {
|
||||
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
|
||||
}
|
||||
```
|
||||
It just adds a `\n` and then invokes the [`print!` macro], which is defined as:
|
||||
Macros are defined through one or more rules, which are similar to `match` arms. The `println` macro has two rules: The first rule is for invocations with a single argument (e.g. `println!("Hello")`) and the second rule is for invocations with additional parameters (e.g. `println!("{}{}", 4, 2)`).
|
||||
|
||||
Both rules simply append a newline character (`\n`) to the format string and then invoke the [`print!` macro], which is defined as:
|
||||
|
||||
[`print!` macro]: https://doc.rust-lang.org/nightly/std/macro.print!.html
|
||||
|
||||
@@ -403,9 +524,14 @@ macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
|
||||
}
|
||||
```
|
||||
It calls the `_print` method in the `io` module of the current crate (`$crate`), which is `std`. The [`_print` function] in libstd is rather complicated, as it supports different `Stdout` devices.
|
||||
The macro expands to a call of the [`_print` function] in the `io` module. The [`$crate` variable] ensures that the macro also works from outside the `std` crate. For example, it expands to `::std` when it's used in other crates.
|
||||
|
||||
[`_print` function]: https://doc.rust-lang.org/nightly/src/std/io/stdio.rs.html#578
|
||||
The [`format_args` macro] builds a [fmt::Arguments] type from the passed arguments, which is passed to `_print`. The [`_print` function] of libstd is rather complicated, as it supports different `Stdout` devices. We don't need that complexity since we just want to print to the VGA buffer.
|
||||
|
||||
[`_print` function]: https://github.com/rust-lang/rust/blob/46d39f3329487115e7d7dcd37bc64eea6ef9ba4e/src/libstd/io/stdio.rs#L631
|
||||
[`$crate` variable]: https://doc.rust-lang.org/1.30.0/book/first-edition/macros.html#the-variable-crate
|
||||
[`format_args` macro]: https://doc.rust-lang.org/nightly/std/macro.format_args.html
|
||||
[fmt::Arguments]: https://doc.rust-lang.org/nightly/core/fmt/struct.Arguments.html
|
||||
|
||||
To print to the VGA buffer, we just copy the `println!` macro and modify the `print!` macro to use our static `WRITER` instead of `_print`:
|
||||
|
||||
@@ -413,15 +539,15 @@ To print to the VGA buffer, we just copy the `println!` macro and modify the `pr
|
||||
// in src/vga_buffer.rs
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ({
|
||||
use core::fmt::Write;
|
||||
let mut writer = $crate::vga_buffer::WRITER.lock();
|
||||
writer.write_fmt(format_args!($($arg)*)).unwrap();
|
||||
use core::fmt::Write;
|
||||
let mut writer = $crate::vga_buffer::WRITER.lock();
|
||||
writer.write_fmt(format_args!($($arg)*)).unwrap();
|
||||
});
|
||||
}
|
||||
```
|
||||
Instead of a `_print` function, we call the `write_fmt` method of our static `Writer`. Since we're using a method from the `Write` trait, we need to import it before. The additional `unwrap()` at the end panics if printing isn't successful. But since we always return `Ok` in `write_str`, that should not happen.
|
||||
|
||||
Note the additional `{}` scope around the macro: I wrote `=> ({…})` instead of `=> (…)`. The additional `{}` avoids that the `Write` trait is silently imported when `print` is used.
|
||||
Note the additional `{}` scope around the macro: We write `=> ({…})` instead of `=> (…)`. The additional `{}` avoids that the `Write` trait is silently imported to the parent scope when `print` is used.
|
||||
|
||||
### Clearing the screen
|
||||
We can now use `println!` to add a rather trivial function to clear the screen:
|
||||
@@ -452,31 +578,91 @@ pub extern fn rust_main() {
|
||||
loop{}
|
||||
}
|
||||
```
|
||||
|
||||
Since we imported the macros at crate level, they are available in all modules and thus provide an easy and safe interface to the VGA buffer.
|
||||
|
||||
As expected, we now see a _“Hello World!”_ on a cleared screen:
|
||||
|
||||

|
||||
|
||||
### Deadlocks
|
||||
Whenever we use locks, we must be careful to not accidentally introduce _deadlocks_. A [deadlock] occurs when a thread/program waits for a lock that will never be released. Normally, this happens when multiple threads access multiple locks. For example, when thread A holds lock 1 and tries to acquire lock 2 and -- at the same time -- thread B holds lock 2 and tries to acquire lock 1.
|
||||
|
||||
[deadlock]: https://en.wikipedia.org/wiki/Deadlock
|
||||
|
||||
However, a deadlock can also occur when a thread tries to acquire the same lock twice. This way we can trigger a deadlock in our VGA driver:
|
||||
|
||||
```rust
|
||||
// in rust_main in src/lib.rs
|
||||
|
||||
println!("{}", { println!("inner"); "outer" });
|
||||
```
|
||||
The argument passed to `println` is new block that resolves to the string _“outer”_ (a block always returns the result of the last expression). But before returning “outer”, the block tries to print the string _“inner”_.
|
||||
|
||||
When we try this code in QEMU, we see that neither of the strings are printed. To understand what's happening, we take a look at our `print` macro again:
|
||||
|
||||
```rust
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ({
|
||||
use core::fmt::Write;
|
||||
let mut writer = $crate::vga_buffer::WRITER.lock();
|
||||
writer.write_fmt(format_args!($($arg)*)).unwrap();
|
||||
});
|
||||
}
|
||||
```
|
||||
So we _first_ lock the `WRITER` and then we evaluate the arguments using `format_args`. The problem is that the argument in our code example contains another `println`, which tries to lock the `WRITER` again. So now the inner `println` waits for the outer `println` and vice versa. Thus, a deadlock occurs and the CPU spins endlessly.
|
||||
|
||||
### Fixing the Deadlock
|
||||
In order to fix the deadlock, we need to evaluate the arguments _before_ locking the `WRITER`. We can do so by moving the locking and printing logic into a new `print` function (like it's done in the standard library):
|
||||
|
||||
```rust
|
||||
// in src/vga_buffer.rs
|
||||
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ({
|
||||
$crate::vga_buffer::print(format_args!($($arg)*));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
WRITER.lock().write_fmt(args).unwrap();
|
||||
}
|
||||
```
|
||||
Now the macro only evaluates the arguments (through `format_args!`) and passes them to the new `print` function. The `print` function then locks the `WRITER` and prints the formatting arguments using `write_fmt`. So now the arguments are evaluated before locking the `WRITER`.
|
||||
|
||||
Thus, we fixed the deadlock:
|
||||
|
||||

|
||||
|
||||
We see that both “inner” and “outer” are printed.
|
||||
|
||||
## What's next?
|
||||
In the next posts we will map the kernel pages correctly so that accessing `0x0` or writing to `.rodata` is not possible anymore. To obtain the loaded kernel sections we will read the Multiboot information structure. Then we will create a paging module and use it to switch to a new page table where the kernel sections are mapped correctly.
|
||||
|
||||
The [next post] describes the Multiboot information structure and creates a frame allocator using the information about memory areas.
|
||||
|
||||
[next post]: {{% relref "2015-11-15-allocating-frames.md" %}}
|
||||
[next post]: @/edition-1/posts/05-allocating-frames/index.md
|
||||
|
||||
## Other Rust OS Projects
|
||||
Now that you know the very basics of OS development in Rust, you should also check out the following projects:
|
||||
|
||||
- [Rust Bare-Bones Kernel]: A basic kernel with roughly the same functionality as ours. Writes output to the serial port instead of the VGA buffer and maps the kernel to the [higher half] \(instead of our identity mapping).
|
||||
_Note_: You need to [cross compile binutils] to build it (or you create some symbolic links[^fn-symlink] if you're on x86_64).
|
||||
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
|
||||
[higher half]: http://wiki.osdev.org/Higher_Half_Kernel
|
||||
[cross compile binutils]: {{% relref "cross-compile-binutils.md" %}}
|
||||
[^fn-symlink]: You will need to symlink `x86_64-none_elf-XXX` to `/usr/bin/XXX` where `XXX` is in {`as`, `ld`, `objcopy`, `objdump`, `strip`}. The `x86_64-none_elf-XXX` files must be in some folder that is in your `$PATH`. But then you can only build for your x86_64 host architecture, so use this hack only for testing.
|
||||
|
||||
- [RustOS]: More advanced kernel that supports allocation, keyboard inputs, and threads. It also has a scheduler and a basic network driver.
|
||||
[RustOS]: https://github.com/RustOS-Fork-Holding-Ground/RustOS
|
||||
|
||||
- ["Tifflin" Experimental Kernel]: Big kernel project by thepowersgang, that is actively developed and has over 650 commits. It has a separate userspace and supports multiple file systems, even a GUI is included. Needs a cross compiler.
|
||||
["Tifflin" Experimental Kernel]:https://github.com/thepowersgang/rust_os
|
||||
|
||||
- [Redox]: Probably the most complete Rust OS today. It has an active community and over 1000 Github stars. File systems, network, an audio player, a picture viewer, and much more. Just take a look at the [screenshots][redox screenshots].
|
||||
|
||||
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
|
||||
[higher half]: https://wiki.osdev.org/Higher_Half_Kernel
|
||||
[cross compile binutils]: @/edition-1/extra/cross-compile-binutils.md
|
||||
[RustOS]: https://github.com/RustOS-Fork-Holding-Ground/RustOS
|
||||
["Tifflin" Experimental Kernel]:https://github.com/thepowersgang/rust_os
|
||||
[Redox]: https://github.com/redox-os/redox
|
||||
[redox screenshots]: https://github.com/redox-os/redox#what-it-looks-like
|
||||
|
||||
## Footnotes
|
||||
[^fn-symlink]: You will need to symlink `x86_64-none_elf-XXX` to `/usr/bin/XXX` where `XXX` is in {`as`, `ld`, `objcopy`, `objdump`, `strip`}. The `x86_64-none_elf-XXX` files must be in some folder that is in your `$PATH`. But then you can only build for your x86_64 host architecture, so use this hack only for testing.
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@@ -1,24 +1,28 @@
|
||||
+++
|
||||
title = "Allocating Frames"
|
||||
date = "2015-11-15"
|
||||
weight = 5
|
||||
path = "allocating-frames"
|
||||
aliases = ["allocating-frames.html"]
|
||||
date = 2015-11-15
|
||||
template = "edition-1/page.html"
|
||||
+++
|
||||
|
||||
In this post we create an allocator that provides free physical frames for a future paging module. To get the required information about available and used memory we use the Multiboot information structure. Additionally, we improve the `panic` handler to print the corresponding message and source line.
|
||||
|
||||
<!--more-->
|
||||
<!-- more -->
|
||||
|
||||
The full source code is available on [Github][source repo]. Feel free to open issues there if you have any problems or improvements. You can also leave a comment at the bottom.
|
||||
The full source code is available on [GitHub][source repo]. Feel free to open issues there if you have any problems or improvements. You can also leave a comment at the bottom.
|
||||
|
||||
[source repo]: https://github.com/phil-opp/blog_os/tree/allocating_frames
|
||||
[source repo]: https://github.com/phil-opp/blog_os/tree/first_edition_post_5
|
||||
|
||||
## Preparation
|
||||
We still have a really tiny stack of 64 bytes, which won't suffice for this post. So we increase it to 4096 bytes (one page) in `boot.asm`:
|
||||
We still have a really tiny stack of 64 bytes, which won't suffice for this post. So we increase it to 16kB (four pages) in `boot.asm`:
|
||||
|
||||
```asm
|
||||
section .bss
|
||||
...
|
||||
stack_bottom:
|
||||
resb 4096
|
||||
resb 4096 * 4
|
||||
stack_top:
|
||||
```
|
||||
|
||||
@@ -44,17 +48,17 @@ Now we can add the argument to our `rust_main`:
|
||||
pub extern fn rust_main(multiboot_information_address: usize) { ... }
|
||||
```
|
||||
|
||||
Instead of writing an own Multiboot module, we use the [multiboot2-elf64] crate. It gives us some basic information about mapped kernel sections and available memory. I just wrote it for this blog post since I could not find any other Multiboot 2 crate. It's really ugly and incomplete, but it does its job[^fn-multiboot-crate].
|
||||
Instead of writing an own Multiboot module, we use the [multiboot2] crate. It gives us some basic information about mapped kernel sections and available memory. I just wrote it for this blog post since I could not find any other Multiboot 2 crate. It's still incomplete, but it does its job.
|
||||
|
||||
[multiboot2-elf64]: https://github.com/phil-opp/multiboot2-elf64
|
||||
[^fn-multiboot-crate]: All contributions are welcome! If you want to maintain it, please contact me!
|
||||
[multiboot2]: https://docs.rs/multiboot2
|
||||
|
||||
So let's add a dependency on the git repository:
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
[dependencies.multiboot2]
|
||||
git = "https://github.com/phil-opp/multiboot2-elf64"
|
||||
[dependencies]
|
||||
...
|
||||
multiboot2 = "0.1.0"
|
||||
```
|
||||
|
||||
```rust
|
||||
@@ -67,7 +71,7 @@ Now we can use it to print available memory areas.
|
||||
### Available Memory
|
||||
The boot information structure consists of various _tags_. See section 3.4 of the Multiboot specification ([PDF][multiboot specification]) for a complete list. The _memory map_ tag contains a list of all available RAM areas. Special areas such as the VGA text buffer at `0xb8000` are not available. Note that some of the available memory is already used by our kernel and by the multiboot information structure itself.
|
||||
|
||||
[multiboot specification]: http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||
[multiboot specification]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||
|
||||
To print all available memory areas, we can use the `multiboot2` crate in our `rust_main` as follows:
|
||||
|
||||
@@ -96,14 +100,15 @@ So we have one area from `0x0` to `0x9fc00`, which is a bit below the 1MiB mark.
|
||||
|
||||
If you give QEMU more than 4GiB of memory by passing `-m 5G`, you get another unusable area below the 4GiB mark. This memory is normally mapped to some hardware devices. See the [OSDev Wiki][Memory_map] for more information.
|
||||
|
||||
[Memory_map]: http://wiki.osdev.org/Memory_Map_(x86)
|
||||
[Memory_map]: https://wiki.osdev.org/Memory_Map_(x86)
|
||||
|
||||
### Handling Panics
|
||||
We used `expect` in the code above, which will panic if there is no memory map tag. But our current panic handler just loops without printing any error message. Of course we could replace `expect` by a `match`, but we should fix the panic handler nonetheless:
|
||||
|
||||
```rust
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn panic_fmt() -> ! {
|
||||
#[no_mangle]
|
||||
pub extern fn panic_fmt() -> ! {
|
||||
println!("PANIC");
|
||||
loop{}
|
||||
}
|
||||
@@ -112,7 +117,10 @@ Now we get a `PANIC` message. But we can do even better. The `panic_fmt` functio
|
||||
|
||||
```rust
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn panic_fmt(fmt: core::fmt::Arguments, file: &str, line: u32) -> ! {
|
||||
#[no_mangle]
|
||||
pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str,
|
||||
line: u32) -> !
|
||||
{
|
||||
println!("\n\nPANIC in {} at line {}:", file, line);
|
||||
println!(" {}", fmt);
|
||||
loop{}
|
||||
@@ -142,6 +150,8 @@ But when we execute it, tons of really small sections are printed. We can use th
|
||||
To merge these subsections, we need to update our linker script:
|
||||
|
||||
```
|
||||
ENTRY(start)
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
@@ -169,7 +179,7 @@ These lines are taken from the default linker script of `ld`, which can be obtai
|
||||
|
||||
Now there are only 12 sections left and we get a much more useful output:
|
||||
|
||||

|
||||

|
||||
|
||||
If you like, you can compare this output to the `objdump -h build/kernel-x86_64.bin` output. You will see that the start addresses and sizes match exactly for each section. The sections with flags `0x0` are mostly debug sections, so they don't need to be loaded. And the last few sections of the QEMU output aren't in the `objdump` output because they are special sections such as string tables.
|
||||
|
||||
@@ -206,7 +216,8 @@ There are various ways to write such a frame allocator:
|
||||
We could create some kind of linked list from the free frames. For example, each frame could begin with a pointer to the next free frame. Since the frames are free, this would not overwrite any data. Our allocator would just save the head of the list and could easily allocate and deallocate frames by updating pointers. Unfortunately, this approach has a problem: It requires reading and writing these free frames. So we would need to map all physical frames to some virtual address, at least temporary. Another disadvantage is that we need to create this linked list at startup. That implies that we need to set over one million pointers at startup if the machine has 4GiB of RAM.
|
||||
|
||||
Another approach is to create some kind of data structure such as a [bitmap or a stack] to manage free frames. We could place it in the already identity mapped area right behind the kernel or multiboot structure. That way we would not need to (temporary) map each free frame. But it has the same problem of the slow initial creating/filling. In fact, we will use this approach in a future post to manage frames that are freed again. But for the initial management of free frames, we use a different method.
|
||||
[bitmap or a stack]: http://wiki.osdev.org/Page_Frame_Allocation#Physical_Memory_Allocators
|
||||
|
||||
[bitmap or a stack]: https://wiki.osdev.org/Page_Frame_Allocation#Physical_Memory_Allocators
|
||||
|
||||
In the following, we will use Multiboot's memory map directly. The idea is to maintain a simple counter that starts at frame 0 and is increased constantly. If the current frame is available (part of an available area in the memory map) and not used by the kernel or the multiboot structure (we know their start and end addresses), we know that it's free and return it. Else, we increase the counter to the next possibly free frame. That way, we don't need to create a data structure when booting and the physical frames can remain unmapped. The only problem is that we cannot reasonably free frames again, but we will solve that problem in a future post (by adding an intermediate frame stack that saves freed frames).
|
||||
|
||||
@@ -225,9 +236,6 @@ pub struct Frame {
|
||||
```
|
||||
(Don't forget to add the `mod memory` line to `src/lib.rs`.) Instead of e.g. the start address, we just store the frame number. We use `usize` here since the number of frames depends on the memory size. The long `derive` line makes frames printable and comparable.
|
||||
|
||||
_Update_: In a previous version, the `Clone` and `Copy` traits were derived, too. [This was removed][PR 52] to make the allocator interface safer.
|
||||
[PR 52]: https://github.com/phil-opp/blog_os/pull/52
|
||||
|
||||
To make it easy to get the corresponding frame for a physical address, we add a `containing_address` method:
|
||||
|
||||
```rust
|
||||
@@ -386,7 +394,7 @@ Note that we call `choose_next_area` manually here because `allocate_frame` retu
|
||||
### Testing it
|
||||
In order to test it in main, we need to [re-export] the `AreaFrameAllocator` in the `memory` module. Then we can create a new allocator:
|
||||
|
||||
[re-export]: https://doc.rust-lang.org/book/crates-and-modules.html#re-exporting-with-pub-use
|
||||
[re-export]: https://doc.rust-lang.org/1.30.0/book/first-edition/crates-and-modules.html#re-exporting-with-pub-use
|
||||
|
||||
```rust
|
||||
let mut frame_allocator = memory::AreaFrameAllocator::new(
|
||||
@@ -413,7 +421,7 @@ for i in 0.. {
|
||||
```
|
||||
You can try different amounts of memory by passing e.g. `-m 500M` to QEMU. To compare these numbers, [WolframAlpha] can be very helpful.
|
||||
|
||||
[WolframAlpha]: http://www.wolframalpha.com/input/?i=%2832605+*+4096%29+bytes+in+MiB
|
||||
[WolframAlpha]: https://www.wolframalpha.com/input/?i=%2832605+*+4096%29+bytes+in+MiB
|
||||
|
||||
## Conclusion
|
||||
|
||||
@@ -422,10 +430,10 @@ Now we have a working frame allocator. It is a bit rudimentary and cannot free f
|
||||
## What's next?
|
||||
The [next post] will be about paging again. We will use the frame allocator to create a safe module that allows us to switch page tables and map pages. Then we will use this module and the information from the Elf-sections tag to remap the kernel correctly.
|
||||
|
||||
[next post]: {{% relref "2015-12-09-modifying-page-tables.md" %}}
|
||||
[next post]: @/edition-1/posts/06-page-tables/index.md
|
||||
|
||||
## Recommended Posts
|
||||
Eric Kidd started the [Bare Metal Rust] series last week. Like this post, it builds upon the code from [Printing to Screen], but tries to support keyboard input instead of wrestling through memory management details.
|
||||
|
||||
[Bare Metal Rust]: http://www.randomhacks.net/bare-metal-rust/
|
||||
[Printing to Screen]: {{% relref "2015-10-23-printing-to-screen.md" %}}
|
||||
[Printing to Screen]: @/edition-1/posts/04-printing-to-screen/index.md
|
||||
|
After Width: | Height: | Size: 18 KiB |
929
blog/content/edition-1/posts/06-page-tables/X86_Paging_64bit.svg
Normal file
@@ -0,0 +1,929 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="7.2973523in"
|
||||
height="4.1279249in"
|
||||
viewBox="-2141 2141 8777.1352 4929.2808"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="X86_Paging_64bit.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1080"
|
||||
inkscape:window-height="1868"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="1.4404005"
|
||||
inkscape:cx="383.13253"
|
||||
inkscape:cy="73.778068"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(-2845,-286.64442)">
|
||||
<rect
|
||||
x="8503"
|
||||
y="4015"
|
||||
width="708"
|
||||
height="3307"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect8" />
|
||||
<polyline
|
||||
points="3365,2598 3365,3070"
|
||||
id="polyline10" />
|
||||
<polyline
|
||||
points="3189,2598 3189,3070"
|
||||
id="polyline12" />
|
||||
<polyline
|
||||
points="3012,2598 3012,3070"
|
||||
id="polyline14" />
|
||||
<polyline
|
||||
points="2834,2598 2834,3070"
|
||||
id="polyline16" />
|
||||
<polyline
|
||||
points="2657,2598 2657,3070"
|
||||
id="polyline18" />
|
||||
<polyline
|
||||
points="2480,2598 2480,3070"
|
||||
id="polyline20" />
|
||||
<polyline
|
||||
points="2303,2598 2303,3070"
|
||||
id="polyline22" />
|
||||
<polyline
|
||||
points="2125,2456 2125,3070"
|
||||
id="polyline24" />
|
||||
<polyline
|
||||
points="1948,2598 1948,3070"
|
||||
id="polyline26" />
|
||||
<polyline
|
||||
points="1772,2598 1772,3070"
|
||||
id="polyline28" />
|
||||
<polyline
|
||||
points="1594,2598 1594,3070"
|
||||
id="polyline30" />
|
||||
<polyline
|
||||
points="1417,2598 1417,3070"
|
||||
id="polyline32" />
|
||||
<polyline
|
||||
points="1239,2598 1239,3070"
|
||||
id="polyline34" />
|
||||
<polyline
|
||||
points="1063,2598 1063,3070"
|
||||
id="polyline36" />
|
||||
<polyline
|
||||
points="886,2598 886,3070"
|
||||
id="polyline38" />
|
||||
<polyline
|
||||
points="708,2456 708,3070"
|
||||
id="polyline40" />
|
||||
<polyline
|
||||
points="3543,2456 3543,3070"
|
||||
id="polyline74" />
|
||||
<polyline
|
||||
points="-2125,2598 3543,2598"
|
||||
id="polyline76"
|
||||
transform="matrix(0.49978943,0,0,1,1772.2461,0)" />
|
||||
<polyline
|
||||
points="3543,3070 -1948,3070 -2125,3070"
|
||||
id="polyline78"
|
||||
transform="matrix(0.49786923,0,0,1,1779.0493,0)" />
|
||||
<polyline
|
||||
points="9035,2598 9035,3070"
|
||||
id="polyline80" />
|
||||
<polyline
|
||||
points="8858,2598 8858,3070"
|
||||
id="polyline82" />
|
||||
<polyline
|
||||
points="8681,2598 8681,3070"
|
||||
id="polyline84" />
|
||||
<polyline
|
||||
points="8503,2598 8503,3070"
|
||||
id="polyline86" />
|
||||
<polyline
|
||||
points="8326,2598 8326,3070"
|
||||
id="polyline88" />
|
||||
<polyline
|
||||
points="8150,2598 8150,3070"
|
||||
id="polyline90" />
|
||||
<polyline
|
||||
points="7972,2598 7972,3070"
|
||||
id="polyline92" />
|
||||
<polyline
|
||||
points="7795,2456 7795,3070"
|
||||
id="polyline94" />
|
||||
<polyline
|
||||
points="7617,2598 7617,3070"
|
||||
id="polyline96" />
|
||||
<polyline
|
||||
points="7441,2598 7441,3070"
|
||||
id="polyline98" />
|
||||
<polyline
|
||||
points="7264,2598 7264,3070"
|
||||
id="polyline100" />
|
||||
<polyline
|
||||
points="7086,2598 7086,3070"
|
||||
id="polyline102" />
|
||||
<polyline
|
||||
points="6909,2598 6909,3070"
|
||||
id="polyline104" />
|
||||
<polyline
|
||||
points="6732,2598 6732,3070"
|
||||
id="polyline106" />
|
||||
<polyline
|
||||
points="6555,2598 6555,3070"
|
||||
id="polyline108" />
|
||||
<polyline
|
||||
points="6377,2456 6377,3070"
|
||||
id="polyline110" />
|
||||
<polyline
|
||||
points="6200,2598 6200,3070"
|
||||
id="polyline112" />
|
||||
<polyline
|
||||
points="6024,2598 6024,3070"
|
||||
id="polyline114" />
|
||||
<polyline
|
||||
points="5846,2598 5846,3070"
|
||||
id="polyline116" />
|
||||
<polyline
|
||||
points="5669,2598 5669,3070"
|
||||
id="polyline118" />
|
||||
<polyline
|
||||
points="5491,2598 5491,3070"
|
||||
id="polyline120" />
|
||||
<polyline
|
||||
points="5315,2598 5315,3070"
|
||||
id="polyline122" />
|
||||
<polyline
|
||||
points="5138,2598 5138,3070"
|
||||
id="polyline124" />
|
||||
<polyline
|
||||
points="4960,2409 4960,3070"
|
||||
id="polyline126" />
|
||||
<polyline
|
||||
points="4783,2598 4783,3070"
|
||||
id="polyline128" />
|
||||
<polyline
|
||||
points="4606,2598 4606,3070"
|
||||
id="polyline130" />
|
||||
<polyline
|
||||
points="4429,2598 4429,3070"
|
||||
id="polyline132" />
|
||||
<polyline
|
||||
points="4251,2598 4251,3070"
|
||||
id="polyline134" />
|
||||
<polyline
|
||||
points="4074,2598 4074,3070"
|
||||
id="polyline136" />
|
||||
<polyline
|
||||
points="3898,2598 3898,3070"
|
||||
id="polyline138" />
|
||||
<polyline
|
||||
points="3720,2598 3720,3070"
|
||||
id="polyline140" />
|
||||
<polyline
|
||||
points="9212,2456 9212,3070"
|
||||
id="polyline142" />
|
||||
<polyline
|
||||
points="3543,2598 9212,2598"
|
||||
id="polyline144" />
|
||||
<polyline
|
||||
points="9212,3070 3720,3070 0,3070"
|
||||
id="polyline146"
|
||||
transform="matrix(0.92335242,0,0,1,706.07747,0)" />
|
||||
<rect
|
||||
x="5102"
|
||||
y="4488"
|
||||
width="1181"
|
||||
height="2362"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect148" />
|
||||
<rect
|
||||
x="3307"
|
||||
y="4251"
|
||||
width="1181"
|
||||
height="2362"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect150" />
|
||||
<rect
|
||||
x="6897"
|
||||
y="4724"
|
||||
width="1181"
|
||||
height="2362"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect152" />
|
||||
<rect
|
||||
x="1511"
|
||||
y="4015"
|
||||
width="1181"
|
||||
height="2362"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect154" />
|
||||
<rect
|
||||
x="1417"
|
||||
y="6850"
|
||||
width="1417"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect156" />
|
||||
<rect
|
||||
x="8503"
|
||||
y="5433"
|
||||
width="708"
|
||||
height="236"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf"
|
||||
id="rect158" />
|
||||
<rect
|
||||
x="5102"
|
||||
y="5669"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect160" />
|
||||
<rect
|
||||
x="3307"
|
||||
y="5433"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect162" />
|
||||
<rect
|
||||
x="6897"
|
||||
y="5905"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect164" />
|
||||
<rect
|
||||
x="1511"
|
||||
y="5196"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect166" />
|
||||
<circle
|
||||
cx="6141"
|
||||
cy="5905"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle170" />
|
||||
<circle
|
||||
cx="4346"
|
||||
cy="5669"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle172" />
|
||||
<circle
|
||||
cx="7937"
|
||||
cy="6141"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle174" />
|
||||
<circle
|
||||
cx="2551"
|
||||
cy="5433"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle176" />
|
||||
<polyline
|
||||
points="3118,3590 2834,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline178" />
|
||||
<polyline
|
||||
points="1322,3590 1039,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline180" />
|
||||
<polyline
|
||||
points="4913,3590 4629,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline184" />
|
||||
<polyline
|
||||
points="6519,3590 6236,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline186" />
|
||||
<polyline
|
||||
points="8314,3590 8031,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline188" />
|
||||
<circle
|
||||
cx="1653"
|
||||
cy="7086"
|
||||
r="47"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||
id="circle190" />
|
||||
<polyline
|
||||
points="1653,7086 1181,7086 1181,6377 1354,6377"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline192" />
|
||||
<polygon
|
||||
points="1512,6377 1355,6330 1355,6425 1355,6425 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon194" />
|
||||
<polyline
|
||||
points="7937,6141 8173,6141 8173,7322 8346,7322"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline196" />
|
||||
<polygon
|
||||
points="8504,7322 8347,7275 8347,7370 8347,7370 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon198" />
|
||||
<polyline
|
||||
points="8173,3543 8173,5669 8346,5669"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline200" />
|
||||
<polygon
|
||||
points="8504,5669 8347,5622 8347,5716 8347,5716 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon202" />
|
||||
<polyline
|
||||
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline204" />
|
||||
<polyline
|
||||
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline206" />
|
||||
<polyline
|
||||
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline208" />
|
||||
<polyline
|
||||
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline210" />
|
||||
<polyline
|
||||
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline212" />
|
||||
<polyline
|
||||
points="4771,3543 4771,6141 4944,6141"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline214" />
|
||||
<polygon
|
||||
points="5103,6141 4945,6094 4945,6188 4945,6188 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon216" />
|
||||
<polyline
|
||||
points="6377,3543 6377,4015 6519,4157 6519,6377 6740,6377"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline218" />
|
||||
<polygon
|
||||
points="6898,6377 6741,6330 6741,6425 6741,6425 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon220" />
|
||||
<polyline
|
||||
points="6141,5905 6377,5905 6377,7086 6740,7086"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline222" />
|
||||
<polygon
|
||||
points="6898,7086 6741,7039 6741,7133 6741,7133 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon224" />
|
||||
<polyline
|
||||
points="4346,5669 4629,5669 4629,6850 4944,6850"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline226" />
|
||||
<polygon
|
||||
points="5103,6850 4945,6803 4945,6897 4945,6897 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon228" />
|
||||
<polyline
|
||||
points="2598,5433 2834,5433 2834,6614 3149,6614"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline230" />
|
||||
<polygon
|
||||
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon232" />
|
||||
<polyline
|
||||
points="1181,3543 1181,5669 1401,5669"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline234" />
|
||||
<polygon
|
||||
points="1560,5669 1402,5622 1402,5716 1402,5716 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon236" />
|
||||
<polyline
|
||||
points="2976,3543 2976,5905 3149,5905"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline238" />
|
||||
<polygon
|
||||
points="3308,5905 3150,5858 3150,5952 3150,5952 "
|
||||
style="fill:#0000ff;stroke:#0000ff"
|
||||
id="polygon240" />
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2281.8145"
|
||||
y="7165"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text244"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">CR3 register</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3507"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text246"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2161"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text248"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text250"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="9176"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text262"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7759"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text264"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6342"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text266"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4924"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text268"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3579"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text270"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6413"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text272"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7830"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text274"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4996"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text276"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,4724)"
|
||||
id="g278">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text280"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,6614)"
|
||||
id="g282">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text284"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,9448,5622)"
|
||||
id="g286">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text288"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3509">4K memory page</tspan></text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5622"
|
||||
y="5958.457"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text292"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3497">P2 entry</tspan></text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,5723,6614)"
|
||||
id="g296">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text298"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,5723,5196)"
|
||||
id="g300">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text302"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5692"
|
||||
y="4393"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text304"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3505">P2 table</tspan></text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,3928,6377)"
|
||||
id="g306">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,3928,4960)"
|
||||
id="g310">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text312"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3826"
|
||||
y="5722.457"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3495">P3 entry</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="4157"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3503">P3 table</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7417"
|
||||
y="6194.457"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text322"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3499">P1 entry </tspan></text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,7519,6850)"
|
||||
id="g326">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text328"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,7519,5433)"
|
||||
id="g330">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text332"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7487"
|
||||
y="4629"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text334"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3507">P1 table</tspan></text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,2133,6141)"
|
||||
id="g336">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text338"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,2133,4724)"
|
||||
id="g340">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text342"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
x="2004.2715"
|
||||
y="5486.457"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text344"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
|
||||
<tspan
|
||||
style="font-size:200.46390561px"
|
||||
id="tspan3493">P4 entry</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="2078"
|
||||
y="3941.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text348"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
|
||||
<tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3501">P4 table</tspan>
|
||||
</text>
|
||||
<text
|
||||
x="3165"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text350"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text352"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="4960"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text356"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="6566"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text358"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="8362"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text360"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 27 KiB |
@@ -1,16 +1,19 @@
|
||||
+++
|
||||
title = "Page Tables"
|
||||
slug = "modifying-page-tables"
|
||||
date = "2015-12-09"
|
||||
weight = 6
|
||||
path = "page-tables"
|
||||
aliases = ["page-tables.html", "modifying-page-tables.html"]
|
||||
date = 2015-12-09
|
||||
template = "edition-1/page.html"
|
||||
+++
|
||||
|
||||
In this post we will create a paging module, which allows us to access and modify the 4-level page table. We will explore recursive page table mapping and use some Rust features to make it safe. Finally we will create functions to translate virtual addresses and to map and unmap pages.
|
||||
|
||||
<!--more-->
|
||||
<!-- more -->
|
||||
|
||||
You can find the source code and this post itself on [Github][source repository]. Please file an issue there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
|
||||
You can find the source code and this post itself on [GitHub][source repository]. Please file an issue there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
|
||||
|
||||
[source repository]: https://github.com/phil-opp/blog_os/tree/page_tables
|
||||
[source repository]: https://github.com/phil-opp/blog_os/tree/first_edition_post_6
|
||||
|
||||
## Paging
|
||||
_Paging_ is a memory management scheme that separates virtual and physical memory. The address space is split into equal sized _pages_ and _page tables_ specify which virtual page points to which physical frame. For an extensive paging introduction take a look at the paging chapter ([PDF][paging chapter]) of the [Three Easy Pieces] OS book.
|
||||
@@ -20,7 +23,7 @@ _Paging_ is a memory management scheme that separates virtual and physical memor
|
||||
|
||||
The x86 architecture uses a 4-level page table in 64-bit mode. A virtual address has the following structure:
|
||||
|
||||

|
||||

|
||||
|
||||
The bits 48–63 are so-called _sign extension_ bits and must be copies of bit 47. The following 36 bits define the page table indexes (9 bits per table) and the last 12 bits specify the offset in the 4KiB page.
|
||||
|
||||
@@ -28,7 +31,7 @@ Each table has 2^9 = 512 entries and each entry is 8 byte. Thus a page table fit
|
||||
|
||||
To translate an address, the CPU reads the P4 address from the CR3 register. Then it uses the indexes to walk the tables:
|
||||
|
||||

|
||||

|
||||
|
||||
The P4 entry points to a P3 table, where the next 9 bits of the address are used to select an entry. The P3 entry then points to a P2 table and the P2 entry points to a P1 table. The P1 entry, which is specified through bits 12–20, finally points to the physical frame.
|
||||
|
||||
@@ -49,7 +52,7 @@ pub struct Page {
|
||||
```
|
||||
We import the `PAGE_SIZE` and define a constant for the number of entries per table. To make future function signatures more expressive, we can use the type aliases `PhysicalAddress` and `VirtualAddress`. The `Page` struct is similar to the `Frame` struct in the [previous post], but represents a virtual page instead of a physical frame.
|
||||
|
||||
[previous post]: {{% relref "2015-11-15-allocating-frames.md#a-memory-module" %}}
|
||||
[previous post]: @/edition-1/posts/05-allocating-frames/index.md#a-memory-module
|
||||
|
||||
### Page Table Entries
|
||||
To model page table entries, we create a new `entry` submodule:
|
||||
@@ -96,7 +99,7 @@ To model the various flags, we will use the [bitflags] crate. To add it as a dep
|
||||
```toml
|
||||
[dependencies]
|
||||
...
|
||||
bitflags = "0.7.0"
|
||||
bitflags = "0.9.1"
|
||||
```
|
||||
|
||||
To import the macro, we need to use `#[macro_use]` above the `extern crate` definition:
|
||||
@@ -111,23 +114,23 @@ Now we can model the various flags:
|
||||
|
||||
```rust
|
||||
bitflags! {
|
||||
pub flags EntryFlags: u64 {
|
||||
const PRESENT = 1 << 0,
|
||||
const WRITABLE = 1 << 1,
|
||||
const USER_ACCESSIBLE = 1 << 2,
|
||||
const WRITE_THROUGH = 1 << 3,
|
||||
const NO_CACHE = 1 << 4,
|
||||
const ACCESSED = 1 << 5,
|
||||
const DIRTY = 1 << 6,
|
||||
const HUGE_PAGE = 1 << 7,
|
||||
const GLOBAL = 1 << 8,
|
||||
const NO_EXECUTE = 1 << 63,
|
||||
pub struct EntryFlags: u64 {
|
||||
const PRESENT = 1 << 0;
|
||||
const WRITABLE = 1 << 1;
|
||||
const USER_ACCESSIBLE = 1 << 2;
|
||||
const WRITE_THROUGH = 1 << 3;
|
||||
const NO_CACHE = 1 << 4;
|
||||
const ACCESSED = 1 << 5;
|
||||
const DIRTY = 1 << 6;
|
||||
const HUGE_PAGE = 1 << 7;
|
||||
const GLOBAL = 1 << 8;
|
||||
const NO_EXECUTE = 1 << 63;
|
||||
}
|
||||
}
|
||||
```
|
||||
To extract the flags from the entry we create an `Entry::flags` method that uses [from_bits_truncate]:
|
||||
|
||||
[from_bits_truncate]: https://doc.rust-lang.org/bitflags/bitflags/macro.bitflags!.html#methods
|
||||
[from_bits_truncate]: https://docs.rs/bitflags/0.9.1/bitflags/example_generated/struct.Flags.html#method.from_bits_truncate
|
||||
|
||||
```rust
|
||||
pub fn flags(&self) -> EntryFlags {
|
||||
@@ -232,19 +235,19 @@ We will solve the problem in another way using a trick called _recursive mapping
|
||||
### Recursive Mapping
|
||||
The trick is to map the P4 table recursively: The last entry doesn't point to a P3 table, but to the P4 table itself. We can use this entry to remove a translation level so that we land on a page table instead. For example, we can “loop” once to access a P1 table:
|
||||
|
||||

|
||||

|
||||
|
||||
By selecting the 511th P4 entry, which points points to the P4 table itself, the P4 table is used as the P3 table. Similarly, the P3 table is used as a P2 table and the P2 table is treated like a P1 table. Thus the P1 table becomes the target page and can be accessed through the offset.
|
||||
|
||||
It's also possible to access P2 tables by looping twice. And if we select the 511th entry three times, we can access and modify P3 tables:
|
||||
|
||||

|
||||

|
||||
|
||||
So we just need to specify the desired P3 table in the address through the P1 index. By choosing the 511th entry multiple times, we stay on the P4 table until the address's P1 index becomes the actual P4 index.
|
||||
|
||||
To access the P4 table itself, we loop once more and thus never leave the frame:
|
||||
|
||||

|
||||

|
||||
|
||||
So we can access and modify page tables of all levels by just setting one P4 entry once. Most work is done by the CPU, we just the recursive entry to remove one or more translation levels. It may seem a bit strange at first, but it's a clean and simple solution once you wrapped your head around it.
|
||||
|
||||
@@ -320,7 +323,7 @@ We convert the address into raw pointers through `as` casts and then convert the
|
||||
|
||||
Note that `self` stays borrowed as long as the returned reference is valid. This is because of Rust's [lifetime elision] rules. Basically, these rules say that the lifetime of an output reference is the same as the lifetime of the input reference by default. So the above function signatures are expanded to:
|
||||
|
||||
[lifetime elision]: https://doc.rust-lang.org/book/lifetimes.html#lifetime-elision
|
||||
[lifetime elision]: https://doc.rust-lang.org/1.30.0/book/first-edition/lifetimes.html#lifetime-elision
|
||||
|
||||
```rust
|
||||
pub fn next_table<'a>(&'a self, index: usize) -> Option<&'a Table> {...}
|
||||
@@ -345,8 +348,6 @@ _What happens if we call them on a P1 table?_
|
||||
|
||||
Well, they would calculate the address of the next table (which does not exist) and treat it as a page table. Either they construct an invalid address (if `XXX < 400`)[^fn-invalid-address] or access the mapped page itself. That way, we could easily corrupt memory or cause CPU exceptions by accident. So these two functions are not _safe_ in Rust terms. Thus we need to make them `unsafe` functions unless we find some clever solution.
|
||||
|
||||
[^fn-invalid-address]: If the `XXX` part of the address is smaller than `0o400`, it's binary representation doesn't start with `1`. But the sign extension bits, which should be a copy of that bit, are `1` instead of `0`. Thus the address is not valid.
|
||||
|
||||
## Some Clever Solution
|
||||
We can use Rust's type system to statically guarantee that the `next_table` methods can only be called on P4, P3, and P2 tables, but not on a P1 table. The idea is to add a `Level` parameter to the `Table` type and implement the `next_table` methods only for level 4, 3, and 2.
|
||||
|
||||
@@ -598,9 +599,9 @@ pub fn map_to<A>(page: Page, frame: Frame, flags: EntryFlags,
|
||||
p1[page.p1_index()].set(frame, flags | PRESENT);
|
||||
}
|
||||
```
|
||||
We add an reexport for all `entry` types since they are required to call the function. We assert that the page is unmapped and always set the present flag (since it wouldn't make sense to map a page without setting it).
|
||||
We add an re-export for all `entry` types since they are required to call the function. We assert that the page is unmapped and always set the present flag (since it wouldn't make sense to map a page without setting it).
|
||||
|
||||
The `Table::next_table_create` method doesn't exist yet. It should return the next table if it exists, or create a new one. Therefor we need the `FrameAllocator` from the [previous post] and the `Table::zero` method:
|
||||
The `Table::next_table_create` method doesn't exist yet. It should return the next table if it exists, or create a new one. For the implementation we need the `FrameAllocator` from the [previous post] and the `Table::zero` method:
|
||||
|
||||
```rust
|
||||
use memory::FrameAllocator;
|
||||
@@ -651,8 +652,8 @@ pub struct ActivePageTable {
|
||||
```
|
||||
We can't store the `Table<Level4>` directly because it needs to be at a special memory location (like the [VGA text buffer]). We could use a raw pointer or `&mut` instead of [Unique], but Unique indicates ownership better.
|
||||
|
||||
[VGA text buffer]: {{% relref "2015-10-23-printing-to-screen.md#the-text-buffer" %}}
|
||||
[Unique]: https://doc.rust-lang.org/nightly/core/ptr/struct.Unique.html
|
||||
[VGA text buffer]: @/edition-1/posts/04-printing-to-screen/index.md#the-text-buffer
|
||||
[Unique]: https://doc.rust-lang.org/1.10.0/core/ptr/struct.Unique.html
|
||||
|
||||
Because the `ActivePageTable` owns the unique recursive mapped P4 table, there must be only one `ActivePageTable` instance. Thus we make the constructor function unsafe:
|
||||
|
||||
@@ -660,7 +661,7 @@ Because the `ActivePageTable` owns the unique recursive mapped P4 table, there m
|
||||
impl ActivePageTable {
|
||||
pub unsafe fn new() -> ActivePageTable {
|
||||
ActivePageTable {
|
||||
p4: Unique::new(table::P4),
|
||||
p4: Unique::new_unchecked(table::P4),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,11 +671,11 @@ We add some methods to get P4 references:
|
||||
|
||||
```rust
|
||||
fn p4(&self) -> &Table<Level4> {
|
||||
unsafe { self.p4.get() }
|
||||
unsafe { self.p4.as_ref() }
|
||||
}
|
||||
|
||||
fn p4_mut(&mut self) -> &mut Table<Level4> {
|
||||
unsafe { self.p4.get_mut() }
|
||||
unsafe { self.p4.as_mut() }
|
||||
}
|
||||
```
|
||||
|
||||
@@ -776,12 +777,12 @@ To test it, we add a `test_paging` function in `memory/paging/mod.rs`:
|
||||
pub fn test_paging<A>(allocator: &mut A)
|
||||
where A: FrameAllocator
|
||||
{
|
||||
let page_table = unsafe { ActivePageTable::new() };
|
||||
let mut page_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
// test it
|
||||
}
|
||||
```
|
||||
We borrow the frame allocator since we will need it for the mapping functions. To be able to call that function from main, we need to reexport it in `memory/mod.rs`:
|
||||
We borrow the frame allocator since we will need it for the mapping functions. To be able to call that function from main, we need to re-export it in `memory/mod.rs`:
|
||||
|
||||
```rust
|
||||
// in memory/mod.rs
|
||||
@@ -792,63 +793,6 @@ let mut frame_allocator = ...;
|
||||
memory::test_paging(&mut frame_allocator);
|
||||
```
|
||||
|
||||
### translate
|
||||
First, we translate some addresses:
|
||||
|
||||
```rust
|
||||
// address 0 is mapped
|
||||
println!("Some = {:?}", page_table.translate(0));
|
||||
// second P1 entry
|
||||
println!("Some = {:?}", page_table.translate(4096));
|
||||
// second P2 entry
|
||||
println!("Some = {:?}", page_table.translate(512 * 4096));
|
||||
// 300th P2 entry
|
||||
println!("Some = {:?}", page_table.translate(300 * 512 * 4096));
|
||||
// second P3 entry
|
||||
println!("None = {:?}", page_table.translate(512 * 512 * 4096));
|
||||
// last mapped byte
|
||||
println!("Some = {:?}", page_table.translate(512 * 512 * 4096 - 1));
|
||||
```
|
||||
Currently, the first GiB of the address space is identity-mapped. Thus all addresses in this area should translate to `Some(x)`, where `x` is the virtual address. Only the second last address, `512 * 512 * 4096`, is not in that area and should resolve to `None`.
|
||||
|
||||
But the output shows two `None` lines:
|
||||
|
||||
```
|
||||
Some = Some(0)
|
||||
Some = Some(4096)
|
||||
Some = Some(2097152)
|
||||
Some = Some(629145600)
|
||||
None = None
|
||||
Some = None
|
||||
```
|
||||
The last line is wrong. But why?
|
||||
|
||||
In fact, all addresses above `344 * 512 * 4096` seem to get translated to `None`. But even worse, there are some wrong translations, too. For example, on my machine `357 * 512 * 4096` translates to roughly `255TiB`:
|
||||
|
||||
```
|
||||
Some(280735973961728)
|
||||
```
|
||||
Something is terribly wrong here. But it's not our code.
|
||||
|
||||
The reason for this bug is a silent stack overflow. Remember, our `.bss` section in the `boot.asm` file looks like this:
|
||||
|
||||
```nasm
|
||||
section .bss
|
||||
align 4096
|
||||
p4_table:
|
||||
resb 4096
|
||||
p3_table:
|
||||
resb 4096
|
||||
p2_table:
|
||||
resb 4096
|
||||
stack_bottom:
|
||||
resb 4096
|
||||
stack_top:
|
||||
```
|
||||
So a stack overflow overwrites the P2 table, starting at the last entry. But the CPU still uses the memory as page table entries. And if the stack bytes contain the present byte, it seems to point to a frame and `translate` returns a (wrong) `Some`.
|
||||
|
||||
To fix it, we double the stack size to `4096 * 2`. Now the last byte gets translated to `Some(1073741823)` correctly. To avoid this kind of bug in the future, we need to add a guard page to the stack, which causes an exception on stack overflow. We will do that in the next post when we remap the kernel.
|
||||
|
||||
### map_to
|
||||
Let's test the `map_to` function:
|
||||
|
||||
@@ -881,7 +825,7 @@ To test the `unmap` function, we unmap the test page so that it translates to `N
|
||||
page_table.unmap(Page::containing_address(addr), allocator);
|
||||
println!("None = {:?}", page_table.translate(addr));
|
||||
```
|
||||
It causes a panic since we call the unimplemented `deallocate_frame` method in `unwrap`. If we comment this call out, it works without problems. But there is some bug in this function nevertheless.
|
||||
It causes a panic since we call the unimplemented `deallocate_frame` method in `unmap`. If we comment this call out, it works without problems. But there is some bug in this function nevertheless.
|
||||
|
||||
Let's read something from the mapped page (of course before we unmap it again):
|
||||
|
||||
@@ -898,29 +842,28 @@ An x86 processor has many different caches because always accessing the main mem
|
||||
|
||||
The translation lookaside buffer, or TLB, caches the translation of virtual to physical addresses. It's filled automatically when a page is accessed. But it's not updated transparently when the mapping of a page changes. This is the reason that we still can access the page even through we unmapped it in the page table.
|
||||
|
||||
So to fix our `unmap` function, we need to remove the cached translation from the TLB. We can use Gerd Zellweger's [x86][x86 crate] crate to do this easily. To add it, we append the following to our `Cargo.toml`:
|
||||
So to fix our `unmap` function, we need to remove the cached translation from the TLB. We can use the [x86_64][x86_64 crate] crate to do this easily. To add it, we append the following to our `Cargo.toml`:
|
||||
|
||||
[x86 crate]: https://github.com/gz/rust-x86
|
||||
[x86_64 crate]: https://docs.rs/x86_64
|
||||
|
||||
```toml
|
||||
[dependencies.x86]
|
||||
version = "0.6.0"
|
||||
default-features = false
|
||||
[dependencies]
|
||||
...
|
||||
x86_64 = "0.1.2"
|
||||
```
|
||||
It has a `performance-counter` feature that allows reading the CPU specific [performance counters] but increases compile times. We don't need it right now, so we disable it using `default-features = false`.
|
||||
|
||||
[performance counters]: http://gz.github.io/rust-x86/x86/perfcnt/index.html
|
||||
|
||||
Now we can use it to fix `unmap`:
|
||||
|
||||
```rust
|
||||
...
|
||||
p1[page.p1_index()].set_unused();
|
||||
unsafe {
|
||||
::x86::tlb::flush(page.start_address());
|
||||
}
|
||||
// TODO free p(1,2,3) table if empty
|
||||
//allocator.deallocate_frame(frame);
|
||||
p1[page.p1_index()].set_unused();
|
||||
|
||||
use x86_64::instructions::tlb;
|
||||
use x86_64::VirtualAddress;
|
||||
tlb::flush(VirtualAddress(page.start_address()));
|
||||
|
||||
// TODO free p(1,2,3) table if empty
|
||||
//allocator.deallocate_frame(frame);
|
||||
}
|
||||
```
|
||||
Now the desired page fault occurs even when we access the page before.
|
||||
@@ -938,10 +881,13 @@ This post has become pretty long. So let's summarize what we've done:
|
||||
## What's next?
|
||||
In the [next post] we will extend this module and add a function to modify inactive page tables. Through that function, we will create a new page table hierarchy that maps the kernel correctly using 4KiB pages. Then we will switch to the new table to get a safer kernel environment.
|
||||
|
||||
[next post]: {{% relref "2016-01-01-remap-the-kernel.md" %}}
|
||||
[next post]: @/edition-1/posts/07-remap-the-kernel/index.md
|
||||
|
||||
Afterwards, we will use this paging module to build a heap allocator. This will allow us to use allocation and collection types such as `Box` and `Vec`.
|
||||
|
||||
<small>Image sources: [^virtual_physical_translation_source]</small>
|
||||
|
||||
## Footnotes
|
||||
[^fn-invalid-address]: If the `XXX` part of the address is smaller than `0o400`, it's binary representation doesn't start with `1`. But the sign extension bits, which should be a copy of that bit, are `1` instead of `0`. Thus the address is not valid.
|
||||
|
||||
[^virtual_physical_translation_source]: Image sources: Modified versions of an image from [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg). The modified files are licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
|
||||
|
After Width: | Height: | Size: 38 KiB |
@@ -0,0 +1,819 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="900.00409"
|
||||
height="530"
|
||||
viewBox="-2141 2141 12027.89 7032.1014"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="recursive_mapping_access_p3.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="998"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="2.7755576e-17"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="236.56561"
|
||||
inkscape:cy="267.91265"
|
||||
inkscape:window-x="1080"
|
||||
inkscape:window-y="568"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4"
|
||||
units="in" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(-1087.0637,717.51407)">
|
||||
<polyline
|
||||
points="3365,2598 3365,3070"
|
||||
id="polyline10"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3189,2598 3189,3070"
|
||||
id="polyline12"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3012,2598 3012,3070"
|
||||
id="polyline14"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2834,2598 2834,3070"
|
||||
id="polyline16"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2657,2598 2657,3070"
|
||||
id="polyline18"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2480,2598 2480,3070"
|
||||
id="polyline20"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2303,2598 2303,3070"
|
||||
id="polyline22"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2125,2456 2125,3070"
|
||||
id="polyline24"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1948,2598 1948,3070"
|
||||
id="polyline26"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1772,2598 1772,3070"
|
||||
id="polyline28"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1594,2598 1594,3070"
|
||||
id="polyline30"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1417,2598 1417,3070"
|
||||
id="polyline32"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1239,2598 1239,3070"
|
||||
id="polyline34"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1063,2598 1063,3070"
|
||||
id="polyline36"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="886,2598 886,3070"
|
||||
id="polyline38"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="708,2456 708,3070"
|
||||
id="polyline40"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,2456 3543,3070"
|
||||
id="polyline74"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="-2125,2598 3543,2598"
|
||||
id="polyline76"
|
||||
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,3070 -1948,3070 -2125,3070"
|
||||
id="polyline78"
|
||||
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
|
||||
<polyline
|
||||
points="9035,2598 9035,3070"
|
||||
id="polyline80"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8858,2598 8858,3070"
|
||||
id="polyline82"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8681,2598 8681,3070"
|
||||
id="polyline84"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8503,2598 8503,3070"
|
||||
id="polyline86"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8326,2598 8326,3070"
|
||||
id="polyline88"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8150,2598 8150,3070"
|
||||
id="polyline90"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7972,2598 7972,3070"
|
||||
id="polyline92"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7795,2456 7795,3070"
|
||||
id="polyline94"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7617,2598 7617,3070"
|
||||
id="polyline96"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7441,2598 7441,3070"
|
||||
id="polyline98"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7264,2598 7264,3070"
|
||||
id="polyline100"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7086,2598 7086,3070"
|
||||
id="polyline102"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6909,2598 6909,3070"
|
||||
id="polyline104"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6732,2598 6732,3070"
|
||||
id="polyline106"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6555,2598 6555,3070"
|
||||
id="polyline108"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6377,2456 6377,3070"
|
||||
id="polyline110"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6200,2598 6200,3070"
|
||||
id="polyline112"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6024,2598 6024,3070"
|
||||
id="polyline114"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5846,2598 5846,3070"
|
||||
id="polyline116"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5669,2598 5669,3070"
|
||||
id="polyline118"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5491,2598 5491,3070"
|
||||
id="polyline120"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5315,2598 5315,3070"
|
||||
id="polyline122"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5138,2598 5138,3070"
|
||||
id="polyline124"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4960,2409 4960,3070"
|
||||
id="polyline126"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4783,2598 4783,3070"
|
||||
id="polyline128"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4606,2598 4606,3070"
|
||||
id="polyline130"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4429,2598 4429,3070"
|
||||
id="polyline132"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4251,2598 4251,3070"
|
||||
id="polyline134"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4074,2598 4074,3070"
|
||||
id="polyline136"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3898,2598 3898,3070"
|
||||
id="polyline138"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3720,2598 3720,3070"
|
||||
id="polyline140"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,2456 9212,3070"
|
||||
id="polyline142"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,2598 9212,2598"
|
||||
id="polyline144"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,3070 3720,3070 0,3070"
|
||||
id="polyline146"
|
||||
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
|
||||
<rect
|
||||
x="4021.6787"
|
||||
y="4311.5088"
|
||||
width="1668.8131"
|
||||
height="3296.2527"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke-width:11.23412323"
|
||||
id="rect150" />
|
||||
<polyline
|
||||
points="3118,3590 2834,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline178"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1322,3590 1039,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline180"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4913,3590 4629,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline184"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6519,3590 6236,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline186"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8314,3590 8031,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline188"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline204"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline206"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline208"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline210"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline212"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5546.7881"
|
||||
y="6876.4409"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-5">0</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3507"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text246"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2161"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text248"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text250"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="9176"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text262"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7759"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text264"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6342"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text266"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4924"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text268"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3579"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text270"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6413"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text272"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7830"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text274"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4996"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text276"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,6614)"
|
||||
id="g282" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,4916.9545,5254.403)"
|
||||
id="g306">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4862.9551"
|
||||
y="4397.5566"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3503">P4 table</tspan></text>
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="3165"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text350"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text352"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="4960"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text356"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="6566"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text358"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="8362"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text360"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
x="1946.0945"
|
||||
y="6998.52"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text344-8-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3493-3-2" />
|
||||
</text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="4294.9473"
|
||||
y="4493.6152"
|
||||
width="1181"
|
||||
height="471.99994"
|
||||
rx="0"
|
||||
id="rect162-5" />
|
||||
<circle
|
||||
cx="5335.9546"
|
||||
cy="4720.2134"
|
||||
style="fill:#008200;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
|
||||
id="circle172-4"
|
||||
r="46.999996" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4815.9546"
|
||||
y="4773.6704"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6">P4 entry</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4925.6357"
|
||||
y="7173.5298"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6-4">Recursive</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4925.6357"
|
||||
y="7440.8149"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0-9"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6-4-4">Mapping</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="4296.5396"
|
||||
y="5560.2471"
|
||||
width="1182.4656"
|
||||
height="314.50067"
|
||||
rx="0"
|
||||
id="rect162-5-1" />
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.99999994,0.99999994,0,4915.3734,6277.1653)"
|
||||
id="g306-2">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-3"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3097.1609"
|
||||
y="2954.5652"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-4"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
inkscape:transform-center-x="-789.14735"
|
||||
inkscape:transform-center-y="1300.106"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3503-4">111111111111111111111111111</tspan></text>
|
||||
<rect
|
||||
x="8208.377"
|
||||
y="4073.4419"
|
||||
width="691.0863"
|
||||
height="3268.5144"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect8" />
|
||||
<rect
|
||||
x="8208.377"
|
||||
y="5474.9399"
|
||||
width="691.0863"
|
||||
height="233.25354"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect158" />
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.98836234,0.97611052,0,9106.1205,5822.055)"
|
||||
id="g286">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="96.892052"
|
||||
y="38.756821"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text288"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3509">P3 table </tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.53380773;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 2308.9448,-2797.8507 -483.0169,0 0,-2544.6425 -1879.786585,0 0,339.1222"
|
||||
id="path8874"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4809.9756"
|
||||
y="5774.4951"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3495-6-7">P4 entry</tspan></text>
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="5336.855"
|
||||
cy="5714.1816"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="circle172-4-4" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.55735683px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 5392.0276,5712.0004 1512.0496,0 0,1631.4957 1257.8109,0"
|
||||
id="path9464"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 8173.2452,3542.5202 0,364.8071 -584.0141,0 0,1802.0952 528.0052,0"
|
||||
id="path9466"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5610.3569"
|
||||
y="4990.1772"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5">511</tspan></text>
|
||||
</g>
|
||||
<polygon
|
||||
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||
id="polygon232-2"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-646.4618,-1622.6914)" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76697159px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 5574.5569,4637.287 719.9709,0 0,3526.7653 -2765.702,0 0,-560.545 294.8212,0"
|
||||
id="path5354"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.52740288px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 3843.9848,5007.8363 -4223.58417,0 0,-2024.2908"
|
||||
id="path5356"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.65159035px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 2156.7076,2986.8221 0,1988.2634 1691.863,0"
|
||||
id="path5358"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.78581619px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4696.351,2980.052 0,497.5653 -1975.7645,0 0,1464.6642 1128.3337,0"
|
||||
id="path5360"
|
||||
inkscape:connector-curvature="0" />
|
||||
<polygon
|
||||
points="3150,6661 3308,6614 3150,6566 3150,6661 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-625.83944,-2988.2046)" />
|
||||
<polygon
|
||||
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7-3"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,4891.7749,-3219.444)" />
|
||||
<polygon
|
||||
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7-3-5"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,4896.989,-940.27671)" />
|
||||
<polygon
|
||||
points="3150,6566 3150,6661 3150,6661 3308,6614 "
|
||||
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||
id="polygon232-2-7-7"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-628.09888,-4256.4264)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,765 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="900.00409"
|
||||
height="530"
|
||||
viewBox="-2141 2141 12027.89 7032.1014"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="recursive_mapping_access_p4.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="998"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="2.7755576e-17"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.5"
|
||||
inkscape:cx="150.51041"
|
||||
inkscape:cy="507.56343"
|
||||
inkscape:window-x="1080"
|
||||
inkscape:window-y="568"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4"
|
||||
units="in" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(-1087.0637,717.51407)">
|
||||
<polyline
|
||||
points="3365,2598 3365,3070"
|
||||
id="polyline10"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3189,2598 3189,3070"
|
||||
id="polyline12"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3012,2598 3012,3070"
|
||||
id="polyline14"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2834,2598 2834,3070"
|
||||
id="polyline16"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2657,2598 2657,3070"
|
||||
id="polyline18"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2480,2598 2480,3070"
|
||||
id="polyline20"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2303,2598 2303,3070"
|
||||
id="polyline22"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2125,2456 2125,3070"
|
||||
id="polyline24"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1948,2598 1948,3070"
|
||||
id="polyline26"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1772,2598 1772,3070"
|
||||
id="polyline28"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1594,2598 1594,3070"
|
||||
id="polyline30"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1417,2598 1417,3070"
|
||||
id="polyline32"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1239,2598 1239,3070"
|
||||
id="polyline34"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1063,2598 1063,3070"
|
||||
id="polyline36"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="886,2598 886,3070"
|
||||
id="polyline38"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="708,2456 708,3070"
|
||||
id="polyline40"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,2456 3543,3070"
|
||||
id="polyline74"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="-2125,2598 3543,2598"
|
||||
id="polyline76"
|
||||
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,3070 -1948,3070 -2125,3070"
|
||||
id="polyline78"
|
||||
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
|
||||
<polyline
|
||||
points="9035,2598 9035,3070"
|
||||
id="polyline80"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8858,2598 8858,3070"
|
||||
id="polyline82"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8681,2598 8681,3070"
|
||||
id="polyline84"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8503,2598 8503,3070"
|
||||
id="polyline86"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8326,2598 8326,3070"
|
||||
id="polyline88"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8150,2598 8150,3070"
|
||||
id="polyline90"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7972,2598 7972,3070"
|
||||
id="polyline92"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7795,2456 7795,3070"
|
||||
id="polyline94"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7617,2598 7617,3070"
|
||||
id="polyline96"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7441,2598 7441,3070"
|
||||
id="polyline98"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7264,2598 7264,3070"
|
||||
id="polyline100"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7086,2598 7086,3070"
|
||||
id="polyline102"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6909,2598 6909,3070"
|
||||
id="polyline104"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6732,2598 6732,3070"
|
||||
id="polyline106"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6555,2598 6555,3070"
|
||||
id="polyline108"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6377,2456 6377,3070"
|
||||
id="polyline110"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6200,2598 6200,3070"
|
||||
id="polyline112"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6024,2598 6024,3070"
|
||||
id="polyline114"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5846,2598 5846,3070"
|
||||
id="polyline116"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5669,2598 5669,3070"
|
||||
id="polyline118"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5491,2598 5491,3070"
|
||||
id="polyline120"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5315,2598 5315,3070"
|
||||
id="polyline122"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5138,2598 5138,3070"
|
||||
id="polyline124"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4960,2409 4960,3070"
|
||||
id="polyline126"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4783,2598 4783,3070"
|
||||
id="polyline128"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4606,2598 4606,3070"
|
||||
id="polyline130"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4429,2598 4429,3070"
|
||||
id="polyline132"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4251,2598 4251,3070"
|
||||
id="polyline134"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4074,2598 4074,3070"
|
||||
id="polyline136"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3898,2598 3898,3070"
|
||||
id="polyline138"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3720,2598 3720,3070"
|
||||
id="polyline140"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,2456 9212,3070"
|
||||
id="polyline142"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,2598 9212,2598"
|
||||
id="polyline144"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,3070 3720,3070 0,3070"
|
||||
id="polyline146"
|
||||
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
|
||||
<rect
|
||||
x="4021.6787"
|
||||
y="4311.5088"
|
||||
width="1668.8131"
|
||||
height="3296.2527"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke-width:11.23412323"
|
||||
id="rect150" />
|
||||
<polyline
|
||||
points="3118,3590 2834,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline178"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1322,3590 1039,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline180"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4913,3590 4629,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline184"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6519,3590 6236,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline186"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8314,3590 8031,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline188"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline204"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline206"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline208"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline210"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline212"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3507"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text246"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2161"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text248"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text250"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="9176"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text262"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7759"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text264"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6342"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text266"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4924"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text268"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3579"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text270"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6413"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text272"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7830"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text274"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4996"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text276"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,6614)"
|
||||
id="g282" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,4916.9545,5254.403)"
|
||||
id="g306">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4862.9551"
|
||||
y="4397.5566"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3503">P4 table</tspan></text>
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="3165"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text350"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text352"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="4960"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text356"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="6566"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text358"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="8362"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text360"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
x="1946.0945"
|
||||
y="6998.52"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text344-8-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3493-3-2" />
|
||||
</text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="4294.9473"
|
||||
y="4493.6152"
|
||||
width="1181"
|
||||
height="471.99994"
|
||||
rx="0"
|
||||
id="rect162-5" />
|
||||
<circle
|
||||
cx="5335.9546"
|
||||
cy="4720.2134"
|
||||
style="fill:#008200;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||
id="circle172-4"
|
||||
r="46.999996" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4815.9546"
|
||||
y="4773.6704"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6">P4 entry</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4925.6357"
|
||||
y="7173.5298"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6-4">Recursive</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4925.6357"
|
||||
y="7440.8149"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0-9"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6-4-4">Mapping</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="4296.5396"
|
||||
y="5560.2471"
|
||||
width="1182.4656"
|
||||
height="314.50067"
|
||||
rx="0"
|
||||
id="rect162-5-1" />
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.99999994,0.99999994,0,4915.3734,6277.1653)"
|
||||
id="g306-2">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-3"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3891.613"
|
||||
y="2954.5652"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-4"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
inkscape:transform-center-x="-789.14735"
|
||||
inkscape:transform-center-y="1300.106"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3503-4">111111111111111111111111111111111111</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:13.36426067px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 5603.8041,5874.9118 2568.0264,0 0,-2337.6838"
|
||||
id="path5364"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7481.9214"
|
||||
y="6083.3872"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0-6"
|
||||
style="font-style:normal;font-weight:normal;font-size:151.99998474px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46389771px;stroke-width:0"
|
||||
id="tspan3495-6-4-9">Offset in bytes</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5622.4961"
|
||||
y="5000.6953"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5">511</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5548.4839"
|
||||
y="6877.3652"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-1">0</tspan></text>
|
||||
</g>
|
||||
<polygon
|
||||
transform="matrix(-1.4130475,-0.00314156,0.00318097,-1.395531,10325.825,15480.806)"
|
||||
points="3150,6661 3308,6614 3150,6566 3150,6661 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-0-6" />
|
||||
<polygon
|
||||
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||
id="polygon232-2"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-646.4618,-1622.6914)" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76697159px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 5574.5569,4637.287 719.9709,0 0,3526.7653 -2765.702,0 0,-560.545 294.8212,0"
|
||||
id="path5354"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.54883385px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 3842.7968,5013.0895 -4222.39215,0 0,-2029.5495"
|
||||
id="path5356"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.67419624px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 2156.6937,2986.8005 0,2001.4832 1684.7642,0"
|
||||
id="path5358"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.88186646px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4696.3171,2980.021 0,502.6836 -1975.6967,0 0,1479.7303 1128.295,0"
|
||||
id="path5360"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:19.05912971px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 6962.3822,2986.9474 0,745.9077 -3590.1899,0 0,1203.077 448.1838,0"
|
||||
id="path5362"
|
||||
inkscape:connector-curvature="0" />
|
||||
<polygon
|
||||
points="3150,6661 3308,6614 3150,6566 3150,6661 "
|
||||
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||
id="polygon232-2-3"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-639.67391,-4258.2588)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,917 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="900.0036"
|
||||
height="145.45076"
|
||||
viewBox="-2141 2141 12027.883 1929.8575"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="x86_address_structure.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="716"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="84.319072"
|
||||
inkscape:cy="113.89333"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g242"
|
||||
units="px" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(331.35503,-225.83102)">
|
||||
<polyline
|
||||
points="3365,2598 3365,3070"
|
||||
id="polyline10"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="3189,2598 3189,3070"
|
||||
id="polyline12"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="3012,2598 3012,3070"
|
||||
id="polyline14"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="2834,2598 2834,3070"
|
||||
id="polyline16"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="2657,2598 2657,3070"
|
||||
id="polyline18"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="2480,2598 2480,3070"
|
||||
id="polyline20"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="2303,2598 2303,3070"
|
||||
id="polyline22"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="2125,2456 2125,3070"
|
||||
id="polyline24"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="1948,2598 1948,3070"
|
||||
id="polyline26"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="1772,2598 1772,3070"
|
||||
id="polyline28"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="1594,2598 1594,3070"
|
||||
id="polyline30"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="1417,2598 1417,3070"
|
||||
id="polyline32"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="1239,2598 1239,3070"
|
||||
id="polyline34"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="1063,2598 1063,3070"
|
||||
id="polyline36"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="886,2598 886,3070"
|
||||
id="polyline38"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="708,2456 708,3070"
|
||||
id="polyline40"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="3543,2456 3543,3070"
|
||||
id="polyline74"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="-2125,2598 3543,2598"
|
||||
id="polyline76"
|
||||
transform="matrix(0.52969427,0,0,1.0477876,1661.7387,-151.61082)"
|
||||
style="stroke-width:17.93889427;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="3543,3070 -1948,3070 -2125,3070"
|
||||
id="polyline78"
|
||||
transform="matrix(0.52765919,0,0,1.0477876,1673.588,-157.78641)"
|
||||
style="stroke-width:17.97345503;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="9035,2598 9035,3070"
|
||||
id="polyline80"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="8858,2598 8858,3070"
|
||||
id="polyline82"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="8681,2598 8681,3070"
|
||||
id="polyline84"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="8503,2598 8503,3070"
|
||||
id="polyline86"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="8326,2598 8326,3070"
|
||||
id="polyline88"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="8150,2598 8150,3070"
|
||||
id="polyline90"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="7972,2598 7972,3070"
|
||||
id="polyline92"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="7795,2456 7795,3070"
|
||||
id="polyline94"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="7617,2598 7617,3070"
|
||||
id="polyline96"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="7441,2598 7441,3070"
|
||||
id="polyline98"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="7264,2598 7264,3070"
|
||||
id="polyline100"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="7086,2598 7086,3070"
|
||||
id="polyline102"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="6909,2598 6909,3070"
|
||||
id="polyline104"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="6732,2598 6732,3070"
|
||||
id="polyline106"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="6555,2598 6555,3070"
|
||||
id="polyline108"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="6377,2456 6377,3070"
|
||||
id="polyline110"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="6200,2598 6200,3070"
|
||||
id="polyline112"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="6024,2598 6024,3070"
|
||||
id="polyline114"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="5846,2598 5846,3070"
|
||||
id="polyline116"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="5669,2598 5669,3070"
|
||||
id="polyline118"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="5491,2598 5491,3070"
|
||||
id="polyline120"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="5315,2598 5315,3070"
|
||||
id="polyline122"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="5138,2598 5138,3070"
|
||||
id="polyline124"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="4960,2409 4960,3070"
|
||||
id="polyline126"
|
||||
transform="matrix(1.0598349,0,0,0.97582891,-211.91044,63.126762)"
|
||||
style="stroke-width:25.36408615;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="4783,2598 4783,3070"
|
||||
id="polyline128"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="4606,2598 4606,3070"
|
||||
id="polyline130"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="4429,2598 4429,3070"
|
||||
id="polyline132"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="4251,2598 4251,3070"
|
||||
id="polyline134"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="4074,2598 4074,3070"
|
||||
id="polyline136"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="3898,2598 3898,3070"
|
||||
id="polyline138"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="3720,2598 3720,3070"
|
||||
id="polyline140"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:12.6820428;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="9212,2456 9212,3070"
|
||||
id="polyline142"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)"
|
||||
style="stroke-width:25.36408559;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="3543,2598 9212,2598"
|
||||
id="polyline144"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-207.27136,-151.61082)"
|
||||
style="stroke-width:17.75485992;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="9212,3070 3720,3070 0,3070"
|
||||
id="polyline146"
|
||||
transform="matrix(0.65314228,0,0,1.0477876,3541.5006,-157.82019)"
|
||||
style="stroke-width:18.04502869;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
<polyline
|
||||
points="3118,3590 2834,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline178"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="1322,3590 1039,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline180"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="4913,3590 4629,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline184"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="6519,3590 6236,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline186"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="8314,3590 8031,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline188"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline204"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline206"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline208"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline210"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<polyline
|
||||
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline212"
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-211.91044,-157.78641)" />
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3484.9529"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text246"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">32</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2305.7319"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text248"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">39</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="1965.1508"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text250"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">40</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="480.46173"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">48</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="9458.9111"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text262"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">0</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7965.6851"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text264"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">8</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6445.8828"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text266"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">16</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4978.1792"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text268"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">24</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3796.0706"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text270"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">31</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6782.5229"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text272"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">15</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="8167.3521"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text274"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">7</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5289.2969"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text276"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">23</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="1074.5779"
|
||||
y="4269.3325"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text348"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3501">P4 index</tspan>
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan5052" />
|
||||
</text>
|
||||
<text
|
||||
x="3124.5554"
|
||||
y="3774.0684"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text350"
|
||||
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430016)">9</text>
|
||||
<text
|
||||
x="1232.9951"
|
||||
y="3774.0684"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text352"
|
||||
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430016)">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="5016.1157"
|
||||
y="3774.0684"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text356"
|
||||
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430016)">9</text>
|
||||
<text
|
||||
x="6708.5088"
|
||||
y="3774.0684"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text358"
|
||||
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430016)">9</text>
|
||||
<text
|
||||
x="8601.1221"
|
||||
y="3774.0684"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text360"
|
||||
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke-width:0"
|
||||
transform="scale(1.0057325,0.99430016)">12</text>
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="809.5191"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">47</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-684.95245"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252-4"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">55</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-1034.7118"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252-4-7"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">56</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2201.2915"
|
||||
y="2529.5376"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252-4-7-3"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.7585907px;line-height:125%;font-family:'Helvetica Narrow';-inkscape-font-specification:'Helvetica Narrow, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">63</text>
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-2192.9027,-160.09714)"
|
||||
points="1322,3590 1039,3779"
|
||||
style="fill:none;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polyline180-0" />
|
||||
<text
|
||||
x="-736.70618"
|
||||
y="3771.7437"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text352-2"
|
||||
style="font-style:normal;font-weight:normal;font-size:160.17668152px;font-family:Helvetica;text-anchor:start;fill:#0000ff;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430016)">16</text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m -929.72072,3549.5659 0,431.3931"
|
||||
id="path4175"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 1039.4204,3550.0043 0,431.3933"
|
||||
id="path4175-3"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 2942.7798,3549.8477 0,431.3932"
|
||||
id="path4175-3-8"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4844.585,3551.0064 0,431.3933"
|
||||
id="path4175-3-8-6"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 6546.975,3551.1192 0,431.3932"
|
||||
id="path4175-3-8-6-3"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.08317852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 8449.877,3550.1734 0,431.3931"
|
||||
id="path4175-3-8-6-3-5"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
x="-980.58942"
|
||||
y="4269.3325"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text348-3"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3501-0">Sign extension</tspan>
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan5052-5" />
|
||||
</text>
|
||||
<text
|
||||
x="4862.6738"
|
||||
y="4269.3325"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text348-1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3501-5">P2 index</tspan>
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan5052-8" />
|
||||
</text>
|
||||
<text
|
||||
x="2919.9609"
|
||||
y="4269.3325"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text348-1-3"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3501-5-8">P3 index</tspan>
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan5052-8-0" />
|
||||
</text>
|
||||
<text
|
||||
x="6532.3008"
|
||||
y="4269.3325"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text348-1-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3501-5-9">P1 index</tspan>
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan5052-8-7" />
|
||||
</text>
|
||||
<text
|
||||
x="8392.0518"
|
||||
y="4269.3325"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text348-1-5-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="scale(1.0057325,0.99430017)"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:300.69586182px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3501-5-9-5">Offset</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="3365,2598 3365,3070"
|
||||
id="polyline10-3" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="3189,2598 3189,3070"
|
||||
id="polyline12-5" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="3012,2598 3012,3070"
|
||||
id="polyline14-6" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="2834,2598 2834,3070"
|
||||
id="polyline16-9" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="2657,2598 2657,3070"
|
||||
id="polyline18-9" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="2480,2598 2480,3070"
|
||||
id="polyline20-8" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="2303,2598 2303,3070"
|
||||
id="polyline22-2" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:25.36408559;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="2125,2456 2125,3070"
|
||||
id="polyline24-5" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="1948,2598 1948,3070"
|
||||
id="polyline26-2" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="1772,2598 1772,3070"
|
||||
id="polyline28-1" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="1594,2598 1594,3070"
|
||||
id="polyline30-5" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="1417,2598 1417,3070"
|
||||
id="polyline32-9" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="1239,2598 1239,3070"
|
||||
id="polyline34-3" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="1063,2598 1063,3070"
|
||||
id="polyline36-9" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:12.6820428;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="886,2598 886,3070"
|
||||
id="polyline38-2" />
|
||||
<polyline
|
||||
transform="matrix(1.0598349,0,0,1.0477876,-3218.4788,-157.56699)"
|
||||
style="fill:none;stroke:#000000;stroke-width:25.36408559;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="708,2456 708,3070"
|
||||
id="polyline40-6" />
|
||||
<polyline
|
||||
style="fill:none;stroke:#000000;stroke-width:17.93889618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="-2125,2598 3543,2598"
|
||||
id="polyline76-6"
|
||||
transform="matrix(0.52969421,0,0,1.0477875,-1349.4687,-150.28545)" />
|
||||
<polyline
|
||||
style="fill:none;stroke:#000000;stroke-width:17.97345674;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
points="3543,3070 -1948,3070 -2125,3070"
|
||||
id="polyline78-8"
|
||||
transform="matrix(0.52765914,0,0,1.0477875,-1332.9803,-157.56699)" />
|
||||
<polyline
|
||||
transform="matrix(1.4116086,0,0,1.0450205,-12466.637,-150.55138)"
|
||||
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||
style="fill:none;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polyline204-3" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,404 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="617.39313"
|
||||
height="215.65028"
|
||||
viewBox="-2141 2141 8251.0033 2861.2729"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="cyclic_mapping_inactive_table.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="998"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="1"
|
||||
fit-margin-left="1"
|
||||
fit-margin-right="1"
|
||||
fit-margin-bottom="1"
|
||||
inkscape:zoom="1.4142136"
|
||||
inkscape:cx="345.80952"
|
||||
inkscape:cy="50.097583"
|
||||
inkscape:window-x="1080"
|
||||
inkscape:window-y="413"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g242"
|
||||
units="in" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(-1516.6308,-1533.1047)">
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,6614)"
|
||||
id="g282" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
x="1946.0945"
|
||||
y="6998.52"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text344-8-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3493-3-2" />
|
||||
</text>
|
||||
<rect
|
||||
x="3090.1064"
|
||||
y="4497.9272"
|
||||
width="1182.895"
|
||||
height="1367.9473"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:6.0930233;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect150-8" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4341.8867"
|
||||
y="5887.8105"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-5-9"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-5-6">0</tspan></text>
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-1,1,0,3712.0549,5414.9455)"
|
||||
id="g306-4">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-1"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3676.9697"
|
||||
y="4404.876"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3503-3">P4 table</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="3090.0466"
|
||||
y="4500.9346"
|
||||
width="1181"
|
||||
height="471.99997"
|
||||
rx="0"
|
||||
id="rect162-5-4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3611.0537"
|
||||
y="4780.9897"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-1"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3495-6-41">P4 entry</tspan></text>
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.99999992,0.9999999,0,2296.6633,6284.4847)"
|
||||
id="g306-2-3" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4405.4561"
|
||||
y="4997.4966"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-8">511</tspan></text>
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="4117.4102"
|
||||
cy="4723.3125"
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="circle172-4-3" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3674.7217"
|
||||
y="4178.6655"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3503-3-0">active</tspan></text>
|
||||
<rect
|
||||
x="5370.3823"
|
||||
y="4498.0884"
|
||||
width="1182.895"
|
||||
height="1367.9473"
|
||||
rx="0"
|
||||
style="fill:#dfdfbe;fill-opacity:1;stroke:#000000;stroke-width:6.0930233;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect150-8-9" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6622.1631"
|
||||
y="5887.9717"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-5-9-3"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-5-6-5">0</tspan></text>
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-1,1,0,5992.3314,5415.1068)"
|
||||
id="g306-4-9">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-1-8"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5957.2456"
|
||||
y="4405.0371"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8-0"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3503-3-2">P4 table</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="5370.3228"
|
||||
y="4501.0957"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect162-5-4-4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5891.3296"
|
||||
y="4781.1509"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-1-6"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3495-6-41-6">P4 entry</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6685.7324"
|
||||
y="4997.6577"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-6-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-8-1">511</tspan></text>
|
||||
<polygon
|
||||
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="polygon232-2-7-7-1-1"
|
||||
transform="translate(2076.0084,-749.88898)" />
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="6397.6865"
|
||||
cy="4723.4736"
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="circle172-4-3-6" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5954.9976"
|
||||
y="4178.8267"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8-5-9"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3503-3-0-2">inactive</tspan></text>
|
||||
<rect
|
||||
x="1019.4597"
|
||||
y="4821.9668"
|
||||
width="1487.6132"
|
||||
height="469.84174"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:4.0044775;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect150-8-0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="1642.8574"
|
||||
y="5136.835"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-1-4"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3495-6-41-8">CR3 register</tspan></text>
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="2349.6458"
|
||||
cy="5076.04"
|
||||
style="fill:#969696;fill-opacity:1;stroke:#969696;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="circle172-4-3-2" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#969696;stroke-width:9.42606354px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 2349.6459,5073.5559 359.3939,0 0,777.3949 255.3589,0"
|
||||
id="path4612"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4118.0344,4723.1182 672.1066,0 0,1137.6237 494.8846,0"
|
||||
id="path4614"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:9.51690578px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 6398.2284,4730.6471 434.6959,0 0,1347.5425 -4005.8897,0 0,-197.3547 140.4403,0"
|
||||
id="path4641"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<polygon
|
||||
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="polygon232-2-7-7-1-1-7"
|
||||
transform="translate(-214.18472,-748.49788)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 18 KiB |
@@ -1,45 +1,38 @@
|
||||
+++
|
||||
title = "Remap the Kernel"
|
||||
date = "2016-01-01"
|
||||
weight = 7
|
||||
path = "remap-the-kernel"
|
||||
aliases = ["remap-the-kernel.html"]
|
||||
date = 2016-01-01
|
||||
template = "edition-1/page.html"
|
||||
[extra]
|
||||
updated = "2016-03-06"
|
||||
+++
|
||||
|
||||
In this post we will create a new page table to map the kernel sections correctly. Therefor we will extend the paging module to support modifications of _inactive_ page tables as well. Then we will switch to the new table and secure our kernel stack by creating a guard page.
|
||||
In this post we will create a new page table to map the kernel sections correctly. Therefore we will extend the paging module to support modifications of _inactive_ page tables as well. Then we will switch to the new table and secure our kernel stack by creating a guard page.
|
||||
|
||||
<!--more-->
|
||||
<!-- more -->
|
||||
|
||||
As always, you can find the source code on [Github]. Don't hesitate to file issues there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
|
||||
As always, you can find the source code on [GitHub]. Don't hesitate to file issues there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
|
||||
|
||||
[Github]: https://github.com/phil-opp/blog_os/tree/remap_the_kernel
|
||||
|
||||
_Updates_:
|
||||
|
||||
- The `AreaFrameAllocator` [was broken][areaframeallocator broken] after switching to a new table. To fix this, we added the [Fixing the Frame Allocator] section and updated the [linker script][linker script update]. For a complete set of changes see [#131] and [this diff][#131-changes].
|
||||
- We [fixed a bug][#141] in iterating over a section's frames. Therefor we added a `Frame::range_inclusive` function and updated the [Remapping the Kernel section]. For a complete list of changes check out [this diff][#141-changes].
|
||||
|
||||
[areaframeallocator broken]: https://github.com/phil-opp/blog_os/issues/126
|
||||
[Fixing the Frame Allocator]: #fixing-the-frame-allocator
|
||||
[linker script update]: #page-align-sections
|
||||
[#131]: https://github.com/phil-opp/blog_os/pull/131
|
||||
[#131-changes]: https://github.com/phil-opp/blog_os/compare/75aa669cdbb427c7bf0485c68692d243065cd3e9...635f7d3f9dced752f84d429e1d51f5c2b29854e3
|
||||
[#141]: https://github.com/phil-opp/blog_os/pull/141
|
||||
[Remapping the Kernel section]: #remapping-the-kernel
|
||||
[#141-changes]: https://github.com/phil-opp/blog_os/commit/03ed3ce9a0758bf0d14a13144892c731216e25c6
|
||||
[GitHub]: https://github.com/phil-opp/blog_os/tree/first_edition_post_7
|
||||
|
||||
## Motivation
|
||||
|
||||
In the [previous post], we had a strange bug in the `unmap` function. Its reason was a silent stack overflow, which corrupted the page tables. Fortunately, our kernel stack is right above the page tables so that we noticed the overflow relatively quickly. This won't be the case when we add threads with new stacks in the future. Then a silent stack overflow could overwrite some data without us noticing. But eventually some completely unrelated function fails because a variable changed its value.
|
||||
[previous post]: {{% relref "2015-12-09-modifying-page-tables.md" %}}
|
||||
|
||||
[previous post]: @/edition-1/posts/06-page-tables/index.md
|
||||
|
||||
As you can imagine, these kinds of bugs are horrendous to debug. For that reason we will create a new hierarchical page table in this post, which has _guard page_ below the stack. A guard page is basically an unmapped page that causes a page fault when accessed. Thus we can catch stack overflows right when they happen.
|
||||
|
||||
Also, we will use the [information about kernel sections] to map the various sections individually instead of blindly mapping the first gigabyte. To improve safety even further, we will set the correct page table flags for the various sections. Thus it won't be possible to modify the contents of `.text` or to execute code from `.data` anymore.
|
||||
[information about kernel sections]: {{% relref "2015-11-15-allocating-frames.md#kernel-elf-sections" %}}
|
||||
|
||||
[information about kernel sections]: @/edition-1/posts/05-allocating-frames/index.md#kernel-elf-sections
|
||||
|
||||
## Preparation
|
||||
There are many things that can go wrong when we switch to a new table. Therefore it's a good idea to [set up a debugger][set up gdb]. You should not need it when you follow this post, but it's good to know how to debug a problem when it occurs[^fn-debug-notes].
|
||||
[set up gdb]: {{% relref "set-up-gdb.md" %}}
|
||||
[^fn-debug-notes]: For this post the most useful GDB command is probably `p/x *((long int*)0xfffffffffffff000)@512`. It prints all entries of the recursively mapped P4 table by interpreting it as an array of 512 long ints (the `@512` is GDB's array syntax). Of course you can also print other tables by adjusting the address.
|
||||
|
||||
[set up gdb]: @/edition-1/extra/set-up-gdb/index.md
|
||||
|
||||
We also update the `Page` and `Frame` types to make our lives easier. The `Page` struct gets some derived traits:
|
||||
|
||||
@@ -52,9 +45,11 @@ pub struct Page {
|
||||
}
|
||||
```
|
||||
By making it [Copy][Copy trait], we can still use it after passing it to functions such as `map_to`. We also make the `Page::containing_address` public (if it isn't already).
|
||||
|
||||
[Copy trait]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
|
||||
|
||||
The `Frame` type gets a `clone` method too, but it does not implement the [Clone trait]:
|
||||
|
||||
[Clone trait]: https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html
|
||||
|
||||
```rust
|
||||
@@ -76,9 +71,7 @@ We can't do that for a `Frame`. If we wanted to be sure that a given frame is un
|
||||
## Recap: The Paging Module
|
||||
This post builds upon the post about [page tables][previous post], so let's start by quickly recapitulating what we've done there.
|
||||
|
||||
We created a `memory::paging` module, which reads and modifies the hierarchical page table through recursive mapping. The owner of the active P4 table and thus all subtables is an `ActivePageTable`[^fn-apt-renamed] struct, which must be instantiated only once.
|
||||
|
||||
[^fn-apt-renamed]: It was renamed in [#88](https://github.com/phil-opp/blog_os/pull/88). The previous name was `RecursivePageTable`.
|
||||
We created a `memory::paging` module, which reads and modifies the hierarchical page table through recursive mapping. The owner of the active P4 table and thus all subtables is an `ActivePageTable` struct, which must be instantiated only once.
|
||||
|
||||
The `ActivePageTable` struct provides the following interface:
|
||||
|
||||
@@ -287,7 +280,8 @@ pub fn map_table_frame(&mut self,
|
||||
}
|
||||
```
|
||||
This function interprets the given frame as a page table frame and returns a `Table` reference. We return a table of level 1 because it [forbids calling the `next_table` methods][some clever solution]. Calling `next_table` must not be possible since it's not a page of the recursive mapping. To be able to return a `Table<Level1>`, we need to make the `Level1` enum in `memory/paging/table.rs` public.
|
||||
[some clever solution]: {{% relref "2015-12-09-modifying-page-tables.md#some-clever-solution" %}}
|
||||
|
||||
[some clever solution]: @/edition-1/posts/06-page-tables/index.md#some-clever-solution
|
||||
|
||||
|
||||
The `unsafe` block is safe since the `VirtualAddress` returned by the `map` function is always valid and the type cast just reinterprets the frame's content.
|
||||
@@ -333,7 +327,8 @@ impl InactivePageTable {
|
||||
}
|
||||
```
|
||||
We added two new arguments, `active_table` and `temporary_page`. We need an [inner scope] to ensure that the `table` variable is dropped before we try to unmap the temporary page again. This is required since the `table` variable exclusively borrows `temporary_page` as long as it's alive.
|
||||
[inner scope]: http://rustbyexample.com/variable_bindings/scope.html
|
||||
|
||||
[inner scope]: https://doc.rust-lang.org/rust-by-example/variable_bindings/scope.html
|
||||
|
||||
Now we are able to create valid inactive page tables, which are zeroed and recursively mapped. But we still can't modify them. To resolve this problem, we need to look at recursive mapping again.
|
||||
|
||||
@@ -342,11 +337,11 @@ Recursive mapping works by mapping the last P4 entry to the P4 table itself. Thu
|
||||
|
||||
For example, accessing a P3 table requires lopping three times:
|
||||
|
||||

|
||||

|
||||
|
||||
We can use the same mechanism to access inactive tables. The trick is to change the recursive mapping of the active P4 table to point to the inactive P4 table:
|
||||
|
||||

|
||||

|
||||
|
||||
Now the inactive table can be accessed exactly as the active table, even the magic addresses are the same. This allows us to use the `ActivePageTable` interface and the existing mapping methods for inactive tables, too. Note that everything besides the recursive mapping continues to work exactly as before since we've never changed the active table in the CPU.
|
||||
|
||||
@@ -359,12 +354,11 @@ pub fn with<F>(&mut self,
|
||||
f: F)
|
||||
where F: FnOnce(&mut ActivePageTable)
|
||||
{
|
||||
use x86::tlb;
|
||||
let flush_tlb = || unsafe { tlb::flush_all() };
|
||||
use x86_64::instructions::tlb;
|
||||
|
||||
// overwrite recursive mapping
|
||||
self.p4_mut()[511].set(table.p4_frame.clone(), PRESENT | WRITABLE);
|
||||
flush_tlb();
|
||||
tlb::flush_all();
|
||||
|
||||
// execute f in the new context
|
||||
f(self);
|
||||
@@ -373,12 +367,13 @@ pub fn with<F>(&mut self,
|
||||
}
|
||||
```
|
||||
It overwrites the 511th P4 entry and points it to the inactive table frame. Then it flushes the [translation lookaside buffer (TLB)][TLB], which still contains some old translations. We need to flush all pages that are part of the recursive mapping, so the easiest way is to flush the TLB completely.
|
||||
[TLB]: http://wiki.osdev.org/TLB
|
||||
|
||||
[TLB]: https://wiki.osdev.org/TLB
|
||||
|
||||
|
||||
Now that the recursive mapping points to the given inactive table, we execute the closure in the new context. The closure can call all active table methods such as `translate` or `map_to`. It could even call `with` again and chain another inactive table! Wait… that would not work:
|
||||
|
||||

|
||||

|
||||
|
||||
Here the closure called `with` again and thus changed the recursive mapping of the inactive table to point to a second inactive table. Now we want to modify the P1 of the _second_ inactive table, but instead we land on the P1 of the _first_ inactive table since we never follow the pointer to the second table. Only when modifying the P2, P3, or P4 table we really access the second inactive table. This inconsistency would break our mapping functions completely.
|
||||
|
||||
@@ -453,6 +448,7 @@ impl ActivePageTable {
|
||||
}
|
||||
```
|
||||
The [Deref] and [DerefMut] implementations allow us to use the `ActivePageTable` exactly as before, for example we still can call `map_to` on it (because of [deref coercions]). But the closure called in the `with` function can no longer invoke `with` again. The reason is that we changed the type of the generic `F` parameter a bit: Instead of an `ActivePageTable`, the closure just gets a `Mapper` as argument.
|
||||
|
||||
[Deref]: https://doc.rust-lang.org/nightly/core/ops/trait.Deref.html
|
||||
[DerefMut]: https://doc.rust-lang.org/nightly/core/ops/trait.DerefMut.html
|
||||
[deref coercions]: https://doc.rust-lang.org/nightly/book/deref-coercions.html
|
||||
@@ -463,24 +459,24 @@ Right now, the `with` function overwrites the recursive mapping and calls the cl
|
||||
To backup the physical P4 frame of the active table, we can either read it from the 511th P4 entry (before we change it) or from the CR3 control register directly. We will do the latter as it should be faster and we already have a external crate that makes it easy:
|
||||
|
||||
```rust
|
||||
use x86::controlregs;
|
||||
use x86_64::registers::control_regs;
|
||||
let backup = Frame::containing_address(
|
||||
unsafe { controlregs::cr3() } as usize
|
||||
unsafe { control_regs::cr3() } as usize
|
||||
);
|
||||
```
|
||||
Why is it unsafe? Because reading the CR3 register leads to a CPU exception if the processor is not running in kernel mode ([Ring 0]). But this code will always run in kernel mode, so the `unsafe` block is completely safe here.
|
||||
|
||||
[Ring 0]: http://wiki.osdev.org/Security#Low-level_Protection_Mechanisms
|
||||
[Ring 0]: https://wiki.osdev.org/Security#Low-level_Protection_Mechanisms
|
||||
|
||||
Now that we have a backup of the original P4 frame, we need a way to restore it after the closure has run. So we need to somehow modify the 511th entry of the original P4 frame, which is still the active table in the CPU. But we can't access it because the recursive mapping now points to the inactive table:
|
||||
|
||||

|
||||

|
||||
|
||||
It's just not possible to access the active P4 entry in 4 steps, so we can't reach it through the 4-level page table.
|
||||
|
||||
We could try to overwrite the recursive mapping of the _inactive_ P4 table and point it back to the original P4 frame:
|
||||
|
||||

|
||||

|
||||
|
||||
Now we can reach the active P4 entry in 4 steps and could restore the original mapping in the active table. But this hack has a drawback: The inactive table is now invalid since it is no longer recursive mapped. We would need to fix it by using a temporary page again (as above).
|
||||
|
||||
@@ -496,26 +492,26 @@ pub fn with<F>(&mut self,
|
||||
f: F)
|
||||
where F: FnOnce(&mut Mapper)
|
||||
{
|
||||
use x86::{controlregs, tlb};
|
||||
let flush_tlb = || unsafe { tlb::flush_all() };
|
||||
use x86_64::instructions::tlb;
|
||||
use x86_64::registers::control_regs;
|
||||
|
||||
{
|
||||
let backup = Frame::containing_address(
|
||||
unsafe { controlregs::cr3() } as usize);
|
||||
control_regs::cr3().0 as usize);
|
||||
|
||||
// map temporary_page to current p4 table
|
||||
let p4_table = temporary_page.map_table_frame(backup.clone(), self);
|
||||
|
||||
// overwrite recursive mapping
|
||||
self.p4_mut()[511].set(table.p4_frame.clone(), PRESENT | WRITABLE);
|
||||
flush_tlb();
|
||||
tlb::flush_all();
|
||||
|
||||
// execute f in the new context
|
||||
f(self);
|
||||
|
||||
// restore recursive mapping to original p4 table
|
||||
p4_table[511].set(backup, PRESENT | WRITABLE);
|
||||
flush_tlb();
|
||||
tlb::flush_all();
|
||||
}
|
||||
|
||||
temporary_page.unmap(self);
|
||||
@@ -557,7 +553,8 @@ pub fn remap_the_kernel<A>(allocator: &mut A, boot_info: &BootInformation)
|
||||
First, we create a temporary page at page number `0xcafebabe`. We could use `0xdeadbeaf` or `0x123456789` as well, as long as the page is unused. The `active_table` and the `new_table` are created using their constructor functions.
|
||||
|
||||
Then we use the `with` function to temporary change the recursive mapping and execute the closure as if the `new_table` were active. This allows us to map the sections in the new table without changing the active mapping. To get the kernel sections, we use the [Multiboot information structure].
|
||||
[Multiboot information structure]: {{% relref "2015-11-15-allocating-frames.md#the-multiboot-information-structure" %}}
|
||||
|
||||
[Multiboot information structure]: @/edition-1/posts/05-allocating-frames/index.md#the-multiboot-information-structure
|
||||
|
||||
Let's resolve the above `TODO` by identity mapping the sections:
|
||||
|
||||
@@ -569,7 +566,7 @@ for section in elf_sections_tag.sections() {
|
||||
// section is not loaded to memory
|
||||
continue;
|
||||
}
|
||||
assert!(address % PAGE_SIZE == 0,
|
||||
assert!(section.start_address() % PAGE_SIZE == 0,
|
||||
"sections need to be page aligned");
|
||||
|
||||
println!("mapping section at addr: {:#x}, size: {:#x}",
|
||||
@@ -579,7 +576,7 @@ for section in elf_sections_tag.sections() {
|
||||
|
||||
let start_frame = Frame::containing_address(section.start_address());
|
||||
let end_frame = Frame::containing_address(section.end_address() - 1);
|
||||
for frame in Frame::range_inclusive(start_frame, end_frame)
|
||||
for frame in Frame::range_inclusive(start_frame, end_frame) {
|
||||
mapper.identity_map(frame, flags, allocator);
|
||||
}
|
||||
}
|
||||
@@ -625,7 +622,7 @@ impl Iterator for FrameIter {
|
||||
Instead of creating a custom iterator, we could have used the [Range] struct of the standard library. But it requires that we implement the [One] and [Add] traits for `Frame`. Then every module could perform arithmetic operations on frames, for example `let frame3 = frame1 + frame2`. This would violate our safety invariants because `frame3` could be already in use. The `range_inclusive` function does not have these problems because it is only available inside the `memory` module.
|
||||
|
||||
[Range]: https://doc.rust-lang.org/nightly/core/ops/struct.Range.html
|
||||
[One]: https://doc.rust-lang.org/nightly/core/num/trait.One.html
|
||||
[One]: https://doc.rust-lang.org/1.10.0/core/num/trait.One.html
|
||||
[Add]: https://doc.rust-lang.org/nightly/core/ops/trait.Add.html
|
||||
|
||||
### Page Align Sections
|
||||
@@ -643,13 +640,16 @@ SECTIONS {
|
||||
}
|
||||
```
|
||||
The `.` is the “current location counter” and represents the current virtual address. At the beginning of the `SECTIONS` tag we set it to `1M`, so our kernel starts at 1MiB. We use the [ALIGN][linker align] function to align the current location counter to the next `4K` boundary (`4K` is the page size). Thus the end of the `.text` section – and the beginning of the next section – are page aligned.
|
||||
[linker align]: http://www.math.utah.edu/docs/info/ld_3.html#SEC12
|
||||
|
||||
[linker align]: https://www.math.utah.edu/docs/info/ld_3.html#SEC12
|
||||
|
||||
To put all sections on their own page, we add the `ALIGN` statement to all of them:
|
||||
|
||||
```
|
||||
/* src/arch/x86_64/linker.ld */
|
||||
|
||||
ENTRY(start)
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
@@ -679,6 +679,18 @@ SECTIONS {
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.got :
|
||||
{
|
||||
*(.got)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.got.plt :
|
||||
{
|
||||
*(.got.plt)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.data.rel.ro : ALIGN(4K) {
|
||||
*(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*)
|
||||
. = ALIGN(4K);
|
||||
@@ -693,7 +705,7 @@ SECTIONS {
|
||||
Instead of page aligning the `.multiboot_header` section, we merge it into the `.rodata` section. That way, we don't waste a whole page for the few bytes of the Multiboot header. We could merge it into any section, but `.rodata` fits best because it has the same flags (neither writable nor executable). The Multiboot header still needs to be at the beginning of the file, so `.rodata` must be our first section now.
|
||||
|
||||
### Testing it
|
||||
Time to test it! We reexport the `remap_the_kernel` function from the memory module and call it from `rust_main`:
|
||||
Time to test it! We re-export the `remap_the_kernel` function from the memory module and call it from `rust_main`:
|
||||
|
||||
```rust
|
||||
// in src/memory/mod.rs
|
||||
@@ -755,21 +767,24 @@ We do this in a new `ActivePageTable::switch` method:
|
||||
// in `impl ActivePageTable` in src/memory/paging/mod.rs
|
||||
|
||||
pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable {
|
||||
use x86::controlregs;
|
||||
use x86_64::PhysicalAddress;
|
||||
use x86_64::registers::control_regs;
|
||||
|
||||
let old_table = InactivePageTable {
|
||||
p4_frame: Frame::containing_address(
|
||||
unsafe { controlregs::cr3() } as usize
|
||||
control_regs::cr3().0 as usize
|
||||
),
|
||||
};
|
||||
unsafe {
|
||||
controlregs::cr3_write(new_table.p4_frame.start_address() as u64);
|
||||
control_regs::cr3_write(PhysicalAddress(
|
||||
new_table.p4_frame.start_address() as u64));
|
||||
}
|
||||
old_table
|
||||
}
|
||||
```
|
||||
This function activates the given inactive table and returns the previous active table as a `InactivePageTable`. We don't need to flush the TLB here, as the CPU does it automatically when the P4 table is switched. In fact, the `tlb::flush_all` function, which we used above, does nothing more than [reloading the CR3 register].
|
||||
[reloading the CR3 register]: https://github.com/gz/rust-x86/blob/master/src/tlb.rs#L19
|
||||
|
||||
[reloading the CR3 register]: https://docs.rs/x86_64/0.1.2/src/x86_64/instructions/tlb.rs.html#11-14
|
||||
|
||||
Now we are finally able to switch to the new table. We do it by adding the following lines to our `remap_the_kernel` function:
|
||||
|
||||
@@ -789,8 +804,7 @@ Let's cross our fingers and run it…
|
||||
… and it fails with a boot loop.
|
||||
|
||||
### Debugging
|
||||
A QEMU boot load indicates that some CPU exception occured. We can see all thrown CPU exception by starting QEMU with `-d int` (as described [here][qemu debugging]):
|
||||
[qemu debugging]: {{% relref "2015-09-02-set-up-rust.md#debugging" %}}
|
||||
A QEMU boot loop indicates that some CPU exception occurred. We can see all thrown CPU exception by starting QEMU with `-d int`:
|
||||
|
||||
```bash
|
||||
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso
|
||||
@@ -803,26 +817,27 @@ check_exception old: 0xffffffff new 0xe
|
||||
These lines are the important ones. We can read many useful information from them:
|
||||
|
||||
- `v=0e`: An exception with number `0xe` occurred, which is a page fault according to the [OSDev Wiki][osdev exception overview].
|
||||
[osdev exception overview]: http://wiki.osdev.org/Exceptions
|
||||
[page fault]: http://wiki.osdev.org/Exceptions#Page_Fault
|
||||
|
||||
- `e=0002`: The CPU set an [error code][page fault error code], which tells us why the exception occurred. The `0x2` bit tells us that it was caused by a write operation. And since the `0x1` bit is not set, the target page was not present.
|
||||
[page fault error code]: http://wiki.osdev.org/Exceptions#Error_code
|
||||
|
||||
- `IP=0008:000000000010ab97` or `pc=000000000010ab97`: The program counter register tells us that the exception occurred when the CPU tried to execute the instruction at `0x10ab97`. We can disassemble this address to see the corresponding function. The `0008:` prefix in `IP` indicates the code [GDT segment].
|
||||
[GDT segment]: {{% relref "2015-08-25-entering-longmode.md#loading-the-gdt" %}}
|
||||
|
||||
- `SP=0010:00000000001182d0`: The stack pointer was `0x1182d0` (the `0010:` prefix indicates the data [GDT segment]). This tells us if it the stack overflowed.
|
||||
|
||||
- `CR2=00000000000b8f00`: Finally the most useful register. It tells us which virtual address caused the page fault. In our case it's `0xb8f00`, which is part of the [VGA text buffer].
|
||||
[VGA text buffer]: {{% relref "2015-10-23-printing-to-screen.md#the-vga-text-buffer" %}}
|
||||
|
||||
[osdev exception overview]: https://wiki.osdev.org/Exceptions
|
||||
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||
[page fault error code]: https://wiki.osdev.org/Exceptions#Error_code
|
||||
[GDT segment]: @/edition-1/posts/02-entering-longmode/index.md#loading-the-gdt
|
||||
[VGA text buffer]: @/edition-1/posts/04-printing-to-screen/index.md#the-vga-text-buffer
|
||||
|
||||
So let's find out which function caused the exception:
|
||||
|
||||
```
|
||||
objdump -d build/kernel-x86_64.bin | grep -B100 "10ab97"
|
||||
```
|
||||
We disassemble our kernel and search for `10ab97`. The `-B100` option prints the 100 preceeding lines too. The output tells us the responsible function:
|
||||
We disassemble our kernel and search for `10ab97`. The `-B100` option prints the 100 preceding lines too. The output tells us the responsible function:
|
||||
|
||||
```
|
||||
...
|
||||
@@ -833,6 +848,7 @@ We disassemble our kernel and search for `10ab97`. The `-B100` option prints the
|
||||
10ab97: 66 89 14 48 mov %dx,(%rax,%rcx,2)
|
||||
```
|
||||
The reason for the cryptical function name is Rust's [name mangling]. But we can identity the `vga_buffer::Writer::write_byte` function nonetheless.
|
||||
|
||||
[name mangling]: https://en.wikipedia.org/wiki/Name_mangling
|
||||
|
||||
So the reason for the page fault is that the `write_byte` function tried to write to the VGA text buffer at `0xb8f00`. Of course this provokes a page fault: We forgot to identity map the VGA buffer in the new page table.
|
||||
@@ -866,7 +882,7 @@ Now we should see the `NEW TABLE!!!` message (and also the `It did not crash!` l
|
||||
### Fixing the Frame Allocator
|
||||
The same problem as above occurs when we try to use our [AreaFrameAllocator] again. Try to add the following to `rust_main` after switching to the new table:
|
||||
|
||||
[AreaFrameAllocator]: http://os.phil-opp.com/allocating-frames.html#the-allocator
|
||||
[AreaFrameAllocator]: @/edition-1/posts/05-allocating-frames/index.md#the-allocator
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
@@ -963,20 +979,22 @@ pub fn remap_the_kernel<A>(allocator: &mut A, boot_info: &BootInformation)
|
||||
|
||||
But when we test it now, we get a page fault again. We can use the same technique as above to get the responsible function. I won't bother you with the QEMU output and just tell you the results:
|
||||
|
||||
This time the responsible function is `controlregs::cr3_write()` itself. From the [error code][page fault error code] we learn that it was a page protection violation and caused by “reading a 1 in a reserved field”. So the page table had some reserved bit set that should be always 0. It must be the `NO_EXECUTE` flag, since it's the only new bit that we set in the page table.
|
||||
This time the responsible function is `control_regs::cr3_write()` itself. From the [error code][page fault error code] we learn that it was a page protection violation and caused by “reading a 1 in a reserved field”. So the page table had some reserved bit set that should be always 0. It must be the `NO_EXECUTE` flag, since it's the only new bit that we set in the page table.
|
||||
|
||||
### The NXE Bit
|
||||
The reason is that the `NO_EXECUTE` bit must only be used when the `NXE` bit in the [Extended Feature Enable Register] \(EFER) is set. That register is similar to Rust's feature gating and can be used to enable all sorts of advanced CPU features. Since the `NXE` bit is off by default, we caused a page fault when we added the `NO_EXECUTE` bit to the page table.
|
||||
|
||||
[Extended Feature Enable Register]: https://en.wikipedia.org/wiki/Control_register#EFER
|
||||
|
||||
So we need to enable the `NXE` bit. Therefor we use Gerd Zellweger's awesome [x86][rust-x86] crate again:
|
||||
[rust-x86]: https://github.com/gz/rust-x86
|
||||
So we need to enable the `NXE` bit. For that we use the [x86_64 crate] again:
|
||||
|
||||
[x86_64 crate]: https://docs.rs/x86_64
|
||||
|
||||
```rust
|
||||
// in lib.rs
|
||||
|
||||
fn enable_nxe_bit() {
|
||||
use x86::msr::{IA32_EFER, rdmsr, wrmsr};
|
||||
use x86_64::registers::msr::{IA32_EFER, rdmsr, wrmsr};
|
||||
|
||||
let nxe_bit = 1 << 11;
|
||||
unsafe {
|
||||
@@ -996,10 +1014,9 @@ Right now, we are still able to modify the `.code` and `.rodata` sections, even
|
||||
// in lib.rs
|
||||
|
||||
fn enable_write_protect_bit() {
|
||||
use x86::controlregs::{cr0, cr0_write};
|
||||
use x86_64::registers::control_regs::{cr0, cr0_write, Cr0};
|
||||
|
||||
let wp_bit = 1 << 16;
|
||||
unsafe { cr0_write(cr0() | wp_bit) };
|
||||
unsafe { cr0_write(cr0() | Cr0::WRITE_PROTECT) };
|
||||
}
|
||||
```
|
||||
The `cr0` functions are unsafe because accessing the `CR0` register is only allowed in kernel mode.
|
||||
@@ -1010,7 +1027,8 @@ If we haven't forgotten to set the `WRITABLE` flag somewhere, it should still wo
|
||||
The final step is to create a guard page for our kernel stack.
|
||||
|
||||
The decision to place the kernel stack right above the page tables was already useful to detect a silent stack overflow in the [previous post][silent stack overflow]. Now we profit from it again. Let's look at our assembly `.bss` section again to understand why:
|
||||
[silent stack overflow]: {{% relref "2015-12-09-modifying-page-tables.md#translate" %}}
|
||||
|
||||
[silent stack overflow]: @/edition-1/posts/06-page-tables/index.md
|
||||
|
||||
```nasm
|
||||
; in src/arch/x86_64/boot.asm
|
||||
@@ -1024,7 +1042,7 @@ p3_table:
|
||||
p2_table:
|
||||
resb 4096
|
||||
stack_bottom:
|
||||
resb 4096 * 2
|
||||
resb 4096 * 4
|
||||
stack_top:
|
||||
```
|
||||
|
||||
@@ -1070,7 +1088,11 @@ Unfortunately stack probes require compiler support. They already work on Window
|
||||
|
||||
## What's next?
|
||||
Now that we have a (mostly) safe kernel stack and a working page table module, we can add a virtual memory allocator. The [next post] will explore Rust's allocator API and create a very basic allocator. At the end of that post, we will be able to use Rust's allocation and collections types such as [Box], [Vec], or even [BTreeMap].
|
||||
[next post]: {{% relref "2016-04-11-kernel-heap.md" %}}
|
||||
|
||||
[next post]: @/edition-1/posts/08-kernel-heap/index.md
|
||||
[Box]: https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html
|
||||
[Vec]: https://doc.rust-lang.org/nightly/collections/vec/struct.Vec.html
|
||||
[BTreeMap]: https://doc.rust-lang.org/nightly/collections/struct.BTreeMap.html
|
||||
[Vec]: https://doc.rust-lang.org/1.10.0/collections/vec/struct.Vec.html
|
||||
[BTreeMap]: https://doc.rust-lang.org/1.10.0/collections/btree_map/struct.BTreeMap.html
|
||||
|
||||
## Footnotes
|
||||
[^fn-debug-notes]: For this post the most useful GDB command is probably `p/x *((long int*)0xfffffffffffff000)@512`. It prints all entries of the recursively mapped P4 table by interpreting it as an array of 512 long ints (the `@512` is GDB's array syntax). Of course you can also print other tables by adjusting the address.
|
||||
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,819 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="900.00409"
|
||||
height="530"
|
||||
viewBox="-2141 2141 12027.89 7032.1014"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="recursive_mapping_access_p3.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="998"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="2.7755576e-17"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="236.56561"
|
||||
inkscape:cy="267.91265"
|
||||
inkscape:window-x="1080"
|
||||
inkscape:window-y="568"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4"
|
||||
units="in" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(-1087.0637,717.51407)">
|
||||
<polyline
|
||||
points="3365,2598 3365,3070"
|
||||
id="polyline10"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3189,2598 3189,3070"
|
||||
id="polyline12"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3012,2598 3012,3070"
|
||||
id="polyline14"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2834,2598 2834,3070"
|
||||
id="polyline16"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2657,2598 2657,3070"
|
||||
id="polyline18"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2480,2598 2480,3070"
|
||||
id="polyline20"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2303,2598 2303,3070"
|
||||
id="polyline22"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2125,2456 2125,3070"
|
||||
id="polyline24"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1948,2598 1948,3070"
|
||||
id="polyline26"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1772,2598 1772,3070"
|
||||
id="polyline28"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1594,2598 1594,3070"
|
||||
id="polyline30"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1417,2598 1417,3070"
|
||||
id="polyline32"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1239,2598 1239,3070"
|
||||
id="polyline34"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1063,2598 1063,3070"
|
||||
id="polyline36"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="886,2598 886,3070"
|
||||
id="polyline38"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="708,2456 708,3070"
|
||||
id="polyline40"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,2456 3543,3070"
|
||||
id="polyline74"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="-2125,2598 3543,2598"
|
||||
id="polyline76"
|
||||
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,3070 -1948,3070 -2125,3070"
|
||||
id="polyline78"
|
||||
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
|
||||
<polyline
|
||||
points="9035,2598 9035,3070"
|
||||
id="polyline80"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8858,2598 8858,3070"
|
||||
id="polyline82"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8681,2598 8681,3070"
|
||||
id="polyline84"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8503,2598 8503,3070"
|
||||
id="polyline86"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8326,2598 8326,3070"
|
||||
id="polyline88"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8150,2598 8150,3070"
|
||||
id="polyline90"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7972,2598 7972,3070"
|
||||
id="polyline92"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7795,2456 7795,3070"
|
||||
id="polyline94"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7617,2598 7617,3070"
|
||||
id="polyline96"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7441,2598 7441,3070"
|
||||
id="polyline98"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7264,2598 7264,3070"
|
||||
id="polyline100"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7086,2598 7086,3070"
|
||||
id="polyline102"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6909,2598 6909,3070"
|
||||
id="polyline104"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6732,2598 6732,3070"
|
||||
id="polyline106"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6555,2598 6555,3070"
|
||||
id="polyline108"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6377,2456 6377,3070"
|
||||
id="polyline110"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6200,2598 6200,3070"
|
||||
id="polyline112"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6024,2598 6024,3070"
|
||||
id="polyline114"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5846,2598 5846,3070"
|
||||
id="polyline116"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5669,2598 5669,3070"
|
||||
id="polyline118"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5491,2598 5491,3070"
|
||||
id="polyline120"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5315,2598 5315,3070"
|
||||
id="polyline122"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5138,2598 5138,3070"
|
||||
id="polyline124"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4960,2409 4960,3070"
|
||||
id="polyline126"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4783,2598 4783,3070"
|
||||
id="polyline128"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4606,2598 4606,3070"
|
||||
id="polyline130"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4429,2598 4429,3070"
|
||||
id="polyline132"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4251,2598 4251,3070"
|
||||
id="polyline134"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4074,2598 4074,3070"
|
||||
id="polyline136"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3898,2598 3898,3070"
|
||||
id="polyline138"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3720,2598 3720,3070"
|
||||
id="polyline140"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,2456 9212,3070"
|
||||
id="polyline142"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,2598 9212,2598"
|
||||
id="polyline144"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,3070 3720,3070 0,3070"
|
||||
id="polyline146"
|
||||
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
|
||||
<rect
|
||||
x="4021.6787"
|
||||
y="4311.5088"
|
||||
width="1668.8131"
|
||||
height="3296.2527"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke-width:11.23412323"
|
||||
id="rect150" />
|
||||
<polyline
|
||||
points="3118,3590 2834,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline178"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1322,3590 1039,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline180"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4913,3590 4629,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline184"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6519,3590 6236,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline186"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8314,3590 8031,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline188"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline204"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline206"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline208"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline210"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline212"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5546.7881"
|
||||
y="6876.4409"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-5">0</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3507"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text246"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2161"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text248"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text250"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="9176"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text262"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7759"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text264"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6342"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text266"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4924"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text268"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3579"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text270"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6413"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text272"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7830"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text274"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4996"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text276"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,6614)"
|
||||
id="g282" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,4916.9545,5254.403)"
|
||||
id="g306">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4862.9551"
|
||||
y="4397.5566"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3503">P4 table</tspan></text>
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="3165"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text350"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text352"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="4960"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text356"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||
<text
|
||||
x="6566"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text358"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="8362"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text360"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
x="1946.0945"
|
||||
y="6998.52"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text344-8-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3493-3-2" />
|
||||
</text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="4294.9473"
|
||||
y="4493.6152"
|
||||
width="1181"
|
||||
height="471.99994"
|
||||
rx="0"
|
||||
id="rect162-5" />
|
||||
<circle
|
||||
cx="5335.9546"
|
||||
cy="4720.2134"
|
||||
style="fill:#008200;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
|
||||
id="circle172-4"
|
||||
r="46.999996" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4815.9546"
|
||||
y="4773.6704"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6">P4 entry</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4925.6357"
|
||||
y="7173.5298"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6-4">Recursive</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4925.6357"
|
||||
y="7440.8149"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0-9"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6-4-4">Mapping</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="4296.5396"
|
||||
y="5560.2471"
|
||||
width="1182.4656"
|
||||
height="314.50067"
|
||||
rx="0"
|
||||
id="rect162-5-1" />
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.99999994,0.99999994,0,4915.3734,6277.1653)"
|
||||
id="g306-2">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-3"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3097.1609"
|
||||
y="2954.5652"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-4"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
inkscape:transform-center-x="-789.14735"
|
||||
inkscape:transform-center-y="1300.106"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3503-4">111111111111111111111111111</tspan></text>
|
||||
<rect
|
||||
x="8208.377"
|
||||
y="4073.4419"
|
||||
width="691.0863"
|
||||
height="3268.5144"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect8" />
|
||||
<rect
|
||||
x="8208.377"
|
||||
y="5474.9399"
|
||||
width="691.0863"
|
||||
height="233.25354"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect158" />
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.98836234,0.97611052,0,9106.1205,5822.055)"
|
||||
id="g286">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="96.892052"
|
||||
y="38.756821"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text288"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3509">P3 table </tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.53380773;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 2308.9448,-2797.8507 -483.0169,0 0,-2544.6425 -1879.786585,0 0,339.1222"
|
||||
id="path8874"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4809.9756"
|
||||
y="5774.4951"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3495-6-7">P4 entry</tspan></text>
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="5336.855"
|
||||
cy="5714.1816"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="circle172-4-4" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.55735683px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 5392.0276,5712.0004 1512.0496,0 0,1631.4957 1257.8109,0"
|
||||
id="path9464"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 8173.2452,3542.5202 0,364.8071 -584.0141,0 0,1802.0952 528.0052,0"
|
||||
id="path9466"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5610.3569"
|
||||
y="4990.1772"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5">511</tspan></text>
|
||||
</g>
|
||||
<polygon
|
||||
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||
id="polygon232-2"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-646.4618,-1622.6914)" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76697159px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 5574.5569,4637.287 719.9709,0 0,3526.7653 -2765.702,0 0,-560.545 294.8212,0"
|
||||
id="path5354"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.52740288px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 3843.9848,5007.8363 -4223.58417,0 0,-2024.2908"
|
||||
id="path5356"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.65159035px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 2156.7076,2986.8221 0,1988.2634 1691.863,0"
|
||||
id="path5358"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.78581619px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4696.351,2980.052 0,497.5653 -1975.7645,0 0,1464.6642 1128.3337,0"
|
||||
id="path5360"
|
||||
inkscape:connector-curvature="0" />
|
||||
<polygon
|
||||
points="3150,6661 3308,6614 3150,6566 3150,6661 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-625.83944,-2988.2046)" />
|
||||
<polygon
|
||||
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7-3"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,4891.7749,-3219.444)" />
|
||||
<polygon
|
||||
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7-3-5"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,4896.989,-940.27671)" />
|
||||
<polygon
|
||||
points="3150,6566 3150,6661 3150,6661 3308,6614 "
|
||||
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||
id="polygon232-2-7-7"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-628.09888,-4256.4264)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,936 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="900.00409"
|
||||
height="530.5"
|
||||
viewBox="-2141 2141 12027.89 7038.7354"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="recursive_mapping_access_p3_inactive_table.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="998"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="1"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="466.92372"
|
||||
inkscape:cy="267.78109"
|
||||
inkscape:window-x="1080"
|
||||
inkscape:window-y="413"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g242"
|
||||
units="in" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(-1087.0637,710.26871)">
|
||||
<polyline
|
||||
points="3365,2598 3365,3070"
|
||||
id="polyline10"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3189,2598 3189,3070"
|
||||
id="polyline12"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3012,2598 3012,3070"
|
||||
id="polyline14"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2834,2598 2834,3070"
|
||||
id="polyline16"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2657,2598 2657,3070"
|
||||
id="polyline18"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2480,2598 2480,3070"
|
||||
id="polyline20"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2303,2598 2303,3070"
|
||||
id="polyline22"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2125,2456 2125,3070"
|
||||
id="polyline24"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1948,2598 1948,3070"
|
||||
id="polyline26"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1772,2598 1772,3070"
|
||||
id="polyline28"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1594,2598 1594,3070"
|
||||
id="polyline30"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1417,2598 1417,3070"
|
||||
id="polyline32"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1239,2598 1239,3070"
|
||||
id="polyline34"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1063,2598 1063,3070"
|
||||
id="polyline36"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="886,2598 886,3070"
|
||||
id="polyline38"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="708,2456 708,3070"
|
||||
id="polyline40"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,2456 3543,3070"
|
||||
id="polyline74"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="-2125,2598 3543,2598"
|
||||
id="polyline76"
|
||||
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,3070 -1948,3070 -2125,3070"
|
||||
id="polyline78"
|
||||
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
|
||||
<polyline
|
||||
points="9035,2598 9035,3070"
|
||||
id="polyline80"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8858,2598 8858,3070"
|
||||
id="polyline82"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8681,2598 8681,3070"
|
||||
id="polyline84"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8503,2598 8503,3070"
|
||||
id="polyline86"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8326,2598 8326,3070"
|
||||
id="polyline88"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8150,2598 8150,3070"
|
||||
id="polyline90"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7972,2598 7972,3070"
|
||||
id="polyline92"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7795,2456 7795,3070"
|
||||
id="polyline94"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7617,2598 7617,3070"
|
||||
id="polyline96"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7441,2598 7441,3070"
|
||||
id="polyline98"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7264,2598 7264,3070"
|
||||
id="polyline100"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="7086,2598 7086,3070"
|
||||
id="polyline102"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6909,2598 6909,3070"
|
||||
id="polyline104"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6732,2598 6732,3070"
|
||||
id="polyline106"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6555,2598 6555,3070"
|
||||
id="polyline108"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6377,2456 6377,3070"
|
||||
id="polyline110"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6200,2598 6200,3070"
|
||||
id="polyline112"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6024,2598 6024,3070"
|
||||
id="polyline114"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5846,2598 5846,3070"
|
||||
id="polyline116"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5669,2598 5669,3070"
|
||||
id="polyline118"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5491,2598 5491,3070"
|
||||
id="polyline120"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5315,2598 5315,3070"
|
||||
id="polyline122"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5138,2598 5138,3070"
|
||||
id="polyline124"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4960,2409 4960,3070"
|
||||
id="polyline126"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4783,2598 4783,3070"
|
||||
id="polyline128"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4606,2598 4606,3070"
|
||||
id="polyline130"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4429,2598 4429,3070"
|
||||
id="polyline132"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4251,2598 4251,3070"
|
||||
id="polyline134"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4074,2598 4074,3070"
|
||||
id="polyline136"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3898,2598 3898,3070"
|
||||
id="polyline138"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3720,2598 3720,3070"
|
||||
id="polyline140"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,2456 9212,3070"
|
||||
id="polyline142"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3543,2598 9212,2598"
|
||||
id="polyline144"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,3070 3720,3070 0,3070"
|
||||
id="polyline146"
|
||||
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
|
||||
<rect
|
||||
x="5478.3828"
|
||||
y="4304.8267"
|
||||
width="1668.8131"
|
||||
height="3296.2527"
|
||||
rx="0"
|
||||
style="fill:#dfdfc7;fill-opacity:1;stroke-width:11.23412323"
|
||||
id="rect150" />
|
||||
<polyline
|
||||
points="3118,3590 2834,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline178"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="1322,3590 1039,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline180"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="4913,3590 4629,3779"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline184"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="6519,3590 6236,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline186"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="8314,3590 8031,3779"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline188"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline204"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline206"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||
style="stroke:#0000ff"
|
||||
id="polyline208"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline210"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<polyline
|
||||
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||
style="stroke:#008200;stroke-opacity:1"
|
||||
id="polyline212"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6577.6826"
|
||||
y="6866.8647"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-5">0</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3507"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text246"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2161"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text248"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text250"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text252"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="9176"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text262"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7759"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text264"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6342"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text266"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4924"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text268"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3579"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text270"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6413"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text272"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="7830"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text274"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4996"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text276"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,6614)"
|
||||
id="g282" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,5947.8484,5249.6147)"
|
||||
id="g306">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5912.7651"
|
||||
y="4431.0742"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||
style="font-size:200.46391296px"
|
||||
id="tspan3503">P4 table</tspan></text>
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="3165"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text350"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text352"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1">9</text>
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="4960"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text356"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1">9</text>
|
||||
<text
|
||||
x="6566"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text358"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||
<text
|
||||
x="8362"
|
||||
y="3732"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text360"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
x="1946.0945"
|
||||
y="6998.52"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text344-8-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3493-3-2" />
|
||||
</text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="5325.8418"
|
||||
y="4484.0391"
|
||||
width="1181"
|
||||
height="471.99994"
|
||||
rx="0"
|
||||
id="rect162-5" />
|
||||
<circle
|
||||
cx="6366.8491"
|
||||
cy="4715.4253"
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="circle172-4"
|
||||
r="46.999996" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5846.8491"
|
||||
y="4768.8823"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6">P4 entry</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5956.5303"
|
||||
y="7168.7417"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6-4">Recursive</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5956.5303"
|
||||
y="7436.0269"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-0-9"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46391296px;stroke-width:0"
|
||||
id="tspan3495-6-4-4">Mapping</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="5327.4341"
|
||||
y="5550.6709"
|
||||
width="1182.4656"
|
||||
height="314.50067"
|
||||
rx="0"
|
||||
id="rect162-5-1" />
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.99999994,0.99999994,0,5946.2673,6272.377)"
|
||||
id="g306-2">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-3"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3097.1609"
|
||||
y="2954.5652"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-4"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
inkscape:transform-center-x="-789.14735"
|
||||
inkscape:transform-center-y="1300.106"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3503-4">111111111111111111111111111</tspan></text>
|
||||
<rect
|
||||
x="8208.377"
|
||||
y="4073.4419"
|
||||
width="691.0863"
|
||||
height="3268.5144"
|
||||
rx="0"
|
||||
style="fill:#dfdfc7;fill-opacity:1;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect8" />
|
||||
<rect
|
||||
x="8208.377"
|
||||
y="5474.9399"
|
||||
width="691.0863"
|
||||
height="233.25354"
|
||||
rx="0"
|
||||
style="fill:#dfdfc7;fill-opacity:1;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect158" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5840.8701"
|
||||
y="5769.707"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3495-6-7">P4 entry</tspan></text>
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="6367.7495"
|
||||
cy="5709.3936"
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="circle172-4-4" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 8173.2452,3542.5202 0,364.8071 -584.0141,0 0,1802.0952 528.0052,0"
|
||||
id="path9466"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6641.2515"
|
||||
y="4980.6011"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5">511</tspan></text>
|
||||
<rect
|
||||
x="1677.2448"
|
||||
y="4498.876"
|
||||
width="1181"
|
||||
height="2361.9998"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.99999905;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect150-8" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2928.0776"
|
||||
y="6883.7607"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-5-9"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-5-6">0</tspan></text>
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-1,1,0,2298.2444,5414.9455)"
|
||||
id="g306-4">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-1"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2263.1606"
|
||||
y="4404.876"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3503-3">P4 table</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="1676.2374"
|
||||
y="4500.9346"
|
||||
width="1181"
|
||||
height="471.99997"
|
||||
rx="0"
|
||||
id="rect162-5-4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2197.2446"
|
||||
y="4780.9897"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-1"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3495-6-41">P4 entry</tspan></text>
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.99999992,0.9999999,0,2296.6633,6284.4847)"
|
||||
id="g306-2-3" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2991.647"
|
||||
y="4997.4966"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-8">511</tspan></text>
|
||||
<polygon
|
||||
points="3150,6661 3308,6614 3150,6566 3150,6661 "
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="polygon232-2-7-7-1"
|
||||
transform="translate(-1613.3498,-1640.66)" />
|
||||
<text
|
||||
transform="matrix(0,-1.0062563,0.9937826,0,0,0)"
|
||||
xml:space="preserve"
|
||||
x="-5614.5532"
|
||||
y="9191.6416"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text288"
|
||||
style="font-style:normal;font-weight:normal;font-size:149.29702759px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:196.89912415px;stroke-width:0"
|
||||
id="tspan3509">inactive P3 table </tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 1180.4935,3543.3667 0,1428.1431 381.7959,0"
|
||||
id="path6515"
|
||||
inkscape:connector-curvature="0" />
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="2703.6011"
|
||||
cy="4723.3125"
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="circle172-4-3" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:14.74861526;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 2716.9762,4721.7506 729.3182,0 0,2110.8251 1739.3012,0"
|
||||
id="path6534"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 4771.7501,3542.097 0,1413.9884 417.9767,0"
|
||||
id="path6542"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 2975.9128,3541.8854 0,541.514 1015.0566,0 0,897.233 1213.8046,0"
|
||||
id="path6544"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 6376.72,3540.8786 0,343.9797 -1325.7614,2.3941 0,1978.7627 172.2064,0"
|
||||
id="path6546"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:11.42028618;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 6378.641,5717.2755 889.0271,0 0,1623.2076 851.1962,0"
|
||||
id="path6548"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="2260.9126"
|
||||
y="4178.6655"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3503-3-0">active</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5915.9365"
|
||||
y="4225.4873"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-0"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3503-9">inactive</tspan></text>
|
||||
</g>
|
||||
<polygon
|
||||
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="polygon232-2"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,810.24264,-1636.0556)" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76342201;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 7031.2608,4630.6049 719.9709,0 0,3526.7653 -2765.702,0 2.3625,-539.2826 285.3712,0"
|
||||
id="path5354"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<polygon
|
||||
points="3150,6661 3308,6614 3150,6566 3150,6661 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,830.86496,-2994.8867)" />
|
||||
<polygon
|
||||
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7-3"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,4891.7749,-3219.444)" />
|
||||
<polygon
|
||||
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="polygon232-2-7-3-5"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,4896.989,-940.27671)" />
|
||||
<polygon
|
||||
points="3150,6566 3150,6661 3150,6661 3308,6614 "
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="polygon232-2-7-7"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,828.60556,-4263.1085)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 39 KiB |
@@ -0,0 +1,403 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="617.77704"
|
||||
height="216.27831"
|
||||
viewBox="-2141 2141 8256.134 2869.6056"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="recursive_mapping_inactive_table_scheme.svg">
|
||||
<metadata
|
||||
id="metadata370">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs368" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="998"
|
||||
id="namedview366"
|
||||
showgrid="false"
|
||||
fit-margin-top="1"
|
||||
fit-margin-left="1"
|
||||
fit-margin-right="1"
|
||||
fit-margin-bottom="1"
|
||||
inkscape:zoom="1.4142136"
|
||||
inkscape:cx="523.64687"
|
||||
inkscape:cy="50.722008"
|
||||
inkscape:window-x="1080"
|
||||
inkscape:window-y="413"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g242"
|
||||
units="in" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="g4"
|
||||
transform="translate(-1516.6308,-1533.0567)">
|
||||
<g
|
||||
style="fill:#000000;stroke-width:0"
|
||||
id="g242"
|
||||
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text254"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-672"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text256"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-744"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text258"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2090"
|
||||
y="2551"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text260"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||
<g
|
||||
transform="matrix(0,-1,1,0,8976,6614)"
|
||||
id="g282" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="-2125"
|
||||
y="2314"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text290"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3874"
|
||||
y="3968"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text318"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="2031"
|
||||
y="5622"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text346"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||
<text
|
||||
x="1370"
|
||||
y="6708"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text354"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||
<text
|
||||
x="1181"
|
||||
y="7532.2715"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text364"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||
<text
|
||||
x="1946.0945"
|
||||
y="6998.52"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text344-8-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%">
|
||||
<tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||
id="tspan3493-3-2" />
|
||||
</text>
|
||||
<rect
|
||||
x="3090.1064"
|
||||
y="4497.9272"
|
||||
width="1182.895"
|
||||
height="1367.9473"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:6.0930233;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect150-8" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4341.8867"
|
||||
y="5887.8105"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-5-9"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-5-6">0</tspan></text>
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-1,1,0,3712.0549,5414.9455)"
|
||||
id="g306-4">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-1"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3676.9697"
|
||||
y="4404.876"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3503-3">P4 table</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="3090.0466"
|
||||
y="4500.9346"
|
||||
width="1181"
|
||||
height="471.99997"
|
||||
rx="0"
|
||||
id="rect162-5-4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3611.0537"
|
||||
y="4780.9897"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-1"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46392822px;stroke-width:0"
|
||||
id="tspan3495-6-41">P4 entry</tspan></text>
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-0.99999992,0.9999999,0,2296.6633,6284.4847)"
|
||||
id="g306-2-3" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="4405.4561"
|
||||
y="4997.4966"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-6"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-8">511</tspan></text>
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="4117.4102"
|
||||
cy="4723.3125"
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="circle172-4-3" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="3674.7217"
|
||||
y="4178.6655"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8-5"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3503-3-0">active</tspan></text>
|
||||
<rect
|
||||
x="5370.3823"
|
||||
y="4498.0884"
|
||||
width="1182.895"
|
||||
height="1367.9473"
|
||||
rx="0"
|
||||
style="fill:#dfdfbe;fill-opacity:1;stroke:#000000;stroke-width:6.0930233;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect150-8-9" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6622.1631"
|
||||
y="5887.9717"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-5-9-3"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-5-6-5">0</tspan></text>
|
||||
<g
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
transform="matrix(0,-1,1,0,5992.3314,5415.1068)"
|
||||
id="g306-4-9">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="0"
|
||||
y="0"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="404"
|
||||
id="text308-1-8"
|
||||
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5957.2456"
|
||||
y="4405.0371"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8-0"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3503-3-2">P4 table</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
x="5370.3228"
|
||||
y="4501.0957"
|
||||
width="1181"
|
||||
height="472"
|
||||
rx="0"
|
||||
id="rect162-5-4-4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5891.3296"
|
||||
y="4781.1509"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-1-6"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3495-6-41-6">P4 entry</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="6685.7324"
|
||||
y="4997.6577"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-2-1-6-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||
id="tspan3503-48-5-8-1">511</tspan></text>
|
||||
<polygon
|
||||
points="3150,6566 3150,6661 3150,6661 3308,6614 "
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="polygon232-2-7-7-1-1"
|
||||
transform="translate(2076.0084,-749.88898)" />
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="6397.6865"
|
||||
cy="4723.4736"
|
||||
style="fill:#008200;fill-opacity:1;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="circle172-4-3-6" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="5954.9976"
|
||||
y="4178.8267"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text320-8-5-9"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3503-3-0-2">inactive</tspan></text>
|
||||
<rect
|
||||
x="1019.4597"
|
||||
y="4821.9668"
|
||||
width="1487.6132"
|
||||
height="469.84174"
|
||||
rx="0"
|
||||
style="fill:#dfdfdf;stroke:#000000;stroke-width:4.0044775;stroke-linecap:butt;stroke-linejoin:miter"
|
||||
id="rect150-8-0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
x="1642.8574"
|
||||
y="5136.835"
|
||||
font-style="normal"
|
||||
font-weight="normal"
|
||||
font-size="152"
|
||||
id="text314-5-1-4"
|
||||
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||
style="font-size:200.46394348px;stroke-width:0"
|
||||
id="tspan3495-6-41-8">CR3 register</tspan></text>
|
||||
<ellipse
|
||||
ry="46.999996"
|
||||
rx="46.999992"
|
||||
cx="2349.6458"
|
||||
cy="5076.04"
|
||||
style="fill:#969696;fill-opacity:1;stroke:#969696;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="circle172-4-3-2" />
|
||||
<polygon
|
||||
points="3150,6566 3150,6661 3150,6661 3308,6614 "
|
||||
style="fill:#969696;fill-opacity:1;stroke:#969696;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="polygon232-2-7-7-1-1-7"
|
||||
transform="translate(-214.18472,-748.49788)" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#969696;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 2349.6459,5073.646 359.3939,0 0,792.4508 255.3589,0"
|
||||
id="path4612"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4118.0344,4723.1182 672.1066,0 0,1137.6237 494.8846,0"
|
||||
id="path4614"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 6395.1719,4726.5039 441.3835,0 0,1357.6997 -1725.4081,0 0,-199.7613 127.0649,0"
|
||||
id="path4620"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 18 KiB |
2
blog/content/edition-1/posts/08-kernel-heap/allocate.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -1,95 +1,170 @@
|
||||
+++
|
||||
title = "Kernel Heap"
|
||||
date = "2016-04-11"
|
||||
weight = 8
|
||||
path = "kernel-heap"
|
||||
aliases = ["kernel-heap.html"]
|
||||
date = 2016-04-11
|
||||
template = "edition-1/page.html"
|
||||
[extra]
|
||||
updated = "2017-11-19"
|
||||
+++
|
||||
|
||||
In the previous posts we have created a [frame allocator] and a [page table module]. Now we are ready to create a kernel heap and a memory allocator. Thus, we will unlock `Box`, `Vec`, `BTreeMap`, and the rest of the [alloc] and [collections] crates.
|
||||
In the previous posts we created a [frame allocator] and a [page table module]. Now we are ready to create a kernel heap and a memory allocator. Thus, we will unlock `Box`, `Vec`, `BTreeMap`, and the rest of the [alloc] crate.
|
||||
|
||||
[frame allocator]: {{% relref "2015-11-15-allocating-frames.md" %}}
|
||||
[page table module]: {{% relref "2015-12-09-modifying-page-tables.md" %}}
|
||||
[frame allocator]: @/edition-1/posts/05-allocating-frames/index.md
|
||||
[page table module]: @/edition-1/posts/06-page-tables/index.md
|
||||
[alloc]: https://doc.rust-lang.org/nightly/alloc/index.html
|
||||
[collections]: https://doc.rust-lang.org/nightly/collections/index.html
|
||||
|
||||
<!--more-->
|
||||
<!-- more -->
|
||||
|
||||
As always, you can find the complete source code on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
||||
As always, you can find the complete source code on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
||||
|
||||
[Github]: https://github.com/phil-opp/blog_os/tree/kernel_heap
|
||||
[GitHub]: https://github.com/phil-opp/blog_os/tree/first_edition_post_8
|
||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||
|
||||
## Introduction
|
||||
The _heap_ is the memory area for long-lived allocations. The programmer can access it by using types like [Box][Box rustbyexample] or [Vec]. Behind the scenes, the compiler manages that memory by inserting calls to some memory allocator. By default, Rust links to the [jemalloc] allocator (for binaries) or the system allocator (for libraries). However, both rely on [system calls] such as [sbrk] and are thus unusable in our kernel. So we need to create and link our own allocator.
|
||||
|
||||
[Box rustbyexample]: http://rustbyexample.com/std/box.html
|
||||
[Box rustbyexample]: https://doc.rust-lang.org/rust-by-example/std/box.html
|
||||
[Vec]: https://doc.rust-lang.org/book/vectors.html
|
||||
[jemalloc]: http://www.canonware.com/jemalloc/
|
||||
[jemalloc]: http://jemalloc.net/
|
||||
[system calls]: https://en.wikipedia.org/wiki/System_call
|
||||
[sbrk]: https://en.wikipedia.org/wiki/Sbrk
|
||||
|
||||
A good allocator is fast and reliable. It also effectively utilizes the available memory and keeps [fragmentation] low. Furthermore, it works well for concurrent applications and scales to any number of processors. It even optimizes the memory layout with respect to the CPU caches to improve [cache locality] and avoid [false sharing].
|
||||
|
||||
[cache locality]: http://docs.cray.com/books/S-2315-50/html-S-2315-50/qmeblljm.html
|
||||
[cache locality]: https://www.geeksforgeeks.org/locality-of-reference-and-cache-operation-in-cache-memory/
|
||||
[fragmentation]: https://en.wikipedia.org/wiki/Fragmentation_(computing)
|
||||
[false sharing]: http://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
|
||||
[false sharing]: https://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
|
||||
|
||||
These requirements make good allocators pretty complex. For example, [jemalloc] has over 30.000 lines of code. This complexity is out of scope for our kernel, so we will create a much simpler allocator. However, it should suffice for the foreseeable future, since we'll allocate only when it's absolutely necessary.
|
||||
These requirements make good allocators pretty complex. For example, [jemalloc] has over 30.000 lines of code. This complexity is out of scope for our kernel, so we will create a much simpler allocator. Nevertheless, it should suffice for the foreseeable future, since we'll allocate only when it's absolutely necessary.
|
||||
|
||||
## The Allocator Interface
|
||||
|
||||
The allocator interface in Rust is defined through the [`Alloc` trait], which looks like this:
|
||||
|
||||
[`Alloc` trait]: https://doc.rust-lang.org/1.20.0/alloc/allocator/trait.Alloc.html
|
||||
|
||||
```rust
|
||||
pub unsafe trait Alloc {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>;
|
||||
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout);
|
||||
[…] // about 13 methods with default implementations
|
||||
}
|
||||
```
|
||||
|
||||
The `alloc` method should allocate a memory block with the size and alignment given through `Layout` parameter. The `deallocate` method should free such memory blocks again. Both methods are `unsafe`, as is the trait itself. This has different reasons:
|
||||
|
||||
- Implementing the `Alloc` trait is unsafe, because the implementation must satisfy a set of contracts. Among other things, pointers returned by `alloc` must point to valid memory and adhere to the `Layout` requirements.
|
||||
- Calling `alloc` is unsafe because the caller must ensure that the passed layout does not have size zero. I think this is because of compatibility reasons with existing C-allocators, where zero-sized allocations are undefined behavior.
|
||||
- Calling `dealloc` is unsafe because the caller must guarantee that the passed parameters adhere to the contract. For example, `ptr` must denote a valid memory block allocated via this allocator.
|
||||
|
||||
To set the system allocator, the `global_allocator` attribute can be added to a `static` that implements `Alloc` for a shared reference of itself. For example:
|
||||
|
||||
```rust
|
||||
#[global_allocator]
|
||||
static MY_ALLOCATOR: MyAllocator = MyAllocator {...};
|
||||
|
||||
impl<'a> Alloc for &'a MyAllocator {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {...}
|
||||
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {...}
|
||||
}
|
||||
```
|
||||
|
||||
Note that `Alloc` needs to be implemented for `&MyAllocator`, not for `MyAllocator`. The reason is that the `alloc` and `dealloc` methods require mutable `self` references, but there's no way to get such a reference safely from a `static`. By requiring implementations for `&MyAllocator`, the global allocator interface avoids this problem and pushes the burden of synchronization onto the user.
|
||||
|
||||
## Including the alloc crate
|
||||
The `Alloc` trait is part of the `alloc` crate, which like `core` is a subset of Rust's standard library. Apart from the trait, the crate also contains the standard types that require allocations such as `Box`, `Vec` and `Arc`. We can include it through a simple `extern crate`:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
#![feature(alloc)] // the alloc crate is still unstable
|
||||
|
||||
[...]
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
```
|
||||
|
||||
We don't need to add anything to our Cargo.toml, since the `alloc` crate is part of the standard library and shipped with the Rust compiler. The `alloc` crate provides the [format!] and [vec!] macros, so we use `#[macro_use]` to import them.
|
||||
|
||||
[format!]: https://doc.rust-lang.org/1.10.0/collections/macro.format!.html
|
||||
[vec!]: https://doc.rust-lang.org/1.10.0/collections/macro.vec!.html
|
||||
|
||||
When we try to compile our crate now, the following error occurs:
|
||||
|
||||
```
|
||||
error[E0463]: can't find crate for `alloc`
|
||||
--> src/lib.rs:10:1
|
||||
|
|
||||
16 | extern crate alloc;
|
||||
| ^^^^^^^^^^^^^^^^^^^ can't find crate
|
||||
```
|
||||
|
||||
The problem is that [`xargo`] only cross compiles `libcore` by default. To also cross compile the `alloc` crate, we need to create a file named `Xargo.toml` in our project root (right next to the `Cargo.toml`) with the following content:
|
||||
|
||||
[`xargo`]: https://github.com/japaric/xargo
|
||||
|
||||
```toml
|
||||
[target.x86_64-blog_os.dependencies]
|
||||
alloc = {}
|
||||
```
|
||||
|
||||
This instructs `xargo` that we also need `alloc`. It still doesn't compile, since we need to define a global allocator in order to use the `alloc` crate:
|
||||
|
||||
```
|
||||
error: no #[default_lib_allocator] found but one is required; is libstd not linked?
|
||||
```
|
||||
|
||||
## A Bump Allocator
|
||||
|
||||
For our own allocator, we start simple. We create an allocator crate in a new `libs` subfolder:
|
||||
For our first allocator, we start simple. We create a `memory::heap_allocator` module containing a so-called _bump allocator_:
|
||||
|
||||
``` shell
|
||||
> mkdir libs
|
||||
> cd libs
|
||||
> cargo new bump_allocator
|
||||
> cd bump_allocator
|
||||
```
|
||||
```rust
|
||||
// in src/memory/mod.rs
|
||||
|
||||
Our allocator is very basic. It only keeps track of the next free address:
|
||||
mod heap_allocator;
|
||||
|
||||
``` rust
|
||||
// in libs/bump_allocator/src/lib.rs
|
||||
// in src/memory/heap_allocator.rs
|
||||
|
||||
#![feature(const_fn)]
|
||||
use alloc::heap::{Alloc, AllocErr, Layout};
|
||||
|
||||
/// A simple allocator that allocates memory linearly and ignores freed memory.
|
||||
#[derive(Debug)]
|
||||
struct BumpAllocator {
|
||||
pub struct BumpAllocator {
|
||||
heap_start: usize,
|
||||
heap_size: usize,
|
||||
heap_end: usize,
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl BumpAllocator {
|
||||
/// Create a new allocator, which uses the memory in the
|
||||
/// range [heap_start, heap_start + heap_size).
|
||||
const fn new(heap_start: usize, heap_size: usize) -> BumpAllocator {
|
||||
BumpAllocator {
|
||||
heap_start: heap_start,
|
||||
heap_size: heap_size,
|
||||
next: heap_start,
|
||||
pub const fn new(heap_start: usize, heap_end: usize) -> Self {
|
||||
Self { heap_start, heap_end, next: heap_start }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Alloc for BumpAllocator {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
let alloc_start = align_up(self.next, layout.align());
|
||||
let alloc_end = alloc_start.saturating_add(layout.size());
|
||||
|
||||
if alloc_end <= self.heap_end {
|
||||
self.next = alloc_end;
|
||||
Ok(alloc_start as *mut u8)
|
||||
} else {
|
||||
Err(AllocErr::Exhausted{ request: layout })
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a block of memory with the given size and alignment.
|
||||
fn allocate(&mut self, size: usize, align: usize) -> Option<*mut u8> {
|
||||
let alloc_start = align_up(self.next, align);
|
||||
let alloc_end = alloc_start + size;
|
||||
|
||||
if alloc_end <= self.heap_start + self.heap_size {
|
||||
self.next = alloc_end;
|
||||
Some(alloc_start as *mut u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
// do nothing, leak memory
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `heap_start` and `heap_size` fields just contain the start address and size of our kernel heap. The `next` field contains the next free address and is increased after every allocation. To `allocate` a memory block we align the `next` address using the `align_up` function (decribed below). Then we add up the desired `size` and make sure that we don't exceed the end of the heap. If everything goes well, we update the `next` address and return a pointer to the start address of the allocation. Else, we return `None`.
|
||||
We also need to add `#![feature(allocator_api)]` to our `lib.rs`, since the allocator API is still unstable.
|
||||
|
||||
Note that we need to add a feature flag at the beginning of the file, because we've marked the `new` function as `const`. [Const functions] are unstable, so we need to add the `#![feature(const_fn)]` flag.
|
||||
|
||||
[Const functions]: https://github.com/rust-lang/rust/issues/24111
|
||||
The `heap_start` and `heap_end` fields contain the start and end address of our kernel heap. The `next` field contains the next free address and is increased after every allocation. To `allocate` a memory block we align the `next` address using the `align_up` function (described below). Then we add up the desired `size` and make sure that we don't exceed the end of the heap. We use a saturating add so that the `alloc_end` cannot overflow, which could lead to an invalid allocation. If everything goes well, we update the `next` address and return a pointer to the start address of the allocation. Else, we return `None`.
|
||||
|
||||
### Alignment
|
||||
In order to simplify alignment, we add `align_down` and `align_up` functions:
|
||||
@@ -114,7 +189,7 @@ pub fn align_up(addr: usize, align: usize) -> usize {
|
||||
}
|
||||
```
|
||||
|
||||
Let's start with `align_down`: If the alignment is a valid power of two (i.e. in `{1,2,4,8,…}`), we use some bit-fiddling to return the aligned address. It works because every power of two has exactly one bit set in its binary representation. For example, the numbers `{1,2,4,8,…}` are `{1,10,100,1000,…}` in binary. By subtracting 1 we get `{0,01,011,0111,…}`. These binary numbers have a `1` at exactly the positions that need to be zeroed in `addr`. For example, the last 3 bits need to be zeroed for a alignment of 8.
|
||||
Let's start with `align_down`: If the alignment is a valid power of two (i.e. in `{1,2,4,8,…}`), we use some bitwise operations to return the aligned address. It works because every power of two has exactly one bit set in its binary representation. For example, the numbers `{1,2,4,8,…}` are `{1,10,100,1000,…}` in binary. By subtracting 1 we get `{0,01,011,0111,…}`. These binary numbers have a `1` at exactly the positions that need to be zeroed in `addr`. For example, the last 3 bits need to be zeroed for a alignment of 8.
|
||||
|
||||
To align `addr`, we create a [bitmask] from `align-1`. We want a `0` at the position of each `1`, so we invert it using `!`. After that, the binary numbers look like this: `{…11111,…11110,…11100,…11000,…}`. Finally, we zero the correct bits using a binary `AND`.
|
||||
|
||||
@@ -122,193 +197,132 @@ To align `addr`, we create a [bitmask] from `align-1`. We want a `0` at the posi
|
||||
|
||||
Aligning upwards is simple now. We just increase `addr` by `align-1` and call `align_down`. We add `align-1` instead of `align` because we would otherwise waste `align` bytes for already aligned addresses.
|
||||
|
||||
### Deallocate
|
||||
But how do we deallocate memory in our bump allocator? Well, we don't ;). We just leak all freed memory for now. Thus our allocator quickly runs out of memory in a real system. On the other hand, it's as fast as an allocator can get: It just increases a single variable when allocating and does nothing at all when deallocating. And RAM is cheap nowadays, right? :)
|
||||
### Reusing Freed Memory
|
||||
The heap memory is limited, so we should reuse freed memory for new allocations. This sounds simple, but is not so easy in practice since allocations can live arbitrarily long (and can be freed in an arbitrary order). This means that we need some kind of data structure to keep track of which memory areas are free and which are in use. This data structure should be very optimized since it causes overheads in both space (i.e. it needs backing memory) and time (i.e. accessing and organizing it needs CPU cycles).
|
||||
|
||||
(Don't worry, we will introduce a better allocator later in this post.)
|
||||
Our bump allocator only keeps track of the next free memory address, which doesn't suffice to keep track of freed memory areas. So our only choice is to ignore deallocations and leak the corresponding memory. Thus our allocator quickly runs out of memory in a real system, but it suffices for simple testing. Later in this post, we will introduce a better allocator that does not leak freed memory.
|
||||
|
||||
### Custom Allocators in Rust
|
||||
In order to use our crate as system allocator, we add some attributes at the beginning of the file:
|
||||
### Using it as System Allocator
|
||||
|
||||
``` rust
|
||||
// in libs/bump_allocator/src/lib.rs
|
||||
Above we saw that we can use a static allocator as system allocator through the `global_allocator` attribute:
|
||||
|
||||
#![feature(allocator)]
|
||||
|
||||
#![allocator]
|
||||
#![no_std]
|
||||
```
|
||||
The `#![allocator]` attribute tells the compiler that it should not link a default allocator when this crate is linked. The attribute is unstable and feature-gated, so we need to add `#![feature(allocator)]` as well. Allocator crates must not depend on [liballoc], because this would introduce a circular dependency. Thus, allocator crates can't use the standard library either (as it depends on `liballoc`). Therefore all allocator crates must be marked as `#![no_std]`.
|
||||
|
||||
[liballoc]: https://doc.rust-lang.org/nightly/alloc/index.html
|
||||
|
||||
According to [the book][custom-allocators], an allocator crate needs to implement the following five functions:
|
||||
|
||||
[custom-allocators]: https://doc.rust-lang.org/book/custom-allocators.html
|
||||
|
||||
``` rust
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_usable_size(size: usize, align: usize) -> usize {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_deallocate(ptr: *mut u8, size: usize, align: usize) {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_reallocate(ptr: *mut u8, size: usize, new_size: usize,
|
||||
align: usize) -> *mut u8 {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_reallocate_inplace(ptr: *mut u8, size: usize,
|
||||
new_size: usize, align: usize)
|
||||
-> usize {}
|
||||
```rust
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: MyAllocator = MyAllocator {...};
|
||||
```
|
||||
|
||||
These functions are highly unstable and the compiler does not check their types. So make sure that the type, number, and order of parameters are correct when you implement it.
|
||||
This requires an implementation of `Alloc` for `&MyAllocator`, i.e. a shared reference. If we try to add such an implementation for our bump allocator (`unsafe impl<'a> Alloc for &'a BumpAllocator`), we have a problem: Our `alloc` method requires updating the `next` field, which is not possible for a shared reference.
|
||||
|
||||
Let's look at each function individually:
|
||||
One solution could be to put the bump allocator behind a Mutex and wrap it into a new type, for which we can implement `Alloc` for a shared reference:
|
||||
|
||||
- The `__rust_allocate` function allocates a block of memory with the given size (in bytes) and alignment. _Alignment_ means that the start address of the allocation needs to be a multiple of the `align` parameter. This is required because some CPUs can only access e.g. 4 byte aligned addresses. The alignment is always a power of 2.
|
||||
- The `__rust_usable_size` returns the usable size of an allocation created with the specified size and alignment. The usable size is at least `size`, but might be larger if the allocator uses fixed block sizes. For example, a [buddy allocator] rounds the size of each allocation to the next power of two.
|
||||
- The `__rust_deallocate` function frees the memory block referenced `ptr` again. The `size` and `align` parameters contain the values that were used to create the allocation. Thus the allocator knows exactly how much memory it needs to free. In constrast, the [free function] of C only has a single `ptr` argument. So a C allocator needs to [maintain information][c free info] about the size of each block itself. In Rust, the compiler maintains this information for us.
|
||||
- The `__rust_reallocate` function changes the size of the block referenced by `ptr` from `size` to `new_size`. If it's possible to do in in-place, the function resizes the block and returns `ptr` again. Else, it allocates a new block of `new_size` and copies the memory contents from the old block. Then it frees the old block and returns the pointer to the new block.
|
||||
- The `__rust_reallocate_inplace` function tries to change the size of the block referenced by `ptr` from `size` to `new_size` without relocating the memory block. If it succeeds, it returns `usable_size(new_size, align)`, else it returns `usable_size(size, align)`.
|
||||
```rust
|
||||
struct LockedBumpAllocator(Mutex<BumpAllocator>);
|
||||
|
||||
[buddy allocator]: https://en.wikipedia.org/wiki/Buddy_memory_allocation
|
||||
[free function]: http://www.cplusplus.com/reference/cstdlib/free/
|
||||
[c free info]: http://stackoverflow.com/questions/1518711/how-does-free-know-how-much-to-free
|
||||
impl<'a> Alloc for &'a LockedBumpAllocator {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
self.0.lock().alloc(layout)
|
||||
}
|
||||
|
||||
A more detailed documentation for these functions can be found in the [API docs for alloc::heap][alloc::heap]. Note that all of these functions and custom allocators in general are _unstable_ (as indicated by the `allocator` feature gate).
|
||||
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
self.0.lock().dealloc(ptr, layout)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[alloc::heap]: https://doc.rust-lang.org/nightly/alloc/heap/
|
||||
However, there is a more interesting solution for our bump allocator that avoids locking altogether. The idea is to exploit that we only need to update a single `usize` field byusing an `AtomicUsize` type. This type uses special synchronized hardware instructions to ensure data race freedom without requiring locks.
|
||||
|
||||
### Implementation
|
||||
Let's implement the allocation functions using our new allocator. First we need a way to access the allocator. The functions do not know anything about our allocator, so we can only access it through a `static`:
|
||||
#### A lock-free Bump Allocator
|
||||
A lock-free implementation looks like this:
|
||||
|
||||
``` rust
|
||||
// in libs/bump_allocator/src/lib.rs
|
||||
```rust
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use spin::Mutex;
|
||||
/// A simple allocator that allocates memory linearly and ignores freed memory.
|
||||
#[derive(Debug)]
|
||||
pub struct BumpAllocator {
|
||||
heap_start: usize,
|
||||
heap_end: usize,
|
||||
next: AtomicUsize,
|
||||
}
|
||||
|
||||
extern crate spin;
|
||||
impl BumpAllocator {
|
||||
pub const fn new(heap_start: usize, heap_end: usize) -> Self {
|
||||
// NOTE: requires adding #![feature(const_atomic_usize_new)] to lib.rs
|
||||
Self { heap_start, heap_end, next: AtomicUsize::new(heap_start) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> Alloc for &'a BumpAllocator {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
loop {
|
||||
// load current state of the `next` field
|
||||
let current_next = self.next.load(Ordering::Relaxed);
|
||||
let alloc_start = align_up(current_next, layout.align());
|
||||
let alloc_end = alloc_start.saturating_add(layout.size());
|
||||
|
||||
if alloc_end <= self.heap_end {
|
||||
// update the `next` pointer if it still has the value `current_next`
|
||||
let next_now = self.next.compare_and_swap(current_next, alloc_end,
|
||||
Ordering::Relaxed);
|
||||
if next_now == current_next {
|
||||
// next address was successfully updated, allocation succeeded
|
||||
return Ok(alloc_start as *mut u8);
|
||||
}
|
||||
} else {
|
||||
return Err(AllocErr::Exhausted{ request: layout })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
// do nothing, leak memory
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The implementation is a bit more complicated now. First, there is now a `loop` around the whole method body, since we might need multiple tries until we succeed (e.g. if multiple threads try to allocate at the same time). Also, the loads operation is an explicit method call now, i.e. `self.next.load(Ordering::Relaxed)` instead of just `self.next`. The ordering parameter makes it possible to restrict the automatic instruction reordering performed by both the compiler and the CPU itself. For example, it is used when implementing locks to ensure that no write to the locked variable happens before the lock is acquired. We don't have such requirements, so we use the less restrictive `Relaxed` ordering.
|
||||
|
||||
The heart of this lock-free method is the `compare_and_swap` call that updates the `next` address:
|
||||
|
||||
```rust
|
||||
...
|
||||
let next_now = self.next.compare_and_swap(current_next, alloc_end,
|
||||
Ordering::Relaxed);
|
||||
if next_now == current_next {
|
||||
// next address was successfully updated, allocation succeeded
|
||||
return Ok(alloc_start as *mut u8);
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
Compare-and-swap is a special CPU instruction that updates a variable with a given value if it still contains the value we expect. If it doesn't, it means that another thread updated the value simultaneously, so we need to try again. The important feature is that this happens in a single uninteruptible operation (thus the name `atomic`), so no partial updates or intermediate states are possible.
|
||||
|
||||
In detail, `compare_and_swap` works by comparing `next` with the first argument and, in case they're equal, updates `next` with the second parameter (the previous value is returned). To find out whether a switch happened, we check the returned previous value of `next`. If it is equal to the first parameter, the values were swapped. Otherwise, we try again in the next loop iteration.
|
||||
|
||||
#### Setting the Global Allocator
|
||||
|
||||
Now we can define a static bump allocator, that we can set as system allocator:
|
||||
|
||||
```rust
|
||||
pub const HEAP_START: usize = 0o_000_001_000_000_0000;
|
||||
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
|
||||
|
||||
static BUMP_ALLOCATOR: Mutex<BumpAllocator> = Mutex::new(
|
||||
BumpAllocator::new(HEAP_START, HEAP_SIZE));
|
||||
#[global_allocator]
|
||||
static HEAP_ALLOCATOR: BumpAllocator = BumpAllocator::new(HEAP_START,
|
||||
HEAP_START + HEAP_SIZE);
|
||||
```
|
||||
|
||||
We use `0o_000_001_000_000_0000` as heap start address, which is the address starting at the second `P3` entry. It doesn't really matter which address we choose here as long as it's unused. We use a heap size of 100 KiB, which should be large enough for the near future. The static allocator is protected by a spinlock since we need to able to modify it. Our allocator crate is distinct from our main crate, so we need to add the `spin` dependency to its `Cargo.toml` as well. The easiest way is to run `cargo add spin` (using the [cargo-edit] crate).
|
||||
We use `0o_000_001_000_000_0000` as heap start address, which is the address starting at the second `P3` entry. It doesn't really matter which address we choose here as long as it's unused. We use a heap size of 100 KiB, which should be large enough for the near future.
|
||||
|
||||
[cargo-edit]: https://github.com/killercup/cargo-edit
|
||||
Putting the above in the `memory::heap_allocator` module would make most sense, but unfortunately there is currently a [weird bug][global allocator bug] in the global allocator implementation that requires putting the global allocator in the root module. I hope it's fixed soon, but until then we need to put the above lines in `src/lib.rs`. For that, we need to make the `memory::heap_allocator` module public and add an import for `BumpAllocator`. We also need to add the `#![feature(global_allocator)]` at the top of our `lib.rs`, since the `global_allocator` attribute is still unstable.
|
||||
|
||||
Now we can easily implement the `__rust_allocate` and `__rust_deallocate` functions:
|
||||
[global allocator bug]: https://github.com/rust-lang/rust/issues/44113
|
||||
|
||||
```rust
|
||||
// in libs/bump_allocator/src/lib.rs
|
||||
That's it! We have successfully created and linked a custom system allocator. Now we're ready to test it.
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
|
||||
BUMP_ALLOCATOR.lock().allocate(size, align).expect("out of memory")
|
||||
}
|
||||
### Testing
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_deallocate(_ptr: *mut u8, _size: usize,
|
||||
_align: usize)
|
||||
{
|
||||
// just leak it
|
||||
}
|
||||
```
|
||||
We use `expect` to panic in out of memory (OOM) situations. We could alternatively return a null pointer, which indicates an OOM situation to the Rust runtime. However, the runtime would react by aborting the process. On Linux, the abort function intentionally raises an [invalid opcode] exception, which would lead to a boot loop for our kernel. So panickying is a better solution for our kernel.
|
||||
|
||||
[invalid opcode]: http://wiki.osdev.org/Exceptions#Invalid_Opcode
|
||||
|
||||
We never allocate more memory than requested, so the `__rust_usable_size` function is simple:
|
||||
|
||||
```rust
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize {
|
||||
size
|
||||
}
|
||||
```
|
||||
|
||||
In order to keep things simple, we don't support the `__rust_reallocate_inplace` function and always return the old size:
|
||||
|
||||
```rust
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, size: usize,
|
||||
_new_size: usize, _align: usize) -> usize
|
||||
{
|
||||
size
|
||||
}
|
||||
```
|
||||
|
||||
Now only `__rust_reallocate` is left. It's a bit more difficult, since we need to copy the contents of the old allocation to the new allocation. However, we can just steal some code from the official [reallocate implementation for unix][unix realloc]:
|
||||
|
||||
[unix realloc]: https://github.com/rust-lang/rust/blob/c66d2380a810c9a2b3dbb4f93a830b101ee49cc2/src/liballoc_system/lib.rs#L98-L101
|
||||
|
||||
|
||||
``` rust
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_reallocate(ptr: *mut u8, size: usize, new_size: usize,
|
||||
align: usize) -> *mut u8 {
|
||||
use core::{ptr, cmp};
|
||||
|
||||
// from: https://github.com/rust-lang/rust/blob/
|
||||
// c66d2380a810c9a2b3dbb4f93a830b101ee49cc2/
|
||||
// src/liballoc_system/lib.rs#L98-L101
|
||||
|
||||
let new_ptr = __rust_allocate(new_size, align);
|
||||
unsafe { ptr::copy(ptr, new_ptr, cmp::min(size, new_size)) };
|
||||
__rust_deallocate(ptr, size, align);
|
||||
new_ptr
|
||||
}
|
||||
```
|
||||
|
||||
That's it! We have successfully created a custom allocator. Now we're ready to test it.
|
||||
|
||||
## Box, Vec, and Friends
|
||||
|
||||
In order to use our new allocator we import it in our main project:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs of our main project
|
||||
|
||||
extern crate bump_allocator;
|
||||
```
|
||||
|
||||
Additionally, we need to tell cargo where our `bump_allocator` crate lives:
|
||||
|
||||
``` toml
|
||||
# in Cargo.toml of our main project
|
||||
|
||||
[dependencies.bump_allocator]
|
||||
path = "libs/bump_allocator"
|
||||
```
|
||||
|
||||
Now we're able to import the `alloc` and `collections` crates in order to unlock `Box`, `Vec`, `BTreeMap`, and friends:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs of our main project
|
||||
|
||||
#![feature(alloc, collections)]
|
||||
|
||||
extern crate bump_allocator;
|
||||
extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate collections;
|
||||
```
|
||||
The `collections` crate provides the [format!] and [vec!] macros, so we use `#[macro_use]` to import them.
|
||||
|
||||
[format!]: //doc.rust-lang.org/nightly/collections/macro.format!.html
|
||||
[vec!]: https://doc.rust-lang.org/nightly/collections/macro.vec!.html
|
||||
|
||||
## Testing
|
||||
|
||||
Now we should be able to allocate memory on the heap. Let's try it in our `rust_main`:
|
||||
We should be able to allocate memory on the heap now. Let's try it in our `rust_main`:
|
||||
|
||||
```rust
|
||||
// in rust_main in src/lib.rs
|
||||
@@ -317,33 +331,9 @@ use alloc::boxed::Box;
|
||||
let heap_test = Box::new(42);
|
||||
```
|
||||
|
||||
When we try to compile it using `make run`, we get several linker errors about a function named `_Unwind_Resume`:
|
||||
When we run it, a triple fault occurs and causes permanent rebooting. Let's try debug it using QEMU and objdump as described [in the previous post][qemu debugging]:
|
||||
|
||||
```
|
||||
target/x86_64-unknown-linux-gnu/debug/libblog_os.a(bump_allocator-[…].0.o):
|
||||
In function `bump_allocator::__rust_allocate':
|
||||
/home/…/blog_os/libs/bump_allocator/src/lib.rs:19:
|
||||
undefined reference to `_Unwind_Resume'
|
||||
```
|
||||
|
||||
This function is part of Rust's unwinding machinery. We disabled most of by passing `-Z no-landing-pads` to rustc, but apparently our precompiled `libcollections` still links to it.
|
||||
|
||||
To work around this issue for now, we add a dummy function:
|
||||
|
||||
```rust
|
||||
// in libs/bump_allocator/src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn _Unwind_Resume() -> ! {
|
||||
loop{}
|
||||
}
|
||||
```
|
||||
|
||||
This is just a temporary fix to keep this post simple. We will resolve this issue in a better way in a future post.
|
||||
|
||||
Now our kernel compiles again. But when we run it, a triple fault occurs and causes permanent rebooting. We use QEMU for debugging as described [in the previous post][qemu debugging]:
|
||||
|
||||
[qemu debugging]: http://os.phil-opp.com/remap-the-kernel.html#debugging
|
||||
[qemu debugging]: @/edition-1/posts/07-remap-the-kernel/index.md#debugging
|
||||
|
||||
```
|
||||
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso
|
||||
@@ -353,9 +343,9 @@ check_exception old: 0xffffffff new 0xe
|
||||
SP=0010:0000000000116af0 CR2=0000000040000000
|
||||
…
|
||||
```
|
||||
Aha! It's a [page fault] (`v=0e`) and was caused by the code at `0x102860`. The code tried to write (`e=0002`) to address `0x40000000`. This address is `0o_000_001_000_000_0000` in octal, which is the `HEAP_START` address defined above. Of course it page-faults: We have forgotten to map the heap memory to some physical memory.
|
||||
Aha! It's a [page fault] \(`v=0e`) and was caused by the code at `0x102860`. The code tried to write (`e=0002`) to address `0x40000000`. This address is `0o_000_001_000_000_0000` in octal, which is the `HEAP_START` address defined above. Of course it page-faults: We have forgotten to map the heap memory to some physical memory.
|
||||
|
||||
[page fault]: http://wiki.osdev.org/Exceptions#Page_Fault
|
||||
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||
|
||||
### Some Refactoring
|
||||
In order to map the heap cleanly, we do a bit of refactoring first. We move all memory initialization from our `rust_main` to a new `memory::init` function. Now our `rust_main` looks like this:
|
||||
@@ -363,6 +353,7 @@ In order to map the heap cleanly, we do a bit of refactoring first. We move all
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
// ATTENTION: we have a very small stack and no guard page
|
||||
vga_buffer::clear_screen();
|
||||
@@ -423,8 +414,8 @@ pub fn init(boot_info: &BootInformation) {
|
||||
|
||||
We've just moved the code to a new function. However, we've sneaked some improvements in:
|
||||
|
||||
- An additional `.filter(|s| s.is_allocated())` in the calculation of `kernel_start` and `kernel_end`. This ignores all sections that aren't loaded to memory (such as debug sections). Thus, the kernel end address is no longer artifically increased by such sections.
|
||||
- We use the `start_address()` and `end_address()` methods of `boot_info` instead of calculating the adresses manually.
|
||||
- An additional `.filter(|s| s.is_allocated())` in the calculation of `kernel_start` and `kernel_end`. This ignores all sections that aren't loaded to memory (such as debug sections). Thus, the kernel end address is no longer artificially increased by such sections.
|
||||
- We use the `start_address()` and `end_address()` methods of `boot_info` instead of calculating the addresses manually.
|
||||
- We use the alternate `{:#x}` form when printing kernel/multiboot addresses. Before, we used `0x{:x}`, which leads to the same result. For a complete list of these “alternate” formatting forms, check out the [std::fmt documentation].
|
||||
|
||||
[std::fmt documentation]: https://doc.rust-lang.org/nightly/std/fmt/index.html#sign0
|
||||
@@ -451,7 +442,7 @@ extern crate once;
|
||||
|
||||
The crate provides an [assert_has_not_been_called!] macro (sorry for the long name :D). We can use it to fix the safety problem easily:
|
||||
|
||||
[assert_has_not_been_called!]: https://phil-opp.rustdocs.org/once/macro.assert_has_not_been_called!.html
|
||||
[assert_has_not_been_called!]: https://docs.rs/once/0.3.2/once/macro.assert_has_not_been_called!.html
|
||||
|
||||
``` rust
|
||||
// in src/memory/mod.rs
|
||||
@@ -468,9 +459,10 @@ That's it. Now our `memory::init` function can only be called once. The macro wo
|
||||
[AtomicBool]: https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicBool.html
|
||||
|
||||
### Mapping the Heap
|
||||
Now we're ready to map the heap pages. In order to do it, we need access to the `ActivePageTable` or `Mapper` instance (see the [previous post]). Therefore we return it from the `paging::remap_the_kernel` function:
|
||||
Now we're ready to map the heap pages. In order to do it, we need access to the `ActivePageTable` or `Mapper` instance (see the [page table] and [kernel remapping] posts). For that we return it from the `paging::remap_the_kernel` function:
|
||||
|
||||
[previous post]: {{ page.previous.url }}
|
||||
[page table]: @/edition-1/posts/06-page-tables/index.md
|
||||
[kernel remapping]: @/edition-1/posts/07-remap-the-kernel/index.md
|
||||
|
||||
```rust
|
||||
// in src/memory/paging/mod.rs
|
||||
@@ -502,7 +494,7 @@ pub fn init(boot_info: &BootInformation) {
|
||||
boot_info);
|
||||
|
||||
use self::paging::Page;
|
||||
use bump_allocator::{HEAP_START, HEAP_SIZE};
|
||||
use {HEAP_START, HEAP_SIZE};
|
||||
|
||||
let heap_start_page = Page::containing_address(HEAP_START);
|
||||
let heap_end_page = Page::containing_address(HEAP_START + HEAP_SIZE-1);
|
||||
@@ -510,6 +502,7 @@ pub fn init(boot_info: &BootInformation) {
|
||||
for page in Page::range_inclusive(heap_start_page, heap_end_page) {
|
||||
active_table.map(page, paging::WRITABLE, &mut frame_allocator);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `Page::range_inclusive` function is just a copy of the `Frame::range_inclusive` function:
|
||||
@@ -572,7 +565,7 @@ for i in &vec_test {
|
||||
}
|
||||
```
|
||||
|
||||
We can also use all other types of the `alloc` and `collections` crates, including:
|
||||
We can also use all other types of the `alloc` crate, including:
|
||||
|
||||
- the reference counted pointers [Rc] and [Arc]
|
||||
- the owned string type [String] and the [format!] macro
|
||||
@@ -581,14 +574,14 @@ We can also use all other types of the `alloc` and `collections` crates, includi
|
||||
- [BinaryHeap]
|
||||
- [BTreeMap] and [BTreeSet]
|
||||
|
||||
[Rc]: https://doc.rust-lang.org/nightly/alloc/rc/
|
||||
[Arc]: https://doc.rust-lang.org/nightly/alloc/arc/
|
||||
[String]: https://doc.rust-lang.org/nightly/collections/string/struct.String.html
|
||||
[Linked List]: https://doc.rust-lang.org/nightly/collections/linked_list/struct.LinkedList.html
|
||||
[VecDeque]: https://doc.rust-lang.org/nightly/collections/vec_deque/struct.VecDeque.html
|
||||
[BinaryHeap]: https://doc.rust-lang.org/nightly/collections/binary_heap/struct.BinaryHeap.html
|
||||
[BTreeMap]: https://doc.rust-lang.org/nightly/collections/btree_map/struct.BTreeMap.html
|
||||
[BTreeSet]: https://doc.rust-lang.org/nightly/collections/btree_set/struct.BTreeSet.html
|
||||
[Rc]: https://doc.rust-lang.org/1.10.0/alloc/rc/
|
||||
[Arc]: https://doc.rust-lang.org/1.10.0/alloc/arc/
|
||||
[String]: https://doc.rust-lang.org/1.10.0/collections/string/struct.String.html
|
||||
[Linked List]: https://doc.rust-lang.org/1.10.0/collections/linked_list/struct.LinkedList.html
|
||||
[VecDeque]: https://doc.rust-lang.org/1.10.0/collections/vec_deque/struct.VecDeque.html
|
||||
[BinaryHeap]: https://doc.rust-lang.org/1.10.0/collections/binary_heap/struct.BinaryHeap.html
|
||||
[BTreeMap]: https://doc.rust-lang.org/1.10.0/collections/btree_map/struct.BTreeMap.html
|
||||
[BTreeSet]: https://doc.rust-lang.org/1.10.0/collections/btree_set/struct.BTreeSet.html
|
||||
|
||||
## A better Allocator
|
||||
Right now, we leak every freed memory block. Thus, we run out of memory quickly, for example, by creating a new `String` in each iteration of a loop:
|
||||
@@ -621,25 +614,25 @@ We will choose another solution with different tradoffs. It's not clearly “bet
|
||||
|
||||
A freed memory block is not used anymore and no one needs the stored information. It is still mapped to a virtual address and backed by a physical page. So we just store the information about the freed block _in the block itself_. We keep a pointer to the first block and store a pointer to the next block in each block. Thus, we create a single linked list:
|
||||
|
||||

|
||||

|
||||
|
||||
In the following, we call a freed block a _hole_. Each hole stores its size and a pointer to the next hole. If a hole is larger than needed, we leave the remaining memory unused. By storing a pointer to the first hole, we are able to traverse the complete list.
|
||||
|
||||
#### Initialization
|
||||
When the heap is created, all of its memory is unused. Thus, it forms a single large hole:
|
||||
|
||||

|
||||

|
||||
|
||||
The optional pointer to the next hole is set to `None`.
|
||||
|
||||
#### Allocation
|
||||
In order to allocate a block of memory, we need to find a hole that satisfies the size and alignment requirements. If the found hole is larger than required, we split it into two smaller holes. For example, when we allocate a 24 byte block right after initialization, we split the single hole into a hole of size 24 and a hole with the remaining size:
|
||||
|
||||

|
||||

|
||||
|
||||
Then we use the new 24 byte hole to perform the allocation:
|
||||
|
||||

|
||||

|
||||
|
||||
To find a suitable hole, we can use several search strategies:
|
||||
|
||||
@@ -658,11 +651,11 @@ However, both best fit and worst fit have a significant problem: They need to sc
|
||||
#### Deallocation
|
||||
To deallocate a block of memory, we can just insert its corresponding hole somewhere into the list. However, we need to merge adjacent holes. Otherwise, we are unable to reuse the freed memory for larger allocations. For example:
|
||||
|
||||

|
||||

|
||||
|
||||
In order to use these adjacent holes for a large allocation, we need to merge them to a single large hole first:
|
||||
|
||||

|
||||

|
||||
|
||||
The easiest way to ensure that adjacent holes are always merged, is to keep the hole list sorted by address. Thus, we only need to check the predecessor and the successor in the list when we free a memory block. If they are adjacent to the freed block, we merge the corresponding holes. Else, we insert the freed block as a new hole at the correct position.
|
||||
|
||||
@@ -673,167 +666,54 @@ The detailed implementation would go beyond the scope of this post, since it con
|
||||
- We need to satisfy the alignment requirements, which requires additional splitting logic.
|
||||
- The minimal hole size of 16 bytes: We must not create smaller holes when splitting a hole.
|
||||
|
||||
I created the [linked_list_allocator] crate to handle all of these cases. It consists of a [Heap struct] that provides an `allocate_first_fit` and a `deallocate` method. If you are interested in the implementation details, check out the [source code][linked_list_allocator source].
|
||||
I created the [linked_list_allocator] crate to handle all of these cases. It consists of a [Heap struct] that provides an `allocate_first_fit` and a `deallocate` method. It also contains a [LockedHeap] type that wraps `Heap` into spinlock so that it's usable as a static system allocator. If you are interested in the implementation details, check out the [source code][linked_list_allocator source].
|
||||
|
||||
[linked_list_allocator]: https://crates.io/crates/linked_list_allocator
|
||||
[Heap struct]: http://phil-opp.github.io/linked-list-allocator/linked_list_allocator/struct.Heap.html
|
||||
[linked_list_allocator]: https://docs.rs/crate/linked_list_allocator/0.4.1
|
||||
[Heap struct]: https://docs.rs/linked_list_allocator/0.4.1/linked_list_allocator/struct.Heap.html
|
||||
[LockedHeap]: https://docs.rs/linked_list_allocator/0.4.1/linked_list_allocator/struct.LockedHeap.html
|
||||
[linked_list_allocator source]: https://github.com/phil-opp/linked-list-allocator
|
||||
|
||||
So we just need to implement Rust's allocation modules and integrate it into our kernel. We start by creating a new `hole_list_allocator`[^1] crate inside the `libs` directory:
|
||||
We need to add the extern crate to our `Cargo.toml` and our `lib.rs`:
|
||||
|
||||
[^1]: The name `linked_list_allocator` is already taken, sorry :P.
|
||||
|
||||
```shell
|
||||
> cd libs
|
||||
> cargo new hole_list_allocator
|
||||
> cd hole_list_allocator
|
||||
```
|
||||
|
||||
We add the `allocator` and `no_std` attributes to `src/lib.rs` like described above:
|
||||
|
||||
```rust
|
||||
// in libs/hole_list_allocator/src/lib.rs
|
||||
|
||||
#![feature(allocator)]
|
||||
|
||||
#![allocator]
|
||||
#![no_std]
|
||||
```
|
||||
|
||||
We also add a static allocator protected by a spinlock, but this time we use the `Heap` type of the `linked_list_allocator` crate:
|
||||
|
||||
```rust
|
||||
// in libs/hole_list_allocator/src/lib.rs
|
||||
|
||||
#![feature(const_fn)]
|
||||
|
||||
use spin::Mutex;
|
||||
use linked_list_allocator::Heap;
|
||||
|
||||
extern crate spin;
|
||||
extern crate linked_list_allocator;
|
||||
|
||||
pub const HEAP_START: usize = 0o_000_001_000_000_0000;
|
||||
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
|
||||
|
||||
static HEAP: Mutex<Heap> = Mutex::new(Heap::new(HEAP_START, HEAP_SIZE));
|
||||
```
|
||||
Note that we use the same values for `HEAP_START` and `HEAP_SIZE` as in the `bump_allocator`.
|
||||
|
||||
We need to add the extern crates to our `Cargo.toml`:
|
||||
|
||||
``` shell
|
||||
> cargo add spin
|
||||
``` bash
|
||||
> cargo add linked_list_allocator
|
||||
```
|
||||
|
||||
However, we get an error when we try to compile it:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
extern crate linked_list_allocator;
|
||||
```
|
||||
error: function calls in statics are limited to constant functions,
|
||||
struct and enum constructors [E0015]
|
||||
static HEAP: Mutex<Heap> = Mutex::new(Heap::new(HEAP_START, HEAP_SIZE));
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
The reason is that the `Heap::new` function needs to initialize the first hole (like described [above](#initialization)). This can't be done at compile time, so the function can't be a `const` function. Therefore we can't use it to initialize a static.
|
||||
|
||||
There is an easy solution for crates with access to the standard library: [lazy_static]. It automatically initializes the static when it's used the first time. By default, it relies on the `std::sync::once` module and is thus unusable in our kernel. Fortunately it has a `spin_no_std` feature for `no_std` projects.
|
||||
|
||||
[lazy_static]: https://github.com/rust-lang-nursery/lazy-static.rs
|
||||
|
||||
So let's use the `lazy_static!` macro to fix our `hole_list_allocator`:
|
||||
|
||||
```toml
|
||||
# in libs/hole_list_allocator/Cargo.toml
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "0.2.1"
|
||||
features = ["spin_no_std"]
|
||||
```
|
||||
Now we can change our global allocator:
|
||||
|
||||
```rust
|
||||
// in libs/hole_list_allocator/src/lib.rs
|
||||
use linked_list_allocator::LockedHeap;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
static ref HEAP: Mutex<Heap> = Mutex::new(unsafe {
|
||||
Heap::new(HEAP_START, HEAP_SIZE)
|
||||
});
|
||||
}
|
||||
#[global_allocator]
|
||||
static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||
```
|
||||
The `unsafe` block is required since `Heap::new` is `unsafe`. It's unsafe because it assumes that `HEAP_START` is a valid and unused address.
|
||||
|
||||
Now we can implement the allocation functions:
|
||||
We can't initialize the linked list allocator statically, since it needs to initialize the first hole (like described [above](#initialization)). This can't be done at compile time, so the function can't be a `const` function. Therefore we can only create an empty heap and initialize it later at runtime. For that, we add the following lines to our `rust_main` function:
|
||||
|
||||
```rust
|
||||
// in libs/hole_list_allocator/src/lib.rs
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
|
||||
HEAP.lock().allocate_first_fit(size, align).expect("out of memory")
|
||||
}
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
[…]
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_deallocate(ptr: *mut u8, size: usize, align: usize) {
|
||||
unsafe { HEAP.lock().deallocate(ptr, size, align) };
|
||||
// set up guard page and map the heap pages
|
||||
memory::init(boot_info);
|
||||
|
||||
// initialize the heap allocator
|
||||
unsafe {
|
||||
HEAP_ALLOCATOR.lock().init(HEAP_START, HEAP_START + HEAP_SIZE);
|
||||
}
|
||||
[…]
|
||||
}
|
||||
```
|
||||
|
||||
The remaining functions are implemented like above:
|
||||
|
||||
```rust
|
||||
// in libs/hole_list_allocator/src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize {
|
||||
size
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, size: usize,
|
||||
_new_size: usize, _align: usize) -> usize
|
||||
{
|
||||
size
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_reallocate(ptr: *mut u8, size: usize, new_size: usize,
|
||||
align: usize) -> *mut u8 {
|
||||
use core::{ptr, cmp};
|
||||
|
||||
// from: https://github.com/rust-lang/rust/blob/
|
||||
// c66d2380a810c9a2b3dbb4f93a830b101ee49cc2/
|
||||
// src/liballoc_system/lib.rs#L98-L101
|
||||
|
||||
let new_ptr = __rust_allocate(new_size, align);
|
||||
unsafe { ptr::copy(ptr, new_ptr, cmp::min(size, new_size)) };
|
||||
__rust_deallocate(ptr, size, align);
|
||||
new_ptr
|
||||
}
|
||||
```
|
||||
|
||||
Now we just need to replace every use of `bump_allocator` with `hole_list_allocator` in our kernel:
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
[dependencies.hole_list_allocator]
|
||||
path = "libs/hole_list_allocator"
|
||||
```
|
||||
|
||||
```diff
|
||||
in src/lib.rs:
|
||||
|
||||
-extern crate bump_allocator;
|
||||
+extern crate hole_list_allocator;
|
||||
|
||||
in memory::init in src/memory/mod.rs:
|
||||
|
||||
-use bump_allocator::{HEAP_START, HEAP_SIZE};
|
||||
+use hole_list_allocator::{HEAP_START, HEAP_SIZE};
|
||||
```
|
||||
It is important that we initialize the heap _after_ mapping the heap pages, since the init function writes to the heap memory (the first hole).
|
||||
|
||||
Our kernel uses the new allocator now, so we can deallocate memory without leaking it. The example from above should work now without causing an OOM situation:
|
||||
|
||||
@@ -857,4 +737,4 @@ Now we're able to use heap storage in our kernel without leaking memory. This al
|
||||
## What's next?
|
||||
This post concludes the section about memory management for now. We will revisit this topic eventually, but now it's time to explore other topics. The upcoming posts will be about CPU exceptions and interrupts. We will catch all page, double, and triple faults and create a driver to read keyboard input. The [next post] starts by setting up a so-called _Interrupt Descriptor Table_.
|
||||
|
||||
[next post]: {{% relref "2016-05-28-catching-exceptions.md" %}}
|
||||
[next post]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 20 KiB |
2
blog/content/edition-1/posts/08-kernel-heap/overview.svg
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
473
blog/content/edition-1/posts/09-handling-exceptions/index.md
Normal file
@@ -0,0 +1,473 @@
|
||||
+++
|
||||
title = "Handling Exceptions"
|
||||
weight = 9
|
||||
path = "handling-exceptions"
|
||||
aliases = ["handling-exceptions.html"]
|
||||
date = 2017-03-26
|
||||
template = "edition-1/page.html"
|
||||
+++
|
||||
|
||||
In this post, we start exploring CPU exceptions. Exceptions occur in various erroneous situations, for example when accessing an invalid memory address or when dividing by zero. To catch them, we have to set up an _interrupt descriptor table_ that provides handler functions. At the end of this post, our kernel will be able to catch [breakpoint exceptions] and to resume normal execution afterwards.
|
||||
|
||||
[breakpoint exceptions]: https://wiki.osdev.org/Exceptions#Breakpoint
|
||||
|
||||
<!-- more -->
|
||||
|
||||
As always, the complete source code is available on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
||||
|
||||
[GitHub]: https://github.com/phil-opp/blog_os/tree/first_edition_post_9
|
||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||
|
||||
## Exceptions
|
||||
An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type.
|
||||
|
||||
We've already seen several types of exceptions in our kernel:
|
||||
|
||||
- **Invalid Opcode**: This exception occurs when the current instruction is invalid. For example, this exception occurred when we tried to use SSE instructions before enabling SSE. Without SSE, the CPU didn't know the `movups` and `movaps` instructions, so it throws an exception when it stumbles over them.
|
||||
- **Page Fault**: A page fault occurs on illegal memory accesses. For example, if the current instruction tries to read from an unmapped page or tries to write to a read-only page.
|
||||
- **Double Fault**: When an exception occurs, the CPU tries to call the corresponding handler function. If another exception occurs _while calling the exception handler_, the CPU raises a double fault exception. This exception also occurs when there is no handler function registered for an exception.
|
||||
- **Triple Fault**: If an exception occurs while the CPU tries to call the double fault handler function, it issues a fatal _triple fault_. We can't catch or handle a triple fault. Most processors react by resetting themselves and rebooting the operating system. This causes the bootloops we experienced in the previous posts.
|
||||
|
||||
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
||||
|
||||
[exceptions]: https://wiki.osdev.org/Exceptions
|
||||
|
||||
### The Interrupt Descriptor Table
|
||||
In order to catch and handle exceptions, we have to set up a so-called _Interrupt Descriptor Table_ (IDT). In this table we can specify a handler function for each CPU exception. The hardware uses this table directly, so we need to follow a predefined format. Each entry must have the following 16-byte structure:
|
||||
|
||||
Type| Name | Description
|
||||
----|--------------------------|-----------------------------------
|
||||
u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function.
|
||||
u16 | GDT selector | Selector of a code segment in the GDT.
|
||||
u16 | Options | (see below)
|
||||
u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function.
|
||||
u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function.
|
||||
u32 | Reserved |
|
||||
|
||||
The options field has the following format:
|
||||
|
||||
Bits | Name | Description
|
||||
------|-----------------------------------|-----------------------------------
|
||||
0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called.
|
||||
3-7 | Reserved |
|
||||
8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called.
|
||||
9-11 | must be one |
|
||||
12 | must be zero |
|
||||
13‑14 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler.
|
||||
15 | Present |
|
||||
|
||||
Each exception has a predefined IDT index. For example the invalid opcode exception has table index 6 and the page fault exception has table index 14. Thus, the hardware can automatically load the corresponding IDT entry for each exception. The [Exception Table][exceptions] in the OSDev wiki shows the IDT indexes of all exceptions in the “Vector nr.” column.
|
||||
|
||||
When an exception occurs, the CPU roughly does the following:
|
||||
|
||||
1. Push some registers on the stack, including the instruction pointer and the [RFLAGS] register. (We will use these values later in this post.)
|
||||
2. Read the corresponding entry from the Interrupt Descriptor Table (IDT). For example, the CPU reads the 14-th entry when a page fault occurs.
|
||||
3. Check if the entry is present. Raise a double fault if not.
|
||||
4. Disable interrupts if the entry is an interrupt gate (bit 40 not set).
|
||||
5. Load the specified GDT selector into the CS segment.
|
||||
6. Jump to the specified handler function.
|
||||
|
||||
[RFLAGS]: https://en.wikipedia.org/wiki/FLAGS_register
|
||||
|
||||
## An IDT Type
|
||||
Instead of creating our own IDT type, we will use the [`Idt` struct] of the `x86_64` crate, which looks like this:
|
||||
|
||||
[`Idt` struct]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html
|
||||
|
||||
``` rust
|
||||
#[repr(C)]
|
||||
pub struct Idt {
|
||||
pub divide_by_zero: IdtEntry<HandlerFunc>,
|
||||
pub debug: IdtEntry<HandlerFunc>,
|
||||
pub non_maskable_interrupt: IdtEntry<HandlerFunc>,
|
||||
pub breakpoint: IdtEntry<HandlerFunc>,
|
||||
pub overflow: IdtEntry<HandlerFunc>,
|
||||
pub bound_range_exceeded: IdtEntry<HandlerFunc>,
|
||||
pub invalid_opcode: IdtEntry<HandlerFunc>,
|
||||
pub device_not_available: IdtEntry<HandlerFunc>,
|
||||
pub double_fault: IdtEntry<HandlerFuncWithErrCode>,
|
||||
pub invalid_tss: IdtEntry<HandlerFuncWithErrCode>,
|
||||
pub segment_not_present: IdtEntry<HandlerFuncWithErrCode>,
|
||||
pub stack_segment_fault: IdtEntry<HandlerFuncWithErrCode>,
|
||||
pub general_protection_fault: IdtEntry<HandlerFuncWithErrCode>,
|
||||
pub page_fault: IdtEntry<PageFaultHandlerFunc>,
|
||||
pub x87_floating_point: IdtEntry<HandlerFunc>,
|
||||
pub alignment_check: IdtEntry<HandlerFuncWithErrCode>,
|
||||
pub machine_check: IdtEntry<HandlerFunc>,
|
||||
pub simd_floating_point: IdtEntry<HandlerFunc>,
|
||||
pub virtualization: IdtEntry<HandlerFunc>,
|
||||
pub security_exception: IdtEntry<HandlerFuncWithErrCode>,
|
||||
pub interrupts: [IdtEntry<HandlerFunc>; 224],
|
||||
// some fields omitted
|
||||
}
|
||||
```
|
||||
|
||||
The fields have the type [`IdtEntry<F>`], which is a struct that represents the fields of an IDT entry (see the table above). The type parameter `F` defines the expected handler function type. We see that some entries require a [`HandlerFunc`] and some entries require a [`HandlerFuncWithErrCode`]. The page fault even has its own special type: [`PageFaultHandlerFunc`].
|
||||
|
||||
[`IdtEntry<F>`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.IdtEntry.html
|
||||
[`HandlerFunc`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.HandlerFunc.html
|
||||
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
|
||||
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.PageFaultHandlerFunc.html
|
||||
|
||||
Let's look at the `HandlerFunc` type first:
|
||||
|
||||
```rust
|
||||
type HandlerFunc = extern "x86-interrupt" fn(_: &mut ExceptionStackFrame);
|
||||
```
|
||||
|
||||
It's a [type alias] for an `extern "x86-interrupt" fn` type. The `extern` keyword defines a function with a [foreign calling convention] and is often used to communicate with C code (`extern "C" fn`). But what is the `x86-interrupt` calling convention?
|
||||
|
||||
[type alias]: https://doc.rust-lang.org/book/type-aliases.html
|
||||
[foreign calling convention]: https://doc.rust-lang.org/1.30.0/book/first-edition/ffi.html#foreign-calling-conventions
|
||||
|
||||
## The Interrupt Calling Convention
|
||||
Exceptions are quite similar to function calls: The CPU jumps to the first instruction of the called function and executes it. Afterwards, if the function is not diverging, the CPU jumps to the return address and continues the execution of the parent function.
|
||||
|
||||
However, there is a major difference between exceptions and function calls: A function call is invoked voluntary by a compiler inserted `call` instruction, while an exception might occur at _any_ instruction. In order to understand the consequences of this difference, we need to examine function calls in more detail.
|
||||
|
||||
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
|
||||
|
||||
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
|
||||
[System V ABI]: https://refspecs.linuxbase.org/elf/gabi41.pdf
|
||||
|
||||
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
||||
- additional arguments are passed on the stack
|
||||
- results are returned in `rax` and `rdx`
|
||||
|
||||
Note that Rust does not follow the C ABI (in fact, [there isn't even a Rust ABI yet][rust abi]). So these rules apply only to functions declared as `extern "C" fn`.
|
||||
|
||||
[rust abi]: https://github.com/rust-lang/rfcs/issues/600
|
||||
|
||||
### Preserved and Scratch Registers
|
||||
The calling convention divides the registers in two parts: _preserved_ and _scratch_ registers.
|
||||
|
||||
The values of _preserved_ registers must remain unchanged across function calls. So a called function (the _“callee”_) is only allowed to overwrite these registers if it restores their original values before returning. Therefore these registers are called _“callee-saved”_. A common pattern is to save these registers to the stack at the function's beginning and restore them just before returning.
|
||||
|
||||
In contrast, a called function is allowed to overwrite _scratch_ registers without restrictions. If the caller wants to preserve the value of a scratch register across a function call, it needs to backup and restore it before the function call (e.g. by pushing it to the stack). So the scratch registers are _caller-saved_.
|
||||
|
||||
On x86_64, the C calling convention specifies the following preserved and scratch registers:
|
||||
|
||||
preserved registers | scratch registers
|
||||
---|---
|
||||
`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11`
|
||||
_callee-saved_ | _caller-saved_
|
||||
|
||||
The compiler knows these rules, so it generates the code accordingly. For example, most functions begin with a `push rbp`, which backups `rbp` on the stack (because it's a callee-saved register).
|
||||
|
||||
### Preserving all Registers
|
||||
In contrast to function calls, exceptions can occur on _any_ instruction. In most cases we don't even know at compile time if the generated code will cause an exception. For example, the compiler can't know if an instruction causes a stack overflow or a page fault.
|
||||
|
||||
Since we don't know when an exception occurs, we can't backup any registers before. This means that we can't use a calling convention that relies on caller-saved registers for exception handlers. Instead, we need a calling convention means that preserves _all registers_. The `x86-interrupt` calling convention is such a calling convention, so it guarantees that all register values are restored to their original values on function return.
|
||||
|
||||
### The Exception Stack Frame
|
||||
On a normal function call (using the `call` instruction), the CPU pushes the return address before jumping to the target function. On function return (using the `ret` instruction), the CPU pops this return address and jumps to it. So the stack frame of a normal function call looks like this:
|
||||
|
||||

|
||||
|
||||
For exception and interrupt handlers, however, pushing a return address would not suffice, since interrupt handlers often run in a different context (stack pointer, CPU flags, etc.). Instead, the CPU performs the following steps when an interrupt occurs:
|
||||
|
||||
1. **Aligning the stack pointer**: An interrupt can occur at any instructions, so the stack pointer can have any value, too. However, some CPU instructions (e.g. some SSE instructions) require that the stack pointer is aligned on a 16 byte boundary, therefore the CPU performs such an alignment right after the interrupt.
|
||||
2. **Switching stacks** (in some cases): A stack switch occurs when the CPU privilege level changes, for example when a CPU exception occurs in an user mode program. It is also possible to configure stack switches for specific interrupts using the so-called _Interrupt Stack Table_ (described in the next post).
|
||||
3. **Pushing the old stack pointer**: The CPU pushes the values of the stack pointer (`rsp`) and the stack segment (`ss`) registers at the time when the interrupt occurred (before the alignment). This makes it possible to restore the original stack pointer when returning from an interrupt handler.
|
||||
4. **Pushing and updating the `RFLAGS` register**: The [`RFLAGS`] register contains various control and status bits. On interrupt entry, the CPU changes some bits and pushes the old value.
|
||||
5. **Pushing the instruction pointer**: Before jumping to the interrupt handler function, the CPU pushes the instruction pointer (`rip`) and the code segment (`cs`). This is comparable to the return address push of a normal function call.
|
||||
6. **Pushing an error code** (for some exceptions): For some specific exceptions such as page faults, the CPU pushes an error code, which describes the cause of the exception.
|
||||
7. **Invoking the interrupt handler**: The CPU reads the address and the segment descriptor of the interrupt handler function from the corresponding field in the IDT. It then invokes this handler by loading the values into the `rip` and `cs` registers.
|
||||
|
||||
[`RFLAGS`]: https://en.wikipedia.org/wiki/FLAGS_register
|
||||
|
||||
So the _exception stack frame_ looks like this:
|
||||
|
||||

|
||||
|
||||
In the `x86_64` crate, the exception stack frame is represented by the [`ExceptionStackFrame`] struct. It is passed to interrupt handlers as `&mut` and can be used to retrieve additional information about the exception's cause. The struct contains no error code field, since only some few exceptions push an error code. These exceptions use the separate [`HandlerFuncWithErrCode`] function type, which has an additional `error_code` argument.
|
||||
|
||||
[`ExceptionStackFrame`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.ExceptionStackFrame.html
|
||||
|
||||
### Behind the Scenes
|
||||
The `x86-interrupt` calling convention is a powerful abstraction that hides almost all of the messy details of the exception handling process. However, sometimes it's useful to know what's happening behind the curtain. Here is a short overview of the things that the `x86-interrupt` calling convention takes care of:
|
||||
|
||||
- **Retrieving the arguments**: Most calling conventions expect that the arguments are passed in registers. This is not possible for exception handlers, since we must not overwrite any register values before backing them up on the stack. Instead, the `x86-interrupt` calling convention is aware that the arguments already lie on the stack at a specific offset.
|
||||
- **Returning using `iretq`**: Since the exception stack frame completely differs from stack frames of normal function calls, we can't return from handlers functions through the normal `ret` instruction. Instead, the `iretq` instruction must be used.
|
||||
- **Handling the error code**: The error code, which is pushed for some exceptions, makes things much more complex. It changes the stack alignment (see the next point) and needs to be popped off the stack before returning. The `x86-interrupt` calling convention handles all that complexity. However, it doesn't know which handler function is used for which exception, so it needs to deduce that information from the number of function arguments. That means that the programmer is still responsible to use the correct function type for each exception. Luckily, the `Idt` type defined by the `x86_64` crate ensures that the correct function types are used.
|
||||
- **Aligning the stack**: There are some instructions (especially SSE instructions) that require a 16-byte stack alignment. The CPU ensures this alignment whenever an exception occurs, but for some exceptions it destroys it again later when it pushes an error code. The `x86-interrupt` calling convention takes care of this by realigning the stack in this case.
|
||||
|
||||
If you are interested in more details: We also have a series of posts that explains exception handling using [naked functions] linked [at the end of this post][too-much-magic].
|
||||
|
||||
[naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
||||
[too-much-magic]: #too-much-magic
|
||||
|
||||
## Implementation
|
||||
Now that we've understood the theory, it's time to handle CPU exceptions in our kernel. We start by creating a new `interrupts` module:
|
||||
|
||||
``` rust
|
||||
// in src/lib.rs
|
||||
...
|
||||
mod interrupts;
|
||||
...
|
||||
```
|
||||
|
||||
In the new module, we create an `init` function, that creates a new `Idt`:
|
||||
|
||||
``` rust
|
||||
// in src/interrupts.rs
|
||||
|
||||
use x86_64::structures::idt::Idt;
|
||||
|
||||
pub fn init() {
|
||||
let mut idt = Idt::new();
|
||||
}
|
||||
```
|
||||
|
||||
Now we can add handler functions. We start by adding a handler for the [breakpoint exception]. The breakpoint exception is the perfect exception to test exception handling. Its only purpose is to temporary pause a program when the breakpoint instruction `int3` is executed.
|
||||
|
||||
[breakpoint exception]: https://wiki.osdev.org/Exceptions#Breakpoint
|
||||
|
||||
The breakpoint exception is commonly used in debuggers: When the user sets a breakpoint, the debugger overwrites the corresponding instruction with the `int3` instruction so that the CPU throws the breakpoint exception when it reaches that line. When the user wants to continue the program, the debugger replaces the `int3` instruction with the original instruction again and continues the program. For more details, see the ["_How debuggers work_"] series.
|
||||
|
||||
["_How debuggers work_"]: https://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints
|
||||
|
||||
For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.
|
||||
|
||||
[set the page table flags]: @/edition-1/posts/07-remap-the-kernel/index.md#using-the-correct-flags
|
||||
|
||||
So let's create a simple `breakpoint_handler` function and add it to our IDT:
|
||||
|
||||
```rust
|
||||
/// in src/interrupts.rs
|
||||
|
||||
use x86_64::structures::idt::ExceptionStackFrame;
|
||||
|
||||
pub fn init() {
|
||||
let mut idt = Idt::new();
|
||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn breakpoint_handler(
|
||||
stack_frame: &mut ExceptionStackFrame)
|
||||
{
|
||||
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
||||
}
|
||||
```
|
||||
|
||||
Our handler just outputs a message and pretty-prints the exception stack frame.
|
||||
|
||||
When we try to compile it, the following error occurs:
|
||||
|
||||
```
|
||||
error: x86-interrupt ABI is experimental and subject to change (see issue #40180)
|
||||
--> src/interrupts.rs:8:1
|
||||
|
|
||||
8 | extern "x86-interrupt" fn breakpoint_handler(
|
||||
| _^ starting here...
|
||||
9 | | stack_frame: &mut ExceptionStackFrame)
|
||||
10 | | {
|
||||
11 | | println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
||||
12 | | }
|
||||
| |_^ ...ending here
|
||||
|
|
||||
= help: add #![feature(abi_x86_interrupt)] to the crate attributes to enable
|
||||
```
|
||||
|
||||
This error occurs because the `x86-interrupt` calling convention is still unstable. To use it anyway, we have to explicitly enable it by adding `#![feature(abi_x86_interrupt)]` on the top of our `lib.rs`.
|
||||
|
||||
### Loading the IDT
|
||||
In order that the CPU uses our new interrupt descriptor table, we need to load it using the [`lidt`] instruction. The `Idt` struct of the `x86_64` provides a [`load`][Idt::load] method function for that. Let's try to use it:
|
||||
|
||||
[`lidt`]: https://www.felixcloutier.com/x86/lgdt:lidt
|
||||
[Idt::load]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html#method.load
|
||||
|
||||
```rust
|
||||
pub fn init() {
|
||||
let mut idt = Idt::new();
|
||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||
idt.load();
|
||||
}
|
||||
```
|
||||
|
||||
When we try to compile it now, the following error occurs:
|
||||
|
||||
```
|
||||
error: `idt` does not live long enough
|
||||
--> src/interrupts/mod.rs:43:5
|
||||
|
|
||||
43 | idt.load();
|
||||
| ^^^ does not live long enough
|
||||
44 | }
|
||||
| - borrowed value only lives until here
|
||||
|
|
||||
= note: borrowed value must be valid for the static lifetime...
|
||||
```
|
||||
|
||||
So the `load` methods expects a `&'static self`, that is a reference that is valid for the complete runtime of the program. The reason is that the CPU will access this table on every interrupt until we load a different IDT. So using a shorter lifetime than `'static` could lead to use-after-free bugs.
|
||||
|
||||
In fact, this is exactly what happens here. Our `idt` is created on the stack, so it is only valid inside the `init` function. Afterwards the stack memory is reused for other functions, so the CPU would interpret random stack memory as IDT. Luckily, the `Idt::load` method encodes this lifetime requirement in its function definition, so that the Rust compiler is able to prevent this possible bug at compile time.
|
||||
|
||||
In order to fix this problem, we need to store our `idt` at a place where it has a `'static` lifetime. To achieve this, we could either allocate our IDT on the heap using `Box` and then convert it to a `'static` reference or we can store the IDT as a `static`. Let's try the latter:
|
||||
|
||||
```rust
|
||||
static IDT: Idt = Idt::new();
|
||||
|
||||
pub fn init() {
|
||||
IDT.breakpoint.set_handler_fn(breakpoint_handler);
|
||||
IDT.load();
|
||||
}
|
||||
```
|
||||
|
||||
There are two problems with this. First, statics are immutable, so we can't modify the breakpoint entry from our `init` function. Second, the `Idt::new` function is not a [`const` function], so it can't be used to initialize a `static`. We could solve this problem by using a [`static mut`] of type `Option<Idt>`:
|
||||
|
||||
[`const` function]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
|
||||
[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
|
||||
|
||||
```rust
|
||||
static mut IDT: Option<Idt> = None;
|
||||
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
let IDT = Some(Idt::new());
|
||||
let idt = IDT.as_mut_ref().unwrap();
|
||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||
idt.load();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This variant compiles without errors but it's far from idiomatic. `static mut`s are very prone to data races, so we need an [`unsafe` block] on each access. Also, we need to explicitly `unwrap` the `IDT` on each use, since might be `None`.
|
||||
|
||||
[`unsafe` block]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers
|
||||
|
||||
#### Lazy Statics to the Rescue
|
||||
The one-time initialization of statics with non-const functions is a common problem in Rust. Fortunately, there already exists a good solution in a crate named [lazy_static]. This crate provides a `lazy_static!` macro that defines a lazily initialized `static`. Instead of computing its value at compile time, the `static` laziliy initializes itself when it's accessed the first time. Thus, the initialization happens at runtime so that arbitrarily complex initialization code is possible.
|
||||
|
||||
[lazy_static]: https://docs.rs/lazy_static/0.2.4/lazy_static/
|
||||
|
||||
Let's add the `lazy_static` crate to our project:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
```
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "0.2.4"
|
||||
features = ["spin_no_std"]
|
||||
```
|
||||
We need the `spin_no_std` feature, since we don't link the standard library. We also need the `#[macro_use]` attribute on the `extern crate` line to import the `lazy_static!` macro.
|
||||
|
||||
Now we can create our static IDT using `lazy_static`:
|
||||
|
||||
```rust
|
||||
lazy_static! {
|
||||
static ref IDT: Idt = {
|
||||
let mut idt = Idt::new();
|
||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||
idt
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
IDT.load();
|
||||
}
|
||||
```
|
||||
|
||||
Note how this solution requires no `unsafe` blocks or `unwrap` calls.
|
||||
|
||||
> ##### Aside: How does the `lazy_static!` macro work?
|
||||
>
|
||||
> The macro generates a `static` of type `Once<Idt>`. The [`Once`][spin::Once] type is provided by the `spin` crate and allows deferred one-time initialization. It is implemented using an [`AtomicUsize`] for synchronization and an [`UnsafeCell`] for storing the (possibly uninitialized) value. So this solution also uses `unsafe` behind the scenes, but it is abstracted away in a safe interface.
|
||||
|
||||
[spin::Once]: https://docs.rs/spin/0.4.5/spin/struct.Once.html
|
||||
[`AtomicUsize`]: https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicUsize.html
|
||||
[`UnsafeCell`]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html
|
||||
|
||||
### Testing it
|
||||
Now we should be able to handle breakpoint exceptions! Let's try it in our `rust_main`:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
pub extern "C" fn rust_main(...) {
|
||||
...
|
||||
memory::init(boot_info);
|
||||
|
||||
// initialize our IDT
|
||||
interrupts::init();
|
||||
|
||||
// invoke a breakpoint exception
|
||||
x86_64::instructions::interrupts::int3();
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
When we run it in QEMU now (using `make run`), we see the following:
|
||||
|
||||

|
||||
|
||||
It works! The CPU successfully invokes our breakpoint handler, which prints the message, and then returns back to the `rust_main` function, where the `It did not crash!` message is printed.
|
||||
|
||||
> **Aside**: If it doesn't work and a boot loop occurs, this might be caused by a kernel stack overflow. Try increasing the stack size to at least 16kB (4096 * 4 bytes) in the `boot.asm` file.
|
||||
|
||||
We see that the exception stack frame tells us the instruction and stack pointers at the time when the exception occurred. This information is very useful when debugging unexpected exceptions. For example, we can look at the corresponding assembly line using `objdump`:
|
||||
|
||||
```
|
||||
> objdump -d build/kernel-x86_64.bin | grep -B5 "1140a6:"
|
||||
00000000001140a0 <x86_64::instructions::interrupts::int3::h015bf61815bb8afe>:
|
||||
1140a0: 55 push %rbp
|
||||
1140a1: 48 89 e5 mov %rsp,%rbp
|
||||
1140a4: 50 push %rax
|
||||
1140a5: cc int3
|
||||
1140a6: 48 83 c4 08 add $0x8,%rsp
|
||||
```
|
||||
|
||||
The `-d` flags disassembles the `code` section and `-C` flag makes function names more readable by [demangling] them. The `-B` flag of `grep` specifies the number of preceding lines that should be shown (5 in our case).
|
||||
|
||||
[demangling]: https://en.wikipedia.org/wiki/Name_mangling
|
||||
|
||||
We clearly see the `int3` exception that caused the breakpoint exception at address `1140a5`. Wait… the stored instruction pointer was `1140a6`, which is a normal `add` operation. What's happening here?
|
||||
|
||||
### Faults, Aborts, and Traps
|
||||
The answer is that the stored instruction pointer only points to the causing instruction for _fault_ type exceptions, but not for _trap_ or _abort_ type exceptions. The difference between these types is the following:
|
||||
|
||||
- **Faults** are exceptions that can be corrected so that the program can continue as if nothing happened. An example is the [page fault], which can often be resolved by loading the accessed page from the disk into memory.
|
||||
- **Aborts** are fatal exceptions that can't be recovered. Examples are [machine check exception] or the [double fault].
|
||||
- **Traps** are only reported to the kernel, but don't hinder the continuation of the program. Examples are the breakpoint exception and the [overflow exception].
|
||||
|
||||
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||
[machine check exception]: https://wiki.osdev.org/Exceptions#Machine_Check
|
||||
[double fault]: https://wiki.osdev.org/Exceptions#Double_Fault
|
||||
[overflow exception]: https://wiki.osdev.org/Exceptions#Overflow
|
||||
|
||||
The reason for the diffent instruction pointer values is that the stored value is also the return address. So for faults, the instruction that caused the exception is restarted and might cause the same exception again if it's not resolved. This would not make much sense for traps, since invoking the breakpoint exception again would just cause another breakpoint exception[^fn-breakpoint-restart-use-cases]. Thus the instruction pointer points to the _next_ instruction for these exceptions.
|
||||
|
||||
In some cases, the distinction between faults and traps is vague. For example, the [debug exception] behaves like a fault in some cases, but like a trap in others. So to find out the meaning of the saved instruction pointer, it is a good idea to read the official documentation for the exception, which can be found in the [AMD64 manual] in Section 8.2. For example, for the breakpoint exception it says:
|
||||
|
||||
[debug exception]: https://wiki.osdev.org/Exceptions#Debug
|
||||
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||
|
||||
> `#BP` is a trap-type exception. The saved instruction pointer points to the byte after the `INT3` instruction.
|
||||
|
||||
The documentation of the [`Idt`] struct and the [OSDev Wiki][osdev wiki exceptions] also contain this information.
|
||||
|
||||
[`Idt`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html
|
||||
[osdev wiki exceptions]: https://wiki.osdev.org/Exceptions
|
||||
|
||||
## Too much Magic?
|
||||
The `x86-interrupt` calling convention and the [`Idt`] type made the exception handling process relatively straightforward and painless. If this was too much magic for you and you like to learn all the gory details of exception handling, we got you covered: Our [“Handling Exceptions with Naked Functions”] series shows how to handle exceptions without the `x86-interrupt` calling convention and also creates its own `Idt` type. Historically, these posts were the main exception handling posts before the `x86-interrupt` calling convention and the `x86_64` crate existed.
|
||||
|
||||
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||
|
||||
## What's next?
|
||||
We've successfully caught our first exception and returned from it! The next step is to add handlers for other common exceptions such as page faults. We also need to make sure that we never cause a [triple fault], since it causes a complete system reset. The next post explains how we can avoid this by correctly catching [double faults].
|
||||
|
||||
[triple fault]: https://wiki.osdev.org/Triple_Fault
|
||||
[double faults]: https://wiki.osdev.org/Double_Fault#Double_Fault
|
||||
|
||||
## Footnotes
|
||||
[^fn-breakpoint-restart-use-cases]: There are valid use cases for restarting an instruction that caused a breakpoint. The most common use case is a debugger: When setting a breakpoint on some code line, the debugger overwrites the corresponding instruction with an `int3` instruction, so that the CPU traps when that line is executed. When the user continues execution, the debugger swaps in the original instruction and continues the program from the replaced instruction.
|
||||
|
After Width: | Height: | Size: 17 KiB |
BIN
blog/content/edition-1/posts/10-double-faults/boot-loop.gif
Normal file
|
After Width: | Height: | Size: 39 KiB |
920
blog/content/edition-1/posts/10-double-faults/index.md
Normal file
@@ -0,0 +1,920 @@
|
||||
+++
|
||||
title = "Double Faults"
|
||||
weight = 10
|
||||
path = "double-faults"
|
||||
aliases = ["double-faults.html"]
|
||||
date = 2017-01-02
|
||||
template = "edition-1/page.html"
|
||||
+++
|
||||
|
||||
In this post we explore double faults in detail. We also set up an _Interrupt Stack Table_ to catch double faults on a separate kernel stack. This way, we can completely prevent triple faults, even on kernel stack overflow.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
As always, the complete source code is available on [GitHub]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
|
||||
|
||||
[GitHub]: https://github.com/phil-opp/blog_os/tree/first_edition_post_10
|
||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
||||
|
||||
## What is a Double Fault?
|
||||
In simplified terms, a double fault is a special exception that occurs when the CPU fails to invoke an exception handler. For example, it occurs when a page fault is triggered but there is no page fault handler registered in the [Interrupt Descriptor Table][IDT] (IDT). So it's kind of similar to catch-all blocks in programming languages with exceptions, e.g. `catch(...)` in C++ or `catch(Exception e)` in Java or C#.
|
||||
|
||||
[IDT]: @/edition-1/posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
|
||||
|
||||
A double fault behaves like a normal exception. It has the vector number `8` and we can define a normal handler function for it in the IDT. It is really important to provide a double fault handler, because if a double fault is unhandled a fatal _triple fault_ occurs. Triple faults can't be caught and most hardware reacts with a system reset.
|
||||
|
||||
### Triggering a Double Fault
|
||||
Let's provoke a double fault by triggering an exception for that we didn't define a handler function:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
...
|
||||
// initialize our IDT
|
||||
interrupts::init();
|
||||
|
||||
// trigger a page fault
|
||||
unsafe {
|
||||
*(0xdeadbeaf as *mut u64) = 42;
|
||||
};
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
We try to write to address `0xdeadbeaf`, but the corresponding page is not present in the page tables. Thus, a page fault occurs. We haven't registered a page fault handler in our [IDT], so a double fault occurs.
|
||||
|
||||
When we start our kernel now, we see that it enters an endless boot loop:
|
||||
|
||||

|
||||
|
||||
The reason for the boot loop is the following:
|
||||
|
||||
1. The CPU tries to write to `0xdeadbeaf`, which causes a page fault.
|
||||
2. The CPU looks at the corresponding entry in the IDT and sees that the present bit isn't set. Thus, it can't call the page fault handler and a double fault occurs.
|
||||
3. The CPU looks at the IDT entry of the double fault handler, but this entry is also non-present. Thus, a _triple_ fault occurs.
|
||||
4. A triple fault is fatal. QEMU reacts to it like most real hardware and issues a system reset.
|
||||
|
||||
So in order to prevent this triple fault, we need to either provide a handler function for page faults or a double fault handler. Let's start with the latter, since we want to avoid triple faults in all cases.
|
||||
|
||||
### A Double Fault Handler
|
||||
A double fault is a normal exception with an error code, so we can use our `handler_with_error_code` macro to create a wrapper function:
|
||||
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
|
||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||
idt.double_fault.set_handler_fn(double_fault_handler);
|
||||
|
||||
idt
|
||||
};
|
||||
}
|
||||
|
||||
// our new double fault handler
|
||||
extern "x86-interrupt" fn double_fault_handler(
|
||||
stack_frame: &mut ExceptionStackFrame, _error_code: u64)
|
||||
{
|
||||
println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
Our handler prints a short error message and dumps the exception stack frame. The error code of the double fault handler is always zero, so there's no reason to print it.
|
||||
|
||||
When we start our kernel now, we should see that the double fault handler is invoked:
|
||||
|
||||

|
||||
|
||||
It worked! Here is what happens this time:
|
||||
|
||||
1. The CPU executes tries to write to `0xdeadbeaf`, which causes a page fault.
|
||||
2. Like before, the CPU looks at the corresponding entry in the IDT and sees that the present bit isn't set. Thus, a double fault occurs.
|
||||
3. The CPU jumps to the – now present – double fault handler.
|
||||
|
||||
The triple fault (and the boot-loop) no longer occurs, since the CPU can now call the double fault handler.
|
||||
|
||||
That was quite straightforward! So why do we need a whole post for this topic? Well, we're now able to catch _most_ double faults, but there are some cases where our current approach doesn't suffice.
|
||||
|
||||
## Causes of Double Faults
|
||||
Before we look at the special cases, we need to know the exact causes of double faults. Above, we used a pretty vague definition:
|
||||
|
||||
> A double fault is a special exception that occurs when the CPU fails to invoke an exception handler.
|
||||
|
||||
What does _“fails to invoke”_ mean exactly? The handler is not present? The handler is [swapped out]? And what happens if a handler causes exceptions itself?
|
||||
|
||||
[swapped out]: http://pages.cs.wisc.edu/~remzi/OSTEP/vm-beyondphys.pdf
|
||||
|
||||
For example, what happens if… :
|
||||
|
||||
1. a divide-by-zero exception occurs, but the corresponding handler function is swapped out?
|
||||
2. a page fault occurs, but the page fault handler is swapped out?
|
||||
3. a divide-by-zero handler causes a breakpoint exception, but the breakpoint handler is swapped out?
|
||||
4. our kernel overflows its stack and the [guard page] is hit?
|
||||
|
||||
[guard page]: @/edition-1/posts/07-remap-the-kernel/index.md#creating-a-guard-page
|
||||
|
||||
Fortunately, the AMD64 manual ([PDF][AMD64 manual]) has an exact definition (in Section 8.2.9). According to it, a “double fault exception _can_ occur when a second exception occurs during the handling of a prior (first) exception handler”. The _“can”_ is important: Only very specific combinations of exceptions lead to a double fault. These combinations are:
|
||||
|
||||
First Exception | Second Exception
|
||||
----------------|-----------------
|
||||
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
||||
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
||||
|
||||
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Division_Error
|
||||
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
|
||||
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
|
||||
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault
|
||||
[General Protection Fault]: https://wiki.osdev.org/Exceptions#General_Protection_Fault
|
||||
[Page Fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||
|
||||
|
||||
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||
|
||||
So for example a divide-by-zero fault followed by a page fault is fine (the page fault handler is invoked), but a divide-by-zero fault followed by a general-protection fault leads to a double fault.
|
||||
|
||||
With the help of this table, we can answer the first three of the above questions:
|
||||
|
||||
1. If a divide-by-zero exception occurs and the corresponding handler function is swapped out, a _page fault_ occurs and the _page fault handler_ is invoked.
|
||||
2. If a page fault occurs and the page fault handler is swapped out, a _double fault_ occurs and the _double fault handler_ is invoked.
|
||||
3. If a divide-by-zero handler causes a breakpoint exception, the CPU tries to invoke the breakpoint handler. If the breakpoint handler is swapped out, a _page fault_ occurs and the _page fault handler_ is invoked.
|
||||
|
||||
In fact, even the case of a non-present handler follows this scheme: A non-present handler causes a _segment-not-present_ exception. We didn't define a segment-not-present handler, so another segment-not-present exception occurs. According to the table, this leads to a double fault.
|
||||
|
||||
### Kernel Stack Overflow
|
||||
Let's look at the fourth question:
|
||||
|
||||
> What happens if our kernel overflows its stack and the [guard page] is hit?
|
||||
|
||||
When our kernel overflows its stack and hits the guard page, a _page fault_ occurs. The CPU looks up the page fault handler in the IDT and tries to push the [exception stack frame] onto the stack. However, our current stack pointer still points to the non-present guard page. Thus, a second page fault occurs, which causes a double fault (according to the above table).
|
||||
|
||||
[exception stack frame]: @/edition-1/posts/09-handling-exceptions/index.md#the-exception-stack-frame
|
||||
|
||||
So the CPU tries to call our _double fault handler_ now. However, on a double fault the CPU tries to push the exception stack frame, too. Our stack pointer still points to the guard page, so a _third_ page fault occurs, which causes a _triple fault_ and a system reboot. So our current double fault handler can't avoid a triple fault in this case.
|
||||
|
||||
Let's try it ourselves! We can easily provoke a kernel stack overflow by calling a function that recurses endlessly:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
...
|
||||
// initialize our IDT
|
||||
interrupts::init();
|
||||
|
||||
fn stack_overflow() {
|
||||
stack_overflow(); // for each recursion, the return address is pushed
|
||||
}
|
||||
|
||||
// trigger a stack overflow
|
||||
stack_overflow();
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
When we try this code in QEMU, we see that the system enters a boot-loop again.
|
||||
|
||||
So how can we avoid this problem? We can't omit the pushing of the exception stack frame, since the CPU itself does it. So we need to ensure somehow that the stack is always valid when a double fault exception occurs. Fortunately, the x86_64 architecture has a solution to this problem.
|
||||
|
||||
## Switching Stacks
|
||||
The x86_64 architecture is able to switch to a predefined, known-good stack when an exception occurs. This switch happens at hardware level, so it can be performed before the CPU pushes the exception stack frame.
|
||||
|
||||
This switching mechanism is implemented as an _Interrupt Stack Table_ (IST). The IST is a table of 7 pointers to known-good stacks. In Rust-like pseudo code:
|
||||
|
||||
```rust
|
||||
struct InterruptStackTable {
|
||||
stack_pointers: [Option<StackPointer>; 7],
|
||||
}
|
||||
```
|
||||
|
||||
For each exception handler, we can choose a stack from the IST through the `options` field in the corresponding [IDT entry]. For example, we could use the first stack in the IST for our double fault handler. Then the CPU would automatically switch to this stack whenever a double fault occurs. This switch would happen before anything is pushed, so it would prevent the triple fault.
|
||||
|
||||
[IDT entry]: @/edition-1/posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
|
||||
|
||||
### Allocating a new Stack
|
||||
In order to fill an Interrupt Stack Table later, we need a way to allocate new stacks. Therefore we extend our `memory` module with a new `stack_allocator` submodule:
|
||||
|
||||
```rust
|
||||
// in src/memory/mod.rs
|
||||
|
||||
mod stack_allocator;
|
||||
|
||||
```
|
||||
|
||||
First, we create a new `StackAllocator` struct and a constructor function:
|
||||
|
||||
```rust
|
||||
// in src/memory/stack_allocator.rs
|
||||
|
||||
use memory::paging::PageIter;
|
||||
|
||||
pub struct StackAllocator {
|
||||
range: PageIter,
|
||||
}
|
||||
|
||||
impl StackAllocator {
|
||||
pub fn new(page_range: PageIter) -> StackAllocator {
|
||||
StackAllocator { range: page_range }
|
||||
}
|
||||
}
|
||||
```
|
||||
We create a simple `StackAllocator` that allocates stacks from a given range of pages (`PageIter` is an Iterator over a range of pages; we introduced it [in the kernel heap post].).
|
||||
|
||||
[in the kernel heap post]: @/edition-1/posts/08-kernel-heap/index.md#mapping-the-heap
|
||||
|
||||
We add a `alloc_stack` method that allocates a new stack:
|
||||
|
||||
```rust
|
||||
// in src/memory/stack_allocator.rs
|
||||
|
||||
use memory::paging::{self, Page, ActivePageTable};
|
||||
use memory::{PAGE_SIZE, FrameAllocator};
|
||||
|
||||
impl StackAllocator {
|
||||
pub fn alloc_stack<FA: FrameAllocator>(&mut self,
|
||||
active_table: &mut ActivePageTable,
|
||||
frame_allocator: &mut FA,
|
||||
size_in_pages: usize)
|
||||
-> Option<Stack> {
|
||||
if size_in_pages == 0 {
|
||||
return None; /* a zero sized stack makes no sense */
|
||||
}
|
||||
|
||||
// clone the range, since we only want to change it on success
|
||||
let mut range = self.range.clone();
|
||||
|
||||
// try to allocate the stack pages and a guard page
|
||||
let guard_page = range.next();
|
||||
let stack_start = range.next();
|
||||
let stack_end = if size_in_pages == 1 {
|
||||
stack_start
|
||||
} else {
|
||||
// choose the (size_in_pages-2)th element, since index
|
||||
// starts at 0 and we already allocated the start page
|
||||
range.nth(size_in_pages - 2)
|
||||
};
|
||||
|
||||
match (guard_page, stack_start, stack_end) {
|
||||
(Some(_), Some(start), Some(end)) => {
|
||||
// success! write back updated range
|
||||
self.range = range;
|
||||
|
||||
// map stack pages to physical frames
|
||||
for page in Page::range_inclusive(start, end) {
|
||||
active_table.map(page, paging::WRITABLE, frame_allocator);
|
||||
}
|
||||
|
||||
// create a new stack
|
||||
let top_of_stack = end.start_address() + PAGE_SIZE;
|
||||
Some(Stack::new(top_of_stack, start.start_address()))
|
||||
}
|
||||
_ => None, /* not enough pages */
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
The method takes mutable references to the [ActivePageTable] and a [FrameAllocator], since it needs to map the new virtual stack pages to physical frames. We define that the stack size is a multiple of the page size.
|
||||
|
||||
[ActivePageTable]: @/edition-1/posts/06-page-tables/index.md#page-table-ownership
|
||||
[FrameAllocator]: @/edition-1/posts/05-allocating-frames/index.md#a-frame-allocator
|
||||
|
||||
Instead of operating directly on `self.range`, we [clone] it and only write it back on success. This way, subsequent stack allocations can still succeed if there are pages left (e.g., a call with `size_in_pages = 3` can still succeed after a failed call with `size_in_pages = 100`).
|
||||
|
||||
In order to be able to clone `PageIter`, we add a `#[derive(Clone)]` to its definition in `src/memory/paging/mod.rs`. We also need to make the `start_address` method of the `Page` type public (in the same file).
|
||||
|
||||
[clone]: https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html#tymethod.clone
|
||||
|
||||
The actual allocation is straightforward: First, we choose the next page as [guard page]. Then we choose the next `size_in_pages` pages as stack pages using [Iterator::nth]. If all three variables are `Some`, the allocation succeeded and we map the stack pages to physical frames using [ActivePageTable::map]. The guard page remains unmapped.
|
||||
|
||||
[Iterator::nth]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.nth
|
||||
[ActivePageTable::map]: @/edition-1/posts/06-page-tables/index.md#more-mapping-functions
|
||||
|
||||
Finally, we create and return a new `Stack`, which we define as follows:
|
||||
|
||||
```rust
|
||||
// in src/memory/stack_allocator.rs
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Stack {
|
||||
top: usize,
|
||||
bottom: usize,
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
fn new(top: usize, bottom: usize) -> Stack {
|
||||
assert!(top > bottom);
|
||||
Stack {
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn top(&self) -> usize {
|
||||
self.top
|
||||
}
|
||||
|
||||
pub fn bottom(&self) -> usize {
|
||||
self.bottom
|
||||
}
|
||||
}
|
||||
```
|
||||
The `Stack` struct describes a stack though its top and bottom addresses.
|
||||
|
||||
#### The Memory Controller
|
||||
Now we're able to allocate a new double fault stack. However, we add one more level of abstraction to make things easier. For that we add a new `MemoryController` type to our `memory` module:
|
||||
|
||||
```rust
|
||||
// in src/memory/mod.rs
|
||||
|
||||
pub use self::stack_allocator::Stack;
|
||||
|
||||
pub struct MemoryController {
|
||||
active_table: paging::ActivePageTable,
|
||||
frame_allocator: AreaFrameAllocator,
|
||||
stack_allocator: stack_allocator::StackAllocator,
|
||||
}
|
||||
|
||||
impl MemoryController {
|
||||
pub fn alloc_stack(&mut self, size_in_pages: usize) -> Option<Stack> {
|
||||
let &mut MemoryController { ref mut active_table,
|
||||
ref mut frame_allocator,
|
||||
ref mut stack_allocator } = self;
|
||||
stack_allocator.alloc_stack(active_table, frame_allocator,
|
||||
size_in_pages)
|
||||
}
|
||||
}
|
||||
```
|
||||
The `MemoryController` struct holds the three types that are required for `alloc_stack` and provides a simpler interface (only one argument). The `alloc_stack` wrapper just takes the tree types as `&mut` through [destructuring] and forwards them to the `stack_allocator`. The [ref mut]-s are needed to take the inner fields by mutable reference. Note that we're re-exporting the `Stack` type since it is returned by `alloc_stack`.
|
||||
|
||||
[destructuring]: https://doc.rust-lang.org/1.10.0/book/patterns.html#destructuring
|
||||
[ref mut]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch18-03-pattern-syntax.html#creating-references-in-patterns-with-ref-and-ref-mut
|
||||
|
||||
The last step is to create a `StackAllocator` and return a `MemoryController` from `memory::init`:
|
||||
|
||||
```rust
|
||||
// in src/memory/mod.rs
|
||||
|
||||
pub fn init(boot_info: &BootInformation) -> MemoryController {
|
||||
...
|
||||
|
||||
let stack_allocator = {
|
||||
let stack_alloc_start = heap_end_page + 1;
|
||||
let stack_alloc_end = stack_alloc_start + 100;
|
||||
let stack_alloc_range = Page::range_inclusive(stack_alloc_start,
|
||||
stack_alloc_end);
|
||||
stack_allocator::StackAllocator::new(stack_alloc_range)
|
||||
};
|
||||
|
||||
MemoryController {
|
||||
active_table: active_table,
|
||||
frame_allocator: frame_allocator,
|
||||
stack_allocator: stack_allocator,
|
||||
}
|
||||
}
|
||||
```
|
||||
We create a new `StackAllocator` with a range of 100 pages starting right after the last heap page.
|
||||
|
||||
In order to do arithmetic on pages (e.g. calculate the hundredth page after `stack_alloc_start`), we implement `Add<usize>` for `Page`:
|
||||
|
||||
```rust
|
||||
// in src/memory/paging/mod.rs
|
||||
|
||||
use core::ops::Add;
|
||||
|
||||
impl Add<usize> for Page {
|
||||
type Output = Page;
|
||||
|
||||
fn add(self, rhs: usize) -> Page {
|
||||
Page { number: self.number + rhs }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Allocating a Double Fault Stack
|
||||
Now we can allocate a new double fault stack by passing the memory controller to our `interrupts::init` function:
|
||||
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
...
|
||||
|
||||
// set up guard page and map the heap pages
|
||||
let mut memory_controller = memory::init(boot_info); // new return type
|
||||
|
||||
// initialize our IDT
|
||||
interrupts::init(&mut memory_controller); // new argument
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
// in src/interrupts.rs
|
||||
|
||||
use memory::MemoryController;
|
||||
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
let double_fault_stack = memory_controller.alloc_stack(1)
|
||||
.expect("could not allocate double fault stack");
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
```
|
||||
|
||||
We allocate a 4096 bytes stack (one page) for our double fault handler. Now we just need some way to tell the CPU that it should use this stack for handling double faults.
|
||||
|
||||
### The IST and TSS
|
||||
The Interrupt Stack Table (IST) is part of an old legacy structure called _[Task State Segment]_ \(TSS). The TSS used to hold various information (e.g. processor register state) about a task in 32-bit mode and was for example used for [hardware context switching]. However, hardware context switching is no longer supported in 64-bit mode and the format of the TSS changed completely.
|
||||
|
||||
[Task State Segment]: https://en.wikipedia.org/wiki/Task_state_segment
|
||||
[hardware context switching]: https://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
|
||||
|
||||
On x86_64, the TSS no longer holds any task specific information at all. Instead, it holds two stack tables (the IST is one of them). The only common field between the 32-bit and 64-bit TSS is the pointer to the [I/O port permissions bitmap].
|
||||
|
||||
[I/O port permissions bitmap]: https://en.wikipedia.org/wiki/Task_state_segment#I.2FO_port_permissions
|
||||
|
||||
The 64-bit TSS has the following format:
|
||||
|
||||
Field | Type
|
||||
------ | ----------------
|
||||
<span style="opacity: 0.5">(reserved)</span> | `u32`
|
||||
Privilege Stack Table | `[u64; 3]`
|
||||
<span style="opacity: 0.5">(reserved)</span> | `u64`
|
||||
Interrupt Stack Table | `[u64; 7]`
|
||||
<span style="opacity: 0.5">(reserved)</span> | `u64`
|
||||
<span style="opacity: 0.5">(reserved)</span> | `u16`
|
||||
I/O Map Base Address | `u16`
|
||||
|
||||
The _Privilege Stack Table_ is used by the CPU when the privilege level changes. For example, if an exception occurs while the CPU is in user mode (privilege level 3), the CPU normally switches to kernel mode (privilege level 0) before invoking the exception handler. In that case, the CPU would switch to the 0th stack in the Privilege Stack Table (since 0 is the target privilege level). We don't have any user mode programs yet, so we ignore this table for now.
|
||||
|
||||
#### Creating a TSS
|
||||
Let's create a new TSS that contains our double fault stack in its interrupt stack table. For that we need a TSS struct. Fortunately, the `x86_64` crate already contains a [`TaskStateSegment` struct] that we can use:
|
||||
|
||||
[`TaskStateSegment` struct]: https://docs.rs/x86_64/0.1.1/x86_64/structures/tss/struct.TaskStateSegment.html
|
||||
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
```
|
||||
|
||||
Let's create a new TSS in our `interrupts::init` function:
|
||||
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
|
||||
use x86_64::VirtualAddress;
|
||||
|
||||
const DOUBLE_FAULT_IST_INDEX: usize = 0;
|
||||
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
let double_fault_stack = memory_controller.alloc_stack(1)
|
||||
.expect("could not allocate double fault stack");
|
||||
|
||||
let mut tss = TaskStateSegment::new();
|
||||
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress(
|
||||
double_fault_stack.top());
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
```
|
||||
|
||||
We define that the 0th IST entry is the double fault stack (any other IST index would work too). We create a new TSS through the `TaskStateSegment::new` function and load the top address (stacks grow downwards) of the double fault stack into the 0th entry.
|
||||
|
||||
#### Loading the TSS
|
||||
Now that we created a new TSS, we need a way to tell the CPU that it should use it. Unfortunately, this is a bit cumbersome, since the TSS is a Task State _Segment_ (for historical reasons). So instead of loading the table directly, we need to add a new segment descriptor to the [Global Descriptor Table] \(GDT). Then we can load our TSS invoking the [`ltr` instruction] with the respective GDT index.
|
||||
|
||||
[Global Descriptor Table]: https://web.archive.org/web/20190217233448/https://www.flingos.co.uk/docs/reference/Global-Descriptor-Table/
|
||||
[`ltr` instruction]: https://www.felixcloutier.com/x86/ltr
|
||||
|
||||
### The Global Descriptor Table (again)
|
||||
The Global Descriptor Table (GDT) is a relict that was used for [memory segmentation] before paging became the de facto standard. It is still needed in 64-bit mode for various things such as kernel/user mode configuration or TSS loading.
|
||||
|
||||
[memory segmentation]: https://en.wikipedia.org/wiki/X86_memory_segmentation
|
||||
|
||||
We already created a GDT [when switching to long mode]. Back then, we used assembly to create valid code and data segment descriptors, which were required to enter 64-bit mode. We could just edit that assembly file and add an additional TSS descriptor. However, we now have the expressiveness of Rust, so let's do it in Rust instead.
|
||||
|
||||
[when switching to long mode]: @/edition-1/posts/02-entering-longmode/index.md#the-global-descriptor-table
|
||||
|
||||
We start by creating a new `interrupts::gdt` submodule. For that we need to rename the `src/interrupts.rs` file to `src/interrupts/mod.rs`. Then we can create a new submodule:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
mod gdt;
|
||||
```
|
||||
|
||||
```rust
|
||||
// src/interrupts/gdt.rs
|
||||
|
||||
pub struct Gdt {
|
||||
table: [u64; 8],
|
||||
next_free: usize,
|
||||
}
|
||||
|
||||
impl Gdt {
|
||||
pub fn new() -> Gdt {
|
||||
Gdt {
|
||||
table: [0; 8],
|
||||
next_free: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
We create a simple `Gdt` struct with two fields. The `table` field contains the actual GDT modeled as a `[u64; 8]`. Theoretically, a GDT can have up to 8192 entries, but this doesn't make much sense in 64-bit mode (since there is no real segmentation support). Eight entries should be more than enough for our system.
|
||||
|
||||
The `next_free` field stores the index of the next free entry. We initialize it with `1` since the 0th entry needs always needs to be 0 in a valid GDT.
|
||||
|
||||
#### User and System Segments
|
||||
There are two types of GDT entries in long mode: user and system segment descriptors. Descriptors for code and data segment segments are user segment descriptors. They contain no addresses since segments always span the complete address space on x86_64 (real segmentation is no longer supported). Thus, user segment descriptors only contain a few flags (e.g. present or user mode) and fit into a single `u64` entry.
|
||||
|
||||
System descriptors such as TSS descriptors are different. They often contain a base address and a limit (e.g. TSS start and length) and thus need more than 64 bits. Therefore, system segments are 128 bits. They are stored as two consecutive entries in the GDT.
|
||||
|
||||
Consequently, we model a `Descriptor` as an `enum`:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/gdt.rs
|
||||
|
||||
pub enum Descriptor {
|
||||
UserSegment(u64),
|
||||
SystemSegment(u64, u64),
|
||||
}
|
||||
```
|
||||
|
||||
The flag bits are common between all descriptor types, so we create a general `DescriptorFlags` type (using the [bitflags] macro):
|
||||
|
||||
[bitflags]: https://docs.rs/bitflags/0.9.1/bitflags/macro.bitflags.html
|
||||
|
||||
```rust
|
||||
// in src/interrupts/gdt.rs
|
||||
|
||||
bitflags! {
|
||||
struct DescriptorFlags: u64 {
|
||||
const CONFORMING = 1 << 42;
|
||||
const EXECUTABLE = 1 << 43;
|
||||
const USER_SEGMENT = 1 << 44;
|
||||
const PRESENT = 1 << 47;
|
||||
const LONG_MODE = 1 << 53;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We only add flags that are relevant in 64-bit mode. For example, we omit the read/write bit, since it is completely ignored by the CPU in 64-bit mode.
|
||||
|
||||
#### Code Segments
|
||||
We add a function to create kernel mode code segments:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/gdt.rs
|
||||
|
||||
impl Descriptor {
|
||||
pub fn kernel_code_segment() -> Descriptor {
|
||||
let flags = USER_SEGMENT | PRESENT | EXECUTABLE | LONG_MODE;
|
||||
Descriptor::UserSegment(flags.bits())
|
||||
}
|
||||
}
|
||||
```
|
||||
We set the `USER_SEGMENT` bit to indicate a 64 bit user segment descriptor (otherwise the CPU expects a 128 bit system segment descriptor). The `PRESENT`, `EXECUTABLE`, and `LONG_MODE` bits are also needed for a 64-bit mode code segment.
|
||||
|
||||
The data segment registers `ds`, `ss`, and `es` are completely ignored in 64-bit mode, so we don't need any data segment descriptors in our GDT.
|
||||
|
||||
#### TSS Segments
|
||||
A TSS descriptor is a system segment descriptor with the following format:
|
||||
|
||||
Bit(s) | Name | Meaning
|
||||
--------------------- | ------ | ----------------------------------
|
||||
0-15 | **limit 0-15** | the first 2 byte of the TSS's limit
|
||||
16-39 | **base 0-23** | the first 3 byte of the TSS's base address
|
||||
40-43 | **type** | must be `0b1001` for an available 64-bit TSS
|
||||
44 | zero | must be 0
|
||||
45-46 | privilege | the [ring level]: 0 for kernel, 3 for user
|
||||
47 | **present** | must be 1 for valid selectors
|
||||
48-51 | limit 16-19 | bits 16 to 19 of the segment's limit
|
||||
52 | available | freely available to the OS
|
||||
53-54 | ignored |
|
||||
55 | granularity | if it's set, the limit is the number of pages, else it's a byte number
|
||||
56-63 | **base 24-31** | the fourth byte of the base address
|
||||
64-95 | **base 32-63** | the last four bytes of the base address
|
||||
96-127 | ignored/must be zero | bits 104-108 must be zero, the rest is ignored
|
||||
|
||||
[ring level]: https://wiki.osdev.org/Security#Rings
|
||||
|
||||
We only need the bold fields for our TSS descriptor. For example, we don't need the `limit 16-19` field since a TSS has a fixed size that is smaller than `2^16`.
|
||||
|
||||
Let's add a function to our descriptor that creates a TSS descriptor for a given TSS:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/gdt.rs
|
||||
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
|
||||
impl Descriptor {
|
||||
pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
|
||||
use core::mem::size_of;
|
||||
use bit_field::BitField;
|
||||
|
||||
let ptr = tss as *const _ as u64;
|
||||
|
||||
let mut low = PRESENT.bits();
|
||||
// base
|
||||
low.set_bits(16..40, ptr.get_bits(0..24));
|
||||
low.set_bits(56..64, ptr.get_bits(24..32));
|
||||
// limit (the `-1` in needed since the bound is inclusive)
|
||||
low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
|
||||
// type (0b1001 = available 64-bit tss)
|
||||
low.set_bits(40..44, 0b1001);
|
||||
|
||||
let mut high = 0;
|
||||
high.set_bits(0..32, ptr.get_bits(32..64));
|
||||
|
||||
Descriptor::SystemSegment(low, high)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `set_bits` and `get_bits` methods are provided by the [`BitField` trait] of the `bit_fields` crate. They allow us to easily get or set specific bits in an integer without using bit masks or shift operations. For example, we can do `x.set_bits(8..12, 42)` instead of `x = (x & 0xfffff0ff) | (42 << 8)`.
|
||||
|
||||
[`BitField` trait]: https://docs.rs/bit_field/0.6.0/bit_field/trait.BitField.html#method.get_bit
|
||||
|
||||
To link the `bit_fields` crate, we modify our `Cargo.toml` and our `src/lib.rs`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
bit_field = "0.7.0"
|
||||
```
|
||||
|
||||
```rust
|
||||
extern crate bit_field;
|
||||
```
|
||||
|
||||
We require the `'static` lifetime for the `TaskStateSegment` reference, since the hardware might access it on every interrupt as long as the OS runs.
|
||||
|
||||
|
||||
#### Adding Descriptors to the GDT
|
||||
In order to add descriptors to the GDT, we add a `add_entry` method:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/gdt.rs
|
||||
|
||||
use x86_64::structures::gdt::SegmentSelector;
|
||||
use x86_64::PrivilegeLevel;
|
||||
|
||||
impl Gdt {
|
||||
pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
|
||||
let index = match entry {
|
||||
Descriptor::UserSegment(value) => self.push(value),
|
||||
Descriptor::SystemSegment(value_low, value_high) => {
|
||||
let index = self.push(value_low);
|
||||
self.push(value_high);
|
||||
index
|
||||
}
|
||||
};
|
||||
SegmentSelector::new(index as u16, PrivilegeLevel::Ring0)
|
||||
}
|
||||
}
|
||||
```
|
||||
For an user segment we just push the `u64` and remember the index. For a system segment, we push the low and high `u64` and use the index of the low value. We then use this index to return a new [SegmentSelector].
|
||||
|
||||
[SegmentSelector]: https://docs.rs/x86/0.8.0/x86/shared/segmentation/struct.SegmentSelector.html#method.new
|
||||
|
||||
The `push` method looks like this:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/gdt.rs
|
||||
|
||||
impl Gdt {
|
||||
fn push(&mut self, value: u64) -> usize {
|
||||
if self.next_free < self.table.len() {
|
||||
let index = self.next_free;
|
||||
self.table[index] = value;
|
||||
self.next_free += 1;
|
||||
index
|
||||
} else {
|
||||
panic!("GDT full");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
The method just writes to the `next_free` entry and returns the corresponding index. If there is no free entry left, we panic since this likely indicates a programming error (we should never need to create more than two or three GDT entries for our kernel).
|
||||
|
||||
#### Loading the GDT
|
||||
To load the GDT, we add a new `load` method:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/gdt.rs
|
||||
|
||||
impl Gdt {
|
||||
pub fn load(&'static self) {
|
||||
use x86_64::instructions::tables::{DescriptorTablePointer, lgdt};
|
||||
use core::mem::size_of;
|
||||
|
||||
let ptr = DescriptorTablePointer {
|
||||
base: self.table.as_ptr() as u64,
|
||||
limit: (self.table.len() * size_of::<u64>() - 1) as u16,
|
||||
};
|
||||
|
||||
unsafe { lgdt(&ptr) };
|
||||
}
|
||||
}
|
||||
```
|
||||
We use the [`DescriptorTablePointer` struct] and the [`lgdt` function] provided by the `x86_64` crate to load our GDT. Again, we require a `'static` reference since the GDT possibly needs to live for the rest of the run time.
|
||||
|
||||
[`DescriptorTablePointer` struct]: https://docs.rs/x86_64/0.1.1/x86_64/instructions/tables/struct.DescriptorTablePointer.html
|
||||
[`lgdt` function]: https://docs.rs/x86_64/0.1.1/x86_64/instructions/tables/fn.lgdt.html
|
||||
|
||||
### Putting it together
|
||||
We now have a double fault stack and are able to create and load a TSS (which contains an IST). So let's put everything together to catch kernel stack overflows.
|
||||
|
||||
We already created a new TSS in our `interrupts::init` function. Now we can load this TSS by creating a new GDT:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
let double_fault_stack = memory_controller.alloc_stack(1)
|
||||
.expect("could not allocate double fault stack");
|
||||
|
||||
let mut tss = TaskStateSegment::new();
|
||||
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress(
|
||||
double_fault_stack.top());
|
||||
|
||||
let mut gdt = gdt::Gdt::new();
|
||||
let code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment());
|
||||
let tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
|
||||
gdt.load();
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
```
|
||||
|
||||
However, when we try to compile it, the following errors occur:
|
||||
|
||||
```
|
||||
error: `tss` does not live long enough
|
||||
--> src/interrupts/mod.rs:118:68
|
||||
|
|
||||
118 | let tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
|
||||
| does not live long enough ^^^
|
||||
...
|
||||
122 | }
|
||||
| - borrowed value only lives until here
|
||||
|
|
||||
= note: borrowed value must be valid for the static lifetime...
|
||||
|
||||
error: `gdt` does not live long enough
|
||||
--> src/interrupts/mod.rs:119:5
|
||||
|
|
||||
119 | gdt.load();
|
||||
| ^^^ does not live long enough
|
||||
...
|
||||
122 | }
|
||||
| - borrowed value only lives until here
|
||||
|
|
||||
= note: borrowed value must be valid for the static lifetime...
|
||||
```
|
||||
The problem is that we require that the TSS and GDT are valid for the rest of the run time (i.e. for the `'static` lifetime). But our created `tss` and `gdt` live on the stack and are thus destroyed at the end of the `init` function. So how do we fix this problem?
|
||||
|
||||
We could allocate our TSS and GDT on the heap using `Box` and use [into_raw] and a bit of `unsafe` to convert it to `&'static` references ([RFC 1233] was closed unfortunately).
|
||||
|
||||
Alternatively, we could store them in a `static` somehow. The [`lazy_static` macro] doesn't work here, since we need access to the `MemoryController` for initialization. However, we can use its fundamental building block, the [`spin::Once` type].
|
||||
|
||||
[into_raw]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.into_raw
|
||||
[RFC 1233]: https://github.com/rust-lang/rfcs/pull/1233
|
||||
[`lazy_static` macro]: https://docs.rs/lazy_static/0.2.2/lazy_static/
|
||||
[`spin::Once` type]: https://docs.rs/spin/0.4.5/spin/struct.Once.html
|
||||
|
||||
#### spin::Once
|
||||
Let's try to solve our problem using [`spin::Once`][`spin::Once` type]:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
use spin::Once;
|
||||
|
||||
static TSS: Once<TaskStateSegment> = Once::new();
|
||||
static GDT: Once<gdt::Gdt> = Once::new();
|
||||
```
|
||||
The `Once` type allows us to initialize a `static` at runtime. It is safe because the only way to access the static value is through the provided methods ([call_once][Once::call_once], [try][Once::try], and [wait][Once::wait]). Thus, no value can be read before initialization and the value can only be initialized once.
|
||||
|
||||
[Once::call_once]: https://docs.rs/spin/0.4.5/spin/struct.Once.html#method.call_once
|
||||
[Once::try]: https://docs.rs/spin/0.4.5/spin/struct.Once.html#method.try
|
||||
[Once::wait]: https://docs.rs/spin/0.4.5/spin/struct.Once.html#method.wait
|
||||
|
||||
(The `Once` was added in spin 0.4, so you're probably need to update your spin dependency.)
|
||||
|
||||
So let's rewrite our `interrupts::init` function to use the static `TSS` and `GDT`:
|
||||
|
||||
```rust
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
let double_fault_stack = memory_controller.alloc_stack(1)
|
||||
.expect("could not allocate double fault stack");
|
||||
|
||||
let tss = TSS.call_once(|| {
|
||||
let mut tss = TaskStateSegment::new();
|
||||
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress(
|
||||
double_fault_stack.top());
|
||||
tss
|
||||
});
|
||||
|
||||
let gdt = GDT.call_once(|| {
|
||||
let mut gdt = gdt::Gdt::new();
|
||||
let code_selector = gdt.add_entry(gdt::Descriptor::
|
||||
kernel_code_segment());
|
||||
let tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
|
||||
gdt
|
||||
});
|
||||
gdt.load();
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
```
|
||||
|
||||
Now it should compile again!
|
||||
|
||||
#### The final Steps
|
||||
We're almost done. We successfully loaded our new GDT, which contains a TSS descriptor. Now there are just a few steps left:
|
||||
|
||||
1. We changed our GDT, so we should reload the `cs`, the code segment register. This required since the old segment selector could point a different GDT descriptor now (e.g. a TSS descriptor).
|
||||
2. We loaded a GDT that contains a TSS selector, but we still need to tell the CPU that it should use that TSS.
|
||||
3. As soon as our TSS is loaded, the CPU has access to a valid interrupt stack table (IST). Then we can tell the CPU that it should use our new double fault stack by modifying our double fault IDT entry.
|
||||
|
||||
For the first two steps, we need access to the `code_selector` and `tss_selector` variables outside of the closure. We can achieve this by moving the `let` declarations out of the closure:
|
||||
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
use x86_64::structures::gdt::SegmentSelector;
|
||||
use x86_64::instructions::segmentation::set_cs;
|
||||
use x86_64::instructions::tables::load_tss;
|
||||
...
|
||||
|
||||
let mut code_selector = SegmentSelector(0);
|
||||
let mut tss_selector = SegmentSelector(0);
|
||||
let gdt = GDT.call_once(|| {
|
||||
let mut gdt = gdt::Gdt::new();
|
||||
code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment());
|
||||
tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
|
||||
gdt
|
||||
});
|
||||
gdt.load();
|
||||
|
||||
unsafe {
|
||||
// reload code segment register
|
||||
set_cs(code_selector);
|
||||
// load TSS
|
||||
load_tss(tss_selector);
|
||||
}
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
```
|
||||
|
||||
We first set the descriptors to `empty` and then update them from inside the closure (which implicitly borrows them as `&mut`). Now we're able to reload the code segment register using [`set_cs`] and to load the TSS using [`load_tss`].
|
||||
|
||||
[`set_cs`]: https://docs.rs/x86_64/0.1.2/x86_64/instructions/segmentation/fn.set_cs.html
|
||||
[`load_tss`]: https://docs.rs/x86_64/0.1.2/x86_64/instructions/tables/fn.load_tss.html
|
||||
|
||||
Now that we loaded a valid TSS and interrupt stack table, we can set the stack index for our double fault handler in the IDT:
|
||||
|
||||
```rust
|
||||
// in src/interrupt/mod.rs
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
...
|
||||
unsafe {
|
||||
idt.double_fault.set_handler_fn(double_fault_handler)
|
||||
.set_stack_index(DOUBLE_FAULT_IST_INDEX as u16);
|
||||
}
|
||||
...
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The `set_stack_index` method is unsafe because the the caller must ensure that the used index is valid and not already used for another exception.
|
||||
|
||||
That's it! Now the CPU should switch to the double fault stack whenever a double fault occurs. Thus, we are able to catch _all_ double faults, including kernel stack overflows:
|
||||
|
||||

|
||||
|
||||
From now on we should never see a triple fault again!
|
||||
|
||||
## What's next?
|
||||
Now that we mastered exceptions, it's time to explore another kind of interrupts: interrupts from external devices such as timers, keyboards, or network controllers. These hardware interrupts are very similar to exceptions, e.g. they are also dispatched through the IDT.
|
||||
|
||||
However, unlike exceptions, they don't arise directly on the CPU. Instead, an _interrupt controller_ aggregates these interrupts and forwards them to CPU depending on their priority. In the next posts we will explore the two interrupt controller variants on x86: the [Intel 8259] \(“PIC”) and the [APIC]. This will allow us to react to keyboard and mouse input.
|
||||
|
||||
[Intel 8259]: https://en.wikipedia.org/wiki/Intel_8259
|
||||
[APIC]: https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 17 KiB |
6
blog/content/edition-1/posts/_index.md
Normal file
@@ -0,0 +1,6 @@
|
||||
+++
|
||||
title = "Posts"
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "left"
|
||||
render = false
|
||||
+++
|
||||
5
blog/content/edition-2/_index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
+++
|
||||
title = "Second Edition"
|
||||
template = "redirect-to-frontpage.html"
|
||||
aliases = ["second-edition/index.html"]
|
||||
+++
|
||||
7
blog/content/edition-2/extra/_index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
+++
|
||||
title = "Extra Content"
|
||||
insert_anchor_links = "left"
|
||||
render = false
|
||||
sort_by = "weight"
|
||||
page_template = "edition-2/extra.html"
|
||||
+++
|
||||
|
After Width: | Height: | Size: 287 KiB |
115
blog/content/edition-2/extra/building-on-android/index.md
Normal file
@@ -0,0 +1,115 @@
|
||||
+++
|
||||
title = "Building on Android"
|
||||
weight = 3
|
||||
aliases = ["second-edition/extra/building-on-android/index.html"]
|
||||
+++
|
||||
|
||||
I finally managed to get `blog_os` building on my Android phone using [termux](https://termux.com/). This post explains the necessary steps to set it up.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
<div class = "warning">
|
||||
|
||||
This post is outdated and the instructions provided here might not work anymore.
|
||||
|
||||
</div>
|
||||
|
||||
<img src="building-on-android.png" alt="Screenshot of the compilation output from android" style="height: 50rem;" >
|
||||
|
||||
|
||||
### Install Termux and Nightly Rust
|
||||
|
||||
First, install [termux](https://termux.com/) from the [Google Play Store](https://play.google.com/store/apps/details?id=com.termux) or from F-Droid. After installing, open it and perform the following steps:
|
||||
|
||||
- Install fish shell, set as default shell, and launch it:
|
||||
```
|
||||
pkg install fish
|
||||
chsh -s fish
|
||||
fish
|
||||
```
|
||||
|
||||
This step is of course optional. However, if you continue with bash you will need to adjust some of the following commands to bash syntax.
|
||||
|
||||
- Install some basic tools:
|
||||
```
|
||||
pkg install wget tar
|
||||
```
|
||||
|
||||
- Add the [community repository by its-pointless](https://wiki.termux.com/wiki/Package_Management):
|
||||
```
|
||||
wget https://its-pointless.github.io/setup-pointless-repo.sh
|
||||
bash setup-pointless-repo.sh
|
||||
```
|
||||
|
||||
- Install cargo and a nightly version of rustc:
|
||||
```
|
||||
pkg install rustc cargo rustc-nightly
|
||||
```
|
||||
|
||||
- Prepend the nightly rustc path to your `PATH` in order to use nightly (fish syntax):
|
||||
```
|
||||
set -U fish_user_paths $PREFIX/opt/rust-nightly/bin/ $fish_user_paths
|
||||
```
|
||||
|
||||
Now `rustc --version` should work and output a nightly version number.
|
||||
|
||||
### Install Git and Clone blog_os
|
||||
|
||||
We need something to compile, so let's download the `blog_os` repository:
|
||||
|
||||
- Install git:
|
||||
```
|
||||
pkg install git
|
||||
```
|
||||
|
||||
- Clone the `blog_os` repository:
|
||||
```
|
||||
git clone https://github.com/phil-opp/blog_os.git
|
||||
```
|
||||
|
||||
If you want to clone/push via SSH, you need to install the `openssh` package: `pkg install openssh`.
|
||||
|
||||
### Install Xbuild and Bootimage
|
||||
|
||||
Now we're ready to install `cargo xbuild` and `bootimage`
|
||||
|
||||
- Run `cargo install`:
|
||||
```
|
||||
cargo install cargo-xbuild bootimage
|
||||
```
|
||||
|
||||
- Add the cargo bin directory to your `PATH` (fish syntax):
|
||||
```
|
||||
set -U fish_user_paths ~/.cargo/bin/ $fish_user_paths
|
||||
```
|
||||
|
||||
Now `cargo xbuild` and `bootimage` should be available. It does not work yet because `cargo xbuild` needs access to the rust source code. By default it tries to use rustup for this, but we have no rustup support so we need a different way.
|
||||
|
||||
### Providing the Rust Source Code
|
||||
|
||||
The Rust source code corresponding to our installed nightly is available in the [`its-pointless` repository](https://github.com/its-pointless/its-pointless.github.io):
|
||||
|
||||
- Download a tar containing the source code:
|
||||
```
|
||||
wget https://github.com/its-pointless/its-pointless.github.io/raw/master/rust-src-nightly.tar.xz
|
||||
```
|
||||
|
||||
- Extract it:
|
||||
```
|
||||
tar xf rust-src-nightly.tar.xz
|
||||
```
|
||||
|
||||
- Set the `XARGO_RUST_SRC` environment variable to tell cargo-xbuild the source path (fish syntax):
|
||||
```
|
||||
set -Ux XARGO_RUST_SRC ~/rust-src-nightly/rust-src/lib/rustlib/src/rust/src
|
||||
```
|
||||
|
||||
Now cargo-xbuild should no longer complain about a missing `rust-src` component. However it will throw an I/O error after building the sysroot. The problem is that the downloaded Rust source code has a different structure than the source provided by rustup. We can fix this by adding a symbolic link:
|
||||
|
||||
```
|
||||
ln -s ~/../usr/opt/rust-nightly/bin ~/../usr/opt/rust-nightly/lib/rustlib/aarch64-linux-android/bin
|
||||
```
|
||||
|
||||
Now `cargo xbuild --target x86_64-blog_os.json` and `bootimage build` should both work!
|
||||
|
||||
I couldn't get QEMU to run yet, so you won't be able to run your kernel. If you manage to get it working, please tell me :).
|
||||
@@ -0,0 +1,129 @@
|
||||
+++
|
||||
title = "A Freestanding Rust Binary"
|
||||
weight = 1
|
||||
path = "ar/freestanding-rust-binary"
|
||||
date = 2018-02-10
|
||||
|
||||
[extra]
|
||||
# Please update this when updating the translation
|
||||
translation_based_on_commit = "087a464ed77361cff6c459fb42fc655cb9eacbea"
|
||||
# GitHub usernames of the people that translated this post
|
||||
translators = ["ZAAFHachemrachid"]
|
||||
+++
|
||||
|
||||
تتمثل الخطوة الأولى في إنشاء نواة نظام التشغيل الخاصة بنا في إنشاء ملف Rust قابل للتنفيذ لا يربط المكتبة القياسية. هذا يجعل من الممكن تشغيل شيفرة Rust على [bare metal] دون نظام تشغيل أساسي.
|
||||
|
||||
[bare metal]: https://en.wikipedia.org/wiki/Bare_machine
|
||||
<!-- more -->
|
||||
|
||||
تم تطوير هذه المدونة بشكل مفتوح على [GitHub]. إذا كان لديك أي مشاكل أو أسئلة، يرجى فتح مشكلة هناك. يمكنك أيضًا ترك تعليقات [في الأسفل]. يمكن العثور على الشيفرة المصدرية الكاملة لهذا المنشور في فرع [post-01][post branch].
|
||||
|
||||
|
||||
[GitHub]: https://github.com/phil-opp/blog_os
|
||||
[at the bottom]: #comments
|
||||
<!-- fix for zola anchor checker (target is in template): <a id="comments"> -->
|
||||
[post branch]: https://github.com/phil-opp/blog_os/tree/post-01
|
||||
<!-- toc -->
|
||||
|
||||
|
||||
## مقدمة
|
||||
لكتابة نواة نظام تشغيل، نحتاج إلى شيفرة لا تعتمد على أي ميزات نظام تشغيل. هذا يعني أنه لا يمكننا استخدام سلاسل الرسائل(threads) أو الملفات(File System) أو Heap ram أو الشبكة أو الأرقام العشوائية أو الإخراج القياسي(I/O) أو أي ميزات أخرى تتطلب تجريدات نظام التشغيل أو أجهزة معينة. وهذا منطقي، لأننا نحاول كتابة نظام التشغيل الخاص بنا (OS) وبرامج التشغيل الخاصة بنا (drivers).
|
||||
|
||||
هذا يعني أنه لا يمكننا استخدام معظم [Rust standard library]، ولكن هناك الكثير من ميزات Rust التي _يمكننا استخدامها. على سبيل المثال، يمكننا استخدام [iterators] و [closures] و [pattern matching] و [option] و [اresult] و [string formatting] وبالطبع [ownership system]. هذه الميزات تجعل من الممكن كتابة نواة بطريقة معبرة جدًا وعالية المستوى دون القلق بشأن [undefined behavior] أو [memory safety].
|
||||
|
||||
|
||||
[option]: https://doc.rust-lang.org/core/option/
|
||||
[result]:https://doc.rust-lang.org/core/result/
|
||||
[Rust standard library]: https://doc.rust-lang.org/std/
|
||||
[iterators]: https://doc.rust-lang.org/book/ch13-02-iterators.html
|
||||
[closures]: https://doc.rust-lang.org/book/ch13-01-closures.html
|
||||
[pattern matching]: https://doc.rust-lang.org/book/ch06-00-enums.html
|
||||
[string formatting]: https://doc.rust-lang.org/core/macro.write.html
|
||||
[ownership system]: https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
|
||||
[undefined behavior]: https://www.nayuki.io/page/undefined-behavior-in-c-and-cplusplus-programs
|
||||
[memory safety]: https://tonyarcieri.com/it-s-time-for-a-memory-safety-intervention
|
||||
|
||||
|
||||
من أجل إنشاء نواة نظام تشغيل في Rust، نحتاج إلى إنشاء ملف قابل للتنفيذ يمكن تشغيله بدون نظام تشغيل أساسي. غالبًا ما يُطلق على هذا الملف القابل للتنفيذ اسم الملف القابل للتنفيذ ”القائم بذاته“ أو ”المعدني العاري“.
|
||||
|
||||
يصف هذا المنشور الخطوات اللازمة لإنشاء ثنائي Rust قائم بذاته ويشرح سبب الحاجة إلى هذه الخطوات. إذا كنت مهتمًا بمثال بسيط فقط، يمكنك **[الانتقال إلى الملخص] (#ملخص)**.
|
||||
|
||||
|
||||
|
||||
## تعطيل المكتبة القياسية
|
||||
بشكل افتراضي، تربط جميع صناديق Rust [standard library]، والتي تعتمد على نظام التشغيل لميزات (مثل threads, files, or networking). كما أنها تعتمد أيضًا على مكتبة C القياسية 'libc'، والتي تتفاعل بشكل وثيق مع خدمات نظام التشغيل. نظرًا لأن خطتنا هي كتابة نظام تشغيل، لا يمكننا استخدام أي مكتبات تعتمد على نظام التشغيل. لذا يجب علينا تعطيل التضمين التلقائي للمكتبة القياسية من خلال سمة [no_std].
|
||||
|
||||
|
||||
[standard library]: https://doc.rust-lang.org/std/
|
||||
[`no_std` attribute]: https://doc.rust-lang.org/1.30.0/book/first-edition/using-rust-without-the-standard-library.html
|
||||
|
||||
```
|
||||
cargo new blog_os --bin --edition 2018
|
||||
```
|
||||
|
||||
لقد أطلقتُ على المشروع اسم ”Blog_os“، ولكن بالطبع يمكنك اختيار اسمك الخاص. تُحدّد علامة ”bin“ أننا نريد إنشاء نسخة binary قابلة للتنفيذ (على عكس المكتبة) وتحدّد علامة ”--- Edition 2018“ أننا نريد استخدام [2018 edition] من Rust لصندوقنا. عندما نُشغّل الأمر، تُنشئ لنا الشحنة بنية الدليل التالية:
|
||||
|
||||
[2018 edition]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html
|
||||
|
||||
```
|
||||
blog_os
|
||||
├── Cargo.toml
|
||||
└── src
|
||||
└── main.rs
|
||||
```
|
||||
يحتوي ملف 'Cargo.toml' على تكوين الصندوق، على سبيل المثال اسم الصندوق، والمؤلف، ورقم [semantic version]، والتبعيات. يحتوي الملف 'src/main.rs' على الوحدة النمطية الجذرية للصندوق والدالة 'الرئيسية'. يمكنك تجميع قفصك من خلال 'cargo build' ثم تشغيل الملف الثنائي 'blog_os' المجمّع في المجلد الفرعي 'target/debug'.
|
||||
|
||||
[semantic version]: https://semver.org/
|
||||
|
||||
### السمة 'no_std'
|
||||
|
||||
يربط صندوقنا الآن المكتبة القياسية ضمنيًا بالمكتبة القياسية. دعونا نحاول تعطيل ذلك بإضافة سمة [no_std]:
|
||||
|
||||
|
||||
```rust
|
||||
// main.rs
|
||||
|
||||
#![no_std]
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
عندما نحاول بناءه الآن (عن طريق تشغيل ”cargo build“)، يحدث الخطأ التالي:
|
||||
|
||||
```
|
||||
error: cannot find macro `println!` in this scope
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
4 | println!("Hello, world!");
|
||||
| ^^^^^^^
|
||||
```
|
||||
|
||||
والسبب في هذا الخطأ هو أن [`println` macro] هو جزء من المكتبة القياسية، والتي لم نعد نضمّنها. لذا لم يعد بإمكاننا طباعة الأشياء. هذا أمر منطقي، لأن 'println' يكتب إلى [standard output]، وهو واصف ملف خاص يوفره نظام التشغيل.
|
||||
|
||||
|
||||
[`println` macro]: https://doc.rust-lang.org/std/macro.println.html
|
||||
[standard output]: https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29
|
||||
|
||||
لذا دعنا نحذف الطباعة ونحاول مرة أخرى بدالة رئيسية فارغة:
|
||||
|
||||
```rust
|
||||
// main.rs
|
||||
|
||||
#![no_std]
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
```
|
||||
> cargo build
|
||||
error: `#[panic_handler]` function required, but not found
|
||||
error: language item required, but not found: `eh_personality`
|
||||
```
|
||||
|
||||
|
||||
يفتقد بناء المترجمات البرمجية الآن إلى `#[panic_handler]` دالة و _language item_.
|
||||
|
||||
|
||||
|
||||