Home | History | Annotate | Download | only in libnos_transport
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <nos/transport.h>
     18 
     19 #include <errno.h>
     20 #include <stdarg.h>
     21 #include <stdbool.h>
     22 #include <stddef.h>
     23 #include <stdint.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <time.h>
     27 #include <unistd.h>
     28 
     29 #include <application.h>
     30 
     31 #include "crc16.h"
     32 
     33 /* Note: evaluates expressions multiple times */
     34 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
     35 
     36 #define DEBUG_LOG 0
     37 #define VERBOSE_LOG 0
     38 
     39 #ifdef ANDROID
     40 /* Logging for Android */
     41 #define LOG_TAG "libnos_transport"
     42 #include <android-base/endian.h>
     43 #include <log/log.h>
     44 #include <sys/types.h>
     45 
     46 #define NLOGE(...) ALOGE(__VA_ARGS__)
     47 #define NLOGW(...) ALOGW(__VA_ARGS__)
     48 #define NLOGD(...) ALOGD(__VA_ARGS__)
     49 #define NLOGV(...) ALOGV(__VA_ARGS__)
     50 
     51 extern int usleep (uint32_t usec);
     52 
     53 #else
     54 /* Logging for other platforms */
     55 #include <stdio.h>
     56 
     57 #define NLOGE(...) do { fprintf(stderr, __VA_ARGS__); \
     58   fprintf(stderr, "\n"); } while (0)
     59 #define NLOGW(...) do { printf(__VA_ARGS__); \
     60   printf("\n"); } while (0)
     61 #define NLOGD(...) do { if (DEBUG_LOG) { \
     62   printf(__VA_ARGS__); printf("\n"); } } while (0)
     63 #define NLOGV(...) do { if (VERBOSE_LOG) { \
     64   printf(__VA_ARGS__); printf("\n"); } } while (0)
     65 
     66 #endif
     67 
     68 /*
     69  * If Citadel is rebooting it will take a while to become responsive again. We
     70  * expect a reboot to take around 100ms but we'll keep trying for 300ms to leave
     71  * plenty of margin.
     72  */
     73 #define RETRY_COUNT 60
     74 #define RETRY_WAIT_TIME_US 5000
     75 
     76 /* In case of CRC error, try to retransmit */
     77 #define CRC_RETRY_COUNT 3
     78 
     79 /* How long to poll before giving up */
     80 #define POLL_LIMIT_SECONDS 60
     81 
     82 struct transport_context {
     83   const struct nos_device *dev;
     84   uint8_t app_id;
     85   uint16_t params;
     86   const uint8_t *args;
     87   uint32_t arg_len;
     88   uint8_t *reply;
     89   uint32_t *reply_len;
     90 };
     91 
     92 /*
     93  * Read a datagram from the device, correctly handling retries.
     94  */
     95 static int nos_device_read(const struct nos_device *dev, uint32_t command,
     96                            void *buf, uint32_t len) {
     97   int retries = RETRY_COUNT;
     98   while (retries--) {
     99     int err = dev->ops.read(dev->ctx, command, buf, len);
    100 
    101     if (err == -EAGAIN) {
    102       /* Linux driver returns EAGAIN error if Citadel chip is asleep.
    103        * Give to the chip a little bit of time to awake and retry reading
    104        * status again. */
    105       usleep(RETRY_WAIT_TIME_US);
    106       continue;
    107     }
    108 
    109     if (err) {
    110       NLOGE("Failed to read: %s", strerror(-err));
    111     }
    112     return -err;
    113   }
    114 
    115   return ETIMEDOUT;
    116 }
    117 
    118 /*
    119  * Write a datagram to the device, correctly handling retries.
    120  */
    121 static int nos_device_write(const struct nos_device *dev, uint32_t command,
    122                             const void *buf, uint32_t len) {
    123   int retries = RETRY_COUNT;
    124   while (retries--) {
    125     int err = dev->ops.write(dev->ctx, command, buf, len);
    126 
    127     if (err == -EAGAIN) {
    128       /* Linux driver returns EAGAIN error if Citadel chip is asleep.
    129        * Give to the chip a little bit of time to awake and retry reading
    130        * status again. */
    131       usleep(RETRY_WAIT_TIME_US);
    132       continue;
    133     }
    134 
    135     if (err) {
    136       NLOGE("Failed to write: %s", strerror(-err));
    137     }
    138     return -err;
    139   }
    140 
    141   return ETIMEDOUT;
    142 }
    143 
    144 /*
    145  * Get the status regardless of protocol version. All fields not passed by the
    146  * slave are set to 0 so the caller must check the version before interpretting
    147  * them.
    148  *
    149  * Returns non-zero on error.
    150  */
    151 static int get_status(const struct transport_context *ctx,
    152                       struct transport_status *out) {
    153   union {
    154     struct transport_status status;
    155     uint8_t data[STATUS_MAX_LENGTH];
    156   } st;
    157   int retries = CRC_RETRY_COUNT;
    158   while (retries--) {
    159     /* Get the status from the device */
    160     const uint32_t command = CMD_ID(ctx->app_id) | CMD_IS_READ | CMD_TRANSPORT;
    161     if (nos_device_read(ctx->dev, command, &st, STATUS_MAX_LENGTH) != 0) {
    162       NLOGE("Failed to read app %d status", ctx->app_id);
    163       return -1;
    164     }
    165 
    166     /* All unset fields will be 0. */
    167     memset(out, 0, sizeof(*out));
    168 
    169     /* Examine v0 fields */
    170     out->status = le32toh(st.status.status);
    171     out->reply_len = le16toh(st.status.reply_len);
    172 
    173     /* Identify v0 as length will be an invalid value */
    174     const uint16_t length = le16toh(st.status.length);
    175     if (length < STATUS_MIN_LENGTH || length > STATUS_MAX_LENGTH) {
    176       out->version = TRANSPORT_V0;
    177       return 0;
    178     }
    179 
    180     /* Examine v1 fields */
    181     out->length = length;
    182     out->version = le16toh(st.status.version);
    183     out->flags = le16toh(st.status.flags);
    184     out->crc = le16toh(st.status.crc);
    185     out->reply_crc = le16toh(st.status.reply_crc);
    186 
    187     /* Calculate the CRC of the status message */
    188     st.status.crc = 0;
    189     const uint16_t our_crc = crc16(&st.status, length);
    190 
    191     /* Check the CRC, if it fails we will retry */
    192     if (out->crc != our_crc) {
    193       NLOGW("App %d status CRC mismatch: theirs=%04x ours=%04x",
    194             ctx->app_id, out->crc, our_crc);
    195       continue;
    196     }
    197 
    198     /* Identify and examine v2+ fields here */
    199 
    200     return 0;
    201   }
    202 
    203   NLOGE("Unable to get valid checksum on app %d status", ctx->app_id);
    204   return -1;
    205 }
    206 
    207 /*
    208  * Try and reset the protocol state on Citadel for a new transaction.
    209  */
    210 static int clear_status(const struct transport_context *ctx) {
    211   const uint32_t command = CMD_ID(ctx->app_id) | CMD_TRANSPORT;
    212   if (nos_device_write(ctx->dev, command, NULL, 0) != 0) {
    213     NLOGE("Failed to clear app %d status", ctx->app_id);
    214     return -1;
    215   }
    216   return 0;
    217 }
    218 
    219 /*
    220  * Ensure that the app is in an idle state ready to handle the transaction.
    221  */
    222 static uint32_t make_ready(const struct transport_context *ctx) {
    223   struct transport_status status;
    224 
    225   if (get_status(ctx, &status) != 0) {
    226     NLOGE("Failed to inspect app %d", ctx->app_id);
    227     return APP_ERROR_IO;
    228   }
    229   NLOGD("App %d inspection status=0x%08x reply_len=%d protocol=%d flags=0x%04x",
    230         ctx->app_id, status.status, status.reply_len, status.version, status.flags);
    231 
    232   /* If it's already idle then we're ready to proceed */
    233   if (status.status == APP_STATUS_IDLE) {
    234     if (status.version != TRANSPORT_V0
    235         && (status.flags & STATUS_FLAG_WORKING)) {
    236       /* The app is still working when we don't expect it to be. We won't be
    237        * able to clear the state so might need to force a reset to recover. */
    238       NLOGE("App %d is still working", ctx->app_id);
    239       return APP_ERROR_BUSY;
    240     }
    241     return APP_SUCCESS;
    242   }
    243 
    244   /* Try clearing the status */
    245   NLOGD("Clearing previous app %d status", ctx->app_id);
    246   if (clear_status(ctx) != 0) {
    247     NLOGE("Failed to force app %d to idle status", ctx->app_id);
    248     return APP_ERROR_IO;
    249   }
    250 
    251   /* Check again */
    252   if (get_status(ctx, &status) != 0) {
    253     NLOGE("Failed to get app %d's cleared status", ctx->app_id);
    254     return APP_ERROR_IO;
    255   }
    256   NLOGD("Cleared app %d status=0x%08x reply_len=%d flags=0x%04x",
    257         ctx->app_id, status.status, status.reply_len, status.flags);
    258 
    259   /* It's ignoring us and is still not ready, so it's broken */
    260   if (status.status != APP_STATUS_IDLE) {
    261     NLOGE("App %d is not responding", ctx->app_id);
    262     return APP_ERROR_IO;
    263   }
    264 
    265   return APP_SUCCESS;
    266 }
    267 
    268 /*
    269  * Split request into datagrams and send command to have app process it.
    270  */
    271 static uint32_t send_command(const struct transport_context *ctx) {
    272   const uint8_t *args = ctx->args;
    273   uint16_t arg_len = ctx->arg_len;
    274   uint16_t crc;
    275 
    276   NLOGD("Send app %d command data (%d bytes)", ctx->app_id, arg_len);
    277   uint32_t command = CMD_ID(ctx->app_id) | CMD_IS_DATA | CMD_TRANSPORT;
    278   /* This always sends at least 1 packet to support the v0 protocol */
    279   do {
    280     /*
    281      * We can't send more per datagram than the device can accept. For Citadel
    282      * using the TPM Wait protocol on SPS, this is a constant. For other buses
    283      * it may not be, but this is what we support here. Due to peculiarities of
    284      * Citadel's SPS hardware, our protocol requires that we specify the length
    285      * of what we're about to send in the params field of each Write.
    286      */
    287     const uint16_t ulen = MIN(arg_len, MAX_DEVICE_TRANSFER);
    288     CMD_SET_PARAM(command, ulen);
    289 
    290     NLOGV("Write app %d command 0x%08x, bytes %d", ctx->app_id, command, ulen);
    291     if (nos_device_write(ctx->dev, command, args, ulen) != 0) {
    292       NLOGE("Failed to send datagram to app %d", ctx->app_id);
    293       return APP_ERROR_IO;
    294     }
    295 
    296     /* Any further Writes needed to send all the args must set the MORE bit */
    297     command |= CMD_MORE_TO_COME;
    298     args += ulen;
    299     arg_len -= ulen;
    300   } while (arg_len);
    301 
    302   /* Finally, send the "go" command */
    303   command = CMD_ID(ctx->app_id) | CMD_PARAM(ctx->params);
    304 
    305   /*
    306    * The outgoing crc covers:
    307    *
    308    *   1. the (16-bit) length of args
    309    *   2. the args buffer (if any)
    310    *   3. the (32-bit) "go" command
    311    *   4. the command info with crc set to 0
    312    */
    313   struct transport_command_info command_info = {
    314     .length = sizeof(command_info),
    315     .version = htole16(TRANSPORT_V1),
    316     .crc = 0,
    317     .reply_len_hint = ctx->reply_len ? htole16(*ctx->reply_len) : 0,
    318   };
    319   arg_len = ctx->arg_len;
    320   crc = crc16(&arg_len, sizeof(arg_len));
    321   crc = crc16_update(ctx->args, ctx->arg_len, crc);
    322   crc = crc16_update(&command, sizeof(command), crc);
    323   crc = crc16_update(&command_info, sizeof(command_info), crc);
    324   command_info.crc = htole16(crc);
    325 
    326   /* Tell the app to handle the request while also sending the command_info
    327    * which will be ignored by the v0 protocol. */
    328   NLOGD("Send app %d go command 0x%08x", ctx->app_id, command);
    329   if (0 != nos_device_write(ctx->dev, command, &command_info, sizeof(command_info))) {
    330     NLOGE("Failed to send command datagram to app %d", ctx->app_id);
    331     return APP_ERROR_IO;
    332   }
    333 
    334   return APP_SUCCESS;
    335 }
    336 
    337 static bool timespec_before(const struct timespec *lhs, const struct timespec *rhs) {
    338   if (lhs->tv_sec == rhs->tv_sec) {
    339     return lhs->tv_nsec < rhs->tv_nsec;
    340   } else {
    341     return lhs->tv_sec < rhs->tv_sec;
    342   }
    343 }
    344 
    345 /*
    346  * Keep polling until the app says it is done.
    347  */
    348 static uint32_t poll_until_done(const struct transport_context *ctx,
    349                                 struct transport_status *status) {
    350   uint32_t poll_count = 0;
    351 
    352   /* Start the timer */
    353   struct timespec now;
    354   struct timespec abort_at;
    355   if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
    356     NLOGE("clock_gettime() failing: %s", strerror(errno));
    357     return APP_ERROR_IO;
    358   }
    359   abort_at.tv_sec = now.tv_sec + POLL_LIMIT_SECONDS;
    360   abort_at.tv_nsec = now.tv_nsec;
    361 
    362   NLOGD("Polling app %d", ctx->app_id);
    363   do {
    364     /* Poll the status */
    365     if (get_status(ctx, status) != 0) {
    366       return APP_ERROR_IO;
    367     }
    368     poll_count++;
    369     /* Log at higher priority every 16 polls */
    370     if ((poll_count & (16 - 1)) == 0) {
    371       NLOGD("App %d poll=%d status=0x%08x reply_len=%d flags=0x%04x",
    372             ctx->app_id, poll_count, status->status, status->reply_len, status->flags);
    373     } else {
    374       NLOGV("App %d poll=%d status=0x%08x reply_len=%d flags=0x%04x",
    375             ctx->app_id, poll_count, status->status, status->reply_len, status->flags);
    376     }
    377 
    378     /* Check whether the app is done */
    379     if (status->status & APP_STATUS_DONE) {
    380       NLOGD("App %d polled=%d status=0x%08x reply_len=%d flags=0x%04x",
    381             ctx->app_id, poll_count, status->status, status->reply_len, status->flags);
    382       return APP_STATUS_CODE(status->status);
    383     }
    384 
    385     /* Check that the app is still working on it */
    386     if (status->version != TRANSPORT_V0
    387         && !(status->flags & STATUS_FLAG_WORKING)) {
    388       /* The slave has stopped working without being done so it's misbehaving */
    389       NLOGE("App %d just stopped working", ctx->app_id);
    390       return APP_ERROR_INTERNAL;
    391     }
    392     if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
    393       NLOGE("clock_gettime() failing: %s", strerror(errno));
    394       return APP_ERROR_IO;
    395     }
    396   } while (timespec_before(&now, &abort_at));
    397 
    398   NLOGE("App %d not done after polling %d times in %d seconds",
    399         ctx->app_id, poll_count, POLL_LIMIT_SECONDS);
    400   return APP_ERROR_TIMEOUT;
    401 }
    402 
    403 /*
    404  * Reconstruct the reply data from datagram stream.
    405  */
    406 static uint32_t receive_reply(const struct transport_context *ctx,
    407                               const struct transport_status *status) {
    408   int retries = CRC_RETRY_COUNT;
    409   while (retries--) {
    410     NLOGD("Read app %d reply data (%d bytes)", ctx->app_id, status->reply_len);
    411 
    412     uint32_t command = CMD_ID(ctx->app_id) | CMD_IS_READ | CMD_TRANSPORT | CMD_IS_DATA;
    413     uint8_t *reply = ctx->reply;
    414     uint16_t left = MIN(*ctx->reply_len, status->reply_len);
    415     uint16_t got = 0;
    416     uint16_t crc = 0;
    417     while (left) {
    418       /* We can't read more per datagram than the device can send */
    419       const uint16_t gimme = MIN(left, MAX_DEVICE_TRANSFER);
    420       NLOGV("Read app %d command=0x%08x, bytes=%d", ctx->app_id, command, gimme);
    421       if (nos_device_read(ctx->dev, command, reply, gimme) != 0) {
    422         NLOGE("Failed to receive datagram from app %d", ctx->app_id);
    423         return APP_ERROR_IO;
    424       }
    425 
    426       /* Any further Reads should set the MORE bit. This only works if Nugget
    427        * OS sends back CRCs, but that's the only time we'd retry anyway. */
    428       command |= CMD_MORE_TO_COME;
    429 
    430       crc = crc16_update(reply, gimme, crc);
    431       reply += gimme;
    432       left -= gimme;
    433       got += gimme;
    434     }
    435     /* got it all */
    436     *ctx->reply_len = got;
    437 
    438     /* v0 protocol doesn't support CRC so hopefully it's ok */
    439     if (status->version == TRANSPORT_V0) return APP_SUCCESS;
    440 
    441     if (crc == status->reply_crc) return APP_SUCCESS;
    442     NLOGW("App %d reply CRC mismatch: theirs=%04x ours=%04x", ctx->app_id, status->reply_crc, crc);
    443   }
    444 
    445   NLOGE("Unable to get valid checksum on app %d reply data", ctx->app_id);
    446   return APP_ERROR_IO;
    447 }
    448 
    449 /*
    450  * Driver for the master of the transport protocol.
    451  */
    452 uint32_t nos_call_application(const struct nos_device *dev,
    453                               uint8_t app_id, uint16_t params,
    454                               const uint8_t *args, uint32_t arg_len,
    455                               uint8_t *reply, uint32_t *reply_len)
    456 {
    457   uint32_t res;
    458   const struct transport_context ctx = {
    459     .dev = dev,
    460     .app_id = app_id,
    461     .params = params,
    462     .args = args,
    463     .arg_len = arg_len,
    464     .reply = reply,
    465     .reply_len = reply_len,
    466   };
    467 
    468   if ((ctx.arg_len && !ctx.args) ||
    469       (ctx.reply_len && *ctx.reply_len && !ctx.reply)) {
    470     NLOGE("Invalid args to %s()", __func__);
    471     return APP_ERROR_IO;
    472   }
    473 
    474   NLOGD("Calling app %d with params 0x%04x", app_id, params);
    475 
    476   struct transport_status status;
    477   uint32_t status_code;
    478   int retries = CRC_RETRY_COUNT;
    479   while (retries--) {
    480     /* Wake up and wait for Citadel to be ready */
    481     res = make_ready(&ctx);
    482     if (res) return res;
    483 
    484     /* Tell the app what to do */
    485     res = send_command(&ctx);
    486     if (res) return res;
    487 
    488     /* Wait until the app has finished */
    489     status_code = poll_until_done(&ctx, &status);
    490     if (status_code != APP_ERROR_CHECKSUM) break;
    491     NLOGW("App %d request checksum error", app_id);
    492   }
    493   if (status_code == APP_ERROR_CHECKSUM) {
    494     NLOGE("App %d equest checksum failed too many times", app_id);
    495     status_code = APP_ERROR_IO;
    496   }
    497 
    498   /* Get the reply, but only if the app produced data and the caller wants it */
    499   if (ctx.reply && ctx.reply_len && *ctx.reply_len && status.reply_len) {
    500     res = receive_reply(&ctx, &status);
    501     if (res) return res;
    502   } else if (reply_len) {
    503     *reply_len = 0;
    504   }
    505 
    506   NLOGV("Clear app %d reply for the next caller", app_id);
    507   /* This should work, but isn't completely fatal if it doesn't because the
    508    * next call will try again. */
    509   (void)clear_status(&ctx);
    510 
    511   NLOGD("App %d returning 0x%x", app_id, status_code);
    512   return status_code;
    513 }
    514