lpc_nuvoton: add mapping implementation
Add mapper implementation for Nuvoton. This currently requires /dev/mem
to be enabled. However, that'll be true for this implementation until
all the kernel pieces are in place.
Change-Id: Ia297fcc4d4a556ff8e92253f04f36f4c648ec97f
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/lpc_nuvoton.cpp b/lpc_nuvoton.cpp
index 29567e5..76edb97 100644
--- a/lpc_nuvoton.cpp
+++ b/lpc_nuvoton.cpp
@@ -18,12 +18,21 @@
#include "lpc_interface.hpp"
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <cinttypes>
#include <cstdint>
+#include <cstdio>
#include <memory>
#include <utility>
namespace blobs
{
+using std::uint16_t;
+using std::uint32_t;
+using std::uint8_t;
std::unique_ptr<LpcMapperInterface> LpcMapperNuvoton::createNuvotonMapper()
{
@@ -31,10 +40,92 @@
return std::make_unique<LpcMapperNuvoton>();
}
+/*
+ * The host buffer address is configured by host through
+ * SuperIO. On BMC side the max memory can be mapped is 4kB with the caveat that
+ * first byte of the buffer is reserved as host/BMC semaphore and not usable as
+ * shared memory.
+ *
+ * Mapper returns success for (addr, len) where (addr & 0x7) == 4 and len <=
+ * (4096 - 4). Otherwise, mapper returns either
+ * - WindowOffset = 4 and WindowSize = len - 4 if (addr & 0x7) == 0
+ * - WindowSize = 0 means that the region cannot be mapped otherwise
+ */
std::pair<std::uint32_t, std::uint32_t>
LpcMapperNuvoton::mapWindow(std::uint32_t address, std::uint32_t length)
{
- return std::make_pair(0, 0);
+ /* We reserve the first 4 bytes from the mapped region; the first byte
+ * is shared semaphore, and the number of 4 is for alignment.
+ */
+ const uint32_t bmcMapReserveBytes = 4;
+ const uint32_t bmcMapMaxSizeBytes = 4 * 1024 - bmcMapReserveBytes;
+
+ if (length <= bmcMapReserveBytes)
+ {
+ std::fprintf(stderr, "window size %" PRIx32 " too small to map.\n",
+ length);
+ return std::make_pair(0, 0);
+ }
+
+ if (length > bmcMapMaxSizeBytes)
+ {
+ std::fprintf(stderr,
+ "window size %" PRIx32 " not supported. Max size 4k.\n",
+ length);
+ length = bmcMapMaxSizeBytes;
+ }
+
+ /* If host requested region starts at an aligned address, return offset of 4
+ * bytes so as to skip the semaphore register.
+ */
+ uint32_t windowOffset = bmcMapReserveBytes;
+ uint32_t windowSize = length;
+
+ const uint32_t addressOffset = address & 0x7;
+
+ if (addressOffset == 0)
+ {
+ std::fprintf(stderr, "Map address offset should be 4 for Nuvoton.\n");
+ return std::make_pair(0, 0);
+ }
+ else if (addressOffset != bmcMapReserveBytes)
+ {
+ std::fprintf(stderr, "Map address offset should be 4 for Nuvoton.\n");
+ return std::make_pair(0, 0);
+ }
+
+ /* TODO: need a kernel driver to handle mapping configuration.
+ * Until then program the register through /dev/mem.
+ */
+ int fd;
+ if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
+ {
+ std::fprintf(stderr, "Failed to open /dev/mem\n");
+ close(fd);
+ return std::make_pair(0, 0);
+ }
+
+ const uint32_t bmcMapConfigBaseAddr = 0xc0001000;
+ const uint32_t bmcMapConfigWindowSizeOffset = 0x7;
+ const uint32_t bmcMapConfigWindowBaseOffset = 0xa;
+ const uint8_t bmcWindowSizeValue = 0xc; // 4k
+ const uint16_t bmcWindowBaseValue = 0x8000; // BMC phyAddr from 0xc0008000
+
+ auto mapBasePtr = reinterpret_cast<uint8_t*>(
+ mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ bmcMapConfigBaseAddr));
+
+ uint8_t* bmcWindowSize = mapBasePtr + bmcMapConfigWindowSizeOffset;
+ uint16_t* bmcWindowBase =
+ reinterpret_cast<uint16_t*>(mapBasePtr + bmcMapConfigWindowBaseOffset);
+
+ *bmcWindowSize = bmcWindowSizeValue;
+ *bmcWindowBase = bmcWindowBaseValue;
+
+ munmap(mapBasePtr, getpagesize());
+ close(fd);
+
+ return std::make_pair(windowOffset, windowSize);
}
} // namespace blobs