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