12 Commits
v2.1 ... 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
24 changed files with 1351 additions and 1403 deletions

View File

@@ -1,32 +0,0 @@
name: Buildtest
on:
pull_request:
push:
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Dependencies (Ubuntu)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
- name: Dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew install libxml2
brew install libusb
- name: Build
run: make

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

4
.gitignore vendored
View File

@@ -1,6 +1,2 @@
*.o
qdl
qdl-ramdump
ks
compile_commands.json
.cache

View File

@@ -1,40 +1,33 @@
QDL := qdl
RAMDUMP := qdl-ramdump
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`
CFLAGS := -O2 -Wall -g `xml2-config --cflags` -fPIC
LDFLAGS := `xml2-config --libs` -ludev
prefix := /usr/local
QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c read.c ufs.c usb.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 usb.c util.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
KS_OBJS := $(KS_SRCS:.c=.o)
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:.}]" > $@
.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 $(OUT) $(QDL_OBJS) $(NBDKIT_OBJS)
install: $(QDL) $(RAMDUMP) $(KS_OUT)
install -d $(DESTDIR)$(prefix)/bin
install -m 755 $^ $(DESTDIR)$(prefix)/bin
install: $(OUT)
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<

33
README
View File

@@ -7,10 +7,37 @@ 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 and libusb-1.0 headers
and libraries, found in e.g. the libxml2-dev and libusb-1.0.0-dev packages
In order to build the project you need libxml2 headers and libraries, found in
e.g. the libxml2-dev package.
With these installed run:
With this installed run:
make
or to build the nbdkit plugin run:
make lib

File diff suppressed because it is too large Load Diff

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

117
ks.c
View File

@@ -1,117 +0,0 @@
#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 <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "qdl.h"
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[] = {
{"port", required_argument, 0, 'p'},
{"sahara", required_argument, 0, 's'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "p:s:", options, NULL )) != -1) {
switch (opt) {
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;
}
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

@@ -30,7 +30,6 @@
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

159
program.c
View File

@@ -31,103 +31,23 @@
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "program.h"
#include "qdl.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;
if (!is_nand) {
fprintf(stderr, "got \"erase\" tag for non-NAND storage\n");
return -EINVAL;
}
program = calloc(1, sizeof(struct program));
program->is_nand = true;
program->is_erase = true;
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
program->start_sector = attr_as_string(node, "start_sector", &errors);
if (errors) {
fprintf(stderr, "[PROGRAM] 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 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->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 (NULL != 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) {
fprintf(stderr, "[PROGRAM] errors while parsing program\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) {
@@ -140,27 +60,45 @@ 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 {
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\"\n", node->name);
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;
const char *filename;
@@ -169,7 +107,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
int fd;
for (program = programes; program; program = program->next) {
if (program->is_erase || !program->filename)
if (!program->filename)
continue;
filename = program->filename;
@@ -182,12 +120,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
fd = open(filename, O_RDONLY);
if (fd < 0) {
printf("Unable to open %s", program->filename);
if (!allow_missing) {
printf("...failing\n");
return -1;
}
printf("...ignoring\n");
printf("Unable to open %s...ignoring\n", program->filename);
continue;
}
@@ -201,24 +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;
}
/**
* program_find_bootable_partition() - find one bootable partition
*
@@ -236,8 +151,6 @@ int program_find_bootable_partition(void)
for (program = programes; program; program = program->next) {
label = program->label;
if (!label)
continue;
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
!strcmp(label, "sbl1")) {

View File

@@ -5,7 +5,6 @@
#include "qdl.h"
struct program {
unsigned pages_per_block;
unsigned sector_size;
unsigned file_offset;
const char *filename;
@@ -13,18 +12,13 @@ struct program {
unsigned num_sectors;
unsigned partition;
const char *start_sector;
unsigned last_sector;
bool is_nand;
bool is_erase;
struct program *next;
};
int program_load(const char *program_file, bool is_nand);
int program_load(const char *program_file);
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 erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
const char *incdir);
int program_find_bootable_partition(void);
#endif

79
qdl.c
View File

@@ -29,33 +29,43 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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"
#define MAX_USBFS_BULK_SIZE (16*1024)
#include "usb.h"
enum {
QDL_FILE_UNKNOWN,
QDL_FILE_PATCH,
QDL_FILE_PROGRAM,
QDL_FILE_READ,
QDL_FILE_UFS,
QDL_FILE_CONTENTS,
};
bool qdl_debug;
static struct qdl_device qdl;
static int detect_type(const char *xml_file)
{
@@ -81,10 +91,6 @@ static int detect_type(const char *xml_file)
type = QDL_FILE_PROGRAM;
break;
}
if (!xmlStrcmp(node->name, (xmlChar*)"read")) {
type = QDL_FILE_READ;
break;
}
if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) {
type = QDL_FILE_UFS;
break;
@@ -103,61 +109,44 @@ static void print_usage(void)
{
extern const char *__progname;
fprintf(stderr,
"%s [--debug] [--allow-missing] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] [--serial <NUM>] [--out-chunk-size <SIZE>] <prog.mbn> [<program> <patch> ...]\n",
"%s [--debug] [--storage <emmc|ufs>] [--finalize-provisioning] [--include <PATH>] <prog.mbn> [<program> <patch> ...]\n",
__progname);
}
enum {
OPT_OUT_CHUNK_SIZE = 1000,
};
int main(int argc, char **argv)
{
struct qdl_device *qdl;
char *prog_mbn, *storage="ufs";
char *incdir = NULL;
char *serial = NULL;
int bootable;
int type;
int ret;
int opt;
bool qdl_finalize_provisioning = false;
bool allow_missing = false;
long out_chunk_size;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"include", required_argument, 0, 'i'},
{"finalize-provisioning", no_argument, 0, 'l'},
{"out-chunk-size", required_argument, 0, OPT_OUT_CHUNK_SIZE },
{"serial", required_argument, 0, 'S'},
{"storage", required_argument, 0, 's'},
{"allow-missing", no_argument, 0, 'f'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "dfi:S:", options, NULL )) != -1) {
while ((opt = getopt_long(argc, argv, "di:", options, NULL )) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'f':
allow_missing = true;
break;
case 'i':
incdir = optarg;
break;
case 'l':
qdl_finalize_provisioning = true;
break;
case OPT_OUT_CHUNK_SIZE:
out_chunk_size = strtol(optarg, NULL, 10);
qdl_set_out_chunk_size(&qdl, out_chunk_size);
break;
case 's':
storage = optarg;
break;
case 'S':
serial = optarg;
break;
default:
print_usage();
return 1;
@@ -184,15 +173,10 @@ int main(int argc, char **argv)
errx(1, "patch_load %s failed", argv[optind]);
break;
case QDL_FILE_PROGRAM:
ret = program_load(argv[optind], !strcmp(storage, "nand"));
ret = program_load(argv[optind]);
if (ret < 0)
errx(1, "program_load %s failed", argv[optind]);
break;
case QDL_FILE_READ:
ret = read_op_load(argv[optind]);
if (ret < 0)
errx(1, "read_op_load %s failed", argv[optind]);
break;
case QDL_FILE_UFS:
ret = ufs_load(argv[optind],qdl_finalize_provisioning);
if (ret < 0)
@@ -204,18 +188,33 @@ int main(int argc, char **argv)
}
} while (++optind < argc);
ret = qdl_open(&qdl, serial);
qdl = usb_open();
if (ret)
return 1;
qdl.mappings[0] = prog_mbn;
ret = sahara_run(&qdl, qdl.mappings, true, NULL, NULL);
ret = sahara_run(qdl, prog_mbn);
if (ret < 0)
return 1;
ret = firehose_run(&qdl, incdir, storage, allow_missing);
ret = firehose_open(qdl, !strcmp(storage, "ufs"));
if (ret < 0)
return 1;
ret = program_execute(qdl, firehose_program, incdir);
if (ret)
return 1;
ret = patch_execute(qdl, firehose_apply_patch);
if (ret)
return 1;
bootable = program_find_bootable_partition();
if (bootable < 0)
fprintf(stderr, "no boot partition found\n");
else
firehose_set_bootable(qdl, bootable);
firehose_reset(qdl);
return 0;
}

43
qdl.h
View File

@@ -5,39 +5,30 @@
#include "patch.h"
#include "program.h"
#include "read.h"
#include <libxml/tree.h>
#define MAPPING_SZ 64
struct qdl_device;
struct program;
struct patch;
struct libusb_device_handle;
struct qdl_device {
struct libusb_device_handle *usb_handle;
int fd;
int in_ep;
int out_ep;
size_t in_maxpktsize;
size_t out_maxpktsize;
size_t out_chunk_size;
char *mappings[MAPPING_SZ]; // array index is the id from the device
};
int qdl_open(struct qdl_device *qdl, const char *serial);
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);
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size);
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_open(struct qdl_device *qdl, bool ufs);
int sahara_run(struct qdl_device *qdl, const char *prog_mbn);
void print_hex_dump(const char *prefix, const void *buf, size_t len);
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);
int firehose_getsize(struct qdl_device *qdl, int lun, size_t *sector_size,
size_t *num_sectors);
int firehose_reset(struct qdl_device *qdl);
int firehose_set_bootable(struct qdl_device *qdl, int part);
int firehose_program(struct qdl_device *qdl, struct program *program, int fd);
int firehose_apply_patch(struct qdl_device *qdl, struct patch *patch);
ssize_t firehose_pread(struct qdl_device *qdl, int lun, size_t offset, void *buf,
size_t sector_size, size_t num_sectors);
ssize_t firehose_pwrite(struct qdl_device *qdl, int lun, size_t offset,
const void *buf, size_t sector_size, size_t num_sectors);
extern bool qdl_debug;
#endif

View File

@@ -1,66 +0,0 @@
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "qdl.h"
bool qdl_debug;
static void print_usage(void)
{
extern const char *__progname;
fprintf(stderr,
"%s [--debug] [-o <ramdump-path>] [segment-filter,...]\n",
__progname);
exit(1);
}
int main(int argc, char **argv)
{
struct qdl_device qdl;
char *ramdump_path = ".";
char *filter = NULL;
char *serial = NULL;
int ret;
int opt;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"output", required_argument, 0, 'o'},
{"serial", required_argument, 0, 'S'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "do:S:", options, NULL )) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'o':
ramdump_path = optarg;
break;
case 'S':
serial = optarg;
break;
default:
print_usage();
}
}
if (optind < argc)
filter = argv[optind++];
if (optind != argc)
print_usage();
ret = qdl_open(&qdl, serial);
if (ret)
return 1;
ret = sahara_run(&qdl, NULL, true, ramdump_path, filter);
if (ret < 0)
return 1;
return 0;
}

131
read.c
View File

@@ -1,131 +0,0 @@
/*
* 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 <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "read.h"
#include "qdl.h"
static struct read_op *read_ops;
static struct read_op *read_ops_last;
int read_op_load(const char *read_op_file)
{
struct read_op *read_op;
xmlNode *node;
xmlNode *root;
xmlDoc *doc;
int errors;
doc = xmlReadFile(read_op_file, NULL, 0);
if (!doc) {
fprintf(stderr, "[READ] failed to parse %s\n", read_op_file);
return -EINVAL;
}
root = xmlDocGetRootElement(doc);
for (node = root->children; node ; node = node->next) {
if (node->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp(node->name, (xmlChar*)"read")) {
fprintf(stderr, "[READ] unrecognized tag \"%s\", ignoring\n", node->name);
continue;
}
errors = 0;
read_op = calloc(1, sizeof(struct read_op));
read_op->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
read_op->filename = attr_as_string(node, "filename", &errors);
read_op->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
read_op->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
read_op->start_sector = attr_as_string(node, "start_sector", &errors);
if (errors) {
fprintf(stderr, "[READ] errors while parsing read\n");
free(read_op);
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;
}
}
xmlFreeDoc(doc);
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)
{
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, 0644);
if (fd < 0) {
printf("Unable to open %s...\n", read_op->filename);
return ret;
}
ret = apply(qdl, read_op, fd);
close(fd);
if (ret)
return ret;
}
return 0;
}

22
read.h
View File

@@ -1,22 +0,0 @@
#ifndef __READ_H__
#define __READ_H__
#include <stdbool.h>
struct qdl_device;
struct read_op {
unsigned sector_size;
const char *filename;
unsigned partition;
unsigned num_sectors;
const char *start_sector;
struct read_op *next;
};
int read_op_load(const char *read_op_file);
int read_op_execute(struct qdl_device *qdl,
int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
const char *incdir);
#endif

330
sahara.c
View File

@@ -35,7 +35,6 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <inttypes.h>
#include <poll.h>
#include <stdbool.h>
@@ -46,49 +45,7 @@
#include <termios.h>
#include <unistd.h>
#include "qdl.h"
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define SAHARA_HELLO_CMD 0x1 /* Min protocol version 1.0 */
#define SAHARA_HELLO_RESP_CMD 0x2 /* Min protocol version 1.0 */
#define SAHARA_READ_DATA_CMD 0x3 /* Min protocol version 1.0 */
#define SAHARA_END_OF_IMAGE_CMD 0x4 /* Min protocol version 1.0 */
#define SAHARA_DONE_CMD 0x5 /* Min protocol version 1.0 */
#define SAHARA_DONE_RESP_CMD 0x6 /* Min protocol version 1.0 */
#define SAHARA_RESET_CMD 0x7 /* Min protocol version 1.0 */
#define SAHARA_RESET_RESP_CMD 0x8 /* Min protocol version 1.0 */
#define SAHARA_MEM_DEBUG_CMD 0x9 /* Min protocol version 2.0 */
#define SAHARA_MEM_READ_CMD 0xa /* Min protocol version 2.0 */
#define SAHARA_CMD_READY_CMD 0xb /* Min protocol version 2.1 */
#define SAHARA_SWITCH_MODE_CMD 0xc /* Min protocol version 2.1 */
#define SAHARA_EXECUTE_CMD 0xd /* Min protocol version 2.1 */
#define SAHARA_EXECUTE_RESP_CMD 0xe /* Min protocol version 2.1 */
#define SAHARA_EXECUTE_DATA_CMD 0xf /* Min protocol version 2.1 */
#define SAHARA_MEM_DEBUG64_CMD 0x10 /* Min protocol version 2.5 */
#define SAHARA_MEM_READ64_CMD 0x11 /* Min protocol version 2.5 */
#define SAHARA_READ_DATA64_CMD 0x12 /* Min protocol version 2.8 */
#define SAHARA_RESET_STATE_CMD 0x13 /* Min protocol version 2.9 */
#define SAHARA_WRITE_DATA_CMD 0x14 /* Min protocol version 3.0 */
#define SAHARA_VERSION 2
#define SAHARA_SUCCESS 0
#define SAHARA_MODE_IMAGE_TX_PENDING 0x0
#define SAHARA_MODE_IMAGE_TX_COMPLETE 0x1
#define SAHARA_MODE_MEMORY_DEBUG 0x2
#define SAHARA_MODE_COMMAND 0x3
#define SAHARA_HELLO_LENGTH 0x30
#define SAHARA_READ_DATA_LENGTH 0x14
#define SAHARA_READ_DATA64_LENGTH 0x20
#define SAHARA_END_OF_IMAGE_LENGTH 0x10
#define SAHARA_MEM_READ64_LENGTH 0x18
#define SAHARA_MEM_DEBUG64_LENGTH 0x18
#define SAHARA_DONE_LENGTH 0x8
#define SAHARA_DONE_RESP_LENGTH 0xc
#define SAHARA_RESET_LENGTH 0x8
#define DEBUG_BLOCK_SIZE (512*1024)
#include "usb.h"
struct sahara_pkt {
uint32_t cmd;
@@ -121,10 +78,6 @@ struct sahara_pkt {
struct {
uint32_t status;
} done_resp;
struct {
uint64_t addr;
uint64_t length;
} debug64_req;
struct {
uint64_t image;
uint64_t offset;
@@ -133,49 +86,36 @@ struct sahara_pkt {
};
};
struct sahara_debug_region64 {
uint64_t type;
uint64_t addr;
uint64_t length;
char region[20];
char filename[20];
};
static void sahara_send_reset(struct qdl_device *qdl)
{
struct sahara_pkt resp;
resp.cmd = SAHARA_RESET_CMD;
resp.length = SAHARA_RESET_LENGTH;
qdl_write(qdl, &resp, resp.length);
}
static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
{
struct sahara_pkt resp;
assert(pkt->length == SAHARA_HELLO_LENGTH);
assert(pkt->length == 0x30);
printf("HELLO version: 0x%x compatible: 0x%x max_len: %d mode: %d\n",
pkt->hello_req.version, pkt->hello_req.compatible, pkt->hello_req.max_len, pkt->hello_req.mode);
resp.cmd = SAHARA_HELLO_RESP_CMD;
resp.length = SAHARA_HELLO_LENGTH;
resp.hello_resp.version = SAHARA_VERSION;
resp.cmd = 2;
resp.length = 0x30;
resp.hello_resp.version = 2;
resp.hello_resp.compatible = 1;
resp.hello_resp.status = SAHARA_SUCCESS;
resp.hello_resp.status = 0;
resp.hello_resp.mode = pkt->hello_req.mode;
qdl_write(qdl, &resp, resp.length);
qdl_write(qdl, &resp, resp.length, true);
}
static int sahara_read_common(struct qdl_device *qdl, int progfd, off_t offset, size_t len)
static int sahara_read_common(struct qdl_device *qdl, const char *mbn, off_t offset, size_t len)
{
int progfd;
ssize_t n;
void *buf;
int ret = 0;
progfd = open(mbn, O_RDONLY);
if (progfd < 0)
return -errno;
buf = malloc(len);
if (!buf)
return -ENOMEM;
@@ -187,96 +127,50 @@ static int sahara_read_common(struct qdl_device *qdl, int progfd, off_t offset,
goto out;
}
n = qdl_write(qdl, buf, n);
n = qdl_write(qdl, buf, n, true);
if (n != len)
err(1, "failed to write %zu bytes to sahara", len);
free(buf);
close(progfd);
out:
return ret;
}
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
{
unsigned int image;
int ret;
int fd;
assert(pkt->length == SAHARA_READ_DATA_LENGTH);
assert(pkt->length == 0x14);
printf("READ image: %d offset: 0x%x length: 0x%x\n",
pkt->read_req.image, pkt->read_req.offset, pkt->read_req.length);
if (single_image)
image = 0;
else
image = pkt->read_req.image;
if (image >= MAPPING_SZ || !img_arr[image]) {
fprintf(stderr, "Device specified invalid image: %u\n", image);
sahara_send_reset(qdl);
return;
}
fd = open(img_arr[image], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "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);
return;
}
ret = sahara_read_common(qdl, fd, pkt->read_req.offset, pkt->read_req.length);
ret = sahara_read_common(qdl, mbn, pkt->read_req.offset, pkt->read_req.length);
if (ret < 0)
errx(1, "failed to read image chunk to sahara");
close(fd);
}
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 char *mbn)
{
unsigned int image;
int ret;
int fd;
assert(pkt->length == SAHARA_READ_DATA64_LENGTH);
assert(pkt->length == 0x20);
printf("READ64 image: %" PRId64 " offset: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
pkt->read64_req.image, pkt->read64_req.offset, pkt->read64_req.length);
if (single_image)
image = 0;
else
image = pkt->read64_req.image;
if (image >= MAPPING_SZ || !img_arr[image]) {
fprintf(stderr, "Device specified invalid image: %u\n", image);
sahara_send_reset(qdl);
return;
}
fd = open(img_arr[image], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "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);
return;
}
ret = sahara_read_common(qdl, fd, pkt->read64_req.offset, pkt->read64_req.length);
ret = sahara_read_common(qdl, mbn, pkt->read64_req.offset, pkt->read64_req.length);
if (ret < 0)
errx(1, "failed to read image chunk to sahara");
close(fd);
}
static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
{
struct sahara_pkt done;
assert(pkt->length == SAHARA_END_OF_IMAGE_LENGTH);
assert(pkt->length == 0x10);
printf("END OF IMAGE image: %d status: %d\n", pkt->eoi.image, pkt->eoi.status);
@@ -285,165 +179,28 @@ static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
return;
}
done.cmd = SAHARA_DONE_CMD;
done.length = SAHARA_DONE_LENGTH;
qdl_write(qdl, &done, done.length);
done.cmd = 5;
done.length = 0x8;
qdl_write(qdl, &done, done.length, true);
}
static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
{
assert(pkt->length == SAHARA_DONE_RESP_LENGTH);
assert(pkt->length == 0xc);
printf("DONE status: %d\n", pkt->done_resp.status);
// 0 == PENDING, 1 == COMPLETE. Device expects more images if
// PENDING is set in status.
return pkt->done_resp.status;
}
static ssize_t sahara_debug64_one(struct qdl_device *qdl,
struct sahara_debug_region64 region,
int ramdump_dir)
{
struct sahara_pkt read_req;
uint64_t remain;
size_t offset;
size_t chunk;
ssize_t n;
void *buf;
int fd;
buf = malloc(DEBUG_BLOCK_SIZE);
if (!buf)
return -1;
fd = openat(ramdump_dir, region.filename, O_WRONLY | O_CREAT, 0644);
if (fd < 0) {
warn("failed to open \"%s\"", region.filename);
return -1;
}
chunk = 0;
while (chunk < region.length) {
remain = MIN(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);
if (n < 0)
break;
offset = 0;
while (offset < remain) {
n = qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 30000);
if (n < 0) {
warn("failed to read ramdump chunk");
goto out;
}
write(fd, buf, n);
offset += n;
}
qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 10);
chunk += DEBUG_BLOCK_SIZE;
}
out:
close(fd);
free(buf);
return 0;
}
static bool sahara_debug64_filter(const char *filename, const char *filter)
{
bool anymatch = false;
char *ptr;
char *tmp;
char *s;
if (!filter)
return false;
tmp = strdup(filter);
for (s = strtok_r(tmp, ",", &ptr); s; s = strtok_r(NULL, ",", &ptr)) {
if (fnmatch(s, filename, 0) == 0) {
anymatch = true;
break;
}
}
free(tmp);
return !anymatch;
}
static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
int ramdump_dir, const char *filter)
{
struct sahara_debug_region64 *table;
struct sahara_pkt read_req;
ssize_t n;
int i;
assert(pkt->length == SAHARA_MEM_DEBUG64_LENGTH);
printf("DEBUG64 address: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
pkt->debug64_req.addr, pkt->debug64_req.length);
read_req.cmd = SAHARA_MEM_READ64_CMD;
read_req.length = SAHARA_MEM_READ64_LENGTH;
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);
if (n < 0)
return;
table = malloc(read_req.debug64_req.length);
n = qdl_read(qdl, table, pkt->debug64_req.length, 1000);
if (n < 0)
return;
for (i = 0; i < pkt->debug64_req.length / sizeof(table[0]); i++) {
if (sahara_debug64_filter(table[i].filename, filter))
continue;
printf("%-2d: type 0x%" PRIx64 " address: 0x%" PRIx64 " length: 0x%" PRIx64 " region: %s filename: %s\n",
i, table[i].type, table[i].addr, table[i].length, table[i].region, table[i].filename);
n = sahara_debug64_one(qdl, table[i], ramdump_dir);
if (n < 0)
break;
}
free(table);
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)
int sahara_run(struct qdl_device *qdl, const char *prog_mbn)
{
struct sahara_pkt *pkt;
int ramdump_dir = -1;
char buf[4096];
char tmp[32];
bool done = false;
int n;
if (ramdump_path) {
ramdump_dir = open(ramdump_path, O_DIRECTORY);
if (ramdump_dir < 0)
err(1, "failed to open directory for ramdump output");
}
while (!done) {
n = qdl_read(qdl, buf, sizeof(buf), 1000);
if (n < 0)
@@ -451,37 +208,26 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
pkt = (struct sahara_pkt*)buf;
if (n != pkt->length) {
fprintf(stderr, "length not matching\n");
fprintf(stderr, "length not matching");
return -EINVAL;
}
switch (pkt->cmd) {
case SAHARA_HELLO_CMD:
case 1:
sahara_hello(qdl, pkt);
break;
case SAHARA_READ_DATA_CMD:
sahara_read(qdl, pkt, img_arr, single_image);
case 3:
sahara_read(qdl, pkt, prog_mbn);
break;
case SAHARA_END_OF_IMAGE_CMD:
case 4:
sahara_eoi(qdl, pkt);
break;
case SAHARA_DONE_RESP_CMD:
done = sahara_done(qdl, pkt);
/* E.g MSM8916 EDL reports done = 0 here */
if (single_image)
done = true;
case 6:
sahara_done(qdl, pkt);
done = true;
break;
case SAHARA_MEM_DEBUG64_CMD:
sahara_debug64(qdl, pkt, ramdump_dir, ramdump_filter);
break;
case SAHARA_READ_DATA64_CMD:
sahara_read64(qdl, pkt, img_arr, single_image);
break;
case SAHARA_RESET_RESP_CMD:
assert(pkt->length == SAHARA_RESET_LENGTH);
if (ramdump_path)
done = true;
case 0x12:
sahara_read64(qdl, pkt, prog_mbn);
break;
default:
sprintf(tmp, "CMD%x", pkt->cmd);
@@ -490,7 +236,5 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
}
}
close(ramdump_dir);
return done ? 0 : -1;
}

7
ufs.c
View File

@@ -86,13 +86,6 @@ struct ufs_common *ufs_parse_common_params(xmlNode *node, bool finalize_provisio
return NULL;
}
/* These parameters are optional */
errors = 0;
result->bWriteBoosterBufferPreserveUserSpaceEn = !!attr_as_unsigned(node, "bWriteBoosterBufferPreserveUserSpaceEn", &errors);
result->bWriteBoosterBufferType = !!attr_as_unsigned(node, "bWriteBoosterBufferType", &errors);
result->shared_wb_buffer_size_in_kb = attr_as_unsigned(node, "shared_wb_buffer_size_in_kb", &errors);
result->wb = !errors;
return result;
}

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