├── .nojekyll ├── .eslintignore ├── .prettierrc ├── examples └── typescript │ ├── .gitignore │ ├── assets │ ├── favicon.ico │ └── esp-logo.png │ ├── .parcelrc │ ├── tsconfig.json │ ├── README.md │ ├── package.json │ └── src │ └── index.html ├── .gitignore ├── src ├── types │ ├── error.ts │ ├── resetModes.ts │ ├── arguments.ts │ ├── loaderTerminal.ts │ ├── flashOptions.ts │ └── loaderOptions.ts ├── targets │ ├── stub_flasher │ │ ├── README.md │ │ ├── stub_flasher_32c2.json │ │ ├── stub_flasher_32.json │ │ ├── stub_flasher_32p4.json │ │ ├── stub_flasher_32c61.json │ │ ├── stub_flasher_32c6.json │ │ ├── stub_flasher_32h2.json │ │ ├── stub_flasher_32c3.json │ │ ├── stub_flasher_32s2.json │ │ ├── stub_flasher_32c5.json │ │ ├── stub_flasher_32s3.json │ │ └── stub_flasher_8266.json │ ├── esp32c6.ts │ ├── rom.ts │ ├── esp32c2.ts │ ├── esp32h2.ts │ ├── esp8266.ts │ ├── esp32c61.ts │ ├── esp32c5.ts │ ├── esp32c3.ts │ ├── esp32.ts │ ├── esp32p4.ts │ ├── esp32s3.ts │ └── esp32s2.ts ├── index.ts ├── util.ts ├── stubFlasher.ts ├── image │ ├── index.ts │ ├── others.ts │ ├── esp8266.ts │ └── esp32.ts └── reset.ts ├── .devcontainer └── devcontainer.json ├── .eslintrc ├── tsconfig.json ├── .vscode └── tasks.json ├── .github └── workflows │ ├── new_issues.yml │ ├── issue_comment.yml │ ├── new_prs.yml │ ├── ci.yml │ ├── pages.yml │ ├── publish.yml │ └── pr_comment.yml ├── rollup.config.mjs ├── package.json ├── README.md └── LICENSE /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /examples/typescript/.gitignore: -------------------------------------------------------------------------------- 1 | node/modules 2 | .parcel-cache 3 | dist 4 | package-lock.json -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | bundle.js 4 | docs 5 | esptool-js-*.tgz 6 | .vscode/settings.json 7 | .parcel-cache 8 | -------------------------------------------------------------------------------- /examples/typescript/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esptool-js/HEAD/examples/typescript/assets/favicon.ico -------------------------------------------------------------------------------- /examples/typescript/assets/esp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espressif/esptool-js/HEAD/examples/typescript/assets/esp-logo.png -------------------------------------------------------------------------------- /examples/typescript/.parcelrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@parcel/config-default", 3 | "resolvers": [ 4 | "parcel-resolver-ignore", 5 | "..." 6 | ] 7 | } -------------------------------------------------------------------------------- /src/types/error.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a Espressif chip error. 3 | */ 4 | class ESPError extends Error {} 5 | 6 | /** 7 | * Represents a Espressif timeout chip error. 8 | */ 9 | class TimeoutError extends ESPError {} 10 | 11 | export { ESPError, TimeoutError }; 12 | -------------------------------------------------------------------------------- /src/types/resetModes.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Reset modes that can be used before connection to chip 3 | */ 4 | export type Before = "default_reset" | "usb_reset" | "no_reset" | "no_reset_no_sync"; 5 | 6 | /** 7 | * Reset modes that can be used after operation is finished. 8 | */ 9 | export type After = "hard_reset" | "soft_reset" | "no_reset" | "no_reset_stub" | "custom_reset"; 10 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esptool.js", 3 | "image": "mcr.microsoft.com/devcontainers/typescript-node:0-18-bullseye", 4 | 5 | "features": { 6 | "ghcr.io/devcontainers-contrib/features/npm-package:1": { 7 | "package": "http-server" 8 | } 9 | }, 10 | "forwardPorts": [ 5001 ], 11 | "customizations": { 12 | "vscode": { 13 | "settings": { 14 | "task.allowAutomaticTasks": "on" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint", "prettier", "jsdoc"], 5 | "extends": [ 6 | "eslint:recommended", 7 | "plugin:@typescript-eslint/eslint-recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:jsdoc/recommended", 10 | "prettier" 11 | ], 12 | "rules": { 13 | "no-console": 1, // Means warning 14 | "prettier/prettier": 2 // Means error 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2020", 4 | "target": "ES2019", 5 | "declaration": false, 6 | "allowSyntheticDefaultImports": true, 7 | "moduleResolution": "node", 8 | "noUnusedLocals": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "noImplicitReturns": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "lib": ["ES2020", "DOM"], 13 | "importHelpers": true, 14 | "resolveJsonModule": true 15 | }, 16 | "include": ["src/**/*"] 17 | } 18 | -------------------------------------------------------------------------------- /src/types/arguments.ts: -------------------------------------------------------------------------------- 1 | export type FlashSizeValues = 2 | | "detect" 3 | | "keep" 4 | | "256KB" 5 | | "512KB" 6 | | "1MB" 7 | | "2MB" 8 | | "2MB-c1" 9 | | "4MB" 10 | | "4MB-c1" 11 | | "8MB" 12 | | "16MB" 13 | | "32MB" 14 | | "64MB" 15 | | "128MB"; 16 | 17 | export type FlashModeValues = "keep" | "dio" | "qio" | "dout" | "qout"; 18 | 19 | export type FlashFreqValues = 20 | | "keep" 21 | | "80m" 22 | | "60m" 23 | | "48m" 24 | | "40m" 25 | | "30m" 26 | | "26m" 27 | | "24m" 28 | | "20m" 29 | | "16m" 30 | | "15m" 31 | | "12m"; 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2020", 4 | "target": "ES2019", 5 | "declaration": true, 6 | "allowSyntheticDefaultImports": true, 7 | "outDir": "./lib", 8 | "moduleResolution": "node", 9 | "noUnusedLocals": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitReturns": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "lib": ["ES2020", "DOM"], 14 | "importHelpers": true, 15 | "resolveJsonModule": true, 16 | "strict": true 17 | }, 18 | "typedocOptions": { 19 | "entryPoints": ["src/index.ts"], 20 | "out": "docs" 21 | }, 22 | "include": ["src/**/*"] 23 | } 24 | -------------------------------------------------------------------------------- /src/targets/stub_flasher/README.md: -------------------------------------------------------------------------------- 1 | # Flasher stub binaries 2 | 3 | Included here are the flasher stub binaries from [esptool](https://github.com/espressif/esptool) (.py) project. 4 | 5 | The binaries were copied from [v4.6.1 release of esptool](https://github.com/espressif/esptool/tree/v4.6.1/esptool/targets/stub_flasher). 6 | 7 | The source code used to produce these binaries can be found [here](https://github.com/espressif/esptool/tree/v4.6.1/flasher_stub). 8 | 9 | The stub is Copyright 2016 Cesanta Software Limited, 2016-2022 Espressif Systems (Shanghai) Co. Ltd. and is licensed under [GNU General Public License v2.0 or later](https://spdx.org/licenses/GPL-2.0-or-later.html) license. 10 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "build", 7 | "group": { 8 | "kind": "build", 9 | "isDefault": true 10 | }, 11 | "problemMatcher": [], 12 | "label": "npm: build", 13 | "detail": "npm run clean && tsc && rollup --config" 14 | }, 15 | { 16 | "type": "npm", 17 | "script": "install", 18 | "label": "NPM install", 19 | "runOptions": { 20 | "runOn": "folderOpen" 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "lint", 26 | "problemMatcher": ["$eslint-stylish"], 27 | "label": "npm: lint" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /src/types/loaderTerminal.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A wrapper around your implementation of a terminal by 3 | * implementing the clean, write and writeLine methods 4 | * which are called by the ESPLoader class. 5 | * @interface IEspLoaderTerminal 6 | */ 7 | export interface IEspLoaderTerminal { 8 | /** 9 | * Execute a terminal clean command. 10 | */ 11 | clean: () => void; 12 | /** 13 | * Write a string of data that include a line terminator. 14 | * @param {string} data - The string to write with line terminator. 15 | */ 16 | writeLine: (data: string) => void; 17 | /** 18 | * Write a string of data. 19 | * @param {string} data - The string to write. 20 | */ 21 | write: (data: string) => void; 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/new_issues.yml: -------------------------------------------------------------------------------- 1 | name: Sync issues to Jira 2 | 3 | # This workflow will be triggered when a new issue is opened 4 | on: issues 5 | 6 | jobs: 7 | sync_issues_to_jira: 8 | name: Sync issues to Jira 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - name: Sync GitHub issues to Jira project 13 | uses: espressif/github-actions/sync_issues_to_jira@master 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | JIRA_PASS: ${{ secrets.JIRA_PASS }} 17 | JIRA_PROJECT: ESPTOOL 18 | JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} 19 | JIRA_URL: ${{ secrets.JIRA_URL }} 20 | JIRA_USER: ${{ secrets.JIRA_USER }} 21 | -------------------------------------------------------------------------------- /.github/workflows/issue_comment.yml: -------------------------------------------------------------------------------- 1 | name: Sync issue comments to JIRA 2 | 3 | # This workflow will be triggered when new issue comment is created (including PR comments) 4 | on: issue_comment 5 | 6 | jobs: 7 | sync_issue_comments_to_jira: 8 | name: Sync Issue Comments to Jira 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - name: Sync issue comments to JIRA 13 | uses: espressif/github-actions/sync_issues_to_jira@master 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | JIRA_PASS: ${{ secrets.JIRA_PASS }} 17 | JIRA_PROJECT: ESPTOOL 18 | JIRA_COMPONENT: GitHub 19 | JIRA_URL: ${{ secrets.JIRA_URL }} 20 | JIRA_USER: ${{ secrets.JIRA_USER }} 21 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { ESPLoader, FlashReadCallback } from "./esploader.js"; 2 | export { 3 | ClassicReset, 4 | CustomReset, 5 | HardReset, 6 | UsbJtagSerialReset, 7 | validateCustomResetStringSequence, 8 | ResetConstructors, 9 | ResetStrategy, 10 | } from "./reset.js"; 11 | export { ROM } from "./targets/rom.js"; 12 | export { Transport, SerialOptions } from "./webserial.js"; 13 | export { decodeBase64Data, getStubJsonByChipName, Stub } from "./stubFlasher.js"; 14 | export { LoaderOptions } from "./types/loaderOptions.js"; 15 | export { FlashOptions } from "./types/flashOptions.js"; 16 | export { IEspLoaderTerminal } from "./types/loaderTerminal.js"; 17 | export { Before, After } from "./types/resetModes.js"; 18 | export { FlashModeValues, FlashSizeValues, FlashFreqValues } from "./types/arguments.js"; 19 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import babel from "@rollup/plugin-babel"; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import terser from '@rollup/plugin-terser'; 5 | import json from '@rollup/plugin-json'; 6 | 7 | // rollup.config.js 8 | /** 9 | * @type {import('rollup').RollupOptions} 10 | */ 11 | const config = { 12 | input: 'lib/index.js', 13 | output: { 14 | name: "esptooljs", 15 | file: 'bundle.js', 16 | format: 'es', 17 | inlineDynamicImports: true 18 | }, 19 | plugins: [ 20 | resolve({ preferBuiltins: false, mainFields: ["browser"]}), 21 | commonjs(), 22 | babel({ exclude: 'node_modules/**', babelHelpers: "runtime", skipPreflightCheck: true }), 23 | json({ namedExports: true, preferConst: true }), 24 | terser() 25 | ], 26 | }; 27 | 28 | const _default = config; 29 | export { _default as default }; 30 | -------------------------------------------------------------------------------- /.github/workflows/new_prs.yml: -------------------------------------------------------------------------------- 1 | name: Sync remain PRs to Jira 2 | 3 | # This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project 4 | # Note that, PRs can also get synced when new PR comment is created 5 | on: 6 | schedule: 7 | - cron: "0 * * * *" 8 | 9 | jobs: 10 | sync_prs_to_jira: 11 | name: Sync PRs to Jira 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@master 15 | - name: Sync PRs to Jira project 16 | uses: espressif/github-actions/sync_issues_to_jira@master 17 | with: 18 | cron_job: true 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | JIRA_PASS: ${{ secrets.JIRA_PASS }} 22 | JIRA_PROJECT: ESPTOOL 23 | JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} 24 | JIRA_URL: ${{ secrets.JIRA_URL }} 25 | JIRA_USER: ${{ secrets.JIRA_USER }} 26 | -------------------------------------------------------------------------------- /examples/typescript/README.md: -------------------------------------------------------------------------------- 1 | # Using Esptool-JS in a Typescript environment 2 | 3 | Typescript example shows basic usage of esptool-js in static html js website with Webserial. The main code is in `src/index.ts` which is called in the `index.html`. 4 | 5 | We are using Parcel to bundle resulting files for simplicity here. 6 | 7 | **NOTE:** This example is linked to the documentation generated from the source code. You could remove such dependency if necessary by remove `./docs/index.html` from `src/index.html` if you need so. NPM commands used below will generate documentation as well. 8 | 9 | ## Testing it locally 10 | 11 | ``` 12 | npm install 13 | npm run dev 14 | ``` 15 | 16 | Then open http://localhost:1234 in Chrome or Edge. The `npm run dev` step will call Parcel which start a local http server serving `index.html` with compiled `index.ts`. 17 | 18 | ## Generate build to publish 19 | 20 | ``` 21 | npm install 22 | npm run build 23 | ``` 24 | 25 | Copy the content of `dist` to your static pages service like Github pages. -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | ci: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-node@v4 17 | with: 18 | node-version: 16 19 | registry-url: 'https://registry.npmjs.org' 20 | - name: Install dependencies and build 🔧 21 | run: npm ci && npm run build 22 | - name: Run lint 23 | run: npm run lint 24 | - name: Run tests 25 | run: npm run test 26 | - name: Package module 27 | run: npm pack 28 | - name: Determine version 29 | id: version 30 | run: | 31 | ESPTOOLJS_VERSION=$(node -p "require('./package.json').version") 32 | echo $ESPTOOLJS_VERSION 33 | echo "version=$ESPTOOLJS_VERSION" >> $GITHUB_OUTPUT 34 | - name: Upload npm package file 35 | uses: actions/upload-artifact@v4 36 | with: 37 | name: esptool-js-${{ steps.version.outputs.version }}.tgz 38 | path: esptool-js-${{ steps.version.outputs.version }}.tgz -------------------------------------------------------------------------------- /examples/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript", 3 | "version": "1.0.0", 4 | "description": "This an example of using esptool-js with parcel and typescript", 5 | "source": "src/index.html", 6 | "scripts": { 7 | "build": "npm-run-all clean genDocs parcel:build", 8 | "clean": "shx rm -rf dist .parcel-cache", 9 | "dev": "npm-run-all clean genDocs parcel:dev", 10 | "genDocs": "npm run genDocs:root && shx mkdir -p dist && shx cp -r ../../docs dist", 11 | "genDocs:root": "cd ../.. && npm run genDocs", 12 | "parcel:dev": "cross-env PARCEL_WORKERS=0 parcel src/index.html", 13 | "parcel:build": "cross-env PARCEL_WORKERS=0 parcel build src/index.html --no-optimize --public-url ./", 14 | "test": "echo \"Error: no test specified\" && exit 1" 15 | }, 16 | "parcelIgnore": [ 17 | "./docs/.+" 18 | ], 19 | "author": "", 20 | "license": "ISC", 21 | "devDependencies": { 22 | "@types/w3c-web-usb": "^1.0.10", 23 | "cross-env": "^7.0.3", 24 | "npm-run-all": "^4.1.5", 25 | "parcel": "^2.8.3", 26 | "parcel-resolver-ignore": "^2.1.5", 27 | "shx": "^0.3.4", 28 | "typescript": "^4.9.4", 29 | "web-serial-polyfill": "^1.0.15" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy static content to Pages 2 | 3 | # Run on the main branch or on a manual trigger 4 | on: 5 | push: 6 | branches: ["main"] 7 | workflow_dispatch: 8 | 9 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 10 | permissions: 11 | contents: read 12 | pages: write 13 | id-token: write 14 | 15 | # Allow one concurrent deployment 16 | concurrency: 17 | group: "pages" 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | deploy: 22 | environment: 23 | name: github-pages 24 | url: ${{ steps.deployment.outputs.page_url }} 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | - name: Set up Node 30 | uses: actions/setup-node@v4 31 | with: 32 | node-version: 20 33 | - name: Install Node modules 34 | run: | 35 | npm install 36 | - name: Build example project and copy docs 37 | run: | 38 | npm run build 39 | cd examples/typescript 40 | npm install 41 | npm run build 42 | - name: Setup Pages 43 | uses: actions/configure-pages@v2 44 | - name: Upload artifact 45 | uses: actions/upload-pages-artifact@v3 46 | with: 47 | path: 'examples/typescript/dist' 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@v4 51 | -------------------------------------------------------------------------------- /src/types/flashOptions.ts: -------------------------------------------------------------------------------- 1 | import { FlashFreqValues, FlashModeValues } from "./arguments"; 2 | 3 | /** 4 | * Options for flashing a device with firmware. 5 | * @interface FlashOptions 6 | */ 7 | export interface FlashOptions { 8 | /** 9 | * An array of file objects representing the data to be flashed. 10 | * @type {Array<{ data: string; address: number }>} 11 | */ 12 | fileArray: { data: string; address: number }[]; 13 | 14 | /** 15 | * The flash mode to be used (e.g., QIO, QOUT, DIO, DOUT). 16 | * @type {FlashModeValues} 17 | */ 18 | flashMode: FlashModeValues; 19 | 20 | /** 21 | * The flash frequency to be used (e.g., 40MHz, 80MHz). 22 | * @type {FlashFreqValues} 23 | */ 24 | flashFreq: FlashFreqValues; 25 | 26 | /** 27 | * Flag indicating whether to erase all existing data in the flash memory before flashing. 28 | * @type {boolean} 29 | */ 30 | eraseAll: boolean; 31 | 32 | /** 33 | * Flag indicating whether to compress the data before flashing. 34 | * @type {boolean} 35 | */ 36 | compress: boolean; 37 | 38 | /** 39 | * A function to report the progress of the flashing operation (optional). 40 | * @type {(fileIndex: number, written: number, total: number) => void} 41 | */ 42 | reportProgress?: (fileIndex: number, written: number, total: number) => void; 43 | 44 | /** 45 | * A function to calculate the MD5 hash of the firmware image (optional). 46 | * @type {(image: string) => string} 47 | */ 48 | calculateMD5Hash?: (image: string) => string; 49 | } 50 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Pad to the next alignment boundary 3 | * @param {Uint8Array} data Uint8Array data to pad 4 | * @param {number} alignment Alignment boundary to fulfill 5 | * @param {number} padCharacter Character to fill with 6 | * @returns {Uint8Array} Padded UInt8Array image 7 | */ 8 | export function padTo(data: Uint8Array, alignment: number, padCharacter = 0xff): Uint8Array { 9 | const padMod = data.length % alignment; 10 | if (padMod !== 0) { 11 | const padding = new Uint8Array(alignment - padMod).fill(padCharacter); 12 | const paddedData = new Uint8Array(data.length + padding.length); 13 | paddedData.set(data); 14 | paddedData.set(padding, data.length); 15 | return paddedData; 16 | } 17 | return data; 18 | } 19 | 20 | export const ESP_CHECKSUM_MAGIC = 0xef; 21 | 22 | /** 23 | * Get the checksum for given unsigned 8-bit array 24 | * @param {Uint8Array} data Unsigned 8-bit integer array 25 | * @param {number} state Initial checksum 26 | * @returns {number} - Array checksum 27 | */ 28 | export function checksum(data: Uint8Array, state: number = ESP_CHECKSUM_MAGIC): number { 29 | for (let i = 0; i < data.length; i++) { 30 | state ^= data[i]; 31 | } 32 | return state; 33 | } 34 | 35 | /** 36 | * Convert a byte string to unsigned 8 bit integer array. 37 | * @param {string} bStr - binary string input 38 | * @returns {Uint8Array} Return a 8 bit unsigned integer array. 39 | */ 40 | export function bstrToUi8(bStr: string) { 41 | const u8Array = new Uint8Array(bStr.length); 42 | for (let i = 0; i < bStr.length; i++) { 43 | u8Array[i] = bStr.charCodeAt(i); 44 | } 45 | return u8Array; 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esptool-js", 3 | "version": "0.5.7", 4 | "module": "lib/index.js", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "license": "Apache-2.0", 8 | "files": [ 9 | "lib", 10 | "bundle.js" 11 | ], 12 | "scripts": { 13 | "build": "npm run clean && tsc && rollup --config", 14 | "clean": "rimraf lib bundle.js .parcel-cache esptool-js-*.tgz", 15 | "format": "prettier --write \"src/**/*.ts\"", 16 | "genDocs": "rimraf docs && typedoc", 17 | "lint": "eslint . --ext .ts", 18 | "lintAndFix": "eslint . --ext .ts --fix", 19 | "prepare": "npm run build", 20 | "test": "echo \"Error: no test specified\"", 21 | "prepublishOnly": "npm run test && npm run lint" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/espressif/esptool-js.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/espressif/esptool-js/issues" 29 | }, 30 | "dependencies": { 31 | "atob-lite": "^2.0.0", 32 | "pako": "^2.1.0", 33 | "tslib": "^2.4.1" 34 | }, 35 | "devDependencies": { 36 | "@rollup/plugin-babel": "^6.0.2", 37 | "@rollup/plugin-commonjs": "^23.0.2", 38 | "@rollup/plugin-json": "^6.0.0", 39 | "@rollup/plugin-node-resolve": "^15.0.1", 40 | "@rollup/plugin-terser": "^0.1.0", 41 | "@types/atob-lite": "^2.0.2", 42 | "@types/pako": "^2.0.0", 43 | "@types/w3c-web-serial": "^1.0.3", 44 | "@typescript-eslint/eslint-plugin": "^5.43.0", 45 | "@typescript-eslint/parser": "^5.43.0", 46 | "babel-loader": "^9.1.0", 47 | "eslint": "^8.28.0", 48 | "eslint-config-prettier": "^8.5.0", 49 | "eslint-plugin-jsdoc": "^46.4.5", 50 | "eslint-plugin-prettier": "^4.2.1", 51 | "prettier": "^2.7.1", 52 | "rimraf": "^3.0.2", 53 | "rollup": "^3.3.0", 54 | "typedoc": "^0.25.2", 55 | "typescript": "^4.8.4" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/types/loaderOptions.ts: -------------------------------------------------------------------------------- 1 | import { ResetConstructors } from "../reset"; 2 | import { Transport } from "../webserial"; 3 | import { FlashSizeValues } from "./arguments"; 4 | import { IEspLoaderTerminal } from "./loaderTerminal"; 5 | 6 | /* global SerialPort */ 7 | 8 | /** 9 | * Options to configure ESPLoader. 10 | * @interface LoaderOptions 11 | */ 12 | export interface LoaderOptions { 13 | /** 14 | * The transport mechanism to communicate with the device. 15 | * @type {Transport} 16 | */ 17 | transport: Transport; 18 | 19 | /** 20 | * The port to initialize the transport class. 21 | * @type {SerialPort} 22 | */ 23 | port?: SerialPort; 24 | 25 | /** 26 | * Set of options for SerialPort class. 27 | * @type {Transport} 28 | */ 29 | serialOptions?: SerialOptions; 30 | 31 | /** 32 | * The baud rate to be used for communication with the device. 33 | * @type {number} 34 | */ 35 | baudrate: number; 36 | 37 | /** 38 | * The size of the flash memory to be used. 39 | * Possible values are "detect", "keep", "256KB", "512KB", "1MB", "2MB", "2MB-c1", 40 | * "4MB", "4MB-c1", "8MB", "16MB", "32MB", "64MB", "128MB". 41 | * @type {FlashSizeValues} 42 | */ 43 | flashSize: FlashSizeValues; 44 | 45 | /** 46 | * An optional terminal interface to interact with the loader during the process. 47 | * @type {IEspLoaderTerminal} 48 | */ 49 | terminal?: IEspLoaderTerminal; 50 | 51 | /** 52 | * Flag indicating whether to enable debug logging for the loader (optional). 53 | * @type {boolean} 54 | */ 55 | debugLogging?: boolean; 56 | 57 | /** 58 | * Reset functions for connection. If undefined will use default ones. 59 | * @type {ResetConstructors} 60 | */ 61 | resetConstructors?: ResetConstructors; 62 | 63 | /** 64 | * Indicate if trace messages should be enabled or not. 65 | */ 66 | enableTracing?: boolean; 67 | } 68 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - "v*" 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: actions/setup-node@v4 12 | with: 13 | node-version: 16 14 | registry-url: "https://registry.npmjs.org" 15 | - name: Install dependencies and build 🔧 16 | run: npm ci && npm run build 17 | - name: Run lint 18 | run: npm run lint 19 | - name: Run tests 20 | run: npm run test 21 | - name: Package module 22 | run: npm pack 23 | - name: Verify tag version is the same as package.json version 24 | id: version 25 | run: | 26 | [ ${GITHUB_REF:11} = $(node -p "require('./package.json').version") ] 27 | echo "version=${GITHUB_REF:11}" >> $GITHUB_OUTPUT 28 | - name: Upload package file 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: esptool-js-${{ steps.version.outputs.version }}.tgz 32 | path: esptool-js-${{ steps.version.outputs.version }}.tgz 33 | - name: Create Release 34 | id: create_release 35 | uses: actions/create-release@v1.0.0 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | with: 39 | tag_name: ${{ github.ref }} 40 | release_name: Espressif ESPTOOL-JS ${{ github.ref }} 41 | draft: true 42 | body: | 43 | ### Release Highlights 44 | 45 | ### Features & Enhancements 46 | 47 | 48 | 49 | ### Bug Fixes 50 | 51 | 52 | - name: Upload release 53 | id: upload-release-asset 54 | uses: actions/upload-release-asset@v1.0.1 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | with: 58 | upload_url: ${{ steps.create_release.outputs.upload_url }} 59 | asset_path: ./esptool-js-${{ steps.version.outputs.version }}.tgz 60 | asset_name: esptool-js-${{ steps.version.outputs.version }}.tgz 61 | asset_content_type: application/zip 62 | - name: Publish package on NPM 📦 63 | run: npm publish 64 | env: 65 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 66 | -------------------------------------------------------------------------------- /.github/workflows/pr_comment.yml: -------------------------------------------------------------------------------- 1 | name: Comment on pull request 2 | on: 3 | workflow_run: 4 | workflows: [CI] 5 | types: [completed] 6 | jobs: 7 | pr_comment: 8 | if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/github-script@v6 12 | with: 13 | # This snippet is public-domain, taken from 14 | # https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml 15 | script: | 16 | async function upsertComment(owner, repo, issue_number, purpose, body) { 17 | const {data: comments} = await github.rest.issues.listComments( 18 | {owner, repo, issue_number}); 19 | 20 | const marker = ``; 21 | body = marker + "\n" + body; 22 | 23 | const existing = comments.filter((c) => c.body.includes(marker)); 24 | if (existing.length > 0) { 25 | const last = existing[existing.length - 1]; 26 | core.info(`Updating comment ${last.id}`); 27 | await github.rest.issues.updateComment({ 28 | owner, repo, 29 | body, 30 | comment_id: last.id, 31 | }); 32 | } else { 33 | core.info(`Creating a comment in issue / PR #${issue_number}`); 34 | await github.rest.issues.createComment({issue_number, body, owner, repo}); 35 | } 36 | } 37 | 38 | const {owner, repo} = context.repo; 39 | const run_id = ${{github.event.workflow_run.id}}; 40 | 41 | const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }}; 42 | if (!pull_requests.length) { 43 | return core.error("This workflow doesn't match any pull requests!"); 44 | } 45 | 46 | const artifacts = await github.paginate( 47 | github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id}); 48 | if (!artifacts.length) { 49 | return core.error(`No artifacts found`); 50 | } 51 | let body = `Download the artifacts for this pull request:\n`; 52 | for (const art of artifacts) { 53 | body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; 54 | } 55 | 56 | core.info("Review thread message body:", body); 57 | 58 | for (const pr of pull_requests) { 59 | await upsertComment(owner, repo, pr.number, 60 | "nightly-link", body); 61 | } 62 | -------------------------------------------------------------------------------- /src/stubFlasher.ts: -------------------------------------------------------------------------------- 1 | import atob from "atob-lite"; 2 | 3 | export interface Stub { 4 | bss_start?: number; 5 | data: string; 6 | decodedData: Uint8Array; 7 | data_start: number; 8 | entry: number; 9 | text: string; 10 | decodedText: Uint8Array; 11 | text_start: number; 12 | } 13 | 14 | /** 15 | * Import flash stub json for the given chip name. 16 | * @param {string} chipName Name of chip to obtain flasher stub 17 | * @returns {Stub} Stub information and decoded text and data 18 | */ 19 | export async function getStubJsonByChipName(chipName: string) { 20 | let jsonStub; 21 | switch (chipName) { 22 | case "ESP32": 23 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32.json"); 24 | break; 25 | case "ESP32-C2": 26 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32c2.json"); 27 | break; 28 | case "ESP32-C3": 29 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32c3.json"); 30 | break; 31 | case "ESP32-C5": 32 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32c5.json"); 33 | break; 34 | case "ESP32-C6": 35 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32c6.json"); 36 | break; 37 | case "ESP32-C61": 38 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32c61.json"); 39 | break; 40 | case "ESP32-H2": 41 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32h2.json"); 42 | break; 43 | case "ESP32-P4": 44 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32p4.json"); 45 | break; 46 | case "ESP32-S2": 47 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32s2.json"); 48 | break; 49 | case "ESP32-S3": 50 | jsonStub = await import("./targets/stub_flasher/stub_flasher_32s3.json"); 51 | break; 52 | case "ESP8266": 53 | jsonStub = await import("./targets/stub_flasher/stub_flasher_8266.json"); 54 | break; 55 | } 56 | 57 | if (jsonStub) { 58 | return { 59 | bss_start: jsonStub.bss_start, 60 | data: jsonStub.data, 61 | data_start: jsonStub.data_start, 62 | entry: jsonStub.entry, 63 | text: jsonStub.text, 64 | text_start: jsonStub.text_start, 65 | decodedData: decodeBase64Data(jsonStub.data), 66 | decodedText: decodeBase64Data(jsonStub.text), 67 | } as Stub; 68 | } 69 | return; 70 | } 71 | 72 | /** 73 | * Convert a base 64 string to Uint8Array. 74 | * @param {string} dataStr Base64 String to decode 75 | * @returns {Uint8Array} Decoded Uint8Array 76 | */ 77 | export function decodeBase64Data(dataStr: string) { 78 | const decoded = atob(dataStr); 79 | const chardata = decoded.split("").map(function (x) { 80 | return x.charCodeAt(0); 81 | }); 82 | return new Uint8Array(chardata); 83 | } 84 | -------------------------------------------------------------------------------- /src/image/index.ts: -------------------------------------------------------------------------------- 1 | import { ROM } from "../targets/rom"; 2 | import { ESPError } from "../types/error"; 3 | import { bstrToUi8 } from "../util"; 4 | import { BaseFirmwareImage, ESP_IMAGE_MAGIC } from "./base"; 5 | import { ESP32FirmwareImage } from "./esp32"; 6 | import { ESP8266ROMFirmwareImage, ESP8266V2FirmwareImage } from "./esp8266"; 7 | import { 8 | ESP32C2FirmwareImage, 9 | ESP32C3FirmwareImage, 10 | ESP32C5FirmwareImage, 11 | ESP32C61FirmwareImage, 12 | ESP32C6FirmwareImage, 13 | ESP32H2FirmwareImage, 14 | ESP32P4FirmwareImage, 15 | ESP32S2FirmwareImage, 16 | ESP32S3FirmwareImage, 17 | } from "./others"; 18 | 19 | /** 20 | * Function to load a firmware image from a string (from FileReader) 21 | * @param {ROM} rom - The ROM object representing the target device 22 | * @param {string} imageData Image data as a string 23 | * @returns {Promise} - A promise that resolves to the loaded firmware image 24 | */ 25 | export async function loadFirmwareImage(rom: ROM, imageData: string): Promise { 26 | // Convert the string data to a Uint8Array 27 | const binaryData = bstrToUi8(imageData); 28 | 29 | // Select the appropriate image class based on the chip 30 | const chipName = rom.CHIP_NAME.toLowerCase().replace(/[-()]/g, ""); 31 | 32 | let firmwareImageClass: typeof BaseFirmwareImage; 33 | 34 | if (chipName !== "esp8266") { 35 | switch (chipName) { 36 | case "esp32": 37 | firmwareImageClass = ESP32FirmwareImage; 38 | break; 39 | case "esp32s2": 40 | firmwareImageClass = ESP32S2FirmwareImage; 41 | break; 42 | case "esp32s3": 43 | firmwareImageClass = ESP32S3FirmwareImage; 44 | break; 45 | case "esp32c3": 46 | firmwareImageClass = ESP32C3FirmwareImage; 47 | break; 48 | case "esp32c2": 49 | firmwareImageClass = ESP32C2FirmwareImage; 50 | break; 51 | case "esp32c6": 52 | firmwareImageClass = ESP32C6FirmwareImage; 53 | break; 54 | case "esp32c61": 55 | firmwareImageClass = ESP32C61FirmwareImage; 56 | break; 57 | case "esp32c5": 58 | firmwareImageClass = ESP32C5FirmwareImage; 59 | break; 60 | case "esp32h2": 61 | firmwareImageClass = ESP32H2FirmwareImage; 62 | break; 63 | case "esp32p4": 64 | firmwareImageClass = ESP32P4FirmwareImage; 65 | break; 66 | default: 67 | throw new ESPError(`Unsupported chip name: ${chipName}`); 68 | } 69 | } else { 70 | const magic = binaryData[0]; 71 | if (magic === ESP_IMAGE_MAGIC) { 72 | firmwareImageClass = ESP8266ROMFirmwareImage; 73 | } else if (magic === ESP8266V2FirmwareImage.IMAGE_V2_MAGIC) { 74 | firmwareImageClass = ESP8266V2FirmwareImage; 75 | } else { 76 | throw new ESPError(`Invalid image magic number: ${magic}`); 77 | } 78 | } 79 | 80 | // Create an instance of the selected image class 81 | const image = new firmwareImageClass(rom); 82 | 83 | return image; 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Javascript implementation of esptool 2 | 3 | This repository contains a Javascript implementation of [esptool](https://github.com/espressif/esptool), a serial flasher utility for Espressif chips. `esptool-js` is based on [Web Serial API](https://wicg.github.io/serial/) and works in Google Chrome and Microsoft Edge [version 89 or later](https://developer.mozilla.org/en-US/docs/Web/API/Serial#browser_compatibility) browsers and Google Chrome on Android [version 61 or later](https://developer.mozilla.org/en-US/docs/Web/API/USB#browser_compatibility) via the [web-serial-polyfill](https://github.com/google/web-serial-polyfill) compatibility layer. 4 | 5 | **NOTE:** Unlike the Python-based esptool, `esptool-js` doesn't implement generation of binary images out of ELF files, and doesn't include companion tools similar to [espefuse.py](https://github.com/espressif/esptool/wiki/espefuse) and [espsecure.py](https://github.com/espressif/esptool/wiki/espsecure). 6 | 7 | ## Usage 8 | 9 | **CDN** 10 | 11 | `https://unpkg.com/esptool-js/lib/index.js` or `https://unpkg.com/esptool-js/bundle.js` to use the single bundle JavaScript file. 12 | 13 | **NPM** 14 | 15 | `npm install --save esptool-js` 16 | 17 | **Yarn** 18 | 19 | `yarn add --save esptool-js` 20 | 21 | Check an example project [here](https://github.com/espressif/esptool-js/tree/main/examples/typescript). 22 | 23 | **Nightly builds** for ESPTOOL-JS 24 | 25 | ## Define port filters for device using WebSerial 26 | 27 | ```js 28 | const portFilters: { usbVendorId?: number | undefined, usbProductId?: number | undefined }[] = []; 29 | const device = await navigator.serial.requestPort({ filters: portFilters }); 30 | ``` 31 | 32 | ## Inject a Terminal to use with esptool-js 33 | 34 | ```js 35 | // You can use any JavaScript compatible terminal by wrapping it in a helper object like this: 36 | let espLoaderTerminal = { 37 | clean() { 38 | // Implement the clean function call for your terminal here. 39 | }, 40 | writeLine(data) { 41 | // Implement the writeLine function call for your terminal here. 42 | }, 43 | write(data) { 44 | // Implement the write function call for your terminal here. 45 | }, 46 | }; 47 | ``` 48 | 49 | You can pass this terminal object to `ESPLoader` constructor as shown in the [examples projects](./examples/). 50 | 51 | ## Live demo 52 | 53 | Visit https://espressif.github.io/esptool-js/ to see this tool in action. 54 | 55 | ## Testing it locally 56 | 57 | ```sh 58 | npm install 59 | npm run build 60 | cd examples/typescript 61 | npm install 62 | npm run dev # Run local sever with example code 63 | ``` 64 | 65 | Then open `http://localhost:1234` in a Chrome browser. The `npm run build` step builds the `lib` used in the example `examples/typescript/index.html`. Update this reference as described in [Usage](#usage) section. 66 | 67 | ## Test from Pull Request artifact 68 | 69 | If you are testing the main branch or any Pull Request (PR) artifact you can follow these steps: 70 | 71 | 1. Get the `esptool-js-.tgz` where `` is the current version and download it. 72 | 2. Add the following line to your project's package.json dependencies 73 | 74 | ```json 75 | "dependencies": { 76 | "esptool-js": "file:../path/to/esptool-js-.tgz" 77 | } 78 | ``` 79 | 3. Use the package like `import "esptool-js/lib/index.js"` when added in package.json as shown before. 80 | 81 | ## License 82 | 83 | The code in this repository is Copyright (c) 2023 Espressif Systems (Shanghai) Co. Ltd. It is licensed under Apache 2.0 license, as described in [LICENSE](https://github.com/espressif/esptool-js/blob/main/LICENSE) file. 84 | -------------------------------------------------------------------------------- /src/image/others.ts: -------------------------------------------------------------------------------- 1 | import { ESP32C2ROM } from "../targets/esp32c2"; 2 | import { ESP32C3ROM } from "../targets/esp32c3"; 3 | import { ESP32C5ROM } from "../targets/esp32c5"; 4 | import { ESP32C6ROM } from "../targets/esp32c6"; 5 | import { ESP32C61ROM } from "../targets/esp32c61"; 6 | import { ESP32H2ROM } from "../targets/esp32h2"; 7 | import { ESP32P4ROM } from "../targets/esp32p4"; 8 | import { ESP32S2ROM } from "../targets/esp32s2"; 9 | import { ESP32S3ROM } from "../targets/esp32s3"; 10 | import { ESP32FirmwareImage } from "./esp32"; 11 | 12 | export class ESP32S2FirmwareImage extends ESP32FirmwareImage { 13 | ROM_LOADER: ESP32S2ROM; 14 | 15 | constructor(rom: ESP32S2ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 16 | super(rom, loadFile, appendDigest, ramOnlyHeader); 17 | this.ROM_LOADER = rom as ESP32S2ROM; 18 | } 19 | } 20 | 21 | export class ESP32S3FirmwareImage extends ESP32FirmwareImage { 22 | ROM_LOADER: ESP32S3ROM; 23 | 24 | constructor(rom: ESP32S3ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 25 | super(rom, loadFile, appendDigest, ramOnlyHeader); 26 | this.ROM_LOADER = rom as ESP32S3ROM; 27 | } 28 | } 29 | 30 | export class ESP32C3FirmwareImage extends ESP32FirmwareImage { 31 | ROM_LOADER: ESP32C3ROM; 32 | 33 | constructor(rom: ESP32C3ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 34 | super(rom, loadFile, appendDigest, ramOnlyHeader); 35 | this.ROM_LOADER = rom as ESP32C3ROM; 36 | } 37 | } 38 | 39 | export class ESP32C2FirmwareImage extends ESP32FirmwareImage { 40 | ROM_LOADER: ESP32C2ROM; 41 | 42 | constructor(rom: ESP32C2ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 43 | super(rom, loadFile, appendDigest, ramOnlyHeader); 44 | this.ROM_LOADER = rom as ESP32C2ROM; 45 | } 46 | 47 | MMU_PAGE_SIZE_CONF = [16384, 32768, 65536]; // 16KB, 32KB, 64KB 48 | } 49 | 50 | export class ESP32C6FirmwareImage extends ESP32FirmwareImage { 51 | ROM_LOADER: ESP32C6ROM; 52 | 53 | constructor(rom: ESP32C6ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 54 | super(rom, loadFile, appendDigest, ramOnlyHeader); 55 | this.ROM_LOADER = rom as ESP32C6ROM; 56 | } 57 | 58 | MMU_PAGE_SIZE_CONF = [8192, 16384, 32768, 65536]; 59 | } 60 | 61 | export class ESP32C61FirmwareImage extends ESP32C6FirmwareImage { 62 | ROM_LOADER: ESP32C61ROM; 63 | 64 | constructor(rom: ESP32C61ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 65 | super(rom, loadFile, appendDigest, ramOnlyHeader); 66 | this.ROM_LOADER = rom as ESP32C61ROM; 67 | } 68 | } 69 | 70 | export class ESP32C5FirmwareImage extends ESP32FirmwareImage { 71 | ROM_LOADER: ESP32C5ROM; 72 | 73 | constructor(rom: ESP32C5ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 74 | super(rom, loadFile, appendDigest, ramOnlyHeader); 75 | this.ROM_LOADER = rom as ESP32C5ROM; 76 | } 77 | } 78 | 79 | export class ESP32P4FirmwareImage extends ESP32FirmwareImage { 80 | ROM_LOADER: ESP32P4ROM; 81 | 82 | constructor(rom: ESP32P4ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 83 | super(rom, loadFile, appendDigest, ramOnlyHeader); 84 | this.ROM_LOADER = rom as ESP32P4ROM; 85 | } 86 | } 87 | 88 | export class ESP32H2FirmwareImage extends ESP32C6FirmwareImage { 89 | ROM_LOADER: ESP32H2ROM; 90 | 91 | constructor(rom: ESP32H2ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 92 | super(rom, loadFile, appendDigest, ramOnlyHeader); 93 | this.ROM_LOADER = rom as ESP32H2ROM; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/targets/esp32c6.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { ESP32C3ROM } from "./esp32c3.js"; 3 | import { MemoryMapEntry } from "./rom.js"; 4 | 5 | export class ESP32C6ROM extends ESP32C3ROM { 6 | public CHIP_NAME = "ESP32-C6"; 7 | public IMAGE_CHIP_ID = 13; 8 | public EFUSE_BASE = 0x600b0800; 9 | public EFUSE_BLOCK1_ADDR = this.EFUSE_BASE + 0x044; 10 | public MAC_EFUSE_REG = this.EFUSE_BASE + 0x044; 11 | public UART_CLKDIV_REG = 0x3ff40014; 12 | public UART_CLKDIV_MASK = 0xfffff; 13 | public UART_DATE_REG_ADDR = 0x6000007c; 14 | 15 | public FLASH_WRITE_SIZE = 0x400; 16 | public BOOTLOADER_FLASH_OFFSET = 0; 17 | 18 | public SPI_REG_BASE = 0x60002000; 19 | public SPI_USR_OFFS = 0x18; 20 | public SPI_USR1_OFFS = 0x1c; 21 | public SPI_USR2_OFFS = 0x20; 22 | public SPI_MOSI_DLEN_OFFS = 0x24; 23 | public SPI_MISO_DLEN_OFFS = 0x28; 24 | public SPI_W0_OFFS = 0x58; 25 | 26 | IROM_MAP_START = 0x42000000; 27 | IROM_MAP_END = 0x42800000; 28 | 29 | public MEMORY_MAP: MemoryMapEntry[] = [ 30 | [0x00000000, 0x00010000, "PADDING"], 31 | [0x42000000, 0x43000000, "DROM"], 32 | [0x40800000, 0x40880000, "DRAM"], 33 | [0x40800000, 0x40880000, "BYTE_ACCESSIBLE"], 34 | [0x4004ac00, 0x40050000, "DROM_MASK"], 35 | [0x40000000, 0x4004ac00, "IROM_MASK"], 36 | [0x42000000, 0x43000000, "IROM"], 37 | [0x40800000, 0x40880000, "IRAM"], 38 | [0x50000000, 0x50004000, "RTC_IRAM"], 39 | [0x50000000, 0x50004000, "RTC_DRAM"], 40 | [0x600fe000, 0x60100000, "MEM_INTERNAL2"], 41 | ]; 42 | 43 | public async getPkgVersion(loader: ESPLoader) { 44 | const numWord = 3; 45 | const block1Addr = this.EFUSE_BASE + 0x044; 46 | const addr = block1Addr + 4 * numWord; 47 | const word3 = await loader.readReg(addr); 48 | const pkgVersion = (word3 >> 21) & 0x07; 49 | return pkgVersion; 50 | } 51 | 52 | public async getChipRevision(loader: ESPLoader) { 53 | const block1Addr = this.EFUSE_BASE + 0x044; 54 | const numWord = 3; 55 | const pos = 18; 56 | const addr = block1Addr + 4 * numWord; 57 | const ret = ((await loader.readReg(addr)) & (0x7 << pos)) >> pos; 58 | return ret; 59 | } 60 | 61 | public async getChipDescription(loader: ESPLoader) { 62 | let desc: string; 63 | const pkgVer = await this.getPkgVersion(loader); 64 | if (pkgVer === 0) { 65 | desc = "ESP32-C6"; 66 | } else { 67 | desc = "unknown ESP32-C6"; 68 | } 69 | const chipRev = await this.getChipRevision(loader); 70 | desc += " (revision " + chipRev + ")"; 71 | return desc; 72 | } 73 | 74 | public async getChipFeatures(loader: ESPLoader) { 75 | return ["Wi-Fi 6", "BT 5", "IEEE802.15.4"]; 76 | } 77 | 78 | public async getCrystalFreq(loader: ESPLoader) { 79 | return 40; 80 | } 81 | 82 | public _d2h(d: number) { 83 | const h = (+d).toString(16); 84 | return h.length === 1 ? "0" + h : h; 85 | } 86 | 87 | public async readMac(loader: ESPLoader) { 88 | let mac0 = await loader.readReg(this.MAC_EFUSE_REG); 89 | mac0 = mac0 >>> 0; 90 | let mac1 = await loader.readReg(this.MAC_EFUSE_REG + 4); 91 | mac1 = (mac1 >>> 0) & 0x0000ffff; 92 | const mac = new Uint8Array(6); 93 | mac[0] = (mac1 >> 8) & 0xff; 94 | mac[1] = mac1 & 0xff; 95 | mac[2] = (mac0 >> 24) & 0xff; 96 | mac[3] = (mac0 >> 16) & 0xff; 97 | mac[4] = (mac0 >> 8) & 0xff; 98 | mac[5] = mac0 & 0xff; 99 | 100 | return ( 101 | this._d2h(mac[0]) + 102 | ":" + 103 | this._d2h(mac[1]) + 104 | ":" + 105 | this._d2h(mac[2]) + 106 | ":" + 107 | this._d2h(mac[3]) + 108 | ":" + 109 | this._d2h(mac[4]) + 110 | ":" + 111 | this._d2h(mac[5]) 112 | ); 113 | } 114 | 115 | public getEraseSize(offset: number, size: number) { 116 | return size; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/targets/rom.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | 3 | export type MemoryMapEntry = [number, number, string]; 4 | 5 | /** 6 | * Represents a chip ROM with basic registers field and abstract functions. 7 | */ 8 | export abstract class ROM { 9 | /** 10 | * Read ESP32 eFuse. 11 | * @param {ESPLoader} loader - Loader class to communicate with chip. 12 | * @param {number} offset - Offset to start erase. 13 | * @returns {number} The eFuse number. 14 | */ 15 | protected readEfuse?(loader: ESPLoader, offset: number): Promise; 16 | 17 | /** 18 | * Get the package version number. 19 | * @param {ESPLoader} loader - Loader class to communicate with chip. 20 | * @returns {number} The package version number. 21 | */ 22 | protected getPkgVersion?(loader: ESPLoader): Promise; 23 | 24 | /** 25 | * Get the chip revision number. 26 | * @param {ESPLoader} loader - Loader class to communicate with chip. 27 | * @returns {number} The chip revision number. 28 | */ 29 | protected getChipRevision?(loader: ESPLoader): Promise; 30 | 31 | /** 32 | * Get the chip description. 33 | * @param {ESPLoader} loader - Loader class to communicate with chip. 34 | * @returns {string} The chip description as string. 35 | */ 36 | abstract getChipDescription(loader: ESPLoader): Promise; 37 | /** 38 | * Get the chip features. 39 | * @param {ESPLoader} loader - Loader class to communicate with chip. 40 | * @returns {string} The chip features as string. 41 | */ 42 | abstract getChipFeatures(loader: ESPLoader): Promise; 43 | /** 44 | * Get the crystal frequency for the chip. 45 | * @param {ESPLoader} loader - Loader class to communicate with chip. 46 | * @returns {string} The crystal frequency as number. 47 | */ 48 | abstract getCrystalFreq(loader: ESPLoader): Promise; 49 | 50 | /** 51 | * Convert a number to hex string. 52 | * @param {number} d - Number to convert to hex string. 53 | * @returns {string} The hex string. 54 | */ 55 | abstract _d2h(d: number): string; 56 | 57 | /** 58 | * Get the chip mac address. 59 | * @param {ESPLoader} loader - Loader class to communicate with chip. 60 | * @returns {string} The mac address string. 61 | */ 62 | abstract readMac(loader: ESPLoader): Promise; 63 | 64 | /** 65 | * Function to be executed after chip connection 66 | * @param {ESPLoader} loader - Loader class to communicate with chip. 67 | */ 68 | postConnect?(loader: ESPLoader): Promise; 69 | 70 | /** 71 | * Get the chip erase size. 72 | * @param {number} offset - Offset to start erase. 73 | * @param {number} size - Size to erase. 74 | * @returns {number} The erase size of the chip as number. 75 | */ 76 | getEraseSize(offset: number, size: number): number { 77 | return size; 78 | } 79 | 80 | public FLASH_SIZES: { [key: string]: number } = { 81 | "1MB": 0x00, 82 | "2MB": 0x10, 83 | "4MB": 0x20, 84 | "8MB": 0x30, 85 | "16MB": 0x40, 86 | "32MB": 0x50, 87 | "64MB": 0x60, 88 | "128MB": 0x70, 89 | }; 90 | 91 | public FLASH_FREQUENCY: { [key: string]: number } = { 92 | "80m": 0xf, 93 | "40m": 0x0, 94 | "26m": 0x1, 95 | "20m": 0x2, 96 | }; 97 | 98 | abstract BOOTLOADER_FLASH_OFFSET: number; 99 | abstract CHIP_NAME: string; 100 | // abstract DR_REG_SYSCON_BASE: number; //esp32 101 | // abstract EFUSE_RD_REG_BASE: number; //esp32 102 | 103 | abstract FLASH_WRITE_SIZE: number; 104 | // abstract IMAGE_CHIP_ID: number; // not in esp8266 105 | abstract SPI_MOSI_DLEN_OFFS: number; // not in esp8266 106 | abstract SPI_MISO_DLEN_OFFS: number; // not in esp8266 107 | abstract SPI_REG_BASE: number; 108 | abstract SPI_USR_OFFS: number; 109 | abstract SPI_USR1_OFFS: number; 110 | abstract SPI_USR2_OFFS: number; 111 | abstract SPI_W0_OFFS: number; 112 | abstract UART_CLKDIV_MASK: number; 113 | abstract UART_CLKDIV_REG: number; 114 | abstract UART_DATE_REG_ADDR: number; // not in esp8266 115 | // abstract XTAL_CLK_DIVIDER: number; //esp32 116 | abstract MEMORY_MAP: MemoryMapEntry[]; 117 | } 118 | -------------------------------------------------------------------------------- /src/targets/esp32c2.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { ESP32C3ROM } from "./esp32c3.js"; 3 | import { MemoryMapEntry } from "./rom.js"; 4 | 5 | export class ESP32C2ROM extends ESP32C3ROM { 6 | public CHIP_NAME = "ESP32-C2"; 7 | public IMAGE_CHIP_ID = 12; 8 | public EFUSE_BASE = 0x60008800; 9 | public MAC_EFUSE_REG = this.EFUSE_BASE + 0x040; 10 | public UART_CLKDIV_REG = 0x60000014; 11 | public UART_CLKDIV_MASK = 0xfffff; 12 | public UART_DATE_REG_ADDR = 0x6000007c; 13 | public XTAL_CLK_DIVIDER = 1; 14 | 15 | public FLASH_WRITE_SIZE = 0x400; 16 | public BOOTLOADER_FLASH_OFFSET = 0; 17 | 18 | public SPI_REG_BASE = 0x60002000; 19 | public SPI_USR_OFFS = 0x18; 20 | public SPI_USR1_OFFS = 0x1c; 21 | public SPI_USR2_OFFS = 0x20; 22 | public SPI_MOSI_DLEN_OFFS = 0x24; 23 | public SPI_MISO_DLEN_OFFS = 0x28; 24 | public SPI_W0_OFFS = 0x58; 25 | 26 | IROM_MAP_START = 0x42000000; 27 | IROM_MAP_END = 0x42400000; 28 | 29 | public MEMORY_MAP: MemoryMapEntry[] = [ 30 | [0x00000000, 0x00010000, "PADDING"], 31 | [0x3c000000, 0x3c400000, "DROM"], 32 | [0x3fca0000, 0x3fce0000, "DRAM"], 33 | [0x3fc88000, 0x3fd00000, "BYTE_ACCESSIBLE"], 34 | [0x3ff00000, 0x3ff50000, "DROM_MASK"], 35 | [0x40000000, 0x40090000, "IROM_MASK"], 36 | [0x42000000, 0x42400000, "IROM"], 37 | [0x4037c000, 0x403c0000, "IRAM"], 38 | ]; 39 | 40 | public async getPkgVersion(loader: ESPLoader): Promise { 41 | const numWord = 1; 42 | const block1Addr = this.EFUSE_BASE + 0x040; 43 | const addr = block1Addr + 4 * numWord; 44 | const word3 = await loader.readReg(addr); 45 | const pkgVersion = (word3 >> 22) & 0x07; 46 | return pkgVersion; 47 | } 48 | 49 | public async getChipRevision(loader: ESPLoader): Promise { 50 | const block1Addr = this.EFUSE_BASE + 0x040; 51 | const numWord = 1; 52 | const pos = 20; 53 | const addr = block1Addr + 4 * numWord; 54 | const ret = ((await loader.readReg(addr)) & (0x03 << pos)) >> pos; 55 | return ret; 56 | } 57 | 58 | public async getChipDescription(loader: ESPLoader) { 59 | let desc: string; 60 | const pkgVer = await this.getPkgVersion(loader); 61 | if (pkgVer === 0 || pkgVer === 1) { 62 | desc = "ESP32-C2"; 63 | } else { 64 | desc = "unknown ESP32-C2"; 65 | } 66 | const chip_rev = await this.getChipRevision(loader); 67 | desc += " (revision " + chip_rev + ")"; 68 | return desc; 69 | } 70 | 71 | public async getChipFeatures(loader: ESPLoader) { 72 | return ["Wi-Fi", "BLE"]; 73 | } 74 | 75 | public async getCrystalFreq(loader: ESPLoader) { 76 | const uartDiv = (await loader.readReg(this.UART_CLKDIV_REG)) & this.UART_CLKDIV_MASK; 77 | const etsXtal = (loader.transport.baudrate * uartDiv) / 1000000 / this.XTAL_CLK_DIVIDER; 78 | let normXtal; 79 | if (etsXtal > 33) { 80 | normXtal = 40; 81 | } else { 82 | normXtal = 26; 83 | } 84 | if (Math.abs(normXtal - etsXtal) > 1) { 85 | loader.info("WARNING: Unsupported crystal in use"); 86 | } 87 | return normXtal; 88 | } 89 | 90 | public async changeBaudRate(loader: ESPLoader) { 91 | const rom_with_26M_XTAL = await this.getCrystalFreq(loader); 92 | if (rom_with_26M_XTAL === 26) { 93 | loader.changeBaud(); 94 | } 95 | } 96 | 97 | public _d2h(d: number) { 98 | const h = (+d).toString(16); 99 | return h.length === 1 ? "0" + h : h; 100 | } 101 | 102 | public async readMac(loader: ESPLoader) { 103 | let mac0 = await loader.readReg(this.MAC_EFUSE_REG); 104 | mac0 = mac0 >>> 0; 105 | let mac1 = await loader.readReg(this.MAC_EFUSE_REG + 4); 106 | mac1 = (mac1 >>> 0) & 0x0000ffff; 107 | const mac = new Uint8Array(6); 108 | mac[0] = (mac1 >> 8) & 0xff; 109 | mac[1] = mac1 & 0xff; 110 | mac[2] = (mac0 >> 24) & 0xff; 111 | mac[3] = (mac0 >> 16) & 0xff; 112 | mac[4] = (mac0 >> 8) & 0xff; 113 | mac[5] = mac0 & 0xff; 114 | 115 | return ( 116 | this._d2h(mac[0]) + 117 | ":" + 118 | this._d2h(mac[1]) + 119 | ":" + 120 | this._d2h(mac[2]) + 121 | ":" + 122 | this._d2h(mac[3]) + 123 | ":" + 124 | this._d2h(mac[4]) + 125 | ":" + 126 | this._d2h(mac[5]) 127 | ); 128 | } 129 | 130 | public getEraseSize(offset: number, size: number) { 131 | return size; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/targets/esp32h2.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { ESP32C6ROM } from "./esp32c6.js"; 3 | import { MemoryMapEntry } from "./rom.js"; 4 | 5 | export class ESP32H2ROM extends ESP32C6ROM { 6 | public CHIP_NAME = "ESP32-H2"; 7 | public IMAGE_CHIP_ID = 16; 8 | public EFUSE_BASE = 0x600b0800; 9 | public EFUSE_BLOCK1_ADDR = this.EFUSE_BASE + 0x044; 10 | public MAC_EFUSE_REG = this.EFUSE_BASE + 0x044; 11 | public UART_CLKDIV_REG = 0x3ff40014; 12 | public UART_CLKDIV_MASK = 0xfffff; 13 | public UART_DATE_REG_ADDR = 0x6000007c; 14 | 15 | public FLASH_WRITE_SIZE = 0x400; 16 | public BOOTLOADER_FLASH_OFFSET = 0x0; 17 | 18 | public SPI_REG_BASE = 0x60002000; 19 | public SPI_USR_OFFS = 0x18; 20 | public SPI_USR1_OFFS = 0x1c; 21 | public SPI_USR2_OFFS = 0x20; 22 | public SPI_MOSI_DLEN_OFFS = 0x24; 23 | public SPI_MISO_DLEN_OFFS = 0x28; 24 | public SPI_W0_OFFS = 0x58; 25 | 26 | public USB_RAM_BLOCK = 0x800; 27 | public UARTDEV_BUF_NO_USB = 3; 28 | public UARTDEV_BUF_NO = 0x3fcef14c; 29 | 30 | IROM_MAP_START = 0x42000000; 31 | IROM_MAP_END = 0x42800000; 32 | 33 | public MEMORY_MAP: MemoryMapEntry[] = [ 34 | [0x00000000, 0x00010000, "PADDING"], 35 | [0x42000000, 0x43000000, "DROM"], 36 | [0x40800000, 0x40880000, "DRAM"], 37 | [0x40800000, 0x40880000, "BYTE_ACCESSIBLE"], 38 | [0x4004ac00, 0x40050000, "DROM_MASK"], 39 | [0x40000000, 0x4004ac00, "IROM_MASK"], 40 | [0x42000000, 0x43000000, "IROM"], 41 | [0x40800000, 0x40880000, "IRAM"], 42 | [0x50000000, 0x50004000, "RTC_IRAM"], 43 | [0x50000000, 0x50004000, "RTC_DRAM"], 44 | [0x600fe000, 0x60100000, "MEM_INTERNAL2"], 45 | ]; 46 | 47 | public async getPkgVersion(loader: ESPLoader): Promise { 48 | const numWord = 4; 49 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 0) & 0x07; 50 | } 51 | 52 | public async getMinorChipVersion(loader: ESPLoader): Promise { 53 | const numWord = 3; 54 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 18) & 0x07; 55 | } 56 | 57 | public async getMajorChipVersion(loader: ESPLoader): Promise { 58 | const numWord = 3; 59 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 21) & 0x03; 60 | } 61 | 62 | public async getChipDescription(loader: ESPLoader): Promise { 63 | const pkgVer = await this.getPkgVersion(loader); 64 | let desc: string; 65 | if (pkgVer === 0) { 66 | desc = "ESP32-H2"; 67 | } else { 68 | desc = "unknown ESP32-H2"; 69 | } 70 | const majorRev = await this.getMajorChipVersion(loader); 71 | const minorRev = await this.getMinorChipVersion(loader); 72 | return `${desc} (revision v${majorRev}.${minorRev})`; 73 | } 74 | 75 | public async getChipFeatures(loader: ESPLoader) { 76 | return ["BT 5 (LE)", "IEEE802.15.4", "Single Core", "96MHz"]; 77 | } 78 | 79 | public async getCrystalFreq(loader: ESPLoader) { 80 | // ESP32H2 XTAL is fixed to 32MHz 81 | return 32; 82 | } 83 | 84 | public _d2h(d: number) { 85 | const h = (+d).toString(16); 86 | return h.length === 1 ? "0" + h : h; 87 | } 88 | 89 | public async postConnect(loader: ESPLoader) { 90 | const bufNo = (await loader.readReg(this.UARTDEV_BUF_NO)) & 0xff; 91 | loader.debug("In _post_connect " + bufNo); 92 | if (bufNo == this.UARTDEV_BUF_NO_USB) { 93 | loader.ESP_RAM_BLOCK = this.USB_RAM_BLOCK; 94 | } 95 | } 96 | 97 | public async readMac(loader: ESPLoader) { 98 | let mac0 = await loader.readReg(this.MAC_EFUSE_REG); 99 | mac0 = mac0 >>> 0; 100 | let mac1 = await loader.readReg(this.MAC_EFUSE_REG + 4); 101 | mac1 = (mac1 >>> 0) & 0x0000ffff; 102 | const mac = new Uint8Array(6); 103 | mac[0] = (mac1 >> 8) & 0xff; 104 | mac[1] = mac1 & 0xff; 105 | mac[2] = (mac0 >> 24) & 0xff; 106 | mac[3] = (mac0 >> 16) & 0xff; 107 | mac[4] = (mac0 >> 8) & 0xff; 108 | mac[5] = mac0 & 0xff; 109 | 110 | return ( 111 | this._d2h(mac[0]) + 112 | ":" + 113 | this._d2h(mac[1]) + 114 | ":" + 115 | this._d2h(mac[2]) + 116 | ":" + 117 | this._d2h(mac[3]) + 118 | ":" + 119 | this._d2h(mac[4]) + 120 | ":" + 121 | this._d2h(mac[5]) 122 | ); 123 | } 124 | 125 | public getEraseSize(offset: number, size: number) { 126 | return size; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/targets/esp8266.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { MemoryMapEntry, ROM } from "./rom.js"; 3 | 4 | export class ESP8266ROM extends ROM { 5 | public CHIP_NAME = "ESP8266"; 6 | public CHIP_DETECT_MAGIC_VALUE = [0xfff0c101]; 7 | public EFUSE_RD_REG_BASE = 0x3ff00050; 8 | public UART_CLKDIV_REG = 0x60000014; 9 | public UART_CLKDIV_MASK = 0xfffff; 10 | public XTAL_CLK_DIVIDER = 2; 11 | 12 | public FLASH_WRITE_SIZE = 0x4000; 13 | 14 | // NOT IMPLEMENTED, SETTING EMPTY VALUE 15 | public BOOTLOADER_FLASH_OFFSET = 0; 16 | public UART_DATE_REG_ADDR = 0; 17 | 18 | static IROM_MAP_START = 0x40200000; 19 | static IROM_MAP_END = 0x40300000; 20 | 21 | public FLASH_SIZES = { 22 | "512KB": 0x00, 23 | "256KB": 0x10, 24 | "1MB": 0x20, 25 | "2MB": 0x30, 26 | "4MB": 0x40, 27 | "2MB-c1": 0x50, 28 | "4MB-c1": 0x60, 29 | "8MB": 0x80, 30 | "16MB": 0x90, 31 | }; 32 | 33 | public FLASH_FREQUENCY = { 34 | "80m": 0xf, 35 | "40m": 0x0, 36 | "26m": 0x1, 37 | "20m": 0x2, 38 | }; 39 | 40 | public MEMORY_MAP: MemoryMapEntry[] = [ 41 | [0x3ff00000, 0x3ff00010, "DPORT"], 42 | [0x3ffe8000, 0x40000000, "DRAM"], 43 | [0x40100000, 0x40108000, "IRAM"], 44 | [0x40201010, 0x402e1010, "IROM"], 45 | ]; 46 | 47 | public SPI_REG_BASE = 0x60000200; 48 | public SPI_USR_OFFS = 0x1c; 49 | public SPI_USR1_OFFS = 0x20; 50 | public SPI_USR2_OFFS = 0x24; 51 | public SPI_MOSI_DLEN_OFFS = 0; // not in esp8266 52 | public SPI_MISO_DLEN_OFFS = 0; // not in esp8266 53 | public SPI_W0_OFFS = 0x40; 54 | 55 | public async readEfuse(loader: ESPLoader, offset: number): Promise { 56 | const addr = this.EFUSE_RD_REG_BASE + 4 * offset; 57 | loader.debug("Read efuse " + addr); 58 | return await loader.readReg(addr); 59 | } 60 | 61 | public async getChipDescription(loader: ESPLoader) { 62 | const efuse3 = await this.readEfuse(loader, 2); 63 | const efuse0 = await this.readEfuse(loader, 0); 64 | 65 | const is8285 = ((efuse0 & (1 << 4)) | (efuse3 & (1 << 16))) != 0; // One or the other efuse bit is set for ESP8285 66 | return is8285 ? "ESP8285" : "ESP8266EX"; 67 | } 68 | 69 | public getChipFeatures = async (loader: ESPLoader) => { 70 | const features = ["WiFi"]; 71 | if ((await this.getChipDescription(loader)) == "ESP8285") features.push("Embedded Flash"); 72 | return features; 73 | }; 74 | 75 | public async getCrystalFreq(loader: ESPLoader) { 76 | const uartDiv = (await loader.readReg(this.UART_CLKDIV_REG)) & this.UART_CLKDIV_MASK; 77 | const etsXtal = (loader.transport.baudrate * uartDiv) / 1000000 / this.XTAL_CLK_DIVIDER; 78 | let normXtal; 79 | if (etsXtal > 33) { 80 | normXtal = 40; 81 | } else { 82 | normXtal = 26; 83 | } 84 | if (Math.abs(normXtal - etsXtal) > 1) { 85 | loader.info( 86 | "WARNING: Detected crystal freq " + 87 | etsXtal + 88 | "MHz is quite different to normalized freq " + 89 | normXtal + 90 | "MHz. Unsupported crystal in use?", 91 | ); 92 | } 93 | return normXtal; 94 | } 95 | 96 | public _d2h(d: number) { 97 | const h = (+d).toString(16); 98 | return h.length === 1 ? "0" + h : h; 99 | } 100 | 101 | public async readMac(loader: ESPLoader) { 102 | let mac0 = await this.readEfuse(loader, 0); 103 | mac0 = mac0 >>> 0; 104 | let mac1 = await this.readEfuse(loader, 1); 105 | mac1 = mac1 >>> 0; 106 | let mac3 = await this.readEfuse(loader, 3); 107 | mac3 = mac3 >>> 0; 108 | const mac = new Uint8Array(6); 109 | 110 | if (mac3 != 0) { 111 | mac[0] = (mac3 >> 16) & 0xff; 112 | mac[1] = (mac3 >> 8) & 0xff; 113 | mac[2] = mac3 & 0xff; 114 | } else if (((mac1 >> 16) & 0xff) == 0) { 115 | mac[0] = 0x18; 116 | mac[1] = 0xfe; 117 | mac[2] = 0x34; 118 | } else if (((mac1 >> 16) & 0xff) == 1) { 119 | mac[0] = 0xac; 120 | mac[1] = 0xd0; 121 | mac[2] = 0x74; 122 | } else { 123 | loader.error("Unknown OUI"); 124 | } 125 | 126 | mac[3] = (mac1 >> 8) & 0xff; 127 | mac[4] = mac1 & 0xff; 128 | mac[5] = (mac0 >> 24) & 0xff; 129 | 130 | return ( 131 | this._d2h(mac[0]) + 132 | ":" + 133 | this._d2h(mac[1]) + 134 | ":" + 135 | this._d2h(mac[2]) + 136 | ":" + 137 | this._d2h(mac[3]) + 138 | ":" + 139 | this._d2h(mac[4]) + 140 | ":" + 141 | this._d2h(mac[5]) 142 | ); 143 | } 144 | 145 | public getEraseSize(offset: number, size: number) { 146 | return size; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32c2.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1077413304, 3 | "text": "ARG3BwBgTsaDqYcASsg3Sco/JspSxAbOIsy3BABgfVoTCQkAwEwTdPQ/DeDyQGJEI6g0AUJJ0kSySSJKBWGCgIhAgycJABN19Q+Cl30U4xlE/8m/EwcADJRBqodjGOUAhUeFxiOgBQB5VYKABUdjh+YACUZjjcYAfVWCgEIFEwewDUGFY5XnAolHnMH1t5MGwA1jFtUAmMETBQAMgoCTBtANfVVjldcAmMETBbANgoC3dcs/QRGThQW6BsZhP2NFBQa3d8s/k4eHsQOnBwgD1kcIE3X1D5MGFgDCBsGCI5LXCDKXIwCnAAPXRwiRZ5OHBwRjHvcCN/fKPxMHh7GhZ7qXA6YHCLc2yz+3d8s/k4eHsZOGhrVjH+YAI6bHCCOg1wgjkgcIIaD5V+MG9fyyQEEBgoAjptcII6DnCN23NycAYHxLnYv1/zc3AGB8S52L9f+CgEERBsbdN7cnAGAjpgcCNwcACJjDmEN9/8hXskATRfX/BYlBAYKAQREGxtk/fd03BwBAtycAYJjDNycAYBxD/f+yQEEBgoBBESLEN8TKP5MHxABKwAOpBwEGxibCYwoJBEU3OcW9RxMExACBRGPWJwEERL2Ik7QUAH03hT8cRDcGgAATl8cAmeA3BgABt/b/AHWPtyYAYNjCkMKYQn3/QUeR4AVHMwnpQLqXIygkARzEskAiRJJEAklBAYKAQREGxhMHAAxjEOUCEwWwDZcAyP/ngIDjEwXADbJAQQEXA8j/ZwCD4hMHsA3jGOX+lwDI/+eAgOETBdANxbdBESLEJsIGxiqEswS1AGMXlACyQCJEkkRBAYKAA0UEAAUERTfttxMFAAwXA8j/ZwAD3nVxJsPO3v10hWn9cpOEhPqThwkHIsVKwdLc1tqmlwbHFpGzhCcAKokmhS6ElzDI/+eAgJOThwkHBWqKl7OKR0Ep5AVnfXUTBIX5kwcHB6KXM4QnABMFhfqTBwcHqpeihTOFJwCXMMj/54CAkCKFwUW5PwFFhWIWkbpAKkSaRApJ9llmWtZaSWGCgKKJY3OKAIVpTobWhUqFlwDI/+eAQOITdfUPAe1OhtaFJoWXMMj/54DAi06ZMwQ0QVm3EwUwBlW/cXH9ck7PUs1Wy17HBtci1SbTStFayWLFZsNqwe7eqokWkRMFAAIuirKKtosCwpcAyP/ngEBIhWdj7FcRhWR9dBMEhPqThwQHopczhCcAIoWXMMj/54AghX17Eww7+ZMMi/kThwQHk4cEB2KX5pcBSTMMJwCzjCcAEk1je00JY3GpA3mgfTWmhYgYSTVdNSaGjBgihZcwyP/ngCCBppkmmWN1SQOzB6lBY/F3A7MEKkFj85oA1oQmhowYToWXAMj/54Dg0xN19Q9V3QLEgUR5XY1NowEBAGKFlwDI/+eAYMR9+QNFMQDmhS0xY04FAOPinf6FZ5OHBweml4qX2pcjiqf4hQT5t+MWpf2RR+OG9PYFZ311kwcHBxMEhfmilzOEJwATBYX6kwcHB6qXM4UnAKKFlyDI/+eAgHflOyKFwUXxM8U7EwUAApcAyP/ngOA2hWIWkbpQKlSaVApZ+klqStpKSku6SypMmkwKTfZdTWGCgAERBs4izFExNwTOP2wAEwVE/5cAyP/ngKDKqocFRZXnskeT9wcgPsZ5OTcnAGAcR7cGQAATBUT/1Y8cx7JFlwDI/+eAIMgzNaAA8kBiRAVhgoBBEbfHyj8GxpOHxwAFRyOA5wAT18UAmMcFZ30XzMPIx/mNOpWqlbGBjMsjqgcAQTcZwRMFUAyyQEEBgoABESLMN8TKP5MHxAAmysRHTsYGzkrIqokTBMQAY/OVAK6EqcADKUQAJpkTWckAHEhjVfAAHERjXvkC4T593UhAJobOhZcAyP/ngCC7E3X1DwHFkwdADFzIXECml1zAXESFj1zE8kBiRNJEQkmySQVhgoDdNm2/t1dBSRlxk4f3hAFFPs6G3qLcptrK2M7W0tTW0trQ3s7izObK6sjuxpcAyP/ngICtt0fKPzd3yz+ThwcAEweHumPg5xSlOZFFaAixMYU5t/fKP5OHh7EhZz6XIyD3CLcFOEC3BzhAAUaThwcLk4UFADdJyj8VRSMg+QCXAMj/54DgGzcHAGBcRxMFAAK3xMo/k+cXEFzHlwDI/+eAoBq3RwBgiF+BRbd5yz9xiWEVEzUVAJcAyP/ngOCwwWf9FxMHABCFZkFmtwUAAQFFk4TEALdKyj8NapcAyP/ngOCrk4mJsRMJCQATi8oAJpqDp8kI9d+Dq8kIhUcjpgkIIwLxAoPHGwAJRyMT4QKjAvECAtRNR2OL5wZRR2OJ5wYpR2Of5wCDxzsAA8crAKIH2Y8RR2OW5wCDp4sAnEM+1EE2oUVIEJE+g8c7AAPHKwCiB9mPEWdBB2N+9wITBbANlwDI/+eAQJQTBcANlwDI/+eAgJMTBeAOlwDI/+eAwJKBNr23I6AHAJEHbb3JRyMT8QJ9twPHGwDRRmPn5gKFRmPm5gABTBME8A+dqHkXE3f3D8lG4+jm/rd2yz8KB5OGxro2lxhDAoeTBgcDk/b2DxFG42nW/BMH9wITd/cPjUZj7uYIt3bLPwoHk4aGvzaXGEMChxMHQAJjmucQAtQdRAFFlwDI/+eAIIoBRYE8TTxFPKFFSBB9FEk0ffABTAFEE3X0DyU8E3X8Dw08UTzjEQTsg8cbAElHY2X3MAlH43n36vUXk/f3Dz1H42P36jd3yz+KBxMHh8C6l5xDgocFRJ3rcBCBRQFFlwDI/+eAQIkd4dFFaBAVNAFEMagFRIHvlwDI/+eAwI0zNKAAKaAhR2OF5wAFRAFMYbcDrIsAA6TLALNnjADSB/X3mTll9cFsIpz9HH19MwWMQF3cs3eVAZXjwWwzBYxAY+aMAv18MwWMQF3QMYGXAMj/54Bgil35ZpT1tzGBlwDI/+eAYIld8WqU0bdBgZcAyP/ngKCIWfkzBJRBwbchR+OK5/ABTBMEAAw5t0FHzb9BRwVE453n9oOlywADpYsAVTK5v0FHBUTjk+f2A6cLAZFnY+jnHoOlSwEDpYsAMTGBt0FHBUTjlOf0g6cLARFnY2n3HAOnywCDpUsBA6WLADOE5wLdNiOsBAAjJIqwCb8DxwQAYwMHFAOniwDBFxMEAAxjE/cAwEgBR5MG8A5jRvcCg8dbAAPHSwABTKIH2Y8Dx2sAQgddj4PHewDiB9mP44T25hMEEAyFtTOG6wADRoYBBQexjuG3g8cEAP3H3ERjnQcUwEgjgAQAVb1hR2OW5wKDp8sBA6eLAYOmSwEDpgsBg6XLAAOliwCX8Mf/54BgeSqMMzSgAAG9AUwFRCm1EUcFROOd5+a3lwBgtENld30XBWb5jtGOA6WLALTDtEeBRfmO0Y60x/RD+Y7RjvTD1F91j1GP2N+X8Mf/54BAdwW1E/f3AOMXB+qT3EcAE4SLAAFMfV3jd5zbSESX8Mf/54DAYRhEVEAQQPmOYwenARxCE0f3/32P2Y4UwgUMQQTZvxFHtbVBRwVE45rn3oOniwADp0sBIyT5ACMi6QDJs4MlSQDBF5Hlic8BTBMEYAyhuwMniQBjZvcGE/c3AOMbB+IDKIkAAUYBRzMF6ECzhuUAY2n3AOMHBtIjJKkAIyLZAA2zM4brABBOEQeQwgVG6b8hRwVE45Tn2AMkiQAZwBMEgAwjJAkAIyIJADM0gAC9swFMEwQgDMW5AUwTBIAM5bEBTBMEkAzFsRMHIA1jg+cMEwdADeOR57oDxDsAg8crACIEXYyX8Mf/54BgXwOsxABBFGNzhAEijOMPDLbAQGKUMYCcSGNV8ACcRGNa9Arv8I/hdd3IQGKGk4WLAZfwx//ngGBbAcWTB0AM3MjcQOKX3MDcRLOHh0HcxJfwx//ngEBaFb4JZRMFBXEDrMsAA6SLAJfwx//ngEBMtwcAYNhLtwYAAcEWk1dHARIHdY+9i9mPs4eHAwFFs9WHApfwx//ngOBMEwWAPpfwx//ngOBI3bSDpksBA6YLAYOlywADpYsA7/Av98G8g8U7AIPHKwAThYsBogXdjcEVqTptvO/w79qBtwPEOwCDxysAE4yLASIEXYzcREEUxeORR4VLY/6HCJMHkAzcyHm0A6cNACLQBUizh+xAPtaDJ4qwY3P0AA1IQsY6xO/wb9YiRzJIN8XKP+KFfBCThsoAEBATBUUCl/DH/+eA4Ek398o/kwjHAIJXA6eIsIOlDQAdjB2PPpyyVyOk6LCqi76VI6C9AJOHygCdjQHFoWdjlvUAWoVdOCOgbQEJxNxEmcPjQHD5Y98LAJMHcAyFv4VLt33LP7fMyj+TjY26k4zMAOm/45ULntxE44IHnpMHgAyxt4OniwDjmwecAUWX8Mf/54DAOQllEwUFcZfwx//ngCA2l/DH/+eA4DlNugOkywDjBgSaAUWX8Mf/54AgNxMFgD6X8Mf/54CgMwKUQbr2UGZU1lRGWbZZJlqWWgZb9ktmTNZMRk22TQlhgoA=", 4 | "text_start": 1077411840, 5 | "data": "DEDKP+AIOEAsCThAhAk4QFIKOEC+CjhAbAo4QKgHOEAOCjhATgo4QJgJOEBYBzhAzAk4QFgHOEC6CDhA/gg4QCwJOECECThAzAg4QBIIOEBCCDhAyAg4QBYNOEAsCThA1gs4QMoMOECkBjhA9Aw4QKQGOECkBjhApAY4QKQGOECkBjhApAY4QKQGOECkBjhAcgs4QKQGOEDyCzhAygw4QA==", 6 | "data_start": 1070295976, 7 | "bss_start": 1070219264 8 | } -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1074521580, 3 | "text": "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", 4 | "text_start": 1074520064, 5 | "data": "DMD8P+znC0B/6AtAZ+0LQAbpC0Cf6AtABukLQGXpC0CC6gtA9OoLQJ3qC0CV5wtAGuoLQHTqC0CI6QtAGOsLQLDpC0AY6wtAbegLQMroC0AG6QtAZekLQIXoC0DI6wtAKe0LQLjmC0BL7QtAuOYLQLjmC0C45gtAuOYLQLjmC0C45gtAuOYLQLjmC0Bv6wtAuOYLQEnsC0Ap7QtA", 6 | "data_start": 1073605544, 7 | "bss_start": 1073528832 8 | } -------------------------------------------------------------------------------- /src/image/esp8266.ts: -------------------------------------------------------------------------------- 1 | import { ESP8266ROM } from "../targets/esp8266"; 2 | import { bstrToUi8 } from "../util"; 3 | import { BaseFirmwareImage, ESP_IMAGE_MAGIC } from "./base"; 4 | 5 | export class ESP8266ROMFirmwareImage extends BaseFirmwareImage { 6 | version = 1; 7 | 8 | ROM_LOADER: ESP8266ROM; 9 | 10 | constructor(rom: ESP8266ROM, loadFile: string | null = null) { 11 | super(rom); 12 | this.ROM_LOADER = rom; 13 | this.flashMode = 0; 14 | this.flashSizeFreq = 0; 15 | 16 | if (loadFile !== null) { 17 | this.loadFromFile(loadFile); 18 | } 19 | } 20 | 21 | loadFromFile(file: string): void { 22 | const binaryData = bstrToUi8(file); 23 | let offset = 0; 24 | 25 | const segments = this.loadCommonHeader(binaryData, offset, ESP_IMAGE_MAGIC); 26 | offset += 8; 27 | 28 | for (let i = 0; i < segments; i++) { 29 | const segment = this.loadSegment(binaryData, offset); 30 | offset += 8 + segment.data.length; 31 | } 32 | this.checksum = this.readChecksum(binaryData, offset); 33 | this.verify(); 34 | } 35 | 36 | defaultOutputName(inputFile: string): string { 37 | return inputFile + "-"; 38 | } 39 | } 40 | 41 | export class ESP8266V2FirmwareImage extends BaseFirmwareImage { 42 | // First byte of the "v2" application image 43 | static readonly IMAGE_V2_MAGIC = 0xea; 44 | // First 'segment' value in a "v2" application image 45 | static readonly IMAGE_V2_SEGMENT = 4; 46 | 47 | version = 2; 48 | 49 | ROM_LOADER: ESP8266ROM; 50 | 51 | constructor(rom: ESP8266ROM, loadFile: string | null = null) { 52 | super(rom); 53 | this.ROM_LOADER = rom; 54 | this.flashMode = 0; 55 | this.flashSizeFreq = 0; 56 | 57 | if (loadFile !== null) { 58 | this.loadFromFile(loadFile); 59 | } 60 | } 61 | 62 | async loadFromFile(fileStr: string): Promise { 63 | const binaryData = bstrToUi8(fileStr); 64 | let offset = 0; 65 | 66 | // Load first header 67 | const segments = this.loadCommonHeader(binaryData, offset, ESP8266V2FirmwareImage.IMAGE_V2_MAGIC); 68 | offset += 8; 69 | 70 | if (segments !== ESP8266V2FirmwareImage.IMAGE_V2_SEGMENT) { 71 | console.warn(`Warning: V2 header has unexpected "segment" count ${segments} (usually 4)`); 72 | } 73 | 74 | // Save first header values 75 | const firstFlashMode = this.flashMode; 76 | const firstFlashSizeFreq = this.flashSizeFreq; 77 | const firstEntrypoint = this.entrypoint; 78 | 79 | // irom segment comes before the second header 80 | const iromSegment = this.loadSegment(binaryData, offset, true); 81 | // for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 82 | iromSegment.addr = 0; 83 | iromSegment.includeInChecksum = false; 84 | offset += 8 + iromSegment.data.length; 85 | 86 | // Load the second header 87 | const secondSegments = this.loadCommonHeader(binaryData, offset, ESP_IMAGE_MAGIC); 88 | offset += 8; 89 | 90 | // Compare headers 91 | if (firstFlashMode !== this.flashMode) { 92 | console.warn( 93 | `WARNING: Flash mode value in first header (0x${firstFlashMode.toString( 94 | 16, 95 | )}) disagrees with second (0x${this.flashMode.toString(16)}). Using second value.`, 96 | ); 97 | } 98 | if (firstFlashSizeFreq !== this.flashSizeFreq) { 99 | console.warn( 100 | `WARNING: Flash size/freq value in first header (0x${firstFlashSizeFreq.toString( 101 | 16, 102 | )}) disagrees with second (0x${this.flashSizeFreq.toString(16)}). Using second value.`, 103 | ); 104 | } 105 | if (firstEntrypoint !== this.entrypoint) { 106 | console.warn( 107 | `WARNING: Entrypoint address in first header (0x${firstEntrypoint.toString( 108 | 16, 109 | )}) disagrees with second header (0x${this.entrypoint.toString(16)}). Using second value.`, 110 | ); 111 | } 112 | 113 | // Load all the usual segments 114 | for (let i = 0; i < secondSegments; i++) { 115 | const segment = this.loadSegment(binaryData, offset); 116 | offset += 8 + segment.data.length; 117 | } 118 | 119 | this.checksum = this.readChecksum(binaryData, offset); 120 | this.verify(); 121 | } 122 | 123 | defaultOutputName(inputFile: string): string { 124 | const iromSegment = this.getIromSegment(); 125 | let iromOffs = 0; 126 | 127 | if (iromSegment !== null) { 128 | iromOffs = iromSegment.addr - ESP8266ROM.IROM_MAP_START; 129 | } 130 | 131 | // Get the base name without extension 132 | const baseName = inputFile.replace(/\.[^/.]+$/, ""); 133 | 134 | const FLASH_SECTOR_SIZE = 0x1000; 135 | 136 | // Calculate the offset aligned to flash sector size 137 | const alignedOffset = iromOffs & ~(FLASH_SECTOR_SIZE - 1); 138 | 139 | return `${baseName}-0x${alignedOffset.toString(16).padStart(5, "0")}.bin`; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32p4.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1341195918, 3 | "text": "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", 4 | "text_start": 1341194240, 5 | "data": "EAD1TwYK8U9WCvFPrgrxT4QL8U/wC/FPngvxT9QI8U9AC/FPgAvxT8IK8U+ECPFP9grxT4QI8U/gCfFPJgrxT1YK8U+uCvFP8gnxTzgJ8U9oCfFP7gnxT0AO8U9WCvFPCA3xTwAO8U/EB/FPJA7xT8QH8U/EB/FPxAfxT8QH8U/EB/FPxAfxT8QH8U/EB/FPpAzxT8QH8U8mDfFPAA7xTw==", 6 | "data_start": 1341533100, 7 | "bss_start": 1341456384 8 | } -------------------------------------------------------------------------------- /src/targets/esp32c61.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader"; 2 | import { ESP32C6ROM } from "./esp32c6"; 3 | import { MemoryMapEntry } from "./rom"; 4 | 5 | export class ESP32C61ROM extends ESP32C6ROM { 6 | public CHIP_NAME = "ESP32-C61"; 7 | public IMAGE_CHIP_ID = 20; 8 | 9 | public CHIP_DETECT_MAGIC_VALUE = [0x33f0206f, 0x2421606f]; 10 | 11 | public UART_DATE_REG_ADDR = 0x60000000 + 0x7c; 12 | 13 | public EFUSE_BASE = 0x600b4800; 14 | public EFUSE_BLOCK1_ADDR = this.EFUSE_BASE + 0x044; 15 | public MAC_EFUSE_REG = this.EFUSE_BASE + 0x044; 16 | 17 | public EFUSE_RD_REG_BASE = this.EFUSE_BASE + 0x030; // BLOCK0 read base address 18 | 19 | public EFUSE_PURPOSE_KEY0_REG = this.EFUSE_BASE + 0x34; 20 | public EFUSE_PURPOSE_KEY0_SHIFT = 0; 21 | public EFUSE_PURPOSE_KEY1_REG = this.EFUSE_BASE + 0x34; 22 | public EFUSE_PURPOSE_KEY1_SHIFT = 4; 23 | public EFUSE_PURPOSE_KEY2_REG = this.EFUSE_BASE + 0x34; 24 | public EFUSE_PURPOSE_KEY2_SHIFT = 8; 25 | public EFUSE_PURPOSE_KEY3_REG = this.EFUSE_BASE + 0x34; 26 | public EFUSE_PURPOSE_KEY3_SHIFT = 12; 27 | public EFUSE_PURPOSE_KEY4_REG = this.EFUSE_BASE + 0x34; 28 | public EFUSE_PURPOSE_KEY4_SHIFT = 16; 29 | public EFUSE_PURPOSE_KEY5_REG = this.EFUSE_BASE + 0x34; 30 | public EFUSE_PURPOSE_KEY5_SHIFT = 20; 31 | 32 | public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_RD_REG_BASE; 33 | public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20; 34 | 35 | public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x030; 36 | public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 23; 37 | 38 | public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x034; 39 | public EFUSE_SECURE_BOOT_EN_MASK = 1 << 26; 40 | 41 | public FLASH_FREQUENCY = { 42 | "80m": 0xf, 43 | "40m": 0x0, 44 | "20m": 0x2, 45 | }; 46 | 47 | IROM_MAP_START = 0x42000000; 48 | IROM_MAP_END = 0x42800000; 49 | 50 | public MEMORY_MAP: MemoryMapEntry[] = [ 51 | [0x00000000, 0x00010000, "PADDING"], 52 | [0x41800000, 0x42000000, "DROM"], 53 | [0x40800000, 0x40860000, "DRAM"], 54 | [0x40800000, 0x40860000, "BYTE_ACCESSIBLE"], 55 | [0x4004ac00, 0x40050000, "DROM_MASK"], 56 | [0x40000000, 0x4004ac00, "IROM_MASK"], 57 | [0x41000000, 0x41800000, "IROM"], 58 | [0x40800000, 0x40860000, "IRAM"], 59 | [0x50000000, 0x50004000, "RTC_IRAM"], 60 | [0x50000000, 0x50004000, "RTC_DRAM"], 61 | [0x600fe000, 0x60100000, "MEM_INTERNAL2"], 62 | ]; 63 | 64 | public UF2_FAMILY_ID = 0x77d850c4; 65 | 66 | public EFUSE_MAX_KEY = 5; 67 | public KEY_PURPOSES = { 68 | 0: "USER/EMPTY", 69 | 1: "ECDSA_KEY", 70 | 2: "XTS_AES_256_KEY_1", 71 | 3: "XTS_AES_256_KEY_2", 72 | 4: "XTS_AES_128_KEY", 73 | 5: "HMAC_DOWN_ALL", 74 | 6: "HMAC_DOWN_JTAG", 75 | 7: "HMAC_DOWN_DIGITAL_SIGNATURE", 76 | 8: "HMAC_UP", 77 | 9: "SECURE_BOOT_DIGEST0", 78 | 10: "SECURE_BOOT_DIGEST1", 79 | 11: "SECURE_BOOT_DIGEST2", 80 | 12: "KM_INIT_KEY", 81 | 13: "XTS_AES_256_KEY_1_PSRAM", 82 | 14: "XTS_AES_256_KEY_2_PSRAM", 83 | 15: "XTS_AES_128_KEY_PSRAM", 84 | }; 85 | 86 | public async getPkgVersion(loader: ESPLoader): Promise { 87 | const numWord = 2; 88 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 26) & 0x07; 89 | } 90 | 91 | public async getMinorChipVersion(loader: ESPLoader): Promise { 92 | const numWord = 2; 93 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 0) & 0x0f; 94 | } 95 | 96 | public async getMajorChipVersion(loader: ESPLoader): Promise { 97 | const numWord = 2; 98 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 4) & 0x03; 99 | } 100 | 101 | public async getChipDescription(loader: ESPLoader): Promise { 102 | const pkgVer = await this.getPkgVersion(loader); 103 | let desc: string; 104 | if (pkgVer === 0) { 105 | desc = "ESP32-C61"; 106 | } else { 107 | desc = "unknown ESP32-C61"; 108 | } 109 | const majorRev = await this.getMajorChipVersion(loader); 110 | const minorRev = await this.getMinorChipVersion(loader); 111 | return `${desc} (revision v${majorRev}.${minorRev})`; 112 | } 113 | 114 | public async getChipFeatures(loader: ESPLoader): Promise { 115 | return ["WiFi 6", "BT 5"]; 116 | } 117 | 118 | public async readMac(loader: ESPLoader) { 119 | let mac0 = await loader.readReg(this.MAC_EFUSE_REG); 120 | mac0 = mac0 >>> 0; 121 | let mac1 = await loader.readReg(this.MAC_EFUSE_REG + 4); 122 | mac1 = (mac1 >>> 0) & 0x0000ffff; 123 | const mac = new Uint8Array(6); 124 | mac[0] = (mac1 >> 8) & 0xff; 125 | mac[1] = mac1 & 0xff; 126 | mac[2] = (mac0 >> 24) & 0xff; 127 | mac[3] = (mac0 >> 16) & 0xff; 128 | mac[4] = (mac0 >> 8) & 0xff; 129 | mac[5] = mac0 & 0xff; 130 | 131 | return ( 132 | this._d2h(mac[0]) + 133 | ":" + 134 | this._d2h(mac[1]) + 135 | ":" + 136 | this._d2h(mac[2]) + 137 | ":" + 138 | this._d2h(mac[3]) + 139 | ":" + 140 | this._d2h(mac[4]) + 141 | ":" + 142 | this._d2h(mac[5]) 143 | ); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32c61.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1082132164, 3 | "text": "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", 4 | "text_start": 1082130432, 5 | "data": "FECAQHQKgEDECoBAHAuAQOoLgEBWDIBABAyAQEAJgECmC4BA5guAQDALgEDwCIBAZAuAQPAIgEBOCoBAlAqAQMQKgEAcC4BAYAqAQKQJgEDUCYBAXAqAQK4OgEDECoBAbg2AQGYOgEAwCIBAjg6AQDAIgEAwCIBAMAiAQDAIgEAwCIBAMAiAQDAIgEAwCIBACg2AQDAIgECMDYBAZg6AQA==", 6 | "data_start": 1082223536, 7 | "bss_start": 1082146816 8 | } -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32c6.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1082132164, 3 | "text": "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", 4 | "text_start": 1082130432, 5 | "data": "FACEQHIKgEDCCoBAGguAQOgLgEBUDIBAAgyAQD4JgECkC4BA5AuAQC4LgEDuCIBAYguAQO4IgEBMCoBAkgqAQMIKgEAaC4BAXgqAQKIJgEDSCYBAWgqAQKwOgEDCCoBAbA2AQGQOgEAuCIBAjA6AQC4IgEAuCIBALgiAQC4IgEAuCIBALgiAQC4IgEAuCIBACA2AQC4IgECKDYBAZA6AQA==", 6 | "data_start": 1082469296, 7 | "bss_start": 1082392576 8 | } -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32h2.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1082132164, 3 | "text": "QREixCbCBsa39wBgEUc3BINA2Mu39ABgEwQEANxAkYuR57JAIkSSREEBgoCIQBxAE3X1D4KX3bcBEbcHAGBOxoOphwBKyDcJg0AmylLEBs4izLcEAGB9WhMJCQDATBN09A8N4PJAYkQjqDQBQknSRLJJIkoFYYKAiECDJwkAE3X1D4KXfRTjGUT/yb8TBwAMlEGqh2MY5QCFR4XGI6AFAHlVgoAFR2OH5gAJRmONxgB9VYKAQgUTB7ANQYVjlecCiUecwfW3kwbADWMW1QCYwRMFAAyCgJMG0A19VWOV1wCYwRMFsA2CgLc1hEBBEZOFhboGxmE/Y0UFBrc3hECThweyA6cHCAPWRwgTdfUPkwYWAMIGwYIjktcIMpcjAKcAA9dHCJFnk4cHBGMe9wI3t4NAEwcHsqFnupcDpgcIt/aDQLc3hECThweyk4YGtmMf5gAjpscII6DXCCOSBwghoPlX4wb1/LJAQQGCgCOm1wgjoOcI3bc3NwBgfEudi/X/NycAYHxLnYv1/4KAQREGxt03tzcAYCOmBwI3BwAImMOYQ33/yFeyQBNF9f8FiUEBgoBBEQbG2T993TcHAEC3NwBgmMM3NwBgHEP9/7JAQQGCgEERIsQ3hINAkwdEAUrAA6kHAQbGJsJjCgkERTc5xb1HEwREAYFEY9YnAQREvYiTtBQAfTeFPxxENwaAABOXxwCZ4DcGAAG39v8AdY+3NgBg2MKQwphCff9BR5HgBUczCelAupcjKCQBHMSyQCJEkkQCSUEBgoABEQbOIswlNzcEhUBsABMFBP+XAID/54Ag8qqHBUWV57JHk/cHID7GiTc3NwBgHEe3BkAAEwUE/9WPHMeyRZcAgP/ngKDvMzWgAPJAYkQFYYKAQRG3h4NABsaTh0cBBUcjgOcAE9fFAJjHBWd9F8zDyMf5jTqVqpWxgYzLI6oHAEE3GcETBVAMskBBAYKAAREizDeEg0CTB0QBJsrER07GBs5KyKqJEwREAWPzlQCuhKnAAylEACaZE1nJABxIY1XwABxEY175ArU9fd1IQCaGzoWXAID/54Cg4hN19Q8BxZMHQAxcyFxAppdcwFxEhY9cxPJAYkTSREJJskkFYYKAaTVtv0ERBsaXAID/54BA1gNFhQGyQHUVEzUVAEEBgoBBEQbGxTcNxbcHg0CThwcA1EOZzjdnCWATB8cQHEM3Bv3/fRbxjzcGAwDxjtWPHMOyQEEBgoBBEQbGbTcRwQ1FskBBARcDgP9nAIPMQREGxibCIsSqhJcAgP/ngODJWTcNyTcHg0CTBgcAg9eGABMEBwCFB8IHwYMjlPYAkwYADGOG1AATB+ADY3X3AG03IxQEALJAIkSSREEBgoBBEQbGEwcADGMa5QATBbANRTcTBcANskBBAVm/EwewDeMb5f5xNxMF0A31t0ERIsQmwgbGKoSzBLUAYxeUALJAIkSSREEBgoADRQQABQRNP+23NXEmy07H/XKFaf10Is1KyVLFVsMGz5OEhPoWkZOHCQemlxgIs4TnACqJJoUuhJcAgP/ngEApk4cJBxgIBWq6l7OKR0Ex5AVnfXWTBYX6kwcHBxMFhfkUCKqXM4XXAJMHBweul7OF1wAqxpcAgP/ngAAmMkXBRZU3AUWFYhaR+kBqRNpESkm6SSpKmkoNYYKAooljc4oAhWlOhtaFSoWXAID/54BAxRN19Q8B7U6G1oUmhZcAgP/ngEAhTpkzBDRBUbcTBTAGVb8TBQAMSb0xcf1yBWdO11LVVtNezwbfIt0m20rZWtFizWbLaslux/13FpETBwcHPpccCLqXPsYjqgf4qokuirKKtov1M5MHAAIZwbcHAgA+hZcAgP/ngOAZhWdj5VcTBWR9eRMJifqTBwQHypcYCDOJ5wBKhZcAgP/ngGAYfXsTDDv5kwyL+RMHBAeTBwQHFAhil+aXgUQzDNcAs4zXAFJNY3xNCWPxpANBqJk/ooUIAY01uTcihgwBSoWXAID/54BAFKKZopRj9UQDs4ekQWPxdwMzBJpAY/OKAFaEIoYMAU6FlwCA/+eAgLQTdfUPVd0CzAFEeV2NTaMJAQBihZcAgP/ngECkffkDRTEB5oWFNGNPBQDj4o3+hWeThwcHopcYCLqX2pcjiqf4BQTxt+MVpf2RR+MF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAID/54BgCnE9MkXBRWUzUT3BMbcHAgAZ4ZMHAAI+hZcAgP/ngGAHhWIWkfpQalTaVEpZulkqWppaClv6S2pM2kxKTbpNKWGCgLdXQUkZcZOH94QBRYbeotym2srYztbS1NbS2tDezuLM5srqyO7GPs6XAID/54CAnaE5DcE3ZwlgEwfHEBxDtwaDQCOi9gC3Bv3//Rb1j8Fm1Y8cwxU5Bc23JwtgN0fYUJOGx8ETBxeqmMIThgfAIyAGACOgBgCThkfCmMKThwfCmEM3BgQAUY+YwyOgBgC3B4NANzeEQJOHBwATBwe7IaAjoAcAkQfj7ef+RTuRRWgIdTllM7e3g0CThweyIWc+lyMg9wi3B4BANwmDQJOHhw4jIPkAtzmEQEU+EwkJAJOJCbJjBQUQtwcBYEVHI6rnCIVFRUWXAID/54DA8rcFgEABRpOFBQBFRZcAgP/ngMDzt/cAYBFHmMs3BQIAlwCA/+eAAPO3FwlgiF+BRbeEg0BxiWEVEzUVAJcAgP/ngICdwWf9FxMHABCFZkFmtwUAAQFFk4REAbcKg0ANapcAgP/ngICTE4tKASaag6fJCPXfg6vJCIVHI6YJCCMC8QKDxxsACUcjE+ECowLxAgLUTUdjgecIUUdjj+cGKUdjn+cAg8c7AAPHKwCiB9mPEUdjlucAg6eLAJxDPtRFMaFFSBB1NoPHOwADxysAogfZjxFnQQdjdPcEEwWwDRk+EwXADQE+EwXgDik2jTlBt7cFgEABRpOFhQMVRZcAgP/ngMDkNwcAYFxHEwUAApPnFxBcxzG3yUcjE/ECTbcDxxsA0UZj5+YChUZj5uYAAUwTBPAPhah5FxN39w/JRuPo5v63NoRACgeThka7NpcYQwKHkwYHA5P29g8RRuNp1vwTB/cCE3f3D41GY+vmCLc2hEAKB5OGBsA2lxhDAocTB0ACY5jnEALUHUQBRaU0AUVVPPE26TahRUgQfRTRPHX0AUwBRBN19A9xPBN1/A9ZPH024x4E6oPHGwBJR2No9zAJR+N29+r1F5P39w89R+Ng9+o3N4RAigcTBwfBupecQ4KHBUSd63AQgUUBRZfwf//ngABxHeHRRWgQnTwBRDGoBUSB75fwf//ngIB1MzSgACmgIUdjhecABUQBTGG3A6yLAAOkywCzZ4wA0gf19+/wv4V98cFsIpz9HH19MwWMQFXcs3eVAZXjwWwzBYxAY+aMAv18MwWMQFXQMYGX8H//54AAclX5ZpT1tzGBl/B//+eAAHFV8WqU0bdBgZfwf//ngEBwUfkzBJRBwbchR+OJ5/ABTBMEAAwxt0FHzb9BRwVE45zn9oOlywADpYsA5TKxv0FHBUTjkuf2A6cLAZFnY+rnHoOlSwEDpYsA7/D/gDW/QUcFROOS5/SDpwsBEWdjavccA6fLAIOlSwEDpYsAM4TnAu/wb/4jrAQAIySKsDG3A8cEAGMDBxQDp4sAwRcTBAAMYxP3AMBIAUeTBvAOY0b3AoPHWwADx0sAAUyiB9mPA8drAEIHXY+Dx3sA4gfZj+OB9uYTBBAMqb0zhusAA0aGAQUHsY7ht4PHBAD9x9xEY50HFMBII4AEAH21YUdjlucCg6fLAQOniwGDpksBA6YLAYOlywADpYsAl/B//+eAwGAqjDM0oAAptQFMBUQRtRFHBUTjmufmt5cAYLRLZXd9FwVm+Y7RjgOliwC0y/RDgUX5jtGO9MP0S/mO0Y70y7RDdY9Rj7jDl/B//+eAoGMpvRP39wDjFQfqk9xHABOEiwABTH1d43Sc20hEl/B//+eAIEgYRFRAEED5jmMHpwEcQhNH9/99j9mOFMIFDEEE2b8RR6W1QUcFROOX596Dp4sAA6dLASMo+QAjJukAdbuDJckAwReR5YnPAUwTBGAMibsDJwkBY2b3BhP3NwDjGQfiAygJAQFGAUczBehAs4blAGNp9wDjBAbSIyipACMm2QAxuzOG6wAQThEHkMIFRum/IUcFROOR59gDJAkBGcATBIAMIygJACMmCQAzNIAApbMBTBMEIAztsQFMEwSADM2xAUwTBJAM6bkTByANY4PnDBMHQA3jm+e4A8Q7AIPHKwAiBF2Ml/B//+eAwEYDrMQAQRRjc4QBIozjCQy2wEBilDGAnEhjVfAAnERjW/QK7/Cvy3XdyEBihpOFiwGX8H//54DAQgHFkwdADNzI3EDil9zA3ESzh4dB3MSX8H//54CgQSW2CWUTBQVxA6zLAAOkiwCX8H//54CgMrcHAGDYS7cGAAHBFpNXRwESB3WPvYvZj7OHhwMBRbPVhwKX8H//54DAMxMFgD6X8H//54BAL+m8g6ZLAQOmCwGDpcsAA6WLAO/w7/vRtIPFOwCDxysAE4WLAaIF3Y3BFe/wj9V1tO/w78Q9vwPEOwCDxysAE4yLASIEXYzcREEUzeORR4VLY/+HCJMHkAzcyEG0A6cNACLQBUizh+xAPtaDJ4qwY3P0AA1IQsY6xO/wb8AiRzJIN4WDQOKFfBCThkoBEBATBcUCl/B//+eAIDE3t4NAkwhHAYJXA6eIsIOlDQAdjB2PPpyyVyOk6LCqi76VI6C9AJOHSgGdjQHFoWdjl/UAWoXv8C/LI6BtAQnE3ESZw+NPcPdj3wsAkwdwDL23hUu3PYRAt4yDQJONDbuTjEwB6b/jnQuc3ETjigeckweADKm3g6eLAOOTB5zv8C/TCWUTBQVxl/B//+eAoBzv8K/Ol/B//+eA4CBVsgOkywDjDwSY7/Cv0BMFgD6X8H//54BAGu/wT8wClFGy7/DPy/ZQZlTWVEZZtlkmWpZaBlv2S2ZM1kxGTbZNCWGCgAAA", 4 | "text_start": 1082130432, 5 | "data": "FACDQHIKgEDCCoBAGguAQOgLgEBUDIBAAgyAQD4JgECkC4BA5AuAQC4LgEDuCIBAYguAQO4IgEBMCoBAkgqAQMIKgEAaC4BAXgqAQKIJgEDSCYBAWgqAQKwOgEDCCoBAbA2AQGQOgEAuCIBAjA6AQC4IgEAuCIBALgiAQC4IgEAuCIBALgiAQC4IgEAuCIBACA2AQC4IgECKDYBAZA6AQA==", 6 | "data_start": 1082403760, 7 | "bss_start": 1082327040 8 | } -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32c3.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1077413584, 3 | "text": "QREixCbCBsa3NwRgEUc3RMg/2Mu3NARgEwQEANxAkYuR57JAIkSSREEBgoCIQBxAE3X1D4KX3bcBEbcHAGBOxoOphwBKyDdJyD8mylLEBs4izLcEAGB9WhMJCQDATBN09D8N4PJAYkQjqDQBQknSRLJJIkoFYYKAiECDJwkAE3X1D4KXfRTjGUT/yb8TBwAMlEGqh2MY5QCFR4XGI6AFAHlVgoAFR2OH5gAJRmONxgB9VYKAQgUTB7ANQYVjlecCiUecwfW3kwbADWMW1QCYwRMFAAyCgJMG0A19VWOV1wCYwRMFsA2CgLd1yT9BEZOFxboGxmE/Y0UFBrd3yT+Th0eyA6cHCAPWRwgTdfUPkwYWAMIGwYIjktcIMpcjAKcAA9dHCJFnk4cHBGMe9wI398g/EwdHsqFnupcDpgcItzbJP7d3yT+Th0eyk4ZGtmMf5gAjpscII6DXCCOSBwghoPlX4wb1/LJAQQGCgCOm1wgjoOcI3bc3JwBgfEudi/X/NzcAYHxLnYv1/4KAQREGxt03tycAYCOmBwI3BwAImMOYQ33/yFeyQBNF9f8FiUEBgoBBEQbG2T993TcHAEC3JwBgmMM3JwBgHEP9/7JAQQGCgEERIsQ3xMg/kweEAUrAA6kHAQbGJsJjCgkERTc5xb1HEwSEAYFEY9YnAQREvYiTtBQAfTeFPxxENwaAABOXxwCZ4DcGAAG39v8AdY+3JgBg2MKQwphCff9BR5HgBUczCelAupcjKCQBHMSyQCJEkkQCSUEBgoABEQbOIswlNzcEzj9sABMFRP+XAMj/54Ag8KqHBUWV57JHk/cHID7GiTc3JwBgHEe3BkAAEwVE/9WPHMeyRZcAyP/ngKDtMzWgAPJAYkQFYYKAQRG3x8g/BsaTh4cBBUcjgOcAE9fFAJjHBWd9F8zDyMf5jTqVqpWxgYzLI6oHAEE3GcETBVAMskBBAYKAAREizDfEyD+TB4QBJsrER07GBs5KyKqJEwSEAWPzlQCuhKnAAylEACaZE1nJABxIY1XwABxEY175ArU9fd1IQCaGzoWXAMj/54Ag4RN19Q8BxZMHQAxcyFxAppdcwFxEhY9cxPJAYkTSREJJskkFYYKAaTVtv0ERBsaXAMj/54AA1gNFhQGyQHUVEzUVAEEBgoBBEQbGxTcdyTdHyD8TBwcAXEONxxBHHcK3BgxgmEYNinGbUY+YxgVmuE4TBgbA8Y99dhMG9j9xj9mPvM6yQEEBgoBBEQbGeT8RwQ1FskBBARcDyP9nAIPMQREGxibCIsSqhJcAyP/ngODJrT8NyTdHyD+TBgcAg9fGABMEBwCFB8IHwYMjlvYAkwYADGOG1AATB+ADY3X3AG03IxYEALJAIkSSREEBgoBBEQbGEwcADGMa5QATBbANRTcTBcANskBBAVm/EwewDeMb5f5xNxMF0A31t0ERIsQmwgbGKoSzBLUAYxeUALJAIkSSREEBgoADRQQABQRNP+23NXEmy07H/XKFaf10Is1KyVLFVsMGz5OEhPoWkZOHCQemlxgIs4TnACqJJoUuhJcAyP/ngEAYk4cJBxgIBWq6l7OKR0Ex5AVnfXWTBYX6kwcHBxMFhfkUCKqXM4XXAJMHBweul7OF1wAqxpcAyP/ngAAVMkXBRZU3AUWFYhaR+kBqRNpESkm6SSpKmkoNYYKAooljc4oAhWlOhtaFSoWXAMj/54AAwxN19Q8B7U6G1oUmhZcAyP/ngEAQTpkzBDRBUbcTBTAGVb8TBQAMSb0xcf1yBWdO11LVVtNezwbfIt0m20rZWtFizWbLaslux/13FpETBwcHPpccCLqXPsYjqgf4qokuirKKtovFM5MHAAIZwbcHAgA+hZcAyP/ngOAIhWdj5VcTBWR9eRMJifqTBwQHypcYCDOJ5wBKhZcAyP/ngGAHfXsTDDv5kwyL+RMHBAeTBwQHFAhil+aXgUQzDNcAs4zXAFJNY3xNCWPxpANBqJk/ooUIAY01uTcihgwBSoWXAMj/54BAA6KZopRj9UQDs4ekQWPxdwMzBJpAY/OKAFaEIoYMAU6FlwDI/+eAQLITdfUPVd0CzAFEeV2NTaMJAQBihZcAyP/ngICkffkDRTEB5oWRPGNPBQDj4o3+hWeThwcHopcYCLqX2pcjiqf4BQTxt+MVpf2RR+MF9PYFZ311kwcHB5MFhfoTBYX5FAiqlzOF1wCTBwcHrpezhdcAKsaXAMj/54Bg+XE9MkXBRWUzUT1VObcHAgAZ4ZMHAAI+hZcAyP/ngGD2hWIWkfpQalTaVEpZulkqWppaClv6S2pM2kxKTbpNKWGCgLdXQUkZcZOH94QBRYbeotym2srYztbS1NbS2tDezuLM5srqyO7GPs6XAMj/54BAnLExDc23BAxgnEQ3RMg/EwQEABzEvEx9dxMH9z9cwPmPk+cHQLzMEwVABpcAyP/ngGCSHETxm5PnFwCcxAE5IcG3hwBgN0fYUJOGhwoTBxeqmMIThwcJIyAHADc3HY8joAYAEwenEpOGBwuYwpOHxwqYQzcGAIBRj5jDI6AGALdHyD83d8k/k4cHABMHR7shoCOgBwCRB+Pt5/5BO5FFaAhxOWEzt/fIP5OHR7IhZz6XIyD3CLcHOEA3Scg/k4eHDiMg+QC3eck/UTYTCQkAk4lJsmMJBRC3JwxgRUe414VFRUWXAMj/54Dg37cFOEABRpOFBQBFRZcAyP/ngODgtzcEYBFHmMs3BQIAlwDI/+eAIOCXAMj/54Cg8LdHAGCcXwnl8YvhFxO1FwCBRZcAyP/ngICTwWe3xMg//RcTBwAQhWZBZrcFAAEBRZOEhAG3Ssg/DWqXAMj/54AAjhOLigEmmoOnyQj134OryQiFRyOmCQgjAvECg8cbAAlHIxPhAqMC8QIC1E1HY4HnCFFHY4/nBilHY5/nAIPHOwADxysAogfZjxFHY5bnAIOniwCcQz7UpTmhRUgQUTaDxzsAA8crAKIH2Y8RZ0EHY3T3BBMFsA39NBMFwA3lNBMF4A7NNKkxQbe3BThAAUaThYUDFUWXAMj/54BA0TcHAGBcRxMFAAKT5xcQXMcJt8lHIxPxAk23A8cbANFGY+fmAoVGY+bmAAFMEwTwD4WoeRcTd/cPyUbj6Ob+t3bJPwoHk4aGuzaXGEMCh5MGBwOT9vYPEUbjadb8Ewf3AhN39w+NRmPo5gq3dsk/CgeThkbANpcYQwKHEwdAAmOV5xIC1B1EAUWBNAFFcTRVNk02oUVIEH0UdTR19AFMAUQTdfQPlTwTdfwPvTRZNuMeBOqDxxsASUdjZfcyCUfjdvfq9ReT9/cPPUfjYPfqN3fJP4oHEwdHwbqXnEOChwVEoeu3BwBAA6dHAZlHcBCBRQFFY/3nAJfQzP/ngACzBUQF6dFFaBA9PAFEHaCXsMz/54Bg/e23BUSB75fwx//ngOBwMzSgACmgIUdjhecABUQBTL23A6yLAAOkywCzZ4wA0gf19+/w34B98cFsIpz9HH19MwWMQE3Ys3eVAZXjwWwzBYxAY+aMAv18MwWMQEncMYGX8Mf/54Dga1X5ZpT1tzGBl/DH/+eA4GpV8WqU0bdBgZfwx//ngKBpUfkzBJRBwbchR+OM5+4BTBMEAAzNvUFHzb9BRwVE45zn9oOlywADpYsAXTKxv0FHBUTjkuf2A6cLAZFnY+rnHoOlSwEDpYsA7/AP/DW/QUcFROOS5/SDpwsBEWdjavccA6fLAIOlSwEDpYsAM4TnAu/wj/kjrAQAIySKsDG3A8cEAGMDBxQDp4sAwRcTBAAMYxP3AMBIAUeTBvAOY0b3AoPHWwADx0sAAUyiB9mPA8drAEIHXY+Dx3sA4gfZj+OE9uQTBBAMgbUzhusAA0aGAQUHsY7ht4PHBAD9x9xEY50HFMBII4AEAH21YUdjlucCg6fLAQOniwGDpksBA6YLAYOlywADpYsAl/DH/+eAoFkqjDM0oADFuwFMBUTtsxFHBUTjmufmt5cAYLRDZXd9FwVm+Y7RjgOliwC0w7RHgUX5jtGOtMf0Q/mO0Y70w9RfdY9Rj9jfl/DH/+eAwFcBvRP39wDjFQfqk9xHABOEiwABTH1d43ec2UhEl/DH/+eAQEQYRFRAEED5jmMHpwEcQhNH9/99j9mOFMIFDEEE2b8RR6W1QUcFROOX596Dp4sAA6dLASMq+QAjKOkATbuDJQkBwReR5YnPAUwTBGAMJbsDJ0kBY2b3BhP3NwDjGQfiAyhJAQFGAUczBehAs4blAGNp9wDjBwbQIyqpACMo2QAJszOG6wAQThEHkMIFRum/IUcFROOR59gDJEkBGcATBIAMIyoJACMoCQAzNIAApbMBTBMEIAzBuQFMEwSADOGxAUwTBJAMwbETByANY4PnDBMHQA3jnue2A8Q7AIPHKwAiBF2Ml/DH/+eAIEIDrMQAQRRjc4QBIozjDAy0wEBilDGAnEhjVfAAnERjW/QK7/DPxnXdyEBihpOFiwGX8Mf/54AgPgHFkwdADNzI3EDil9zA3ESzh4dB3MSX8Mf/54AAPTm2CWUTBQVxA6zLAAOkiwCX8Mf/54DALrcHAGDYS7cGAAHBFpNXRwESB3WPvYvZj7OHhwMBRbPVhwKX8Mf/54CgLxMFgD6X8Mf/54BgK8G0g6ZLAQOmCwGDpcsAA6WLAO/wz/dttIPFOwCDxysAE4WLAaIF3Y3BFe/wr9BJvO/wD8A9vwPEOwCDxysAE4yLASIEXYzcREEUzeORR4VLY/+HCJMHkAzcyJ20A6cNACLQBUizh+xAPtaDJ4qwY3P0AA1IQsY6xO/wj7siRzJIN8XIP+KFfBCThooBEBATBQUDl/DH/+eAACw398g/kwiHAYJXA6eIsIOlDQAdjB2PPpyyVyOk6LCqi76VI6C9AJOHigGdjQHFoWdjl/UAWoXv8E/GI6BtAQnE3ESZw+NPcPdj3wsAkwdwDL23hUu3fck/t8zIP5ONTbuTjIwB6b/jkAuc3ETjjQeakweADKm3g6eLAOOWB5rv8A/PCWUTBQVxl/DH/+eAwBjv8M/Jl/DH/+eAABxpsgOkywDjAgSY7/CPzBMFgD6X8Mf/54BgFu/wb8cClK2y7/DvxvZQZlTWVEZZtlkmWpZaBlv2S2ZM1kxGTbZNCWGCgA==", 4 | "text_start": 1077411840, 5 | "data": "GEDIP8AKOEAQCzhAaAs4QDYMOECiDDhAUAw4QHIJOEDyCzhAMgw4QHwLOEAiCThAsAs4QCIJOECaCjhA4Ao4QBALOEBoCzhArAo4QNYJOEAgCjhAqAo4QPoOOEAQCzhAug04QLIOOEBiCDhA2g44QGIIOEBiCDhAYgg4QGIIOEBiCDhAYgg4QGIIOEBiCDhAVg04QGIIOEDYDThAsg44QA==", 6 | "data_start": 1070164916, 7 | "bss_start": 1070088192 8 | } -------------------------------------------------------------------------------- /src/targets/esp32c5.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader"; 2 | import { ESP32C6ROM } from "./esp32c6"; 3 | import { MemoryMapEntry } from "./rom"; 4 | 5 | export class ESP32C5ROM extends ESP32C6ROM { 6 | public CHIP_NAME = "ESP32-C5"; 7 | public IMAGE_CHIP_ID = 23; 8 | public BOOTLOADER_FLASH_OFFSET = 0x2000; 9 | 10 | public EFUSE_BASE = 0x600b4800; 11 | public EFUSE_BLOCK1_ADDR = this.EFUSE_BASE + 0x044; 12 | public MAC_EFUSE_REG = this.EFUSE_BASE + 0x044; 13 | public UART_CLKDIV_REG = 0x60000014; 14 | 15 | public EFUSE_RD_REG_BASE = this.EFUSE_BASE + 0x030; // BLOCK0 read base address 16 | 17 | public EFUSE_PURPOSE_KEY0_REG = this.EFUSE_BASE + 0x34; 18 | public EFUSE_PURPOSE_KEY0_SHIFT = 24; 19 | public EFUSE_PURPOSE_KEY1_REG = this.EFUSE_BASE + 0x34; 20 | public EFUSE_PURPOSE_KEY1_SHIFT = 28; 21 | public EFUSE_PURPOSE_KEY2_REG = this.EFUSE_BASE + 0x38; 22 | public EFUSE_PURPOSE_KEY2_SHIFT = 0; 23 | public EFUSE_PURPOSE_KEY3_REG = this.EFUSE_BASE + 0x38; 24 | public EFUSE_PURPOSE_KEY3_SHIFT = 4; 25 | public EFUSE_PURPOSE_KEY4_REG = this.EFUSE_BASE + 0x38; 26 | public EFUSE_PURPOSE_KEY4_SHIFT = 8; 27 | public EFUSE_PURPOSE_KEY5_REG = this.EFUSE_BASE + 0x38; 28 | public EFUSE_PURPOSE_KEY5_SHIFT = 12; 29 | 30 | public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_RD_REG_BASE; 31 | public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20; 32 | 33 | public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; 34 | public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; 35 | 36 | public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; 37 | public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; 38 | 39 | public IROM_MAP_START = 0x42000000; 40 | public IROM_MAP_END = 0x42800000; 41 | public DROM_MAP_START = 0x42800000; 42 | public DROM_MAP_END = 0x43000000; 43 | 44 | public PCR_SYSCLK_CONF_REG = 0x60096110; 45 | public PCR_SYSCLK_XTAL_FREQ_V = 0x7f << 24; 46 | public PCR_SYSCLK_XTAL_FREQ_S = 24; 47 | 48 | public XTAL_CLK_DIVIDER = 1; 49 | 50 | public UARTDEV_BUF_NO = 0x4085f514; // Variable in ROM .bss which indicates the port in use 51 | 52 | // Magic value for ESP32C5 53 | public CHIP_DETECT_MAGIC_VALUE = [0x1101406f, 0x63e1406f, 0x5fd1406f]; 54 | 55 | public FLASH_FREQUENCY = { 56 | "80m": 0xf, 57 | "40m": 0x0, 58 | "20m": 0x2, 59 | }; 60 | 61 | public MEMORY_MAP: MemoryMapEntry[] = [ 62 | [0x00000000, 0x00010000, "PADDING"], 63 | [0x42800000, 0x43000000, "DROM"], 64 | [0x40800000, 0x40860000, "DRAM"], 65 | [0x40800000, 0x40860000, "BYTE_ACCESSIBLE"], 66 | [0x4003a000, 0x40040000, "DROM_MASK"], 67 | [0x40000000, 0x4003a000, "IROM_MASK"], 68 | [0x42000000, 0x42800000, "IROM"], 69 | [0x40800000, 0x40860000, "IRAM"], 70 | [0x50000000, 0x50004000, "RTC_IRAM"], 71 | [0x50000000, 0x50004000, "RTC_DRAM"], 72 | [0x600fe000, 0x60100000, "MEM_INTERNAL2"], 73 | ]; 74 | 75 | UF2_FAMILY_ID = 0xf71c0343; 76 | 77 | EFUSE_MAX_KEY = 5; 78 | KEY_PURPOSES = { 79 | 0: "USER/EMPTY", 80 | 1: "ECDSA_KEY", 81 | 2: "XTS_AES_256_KEY_1", 82 | 3: "XTS_AES_256_KEY_2", 83 | 4: "XTS_AES_128_KEY", 84 | 5: "HMAC_DOWN_ALL", 85 | 6: "HMAC_DOWN_JTAG", 86 | 7: "HMAC_DOWN_DIGITAL_SIGNATURE", 87 | 8: "HMAC_UP", 88 | 9: "SECURE_BOOT_DIGEST0", 89 | 10: "SECURE_BOOT_DIGEST1", 90 | 11: "SECURE_BOOT_DIGEST2", 91 | 12: "KM_INIT_KEY", 92 | }; 93 | 94 | public async getPkgVersion(loader: ESPLoader): Promise { 95 | const numWord = 2; 96 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 26) & 0x07; 97 | } 98 | 99 | public async getMinorChipVersion(loader: ESPLoader): Promise { 100 | const numWord = 2; 101 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 0) & 0x0f; 102 | } 103 | 104 | public async getMajorChipVersion(loader: ESPLoader): Promise { 105 | const numWord = 2; 106 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 4) & 0x03; 107 | } 108 | 109 | public async getChipDescription(loader: ESPLoader): Promise { 110 | const pkgVer = await this.getPkgVersion(loader); 111 | let desc: string; 112 | if (pkgVer === 0) { 113 | desc = "ESP32-C5"; 114 | } else { 115 | desc = "unknown ESP32-C5"; 116 | } 117 | const majorRev = await this.getMajorChipVersion(loader); 118 | const minorRev = await this.getMinorChipVersion(loader); 119 | return `${desc} (revision v${majorRev}.${minorRev})`; 120 | } 121 | 122 | public async getChipFeatures(loader: ESPLoader): Promise { 123 | return ["Wi-Fi 6 (dual-band)", "BT 5 (LE)"]; 124 | } 125 | 126 | public async getCrystalFreq(loader: ESPLoader): Promise { 127 | // The crystal detection algorithm of ESP32/ESP8266 128 | // works for ESP32-C5 as well. 129 | const uartDiv = (await loader.readReg(this.UART_CLKDIV_REG)) & this.UART_CLKDIV_MASK; 130 | const etsXtal = (loader.transport.baudrate * uartDiv) / 1000000 / this.XTAL_CLK_DIVIDER; 131 | let normXtal; 132 | if (etsXtal > 45) { 133 | normXtal = 48; 134 | } else if (etsXtal > 33) { 135 | normXtal = 40; 136 | } else { 137 | normXtal = 26; 138 | } 139 | if (Math.abs(normXtal - etsXtal) > 1) { 140 | loader.info("WARNING: Unsupported crystal in use"); 141 | } 142 | return normXtal; 143 | } 144 | 145 | public async getCrystalFreqRomExpect(loader: ESPLoader) { 146 | return ( 147 | ((await loader.readReg(this.PCR_SYSCLK_CONF_REG)) & this.PCR_SYSCLK_XTAL_FREQ_V) >> this.PCR_SYSCLK_XTAL_FREQ_S 148 | ); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32s2.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1073907716, 3 | "text": "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", 4 | "text_start": 1073905664, 5 | "data": "WAD9P0uLAkDdiwJA8pACQGaMAkD+iwJAZowCQMWMAkDejQJAUY4CQPmNAkDVigJAd40CQNCNAkDojAJAdI4CQBCNAkB0jgJAy4sCQCqMAkBmjAJAxYwCQOOLAkAXiwJAN48CQKqQAkDqiQJA0ZACQOqJAkDqiQJA6okCQOqJAkDqiQJA6okCQOqJAkDqiQJA1I4CQOqJAkDJjwJAqpACQA==", 6 | "data_start": 1073622012, 7 | "bss_start": 1073545216 8 | } -------------------------------------------------------------------------------- /examples/typescript/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ESP Tool 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

ESP Tool

20 |

A Serial Flasher utility for Espressif chips

21 | 23 |
24 | View the API Documentation 25 | 26 |
27 | 28 | 29 |
30 |
31 |
32 |
33 |
34 |

Program

35 | 36 | 37 | 43 | 44 |

45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
Flash AddressFile
69 | 70 | 71 |
72 | 73 |
74 |
75 |
76 |

Console

77 | 78 | 79 | 83 | 84 | 85 | 86 | 87 | 88 |

89 | 90 | 91 | 92 | 93 |
94 |
95 |
96 |
97 | 98 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/targets/esp32c3.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { ESP32ROM } from "./esp32.js"; 3 | import { MemoryMapEntry } from "./rom.js"; 4 | 5 | export class ESP32C3ROM extends ESP32ROM { 6 | public CHIP_NAME = "ESP32-C3"; 7 | public IMAGE_CHIP_ID = 5; 8 | public EFUSE_BASE = 0x60008800; 9 | public MAC_EFUSE_REG = this.EFUSE_BASE + 0x044; 10 | public UART_CLKDIV_REG = 0x3ff40014; 11 | public UART_CLKDIV_MASK = 0xfffff; 12 | public UART_DATE_REG_ADDR = 0x6000007c; 13 | 14 | public FLASH_WRITE_SIZE = 0x400; 15 | public BOOTLOADER_FLASH_OFFSET = 0; 16 | 17 | public SPI_REG_BASE = 0x60002000; 18 | public SPI_USR_OFFS = 0x18; 19 | public SPI_USR1_OFFS = 0x1c; 20 | public SPI_USR2_OFFS = 0x20; 21 | public SPI_MOSI_DLEN_OFFS = 0x24; 22 | public SPI_MISO_DLEN_OFFS = 0x28; 23 | public SPI_W0_OFFS = 0x58; 24 | 25 | IROM_MAP_START = 0x42000000; 26 | IROM_MAP_END = 0x42800000; 27 | 28 | public MEMORY_MAP: MemoryMapEntry[] = [ 29 | [0x00000000, 0x00010000, "PADDING"], 30 | [0x3c000000, 0x3c800000, "DROM"], 31 | [0x3fc80000, 0x3fce0000, "DRAM"], 32 | [0x3fc88000, 0x3fd00000, "BYTE_ACCESSIBLE"], 33 | [0x3ff00000, 0x3ff20000, "DROM_MASK"], 34 | [0x40000000, 0x40060000, "IROM_MASK"], 35 | [0x42000000, 0x42800000, "IROM"], 36 | [0x4037c000, 0x403e0000, "IRAM"], 37 | [0x50000000, 0x50002000, "RTC_IRAM"], 38 | [0x50000000, 0x50002000, "RTC_DRAM"], 39 | [0x600fe000, 0x60100000, "MEM_INTERNAL2"], 40 | ]; 41 | 42 | public async getPkgVersion(loader: ESPLoader): Promise { 43 | const numWord = 3; 44 | const block1Addr = this.EFUSE_BASE + 0x044; 45 | const addr = block1Addr + 4 * numWord; 46 | const word3 = await loader.readReg(addr); 47 | const pkgVersion = (word3 >> 21) & 0x07; 48 | return pkgVersion; 49 | } 50 | 51 | public async getChipRevision(loader: ESPLoader): Promise { 52 | const block1Addr = this.EFUSE_BASE + 0x044; 53 | const numWord = 3; 54 | const pos = 18; 55 | const addr = block1Addr + 4 * numWord; 56 | const ret = ((await loader.readReg(addr)) & (0x7 << pos)) >> pos; 57 | return ret; 58 | } 59 | 60 | public async getMinorChipVersion(loader: ESPLoader): Promise { 61 | const hiNumWord = 5; 62 | const hiAddr = this.EFUSE_BASE + 0x044 + 4 * hiNumWord; 63 | const hi = ((await loader.readReg(hiAddr)) >> 23) & 0x01; 64 | 65 | const lowNumWord = 3; 66 | const lowAddr = this.EFUSE_BASE + 0x044 + 4 * lowNumWord; 67 | const low = ((await loader.readReg(lowAddr)) >> 18) & 0x07; 68 | 69 | return (hi << 3) + low; 70 | } 71 | 72 | public async getMajorChipVersion(loader: ESPLoader): Promise { 73 | const numWord = 5; 74 | const addr = this.EFUSE_BASE + 0x044 + 4 * numWord; 75 | return ((await loader.readReg(addr)) >> 24) & 0x03; 76 | } 77 | 78 | public async getChipDescription(loader: ESPLoader) { 79 | const chipDesc: { [key: number]: string } = { 80 | 0: "ESP32-C3 (QFN32)", 81 | 1: "ESP8685 (QFN28)", 82 | 2: "ESP32-C3 AZ (QFN32)", 83 | 3: "ESP8686 (QFN24)", 84 | }; 85 | const chipIndex = await this.getPkgVersion(loader); 86 | const majorRev = await this.getMajorChipVersion(loader); 87 | const minorRev = await this.getMinorChipVersion(loader); 88 | return `${chipDesc[chipIndex] || "Unknown ESP32-C3"} (revision v${majorRev}.${minorRev})`; 89 | } 90 | 91 | public async getFlashCap(loader: ESPLoader): Promise { 92 | const numWord = 3; 93 | const block1Addr = this.EFUSE_BASE + 0x044; 94 | const addr = block1Addr + 4 * numWord; 95 | const registerValue = await loader.readReg(addr); 96 | const flashCap = (registerValue >> 27) & 0x07; 97 | return flashCap; 98 | } 99 | 100 | public async getFlashVendor(loader: ESPLoader): Promise { 101 | const numWord = 4; 102 | const block1Addr = this.EFUSE_BASE + 0x044; 103 | const addr = block1Addr + 4 * numWord; 104 | const registerValue = await loader.readReg(addr); 105 | const vendorId = (registerValue >> 0) & 0x07; 106 | const vendorMap: { [key: number]: string } = { 107 | 1: "XMC", 108 | 2: "GD", 109 | 3: "FM", 110 | 4: "TT", 111 | 5: "ZBIT", 112 | }; 113 | return vendorMap[vendorId] || ""; 114 | } 115 | 116 | public async getChipFeatures(loader: ESPLoader): Promise { 117 | const features: string[] = ["Wi-Fi", "BLE"]; 118 | 119 | const flashMap: { [key: number]: string | null } = { 120 | 0: null, 121 | 1: "Embedded Flash 4MB", 122 | 2: "Embedded Flash 2MB", 123 | 3: "Embedded Flash 1MB", 124 | 4: "Embedded Flash 8MB", 125 | }; 126 | const flashCap = await this.getFlashCap(loader); 127 | const flashVendor = await this.getFlashVendor(loader); 128 | const flash = flashMap[flashCap]; 129 | const flashDescription = flash !== undefined ? flash : "Unknown Embedded Flash"; 130 | if (flash !== null) { 131 | features.push(`${flashDescription} (${flashVendor})`); 132 | } 133 | return features; 134 | } 135 | 136 | public async getCrystalFreq(loader: ESPLoader) { 137 | return 40; 138 | } 139 | 140 | public _d2h(d: number) { 141 | const h = (+d).toString(16); 142 | return h.length === 1 ? "0" + h : h; 143 | } 144 | 145 | public async readMac(loader: ESPLoader) { 146 | let mac0 = await loader.readReg(this.MAC_EFUSE_REG); 147 | mac0 = mac0 >>> 0; 148 | let mac1 = await loader.readReg(this.MAC_EFUSE_REG + 4); 149 | mac1 = (mac1 >>> 0) & 0x0000ffff; 150 | const mac = new Uint8Array(6); 151 | mac[0] = (mac1 >> 8) & 0xff; 152 | mac[1] = mac1 & 0xff; 153 | mac[2] = (mac0 >> 24) & 0xff; 154 | mac[3] = (mac0 >> 16) & 0xff; 155 | mac[4] = (mac0 >> 8) & 0xff; 156 | mac[5] = mac0 & 0xff; 157 | 158 | return ( 159 | this._d2h(mac[0]) + 160 | ":" + 161 | this._d2h(mac[1]) + 162 | ":" + 163 | this._d2h(mac[2]) + 164 | ":" + 165 | this._d2h(mac[3]) + 166 | ":" + 167 | this._d2h(mac[4]) + 168 | ":" + 169 | this._d2h(mac[5]) 170 | ); 171 | } 172 | 173 | public getEraseSize(offset: number, size: number) { 174 | return size; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32c5.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1082132526, 3 | "text": "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", 4 | "text_start": 1082130432, 5 | "data": "GACEQL4MgEAMDYBApA2AQIIOgEDqDoBAmA6AQPIKgEA2DoBAfg6AQMINgECiCoBA6g2AQKIKgECcDIBA2AyAQAwNgECkDYBArgyAQBwLgEBKC4BAqgyAQK4SgEAMDYBAUBCAQGYSgEDgCYBAjhKAQOAJgEDgCYBA4AmAQOAJgEDgCYBA4AmAQOAJgEDgCYBA9g+AQOAJgECKEYBAZhKAQA==", 6 | "data_start": 1082469300, 7 | "bss_start": 1082392576 8 | } -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_32s3.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1077381760, 3 | "text": "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", 4 | "text_start": 1077379072, 5 | "data": "XADKP16ON0AzjzdAR5Q3QL2PN0BTjzdAvY83QB2QN0A6kTdArJE3QFWRN0DpjTdA0JA3QCyRN0BAkDdA0JE3QGiQN0DQkTdAIY83QH6PN0C9jzdAHZA3QDmPN0AqjjdAkJI3QA2UN0AAjTdALZQ3QACNN0AAjTdAAI03QACNN0AAjTdAAI03QACNN0AAjTdAKpI3QACNN0AlkzdADZQ3QAQInwAAAAAAAAAYAQQIBQAAAAAAAAAIAQQIBgAAAAAAAAAAAQQIIQAAAAAAIAAAEQQI3AAAAAAAIAAAEQQIDAAAAAAAIAAAAQQIEgAAAAAAIAAAESAoDAAQAQAA", 6 | "data_start": 1070279676, 7 | "bss_start": 1070202880 8 | } -------------------------------------------------------------------------------- /src/targets/esp32.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { MemoryMapEntry, ROM } from "./rom.js"; 3 | 4 | export class ESP32ROM extends ROM { 5 | public CHIP_NAME = "ESP32"; 6 | public IMAGE_CHIP_ID = 0; 7 | public EFUSE_RD_REG_BASE = 0x3ff5a000; 8 | public DR_REG_SYSCON_BASE = 0x3ff66000; 9 | public UART_CLKDIV_REG = 0x3ff40014; 10 | public UART_CLKDIV_MASK = 0xfffff; 11 | public UART_DATE_REG_ADDR = 0x60000078; 12 | public XTAL_CLK_DIVIDER = 1; 13 | 14 | IROM_MAP_START = 0x400d0000; 15 | IROM_MAP_END = 0x40400000; 16 | DROM_MAP_START = 0x3f400000; 17 | DROM_MAP_END = 0x3f800000; 18 | 19 | public MEMORY_MAP: MemoryMapEntry[] = [ 20 | [0x00000000, 0x00010000, "PADDING"], 21 | [0x3f400000, 0x3f800000, "DROM"], 22 | [0x3f800000, 0x3fc00000, "EXTRAM_DATA"], 23 | [0x3ff80000, 0x3ff82000, "RTC_DRAM"], 24 | [0x3ff90000, 0x40000000, "BYTE_ACCESSIBLE"], 25 | [0x3ffae000, 0x40000000, "DRAM"], 26 | [0x3ffe0000, 0x3ffffffc, "DIRAM_DRAM"], 27 | [0x40000000, 0x40070000, "IROM"], 28 | [0x40070000, 0x40078000, "CACHE_PRO"], 29 | [0x40078000, 0x40080000, "CACHE_APP"], 30 | [0x40080000, 0x400a0000, "IRAM"], 31 | [0x400a0000, 0x400bfffc, "DIRAM_IRAM"], 32 | [0x400c0000, 0x400c2000, "RTC_IRAM"], 33 | [0x400d0000, 0x40400000, "IROM"], 34 | [0x50000000, 0x50002000, "RTC_DATA"], 35 | ]; 36 | 37 | public FLASH_SIZES: { [key: string]: number } = { 38 | "1MB": 0x00, 39 | "2MB": 0x10, 40 | "4MB": 0x20, 41 | "8MB": 0x30, 42 | "16MB": 0x40, 43 | "32MB": 0x50, 44 | "64MB": 0x60, 45 | "128MB": 0x70, 46 | }; 47 | 48 | public FLASH_FREQUENCY: { [key: string]: number } = { 49 | "80m": 0xf, 50 | "40m": 0x0, 51 | "26m": 0x1, 52 | "20m": 0x2, 53 | }; 54 | 55 | public FLASH_WRITE_SIZE = 0x400; 56 | public BOOTLOADER_FLASH_OFFSET = 0x1000; 57 | 58 | public SPI_REG_BASE = 0x3ff42000; 59 | public SPI_USR_OFFS = 0x1c; 60 | public SPI_USR1_OFFS = 0x20; 61 | public SPI_USR2_OFFS = 0x24; 62 | public SPI_W0_OFFS = 0x80; 63 | public SPI_MOSI_DLEN_OFFS = 0x28; 64 | public SPI_MISO_DLEN_OFFS = 0x2c; 65 | 66 | public async readEfuse(loader: ESPLoader, offset: number): Promise { 67 | const addr = this.EFUSE_RD_REG_BASE + 4 * offset; 68 | loader.debug("Read efuse " + addr); 69 | return await loader.readReg(addr); 70 | } 71 | 72 | public async getPkgVersion(loader: ESPLoader): Promise { 73 | const word3 = await this.readEfuse(loader, 3); 74 | let pkgVersion = (word3 >> 9) & 0x07; 75 | pkgVersion += ((word3 >> 2) & 0x1) << 3; 76 | return pkgVersion; 77 | } 78 | 79 | public async getChipRevision(loader: ESPLoader): Promise { 80 | const word3 = await this.readEfuse(loader, 3); 81 | const word5 = await this.readEfuse(loader, 5); 82 | const apbCtlDate = await loader.readReg(this.DR_REG_SYSCON_BASE + 0x7c); 83 | 84 | const revBit0 = (word3 >> 15) & 0x1; 85 | const revBit1 = (word5 >> 20) & 0x1; 86 | const revBit2 = (apbCtlDate >> 31) & 0x1; 87 | if (revBit0 != 0) { 88 | if (revBit1 != 0) { 89 | if (revBit2 != 0) { 90 | return 3; 91 | } else { 92 | return 2; 93 | } 94 | } else { 95 | return 1; 96 | } 97 | } 98 | return 0; 99 | } 100 | 101 | public async getChipDescription(loader: ESPLoader) { 102 | const chipDesc = [ 103 | "ESP32-D0WDQ6", 104 | "ESP32-D0WD", 105 | "ESP32-D2WD", 106 | "", 107 | "ESP32-U4WDH", 108 | "ESP32-PICO-D4", 109 | "ESP32-PICO-V3-02", 110 | ]; 111 | let chipName = ""; 112 | const pkgVersion = await this.getPkgVersion(loader); 113 | const chipRevision = await this.getChipRevision(loader); 114 | const rev3 = chipRevision == 3; 115 | const single_core = (await this.readEfuse(loader, 3)) & (1 << 0); 116 | 117 | if (single_core != 0) { 118 | chipDesc[0] = "ESP32-S0WDQ6"; 119 | chipDesc[1] = "ESP32-S0WD"; 120 | } 121 | if (rev3) { 122 | chipDesc[5] = "ESP32-PICO-V3"; 123 | } 124 | if (pkgVersion >= 0 && pkgVersion <= 6) { 125 | chipName = chipDesc[pkgVersion]; 126 | } else { 127 | chipName = "Unknown ESP32"; 128 | } 129 | 130 | if (rev3 && (pkgVersion === 0 || pkgVersion === 1)) { 131 | chipName += "-V3"; 132 | } 133 | return chipName + " (revision " + chipRevision + ")"; 134 | } 135 | 136 | public async getChipFeatures(loader: ESPLoader) { 137 | const features = ["Wi-Fi"]; 138 | const word3 = await this.readEfuse(loader, 3); 139 | 140 | const chipVerDisBt = word3 & (1 << 1); 141 | if (chipVerDisBt === 0) { 142 | features.push(" BT"); 143 | } 144 | 145 | const chipVerDisAppCpu = word3 & (1 << 0); 146 | if (chipVerDisAppCpu !== 0) { 147 | features.push(" Single Core"); 148 | } else { 149 | features.push(" Dual Core"); 150 | } 151 | 152 | const chipCpuFreqRated = word3 & (1 << 13); 153 | if (chipCpuFreqRated !== 0) { 154 | const chipCpuFreqLow = word3 & (1 << 12); 155 | if (chipCpuFreqLow !== 0) { 156 | features.push(" 160MHz"); 157 | } else { 158 | features.push(" 240MHz"); 159 | } 160 | } 161 | 162 | const pkgVersion = await this.getPkgVersion(loader); 163 | if ([2, 4, 5, 6].indexOf(pkgVersion) !== -1) { 164 | features.push(" Embedded Flash"); 165 | } 166 | 167 | if (pkgVersion === 6) { 168 | features.push(" Embedded PSRAM"); 169 | } 170 | 171 | const word4 = await this.readEfuse(loader, 4); 172 | const adcVref = (word4 >> 8) & 0x1f; 173 | if (adcVref !== 0) { 174 | features.push(" VRef calibration in efuse"); 175 | } 176 | 177 | const blk3PartRes = (word3 >> 14) & 0x1; 178 | if (blk3PartRes !== 0) { 179 | features.push(" BLK3 partially reserved"); 180 | } 181 | 182 | const word6 = await this.readEfuse(loader, 6); 183 | const codingScheme = word6 & 0x3; 184 | const codingSchemeArr = ["None", "3/4", "Repeat (UNSUPPORTED)", "Invalid"]; 185 | features.push(" Coding Scheme " + codingSchemeArr[codingScheme]); 186 | 187 | return features; 188 | } 189 | 190 | public async getCrystalFreq(loader: ESPLoader) { 191 | const uartDiv = (await loader.readReg(this.UART_CLKDIV_REG)) & this.UART_CLKDIV_MASK; 192 | const etsXtal = (loader.transport.baudrate * uartDiv) / 1000000 / this.XTAL_CLK_DIVIDER; 193 | let normXtal; 194 | if (etsXtal > 33) { 195 | normXtal = 40; 196 | } else { 197 | normXtal = 26; 198 | } 199 | if (Math.abs(normXtal - etsXtal) > 1) { 200 | loader.info("WARNING: Unsupported crystal in use"); 201 | } 202 | return normXtal; 203 | } 204 | 205 | public _d2h(d: number) { 206 | const h = (+d).toString(16); 207 | return h.length === 1 ? "0" + h : h; 208 | } 209 | 210 | public async readMac(loader: ESPLoader) { 211 | let mac0 = await this.readEfuse(loader, 1); 212 | mac0 = mac0 >>> 0; 213 | let mac1 = await this.readEfuse(loader, 2); 214 | mac1 = mac1 >>> 0; 215 | const mac = new Uint8Array(6); 216 | mac[0] = (mac1 >> 8) & 0xff; 217 | mac[1] = mac1 & 0xff; 218 | mac[2] = (mac0 >> 24) & 0xff; 219 | mac[3] = (mac0 >> 16) & 0xff; 220 | mac[4] = (mac0 >> 8) & 0xff; 221 | mac[5] = mac0 & 0xff; 222 | 223 | return ( 224 | this._d2h(mac[0]) + 225 | ":" + 226 | this._d2h(mac[1]) + 227 | ":" + 228 | this._d2h(mac[2]) + 229 | ":" + 230 | this._d2h(mac[3]) + 231 | ":" + 232 | this._d2h(mac[4]) + 233 | ":" + 234 | this._d2h(mac[5]) 235 | ); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/targets/esp32p4.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { ESP32ROM } from "./esp32.js"; 3 | import { MemoryMapEntry } from "./rom.js"; 4 | 5 | export class ESP32P4ROM extends ESP32ROM { 6 | public CHIP_NAME = "ESP32-P4"; 7 | public IMAGE_CHIP_ID = 18; 8 | 9 | public IROM_MAP_START = 0x40000000; 10 | public IROM_MAP_END = 0x4c000000; 11 | public DROM_MAP_START = 0x40000000; 12 | public DROM_MAP_END = 0x4c000000; 13 | 14 | public BOOTLOADER_FLASH_OFFSET = 0x2000; // First 2 sectors are reserved for FE purposes 15 | 16 | public CHIP_DETECT_MAGIC_VALUE = [0x0, 0x0addbad0]; 17 | 18 | public UART_DATE_REG_ADDR = 0x500ca000 + 0x8c; 19 | 20 | public EFUSE_BASE = 0x5012d000; 21 | public EFUSE_BLOCK1_ADDR = this.EFUSE_BASE + 0x044; 22 | public MAC_EFUSE_REG = this.EFUSE_BASE + 0x044; 23 | 24 | public SPI_REG_BASE = 0x5008d000; // SPIMEM1 25 | public SPI_USR_OFFS = 0x18; 26 | public SPI_USR1_OFFS = 0x1c; 27 | public SPI_USR2_OFFS = 0x20; 28 | public SPI_MOSI_DLEN_OFFS = 0x24; 29 | public SPI_MISO_DLEN_OFFS = 0x28; 30 | public SPI_W0_OFFS = 0x58; 31 | 32 | public EFUSE_RD_REG_BASE = this.EFUSE_BASE + 0x030; // BLOCK0 read base address 33 | 34 | public EFUSE_PURPOSE_KEY0_REG = this.EFUSE_BASE + 0x34; 35 | public EFUSE_PURPOSE_KEY0_SHIFT = 24; 36 | public EFUSE_PURPOSE_KEY1_REG = this.EFUSE_BASE + 0x34; 37 | public EFUSE_PURPOSE_KEY1_SHIFT = 28; 38 | public EFUSE_PURPOSE_KEY2_REG = this.EFUSE_BASE + 0x38; 39 | public EFUSE_PURPOSE_KEY2_SHIFT = 0; 40 | public EFUSE_PURPOSE_KEY3_REG = this.EFUSE_BASE + 0x38; 41 | public EFUSE_PURPOSE_KEY3_SHIFT = 4; 42 | public EFUSE_PURPOSE_KEY4_REG = this.EFUSE_BASE + 0x38; 43 | public EFUSE_PURPOSE_KEY4_SHIFT = 8; 44 | public EFUSE_PURPOSE_KEY5_REG = this.EFUSE_BASE + 0x38; 45 | public EFUSE_PURPOSE_KEY5_SHIFT = 12; 46 | 47 | public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_RD_REG_BASE; 48 | public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20; 49 | 50 | public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; 51 | public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; 52 | 53 | public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; 54 | public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; 55 | 56 | public PURPOSE_VAL_XTS_AES256_KEY_1 = 2; 57 | public PURPOSE_VAL_XTS_AES256_KEY_2 = 3; 58 | public PURPOSE_VAL_XTS_AES128_KEY = 4; 59 | 60 | public SUPPORTS_ENCRYPTED_FLASH = true; 61 | 62 | public FLASH_ENCRYPTED_WRITE_ALIGN = 16; 63 | 64 | public MEMORY_MAP: MemoryMapEntry[] = [ 65 | [0x00000000, 0x00010000, "PADDING"], 66 | [0x40000000, 0x4c000000, "DROM"], 67 | [0x4ff00000, 0x4ffa0000, "DRAM"], 68 | [0x4ff00000, 0x4ffa0000, "BYTE_ACCESSIBLE"], 69 | [0x4fc00000, 0x4fc20000, "DROM_MASK"], 70 | [0x4fc00000, 0x4fc20000, "IROM_MASK"], 71 | [0x40000000, 0x4c000000, "IROM"], 72 | [0x4ff00000, 0x4ffa0000, "IRAM"], 73 | [0x50108000, 0x50110000, "RTC_IRAM"], 74 | [0x50108000, 0x50110000, "RTC_DRAM"], 75 | [0x600fe000, 0x60100000, "MEM_INTERNAL2"], 76 | ]; 77 | 78 | public UF2_FAMILY_ID = 0x3d308e94; 79 | 80 | public EFUSE_MAX_KEY = 5; 81 | public KEY_PURPOSES = { 82 | 0: "USER/EMPTY", 83 | 1: "ECDSA_KEY", 84 | 2: "XTS_AES_256_KEY_1", 85 | 3: "XTS_AES_256_KEY_2", 86 | 4: "XTS_AES_128_KEY", 87 | 5: "HMAC_DOWN_ALL", 88 | 6: "HMAC_DOWN_JTAG", 89 | 7: "HMAC_DOWN_DIGITAL_SIGNATURE", 90 | 8: "HMAC_UP", 91 | 9: "SECURE_BOOT_DIGEST0", 92 | 10: "SECURE_BOOT_DIGEST1", 93 | 11: "SECURE_BOOT_DIGEST2", 94 | 12: "KM_INIT_KEY", 95 | }; 96 | 97 | public async getPkgVersion(loader: ESPLoader): Promise { 98 | const numWord = 2; 99 | const addr = this.EFUSE_BLOCK1_ADDR + 4 * numWord; 100 | const registerValue = await loader.readReg(addr); 101 | return (registerValue >> 27) & 0x07; 102 | } 103 | 104 | public async getMinorChipVersion(loader: ESPLoader): Promise { 105 | const numWord = 2; 106 | const addr = this.EFUSE_BLOCK1_ADDR + 4 * numWord; 107 | const registerValue = await loader.readReg(addr); 108 | return (registerValue >> 0) & 0x0f; 109 | } 110 | 111 | public async getMajorChipVersion(loader: ESPLoader): Promise { 112 | const numWord = 2; 113 | const addr = this.EFUSE_BLOCK1_ADDR + 4 * numWord; 114 | const registerValue = await loader.readReg(addr); 115 | return (registerValue >> 4) & 0x03; 116 | } 117 | 118 | public async getChipDescription(loader: ESPLoader): Promise { 119 | const pkgVersion = await this.getPkgVersion(loader); 120 | const chipName = pkgVersion === 0 ? "ESP32-P4" : "unknown ESP32-P4"; 121 | const majorRev = await this.getMajorChipVersion(loader); 122 | const minorRev = await this.getMinorChipVersion(loader); 123 | return `${chipName} (revision v${majorRev}.${minorRev})`; 124 | } 125 | 126 | public async getChipFeatures(loader: ESPLoader): Promise { 127 | return ["High-Performance MCU"]; 128 | } 129 | 130 | public async getCrystalFreq(loader: ESPLoader): Promise { 131 | return 40; // ESP32P4 XTAL is fixed to 40MHz 132 | } 133 | 134 | public async getFlashVoltage(loader: ESPLoader) { 135 | return; 136 | } 137 | 138 | public async overrideVddsdio(loader: ESPLoader) { 139 | loader.debug("VDD_SDIO overrides are not supported for ESP32-P4"); 140 | } 141 | 142 | public async readMac(loader: ESPLoader) { 143 | let mac0 = await loader.readReg(this.MAC_EFUSE_REG); 144 | mac0 = mac0 >>> 0; 145 | let mac1 = await loader.readReg(this.MAC_EFUSE_REG + 4); 146 | mac1 = (mac1 >>> 0) & 0x0000ffff; 147 | const mac = new Uint8Array(6); 148 | mac[0] = (mac1 >> 8) & 0xff; 149 | mac[1] = mac1 & 0xff; 150 | mac[2] = (mac0 >> 24) & 0xff; 151 | mac[3] = (mac0 >> 16) & 0xff; 152 | mac[4] = (mac0 >> 8) & 0xff; 153 | mac[5] = mac0 & 0xff; 154 | 155 | return ( 156 | this._d2h(mac[0]) + 157 | ":" + 158 | this._d2h(mac[1]) + 159 | ":" + 160 | this._d2h(mac[2]) + 161 | ":" + 162 | this._d2h(mac[3]) + 163 | ":" + 164 | this._d2h(mac[4]) + 165 | ":" + 166 | this._d2h(mac[5]) 167 | ); 168 | } 169 | 170 | public async getFlashCryptConfig(loader: ESPLoader) { 171 | return; // doesn't exist on ESP32-P4 172 | } 173 | 174 | public async getSecureBootEnabled(laoder: ESPLoader) { 175 | const registerValue = await laoder.readReg(this.EFUSE_SECURE_BOOT_EN_REG); 176 | return registerValue & this.EFUSE_SECURE_BOOT_EN_MASK; 177 | } 178 | 179 | public async getKeyBlockPurpose(loader: ESPLoader, keyBlock: number) { 180 | if (keyBlock < 0 || keyBlock > this.EFUSE_MAX_KEY) { 181 | loader.debug(`Valid key block numbers must be in range 0-${this.EFUSE_MAX_KEY}`); 182 | return; 183 | } 184 | 185 | const regShiftDictionary = [ 186 | [this.EFUSE_PURPOSE_KEY0_REG, this.EFUSE_PURPOSE_KEY0_SHIFT], 187 | [this.EFUSE_PURPOSE_KEY1_REG, this.EFUSE_PURPOSE_KEY1_SHIFT], 188 | [this.EFUSE_PURPOSE_KEY2_REG, this.EFUSE_PURPOSE_KEY2_SHIFT], 189 | [this.EFUSE_PURPOSE_KEY3_REG, this.EFUSE_PURPOSE_KEY3_SHIFT], 190 | [this.EFUSE_PURPOSE_KEY4_REG, this.EFUSE_PURPOSE_KEY4_SHIFT], 191 | [this.EFUSE_PURPOSE_KEY5_REG, this.EFUSE_PURPOSE_KEY5_SHIFT], 192 | ]; 193 | const [reg, shift] = regShiftDictionary[keyBlock]; 194 | 195 | const registerValue = await loader.readReg(reg); 196 | return (registerValue >> shift) & 0xf; 197 | } 198 | 199 | public async isFlashEncryptionKeyValid(loader: ESPLoader) { 200 | const purposes = []; 201 | for (let i = 0; i <= this.EFUSE_MAX_KEY; i++) { 202 | const purpose = await this.getKeyBlockPurpose(loader, i); 203 | purposes.push(purpose); 204 | } 205 | const isXtsAes128Key = purposes.find((p) => p === this.PURPOSE_VAL_XTS_AES128_KEY); 206 | if (typeof isXtsAes128Key !== undefined) { 207 | return true; 208 | } 209 | const isXtsAes256Key1 = purposes.find((p) => p === this.PURPOSE_VAL_XTS_AES256_KEY_1); 210 | const isXtsAes256Key2 = purposes.find((p) => p === this.PURPOSE_VAL_XTS_AES256_KEY_2); 211 | if (typeof isXtsAes256Key1 !== undefined && typeof isXtsAes256Key2 !== undefined) { 212 | return true; 213 | } 214 | return false; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/reset.ts: -------------------------------------------------------------------------------- 1 | import { Transport } from "./webserial.js"; 2 | 3 | /** 4 | * Set of reset functions for ESP Loader connection. 5 | * @interface ResetConstructors 6 | */ 7 | export interface ResetConstructors { 8 | /** 9 | * Execute a classic set of commands that will reset the chip. 10 | * @param transport Transport class to perform serial communication. 11 | * @param resetDelay Delay in milliseconds for reset. 12 | */ 13 | classicReset?: (transport: Transport, resetDelay: number) => ClassicReset; 14 | 15 | /** 16 | * Execute a set of commands for USB JTAG serial reset. 17 | * @param transport Transport class to perform serial communication. 18 | */ 19 | usbJTAGSerialReset?: (transport: Transport) => UsbJtagSerialReset; 20 | 21 | /** 22 | * Execute a classic set of commands that will reset the chip. 23 | * @param transport Transport class to perform serial communication. 24 | * @param {boolean} usingUsbOtg is it using USB-OTG ? 25 | */ 26 | hardReset?: (transport: Transport, usingUsbOtg?: boolean) => HardReset; 27 | 28 | /** 29 | * Execute a custom set of commands that will reset the chip. 30 | * @param transport Transport class to perform serial communication. 31 | * @param {string} sequenceString Custom string sequence for reset strategy 32 | */ 33 | customReset?: (transport: Transport, sequenceString: string) => CustomReset; 34 | } 35 | 36 | /** 37 | * Sleep for ms milliseconds 38 | * @param {number} ms Milliseconds to wait 39 | * @returns {Promise} 40 | */ 41 | function sleep(ms: number): Promise { 42 | return new Promise((resolve) => setTimeout(resolve, ms)); 43 | } 44 | 45 | /** 46 | * Reset strategy class 47 | * @interface ResetStrategy 48 | */ 49 | export interface ResetStrategy { 50 | transport: Transport; 51 | 52 | reset(): Promise; 53 | } 54 | 55 | /** 56 | * Execute a classic set of commands that will reset the chip. 57 | * 58 | * Commands (e.g. R0) are defined by a code (R) and an argument (0). 59 | * 60 | * The commands are: 61 | * 62 | * D: setDTR - 1=True / 0=False 63 | * 64 | * R: setRTS - 1=True / 0=False 65 | * 66 | * W: Wait (time delay) - positive integer number (miliseconds) 67 | * 68 | * "D0|R1|W100|D1|R0|W50|D0" represents the classic reset strategy 69 | * @param {Transport} transport Transport class to perform serial communication. 70 | * @param {number} resetDelay Delay in milliseconds for reset. 71 | */ 72 | export class ClassicReset implements ResetStrategy { 73 | resetDelay: number; 74 | transport: Transport; 75 | 76 | constructor(transport: Transport, resetDelay: number) { 77 | this.resetDelay = resetDelay; 78 | this.transport = transport; 79 | } 80 | 81 | async reset() { 82 | await this.transport.setDTR(false); 83 | await this.transport.setRTS(true); 84 | await sleep(100); 85 | await this.transport.setDTR(true); 86 | await this.transport.setRTS(false); 87 | await sleep(this.resetDelay); 88 | await this.transport.setDTR(false); 89 | } 90 | } 91 | 92 | /** 93 | * Execute a set of commands for USB JTAG serial reset. 94 | * 95 | * Commands (e.g. R0) are defined by a code (R) and an argument (0). 96 | * 97 | * The commands are: 98 | * 99 | * D: setDTR - 1=True / 0=False 100 | * 101 | * R: setRTS - 1=True / 0=False 102 | * 103 | * W: Wait (time delay) - positive integer number (miliseconds) 104 | * @param {Transport} transport Transport class to perform serial communication. 105 | */ 106 | export class UsbJtagSerialReset implements ResetStrategy { 107 | transport: Transport; 108 | 109 | constructor(transport: Transport) { 110 | this.transport = transport; 111 | } 112 | 113 | async reset() { 114 | await this.transport.setRTS(false); 115 | await this.transport.setDTR(false); 116 | await sleep(100); 117 | 118 | await this.transport.setDTR(true); 119 | await this.transport.setRTS(false); 120 | await sleep(100); 121 | 122 | await this.transport.setRTS(true); 123 | await this.transport.setDTR(false); 124 | await this.transport.setRTS(true); 125 | 126 | await sleep(100); 127 | await this.transport.setRTS(false); 128 | await this.transport.setDTR(false); 129 | } 130 | } 131 | 132 | /** 133 | * Execute a set of commands that will hard reset the chip. 134 | * 135 | * Commands (e.g. R0) are defined by a code (R) and an argument (0). 136 | * 137 | * The commands are: 138 | * 139 | * D: setDTR - 1=True / 0=False 140 | * 141 | * R: setRTS - 1=True / 0=False 142 | * 143 | * W: Wait (time delay) - positive integer number (miliseconds) 144 | * @param {Transport} transport Transport class to perform serial communication. 145 | * @param {boolean} usingUsbOtg is it using USB-OTG ? 146 | */ 147 | export class HardReset implements ResetStrategy { 148 | constructor(public transport: Transport, private usingUsbOtg = false) { 149 | this.transport = transport; 150 | } 151 | 152 | async reset() { 153 | if (this.usingUsbOtg) { 154 | await sleep(200); 155 | await this.transport.setRTS(false); 156 | await sleep(200); 157 | } else { 158 | await sleep(100); 159 | await this.transport.setRTS(false); 160 | } 161 | } 162 | } 163 | 164 | type CmdsArgsTypes = { 165 | D: boolean; 166 | R: boolean; 167 | W: number; 168 | }; 169 | 170 | /** 171 | * Validate a sequence string based on the following format: 172 | * 173 | * Commands (e.g. R0) are defined by a code (R) and an argument (0). 174 | * 175 | * The commands are: 176 | * 177 | * D: setDTR - 1=True / 0=False 178 | * 179 | * R: setRTS - 1=True / 0=False 180 | * 181 | * W: Wait (time delay) - positive integer number (miliseconds) 182 | * @param {string} seqStr Sequence string to validate 183 | * @returns {boolean} Is the sequence string valid ? 184 | */ 185 | export function validateCustomResetStringSequence(seqStr: string): boolean { 186 | const commands: (keyof CmdsArgsTypes)[] = ["D", "R", "W"]; 187 | 188 | const commandsList = seqStr.split("|"); 189 | 190 | for (const cmd of commandsList) { 191 | const code = cmd[0]; 192 | const arg = cmd.slice(1); 193 | 194 | if (!commands.includes(code as keyof CmdsArgsTypes)) { 195 | return false; // Invalid command code 196 | } 197 | 198 | if (code === "D" || code === "R") { 199 | if (arg !== "0" && arg !== "1") { 200 | return false; // Invalid argument for D and R commands 201 | } 202 | } else if (code === "W") { 203 | const delay = parseInt(arg); 204 | if (isNaN(delay) || delay <= 0) { 205 | return false; // Invalid argument for W command 206 | } 207 | } 208 | } 209 | return true; // All commands are valid 210 | } 211 | 212 | /** 213 | * Custom reset strategy defined with a string. 214 | * 215 | * The sequenceString input string consists of individual commands divided by "|". 216 | * 217 | * Commands (e.g. R0) are defined by a code (R) and an argument (0). 218 | * 219 | * The commands are: 220 | * 221 | * D: setDTR - 1=True / 0=False 222 | * 223 | * R: setRTS - 1=True / 0=False 224 | * 225 | * W: Wait (time delay) - positive integer number (miliseconds) 226 | * 227 | * "D0|R1|W100|D1|R0|W50|D0" represents the classic reset strategy 228 | * @param {Transport} transport Transport class to perform serial communication. 229 | * @param {string} sequenceString Custom string sequence for reset strategy 230 | */ 231 | export class CustomReset implements ResetStrategy { 232 | constructor(public transport: Transport, private sequenceString: string) { 233 | this.transport = transport; 234 | } 235 | 236 | async reset() { 237 | const resetDictionary: { [K in keyof CmdsArgsTypes]: (arg: CmdsArgsTypes[K]) => Promise } = { 238 | D: async (arg: boolean) => await this.transport.setDTR(arg), 239 | R: async (arg: boolean) => await this.transport.setRTS(arg), 240 | W: async (delay: number) => await sleep(delay), 241 | }; 242 | try { 243 | const isValidSequence = validateCustomResetStringSequence(this.sequenceString); 244 | if (!isValidSequence) { 245 | return; 246 | } 247 | const cmds = this.sequenceString.split("|"); 248 | for (const cmd of cmds) { 249 | const cmdKey = cmd[0]; 250 | const cmdVal = cmd.slice(1); 251 | if (cmdKey === "W") { 252 | await resetDictionary["W"](Number(cmdVal)); 253 | } else if (cmdKey === "D" || cmdKey === "R") { 254 | await resetDictionary[cmdKey as "D" | "R"](cmdVal === "1"); 255 | } 256 | } 257 | } catch (error) { 258 | throw new Error("Invalid custom reset sequence"); 259 | } 260 | } 261 | } 262 | 263 | export default { ClassicReset, CustomReset, HardReset, UsbJtagSerialReset, validateCustomResetStringSequence }; 264 | -------------------------------------------------------------------------------- /src/targets/esp32s3.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { ESP32ROM } from "./esp32.js"; 3 | import { MemoryMapEntry } from "./rom.js"; 4 | 5 | export class ESP32S3ROM extends ESP32ROM { 6 | public CHIP_NAME = "ESP32-S3"; 7 | public IMAGE_CHIP_ID = 9; 8 | public EFUSE_BASE = 0x60007000; 9 | public MAC_EFUSE_REG = this.EFUSE_BASE + 0x044; 10 | public EFUSE_BLOCK1_ADDR = this.EFUSE_BASE + 0x44; 11 | public EFUSE_BLOCK2_ADDR = this.EFUSE_BASE + 0x5c; 12 | public UART_CLKDIV_REG = 0x60000014; 13 | public UART_CLKDIV_MASK = 0xfffff; 14 | public UART_DATE_REG_ADDR = 0x60000080; 15 | 16 | public FLASH_WRITE_SIZE = 0x400; 17 | public BOOTLOADER_FLASH_OFFSET = 0x0; 18 | 19 | public SPI_REG_BASE = 0x60002000; 20 | public SPI_USR_OFFS = 0x18; 21 | public SPI_USR1_OFFS = 0x1c; 22 | public SPI_USR2_OFFS = 0x20; 23 | public SPI_MOSI_DLEN_OFFS = 0x24; 24 | public SPI_MISO_DLEN_OFFS = 0x28; 25 | public SPI_W0_OFFS = 0x58; 26 | 27 | public USB_RAM_BLOCK = 0x800; 28 | public UARTDEV_BUF_NO_USB = 3; 29 | public UARTDEV_BUF_NO = 0x3fcef14c; 30 | 31 | IROM_MAP_START = 0x42000000; 32 | IROM_MAP_END = 0x44000000; 33 | 34 | public MEMORY_MAP: MemoryMapEntry[] = [ 35 | [0x00000000, 0x00010000, "PADDING"], 36 | [0x3c000000, 0x3d000000, "DROM"], 37 | [0x3d000000, 0x3e000000, "EXTRAM_DATA"], 38 | [0x600fe000, 0x60100000, "RTC_DRAM"], 39 | [0x3fc88000, 0x3fd00000, "BYTE_ACCESSIBLE"], 40 | [0x3fc88000, 0x403e2000, "MEM_INTERNAL"], 41 | [0x3fc88000, 0x3fd00000, "DRAM"], 42 | [0x40000000, 0x4001a100, "IROM_MASK"], 43 | [0x40370000, 0x403e0000, "IRAM"], 44 | [0x600fe000, 0x60100000, "RTC_IRAM"], 45 | [0x42000000, 0x42800000, "IROM"], 46 | [0x50000000, 0x50002000, "RTC_DATA"], 47 | ]; 48 | 49 | public async getChipDescription(loader: ESPLoader) { 50 | const majorRev = await this.getMajorChipVersion(loader); 51 | const minorRev = await this.getMinorChipVersion(loader); 52 | const pkgVersion = await this.getPkgVersion(loader); 53 | 54 | const chipName: { [key: number]: string } = { 55 | 0: "ESP32-S3 (QFN56)", 56 | 1: "ESP32-S3-PICO-1 (LGA56)", 57 | }; 58 | return `${chipName[pkgVersion] || "unknown ESP32-S3"} (revision v${majorRev}.${minorRev})`; 59 | } 60 | 61 | public async getPkgVersion(loader: ESPLoader): Promise { 62 | const numWord = 3; 63 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 21) & 0x07; 64 | } 65 | 66 | public async getRawMinorChipVersion(loader: ESPLoader) { 67 | const hiNumWord = 5; 68 | const hi = ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * hiNumWord)) >> 23) & 0x01; 69 | const lowNumWord = 3; 70 | const low = ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * lowNumWord)) >> 18) & 0x07; 71 | return (hi << 3) + low; 72 | } 73 | 74 | public async getMinorChipVersion(loader: ESPLoader) { 75 | const minorRaw = await this.getRawMinorChipVersion(loader); 76 | if (await this.isEco0(loader, minorRaw)) { 77 | return 0; 78 | } 79 | return this.getRawMinorChipVersion(loader); 80 | } 81 | 82 | public async getRawMajorChipVersion(loader: ESPLoader) { 83 | const numWord = 5; 84 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 24) & 0x03; 85 | } 86 | 87 | public async getMajorChipVersion(loader: ESPLoader) { 88 | const minorRaw = await this.getRawMinorChipVersion(loader); 89 | if (await this.isEco0(loader, minorRaw)) { 90 | return 0; 91 | } 92 | return this.getRawMajorChipVersion(loader); 93 | } 94 | 95 | public async getBlkVersionMajor(loader: ESPLoader) { 96 | const numWord = 4; 97 | return ((await loader.readReg(this.EFUSE_BLOCK2_ADDR + 4 * numWord)) >> 0) & 0x03; 98 | } 99 | 100 | public async getBlkVersionMinor(loader: ESPLoader) { 101 | const numWord = 3; 102 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 24) & 0x07; 103 | } 104 | 105 | public async isEco0(loader: ESPLoader, minorRaw: number) { 106 | // Workaround: The major version field was allocated to other purposes 107 | // when block version is v1.1. 108 | // Luckily only chip v0.0 have this kind of block version and efuse usage. 109 | return ( 110 | (minorRaw & 0x7) === 0 && 111 | (await this.getBlkVersionMajor(loader)) === 1 && 112 | (await this.getBlkVersionMinor(loader)) === 1 113 | ); 114 | } 115 | 116 | public async getFlashCap(loader: ESPLoader): Promise { 117 | const numWord = 3; 118 | const block1Addr = this.EFUSE_BASE + 0x044; 119 | const addr = block1Addr + 4 * numWord; 120 | const registerValue = await loader.readReg(addr); 121 | const flashCap = (registerValue >> 27) & 0x07; 122 | return flashCap; 123 | } 124 | 125 | public async getFlashVendor(loader: ESPLoader): Promise { 126 | const numWord = 4; 127 | const block1Addr = this.EFUSE_BASE + 0x044; 128 | const addr = block1Addr + 4 * numWord; 129 | const registerValue = await loader.readReg(addr); 130 | const vendorId = (registerValue >> 0) & 0x07; 131 | const vendorMap: { [key: number]: string } = { 132 | 1: "XMC", 133 | 2: "GD", 134 | 3: "FM", 135 | 4: "TT", 136 | 5: "BY", 137 | }; 138 | return vendorMap[vendorId] || ""; 139 | } 140 | 141 | public async getPsramCap(loader: ESPLoader): Promise { 142 | const numWord = 4; 143 | const block1Addr = this.EFUSE_BASE + 0x044; 144 | const addr = block1Addr + 4 * numWord; 145 | const registerValue = await loader.readReg(addr); 146 | const psramCap = (registerValue >> 3) & 0x03; 147 | return psramCap; 148 | } 149 | 150 | public async getPsramVendor(loader: ESPLoader): Promise { 151 | const numWord = 4; 152 | const block1Addr = this.EFUSE_BASE + 0x044; 153 | const addr = block1Addr + 4 * numWord; 154 | const registerValue = await loader.readReg(addr); 155 | const vendorId = (registerValue >> 7) & 0x03; 156 | const vendorMap: { [key: number]: string } = { 157 | 1: "AP_3v3", 158 | 2: "AP_1v8", 159 | }; 160 | return vendorMap[vendorId] || ""; 161 | } 162 | 163 | public async getChipFeatures(loader: ESPLoader): Promise { 164 | const features: string[] = ["Wi-Fi", "BLE"]; 165 | 166 | const flashMap: { [key: number]: string | null } = { 167 | 0: null, 168 | 1: "Embedded Flash 8MB", 169 | 2: "Embedded Flash 4MB", 170 | }; 171 | const flashCap = await this.getFlashCap(loader); 172 | const flashVendor = await this.getFlashVendor(loader); 173 | const flash = flashMap[flashCap]; 174 | const flashDescription = flash !== undefined ? flash : "Unknown Embedded Flash"; 175 | if (flash !== null) { 176 | features.push(`${flashDescription} (${flashVendor})`); 177 | } 178 | 179 | const psramMap: { [key: number]: string | null } = { 180 | 0: null, 181 | 1: "Embedded PSRAM 8MB", 182 | 2: "Embedded PSRAM 2MB", 183 | }; 184 | const psramCap = await this.getPsramCap(loader); 185 | const psramVendor = await this.getPsramVendor(loader); 186 | const psram = psramMap[psramCap]; 187 | const psramDescription = psram !== undefined ? psram : "Unknown Embedded PSRAM"; 188 | if (psram !== null) { 189 | features.push(`${psramDescription} (${psramVendor})`); 190 | } 191 | return features; 192 | } 193 | 194 | public async getCrystalFreq(loader: ESPLoader) { 195 | return 40; 196 | } 197 | public _d2h(d: number) { 198 | const h = (+d).toString(16); 199 | return h.length === 1 ? "0" + h : h; 200 | } 201 | 202 | public async postConnect(loader: ESPLoader) { 203 | const bufNo = (await loader.readReg(this.UARTDEV_BUF_NO)) & 0xff; 204 | loader.debug("In _post_connect " + bufNo); 205 | if (bufNo == this.UARTDEV_BUF_NO_USB) { 206 | loader.ESP_RAM_BLOCK = this.USB_RAM_BLOCK; 207 | } 208 | } 209 | 210 | public async readMac(loader: ESPLoader) { 211 | let mac0 = await loader.readReg(this.MAC_EFUSE_REG); 212 | mac0 = mac0 >>> 0; 213 | let mac1 = await loader.readReg(this.MAC_EFUSE_REG + 4); 214 | mac1 = (mac1 >>> 0) & 0x0000ffff; 215 | const mac = new Uint8Array(6); 216 | mac[0] = (mac1 >> 8) & 0xff; 217 | mac[1] = mac1 & 0xff; 218 | mac[2] = (mac0 >> 24) & 0xff; 219 | mac[3] = (mac0 >> 16) & 0xff; 220 | mac[4] = (mac0 >> 8) & 0xff; 221 | mac[5] = mac0 & 0xff; 222 | 223 | return ( 224 | this._d2h(mac[0]) + 225 | ":" + 226 | this._d2h(mac[1]) + 227 | ":" + 228 | this._d2h(mac[2]) + 229 | ":" + 230 | this._d2h(mac[3]) + 231 | ":" + 232 | this._d2h(mac[4]) + 233 | ":" + 234 | this._d2h(mac[5]) 235 | ); 236 | } 237 | 238 | public getEraseSize(offset: number, size: number) { 239 | return size; 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/targets/esp32s2.ts: -------------------------------------------------------------------------------- 1 | import { ESPLoader } from "../esploader.js"; 2 | import { ESP32ROM } from "./esp32.js"; 3 | import { MemoryMapEntry } from "./rom.js"; 4 | 5 | export class ESP32S2ROM extends ESP32ROM { 6 | public CHIP_NAME = "ESP32-S2"; 7 | public IMAGE_CHIP_ID = 2; 8 | public IROM_MAP_START = 0x40080000; 9 | public IROM_MAP_END = 0x40b80000; 10 | public DROM_MAP_START = 0x3f000000; 11 | public DROM_MAP_END = 0x3f3f0000; 12 | 13 | public CHIP_DETECT_MAGIC_VALUE = [0x000007c6]; 14 | 15 | public SPI_REG_BASE = 0x3f402000; 16 | public SPI_USR_OFFS = 0x18; 17 | public SPI_USR1_OFFS = 0x1c; 18 | public SPI_USR2_OFFS = 0x20; 19 | public SPI_MOSI_DLEN_OFFS = 0x24; 20 | public SPI_MISO_DLEN_OFFS = 0x28; 21 | public SPI_W0_OFFS = 0x58; 22 | 23 | public SPI_ADDR_REG_MSB = false; 24 | 25 | public MAC_EFUSE_REG = 0x3f41a044; // ESP32-S2 has special block for MAC efuses 26 | 27 | public UART_CLKDIV_REG = 0x3f400014; 28 | 29 | public SUPPORTS_ENCRYPTED_FLASH = true; 30 | 31 | public FLASH_ENCRYPTED_WRITE_ALIGN = 16; 32 | 33 | // todo: use espefuse APIs to get this info 34 | public EFUSE_BASE = 0x3f41a000; 35 | public EFUSE_RD_REG_BASE = this.EFUSE_BASE + 0x030; // BLOCK0 read base address 36 | public EFUSE_BLOCK1_ADDR = this.EFUSE_BASE + 0x044; 37 | public EFUSE_BLOCK2_ADDR = this.EFUSE_BASE + 0x05c; 38 | 39 | public EFUSE_PURPOSE_KEY0_REG = this.EFUSE_BASE + 0x34; 40 | public EFUSE_PURPOSE_KEY0_SHIFT = 24; 41 | public EFUSE_PURPOSE_KEY1_REG = this.EFUSE_BASE + 0x34; 42 | public EFUSE_PURPOSE_KEY1_SHIFT = 28; 43 | public EFUSE_PURPOSE_KEY2_REG = this.EFUSE_BASE + 0x38; 44 | public EFUSE_PURPOSE_KEY2_SHIFT = 0; 45 | public EFUSE_PURPOSE_KEY3_REG = this.EFUSE_BASE + 0x38; 46 | public EFUSE_PURPOSE_KEY3_SHIFT = 4; 47 | public EFUSE_PURPOSE_KEY4_REG = this.EFUSE_BASE + 0x38; 48 | public EFUSE_PURPOSE_KEY4_SHIFT = 8; 49 | public EFUSE_PURPOSE_KEY5_REG = this.EFUSE_BASE + 0x38; 50 | public EFUSE_PURPOSE_KEY5_SHIFT = 12; 51 | 52 | public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = this.EFUSE_RD_REG_BASE; 53 | public EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 19; 54 | 55 | public EFUSE_SPI_BOOT_CRYPT_CNT_REG = this.EFUSE_BASE + 0x034; 56 | public EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18; 57 | 58 | public EFUSE_SECURE_BOOT_EN_REG = this.EFUSE_BASE + 0x038; 59 | public EFUSE_SECURE_BOOT_EN_MASK = 1 << 20; 60 | 61 | public EFUSE_RD_REPEAT_DATA3_REG = this.EFUSE_BASE + 0x3c; 62 | public EFUSE_RD_REPEAT_DATA3_REG_FLASH_TYPE_MASK = 1 << 9; 63 | 64 | public PURPOSE_VAL_XTS_AES256_KEY_1 = 2; 65 | public PURPOSE_VAL_XTS_AES256_KEY_2 = 3; 66 | public PURPOSE_VAL_XTS_AES128_KEY = 4; 67 | 68 | public UARTDEV_BUF_NO = 0x3ffffd14; // Variable in ROM .bss which indicates the port in use 69 | public UARTDEV_BUF_NO_USB_OTG = 2; // Value of the above indicating that USB-OTG is in use 70 | 71 | public USB_RAM_BLOCK = 0x800; // Max block size USB-OTG is used 72 | 73 | public GPIO_STRAP_REG = 0x3f404038; 74 | public GPIO_STRAP_SPI_BOOT_MASK = 1 << 3; // Not download mode 75 | public GPIO_STRAP_VDDSPI_MASK = 1 << 4; 76 | public RTC_CNTL_OPTION1_REG = 0x3f408128; 77 | public RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1; // Is download mode forced over USB? 78 | 79 | public RTCCNTL_BASE_REG = 0x3f408000; 80 | public RTC_CNTL_WDTCONFIG0_REG = this.RTCCNTL_BASE_REG + 0x0094; 81 | public RTC_CNTL_WDTCONFIG1_REG = this.RTCCNTL_BASE_REG + 0x0098; 82 | public RTC_CNTL_WDTWPROTECT_REG = this.RTCCNTL_BASE_REG + 0x00ac; 83 | public RTC_CNTL_WDT_WKEY = 0x50d83aa1; 84 | 85 | public MEMORY_MAP: MemoryMapEntry[] = [ 86 | [0x00000000, 0x00010000, "PADDING"], 87 | [0x3f000000, 0x3ff80000, "DROM"], 88 | [0x3f500000, 0x3ff80000, "EXTRAM_DATA"], 89 | [0x3ff9e000, 0x3ffa0000, "RTC_DRAM"], 90 | [0x3ff9e000, 0x40000000, "BYTE_ACCESSIBLE"], 91 | [0x3ff9e000, 0x40072000, "MEM_INTERNAL"], 92 | [0x3ffb0000, 0x40000000, "DRAM"], 93 | [0x40000000, 0x4001a100, "IROM_MASK"], 94 | [0x40020000, 0x40070000, "IRAM"], 95 | [0x40070000, 0x40072000, "RTC_IRAM"], 96 | [0x40080000, 0x40800000, "IROM"], 97 | [0x50000000, 0x50002000, "RTC_DATA"], 98 | ]; 99 | 100 | public EFUSE_VDD_SPI_REG = this.EFUSE_BASE + 0x34; 101 | public VDD_SPI_XPD = 1 << 4; 102 | public VDD_SPI_TIEH = 1 << 5; 103 | public VDD_SPI_FORCE = 1 << 6; 104 | 105 | public UF2_FAMILY_ID = 0xbfdd4eee; 106 | 107 | public EFUSE_MAX_KEY = 5; 108 | public KEY_PURPOSES = { 109 | 0: "USER/EMPTY", 110 | 1: "RESERVED", 111 | 2: "XTS_AES_256_KEY_1", 112 | 3: "XTS_AES_256_KEY_2", 113 | 4: "XTS_AES_128_KEY", 114 | 5: "HMAC_DOWN_ALL", 115 | 6: "HMAC_DOWN_JTAG", 116 | 7: "HMAC_DOWN_DIGITAL_SIGNATURE", 117 | 8: "HMAC_UP", 118 | 9: "SECURE_BOOT_DIGEST0", 119 | 10: "SECURE_BOOT_DIGEST1", 120 | 11: "SECURE_BOOT_DIGEST2", 121 | }; 122 | 123 | public UART_CLKDIV_MASK = 0xfffff; 124 | public UART_DATE_REG_ADDR = 0x60000078; 125 | 126 | public FLASH_WRITE_SIZE = 0x400; 127 | public BOOTLOADER_FLASH_OFFSET = 0x1000; 128 | 129 | public async getPkgVersion(loader: ESPLoader): Promise { 130 | const numWord = 4; 131 | const addr = this.EFUSE_BLOCK1_ADDR + 4 * numWord; 132 | const word = await loader.readReg(addr); 133 | const pkgVersion = (word >> 0) & 0x0f; 134 | return pkgVersion; 135 | } 136 | 137 | public async getMinorChipVersion(loader: ESPLoader) { 138 | const hiNumWord = 3; 139 | const hi = ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * hiNumWord)) >> 20) & 0x01; 140 | const lowNumWord = 4; 141 | const low = ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * lowNumWord)) >> 4) & 0x07; 142 | return (hi << 3) + low; 143 | } 144 | 145 | public async getMajorChipVersion(loader: ESPLoader) { 146 | const numWord = 3; 147 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 18) & 0x03; 148 | } 149 | 150 | public async getFlashVersion(loader: ESPLoader) { 151 | const numWord = 3; 152 | return ((await loader.readReg(this.EFUSE_BLOCK1_ADDR + 4 * numWord)) >> 21) & 0x0f; 153 | } 154 | 155 | public async getChipDescription(loader: ESPLoader) { 156 | const chipDesc: { [key: number]: string } = { 157 | 0: "ESP32-S2", 158 | 1: "ESP32-S2FH2", 159 | 2: "ESP32-S2FH4", 160 | 102: "ESP32-S2FNR2", 161 | 100: "ESP32-S2R2", 162 | }; 163 | const chipIndex = (await this.getFlashCap(loader)) + (await this.getPsramCap(loader)) * 100; 164 | const majorRev = await this.getMajorChipVersion(loader); 165 | const minorRev = await this.getMinorChipVersion(loader); 166 | return `${chipDesc[chipIndex] || "unknown ESP32-S2"} (revision v${majorRev}.${minorRev})`; 167 | } 168 | 169 | public async getFlashCap(loader: ESPLoader): Promise { 170 | return await this.getFlashVersion(loader); 171 | } 172 | 173 | public async getPsramVersion(loader: ESPLoader): Promise { 174 | const numWord = 3; 175 | const addr = this.EFUSE_BLOCK1_ADDR + 4 * numWord; 176 | const registerValue = await loader.readReg(addr); 177 | const psramCap = (registerValue >> 28) & 0x0f; 178 | return psramCap; 179 | } 180 | 181 | public async getPsramCap(loader: ESPLoader): Promise { 182 | return await this.getPsramVersion(loader); 183 | } 184 | 185 | public async getBlock2Version(loader: ESPLoader): Promise { 186 | const numWord = 4; 187 | const addr = this.EFUSE_BLOCK2_ADDR + 4 * numWord; 188 | const registerValue = await loader.readReg(addr); 189 | const block2Ver = (registerValue >> 4) & 0x07; 190 | return block2Ver; 191 | } 192 | 193 | public async getChipFeatures(loader: ESPLoader) { 194 | const features: string[] = ["Wi-Fi"]; 195 | 196 | const flashMap: { [key: number]: string | null } = { 197 | 0: "No Embedded Flash", 198 | 1: "Embedded Flash 2MB", 199 | 2: "Embedded Flash 4MB", 200 | }; 201 | const flashCap = await this.getFlashCap(loader); 202 | const flashDescription = flashMap[flashCap] || "Unknown Embedded Flash"; 203 | features.push(flashDescription); 204 | 205 | const psramMap: { [key: number]: string | null } = { 206 | 0: "No Embedded Flash", 207 | 1: "Embedded PSRAM 2MB", 208 | 2: "Embedded PSRAM 4MB", 209 | }; 210 | const psramCap = await this.getPsramCap(loader); 211 | const psramDescription = psramMap[psramCap] || "Unknown Embedded PSRAM"; 212 | features.push(psramDescription); 213 | 214 | const block2VersionMap: { [key: number]: string | null } = { 215 | 0: "No calibration in BLK2 of efuse", 216 | 1: "ADC and temperature sensor calibration in BLK2 of efuse V1", 217 | 2: "ADC and temperature sensor calibration in BLK2 of efuse V2", 218 | }; 219 | const block2Ver = await this.getBlock2Version(loader); 220 | const block2VersionDescription = block2VersionMap[block2Ver] || "Unknown Calibration in BLK2"; 221 | features.push(block2VersionDescription); 222 | 223 | return features; 224 | } 225 | 226 | public async getCrystalFreq(loader: ESPLoader) { 227 | return 40; 228 | } 229 | public _d2h(d: number) { 230 | const h = (+d).toString(16); 231 | return h.length === 1 ? "0" + h : h; 232 | } 233 | public async readMac(loader: ESPLoader) { 234 | let mac0 = await loader.readReg(this.MAC_EFUSE_REG); 235 | mac0 = mac0 >>> 0; 236 | let mac1 = await loader.readReg(this.MAC_EFUSE_REG + 4); 237 | mac1 = (mac1 >>> 0) & 0x0000ffff; 238 | const mac = new Uint8Array(6); 239 | mac[0] = (mac1 >> 8) & 0xff; 240 | mac[1] = mac1 & 0xff; 241 | mac[2] = (mac0 >> 24) & 0xff; 242 | mac[3] = (mac0 >> 16) & 0xff; 243 | mac[4] = (mac0 >> 8) & 0xff; 244 | mac[5] = mac0 & 0xff; 245 | 246 | return ( 247 | this._d2h(mac[0]) + 248 | ":" + 249 | this._d2h(mac[1]) + 250 | ":" + 251 | this._d2h(mac[2]) + 252 | ":" + 253 | this._d2h(mac[3]) + 254 | ":" + 255 | this._d2h(mac[4]) + 256 | ":" + 257 | this._d2h(mac[5]) 258 | ); 259 | } 260 | 261 | public getEraseSize(offset: number, size: number) { 262 | return size; 263 | } 264 | 265 | public async usingUsbOtg(loader: ESPLoader) { 266 | const uartNo = (await loader.readReg(this.UARTDEV_BUF_NO)) & 0xff; 267 | return uartNo === this.UARTDEV_BUF_NO_USB_OTG; 268 | } 269 | 270 | public async postConnect(loader: ESPLoader) { 271 | const usingUsbOtg = await this.usingUsbOtg(loader); 272 | loader.debug("In _post_connect using USB OTG ?" + usingUsbOtg); 273 | if (usingUsbOtg) { 274 | loader.ESP_RAM_BLOCK = this.USB_RAM_BLOCK; 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/targets/stub_flasher/stub_flasher_8266.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": 1074843652, 3 | "text": "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", 4 | "text_start": 1074843648, 5 | "data": "CIH+PwUFBAACAwcAAwMLANTXEEAL2BBAOdgQQNbYEECF5xBAOtkQQJDZEEDc2RBAhecQQKLaEEAf2xBA4NsQQIXnEECF5xBAeNwQQIXnEEBV3xBAHOAQQFfgEECF5xBAhecQQPPgEECF5xBA2+EQQIHiEEDA4xBAf+QQQFDlEECF5xBAhecQQIXnEECF5xBAfuYQQIXnEEB05xBAsN0QQKnYEEDC5RBAydoQQBvaEECF5xBACOcQQE/nEECF5xBAhecQQIXnEECF5xBAhecQQIXnEECF5xBAhecQQELaEEB/2hBA2uUQQAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAkAAAANAAAAEQAAABkAAAAhAAAAMQAAAEEAAABhAAAAgQAAAMEAAAABAQAAgQEAAAECAAABAwAAAQQAAAEGAAABCAAAAQwAAAEQAAABGAAAASAAAAEwAAABQAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAEAAAABQAAAAUAAAAGAAAABgAAAAcAAAAHAAAACAAAAAgAAAAJAAAACQAAAAoAAAAKAAAACwAAAAsAAAAMAAAADAAAAA0AAAANAAAAAAAAAAAAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAANAAAADwAAABEAAAATAAAAFwAAABsAAAAfAAAAIwAAACsAAAAzAAAAOwAAAEMAAABTAAAAYwAAAHMAAACDAAAAowAAAMMAAADjAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABQAAAAAAAAAAAAAAAAAAABAREgAIBwkGCgULBAwDDQIOAQ8AAQEAAAEAAAAEAAAA", 6 | "data_start": 1073720488, 7 | "bss_start": 1073643776 8 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2019 Espressif Systems (Shanghai) CO LTD 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /src/image/esp32.ts: -------------------------------------------------------------------------------- 1 | import { bstrToUi8, ESP_CHECKSUM_MAGIC } from "../util"; 2 | import { alignFilePosition, BaseFirmwareImage, ELFSection, ESP_IMAGE_MAGIC, ImageSegment } from "./base"; 3 | import { ESPError } from "../types/error"; 4 | import { ROM } from "../targets/rom"; 5 | import { ESP32ROM } from "../targets/esp32"; 6 | 7 | export class ESP32FirmwareImage extends BaseFirmwareImage { 8 | securePad: string | null = null; 9 | flashMode = 0; 10 | flashSizeFreq = 0; 11 | version = 1; 12 | ramOnlyHeader: boolean; 13 | 14 | // ROM bootloader will read the wp_pin field if SPI flash 15 | // pins are remapped via flash. IDF actually enables QIO only 16 | // from software bootloader, so this can be ignored. But needs 17 | // to be set to this value so ROM bootloader will skip it. 18 | WP_PIN_DISABLED = 0xee; 19 | 20 | wpPin: number = this.WP_PIN_DISABLED; 21 | 22 | // Extended header fields 23 | clkDrv = 0; 24 | qDrv = 0; 25 | dDrv = 0; 26 | csDrv = 0; 27 | hdDrv = 0; 28 | wpDrv = 0; 29 | chipId = 0; 30 | minRev = 0; 31 | minRevFull = 0; 32 | maxRevFull = 0; 33 | 34 | appendDigest: boolean; 35 | storedDigest: Uint8Array | null = null; 36 | calcDigest: Uint8Array | null = null; 37 | dataLength = 0; 38 | 39 | IROM_ALIGN = 65536; 40 | 41 | ROM_LOADER: ESP32ROM; 42 | 43 | constructor(rom: ROM, loadFile: string | null = null, appendDigest = true, ramOnlyHeader = false) { 44 | super(rom); 45 | this.ROM_LOADER = rom as ESP32ROM; 46 | this.appendDigest = appendDigest; 47 | this.ramOnlyHeader = ramOnlyHeader; 48 | 49 | if (loadFile !== null) { 50 | this.loadFromFile(loadFile); 51 | } 52 | } 53 | 54 | async loadFromFile(loadFile: string): Promise { 55 | const start = 0; 56 | const binaryData = bstrToUi8(loadFile); 57 | let offset = 0; 58 | 59 | const segments = this.loadCommonHeader(binaryData, offset, ESP_IMAGE_MAGIC); 60 | offset += 8; 61 | 62 | this.loadExtendedHeader(binaryData, offset); 63 | offset += 16; // Extended header is 16 bytes 64 | 65 | // Load segments 66 | for (let i = 0; i < segments; i++) { 67 | const segment = this.loadSegment(binaryData, offset); 68 | offset += 8 + segment.data.length; 69 | } 70 | 71 | // Read checksum 72 | this.checksum = this.readChecksum(binaryData, offset); 73 | offset = alignFilePosition(offset, 16); 74 | 75 | if (this.appendDigest) { 76 | const end = offset; 77 | this.storedDigest = binaryData.slice(offset, offset + this.SHA256_DIGEST_LEN); 78 | 79 | const shaDigest = await crypto.subtle.digest("SHA-256", binaryData.slice(start, end)); 80 | this.calcDigest = new Uint8Array(shaDigest); 81 | this.dataLength = end - start; 82 | } 83 | 84 | this.verify(); 85 | } 86 | 87 | isFlashAddr(addr: number): boolean { 88 | return ( 89 | (this.ROM_LOADER.IROM_MAP_START <= addr && addr < this.ROM_LOADER.IROM_MAP_END) || 90 | (this.ROM_LOADER.DROM_MAP_START <= addr && addr < this.ROM_LOADER.DROM_MAP_END) 91 | ); 92 | } 93 | 94 | async save() { 95 | let totalSegments = 0; 96 | const output = new Uint8Array(1024 * 1024); // Start with 1MB buffer, will grow if needed 97 | let offset = 0; 98 | 99 | // Write common header 100 | this.writeCommonHeader(output, offset, this.segments.length); 101 | offset += 8; 102 | 103 | // Write extended header 104 | this.saveExtendedHeader(output, offset); 105 | offset += 16; 106 | 107 | let checksum = ESP_CHECKSUM_MAGIC; 108 | 109 | // Split segments into flash-mapped vs ram-loaded 110 | const flashSegments = this.segments.filter((s) => this.isFlashAddr(s.addr)).sort((a, b) => a.addr - b.addr); 111 | 112 | const ramSegments = this.segments.filter((s) => !this.isFlashAddr(s.addr)).sort((a, b) => a.addr - b.addr); 113 | 114 | // Patch to support ESP32-C6 union bus memmap 115 | // move ".flash.appdesc" segment to the top of the flash segment 116 | for (let i = 0; i < flashSegments.length; i++) { 117 | const segment = flashSegments[i]; 118 | if (segment instanceof ELFSection && segment.name === ".flash.appdesc") { 119 | flashSegments.splice(i, 1); 120 | flashSegments.unshift(segment); 121 | break; 122 | } 123 | } 124 | 125 | // For the bootloader image 126 | // move ".dram0.bootdesc" segment to the top of the ram segment 127 | // So bootdesc will be at the very top of the binary at 0x20 offset 128 | // (in the first segment). 129 | for (let i = 0; i < ramSegments.length; i++) { 130 | const segment = ramSegments[i]; 131 | if (segment instanceof ELFSection && segment.name === ".dram0.bootdesc") { 132 | ramSegments.splice(i, 1); 133 | ramSegments.unshift(segment); 134 | break; 135 | } 136 | } 137 | 138 | // Check for multiple ELF sections in same flash mapping region 139 | if (flashSegments.length > 0) { 140 | let lastAddr = flashSegments[0].addr; 141 | for (const segment of flashSegments.slice(1)) { 142 | if (Math.floor(segment.addr / this.IROM_ALIGN) === Math.floor(lastAddr / this.IROM_ALIGN)) { 143 | throw new ESPError( 144 | `Segment loaded at 0x${segment.addr.toString(16)} lands in same 64KB flash mapping ` + 145 | `as segment loaded at 0x${lastAddr.toString(16)}. Can't generate binary. ` + 146 | "Suggest changing linker script or ELF to merge sections.", 147 | ); 148 | } 149 | lastAddr = segment.addr; 150 | } 151 | } 152 | 153 | if (this.ramOnlyHeader) { 154 | // Write RAM segments first 155 | for (const segment of ramSegments) { 156 | checksum = this.saveSegment(output, offset, segment, checksum); 157 | offset += 8 + segment.data.length; 158 | totalSegments++; 159 | } 160 | this.appendChecksum(output, offset, checksum); 161 | offset = alignFilePosition(offset, 16); 162 | 163 | // Write flash segments 164 | for (const segment of flashSegments.reverse()) { 165 | let padLen = this.getAlignmentDataNeeded(segment, offset); 166 | if (padLen > 0) { 167 | const align_min = this.ROM_LOADER.BOOTLOADER_FLASH_OFFSET - this.SEG_HEADER_LEN; 168 | if (padLen < align_min) { 169 | // in case pad_len does not fit minimum alignment, 170 | // pad it to next aligned boundary 171 | padLen += this.IROM_ALIGN; 172 | } 173 | 174 | padLen -= this.ROM_LOADER.BOOTLOADER_FLASH_OFFSET; 175 | const padSegment = new ImageSegment(0, new Uint8Array(padLen).fill(0), offset); 176 | checksum = this.saveSegment(output, offset, padSegment, checksum); 177 | offset += 8 + padLen; 178 | totalSegments++; 179 | } 180 | this.saveFlashSegment(output, offset, segment); 181 | offset += 8 + segment.data.length; 182 | totalSegments++; 183 | } 184 | } else { 185 | // Write flash segments with padding 186 | while (flashSegments.length > 0) { 187 | const segment = flashSegments[0]; 188 | const padLen = this.getAlignmentDataNeeded(segment, offset); 189 | 190 | if (padLen > 0) { 191 | // need to pad 192 | if (ramSegments.length > 0 && padLen > this.SEG_HEADER_LEN) { 193 | // Split a part of the first RAM segment to use as padding 194 | const padSegment = ramSegments[0].splitImage(padLen); 195 | if (ramSegments[0].data.length === 0) { 196 | ramSegments.shift(); 197 | } 198 | checksum = this.saveSegment(output, offset, padSegment, checksum); 199 | } else { 200 | // Use zero padding 201 | const padSegment = new ImageSegment(0, new Uint8Array(padLen).fill(0), offset); 202 | checksum = this.saveSegment(output, offset, padSegment, checksum); 203 | } 204 | offset += 8 + padLen; 205 | totalSegments++; 206 | } else { 207 | // write the flash segment 208 | if ((offset + 8) % this.IROM_ALIGN !== segment.addr % this.IROM_ALIGN) { 209 | throw new Error("Flash segment alignment mismatch"); 210 | } 211 | checksum = this.saveFlashSegment(output, offset, segment, checksum); 212 | flashSegments.shift(); 213 | offset += 8 + segment.data.length; 214 | totalSegments++; 215 | } 216 | } 217 | 218 | // Write remaining RAM segments 219 | for (const segment of ramSegments) { 220 | checksum = this.saveSegment(output, offset, segment, checksum); 221 | offset += 8 + segment.data.length; 222 | totalSegments++; 223 | } 224 | } 225 | 226 | // Handle secure padding if needed 227 | if (this.securePad) { 228 | if (!this.appendDigest) { 229 | throw new Error("secure_pad only applies if a SHA-256 digest is also appended to the image"); 230 | } 231 | 232 | const alignPast = (offset + this.SEG_HEADER_LEN) % this.IROM_ALIGN; 233 | const checksumSpace = 16; // 16 byte aligned checksum 234 | 235 | let spaceAfterChecksum = 0; 236 | if (this.securePad === "1") { 237 | // Secure Boot V1: SHA-256 digest + version + signature + 12 trailing bytes 238 | spaceAfterChecksum = 32 + 4 + 64 + 12; 239 | } else if (this.securePad === "2") { 240 | // Secure Boot V2: SHA-256 digest + signature sector (placed after 64KB boundary) 241 | spaceAfterChecksum = 32; 242 | } 243 | 244 | const padLen = (this.IROM_ALIGN - alignPast - checksumSpace - spaceAfterChecksum) % this.IROM_ALIGN; 245 | const padSegment = new ImageSegment(0, new Uint8Array(padLen).fill(0), offset); 246 | checksum = this.saveSegment(output, offset, padSegment, checksum); 247 | offset += 8 + padLen; 248 | totalSegments++; 249 | } 250 | 251 | // Append checksum after all segments are written 252 | if (!this.ramOnlyHeader) { 253 | this.appendChecksum(output, offset, checksum); 254 | offset = alignFilePosition(offset, 16); 255 | } 256 | 257 | const imageLength = offset; 258 | 259 | // Go back to the initial header and write the new segment count 260 | // This header is not checksummed 261 | if (this.ramOnlyHeader) { 262 | // Update the header with the RAM segments quantity as it should be 263 | // visible by the ROM bootloader 264 | output[1] = ramSegments.length; 265 | } else { 266 | output[1] = totalSegments; 267 | } 268 | 269 | if (this.appendDigest) { 270 | // calculate the SHA256 of the whole file and append it 271 | const shaDigest = await crypto.subtle.digest("SHA-256", output.slice(0, imageLength)); 272 | const digest = new Uint8Array(shaDigest); 273 | 274 | output.set(digest, imageLength); 275 | offset += 32; 276 | } 277 | 278 | if (this.padToSize) { 279 | if (offset % this.padToSize !== 0) { 280 | const padBy = this.padToSize - (offset % this.padToSize); 281 | const padding = new Uint8Array(padBy); 282 | padding.fill(0xff); 283 | output.set(padding, offset); 284 | offset += padBy; 285 | } 286 | } 287 | 288 | return output; 289 | } 290 | 291 | loadExtendedHeader(data: Uint8Array, offset: number): void { 292 | const view = new DataView(data.buffer, offset); 293 | 294 | this.wpPin = view.getUint8(0); 295 | 296 | const driveConfig = view.getUint8(1); 297 | [this.clkDrv, this.qDrv] = this.splitByte(driveConfig); 298 | const dConfig = view.getUint8(2); 299 | [this.dDrv, this.csDrv] = this.splitByte(dConfig); 300 | const hdConfig = view.getUint8(3); 301 | [this.hdDrv, this.wpDrv] = this.splitByte(hdConfig); 302 | 303 | this.chipId = view.getUint8(4); 304 | if (this.chipId !== this.ROM_LOADER.IMAGE_CHIP_ID) { 305 | console.warn( 306 | `Unexpected chip id in image. Expected ${this.ROM_LOADER.IMAGE_CHIP_ID} but value was ${this.chipId}. ` + 307 | "Is this image for a different chip model?", 308 | ); 309 | } 310 | this.minRev = view.getUint8(5); 311 | this.minRevFull = view.getUint16(6, true); 312 | this.maxRevFull = view.getUint16(8, true); 313 | 314 | // Last byte is append_digest validation 315 | const appendDigest = view.getUint8(15); 316 | if (appendDigest === 0 || appendDigest === 1) { 317 | this.appendDigest = appendDigest === 1; 318 | } else { 319 | throw new Error(`Invalid value for append_digest field (0x${appendDigest.toString(16)}). Should be 0 or 1.`); 320 | } 321 | } 322 | 323 | saveExtendedHeader(output: Uint8Array, offset: number): void { 324 | const headerBuffer = new ArrayBuffer(16); 325 | const view = new DataView(headerBuffer); 326 | 327 | view.setUint8(0, this.wpPin); 328 | 329 | view.setUint8(1, this.joinByte(this.clkDrv, this.qDrv)); 330 | view.setUint8(2, this.joinByte(this.dDrv, this.csDrv)); 331 | view.setUint8(3, this.joinByte(this.hdDrv, this.wpDrv)); 332 | 333 | view.setUint8(4, this.ROM_LOADER.IMAGE_CHIP_ID); 334 | view.setUint8(5, this.minRev); 335 | view.setUint16(6, this.minRevFull, true); 336 | view.setUint16(8, this.maxRevFull, true); 337 | 338 | for (let i = 9; i < 15; i++) { 339 | view.setUint8(i, 0); 340 | } 341 | view.setUint8(15, this.appendDigest ? 1 : 0); 342 | 343 | // Copy the header buffer to the output 344 | output.set(new Uint8Array(headerBuffer), offset); 345 | } 346 | 347 | private splitByte(n: number): [number, number] { 348 | return [n & 0x0f, (n >> 4) & 0x0f]; 349 | } 350 | 351 | private joinByte(ln: number, hn: number): number { 352 | return (ln & 0x0f) | ((hn & 0x0f) << 4); 353 | } 354 | 355 | private getAlignmentDataNeeded(segment: ImageSegment, currentOffset: number): number { 356 | // Calculate alignment needed for segment 357 | const alignPast = (segment.addr % this.IROM_ALIGN) - this.SEG_HEADER_LEN; 358 | let padLen = this.IROM_ALIGN - (currentOffset % this.IROM_ALIGN) + alignPast; 359 | 360 | if (padLen === 0 || padLen === this.IROM_ALIGN) { 361 | return 0; // already aligned 362 | } 363 | 364 | padLen -= this.SEG_HEADER_LEN; 365 | if (padLen < 0) { 366 | padLen += this.IROM_ALIGN; 367 | } 368 | return padLen; 369 | } 370 | } 371 | --------------------------------------------------------------------------------