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