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