xref: /aosp_15_r20/external/flashrom/printlock.c (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
1*0d6140beSAndroid Build Coastguard Worker /*
2*0d6140beSAndroid Build Coastguard Worker  * This file is part of the flashrom project.
3*0d6140beSAndroid Build Coastguard Worker  *
4*0d6140beSAndroid Build Coastguard Worker  * Copyright (C) 2000 Silicon Integrated System Corporation
5*0d6140beSAndroid Build Coastguard Worker  * Copyright (C) 2006 Giampiero Giancipoli <[email protected]>
6*0d6140beSAndroid Build Coastguard Worker  * Copyright (C) 2006 coresystems GmbH <[email protected]>
7*0d6140beSAndroid Build Coastguard Worker  * Copyright (C) 2007-2012 Carl-Daniel Hailfinger
8*0d6140beSAndroid Build Coastguard Worker  * Copyright (C) 2009 Sean Nelson <[email protected]>
9*0d6140beSAndroid Build Coastguard Worker  * Copyright (C) 2014 Stefan Tauner
10*0d6140beSAndroid Build Coastguard Worker  *
11*0d6140beSAndroid Build Coastguard Worker  * This program is free software; you can redistribute it and/or modify
12*0d6140beSAndroid Build Coastguard Worker  * it under the terms of the GNU General Public License as published by
13*0d6140beSAndroid Build Coastguard Worker  * the Free Software Foundation; either version 2 of the License, or
14*0d6140beSAndroid Build Coastguard Worker  * (at your option) any later version.
15*0d6140beSAndroid Build Coastguard Worker  *
16*0d6140beSAndroid Build Coastguard Worker  * This program is distributed in the hope that it will be useful,
17*0d6140beSAndroid Build Coastguard Worker  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18*0d6140beSAndroid Build Coastguard Worker  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19*0d6140beSAndroid Build Coastguard Worker  * GNU General Public License for more details.
20*0d6140beSAndroid Build Coastguard Worker  */
21*0d6140beSAndroid Build Coastguard Worker 
22*0d6140beSAndroid Build Coastguard Worker #include "flash.h"
23*0d6140beSAndroid Build Coastguard Worker #include "chipdrivers.h"
24*0d6140beSAndroid Build Coastguard Worker 
25*0d6140beSAndroid Build Coastguard Worker 
26*0d6140beSAndroid Build Coastguard Worker struct unlockblock {
27*0d6140beSAndroid Build Coastguard Worker 	unsigned int size;
28*0d6140beSAndroid Build Coastguard Worker 	unsigned int count;
29*0d6140beSAndroid Build Coastguard Worker };
30*0d6140beSAndroid Build Coastguard Worker 
31*0d6140beSAndroid Build Coastguard Worker typedef int (*unlockblock_func)(const struct flashctx *flash, chipaddr offset);
regspace2_walk_unlockblocks(const struct flashctx * flash,const struct unlockblock * block,unlockblock_func func)32*0d6140beSAndroid Build Coastguard Worker static int regspace2_walk_unlockblocks(const struct flashctx *flash, const struct unlockblock *block, unlockblock_func func)
33*0d6140beSAndroid Build Coastguard Worker {
34*0d6140beSAndroid Build Coastguard Worker 	chipaddr off = flash->virtual_registers + 2;
35*0d6140beSAndroid Build Coastguard Worker 	while (block->count != 0) {
36*0d6140beSAndroid Build Coastguard Worker 		unsigned int j;
37*0d6140beSAndroid Build Coastguard Worker 		for (j = 0; j < block->count; j++) {
38*0d6140beSAndroid Build Coastguard Worker 			if (func(flash, off))
39*0d6140beSAndroid Build Coastguard Worker 				return -1;
40*0d6140beSAndroid Build Coastguard Worker 			off += block->size;
41*0d6140beSAndroid Build Coastguard Worker 		}
42*0d6140beSAndroid Build Coastguard Worker 		block++;
43*0d6140beSAndroid Build Coastguard Worker 	}
44*0d6140beSAndroid Build Coastguard Worker 	return 0;
45*0d6140beSAndroid Build Coastguard Worker }
46*0d6140beSAndroid Build Coastguard Worker 
47*0d6140beSAndroid Build Coastguard Worker #define REG2_RWLOCK ((1 << 2) | (1 << 0))
48*0d6140beSAndroid Build Coastguard Worker #define REG2_LOCKDOWN (1 << 1)
49*0d6140beSAndroid Build Coastguard Worker #define REG2_MASK (REG2_RWLOCK | REG2_LOCKDOWN)
50*0d6140beSAndroid Build Coastguard Worker 
printlock_regspace2_block(const struct flashctx * flash,chipaddr lockreg)51*0d6140beSAndroid Build Coastguard Worker static int printlock_regspace2_block(const struct flashctx *flash, chipaddr lockreg)
52*0d6140beSAndroid Build Coastguard Worker {
53*0d6140beSAndroid Build Coastguard Worker 	uint8_t state = chip_readb(flash, lockreg);
54*0d6140beSAndroid Build Coastguard Worker 	msg_cdbg("Lock status of block at 0x%0*" PRIxPTR " is ", PRIxPTR_WIDTH, lockreg);
55*0d6140beSAndroid Build Coastguard Worker 	switch (state & REG2_MASK) {
56*0d6140beSAndroid Build Coastguard Worker 	case 0:
57*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Full Access.\n");
58*0d6140beSAndroid Build Coastguard Worker 		break;
59*0d6140beSAndroid Build Coastguard Worker 	case 1:
60*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Write Lock (Default State).\n");
61*0d6140beSAndroid Build Coastguard Worker 		break;
62*0d6140beSAndroid Build Coastguard Worker 	case 2:
63*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Locked Open (Full Access, Locked Down).\n");
64*0d6140beSAndroid Build Coastguard Worker 		break;
65*0d6140beSAndroid Build Coastguard Worker 	case 3:
66*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Write Lock, Locked Down.\n");
67*0d6140beSAndroid Build Coastguard Worker 		break;
68*0d6140beSAndroid Build Coastguard Worker 	case 4:
69*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Read Lock.\n");
70*0d6140beSAndroid Build Coastguard Worker 		break;
71*0d6140beSAndroid Build Coastguard Worker 	case 5:
72*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Read/Write Lock.\n");
73*0d6140beSAndroid Build Coastguard Worker 		break;
74*0d6140beSAndroid Build Coastguard Worker 	case 6:
75*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Read Lock, Locked Down.\n");
76*0d6140beSAndroid Build Coastguard Worker 		break;
77*0d6140beSAndroid Build Coastguard Worker 	case 7:
78*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Read/Write Lock, Locked Down.\n");
79*0d6140beSAndroid Build Coastguard Worker 		break;
80*0d6140beSAndroid Build Coastguard Worker 	}
81*0d6140beSAndroid Build Coastguard Worker 	return 0;
82*0d6140beSAndroid Build Coastguard Worker }
83*0d6140beSAndroid Build Coastguard Worker 
printlock_regspace2_uniform(struct flashctx * flash,unsigned long block_size)84*0d6140beSAndroid Build Coastguard Worker static int printlock_regspace2_uniform(struct flashctx *flash, unsigned long block_size)
85*0d6140beSAndroid Build Coastguard Worker {
86*0d6140beSAndroid Build Coastguard Worker 	const unsigned int elems = flash->chip->total_size * 1024 / block_size;
87*0d6140beSAndroid Build Coastguard Worker 	struct unlockblock blocks[2] = {{.size = block_size, .count = elems}};
88*0d6140beSAndroid Build Coastguard Worker 	return regspace2_walk_unlockblocks(flash, blocks, &printlock_regspace2_block);
89*0d6140beSAndroid Build Coastguard Worker }
90*0d6140beSAndroid Build Coastguard Worker 
printlock_regspace2_uniform_64k(struct flashctx * flash)91*0d6140beSAndroid Build Coastguard Worker int printlock_regspace2_uniform_64k(struct flashctx *flash)
92*0d6140beSAndroid Build Coastguard Worker {
93*0d6140beSAndroid Build Coastguard Worker 	return printlock_regspace2_uniform(flash, 64 * 1024);
94*0d6140beSAndroid Build Coastguard Worker }
95*0d6140beSAndroid Build Coastguard Worker 
printlock_regspace2_block_eraser_0(struct flashctx * flash)96*0d6140beSAndroid Build Coastguard Worker int printlock_regspace2_block_eraser_0(struct flashctx *flash)
97*0d6140beSAndroid Build Coastguard Worker {
98*0d6140beSAndroid Build Coastguard Worker 	// FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
99*0d6140beSAndroid Build Coastguard Worker 	const struct unlockblock *unlockblocks =
100*0d6140beSAndroid Build Coastguard Worker 		(const struct unlockblock *)flash->chip->block_erasers[0].eraseblocks;
101*0d6140beSAndroid Build Coastguard Worker 	return regspace2_walk_unlockblocks(flash, unlockblocks, &printlock_regspace2_block);
102*0d6140beSAndroid Build Coastguard Worker }
103*0d6140beSAndroid Build Coastguard Worker 
printlock_regspace2_block_eraser_1(struct flashctx * flash)104*0d6140beSAndroid Build Coastguard Worker int printlock_regspace2_block_eraser_1(struct flashctx *flash)
105*0d6140beSAndroid Build Coastguard Worker {
106*0d6140beSAndroid Build Coastguard Worker 	// FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
107*0d6140beSAndroid Build Coastguard Worker 	const struct unlockblock *unlockblocks =
108*0d6140beSAndroid Build Coastguard Worker 		(const struct unlockblock *)flash->chip->block_erasers[1].eraseblocks;
109*0d6140beSAndroid Build Coastguard Worker 	return regspace2_walk_unlockblocks(flash, unlockblocks, &printlock_regspace2_block);
110*0d6140beSAndroid Build Coastguard Worker }
111*0d6140beSAndroid Build Coastguard Worker 
112*0d6140beSAndroid Build Coastguard Worker /* Try to change the lock register at address lockreg from cur to new.
113*0d6140beSAndroid Build Coastguard Worker  *
114*0d6140beSAndroid Build Coastguard Worker  * - Try to unlock the lock bit if requested and it is currently set (although this is probably futile).
115*0d6140beSAndroid Build Coastguard Worker  * - Try to change the read/write bits if requested.
116*0d6140beSAndroid Build Coastguard Worker  * - Try to set the lockdown bit if requested.
117*0d6140beSAndroid Build Coastguard Worker  * Return an error immediately if any of this fails. */
changelock_regspace2_block(const struct flashctx * flash,chipaddr lockreg,uint8_t cur,uint8_t new)118*0d6140beSAndroid Build Coastguard Worker static int changelock_regspace2_block(const struct flashctx *flash, chipaddr lockreg, uint8_t cur, uint8_t new)
119*0d6140beSAndroid Build Coastguard Worker {
120*0d6140beSAndroid Build Coastguard Worker 	/* Only allow changes to known read/write/lockdown bits */
121*0d6140beSAndroid Build Coastguard Worker 	if (((cur ^ new) & ~REG2_MASK) != 0) {
122*0d6140beSAndroid Build Coastguard Worker 		msg_cerr("Invalid lock change from 0x%02x to 0x%02x requested at 0x%0*" PRIxPTR "!\n"
123*0d6140beSAndroid Build Coastguard Worker 			 "Please report a bug at [email protected]\n",
124*0d6140beSAndroid Build Coastguard Worker 			 cur, new, PRIxPTR_WIDTH, lockreg);
125*0d6140beSAndroid Build Coastguard Worker 		return -1;
126*0d6140beSAndroid Build Coastguard Worker 	}
127*0d6140beSAndroid Build Coastguard Worker 
128*0d6140beSAndroid Build Coastguard Worker 	/* Exit early if no change (of read/write/lockdown bits) was requested. */
129*0d6140beSAndroid Build Coastguard Worker 	if (((cur ^ new) & REG2_MASK) == 0) {
130*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg2("Lock bits at 0x%0*" PRIxPTR " not changed.\n", PRIxPTR_WIDTH, lockreg);
131*0d6140beSAndroid Build Coastguard Worker 		return 0;
132*0d6140beSAndroid Build Coastguard Worker 	}
133*0d6140beSAndroid Build Coastguard Worker 
134*0d6140beSAndroid Build Coastguard Worker 	/* Normally the lockdown bit can not be cleared. Try nevertheless if requested. */
135*0d6140beSAndroid Build Coastguard Worker 	if ((cur & REG2_LOCKDOWN) && !(new & REG2_LOCKDOWN)) {
136*0d6140beSAndroid Build Coastguard Worker 		chip_writeb(flash, cur & ~REG2_LOCKDOWN, lockreg);
137*0d6140beSAndroid Build Coastguard Worker 		cur = chip_readb(flash, lockreg);
138*0d6140beSAndroid Build Coastguard Worker 		if ((cur & REG2_LOCKDOWN) == REG2_LOCKDOWN) {
139*0d6140beSAndroid Build Coastguard Worker 			msg_cwarn("Lockdown can't be removed at 0x%0*" PRIxPTR "! New value: 0x%02x.\n",
140*0d6140beSAndroid Build Coastguard Worker 				  PRIxPTR_WIDTH, lockreg, cur);
141*0d6140beSAndroid Build Coastguard Worker 			return -1;
142*0d6140beSAndroid Build Coastguard Worker 		}
143*0d6140beSAndroid Build Coastguard Worker 	}
144*0d6140beSAndroid Build Coastguard Worker 
145*0d6140beSAndroid Build Coastguard Worker 	/* Change read and/or write bit */
146*0d6140beSAndroid Build Coastguard Worker 	if ((cur ^ new) & REG2_RWLOCK) {
147*0d6140beSAndroid Build Coastguard Worker 		/* Do not lockdown yet. */
148*0d6140beSAndroid Build Coastguard Worker 		uint8_t wanted = (cur & ~REG2_RWLOCK) | (new & REG2_RWLOCK);
149*0d6140beSAndroid Build Coastguard Worker 		chip_writeb(flash, wanted, lockreg);
150*0d6140beSAndroid Build Coastguard Worker 		cur = chip_readb(flash, lockreg);
151*0d6140beSAndroid Build Coastguard Worker 		if (cur != wanted) {
152*0d6140beSAndroid Build Coastguard Worker 			msg_cerr("Changing lock bits failed at 0x%0*" PRIxPTR "! New value: 0x%02x.\n",
153*0d6140beSAndroid Build Coastguard Worker 				 PRIxPTR_WIDTH, lockreg, cur);
154*0d6140beSAndroid Build Coastguard Worker 			return -1;
155*0d6140beSAndroid Build Coastguard Worker 		}
156*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Changed lock bits at 0x%0*" PRIxPTR " to 0x%02x.\n",
157*0d6140beSAndroid Build Coastguard Worker 			 PRIxPTR_WIDTH, lockreg, cur);
158*0d6140beSAndroid Build Coastguard Worker 	}
159*0d6140beSAndroid Build Coastguard Worker 
160*0d6140beSAndroid Build Coastguard Worker 	/* Eventually, enable lockdown if requested. */
161*0d6140beSAndroid Build Coastguard Worker 	if (!(cur & REG2_LOCKDOWN) && (new & REG2_LOCKDOWN)) {
162*0d6140beSAndroid Build Coastguard Worker 		chip_writeb(flash, new, lockreg);
163*0d6140beSAndroid Build Coastguard Worker 		cur = chip_readb(flash, lockreg);
164*0d6140beSAndroid Build Coastguard Worker 		if (cur != new) {
165*0d6140beSAndroid Build Coastguard Worker 			msg_cerr("Enabling lockdown FAILED at 0x%0*" PRIxPTR "! New value: 0x%02x.\n",
166*0d6140beSAndroid Build Coastguard Worker 				 PRIxPTR_WIDTH, lockreg, cur);
167*0d6140beSAndroid Build Coastguard Worker 			return -1;
168*0d6140beSAndroid Build Coastguard Worker 		}
169*0d6140beSAndroid Build Coastguard Worker 		msg_cdbg("Enabled lockdown at 0x%0*" PRIxPTR ".\n", PRIxPTR_WIDTH, lockreg);
170*0d6140beSAndroid Build Coastguard Worker 	}
171*0d6140beSAndroid Build Coastguard Worker 
172*0d6140beSAndroid Build Coastguard Worker 	return 0;
173*0d6140beSAndroid Build Coastguard Worker }
174*0d6140beSAndroid Build Coastguard Worker 
unlock_regspace2_block_generic(const struct flashctx * flash,chipaddr lockreg)175*0d6140beSAndroid Build Coastguard Worker static int unlock_regspace2_block_generic(const struct flashctx *flash, chipaddr lockreg)
176*0d6140beSAndroid Build Coastguard Worker {
177*0d6140beSAndroid Build Coastguard Worker 	uint8_t old = chip_readb(flash, lockreg);
178*0d6140beSAndroid Build Coastguard Worker 	/* We don't care for the lockdown bit as long as the RW locks are 0 after we're done */
179*0d6140beSAndroid Build Coastguard Worker 	return changelock_regspace2_block(flash, lockreg, old, old & ~REG2_RWLOCK);
180*0d6140beSAndroid Build Coastguard Worker }
181*0d6140beSAndroid Build Coastguard Worker 
unlock_regspace2_uniform(struct flashctx * flash,unsigned long block_size)182*0d6140beSAndroid Build Coastguard Worker static int unlock_regspace2_uniform(struct flashctx *flash, unsigned long block_size)
183*0d6140beSAndroid Build Coastguard Worker {
184*0d6140beSAndroid Build Coastguard Worker 	const unsigned int elems = flash->chip->total_size * 1024 / block_size;
185*0d6140beSAndroid Build Coastguard Worker 	struct unlockblock blocks[2] = {{.size = block_size, .count = elems}};
186*0d6140beSAndroid Build Coastguard Worker 	return regspace2_walk_unlockblocks(flash, blocks, &unlock_regspace2_block_generic);
187*0d6140beSAndroid Build Coastguard Worker }
188*0d6140beSAndroid Build Coastguard Worker 
unlock_regspace2_uniform_64k(struct flashctx * flash)189*0d6140beSAndroid Build Coastguard Worker static int unlock_regspace2_uniform_64k(struct flashctx *flash)
190*0d6140beSAndroid Build Coastguard Worker {
191*0d6140beSAndroid Build Coastguard Worker 	return unlock_regspace2_uniform(flash, 64 * 1024);
192*0d6140beSAndroid Build Coastguard Worker }
193*0d6140beSAndroid Build Coastguard Worker 
unlock_regspace2_uniform_32k(struct flashctx * flash)194*0d6140beSAndroid Build Coastguard Worker static int unlock_regspace2_uniform_32k(struct flashctx *flash)
195*0d6140beSAndroid Build Coastguard Worker {
196*0d6140beSAndroid Build Coastguard Worker 	return unlock_regspace2_uniform(flash, 32 * 1024);
197*0d6140beSAndroid Build Coastguard Worker }
198*0d6140beSAndroid Build Coastguard Worker 
unlock_regspace2_block_eraser_0(struct flashctx * flash)199*0d6140beSAndroid Build Coastguard Worker static int unlock_regspace2_block_eraser_0(struct flashctx *flash)
200*0d6140beSAndroid Build Coastguard Worker {
201*0d6140beSAndroid Build Coastguard Worker 	// FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
202*0d6140beSAndroid Build Coastguard Worker 	const struct unlockblock *unlockblocks =
203*0d6140beSAndroid Build Coastguard Worker 		(const struct unlockblock *)flash->chip->block_erasers[0].eraseblocks;
204*0d6140beSAndroid Build Coastguard Worker 	return regspace2_walk_unlockblocks(flash, unlockblocks, &unlock_regspace2_block_generic);
205*0d6140beSAndroid Build Coastguard Worker }
206*0d6140beSAndroid Build Coastguard Worker 
unlock_regspace2_block_eraser_1(struct flashctx * flash)207*0d6140beSAndroid Build Coastguard Worker static int unlock_regspace2_block_eraser_1(struct flashctx *flash)
208*0d6140beSAndroid Build Coastguard Worker {
209*0d6140beSAndroid Build Coastguard Worker 	// FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
210*0d6140beSAndroid Build Coastguard Worker 	const struct unlockblock *unlockblocks =
211*0d6140beSAndroid Build Coastguard Worker 		(const struct unlockblock *)flash->chip->block_erasers[1].eraseblocks;
212*0d6140beSAndroid Build Coastguard Worker 	return regspace2_walk_unlockblocks(flash, unlockblocks, &unlock_regspace2_block_generic);
213*0d6140beSAndroid Build Coastguard Worker }
214*0d6140beSAndroid Build Coastguard Worker 
lookup_jedec_blockprotect_func_ptr(const struct flashchip * const chip)215*0d6140beSAndroid Build Coastguard Worker blockprotect_func_t *lookup_jedec_blockprotect_func_ptr(const struct flashchip *const chip)
216*0d6140beSAndroid Build Coastguard Worker {
217*0d6140beSAndroid Build Coastguard Worker 	switch (chip->unlock) {
218*0d6140beSAndroid Build Coastguard Worker 		case UNLOCK_REGSPACE2_BLOCK_ERASER_0: return unlock_regspace2_block_eraser_0;
219*0d6140beSAndroid Build Coastguard Worker 		case UNLOCK_REGSPACE2_BLOCK_ERASER_1: return unlock_regspace2_block_eraser_1;
220*0d6140beSAndroid Build Coastguard Worker 		case UNLOCK_REGSPACE2_UNIFORM_32K: return unlock_regspace2_uniform_32k;
221*0d6140beSAndroid Build Coastguard Worker 		case UNLOCK_REGSPACE2_UNIFORM_64K: return unlock_regspace2_uniform_64k;
222*0d6140beSAndroid Build Coastguard Worker 		default: return NULL; /* fallthough */
223*0d6140beSAndroid Build Coastguard Worker 	};
224*0d6140beSAndroid Build Coastguard Worker }
225