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