xref: /openbmc/hiomapd/vpnor/backend.cpp (revision 2be4563d3f2fbfa67d18a070d387e1653f94d42f)
1f1e547c7SEvan Lojewski // SPDX-License-Identifier: Apache-2.0
2f1e547c7SEvan Lojewski // Copyright (C) 2018 IBM Corp.
3f1e547c7SEvan Lojewski 
4035ad76bSAndrew Jeffery #include "config.h"
5035ad76bSAndrew Jeffery 
6f1e547c7SEvan Lojewski #include <fcntl.h>
7f1e547c7SEvan Lojewski #include <stdint.h>
8f1e547c7SEvan Lojewski #include <stdlib.h>
9f1e547c7SEvan Lojewski #include <sys/ioctl.h>
10f1e547c7SEvan Lojewski #include <sys/mman.h>
11f1e547c7SEvan Lojewski #include <syslog.h>
12f1e547c7SEvan Lojewski #include <unistd.h>
13f1e547c7SEvan Lojewski 
14f1e547c7SEvan Lojewski #include <algorithm>
15f1e547c7SEvan Lojewski 
16f1e547c7SEvan Lojewski extern "C" {
17035ad76bSAndrew Jeffery #include "backend.h"
18f1e547c7SEvan Lojewski #include "common.h"
19f1e547c7SEvan Lojewski #include "lpc.h"
20f1e547c7SEvan Lojewski #include "mboxd.h"
21f1e547c7SEvan Lojewski #include "protocol.h"
22035ad76bSAndrew Jeffery #include "vpnor/backend.h"
23f1e547c7SEvan Lojewski }
24f1e547c7SEvan Lojewski 
25fb01e14eSAndrew Jeffery #include "vpnor/partition.hpp"
26de08ca2dSAndrew Jeffery #include "vpnor/table.hpp"
27f1e547c7SEvan Lojewski #include "xyz/openbmc_project/Common/error.hpp"
28f1e547c7SEvan Lojewski 
29035ad76bSAndrew Jeffery #include <cassert>
30f1e547c7SEvan Lojewski #include <exception>
31150be912SPatrick Williams #include <filesystem>
32f1e547c7SEvan Lojewski #include <memory>
33f1e547c7SEvan Lojewski #include <phosphor-logging/elog-errors.hpp>
34f1e547c7SEvan Lojewski #include <phosphor-logging/log.hpp>
35f1e547c7SEvan Lojewski #include <stdexcept>
36f1e547c7SEvan Lojewski #include <string>
37f1e547c7SEvan Lojewski 
38f4bc335bSAndrew Jeffery #include "vpnor/backend.h"
39f1e547c7SEvan Lojewski 
40f1e547c7SEvan Lojewski namespace err = sdbusplus::xyz::openbmc_project::Common::Error;
41150be912SPatrick Williams namespace fs = std::filesystem;
42f1e547c7SEvan Lojewski namespace vpnor = openpower::virtual_pnor;
43f1e547c7SEvan Lojewski 
44f1e547c7SEvan Lojewski static constexpr uint32_t VPNOR_ERASE_SIZE = 4 * 1024;
45f1e547c7SEvan Lojewski 
vpnor_default_paths(vpnor_partition_paths * paths)46035ad76bSAndrew Jeffery void vpnor_default_paths(vpnor_partition_paths* paths)
47035ad76bSAndrew Jeffery {
48035ad76bSAndrew Jeffery     strncpy(paths->ro_loc, PARTITION_FILES_RO_LOC, PATH_MAX);
49035ad76bSAndrew Jeffery     paths->ro_loc[PATH_MAX - 1] = '\0';
50035ad76bSAndrew Jeffery     strncpy(paths->rw_loc, PARTITION_FILES_RW_LOC, PATH_MAX);
51035ad76bSAndrew Jeffery     paths->rw_loc[PATH_MAX - 1] = '\0';
52035ad76bSAndrew Jeffery     strncpy(paths->prsv_loc, PARTITION_FILES_PRSV_LOC, PATH_MAX);
53035ad76bSAndrew Jeffery     paths->prsv_loc[PATH_MAX - 1] = '\0';
54035ad76bSAndrew Jeffery     strncpy(paths->patch_loc, PARTITION_FILES_PATCH_LOC, PATH_MAX);
55035ad76bSAndrew Jeffery     paths->prsv_loc[PATH_MAX - 1] = '\0';
56035ad76bSAndrew Jeffery }
57035ad76bSAndrew Jeffery 
58035ad76bSAndrew Jeffery /** @brief Create a virtual PNOR partition table.
59035ad76bSAndrew Jeffery  *
60035ad76bSAndrew Jeffery  *  @param[in] backend - The backend context pointer
61035ad76bSAndrew Jeffery  *  @param[in] paths - A paths object pointer to initialise vpnor
62035ad76bSAndrew Jeffery  *
63035ad76bSAndrew Jeffery  *  This API should be called before calling any other APIs below. If a table
64035ad76bSAndrew Jeffery  *  already exists, this function will not do anything further. This function
65035ad76bSAndrew Jeffery  *  will not do anything if the context is NULL.
66035ad76bSAndrew Jeffery  *
67035ad76bSAndrew Jeffery  *  The content of the paths object is copied out, ownership is retained by the
68035ad76bSAndrew Jeffery  *  caller.
69035ad76bSAndrew Jeffery  *
70035ad76bSAndrew Jeffery  *  Returns 0 if the call succeeds, else a negative error code.
71035ad76bSAndrew Jeffery  */
vpnor_init(struct backend * backend,const vpnor_partition_paths * paths)72035ad76bSAndrew Jeffery static int vpnor_init(struct backend* backend,
73035ad76bSAndrew Jeffery                       const vpnor_partition_paths* paths)
74035ad76bSAndrew Jeffery {
75035ad76bSAndrew Jeffery     namespace err = sdbusplus::xyz::openbmc_project::Common::Error;
76150be912SPatrick Williams     namespace fs = std::filesystem;
77035ad76bSAndrew Jeffery     namespace vpnor = openpower::virtual_pnor;
78035ad76bSAndrew Jeffery 
79035ad76bSAndrew Jeffery     vpnor_data* priv = new vpnor_data;
80035ad76bSAndrew Jeffery     assert(priv);
81035ad76bSAndrew Jeffery 
82035ad76bSAndrew Jeffery     priv->paths = *paths;
83035ad76bSAndrew Jeffery     backend->priv = priv;
84035ad76bSAndrew Jeffery 
85035ad76bSAndrew Jeffery     try
86035ad76bSAndrew Jeffery     {
87035ad76bSAndrew Jeffery         priv->vpnor = new vpnor_partition_table;
88*2be4563dSNinad Palsule         // Table object may throw error hence initialize table pointer
89*2be4563dSNinad Palsule         // to null so that no one try to free the junk pointer.
90*2be4563dSNinad Palsule         priv->vpnor->table = NULL;
91035ad76bSAndrew Jeffery         priv->vpnor->table =
92035ad76bSAndrew Jeffery             new openpower::virtual_pnor::partition::Table(backend);
93035ad76bSAndrew Jeffery     }
94035ad76bSAndrew Jeffery     catch (vpnor::TocEntryError& e)
95035ad76bSAndrew Jeffery     {
96b8d89b77SNinad Palsule         MSG_ERR("vpnor init: %s\n", e.what());
97035ad76bSAndrew Jeffery         try
98035ad76bSAndrew Jeffery         {
99035ad76bSAndrew Jeffery             phosphor::logging::commit<err::InternalFailure>();
100035ad76bSAndrew Jeffery         }
101035ad76bSAndrew Jeffery         catch (const std::exception& e)
102035ad76bSAndrew Jeffery         {
103035ad76bSAndrew Jeffery             MSG_ERR("Failed to commit InternalFailure: %s\n", e.what());
104035ad76bSAndrew Jeffery         }
105035ad76bSAndrew Jeffery         return -EINVAL;
106035ad76bSAndrew Jeffery     }
107035ad76bSAndrew Jeffery 
108035ad76bSAndrew Jeffery     return 0;
109035ad76bSAndrew Jeffery }
110035ad76bSAndrew Jeffery 
111035ad76bSAndrew Jeffery /** @brief Copy bootloader partition (alongwith TOC) to LPC memory
112035ad76bSAndrew Jeffery  *
113035ad76bSAndrew Jeffery  *  @param[in] backend - The backend context pointer
114035ad76bSAndrew Jeffery  *
115035ad76bSAndrew Jeffery  *  @returns 0 on success, negative error code on failure
116035ad76bSAndrew Jeffery  */
vpnor_copy_bootloader_partition(const struct backend * backend,void * buf,uint32_t count)117035ad76bSAndrew Jeffery int vpnor_copy_bootloader_partition(const struct backend* backend, void* buf,
118035ad76bSAndrew Jeffery                                     uint32_t count)
119035ad76bSAndrew Jeffery {
120035ad76bSAndrew Jeffery     // The hostboot bootloader has certain size/offset assumptions, so
121035ad76bSAndrew Jeffery     // we need a special partition table here.
122035ad76bSAndrew Jeffery     // It assumes the PNOR is 64M, the TOC size is 32K, the erase block is
123035ad76bSAndrew Jeffery     // 4K, the page size is 4K.
124035ad76bSAndrew Jeffery     // It also assumes the TOC is at the 'end of pnor - toc size - 1 page size'
125035ad76bSAndrew Jeffery     // offset, and first looks for the TOC here, before proceeding to move up
126035ad76bSAndrew Jeffery     // page by page looking for the TOC. So it is optimal to place the TOC at
127035ad76bSAndrew Jeffery     // this offset.
128035ad76bSAndrew Jeffery     constexpr size_t eraseSize = 0x1000;
129035ad76bSAndrew Jeffery     constexpr size_t pageSize = 0x1000;
130035ad76bSAndrew Jeffery     constexpr size_t pnorSize = 0x4000000;
131035ad76bSAndrew Jeffery     constexpr size_t tocMaxSize = 0x8000;
132035ad76bSAndrew Jeffery     constexpr size_t tocStart = pnorSize - tocMaxSize - pageSize;
133035ad76bSAndrew Jeffery     constexpr auto blPartitionName = "HBB";
134035ad76bSAndrew Jeffery 
135035ad76bSAndrew Jeffery     namespace err = sdbusplus::xyz::openbmc_project::Common::Error;
136150be912SPatrick Williams     namespace fs = std::filesystem;
137035ad76bSAndrew Jeffery     namespace vpnor = openpower::virtual_pnor;
138035ad76bSAndrew Jeffery 
139035ad76bSAndrew Jeffery     try
140035ad76bSAndrew Jeffery     {
141035ad76bSAndrew Jeffery         vpnor_partition_table vtbl{};
142035ad76bSAndrew Jeffery         struct vpnor_data priv;
143035ad76bSAndrew Jeffery         struct backend local = *backend;
144035ad76bSAndrew Jeffery 
145035ad76bSAndrew Jeffery         priv.vpnor = &vtbl;
146035ad76bSAndrew Jeffery         priv.paths = ((struct vpnor_data*)backend->priv)->paths;
147035ad76bSAndrew Jeffery         local.priv = &priv;
148035ad76bSAndrew Jeffery         local.block_size_shift = log_2(eraseSize);
149035ad76bSAndrew Jeffery 
150035ad76bSAndrew Jeffery         openpower::virtual_pnor::partition::Table blTable(&local);
151035ad76bSAndrew Jeffery 
152035ad76bSAndrew Jeffery         vtbl.table = &blTable;
153035ad76bSAndrew Jeffery 
154035ad76bSAndrew Jeffery         size_t tocOffset = 0;
155035ad76bSAndrew Jeffery 
156035ad76bSAndrew Jeffery         const pnor_partition& partition = blTable.partition(blPartitionName);
157035ad76bSAndrew Jeffery         size_t hbbOffset = partition.data.base * eraseSize;
158035ad76bSAndrew Jeffery         uint32_t hbbSize = partition.data.actual;
159035ad76bSAndrew Jeffery 
160035ad76bSAndrew Jeffery         if (count < tocStart + blTable.capacity() ||
161035ad76bSAndrew Jeffery             count < hbbOffset + hbbSize)
162035ad76bSAndrew Jeffery         {
163035ad76bSAndrew Jeffery             MSG_ERR("Reserved memory too small for dumb bootstrap\n");
164035ad76bSAndrew Jeffery             return -EINVAL;
165035ad76bSAndrew Jeffery         }
166035ad76bSAndrew Jeffery 
167035ad76bSAndrew Jeffery         uint8_t* buf8 = static_cast<uint8_t*>(buf);
168035ad76bSAndrew Jeffery         backend_copy(&local, tocOffset, buf8 + tocStart, blTable.capacity());
169035ad76bSAndrew Jeffery         backend_copy(&local, hbbOffset, buf8 + hbbOffset, hbbSize);
170035ad76bSAndrew Jeffery     }
171035ad76bSAndrew Jeffery     catch (err::InternalFailure& e)
172035ad76bSAndrew Jeffery     {
173035ad76bSAndrew Jeffery         phosphor::logging::commit<err::InternalFailure>();
174035ad76bSAndrew Jeffery         return -EIO;
175035ad76bSAndrew Jeffery     }
176035ad76bSAndrew Jeffery     catch (vpnor::ReasonedError& e)
177035ad76bSAndrew Jeffery     {
178b8d89b77SNinad Palsule         MSG_ERR("vpnor part copy: %s\n", e.what());
179035ad76bSAndrew Jeffery         phosphor::logging::commit<err::InternalFailure>();
180035ad76bSAndrew Jeffery         return -EIO;
181035ad76bSAndrew Jeffery     }
182035ad76bSAndrew Jeffery 
183035ad76bSAndrew Jeffery     return 0;
184035ad76bSAndrew Jeffery }
185035ad76bSAndrew Jeffery 
vpnor_dev_init(struct backend * backend,void * data)186f1e547c7SEvan Lojewski int vpnor_dev_init(struct backend* backend, void* data)
187f1e547c7SEvan Lojewski {
188f1e547c7SEvan Lojewski     vpnor_partition_paths* paths = (vpnor_partition_paths*)data;
189f1e547c7SEvan Lojewski     struct mtd_info_user mtd_info;
190f1e547c7SEvan Lojewski     const char* filename = NULL;
191f1e547c7SEvan Lojewski     int fd;
192f1e547c7SEvan Lojewski     int rc = 0;
193f1e547c7SEvan Lojewski 
194f1e547c7SEvan Lojewski     if (!(fs::is_directory(fs::status(paths->ro_loc)) &&
195f1e547c7SEvan Lojewski           fs::is_directory(fs::status(paths->rw_loc)) &&
196f1e547c7SEvan Lojewski           fs::is_directory(fs::status(paths->prsv_loc))))
197f1e547c7SEvan Lojewski     {
198b8d89b77SNinad Palsule         MSG_ERR("Couldn't find partition path\n");
199f1e547c7SEvan Lojewski         return -EINVAL;
200f1e547c7SEvan Lojewski     }
201f1e547c7SEvan Lojewski 
202f1e547c7SEvan Lojewski     if (backend->flash_size == 0)
203f1e547c7SEvan Lojewski     {
204f1e547c7SEvan Lojewski         filename = get_dev_mtd();
205f1e547c7SEvan Lojewski 
206f1e547c7SEvan Lojewski         MSG_INFO("No flash size provided, using PNOR MTD size\n");
207f1e547c7SEvan Lojewski 
208f1e547c7SEvan Lojewski         if (!filename)
209f1e547c7SEvan Lojewski         {
210f1e547c7SEvan Lojewski             MSG_ERR("Couldn't find the flash /dev/mtd partition\n");
211f1e547c7SEvan Lojewski             return -errno;
212f1e547c7SEvan Lojewski         }
213f1e547c7SEvan Lojewski 
214f1e547c7SEvan Lojewski         MSG_DBG("Opening %s\n", filename);
215f1e547c7SEvan Lojewski 
216f1e547c7SEvan Lojewski         fd = open(filename, O_RDWR);
217f1e547c7SEvan Lojewski         if (fd < 0)
218f1e547c7SEvan Lojewski         {
219f1e547c7SEvan Lojewski             MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", filename,
220f1e547c7SEvan Lojewski                     strerror(errno));
221f1e547c7SEvan Lojewski             rc = -errno;
222f1e547c7SEvan Lojewski             goto cleanup_filename;
223f1e547c7SEvan Lojewski         }
224f1e547c7SEvan Lojewski 
225f1e547c7SEvan Lojewski         // Read the Flash Info
226f1e547c7SEvan Lojewski         if (ioctl(fd, MEMGETINFO, &mtd_info) == -1)
227f1e547c7SEvan Lojewski         {
228f1e547c7SEvan Lojewski             MSG_ERR("Couldn't get information about MTD: %s\n",
229f1e547c7SEvan Lojewski                     strerror(errno));
230f1e547c7SEvan Lojewski             rc = -errno;
231f1e547c7SEvan Lojewski             goto cleanup_fd;
232f1e547c7SEvan Lojewski         }
233f1e547c7SEvan Lojewski 
234f1e547c7SEvan Lojewski         close(fd);
235f1e547c7SEvan Lojewski         free((void*)filename);
236f1e547c7SEvan Lojewski 
237f1e547c7SEvan Lojewski         // See comment in flash.c on why
238f1e547c7SEvan Lojewski         // this is needed.
239f1e547c7SEvan Lojewski         backend->flash_size = mtd_info.size;
240f1e547c7SEvan Lojewski     }
241f1e547c7SEvan Lojewski 
242f1e547c7SEvan Lojewski     // Hostboot requires a 4K block-size to be used in the FFS flash structure
243f1e547c7SEvan Lojewski     backend->erase_size_shift = log_2(VPNOR_ERASE_SIZE);
244f1e547c7SEvan Lojewski     backend->block_size_shift = backend->erase_size_shift;
245f1e547c7SEvan Lojewski 
246f1e547c7SEvan Lojewski     return vpnor_init(backend, paths);
247f1e547c7SEvan Lojewski 
248f1e547c7SEvan Lojewski cleanup_fd:
249f1e547c7SEvan Lojewski     close(fd);
250f1e547c7SEvan Lojewski 
251f1e547c7SEvan Lojewski cleanup_filename:
252f1e547c7SEvan Lojewski     free((void*)filename);
253f1e547c7SEvan Lojewski 
254f1e547c7SEvan Lojewski     return rc;
255f1e547c7SEvan Lojewski }
256f1e547c7SEvan Lojewski 
vpnor_free(struct backend * backend)257f1e547c7SEvan Lojewski static void vpnor_free(struct backend* backend)
258f1e547c7SEvan Lojewski {
259035ad76bSAndrew Jeffery     struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
260035ad76bSAndrew Jeffery 
261035ad76bSAndrew Jeffery     if (priv)
262035ad76bSAndrew Jeffery     {
263035ad76bSAndrew Jeffery         if (priv->vpnor)
264035ad76bSAndrew Jeffery         {
265035ad76bSAndrew Jeffery             delete priv->vpnor->table;
266035ad76bSAndrew Jeffery         }
267035ad76bSAndrew Jeffery         delete priv->vpnor;
268035ad76bSAndrew Jeffery     }
269035ad76bSAndrew Jeffery     delete priv;
270f1e547c7SEvan Lojewski }
271f1e547c7SEvan Lojewski 
272f1e547c7SEvan Lojewski /*
273f1e547c7SEvan Lojewski  * vpnor_copy() - Copy data from the virtual pnor into a provided buffer
274f1e547c7SEvan Lojewski  * @context:    The backend context pointer
275f1e547c7SEvan Lojewski  * @offset:     The pnor offset to copy from (bytes)
276f1e547c7SEvan Lojewski  * @mem:        The buffer to copy into (must be of atleast 'size' bytes)
277f1e547c7SEvan Lojewski  * @size:       The number of bytes to copy
278f1e547c7SEvan Lojewski  * Return:      Number of bytes copied on success, otherwise negative error
279f1e547c7SEvan Lojewski  *              code. vpnor_copy will copy at most 'size' bytes, but it may
280f1e547c7SEvan Lojewski  *              copy less.
281f1e547c7SEvan Lojewski  */
vpnor_copy(struct backend * backend,uint32_t offset,void * mem,uint32_t size)282f1e547c7SEvan Lojewski static int64_t vpnor_copy(struct backend* backend, uint32_t offset, void* mem,
283f1e547c7SEvan Lojewski                           uint32_t size)
284f1e547c7SEvan Lojewski {
285f1e547c7SEvan Lojewski     struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
286f1e547c7SEvan Lojewski     vpnor::partition::Table* table;
287f1e547c7SEvan Lojewski     int rc = size;
288f1e547c7SEvan Lojewski 
289f1e547c7SEvan Lojewski     if (!(priv->vpnor && priv->vpnor->table))
290f1e547c7SEvan Lojewski     {
291f1e547c7SEvan Lojewski         MSG_ERR("Trying to copy data with uninitialised context!\n");
292f1e547c7SEvan Lojewski         return -EINVAL;
293f1e547c7SEvan Lojewski     }
294f1e547c7SEvan Lojewski 
295f1e547c7SEvan Lojewski     table = priv->vpnor->table;
296f1e547c7SEvan Lojewski 
297f1e547c7SEvan Lojewski     MSG_DBG("Copy virtual pnor to %p for size 0x%.8x from offset 0x%.8x\n", mem,
298f1e547c7SEvan Lojewski             size, offset);
299f1e547c7SEvan Lojewski 
300f1e547c7SEvan Lojewski     /* The virtual PNOR partition table starts at offset 0 in the virtual
301f1e547c7SEvan Lojewski      * pnor image. Check if host asked for an offset that lies within the
302f1e547c7SEvan Lojewski      * partition table.
303f1e547c7SEvan Lojewski      */
304f1e547c7SEvan Lojewski     size_t sz = table->size();
305f1e547c7SEvan Lojewski     if (offset < sz)
306f1e547c7SEvan Lojewski     {
307f1e547c7SEvan Lojewski         const pnor_partition_table& toc = table->getHostTable();
308f1e547c7SEvan Lojewski         rc = std::min(sz - offset, static_cast<size_t>(size));
309f1e547c7SEvan Lojewski         memcpy(mem, ((uint8_t*)&toc) + offset, rc);
310f1e547c7SEvan Lojewski         return rc;
311f1e547c7SEvan Lojewski     }
312f1e547c7SEvan Lojewski 
313f1e547c7SEvan Lojewski     try
314f1e547c7SEvan Lojewski     {
315f1e547c7SEvan Lojewski         vpnor::Request req(backend, offset);
316f1e547c7SEvan Lojewski         rc = req.read(mem, size);
317f1e547c7SEvan Lojewski     }
318f1e547c7SEvan Lojewski     catch (vpnor::UnmappedOffset& e)
319f1e547c7SEvan Lojewski     {
320f1e547c7SEvan Lojewski         /*
321f1e547c7SEvan Lojewski          * Hooo boy. Pretend that this is valid flash so we don't have
322f1e547c7SEvan Lojewski          * discontiguous regions presented to the host. Instead, fill a window
323f1e547c7SEvan Lojewski          * with 0xff so the 'flash' looks erased. Writes to such regions are
324f1e547c7SEvan Lojewski          * dropped on the floor, see the implementation of vpnor_write() below.
325f1e547c7SEvan Lojewski          */
326f1e547c7SEvan Lojewski         MSG_INFO("Host requested unmapped region of %" PRId32
327f1e547c7SEvan Lojewski                  " bytes at offset 0x%" PRIx32 "\n",
328f1e547c7SEvan Lojewski                  size, offset);
329f1e547c7SEvan Lojewski         uint32_t span = e.next - e.base;
330f1e547c7SEvan Lojewski         rc = std::min(size, span);
331f1e547c7SEvan Lojewski         memset(mem, 0xff, rc);
332f1e547c7SEvan Lojewski     }
333f1e547c7SEvan Lojewski     catch (std::exception& e)
334f1e547c7SEvan Lojewski     {
335b8d89b77SNinad Palsule         MSG_ERR("vpnor copy: %s\n", e.what());
336f1e547c7SEvan Lojewski         phosphor::logging::commit<err::InternalFailure>();
337f1e547c7SEvan Lojewski         rc = -EIO;
338f1e547c7SEvan Lojewski     }
339f1e547c7SEvan Lojewski     return rc;
340f1e547c7SEvan Lojewski }
341f1e547c7SEvan Lojewski 
342f1e547c7SEvan Lojewski /*
343f1e547c7SEvan Lojewski  * vpnor_write() - Write to the virtual pnor from a provided buffer
344f1e547c7SEvan Lojewski  * @context: The backend context pointer
345f1e547c7SEvan Lojewski  * @offset:  The flash offset to write to (bytes)
346f1e547c7SEvan Lojewski  * @buf:     The buffer to write from (must be of atleast size)
347f1e547c7SEvan Lojewski  * @size:    The number of bytes to write
348f1e547c7SEvan Lojewski  *
349f1e547c7SEvan Lojewski  * Return:  0 on success otherwise negative error code
350f1e547c7SEvan Lojewski  */
351f1e547c7SEvan Lojewski 
vpnor_write(struct backend * backend,uint32_t offset,void * buf,uint32_t count)352f1e547c7SEvan Lojewski static int vpnor_write(struct backend* backend, uint32_t offset, void* buf,
353f1e547c7SEvan Lojewski                        uint32_t count)
354f1e547c7SEvan Lojewski {
355f1e547c7SEvan Lojewski     assert(backend);
356f1e547c7SEvan Lojewski 
357f1e547c7SEvan Lojewski     struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
358f1e547c7SEvan Lojewski 
359f1e547c7SEvan Lojewski     if (!(priv && priv->vpnor && priv->vpnor->table))
360f1e547c7SEvan Lojewski     {
361f1e547c7SEvan Lojewski         MSG_ERR("Trying to write data with uninitialised context!\n");
362f1e547c7SEvan Lojewski         return -EINVAL;
363f1e547c7SEvan Lojewski     }
364f1e547c7SEvan Lojewski 
365f1e547c7SEvan Lojewski     vpnor::partition::Table* table = priv->vpnor->table;
366f1e547c7SEvan Lojewski 
367f1e547c7SEvan Lojewski     try
368f1e547c7SEvan Lojewski     {
369f1e547c7SEvan Lojewski         const struct pnor_partition& part = table->partition(offset);
370f1e547c7SEvan Lojewski         if (part.data.user.data[1] & PARTITION_READONLY)
371f1e547c7SEvan Lojewski         {
372f1e547c7SEvan Lojewski             MSG_ERR("Unreachable: Host attempted to write to read-only "
373f1e547c7SEvan Lojewski                     "partition %s\n",
374f1e547c7SEvan Lojewski                     part.data.name);
375f1e547c7SEvan Lojewski             return -EPERM;
376f1e547c7SEvan Lojewski         }
377f1e547c7SEvan Lojewski 
378f1e547c7SEvan Lojewski         MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count,
379f1e547c7SEvan Lojewski                 buf);
380f1e547c7SEvan Lojewski         vpnor::Request req(backend, offset);
381f1e547c7SEvan Lojewski         req.write(buf, count);
382f1e547c7SEvan Lojewski     }
383f1e547c7SEvan Lojewski     catch (vpnor::UnmappedOffset& e)
384f1e547c7SEvan Lojewski     {
385f1e547c7SEvan Lojewski         MSG_ERR("Unreachable: Host attempted to write %" PRIu32
386f1e547c7SEvan Lojewski                 " bytes to unmapped offset 0x%" PRIx32 "\n",
387f1e547c7SEvan Lojewski                 count, offset);
388f1e547c7SEvan Lojewski         return -EACCES;
389f1e547c7SEvan Lojewski     }
390f1e547c7SEvan Lojewski     catch (const vpnor::OutOfBoundsOffset& e)
391f1e547c7SEvan Lojewski     {
392b8d89b77SNinad Palsule         MSG_ERR("vpnor write: %s\n", e.what());
393f1e547c7SEvan Lojewski         return -EINVAL;
394f1e547c7SEvan Lojewski     }
395f1e547c7SEvan Lojewski     catch (const std::exception& e)
396f1e547c7SEvan Lojewski     {
397b8d89b77SNinad Palsule         MSG_ERR("vpnor write exception: %s\n", e.what());
398f1e547c7SEvan Lojewski         phosphor::logging::commit<err::InternalFailure>();
399f1e547c7SEvan Lojewski         return -EIO;
400f1e547c7SEvan Lojewski     }
401f1e547c7SEvan Lojewski     return 0;
402f1e547c7SEvan Lojewski }
403f1e547c7SEvan Lojewski 
vpnor_partition_is_readonly(const pnor_partition & part)404f1e547c7SEvan Lojewski static bool vpnor_partition_is_readonly(const pnor_partition& part)
405f1e547c7SEvan Lojewski {
406f1e547c7SEvan Lojewski     return part.data.user.data[1] & PARTITION_READONLY;
407f1e547c7SEvan Lojewski }
408f1e547c7SEvan Lojewski 
vpnor_validate(struct backend * backend,uint32_t offset,uint32_t size,bool ro)409f1e547c7SEvan Lojewski static int vpnor_validate(struct backend* backend, uint32_t offset,
410f1e547c7SEvan Lojewski                           uint32_t size __attribute__((unused)), bool ro)
411f1e547c7SEvan Lojewski {
412f1e547c7SEvan Lojewski     struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
413f1e547c7SEvan Lojewski 
414f1e547c7SEvan Lojewski     /* All reads are allowed */
415f1e547c7SEvan Lojewski     if (ro)
416f1e547c7SEvan Lojewski     {
417f1e547c7SEvan Lojewski         return 0;
418f1e547c7SEvan Lojewski     }
419f1e547c7SEvan Lojewski 
420f1e547c7SEvan Lojewski     /* Only allow write windows on regions mapped by the ToC as writeable */
421f1e547c7SEvan Lojewski     try
422f1e547c7SEvan Lojewski     {
423f1e547c7SEvan Lojewski         const pnor_partition& part = priv->vpnor->table->partition(offset);
424f1e547c7SEvan Lojewski         if (vpnor_partition_is_readonly(part))
425f1e547c7SEvan Lojewski         {
426b8d89b77SNinad Palsule             MSG_ERR("Try to write read only partition (part=%s, offset=0x%x)\n",
4278cef63e3SAlvin Wang                     part.data.name, offset);
428f1e547c7SEvan Lojewski             return -EPERM;
429f1e547c7SEvan Lojewski         }
430f1e547c7SEvan Lojewski     }
431f1e547c7SEvan Lojewski     catch (const openpower::virtual_pnor::UnmappedOffset& e)
432f1e547c7SEvan Lojewski     {
433b8d89b77SNinad Palsule         MSG_ERR("Try to write unmapped area (offset=0x%lx)\n", e.base);
434b8d89b77SNinad Palsule 
435f1e547c7SEvan Lojewski         /*
436f1e547c7SEvan Lojewski          * Writes to unmapped areas are not meaningful, so deny the request.
437f1e547c7SEvan Lojewski          * This removes the ability for a compromised host to abuse unused
438f1e547c7SEvan Lojewski          * space if any data was to be persisted (which it isn't).
439f1e547c7SEvan Lojewski          */
440f1e547c7SEvan Lojewski         return -EACCES;
441f1e547c7SEvan Lojewski     }
442f1e547c7SEvan Lojewski 
443f1e547c7SEvan Lojewski     // Allowed.
444f1e547c7SEvan Lojewski     return 0;
445f1e547c7SEvan Lojewski }
446f1e547c7SEvan Lojewski 
447f1e547c7SEvan Lojewski /*
448f1e547c7SEvan Lojewski  * vpnor_reset() - Reset the lpc bus mapping
449f1e547c7SEvan Lojewski  * @context:     The mbox context pointer
450f1e547c7SEvan Lojewski  *
451f1e547c7SEvan Lojewski  * Return        0 on success otherwise negative error code
452f1e547c7SEvan Lojewski  */
vpnor_reset(struct backend * backend,void * buf,uint32_t count)453f1e547c7SEvan Lojewski static int vpnor_reset(struct backend* backend, void* buf, uint32_t count)
454f1e547c7SEvan Lojewski {
455f1e547c7SEvan Lojewski     const struct vpnor_data* priv = (const struct vpnor_data*)backend->priv;
456f1e547c7SEvan Lojewski     int rc;
457f1e547c7SEvan Lojewski 
458f1e547c7SEvan Lojewski     vpnor_partition_paths paths = priv->paths;
459f1e547c7SEvan Lojewski 
460035ad76bSAndrew Jeffery     vpnor_free(backend);
461f1e547c7SEvan Lojewski 
462f1e547c7SEvan Lojewski     rc = vpnor_init(backend, &paths);
463f1e547c7SEvan Lojewski     if (rc < 0)
464f1e547c7SEvan Lojewski         return rc;
465f1e547c7SEvan Lojewski 
466f1e547c7SEvan Lojewski     rc = vpnor_copy_bootloader_partition(backend, buf, count);
467f1e547c7SEvan Lojewski     if (rc < 0)
468f1e547c7SEvan Lojewski         return rc;
469f1e547c7SEvan Lojewski 
470f1e547c7SEvan Lojewski     return reset_lpc_memory;
471f1e547c7SEvan Lojewski }
472f1e547c7SEvan Lojewski 
4738cef63e3SAlvin Wang /*
4748cef63e3SAlvin Wang  * vpnor_align_offset() - Align the offset
4758cef63e3SAlvin Wang  * @context:    The backend context pointer
4768cef63e3SAlvin Wang  * @offset:	The flash offset
4778cef63e3SAlvin Wang  * @window_size:The window size
4788cef63e3SAlvin Wang  *
4798cef63e3SAlvin Wang  * Return:      0 on success otherwise negative error code
4808cef63e3SAlvin Wang  */
vpnor_align_offset(struct backend * backend,uint32_t * offset,uint32_t window_size)4818cef63e3SAlvin Wang static int vpnor_align_offset(struct backend* backend, uint32_t* offset,
4828cef63e3SAlvin Wang                               uint32_t window_size)
4838cef63e3SAlvin Wang {
4848cef63e3SAlvin Wang     const struct vpnor_data* priv = (const struct vpnor_data*)backend->priv;
4858cef63e3SAlvin Wang 
4868cef63e3SAlvin Wang     /* Adjust the offset to align with the offset of partition base */
4878cef63e3SAlvin Wang     try
4888cef63e3SAlvin Wang     {
4898cef63e3SAlvin Wang         // Get the base of the partition
4908cef63e3SAlvin Wang         const pnor_partition& part = priv->vpnor->table->partition(*offset);
4918cef63e3SAlvin Wang         uint32_t base = part.data.base * VPNOR_ERASE_SIZE;
4928cef63e3SAlvin Wang 
4938cef63e3SAlvin Wang         // Get the base offset relative to the window_size
4948cef63e3SAlvin Wang         uint32_t base_offset = base & (window_size - 1);
4958cef63e3SAlvin Wang 
4968cef63e3SAlvin Wang         // Adjust the offset to align with the base
4978cef63e3SAlvin Wang         *offset = ((*offset - base_offset) & ~(window_size - 1)) + base_offset;
4988cef63e3SAlvin Wang         MSG_DBG(
4998cef63e3SAlvin Wang             "vpnor_align_offset: to @ 0x%.8x(base=0x%.8x base_offset=0x%.8x)\n",
5008cef63e3SAlvin Wang             *offset, base, base_offset);
5018cef63e3SAlvin Wang         return 0;
5028cef63e3SAlvin Wang     }
5038cef63e3SAlvin Wang     catch (const openpower::virtual_pnor::UnmappedOffset& e)
5048cef63e3SAlvin Wang     {
505b8d89b77SNinad Palsule         MSG_ERR("Aligned offset is unmapped area (offset=0x%lx)\n", e.base);
506b8d89b77SNinad Palsule 
5078cef63e3SAlvin Wang         /*
5088cef63e3SAlvin Wang          * Writes to unmapped areas are not meaningful, so deny the request.
5098cef63e3SAlvin Wang          * This removes the ability for a compromised host to abuse unused
5108cef63e3SAlvin Wang          * space if any data was to be persisted (which it isn't).
5118cef63e3SAlvin Wang          */
5128cef63e3SAlvin Wang         return -EACCES;
5138cef63e3SAlvin Wang     }
5148cef63e3SAlvin Wang }
5158cef63e3SAlvin Wang 
516f1e547c7SEvan Lojewski static const struct backend_ops vpnor_ops = {
517f1e547c7SEvan Lojewski     .init = vpnor_dev_init,
518f1e547c7SEvan Lojewski     .free = vpnor_free,
519f1e547c7SEvan Lojewski     .copy = vpnor_copy,
520f1e547c7SEvan Lojewski     .set_bytemap = NULL,
521f1e547c7SEvan Lojewski     .erase = NULL,
522f1e547c7SEvan Lojewski     .write = vpnor_write,
523f1e547c7SEvan Lojewski     .validate = vpnor_validate,
524f1e547c7SEvan Lojewski     .reset = vpnor_reset,
5258cef63e3SAlvin Wang     .align_offset = vpnor_align_offset,
526f1e547c7SEvan Lojewski };
527f1e547c7SEvan Lojewski 
backend_get_vpnor(void)528f1e547c7SEvan Lojewski struct backend backend_get_vpnor(void)
529f1e547c7SEvan Lojewski {
53068a24c9eSPatrick Williams     struct backend be = {nullptr, nullptr, 0, 0, 0};
531f1e547c7SEvan Lojewski 
532f1e547c7SEvan Lojewski     be.ops = &vpnor_ops;
533f1e547c7SEvan Lojewski 
534f1e547c7SEvan Lojewski     return be;
535f1e547c7SEvan Lojewski }
536f1e547c7SEvan Lojewski 
backend_probe_vpnor(struct backend * master,const struct vpnor_partition_paths * paths)537f1e547c7SEvan Lojewski int backend_probe_vpnor(struct backend* master,
538f1e547c7SEvan Lojewski                         const struct vpnor_partition_paths* paths)
539f1e547c7SEvan Lojewski {
540f1e547c7SEvan Lojewski     struct backend with;
541f1e547c7SEvan Lojewski 
542f1e547c7SEvan Lojewski     assert(master);
543f1e547c7SEvan Lojewski     with = backend_get_vpnor();
544f1e547c7SEvan Lojewski 
545f1e547c7SEvan Lojewski     return backend_init(master, &with, (void*)paths);
546f1e547c7SEvan Lojewski }
547