20 Commits

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
John Stultz
760b3dffb0 qdl: Rework qdl_write to limit write sizes to out_maxpktsize
On a number of machines, qdl could cause crashes on the host
system it ran on, due to swiotlb exaustion.

This seems to be due to 1M buffers being used during the writes.

In order to avoid this, rework qdl_write to break up the writes
into out_maxpktsize chunks.

With this patch, I no longer see host crashes when running qdl

Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Amit Pundir <amit.pundir@linaro.org>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: dragonboard-aosp@lists.96boards.org
Tested-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
Signed-off-by: John Stultz <john.stultz@linaro.org>
[bjorn: Dropped change of max_payload_size]
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-03 12:18:25 -07:00
Bjorn Andersson
7623ff5e1e firehose: Wait 3 seconds for payload to boot
The downstream tool has a 3 second delay to allow the firehose payload
to boot. Adding the same makes db820c successfully enter firehose mode.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-04-30 11:27:17 -07:00
Bjorn Andersson
fd48f7ce18 firehose: Lower the initial read timeout
Upon booting the firehose payload some platforms will issue log messages
others won't. Rather than waiting for 10 seconds on the ones that
doesn't wait only for a second.

Hopefully this is long enough, but the firmware synchronization and
handling of incoming log messages should be reviewed further.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 15:25:34 -08:00
Bjorn Andersson
90d3a6fee6 firehose: Remove unused firehose_nop()
We don't send any NOP requests, so remove this function for now.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 15:17:52 -08:00
Bjorn Andersson
1c958b2bc2 usb: Silence usb helpers
It's perfectly normal to "poll" for incoming messages, so silence the
warning printout in the usb accessor functions.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 15:08:02 -08:00
Bjorn Andersson
5ec4f210a0 firehose: Continue reading log entries after response
On db410c the target sends log entries after the response and refuse to
serve any subsequent requests until these are consumed, so continue
reading log entries until we get a timeout.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 15:06:32 -08:00
Nicolas Dechesne
1ac148c4e6 sahara_run: properly propagate error
We should return in case we haven't reached the end of the Sahara
init, which is when we set done to true. If done is not set to true
before returning from sahara_run() , it means something went wrong.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
2019-02-27 15:06:12 -08:00
Bjorn Andersson
8456cb2cf5 program: Correct a/b XBL match
A full build will contain both xbl_a and xbl_b, so matching on the
prefix of "xbl" will find two entries which we consider invalid.
Explicitly search for "xbl_a" instead.

Fixes: 5ea1e20c01 ("program: Match xbl in a/b scenarios")
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 09:10:12 -08:00
12 changed files with 1346 additions and 430 deletions

View File

@@ -1,17 +1,33 @@
OUT := qdl
NBDKIT := nbdkit-qdl-plugin.so
CFLAGS := -O2 -Wall -g `xml2-config --cflags`
CFLAGS := -O2 -Wall -g `xml2-config --cflags` -fPIC
LDFLAGS := `xml2-config --libs` -ludev
prefix := /usr/local
SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c
OBJS := $(SRCS:.c=.o)
COMMON_SRCS := firehose.c json.c sahara.c util.c patch.c program.c ufs.c usb.c
$(OUT): $(OBJS)
QDL_SRCS := qdl.c
QDL_OBJS := $(COMMON_SRCS:.c=.o) $(QDL_SRCS:.c=.o)
$(OUT): $(QDL_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
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)
.PHONY: lib
lib: $(NBDKIT)
.PHONY: all
all: $(OUT) $(NBDKIT)
clean:
rm -f $(OUT) $(OBJS)
rm -f $(OUT) $(QDL_OBJS) $(NBDKIT_OBJS)
install: $(OUT)
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<

27
README
View File

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

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

@@ -139,9 +139,9 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
*
* Returns partition number, or negative errno on failure.
*
* Scan program tags for a partition with the label "sbl1" or that starts with
* the string "xbl" and return the partition number for this. If more than one
* line matches we're assuming our logic is flawed and return an error.
* Scan program tags for a partition with the label "sbl1", "xbl" or "xbl_a"
* and return the partition number for this. If more than one line matches
* we're assuming our logic is flawed and return an error.
*/
int program_find_bootable_partition(void)
{
@@ -152,7 +152,8 @@ int program_find_bootable_partition(void)
for (program = programes; program; program = program->next) {
label = program->label;
if (!strncmp(label, "xbl", 3) || !strcmp(label, "sbl1")) {
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
!strcmp(label, "sbl1")) {
if (part != -ENOENT)
return -EINVAL;

308
qdl.c
View File

@@ -55,8 +55,7 @@
#include "qdl.h"
#include "patch.h"
#include "ufs.h"
#define MAX_USBFS_BULK_SIZE (16*1024)
#include "usb.h"
enum {
QDL_FILE_UNKNOWN,
@@ -66,16 +65,6 @@ enum {
QDL_FILE_CONTENTS,
};
struct qdl_device {
int fd;
int in_ep;
int out_ep;
size_t in_maxpktsize;
size_t out_maxpktsize;
};
bool qdl_debug;
static int detect_type(const char *xml_file)
@@ -116,276 +105,6 @@ static int detect_type(const char *xml_file)
return type;
}
static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf)
{
const struct usb_interface_descriptor *ifc;
const struct usb_endpoint_descriptor *ept;
const struct usb_device_descriptor *dev;
const struct usb_config_descriptor *cfg;
const struct usb_descriptor_header *hdr;
unsigned type;
unsigned out;
unsigned in;
unsigned k;
unsigned l;
ssize_t n;
size_t out_size;
size_t in_size;
void *ptr;
void *end;
char desc[1024];
n = read(fd, desc, sizeof(desc));
if (n < 0)
return n;
ptr = (void*)desc;
end = ptr + n;
dev = ptr;
/* Consider only devices with vid 0x0506 and product id 0x9008 */
if (dev->idVendor != 0x05c6 || dev->idProduct != 0x9008)
return -EINVAL;
ptr += dev->bLength;
if (ptr >= end || dev->bDescriptorType != USB_DT_DEVICE)
return -EINVAL;
cfg = ptr;
ptr += cfg->bLength;
if (ptr >= end || cfg->bDescriptorType != USB_DT_CONFIG)
return -EINVAL;
for (k = 0; k < cfg->bNumInterfaces; k++) {
if (ptr >= end)
return -EINVAL;
do {
ifc = ptr;
if (ifc->bLength < USB_DT_INTERFACE_SIZE)
return -EINVAL;
ptr += ifc->bLength;
} while (ptr < end && ifc->bDescriptorType != USB_DT_INTERFACE);
in = -1;
out = -1;
in_size = 0;
out_size = 0;
for (l = 0; l < ifc->bNumEndpoints; l++) {
if (ptr >= end)
return -EINVAL;
do {
ept = ptr;
if (ept->bLength < USB_DT_ENDPOINT_SIZE)
return -EINVAL;
ptr += ept->bLength;
} while (ptr < end && ept->bDescriptorType != USB_DT_ENDPOINT);
type = ept->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (type != USB_ENDPOINT_XFER_BULK)
continue;
if (ept->bEndpointAddress & USB_DIR_IN) {
in = ept->bEndpointAddress;
in_size = ept->wMaxPacketSize;
} else {
out = ept->bEndpointAddress;
out_size = ept->wMaxPacketSize;
}
if (ptr >= end)
break;
hdr = ptr;
if (hdr->bDescriptorType == USB_DT_SS_ENDPOINT_COMP)
ptr += USB_DT_SS_EP_COMP_SIZE;
}
if (ifc->bInterfaceClass != 0xff)
continue;
if (ifc->bInterfaceSubClass != 0xff)
continue;
/* bInterfaceProtocol of 0xff and 0x10 has been seen */
if (ifc->bInterfaceProtocol != 0xff &&
ifc->bInterfaceProtocol != 16)
continue;
qdl->fd = fd;
qdl->in_ep = in;
qdl->out_ep = out;
qdl->in_maxpktsize = in_size;
qdl->out_maxpktsize = out_size;
*intf = ifc->bInterfaceNumber;
return 0;
}
return -ENOENT;
}
static int usb_open(struct qdl_device *qdl)
{
struct udev_enumerate *enumerate;
struct udev_list_entry *devices;
struct udev_list_entry *dev_list_entry;
struct udev_monitor *mon;
struct udev_device *dev;
const char *dev_node;
struct udev *udev;
const char *path;
struct usbdevfs_ioctl cmd;
int mon_fd;
int intf = -1;
int ret;
int fd;
udev = udev_new();
if (!udev)
err(1, "failed to initialize udev");
mon = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", NULL);
udev_monitor_enable_receiving(mon);
mon_fd = udev_monitor_get_fd(mon);
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "usb");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
dev_node = udev_device_get_devnode(dev);
if (!dev_node)
continue;
fd = open(dev_node, O_RDWR);
if (fd < 0)
continue;
ret = parse_usb_desc(fd, qdl, &intf);
if (!ret)
goto found;
close(fd);
}
fprintf(stderr, "Waiting for EDL device\n");
for (;;) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(mon_fd, &rfds);
ret = select(mon_fd + 1, &rfds, NULL, NULL, NULL);
if (ret < 0)
return -1;
if (!FD_ISSET(mon_fd, &rfds))
continue;
dev = udev_monitor_receive_device(mon);
dev_node = udev_device_get_devnode(dev);
if (!dev_node)
continue;
printf("%s\n", dev_node);
fd = open(dev_node, O_RDWR);
if (fd < 0)
continue;
ret = parse_usb_desc(fd, qdl, &intf);
if (!ret)
goto found;
close(fd);
}
udev_enumerate_unref(enumerate);
udev_monitor_unref(mon);
udev_unref(udev);
return -ENOENT;
found:
udev_enumerate_unref(enumerate);
udev_monitor_unref(mon);
udev_unref(udev);
cmd.ifno = intf;
cmd.ioctl_code = USBDEVFS_DISCONNECT;
cmd.data = NULL;
ret = ioctl(qdl->fd, USBDEVFS_IOCTL, &cmd);
if (ret && errno != ENODATA)
err(1, "failed to disconnect kernel driver");
ret = ioctl(qdl->fd, USBDEVFS_CLAIMINTERFACE, &intf);
if (ret < 0)
err(1, "failed to claim USB interface");
return 0;
}
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
{
struct usbdevfs_bulktransfer bulk = {};
int n;
bulk.ep = qdl->in_ep;
bulk.len = len;
bulk.data = buf;
bulk.timeout = timeout;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if (n < 0)
warn("receive usb bulk transfer failed");
return n;
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, bool eot)
{
struct usbdevfs_bulktransfer bulk = {};
int ret;
int n;
bulk.ep = qdl->out_ep;
bulk.len = len;
bulk.data = (void *)buf;
bulk.timeout = 10000;
ret = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if (ret < 0)
warn("transmit usb bulk transfer failed");
if (eot && (len % qdl->out_maxpktsize) == 0) {
bulk.ep = qdl->out_ep;
bulk.len = 0;
bulk.data = NULL;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if (n < 0)
warn("transmit zlp failed");
}
return ret;
}
static void print_usage(void)
{
extern const char *__progname;
@@ -396,13 +115,14 @@ static void print_usage(void)
int main(int argc, char **argv)
{
struct qdl_device *qdl;
char *prog_mbn, *storage="ufs";
char *incdir = NULL;
int bootable;
int type;
int ret;
int opt;
bool qdl_finalize_provisioning = false;
struct qdl_device qdl;
static struct option options[] = {
@@ -468,17 +188,33 @@ int main(int argc, char **argv)
}
} while (++optind < argc);
ret = usb_open(&qdl);
qdl = usb_open();
if (ret)
return 1;
ret = sahara_run(&qdl, prog_mbn);
ret = sahara_run(qdl, prog_mbn);
if (ret < 0)
return 1;
ret = firehose_run(&qdl, incdir, storage);
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;
}

21
qdl.h
View File

@@ -8,16 +8,27 @@
#include <libxml/tree.h>
struct qdl_device;
struct program;
struct patch;
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, bool eot);
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage);
int sahara_run(struct qdl_device *qdl, char *prog_mbn);
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

@@ -45,6 +45,7 @@
#include <termios.h>
#include <unistd.h>
#include "qdl.h"
#include "usb.h"
struct sahara_pkt {
uint32_t cmd;
@@ -192,7 +193,7 @@ static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
return pkt->done_resp.status;
}
int sahara_run(struct qdl_device *qdl, char *prog_mbn)
int sahara_run(struct qdl_device *qdl, const char *prog_mbn)
{
struct sahara_pkt *pkt;
char buf[4096];
@@ -235,5 +236,5 @@ int sahara_run(struct qdl_device *qdl, char *prog_mbn)
}
}
return 0;
return done ? 0 : -1;
}

332
usb.c Normal file
View File

@@ -0,0 +1,332 @@
#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 "usb.h"
struct qdl_device {
int fd;
int in_ep;
int out_ep;
size_t in_maxpktsize;
size_t out_maxpktsize;
};
static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf)
{
const struct usb_interface_descriptor *ifc;
const struct usb_endpoint_descriptor *ept;
const struct usb_device_descriptor *dev;
const struct usb_config_descriptor *cfg;
const struct usb_descriptor_header *hdr;
unsigned type;
unsigned out;
unsigned in;
unsigned k;
unsigned l;
ssize_t n;
size_t out_size;
size_t in_size;
void *ptr;
void *end;
char desc[1024];
n = read(fd, desc, sizeof(desc));
if (n < 0)
return n;
ptr = (void*)desc;
end = ptr + n;
dev = ptr;
/* Consider only devices with vid 0x0506 and product id 0x9008 */
if (dev->idVendor != 0x05c6 || dev->idProduct != 0x9008)
return -EINVAL;
ptr += dev->bLength;
if (ptr >= end || dev->bDescriptorType != USB_DT_DEVICE)
return -EINVAL;
cfg = ptr;
ptr += cfg->bLength;
if (ptr >= end || cfg->bDescriptorType != USB_DT_CONFIG)
return -EINVAL;
for (k = 0; k < cfg->bNumInterfaces; k++) {
if (ptr >= end)
return -EINVAL;
do {
ifc = ptr;
if (ifc->bLength < USB_DT_INTERFACE_SIZE)
return -EINVAL;
ptr += ifc->bLength;
} while (ptr < end && ifc->bDescriptorType != USB_DT_INTERFACE);
in = -1;
out = -1;
in_size = 0;
out_size = 0;
for (l = 0; l < ifc->bNumEndpoints; l++) {
if (ptr >= end)
return -EINVAL;
do {
ept = ptr;
if (ept->bLength < USB_DT_ENDPOINT_SIZE)
return -EINVAL;
ptr += ept->bLength;
} while (ptr < end && ept->bDescriptorType != USB_DT_ENDPOINT);
type = ept->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (type != USB_ENDPOINT_XFER_BULK)
continue;
if (ept->bEndpointAddress & USB_DIR_IN) {
in = ept->bEndpointAddress;
in_size = ept->wMaxPacketSize;
} else {
out = ept->bEndpointAddress;
out_size = ept->wMaxPacketSize;
}
if (ptr >= end)
break;
hdr = ptr;
if (hdr->bDescriptorType == USB_DT_SS_ENDPOINT_COMP)
ptr += USB_DT_SS_EP_COMP_SIZE;
}
if (ifc->bInterfaceClass != 0xff)
continue;
if (ifc->bInterfaceSubClass != 0xff)
continue;
/* bInterfaceProtocol of 0xff and 0x10 has been seen */
if (ifc->bInterfaceProtocol != 0xff &&
ifc->bInterfaceProtocol != 16)
continue;
qdl->fd = fd;
qdl->in_ep = in;
qdl->out_ep = out;
qdl->in_maxpktsize = in_size;
qdl->out_maxpktsize = out_size;
*intf = ifc->bInterfaceNumber;
return 0;
}
return -ENOENT;
}
struct qdl_device *usb_open(void)
{
struct udev_enumerate *enumerate;
struct udev_list_entry *devices;
struct udev_list_entry *dev_list_entry;
struct udev_monitor *mon;
struct udev_device *dev;
struct qdl_device *qdl;
const char *dev_node;
struct udev *udev;
const char *path;
struct usbdevfs_ioctl cmd;
int mon_fd;
int intf = -1;
int ret;
int fd;
qdl = calloc(1, sizeof(*qdl));
udev = udev_new();
if (!udev)
err(1, "failed to initialize udev");
mon = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", NULL);
udev_monitor_enable_receiving(mon);
mon_fd = udev_monitor_get_fd(mon);
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "usb");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
dev_node = udev_device_get_devnode(dev);
if (!dev_node)
continue;
fd = open(dev_node, O_RDWR);
if (fd < 0)
continue;
ret = parse_usb_desc(fd, qdl, &intf);
if (!ret)
goto found;
close(fd);
}
fprintf(stderr, "Waiting for EDL device\n");
for (;;) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(mon_fd, &rfds);
ret = select(mon_fd + 1, &rfds, NULL, NULL, NULL);
if (ret < 0)
err(1, "failed to select");
if (!FD_ISSET(mon_fd, &rfds))
continue;
dev = udev_monitor_receive_device(mon);
dev_node = udev_device_get_devnode(dev);
if (!dev_node)
continue;
printf("%s\n", dev_node);
fd = open(dev_node, O_RDWR);
if (fd < 0)
continue;
ret = parse_usb_desc(fd, qdl, &intf);
if (!ret)
goto found;
close(fd);
}
udev_enumerate_unref(enumerate);
udev_monitor_unref(mon);
udev_unref(udev);
return NULL;
found:
udev_enumerate_unref(enumerate);
udev_monitor_unref(mon);
udev_unref(udev);
cmd.ifno = intf;
cmd.ioctl_code = USBDEVFS_DISCONNECT;
cmd.data = NULL;
ret = ioctl(qdl->fd, USBDEVFS_IOCTL, &cmd);
if (ret && errno != ENODATA)
err(1, "failed to disconnect kernel driver");
ret = ioctl(qdl->fd, USBDEVFS_CLAIMINTERFACE, &intf);
if (ret < 0)
err(1, "failed to claim USB interface");
return qdl;
}
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
{
struct usbdevfs_bulktransfer bulk = {};
bulk.ep = qdl->in_ep;
bulk.len = len;
bulk.data = buf;
bulk.timeout = timeout;
return ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, bool eot)
{
unsigned char *data = (unsigned char*) buf;
struct usbdevfs_bulktransfer bulk = {};
unsigned count = 0;
size_t len_orig = len;
int n;
if(len == 0) {
bulk.ep = qdl->out_ep;
bulk.len = 0;
bulk.data = data;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if(n != 0) {
fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
return 0;
}
while(len > 0) {
int xfer;
xfer = (len > qdl->out_maxpktsize) ? qdl->out_maxpktsize : len;
bulk.ep = qdl->out_ep;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if(n != xfer) {
fprintf(stderr, "ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
count += xfer;
len -= xfer;
data += xfer;
}
if (eot && (len_orig % qdl->out_maxpktsize) == 0) {
bulk.ep = qdl->out_ep;
bulk.len = 0;
bulk.data = NULL;
bulk.timeout = 1000;
n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk);
if (n < 0)
return n;
}
return count;
}

10
usb.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __QDL_USB_H__
#define __QDL_USB_H__
struct qdl_device;
struct qdl_device *usb_open(void);
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, bool eot);
#endif