12 Commits
v2.2 ... 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
48 changed files with 1825 additions and 4508 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,155 +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
- name: Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
- name: Build
run: make
- name: Run tests
run: make tests
- 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-13, arch: intel }
runs-on: ${{ matrix.sys }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Dependencies
run: |
brew install libxml2
- name: Build
run: make
- 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
- name: Setup MSYS2
id: msys2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: >
base-devel
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
- name: Build
run: make
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

9
.gitignore vendored
View File

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

View File

@@ -1,83 +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
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
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")
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
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:.}]" > $@
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 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

170
README.md
View File

@@ -1,170 +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
make
```
### MacOS
```bash
brew install libxml2 pkg-config libusb
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 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
```
### 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
```
## Run tests
To run the integration test suite for QDL, use the `make tests` target:
```bash
make tests
```
## 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.

File diff suppressed because it is too large Load Diff

49
io.c
View File

@@ -1,49 +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);
}
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
{
return qdl->read(qdl, buf, len, timeout);
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len)
{
return qdl->write(qdl, buf, len);
}

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

131
ks.c
View File

@@ -1,131 +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)
{
return read(qdl->fd, buf, len);
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len)
{
return write(qdl->fd, buf, len);
}
static void print_usage(void)
{
extern const char *__progname;
fprintf(stderr,
"%s -p <sahara dev_node> -s <id:file path> ...\n",
__progname);
fprintf(stderr,
" -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)
{
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'},
{"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:", 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();
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();
return 1;
}
qdl.mappings[file_id] = &optarg[colon - optarg + 1];
printf("Created mapping ID:%ld File:%s\n", file_id, qdl.mappings[file_id]);
break;
default:
print_usage();
return 1;
}
}
// -p and -s is required
if (!dev_node || !found_mapping) {
print_usage();
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, qdl.mappings, false, NULL, NULL);
if (ret < 0)
return 1;
return 0;
}

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

69
patch.c
View File

@@ -1,18 +1,41 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2016-2017, 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 <errno.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <unistd.h>
#include "patch.h"
#include "qdl.h"
static struct patch *patches;
static struct patch *patches_last;
@@ -26,7 +49,7 @@ int patch_load(const char *patch_file)
doc = xmlReadFile(patch_file, NULL, 0);
if (!doc) {
ux_err("failed to parse patch-type file \"%s\"\n", patch_file);
fprintf(stderr, "[PATCH] failed to parse %s\n", patch_file);
return -EINVAL;
}
@@ -35,8 +58,8 @@ int patch_load(const char *patch_file)
if (node->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp(node->name, (xmlChar *)"patch")) {
ux_err("unrecognized tag \"%s\" in patch-type file, ignoring\n", node->name);
if (xmlStrcmp(node->name, (xmlChar*)"patch")) {
fprintf(stderr, "[PATCH] unrecognized tag \"%s\", ignoring\n", node->name);
continue;
}
@@ -54,7 +77,7 @@ int patch_load(const char *patch_file)
patch->what = attr_as_string(node, "what", &errors);
if (errors) {
ux_err("errors while parsing patch-type file \"%s\"\n", patch_file);
fprintf(stderr, "[PATCH] errors while parsing patch\n");
free(patch);
continue;
}
@@ -72,19 +95,12 @@ int patch_load(const char *patch_file)
return 0;
}
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch))
{
struct patch *patch;
unsigned int count = 0;
unsigned int idx = 0;
int ret;
for (patch = patches; patch; patch = patch->next) {
if (!strcmp(patch->filename, "DISK"))
count++;
}
for (patch = patches; patch; patch = patch->next) {
if (strcmp(patch->filename, "DISK"))
continue;
@@ -92,28 +108,7 @@ int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
ret = apply(qdl, patch);
if (ret)
return ret;
ux_progress("Applying patches", idx++, count);
}
ux_info("%d patches applied\n", idx);
return 0;
}
void free_patches(void)
{
struct patch *patch = patches;
struct patch *next;
for (patch = patches; patch; patch = next) {
next = patch->next;
free((void *)patch->filename);
free((void *)patch->start_sector);
free((void *)patch->value);
free((void *)patch->what);
free(patch);
}
patches = NULL;
}

10
patch.h
View File

@@ -1,15 +1,14 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __PATCH_H__
#define __PATCH_H__
struct qdl_device;
struct patch {
unsigned int sector_size;
unsigned int byte_offset;
unsigned sector_size;
unsigned byte_offset;
const char *filename;
unsigned int partition;
unsigned int size_in_bytes;
unsigned partition;
unsigned size_in_bytes;
const char *start_sector;
const char *value;
const char *what;
@@ -19,6 +18,5 @@ struct patch {
int patch_load(const char *patch_file);
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch));
void free_patches(void);
#endif

409
program.c
View File

@@ -1,234 +1,57 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2016-2017, 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.
*/
#define _FILE_OFFSET_BITS 64
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "program.h"
#include "qdl.h"
#include "oscompat.h"
#include "sparse.h"
static struct program *programes;
static struct program *programes_last;
static int load_erase_tag(xmlNode *node, bool is_nand)
int program_load(const char *program_file)
{
struct program *program;
int errors = 0;
program = calloc(1, sizeof(struct program));
program->is_nand = is_nand;
program->is_erase = true;
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
program->start_sector = attr_as_string(node, "start_sector", &errors);
if (is_nand) {
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
}
if (errors) {
ux_err("errors while parsing erase tag\n");
free(program);
return -EINVAL;
}
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
}
return 0;
}
static struct program *program_load_sparse(struct program *program, int fd)
{
struct program *program_sparse = NULL;
struct program *programes_sparse = NULL;
struct program *programes_sparse_last = NULL;
char tmp[PATH_MAX];
sparse_header_t sparse_header;
unsigned int start_sector;
uint32_t sparse_fill_value;
uint64_t chunk_size;
off_t sparse_offset;
int chunk_type;
if (sparse_header_parse(fd, &sparse_header)) {
/*
* If the XML tag "program" contains the attribute 'sparse="true"'
* for a partition node but lacks a sparse header,
* it will be validated against the defined partition size.
* If the sizes match, it is likely that the 'sparse="true"' attribute
* was set by mistake.
*/
if ((off_t)program->sector_size * program->num_sectors ==
lseek(fd, 0, SEEK_END)) {
program_sparse = calloc(1, sizeof(struct program));
memcpy(program_sparse, program, sizeof(struct program));
program_sparse->sparse = false;
program_sparse->next = NULL;
return program_sparse;
}
ux_err("[PROGRAM] Unable to parse sparse header at %s...failed\n",
program->filename);
return NULL;
}
for (uint32_t i = 0; i < sparse_header.total_chunks; ++i) {
chunk_type = sparse_chunk_header_parse(fd, &sparse_header,
&chunk_size,
&sparse_fill_value,
&sparse_offset);
if (chunk_type < 0) {
ux_err("[PROGRAM] Unable to parse sparse chunk %i at %s...failed\n",
i, program->filename);
return NULL;
}
if (chunk_size == 0)
continue;
if (chunk_size % program->sector_size != 0) {
ux_err("[SPARSE] File chunk #%u size %" PRIu64 " is not a sector-multiple\n",
i, chunk_size);
return NULL;
}
if (chunk_size / program->sector_size >= UINT_MAX) {
/*
* Perhaps the programmer can handle larger "num_sectors"?
* Let's cap it for now, it's big enough for now...
*/
ux_err("[SPARSE] File chunk #%u size %" PRIu64 " is too large\n",
i, chunk_size);
return NULL;
}
switch (chunk_type) {
case CHUNK_TYPE_RAW:
program_sparse = calloc(1, sizeof(struct program));
memcpy(program_sparse, program, sizeof(struct program));
program_sparse->next = NULL;
program_sparse->num_sectors = chunk_size / program->sector_size;
program_sparse->sparse_chunk_type = CHUNK_TYPE_RAW;
program_sparse->sparse_offset = sparse_offset;
if (programes_sparse) {
programes_sparse_last->next = program_sparse;
programes_sparse_last = program_sparse;
} else {
programes_sparse = program_sparse;
programes_sparse_last = program_sparse;
}
break;
case CHUNK_TYPE_FILL:
program_sparse = calloc(1, sizeof(struct program));
memcpy(program_sparse, program, sizeof(struct program));
program_sparse->next = NULL;
program_sparse->num_sectors = chunk_size / program->sector_size;
program_sparse->sparse_chunk_type = CHUNK_TYPE_FILL;
program_sparse->sparse_fill_value = sparse_fill_value;
if (programes_sparse) {
programes_sparse_last->next = program_sparse;
programes_sparse_last = program_sparse;
} else {
programes_sparse = program_sparse;
programes_sparse_last = program_sparse;
}
break;
default:
break;
}
start_sector = (unsigned int)strtoul(program->start_sector, NULL, 0);
start_sector += chunk_size / program->sector_size;
sprintf(tmp, "%u", start_sector);
program->start_sector = strdup(tmp);
}
return programes_sparse;
}
static int load_program_tag(xmlNode *node, bool is_nand)
{
struct program *program;
int errors = 0;
program = calloc(1, sizeof(struct program));
program->is_nand = is_nand;
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
program->filename = attr_as_string(node, "filename", &errors);
program->label = attr_as_string(node, "label", &errors);
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
program->sparse = attr_as_bool(node, "sparse", &errors);
program->start_sector = attr_as_string(node, "start_sector", &errors);
if (is_nand) {
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
if (xmlGetProp(node, (xmlChar *)"last_sector")) {
program->last_sector = attr_as_unsigned(node, "last_sector", &errors);
}
} else {
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
}
if (errors) {
ux_err("errors while parsing program tag\n");
free(program);
return -EINVAL;
}
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
}
return 0;
}
int program_load(const char *program_file, bool is_nand)
{
xmlNode *node;
xmlNode *root;
xmlDoc *doc;
int errors = 0;
int errors;
doc = xmlReadFile(program_file, NULL, 0);
if (!doc) {
ux_err("failed to parse program-type file \"%s\"\n", program_file);
fprintf(stderr, "[PROGRAM] failed to parse %s\n", program_file);
return -EINVAL;
}
@@ -237,37 +60,54 @@ int program_load(const char *program_file, bool is_nand)
if (node->type != XML_ELEMENT_NODE)
continue;
if (!xmlStrcmp(node->name, (xmlChar *)"erase"))
errors = load_erase_tag(node, is_nand);
else if (!xmlStrcmp(node->name, (xmlChar *)"program"))
errors = load_program_tag(node, is_nand);
else {
ux_err("unrecognized tag \"%s\" in program-type file \"%s\"\n", node->name, program_file);
errors = -EINVAL;
if (xmlStrcmp(node->name, (xmlChar*)"program")) {
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\", ignoring\n", node->name);
continue;
}
if (errors)
goto out;
errors = 0;
program = calloc(1, sizeof(struct program));
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
program->filename = attr_as_string(node, "filename", &errors);
program->label = attr_as_string(node, "label", &errors);
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
program->start_sector = attr_as_string(node, "start_sector", &errors);
if (errors) {
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
free(program);
continue;
}
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
}
}
out:
xmlFreeDoc(doc);
return errors;
return 0;
}
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
const char *incdir, bool allow_missing)
const char *incdir)
{
struct program *program;
struct program *program_sparse;
const char *filename;
char tmp[PATH_MAX];
int ret;
int fd;
for (program = programes; program; program = program->next) {
if (program->is_erase || !program->filename)
if (!program->filename)
continue;
filename = program->filename;
@@ -277,33 +117,14 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
filename = tmp;
}
fd = open(filename, O_RDONLY | O_BINARY);
fd = open(filename, O_RDONLY);
if (fd < 0) {
ux_info("unable to open %s", program->filename);
if (!allow_missing) {
ux_info("...failing\n");
return -1;
}
ux_info("...ignoring\n");
printf("Unable to open %s...ignoring\n", program->filename);
continue;
}
if (!program->sparse) {
ret = apply(qdl, program, fd);
} else {
program_sparse = program_load_sparse(program, fd);
if (!program_sparse) {
ux_err("[PROGRAM] load sparse failed\n");
return -EINVAL;
}
for (; program_sparse; program_sparse = program_sparse->next) {
ret = apply(qdl, program_sparse, fd);
if (ret)
break;
}
}
ret = apply(qdl, program, fd);
close(fd);
if (ret)
@@ -313,40 +134,6 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
return 0;
}
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program))
{
struct program *program;
int ret;
for (program = programes; program; program = program->next) {
if (!program->is_erase)
continue;
ret = apply(qdl, program);
if (ret)
return ret;
}
return 0;
}
static struct program *program_find_partition(const char *partition)
{
struct program *program;
const char *label;
for (program = programes; program; program = program->next) {
label = program->label;
if (!label)
continue;
if (!strcmp(label, partition))
return program;
}
return NULL;
}
/**
* program_find_bootable_partition() - find one bootable partition
*
@@ -354,71 +141,25 @@ static struct program *program_find_partition(const char *partition)
*
* Scan program tags for a partition with the label "sbl1", "xbl" or "xbl_a"
* and return the partition number for this. If more than one line matches
* we're informing the caller so that they can warn the user about the
* uncertainty of this logic.
* we're assuming our logic is flawed and return an error.
*/
int program_find_bootable_partition(bool *multiple_found)
int program_find_bootable_partition(void)
{
struct program *program;
const char *label;
int part = -ENOENT;
*multiple_found = false;
for (program = programes; program; program = program->next) {
label = program->label;
program = program_find_partition("xbl");
if (program)
part = program->partition;
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
!strcmp(label, "sbl1")) {
if (part != -ENOENT)
return -EINVAL;
program = program_find_partition("xbl_a");
if (program) {
if (part != -ENOENT)
*multiple_found = true;
else
part = program->partition;
}
program = program_find_partition("sbl1");
if (program) {
if (part != -ENOENT)
*multiple_found = true;
else
part = program->partition;
}
}
return part;
}
/**
* program_is_sec_partition_flashed() - find if secdata partition is flashed
*
* Returns true if filename for secdata is set in program*.xml,
* or false otherwise.
*/
int program_is_sec_partition_flashed(void)
{
struct program *program;
program = program_find_partition("secdata");
if (!program)
return false;
if (program->filename)
return true;
return false;
}
void free_programs(void)
{
struct program *program = programes;
struct program *next;
for (program = programes; program; program = next) {
next = program->next;
free((void *)program->filename);
free((void *)program->label);
free((void *)program->start_sector);
free(program);
}
programes = NULL;
}

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