fix: Recovery-Rettungsweg gegen Endlosschleife, Werksreset und Fremdnetz absichern
C1: nl_recovery_try() verglich nichts mit der laufenden Firmware. Vor einer laufenden Rettungsstation - laut Spec 9 der Normalfall, solange eine Charge migriert - lud die Lampe bei jedem Boot dasselbe Image erneut und startete neu. nl_ota_start_recovery() setzt jetzt ein Flag, das nl_fwup_verify_tail() nur im Recovery-Pfad auswerten lässt: Trägt der Trailer dieselbe Version wie die laufende Firmware (nl_trailer_version(), bisher ungenutzt), wird das Image abgelehnt und der Boot läuft normal weiter. Ein absichtliches Neuflashen derselben Version über die REST-API bleibt möglich. Kein neuer Wert im last_ota_error-Vokabular (Spec 8.2); unterschieden wird über die Konsole. C1/I2: boot_ms wurde erst nach nl_recovery_try() gesetzt und der Power-Cycle-Zähler erst 5 s danach gelöscht - bei erfolgreicher Recovery also nie. Nach NL_FACTORY_RESET_BOOTS Runden folgte ein Werksreset. boot_ms startet jetzt am Anfang von app_task (damit gilt wieder das in Spec 3.4/5.5 zugesagte 5-s-Fenster statt faktisch ~8 s), und nl_app_clear_reset_counter() löscht den Zähler zusätzlich, sobald die Rettungsstation gefunden ist. I3: tls_sys_auto_mode_run() stößt direkt nach UserMain() einen SDK-eigenen Verbindungsversuch an; das dabei entstehende NETIF_IP_NET_UP war in recovery_join_event() nicht vom eigenen Join zu unterscheiden, die Lampe hätte im falschen Netz geladen. nl_wifi_disable_sdk_auto_connect() schaltet das Flag ab und kappt eine bestehende Verbindung jetzt am Anfang von nl_recovery_try(); zusätzlich prüft nl_wifi_join_blocking() über tls_wifi_get_current_bss(), dass die Verbindung wirklich zur erwarteten SSID gehört.
This commit is contained in:
+36
-4
@@ -10,6 +10,8 @@
|
||||
#include "sys/nl_ota.h"
|
||||
#include "util/nl_fwup_trailer.h"
|
||||
|
||||
#include "nl_version.h"
|
||||
|
||||
/* t_http_fwup() (sdk/Src/App/OTA/wm_http_fwup.c) has no declaration in any SDK header. */
|
||||
extern int t_http_fwup(char *url);
|
||||
|
||||
@@ -19,8 +21,10 @@ extern int t_http_fwup(char *url);
|
||||
|
||||
static char s_url[NL_OTA_URL_MAX];
|
||||
static volatile bool s_busy;
|
||||
static volatile bool s_reject_running_version; /* recovery path only, see nl_ota_start_recovery() */
|
||||
static const char * volatile s_error;
|
||||
static u32 s_stack[NL_OTA_STACK];
|
||||
static char s_tail_version[NL_TRAILER_VERSION_LEN]; /* used by the hook, kept off its 2 KB stack */
|
||||
|
||||
void nl_ota_init(void)
|
||||
{
|
||||
@@ -42,10 +46,28 @@ bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t
|
||||
{
|
||||
nl_trailer_err_t e = nl_trailer_verify(tail, tail_len, NINLIGHT_DEVICE_ID, sha256);
|
||||
|
||||
if (e != NL_TRAILER_OK)
|
||||
if (e != NL_TRAILER_OK) {
|
||||
s_error = nl_trailer_err_name(e);
|
||||
printf("ota: trailer %s\r\n", e == NL_TRAILER_OK ? "ok" : s_error);
|
||||
return e == NL_TRAILER_OK;
|
||||
printf("ota: trailer %s\r\n", s_error);
|
||||
return false;
|
||||
}
|
||||
/* C1: recovery only - the rescue station serves the same image to every lamp that boots in
|
||||
* front of it, so an image that already matches the running version must be refused here;
|
||||
* otherwise the lamp flashes and reboots in an endless loop. No new last_ota_error value is
|
||||
* invented for this (the vocabulary is fixed in spec 8.2); the log line below is what tells
|
||||
* the two cases apart. */
|
||||
if (s_reject_running_version) {
|
||||
nl_trailer_version(tail, s_tail_version);
|
||||
if (strcmp(s_tail_version, NINLIGHT_FW_VERSION) == 0) {
|
||||
printf("ota: recovery image already carries the running version %s, not flashing\r\n",
|
||||
s_tail_version);
|
||||
s_error = "trailer";
|
||||
return false;
|
||||
}
|
||||
printf("ota: recovery image version %s (running %s)\r\n", s_tail_version, NINLIGHT_FW_VERSION);
|
||||
}
|
||||
printf("ota: trailer ok\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Logs the URL without any "user:pass@" userinfo, so credentials embedded in the URL never
|
||||
@@ -83,7 +105,7 @@ static void ota_task(void *arg)
|
||||
tls_os_task_del(NL_OTA_PRIO, NULL);
|
||||
}
|
||||
|
||||
int nl_ota_start(const char *url)
|
||||
static int ota_start(const char *url, bool reject_running_version)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
@@ -98,6 +120,7 @@ int nl_ota_start(const char *url)
|
||||
}
|
||||
s_busy = true;
|
||||
s_error = NULL;
|
||||
s_reject_running_version = reject_running_version;
|
||||
memcpy(s_url, url, n + 1);
|
||||
|
||||
/* Persist the open light state and mark the pending update before the flash gets busy. */
|
||||
@@ -122,8 +145,17 @@ int nl_ota_start(const char *url)
|
||||
nl_app_unlock();
|
||||
(void)nl_app_save_settings();
|
||||
s_busy = false;
|
||||
/* Minor (final review): no value in the fixed vocabulary of spec 8.2 describes "could not
|
||||
* create the download task" (out of RAM/TCBs), and "download" would claim a transfer that
|
||||
* never happened. `busy` stays the closest fit - the chip really is too busy to start one -
|
||||
* and this log line is what distinguishes it from "an update is already running". */
|
||||
printf("ota: cannot create download task (out of resources)\r\n");
|
||||
s_error = "busy";
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nl_ota_start(const char *url) { return ota_start(url, false); }
|
||||
|
||||
int nl_ota_start_recovery(const char *url) { return ota_start(url, true); }
|
||||
|
||||
Reference in New Issue
Block a user