-
Notifications
You must be signed in to change notification settings - Fork 0
/
patchsum.c
167 lines (135 loc) · 3.87 KB
/
patchsum.c
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
/**
* Patchsum -- a tool to fix RISC OS 3.1x ROM headers and footers.
*
* Based on Bigsplit2 and CRC by Acorn, released by RISC OS Open Ltd:
* https://gitlab.riscosopen.org/RiscOS/Utilities/Release/bigsplit2
*/
#include <stdio.h>
#include <stdint.h>
/* A fundamental constant of the CRC algorithm */
#define CRC_MAGIC 0xA001
/* I don't think you'll want to change this one */
#define CRC_TABLE_SIZE (1<<8)
/* CRC table to speed up CRC calculation */
static uint16_t crc_table[CRC_TABLE_SIZE];
/* CRC accumulator */
static uint16_t crc_acc[4] = {0,0,0,0};
/// Create CRC table, must be called at startup
static void make_crc_table(void) {
int i,j;
uint16_t crc;
for(i=0;i<CRC_TABLE_SIZE;i++) {
crc=i;
for(j=0;j<8;j++)
crc=(crc>>1)^(crc&1?CRC_MAGIC:0);
crc_table[i]=crc;
};
}
// Update the CRC with a 4-byte quad
static void update_crc_quad(const uint8_t *quad)
{
for (int i=0; i<4; i++) {
crc_acc[i] ^= (quad[i] & 0xFF);
crc_acc[i] = (crc_acc[i] >> 8) ^ crc_table[crc_acc[i] & 0xff];
}
}
int main(int argc, char **argv)
{
uint32_t csum = 0;
uint8_t bytes[4];
FILE *fp;
if (argc == 1) {
fprintf(stderr, "syntax: patchsum romfile\n");
return -1;
}
fp = fopen(argv[1], "r+b");
if (fp == NULL) {
fprintf(stderr, "cannot open input file\n");
return -1;
}
make_crc_table();
// get the filesize
fseek(fp, 0, SEEK_END);
size_t romsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
// Check ROM size is a multiple of the RISC OS word size
if ((romsize % 4) != 0) {
fprintf(stderr,
"Input file length is not a multple of 4.\n"
"The ROM file must be padded to a multiple of 4 bytes, with a minimum of\n"
"12 spare bytes at the EOF for the checksums.\n");
return -1;
}
//
// The RISC OS ROM starts with a header which looks like this:
//
// * 4-byte jump to startup code
// * 4-byte ROM length (little endian)
// * 4-byte pointer to code vector table
// * 4-byte pointer to data vector table
// * 4-byte pointer to branch table
// * Three unused words which encode branches to the start of ROM.
//
// The vector tables are used to allow downloaded selftest code to use the ROM selftest subroutines.
//
// We need to patch the ROM length or the selftest will miscalculate the ROM checksum and fail to boot.
//
// Source: https://gitlab.riscosopen.org/RiscOS/Sources/Kernel/-/blob/RO_3_60/OldTestSrc/Begin#L149
//
// patch ts_Rom_length
printf("Setting ts_Rom_length to 0x%08lX (%lu bytes = %lu MiB)\n",
romsize, romsize, romsize / 1048576);
fseek(fp, 4, SEEK_SET);
bytes[0] = romsize & 0xFF;
bytes[1] = (romsize >> 8) & 0xFF;
bytes[2] = (romsize >> 16) & 0xFF;
bytes[3] = (romsize >> 24) & 0xFF;
fwrite(&bytes, 1, 4, fp);
/////////////////
// Checksum recalculation
/////////////////
// back to the start again...
fseek(fp, 0, SEEK_SET);
// ignore the last 3x 32-bit words in ROM
romsize -= (3*4);
// checksum time!
while (romsize > 0) {
romsize -= 4;
fread(&bytes, 1, 4, fp);
csum +=
((uint32_t)bytes[0]) |
((uint32_t)bytes[1] << 8) |
((uint32_t)bytes[2] << 16) |
((uint32_t)bytes[3] << 24);
// update the CRC too
update_crc_quad(bytes);
}
// calculate the expected checksum
csum = 0-csum;
// convert to bytes and write
printf("Patching additive checksum...\n");
bytes[0] = csum & 0xFF;
bytes[1] = (csum >> 8) & 0xFF;
bytes[2] = (csum >> 16) & 0xFF;
bytes[3] = (csum >> 24) & 0xFF;
fwrite(&bytes, 1, 4, fp);
// update the CRC with the additive checksum
update_crc_quad(bytes);
// status message and print the CRC
printf("Patching CRC...\n");
for (int i=0; i<4; i++) {
printf(" crc[%d] = 0x%04X\n", i, crc_acc[i]);
}
// patch the CRC
for (int i=0; i<4; i++) {
bytes[i] = crc_acc[i] & 0xFF;
}
fwrite(&bytes, 1, 4, fp);
for (int i=0; i<4; i++) {
bytes[i] = (crc_acc[i] >> 8) & 0xFF;
}
fwrite(&bytes, 1, 4, fp);
// and... done!
fclose(fp);
return 0;
}