xref: /openbmc/hiomapd/vpnor/partition.cpp (revision 150be912)
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