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