1f593b1bdSAndrew Jeffery // SPDX-License-Identifier: Apache-2.0 2f593b1bdSAndrew Jeffery // Copyright (C) 2018 IBM Corp. 3f593b1bdSAndrew Jeffery 4f593b1bdSAndrew Jeffery #define _GNU_SOURCE 5f593b1bdSAndrew Jeffery #include <assert.h> 6f593b1bdSAndrew Jeffery #include <errno.h> 7f593b1bdSAndrew Jeffery #include <fcntl.h> 8f593b1bdSAndrew Jeffery #include <getopt.h> 9f593b1bdSAndrew Jeffery #include <limits.h> 10f593b1bdSAndrew Jeffery #include <poll.h> 11f593b1bdSAndrew Jeffery #include <stdbool.h> 12f593b1bdSAndrew Jeffery #include <stdint.h> 13f593b1bdSAndrew Jeffery #include <stdio.h> 14f593b1bdSAndrew Jeffery #include <stdlib.h> 15f593b1bdSAndrew Jeffery #include <string.h> 16f593b1bdSAndrew Jeffery #include <syslog.h> 17f593b1bdSAndrew Jeffery #include <signal.h> 18f593b1bdSAndrew Jeffery #include <sys/ioctl.h> 19f593b1bdSAndrew Jeffery #include <sys/mman.h> 20f593b1bdSAndrew Jeffery #include <sys/stat.h> 21f593b1bdSAndrew Jeffery #include <sys/timerfd.h> 22f593b1bdSAndrew Jeffery #include <sys/types.h> 23f593b1bdSAndrew Jeffery #include <time.h> 24f593b1bdSAndrew Jeffery #include <unistd.h> 25f593b1bdSAndrew Jeffery #include <inttypes.h> 26f593b1bdSAndrew Jeffery #include <mtd/mtd-abi.h> 27f593b1bdSAndrew Jeffery 28f593b1bdSAndrew Jeffery #include "mbox.h" 29f593b1bdSAndrew Jeffery #include "common.h" 30f593b1bdSAndrew Jeffery #include "mboxd_msg.h" 31f593b1bdSAndrew Jeffery #include "windows.h" 32f593b1bdSAndrew Jeffery #include "flash.h" 33f593b1bdSAndrew Jeffery 34f593b1bdSAndrew Jeffery /* Initialisation Functions */ 35f593b1bdSAndrew Jeffery 36f593b1bdSAndrew Jeffery /* 37f593b1bdSAndrew Jeffery * init_window_state() - Initialise a new window to a known state 38f593b1bdSAndrew Jeffery * @window: The window to initialise 39f593b1bdSAndrew Jeffery * @size: The size of the window 40f593b1bdSAndrew Jeffery */ 41f593b1bdSAndrew Jeffery static void init_window_state(struct window_context *window, uint32_t size) 42f593b1bdSAndrew Jeffery { 43f593b1bdSAndrew Jeffery window->mem = NULL; 44f593b1bdSAndrew Jeffery window->flash_offset = FLASH_OFFSET_UNINIT; 45f593b1bdSAndrew Jeffery window->size = size; 46f593b1bdSAndrew Jeffery window->dirty_bmap = NULL; 47f593b1bdSAndrew Jeffery window->age = 0; 48f593b1bdSAndrew Jeffery } 49f593b1bdSAndrew Jeffery 50f593b1bdSAndrew Jeffery /* 51f593b1bdSAndrew Jeffery * init_window_mem() - Divide the reserved memory region among the windows 52f593b1bdSAndrew Jeffery * @context: The mbox context pointer 53f593b1bdSAndrew Jeffery * 54f593b1bdSAndrew Jeffery * Return: 0 on success otherwise negative error code 55f593b1bdSAndrew Jeffery */ 56f593b1bdSAndrew Jeffery static int init_window_mem(struct mbox_context *context) 57f593b1bdSAndrew Jeffery { 58f593b1bdSAndrew Jeffery void *mem_location = context->mem; 59f593b1bdSAndrew Jeffery int i; 60f593b1bdSAndrew Jeffery 61f593b1bdSAndrew Jeffery /* 62f593b1bdSAndrew Jeffery * Carve up the reserved memory region and allocate it to each of the 63f593b1bdSAndrew Jeffery * windows. The windows are placed one after the other in ascending 64f593b1bdSAndrew Jeffery * order, so the first window will be first in memory and so on. We 65f593b1bdSAndrew Jeffery * shouldn't have allocated more windows than we have memory, but if we 66f593b1bdSAndrew Jeffery * did we will error out here 67f593b1bdSAndrew Jeffery */ 68f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 69f593b1bdSAndrew Jeffery uint32_t size = context->windows.window[i].size; 70f593b1bdSAndrew Jeffery MSG_DBG("Window %d @ %p for size 0x%.8x\n", i, 71f593b1bdSAndrew Jeffery mem_location, size); 72f593b1bdSAndrew Jeffery context->windows.window[i].mem = mem_location; 73f593b1bdSAndrew Jeffery mem_location += size; 74f593b1bdSAndrew Jeffery if (mem_location > (context->mem + context->mem_size)) { 75f593b1bdSAndrew Jeffery /* Tried to allocate window past the end of memory */ 76f593b1bdSAndrew Jeffery MSG_ERR("Total size of windows exceeds reserved mem\n"); 77f593b1bdSAndrew Jeffery MSG_ERR("Try smaller or fewer windows\n"); 78f593b1bdSAndrew Jeffery MSG_ERR("Mem size: 0x%.8x\n", context->mem_size); 79f593b1bdSAndrew Jeffery return -1; 80f593b1bdSAndrew Jeffery } 81f593b1bdSAndrew Jeffery } 82f593b1bdSAndrew Jeffery 83f593b1bdSAndrew Jeffery return 0; 84f593b1bdSAndrew Jeffery } 85f593b1bdSAndrew Jeffery /* 86c1a67fa8SAndrew Jeffery * windows_init() - Initalise the window cache 87f593b1bdSAndrew Jeffery * @context: The mbox context pointer 88f593b1bdSAndrew Jeffery * 89f593b1bdSAndrew Jeffery * Return: 0 on success otherwise negative 90f593b1bdSAndrew Jeffery */ 91c1a67fa8SAndrew Jeffery int windows_init(struct mbox_context *context) 92f593b1bdSAndrew Jeffery { 93f593b1bdSAndrew Jeffery int i; 94f593b1bdSAndrew Jeffery 95f593b1bdSAndrew Jeffery /* Check if window size and number set - otherwise set to default */ 96f593b1bdSAndrew Jeffery if (!context->windows.default_size) { 97f593b1bdSAndrew Jeffery /* Default to 1MB windows */ 98f593b1bdSAndrew Jeffery context->windows.default_size = 1 << 20; 99f593b1bdSAndrew Jeffery } 100f593b1bdSAndrew Jeffery MSG_INFO("Window size: 0x%.8x\n", context->windows.default_size); 101f593b1bdSAndrew Jeffery if (!context->windows.num) { 102f593b1bdSAndrew Jeffery /* Use the entire reserved memory region by default */ 103f593b1bdSAndrew Jeffery context->windows.num = context->mem_size / 104f593b1bdSAndrew Jeffery context->windows.default_size; 105f593b1bdSAndrew Jeffery } 106f593b1bdSAndrew Jeffery MSG_INFO("Number of windows: %d\n", context->windows.num); 107f593b1bdSAndrew Jeffery 108f593b1bdSAndrew Jeffery context->windows.window = calloc(context->windows.num, 109f593b1bdSAndrew Jeffery sizeof(*context->windows.window)); 110f593b1bdSAndrew Jeffery if (!context->windows.window) { 111f593b1bdSAndrew Jeffery MSG_ERR("Memory allocation failed\n"); 112f593b1bdSAndrew Jeffery return -1; 113f593b1bdSAndrew Jeffery } 114f593b1bdSAndrew Jeffery 115f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 116f593b1bdSAndrew Jeffery init_window_state(&context->windows.window[i], 117f593b1bdSAndrew Jeffery context->windows.default_size); 118f593b1bdSAndrew Jeffery } 119f593b1bdSAndrew Jeffery 120f593b1bdSAndrew Jeffery return init_window_mem(context); 121f593b1bdSAndrew Jeffery } 122f593b1bdSAndrew Jeffery 123f593b1bdSAndrew Jeffery /* 124f5f51428SAndrew Jeffery * windows_free() - Free the window cache 125f593b1bdSAndrew Jeffery * @context: The mbox context pointer 126f593b1bdSAndrew Jeffery */ 127f5f51428SAndrew Jeffery void windows_free(struct mbox_context *context) 128f593b1bdSAndrew Jeffery { 129f593b1bdSAndrew Jeffery int i; 130f593b1bdSAndrew Jeffery 131f593b1bdSAndrew Jeffery /* Check window cache has actually been allocated */ 132f593b1bdSAndrew Jeffery if (context->windows.window) { 133f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 134f593b1bdSAndrew Jeffery free(context->windows.window[i].dirty_bmap); 135f593b1bdSAndrew Jeffery } 136f593b1bdSAndrew Jeffery free(context->windows.window); 137f593b1bdSAndrew Jeffery } 138f593b1bdSAndrew Jeffery } 139f593b1bdSAndrew Jeffery 140f593b1bdSAndrew Jeffery /* Write from Window Functions */ 141f593b1bdSAndrew Jeffery 142f593b1bdSAndrew Jeffery /* 1433200c072SAndrew Jeffery * window_flush_v1() - Handle writing when erase and block size differ 144f593b1bdSAndrew Jeffery * @context: The mbox context pointer 145f593b1bdSAndrew Jeffery * @offset_bytes: The offset in the current window to write from (bytes) 146f593b1bdSAndrew Jeffery * @count_bytes: Number of bytes to write 147f593b1bdSAndrew Jeffery * 1483200c072SAndrew Jeffery * Handle a window_flush for dirty memory when block_size is less than the 149f593b1bdSAndrew Jeffery * flash erase size 150f593b1bdSAndrew Jeffery * This requires us to be a bit careful because we might have to erase more 151f593b1bdSAndrew Jeffery * than we want to write which could result in data loss if we don't have the 152f593b1bdSAndrew Jeffery * entire portion of flash to be erased already saved in memory (for us to 153f593b1bdSAndrew Jeffery * write back after the erase) 154f593b1bdSAndrew Jeffery * 155f593b1bdSAndrew Jeffery * Return: 0 on success otherwise negative error code 156f593b1bdSAndrew Jeffery */ 1573200c072SAndrew Jeffery int window_flush_v1(struct mbox_context *context, 158f593b1bdSAndrew Jeffery uint32_t offset_bytes, uint32_t count_bytes) 159f593b1bdSAndrew Jeffery { 160f593b1bdSAndrew Jeffery int rc; 161f593b1bdSAndrew Jeffery uint32_t flash_offset; 162f593b1bdSAndrew Jeffery struct window_context low_mem = { 0 }, high_mem = { 0 }; 163f593b1bdSAndrew Jeffery 164f593b1bdSAndrew Jeffery /* Find where in phys flash this is based on the window.flash_offset */ 165f593b1bdSAndrew Jeffery flash_offset = context->current->flash_offset + offset_bytes; 166f593b1bdSAndrew Jeffery 167f593b1bdSAndrew Jeffery /* 168f593b1bdSAndrew Jeffery * low_mem.flash_offset = erase boundary below where we're writing 169f593b1bdSAndrew Jeffery * low_mem.size = size from low_mem.flash_offset to where we're writing 170f593b1bdSAndrew Jeffery * 171f593b1bdSAndrew Jeffery * high_mem.flash_offset = end of where we're writing 172f593b1bdSAndrew Jeffery * high_mem.size = size from end of where we're writing to next erase 173f593b1bdSAndrew Jeffery * boundary 174f593b1bdSAndrew Jeffery */ 175f593b1bdSAndrew Jeffery low_mem.flash_offset = align_down(flash_offset, 176f593b1bdSAndrew Jeffery context->mtd_info.erasesize); 177f593b1bdSAndrew Jeffery low_mem.size = flash_offset - low_mem.flash_offset; 178f593b1bdSAndrew Jeffery high_mem.flash_offset = flash_offset + count_bytes; 179f593b1bdSAndrew Jeffery high_mem.size = align_up(high_mem.flash_offset, 180f593b1bdSAndrew Jeffery context->mtd_info.erasesize) - 181f593b1bdSAndrew Jeffery high_mem.flash_offset; 182f593b1bdSAndrew Jeffery 183f593b1bdSAndrew Jeffery /* 184f593b1bdSAndrew Jeffery * Check if we already have a copy of the required flash areas in 185f593b1bdSAndrew Jeffery * memory as part of the existing window 186f593b1bdSAndrew Jeffery */ 187f593b1bdSAndrew Jeffery if (low_mem.flash_offset < context->current->flash_offset) { 188f593b1bdSAndrew Jeffery /* Before the start of our current window */ 189f593b1bdSAndrew Jeffery low_mem.mem = malloc(low_mem.size); 190f593b1bdSAndrew Jeffery if (!low_mem.mem) { 191f593b1bdSAndrew Jeffery MSG_ERR("Unable to allocate memory\n"); 192f593b1bdSAndrew Jeffery return -MBOX_R_SYSTEM_ERROR; 193f593b1bdSAndrew Jeffery } 194f593b1bdSAndrew Jeffery rc = flash_copy(context, low_mem.flash_offset, 195f593b1bdSAndrew Jeffery low_mem.mem, low_mem.size); 196f593b1bdSAndrew Jeffery if (rc < 0) { 197f593b1bdSAndrew Jeffery goto out; 198f593b1bdSAndrew Jeffery } 199f593b1bdSAndrew Jeffery } 200f593b1bdSAndrew Jeffery if ((high_mem.flash_offset + high_mem.size) > 201f593b1bdSAndrew Jeffery (context->current->flash_offset + context->current->size)) { 202f593b1bdSAndrew Jeffery /* After the end of our current window */ 203f593b1bdSAndrew Jeffery high_mem.mem = malloc(high_mem.size); 204f593b1bdSAndrew Jeffery if (!high_mem.mem) { 205f593b1bdSAndrew Jeffery MSG_ERR("Unable to allocate memory\n"); 206f593b1bdSAndrew Jeffery rc = -MBOX_R_SYSTEM_ERROR; 207f593b1bdSAndrew Jeffery goto out; 208f593b1bdSAndrew Jeffery } 209f593b1bdSAndrew Jeffery rc = flash_copy(context, high_mem.flash_offset, 210f593b1bdSAndrew Jeffery high_mem.mem, high_mem.size); 211f593b1bdSAndrew Jeffery if (rc < 0) { 212f593b1bdSAndrew Jeffery goto out; 213f593b1bdSAndrew Jeffery } 214f593b1bdSAndrew Jeffery } 215f593b1bdSAndrew Jeffery 216f593b1bdSAndrew Jeffery /* 217f593b1bdSAndrew Jeffery * We need to erase the flash from low_mem.flash_offset-> 218f593b1bdSAndrew Jeffery * high_mem.flash_offset + high_mem.size 219f593b1bdSAndrew Jeffery */ 220f593b1bdSAndrew Jeffery rc = flash_erase(context, low_mem.flash_offset, 221f593b1bdSAndrew Jeffery (high_mem.flash_offset - low_mem.flash_offset) + 222f593b1bdSAndrew Jeffery high_mem.size); 223f593b1bdSAndrew Jeffery if (rc < 0) { 224f593b1bdSAndrew Jeffery MSG_ERR("Couldn't erase flash\n"); 225f593b1bdSAndrew Jeffery goto out; 226f593b1bdSAndrew Jeffery } 227f593b1bdSAndrew Jeffery 228f593b1bdSAndrew Jeffery /* Write back over the erased area */ 229f593b1bdSAndrew Jeffery if (low_mem.mem) { 230f593b1bdSAndrew Jeffery /* Exceed window at the start */ 231f593b1bdSAndrew Jeffery rc = flash_write(context, low_mem.flash_offset, low_mem.mem, 232f593b1bdSAndrew Jeffery low_mem.size); 233f593b1bdSAndrew Jeffery if (rc < 0) { 234f593b1bdSAndrew Jeffery goto out; 235f593b1bdSAndrew Jeffery } 236f593b1bdSAndrew Jeffery } 237f593b1bdSAndrew Jeffery rc = flash_write(context, flash_offset, 238f593b1bdSAndrew Jeffery context->current->mem + offset_bytes, count_bytes); 239f593b1bdSAndrew Jeffery if (rc < 0) { 240f593b1bdSAndrew Jeffery goto out; 241f593b1bdSAndrew Jeffery } 242f593b1bdSAndrew Jeffery /* 243f593b1bdSAndrew Jeffery * We still need to write the last little bit that we erased - it's 244f593b1bdSAndrew Jeffery * either in the current window or the high_mem window. 245f593b1bdSAndrew Jeffery */ 246f593b1bdSAndrew Jeffery if (high_mem.mem) { 247f593b1bdSAndrew Jeffery /* Exceed window at the end */ 248f593b1bdSAndrew Jeffery rc = flash_write(context, high_mem.flash_offset, high_mem.mem, 249f593b1bdSAndrew Jeffery high_mem.size); 250f593b1bdSAndrew Jeffery if (rc < 0) { 251f593b1bdSAndrew Jeffery goto out; 252f593b1bdSAndrew Jeffery } 253f593b1bdSAndrew Jeffery } else { 254f593b1bdSAndrew Jeffery /* Write from the current window - it's atleast that big */ 255f593b1bdSAndrew Jeffery rc = flash_write(context, high_mem.flash_offset, 256f593b1bdSAndrew Jeffery context->current->mem + offset_bytes + 257f593b1bdSAndrew Jeffery count_bytes, high_mem.size); 258f593b1bdSAndrew Jeffery if (rc < 0) { 259f593b1bdSAndrew Jeffery goto out; 260f593b1bdSAndrew Jeffery } 261f593b1bdSAndrew Jeffery } 262f593b1bdSAndrew Jeffery 263f593b1bdSAndrew Jeffery out: 264f593b1bdSAndrew Jeffery free(low_mem.mem); 265f593b1bdSAndrew Jeffery free(high_mem.mem); 266f593b1bdSAndrew Jeffery return rc; 267f593b1bdSAndrew Jeffery } 268f593b1bdSAndrew Jeffery 269f593b1bdSAndrew Jeffery /* 2703200c072SAndrew Jeffery * window_flush() - Write back to the flash from the current window 271f593b1bdSAndrew Jeffery * @context: The mbox context pointer 272f593b1bdSAndrew Jeffery * @offset_bytes: The offset in the current window to write from (blocks) 273f593b1bdSAndrew Jeffery * @count_bytes: Number of blocks to write 274f593b1bdSAndrew Jeffery * @type: Whether this is an erase & write or just an erase 275f593b1bdSAndrew Jeffery * 276f593b1bdSAndrew Jeffery * Return: 0 on success otherwise negative error code 277f593b1bdSAndrew Jeffery */ 2783200c072SAndrew Jeffery int window_flush(struct mbox_context *context, uint32_t offset, 279f593b1bdSAndrew Jeffery uint32_t count, uint8_t type) 280f593b1bdSAndrew Jeffery { 281f593b1bdSAndrew Jeffery int rc; 282f593b1bdSAndrew Jeffery uint32_t flash_offset, count_bytes = count << context->block_size_shift; 283f593b1bdSAndrew Jeffery uint32_t offset_bytes = offset << context->block_size_shift; 284f593b1bdSAndrew Jeffery 285f593b1bdSAndrew Jeffery switch (type) { 286f593b1bdSAndrew Jeffery case WINDOW_ERASED: /* >= V2 ONLY -> block_size == erasesize */ 287f593b1bdSAndrew Jeffery flash_offset = context->current->flash_offset + offset_bytes; 288f593b1bdSAndrew Jeffery rc = flash_erase(context, flash_offset, count_bytes); 289f593b1bdSAndrew Jeffery if (rc < 0) { 290f593b1bdSAndrew Jeffery MSG_ERR("Couldn't erase flash\n"); 291f593b1bdSAndrew Jeffery return rc; 292f593b1bdSAndrew Jeffery } 293f593b1bdSAndrew Jeffery break; 294f593b1bdSAndrew Jeffery case WINDOW_DIRTY: 295f593b1bdSAndrew Jeffery /* 296f593b1bdSAndrew Jeffery * For protocol V1, block_size may be smaller than erase size 297f593b1bdSAndrew Jeffery * so we have a special function to make sure that we do this 298f593b1bdSAndrew Jeffery * correctly without losing data. 299f593b1bdSAndrew Jeffery */ 300f593b1bdSAndrew Jeffery if (log_2(context->mtd_info.erasesize) != 301f593b1bdSAndrew Jeffery context->block_size_shift) { 3023200c072SAndrew Jeffery return window_flush_v1(context, offset_bytes, 303f593b1bdSAndrew Jeffery count_bytes); 304f593b1bdSAndrew Jeffery } 305f593b1bdSAndrew Jeffery flash_offset = context->current->flash_offset + offset_bytes; 306f593b1bdSAndrew Jeffery 307f593b1bdSAndrew Jeffery /* Erase the flash */ 308f593b1bdSAndrew Jeffery rc = flash_erase(context, flash_offset, count_bytes); 309f593b1bdSAndrew Jeffery if (rc < 0) { 310f593b1bdSAndrew Jeffery return rc; 311f593b1bdSAndrew Jeffery } 312f593b1bdSAndrew Jeffery 313f593b1bdSAndrew Jeffery /* Write to the erased flash */ 314f593b1bdSAndrew Jeffery rc = flash_write(context, flash_offset, 315f593b1bdSAndrew Jeffery context->current->mem + offset_bytes, 316f593b1bdSAndrew Jeffery count_bytes); 317f593b1bdSAndrew Jeffery if (rc < 0) { 318f593b1bdSAndrew Jeffery return rc; 319f593b1bdSAndrew Jeffery } 320f593b1bdSAndrew Jeffery 321f593b1bdSAndrew Jeffery break; 322f593b1bdSAndrew Jeffery default: 323f593b1bdSAndrew Jeffery /* We shouldn't be able to get here */ 324f593b1bdSAndrew Jeffery MSG_ERR("Write from window with invalid type: %d\n", type); 325f593b1bdSAndrew Jeffery return -MBOX_R_SYSTEM_ERROR; 326f593b1bdSAndrew Jeffery } 327f593b1bdSAndrew Jeffery 328f593b1bdSAndrew Jeffery return 0; 329f593b1bdSAndrew Jeffery } 330f593b1bdSAndrew Jeffery 331f593b1bdSAndrew Jeffery /* Window Management Functions */ 332f593b1bdSAndrew Jeffery 333f593b1bdSAndrew Jeffery /* 334348ea792SAndrew Jeffery * windows_alloc_dirty_bytemap() - (re)allocate all the window dirty bytemaps 335f593b1bdSAndrew Jeffery * @context: The mbox context pointer 336f593b1bdSAndrew Jeffery */ 337348ea792SAndrew Jeffery void windows_alloc_dirty_bytemap(struct mbox_context *context) 338f593b1bdSAndrew Jeffery { 339f593b1bdSAndrew Jeffery struct window_context *cur; 340f593b1bdSAndrew Jeffery int i; 341f593b1bdSAndrew Jeffery 342f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 343f593b1bdSAndrew Jeffery cur = &context->windows.window[i]; 344f593b1bdSAndrew Jeffery /* There may already be one allocated */ 345f593b1bdSAndrew Jeffery free(cur->dirty_bmap); 346f593b1bdSAndrew Jeffery /* Allocate the new one */ 347f593b1bdSAndrew Jeffery cur->dirty_bmap = calloc((cur->size >> 348f593b1bdSAndrew Jeffery context->block_size_shift), 349f593b1bdSAndrew Jeffery sizeof(*cur->dirty_bmap)); 350f593b1bdSAndrew Jeffery } 351f593b1bdSAndrew Jeffery } 352f593b1bdSAndrew Jeffery 353f593b1bdSAndrew Jeffery /* 3547d5ada63SAndrew Jeffery * window_set_bytemap() - Set the window bytemap 355f593b1bdSAndrew Jeffery * @context: The mbox context pointer 356f593b1bdSAndrew Jeffery * @cur: The window to set the bytemap of 357f593b1bdSAndrew Jeffery * @offset: Where in the window to set the bytemap (blocks) 358f593b1bdSAndrew Jeffery * @size: The number of blocks to set 359f593b1bdSAndrew Jeffery * @val: The value to set the bytemap to 360f593b1bdSAndrew Jeffery * 361f593b1bdSAndrew Jeffery * Return: 0 on success otherwise negative error code 362f593b1bdSAndrew Jeffery */ 3637d5ada63SAndrew Jeffery int window_set_bytemap(struct mbox_context *context, struct window_context *cur, 364f593b1bdSAndrew Jeffery uint32_t offset, uint32_t size, uint8_t val) 365f593b1bdSAndrew Jeffery { 366f593b1bdSAndrew Jeffery if (offset + size > (cur->size >> context->block_size_shift)) { 367f593b1bdSAndrew Jeffery MSG_ERR("Tried to set window bytemap past end of window\n"); 368f593b1bdSAndrew Jeffery MSG_ERR("Requested offset: 0x%x size: 0x%x window size: 0x%x\n", 369f593b1bdSAndrew Jeffery offset << context->block_size_shift, 370f593b1bdSAndrew Jeffery size << context->block_size_shift, 371f593b1bdSAndrew Jeffery cur->size << context->block_size_shift); 372f593b1bdSAndrew Jeffery return -EACCES; 373f593b1bdSAndrew Jeffery } 374f593b1bdSAndrew Jeffery 375f593b1bdSAndrew Jeffery memset(cur->dirty_bmap + offset, val, size); 376f593b1bdSAndrew Jeffery return 0; 377f593b1bdSAndrew Jeffery } 378f593b1bdSAndrew Jeffery 379f593b1bdSAndrew Jeffery /* 380b65bb4c5SAndrew Jeffery * windows_close_current() - Close the current (active) window 381f593b1bdSAndrew Jeffery * @context: The mbox context pointer 382f593b1bdSAndrew Jeffery * @set_bmc_event: Whether to set the bmc event bit 383f593b1bdSAndrew Jeffery * @flags: Flags as defined for a close command in the protocol 384f593b1bdSAndrew Jeffery * 385f593b1bdSAndrew Jeffery * This closes the current window. If the host has requested the current window 386f593b1bdSAndrew Jeffery * be closed then we don't need to set the bmc event bit 387f593b1bdSAndrew Jeffery * (set_bmc_event == false), otherwise if the current window has been closed 388f593b1bdSAndrew Jeffery * without the host requesting it the bmc event bit must be set to indicate this 389f593b1bdSAndrew Jeffery * to the host (set_bmc_event == true). 390f593b1bdSAndrew Jeffery */ 391b65bb4c5SAndrew Jeffery void windows_close_current(struct mbox_context *context, bool set_bmc_event, 392f593b1bdSAndrew Jeffery uint8_t flags) 393f593b1bdSAndrew Jeffery { 394f593b1bdSAndrew Jeffery MSG_DBG("Close current window, flags: 0x%.2x\n", flags); 395f593b1bdSAndrew Jeffery 396f593b1bdSAndrew Jeffery if (set_bmc_event) { 397f593b1bdSAndrew Jeffery set_bmc_events(context, BMC_EVENT_WINDOW_RESET, SET_BMC_EVENT); 398f593b1bdSAndrew Jeffery } 399f593b1bdSAndrew Jeffery 400f593b1bdSAndrew Jeffery if (flags & FLAGS_SHORT_LIFETIME) { 401f593b1bdSAndrew Jeffery context->current->age = 0; 402f593b1bdSAndrew Jeffery } 403f593b1bdSAndrew Jeffery 404f593b1bdSAndrew Jeffery context->current = NULL; 405f593b1bdSAndrew Jeffery context->current_is_write = false; 406f593b1bdSAndrew Jeffery } 407f593b1bdSAndrew Jeffery 408f593b1bdSAndrew Jeffery /* 4095dc9f959SAndrew Jeffery * window_reset() - Reset a window context to a well defined default state 410f593b1bdSAndrew Jeffery * @context: The mbox context pointer 411f593b1bdSAndrew Jeffery * @window: The window to reset 412f593b1bdSAndrew Jeffery */ 4135dc9f959SAndrew Jeffery void window_reset(struct mbox_context *context, struct window_context *window) 414f593b1bdSAndrew Jeffery { 415f593b1bdSAndrew Jeffery window->flash_offset = FLASH_OFFSET_UNINIT; 416f593b1bdSAndrew Jeffery window->size = context->windows.default_size; 417f593b1bdSAndrew Jeffery if (window->dirty_bmap) { /* Might not have been allocated */ 4187d5ada63SAndrew Jeffery window_set_bytemap(context, window, 0, 419f593b1bdSAndrew Jeffery window->size >> context->block_size_shift, 420f593b1bdSAndrew Jeffery WINDOW_CLEAN); 421f593b1bdSAndrew Jeffery } 422f593b1bdSAndrew Jeffery window->age = 0; 423f593b1bdSAndrew Jeffery } 424f593b1bdSAndrew Jeffery 425f593b1bdSAndrew Jeffery /* 426d6a7426eSAndrew Jeffery * windows_reset_all() - Reset all windows to a well defined default state 427f593b1bdSAndrew Jeffery * @context: The mbox context pointer 428f593b1bdSAndrew Jeffery * @set_bmc_event: If any state change should be indicated to the host 429f593b1bdSAndrew Jeffery */ 430d6a7426eSAndrew Jeffery void windows_reset_all(struct mbox_context *context, bool set_bmc_event) 431f593b1bdSAndrew Jeffery { 432f593b1bdSAndrew Jeffery int i; 433f593b1bdSAndrew Jeffery 434f593b1bdSAndrew Jeffery MSG_DBG("Resetting all windows\n"); 435f593b1bdSAndrew Jeffery /* We might have an open window which needs closing */ 436f593b1bdSAndrew Jeffery if (context->current) { 437b65bb4c5SAndrew Jeffery windows_close_current(context, set_bmc_event, FLAGS_NONE); 438f593b1bdSAndrew Jeffery } 439f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 4405dc9f959SAndrew Jeffery window_reset(context, &context->windows.window[i]); 441f593b1bdSAndrew Jeffery } 442f593b1bdSAndrew Jeffery 443f593b1bdSAndrew Jeffery context->windows.max_age = 0; 444f593b1bdSAndrew Jeffery } 445f593b1bdSAndrew Jeffery 446f593b1bdSAndrew Jeffery /* 4479412f059SAndrew Jeffery * windows_find_oldest() - Find the oldest (Least Recently Used) window 448f593b1bdSAndrew Jeffery * @context: The mbox context pointer 449f593b1bdSAndrew Jeffery * 450f593b1bdSAndrew Jeffery * Return: Pointer to the least recently used window 451f593b1bdSAndrew Jeffery */ 4529412f059SAndrew Jeffery struct window_context *windows_find_oldest(struct mbox_context *context) 453f593b1bdSAndrew Jeffery { 454f593b1bdSAndrew Jeffery struct window_context *oldest = NULL, *cur; 455f593b1bdSAndrew Jeffery uint32_t min_age = context->windows.max_age + 1; 456f593b1bdSAndrew Jeffery int i; 457f593b1bdSAndrew Jeffery 458f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 459f593b1bdSAndrew Jeffery cur = &context->windows.window[i]; 460f593b1bdSAndrew Jeffery 461f593b1bdSAndrew Jeffery if (cur->age < min_age) { 462f593b1bdSAndrew Jeffery min_age = cur->age; 463f593b1bdSAndrew Jeffery oldest = cur; 464f593b1bdSAndrew Jeffery } 465f593b1bdSAndrew Jeffery } 466f593b1bdSAndrew Jeffery 467f593b1bdSAndrew Jeffery return oldest; 468f593b1bdSAndrew Jeffery } 469f593b1bdSAndrew Jeffery 470f593b1bdSAndrew Jeffery /* 471d8c12e1bSAndrew Jeffery * windows_find_largest() - Find the largest window in the window cache 472f593b1bdSAndrew Jeffery * @context: The mbox context pointer 473f593b1bdSAndrew Jeffery * 474f593b1bdSAndrew Jeffery * Return: The largest window 475f593b1bdSAndrew Jeffery */ 476d8c12e1bSAndrew Jeffery struct window_context *windows_find_largest(struct mbox_context *context) 477f593b1bdSAndrew Jeffery { 478f593b1bdSAndrew Jeffery struct window_context *largest = NULL, *cur; 479f593b1bdSAndrew Jeffery uint32_t max_size = 0; 480f593b1bdSAndrew Jeffery int i; 481f593b1bdSAndrew Jeffery 482f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 483f593b1bdSAndrew Jeffery cur = &context->windows.window[i]; 484f593b1bdSAndrew Jeffery 485f593b1bdSAndrew Jeffery if (cur->size > max_size) { 486f593b1bdSAndrew Jeffery max_size = cur->size; 487f593b1bdSAndrew Jeffery largest = cur; 488f593b1bdSAndrew Jeffery } 489f593b1bdSAndrew Jeffery } 490f593b1bdSAndrew Jeffery 491f593b1bdSAndrew Jeffery return largest; 492f593b1bdSAndrew Jeffery } 493f593b1bdSAndrew Jeffery 494f593b1bdSAndrew Jeffery /* 495*17c477a7SAndrew Jeffery * windows_search() - Search the window cache for a window containing offset 496f593b1bdSAndrew Jeffery * @context: The mbox context pointer 497f593b1bdSAndrew Jeffery * @offset: Absolute flash offset to search for (bytes) 498f593b1bdSAndrew Jeffery * @exact: If the window must exactly map the requested offset 499f593b1bdSAndrew Jeffery * 500f593b1bdSAndrew Jeffery * This will search the cache of windows for one containing the requested 501f593b1bdSAndrew Jeffery * offset. For V1 of the protocol windows must exactly map the offset since we 502f593b1bdSAndrew Jeffery * can't tell the host how much of its request we actually mapped and it will 503f593b1bdSAndrew Jeffery * thus assume it can access window->size from the offset we give it. 504f593b1bdSAndrew Jeffery * 505f593b1bdSAndrew Jeffery * Return: Pointer to a window containing the requested offset otherwise 506f593b1bdSAndrew Jeffery * NULL 507f593b1bdSAndrew Jeffery */ 508*17c477a7SAndrew Jeffery struct window_context *windows_search(struct mbox_context *context, 509f593b1bdSAndrew Jeffery uint32_t offset, bool exact) 510f593b1bdSAndrew Jeffery { 511f593b1bdSAndrew Jeffery struct window_context *cur; 512f593b1bdSAndrew Jeffery int i; 513f593b1bdSAndrew Jeffery 514f593b1bdSAndrew Jeffery MSG_DBG("Searching for window which contains 0x%.8x %s\n", 515f593b1bdSAndrew Jeffery offset, exact ? "exactly" : ""); 516f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 517f593b1bdSAndrew Jeffery cur = &context->windows.window[i]; 518f593b1bdSAndrew Jeffery if (cur->flash_offset == FLASH_OFFSET_UNINIT) { 519f593b1bdSAndrew Jeffery /* Uninitialised Window */ 520f593b1bdSAndrew Jeffery if (offset == FLASH_OFFSET_UNINIT) { 521f593b1bdSAndrew Jeffery return cur; 522f593b1bdSAndrew Jeffery } 523f593b1bdSAndrew Jeffery continue; 524f593b1bdSAndrew Jeffery } 525f593b1bdSAndrew Jeffery if ((offset >= cur->flash_offset) && 526f593b1bdSAndrew Jeffery (offset < (cur->flash_offset + cur->size))) { 527f593b1bdSAndrew Jeffery if (exact && (cur->flash_offset != offset)) { 528f593b1bdSAndrew Jeffery continue; 529f593b1bdSAndrew Jeffery } 530f593b1bdSAndrew Jeffery /* This window contains the requested offset */ 531f593b1bdSAndrew Jeffery cur->age = ++(context->windows.max_age); 532f593b1bdSAndrew Jeffery return cur; 533f593b1bdSAndrew Jeffery } 534f593b1bdSAndrew Jeffery } 535f593b1bdSAndrew Jeffery 536f593b1bdSAndrew Jeffery return NULL; 537f593b1bdSAndrew Jeffery } 538f593b1bdSAndrew Jeffery 539f593b1bdSAndrew Jeffery /* 540f593b1bdSAndrew Jeffery * create_map_window() - Create a window mapping which maps the requested offset 541f593b1bdSAndrew Jeffery * @context: The mbox context pointer 542f593b1bdSAndrew Jeffery * @this_window: A pointer to update to the "new" window 543f593b1bdSAndrew Jeffery * @offset: Absolute flash offset to create a mapping for (bytes) 544f593b1bdSAndrew Jeffery * @exact: If the window must exactly map the requested offset 545f593b1bdSAndrew Jeffery * 546f593b1bdSAndrew Jeffery * This is used to create a window mapping for the requested offset when there 547f593b1bdSAndrew Jeffery * is no existing window in the cache which satisfies the offset. This involves 548f593b1bdSAndrew Jeffery * choosing an existing window from the window cache to evict so we can use it 549f593b1bdSAndrew Jeffery * to store the flash contents from the requested offset, we then point the 550f593b1bdSAndrew Jeffery * caller to that window since it now maps their request. 551f593b1bdSAndrew Jeffery * 552f593b1bdSAndrew Jeffery * Return: 0 on success otherwise negative error code 553f593b1bdSAndrew Jeffery */ 554f593b1bdSAndrew Jeffery int create_map_window(struct mbox_context *context, 555f593b1bdSAndrew Jeffery struct window_context **this_window, uint32_t offset, 556f593b1bdSAndrew Jeffery bool exact) 557f593b1bdSAndrew Jeffery { 558f593b1bdSAndrew Jeffery struct window_context *cur = NULL; 559f593b1bdSAndrew Jeffery int rc; 560f593b1bdSAndrew Jeffery 561f593b1bdSAndrew Jeffery MSG_DBG("Creating window which maps 0x%.8x %s\n", offset, 562f593b1bdSAndrew Jeffery exact ? "exactly" : ""); 563f593b1bdSAndrew Jeffery 564f593b1bdSAndrew Jeffery /* Search for an uninitialised window, use this before evicting */ 565*17c477a7SAndrew Jeffery cur = windows_search(context, FLASH_OFFSET_UNINIT, true); 566f593b1bdSAndrew Jeffery 567f593b1bdSAndrew Jeffery /* No uninitialised window found, we need to choose one to "evict" */ 568f593b1bdSAndrew Jeffery if (!cur) { 569f593b1bdSAndrew Jeffery MSG_DBG("No uninitialised window, evicting one\n"); 5709412f059SAndrew Jeffery cur = windows_find_oldest(context); 5715dc9f959SAndrew Jeffery window_reset(context, cur); 572f593b1bdSAndrew Jeffery } 573f593b1bdSAndrew Jeffery 574f593b1bdSAndrew Jeffery /* 575f593b1bdSAndrew Jeffery * In case of the virtual pnor, as of now it's possible that a window may 576f593b1bdSAndrew Jeffery * have content less than it's max size. We basically copy one flash partition 577f593b1bdSAndrew Jeffery * per window, and some partitions are smaller than the max size. An offset 578f593b1bdSAndrew Jeffery * right after such a small partition ends should lead to new mapping. The code 579f593b1bdSAndrew Jeffery * below prevents that. 580f593b1bdSAndrew Jeffery */ 581f593b1bdSAndrew Jeffery #ifndef VIRTUAL_PNOR_ENABLED 582f593b1bdSAndrew Jeffery if (!exact) { 583f593b1bdSAndrew Jeffery /* 584f593b1bdSAndrew Jeffery * It would be nice to align the offsets which we map to window 585f593b1bdSAndrew Jeffery * size, this will help prevent overlap which would be an 586f593b1bdSAndrew Jeffery * inefficient use of our reserved memory area (we would like 587f593b1bdSAndrew Jeffery * to "cache" as much of the acutal flash as possible in 588f593b1bdSAndrew Jeffery * memory). If we're protocol V1 however we must ensure the 589f593b1bdSAndrew Jeffery * offset requested is exactly mapped. 590f593b1bdSAndrew Jeffery */ 591f593b1bdSAndrew Jeffery offset &= ~(cur->size - 1); 592f593b1bdSAndrew Jeffery } 593f593b1bdSAndrew Jeffery #endif 594f593b1bdSAndrew Jeffery 595f593b1bdSAndrew Jeffery if (offset > context->flash_size) { 596f593b1bdSAndrew Jeffery MSG_ERR("Tried to open read window past flash limit\n"); 597f593b1bdSAndrew Jeffery return -MBOX_R_PARAM_ERROR; 598f593b1bdSAndrew Jeffery } else if ((offset + cur->size) > context->flash_size) { 599f593b1bdSAndrew Jeffery /* 600f593b1bdSAndrew Jeffery * There is V1 skiboot implementations out there which don't 601f593b1bdSAndrew Jeffery * mask offset with window size, meaning when we have 602f593b1bdSAndrew Jeffery * window size == flash size we will never allow the host to 603f593b1bdSAndrew Jeffery * open a window except at 0x0, which isn't always where the 604f593b1bdSAndrew Jeffery * host requests it. Thus we have to ignore this check and just 605f593b1bdSAndrew Jeffery * hope the host doesn't access past the end of the window 606f593b1bdSAndrew Jeffery * (which it shouldn't) for V1 implementations to get around 607f593b1bdSAndrew Jeffery * this. 608f593b1bdSAndrew Jeffery */ 609f593b1bdSAndrew Jeffery if (context->version == API_VERSION_1) { 610f593b1bdSAndrew Jeffery cur->size = align_down(context->flash_size - offset, 611f593b1bdSAndrew Jeffery 1 << context->block_size_shift); 612f593b1bdSAndrew Jeffery } else { 613f593b1bdSAndrew Jeffery /* 614f593b1bdSAndrew Jeffery * Allow requests to exceed the flash size, but limit 615f593b1bdSAndrew Jeffery * the response to the size of the flash. 616f593b1bdSAndrew Jeffery */ 617f593b1bdSAndrew Jeffery cur->size = context->flash_size - offset; 618f593b1bdSAndrew Jeffery } 619f593b1bdSAndrew Jeffery } 620f593b1bdSAndrew Jeffery 621f593b1bdSAndrew Jeffery /* Copy from flash into the window buffer */ 622f593b1bdSAndrew Jeffery rc = flash_copy(context, offset, cur->mem, cur->size); 623f593b1bdSAndrew Jeffery if (rc < 0) { 624f593b1bdSAndrew Jeffery /* We don't know how much we've copied -> better reset window */ 6255dc9f959SAndrew Jeffery window_reset(context, cur); 626f593b1bdSAndrew Jeffery return rc; 627f593b1bdSAndrew Jeffery } 628f593b1bdSAndrew Jeffery /* 629f593b1bdSAndrew Jeffery * rc isn't guaranteed to be aligned, so align up 630f593b1bdSAndrew Jeffery * 631f593b1bdSAndrew Jeffery * FIXME: This should only be the case for the vpnor ToC now, so handle 632f593b1bdSAndrew Jeffery * it there 633f593b1bdSAndrew Jeffery */ 634f593b1bdSAndrew Jeffery cur->size = align_up(rc, (1ULL << context->block_size_shift)); 635f593b1bdSAndrew Jeffery /* Would like a known value, pick 0xFF to it looks like erased flash */ 636f593b1bdSAndrew Jeffery memset(cur->mem + rc, 0xFF, cur->size - rc); 637f593b1bdSAndrew Jeffery 638f593b1bdSAndrew Jeffery /* 639f593b1bdSAndrew Jeffery * Since for V1 windows aren't constrained to start at multiples of 640f593b1bdSAndrew Jeffery * window size it's possible that something already maps this offset. 641f593b1bdSAndrew Jeffery * Reset any windows which map this offset to avoid coherency problems. 642f593b1bdSAndrew Jeffery * We just have to check for anything which maps the start or the end 643f593b1bdSAndrew Jeffery * of the window since all windows are the same size so another window 644f593b1bdSAndrew Jeffery * cannot map just the middle of this window. 645f593b1bdSAndrew Jeffery */ 646f593b1bdSAndrew Jeffery if (context->version == API_VERSION_1) { 647f593b1bdSAndrew Jeffery uint32_t i; 648f593b1bdSAndrew Jeffery 649f593b1bdSAndrew Jeffery MSG_DBG("Checking for window overlap\n"); 650f593b1bdSAndrew Jeffery 651f593b1bdSAndrew Jeffery for (i = offset; i < (offset + cur->size); i += (cur->size - 1)) { 652f593b1bdSAndrew Jeffery struct window_context *tmp = NULL; 653f593b1bdSAndrew Jeffery do { 654*17c477a7SAndrew Jeffery tmp = windows_search(context, i, false); 655f593b1bdSAndrew Jeffery if (tmp) { 6565dc9f959SAndrew Jeffery window_reset(context, tmp); 657f593b1bdSAndrew Jeffery } 658f593b1bdSAndrew Jeffery } while (tmp); 659f593b1bdSAndrew Jeffery } 660f593b1bdSAndrew Jeffery } 661f593b1bdSAndrew Jeffery 662f593b1bdSAndrew Jeffery /* Clear the bytemap of the window just loaded -> we know it's clean */ 6637d5ada63SAndrew Jeffery window_set_bytemap(context, cur, 0, 664f593b1bdSAndrew Jeffery cur->size >> context->block_size_shift, 665f593b1bdSAndrew Jeffery WINDOW_CLEAN); 666f593b1bdSAndrew Jeffery 667f593b1bdSAndrew Jeffery /* Update so we know what's in the window */ 668f593b1bdSAndrew Jeffery cur->flash_offset = offset; 669f593b1bdSAndrew Jeffery cur->age = ++(context->windows.max_age); 670f593b1bdSAndrew Jeffery *this_window = cur; 671f593b1bdSAndrew Jeffery 672f593b1bdSAndrew Jeffery return 0; 673f593b1bdSAndrew Jeffery } 674