Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /* Copyright 2018-2019 IBM Corp. */
3 : :
4 : : #define pr_fmt(fmt) "HIOMAP: " fmt
5 : :
6 : : #include <hiomap.h>
7 : : #include <inttypes.h>
8 : : #include <ipmi.h>
9 : : #include <lpc.h>
10 : : #include <mem_region-malloc.h>
11 : : #include <stdbool.h>
12 : : #include <stdint.h>
13 : : #include <string.h>
14 : :
15 : : #include <ccan/container_of/container_of.h>
16 : :
17 : : #include "errors.h"
18 : : #include "ipmi-hiomap.h"
19 : :
20 : : #define CMD_OP_HIOMAP_EVENT 0x0f
21 : :
22 : : struct ipmi_hiomap_result {
23 : : struct ipmi_hiomap *ctx;
24 : : int16_t cc;
25 : : };
26 : :
27 : : #define RESULT_INIT(_name, _ctx) struct ipmi_hiomap_result _name = { _ctx, -1 }
28 : :
29 : 222 : static inline uint32_t blocks_to_bytes(struct ipmi_hiomap *ctx, uint16_t blocks)
30 : : {
31 : 222 : return blocks << ctx->block_size_shift;
32 : : }
33 : :
34 : 80 : static inline uint16_t bytes_to_blocks(struct ipmi_hiomap *ctx, uint32_t bytes)
35 : : {
36 : 80 : return bytes >> ctx->block_size_shift;
37 : : }
38 : :
39 : 80 : static inline uint16_t bytes_to_blocks_align_up(struct ipmi_hiomap *ctx,
40 : : uint32_t pos, uint32_t len)
41 : : {
42 : 80 : uint32_t block_size = 1 << ctx->block_size_shift;
43 : 80 : uint32_t delta = pos & (block_size - 1);
44 : 80 : uint32_t aligned = ALIGN_UP((len + delta), block_size);
45 : 80 : uint32_t blocks = aligned >> ctx->block_size_shift;
46 : : /* Our protocol can handle block count < sizeof(u16) */
47 : 80 : uint32_t mask = ((1 << 16) - 1);
48 : :
49 : 80 : assert(!(blocks & ~mask));
50 : :
51 : 80 : return blocks & mask;
52 : : }
53 : :
54 : : /* Call under ctx->lock */
55 : 282 : static int hiomap_protocol_ready(struct ipmi_hiomap *ctx)
56 : : {
57 : 282 : if (!(ctx->bmc_state & HIOMAP_E_DAEMON_READY))
58 : 2 : return FLASH_ERR_DEVICE_GONE;
59 : 280 : if (ctx->bmc_state & HIOMAP_E_FLASH_LOST)
60 : 6 : return FLASH_ERR_AGAIN;
61 : :
62 : 274 : return 0;
63 : : }
64 : :
65 : 282 : static int hiomap_queue_msg_sync(struct ipmi_hiomap *ctx, struct ipmi_msg *msg)
66 : : {
67 : : int rc;
68 : :
69 : : /*
70 : : * There's an unavoidable TOCTOU race here with the BMC sending an
71 : : * event saying it's no-longer available right after we test but before
72 : : * we call into the IPMI stack to send the message.
73 : : * hiomap_queue_msg_sync() exists to capture the race in a single
74 : : * location.
75 : : */
76 : 282 : lock(&ctx->lock);
77 : 282 : rc = hiomap_protocol_ready(ctx);
78 : 282 : unlock(&ctx->lock);
79 : 282 : if (rc) {
80 : 8 : ipmi_free_msg(msg);
81 : 8 : return rc;
82 : : }
83 : :
84 : 274 : ipmi_queue_msg_sync(msg);
85 : :
86 : 274 : return 0;
87 : : }
88 : :
89 : : /* Call under ctx->lock */
90 : 66 : static int hiomap_window_valid(struct ipmi_hiomap *ctx, uint64_t pos,
91 : : uint64_t len)
92 : : {
93 : 66 : if (ctx->bmc_state & HIOMAP_E_FLASH_LOST)
94 : 5 : return FLASH_ERR_AGAIN;
95 : 61 : if (ctx->bmc_state & HIOMAP_E_PROTOCOL_RESET)
96 : 0 : return FLASH_ERR_AGAIN;
97 : 61 : if (ctx->bmc_state & HIOMAP_E_WINDOW_RESET)
98 : 0 : return FLASH_ERR_AGAIN;
99 : 61 : if (ctx->window_state == closed_window)
100 : 41 : return FLASH_ERR_PARM_ERROR;
101 : 20 : if (pos < ctx->current.cur_pos)
102 : 0 : return FLASH_ERR_PARM_ERROR;
103 : 20 : if ((pos + len) > (ctx->current.cur_pos + ctx->current.size))
104 : 5 : return FLASH_ERR_PARM_ERROR;
105 : :
106 : 15 : return 0;
107 : : }
108 : :
109 : 320 : static void ipmi_hiomap_cmd_cb(struct ipmi_msg *msg)
110 : : {
111 : 320 : struct ipmi_hiomap_result *res = msg->user_data;
112 : 320 : struct ipmi_hiomap *ctx = res->ctx;
113 : :
114 : 320 : res->cc = msg->cc;
115 : 320 : if (msg->cc != IPMI_CC_NO_ERROR) {
116 : 12 : ipmi_free_msg(msg);
117 : 12 : return;
118 : : }
119 : :
120 : : /* We at least need the command and sequence */
121 : 308 : if (msg->resp_size < 2) {
122 : 4 : prerror("Illegal response size: %u\n", msg->resp_size);
123 : 4 : res->cc = IPMI_ERR_UNSPECIFIED;
124 : 4 : ipmi_free_msg(msg);
125 : 4 : return;
126 : : }
127 : :
128 : 304 : if (msg->data[1] != ctx->seq) {
129 : 1 : prerror("Unmatched sequence number: wanted %u got %u\n",
130 : : ctx->seq, msg->data[1]);
131 : 1 : res->cc = IPMI_ERR_UNSPECIFIED;
132 : 1 : ipmi_free_msg(msg);
133 : 1 : return;
134 : : }
135 : :
136 : 303 : switch (msg->data[0]) {
137 : 56 : case HIOMAP_C_GET_INFO:
138 : : {
139 : : struct hiomap_v2_info *parms;
140 : :
141 : 56 : if (msg->resp_size != 6) {
142 : 2 : prerror("%u: Unexpected response size: %u\n", msg->data[0],
143 : : msg->resp_size);
144 : 2 : res->cc = IPMI_ERR_UNSPECIFIED;
145 : 2 : break;
146 : : }
147 : :
148 : 54 : ctx->version = msg->data[2];
149 : 54 : if (ctx->version < 2) {
150 : 0 : prerror("Failed to negotiate protocol v2 or higher: %d\n",
151 : : ctx->version);
152 : 0 : res->cc = IPMI_ERR_UNSPECIFIED;
153 : 0 : break;
154 : : }
155 : :
156 : 54 : parms = (struct hiomap_v2_info *)&msg->data[3];
157 : 54 : ctx->block_size_shift = parms->block_size_shift;
158 : 54 : ctx->timeout = le16_to_cpu(parms->timeout);
159 : 54 : break;
160 : : }
161 : 53 : case HIOMAP_C_GET_FLASH_INFO:
162 : : {
163 : : struct hiomap_v2_flash_info *parms;
164 : :
165 : 53 : if (msg->resp_size != 6) {
166 : 2 : prerror("%u: Unexpected response size: %u\n", msg->data[0],
167 : : msg->resp_size);
168 : 2 : res->cc = IPMI_ERR_UNSPECIFIED;
169 : 2 : break;
170 : : }
171 : :
172 : 51 : parms = (struct hiomap_v2_flash_info *)&msg->data[2];
173 : 51 : ctx->total_size =
174 : 51 : blocks_to_bytes(ctx, le16_to_cpu(parms->total_size));
175 : 51 : ctx->erase_granule =
176 : 51 : blocks_to_bytes(ctx, le16_to_cpu(parms->erase_granule));
177 : 51 : break;
178 : : }
179 : 44 : case HIOMAP_C_CREATE_READ_WINDOW:
180 : : case HIOMAP_C_CREATE_WRITE_WINDOW:
181 : : {
182 : : struct hiomap_v2_create_window *parms;
183 : :
184 : 44 : if (msg->resp_size != 8) {
185 : 4 : prerror("%u: Unexpected response size: %u\n", msg->data[0],
186 : : msg->resp_size);
187 : 4 : res->cc = IPMI_ERR_UNSPECIFIED;
188 : 4 : break;
189 : : }
190 : :
191 : 40 : parms = (struct hiomap_v2_create_window *)&msg->data[2];
192 : :
193 : 40 : ctx->current.lpc_addr =
194 : 40 : blocks_to_bytes(ctx, le16_to_cpu(parms->lpc_addr));
195 : 40 : ctx->current.size =
196 : 40 : blocks_to_bytes(ctx, le16_to_cpu(parms->size));
197 : 40 : ctx->current.cur_pos =
198 : 40 : blocks_to_bytes(ctx, le16_to_cpu(parms->offset));
199 : :
200 : 40 : lock(&ctx->lock);
201 : 40 : if (msg->data[0] == HIOMAP_C_CREATE_READ_WINDOW)
202 : 12 : ctx->window_state = read_window;
203 : : else
204 : 28 : ctx->window_state = write_window;
205 : 40 : unlock(&ctx->lock);
206 : :
207 : 40 : break;
208 : : }
209 : 150 : case HIOMAP_C_MARK_DIRTY:
210 : : case HIOMAP_C_FLUSH:
211 : : case HIOMAP_C_ACK:
212 : : case HIOMAP_C_ERASE:
213 : : case HIOMAP_C_RESET:
214 : 150 : if (msg->resp_size != 2) {
215 : 4 : prerror("%u: Unexpected response size: %u\n", msg->data[0],
216 : : msg->resp_size);
217 : 4 : res->cc = IPMI_ERR_UNSPECIFIED;
218 : 4 : break;
219 : : }
220 : 146 : break;
221 : 0 : default:
222 : 0 : prlog(PR_WARNING, "Unimplemented command handler: %u\n",
223 : : msg->data[0]);
224 : 0 : break;
225 : : };
226 : 303 : ipmi_free_msg(msg);
227 : : }
228 : :
229 : 57 : static void hiomap_init(struct ipmi_hiomap *ctx)
230 : : {
231 : : /*
232 : : * Speculatively mark the daemon as available so we attempt to perform
233 : : * the handshake without immediately bailing out.
234 : : */
235 : 57 : lock(&ctx->lock);
236 : 57 : ctx->bmc_state = HIOMAP_E_DAEMON_READY;
237 : 57 : unlock(&ctx->lock);
238 : 57 : }
239 : :
240 : 58 : static int hiomap_get_info(struct ipmi_hiomap *ctx)
241 : : {
242 : 58 : RESULT_INIT(res, ctx);
243 : : unsigned char req[3];
244 : : struct ipmi_msg *msg;
245 : : int rc;
246 : :
247 : : /* Negotiate protocol version 2 */
248 : 58 : req[0] = HIOMAP_C_GET_INFO;
249 : 58 : req[1] = ++ctx->seq;
250 : 58 : req[2] = HIOMAP_V2;
251 : :
252 : 58 : msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
253 : 58 : bmc_platform->sw->ipmi_oem_hiomap_cmd,
254 : : ipmi_hiomap_cmd_cb, &res, req, sizeof(req), 6);
255 : :
256 : 58 : rc = hiomap_queue_msg_sync(ctx, msg);
257 : 58 : if (rc)
258 : 0 : return rc;
259 : :
260 : 58 : if (res.cc != IPMI_CC_NO_ERROR) {
261 : 4 : prerror("%s failed: %d\n", __func__, res.cc);
262 : 4 : return FLASH_ERR_PARM_ERROR; /* XXX: Find something better? */
263 : : }
264 : :
265 : 54 : return 0;
266 : : }
267 : :
268 : 55 : static int hiomap_get_flash_info(struct ipmi_hiomap *ctx)
269 : : {
270 : 55 : RESULT_INIT(res, ctx);
271 : : unsigned char req[2];
272 : : struct ipmi_msg *msg;
273 : : int rc;
274 : :
275 : 55 : req[0] = HIOMAP_C_GET_FLASH_INFO;
276 : 55 : req[1] = ++ctx->seq;
277 : 55 : msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
278 : 55 : bmc_platform->sw->ipmi_oem_hiomap_cmd,
279 : : ipmi_hiomap_cmd_cb, &res, req, sizeof(req), 2 + 2 + 2);
280 : :
281 : 55 : rc = hiomap_queue_msg_sync(ctx, msg);
282 : 55 : if (rc)
283 : 0 : return rc;
284 : :
285 : 55 : if (res.cc != IPMI_CC_NO_ERROR) {
286 : 4 : prerror("%s failed: %d\n", __func__, res.cc);
287 : 4 : return FLASH_ERR_PARM_ERROR; /* XXX: Find something better? */
288 : : }
289 : :
290 : 51 : return 0;
291 : : }
292 : :
293 : 53 : static int hiomap_window_move(struct ipmi_hiomap *ctx, uint8_t command,
294 : : uint64_t pos, uint64_t len, uint64_t *size)
295 : : {
296 : : enum lpc_window_state want_state;
297 : : struct hiomap_v2_range *range;
298 : 53 : RESULT_INIT(res, ctx);
299 : : unsigned char req[6];
300 : : struct ipmi_msg *msg;
301 : : bool valid_state;
302 : : bool is_read;
303 : : int rc;
304 : :
305 : 53 : is_read = (command == HIOMAP_C_CREATE_READ_WINDOW);
306 : 53 : want_state = is_read ? read_window : write_window;
307 : :
308 : 53 : lock(&ctx->lock);
309 : :
310 : 53 : valid_state = want_state == ctx->window_state;
311 : 53 : rc = hiomap_window_valid(ctx, pos, len);
312 : 53 : if (valid_state && !rc) {
313 : 3 : unlock(&ctx->lock);
314 : 3 : *size = len;
315 : 3 : return 0;
316 : : }
317 : :
318 : 50 : ctx->window_state = closed_window;
319 : :
320 : 50 : unlock(&ctx->lock);
321 : :
322 : 50 : req[0] = command;
323 : 50 : req[1] = ++ctx->seq;
324 : :
325 : 50 : range = (struct hiomap_v2_range *)&req[2];
326 : 50 : range->offset = cpu_to_le16(bytes_to_blocks(ctx, pos));
327 : 50 : range->size = cpu_to_le16(bytes_to_blocks_align_up(ctx, pos, len));
328 : :
329 : 50 : msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
330 : 50 : bmc_platform->sw->ipmi_oem_hiomap_cmd,
331 : : ipmi_hiomap_cmd_cb, &res, req, sizeof(req),
332 : : 2 + 2 + 2 + 2);
333 : :
334 : 50 : rc = hiomap_queue_msg_sync(ctx, msg);
335 : 50 : if (rc)
336 : 4 : return rc;
337 : :
338 : 46 : if (res.cc != IPMI_CC_NO_ERROR) {
339 : 6 : prlog(PR_INFO, "%s failed: %d\n", __func__, res.cc);
340 : 6 : return FLASH_ERR_PARM_ERROR; /* XXX: Find something better? */
341 : : }
342 : :
343 : 40 : lock(&ctx->lock);
344 : 40 : *size = len;
345 : : /* Is length past the end of the window? */
346 : 40 : if ((pos + len) > (ctx->current.cur_pos + ctx->current.size))
347 : : /* Adjust size to meet current window */
348 : 5 : *size = (ctx->current.cur_pos + ctx->current.size) - pos;
349 : :
350 : 40 : if (len != 0 && *size == 0) {
351 : 0 : unlock(&ctx->lock);
352 : 0 : prerror("Invalid window properties: len: %"PRIu64", size: %"PRIu64"\n",
353 : : len, *size);
354 : 0 : return FLASH_ERR_PARM_ERROR;
355 : : }
356 : :
357 : 40 : prlog(PR_DEBUG, "Opened %s window from 0x%x for %u bytes at 0x%x\n",
358 : : (command == HIOMAP_C_CREATE_READ_WINDOW) ? "read" : "write",
359 : : ctx->current.cur_pos, ctx->current.size, ctx->current.lpc_addr);
360 : :
361 : 40 : unlock(&ctx->lock);
362 : :
363 : 40 : return 0;
364 : : }
365 : :
366 : 15 : static int hiomap_mark_dirty(struct ipmi_hiomap *ctx, uint64_t offset,
367 : : uint64_t size)
368 : : {
369 : : struct hiomap_v2_range *range;
370 : : enum lpc_window_state state;
371 : 15 : RESULT_INIT(res, ctx);
372 : : unsigned char req[6];
373 : : struct ipmi_msg *msg;
374 : : uint32_t pos;
375 : : int rc;
376 : :
377 : 15 : lock(&ctx->lock);
378 : 15 : state = ctx->window_state;
379 : 15 : unlock(&ctx->lock);
380 : :
381 : 15 : if (state != write_window)
382 : 0 : return FLASH_ERR_PARM_ERROR;
383 : :
384 : 15 : req[0] = HIOMAP_C_MARK_DIRTY;
385 : 15 : req[1] = ++ctx->seq;
386 : :
387 : 15 : pos = offset - ctx->current.cur_pos;
388 : 15 : range = (struct hiomap_v2_range *)&req[2];
389 : 15 : range->offset = cpu_to_le16(bytes_to_blocks(ctx, pos));
390 : 15 : range->size = cpu_to_le16(bytes_to_blocks_align_up(ctx, pos, size));
391 : :
392 : 15 : msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
393 : 15 : bmc_platform->sw->ipmi_oem_hiomap_cmd,
394 : : ipmi_hiomap_cmd_cb, &res, req, sizeof(req), 2);
395 : :
396 : 15 : rc = hiomap_queue_msg_sync(ctx, msg);
397 : 15 : if (rc)
398 : 1 : return rc;
399 : :
400 : 14 : if (res.cc != IPMI_CC_NO_ERROR) {
401 : 3 : prerror("%s failed: %d\n", __func__, res.cc);
402 : 3 : return FLASH_ERR_PARM_ERROR;
403 : : }
404 : :
405 : 11 : prlog(PR_DEBUG, "Marked flash dirty at 0x%" PRIx64 " for %" PRIu64 "\n",
406 : : offset, size);
407 : :
408 : 11 : return 0;
409 : : }
410 : :
411 : 22 : static int hiomap_flush(struct ipmi_hiomap *ctx)
412 : : {
413 : : enum lpc_window_state state;
414 : 22 : RESULT_INIT(res, ctx);
415 : : unsigned char req[2];
416 : : struct ipmi_msg *msg;
417 : : int rc;
418 : :
419 : 22 : lock(&ctx->lock);
420 : 22 : state = ctx->window_state;
421 : 22 : unlock(&ctx->lock);
422 : :
423 : 22 : if (state != write_window)
424 : 0 : return FLASH_ERR_PARM_ERROR;
425 : :
426 : 22 : req[0] = HIOMAP_C_FLUSH;
427 : 22 : req[1] = ++ctx->seq;
428 : :
429 : 22 : msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
430 : 22 : bmc_platform->sw->ipmi_oem_hiomap_cmd,
431 : : ipmi_hiomap_cmd_cb, &res, req, sizeof(req), 2);
432 : :
433 : 22 : rc = hiomap_queue_msg_sync(ctx, msg);
434 : 22 : if (rc)
435 : 0 : return rc;
436 : :
437 : 22 : if (res.cc != IPMI_CC_NO_ERROR) {
438 : 3 : prerror("%s failed: %d\n", __func__, res.cc);
439 : 3 : return FLASH_ERR_PARM_ERROR;
440 : : }
441 : :
442 : 19 : prlog(PR_DEBUG, "Flushed writes\n");
443 : :
444 : 19 : return 0;
445 : : }
446 : :
447 : 67 : static int hiomap_ack(struct ipmi_hiomap *ctx, uint8_t ack)
448 : : {
449 : 67 : RESULT_INIT(res, ctx);
450 : : unsigned char req[3];
451 : : struct ipmi_msg *msg;
452 : : int rc;
453 : :
454 : 67 : req[0] = HIOMAP_C_ACK;
455 : 67 : req[1] = ++ctx->seq;
456 : 67 : req[2] = ack;
457 : :
458 : 67 : msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
459 : 67 : bmc_platform->sw->ipmi_oem_hiomap_cmd,
460 : : ipmi_hiomap_cmd_cb, &res, req, sizeof(req), 2);
461 : :
462 : 67 : rc = hiomap_queue_msg_sync(ctx, msg);
463 : 67 : if (rc)
464 : 2 : return rc;
465 : :
466 : 65 : if (res.cc != IPMI_CC_NO_ERROR) {
467 : 6 : prlog(PR_DEBUG, "%s failed: %d\n", __func__, res.cc);
468 : 6 : return FLASH_ERR_PARM_ERROR;
469 : : }
470 : :
471 : 59 : prlog(PR_DEBUG, "Acked events: 0x%x\n", ack);
472 : :
473 : 59 : return 0;
474 : : }
475 : :
476 : 15 : static int hiomap_erase(struct ipmi_hiomap *ctx, uint64_t offset,
477 : : uint64_t size)
478 : : {
479 : : struct hiomap_v2_range *range;
480 : : enum lpc_window_state state;
481 : 15 : RESULT_INIT(res, ctx);
482 : : unsigned char req[6];
483 : : struct ipmi_msg *msg;
484 : : uint32_t pos;
485 : : int rc;
486 : :
487 : 15 : lock(&ctx->lock);
488 : 15 : state = ctx->window_state;
489 : 15 : unlock(&ctx->lock);
490 : :
491 : 15 : if (state != write_window)
492 : 0 : return FLASH_ERR_PARM_ERROR;
493 : :
494 : 15 : req[0] = HIOMAP_C_ERASE;
495 : 15 : req[1] = ++ctx->seq;
496 : :
497 : 15 : pos = offset - ctx->current.cur_pos;
498 : 15 : range = (struct hiomap_v2_range *)&req[2];
499 : 15 : range->offset = cpu_to_le16(bytes_to_blocks(ctx, pos));
500 : 15 : range->size = cpu_to_le16(bytes_to_blocks_align_up(ctx, pos, size));
501 : :
502 : 15 : msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
503 : 15 : bmc_platform->sw->ipmi_oem_hiomap_cmd,
504 : : ipmi_hiomap_cmd_cb, &res, req, sizeof(req), 2);
505 : 15 : rc = hiomap_queue_msg_sync(ctx, msg);
506 : 15 : if (rc)
507 : 1 : return rc;
508 : :
509 : 14 : if (res.cc != IPMI_CC_NO_ERROR) {
510 : 3 : prerror("%s failed: %d\n", __func__, res.cc);
511 : 3 : return FLASH_ERR_PARM_ERROR;
512 : : }
513 : :
514 : 11 : prlog(PR_DEBUG, "Erased flash at 0x%" PRIx64 " for %" PRIu64 "\n",
515 : : offset, size);
516 : :
517 : 11 : return 0;
518 : : }
519 : :
520 : 46 : static bool hiomap_reset(struct ipmi_hiomap *ctx)
521 : : {
522 : 46 : RESULT_INIT(res, ctx);
523 : : unsigned char req[2];
524 : : struct ipmi_msg *msg;
525 : :
526 : 46 : prlog(PR_NOTICE, "Reset\n");
527 : :
528 : 46 : req[0] = HIOMAP_C_RESET;
529 : 46 : req[1] = ++ctx->seq;
530 : 46 : msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
531 : 46 : bmc_platform->sw->ipmi_oem_hiomap_cmd,
532 : : ipmi_hiomap_cmd_cb, &res, req, sizeof(req), 2);
533 : 46 : ipmi_queue_msg_sync(msg);
534 : :
535 : 46 : if (res.cc != IPMI_CC_NO_ERROR) {
536 : 0 : prlog(PR_ERR, "%s failed: %d\n", __func__, res.cc);
537 : 0 : return false;
538 : : }
539 : :
540 : 46 : return true;
541 : : }
542 : :
543 : 24 : static void hiomap_event(uint8_t events, void *context)
544 : : {
545 : 24 : struct ipmi_hiomap *ctx = context;
546 : :
547 : 24 : prlog(PR_DEBUG, "Received events: 0x%x\n", events);
548 : :
549 : 24 : lock(&ctx->lock);
550 : 24 : ctx->bmc_state = events | (ctx->bmc_state & HIOMAP_E_ACK_MASK);
551 : 24 : unlock(&ctx->lock);
552 : 24 : }
553 : :
554 : 13 : static int lpc_window_read(struct ipmi_hiomap *ctx, uint32_t pos,
555 : : void *buf, uint32_t len)
556 : : {
557 : 13 : uint32_t off = ctx->current.lpc_addr + (pos - ctx->current.cur_pos);
558 : :
559 : 13 : if ((ctx->current.lpc_addr + ctx->current.size) < (off + len))
560 : 0 : return FLASH_ERR_PARM_ERROR;
561 : :
562 : 13 : return lpc_fw_read(off, buf, len);
563 : : }
564 : :
565 : 15 : static int lpc_window_write(struct ipmi_hiomap *ctx, uint32_t pos,
566 : : const void *buf, uint32_t len)
567 : : {
568 : 15 : uint32_t off = ctx->current.lpc_addr + (pos - ctx->current.cur_pos);
569 : : enum lpc_window_state state;
570 : :
571 : 15 : lock(&ctx->lock);
572 : 15 : state = ctx->window_state;
573 : 15 : unlock(&ctx->lock);
574 : :
575 : 15 : if (state != write_window)
576 : 0 : return FLASH_ERR_PARM_ERROR;
577 : :
578 : 15 : if ((ctx->current.lpc_addr + ctx->current.size) < (off + len))
579 : 0 : return FLASH_ERR_PARM_ERROR;
580 : :
581 : 15 : return lpc_fw_write(off, buf, len);
582 : : }
583 : :
584 : : /* Best-effort asynchronous event handling by blocklevel callbacks */
585 : 54 : static int ipmi_hiomap_handle_events(struct ipmi_hiomap *ctx)
586 : : {
587 : : uint8_t status;
588 : : int rc;
589 : :
590 : 54 : lock(&ctx->lock);
591 : :
592 : 54 : status = ctx->bmc_state;
593 : :
594 : : /*
595 : : * Immediately clear the ackable events to make sure we don't race to
596 : : * clear them after dropping the lock, as we may lose protocol or
597 : : * window state if a race materialises. In the event of a failure where
598 : : * we haven't completed the recovery, the state we mask out below gets
599 : : * OR'ed back in to avoid losing it.
600 : : */
601 : 54 : ctx->bmc_state &= ~HIOMAP_E_ACK_MASK;
602 : :
603 : : /*
604 : : * We won't be attempting to restore window state -
605 : : * ipmi_hiomap_handle_events() is followed by hiomap_window_move() in
606 : : * all cases. Attempting restoration after HIOMAP_E_PROTOCOL_RESET or
607 : : * HIOMAP_E_WINDOW_RESET can be wasteful if we immediately shift the
608 : : * window elsewhere, and if it does not need to be shifted with respect
609 : : * to the subsequent request then hiomap_window_move() will handle
610 : : * re-opening it from the closed state.
611 : : *
612 : : * Therefore it is enough to mark the window as closed to consider it
613 : : * recovered.
614 : : */
615 : 54 : if (status & (HIOMAP_E_PROTOCOL_RESET | HIOMAP_E_WINDOW_RESET))
616 : 10 : ctx->window_state = closed_window;
617 : :
618 : 54 : unlock(&ctx->lock);
619 : :
620 : : /*
621 : : * If there's anything to acknowledge, do so in the one request to
622 : : * minimise overhead. By sending the ACK prior to performing the
623 : : * protocol recovery we ensure that even with coalesced resets we still
624 : : * end up in the recovered state and not unknowingly stuck in a reset
625 : : * state. We may receive reset events after the ACK but prior to the
626 : : * recovery procedures being run, but this just means that we will
627 : : * needlessly perform recovery on the following invocation of
628 : : * ipmi_hiomap_handle_events(). If the reset event is a
629 : : * HIOMAP_E_WINDOW_RESET it is enough that the window is already marked
630 : : * as closed above - future accesses will force it to be re-opened and
631 : : * the BMC's cache must be valid if opening the window is successful.
632 : : */
633 : 54 : if (status & HIOMAP_E_ACK_MASK) {
634 : : /* ACK is unversioned, can send it if the daemon is ready */
635 : 10 : rc = hiomap_ack(ctx, status & HIOMAP_E_ACK_MASK);
636 : 10 : if (rc) {
637 : 3 : prlog(PR_DEBUG, "Failed to ack events: 0x%x\n",
638 : : status & HIOMAP_E_ACK_MASK);
639 : 3 : goto restore;
640 : : }
641 : : }
642 : :
643 : 51 : if (status & HIOMAP_E_PROTOCOL_RESET) {
644 : 6 : prlog(PR_INFO, "Protocol was reset\n");
645 : :
646 : 6 : rc = hiomap_get_info(ctx);
647 : 6 : if (rc) {
648 : 1 : prerror("Failure to renegotiate after protocol reset\n");
649 : 1 : goto restore;
650 : : }
651 : :
652 : 5 : rc = hiomap_get_flash_info(ctx);
653 : 5 : if (rc) {
654 : 1 : prerror("Failure to fetch flash info after protocol reset\n");
655 : 1 : goto restore;
656 : : }
657 : :
658 : 4 : prlog(PR_INFO, "Restored state after protocol reset\n");
659 : : }
660 : :
661 : : /*
662 : : * As there's no change to the protocol on HIOMAP_E_WINDOW_RESET we
663 : : * simply need to open a window to recover, which as mentioned above is
664 : : * handled by hiomap_window_move() after our cleanup here.
665 : : */
666 : :
667 : 49 : return 0;
668 : :
669 : 5 : restore:
670 : : /*
671 : : * Conservatively restore the events to the un-acked state to avoid
672 : : * losing events due to races. It might cause us to restore state more
673 : : * than necessary, but never less than necessary.
674 : : */
675 : 5 : lock(&ctx->lock);
676 : 5 : ctx->bmc_state |= (status & HIOMAP_E_ACK_MASK);
677 : 5 : unlock(&ctx->lock);
678 : :
679 : 5 : return rc;
680 : : }
681 : :
682 : 18 : static int ipmi_hiomap_read(struct blocklevel_device *bl, uint64_t pos,
683 : : void *buf, uint64_t len)
684 : : {
685 : : struct ipmi_hiomap *ctx;
686 : : uint64_t size;
687 : 18 : int rc = 0;
688 : :
689 : : /* LPC is only 32bit */
690 : 18 : if (pos > UINT_MAX || len > UINT_MAX)
691 : 0 : return FLASH_ERR_PARM_ERROR;
692 : :
693 : 18 : ctx = container_of(bl, struct ipmi_hiomap, bl);
694 : :
695 : 18 : rc = ipmi_hiomap_handle_events(ctx);
696 : 18 : if (rc)
697 : 2 : return rc;
698 : :
699 : 16 : prlog(PR_TRACE, "Flash read at %#" PRIx64 " for %#" PRIx64 "\n", pos,
700 : : len);
701 : 28 : while (len > 0) {
702 : : /* Move window and get a new size to read */
703 : 18 : rc = hiomap_window_move(ctx, HIOMAP_C_CREATE_READ_WINDOW, pos,
704 : : len, &size);
705 : 18 : if (rc)
706 : 5 : return rc;
707 : :
708 : : /* Perform the read for this window */
709 : 13 : rc = lpc_window_read(ctx, pos, buf, size);
710 : 13 : if (rc)
711 : 0 : return rc;
712 : :
713 : : /* Check we can trust what we read */
714 : 13 : lock(&ctx->lock);
715 : 13 : rc = hiomap_window_valid(ctx, pos, size);
716 : 13 : unlock(&ctx->lock);
717 : 13 : if (rc)
718 : 1 : return rc;
719 : :
720 : 12 : len -= size;
721 : 12 : pos += size;
722 : 12 : buf += size;
723 : : }
724 : 10 : return rc;
725 : :
726 : : }
727 : :
728 : 17 : static int ipmi_hiomap_write(struct blocklevel_device *bl, uint64_t pos,
729 : : const void *buf, uint64_t len)
730 : : {
731 : : struct ipmi_hiomap *ctx;
732 : : uint64_t size;
733 : 17 : int rc = 0;
734 : :
735 : : /* LPC is only 32bit */
736 : 17 : if (pos > UINT_MAX || len > UINT_MAX)
737 : 0 : return FLASH_ERR_PARM_ERROR;
738 : :
739 : 17 : ctx = container_of(bl, struct ipmi_hiomap, bl);
740 : :
741 : 17 : rc = ipmi_hiomap_handle_events(ctx);
742 : 17 : if (rc)
743 : 0 : return rc;
744 : :
745 : 17 : prlog(PR_TRACE, "Flash write at %#" PRIx64 " for %#" PRIx64 "\n", pos,
746 : : len);
747 : 25 : while (len > 0) {
748 : : /* Move window and get a new size to read */
749 : 19 : rc = hiomap_window_move(ctx, HIOMAP_C_CREATE_WRITE_WINDOW, pos,
750 : : len, &size);
751 : 19 : if (rc)
752 : 4 : return rc;
753 : :
754 : : /* Perform the write for this window */
755 : 15 : rc = lpc_window_write(ctx, pos, buf, size);
756 : 15 : if (rc)
757 : 0 : return rc;
758 : :
759 : : /*
760 : : * Unlike ipmi_hiomap_read() we don't explicitly test if the
761 : : * window is still valid after completing the LPC accesses as
762 : : * the following hiomap_mark_dirty() will implicitly check for
763 : : * us. In the case of a read operation there's no requirement
764 : : * that a command that validates window state follows, so the
765 : : * read implementation explicitly performs a check.
766 : : */
767 : :
768 : 15 : rc = hiomap_mark_dirty(ctx, pos, size);
769 : 15 : if (rc)
770 : 4 : return rc;
771 : :
772 : : /*
773 : : * The BMC *should* flush if the window is implicitly closed,
774 : : * but do an explicit flush here to be sure.
775 : : *
776 : : * XXX: Removing this could improve performance
777 : : */
778 : 11 : rc = hiomap_flush(ctx);
779 : 11 : if (rc)
780 : 3 : return rc;
781 : :
782 : 8 : len -= size;
783 : 8 : pos += size;
784 : 8 : buf += size;
785 : : }
786 : 6 : return rc;
787 : : }
788 : :
789 : 18 : static int ipmi_hiomap_erase(struct blocklevel_device *bl, uint64_t pos,
790 : : uint64_t len)
791 : : {
792 : : struct ipmi_hiomap *ctx;
793 : : int rc;
794 : :
795 : : /* LPC is only 32bit */
796 : 18 : if (pos > UINT_MAX || len > UINT_MAX)
797 : 0 : return FLASH_ERR_PARM_ERROR;
798 : :
799 : 18 : ctx = container_of(bl, struct ipmi_hiomap, bl);
800 : :
801 : 18 : rc = ipmi_hiomap_handle_events(ctx);
802 : 18 : if (rc)
803 : 3 : return rc;
804 : :
805 : 15 : prlog(PR_TRACE, "Flash erase at 0x%08x for 0x%08x\n", (u32) pos,
806 : : (u32) len);
807 : 26 : while (len > 0) {
808 : : uint64_t size;
809 : :
810 : : /* Move window and get a new size to erase */
811 : 16 : rc = hiomap_window_move(ctx, HIOMAP_C_CREATE_WRITE_WINDOW, pos,
812 : : len, &size);
813 : 16 : if (rc)
814 : 5 : return rc;
815 : :
816 : 15 : rc = hiomap_erase(ctx, pos, size);
817 : 15 : if (rc)
818 : 4 : return rc;
819 : :
820 : : /*
821 : : * Flush directly, don't mark that region dirty otherwise it
822 : : * isn't clear if a write happened there or not
823 : : */
824 : 11 : rc = hiomap_flush(ctx);
825 : 11 : if (rc)
826 : 0 : return rc;
827 : :
828 : 11 : len -= size;
829 : 11 : pos += size;
830 : : }
831 : :
832 : 10 : return 0;
833 : : }
834 : :
835 : 1 : static int ipmi_hiomap_get_flash_info(struct blocklevel_device *bl,
836 : : const char **name, uint64_t *total_size,
837 : : uint32_t *erase_granule)
838 : : {
839 : : struct ipmi_hiomap *ctx;
840 : : int rc;
841 : :
842 : 1 : ctx = container_of(bl, struct ipmi_hiomap, bl);
843 : :
844 : 1 : rc = ipmi_hiomap_handle_events(ctx);
845 : 1 : if (rc)
846 : 0 : return rc;
847 : :
848 : 1 : rc = hiomap_get_flash_info(ctx);
849 : 1 : if (rc)
850 : 0 : return rc;
851 : :
852 : 1 : ctx->bl.erase_mask = ctx->erase_granule - 1;
853 : :
854 : 1 : if (name)
855 : 1 : *name = NULL;
856 : 1 : if (total_size)
857 : 1 : *total_size = ctx->total_size;
858 : 1 : if (erase_granule)
859 : 1 : *erase_granule = ctx->erase_granule;
860 : :
861 : 1 : return 0;
862 : : }
863 : :
864 : 57 : int ipmi_hiomap_init(struct blocklevel_device **bl)
865 : : {
866 : : struct ipmi_hiomap *ctx;
867 : : int rc;
868 : :
869 : 57 : if (!bmc_platform->sw->ipmi_oem_hiomap_cmd)
870 : : /* FIXME: Find a better error code */
871 : 0 : return FLASH_ERR_DEVICE_GONE;
872 : :
873 : 57 : if (!bl)
874 : 0 : return FLASH_ERR_PARM_ERROR;
875 : :
876 : 57 : *bl = NULL;
877 : :
878 : 57 : ctx = zalloc(sizeof(struct ipmi_hiomap));
879 : 57 : if (!ctx)
880 : 0 : return FLASH_ERR_MALLOC_FAILED;
881 : :
882 : 57 : init_lock(&ctx->lock);
883 : :
884 : 57 : ctx->bl.read = &ipmi_hiomap_read;
885 : 57 : ctx->bl.write = &ipmi_hiomap_write;
886 : 57 : ctx->bl.erase = &ipmi_hiomap_erase;
887 : 57 : ctx->bl.get_info = &ipmi_hiomap_get_flash_info;
888 : 57 : ctx->bl.exit = &ipmi_hiomap_exit;
889 : :
890 : 57 : hiomap_init(ctx);
891 : :
892 : : /* Ack all pending ack-able events to avoid spurious failures */
893 : 57 : rc = hiomap_ack(ctx, HIOMAP_E_ACK_MASK);
894 : 57 : if (rc) {
895 : 5 : prlog(PR_DEBUG, "Failed to ack events: 0x%x\n",
896 : : HIOMAP_E_ACK_MASK);
897 : 5 : goto err;
898 : : }
899 : :
900 : 52 : rc = ipmi_sel_register(CMD_OP_HIOMAP_EVENT, hiomap_event, ctx);
901 : 52 : if (rc < 0)
902 : 0 : goto err;
903 : :
904 : : /* Negotiate protocol behaviour */
905 : 52 : rc = hiomap_get_info(ctx);
906 : 52 : if (rc) {
907 : 3 : prerror("Failed to get hiomap parameters: %d\n", rc);
908 : 3 : goto err;
909 : : }
910 : :
911 : : /* Grab the flash parameters */
912 : 49 : rc = hiomap_get_flash_info(ctx);
913 : 49 : if (rc) {
914 : 3 : prerror("Failed to get flash parameters: %d\n", rc);
915 : 3 : goto err;
916 : : }
917 : :
918 : 46 : prlog(PR_NOTICE, "Negotiated hiomap protocol v%u\n", ctx->version);
919 : 46 : prlog(PR_NOTICE, "Block size is %uKiB\n",
920 : : 1 << (ctx->block_size_shift - 10));
921 : 46 : prlog(PR_NOTICE, "BMC suggested flash timeout of %us\n", ctx->timeout);
922 : 46 : prlog(PR_NOTICE, "Flash size is %uMiB\n", ctx->total_size >> 20);
923 : 46 : prlog(PR_NOTICE, "Erase granule size is %uKiB\n",
924 : : ctx->erase_granule >> 10);
925 : :
926 : 46 : ctx->bl.keep_alive = 0;
927 : :
928 : 46 : *bl = &(ctx->bl);
929 : :
930 : 46 : return 0;
931 : :
932 : 11 : err:
933 : 11 : free(ctx);
934 : :
935 : 11 : return rc;
936 : : }
937 : :
938 : 46 : bool ipmi_hiomap_exit(struct blocklevel_device *bl)
939 : : {
940 : 46 : bool status = true;
941 : :
942 : : struct ipmi_hiomap *ctx;
943 : 46 : if (bl) {
944 : 46 : ctx = container_of(bl, struct ipmi_hiomap, bl);
945 : 46 : status = hiomap_reset(ctx);
946 : 46 : free(ctx);
947 : : }
948 : :
949 : 46 : return status;
950 : : }
|