Home | History | Annotate | Download | only in tools
      1 #include "nugget_tools.h"
      2 
      3 #include <app_nugget.h>
      4 #include <nos/NuggetClient.h>
      5 
      6 #include <chrono>
      7 #include <cinttypes>
      8 #include <cstring>
      9 #include <iostream>
     10 #include <thread>
     11 #include <vector>
     12 
     13 #ifdef ANDROID
     14 #include <android-base/endian.h>
     15 #include "nos/CitadeldProxyClient.h"
     16 #else
     17 #include "gflags/gflags.h"
     18 
     19 DEFINE_string(nos_core_serial, "", "USB device serial number to open");
     20 #endif  // ANDROID
     21 
     22 #ifndef LOG
     23 #define LOG(x) std::cerr << __FILE__ << ":" << __LINE__ << " " << #x << ": "
     24 #endif  // LOG
     25 
     26 using std::chrono::duration;
     27 using std::chrono::duration_cast;
     28 using std::chrono::high_resolution_clock;
     29 using std::chrono::microseconds;
     30 using std::string;
     31 
     32 namespace nugget_tools {
     33 
     34 std::string GetCitadelUSBSerialNo() {
     35 #ifdef ANDROID
     36   return "";
     37 #else
     38   if (FLAGS_nos_core_serial.empty()) {
     39     const char *env_default = secure_getenv("CITADEL_DEVICE");
     40     if (env_default && *env_default) {
     41       FLAGS_nos_core_serial.assign(env_default);
     42       std::cerr << "Using CITADEL_DEVICE=" << FLAGS_nos_core_serial << "\n";
     43     }
     44   }
     45   return FLAGS_nos_core_serial;
     46 #endif
     47 }
     48 
     49 std::unique_ptr<nos::NuggetClientInterface> MakeNuggetClient() {
     50 #ifdef ANDROID
     51   std::unique_ptr<nos::NuggetClientInterface> client =
     52       std::unique_ptr<nos::NuggetClientInterface>(new nos::NuggetClient());
     53   client->Open();
     54   if (!client->IsOpen()) {
     55     client = std::unique_ptr<nos::NuggetClientInterface>(
     56         new nos::CitadeldProxyClient());
     57   }
     58   return client;
     59 #else
     60   return std::unique_ptr<nos::NuggetClientInterface>(
     61       new nos::NuggetClient(GetCitadelUSBSerialNo()));
     62 #endif
     63 }
     64 
     65 bool CyclesSinceBoot(nos::NuggetClientInterface *client, uint32_t *cycles) {
     66   std::vector<uint8_t> buffer;
     67   buffer.reserve(sizeof(uint32_t));
     68   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_CYCLES_SINCE_BOOT,
     69                       buffer, &buffer) != app_status::APP_SUCCESS) {
     70     perror("test");
     71     LOG(ERROR) << "CallApp(..., NUGGET_PARAM_CYCLES_SINCE_BOOT, ...) failed!\n";
     72     return false;
     73   };
     74   if (buffer.size() != sizeof(uint32_t)) {
     75     LOG(ERROR) << "Unexpected size of cycle count!\n";
     76     return false;
     77   }
     78   *cycles = le32toh(*reinterpret_cast<uint32_t *>(buffer.data()));
     79   return true;
     80 }
     81 
     82 static void ShowStats(const char *msg,
     83                       const struct nugget_app_low_power_stats& stats) {
     84   printf("%s\n", msg);
     85   printf("  hard_reset_count         %" PRIu64 "\n", stats.hard_reset_count);
     86   printf("  time_since_hard_reset    %" PRIu64 "\n",
     87          stats.time_since_hard_reset);
     88   printf("  wake_count               %" PRIu64 "\n", stats.wake_count);
     89   printf("  time_at_last_wake        %" PRIu64 "\n", stats.time_at_last_wake);
     90   printf("  time_spent_awake         %" PRIu64 "\n", stats.time_spent_awake);
     91   printf("  deep_sleep_count         %" PRIu64 "\n", stats.deep_sleep_count);
     92   printf("  time_at_last_deep_sleep  %" PRIu64 "\n",
     93          stats.time_at_last_deep_sleep);
     94   printf("  time_spent_in_deep_sleep %" PRIu64 "\n",
     95          stats.time_spent_in_deep_sleep);
     96 }
     97 
     98 bool RebootNugget(nos::NuggetClientInterface *client) {
     99   struct nugget_app_low_power_stats stats0;
    100   struct nugget_app_low_power_stats stats1;
    101   std::vector<uint8_t> buffer;
    102 
    103   // Grab stats before sleeping
    104   buffer.reserve(sizeof(struct nugget_app_low_power_stats));
    105   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
    106                       buffer, &buffer) != app_status::APP_SUCCESS) {
    107     LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
    108     return false;
    109   }
    110   memcpy(&stats0, buffer.data(), sizeof(stats0));
    111 
    112   // Capture the time here to allow for some tolerance on the reported time.
    113   auto start = high_resolution_clock::now();
    114 
    115   // Tell Nugget OS to reboot
    116   std::vector<uint8_t> ignored;
    117   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_REBOOT, ignored,
    118                       nullptr) != app_status::APP_SUCCESS) {
    119     LOG(ERROR) << "CallApp(..., NUGGET_PARAM_REBOOT, ...) failed!\n";
    120     return false;
    121   }
    122 
    123   // Grab stats after sleeping
    124   buffer.empty();
    125   buffer.reserve(sizeof(struct nugget_app_low_power_stats));
    126   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
    127                       buffer, &buffer) != app_status::APP_SUCCESS) {
    128     LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
    129     return false;
    130   }
    131   memcpy(&stats1, buffer.data(), sizeof(stats1));
    132 
    133   // Figure a max elapsed time that Nugget OS should see (our time + 5%).
    134   auto max_usecs =
    135       duration_cast<microseconds>(high_resolution_clock::now() - start) *
    136           105 / 100;
    137 
    138   // Verify that Citadel rebooted
    139   if (stats1.hard_reset_count == stats0.hard_reset_count + 1 &&
    140       stats1.time_at_last_wake == 0 &&
    141       stats1.deep_sleep_count == 0 &&
    142       std::chrono::microseconds(stats1.time_since_hard_reset) < max_usecs) {
    143     return true;
    144   }
    145 
    146   LOG(ERROR) << "Citadel didn't reboot within "
    147              << max_usecs.count() << " microseconds\n";
    148   ShowStats("stats before waiting", stats0);
    149   ShowStats("stats after waiting", stats1);
    150 
    151   return false;
    152 }
    153 
    154 bool WaitForSleep(nos::NuggetClientInterface *client, uint32_t *seconds_waited) {
    155   struct nugget_app_low_power_stats stats0;
    156   struct nugget_app_low_power_stats stats1;
    157   std::vector<uint8_t> buffer;
    158 
    159   buffer.reserve(sizeof(struct nugget_app_low_power_stats));
    160   // Grab stats before sleeping
    161   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
    162                       buffer, &buffer) != app_status::APP_SUCCESS) {
    163     LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
    164     return false;
    165   }
    166   memcpy(&stats0, buffer.data(), sizeof(stats0));
    167 
    168   // Wait for Citadel to fall asleep
    169   constexpr uint32_t wait_seconds = 4;
    170   std::this_thread::sleep_for(std::chrono::seconds(wait_seconds));
    171 
    172   // Grab stats after sleeping
    173   buffer.empty();
    174   buffer.reserve(sizeof(struct nugget_app_low_power_stats));
    175   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
    176                       buffer, &buffer) != app_status::APP_SUCCESS) {
    177     LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
    178     return false;
    179   }
    180   memcpy(&stats1, buffer.data(), sizeof(stats1));
    181 
    182   // Verify that Citadel went to sleep but didn't reboot
    183   if (stats1.hard_reset_count == stats0.hard_reset_count &&
    184       stats1.deep_sleep_count == stats0.deep_sleep_count + 1 &&
    185       stats1.wake_count == stats0.wake_count + 1 &&
    186       stats1.time_spent_in_deep_sleep > stats0.time_spent_in_deep_sleep) {
    187     // Yep, looks good
    188     if (seconds_waited) {
    189       *seconds_waited = wait_seconds;
    190     }
    191     return true;
    192   }
    193 
    194   LOG(ERROR) << "Citadel didn't sleep\n";
    195   ShowStats("stats before waiting", stats0);
    196   ShowStats("stats after waiting", stats1);
    197 
    198   return false;
    199 }
    200 
    201 bool WipeUserData(nos::NuggetClientInterface *client) {
    202   struct nugget_app_low_power_stats stats0;
    203   struct nugget_app_low_power_stats stats1;
    204   std::vector<uint8_t> buffer;
    205 
    206   // Grab stats before sleeping
    207   buffer.reserve(sizeof(struct nugget_app_low_power_stats));
    208   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
    209                       buffer, &buffer) != app_status::APP_SUCCESS) {
    210     LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
    211     return false;
    212   }
    213   memcpy(&stats0, buffer.data(), sizeof(stats0));
    214 
    215   // Request wipe of user data which should hard reboot
    216   buffer.resize(4);
    217   *reinterpret_cast<uint32_t *>(buffer.data()) = htole32(ERASE_CONFIRMATION);
    218   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT,
    219                          buffer, nullptr) != app_status::APP_SUCCESS) {
    220     return false;
    221   }
    222 
    223   // Grab stats after sleeping
    224   buffer.empty();
    225   buffer.reserve(sizeof(struct nugget_app_low_power_stats));
    226   if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
    227                       buffer, &buffer) != app_status::APP_SUCCESS) {
    228     LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
    229     return false;
    230   }
    231   memcpy(&stats1, buffer.data(), sizeof(stats1));
    232 
    233   // Verify that Citadel didn't reset
    234   const bool ret = stats1.hard_reset_count == stats0.hard_reset_count;
    235   if (!ret) {
    236     LOG(ERROR) << "Citadel reset while wiping user data\n";
    237   }
    238   return ret;
    239 }
    240 
    241 }  // namespace nugget_tools
    242