blob: 0281099181e31fd926679387cabc8d8656ab2b53 [file] [log] [blame]
Jeremy Kerr3d36ee22019-05-30 11:15:37 +08001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
Jeremy Kerr672c8852019-03-01 12:18:07 +08002
Andrew Jeffery3286f172020-03-17 23:04:13 +10303#if HAVE_CONFIG_H
4#include "config.h"
5#endif
6
7#if HAVE_ENDIAN_H
Jeremy Kerr92a10a62019-08-28 16:55:54 +05308#include <endian.h>
Andrew Jeffery3286f172020-03-17 23:04:13 +10309#endif
10
11#include <assert.h>
Andrew Jeffery59c6a5c2020-01-17 15:52:51 +103012#include <err.h>
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093013#include <errno.h>
Andrew Jefferyedfe3832020-02-06 11:52:11 +103014#include <inttypes.h>
Jeremy Kerr672c8852019-03-01 12:18:07 +080015#include <stdbool.h>
16#include <stdlib.h>
17#include <string.h>
Jeremy Kerr672c8852019-03-01 12:18:07 +080018
Jeremy Kerr672c8852019-03-01 12:18:07 +080019#define pr_fmt(x) "astlpc: " x
20
Andrew Jefferyeba19a32021-03-09 23:09:40 +103021#include "container_of.h"
22#include "crc32.h"
Jeremy Kerr672c8852019-03-01 12:18:07 +080023#include "libmctp.h"
24#include "libmctp-alloc.h"
25#include "libmctp-log.h"
26#include "libmctp-astlpc.h"
Andrew Jeffery4622cad2020-11-03 22:20:18 +103027#include "range.h"
Jeremy Kerr672c8852019-03-01 12:18:07 +080028
Jeremy Kerrb214c642019-11-27 11:34:00 +080029#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr92a10a62019-08-28 16:55:54 +053030
Jeremy Kerrc6f676d2019-12-19 09:24:06 +080031#include <unistd.h>
Jeremy Kerr92a10a62019-08-28 16:55:54 +053032#include <fcntl.h>
Andrew Jeffery1111c6a2022-07-25 20:44:39 +093033#include <poll.h>
Jeremy Kerr92a10a62019-08-28 16:55:54 +053034#include <sys/ioctl.h>
35#include <sys/mman.h>
36#include <linux/aspeed-lpc-ctrl.h>
37
38/* kernel interface */
39static const char *kcs_path = "/dev/mctp0";
40static const char *lpc_path = "/dev/aspeed-lpc-ctrl";
41
42#endif
43
Andrew Jefferyfe763e92022-08-05 23:16:17 +093044enum mctp_astlpc_buffer_state {
45 /*
46 * Prior to "Channel Ready" we mark the buffers as "idle" to catch illegal accesses. In this
47 * state neither side is considered the owner of the buffer.
48 *
49 * Upon "Channel Ready", each side transitions the buffers from the initial "idle" state
50 * to the following target states:
51 *
52 * Tx buffer: "acquired"
53 * Rx buffer: "released"
54 */
55 buffer_state_idle,
56
57 /*
58 * Beyond initialisation by "Channel Ready", buffers are in the "acquired" state once:
59 *
60 * 1. We dequeue a control command transferring the buffer to our ownership out of the KCS
61 * interface, and
62 * 2. We are yet to complete all of our required accesses to the buffer
63 *
64 * * The Tx buffer enters the "acquired" state when we dequeue the "Rx Complete" command
65 * * The Rx buffer enters the "acquired" state when we dequeue the "Tx Begin" command
66 *
67 * It is a failure of implementation if it's possible for both sides to simultaneously
68 * consider a buffer as "acquired".
69 */
70 buffer_state_acquired,
71
72 /*
73 * Buffers are in the "prepared" state when:
74 *
75 * 1. We have completed all of our required accesses (read or write) for the buffer, and
76 * 2. We have not yet successfully enqueued the control command to hand off ownership
77 */
78 buffer_state_prepared,
79
80 /*
81 * Beyond initialisation by "Channel Ready", buffers are in the "released" state once:
82 *
83 * 1. We successfully enqueue the control command transferring ownership to the remote
84 * side in to the KCS interface
85 *
86 * * The Tx buffer enters the "released" state when we enqueue the "Tx Begin" command
87 * * The Rx buffer enters the "released" state when we enqueue the "Rx Complete" command
88 *
89 * It may be the case that both sides simultaneously consider a buffer to be in the
90 * "released" state. However, if this is true, it must also be true that a buffer ownership
91 * transfer command has been enqueued in the KCS interface and is yet to be dequeued.
92 */
93 buffer_state_released,
94};
95
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093096struct mctp_astlpc_buffer {
97 uint32_t offset;
98 uint32_t size;
Andrew Jefferyfe763e92022-08-05 23:16:17 +093099 enum mctp_astlpc_buffer_state state;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930100};
101
102struct mctp_astlpc_layout {
103 struct mctp_astlpc_buffer rx;
104 struct mctp_astlpc_buffer tx;
105};
106
Andrew Jeffery88412be2021-03-09 22:05:22 +1030107struct mctp_astlpc_protocol {
108 uint16_t version;
109 uint32_t (*packet_size)(uint32_t body);
110 uint32_t (*body_size)(uint32_t packet);
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030111 void (*pktbuf_protect)(struct mctp_pktbuf *pkt);
112 bool (*pktbuf_validate)(struct mctp_pktbuf *pkt);
Andrew Jeffery88412be2021-03-09 22:05:22 +1030113};
114
Jeremy Kerr672c8852019-03-01 12:18:07 +0800115struct mctp_binding_astlpc {
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600116 struct mctp_binding binding;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530117
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930118 void *lpc_map;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930119 struct mctp_astlpc_layout layout;
120
121 uint8_t mode;
Andrew Jefferya9368982020-06-09 13:07:39 +0930122 uint32_t requested_mtu;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530123
Andrew Jeffery88412be2021-03-09 22:05:22 +1030124 const struct mctp_astlpc_protocol *proto;
125
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530126 /* direct ops data */
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930127 struct mctp_binding_astlpc_ops ops;
128 void *ops_data;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530129
130 /* fileio ops data */
Andrew Jeffery979c6a12020-05-23 20:04:49 +0930131 int kcs_fd;
132 uint8_t kcs_status;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800133};
134
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600135#define binding_to_astlpc(b) \
Jeremy Kerr672c8852019-03-01 12:18:07 +0800136 container_of(b, struct mctp_binding_astlpc, binding)
137
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930138#define astlpc_prlog(ctx, lvl, fmt, ...) \
139 do { \
140 bool __bmc = ((ctx)->mode == MCTP_BINDING_ASTLPC_MODE_BMC); \
141 mctp_prlog(lvl, pr_fmt("%s: " fmt), __bmc ? "bmc" : "host", \
142 ##__VA_ARGS__); \
143 } while (0)
144
145#define astlpc_prerr(ctx, fmt, ...) \
146 astlpc_prlog(ctx, MCTP_LOG_ERR, fmt, ##__VA_ARGS__)
147#define astlpc_prwarn(ctx, fmt, ...) \
148 astlpc_prlog(ctx, MCTP_LOG_WARNING, fmt, ##__VA_ARGS__)
149#define astlpc_prinfo(ctx, fmt, ...) \
150 astlpc_prlog(ctx, MCTP_LOG_INFO, fmt, ##__VA_ARGS__)
151#define astlpc_prdebug(ctx, fmt, ...) \
152 astlpc_prlog(ctx, MCTP_LOG_DEBUG, fmt, ##__VA_ARGS__)
153
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930154/* clang-format off */
155#define ASTLPC_MCTP_MAGIC 0x4d435450
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930156#define ASTLPC_VER_BAD 0
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930157#define ASTLPC_VER_MIN 1
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930158
Andrew Jeffery3a540662020-05-26 19:55:30 +0930159/* Support testing of new binding protocols */
160#ifndef ASTLPC_VER_CUR
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030161#define ASTLPC_VER_CUR 3
Andrew Jeffery3a540662020-05-26 19:55:30 +0930162#endif
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930163/* clang-format on */
Jeremy Kerr672c8852019-03-01 12:18:07 +0800164
Andrew Jeffery88412be2021-03-09 22:05:22 +1030165#ifndef ARRAY_SIZE
166#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
167#endif
168
169static uint32_t astlpc_packet_size_v1(uint32_t body)
170{
171 assert((body + 4) > body);
172
173 return body + 4;
174}
175
176static uint32_t astlpc_body_size_v1(uint32_t packet)
177{
178 assert((packet - 4) < packet);
179
180 return packet - 4;
181}
182
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030183void astlpc_pktbuf_protect_v1(struct mctp_pktbuf *pkt)
184{
185 (void)pkt;
186}
187
188bool astlpc_pktbuf_validate_v1(struct mctp_pktbuf *pkt)
189{
190 (void)pkt;
191 return true;
192}
193
194static uint32_t astlpc_packet_size_v3(uint32_t body)
195{
196 assert((body + 4 + 4) > body);
197
198 return body + 4 + 4;
199}
200
201static uint32_t astlpc_body_size_v3(uint32_t packet)
202{
203 assert((packet - 4 - 4) < packet);
204
205 return packet - 4 - 4;
206}
207
208void astlpc_pktbuf_protect_v3(struct mctp_pktbuf *pkt)
209{
210 uint32_t code;
211
212 code = htobe32(crc32(mctp_pktbuf_hdr(pkt), mctp_pktbuf_size(pkt)));
213 mctp_prdebug("%s: 0x%" PRIx32, __func__, code);
214 mctp_pktbuf_push(pkt, &code, 4);
215}
216
217bool astlpc_pktbuf_validate_v3(struct mctp_pktbuf *pkt)
218{
219 uint32_t code;
220 void *check;
221
222 code = be32toh(crc32(mctp_pktbuf_hdr(pkt), mctp_pktbuf_size(pkt) - 4));
223 mctp_prdebug("%s: 0x%" PRIx32, __func__, code);
224 check = mctp_pktbuf_pop(pkt, 4);
225 return check && !memcmp(&code, check, 4);
226}
227
Andrew Jeffery88412be2021-03-09 22:05:22 +1030228static const struct mctp_astlpc_protocol astlpc_protocol_version[] = {
229 [0] = {
230 .version = 0,
231 .packet_size = NULL,
232 .body_size = NULL,
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030233 .pktbuf_protect = NULL,
234 .pktbuf_validate = NULL,
Andrew Jeffery88412be2021-03-09 22:05:22 +1030235 },
236 [1] = {
237 .version = 1,
238 .packet_size = astlpc_packet_size_v1,
239 .body_size = astlpc_body_size_v1,
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030240 .pktbuf_protect = astlpc_pktbuf_protect_v1,
241 .pktbuf_validate = astlpc_pktbuf_validate_v1,
Andrew Jeffery88412be2021-03-09 22:05:22 +1030242 },
243 [2] = {
244 .version = 2,
245 .packet_size = astlpc_packet_size_v1,
246 .body_size = astlpc_body_size_v1,
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030247 .pktbuf_protect = astlpc_pktbuf_protect_v1,
248 .pktbuf_validate = astlpc_pktbuf_validate_v1,
249 },
250 [3] = {
251 .version = 3,
252 .packet_size = astlpc_packet_size_v3,
253 .body_size = astlpc_body_size_v3,
254 .pktbuf_protect = astlpc_pktbuf_protect_v3,
255 .pktbuf_validate = astlpc_pktbuf_validate_v3,
Andrew Jeffery88412be2021-03-09 22:05:22 +1030256 },
257};
258
Jeremy Kerr672c8852019-03-01 12:18:07 +0800259struct mctp_lpcmap_hdr {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930260 uint32_t magic;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800261
Andrew Jeffery3a540662020-05-26 19:55:30 +0930262 uint16_t bmc_ver_min;
263 uint16_t bmc_ver_cur;
264 uint16_t host_ver_min;
265 uint16_t host_ver_cur;
266 uint16_t negotiated_ver;
267 uint16_t pad0;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800268
Andrew Jeffery3a540662020-05-26 19:55:30 +0930269 struct {
270 uint32_t rx_offset;
271 uint32_t rx_size;
272 uint32_t tx_offset;
273 uint32_t tx_size;
274 } layout;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800275} __attribute__((packed));
276
Andrew Jeffery3a540662020-05-26 19:55:30 +0930277static const uint32_t control_size = 0x100;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800278
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600279#define LPC_WIN_SIZE (1 * 1024 * 1024)
Jeremy Kerr672c8852019-03-01 12:18:07 +0800280
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600281#define KCS_STATUS_BMC_READY 0x80
282#define KCS_STATUS_CHANNEL_ACTIVE 0x40
283#define KCS_STATUS_IBF 0x02
284#define KCS_STATUS_OBF 0x01
Jeremy Kerr672c8852019-03-01 12:18:07 +0800285
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930286static inline int mctp_astlpc_kcs_write(struct mctp_binding_astlpc *astlpc,
287 enum mctp_binding_astlpc_kcs_reg reg,
288 uint8_t val)
289{
290 return astlpc->ops.kcs_write(astlpc->ops_data, reg, val);
291}
292
293static inline int mctp_astlpc_kcs_read(struct mctp_binding_astlpc *astlpc,
294 enum mctp_binding_astlpc_kcs_reg reg,
295 uint8_t *val)
296{
297 return astlpc->ops.kcs_read(astlpc->ops_data, reg, val);
298}
299
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930300static inline int mctp_astlpc_lpc_write(struct mctp_binding_astlpc *astlpc,
301 const void *buf, long offset,
302 size_t len)
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530303{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930304 astlpc_prdebug(astlpc, "%s: %zu bytes to 0x%lx", __func__, len, offset);
305
306 assert(offset >= 0);
307
308 /* Indirect access */
309 if (astlpc->ops.lpc_write) {
310 void *data = astlpc->ops_data;
311
312 return astlpc->ops.lpc_write(data, buf, offset, len);
313 }
314
315 /* Direct mapping */
316 assert(astlpc->lpc_map);
317 memcpy(&((char *)astlpc->lpc_map)[offset], buf, len);
318
319 return 0;
320}
321
322static inline int mctp_astlpc_lpc_read(struct mctp_binding_astlpc *astlpc,
323 void *buf, long offset, size_t len)
324{
325 astlpc_prdebug(astlpc, "%s: %zu bytes from 0x%lx", __func__, len,
326 offset);
327
328 assert(offset >= 0);
329
330 /* Indirect access */
331 if (astlpc->ops.lpc_read) {
332 void *data = astlpc->ops_data;
333
334 return astlpc->ops.lpc_read(data, buf, offset, len);
335 }
336
337 /* Direct mapping */
338 assert(astlpc->lpc_map);
339 memcpy(buf, &((char *)astlpc->lpc_map)[offset], len);
340
341 return 0;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530342}
343
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930344static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc,
345 uint8_t status)
346{
347 uint8_t data;
348 int rc;
349
350 /* Since we're setting the status register, we want the other endpoint
351 * to be interrupted. However, some hardware may only raise a host-side
352 * interrupt on an ODR event.
353 * So, write a dummy value of 0xff to ODR, which will ensure that an
354 * interrupt is triggered, and can be ignored by the host.
355 */
356 data = 0xff;
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930357
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930358 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, status);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930359 if (rc) {
360 astlpc_prwarn(astlpc, "KCS status write failed");
361 return -1;
362 }
363
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930364 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930365 if (rc) {
366 astlpc_prwarn(astlpc, "KCS dummy data write failed");
367 return -1;
368 }
369
370 return 0;
371}
372
Andrew Jeffery3a540662020-05-26 19:55:30 +0930373static int mctp_astlpc_layout_read(struct mctp_binding_astlpc *astlpc,
374 struct mctp_astlpc_layout *layout)
375{
376 struct mctp_lpcmap_hdr hdr;
377 int rc;
378
379 rc = mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
380 if (rc < 0)
381 return rc;
382
383 /* Flip the buffers as the names are defined in terms of the host */
384 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) {
385 layout->rx.offset = be32toh(hdr.layout.tx_offset);
386 layout->rx.size = be32toh(hdr.layout.tx_size);
387 layout->tx.offset = be32toh(hdr.layout.rx_offset);
388 layout->tx.size = be32toh(hdr.layout.rx_size);
389 } else {
390 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
391
392 layout->rx.offset = be32toh(hdr.layout.rx_offset);
393 layout->rx.size = be32toh(hdr.layout.rx_size);
394 layout->tx.offset = be32toh(hdr.layout.tx_offset);
395 layout->tx.size = be32toh(hdr.layout.tx_size);
396 }
397
398 return 0;
399}
400
401static int mctp_astlpc_layout_write(struct mctp_binding_astlpc *astlpc,
402 struct mctp_astlpc_layout *layout)
403{
404 uint32_t rx_size_be;
405
406 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) {
407 struct mctp_lpcmap_hdr hdr;
408
409 /*
410 * Flip the buffers as the names are defined in terms of the
411 * host
412 */
413 hdr.layout.rx_offset = htobe32(layout->tx.offset);
414 hdr.layout.rx_size = htobe32(layout->tx.size);
415 hdr.layout.tx_offset = htobe32(layout->rx.offset);
416 hdr.layout.tx_size = htobe32(layout->rx.size);
417
418 return mctp_astlpc_lpc_write(astlpc, &hdr.layout,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600419 offsetof(struct mctp_lpcmap_hdr,
420 layout),
421 sizeof(hdr.layout));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930422 }
423
424 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
425
426 /*
427 * As of v2 we only need to write rx_size - the offsets are controlled
428 * by the BMC, as is the BMC's rx_size (host tx_size).
429 */
430 rx_size_be = htobe32(layout->rx.size);
431 return mctp_astlpc_lpc_write(astlpc, &rx_size_be,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600432 offsetof(struct mctp_lpcmap_hdr,
433 layout.rx_size),
434 sizeof(rx_size_be));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930435}
436
Andrew Jeffery88412be2021-03-09 22:05:22 +1030437static bool
438mctp_astlpc_buffer_validate(const struct mctp_binding_astlpc *astlpc,
439 const struct mctp_astlpc_buffer *buf,
440 const char *name)
Andrew Jeffery3a540662020-05-26 19:55:30 +0930441{
442 /* Check for overflow */
443 if (buf->offset + buf->size < buf->offset) {
444 mctp_prerr(
445 "%s packet buffer parameters overflow: offset: 0x%" PRIx32
446 ", size: %" PRIu32,
447 name, buf->offset, buf->size);
448 return false;
449 }
450
451 /* Check that the buffers are contained within the allocated space */
452 if (buf->offset + buf->size > LPC_WIN_SIZE) {
453 mctp_prerr(
454 "%s packet buffer parameters exceed %uM window size: offset: 0x%" PRIx32
455 ", size: %" PRIu32,
456 name, (LPC_WIN_SIZE / (1024 * 1024)), buf->offset,
457 buf->size);
458 return false;
459 }
460
461 /* Check that the baseline transmission unit is supported */
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600462 if (buf->size <
463 astlpc->proto->packet_size(MCTP_PACKET_SIZE(MCTP_BTU))) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930464 mctp_prerr(
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600465 "%s packet buffer too small: Require %" PRIu32
466 " bytes to support the %u byte baseline transmission unit, found %" PRIu32,
Andrew Jeffery88412be2021-03-09 22:05:22 +1030467 name,
468 astlpc->proto->packet_size(MCTP_PACKET_SIZE(MCTP_BTU)),
Andrew Jeffery3a540662020-05-26 19:55:30 +0930469 MCTP_BTU, buf->size);
470 return false;
471 }
472
473 /* Check for overlap with the control space */
474 if (buf->offset < control_size) {
475 mctp_prerr(
476 "%s packet buffer overlaps control region {0x%" PRIx32
477 ", %" PRIu32 "}: Rx {0x%" PRIx32 ", %" PRIu32 "}",
478 name, 0U, control_size, buf->offset, buf->size);
479 return false;
480 }
481
482 return true;
483}
484
Andrew Jeffery88412be2021-03-09 22:05:22 +1030485static bool
486mctp_astlpc_layout_validate(const struct mctp_binding_astlpc *astlpc,
487 const struct mctp_astlpc_layout *layout)
Andrew Jeffery3a540662020-05-26 19:55:30 +0930488{
Andrew Jeffery88412be2021-03-09 22:05:22 +1030489 const struct mctp_astlpc_buffer *rx = &layout->rx;
490 const struct mctp_astlpc_buffer *tx = &layout->tx;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930491 bool rx_valid, tx_valid;
492
Andrew Jeffery88412be2021-03-09 22:05:22 +1030493 rx_valid = mctp_astlpc_buffer_validate(astlpc, rx, "Rx");
494 tx_valid = mctp_astlpc_buffer_validate(astlpc, tx, "Tx");
Andrew Jeffery3a540662020-05-26 19:55:30 +0930495
496 if (!(rx_valid && tx_valid))
497 return false;
498
499 /* Check that the buffers are disjoint */
500 if ((rx->offset <= tx->offset && rx->offset + rx->size > tx->offset) ||
501 (tx->offset <= rx->offset && tx->offset + tx->size > rx->offset)) {
502 mctp_prerr("Rx and Tx packet buffers overlap: Rx {0x%" PRIx32
503 ", %" PRIu32 "}, Tx {0x%" PRIx32 ", %" PRIu32 "}",
504 rx->offset, rx->size, tx->offset, tx->size);
505 return false;
506 }
507
508 return true;
509}
510
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930511static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc)
512{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930513 struct mctp_lpcmap_hdr hdr = { 0 };
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930514 uint8_t status;
Andrew Jeffery88412be2021-03-09 22:05:22 +1030515 uint32_t sz;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930516
517 /*
518 * The largest buffer size is half of the allocated MCTP space
519 * excluding the control space.
520 */
521 sz = ((LPC_WIN_SIZE - control_size) / 2);
522
523 /*
524 * Trim the MTU to a multiple of 16 to meet the requirements of 12.17
525 * Query Hop in DSP0236 v1.3.0.
526 */
Andrew Jeffery88412be2021-03-09 22:05:22 +1030527 sz = MCTP_BODY_SIZE(astlpc->proto->body_size(sz));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930528 sz &= ~0xfUL;
Andrew Jeffery88412be2021-03-09 22:05:22 +1030529 sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(sz));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930530
Andrew Jefferya9368982020-06-09 13:07:39 +0930531 if (astlpc->requested_mtu) {
Andrew Jeffery88412be2021-03-09 22:05:22 +1030532 uint32_t rpkt, rmtu;
Andrew Jefferya9368982020-06-09 13:07:39 +0930533
Andrew Jeffery88412be2021-03-09 22:05:22 +1030534 rmtu = astlpc->requested_mtu;
535 rpkt = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu));
536 sz = MIN(sz, rpkt);
Andrew Jefferya9368982020-06-09 13:07:39 +0930537 }
538
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930539 /* Flip the buffers as the names are defined in terms of the host */
Andrew Jeffery3a540662020-05-26 19:55:30 +0930540 astlpc->layout.tx.offset = control_size;
541 astlpc->layout.tx.size = sz;
542 astlpc->layout.rx.offset =
543 astlpc->layout.tx.offset + astlpc->layout.tx.size;
544 astlpc->layout.rx.size = sz;
545
Andrew Jeffery88412be2021-03-09 22:05:22 +1030546 if (!mctp_astlpc_layout_validate(astlpc, &astlpc->layout)) {
547 astlpc_prerr(astlpc, "Cannot support an MTU of %" PRIu32, sz);
Andrew Jefferya9368982020-06-09 13:07:39 +0930548 return -EINVAL;
549 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930550
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930551 hdr = (struct mctp_lpcmap_hdr){
552 .magic = htobe32(ASTLPC_MCTP_MAGIC),
553 .bmc_ver_min = htobe16(ASTLPC_VER_MIN),
554 .bmc_ver_cur = htobe16(ASTLPC_VER_CUR),
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930555
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930556 /* Flip the buffers back as we're now describing the host's
557 * configuration to the host */
Andrew Jeffery3a540662020-05-26 19:55:30 +0930558 .layout.rx_offset = htobe32(astlpc->layout.tx.offset),
559 .layout.rx_size = htobe32(astlpc->layout.tx.size),
560 .layout.tx_offset = htobe32(astlpc->layout.rx.offset),
561 .layout.tx_size = htobe32(astlpc->layout.rx.size),
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930562 };
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930563
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930564 mctp_astlpc_lpc_write(astlpc, &hdr, 0, sizeof(hdr));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930565
Andrew Jefferyb3b55a62020-07-06 13:34:18 +0930566 /*
567 * Set status indicating that the BMC is now active. Be explicit about
568 * clearing OBF; we're reinitialising the binding and so any previous
569 * buffer state is irrelevant.
570 */
571 status = KCS_STATUS_BMC_READY & ~KCS_STATUS_OBF;
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930572 return mctp_astlpc_kcs_set_status(astlpc, status);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930573}
574
575static int mctp_binding_astlpc_start_bmc(struct mctp_binding *b)
576{
577 struct mctp_binding_astlpc *astlpc =
578 container_of(b, struct mctp_binding_astlpc, binding);
579
Andrew Jeffery88412be2021-03-09 22:05:22 +1030580 astlpc->proto = &astlpc_protocol_version[ASTLPC_VER_CUR];
581
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930582 return mctp_astlpc_init_bmc(astlpc);
583}
584
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930585static bool mctp_astlpc_validate_version(uint16_t bmc_ver_min,
586 uint16_t bmc_ver_cur,
587 uint16_t host_ver_min,
588 uint16_t host_ver_cur)
589{
590 if (!(bmc_ver_min && bmc_ver_cur && host_ver_min && host_ver_cur)) {
591 mctp_prerr("Invalid version present in [%" PRIu16 ", %" PRIu16
592 "], [%" PRIu16 ", %" PRIu16 "]",
593 bmc_ver_min, bmc_ver_cur, host_ver_min,
594 host_ver_cur);
595 return false;
596 } else if (bmc_ver_min > bmc_ver_cur) {
597 mctp_prerr("Invalid bmc version range [%" PRIu16 ", %" PRIu16
598 "]",
599 bmc_ver_min, bmc_ver_cur);
600 return false;
601 } else if (host_ver_min > host_ver_cur) {
602 mctp_prerr("Invalid host version range [%" PRIu16 ", %" PRIu16
603 "]",
604 host_ver_min, host_ver_cur);
605 return false;
606 } else if ((host_ver_cur < bmc_ver_min) ||
607 (host_ver_min > bmc_ver_cur)) {
608 mctp_prerr(
609 "Unable to satisfy version negotiation with ranges [%" PRIu16
610 ", %" PRIu16 "] and [%" PRIu16 ", %" PRIu16 "]",
611 bmc_ver_min, bmc_ver_cur, host_ver_min, host_ver_cur);
612 return false;
613 }
614
615 return true;
616}
617
Andrew Jeffery3a540662020-05-26 19:55:30 +0930618static int mctp_astlpc_negotiate_layout_host(struct mctp_binding_astlpc *astlpc)
619{
620 struct mctp_astlpc_layout layout;
Andrew Jeffery88412be2021-03-09 22:05:22 +1030621 uint32_t rmtu;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930622 uint32_t sz;
623 int rc;
624
625 rc = mctp_astlpc_layout_read(astlpc, &layout);
626 if (rc < 0)
627 return rc;
628
Andrew Jeffery88412be2021-03-09 22:05:22 +1030629 if (!mctp_astlpc_layout_validate(astlpc, &layout)) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930630 astlpc_prerr(
631 astlpc,
632 "BMC provided invalid buffer layout: Rx {0x%" PRIx32
633 ", %" PRIu32 "}, Tx {0x%" PRIx32 ", %" PRIu32 "}",
634 layout.rx.offset, layout.rx.size, layout.tx.offset,
635 layout.tx.size);
636 return -EINVAL;
637 }
638
Andrew Jefferya9368982020-06-09 13:07:39 +0930639 astlpc_prinfo(astlpc, "Desire an MTU of %" PRIu32 " bytes",
640 astlpc->requested_mtu);
641
Andrew Jeffery88412be2021-03-09 22:05:22 +1030642 rmtu = astlpc->requested_mtu;
643 sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930644 layout.rx.size = sz;
645
Andrew Jeffery88412be2021-03-09 22:05:22 +1030646 if (!mctp_astlpc_layout_validate(astlpc, &layout)) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930647 astlpc_prerr(
648 astlpc,
649 "Generated invalid buffer layout with size %" PRIu32
650 ": Rx {0x%" PRIx32 ", %" PRIu32 "}, Tx {0x%" PRIx32
651 ", %" PRIu32 "}",
652 sz, layout.rx.offset, layout.rx.size, layout.tx.offset,
653 layout.tx.size);
654 return -EINVAL;
655 }
656
Andrew Jefferya9368982020-06-09 13:07:39 +0930657 astlpc_prinfo(astlpc, "Requesting MTU of %" PRIu32 " bytes",
658 astlpc->requested_mtu);
Andrew Jeffery3a540662020-05-26 19:55:30 +0930659
660 return mctp_astlpc_layout_write(astlpc, &layout);
661}
662
Andrew Jeffery88412be2021-03-09 22:05:22 +1030663static uint16_t mctp_astlpc_negotiate_version(uint16_t bmc_ver_min,
664 uint16_t bmc_ver_cur,
665 uint16_t host_ver_min,
666 uint16_t host_ver_cur)
667{
668 if (!mctp_astlpc_validate_version(bmc_ver_min, bmc_ver_cur,
669 host_ver_min, host_ver_cur))
670 return ASTLPC_VER_BAD;
671
672 if (bmc_ver_cur < host_ver_cur)
673 return bmc_ver_cur;
674
675 return host_ver_cur;
676}
677
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930678static int mctp_astlpc_init_host(struct mctp_binding_astlpc *astlpc)
679{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930680 const uint16_t ver_min_be = htobe16(ASTLPC_VER_MIN);
681 const uint16_t ver_cur_be = htobe16(ASTLPC_VER_CUR);
Andrew Jeffery88412be2021-03-09 22:05:22 +1030682 uint16_t bmc_ver_min, bmc_ver_cur, negotiated;
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930683 struct mctp_lpcmap_hdr hdr;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930684 uint8_t status;
685 int rc;
686
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930687 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930688 if (rc) {
689 mctp_prwarn("KCS status read failed");
690 return rc;
691 }
692
693 astlpc->kcs_status = status;
694
695 if (!(status & KCS_STATUS_BMC_READY))
696 return -EHOSTDOWN;
697
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930698 mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930699
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930700 bmc_ver_min = be16toh(hdr.bmc_ver_min);
701 bmc_ver_cur = be16toh(hdr.bmc_ver_cur);
702
Andrew Jeffery88412be2021-03-09 22:05:22 +1030703 /* Calculate the expected value of negotiated_ver */
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600704 negotiated = mctp_astlpc_negotiate_version(
705 bmc_ver_min, bmc_ver_cur, ASTLPC_VER_MIN, ASTLPC_VER_CUR);
Andrew Jeffery88412be2021-03-09 22:05:22 +1030706 if (!negotiated) {
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930707 astlpc_prerr(astlpc, "Cannot negotiate with invalid versions");
708 return -EINVAL;
709 }
710
Andrew Jeffery88412be2021-03-09 22:05:22 +1030711 /* Assign protocol ops so we can calculate the packet buffer sizes */
712 assert(negotiated < ARRAY_SIZE(astlpc_protocol_version));
713 astlpc->proto = &astlpc_protocol_version[negotiated];
714
715 /* Negotiate packet buffers in v2 style if the BMC supports it */
716 if (negotiated >= 2) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930717 rc = mctp_astlpc_negotiate_layout_host(astlpc);
718 if (rc < 0)
719 return rc;
720 }
721
Andrew Jeffery88412be2021-03-09 22:05:22 +1030722 /* Advertise the host's supported protocol versions */
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930723 mctp_astlpc_lpc_write(astlpc, &ver_min_be,
724 offsetof(struct mctp_lpcmap_hdr, host_ver_min),
725 sizeof(ver_min_be));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930726
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930727 mctp_astlpc_lpc_write(astlpc, &ver_cur_be,
728 offsetof(struct mctp_lpcmap_hdr, host_ver_cur),
729 sizeof(ver_cur_be));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930730
731 /* Send channel init command */
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930732 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, 0x0);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930733 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930734 astlpc_prwarn(astlpc, "KCS write failed");
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930735 }
736
Andrew Jeffery88412be2021-03-09 22:05:22 +1030737 /*
738 * Configure the host so `astlpc->proto->version == 0` holds until we
739 * receive a subsequent status update from the BMC. Until then,
740 * `astlpc->proto->version == 0` indicates that we're yet to complete
741 * the channel initialisation handshake.
742 *
743 * When the BMC provides a status update with KCS_STATUS_CHANNEL_ACTIVE
744 * set we will assign the appropriate protocol ops struct in accordance
745 * with `negotiated_ver`.
746 */
747 astlpc->proto = &astlpc_protocol_version[ASTLPC_VER_BAD];
748
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930749 return rc;
750}
751
752static int mctp_binding_astlpc_start_host(struct mctp_binding *b)
753{
754 struct mctp_binding_astlpc *astlpc =
755 container_of(b, struct mctp_binding_astlpc, binding);
756
757 return mctp_astlpc_init_host(astlpc);
758}
759
760static bool __mctp_astlpc_kcs_ready(struct mctp_binding_astlpc *astlpc,
761 uint8_t status, bool is_write)
762{
763 bool is_bmc;
764 bool ready_state;
765 uint8_t flag;
766
767 is_bmc = (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC);
768 flag = (is_bmc ^ is_write) ? KCS_STATUS_IBF : KCS_STATUS_OBF;
769 ready_state = is_write ? 0 : 1;
770
771 return !!(status & flag) == ready_state;
772}
773
774static inline bool
775mctp_astlpc_kcs_read_ready(struct mctp_binding_astlpc *astlpc, uint8_t status)
776{
777 return __mctp_astlpc_kcs_ready(astlpc, status, false);
778}
779
780static inline bool
781mctp_astlpc_kcs_write_ready(struct mctp_binding_astlpc *astlpc, uint8_t status)
782{
783 return __mctp_astlpc_kcs_ready(astlpc, status, true);
784}
785
Jeremy Kerr672c8852019-03-01 12:18:07 +0800786static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600787 uint8_t data)
Jeremy Kerr672c8852019-03-01 12:18:07 +0800788{
789 uint8_t status;
790 int rc;
791
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600792 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930793 if (rc) {
794 astlpc_prwarn(astlpc, "KCS status read failed");
795 return -EIO;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800796 }
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930797 if (!mctp_astlpc_kcs_write_ready(astlpc, status))
798 return -EBUSY;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800799
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930800 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530801 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930802 astlpc_prwarn(astlpc, "KCS data write failed");
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930803 return -EIO;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800804 }
805
806 return 0;
807}
808
809static int mctp_binding_astlpc_tx(struct mctp_binding *b,
Patrick Williamsa721c2d2022-12-04 14:30:26 -0600810 struct mctp_pktbuf *pkt)
Jeremy Kerr672c8852019-03-01 12:18:07 +0800811{
812 struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b);
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930813 uint32_t len, len_be;
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030814 struct mctp_hdr *hdr;
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930815 int rc;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800816
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030817 hdr = mctp_pktbuf_hdr(pkt);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800818 len = mctp_pktbuf_size(pkt);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030819
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930820 astlpc_prdebug(astlpc,
821 "%s: Transmitting %" PRIu32
822 "-byte packet (%hhu, %hhu, 0x%hhx)",
823 __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030824
Andrew Jeffery88412be2021-03-09 22:05:22 +1030825 if (len > astlpc->proto->body_size(astlpc->layout.tx.size)) {
Andrew Jeffery0721f582022-09-29 12:12:39 +0930826 astlpc_prwarn(astlpc, "invalid TX len %" PRIu32 ": %" PRIu32,
827 len,
828 astlpc->proto->body_size(astlpc->layout.tx.size));
829 return -EMSGSIZE;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800830 }
831
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930832 mctp_binding_set_tx_enabled(b, false);
833
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930834 len_be = htobe32(len);
835 mctp_astlpc_lpc_write(astlpc, &len_be, astlpc->layout.tx.offset,
836 sizeof(len_be));
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030837
838 astlpc->proto->pktbuf_protect(pkt);
839 len = mctp_pktbuf_size(pkt);
840
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930841 mctp_astlpc_lpc_write(astlpc, hdr, astlpc->layout.tx.offset + 4, len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800842
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930843 astlpc->layout.tx.state = buffer_state_prepared;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800844
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930845 rc = mctp_astlpc_kcs_send(astlpc, 0x1);
846 if (!rc)
847 astlpc->layout.tx.state = buffer_state_released;
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030848
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930849 return rc == -EBUSY ? 0 : rc;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800850}
851
Andrew Jeffery3a540662020-05-26 19:55:30 +0930852static uint32_t mctp_astlpc_calculate_mtu(struct mctp_binding_astlpc *astlpc,
853 struct mctp_astlpc_layout *layout)
854{
Andrew Jeffery88412be2021-03-09 22:05:22 +1030855 uint32_t low, high, limit, rpkt;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930856
857 /* Derive the largest MTU the BMC _can_ support */
858 low = MIN(astlpc->layout.rx.offset, astlpc->layout.tx.offset);
859 high = MAX(astlpc->layout.rx.offset, astlpc->layout.tx.offset);
860 limit = high - low;
861
Andrew Jefferya9368982020-06-09 13:07:39 +0930862 /* Determine the largest MTU the BMC _wants_ to support */
863 if (astlpc->requested_mtu) {
Andrew Jeffery88412be2021-03-09 22:05:22 +1030864 uint32_t rmtu = astlpc->requested_mtu;
Andrew Jefferya9368982020-06-09 13:07:39 +0930865
Andrew Jeffery88412be2021-03-09 22:05:22 +1030866 rpkt = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu));
867 limit = MIN(limit, rpkt);
Andrew Jefferya9368982020-06-09 13:07:39 +0930868 }
Andrew Jeffery3a540662020-05-26 19:55:30 +0930869
870 /* Determine the accepted MTU, applied both directions by convention */
Andrew Jeffery88412be2021-03-09 22:05:22 +1030871 rpkt = MIN(limit, layout->tx.size);
872 return MCTP_BODY_SIZE(astlpc->proto->body_size(rpkt));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930873}
874
Andrew Jeffery88412be2021-03-09 22:05:22 +1030875static int mctp_astlpc_negotiate_layout_bmc(struct mctp_binding_astlpc *astlpc)
Andrew Jeffery3a540662020-05-26 19:55:30 +0930876{
877 struct mctp_astlpc_layout proposed, pending;
878 uint32_t sz, mtu;
879 int rc;
880
Andrew Jeffery88412be2021-03-09 22:05:22 +1030881 /* Do we have a valid protocol version? */
882 if (!astlpc->proto->version)
883 return -EINVAL;
884
Andrew Jeffery3a540662020-05-26 19:55:30 +0930885 /* Extract the host's proposed layout */
886 rc = mctp_astlpc_layout_read(astlpc, &proposed);
887 if (rc < 0)
888 return rc;
889
Andrew Jeffery88412be2021-03-09 22:05:22 +1030890 /* Do we have a reasonable layout? */
891 if (!mctp_astlpc_layout_validate(astlpc, &proposed))
Andrew Jeffery3a540662020-05-26 19:55:30 +0930892 return -EINVAL;
893
894 /* Negotiate the MTU */
895 mtu = mctp_astlpc_calculate_mtu(astlpc, &proposed);
Andrew Jeffery88412be2021-03-09 22:05:22 +1030896 sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(mtu));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930897
898 /*
899 * Use symmetric MTUs by convention and to pass constraints in rx/tx
900 * functions
901 */
902 pending = astlpc->layout;
903 pending.tx.size = sz;
904 pending.rx.size = sz;
905
Andrew Jeffery88412be2021-03-09 22:05:22 +1030906 if (mctp_astlpc_layout_validate(astlpc, &pending)) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930907 /* We found a sensible Rx MTU, so honour it */
908 astlpc->layout = pending;
909
910 /* Enforce the negotiated MTU */
911 rc = mctp_astlpc_layout_write(astlpc, &astlpc->layout);
912 if (rc < 0)
913 return rc;
914
915 astlpc_prinfo(astlpc, "Negotiated an MTU of %" PRIu32 " bytes",
916 mtu);
917 } else {
918 astlpc_prwarn(astlpc, "MTU negotiation failed");
919 return -EINVAL;
920 }
921
Andrew Jeffery88412be2021-03-09 22:05:22 +1030922 if (astlpc->proto->version >= 2)
Andrew Jeffery3a540662020-05-26 19:55:30 +0930923 astlpc->binding.pkt_size = MCTP_PACKET_SIZE(mtu);
924
925 return 0;
926}
927
Jeremy Kerr672c8852019-03-01 12:18:07 +0800928static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc)
929{
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930930 uint16_t negotiated, negotiated_be;
931 struct mctp_lpcmap_hdr hdr;
932 uint8_t status;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930933 int rc;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930934
935 mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
936
937 /* Version negotiation */
938 negotiated =
939 mctp_astlpc_negotiate_version(ASTLPC_VER_MIN, ASTLPC_VER_CUR,
940 be16toh(hdr.host_ver_min),
941 be16toh(hdr.host_ver_cur));
942
Andrew Jeffery88412be2021-03-09 22:05:22 +1030943 /* MTU negotiation requires knowing which protocol we'll use */
944 assert(negotiated < ARRAY_SIZE(astlpc_protocol_version));
945 astlpc->proto = &astlpc_protocol_version[negotiated];
946
Andrew Jeffery3a540662020-05-26 19:55:30 +0930947 /* Host Rx MTU negotiation: Failure terminates channel init */
Andrew Jeffery88412be2021-03-09 22:05:22 +1030948 rc = mctp_astlpc_negotiate_layout_bmc(astlpc);
Andrew Jeffery3a540662020-05-26 19:55:30 +0930949 if (rc < 0)
950 negotiated = ASTLPC_VER_BAD;
951
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930952 /* Populate the negotiated version */
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930953 negotiated_be = htobe16(negotiated);
954 mctp_astlpc_lpc_write(astlpc, &negotiated_be,
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930955 offsetof(struct mctp_lpcmap_hdr, negotiated_ver),
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930956 sizeof(negotiated_be));
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930957
Andrew Jefferyfe763e92022-08-05 23:16:17 +0930958 /* Track buffer ownership */
959 astlpc->layout.tx.state = buffer_state_acquired;
960 astlpc->layout.rx.state = buffer_state_released;
961
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930962 /* Finalise the configuration */
963 status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
964 if (negotiated > 0) {
965 astlpc_prinfo(astlpc, "Negotiated binding version %" PRIu16,
966 negotiated);
967 status |= KCS_STATUS_CHANNEL_ACTIVE;
968 } else {
Andrew Jeffery88412be2021-03-09 22:05:22 +1030969 astlpc_prerr(astlpc, "Failed to initialise channel");
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930970 }
Jeremy Kerr672c8852019-03-01 12:18:07 +0800971
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930972 mctp_astlpc_kcs_set_status(astlpc, status);
973
974 mctp_binding_set_tx_enabled(&astlpc->binding,
975 status & KCS_STATUS_CHANNEL_ACTIVE);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800976}
977
978static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
979{
980 struct mctp_pktbuf *pkt;
Rashmica Guptaf447b4f2022-10-11 17:44:31 +1100981 struct mctp_hdr *hdr;
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030982 uint32_t body, packet;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800983
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030984 mctp_astlpc_lpc_read(astlpc, &body, astlpc->layout.rx.offset,
985 sizeof(body));
986 body = be32toh(body);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530987
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030988 if (body > astlpc->proto->body_size(astlpc->layout.rx.size)) {
989 astlpc_prwarn(astlpc, "invalid RX len 0x%x", body);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800990 return;
991 }
992
Andrew Jeffery0f05b6c2022-11-15 11:37:50 +1030993 if ((size_t)body > astlpc->binding.pkt_size) {
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030994 astlpc_prwarn(astlpc, "invalid RX len 0x%x", body);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800995 return;
996 }
997
Andrew Jefferyeba19a32021-03-09 23:09:40 +1030998 /* Eliminate the medium-specific header that we just read */
999 packet = astlpc->proto->packet_size(body) - 4;
1000 pkt = mctp_pktbuf_alloc(&astlpc->binding, packet);
Christian Geddesae59f4f2021-09-01 09:54:25 -05001001 if (!pkt) {
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001002 astlpc_prwarn(astlpc, "unable to allocate pktbuf len 0x%x",
1003 packet);
Christian Geddesae59f4f2021-09-01 09:54:25 -05001004 return;
1005 }
Jeremy Kerr672c8852019-03-01 12:18:07 +08001006
Andrew Jefferyeba19a32021-03-09 23:09:40 +10301007 /*
1008 * Read payload and medium-specific trailer from immediately after the
1009 * medium-specific header.
1010 */
Andrew Jeffery55fb90b2020-05-12 13:54:37 +09301011 mctp_astlpc_lpc_read(astlpc, mctp_pktbuf_hdr(pkt),
Andrew Jefferyeba19a32021-03-09 23:09:40 +10301012 astlpc->layout.rx.offset + 4, packet);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001013
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301014 astlpc->layout.rx.state = buffer_state_prepared;
1015
Christian Geddesae59f4f2021-09-01 09:54:25 -05001016 /* Inform the other side of the MCTP interface that we have read
1017 * the packet off the bus before handling the contents of the packet.
1018 */
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301019 if (!mctp_astlpc_kcs_send(astlpc, 0x2))
1020 astlpc->layout.rx.state = buffer_state_released;
Christian Geddesae59f4f2021-09-01 09:54:25 -05001021
Rashmica Guptaf447b4f2022-10-11 17:44:31 +11001022 hdr = mctp_pktbuf_hdr(pkt);
1023 if (hdr->ver != 1) {
1024 mctp_pktbuf_free(pkt);
1025 astlpc_prdebug(astlpc, "Dropped packet with invalid version");
1026 return;
1027 }
1028
Andrew Jefferyeba19a32021-03-09 23:09:40 +10301029 /*
1030 * v3 will validate the CRC32 in the medium-specific trailer and adjust
1031 * the packet size accordingly. On older protocols validation is a no-op
1032 * that always returns true.
1033 */
1034 if (astlpc->proto->pktbuf_validate(pkt)) {
1035 mctp_bus_rx(&astlpc->binding, pkt);
1036 } else {
1037 /* TODO: Drop any associated assembly */
1038 mctp_pktbuf_free(pkt);
1039 astlpc_prdebug(astlpc, "Dropped corrupt packet");
1040 }
Jeremy Kerr672c8852019-03-01 12:18:07 +08001041}
1042
1043static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc)
1044{
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301045 astlpc->layout.tx.state = buffer_state_acquired;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001046 mctp_binding_set_tx_enabled(&astlpc->binding, true);
1047}
1048
Andrew Jeffery4e8264b2020-05-23 20:34:33 +09301049static int mctp_astlpc_finalise_channel(struct mctp_binding_astlpc *astlpc)
1050{
Andrew Jeffery3a540662020-05-26 19:55:30 +09301051 struct mctp_astlpc_layout layout;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +09301052 uint16_t negotiated;
1053 int rc;
1054
1055 rc = mctp_astlpc_lpc_read(astlpc, &negotiated,
1056 offsetof(struct mctp_lpcmap_hdr,
1057 negotiated_ver),
1058 sizeof(negotiated));
1059 if (rc < 0)
1060 return rc;
1061
1062 negotiated = be16toh(negotiated);
Andrew Jeffery88412be2021-03-09 22:05:22 +10301063 astlpc_prerr(astlpc, "Version negotiation got: %u", negotiated);
Andrew Jeffery4e8264b2020-05-23 20:34:33 +09301064
1065 if (negotiated == ASTLPC_VER_BAD || negotiated < ASTLPC_VER_MIN ||
1066 negotiated > ASTLPC_VER_CUR) {
1067 astlpc_prerr(astlpc, "Failed to negotiate version, got: %u\n",
1068 negotiated);
1069 return -EINVAL;
1070 }
1071
Andrew Jeffery88412be2021-03-09 22:05:22 +10301072 assert(negotiated < ARRAY_SIZE(astlpc_protocol_version));
1073 astlpc->proto = &astlpc_protocol_version[negotiated];
Andrew Jeffery4e8264b2020-05-23 20:34:33 +09301074
Andrew Jeffery3a540662020-05-26 19:55:30 +09301075 rc = mctp_astlpc_layout_read(astlpc, &layout);
1076 if (rc < 0)
1077 return rc;
1078
Andrew Jeffery88412be2021-03-09 22:05:22 +10301079 if (!mctp_astlpc_layout_validate(astlpc, &layout)) {
Andrew Jeffery3a540662020-05-26 19:55:30 +09301080 mctp_prerr("BMC proposed invalid buffer parameters");
1081 return -EINVAL;
1082 }
1083
1084 astlpc->layout = layout;
1085
1086 if (negotiated >= 2)
1087 astlpc->binding.pkt_size =
Andrew Jeffery88412be2021-03-09 22:05:22 +10301088 astlpc->proto->body_size(astlpc->layout.tx.size);
Andrew Jeffery3a540662020-05-26 19:55:30 +09301089
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301090 /* Track buffer ownership */
1091 astlpc->layout.tx.state = buffer_state_acquired;
1092 astlpc->layout.rx.state = buffer_state_released;
1093
Andrew Jeffery4e8264b2020-05-23 20:34:33 +09301094 return 0;
1095}
1096
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301097static int mctp_astlpc_update_channel(struct mctp_binding_astlpc *astlpc,
1098 uint8_t status)
1099{
1100 uint8_t updated;
1101 int rc = 0;
1102
1103 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
1104
1105 updated = astlpc->kcs_status ^ status;
1106
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301107 astlpc_prdebug(astlpc, "%s: status: 0x%x, update: 0x%x", __func__,
1108 status, updated);
1109
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301110 if (updated & KCS_STATUS_BMC_READY) {
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301111 if (status & KCS_STATUS_BMC_READY) {
1112 astlpc->kcs_status = status;
1113 return astlpc->binding.start(&astlpc->binding);
1114 } else {
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301115 /* Shut down the channel */
1116 astlpc->layout.rx.state = buffer_state_idle;
1117 astlpc->layout.tx.state = buffer_state_idle;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301118 mctp_binding_set_tx_enabled(&astlpc->binding, false);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301119 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301120 }
1121
Andrew Jeffery88412be2021-03-09 22:05:22 +10301122 if (astlpc->proto->version == 0 ||
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001123 updated & KCS_STATUS_CHANNEL_ACTIVE) {
Andrew Jeffery4e8264b2020-05-23 20:34:33 +09301124 bool enable;
1125
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301126 astlpc->layout.rx.state = buffer_state_idle;
1127 astlpc->layout.tx.state = buffer_state_idle;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +09301128 rc = mctp_astlpc_finalise_channel(astlpc);
1129 enable = (status & KCS_STATUS_CHANNEL_ACTIVE) && rc == 0;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +09301130 mctp_binding_set_tx_enabled(&astlpc->binding, enable);
1131 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301132
1133 astlpc->kcs_status = status;
1134
1135 return rc;
1136}
1137
Jeremy Kerr672c8852019-03-01 12:18:07 +08001138int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
1139{
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301140 uint8_t status, data;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001141 int rc;
1142
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301143 if (astlpc->layout.rx.state == buffer_state_prepared)
1144 if (!mctp_astlpc_kcs_send(astlpc, 0x2))
1145 astlpc->layout.rx.state = buffer_state_released;
1146
1147 if (astlpc->layout.tx.state == buffer_state_prepared)
1148 if (!mctp_astlpc_kcs_send(astlpc, 0x1))
1149 astlpc->layout.tx.state = buffer_state_released;
1150
Andrew Jefferyf13cb972020-05-28 09:30:09 +09301151 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301152 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301153 astlpc_prwarn(astlpc, "KCS read error");
Jeremy Kerr672c8852019-03-01 12:18:07 +08001154 return -1;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001155 }
1156
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301157 astlpc_prdebug(astlpc, "%s: status: 0x%hhx", __func__, status);
Andrew Jefferyedfe3832020-02-06 11:52:11 +10301158
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301159 if (!mctp_astlpc_kcs_read_ready(astlpc, status))
Jeremy Kerr672c8852019-03-01 12:18:07 +08001160 return 0;
1161
Andrew Jefferyf13cb972020-05-28 09:30:09 +09301162 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_DATA, &data);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301163 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301164 astlpc_prwarn(astlpc, "KCS data read error");
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301165 return -1;
1166 }
1167
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301168 astlpc_prdebug(astlpc, "%s: data: 0x%hhx", __func__, data);
Andrew Jefferyedfe3832020-02-06 11:52:11 +10301169
Andrew Jeffery88412be2021-03-09 22:05:22 +10301170 if (!astlpc->proto->version && !(data == 0x0 || data == 0xff)) {
Andrew Jefferyafcb7012021-01-28 17:00:36 +10301171 astlpc_prwarn(astlpc, "Invalid message for binding state: 0x%x",
1172 data);
1173 return 0;
1174 }
1175
Jeremy Kerr672c8852019-03-01 12:18:07 +08001176 switch (data) {
1177 case 0x0:
1178 mctp_astlpc_init_channel(astlpc);
1179 break;
1180 case 0x1:
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301181 if (astlpc->layout.rx.state != buffer_state_released) {
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001182 astlpc_prerr(
1183 astlpc,
1184 "Protocol error: Invalid Rx buffer state for event %d: %d\n",
1185 data, astlpc->layout.rx.state);
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301186 return 0;
1187 }
Jeremy Kerr672c8852019-03-01 12:18:07 +08001188 mctp_astlpc_rx_start(astlpc);
1189 break;
1190 case 0x2:
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301191 if (astlpc->layout.tx.state != buffer_state_released) {
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001192 astlpc_prerr(
1193 astlpc,
1194 "Protocol error: Invalid Tx buffer state for event %d: %d\n",
1195 data, astlpc->layout.tx.state);
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301196 return 0;
1197 }
Jeremy Kerr672c8852019-03-01 12:18:07 +08001198 mctp_astlpc_tx_complete(astlpc);
1199 break;
Jeremy Kerr1a4b55a2019-06-24 14:22:39 +08001200 case 0xff:
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301201 /* No responsibilities for the BMC on 0xff */
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301202 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) {
1203 rc = mctp_astlpc_update_channel(astlpc, status);
1204 if (rc < 0)
1205 return rc;
1206 }
1207 break;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001208 default:
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301209 astlpc_prwarn(astlpc, "unknown message 0x%x", data);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001210 }
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301211
1212 /* Handle silent loss of bmc-ready */
1213 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) {
1214 if (!(status & KCS_STATUS_BMC_READY && data == 0xff))
1215 return mctp_astlpc_update_channel(astlpc, status);
1216 }
1217
1218 return rc;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001219}
1220
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301221/* allocate and basic initialisation */
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301222static struct mctp_binding_astlpc *__mctp_astlpc_init(uint8_t mode,
1223 uint32_t mtu)
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301224{
1225 struct mctp_binding_astlpc *astlpc;
1226
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301227 assert((mode == MCTP_BINDING_ASTLPC_MODE_BMC) ||
1228 (mode == MCTP_BINDING_ASTLPC_MODE_HOST));
1229
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301230 astlpc = __mctp_alloc(sizeof(*astlpc));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301231 if (!astlpc)
1232 return NULL;
1233
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301234 memset(astlpc, 0, sizeof(*astlpc));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301235 astlpc->mode = mode;
1236 astlpc->lpc_map = NULL;
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301237 astlpc->layout.rx.state = buffer_state_idle;
1238 astlpc->layout.tx.state = buffer_state_idle;
Andrew Jefferya9368982020-06-09 13:07:39 +09301239 astlpc->requested_mtu = mtu;
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301240 astlpc->binding.name = "astlpc";
1241 astlpc->binding.version = 1;
Andrew Jeffery1a4f4412021-01-28 13:42:02 +10301242 astlpc->binding.pkt_size =
1243 MCTP_PACKET_SIZE(mtu > MCTP_BTU ? mtu : MCTP_BTU);
Andrew Jefferyeba19a32021-03-09 23:09:40 +10301244 astlpc->binding.pkt_header = 4;
1245 astlpc->binding.pkt_trailer = 4;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301246 astlpc->binding.tx = mctp_binding_astlpc_tx;
1247 if (mode == MCTP_BINDING_ASTLPC_MODE_BMC)
1248 astlpc->binding.start = mctp_binding_astlpc_start_bmc;
1249 else if (mode == MCTP_BINDING_ASTLPC_MODE_HOST)
1250 astlpc->binding.start = mctp_binding_astlpc_start_host;
1251 else {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301252 astlpc_prerr(astlpc, "%s: Invalid mode: %d\n", __func__, mode);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301253 __mctp_free(astlpc);
1254 return NULL;
1255 }
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301256
1257 return astlpc;
1258}
1259
Jeremy Kerr3b36d172019-09-04 11:56:09 +08001260struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b)
1261{
1262 return &b->binding;
1263}
1264
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301265struct mctp_binding_astlpc *
1266mctp_astlpc_init(uint8_t mode, uint32_t mtu, void *lpc_map,
1267 const struct mctp_binding_astlpc_ops *ops, void *ops_data)
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301268{
1269 struct mctp_binding_astlpc *astlpc;
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301270
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301271 if (!(mode == MCTP_BINDING_ASTLPC_MODE_BMC ||
1272 mode == MCTP_BINDING_ASTLPC_MODE_HOST)) {
1273 mctp_prerr("Unknown binding mode: %u", mode);
1274 return NULL;
1275 }
1276
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301277 astlpc = __mctp_astlpc_init(mode, mtu);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301278 if (!astlpc)
1279 return NULL;
1280
1281 memcpy(&astlpc->ops, ops, sizeof(astlpc->ops));
1282 astlpc->ops_data = ops_data;
1283 astlpc->lpc_map = lpc_map;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301284 astlpc->mode = mode;
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301285
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301286 return astlpc;
1287}
1288
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301289struct mctp_binding_astlpc *
1290mctp_astlpc_init_ops(const struct mctp_binding_astlpc_ops *ops, void *ops_data,
1291 void *lpc_map)
1292{
1293 return mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, MCTP_BTU, lpc_map,
1294 ops, ops_data);
1295}
1296
Andrew Jeffery4663f672020-03-10 23:36:24 +10301297void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc)
1298{
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301299 /* Clear channel-active and bmc-ready */
1300 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC)
Andrew Jeffery7c4509a2021-08-26 14:56:16 +09301301 mctp_astlpc_kcs_set_status(astlpc, 0);
Andrew Jeffery4663f672020-03-10 23:36:24 +10301302 __mctp_free(astlpc);
1303}
1304
Jeremy Kerrb214c642019-11-27 11:34:00 +08001305#ifdef MCTP_HAVE_FILEIO
Andrew Jeffery55fb90b2020-05-12 13:54:37 +09301306
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301307static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001308{
1309 struct aspeed_lpc_ctrl_mapping map = {
1310 .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
1311 .window_id = 0, /* There's only one */
1312 .flags = 0,
1313 .addr = 0,
1314 .offset = 0,
1315 .size = 0
1316 };
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301317 void *lpc_map_base;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001318 int fd, rc;
1319
1320 fd = open(lpc_path, O_RDWR | O_SYNC);
1321 if (fd < 0) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301322 astlpc_prwarn(astlpc, "LPC open (%s) failed", lpc_path);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001323 return -1;
1324 }
1325
1326 rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map);
1327 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301328 astlpc_prwarn(astlpc, "LPC GET_SIZE failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +08001329 close(fd);
1330 return -1;
1331 }
1332
Andrew Jefferyc1d5c542021-10-27 15:25:48 +10301333 /*
1334 * 🚨🚨🚨
1335 *
1336 * Decouple ourselves from hiomapd[1] (another user of the FW2AHB) by
1337 * mapping the FW2AHB to the reserved memory here as well.
1338 *
1339 * It's not possible to use the MCTP ASTLPC binding on machines that
1340 * need the FW2AHB bridge mapped anywhere except to the reserved memory
1341 * (e.g. the host SPI NOR).
1342 *
1343 * [1] https://github.com/openbmc/hiomapd/
1344 *
1345 * 🚨🚨🚨
1346 *
1347 * The following calculation must align with what's going on in
1348 * hiomapd's lpc.c so as not to disrupt its behaviour:
1349 *
1350 * https://github.com/openbmc/hiomapd/blob/5ff50e3cbd7702aefc185264e4adfb9952040575/lpc.c#L68
1351 *
1352 * 🚨🚨🚨
1353 */
1354
1355 /* Map the reserved memory at the top of the 28-bit LPC firmware address space */
1356 map.addr = 0x0FFFFFFF & -map.size;
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001357 astlpc_prinfo(
1358 astlpc,
1359 "Configuring FW2AHB to map reserved memory at 0x%08x for 0x%x in the LPC FW cycle address-space",
1360 map.addr, map.size);
Andrew Jefferyc1d5c542021-10-27 15:25:48 +10301361
1362 rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_MAP, &map);
1363 if (rc) {
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001364 astlpc_prwarn(astlpc,
1365 "Failed to map FW2AHB to reserved memory");
Andrew Jefferyc1d5c542021-10-27 15:25:48 +10301366 close(fd);
1367 return -1;
1368 }
1369
1370 /* Map the reserved memory into our address space */
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301371 lpc_map_base =
1372 mmap(NULL, map.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1373 if (lpc_map_base == MAP_FAILED) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301374 astlpc_prwarn(astlpc, "LPC mmap failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +08001375 rc = -1;
1376 } else {
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301377 astlpc->lpc_map = lpc_map_base + map.size - LPC_WIN_SIZE;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001378 }
1379
1380 close(fd);
1381
1382 return rc;
1383}
1384
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301385static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001386{
1387 astlpc->kcs_fd = open(kcs_path, O_RDWR);
1388 if (astlpc->kcs_fd < 0)
1389 return -1;
1390
1391 return 0;
1392}
1393
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301394static int __mctp_astlpc_fileio_kcs_read(void *arg,
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001395 enum mctp_binding_astlpc_kcs_reg reg,
1396 uint8_t *val)
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301397{
1398 struct mctp_binding_astlpc *astlpc = arg;
1399 off_t offset = reg;
1400 int rc;
1401
1402 rc = pread(astlpc->kcs_fd, val, 1, offset);
1403
1404 return rc == 1 ? 0 : -1;
1405}
1406
1407static int __mctp_astlpc_fileio_kcs_write(void *arg,
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001408 enum mctp_binding_astlpc_kcs_reg reg,
1409 uint8_t val)
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301410{
1411 struct mctp_binding_astlpc *astlpc = arg;
1412 off_t offset = reg;
1413 int rc;
1414
1415 rc = pwrite(astlpc->kcs_fd, &val, 1, offset);
1416
1417 return rc == 1 ? 0 : -1;
1418}
1419
Andrew Jeffery1111c6a2022-07-25 20:44:39 +09301420int mctp_astlpc_init_pollfd(struct mctp_binding_astlpc *astlpc,
1421 struct pollfd *pollfd)
1422{
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301423 bool release;
1424
Andrew Jeffery1111c6a2022-07-25 20:44:39 +09301425 pollfd->fd = astlpc->kcs_fd;
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301426 pollfd->events = 0;
1427
1428 release = astlpc->layout.rx.state == buffer_state_prepared ||
Patrick Williamsa721c2d2022-12-04 14:30:26 -06001429 astlpc->layout.tx.state == buffer_state_prepared;
Andrew Jefferyfe763e92022-08-05 23:16:17 +09301430
1431 pollfd->events = release ? POLLOUT : POLLIN;
Andrew Jeffery1111c6a2022-07-25 20:44:39 +09301432
1433 return 0;
1434}
1435
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301436struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001437{
1438 struct mctp_binding_astlpc *astlpc;
1439 int rc;
1440
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301441 /*
1442 * If we're doing file IO then we're very likely not running
Andrew Jeffery8877c462020-06-15 12:22:53 +09301443 * freestanding, so lets assume that we're on the BMC side.
1444 *
1445 * Requesting an MTU of 0 requests the largest possible MTU, whatever
1446 * value that might take.
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301447 */
Andrew Jeffery8877c462020-06-15 12:22:53 +09301448 astlpc = __mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, 0);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301449 if (!astlpc)
1450 return NULL;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001451
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301452 /* Set internal operations for kcs. We use direct accesses to the lpc
1453 * map area */
1454 astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read;
1455 astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write;
1456 astlpc->ops_data = astlpc;
1457
1458 rc = mctp_astlpc_init_fileio_lpc(astlpc);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001459 if (rc) {
1460 free(astlpc);
1461 return NULL;
1462 }
1463
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301464 rc = mctp_astlpc_init_fileio_kcs(astlpc);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001465 if (rc) {
1466 free(astlpc);
1467 return NULL;
1468 }
1469
Jeremy Kerr672c8852019-03-01 12:18:07 +08001470 return astlpc;
1471}
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301472#else
Andrew Jeffery5b2702d2022-07-25 16:21:43 +09301473struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void)
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301474{
Andrew Jeffery5b2702d2022-07-25 16:21:43 +09301475 mctp_prlog(MCTP_LOG_ERR, "%s: Missing support for file IO", __func__);
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301476 return NULL;
1477}
Jeremy Kerr672c8852019-03-01 12:18:07 +08001478
Andrew Jeffery1111c6a2022-07-25 20:44:39 +09301479int mctp_astlpc_init_pollfd(struct mctp_binding_astlpc *astlpc __unused,
1480 struct pollfd *pollfd __unused)
1481{
1482 mctp_prlog(MCTP_LOG_ERR, "%s: Missing support for file IO", __func__);
1483 return -1;
1484}
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301485#endif