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