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