Merge branch 'copilot/fix-d4f5fc55-f916-458a-9155-deb9bbff6662' of https://github.com/wled/WLED into copilot/fix-d4f5fc55-f916-458a-9155-deb9bbff6662
This commit is contained in:
@@ -45,12 +45,26 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var isESP32 = false;
|
||||
function checkESP32() {
|
||||
fetch(getURL('/json/info')).then(r=>r.json()).then(d=>{
|
||||
isESP32 = d.arch && d.arch.startsWith('esp32');
|
||||
if (isESP32) {
|
||||
gId('bootloader-section').style.display = 'block';
|
||||
if (d.bootloaderSHA256) {
|
||||
gId('bootloader-hash').innerText = 'Current bootloader SHA256: ' + d.bootloaderSHA256;
|
||||
}
|
||||
}
|
||||
}).catch(e=>console.error(e));
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@import url("style.css");
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="GetV()">
|
||||
<body onload="GetV(); checkESP32();">
|
||||
<h2>WLED Software Update</h2>
|
||||
<form method='POST' action='./update' id='upd' enctype='multipart/form-data' onsubmit="toggle('upd')">
|
||||
Installed version: <span class="sip installed-version">Loading...</span><br>
|
||||
|
||||
@@ -24,6 +24,15 @@
|
||||
#include <mbedtls/sha256.h>
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(WLED_DISABLE_OTA)
|
||||
#include <esp_partition.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_flash.h>
|
||||
#include <esp_image_format.h>
|
||||
#include <bootloader_common.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#endif
|
||||
|
||||
// define flash strings once (saves flash memory)
|
||||
static const char s_redirecting[] PROGMEM = "Redirecting...";
|
||||
static const char s_content_enc[] PROGMEM = "Content-Encoding";
|
||||
@@ -865,6 +874,140 @@ void initServer()
|
||||
});
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(WLED_DISABLE_OTA)
|
||||
// ESP32 bootloader update endpoint
|
||||
server.on(F("/updatebootloader"), HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
if (!correctPIN) {
|
||||
serveSettings(request, true); // handle PIN page POST request
|
||||
return;
|
||||
}
|
||||
if (otaLock) {
|
||||
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254);
|
||||
return;
|
||||
}
|
||||
if (Update.hasError()) {
|
||||
serveMessage(request, 500, F("Bootloader update failed!"), F("Please check your file and retry!"), 254);
|
||||
} else {
|
||||
serveMessage(request, 200, F("Bootloader updated successfully!"), FPSTR(s_rebooting), 131);
|
||||
doReboot = true;
|
||||
}
|
||||
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool isFinal){
|
||||
IPAddress client = request->client()->remoteIP();
|
||||
if (((otaSameSubnet && !inSameSubnet(client)) && !strlen(settingsPIN)) || (!otaSameSubnet && !inLocalSubnet(client))) {
|
||||
DEBUG_PRINTLN(F("Attempted bootloader update from different/non-local subnet!"));
|
||||
request->send(401, FPSTR(CONTENT_TYPE_PLAIN), FPSTR(s_accessdenied));
|
||||
return;
|
||||
}
|
||||
if (!correctPIN || otaLock) return;
|
||||
|
||||
static uint8_t* bootloaderBuffer = nullptr;
|
||||
static size_t bootloaderBytesBuffered = 0;
|
||||
const uint32_t bootloaderOffset = 0x1000;
|
||||
const uint32_t maxBootloaderSize = 0x8000; // 32KB max
|
||||
|
||||
if (!index) {
|
||||
DEBUG_PRINTLN(F("Bootloader Update Start - buffering data"));
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
WLED::instance().disableWatchdog();
|
||||
#endif
|
||||
lastEditTime = millis(); // make sure PIN does not lock during update
|
||||
strip.suspend();
|
||||
strip.resetSegments();
|
||||
|
||||
// Allocate buffer for entire bootloader
|
||||
if (bootloaderBuffer) {
|
||||
free(bootloaderBuffer);
|
||||
bootloaderBuffer = nullptr;
|
||||
}
|
||||
bootloaderBuffer = (uint8_t*)malloc(maxBootloaderSize);
|
||||
if (!bootloaderBuffer) {
|
||||
DEBUG_PRINTLN(F("Failed to allocate bootloader buffer!"));
|
||||
strip.resume();
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
WLED::instance().enableWatchdog();
|
||||
#endif
|
||||
Update.abort();
|
||||
return;
|
||||
}
|
||||
bootloaderBytesBuffered = 0;
|
||||
|
||||
// Verify bootloader magic on first chunk
|
||||
if (!isValidBootloader(data, len)) {
|
||||
DEBUG_PRINTLN(F("Invalid bootloader file!"));
|
||||
free(bootloaderBuffer);
|
||||
bootloaderBuffer = nullptr;
|
||||
strip.resume();
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
WLED::instance().enableWatchdog();
|
||||
#endif
|
||||
Update.abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer the incoming data
|
||||
if (bootloaderBuffer && bootloaderBytesBuffered + len <= maxBootloaderSize) {
|
||||
memcpy(bootloaderBuffer + bootloaderBytesBuffered, data, len);
|
||||
bootloaderBytesBuffered += len;
|
||||
} else if (!bootloaderBuffer) {
|
||||
DEBUG_PRINTLN(F("Bootloader buffer not allocated!"));
|
||||
Update.abort();
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("Bootloader size exceeds maximum!"));
|
||||
if (bootloaderBuffer) {
|
||||
free(bootloaderBuffer);
|
||||
bootloaderBuffer = nullptr;
|
||||
}
|
||||
Update.abort();
|
||||
}
|
||||
|
||||
// Only write to flash when upload is complete
|
||||
if (isFinal) {
|
||||
bool success = false;
|
||||
if (!Update.hasError() && bootloaderBuffer && bootloaderBytesBuffered > 0) {
|
||||
DEBUG_PRINTF_P(PSTR("Bootloader buffered (%d bytes) - validating\n"), bootloaderBytesBuffered);
|
||||
|
||||
// Verify the complete bootloader image before flashing
|
||||
if (!verifyBootloaderImage(bootloaderBuffer, bootloaderBytesBuffered)) {
|
||||
DEBUG_PRINTLN(F("Bootloader validation failed!"));
|
||||
} else {
|
||||
// Erase bootloader region
|
||||
DEBUG_PRINTLN(F("Erasing bootloader region..."));
|
||||
esp_err_t err = esp_flash_erase_region(NULL, bootloaderOffset, maxBootloaderSize);
|
||||
if (err != ESP_OK) {
|
||||
DEBUG_PRINTF_P(PSTR("Bootloader erase error: %d\n"), err);
|
||||
} else {
|
||||
// Write buffered data to flash
|
||||
err = esp_flash_write(NULL, bootloaderBuffer, bootloaderOffset, bootloaderBytesBuffered);
|
||||
if (err != ESP_OK) {
|
||||
DEBUG_PRINTF_P(PSTR("Bootloader flash write error: %d\n"), err);
|
||||
} else {
|
||||
DEBUG_PRINTF_P(PSTR("Bootloader Update Success - %d bytes written\n"), bootloaderBytesBuffered);
|
||||
bootloaderSHA256Cached = false; // Invalidate cached bootloader hash
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (bootloaderBuffer) {
|
||||
free(bootloaderBuffer);
|
||||
bootloaderBuffer = nullptr;
|
||||
}
|
||||
bootloaderBytesBuffered = 0;
|
||||
|
||||
if (!success) {
|
||||
DEBUG_PRINTLN(F("Bootloader Update Failed"));
|
||||
strip.resume();
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
WLED::instance().enableWatchdog();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
server.on(F("/dmxmap"), HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send_P(200, FPSTR(CONTENT_TYPE_HTML), PAGE_dmxmap, dmxProcessor);
|
||||
|
||||
Reference in New Issue
Block a user