xref: /openbmc/hiomapd/protocol.c (revision 9b920cf4)
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 	rc = protocol_v1_mark_dirty(context, (struct protocol_mark_dirty *)io);
252 	if (rc < 0) {
253 		return rc;
254 	}
255 
256 	return generic_flush(context);
257 }
258 
259 /*
260  * get_suggested_timeout() - get the suggested timeout value in seconds
261  * @context:	The mbox context pointer
262  *
263  * Return:	Suggested timeout in seconds
264  */
265 static uint16_t get_suggested_timeout(struct mbox_context *context)
266 {
267 	struct window_context *window = windows_find_largest(context);
268 	uint32_t max_size_mb = window ? (window->size >> 20) : 0;
269 	uint16_t ret;
270 
271 	ret = align_up(max_size_mb * FLASH_ACCESS_MS_PER_MB, 1000) / 1000;
272 
273 	MSG_DBG("Suggested Timeout: %us, max window size: %uMB, for %dms/MB\n",
274 		ret, max_size_mb, FLASH_ACCESS_MS_PER_MB);
275 	return ret;
276 }
277 
278 int protocol_v2_get_info(struct mbox_context *context,
279 			 struct protocol_get_info *io)
280 {
281 	uint8_t old_version = context->version;
282 	int rc;
283 
284 	/* Bootstrap protocol version. This may involve {up,down}grading */
285 	rc = protocol_negotiate_version(context, io->req.api_version);
286 	if (rc < 0)
287 		return rc;
288 
289 	/* Do the {up,down}grade if necessary*/
290 	if (rc != old_version) {
291 		windows_reset_all(context, SET_BMC_EVENT);
292 		return context->protocol->get_info(context, io);
293 	}
294 
295 	/* Record the negotiated version for the response */
296 	io->resp.api_version = rc;
297 
298 	/* Now do all required intialisation for v2 */
299 	context->block_size_shift = log_2(context->mtd_info.erasesize);
300 	MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
301 		 1 << context->block_size_shift, context->block_size_shift);
302 
303 	/* Knowing blocksize we can allocate the window dirty_bytemap */
304 	windows_alloc_dirty_bytemap(context);
305 
306 	io->resp.v2.block_size_shift = context->block_size_shift;
307 	io->resp.v2.timeout = get_suggested_timeout(context);
308 
309 	return lpc_map_memory(context);
310 }
311 
312 int protocol_v2_get_flash_info(struct mbox_context *context,
313 			       struct protocol_get_flash_info *io)
314 {
315 	io->resp.v2.flash_size =
316 		context->flash_size >> context->block_size_shift;
317 	io->resp.v2.erase_size =
318 		context->mtd_info.erasesize >> context->block_size_shift;
319 
320 	return 0;
321 }
322 
323 int protocol_v2_create_window(struct mbox_context *context,
324 			      struct protocol_create_window *io)
325 {
326 	int rc;
327 
328 	rc = protocol_v1_create_window(context, io);
329 	if (rc < 0)
330 		return rc;
331 
332 	io->resp.size = context->current->size >> context->block_size_shift;
333 	io->resp.offset = context->current->flash_offset >>
334 					context->block_size_shift;
335 
336 	return 0;
337 }
338 
339 int protocol_v2_mark_dirty(struct mbox_context *context,
340 			   struct protocol_mark_dirty *io)
341 {
342 	if (!(context->current && context->current_is_write)) {
343 		MSG_ERR("Tried to call mark dirty without open write window\n");
344 		return -EPERM;
345 	}
346 
347 	MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
348 		 io->req.v2.offset << context->block_size_shift,
349 		 io->req.v2.size << context->block_size_shift);
350 
351 	return window_set_bytemap(context, context->current, io->req.v2.offset,
352 				  io->req.v2.size, WINDOW_DIRTY);
353 }
354 
355 int protocol_v2_erase(struct mbox_context *context,
356 		      struct protocol_erase *io)
357 {
358 	size_t start, len;
359 	int rc;
360 
361 	if (!(context->current && context->current_is_write)) {
362 		MSG_ERR("Tried to call erase without open write window\n");
363 		return -EPERM;
364 	}
365 
366 	MSG_INFO("Erase window @ 0x%.8x for 0x%.8x\n",
367 		 io->req.offset << context->block_size_shift,
368 		 io->req.size << context->block_size_shift);
369 
370 	rc = window_set_bytemap(context, context->current, io->req.offset,
371 				io->req.size, WINDOW_ERASED);
372 	if (rc < 0) {
373 		return rc;
374 	}
375 
376 	/* Write 0xFF to mem -> This ensures consistency between flash & ram */
377 	start = io->req.offset << context->block_size_shift;
378 	len = io->req.size << context->block_size_shift;
379 	memset(context->current->mem + start, 0xFF, len);
380 
381 	return 0;
382 }
383 
384 int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
385 {
386 	if (!(context->current && context->current_is_write)) {
387 		MSG_ERR("Tried to call flush without open write window\n");
388 		return -EPERM;
389 	}
390 
391 	return generic_flush(context);
392 }
393 
394 static const struct protocol_ops protocol_ops_v1 = {
395 	.reset = protocol_v1_reset,
396 	.get_info = protocol_v1_get_info,
397 	.get_flash_info = protocol_v1_get_flash_info,
398 	.create_window = protocol_v1_create_window,
399 	.mark_dirty = protocol_v1_mark_dirty,
400 	.erase = NULL,
401 	.flush = protocol_v1_flush,
402 };
403 
404 static const struct protocol_ops protocol_ops_v2 = {
405 	.reset = protocol_v1_reset,
406 	.get_info = protocol_v2_get_info,
407 	.get_flash_info = protocol_v2_get_flash_info,
408 	.create_window = protocol_v2_create_window,
409 	.mark_dirty = protocol_v2_mark_dirty,
410 	.erase = protocol_v2_erase,
411 	.flush = protocol_v2_flush,
412 };
413 
414 static const struct protocol_ops *protocol_ops_map[] = {
415 	[0] = NULL,
416 	[1] = &protocol_ops_v1,
417 	[2] = &protocol_ops_v2,
418 };
419 
420 int protocol_negotiate_version(struct mbox_context *context,
421 				   uint8_t requested)
422 {
423 	/* Check we support the version requested */
424 	if (requested < API_MIN_VERSION)
425 		return -EINVAL;
426 
427 	context->version = (requested > API_MAX_VERSION) ?
428 				API_MAX_VERSION : requested;
429 
430 	context->protocol = protocol_ops_map[context->version];
431 
432 	return context->version;
433 }
434 
435 int protocol_init(struct mbox_context *context)
436 {
437 	context->version = API_MAX_VERSION;
438 	context->protocol = protocol_ops_map[context->version];
439 
440 	return 0;
441 }
442 
443 void protocol_free(struct mbox_context *context)
444 {
445 	return;
446 }
447