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 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 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 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 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 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