├── assets └── fulu-bounties.png ├── shell.nix ├── .gitignore ├── src └── omap_loader │ ├── Makefile │ ├── README.md │ ├── LICENSE.md │ └── omap_loader.c ├── LICENSE ├── install.sh ├── README.md └── patches └── omap_loader_mac.patch /assets/fulu-bounties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codykociemba/NoLongerEvil-Thermostat/HEAD/assets/fulu-bounties.png -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import { }, 3 | }: 4 | pkgs.mkShell { 5 | buildInputs = with pkgs; [ 6 | pkg-config 7 | libusb1 8 | ]; 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binary directories 2 | bin/ 3 | bin/firmware/ 4 | 5 | # Compiled binaries 6 | *.bin 7 | *.exe 8 | 9 | # Build artifacts 10 | *.o 11 | *.a 12 | *.so 13 | *.dylib 14 | 15 | # Temporary files 16 | *.zip 17 | *.tar.gz 18 | -------------------------------------------------------------------------------- /src/omap_loader/Makefile: -------------------------------------------------------------------------------- 1 | # OMAP Loader Makefile 2 | # Written by Grant Hernandez 3 | CC=gcc 4 | 5 | INCLUDEDIR=$(shell pkg-config --cflags libusb-1.0) 6 | LIBS=$(shell pkg-config --libs libusb-1.0) 7 | 8 | CFLAGS=-Wall -O2 $(INCLUDEDIR) 9 | LDFLAGS= 10 | EXECNAME=omap_loader 11 | 12 | SRCS=omap_loader.c 13 | OBJS = $(SRCS:.c=.o) 14 | 15 | ifdef CROSS_COMPILE 16 | CC := $(CROSS_COMPILE)$(CC) 17 | CXX := $(CROSS_COMPILE)$(CC) 18 | endif 19 | 20 | all: $(OBJS) 21 | $(CC) $(LDFLAGS) -o $(EXECNAME) $(OBJS) $(LIBS) 22 | 23 | clean: 24 | -rm -f *.o $(EXECNAME) 25 | 26 | .c.o: 27 | $(CC) $(CFLAGS) -c $< -o $@ 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Cody Kociemba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/omap_loader/README.md: -------------------------------------------------------------------------------- 1 | OMAP Loader 2 | =========== 3 | OMAP Loader is a libUSB 1.0 rewrite of the omap3\_usbload utility. 4 | Essentially it is a USB BootROM uploading utility for TI ARM OMAP3 family processors. 5 | The motivation to rewrite this came from using the utility to upload code 6 | on to Nest Thermostats. 7 | 8 | This rewrite is versioned and implements all of the original features. 9 | Supports USBLOAD functionality in TI's X-Loader (Google: omap3\_usbload for more info) 10 | 11 | Build Requirements 12 | ------- 13 | * GCC or a compatible C compiler is required 14 | * GNU Make 15 | * libUSB 1.0.X or higher 16 | 17 | To install the required libusb version on Ubuntu or Debian run `sudo apt-get install libusb-1.0` 18 | 19 | Building 20 | ------- 21 | ``` 22 | ~/omap_usbload $ make 23 | gcc -Wall -O2 -I/usr/include/libusb-1.0 -c omap_loader.c -o omap_loader.o 24 | gcc -o omap_loader omap_loader.o -lusb-1.0 25 | ~/omap_usbload $ ./omap_loader --help 26 | ``` 27 | 28 | Running 29 | ------- 30 | ``` 31 | OMAP Loader 1.0.0 32 | Usage: ./omap_loader [options] -f first-stage [-f file -a addr]... 33 | Options: 34 | -f, --file Provide the filename of a binary file to be 35 | uploaded. The first file specified is uploaded to 36 | the fixed address 0x40200000 as defined by the manual. 37 | Additional files must be followed by an address 38 | argument (-a). 39 | -a, --addr The address to load the prior file at. 40 | -j, --jump Specify the address to jump to after loading all 41 | of the files in to memory. 42 | -i, --vendor Override the default vendor ID to poll for 43 | (default 0x0451). 44 | -p, --product Override the default product ID to poll for 45 | (default 0xd00e). 46 | -h, --help Display this message. 47 | -v, --verbose Enable verbose output. 48 | ``` 49 | 50 | Examples 51 | -------- 52 | Uploading a compatible X-Loader, U-Boot, Kernel, and RAMDisk, then jumping 53 | to the U-Boot image for further bootloading: 54 | `./omap_loader -f x-load.bin -f u-boot.bin -a 0x80200000 -f uImage -a 0x80800000 \ 55 | -f uRamdisk -a 0x81000000 -j 0x80200000` 56 | 57 | Uploading arbitrary code to be executed (doesn't have to be X-loader): 58 | `./omap_loader -f exec_me.bin` 59 | 60 | Trying to debug an upload issue using verbose output: 61 | `./omap_loader -v -f exec_me.bin -f file1.bin -a 0xdeadbeef -j 0xabad1dea` 62 | 63 | Support 64 | ------- 65 | If you experience any difficulties in building or using this project, please search through the open issues or open a new one describing your problem. Currently, the maintainer \[me\] does not own a USBLOAD compatible device. This means any device testing is impossible on my end. 66 | 67 | License 68 | ------- 69 | GNU GPLv2 70 | 71 | Authors 72 | ------- 73 | Grant Hernandez (2014)
74 | Martin Mueller (2008) - original omap3\_usbload 75 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Auto-detect platform and install firmware to OMAP device 3 | 4 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | # Detect OS and architecture 7 | OS="$(uname -s)" 8 | ARCH="$(uname -m)" 9 | 10 | echo "=========================================" 11 | echo "NoLongerEvil Firmware Installer" 12 | echo "=========================================" 13 | echo "Detected OS: $OS" 14 | echo "Detected Architecture: $ARCH" 15 | echo "" 16 | 17 | # Determine which binary to use 18 | case "$OS" in 19 | Linux*) 20 | OMAP_LOADER="$SCRIPT_DIR/bin/linux-x64/omap_loader" 21 | USE_SUDO="sudo" 22 | ;; 23 | Darwin*) 24 | if [ "$ARCH" = "arm64" ]; then 25 | OMAP_LOADER="$SCRIPT_DIR/bin/macos-arm64/omap_loader" 26 | else 27 | OMAP_LOADER="$SCRIPT_DIR/bin/macos-x64/omap_loader" 28 | fi 29 | USE_SUDO="sudo" 30 | ;; 31 | MINGW*|MSYS*|CYGWIN*) 32 | OMAP_LOADER="$SCRIPT_DIR/bin/windows-x64/omap_loader.exe" 33 | USE_SUDO="" 34 | ;; 35 | *) 36 | echo "Error: Unsupported operating system: $OS" 37 | exit 1 38 | ;; 39 | esac 40 | 41 | # Check if binary exists 42 | if [ ! -f "$OMAP_LOADER" ]; then 43 | echo "Error: omap_loader binary not found at: $OMAP_LOADER" 44 | echo "" 45 | echo "Please build it first by running:" 46 | echo " ./build.sh" 47 | exit 1 48 | fi 49 | 50 | echo "Using omap_loader: $OMAP_LOADER" 51 | echo "" 52 | 53 | # Download and extract firmware files 54 | FIRMWARE_DIR="$SCRIPT_DIR/bin/firmware" 55 | FIRMWARE_URL="https://github.com/codykociemba/NoLongerEvil-Thermostat/releases/download/v1.0.0/firmware-files.zip" 56 | FIRMWARE_ZIP="$SCRIPT_DIR/bin/firmware-files.zip" 57 | 58 | # Create bin and firmware directories if they don't exist 59 | mkdir -p "$SCRIPT_DIR/bin" 60 | mkdir -p "$FIRMWARE_DIR" 61 | 62 | # Check if firmware files already exist 63 | if [ -f "$FIRMWARE_DIR/x-load-gen1.bin" ] && [ -f "$FIRMWARE_DIR/x-load-gen2.bin" ] && [ -f "$FIRMWARE_DIR/u-boot.bin" ] && [ -f "$FIRMWARE_DIR/uImage" ]; then 64 | echo "=========================================" 65 | echo "Firmware files already exist, skipping download" 66 | echo "=========================================" 67 | echo "" 68 | else 69 | echo "=========================================" 70 | echo "Downloading firmware files..." 71 | echo "=========================================" 72 | echo "" 73 | 74 | # Download firmware archive 75 | if command -v curl &> /dev/null; then 76 | echo "Downloading from: $FIRMWARE_URL" 77 | if ! curl -L -o "$FIRMWARE_ZIP" "$FIRMWARE_URL"; then 78 | echo "Error: Failed to download firmware files" 79 | exit 1 80 | fi 81 | elif command -v wget &> /dev/null; then 82 | echo "Downloading from: $FIRMWARE_URL" 83 | if ! wget -O "$FIRMWARE_ZIP" "$FIRMWARE_URL"; then 84 | echo "Error: Failed to download firmware files" 85 | exit 1 86 | fi 87 | else 88 | echo "Error: Neither curl nor wget found. Please install one of them." 89 | exit 1 90 | fi 91 | 92 | echo "Download complete!" 93 | echo "" 94 | 95 | # Extract firmware files 96 | echo "Extracting firmware files..." 97 | if ! unzip -o "$FIRMWARE_ZIP" -d "$FIRMWARE_DIR"; then 98 | echo "Error: Failed to extract firmware files" 99 | echo "Make sure unzip is installed" 100 | exit 1 101 | fi 102 | 103 | # Clean up zip file 104 | rm -f "$FIRMWARE_ZIP" 105 | 106 | echo "Firmware files extracted successfully!" 107 | echo "" 108 | fi 109 | 110 | # Prompt for Nest generation 111 | echo "=========================================" 112 | echo "Nest Generation Selection" 113 | echo "=========================================" 114 | echo "" 115 | echo "Which generation Nest Thermostat do you have?" 116 | echo "You can check the back plate - the bubble level should be green for Gen 1/2." 117 | echo "" 118 | echo "Enter 1 for Generation 1" 119 | echo "Enter 2 for Generation 2" 120 | echo "" 121 | read -p "Generation (1 or 2): " NEST_GEN 122 | 123 | # Validate input 124 | while [[ ! "$NEST_GEN" =~ ^[12]$ ]]; do 125 | echo "Invalid input. Please enter 1 or 2." 126 | read -p "Generation (1 or 2): " NEST_GEN 127 | done 128 | 129 | echo "" 130 | echo "Selected: Generation $NEST_GEN" 131 | echo "" 132 | 133 | # Set firmware paths based on generation (use absolute paths to avoid issues with sudo) 134 | XLOAD_BIN="$(cd "$FIRMWARE_DIR" && pwd)/x-load-gen${NEST_GEN}.bin" 135 | UBOOT_BIN="$(cd "$FIRMWARE_DIR" && pwd)/u-boot.bin" 136 | UIMAGE_BIN="$(cd "$FIRMWARE_DIR" && pwd)/uImage" 137 | 138 | # Verify firmware files exist 139 | if [ ! -f "$XLOAD_BIN" ]; then 140 | echo "Error: x-load-gen${NEST_GEN}.bin not found at: $XLOAD_BIN" 141 | exit 1 142 | fi 143 | 144 | if [ ! -f "$UBOOT_BIN" ]; then 145 | echo "Error: u-boot.bin not found at: $UBOOT_BIN" 146 | exit 1 147 | fi 148 | 149 | if [ ! -f "$UIMAGE_BIN" ]; then 150 | echo "Error: uImage not found at: $UIMAGE_BIN" 151 | exit 1 152 | fi 153 | 154 | echo "Firmware files verified:" 155 | echo " x-load: $XLOAD_BIN" 156 | echo " u-boot: $UBOOT_BIN" 157 | echo " uImage: $UIMAGE_BIN" 158 | echo "" 159 | 160 | echo "=========================================" 161 | echo "Waiting for device to enter DFU mode..." 162 | echo "=========================================" 163 | echo "" 164 | echo "Instructions:" 165 | echo "1. Ensure your Nest is charged (50%+ recommended)" 166 | echo "2. Remove the Nest from the wall mount" 167 | echo "3. Connect it to your computer via micro USB" 168 | echo "4. Press and hold the display for 10-15 seconds" 169 | echo "5. The device will reboot and enter DFU mode" 170 | echo "" 171 | echo "The installer will automatically detect the device and begin flashing..." 172 | echo "" 173 | 174 | # Load firmware (expand all variables before sudo to prevent path issues) 175 | if [ -n "$USE_SUDO" ]; then 176 | sudo "$OMAP_LOADER" \ 177 | -f "$XLOAD_BIN" \ 178 | -f "$UBOOT_BIN" \ 179 | -a 0x80100000 \ 180 | -f "$UIMAGE_BIN" \ 181 | -a 0x80A00000 \ 182 | -v \ 183 | -j 0x80100000 184 | else 185 | "$OMAP_LOADER" \ 186 | -f "$XLOAD_BIN" \ 187 | -f "$UBOOT_BIN" \ 188 | -a 0x80100000 \ 189 | -f "$UIMAGE_BIN" \ 190 | -a 0x80A00000 \ 191 | -v \ 192 | -j 0x80100000 193 | fi 194 | 195 | # Check if successful 196 | if [ $? -eq 0 ]; then 197 | echo "" 198 | echo "=========================================" 199 | echo "Firmware installed successfully!" 200 | echo "=========================================" 201 | echo "" 202 | echo "Next steps:" 203 | echo "1. Keep the device plugged in via USB" 204 | echo "2. Wait 2-3 minutes for the device to boot" 205 | echo "3. You should see the NoLongerEvil logo" 206 | echo "4. Visit https://nolongerevil.com to register" 207 | echo "5. Link your device using the entry code from:" 208 | echo " Settings → Nest App → Get Entry Code" 209 | echo "" 210 | else 211 | echo "" 212 | echo "=========================================" 213 | echo "Installation failed!" 214 | echo "=========================================" 215 | echo "" 216 | echo "Please check:" 217 | echo "- Device is properly connected via USB" 218 | echo "- Device entered DFU mode correctly" 219 | echo "- USB drivers are installed (Windows)" 220 | echo "" 221 | exit 1 222 | fi 223 | 224 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nest Thermostat Firmware Setup 2 | 3 | [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-donate-yellow.svg)](https://buymeacoffee.com/codykociemba) 4 | [![Discord](https://img.shields.io/badge/Discord-Join%20Us-5865F2?logo=discord&logoColor=white)](https://discord.gg/hackhouse) 5 | [![Release](https://img.shields.io/badge/Release-v1.0.0-blue)](https://github.com/codykociemba/NoLongerEvil-Thermostat/releases/tag/v1.0.0) 6 | 7 |
8 | 9 | FULU Bounties Winner 10 | 11 |

🏆 FULU Bounty Winner 🏆

12 |

Hack House and this project are the official winners of the FULU Bounty for Nest Learning Thermostat Gen 1/2

13 |
14 | 15 | **Hardware Alternative:** If you're interested in the hardware side of things, check out [https://sett.homes](https://sett.homes) for a drop-in PCB replacement option. 16 | 17 | --- 18 | 19 | ## Installation Options 20 | 21 | Choose the installation method that works best for you: 22 | 23 | ### Option 1: Hosted (GUI Installer) - **Recommended** 24 | 25 | The easiest way to get started. Download our GUI installer that handles everything automatically. 26 | 27 | 📖 **[View Installation Guide](https://docs.nolongerevil.com/hosted/installation)** 28 | 29 | ### Option 2: Hosted (Manual) 30 | 31 | If the GUI installer doesn't work for you, follow the manual installation steps below. This method uses command-line tools to flash the firmware. 32 | 33 | ### Option 3: Self-Hosted - **Advanced Users Only** 34 | 35 | Host your own No Longer Evil server infrastructure. Requires technical expertise. 36 | 37 | 📖 **[View Self-Hosted Guide](https://docs.nolongerevil.com/self-hosted/overview)** 38 | 39 | ⚠️ **Warning:** This option is still a work in progress and may or may not function properly. Check out the [discussion here](https://github.com/codykociemba/NoLongerEvil-Thermostat/discussions/34) for more details. 40 | 41 | --- 42 | 43 | ## Manual Installation (Option 2) 44 | 45 | This directory contains the tools and firmware needed to flash custom firmware to Nest Thermostat devices using the OMAP DFU (Device Firmware Update) interface. 46 | 47 | ### Prerequesites 48 | 49 | You will need to have a Linux or MacOS computer available. 50 | 51 | ⚠️ Please verify your Nest is compatible at **[https://docs.nolongerevil.com/compatibility](https://docs.nolongerevil.com/compatibility)** - we currently only support Nest Generation 1 and 2 at the moment. 52 | 53 | ### How it Works 54 | 55 | The custom firmware flashes the device with modified bootloader and kernel components that redirect all network traffic from the original Nest/Google servers to a server we specify. This server hosts a reverse-engineered replica of their API, allowing the thermostat to function independently while giving you complete control over your device data and settings. 56 | 57 | By intercepting the communication layer, the thermostat believes it's communicating with the official Nest infrastructure, but instead connects to the No Longer Evil platform. This approach ensures full compatibility with the device's existing software while breaking free from Google's cloud dependency. 58 | 59 | ## Quick Start 60 | 61 | ### 1. Clone the Repository 62 | 63 | ```bash 64 | git clone https://github.com/codykociemba/NoLongerEvil-Thermostat.git 65 | cd NoLongerEvil-Thermostat 66 | ``` 67 | 68 | ### 2. Install Prerequisites 69 | 70 | Before building, you'll need to install some required packages: 71 | 72 | #### Linux (Debian/Ubuntu) 73 | 74 | ```bash 75 | sudo apt-get update 76 | sudo apt-get install build-essential libusb-1.0-0-dev gcc pkg-config 77 | ``` 78 | 79 | #### macOS 80 | 81 | First, install Xcode Command Line Tools: 82 | 83 | ```bash 84 | xcode-select --install 85 | ``` 86 | 87 | Then install libusb using Homebrew (the build script will attempt to install this automatically if missing): 88 | 89 | ```bash 90 | # Install Homebrew if you don't have it 91 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 92 | 93 | # Install libusb 94 | brew install libusb pkg-config 95 | ``` 96 | 97 | ### 3. Build the omap_loader tool 98 | 99 | ```bash 100 | chmod +x build.sh 101 | ./build.sh 102 | ``` 103 | 104 | The build script will automatically detect your operating system and build the appropriate binary. 105 | 106 | ### 4. Start the firmware installer 107 | 108 | **IMPORTANT: You must start the installer script BEFORE rebooting the device.** 109 | 110 | ```bash 111 | chmod +x install.sh 112 | ./install.sh 113 | ``` 114 | 115 | **Note for macOS:** You may need to grant USB permissions. If you encounter permission issues, check System Preferences → Security & Privacy. 116 | 117 | The script will wait for the device to enter DFU mode. 118 | 119 | ### 5. Put your Nest device in DFU mode 120 | 121 | Follow these steps carefully: 122 | 123 | 1. **Charge the device** - Ensure your Nest Thermostat is properly charged (at least 50% battery recommended) 124 | 2. **Remove from wall** - Remove the Nest from its back plate/wall mount 125 | 3. **Connect via USB** - Plug the Nest into your computer using a micro USB cable 126 | 4. **Wait for the installer** - Make sure the `install.sh` script is running and waiting 127 | 5. **Reboot the device** - Press and hold down on the display for 10-15 seconds until the device reboots 128 | 6. **DFU mode active** - Once it reboots, the device will enter DFU mode and the installer script will recognize it and begin flashing 129 | 130 | The firmware installer will automatically detect the device and flash the custom bootloader (x-load, u-boot) and kernel (uImage). 131 | 132 | ### 6. Wait for the device to boot 133 | 134 | After the firmware is flashed successfully, you should see our NLE logo on the device screen. 135 | 136 | **Important:** 137 | - Keep the device plugged in via USB 138 | - Wait for the device to complete its boot sequence (this may take 3-4 minutes) 139 | - Do not disconnect or power off the device during this time 140 | 141 | ### 7. Register your account 142 | 143 | Once the device has fully rebooted: 144 | 145 | 1. Visit **[https://nolongerevil.com](https://nolongerevil.com)** in your web browser 146 | 2. **Register an account** (or sign in if you already have one) 147 | 3. Navigate to your **Dashboard** 148 | 149 | You will see a "No devices" screen that prompts you for an entry code. 150 | 151 | ### 8. Link your device 152 | 153 | To link your Nest device to your No Longer Evil account: 154 | 155 | 1. On your Nest device, navigate to: **Settings → Nest App → Get Entry Code** 156 | 2. The device will display a unique entry code 157 | 3. Enter this code on the No Longer Evil dashboard 158 | 4. Your device is now linked and ready to use! 159 | 160 | ## Security Considerations 161 | 162 | This tool provides low-level access to the device's boot process. Use responsibly: 163 | 164 | - Only use on devices you own 165 | - Improper firmware can brick your device (Don't sue me bro) 166 | 167 | ## Credits & Acknowledgments 168 | 169 | This project builds upon the excellent work of several security researchers and developers: 170 | 171 | - **[grant-h](https://github.com/grant-h) / [ajb142](https://github.com/ajb142)** - [omap_loader](https://github.com/ajb142/omap_loader), the USB bootloader tool used to flash OMAP devices 172 | - **[exploiteers (GTVHacker)](https://github.com/exploiteers)** - Original research and development of the [Nest DFU attack](https://github.com/exploiteers/NestDFUAttack), which demonstrated the ability to flash custom firmware to Nest devices gen 1 & gen 2 173 | - **[FULU](https://bounties.fulu.org/)** and all bounty backers - For funding the [Nest Learning Thermostat Gen 1/2 bounty](https://bounties.fulu.org/bounties/nest-learning-thermostat-gen-1-2) and supporting the right-to-repair movement 174 | 175 | Without their groundbreaking research, open-source contributions, and advocacy for device ownership rights, this work would not be possible. Thank you! 176 | 177 | ### Open Source Commitment 178 | 179 | We are committed to transparency and the right-to-repair movement. The firmware images and backend API server code will be open sourced soon, allowing the community to audit, improve, and self-host their own infrastructure. 180 | 181 | ## References 182 | 183 | - [OMAP Loader by ajb142](https://github.com/ajb142/omap_loader) 184 | - [Nest DFU Attack by exploiteers](https://github.com/exploiteers/NestDFUAttack) 185 | -------------------------------------------------------------------------------- /patches/omap_loader_mac.patch: -------------------------------------------------------------------------------- 1 | --- src/omap_loader/omap_loader.c 2025-11-03 00:39:43 2 | +++ src/omap_loader/omap_loader_mac.c 2025-11-03 00:40:23 3 | @@ -58,7 +58,12 @@ 4 | /* TODO: dynamically discover these endpoints */ 5 | #define OMAP_USB_BULK_IN 0x81 6 | #define OMAP_USB_BULK_OUT 0x01 7 | +#define OMAP_USB_INTERFACE 0 8 | +#define OMAP_USB_CONFIGURATION 1 9 | #define OMAP_ASIC_ID_LEN 69 10 | +#define OMAP_USB_MAX_RECOVERIES 10 11 | +#define OMAP_USB_CLEAR_TIMEOUT_US 5000 12 | +#define OMAP_USB_MAX_XFER (64 * 1024) 13 | 14 | #ifdef OMAP_IS_BIG_ENDIAN 15 | # define cpu_to_le32(v) (((v & 0xff) << 24) | ((v & 0xff00) << 8) | \ 16 | @@ -80,8 +85,8 @@ 17 | #define USBLOAD_CMD_MESSAGE PACK4('U','S','B','m') /* debug message */ 18 | 19 | /* USB transfer characteristics */ 20 | -#define USB_MAX_WAIT 5000 21 | -#define USB_TIMEOUT 1000 22 | +#define USB_MAX_WAIT 20000 23 | +#define USB_TIMEOUT 4000 24 | #define USB_MAX_TIMEOUTS (USB_MAX_WAIT/USB_TIMEOUT) 25 | 26 | /* Datatypes 27 | @@ -204,9 +209,56 @@ 28 | usleep(10000); 29 | } 30 | 31 | + unsigned char * mfgStr = NULL; 32 | + unsigned char * prodStr = NULL; 33 | + 34 | +#ifdef LIBUSB_API_VERSION 35 | + ret = libusb_set_auto_detach_kernel_driver(handle, 1); 36 | + 37 | + if(ret < 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED) 38 | + { 39 | + log_error("failed to enable auto detach for kernel drivers: %s\n", 40 | + libusb_error_name(ret)); 41 | + libusb_close(handle); 42 | + return NULL; 43 | + } 44 | +#endif 45 | + 46 | + int active_config = 0; 47 | + ret = libusb_get_configuration(handle, &active_config); 48 | + 49 | + if(ret < 0) 50 | + { 51 | + log_error("failed to query active USB configuration: %s\n", 52 | + libusb_error_name(ret)); 53 | + libusb_close(handle); 54 | + return NULL; 55 | + } 56 | + 57 | + if(active_config == 0) 58 | + { 59 | + ret = libusb_set_configuration(handle, OMAP_USB_CONFIGURATION); 60 | + 61 | + if(ret < 0 && ret != LIBUSB_ERROR_BUSY) 62 | + { 63 | + log_error("failed to select USB configuration %d: %s\n", 64 | + OMAP_USB_CONFIGURATION, libusb_error_name(ret)); 65 | + libusb_close(handle); 66 | + return NULL; 67 | + } 68 | + } 69 | + 70 | + if((ret = libusb_claim_interface(handle, OMAP_USB_INTERFACE)) < 0) 71 | + { 72 | + log_error("failed to claim interface %d: %s\n", 73 | + OMAP_USB_INTERFACE, libusb_error_name(ret)); 74 | + libusb_close(handle); 75 | + return NULL; 76 | + } 77 | + 78 | /* grab the manufacturer and product strings for printing */ 79 | - unsigned char * mfgStr = omap_usb_get_string(handle, desc.iManufacturer); 80 | - unsigned char * prodStr = omap_usb_get_string(handle, desc.iProduct); 81 | + mfgStr = omap_usb_get_string(handle, desc.iManufacturer); 82 | + prodStr = omap_usb_get_string(handle, desc.iProduct); 83 | 84 | log_info("successfully opened %04hx:%04hx (", vendor, product); 85 | 86 | @@ -287,7 +339,7 @@ 87 | 88 | while(1) 89 | { 90 | - if(iter >= iter) 91 | + if(iter >= allocSize) 92 | { 93 | allocSize += 1024; 94 | data = realloc(data, allocSize); 95 | @@ -355,8 +407,8 @@ 96 | } 97 | } 98 | 99 | - libusb_context * ctx; 100 | - libusb_device_handle * dev; 101 | + libusb_context * ctx = NULL; 102 | + libusb_device_handle * dev = NULL; 103 | int ret; 104 | 105 | if((ret = libusb_init(&ctx)) < 0) 106 | @@ -374,12 +426,6 @@ 107 | return 1; 108 | } 109 | 110 | - if((ret = libusb_claim_interface(dev, 0)) < 0) 111 | - { 112 | - log_error("failed to claim interface: %s\n", libusb_error_name(ret)); 113 | - return ret; 114 | - } 115 | - 116 | /* Communicate with the TI BootROM directly 117 | - retrieve ASIC ID 118 | - start peripheral boot 119 | @@ -393,10 +439,8 @@ 120 | goto fail; 121 | } 122 | 123 | - /* Wait a moment before attempting to communicate with X-loader. If we don't 124 | - * wait here, some computers end up failing to upload additional files. 125 | - */ 126 | - usleep(20000); 127 | + /* give the device time to bring X-loader fully online */ 128 | + usleep(200 * 1000); 129 | 130 | /* If we are passed one file, assume that the user just wants to 131 | upload some initial code with no X-loader chaining 132 | @@ -413,13 +457,17 @@ 133 | log_info("successfully transfered %d %s\n", args->numFiles, 134 | (args->numFiles > 1) ? "files" : "file"); 135 | 136 | - /* safely close our USB handle and context */ 137 | + libusb_release_interface(dev, OMAP_USB_INTERFACE); 138 | libusb_close(dev); 139 | libusb_exit(ctx); 140 | return 0; 141 | 142 | fail: 143 | - libusb_close(dev); 144 | + if(dev) 145 | + { 146 | + libusb_release_interface(dev, OMAP_USB_INTERFACE); 147 | + libusb_close(dev); 148 | + } 149 | libusb_exit(ctx); 150 | 151 | return 1; 152 | @@ -431,6 +479,7 @@ 153 | int ret = 0; 154 | int iter = 0; 155 | int sizeLeft = length; 156 | + int recoveries = 0; 157 | 158 | if(!actualLength) 159 | return false; 160 | @@ -440,7 +489,9 @@ 161 | int actualRead = 0; 162 | int readAmt = sizeLeft; 163 | 164 | - usleep(1000); 165 | + if(readAmt > OMAP_USB_MAX_XFER) 166 | + readAmt = OMAP_USB_MAX_XFER; 167 | + 168 | ret = libusb_bulk_transfer(handle, OMAP_USB_BULK_IN, data+iter, 169 | readAmt, &actualRead, USB_TIMEOUT); 170 | 171 | @@ -471,6 +522,41 @@ 172 | } 173 | else 174 | { 175 | + if(ret == LIBUSB_ERROR_PIPE || ret == LIBUSB_ERROR_IO) 176 | + { 177 | + if(recoveries >= OMAP_USB_MAX_RECOVERIES) 178 | + { 179 | + log_error( 180 | + "usb bulk in stalled too many times while transfering %d bytes " 181 | + "(got %d): %s\n", 182 | + length, iter, libusb_error_name(ret)); 183 | + return false; 184 | + } 185 | + 186 | + recoveries++; 187 | + 188 | + int clear = libusb_clear_halt(handle, OMAP_USB_BULK_IN); 189 | + 190 | + if(clear < 0) 191 | + { 192 | + if(clear == LIBUSB_ERROR_NOT_FOUND || 193 | + clear == LIBUSB_ERROR_NO_DEVICE || 194 | + clear == LIBUSB_ERROR_OTHER) 195 | + { 196 | + /* transient condition; retry */ 197 | + } 198 | + else 199 | + { 200 | + log_error("failed to clear BULK_IN halt condition: %s\n", 201 | + libusb_error_name(clear)); 202 | + return false; 203 | + } 204 | + } 205 | + 206 | + usleep(OMAP_USB_CLEAR_TIMEOUT_US); 207 | + continue; 208 | + } 209 | + 210 | log_error( 211 | "fatal transfer error (BULK_IN) for %d bytes (got %d): %s\n", 212 | length, iter, libusb_error_name(ret)); 213 | @@ -490,15 +576,16 @@ 214 | int numTimeouts = 0; 215 | int iter = 0; 216 | int sizeLeft = length; 217 | + int recoveries = 0; 218 | 219 | while(sizeLeft > 0) 220 | { 221 | int actualWrite = 0; 222 | int writeAmt = sizeLeft; 223 | - /* Only send 512 bytes at a time. Helps with reliability sometimes. */ 224 | - if(writeAmt > 512) { writeAmt = 512; } 225 | 226 | - usleep(1000); 227 | + if(writeAmt > OMAP_USB_MAX_XFER) 228 | + writeAmt = OMAP_USB_MAX_XFER; 229 | + 230 | ret = libusb_bulk_transfer(handle, OMAP_USB_BULK_OUT, data+iter, 231 | writeAmt, &actualWrite, USB_TIMEOUT); 232 | 233 | @@ -525,6 +612,41 @@ 234 | } 235 | else 236 | { 237 | + if(ret == LIBUSB_ERROR_PIPE || ret == LIBUSB_ERROR_IO) 238 | + { 239 | + if(recoveries >= OMAP_USB_MAX_RECOVERIES) 240 | + { 241 | + log_error( 242 | + "usb bulk out stalled too many times while transfering %d bytes " 243 | + "(%d made it): %s\n", 244 | + length, iter, libusb_error_name(ret)); 245 | + return false; 246 | + } 247 | + 248 | + recoveries++; 249 | + 250 | + int clear = libusb_clear_halt(handle, OMAP_USB_BULK_OUT); 251 | + 252 | + if(clear < 0) 253 | + { 254 | + if(clear == LIBUSB_ERROR_NOT_FOUND || 255 | + clear == LIBUSB_ERROR_NO_DEVICE || 256 | + clear == LIBUSB_ERROR_OTHER) 257 | + { 258 | + /* transient condition; retry */ 259 | + } 260 | + else 261 | + { 262 | + log_error("failed to clear BULK_OUT halt condition: %s\n", 263 | + libusb_error_name(clear)); 264 | + return false; 265 | + } 266 | + } 267 | + 268 | + usleep(OMAP_USB_CLEAR_TIMEOUT_US); 269 | + continue; 270 | + } 271 | + 272 | log_error( 273 | "fatal transfer error (BULK_OUT) for %d bytes (%d made it): %s\n", 274 | length, iter, libusb_error_name(ret)); 275 | @@ -744,6 +866,9 @@ 276 | } 277 | } 278 | 279 | + /* allow the device to settle before issuing the final jump */ 280 | + usleep(500 * 1000); 281 | + 282 | /* we're done uploading files to X-loader send the jump command */ 283 | int jumpCmd[3]; 284 | jumpCmd[0] = cpu_to_le32(USBLOAD_CMD_JUMP); 285 | @@ -883,7 +1008,7 @@ 286 | char * exe = NULL; 287 | 288 | /* temporary local file object */ 289 | - struct file_upload file; 290 | + struct file_upload file = {0}; 291 | /* total arg state */ 292 | struct arg_state * args = calloc(1, sizeof(*args)); 293 | 294 | @@ -941,7 +1066,8 @@ 295 | file.addr = OMAP_BASE_ADDRESS; 296 | 297 | /* commit the file object with the processor specified base address */ 298 | - args->files = realloc(args->files, fileCount); 299 | + args->files = realloc(args->files, 300 | + fileCount * sizeof(*args->files)); 301 | args->numFiles = fileCount; 302 | args->files[fileCount-1] = malloc(sizeof(file)); 303 | memcpy(args->files[fileCount-1], &file, sizeof(file)); 304 | @@ -967,7 +1093,8 @@ 305 | file.addr = strtoul(optarg, NULL, 0); 306 | 307 | /* commit the file object */ 308 | - args->files = realloc(args->files, fileCount); 309 | + args->files = realloc(args->files, 310 | + fileCount * sizeof(*args->files)); 311 | args->numFiles = fileCount; 312 | args->files[fileCount-1] = malloc(sizeof(file)); 313 | memcpy(args->files[fileCount-1], &file, sizeof(file)); 314 | -------------------------------------------------------------------------------- /src/omap_loader/LICENSE.md: -------------------------------------------------------------------------------- 1 | The GNU General Public License, Version 2, June 1991 (GPLv2) 2 | ============================================================ 3 | 4 | > Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | > 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 6 | 7 | Everyone is permitted to copy and distribute verbatim copies of this license 8 | document, but changing it is not allowed. 9 | 10 | 11 | Preamble 12 | -------- 13 | 14 | The licenses for most software are designed to take away your freedom to share 15 | and change it. By contrast, the GNU General Public License is intended to 16 | guarantee your freedom to share and change free software--to make sure the 17 | software is free for all its users. This General Public License applies to most 18 | of the Free Software Foundation's software and to any other program whose 19 | authors commit to using it. (Some other Free Software Foundation software is 20 | covered by the GNU Library General Public License instead.) You can apply it to 21 | your programs, too. 22 | 23 | When we speak of free software, we are referring to freedom, not price. Our 24 | General Public Licenses are designed to make sure that you have the freedom to 25 | distribute copies of free software (and charge for this service if you wish), 26 | that you receive source code or can get it if you want it, that you can change 27 | the software or use pieces of it in new free programs; and that you know you can 28 | do these things. 29 | 30 | To protect your rights, we need to make restrictions that forbid anyone to deny 31 | you these rights or to ask you to surrender the rights. These restrictions 32 | translate to certain responsibilities for you if you distribute copies of the 33 | software, or if you modify it. 34 | 35 | For example, if you distribute copies of such a program, whether gratis or for a 36 | fee, you must give the recipients all the rights that you have. You must make 37 | sure that they, too, receive or can get the source code. And you must show them 38 | these terms so they know their rights. 39 | 40 | We protect your rights with two steps: (1) copyright the software, and (2) offer 41 | you this license which gives you legal permission to copy, distribute and/or 42 | modify the software. 43 | 44 | Also, for each author's protection and ours, we want to make certain that 45 | everyone understands that there is no warranty for this free software. If the 46 | software is modified by someone else and passed on, we want its recipients to 47 | know that what they have is not the original, so that any problems introduced by 48 | others will not reflect on the original authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software patents. We wish 51 | to avoid the danger that redistributors of a free program will individually 52 | obtain patent licenses, in effect making the program proprietary. To prevent 53 | this, we have made it clear that any patent must be licensed for everyone's free 54 | use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and modification 57 | follow. 58 | 59 | 60 | Terms And Conditions For Copying, Distribution And Modification 61 | --------------------------------------------------------------- 62 | 63 | **0.** This License applies to any program or other work which contains a notice 64 | placed by the copyright holder saying it may be distributed under the terms of 65 | this General Public License. The "Program", below, refers to any such program or 66 | work, and a "work based on the Program" means either the Program or any 67 | derivative work under copyright law: that is to say, a work containing the 68 | Program or a portion of it, either verbatim or with modifications and/or 69 | translated into another language. (Hereinafter, translation is included without 70 | limitation in the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not covered by 73 | this License; they are outside its scope. The act of running the Program is not 74 | restricted, and the output from the Program is covered only if its contents 75 | constitute a work based on the Program (independent of having been made by 76 | running the Program). Whether that is true depends on what the Program does. 77 | 78 | **1.** You may copy and distribute verbatim copies of the Program's source code 79 | as you receive it, in any medium, provided that you conspicuously and 80 | appropriately publish on each copy an appropriate copyright notice and 81 | disclaimer of warranty; keep intact all the notices that refer to this License 82 | and to the absence of any warranty; and give any other recipients of the Program 83 | a copy of this License along with the Program. 84 | 85 | You may charge a fee for the physical act of transferring a copy, and you may at 86 | your option offer warranty protection in exchange for a fee. 87 | 88 | **2.** You may modify your copy or copies of the Program or any portion of it, 89 | thus forming a work based on the Program, and copy and distribute such 90 | modifications or work under the terms of Section 1 above, provided that you also 91 | meet all of these conditions: 92 | 93 | * **a)** You must cause the modified files to carry prominent notices stating 94 | that you changed the files and the date of any change. 95 | 96 | * **b)** You must cause any work that you distribute or publish, that in whole 97 | or in part contains or is derived from the Program or any part thereof, to 98 | be licensed as a whole at no charge to all third parties under the terms of 99 | this License. 100 | 101 | * **c)** If the modified program normally reads commands interactively when 102 | run, you must cause it, when started running for such interactive use in the 103 | most ordinary way, to print or display an announcement including an 104 | appropriate copyright notice and a notice that there is no warranty (or 105 | else, saying that you provide a warranty) and that users may redistribute 106 | the program under these conditions, and telling the user how to view a copy 107 | of this License. (Exception: if the Program itself is interactive but does 108 | not normally print such an announcement, your work based on the Program is 109 | not required to print an announcement.) 110 | 111 | These requirements apply to the modified work as a whole. If identifiable 112 | sections of that work are not derived from the Program, and can be reasonably 113 | considered independent and separate works in themselves, then this License, and 114 | its terms, do not apply to those sections when you distribute them as separate 115 | works. But when you distribute the same sections as part of a whole which is a 116 | work based on the Program, the distribution of the whole must be on the terms of 117 | this License, whose permissions for other licensees extend to the entire whole, 118 | and thus to each and every part regardless of who wrote it. 119 | 120 | Thus, it is not the intent of this section to claim rights or contest your 121 | rights to work written entirely by you; rather, the intent is to exercise the 122 | right to control the distribution of derivative or collective works based on the 123 | Program. 124 | 125 | In addition, mere aggregation of another work not based on the Program with the 126 | Program (or with a work based on the Program) on a volume of a storage or 127 | distribution medium does not bring the other work under the scope of this 128 | License. 129 | 130 | **3.** You may copy and distribute the Program (or a work based on it, under 131 | Section 2) in object code or executable form under the terms of Sections 1 and 2 132 | above provided that you also do one of the following: 133 | 134 | * **a)** Accompany it with the complete corresponding machine-readable source 135 | code, which must be distributed under the terms of Sections 1 and 2 above on 136 | a medium customarily used for software interchange; or, 137 | 138 | * **b)** Accompany it with a written offer, valid for at least three years, to 139 | give any third party, for a charge no more than your cost of physically 140 | performing source distribution, a complete machine-readable copy of the 141 | corresponding source code, to be distributed under the terms of Sections 1 142 | and 2 above on a medium customarily used for software interchange; or, 143 | 144 | * **c)** Accompany it with the information you received as to the offer to 145 | distribute corresponding source code. (This alternative is allowed only for 146 | noncommercial distribution and only if you received the program in object 147 | code or executable form with such an offer, in accord with Subsection b 148 | above.) 149 | 150 | The source code for a work means the preferred form of the work for making 151 | modifications to it. For an executable work, complete source code means all the 152 | source code for all modules it contains, plus any associated interface 153 | definition files, plus the scripts used to control compilation and installation 154 | of the executable. However, as a special exception, the source code distributed 155 | need not include anything that is normally distributed (in either source or 156 | binary form) with the major components (compiler, kernel, and so on) of the 157 | operating system on which the executable runs, unless that component itself 158 | accompanies the executable. 159 | 160 | If distribution of executable or object code is made by offering access to copy 161 | from a designated place, then offering equivalent access to copy the source code 162 | from the same place counts as distribution of the source code, even though third 163 | parties are not compelled to copy the source along with the object code. 164 | 165 | **4.** You may not copy, modify, sublicense, or distribute the Program except as 166 | expressly provided under this License. Any attempt otherwise to copy, modify, 167 | sublicense or distribute the Program is void, and will automatically terminate 168 | your rights under this License. However, parties who have received copies, or 169 | rights, from you under this License will not have their licenses terminated so 170 | long as such parties remain in full compliance. 171 | 172 | **5.** You are not required to accept this License, since you have not signed 173 | it. However, nothing else grants you permission to modify or distribute the 174 | Program or its derivative works. These actions are prohibited by law if you do 175 | not accept this License. Therefore, by modifying or distributing the Program (or 176 | any work based on the Program), you indicate your acceptance of this License to 177 | do so, and all its terms and conditions for copying, distributing or modifying 178 | the Program or works based on it. 179 | 180 | **6.** Each time you redistribute the Program (or any work based on the 181 | Program), the recipient automatically receives a license from the original 182 | licensor to copy, distribute or modify the Program subject to these terms and 183 | conditions. You may not impose any further restrictions on the recipients' 184 | exercise of the rights granted herein. You are not responsible for enforcing 185 | compliance by third parties to this License. 186 | 187 | **7.** If, as a consequence of a court judgment or allegation of patent 188 | infringement or for any other reason (not limited to patent issues), conditions 189 | are imposed on you (whether by court order, agreement or otherwise) that 190 | contradict the conditions of this License, they do not excuse you from the 191 | conditions of this License. If you cannot distribute so as to satisfy 192 | simultaneously your obligations under this License and any other pertinent 193 | obligations, then as a consequence you may not distribute the Program at all. 194 | For example, if a patent license would not permit royalty-free redistribution of 195 | the Program by all those who receive copies directly or indirectly through you, 196 | then the only way you could satisfy both it and this License would be to refrain 197 | entirely from distribution of the Program. 198 | 199 | If any portion of this section is held invalid or unenforceable under any 200 | particular circumstance, the balance of the section is intended to apply and the 201 | section as a whole is intended to apply in other circumstances. 202 | 203 | It is not the purpose of this section to induce you to infringe any patents or 204 | other property right claims or to contest validity of any such claims; this 205 | section has the sole purpose of protecting the integrity of the free software 206 | distribution system, which is implemented by public license practices. Many 207 | people have made generous contributions to the wide range of software 208 | distributed through that system in reliance on consistent application of that 209 | system; it is up to the author/donor to decide if he or she is willing to 210 | distribute software through any other system and a licensee cannot impose that 211 | choice. 212 | 213 | This section is intended to make thoroughly clear what is believed to be a 214 | consequence of the rest of this License. 215 | 216 | **8.** If the distribution and/or use of the Program is restricted in certain 217 | countries either by patents or by copyrighted interfaces, the original copyright 218 | holder who places the Program under this License may add an explicit 219 | geographical distribution limitation excluding those countries, so that 220 | distribution is permitted only in or among countries not thus excluded. In such 221 | case, this License incorporates the limitation as if written in the body of this 222 | License. 223 | 224 | **9.** The Free Software Foundation may publish revised and/or new versions of 225 | the General Public License from time to time. Such new versions will be similar 226 | in spirit to the present version, but may differ in detail to address new 227 | problems or concerns. 228 | 229 | Each version is given a distinguishing version number. If the Program specifies 230 | a version number of this License which applies to it and "any later version", 231 | you have the option of following the terms and conditions either of that version 232 | or of any later version published by the Free Software Foundation. If the 233 | Program does not specify a version number of this License, you may choose any 234 | version ever published by the Free Software Foundation. 235 | 236 | **10.** If you wish to incorporate parts of the Program into other free programs 237 | whose distribution conditions are different, write to the author to ask for 238 | permission. For software which is copyrighted by the Free Software Foundation, 239 | write to the Free Software Foundation; we sometimes make exceptions for this. 240 | Our decision will be guided by the two goals of preserving the free status of 241 | all derivatives of our free software and of promoting the sharing and reuse of 242 | software generally. 243 | 244 | 245 | No Warranty 246 | ----------- 247 | 248 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 249 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 250 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM 251 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 252 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 253 | PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 254 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 255 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 256 | 257 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 258 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 259 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 260 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR 261 | INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA 262 | BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 263 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER 264 | OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 265 | -------------------------------------------------------------------------------- /src/omap_loader/omap_loader.c: -------------------------------------------------------------------------------- 1 | /* 2 | OMAP Loader, a USB uploader application targeted at OMAP3 processors 3 | Copyright (C) 2008 Martin Mueller 4 | Copyright (C) 2014 Grant Hernandez 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /* Reasons for the name change: this is a complete rewrite of 29 | the unversioned omap3_usbload so to lower ambiguity the name was changed. 30 | The GPLv2 license specifies rewrites as derived work. 31 | */ 32 | #define PROG_NAME "OMAP Loader" 33 | #define VERSION "1.0.0" 34 | 35 | #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 36 | # define OMAP_IS_BIG_ENDIAN 37 | #endif 38 | 39 | #ifdef OMAP_IS_BIG_ENDIAN 40 | # include 41 | #endif 42 | 43 | #include /* for usleep and friends */ 44 | #include 45 | #include 46 | #include /* for basename */ 47 | 48 | #include /* the main event */ 49 | 50 | /* Device specific defines (OMAP) 51 | Primary source: http://www.ti.com/lit/pdf/sprugn4 52 | Section 26.4.5 "Peripheral Booting" 53 | */ 54 | #define OMAP_BASE_ADDRESS 0x40200000 55 | #define OMAP_PERIPH_BOOT 0xF0030002 56 | #define OMAP_VENDOR_ID 0x0451 57 | #define OMAP_PRODUCT_ID 0xD00E 58 | /* TODO: dynamically discover these endpoints */ 59 | #define OMAP_USB_BULK_IN 0x81 60 | #define OMAP_USB_BULK_OUT 0x01 61 | #define OMAP_ASIC_ID_LEN 69 62 | 63 | #ifdef OMAP_IS_BIG_ENDIAN 64 | # define cpu_to_le32(v) (((v & 0xff) << 24) | ((v & 0xff00) << 8) | \ 65 | ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24)) 66 | # define le32_to_cpu(v) cpu_to_le32(v) 67 | #else 68 | # define cpu_to_le32(v) (v) 69 | # define le32_to_cpu(v) (v) 70 | #endif 71 | 72 | /* taken from x-loader/drivers/usb/usb.c 73 | All credit to Martin Mueller */ 74 | #define PACK4(a,b,c,d) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)) 75 | #define USBLOAD_CMD_FILE PACK4('U','S','B','s') /* file size request */ 76 | #define USBLOAD_CMD_FILE_REQ PACK4('U','S','B','f') /* file size resp */ 77 | #define USBLOAD_CMD_JUMP PACK4('U','S','B','j') /* execute code here */ 78 | #define USBLOAD_CMD_ECHO_SZ PACK4('U','S','B','n') /* file size confirm to */ 79 | #define USBLOAD_CMD_REPORT_SZ PACK4('U','S','B','o') /* confirm full file */ 80 | #define USBLOAD_CMD_MESSAGE PACK4('U','S','B','m') /* debug message */ 81 | 82 | /* USB transfer characteristics */ 83 | #define USB_MAX_WAIT 5000 84 | #define USB_TIMEOUT 1000 85 | #define USB_MAX_TIMEOUTS (USB_MAX_WAIT/USB_TIMEOUT) 86 | 87 | /* Datatypes 88 | */ 89 | 90 | /* stores the data and attributes of a file to upload in to memory */ 91 | struct file_upload 92 | { 93 | size_t size; 94 | unsigned char * data; 95 | uint32_t addr; 96 | char * path; 97 | char * basename; 98 | }; 99 | 100 | /* stores all of the arguments read in by getopt in main() */ 101 | struct arg_state 102 | { 103 | struct file_upload ** files; 104 | int numFiles; 105 | uint32_t jumpTarget; 106 | uint16_t vendor, product; 107 | }; 108 | 109 | /* Datatypes 110 | */ 111 | 112 | static int g_verbose = 0; 113 | 114 | /* Function Prototypes 115 | */ 116 | 117 | /* Poll for a USB device matching vendor:product and return the open handle */ 118 | libusb_device_handle * 119 | omap_usb_open(libusb_context * ctx, uint16_t vendor, uint16_t product); 120 | 121 | /* Grab the string from the specified index. Returns dynamic memory 122 | * which is caller freed */ 123 | unsigned char * 124 | omap_usb_get_string(libusb_device_handle * handle, uint8_t idx); 125 | 126 | bool omap_usb_read(libusb_device_handle * handle, unsigned char * data, 127 | int length, int * actualLength); 128 | bool omap_usb_write(libusb_device_handle * handle, unsigned char * data, 129 | int length); 130 | 131 | unsigned char * read_file(char * path, size_t * readamt); 132 | 133 | /* process the argument list from main() */ 134 | int process_args(struct arg_state * args); 135 | 136 | /* negotiate with the BootROM to transfer the first stage */ 137 | int transfer_first_stage(libusb_device_handle * handle, 138 | struct arg_state * args); 139 | 140 | /* negotiate with the X-loader (expected) to transfer the other files */ 141 | int transfer_other_files(libusb_device_handle * handle, 142 | struct arg_state * args); 143 | 144 | /* standard usage function */ 145 | void usage(char * exe); 146 | 147 | void log_error(char * fmt, ...); 148 | void log_info(char * fmt, ...); 149 | 150 | /* Function Declarations 151 | */ 152 | 153 | libusb_device_handle * 154 | omap_usb_open(libusb_context * ctx, uint16_t vendor, uint16_t product) 155 | { 156 | libusb_device ** devlist; 157 | libusb_device_handle * handle; 158 | struct libusb_device_descriptor desc; 159 | 160 | ssize_t count, i; 161 | int ret; 162 | bool found = false; 163 | 164 | log_info("scanning for USB device matching %04hx:%04hx...\n", 165 | vendor, product); 166 | 167 | while(!found) 168 | { 169 | if((count = libusb_get_device_list(ctx, &devlist)) < 0) { 170 | log_error("failed to gather USB device list: %s\n", 171 | libusb_error_name(count)); 172 | return NULL; 173 | } 174 | 175 | for(i = 0; i < count; i++) 176 | { 177 | if((ret = libusb_get_device_descriptor(devlist[i], &desc)) < 0) { 178 | log_error("failed to get USB device descriptor: %s\n", 179 | libusb_error_name(ret)); 180 | libusb_free_device_list(devlist, 1); 181 | return NULL; 182 | } 183 | 184 | if(desc.idVendor == vendor && desc.idProduct == product) 185 | { 186 | if((ret = libusb_open(devlist[i], &handle)) < 0) 187 | { 188 | log_error("failed to open USB device %04hx:%04hx: %s\n", 189 | vendor, product, libusb_error_name(ret)); 190 | libusb_free_device_list(devlist, 1); 191 | return NULL; 192 | } 193 | 194 | found = true; 195 | break; 196 | } 197 | } 198 | 199 | /* clean up our device list */ 200 | libusb_free_device_list(devlist, 1); 201 | 202 | /* nothing found yet. have a 10ms nap */ 203 | if(!found) 204 | usleep(10000); 205 | } 206 | 207 | /* grab the manufacturer and product strings for printing */ 208 | unsigned char * mfgStr = omap_usb_get_string(handle, desc.iManufacturer); 209 | unsigned char * prodStr = omap_usb_get_string(handle, desc.iProduct); 210 | 211 | log_info("successfully opened %04hx:%04hx (", vendor, product); 212 | 213 | if(mfgStr) 214 | { 215 | fprintf(stdout, prodStr ? "%s " : "%s", mfgStr); 216 | free(mfgStr); 217 | } 218 | 219 | if(prodStr) 220 | { 221 | fprintf(stdout, "%s", prodStr); 222 | free(prodStr); 223 | } 224 | 225 | fprintf(stdout, ")\n"); 226 | 227 | return handle; 228 | } 229 | 230 | unsigned char * 231 | omap_usb_get_string(libusb_device_handle * handle, uint8_t idx) 232 | { 233 | unsigned char * data = NULL; 234 | int len = 0; 235 | int ret = 0; 236 | 237 | if(!handle) 238 | return NULL; 239 | 240 | while(true) 241 | { 242 | if(!len || ret < 0) 243 | { 244 | len += 256; 245 | data = realloc(data, len); 246 | 247 | if(!data) 248 | return NULL; 249 | } 250 | 251 | ret = libusb_get_string_descriptor_ascii(handle, idx, data, len); 252 | 253 | /* we can still recover... */ 254 | if(ret < 0) 255 | { 256 | if(ret == LIBUSB_ERROR_INVALID_PARAM) 257 | continue; /* try again with an increased size */ 258 | 259 | log_error("failed to lookup string index %hhu: %s\n", 260 | idx, libusb_error_name(ret)); 261 | 262 | /* unrecoverable */ 263 | free(data); 264 | return NULL; 265 | } 266 | /* we got something! */ 267 | else 268 | break; 269 | } 270 | 271 | return data; 272 | } 273 | 274 | unsigned char * read_file(char * path, size_t * readamt) 275 | { 276 | FILE * fp = fopen(path, "rb"); 277 | 278 | if(!fp) 279 | { 280 | log_error("failed to open file \'%s\': %s\n", path, strerror(errno)); 281 | return NULL; 282 | } 283 | 284 | unsigned char * data = NULL; 285 | size_t allocSize = 0; 286 | size_t iter = 0; 287 | 288 | while(1) 289 | { 290 | if(iter >= allocSize) 291 | { 292 | allocSize += 1024; 293 | data = realloc(data, allocSize); 294 | 295 | if(!data) 296 | return NULL; 297 | } 298 | 299 | size_t readSize = allocSize - iter; 300 | size_t ret = fread(data+iter, sizeof(unsigned char), readSize, fp); 301 | 302 | iter += ret; 303 | 304 | if(ret != readSize) 305 | { 306 | if(feof(fp)) 307 | { 308 | break; 309 | } 310 | else if(ferror(fp)) 311 | { 312 | log_error("error file reading file \'%s\': %s\n", 313 | path, strerror(errno)); 314 | free(data); 315 | return NULL; 316 | } 317 | } 318 | } 319 | 320 | /* trim the allocation down to size */ 321 | data = realloc(data, iter); 322 | *readamt = iter; 323 | 324 | return data; 325 | } 326 | 327 | int process_args(struct arg_state * args) 328 | { 329 | int i; 330 | 331 | /* For each file, load it in to memory 332 | * TODO: defer this until transfer time (save memory and pipeline IO) 333 | */ 334 | 335 | for(i = 0; i < args->numFiles; i++) 336 | { 337 | struct file_upload * f = args->files[i]; 338 | 339 | f->data = read_file(f->path, &f->size); 340 | 341 | if(!f->data) 342 | { 343 | return 1; 344 | } 345 | } 346 | 347 | if(g_verbose > 0) 348 | { 349 | for(i = 0; i < args->numFiles; i++) 350 | { 351 | struct file_upload * f = args->files[i]; 352 | 353 | printf("File \'%s\' at 0x%08x, size %zu\n", 354 | f->basename, f->addr, f->size); 355 | } 356 | } 357 | 358 | libusb_context * ctx; 359 | libusb_device_handle * dev; 360 | int ret; 361 | 362 | if((ret = libusb_init(&ctx)) < 0) 363 | { 364 | log_error("failed to initialize libusb context: %s\n", 365 | libusb_error_name(ret)); 366 | return ret; 367 | } 368 | 369 | dev = omap_usb_open(ctx, args->vendor, args->product); 370 | 371 | if(!dev) 372 | { 373 | libusb_exit(ctx); 374 | return 1; 375 | } 376 | 377 | if((ret = libusb_claim_interface(dev, 0)) < 0) 378 | { 379 | log_error("failed to claim interface: %s\n", libusb_error_name(ret)); 380 | return ret; 381 | } 382 | 383 | /* Communicate with the TI BootROM directly 384 | - retrieve ASIC ID 385 | - start peripheral boot 386 | - upload first file 387 | - execute first file 388 | */ 389 | if(!transfer_first_stage(dev, args)) 390 | { 391 | log_error("failed to transfer the first stage file \'%s\'\n", 392 | args->files[0]->basename); 393 | goto fail; 394 | } 395 | 396 | /* Wait a moment before attempting to communicate with X-loader. If we don't 397 | * wait here, some computers end up failing to upload additional files. 398 | */ 399 | usleep(20000); 400 | 401 | /* If we are passed one file, assume that the user just wants to 402 | upload some initial code with no X-loader chaining 403 | */ 404 | if(args->numFiles > 1) 405 | { 406 | if(!transfer_other_files(dev, args)) 407 | { 408 | log_error("failed to transfer the additional files in to memory\n"); 409 | goto fail; 410 | } 411 | } 412 | 413 | log_info("successfully transfered %d %s\n", args->numFiles, 414 | (args->numFiles > 1) ? "files" : "file"); 415 | 416 | /* safely close our USB handle and context */ 417 | libusb_close(dev); 418 | libusb_exit(ctx); 419 | return 0; 420 | 421 | fail: 422 | libusb_close(dev); 423 | libusb_exit(ctx); 424 | 425 | return 1; 426 | } 427 | 428 | bool omap_usb_read(libusb_device_handle * handle, unsigned char * data, 429 | int length, int * actualLength) 430 | { 431 | int ret = 0; 432 | int iter = 0; 433 | int sizeLeft = length; 434 | 435 | if(!actualLength) 436 | return false; 437 | 438 | while(sizeLeft > 0) 439 | { 440 | int actualRead = 0; 441 | int readAmt = sizeLeft; 442 | 443 | usleep(1000); 444 | ret = libusb_bulk_transfer(handle, OMAP_USB_BULK_IN, data+iter, 445 | readAmt, &actualRead, USB_TIMEOUT); 446 | 447 | if(ret == LIBUSB_ERROR_TIMEOUT) 448 | { 449 | sizeLeft -= actualRead; 450 | iter += actualRead; 451 | 452 | /* we got some data, lets cut our losses and stop here */ 453 | if(iter > 0) 454 | break; 455 | 456 | log_error( 457 | "device timed out while transfering in %d bytes (got %d)\n", 458 | length, iter); 459 | 460 | return false; 461 | } 462 | else if(ret == LIBUSB_SUCCESS) 463 | { 464 | /* we cant trust actualRead on anything but a timeout or success */ 465 | sizeLeft -= actualRead; 466 | iter += actualRead; 467 | 468 | /* stop at the first sign of data */ 469 | if(iter > 0) 470 | break; 471 | } 472 | else 473 | { 474 | log_error( 475 | "fatal transfer error (BULK_IN) for %d bytes (got %d): %s\n", 476 | length, iter, libusb_error_name(ret)); 477 | return false; 478 | } 479 | } 480 | 481 | *actualLength = iter; 482 | 483 | return true; 484 | } 485 | 486 | bool omap_usb_write(libusb_device_handle * handle, unsigned char * data, 487 | int length) 488 | { 489 | int ret = 0; 490 | int numTimeouts = 0; 491 | int iter = 0; 492 | int sizeLeft = length; 493 | 494 | while(sizeLeft > 0) 495 | { 496 | int actualWrite = 0; 497 | int writeAmt = sizeLeft; 498 | /* Only send 512 bytes at a time. Helps with reliability sometimes. */ 499 | if(writeAmt > 512) { writeAmt = 512; } 500 | 501 | usleep(1000); 502 | ret = libusb_bulk_transfer(handle, OMAP_USB_BULK_OUT, data+iter, 503 | writeAmt, &actualWrite, USB_TIMEOUT); 504 | 505 | if(ret == LIBUSB_ERROR_TIMEOUT) 506 | { 507 | numTimeouts++; 508 | sizeLeft -= actualWrite; 509 | iter += actualWrite; 510 | 511 | /* build in some reliablity */ 512 | if(numTimeouts > USB_MAX_TIMEOUTS) 513 | { 514 | log_error( 515 | "device timed out while transfering out %d bytes (%d made it)\n", 516 | length, iter); 517 | return false; 518 | } 519 | } 520 | else if(ret == LIBUSB_SUCCESS) 521 | { 522 | /* we cant trust actualWrite on anything but a timeout or success */ 523 | sizeLeft -= actualWrite; 524 | iter += actualWrite; 525 | } 526 | else 527 | { 528 | log_error( 529 | "fatal transfer error (BULK_OUT) for %d bytes (%d made it): %s\n", 530 | length, iter, libusb_error_name(ret)); 531 | return false; 532 | } 533 | } 534 | 535 | return true; 536 | } 537 | 538 | int transfer_first_stage(libusb_device_handle * handle, struct arg_state * args) 539 | { 540 | unsigned char * buffer = NULL; 541 | uint32_t cmd = 0; 542 | uint32_t filelen = 0; 543 | int bufSize = 0x200; 544 | int transLen = 0; 545 | int i; 546 | 547 | struct file_upload * file = args->files[0]; 548 | 549 | /* TODO determine buffer size based on endpoint */ 550 | buffer = calloc(bufSize, sizeof(unsigned char)); 551 | filelen = cpu_to_le32(file->size); 552 | 553 | /* read the ASIC ID */ 554 | if(!omap_usb_read(handle, buffer, bufSize, &transLen)) 555 | { 556 | log_error("failed to read ASIC ID from USB connection. " 557 | "Check your USB device!\n"); 558 | goto fail; 559 | } 560 | 561 | if(transLen != OMAP_ASIC_ID_LEN) 562 | { 563 | log_error("got some ASIC ID, but it's not the right length, %d " 564 | "(expected %d)\n", transLen, OMAP_ASIC_ID_LEN); 565 | goto fail; 566 | } 567 | 568 | /* optionally, print some ASIC ID info */ 569 | if(g_verbose) 570 | { 571 | char * fields[] = { "Num Subblocks", "Device ID Info", "Reserved", 572 | "Ident Data", "Reserved", "CRC (4 bytes)"}; 573 | int fieldLen[] = {1, 7, 4, 23, 23, 11}; 574 | int field = 0; 575 | int nextSep = 0; 576 | 577 | log_info("got ASIC ID - "); 578 | 579 | for(i = 0; i < transLen; i++) 580 | { 581 | if(i == nextSep) 582 | { 583 | fprintf(stdout, "%s%s ", (field > 0) ? ", " : "", fields[field]); 584 | nextSep += fieldLen[field]; 585 | field++; 586 | 587 | fprintf(stdout, "["); 588 | } 589 | 590 | fprintf(stdout, "%02x", buffer[i]); 591 | 592 | if(i+1 == nextSep) 593 | fprintf(stdout, "]"); 594 | } 595 | 596 | fprintf(stdout, "\n"); 597 | } 598 | 599 | /* send the peripheral boot command */ 600 | cmd = cpu_to_le32(OMAP_PERIPH_BOOT); 601 | 602 | if(!omap_usb_write(handle, (unsigned char *)&cmd, sizeof(cmd))) 603 | { 604 | log_error("failed to send the peripheral boot command 0x%08x\n", 605 | OMAP_PERIPH_BOOT); 606 | goto fail; 607 | } 608 | 609 | /* send the length of the first file (little endian) */ 610 | if(!omap_usb_write(handle, (unsigned char *)&filelen, sizeof(filelen))) 611 | { 612 | log_error("failed to length specifier of %u to OMAP BootROM\n", filelen); 613 | goto fail; 614 | } 615 | 616 | /* send the file! */ 617 | if(!omap_usb_write(handle, file->data, file->size)) 618 | { 619 | log_error("failed to send file \'%s\' (size %u)\n", 620 | file->basename, filelen); 621 | goto fail; 622 | } 623 | 624 | free(buffer); 625 | return 1; 626 | 627 | fail: 628 | free(buffer); 629 | return 0; 630 | } 631 | 632 | int transfer_other_files(libusb_device_handle * handle, struct arg_state * args) 633 | { 634 | uint32_t * buffer = NULL; 635 | int bufSize = 128*sizeof(*buffer); 636 | int numFailures = 0; /* build in some reliablity */ 637 | int maxFailures = 3; 638 | int transLen = 0; 639 | int curFile = 1; /* skip the first file */ 640 | 641 | buffer = calloc(bufSize, sizeof(unsigned char)); 642 | 643 | /* handle the state machine for the X-Loader */ 644 | while(curFile < args->numFiles) 645 | { 646 | uint32_t opcode = 0; 647 | uint8_t * extra = NULL; 648 | struct file_upload * f = args->files[curFile]; 649 | 650 | /* read the opcode from xloader ID */ 651 | if(!omap_usb_read(handle, (unsigned char *)buffer, bufSize, &transLen)) 652 | { 653 | numFailures++; 654 | 655 | if(numFailures >= maxFailures) 656 | { 657 | log_error("failed to read command from X-Loader\n"); 658 | goto fail; 659 | } 660 | 661 | /* sleep a bit */ 662 | usleep(2000*1000); /* 2s */ 663 | continue; /* try the opcode read again */ 664 | } 665 | 666 | if(transLen < 8) 667 | { 668 | log_error("failed to recieve enough data for the opcode\n"); 669 | goto fail; 670 | } 671 | 672 | /* extract the opcode and extra data pointer */ 673 | opcode = le32_to_cpu(buffer[0]); 674 | extra = (uint8_t *)buffer; 675 | 676 | switch(opcode) 677 | { 678 | /* X-loader is requesting a file to be sent */ 679 | case USBLOAD_CMD_FILE_REQ: 680 | /* send the opcode, size, and addr */ 681 | buffer[0] = cpu_to_le32(USBLOAD_CMD_FILE); 682 | buffer[1] = cpu_to_le32(f->size); 683 | buffer[2] = cpu_to_le32(f->addr); 684 | 685 | if(!omap_usb_write(handle, (unsigned char *)buffer, sizeof(*buffer)*3)) 686 | { 687 | log_error("failed to send load file command to the X-loader\n"); 688 | goto fail; 689 | } 690 | 691 | if(g_verbose) 692 | { 693 | log_info("uploading \'%s\' (size %zu) to 0x%08x\n", 694 | f->basename, f->size, f->addr); 695 | } 696 | break; 697 | /* X-loader confirms the size to recieve */ 698 | case USBLOAD_CMD_ECHO_SZ: 699 | if(buffer[1] != f->size) 700 | { 701 | log_error("X-loader failed to recieve the right file size for " 702 | "file \'%s\' (got %u, expected %zu)\n", 703 | f->basename, buffer[1], f->size); 704 | goto fail; 705 | } 706 | 707 | /* upload the raw file data */ 708 | 709 | if(!omap_usb_write(handle, f->data, f->size)) 710 | { 711 | log_error("failed to send file \'%s\' to the X-loader\n", f->basename); 712 | goto fail; 713 | } 714 | break; 715 | /* X-loader confirms the amount of data it recieved */ 716 | case USBLOAD_CMD_REPORT_SZ: 717 | if(buffer[1] != f->size) 718 | { 719 | log_error("X-loader failed to recieve the right amount of data for " 720 | "file \'%s\' (got %u, expected %zu)\n", 721 | f->basename, buffer[1], f->size); 722 | goto fail; 723 | } 724 | 725 | curFile++; /* move on to the next file */ 726 | break; 727 | /* X-loader debug message */ 728 | case USBLOAD_CMD_MESSAGE: 729 | { 730 | size_t len = strlen((char *)extra); 731 | 732 | if(len > (bufSize - sizeof(opcode) - 1)) 733 | log_error("X-loader debug message not NUL terminated (size %zu)\n", 734 | len); 735 | else 736 | fprintf(stdout, "X-loader Debug: %s\n", extra); 737 | 738 | break; 739 | } 740 | default: 741 | log_error("unknown X-Loader opcode 0x%08X (%c%c%c%c)\n", 742 | opcode, extra[0], extra[1], extra[2], extra[3]); 743 | goto fail; 744 | } 745 | } 746 | 747 | /* we're done uploading files to X-loader send the jump command */ 748 | int jumpCmd[3]; 749 | jumpCmd[0] = cpu_to_le32(USBLOAD_CMD_JUMP); 750 | jumpCmd[1] = cpu_to_le32(args->jumpTarget); 751 | jumpCmd[2] = 0; /* padding? */ 752 | 753 | log_info( 754 | "sending jump command: 0x%08x, 0x%08x, %d\n", 755 | jumpCmd[0], 756 | jumpCmd[1], 757 | sizeof(jumpCmd)); 758 | 759 | if(!omap_usb_write(handle, (unsigned char *)jumpCmd, sizeof(jumpCmd))) 760 | { 761 | log_error("failed to send the final jump command to the X-loader. " 762 | "Target was 0x%08x\n", args->jumpTarget); 763 | goto fail; 764 | } 765 | 766 | if(g_verbose) 767 | log_info("jumping to address 0x%08x\n", args->jumpTarget); 768 | 769 | free(buffer); 770 | return 1; 771 | 772 | fail: 773 | free(buffer); 774 | return 0; 775 | } 776 | 777 | /* getopt configuration */ 778 | int do_version = 0; 779 | const char * const short_opt= "f:a:j:i:p:vh"; 780 | const struct option long_opt[] = 781 | { 782 | {"file", 1, NULL, 'f'}, 783 | {"addr", 1, NULL, 'a'}, 784 | {"jump", 1, NULL, 'j'}, 785 | {"vendor", 1, NULL, 'i'}, 786 | {"product", 1, NULL, 'p'}, 787 | {"verbose", 0, NULL, 'v'}, 788 | {"help", 0, NULL, 'h'}, 789 | {"version", 0, &do_version, 1}, 790 | {NULL, 0, NULL, 0 } 791 | }; 792 | 793 | void usage(char * exe) 794 | { 795 | printf("Usage: %s [options] -f first-stage [-f file -a addr]...\n", exe); 796 | printf("Options:\n"); 797 | printf(" -f, --file Provide the filename of a binary file to be\n"); 798 | printf(" uploaded. The first file specified is uploaded to\n"); 799 | printf(" the fixed address 0x%08x as defined by the manual.\n", 800 | OMAP_BASE_ADDRESS); 801 | printf(" Additional files must be followed by an address\n"); 802 | printf(" argument (-a).\n"); 803 | printf(" -a, --addr The address to load the prior file at.\n"); 804 | printf(" -j, --jump Specify the address to jump to after loading all\n"); 805 | printf(" of the files in to memory.\n"); 806 | printf(" -i, --vendor Override the default vendor ID to poll for\n"); 807 | printf(" (default 0x%04x).\n", OMAP_VENDOR_ID); 808 | printf(" -p, --product Override the default product ID to poll for\n"); 809 | printf(" (default 0x%04x).\n", OMAP_PRODUCT_ID); 810 | printf(" -h, --help Display this message.\n"); 811 | printf(" -v, --verbose Enable verbose output.\n"); 812 | printf("\n"); 813 | printf("Description:\n"); 814 | printf(" %s's basic usage is to upload an arbitrary file in to the memory\n", 815 | PROG_NAME); 816 | printf(" of a TI OMAP3 compatible processor. This program directly\n"); 817 | printf(" communicates with the TI BootROM in order to upload a first stage\n"); 818 | printf(" payload, in most cases, TI's X-Loader. Using a compatible X-Loader\n"); 819 | printf(" will enable the upload of any file to any part in device memory.\n"); 820 | printf("\n"); 821 | printf("Examples:\n"); 822 | printf( 823 | " Uploading a compatible X-Loader, U-Boot, Kernel, and RAMDisk, then jumping\n" 824 | " to the U-Boot image for further bootloading:\n"); 825 | printf( 826 | " %s -f x-load.bin -f u-boot.bin -a 0x80200000 -f uImage -a 0x80800000 \\\n" 827 | " -f uRamdisk -a 0x81000000 -j 0x80200000\n", exe); 828 | printf( 829 | " Uploading arbitrary code to be executed (doesn't have to be X-loader):\n"); 830 | printf( 831 | " %s -f exec_me.bin\n", exe); 832 | printf(" Trying to debug an upload issue using verbose output:\n"); 833 | printf(" %s -v -f exec_me.bin -f file1.bin -a 0xdeadbeef -j 0xabad1dea\n", 834 | exe); 835 | printf("\n"); 836 | printf("Authors:\n"); 837 | printf( 838 | " Grant Hernandez - rewrite of omap3_usbload to\n"); 839 | printf( 840 | " use the newer libusb 1.0\n"); 841 | printf( 842 | " Martin Mueller - initial code (omap3_usbload)\n"); 843 | printf( 844 | " and X-Loader patch\n"); 845 | } 846 | 847 | void license() 848 | { 849 | printf("Copyright (C) 2008 Martin Mueller \n"); 850 | printf("Copyright (C) 2014 Grant Hernandez \n"); 851 | printf("License GPLv2: GNU GPL version 2 or later .\n"); 852 | printf("This is free software: you are free to change and redistribute it.\n"); 853 | printf("There is NO WARRANTY, to the extent permitted by law.\n"); 854 | } 855 | 856 | void log_error(char * fmt, ...) 857 | { 858 | va_list va; 859 | 860 | va_start(va, fmt); 861 | fprintf(stdout, "[-] "); 862 | vfprintf(stdout, fmt, va); 863 | va_end(va); 864 | } 865 | 866 | void log_info(char * fmt, ...) 867 | { 868 | va_list va; 869 | 870 | va_start(va, fmt); 871 | fprintf(stdout, "[+] "); 872 | vfprintf(stdout, fmt, va); 873 | va_end(va); 874 | } 875 | 876 | 877 | int main(int argc, char * argv[]) 878 | { 879 | int opt; 880 | bool gotFile = false; 881 | bool gotJump = false; 882 | int fileCount = 0; 883 | char * exe = NULL; 884 | 885 | /* temporary local file object */ 886 | struct file_upload file; 887 | /* total arg state */ 888 | struct arg_state * args = calloc(1, sizeof(*args)); 889 | 890 | log_info("jump command: 0x%08x\n", USBLOAD_CMD_JUMP); 891 | log_info("jump command le: 0x%08x\n", cpu_to_le32(USBLOAD_CMD_JUMP)); 892 | 893 | if(argc < 1) 894 | { 895 | log_error("invalid arguments (no argv[0])\n"); 896 | return 1; 897 | } 898 | 899 | exe = argv[0]; 900 | 901 | fprintf(stdout, "%s %s\n", PROG_NAME, VERSION); 902 | 903 | /* set the default vendor and product */ 904 | args->vendor = OMAP_VENDOR_ID; 905 | args->product = OMAP_PRODUCT_ID; 906 | 907 | while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) 908 | { 909 | switch(opt) 910 | { 911 | case 0: 912 | if(do_version) { 913 | license(); 914 | return 0; 915 | } 916 | break; 917 | case 'f': 918 | { 919 | if(gotFile) 920 | { 921 | log_error("missing address argument (-a) for file \'%s\'\n", 922 | file.path); 923 | usage(exe); 924 | return 1; 925 | } 926 | 927 | file.path = strdup(optarg); 928 | 929 | /* necessary to be sure that we own all the memory 930 | and that the path input can be modified */ 931 | char * tmpPath = strdup(file.path); 932 | file.basename = strdup(basename(tmpPath)); 933 | free(tmpPath); 934 | 935 | fileCount++; 936 | 937 | /* the first file gets uploaded to a fixed address 938 | as specified by the technical reference manual */ 939 | if(fileCount == 1) 940 | { 941 | file.addr = OMAP_BASE_ADDRESS; 942 | 943 | /* commit the file object with the processor specified base address */ 944 | args->files = realloc(args->files, fileCount * sizeof(struct file_upload *)); 945 | args->numFiles = fileCount; 946 | args->files[fileCount-1] = malloc(sizeof(file)); 947 | memcpy(args->files[fileCount-1], &file, sizeof(file)); 948 | } 949 | else 950 | { 951 | /* commit only after an address is specified */ 952 | gotFile = true; 953 | } 954 | break; 955 | } 956 | case 'a': 957 | if(!gotFile) 958 | { 959 | log_error("missing file argument (-f) before address \'%s\'\n", 960 | optarg); 961 | usage(exe); 962 | return 1; 963 | } 964 | 965 | /* passing 0 to strtoul enables detection of the 0x prefix with 966 | base-10 fallback if missing */ 967 | file.addr = strtoul(optarg, NULL, 0); 968 | 969 | /* commit the file object */ 970 | args->files = realloc(args->files, fileCount * sizeof(struct file_upload *)); 971 | args->numFiles = fileCount; 972 | args->files[fileCount-1] = malloc(sizeof(file)); 973 | memcpy(args->files[fileCount-1], &file, sizeof(file)); 974 | 975 | gotFile = false; 976 | break; 977 | case 'j': 978 | args->jumpTarget = strtoul(optarg, NULL, 0); 979 | gotJump = true; 980 | break; 981 | case 'i': 982 | args->vendor = (uint16_t)strtoul(optarg, NULL, 0); 983 | break; 984 | case 'p': 985 | args->product = (uint16_t)strtoul(optarg, NULL, 0); 986 | break; 987 | case 'v': 988 | g_verbose++; 989 | break; 990 | case 'h': 991 | usage(exe); 992 | return 0; 993 | default: 994 | usage(exe); 995 | return 1; 996 | } 997 | } 998 | 999 | if(gotFile) 1000 | { 1001 | log_error("got file \'%s\', but no address!\n", file.path); 1002 | usage(exe); 1003 | return 1; 1004 | } 1005 | 1006 | if(args->numFiles <= 0) 1007 | { 1008 | log_error("at least one file needs to be specified\n"); 1009 | usage(exe); 1010 | return 1; 1011 | } 1012 | 1013 | if(args->numFiles == 1 && gotJump) 1014 | { 1015 | log_info("WARNING: jump target 0x%08x specified, but will never be taken " 1016 | "(more than one file required)\n", args->jumpTarget); 1017 | } 1018 | else if(args->numFiles > 1 && !gotJump) 1019 | { 1020 | log_info("WARNING: no jump target specified. Defaulting to the first " 1021 | "file's (\'%s\') address 0x%08x\n", 1022 | args->files[1]->basename, args->files[1]->addr); 1023 | args->jumpTarget = args->files[1]->addr; 1024 | } 1025 | 1026 | return process_args(args); 1027 | } --------------------------------------------------------------------------------