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