blob: c66f2dfdce1f5142f1c54c28fcfd42f3aa2ab108 [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) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +093093 /* There is an implicit flush if it was a write window
94 *
95 * protocol_v2_create_window() calls
96 * protocol_v1_create_window(), so use indirect call to
97 * write_flush() to make sure we pick the right one.
98 */
Andrew Jeffery22fa5002018-08-07 15:22:50 +093099 if (context->current_is_write) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930100 rc = context->protocol->flush(context, NULL);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930101 if (rc < 0) {
102 MSG_ERR("Couldn't Flush Write Window\n");
103 return rc;
104 }
105 }
106 windows_close_current(context, NO_BMC_EVENT, FLAGS_NONE);
107 }
108
109 /* Offset the host has requested */
110 MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
111 /* Check if we have an existing window */
112 context->current = windows_search(context, offset,
113 context->version == API_VERSION_1);
114
115 if (!context->current) { /* No existing window */
116 MSG_DBG("No existing window which maps that flash offset\n");
117 rc = windows_create_map(context, &context->current,
118 offset,
119 context->version == API_VERSION_1);
120 if (rc < 0) { /* Unable to map offset */
121 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930122 offset);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930123 return rc;
124 }
125 }
126
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930127 context->current_is_write = !io->req.ro;
128
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930129 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
130 context->current->mem, context->current->size,
131 context->current->flash_offset);
132
133 io->resp.lpc_address = get_lpc_addr_shifted(context);
134
135 return 0;
136}
137
Andrew Jefferya336e432018-08-07 16:00:40 +0930138int protocol_v1_mark_dirty(struct mbox_context *context,
139 struct protocol_mark_dirty *io)
140{
141 uint32_t offset = io->req.v1.offset;
142 uint32_t size = io->req.v1.size;
143 uint32_t off;
144
145 if (!(context->current && context->current_is_write)) {
146 MSG_ERR("Tried to call mark dirty without open write window\n");
147 return -EPERM;
148 }
149
150 /* For V1 offset given relative to flash - we want the window */
151 off = offset - ((context->current->flash_offset) >>
152 context->block_size_shift);
153 if (off > offset) { /* Underflow - before current window */
154 MSG_ERR("Tried to mark dirty before start of window\n");
155 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
156 offset << context->block_size_shift,
157 context->current->flash_offset);
158 return -EINVAL;
159 }
160 offset = off;
161 /*
162 * We only track dirty at the block level.
163 * For protocol V1 we can get away with just marking the whole
164 * block dirty.
165 */
166 size = align_up(size, 1 << context->block_size_shift);
167 size >>= context->block_size_shift;
168
169 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
170 offset << context->block_size_shift,
171 size << context->block_size_shift);
172
173 return window_set_bytemap(context, context->current, offset, size,
174 WINDOW_DIRTY);
175}
176
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930177static int generic_flush(struct mbox_context *context)
178{
179 int rc, i, offset, count;
180 uint8_t prev;
181
182 offset = 0;
183 count = 0;
184 prev = WINDOW_CLEAN;
185
186 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
187 context->current->mem, context->current->size,
188 context->current->flash_offset);
189
190 /*
191 * We look for streaks of the same type and keep a count, when the type
192 * (dirty/erased) changes we perform the required action on the backing
193 * store and update the current streak-type
194 */
195 for (i = 0; i < (context->current->size >> context->block_size_shift);
196 i++) {
197 uint8_t cur = context->current->dirty_bmap[i];
198 if (cur != WINDOW_CLEAN) {
199 if (cur == prev) { /* Same as previous block, incrmnt */
200 count++;
201 } else if (prev == WINDOW_CLEAN) { /* Start of run */
202 offset = i;
203 count++;
204 } else { /* Change in streak type */
205 rc = window_flush(context, offset, count,
206 prev);
207 if (rc < 0) {
208 return rc;
209 }
210 offset = i;
211 count = 1;
212 }
213 } else {
214 if (prev != WINDOW_CLEAN) { /* End of a streak */
215 rc = window_flush(context, offset, count,
216 prev);
217 if (rc < 0) {
218 return rc;
219 }
220 offset = 0;
221 count = 0;
222 }
223 }
224 prev = cur;
225 }
226
227 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
228 rc = window_flush(context, offset, count, prev);
229 if (rc < 0) {
230 return rc;
231 }
232 }
233
234 /* Clear the dirty bytemap since we have written back all changes */
235 return window_set_bytemap(context, context->current, 0,
236 context->current->size >>
237 context->block_size_shift,
238 WINDOW_CLEAN);
239}
240
241int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
242{
243 int rc;
244
245 if (!(context->current && context->current_is_write)) {
246 MSG_ERR("Tried to call flush without open write window\n");
247 return -EPERM;
248 }
249
250 /*
251 * For V1 the Flush command acts much the same as the dirty command
252 * except with a flush as well. Only do this on an actual flush
253 * command not when we call flush because we've implicitly closed a
254 * window because we might not have the required args in req.
255 */
Andrew Jeffery093eda52018-08-07 23:10:43 +0930256 if (io) {
257 struct protocol_mark_dirty *mdio = (void *)io;
258 rc = protocol_v1_mark_dirty(context, mdio);
259 if (rc < 0) {
260 return rc;
261 }
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930262 }
263
264 return generic_flush(context);
265}
266
Andrew Jeffery093eda52018-08-07 23:10:43 +0930267int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
268{
269 int rc;
270
271 /* Close the current window if there is one */
272 if (!context->current) {
273 return 0;
274 }
275
276 /* There is an implicit flush if it was a write window */
277 if (context->current_is_write) {
278 rc = protocol_v1_flush(context, NULL);
279 if (rc < 0) {
280 MSG_ERR("Couldn't Flush Write Window\n");
281 return rc;
282 }
283 }
284
285 /* Host asked for it -> Don't set the BMC Event */
286 windows_close_current(context, NO_BMC_EVENT, io->req.flags);
287
288 return 0;
289}
290
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930291int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
292{
293 return clr_bmc_events(context, (io->req.flags & BMC_EVENT_ACK_MASK),
294 SET_BMC_EVENT);
295}
296
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930297/*
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930298 * get_suggested_timeout() - get the suggested timeout value in seconds
299 * @context: The mbox context pointer
300 *
301 * Return: Suggested timeout in seconds
302 */
303static uint16_t get_suggested_timeout(struct mbox_context *context)
304{
305 struct window_context *window = windows_find_largest(context);
306 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
307 uint16_t ret;
308
309 ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
310
311 MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
312 ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
313 return ret;
314}
315
316int protocol_v2_get_info(struct mbox_context *context,
317 struct protocol_get_info *io)
318{
319 uint8_t old_version = context->version;
320 int rc;
321
322 /* Bootstrap protocol version. This may involve {up,down}grading */
323 rc = protocol_negotiate_version(context, io->req.api_version);
324 if (rc < 0)
325 return rc;
326
327 /* Do the {up,down}grade if necessary*/
328 if (rc != old_version) {
329 windows_reset_all(context, SET_BMC_EVENT);
330 return context->protocol->get_info(context, io);
331 }
332
333 /* Record the negotiated version for the response */
334 io->resp.api_version = rc;
335
336 /* Now do all required intialisation for v2 */
337 context->block_size_shift = log_2(context->mtd_info.erasesize);
338 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
339 1 << context->block_size_shift, context->block_size_shift);
340
341 /* Knowing blocksize we can allocate the window dirty_bytemap */
342 windows_alloc_dirty_bytemap(context);
343
344 io->resp.v2.block_size_shift = context->block_size_shift;
345 io->resp.v2.timeout = get_suggested_timeout(context);
346
347 return lpc_map_memory(context);
348}
349
Andrew Jeffery91a87452018-08-07 14:54:14 +0930350int protocol_v2_get_flash_info(struct mbox_context *context,
351 struct protocol_get_flash_info *io)
352{
353 io->resp.v2.flash_size =
354 context->flash_size >> context->block_size_shift;
355 io->resp.v2.erase_size =
356 context->mtd_info.erasesize >> context->block_size_shift;
357
358 return 0;
359}
360
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930361int protocol_v2_create_window(struct mbox_context *context,
362 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930363{
364 int rc;
365
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930366 rc = protocol_v1_create_window(context, io);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930367 if (rc < 0)
368 return rc;
369
370 io->resp.size = context->current->size >> context->block_size_shift;
371 io->resp.offset = context->current->flash_offset >>
372 context->block_size_shift;
373
374 return 0;
375}
376
Andrew Jefferya336e432018-08-07 16:00:40 +0930377int protocol_v2_mark_dirty(struct mbox_context *context,
378 struct protocol_mark_dirty *io)
379{
380 if (!(context->current && context->current_is_write)) {
381 MSG_ERR("Tried to call mark dirty without open write window\n");
382 return -EPERM;
383 }
384
385 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
386 io->req.v2.offset << context->block_size_shift,
387 io->req.v2.size << context->block_size_shift);
388
389 return window_set_bytemap(context, context->current, io->req.v2.offset,
390 io->req.v2.size, WINDOW_DIRTY);
391}
392
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930393int protocol_v2_erase(struct mbox_context *context,
394 struct protocol_erase *io)
395{
396 size_t start, len;
397 int rc;
398
399 if (!(context->current && context->current_is_write)) {
400 MSG_ERR("Tried to call erase without open write window\n");
401 return -EPERM;
402 }
403
404 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
405 io->req.offset << context->block_size_shift,
406 io->req.size << context->block_size_shift);
407
408 rc = window_set_bytemap(context, context->current, io->req.offset,
409 io->req.size, WINDOW_ERASED);
410 if (rc < 0) {
411 return rc;
412 }
413
414 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
415 start = io->req.offset << context->block_size_shift;
416 len = io->req.size << context->block_size_shift;
417 memset(context->current->mem + start, 0xFF, len);
418
419 return 0;
420}
421
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930422int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
423{
424 if (!(context->current && context->current_is_write)) {
425 MSG_ERR("Tried to call flush without open write window\n");
426 return -EPERM;
427 }
428
429 return generic_flush(context);
430}
431
Andrew Jeffery093eda52018-08-07 23:10:43 +0930432int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
433{
434 int rc;
435
436 /* Close the current window if there is one */
437 if (!context->current) {
438 return 0;
439 }
440
441 /* There is an implicit flush if it was a write window */
442 if (context->current_is_write) {
443 rc = protocol_v2_flush(context, NULL);
444 if (rc < 0) {
445 MSG_ERR("Couldn't Flush Write Window\n");
446 return rc;
447 }
448 }
449
450 /* Host asked for it -> Don't set the BMC Event */
451 windows_close_current(context, NO_BMC_EVENT, io->req.flags);
452
453 return 0;
454}
455
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930456int protocol_init(struct mbox_context *context)
457{
Andrew Jefferyc7d19472018-08-08 11:43:08 +0930458 protocol_negotiate_version(context, API_MAX_VERSION);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930459
460 return 0;
461}
462
463void protocol_free(struct mbox_context *context)
464{
465 return;
466}