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 2826558dbbSAndrew Jeffery #include "mboxd.h" 29f593b1bdSAndrew Jeffery #include "common.h" 30457a6e5fSAndrew Jeffery #include "transport_mbox.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"); 1928eab2151SAndrew Jeffery return -ENOMEM; 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"); 2068eab2151SAndrew Jeffery rc = -ENOMEM; 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); 3258eab2151SAndrew Jeffery return -EPERM; 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 */ 34741c337e8SAndrew Jeffery cur->dirty_bmap = calloc((context->windows.default_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 * @flags: Flags as defined for a close command in the protocol 383f593b1bdSAndrew Jeffery * 384f593b1bdSAndrew Jeffery * This closes the current window. If the host has requested the current window 385f593b1bdSAndrew Jeffery * be closed then we don't need to set the bmc event bit 386f593b1bdSAndrew Jeffery * (set_bmc_event == false), otherwise if the current window has been closed 387f593b1bdSAndrew Jeffery * without the host requesting it the bmc event bit must be set to indicate this 388f593b1bdSAndrew Jeffery * to the host (set_bmc_event == true). 389f593b1bdSAndrew Jeffery */ 390*2ebfd20fSAndrew Jeffery void windows_close_current(struct mbox_context *context, uint8_t flags) 391f593b1bdSAndrew Jeffery { 392f593b1bdSAndrew Jeffery MSG_DBG("Close current window, flags: 0x%.2x\n", flags); 393f593b1bdSAndrew Jeffery 394f593b1bdSAndrew Jeffery if (flags & FLAGS_SHORT_LIFETIME) { 395f593b1bdSAndrew Jeffery context->current->age = 0; 396f593b1bdSAndrew Jeffery } 397f593b1bdSAndrew Jeffery 398f593b1bdSAndrew Jeffery context->current = NULL; 399f593b1bdSAndrew Jeffery context->current_is_write = false; 400f593b1bdSAndrew Jeffery } 401f593b1bdSAndrew Jeffery 402f593b1bdSAndrew Jeffery /* 4035dc9f959SAndrew Jeffery * window_reset() - Reset a window context to a well defined default state 404f593b1bdSAndrew Jeffery * @context: The mbox context pointer 405f593b1bdSAndrew Jeffery * @window: The window to reset 406f593b1bdSAndrew Jeffery */ 4075dc9f959SAndrew Jeffery void window_reset(struct mbox_context *context, struct window_context *window) 408f593b1bdSAndrew Jeffery { 409f593b1bdSAndrew Jeffery window->flash_offset = FLASH_OFFSET_UNINIT; 410f593b1bdSAndrew Jeffery window->size = context->windows.default_size; 411f593b1bdSAndrew Jeffery if (window->dirty_bmap) { /* Might not have been allocated */ 4127d5ada63SAndrew Jeffery window_set_bytemap(context, window, 0, 413f593b1bdSAndrew Jeffery window->size >> context->block_size_shift, 414f593b1bdSAndrew Jeffery WINDOW_CLEAN); 415f593b1bdSAndrew Jeffery } 416f593b1bdSAndrew Jeffery window->age = 0; 417f593b1bdSAndrew Jeffery } 418f593b1bdSAndrew Jeffery 419f593b1bdSAndrew Jeffery /* 420d6a7426eSAndrew Jeffery * windows_reset_all() - Reset all windows to a well defined default state 421f593b1bdSAndrew Jeffery * @context: The mbox context pointer 422*2ebfd20fSAndrew Jeffery * 423*2ebfd20fSAndrew Jeffery * @return True if there was a window open that was closed, false otherwise 424f593b1bdSAndrew Jeffery */ 425*2ebfd20fSAndrew Jeffery bool windows_reset_all(struct mbox_context *context) 426f593b1bdSAndrew Jeffery { 427*2ebfd20fSAndrew Jeffery bool closed = context->current; 428f593b1bdSAndrew Jeffery int i; 429f593b1bdSAndrew Jeffery 430f593b1bdSAndrew Jeffery MSG_DBG("Resetting all windows\n"); 431*2ebfd20fSAndrew Jeffery 432*2ebfd20fSAndrew Jeffery context->windows.max_age = 0; 433*2ebfd20fSAndrew Jeffery 434f593b1bdSAndrew Jeffery /* We might have an open window which needs closing */ 435*2ebfd20fSAndrew Jeffery 436f593b1bdSAndrew Jeffery if (context->current) { 437*2ebfd20fSAndrew Jeffery windows_close_current(context, FLAGS_NONE); 438f593b1bdSAndrew Jeffery } 439*2ebfd20fSAndrew Jeffery 440f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 4415dc9f959SAndrew Jeffery window_reset(context, &context->windows.window[i]); 442f593b1bdSAndrew Jeffery } 443f593b1bdSAndrew Jeffery 444*2ebfd20fSAndrew Jeffery return closed; 445f593b1bdSAndrew Jeffery } 446f593b1bdSAndrew Jeffery 447f593b1bdSAndrew Jeffery /* 4489412f059SAndrew Jeffery * windows_find_oldest() - Find the oldest (Least Recently Used) window 449f593b1bdSAndrew Jeffery * @context: The mbox context pointer 450f593b1bdSAndrew Jeffery * 451f593b1bdSAndrew Jeffery * Return: Pointer to the least recently used window 452f593b1bdSAndrew Jeffery */ 4539412f059SAndrew Jeffery struct window_context *windows_find_oldest(struct mbox_context *context) 454f593b1bdSAndrew Jeffery { 455f593b1bdSAndrew Jeffery struct window_context *oldest = NULL, *cur; 456f593b1bdSAndrew Jeffery uint32_t min_age = context->windows.max_age + 1; 457f593b1bdSAndrew Jeffery int i; 458f593b1bdSAndrew Jeffery 459f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 460f593b1bdSAndrew Jeffery cur = &context->windows.window[i]; 461f593b1bdSAndrew Jeffery 462f593b1bdSAndrew Jeffery if (cur->age < min_age) { 463f593b1bdSAndrew Jeffery min_age = cur->age; 464f593b1bdSAndrew Jeffery oldest = cur; 465f593b1bdSAndrew Jeffery } 466f593b1bdSAndrew Jeffery } 467f593b1bdSAndrew Jeffery 468f593b1bdSAndrew Jeffery return oldest; 469f593b1bdSAndrew Jeffery } 470f593b1bdSAndrew Jeffery 471f593b1bdSAndrew Jeffery /* 472d8c12e1bSAndrew Jeffery * windows_find_largest() - Find the largest window in the window cache 473f593b1bdSAndrew Jeffery * @context: The mbox context pointer 474f593b1bdSAndrew Jeffery * 475f593b1bdSAndrew Jeffery * Return: The largest window 476f593b1bdSAndrew Jeffery */ 477d8c12e1bSAndrew Jeffery struct window_context *windows_find_largest(struct mbox_context *context) 478f593b1bdSAndrew Jeffery { 479f593b1bdSAndrew Jeffery struct window_context *largest = NULL, *cur; 480f593b1bdSAndrew Jeffery uint32_t max_size = 0; 481f593b1bdSAndrew Jeffery int i; 482f593b1bdSAndrew Jeffery 483f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 484f593b1bdSAndrew Jeffery cur = &context->windows.window[i]; 485f593b1bdSAndrew Jeffery 486f593b1bdSAndrew Jeffery if (cur->size > max_size) { 487f593b1bdSAndrew Jeffery max_size = cur->size; 488f593b1bdSAndrew Jeffery largest = cur; 489f593b1bdSAndrew Jeffery } 490f593b1bdSAndrew Jeffery } 491f593b1bdSAndrew Jeffery 492f593b1bdSAndrew Jeffery return largest; 493f593b1bdSAndrew Jeffery } 494f593b1bdSAndrew Jeffery 495f593b1bdSAndrew Jeffery /* 49617c477a7SAndrew Jeffery * windows_search() - Search the window cache for a window containing offset 497f593b1bdSAndrew Jeffery * @context: The mbox context pointer 498f593b1bdSAndrew Jeffery * @offset: Absolute flash offset to search for (bytes) 499f593b1bdSAndrew Jeffery * @exact: If the window must exactly map the requested offset 500f593b1bdSAndrew Jeffery * 501f593b1bdSAndrew Jeffery * This will search the cache of windows for one containing the requested 502f593b1bdSAndrew Jeffery * offset. For V1 of the protocol windows must exactly map the offset since we 503f593b1bdSAndrew Jeffery * can't tell the host how much of its request we actually mapped and it will 504f593b1bdSAndrew Jeffery * thus assume it can access window->size from the offset we give it. 505f593b1bdSAndrew Jeffery * 506f593b1bdSAndrew Jeffery * Return: Pointer to a window containing the requested offset otherwise 507f593b1bdSAndrew Jeffery * NULL 508f593b1bdSAndrew Jeffery */ 50917c477a7SAndrew Jeffery struct window_context *windows_search(struct mbox_context *context, 510f593b1bdSAndrew Jeffery uint32_t offset, bool exact) 511f593b1bdSAndrew Jeffery { 512f593b1bdSAndrew Jeffery struct window_context *cur; 513f593b1bdSAndrew Jeffery int i; 514f593b1bdSAndrew Jeffery 515f593b1bdSAndrew Jeffery MSG_DBG("Searching for window which contains 0x%.8x %s\n", 516f593b1bdSAndrew Jeffery offset, exact ? "exactly" : ""); 517f593b1bdSAndrew Jeffery for (i = 0; i < context->windows.num; i++) { 518f593b1bdSAndrew Jeffery cur = &context->windows.window[i]; 519f593b1bdSAndrew Jeffery if (cur->flash_offset == FLASH_OFFSET_UNINIT) { 520f593b1bdSAndrew Jeffery /* Uninitialised Window */ 521f593b1bdSAndrew Jeffery if (offset == FLASH_OFFSET_UNINIT) { 522f593b1bdSAndrew Jeffery return cur; 523f593b1bdSAndrew Jeffery } 524f593b1bdSAndrew Jeffery continue; 525f593b1bdSAndrew Jeffery } 526f593b1bdSAndrew Jeffery if ((offset >= cur->flash_offset) && 527f593b1bdSAndrew Jeffery (offset < (cur->flash_offset + cur->size))) { 528f593b1bdSAndrew Jeffery if (exact && (cur->flash_offset != offset)) { 529f593b1bdSAndrew Jeffery continue; 530f593b1bdSAndrew Jeffery } 531f593b1bdSAndrew Jeffery /* This window contains the requested offset */ 532f593b1bdSAndrew Jeffery cur->age = ++(context->windows.max_age); 533f593b1bdSAndrew Jeffery return cur; 534f593b1bdSAndrew Jeffery } 535f593b1bdSAndrew Jeffery } 536f593b1bdSAndrew Jeffery 537f593b1bdSAndrew Jeffery return NULL; 538f593b1bdSAndrew Jeffery } 539f593b1bdSAndrew Jeffery 540f593b1bdSAndrew Jeffery /* 541ebbfce5eSAndrew Jeffery * windows_create_map() - Create a window mapping which maps the requested offset 542f593b1bdSAndrew Jeffery * @context: The mbox context pointer 543f593b1bdSAndrew Jeffery * @this_window: A pointer to update to the "new" window 544f593b1bdSAndrew Jeffery * @offset: Absolute flash offset to create a mapping for (bytes) 545f593b1bdSAndrew Jeffery * @exact: If the window must exactly map the requested offset 546f593b1bdSAndrew Jeffery * 547f593b1bdSAndrew Jeffery * This is used to create a window mapping for the requested offset when there 548f593b1bdSAndrew Jeffery * is no existing window in the cache which satisfies the offset. This involves 549f593b1bdSAndrew Jeffery * choosing an existing window from the window cache to evict so we can use it 550f593b1bdSAndrew Jeffery * to store the flash contents from the requested offset, we then point the 551f593b1bdSAndrew Jeffery * caller to that window since it now maps their request. 552f593b1bdSAndrew Jeffery * 553f593b1bdSAndrew Jeffery * Return: 0 on success otherwise negative error code 554f593b1bdSAndrew Jeffery */ 555ebbfce5eSAndrew Jeffery int windows_create_map(struct mbox_context *context, 556f593b1bdSAndrew Jeffery struct window_context **this_window, uint32_t offset, 557f593b1bdSAndrew Jeffery bool exact) 558f593b1bdSAndrew Jeffery { 559f593b1bdSAndrew Jeffery struct window_context *cur = NULL; 560f593b1bdSAndrew Jeffery int rc; 561f593b1bdSAndrew Jeffery 562f593b1bdSAndrew Jeffery MSG_DBG("Creating window which maps 0x%.8x %s\n", offset, 563f593b1bdSAndrew Jeffery exact ? "exactly" : ""); 564f593b1bdSAndrew Jeffery 565f593b1bdSAndrew Jeffery /* Search for an uninitialised window, use this before evicting */ 56617c477a7SAndrew Jeffery cur = windows_search(context, FLASH_OFFSET_UNINIT, true); 567f593b1bdSAndrew Jeffery 568f593b1bdSAndrew Jeffery /* No uninitialised window found, we need to choose one to "evict" */ 569f593b1bdSAndrew Jeffery if (!cur) { 570f593b1bdSAndrew Jeffery MSG_DBG("No uninitialised window, evicting one\n"); 5719412f059SAndrew Jeffery cur = windows_find_oldest(context); 5725dc9f959SAndrew Jeffery window_reset(context, cur); 573f593b1bdSAndrew Jeffery } 574f593b1bdSAndrew Jeffery 575f593b1bdSAndrew Jeffery /* 576f593b1bdSAndrew Jeffery * In case of the virtual pnor, as of now it's possible that a window may 577f593b1bdSAndrew Jeffery * have content less than it's max size. We basically copy one flash partition 578f593b1bdSAndrew Jeffery * per window, and some partitions are smaller than the max size. An offset 579f593b1bdSAndrew Jeffery * right after such a small partition ends should lead to new mapping. The code 580f593b1bdSAndrew Jeffery * below prevents that. 581f593b1bdSAndrew Jeffery */ 582f593b1bdSAndrew Jeffery #ifndef VIRTUAL_PNOR_ENABLED 583f593b1bdSAndrew Jeffery if (!exact) { 584f593b1bdSAndrew Jeffery /* 585f593b1bdSAndrew Jeffery * It would be nice to align the offsets which we map to window 586f593b1bdSAndrew Jeffery * size, this will help prevent overlap which would be an 587f593b1bdSAndrew Jeffery * inefficient use of our reserved memory area (we would like 588f593b1bdSAndrew Jeffery * to "cache" as much of the acutal flash as possible in 589f593b1bdSAndrew Jeffery * memory). If we're protocol V1 however we must ensure the 590f593b1bdSAndrew Jeffery * offset requested is exactly mapped. 591f593b1bdSAndrew Jeffery */ 592f593b1bdSAndrew Jeffery offset &= ~(cur->size - 1); 593f593b1bdSAndrew Jeffery } 594f593b1bdSAndrew Jeffery #endif 595f593b1bdSAndrew Jeffery 596f593b1bdSAndrew Jeffery if (offset > context->flash_size) { 597f593b1bdSAndrew Jeffery MSG_ERR("Tried to open read window past flash limit\n"); 5988eab2151SAndrew Jeffery return -EINVAL; 599f593b1bdSAndrew Jeffery } else if ((offset + cur->size) > context->flash_size) { 600f593b1bdSAndrew Jeffery /* 601f593b1bdSAndrew Jeffery * There is V1 skiboot implementations out there which don't 602f593b1bdSAndrew Jeffery * mask offset with window size, meaning when we have 603f593b1bdSAndrew Jeffery * window size == flash size we will never allow the host to 604f593b1bdSAndrew Jeffery * open a window except at 0x0, which isn't always where the 605f593b1bdSAndrew Jeffery * host requests it. Thus we have to ignore this check and just 606f593b1bdSAndrew Jeffery * hope the host doesn't access past the end of the window 607f593b1bdSAndrew Jeffery * (which it shouldn't) for V1 implementations to get around 608f593b1bdSAndrew Jeffery * this. 609f593b1bdSAndrew Jeffery */ 610f593b1bdSAndrew Jeffery if (context->version == API_VERSION_1) { 611f593b1bdSAndrew Jeffery cur->size = align_down(context->flash_size - offset, 612f593b1bdSAndrew Jeffery 1 << context->block_size_shift); 613f593b1bdSAndrew Jeffery } else { 614f593b1bdSAndrew Jeffery /* 615f593b1bdSAndrew Jeffery * Allow requests to exceed the flash size, but limit 616f593b1bdSAndrew Jeffery * the response to the size of the flash. 617f593b1bdSAndrew Jeffery */ 618f593b1bdSAndrew Jeffery cur->size = context->flash_size - offset; 619f593b1bdSAndrew Jeffery } 620f593b1bdSAndrew Jeffery } 621f593b1bdSAndrew Jeffery 622f593b1bdSAndrew Jeffery /* Copy from flash into the window buffer */ 623f593b1bdSAndrew Jeffery rc = flash_copy(context, offset, cur->mem, cur->size); 624f593b1bdSAndrew Jeffery if (rc < 0) { 625f593b1bdSAndrew Jeffery /* We don't know how much we've copied -> better reset window */ 6265dc9f959SAndrew Jeffery window_reset(context, cur); 627f593b1bdSAndrew Jeffery return rc; 628f593b1bdSAndrew Jeffery } 629f593b1bdSAndrew Jeffery /* 630f593b1bdSAndrew Jeffery * rc isn't guaranteed to be aligned, so align up 631f593b1bdSAndrew Jeffery * 632f593b1bdSAndrew Jeffery * FIXME: This should only be the case for the vpnor ToC now, so handle 633f593b1bdSAndrew Jeffery * it there 634f593b1bdSAndrew Jeffery */ 635f593b1bdSAndrew Jeffery cur->size = align_up(rc, (1ULL << context->block_size_shift)); 636f593b1bdSAndrew Jeffery /* Would like a known value, pick 0xFF to it looks like erased flash */ 637f593b1bdSAndrew Jeffery memset(cur->mem + rc, 0xFF, cur->size - rc); 638f593b1bdSAndrew Jeffery 639f593b1bdSAndrew Jeffery /* 640f593b1bdSAndrew Jeffery * Since for V1 windows aren't constrained to start at multiples of 641f593b1bdSAndrew Jeffery * window size it's possible that something already maps this offset. 642f593b1bdSAndrew Jeffery * Reset any windows which map this offset to avoid coherency problems. 643f593b1bdSAndrew Jeffery * We just have to check for anything which maps the start or the end 644f593b1bdSAndrew Jeffery * of the window since all windows are the same size so another window 645f593b1bdSAndrew Jeffery * cannot map just the middle of this window. 646f593b1bdSAndrew Jeffery */ 647f593b1bdSAndrew Jeffery if (context->version == API_VERSION_1) { 648f593b1bdSAndrew Jeffery uint32_t i; 649f593b1bdSAndrew Jeffery 650f593b1bdSAndrew Jeffery MSG_DBG("Checking for window overlap\n"); 651f593b1bdSAndrew Jeffery 652f593b1bdSAndrew Jeffery for (i = offset; i < (offset + cur->size); i += (cur->size - 1)) { 653f593b1bdSAndrew Jeffery struct window_context *tmp = NULL; 654f593b1bdSAndrew Jeffery do { 65517c477a7SAndrew Jeffery tmp = windows_search(context, i, false); 656f593b1bdSAndrew Jeffery if (tmp) { 6575dc9f959SAndrew Jeffery window_reset(context, tmp); 658f593b1bdSAndrew Jeffery } 659f593b1bdSAndrew Jeffery } while (tmp); 660f593b1bdSAndrew Jeffery } 661f593b1bdSAndrew Jeffery } 662f593b1bdSAndrew Jeffery 663f593b1bdSAndrew Jeffery /* Clear the bytemap of the window just loaded -> we know it's clean */ 6647d5ada63SAndrew Jeffery window_set_bytemap(context, cur, 0, 665f593b1bdSAndrew Jeffery cur->size >> context->block_size_shift, 666f593b1bdSAndrew Jeffery WINDOW_CLEAN); 667f593b1bdSAndrew Jeffery 668f593b1bdSAndrew Jeffery /* Update so we know what's in the window */ 669f593b1bdSAndrew Jeffery cur->flash_offset = offset; 670f593b1bdSAndrew Jeffery cur->age = ++(context->windows.max_age); 671f593b1bdSAndrew Jeffery *this_window = cur; 672f593b1bdSAndrew Jeffery 673f593b1bdSAndrew Jeffery return 0; 674f593b1bdSAndrew Jeffery } 675