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