Hi Klaus,

> Add an abstract MCTP over I2C endpoint model. This implements MCTP
> control message handling as well as handling the actual I2C transport
> (packetization).
> 
> Devices are intended to derive from this and implement the class
> methods.

Looks good, nice to see how it's used by the nmi device later too.

A couple of issues with the state machine though, comments inline, and
a bit of a patch below.

> +static void i2c_mctp_tx(void *opaque)
> +{
> +    DeviceState *dev = DEVICE(opaque);
> +    I2CBus *i2c = I2C_BUS(qdev_get_parent_bus(dev));
> +    I2CSlave *slave = I2C_SLAVE(dev);
> +    MCTPI2CEndpoint *mctp = MCTP_I2C_ENDPOINT(dev);
> +    MCTPI2CEndpointClass *mc = MCTP_I2C_ENDPOINT_GET_CLASS(mctp);
> +    MCTPI2CPacket *pkt = (MCTPI2CPacket *)mctp->buffer;
> +    uint8_t flags = 0;
> +
> +    switch (mctp->tx.state) {
> +    case I2C_MCTP_STATE_TX_SEND_BYTE:
> +        if (mctp->pos < mctp->len) {
> +            uint8_t byte = mctp->buffer[mctp->pos];
> +
> +            trace_i2c_mctp_tx_send_byte(mctp->pos, byte);
> +
> +            /* send next byte */
> +            i2c_send_async(i2c, byte);
> +
> +            mctp->pos++;
> +
> +            break;
> +        }
> +
> +        /* packet sent */
> +        i2c_end_transfer(i2c);

If we're sending a control message, mctp->len will be set to the control
msg len here, then:

> +
> +        /* fall through */
> +
> +    case I2C_MCTP_STATE_TX_START_SEND:
> +        if (mctp->tx.is_control) {
> +            /* packet payload is already in buffer */
> +            flags |= MCTP_H_FLAGS_SOM | MCTP_H_FLAGS_EOM;
> +        } else {
> +            /* get message bytes from derived device */
> +            mctp->len = mc->get_message_bytes(mctp, pkt->mctp.payload,
> +                                              I2C_MCTP_MAXMTU, &flags);
> +        }

... it doesn't get cleared above, so:

> +
> +        if (!mctp->len) {

... we don't hit this conditional, and hence keep sending unlimited
bytes. This presents as continuous interrupts to the aspeed i2c driver
when replying to any control message.

I think we need a mctp->len = 0 with the i2c_end_transfer(). With that,
I can get control protocol communication working with mctpd.

> +static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event event)
> +{
> +    MCTPI2CEndpoint *mctp = MCTP_I2C_ENDPOINT(i2c);
> +    MCTPI2CEndpointClass *mc = MCTP_I2C_ENDPOINT_GET_CLASS(mctp);
> +    MCTPI2CPacket *pkt = (MCTPI2CPacket *)mctp->buffer;
> +    size_t payload_len;
> +    uint8_t pec;
> +
> +    switch (event) {
> +    case I2C_START_SEND:
> +        if (mctp->state != I2C_MCTP_STATE_IDLE) {
> +            return -1;

mctp->state may (validly) be I2C_MCTP_STATE_RX here, if we're receiving
the start event for the second packet of a multi-packet message.

> +        }
> +
> +        /* the i2c core eats the slave address, so put it back in */
> +        pkt->i2c.dest = i2c->address << 1;
> +        mctp->len = 1;
> +
> +        mctp->state = I2C_MCTP_STATE_RX_STARTED;
> +
> +        return 0;
> +
> +    case I2C_FINISH:
> +        payload_len = mctp->len - (1 + offsetof(MCTPI2CPacket, 
> mctp.payload));
> +
> +        if (pkt->i2c.byte_count + 3 != mctp->len - 1) {
> +            trace_i2c_mctp_drop_invalid_length(pkt->i2c.byte_count +
> 3,
> +                                               mctp->len - 1);
> +            goto drop;
> +        }
> +
> +        pec = i2c_smbus_pec(0, mctp->buffer, mctp->len - 1);
> +        if (mctp->buffer[mctp->len - 1] != pec) {
> +            trace_i2c_mctp_drop_invalid_pec(mctp->buffer[mctp->len - 1], 
> pec);
> +            goto drop;
> +        }
> +
> +        if (pkt->mctp.hdr.eid.dest != mctp->my_eid) {
> +            trace_i2c_mctp_drop_invalid_eid(pkt->mctp.hdr.eid.dest,
> +                                            mctp->my_eid);
> +            goto drop;
> +        }
> +
> +        if (pkt->mctp.hdr.flags & MCTP_H_FLAGS_SOM) {
> +            mctp->tx.is_control = false;
> +
> +            if (mctp->state == I2C_MCTP_STATE_RX) {
> +                mc->reset_message(mctp);
> +            }
> +
> +            mctp->state = I2C_MCTP_STATE_RX;
> +
> +            mctp->tx.addr = pkt->i2c.source;
> +            mctp->tx.eid = pkt->mctp.hdr.eid.source;
> +            mctp->tx.flags = pkt->mctp.hdr.flags & 0x7;
> +            mctp->tx.pktseq = (pkt->mctp.hdr.flags >> 4) & 0x3;
> +
> +            if ((pkt->mctp.payload[0] & 0x7f) == MCTP_MESSAGE_TYPE_CONTROL) {
> +                mctp->tx.is_control = true;
> +
> +                i2c_mctp_handle_control(mctp);
> +
> +                return 0;
> +            }
> +        } else if (mctp->state == I2C_MCTP_STATE_RX_STARTED) {
> +            trace_i2c_mctp_drop("expected SOM");
> +            goto drop;
> +        } else if (((pkt->mctp.hdr.flags >> 4) & 0x3) != (mctp->tx.pktseq++ 
> & 0x3)) {

The pktseq is the sequence number of the last packet seen, so you want a
pre-increment there: ++mctp->tx.pktseq & 0x3

        } else if (((pkt->mctp.hdr.flags >> 4) & 0x3) != (++mctp->tx.pktseq & 
0x3)) {

With those changes, I can get control protocol going, and multi-packet
messages work. There's a couple of failures from unsupported commands,
but otherwise looks good:

  # mctp addr add 8 dev mctpi2c6
  # mctp link set mctpi2c6 up
  # mctp link set mctpi2c6 mtu 254
  # systemctl restart mctpd
  # busctl --no-pager call xyz.openbmc_project.MCTP \
    /xyz/openbmc_project/mctp au.com.CodeConstruct.MCTP \
    SetupEndpoint say mctpi2c6 1 0x1d
  yisb 9 1 "/xyz/openbmc_project/mctp/1/9" true
  # mctp route del 9 via mctpi2c6
  # mctp route add 9 via mctpi2c6 mtu 68
  # mi-mctp 1 9 info
  nmi message type 0x2 not handled
  Identify Controller failed, no quirks applied
  NVMe MI subsys info:
   num ports: 1
   major ver: 1
   minor ver: 1
  NVMe MI port info:
    port 0
      type SMBus[2]
      MCTP MTU: 64
      MEB size: 0
      SMBus address: 0x00
      VPD access freq: 0x00
      MCTP address: 0x1d
      MCTP access freq: 0x01
      NVMe basic management: disabled
  nmi command 0x1 not handled
  mi-mctp: can't perform Health Status Poll operation: Success
  # mi-mctp 1 9 get-config 0
  nmi message type 0x2 not handled
  Identify Controller failed, no quirks applied
  SMBus access frequency (port 0): 100k [0x1]
  MCTP MTU (port 0): 64

I've included a patch below, with some fixes for the above, in case that
helps.

Cheers,


Jeremy


diff --git a/hw/i2c/mctp.c b/hw/i2c/mctp.c
index 46376de95a..1775deb46f 100644
--- a/hw/i2c/mctp.c
+++ b/hw/i2c/mctp.c
@@ -78,6 +78,9 @@ static void i2c_mctp_tx(void *opaque)
         /* packet sent */
         i2c_end_transfer(i2c);
 
+        /* end of any control data */
+        mctp->len = 0;
+
         /* fall through */
 
     case I2C_MCTP_STATE_TX_START_SEND:
@@ -228,7 +231,9 @@ static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event 
event)
 
     switch (event) {
     case I2C_START_SEND:
-        if (mctp->state != I2C_MCTP_STATE_IDLE) {
+        if (mctp->state == I2C_MCTP_STATE_IDLE) {
+            mctp->state = I2C_MCTP_STATE_RX_STARTED;
+        } else if (mctp->state != I2C_MCTP_STATE_RX) {
             return -1;
         }
 
@@ -236,8 +241,6 @@ static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event 
event)
         pkt->i2c.dest = i2c->address << 1;
         mctp->len = 1;
 
-        mctp->state = I2C_MCTP_STATE_RX_STARTED;
-
         return 0;
 
     case I2C_FINISH:
@@ -285,7 +288,7 @@ static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event 
event)
         } else if (mctp->state == I2C_MCTP_STATE_RX_STARTED) {
             trace_i2c_mctp_drop("expected SOM");
             goto drop;
-        } else if (((pkt->mctp.hdr.flags >> 4) & 0x3) != (mctp->tx.pktseq++ & 
0x3)) {
+        } else if (((pkt->mctp.hdr.flags >> 4) & 0x3) != (++mctp->tx.pktseq & 
0x3)) {
             trace_i2c_mctp_drop_invalid_pktseq((pkt->mctp.hdr.flags >> 4) & 
0x3,
                                                mctp->tx.pktseq & 0x3);
             goto drop;

Reply via email to