xref: /openbmc/hiomapd/protocol.c (revision c7d1947e88d9cf7c3ab7ac73a1b34b312ce5f361)
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 "mbox.h"
9 #include "lpc.h"
10 #include "transport_mbox.h" /* TODO: Remove dependency on transport_mbox.h */
11 #include "windows.h"
12 
13 int 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 
20 int 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 
56 int 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 
65 /*
66  * 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  */
71 static 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 
85 int protocol_v1_create_window(struct mbox_context *context,
86 			      struct protocol_create_window *io)
87 {
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",
117 				offset);
118 			return rc;
119 		}
120 	}
121 
122 	context->current_is_write = !io->req.ro;
123 
124 	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 
133 int 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 
172 static 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 
236 int 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 	 */
251 	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 		}
257 	}
258 
259 	return generic_flush(context);
260 }
261 
262 int 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 
286 int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
287 {
288 	return clr_bmc_events(context, (io->req.flags & BMC_EVENT_ACK_MASK),
289 			      SET_BMC_EVENT);
290 }
291 
292 /*
293  * get_suggested_timeout() - get the suggested timeout value in seconds
294  * @context:	The mbox context pointer
295  *
296  * Return:	Suggested timeout in seconds
297  */
298 static uint16_t get_suggested_timeout(struct mbox_context *context)
299 {
300 	struct window_context *window = windows_find_largest(context);
301 	uint32_t max_size_mb = window ? (window->size >> 20) : 0;
302 	uint16_t ret;
303 
304 	ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
305 
306 	MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
307 		ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
308 	return ret;
309 }
310 
311 int protocol_v2_get_info(struct mbox_context *context,
312 			 struct protocol_get_info *io)
313 {
314 	uint8_t old_version = context->version;
315 	int rc;
316 
317 	/* Bootstrap protocol version. This may involve {up,down}grading */
318 	rc = protocol_negotiate_version(context, io->req.api_version);
319 	if (rc < 0)
320 		return rc;
321 
322 	/* Do the {up,down}grade if necessary*/
323 	if (rc != old_version) {
324 		windows_reset_all(context, SET_BMC_EVENT);
325 		return context->protocol->get_info(context, io);
326 	}
327 
328 	/* Record the negotiated version for the response */
329 	io->resp.api_version = rc;
330 
331 	/* Now do all required intialisation for v2 */
332 	context->block_size_shift = log_2(context->mtd_info.erasesize);
333 	MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
334 		 1 << context->block_size_shift, context->block_size_shift);
335 
336 	/* Knowing blocksize we can allocate the window dirty_bytemap */
337 	windows_alloc_dirty_bytemap(context);
338 
339 	io->resp.v2.block_size_shift = context->block_size_shift;
340 	io->resp.v2.timeout = get_suggested_timeout(context);
341 
342 	return lpc_map_memory(context);
343 }
344 
345 int protocol_v2_get_flash_info(struct mbox_context *context,
346 			       struct protocol_get_flash_info *io)
347 {
348 	io->resp.v2.flash_size =
349 		context->flash_size >> context->block_size_shift;
350 	io->resp.v2.erase_size =
351 		context->mtd_info.erasesize >> context->block_size_shift;
352 
353 	return 0;
354 }
355 
356 int protocol_v2_create_window(struct mbox_context *context,
357 			      struct protocol_create_window *io)
358 {
359 	int rc;
360 
361 	rc = protocol_v1_create_window(context, io);
362 	if (rc < 0)
363 		return rc;
364 
365 	io->resp.size = context->current->size >> context->block_size_shift;
366 	io->resp.offset = context->current->flash_offset >>
367 					context->block_size_shift;
368 
369 	return 0;
370 }
371 
372 int protocol_v2_mark_dirty(struct mbox_context *context,
373 			   struct protocol_mark_dirty *io)
374 {
375 	if (!(context->current && context->current_is_write)) {
376 		MSG_ERR("Tried to call mark dirty without open write window\n");
377 		return -EPERM;
378 	}
379 
380 	MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
381 		 io->req.v2.offset << context->block_size_shift,
382 		 io->req.v2.size << context->block_size_shift);
383 
384 	return window_set_bytemap(context, context->current, io->req.v2.offset,
385 				  io->req.v2.size, WINDOW_DIRTY);
386 }
387 
388 int protocol_v2_erase(struct mbox_context *context,
389 		      struct protocol_erase *io)
390 {
391 	size_t start, len;
392 	int rc;
393 
394 	if (!(context->current && context->current_is_write)) {
395 		MSG_ERR("Tried to call erase without open write window\n");
396 		return -EPERM;
397 	}
398 
399 	MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
400 		 io->req.offset << context->block_size_shift,
401 		 io->req.size << context->block_size_shift);
402 
403 	rc = window_set_bytemap(context, context->current, io->req.offset,
404 				io->req.size, WINDOW_ERASED);
405 	if (rc < 0) {
406 		return rc;
407 	}
408 
409 	/* Write 0xFF to mem -> This ensures consistency between flash & ram */
410 	start = io->req.offset << context->block_size_shift;
411 	len = io->req.size << context->block_size_shift;
412 	memset(context->current->mem + start, 0xFF, len);
413 
414 	return 0;
415 }
416 
417 int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
418 {
419 	if (!(context->current && context->current_is_write)) {
420 		MSG_ERR("Tried to call flush without open write window\n");
421 		return -EPERM;
422 	}
423 
424 	return generic_flush(context);
425 }
426 
427 int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
428 {
429 	int rc;
430 
431 	/* Close the current window if there is one */
432 	if (!context->current) {
433 		return 0;
434 	}
435 
436 	/* There is an implicit flush if it was a write window */
437 	if (context->current_is_write) {
438 		rc = protocol_v2_flush(context, NULL);
439 		if (rc < 0) {
440 			MSG_ERR("Couldn't Flush Write Window\n");
441 			return rc;
442 		}
443 	}
444 
445 	/* Host asked for it -> Don't set the BMC Event */
446 	windows_close_current(context, NO_BMC_EVENT, io->req.flags);
447 
448 	return 0;
449 }
450 
451 int protocol_init(struct mbox_context *context)
452 {
453 	protocol_negotiate_version(context, API_MAX_VERSION);
454 
455 	return 0;
456 }
457 
458 void protocol_free(struct mbox_context *context)
459 {
460 	return;
461 }
462