xref: /openbmc/hiomapd/vpnor/partition.cpp (revision 8cef63e3)
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 void Request::resize(const fs::path& path, size_t len)
107 {
108     size_t maxAccess = offset + len;
109     size_t fileSize = fs::file_size(path);
110     if (maxAccess < fileSize)
111     {
112         return;
113     }
114     MSG_DBG("Resizing %s to %zu bytes\n", path.c_str(), maxAccess);
115     int rc = truncate(path.c_str(), maxAccess);
116     if (rc == -1)
117     {
118         MSG_ERR("Failed to resize %s: %d\n", path.c_str(), errno);
119         throw std::system_error(errno, std::system_category());
120     }
121 }
122 
123 size_t Request::fulfil(const fs::path& path, int flags, void* buf, size_t len)
124 {
125     if (!(flags == O_RDONLY || flags == O_RDWR))
126     {
127         std::stringstream err;
128         err << "Require O_RDONLY (0x" << std::hex << O_RDONLY << " or O_RDWR "
129             << std::hex << O_RDWR << " for flags, got: 0x" << std::hex << flags;
130         throw std::invalid_argument(err.str());
131     }
132 
133     int fd = ::open(path.c_str(), flags);
134     if (fd == -1)
135     {
136         MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(),
137                 errno);
138         throw std::system_error(errno, std::system_category());
139     }
140 
141     if (flags == O_RDONLY)
142     {
143         MSG_INFO("Fulfilling read request against %s at offset 0x%zx into %p "
144                  "for %zu\n",
145                  path.c_str(), offset, buf, len);
146     }
147     else
148     {
149         MSG_INFO("Fulfilling write request against %s at offset 0x%zx from %p "
150                  "for %zu\n",
151                  path.c_str(), offset, buf, len);
152     }
153 
154     size_t fileSize = fs::file_size(path);
155     int mprot = PROT_READ | ((flags == O_RDWR) ? PROT_WRITE : 0);
156     auto map = mmap(NULL, fileSize, mprot, MAP_SHARED, fd, 0);
157     if (map == MAP_FAILED)
158     {
159         close(fd);
160         MSG_ERR("Failed to map backing file '%s' for %zd bytes: %d\n",
161                 path.c_str(), fileSize, errno);
162         throw std::system_error(errno, std::system_category());
163     }
164 
165     // copy to the reserved memory area
166     if (flags == O_RDONLY)
167     {
168         memset(buf, 0xff, len);
169         memcpy(buf, (char*)map + offset, std::min(len, fileSize));
170     }
171     else
172     {
173         memcpy((char*)map + offset, buf, len);
174         backend_set_bytemap(backend, base + offset, len, FLASH_DIRTY);
175     }
176     munmap(map, fileSize);
177     close(fd);
178 
179     return len;
180 }
181 
182 } // namespace virtual_pnor
183 } // namespace openpower
184