1 /* 2 * Copyright 2018 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "lpc_nuvoton.hpp" 18 19 #include "mapper_errors.hpp" 20 #include "window_hw_interface.hpp" 21 22 #include <fcntl.h> 23 #include <sys/mman.h> 24 25 #include <cerrno> 26 #include <cinttypes> 27 #include <cstdint> 28 #include <cstdio> 29 #include <memory> 30 #include <utility> 31 #include <vector> 32 33 namespace ipmi_flash 34 { 35 using std::uint16_t; 36 using std::uint32_t; 37 using std::uint8_t; 38 39 std::unique_ptr<HardwareMapperInterface> 40 LpcMapperNuvoton::createNuvotonMapper(std::uint32_t regionAddress, 41 std::uint32_t regionSize) 42 { 43 /* NOTE: Considered making one factory for both types. */ 44 return std::make_unique<LpcMapperNuvoton>(regionAddress, regionSize); 45 } 46 47 MemorySet LpcMapperNuvoton::open() 48 { 49 static constexpr auto devmem = "/dev/mem"; 50 51 mappedFd = sys->open(devmem, O_RDWR | O_SYNC); 52 if (mappedFd == -1) 53 { 54 throw MapperException("Unable to open /dev/mem"); 55 } 56 57 mapped = reinterpret_cast<uint8_t*>(sys->mmap( 58 0, memoryRegionSize, PROT_READ, MAP_SHARED, mappedFd, regionAddress)); 59 if (mapped == MAP_FAILED) 60 { 61 sys->close(mappedFd); 62 mappedFd = -1; 63 mapped = nullptr; 64 65 throw MapperException("Unable to map region"); 66 } 67 68 MemorySet output; 69 output.mappedFd = mappedFd; 70 output.mapped = mapped; 71 72 return output; 73 } 74 75 void LpcMapperNuvoton::close() 76 { 77 if (mapped) 78 { 79 sys->munmap(mapped, memoryRegionSize); 80 mapped = nullptr; 81 } 82 83 if (mappedFd != -1) 84 { 85 sys->close(mappedFd); 86 mappedFd = -1; 87 } 88 } 89 90 /* 91 * The host buffer address is configured by host through 92 * SuperIO. On BMC side the max memory can be mapped is 4kB with the caveat that 93 * first byte of the buffer is reserved as host/BMC semaphore and not usable as 94 * shared memory. 95 * 96 * Mapper returns success for (addr, len) where (addr & 0x7) == 4 and len <= 97 * (4096 - 4). Otherwise, mapper returns either 98 * - WindowOffset = 4 and WindowSize = len - 4 if (addr & 0x7) == 0 99 * - WindowSize = 0 means that the region cannot be mapped otherwise 100 */ 101 WindowMapResult LpcMapperNuvoton::mapWindow(std::uint32_t address, 102 std::uint32_t length) 103 { 104 WindowMapResult result = {}; 105 106 /* We reserve the first 4 bytes from the mapped region; the first byte 107 * is shared semaphore, and the number of 4 is for alignment. 108 */ 109 const uint32_t bmcMapReserveBytes = 4; 110 const uint32_t bmcMapMaxSizeBytes = 4 * 1024 - bmcMapReserveBytes; 111 112 if (length <= bmcMapReserveBytes) 113 { 114 std::fprintf(stderr, "window size %" PRIx32 " too small to map.\n", 115 length); 116 result.response = EINVAL; 117 return result; 118 } 119 120 if (length > bmcMapMaxSizeBytes) 121 { 122 std::fprintf(stderr, 123 "window size %" PRIx32 " not supported. Max size 4k.\n", 124 length); 125 length = bmcMapMaxSizeBytes; 126 } 127 128 /* If host requested region starts at an aligned address, return offset of 4 129 * bytes so as to skip the semaphore register. 130 */ 131 uint32_t windowOffset = bmcMapReserveBytes; 132 // uint32_t windowSize = length; 133 134 result.response = 0; 135 result.windowOffset = windowOffset; 136 result.windowSize = length; 137 138 const uint32_t addressOffset = address & 0x7; 139 140 if (addressOffset == 0) 141 { 142 std::fprintf(stderr, "Map address offset should be 4 for Nuvoton.\n"); 143 144 result.response = EFBIG; 145 return result; 146 } 147 else if (addressOffset != bmcMapReserveBytes) 148 { 149 std::fprintf(stderr, "Map address offset should be 4 for Nuvoton.\n"); 150 151 result.response = EINVAL; 152 return result; 153 } 154 155 /* TODO: need a kernel driver to handle mapping configuration. 156 * Until then program the register through /dev/mem. 157 */ 158 int fd; 159 if ((fd = sys->open("/dev/mem", O_RDWR | O_SYNC)) == -1) 160 { 161 std::fprintf(stderr, "Failed to open /dev/mem\n"); 162 sys->close(fd); 163 164 result.response = EINVAL; 165 return result; 166 } 167 168 const uint32_t bmcMapConfigBaseAddr = 0xc0001000; 169 const uint32_t bmcMapConfigWindowSizeOffset = 0x7; 170 const uint32_t bmcMapConfigWindowBaseOffset = 0xa; 171 const uint8_t bmcWindowSizeValue = 0xc; // 4k 172 const uint16_t bmcWindowBaseValue = 0x8000; // BMC phyAddr from 0xc0008000 173 174 int pageSize = sys->getpagesize(); 175 176 auto mapBasePtr = reinterpret_cast<uint8_t*>( 177 sys->mmap(nullptr, pageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 178 bmcMapConfigBaseAddr)); 179 180 uint8_t* bmcWindowSize = mapBasePtr + bmcMapConfigWindowSizeOffset; 181 uint16_t* bmcWindowBase = 182 reinterpret_cast<uint16_t*>(mapBasePtr + bmcMapConfigWindowBaseOffset); 183 184 *bmcWindowSize = bmcWindowSizeValue; 185 *bmcWindowBase = bmcWindowBaseValue; 186 187 sys->munmap(mapBasePtr, pageSize); 188 sys->close(fd); 189 190 return result; 191 } 192 193 } // namespace ipmi_flash 194