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