diff --git a/examples/typescript/src/index.html b/examples/typescript/src/index.html
index 82d40802..638273e8 100644
--- a/examples/typescript/src/index.html
+++ b/examples/typescript/src/index.html
@@ -54,6 +54,7 @@
Program
Flash Address |
+ Encrypt file ? |
File |
|
diff --git a/examples/typescript/src/index.ts b/examples/typescript/src/index.ts
index 7df3008f..bc0377de 100644
--- a/examples/typescript/src/index.ts
+++ b/examples/typescript/src/index.ts
@@ -155,35 +155,43 @@ addFileButton.onclick = () => {
element1.value = "0x1000";
cell1.appendChild(element1);
- // Column 2 - File selector
+ // Column 2 - Encrypt file ?
const cell2 = row.insertCell(1);
const element2 = document.createElement("input");
- element2.type = "file";
- element2.id = "selectFile" + rowCount;
- element2.name = "selected_File" + rowCount;
- element2.addEventListener("change", handleFileSelect, false);
+ element2.type = "checkbox";
+ element2.id = "file_encrypted" + rowCount;
+ element2.checked = false;
cell2.appendChild(element2);
- // Column 3 - Progress
+ // Column 3 - File selector
const cell3 = row.insertCell(2);
- cell3.classList.add("progress-cell");
- cell3.style.display = "none";
- cell3.innerHTML = ``;
-
- // Column 4 - Remove File
+ const element3 = document.createElement("input");
+ element3.type = "file";
+ element3.id = "selectFile" + rowCount;
+ element3.name = "selected_File" + rowCount;
+ element3.addEventListener("change", handleFileSelect, false);
+ cell3.appendChild(element3);
+
+ // Column 4 - Progress
const cell4 = row.insertCell(3);
- cell4.classList.add("action-cell");
+ cell4.classList.add("progress-cell");
+ cell4.style.display = "none";
+ cell4.innerHTML = ``;
+
+ // Column 5 - Remove File
+ const cell5 = row.insertCell(4);
+ cell5.classList.add("action-cell");
if (rowCount > 1) {
- const element4 = document.createElement("input");
- element4.type = "button";
+ const element5 = document.createElement("input");
+ element5.type = "button";
const btnName = "button" + rowCount;
- element4.name = btnName;
- element4.setAttribute("class", "btn");
- element4.setAttribute("value", "Remove"); // or element1.value = "button";
- element4.onclick = function () {
+ element5.name = btnName;
+ element5.setAttribute("class", "btn");
+ element5.setAttribute("value", "Remove"); // or element1.value = "button";
+ element5.onclick = function () {
removeRow(row);
};
- cell4.appendChild(element4);
+ cell5.appendChild(element5);
}
};
@@ -299,7 +307,7 @@ function validateProgramInputs() {
else if (offsetArr.includes(offset)) return "Offset field in row " + index + " is already in use!";
else offsetArr.push(offset);
- const fileObj = row.cells[1].childNodes[0];
+ const fileObj = row.cells[2].childNodes[0];
fileData = fileObj.data;
if (fileData == null) return "No file selected for row " + index + "!";
}
@@ -319,7 +327,7 @@ programButton.onclick = async () => {
// Hide error message
alertDiv.style.display = "none";
- const fileArray = [];
+ const fileArray: { data: string; address: number; encrypted: boolean }[] = [];
const progressBars = [];
for (let index = 1; index < table.rows.length; index++) {
@@ -328,16 +336,19 @@ programButton.onclick = async () => {
const offSetObj = row.cells[0].childNodes[0] as HTMLInputElement;
const offset = parseInt(offSetObj.value);
- const fileObj = row.cells[1].childNodes[0] as ChildNode & { data: string };
- const progressBar = row.cells[2].childNodes[0];
+ const encryptedObj = row.cells[1].childNodes[0] as HTMLInputElement;
+ const encryptFlag = encryptedObj.checked;
+
+ const fileObj = row.cells[2].childNodes[0] as ChildNode & { data: string };
+ const progressBar = row.cells[3].childNodes[0];
progressBar.textContent = "0";
progressBars.push(progressBar);
- row.cells[2].style.display = "initial";
- row.cells[3].style.display = "none";
+ row.cells[3].style.display = "initial";
+ row.cells[4].style.display = "none";
- fileArray.push({ data: fileObj.data, address: offset });
+ fileArray.push({ data: fileObj.data, address: offset, encrypted: encryptFlag });
}
try {
@@ -358,8 +369,8 @@ programButton.onclick = async () => {
} finally {
// Hide progress bars and show erase buttons
for (let index = 1; index < table.rows.length; index++) {
- table.rows[index].cells[2].style.display = "none";
- table.rows[index].cells[3].style.display = "initial";
+ table.rows[index].cells[3].style.display = "none";
+ table.rows[index].cells[4].style.display = "initial";
}
}
};
diff --git a/src/esploader.ts b/src/esploader.ts
index e47b1900..f6ff6c6d 100644
--- a/src/esploader.ts
+++ b/src/esploader.ts
@@ -13,10 +13,10 @@ import atob from "atob-lite";
*/
export interface FlashOptions {
/**
- * An array of file objects representing the data to be flashed.
- * @type {Array<{ data: string; address: number }>}
+ * An array of file objects representing the data to be flashed, address offset and if to be encrypted.
+ * @type {Array<{ data: string; address: number, encrypted: boolean }>}
*/
- fileArray: { data: string; address: number }[];
+ fileArray: { data: string; address: number; encrypted: boolean }[];
/**
* The size of the flash memory to be used.
@@ -207,12 +207,17 @@ export class ESPLoader {
ESP_READ_FLASH = 0xd2;
ESP_RUN_USER_CODE = 0xd3;
+ ESP_FLASH_ENCRYPT_DATA = 0xd4;
+
ESP_IMAGE_MAGIC = 0xe9;
ESP_CHECKSUM_MAGIC = 0xef;
// Response code(s) sent by ROM
ROM_INVALID_RECV_MSG = 0x05; // response if an invalid message is received
+ // Commands supported by ESP32-S2 and later chips ROM bootloader only
+ ESP_GET_SECURITY_INFO = 0x14;
+
ERASE_REGION_TIMEOUT_PER_MB = 30000;
ERASE_WRITE_TIMEOUT_PER_MB = 40000;
MD5_TIMEOUT_PER_MB = 8000;
@@ -255,6 +260,7 @@ export class ESPLoader {
private romBaudrate = 115200;
private debugLogging = false;
private syncStubDetected = false;
+ private secureDownloadMode = false;
/**
* Create a new ESPLoader to perform serial communication
@@ -266,7 +272,7 @@ export class ESPLoader {
*/
constructor(options: LoaderOptions) {
this.IS_STUB = false;
- this.FLASH_WRITE_SIZE = 0x4000;
+ this.FLASH_WRITE_SIZE = 0x400;
this.transport = options.transport;
this.baudrate = options.baudrate;
@@ -670,13 +676,21 @@ export class ESPLoader {
this.info("\n\r", false);
if (!detecting) {
- const chipMagicValue = (await this.readReg(0x40001000)) >>> 0;
- this.debug("Chip Magic " + chipMagicValue.toString(16));
- const chip = await magic2Chip(chipMagicValue);
- if (this.chip === null) {
- throw new ESPError(`Unexpected CHIP magic value ${chipMagicValue}. Failed to autodetect chip type.`);
- } else {
- this.chip = chip as ROM;
+ try {
+ const chipMagicValue = (await this.readReg(this.CHIP_DETECT_MAGIC_REG_ADDR)) >>> 0;
+ this.debug("Chip Magic " + chipMagicValue.toString(16));
+ const chip = await magic2Chip(chipMagicValue);
+ if (this.chip === null) {
+ throw new ESPError(`Unexpected CHIP magic value ${chipMagicValue}. Failed to autodetect chip type.`);
+ } else {
+ this.chip = chip as ROM;
+ }
+ } catch (error) {
+ if (error instanceof ESPError && error.message && error.message.indexOf("unsupported command error") !== -1) {
+ this.secureDownloadMode = true;
+ } else {
+ throw error;
+ }
}
}
}
@@ -695,6 +709,36 @@ export class ESPLoader {
}
}
+ /**
+ * Get the chip ID using the check command
+ * @returns {number} Chip ID
+ */
+ async getChipId() {
+ const securityInfo = await this.getSecurityInfo();
+ return securityInfo.chipId;
+ }
+
+ /**
+ * Get the chip security information
+ * @returns {number} Chip Security Information
+ */
+ async getSecurityInfo() {
+ const res = (await this.checkCommand(
+ "get security info",
+ this.ESP_GET_SECURITY_INFO,
+ new Uint8Array(0),
+ 0,
+ )) as Uint8Array;
+ const esp32s2 = res && res.length === 12;
+ return {
+ flags: res[0],
+ flashCryptCnt: res[1],
+ keyPurposes: res.slice(2, 9),
+ chipId: esp32s2 ? undefined : res[9],
+ apiVersion: esp32s2 ? undefined : res[10],
+ };
+ }
+
/**
* Execute the command and check the command response.
* @param {string} opDescription Command operation description.
@@ -780,7 +824,10 @@ export class ESPLoader {
* @param {number} hspiArg - Argument for SPI attachment
*/
async flashSpiAttach(hspiArg: number) {
- const pkt = this._intToByteArray(hspiArg);
+ let pkt = this._intToByteArray(hspiArg);
+ if (!this.IS_STUB) {
+ pkt = this._appendArray(pkt, this._intToByteArray(0));
+ }
await this.checkCommand("configure SPI flash pins", this.ESP_SPI_ATTACH, pkt);
}
@@ -803,9 +850,10 @@ export class ESPLoader {
* Start downloading to Flash (performs an erase)
* @param {number} size Size to erase
* @param {number} offset Offset to erase
+ * @param {boolean} encrypt Flag if encrypt
* @returns {number} Number of blocks (of size self.FLASH_WRITE_SIZE) to write.
*/
- async flashBegin(size: number, offset: number) {
+ async flashBegin(size: number, offset: number, encrypt = false) {
const numBlocks = Math.floor((size + this.FLASH_WRITE_SIZE - 1) / this.FLASH_WRITE_SIZE);
const eraseSize = this.chip.getEraseSize(offset, size);
@@ -822,7 +870,7 @@ export class ESPLoader {
pkt = this._appendArray(pkt, this._intToByteArray(this.FLASH_WRITE_SIZE));
pkt = this._appendArray(pkt, this._intToByteArray(offset));
if (this.IS_STUB == false) {
- pkt = this._appendArray(pkt, this._intToByteArray(0)); // XXX: Support encrypted
+ pkt = this._appendArray(pkt, this._intToByteArray(encrypt ? 1 : 0));
}
await this.checkCommand("enter Flash download mode", this.ESP_FLASH_BEGIN, pkt, undefined, timeout);
@@ -896,6 +944,31 @@ export class ESPLoader {
await this.checkCommand("write to target Flash after seq " + seq, this.ESP_FLASH_DATA, pkt, checksum, timeout);
}
+ /**
+ * Encrypt, write block to flash, retry if fail
+ * @param {Uint8Array} data Unsigned 8-bit array data.
+ * @param {number} seq Sequence number
+ * @param {number} timeout Timeout in milliseconds (ms)
+ */
+ async flashEncryptBlock(data: Uint8Array, seq: number, timeout: number) {
+ if (this.chip.SUPPORTS_ENCRYPTED_FLASH && !this.IS_STUB) {
+ await this.flashBlock(data, seq, timeout);
+ } else {
+ let pkt = this._appendArray(this._intToByteArray(data.length), this._intToByteArray(seq));
+ pkt = this._appendArray(pkt, this._intToByteArray(0));
+ pkt = this._appendArray(pkt, this._intToByteArray(0));
+ pkt = this._appendArray(pkt, data);
+ const checksum = this.checksum(data);
+ await this.checkCommand(
+ "Write encrypted to target Flash after seq " + seq,
+ this.ESP_FLASH_ENCRYPT_DATA,
+ pkt,
+ checksum,
+ timeout,
+ );
+ }
+ }
+
/**
* Write block to flash, send compressed, retry if fail
* @param {Uint8Array} data Unsigned int 8-bit array data to write
@@ -1335,13 +1408,13 @@ export class ESPLoader {
aFlashSize = this.parseFlashSizeArg(flashSize);
}
- const flashParams = (aFlashMode << 8) | (aFlashFreq + aFlashSize);
+ const flashParams = (aFlashMode << 8) | (aFlashSize + aFlashFreq);
this.info("Flash params set to " + flashParams.toString(16));
if (parseInt(image[2]) !== aFlashMode << 8) {
image = image.substring(0, 2) + (aFlashMode << 8).toString() + image.substring(2 + 1);
}
- if (parseInt(image[3]) !== aFlashFreq + aFlashSize) {
- image = image.substring(0, 3) + (aFlashFreq + aFlashSize).toString() + image.substring(3 + 1);
+ if (parseInt(image[3]) !== aFlashSize + aFlashFreq) {
+ image = image.substring(0, 3) + (aFlashSize + aFlashFreq).toString() + image.substring(3 + 1);
}
return image;
}
@@ -1361,12 +1434,68 @@ export class ESPLoader {
}
}
+ const encryptedFiles = options.fileArray.filter((f) => f.encrypted);
+
+ if (encryptedFiles && encryptedFiles.length > 0) {
+ let doWrite = true;
+ const isEncryptedDownloadDisabled = await this.chip.getEncryptedDownloadDisabled(this);
+ if (!this.secureDownloadMode && isEncryptedDownloadDisabled) {
+ const errMsg =
+ "This chip has encrypt functionality \n" +
+ "in UART download mode disabled. \n" +
+ "This is the Flash Encryption configuration for Production mode \n" +
+ "instead of Development mode.";
+ this.debug(errMsg);
+ throw new ESPError(errMsg);
+ }
+ for (const f of encryptedFiles) {
+ if (f.encrypted && this.chip.SUPPORTS_ENCRYPTED_FLASH && f.address % this.chip.FLASH_ENCRYPTED_WRITE_ALIGN) {
+ doWrite = false;
+ this.info(`File at address ${f.address} is not %d byte aligned, can't flash encrypted`);
+ }
+ }
+ if (!doWrite) {
+ const errMsg =
+ "Can't perform encrypted flash write,\n" + "consult Flash Encryption documentation for more information";
+ this.debug(errMsg);
+ throw new ESPError(errMsg);
+ }
+ } else if (this.chip.CHIP_NAME !== "ESP8266") {
+ const flashSecurityInfo = await this.getSecurityInfo();
+ const flashCryptCntBinaryString = flashSecurityInfo.flashCryptCnt.toString(2);
+ const onesCount = flashCryptCntBinaryString.split("").filter((char) => char === "1").length & 1;
+
+ if (this.chip.CHIP_NAME !== "ESP32" && this.secureDownloadMode && onesCount !== 0) {
+ throw new ESPError(
+ "WARNING: Detected flash encryption and " +
+ "secure download mode enabled.\n" +
+ "Flashing plaintext binary may brick your device! ",
+ );
+ }
+ const isEncryptedDownloadDisabled = await this.chip.getEncryptedDownloadDisabled(this);
+ const isFlashEncryptionEnabled = await this.chip.getFlashEncryptionEnabled(this);
+
+ if (!this.secureDownloadMode && isEncryptedDownloadDisabled && isFlashEncryptionEnabled) {
+ throw new ESPError(
+ "WARNING: Detected flash encryption enabled and " +
+ "download manual encrypt disabled.\n" +
+ "Flashing plaintext binary may brick your device! " +
+ "Use --force to override the warning.",
+ );
+ }
+ }
+
if (this.IS_STUB === true && options.eraseAll === true) {
await this.eraseFlash();
}
let image: string, address: number;
for (let i = 0; i < options.fileArray.length; i++) {
this.debug("Data Length " + options.fileArray[i].data.length);
+
+ let compress = options.compress;
+ if (compress && options.fileArray[i].encrypted) {
+ compress = false;
+ }
image = options.fileArray[i].data;
const reminder = options.fileArray[i].data.length % 4;
if (reminder > 0) image += "\xff\xff\xff\xff".substring(4 - reminder);
@@ -1384,12 +1513,12 @@ export class ESPLoader {
}
const uncsize = image.length;
let blocks: number;
- if (options.compress) {
+ if (compress) {
const uncimage = this.bstrToUi8(image);
image = this.ui8ToBstr(deflate(uncimage, { level: 9 }));
blocks = await this.flashDeflBegin(uncsize, image.length, address);
} else {
- blocks = await this.flashBegin(uncsize, address);
+ blocks = await this.flashBegin(uncsize, address, options.fileArray[i].encrypted);
}
let seq = 0;
let bytesSent = 0;
@@ -1418,7 +1547,7 @@ export class ESPLoader {
);
const block = this.bstrToUi8(image.slice(0, this.FLASH_WRITE_SIZE));
- if (options.compress) {
+ if (compress) {
const lenUncompressedPrevious = totalLenUncompressed;
inflate.push(block, false);
const blockUncompressed = totalLenUncompressed - lenUncompressedPrevious;
@@ -1436,7 +1565,11 @@ export class ESPLoader {
timeout = blockTimeout;
}
} else {
- throw new ESPError("Yet to handle Non Compressed writes");
+ if (options.fileArray[i].encrypted) {
+ await this.flashEncryptBlock(block, seq, timeout);
+ } else {
+ await this.flashBlock(block, seq, timeout);
+ }
}
bytesSent += block.length;
image = image.slice(this.FLASH_WRITE_SIZE, image.length);
@@ -1448,7 +1581,7 @@ export class ESPLoader {
}
d = new Date();
const t = d.getTime() - t1;
- if (options.compress) {
+ if (compress) {
this.info(
"Wrote " +
uncsize +
@@ -1461,7 +1594,7 @@ export class ESPLoader {
" seconds.",
);
}
- if (calcmd5) {
+ if (calcmd5 && !options.fileArray[i].encrypted && !this.secureDownloadMode) {
const res = await this.flashMd5sum(address, uncsize);
if (new String(res).valueOf() != new String(calcmd5).valueOf()) {
this.info("File md5: " + calcmd5);
@@ -1476,7 +1609,8 @@ export class ESPLoader {
if (this.IS_STUB) {
await this.flashBegin(0, 0);
- if (options.compress) {
+ const lastFileIndex = options.fileArray && options.fileArray.length ? options.fileArray.length - 1 : 0;
+ if (options.compress && lastFileIndex && !options.fileArray[lastFileIndex].encrypted) {
await this.flashDeflFinish();
} else {
await this.flashFinish();
@@ -1496,11 +1630,34 @@ export class ESPLoader {
this.info("Detected flash size: " + this.DETECTED_FLASH_SIZES[flidLowbyte]);
}
- async getFlashSize() {
- this.debug("flash_id");
+ async detectFlashSize(options?: FlashOptions) {
+ this.debug("flash_size");
+ if (this.secureDownloadMode) {
+ if (options && options.flashSize === "detect") {
+ const errMsg =
+ "Detecting flash size is not supported in secure download mode. Need to manually specify flash size.";
+ this.debug(errMsg);
+ throw new ESPError(errMsg);
+ } else {
+ return;
+ }
+ }
const flashid = await this.readFlashId();
const flidLowbyte = (flashid >> 16) & 0xff;
- return this.DETECTED_FLASH_SIZES_NUM[flidLowbyte];
+ let flashSize = this.DETECTED_FLASH_SIZES[flidLowbyte];
+
+ if (options && options.flashSize === "detect") {
+ if (!flashSize) {
+ this.info(
+ `WARNING: Could not auto-detect Flash size. FlashID: ${flashid}, SizeId: ${flashSize}. Defaulting to 4MB.`,
+ );
+ flashSize = "4MB";
+ } else {
+ this.info(`Auto-detected Flash size: ${flashSize}`);
+ }
+ options.flashSize = flashSize;
+ }
+ return flashSize;
}
/**
diff --git a/src/targets/esp32.ts b/src/targets/esp32.ts
index 6ef67f3c..9f942855 100644
--- a/src/targets/esp32.ts
+++ b/src/targets/esp32.ts
@@ -37,6 +37,43 @@ export class ESP32ROM extends ROM {
public ROM_DATA = ESP32_STUB.data;
public ROM_TEXT = ESP32_STUB.text;
+ public SUPPORTS_ENCRYPTED_FLASH = false;
+ public FLASH_ENCRYPTED_WRITE_ALIGN = 32;
+
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_RD_REG_BASE + 0x18;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 7; // EFUSE_RD_DISABLE_DL_ENCRYPT;
+
+ public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_RD_REG_BASE; // EFUSE_BLK0_WDATA0_REG
+ public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7f << 20; // EFUSE_FLASH_CRYPT_CNT
+
+ public EFUSE_RD_ABS_DONE_REG = this.EFUSE_RD_REG_BASE + 0x018;
+ public EFUSE_RD_ABS_DONE_0_MASK = 1 << 4;
+ public EFUSE_RD_ABS_DONE_1_MASK = 1 << 5;
+
+ public async getSecureBootEnabled(loader: ESPLoader): Promise {
+ const efuses = await loader.readReg(this.EFUSE_RD_ABS_DONE_REG);
+ const rev = await this.getChipRevision(loader);
+ return (
+ (efuses & this.EFUSE_RD_ABS_DONE_0_MASK) !== 0 || (rev >= 300 && efuses & this.EFUSE_RD_ABS_DONE_1_MASK) !== 0
+ );
+ }
+
+ public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise {
+ return (
+ ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !==
+ 0
+ );
+ }
+
+ public async getFlashEncryptionEnabled(loader: ESPLoader): Promise {
+ const flashCryptCounter =
+ (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK;
+
+ const binaryString = flashCryptCounter.toString(2);
+ const onesCount = binaryString.split("").filter((char) => char === "1").length & 1;
+ return onesCount !== 0;
+ }
+
public async readEfuse(loader: ESPLoader, offset: number): Promise {
const addr = this.EFUSE_RD_REG_BASE + 4 * offset;
loader.debug("Read efuse " + addr);
diff --git a/src/targets/esp32c2.ts b/src/targets/esp32c2.ts
index 17620ab1..b2912985 100644
--- a/src/targets/esp32c2.ts
+++ b/src/targets/esp32c2.ts
@@ -37,6 +37,36 @@ export class ESP32C2ROM extends ESP32C3ROM {
public ROM_DATA = ESP32C2_STUB.data;
public ROM_TEXT = ESP32C2_STUB.text;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 6;
+
+ public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x30; // EFUSE_BLK0_WDATA0_REG
+ public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT
+
+ public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038;
+ public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20;
+
+ public async getSecureBootEnabled(loader: ESPLoader): Promise {
+ const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG);
+ return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0;
+ }
+
+ public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise {
+ return (
+ ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !==
+ 0
+ );
+ }
+
+ public async getFlashEncryptionEnabled(loader: ESPLoader): Promise {
+ const flashCryptCounter =
+ (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK;
+
+ const binaryString = flashCryptCounter.toString(2);
+ const onesCount = binaryString.split("").filter((char) => char === "1").length & 1;
+ return onesCount !== 0;
+ }
+
public async getPkgVersion(loader: ESPLoader): Promise {
const numWord = 1;
const block1Addr = this.EFUSE_BASE + 0x040;
diff --git a/src/targets/esp32c3.ts b/src/targets/esp32c3.ts
index af65a081..4af37a94 100644
--- a/src/targets/esp32c3.ts
+++ b/src/targets/esp32c3.ts
@@ -36,6 +36,40 @@ export class ESP32C3ROM extends ROM {
public ROM_DATA = ESP32C3_STUB.data;
public ROM_TEXT = ESP32C3_STUB.text;
+ public SUPPORTS_ENCRYPTED_FLASH = true;
+ public FLASH_ENCRYPTED_WRITE_ALIGN = 16;
+
+ public EFUSE_RD_REG_BASE = this.EFUSE_BASE + 0x030;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_RD_REG_BASE + 0x30;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20;
+
+ public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG
+ public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT
+
+ public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038;
+ public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20;
+
+ public async getSecureBootEnabled(loader: ESPLoader): Promise {
+ const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG);
+ return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0;
+ }
+
+ public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise {
+ return (
+ ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !==
+ 0
+ );
+ }
+
+ public async getFlashEncryptionEnabled(loader: ESPLoader): Promise {
+ const flashCryptCounter =
+ (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK;
+
+ const binaryString = flashCryptCounter.toString(2);
+ const onesCount = binaryString.split("").filter((char) => char === "1").length & 1;
+ return onesCount !== 0;
+ }
+
public async getPkgVersion(loader: ESPLoader): Promise {
const numWord = 3;
const block1Addr = this.EFUSE_BASE + 0x044;
diff --git a/src/targets/esp32c6.ts b/src/targets/esp32c6.ts
index 6d82e3bd..326c6c82 100644
--- a/src/targets/esp32c6.ts
+++ b/src/targets/esp32c6.ts
@@ -36,6 +36,39 @@ export class ESP32C6ROM extends ROM {
public ROM_DATA = ESP32C6_STUB.data;
public ROM_TEXT = ESP32C6_STUB.text;
+ public SUPPORTS_ENCRYPTED_FLASH = true;
+ public FLASH_ENCRYPTED_WRITE_ALIGN = 16;
+
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20;
+
+ public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG
+ public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT
+
+ public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038;
+ public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20;
+
+ public async getSecureBootEnabled(loader: ESPLoader): Promise {
+ const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG);
+ return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0;
+ }
+
+ public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise {
+ return (
+ ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !==
+ 0
+ );
+ }
+
+ public async getFlashEncryptionEnabled(loader: ESPLoader): Promise {
+ const flashCryptCounter =
+ (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK;
+
+ const binaryString = flashCryptCounter.toString(2);
+ const onesCount = binaryString.split("").filter((char) => char === "1").length & 1;
+ return onesCount !== 0;
+ }
+
public async getPkgVersion(loader: ESPLoader) {
const numWord = 3;
const block1Addr = this.EFUSE_BASE + 0x044;
diff --git a/src/targets/esp32h2.ts b/src/targets/esp32h2.ts
index 4d2ccfaf..d0e4d9a4 100644
--- a/src/targets/esp32h2.ts
+++ b/src/targets/esp32h2.ts
@@ -40,6 +40,39 @@ export class ESP32H2ROM extends ROM {
public ROM_DATA = ESP32H2_STUB.data;
public ROM_TEXT = ESP32H2_STUB.text;
+ public SUPPORTS_ENCRYPTED_FLASH = true;
+ public FLASH_ENCRYPTED_WRITE_ALIGN = 16;
+
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20;
+
+ public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG
+ public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT
+
+ public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038;
+ public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20;
+
+ public async getSecureBootEnabled(loader: ESPLoader): Promise {
+ const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG);
+ return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0;
+ }
+
+ public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise {
+ return (
+ ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !==
+ 0
+ );
+ }
+
+ public async getFlashEncryptionEnabled(loader: ESPLoader): Promise {
+ const flashCryptCounter =
+ (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK;
+
+ const binaryString = flashCryptCounter.toString(2);
+ const onesCount = binaryString.split("").filter((char) => char === "1").length & 1;
+ return onesCount !== 0;
+ }
+
public async getChipDescription(loader: ESPLoader) {
return this.CHIP_NAME;
}
diff --git a/src/targets/esp32s2.ts b/src/targets/esp32s2.ts
index f23387a3..f4e84802 100644
--- a/src/targets/esp32s2.ts
+++ b/src/targets/esp32s2.ts
@@ -36,6 +36,39 @@ export class ESP32S2ROM extends ROM {
public ROM_DATA = ESP32S2_STUB.data;
public ROM_TEXT = ESP32S2_STUB.text;
+ public SUPPORTS_ENCRYPTED_FLASH = true;
+ public FLASH_ENCRYPTED_WRITE_ALIGN = 16;
+
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 19;
+
+ public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG
+ public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT
+
+ public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038;
+ public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20;
+
+ public async getSecureBootEnabled(loader: ESPLoader): Promise {
+ const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG);
+ return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0;
+ }
+
+ public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise {
+ return (
+ ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !==
+ 0
+ );
+ }
+
+ public async getFlashEncryptionEnabled(loader: ESPLoader): Promise {
+ const flashCryptCounter =
+ (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK;
+
+ const binaryString = flashCryptCounter.toString(2);
+ const onesCount = binaryString.split("").filter((char) => char === "1").length & 1;
+ return onesCount !== 0;
+ }
+
public async getPkgVersion(loader: ESPLoader): Promise {
const numWord = 3;
const block1Addr = this.EFUSE_BASE + 0x044;
diff --git a/src/targets/esp32s3.ts b/src/targets/esp32s3.ts
index 583a3f87..f506be29 100644
--- a/src/targets/esp32s3.ts
+++ b/src/targets/esp32s3.ts
@@ -40,6 +40,39 @@ export class ESP32S3ROM extends ROM {
public ROM_DATA = ESP32S3_STUB.data;
public ROM_TEXT = ESP32S3_STUB.text;
+ public SUPPORTS_ENCRYPTED_FLASH = true;
+ public FLASH_ENCRYPTED_WRITE_ALIGN = 16;
+
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_BASE + 0x30;
+ public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20;
+
+ public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; // EFUSE_BLK0_WDATA0_REG
+ public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; // EFUSE_FLASH_CRYPT_CNT
+
+ public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038;
+ public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20;
+
+ public async getSecureBootEnabled(loader: ESPLoader): Promise {
+ const secureBootEnableReg = await loader.readReg(this.EFUSE_SECURE_BOOT_EN_REG);
+ return (secureBootEnableReg & this.EFUSE_SECURE_BOOT_EN_MASK) !== 0;
+ }
+
+ public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise {
+ return (
+ ((await loader.readReg(this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG)) & this.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT) !==
+ 0
+ );
+ }
+
+ public async getFlashEncryptionEnabled(loader: ESPLoader): Promise {
+ const flashCryptCounter =
+ (await loader.readReg(this.EFUSE_SPI_BOOT_CRYPT_CNT_REG)) & this.EFUSE_SPI_BOOT_CRYPT_CNT_MASK;
+
+ const binaryString = flashCryptCounter.toString(2);
+ const onesCount = binaryString.split("").filter((char) => char === "1").length & 1;
+ return onesCount !== 0;
+ }
+
public async getChipDescription(loader: ESPLoader) {
return "ESP32-S3";
}
diff --git a/src/targets/esp8266.ts b/src/targets/esp8266.ts
index 96ba8df5..75740673 100644
--- a/src/targets/esp8266.ts
+++ b/src/targets/esp8266.ts
@@ -42,6 +42,9 @@ export class ESP8266ROM extends ROM {
public ROM_DATA = ESP8266_STUB.data;
public ROM_TEXT = ESP8266_STUB.text;
+ public SUPPORTS_ENCRYPTED_FLASH = false;
+ public FLASH_ENCRYPTED_WRITE_ALIGN = 16;
+
public async readEfuse(loader: ESPLoader, offset: number): Promise {
const addr = this.EFUSE_RD_REG_BASE + 4 * offset;
loader.debug("Read efuse " + addr);
@@ -135,4 +138,16 @@ export class ESP8266ROM extends ROM {
public getEraseSize(offset: number, size: number) {
return size;
}
+
+ public async getEncryptedDownloadDisabled(loader: ESPLoader): Promise {
+ return false;
+ }
+
+ public async getFlashEncryptionEnabled(loader: ESPLoader): Promise {
+ return false;
+ }
+
+ public async getSecureBootEnabled(loader: ESPLoader) {
+ return false;
+ }
}
diff --git a/src/targets/rom.ts b/src/targets/rom.ts
index 02146ded..4fcf417e 100644
--- a/src/targets/rom.ts
+++ b/src/targets/rom.ts
@@ -75,6 +75,24 @@ export abstract class ROM {
return size;
}
+ /**
+ * Check if download has been disabled due to encryption
+ * @returns {boolean} Is encrypted download disabled (EFUSE_RD_DISABLE_DL_ENCRYPT).
+ */
+ abstract getEncryptedDownloadDisabled(loader: ESPLoader): Promise;
+
+ /**
+ * Check if flash encryption is enabled
+ * @returns {boolean} Is flash encryption enabled (EFUSE_FLASH_CRYPT_CNT).
+ */
+ abstract getFlashEncryptionEnabled(loader: ESPLoader): Promise;
+
+ /**
+ * Check if secure boot is enabled
+ * @returns {number} Is Secure boot enabled (EFUSE_RD_ABS_DONE_REG).
+ */
+ abstract getSecureBootEnabled(loader: ESPLoader): Promise;
+
abstract FLASH_SIZES: { [key: string]: number };
abstract BOOTLOADER_FLASH_OFFSET: number;
@@ -100,4 +118,7 @@ export abstract class ROM {
abstract UART_DATE_REG_ADDR: number; // not in esp8266
abstract TEXT_START: number;
// abstract XTAL_CLK_DIVIDER: number; //esp32
+
+ abstract SUPPORTS_ENCRYPTED_FLASH: boolean;
+ abstract FLASH_ENCRYPTED_WRITE_ALIGN: number;
}