blob: 2a04d897a0f113ed8deee2b44e38a10ae82f1ec4 [file] [log] [blame]
Andrew Jeffery1e531af2018-08-07 13:32:57 +09301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2018 IBM Corp.
3#include "config.h"
4
5#include <errno.h>
6#include <stdint.h>
7
Andrew Jeffery5335f092018-08-09 14:56:08 +09308#include "common.h"
Andrew Jefferyc48d0702018-08-09 14:04:27 +09309#include "flash.h"
Andrew Jeffery26558db2018-08-10 00:22:38 +093010#include "mboxd.h"
Andrew Jeffery1e531af2018-08-07 13:32:57 +093011#include "lpc.h"
Andrew Jeffery1e531af2018-08-07 13:32:57 +093012#include "windows.h"
13
Andrew Jeffery26558db2018-08-10 00:22:38 +093014#define BLOCK_SIZE_SHIFT_V1 12 /* 4K */
15
Andrew Jeffery0453aa42018-08-21 08:25:46 +093016static inline uint8_t protocol_get_bmc_event_mask(struct mbox_context *context)
17{
18 if (context->version == API_VERSION_1) {
19 return BMC_EVENT_V1_MASK;
20 }
21
22 return BMC_EVENT_V2_MASK;
23}
24
Andrew Jeffery5335f092018-08-09 14:56:08 +093025/*
Andrew Jefferyfe0c9e82018-11-01 14:02:17 +103026 * protocol_events_put() - Push the full set/cleared state of BMC events on the
27 * provided transport
28 * @context: The mbox context pointer
29 * @ops: The operations struct for the transport of interest
30 *
31 * Return: 0 on success otherwise negative error code
32 */
33int protocol_events_put(struct mbox_context *context,
34 const struct transport_ops *ops)
35{
36 const uint8_t mask = protocol_get_bmc_event_mask(context);
37
38 return ops->put_events(context, mask);
39}
40
41/*
42 * protocol_events_set() - Update the set BMC events on the active transport
Andrew Jeffery5335f092018-08-09 14:56:08 +093043 * @context: The mbox context pointer
44 * @bmc_event: The bits to set
Andrew Jeffery5335f092018-08-09 14:56:08 +093045 *
46 * Return: 0 on success otherwise negative error code
47 */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093048int protocol_events_set(struct mbox_context *context, uint8_t bmc_event)
Andrew Jeffery5335f092018-08-09 14:56:08 +093049{
Andrew Jeffery0453aa42018-08-21 08:25:46 +093050 const uint8_t mask = protocol_get_bmc_event_mask(context);
Andrew Jeffery5335f092018-08-09 14:56:08 +093051
Andrew Jeffery0453aa42018-08-21 08:25:46 +093052 /*
53 * Store the raw value, as we may up- or down- grade the protocol
54 * version and subsequently need to flush the appropriate set. Instead
55 * we pass the masked value through to the transport
56 */
57 context->bmc_events |= bmc_event;
Andrew Jeffery5335f092018-08-09 14:56:08 +093058
Andrew Jefferyf62601b2018-11-01 13:44:25 +103059 return context->transport->set_events(context, bmc_event, mask);
Andrew Jeffery5335f092018-08-09 14:56:08 +093060}
61
62/*
Andrew Jefferyfe0c9e82018-11-01 14:02:17 +103063 * protocol_events_clear() - Update the cleared BMC events on the active
64 * transport
Andrew Jeffery5335f092018-08-09 14:56:08 +093065 * @context: The mbox context pointer
66 * @bmc_event: The bits to clear
Andrew Jeffery5335f092018-08-09 14:56:08 +093067 *
68 * Return: 0 on success otherwise negative error code
69 */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093070int protocol_events_clear(struct mbox_context *context, uint8_t bmc_event)
Andrew Jeffery5335f092018-08-09 14:56:08 +093071{
Andrew Jeffery0453aa42018-08-21 08:25:46 +093072 const uint8_t mask = protocol_get_bmc_event_mask(context);
73
74 context->bmc_events &= ~bmc_event;
75
Andrew Jefferyf62601b2018-11-01 13:44:25 +103076 return context->transport->clear_events(context, bmc_event, mask);
Andrew Jeffery5335f092018-08-09 14:56:08 +093077}
78
Andrew Jefferyab666a52018-08-07 14:28:09 +093079int protocol_v1_reset(struct mbox_context *context)
80{
81 /* Host requested it -> No BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093082 windows_reset_all(context);
Andrew Jefferyab666a52018-08-07 14:28:09 +093083 return lpc_reset(context);
84}
85
Andrew Jeffery1e531af2018-08-07 13:32:57 +093086int protocol_v1_get_info(struct mbox_context *context,
87 struct protocol_get_info *io)
88{
89 uint8_t old_version = context->version;
90 int rc;
91
92 /* Bootstrap protocol version. This may involve {up,down}grading */
93 rc = protocol_negotiate_version(context, io->req.api_version);
94 if (rc < 0)
95 return rc;
96
97 /* Do the {up,down}grade if necessary*/
98 if (rc != old_version) {
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093099 /* Doing version negotiation, don't alert host to reset */
100 windows_reset_all(context);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930101 return context->protocol->get_info(context, io);
102 }
103
104 /* Record the negotiated version for the response */
105 io->resp.api_version = rc;
106
107 /* Now do all required intialisation for v1 */
108 context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
109 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
110 1 << context->block_size_shift, context->block_size_shift);
111
112 /* Knowing blocksize we can allocate the window dirty_bytemap */
113 windows_alloc_dirty_bytemap(context);
114
115 io->resp.v1.read_window_size =
116 context->windows.default_size >> context->block_size_shift;
117 io->resp.v1.write_window_size =
118 context->windows.default_size >> context->block_size_shift;
119
120 return lpc_map_memory(context);
121}
122
Andrew Jeffery91a87452018-08-07 14:54:14 +0930123int protocol_v1_get_flash_info(struct mbox_context *context,
124 struct protocol_get_flash_info *io)
125{
126 io->resp.v1.flash_size = context->flash_size;
127 io->resp.v1.erase_size = context->mtd_info.erasesize;
128
129 return 0;
130}
131
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930132/*
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930133 * get_lpc_addr_shifted() - Get lpc address of the current window
134 * @context: The mbox context pointer
135 *
136 * Return: The lpc address to access that offset shifted by block size
137 */
138static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
139{
140 uint32_t lpc_addr, mem_offset;
141
142 /* Offset of the current window in the reserved memory region */
143 mem_offset = context->current->mem - context->mem;
144 /* Total LPC Address */
145 lpc_addr = context->lpc_base + mem_offset;
146
147 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
148
149 return lpc_addr >> context->block_size_shift;
150}
151
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930152int protocol_v1_create_window(struct mbox_context *context,
153 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930154{
155 int rc;
156 uint32_t offset = io->req.offset << context->block_size_shift;
157
158 /* Close the current window if there is one */
159 if (context->current) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930160 /* There is an implicit flush if it was a write window
161 *
162 * protocol_v2_create_window() calls
163 * protocol_v1_create_window(), so use indirect call to
164 * write_flush() to make sure we pick the right one.
165 */
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930166 if (context->current_is_write) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930167 rc = context->protocol->flush(context, NULL);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930168 if (rc < 0) {
169 MSG_ERR("Couldn't Flush Write Window\n");
170 return rc;
171 }
172 }
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930173 windows_close_current(context, FLAGS_NONE);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930174 }
175
176 /* Offset the host has requested */
177 MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
178 /* Check if we have an existing window */
179 context->current = windows_search(context, offset,
180 context->version == API_VERSION_1);
181
182 if (!context->current) { /* No existing window */
183 MSG_DBG("No existing window which maps that flash offset\n");
184 rc = windows_create_map(context, &context->current,
185 offset,
186 context->version == API_VERSION_1);
187 if (rc < 0) { /* Unable to map offset */
188 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930189 offset);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930190 return rc;
191 }
192 }
193
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930194 context->current_is_write = !io->req.ro;
195
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930196 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
197 context->current->mem, context->current->size,
198 context->current->flash_offset);
199
200 io->resp.lpc_address = get_lpc_addr_shifted(context);
201
202 return 0;
203}
204
Andrew Jefferya336e432018-08-07 16:00:40 +0930205int protocol_v1_mark_dirty(struct mbox_context *context,
206 struct protocol_mark_dirty *io)
207{
208 uint32_t offset = io->req.v1.offset;
209 uint32_t size = io->req.v1.size;
210 uint32_t off;
211
212 if (!(context->current && context->current_is_write)) {
213 MSG_ERR("Tried to call mark dirty without open write window\n");
214 return -EPERM;
215 }
216
217 /* For V1 offset given relative to flash - we want the window */
218 off = offset - ((context->current->flash_offset) >>
219 context->block_size_shift);
220 if (off > offset) { /* Underflow - before current window */
221 MSG_ERR("Tried to mark dirty before start of window\n");
222 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
223 offset << context->block_size_shift,
224 context->current->flash_offset);
225 return -EINVAL;
226 }
227 offset = off;
228 /*
229 * We only track dirty at the block level.
230 * For protocol V1 we can get away with just marking the whole
231 * block dirty.
232 */
233 size = align_up(size, 1 << context->block_size_shift);
234 size >>= context->block_size_shift;
235
236 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
237 offset << context->block_size_shift,
238 size << context->block_size_shift);
239
240 return window_set_bytemap(context, context->current, offset, size,
241 WINDOW_DIRTY);
242}
243
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930244static int generic_flush(struct mbox_context *context)
245{
246 int rc, i, offset, count;
247 uint8_t prev;
248
249 offset = 0;
250 count = 0;
251 prev = WINDOW_CLEAN;
252
253 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
254 context->current->mem, context->current->size,
255 context->current->flash_offset);
256
257 /*
258 * We look for streaks of the same type and keep a count, when the type
259 * (dirty/erased) changes we perform the required action on the backing
260 * store and update the current streak-type
261 */
262 for (i = 0; i < (context->current->size >> context->block_size_shift);
263 i++) {
264 uint8_t cur = context->current->dirty_bmap[i];
265 if (cur != WINDOW_CLEAN) {
266 if (cur == prev) { /* Same as previous block, incrmnt */
267 count++;
268 } else if (prev == WINDOW_CLEAN) { /* Start of run */
269 offset = i;
270 count++;
271 } else { /* Change in streak type */
272 rc = window_flush(context, offset, count,
273 prev);
274 if (rc < 0) {
275 return rc;
276 }
277 offset = i;
278 count = 1;
279 }
280 } else {
281 if (prev != WINDOW_CLEAN) { /* End of a streak */
282 rc = window_flush(context, offset, count,
283 prev);
284 if (rc < 0) {
285 return rc;
286 }
287 offset = 0;
288 count = 0;
289 }
290 }
291 prev = cur;
292 }
293
294 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
295 rc = window_flush(context, offset, count, prev);
296 if (rc < 0) {
297 return rc;
298 }
299 }
300
301 /* Clear the dirty bytemap since we have written back all changes */
302 return window_set_bytemap(context, context->current, 0,
303 context->current->size >>
304 context->block_size_shift,
305 WINDOW_CLEAN);
306}
307
308int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
309{
310 int rc;
311
312 if (!(context->current && context->current_is_write)) {
313 MSG_ERR("Tried to call flush without open write window\n");
314 return -EPERM;
315 }
316
317 /*
318 * For V1 the Flush command acts much the same as the dirty command
319 * except with a flush as well. Only do this on an actual flush
320 * command not when we call flush because we've implicitly closed a
321 * window because we might not have the required args in req.
322 */
Andrew Jeffery093eda52018-08-07 23:10:43 +0930323 if (io) {
324 struct protocol_mark_dirty *mdio = (void *)io;
325 rc = protocol_v1_mark_dirty(context, mdio);
326 if (rc < 0) {
327 return rc;
328 }
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930329 }
330
331 return generic_flush(context);
332}
333
Andrew Jeffery093eda52018-08-07 23:10:43 +0930334int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
335{
336 int rc;
337
338 /* Close the current window if there is one */
339 if (!context->current) {
340 return 0;
341 }
342
343 /* There is an implicit flush if it was a write window */
344 if (context->current_is_write) {
345 rc = protocol_v1_flush(context, NULL);
346 if (rc < 0) {
347 MSG_ERR("Couldn't Flush Write Window\n");
348 return rc;
349 }
350 }
351
352 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930353 windows_close_current(context, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930354
355 return 0;
356}
357
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930358int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
359{
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930360 return protocol_events_clear(context,
361 (io->req.flags & BMC_EVENT_ACK_MASK));
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930362}
363
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930364/*
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930365 * get_suggested_timeout() - get the suggested timeout value in seconds
366 * @context: The mbox context pointer
367 *
368 * Return: Suggested timeout in seconds
369 */
370static uint16_t get_suggested_timeout(struct mbox_context *context)
371{
372 struct window_context *window = windows_find_largest(context);
373 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
374 uint16_t ret;
375
376 ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
377
378 MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
379 ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
380 return ret;
381}
382
383int protocol_v2_get_info(struct mbox_context *context,
384 struct protocol_get_info *io)
385{
386 uint8_t old_version = context->version;
387 int rc;
388
389 /* Bootstrap protocol version. This may involve {up,down}grading */
390 rc = protocol_negotiate_version(context, io->req.api_version);
391 if (rc < 0)
392 return rc;
393
394 /* Do the {up,down}grade if necessary*/
395 if (rc != old_version) {
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930396 /* Doing version negotiation, don't alert host to reset */
397 windows_reset_all(context);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930398 return context->protocol->get_info(context, io);
399 }
400
401 /* Record the negotiated version for the response */
402 io->resp.api_version = rc;
403
404 /* Now do all required intialisation for v2 */
405 context->block_size_shift = log_2(context->mtd_info.erasesize);
406 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
407 1 << context->block_size_shift, context->block_size_shift);
408
409 /* Knowing blocksize we can allocate the window dirty_bytemap */
410 windows_alloc_dirty_bytemap(context);
411
412 io->resp.v2.block_size_shift = context->block_size_shift;
413 io->resp.v2.timeout = get_suggested_timeout(context);
414
415 return lpc_map_memory(context);
416}
417
Andrew Jeffery91a87452018-08-07 14:54:14 +0930418int protocol_v2_get_flash_info(struct mbox_context *context,
419 struct protocol_get_flash_info *io)
420{
421 io->resp.v2.flash_size =
422 context->flash_size >> context->block_size_shift;
423 io->resp.v2.erase_size =
424 context->mtd_info.erasesize >> context->block_size_shift;
425
426 return 0;
427}
428
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930429int protocol_v2_create_window(struct mbox_context *context,
430 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930431{
432 int rc;
433
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930434 rc = protocol_v1_create_window(context, io);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930435 if (rc < 0)
436 return rc;
437
438 io->resp.size = context->current->size >> context->block_size_shift;
439 io->resp.offset = context->current->flash_offset >>
440 context->block_size_shift;
441
442 return 0;
443}
444
Andrew Jefferya336e432018-08-07 16:00:40 +0930445int protocol_v2_mark_dirty(struct mbox_context *context,
446 struct protocol_mark_dirty *io)
447{
448 if (!(context->current && context->current_is_write)) {
449 MSG_ERR("Tried to call mark dirty without open write window\n");
450 return -EPERM;
451 }
452
453 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
454 io->req.v2.offset << context->block_size_shift,
455 io->req.v2.size << context->block_size_shift);
456
457 return window_set_bytemap(context, context->current, io->req.v2.offset,
458 io->req.v2.size, WINDOW_DIRTY);
459}
460
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930461int protocol_v2_erase(struct mbox_context *context,
462 struct protocol_erase *io)
463{
464 size_t start, len;
465 int rc;
466
467 if (!(context->current && context->current_is_write)) {
468 MSG_ERR("Tried to call erase without open write window\n");
469 return -EPERM;
470 }
471
472 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
473 io->req.offset << context->block_size_shift,
474 io->req.size << context->block_size_shift);
475
476 rc = window_set_bytemap(context, context->current, io->req.offset,
477 io->req.size, WINDOW_ERASED);
478 if (rc < 0) {
479 return rc;
480 }
481
482 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
483 start = io->req.offset << context->block_size_shift;
484 len = io->req.size << context->block_size_shift;
485 memset(context->current->mem + start, 0xFF, len);
486
487 return 0;
488}
489
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930490int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
491{
492 if (!(context->current && context->current_is_write)) {
493 MSG_ERR("Tried to call flush without open write window\n");
494 return -EPERM;
495 }
496
497 return generic_flush(context);
498}
499
Andrew Jeffery093eda52018-08-07 23:10:43 +0930500int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
501{
502 int rc;
503
504 /* Close the current window if there is one */
505 if (!context->current) {
506 return 0;
507 }
508
509 /* There is an implicit flush if it was a write window */
510 if (context->current_is_write) {
511 rc = protocol_v2_flush(context, NULL);
512 if (rc < 0) {
513 MSG_ERR("Couldn't Flush Write Window\n");
514 return rc;
515 }
516 }
517
518 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930519 windows_close_current(context, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930520
521 return 0;
522}
523
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930524int protocol_init(struct mbox_context *context)
525{
Andrew Jefferyc7d19472018-08-08 11:43:08 +0930526 protocol_negotiate_version(context, API_MAX_VERSION);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930527
528 return 0;
529}
530
531void protocol_free(struct mbox_context *context)
532{
533 return;
534}
Andrew Jefferyf69760d2019-03-14 16:54:13 +1030535
536/* Don't do any state manipulation, just perform the reset */
537int __protocol_reset(struct mbox_context *context)
538{
539 windows_reset_all(context);
540
541 return lpc_reset(context);
542}
543
544/* Prevent the host from performing actions whilst reset takes place */
545int protocol_reset(struct mbox_context *context)
546{
547 int rc;
548
549 rc = protocol_events_clear(context, BMC_EVENT_DAEMON_READY);
550 if (rc < 0) {
551 MSG_ERR("Failed to clear daemon ready state, reset failed\n");
552 return rc;
553 }
554
555 rc = __protocol_reset(context);
556 if (rc < 0) {
557 MSG_ERR("Failed to reset protocol, daemon remains not ready\n");
558 return rc;
559 }
560
561 rc = protocol_events_set(context,
562 BMC_EVENT_DAEMON_READY | BMC_EVENT_PROTOCOL_RESET);
563 if (rc < 0) {
564 MSG_ERR("Failed to set daemon ready state, daemon remains not ready\n");
565 return rc;
566 }
567
568 return 0;
569}