Compare commits
823 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c73fec46e6 | ||
|
|
b493e2bd5f | ||
|
|
78a7d6c5af | ||
|
|
a8b2e9a50c | ||
|
|
2d7c1e42d8 | ||
|
|
64ebec3ea4 | ||
|
|
252d32aafe | ||
|
|
2349370960 | ||
|
|
6ccc0f0538 | ||
|
|
9f12a47cfc | ||
|
|
ee880802c8 | ||
|
|
ab14e77b50 | ||
|
|
1d4245e582 | ||
|
|
8636a74ba4 | ||
|
|
3b766de306 | ||
|
|
1f300e1b75 | ||
|
|
56efe6035f | ||
|
|
73e363d159 | ||
|
|
3ac134bc16 | ||
|
|
c7bb1f9655 | ||
|
|
7847646b3e | ||
|
|
ec6e9f73bb | ||
|
|
bd62f033ab | ||
|
|
ba88ec25f6 | ||
|
|
b9bc41a772 | ||
|
|
e029458b09 | ||
|
|
b3ecf1776b | ||
|
|
5fe83588eb | ||
|
|
18b8eaabae | ||
|
|
e11695c2f0 | ||
|
|
91edbf71c8 | ||
|
|
5b9b22021b | ||
|
|
033d90b866 | ||
|
|
540c0df918 | ||
|
|
6de167467f | ||
|
|
c746326eb3 | ||
|
|
0fbd384a73 | ||
|
|
e8d4a98c7b | ||
|
|
f5aa63936c | ||
|
|
62a57d0f47 | ||
|
|
958fb4c739 | ||
|
|
e231275cbe | ||
|
|
5a404c3a35 | ||
|
|
b1c07c6204 | ||
|
|
60f158d488 | ||
|
|
1887c6fae3 | ||
|
|
725a825762 | ||
|
|
b81f59d5ae | ||
|
|
39c2b8927b | ||
|
|
89dfdac7bf | ||
|
|
c087edf2ba | ||
|
|
db33c315f2 | ||
|
|
c7e7dd8248 | ||
|
|
b10d82a883 | ||
|
|
e151b3d4ef | ||
|
|
5ccf4a45c1 | ||
|
|
c5369a5cad | ||
|
|
b5eba765a5 | ||
|
|
a5ddc38477 | ||
|
|
8d102256a2 | ||
|
|
2e561f7ea9 | ||
|
|
9656fe1b6f | ||
|
|
48c23f2a43 | ||
|
|
6d82649c3c | ||
|
|
81dea35754 | ||
|
|
fdbe43421b | ||
|
|
0d71560785 | ||
|
|
108e8a0cd4 | ||
|
|
653e81c48e | ||
|
|
0dc0783094 | ||
|
|
13ef1bf6be | ||
|
|
7ea80353f6 | ||
|
|
07bbff7d11 | ||
|
|
2ff4fc3cdd | ||
|
|
c9456055ea | ||
|
|
2900f2d67b | ||
|
|
93c8c90804 | ||
|
|
36e54642d2 | ||
|
|
e04cb28711 | ||
|
|
ad1dc51e06 | ||
|
|
5b6bab7a58 | ||
|
|
9bd2fc659d | ||
|
|
f18633f65f | ||
|
|
1026bc7b4f | ||
|
|
f1dee68142 | ||
|
|
f7cc2d295c | ||
|
|
a6f254b5cf | ||
|
|
00dbbe1b28 | ||
|
|
7f33309903 | ||
|
|
c95808fa2d | ||
|
|
5b6bd048a9 | ||
|
|
28e6ad3372 | ||
|
|
9b128ebba5 | ||
|
|
71f49180c3 | ||
|
|
4a042241a8 | ||
|
|
0eab9127e3 | ||
|
|
9f11991410 | ||
|
|
fec0b95098 | ||
|
|
5081439f53 | ||
|
|
c44aebee4d | ||
|
|
706cd50f25 | ||
|
|
b1b81c87f2 | ||
|
|
bf7e30539e | ||
|
|
5f8a61201b | ||
|
|
ed8aee8b8c | ||
|
|
92ec2af587 | ||
|
|
7960c87579 | ||
|
|
700d572732 | ||
|
|
014a0d86f1 | ||
|
|
88db59405f | ||
|
|
ef2e008700 | ||
|
|
07101c053a | ||
|
|
6c5f6ac475 | ||
|
|
61ecd9a617 | ||
|
|
6f56a9bfe9 | ||
|
|
416875ee46 | ||
|
|
c875b506ea | ||
|
|
ff4517e583 | ||
|
|
92cc989dc9 | ||
|
|
6e5987e3ca | ||
|
|
f03e232b90 | ||
|
|
1eb18c1790 | ||
|
|
3076d56973 | ||
|
|
b35707edf9 | ||
|
|
dfbd0dc9e7 | ||
|
|
620ad7ba71 | ||
|
|
5e4fd53243 | ||
|
|
0ba7630354 | ||
|
|
028d0ec0d5 | ||
|
|
751fa05fcd | ||
|
|
f89845257e | ||
|
|
2f274e2f47 | ||
|
|
38088e80e1 | ||
|
|
14162828ea | ||
|
|
ab8507e09d | ||
|
|
0d25c07333 | ||
|
|
bfc8ba8447 | ||
|
|
a45f8293d9 | ||
|
|
f29b9d14d7 | ||
|
|
9c9fd2deef | ||
|
|
3bb01ffd68 | ||
|
|
bc6d9994dc | ||
|
|
e177f9e0fe | ||
|
|
c5d75d2de9 | ||
|
|
cc0cade4d2 | ||
|
|
1e3362250f | ||
|
|
db16e8d602 | ||
|
|
2a563eddd0 | ||
|
|
6b8474b7b9 | ||
|
|
b5336c81cc | ||
|
|
1fcab825bf | ||
|
|
0306556032 | ||
|
|
b093578046 | ||
|
|
9332b9eb33 | ||
|
|
141c7817a0 | ||
|
|
f155026d8f | ||
|
|
37e467ba60 | ||
|
|
3572b835b5 | ||
|
|
da4f3a72af | ||
|
|
2938bbd11f | ||
|
|
abf3017eb2 | ||
|
|
5c855ea52f | ||
|
|
c83edea7ad | ||
|
|
6d3113c8c3 | ||
|
|
48c48c7bbc | ||
|
|
e4093ed988 | ||
|
|
c79af52720 | ||
|
|
61eeaca6d5 | ||
|
|
320a79ba72 | ||
|
|
2e1b943805 | ||
|
|
bc167dde2d | ||
|
|
7d9a8b4211 | ||
|
|
0b16d88756 | ||
|
|
796cb31ed7 | ||
|
|
d7fd2f26c1 | ||
|
|
5adb8749de | ||
|
|
3474faa4a2 | ||
|
|
acd9c04ff6 | ||
|
|
833c9406b0 | ||
|
|
2f1253e27f | ||
|
|
53b847e31c | ||
|
|
7efa33dd7f | ||
|
|
4011970a57 | ||
|
|
a2cfa2be16 | ||
|
|
c4b691d688 | ||
|
|
72a7a8eee5 | ||
|
|
52d352385f | ||
|
|
c1f85650bd | ||
|
|
b830909504 | ||
|
|
4ad6b1c220 | ||
|
|
429488a4ba | ||
|
|
40c6cc11a5 | ||
|
|
594881c6ce | ||
|
|
1cc64a0fbc | ||
|
|
f492318e3c | ||
|
|
4b06bb7795 | ||
|
|
f7e570383a | ||
|
|
896a088199 | ||
|
|
f3322bd003 | ||
|
|
cef947d67d | ||
|
|
b6640d118d | ||
|
|
273d0b2ce9 | ||
|
|
9b724d776e | ||
|
|
3eaa706ccf | ||
|
|
b4c854dfe8 | ||
|
|
9cdadbe834 | ||
|
|
b3f38a8764 | ||
|
|
5d868284c6 | ||
|
|
035751980d | ||
|
|
6b80bc08d5 | ||
|
|
2089959d1b | ||
|
|
67b00ec28d | ||
|
|
127ae6b945 | ||
|
|
4f53b3ce35 | ||
|
|
c3092b482a | ||
|
|
080219f88d | ||
|
|
3fd783cd01 | ||
|
|
211cd964d7 | ||
|
|
04f42f0be4 | ||
|
|
e00ef893d0 | ||
|
|
858efa604e | ||
|
|
b63b17c54f | ||
|
|
f0e111c20e | ||
|
|
7339f57138 | ||
|
|
1e39c999f9 | ||
|
|
62f7a06192 | ||
|
|
a0531b7930 | ||
|
|
0619d04939 | ||
|
|
cd76476d26 | ||
|
|
53b6c17e33 | ||
|
|
3b7b66b272 | ||
|
|
a795a45c34 | ||
|
|
b58cbd228c | ||
|
|
fc994285f9 | ||
|
|
d5e74b91c7 | ||
|
|
c5eb2e1070 | ||
|
|
d613cb057e | ||
|
|
121792bebe | ||
|
|
e07c230106 | ||
|
|
d5ce3044c8 | ||
|
|
98ff43b4d2 | ||
|
|
1704fbcd62 | ||
|
|
014ac1cf72 | ||
|
|
b499c7ee75 | ||
|
|
2e069e326c | ||
|
|
e47d42da22 | ||
|
|
615e5dfaa7 | ||
|
|
fde2c371ef | ||
|
|
a0b4e7fd5d | ||
|
|
45c36bbcae | ||
|
|
66c041ad93 | ||
|
|
32d5c52b5f | ||
|
|
9cf5b01633 | ||
|
|
d4cf22d370 | ||
|
|
973640f023 | ||
|
|
248ea82f76 | ||
|
|
a868079a93 | ||
|
|
d270d5b9ca | ||
|
|
e9692a438b | ||
|
|
06ea123dbd | ||
|
|
85141d5eda | ||
|
|
fa13b8fbd0 | ||
|
|
5b4712644a | ||
|
|
aa422914bd | ||
|
|
22c453e297 | ||
|
|
e5f40cec5a | ||
|
|
50b24bf6c2 | ||
|
|
b575ee9e28 | ||
|
|
37030621ac | ||
|
|
f005da4d12 | ||
|
|
6295559d9c | ||
|
|
f36ff303d9 | ||
|
|
1a62e91c01 | ||
|
|
7f314dfe11 | ||
|
|
0caf9f4214 | ||
|
|
1ad600c81a | ||
|
|
bd6c7b7fdb | ||
|
|
5f93724845 | ||
|
|
ad60eac6ef | ||
|
|
b3e3a2937a | ||
|
|
d010038228 | ||
|
|
c28eada93e | ||
|
|
9d84a92b1f | ||
|
|
7884be106d | ||
|
|
5fe7c7e7e1 | ||
|
|
f244b95aad | ||
|
|
3b5a5759b8 | ||
|
|
528e7ee33b | ||
|
|
c1f55735fc | ||
|
|
185ea86284 | ||
|
|
618262f015 | ||
|
|
c0f41ac10e | ||
|
|
3010699f20 | ||
|
|
fb274538e1 | ||
|
|
1a5d953a98 | ||
|
|
2dd1baebb3 | ||
|
|
d46beac22a | ||
|
|
6ed8741006 | ||
|
|
6845e42f2c | ||
|
|
5196869634 | ||
|
|
083806bfc9 | ||
|
|
11d29368ce | ||
|
|
100c3d9e36 | ||
|
|
01fbf8c3a5 | ||
|
|
a36556d7e4 | ||
|
|
4f11162fae | ||
|
|
2cbaf39fd5 | ||
|
|
2cc46c618b | ||
|
|
a0325e91f9 | ||
|
|
e11edd5dee | ||
|
|
5e8990f571 | ||
|
|
a3fd55036a | ||
|
|
7a3d15c48b | ||
|
|
a5b52a2470 | ||
|
|
a8cd14dafd | ||
|
|
2b249bd496 | ||
|
|
c8586cfe26 | ||
|
|
f228cb241f | ||
|
|
fd33ef8496 | ||
|
|
ba1cf6473a | ||
|
|
f418dcdb7b | ||
|
|
f91c26d752 | ||
|
|
f7156f2ff2 | ||
|
|
e444b587cf | ||
|
|
a7fcc6a5cf | ||
|
|
872b1ccbb6 | ||
|
|
b7b3400296 | ||
|
|
420ccdcb82 | ||
|
|
eaa4d6323e | ||
|
|
4e7ac41a6c | ||
|
|
92f3a736a7 | ||
|
|
5a30b2b298 | ||
|
|
85aaae67bf | ||
|
|
efe66bc72e | ||
|
|
d7bdf3fc19 | ||
|
|
c4e5f4410c | ||
|
|
f5039dca56 | ||
|
|
de6f9789cb | ||
|
|
4376a1093f | ||
|
|
a413ed01f3 | ||
|
|
05bb831321 | ||
|
|
6e0f1e5235 | ||
|
|
2f915401dd | ||
|
|
14152b3072 | ||
|
|
8f3cba37b3 | ||
|
|
0853f6f7ad | ||
|
|
ae7a7ba365 | ||
|
|
724af0fdc4 | ||
|
|
550ea2116e | ||
|
|
7952271d61 | ||
|
|
ffaa5790c2 | ||
|
|
934c4bed0b | ||
|
|
6f040caa98 | ||
|
|
50e5c29b1c | ||
|
|
e0b5539b91 | ||
|
|
050f58a3bb | ||
|
|
c739ed8e48 | ||
|
|
a09fc6ebcb | ||
|
|
4367ea6afa | ||
|
|
da0adeb39e | ||
|
|
63160a22a6 | ||
|
|
2bc1927ebd | ||
|
|
66e0cfc185 | ||
|
|
7bd0e4f5b4 | ||
|
|
7271850df2 | ||
|
|
8c79285774 | ||
|
|
56a77ceac1 | ||
|
|
3034420ac2 | ||
|
|
99a157649d | ||
|
|
e6645554f0 | ||
|
|
0f83d1ffaa | ||
|
|
2286eb21a7 | ||
|
|
64c81aa5f8 | ||
|
|
29dcfca608 | ||
|
|
f1cb9630ec | ||
|
|
ebbea57400 | ||
|
|
6016dc14f8 | ||
|
|
dc8e4e5f14 | ||
|
|
72c8212a0e | ||
|
|
2cd2a081d6 | ||
|
|
b44ec544f4 | ||
|
|
063eae7e95 | ||
|
|
2552572904 | ||
|
|
b74dda42a2 | ||
|
|
6e178e2069 | ||
|
|
277d723992 | ||
|
|
2f6afe9932 | ||
|
|
edcc617780 | ||
|
|
5d39242b83 | ||
|
|
ddbe5fd27b | ||
|
|
deb91d1d02 | ||
|
|
63073ea07f | ||
|
|
a331fcd873 | ||
|
|
99654bd5b2 | ||
|
|
f10427287f | ||
|
|
5b6318ee3a | ||
|
|
98cdbe0784 | ||
|
|
e4eb3ae38d | ||
|
|
6686e4add7 | ||
|
|
96a274bf7d | ||
|
|
add17a7994 | ||
|
|
26e404c6a2 | ||
|
|
144de8b328 | ||
|
|
c0226e6fd9 | ||
|
|
57d728a9be | ||
|
|
b2e9f5377b | ||
|
|
d3d315784c | ||
|
|
942a371b6a | ||
|
|
66ffc78f60 | ||
|
|
a9570cef14 | ||
|
|
16bc08f046 | ||
|
|
024335e5cd | ||
|
|
8f32048774 | ||
|
|
33d3dd256d | ||
|
|
fc151f7595 | ||
|
|
9152884815 | ||
|
|
2b4e97bec5 | ||
|
|
a63393504f | ||
|
|
97ae8d2d44 | ||
|
|
6c59728516 | ||
|
|
201fe05a06 | ||
|
|
a78889523f | ||
|
|
5cd1a207c9 | ||
|
|
423f0ca631 | ||
|
|
7612e38d41 | ||
|
|
bf234950cb | ||
|
|
bd992fca15 | ||
|
|
45fee57abd | ||
|
|
a2bca96d47 | ||
|
|
9862256ca7 | ||
|
|
6afc8b3b5c | ||
|
|
9b4b4ec543 | ||
|
|
d412711868 | ||
|
|
0b33551d94 | ||
|
|
9c1b585054 | ||
|
|
fc3a69d9ca | ||
|
|
cce4a6d07f | ||
|
|
b16539408b | ||
|
|
eba56d0f64 | ||
|
|
f2181b1d3e | ||
|
|
c93a97f8d7 | ||
|
|
8fd2525cd1 | ||
|
|
62a70cfdac | ||
|
|
ca0a79fc89 | ||
|
|
104b8e859f | ||
|
|
a99b26ff8c | ||
|
|
9bc62ec12d | ||
|
|
84a6bb772f | ||
|
|
263a5eda56 | ||
|
|
7fb17ce52f | ||
|
|
631fad24c0 | ||
|
|
f1d77fa62a | ||
|
|
3bc247b43b | ||
|
|
3608799dff | ||
|
|
c0f042bc2a | ||
|
|
8982ff259a | ||
|
|
726b06b748 | ||
|
|
0057ab9c5a | ||
|
|
048bf0ee4d | ||
|
|
4da0708b51 | ||
|
|
4c529e3145 | ||
|
|
8ceeca372f | ||
|
|
4326ac9be1 | ||
|
|
697c64abe4 | ||
|
|
7e8cf84c42 | ||
|
|
b6d91375a5 | ||
|
|
61305688fd | ||
|
|
492ae4139f | ||
|
|
a84f393bd5 | ||
|
|
5de54d6f9f | ||
|
|
8fc797120f | ||
|
|
5406d648bc | ||
|
|
a2003fba95 | ||
|
|
a76c15d018 | ||
|
|
ae8caf3d3a | ||
|
|
f221915a95 | ||
|
|
7f93733107 | ||
|
|
e8b9e49f57 | ||
|
|
cb9576b10e | ||
|
|
06fe06f9f1 | ||
|
|
abf7c8e565 | ||
|
|
46227e6763 | ||
|
|
2ffe4a5451 | ||
|
|
18bd4bbcf6 | ||
|
|
8209433696 | ||
|
|
ad9e00acaa | ||
|
|
c96f96258f | ||
|
|
82699f3e00 | ||
|
|
e2d828a4a2 | ||
|
|
92328c6a7e | ||
|
|
3f8ad17e86 | ||
|
|
52999db43a | ||
|
|
760aa99709 | ||
|
|
51c514de84 | ||
|
|
317899b4bf | ||
|
|
4c93d2b1f9 | ||
|
|
71c5404bfe | ||
|
|
9c5248b87a | ||
|
|
83a0293af4 | ||
|
|
84f3eddaf8 | ||
|
|
a5c6b908b6 | ||
|
|
94f0d873df | ||
|
|
d6f66d24fa | ||
|
|
31458e9938 | ||
|
|
dca612ffd7 | ||
|
|
5177308d48 | ||
|
|
59ab44a8c7 | ||
|
|
c711ed6253 | ||
|
|
58c6be17eb | ||
|
|
259bcc686c | ||
|
|
356268eae5 | ||
|
|
2dd64b8a92 | ||
|
|
b5cddedb7d | ||
|
|
7afdc2b3b5 | ||
|
|
60c4956290 | ||
|
|
4c6a2f7ab1 | ||
|
|
76cee0d95b | ||
|
|
7331a919e4 | ||
|
|
27ea6b96c4 | ||
|
|
33238cee54 | ||
|
|
384dd2ad81 | ||
|
|
4885e0b8d8 | ||
|
|
2a84911d3b | ||
|
|
7f672fb417 | ||
|
|
a13fdeecf6 | ||
|
|
74ac76ba84 | ||
|
|
253e031f83 | ||
|
|
ef1ecf859c | ||
|
|
a302ad3bea | ||
|
|
0951260df7 | ||
|
|
b87dadbb72 | ||
|
|
1dd6a9821a | ||
|
|
50a2424001 | ||
|
|
61e5a4444f | ||
|
|
de3eb6ccd7 | ||
|
|
4f8c66b2b7 | ||
|
|
4d9cbc4e19 | ||
|
|
40369d44df | ||
|
|
ee3720f0b7 | ||
|
|
e5dcca1c2b | ||
|
|
ef8438a214 | ||
|
|
dfd699c16b | ||
|
|
a381c2a811 | ||
|
|
e4c87a59d0 | ||
|
|
eef30ceb3c | ||
|
|
76411387ba | ||
|
|
8d5d8d2100 | ||
|
|
802bce12a7 | ||
|
|
a1024c288e | ||
|
|
a4531b01d5 | ||
|
|
d445b20e90 | ||
|
|
24ddf5ca3d | ||
|
|
bb117d3d74 | ||
|
|
c101653077 | ||
|
|
5248b96f8a | ||
|
|
7ab59e420a | ||
|
|
83a849126a | ||
|
|
c9c373f607 | ||
|
|
aa7c2c0009 | ||
|
|
57fc4df86f | ||
|
|
75acdc8a98 | ||
|
|
070f7fcc68 | ||
|
|
4d48238fb1 | ||
|
|
9b304404f7 | ||
|
|
8e5ced8e3c | ||
|
|
95ef379ac5 | ||
|
|
915f9ccb0d | ||
|
|
3709ac301a | ||
|
|
3bc966f84e | ||
|
|
457b4cec13 | ||
|
|
88891ef6cd | ||
|
|
25811e2b52 | ||
|
|
f81c92e35b | ||
|
|
39b2aff627 | ||
|
|
905777466d | ||
|
|
4e21cf4f1c | ||
|
|
eccf9eab93 | ||
|
|
c36ad54bcb | ||
|
|
81e2b7c11c | ||
|
|
18b5cdcddf | ||
|
|
fe31114779 | ||
|
|
df30a2a320 | ||
|
|
cf7fa8ecbd | ||
|
|
389a169443 | ||
|
|
e01802e299 | ||
|
|
709aefba5d | ||
|
|
e4ed713fce | ||
|
|
d3c507b0d4 | ||
|
|
a68e14def3 | ||
|
|
8175642a2a | ||
|
|
8d1befea9e | ||
|
|
e7b8a0ef39 | ||
|
|
be6ee894f9 | ||
|
|
e0e8ed2113 | ||
|
|
c63e46b1a9 | ||
|
|
eb9c74a1ed | ||
|
|
efd83e063e | ||
|
|
ae23a1c84d | ||
|
|
fd1253caac | ||
|
|
a0e8bc1de3 | ||
|
|
004f0652c9 | ||
|
|
d5190cd788 | ||
|
|
2412faf033 | ||
|
|
72caad86cc | ||
|
|
7df9917a00 | ||
|
|
575adcbb9d | ||
|
|
2d6debddb9 | ||
|
|
53d2aac2ae | ||
|
|
96211813e3 | ||
|
|
153bbd6242 | ||
|
|
730e716f0f | ||
|
|
2238293c0f | ||
|
|
29d8e637d8 | ||
|
|
136e0d8974 | ||
|
|
0eb87df84f | ||
|
|
a9db998d84 | ||
|
|
fcb8edffdf | ||
|
|
61684ecb68 | ||
|
|
0ced86b55f | ||
|
|
159c9cb475 | ||
|
|
2943dcb2e9 | ||
|
|
984c3e549c | ||
|
|
edfd63e1f7 | ||
|
|
e593584a47 | ||
|
|
ef60c8ea4c | ||
|
|
e478908dca | ||
|
|
ca587692eb | ||
|
|
f78dd5365c | ||
|
|
f9d6cdccb0 | ||
|
|
35fdd470b3 | ||
|
|
edff11be7b | ||
|
|
bb230de72c | ||
|
|
2d7dde9cf9 | ||
|
|
b77d619873 | ||
|
|
ae1033d975 | ||
|
|
99d28c4f79 | ||
|
|
2424865fa1 | ||
|
|
6a280723f8 | ||
|
|
f85bbfff58 | ||
|
|
d239d82379 | ||
|
|
9017de6082 | ||
|
|
477414a636 | ||
|
|
5fac49447c | ||
|
|
30b3a51309 | ||
|
|
bafba1197e | ||
|
|
2fab0be5e8 | ||
|
|
7ea2a0a278 | ||
|
|
a3ae38520c | ||
|
|
c36fa01651 | ||
|
|
9aeac7af92 | ||
|
|
859944dc8e | ||
|
|
a6dc3c8fc7 | ||
|
|
61c02ed079 | ||
|
|
afc6f51ff2 | ||
|
|
c32eefaa51 | ||
|
|
b6969cf0d1 | ||
|
|
58a33ee6e9 | ||
|
|
050f433046 | ||
|
|
d48f6b62ce | ||
|
|
ffee64c67f | ||
|
|
8cbc535755 | ||
|
|
ca07af2fe9 | ||
|
|
2fe5da97eb | ||
|
|
6ffaeadd1b | ||
|
|
2c10b6bff2 | ||
|
|
76acfd9934 | ||
|
|
06cc4f7172 | ||
|
|
158659e5b0 | ||
|
|
7e46046e3b | ||
|
|
808c4b7361 | ||
|
|
03dc4fc425 | ||
|
|
b4029dcd73 | ||
|
|
1750b256eb | ||
|
|
d00d82ac89 | ||
|
|
edb5a82a89 | ||
|
|
f7552f7c32 | ||
|
|
4483b65b25 | ||
|
|
d2f53626ae | ||
|
|
ac01fe417e | ||
|
|
2fd8c7aace | ||
|
|
b90b138766 | ||
|
|
c3ad7eda08 | ||
|
|
02c6a3c214 | ||
|
|
8dd9c1a1af | ||
|
|
be54052b6d | ||
|
|
9e87679ee2 | ||
|
|
17964f8cd1 | ||
|
|
a63fb971d4 | ||
|
|
4b500349e6 | ||
|
|
8e554e17c9 | ||
|
|
1d77206ae4 | ||
|
|
4340c326dc | ||
|
|
c6cb97ae30 | ||
|
|
9d68c980e6 | ||
|
|
e2596a0a61 | ||
|
|
17d3c6491a | ||
|
|
f55fc421ea | ||
|
|
169b12fc20 | ||
|
|
905f816bbe | ||
|
|
fef48a449a | ||
|
|
95d06c115a | ||
|
|
113059e57c | ||
|
|
408c70b90c | ||
|
|
5b51574ebf | ||
|
|
1e6d7a5aee | ||
|
|
a28b50cee8 | ||
|
|
df112b550b | ||
|
|
85615d1916 | ||
|
|
c6d3158b56 | ||
|
|
4a4de608ec | ||
|
|
e651c3d9cc | ||
|
|
9c12496b30 | ||
|
|
bfc2448662 | ||
|
|
faa04b0309 | ||
|
|
ca1ea7fd6e | ||
|
|
57dc1b09ad | ||
|
|
5a9d0e2569 | ||
|
|
f6d3abbc11 | ||
|
|
041ca8451e | ||
|
|
9ab3a7c451 | ||
|
|
2382e6d82c | ||
|
|
f0e1937eeb | ||
|
|
efd08ff731 | ||
|
|
f9a1f1a79b | ||
|
|
a736e4602a | ||
|
|
5202ba8826 | ||
|
|
0f1bc98bb7 | ||
|
|
d40722af53 | ||
|
|
c9781ab626 | ||
|
|
4cbf4e93e7 | ||
|
|
1f68c2da42 | ||
|
|
d786c292f3 | ||
|
|
ada3e09784 | ||
|
|
c928b3b68b | ||
|
|
ae9c29629c | ||
|
|
3fd4603553 | ||
|
|
c14526068a | ||
|
|
180b2a6f4d | ||
|
|
b926ab7dd2 | ||
|
|
19d8861d27 | ||
|
|
70e82e627b | ||
|
|
67e229e5a9 | ||
|
|
d0d6baaa85 | ||
|
|
fd4352a1d6 | ||
|
|
08808da741 | ||
|
|
a11124a3d6 | ||
|
|
d2d6e786c7 | ||
|
|
a76c943c01 | ||
|
|
3c64a3a234 | ||
|
|
06060c67b5 | ||
|
|
0f64fd73ec | ||
|
|
2323528975 | ||
|
|
22b38a0f88 | ||
|
|
034c0f8d7c | ||
|
|
b4cd67cb8c | ||
|
|
be83d77187 | ||
|
|
615cf81a9f | ||
|
|
c60ef7fe82 | ||
|
|
fea5a111a9 | ||
|
|
7eebfd4f6a | ||
|
|
939965b5a0 | ||
|
|
a0334120a6 | ||
|
|
308417a48e | ||
|
|
85533411c9 | ||
|
|
6d4a84a325 | ||
|
|
89350b1edd | ||
|
|
9e58e4ed7a | ||
|
|
f8f4ecea27 | ||
|
|
e9449f86bf | ||
|
|
4fabe1f704 | ||
|
|
ef3ba896d5 | ||
|
|
649b160292 | ||
|
|
697bc74535 | ||
|
|
0e9ff44746 | ||
|
|
49d49e637a | ||
|
|
b3f0d13aa4 | ||
|
|
bd1547710e | ||
|
|
7479bf8092 | ||
|
|
d4d281bb9c | ||
|
|
68bf134017 | ||
|
|
c7b90f739f | ||
|
|
160bc36ae8 | ||
|
|
464bf1680c | ||
|
|
c51eaba18c | ||
|
|
7dbded99a2 | ||
|
|
6351e5b8c3 | ||
|
|
5a0f2e4dfc | ||
|
|
6d06ea6975 | ||
|
|
4180261f1f | ||
|
|
e882dd7aaf | ||
|
|
9e851d2dfd | ||
|
|
21a3edb150 | ||
|
|
3aacbd17ce | ||
|
|
6f8a9421ef | ||
|
|
bb437f6f7b | ||
|
|
0001f301f8 | ||
|
|
f7bbc4bfec | ||
|
|
a127a38438 | ||
|
|
16225b97d7 | ||
|
|
0587c14162 | ||
|
|
a181bba9f2 | ||
|
|
993e564fbb | ||
|
|
956829864c | ||
|
|
24186a7148 | ||
|
|
3475cfe1e6 | ||
|
|
6b64a10362 | ||
|
|
5e99fb3aa0 | ||
|
|
060d8e9945 | ||
|
|
1026471842 | ||
|
|
645208ec82 | ||
|
|
f4c48a64ca | ||
|
|
c80ac985fe | ||
|
|
ac9bdc7665 | ||
|
|
217d75024d | ||
|
|
12e5b4adb9 | ||
|
|
945f80993b | ||
|
|
5f32779ceb | ||
|
|
e8ebb2f7fe | ||
|
|
034e63669a | ||
|
|
c75ed567fc | ||
|
|
1e2bf61dcf | ||
|
|
de4fea77a1 | ||
|
|
441c143b3e |
87
.github/ISSUE_TEMPLATE.md
vendored
Normal file
87
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<!--
|
||||
-- THIS IS NOT A SUPPORT FORUM! For support please go to:
|
||||
-- Luma3DS GBATemp thread: https://gbatemp.net/threads/luma3ds-noob-proof-3ds-custom-firmware.411110/
|
||||
-- Nintendo Hacking: https://discord.gg/MjzatM8y
|
||||
--
|
||||
-- Also check the Wiki (https://github.com/AuroraWright/Luma3DS/wiki) before making an issue.
|
||||
--
|
||||
-- For those with GBA/DSiWare/DS/AGB_FIRM/TWL_FIRM problems: https://3ds.guide/troubleshooting
|
||||
-- If you're using an emu/redNAND anything related to that must also be installed to sysNAND.
|
||||
-- Please make sure to read "Enable game patching" https://github.com/AuroraWright/Luma3DS/wiki/Options-and-usage before posting any issues about the "Enable game patching" option(s).
|
||||
--
|
||||
-- Please fill in the placeholders.
|
||||
--
|
||||
-->
|
||||
**System model:** [e.g. 2DS, New 3DS, Old 3DS]
|
||||
|
||||
**SysNAND version (+emu/redNAND version if applicable):** [e.g. 11.4.0-37U]
|
||||
|
||||
**Entrypoint (How/what you're using to boot Luma3DS):** [e.g. A9LH]
|
||||
|
||||
**Luma3DS version:** [e.g. 7.0.5 stable or if using nightly specify the commit like this https://github.com/AuroraWright/Luma3DS/commit/5fe83588eb4b8faafc7773b3739a3d3307160b04]
|
||||
<!--
|
||||
-- You can check which version you're on in the configuration menu. It will be on the top of the screen.
|
||||
-->
|
||||
|
||||
**Luma3DS configuration/options:**
|
||||
|
||||
Default EmuNAND: ( )
|
||||
|
||||
Screen brightness: ( )
|
||||
|
||||
Splash: ( )
|
||||
|
||||
PIN lock: ( )
|
||||
|
||||
New 3DS CPU: ( )
|
||||
<!--
|
||||
-- This option is only available for New 3DS/2DS.
|
||||
-->
|
||||
|
||||
|
||||
--
|
||||
|
||||
Autoboot SysNAND: ( )
|
||||
|
||||
Use SysNAND FIRM if booting with R: ( )
|
||||
|
||||
Enable loading external FIRMs and modules: ( )
|
||||
<!--
|
||||
-- Firmware (.bin) files are not required by Luma, or NTR CFW anymore.
|
||||
-- If you're having issues with this option enabled try deleting them from the luma folder on the root of the SD card and disabling this option.
|
||||
-->
|
||||
|
||||
Use custom path: ( )
|
||||
|
||||
Enable game patching: ( )
|
||||
|
||||
Show NAND or user string in System Settings: ( )
|
||||
|
||||
Show GBA boot screen in patched AGB_FIRM: ( )
|
||||
|
||||
Patch SVC/service/archive/ARM9 access: ( )
|
||||
|
||||
Set developer UNITINFO: ( )
|
||||
|
||||
Enable exception handlers: ( )
|
||||
|
||||
--
|
||||
|
||||
**Explanation of the issue:**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**Steps to reproduce:**
|
||||
|
||||
1.
|
||||
|
||||
2.
|
||||
|
||||
|
||||
**Dump file:**
|
||||
<!-- If the issue leads to a crash you can generate a crash dump by checking the "Enable exception handlers" option.
|
||||
-- The error message will tell you where the dump is.
|
||||
Zip the dmp file and drag & drop it below. -->
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,10 +1,13 @@
|
||||
out
|
||||
build
|
||||
loader/build
|
||||
injector/build
|
||||
exceptions/arm9/build
|
||||
exceptions/arm11/build
|
||||
haxloader/build
|
||||
*.bin
|
||||
*.3dsx
|
||||
*.smdh
|
||||
*.o
|
||||
*.d
|
||||
*.elf
|
||||
*.bat
|
||||
*.elf
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,6 +0,0 @@
|
||||
[submodule "CakeBrah"]
|
||||
path = CakeBrah
|
||||
url = https://github.com/mid-kid/CakeBrah
|
||||
[submodule "CakeHax"]
|
||||
path = CakeHax
|
||||
url = https://github.com/mid-kid/CakeHax
|
||||
1
CakeBrah
1
CakeBrah
Submodule CakeBrah deleted from 42ebe0d0bc
1
CakeHax
1
CakeHax
Submodule CakeHax deleted from 6b8fca0b37
142
Makefile
142
Makefile
@@ -1,102 +1,112 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
name := AuReiNand
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
name := Luma3DS
|
||||
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i')
|
||||
commit := $(shell git rev-parse --short=8 HEAD)
|
||||
|
||||
dir_source := source
|
||||
dir_patches := patches
|
||||
dir_loader := loader
|
||||
dir_mset := CakeHax
|
||||
dir_ninjhax := CakeBrah
|
||||
dir_injector := injector
|
||||
dir_exceptions := exceptions
|
||||
dir_arm9_exceptions := $(dir_exceptions)/arm9
|
||||
dir_arm11_exceptions := $(dir_exceptions)/arm11
|
||||
dir_build := build
|
||||
dir_out := out
|
||||
|
||||
ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -ffast-math
|
||||
FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) APP_DESCRIPTION="Noob-friendly 3DS CFW." APP_AUTHOR="Reisyukaku/Aurora Wright" --no-print-directory
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostartfiles
|
||||
|
||||
objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
bundled = $(dir_build)/reboot.bin.o $(dir_build)/emunand.bin.o $(dir_build)/svcGetCFWInfo.bin.o $(dir_build)/k11modules.bin.o \
|
||||
$(dir_build)/injector.bin.o $(dir_build)/loader.bin.o $(dir_build)/arm9_exceptions.bin.o $(dir_build)/arm11_exceptions.bin.o
|
||||
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
endef
|
||||
|
||||
.PHONY: all
|
||||
all: launcher a9lh ninjhax
|
||||
|
||||
.PHONY: launcher
|
||||
launcher: $(dir_out)/$(name).dat
|
||||
|
||||
.PHONY: a9lh
|
||||
a9lh: $(dir_out)/arm9loaderhax.bin
|
||||
|
||||
.PHONY: ninjhax
|
||||
ninjhax: $(dir_out)/3ds/$(name)
|
||||
all: firm
|
||||
|
||||
.PHONY: release
|
||||
release: $(dir_out)/$(name).zip
|
||||
release: $(dir_out)/$(name)$(revision).7z
|
||||
|
||||
.PHONY: firm
|
||||
firm: $(dir_out)/boot.firm
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@$(MAKE) $(FLAGS) -C $(dir_mset) clean
|
||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
||||
@rm -rf $(dir_out) $(dir_build)
|
||||
@$(MAKE) -C $(dir_loader) clean
|
||||
@$(MAKE) -C $(dir_arm9_exceptions) clean
|
||||
@$(MAKE) -C $(dir_arm11_exceptions) clean
|
||||
@$(MAKE) -C $(dir_injector) clean
|
||||
@rm -rf $(dir_out) $(dir_build)
|
||||
|
||||
$(dir_out):
|
||||
@mkdir -p "$(dir_out)/aurei/payloads"
|
||||
.PRECIOUS: $(dir_build)/%.bin
|
||||
|
||||
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
|
||||
@$(MAKE) $(FLAGS) -C $(dir_mset) launcher
|
||||
@dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144
|
||||
.PHONY: $(dir_loader)
|
||||
.PHONY: $(dir_arm9_exceptions)
|
||||
.PHONY: $(dir_arm11_exceptions)
|
||||
.PHONY: $(dir_injector)
|
||||
|
||||
$(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out)
|
||||
@cp -av $(dir_build)/main.bin $@
|
||||
$(dir_out)/$(name)$(revision).7z: all
|
||||
@mkdir -p "$(@D)"
|
||||
@7z a -mx $@ ./$(@D)/* ./$(dir_exceptions)/exception_dump_parser.py
|
||||
|
||||
$(dir_out)/3ds/$(name): $(dir_out)
|
||||
@mkdir -p "$(dir_out)/3ds/$(name)"
|
||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax)
|
||||
@mv $(dir_out)/$(name).3dsx $@
|
||||
@mv $(dir_out)/$(name).smdh $@
|
||||
$(dir_out)/boot.firm: $(dir_build)/main.elf
|
||||
@mkdir -p "$(@D)"
|
||||
@firmtool build $@ -e 0 -D $^ -C NDMA
|
||||
|
||||
$(dir_out)/$(name).zip: launcher a9lh ninjhax
|
||||
@cd $(dir_out) && zip -9 -r $(name) *
|
||||
$(dir_build)/main.elf: $(bundled) $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/patches.h: $(dir_patches)/emunand.s $(dir_patches)/reboot.s
|
||||
@mkdir -p "$(dir_build)"
|
||||
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||
@$(bin2o)
|
||||
|
||||
$(dir_build)/injector.bin: $(dir_injector)
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/loader.bin: $(dir_loader)
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/arm9_exceptions.bin: $(dir_arm9_exceptions)
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/arm11_exceptions.bin: $(dir_arm11_exceptions)
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/%.bin: $(dir_patches)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
@armips $(word 2,$^)
|
||||
@mv emunand.bin reboot.bin $(dir_build)
|
||||
@bin2c -o $@ -n emunand $(dir_build)/emunand.bin -n reboot $(dir_build)/reboot.bin
|
||||
|
||||
$(dir_build)/loader.h: $(dir_loader)/Makefile
|
||||
@$(MAKE) -C $(dir_loader)
|
||||
@mv $(dir_loader)/loader.bin $(dir_build)
|
||||
@bin2c -o $@ -n loader $(dir_build)/loader.bin
|
||||
$(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
|
||||
$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\""
|
||||
$(dir_build)/patches.o: CFLAGS += -DREVISION=\"$(revision)\" -DCOMMIT_HASH="0x$(commit)"
|
||||
|
||||
$(dir_build)/main.bin: $(dir_build)/main.elf
|
||||
$(OC) -S -O binary $< $@
|
||||
$(dir_build)/bundled.h: $(bundled)
|
||||
@$(foreach f, $(bundled),\
|
||||
echo "extern const u8" `(echo $(basename $(notdir $(f))) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $@;\
|
||||
echo "extern const u32" `(echo $(basename $(notdir $(f)))| sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $@;\
|
||||
)
|
||||
|
||||
$(dir_build)/main.elf: $(objects_cfw)
|
||||
# FatFs requires libgcc for __aeabi_uidiv
|
||||
$(CC) -nostartfiles $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/patches.h $(dir_build)/loader.h
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/bundled.h
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
42
README.md
42
README.md
@@ -1,32 +1,38 @@
|
||||
# AuReiNand
|
||||
*A modification of the ReiNand custom firmware*
|
||||
# Luma3DS
|
||||
*Noob-proof (N)3DS "Custom Firmware"*
|
||||
|
||||
**Compiling:**
|
||||
## What it is
|
||||
|
||||
You'll need armips and [bin2c](https://sourceforge.net/projects/bin2c/) added to your Path. [HERE](http://www91.zippyshare.com/v/ePGpjk9r/file.html) is a pre-compiled version of armips.
|
||||
**Luma3DS** is a program to patch the system software of (New) Nintendo 3DS handheld consoles "on the fly", adding features (such as per-game language settings and debugging capabilities for developers) and removing restrictions enforced by Nintendo (such as the region lock).
|
||||
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
|
||||
To use it, you will need a console capable of running homebrew software on the ARM9 processor. We recommend [Plailect's guide](https://3ds.guide/) for details on how to get your system ready.
|
||||
|
||||
Lastly, just run Make and everything should work!
|
||||
---
|
||||
|
||||
Copy everything in 'out' folder to SD root and run!
|
||||
## Compiling
|
||||
|
||||
**Usage / Features:**
|
||||
First you need to clone the repository recursively with: `git clone --recursive https://github.com/AuroraWright/Luma3DS.git`
|
||||
To compile, you'll need [armips](https://github.com/Kingcom/armips) and a build of a recent commit of [makerom](https://github.com/profi200/Project_CTR) added to your PATH.
|
||||
For now, you'll also need to update your [libctru](https://github.com/smealum/ctrulib) install, building from the latest commit.
|
||||
For your convenience, here are [Windows](http://www91.zippyshare.com/v/ePGpjk9r/file.html) and [Linux](https://mega.nz/#!uQ1T1IAD!Q91O0e12LXKiaXh_YjXD3D5m8_W3FuMI-hEa6KVMRDQ) builds of armips (thanks to who compiled them!).
|
||||
Finally just run `make` and everything should work!
|
||||
You can find the compiled files in the `out` folder.
|
||||
|
||||
See https://github.com/Reisyukaku/ReiNand and http://gbatemp.net/threads/reinand-mod-o3ds-n3ds-sysnand.411110
|
||||
---
|
||||
|
||||
The FIRMs you need are [HERE](http://www99.zippyshare.com/v/kEIiQl0x/file.html).
|
||||
## Setup / Usage / Features
|
||||
|
||||
**Credits:**
|
||||
|
||||
Rei as this is mostly his code.
|
||||
See https://github.com/AuroraWright/Luma3DS/wiki
|
||||
|
||||
The offset to detect the console, and to calculate the O3DS NAND CTR are from Decrypt9.
|
||||
---
|
||||
|
||||
tiniVi suggested me a way to detect a A9LH environment, and figured out screen deinit.
|
||||
## Credits
|
||||
|
||||
Delebile provided me with the FIRM writes blocking patch.
|
||||
See https://github.com/AuroraWright/Luma3DS/wiki/Credits
|
||||
|
||||
A skilled reverser gave me the new reboot patch.
|
||||
---
|
||||
|
||||
The screen init code is from dark_samus, bil1s, Normmatt, delebile and everyone who contributed.
|
||||
## Licensing
|
||||
|
||||
The code for printing to the screen is from CakesFW.
|
||||
This software is licensed under the terms of the GPLv3.
|
||||
You can find a copy of the license in the LICENSE.txt file.
|
||||
|
||||
42
exceptions/arm11/Makefile
Normal file
42
exceptions/arm11/Makefile
Normal file
@@ -0,0 +1,42 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
name := arm11_exceptions
|
||||
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
dir_out := ../../$(dir_build)
|
||||
|
||||
ASFLAGS := -mcpu=mpcore -mfpu=vfp
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
.PHONY: all
|
||||
all: $(dir_out)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
14
exceptions/arm11/linker.ld
Normal file
14
exceptions/arm11/linker.ld
Normal file
@@ -0,0 +1,14 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0;
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
48
exceptions/arm11/source/handlers.h
Normal file
48
exceptions/arm11/source/handlers.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 magic[2];
|
||||
u16 versionMinor, versionMajor;
|
||||
|
||||
u16 processor, core;
|
||||
u32 type;
|
||||
|
||||
u32 totalSize;
|
||||
u32 registerDumpSize;
|
||||
u32 codeDumpSize;
|
||||
u32 stackDumpSize;
|
||||
u32 additionalDataSize;
|
||||
} ExceptionDumpHeader;
|
||||
|
||||
void __attribute__((noreturn)) mcuReboot(void);
|
||||
void cleanInvalidateDCacheAndDMB(void);
|
||||
bool cannotAccessVA(const void *address);
|
||||
void FIQHandler(void);
|
||||
void undefinedInstructionHandler(void);
|
||||
void dataAbortHandler(void);
|
||||
void prefetchAbortHandler(void);
|
||||
151
exceptions/arm11/source/handlers.s
Normal file
151
exceptions/arm11/source/handlers.s
Normal file
@@ -0,0 +1,151 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
.macro GEN_HANDLER name
|
||||
.global \name
|
||||
.type \name, %function
|
||||
\name:
|
||||
ldr sp, =#0xffff3000
|
||||
stmfd sp!, {r0-r7}
|
||||
mov r1, #\@ @ macro expansion counter
|
||||
b _commonHandler
|
||||
|
||||
.size \name, . - \name
|
||||
.endm
|
||||
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global _commonHandler
|
||||
.type _commonHandler, %function
|
||||
_commonHandler:
|
||||
clrex
|
||||
cpsid aif
|
||||
mrs r2, spsr
|
||||
mov r6, sp
|
||||
mrs r3, cpsr
|
||||
|
||||
tst r2, #0x20
|
||||
bne noFPUInitNorSvcBreak
|
||||
sub r0, lr, #4
|
||||
stmfd sp!, {lr}
|
||||
bl cannotAccessVA
|
||||
ldmfd sp!, {lr}
|
||||
cmp r0, #0
|
||||
bne noFPUInitNorSvcBreak
|
||||
ldr r4, [lr, #-4]
|
||||
cmp r1, #1
|
||||
bne noFPUInit
|
||||
|
||||
lsl r4, #4
|
||||
sub r4, #0xc0000000
|
||||
cmp r4, #0x30000000
|
||||
bcs noFPUInitNorSvcBreak
|
||||
fmrx r0, fpexc
|
||||
tst r0, #0x40000000
|
||||
bne noFPUInitNorSvcBreak
|
||||
|
||||
sub lr, #4
|
||||
srsfd sp!, #0x13
|
||||
ldmfd sp!, {r0-r7} @ restore context
|
||||
cps #0x13 @ FPU init
|
||||
stmfd sp, {r0-r3, r11-lr}^
|
||||
sub sp, #0x20
|
||||
bl . @ will be replaced
|
||||
ldmfd sp, {r0-r3, r11-lr}^
|
||||
add sp, #0x20
|
||||
rfefd sp!
|
||||
|
||||
noFPUInit:
|
||||
cmp r1, #2
|
||||
bne noFPUInitNorSvcBreak
|
||||
ldr r5, =#0xe12fff7f
|
||||
cmp r4, r5
|
||||
bne noFPUInitNorSvcBreak
|
||||
cps #0x13 @ switch to supervisor mode
|
||||
cmp r10, #0
|
||||
addne sp, #0x28
|
||||
ldmfd sp, {r8-r11}^ @ implementation details of the official svc handler
|
||||
ldr r2, [sp, #0x1c]
|
||||
ldr r4, [sp, #0x18]
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
tst r2, #0x20
|
||||
addne lr, r4, #2 @ adjust address for later
|
||||
moveq lr, r4
|
||||
|
||||
noFPUInitNorSvcBreak:
|
||||
ands r4, r2, #0xf @ get the mode that triggered the exception
|
||||
moveq r4, #0xf @ usr => sys
|
||||
bic r5, r3, #0xf
|
||||
orr r5, r4
|
||||
msr cpsr_c, r5 @ change processor mode
|
||||
stmfd r6!, {r8-lr}
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
mov sp, r6
|
||||
|
||||
stmfd sp!, {r2,lr}
|
||||
|
||||
mrc p15,0,r4,c5,c0,0 @ dfsr
|
||||
mrc p15,0,r5,c5,c0,1 @ ifsr
|
||||
mrc p15,0,r6,c6,c0,0 @ far
|
||||
fmrx r7, fpexc
|
||||
fmrx r8, fpinst
|
||||
fmrx r9, fpinst2
|
||||
|
||||
stmfd sp!, {r4-r9} @ it's a bit of a mess, but we will fix that later
|
||||
@ order of saved regs now: dfsr, ifsr, far, fpexc, fpinst, fpinst2, cpsr, pc + (2/4/8), r8-r14, r0-r7
|
||||
|
||||
bic r3, #(1<<31)
|
||||
fmxr fpexc, r3 @ clear the VFP11 exception flag (if it's set)
|
||||
|
||||
mov r0, sp
|
||||
mrc p15,0,r2,c0,c0,5 @ CPU ID register
|
||||
|
||||
b mainHandler
|
||||
|
||||
GEN_HANDLER FIQHandler
|
||||
GEN_HANDLER undefinedInstructionHandler
|
||||
GEN_HANDLER prefetchAbortHandler
|
||||
GEN_HANDLER dataAbortHandler
|
||||
|
||||
.global mcuReboot
|
||||
.type mcuReboot, %function
|
||||
mcuReboot:
|
||||
b . @ will be replaced
|
||||
|
||||
.global cleanInvalidateDCacheAndDMB
|
||||
.type cleanInvalidateDCacheAndDMB, %function
|
||||
cleanInvalidateDCacheAndDMB:
|
||||
mov r0, #0
|
||||
mcr p15,0,r0,c7,c14,0 @ Clean and Invalidate Entire Data Cache
|
||||
mcr p15,0,r0,c7,c10,4 @ Drain Memory Barrier
|
||||
bx lr
|
||||
|
||||
.global cannotAccessVA
|
||||
.type cannotAccessVA, %function
|
||||
cannotAccessVA:
|
||||
@ Thanks yellows8 for the hint
|
||||
lsr r0, #12
|
||||
lsl r0, #12
|
||||
mcr p15,0,r0,c7,c8,0 @ VA to PA translation with privileged read permission check
|
||||
mrc p15,0,r0,c7,c4,0 @ read PA register
|
||||
and r0, #1 @ failure bit
|
||||
bx lr
|
||||
109
exceptions/arm11/source/mainHandler.c
Normal file
109
exceptions/arm11/source/mainHandler.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#include "handlers.h"
|
||||
|
||||
#define REG_DUMP_SIZE 4 * 23
|
||||
#define CODE_DUMP_SIZE 48
|
||||
|
||||
#define CODESET_OFFSET 0xBEEFBEEF
|
||||
|
||||
static u32 __attribute__((noinline)) copyMemory(void *dst, const void *src, u32 size, u32 alignment)
|
||||
{
|
||||
u8 *out = (u8 *)dst;
|
||||
const u8 *in = (const u8 *)src;
|
||||
|
||||
if(((u32)src & (alignment - 1)) != 0 || cannotAccessVA(src) || (size != 0 && cannotAccessVA((u8 *)src + size - 1)))
|
||||
return 0;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
*out++ = *in++;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
|
||||
{
|
||||
ExceptionDumpHeader dumpHeader;
|
||||
|
||||
u32 registerDump[REG_DUMP_SIZE / 4];
|
||||
u8 codeDump[CODE_DUMP_SIZE];
|
||||
u8 *finalBuffer = cannotAccessVA((void *)0xE5000000) ? (u8 *)0xF5000000 : (u8 *)0xE5000000; //VA for 0x25000000
|
||||
u8 *final = finalBuffer;
|
||||
|
||||
while(*(vu32 *)final == 0xDEADC0DE && *((vu32 *)final + 1) == 0xDEADCAFE);
|
||||
|
||||
dumpHeader.magic[0] = 0xDEADC0DE;
|
||||
dumpHeader.magic[1] = 0xDEADCAFE;
|
||||
dumpHeader.versionMajor = 1;
|
||||
dumpHeader.versionMinor = 2;
|
||||
|
||||
dumpHeader.processor = 11;
|
||||
dumpHeader.core = cpuId & 0xF;
|
||||
dumpHeader.type = type;
|
||||
|
||||
dumpHeader.registerDumpSize = REG_DUMP_SIZE;
|
||||
dumpHeader.codeDumpSize = CODE_DUMP_SIZE;
|
||||
|
||||
//Dump registers
|
||||
//Current order of saved regs: dfsr, ifsr, far, fpexc, fpinst, fpinst2, cpsr, pc, r8-r12, sp, lr, r0-r7
|
||||
u32 cpsr = regs[6];
|
||||
u32 pc = regs[7] - (type < 3 ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 0);
|
||||
|
||||
registerDump[15] = pc;
|
||||
registerDump[16] = cpsr;
|
||||
for(u32 i = 0; i < 6; i++) registerDump[17 + i] = regs[i];
|
||||
for(u32 i = 0; i < 7; i++) registerDump[8 + i] = regs[8 + i];
|
||||
for(u32 i = 0; i < 8; i++) registerDump[i] = regs[15 + i];
|
||||
|
||||
//Dump code
|
||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||
dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4);
|
||||
|
||||
//Copy register dump and code dump
|
||||
final = (u8 *)(finalBuffer + sizeof(ExceptionDumpHeader));
|
||||
final += copyMemory(final, registerDump, dumpHeader.registerDumpSize, 1);
|
||||
final += copyMemory(final, codeDump, dumpHeader.codeDumpSize, 1);
|
||||
|
||||
//Dump stack in place
|
||||
dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1);
|
||||
final += dumpHeader.stackDumpSize;
|
||||
|
||||
if(!cannotAccessVA((void *)0xFFFF9004))
|
||||
{
|
||||
vu64 *additionalData = (vu64 *)final;
|
||||
dumpHeader.additionalDataSize = 16;
|
||||
vu8 *currentKCodeSet = *(vu8 **)(*(vu8 **)0xFFFF9004 + CODESET_OFFSET); //currentKProcess + CodeSet
|
||||
|
||||
additionalData[0] = *(vu64 *)(currentKCodeSet + 0x50); //Process name
|
||||
additionalData[1] = *(vu64 *)(currentKCodeSet + 0x5C); //Title ID
|
||||
}
|
||||
else dumpHeader.additionalDataSize = 0;
|
||||
|
||||
dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize + dumpHeader.additionalDataSize;
|
||||
|
||||
//Copy header (actually optimized by the compiler)
|
||||
*(ExceptionDumpHeader *)finalBuffer = dumpHeader;
|
||||
|
||||
cleanInvalidateDCacheAndDMB();
|
||||
mcuReboot(); //Also contains DCache-cleaning code
|
||||
}
|
||||
31
exceptions/arm11/source/start.s
Normal file
31
exceptions/arm11/source/start.s
Normal file
@@ -0,0 +1,31 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
add pc, r0, #(handlers - .) @ Dummy instruction to prevent compiler optimizations
|
||||
|
||||
handlers:
|
||||
.word FIQHandler
|
||||
.word undefinedInstructionHandler
|
||||
.word prefetchAbortHandler
|
||||
.word dataAbortHandler
|
||||
37
exceptions/arm11/source/types.h
Normal file
37
exceptions/arm11/source/types.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//Common data types
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
42
exceptions/arm9/Makefile
Normal file
42
exceptions/arm9/Makefile
Normal file
@@ -0,0 +1,42 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
name := arm9_exceptions
|
||||
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
dir_out := ../../$(dir_build)
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -mthumb $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
.PHONY: all
|
||||
all: $(dir_out)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
14
exceptions/arm9/linker.ld
Normal file
14
exceptions/arm9/linker.ld
Normal file
@@ -0,0 +1,14 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x01FF7FE0;
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
46
exceptions/arm9/source/handlers.h
Normal file
46
exceptions/arm9/source/handlers.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 magic[2];
|
||||
u16 versionMinor, versionMajor;
|
||||
|
||||
u16 processor, core;
|
||||
u32 type;
|
||||
|
||||
u32 totalSize;
|
||||
u32 registerDumpSize;
|
||||
u32 codeDumpSize;
|
||||
u32 stackDumpSize;
|
||||
u32 additionalDataSize;
|
||||
} ExceptionDumpHeader;
|
||||
|
||||
u32 readMPUConfig(u32 *regionSettings);
|
||||
void FIQHandler(void);
|
||||
void undefinedInstructionHandler(void);
|
||||
void dataAbortHandler(void);
|
||||
void prefetchAbortHandler(void);
|
||||
109
exceptions/arm9/source/handlers.s
Normal file
109
exceptions/arm9/source/handlers.s
Normal file
@@ -0,0 +1,109 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
.macro GEN_HANDLER name
|
||||
.global \name
|
||||
.type \name, %function
|
||||
\name:
|
||||
ldr sp, =#0x02000000 @ We make the (full descending) stack point to the end of ITCM for our exception handlers.
|
||||
@ It doesn't matter if we're overwriting stuff here, since we're going to reboot.
|
||||
|
||||
stmfd sp!, {r0-r7} @ FIQ has its own r8-r14 regs
|
||||
ldr r1, =\@ @ macro expansion counter
|
||||
b _commonHandler
|
||||
|
||||
.size \name, . - \name
|
||||
.endm
|
||||
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global _commonHandler
|
||||
.type _commonHandler, %function
|
||||
_commonHandler:
|
||||
mrs r2, spsr
|
||||
mov r6, sp
|
||||
mrs r3, cpsr
|
||||
|
||||
orr r3, #0x1c0 @ disable Imprecise Aborts, IRQ and FIQ (equivalent to "cpsid aif" on arm11)
|
||||
msr cpsr_cx, r3
|
||||
|
||||
tst r2, #0x20
|
||||
bne noSvcBreak
|
||||
cmp r1, #2
|
||||
bne noSvcBreak
|
||||
|
||||
sub r0, lr, #4 @ calling cannotAccessAddress cause more problems that it actually solves... (I've to save a lot of regs and that's a pain tbh)
|
||||
lsr r0, #20 @ we'll just do some address checks (to see if it's in ARM9 internal memory)
|
||||
cmp r0, #0x80
|
||||
bne noSvcBreak
|
||||
ldr r4, [lr, #-4]
|
||||
ldr r5, =#0xe12fff7f
|
||||
cmp r4, r5
|
||||
bne noSvcBreak
|
||||
bic r5, r3, #0xf
|
||||
orr r5, #0x3
|
||||
msr cpsr_c, r5 @ switch to supervisor mode
|
||||
ldmfd sp, {r8-r11}^
|
||||
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
|
||||
ldr r4, [sp, #0x18]
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
tst r2, #0x20
|
||||
addne lr, r4, #2 @ adjust address for later
|
||||
moveq lr, r4
|
||||
|
||||
noSvcBreak:
|
||||
ands r4, r2, #0xf @ get the mode that triggered the exception
|
||||
moveq r4, #0xf @ usr => sys
|
||||
bic r5, r3, #0xf
|
||||
orr r5, r4
|
||||
msr cpsr_c, r5 @ change processor mode
|
||||
stmfd r6!, {r8-lr}
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
mov sp, r6
|
||||
|
||||
stmfd sp!, {r2,lr} @ it's a bit of a mess, but we will fix that later
|
||||
@ order of saved regs now: cpsr, pc + (2/4/8), r8-r14, r0-r7
|
||||
|
||||
mov r0, sp
|
||||
|
||||
b mainHandler
|
||||
|
||||
GEN_HANDLER FIQHandler
|
||||
GEN_HANDLER undefinedInstructionHandler
|
||||
GEN_HANDLER prefetchAbortHandler
|
||||
GEN_HANDLER dataAbortHandler
|
||||
|
||||
.global readMPUConfig
|
||||
.type readMPUConfig, %function
|
||||
readMPUConfig:
|
||||
stmfd sp!, {r4-r8, lr}
|
||||
mrc p15,0,r1,c6,c0,0
|
||||
mrc p15,0,r2,c6,c1,0
|
||||
mrc p15,0,r3,c6,c2,0
|
||||
mrc p15,0,r4,c6,c3,0
|
||||
mrc p15,0,r5,c6,c4,0
|
||||
mrc p15,0,r6,c6,c5,0
|
||||
mrc p15,0,r7,c6,c6,0
|
||||
mrc p15,0,r8,c6,c7,0
|
||||
stmia r0, {r1-r8}
|
||||
mrc p15,0,r0,c5,c0,2 @ read data access permission bits
|
||||
ldmfd sp!, {r4-r8, pc}
|
||||
139
exceptions/arm9/source/i2c.c
Normal file
139
exceptions/arm9/source/i2c.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Thanks to the everyone who contributed in the development of this file
|
||||
*/
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static const struct { u8 bus_id, reg_addr; } dev_data[] = {
|
||||
{0, 0x4A}, {0, 0x7A}, {0, 0x78},
|
||||
{1, 0x4A}, {1, 0x78}, {1, 0x2C},
|
||||
{1, 0x2E}, {1, 0x40}, {1, 0x44},
|
||||
{2, 0xD6}, {2, 0xD0}, {2, 0xD2},
|
||||
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
|
||||
};
|
||||
|
||||
static inline u8 i2cGetDeviceBusId(u8 device_id)
|
||||
{
|
||||
return dev_data[device_id].bus_id;
|
||||
}
|
||||
|
||||
static inline u8 i2cGetDeviceRegAddr(u8 device_id)
|
||||
{
|
||||
return dev_data[device_id].reg_addr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static vu8 *reg_data_addrs[] = {
|
||||
(vu8 *)(I2C1_REG_OFF + I2C_REG_DATA),
|
||||
(vu8 *)(I2C2_REG_OFF + I2C_REG_DATA),
|
||||
(vu8 *)(I2C3_REG_OFF + I2C_REG_DATA),
|
||||
};
|
||||
|
||||
static inline vu8 *i2cGetDataReg(u8 bus_id)
|
||||
{
|
||||
return reg_data_addrs[bus_id];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static vu8 *reg_cnt_addrs[] = {
|
||||
(vu8 *)(I2C1_REG_OFF + I2C_REG_CNT),
|
||||
(vu8 *)(I2C2_REG_OFF + I2C_REG_CNT),
|
||||
(vu8 *)(I2C3_REG_OFF + I2C_REG_CNT),
|
||||
};
|
||||
|
||||
static inline vu8 *i2cGetCntReg(u8 bus_id)
|
||||
{
|
||||
return reg_cnt_addrs[bus_id];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static inline void i2cWaitBusy(u8 bus_id)
|
||||
{
|
||||
while (*i2cGetCntReg(bus_id) & 0x80);
|
||||
}
|
||||
|
||||
static inline bool i2cGetResult(u8 bus_id)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
|
||||
return (*i2cGetCntReg(bus_id) >> 4) & 1;
|
||||
}
|
||||
|
||||
static void i2cStop(u8 bus_id, u8 arg0)
|
||||
{
|
||||
*i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0;
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetCntReg(bus_id) = 0xC5;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static bool i2cSelectDevice(u8 bus_id, u8 dev_reg)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = dev_reg;
|
||||
*i2cGetCntReg(bus_id) = 0xC2;
|
||||
|
||||
return i2cGetResult(bus_id);
|
||||
}
|
||||
|
||||
static bool i2cSelectRegister(u8 bus_id, u8 reg)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = reg;
|
||||
*i2cGetCntReg(bus_id) = 0xC0;
|
||||
|
||||
return i2cGetResult(bus_id);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||
{
|
||||
u8 bus_id = i2cGetDeviceBusId(dev_id);
|
||||
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
{
|
||||
if(i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg))
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = data;
|
||||
*i2cGetCntReg(bus_id) = 0xC1;
|
||||
i2cStop(bus_id, 0);
|
||||
|
||||
if(i2cGetResult(bus_id)) return true;
|
||||
}
|
||||
*i2cGetCntReg(bus_id) = 0xC5;
|
||||
i2cWaitBusy(bus_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
44
exceptions/arm9/source/i2c.h
Normal file
44
exceptions/arm9/source/i2c.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Thanks to the everyone who contributed in the development of this file
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define I2C1_REG_OFF 0x10161000
|
||||
#define I2C2_REG_OFF 0x10144000
|
||||
#define I2C3_REG_OFF 0x10148000
|
||||
|
||||
#define I2C_REG_DATA 0
|
||||
#define I2C_REG_CNT 1
|
||||
#define I2C_REG_CNTEX 2
|
||||
#define I2C_REG_SCL 4
|
||||
|
||||
#define I2C_DEV_MCU 3
|
||||
#define I2C_DEV_GYRO 10
|
||||
#define I2C_DEV_IR 13
|
||||
|
||||
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
||||
118
exceptions/arm9/source/mainHandler.c
Normal file
118
exceptions/arm9/source/mainHandler.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#include "i2c.h"
|
||||
#include "handlers.h"
|
||||
|
||||
#define FINAL_BUFFER 0x25000000
|
||||
|
||||
#define REG_DUMP_SIZE 4 * 17
|
||||
#define CODE_DUMP_SIZE 48
|
||||
|
||||
bool cannotAccessAddress(const void *address)
|
||||
{
|
||||
u32 regionSettings[8];
|
||||
u32 addr = (u32)address;
|
||||
|
||||
u32 dataAccessPermissions = readMPUConfig(regionSettings);
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
{
|
||||
if((dataAccessPermissions & 0xF) == 0 || (regionSettings[i] & 1) == 0)
|
||||
continue; //No access / region not enabled
|
||||
|
||||
u32 regionAddrBase = regionSettings[i] & ~0xFFF;
|
||||
u32 regionSize = 1 << (((regionSettings[i] >> 1) & 0x1F) + 1);
|
||||
|
||||
if(addr >= regionAddrBase && addr < regionAddrBase + regionSize)
|
||||
return false;
|
||||
|
||||
dataAccessPermissions >>= 4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 __attribute__((noinline)) copyMemory(void *dst, const void *src, u32 size, u32 alignment)
|
||||
{
|
||||
u8 *out = (u8 *)dst;
|
||||
const u8 *in = (const u8 *)src;
|
||||
|
||||
if(((u32)src & (alignment - 1)) != 0 || cannotAccessAddress(src) || (size != 0 && cannotAccessAddress((u8 *)src + size - 1)))
|
||||
return 0;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
*out++ = *in++;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type)
|
||||
{
|
||||
ExceptionDumpHeader dumpHeader;
|
||||
|
||||
u32 registerDump[REG_DUMP_SIZE / 4];
|
||||
u8 codeDump[CODE_DUMP_SIZE];
|
||||
|
||||
dumpHeader.magic[0] = 0xDEADC0DE;
|
||||
dumpHeader.magic[1] = 0xDEADCAFE;
|
||||
dumpHeader.versionMajor = 1;
|
||||
dumpHeader.versionMinor = 2;
|
||||
|
||||
dumpHeader.processor = 9;
|
||||
dumpHeader.core = 0;
|
||||
dumpHeader.type = type;
|
||||
|
||||
dumpHeader.registerDumpSize = REG_DUMP_SIZE;
|
||||
dumpHeader.codeDumpSize = CODE_DUMP_SIZE;
|
||||
dumpHeader.additionalDataSize = 0;
|
||||
|
||||
//Dump registers
|
||||
//Current order of saved regs: cpsr, pc, r8-r14, r0-r7
|
||||
u32 cpsr = regs[0];
|
||||
u32 pc = regs[1] - (type < 3 ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 8);
|
||||
|
||||
registerDump[15] = pc;
|
||||
registerDump[16] = cpsr;
|
||||
for(u32 i = 0; i < 7; i++) registerDump[8 + i] = regs[2 + i];
|
||||
for(u32 i = 0; i < 8; i++) registerDump[i] = regs[9 + i];
|
||||
|
||||
//Dump code
|
||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||
dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4);
|
||||
|
||||
//Copy register dump and code dump
|
||||
u8 *final = (u8 *)(FINAL_BUFFER + sizeof(ExceptionDumpHeader));
|
||||
final += copyMemory(final, registerDump, dumpHeader.registerDumpSize, 1);
|
||||
final += copyMemory(final, codeDump, dumpHeader.codeDumpSize, 1);
|
||||
|
||||
//Dump stack in place
|
||||
dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1);
|
||||
|
||||
dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize + dumpHeader.additionalDataSize;
|
||||
|
||||
//Copy header (actually optimized by the compiler)
|
||||
*(ExceptionDumpHeader *)FINAL_BUFFER = dumpHeader;
|
||||
|
||||
((void (*)())0xFFFF0830)(); //Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); //Reboot
|
||||
while(true);
|
||||
}
|
||||
31
exceptions/arm9/source/start.s
Normal file
31
exceptions/arm9/source/start.s
Normal file
@@ -0,0 +1,31 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
add pc, r0, #(handlers - .) @ Dummy instruction to prevent compiler optimizations
|
||||
|
||||
handlers:
|
||||
.word FIQHandler
|
||||
.word undefinedInstructionHandler
|
||||
.word prefetchAbortHandler
|
||||
.word dataAbortHandler
|
||||
37
exceptions/arm9/source/types.h
Normal file
37
exceptions/arm9/source/types.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//Common data types
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
169
exceptions/exception_dump_parser.py
Normal file
169
exceptions/exception_dump_parser.py
Normal file
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python
|
||||
# Requires Python >= 3.2 or >= 2.7
|
||||
|
||||
# This file is part of Luma3DS
|
||||
# Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
# reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
# Notices displayed by works containing it.
|
||||
|
||||
__author__ = "TuxSH"
|
||||
__copyright__ = "Copyright (c) 2016 TuxSH"
|
||||
__license__ = "GPLv3"
|
||||
__version__ = "v1.2"
|
||||
|
||||
"""
|
||||
Parses Luma3DS exception dumps
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from struct import unpack_from
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
# Source of hexdump: https://gist.github.com/ImmortalPC/c340564823f283fe530b
|
||||
# Credits for hexdump go to the original authors
|
||||
# Slightly edited by TuxSH
|
||||
|
||||
def hexdump(addr, src, length=16, sep='.' ):
|
||||
'''
|
||||
@brief Return {src} in hex dump.
|
||||
@param[in] length {Int} Nb Bytes by row.
|
||||
@param[in] sep {Char} For the text part, {sep} will be used for non ASCII char.
|
||||
@return {Str} The hexdump
|
||||
@note Full support for python2 and python3 !
|
||||
'''
|
||||
result = []
|
||||
|
||||
# Python3 support
|
||||
try:
|
||||
xrange(0,1)
|
||||
except NameError:
|
||||
xrange = range
|
||||
|
||||
for i in xrange(0, len(src), length):
|
||||
subSrc = src[i:i+length]
|
||||
hexa = ''
|
||||
isMiddle = False
|
||||
for h in xrange(0,len(subSrc)):
|
||||
if h == length/2:
|
||||
hexa += ' '
|
||||
h = subSrc[h]
|
||||
if not isinstance(h, int):
|
||||
h = ord(h)
|
||||
h = hex(h).replace('0x','')
|
||||
if len(h) == 1:
|
||||
h = '0'+h
|
||||
hexa += h+' '
|
||||
hexa = hexa.strip(' ')
|
||||
text = ''
|
||||
for c in subSrc:
|
||||
if not isinstance(c, int):
|
||||
c = ord(c)
|
||||
if 0x20 <= c < 0x7F:
|
||||
text += chr(c)
|
||||
else:
|
||||
text += sep
|
||||
result.append(('%08x: %-'+str(length*(2+1)+1)+'s |%s|') % (addr + i, hexa, text))
|
||||
|
||||
return '\n'.join(result)
|
||||
|
||||
|
||||
def makeRegisterLine(A, rA, B, rB):
|
||||
return "{0:<15}{1:<20}{2:<15}{3:<20}".format(A, "{0:08x}".format(rA), B, "{0:08x}".format(rB))
|
||||
|
||||
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
|
||||
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr") + ("dfsr", "ifsr", "far") + ("fpexc", "fpinst", "fpinst2")
|
||||
svcBreakReasons = ("(svcBreak: panic)", "(svcBreak: assertion failed)", "(svcBreak: user-related)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Parse Luma3DS exception dumps")
|
||||
parser.add_argument("filename")
|
||||
args = parser.parse_args()
|
||||
data = b""
|
||||
with open(args.filename, "rb") as f: data = f.read()
|
||||
if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe):
|
||||
raise SystemExit("Invalid file format")
|
||||
|
||||
version, processor, exceptionType, _, nbRegisters, codeDumpSize, stackDumpSize, additionalDataSize = unpack_from("<8I", data, 8)
|
||||
nbRegisters //= 4
|
||||
|
||||
if version < (1 << 16) | 2:
|
||||
raise SystemExit("Incompatible format version, please use the appropriate parser.")
|
||||
|
||||
registers = unpack_from("<{0}I".format(nbRegisters), data, 40)
|
||||
codeOffset = 40 + 4 * nbRegisters
|
||||
codeDump = data[codeOffset : codeOffset + codeDumpSize]
|
||||
stackOffset = codeOffset + codeDumpSize
|
||||
stackDump = data[stackOffset : stackOffset + stackDumpSize]
|
||||
addtionalDataOffset = stackOffset + stackDumpSize
|
||||
additionalData = data[addtionalDataOffset : addtionalDataOffset + additionalDataSize]
|
||||
|
||||
if processor == 9: print("Processor: ARM9")
|
||||
else: print("Processor: ARM11 (core {0})".format(processor >> 16))
|
||||
|
||||
typeDetailsStr = ""
|
||||
if exceptionType == 2:
|
||||
if (registers[16] & 0x20) == 0 and codeDumpSize >= 4:
|
||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
||||
if instr == 0xe12fff7e:
|
||||
typeDetailsStr = " (kernel panic)"
|
||||
elif instr == 0xef00003c:
|
||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
||||
elif (registers[16] & 0x20) == 1 and codeDumpSize >= 2:
|
||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
||||
if instr == 0xdf3c:
|
||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
||||
|
||||
elif processor != 9 and (registers[20] & 0x80000000) != 0:
|
||||
typeDetailsStr = " (VFP exception)"
|
||||
|
||||
print("Exception type: {0}{1}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType], typeDetailsStr))
|
||||
if additionalDataSize != 0:
|
||||
print("Current process: {0} ({1:016x})".format(additionalData[:8].decode("ascii"), unpack_from("<Q", additionalData, 8)[0]))
|
||||
|
||||
print("\nRegister dump:\n")
|
||||
for i in range(0, nbRegisters - (nbRegisters % 2), 2):
|
||||
if i == 16: print("")
|
||||
print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1]))
|
||||
if nbRegisters % 2 == 1: print("{0:<15}{1:<20}".format(registerNames[nbRegisters - 1], "{0:08x}".format(registers[nbRegisters - 1])))
|
||||
|
||||
thumb = registers[16] & 0x20 != 0
|
||||
addr = registers[15] - codeDumpSize + (2 if thumb else 4)
|
||||
|
||||
print("\nCode dump:\n")
|
||||
|
||||
objdump_res = ""
|
||||
try:
|
||||
path = os.path.join(os.environ["DEVKITARM"], "bin", "arm-none-eabi-objdump")
|
||||
if os.name == "nt":
|
||||
path = ''.join((path[1], ':', path[2:])).replace('/', '\\')
|
||||
|
||||
objdump_res = subprocess.check_output((
|
||||
path, "-marm", "-b", "binary",
|
||||
"--adjust-vma="+hex(addr - codeOffset), "--start-address="+hex(addr),
|
||||
"--stop-address="+hex(addr + codeDumpSize), "-D", "-z", "-M",
|
||||
"reg-names-std" + (",force-thumb" if thumb else ""), args.filename
|
||||
)).decode("utf-8")
|
||||
objdump_res = '\n'.join(objdump_res[objdump_res.find('<.data+'):].split('\n')[1:])
|
||||
except: objdump_res = ""
|
||||
|
||||
print(objdump_res if objdump_res != "" else hexdump(addr, codeDump))
|
||||
|
||||
print("\nStack dump:\n")
|
||||
print(hexdump(registers[13], stackDump))
|
||||
21
injector/LICENSE
Normal file
21
injector/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Yifan Lu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
74
injector/Makefile
Executable file
74
injector/Makefile
Executable file
@@ -0,0 +1,74 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
dir_source := source
|
||||
dir_patches := patches
|
||||
dir_build := build
|
||||
dir_out := ../$(dir_build)
|
||||
|
||||
LIBS := -lctru
|
||||
LIBDIRS := $(CTRULIB)
|
||||
LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
|
||||
|
||||
ASFLAGS := -mcpu=mpcore -mfloat-abi=hard
|
||||
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -O2 -flto -ffast-math $(INCLUDE) -DARM11 -D_3DS
|
||||
LDFLAGS := -specs=3dsx.specs $(ASFLAGS) -Wl,--section-start,.text=0x14000000
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c))
|
||||
|
||||
bundled = $(dir_build)/romfsredir.bin.o
|
||||
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
endef
|
||||
|
||||
.PHONY: all
|
||||
all: $(dir_out)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
.PRECIOUS: $(dir_build)/%.bin
|
||||
|
||||
$(dir_build):
|
||||
@mkdir -p "$@"
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
@makerom -f ncch -rsf loader.rsf -nocodepadding -o $@ -elf $<
|
||||
|
||||
$(dir_build)/$(name).elf: $(bundled) $(objects)
|
||||
$(LINK.o) $(OUTPUT_OPTION) $^ $(LIBPATHS) $(LIBS)
|
||||
|
||||
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||
@$(bin2o)
|
||||
|
||||
$(dir_build)/%.bin: $(dir_patches)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
|
||||
$(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
|
||||
|
||||
$(dir_build)/bundled.h: $(bundled)
|
||||
@$(foreach f, $(bundled),\
|
||||
echo "extern const u8" `(echo $(basename $(notdir $(f))) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $@;\
|
||||
echo "extern const u32" `(echo $(basename $(notdir $(f)))| sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $@;\
|
||||
)
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/bundled.h
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
30
injector/README.md
Normal file
30
injector/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
3DS Loader Replacement
|
||||
======================
|
||||
|
||||
This is an open source implementation of 3DS `loader` system module--with
|
||||
additional features. The current aim of the project is to provide a nice
|
||||
entry point for patching 3DS modules.
|
||||
|
||||
## Roadmap
|
||||
Right now, this can serve as an open-source replacement for the built in loader.
|
||||
There is additional support for patching any executable after it's loaded but
|
||||
before it starts. For example, you can patch `menu` to skip region checks and
|
||||
have region free game launching directly from the home menu. There is also
|
||||
support for SDMC reading (not found in original loader implementation) which
|
||||
means that patches can be loaded from the SD card. Ultimately, there would be
|
||||
a patch system that supports easy loading of patches from the SD card.
|
||||
|
||||
## Build
|
||||
You need a working 3DS build environment with a fairly recent copy of devkitARM,
|
||||
ctrulib, and makerom. If you see any errors in the build process, it's likely
|
||||
that you're using an older version.
|
||||
|
||||
Currently, there is no support for FIRM building, so you need to do some steps
|
||||
manually. First, you have to add padding to make sure the NCCH is of the right
|
||||
size to drop in as a replacement. A hacky way is
|
||||
[this patch](http://pastebin.com/nyKXLnNh) which adds junk data. Play around
|
||||
with the size value to get the NCCH to be the exact same size as the one
|
||||
found in your decrypted FIRM dump.
|
||||
|
||||
Once you have a NCCH of the right size, just replace it in your decrypted FIRM
|
||||
and find a way to launch it (for example with ReiNAND).
|
||||
115
injector/loader.rsf
Normal file
115
injector/loader.rsf
Normal file
@@ -0,0 +1,115 @@
|
||||
BasicInfo:
|
||||
Title : loader
|
||||
CompanyCode : "00"
|
||||
ProductCode : 0828builder
|
||||
ContentType : Application
|
||||
Logo : None
|
||||
|
||||
TitleInfo:
|
||||
UniqueId : 0x13
|
||||
Category : Base
|
||||
Version : 2
|
||||
|
||||
Option:
|
||||
UseOnSD : false
|
||||
FreeProductCode : true # Removes limitations on ProductCode
|
||||
MediaFootPadding : false # If true CCI files are created with padding
|
||||
EnableCrypt : false # Enables encryption for NCCH and CIA
|
||||
EnableCompress : true # Compresses exefs code
|
||||
|
||||
AccessControlInfo:
|
||||
IdealProcessor : 1
|
||||
AffinityMask : 3
|
||||
|
||||
Priority : 20
|
||||
|
||||
DisableDebug : true
|
||||
EnableForceDebug : false
|
||||
CanWriteSharedPage : false
|
||||
CanUsePrivilegedPriority : false
|
||||
CanUseNonAlphabetAndNumber : false
|
||||
PermitMainFunctionArgument : false
|
||||
CanShareDeviceMemory : false
|
||||
RunnableOnSleep : true
|
||||
SpecialMemoryArrange : true
|
||||
ResourceLimitCategory : Other
|
||||
|
||||
CoreVersion : 2
|
||||
DescVersion : 2
|
||||
|
||||
MemoryType : Base # Application / System / Base
|
||||
HandleTableSize: 0
|
||||
SystemCallAccess:
|
||||
AcceptSession: 74
|
||||
ArbitrateAddress: 34
|
||||
Break: 60
|
||||
CancelTimer: 28
|
||||
ClearEvent: 25
|
||||
ClearTimer: 29
|
||||
CloseHandle: 35
|
||||
ConnectToPort: 45
|
||||
ControlMemory: 1
|
||||
CreateAddressArbiter: 33
|
||||
CreateCodeSet: 115
|
||||
CreateEvent: 23
|
||||
CreateMemoryBlock: 30
|
||||
CreateMutex: 19
|
||||
CreatePort: 71
|
||||
CreateProcess: 117
|
||||
CreateSemaphore: 21
|
||||
CreateSessionToPort: 72
|
||||
CreateThread: 8
|
||||
CreateTimer: 26
|
||||
DuplicateHandle: 39
|
||||
ExitProcess: 3
|
||||
ExitThread: 9
|
||||
GetCurrentProcessorNumber: 17
|
||||
GetHandleInfo: 41
|
||||
GetProcessId: 53
|
||||
GetProcessIdealProcessor: 6
|
||||
GetProcessIdOfThread: 54
|
||||
GetProcessInfo: 43
|
||||
GetResourceLimit: 56
|
||||
GetResourceLimitCurrentValues: 58
|
||||
GetResourceLimitLimitValues: 57
|
||||
GetSystemInfo: 42
|
||||
GetSystemTick: 40
|
||||
GetThreadContext: 59
|
||||
GetThreadId: 55
|
||||
GetThreadIdealProcessor: 15
|
||||
GetThreadInfo: 44
|
||||
GetThreadPriority: 11
|
||||
MapMemoryBlock: 31
|
||||
OutputDebugString: 61
|
||||
QueryMemory: 2
|
||||
RandomStub: 116
|
||||
ReleaseMutex: 20
|
||||
ReleaseSemaphore: 22
|
||||
ReplyAndReceive1: 75
|
||||
ReplyAndReceive2: 76
|
||||
ReplyAndReceive3: 77
|
||||
ReplyAndReceive4: 78
|
||||
ReplyAndReceive: 79
|
||||
SendSyncRequest1: 46
|
||||
SendSyncRequest2: 47
|
||||
SendSyncRequest3: 48
|
||||
SendSyncRequest4: 49
|
||||
SendSyncRequest: 50
|
||||
SetThreadPriority: 12
|
||||
SetTimer: 27
|
||||
SignalEvent: 24
|
||||
SleepThread: 10
|
||||
UnmapMemoryBlock: 32
|
||||
WaitSynchronization1: 36
|
||||
WaitSynchronizationN: 37
|
||||
InterruptNumbers:
|
||||
ServiceAccessControl:
|
||||
- fs:LDR
|
||||
FileSystemAccess:
|
||||
- DirectSdmc
|
||||
- CtrNandRw
|
||||
|
||||
SystemControlInfo:
|
||||
SaveDataSize: 0KB # It doesn't use any save data.
|
||||
RemasterVersion: 0
|
||||
StackSize: 0x1000
|
||||
114
injector/patches/romfsredir.s
Normal file
114
injector/patches/romfsredir.s
Normal file
@@ -0,0 +1,114 @@
|
||||
.arm.little
|
||||
.create "build/romfsredir.bin", 0
|
||||
|
||||
.macro addr, reg, func
|
||||
add reg, pc, #func-.-8
|
||||
.endmacro
|
||||
.macro load, reg, func
|
||||
ldr reg, [pc, #func-.-8]
|
||||
.endmacro
|
||||
|
||||
; Patch by delebile
|
||||
|
||||
.arm
|
||||
_start:
|
||||
|
||||
; Jumps here before the fsOpenFileDirectly call
|
||||
_mountArchive:
|
||||
b mountArchive
|
||||
.word 0xdead0000 ; Substituted opcode
|
||||
.word 0xdead0001 ; Branch to hooked function
|
||||
|
||||
; Jumps here before every iFileOpen call
|
||||
_fsRedir:
|
||||
b fsRedir
|
||||
.word 0xdead0002 ; Substituted opcode
|
||||
.word 0xdead0003 ; Branch to hooked function
|
||||
|
||||
; Mounts the archive and registers it as 'lf:'
|
||||
mountArchive:
|
||||
cmp r3, #3
|
||||
bne _mountArchive + 4
|
||||
stmfd sp!, {r0-r4, lr}
|
||||
sub sp, sp, #4
|
||||
load r1, archiveId
|
||||
mov r0, sp
|
||||
load r4, fsMountArchive
|
||||
blx r4
|
||||
mov r3, #0
|
||||
mov r2, #0
|
||||
ldr r1, [sp]
|
||||
addr r0, archiveName
|
||||
load r4, fsRegisterArchive
|
||||
blx r4
|
||||
add sp, sp, #4
|
||||
ldmfd sp!, {r0-r4, lr}
|
||||
b _mountArchive + 4
|
||||
|
||||
; Check the path passed to iFileOpen.
|
||||
; If it is trying to access a RomFS file, we try to
|
||||
; open it from the LayeredFS folder.
|
||||
; If the file cannot be opened, we just open
|
||||
; it from its original archive like nothing happened
|
||||
fsRedir:
|
||||
stmfd sp!, {r0-r12, lr}
|
||||
addr r3, romFsMount
|
||||
bl compare
|
||||
addne r3, pc, #updateRomFsMount-.-8
|
||||
blne compare
|
||||
bne endRedir
|
||||
sub sp, sp, #0x400
|
||||
pathRedir:
|
||||
stmfd sp!, {r0-r3}
|
||||
add r0, sp, #0x10
|
||||
load r3, customPath
|
||||
pathRedir_1:
|
||||
ldrb r2, [r3], #1
|
||||
strh r2, [r0], #2
|
||||
cmp r2, #0
|
||||
bne pathRedir_1
|
||||
sub r0, r0, #2
|
||||
pathRedir_2:
|
||||
ldrh r2, [r1], #2
|
||||
cmp r2, #0x3A ; ':'
|
||||
bne pathRedir_2
|
||||
pathRedir_3:
|
||||
ldrh r2, [r1], #2
|
||||
strh r2, [r0], #2
|
||||
cmp r2, #0
|
||||
bne pathRedir_3
|
||||
ldmfd sp!, {r0-r3}
|
||||
mov r1, sp
|
||||
bl _fsRedir + 4
|
||||
add sp, sp, #0x400
|
||||
cmp r0, #0
|
||||
|
||||
endRedir:
|
||||
ldmfd sp!, {r0-r12, lr}
|
||||
moveq r0, #0
|
||||
bxeq lr
|
||||
b _fsRedir + 4
|
||||
|
||||
compare:
|
||||
mov r9, r1
|
||||
add r10, r3, #4
|
||||
loop:
|
||||
ldrb r12, [r3], #1
|
||||
ldrb r11, [r9], #2
|
||||
cmp r11, r12
|
||||
bxne lr
|
||||
cmp r10, r3
|
||||
bne loop
|
||||
bx lr
|
||||
|
||||
.pool
|
||||
.align 4
|
||||
archiveName : .dcb "lf:", 0
|
||||
fsMountArchive : .word 0xdead0005
|
||||
fsRegisterArchive : .word 0xdead0006
|
||||
archiveId : .word 0xdead0007
|
||||
romFsMount : .dcb "rom:"
|
||||
updateRomFsMount : .word 0xdead0008
|
||||
customPath : .word 0xdead0004
|
||||
|
||||
.close
|
||||
19
injector/source/CFWInfo.h
Normal file
19
injector/source/CFWInfo.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic[4];
|
||||
|
||||
u8 versionMajor;
|
||||
u8 versionMinor;
|
||||
u8 versionBuild;
|
||||
u8 flags;
|
||||
|
||||
u32 commitHash;
|
||||
|
||||
u32 config;
|
||||
} CFWInfo;
|
||||
|
||||
u32 svcGetCFWInfo(CFWInfo *info);
|
||||
9
injector/source/CFWInfo.s
Normal file
9
injector/source/CFWInfo.s
Normal file
@@ -0,0 +1,9 @@
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global svcGetCFWInfo
|
||||
.type svcGetCFWInfo, %function
|
||||
svcGetCFWInfo:
|
||||
svc 0x2e
|
||||
bx lr
|
||||
97
injector/source/exheader.h
Executable file
97
injector/source/exheader.h
Executable file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 reserved[5];
|
||||
u8 flag;
|
||||
u8 remasterversion[2];
|
||||
} PACKED exheader_systeminfoflags;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 address;
|
||||
u32 nummaxpages;
|
||||
u32 codesize;
|
||||
} PACKED exheader_codesegmentinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 name[8];
|
||||
exheader_systeminfoflags flags;
|
||||
exheader_codesegmentinfo text;
|
||||
u8 stacksize[4];
|
||||
exheader_codesegmentinfo ro;
|
||||
u8 reserved[4];
|
||||
exheader_codesegmentinfo data;
|
||||
u32 bsssize;
|
||||
} PACKED exheader_codesetinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 programid[0x30];
|
||||
} PACKED exheader_dependencylist;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 savedatasize[4];
|
||||
u8 reserved[4];
|
||||
u8 jumpid[8];
|
||||
u8 reserved2[0x30];
|
||||
} PACKED exheader_systeminfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 extsavedataid[8];
|
||||
u8 systemsavedataid[8];
|
||||
u8 reserved[8];
|
||||
u8 accessinfo[7];
|
||||
u8 otherattributes;
|
||||
} PACKED exheader_storageinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 programid;
|
||||
u8 flags[8];
|
||||
u16 resourcelimitdescriptor[0x10];
|
||||
exheader_storageinfo storageinfo;
|
||||
u64 serviceaccesscontrol[0x20];
|
||||
u8 reserved[0x1f];
|
||||
u8 resourcelimitcategory;
|
||||
} PACKED exheader_arm11systemlocalcaps;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 descriptors[28];
|
||||
u8 reserved[0x10];
|
||||
} PACKED exheader_arm11kernelcapabilities;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 descriptors[15];
|
||||
u8 descversion;
|
||||
} PACKED exheader_arm9accesscontrol;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// systemcontrol info {
|
||||
// coreinfo {
|
||||
exheader_codesetinfo codesetinfo;
|
||||
exheader_dependencylist deplist;
|
||||
// }
|
||||
exheader_systeminfo systeminfo;
|
||||
// }
|
||||
// accesscontrolinfo {
|
||||
exheader_arm11systemlocalcaps arm11systemlocalcaps;
|
||||
exheader_arm11kernelcapabilities arm11kernelcaps;
|
||||
exheader_arm9accesscontrol arm9accesscontrol;
|
||||
// }
|
||||
struct {
|
||||
u8 signature[0x100];
|
||||
u8 ncchpubkeymodulus[0x100];
|
||||
exheader_arm11systemlocalcaps arm11systemlocalcaps;
|
||||
exheader_arm11kernelcapabilities arm11kernelcaps;
|
||||
exheader_arm9accesscontrol arm9accesscontrol;
|
||||
} PACKED accessdesc;
|
||||
} PACKED exheader_header;
|
||||
166
injector/source/fsldr.c
Normal file
166
injector/source/fsldr.c
Normal file
@@ -0,0 +1,166 @@
|
||||
#include <3ds.h>
|
||||
#include "fsldr.h"
|
||||
#include "fsreg.h"
|
||||
#include "srvsys.h"
|
||||
|
||||
#define SDK_VERSION 0x70200C8
|
||||
|
||||
static Handle fsldrHandle;
|
||||
static int fsldrRefCount;
|
||||
|
||||
// MAKE SURE fsreg has been init before calling this
|
||||
static Result fsldrPatchPermissions(void)
|
||||
{
|
||||
u32 pid;
|
||||
Result res;
|
||||
FS_ProgramInfo info;
|
||||
u32 storage[8] = {0};
|
||||
|
||||
storage[6] = 0x680; // SDMC access and NAND access flag
|
||||
info.programId = 0x0004013000001302LL; // loader PID
|
||||
info.mediaType = MEDIATYPE_NAND;
|
||||
res = svcGetProcessId(&pid, 0xFFFF8001);
|
||||
if (R_SUCCEEDED(res))
|
||||
{
|
||||
res = FSREG_Register(pid, 0xFFFF000000000000LL, &info, (u8 *)storage);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result fsldrInit(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
if (AtomicPostIncrement(&fsldrRefCount)) return 0;
|
||||
|
||||
ret = srvSysGetServiceHandle(&fsldrHandle, "fs:LDR");
|
||||
if (R_SUCCEEDED(ret))
|
||||
{
|
||||
fsldrPatchPermissions();
|
||||
ret = FSLDR_InitializeWithSdkVersion(fsldrHandle, SDK_VERSION);
|
||||
ret = FSLDR_SetPriority(0);
|
||||
if (R_FAILED(ret)) svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomicDecrement(&fsldrRefCount);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fsldrExit(void)
|
||||
{
|
||||
if (AtomicDecrement(&fsldrRefCount)) return;
|
||||
svcCloseHandle(fsldrHandle);
|
||||
}
|
||||
|
||||
Result FSLDR_InitializeWithSdkVersion(Handle session, u32 version)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x861,1,2); // 0x8610042
|
||||
cmdbuf[1] = version;
|
||||
cmdbuf[2] = 32;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(session))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_SetPriority(u32 priority)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x862,1,0); // 0x8620040
|
||||
cmdbuf[1] = priority;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_OpenFileDirectly(Handle* out, FS_ArchiveID archiveId, FS_Path archivePath, FS_Path filePath, u32 openFlags, u32 attributes)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x803,8,4); // 0x8030204
|
||||
cmdbuf[1] = 0;
|
||||
cmdbuf[2] = archiveId;
|
||||
cmdbuf[3] = archivePath.type;
|
||||
cmdbuf[4] = archivePath.size;
|
||||
cmdbuf[5] = filePath.type;
|
||||
cmdbuf[6] = filePath.size;
|
||||
cmdbuf[7] = openFlags;
|
||||
cmdbuf[8] = attributes;
|
||||
cmdbuf[9] = IPC_Desc_StaticBuffer(archivePath.size, 2);
|
||||
cmdbuf[10] = (u32)archivePath.data;
|
||||
cmdbuf[11] = IPC_Desc_StaticBuffer(filePath.size, 0);
|
||||
cmdbuf[12] = (u32)filePath.data;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_OpenArchive(FS_Archive* archive, FS_ArchiveID id, FS_Path path)
|
||||
{
|
||||
if(!archive) return -2;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80C,3,2); // 0x80C00C2
|
||||
cmdbuf[1] = id;
|
||||
cmdbuf[2] = path.type;
|
||||
cmdbuf[3] = path.size;
|
||||
cmdbuf[4] = IPC_Desc_StaticBuffer(path.size, 0);
|
||||
cmdbuf[5] = (u32) path.data;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
if(archive) *archive = cmdbuf[2] | ((u64) cmdbuf[3] << 32);
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_CloseArchive(FS_Archive archive)
|
||||
{
|
||||
if(!archive) return -2;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80E,2,0); // 0x80E0080
|
||||
cmdbuf[1] = (u32) archive;
|
||||
cmdbuf[2] = (u32) (archive >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_OpenDirectory(Handle* out, FS_Archive archive, FS_Path path)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80B,4,2); // 0x80B0102
|
||||
cmdbuf[1] = (u32) archive;
|
||||
cmdbuf[2] = (u32) (archive >> 32);
|
||||
cmdbuf[3] = path.type;
|
||||
cmdbuf[4] = path.size;
|
||||
cmdbuf[5] = IPC_Desc_StaticBuffer(path.size, 0);
|
||||
cmdbuf[6] = (u32) path.data;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
12
injector/source/fsldr.h
Normal file
12
injector/source/fsldr.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
Result fsldrInit(void);
|
||||
void fsldrExit(void);
|
||||
Result FSLDR_InitializeWithSdkVersion(Handle session, u32 version);
|
||||
Result FSLDR_SetPriority(u32 priority);
|
||||
Result FSLDR_OpenFileDirectly(Handle* out, FS_ArchiveID archiveId, FS_Path archivePath, FS_Path filePath, u32 openFlags, u32 attributes);
|
||||
Result FSLDR_OpenArchive(FS_Archive* archive, FS_ArchiveID id, FS_Path path);
|
||||
Result FSLDR_CloseArchive(FS_Archive archive);
|
||||
Result FSLDR_OpenDirectory(Handle* out, FS_Archive archive, FS_Path path);
|
||||
116
injector/source/fsreg.c
Normal file
116
injector/source/fsreg.c
Normal file
@@ -0,0 +1,116 @@
|
||||
#include <3ds.h>
|
||||
#include <string.h>
|
||||
#include "fsreg.h"
|
||||
#include "srvsys.h"
|
||||
|
||||
static Handle fsregHandle;
|
||||
static int fsregRefCount;
|
||||
|
||||
Result fsregInit(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
if (AtomicPostIncrement(&fsregRefCount)) return 0;
|
||||
|
||||
ret = srvSysGetServiceHandle(&fsregHandle, "fs:REG");
|
||||
|
||||
if (R_FAILED(ret)) AtomicDecrement(&fsregRefCount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fsregExit(void)
|
||||
{
|
||||
if (AtomicDecrement(&fsregRefCount)) return;
|
||||
svcCloseHandle(fsregHandle);
|
||||
}
|
||||
|
||||
Result FSREG_CheckHostLoadId(u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x406,2,0); // 0x4060080
|
||||
cmdbuf[1] = (u32) (prog_handle);
|
||||
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_LoadProgram(u64 *prog_handle, FS_ProgramInfo *title)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x404,4,0); // 0x4040100
|
||||
memcpy(&cmdbuf[1], &title->programId, sizeof(u64));
|
||||
*(u8 *)&cmdbuf[3] = title->mediaType;
|
||||
memcpy(((u8 *)&cmdbuf[3])+1, &title->padding, 7);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
*prog_handle = *(u64 *)&cmdbuf[2];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_GetProgramInfo(exheader_header *exheader, u32 entry_count, u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x403,3,0); // 0x40300C0
|
||||
cmdbuf[1] = entry_count;
|
||||
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||
cmdbuf[64] = ((entry_count << 10) << 14) | 2;
|
||||
cmdbuf[65] = (u32) exheader;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_UnloadProgram(u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x405,2,0); // 0x4050080
|
||||
cmdbuf[1] = (u32) (prog_handle);
|
||||
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_Unregister(u32 pid)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x402,1,0); // 0x4020040
|
||||
cmdbuf[1] = pid;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_Register(u32 pid, u64 prog_handle, FS_ProgramInfo *info, void *storageinfo)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x401,0xf,0); // 0x40103C0
|
||||
cmdbuf[1] = pid;
|
||||
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||
memcpy(&cmdbuf[4], &info->programId, sizeof(u64));
|
||||
*(u8 *)&cmdbuf[6] = info->mediaType;
|
||||
memcpy(((u8 *)&cmdbuf[6])+1, &info->padding, 7);
|
||||
memcpy((u8 *)&cmdbuf[8], storageinfo, 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
13
injector/source/fsreg.h
Normal file
13
injector/source/fsreg.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include "exheader.h"
|
||||
|
||||
Result fsregInit(void);
|
||||
void fsregExit(void);
|
||||
Result FSREG_CheckHostLoadId(u64 prog_handle);
|
||||
Result FSREG_LoadProgram(u64 *prog_handle, FS_ProgramInfo *title);
|
||||
Result FSREG_GetProgramInfo(exheader_header *exheader, u32 entry_count, u64 prog_handle);
|
||||
Result FSREG_UnloadProgram(u64 prog_handle);
|
||||
Result FSREG_Unregister(u32 pid);
|
||||
Result FSREG_Register(u32 pid, u64 prog_handle, FS_ProgramInfo *info, void *storageinfo);
|
||||
66
injector/source/ifile.c
Normal file
66
injector/source/ifile.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <3ds.h>
|
||||
#include "ifile.h"
|
||||
#include "fsldr.h"
|
||||
|
||||
Result IFile_Open(IFile *file, FS_ArchiveID archiveId, FS_Path archivePath, FS_Path filePath, u32 flags)
|
||||
{
|
||||
Result res;
|
||||
|
||||
res = FSLDR_OpenFileDirectly(&file->handle, archiveId, archivePath, filePath, flags, 0);
|
||||
file->pos = 0;
|
||||
file->size = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
Result IFile_Close(IFile *file)
|
||||
{
|
||||
return FSFILE_Close(file->handle);
|
||||
}
|
||||
|
||||
Result IFile_GetSize(IFile *file, u64 *size)
|
||||
{
|
||||
Result res;
|
||||
|
||||
res = FSFILE_GetSize(file->handle, size);
|
||||
file->size = *size;
|
||||
return res;
|
||||
}
|
||||
|
||||
Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len)
|
||||
{
|
||||
u32 read;
|
||||
u32 left;
|
||||
char *buf;
|
||||
u64 cur;
|
||||
Result res;
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
*total = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = (char *)buffer;
|
||||
cur = 0;
|
||||
left = len;
|
||||
while (1)
|
||||
{
|
||||
res = FSFILE_Read(file->handle, &read, file->pos, buf, left);
|
||||
if (R_FAILED(res))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cur += read;
|
||||
file->pos += read;
|
||||
if (read == left)
|
||||
{
|
||||
break;
|
||||
}
|
||||
buf += read;
|
||||
left -= read;
|
||||
}
|
||||
|
||||
*total = cur;
|
||||
return res;
|
||||
}
|
||||
15
injector/source/ifile.h
Normal file
15
injector/source/ifile.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Handle handle;
|
||||
u64 pos;
|
||||
u64 size;
|
||||
} IFile;
|
||||
|
||||
Result IFile_Open(IFile *file, FS_ArchiveID archiveId, FS_Path archivePath, FS_Path filePath, u32 flags);
|
||||
Result IFile_Close(IFile *file);
|
||||
Result IFile_GetSize(IFile *file, u64 *size);
|
||||
Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len);
|
||||
593
injector/source/loader.c
Normal file
593
injector/source/loader.c
Normal file
@@ -0,0 +1,593 @@
|
||||
#include <3ds.h>
|
||||
#include "memory.h"
|
||||
#include "patcher.h"
|
||||
#include "exheader.h"
|
||||
#include "ifile.h"
|
||||
#include "fsldr.h"
|
||||
#include "fsreg.h"
|
||||
#include "pxipm.h"
|
||||
#include "srvsys.h"
|
||||
|
||||
#define MAX_SESSIONS 1
|
||||
|
||||
const char CODE_PATH[] = {0x01, 0x00, 0x00, 0x00, 0x2E, 0x63, 0x6F, 0x64, 0x65, 0x00, 0x00, 0x00};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 text_addr;
|
||||
u32 text_size;
|
||||
u32 ro_addr;
|
||||
u32 ro_size;
|
||||
u32 data_addr;
|
||||
u32 data_size;
|
||||
u32 total_size;
|
||||
} prog_addrs_t;
|
||||
|
||||
static Handle g_handles[MAX_SESSIONS+2];
|
||||
static int g_active_handles;
|
||||
static u64 g_cached_prog_handle;
|
||||
static exheader_header g_exheader;
|
||||
static char g_ret_buf[1024];
|
||||
|
||||
static int lzss_decompress(u8 *end)
|
||||
{
|
||||
unsigned int v1; // r1@2
|
||||
u8 *v2; // r2@2
|
||||
u8 *v3; // r3@2
|
||||
u8 *v4; // r1@2
|
||||
char v5; // r5@4
|
||||
char v6; // t1@4
|
||||
signed int v7; // r6@4
|
||||
int v9; // t1@7
|
||||
u8 *v11; // r3@8
|
||||
int v12; // r12@8
|
||||
int v13; // t1@8
|
||||
int v14; // t1@8
|
||||
unsigned int v15; // r7@8
|
||||
int v16; // r12@8
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
if ( end )
|
||||
{
|
||||
v1 = *((u32 *)end - 2);
|
||||
v2 = &end[*((u32 *)end - 1)];
|
||||
v3 = &end[-(v1 >> 24)];
|
||||
v4 = &end[-(v1 & 0xFFFFFF)];
|
||||
while ( v3 > v4 )
|
||||
{
|
||||
v6 = *(v3-- - 1);
|
||||
v5 = v6;
|
||||
v7 = 8;
|
||||
while ( 1 )
|
||||
{
|
||||
if ( (v7-- < 1) )
|
||||
break;
|
||||
if ( v5 & 0x80 )
|
||||
{
|
||||
v13 = *(v3 - 1);
|
||||
v11 = v3 - 1;
|
||||
v12 = v13;
|
||||
v14 = *(v11 - 1);
|
||||
v3 = v11 - 1;
|
||||
v15 = ((v14 | (v12 << 8)) & 0xFFFF0FFF) + 2;
|
||||
v16 = v12 + 32;
|
||||
do
|
||||
{
|
||||
ret = v2[v15];
|
||||
*(v2-- - 1) = ret;
|
||||
v16 -= 16;
|
||||
}
|
||||
while ( !(v16 < 0) );
|
||||
}
|
||||
else
|
||||
{
|
||||
v9 = *(v3-- - 1);
|
||||
ret = v9;
|
||||
*(v2-- - 1) = v9;
|
||||
}
|
||||
v5 *= 2;
|
||||
if ( v3 <= v4 )
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Result allocate_shared_mem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
|
||||
{
|
||||
u32 dummy;
|
||||
|
||||
memcpy(shared, vaddr, sizeof(prog_addrs_t));
|
||||
shared->text_addr = 0x10000000;
|
||||
shared->ro_addr = shared->text_addr + (shared->text_size << 12);
|
||||
shared->data_addr = shared->ro_addr + (shared->ro_size << 12);
|
||||
return svcControlMemory(&dummy, shared->text_addr, 0, shared->total_size << 12, (flags & 0xF00) | MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
|
||||
}
|
||||
|
||||
static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int is_compressed)
|
||||
{
|
||||
IFile file;
|
||||
FS_Path archivePath;
|
||||
FS_Path filePath;
|
||||
Result res;
|
||||
u64 size;
|
||||
u64 total;
|
||||
|
||||
archivePath.type = PATH_BINARY;
|
||||
archivePath.data = &prog_handle;
|
||||
archivePath.size = 8;
|
||||
|
||||
filePath.type = PATH_BINARY;
|
||||
filePath.data = CODE_PATH;
|
||||
filePath.size = sizeof(CODE_PATH);
|
||||
if (R_FAILED(IFile_Open(&file, ARCHIVE_SAVEDATA_AND_CONTENT2, archivePath, filePath, FS_OPEN_READ)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
// get file size
|
||||
if (R_FAILED(IFile_GetSize(&file, &size)))
|
||||
{
|
||||
IFile_Close(&file);
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
// check size
|
||||
if (size > (u64)shared->total_size << 12)
|
||||
{
|
||||
IFile_Close(&file);
|
||||
return 0xC900464F;
|
||||
}
|
||||
|
||||
// read code
|
||||
res = IFile_Read(&file, &total, (void *)shared->text_addr, size);
|
||||
IFile_Close(&file); // done reading
|
||||
if (R_FAILED(res))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
// decompress
|
||||
if (is_compressed)
|
||||
{
|
||||
lzss_decompress((u8 *)shared->text_addr + size);
|
||||
}
|
||||
|
||||
u16 progver = g_exheader.codesetinfo.flags.remasterversion[0] | (g_exheader.codesetinfo.flags.remasterversion[1] << 8);
|
||||
|
||||
// patch
|
||||
patchCode(progid, progver, (u8 *)shared->text_addr, shared->total_size << 12, g_exheader.codesetinfo.text.codesize, g_exheader.codesetinfo.ro.codesize, g_exheader.codesetinfo.data.codesize, g_exheader.codesetinfo.ro.address, g_exheader.codesetinfo.data.address);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Result loader_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||
{
|
||||
Result res;
|
||||
|
||||
if (prog_handle >> 32 == 0xFFFF0000)
|
||||
{
|
||||
return FSREG_GetProgramInfo(exheader, 1, prog_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = FSREG_CheckHostLoadId(prog_handle);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
//so use PXIPM if FSREG fails OR returns "info", is the second condition a bug?
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
return PXIPM_GetProgramInfo(exheader, prog_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FSREG_GetProgramInfo(exheader, 1, prog_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Result loader_LoadProcess(Handle *process, u64 prog_handle)
|
||||
{
|
||||
Result res;
|
||||
int count;
|
||||
u32 flags;
|
||||
u32 desc;
|
||||
u32 dummy;
|
||||
prog_addrs_t shared_addr;
|
||||
prog_addrs_t vaddr;
|
||||
Handle codeset;
|
||||
CodeSetInfo codesetinfo;
|
||||
u32 data_mem_size;
|
||||
u64 progid;
|
||||
|
||||
// make sure the cached info corrosponds to the current prog_handle
|
||||
if (g_cached_prog_handle != prog_handle)
|
||||
{
|
||||
res = loader_GetProgramInfo(&g_exheader, prog_handle);
|
||||
g_cached_prog_handle = prog_handle;
|
||||
if (res < 0)
|
||||
{
|
||||
g_cached_prog_handle = 0;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// get kernel flags
|
||||
flags = 0;
|
||||
for (count = 0; count < 28; count++)
|
||||
{
|
||||
desc = g_exheader.arm11kernelcaps.descriptors[count];
|
||||
if (0x1FE == desc >> 23)
|
||||
{
|
||||
flags = desc & 0xF00;
|
||||
}
|
||||
}
|
||||
if (flags == 0)
|
||||
{
|
||||
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
|
||||
}
|
||||
|
||||
// allocate process memory
|
||||
vaddr.text_addr = g_exheader.codesetinfo.text.address;
|
||||
vaddr.text_size = (g_exheader.codesetinfo.text.codesize + 4095) >> 12;
|
||||
vaddr.ro_addr = g_exheader.codesetinfo.ro.address;
|
||||
vaddr.ro_size = (g_exheader.codesetinfo.ro.codesize + 4095) >> 12;
|
||||
vaddr.data_addr = g_exheader.codesetinfo.data.address;
|
||||
vaddr.data_size = (g_exheader.codesetinfo.data.codesize + 4095) >> 12;
|
||||
data_mem_size = (g_exheader.codesetinfo.data.codesize + g_exheader.codesetinfo.bsssize + 4095) >> 12;
|
||||
vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size;
|
||||
if ((res = allocate_shared_mem(&shared_addr, &vaddr, flags)) < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
// load code
|
||||
progid = g_exheader.arm11systemlocalcaps.programid;
|
||||
if ((res = load_code(progid, &shared_addr, prog_handle, g_exheader.codesetinfo.flags.flag & 1)) >= 0)
|
||||
{
|
||||
memcpy(&codesetinfo.name, g_exheader.codesetinfo.name, 8);
|
||||
codesetinfo.program_id = progid;
|
||||
codesetinfo.text_addr = vaddr.text_addr;
|
||||
codesetinfo.text_size = vaddr.text_size;
|
||||
codesetinfo.text_size_total = vaddr.text_size;
|
||||
codesetinfo.ro_addr = vaddr.ro_addr;
|
||||
codesetinfo.ro_size = vaddr.ro_size;
|
||||
codesetinfo.ro_size_total = vaddr.ro_size;
|
||||
codesetinfo.rw_addr = vaddr.data_addr;
|
||||
codesetinfo.rw_size = vaddr.data_size;
|
||||
codesetinfo.rw_size_total = data_mem_size;
|
||||
res = svcCreateCodeSet(&codeset, &codesetinfo, (void *)shared_addr.text_addr, (void *)shared_addr.ro_addr, (void *)shared_addr.data_addr);
|
||||
if (res >= 0)
|
||||
{
|
||||
res = svcCreateProcess(process, codeset, g_exheader.arm11kernelcaps.descriptors, count);
|
||||
svcCloseHandle(codeset);
|
||||
if (res >= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svcControlMemory(&dummy, shared_addr.text_addr, 0, shared_addr.total_size << 12, MEMOP_FREE, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result loader_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update)
|
||||
{
|
||||
Result res;
|
||||
u64 prog_id;
|
||||
|
||||
prog_id = title->programId;
|
||||
if (prog_id >> 32 != 0xFFFF0000)
|
||||
{
|
||||
res = FSREG_CheckHostLoadId(prog_id);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
res = PXIPM_RegisterProgram(prog_handle, title, update);
|
||||
if (res < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
if (*prog_handle >> 32 != 0xFFFF0000)
|
||||
{
|
||||
res = FSREG_CheckHostLoadId(*prog_handle);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
}
|
||||
|
||||
if ((title->mediaType != update->mediaType) || (prog_id != update->programId))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
res = FSREG_LoadProgram(prog_handle, title);
|
||||
if (R_SUCCEEDED(res))
|
||||
{
|
||||
if (*prog_handle >> 32 == 0xFFFF0000)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
res = FSREG_CheckHostLoadId(*prog_handle);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result loader_UnregisterProgram(u64 prog_handle)
|
||||
{
|
||||
Result res;
|
||||
|
||||
if (prog_handle >> 32 == 0xFFFF0000)
|
||||
{
|
||||
return FSREG_UnloadProgram(prog_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = FSREG_CheckHostLoadId(prog_handle);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
return PXIPM_UnregisterProgram(prog_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FSREG_UnloadProgram(prog_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_commands(void)
|
||||
{
|
||||
FS_ProgramInfo title;
|
||||
FS_ProgramInfo update;
|
||||
u32* cmdbuf;
|
||||
u16 cmdid;
|
||||
int res;
|
||||
Handle handle;
|
||||
u64 prog_handle;
|
||||
|
||||
cmdbuf = getThreadCommandBuffer();
|
||||
cmdid = cmdbuf[0] >> 16;
|
||||
res = 0;
|
||||
switch (cmdid)
|
||||
{
|
||||
case 1: // LoadProcess
|
||||
{
|
||||
res = loader_LoadProcess(&handle, *(u64 *)&cmdbuf[1]);
|
||||
cmdbuf[0] = 0x10042;
|
||||
cmdbuf[1] = res;
|
||||
cmdbuf[2] = 16;
|
||||
cmdbuf[3] = handle;
|
||||
break;
|
||||
}
|
||||
case 2: // RegisterProgram
|
||||
{
|
||||
memcpy(&title, &cmdbuf[1], sizeof(FS_ProgramInfo));
|
||||
memcpy(&update, &cmdbuf[5], sizeof(FS_ProgramInfo));
|
||||
res = loader_RegisterProgram(&prog_handle, &title, &update);
|
||||
cmdbuf[0] = 0x200C0;
|
||||
cmdbuf[1] = res;
|
||||
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||
break;
|
||||
}
|
||||
case 3: // UnregisterProgram
|
||||
{
|
||||
if (g_cached_prog_handle == prog_handle)
|
||||
{
|
||||
g_cached_prog_handle = 0;
|
||||
}
|
||||
cmdbuf[0] = 0x30040;
|
||||
cmdbuf[1] = loader_UnregisterProgram(*(u64 *)&cmdbuf[1]);
|
||||
break;
|
||||
}
|
||||
case 4: // GetProgramInfo
|
||||
{
|
||||
prog_handle = *(u64 *)&cmdbuf[1];
|
||||
if (prog_handle != g_cached_prog_handle)
|
||||
{
|
||||
res = loader_GetProgramInfo(&g_exheader, prog_handle);
|
||||
if (res >= 0)
|
||||
{
|
||||
g_cached_prog_handle = prog_handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_cached_prog_handle = 0;
|
||||
}
|
||||
}
|
||||
memcpy(&g_ret_buf, &g_exheader, 1024);
|
||||
cmdbuf[0] = 0x40042;
|
||||
cmdbuf[1] = res;
|
||||
cmdbuf[2] = 0x1000002;
|
||||
cmdbuf[3] = (u32) &g_ret_buf;
|
||||
break;
|
||||
}
|
||||
default: // error
|
||||
{
|
||||
cmdbuf[0] = 0x40;
|
||||
cmdbuf[1] = 0xD900182F;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Result should_terminate(int *term_request)
|
||||
{
|
||||
u32 notid;
|
||||
Result ret;
|
||||
|
||||
ret = srvSysReceiveNotification(¬id);
|
||||
if (R_FAILED(ret))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
if (notid == 0x100) // term request
|
||||
{
|
||||
*term_request = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// this is called before main
|
||||
void __appInit()
|
||||
{
|
||||
srvSysInit();
|
||||
fsregInit();
|
||||
fsldrInit();
|
||||
pxipmInit();
|
||||
}
|
||||
|
||||
// this is called after main exits
|
||||
void __appExit()
|
||||
{
|
||||
pxipmExit();
|
||||
fsldrExit();
|
||||
fsregExit();
|
||||
srvSysExit();
|
||||
}
|
||||
|
||||
// stubs for non-needed pre-main functions
|
||||
void __sync_init();
|
||||
void __sync_fini();
|
||||
void __system_initSyscalls();
|
||||
|
||||
void __ctru_exit()
|
||||
{
|
||||
__appExit();
|
||||
__sync_fini();
|
||||
svcExitProcess();
|
||||
}
|
||||
|
||||
void initSystem()
|
||||
{
|
||||
__sync_init();
|
||||
__system_initSyscalls();
|
||||
__appInit();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Result ret;
|
||||
Handle handle;
|
||||
Handle reply_target;
|
||||
Handle *srv_handle;
|
||||
Handle *notification_handle;
|
||||
s32 index;
|
||||
int i;
|
||||
int term_request;
|
||||
u32* cmdbuf;
|
||||
|
||||
ret = 0;
|
||||
|
||||
srv_handle = &g_handles[1];
|
||||
notification_handle = &g_handles[0];
|
||||
|
||||
if (R_FAILED(srvSysRegisterService(srv_handle, "Loader", MAX_SESSIONS)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
if (R_FAILED(srvSysEnableNotification(notification_handle)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
g_active_handles = 2;
|
||||
g_cached_prog_handle = 0;
|
||||
index = 1;
|
||||
|
||||
reply_target = 0;
|
||||
term_request = 0;
|
||||
do
|
||||
{
|
||||
if (reply_target == 0)
|
||||
{
|
||||
cmdbuf = getThreadCommandBuffer();
|
||||
cmdbuf[0] = 0xFFFF0000;
|
||||
}
|
||||
ret = svcReplyAndReceive(&index, g_handles, g_active_handles, reply_target);
|
||||
|
||||
if (R_FAILED(ret))
|
||||
{
|
||||
// check if any handle has been closed
|
||||
if (ret == (int)0xC920181A)
|
||||
{
|
||||
if (index == -1)
|
||||
{
|
||||
for (i = 2; i < MAX_SESSIONS+2; i++)
|
||||
{
|
||||
if (g_handles[i] == reply_target)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
svcCloseHandle(g_handles[index]);
|
||||
g_handles[index] = g_handles[g_active_handles-1];
|
||||
g_active_handles--;
|
||||
reply_target = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// process responses
|
||||
reply_target = 0;
|
||||
switch (index)
|
||||
{
|
||||
case 0: // notification
|
||||
{
|
||||
if (R_FAILED(should_terminate(&term_request)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: // new session
|
||||
{
|
||||
if (R_FAILED(svcAcceptSession(&handle, *srv_handle)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
if (g_active_handles < MAX_SESSIONS+2)
|
||||
{
|
||||
g_handles[g_active_handles] = handle;
|
||||
g_active_handles++;
|
||||
}
|
||||
else
|
||||
{
|
||||
svcCloseHandle(handle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: // session
|
||||
{
|
||||
handle_commands();
|
||||
reply_target = g_handles[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!term_request || g_active_handles != 2);
|
||||
|
||||
srvSysUnregisterService("Loader");
|
||||
svcCloseHandle(*srv_handle);
|
||||
svcCloseHandle(*notification_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
49
injector/source/memory.c
Normal file
49
injector/source/memory.c
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "memory.h"
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size)
|
||||
{
|
||||
u8 *destc = (u8 *)dest;
|
||||
const u8 *srcc = (const u8 *)src;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
destc[i] = srcc[i];
|
||||
}
|
||||
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
{
|
||||
const u8 *buf1c = (const u8 *)buf1,
|
||||
*buf2c = (const u8 *)buf2;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
{
|
||||
int cmp = buf1c[i] - buf2c[i];
|
||||
if(cmp != 0) return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Boyer-Moore Horspool algorithm, adapted from http://www-igm.univ-mlv.fr/~lecroq/string/node18.html#SECTION00180
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
||||
{
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
u32 table[256];
|
||||
|
||||
//Preprocessing
|
||||
for(u32 i = 0; i < 256; i++)
|
||||
table[i] = patternSize;
|
||||
for(u32 i = 0; i < patternSize - 1; i++)
|
||||
table[patternc[i]] = patternSize - i - 1;
|
||||
|
||||
//Searching
|
||||
u32 j = 0;
|
||||
while(j <= size - patternSize)
|
||||
{
|
||||
u8 c = startPos[j + patternSize - 1];
|
||||
if(patternc[patternSize - 1] == c && memcmp(pattern, startPos + j, patternSize - 1) == 0)
|
||||
return startPos + j;
|
||||
j += table[c];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
7
injector/source/memory.h
Normal file
7
injector/source/memory.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size);
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size);
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
|
||||
918
injector/source/patcher.c
Normal file
918
injector/source/patcher.c
Normal file
@@ -0,0 +1,918 @@
|
||||
#include <3ds.h>
|
||||
#include "patcher.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "fsldr.h"
|
||||
#include "ifile.h"
|
||||
#include "CFWInfo.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
static CFWInfo info;
|
||||
|
||||
static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, s32 offset, const void *replace, u32 repSize, u32 count)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
u8 *found = memsearch(start, pattern, size, patSize);
|
||||
|
||||
if(found == NULL) break;
|
||||
|
||||
memcpy(found + offset, replace, repSize);
|
||||
|
||||
u32 at = (u32)(found - start);
|
||||
|
||||
if(at + patSize > size) break;
|
||||
|
||||
size -= at + patSize;
|
||||
start = found + patSize;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
||||
{
|
||||
FS_Path filePath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
||||
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
||||
|
||||
return IFile_Open(file, archiveId, archivePath, filePath, flags);
|
||||
}
|
||||
|
||||
static u32 dirCheck(FS_ArchiveID archiveId, const char *path)
|
||||
{
|
||||
u32 ret;
|
||||
Handle handle;
|
||||
FS_Archive archive;
|
||||
FS_Path dirPath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
||||
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
||||
|
||||
if(R_FAILED(FSLDR_OpenArchive(&archive, archiveId, archivePath))) ret = 1;
|
||||
else
|
||||
{
|
||||
ret = R_SUCCEEDED(FSLDR_OpenDirectory(&handle, archive, dirPath)) ? 0 : 2;
|
||||
if(!ret) FSDIR_Close(handle);
|
||||
FSLDR_CloseArchive(archive);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool openLumaFile(IFile *file, const char *path)
|
||||
{
|
||||
Result res = fileOpen(file, ARCHIVE_SDMC, path, FS_OPEN_READ);
|
||||
|
||||
if(R_SUCCEEDED(res)) return true;
|
||||
|
||||
//Returned if SD is not mounted
|
||||
return (u32)res == 0xC88044AB && R_SUCCEEDED(fileOpen(file, ARCHIVE_NAND_RW, path, FS_OPEN_READ));
|
||||
}
|
||||
|
||||
static u32 checkLumaDir(const char *path)
|
||||
{
|
||||
u32 res = dirCheck(ARCHIVE_SDMC, path);
|
||||
|
||||
if(!res) return ARCHIVE_SDMC;
|
||||
|
||||
return res == 1 && !dirCheck(ARCHIVE_NAND_RW, path) ? ARCHIVE_NAND_RW : 0;
|
||||
}
|
||||
|
||||
static inline void loadCFWInfo(void)
|
||||
{
|
||||
static bool infoLoaded = false;
|
||||
|
||||
if(infoLoaded) return;
|
||||
|
||||
svcGetCFWInfo(&info);
|
||||
|
||||
IFile file;
|
||||
if(LOADERFLAG(ISSAFEMODE)) fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ); //Init SD card if SAFE_MODE is being booted
|
||||
|
||||
infoLoaded = true;
|
||||
}
|
||||
|
||||
static inline bool secureInfoExists(void)
|
||||
{
|
||||
static bool exists = false;
|
||||
|
||||
if(exists) return true;
|
||||
|
||||
IFile file;
|
||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ)))
|
||||
{
|
||||
exists = true;
|
||||
IFile_Close(&file);
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
static inline void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
|
||||
{
|
||||
static const char *paths[] = { "/luma/customversion_sys.txt",
|
||||
"/luma/customversion_emu.txt",
|
||||
"/luma/customversion_emu2.txt",
|
||||
"/luma/customversion_emu3.txt",
|
||||
"/luma/customversion_emu4.txt" };
|
||||
|
||||
IFile file;
|
||||
|
||||
if(!openLumaFile(&file, paths[currentNand])) return;
|
||||
|
||||
u64 fileSize;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > 62) goto exit;
|
||||
|
||||
u8 buf[62];
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
||||
|
||||
static const u8 bom[] = {0xEF, 0xBB, 0xBF};
|
||||
u32 finalSize = 0;
|
||||
|
||||
//Convert from UTF-8 to UTF-16 (Nintendo doesn't support 4-byte UTF-16, so 4-byte UTF-8 is unsupported)
|
||||
for(u32 increase, fileSizeTmp = (u32)fileSize, i = (fileSizeTmp > 2 && memcmp(buf, bom, 3) == 0) ? 3 : 0;
|
||||
i < fileSizeTmp && finalSize < 19; i += increase, finalSize++)
|
||||
{
|
||||
if((buf[i] & 0x80) == 0 && !(buf[i] == 0xA || buf[i] == 0xD))
|
||||
{
|
||||
increase = 1;
|
||||
out[finalSize] = (u16)buf[i];
|
||||
}
|
||||
else if((buf[i] & 0xE0) == 0xC0 && i + 1 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80)
|
||||
{
|
||||
increase = 2;
|
||||
out[finalSize] = (u16)(((buf[i] & 0x1F) << 6) | (buf[i + 1] & 0x3F));
|
||||
}
|
||||
else if((buf[i] & 0xF0) == 0xE0 && i + 2 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80 && (buf[i + 2] & 0xC0) == 0x80)
|
||||
{
|
||||
increase = 3;
|
||||
out[finalSize] = (u16)(((buf[i] & 0xF) << 12) | ((buf[i + 1] & 0x3F) << 6) | (buf[i + 2] & 0x3F));
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
if(finalSize > 0)
|
||||
{
|
||||
if(finalSize > 5 && finalSize < 19) out[finalSize++] = 0;
|
||||
*verStringSize = finalSize * 2;
|
||||
}
|
||||
|
||||
exit:
|
||||
IFile_Close(&file);
|
||||
}
|
||||
|
||||
static inline u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
||||
{
|
||||
/* HANS:
|
||||
Look for error code which is known to be stored near cfg:u handle
|
||||
this way we can find the right candidate
|
||||
(handle should also be stored right after end of candidate function) */
|
||||
|
||||
u32 n = 0,
|
||||
possible[24];
|
||||
|
||||
for(u8 *pos = code + 16; n < 24 && pos <= code + size - 16; pos += 4)
|
||||
{
|
||||
if(*(u32 *)pos != 0xD8A103F9) continue;
|
||||
|
||||
for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
|
||||
if(*l <= 0x10000000) possible[n++] = *l;
|
||||
}
|
||||
|
||||
if(!n) return NULL;
|
||||
|
||||
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos <= code + size - 12; CFGU_GetConfigInfoBlk2_endPos += 4)
|
||||
{
|
||||
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
||||
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
if(cmp[0] != 0xE8BD8010 || cmp[1] != 0x00010082) continue;
|
||||
|
||||
for(u32 i = 0; i < n; i++)
|
||||
if(possible[i] == cmp[2])
|
||||
{
|
||||
*CFGUHandleOffset = cmp[2];
|
||||
|
||||
return CFGU_GetConfigInfoBlk2_endPos;
|
||||
}
|
||||
|
||||
CFGU_GetConfigInfoBlk2_endPos += 4;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
|
||||
{
|
||||
u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind)
|
||||
|
||||
for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
|
||||
*((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D; CFGU_GetConfigInfoBlk2_startPos -= 4)
|
||||
if(CFGU_GetConfigInfoBlk2_startPos < code + 4) return false;
|
||||
|
||||
for(u8 *languageBlkIdPos = code; languageBlkIdPos <= code + size - 4; languageBlkIdPos += 4)
|
||||
{
|
||||
if(*(u32 *)languageBlkIdPos != 0xA0002) continue;
|
||||
|
||||
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
||||
{
|
||||
if(instr[3] != 0xEB) continue; //We're looking for BL
|
||||
|
||||
u8 *calledFunction = instr;
|
||||
u32 i = 0;
|
||||
|
||||
do
|
||||
{
|
||||
u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
|
||||
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
|
||||
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
|
||||
|
||||
calledFunction += offset;
|
||||
|
||||
if(calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos)
|
||||
{
|
||||
*((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
|
||||
*(u32 *)instr = 0xE5CD0000; //bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
|
||||
*((u32 *)instr + 1) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
|
||||
|
||||
//We're done
|
||||
return true;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
while(i < 2 && calledFunction[3] == 0xEA);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
|
||||
{
|
||||
for(u8 *cmdPos = code; cmdPos <= code + size - 28; cmdPos += 4)
|
||||
{
|
||||
u32 *cmp = (u32 *)cmdPos;
|
||||
|
||||
if(*cmp != 0xE3A00802) continue;
|
||||
|
||||
for(u32 i = 1; i < 3; i++)
|
||||
if((*(cmp - i) & 0xFFFF0FFF) == 0xEE1D0F70 && *((u16 *)cmdPos + 5) == 0xE59F &&
|
||||
*(u32 *)(cmdPos + 16 + *((u16 *)cmdPos + 4)) == CFGUHandleOffset)
|
||||
{
|
||||
cmp[3] = 0xE3A00000 | regionId; //mov r0, =regionId
|
||||
cmp[4] = 0xE5C40008; //strb r0, [r4, #8]
|
||||
cmp[5] = 0xE3A00000; //mov r0, #0 (result code)
|
||||
cmp[6] = 0xE5840004; //str r0, [r4, #4]
|
||||
|
||||
//The remaining, not patched, function code will do the rest for us
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u32 findFunctionStart(u8 *code, u32 pos)
|
||||
{
|
||||
while(pos >= 4)
|
||||
{
|
||||
pos -= 4;
|
||||
if(*(u16 *)(code + pos + 2) == 0xE92D) return pos;
|
||||
}
|
||||
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static inline bool findLayeredFsSymbols(u8 *code, u32 size, u32 *fsMountArchive, u32 *fsRegisterArchive, u32 *fsTryOpenFile, u32 *fsOpenFileDirectly)
|
||||
{
|
||||
u32 found = 0,
|
||||
*temp = NULL;
|
||||
|
||||
for(u32 addr = 0; addr <= size - 4; addr += 4)
|
||||
{
|
||||
u32 *addr32 = (u32 *)(code + addr);
|
||||
|
||||
switch(*addr32)
|
||||
{
|
||||
case 0xE5970010:
|
||||
if(addr <= size - 12 && *fsMountArchive == 0xFFFFFFFF && addr32[1] == 0xE1CD20D8 && (addr32[2] & 0xFFFFFF) == 0x008D0000) temp = fsMountArchive;
|
||||
break;
|
||||
case 0xE24DD028:
|
||||
if(addr <= size - 16 && *fsMountArchive == 0xFFFFFFFF && addr32[1] == 0xE1A04000 && addr32[2] == 0xE59F60A8 && addr32[3] == 0xE3A0C001) temp = fsMountArchive;
|
||||
break;
|
||||
case 0xE3500008:
|
||||
if(addr <= size - 12 && *fsRegisterArchive == 0xFFFFFFFF && (addr32[1] & 0xFFF00FF0) == 0xE1800400 && (addr32[2] & 0xFFF00FF0) == 0xE1800FC0) temp = fsRegisterArchive;
|
||||
break;
|
||||
case 0xE351003A:
|
||||
if(addr <= size - 0x40 && *fsTryOpenFile == 0xFFFFFFFF && addr32[1] == 0x1AFFFFFC && addr32[0xD] == 0xE590C000 && addr32[0xF] == 0xE12FFF3C) temp = fsTryOpenFile;
|
||||
break;
|
||||
case 0x08030204:
|
||||
if(*fsOpenFileDirectly == 0xFFFFFFFF) temp = fsOpenFileDirectly;
|
||||
break;
|
||||
}
|
||||
|
||||
if(temp != NULL)
|
||||
{
|
||||
*temp = findFunctionStart(code, addr);
|
||||
|
||||
if(*temp != 0xFFFFFFFF)
|
||||
{
|
||||
found++;
|
||||
if(found == 4) break;
|
||||
}
|
||||
|
||||
temp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return found == 4;
|
||||
}
|
||||
|
||||
static inline bool findLayeredFsPayloadOffset(u8 *code, u32 size, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress, u32 *payloadOffset, u32 *pathOffset, u32 *pathAddress)
|
||||
{
|
||||
u32 roundedTextSize = ((size + 4095) & 0xFFFFF000),
|
||||
roundedRoSize = ((roSize + 4095) & 0xFFFFF000),
|
||||
roundedDataSize = ((dataSize + 4095) & 0xFFFFF000);
|
||||
|
||||
//First check for sufficient padding at the end of the .text segment
|
||||
if(roundedTextSize - size >= romfsredir_bin_size) *payloadOffset = size;
|
||||
else
|
||||
{
|
||||
//If there isn't enough padding look for the "throwFatalError" function to replace
|
||||
u32 svcConnectToPort = 0xFFFFFFFF;
|
||||
|
||||
for(u32 addr = 4; svcConnectToPort == 0xFFFFFFFF && addr <= size - 4; addr += 4)
|
||||
{
|
||||
if(*(u32 *)(code + addr) == 0xEF00002D)
|
||||
svcConnectToPort = addr - 4;
|
||||
}
|
||||
|
||||
if(svcConnectToPort != 0xFFFFFFFF)
|
||||
{
|
||||
u32 func = 0xFFFFFFFF;
|
||||
|
||||
for(u32 i = 4; func == 0xFFFFFFFF && i <= size - 4; i += 4)
|
||||
{
|
||||
if(*(u32 *)(code + i) != MAKE_BRANCH_LINK(i, svcConnectToPort)) continue;
|
||||
|
||||
func = findFunctionStart(code, i);
|
||||
|
||||
for(u32 pos = func + 4; func != 0xFFFFFFFF && pos <= size - 4 && *(u16 *)(code + pos + 2) != 0xE92D; pos += 4)
|
||||
if(*(u32 *)(code + pos) == 0xE200167E) func = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if(func != 0xFFFFFFFF)
|
||||
*payloadOffset = func;
|
||||
}
|
||||
}
|
||||
|
||||
if(roundedRoSize - roSize >= 39)
|
||||
{
|
||||
*pathOffset = roundedTextSize + roSize;
|
||||
*pathAddress = roAddress + roSize;
|
||||
}
|
||||
else if(roundedDataSize - dataSize >= 39)
|
||||
{
|
||||
*pathOffset = roundedTextSize + roundedRoSize + dataSize;
|
||||
*pathAddress = dataAddress + dataSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 strSpace = 0xFFFFFFFF;
|
||||
|
||||
for(u32 addr = 0; strSpace == 0xFFFFFFFF && addr <= size - 4; addr += 4)
|
||||
{
|
||||
if(*(u32 *)(code + addr) == 0xE3A00B42)
|
||||
strSpace = findFunctionStart(code, addr);
|
||||
}
|
||||
|
||||
if(strSpace != 0xFFFFFFFF)
|
||||
{
|
||||
*pathOffset = strSpace;
|
||||
*pathAddress = 0x100000 + strSpace;
|
||||
}
|
||||
}
|
||||
|
||||
return *payloadOffset != 0 && *pathOffset != 0;
|
||||
}
|
||||
|
||||
static inline bool applyCodeIpsPatch(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/code.ips"
|
||||
If it exists it should be an IPS format patch */
|
||||
|
||||
char path[] = "/luma/titles/0000000000000000/code.ips";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
IFile file;
|
||||
|
||||
if(!openLumaFile(&file, path)) return true;
|
||||
|
||||
bool ret = false;
|
||||
u8 buffer[5];
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buffer, 5)) || total != 5 || memcmp(buffer, "PATCH", 5) != 0) goto exit;
|
||||
|
||||
while(R_SUCCEEDED(IFile_Read(&file, &total, buffer, 3)) && total == 3)
|
||||
{
|
||||
if(memcmp(buffer, "EOF", 3) == 0)
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 offset = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buffer, 2)) || total != 2) break;
|
||||
|
||||
u32 patchSize = (buffer[0] << 8) | buffer[1];
|
||||
|
||||
if(!patchSize)
|
||||
{
|
||||
if(R_FAILED(IFile_Read(&file, &total, buffer, 2)) || total != 2) break;
|
||||
|
||||
u32 rleSize = (buffer[0] << 8) | buffer[1];
|
||||
|
||||
if(offset + rleSize > size) break;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buffer, 1)) || total != 1) break;
|
||||
|
||||
for(u32 i = 0; i < rleSize; i++)
|
||||
code[offset + i] = buffer[0];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(offset + patchSize > size) break;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, code + offset, patchSize)) || total != patchSize) break;
|
||||
}
|
||||
|
||||
exit:
|
||||
IFile_Close(&file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/code.bin"
|
||||
If it exists it should be a decrypted and decompressed binary code file */
|
||||
|
||||
char path[] = "/luma/titles/0000000000000000/code.bin";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
IFile file;
|
||||
|
||||
if(!openLumaFile(&file, path)) return true;
|
||||
|
||||
bool ret;
|
||||
u64 fileSize;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = false;
|
||||
else
|
||||
{
|
||||
u64 total;
|
||||
|
||||
ret = R_SUCCEEDED(IFile_Read(&file, &total, code, fileSize)) && total == fileSize;
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
{
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/locale.txt"
|
||||
If it exists it should contain, for example, "EUR IT" */
|
||||
|
||||
char path[] = "/luma/titles/0000000000000000/locale.txt";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
IFile file;
|
||||
|
||||
if(!openLumaFile(&file, path)) return true;
|
||||
|
||||
bool ret = false;
|
||||
u64 fileSize;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8) goto exit;
|
||||
|
||||
char buf[8];
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
||||
|
||||
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"},
|
||||
*languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
|
||||
|
||||
u32 i;
|
||||
|
||||
for(i = 0; i < sizeof(regions) / sizeof(char *); i++)
|
||||
{
|
||||
if(memcmp(buf, regions[i], 3) == 0)
|
||||
{
|
||||
*regionId = (u8)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i != sizeof(regions) / sizeof(char *))
|
||||
{
|
||||
for(i = 0; i < sizeof(languages) / sizeof(char *); i++)
|
||||
{
|
||||
if(memcmp(buf + 4, languages[i], 2) == 0)
|
||||
{
|
||||
*languageId = (u8)i;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
IFile_Close(&file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool patchLayeredFs(u64 progId, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress)
|
||||
{
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/romfs"
|
||||
If it exists it should be a folder containing ROMFS files */
|
||||
|
||||
char path[] = "/luma/titles/0000000000000000/romfs";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
u32 archiveId = checkLumaDir(path);
|
||||
|
||||
if(!archiveId) return true;
|
||||
|
||||
u32 fsMountArchive = 0xFFFFFFFF,
|
||||
fsRegisterArchive = 0xFFFFFFFF,
|
||||
fsTryOpenFile = 0xFFFFFFFF,
|
||||
fsOpenFileDirectly = 0xFFFFFFFF,
|
||||
payloadOffset = 0,
|
||||
pathOffset = 0,
|
||||
pathAddress;
|
||||
|
||||
if(!findLayeredFsSymbols(code, textSize, &fsMountArchive, &fsRegisterArchive, &fsTryOpenFile, &fsOpenFileDirectly) ||
|
||||
!findLayeredFsPayloadOffset(code, textSize, roSize, dataSize, roAddress, dataAddress, &payloadOffset, &pathOffset, &pathAddress)) return false;
|
||||
|
||||
static const char *updateRomFsMounts[] = { "rom2:",
|
||||
"rex:",
|
||||
"patch:",
|
||||
"ext:",
|
||||
"rom:" };
|
||||
u32 updateRomFsIndex;
|
||||
|
||||
//Locate update RomFSes
|
||||
for(updateRomFsIndex = 0; updateRomFsIndex < sizeof(updateRomFsMounts) / sizeof(char *) - 1; updateRomFsIndex++)
|
||||
{
|
||||
u32 patternSize = strnlen(updateRomFsMounts[updateRomFsIndex], 255);
|
||||
u8 temp[7];
|
||||
temp[0] = 0;
|
||||
memcpy(temp + 1, updateRomFsMounts[updateRomFsIndex], patternSize);
|
||||
if(memsearch(code, temp, size, patternSize + 1) != NULL) break;
|
||||
}
|
||||
|
||||
//Setup the payload
|
||||
u8 *payload = code + payloadOffset;
|
||||
memcpy(payload, romfsredir_bin, romfsredir_bin_size);
|
||||
|
||||
//Insert symbols in the payload
|
||||
u32 *payload32 = (u32 *)payload;
|
||||
for(u32 i = 0; i < romfsredir_bin_size / 4; i++)
|
||||
{
|
||||
switch(payload32[i])
|
||||
{
|
||||
case 0xdead0000:
|
||||
payload32[i] = *(u32 *)(code + fsOpenFileDirectly);
|
||||
break;
|
||||
case 0xdead0001:
|
||||
payload32[i] = MAKE_BRANCH(payloadOffset + i * 4, fsOpenFileDirectly + 4);
|
||||
break;
|
||||
case 0xdead0002:
|
||||
payload32[i] = *(u32 *)(code + fsTryOpenFile);
|
||||
break;
|
||||
case 0xdead0003:
|
||||
payload32[i] = MAKE_BRANCH(payloadOffset + i * 4, fsTryOpenFile + 4);
|
||||
break;
|
||||
case 0xdead0004:
|
||||
payload32[i] = pathAddress;
|
||||
break;
|
||||
case 0xdead0005:
|
||||
payload32[i] = 0x100000 + fsMountArchive;
|
||||
break;
|
||||
case 0xdead0006:
|
||||
payload32[i] = 0x100000 + fsRegisterArchive;
|
||||
break;
|
||||
case 0xdead0007:
|
||||
payload32[i] = archiveId;
|
||||
break;
|
||||
case 0xdead0008:
|
||||
memcpy(payload32 + i, updateRomFsMounts[updateRomFsIndex], 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(code + pathOffset, "lf:", 3);
|
||||
memcpy(code + pathOffset + 3, path, sizeof(path));
|
||||
|
||||
//Place the hooks
|
||||
*(u32 *)(code + fsOpenFileDirectly) = MAKE_BRANCH(fsOpenFileDirectly, payloadOffset);
|
||||
*(u32 *)(code + fsTryOpenFile) = MAKE_BRANCH(fsTryOpenFile, payloadOffset + 12);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress)
|
||||
{
|
||||
loadCFWInfo();
|
||||
|
||||
if(((progId == 0x0004003000008F02LL || //USA Home Menu
|
||||
progId == 0x0004003000008202LL || //JPN Home Menu
|
||||
progId == 0x0004003000009802LL) //EUR Home Menu
|
||||
&& progVer > 4) ||
|
||||
(progId == 0x000400300000A902LL //KOR Home Menu
|
||||
&& progVer > 0) ||
|
||||
progId == 0x000400300000A102LL || //CHN Home Menu
|
||||
progId == 0x000400300000B102LL) //TWN Home Menu
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x0A, 0x0C, 0x00, 0x10
|
||||
},
|
||||
patch[] = {
|
||||
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||
};
|
||||
|
||||
//Patch SMDH region checks
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), -31,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000003202LL) //FRIENDS
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x42, 0xE0, 0x1E, 0xFF
|
||||
};
|
||||
|
||||
u8 mostRecentFpdVer = 10;
|
||||
|
||||
u8 *off = memsearch(code, pattern, textSize, sizeof(pattern));
|
||||
|
||||
if(off == NULL) goto error;
|
||||
|
||||
//Allow online access to work with old friends modules
|
||||
if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
||||
}
|
||||
|
||||
else if((progId == 0x0004001000021000LL || //USA MSET
|
||||
progId == 0x0004001000020000LL || //JPN MSET
|
||||
progId == 0x0004001000022000LL || //EUR MSET
|
||||
progId == 0x0004001000026000LL || //CHN MSET
|
||||
progId == 0x0004001000027000LL || //KOR MSET
|
||||
progId == 0x0004001000028000LL) //TWN MSET
|
||||
&& CONFIG(PATCHVERSTRING))
|
||||
{
|
||||
static const u16 pattern[] = u"Ve";
|
||||
static u16 *patch;
|
||||
u32 patchSize = 0,
|
||||
currentNand = BOOTCFG_NAND;
|
||||
|
||||
u16 customVerString[19];
|
||||
loadCustomVerString(customVerString, &patchSize, currentNand);
|
||||
|
||||
if(patchSize != 0) patch = customVerString;
|
||||
else
|
||||
{
|
||||
patchSize = 8;
|
||||
u32 currentFirm = BOOTCFG_FIRM;
|
||||
|
||||
static u16 *verStringsNands[] = { u" Sys",
|
||||
u" Emu",
|
||||
u"Emu2",
|
||||
u"Emu3",
|
||||
u"Emu4" },
|
||||
|
||||
*verStringsEmuSys[] = { u"EmuS",
|
||||
u"Em2S",
|
||||
u"Em3S",
|
||||
u"Em4S" },
|
||||
|
||||
*verStringsSysEmu[] = { u"SysE",
|
||||
u"SyE2",
|
||||
u"SyE3",
|
||||
u"SyE4" };
|
||||
|
||||
patch = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
|
||||
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
|
||||
}
|
||||
|
||||
//Patch Ver. string
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern) - 2, 0,
|
||||
patch,
|
||||
patchSize, 1
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000008002LL) //NS
|
||||
{
|
||||
if(progVer > 4)
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x0C, 0x18, 0xE1, 0xD8
|
||||
},
|
||||
patch[] = {
|
||||
0x0B, 0x18, 0x21, 0xC8
|
||||
};
|
||||
|
||||
//Disable updates from foreign carts (makes carts region-free)
|
||||
u32 ret = patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), 0,
|
||||
patch,
|
||||
sizeof(patch), 2
|
||||
);
|
||||
|
||||
if(ret == 0 || (ret == 1 && progVer > 0xB)) goto error;
|
||||
}
|
||||
|
||||
if(LOADERFLAG(ISN3DS))
|
||||
{
|
||||
u32 cpuSetting = MULTICONFIG(NEWCPU);
|
||||
|
||||
if(cpuSetting != 0)
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x0C, 0x00, 0x94, 0x15
|
||||
};
|
||||
|
||||
u32 *off = (u32 *)memsearch(code, pattern, textSize, sizeof(pattern));
|
||||
|
||||
if(off == NULL) goto error;
|
||||
|
||||
//Patch N3DS CPU Clock and L2 cache setting
|
||||
*(off - 4) = *(off - 3);
|
||||
*(off - 3) = *(off - 1);
|
||||
memcpy(off - 1, off, 16);
|
||||
*(off + 3) = 0xE3800000 | cpuSetting;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000001702LL) //CFG
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x06, 0x46, 0x10, 0x48
|
||||
},
|
||||
patch[] = {
|
||||
0x00, 0x26
|
||||
};
|
||||
|
||||
//Disable SecureInfo signature check
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), 0,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) goto error;
|
||||
|
||||
if(secureInfoExists())
|
||||
{
|
||||
static const u16 pattern[] = u"Sec",
|
||||
patch[] = u"C";
|
||||
|
||||
//Use SecureInfo_C
|
||||
if(patchMemory(code + ((textSize + 4095) & 0xFFFFF000), roSize,
|
||||
pattern,
|
||||
sizeof(pattern) - 2, 22,
|
||||
patch,
|
||||
sizeof(patch) - 2, 2
|
||||
) != 2) goto error;
|
||||
}
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000003702LL && progVer > 0) //RO
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x20, 0xA0, 0xE1, 0x8B
|
||||
},
|
||||
pattern2[] = {
|
||||
0xE1, 0x30, 0x40, 0x2D
|
||||
},
|
||||
pattern3[] = {
|
||||
0x2D, 0xE9, 0x01, 0x70
|
||||
},
|
||||
patch[] = {
|
||||
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 //mov r0, #0; bx lr
|
||||
};
|
||||
|
||||
//Disable CRR0 signature (RSA2048 with SHA256) check and CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), -9,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
) ||
|
||||
!patchMemory(code, textSize,
|
||||
pattern2,
|
||||
sizeof(pattern2), 1,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
) ||
|
||||
!patchMemory(code, textSize,
|
||||
pattern3,
|
||||
sizeof(pattern3), -2,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004003000008A02LL && CONFIG(ENABLEEXCEPTIONHANDLERS) && !CONFIG(PATCHUNITINFO)) //ErrDisp
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x00, 0xD0, 0xE5, 0xDB
|
||||
},
|
||||
pattern2[] = {
|
||||
0x14, 0x00, 0xD0, 0xE5, 0x01
|
||||
},
|
||||
patch[] = {
|
||||
0x00, 0x00, 0xA0, 0xE3
|
||||
};
|
||||
|
||||
//Patch UNITINFO checks to make ErrDisp more verbose
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), -1,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
) ||
|
||||
patchMemory(code, textSize,
|
||||
pattern2,
|
||||
sizeof(pattern2), 0,
|
||||
patch,
|
||||
sizeof(patch), 3
|
||||
) != 3) goto error;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000002802LL) //DLP
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x0C, 0xAC, 0xC0, 0xD8
|
||||
},
|
||||
patch[] = {
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
//Patch DLP region checks
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), 0,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
if(CONFIG(PATCHGAMES))
|
||||
{
|
||||
if(!loadTitleCodeSection(progId, code, size) ||
|
||||
!applyCodeIpsPatch(progId, code, size)) goto error;
|
||||
|
||||
if((u32)((progId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)
|
||||
{
|
||||
u8 regionId = 0xFF,
|
||||
languageId;
|
||||
|
||||
if(!loadTitleLocaleConfig(progId, ®ionId, &languageId) ||
|
||||
!patchLayeredFs(progId, code, size, textSize, roSize, dataSize, roAddress, dataAddress)) goto error;
|
||||
|
||||
if(regionId != 0xFF)
|
||||
{
|
||||
u32 CFGUHandleOffset;
|
||||
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, textSize, &CFGUHandleOffset);
|
||||
|
||||
if(CFGU_GetConfigInfoBlk2_endPos == NULL ||
|
||||
!patchCfgGetLanguage(code, textSize, languageId, CFGU_GetConfigInfoBlk2_endPos)) goto error;
|
||||
|
||||
patchCfgGetRegion(code, textSize, regionId, CFGUHandleOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
while(true);
|
||||
}
|
||||
45
injector/source/patcher.h
Normal file
45
injector/source/patcher.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
#define MAKE_BRANCH(src,dst) (0xEA000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
#define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
|
||||
#define CONFIG(a) (((info.config >> (a + 17)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((info.config >> (a * 2 + 7)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((info.config >> a) & b)
|
||||
#define LOADERFLAG(a) ((info.flags >> (a + 4)) & 1) != 0
|
||||
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(6, 1)
|
||||
|
||||
enum multiOptions
|
||||
{
|
||||
DEFAULTEMU = 0,
|
||||
BRIGHTNESS,
|
||||
SPLASH,
|
||||
PIN,
|
||||
NEWCPU
|
||||
};
|
||||
|
||||
enum singleOptions
|
||||
{
|
||||
AUTOBOOTEMU = 0,
|
||||
USEEMUFIRM,
|
||||
LOADEXTFIRMSANDMODULES,
|
||||
PATCHGAMES,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHACCESS,
|
||||
PATCHUNITINFO,
|
||||
ENABLEEXCEPTIONHANDLERS
|
||||
};
|
||||
|
||||
enum flags
|
||||
{
|
||||
ISN3DS = 0,
|
||||
ISSAFEMODE
|
||||
};
|
||||
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress);
|
||||
74
injector/source/pxipm.c
Normal file
74
injector/source/pxipm.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <3ds.h>
|
||||
#include <string.h>
|
||||
#include "pxipm.h"
|
||||
#include "srvsys.h"
|
||||
|
||||
static Handle pxipmHandle;
|
||||
static int pxipmRefCount;
|
||||
|
||||
Result pxipmInit(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
if (AtomicPostIncrement(&pxipmRefCount)) return 0;
|
||||
|
||||
ret = srvSysGetServiceHandle(&pxipmHandle, "PxiPM");
|
||||
|
||||
if (R_FAILED(ret)) AtomicDecrement(&pxipmRefCount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pxipmExit(void)
|
||||
{
|
||||
if (AtomicDecrement(&pxipmRefCount)) return;
|
||||
svcCloseHandle(pxipmHandle);
|
||||
}
|
||||
|
||||
Result PXIPM_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x2,8,0); // 0x20200
|
||||
memcpy(&cmdbuf[1], &title->programId, sizeof(u64));
|
||||
*(u8 *)&cmdbuf[3] = title->mediaType;
|
||||
memcpy(((u8 *)&cmdbuf[3])+1, &title->padding, 7);
|
||||
memcpy(&cmdbuf[5], &update->programId, sizeof(u64));
|
||||
*(u8 *)&cmdbuf[7] = update->mediaType;
|
||||
memcpy(((u8 *)&cmdbuf[7])+1, &update->padding, 7);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||
*prog_handle = *(u64*)&cmdbuf[2];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result PXIPM_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x1,2,2); // 0x10082
|
||||
cmdbuf[1] = (u32) (prog_handle);
|
||||
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||
cmdbuf[3] = (0x400 << 8) | 0x4;
|
||||
cmdbuf[4] = (u32) exheader;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result PXIPM_UnregisterProgram(u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x3,2,0); // 0x30080
|
||||
cmdbuf[1] = (u32) (prog_handle);
|
||||
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
10
injector/source/pxipm.h
Normal file
10
injector/source/pxipm.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include "exheader.h"
|
||||
|
||||
Result pxipmInit(void);
|
||||
void pxipmExit(void);
|
||||
Result PXIPM_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update);
|
||||
Result PXIPM_GetProgramInfo(exheader_header *exheader, u64 prog_handle);
|
||||
Result PXIPM_UnregisterProgram(u64 prog_handle);
|
||||
154
injector/source/srvsys.c
Normal file
154
injector/source/srvsys.c
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <3ds.h>
|
||||
#include <string.h>
|
||||
#include "srvsys.h"
|
||||
|
||||
static Handle srvHandle;
|
||||
static int srvRefCount;
|
||||
static RecursiveLock initLock;
|
||||
static int initLockinit = 0;
|
||||
|
||||
Result srvSysInit()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
if (!initLockinit)
|
||||
{
|
||||
RecursiveLock_Init(&initLock);
|
||||
}
|
||||
|
||||
RecursiveLock_Lock(&initLock);
|
||||
|
||||
if (srvRefCount > 0)
|
||||
{
|
||||
RecursiveLock_Unlock(&initLock);
|
||||
return MAKERESULT(RL_INFO, RS_NOP, 25, RD_ALREADY_INITIALIZED);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
rc = svcConnectToPort(&srvHandle, "srv:");
|
||||
if (R_LEVEL(rc) != RL_PERMANENT ||
|
||||
R_SUMMARY(rc) != RS_NOTFOUND ||
|
||||
R_DESCRIPTION(rc) != RD_NOT_FOUND
|
||||
) break;
|
||||
svcSleepThread(500000);
|
||||
}
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = srvSysRegisterClient();
|
||||
srvRefCount++;
|
||||
}
|
||||
|
||||
RecursiveLock_Unlock(&initLock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result srvSysRegisterClient(void)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x1,0,2); // 0x10002
|
||||
cmdbuf[1] = IPC_Desc_CurProcessHandle();
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysExit()
|
||||
{
|
||||
Result rc;
|
||||
RecursiveLock_Lock(&initLock);
|
||||
|
||||
if (srvRefCount > 1)
|
||||
{
|
||||
srvRefCount--;
|
||||
RecursiveLock_Unlock(&initLock);
|
||||
return MAKERESULT(RL_INFO, RS_NOP, 25, RD_BUSY);
|
||||
}
|
||||
|
||||
if (srvHandle != 0) svcCloseHandle(srvHandle);
|
||||
else svcBreak(USERBREAK_ASSERT);
|
||||
rc = (Result)srvHandle; // yeah, I think this is a benign bug
|
||||
srvHandle = 0;
|
||||
srvRefCount--;
|
||||
RecursiveLock_Unlock(&initLock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result srvSysGetServiceHandle(Handle* out, const char* name)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x5,4,0); // 0x50100
|
||||
strncpy((char*) &cmdbuf[1], name,8);
|
||||
cmdbuf[3] = strlen(name);
|
||||
cmdbuf[4] = 0x0;
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysEnableNotification(Handle* semaphoreOut)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x2,0,0);
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
if(semaphoreOut) *semaphoreOut = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysReceiveNotification(u32* notificationIdOut)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0xB,0,0); // 0xB0000
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
if(notificationIdOut) *notificationIdOut = cmdbuf[2];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysRegisterService(Handle* out, const char* name, int maxSessions)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x3,4,0); // 0x30100
|
||||
strncpy((char*) &cmdbuf[1], name,8);
|
||||
cmdbuf[3] = strlen(name);
|
||||
cmdbuf[4] = maxSessions;
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysUnregisterService(const char* name)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x4,3,0); // 0x400C0
|
||||
strncpy((char*) &cmdbuf[1], name,8);
|
||||
cmdbuf[3] = strlen(name);
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
47
injector/source/srvsys.h
Normal file
47
injector/source/srvsys.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file srv.h
|
||||
* @brief Service API.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/// Initializes the service API.
|
||||
Result srvSysInit(void);
|
||||
|
||||
/// Exits the service API.
|
||||
Result srvSysExit(void);
|
||||
|
||||
/**
|
||||
* @brief Retrieves a service handle, retrieving from the environment handle list if possible.
|
||||
* @param out Pointer to write the handle to.
|
||||
* @param name Name of the service.
|
||||
*/
|
||||
Result srvSysGetServiceHandle(Handle* out, const char* name);
|
||||
|
||||
/// Registers the current process as a client to the service API.
|
||||
Result srvSysRegisterClient(void);
|
||||
|
||||
/**
|
||||
* @brief Enables service notificatios, returning a notification semaphore.
|
||||
* @param semaphoreOut Pointer to output the notification semaphore to.
|
||||
*/
|
||||
Result srvSysEnableNotification(Handle* semaphoreOut);
|
||||
|
||||
/**
|
||||
* @brief Receives a notification.
|
||||
* @param notificationIdOut Pointer to output the ID of the received notification to.
|
||||
*/
|
||||
Result srvSysReceiveNotification(u32* notificationIdOut);
|
||||
|
||||
/**
|
||||
* @brief Registers the current process as a service.
|
||||
* @param out Pointer to write the service handle to.
|
||||
* @param name Name of the service.
|
||||
* @param maxSessions Maximum number of sessions the service can handle.
|
||||
*/
|
||||
Result srvSysRegisterService(Handle* out, const char* name, int maxSessions);
|
||||
|
||||
/**
|
||||
* @brief Unregisters the current process as a service.
|
||||
* @param name Name of the service.
|
||||
*/
|
||||
Result srvSysUnregisterService(const char* name);
|
||||
20
injector/source/strings.c
Normal file
20
injector/source/strings.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "strings.h"
|
||||
|
||||
size_t strnlen(const char *string, size_t maxlen)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
for(size = 0; *string && size < maxlen; string++, size++);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void progIdToStr(char *strEnd, u64 progId)
|
||||
{
|
||||
while(progId > 0)
|
||||
{
|
||||
static const char hexDigits[] = "0123456789ABCDEF";
|
||||
*strEnd-- = hexDigits[(u32)(progId & 0xF)];
|
||||
progId >>= 4;
|
||||
}
|
||||
}
|
||||
6
injector/source/strings.h
Normal file
6
injector/source/strings.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
size_t strnlen(const char *string, size_t maxlen);
|
||||
void progIdToStr(char *strEnd, u64 progId);
|
||||
14
linker.ld
14
linker.ld
@@ -1,11 +1,15 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x23F00000;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(4); }
|
||||
.bss : ALIGN(8) { __bss_start = .; *(.bss* COMMON); . = ALIGN(8); __bss_end = .; }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
146
loader/Makefile
146
loader/Makefile
@@ -1,134 +1,44 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# SPECS is the directory containing the important build and link files
|
||||
#---------------------------------------------------------------------------------
|
||||
export TARGET := $(shell basename $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source source/fatfs source/fatfs/sdmmc
|
||||
DATA := data
|
||||
INCLUDES := include source/fatfs source/fatfs/sdmmc
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# Setup some defines
|
||||
#---------------------------------------------------------------------------------
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
dir_out := ../$(dir_build)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -mthumb -mthumb-interwork
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -mthumb $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
CFLAGS := -g -Wall -O2\
|
||||
-march=armv5te -mtune=arm946e-s\
|
||||
-ffast-math -Wno-main -std=c99\
|
||||
$(ARCH)
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
CFLAGS += $(INCLUDE) -DARM9
|
||||
.PHONY: all
|
||||
all: $(dir_out)/$(name).bin
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -nostartfiles -g --specs=../stub.specs $(ARCH) -Wl,-Map,$(TARGET).map
|
||||
|
||||
LIBS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(OUTPUT).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT).bin : $(OUTPUT).elf
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin: %.elf
|
||||
@$(OBJCOPY) -O binary $< $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
-include $(DEPENDS)
|
||||
$(dir_build)/memory.o: CFLAGS += -O3
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
|
||||
14
loader/linker.ld
Normal file
14
loader/linker.ld
Normal file
@@ -0,0 +1,14 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x27FFE000;
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define HID_PAD (*(vu16 *)0x10146000 ^ 0xFFF)
|
||||
#define BUTTON_B (1 << 1)
|
||||
#define BUTTON_X (1 << 10)
|
||||
#define BUTTON_Y (1 << 11)
|
||||
#define BUTTON_SELECT (1 << 2)
|
||||
#define BUTTON_START (1 << 3)
|
||||
#define BUTTON_RIGHT (1 << 4)
|
||||
#define BUTTON_LEFT (1 << 5)
|
||||
#define BUTTON_UP (1 << 6)
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
27
loader/source/cache.h
Normal file
27
loader/source/cache.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void flushCaches(void);
|
||||
56
loader/source/cache.s
Normal file
56
loader/source/cache.s
Normal file
@@ -0,0 +1,56 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global flushCaches
|
||||
.type flushCaches, %function
|
||||
flushCaches:
|
||||
@ Clean and flush both the data cache and instruction caches
|
||||
|
||||
@ Adpated from http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0155a/ch03s03s05.html ,
|
||||
@ and https://github.com/gemarcano/libctr9_io/blob/master/src/ctr_system_ARM.c#L39 as well
|
||||
@ Note: ARM's example is actually for a 8KB DCache (which is what the 3DS has)
|
||||
|
||||
@ Implemented in bootROM at addresses 0xffff0830 (DCache) and 0xffff0ab4 (ICache)
|
||||
|
||||
mov r1, #0 @ segment counter
|
||||
outer_loop:
|
||||
mov r0, #0 @ line counter
|
||||
|
||||
inner_loop:
|
||||
orr r2, r1, r0 @ generate segment and line address
|
||||
mcr p15, 0, r2, c7, c14, 2 @ clean and flush the line
|
||||
add r0, #0x20 @ increment to next line
|
||||
cmp r0, #0x400
|
||||
bne inner_loop
|
||||
|
||||
add r1, #0x40000000
|
||||
cmp r1, #0
|
||||
bne outer_loop
|
||||
|
||||
mcr p15, 0, r1, c7, c10, 4 @ drain write buffer
|
||||
|
||||
@ Flush instruction cache
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
|
||||
bx lr
|
||||
@@ -1,21 +0,0 @@
|
||||
FatFs Module Source Files R0.11
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
history.txt Revision history.
|
||||
ffconf.h Configuration file for FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
ff.c FatFs module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module that written
|
||||
to control the target storage device.
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2014 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "diskio.h" /* FatFs lower layer API */
|
||||
#include "sdmmc/sdmmc.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_status (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
sdmmc_sdcard_init();
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_read (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
if (sdmmc_sdcard_readsectors(sector, count, buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if _USE_WRITE
|
||||
DRESULT disk_write (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if _USE_IOCTL
|
||||
DRESULT disk_ioctl (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
||||
__attribute__((unused))
|
||||
BYTE cmd, /* Control code */
|
||||
__attribute__((unused))
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
return RES_PARERR;
|
||||
}
|
||||
#endif
|
||||
@@ -1,80 +0,0 @@
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2014 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define _USE_WRITE 1 /* 1: Enable disk_write function */
|
||||
#define _USE_IOCTL 1 /* 1: Enable disk_ioctl fucntion */
|
||||
|
||||
#include "integer.h"
|
||||
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
|
||||
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,350 +0,0 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module include R0.11 (C)ChaN, 2015
|
||||
/----------------------------------------------------------------------------/
|
||||
/ FatFs module is a free software that opened under license policy of
|
||||
/ following conditions.
|
||||
/
|
||||
/ Copyright (C) 2015, ChaN, all right reserved.
|
||||
/
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef _FATFS
|
||||
#define _FATFS 32020 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "integer.h" /* Basic integer types */
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
#if _FATFS != _FFCONF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if _MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||
|
||||
#else /* Single partition configuration */
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API */
|
||||
|
||||
#if _LFN_UNICODE /* Unicode string */
|
||||
#if !_USE_LFN
|
||||
#error _LFN_UNICODE must be 0 at non-LFN cfg.
|
||||
#endif
|
||||
#ifndef _INC_TCHAR
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#endif
|
||||
|
||||
#else /* ANSI/OEM string */
|
||||
#ifndef _INC_TCHAR
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* File system object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* FAT sub-type (0:Not mounted) */
|
||||
BYTE drv; /* Physical drive number */
|
||||
BYTE csize; /* Sectors per cluster (1,2,4...128) */
|
||||
BYTE n_fats; /* Number of FAT copies (1 or 2) */
|
||||
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||
WORD id; /* File system mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
#if _MAX_SS != _MIN_SS
|
||||
WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if _FS_REENTRANT
|
||||
_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !_FS_READONLY
|
||||
DWORD last_clust; /* Last allocated cluster */
|
||||
DWORD free_clust; /* Number of free clusters */
|
||||
#endif
|
||||
#if _FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */
|
||||
DWORD fsize; /* Sectors per FAT */
|
||||
DWORD volbase; /* Volume start sector */
|
||||
DWORD fatbase; /* FAT start sector */
|
||||
DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */
|
||||
DWORD database; /* Data start sector */
|
||||
DWORD winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the related file system object (**do not change order**) */
|
||||
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||
BYTE flag; /* Status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
DWORD fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD fsize; /* File size */
|
||||
DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */
|
||||
DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */
|
||||
DWORD dsect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */
|
||||
#endif
|
||||
#if _USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */
|
||||
#endif
|
||||
#if _FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
#if !_FS_TINY
|
||||
BYTE buf[_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */
|
||||
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||
WORD index; /* Current read/write index number */
|
||||
DWORD sclust; /* Table start cluster (0:Root dir) */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector */
|
||||
BYTE* dir; /* Pointer to the current SFN entry in the win[] */
|
||||
BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
|
||||
#if _FS_LOCK
|
||||
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
#if _USE_LFN
|
||||
WCHAR* lfn; /* Pointer to the LFN working buffer */
|
||||
WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
|
||||
#endif
|
||||
#if _USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
DWORD fsize; /* File size */
|
||||
WORD fdate; /* Last modified date */
|
||||
WORD ftime; /* Last modified time */
|
||||
BYTE fattrib; /* Attribute */
|
||||
TCHAR fname[13]; /* Short file name (8.3 format) */
|
||||
#if _USE_LFN
|
||||
TCHAR* lfname; /* Pointer to the LFN buffer */
|
||||
UINT lfsize; /* Size of LFN buffer in TCHAR */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_SHARE */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */
|
||||
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change times-tamp of the file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->fsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !_FS_READONLY && !_FS_NORTC
|
||||
DWORD get_fattime (void);
|
||||
#endif
|
||||
|
||||
/* Unicode support functions */
|
||||
#if _USE_LFN /* Unicode - OEM code conversion */
|
||||
WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */
|
||||
WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */
|
||||
#if _USE_LFN == 3 /* Memory functions */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Sync functions */
|
||||
#if _FS_REENTRANT
|
||||
int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */
|
||||
int ff_req_grant (_SYNC_t sobj); /* Lock sync object */
|
||||
void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */
|
||||
int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access control and file status flags (FIL.flag) */
|
||||
|
||||
#define FA_READ 0x01
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
|
||||
#if !_FS_READONLY
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA__WRITTEN 0x20
|
||||
#define FA__DIRTY 0x40
|
||||
#endif
|
||||
|
||||
|
||||
/* FAT sub type (FATFS.fs_type) */
|
||||
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
|
||||
|
||||
/* File attribute bits for directory entry */
|
||||
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_VOL 0x08 /* Volume label */
|
||||
#define AM_LFN 0x0F /* LFN entry */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
#define AM_MASK 0x3F /* Mask of defined bits */
|
||||
|
||||
|
||||
/* Fast seek feature */
|
||||
#define CREATE_LINKMAP 0xFFFFFFFF
|
||||
|
||||
|
||||
|
||||
/*--------------------------------*/
|
||||
/* Multi-byte word access macros */
|
||||
|
||||
#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */
|
||||
#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))
|
||||
#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr))
|
||||
#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val)
|
||||
#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
|
||||
#else /* Use byte-by-byte access to the FAT structure */
|
||||
#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
|
||||
#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
|
||||
#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
|
||||
#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FATFS */
|
||||
@@ -1,271 +0,0 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module configuration file R0.11 (C)ChaN, 2015
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 32020 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Functions and Buffer Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_TINY 1
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS
|
||||
/ bytes. Instead of private sector buffer eliminated from the file object,
|
||||
/ common sector buffer in the file system object (FATFS) is used for the file
|
||||
/ data transfer. */
|
||||
|
||||
|
||||
#define _FS_READONLY 1
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define _FS_MINIMIZE 3
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: All basic functions are enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
|
||||
/ f_truncate() and f_rename() function are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define _USE_STRFUNC 0
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
|
||||
/ f_printf().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define _USE_FIND 0
|
||||
/* This option switches filtered directory read feature and related functions,
|
||||
/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_MKFS 0
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FASTSEEK 0
|
||||
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable)
|
||||
/ To enable it, also _FS_TINY need to be set to 1. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _CODE_PAGE 437
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect setting of the code page can cause a file open failure.
|
||||
/
|
||||
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 775 - Baltic
|
||||
/ 850 - Multilingual Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 858 - Multilingual Latin 1 + Euro
|
||||
/ 862 - Hebrew
|
||||
/ 866 - Russian
|
||||
/ 874 - Thai
|
||||
/ 932 - Japanese Shift_JIS (DBCS)
|
||||
/ 936 - Simplified Chinese GBK (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese Big5 (DBCS)
|
||||
*/
|
||||
|
||||
|
||||
#define _USE_LFN 2
|
||||
#define _MAX_LFN 255
|
||||
/* The _USE_LFN option switches the LFN feature.
|
||||
/
|
||||
/ 0: Disable LFN feature. _MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must
|
||||
/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree(), must be added to the project. */
|
||||
|
||||
|
||||
#define _LFN_UNICODE 0
|
||||
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
|
||||
/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
|
||||
/ to 1. This option also affects behavior of string I/O functions. */
|
||||
|
||||
|
||||
#define _STRF_ENCODE 0
|
||||
/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
|
||||
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
|
||||
/
|
||||
/ 0: ANSI/OEM
|
||||
/ 1: UTF-16LE
|
||||
/ 2: UTF-16BE
|
||||
/ 3: UTF-8
|
||||
/
|
||||
/ When _LFN_UNICODE is 0, this option has no effect. */
|
||||
|
||||
|
||||
#define _FS_RPATH 0
|
||||
/* This option configures relative path feature.
|
||||
/
|
||||
/ 0: Disable relative path feature and remove related functions.
|
||||
/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
/
|
||||
/ Note that directory items read via f_readdir() are affected by this option. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
|
||||
/* _STR_VOLUME_ID option switches string volume ID feature.
|
||||
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
|
||||
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
|
||||
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
|
||||
/ the drive ID strings are: A-Z and 0-9. */
|
||||
|
||||
|
||||
#define _MULTI_PARTITION 0
|
||||
/* This option switches multi-partition feature. By default (0), each logical drive
|
||||
/ number is bound to the same physical drive number and only an FAT volume found on
|
||||
/ the physical drive will be mounted. When multi-partition feature is enabled (1),
|
||||
/ each logical drive number is bound to arbitrary physical drive and partition
|
||||
/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */
|
||||
|
||||
|
||||
#define _MIN_SS 512
|
||||
#define _MAX_SS 512
|
||||
/* These options configure the range of sector size to be supported. (512, 1024,
|
||||
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
|
||||
/ To enable Trim feature, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_NORTC 1
|
||||
#define _NORTC_MON 2
|
||||
#define _NORTC_MDAY 1
|
||||
#define _NORTC_YEAR 2015
|
||||
/* The _FS_NORTC option switches timestamp feature. If the system does not have
|
||||
/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
|
||||
/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp
|
||||
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
|
||||
/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
|
||||
/ to be added to the project to read current time form RTC. _NORTC_MON,
|
||||
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (_FS_READONLY == 1). */
|
||||
|
||||
|
||||
#define _FS_LOCK 0
|
||||
/* The _FS_LOCK option switches file lock feature to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock feature. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock feature. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock feature is independent of re-entrancy. */
|
||||
|
||||
|
||||
#define _FS_REENTRANT 0
|
||||
#define _FS_TIMEOUT 1000
|
||||
#define _SYNC_t HANDLE
|
||||
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this feature.
|
||||
/
|
||||
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.c. */
|
||||
|
||||
|
||||
#define _WORD_ACCESS 0
|
||||
/* The _WORD_ACCESS option is an only platform dependent option. It defines
|
||||
/ which access method is used to the word data on the FAT volume.
|
||||
/
|
||||
/ 0: Byte-by-byte access. Always compatible with all platforms.
|
||||
/ 1: Word access. Do not choose this unless under both the following conditions.
|
||||
/
|
||||
/ * Address misaligned memory access is always allowed to ALL instructions.
|
||||
/ * Byte order on the memory is little-endian.
|
||||
/
|
||||
/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
|
||||
/ Following table shows allowable settings of some processor types.
|
||||
/
|
||||
/ ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2
|
||||
/ Cortex-M3 0 *3 Z80 0/1 V850ES 0/1
|
||||
/ Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1
|
||||
/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1
|
||||
/ AVR32 0 *1 RL78 0 *2 R32C 0 *2
|
||||
/ PIC18 0/1 SH-2 0 *1 M16C 0/1
|
||||
/ PIC24 0 *2 H8S 0 *1 MSP430 0 *2
|
||||
/ PIC32 0 *1 H8/300H 0 *1 8051 0/1
|
||||
/
|
||||
/ *1:Big-endian.
|
||||
/ *2:Unaligned memory access is not supported.
|
||||
/ *3:Some compilers generate LDM/STM for mem_cpy function.
|
||||
*/
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
Prototype.
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
First stable version.
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code.
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
@@ -1,33 +0,0 @@
|
||||
/*-------------------------------------------*/
|
||||
/* Integer type definitions for FatFs module */
|
||||
/*-------------------------------------------*/
|
||||
|
||||
#ifndef _FF_INTEGER
|
||||
#define _FF_INTEGER
|
||||
|
||||
#ifdef _WIN32 /* FatFs development platform */
|
||||
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#else /* Embedded platform */
|
||||
|
||||
/* This type MUST be 8 bit */
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
/* These types MUST be 16 bit */
|
||||
typedef short SHORT;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned short WCHAR;
|
||||
|
||||
/* These types MUST be 16 bit or 32 bit */
|
||||
typedef int INT;
|
||||
typedef unsigned int UINT;
|
||||
|
||||
/* These types MUST be 32 bit */
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../../types.h"
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void ioDelay(u32 us);
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
.arm
|
||||
.global ioDelay
|
||||
.type ioDelay STT_FUNC
|
||||
|
||||
@ioDelay ( u32 us )
|
||||
ioDelay:
|
||||
ldr r1, =0x18000000 @ VRAM
|
||||
1:
|
||||
@ Loop doing uncached reads from VRAM to make loop timing more reliable
|
||||
ldr r2, [r1]
|
||||
subs r0, #1
|
||||
bgt 1b
|
||||
bx lr
|
||||
@@ -1,404 +0,0 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "sdmmc.h"
|
||||
#include "delay.h"
|
||||
|
||||
struct mmcdevice handleNAND;
|
||||
struct mmcdevice handleSD;
|
||||
|
||||
static inline u16 sdmmc_read16(u16 reg) {
|
||||
return *(vu16*)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
static inline void sdmmc_write16(u16 reg, u16 val) {
|
||||
*(vu16*)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
static inline u32 sdmmc_read32(u16 reg) {
|
||||
return *(vu32*)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
static inline void sdmmc_write32(u16 reg, u32 val) {
|
||||
*(vu32*)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) {
|
||||
u16 val = sdmmc_read16(reg);
|
||||
val &= ~clear;
|
||||
val |= set;
|
||||
sdmmc_write16(reg, val);
|
||||
}
|
||||
|
||||
static inline void setckl(u32 data)
|
||||
{
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x100, 0);
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF);
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
|
||||
}
|
||||
|
||||
|
||||
mmcdevice *getMMCDevice(int drive)
|
||||
{
|
||||
if(drive==0) return &handleNAND;
|
||||
return &handleSD;
|
||||
}
|
||||
|
||||
static u32 __attribute__((noinline)) geterror(struct mmcdevice *ctx)
|
||||
{
|
||||
return (ctx->error << 29) >> 31;
|
||||
}
|
||||
|
||||
static void __attribute__((noinline)) inittarget(struct mmcdevice *ctx)
|
||||
{
|
||||
sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber);
|
||||
setckl(ctx->clk);
|
||||
if (ctx->SDOPT == 0) {
|
||||
sdmmc_mask16(REG_SDOPT, 0, 0x8000);
|
||||
} else {
|
||||
sdmmc_mask16(REG_SDOPT, 0x8000, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args)
|
||||
{
|
||||
bool getSDRESP = (cmd << 15) >> 31;
|
||||
u16 flags = (cmd << 15) >> 31;
|
||||
const bool readdata = cmd & 0x20000;
|
||||
const bool writedata = cmd & 0x40000;
|
||||
|
||||
if (readdata || writedata)
|
||||
flags |= TMIO_STAT0_DATAEND;
|
||||
|
||||
ctx->error = 0;
|
||||
while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working?
|
||||
sdmmc_write16(REG_SDIRMASK0,0);
|
||||
sdmmc_write16(REG_SDIRMASK1,0);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
sdmmc_mask16(REG_SDDATACTL32,0x1800,0);
|
||||
|
||||
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
|
||||
sdmmc_write16(REG_SDCMDARG1,args >> 16);
|
||||
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
|
||||
|
||||
u32 size = ctx->size;
|
||||
vu8 *dataPtr = ctx->data;
|
||||
|
||||
bool useBuf = ( NULL != dataPtr );
|
||||
|
||||
u16 status0 = 0;
|
||||
while(true) {
|
||||
u16 status1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
if (status1 & TMIO_STAT1_RXRDY) {
|
||||
if (readdata && useBuf) {
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY);
|
||||
if (size > 0x1FF) {
|
||||
for(int i = 0; i<0x200; i+=2) {
|
||||
u16 data = sdmmc_read16(REG_SDFIFO);
|
||||
*dataPtr++ = data & 0xFF;
|
||||
*dataPtr++ = data >> 8;
|
||||
}
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status1 & TMIO_STAT1_TXRQ) {
|
||||
if (writedata && useBuf) {
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ);
|
||||
if (size > 0x1FF) {
|
||||
for (int i = 0; i<0x200; i+=2) {
|
||||
u16 data = *dataPtr++;
|
||||
data |= *dataPtr++ << 8;
|
||||
sdmmc_write16(REG_SDFIFO, data);
|
||||
}
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status1 & TMIO_MASK_GW) {
|
||||
ctx->error |= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(status1 & TMIO_STAT1_CMD_BUSY)) {
|
||||
status0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
|
||||
ctx->error |= 0x1;
|
||||
if (status0 & TMIO_STAT0_DATAEND)
|
||||
ctx->error |= 0x2;
|
||||
|
||||
if ((status0 & flags) == flags)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
|
||||
if (getSDRESP != 0) {
|
||||
ctx->ret[0] = (u32)sdmmc_read16(REG_SDRESP0) | (u32)(sdmmc_read16(REG_SDRESP1) << 16);
|
||||
ctx->ret[1] = (u32)sdmmc_read16(REG_SDRESP2) | (u32)(sdmmc_read16(REG_SDRESP3) << 16);
|
||||
ctx->ret[2] = (u32)sdmmc_read16(REG_SDRESP4) | (u32)(sdmmc_read16(REG_SDRESP5) << 16);
|
||||
ctx->ret[3] = (u32)sdmmc_read16(REG_SDRESP6) | (u32)(sdmmc_read16(REG_SDRESP7) << 16);
|
||||
}
|
||||
}
|
||||
|
||||
u32 __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in)
|
||||
{
|
||||
if (handleSD.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
inittarget(&handleSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handleSD.data = in;
|
||||
handleSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleSD,0x52C19,sector_no);
|
||||
return geterror(&handleSD);
|
||||
}
|
||||
|
||||
u32 __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out)
|
||||
{
|
||||
if (handleSD.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
inittarget(&handleSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handleSD.data = out;
|
||||
handleSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleSD,0x33C12,sector_no);
|
||||
return geterror(&handleSD);
|
||||
}
|
||||
|
||||
u32 __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out)
|
||||
{
|
||||
if (handleNAND.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
inittarget(&handleNAND);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
|
||||
handleNAND.data = out;
|
||||
handleNAND.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleNAND,0x33C12,sector_no);
|
||||
inittarget(&handleSD);
|
||||
return geterror(&handleNAND);
|
||||
}
|
||||
|
||||
u32 __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, vu8 *in) //experimental
|
||||
{
|
||||
if (handleNAND.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
inittarget(&handleNAND);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
|
||||
handleNAND.data = in;
|
||||
handleNAND.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleNAND,0x52C19,sector_no);
|
||||
inittarget(&handleSD);
|
||||
return geterror(&handleNAND);
|
||||
}
|
||||
|
||||
static u32 calcSDSize(u8* csd, int type)
|
||||
{
|
||||
u32 result = 0;
|
||||
if (type == -1) type = csd[14] >> 6;
|
||||
switch (type) {
|
||||
case 0:
|
||||
{
|
||||
u32 block_len = csd[9] & 0xf;
|
||||
block_len = 1u << block_len;
|
||||
u32 mult = (u32)(csd[4] >> 7) | (u32)((csd[5] & 3) << 1);
|
||||
mult = 1u << (mult + 2);
|
||||
result = csd[8] & 3;
|
||||
result = (result << 8) | csd[7];
|
||||
result = (result << 2) | (csd[6] >> 6);
|
||||
result = (result + 1) * mult * block_len / 512;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
result = csd[7] & 0x3f;
|
||||
result = (result << 8) | csd[6];
|
||||
result = (result << 8) | csd[5];
|
||||
result = (result + 1) * 1024;
|
||||
break;
|
||||
default:
|
||||
break; //Do nothing otherwise
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void InitSD()
|
||||
{
|
||||
//NAND
|
||||
handleNAND.isSDHC = 0;
|
||||
handleNAND.SDOPT = 0;
|
||||
handleNAND.res = 0;
|
||||
handleNAND.initarg = 1;
|
||||
handleNAND.clk = 0x80;
|
||||
handleNAND.devicenumber = 1;
|
||||
|
||||
//SD
|
||||
handleSD.isSDHC = 0;
|
||||
handleSD.SDOPT = 0;
|
||||
handleSD.res = 0;
|
||||
handleSD.initarg = 0;
|
||||
handleSD.clk = 0x80;
|
||||
handleSD.devicenumber = 0;
|
||||
|
||||
*(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32
|
||||
*(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32
|
||||
*(vu16*)0x10006100 |= 0x402u; //SDDATACTL32
|
||||
*(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2;
|
||||
*(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32
|
||||
*(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL
|
||||
*(vu16*)0x10006104 = 0; //SDBLKLEN32
|
||||
*(vu16*)0x10006108 = 1; //SDBLKCOUNT32
|
||||
*(vu16*)0x100060E0 &= 0xFFFEu; //SDRESET
|
||||
*(vu16*)0x100060E0 |= 1u; //SDRESET
|
||||
*(vu16*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0
|
||||
*(vu16*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1
|
||||
*(vu16*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7
|
||||
*(vu16*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8
|
||||
*(vu16*)0x10006002 &= 0xFFFCu; //SDPORTSEL
|
||||
*(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20
|
||||
*(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE
|
||||
*(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL
|
||||
*(vu16*)0x10006026 = 512; //SDBLKLEN
|
||||
*(vu16*)0x10006008 = 0; //SDSTOP
|
||||
|
||||
inittarget(&handleSD);
|
||||
}
|
||||
|
||||
static int Nand_Init()
|
||||
{
|
||||
inittarget(&handleNAND);
|
||||
ioDelay(0xF000);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0,0);
|
||||
|
||||
do {
|
||||
do {
|
||||
sdmmc_send_command(&handleNAND,0x10701,0x100000);
|
||||
} while ( !(handleNAND.error & 1) );
|
||||
} while((handleNAND.ret[0] & 0x80000000) == 0);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10602,0x0);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10403,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10609,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
handleNAND.total_size = calcSDSize((u8*)&handleNAND.ret[0],0);
|
||||
handleNAND.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10407,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
handleNAND.SDOPT = 1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10506,0x3B70100);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10506,0x3B90100);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x1040D,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10410,0x200);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
handleNAND.clk |= 0x200;
|
||||
|
||||
inittarget(&handleSD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SD_Init()
|
||||
{
|
||||
inittarget(&handleSD);
|
||||
|
||||
ioDelay(1u << 18); //Card needs a little bit of time to be detected, it seems
|
||||
|
||||
//If not inserted
|
||||
if (!(*((vu16*)0x1000601c) & TMIO_STAT0_SIGSTATE)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0,0);
|
||||
sdmmc_send_command(&handleSD,0x10408,0x1AA);
|
||||
//u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E;
|
||||
u32 temp = (handleSD.error & 0x1) << 0x1E;
|
||||
|
||||
//int count = 0;
|
||||
u32 temp2 = 0;
|
||||
do {
|
||||
do {
|
||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
||||
sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp);
|
||||
temp2 = 1;
|
||||
} while ( !(handleSD.error & 1) );
|
||||
|
||||
} while((handleSD.ret[0] & 0x80000000) == 0);
|
||||
|
||||
if(!((handleSD.ret[0] >> 30) & 1) || !temp)
|
||||
temp2 = 0;
|
||||
|
||||
handleSD.isSDHC = temp2;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10602,0);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10403,0);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
handleSD.initarg = handleSD.ret[0] >> 0x10;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0],-1);
|
||||
handleSD.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10507,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
handleSD.SDOPT = 1;
|
||||
sdmmc_send_command(&handleSD,0x10446,0x2);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10410,0x200);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
handleSD.clk |= 0x200;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sdmmc_sdcard_init()
|
||||
{
|
||||
InitSD();
|
||||
int result = Nand_Init();
|
||||
return result | SD_Init();
|
||||
}
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define SDMMC_BASE 0x10006000u
|
||||
|
||||
#define REG_SDCMD 0x00
|
||||
#define REG_SDPORTSEL 0x02
|
||||
#define REG_SDCMDARG 0x04
|
||||
#define REG_SDCMDARG0 0x04
|
||||
#define REG_SDCMDARG1 0x06
|
||||
#define REG_SDSTOP 0x08
|
||||
#define REG_SDBLKCOUNT 0x0a
|
||||
|
||||
#define REG_SDRESP0 0x0c
|
||||
#define REG_SDRESP1 0x0e
|
||||
#define REG_SDRESP2 0x10
|
||||
#define REG_SDRESP3 0x12
|
||||
#define REG_SDRESP4 0x14
|
||||
#define REG_SDRESP5 0x16
|
||||
#define REG_SDRESP6 0x18
|
||||
#define REG_SDRESP7 0x1a
|
||||
|
||||
#define REG_SDSTATUS0 0x1c
|
||||
#define REG_SDSTATUS1 0x1e
|
||||
|
||||
#define REG_SDIRMASK0 0x20
|
||||
#define REG_SDIRMASK1 0x22
|
||||
#define REG_SDCLKCTL 0x24
|
||||
|
||||
#define REG_SDBLKLEN 0x26
|
||||
#define REG_SDOPT 0x28
|
||||
#define REG_SDFIFO 0x30
|
||||
|
||||
#define REG_SDDATACTL 0xd8
|
||||
#define REG_SDRESET 0xe0
|
||||
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
|
||||
|
||||
#define REG_SDDATACTL32 0x100
|
||||
#define REG_SDBLKLEN32 0x104
|
||||
#define REG_SDBLKCOUNT32 0x108
|
||||
#define REG_SDFIFO32 0x10C
|
||||
|
||||
#define REG_CLK_AND_WAIT_CTL 0x138
|
||||
#define REG_RESET_SDIO 0x1e0
|
||||
|
||||
#define TMIO_STAT0_CMDRESPEND 0x0001
|
||||
#define TMIO_STAT0_DATAEND 0x0004
|
||||
#define TMIO_STAT0_CARD_REMOVE 0x0008
|
||||
#define TMIO_STAT0_CARD_INSERT 0x0010
|
||||
#define TMIO_STAT0_SIGSTATE 0x0020
|
||||
#define TMIO_STAT0_WRPROTECT 0x0080
|
||||
#define TMIO_STAT0_CARD_REMOVE_A 0x0100
|
||||
#define TMIO_STAT0_CARD_INSERT_A 0x0200
|
||||
#define TMIO_STAT0_SIGSTATE_A 0x0400
|
||||
#define TMIO_STAT1_CMD_IDX_ERR 0x0001
|
||||
#define TMIO_STAT1_CRCFAIL 0x0002
|
||||
#define TMIO_STAT1_STOPBIT_ERR 0x0004
|
||||
#define TMIO_STAT1_DATATIMEOUT 0x0008
|
||||
#define TMIO_STAT1_RXOVERFLOW 0x0010
|
||||
#define TMIO_STAT1_TXUNDERRUN 0x0020
|
||||
#define TMIO_STAT1_CMDTIMEOUT 0x0040
|
||||
#define TMIO_STAT1_RXRDY 0x0100
|
||||
#define TMIO_STAT1_TXRQ 0x0200
|
||||
#define TMIO_STAT1_ILL_FUNC 0x2000
|
||||
#define TMIO_STAT1_CMD_BUSY 0x4000
|
||||
#define TMIO_STAT1_ILL_ACCESS 0x8000
|
||||
|
||||
//Comes from TWLSDK mongoose.tef DWARF info
|
||||
#define SDMC_NORMAL 0x00000000
|
||||
#define SDMC_ERR_COMMAND 0x00000001
|
||||
#define SDMC_ERR_CRC 0x00000002
|
||||
#define SDMC_ERR_END 0x00000004
|
||||
#define SDMC_ERR_TIMEOUT 0x00000008
|
||||
#define SDMC_ERR_FIFO_OVF 0x00000010
|
||||
#define SDMC_ERR_FIFO_UDF 0x00000020
|
||||
#define SDMC_ERR_WP 0x00000040
|
||||
#define SDMC_ERR_ABORT 0x00000080
|
||||
#define SDMC_ERR_FPGA_TIMEOUT 0x00000100
|
||||
#define SDMC_ERR_PARAM 0x00000200
|
||||
#define SDMC_ERR_R1_STATUS 0x00000800
|
||||
#define SDMC_ERR_NUM_WR_SECTORS 0x00001000
|
||||
#define SDMC_ERR_RESET 0x00002000
|
||||
#define SDMC_ERR_ILA 0x00004000
|
||||
#define SDMC_ERR_INFO_DETECT 0x00008000
|
||||
|
||||
#define SDMC_STAT_ERR_UNKNOWN 0x00080000
|
||||
#define SDMC_STAT_ERR_CC 0x00100000
|
||||
#define SDMC_STAT_ERR_ECC_FAILED 0x00200000
|
||||
#define SDMC_STAT_ERR_CRC 0x00800000
|
||||
#define SDMC_STAT_ERR_OTHER 0xf9c70008
|
||||
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
|
||||
#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
|
||||
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
|
||||
|
||||
#define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND)
|
||||
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
|
||||
|
||||
typedef struct mmcdevice {
|
||||
vu8* data;
|
||||
u32 size;
|
||||
u32 error;
|
||||
u16 stat0;
|
||||
u16 stat1;
|
||||
u32 ret[4];
|
||||
u32 initarg;
|
||||
u32 isSDHC;
|
||||
u32 clk;
|
||||
u32 SDOPT;
|
||||
u32 devicenumber;
|
||||
u32 total_size; //size in sectors of the device
|
||||
u32 res;
|
||||
} mmcdevice;
|
||||
|
||||
int sdmmc_sdcard_init();
|
||||
u32 sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out);
|
||||
u32 sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in);
|
||||
mmcdevice *getMMCDevice(int drive);
|
||||
|
||||
u32 sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out);
|
||||
u32 sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, vu8 *in);
|
||||
44
loader/source/firm.c
Normal file
44
loader/source/firm.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2017 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#include "firm.h"
|
||||
#include "memory.h"
|
||||
#include "cache.h"
|
||||
|
||||
void launchFirm(Firm *firm, int argc, char **argv)
|
||||
{
|
||||
|
||||
//Copy FIRM sections to respective memory locations
|
||||
for(u32 sectionNum = 0; sectionNum < 4 && firm->section[sectionNum].size != 0; sectionNum++)
|
||||
memcpy(firm->section[sectionNum].address, (u8 *)firm + firm->section[sectionNum].offset, firm->section[sectionNum].size);
|
||||
|
||||
//Set ARM11 entrypoint
|
||||
*(vu32 *)0x1FFFFFFC = (u32)firm->arm11Entry;
|
||||
|
||||
//Ensure that all memory transfers have completed and that the caches have been flushed
|
||||
flushCaches();
|
||||
|
||||
//Jump to ARM9 entrypoint. Also give it additional arguments it can dismiss
|
||||
((void (*)(int, char**, u32))firm->arm9Entry)(argc, argv, 0x0000BEEF);
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
46
loader/source/firm.h
Normal file
46
loader/source/firm.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2017 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 offset;
|
||||
u8 *address;
|
||||
u32 size;
|
||||
u32 procType;
|
||||
u8 hash[0x20];
|
||||
} FirmSection;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic[4];
|
||||
u32 reserved1;
|
||||
u8 *arm11Entry;
|
||||
u8 *arm9Entry;
|
||||
u8 reserved2[0x30];
|
||||
FirmSection section[4];
|
||||
} Firm;
|
||||
|
||||
void launchFirm(Firm *firm, int argc, char **argv);
|
||||
@@ -1,39 +1,41 @@
|
||||
#include "types.h"
|
||||
#include "buttons.h"
|
||||
#include "fatfs/ff.h"
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#define PAYLOAD_ADDRESS 0x23F00000
|
||||
#include "memory.h"
|
||||
#include "cache.h"
|
||||
#include "firm.h"
|
||||
|
||||
static u32 loadPayload(const char *path){
|
||||
FIL payload;
|
||||
unsigned int br;
|
||||
if(f_open(&payload, path, FA_READ) == FR_OK)
|
||||
{
|
||||
f_read(&payload, (void *)PAYLOAD_ADDRESS, f_size(&payload), &br);
|
||||
f_close(&payload);
|
||||
void main(int argc __attribute__((unused)), char **argv)
|
||||
{
|
||||
Firm *firm = (Firm *)0x24000000;
|
||||
char absPath[24 + 255];
|
||||
|
||||
return 1;
|
||||
}
|
||||
u32 i;
|
||||
for(i = 0; i < 23 + 255 && argv[0][i] != 0; i++)
|
||||
absPath[i] = argv[0][i];
|
||||
for(; i < 24 + 255; i++)
|
||||
absPath[i] = 0;
|
||||
|
||||
return 0;
|
||||
char *argvPassed[1] = {absPath};
|
||||
|
||||
launchFirm(firm, 1, argvPassed);
|
||||
}
|
||||
|
||||
void main(void){
|
||||
FATFS fs;
|
||||
f_mount(&fs, "0:", 1);
|
||||
|
||||
//Get pressed buttons
|
||||
u16 pressed = HID_PAD;
|
||||
|
||||
if(((pressed & BUTTON_B) && loadPayload("/aurei/payloads/b.bin")) ||
|
||||
((pressed & BUTTON_X) && loadPayload("/aurei/payloads/x.bin")) ||
|
||||
((pressed & BUTTON_Y) && loadPayload("/aurei/payloads/y.bin")) ||
|
||||
((pressed & BUTTON_SELECT) && loadPayload("/aurei/payloads/select.bin")) ||
|
||||
((pressed & BUTTON_START) && loadPayload("/aurei/payloads/start.bin")) ||
|
||||
((pressed & BUTTON_RIGHT) && loadPayload("/aurei/payloads/right.bin")) ||
|
||||
((pressed & BUTTON_LEFT) && loadPayload("/aurei/payloads/left.bin")) ||
|
||||
((pressed & BUTTON_UP) && loadPayload("/aurei/payloads/up.bin")) ||
|
||||
((pressed & BUTTON_DOWN) && loadPayload("/aurei/payloads/down.bin")) ||
|
||||
loadPayload("/aurei/payloads/default.bin"))
|
||||
((void (*)())PAYLOAD_ADDRESS)();
|
||||
}
|
||||
36
loader/source/memory.c
Normal file
36
loader/source/memory.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* memcpy adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size)
|
||||
{
|
||||
u8 *destc = (u8 *)dest;
|
||||
const u8 *srcc = (const u8 *)src;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
destc[i] = srcc[i];
|
||||
}
|
||||
31
loader/source/memory.h
Normal file
31
loader/source/memory.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* memcpy adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size);
|
||||
@@ -1,18 +1,30 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2017 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
@ Fix payloads like Decrypt9
|
||||
mov r0, #0x5
|
||||
mcr p15, 0, r0, c3, c0, 0 @ data bufferable
|
||||
ldr sp, =0x27ffe000
|
||||
b main
|
||||
|
||||
@ Flush caches
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c5, 0 @ flush I-cache
|
||||
mcr p15, 0, r0, c7, c6, 0 @ flush D-cache
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
|
||||
|
||||
bl main
|
||||
|
||||
.die:
|
||||
b .die
|
||||
.global payloadSize
|
||||
payloadSize:
|
||||
.word 0
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
/*
|
||||
* types.h
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//Common data types
|
||||
typedef uint8_t u8;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x24F00000;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T ../stub.ld%s
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
; Code by Normmatt
|
||||
|
||||
.arm.little
|
||||
|
||||
.create "emunand.bin", 0
|
||||
.create "build/emunand.bin", 0
|
||||
.arm
|
||||
nand_sd:
|
||||
; Original code that still needs to be executed.
|
||||
; Original code that still needs to be executed
|
||||
mov r4, r0
|
||||
mov r5, r1
|
||||
mov r7, r2
|
||||
mov r6, r3
|
||||
; End.
|
||||
; End
|
||||
|
||||
; If we're already trying to access the SD, return.
|
||||
; If we're already trying to access the SD, return
|
||||
ldr r2, [r0, #4]
|
||||
ldr r1, [sdmmc]
|
||||
cmp r2, r1
|
||||
beq nand_sd_ret
|
||||
beq out
|
||||
|
||||
str r1, [r0, #4] ; Set object to be SD
|
||||
ldr r2, [r0, #8] ; Get sector to read
|
||||
cmp r2, #0 ; For GW compatibility, see if we're trying to read the ncsd header (sector 0)
|
||||
str r1, [r0, #4] ; Set object to be SD
|
||||
ldr r2, [r0, #8] ; Get sector to read
|
||||
cmp r2, #0 ; For GW compatibility, see if we're trying to read the ncsd header (sector 0)
|
||||
|
||||
ldr r3, [nand_offset]
|
||||
add r2, r3 ; Add the offset to the NAND in the SD.
|
||||
add r2, r3 ; Add the offset to the NAND in the SD
|
||||
|
||||
ldreq r3, [ncsd_header_offset]
|
||||
addeq r2, r3 ; If we're reading the ncsd header, add the offset of that sector.
|
||||
addeq r2, r3 ; If we're reading the ncsd header, add the offset of that sector
|
||||
|
||||
str r2, [r0, #8] ; Store sector to read
|
||||
str r2, [r0, #8] ; Store sector to read
|
||||
|
||||
nand_sd_ret:
|
||||
out:
|
||||
; Restore registers.
|
||||
mov r1, r5
|
||||
mov r2, r7
|
||||
mov r3, r6
|
||||
|
||||
; Return 4 bytes behind where we got called,
|
||||
; due to the offset of this function being stored there.
|
||||
; due to the offset of this function being stored there
|
||||
mov r0, lr
|
||||
add r0, #4
|
||||
bx r0
|
||||
|
||||
.pool
|
||||
sdmmc: .ascii "SDMC"
|
||||
nand_offset: .ascii "NAND" ; for rednand this should be 1
|
||||
ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED)
|
||||
nand_offset: .ascii "NAND" ; For rednand this should be 1
|
||||
ncsd_header_offset: .ascii "NCSD" ; Depends on nand manufacturer + emunand type (GW/RED)
|
||||
.close
|
||||
|
||||
105
patches/k11modules.s
Normal file
105
patches/k11modules.s
Normal file
@@ -0,0 +1,105 @@
|
||||
;
|
||||
; This file is part of Luma3DS
|
||||
; Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
;
|
||||
; This program is free software: you can redistribute it and/or modify
|
||||
; it under the terms of the GNU General Public License as published by
|
||||
; the Free Software Foundation, either version 3 of the License, or
|
||||
; (at your option) any later version.
|
||||
;
|
||||
; This program is distributed in the hope that it will be useful,
|
||||
; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
; GNU General Public License for more details.
|
||||
;
|
||||
; You should have received a copy of the GNU General Public License
|
||||
; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
;
|
||||
; Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
; reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
; Notices displayed by works containing it.
|
||||
;
|
||||
|
||||
; Code originally from Subv
|
||||
|
||||
.arm.little
|
||||
|
||||
.create "build/k11modules.bin", 0
|
||||
.arm
|
||||
; This code searches the sm module for a specific byte pattern and patches some of the instructions
|
||||
; in the code to disable service access checks when calling srv:GetServiceHandle
|
||||
|
||||
; It also searches the fs module for archive access check code
|
||||
|
||||
; Save the registers we'll be using
|
||||
; Register contents:
|
||||
; r4: Pointer to a pointer to the exheader of the current NCCH
|
||||
; r6: Constant 0
|
||||
; SP + 4: Pointer to the memory location where the NCCH text was loaded
|
||||
|
||||
; Execute the instruction we overwrote in our detour
|
||||
ldr r0, [r4]
|
||||
|
||||
; Save the value of the register we use
|
||||
push {r0-r4}
|
||||
|
||||
ldr r1, [sp, #24] ; Load the .text address
|
||||
ldr r2, [r0, #0x200] ; Load the low title id of the current NCCH
|
||||
ldr r0, [r0, #0x18] ; Load the size of the .text
|
||||
add r0, r1, r0 ; Max bounds of the memory region
|
||||
|
||||
ldr r3, =0x1002 ; Low title id of the sm module
|
||||
cmp r2, r3 ; Compare the low title id to the id of the sm module
|
||||
bne fs_patch ; Skip if they're not the same
|
||||
|
||||
ldr r2, =0xE1A01006 ; mov r1, r6
|
||||
|
||||
loop:
|
||||
cmp r0, r1
|
||||
blo die ; Check if we didn't go past the bounds of the memory region
|
||||
ldr r3, [r1]
|
||||
cmp r3, r2
|
||||
ldreqh r3, [r1, #4]
|
||||
cmpeq r3, #5
|
||||
addne r1, #4
|
||||
bne loop
|
||||
|
||||
; r1 now contains the start address of the pattern we found
|
||||
ldr r0, =0xE3A00001 ; mov r0, #1
|
||||
str r0, [r1, #8] ; Patch the bl
|
||||
b out
|
||||
|
||||
fs_patch: ; patch adapted from BootNTR
|
||||
ldr r3, =0x1102 ; Low title id of the fs module
|
||||
cmp r2, r3 ; Compare the low title id to the id of the sm module
|
||||
bne out ; Skip if they're not the same
|
||||
|
||||
ldr r2, =0x7401 ; strb r1, [r0, #16]
|
||||
ldr r3, =0x2000 ; movs r0, #0
|
||||
|
||||
loop_fs:
|
||||
cmp r0, r1
|
||||
blo die
|
||||
ldrh r4, [r1]
|
||||
cmp r4, r2
|
||||
ldreqh r4, [r1, #2]
|
||||
cmpeq r4, r3
|
||||
addeq r1, #8
|
||||
addne r1, #2
|
||||
bne loop_fs
|
||||
|
||||
; r1 now contains the start address of the pattern we found
|
||||
ldr r0, =0x2001 ; mov r0, #1
|
||||
ldr r2, =0x4770 ; bx lr
|
||||
strh r0, [r1]
|
||||
strh r2, [r1, #2]
|
||||
|
||||
out:
|
||||
pop {r0-r4} ; Restore the registers we used
|
||||
bx lr ; Jump back to whoever called us
|
||||
|
||||
die:
|
||||
b die
|
||||
|
||||
.pool
|
||||
.close
|
||||
391
patches/reboot.s
391
patches/reboot.s
@@ -1,221 +1,214 @@
|
||||
; Code originally from delebile and mid-kid
|
||||
|
||||
.arm.little
|
||||
|
||||
byteswritten equ 0x2000E000
|
||||
kernelCode equ 0x080F0000
|
||||
buffer equ 0x24000000
|
||||
fileOpen equ 0x4E45504F ;dummy
|
||||
argv_addr equ 0x27FFDF00
|
||||
fname_addr equ 0x27FFDF80
|
||||
low_tid_addr equ 0x27FFDFE0
|
||||
copy_launch_stub_addr equ 0x27FFE000
|
||||
|
||||
.create "reboot.bin", 0
|
||||
firm_addr equ 0x24000000
|
||||
firm_maxsize equ (copy_launch_stub_addr - 0x1000 - firm_addr)
|
||||
|
||||
arm11_entrypoint_addr equ 0x1FFFFFFC
|
||||
.create "build/reboot.bin", 0
|
||||
.arm
|
||||
//Code jumps here right after the sprintf call
|
||||
process9Reboot:
|
||||
doPxi:
|
||||
ldr r4, =0x44846
|
||||
ldr r0, =0x10008000
|
||||
readPxiLoop1:
|
||||
ldrh r1, [r0,#4]
|
||||
.word 0xE1B01B81 //lsls r1, r1, #0x17
|
||||
bmi readPxiLoop1
|
||||
ldr r0, [r0,#0xC]
|
||||
cmp r0, r4
|
||||
bne doPxi
|
||||
|
||||
GetFirmPath:
|
||||
add r0, sp, #0x3A8-0x70+0x24
|
||||
ldr r1, [r0], #4
|
||||
ldr r2, =0x00300030
|
||||
cmp r1, r2
|
||||
ldreq r1, [r0], #4
|
||||
ldreq r2, =0x002F0032
|
||||
cmpeq r1, r2
|
||||
|
||||
OpenFirm:
|
||||
ldreq r1, =(FileName - OpenFirm - 12)
|
||||
addeq r1, pc
|
||||
addne r1, sp, #0x3A8-0x70
|
||||
moveq r2, #1
|
||||
movne r2, #0
|
||||
str r2, [externalFirm]
|
||||
mov r2, #1
|
||||
add r0, r7, #8
|
||||
ldr r6, =fileOpen
|
||||
blx r6
|
||||
|
||||
SeekFirm:
|
||||
ldr r0, [externalFirm]
|
||||
cmp r0, #1
|
||||
moveq r0, r7
|
||||
ldreq r1, =byteswritten
|
||||
ldreq r2, =buffer
|
||||
ldreq r3, =0x0
|
||||
ldreq r6, [sp,#0x3A8-0x198]
|
||||
ldreq r6, [r6,#0x28] //fread function stored here
|
||||
blxeq r6
|
||||
|
||||
ReadFirm:
|
||||
mov r0, r7
|
||||
ldr r1, =byteswritten
|
||||
ldr r2, =buffer
|
||||
ldr r3, =0x200000
|
||||
ldr r6, [sp,#0x3A8-0x198]
|
||||
ldr r6, [r6,#0x28] //fread function stored here
|
||||
blx r6
|
||||
; Interesting registers and locations to keep in mind, set just before this code is ran:
|
||||
; - r1: FIRM path in exefs.
|
||||
; - r7 (or r8): pointer to file object
|
||||
; - *r7: vtable
|
||||
; - *(vtable + 0x28): fread function
|
||||
; - *(r7 + 8): file handle
|
||||
|
||||
KernelSetState:
|
||||
mov r2, #0
|
||||
mov r3, r2
|
||||
mov r1, r2
|
||||
mov r0, r2
|
||||
.word 0xEF00007C //SVC 0x7C
|
||||
sub r7, r0, #8
|
||||
mov r8, r1
|
||||
|
||||
GoToReboot:
|
||||
ldr r0, =(KernelCodeStart - GoToReboot - 12)
|
||||
add r0, pc
|
||||
ldr r1, =kernelCode
|
||||
ldr r2, =0x300
|
||||
bl Memcpy
|
||||
|
||||
ldr r0, =kernelCode
|
||||
.word 0xEF00007B //SVC 0x7B
|
||||
pxi_wait_recv:
|
||||
ldr r2, =0x44846
|
||||
ldr r0, =0x10008000
|
||||
readPxiLoop1:
|
||||
ldrh r1, [r0, #4]
|
||||
lsls r1, #0x17
|
||||
bmi readPxiLoop1
|
||||
ldr r0, [r0, #0xC]
|
||||
cmp r0, r2
|
||||
bne pxi_wait_recv
|
||||
|
||||
InfiniteLoop:
|
||||
b InfiniteLoop
|
||||
; Open file
|
||||
add r0, r7, #8
|
||||
adr r1, fname
|
||||
mov r2, #1
|
||||
ldr r6, [fopen]
|
||||
orr r6, 1
|
||||
blx r6
|
||||
cmp r0, #0
|
||||
bne panic
|
||||
|
||||
Memcpy:
|
||||
MOV R12, LR
|
||||
STMFD SP!, {R0-R4}
|
||||
ADD R2, R2, R0
|
||||
; Read file
|
||||
mov r0, r7
|
||||
adr r1, bytes_read
|
||||
ldr r2, =firm_addr
|
||||
ldr r3, =firm_maxsize
|
||||
ldr r6, [r7]
|
||||
ldr r6, [r6, #0x28]
|
||||
blx r6
|
||||
|
||||
memcpyLoop:
|
||||
LDR R3, [R0],#4
|
||||
STR R3, [R1],#4
|
||||
CMP R0, R2
|
||||
BLT memcpyLoop
|
||||
LDMFD SP!, {R0-R4}
|
||||
MOV LR, R12
|
||||
BX LR
|
||||
; Copy the low TID (in UTF-16) of the wanted firm to the 5th byte of the payload
|
||||
ldr r0, =low_tid_addr
|
||||
add r1, r8, #0x1A
|
||||
mov r2, #0x10
|
||||
bl memcpy16
|
||||
|
||||
FileName:
|
||||
.dcw "sdmc:/aurei/patched_firmware_sys.bin"
|
||||
.word 0x0
|
||||
ldr r0, =fname_addr
|
||||
adr r1, fname
|
||||
mov r2, #42
|
||||
bl memcpy16
|
||||
|
||||
externalFirm:
|
||||
.word 0x2000A000
|
||||
ldr r0, =argv_addr
|
||||
ldr r1, =fname_addr
|
||||
ldr r2, =low_tid_addr
|
||||
stmia r0, {r1, r2}
|
||||
|
||||
; Set kernel state
|
||||
mov r0, #0
|
||||
mov r1, #0
|
||||
mov r2, #0
|
||||
mov r3, #0
|
||||
swi 0x7C
|
||||
|
||||
goto_reboot:
|
||||
; Jump to reboot code
|
||||
ldr r0, =(kernelcode_start - goto_reboot - 12)
|
||||
add r0, pc ; pc is two instructions ahead of the instruction being executed (12 = 2*4 + 4)
|
||||
swi 0x7B
|
||||
|
||||
die:
|
||||
b die
|
||||
|
||||
memcpy16:
|
||||
cmp r2, #0
|
||||
bxeq lr
|
||||
add r2, r0, r2
|
||||
copy_loop16:
|
||||
ldrh r3, [r1], #2
|
||||
strh r3, [r0], #2
|
||||
cmp r0, r2
|
||||
blo copy_loop16
|
||||
bx lr
|
||||
|
||||
panic:
|
||||
mov r1, r0 ; unused register
|
||||
mov r0, #0
|
||||
swi 0x3C ; svcBreak(USERBREAK_PANIC)
|
||||
b die
|
||||
|
||||
bytes_read: .word 0
|
||||
fopen: .ascii "OPEN"
|
||||
.pool
|
||||
|
||||
// Kernel Code
|
||||
.area 82, 0
|
||||
fname: .ascii "FILE"
|
||||
.endarea
|
||||
|
||||
.pool
|
||||
nand_mount: .dcw "nand"
|
||||
|
||||
.align 4
|
||||
KernelCodeStart:
|
||||
memorySetting:
|
||||
MRC p15, 0, R0,c2,c0, 0
|
||||
MRC p15, 0, R12,c2,c0, 1
|
||||
MRC p15, 0, R1,c3,c0, 0
|
||||
MRC p15, 0, R2,c5,c0, 2
|
||||
MRC p15, 0, R3,c5,c0, 3
|
||||
LDR R4, =0x18000035
|
||||
BIC R2, R2, #0xF0000
|
||||
BIC R3, R3, #0xF0000
|
||||
ORR R0, R0, #0x10
|
||||
ORR R2, R2, #0x30000
|
||||
ORR R3, R3, #0x30000
|
||||
ORR R12, R12, #0x10
|
||||
ORR R1, R1, #0x10
|
||||
MCR p15, 0, R0,c2,c0, 0
|
||||
MCR p15, 0, R12,c2,c0, 1
|
||||
MCR p15, 0, R1,c3,c0, 0
|
||||
MCR p15, 0, R2,c5,c0, 2
|
||||
MCR p15, 0, R3,c5,c0, 3
|
||||
MCR p15, 0, R4,c6,c4, 0
|
||||
MRC p15, 0, R0,c2,c0, 0
|
||||
MRC p15, 0, R1,c2,c0, 1
|
||||
MRC p15, 0, R2,c3,c0, 0
|
||||
ORR R0, R0, #0x20
|
||||
ORR R1, R1, #0x20
|
||||
ORR R2, R2, #0x20
|
||||
MCR p15, 0, R0,c2,c0, 0
|
||||
MCR p15, 0, R1,c2,c0, 1
|
||||
MCR p15, 0, R2,c3,c0, 0
|
||||
kernelcode_start:
|
||||
|
||||
copyFirmPartitions:
|
||||
LDR R4, =buffer
|
||||
ADD R3, R4, #0x40
|
||||
LDR R0, [R3]
|
||||
ADD R0, R0, R4
|
||||
LDR R1, [R3,#4]
|
||||
LDR R2, [R3,#8]
|
||||
bl KernelMemcpy
|
||||
|
||||
ADD R3, R4, #0x70
|
||||
LDR R0, [R3]
|
||||
ADD R0, R0, R4
|
||||
LDR R1, [R3,#4]
|
||||
LDR R2, [R3,#8]
|
||||
bl KernelMemcpy
|
||||
|
||||
ADD R3, R4, #0xA0
|
||||
LDR R0, [R3]
|
||||
ADD R0, R0, R4
|
||||
LDR R1, [R3,#4]
|
||||
LDR R2, [R3,#8]
|
||||
bl KernelMemcpy
|
||||
|
||||
ADD R3, R4, #0xD0
|
||||
LDR R0, [R3]
|
||||
CMP R0, #0
|
||||
BEQ invalidateDataCache
|
||||
ADD R0, R0, R4
|
||||
LDR R1, [R3,#4]
|
||||
LDR R2, [R3,#8]
|
||||
bl KernelMemcpy
|
||||
|
||||
invalidateDataCache:
|
||||
MOV R2, #0
|
||||
MOV R1, R2
|
||||
loc_809460C:
|
||||
MOV R0, #0
|
||||
MOV R3, R2,LSL#30
|
||||
loc_8094614:
|
||||
ORR R12, R3, R0,LSL#5
|
||||
MCR p15, 0, R1,c7,c10, 4
|
||||
MCR p15, 0, R12,c7,c14, 2
|
||||
ADD R0, R0, #1
|
||||
CMP R0, #0x20
|
||||
BCC loc_8094614
|
||||
ADD R2, R2, #1
|
||||
CMP R2, #4
|
||||
BCC loc_809460C
|
||||
mrs r0, cpsr ; disable interrupts
|
||||
orr r0, #0xC0
|
||||
msr cpsr, r0
|
||||
|
||||
jumpToEntrypoint:
|
||||
MCR p15, 0, R1,c7,c10, 4
|
||||
LDR R0, =0x42078
|
||||
MCR p15, 0, R0,c1,c0, 0
|
||||
MCR p15, 0, R1,c7,c5, 0
|
||||
MCR p15, 0, R1,c7,c6, 0
|
||||
MCR p15, 0, R1,c7,c10, 4
|
||||
LDR R4, =buffer
|
||||
MOV R1, #0x1FFFFFFC
|
||||
LDR R2, [R4,#8]
|
||||
STR R2, [R1]
|
||||
LDR R0, [R4,#0xC]
|
||||
BX R0
|
||||
.pool
|
||||
ldr sp, =0x27FFDF00
|
||||
|
||||
KernelMemcpy:
|
||||
MOV R12, LR
|
||||
STMFD SP!, {R0-R4}
|
||||
ADD R2, R2, R0
|
||||
ldr r0, =copy_launch_stub_addr
|
||||
adr r1, copy_launch_stub
|
||||
mov r2, #(copy_launch_stub_end - copy_launch_stub)
|
||||
bl memcpy32
|
||||
|
||||
kmemcpyLoop:
|
||||
LDR R3, [R0],#4
|
||||
STR R3, [R1],#4
|
||||
CMP R0, R2
|
||||
BLT kmemcpyLoop
|
||||
LDMFD SP!, {R0-R4}
|
||||
MOV LR, R12
|
||||
BX LR
|
||||
.pool
|
||||
; Disable MPU
|
||||
ldr r0, =0x42078 ; alt vector select, enable itcm
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
|
||||
KernelCodeEnd:
|
||||
bl flushCaches
|
||||
|
||||
ldr r0, =copy_launch_stub_addr
|
||||
bx r0
|
||||
|
||||
copy_launch_stub:
|
||||
|
||||
ldr r4, =firm_addr
|
||||
|
||||
mov r5, #0
|
||||
load_section_loop:
|
||||
; Such checks. Very ghetto. Wow.
|
||||
add r3, r4, #0x40
|
||||
add r3, r5,lsl #5
|
||||
add r3, r5,lsl #4
|
||||
ldmia r3, {r6-r8}
|
||||
mov r0, r7
|
||||
add r1, r4, r6
|
||||
mov r2, r8
|
||||
bl memcpy32
|
||||
add r5, #1
|
||||
cmp r5, #3
|
||||
blo load_section_loop
|
||||
|
||||
ldr r0, =arm11_entrypoint_addr
|
||||
ldr r1, [r4, #0x08]
|
||||
str r1, [r0]
|
||||
|
||||
mov r0, #2 ; argc
|
||||
ldr r1, =argv_addr ; argv
|
||||
ldr r2, =0xBEEF ; magic word
|
||||
|
||||
ldr r5, =arm11_entrypoint_addr
|
||||
ldr r6, [r4, #0x08]
|
||||
str r6, [r5]
|
||||
|
||||
ldr lr, [r4, #0x0c]
|
||||
bx lr
|
||||
|
||||
memcpy32:
|
||||
cmp r2, #0
|
||||
bxeq lr
|
||||
add r2, r0, r2
|
||||
copy_loop32:
|
||||
ldr r3, [r1], #4
|
||||
str r3, [r0], #4
|
||||
cmp r0, r2
|
||||
blo copy_loop32
|
||||
bx lr
|
||||
|
||||
.pool
|
||||
|
||||
copy_launch_stub_end:
|
||||
|
||||
flushCaches:
|
||||
|
||||
; Clean and flush data cache
|
||||
mov r1, #0 ; segment counter
|
||||
outer_loop:
|
||||
mov r0, #0 ; line counter
|
||||
|
||||
inner_loop:
|
||||
orr r2, r1, r0 ; generate segment and line address
|
||||
mcr p15, 0, r2, c7, c14, 2 ; clean and flush the line
|
||||
add r0, #0x20 ; increment to next line
|
||||
cmp r0, #0x400
|
||||
bne inner_loop
|
||||
|
||||
add r1, #0x40000000
|
||||
cmp r1, #0
|
||||
bne outer_loop
|
||||
|
||||
; Drain write buffer
|
||||
mcr p15, 0, r1, c7, c10, 4
|
||||
|
||||
; Flush instruction cache
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
|
||||
bx lr
|
||||
|
||||
.close
|
||||
|
||||
48
patches/svcGetCFWInfo.s
Normal file
48
patches/svcGetCFWInfo.s
Normal file
@@ -0,0 +1,48 @@
|
||||
;
|
||||
; This file is part of Luma3DS
|
||||
; Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
;
|
||||
; This program is free software: you can redistribute it and/or modify
|
||||
; it under the terms of the GNU General Public License as published by
|
||||
; the Free Software Foundation, either version 3 of the License, or
|
||||
; (at your option) any later version.
|
||||
;
|
||||
; This program is distributed in the hope that it will be useful,
|
||||
; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
; GNU General Public License for more details.
|
||||
;
|
||||
; You should have received a copy of the GNU General Public License
|
||||
; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
;
|
||||
; Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
; reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
; Notices displayed by works containing it.
|
||||
;
|
||||
|
||||
.arm.little
|
||||
|
||||
.create "build/svcGetCFWInfo.bin", 0
|
||||
.arm
|
||||
|
||||
adr r1, infoStart
|
||||
add r2, r0, #(infoEnd - infoStart)
|
||||
|
||||
loop:
|
||||
ldrb r3, [r1], #1
|
||||
strbt r3, [r0], #1
|
||||
cmp r0, r2
|
||||
blo loop
|
||||
|
||||
mov r0, #0
|
||||
|
||||
bx lr
|
||||
|
||||
.pool
|
||||
infoStart:
|
||||
.ascii "LUMA" ; magic
|
||||
.word 0 ; version
|
||||
.word 0 ; truncated commit hash
|
||||
.word 0 ; config
|
||||
infoEnd:
|
||||
.close
|
||||
163
source/3dsheaders.h
Normal file
163
source/3dsheaders.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Adapted from 3DBrew and https://github.com/mid-kid/CakesForeveryWan/blob/master/source/headers.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 address;
|
||||
u32 phyRegionSize;
|
||||
u32 size;
|
||||
} CodeSetInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 saveDataSize[2];
|
||||
u32 jumpID[2];
|
||||
u8 reserved[0x30];
|
||||
} SystemInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char appTitle[8];
|
||||
u8 reserved1[5];
|
||||
u8 flag;
|
||||
u8 remasterVersion[2];
|
||||
CodeSetInfo textCodeSet;
|
||||
u32 stackSize;
|
||||
CodeSetInfo roCodeSet;
|
||||
u8 reserved2[4];
|
||||
CodeSetInfo dataCodeSet;
|
||||
u32 bssSize;
|
||||
char depends[0x180];
|
||||
SystemInfo systemInfo;
|
||||
} SystemControlInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
SystemControlInfo systemControlInfo;
|
||||
u8 aci[0x200];
|
||||
u8 accessDescSig[0x100];
|
||||
u8 ncchPubKey[0x100];
|
||||
u8 aciLim[0x200];
|
||||
} ExHeader;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u8 sig[0x100]; //RSA-2048 signature of the NCCH header, using SHA-256
|
||||
char magic[4]; //NCCH
|
||||
u32 contentSize; //Media unit
|
||||
u8 partitionId[8];
|
||||
u8 makerCode[2];
|
||||
u16 version;
|
||||
u8 reserved1[4];
|
||||
u8 programID[8];
|
||||
u8 reserved2[0x10];
|
||||
u8 logoHash[0x20]; //Logo Region SHA-256 hash
|
||||
char productCode[0x10];
|
||||
u8 exHeaderHash[0x20]; //Extended header SHA-256 hash
|
||||
u32 exHeaderSize; //Extended header size
|
||||
u32 reserved3;
|
||||
u8 flags[8];
|
||||
u32 plainOffset; //Media unit
|
||||
u32 plainSize; //Media unit
|
||||
u32 logoOffset; //Media unit
|
||||
u32 logoSize; //Media unit
|
||||
u32 exeFsOffset; //Media unit
|
||||
u32 exeFsSize; //Media unit
|
||||
u32 exeFsHashSize; //Media unit
|
||||
u32 reserved4;
|
||||
u32 romFsOffset; //Media unit
|
||||
u32 romFsSize; //Media unit
|
||||
u32 romFsHashSize; //Media unit
|
||||
u32 reserved5;
|
||||
u8 exeFsHash[0x20]; //ExeFS superblock SHA-256 hash
|
||||
u8 romFsHash[0x20]; //RomFS superblock SHA-256 hash
|
||||
} Ncch;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
Ncch ncch;
|
||||
ExHeader exHeader;
|
||||
} Cxi;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char sigIssuer[0x40];
|
||||
u8 eccPubKey[0x3C];
|
||||
u8 version;
|
||||
u8 caCrlVersion;
|
||||
u8 signerCrlVersion;
|
||||
u8 titleKey[0x10];
|
||||
u8 reserved1;
|
||||
u8 ticketId[8];
|
||||
u8 consoleId[4];
|
||||
u8 titleId[8];
|
||||
u8 reserved2[2];
|
||||
u16 ticketTitleVersion;
|
||||
u8 reserved3[8];
|
||||
u8 licenseType;
|
||||
u8 ticketCommonKeyYIndex; //Ticket common keyY index, usually 0x1 for retail system titles.
|
||||
u8 reserved4[0x2A];
|
||||
u8 unk[4]; //eShop Account ID?
|
||||
u8 reserved5;
|
||||
u8 audit;
|
||||
u8 reserved6[0x42];
|
||||
u8 limits[0x40];
|
||||
u8 contentIndex[0xAC];
|
||||
} Ticket;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 offset;
|
||||
u8 *address;
|
||||
u32 size;
|
||||
u32 procType;
|
||||
u8 hash[0x20];
|
||||
} FirmSection;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic[4];
|
||||
u32 reserved1;
|
||||
u8 *arm11Entry;
|
||||
u8 *arm9Entry;
|
||||
u8 reserved2[0x30];
|
||||
FirmSection section[4];
|
||||
} Firm;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u8 keyX[0x10];
|
||||
u8 keyY[0x10];
|
||||
u8 ctr[0x10];
|
||||
char size[8];
|
||||
u8 reserved[8];
|
||||
u8 ctlBlock[0x10];
|
||||
char magic[4];
|
||||
u8 reserved2[0xC];
|
||||
u8 slot0x16keyX[0x10];
|
||||
} Arm9Bin;
|
||||
@@ -1,21 +1,47 @@
|
||||
/*
|
||||
* buttons.h
|
||||
* by Aurora Wright
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define HID_PAD (*(vu16 *)0x10146000 ^ 0xFFF)
|
||||
#define BUTTON_R1 (1 << 8)
|
||||
#define BUTTON_L1 (1 << 9)
|
||||
#define BUTTON_A 1
|
||||
#define BUTTON_B (1 << 1)
|
||||
#define BUTTON_UP (1 << 6)
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
#define BUTTON_START (1 << 3)
|
||||
#define BUTTON_SELECT (1 << 2)
|
||||
#define SAFE_MODE (BUTTON_R1 | BUTTON_L1 | BUTTON_A | BUTTON_UP)
|
||||
#define PAYLOAD_BUTTONS ((BUTTON_L1 | BUTTON_A) ^ 0xFFF)
|
||||
#define HID_PAD (*(vu32 *)0x10146000 ^ 0xFFF)
|
||||
|
||||
#define BUTTON_R1 (1 << 8)
|
||||
#define BUTTON_L1 (1 << 9)
|
||||
#define BUTTON_A (1 << 0)
|
||||
#define BUTTON_B (1 << 1)
|
||||
#define BUTTON_X (1 << 10)
|
||||
#define BUTTON_Y (1 << 11)
|
||||
#define BUTTON_SELECT (1 << 2)
|
||||
#define BUTTON_START (1 << 3)
|
||||
#define BUTTON_RIGHT (1 << 4)
|
||||
#define BUTTON_LEFT (1 << 5)
|
||||
#define BUTTON_UP (1 << 6)
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
|
||||
#define DPAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN)
|
||||
#define SAFE_MODE (BUTTON_R1 | BUTTON_L1 | BUTTON_A | BUTTON_UP)
|
||||
#define SINGLE_PAYLOAD_BUTTONS (DPAD_BUTTONS | BUTTON_B | BUTTON_X | BUTTON_Y)
|
||||
#define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_A | BUTTON_START | BUTTON_SELECT)
|
||||
#define MENU_BUTTONS (DPAD_BUTTONS | BUTTON_A | BUTTON_START)
|
||||
#define PIN_BUTTONS (BUTTON_A | BUTTON_B | BUTTON_X | BUTTON_Y | DPAD_BUTTONS | BUTTON_START | BUTTON_SELECT)
|
||||
39
source/cache.h
Normal file
39
source/cache.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/***
|
||||
The following functions flush the data cache, then waits for all memory transfers to be finished.
|
||||
The data cache and/or the instruction cache MUST be flushed before doing one of the following:
|
||||
- rebooting
|
||||
- powering down
|
||||
- setting the ARM11 entrypoint to execute a function
|
||||
- jumping to a payload
|
||||
***/
|
||||
|
||||
void flushEntireDCache(void); //actually: "clean and flush"
|
||||
void flushDCacheRange(void *startAddress, u32 size);
|
||||
void flushEntireICache(void);
|
||||
void flushICacheRange(void *startAddress, u32 size);
|
||||
89
source/cache.s
Normal file
89
source/cache.s
Normal file
@@ -0,0 +1,89 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global flushEntireDCache
|
||||
.type flushEntireDCache, %function
|
||||
flushEntireDCache:
|
||||
@ Adapted from http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0155a/ch03s03s05.html,
|
||||
@ and https://github.com/gemarcano/libctr9_io/blob/master/src/ctr_system_ARM.c#L39 as well
|
||||
@ Note: ARM's example is actually for a 8KB DCache (which is what the 3DS has)
|
||||
|
||||
@ Implemented in bootROM at address 0xffff0830
|
||||
mov r1, #0 @ segment counter
|
||||
outer_loop:
|
||||
mov r0, #0 @ line counter
|
||||
|
||||
inner_loop:
|
||||
orr r2, r1, r0 @ generate segment and line address
|
||||
mcr p15, 0, r2, c7, c14, 2 @ clean and flush the line
|
||||
add r0, #0x20 @ increment to next line
|
||||
cmp r0, #0x400
|
||||
bne inner_loop
|
||||
|
||||
add r1, #0x40000000
|
||||
cmp r1, #0
|
||||
bne outer_loop
|
||||
|
||||
mcr p15, 0, r1, c7, c10, 4 @ drain write buffer
|
||||
bx lr
|
||||
|
||||
.global flushDCacheRange
|
||||
.type flushDCacheRange, %function
|
||||
flushDCacheRange:
|
||||
@ Implemented in bootROM at address 0xffff08a0
|
||||
add r1, r0, r1 @ end address
|
||||
bic r0, #0x1f @ align source address to cache line size (32 bytes)
|
||||
|
||||
flush_dcache_range_loop:
|
||||
mcr p15, 0, r0, c7, c14, 1 @ clean and flush the line corresponding to the address r0 is holding
|
||||
add r0, #0x20
|
||||
cmp r0, r1
|
||||
blo flush_dcache_range_loop
|
||||
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
|
||||
bx lr
|
||||
|
||||
.global flushEntireICache
|
||||
.type flushEntireICache, %function
|
||||
flushEntireICache:
|
||||
@ Implemented in bootROM at address 0xffff0ab4
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c5, 0
|
||||
bx lr
|
||||
|
||||
.global flushICacheRange
|
||||
.type flushICacheRange, %function
|
||||
flushICacheRange:
|
||||
@ Implemented in bootROM at address 0xffff0ac0
|
||||
add r1, r0, r1 @ end address
|
||||
bic r0, #0x1f @ align source address to cache line size (32 bytes)
|
||||
|
||||
flush_icache_range_loop:
|
||||
mcr p15, 0, r0, c7, c5, 1 @ flush the line corresponding to the address r0 is holding
|
||||
add r0, #0x20
|
||||
cmp r0, r1
|
||||
blo flush_icache_range_loop
|
||||
|
||||
bx lr
|
||||
399
source/config.c
Normal file
399
source/config.c
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "memory.h"
|
||||
#include "fs.h"
|
||||
#include "utils.h"
|
||||
#include "screen.h"
|
||||
#include "draw.h"
|
||||
#include "buttons.h"
|
||||
#include "pin.h"
|
||||
|
||||
CfgData configData;
|
||||
ConfigurationStatus needConfig;
|
||||
static u32 oldConfig;
|
||||
|
||||
bool readConfig(void)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if(fileRead(&configData, CONFIG_FILE, sizeof(CfgData)) != sizeof(CfgData) ||
|
||||
memcmp(configData.magic, "CONF", 4) != 0 ||
|
||||
configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
|
||||
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
|
||||
{
|
||||
configData.config = 0;
|
||||
|
||||
ret = false;
|
||||
}
|
||||
else ret = true;
|
||||
|
||||
oldConfig = configData.config;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void writeConfig(bool isPayloadLaunch)
|
||||
{
|
||||
if(isPayloadLaunch) configData.config = (configData.config & 0xFFFFFF80) | (oldConfig & 0x7F);
|
||||
|
||||
/* If the configuration is different from previously, overwrite it.
|
||||
Just the no-forcing flag being set is not enough */
|
||||
if(needConfig != CREATE_CONFIGURATION && (configData.config & 0xFFFFFFBF) == oldConfig) return;
|
||||
|
||||
if(needConfig == CREATE_CONFIGURATION)
|
||||
{
|
||||
memcpy(configData.magic, "CONF", 4);
|
||||
configData.formatVersionMajor = CONFIG_VERSIONMAJOR;
|
||||
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
|
||||
}
|
||||
|
||||
if(!fileWrite(&configData, CONFIG_FILE, sizeof(CfgData)))
|
||||
error("Error writing the configuration file");
|
||||
}
|
||||
|
||||
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
{
|
||||
const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
|
||||
"Screen brightness: 4( ) 3( ) 2( ) 1( )",
|
||||
"Splash: Off( ) Before( ) After( ) payloads",
|
||||
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
|
||||
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )",
|
||||
};
|
||||
|
||||
const char *singleOptionsText[] = { "( ) Autoboot EmuNAND",
|
||||
"( ) Use EmuNAND FIRM if booting with R",
|
||||
"( ) Enable loading external FIRMs and modules",
|
||||
"( ) Enable game patching",
|
||||
"( ) Show NAND or user string in System Settings",
|
||||
"( ) Show GBA boot screen in patched AGB_FIRM",
|
||||
"( ) Patch SVC/service/archive/ARM9 access",
|
||||
"( ) Set developer UNITINFO",
|
||||
"( ) Enable exception handlers"
|
||||
};
|
||||
|
||||
const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
|
||||
"It will be booted when no\n"
|
||||
"directional pad buttons are pressed.",
|
||||
|
||||
"Select the screen brightness.",
|
||||
|
||||
"Enable splash screen support.\n\n"
|
||||
"\t* 'Before payloads' displays it\n"
|
||||
"before booting payloads\n"
|
||||
"(intended for splashes that display\n"
|
||||
"button hints).\n\n"
|
||||
"\t* 'After payloads' displays it\n"
|
||||
"afterwards.",
|
||||
|
||||
"Activate a PIN lock.\n\n"
|
||||
"The PIN will be asked each time\n"
|
||||
"Luma3DS boots.\n\n"
|
||||
"4, 6 or 8 digits can be selected.\n\n"
|
||||
"The ABXY buttons and the directional\n"
|
||||
"pad buttons can be used as keys.\n\n"
|
||||
"A message can also be displayed\n"
|
||||
"(refer to the wiki for instructions).",
|
||||
|
||||
"Select the New 3DS CPU mode.\n\n"
|
||||
"This won't apply to\n"
|
||||
"New 3DS exclusive/enhanced games.\n\n"
|
||||
"'Clock+L2' can cause issues with some\n"
|
||||
"games.",
|
||||
|
||||
"If enabled, an EmuNAND\n"
|
||||
"will be launched on boot.\n\n"
|
||||
"Otherwise, SysNAND will.\n\n"
|
||||
"Hold L on boot to switch NAND.\n\n"
|
||||
"To use a different EmuNAND from the\n"
|
||||
"default, hold a directional pad button\n"
|
||||
"(Up/Right/Down/Left equal EmuNANDs\n"
|
||||
"1/2/3/4).",
|
||||
|
||||
"If enabled, when holding R on boot\n"
|
||||
"SysNAND will be booted with an\n"
|
||||
"EmuNAND FIRM.\n\n"
|
||||
"Otherwise, an EmuNAND will be booted\n"
|
||||
"with the SysNAND FIRM.\n\n"
|
||||
"To use a different EmuNAND from the\n"
|
||||
"default, hold a directional pad button\n"
|
||||
"(Up/Right/Down/Left equal EmuNANDs\n"
|
||||
"1/2/3/4), also add A if you have\n"
|
||||
"a matching payload.",
|
||||
|
||||
"Enable loading external FIRMs and\n"
|
||||
"system modules.\n\n"
|
||||
"This isn't needed in most cases.\n\n"
|
||||
"Refer to the wiki for instructions.",
|
||||
|
||||
"Enable overriding the region and\n"
|
||||
"language configuration and the usage\n"
|
||||
"of patched code binaries,\n"
|
||||
"IPS code patches and LayeredFS\n"
|
||||
"for specific games.\n\n"
|
||||
"Also makes certain DLCs\n"
|
||||
"for out-of-region games work.\n\n"
|
||||
"Enabling this requires the\n"
|
||||
"archive patch to be applied.\n\n"
|
||||
"Refer to the wiki for instructions.",
|
||||
|
||||
"Enable showing the current NAND/FIRM:\n\n"
|
||||
"\t* Sys = SysNAND\n"
|
||||
"\t* Emu = EmuNAND 1\n"
|
||||
"\t* EmuX = EmuNAND X\n"
|
||||
"\t* SysE = SysNAND with EmuNAND 1 FIRM\n"
|
||||
"\t* SyEX = SysNAND with EmuNAND X FIRM\n"
|
||||
"\t* EmuS = EmuNAND 1 with SysNAND FIRM\n"
|
||||
"\t* EmXS = EmuNAND X with SysNAND FIRM\n\n"
|
||||
"or an user-defined custom string in\n"
|
||||
"System Settings.\n\n"
|
||||
"Refer to the wiki for instructions.",
|
||||
|
||||
"Enable showing the GBA boot screen\n"
|
||||
"when booting GBA games.",
|
||||
|
||||
"Disable SVC, service, archive and ARM9\n"
|
||||
"exheader access checks.\n\n"
|
||||
"The service and archive patches\n"
|
||||
"don't work on New 3DS FIRMs between\n"
|
||||
"9.3 and 10.4.\n\n"
|
||||
"Only select this if you know what you\n"
|
||||
"are doing!",
|
||||
|
||||
"Make the console be always detected\n"
|
||||
"as a development unit, and conversely.\n"
|
||||
"(which breaks online features, amiibo\n"
|
||||
"and retail CIAs, but allows installing\n"
|
||||
"and booting some developer software).\n\n"
|
||||
"Only select this if you know what you\n"
|
||||
"are doing!",
|
||||
|
||||
"Enable Luma3DS's ARM9/ARM11 exception\n"
|
||||
"handlers. Luma3DS should be ran as\n"
|
||||
"boot.firm\n\n"
|
||||
"Useful for debugging."
|
||||
};
|
||||
|
||||
struct multiOption {
|
||||
u32 posXs[4];
|
||||
u32 posY;
|
||||
u32 enabled;
|
||||
bool visible;
|
||||
} multiOptions[] = {
|
||||
{ .posXs = {19, 24, 29, 34}, .visible = isSdMode },
|
||||
{ .posXs = {21, 26, 31, 36}, .visible = true },
|
||||
{ .posXs = {12, 22, 31, 0}, .visible = true },
|
||||
{ .posXs = {14, 19, 24, 29}, .visible = true },
|
||||
{ .posXs = {17, 26, 32, 44}, .visible = ISN3DS },
|
||||
};
|
||||
|
||||
struct singleOption {
|
||||
u32 posY;
|
||||
bool enabled;
|
||||
bool visible;
|
||||
} singleOptions[] = {
|
||||
{ .visible = isSdMode },
|
||||
{ .visible = isSdMode },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true }
|
||||
};
|
||||
|
||||
//Calculate the amount of the various kinds of options and pre-select the first single one
|
||||
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
|
||||
singleOptionsAmount = sizeof(singleOptions) / sizeof(struct singleOption),
|
||||
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
|
||||
selectedOption,
|
||||
singleSelected;
|
||||
bool isMultiOption = false;
|
||||
|
||||
//Parse the existing options
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
multiOptions[i].enabled = MULTICONFIG(i);
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
singleOptions[i].enabled = CONFIG(i);
|
||||
|
||||
initScreens();
|
||||
|
||||
drawString(true, 10, 10, COLOR_TITLE, CONFIG_TITLE);
|
||||
drawString(true, 10, 10 + SPACING_Y, COLOR_TITLE, "Press A to select, START to save");
|
||||
|
||||
//Character to display a selected option
|
||||
char selected = 'x';
|
||||
|
||||
u32 endPos = 10 + 2 * SPACING_Y;
|
||||
|
||||
//Display all the multiple choice options in white
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
{
|
||||
if(!multiOptions[i].visible) continue;
|
||||
|
||||
multiOptions[i].posY = endPos + SPACING_Y;
|
||||
endPos = drawString(true, 10, multiOptions[i].posY, COLOR_WHITE, multiOptionsText[i]);
|
||||
drawCharacter(true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE, selected);
|
||||
}
|
||||
|
||||
endPos += SPACING_Y / 2;
|
||||
|
||||
//Display all the normal options in white except for the first one
|
||||
for(u32 i = 0, color = COLOR_RED; i < singleOptionsAmount; i++)
|
||||
{
|
||||
if(!singleOptions[i].visible) continue;
|
||||
|
||||
singleOptions[i].posY = endPos + SPACING_Y;
|
||||
endPos = drawString(true, 10, singleOptions[i].posY, color, singleOptionsText[i]);
|
||||
if(singleOptions[i].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[i].posY, color, selected);
|
||||
|
||||
if(color == COLOR_RED)
|
||||
{
|
||||
singleSelected = i;
|
||||
selectedOption = i + multiOptionsAmount;
|
||||
color = COLOR_WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
drawString(false, 10, 10, COLOR_WHITE, optionsDescription[selectedOption]);
|
||||
|
||||
//Boring configuration menu
|
||||
while(true)
|
||||
{
|
||||
u32 pressed;
|
||||
do
|
||||
{
|
||||
pressed = waitInput(true);
|
||||
}
|
||||
while(!(pressed & MENU_BUTTONS));
|
||||
|
||||
if(pressed == BUTTON_START) break;
|
||||
|
||||
if(pressed != BUTTON_A)
|
||||
{
|
||||
//Remember the previously selected option
|
||||
u32 oldSelectedOption = selectedOption;
|
||||
|
||||
while(true)
|
||||
{
|
||||
switch(pressed)
|
||||
{
|
||||
case BUTTON_UP:
|
||||
selectedOption = !selectedOption ? totalIndexes : selectedOption - 1;
|
||||
break;
|
||||
case BUTTON_DOWN:
|
||||
selectedOption = selectedOption == totalIndexes ? 0 : selectedOption + 1;
|
||||
break;
|
||||
case BUTTON_LEFT:
|
||||
pressed = BUTTON_DOWN;
|
||||
selectedOption = 0;
|
||||
break;
|
||||
case BUTTON_RIGHT:
|
||||
pressed = BUTTON_UP;
|
||||
selectedOption = totalIndexes;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
{
|
||||
if(!multiOptions[selectedOption].visible) continue;
|
||||
|
||||
isMultiOption = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
singleSelected = selectedOption - multiOptionsAmount;
|
||||
|
||||
if(!singleOptions[singleSelected].visible) continue;
|
||||
|
||||
isMultiOption = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(selectedOption == oldSelectedOption) continue;
|
||||
|
||||
//The user moved to a different option, print the old option in white and the new one in red. Only print 'x's if necessary
|
||||
if(oldSelectedOption < multiOptionsAmount)
|
||||
{
|
||||
drawString(true, 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE, multiOptionsText[oldSelectedOption]);
|
||||
drawCharacter(true, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE, selected);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 singleOldSelected = oldSelectedOption - multiOptionsAmount;
|
||||
drawString(true, 10, singleOptions[singleOldSelected].posY, COLOR_WHITE, singleOptionsText[singleOldSelected]);
|
||||
if(singleOptions[singleOldSelected].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE, selected);
|
||||
}
|
||||
|
||||
if(isMultiOption) drawString(true, 10, multiOptions[selectedOption].posY, COLOR_RED, multiOptionsText[selectedOption]);
|
||||
else drawString(true, 10, singleOptions[singleSelected].posY, COLOR_RED, singleOptionsText[singleSelected]);
|
||||
|
||||
drawString(false, 10, 10, COLOR_BLACK, optionsDescription[oldSelectedOption]);
|
||||
drawString(false, 10, 10, COLOR_WHITE, optionsDescription[selectedOption]);
|
||||
}
|
||||
else
|
||||
{
|
||||
//The selected option's status changed, print the 'x's accordingly
|
||||
if(isMultiOption)
|
||||
{
|
||||
u32 oldEnabled = multiOptions[selectedOption].enabled;
|
||||
drawCharacter(true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK, selected);
|
||||
multiOptions[selectedOption].enabled = (oldEnabled == 3 || !multiOptions[selectedOption].posXs[oldEnabled + 1]) ? 0 : oldEnabled + 1;
|
||||
|
||||
if(selectedOption == BRIGHTNESS) updateBrightness(multiOptions[BRIGHTNESS].enabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool oldEnabled = singleOptions[singleSelected].enabled;
|
||||
singleOptions[singleSelected].enabled = !oldEnabled;
|
||||
if(oldEnabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_BLACK, selected);
|
||||
}
|
||||
}
|
||||
|
||||
//In any case, if the current option is enabled (or a multiple choice option is selected) we must display a red 'x'
|
||||
if(isMultiOption) drawCharacter(true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED, selected);
|
||||
else if(singleOptions[singleSelected].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED, selected);
|
||||
}
|
||||
|
||||
//Preserve the last-used boot options (first 9 bits)
|
||||
configData.config &= 0x7F;
|
||||
|
||||
//Parse and write the new configuration
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
configData.config |= multiOptions[i].enabled << (i * 2 + 7);
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 17);
|
||||
|
||||
u32 newPinMode = MULTICONFIG(PIN);
|
||||
|
||||
if(newPinMode != 0) newPin(oldPinStatus && newPinMode == oldPinMode, newPinMode);
|
||||
else if(oldPinStatus) fileDelete(PIN_FILE);
|
||||
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
wait(2000ULL);
|
||||
}
|
||||
70
source/config.h
Normal file
70
source/config.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define CONFIG(a) (((configData.config >> (a + 17)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((configData.config >> (a * 2 + 7)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((configData.config >> a) & b)
|
||||
|
||||
#define CONFIG_FILE "config.bin"
|
||||
#define CONFIG_VERSIONMAJOR 1
|
||||
#define CONFIG_VERSIONMINOR 11
|
||||
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(6, 1)
|
||||
|
||||
enum multiOptions
|
||||
{
|
||||
DEFAULTEMU = 0,
|
||||
BRIGHTNESS,
|
||||
SPLASH,
|
||||
PIN,
|
||||
NEWCPU,
|
||||
};
|
||||
|
||||
enum singleOptions
|
||||
{
|
||||
AUTOBOOTEMU = 0,
|
||||
USEEMUFIRM,
|
||||
LOADEXTFIRMSANDMODULES,
|
||||
PATCHGAMES,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHACCESS,
|
||||
PATCHUNITINFO,
|
||||
ENABLEEXCEPTIONHANDLERS
|
||||
};
|
||||
|
||||
typedef enum ConfigurationStatus
|
||||
{
|
||||
DONT_CONFIGURE = 0,
|
||||
MODIFY_CONFIGURATION,
|
||||
CREATE_CONFIGURATION
|
||||
} ConfigurationStatus;
|
||||
|
||||
bool readConfig(void);
|
||||
void writeConfig(bool isPayloadLaunch);
|
||||
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode);
|
||||
660
source/crypto.c
660
source/crypto.c
@@ -1,308 +1,508 @@
|
||||
/*
|
||||
* crypto.c
|
||||
* by Reisyukaku / Aurora Wright
|
||||
* Crypto libs from http://github.com/b1l1s/ctr
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Crypto libs from http://github.com/b1l1s/ctr
|
||||
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
|
||||
* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c
|
||||
* ctrNandWrite logic adapted from https://github.com/d0k3/GodMode9/blob/master/source/nand/nand.c
|
||||
*/
|
||||
|
||||
#include "crypto.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "utils.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
|
||||
/****************************************************************
|
||||
* Crypto Libs
|
||||
* Crypto libs
|
||||
****************************************************************/
|
||||
|
||||
/* original version by megazig */
|
||||
|
||||
#ifndef __thumb__
|
||||
#define BSWAP32(x) {\
|
||||
__asm__\
|
||||
(\
|
||||
"eor r1, %1, %1, ror #16\n\t"\
|
||||
"bic r1, r1, #0xFF0000\n\t"\
|
||||
"mov %0, %1, ror #8\n\t"\
|
||||
"eor %0, %0, r1, lsr #8\n\t"\
|
||||
:"=r"(x)\
|
||||
:"0"(x)\
|
||||
:"r1"\
|
||||
);\
|
||||
__asm__\
|
||||
(\
|
||||
"eor r1, %1, %1, ror #16\n\t"\
|
||||
"bic r1, r1, #0xFF0000\n\t"\
|
||||
"mov %0, %1, ror #8\n\t"\
|
||||
"eor %0, %0, r1, lsr #8\n\t"\
|
||||
:"=r"(x)\
|
||||
:"0"(x)\
|
||||
:"r1"\
|
||||
);\
|
||||
};
|
||||
|
||||
#define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\
|
||||
__asm__\
|
||||
(\
|
||||
"adds %0, %4\n\t"\
|
||||
"addcss %1, %1, #1\n\t"\
|
||||
"addcss %2, %2, #1\n\t"\
|
||||
"addcs %3, %3, #1\n\t"\
|
||||
: "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\
|
||||
: "r"(u32_0)\
|
||||
: "cc"\
|
||||
);\
|
||||
(\
|
||||
"adds %0, %4\n\t"\
|
||||
"addcss %1, %1, #1\n\t"\
|
||||
"addcss %2, %2, #1\n\t"\
|
||||
"addcs %3, %3, #1\n\t"\
|
||||
: "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\
|
||||
: "r"(u32_0)\
|
||||
: "cc"\
|
||||
);\
|
||||
}
|
||||
#else
|
||||
#define BSWAP32(x) {x = __builtin_bswap32(x);}
|
||||
|
||||
#define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\
|
||||
__asm__\
|
||||
(\
|
||||
"mov r4, #0\n\t"\
|
||||
"add %0, %0, %4\n\t"\
|
||||
"adc %1, %1, r4\n\t"\
|
||||
"adc %2, %2, r4\n\t"\
|
||||
"adc %3, %3, r4\n\t"\
|
||||
: "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\
|
||||
: "r"(u32_0)\
|
||||
: "cc", "r4"\
|
||||
);\
|
||||
(\
|
||||
"mov r4, #0\n\t"\
|
||||
"add %0, %0, %4\n\t"\
|
||||
"adc %1, %1, r4\n\t"\
|
||||
"adc %2, %2, r4\n\t"\
|
||||
"adc %3, %3, r4\n\t"\
|
||||
: "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\
|
||||
: "r"(u32_0)\
|
||||
: "cc", "r4"\
|
||||
);\
|
||||
}
|
||||
#endif /*__thumb__*/
|
||||
|
||||
static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode)
|
||||
{
|
||||
if(keyslot <= 0x03) return; // Ignore TWL keys for now
|
||||
u32 *key32 = (u32 *)key;
|
||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
||||
*REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE;
|
||||
u32 *key32 = (u32 *)key;
|
||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
||||
|
||||
REG_AESKEYFIFO[keyType] = key32[0];
|
||||
REG_AESKEYFIFO[keyType] = key32[1];
|
||||
REG_AESKEYFIFO[keyType] = key32[2];
|
||||
REG_AESKEYFIFO[keyType] = key32[3];
|
||||
if(keyslot <= 3)
|
||||
{
|
||||
if((mode & AES_CNT_INPUT_ORDER) == AES_INPUT_REVERSED)
|
||||
{
|
||||
REGs_AESTWLKEYS[keyslot][keyType][0] = key32[3];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][1] = key32[2];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][2] = key32[1];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][3] = key32[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
REGs_AESTWLKEYS[keyslot][keyType][0] = key32[0];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][1] = key32[1];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][2] = key32[2];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][3] = key32[3];
|
||||
}
|
||||
}
|
||||
|
||||
else if(keyslot < 0x40)
|
||||
{
|
||||
*REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE;
|
||||
|
||||
REG_AESKEYFIFO[keyType] = key32[0];
|
||||
REG_AESKEYFIFO[keyType] = key32[1];
|
||||
REG_AESKEYFIFO[keyType] = key32[2];
|
||||
REG_AESKEYFIFO[keyType] = key32[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_use_keyslot(u8 keyslot)
|
||||
{
|
||||
if(keyslot > 0x3F)
|
||||
return;
|
||||
if(keyslot > 0x3F)
|
||||
return;
|
||||
|
||||
*REG_AESKEYSEL = keyslot;
|
||||
*REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */
|
||||
*REG_AESKEYSEL = keyslot;
|
||||
*REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */
|
||||
}
|
||||
|
||||
static void aes_setiv(const void *iv, u32 mode)
|
||||
{
|
||||
const u32 *iv32 = (const u32 *)iv;
|
||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
||||
const u32 *iv32 = (const u32 *)iv;
|
||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
||||
|
||||
// Word order for IV can't be changed in REG_AESCNT and always default to reversed
|
||||
if(mode & AES_INPUT_NORMAL)
|
||||
{
|
||||
REG_AESCTR[0] = iv32[3];
|
||||
REG_AESCTR[1] = iv32[2];
|
||||
REG_AESCTR[2] = iv32[1];
|
||||
REG_AESCTR[3] = iv32[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
REG_AESCTR[0] = iv32[0];
|
||||
REG_AESCTR[1] = iv32[1];
|
||||
REG_AESCTR[2] = iv32[2];
|
||||
REG_AESCTR[3] = iv32[3];
|
||||
}
|
||||
//Word order for IV can't be changed in REG_AESCNT and always default to reversed
|
||||
if(mode & AES_INPUT_NORMAL)
|
||||
{
|
||||
REG_AESCTR[0] = iv32[3];
|
||||
REG_AESCTR[1] = iv32[2];
|
||||
REG_AESCTR[2] = iv32[1];
|
||||
REG_AESCTR[3] = iv32[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
REG_AESCTR[0] = iv32[0];
|
||||
REG_AESCTR[1] = iv32[1];
|
||||
REG_AESCTR[2] = iv32[2];
|
||||
REG_AESCTR[3] = iv32[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_advctr(void *ctr, u32 val, u32 mode)
|
||||
{
|
||||
u32 *ctr32 = (u32 *)ctr;
|
||||
|
||||
int i;
|
||||
if(mode & AES_INPUT_BE)
|
||||
{
|
||||
for(i = 0; i < 4; ++i) // Endian swap
|
||||
BSWAP32(ctr32[i]);
|
||||
}
|
||||
|
||||
if(mode & AES_INPUT_NORMAL)
|
||||
{
|
||||
ADD_u128_u32(ctr32[3], ctr32[2], ctr32[1], ctr32[0], val);
|
||||
}
|
||||
else
|
||||
{
|
||||
ADD_u128_u32(ctr32[0], ctr32[1], ctr32[2], ctr32[3], val);
|
||||
}
|
||||
|
||||
if(mode & AES_INPUT_BE)
|
||||
{
|
||||
for(i = 0; i < 4; ++i) // Endian swap
|
||||
BSWAP32(ctr32[i]);
|
||||
}
|
||||
u32 *ctr32 = (u32 *)ctr;
|
||||
|
||||
int i;
|
||||
if(mode & AES_INPUT_BE)
|
||||
{
|
||||
for(i = 0; i < 4; ++i) //Endian swap
|
||||
BSWAP32(ctr32[i]);
|
||||
}
|
||||
|
||||
if(mode & AES_INPUT_NORMAL)
|
||||
{
|
||||
ADD_u128_u32(ctr32[3], ctr32[2], ctr32[1], ctr32[0], val);
|
||||
}
|
||||
else
|
||||
{
|
||||
ADD_u128_u32(ctr32[0], ctr32[1], ctr32[2], ctr32[3], val);
|
||||
}
|
||||
|
||||
if(mode & AES_INPUT_BE)
|
||||
{
|
||||
for(i = 0; i < 4; ++i) //Endian swap
|
||||
BSWAP32(ctr32[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_change_ctrmode(void *ctr, u32 fromMode, u32 toMode)
|
||||
{
|
||||
u32 *ctr32 = (u32 *)ctr;
|
||||
int i;
|
||||
if((fromMode ^ toMode) & AES_CNT_INPUT_ENDIAN)
|
||||
{
|
||||
for(i = 0; i < 4; ++i)
|
||||
BSWAP32(ctr32[i]);
|
||||
}
|
||||
u32 *ctr32 = (u32 *)ctr;
|
||||
int i;
|
||||
if((fromMode ^ toMode) & AES_CNT_INPUT_ENDIAN)
|
||||
{
|
||||
for(i = 0; i < 4; ++i)
|
||||
BSWAP32(ctr32[i]);
|
||||
}
|
||||
|
||||
if((fromMode ^ toMode) & AES_CNT_INPUT_ORDER)
|
||||
{
|
||||
u32 temp = ctr32[0];
|
||||
ctr32[0] = ctr32[3];
|
||||
ctr32[3] = temp;
|
||||
if((fromMode ^ toMode) & AES_CNT_INPUT_ORDER)
|
||||
{
|
||||
u32 temp = ctr32[0];
|
||||
ctr32[0] = ctr32[3];
|
||||
ctr32[3] = temp;
|
||||
|
||||
temp = ctr32[1];
|
||||
ctr32[1] = ctr32[2];
|
||||
ctr32[2] = temp;
|
||||
}
|
||||
temp = ctr32[1];
|
||||
ctr32[1] = ctr32[2];
|
||||
ctr32[2] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_batch(void *dst, const void *src, u32 blockCount)
|
||||
{
|
||||
*REG_AESBLKCNT = blockCount << 16;
|
||||
*REG_AESCNT |= AES_CNT_START;
|
||||
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
u32 *dst32 = (u32 *)dst;
|
||||
|
||||
u32 wbc = blockCount;
|
||||
u32 rbc = blockCount;
|
||||
|
||||
while(rbc)
|
||||
{
|
||||
if(wbc && ((*REG_AESCNT & 0x1F) <= 0xC)) // There's space for at least 4 ints
|
||||
{
|
||||
*REG_AESWRFIFO = *src32++;
|
||||
*REG_AESWRFIFO = *src32++;
|
||||
*REG_AESWRFIFO = *src32++;
|
||||
*REG_AESWRFIFO = *src32++;
|
||||
wbc--;
|
||||
}
|
||||
|
||||
if(rbc && ((*REG_AESCNT & (0x1F << 0x5)) >= (0x4 << 0x5))) // At least 4 ints available for read
|
||||
{
|
||||
*dst32++ = *REG_AESRDFIFO;
|
||||
*dst32++ = *REG_AESRDFIFO;
|
||||
*dst32++ = *REG_AESRDFIFO;
|
||||
*dst32++ = *REG_AESRDFIFO;
|
||||
rbc--;
|
||||
}
|
||||
}
|
||||
*REG_AESBLKCNT = blockCount << 16;
|
||||
*REG_AESCNT |= AES_CNT_START;
|
||||
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
u32 *dst32 = (u32 *)dst;
|
||||
|
||||
u32 wbc = blockCount;
|
||||
u32 rbc = blockCount;
|
||||
|
||||
while(rbc)
|
||||
{
|
||||
if(wbc && ((*REG_AESCNT & 0x1F) <= 0xC)) //There's space for at least 4 ints
|
||||
{
|
||||
*REG_AESWRFIFO = *src32++;
|
||||
*REG_AESWRFIFO = *src32++;
|
||||
*REG_AESWRFIFO = *src32++;
|
||||
*REG_AESWRFIFO = *src32++;
|
||||
wbc--;
|
||||
}
|
||||
|
||||
if(rbc && ((*REG_AESCNT & (0x1F << 0x5)) >= (0x4 << 0x5))) //At least 4 ints available for read
|
||||
{
|
||||
*dst32++ = *REG_AESRDFIFO;
|
||||
*dst32++ = *REG_AESRDFIFO;
|
||||
*dst32++ = *REG_AESRDFIFO;
|
||||
*dst32++ = *REG_AESRDFIFO;
|
||||
rbc--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode)
|
||||
void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode)
|
||||
{
|
||||
*REG_AESCNT = mode |
|
||||
AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER |
|
||||
AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN |
|
||||
AES_CNT_FLUSH_READ | AES_CNT_FLUSH_WRITE;
|
||||
*REG_AESCNT = mode |
|
||||
AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER |
|
||||
AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN |
|
||||
AES_CNT_FLUSH_READ | AES_CNT_FLUSH_WRITE;
|
||||
|
||||
u32 blocks;
|
||||
while(blockCount != 0)
|
||||
{
|
||||
if((mode & AES_ALL_MODES) != AES_ECB_ENCRYPT_MODE
|
||||
&& (mode & AES_ALL_MODES) != AES_ECB_DECRYPT_MODE)
|
||||
aes_setiv(iv, ivMode);
|
||||
u32 blocks;
|
||||
while(blockCount != 0)
|
||||
{
|
||||
if((mode & AES_ALL_MODES) != AES_ECB_ENCRYPT_MODE
|
||||
&& (mode & AES_ALL_MODES) != AES_ECB_DECRYPT_MODE)
|
||||
aes_setiv(iv, ivMode);
|
||||
|
||||
blocks = (blockCount >= 0xFFFF) ? 0xFFFF : blockCount;
|
||||
blocks = (blockCount >= 0xFFFF) ? 0xFFFF : blockCount;
|
||||
|
||||
// Save the last block for the next decryption CBC batch's iv
|
||||
if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE)
|
||||
{
|
||||
memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
|
||||
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
|
||||
}
|
||||
//Save the last block for the next decryption CBC batch's iv
|
||||
if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE)
|
||||
{
|
||||
memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
|
||||
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
|
||||
}
|
||||
|
||||
// Process the current batch
|
||||
aes_batch(dst, src, blocks);
|
||||
//Process the current batch
|
||||
aes_batch(dst, src, blocks);
|
||||
|
||||
// Save the last block for the next encryption CBC batch's iv
|
||||
if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE)
|
||||
{
|
||||
memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
|
||||
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
|
||||
}
|
||||
|
||||
// Advance counter for CTR mode
|
||||
else if((mode & AES_ALL_MODES) == AES_CTR_MODE)
|
||||
aes_advctr(iv, blocks, ivMode);
|
||||
//Save the last block for the next encryption CBC batch's iv
|
||||
if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE)
|
||||
{
|
||||
memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
|
||||
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
|
||||
}
|
||||
|
||||
src += blocks * AES_BLOCK_SIZE;
|
||||
dst += blocks * AES_BLOCK_SIZE;
|
||||
blockCount -= blocks;
|
||||
}
|
||||
//Advance counter for CTR mode
|
||||
else if((mode & AES_ALL_MODES) == AES_CTR_MODE)
|
||||
aes_advctr(iv, blocks, ivMode);
|
||||
|
||||
src += blocks * AES_BLOCK_SIZE;
|
||||
dst += blocks * AES_BLOCK_SIZE;
|
||||
blockCount -= blocks;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* Nand/FIRM Crypto stuff
|
||||
****************************************************************/
|
||||
|
||||
//Nand key#2 (0x12C10)
|
||||
static const u8 key2[0x10] = {
|
||||
0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0
|
||||
};
|
||||
|
||||
//Get Nand CTR key
|
||||
static void getNandCTR(u8 *buf, u32 console){
|
||||
u8 *addr = (console ? (u8 *)0x080D8BBC : (u8 *)0x080D797C) + 0x0F;
|
||||
for(u8 keyLen = 0x10; keyLen; keyLen--)
|
||||
*(buf++) = *(addr--);
|
||||
static void sha_wait_idle()
|
||||
{
|
||||
while(*REG_SHA_CNT & 1);
|
||||
}
|
||||
|
||||
//Read firm0 from NAND and write to buffer
|
||||
void nandFirm0(u8 *outbuf, u32 size, u32 console){
|
||||
u8 CTR[0x10];
|
||||
getNandCTR(CTR, console);
|
||||
void sha(void *res, const void *src, u32 size, u32 mode)
|
||||
{
|
||||
sha_wait_idle();
|
||||
*REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
|
||||
|
||||
sdmmc_nand_readsectors(0x0B130000 / 0x200, size / 0x200, outbuf);
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
int i;
|
||||
while(size >= 0x40)
|
||||
{
|
||||
sha_wait_idle();
|
||||
for(i = 0; i < 4; ++i)
|
||||
{
|
||||
*REG_SHA_INFIFO = *src32++;
|
||||
*REG_SHA_INFIFO = *src32++;
|
||||
*REG_SHA_INFIFO = *src32++;
|
||||
*REG_SHA_INFIFO = *src32++;
|
||||
}
|
||||
|
||||
aes_advctr(CTR, 0x0B130000/0x10, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x06);
|
||||
aes(outbuf, outbuf, size / AES_BLOCK_SIZE, CTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
//Decrypts the N3DS arm9bin
|
||||
void decryptArm9Bin(u8 *arm9Section, u32 mode){
|
||||
|
||||
//Firm keys
|
||||
u8 keyY[0x10];
|
||||
u8 CTR[0x10];
|
||||
u8 slot = mode ? 0x16 : 0x15;
|
||||
|
||||
//Setup keys needed for arm9bin decryption
|
||||
memcpy(keyY, arm9Section + 0x10, 0x10);
|
||||
memcpy(CTR, arm9Section + 0x20, 0x10);
|
||||
u32 size = 0;
|
||||
//http://stackoverflow.com/questions/12791077/atoi-implementation-in-c
|
||||
for(u8 *tmp = arm9Section + 0x30; *tmp; tmp++)
|
||||
size = (size << 3) + (size << 1) + (*tmp) - '0';
|
||||
|
||||
if(mode){
|
||||
u8 keyX[0x10];
|
||||
|
||||
//Set 0x11 to key2 for the arm9bin and misc keys
|
||||
aes_setkey(0x11, key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x11);
|
||||
aes(keyX, arm9Section + 0x60, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(slot, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
size -= 0x40;
|
||||
}
|
||||
|
||||
aes_setkey(slot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_setiv(CTR, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(slot);
|
||||
sha_wait_idle();
|
||||
memcpy((void *)REG_SHA_INFIFO, src32, size);
|
||||
|
||||
//Decrypt arm9bin
|
||||
aes(arm9Section + 0x800, arm9Section + 0x800, size/AES_BLOCK_SIZE, CTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
*REG_SHA_CNT = (*REG_SHA_CNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND;
|
||||
|
||||
while(*REG_SHA_CNT & SHA_FINAL_ROUND);
|
||||
sha_wait_idle();
|
||||
|
||||
u32 hashSize = SHA_256_HASH_SIZE;
|
||||
if(mode == SHA_224_MODE)
|
||||
hashSize = SHA_224_HASH_SIZE;
|
||||
else if(mode == SHA_1_MODE)
|
||||
hashSize = SHA_1_HASH_SIZE;
|
||||
|
||||
memcpy(res, (void *)REG_SHA_HASH, hashSize);
|
||||
}
|
||||
|
||||
//Sets the N3DS 9.6 KeyXs
|
||||
void setKeyXs(u8 *arm9Section){
|
||||
/*****************************************************************/
|
||||
|
||||
u8 *keyData = arm9Section + 0x89814;
|
||||
u8 *decKey = keyData + 0x10;
|
||||
__attribute__((aligned(4))) static u8 nandCtr[AES_BLOCK_SIZE];
|
||||
static u8 nandSlot;
|
||||
static u32 fatStart;
|
||||
|
||||
//Set keys 0x19..0x1F keyXs
|
||||
aes_setkey(0x11, key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
FirmwareSource firmSource;
|
||||
|
||||
void ctrNandInit(void)
|
||||
{
|
||||
__attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
|
||||
shaSum[SHA_256_HASH_SIZE];
|
||||
|
||||
sdmmc_get_cid(1, (u32 *)cid);
|
||||
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
|
||||
memcpy(nandCtr, shaSum, sizeof(nandCtr));
|
||||
|
||||
if(ISN3DS)
|
||||
{
|
||||
__attribute__((aligned(4))) u8 keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE};
|
||||
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
nandSlot = 0x05;
|
||||
fatStart = 0x5CAD7;
|
||||
}
|
||||
else
|
||||
{
|
||||
nandSlot = 0x04;
|
||||
fatStart = 0x5CAE5;
|
||||
}
|
||||
}
|
||||
|
||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
||||
{
|
||||
__attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
|
||||
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Read
|
||||
int result;
|
||||
if(firmSource == FIRMWARE_SYSNAND)
|
||||
result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf);
|
||||
else
|
||||
{
|
||||
sector += emuOffset;
|
||||
result = sdmmc_sdcard_readsectors(sector + fatStart, sectorCount, outbuf);
|
||||
}
|
||||
|
||||
//Decrypt
|
||||
aes_use_keyslot(nandSlot);
|
||||
aes(outbuf, outbuf, sectorCount * 0x200 / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf)
|
||||
{
|
||||
u8 *buffer = (u8 *)0x23000000;
|
||||
u32 bufferSize = 0xF00000;
|
||||
|
||||
__attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
|
||||
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(nandSlot);
|
||||
|
||||
int result = 0;
|
||||
for(u32 tempSector = 0; tempSector < sectorCount && !result; tempSector += bufferSize / 0x200)
|
||||
{
|
||||
u32 tempCount = (bufferSize / 0x200) < (sectorCount - tempSector) ? (bufferSize / 0x200) : (sectorCount - tempSector);
|
||||
|
||||
memcpy(buffer, inbuf + (tempSector * 0x200), tempCount * 0x200);
|
||||
|
||||
//Encrypt
|
||||
aes(buffer, buffer, tempCount * 0x200 / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Write
|
||||
result = sdmmc_nand_writesectors(tempSector + sector + fatStart, tempCount, buffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool decryptExeFs(Cxi *cxi)
|
||||
{
|
||||
if(memcmp(cxi->ncch.magic, "NCCH", 4) != 0) return false;
|
||||
|
||||
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
||||
u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
|
||||
__attribute__((aligned(4))) u8 ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
||||
ncchCtr[8] = 2;
|
||||
|
||||
aes_setkey(0x2C, cxi, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x2C);
|
||||
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
return memcmp(cxi, "FIRM", 4) == 0;
|
||||
}
|
||||
|
||||
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
||||
{
|
||||
if(memcmp(ticket->sigIssuer, "Root", 4) != 0) return false;
|
||||
|
||||
__attribute__((aligned(4))) const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
|
||||
__attribute__((aligned(4))) u8 titleKey[AES_BLOCK_SIZE],
|
||||
cetkIv[AES_BLOCK_SIZE] = {0};
|
||||
memcpy(titleKey, ticket->titleKey, sizeof(titleKey));
|
||||
memcpy(cetkIv, ticket->titleId, sizeof(ticket->titleId));
|
||||
|
||||
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x3D);
|
||||
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
__attribute__((aligned(4))) u8 ncchIv[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x16);
|
||||
aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
return decryptExeFs(cxi);
|
||||
}
|
||||
|
||||
void kernel9Loader(Arm9Bin *arm9Section)
|
||||
{
|
||||
//Determine the kernel9loader version
|
||||
u32 k9lVersion;
|
||||
switch(arm9Section->magic[3])
|
||||
{
|
||||
case 0xFF:
|
||||
k9lVersion = 0;
|
||||
break;
|
||||
case '1':
|
||||
k9lVersion = 1;
|
||||
break;
|
||||
default:
|
||||
k9lVersion = 2;
|
||||
}
|
||||
|
||||
u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800);
|
||||
if(*startOfArm9Bin == 0x47704770 || *startOfArm9Bin == 0xB0862000) return; //Already decrypted
|
||||
|
||||
//Set 0x11 keyslot
|
||||
__attribute__((aligned(4))) const u8 key1s[2][AES_BLOCK_SIZE] = {
|
||||
{0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8},
|
||||
{0xA2, 0xF4, 0x00, 0x3C, 0x7A, 0x95, 0x10, 0x25, 0xDF, 0x4E, 0x9E, 0x74, 0xE3, 0x0C, 0x92, 0x99}
|
||||
},
|
||||
key2s[2][AES_BLOCK_SIZE] = {
|
||||
{0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0},
|
||||
{0xFF, 0x77, 0xA0, 0x9A, 0x99, 0x81, 0xE9, 0x48, 0xEC, 0x51, 0xC9, 0x32, 0x5D, 0x14, 0xEC, 0x25}
|
||||
};
|
||||
|
||||
aes_setkey(0x11, k9lVersion == 2 ? key2s[ISDEVUNIT ? 1 : 0] : key1s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
u8 arm9BinSlot = k9lVersion == 0 ? 0x15 : 0x16;
|
||||
|
||||
//Set keyX
|
||||
__attribute__((aligned(4))) u8 keyX[AES_BLOCK_SIZE];
|
||||
aes_use_keyslot(0x11);
|
||||
for(u8 slot = 0x19; slot < 0x20; slot++){
|
||||
aes(decKey, keyData, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(slot, decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
*(keyData + 0xF) += 1;
|
||||
}
|
||||
}
|
||||
aes(keyX, k9lVersion == 0 ? arm9Section->keyX : arm9Section->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(arm9BinSlot, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Set keyY
|
||||
__attribute__((aligned(4))) u8 keyY[AES_BLOCK_SIZE];
|
||||
memcpy(keyY, arm9Section->keyY, sizeof(keyY));
|
||||
aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Set CTR
|
||||
__attribute__((aligned(4))) u8 arm9BinCtr[AES_BLOCK_SIZE];
|
||||
memcpy(arm9BinCtr, arm9Section->ctr, sizeof(arm9BinCtr));
|
||||
|
||||
//Decrypt ARM9 binary
|
||||
aes_use_keyslot(arm9BinSlot);
|
||||
aes(startOfArm9Bin, startOfArm9Bin, decAtoi(arm9Section->size, sizeof(arm9Section->size)) / AES_BLOCK_SIZE, arm9BinCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
if(*startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000) error("Failed to decrypt the ARM9 binary.");
|
||||
}
|
||||
|
||||
void computePinHash(u8 *outbuf, const u8 *inbuf)
|
||||
{
|
||||
__attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
|
||||
cipherText[AES_BLOCK_SIZE];
|
||||
|
||||
sdmmc_get_cid(1, (u32 *)cid);
|
||||
aes_use_keyslot(0x04); //Console-unique keyslot whose keys are set by the ARM9 bootROM
|
||||
aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE);
|
||||
}
|
||||
|
||||
146
source/crypto.h
146
source/crypto.h
@@ -1,9 +1,30 @@
|
||||
/*
|
||||
* crypto.h
|
||||
* by Reisyukaku / Aurora Wright
|
||||
* Crypto libs from http://github.com/b1l1s/ctr
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Crypto libs from http://github.com/b1l1s/ctr
|
||||
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
|
||||
* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c
|
||||
* ctrNandWrite logic adapted from https://github.com/d0k3/GodMode9/blob/master/source/nand/nand.c
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -11,49 +32,88 @@
|
||||
#include "types.h"
|
||||
|
||||
/**************************AES****************************/
|
||||
#define REG_AESCNT ((vu32 *)0x10009000)
|
||||
#define REG_AESBLKCNT ((vu32 *)0x10009004)
|
||||
#define REG_AESWRFIFO ((vu32 *)0x10009008)
|
||||
#define REG_AESRDFIFO ((vu32 *)0x1000900C)
|
||||
#define REG_AESKEYSEL ((vu8 *)0x10009010)
|
||||
#define REG_AESKEYCNT ((vu8 *)0x10009011)
|
||||
#define REG_AESCTR ((vu32 *)0x10009020)
|
||||
#define REG_AESCNT ((vu32 *)0x10009000)
|
||||
#define REG_AESBLKCNT ((vu32 *)0x10009004)
|
||||
#define REG_AESWRFIFO ((vu32 *)0x10009008)
|
||||
#define REG_AESRDFIFO ((vu32 *)0x1000900C)
|
||||
#define REG_AESKEYSEL ((vu8 *)0x10009010)
|
||||
#define REG_AESKEYCNT ((vu8 *)0x10009011)
|
||||
#define REG_AESCTR ((vu32 *)0x10009020)
|
||||
|
||||
#define REG_AESKEYFIFO ((vu32 *)0x10009100)
|
||||
#define REG_AESKEYXFIFO ((vu32 *)0x10009104)
|
||||
#define REG_AESKEYYFIFO ((vu32 *)0x10009108)
|
||||
#define REG_AESKEYFIFO ((vu32 *)0x10009100)
|
||||
#define REG_AESKEYXFIFO ((vu32 *)0x10009104)
|
||||
#define REG_AESKEYYFIFO ((vu32 *)0x10009108)
|
||||
|
||||
#define AES_CCM_DECRYPT_MODE (0u << 27)
|
||||
#define AES_CCM_ENCRYPT_MODE (1u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
#define AES_CBC_DECRYPT_MODE (4u << 27)
|
||||
#define AES_CBC_ENCRYPT_MODE (5u << 27)
|
||||
#define AES_ECB_DECRYPT_MODE (6u << 27)
|
||||
#define AES_ECB_ENCRYPT_MODE (7u << 27)
|
||||
#define AES_ALL_MODES (7u << 27)
|
||||
#define REGs_AESTWLKEYS (*((vu32 (*)[4][3][4])0x10009040))
|
||||
|
||||
#define AES_CNT_START 0x80000000
|
||||
#define AES_CNT_INPUT_ORDER 0x02000000
|
||||
#define AES_CNT_OUTPUT_ORDER 0x01000000
|
||||
#define AES_CNT_INPUT_ENDIAN 0x00800000
|
||||
#define AES_CNT_OUTPUT_ENDIAN 0x00400000
|
||||
#define AES_CNT_FLUSH_READ 0x00000800
|
||||
#define AES_CNT_FLUSH_WRITE 0x00000400
|
||||
#define AES_CCM_DECRYPT_MODE (0u << 27)
|
||||
#define AES_CCM_ENCRYPT_MODE (1u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
#define AES_CBC_DECRYPT_MODE (4u << 27)
|
||||
#define AES_CBC_ENCRYPT_MODE (5u << 27)
|
||||
#define AES_ECB_DECRYPT_MODE (6u << 27)
|
||||
#define AES_ECB_ENCRYPT_MODE (7u << 27)
|
||||
#define AES_ALL_MODES (7u << 27)
|
||||
|
||||
#define AES_INPUT_BE (AES_CNT_INPUT_ENDIAN)
|
||||
#define AES_INPUT_LE 0
|
||||
#define AES_INPUT_NORMAL (AES_CNT_INPUT_ORDER)
|
||||
#define AES_INPUT_REVERSED 0
|
||||
#define AES_CNT_START 0x80000000
|
||||
#define AES_CNT_INPUT_ORDER 0x02000000
|
||||
#define AES_CNT_OUTPUT_ORDER 0x01000000
|
||||
#define AES_CNT_INPUT_ENDIAN 0x00800000
|
||||
#define AES_CNT_OUTPUT_ENDIAN 0x00400000
|
||||
#define AES_CNT_FLUSH_READ 0x00000800
|
||||
#define AES_CNT_FLUSH_WRITE 0x00000400
|
||||
|
||||
#define AES_BLOCK_SIZE 0x10
|
||||
#define AES_INPUT_BE (AES_CNT_INPUT_ENDIAN)
|
||||
#define AES_INPUT_LE 0
|
||||
#define AES_INPUT_NORMAL (AES_CNT_INPUT_ORDER)
|
||||
#define AES_INPUT_REVERSED 0
|
||||
|
||||
#define AES_KEYCNT_WRITE (1 << 0x7)
|
||||
#define AES_KEYNORMAL 0
|
||||
#define AES_KEYX 1
|
||||
#define AES_KEYY 2
|
||||
#define AES_BLOCK_SIZE 0x10
|
||||
|
||||
//NAND/FIRM stuff
|
||||
void nandFirm0(u8 *outbuf, u32 size, u32 console);
|
||||
void decryptArm9Bin(u8 *arm9Section, u32 mode);
|
||||
void setKeyXs(u8 *arm9Section);
|
||||
#define AES_KEYCNT_WRITE (1 << 0x7)
|
||||
#define AES_KEYNORMAL 0
|
||||
#define AES_KEYX 1
|
||||
#define AES_KEYY 2
|
||||
|
||||
/**************************SHA****************************/
|
||||
#define REG_SHA_CNT ((vu32 *)0x1000A000)
|
||||
#define REG_SHA_BLKCNT ((vu32 *)0x1000A004)
|
||||
#define REG_SHA_HASH ((vu32 *)0x1000A040)
|
||||
#define REG_SHA_INFIFO ((vu32 *)0x1000A080)
|
||||
|
||||
#define SHA_CNT_STATE 0x00000003
|
||||
#define SHA_CNT_UNK2 0x00000004
|
||||
#define SHA_CNT_OUTPUT_ENDIAN 0x00000008
|
||||
#define SHA_CNT_MODE 0x00000030
|
||||
#define SHA_CNT_ENABLE 0x00010000
|
||||
#define SHA_CNT_ACTIVE 0x00020000
|
||||
|
||||
#define SHA_HASH_READY 0x00000000
|
||||
#define SHA_NORMAL_ROUND 0x00000001
|
||||
#define SHA_FINAL_ROUND 0x00000002
|
||||
|
||||
#define SHA_OUTPUT_BE SHA_CNT_OUTPUT_ENDIAN
|
||||
#define SHA_OUTPUT_LE 0
|
||||
|
||||
#define SHA_256_MODE 0
|
||||
#define SHA_224_MODE 0x00000010
|
||||
#define SHA_1_MODE 0x00000020
|
||||
|
||||
#define SHA_256_HASH_SIZE (256 / 8)
|
||||
#define SHA_224_HASH_SIZE (224 / 8)
|
||||
#define SHA_1_HASH_SIZE (160 / 8)
|
||||
|
||||
extern u32 emuOffset;
|
||||
extern FirmwareSource firmSource;
|
||||
|
||||
void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode);
|
||||
void sha(void *res, const void *src, u32 size, u32 mode);
|
||||
|
||||
void ctrNandInit(void);
|
||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||
int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf);
|
||||
bool decryptExeFs(Cxi *cxi);
|
||||
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize);
|
||||
void kernel9Loader(Arm9Bin *arm9Section);
|
||||
void computePinHash(u8 *outbuf, const u8 *inbuf);
|
||||
|
||||
158
source/draw.c
158
source/draw.c
@@ -1,82 +1,122 @@
|
||||
/*
|
||||
* draw.c
|
||||
* by Reisyukaku / Aurora Wright
|
||||
* Code to print to the screen by mid-kid @CakesFW
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Code to print to the screen by mid-kid @CakesFW
|
||||
* https://github.com/mid-kid/CakesForeveryWan
|
||||
*/
|
||||
|
||||
#include "draw.h"
|
||||
#include "strings.h"
|
||||
#include "screen.h"
|
||||
#include "utils.h"
|
||||
#include "fs.h"
|
||||
#include "memory.h"
|
||||
#include "fmt.h"
|
||||
#include "font.h"
|
||||
|
||||
#define SCREEN_TOP_WIDTH 400
|
||||
#define SCREEN_TOP_HEIGHT 240
|
||||
bool loadSplash(void)
|
||||
{
|
||||
const char *topSplashFile = "splash.bin",
|
||||
*bottomSplashFile = "splashbottom.bin";
|
||||
|
||||
static const struct fb {
|
||||
u8 *top_left;
|
||||
u8 *top_right;
|
||||
u8 *bottom;
|
||||
} *const fb = (struct fb *)0x23FFFE00;
|
||||
bool isTopSplashValid = getFileSize(topSplashFile) == SCREEN_TOP_FBSIZE,
|
||||
isBottomSplashValid = getFileSize(bottomSplashFile) == SCREEN_BOTTOM_FBSIZE;
|
||||
|
||||
static int strlen(const char *string){
|
||||
char *stringEnd = (char *)string;
|
||||
while(*stringEnd) stringEnd++;
|
||||
return stringEnd - string;
|
||||
//Don't delay boot nor init the screens if no splash images or invalid splash images are on the SD
|
||||
if(!isTopSplashValid && !isBottomSplashValid) return false;
|
||||
|
||||
initScreens();
|
||||
clearScreens(true);
|
||||
|
||||
if(isTopSplashValid) isTopSplashValid = fileRead(fbs[1].top_left, topSplashFile, SCREEN_TOP_FBSIZE) == SCREEN_TOP_FBSIZE;
|
||||
if(isBottomSplashValid) isBottomSplashValid = fileRead(fbs[1].bottom, bottomSplashFile, SCREEN_BOTTOM_FBSIZE) == SCREEN_BOTTOM_FBSIZE;
|
||||
|
||||
if(!isTopSplashValid && !isBottomSplashValid) return false;
|
||||
|
||||
swapFramebuffers(true);
|
||||
wait(3000ULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clearScreens(void){
|
||||
memset32(fb->top_left, 0, 0x46500);
|
||||
memset32(fb->top_right, 0, 0x46500);
|
||||
memset32(fb->bottom, 0, 0x38400);
|
||||
}
|
||||
void drawCharacter(bool isTopScreen, u32 posX, u32 posY, u32 color, char character)
|
||||
{
|
||||
u8 *select = isTopScreen ? fbs[0].top_left : fbs[0].bottom;
|
||||
|
||||
void loadSplash(void){
|
||||
clearScreens();
|
||||
for(u32 y = 0; y < 8; y++)
|
||||
{
|
||||
char charPos = font[character * 8 + y];
|
||||
|
||||
//Don't delay boot if no splash image is on the SD
|
||||
if(fileRead(fb->top_left, "/aurei/splash.bin", 0x46500) +
|
||||
fileRead(fb->bottom, "/aurei/splashbottom.bin", 0x38400)){
|
||||
u64 i = 0x1300000; while(--i) __asm("mov r0, r0"); //Less Ghetto sleep func
|
||||
}
|
||||
}
|
||||
for(u32 x = 0; x < 8; x++)
|
||||
if(((charPos >> (7 - x)) & 1) == 1)
|
||||
{
|
||||
u32 screenPos = (posX * SCREEN_HEIGHT * 3 + (SCREEN_HEIGHT - y - posY - 1) * 3) + x * 3 * SCREEN_HEIGHT;
|
||||
|
||||
void drawCharacter(char character, int pos_x, int pos_y, u32 color){
|
||||
u8 *const select = fb->top_left;
|
||||
|
||||
for(int y = 0; y < 8; y++){
|
||||
unsigned char char_pos = font[character * 8 + y];
|
||||
|
||||
for(int x = 7; x >= 0; x--){
|
||||
int screen_pos = (pos_x * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - pos_y - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
||||
|
||||
if ((char_pos >> x) & 1) {
|
||||
select[screen_pos] = color >> 16;
|
||||
select[screen_pos + 1] = color >> 8;
|
||||
select[screen_pos + 2] = color;
|
||||
select[screenPos] = color >> 16;
|
||||
select[screenPos + 1] = color >> 8;
|
||||
select[screenPos + 2] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int drawString(const char *string, int pos_x, int pos_y, u32 color){
|
||||
int length = strlen(string);
|
||||
u32 drawString(bool isTopScreen, u32 posX, u32 posY, u32 color, const char *string)
|
||||
{
|
||||
for(u32 i = 0, line_i = 0; i < strlen(string); i++)
|
||||
switch(string[i])
|
||||
{
|
||||
case '\n':
|
||||
posY += SPACING_Y;
|
||||
line_i = 0;
|
||||
break;
|
||||
|
||||
for(int i = 0, line_i = 0; i < length; i++, line_i++){
|
||||
if(string[i] == '\n'){
|
||||
pos_y += SPACING_VERT;
|
||||
line_i = 0;
|
||||
i++;
|
||||
} else if(line_i >= (SCREEN_TOP_WIDTH - pos_x) / SPACING_HORIZ){
|
||||
// Make sure we never get out of the screen.
|
||||
pos_y += SPACING_VERT;
|
||||
line_i = 2; // Little offset so we know the same string continues.
|
||||
if(string[i] == ' ') i++; // Spaces at the start look weird
|
||||
case '\t':
|
||||
line_i += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
//Make sure we never get out of the screen
|
||||
if(line_i >= ((isTopScreen ? SCREEN_TOP_WIDTH : SCREEN_BOTTOM_WIDTH) - posX) / SPACING_X)
|
||||
{
|
||||
posY += SPACING_Y;
|
||||
line_i = 1; //Little offset so we know the same string continues
|
||||
if(string[i] == ' ') break; //Spaces at the start look weird
|
||||
}
|
||||
|
||||
drawCharacter(isTopScreen, posX + line_i * SPACING_X, posY, color, string[i]);
|
||||
|
||||
line_i++;
|
||||
break;
|
||||
}
|
||||
|
||||
drawCharacter(string[i], pos_x + line_i * SPACING_HORIZ, pos_y, color);
|
||||
}
|
||||
return posY;
|
||||
}
|
||||
|
||||
return pos_y;
|
||||
}
|
||||
u32 drawFormattedString(bool isTopScreen, u32 posX, u32 posY, u32 color, const char *fmt, ...)
|
||||
{
|
||||
char buf[DRAW_MAX_FORMATTED_STRING_SIZE + 1];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return drawString(isTopScreen, posX, posY, color, buf);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,46 @@
|
||||
/*
|
||||
* draw.h
|
||||
* by Reisyukaku / Aurora Wright
|
||||
* Code to print to the screen by mid-kid @CakesFW
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Code to print to the screen by mid-kid @CakesFW
|
||||
* https://github.com/mid-kid/CakesForeveryWan/
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define SPACING_VERT 10
|
||||
#define SPACING_HORIZ 8
|
||||
#define SPACING_Y 10
|
||||
#define SPACING_X 8
|
||||
|
||||
void loadSplash(void);
|
||||
void clearScreens(void);
|
||||
void drawCharacter(char character, int pos_x, int pos_y, u32 color);
|
||||
int drawString(const char *string, int pos_x, int pos_y, u32 color);
|
||||
#define COLOR_TITLE 0xFF9900
|
||||
#define COLOR_WHITE 0xFFFFFF
|
||||
#define COLOR_RED 0x0000FF
|
||||
#define COLOR_BLACK 0x000000
|
||||
#define COLOR_YELLOW 0x00FFFF
|
||||
|
||||
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
||||
|
||||
bool loadSplash(void);
|
||||
void drawCharacter(bool isTopScreen, u32 posX, u32 posY, u32 color, char character);
|
||||
u32 drawString(bool isTopScreen, u32 posX, u32 posY, u32 color, const char *string);
|
||||
u32 drawFormattedString(bool isTopScreen, u32 posX, u32 posY, u32 color, const char *fmt, ...);
|
||||
|
||||
229
source/emunand.c
229
source/emunand.c
@@ -1,64 +1,195 @@
|
||||
/*
|
||||
* emunand.c
|
||||
* by Reisyukaku / Aurora Wright
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Code for locating the SDMMC struct by Normmatt
|
||||
*/
|
||||
|
||||
#include "emunand.h"
|
||||
#include "memory.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
void getEmunandSect(u32 *off, u32 *head, u32 emuNAND){
|
||||
u8 *const temp = (u8 *)0x24300000;
|
||||
u32 emuOffset;
|
||||
|
||||
u32 nandSize = getMMCDevice(0)->total_size;
|
||||
u32 nandOffset = emuNAND == 1 ? 0 :
|
||||
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
||||
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
{
|
||||
static u8 __attribute__((aligned(4))) temp[0x200];
|
||||
static u32 nandSize = 0,
|
||||
fatStart;
|
||||
|
||||
//Check for Gateway emuNAND
|
||||
if(sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) == 0){
|
||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
||||
*off = nandOffset;
|
||||
*head = nandOffset + nandSize;
|
||||
}
|
||||
//Check for RedNAND
|
||||
else if(sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) == 0){
|
||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
||||
*off = nandOffset + 1;
|
||||
*head = nandOffset + 1;
|
||||
}
|
||||
//Fallback to the first emuNAND if there's no second one
|
||||
else if(emuNAND == 2) getEmunandSect(off, head, 1);
|
||||
}
|
||||
if(!nandSize)
|
||||
{
|
||||
nandSize = getMMCDevice(0)->total_size;
|
||||
sdmmc_sdcard_readsectors(0, 1, temp);
|
||||
fatStart = *(u32 *)(temp + 0x1C6); //First sector of the FAT partition
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < 3; i++)
|
||||
{
|
||||
static const u32 roundedMinsizes[] = {0x1D8000, 0x26E000};
|
||||
|
||||
u32 nandOffset;
|
||||
switch(i)
|
||||
{
|
||||
case 1:
|
||||
nandOffset = ROUND_TO_4MB(nandSize + 1); //"Default" layout
|
||||
break;
|
||||
case 2:
|
||||
nandOffset = roundedMinsizes[ISN3DS ? 1 : 0]; //"Minsize" layout
|
||||
break;
|
||||
case 0:
|
||||
nandOffset = *nandType == FIRMWARE_EMUNAND ? 0 : (nandSize > 0x200000 ? 0x400000 : 0x200000); //"Legacy" layout
|
||||
break;
|
||||
}
|
||||
|
||||
if(*nandType != FIRMWARE_EMUNAND) nandOffset *= ((u32)*nandType - 1);
|
||||
|
||||
if(fatStart >= nandOffset + roundedMinsizes[ISN3DS ? 1 : 0])
|
||||
{
|
||||
//Check for RedNAND
|
||||
if(!sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) && memcmp(temp + 0x100, "NCSD", 4) == 0)
|
||||
{
|
||||
emuOffset = nandOffset + 1;
|
||||
*emuHeader = nandOffset + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
//Check for Gateway EmuNAND
|
||||
else if(i != 2 && !sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) && memcmp(temp + 0x100, "NCSD", 4) == 0)
|
||||
{
|
||||
emuOffset = nandOffset;
|
||||
*emuHeader = nandOffset + nandSize;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(*nandType == FIRMWARE_EMUNAND) break;
|
||||
}
|
||||
|
||||
//Fallback to the first EmuNAND if there's no second/third/fourth one, or to SysNAND if there isn't any
|
||||
if(*nandType != FIRMWARE_EMUNAND)
|
||||
{
|
||||
*nandType = FIRMWARE_EMUNAND;
|
||||
locateEmuNand(emuHeader, nandType);
|
||||
}
|
||||
else *nandType = FIRMWARE_SYSNAND;
|
||||
}
|
||||
|
||||
u32 getSDMMC(void *pos, u32 size){
|
||||
//Look for struct code
|
||||
const unsigned char pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||
const u8 *off = (u8 *)memsearch(pos, pattern, size, 4) - 1;
|
||||
static inline bool getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||
|
||||
return *(u32 *)(off + 0x0A) + *(u32 *)(off + 0x0E);
|
||||
}
|
||||
|
||||
void getEmuRW(void *pos, u32 size, u32 *readOff, u32 *writeOff){
|
||||
//Look for read/write code
|
||||
const unsigned char pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||
|
||||
*writeOff = (u32)memsearch(pos, pattern, size, 4) - 6;
|
||||
*readOff = (u32)memsearch((void *)(*writeOff - 0x1000), pattern, 0x1000, 4) - 6;
|
||||
}
|
||||
|
||||
u32 *getMPU(void *pos, u32 size){
|
||||
//Look for MPU pattern
|
||||
const unsigned char pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||
|
||||
return (u32 *)memsearch(pos, pattern, size, 4);
|
||||
}
|
||||
|
||||
void *getEmuCode(u8 *pos, u32 size, u8 *proc9Offset){
|
||||
const unsigned char pattern[] = {0x00, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
//Looking for the last free space before Process9
|
||||
return (u8 *)memsearch(pos, pattern, size - (size - (u32)(proc9Offset - pos)), 4) + 0xD;
|
||||
*freeK9Space = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(*freeK9Space == NULL) return false;
|
||||
|
||||
*freeK9Space += 0x455;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline u32 getSdmmc(u8 *pos, u32 size, u32 *sdmmc)
|
||||
{
|
||||
//Look for struct code
|
||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||
|
||||
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) return 1;
|
||||
|
||||
*sdmmc = *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 patchNandRw(u8 *pos, u32 size, u32 branchOffset)
|
||||
{
|
||||
//Look for read/write code
|
||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||
|
||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(readOffset == NULL) return 1;
|
||||
|
||||
readOffset -= 3;
|
||||
|
||||
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern));
|
||||
|
||||
if(writeOffset == NULL) return 1;
|
||||
|
||||
writeOffset -= 3;
|
||||
*readOffset = *writeOffset = 0x4C00;
|
||||
readOffset[1] = writeOffset[1] = 0x47A0;
|
||||
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 patchMpu(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for MPU pattern
|
||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[1] = 0x0036;
|
||||
off[0xC] = off[0x12] = 0x0603;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u32 emuHeader, u8 *kernel9Address)
|
||||
{
|
||||
u8 *freeK9Space;
|
||||
|
||||
if(!getFreeK9Space(arm9Section, kernel9Size, &freeK9Space)) return 1;
|
||||
|
||||
u32 ret = 0;
|
||||
|
||||
//Copy EmuNAND code
|
||||
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
|
||||
|
||||
//Add the data of the found EmuNAND
|
||||
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
|
||||
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
|
||||
*posOffset = emuOffset;
|
||||
*posHeader = emuHeader;
|
||||
|
||||
//Find and add the SDMMC struct
|
||||
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
|
||||
u32 sdmmc;
|
||||
ret += getSdmmc(process9Offset, process9Size, &sdmmc);
|
||||
if(!ret) *posSdmmc = sdmmc;
|
||||
|
||||
//Add EmuNAND hooks
|
||||
u32 branchOffset = (u32)(freeK9Space - arm9Section + kernel9Address);
|
||||
ret += patchNandRw(process9Offset, process9Size, branchOffset);
|
||||
|
||||
//Set MPU
|
||||
ret += patchMpu(arm9Section, kernel9Size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,17 +1,34 @@
|
||||
/*
|
||||
* emunand.h
|
||||
* by Reisyukaku / Aurora Wright
|
||||
* Copyright (c) 2016 All Rights Reserved
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Code for locating the SDMMC struct by Normmatt
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define NCSD_MAGIC (0x4453434E)
|
||||
#define ROUND_TO_4MB(a) (((a) + 0x2000 - 1) & (~(0x2000 - 1)))
|
||||
|
||||
void getEmunandSect(u32 *off, u32 *head, u32 emuNAND);
|
||||
u32 getSDMMC(void *pos, u32 size);
|
||||
void getEmuRW(void *pos, u32 size, u32 *readOff, u32 *writeOff);
|
||||
u32 *getMPU(void *pos, u32 size);
|
||||
void *getEmuCode(u8 *pos, u32 size, u8 *proc9Offset);
|
||||
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType);
|
||||
u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u32 emuHeader, u8 *kernel9Address);
|
||||
212
source/exceptions.c
Normal file
212
source/exceptions.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#include "exceptions.h"
|
||||
#include "fs.h"
|
||||
#include "strings.h"
|
||||
#include "memory.h"
|
||||
#include "screen.h"
|
||||
#include "draw.h"
|
||||
#include "utils.h"
|
||||
#include "fmt.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
void installArm9Handlers(void)
|
||||
{
|
||||
memcpy((void *)0x01FF8000, arm9_exceptions_bin + 32, arm9_exceptions_bin_size - 32);
|
||||
|
||||
/* IRQHandler is at 0x08000000, but we won't handle it for some reasons
|
||||
svcHandler is at 0x08000010, but we won't handle svc either */
|
||||
|
||||
const u32 offsets[] = {0x08, 0x18, 0x20, 0x28};
|
||||
|
||||
for(u32 i = 0; i < 4; i++)
|
||||
{
|
||||
*(vu32 *)(0x08000000 + offsets[i]) = 0xE51FF004;
|
||||
*(vu32 *)(0x08000000 + offsets[i] + 4) = *((u32 *)arm9_exceptions_bin + 1 + i);
|
||||
}
|
||||
}
|
||||
|
||||
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset, u32 *dAbtHandler, u32 dAbtHandlerMemAddress)
|
||||
{
|
||||
u32 *endPos = exceptionsPage + 0x400;
|
||||
|
||||
u32 *initFPU;
|
||||
for(initFPU = exceptionsPage; initFPU < endPos && *initFPU != 0xE1A0D002; initFPU++);
|
||||
|
||||
u32 *freeSpace;
|
||||
for(freeSpace = initFPU; freeSpace < endPos && *freeSpace != 0xFFFFFFFF; freeSpace++);
|
||||
|
||||
u32 *mcuReboot;
|
||||
for(mcuReboot = exceptionsPage; mcuReboot < endPos && *mcuReboot != 0xE3A0A0C2; mcuReboot++);
|
||||
|
||||
if(initFPU == endPos || freeSpace == endPos || mcuReboot == endPos || *(u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 36) != 0xFFFFFFFF) return 1;
|
||||
|
||||
initFPU += 3;
|
||||
mcuReboot -= 2;
|
||||
|
||||
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
|
||||
|
||||
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 8) - 32); //Undefined Instruction
|
||||
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 12) - 32); //Prefetch Abort
|
||||
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4) - 32); //FIQ
|
||||
|
||||
for(u32 *pos = dAbtHandler; *pos != stackAddress; pos++)
|
||||
{
|
||||
u32 va_dst = 0xFFFF0000 + (((u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4)) - (u8 *)exceptionsPage);
|
||||
u32 va_src;
|
||||
switch(*pos)
|
||||
{
|
||||
case 0xF96D0513: //srsdb sp!, 0x13
|
||||
va_src = dAbtHandlerMemAddress + ((u8 *)pos - (u8 *)dAbtHandler);
|
||||
*pos = MAKE_BRANCH((u8 *)va_src, (u8 *)va_dst);
|
||||
break;
|
||||
case 0xE29EF004: //subs pc, lr, 4
|
||||
pos++;
|
||||
*pos++ = 0xE8BD000F;// pop {r0-r3}
|
||||
va_src = dAbtHandlerMemAddress + ((u8 *)pos - (u8 *)dAbtHandler);
|
||||
*pos = MAKE_BRANCH((u8 *)va_src, (u8 *)va_dst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 32); pos++)
|
||||
{
|
||||
switch(*pos) //Perform relocations
|
||||
{
|
||||
case 0xFFFF3000: *pos = stackAddress - 0x10; break;
|
||||
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
|
||||
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
|
||||
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
|
||||
case 0xBEEFBEEF: *pos = codeSetOffset; break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void detectAndProcessExceptionDumps(void)
|
||||
{
|
||||
volatile ExceptionDumpHeader *dumpHeader = (volatile ExceptionDumpHeader *)0x25000000;
|
||||
|
||||
if(dumpHeader->magic[0] != 0xDEADC0DE || dumpHeader->magic[1] != 0xDEADCAFE || (dumpHeader->processor != 9 && dumpHeader->processor != 11)) return;
|
||||
|
||||
const vu32 *regs = (vu32 *)((vu8 *)dumpHeader + sizeof(ExceptionDumpHeader));
|
||||
const vu8 *stackDump = (vu8 *)regs + dumpHeader->registerDumpSize + dumpHeader->codeDumpSize;
|
||||
const vu8 *additionalData = stackDump + dumpHeader->stackDumpSize;
|
||||
|
||||
const char *handledExceptionNames[] = {
|
||||
"FIQ", "undefined instruction", "prefetch abort", "data abort"
|
||||
};
|
||||
|
||||
const char *specialExceptions[] = {
|
||||
"kernel panic", "svcBreak"
|
||||
};
|
||||
|
||||
const char *registerNames[] = {
|
||||
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12",
|
||||
"SP", "LR", "PC", "CPSR", "FPEXC"
|
||||
};
|
||||
|
||||
initScreens();
|
||||
|
||||
drawString(true, 10, 10, COLOR_RED, "An exception occurred");
|
||||
u32 posY;
|
||||
if(dumpHeader->processor == 11) posY = drawFormattedString(true, 10, 30, COLOR_WHITE, "Processor: ARM11 (core %u)", dumpHeader->core);
|
||||
else posY = drawString(true, 10, 30, COLOR_WHITE, "Processor: ARM9");
|
||||
|
||||
if(dumpHeader->type == 2)
|
||||
{
|
||||
if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 4)
|
||||
{
|
||||
u32 instr = *(vu32 *)(stackDump - 4);
|
||||
if(instr == 0xE12FFF7E)
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s (%s)", handledExceptionNames[dumpHeader->type], specialExceptions[0]);
|
||||
else if(instr == 0xEF00003C)
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s (%s)", handledExceptionNames[dumpHeader->type], specialExceptions[1]);
|
||||
else
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]);
|
||||
}
|
||||
else if((regs[16] & 0x20) != 0 && dumpHeader->codeDumpSize >= 2)
|
||||
{
|
||||
u16 instr = *(vu16 *)(stackDump - 2);
|
||||
if(instr == 0xDF3C)
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s (%s)", handledExceptionNames[dumpHeader->type], specialExceptions[0]);
|
||||
else
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]);
|
||||
}
|
||||
}
|
||||
else
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]);
|
||||
|
||||
if(dumpHeader->processor == 11 && dumpHeader->additionalDataSize != 0)
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE,
|
||||
"Current process: %.8s (%016llX)", (const char *)additionalData, *(vu64 *)(additionalData + 8));
|
||||
posY += SPACING_Y;
|
||||
|
||||
for(u32 i = 0; i < 17; i += 2)
|
||||
{
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "%-7s%08X", registerNames[i], regs[i]);
|
||||
|
||||
if(i != 16 || dumpHeader->processor != 9)
|
||||
posY = drawFormattedString(true, 10 + 22 * SPACING_X, posY, COLOR_WHITE, "%-7s%08X", registerNames[i + 1], regs[i + 1]);
|
||||
}
|
||||
|
||||
posY += SPACING_Y;
|
||||
|
||||
u32 mode = regs[16] & 0xF;
|
||||
if(dumpHeader->type == 3 && (mode == 7 || mode == 11))
|
||||
posY = drawString(true, 10, posY + SPACING_Y, COLOR_YELLOW, "Incorrect dump: failed to dump code and/or stack") + SPACING_Y;
|
||||
|
||||
u32 posYBottom = drawString(false, 10, 10, COLOR_WHITE, "Stack dump:") + SPACING_Y;
|
||||
|
||||
for(u32 line = 0; line < 19 && stackDump < additionalData; line++)
|
||||
{
|
||||
posYBottom = drawFormattedString(false, 10, posYBottom + SPACING_Y, COLOR_WHITE, "%08X:", regs[13] + 8 * line);
|
||||
|
||||
for(u32 i = 0; i < 8 && stackDump < additionalData; i++, stackDump++)
|
||||
drawFormattedString(false, 10 + 10 * SPACING_X + 3 * i * SPACING_X, posYBottom, COLOR_WHITE, "%02X", *stackDump);
|
||||
}
|
||||
|
||||
char folderPath[12],
|
||||
path[36],
|
||||
fileName[24];
|
||||
|
||||
sprintf(folderPath, "dumps/arm%u", dumpHeader->processor);
|
||||
findDumpFile(folderPath, fileName);
|
||||
sprintf(path, "%s/%s", folderPath, fileName);
|
||||
|
||||
if(fileWrite((void *)dumpHeader, path, dumpHeader->totalSize))
|
||||
{
|
||||
posY = drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, "You can find a dump in the following file:");
|
||||
posY = drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, path) + SPACING_Y;
|
||||
}
|
||||
else posY = drawString(true, 10, posY + SPACING_Y, COLOR_RED, "Error writing the dump file");
|
||||
|
||||
drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Press any button to shutdown");
|
||||
|
||||
memset32((void *)dumpHeader, 0, dumpHeader->totalSize);
|
||||
|
||||
waitInput(false);
|
||||
mcuPowerOff();
|
||||
}
|
||||
32
source/exceptions.h
Normal file
32
source/exceptions.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define MAKE_BRANCH(src,dst) (0xEA000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
#define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
|
||||
void installArm9Handlers(void);
|
||||
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset, u32 *dAbtHandler, u32 dAbtHandlerMemAddress);
|
||||
void detectAndProcessExceptionDumps(void);
|
||||
@@ -1,180 +1,288 @@
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
Prototype.
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
First stable version.
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code.
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
|
||||
Prototype.
|
||||
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
|
||||
The first release.
|
||||
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
|
||||
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.11a (September 05, 2015)
|
||||
|
||||
Fixed wrong media change can lead a deadlock at thread-safe configuration.
|
||||
Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
|
||||
Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
|
||||
Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
|
||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12 (April 12, 2016)
|
||||
|
||||
Added support for exFAT file system. (_FS_EXFAT)
|
||||
Added f_expand(). (_USE_EXPAND)
|
||||
Changed some members in FINFO structure and behavior of f_readdir().
|
||||
Added an option _USE_CHMOD.
|
||||
Removed an option _WORD_ACCESS.
|
||||
Fixed errors in the case conversion table of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12a (July 10, 2016)
|
||||
|
||||
Added support for creating exFAT volume with some changes of f_mkfs().
|
||||
Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed.
|
||||
f_forward() is available regardless of _FS_TINY.
|
||||
Fixed f_mkfs() creates wrong volume. (appeared at R0.12)
|
||||
Fixed wrong memory read in create_name(). (appeared at R0.12)
|
||||
Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD.
|
||||
|
||||
|
||||
|
||||
R0.12b (September 04, 2016)
|
||||
|
||||
Made f_rename() be able to rename objects with the same name but case.
|
||||
Fixed an error in the case conversion teble of code page 866. (ff.c)
|
||||
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
|
||||
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
|
||||
Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12)
|
||||
Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12)
|
||||
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
|
||||
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.12c (March 04, 2017)
|
||||
|
||||
Improved write throughput at the fragmented file on the exFAT volume.
|
||||
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
|
||||
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
|
||||
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
FatFs Module Source Files R0.11
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
history.txt Revision history.
|
||||
ffconf.h Configuration file for FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
ff.c FatFs module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module that written
|
||||
to control the target storage device.
|
||||
|
||||
FatFs Module Source Files R0.12c
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
00history.txt Revision history.
|
||||
ff.c FatFs module.
|
||||
ffconf.h Configuration file of FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external modules.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and it does not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module written to
|
||||
control the storage device that attached to the target system.
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
|
||||
#include "diskio.h" /* FatFs lower layer API */
|
||||
#include "sdmmc/sdmmc.h"
|
||||
#include "../crypto.h"
|
||||
|
||||
/* Definitions of physical drive number for each media */
|
||||
#define SDCARD 0
|
||||
#define CTRNAND 1
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
@@ -30,12 +34,26 @@ DSTATUS disk_status (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
sdmmc_sdcard_init();
|
||||
return RES_OK;
|
||||
DSTATUS ret;
|
||||
static u32 sdmmcInitResult = 4;
|
||||
|
||||
if(sdmmcInitResult == 4) sdmmcInitResult = sdmmc_sdcard_init();
|
||||
|
||||
if(pdrv == CTRNAND)
|
||||
{
|
||||
if(!(sdmmcInitResult & 1))
|
||||
{
|
||||
ctrNandInit();
|
||||
ret = 0;
|
||||
}
|
||||
else ret = STA_NOINIT;
|
||||
}
|
||||
else ret = (!(sdmmcInitResult & 2)) ? 0 : STA_NOINIT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,18 +63,14 @@ DSTATUS disk_initialize (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_read (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
if (sdmmc_sdcard_readsectors(sector, count, buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, buff)) ||
|
||||
(pdrv == CTRNAND && !ctrNandRead(sector, count, buff))) ? RES_OK : RES_PARERR;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,18 +81,14 @@ DRESULT disk_read (
|
||||
|
||||
#if _USE_WRITE
|
||||
DRESULT disk_write (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
return ((pdrv == SDCARD && !sdmmc_sdcard_writesectors(sector, count, buff)) ||
|
||||
(pdrv == CTRNAND && !ctrNandWrite(sector, count, buff))) ? RES_OK : RES_PARERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
10837
source/fatfs/ff.c
10837
source/fatfs/ff.c
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user