1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
use VarIntReader;
use ;
// Assembly implementation of the patch overwriter (`__patcher_overwrite`).
//
// The overwriter is responsible for self-modifying the currently running code
// in memory with the new version built at 0x07E00000 by the first patcher stage.
//
// In other words, this code is responsible for actually "applying" the patch.
global_asm!;
global_asm!;
/// Internal patcher state representing what the patcher is attempting to do.
/// Differential Upload Patcher
///
/// This function builds a modified version of the user program in memory with a binary patch (generated
/// by `bidiff` in cargo-v5) applied to it, then overwrites (self-modifies) the current program with the
/// newer version. This allows us to only upload a binary diff of what has changed from the original (the
/// "base") file, which is significanly smaller than reuploading the entire binary every time.
///
/// # Overview
///
/// Patching is performed in two steps. The first step is performed by this function, and involves
/// building the "newer" binary using the uploaded patch file and the current program as a reference.
/// The second step of patching is performed by the `__patcher_overwrite` assembly routine, which
/// is responsible for actually self-modifying the current program's active memory and preparing CPU1
/// to re-run the now patched program memory.
///
/// ## Stage 1 - Building the new binary
///
/// The first stage of patching involves building the new (patched) binary. When `cargo-v5` uploads a
/// new patch file to the brain, we tell it to load the file into memory at address `0x07A00000` — 6mb
/// from the end of the RWX memory block allocated to user code. This 6mb region of memory spanning
/// `0x07A00000`..`0x80000000` is reserved specifically for the patcher to use and is split into three
/// 2mb subregions (which will be discussed later).
///
/// In order to actually use the patch file to build a new binary, we need a reference to the original
/// "base binary" that it's patching over. The program running on the brain before the patch is applied
/// is always the original binary, so in the `_boot` routine (vexide's assembly entrypoint) we preemptively
/// make a copy of the currently running binary before any Rust code gets the chance to modify a writable
/// section of the binary like `.data` or `.bss` (which would corrupt the patch). This unmodified copy of
/// the old binary is copied to address `0x07C00000` (2mb after where our patch is loaded).
///
/// Finally, using the copy of the old binary and the patch, we are able to apply bidiff's [`bipatch`
/// algorithm](https://github.com/divvun/bidiff/blob/main/crates/bipatch/src/lib.rs) to build the new
/// binary file that we will run. This new binary is built at address `0x07E00000` (4mb after where our
/// patch is loaded).
///
/// ## Stage 2 - Overwriting the old binary with the new one
///
/// The second stage of the patch process is handled by the `__patcher_overwrite` assembly routine. This
/// routine is responsible for overwriting our currently running code with that new version we just built.
/// This involves memcpy-ing our new binary at `0x07E00000` to the start of user memory (`0x03800000`).
/// Doing this is far easier said than done though, and requires some important considerations as to not
/// shoot ourselves in the foot:
///
/// - When overwriting, it is *absolutely imperative* that the overwriter not depend on memory that is
/// in the process of being overwritten. This means that the overwriting routine must be done entirely
/// from a fixed address in memory and not reference any outside functions or data. We ensure this by
/// writing the entire stage-2 patch routine and memcpy implementation in assembly and placing these
/// instructions in a linker section (`.text.overwriter`) at a fixed address in memory.
///
/// - There is also a potential opportunity for soundness problems if the overwriter modifies itself while
/// running. This is more unlikely (due to the next point), but has bad implications if the patcher
/// self-modifies its own instructions while in the process running. Writing this part in assembly also
/// ensures this won't happen, because assembly will always compile down to the same instructions
/// regardless of how we tell our compiler to optimize our code. This routine will not change.
///
/// - Finally, there is the problem of cache coherency. ARMv7-A, like most modern architectures, caches
/// instructions fetched from memory into an on-chip L1 cache called the icache. There is also a similar
/// L1 cache called the dcache for memory/data accesses. ARM's cache model does not guarantee L1 cache will
/// always be synchronized (coherent) with what's actually present in physical memory when working with
/// self-modifying code, so we need to explicitly "clean" the instruction cache to bring it back to a
/// [point-of-unification (PoU)](https://developer.arm.com/documentation/den0013/d/Caches/Point-of-coherency-and-unification)
/// with physical memory, ensuring that when we jump back to the start of our program, we are actually
/// executing from the newly overwritten memory rather than stale instructions from icache. This process
/// of invalidating and cleaning instruction caches is described to further detail in
/// [ARM's documentation](https://developer.arm.com/documentation/den0013/latest/Caches/Invalidating-and-cleaning-cache-memory).
///
/// # Safety
///
/// The caller must ensure that the patch loaded at 0x07A00000 has been built using the currently running
/// binary as the basis for the patch.
pub unsafe