blob: b11223145add14689775aa321cc6f22cb507bcb3 [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
8#include "mbox.h"
9#include "lpc.h"
10#include "transport_mbox.h" /* TODO: Remove dependency on transport_mbox.h */
11#include "windows.h"
12
Andrew Jefferyab666a52018-08-07 14:28:09 +093013int protocol_v1_reset(struct mbox_context *context)
14{
15 /* Host requested it -> No BMC Event */
16 windows_reset_all(context, NO_BMC_EVENT);
17 return lpc_reset(context);
18}
19
Andrew Jeffery1e531af2018-08-07 13:32:57 +093020int protocol_v1_get_info(struct mbox_context *context,
21 struct protocol_get_info *io)
22{
23 uint8_t old_version = context->version;
24 int rc;
25
26 /* Bootstrap protocol version. This may involve {up,down}grading */
27 rc = protocol_negotiate_version(context, io->req.api_version);
28 if (rc < 0)
29 return rc;
30
31 /* Do the {up,down}grade if necessary*/
32 if (rc != old_version) {
33 windows_reset_all(context, SET_BMC_EVENT);
34 return context->protocol->get_info(context, io);
35 }
36
37 /* Record the negotiated version for the response */
38 io->resp.api_version = rc;
39
40 /* Now do all required intialisation for v1 */
41 context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
42 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
43 1 << context->block_size_shift, context->block_size_shift);
44
45 /* Knowing blocksize we can allocate the window dirty_bytemap */
46 windows_alloc_dirty_bytemap(context);
47
48 io->resp.v1.read_window_size =
49 context->windows.default_size >> context->block_size_shift;
50 io->resp.v1.write_window_size =
51 context->windows.default_size >> context->block_size_shift;
52
53 return lpc_map_memory(context);
54}
55
Andrew Jeffery91a87452018-08-07 14:54:14 +093056int protocol_v1_get_flash_info(struct mbox_context *context,
57 struct protocol_get_flash_info *io)
58{
59 io->resp.v1.flash_size = context->flash_size;
60 io->resp.v1.erase_size = context->mtd_info.erasesize;
61
62 return 0;
63}
64
Andrew Jeffery1e531af2018-08-07 13:32:57 +093065/*
Andrew Jeffery22fa5002018-08-07 15:22:50 +093066 * get_lpc_addr_shifted() - Get lpc address of the current window
67 * @context: The mbox context pointer
68 *
69 * Return: The lpc address to access that offset shifted by block size
70 */
71static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
72{
73 uint32_t lpc_addr, mem_offset;
74
75 /* Offset of the current window in the reserved memory region */
76 mem_offset = context->current->mem - context->mem;
77 /* Total LPC Address */
78 lpc_addr = context->lpc_base + mem_offset;
79
80 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
81
82 return lpc_addr >> context->block_size_shift;
83}
84
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +093085int protocol_v1_create_window(struct mbox_context *context,
86 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +093087{
88 int rc;
89 uint32_t offset = io->req.offset << context->block_size_shift;
90
91 /* Close the current window if there is one */
92 if (context->current) {
93 /* There is an implicit flush if it was a write window */
94 if (context->current_is_write) {
95 rc = mbox_handle_flush_window(context, NULL, NULL);
96 if (rc < 0) {
97 MSG_ERR("Couldn't Flush Write Window\n");
98 return rc;
99 }
100 }
101 windows_close_current(context, NO_BMC_EVENT, FLAGS_NONE);
102 }
103
104 /* Offset the host has requested */
105 MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
106 /* Check if we have an existing window */
107 context->current = windows_search(context, offset,
108 context->version == API_VERSION_1);
109
110 if (!context->current) { /* No existing window */
111 MSG_DBG("No existing window which maps that flash offset\n");
112 rc = windows_create_map(context, &context->current,
113 offset,
114 context->version == API_VERSION_1);
115 if (rc < 0) { /* Unable to map offset */
116 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930117 offset);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930118 return rc;
119 }
120 }
121
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930122 context->current_is_write = !io->req.ro;
123
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930124 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
125 context->current->mem, context->current->size,
126 context->current->flash_offset);
127
128 io->resp.lpc_address = get_lpc_addr_shifted(context);
129
130 return 0;
131}
132
Andrew Jefferya336e432018-08-07 16:00:40 +0930133int protocol_v1_mark_dirty(struct mbox_context *context,
134 struct protocol_mark_dirty *io)
135{
136 uint32_t offset = io->req.v1.offset;
137 uint32_t size = io->req.v1.size;
138 uint32_t off;
139
140 if (!(context->current && context->current_is_write)) {
141 MSG_ERR("Tried to call mark dirty without open write window\n");
142 return -EPERM;
143 }
144
145 /* For V1 offset given relative to flash - we want the window */
146 off = offset - ((context->current->flash_offset) >>
147 context->block_size_shift);
148 if (off > offset) { /* Underflow - before current window */
149 MSG_ERR("Tried to mark dirty before start of window\n");
150 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
151 offset << context->block_size_shift,
152 context->current->flash_offset);
153 return -EINVAL;
154 }
155 offset = off;
156 /*
157 * We only track dirty at the block level.
158 * For protocol V1 we can get away with just marking the whole
159 * block dirty.
160 */
161 size = align_up(size, 1 << context->block_size_shift);
162 size >>= context->block_size_shift;
163
164 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
165 offset << context->block_size_shift,
166 size << context->block_size_shift);
167
168 return window_set_bytemap(context, context->current, offset, size,
169 WINDOW_DIRTY);
170}
171
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930172static int generic_flush(struct mbox_context *context)
173{
174 int rc, i, offset, count;
175 uint8_t prev;
176
177 offset = 0;
178 count = 0;
179 prev = WINDOW_CLEAN;
180
181 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
182 context->current->mem, context->current->size,
183 context->current->flash_offset);
184
185 /*
186 * We look for streaks of the same type and keep a count, when the type
187 * (dirty/erased) changes we perform the required action on the backing
188 * store and update the current streak-type
189 */
190 for (i = 0; i < (context->current->size >> context->block_size_shift);
191 i++) {
192 uint8_t cur = context->current->dirty_bmap[i];
193 if (cur != WINDOW_CLEAN) {
194 if (cur == prev) { /* Same as previous block, incrmnt */
195 count++;
196 } else if (prev == WINDOW_CLEAN) { /* Start of run */
197 offset = i;
198 count++;
199 } else { /* Change in streak type */
200 rc = window_flush(context, offset, count,
201 prev);
202 if (rc < 0) {
203 return rc;
204 }
205 offset = i;
206 count = 1;
207 }
208 } else {
209 if (prev != WINDOW_CLEAN) { /* End of a streak */
210 rc = window_flush(context, offset, count,
211 prev);
212 if (rc < 0) {
213 return rc;
214 }
215 offset = 0;
216 count = 0;
217 }
218 }
219 prev = cur;
220 }
221
222 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
223 rc = window_flush(context, offset, count, prev);
224 if (rc < 0) {
225 return rc;
226 }
227 }
228
229 /* Clear the dirty bytemap since we have written back all changes */
230 return window_set_bytemap(context, context->current, 0,
231 context->current->size >>
232 context->block_size_shift,
233 WINDOW_CLEAN);
234}
235
236int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
237{
238 int rc;
239
240 if (!(context->current && context->current_is_write)) {
241 MSG_ERR("Tried to call flush without open write window\n");
242 return -EPERM;
243 }
244
245 /*
246 * For V1 the Flush command acts much the same as the dirty command
247 * except with a flush as well. Only do this on an actual flush
248 * command not when we call flush because we've implicitly closed a
249 * window because we might not have the required args in req.
250 */
Andrew Jeffery093eda52018-08-07 23:10:43 +0930251 if (io) {
252 struct protocol_mark_dirty *mdio = (void *)io;
253 rc = protocol_v1_mark_dirty(context, mdio);
254 if (rc < 0) {
255 return rc;
256 }
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930257 }
258
259 return generic_flush(context);
260}
261
Andrew Jeffery093eda52018-08-07 23:10:43 +0930262int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
263{
264 int rc;
265
266 /* Close the current window if there is one */
267 if (!context->current) {
268 return 0;
269 }
270
271 /* There is an implicit flush if it was a write window */
272 if (context->current_is_write) {
273 rc = protocol_v1_flush(context, NULL);
274 if (rc < 0) {
275 MSG_ERR("Couldn't Flush Write Window\n");
276 return rc;
277 }
278 }
279
280 /* Host asked for it -> Don't set the BMC Event */
281 windows_close_current(context, NO_BMC_EVENT, io->req.flags);
282
283 return 0;
284}
285
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930286/*
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930287 * get_suggested_timeout() - get the suggested timeout value in seconds
288 * @context: The mbox context pointer
289 *
290 * Return: Suggested timeout in seconds
291 */
292static uint16_t get_suggested_timeout(struct mbox_context *context)
293{
294 struct window_context *window = windows_find_largest(context);
295 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
296 uint16_t ret;
297
298 ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
299
300 MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
301 ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
302 return ret;
303}
304
305int protocol_v2_get_info(struct mbox_context *context,
306 struct protocol_get_info *io)
307{
308 uint8_t old_version = context->version;
309 int rc;
310
311 /* Bootstrap protocol version. This may involve {up,down}grading */
312 rc = protocol_negotiate_version(context, io->req.api_version);
313 if (rc < 0)
314 return rc;
315
316 /* Do the {up,down}grade if necessary*/
317 if (rc != old_version) {
318 windows_reset_all(context, SET_BMC_EVENT);
319 return context->protocol->get_info(context, io);
320 }
321
322 /* Record the negotiated version for the response */
323 io->resp.api_version = rc;
324
325 /* Now do all required intialisation for v2 */
326 context->block_size_shift = log_2(context->mtd_info.erasesize);
327 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
328 1 << context->block_size_shift, context->block_size_shift);
329
330 /* Knowing blocksize we can allocate the window dirty_bytemap */
331 windows_alloc_dirty_bytemap(context);
332
333 io->resp.v2.block_size_shift = context->block_size_shift;
334 io->resp.v2.timeout = get_suggested_timeout(context);
335
336 return lpc_map_memory(context);
337}
338
Andrew Jeffery91a87452018-08-07 14:54:14 +0930339int protocol_v2_get_flash_info(struct mbox_context *context,
340 struct protocol_get_flash_info *io)
341{
342 io->resp.v2.flash_size =
343 context->flash_size >> context->block_size_shift;
344 io->resp.v2.erase_size =
345 context->mtd_info.erasesize >> context->block_size_shift;
346
347 return 0;
348}
349
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930350int protocol_v2_create_window(struct mbox_context *context,
351 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930352{
353 int rc;
354
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930355 rc = protocol_v1_create_window(context, io);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930356 if (rc < 0)
357 return rc;
358
359 io->resp.size = context->current->size >> context->block_size_shift;
360 io->resp.offset = context->current->flash_offset >>
361 context->block_size_shift;
362
363 return 0;
364}
365
Andrew Jefferya336e432018-08-07 16:00:40 +0930366int protocol_v2_mark_dirty(struct mbox_context *context,
367 struct protocol_mark_dirty *io)
368{
369 if (!(context->current && context->current_is_write)) {
370 MSG_ERR("Tried to call mark dirty without open write window\n");
371 return -EPERM;
372 }
373
374 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
375 io->req.v2.offset << context->block_size_shift,
376 io->req.v2.size << context->block_size_shift);
377
378 return window_set_bytemap(context, context->current, io->req.v2.offset,
379 io->req.v2.size, WINDOW_DIRTY);
380}
381
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930382int protocol_v2_erase(struct mbox_context *context,
383 struct protocol_erase *io)
384{
385 size_t start, len;
386 int rc;
387
388 if (!(context->current && context->current_is_write)) {
389 MSG_ERR("Tried to call erase without open write window\n");
390 return -EPERM;
391 }
392
393 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
394 io->req.offset << context->block_size_shift,
395 io->req.size << context->block_size_shift);
396
397 rc = window_set_bytemap(context, context->current, io->req.offset,
398 io->req.size, WINDOW_ERASED);
399 if (rc < 0) {
400 return rc;
401 }
402
403 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
404 start = io->req.offset << context->block_size_shift;
405 len = io->req.size << context->block_size_shift;
406 memset(context->current->mem + start, 0xFF, len);
407
408 return 0;
409}
410
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930411int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
412{
413 if (!(context->current && context->current_is_write)) {
414 MSG_ERR("Tried to call flush without open write window\n");
415 return -EPERM;
416 }
417
418 return generic_flush(context);
419}
420
Andrew Jeffery093eda52018-08-07 23:10:43 +0930421int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
422{
423 int rc;
424
425 /* Close the current window if there is one */
426 if (!context->current) {
427 return 0;
428 }
429
430 /* There is an implicit flush if it was a write window */
431 if (context->current_is_write) {
432 rc = protocol_v2_flush(context, NULL);
433 if (rc < 0) {
434 MSG_ERR("Couldn't Flush Write Window\n");
435 return rc;
436 }
437 }
438
439 /* Host asked for it -> Don't set the BMC Event */
440 windows_close_current(context, NO_BMC_EVENT, io->req.flags);
441
442 return 0;
443}
444
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930445static const struct protocol_ops protocol_ops_v1 = {
Andrew Jefferyab666a52018-08-07 14:28:09 +0930446 .reset = protocol_v1_reset,
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930447 .get_info = protocol_v1_get_info,
Andrew Jeffery91a87452018-08-07 14:54:14 +0930448 .get_flash_info = protocol_v1_get_flash_info,
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930449 .create_window = protocol_v1_create_window,
Andrew Jefferya336e432018-08-07 16:00:40 +0930450 .mark_dirty = protocol_v1_mark_dirty,
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930451 .erase = NULL,
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930452 .flush = protocol_v1_flush,
Andrew Jeffery093eda52018-08-07 23:10:43 +0930453 .close = protocol_v1_close,
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930454};
455
456static const struct protocol_ops protocol_ops_v2 = {
Andrew Jefferyab666a52018-08-07 14:28:09 +0930457 .reset = protocol_v1_reset,
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930458 .get_info = protocol_v2_get_info,
Andrew Jeffery91a87452018-08-07 14:54:14 +0930459 .get_flash_info = protocol_v2_get_flash_info,
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930460 .create_window = protocol_v2_create_window,
Andrew Jefferya336e432018-08-07 16:00:40 +0930461 .mark_dirty = protocol_v2_mark_dirty,
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930462 .erase = protocol_v2_erase,
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930463 .flush = protocol_v2_flush,
Andrew Jeffery093eda52018-08-07 23:10:43 +0930464 .close = protocol_v2_close,
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930465};
466
467static const struct protocol_ops *protocol_ops_map[] = {
468 [0] = NULL,
469 [1] = &protocol_ops_v1,
470 [2] = &protocol_ops_v2,
471};
472
473int protocol_negotiate_version(struct mbox_context *context,
474 uint8_t requested)
475{
476 /* Check we support the version requested */
477 if (requested < API_MIN_VERSION)
478 return -EINVAL;
479
480 context->version = (requested > API_MAX_VERSION) ?
481 API_MAX_VERSION : requested;
482
483 context->protocol = protocol_ops_map[context->version];
484
485 return context->version;
486}
487
488int protocol_init(struct mbox_context *context)
489{
490 context->version = API_MAX_VERSION;
491 context->protocol = protocol_ops_map[context->version];
492
493 return 0;
494}
495
496void protocol_free(struct mbox_context *context)
497{
498 return;
499}