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_aspeed.hpp"
18 
19 #include "mapper_errors.hpp"
20 #include "window_hw_interface.hpp"
21 
22 #include <fcntl.h>
23 #include <linux/aspeed-lpc-ctrl.h>
24 #include <linux/kernel.h>
25 
26 #include <cerrno>
27 #include <cstdint>
28 #include <cstring>
29 #include <memory>
30 #include <string>
31 #include <utility>
32 
33 namespace ipmi_flash
34 {
35 
36 const std::string LpcMapperAspeed::lpcControlPath = "/dev/aspeed-lpc-ctrl";
37 
createAspeedMapper(std::uint32_t regionAddress,std::size_t regionSize)38 std::unique_ptr<HardwareMapperInterface> LpcMapperAspeed::createAspeedMapper(
39     std::uint32_t regionAddress, std::size_t regionSize)
40 {
41     /* NOTE: considered using a joint factory to create one or the other, for
42      * now, separate factories.
43      */
44     return std::make_unique<LpcMapperAspeed>(regionAddress, regionSize);
45 }
46 
close()47 void LpcMapperAspeed::close()
48 {
49     if (mappedRegion)
50     {
51         sys->munmap(mappedRegion, regionSize);
52         mappedRegion = nullptr;
53     }
54 
55     if (mappedFd != -1)
56     {
57         sys->close(mappedFd);
58         mappedFd = -1;
59     }
60 }
61 
mapWindow(std::uint32_t address,std::uint32_t length)62 WindowMapResult LpcMapperAspeed::mapWindow(std::uint32_t address,
63                                            std::uint32_t length)
64 {
65     WindowMapResult result = {};
66     static const std::uint32_t MASK_64K = 0xFFFFU;
67     const std::uint32_t offset = address & MASK_64K;
68 
69     if (offset + length > regionSize)
70     {
71         std::fprintf(stderr,
72                      "requested window size %" PRIu32 ", offset %#" PRIx32
73                      " is too large for mem region"
74                      " of size %zu\n",
75                      length, offset, regionSize);
76 
77         result.response = EFBIG;
78         result.windowSize = regionSize - offset;
79         return result;
80     }
81 
82     struct aspeed_lpc_ctrl_mapping map = {
83         .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
84         .window_id = 0,
85         .flags = 0,
86         .addr = address & ~MASK_64K,
87         .offset = 0,
88         .size = __ALIGN_KERNEL_MASK(offset + length, MASK_64K),
89     };
90 
91     std::fprintf(stderr,
92                  "requesting Aspeed LPC window at %#" PRIx32 " of size %" PRIu32
93                  "\n",
94                  map.addr, map.size);
95 
96     const auto lpcControlFd = sys->open(lpcControlPath.c_str(), O_RDWR);
97     if (lpcControlFd == -1)
98     {
99         std::fprintf(stderr,
100                      "cannot open Aspeed LPC kernel control dev \"%s\"\n",
101                      lpcControlPath.c_str());
102 
103         result.response = EINVAL;
104         return result;
105     }
106 
107     if (sys->ioctl(lpcControlFd, ASPEED_LPC_CTRL_IOCTL_MAP, &map) == -1)
108     {
109         std::fprintf(stderr, "Failed to ioctl Aspeed LPC map with error %s\n",
110                      std::strerror(errno));
111         sys->close(lpcControlFd);
112 
113         result.response = EINVAL;
114         return result;
115     }
116 
117     sys->close(lpcControlFd);
118 
119     result.response = 0;
120     result.windowOffset = offset;
121     result.windowSize = length;
122     return result;
123 }
124 
open()125 MemorySet LpcMapperAspeed::open()
126 {
127     if (mapRegion())
128     {
129         MemorySet output;
130         output.mappedFd = mappedFd;
131         output.mapped = mappedRegion;
132         return output;
133     }
134 
135     throw MapperException("Unable to memory-map region");
136 }
137 
mapRegion()138 bool LpcMapperAspeed::mapRegion()
139 {
140     /* Open the file to map. */
141     mappedFd = sys->open(lpcControlPath.c_str(), O_RDONLY | O_SYNC);
142     if (mappedFd == -1)
143     {
144         std::fprintf(stderr, "ipmiflash: unable to open %s\n",
145                      lpcControlPath.c_str());
146         return false;
147     }
148 
149     mappedRegion = reinterpret_cast<uint8_t*>(
150         sys->mmap(0, regionSize, PROT_READ, MAP_SHARED, mappedFd, 0));
151 
152     if (mappedRegion == MAP_FAILED)
153     {
154         sys->close(mappedFd);
155         mappedFd = -1;
156         std::fprintf(stderr, "Mmap failure: '%s'\n", std::strerror(errno));
157         return false;
158     }
159 
160     /* TODO: There is no close() method here, to close mappedFd, or mappedRegion
161      * -- therefore, a good next step will be to evaluate whether or not the
162      * other pieces should go here...
163      */
164     return true;
165 }
166 
167 } // namespace ipmi_flash
168