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 int lerrno = errno; 151 close(fd); 152 MSG_ERR("Failed to seek to %zu in %s (%zu bytes): %d\n", offset, 153 path.c_str(), fileSize, lerrno); 154 throw std::system_error(lerrno, std::system_category()); 155 } 156 157 access_len = std::min(len, fileSize - offset); 158 rc = fd_process_all(::read, fd, dst, access_len); 159 if (rc < 0) 160 { 161 int lerrno = errno; 162 close(fd); 163 MSG_ERR( 164 "Requested %zu bytes but failed to read %zu from %s (%zu) at " 165 "%zu: %d\n", 166 len, access_len, path.c_str(), fileSize, offset, lerrno); 167 throw std::system_error(lerrno, std::system_category()); 168 } 169 170 close(fd); 171 } 172 173 /* Set any remaining buffer space to the erased state */ 174 memset((char*)dst + access_len, 0xff, len - access_len); 175 176 return len; 177 } 178 179 ssize_t Request::write(void* dst, size_t len) 180 { 181 if (len != clamp(len)) 182 { 183 std::stringstream err; 184 err << "Request size 0x" << std::hex << len << " from offset 0x" 185 << std::hex << offset << " exceeds the partition size 0x" 186 << std::hex << (partition.data.size << backend->block_size_shift); 187 throw OutOfBoundsOffset(err.str()); 188 } 189 190 /* Ensure file is at least the size of the maximum access */ 191 fs::path path = getPartitionFilePath(O_RDWR); 192 193 MSG_INFO("Fulfilling write request against %s at offset 0x%zx from %p " 194 "for %zu\n", 195 path.c_str(), offset, dst, len); 196 197 int fd = ::open(path.c_str(), O_RDWR); 198 if (fd == -1) 199 { 200 MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(), 201 errno); 202 throw std::system_error(errno, std::system_category()); 203 } 204 205 int rc = lseek(fd, offset, SEEK_SET); 206 if (rc < 0) 207 { 208 int lerrno = errno; 209 close(fd); 210 MSG_ERR("Failed to seek to %zu in %s: %d\n", offset, path.c_str(), 211 lerrno); 212 throw std::system_error(lerrno, std::system_category()); 213 } 214 215 rc = fd_process_all(::write, fd, dst, len); 216 if (rc < 0) 217 { 218 int lerrno = errno; 219 close(fd); 220 MSG_ERR("Failed to write %zu bytes to %s at %zu: %d\n", len, 221 path.c_str(), offset, lerrno); 222 throw std::system_error(lerrno, std::system_category()); 223 } 224 backend_set_bytemap(backend, base + offset, len, FLASH_DIRTY); 225 226 close(fd); 227 228 return len; 229 } 230 231 } // namespace virtual_pnor 232 } // namespace openpower 233