mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6eeb866b15 | ||
|
|
b4030fabe6 | ||
|
|
a10cf7f5da | ||
|
|
e033f0f205 | ||
|
|
b9ad4ceaf8 | ||
|
|
516a75ff89 | ||
|
|
a88edc58ed | ||
|
|
bd656a50a0 | ||
|
|
a3f6c1a569 | ||
|
|
a3436e2b0a | ||
|
|
a3ec7fd90b | ||
|
|
2db10bd0c6 | ||
|
|
95e2a0d3a3 | ||
|
|
db8c0c8819 | ||
|
|
661ca1cba2 | ||
|
|
7206903f43 | ||
|
|
b1d46164e3 | ||
|
|
6b4a0adc3c | ||
|
|
80108974cf | ||
|
|
57b7b56ebe | ||
|
|
406ede8e14 | ||
|
|
4bdcaaec1a | ||
|
|
5b768d8be0 | ||
|
|
ff260604e7 | ||
|
|
0a3c1175bd | ||
|
|
d164eb0ab5 | ||
|
|
a79a572f18 | ||
|
|
18f7e98632 | ||
|
|
09a4f034b0 | ||
|
|
752685df9b | ||
|
|
2869ca68d8 | ||
|
|
8c42508fbd | ||
|
|
f1b6a30a1a | ||
|
|
57910d9696 | ||
|
|
3433cd77c3 | ||
|
|
5c5010c4fd | ||
|
|
e0d254190b | ||
|
|
dd7fb4f002 | ||
|
|
8c0fd741ee | ||
|
|
92b14a1c58 | ||
|
|
f3b4fd4fdc | ||
|
|
1b9974c198 | ||
|
|
33e6ec77ac | ||
|
|
841a0a8e7f | ||
|
|
5449037f1c | ||
|
|
2580547165 | ||
|
|
d561ed5bd2 | ||
|
|
08c4657e8d | ||
|
|
ccc1b6835b | ||
|
|
ce175f2e27 | ||
|
|
6a67b6bd64 | ||
|
|
3eb702d950 | ||
|
|
35b46a0e5e | ||
|
|
dad8b4802d | ||
|
|
60db50966d | ||
|
|
dff2ed7a72 | ||
|
|
10d16aabc8 |
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
@@ -18,11 +18,13 @@ jobs:
|
||||
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
|
||||
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev help2man
|
||||
|
||||
- name: Build
|
||||
run: make
|
||||
@@ -30,6 +32,9 @@ jobs:
|
||||
- name: Run tests
|
||||
run: make tests
|
||||
|
||||
- name: Generate man pages
|
||||
run: make manpages
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
mkdir dist
|
||||
@@ -51,20 +56,25 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- { sys: macos-14, arch: arm64 }
|
||||
- { sys: macos-13, arch: intel }
|
||||
- { 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
|
||||
brew install libxml2 help2man
|
||||
|
||||
- name: Build
|
||||
run: make
|
||||
|
||||
- name: Generate man pages
|
||||
run: make manpages
|
||||
|
||||
- name: Run tests
|
||||
run: make tests
|
||||
|
||||
@@ -109,6 +119,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup MSYS2
|
||||
id: msys2
|
||||
@@ -117,14 +129,23 @@ jobs:
|
||||
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: make
|
||||
run: |
|
||||
git config --global core.autocrlf true
|
||||
make
|
||||
shell: msys2 {0}
|
||||
|
||||
- name: Generate man pages
|
||||
run: make manpages
|
||||
shell: msys2 {0}
|
||||
|
||||
- name: Run tests
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
*.1
|
||||
*.o
|
||||
qdl
|
||||
qdl-ramdump
|
||||
|
||||
12
Makefile
12
Makefile
@@ -9,7 +9,7 @@ LDFLAGS += -lws2_32
|
||||
endif
|
||||
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_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)
|
||||
|
||||
RAMDUMP_SRCS := ramdump.c sahara.c io.c sim.c usb.c util.c ux.c oscompat.c
|
||||
@@ -19,13 +19,15 @@ 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_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)
|
||||
@@ -40,6 +42,11 @@ $(KS_OUT): $(KS_OBJS)
|
||||
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
|
||||
@@ -50,6 +57,7 @@ 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)
|
||||
|
||||
105
README.md
105
README.md
@@ -11,14 +11,23 @@ loader and use this to flash images.
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
sudo apt install libxml2 libusb-1.0-0-dev
|
||||
sudo apt install libxml2 libusb-1.0-0-dev help2man
|
||||
make
|
||||
```
|
||||
|
||||
### MacOS
|
||||
|
||||
For Homebrew users,
|
||||
|
||||
```bash
|
||||
brew install libxml2 pkg-config libusb
|
||||
brew install libxml2 pkg-config libusb help2man
|
||||
make
|
||||
```
|
||||
|
||||
For MacPorts users
|
||||
|
||||
```bash
|
||||
sudo port install libxml2 pkgconfig libusb help2man
|
||||
make
|
||||
```
|
||||
|
||||
@@ -30,6 +39,8 @@ 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
|
||||
@@ -72,6 +83,37 @@ the board to flash through `--serial` param:
|
||||
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
|
||||
@@ -120,6 +162,57 @@ table of digests are stored using `--vip-table-path` param:
|
||||
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:
|
||||
@@ -128,6 +221,14 @@ To run the integration test suite for QDL, use the `make tests` target:
|
||||
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
|
||||
|
||||
355
firehose.c
355
firehose.c
File diff suppressed because it is too large
Load Diff
282
gpt.c
Normal file
282
gpt.c
Normal file
@@ -0,0 +1,282 @@
|
||||
// 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 <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;
|
||||
unsigned int lba;
|
||||
char lba_buf[10];
|
||||
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;
|
||||
sprintf(lba_buf, "%u", 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
Normal file
10
gpt.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* 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
|
||||
24
io.c
24
io.c
@@ -38,12 +38,32 @@ 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);
|
||||
}
|
||||
|
||||
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len)
|
||||
/**
|
||||
* 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);
|
||||
return qdl->write(qdl, buf, len, timeout);
|
||||
}
|
||||
|
||||
37
ks.c
37
ks.c
@@ -24,24 +24,25 @@ static struct qdl_device qdl;
|
||||
|
||||
bool qdl_debug;
|
||||
|
||||
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
|
||||
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)
|
||||
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(void)
|
||||
static void print_usage(FILE *out)
|
||||
{
|
||||
extern const char *__progname;
|
||||
|
||||
fprintf(stderr,
|
||||
fprintf(out,
|
||||
"%s -p <sahara dev_node> -s <id:file path> ...\n",
|
||||
__progname);
|
||||
fprintf(stderr,
|
||||
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"
|
||||
@@ -53,6 +54,8 @@ static void print_usage(void)
|
||||
|
||||
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;
|
||||
@@ -62,13 +65,14 @@ int main(int argc, char **argv)
|
||||
|
||||
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:", options, NULL)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "dvp:s:h", options, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
qdl_debug = true;
|
||||
@@ -84,7 +88,7 @@ int main(int argc, char **argv)
|
||||
found_mapping = true;
|
||||
file_id = strtol(optarg, NULL, 10);
|
||||
if (file_id < 0) {
|
||||
print_usage();
|
||||
print_usage(stderr);
|
||||
return 1;
|
||||
}
|
||||
if (file_id >= MAPPING_SZ) {
|
||||
@@ -96,21 +100,28 @@ int main(int argc, char **argv)
|
||||
}
|
||||
colon = strchr(optarg, ':');
|
||||
if (!colon) {
|
||||
print_usage();
|
||||
print_usage(stderr);
|
||||
return 1;
|
||||
}
|
||||
qdl.mappings[file_id] = &optarg[colon - optarg + 1];
|
||||
printf("Created mapping ID:%ld File:%s\n", file_id, qdl.mappings[file_id]);
|
||||
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();
|
||||
print_usage(stderr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// -p and -s is required
|
||||
if (!dev_node || !found_mapping) {
|
||||
print_usage();
|
||||
print_usage(stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -123,7 +134,7 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = sahara_run(&qdl, qdl.mappings, false, NULL, NULL);
|
||||
ret = sahara_run(&qdl, mappings, false, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
|
||||
|
||||
73
list.h
Normal file
73
list.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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
|
||||
34
patch.c
34
patch.c
@@ -13,8 +13,8 @@
|
||||
#include "patch.h"
|
||||
#include "qdl.h"
|
||||
|
||||
static struct patch *patches;
|
||||
static struct patch *patches_last;
|
||||
static struct list_head patches = LIST_INIT(patches);
|
||||
static bool patches_loaded;
|
||||
|
||||
int patch_load(const char *patch_file)
|
||||
{
|
||||
@@ -59,17 +59,13 @@ int patch_load(const char *patch_file)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patches) {
|
||||
patches_last->next = patch;
|
||||
patches_last = patch;
|
||||
} else {
|
||||
patches = patch;
|
||||
patches_last = patch;
|
||||
}
|
||||
list_add(&patches, &patch->node);
|
||||
}
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
patches_loaded = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -80,12 +76,21 @@ int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
|
||||
unsigned int idx = 0;
|
||||
int ret;
|
||||
|
||||
for (patch = patches; patch; patch = patch->next) {
|
||||
if (!patches_loaded)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(patch, &patches, node) {
|
||||
if (!patch->filename)
|
||||
continue;
|
||||
|
||||
if (!strcmp(patch->filename, "DISK"))
|
||||
count++;
|
||||
}
|
||||
|
||||
for (patch = patches; patch; patch = patch->next) {
|
||||
list_for_each_entry(patch, &patches, node) {
|
||||
if (!patch->filename)
|
||||
continue;
|
||||
|
||||
if (strcmp(patch->filename, "DISK"))
|
||||
continue;
|
||||
|
||||
@@ -103,11 +108,10 @@ int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
|
||||
|
||||
void free_patches(void)
|
||||
{
|
||||
struct patch *patch = patches;
|
||||
struct patch *patch;
|
||||
struct patch *next;
|
||||
|
||||
for (patch = patches; patch; patch = next) {
|
||||
next = patch->next;
|
||||
list_for_each_entry_safe(patch, next, &patches, node) {
|
||||
free((void *)patch->filename);
|
||||
free((void *)patch->start_sector);
|
||||
free((void *)patch->value);
|
||||
@@ -115,5 +119,5 @@ void free_patches(void)
|
||||
free(patch);
|
||||
}
|
||||
|
||||
patches = NULL;
|
||||
list_init(&patches);
|
||||
}
|
||||
|
||||
4
patch.h
4
patch.h
@@ -2,6 +2,8 @@
|
||||
#ifndef __PATCH_H__
|
||||
#define __PATCH_H__
|
||||
|
||||
#include "list.h"
|
||||
|
||||
struct qdl_device;
|
||||
|
||||
struct patch {
|
||||
@@ -14,7 +16,7 @@ struct patch {
|
||||
const char *value;
|
||||
const char *what;
|
||||
|
||||
struct patch *next;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
int patch_load(const char *patch_file);
|
||||
|
||||
259
program.c
259
program.c
@@ -18,9 +18,9 @@
|
||||
#include "qdl.h"
|
||||
#include "oscompat.h"
|
||||
#include "sparse.h"
|
||||
#include "gpt.h"
|
||||
|
||||
static struct program *programes;
|
||||
static struct program *programes_last;
|
||||
static struct list_head programs = LIST_INIT(programs);
|
||||
|
||||
static int load_erase_tag(xmlNode *node, bool is_nand)
|
||||
{
|
||||
@@ -46,22 +46,14 @@ static int load_erase_tag(xmlNode *node, bool is_nand)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (programes) {
|
||||
programes_last->next = program;
|
||||
programes_last = program;
|
||||
} else {
|
||||
programes = program;
|
||||
programes_last = program;
|
||||
}
|
||||
list_add(&programs, &program->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct program *program_load_sparse(struct program *program, int fd)
|
||||
static int 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;
|
||||
@@ -77,20 +69,19 @@ static struct program *program_load_sparse(struct program *program, int fd)
|
||||
* 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.
|
||||
* was set by mistake, fix the sparse flag and add the
|
||||
* program entry to the list.
|
||||
*/
|
||||
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;
|
||||
program->sparse = false;
|
||||
list_add(&programs, &program->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ux_err("[PROGRAM] Unable to parse sparse header at %s...failed\n",
|
||||
program->filename);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < sparse_header.total_chunks; ++i) {
|
||||
@@ -101,7 +92,7 @@ static struct program *program_load_sparse(struct program *program, int fd)
|
||||
if (chunk_type < 0) {
|
||||
ux_err("[PROGRAM] Unable to parse sparse chunk %i at %s...failed\n",
|
||||
i, program->filename);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chunk_size == 0)
|
||||
@@ -110,7 +101,7 @@ static struct program *program_load_sparse(struct program *program, int fd)
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chunk_size / program->sector_size >= UINT_MAX) {
|
||||
@@ -120,51 +111,32 @@ static struct program *program_load_sparse(struct program *program, int fd)
|
||||
*/
|
||||
ux_err("[SPARSE] File chunk #%u size %" PRIu64 " is too large\n",
|
||||
i, chunk_size);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (chunk_type) {
|
||||
case CHUNK_TYPE_RAW:
|
||||
if (chunk_type == CHUNK_TYPE_RAW || chunk_type == CHUNK_TYPE_FILL) {
|
||||
program_sparse = calloc(1, sizeof(struct program));
|
||||
memcpy(program_sparse, program, sizeof(struct program));
|
||||
program_sparse->pages_per_block = program->pages_per_block;
|
||||
program_sparse->sector_size = program->sector_size;
|
||||
program_sparse->file_offset = program->file_offset;
|
||||
program_sparse->filename = strdup(program->filename);
|
||||
program_sparse->label = strdup(program->label);
|
||||
program_sparse->partition = program->partition;
|
||||
program_sparse->sparse = program->sparse;
|
||||
program_sparse->start_sector = strdup(program->start_sector);
|
||||
program_sparse->last_sector = program->last_sector;
|
||||
program_sparse->is_nand = program->is_nand;
|
||||
program_sparse->is_erase = false;
|
||||
|
||||
program_sparse->next = NULL;
|
||||
program_sparse->sparse_chunk_type = chunk_type;
|
||||
program_sparse->num_sectors = chunk_size / program->sector_size;
|
||||
|
||||
program_sparse->sparse_chunk_type = CHUNK_TYPE_RAW;
|
||||
program_sparse->sparse_offset = sparse_offset;
|
||||
if (chunk_type == CHUNK_TYPE_RAW)
|
||||
program_sparse->sparse_offset = sparse_offset;
|
||||
else
|
||||
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;
|
||||
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;
|
||||
list_add(&programs, &program_sparse->node);
|
||||
}
|
||||
|
||||
start_sector = (unsigned int)strtoul(program->start_sector, NULL, 0);
|
||||
@@ -173,13 +145,16 @@ static struct program *program_load_sparse(struct program *program, int fd)
|
||||
program->start_sector = strdup(tmp);
|
||||
}
|
||||
|
||||
return programes_sparse;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_program_tag(xmlNode *node, bool is_nand)
|
||||
static int load_program_tag(xmlNode *node, bool is_nand, bool allow_missing, const char *incdir)
|
||||
{
|
||||
struct program *program;
|
||||
char tmp[PATH_MAX];
|
||||
int errors = 0;
|
||||
int ret;
|
||||
int fd = -1;
|
||||
|
||||
program = calloc(1, sizeof(struct program));
|
||||
|
||||
@@ -208,18 +183,51 @@ static int load_program_tag(xmlNode *node, bool is_nand)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (programes) {
|
||||
programes_last->next = program;
|
||||
programes_last = program;
|
||||
} else {
|
||||
programes = program;
|
||||
programes_last = program;
|
||||
if (program->filename) {
|
||||
if (incdir) {
|
||||
snprintf(tmp, PATH_MAX, "%s/%s", incdir, program->filename);
|
||||
if (access(tmp, F_OK) != -1) {
|
||||
free((void *)program->filename);
|
||||
program->filename = strdup(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
fd = open(program->filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0) {
|
||||
ux_info("unable to open %s", program->filename);
|
||||
if (!allow_missing) {
|
||||
ux_info("...failing\n");
|
||||
return -1;
|
||||
}
|
||||
ux_info("...ignoring\n");
|
||||
|
||||
free((void *)program->filename);
|
||||
program->filename = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd >= 0 && program->sparse) {
|
||||
ret = program_load_sparse(program, fd);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Chunks were added to the program list, drop the filename of
|
||||
* the parent, to prevent this from being written to the device
|
||||
*/
|
||||
free((void *)program->filename);
|
||||
program->filename = NULL;
|
||||
}
|
||||
|
||||
list_add(&programs, &program->node);
|
||||
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int program_load(const char *program_file, bool is_nand)
|
||||
int program_load(const char *program_file, bool is_nand, bool allow_missing, const char *incdir)
|
||||
{
|
||||
xmlNode *node;
|
||||
xmlNode *root;
|
||||
@@ -240,7 +248,7 @@ int program_load(const char *program_file, bool is_nand)
|
||||
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);
|
||||
errors = load_program_tag(node, is_nand, allow_missing, incdir);
|
||||
else {
|
||||
ux_err("unrecognized tag \"%s\" in program-type file \"%s\"\n", node->name, program_file);
|
||||
errors = -EINVAL;
|
||||
@@ -256,55 +264,24 @@ out:
|
||||
return errors;
|
||||
}
|
||||
|
||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
||||
const char *incdir, bool allow_missing)
|
||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd))
|
||||
{
|
||||
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) {
|
||||
list_for_each_entry(program, &programs, node) {
|
||||
if (program->is_erase || !program->filename)
|
||||
continue;
|
||||
|
||||
filename = program->filename;
|
||||
if (incdir) {
|
||||
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
|
||||
if (access(tmp, F_OK) != -1)
|
||||
filename = tmp;
|
||||
}
|
||||
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
fd = open(program->filename, O_RDONLY | O_BINARY);
|
||||
|
||||
if (fd < 0) {
|
||||
ux_info("unable to open %s", program->filename);
|
||||
if (!allow_missing) {
|
||||
ux_info("...failing\n");
|
||||
return -1;
|
||||
}
|
||||
ux_info("...ignoring\n");
|
||||
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;
|
||||
}
|
||||
ux_err("unable to open %s\n", program->filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = apply(qdl, program, fd);
|
||||
close(fd);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -318,7 +295,7 @@ int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
|
||||
struct program *program;
|
||||
int ret;
|
||||
|
||||
for (program = programes; program; program = program->next) {
|
||||
list_for_each_entry(program, &programs, node) {
|
||||
if (!program->is_erase)
|
||||
continue;
|
||||
|
||||
@@ -335,7 +312,7 @@ static struct program *program_find_partition(const char *partition)
|
||||
struct program *program;
|
||||
const char *label;
|
||||
|
||||
for (program = programes; program; program = program->next) {
|
||||
list_for_each_entry(program, &programs, node) {
|
||||
label = program->label;
|
||||
if (!label)
|
||||
continue;
|
||||
@@ -409,16 +386,74 @@ int program_is_sec_partition_flashed(void)
|
||||
|
||||
void free_programs(void)
|
||||
{
|
||||
struct program *program = programes;
|
||||
struct program *program;
|
||||
struct program *next;
|
||||
|
||||
for (program = programes; program; program = next) {
|
||||
next = program->next;
|
||||
list_for_each_entry_safe(program, next, &programs, node) {
|
||||
free((void *)program->filename);
|
||||
free((void *)program->label);
|
||||
free((void *)program->start_sector);
|
||||
free((void *)program->gpt_partition);
|
||||
free(program);
|
||||
}
|
||||
|
||||
programes = NULL;
|
||||
list_init(&programs);
|
||||
}
|
||||
|
||||
int program_cmd_add(const char *address, const char *filename)
|
||||
{
|
||||
unsigned int start_sector;
|
||||
unsigned int num_sectors;
|
||||
struct program *program;
|
||||
char *gpt_partition;
|
||||
int partition;
|
||||
char buf[20];
|
||||
int ret;
|
||||
|
||||
ret = parse_storage_address(address, &partition, &start_sector, &num_sectors, &gpt_partition);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
program = calloc(1, sizeof(struct program));
|
||||
|
||||
program->sector_size = 0;
|
||||
program->file_offset = 0;
|
||||
program->filename = filename ? strdup(filename) : NULL;
|
||||
program->label = filename ? strdup(filename) : NULL;
|
||||
program->num_sectors = num_sectors;
|
||||
program->partition = partition;
|
||||
program->sparse = false;
|
||||
sprintf(buf, "%u", start_sector);
|
||||
program->start_sector = strdup(buf);
|
||||
program->last_sector = 0;
|
||||
program->is_nand = false;
|
||||
program->is_erase = false;
|
||||
program->gpt_partition = gpt_partition;
|
||||
|
||||
list_add(&programs, &program->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int program_resolve_gpt_deferrals(struct qdl_device *qdl)
|
||||
{
|
||||
struct program *program;
|
||||
unsigned int start_sector;
|
||||
char buf[20];
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(program, &programs, node) {
|
||||
if (!program->gpt_partition)
|
||||
continue;
|
||||
|
||||
ret = gpt_find_by_name(qdl, program->gpt_partition, &program->partition,
|
||||
&start_sector, &program->num_sectors);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
sprintf(buf, "%u", start_sector);
|
||||
program->start_sector = strdup(buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
17
program.h
17
program.h
@@ -5,7 +5,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "qdl.h"
|
||||
#include "list.h"
|
||||
|
||||
struct program {
|
||||
unsigned int pages_per_block;
|
||||
@@ -14,7 +14,7 @@ struct program {
|
||||
const char *filename;
|
||||
const char *label;
|
||||
unsigned int num_sectors;
|
||||
unsigned int partition;
|
||||
int partition;
|
||||
bool sparse;
|
||||
const char *start_sector;
|
||||
unsigned int last_sector;
|
||||
@@ -26,15 +26,20 @@ struct program {
|
||||
uint32_t sparse_fill_value;
|
||||
off_t sparse_offset;
|
||||
|
||||
struct program *next;
|
||||
const char *gpt_partition;
|
||||
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
int program_load(const char *program_file, bool is_nand);
|
||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
||||
const char *incdir, bool allow_missing);
|
||||
struct qdl_device;
|
||||
|
||||
int program_load(const char *program_file, bool is_nand, bool allow_missing, const char *incdir);
|
||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd));
|
||||
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
|
||||
int program_find_bootable_partition(bool *multiple_found);
|
||||
int program_is_sec_partition_flashed(void);
|
||||
int program_cmd_add(const char *address, const char *filename);
|
||||
int program_resolve_gpt_deferrals(struct qdl_device *qdl);
|
||||
|
||||
void free_programs(void);
|
||||
|
||||
|
||||
46
qdl.h
46
qdl.h
@@ -26,6 +26,14 @@
|
||||
(_x + _a - 1) & ~(_a - 1); \
|
||||
})
|
||||
|
||||
#define __unused __attribute__((__unused__))
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#define ALIGN_UP(p, size) ({ \
|
||||
__typeof__(size) _mask = (size) - 1; \
|
||||
(__typeof__(p))(((uintptr_t)(p) + _mask) & ~_mask); \
|
||||
})
|
||||
|
||||
#define MAPPING_SZ 64
|
||||
|
||||
enum QDL_DEVICE_TYPE {
|
||||
@@ -33,24 +41,40 @@ enum QDL_DEVICE_TYPE {
|
||||
QDL_DEVICE_SIM,
|
||||
};
|
||||
|
||||
enum qdl_storage_type {
|
||||
QDL_STORAGE_UNKNOWN,
|
||||
QDL_STORAGE_EMMC,
|
||||
QDL_STORAGE_NAND,
|
||||
QDL_STORAGE_UFS,
|
||||
QDL_STORAGE_NVME,
|
||||
QDL_STORAGE_SPINOR,
|
||||
};
|
||||
|
||||
struct qdl_device {
|
||||
enum QDL_DEVICE_TYPE dev_type;
|
||||
int fd;
|
||||
size_t max_payload_size;
|
||||
size_t sector_size;
|
||||
enum qdl_storage_type storage_type;
|
||||
unsigned int slot;
|
||||
|
||||
int (*open)(struct qdl_device *qdl, const char *serial);
|
||||
int (*read)(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
|
||||
int (*write)(struct qdl_device *qdl, const void *buf, size_t nbytes);
|
||||
int (*write)(struct qdl_device *qdl, const void *buf, size_t nbytes, unsigned int timeout);
|
||||
void (*close)(struct qdl_device *qdl);
|
||||
void (*set_out_chunk_size)(struct qdl_device *qdl, long size);
|
||||
void (*set_vip_transfer)(struct qdl_device *qdl, const char *signed_table,
|
||||
const char *chained_table);
|
||||
|
||||
char *mappings[MAPPING_SZ]; // array index is the id from the device
|
||||
|
||||
struct vip_transfer_data vip_data;
|
||||
};
|
||||
|
||||
struct sahara_image {
|
||||
const char *name;
|
||||
void *ptr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct libusb_device_handle;
|
||||
|
||||
struct qdl_device *qdl_init(enum QDL_DEVICE_TYPE type);
|
||||
@@ -58,16 +82,20 @@ void qdl_deinit(struct qdl_device *qdl);
|
||||
int qdl_open(struct qdl_device *qdl, const char *serial);
|
||||
void qdl_close(struct qdl_device *qdl);
|
||||
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
|
||||
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len);
|
||||
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, unsigned int timeout);
|
||||
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size);
|
||||
int qdl_vip_transfer_enable(struct qdl_device *qdl, const char *vip_table_path);
|
||||
|
||||
struct qdl_device *usb_init(void);
|
||||
struct qdl_device *sim_init(void);
|
||||
|
||||
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage, bool allow_missing);
|
||||
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
const char *ramdump_path, const char *ramdump_filter);
|
||||
int firehose_run(struct qdl_device *qdl);
|
||||
int firehose_provision(struct qdl_device *qdl);
|
||||
int firehose_read_buf(struct qdl_device *qdl, struct read_op *read_op, void *out_buf, size_t out_size);
|
||||
int sahara_run(struct qdl_device *qdl, const struct sahara_image *images,
|
||||
bool single_image, const char *ramdump_path,
|
||||
const char *ramdump_filter);
|
||||
int load_sahara_image(const char *filename, struct sahara_image *image);
|
||||
void print_hex_dump(const char *prefix, const void *buf, size_t len);
|
||||
unsigned int attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
|
||||
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);
|
||||
@@ -82,6 +110,10 @@ void ux_progress(const char *fmt, unsigned int value, unsigned int size, ...);
|
||||
|
||||
void print_version(void);
|
||||
|
||||
int parse_storage_address(const char *address, int *physical_partition,
|
||||
unsigned int *start_sector, unsigned int *num_sectors,
|
||||
char **gpt_partition);
|
||||
|
||||
extern bool qdl_debug;
|
||||
|
||||
#endif
|
||||
|
||||
20
ramdump.c
20
ramdump.c
@@ -12,14 +12,13 @@ const char *__progname = "ramdump";
|
||||
|
||||
bool qdl_debug;
|
||||
|
||||
static void print_usage(void)
|
||||
static void print_usage(FILE *out)
|
||||
{
|
||||
extern const char *__progname;
|
||||
|
||||
fprintf(stderr,
|
||||
fprintf(out,
|
||||
"%s [--debug] [-o <ramdump-path>] [segment-filter,...]\n",
|
||||
__progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -41,10 +40,11 @@ int main(int argc, char **argv)
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"output", required_argument, 0, 'o'},
|
||||
{"serial", required_argument, 0, 'S'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "dvo:S:", options, NULL)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "dvo:S:h", options, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
qdl_debug = true;
|
||||
@@ -59,16 +59,22 @@ int main(int argc, char **argv)
|
||||
case 'S':
|
||||
serial = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
print_usage(stdout);
|
||||
return 0;
|
||||
default:
|
||||
print_usage();
|
||||
print_usage(stderr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
filter = argv[optind++];
|
||||
|
||||
if (optind != argc)
|
||||
print_usage();
|
||||
if (optind != argc) {
|
||||
print_usage(stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (qdl_debug)
|
||||
print_version();
|
||||
|
||||
94
read.c
94
read.c
@@ -11,20 +11,22 @@
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "read.h"
|
||||
#include "qdl.h"
|
||||
#include "oscompat.h"
|
||||
#include "gpt.h"
|
||||
|
||||
static struct read_op *read_ops;
|
||||
static struct read_op *read_ops_last;
|
||||
static struct list_head read_ops = LIST_INIT(read_ops);
|
||||
|
||||
int read_op_load(const char *read_op_file)
|
||||
int read_op_load(const char *read_op_file, const char *incdir)
|
||||
{
|
||||
struct read_op *read_op;
|
||||
xmlNode *node;
|
||||
xmlNode *root;
|
||||
xmlDoc *doc;
|
||||
int errors;
|
||||
char tmp[PATH_MAX];
|
||||
|
||||
doc = xmlReadFile(read_op_file, NULL, 0);
|
||||
if (!doc) {
|
||||
@@ -59,13 +61,13 @@ int read_op_load(const char *read_op_file)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (read_ops) {
|
||||
read_ops_last->next = read_op;
|
||||
read_ops_last = read_op;
|
||||
} else {
|
||||
read_ops = read_op;
|
||||
read_ops_last = read_op;
|
||||
if (incdir) {
|
||||
snprintf(tmp, PATH_MAX, "%s/%s", incdir, read_op->filename);
|
||||
if (access(tmp, F_OK) != -1)
|
||||
read_op->filename = strdup(tmp);
|
||||
}
|
||||
|
||||
list_add(&read_ops, &read_op->node);
|
||||
}
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
@@ -73,25 +75,14 @@ int read_op_load(const char *read_op_file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
|
||||
const char *incdir)
|
||||
int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd))
|
||||
{
|
||||
struct read_op *read_op;
|
||||
const char *filename;
|
||||
char tmp[PATH_MAX];
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
for (read_op = read_ops; read_op; read_op = read_op->next) {
|
||||
filename = read_op->filename;
|
||||
if (incdir) {
|
||||
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
|
||||
if (access(tmp, F_OK) != -1)
|
||||
filename = tmp;
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
|
||||
list_for_each_entry(read_op, &read_ops, node) {
|
||||
fd = open(read_op->filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
|
||||
if (fd < 0) {
|
||||
ux_info("unable to open %s...\n", read_op->filename);
|
||||
return ret;
|
||||
@@ -106,3 +97,60 @@ int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_cmd_add(const char *address, const char *filename)
|
||||
{
|
||||
unsigned int start_sector;
|
||||
unsigned int num_sectors;
|
||||
struct read_op *read_op;
|
||||
char *gpt_partition;
|
||||
int partition;
|
||||
char buf[20];
|
||||
int ret;
|
||||
|
||||
ret = parse_storage_address(address, &partition, &start_sector, &num_sectors, &gpt_partition);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (num_sectors == 0 && !gpt_partition) {
|
||||
ux_err("read command without length specifier not supported\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
read_op = calloc(1, sizeof(struct read_op));
|
||||
|
||||
read_op->sector_size = 0;
|
||||
read_op->filename = strdup(filename);
|
||||
read_op->partition = partition;
|
||||
read_op->num_sectors = num_sectors;
|
||||
sprintf(buf, "%u", start_sector);
|
||||
read_op->start_sector = strdup(buf);
|
||||
read_op->gpt_partition = gpt_partition;
|
||||
|
||||
list_add(&read_ops, &read_op->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_resolve_gpt_deferrals(struct qdl_device *qdl)
|
||||
{
|
||||
struct read_op *read_op;
|
||||
unsigned int start_sector;
|
||||
char buf[20];
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(read_op, &read_ops, node) {
|
||||
if (!read_op->gpt_partition)
|
||||
continue;
|
||||
|
||||
ret = gpt_find_by_name(qdl, read_op->gpt_partition, &read_op->partition,
|
||||
&start_sector, &read_op->num_sectors);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
sprintf(buf, "%u", start_sector);
|
||||
read_op->start_sector = strdup(buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
15
read.h
15
read.h
@@ -4,20 +4,25 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
struct qdl_device;
|
||||
|
||||
struct read_op {
|
||||
unsigned int sector_size;
|
||||
const char *filename;
|
||||
unsigned int partition;
|
||||
int partition;
|
||||
unsigned int num_sectors;
|
||||
const char *start_sector;
|
||||
struct read_op *next;
|
||||
const char *gpt_partition;
|
||||
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
int read_op_load(const char *read_op_file);
|
||||
int read_op_load(const char *read_op_file, const char *incdir);
|
||||
int read_op_execute(struct qdl_device *qdl,
|
||||
int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
|
||||
const char *incdir);
|
||||
int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd));
|
||||
int read_cmd_add(const char *source, const char *filename);
|
||||
int read_resolve_gpt_deferrals(struct qdl_device *qdl);
|
||||
|
||||
#endif
|
||||
|
||||
163
sahara.c
163
sahara.c
@@ -58,7 +58,9 @@
|
||||
#define SAHARA_DONE_RESP_LENGTH 0xc
|
||||
#define SAHARA_RESET_LENGTH 0x8
|
||||
|
||||
#define DEBUG_BLOCK_SIZE (512 * 1024)
|
||||
#define DEBUG_BLOCK_SIZE (512u * 1024u)
|
||||
|
||||
#define SAHARA_CMD_TIMEOUT_MS 1000
|
||||
|
||||
struct sahara_pkt {
|
||||
uint32_t cmd;
|
||||
@@ -120,7 +122,7 @@ static void sahara_send_reset(struct qdl_device *qdl)
|
||||
resp.cmd = SAHARA_RESET_CMD;
|
||||
resp.length = SAHARA_RESET_LENGTH;
|
||||
|
||||
qdl_write(qdl, &resp, resp.length);
|
||||
qdl_write(qdl, &resp, resp.length, SAHARA_CMD_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
@@ -139,41 +141,18 @@ static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
resp.hello_resp.status = SAHARA_SUCCESS;
|
||||
resp.hello_resp.mode = pkt->hello_req.mode;
|
||||
|
||||
qdl_write(qdl, &resp, resp.length);
|
||||
qdl_write(qdl, &resp, resp.length, SAHARA_CMD_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static int sahara_read_common(struct qdl_device *qdl, int progfd, off_t offset, size_t len)
|
||||
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt,
|
||||
const struct sahara_image *images,
|
||||
bool single_image)
|
||||
{
|
||||
ssize_t n;
|
||||
void *buf;
|
||||
int ret = 0;
|
||||
|
||||
buf = malloc(len);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
lseek(progfd, offset, SEEK_SET);
|
||||
n = read(progfd, buf, len);
|
||||
if (n != len) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
n = qdl_write(qdl, buf, n);
|
||||
if (n != len)
|
||||
err(1, "failed to write %zu bytes to sahara", len);
|
||||
|
||||
free(buf);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
|
||||
{
|
||||
unsigned int image;
|
||||
const struct sahara_image *image;
|
||||
unsigned int image_idx;
|
||||
size_t offset;
|
||||
size_t len;
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
assert(pkt->length == SAHARA_READ_DATA_LENGTH);
|
||||
|
||||
@@ -181,37 +160,39 @@ static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, char *im
|
||||
pkt->read_req.image, pkt->read_req.offset, pkt->read_req.length);
|
||||
|
||||
if (single_image)
|
||||
image = 0;
|
||||
image_idx = 0;
|
||||
else
|
||||
image = pkt->read_req.image;
|
||||
image_idx = pkt->read_req.image;
|
||||
|
||||
if (image >= MAPPING_SZ || !img_arr[image]) {
|
||||
ux_err("device requested invalid image: %u\n", image);
|
||||
if (image_idx >= MAPPING_SZ || !images[image_idx].ptr) {
|
||||
ux_err("device requested invalid image: %u\n", image_idx);
|
||||
sahara_send_reset(qdl);
|
||||
return;
|
||||
}
|
||||
|
||||
fd = open(img_arr[image], O_RDONLY | O_BINARY);
|
||||
if (fd < 0) {
|
||||
ux_err("Can not open \"%s\": %s\n", img_arr[image], strerror(errno));
|
||||
// Maybe this read was optional. Notify device of error and let
|
||||
// it decide how to proceed.
|
||||
sahara_send_reset(qdl);
|
||||
offset = pkt->read_req.offset;
|
||||
len = pkt->read_req.length;
|
||||
|
||||
image = &images[image_idx];
|
||||
if (offset > image->len || offset + len > image->len) {
|
||||
ux_err("device requested invalid range of image %d\n", image_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = sahara_read_common(qdl, fd, pkt->read_req.offset, pkt->read_req.length);
|
||||
if (ret < 0)
|
||||
errx(1, "failed to read image chunk to sahara");
|
||||
|
||||
close(fd);
|
||||
ret = qdl_write(qdl, image->ptr + offset, len, SAHARA_CMD_TIMEOUT_MS);
|
||||
if (ret < 0 || ((size_t)ret != len))
|
||||
err(1, "failed to write %zu bytes to sahara", len);
|
||||
}
|
||||
|
||||
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
|
||||
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt,
|
||||
const struct sahara_image *images,
|
||||
bool single_image)
|
||||
{
|
||||
unsigned int image;
|
||||
const struct sahara_image *image;
|
||||
unsigned int image_idx;
|
||||
size_t offset;
|
||||
size_t len;
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
assert(pkt->length == SAHARA_READ_DATA64_LENGTH);
|
||||
|
||||
@@ -219,29 +200,28 @@ static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, char *
|
||||
pkt->read64_req.image, pkt->read64_req.offset, pkt->read64_req.length);
|
||||
|
||||
if (single_image)
|
||||
image = 0;
|
||||
image_idx = 0;
|
||||
else
|
||||
image = pkt->read64_req.image;
|
||||
image_idx = pkt->read64_req.image;
|
||||
|
||||
if (image >= MAPPING_SZ || !img_arr[image]) {
|
||||
ux_err("device requested invalid image: %u\n", image);
|
||||
sahara_send_reset(qdl);
|
||||
return;
|
||||
}
|
||||
fd = open(img_arr[image], O_RDONLY | O_BINARY);
|
||||
if (fd < 0) {
|
||||
ux_err("Can not open \"%s\": %s\n", img_arr[image], strerror(errno));
|
||||
// Maybe this read was optional. Notify device of error and let
|
||||
// it decide how to proceed.
|
||||
if (image_idx >= MAPPING_SZ || !images[image_idx].ptr) {
|
||||
ux_err("device requested invalid image: %u\n", image_idx);
|
||||
sahara_send_reset(qdl);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = sahara_read_common(qdl, fd, pkt->read64_req.offset, pkt->read64_req.length);
|
||||
if (ret < 0)
|
||||
errx(1, "failed to read image chunk to sahara");
|
||||
offset = pkt->read64_req.offset;
|
||||
len = pkt->read64_req.length;
|
||||
|
||||
close(fd);
|
||||
image = &images[image_idx];
|
||||
if (offset > image->len || offset + len > image->len) {
|
||||
ux_err("device requested invalid range of image %d\n", image_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = qdl_write(qdl, image->ptr + offset, len, SAHARA_CMD_TIMEOUT_MS);
|
||||
if (ret < 0 || ((size_t)ret != len))
|
||||
err(1, "failed to write %zu bytes to sahara", len);
|
||||
}
|
||||
|
||||
static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
@@ -259,10 +239,10 @@ static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
|
||||
done.cmd = SAHARA_DONE_CMD;
|
||||
done.length = SAHARA_DONE_LENGTH;
|
||||
qdl_write(qdl, &done, done.length);
|
||||
qdl_write(qdl, &done, done.length, SAHARA_CMD_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
static int sahara_done(struct qdl_device *qdl __unused, struct sahara_pkt *pkt)
|
||||
{
|
||||
assert(pkt->length == SAHARA_DONE_RESP_LENGTH);
|
||||
|
||||
@@ -303,13 +283,13 @@ static ssize_t sahara_debug64_one(struct qdl_device *qdl,
|
||||
|
||||
chunk = 0;
|
||||
while (chunk < region.length) {
|
||||
remain = MIN(region.length - chunk, DEBUG_BLOCK_SIZE);
|
||||
remain = MIN((uint64_t)(region.length - chunk), DEBUG_BLOCK_SIZE);
|
||||
|
||||
read_req.cmd = SAHARA_MEM_READ64_CMD;
|
||||
read_req.length = SAHARA_MEM_READ64_LENGTH;
|
||||
read_req.debug64_req.addr = region.addr + chunk;
|
||||
read_req.debug64_req.length = remain;
|
||||
n = qdl_write(qdl, &read_req, read_req.length);
|
||||
n = qdl_write(qdl, &read_req, read_req.length, SAHARA_CMD_TIMEOUT_MS);
|
||||
if (n < 0)
|
||||
break;
|
||||
|
||||
@@ -322,7 +302,7 @@ static ssize_t sahara_debug64_one(struct qdl_device *qdl,
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (buf_offset < n) {
|
||||
while (buf_offset < (size_t)n) {
|
||||
written = write(fd, buf + buf_offset, n - buf_offset);
|
||||
if (written <= 0) {
|
||||
warn("failed to write ramdump chunk to \"%s\"", region.filename);
|
||||
@@ -393,7 +373,7 @@ static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
|
||||
struct sahara_debug_region64 *table;
|
||||
struct sahara_pkt read_req;
|
||||
ssize_t n;
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
assert(pkt->length == SAHARA_MEM_DEBUG64_LENGTH);
|
||||
|
||||
@@ -405,13 +385,13 @@ static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
|
||||
read_req.debug64_req.addr = pkt->debug64_req.addr;
|
||||
read_req.debug64_req.length = pkt->debug64_req.length;
|
||||
|
||||
n = qdl_write(qdl, &read_req, read_req.length);
|
||||
n = qdl_write(qdl, &read_req, read_req.length, SAHARA_CMD_TIMEOUT_MS);
|
||||
if (n < 0)
|
||||
return;
|
||||
|
||||
table = malloc(read_req.debug64_req.length);
|
||||
|
||||
n = qdl_read(qdl, table, pkt->debug64_req.length, 1000);
|
||||
n = qdl_read(qdl, table, pkt->debug64_req.length, SAHARA_CMD_TIMEOUT_MS);
|
||||
if (n < 0)
|
||||
return;
|
||||
|
||||
@@ -434,8 +414,23 @@ static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
|
||||
sahara_send_reset(qdl);
|
||||
}
|
||||
|
||||
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
const char *ramdump_path, const char *ramdump_filter)
|
||||
static void sahara_debug_list_images(const struct sahara_image *images, bool single_image)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (single_image)
|
||||
ux_debug("Sahara image id in read requests will be ignored\n");
|
||||
|
||||
ux_debug("Sahara images:\n");
|
||||
for (i = 0; i < MAPPING_SZ; i++) {
|
||||
if (images[i].ptr)
|
||||
ux_debug(" %2d: %s\n", i, images[i].name ? : "(unknown)");
|
||||
}
|
||||
}
|
||||
|
||||
int sahara_run(struct qdl_device *qdl, const struct sahara_image *images,
|
||||
bool single_image, const char *ramdump_path,
|
||||
const char *ramdump_filter)
|
||||
{
|
||||
struct sahara_pkt *pkt;
|
||||
char buf[4096];
|
||||
@@ -443,6 +438,8 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
bool done = false;
|
||||
int n;
|
||||
|
||||
sahara_debug_list_images(images, single_image);
|
||||
|
||||
/*
|
||||
* Don't need to do anything in simulation mode with Sahara,
|
||||
* we care only about Firehose protocol
|
||||
@@ -451,12 +448,14 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
return 0;
|
||||
|
||||
while (!done) {
|
||||
n = qdl_read(qdl, buf, sizeof(buf), 1000);
|
||||
if (n < 0)
|
||||
n = qdl_read(qdl, buf, sizeof(buf), SAHARA_CMD_TIMEOUT_MS);
|
||||
if (n < 0) {
|
||||
ux_err("failed to read sahara request from device\n");
|
||||
break;
|
||||
}
|
||||
|
||||
pkt = (struct sahara_pkt *)buf;
|
||||
if (n != pkt->length) {
|
||||
if ((uint32_t)n != pkt->length) {
|
||||
ux_err("request length not matching received request\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -466,7 +465,7 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
sahara_hello(qdl, pkt);
|
||||
break;
|
||||
case SAHARA_READ_DATA_CMD:
|
||||
sahara_read(qdl, pkt, img_arr, single_image);
|
||||
sahara_read(qdl, pkt, images, single_image);
|
||||
break;
|
||||
case SAHARA_END_OF_IMAGE_CMD:
|
||||
sahara_eoi(qdl, pkt);
|
||||
@@ -482,7 +481,7 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
sahara_debug64(qdl, pkt, ramdump_path, ramdump_filter);
|
||||
break;
|
||||
case SAHARA_READ_DATA64_CMD:
|
||||
sahara_read64(qdl, pkt, img_arr, single_image);
|
||||
sahara_read64(qdl, pkt, images, single_image);
|
||||
break;
|
||||
case SAHARA_RESET_RESP_CMD:
|
||||
assert(pkt->length == SAHARA_RESET_LENGTH);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user