12 Commits
v2.5 ... nbdkit

Author SHA1 Message Date
Bjorn Andersson
c6200a2068 nbdkit: Don't compile in qdl.c
qdl.c, with its main method, should not be included in the nbdkit.  Add
the global qdl_debug variable to nbdkit and expose it as an nbdkit
configuration option.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-10-02 22:27:25 -07:00
Bjorn Andersson
44d148a63b firhose: Fix max payload size
The returned max payload size is no longer passed back through the
return value of the response parser, resulting in us configuring a max
payload size of 0 (success). Update the logic to use the new calling
scheme.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-10-02 22:27:25 -07:00
Bjorn Andersson
c37088f3fd README: Document nbdkit-qdl-plugin
Document the nbdkit-qdl-plugin and how to use it.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:43:12 -07:00
Bjorn Andersson
e3d3811748 nbdkit: Add nbdkit plugin
With the refactoring leading up to the firehose implementation being
driven by the client code and the USB code extracted we can implement
nbdkit plugin exposing the flash storage to the host PC.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:40:41 -07:00
Bjorn Andersson
9aae1e9ff2 qdl: Extract USB functions
Move the USB functions to a separate file, so that we can build the
nbdkit plugin without including qdl.c.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:40:11 -07:00
Bjorn Andersson
bff56ea21b firehose: Apply programs and patches from main
Move the program and patch invocation to the main function.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:39:53 -07:00
Bjorn Andersson
5b4874667c firehose: Implement generic read and write functions
Add generic block read and write functions, to allow implementing the
block level access needed in the nbdkit plugin.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:38:40 -07:00
Bjorn Andersson
e1a3670d91 firehose: Move set_bootable call to main()
Move the call to set_bootable out of firehose_run(), with the goal of
removing firehose_run()

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:38:10 -07:00
Bjorn Andersson
bb1aa99068 firehose: Extract firehose init
By extracting the initial parts of firehose_run() we get closer to being
able to reuse the firehose code in the upcoming nbdkit plugin.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:35:36 -07:00
Bjorn Andersson
342e99b9be firehose: Extract call to firehose_reset()
Expose firehose_reset() and call this from the main() function instead,
allowing the code to be reused between qdl and the nbdkit plugin.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:34:56 -07:00
Bjorn Andersson
066022e6d8 firehose: Introduce interface for querying the size of a LUN
Implement a function to query the storage information, to allow the
nbdkit plugin to query the size of each storage device.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:33:18 -07:00
Bjorn Andersson
2d239db0ca firehose: Simplify firehose_read()
With the move the USBFS firehose_read() can be simplified and allow us
to implement the parser for the LOG lines carrying the response of the
getStorageInfo request.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-06 22:25:43 -07:00
51 changed files with 1924 additions and 6245 deletions

View File

@@ -1,35 +0,0 @@
--no-tree
--strict
--max-line-length=120
--ignore FILE_PATH_CHANGES
--ignore EMAIL_SUBJECT
--ignore SPLIT_STRING
# NEW_TYPEDEFS reports "do not add new typedefs"
# typedef struct __attribute__((__packed__)) sparse_header {
--ignore NEW_TYPEDEFS
# PREFER_DEFINED_ATTRIBUTE_MACRO reports this kind of messages:
# WARNING: Prefer __packed over __attribute__((__packed__))
--ignore PREFER_DEFINED_ATTRIBUTE_MACRO
# PREFER_KERNEL_TYPES reports this kind of messages (when using --strict):
# "Prefer kernel type 'u32' over 'uint32_t'"
--ignore PREFER_KERNEL_TYPES
# BRACES reports this kind of messages:
# braces {} are not necessary for any arm of this statement
--ignore BRACES
# CAMELCASE reports this kind of messages:
# Avoid CamelCase: <xmlFreeDoc>
--ignore CAMELCASE
# AVOID_EXTERNS reports this kind of messages:
# externs should be avoided in .c files
# extern const char *__progname;
--ignore AVOID_EXTERNS
# COMMIT_LOG_LONG_LINE reports line lengths > 75 in commit log
# Lets ignore this
--ignore COMMIT_LOG_LONG_LINE

View File

@@ -1,176 +0,0 @@
name: Buildtest
on:
pull_request:
push:
jobs:
build-linux:
strategy:
fail-fast: false
matrix:
include:
- { arch: x64, os: 24, runner: ubuntu-24.04 }
- { arch: arm64, os: 24, runner: ubuntu-24.04-arm }
- { arch: x64, os: 22, runner: ubuntu-22.04 }
- { arch: arm64, os: 22, runner: ubuntu-22.04-arm }
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev help2man
- name: Build
run: make
- name: Run tests
run: make tests
- name: Generate man pages
run: make manpages
- name: Package
run: |
mkdir dist
cp `pkg-config --variable=libdir libusb-1.0`/libusb-1.0.so.0 dist
chmod 0644 dist/*
cp qdl dist
patchelf --set-rpath '$ORIGIN' dist/qdl
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: qdl-binary-ubuntu-${{matrix.os}}-${{ matrix.arch }}
path: dist/*
build-mac:
strategy:
fail-fast: false
matrix:
include:
- { sys: macos-14, arch: arm64 }
- { sys: macos-15-intel, arch: intel }
runs-on: ${{ matrix.sys }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Dependencies
run: |
brew install libxml2 help2man
- name: Build
run: make
- name: Generate man pages
run: make manpages
- name: Run tests
run: make tests
- name: Package
run: |
set -x
mkdir dist
cp `pkg-config --variable=libdir libusb-1.0`/libusb-1.0.0.dylib dist
cp `pkg-config --variable=libdir liblzma`/liblzma.5.dylib dist
chmod 0644 dist/*
cp qdl dist
if uname -a | grep -q arm64; then
LIBUSB_DIR=/opt/homebrew/opt/libusb/lib
LIBLZMA_DIR=/usr/lib
else
LIBUSB_DIR=/usr/local/opt/libusb/lib
LIBLZMA_DIR=/usr/local/opt/xz/lib
fi
install_name_tool -add_rpath @executable_path dist/qdl
install_name_tool -change $LIBUSB_DIR/libusb-1.0.0.dylib @rpath/libusb-1.0.0.dylib dist/qdl
install_name_tool -change $LIBLZMA_DIR/liblzma.5.dylib @rpath/liblzma.5.dylib dist/qdl
otool -L dist/qdl
dist/qdl || true
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: qdl-binary-macos-${{ matrix.arch }}
path: dist/*
build-windows:
strategy:
fail-fast: false
matrix:
include:
- { sys: windows-latest, arch: x64 }
- { sys: windows-11-arm, arch: arm64 }
runs-on: ${{ matrix.sys }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup MSYS2
id: msys2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: >
base-devel
git
help2man
mingw-w64-x86_64-gcc
mingw-w64-x86_64-make
mingw-w64-x86_64-pkg-config
mingw-w64-x86_64-libxml2
mingw-w64-x86_64-libusb
mingw-w64-x86_64-xz
- name: Build
run: |
git config --global core.autocrlf true
make
shell: msys2 {0}
- name: Generate man pages
run: make manpages
shell: msys2 {0}
- name: Run tests
run: make tests
shell: msys2 {0}
- name: Package
shell: pwsh
run: |
$MSYS2_LOCATION = "${{ steps.msys2.outputs.msys2-location }}"
$BIN_DIR = Join-Path $MSYS2_LOCATION "mingw64\bin"
$DistDir = "dist"
New-Item -ItemType Directory -Path $DistDir | Out-Null
Copy-Item (Join-Path $BIN_DIR "zlib1.dll") $DistDir
Copy-Item (Join-Path $BIN_DIR "libxml2-16.dll") $DistDir
Copy-Item (Join-Path $BIN_DIR "libusb-1.0.dll") $DistDir
Copy-Item (Join-Path $BIN_DIR "liblzma-5.dll") $DistDir
Copy-Item (Join-Path $BIN_DIR "libiconv-2.dll") $DistDir
Copy-Item "qdl.exe" $DistDir
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: qdl-binary-windows-${{ matrix.arch }}
path: dist/*

View File

@@ -1,15 +0,0 @@
name: Checkpatch Review
on: [pull_request]
jobs:
check-patch:
name: checkpatch review
runs-on: ubuntu-latest
steps:
- name: 'Calculate PR commits + 1'
run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> $GITHUB_ENV
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Run checkpatch review
uses: webispy/checkpatch-action@v9

View File

@@ -1,36 +0,0 @@
name: CodeQL
on:
pull_request:
push:
jobs:
codeql:
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
- name: CodeQL init
uses: github/codeql-action/init@v3
with:
languages: c-cpp
build-mode: autobuild
- name: CodeQL build
uses: github/codeql-action/autobuild@v3
- name: CodeQL analysis
uses: github/codeql-action/analyze@v3

View File

@@ -1,11 +0,0 @@
name: Markdown Lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v20
with:
globs: |
README.md

10
.gitignore vendored
View File

@@ -1,12 +1,2 @@
*.1
*.o
qdl
qdl-ramdump
ks
*.exe
compile_commands.json
.cache
.version.h
version.h
scripts
.checkpatch-camelcase.git.

View File

@@ -1,92 +1,33 @@
QDL := qdl
RAMDUMP := qdl-ramdump
VERSION := $(or $(VERSION), $(shell git describe --dirty --always --tags 2>/dev/null), "unknown-version")
OUT := qdl
NBDKIT := nbdkit-qdl-plugin.so
PKG_CONFIG ?= pkg-config
CFLAGS += -O2 -Wall -g `$(PKG_CONFIG) --cflags libxml-2.0 libusb-1.0`
LDFLAGS += `$(PKG_CONFIG) --libs libxml-2.0 libusb-1.0`
ifeq ($(OS),Windows_NT)
LDFLAGS += -lws2_32
endif
CFLAGS := -O2 -Wall -g `xml2-config --cflags` -fPIC
LDFLAGS := `xml2-config --libs` -ludev
prefix := /usr/local
QDL_SRCS := firehose.c io.c qdl.c sahara.c util.c patch.c program.c read.c sha2.c sim.c ufs.c usb.c ux.c oscompat.c vip.c sparse.c gpt.c
QDL_OBJS := $(QDL_SRCS:.c=.o)
COMMON_SRCS := firehose.c json.c sahara.c util.c patch.c program.c ufs.c usb.c
RAMDUMP_SRCS := ramdump.c sahara.c io.c sim.c usb.c util.c ux.c oscompat.c
RAMDUMP_OBJS := $(RAMDUMP_SRCS:.c=.o)
QDL_SRCS := qdl.c
QDL_OBJS := $(COMMON_SRCS:.c=.o) $(QDL_SRCS:.c=.o)
KS_OUT := ks
KS_SRCS := ks.c sahara.c util.c ux.c oscompat.c
KS_OBJS := $(KS_SRCS:.c=.o)
CHECKPATCH_SOURCES := $(shell find . -type f \( -name "*.c" -o -name "*.h" -o -name "*.sh" \) ! -name "sha2.c" ! -name "sha2.h" ! -name "*version.h" ! -name "list.h")
CHECKPATCH_ROOT := https://raw.githubusercontent.com/torvalds/linux/v6.15/scripts
CHECKPATCH_URL := $(CHECKPATCH_ROOT)/checkpatch.pl
CHECKPATCH_SP_URL := $(CHECKPATCH_ROOT)/spelling.txt
CHECKPATCH := ./.scripts/checkpatch.pl
CHECKPATCH_SP := ./.scripts/spelling.txt
MANPAGES := ks.1 qdl-ramdump.1 qdl.1
default: $(QDL) $(RAMDUMP) $(KS_OUT)
$(QDL): $(QDL_OBJS)
$(OUT): $(QDL_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
$(RAMDUMP): $(RAMDUMP_OBJS)
NBDKIT_SRCS := nbdkit-qdl-plugin.c
NBDKIT_OBJS := $(COMMON_SRCS:.c=.o) $(NBDKIT_SRCS:.c=.o)
$(NBDKIT): LDFLAGS += -shared
$(NBDKIT): $(NBDKIT_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
$(KS_OUT): $(KS_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
.PHONY: lib
lib: $(NBDKIT)
compile_commands.json: $(QDL_SRCS) $(KS_SRCS)
@echo -n $^ | jq -snR "[inputs|split(\" \")[]|{directory:\"$(PWD)\", command: \"$(CC) $(CFLAGS) -c \(.)\", file:.}]" > $@
manpages: $(KS_OUT) $(RAMDUMP) $(QDL)
help2man -N -n "KS" -o ks.1 ./ks
help2man -N -n "Qualcomm Download" -o qdl.1 ./qdl
help2man -N -n "Qualcomm Download Ramdump" -o qdl-ramdump.1 ./qdl-ramdump
version.h::
@echo "#define VERSION \"$(VERSION)\"" > .version.h
@cmp -s .version.h version.h || cp .version.h version.h
util.o: version.h
.PHONY: all
all: $(OUT) $(NBDKIT)
clean:
rm -f $(QDL) $(QDL_OBJS)
rm -f $(RAMDUMP) $(RAMDUMP_OBJS)
rm -f $(KS_OUT) $(KS_OBJS)
rm -f $(MANPAGES)
rm -f compile_commands.json
rm -f version.h .version.h
rm -f $(CHECKPATCH)
rm -f $(CHECKPATCH_SP)
if [ -d .scripts ]; then rmdir .scripts; fi
rm -f $(OUT) $(QDL_OBJS) $(NBDKIT_OBJS)
install: $(QDL) $(RAMDUMP) $(KS_OUT)
install -d $(DESTDIR)$(prefix)/bin
install -m 755 $^ $(DESTDIR)$(prefix)/bin
tests: default
tests:
@./tests/run_tests.sh
# Target to download checkpatch.pl if not present
$(CHECKPATCH):
@echo "Downloading checkpatch.pl..."
@mkdir -p $(dir $(CHECKPATCH))
@curl -sSfL $(CHECKPATCH_URL) -o $(CHECKPATCH)
@curl -sSfL $(CHECKPATCH_SP_URL) -o $(CHECKPATCH_SP)
@chmod +x $(CHECKPATCH)
check: $(CHECKPATCH)
@echo "Running checkpatch on source files (excluding sha2.c and sha2.h)..."
@for file in $(CHECKPATCH_SOURCES); do \
perl $(CHECKPATCH) --no-tree -f $$file || exit 1; \
done
check-cached: $(CHECKPATCH)
@echo "Running checkpatch on staged changes..."
@git diff --cached -- . | perl $(CHECKPATCH) --no-tree -
install: $(OUT)
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<

43
README Normal file
View File

@@ -0,0 +1,43 @@
Qualcomm Download
=================
This tool communicates with USB devices of id 05c6:9008 to upload a flash
loader and use this to flash images.
Usage:
qdl <prog.mbn> [<program> <patch> ...]
nbdkit plugin
=============
In addition to the qdl programmer a nbdkit plugin can be built. This provides
block layer access to the flash on the device, allowing partition tables to be
modified and partitions mounted on the host PC.
The nbdkit plugin has the following configuration parameters:
programmer: firehose programmer
lun: LUN number to be managed, defaults to 1
storage: "ufs" or "emmc" to select firehose configuration
To launch a nbd server, launch nbdkit as follows:
modprobe nbd
nbdkit -fv ./nbdkit-qdl-plugin.so programmer=prog_firehose_ddr.elf lun=0
Then connect nbd to this, using:
sudo nbd-client -b 4096 localhost /dev/nbd0
After this /dev/nbd0 and /dev/nbd0p* can be used to access the full storage
device, as well as the individual partitions on the device.
To disconnect the nbd client, unmount any partitions and run:
sudo nbd-client -d /dev/nbd0
Building
========
In order to build the project you need libxml2 headers and libraries, found in
e.g. the libxml2-dev package.
With this installed run:
make
or to build the nbdkit plugin run:
make lib

271
README.md
View File

@@ -1,271 +0,0 @@
# Qualcomm Download
[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Build on push](https://github.com/linux-msm/qdl/actions/workflows/build.yml/badge.svg)](https://github.com/linux-msm/qdl/actions/workflows/build.yml/badge.svg)
This tool communicates with USB devices of id `05c6:9008` to upload a flash
loader and use this to flash images.
## Build
### Linux
```bash
sudo apt install libxml2 libusb-1.0-0-dev help2man
make
```
### MacOS
For Homebrew users,
```bash
brew install libxml2 pkg-config libusb help2man
make
```
For MacPorts users
```bash
sudo port install libxml2 pkgconfig libusb help2man
make
```
### Windows
First, install the [MSYS2 environment](https://www.msys2.org/). Then, run the
MSYS2 MinGW64 terminal (located at `<msys2-installation-path>\mingw64.exe`) and
install additional packages needed for QDL compilation using the `pacman` tool:
```bash
pacman -S base-devel --needed
pacman -S git
pacman -S help2man
pacman -S mingw-w64-x86_64-gcc
pacman -S mingw-w64-x86_64-make
pacman -S mingw-w64-x86_64-pkg-config
pacman -S mingw-w64-x86_64-libusb
pacman -S mingw-w64-x86_64-libxml2
```
Then use the `make` tool to build QDL:
```bash
make
```
## Use QDL
### EDL mode
The device intended for flashing must be booted into **Emergency Download (EDL)**
mode. EDL is a special boot mode available on Qualcomm-based devices that provides
low-level access for firmware flashing and recovery. It bypasses the standard boot
process, allowing operations such as flashing firmware even on unresponsive devices
or those with locked bootloaders.
Please consult your device’s documentation for instructions on how to enter EDL mode.
### Flash device
Run QDL with the `--help` option to view detailed usage information.
Below is an example of how to invoke QDL to flash a FLAT build:
```bash
qdl --dry-run prog_firehose_ddr.elf rawprogram*.xml patch*.xml
```
If you have multiple boards connected the host, provide the serial number of
the board to flash through `--serial` param:
```bash
qdl --serial=0AA94EFD prog_firehose_ddr.elf rawprogram*.xml patch*.xml
```
### Reading and writing raw binaries
In addition to flashing builds using their XML-based descriptions, QDL supports
reading and writing binaries directly.
```bash
qdl prog_firehose_ddr.elf [read | write] [address specifier] <binary>...
```
Multiple read and write commands can be specified at once. The ***address
specifier*** can take the forms:
- N - single number, specifies the physical partition number N to write the
***binary** into, starting at sector 0 (currently reading a whole physical
partition is not supported).
- N/S - two numbers, specifies the physical partition number N, and the start
sector S, to write the ***binary*** into (reading with an offset is not
supported)
- N/S+L - three numbers, specified the physical partition number N, the start
sector S and the number of sectors L, that ***binary*** should be written to,
or which should be read into ***binary***.
- partition name - a string, will match against partition names across the GPT
partition tables on all physical partitions.
- N/partition_name - single number, followed by string - will match against
partition names of the GPT partition table in the specified physical
partition N.
### Validated Image Programming (VIP)
QDL now supports **Validated Image Programming (VIP)** mode , which is activated
when Secure Boot is enabled on the target. VIP controls which packets are allowed
to be issued to the target. Controlling the packets that can be sent to the target
is done through hashing. The target applies a hashing function to all received data,
comparing the resulting hash digest against an existing digest table in memory.
If the calculated hash digest matches the next entry in the table, the packet
(data or command) is accepted; otherwise, the packet is rejected,and the target halts.
To use VIP programming, a digest table must be generated prior to flashing the device.
To generate table of digests run QDL with `--create-digests` param,
providing a path to store VIP tables. For example:
```bash
mkdir vip
qdl --create-digests=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xml
```
As a result 3 types of files are generated:
- `DIGEST_TABLE.bin` - contains the SHA256 table of digests for all firehose
packets to be sent to the target. It is an intermediary table and is
used only for the subsequent generation of `DigestsToSign.bin` and
`ChainedTableOfDigests\<n\>.bin` files. It is not used by QDL for VIP
programming.
- `DigestsToSign.bin` - first 53 digests + digest of `ChainedTableOfDigests.bin`.
This file has to be converted to MBN format and then signed with sectools:
```bash
sectools mbn-tool generate --data DigestsToSign.bin --mbn-version 6 --outfile DigestsToSign.bin.mbn
sectools secure-image --sign DigestsToSign.bin.mbn --image-id=VIP
```
Please check the security profile for your SoC to determine which version of
the MBN format should be used.
- `ChainedTableOfDigests\<n\>.bin` - contains left digests, split on
multiple files with 255 digests + appended hash of next table.
To flash board using VIP mode provide a path where previously generated and signed
table of digests are stored using `--vip-table-path` param:
```bash
qdl --vip-table-path=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xml
```
### Multi-programmer targets
On some targets multiple files need to be loaded in order to reach the
Firehose programmer, these targets will request multiple images over Sahara.
Three mechanisms for providing these images are provided:
#### Command line argument
The *programmer* argument, allows specifying a comma-separated list of
colon-separated "id" and "filename" pairs. Each filename should refer to the
Sahara image of the specified Sahara image id.
```bash
qdl 13:prog_firehose_ddr.elf,42:the-answer rawprogram.xml
```
#### Sahara configuration XML file
Flattened METAs does include the various images that need to be loaded to
enter Firehose mode, as well as a sahara_config XML file, which defines the
Sahara image id for each of these images.
If the specified device programmer is determined to be a Sahara configuration
XML file, it will be parsed and the referred to files will be loaded and
serviced to the device upon request.
```bash
qdl sahara_programmer.xml rawprogram.xml
```
#### Programmer archive
Directly providing a list of ids and filenames is cumbersome and error prone,
QDL therefore accepts a "*programmer archive*". This allows the user to use the
tool in the same fashion as was done for single-programmer targets.
The *programmer archive* is a CPIO archive containing the Sahara images to be
loaded, identified by the filename **id[:filename]** (*filename* is optional,
but useful for debugging). Each included file will be used to serve requests
for the given Sahara *id*.
Such an archive can be created by putting the target's programmer images in an
empty directory, then in that directory execute the command:
```bash
ls | cpio -o -H newc > ../programmer.cpio
```
*programmer.cpio* can now be passed to QDL and the included images will be
served, in order to reach Firehose mode.
## Run tests
To run the integration test suite for QDL, use the `make tests` target:
```bash
make tests
```
## Generate man pages
Manpages can be generated using `make manpages` target:
```bash
make manpages
```
## Contributing
Please submit any patches to the qdl (`master` branch) by using the GitHub pull
request feature. Fork the repo, create a branch, do the work, rebase with upstream,
and submit the pull request.
The preferred coding style for this tool is [Linux kernel coding style](https://www.kernel.org/doc/html/v6.15/process/coding-style.html).
Before creating a commit, please ensure that your changes adhere to the coding style
by using the `make check-cached` target, for example:
```bash
$ git status
On branch improvements
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: qdl.c
modified: qdl.h
$ make check-cached
Running checkpatch on staged changes...
ERROR: trailing whitespace
#28: FILE: qdl.h:32:
+^IQDL_DEVICE_USB, $
total: 1 errors, 0 warnings, 0 checks, 27 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
NOTE: Whitespace errors detected.
You may wish to use scripts/cleanpatch or scripts/cleanfile
Your patch has style problems, please review.
```
## License
This tool is licensed under the BSD 3-Clause licensed. Check out [LICENSE](LICENSE)
for more detais.

1118
firehose.c

File diff suppressed because it is too large Load Diff

283
gpt.c
View File

@@ -1,283 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <stdlib.h>
#include <string.h>
#define _FILE_OFFSET_BITS 64
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include "qdl.h"
#include "gpt.h"
struct gpt_guid {
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
} __attribute__((packed));
static const struct gpt_guid gpt_zero_guid = {0};
struct gpt_header {
uint8_t signature[8];
uint32_t revision;
uint32_t header_size;
uint32_t header_crc32;
uint32_t reserved;
uint64_t current_lba;
uint64_t backup_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
struct gpt_guid disk_guid;
uint64_t part_entry_lba;
uint32_t num_part_entries;
uint32_t part_entry_size;
uint32_t part_array_crc32;
uint8_t reserved2[420];
} __attribute__((packed));
struct gpt_entry {
struct gpt_guid type_guid;
struct gpt_guid unique_guid;
uint64_t first_lba;
uint64_t last_lba;
uint64_t attrs;
uint16_t name_utf16le[36];
} __attribute__((packed));
struct gpt_partition {
const char *name;
unsigned int partition;
unsigned int start_sector;
unsigned int num_sectors;
struct gpt_partition *next;
};
static struct gpt_partition *gpt_partitions;
static struct gpt_partition *gpt_partitions_last;
static void utf16le_to_utf8(uint16_t *in, size_t in_len, uint8_t *out, size_t out_len)
{
uint32_t codepoint;
uint16_t high;
uint16_t low;
uint16_t w;
size_t i;
size_t j = 0;
for (i = 0; i < in_len; i++) {
w = in[i];
if (w >= 0xd800 && w <= 0xdbff) {
high = w - 0xd800;
if (i < in_len) {
w = in[++i];
if (w >= 0xdc00 && w <= 0xdfff) {
low = w - 0xdc00;
codepoint = (((uint32_t)high << 10) | low) + 0x10000;
} else {
/* Surrogate without low surrogate */
codepoint = 0xfffd;
}
} else {
/* Lone high surrogate at end of string */
codepoint = 0xfffd;
}
} else if (w >= 0xdc00 && w <= 0xdfff) {
/* Low surrogate without high */
codepoint = 0xfffd;
} else {
codepoint = w;
}
if (codepoint == 0)
break;
if (codepoint <= 0x7f) {
if (j + 1 >= out_len)
break;
out[j++] = (uint8_t)codepoint;
} else if (codepoint <= 0x7ff) {
if (j + 2 >= out_len)
break;
out[j++] = 0xc0 | ((codepoint >> 6) & 0x1f);
out[j++] = 0x80 | (codepoint & 0x3f);
} else if (codepoint <= 0xffff) {
if (j + 3 >= out_len)
break;
out[j++] = 0xe0 | ((codepoint >> 12) & 0x0f);
out[j++] = 0x80 | ((codepoint >> 6) & 0x3f);
out[j++] = 0x80 | (codepoint & 0x3f);
} else if (codepoint <= 0x10ffff) {
if (j + 4 >= out_len)
break;
out[j++] = 0xf0 | ((codepoint >> 18) & 0x07);
out[j++] = 0x80 | ((codepoint >> 12) & 0x3f);
out[j++] = 0x80 | ((codepoint >> 6) & 0x3f);
out[j++] = 0x80 | (codepoint & 0x3f);
}
}
out[j] = '\0';
}
static int gpt_load_table_from_partition(struct qdl_device *qdl, unsigned int phys_partition, bool *eof)
{
struct gpt_partition *partition;
struct gpt_entry *entry;
struct gpt_header gpt;
uint8_t buf[4096];
struct read_op op;
unsigned int offset;
uint64_t lba;
char lba_buf[21];
uint16_t name_utf16le[36];
char name[36 * 4];
int ret;
unsigned int i;
memset(&op, 0, sizeof(op));
op.sector_size = qdl->sector_size;
op.start_sector = "1";
op.num_sectors = 1;
op.partition = phys_partition;
memset(&buf, 0, sizeof(buf));
ret = firehose_read_buf(qdl, &op, &gpt, sizeof(gpt));
if (ret) {
/* Assume that we're beyond the last partition */
*eof = true;
return -1;
}
if (memcmp(gpt.signature, "EFI PART", 8)) {
ux_err("partition %d has not GPT header\n", phys_partition);
return 0;
}
if (gpt.part_entry_size > qdl->sector_size || gpt.num_part_entries > 1024) {
ux_debug("partition %d has invalid GPT header\n", phys_partition);
return -1;
}
ux_debug("Loading GPT table from physical partition %d\n", phys_partition);
for (i = 0; i < gpt.num_part_entries; i++) {
offset = (i * gpt.part_entry_size) % qdl->sector_size;
if (offset == 0) {
lba = gpt.part_entry_lba + i * gpt.part_entry_size / qdl->sector_size;
snprintf(lba_buf, sizeof(lba_buf), "%" PRIu64, lba);
op.start_sector = lba_buf;
memset(buf, 0, sizeof(buf));
ret = firehose_read_buf(qdl, &op, buf, sizeof(buf));
if (ret) {
ux_err("failed to read GPT partition entries from %d:%u\n", phys_partition, lba);
return -1;
}
}
entry = (struct gpt_entry *)(buf + offset);
if (!memcmp(&entry->type_guid, &gpt_zero_guid, sizeof(struct gpt_guid)))
continue;
memcpy(name_utf16le, entry->name_utf16le, sizeof(name_utf16le));
utf16le_to_utf8(name_utf16le, 36, (uint8_t *)name, sizeof(name));
partition = calloc(1, sizeof(*partition));
partition->name = strdup(name);
partition->partition = phys_partition;
partition->start_sector = entry->first_lba;
/* if first_lba == last_lba there is 1 sector worth of data (IE: add 1 below) */
partition->num_sectors = entry->last_lba - entry->first_lba + 1;
ux_debug(" %3d: %s start sector %u, num sectors %u\n", i, partition->name,
partition->start_sector, partition->num_sectors);
if (gpt_partitions) {
gpt_partitions_last->next = partition;
gpt_partitions_last = partition;
} else {
gpt_partitions = partition;
gpt_partitions_last = partition;
}
}
return 0;
}
static int gpt_load_tables(struct qdl_device *qdl)
{
unsigned int i;
bool eof = false;
int ret = 0;
if (gpt_partitions)
return 0;
for (i = 0; ; i++) {
ret = gpt_load_table_from_partition(qdl, i, &eof);
if (ret)
break;
}
return eof ? 0 : ret;
}
int gpt_find_by_name(struct qdl_device *qdl, const char *name, int *phys_partition,
unsigned int *start_sector, unsigned int *num_sectors)
{
struct gpt_partition *gpt_part;
bool found = false;
int ret;
if (qdl->dev_type == QDL_DEVICE_SIM)
return 0;
ret = gpt_load_tables(qdl);
if (ret < 0)
return -1;
for (gpt_part = gpt_partitions; gpt_part; gpt_part = gpt_part->next) {
if (*phys_partition >= 0 && gpt_part->partition != (unsigned int)(*phys_partition))
continue;
if (strcmp(gpt_part->name, name))
continue;
if (found) {
ux_err("duplicate candidates for partition \"%s\" found\n", name);
return -1;
}
*phys_partition = gpt_part->partition;
*start_sector = gpt_part->start_sector;
*num_sectors = gpt_part->num_sectors;
found = true;
}
if (!found) {
if (*phys_partition >= 0)
ux_err("no partition \"%s\" found on physical partition %d\n", name, *phys_partition);
else
ux_err("no partition \"%s\" found\n", name);
return -1;
}
return 0;
}

10
gpt.h
View File

@@ -1,10 +0,0 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __GPT_H__
#define __GPT_H__
struct qdl_device;
int gpt_find_by_name(struct qdl_device *qdl, const char *name, int *partition,
unsigned int *start_sector, unsigned int *num_sectors);
#endif

69
io.c
View File

@@ -1,69 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <stdlib.h>
#include "qdl.h"
struct qdl_device *qdl_init(enum QDL_DEVICE_TYPE type)
{
if (type == QDL_DEVICE_USB)
return usb_init();
if (type == QDL_DEVICE_SIM)
return sim_init();
return NULL;
}
void qdl_deinit(struct qdl_device *qdl)
{
if (qdl)
free(qdl);
}
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size)
{
qdl->set_out_chunk_size(qdl, size);
}
int qdl_open(struct qdl_device *qdl, const char *serial)
{
return qdl->open(qdl, serial);
}
void qdl_close(struct qdl_device *qdl)
{
qdl->close(qdl);
}
/**
* qdl_read() - Read a message from the device
* @qdl: device handle
* @buf: buffer to write the data into
* @len: maximum length of data to be read
* @timeout: timeout for the read, in milliseconds
*
* Returns: number of bytes read, might be zero for a ZLP
* negative errno on failure (notably -ETIMEDOUT)
*/
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
{
return qdl->read(qdl, buf, len, timeout);
}
/**
* qdl_write() - Write a message from the device
* @qdl: device handle
* @buf: buffer with data to be written
* @len: length of data to be written
* @timeout: timeout for write, in milliseconds
*
* Returns: number of bytes read
* negative errno on failure (notably -ETIMEDOUT)
*/
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, unsigned int timeout)
{
return qdl->write(qdl, buf, len, timeout);
}

451
json.c Normal file
View File

@@ -0,0 +1,451 @@
/*
* Copyright (c) 2018-2019, Linaro Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 HOLDER 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.
*/
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "json.h"
static const char *input_buf;
static int input_pos;
static int input_len;
static int json_parse_array(struct json_value *array);
static int json_parse_object(struct json_value *object);
static int json_parse_property(struct json_value *value);
static int input(void)
{
if (input_pos >= input_len)
return 0;
return input_buf[input_pos++];
}
static void unput(void)
{
input_pos--;
}
static void json_skip_whitespace(void)
{
int ch;
while ((ch = input()) && isspace(ch))
;
unput();
}
static int json_parse_string(struct json_value *value)
{
char buf[128];
char *b = buf;
int ch;
ch = input();
if (ch != '"') {
unput();
return 0;
}
while ((ch = input()) && ch != '"')
*b++ = ch;
*b = '\0';
if (!ch)
return -1;
value->type = JSON_TYPE_STRING;
value->u.string = strdup(buf);
return 1;
}
static int json_parse_number(struct json_value *value)
{
char buf[20];
char *b = buf;
int ch;
while ((ch = input()) && isdigit(ch) && b - buf < sizeof(buf) - 1)
*b++ = ch;
*b = '\0';
unput();
if (b == buf)
return 0;
value->type = JSON_TYPE_NUMBER;
value->u.number = strtod(buf, NULL);
return 1;
}
static int json_parse_keyword(struct json_value *value)
{
const char *match;
const char *m;
int ch;
ch = input();
switch (ch) {
case 't':
match = "true";
value->type = JSON_TYPE_TRUE;
break;
case 'f':
match = "false";
value->type = JSON_TYPE_FALSE;
break;
case 'n':
match = "null";
value->type = JSON_TYPE_NULL;
break;
default:
unput();
return 0;
}
m = match;
while (*m && *m++ == ch)
ch = input();
unput();
return *m == '\0' ? 1 : -1;
}
static int json_parse_value(struct json_value *value)
{
int ret;
json_skip_whitespace();
ret = json_parse_object(value);
if (ret)
goto out;
ret = json_parse_array(value);
if (ret)
goto out;
ret = json_parse_string(value);
if (ret)
goto out;
ret = json_parse_number(value);
if (ret)
goto out;
ret = json_parse_keyword(value);
if (ret)
goto out;
fprintf(stderr, "unable to match a value\n");
return -1;
out:
json_skip_whitespace();
return ret;
}
static int json_parse_array(struct json_value *array)
{
struct json_value *value;
struct json_value *last = NULL;
int ret;
int ch;
ch = input();
if (ch != '[') {
unput();
return 0;
}
array->type = JSON_TYPE_ARRAY;
do {
value = calloc(1, sizeof(*value));
if (!value)
return -1;
ret = json_parse_value(value);
if (ret <= 0) {
free(value);
return -1;
}
if (!array->u.value)
array->u.value = value;
if (last)
last->next = value;
last = value;
ch = input();
if (ch == ']') {
return 1;
}
} while (ch == ',');
fprintf(stderr, "expected ',' got '%c'\n", ch);
return -1;
}
static int json_parse_object(struct json_value *object)
{
struct json_value *value;
struct json_value *last = NULL;
int ret;
int ch;
ch = input();
if (ch != '{') {
unput();
return 0;
}
object->type = JSON_TYPE_OBJECT;
do {
value = calloc(1, sizeof(*value));
if (!value)
return -1;
ret = json_parse_property(value);
if (ret <= 0) {
free(value);
return -1;
}
if (!object->u.value)
object->u.value = value;
if (last)
last->next = value;
last = value;
ch = input();
if (ch == '}') {
return 1;
}
} while (ch == ',');
return -1;
}
static int json_parse_property(struct json_value *value)
{
struct json_value key;
int ret;
int ch;
json_skip_whitespace();
ret = json_parse_string(&key);
if (ret <= 0)
return -1;
value->key = key.u.string;
json_skip_whitespace();
ch = input();
if (ch != ':')
return -1;
ret = json_parse_value(value);
if (ret <= 0)
return -1;
return 1;
}
struct json_value *json_parse(const char *json)
{
struct json_value *root;
int ret;
input_buf = json;
input_pos = 0;
input_len = strlen(input_buf);
root = calloc(1, sizeof(*root));
if (!root)
return NULL;
ret = json_parse_value(root);
if (ret != 1) {
free(root);
return NULL;
}
return root;
}
struct json_value *json_parse_file(const char *file)
{
struct json_value *root;
struct stat sb;
int ret;
int fd;
fd = open(file, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "failed to open %s: %s\n", file, strerror(errno));
return NULL;
}
ret = fstat(fd, &sb);
if (ret < 0)
return NULL;
input_pos = 0;
input_len = sb.st_size;
input_buf = malloc(sb.st_size);
ret = read(fd, (char *)input_buf, input_len);
close(fd);
if (ret != input_len) {
fprintf(stderr, "failed to read %d bytes form %s\n", input_len, file);
return NULL;
}
root = calloc(1, sizeof(*root));
if (!root)
return NULL;
ret = json_parse_value(root);
if (ret != 1) {
free(root);
return NULL;
}
return root;
}
struct json_value *json_get_child(struct json_value *object, const char *key)
{
struct json_value *it;
if(object->type != JSON_TYPE_OBJECT)
return NULL;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key))
return it;
}
return NULL;
}
int json_count_children(struct json_value *array)
{
struct json_value *it;
int count = 0;
if (!array || array->type != JSON_TYPE_ARRAY)
return -1;
for (it = array->u.value; it; it = it->next)
count++;
return count;
}
int json_get_number(struct json_value *object, const char *key, double *number)
{
struct json_value *it;
if (!object || object->type != JSON_TYPE_OBJECT)
return -1;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key)) {
if (it->type != JSON_TYPE_NUMBER)
return -1;
*number = it->u.number;
return 0;
}
}
return -1;
}
const char *json_get_string(struct json_value *object, const char *key)
{
struct json_value *it;
if (!object || object->type != JSON_TYPE_OBJECT)
return NULL;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key)) {
if (it->type != JSON_TYPE_STRING)
return NULL;
return it->u.string;
}
}
return NULL;
}
void json_free(struct json_value *value)
{
struct json_value *next;
struct json_value *it;
free((char *)value->key);
switch (value->type) {
case JSON_TYPE_OBJECT:
case JSON_TYPE_ARRAY:
it = value->u.value;
while (it) {
next = it->next;
json_free(it);
it = next;
}
break;
case JSON_TYPE_STRING:
free((char *)value->u.string);
break;
}
free(value);
}

67
json.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2019, Linaro Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 HOLDER 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.
*/
#ifndef __JSON_H__
#define __JSON_H__
enum {
JSON_TYPE_UNKNOWN,
JSON_TYPE_TRUE,
JSON_TYPE_FALSE,
JSON_TYPE_NULL,
JSON_TYPE_NUMBER,
JSON_TYPE_STRING,
JSON_TYPE_ARRAY,
JSON_TYPE_OBJECT,
};
struct json_value {
const char *key;
int type;
union {
double number;
const char *string;
struct json_value *value;
} u;
struct json_value *next;
};
struct json_value *json_parse(const char *json);
struct json_value *json_parse_file(const char *file);
int json_count_children(struct json_value *array);
struct json_value *json_get_child(struct json_value *object, const char *key);
int json_get_number(struct json_value *object, const char *key, double *number);
const char *json_get_string(struct json_value *object, const char *key);
void json_free(struct json_value *value);
#endif

142
ks.c
View File

@@ -1,142 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "qdl.h"
#include "oscompat.h"
#ifdef _WIN32
const char *__progname = "ks";
#endif
static struct qdl_device qdl;
bool qdl_debug;
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout __unused)
{
return read(qdl->fd, buf, len);
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, unsigned int timeout __unused)
{
return write(qdl->fd, buf, len);
}
static void print_usage(FILE *out)
{
extern const char *__progname;
fprintf(out,
"%s -p <sahara dev_node> -s <id:file path> ...\n",
__progname);
fprintf(out,
" -h --help Print this usage info\n"
" -p --port Sahara device node to use\n"
" -s <id:file path> --sahara <id:file path> Sahara protocol file mapping\n"
"\n"
"One -p instance is required. One or more -s instances are required.\n"
"\n"
"Example:\n"
"ks -p /dev/mhi0_QAIC_SAHARA -s 1:/opt/qti-aic/firmware/fw1.bin -s 2:/opt/qti-aic/firmware/fw2.bin\n");
}
int main(int argc, char **argv)
{
struct sahara_image mappings[MAPPING_SZ] = {};
const char *filename;
bool found_mapping = false;
char *dev_node = NULL;
long file_id;
char *colon;
int opt;
int ret;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{"port", required_argument, 0, 'p'},
{"sahara", required_argument, 0, 's'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "dvp:s:h", options, NULL)) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'v':
print_version();
return 0;
case 'p':
dev_node = optarg;
printf("Using port - %s\n", dev_node);
break;
case 's':
found_mapping = true;
file_id = strtol(optarg, NULL, 10);
if (file_id < 0) {
print_usage(stderr);
return 1;
}
if (file_id >= MAPPING_SZ) {
fprintf(stderr,
"ID:%ld exceeds the max value of %d\n",
file_id,
MAPPING_SZ - 1);
return 1;
}
colon = strchr(optarg, ':');
if (!colon) {
print_usage(stderr);
return 1;
}
filename = &optarg[colon - optarg + 1];
ret = load_sahara_image(filename, &mappings[file_id]);
if (ret < 0)
exit(1);
printf("Created mapping ID:%ld File:%s\n", file_id, filename);
break;
case 'h':
print_usage(stdout);
return 0;
default:
print_usage(stderr);
return 1;
}
}
// -p and -s is required
if (!dev_node || !found_mapping) {
print_usage(stderr);
return 1;
}
if (qdl_debug)
print_version();
qdl.fd = open(dev_node, O_RDWR);
if (qdl.fd < 0) {
fprintf(stderr, "Unable to open %s\n", dev_node);
return 1;
}
ret = sahara_run(&qdl, mappings, NULL, NULL);
if (ret < 0)
return 1;
return 0;
}

73
list.h
View File

@@ -1,73 +0,0 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright (c) 2016, Linaro Ltd.
*/
#ifndef __LIST_H__
#define __LIST_H__
#include <stdbool.h>
#include <stddef.h>
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_INIT(list) { &(list), &(list) }
static inline void list_init(struct list_head *list)
{
list->prev = list;
list->next = list;
}
static inline bool list_empty(struct list_head *list)
{
return list->next == list;
}
static inline void list_add(struct list_head *list, struct list_head *item)
{
struct list_head *prev = list->prev;
item->next = list;
item->prev = prev;
prev->next = item;
list->prev = item;
}
static inline void list_del(struct list_head *item)
{
item->prev->next = item->next;
item->next->prev = item->prev;
}
#define list_for_each(item, list) \
for (item = (list)->next; item != list; item = item->next)
#define list_for_each_safe(item, tmp, list) \
for (item = (list)->next, tmp = item->next; item != list; item = tmp, tmp = item->next)
#define list_entry(item, type, member) \
container_of(item, type, member)
#define list_entry_first(list, type, member) \
container_of((list)->next, type, member)
#define list_entry_next(item, member) \
container_of((item)->member.next, typeof(*(item)), member)
#define list_for_each_entry(item, list, member) \
for (item = list_entry_first(list, typeof(*(item)), member); \
&item->member != list; \
item = list_entry_next(item, member))
#define list_for_each_entry_safe(item, next, list, member) \
for (item = list_entry_first(list, typeof(*(item)), member), \
next = list_entry_next(item, member); \
&item->member != list; \
item = next, \
next = list_entry_next(item, member)) \
#endif

146
nbdkit-qdl-plugin.c Normal file
View File

@@ -0,0 +1,146 @@
#define NBDKIT_API_VERSION 2
#include <nbdkit-plugin.h>
#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <libudev.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "qdl.h"
#include "patch.h"
#include "ufs.h"
#include "usb.h"
static const char *config_programmer;
static bool config_ufs = true;
static int config_lun = 1;
static size_t sector_size;
bool qdl_debug;
static int qdl_config(const char *key, const char *value)
{
if (!strcmp(key, "programmer")) {
config_programmer = nbdkit_absolute_path(value);
} else if (!strcmp(key, "storage")) {
if (!strcmp(value, "ufs")) {
config_ufs = true;
} else if (!strcmp(value, "emmc")) {
config_ufs = false;
} else {
nbdkit_error("unknown storage type '%s'", value);
return -1;
}
} else if (!strcmp(key, "lun")) {
config_lun = atoi(value);
} else if (!strcmp(key, "debug")) {
qdl_debug = true;
} else {
return -1;
}
return 0;
}
static int qdl_config_complete(void)
{
if (!config_programmer)
return -1;
return 0;
}
static void *qdl_open(int readonly)
{
struct qdl_device *qdl;
int ret;
qdl = usb_open();
if (!qdl)
return NULL;
ret = sahara_run(qdl, config_programmer);
if (ret < 0)
return NULL;
ret = firehose_open(qdl, config_ufs);
if (ret < 0)
return NULL;
return qdl;
}
static void qdl_close(void *handle)
{
struct qdl_device *qdl = handle;
firehose_reset(qdl);
free(qdl);
}
static int64_t qdl_get_size(void *handle)
{
struct qdl_device *qdl = handle;
size_t num_sectors;
int ret;
ret = firehose_getsize(qdl, config_lun, &sector_size, &num_sectors);
if (ret < 0)
return -1;
return sector_size * num_sectors;
}
static int qdl_pread(void *handle, void *buf, uint32_t count, uint64_t offset,
uint32_t flags)
{
struct qdl_device *qdl = handle;
return firehose_pread(qdl, config_lun, offset / sector_size, buf,
sector_size, count / sector_size);
}
static int qdl_pwrite(void *handle, const void *buf, uint32_t count,
uint64_t offset, uint32_t flags)
{
struct qdl_device *qdl = handle;
return firehose_pwrite(qdl, config_lun, offset / sector_size, buf,
sector_size, count / sector_size);
}
static struct nbdkit_plugin qdl_plugin = {
.name = "qdl",
.description = "nbdkit Qualcomm Download plugin",
.open = qdl_open,
.close = qdl_close,
.config = qdl_config,
.config_complete = qdl_config_complete,
.get_size = qdl_get_size,
.pread = qdl_pread,
.pwrite = qdl_pwrite,
};
NBDKIT_REGISTER_PLUGIN(qdl_plugin)

View File

@@ -1,76 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
#ifdef _WIN32
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "oscompat.h"
extern const char *__progname;
void timeradd(const struct timeval *a, const struct timeval *b, struct timeval *result)
{
result->tv_sec = a->tv_sec + b->tv_sec;
result->tv_usec = a->tv_usec + b->tv_usec;
if (result->tv_usec >= 1000000) {
result->tv_sec += 1;
result->tv_usec -= 1000000;
}
}
void err(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", __progname);
if (fmt) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": ");
}
fprintf(stderr, "%s\n", strerror(errno));
va_end(ap);
exit(eval);
}
void errx(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", __progname);
if (fmt)
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(eval);
}
void warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", __progname);
if (fmt) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": ");
}
fprintf(stderr, "%s\n", strerror(errno));
va_end(ap);
}
void warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", __progname);
if (fmt)
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
#endif // _WIN32

View File

@@ -1,25 +0,0 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __OSCOMPAT_H__
#define __OSCOMPAT_H__
#ifndef _WIN32
#include <err.h>
#define O_BINARY 0
#else // _WIN32
#include <sys/time.h>
#include <stdbool.h>
void timeradd(const struct timeval *a, const struct timeval *b, struct timeval *result);
void err(int eval, const char *fmt, ...);
void errx(int eval, const char *fmt, ...);
void warn(const char *fmt, ...);
void warnx(const char *fmt, ...);
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More