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::filesystem;
39
getPartitionFilePath(int flags)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::owner_write, fs::perm_options::add);
102
103 return dst;
104 }
105
clamp(size_t len)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
read(void * dst,size_t len)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
write(void * dst,size_t len)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