Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/soc/fuse.c
1476 views
1
/*
2
* Copyright (c) 2018 naehrwert
3
* Copyright (c) 2018 shuffle2
4
* Copyright (c) 2018 balika011
5
* Copyright (c) 2019-2023 CTCaer
6
*
7
* This program is free software; you can redistribute it and/or modify it
8
* under the terms and conditions of the GNU General Public License,
9
* version 2, as published by the Free Software Foundation.
10
*
11
* This program is distributed in the hope it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
* more details.
15
*
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
*/
19
20
#include <string.h>
21
22
#include <mem/heap.h>
23
#include <sec/se.h>
24
#include <sec/se_t210.h>
25
#include <soc/fuse.h>
26
#include <soc/hw_init.h>
27
#include <soc/pmc.h>
28
#include <soc/t210.h>
29
#include <soc/timer.h>
30
#include <utils/types.h>
31
32
static const u32 evp_thunk_template[] = {
33
0xe92d0007, // STMFD SP!, {R0-R2}
34
0xe1a0200e, // MOV R2, LR
35
0xe2422002, // SUB R2, R2, #2
36
0xe5922000, // LDR R2, [R2]
37
0xe20220ff, // AND R2, R2, #0xFF
38
0xe1a02082, // MOV R2, R2,LSL#1
39
0xe59f001c, // LDR R0, =evp_thunk_template
40
0xe59f101c, // LDR R1, =thunk_end
41
0xe0411000, // SUB R1, R1, R0
42
0xe59f0018, // LDR R0, =iram_evp_thunks
43
0xe0800001, // ADD R0, R0, R1
44
0xe0822000, // ADD R2, R2, R0
45
0xe3822001, // ORR R2, R2, #1
46
0xe8bd0003, // LDMFD SP!, {R0,R1}
47
0xe12fff12, // BX R2
48
// idx: 15:
49
0x001007b0, // off_1007EC DCD evp_thunk_template
50
0x001007f8, // off_1007F0 DCD thunk_end
51
0x40004c30, // off_1007F4 DCD iram_evp_thunks
52
// thunk_end is here
53
};
54
55
static const u32 evp_thunk_func_offsets_t210b01[] = {
56
0x0010022c, // off_100268 DCD evp_thunk_template
57
0x00100174, // off_10026C DCD thunk_end
58
0x40004164, // off_100270 DCD iram_evp_thunks
59
// thunk_end is here
60
};
61
62
// treated as 12bit values
63
static const u32 hash_vals[] = {1, 2, 4, 8, 0, 3, 5, 6, 7, 9, 10, 11};
64
65
void fuse_disable_program()
66
{
67
FUSE(FUSE_DISABLEREGPROGRAM) = 1;
68
}
69
70
u32 fuse_read_odm(u32 idx)
71
{
72
return FUSE(FUSE_RESERVED_ODMX(idx));
73
}
74
75
u32 fuse_read_odm_keygen_rev()
76
{
77
bool has_new_keygen;
78
79
// Check if it has new keygen.
80
if (hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01)
81
has_new_keygen = true;
82
else
83
has_new_keygen = (fuse_read_odm(4) & 0x800) && fuse_read_odm(0) == 0x8E61ECAE && fuse_read_odm(1) == 0xF2BA3BB2;
84
85
if (has_new_keygen)
86
return (fuse_read_odm(2) & 0x1F);
87
88
return 0;
89
}
90
91
u32 fuse_read_dramid(bool raw_id)
92
{
93
bool tegra_t210 = hw_get_chip_id() == GP_HIDREV_MAJOR_T210;
94
u32 odm4 = fuse_read_odm(4);
95
96
u32 dramid = (odm4 & 0xF8) >> 3;
97
98
// Get extended dram id info.
99
if (!tegra_t210)
100
dramid |= (odm4 & 0x7000) >> 7;
101
102
if (raw_id)
103
return dramid;
104
105
if (tegra_t210)
106
{
107
if (dramid > 7)
108
dramid = 0;
109
}
110
else
111
{
112
if (dramid > 34)
113
dramid = 8;
114
}
115
116
return dramid;
117
}
118
119
u32 fuse_read_hw_state()
120
{
121
if ((fuse_read_odm(4) & 3) != 3)
122
return FUSE_NX_HW_STATE_PROD;
123
else
124
return FUSE_NX_HW_STATE_DEV;
125
}
126
127
u32 fuse_read_hw_type()
128
{
129
if (hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01)
130
{
131
switch ((fuse_read_odm(4) & 0xF0000) >> 16)
132
{
133
case 2:
134
return FUSE_NX_HW_TYPE_HOAG;
135
case 4:
136
return FUSE_NX_HW_TYPE_AULA;
137
case 1:
138
default:
139
return FUSE_NX_HW_TYPE_IOWA;
140
}
141
}
142
143
return FUSE_NX_HW_TYPE_ICOSA;
144
}
145
146
int fuse_set_sbk()
147
{
148
if (FUSE(FUSE_PRIVATE_KEY0) != 0xFFFFFFFF)
149
{
150
// Read SBK from fuses.
151
u32 sbk[4] = {
152
FUSE(FUSE_PRIVATE_KEY0),
153
FUSE(FUSE_PRIVATE_KEY1),
154
FUSE(FUSE_PRIVATE_KEY2),
155
FUSE(FUSE_PRIVATE_KEY3)
156
};
157
158
// Set SBK to slot 14.
159
se_aes_key_set(14, sbk, SE_KEY_128_SIZE);
160
161
// Lock SBK from being read.
162
se_key_acc_ctrl(14, SE_KEY_TBL_DIS_KEYREAD_FLAG);
163
164
return 1;
165
}
166
167
return 0;
168
}
169
170
void fuse_wait_idle()
171
{
172
while (((FUSE(FUSE_CTRL) >> 16) & 0x1F) != FUSE_STATUS_IDLE)
173
;
174
}
175
176
u32 fuse_read(u32 addr)
177
{
178
FUSE(FUSE_ADDR) = addr;
179
FUSE(FUSE_CTRL) = (FUSE(FUSE_ADDR) & ~FUSE_CMD_MASK) | FUSE_READ;
180
fuse_wait_idle();
181
182
return FUSE(FUSE_RDATA);
183
}
184
185
void fuse_read_array(u32 *words)
186
{
187
u32 array_size = (hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01) ?
188
FUSE_ARRAY_WORDS_NUM_B01 : FUSE_ARRAY_WORDS_NUM;
189
190
for (u32 i = 0; i < array_size; i++)
191
words[i] = fuse_read(i);
192
}
193
194
static u32 _parity32_even(const u32 *words, u32 count)
195
{
196
u32 acc = words[0];
197
for (u32 i = 1; i < count; i++)
198
acc ^= words[i];
199
200
u32 lo = ((acc & 0xffff) ^ (acc >> 16)) & 0xff;
201
u32 hi = ((acc & 0xffff) ^ (acc >> 16)) >> 8;
202
u32 x = hi ^ lo;
203
lo = ((x & 0xf) ^ (x >> 4)) & 3;
204
hi = ((x & 0xf) ^ (x >> 4)) >> 2;
205
x = hi ^ lo;
206
207
return (x & 1) ^ (x >> 1);
208
}
209
210
static int _patch_hash_one(u32 *word)
211
{
212
u32 bits20_31 = *word & 0xfff00000;
213
u32 parity_bit = _parity32_even(&bits20_31, 1);
214
u32 hash = 0;
215
216
for (u32 i = 0; i < 12; i++)
217
{
218
if (*word & (1 << (20 + i)))
219
hash ^= hash_vals[i];
220
}
221
222
if (hash == 0)
223
{
224
if (parity_bit == 0)
225
return 0;
226
227
*word ^= 1 << 24;
228
229
return 1;
230
}
231
232
if (parity_bit == 0)
233
return 3;
234
235
for (u32 i = 0; i < ARRAY_SIZE(hash_vals); i++)
236
{
237
if (hash_vals[i] == hash)
238
{
239
*word ^= 1 << (20 + i);
240
return 1;
241
}
242
}
243
244
return 2;
245
}
246
247
static int _patch_hash_multi(u32 *words, u32 count)
248
{
249
u32 parity_bit = _parity32_even(words, count);
250
u32 bits0_14 = words[0] & 0x7fff;
251
u32 bit15 = words[0] & 0x8000;
252
u32 bits16_19 = words[0] & 0xf0000;
253
254
u32 hash = 0;
255
words[0] = bits16_19;
256
for (u32 i = 0; i < count; i++)
257
{
258
u32 w = words[i];
259
if (w)
260
{
261
for (u32 bitpos = 0; bitpos < 32; bitpos++)
262
{
263
if ((w >> bitpos) & 1)
264
hash ^= 0x4000 + i * 32 + bitpos;
265
}
266
}
267
}
268
hash ^= bits0_14;
269
// stupid but this is what original code does.
270
// equivalent to original words[0] &= 0xfff00000
271
words[0] = bits16_19 ^ bit15 ^ bits0_14;
272
273
if (hash == 0)
274
{
275
if (parity_bit == 0)
276
return 0;
277
278
words[0] ^= 0x8000;
279
return 1;
280
}
281
if (parity_bit == 0)
282
return 3;
283
284
u32 bitcount = hash - 0x4000;
285
if (bitcount < 16 || bitcount >= count * 32)
286
{
287
u32 num_set = 0;
288
for (u32 bitpos = 0; bitpos < 15; bitpos++)
289
{
290
if ((hash >> bitpos) & 1)
291
num_set++;
292
}
293
if (num_set != 1)
294
return 2;
295
296
words[0] ^= hash;
297
return 1;
298
}
299
words[bitcount / 32] ^= 1 << (hash & 0x1f);
300
return 1;
301
}
302
303
int fuse_read_ipatch(void (*ipatch)(u32 offset, u32 value))
304
{
305
u32 words[80];
306
u32 word_count;
307
u32 word_addr;
308
u32 word0;
309
u32 total_read = 0;
310
311
word_count = FUSE(FUSE_FIRST_BOOTROM_PATCH_SIZE);
312
word_count &= 0x7F;
313
word_addr = FUSE_ARRAY_WORDS_NUM - 1;
314
315
while (word_count)
316
{
317
total_read += word_count;
318
if (total_read >= ARRAY_SIZE(words))
319
break;
320
321
for (u32 i = 0; i < word_count; i++)
322
{
323
words[i] = fuse_read(word_addr--);
324
// Parse extra T210B01 fuses when the difference is reached.
325
if (hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01 &&
326
word_addr == ((FUSE_ARRAY_WORDS_NUM - 1) -
327
(FUSE_ARRAY_WORDS_NUM_B01 - FUSE_ARRAY_WORDS_NUM) / sizeof(u32)))
328
{
329
word_addr = FUSE_ARRAY_WORDS_NUM_B01 - 1;
330
}
331
}
332
333
word0 = words[0];
334
if (_patch_hash_multi(words, word_count) >= 2)
335
return 1;
336
337
u32 ipatch_count = (words[0] >> 16) & 0xF;
338
if (ipatch_count)
339
{
340
for (u32 i = 0; i < ipatch_count; i++)
341
{
342
u32 word = words[i + 1];
343
u32 addr = (word >> 16) * 2;
344
u32 data = word & 0xFFFF;
345
346
ipatch(addr, data);
347
}
348
}
349
350
words[0] = word0;
351
if ((word0 >> 25) == 0)
352
break;
353
354
if (_patch_hash_one(&word0) >= 2)
355
return 3;
356
357
word_count = word0 >> 25;
358
}
359
360
return 0;
361
}
362
363
int fuse_read_evp_thunk(u32 *iram_evp_thunks, u32 *iram_evp_thunks_len)
364
{
365
u32 words[80];
366
u32 word_count;
367
u32 word_addr;
368
u32 word0;
369
u32 total_read = 0;
370
int evp_thunk_written = 0;
371
void *evp_thunk_dst_addr = 0;
372
bool t210b01 = hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01;
373
u32 *evp_thunk_tmp = (u32 *)malloc(sizeof(evp_thunk_template));
374
375
memcpy(evp_thunk_tmp, evp_thunk_template, sizeof(evp_thunk_template));
376
memset(iram_evp_thunks, 0, *iram_evp_thunks_len);
377
378
if (t210b01)
379
memcpy(&evp_thunk_tmp[15], evp_thunk_func_offsets_t210b01, sizeof(evp_thunk_func_offsets_t210b01));
380
381
word_count = FUSE(FUSE_FIRST_BOOTROM_PATCH_SIZE);
382
word_count &= 0x7F;
383
word_addr = FUSE_ARRAY_WORDS_NUM - 1;
384
385
while (word_count)
386
{
387
total_read += word_count;
388
if (total_read >= ARRAY_SIZE(words))
389
break;
390
391
for (u32 i = 0; i < word_count; i++)
392
{
393
words[i] = fuse_read(word_addr--);
394
// Parse extra T210B01 fuses when the difference is reached.
395
if (hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01 &&
396
word_addr == ((FUSE_ARRAY_WORDS_NUM - 1) -
397
(FUSE_ARRAY_WORDS_NUM_B01 - FUSE_ARRAY_WORDS_NUM) / sizeof(u32)))
398
{
399
word_addr = FUSE_ARRAY_WORDS_NUM_B01 - 1;
400
}
401
}
402
403
word0 = words[0];
404
if (_patch_hash_multi(words, word_count) >= 2)
405
{
406
free(evp_thunk_tmp);
407
return 1;
408
}
409
410
u32 ipatch_count = (words[0] >> 16) & 0xF;
411
u32 insn_count = word_count - ipatch_count - 1;
412
if (insn_count)
413
{
414
if (!evp_thunk_written)
415
{
416
evp_thunk_dst_addr = (void *)iram_evp_thunks;
417
418
memcpy(evp_thunk_dst_addr, (void *)evp_thunk_tmp, sizeof(evp_thunk_template));
419
evp_thunk_dst_addr += sizeof(evp_thunk_template);
420
evp_thunk_written = 1;
421
*iram_evp_thunks_len = sizeof(evp_thunk_template);
422
423
//write32(TEGRA_EXCEPTION_VECTORS_BASE + 0x208, iram_evp_thunks);
424
}
425
426
u32 thunk_patch_len = insn_count * sizeof(u32);
427
memcpy(evp_thunk_dst_addr, &words[ipatch_count + 1], thunk_patch_len);
428
evp_thunk_dst_addr += thunk_patch_len;
429
*iram_evp_thunks_len += thunk_patch_len;
430
}
431
432
words[0] = word0;
433
if ((word0 >> 25) == 0)
434
break;
435
436
if (_patch_hash_one(&word0) >= 2)
437
{
438
free(evp_thunk_tmp);
439
return 3;
440
}
441
442
word_count = word0 >> 25;
443
}
444
445
free(evp_thunk_tmp);
446
447
return 0;
448
}
449
450
bool fuse_check_patched_rcm()
451
{
452
// Check if XUSB in use or Tegra X1+.
453
if (FUSE(FUSE_RESERVED_SW) & (1<<7) || hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01)
454
return true;
455
456
// Check if RCM is ipatched.
457
u32 word_count = FUSE(FUSE_FIRST_BOOTROM_PATCH_SIZE) & 0x7F;
458
u32 word_addr = FUSE_ARRAY_WORDS_NUM - 1;
459
460
while (word_count)
461
{
462
u32 word0 = fuse_read(word_addr);
463
u32 ipatch_count = (word0 >> 16) & 0xF;
464
465
for (u32 i = 0; i < ipatch_count; i++)
466
{
467
u32 word = fuse_read(word_addr - (i + 1));
468
u32 addr = (word >> 16) * 2;
469
if (addr == 0x769A)
470
return true;
471
}
472
473
word_addr -= word_count;
474
word_count = word0 >> 25;
475
}
476
477
return false;
478
}
479
480