blob: 4160e6edd11d5e8ccef1899fc9295c1ff6de4aef [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 Jefferyc48d0702018-08-09 14:04:27 +09308#include "flash.h"
Andrew Jeffery1e531af2018-08-07 13:32:57 +09309#include "mbox.h"
10#include "lpc.h"
11#include "transport_mbox.h" /* TODO: Remove dependency on transport_mbox.h */
12#include "windows.h"
13
Andrew Jefferyab666a52018-08-07 14:28:09 +093014int protocol_v1_reset(struct mbox_context *context)
15{
16 /* Host requested it -> No BMC Event */
17 windows_reset_all(context, NO_BMC_EVENT);
18 return lpc_reset(context);
19}
20
Andrew Jeffery1e531af2018-08-07 13:32:57 +093021int protocol_v1_get_info(struct mbox_context *context,
22 struct protocol_get_info *io)
23{
24 uint8_t old_version = context->version;
25 int rc;
26
27 /* Bootstrap protocol version. This may involve {up,down}grading */
28 rc = protocol_negotiate_version(context, io->req.api_version);
29 if (rc < 0)
30 return rc;
31
32 /* Do the {up,down}grade if necessary*/
33 if (rc != old_version) {
34 windows_reset_all(context, SET_BMC_EVENT);
35 return context->protocol->get_info(context, io);
36 }
37
38 /* Record the negotiated version for the response */
39 io->resp.api_version = rc;
40
41 /* Now do all required intialisation for v1 */
42 context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
43 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
44 1 << context->block_size_shift, context->block_size_shift);
45
46 /* Knowing blocksize we can allocate the window dirty_bytemap */
47 windows_alloc_dirty_bytemap(context);
48
49 io->resp.v1.read_window_size =
50 context->windows.default_size >> context->block_size_shift;
51 io->resp.v1.write_window_size =
52 context->windows.default_size >> context->block_size_shift;
53
54 return lpc_map_memory(context);
55}
56
Andrew Jeffery91a87452018-08-07 14:54:14 +093057int protocol_v1_get_flash_info(struct mbox_context *context,
58 struct protocol_get_flash_info *io)
59{
60 io->resp.v1.flash_size = context->flash_size;
61 io->resp.v1.erase_size = context->mtd_info.erasesize;
62
63 return 0;
64}
65
Andrew Jeffery1e531af2018-08-07 13:32:57 +093066/*
Andrew Jeffery22fa5002018-08-07 15:22:50 +093067 * get_lpc_addr_shifted() - Get lpc address of the current window
68 * @context: The mbox context pointer
69 *
70 * Return: The lpc address to access that offset shifted by block size
71 */
72static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
73{
74 uint32_t lpc_addr, mem_offset;
75
76 /* Offset of the current window in the reserved memory region */
77 mem_offset = context->current->mem - context->mem;
78 /* Total LPC Address */
79 lpc_addr = context->lpc_base + mem_offset;
80
81 MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
82
83 return lpc_addr >> context->block_size_shift;
84}
85
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +093086int protocol_v1_create_window(struct mbox_context *context,
87 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +093088{
89 int rc;
90 uint32_t offset = io->req.offset << context->block_size_shift;
91
92 /* Close the current window if there is one */
93 if (context->current) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +093094 /* There is an implicit flush if it was a write window
95 *
96 * protocol_v2_create_window() calls
97 * protocol_v1_create_window(), so use indirect call to
98 * write_flush() to make sure we pick the right one.
99 */
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930100 if (context->current_is_write) {
Andrew Jefferyf21c81c2018-08-09 13:57:46 +0930101 rc = context->protocol->flush(context, NULL);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930102 if (rc < 0) {
103 MSG_ERR("Couldn't Flush Write Window\n");
104 return rc;
105 }
106 }
107 windows_close_current(context, NO_BMC_EVENT, FLAGS_NONE);
108 }
109
110 /* Offset the host has requested */
111 MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
112 /* Check if we have an existing window */
113 context->current = windows_search(context, offset,
114 context->version == API_VERSION_1);
115
116 if (!context->current) { /* No existing window */
117 MSG_DBG("No existing window which maps that flash offset\n");
118 rc = windows_create_map(context, &context->current,
119 offset,
120 context->version == API_VERSION_1);
121 if (rc < 0) { /* Unable to map offset */
122 MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930123 offset);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930124 return rc;
125 }
126 }
127
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930128 context->current_is_write = !io->req.ro;
129
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930130 MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
131 context->current->mem, context->current->size,
132 context->current->flash_offset);
133
134 io->resp.lpc_address = get_lpc_addr_shifted(context);
135
136 return 0;
137}
138
Andrew Jefferya336e432018-08-07 16:00:40 +0930139int protocol_v1_mark_dirty(struct mbox_context *context,
140 struct protocol_mark_dirty *io)
141{
142 uint32_t offset = io->req.v1.offset;
143 uint32_t size = io->req.v1.size;
144 uint32_t off;
145
146 if (!(context->current && context->current_is_write)) {
147 MSG_ERR("Tried to call mark dirty without open write window\n");
148 return -EPERM;
149 }
150
151 /* For V1 offset given relative to flash - we want the window */
152 off = offset - ((context->current->flash_offset) >>
153 context->block_size_shift);
154 if (off > offset) { /* Underflow - before current window */
155 MSG_ERR("Tried to mark dirty before start of window\n");
156 MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
157 offset << context->block_size_shift,
158 context->current->flash_offset);
159 return -EINVAL;
160 }
161 offset = off;
162 /*
163 * We only track dirty at the block level.
164 * For protocol V1 we can get away with just marking the whole
165 * block dirty.
166 */
167 size = align_up(size, 1 << context->block_size_shift);
168 size >>= context->block_size_shift;
169
170 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
171 offset << context->block_size_shift,
172 size << context->block_size_shift);
173
174 return window_set_bytemap(context, context->current, offset, size,
175 WINDOW_DIRTY);
176}
177
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930178static int generic_flush(struct mbox_context *context)
179{
180 int rc, i, offset, count;
181 uint8_t prev;
182
183 offset = 0;
184 count = 0;
185 prev = WINDOW_CLEAN;
186
187 MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
188 context->current->mem, context->current->size,
189 context->current->flash_offset);
190
191 /*
192 * We look for streaks of the same type and keep a count, when the type
193 * (dirty/erased) changes we perform the required action on the backing
194 * store and update the current streak-type
195 */
196 for (i = 0; i < (context->current->size >> context->block_size_shift);
197 i++) {
198 uint8_t cur = context->current->dirty_bmap[i];
199 if (cur != WINDOW_CLEAN) {
200 if (cur == prev) { /* Same as previous block, incrmnt */
201 count++;
202 } else if (prev == WINDOW_CLEAN) { /* Start of run */
203 offset = i;
204 count++;
205 } else { /* Change in streak type */
206 rc = window_flush(context, offset, count,
207 prev);
208 if (rc < 0) {
209 return rc;
210 }
211 offset = i;
212 count = 1;
213 }
214 } else {
215 if (prev != WINDOW_CLEAN) { /* End of a streak */
216 rc = window_flush(context, offset, count,
217 prev);
218 if (rc < 0) {
219 return rc;
220 }
221 offset = 0;
222 count = 0;
223 }
224 }
225 prev = cur;
226 }
227
228 if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
229 rc = window_flush(context, offset, count, prev);
230 if (rc < 0) {
231 return rc;
232 }
233 }
234
235 /* Clear the dirty bytemap since we have written back all changes */
236 return window_set_bytemap(context, context->current, 0,
237 context->current->size >>
238 context->block_size_shift,
239 WINDOW_CLEAN);
240}
241
242int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
243{
244 int rc;
245
246 if (!(context->current && context->current_is_write)) {
247 MSG_ERR("Tried to call flush without open write window\n");
248 return -EPERM;
249 }
250
251 /*
252 * For V1 the Flush command acts much the same as the dirty command
253 * except with a flush as well. Only do this on an actual flush
254 * command not when we call flush because we've implicitly closed a
255 * window because we might not have the required args in req.
256 */
Andrew Jeffery093eda52018-08-07 23:10:43 +0930257 if (io) {
258 struct protocol_mark_dirty *mdio = (void *)io;
259 rc = protocol_v1_mark_dirty(context, mdio);
260 if (rc < 0) {
261 return rc;
262 }
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930263 }
264
265 return generic_flush(context);
266}
267
Andrew Jeffery093eda52018-08-07 23:10:43 +0930268int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
269{
270 int rc;
271
272 /* Close the current window if there is one */
273 if (!context->current) {
274 return 0;
275 }
276
277 /* There is an implicit flush if it was a write window */
278 if (context->current_is_write) {
279 rc = protocol_v1_flush(context, NULL);
280 if (rc < 0) {
281 MSG_ERR("Couldn't Flush Write Window\n");
282 return rc;
283 }
284 }
285
286 /* Host asked for it -> Don't set the BMC Event */
287 windows_close_current(context, NO_BMC_EVENT, io->req.flags);
288
289 return 0;
290}
291
Andrew Jefferyc5c83042018-08-07 23:22:05 +0930292int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
293{
294 return clr_bmc_events(context, (io->req.flags & BMC_EVENT_ACK_MASK),
295 SET_BMC_EVENT);
296}
297
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930298/*
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930299 * get_suggested_timeout() - get the suggested timeout value in seconds
300 * @context: The mbox context pointer
301 *
302 * Return: Suggested timeout in seconds
303 */
304static uint16_t get_suggested_timeout(struct mbox_context *context)
305{
306 struct window_context *window = windows_find_largest(context);
307 uint32_t max_size_mb = window ? (window->size >> 20) : 0;
308 uint16_t ret;
309
310 ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
311
312 MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
313 ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
314 return ret;
315}
316
317int protocol_v2_get_info(struct mbox_context *context,
318 struct protocol_get_info *io)
319{
320 uint8_t old_version = context->version;
321 int rc;
322
323 /* Bootstrap protocol version. This may involve {up,down}grading */
324 rc = protocol_negotiate_version(context, io->req.api_version);
325 if (rc < 0)
326 return rc;
327
328 /* Do the {up,down}grade if necessary*/
329 if (rc != old_version) {
330 windows_reset_all(context, SET_BMC_EVENT);
331 return context->protocol->get_info(context, io);
332 }
333
334 /* Record the negotiated version for the response */
335 io->resp.api_version = rc;
336
337 /* Now do all required intialisation for v2 */
338 context->block_size_shift = log_2(context->mtd_info.erasesize);
339 MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
340 1 << context->block_size_shift, context->block_size_shift);
341
342 /* Knowing blocksize we can allocate the window dirty_bytemap */
343 windows_alloc_dirty_bytemap(context);
344
345 io->resp.v2.block_size_shift = context->block_size_shift;
346 io->resp.v2.timeout = get_suggested_timeout(context);
347
348 return lpc_map_memory(context);
349}
350
Andrew Jeffery91a87452018-08-07 14:54:14 +0930351int protocol_v2_get_flash_info(struct mbox_context *context,
352 struct protocol_get_flash_info *io)
353{
354 io->resp.v2.flash_size =
355 context->flash_size >> context->block_size_shift;
356 io->resp.v2.erase_size =
357 context->mtd_info.erasesize >> context->block_size_shift;
358
359 return 0;
360}
361
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930362int protocol_v2_create_window(struct mbox_context *context,
363 struct protocol_create_window *io)
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930364{
365 int rc;
366
Andrew Jeffery4bcec8e2018-08-07 15:33:41 +0930367 rc = protocol_v1_create_window(context, io);
Andrew Jeffery22fa5002018-08-07 15:22:50 +0930368 if (rc < 0)
369 return rc;
370
371 io->resp.size = context->current->size >> context->block_size_shift;
372 io->resp.offset = context->current->flash_offset >>
373 context->block_size_shift;
374
375 return 0;
376}
377
Andrew Jefferya336e432018-08-07 16:00:40 +0930378int protocol_v2_mark_dirty(struct mbox_context *context,
379 struct protocol_mark_dirty *io)
380{
381 if (!(context->current && context->current_is_write)) {
382 MSG_ERR("Tried to call mark dirty without open write window\n");
383 return -EPERM;
384 }
385
386 MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
387 io->req.v2.offset << context->block_size_shift,
388 io->req.v2.size << context->block_size_shift);
389
390 return window_set_bytemap(context, context->current, io->req.v2.offset,
391 io->req.v2.size, WINDOW_DIRTY);
392}
393
Andrew Jeffery62a3daa2018-08-07 22:30:32 +0930394int protocol_v2_erase(struct mbox_context *context,
395 struct protocol_erase *io)
396{
397 size_t start, len;
398 int rc;
399
400 if (!(context->current && context->current_is_write)) {
401 MSG_ERR("Tried to call erase without open write window\n");
402 return -EPERM;
403 }
404
405 MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
406 io->req.offset << context->block_size_shift,
407 io->req.size << context->block_size_shift);
408
409 rc = window_set_bytemap(context, context->current, io->req.offset,
410 io->req.size, WINDOW_ERASED);
411 if (rc < 0) {
412 return rc;
413 }
414
415 /* Write 0xFF to mem -> This ensures consistency between flash & ram */
416 start = io->req.offset << context->block_size_shift;
417 len = io->req.size << context->block_size_shift;
418 memset(context->current->mem + start, 0xFF, len);
419
420 return 0;
421}
422
Andrew Jeffery9b920cf2018-08-07 22:49:19 +0930423int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
424{
425 if (!(context->current && context->current_is_write)) {
426 MSG_ERR("Tried to call flush without open write window\n");
427 return -EPERM;
428 }
429
430 return generic_flush(context);
431}
432
Andrew Jeffery093eda52018-08-07 23:10:43 +0930433int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
434{
435 int rc;
436
437 /* Close the current window if there is one */
438 if (!context->current) {
439 return 0;
440 }
441
442 /* There is an implicit flush if it was a write window */
443 if (context->current_is_write) {
444 rc = protocol_v2_flush(context, NULL);
445 if (rc < 0) {
446 MSG_ERR("Couldn't Flush Write Window\n");
447 return rc;
448 }
449 }
450
451 /* Host asked for it -> Don't set the BMC Event */
452 windows_close_current(context, NO_BMC_EVENT, io->req.flags);
453
454 return 0;
455}
456
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930457int protocol_init(struct mbox_context *context)
458{
Andrew Jefferyc7d19472018-08-08 11:43:08 +0930459 protocol_negotiate_version(context, API_MAX_VERSION);
Andrew Jeffery1e531af2018-08-07 13:32:57 +0930460
461 return 0;
462}
463
464void protocol_free(struct mbox_context *context)
465{
466 return;
467}