blob: 2c97f9251e4c8bd1d733448ed47077e66e72c60c [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/*
26 * protocol_events_set() - Set BMC events
27 * @context: The mbox context pointer
28 * @bmc_event: The bits to set
Andrew Jeffery5335f092018-08-09 14:56:08 +093029 *
30 * Return: 0 on success otherwise negative error code
31 */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093032int protocol_events_set(struct mbox_context *context, uint8_t bmc_event)
Andrew Jeffery5335f092018-08-09 14:56:08 +093033{
Andrew Jeffery0453aa42018-08-21 08:25:46 +093034 const uint8_t mask = protocol_get_bmc_event_mask(context);
Andrew Jeffery5335f092018-08-09 14:56:08 +093035
Andrew Jeffery0453aa42018-08-21 08:25:46 +093036 /*
37 * Store the raw value, as we may up- or down- grade the protocol
38 * version and subsequently need to flush the appropriate set. Instead
39 * we pass the masked value through to the transport
40 */
41 context->bmc_events |= bmc_event;
Andrew Jeffery5335f092018-08-09 14:56:08 +093042
Andrew Jeffery4414fb82018-08-20 12:13:09 +093043 return context->transport->set_events(context, (bmc_event & mask));
Andrew Jeffery5335f092018-08-09 14:56:08 +093044}
45
46/*
47 * protocol_events_clear() - Clear BMC events
48 * @context: The mbox context pointer
49 * @bmc_event: The bits to clear
Andrew Jeffery5335f092018-08-09 14:56:08 +093050 *
51 * Return: 0 on success otherwise negative error code
52 */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093053int protocol_events_clear(struct mbox_context *context, uint8_t bmc_event)
Andrew Jeffery5335f092018-08-09 14:56:08 +093054{
Andrew Jeffery0453aa42018-08-21 08:25:46 +093055 const uint8_t mask = protocol_get_bmc_event_mask(context);
56
57 context->bmc_events &= ~bmc_event;
58
59 return context->transport->clear_events(context, (bmc_event & mask));
Andrew Jeffery5335f092018-08-09 14:56:08 +093060}
61
Andrew Jefferyab666a52018-08-07 14:28:09 +093062int protocol_v1_reset(struct mbox_context *context)
63{
64 /* Host requested it -> No BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093065 windows_reset_all(context);
Andrew Jefferyab666a52018-08-07 14:28:09 +093066 return lpc_reset(context);
67}
68
Andrew Jeffery1e531af2018-08-07 13:32:57 +093069int protocol_v1_get_info(struct mbox_context *context,
70 struct protocol_get_info *io)
71{
72 uint8_t old_version = context->version;
73 int rc;
74
75 /* Bootstrap protocol version. This may involve {up,down}grading */
76 rc = protocol_negotiate_version(context, io->req.api_version);
77 if (rc < 0)
78 return rc;
79
80 /* Do the {up,down}grade if necessary*/
81 if (rc != old_version) {
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093082 /* Doing version negotiation, don't alert host to reset */
83 windows_reset_all(context);
Andrew Jeffery1e531af2018-08-07 13:32:57 +093084 return context->protocol->get_info(context, io);
85 }
86
87 /* Record the negotiated version for the response */
88 io->resp.api_version = rc;
89
90 /* Now do all required intialisation for v1 */
91 context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
92 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
93 1 << context->block_size_shift, context->block_size_shift);
94
95 /* Knowing blocksize we can allocate the window dirty_bytemap */
96 windows_alloc_dirty_bytemap(context);
97
98 io->resp.v1.read_window_size =
99 context->windows.default_size >> context->block_size_shift;
100 io->resp.v1.write_window_size =
101 context->windows.default_size >> context->block_size_shift;
102
103 return lpc_map_memory(context);
104}
105
Andrew Jeffery91a87452018-08-07 14:54:14 +0930106int protocol_v1_get_flash_info(struct mbox_context *context,
107 struct protocol_get_flash_info *io)
108{
109 io->resp.v1.flash_size = context->flash_size;
110 io->resp.v1.erase_size = context->mtd_info.erasesize;
111
112 return 0;
113}
114
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930115/*
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930116 * get_lpc_addr_shifted() - Get lpc address of the current window
117 * @context: The mbox context pointer
118 *
119 * Return: The lpc address to access that offset shifted by block size
120 */
121static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
122{
123 uint32_t lpc_addr, mem_offset;
124
125 /* Offset of the current window in the reserved memory region */
126 mem_offset = context->current->mem - context->mem;
127 /* Total LPC Address */
128 lpc_addr = context->lpc_base + mem_offset;
129
130 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
131
132 return lpc_addr >> context->block_size_shift;
133}
134
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930135int protocol_v1_create_window(struct mbox_context *context,
136 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930137{
138 int rc;
139 uint32_t offset = io->req.offset << context->block_size_shift;
140
141 /* Close the current window if there is one */
142 if (context->current) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930143 /* There is an implicit flush if it was a write window
144 *
145 * protocol_v2_create_window() calls
146 * protocol_v1_create_window(), so use indirect call to
147 * write_flush() to make sure we pick the right one.
148 */
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930149 if (context->current_is_write) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930150 rc = context->protocol->flush(context, NULL);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930151 if (rc < 0) {
152 MSG_ERR("Couldn't Flush Write Window\n");
153 return rc;
154 }
155 }
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930156 windows_close_current(context, FLAGS_NONE);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930157 }
158
159 /* Offset the host has requested */
160 MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
161 /* Check if we have an existing window */
162 context->current = windows_search(context, offset,
163 context->version == API_VERSION_1);
164
165 if (!context->current) { /* No existing window */
166 MSG_DBG("No existing window which maps that flash offset\n");
167 rc = windows_create_map(context, &context->current,
168 offset,
169 context->version == API_VERSION_1);
170 if (rc < 0) { /* Unable to map offset */
171 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930172 offset);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930173 return rc;
174 }
175 }
176
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930177 context->current_is_write = !io->req.ro;
178
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930179 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
180 context->current->mem, context->current->size,
181 context->current->flash_offset);
182
183 io->resp.lpc_address = get_lpc_addr_shifted(context);
184
185 return 0;
186}
187
Andrew Jefferya336e432018-08-07 16:00:40 +0930188int protocol_v1_mark_dirty(struct mbox_context *context,
189 struct protocol_mark_dirty *io)
190{
191 uint32_t offset = io->req.v1.offset;
192 uint32_t size = io->req.v1.size;
193 uint32_t off;
194
195 if (!(context->current && context->current_is_write)) {
196 MSG_ERR("Tried to call mark dirty without open write window\n");
197 return -EPERM;
198 }
199
200 /* For V1 offset given relative to flash - we want the window */
201 off = offset - ((context->current->flash_offset) >>
202 context->block_size_shift);
203 if (off > offset) { /* Underflow - before current window */
204 MSG_ERR("Tried to mark dirty before start of window\n");
205 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
206 offset << context->block_size_shift,
207 context->current->flash_offset);
208 return -EINVAL;
209 }
210 offset = off;
211 /*
212 * We only track dirty at the block level.
213 * For protocol V1 we can get away with just marking the whole
214 * block dirty.
215 */
216 size = align_up(size, 1 << context->block_size_shift);
217 size >>= context->block_size_shift;
218
219 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
220 offset << context->block_size_shift,
221 size << context->block_size_shift);
222
223 return window_set_bytemap(context, context->current, offset, size,
224 WINDOW_DIRTY);
225}
226
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930227static int generic_flush(struct mbox_context *context)
228{
229 int rc, i, offset, count;
230 uint8_t prev;
231
232 offset = 0;
233 count = 0;
234 prev = WINDOW_CLEAN;
235
236 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
237 context->current->mem, context->current->size,
238 context->current->flash_offset);
239
240 /*
241 * We look for streaks of the same type and keep a count, when the type
242 * (dirty/erased) changes we perform the required action on the backing
243 * store and update the current streak-type
244 */
245 for (i = 0; i < (context->current->size >> context->block_size_shift);
246 i++) {
247 uint8_t cur = context->current->dirty_bmap[i];
248 if (cur != WINDOW_CLEAN) {
249 if (cur == prev) { /* Same as previous block, incrmnt */
250 count++;
251 } else if (prev == WINDOW_CLEAN) { /* Start of run */
252 offset = i;
253 count++;
254 } else { /* Change in streak type */
255 rc = window_flush(context, offset, count,
256 prev);
257 if (rc < 0) {
258 return rc;
259 }
260 offset = i;
261 count = 1;
262 }
263 } else {
264 if (prev != WINDOW_CLEAN) { /* End of a streak */
265 rc = window_flush(context, offset, count,
266 prev);
267 if (rc < 0) {
268 return rc;
269 }
270 offset = 0;
271 count = 0;
272 }
273 }
274 prev = cur;
275 }
276
277 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
278 rc = window_flush(context, offset, count, prev);
279 if (rc < 0) {
280 return rc;
281 }
282 }
283
284 /* Clear the dirty bytemap since we have written back all changes */
285 return window_set_bytemap(context, context->current, 0,
286 context->current->size >>
287 context->block_size_shift,
288 WINDOW_CLEAN);
289}
290
291int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
292{
293 int rc;
294
295 if (!(context->current && context->current_is_write)) {
296 MSG_ERR("Tried to call flush without open write window\n");
297 return -EPERM;
298 }
299
300 /*
301 * For V1 the Flush command acts much the same as the dirty command
302 * except with a flush as well. Only do this on an actual flush
303 * command not when we call flush because we've implicitly closed a
304 * window because we might not have the required args in req.
305 */
Andrew Jeffery093eda52018-08-07 23:10:43 +0930306 if (io) {
307 struct protocol_mark_dirty *mdio = (void *)io;
308 rc = protocol_v1_mark_dirty(context, mdio);
309 if (rc < 0) {
310 return rc;
311 }
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930312 }
313
314 return generic_flush(context);
315}
316
Andrew Jeffery093eda52018-08-07 23:10:43 +0930317int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
318{
319 int rc;
320
321 /* Close the current window if there is one */
322 if (!context->current) {
323 return 0;
324 }
325
326 /* There is an implicit flush if it was a write window */
327 if (context->current_is_write) {
328 rc = protocol_v1_flush(context, NULL);
329 if (rc < 0) {
330 MSG_ERR("Couldn't Flush Write Window\n");
331 return rc;
332 }
333 }
334
335 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930336 windows_close_current(context, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930337
338 return 0;
339}
340
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930341int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
342{
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930343 return protocol_events_clear(context,
344 (io->req.flags & BMC_EVENT_ACK_MASK));
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930345}
346
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930347/*
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930348 * get_suggested_timeout() - get the suggested timeout value in seconds
349 * @context: The mbox context pointer
350 *
351 * Return: Suggested timeout in seconds
352 */
353static uint16_t get_suggested_timeout(struct mbox_context *context)
354{
355 struct window_context *window = windows_find_largest(context);
356 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
357 uint16_t ret;
358
359 ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
360
361 MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
362 ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
363 return ret;
364}
365
366int protocol_v2_get_info(struct mbox_context *context,
367 struct protocol_get_info *io)
368{
369 uint8_t old_version = context->version;
370 int rc;
371
372 /* Bootstrap protocol version. This may involve {up,down}grading */
373 rc = protocol_negotiate_version(context, io->req.api_version);
374 if (rc < 0)
375 return rc;
376
377 /* Do the {up,down}grade if necessary*/
378 if (rc != old_version) {
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930379 /* Doing version negotiation, don't alert host to reset */
380 windows_reset_all(context);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930381 return context->protocol->get_info(context, io);
382 }
383
384 /* Record the negotiated version for the response */
385 io->resp.api_version = rc;
386
387 /* Now do all required intialisation for v2 */
388 context->block_size_shift = log_2(context->mtd_info.erasesize);
389 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
390 1 << context->block_size_shift, context->block_size_shift);
391
392 /* Knowing blocksize we can allocate the window dirty_bytemap */
393 windows_alloc_dirty_bytemap(context);
394
395 io->resp.v2.block_size_shift = context->block_size_shift;
396 io->resp.v2.timeout = get_suggested_timeout(context);
397
398 return lpc_map_memory(context);
399}
400
Andrew Jeffery91a87452018-08-07 14:54:14 +0930401int protocol_v2_get_flash_info(struct mbox_context *context,
402 struct protocol_get_flash_info *io)
403{
404 io->resp.v2.flash_size =
405 context->flash_size >> context->block_size_shift;
406 io->resp.v2.erase_size =
407 context->mtd_info.erasesize >> context->block_size_shift;
408
409 return 0;
410}
411
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930412int protocol_v2_create_window(struct mbox_context *context,
413 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930414{
415 int rc;
416
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930417 rc = protocol_v1_create_window(context, io);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930418 if (rc < 0)
419 return rc;
420
421 io->resp.size = context->current->size >> context->block_size_shift;
422 io->resp.offset = context->current->flash_offset >>
423 context->block_size_shift;
424
425 return 0;
426}
427
Andrew Jefferya336e432018-08-07 16:00:40 +0930428int protocol_v2_mark_dirty(struct mbox_context *context,
429 struct protocol_mark_dirty *io)
430{
431 if (!(context->current && context->current_is_write)) {
432 MSG_ERR("Tried to call mark dirty without open write window\n");
433 return -EPERM;
434 }
435
436 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
437 io->req.v2.offset << context->block_size_shift,
438 io->req.v2.size << context->block_size_shift);
439
440 return window_set_bytemap(context, context->current, io->req.v2.offset,
441 io->req.v2.size, WINDOW_DIRTY);
442}
443
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930444int protocol_v2_erase(struct mbox_context *context,
445 struct protocol_erase *io)
446{
447 size_t start, len;
448 int rc;
449
450 if (!(context->current && context->current_is_write)) {
451 MSG_ERR("Tried to call erase without open write window\n");
452 return -EPERM;
453 }
454
455 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
456 io->req.offset << context->block_size_shift,
457 io->req.size << context->block_size_shift);
458
459 rc = window_set_bytemap(context, context->current, io->req.offset,
460 io->req.size, WINDOW_ERASED);
461 if (rc < 0) {
462 return rc;
463 }
464
465 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
466 start = io->req.offset << context->block_size_shift;
467 len = io->req.size << context->block_size_shift;
468 memset(context->current->mem + start, 0xFF, len);
469
470 return 0;
471}
472
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930473int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
474{
475 if (!(context->current && context->current_is_write)) {
476 MSG_ERR("Tried to call flush without open write window\n");
477 return -EPERM;
478 }
479
480 return generic_flush(context);
481}
482
Andrew Jeffery093eda52018-08-07 23:10:43 +0930483int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
484{
485 int rc;
486
487 /* Close the current window if there is one */
488 if (!context->current) {
489 return 0;
490 }
491
492 /* There is an implicit flush if it was a write window */
493 if (context->current_is_write) {
494 rc = protocol_v2_flush(context, NULL);
495 if (rc < 0) {
496 MSG_ERR("Couldn't Flush Write Window\n");
497 return rc;
498 }
499 }
500
501 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930502 windows_close_current(context, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930503
504 return 0;
505}
506
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930507int protocol_init(struct mbox_context *context)
508{
Andrew Jefferyc7d19472018-08-08 11:43:08 +0930509 protocol_negotiate_version(context, API_MAX_VERSION);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930510
511 return 0;
512}
513
514void protocol_free(struct mbox_context *context)
515{
516 return;
517}