/
boot.cc
195 lines (184 loc) · 4.8 KB
/
boot.cc
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
#define CHERIOT_NO_AMBIENT_MALLOC
#define CHERIOT_NO_NEW_DELETE
#include <cheri.hh>
#include <platform-uart.hh>
using namespace CHERI;
namespace
{
/**
* The bit that indicates that the button is pressed.
*/
constexpr uint32_t ButtonBit = 0x10;
/**
* Write a string to the UART.
*/
void puts(volatile Uart *uart, const char *msg, bool newline = true)
{
while (char c = (*msg++))
{
uart->blocking_write(c);
}
if (newline)
{
uart->blocking_write('\n');
}
}
/**
* Convert a hex character to a 4-bit value.
*/
uint8_t hex_to_nybble(char c)
{
switch (c)
{
case '0' ... '9':
return c - '0';
case 'a' ... 'f':
return c - 'a' + 10;
case 'A' ... 'F':
return c - 'A' + 10;
}
return 0;
}
/**
* Convert a pair of hex characters to a byte.
*/
uint8_t hex_to_byte(char c0, char c1)
{
uint8_t byte = hex_to_nybble(c0) << 4;
byte += hex_to_nybble(c1);
return byte;
}
/**
* Print a word to the UART.
*/
void print_word(volatile Uart *uart, uint32_t word, bool newline = true)
{
// Buffer to hold the output.
char buffer[9];
// Convert a 4-bit value to a hex digit.
auto nybble_to_digit = [](int nybble) {
if (nybble < 10)
{
return nybble + '0';
}
else
{
return nybble - 10 + 'A';
}
};
// Convert each byte to a pair of hex digits
for (int i = 0; i < 4; i++)
{
int digit = (word >> ((3 - i) * 8)) & 0xff;
buffer[(i * 2)] = nybble_to_digit(digit >> 4);
buffer[(i * 2) + 1] = nybble_to_digit(digit & 0xf);
}
// Null terminator
buffer[8] = 0;
// Print the generated string.
puts(uart, buffer, newline);
}
/**
* Load firmware from the specified UART, storing it via the capability in
* iram.
*/
uint32_t load_firmware(volatile Uart *uart, uint32_t *iram, volatile uint32_t *gpioRead)
{
auto read_byte = [&]() {
return hex_to_byte(uart->blocking_read(), uart->blocking_read());
};
uint32_t words = 0;
puts(uart, "Ready to load firmware, hold BTN0 to ignore UART input.");
uint32_t word;
while (true)
{
if (*gpioRead & ButtonBit)
{
puts(uart, "Button pressed, ignoring UART input...");
while (*gpioRead & ButtonBit)
{
// Discard data from the UART.
(void)uart->data;
}
return 0;
}
char c = uart->blocking_read();
if (c == '\n')
{
puts(uart, "\nFinished loading. Last word was: ", false);
print_word(uart, word);
puts(uart, "Number of words loaded to IRAM: ", false);
print_word(uart, words);
return Capability{iram}.address();
}
// Read a little-endian word
word = hex_to_byte(c, uart->blocking_read()) << 24;
word += read_byte() << 16;
word += read_byte() << 8;
word += read_byte();
// Store the word in memory
*(iram++) = word;
// Read the newline.
c = uart->blocking_read();
// Print the first word that we read for some sanity checking.
if (words == 0)
{
puts(uart, "Starting loading. First word was: ", false);
print_word(uart, word);
}
// Print a . for every KiB (256 4-byte words)
if ((words & 0xff) == 0)
{
uart->blocking_write('.');
}
words++;
// If this wasn't a newline, give up. We'll try again in the outer
// loop.
if (c != '\n')
{
puts(uart,
"Character that was not a newline read after the end of a "
"word. Giving up.");
return 0;
}
}
}
} // namespace
/**
* C++ entry point for the loader. This is called from assembly, with the
* read-write root in the first argument.
*/
extern "C" uint32_t rom_loader_entry(void *rwRoot)
{
Capability<void> root{rwRoot};
// Create a bounded capability to the UART
Capability<volatile Uart> uart = root.cast<volatile Uart>();
uart.address() = 0x8f00b000;
uart.bounds() = 0x100;
// Initialise the UART.
uart->init();
// Create a bounded capability to IRAM
Capability<uint32_t> iram = root.cast<uint32_t>();
iram.address() = 0x20040000;
// The actual bounds are 0x40000, but carve off 1 KiB for our stack. We
// don't actually use anything like that much, but a typical firmware image
// isn't going to be 255 KiB either.
iram.bounds() = 0x3fc00;
// Set up the bounds for the GPIO read channel that is connected to the
// buttons.
Capability<volatile uint32_t> gpioRead = root.cast<volatile uint32_t>();
gpioRead.address() = 0x8f00f000;
gpioRead.bounds() = sizeof(uint32_t);
uint32_t end;
// Make messages from the loader red
puts(uart, "\033[31;1m");
// Try loading until we have successfully loaded some data and not see an
// error condition.
while ((end = load_firmware(uart, iram, gpioRead)) == 0) {}
// Return the end location. The assembly code will zero the memory, which
// includes our stack.
puts(uart, "Loaded firmware, jumping to IRAM.");
// Reset the colour
puts(uart, "\033[0m");
return end;
}