xref: /openbmc/hiomapd/protocol.c (revision fd4fa34d)
1 // 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 "common.h"
9 #include "flash.h"
10 #include "mboxd.h"
11 #include "lpc.h"
12 #include "windows.h"
13 
14 #define BLOCK_SIZE_SHIFT_V1		12 /* 4K */
15 
16 static inline uint8_t protocol_get_bmc_event_mask(struct mbox_context *context)
17 {
18 	if (context->version == API_VERSION_1) {
19 		return BMC_EVENT_V1_MASK;
20 	}
21 
22 	return BMC_EVENT_V2_MASK;
23 }
24 
25 /*
26  * protocol_events_put() - Push the full set/cleared state of BMC events on the
27  * 			   provided transport
28  * @context:    The mbox context pointer
29  * @ops:	The operations struct for the transport of interest
30  *
31  * Return:      0 on success otherwise negative error code
32  */
33 int protocol_events_put(struct mbox_context *context,
34 			const struct transport_ops *ops)
35 {
36 	const uint8_t mask = protocol_get_bmc_event_mask(context);
37 
38 	return ops->put_events(context, mask);
39 }
40 
41 /*
42  * protocol_events_set() - Update the set BMC events on the active transport
43  * @context:	The mbox context pointer
44  * @bmc_event:	The bits to set
45  *
46  * Return:	0 on success otherwise negative error code
47  */
48 int protocol_events_set(struct mbox_context *context, uint8_t bmc_event)
49 {
50 	const uint8_t mask = protocol_get_bmc_event_mask(context);
51 
52 	/*
53 	 * Store the raw value, as we may up- or down- grade the protocol
54 	 * version and subsequently need to flush the appropriate set. Instead
55 	 * we pass the masked value through to the transport
56 	 */
57 	context->bmc_events |= bmc_event;
58 
59 	return context->transport->set_events(context, bmc_event, mask);
60 }
61 
62 /*
63  * protocol_events_clear() - Update the cleared BMC events on the active
64  *                           transport
65  * @context:	The mbox context pointer
66  * @bmc_event:	The bits to clear
67  *
68  * Return:	0 on success otherwise negative error code
69  */
70 int protocol_events_clear(struct mbox_context *context, uint8_t bmc_event)
71 {
72 	const uint8_t mask = protocol_get_bmc_event_mask(context);
73 
74 	context->bmc_events &= ~bmc_event;
75 
76 	return context->transport->clear_events(context, bmc_event, mask);
77 }
78 
79 int protocol_v1_reset(struct mbox_context *context)
80 {
81 	/* Host requested it -> No BMC Event */
82 	windows_reset_all(context);
83 	return lpc_reset(context);
84 }
85 
86 int protocol_v1_get_info(struct mbox_context *context,
87 			 struct protocol_get_info *io)
88 {
89 	uint8_t old_version = context->version;
90 	int rc;
91 
92 	/* Bootstrap protocol version. This may involve {up,down}grading */
93 	rc = protocol_negotiate_version(context, io->req.api_version);
94 	if (rc < 0)
95 		return rc;
96 
97 	/* Do the {up,down}grade if necessary*/
98 	if (rc != old_version) {
99 		/* Doing version negotiation, don't alert host to reset */
100 		windows_reset_all(context);
101 		return context->protocol->get_info(context, io);
102 	}
103 
104 	/* Record the negotiated version for the response */
105 	io->resp.api_version = rc;
106 
107 	/* Now do all required intialisation for v1 */
108 	context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
109 	MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
110 		 1 << context->block_size_shift, context->block_size_shift);
111 
112 	/* Knowing blocksize we can allocate the window dirty_bytemap */
113 	windows_alloc_dirty_bytemap(context);
114 
115 	io->resp.v1.read_window_size =
116 		context->windows.default_size >> context->block_size_shift;
117 	io->resp.v1.write_window_size =
118 		context->windows.default_size >> context->block_size_shift;
119 
120 	return lpc_map_memory(context);
121 }
122 
123 int protocol_v1_get_flash_info(struct mbox_context *context,
124 			       struct protocol_get_flash_info *io)
125 {
126 	io->resp.v1.flash_size = context->flash_size;
127 	io->resp.v1.erase_size = context->mtd_info.erasesize;
128 
129 	return 0;
130 }
131 
132 /*
133  * get_lpc_addr_shifted() - Get lpc address of the current window
134  * @context:		The mbox context pointer
135  *
136  * Return:	The lpc address to access that offset shifted by block size
137  */
138 static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
139 {
140 	uint32_t lpc_addr, mem_offset;
141 
142 	/* Offset of the current window in the reserved memory region */
143 	mem_offset = context->current->mem - context->mem;
144 	/* Total LPC Address */
145 	lpc_addr = context->lpc_base + mem_offset;
146 
147 	MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
148 
149 	return lpc_addr >> context->block_size_shift;
150 }
151 
152 int protocol_v1_create_window(struct mbox_context *context,
153 			      struct protocol_create_window *io)
154 {
155 	int rc;
156 	uint32_t offset = io->req.offset << context->block_size_shift;
157 
158 	/* Close the current window if there is one */
159 	if (context->current) {
160 		/* There is an implicit flush if it was a write window
161 		 *
162 		 * protocol_v2_create_window() calls
163 		 * protocol_v1_create_window(), so use indirect call to
164 		 * write_flush() to make sure we pick the right one.
165 		 */
166 		if (context->current_is_write) {
167 			rc = context->protocol->flush(context, NULL);
168 			if (rc < 0) {
169 				MSG_ERR("Couldn't Flush Write Window\n");
170 				return rc;
171 			}
172 		}
173 		windows_close_current(context, FLAGS_NONE);
174 	}
175 
176 	/* Offset the host has requested */
177 	MSG_INFO("Host requested flash @ 0x%.8x\n", offset);
178 	/* Check if we have an existing window */
179 	context->current = windows_search(context, offset,
180 					  context->version == API_VERSION_1);
181 
182 	if (!context->current) { /* No existing window */
183 		MSG_DBG("No existing window which maps that flash offset\n");
184 		rc = windows_create_map(context, &context->current,
185 				       offset,
186 				       context->version == API_VERSION_1);
187 		if (rc < 0) { /* Unable to map offset */
188 			MSG_ERR("Couldn't create window mapping for offset 0x%.8x\n",
189 				offset);
190 			return rc;
191 		}
192 	}
193 
194 	context->current_is_write = !io->req.ro;
195 
196 	MSG_INFO("Window @ %p for size 0x%.8x maps flash offset 0x%.8x\n",
197 		 context->current->mem, context->current->size,
198 		 context->current->flash_offset);
199 
200 	io->resp.lpc_address = get_lpc_addr_shifted(context);
201 
202 	return 0;
203 }
204 
205 int protocol_v1_mark_dirty(struct mbox_context *context,
206 			   struct protocol_mark_dirty *io)
207 {
208 	uint32_t offset = io->req.v1.offset;
209 	uint32_t size = io->req.v1.size;
210 	uint32_t off;
211 
212 	if (!(context->current && context->current_is_write)) {
213 		MSG_ERR("Tried to call mark dirty without open write window\n");
214 		return -EPERM;
215 	}
216 
217 	/* For V1 offset given relative to flash - we want the window */
218 	off = offset - ((context->current->flash_offset) >>
219 			context->block_size_shift);
220 	if (off > offset) { /* Underflow - before current window */
221 		MSG_ERR("Tried to mark dirty before start of window\n");
222 		MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
223 				offset << context->block_size_shift,
224 				context->current->flash_offset);
225 		return -EINVAL;
226 	}
227 	offset = off;
228 	/*
229 	 * We only track dirty at the block level.
230 	 * For protocol V1 we can get away with just marking the whole
231 	 * block dirty.
232 	 */
233 	size = align_up(size, 1 << context->block_size_shift);
234 	size >>= context->block_size_shift;
235 
236 	MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
237 		 offset << context->block_size_shift,
238 		 size << context->block_size_shift);
239 
240 	return window_set_bytemap(context, context->current, offset, size,
241 				  WINDOW_DIRTY);
242 }
243 
244 static int generic_flush(struct mbox_context *context)
245 {
246 	int rc, i, offset, count;
247 	uint8_t prev;
248 
249 	offset = 0;
250 	count = 0;
251 	prev = WINDOW_CLEAN;
252 
253 	MSG_INFO("Flush window @ %p for size 0x%.8x which maps flash @ 0x%.8x\n",
254 		 context->current->mem, context->current->size,
255 		 context->current->flash_offset);
256 
257 	/*
258 	 * We look for streaks of the same type and keep a count, when the type
259 	 * (dirty/erased) changes we perform the required action on the backing
260 	 * store and update the current streak-type
261 	 */
262 	for (i = 0; i < (context->current->size >> context->block_size_shift);
263 			i++) {
264 		uint8_t cur = context->current->dirty_bmap[i];
265 		if (cur != WINDOW_CLEAN) {
266 			if (cur == prev) { /* Same as previous block, incrmnt */
267 				count++;
268 			} else if (prev == WINDOW_CLEAN) { /* Start of run */
269 				offset = i;
270 				count++;
271 			} else { /* Change in streak type */
272 				rc = window_flush(context, offset, count,
273 						       prev);
274 				if (rc < 0) {
275 					return rc;
276 				}
277 				offset = i;
278 				count = 1;
279 			}
280 		} else {
281 			if (prev != WINDOW_CLEAN) { /* End of a streak */
282 				rc = window_flush(context, offset, count,
283 						       prev);
284 				if (rc < 0) {
285 					return rc;
286 				}
287 				offset = 0;
288 				count = 0;
289 			}
290 		}
291 		prev = cur;
292 	}
293 
294 	if (prev != WINDOW_CLEAN) { /* Still the last streak to write */
295 		rc = window_flush(context, offset, count, prev);
296 		if (rc < 0) {
297 			return rc;
298 		}
299 	}
300 
301 	/* Clear the dirty bytemap since we have written back all changes */
302 	return window_set_bytemap(context, context->current, 0,
303 				  context->current->size >>
304 				  context->block_size_shift,
305 				  WINDOW_CLEAN);
306 }
307 
308 int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
309 {
310 	int rc;
311 
312 	if (!(context->current && context->current_is_write)) {
313 		MSG_ERR("Tried to call flush without open write window\n");
314 		return -EPERM;
315 	}
316 
317 	/*
318 	 * For V1 the Flush command acts much the same as the dirty command
319 	 * except with a flush as well. Only do this on an actual flush
320 	 * command not when we call flush because we've implicitly closed a
321 	 * window because we might not have the required args in req.
322 	 */
323 	if (io) {
324 		struct protocol_mark_dirty *mdio = (void *)io;
325 		rc = protocol_v1_mark_dirty(context, mdio);
326 		if (rc < 0) {
327 			return rc;
328 		}
329 	}
330 
331 	return generic_flush(context);
332 }
333 
334 int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
335 {
336 	int rc;
337 
338 	/* Close the current window if there is one */
339 	if (!context->current) {
340 		return 0;
341 	}
342 
343 	/* There is an implicit flush if it was a write window */
344 	if (context->current_is_write) {
345 		rc = protocol_v1_flush(context, NULL);
346 		if (rc < 0) {
347 			MSG_ERR("Couldn't Flush Write Window\n");
348 			return rc;
349 		}
350 	}
351 
352 	/* Host asked for it -> Don't set the BMC Event */
353 	windows_close_current(context, io->req.flags);
354 
355 	return 0;
356 }
357 
358 int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
359 {
360 	return protocol_events_clear(context,
361 				     (io->req.flags & BMC_EVENT_ACK_MASK));
362 }
363 
364 /*
365  * get_suggested_timeout() - get the suggested timeout value in seconds
366  * @context:	The mbox context pointer
367  *
368  * Return:	Suggested timeout in seconds
369  */
370 static uint16_t get_suggested_timeout(struct mbox_context *context)
371 {
372 	struct window_context *window = windows_find_largest(context);
373 	uint32_t max_size_mb = window ? (window->size >> 20) : 0;
374 	uint16_t ret;
375 
376 	ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
377 
378 	MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
379 		ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
380 	return ret;
381 }
382 
383 int protocol_v2_get_info(struct mbox_context *context,
384 			 struct protocol_get_info *io)
385 {
386 	uint8_t old_version = context->version;
387 	int rc;
388 
389 	/* Bootstrap protocol version. This may involve {up,down}grading */
390 	rc = protocol_negotiate_version(context, io->req.api_version);
391 	if (rc < 0)
392 		return rc;
393 
394 	/* Do the {up,down}grade if necessary*/
395 	if (rc != old_version) {
396 		/* Doing version negotiation, don't alert host to reset */
397 		windows_reset_all(context);
398 		return context->protocol->get_info(context, io);
399 	}
400 
401 	/* Record the negotiated version for the response */
402 	io->resp.api_version = rc;
403 
404 	/* Now do all required intialisation for v2 */
405 	context->block_size_shift = log_2(context->mtd_info.erasesize);
406 	MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
407 		 1 << context->block_size_shift, context->block_size_shift);
408 
409 	/* Knowing blocksize we can allocate the window dirty_bytemap */
410 	windows_alloc_dirty_bytemap(context);
411 
412 	io->resp.v2.block_size_shift = context->block_size_shift;
413 	io->resp.v2.timeout = get_suggested_timeout(context);
414 
415 	return lpc_map_memory(context);
416 }
417 
418 int protocol_v2_get_flash_info(struct mbox_context *context,
419 			       struct protocol_get_flash_info *io)
420 {
421 	io->resp.v2.flash_size =
422 		context->flash_size >> context->block_size_shift;
423 	io->resp.v2.erase_size =
424 		context->mtd_info.erasesize >> context->block_size_shift;
425 
426 	return 0;
427 }
428 
429 int protocol_v2_create_window(struct mbox_context *context,
430 			      struct protocol_create_window *io)
431 {
432 	int rc;
433 
434 	rc = protocol_v1_create_window(context, io);
435 	if (rc < 0)
436 		return rc;
437 
438 	io->resp.size = context->current->size >> context->block_size_shift;
439 	io->resp.offset = context->current->flash_offset >>
440 					context->block_size_shift;
441 
442 	return 0;
443 }
444 
445 int protocol_v2_mark_dirty(struct mbox_context *context,
446 			   struct protocol_mark_dirty *io)
447 {
448 	if (!(context->current && context->current_is_write)) {
449 		MSG_ERR("Tried to call mark dirty without open write window\n");
450 		return -EPERM;
451 	}
452 
453 	MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
454 		 io->req.v2.offset << context->block_size_shift,
455 		 io->req.v2.size << context->block_size_shift);
456 
457 	return window_set_bytemap(context, context->current, io->req.v2.offset,
458 				  io->req.v2.size, WINDOW_DIRTY);
459 }
460 
461 int protocol_v2_erase(struct mbox_context *context,
462 		      struct protocol_erase *io)
463 {
464 	size_t start, len;
465 	int rc;
466 
467 	if (!(context->current && context->current_is_write)) {
468 		MSG_ERR("Tried to call erase without open write window\n");
469 		return -EPERM;
470 	}
471 
472 	MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
473 		 io->req.offset << context->block_size_shift,
474 		 io->req.size << context->block_size_shift);
475 
476 	rc = window_set_bytemap(context, context->current, io->req.offset,
477 				io->req.size, WINDOW_ERASED);
478 	if (rc < 0) {
479 		return rc;
480 	}
481 
482 	/* Write 0xFF to mem -> This ensures consistency between flash & ram */
483 	start = io->req.offset << context->block_size_shift;
484 	len = io->req.size << context->block_size_shift;
485 	memset(context->current->mem + start, 0xFF, len);
486 
487 	return 0;
488 }
489 
490 int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
491 {
492 	if (!(context->current && context->current_is_write)) {
493 		MSG_ERR("Tried to call flush without open write window\n");
494 		return -EPERM;
495 	}
496 
497 	return generic_flush(context);
498 }
499 
500 int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
501 {
502 	int rc;
503 
504 	/* Close the current window if there is one */
505 	if (!context->current) {
506 		return 0;
507 	}
508 
509 	/* There is an implicit flush if it was a write window */
510 	if (context->current_is_write) {
511 		rc = protocol_v2_flush(context, NULL);
512 		if (rc < 0) {
513 			MSG_ERR("Couldn't Flush Write Window\n");
514 			return rc;
515 		}
516 	}
517 
518 	/* Host asked for it -> Don't set the BMC Event */
519 	windows_close_current(context, io->req.flags);
520 
521 	return 0;
522 }
523 
524 int protocol_init(struct mbox_context *context)
525 {
526 	protocol_negotiate_version(context, API_MAX_VERSION);
527 
528 	return 0;
529 }
530 
531 void protocol_free(struct mbox_context *context)
532 {
533 	return;
534 }
535