1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright (C) 2018 IBM Corp. 3 4 #include <fcntl.h> 5 #include <stdint.h> 6 #include <stdlib.h> 7 #include <syslog.h> 8 #include <sys/mman.h> 9 #include <unistd.h> 10 #include <sys/ioctl.h> 11 #include <algorithm> 12 13 extern "C" { 14 #include "common.h" 15 } 16 17 #include "config.h" 18 #include "mboxd_flash.h" 19 #include "mboxd_pnor_partition_table.h" 20 #include "pnor_partition.hpp" 21 #include "pnor_partition_table.hpp" 22 #include "xyz/openbmc_project/Common/error.hpp" 23 #include <phosphor-logging/log.hpp> 24 #include <phosphor-logging/elog-errors.hpp> 25 26 #include <memory> 27 #include <string> 28 #include <exception> 29 #include <stdexcept> 30 31 namespace err = sdbusplus::xyz::openbmc_project::Common::Error; 32 namespace fs = std::experimental::filesystem; 33 namespace vpnor = openpower::virtual_pnor; 34 35 /** @brief unique_ptr functor to release a char* reference. */ 36 struct StringDeleter 37 { 38 void operator()(char* ptr) const 39 { 40 free(ptr); 41 } 42 }; 43 using StringPtr = std::unique_ptr<char, StringDeleter>; 44 45 int init_flash_dev(struct mbox_context* context) 46 { 47 StringPtr filename(get_dev_mtd()); 48 int fd = 0; 49 int rc = 0; 50 51 if (!filename) 52 { 53 MSG_ERR("Couldn't find the flash /dev/mtd partition\n"); 54 return -1; 55 } 56 57 MSG_DBG("Opening %s\n", filename.get()); 58 59 fd = open(filename.get(), O_RDWR); 60 if (fd < 0) 61 { 62 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", filename.get(), 63 strerror(errno)); 64 return -errno; 65 } 66 67 // Read the Flash Info 68 if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1) 69 { 70 MSG_ERR("Couldn't get information about MTD: %s\n", strerror(errno)); 71 close(fd); 72 return -errno; 73 } 74 75 if (context->flash_size == 0) 76 { 77 // See comment in mboxd_flash_physical.c on why 78 // this is needed. 79 context->flash_size = context->mtd_info.size; 80 } 81 82 // Hostboot requires a 4K block-size to be used in the FFS flash structure 83 context->mtd_info.erasesize = 4096; 84 context->erase_size_shift = log_2(context->mtd_info.erasesize); 85 context->flash_bmap = NULL; 86 context->fds[MTD_FD].fd = -1; 87 88 close(fd); 89 return rc; 90 } 91 92 void free_flash_dev(struct mbox_context* context) 93 { 94 // No-op 95 } 96 97 int set_flash_bytemap(struct mbox_context* context, uint32_t offset, 98 uint32_t count, uint8_t val) 99 { 100 // No-op 101 return 0; 102 } 103 104 int erase_flash(struct mbox_context* context, uint32_t offset, uint32_t count) 105 { 106 // No-op 107 return 0; 108 } 109 110 /* 111 * copy_flash() - Copy data from the virtual pnor into a provided buffer 112 * @context: The mbox context pointer 113 * @offset: The pnor offset to copy from (bytes) 114 * @mem: The buffer to copy into (must be of atleast 'size' bytes) 115 * @size: The number of bytes to copy 116 * Return: Number of bytes copied on success, otherwise negative error 117 * code. copy_flash will copy at most 'size' bytes, but it may 118 * copy less. 119 */ 120 int64_t copy_flash(struct mbox_context* context, uint32_t offset, void* mem, 121 uint32_t size) 122 { 123 vpnor::partition::Table* table; 124 int rc = size; 125 126 if (!(context && context->vpnor && context->vpnor->table)) 127 { 128 MSG_ERR("Trying to copy data with uninitialised context!\n"); 129 return -MBOX_R_SYSTEM_ERROR; 130 } 131 132 table = context->vpnor->table; 133 134 MSG_DBG("Copy virtual pnor to %p for size 0x%.8x from offset 0x%.8x\n", mem, 135 size, offset); 136 137 /* The virtual PNOR partition table starts at offset 0 in the virtual 138 * pnor image. Check if host asked for an offset that lies within the 139 * partition table. 140 */ 141 size_t sz = table->size(); 142 if (offset < sz) 143 { 144 const pnor_partition_table& toc = table->getHostTable(); 145 rc = std::min(sz - offset, static_cast<size_t>(size)); 146 memcpy(mem, ((uint8_t*)&toc) + offset, rc); 147 return rc; 148 } 149 150 try 151 { 152 vpnor::Request req(context, offset); 153 rc = req.read(mem, size); 154 } 155 catch (vpnor::UnmappedOffset& e) 156 { 157 /* 158 * Hooo boy. Pretend that this is valid flash so we don't have 159 * discontiguous regions presented to the host. Instead, fill a window 160 * with 0xff so the 'flash' looks erased. Writes to such regions are 161 * dropped on the floor, see the implementation of write_flash() below. 162 */ 163 MSG_INFO("Host requested unmapped region of %" PRId32 164 " bytes at offset 0x%" PRIx32 "\n", 165 size, offset); 166 uint32_t span = e.next - e.base; 167 rc = std::min(size, span); 168 memset(mem, 0xff, rc); 169 } 170 catch (std::exception& e) 171 { 172 MSG_ERR("%s\n", e.what()); 173 phosphor::logging::commit<err::InternalFailure>(); 174 rc = -MBOX_R_SYSTEM_ERROR; 175 } 176 return rc; 177 } 178 179 /* 180 * write_flash() - Write to the virtual pnor from a provided buffer 181 * @context: The mbox context pointer 182 * @offset: The flash offset to write to (bytes) 183 * @buf: The buffer to write from (must be of atleast size) 184 * @size: The number of bytes to write 185 * 186 * Return: 0 on success otherwise negative error code 187 */ 188 189 int write_flash(struct mbox_context* context, uint32_t offset, void* buf, 190 uint32_t count) 191 { 192 193 if (!(context && context->vpnor && context->vpnor->table)) 194 { 195 MSG_ERR("Trying to write data with uninitialised context!\n"); 196 return -MBOX_R_SYSTEM_ERROR; 197 } 198 199 vpnor::partition::Table* table = context->vpnor->table; 200 201 try 202 { 203 const struct pnor_partition& part = table->partition(offset); 204 if (part.data.user.data[1] & PARTITION_READONLY) 205 { 206 /* FIXME: This should be done on CREATE_WRITE_WINDOW, not here */ 207 return -MBOX_R_WRITE_ERROR; 208 } 209 210 MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count, 211 buf); 212 vpnor::Request req(context, offset); 213 req.write(buf, count); 214 } 215 catch (vpnor::UnmappedOffset& e) 216 { 217 /* Paper over the fact that the write isn't persistent */ 218 MSG_INFO("Dropping %d bytes host wrote to unmapped offset 0x%" PRIx32 219 "\n", 220 count, offset); 221 return 0; 222 } 223 catch (const vpnor::OutOfBoundsOffset& e) 224 { 225 MSG_ERR("%s\n", e.what()); 226 return -MBOX_R_PARAM_ERROR; 227 } 228 catch (const std::exception& e) 229 { 230 MSG_ERR("%s\n", e.what()); 231 phosphor::logging::commit<err::InternalFailure>(); 232 return -MBOX_R_SYSTEM_ERROR; 233 } 234 return 0; 235 } 236