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