blob: 93e52711685a3fb97f8677c57167528d3e04d62a [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
21#include "libmctp.h"
22#include "libmctp-alloc.h"
23#include "libmctp-log.h"
24#include "libmctp-astlpc.h"
Przemyslaw Czarnowskiff25d7e2020-03-26 11:39:37 +010025#include "container_of.h"
Andrew Jeffery4622cad2020-11-03 22:20:18 +103026#include "range.h"
Jeremy Kerr672c8852019-03-01 12:18:07 +080027
Jeremy Kerrb214c642019-11-27 11:34:00 +080028#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr92a10a62019-08-28 16:55:54 +053029
Jeremy Kerrc6f676d2019-12-19 09:24:06 +080030#include <unistd.h>
Jeremy Kerr92a10a62019-08-28 16:55:54 +053031#include <fcntl.h>
32#include <sys/ioctl.h>
33#include <sys/mman.h>
34#include <linux/aspeed-lpc-ctrl.h>
35
36/* kernel interface */
37static const char *kcs_path = "/dev/mctp0";
38static const char *lpc_path = "/dev/aspeed-lpc-ctrl";
39
40#endif
41
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093042struct mctp_astlpc_buffer {
43 uint32_t offset;
44 uint32_t size;
45};
46
47struct mctp_astlpc_layout {
48 struct mctp_astlpc_buffer rx;
49 struct mctp_astlpc_buffer tx;
50};
51
Andrew Jeffery88412be2021-03-09 22:05:22 +103052struct mctp_astlpc_protocol {
53 uint16_t version;
54 uint32_t (*packet_size)(uint32_t body);
55 uint32_t (*body_size)(uint32_t packet);
56};
57
Jeremy Kerr672c8852019-03-01 12:18:07 +080058struct mctp_binding_astlpc {
59 struct mctp_binding binding;
Jeremy Kerrbc53d352019-08-28 14:26:14 +053060
Andrew Jeffery55fb90b2020-05-12 13:54:37 +093061 void *lpc_map;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093062 struct mctp_astlpc_layout layout;
63
64 uint8_t mode;
Andrew Jefferya9368982020-06-09 13:07:39 +093065 uint32_t requested_mtu;
Jeremy Kerrbc53d352019-08-28 14:26:14 +053066
Andrew Jeffery88412be2021-03-09 22:05:22 +103067 const struct mctp_astlpc_protocol *proto;
68
Jeremy Kerrbc53d352019-08-28 14:26:14 +053069 /* direct ops data */
Andrew Jeffery55fb90b2020-05-12 13:54:37 +093070 struct mctp_binding_astlpc_ops ops;
71 void *ops_data;
Jeremy Kerrbc53d352019-08-28 14:26:14 +053072
73 /* fileio ops data */
Andrew Jeffery979c6a12020-05-23 20:04:49 +093074 int kcs_fd;
75 uint8_t kcs_status;
Jeremy Kerr672c8852019-03-01 12:18:07 +080076
77 bool running;
Jeremy Kerr672c8852019-03-01 12:18:07 +080078};
79
Jeremy Kerr672c8852019-03-01 12:18:07 +080080#define binding_to_astlpc(b) \
81 container_of(b, struct mctp_binding_astlpc, binding)
82
Andrew Jeffery9101a2a2020-05-22 16:08:03 +093083#define astlpc_prlog(ctx, lvl, fmt, ...) \
84 do { \
85 bool __bmc = ((ctx)->mode == MCTP_BINDING_ASTLPC_MODE_BMC); \
86 mctp_prlog(lvl, pr_fmt("%s: " fmt), __bmc ? "bmc" : "host", \
87 ##__VA_ARGS__); \
88 } while (0)
89
90#define astlpc_prerr(ctx, fmt, ...) \
91 astlpc_prlog(ctx, MCTP_LOG_ERR, fmt, ##__VA_ARGS__)
92#define astlpc_prwarn(ctx, fmt, ...) \
93 astlpc_prlog(ctx, MCTP_LOG_WARNING, fmt, ##__VA_ARGS__)
94#define astlpc_prinfo(ctx, fmt, ...) \
95 astlpc_prlog(ctx, MCTP_LOG_INFO, fmt, ##__VA_ARGS__)
96#define astlpc_prdebug(ctx, fmt, ...) \
97 astlpc_prlog(ctx, MCTP_LOG_DEBUG, fmt, ##__VA_ARGS__)
98
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093099/* clang-format off */
100#define ASTLPC_MCTP_MAGIC 0x4d435450
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930101#define ASTLPC_VER_BAD 0
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930102#define ASTLPC_VER_MIN 1
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930103
Andrew Jeffery3a540662020-05-26 19:55:30 +0930104/* Support testing of new binding protocols */
105#ifndef ASTLPC_VER_CUR
Andrew Jeffery5303d9c2020-06-09 13:37:46 +0930106#define ASTLPC_VER_CUR 2
Andrew Jeffery3a540662020-05-26 19:55:30 +0930107#endif
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930108/* clang-format on */
Jeremy Kerr672c8852019-03-01 12:18:07 +0800109
Andrew Jeffery88412be2021-03-09 22:05:22 +1030110#ifndef ARRAY_SIZE
111#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
112#endif
113
114static uint32_t astlpc_packet_size_v1(uint32_t body)
115{
116 assert((body + 4) > body);
117
118 return body + 4;
119}
120
121static uint32_t astlpc_body_size_v1(uint32_t packet)
122{
123 assert((packet - 4) < packet);
124
125 return packet - 4;
126}
127
128static const struct mctp_astlpc_protocol astlpc_protocol_version[] = {
129 [0] = {
130 .version = 0,
131 .packet_size = NULL,
132 .body_size = NULL,
133 },
134 [1] = {
135 .version = 1,
136 .packet_size = astlpc_packet_size_v1,
137 .body_size = astlpc_body_size_v1,
138 },
139 [2] = {
140 .version = 2,
141 .packet_size = astlpc_packet_size_v1,
142 .body_size = astlpc_body_size_v1,
143 },
144};
145
Jeremy Kerr672c8852019-03-01 12:18:07 +0800146struct mctp_lpcmap_hdr {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930147 uint32_t magic;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800148
Andrew Jeffery3a540662020-05-26 19:55:30 +0930149 uint16_t bmc_ver_min;
150 uint16_t bmc_ver_cur;
151 uint16_t host_ver_min;
152 uint16_t host_ver_cur;
153 uint16_t negotiated_ver;
154 uint16_t pad0;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800155
Andrew Jeffery3a540662020-05-26 19:55:30 +0930156 struct {
157 uint32_t rx_offset;
158 uint32_t rx_size;
159 uint32_t tx_offset;
160 uint32_t tx_size;
161 } layout;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800162} __attribute__((packed));
163
Andrew Jeffery3a540662020-05-26 19:55:30 +0930164static const uint32_t control_size = 0x100;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800165
Jeremy Kerr672c8852019-03-01 12:18:07 +0800166#define LPC_WIN_SIZE (1 * 1024 * 1024)
167
Jeremy Kerr672c8852019-03-01 12:18:07 +0800168#define KCS_STATUS_BMC_READY 0x80
169#define KCS_STATUS_CHANNEL_ACTIVE 0x40
170#define KCS_STATUS_IBF 0x02
171#define KCS_STATUS_OBF 0x01
172
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930173static inline int mctp_astlpc_kcs_write(struct mctp_binding_astlpc *astlpc,
174 enum mctp_binding_astlpc_kcs_reg reg,
175 uint8_t val)
176{
177 return astlpc->ops.kcs_write(astlpc->ops_data, reg, val);
178}
179
180static inline int mctp_astlpc_kcs_read(struct mctp_binding_astlpc *astlpc,
181 enum mctp_binding_astlpc_kcs_reg reg,
182 uint8_t *val)
183{
184 return astlpc->ops.kcs_read(astlpc->ops_data, reg, val);
185}
186
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930187static inline int mctp_astlpc_lpc_write(struct mctp_binding_astlpc *astlpc,
188 const void *buf, long offset,
189 size_t len)
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530190{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930191 astlpc_prdebug(astlpc, "%s: %zu bytes to 0x%lx", __func__, len, offset);
192
193 assert(offset >= 0);
194
195 /* Indirect access */
196 if (astlpc->ops.lpc_write) {
197 void *data = astlpc->ops_data;
198
199 return astlpc->ops.lpc_write(data, buf, offset, len);
200 }
201
202 /* Direct mapping */
203 assert(astlpc->lpc_map);
204 memcpy(&((char *)astlpc->lpc_map)[offset], buf, len);
205
206 return 0;
207}
208
209static inline int mctp_astlpc_lpc_read(struct mctp_binding_astlpc *astlpc,
210 void *buf, long offset, size_t len)
211{
212 astlpc_prdebug(astlpc, "%s: %zu bytes from 0x%lx", __func__, len,
213 offset);
214
215 assert(offset >= 0);
216
217 /* Indirect access */
218 if (astlpc->ops.lpc_read) {
219 void *data = astlpc->ops_data;
220
221 return astlpc->ops.lpc_read(data, buf, offset, len);
222 }
223
224 /* Direct mapping */
225 assert(astlpc->lpc_map);
226 memcpy(buf, &((char *)astlpc->lpc_map)[offset], len);
227
228 return 0;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530229}
230
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930231static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc,
232 uint8_t status)
233{
234 uint8_t data;
235 int rc;
236
237 /* Since we're setting the status register, we want the other endpoint
238 * to be interrupted. However, some hardware may only raise a host-side
239 * interrupt on an ODR event.
240 * So, write a dummy value of 0xff to ODR, which will ensure that an
241 * interrupt is triggered, and can be ignored by the host.
242 */
243 data = 0xff;
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930244
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930245 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, status);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930246 if (rc) {
247 astlpc_prwarn(astlpc, "KCS status write failed");
248 return -1;
249 }
250
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930251 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930252 if (rc) {
253 astlpc_prwarn(astlpc, "KCS dummy data write failed");
254 return -1;
255 }
256
257 return 0;
258}
259
Andrew Jeffery3a540662020-05-26 19:55:30 +0930260static int mctp_astlpc_layout_read(struct mctp_binding_astlpc *astlpc,
261 struct mctp_astlpc_layout *layout)
262{
263 struct mctp_lpcmap_hdr hdr;
264 int rc;
265
266 rc = mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
267 if (rc < 0)
268 return rc;
269
270 /* Flip the buffers as the names are defined in terms of the host */
271 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) {
272 layout->rx.offset = be32toh(hdr.layout.tx_offset);
273 layout->rx.size = be32toh(hdr.layout.tx_size);
274 layout->tx.offset = be32toh(hdr.layout.rx_offset);
275 layout->tx.size = be32toh(hdr.layout.rx_size);
276 } else {
277 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
278
279 layout->rx.offset = be32toh(hdr.layout.rx_offset);
280 layout->rx.size = be32toh(hdr.layout.rx_size);
281 layout->tx.offset = be32toh(hdr.layout.tx_offset);
282 layout->tx.size = be32toh(hdr.layout.tx_size);
283 }
284
285 return 0;
286}
287
288static int mctp_astlpc_layout_write(struct mctp_binding_astlpc *astlpc,
289 struct mctp_astlpc_layout *layout)
290{
291 uint32_t rx_size_be;
292
293 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) {
294 struct mctp_lpcmap_hdr hdr;
295
296 /*
297 * Flip the buffers as the names are defined in terms of the
298 * host
299 */
300 hdr.layout.rx_offset = htobe32(layout->tx.offset);
301 hdr.layout.rx_size = htobe32(layout->tx.size);
302 hdr.layout.tx_offset = htobe32(layout->rx.offset);
303 hdr.layout.tx_size = htobe32(layout->rx.size);
304
305 return mctp_astlpc_lpc_write(astlpc, &hdr.layout,
306 offsetof(struct mctp_lpcmap_hdr, layout),
307 sizeof(hdr.layout));
308 }
309
310 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
311
312 /*
313 * As of v2 we only need to write rx_size - the offsets are controlled
314 * by the BMC, as is the BMC's rx_size (host tx_size).
315 */
316 rx_size_be = htobe32(layout->rx.size);
317 return mctp_astlpc_lpc_write(astlpc, &rx_size_be,
318 offsetof(struct mctp_lpcmap_hdr, layout.rx_size),
319 sizeof(rx_size_be));
320}
321
Andrew Jeffery88412be2021-03-09 22:05:22 +1030322static bool
323mctp_astlpc_buffer_validate(const struct mctp_binding_astlpc *astlpc,
324 const struct mctp_astlpc_buffer *buf,
325 const char *name)
Andrew Jeffery3a540662020-05-26 19:55:30 +0930326{
327 /* Check for overflow */
328 if (buf->offset + buf->size < buf->offset) {
329 mctp_prerr(
330 "%s packet buffer parameters overflow: offset: 0x%" PRIx32
331 ", size: %" PRIu32,
332 name, buf->offset, buf->size);
333 return false;
334 }
335
336 /* Check that the buffers are contained within the allocated space */
337 if (buf->offset + buf->size > LPC_WIN_SIZE) {
338 mctp_prerr(
339 "%s packet buffer parameters exceed %uM window size: offset: 0x%" PRIx32
340 ", size: %" PRIu32,
341 name, (LPC_WIN_SIZE / (1024 * 1024)), buf->offset,
342 buf->size);
343 return false;
344 }
345
346 /* Check that the baseline transmission unit is supported */
Andrew Jeffery88412be2021-03-09 22:05:22 +1030347 if (buf->size < astlpc->proto->packet_size(MCTP_PACKET_SIZE(MCTP_BTU))) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930348 mctp_prerr(
Andrew Jeffery88412be2021-03-09 22:05:22 +1030349 "%s packet buffer too small: Require %" PRIu32 " bytes to support the %u byte baseline transmission unit, found %" PRIu32,
350 name,
351 astlpc->proto->packet_size(MCTP_PACKET_SIZE(MCTP_BTU)),
Andrew Jeffery3a540662020-05-26 19:55:30 +0930352 MCTP_BTU, buf->size);
353 return false;
354 }
355
356 /* Check for overlap with the control space */
357 if (buf->offset < control_size) {
358 mctp_prerr(
359 "%s packet buffer overlaps control region {0x%" PRIx32
360 ", %" PRIu32 "}: Rx {0x%" PRIx32 ", %" PRIu32 "}",
361 name, 0U, control_size, buf->offset, buf->size);
362 return false;
363 }
364
365 return true;
366}
367
Andrew Jeffery88412be2021-03-09 22:05:22 +1030368static bool
369mctp_astlpc_layout_validate(const struct mctp_binding_astlpc *astlpc,
370 const struct mctp_astlpc_layout *layout)
Andrew Jeffery3a540662020-05-26 19:55:30 +0930371{
Andrew Jeffery88412be2021-03-09 22:05:22 +1030372 const struct mctp_astlpc_buffer *rx = &layout->rx;
373 const struct mctp_astlpc_buffer *tx = &layout->tx;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930374 bool rx_valid, tx_valid;
375
Andrew Jeffery88412be2021-03-09 22:05:22 +1030376 rx_valid = mctp_astlpc_buffer_validate(astlpc, rx, "Rx");
377 tx_valid = mctp_astlpc_buffer_validate(astlpc, tx, "Tx");
Andrew Jeffery3a540662020-05-26 19:55:30 +0930378
379 if (!(rx_valid && tx_valid))
380 return false;
381
382 /* Check that the buffers are disjoint */
383 if ((rx->offset <= tx->offset && rx->offset + rx->size > tx->offset) ||
384 (tx->offset <= rx->offset && tx->offset + tx->size > rx->offset)) {
385 mctp_prerr("Rx and Tx packet buffers overlap: Rx {0x%" PRIx32
386 ", %" PRIu32 "}, Tx {0x%" PRIx32 ", %" PRIu32 "}",
387 rx->offset, rx->size, tx->offset, tx->size);
388 return false;
389 }
390
391 return true;
392}
393
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930394static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc)
395{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930396 struct mctp_lpcmap_hdr hdr = { 0 };
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930397 uint8_t status;
Andrew Jeffery88412be2021-03-09 22:05:22 +1030398 uint32_t sz;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930399
400 /*
401 * The largest buffer size is half of the allocated MCTP space
402 * excluding the control space.
403 */
404 sz = ((LPC_WIN_SIZE - control_size) / 2);
405
406 /*
407 * Trim the MTU to a multiple of 16 to meet the requirements of 12.17
408 * Query Hop in DSP0236 v1.3.0.
409 */
Andrew Jeffery88412be2021-03-09 22:05:22 +1030410 sz = MCTP_BODY_SIZE(astlpc->proto->body_size(sz));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930411 sz &= ~0xfUL;
Andrew Jeffery88412be2021-03-09 22:05:22 +1030412 sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(sz));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930413
Andrew Jefferya9368982020-06-09 13:07:39 +0930414 if (astlpc->requested_mtu) {
Andrew Jeffery88412be2021-03-09 22:05:22 +1030415 uint32_t rpkt, rmtu;
Andrew Jefferya9368982020-06-09 13:07:39 +0930416
Andrew Jeffery88412be2021-03-09 22:05:22 +1030417 rmtu = astlpc->requested_mtu;
418 rpkt = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu));
419 sz = MIN(sz, rpkt);
Andrew Jefferya9368982020-06-09 13:07:39 +0930420 }
421
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930422 /* Flip the buffers as the names are defined in terms of the host */
Andrew Jeffery3a540662020-05-26 19:55:30 +0930423 astlpc->layout.tx.offset = control_size;
424 astlpc->layout.tx.size = sz;
425 astlpc->layout.rx.offset =
426 astlpc->layout.tx.offset + astlpc->layout.tx.size;
427 astlpc->layout.rx.size = sz;
428
Andrew Jeffery88412be2021-03-09 22:05:22 +1030429 if (!mctp_astlpc_layout_validate(astlpc, &astlpc->layout)) {
430 astlpc_prerr(astlpc, "Cannot support an MTU of %" PRIu32, sz);
Andrew Jefferya9368982020-06-09 13:07:39 +0930431 return -EINVAL;
432 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930433
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930434 hdr = (struct mctp_lpcmap_hdr){
435 .magic = htobe32(ASTLPC_MCTP_MAGIC),
436 .bmc_ver_min = htobe16(ASTLPC_VER_MIN),
437 .bmc_ver_cur = htobe16(ASTLPC_VER_CUR),
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930438
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930439 /* Flip the buffers back as we're now describing the host's
440 * configuration to the host */
Andrew Jeffery3a540662020-05-26 19:55:30 +0930441 .layout.rx_offset = htobe32(astlpc->layout.tx.offset),
442 .layout.rx_size = htobe32(astlpc->layout.tx.size),
443 .layout.tx_offset = htobe32(astlpc->layout.rx.offset),
444 .layout.tx_size = htobe32(astlpc->layout.rx.size),
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930445 };
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930446
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930447 mctp_astlpc_lpc_write(astlpc, &hdr, 0, sizeof(hdr));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930448
Andrew Jefferyb3b55a62020-07-06 13:34:18 +0930449 /*
450 * Set status indicating that the BMC is now active. Be explicit about
451 * clearing OBF; we're reinitialising the binding and so any previous
452 * buffer state is irrelevant.
453 */
454 status = KCS_STATUS_BMC_READY & ~KCS_STATUS_OBF;
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930455 return mctp_astlpc_kcs_set_status(astlpc, status);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930456}
457
458static int mctp_binding_astlpc_start_bmc(struct mctp_binding *b)
459{
460 struct mctp_binding_astlpc *astlpc =
461 container_of(b, struct mctp_binding_astlpc, binding);
462
Andrew Jeffery88412be2021-03-09 22:05:22 +1030463 astlpc->proto = &astlpc_protocol_version[ASTLPC_VER_CUR];
464
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930465 return mctp_astlpc_init_bmc(astlpc);
466}
467
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930468static bool mctp_astlpc_validate_version(uint16_t bmc_ver_min,
469 uint16_t bmc_ver_cur,
470 uint16_t host_ver_min,
471 uint16_t host_ver_cur)
472{
473 if (!(bmc_ver_min && bmc_ver_cur && host_ver_min && host_ver_cur)) {
474 mctp_prerr("Invalid version present in [%" PRIu16 ", %" PRIu16
475 "], [%" PRIu16 ", %" PRIu16 "]",
476 bmc_ver_min, bmc_ver_cur, host_ver_min,
477 host_ver_cur);
478 return false;
479 } else if (bmc_ver_min > bmc_ver_cur) {
480 mctp_prerr("Invalid bmc version range [%" PRIu16 ", %" PRIu16
481 "]",
482 bmc_ver_min, bmc_ver_cur);
483 return false;
484 } else if (host_ver_min > host_ver_cur) {
485 mctp_prerr("Invalid host version range [%" PRIu16 ", %" PRIu16
486 "]",
487 host_ver_min, host_ver_cur);
488 return false;
489 } else if ((host_ver_cur < bmc_ver_min) ||
490 (host_ver_min > bmc_ver_cur)) {
491 mctp_prerr(
492 "Unable to satisfy version negotiation with ranges [%" PRIu16
493 ", %" PRIu16 "] and [%" PRIu16 ", %" PRIu16 "]",
494 bmc_ver_min, bmc_ver_cur, host_ver_min, host_ver_cur);
495 return false;
496 }
497
498 return true;
499}
500
Andrew Jeffery3a540662020-05-26 19:55:30 +0930501static int mctp_astlpc_negotiate_layout_host(struct mctp_binding_astlpc *astlpc)
502{
503 struct mctp_astlpc_layout layout;
Andrew Jeffery88412be2021-03-09 22:05:22 +1030504 uint32_t rmtu;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930505 uint32_t sz;
506 int rc;
507
508 rc = mctp_astlpc_layout_read(astlpc, &layout);
509 if (rc < 0)
510 return rc;
511
Andrew Jeffery88412be2021-03-09 22:05:22 +1030512 if (!mctp_astlpc_layout_validate(astlpc, &layout)) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930513 astlpc_prerr(
514 astlpc,
515 "BMC provided invalid buffer layout: Rx {0x%" PRIx32
516 ", %" PRIu32 "}, Tx {0x%" PRIx32 ", %" PRIu32 "}",
517 layout.rx.offset, layout.rx.size, layout.tx.offset,
518 layout.tx.size);
519 return -EINVAL;
520 }
521
Andrew Jefferya9368982020-06-09 13:07:39 +0930522 astlpc_prinfo(astlpc, "Desire an MTU of %" PRIu32 " bytes",
523 astlpc->requested_mtu);
524
Andrew Jeffery88412be2021-03-09 22:05:22 +1030525 rmtu = astlpc->requested_mtu;
526 sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930527 layout.rx.size = sz;
528
Andrew Jeffery88412be2021-03-09 22:05:22 +1030529 if (!mctp_astlpc_layout_validate(astlpc, &layout)) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930530 astlpc_prerr(
531 astlpc,
532 "Generated invalid buffer layout with size %" PRIu32
533 ": Rx {0x%" PRIx32 ", %" PRIu32 "}, Tx {0x%" PRIx32
534 ", %" PRIu32 "}",
535 sz, layout.rx.offset, layout.rx.size, layout.tx.offset,
536 layout.tx.size);
537 return -EINVAL;
538 }
539
Andrew Jefferya9368982020-06-09 13:07:39 +0930540 astlpc_prinfo(astlpc, "Requesting MTU of %" PRIu32 " bytes",
541 astlpc->requested_mtu);
Andrew Jeffery3a540662020-05-26 19:55:30 +0930542
543 return mctp_astlpc_layout_write(astlpc, &layout);
544}
545
Andrew Jeffery88412be2021-03-09 22:05:22 +1030546static uint16_t mctp_astlpc_negotiate_version(uint16_t bmc_ver_min,
547 uint16_t bmc_ver_cur,
548 uint16_t host_ver_min,
549 uint16_t host_ver_cur)
550{
551 if (!mctp_astlpc_validate_version(bmc_ver_min, bmc_ver_cur,
552 host_ver_min, host_ver_cur))
553 return ASTLPC_VER_BAD;
554
555 if (bmc_ver_cur < host_ver_cur)
556 return bmc_ver_cur;
557
558 return host_ver_cur;
559}
560
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930561static int mctp_astlpc_init_host(struct mctp_binding_astlpc *astlpc)
562{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930563 const uint16_t ver_min_be = htobe16(ASTLPC_VER_MIN);
564 const uint16_t ver_cur_be = htobe16(ASTLPC_VER_CUR);
Andrew Jeffery88412be2021-03-09 22:05:22 +1030565 uint16_t bmc_ver_min, bmc_ver_cur, negotiated;
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930566 struct mctp_lpcmap_hdr hdr;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930567 uint8_t status;
568 int rc;
569
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930570 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930571 if (rc) {
572 mctp_prwarn("KCS status read failed");
573 return rc;
574 }
575
576 astlpc->kcs_status = status;
577
578 if (!(status & KCS_STATUS_BMC_READY))
579 return -EHOSTDOWN;
580
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930581 mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930582
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930583 bmc_ver_min = be16toh(hdr.bmc_ver_min);
584 bmc_ver_cur = be16toh(hdr.bmc_ver_cur);
585
Andrew Jeffery88412be2021-03-09 22:05:22 +1030586 /* Calculate the expected value of negotiated_ver */
587 negotiated = mctp_astlpc_negotiate_version(bmc_ver_min, bmc_ver_cur,
588 ASTLPC_VER_MIN,
589 ASTLPC_VER_CUR);
590 if (!negotiated) {
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930591 astlpc_prerr(astlpc, "Cannot negotiate with invalid versions");
592 return -EINVAL;
593 }
594
Andrew Jeffery88412be2021-03-09 22:05:22 +1030595 /* Assign protocol ops so we can calculate the packet buffer sizes */
596 assert(negotiated < ARRAY_SIZE(astlpc_protocol_version));
597 astlpc->proto = &astlpc_protocol_version[negotiated];
598
599 /* Negotiate packet buffers in v2 style if the BMC supports it */
600 if (negotiated >= 2) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930601 rc = mctp_astlpc_negotiate_layout_host(astlpc);
602 if (rc < 0)
603 return rc;
604 }
605
Andrew Jeffery88412be2021-03-09 22:05:22 +1030606 /* Advertise the host's supported protocol versions */
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930607 mctp_astlpc_lpc_write(astlpc, &ver_min_be,
608 offsetof(struct mctp_lpcmap_hdr, host_ver_min),
609 sizeof(ver_min_be));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930610
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930611 mctp_astlpc_lpc_write(astlpc, &ver_cur_be,
612 offsetof(struct mctp_lpcmap_hdr, host_ver_cur),
613 sizeof(ver_cur_be));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930614
615 /* Send channel init command */
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930616 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, 0x0);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930617 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930618 astlpc_prwarn(astlpc, "KCS write failed");
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930619 }
620
Andrew Jeffery88412be2021-03-09 22:05:22 +1030621 /*
622 * Configure the host so `astlpc->proto->version == 0` holds until we
623 * receive a subsequent status update from the BMC. Until then,
624 * `astlpc->proto->version == 0` indicates that we're yet to complete
625 * the channel initialisation handshake.
626 *
627 * When the BMC provides a status update with KCS_STATUS_CHANNEL_ACTIVE
628 * set we will assign the appropriate protocol ops struct in accordance
629 * with `negotiated_ver`.
630 */
631 astlpc->proto = &astlpc_protocol_version[ASTLPC_VER_BAD];
632
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930633 return rc;
634}
635
636static int mctp_binding_astlpc_start_host(struct mctp_binding *b)
637{
638 struct mctp_binding_astlpc *astlpc =
639 container_of(b, struct mctp_binding_astlpc, binding);
640
641 return mctp_astlpc_init_host(astlpc);
642}
643
644static bool __mctp_astlpc_kcs_ready(struct mctp_binding_astlpc *astlpc,
645 uint8_t status, bool is_write)
646{
647 bool is_bmc;
648 bool ready_state;
649 uint8_t flag;
650
651 is_bmc = (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC);
652 flag = (is_bmc ^ is_write) ? KCS_STATUS_IBF : KCS_STATUS_OBF;
653 ready_state = is_write ? 0 : 1;
654
655 return !!(status & flag) == ready_state;
656}
657
658static inline bool
659mctp_astlpc_kcs_read_ready(struct mctp_binding_astlpc *astlpc, uint8_t status)
660{
661 return __mctp_astlpc_kcs_ready(astlpc, status, false);
662}
663
664static inline bool
665mctp_astlpc_kcs_write_ready(struct mctp_binding_astlpc *astlpc, uint8_t status)
666{
667 return __mctp_astlpc_kcs_ready(astlpc, status, true);
668}
669
Jeremy Kerr672c8852019-03-01 12:18:07 +0800670static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc,
671 uint8_t data)
672{
673 uint8_t status;
674 int rc;
675
676 for (;;) {
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930677 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS,
678 &status);
Andrew Jeffery1b27fe82020-01-24 16:05:11 +1030679 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930680 astlpc_prwarn(astlpc, "KCS status read failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +0800681 return -1;
682 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930683 if (mctp_astlpc_kcs_write_ready(astlpc, status))
Jeremy Kerr672c8852019-03-01 12:18:07 +0800684 break;
685 /* todo: timeout */
686 }
687
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930688 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530689 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930690 astlpc_prwarn(astlpc, "KCS data write failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +0800691 return -1;
692 }
693
694 return 0;
695}
696
697static int mctp_binding_astlpc_tx(struct mctp_binding *b,
698 struct mctp_pktbuf *pkt)
699{
700 struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b);
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930701 uint32_t len, len_be;
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030702 struct mctp_hdr *hdr;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800703
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030704 hdr = mctp_pktbuf_hdr(pkt);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800705 len = mctp_pktbuf_size(pkt);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030706
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930707 astlpc_prdebug(astlpc,
708 "%s: Transmitting %" PRIu32
709 "-byte packet (%hhu, %hhu, 0x%hhx)",
710 __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030711
Andrew Jeffery88412be2021-03-09 22:05:22 +1030712 if (len > astlpc->proto->body_size(astlpc->layout.tx.size)) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930713 astlpc_prwarn(astlpc, "invalid TX len 0x%x", len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800714 return -1;
715 }
716
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930717 len_be = htobe32(len);
718 mctp_astlpc_lpc_write(astlpc, &len_be, astlpc->layout.tx.offset,
719 sizeof(len_be));
720 mctp_astlpc_lpc_write(astlpc, hdr, astlpc->layout.tx.offset + 4, len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800721
722 mctp_binding_set_tx_enabled(b, false);
723
724 mctp_astlpc_kcs_send(astlpc, 0x1);
725 return 0;
726}
727
Andrew Jeffery3a540662020-05-26 19:55:30 +0930728static uint32_t mctp_astlpc_calculate_mtu(struct mctp_binding_astlpc *astlpc,
729 struct mctp_astlpc_layout *layout)
730{
Andrew Jeffery88412be2021-03-09 22:05:22 +1030731 uint32_t low, high, limit, rpkt;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930732
733 /* Derive the largest MTU the BMC _can_ support */
734 low = MIN(astlpc->layout.rx.offset, astlpc->layout.tx.offset);
735 high = MAX(astlpc->layout.rx.offset, astlpc->layout.tx.offset);
736 limit = high - low;
737
Andrew Jefferya9368982020-06-09 13:07:39 +0930738 /* Determine the largest MTU the BMC _wants_ to support */
739 if (astlpc->requested_mtu) {
Andrew Jeffery88412be2021-03-09 22:05:22 +1030740 uint32_t rmtu = astlpc->requested_mtu;
Andrew Jefferya9368982020-06-09 13:07:39 +0930741
Andrew Jeffery88412be2021-03-09 22:05:22 +1030742 rpkt = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu));
743 limit = MIN(limit, rpkt);
Andrew Jefferya9368982020-06-09 13:07:39 +0930744 }
Andrew Jeffery3a540662020-05-26 19:55:30 +0930745
746 /* Determine the accepted MTU, applied both directions by convention */
Andrew Jeffery88412be2021-03-09 22:05:22 +1030747 rpkt = MIN(limit, layout->tx.size);
748 return MCTP_BODY_SIZE(astlpc->proto->body_size(rpkt));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930749}
750
Andrew Jeffery88412be2021-03-09 22:05:22 +1030751static int mctp_astlpc_negotiate_layout_bmc(struct mctp_binding_astlpc *astlpc)
Andrew Jeffery3a540662020-05-26 19:55:30 +0930752{
753 struct mctp_astlpc_layout proposed, pending;
754 uint32_t sz, mtu;
755 int rc;
756
Andrew Jeffery88412be2021-03-09 22:05:22 +1030757 /* Do we have a valid protocol version? */
758 if (!astlpc->proto->version)
759 return -EINVAL;
760
Andrew Jeffery3a540662020-05-26 19:55:30 +0930761 /* Extract the host's proposed layout */
762 rc = mctp_astlpc_layout_read(astlpc, &proposed);
763 if (rc < 0)
764 return rc;
765
Andrew Jeffery88412be2021-03-09 22:05:22 +1030766 /* Do we have a reasonable layout? */
767 if (!mctp_astlpc_layout_validate(astlpc, &proposed))
Andrew Jeffery3a540662020-05-26 19:55:30 +0930768 return -EINVAL;
769
770 /* Negotiate the MTU */
771 mtu = mctp_astlpc_calculate_mtu(astlpc, &proposed);
Andrew Jeffery88412be2021-03-09 22:05:22 +1030772 sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(mtu));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930773
774 /*
775 * Use symmetric MTUs by convention and to pass constraints in rx/tx
776 * functions
777 */
778 pending = astlpc->layout;
779 pending.tx.size = sz;
780 pending.rx.size = sz;
781
Andrew Jeffery88412be2021-03-09 22:05:22 +1030782 if (mctp_astlpc_layout_validate(astlpc, &pending)) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930783 /* We found a sensible Rx MTU, so honour it */
784 astlpc->layout = pending;
785
786 /* Enforce the negotiated MTU */
787 rc = mctp_astlpc_layout_write(astlpc, &astlpc->layout);
788 if (rc < 0)
789 return rc;
790
791 astlpc_prinfo(astlpc, "Negotiated an MTU of %" PRIu32 " bytes",
792 mtu);
793 } else {
794 astlpc_prwarn(astlpc, "MTU negotiation failed");
795 return -EINVAL;
796 }
797
Andrew Jeffery88412be2021-03-09 22:05:22 +1030798 if (astlpc->proto->version >= 2)
Andrew Jeffery3a540662020-05-26 19:55:30 +0930799 astlpc->binding.pkt_size = MCTP_PACKET_SIZE(mtu);
800
801 return 0;
802}
803
Jeremy Kerr672c8852019-03-01 12:18:07 +0800804static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc)
805{
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930806 uint16_t negotiated, negotiated_be;
807 struct mctp_lpcmap_hdr hdr;
808 uint8_t status;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930809 int rc;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930810
811 mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
812
813 /* Version negotiation */
814 negotiated =
815 mctp_astlpc_negotiate_version(ASTLPC_VER_MIN, ASTLPC_VER_CUR,
816 be16toh(hdr.host_ver_min),
817 be16toh(hdr.host_ver_cur));
818
Andrew Jeffery88412be2021-03-09 22:05:22 +1030819 /* MTU negotiation requires knowing which protocol we'll use */
820 assert(negotiated < ARRAY_SIZE(astlpc_protocol_version));
821 astlpc->proto = &astlpc_protocol_version[negotiated];
822
Andrew Jeffery3a540662020-05-26 19:55:30 +0930823 /* Host Rx MTU negotiation: Failure terminates channel init */
Andrew Jeffery88412be2021-03-09 22:05:22 +1030824 rc = mctp_astlpc_negotiate_layout_bmc(astlpc);
Andrew Jeffery3a540662020-05-26 19:55:30 +0930825 if (rc < 0)
826 negotiated = ASTLPC_VER_BAD;
827
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930828 /* Populate the negotiated version */
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930829 negotiated_be = htobe16(negotiated);
830 mctp_astlpc_lpc_write(astlpc, &negotiated_be,
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930831 offsetof(struct mctp_lpcmap_hdr, negotiated_ver),
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930832 sizeof(negotiated_be));
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930833
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930834 /* Finalise the configuration */
835 status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
836 if (negotiated > 0) {
837 astlpc_prinfo(astlpc, "Negotiated binding version %" PRIu16,
838 negotiated);
839 status |= KCS_STATUS_CHANNEL_ACTIVE;
840 } else {
Andrew Jeffery88412be2021-03-09 22:05:22 +1030841 astlpc_prerr(astlpc, "Failed to initialise channel");
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930842 }
Jeremy Kerr672c8852019-03-01 12:18:07 +0800843
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930844 mctp_astlpc_kcs_set_status(astlpc, status);
845
846 mctp_binding_set_tx_enabled(&astlpc->binding,
847 status & KCS_STATUS_CHANNEL_ACTIVE);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800848}
849
850static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
851{
852 struct mctp_pktbuf *pkt;
853 uint32_t len;
854
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930855 mctp_astlpc_lpc_read(astlpc, &len, astlpc->layout.rx.offset,
856 sizeof(len));
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530857 len = be32toh(len);
858
Andrew Jeffery88412be2021-03-09 22:05:22 +1030859 if (len > astlpc->proto->body_size(astlpc->layout.rx.size)) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930860 astlpc_prwarn(astlpc, "invalid RX len 0x%x", len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800861 return;
862 }
863
Andrew Jefferyb93b6112020-06-05 14:13:44 +0930864 assert(astlpc->binding.pkt_size >= 0);
865 if (len > (uint32_t)astlpc->binding.pkt_size) {
Jeremy Kerr672c8852019-03-01 12:18:07 +0800866 mctp_prwarn("invalid RX len 0x%x", len);
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930867 astlpc_prwarn(astlpc, "invalid RX len 0x%x", len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800868 return;
869 }
870
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800871 pkt = mctp_pktbuf_alloc(&astlpc->binding, len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800872 if (!pkt)
873 goto out_complete;
874
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930875 mctp_astlpc_lpc_read(astlpc, mctp_pktbuf_hdr(pkt),
876 astlpc->layout.rx.offset + 4, len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800877
878 mctp_bus_rx(&astlpc->binding, pkt);
879
880out_complete:
881 mctp_astlpc_kcs_send(astlpc, 0x2);
882}
883
884static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc)
885{
886 mctp_binding_set_tx_enabled(&astlpc->binding, true);
887}
888
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930889static int mctp_astlpc_finalise_channel(struct mctp_binding_astlpc *astlpc)
890{
Andrew Jeffery3a540662020-05-26 19:55:30 +0930891 struct mctp_astlpc_layout layout;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930892 uint16_t negotiated;
893 int rc;
894
895 rc = mctp_astlpc_lpc_read(astlpc, &negotiated,
896 offsetof(struct mctp_lpcmap_hdr,
897 negotiated_ver),
898 sizeof(negotiated));
899 if (rc < 0)
900 return rc;
901
902 negotiated = be16toh(negotiated);
Andrew Jeffery88412be2021-03-09 22:05:22 +1030903 astlpc_prerr(astlpc, "Version negotiation got: %u", negotiated);
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930904
905 if (negotiated == ASTLPC_VER_BAD || negotiated < ASTLPC_VER_MIN ||
906 negotiated > ASTLPC_VER_CUR) {
907 astlpc_prerr(astlpc, "Failed to negotiate version, got: %u\n",
908 negotiated);
909 return -EINVAL;
910 }
911
Andrew Jeffery88412be2021-03-09 22:05:22 +1030912 assert(negotiated < ARRAY_SIZE(astlpc_protocol_version));
913 astlpc->proto = &astlpc_protocol_version[negotiated];
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930914
Andrew Jeffery3a540662020-05-26 19:55:30 +0930915 rc = mctp_astlpc_layout_read(astlpc, &layout);
916 if (rc < 0)
917 return rc;
918
Andrew Jeffery88412be2021-03-09 22:05:22 +1030919 if (!mctp_astlpc_layout_validate(astlpc, &layout)) {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930920 mctp_prerr("BMC proposed invalid buffer parameters");
921 return -EINVAL;
922 }
923
924 astlpc->layout = layout;
925
926 if (negotiated >= 2)
927 astlpc->binding.pkt_size =
Andrew Jeffery88412be2021-03-09 22:05:22 +1030928 astlpc->proto->body_size(astlpc->layout.tx.size);
Andrew Jeffery3a540662020-05-26 19:55:30 +0930929
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930930 return 0;
931}
932
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930933static int mctp_astlpc_update_channel(struct mctp_binding_astlpc *astlpc,
934 uint8_t status)
935{
936 uint8_t updated;
937 int rc = 0;
938
939 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
940
941 updated = astlpc->kcs_status ^ status;
942
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930943 astlpc_prdebug(astlpc, "%s: status: 0x%x, update: 0x%x", __func__,
944 status, updated);
945
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930946 if (updated & KCS_STATUS_BMC_READY) {
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930947 if (status & KCS_STATUS_BMC_READY) {
948 astlpc->kcs_status = status;
949 return astlpc->binding.start(&astlpc->binding);
950 } else {
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930951 mctp_binding_set_tx_enabled(&astlpc->binding, false);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930952 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930953 }
954
Andrew Jeffery88412be2021-03-09 22:05:22 +1030955 if (astlpc->proto->version == 0 ||
956 updated & KCS_STATUS_CHANNEL_ACTIVE) {
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930957 bool enable;
958
959 rc = mctp_astlpc_finalise_channel(astlpc);
960 enable = (status & KCS_STATUS_CHANNEL_ACTIVE) && rc == 0;
961
962 mctp_binding_set_tx_enabled(&astlpc->binding, enable);
963 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930964
965 astlpc->kcs_status = status;
966
967 return rc;
968}
969
Jeremy Kerr672c8852019-03-01 12:18:07 +0800970int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
971{
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530972 uint8_t status, data;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800973 int rc;
974
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930975 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530976 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930977 astlpc_prwarn(astlpc, "KCS read error");
Jeremy Kerr672c8852019-03-01 12:18:07 +0800978 return -1;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800979 }
980
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930981 astlpc_prdebug(astlpc, "%s: status: 0x%hhx", __func__, status);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030982
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930983 if (!mctp_astlpc_kcs_read_ready(astlpc, status))
Jeremy Kerr672c8852019-03-01 12:18:07 +0800984 return 0;
985
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930986 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_DATA, &data);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530987 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930988 astlpc_prwarn(astlpc, "KCS data read error");
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530989 return -1;
990 }
991
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930992 astlpc_prdebug(astlpc, "%s: data: 0x%hhx", __func__, data);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030993
Andrew Jeffery88412be2021-03-09 22:05:22 +1030994 if (!astlpc->proto->version && !(data == 0x0 || data == 0xff)) {
Andrew Jefferyafcb7012021-01-28 17:00:36 +1030995 astlpc_prwarn(astlpc, "Invalid message for binding state: 0x%x",
996 data);
997 return 0;
998 }
999
Jeremy Kerr672c8852019-03-01 12:18:07 +08001000 switch (data) {
1001 case 0x0:
1002 mctp_astlpc_init_channel(astlpc);
1003 break;
1004 case 0x1:
1005 mctp_astlpc_rx_start(astlpc);
1006 break;
1007 case 0x2:
1008 mctp_astlpc_tx_complete(astlpc);
1009 break;
Jeremy Kerr1a4b55a2019-06-24 14:22:39 +08001010 case 0xff:
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301011 /* No responsibilities for the BMC on 0xff */
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301012 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) {
1013 rc = mctp_astlpc_update_channel(astlpc, status);
1014 if (rc < 0)
1015 return rc;
1016 }
1017 break;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001018 default:
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301019 astlpc_prwarn(astlpc, "unknown message 0x%x", data);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001020 }
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301021
1022 /* Handle silent loss of bmc-ready */
1023 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) {
1024 if (!(status & KCS_STATUS_BMC_READY && data == 0xff))
1025 return mctp_astlpc_update_channel(astlpc, status);
1026 }
1027
1028 return rc;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001029}
1030
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301031/* allocate and basic initialisation */
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301032static struct mctp_binding_astlpc *__mctp_astlpc_init(uint8_t mode,
1033 uint32_t mtu)
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301034{
1035 struct mctp_binding_astlpc *astlpc;
1036
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301037 assert((mode == MCTP_BINDING_ASTLPC_MODE_BMC) ||
1038 (mode == MCTP_BINDING_ASTLPC_MODE_HOST));
1039
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301040 astlpc = __mctp_alloc(sizeof(*astlpc));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301041 if (!astlpc)
1042 return NULL;
1043
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301044 memset(astlpc, 0, sizeof(*astlpc));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301045 astlpc->mode = mode;
1046 astlpc->lpc_map = NULL;
Andrew Jefferya9368982020-06-09 13:07:39 +09301047 astlpc->requested_mtu = mtu;
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301048 astlpc->binding.name = "astlpc";
1049 astlpc->binding.version = 1;
Andrew Jeffery1a4f4412021-01-28 13:42:02 +10301050 astlpc->binding.pkt_size =
1051 MCTP_PACKET_SIZE(mtu > MCTP_BTU ? mtu : MCTP_BTU);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301052 astlpc->binding.pkt_pad = 0;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301053 astlpc->binding.tx = mctp_binding_astlpc_tx;
1054 if (mode == MCTP_BINDING_ASTLPC_MODE_BMC)
1055 astlpc->binding.start = mctp_binding_astlpc_start_bmc;
1056 else if (mode == MCTP_BINDING_ASTLPC_MODE_HOST)
1057 astlpc->binding.start = mctp_binding_astlpc_start_host;
1058 else {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301059 astlpc_prerr(astlpc, "%s: Invalid mode: %d\n", __func__, mode);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301060 __mctp_free(astlpc);
1061 return NULL;
1062 }
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301063
1064 return astlpc;
1065}
1066
Jeremy Kerr3b36d172019-09-04 11:56:09 +08001067struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b)
1068{
1069 return &b->binding;
1070}
1071
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301072struct mctp_binding_astlpc *
1073mctp_astlpc_init(uint8_t mode, uint32_t mtu, void *lpc_map,
1074 const struct mctp_binding_astlpc_ops *ops, void *ops_data)
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301075{
1076 struct mctp_binding_astlpc *astlpc;
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301077
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301078 if (!(mode == MCTP_BINDING_ASTLPC_MODE_BMC ||
1079 mode == MCTP_BINDING_ASTLPC_MODE_HOST)) {
1080 mctp_prerr("Unknown binding mode: %u", mode);
1081 return NULL;
1082 }
1083
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301084 astlpc = __mctp_astlpc_init(mode, mtu);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301085 if (!astlpc)
1086 return NULL;
1087
1088 memcpy(&astlpc->ops, ops, sizeof(astlpc->ops));
1089 astlpc->ops_data = ops_data;
1090 astlpc->lpc_map = lpc_map;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301091 astlpc->mode = mode;
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301092
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301093 return astlpc;
1094}
1095
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301096struct mctp_binding_astlpc *
1097mctp_astlpc_init_ops(const struct mctp_binding_astlpc_ops *ops, void *ops_data,
1098 void *lpc_map)
1099{
1100 return mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, MCTP_BTU, lpc_map,
1101 ops, ops_data);
1102}
1103
Andrew Jeffery4663f672020-03-10 23:36:24 +10301104void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc)
1105{
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301106 /* Clear channel-active and bmc-ready */
1107 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC)
1108 mctp_astlpc_kcs_set_status(astlpc, KCS_STATUS_OBF);
Andrew Jeffery4663f672020-03-10 23:36:24 +10301109 __mctp_free(astlpc);
1110}
1111
Jeremy Kerrb214c642019-11-27 11:34:00 +08001112#ifdef MCTP_HAVE_FILEIO
Andrew Jeffery55fb90b2020-05-12 13:54:37 +09301113
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301114static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001115{
1116 struct aspeed_lpc_ctrl_mapping map = {
1117 .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
1118 .window_id = 0, /* There's only one */
1119 .flags = 0,
1120 .addr = 0,
1121 .offset = 0,
1122 .size = 0
1123 };
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301124 void *lpc_map_base;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001125 int fd, rc;
1126
1127 fd = open(lpc_path, O_RDWR | O_SYNC);
1128 if (fd < 0) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301129 astlpc_prwarn(astlpc, "LPC open (%s) failed", lpc_path);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001130 return -1;
1131 }
1132
1133 rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map);
1134 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301135 astlpc_prwarn(astlpc, "LPC GET_SIZE failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +08001136 close(fd);
1137 return -1;
1138 }
1139
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301140 lpc_map_base =
1141 mmap(NULL, map.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1142 if (lpc_map_base == MAP_FAILED) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301143 astlpc_prwarn(astlpc, "LPC mmap failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +08001144 rc = -1;
1145 } else {
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301146 astlpc->lpc_map = lpc_map_base + map.size - LPC_WIN_SIZE;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001147 }
1148
1149 close(fd);
1150
1151 return rc;
1152}
1153
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301154static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001155{
1156 astlpc->kcs_fd = open(kcs_path, O_RDWR);
1157 if (astlpc->kcs_fd < 0)
1158 return -1;
1159
1160 return 0;
1161}
1162
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301163static int __mctp_astlpc_fileio_kcs_read(void *arg,
1164 enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val)
1165{
1166 struct mctp_binding_astlpc *astlpc = arg;
1167 off_t offset = reg;
1168 int rc;
1169
1170 rc = pread(astlpc->kcs_fd, val, 1, offset);
1171
1172 return rc == 1 ? 0 : -1;
1173}
1174
1175static int __mctp_astlpc_fileio_kcs_write(void *arg,
1176 enum mctp_binding_astlpc_kcs_reg reg, uint8_t val)
1177{
1178 struct mctp_binding_astlpc *astlpc = arg;
1179 off_t offset = reg;
1180 int rc;
1181
1182 rc = pwrite(astlpc->kcs_fd, &val, 1, offset);
1183
1184 return rc == 1 ? 0 : -1;
1185}
1186
1187int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc)
1188{
1189 return astlpc->kcs_fd;
1190}
1191
1192struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001193{
1194 struct mctp_binding_astlpc *astlpc;
1195 int rc;
1196
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301197 /*
1198 * If we're doing file IO then we're very likely not running
Andrew Jeffery8877c462020-06-15 12:22:53 +09301199 * freestanding, so lets assume that we're on the BMC side.
1200 *
1201 * Requesting an MTU of 0 requests the largest possible MTU, whatever
1202 * value that might take.
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301203 */
Andrew Jeffery8877c462020-06-15 12:22:53 +09301204 astlpc = __mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, 0);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301205 if (!astlpc)
1206 return NULL;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001207
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301208 /* Set internal operations for kcs. We use direct accesses to the lpc
1209 * map area */
1210 astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read;
1211 astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write;
1212 astlpc->ops_data = astlpc;
1213
1214 rc = mctp_astlpc_init_fileio_lpc(astlpc);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001215 if (rc) {
1216 free(astlpc);
1217 return NULL;
1218 }
1219
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301220 rc = mctp_astlpc_init_fileio_kcs(astlpc);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001221 if (rc) {
1222 free(astlpc);
1223 return NULL;
1224 }
1225
Jeremy Kerr672c8852019-03-01 12:18:07 +08001226 return astlpc;
1227}
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301228#else
1229struct mctp_binding_astlpc * __attribute__((const))
1230 mctp_astlpc_init_fileio(void)
1231{
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301232 astlpc_prerr(astlpc, "Missing support for file IO");
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301233 return NULL;
1234}
Jeremy Kerr672c8852019-03-01 12:18:07 +08001235
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301236int __attribute__((const)) mctp_astlpc_get_fd(
1237 struct mctp_binding_astlpc *astlpc __attribute__((unused)))
1238{
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301239 astlpc_prerr(astlpc, "Missing support for file IO");
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301240 return -1;
1241}
1242#endif