1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 3 #include <assert.h> 4 #include <endian.h> 5 #include <err.h> 6 #include <inttypes.h> 7 #include <stdbool.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #define pr_fmt(x) "astlpc: " x 12 13 #if HAVE_CONFIG_H 14 #include "config.h" 15 #endif 16 17 #include "libmctp.h" 18 #include "libmctp-alloc.h" 19 #include "libmctp-log.h" 20 #include "libmctp-astlpc.h" 21 22 #ifdef MCTP_HAVE_FILEIO 23 24 #include <unistd.h> 25 #include <fcntl.h> 26 #include <sys/ioctl.h> 27 #include <sys/mman.h> 28 #include <linux/aspeed-lpc-ctrl.h> 29 30 /* kernel interface */ 31 static const char *kcs_path = "/dev/mctp0"; 32 static const char *lpc_path = "/dev/aspeed-lpc-ctrl"; 33 34 #endif 35 36 struct mctp_binding_astlpc { 37 struct mctp_binding binding; 38 39 union { 40 void *lpc_map; 41 struct mctp_lpcmap_hdr *lpc_hdr; 42 }; 43 44 /* direct ops data */ 45 struct mctp_binding_astlpc_ops ops; 46 void *ops_data; 47 struct mctp_lpcmap_hdr *priv_hdr; 48 49 /* fileio ops data */ 50 void *lpc_map_base; 51 int kcs_fd; 52 uint8_t kcs_status; 53 54 bool running; 55 56 /* temporary transmit buffer */ 57 uint8_t txbuf[256]; 58 }; 59 60 #ifndef container_of 61 #define container_of(ptr, type, member) \ 62 (type *)((char *)(ptr) - (char *)&((type *)0)->member) 63 #endif 64 65 #define binding_to_astlpc(b) \ 66 container_of(b, struct mctp_binding_astlpc, binding) 67 68 #define MCTP_MAGIC 0x4d435450 69 #define BMC_VER_MIN 1 70 #define BMC_VER_CUR 1 71 72 struct mctp_lpcmap_hdr { 73 uint32_t magic; 74 75 uint16_t bmc_ver_min; 76 uint16_t bmc_ver_cur; 77 uint16_t host_ver_min; 78 uint16_t host_ver_cur; 79 uint16_t negotiated_ver; 80 uint16_t pad0; 81 82 uint32_t rx_offset; 83 uint32_t rx_size; 84 uint32_t tx_offset; 85 uint32_t tx_size; 86 } __attribute__((packed)); 87 88 /* layout of TX/RX areas */ 89 static const uint32_t rx_offset = 0x100; 90 static const uint32_t rx_size = 0x100; 91 static const uint32_t tx_offset = 0x200; 92 static const uint32_t tx_size = 0x100; 93 94 #define LPC_WIN_SIZE (1 * 1024 * 1024) 95 96 enum { 97 KCS_REG_DATA = 0, 98 KCS_REG_STATUS = 1, 99 }; 100 101 #define KCS_STATUS_BMC_READY 0x80 102 #define KCS_STATUS_CHANNEL_ACTIVE 0x40 103 #define KCS_STATUS_IBF 0x02 104 #define KCS_STATUS_OBF 0x01 105 106 static bool lpc_direct(struct mctp_binding_astlpc *astlpc) 107 { 108 return astlpc->lpc_map != NULL; 109 } 110 111 static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc, 112 uint8_t status) 113 { 114 uint8_t data; 115 int rc; 116 117 /* Since we're setting the status register, we want the other endpoint 118 * to be interrupted. However, some hardware may only raise a host-side 119 * interrupt on an ODR event. 120 * So, write a dummy value of 0xff to ODR, which will ensure that an 121 * interrupt is triggered, and can be ignored by the host. 122 */ 123 data = 0xff; 124 status |= KCS_STATUS_OBF; 125 126 rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 127 status); 128 if (rc) { 129 mctp_prwarn("KCS status write failed"); 130 return -1; 131 } 132 133 rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 134 data); 135 if (rc) { 136 mctp_prwarn("KCS dummy data write failed"); 137 return -1; 138 } 139 140 return 0; 141 } 142 143 static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc, 144 uint8_t data) 145 { 146 uint8_t status; 147 int rc; 148 149 for (;;) { 150 rc = astlpc->ops.kcs_read(astlpc->ops_data, 151 MCTP_ASTLPC_KCS_REG_STATUS, &status); 152 if (rc) { 153 mctp_prwarn("KCS status read failed"); 154 return -1; 155 } 156 if (!(status & KCS_STATUS_OBF)) 157 break; 158 /* todo: timeout */ 159 } 160 161 rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 162 data); 163 if (rc) { 164 mctp_prwarn("KCS data write failed"); 165 return -1; 166 } 167 168 return 0; 169 } 170 171 static int mctp_binding_astlpc_tx(struct mctp_binding *b, 172 struct mctp_pktbuf *pkt) 173 { 174 struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b); 175 uint32_t len; 176 struct mctp_hdr *hdr; 177 178 hdr = mctp_pktbuf_hdr(pkt); 179 len = mctp_pktbuf_size(pkt); 180 181 mctp_prdebug("%s: Transmitting %"PRIu32"-byte packet (%hhu, %hhu, 0x%hhx)", 182 __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag); 183 184 if (len > rx_size - 4) { 185 mctp_prwarn("invalid TX len 0x%x", len); 186 return -1; 187 } 188 189 if (lpc_direct(astlpc)) { 190 *(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len); 191 memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt), 192 len); 193 } else { 194 uint32_t tmp = htobe32(len); 195 astlpc->ops.lpc_write(astlpc->ops_data, &tmp, rx_offset, 196 sizeof(tmp)); 197 astlpc->ops.lpc_write(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 198 rx_offset + 4, len); 199 } 200 201 mctp_binding_set_tx_enabled(b, false); 202 203 mctp_astlpc_kcs_send(astlpc, 0x1); 204 return 0; 205 } 206 207 static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc) 208 { 209 /* todo: actual version negotiation */ 210 if (lpc_direct(astlpc)) { 211 astlpc->lpc_hdr->negotiated_ver = htobe16(1); 212 } else { 213 uint16_t ver = htobe16(1); 214 astlpc->ops.lpc_write(astlpc->ops_data, &ver, 215 offsetof(struct mctp_lpcmap_hdr, 216 negotiated_ver), 217 sizeof(ver)); 218 } 219 mctp_astlpc_kcs_set_status(astlpc, 220 KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE | 221 KCS_STATUS_OBF); 222 223 mctp_binding_set_tx_enabled(&astlpc->binding, true); 224 } 225 226 static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc) 227 { 228 struct mctp_pktbuf *pkt; 229 uint32_t len; 230 231 if (lpc_direct(astlpc)) { 232 len = *(uint32_t *)(astlpc->lpc_map + tx_offset); 233 } else { 234 astlpc->ops.lpc_read(astlpc->ops_data, &len, 235 tx_offset, sizeof(len)); 236 } 237 len = be32toh(len); 238 239 if (len > tx_size - 4) { 240 mctp_prwarn("invalid RX len 0x%x", len); 241 return; 242 } 243 244 if (len > astlpc->binding.pkt_size) { 245 mctp_prwarn("invalid RX len 0x%x", len); 246 return; 247 } 248 249 pkt = mctp_pktbuf_alloc(&astlpc->binding, len); 250 if (!pkt) 251 goto out_complete; 252 253 if (lpc_direct(astlpc)) { 254 memcpy(mctp_pktbuf_hdr(pkt), 255 astlpc->lpc_map + tx_offset + 4, len); 256 } else { 257 astlpc->ops.lpc_read(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 258 tx_offset + 4, len); 259 } 260 261 mctp_bus_rx(&astlpc->binding, pkt); 262 263 out_complete: 264 mctp_astlpc_kcs_send(astlpc, 0x2); 265 } 266 267 static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc) 268 { 269 mctp_binding_set_tx_enabled(&astlpc->binding, true); 270 } 271 272 int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc) 273 { 274 uint8_t status, data; 275 int rc; 276 277 rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 278 &status); 279 if (rc) { 280 mctp_prwarn("KCS read error"); 281 return -1; 282 } 283 284 mctp_prdebug("%s: status: 0x%hhx", __func__, status); 285 286 if (!(status & KCS_STATUS_IBF)) 287 return 0; 288 289 rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 290 &data); 291 if (rc) { 292 mctp_prwarn("KCS data read error"); 293 return -1; 294 } 295 296 mctp_prdebug("%s: data: 0x%hhx", __func__, data); 297 298 switch (data) { 299 case 0x0: 300 mctp_astlpc_init_channel(astlpc); 301 break; 302 case 0x1: 303 mctp_astlpc_rx_start(astlpc); 304 break; 305 case 0x2: 306 mctp_astlpc_tx_complete(astlpc); 307 break; 308 case 0xff: 309 /* reserved value for dummy data writes; do nothing */ 310 break; 311 default: 312 mctp_prwarn("unknown message 0x%x", data); 313 } 314 return 0; 315 } 316 317 static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc) 318 { 319 struct mctp_lpcmap_hdr *hdr; 320 uint8_t status; 321 int rc; 322 323 if (lpc_direct(astlpc)) 324 hdr = astlpc->lpc_hdr; 325 else 326 hdr = astlpc->priv_hdr; 327 328 hdr->magic = htobe32(MCTP_MAGIC); 329 hdr->bmc_ver_min = htobe16(BMC_VER_MIN); 330 hdr->bmc_ver_cur = htobe16(BMC_VER_CUR); 331 332 hdr->rx_offset = htobe32(rx_offset); 333 hdr->rx_size = htobe32(rx_size); 334 hdr->tx_offset = htobe32(tx_offset); 335 hdr->tx_size = htobe32(tx_size); 336 337 if (!lpc_direct(astlpc)) 338 astlpc->ops.lpc_write(astlpc->ops_data, hdr, 0, sizeof(*hdr)); 339 340 /* set status indicating that the BMC is now active */ 341 status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF; 342 rc = astlpc->ops.kcs_write(astlpc->ops_data, 343 MCTP_ASTLPC_KCS_REG_STATUS, status); 344 if (rc) { 345 mctp_prwarn("KCS write failed"); 346 } 347 348 return rc; 349 } 350 351 static int mctp_binding_astlpc_start(struct mctp_binding *b) 352 { 353 struct mctp_binding_astlpc *astlpc = container_of(b, 354 struct mctp_binding_astlpc, binding); 355 356 return mctp_astlpc_init_bmc(astlpc); 357 } 358 359 /* allocate and basic initialisation */ 360 static struct mctp_binding_astlpc *__mctp_astlpc_init(void) 361 { 362 struct mctp_binding_astlpc *astlpc; 363 364 astlpc = __mctp_alloc(sizeof(*astlpc)); 365 memset(astlpc, 0, sizeof(*astlpc)); 366 astlpc->binding.name = "astlpc"; 367 astlpc->binding.version = 1; 368 astlpc->binding.tx = mctp_binding_astlpc_tx; 369 astlpc->binding.start = mctp_binding_astlpc_start; 370 astlpc->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU); 371 astlpc->binding.pkt_pad = 0; 372 astlpc->lpc_map = NULL; 373 374 return astlpc; 375 } 376 377 struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b) 378 { 379 return &b->binding; 380 } 381 382 struct mctp_binding_astlpc *mctp_astlpc_init_ops( 383 const struct mctp_binding_astlpc_ops *ops, 384 void *ops_data, void *lpc_map) 385 { 386 struct mctp_binding_astlpc *astlpc; 387 388 astlpc = __mctp_astlpc_init(); 389 if (!astlpc) 390 return NULL; 391 392 memcpy(&astlpc->ops, ops, sizeof(astlpc->ops)); 393 astlpc->ops_data = ops_data; 394 astlpc->lpc_map = lpc_map; 395 396 /* In indirect mode, we keep a separate buffer of header data. 397 * We need to sync this through the lpc_read/lpc_write ops. 398 */ 399 if (!astlpc->lpc_map) 400 astlpc->priv_hdr = __mctp_alloc(sizeof(*astlpc->priv_hdr)); 401 402 return astlpc; 403 } 404 405 void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc) 406 { 407 if (astlpc->priv_hdr) 408 __mctp_free(astlpc->priv_hdr); 409 __mctp_free(astlpc); 410 } 411 412 #ifdef MCTP_HAVE_FILEIO 413 static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc) 414 { 415 struct aspeed_lpc_ctrl_mapping map = { 416 .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY, 417 .window_id = 0, /* There's only one */ 418 .flags = 0, 419 .addr = 0, 420 .offset = 0, 421 .size = 0 422 }; 423 int fd, rc; 424 425 fd = open(lpc_path, O_RDWR | O_SYNC); 426 if (fd < 0) { 427 mctp_prwarn("LPC open (%s) failed", lpc_path); 428 return -1; 429 } 430 431 rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map); 432 if (rc) { 433 mctp_prwarn("LPC GET_SIZE failed"); 434 close(fd); 435 return -1; 436 } 437 438 astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE, 439 MAP_SHARED, fd, 0); 440 if (astlpc->lpc_map_base == MAP_FAILED) { 441 mctp_prwarn("LPC mmap failed"); 442 rc = -1; 443 } else { 444 astlpc->lpc_map = astlpc->lpc_map_base + 445 map.size - LPC_WIN_SIZE; 446 } 447 448 close(fd); 449 450 return rc; 451 } 452 453 static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc) 454 { 455 astlpc->kcs_fd = open(kcs_path, O_RDWR); 456 if (astlpc->kcs_fd < 0) 457 return -1; 458 459 return 0; 460 } 461 462 static int __mctp_astlpc_fileio_kcs_read(void *arg, 463 enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val) 464 { 465 struct mctp_binding_astlpc *astlpc = arg; 466 off_t offset = reg; 467 int rc; 468 469 rc = pread(astlpc->kcs_fd, val, 1, offset); 470 471 return rc == 1 ? 0 : -1; 472 } 473 474 static int __mctp_astlpc_fileio_kcs_write(void *arg, 475 enum mctp_binding_astlpc_kcs_reg reg, uint8_t val) 476 { 477 struct mctp_binding_astlpc *astlpc = arg; 478 off_t offset = reg; 479 int rc; 480 481 rc = pwrite(astlpc->kcs_fd, &val, 1, offset); 482 483 return rc == 1 ? 0 : -1; 484 } 485 486 int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc) 487 { 488 return astlpc->kcs_fd; 489 } 490 491 struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void) 492 { 493 struct mctp_binding_astlpc *astlpc; 494 int rc; 495 496 astlpc = __mctp_astlpc_init(); 497 if (!astlpc) 498 return NULL; 499 500 /* Set internal operations for kcs. We use direct accesses to the lpc 501 * map area */ 502 astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read; 503 astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write; 504 astlpc->ops_data = astlpc; 505 506 rc = mctp_astlpc_init_fileio_lpc(astlpc); 507 if (rc) { 508 free(astlpc); 509 return NULL; 510 } 511 512 rc = mctp_astlpc_init_fileio_kcs(astlpc); 513 if (rc) { 514 free(astlpc); 515 return NULL; 516 } 517 518 return astlpc; 519 } 520 #else 521 struct mctp_binding_astlpc * __attribute__((const)) 522 mctp_astlpc_init_fileio(void) 523 { 524 warnx("Missing support for file IO"); 525 return NULL; 526 } 527 528 int __attribute__((const)) mctp_astlpc_get_fd( 529 struct mctp_binding_astlpc *astlpc __attribute__((unused))) 530 { 531 warnx("Missing support for file IO"); 532 return -1; 533 } 534 #endif 535