blob: 58dad982370f003918bb25660accdda978036a9a [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
36 context->bmc_events |= (bmc_event & mask);
37 MSG_DBG("BMC Events set to: 0x%.2x\n", context->bmc_events);
38
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093039 return context->transport->flush_events(context);
Andrew Jeffery5335f092018-08-09 14:56:08 +093040}
41
42/*
43 * protocol_events_clear() - Clear BMC events
44 * @context: The mbox context pointer
45 * @bmc_event: The bits to clear
Andrew Jeffery5335f092018-08-09 14:56:08 +093046 *
47 * Return: 0 on success otherwise negative error code
48 */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093049int protocol_events_clear(struct mbox_context *context, uint8_t bmc_event)
Andrew Jeffery5335f092018-08-09 14:56:08 +093050{
51 context->bmc_events &= ~bmc_event;
52 MSG_DBG("BMC Events clear to: 0x%.2x\n", context->bmc_events);
53
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093054 return context->transport->flush_events(context);
Andrew Jeffery5335f092018-08-09 14:56:08 +093055}
56
Andrew Jefferyab666a52018-08-07 14:28:09 +093057int protocol_v1_reset(struct mbox_context *context)
58{
59 /* Host requested it -> No BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093060 windows_reset_all(context);
Andrew Jefferyab666a52018-08-07 14:28:09 +093061 return lpc_reset(context);
62}
63
Andrew Jeffery1e531af2018-08-07 13:32:57 +093064int protocol_v1_get_info(struct mbox_context *context,
65 struct protocol_get_info *io)
66{
67 uint8_t old_version = context->version;
68 int rc;
69
70 /* Bootstrap protocol version. This may involve {up,down}grading */
71 rc = protocol_negotiate_version(context, io->req.api_version);
72 if (rc < 0)
73 return rc;
74
75 /* Do the {up,down}grade if necessary*/
76 if (rc != old_version) {
Andrew Jeffery2ebfd202018-08-20 11:46:28 +093077 /* Doing version negotiation, don't alert host to reset */
78 windows_reset_all(context);
Andrew Jeffery1e531af2018-08-07 13:32:57 +093079 return context->protocol->get_info(context, io);
80 }
81
82 /* Record the negotiated version for the response */
83 io->resp.api_version = rc;
84
85 /* Now do all required intialisation for v1 */
86 context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
87 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
88 1 << context->block_size_shift, context->block_size_shift);
89
90 /* Knowing blocksize we can allocate the window dirty_bytemap */
91 windows_alloc_dirty_bytemap(context);
92
93 io->resp.v1.read_window_size =
94 context->windows.default_size >> context->block_size_shift;
95 io->resp.v1.write_window_size =
96 context->windows.default_size >> context->block_size_shift;
97
98 return lpc_map_memory(context);
99}
100
Andrew Jeffery91a87452018-08-07 14:54:14 +0930101int protocol_v1_get_flash_info(struct mbox_context *context,
102 struct protocol_get_flash_info *io)
103{
104 io->resp.v1.flash_size = context->flash_size;
105 io->resp.v1.erase_size = context->mtd_info.erasesize;
106
107 return 0;
108}
109
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930110/*
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930111 * get_lpc_addr_shifted() - Get lpc address of the current window
112 * @context: The mbox context pointer
113 *
114 * Return: The lpc address to access that offset shifted by block size
115 */
116static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
117{
118 uint32_t lpc_addr, mem_offset;
119
120 /* Offset of the current window in the reserved memory region */
121 mem_offset = context->current->mem - context->mem;
122 /* Total LPC Address */
123 lpc_addr = context->lpc_base + mem_offset;
124
125 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
126
127 return lpc_addr >> context->block_size_shift;
128}
129
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930130int protocol_v1_create_window(struct mbox_context *context,
131 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930132{
133 int rc;
134 uint32_t offset = io->req.offset << context->block_size_shift;
135
136 /* Close the current window if there is one */
137 if (context->current) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930138 /* There is an implicit flush if it was a write window
139 *
140 * protocol_v2_create_window() calls
141 * protocol_v1_create_window(), so use indirect call to
142 * write_flush() to make sure we pick the right one.
143 */
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930144 if (context->current_is_write) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930145 rc = context->protocol->flush(context, NULL);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930146 if (rc < 0) {
147 MSG_ERR("Couldn't Flush Write Window\n");
148 return rc;
149 }
150 }
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930151 windows_close_current(context, FLAGS_NONE);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930152 }
153
154 /* Offset the host has requested */
155 MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
156 /* Check if we have an existing window */
157 context->current = windows_search(context, offset,
158 context->version == API_VERSION_1);
159
160 if (!context->current) { /* No existing window */
161 MSG_DBG("No existing window which maps that flash offset\n");
162 rc = windows_create_map(context, &context->current,
163 offset,
164 context->version == API_VERSION_1);
165 if (rc < 0) { /* Unable to map offset */
166 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930167 offset);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930168 return rc;
169 }
170 }
171
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930172 context->current_is_write = !io->req.ro;
173
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930174 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
175 context->current->mem, context->current->size,
176 context->current->flash_offset);
177
178 io->resp.lpc_address = get_lpc_addr_shifted(context);
179
180 return 0;
181}
182
Andrew Jefferya336e432018-08-07 16:00:40 +0930183int protocol_v1_mark_dirty(struct mbox_context *context,
184 struct protocol_mark_dirty *io)
185{
186 uint32_t offset = io->req.v1.offset;
187 uint32_t size = io->req.v1.size;
188 uint32_t off;
189
190 if (!(context->current && context->current_is_write)) {
191 MSG_ERR("Tried to call mark dirty without open write window\n");
192 return -EPERM;
193 }
194
195 /* For V1 offset given relative to flash - we want the window */
196 off = offset - ((context->current->flash_offset) >>
197 context->block_size_shift);
198 if (off > offset) { /* Underflow - before current window */
199 MSG_ERR("Tried to mark dirty before start of window\n");
200 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
201 offset << context->block_size_shift,
202 context->current->flash_offset);
203 return -EINVAL;
204 }
205 offset = off;
206 /*
207 * We only track dirty at the block level.
208 * For protocol V1 we can get away with just marking the whole
209 * block dirty.
210 */
211 size = align_up(size, 1 << context->block_size_shift);
212 size >>= context->block_size_shift;
213
214 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
215 offset << context->block_size_shift,
216 size << context->block_size_shift);
217
218 return window_set_bytemap(context, context->current, offset, size,
219 WINDOW_DIRTY);
220}
221
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930222static int generic_flush(struct mbox_context *context)
223{
224 int rc, i, offset, count;
225 uint8_t prev;
226
227 offset = 0;
228 count = 0;
229 prev = WINDOW_CLEAN;
230
231 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
232 context->current->mem, context->current->size,
233 context->current->flash_offset);
234
235 /*
236 * We look for streaks of the same type and keep a count, when the type
237 * (dirty/erased) changes we perform the required action on the backing
238 * store and update the current streak-type
239 */
240 for (i = 0; i < (context->current->size >> context->block_size_shift);
241 i++) {
242 uint8_t cur = context->current->dirty_bmap[i];
243 if (cur != WINDOW_CLEAN) {
244 if (cur == prev) { /* Same as previous block, incrmnt */
245 count++;
246 } else if (prev == WINDOW_CLEAN) { /* Start of run */
247 offset = i;
248 count++;
249 } else { /* Change in streak type */
250 rc = window_flush(context, offset, count,
251 prev);
252 if (rc < 0) {
253 return rc;
254 }
255 offset = i;
256 count = 1;
257 }
258 } else {
259 if (prev != WINDOW_CLEAN) { /* End of a streak */
260 rc = window_flush(context, offset, count,
261 prev);
262 if (rc < 0) {
263 return rc;
264 }
265 offset = 0;
266 count = 0;
267 }
268 }
269 prev = cur;
270 }
271
272 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
273 rc = window_flush(context, offset, count, prev);
274 if (rc < 0) {
275 return rc;
276 }
277 }
278
279 /* Clear the dirty bytemap since we have written back all changes */
280 return window_set_bytemap(context, context->current, 0,
281 context->current->size >>
282 context->block_size_shift,
283 WINDOW_CLEAN);
284}
285
286int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
287{
288 int rc;
289
290 if (!(context->current && context->current_is_write)) {
291 MSG_ERR("Tried to call flush without open write window\n");
292 return -EPERM;
293 }
294
295 /*
296 * For V1 the Flush command acts much the same as the dirty command
297 * except with a flush as well. Only do this on an actual flush
298 * command not when we call flush because we've implicitly closed a
299 * window because we might not have the required args in req.
300 */
Andrew Jeffery093eda52018-08-07 23:10:43 +0930301 if (io) {
302 struct protocol_mark_dirty *mdio = (void *)io;
303 rc = protocol_v1_mark_dirty(context, mdio);
304 if (rc < 0) {
305 return rc;
306 }
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930307 }
308
309 return generic_flush(context);
310}
311
Andrew Jeffery093eda52018-08-07 23:10:43 +0930312int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
313{
314 int rc;
315
316 /* Close the current window if there is one */
317 if (!context->current) {
318 return 0;
319 }
320
321 /* There is an implicit flush if it was a write window */
322 if (context->current_is_write) {
323 rc = protocol_v1_flush(context, NULL);
324 if (rc < 0) {
325 MSG_ERR("Couldn't Flush Write Window\n");
326 return rc;
327 }
328 }
329
330 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930331 windows_close_current(context, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930332
333 return 0;
334}
335
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930336int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
337{
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930338 return protocol_events_clear(context,
339 (io->req.flags & BMC_EVENT_ACK_MASK));
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930340}
341
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930342/*
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930343 * get_suggested_timeout() - get the suggested timeout value in seconds
344 * @context: The mbox context pointer
345 *
346 * Return: Suggested timeout in seconds
347 */
348static uint16_t get_suggested_timeout(struct mbox_context *context)
349{
350 struct window_context *window = windows_find_largest(context);
351 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
352 uint16_t ret;
353
354 ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
355
356 MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
357 ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
358 return ret;
359}
360
361int protocol_v2_get_info(struct mbox_context *context,
362 struct protocol_get_info *io)
363{
364 uint8_t old_version = context->version;
365 int rc;
366
367 /* Bootstrap protocol version. This may involve {up,down}grading */
368 rc = protocol_negotiate_version(context, io->req.api_version);
369 if (rc < 0)
370 return rc;
371
372 /* Do the {up,down}grade if necessary*/
373 if (rc != old_version) {
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930374 /* Doing version negotiation, don't alert host to reset */
375 windows_reset_all(context);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930376 return context->protocol->get_info(context, io);
377 }
378
379 /* Record the negotiated version for the response */
380 io->resp.api_version = rc;
381
382 /* Now do all required intialisation for v2 */
383 context->block_size_shift = log_2(context->mtd_info.erasesize);
384 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
385 1 << context->block_size_shift, context->block_size_shift);
386
387 /* Knowing blocksize we can allocate the window dirty_bytemap */
388 windows_alloc_dirty_bytemap(context);
389
390 io->resp.v2.block_size_shift = context->block_size_shift;
391 io->resp.v2.timeout = get_suggested_timeout(context);
392
393 return lpc_map_memory(context);
394}
395
Andrew Jeffery91a87452018-08-07 14:54:14 +0930396int protocol_v2_get_flash_info(struct mbox_context *context,
397 struct protocol_get_flash_info *io)
398{
399 io->resp.v2.flash_size =
400 context->flash_size >> context->block_size_shift;
401 io->resp.v2.erase_size =
402 context->mtd_info.erasesize >> context->block_size_shift;
403
404 return 0;
405}
406
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930407int protocol_v2_create_window(struct mbox_context *context,
408 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930409{
410 int rc;
411
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930412 rc = protocol_v1_create_window(context, io);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930413 if (rc < 0)
414 return rc;
415
416 io->resp.size = context->current->size >> context->block_size_shift;
417 io->resp.offset = context->current->flash_offset >>
418 context->block_size_shift;
419
420 return 0;
421}
422
Andrew Jefferya336e432018-08-07 16:00:40 +0930423int protocol_v2_mark_dirty(struct mbox_context *context,
424 struct protocol_mark_dirty *io)
425{
426 if (!(context->current && context->current_is_write)) {
427 MSG_ERR("Tried to call mark dirty without open write window\n");
428 return -EPERM;
429 }
430
431 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
432 io->req.v2.offset << context->block_size_shift,
433 io->req.v2.size << context->block_size_shift);
434
435 return window_set_bytemap(context, context->current, io->req.v2.offset,
436 io->req.v2.size, WINDOW_DIRTY);
437}
438
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930439int protocol_v2_erase(struct mbox_context *context,
440 struct protocol_erase *io)
441{
442 size_t start, len;
443 int rc;
444
445 if (!(context->current && context->current_is_write)) {
446 MSG_ERR("Tried to call erase without open write window\n");
447 return -EPERM;
448 }
449
450 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
451 io->req.offset << context->block_size_shift,
452 io->req.size << context->block_size_shift);
453
454 rc = window_set_bytemap(context, context->current, io->req.offset,
455 io->req.size, WINDOW_ERASED);
456 if (rc < 0) {
457 return rc;
458 }
459
460 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
461 start = io->req.offset << context->block_size_shift;
462 len = io->req.size << context->block_size_shift;
463 memset(context->current->mem + start, 0xFF, len);
464
465 return 0;
466}
467
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930468int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
469{
470 if (!(context->current && context->current_is_write)) {
471 MSG_ERR("Tried to call flush without open write window\n");
472 return -EPERM;
473 }
474
475 return generic_flush(context);
476}
477
Andrew Jeffery093eda52018-08-07 23:10:43 +0930478int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
479{
480 int rc;
481
482 /* Close the current window if there is one */
483 if (!context->current) {
484 return 0;
485 }
486
487 /* There is an implicit flush if it was a write window */
488 if (context->current_is_write) {
489 rc = protocol_v2_flush(context, NULL);
490 if (rc < 0) {
491 MSG_ERR("Couldn't Flush Write Window\n");
492 return rc;
493 }
494 }
495
496 /* Host asked for it -> Don't set the BMC Event */
Andrew Jeffery2ebfd202018-08-20 11:46:28 +0930497 windows_close_current(context, io->req.flags);
Andrew Jeffery093eda52018-08-07 23:10:43 +0930498
499 return 0;
500}
501
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930502int protocol_init(struct mbox_context *context)
503{
Andrew Jefferyc7d19472018-08-08 11:43:08 +0930504 protocol_negotiate_version(context, API_MAX_VERSION);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930505
506 return 0;
507}
508
509void protocol_free(struct mbox_context *context)
510{
511 return;
512}