blob: 82c1ca79f7c23281a4ed2e7df6b54fe638e352f8 [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 Jeffery5335f092018-08-09 14:56:08 +093016/*
17 * protocol_events_set() - Set BMC events
18 * @context: The mbox context pointer
19 * @bmc_event: The bits to set
Andrew Jeffery5335f092018-08-09 14:56:08 +093020 *
21 * Return: 0 on success otherwise negative error code
22 */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093023int protocol_events_set(struct mbox_context *context, uint8_t bmc_event)
Andrew Jeffery5335f092018-08-09 14:56:08 +093024{
25 uint8_t mask = 0x00;
26
27 switch (context->version) {
28 case API_VERSION_1:
29 mask = BMC_EVENT_V1_MASK;
30 break;
31 default:
32 mask = BMC_EVENT_V2_MASK;
33 break;
34 }
35
Andrew Jeffery4414fb82018-08-20 12:13:09 +093036 return context->transport->set_events(context, (bmc_event & mask));
Andrew Jeffery5335f092018-08-09 14:56:08 +093037}
38
39/*
40 * protocol_events_clear() - Clear BMC events
41 * @context: The mbox context pointer
42 * @bmc_event: The bits to clear
Andrew Jeffery5335f092018-08-09 14:56:08 +093043 *
44 * Return: 0 on success otherwise negative error code
45 */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093046int protocol_events_clear(struct mbox_context *context, uint8_t bmc_event)
Andrew Jeffery5335f092018-08-09 14:56:08 +093047{
Andrew Jeffery4414fb82018-08-20 12:13:09 +093048 return context->transport->clear_events(context, bmc_event);
Andrew Jeffery5335f092018-08-09 14:56:08 +093049}
50
Andrew Jefferyab666a52018-08-07 14:28:09 +093051int protocol_v1_reset(struct mbox_context *context)
52{
53 /* Host requested it -> No BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093054 windows_reset_all(context);
Andrew Jefferyab666a52018-08-07 14:28:09 +093055 return lpc_reset(context);
56}
57
Andrew Jeffery1e531af2018-08-07 13:32:57 +093058int protocol_v1_get_info(struct mbox_context *context,
59 struct protocol_get_info *io)
60{
61 uint8_t old_version = context->version;
62 int rc;
63
64 /* Bootstrap protocol version. This may involve {up,down}grading */
65 rc = protocol_negotiate_version(context, io->req.api_version);
66 if (rc < 0)
67 return rc;
68
69 /* Do the {up,down}grade if necessary*/
70 if (rc != old_version) {
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093071 /* Doing version negotiation, don't alert host to reset */
72 windows_reset_all(context);
Andrew Jeffery1e531af2018-08-07 13:32:57 +093073 return context->protocol->get_info(context, io);
74 }
75
76 /* Record the negotiated version for the response */
77 io->resp.api_version = rc;
78
79 /* Now do all required intialisation for v1 */
80 context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
81 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
82 1 << context->block_size_shift, context->block_size_shift);
83
84 /* Knowing blocksize we can allocate the window dirty_bytemap */
85 windows_alloc_dirty_bytemap(context);
86
87 io->resp.v1.read_window_size =
88 context->windows.default_size >> context->block_size_shift;
89 io->resp.v1.write_window_size =
90 context->windows.default_size >> context->block_size_shift;
91
92 return lpc_map_memory(context);
93}
94
Andrew Jeffery91a87452018-08-07 14:54:14 +093095int protocol_v1_get_flash_info(struct mbox_context *context,
96 struct protocol_get_flash_info *io)
97{
98 io->resp.v1.flash_size = context->flash_size;
99 io->resp.v1.erase_size = context->mtd_info.erasesize;
100
101 return 0;
102}
103
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930104/*
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930105 * get_lpc_addr_shifted() - Get lpc address of the current window
106 * @context: The mbox context pointer
107 *
108 * Return: The lpc address to access that offset shifted by block size
109 */
110static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
111{
112 uint32_t lpc_addr, mem_offset;
113
114 /* Offset of the current window in the reserved memory region */
115 mem_offset = context->current->mem - context->mem;
116 /* Total LPC Address */
117 lpc_addr = context->lpc_base + mem_offset;
118
119 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
120
121 return lpc_addr >> context->block_size_shift;
122}
123
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930124int protocol_v1_create_window(struct mbox_context *context,
125 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930126{
127 int rc;
128 uint32_t offset = io->req.offset << context->block_size_shift;
129
130 /* Close the current window if there is one */
131 if (context->current) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930132 /* There is an implicit flush if it was a write window
133 *
134 * protocol_v2_create_window() calls
135 * protocol_v1_create_window(), so use indirect call to
136 * write_flush() to make sure we pick the right one.
137 */
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930138 if (context->current_is_write) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930139 rc = context->protocol->flush(context, NULL);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930140 if (rc < 0) {
141 MSG_ERR("Couldn't Flush Write Window\n");
142 return rc;
143 }
144 }
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930145 windows_close_current(context, FLAGS_NONE);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930146 }
147
148 /* Offset the host has requested */
149 MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
150 /* Check if we have an existing window */
151 context->current = windows_search(context, offset,
152 context->version == API_VERSION_1);
153
154 if (!context->current) { /* No existing window */
155 MSG_DBG("No existing window which maps that flash offset\n");
156 rc = windows_create_map(context, &context->current,
157 offset,
158 context->version == API_VERSION_1);
159 if (rc < 0) { /* Unable to map offset */
160 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930161 offset);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930162 return rc;
163 }
164 }
165
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930166 context->current_is_write = !io->req.ro;
167
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930168 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
169 context->current->mem, context->current->size,
170 context->current->flash_offset);
171
172 io->resp.lpc_address = get_lpc_addr_shifted(context);
173
174 return 0;
175}
176
Andrew Jefferya336e432018-08-07 16:00:40 +0930177int protocol_v1_mark_dirty(struct mbox_context *context,
178 struct protocol_mark_dirty *io)
179{
180 uint32_t offset = io->req.v1.offset;
181 uint32_t size = io->req.v1.size;
182 uint32_t off;
183
184 if (!(context->current && context->current_is_write)) {
185 MSG_ERR("Tried to call mark dirty without open write window\n");
186 return -EPERM;
187 }
188
189 /* For V1 offset given relative to flash - we want the window */
190 off = offset - ((context->current->flash_offset) >>
191 context->block_size_shift);
192 if (off > offset) { /* Underflow - before current window */
193 MSG_ERR("Tried to mark dirty before start of window\n");
194 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
195 offset << context->block_size_shift,
196 context->current->flash_offset);
197 return -EINVAL;
198 }
199 offset = off;
200 /*
201 * We only track dirty at the block level.
202 * For protocol V1 we can get away with just marking the whole
203 * block dirty.
204 */
205 size = align_up(size, 1 << context->block_size_shift);
206 size >>= context->block_size_shift;
207
208 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
209 offset << context->block_size_shift,
210 size << context->block_size_shift);
211
212 return window_set_bytemap(context, context->current, offset, size,
213 WINDOW_DIRTY);
214}
215
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930216static int generic_flush(struct mbox_context *context)
217{
218 int rc, i, offset, count;
219 uint8_t prev;
220
221 offset = 0;
222 count = 0;
223 prev = WINDOW_CLEAN;
224
225 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
226 context->current->mem, context->current->size,
227 context->current->flash_offset);
228
229 /*
230 * We look for streaks of the same type and keep a count, when the type
231 * (dirty/erased) changes we perform the required action on the backing
232 * store and update the current streak-type
233 */
234 for (i = 0; i < (context->current->size >> context->block_size_shift);
235 i++) {
236 uint8_t cur = context->current->dirty_bmap[i];
237 if (cur != WINDOW_CLEAN) {
238 if (cur == prev) { /* Same as previous block, incrmnt */
239 count++;
240 } else if (prev == WINDOW_CLEAN) { /* Start of run */
241 offset = i;
242 count++;
243 } else { /* Change in streak type */
244 rc = window_flush(context, offset, count,
245 prev);
246 if (rc < 0) {
247 return rc;
248 }
249 offset = i;
250 count = 1;
251 }
252 } else {
253 if (prev != WINDOW_CLEAN) { /* End of a streak */
254 rc = window_flush(context, offset, count,
255 prev);
256 if (rc < 0) {
257 return rc;
258 }
259 offset = 0;
260 count = 0;
261 }
262 }
263 prev = cur;
264 }
265
266 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
267 rc = window_flush(context, offset, count, prev);
268 if (rc < 0) {
269 return rc;
270 }
271 }
272
273 /* Clear the dirty bytemap since we have written back all changes */
274 return window_set_bytemap(context, context->current, 0,
275 context->current->size >>
276 context->block_size_shift,
277 WINDOW_CLEAN);
278}
279
280int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
281{
282 int rc;
283
284 if (!(context->current && context->current_is_write)) {
285 MSG_ERR("Tried to call flush without open write window\n");
286 return -EPERM;
287 }
288
289 /*
290 * For V1 the Flush command acts much the same as the dirty command
291 * except with a flush as well. Only do this on an actual flush
292 * command not when we call flush because we've implicitly closed a
293 * window because we might not have the required args in req.
294 */
Andrew Jeffery093eda52018-08-07 23:10:43 +0930295 if (io) {
296 struct protocol_mark_dirty *mdio = (void *)io;
297 rc = protocol_v1_mark_dirty(context, mdio);
298 if (rc < 0) {
299 return rc;
300 }
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930301 }
302
303 return generic_flush(context);
304}
305
Andrew Jeffery093eda52018-08-07 23:10:43 +0930306int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
307{
308 int rc;
309
310 /* Close the current window if there is one */
311 if (!context->current) {
312 return 0;
313 }
314
315 /* There is an implicit flush if it was a write window */
316 if (context->current_is_write) {
317 rc = protocol_v1_flush(context, NULL);
318 if (rc < 0) {
319 MSG_ERR("Couldn't Flush Write Window\n");
320 return rc;
321 }
322 }
323
324 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930325 windows_close_current(context, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930326
327 return 0;
328}
329
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930330int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
331{
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930332 return protocol_events_clear(context,
333 (io->req.flags & BMC_EVENT_ACK_MASK));
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930334}
335
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930336/*
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930337 * get_suggested_timeout() - get the suggested timeout value in seconds
338 * @context: The mbox context pointer
339 *
340 * Return: Suggested timeout in seconds
341 */
342static uint16_t get_suggested_timeout(struct mbox_context *context)
343{
344 struct window_context *window = windows_find_largest(context);
345 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
346 uint16_t ret;
347
348 ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
349
350 MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
351 ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
352 return ret;
353}
354
355int protocol_v2_get_info(struct mbox_context *context,
356 struct protocol_get_info *io)
357{
358 uint8_t old_version = context->version;
359 int rc;
360
361 /* Bootstrap protocol version. This may involve {up,down}grading */
362 rc = protocol_negotiate_version(context, io->req.api_version);
363 if (rc < 0)
364 return rc;
365
366 /* Do the {up,down}grade if necessary*/
367 if (rc != old_version) {
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930368 /* Doing version negotiation, don't alert host to reset */
369 windows_reset_all(context);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930370 return context->protocol->get_info(context, io);
371 }
372
373 /* Record the negotiated version for the response */
374 io->resp.api_version = rc;
375
376 /* Now do all required intialisation for v2 */
377 context->block_size_shift = log_2(context->mtd_info.erasesize);
378 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
379 1 << context->block_size_shift, context->block_size_shift);
380
381 /* Knowing blocksize we can allocate the window dirty_bytemap */
382 windows_alloc_dirty_bytemap(context);
383
384 io->resp.v2.block_size_shift = context->block_size_shift;
385 io->resp.v2.timeout = get_suggested_timeout(context);
386
387 return lpc_map_memory(context);
388}
389
Andrew Jeffery91a87452018-08-07 14:54:14 +0930390int protocol_v2_get_flash_info(struct mbox_context *context,
391 struct protocol_get_flash_info *io)
392{
393 io->resp.v2.flash_size =
394 context->flash_size >> context->block_size_shift;
395 io->resp.v2.erase_size =
396 context->mtd_info.erasesize >> context->block_size_shift;
397
398 return 0;
399}
400
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930401int protocol_v2_create_window(struct mbox_context *context,
402 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930403{
404 int rc;
405
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930406 rc = protocol_v1_create_window(context, io);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930407 if (rc < 0)
408 return rc;
409
410 io->resp.size = context->current->size >> context->block_size_shift;
411 io->resp.offset = context->current->flash_offset >>
412 context->block_size_shift;
413
414 return 0;
415}
416
Andrew Jefferya336e432018-08-07 16:00:40 +0930417int protocol_v2_mark_dirty(struct mbox_context *context,
418 struct protocol_mark_dirty *io)
419{
420 if (!(context->current && context->current_is_write)) {
421 MSG_ERR("Tried to call mark dirty without open write window\n");
422 return -EPERM;
423 }
424
425 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
426 io->req.v2.offset << context->block_size_shift,
427 io->req.v2.size << context->block_size_shift);
428
429 return window_set_bytemap(context, context->current, io->req.v2.offset,
430 io->req.v2.size, WINDOW_DIRTY);
431}
432
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930433int protocol_v2_erase(struct mbox_context *context,
434 struct protocol_erase *io)
435{
436 size_t start, len;
437 int rc;
438
439 if (!(context->current && context->current_is_write)) {
440 MSG_ERR("Tried to call erase without open write window\n");
441 return -EPERM;
442 }
443
444 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
445 io->req.offset << context->block_size_shift,
446 io->req.size << context->block_size_shift);
447
448 rc = window_set_bytemap(context, context->current, io->req.offset,
449 io->req.size, WINDOW_ERASED);
450 if (rc < 0) {
451 return rc;
452 }
453
454 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
455 start = io->req.offset << context->block_size_shift;
456 len = io->req.size << context->block_size_shift;
457 memset(context->current->mem + start, 0xFF, len);
458
459 return 0;
460}
461
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930462int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
463{
464 if (!(context->current && context->current_is_write)) {
465 MSG_ERR("Tried to call flush without open write window\n");
466 return -EPERM;
467 }
468
469 return generic_flush(context);
470}
471
Andrew Jeffery093eda52018-08-07 23:10:43 +0930472int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
473{
474 int rc;
475
476 /* Close the current window if there is one */
477 if (!context->current) {
478 return 0;
479 }
480
481 /* There is an implicit flush if it was a write window */
482 if (context->current_is_write) {
483 rc = protocol_v2_flush(context, NULL);
484 if (rc < 0) {
485 MSG_ERR("Couldn't Flush Write Window\n");
486 return rc;
487 }
488 }
489
490 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930491 windows_close_current(context, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930492
493 return 0;
494}
495
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930496int protocol_init(struct mbox_context *context)
497{
Andrew Jefferyc7d19472018-08-08 11:43:08 +0930498 protocol_negotiate_version(context, API_MAX_VERSION);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930499
500 return 0;
501}
502
503void protocol_free(struct mbox_context *context)
504{
505 return;
506}