1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "google_accel_oob.hpp" 16 17 #include "commands.hpp" 18 19 #include <sdbusplus/bus.hpp> 20 #include <stdplus/print.hpp> 21 22 #include <cstdint> 23 #include <cstring> 24 #include <span> 25 #include <string> 26 #include <vector> 27 28 namespace google 29 { 30 namespace ipmi 31 { 32 33 #ifndef MAX_IPMI_BUFFER 34 #define MAX_IPMI_BUFFER 64 35 #endif 36 37 // token + address(8) + num_bytes + data(8) + len + NULL 38 constexpr size_t MAX_NAME_SIZE = MAX_IPMI_BUFFER - 1 - 8 - 1 - 8 - 1 - 1; 39 40 Resp accelOobDeviceCount(std::span<const uint8_t> data, 41 HandlerInterface* handler) 42 { 43 struct Request 44 { 45 } __attribute__((packed)); 46 47 struct Reply 48 { 49 uint32_t count; 50 } __attribute__((packed)); 51 52 if (data.size_bytes() < sizeof(Request)) 53 { 54 stdplus::print(stderr, "AccelOob DeviceCount command too small: {}\n", 55 data.size_bytes()); 56 return ::ipmi::responseReqDataLenInvalid(); 57 } 58 59 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER) 60 { 61 stdplus::print( 62 stderr, 63 "AccelOob DeviceCount command too large for reply buffer: " 64 "command={}B, payload={}B, max={}B\n", 65 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER); 66 return ::ipmi::responseReqDataLenExceeded(); 67 } 68 69 uint32_t count = handler->accelOobDeviceCount(); 70 71 std::vector<uint8_t> replyBuf(sizeof(Reply)); 72 auto* reply = reinterpret_cast<Reply*>(replyBuf.data()); 73 reply->count = count; 74 75 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobDeviceCount, 76 replyBuf); 77 } 78 79 Resp accelOobDeviceName(std::span<const uint8_t> data, 80 HandlerInterface* handler) 81 { 82 struct Request 83 { 84 uint32_t index; 85 } __attribute__((packed)); 86 87 struct Reply 88 { 89 uint8_t nameLength; 90 char name[MAX_NAME_SIZE]; 91 } __attribute__((packed)); 92 93 if (data.size_bytes() < sizeof(Request)) 94 { 95 stdplus::print(stderr, "AccelOob DeviceName command too small: {}\n", 96 data.size_bytes()); 97 return ::ipmi::responseReqDataLenInvalid(); 98 } 99 100 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER) 101 { 102 stdplus::print( 103 stderr, 104 "AccelOob DeviceName command too large for reply buffer: " 105 "command={}B, payload={}B, max={}B\n", 106 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER); 107 return ::ipmi::responseReqDataLenExceeded(); 108 } 109 110 auto* req = reinterpret_cast<const Request*>(data.data()); 111 std::string name = handler->accelOobDeviceName(req->index); 112 113 if (name.size() > MAX_NAME_SIZE) 114 { 115 stdplus::print(stderr, 116 "AccelOob: name was too long. " 117 "'{}' len must be <= {}\n", 118 name, MAX_NAME_SIZE); 119 return ::ipmi::responseReqDataTruncated(); 120 } 121 122 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply)); 123 std::copy(data.begin(), data.end(), replyBuf.data()); 124 auto* reply = reinterpret_cast<Reply*>(replyBuf.data() + data.size_bytes()); 125 reply->nameLength = name.length(); 126 memcpy(reply->name, name.c_str(), reply->nameLength + 1); 127 128 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobDeviceName, 129 replyBuf); 130 } 131 132 namespace 133 { 134 135 struct NameHeader 136 { 137 uint8_t nameLength; 138 char name[MAX_NAME_SIZE]; 139 } __attribute__((packed)); 140 141 // Reads the variable-length name from reqBuf and outputs the name and a pointer 142 // to the payload (next byte after name). 143 // 144 // Returns: =0: success. 145 // >0: if dataLen is too small, returns the minimum buffers size. 146 // 147 // Params: 148 // [in] reqBuf - the request buffer 149 // [in] dataLen - the length of reqBuf, in bytes 150 // [in] payloadSize - the size of the expected payload 151 // [out] name - the name string 152 // [out] payload - pointer into reqBuf just after name 153 size_t ReadNameHeader(const uint8_t* reqBuf, size_t dataLen, size_t payloadSize, 154 std::string* name, const uint8_t** payload) 155 { 156 constexpr size_t kNameHeaderSize = sizeof(NameHeader) - MAX_NAME_SIZE; 157 158 auto* req_header = reinterpret_cast<const NameHeader*>(reqBuf); 159 160 size_t minDataLen = kNameHeaderSize + payloadSize + req_header->nameLength; 161 if (dataLen < minDataLen) 162 { 163 return minDataLen; 164 } 165 166 if (name) 167 { 168 *name = std::string(req_header->name, req_header->nameLength); 169 } 170 if (payload) 171 { 172 *payload = reqBuf + kNameHeaderSize + req_header->nameLength; 173 } 174 return 0; 175 } 176 177 } // namespace 178 179 Resp accelOobRead(std::span<const uint8_t> data, HandlerInterface* handler) 180 { 181 struct Request 182 { 183 // Variable length header, handled by ReadNameHeader 184 // uint8_t nameLength; // <= MAX_NAME_SIZE 185 // char name[nameLength]; 186 187 // Additional arguments 188 uint8_t token; 189 uint64_t address; 190 uint8_t num_bytes; 191 } __attribute__((packed)); 192 193 struct Reply 194 { 195 uint64_t data; 196 } __attribute__((packed)); 197 198 stdplus::print(stderr, 199 "AccelOob Read command sizes: " 200 "command={}B, payload={}B, max={}B\n", 201 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER); 202 203 std::string name; 204 const uint8_t* payload; 205 206 size_t min_size = ReadNameHeader(data.data(), data.size_bytes(), 207 sizeof(Request), &name, &payload); 208 if (min_size != 0) 209 { 210 stdplus::print(stderr, "AccelOob Read command too small: {}B < {}B\n", 211 data.size_bytes(), min_size); 212 return ::ipmi::responseReqDataLenInvalid(); 213 } 214 215 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER) 216 { 217 stdplus::print(stderr, 218 "AccelOob Read command too large for reply buffer: " 219 "command={}B, payload={}B, max={}B\n", 220 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER); 221 return ::ipmi::responseReqDataLenExceeded(); 222 } 223 224 auto req = reinterpret_cast<const Request*>(payload); 225 uint64_t r = handler->accelOobRead(name, req->address, req->num_bytes); 226 227 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply)); 228 std::copy(data.begin(), data.end(), replyBuf.data()); 229 auto* reply = reinterpret_cast<Reply*>(replyBuf.data() + data.size_bytes()); 230 reply->data = r; 231 232 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobRead, replyBuf); 233 } 234 235 Resp accelOobWrite(std::span<const uint8_t> data, HandlerInterface* handler) 236 { 237 struct Request 238 { 239 // Variable length header, handled by ReadNameHeader 240 // uint8_t nameLength; // <= MAX_NAME_SIZE 241 // char name[nameLength]; 242 243 // Additional arguments 244 uint8_t token; 245 uint64_t address; 246 uint8_t num_bytes; 247 uint64_t data; 248 } __attribute__((packed)); 249 250 struct Reply 251 { 252 // Empty 253 } __attribute__((packed)); 254 255 std::string name{}; 256 const uint8_t* payload; 257 258 size_t min_size = ReadNameHeader(data.data(), data.size_bytes(), 259 sizeof(Request), &name, &payload); 260 if (min_size != 0) 261 { 262 stdplus::print(stderr, "AccelOob Write command too small: {}B < {}B\n", 263 data.size_bytes(), min_size); 264 return ::ipmi::responseReqDataLenInvalid(); 265 } 266 267 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER) 268 { 269 stdplus::print(stderr, 270 "AccelOob Write command too large for reply buffer: " 271 "command={}B, payload={}B, max={}B\n", 272 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER); 273 return ::ipmi::responseReqDataLenExceeded(); 274 } 275 276 auto req = reinterpret_cast<const Request*>(payload); 277 handler->accelOobWrite(name, req->address, req->num_bytes, req->data); 278 279 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply)); 280 std::copy(data.begin(), data.end(), replyBuf.data()); 281 282 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobWrite, replyBuf); 283 } 284 285 } // namespace ipmi 286 } // namespace google 287