• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetCitadelUSBSerialNo()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 
MakeNuggetClient()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 
CyclesSinceBoot(nos::NuggetClientInterface * client,uint32_t * cycles)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 
ShowStats(const char * msg,const struct nugget_app_low_power_stats & stats)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 
RebootNugget(nos::NuggetClientInterface * client)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 
WaitForSleep(nos::NuggetClientInterface * client,uint32_t * seconds_waited)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 
WipeUserData(nos::NuggetClientInterface * client)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