1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright (C) 2018 IBM Corp. 3 extern "C" { 4 #include "mboxd.h" 5 } 6 7 #include "config.h" 8 9 #include "vpnor/partition.hpp" 10 #include "vpnor/table.hpp" 11 #include "xyz/openbmc_project/Common/error.hpp" 12 13 #include <assert.h> 14 #include <fcntl.h> 15 #include <stdint.h> 16 #include <stdlib.h> 17 #include <sys/ioctl.h> 18 #include <sys/mman.h> 19 #include <sys/types.h> 20 #include <syslog.h> 21 #include <unistd.h> 22 23 #include <exception> 24 #include <iostream> 25 #include <phosphor-logging/elog-errors.hpp> 26 #include <phosphor-logging/log.hpp> 27 #include <stdexcept> 28 #include <string> 29 30 #include "common.h" 31 #include "vpnor/backend.h" 32 33 namespace openpower 34 { 35 namespace virtual_pnor 36 { 37 38 namespace fs = std::experimental::filesystem; 39 40 fs::path Request::getPartitionFilePath(int flags) 41 { 42 struct vpnor_data* priv = (struct vpnor_data*)backend->priv; 43 44 // Check if partition exists in patch location 45 auto dst = fs::path(priv->paths.patch_loc) / partition.data.name; 46 if (fs::is_regular_file(dst)) 47 { 48 return dst; 49 } 50 51 switch (partition.data.user.data[1] & 52 (PARTITION_PRESERVED | PARTITION_READONLY)) 53 { 54 case PARTITION_PRESERVED: 55 dst = priv->paths.prsv_loc; 56 break; 57 58 case PARTITION_READONLY: 59 dst = priv->paths.ro_loc; 60 break; 61 62 default: 63 dst = priv->paths.rw_loc; 64 } 65 dst /= partition.data.name; 66 67 if (fs::exists(dst)) 68 { 69 return dst; 70 } 71 72 if (flags == O_RDONLY) 73 { 74 dst = fs::path(priv->paths.ro_loc) / partition.data.name; 75 assert(fs::exists(dst)); 76 return dst; 77 } 78 79 assert(flags == O_RDWR); 80 auto src = fs::path(priv->paths.ro_loc) / partition.data.name; 81 assert(fs::exists(src)); 82 83 MSG_DBG("RWRequest: Didn't find '%s' under '%s', copying from '%s'\n", 84 partition.data.name, dst.c_str(), src.c_str()); 85 86 dst = priv->paths.rw_loc; 87 if (partition.data.user.data[1] & PARTITION_PRESERVED) 88 { 89 dst = priv->paths.prsv_loc; 90 } 91 92 dst /= partition.data.name; 93 fs::copy_file(src, dst); 94 fs::permissions(dst, fs::perms::add_perms | fs::perms::owner_write); 95 96 return dst; 97 } 98 99 size_t Request::clamp(size_t len) 100 { 101 size_t maxAccess = offset + len; 102 size_t partSize = partition.data.size << backend->block_size_shift; 103 return std::min(maxAccess, partSize) - offset; 104 } 105 106 /* Perform reads and writes for an entire buffer's worth of data 107 * 108 * Post-condition: All bytes read or written, or an error has occurred 109 * 110 * Yields 0 if the entire buffer was processed, otherwise -1 111 */ 112 #define fd_process_all(fn, fd, src, len) \ 113 ({ \ 114 size_t __len = len; \ 115 ssize_t __accessed = 0; \ 116 do \ 117 { \ 118 __len -= __accessed; \ 119 __accessed = TEMP_FAILURE_RETRY(fn(fd, src, __len)); \ 120 } while (__len > 0 && __accessed > 0); \ 121 __len ? -1 : 0; \ 122 }) 123 124 ssize_t Request::read(void* dst, size_t len) 125 { 126 len = clamp(len); 127 128 fs::path path = getPartitionFilePath(O_RDONLY); 129 130 MSG_INFO("Fulfilling read request against %s at offset 0x%zx into %p " 131 "for %zu\n", 132 path.c_str(), offset, dst, len); 133 134 size_t fileSize = fs::file_size(path); 135 136 size_t access_len = 0; 137 if (offset < fileSize) 138 { 139 int fd = ::open(path.c_str(), O_RDONLY); 140 if (fd == -1) 141 { 142 MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(), 143 errno); 144 throw std::system_error(errno, std::system_category()); 145 } 146 147 int rc = lseek(fd, offset, SEEK_SET); 148 if (rc < 0) 149 { 150 MSG_ERR("Failed to seek to %zu in %s (%zu bytes): %d\n", offset, 151 path.c_str(), fileSize, errno); 152 throw std::system_error(errno, std::system_category()); 153 } 154 155 access_len = std::min(len, fileSize - offset); 156 rc = fd_process_all(::read, fd, dst, access_len); 157 if (rc < 0) 158 { 159 MSG_ERR( 160 "Requested %zu bytes but failed to read %zu from %s (%zu) at " 161 "%zu: %d\n", 162 len, access_len, path.c_str(), fileSize, offset, errno); 163 throw std::system_error(errno, std::system_category()); 164 } 165 166 close(fd); 167 } 168 169 /* Set any remaining buffer space to the erased state */ 170 memset((char*)dst + access_len, 0xff, len - access_len); 171 172 return len; 173 } 174 175 ssize_t Request::write(void* dst, size_t len) 176 { 177 if (len != clamp(len)) 178 { 179 std::stringstream err; 180 err << "Request size 0x" << std::hex << len << " from offset 0x" 181 << std::hex << offset << " exceeds the partition size 0x" 182 << std::hex << (partition.data.size << backend->block_size_shift); 183 throw OutOfBoundsOffset(err.str()); 184 } 185 186 /* Ensure file is at least the size of the maximum access */ 187 fs::path path = getPartitionFilePath(O_RDWR); 188 189 MSG_INFO("Fulfilling write request against %s at offset 0x%zx from %p " 190 "for %zu\n", 191 path.c_str(), offset, dst, len); 192 193 int fd = ::open(path.c_str(), O_RDWR); 194 if (fd == -1) 195 { 196 MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(), 197 errno); 198 throw std::system_error(errno, std::system_category()); 199 } 200 201 int rc = lseek(fd, offset, SEEK_SET); 202 if (rc < 0) 203 { 204 MSG_ERR("Failed to seek to %zu in %s: %d\n", offset, path.c_str(), 205 errno); 206 throw std::system_error(errno, std::system_category()); 207 } 208 209 rc = fd_process_all(::write, fd, dst, len); 210 if (rc < 0) 211 { 212 MSG_ERR("Failed to write %zu bytes to %s at %zu: %d\n", len, 213 path.c_str(), offset, errno); 214 throw std::system_error(errno, std::system_category()); 215 } 216 backend_set_bytemap(backend, base + offset, len, FLASH_DIRTY); 217 218 close(fd); 219 220 return len; 221 } 222 223 } // namespace virtual_pnor 224 } // namespace openpower 225