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 // Check if partition exists in rw location (default) 52 dst = fs::path(priv->paths.rw_loc) / partition.data.name; 53 if (fs::exists(dst)) 54 { 55 return dst; 56 } 57 58 switch (partition.data.user.data[1] & 59 (PARTITION_PRESERVED | PARTITION_READONLY)) 60 { 61 case PARTITION_PRESERVED: 62 dst = priv->paths.prsv_loc; 63 break; 64 65 case PARTITION_READONLY: 66 dst = priv->paths.ro_loc; 67 break; 68 69 default: 70 dst = priv->paths.rw_loc; 71 } 72 dst /= partition.data.name; 73 74 if (fs::exists(dst)) 75 { 76 return dst; 77 } 78 79 if (flags == O_RDONLY) 80 { 81 dst = fs::path(priv->paths.ro_loc) / partition.data.name; 82 assert(fs::exists(dst)); 83 return dst; 84 } 85 86 assert(flags == O_RDWR); 87 auto src = fs::path(priv->paths.ro_loc) / partition.data.name; 88 assert(fs::exists(src)); 89 90 MSG_DBG("RWRequest: Didn't find '%s' under '%s', copying from '%s'\n", 91 partition.data.name, dst.c_str(), src.c_str()); 92 93 dst = priv->paths.rw_loc; 94 if (partition.data.user.data[1] & PARTITION_PRESERVED) 95 { 96 dst = priv->paths.prsv_loc; 97 } 98 99 dst /= partition.data.name; 100 fs::copy_file(src, dst); 101 fs::permissions(dst, fs::perms::add_perms | fs::perms::owner_write); 102 103 return dst; 104 } 105 106 size_t Request::clamp(size_t len) 107 { 108 size_t maxAccess = offset + len; 109 size_t partSize = partition.data.size << backend->block_size_shift; 110 return std::min(maxAccess, partSize) - offset; 111 } 112 113 /* Perform reads and writes for an entire buffer's worth of data 114 * 115 * Post-condition: All bytes read or written, or an error has occurred 116 * 117 * Yields 0 if the entire buffer was processed, otherwise -1 118 */ 119 #define fd_process_all(fn, fd, src, len) \ 120 ({ \ 121 size_t __len = len; \ 122 ssize_t __accessed = 0; \ 123 do \ 124 { \ 125 __len -= __accessed; \ 126 __accessed = TEMP_FAILURE_RETRY(fn(fd, src, __len)); \ 127 } while (__len > 0 && __accessed > 0); \ 128 __len ? -1 : 0; \ 129 }) 130 131 ssize_t Request::read(void* dst, size_t len) 132 { 133 len = clamp(len); 134 135 fs::path path = getPartitionFilePath(O_RDONLY); 136 137 MSG_INFO("Fulfilling read request against %s at offset 0x%zx into %p " 138 "for %zu\n", 139 path.c_str(), offset, dst, len); 140 141 size_t fileSize = fs::file_size(path); 142 143 size_t access_len = 0; 144 if (offset < fileSize) 145 { 146 int fd = ::open(path.c_str(), O_RDONLY); 147 if (fd == -1) 148 { 149 MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(), 150 errno); 151 throw std::system_error(errno, std::system_category()); 152 } 153 154 int rc = lseek(fd, offset, SEEK_SET); 155 if (rc < 0) 156 { 157 int lerrno = errno; 158 close(fd); 159 MSG_ERR("Failed to seek to %zu in %s (%zu bytes): %d\n", offset, 160 path.c_str(), fileSize, lerrno); 161 throw std::system_error(lerrno, std::system_category()); 162 } 163 164 access_len = std::min(len, fileSize - offset); 165 rc = fd_process_all(::read, fd, dst, access_len); 166 if (rc < 0) 167 { 168 int lerrno = errno; 169 close(fd); 170 MSG_ERR( 171 "Requested %zu bytes but failed to read %zu from %s (%zu) at " 172 "%zu: %d\n", 173 len, access_len, path.c_str(), fileSize, offset, lerrno); 174 throw std::system_error(lerrno, std::system_category()); 175 } 176 177 close(fd); 178 } 179 180 /* Set any remaining buffer space to the erased state */ 181 memset((char*)dst + access_len, 0xff, len - access_len); 182 183 return len; 184 } 185 186 ssize_t Request::write(void* dst, size_t len) 187 { 188 if (len != clamp(len)) 189 { 190 std::stringstream err; 191 err << "Request size 0x" << std::hex << len << " from offset 0x" 192 << std::hex << offset << " exceeds the partition size 0x" 193 << std::hex << (partition.data.size << backend->block_size_shift); 194 throw OutOfBoundsOffset(err.str()); 195 } 196 197 /* Ensure file is at least the size of the maximum access */ 198 fs::path path = getPartitionFilePath(O_RDWR); 199 200 MSG_INFO("Fulfilling write request against %s at offset 0x%zx from %p " 201 "for %zu\n", 202 path.c_str(), offset, dst, len); 203 204 int fd = ::open(path.c_str(), O_RDWR); 205 if (fd == -1) 206 { 207 MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(), 208 errno); 209 throw std::system_error(errno, std::system_category()); 210 } 211 212 int rc = lseek(fd, offset, SEEK_SET); 213 if (rc < 0) 214 { 215 int lerrno = errno; 216 close(fd); 217 MSG_ERR("Failed to seek to %zu in %s: %d\n", offset, path.c_str(), 218 lerrno); 219 throw std::system_error(lerrno, std::system_category()); 220 } 221 222 rc = fd_process_all(::write, fd, dst, len); 223 if (rc < 0) 224 { 225 int lerrno = errno; 226 close(fd); 227 MSG_ERR("Failed to write %zu bytes to %s at %zu: %d\n", len, 228 path.c_str(), offset, lerrno); 229 throw std::system_error(lerrno, std::system_category()); 230 } 231 backend_set_bytemap(backend, base + offset, len, FLASH_DIRTY); 232 233 close(fd); 234 235 return len; 236 } 237 238 } // namespace virtual_pnor 239 } // namespace openpower 240