mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6200a2068 | ||
|
|
44d148a63b | ||
|
|
c37088f3fd | ||
|
|
e3d3811748 | ||
|
|
9aae1e9ff2 | ||
|
|
bff56ea21b | ||
|
|
5b4874667c | ||
|
|
e1a3670d91 | ||
|
|
bb1aa99068 | ||
|
|
342e99b9be | ||
|
|
066022e6d8 | ||
|
|
2d239db0ca | ||
|
|
760b3dffb0 | ||
|
|
7623ff5e1e | ||
|
|
fd48f7ce18 | ||
|
|
90d3a6fee6 | ||
|
|
1c958b2bc2 | ||
|
|
5ec4f210a0 | ||
|
|
1ac148c4e6 | ||
|
|
8456cb2cf5 | ||
|
|
8589513f65 | ||
|
|
ba86b03391 | ||
|
|
59717efcd4 | ||
|
|
5ea1e20c01 | ||
|
|
826a5cd4fd | ||
|
|
8f7987f756 | ||
|
|
b338928519 | ||
|
|
dc61f8f79e | ||
|
|
cfce0beeab | ||
|
|
a50ec8047c | ||
|
|
5fc4cdbba4 | ||
|
|
37edf318b2 | ||
|
|
d9935e1f54 | ||
|
|
d77b106a57 | ||
|
|
45cc3521d8 | ||
|
|
5d51472f1c | ||
|
|
df842101b1 | ||
|
|
44a80b1266 | ||
|
|
d2e791a950 | ||
|
|
b4c2e8f267 | ||
|
|
7be48f4fc9 | ||
|
|
19e8a2d4ba | ||
|
|
1fc3c04d17 | ||
|
|
0251833b4d | ||
|
|
febbbcc32e | ||
|
|
922699c4db |
4
.gitreview
Normal file
4
.gitreview
Normal file
@@ -0,0 +1,4 @@
|
||||
[gerrit]
|
||||
host=review.linaro.org
|
||||
port=29418
|
||||
project=landing-teams/working/qualcomm/qdl
|
||||
28
Makefile
28
Makefile
@@ -1,17 +1,33 @@
|
||||
OUT := qdl
|
||||
NBDKIT := nbdkit-qdl-plugin.so
|
||||
|
||||
CFLAGS := -O2 -Wall -g `xml2-config --cflags`
|
||||
LDFLAGS := `xml2-config --libs`
|
||||
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
|
||||
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
27
README
@@ -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
|
||||
|
||||
582
firehose.c
582
firehose.c
File diff suppressed because it is too large
Load Diff
451
json.c
Normal file
451
json.c
Normal 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
67
json.h
Normal 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
146
nbdkit-qdl-plugin.c
Normal 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, §or_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)
|
||||
27
patch.c
27
patch.c
@@ -34,32 +34,11 @@
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "patch.h"
|
||||
#include "qdl.h"
|
||||
|
||||
static struct patch *patches;
|
||||
static struct patch *patches_last;
|
||||
|
||||
static unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors)
|
||||
{
|
||||
xmlChar *value;
|
||||
|
||||
value = xmlGetProp(node, (xmlChar*)attr);
|
||||
if (!value)
|
||||
(*errors)++;
|
||||
|
||||
return strtoul((char*)value, NULL, 10);
|
||||
}
|
||||
|
||||
static const char *attr_as_string(xmlNode *node, const char *attr, int *errors)
|
||||
{
|
||||
xmlChar *value;
|
||||
|
||||
value = xmlGetProp(node, (xmlChar*)attr);
|
||||
if (!value)
|
||||
(*errors)++;
|
||||
|
||||
return strdup((char*)value);
|
||||
}
|
||||
|
||||
int patch_load(const char *patch_file)
|
||||
{
|
||||
struct patch *patch;
|
||||
@@ -117,7 +96,7 @@ int patch_load(const char *patch_file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int patch_execute(int fd, int (*apply)(int fd, struct patch *patch))
|
||||
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch))
|
||||
{
|
||||
struct patch *patch;
|
||||
int ret;
|
||||
@@ -126,7 +105,7 @@ int patch_execute(int fd, int (*apply)(int fd, struct patch *patch))
|
||||
if (strcmp(patch->filename, "DISK"))
|
||||
continue;
|
||||
|
||||
ret = apply(fd, patch);
|
||||
ret = apply(qdl, patch);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
4
patch.h
4
patch.h
@@ -1,6 +1,8 @@
|
||||
#ifndef __PATCH_H__
|
||||
#define __PATCH_H__
|
||||
|
||||
struct qdl_device;
|
||||
|
||||
struct patch {
|
||||
unsigned sector_size;
|
||||
unsigned byte_offset;
|
||||
@@ -15,6 +17,6 @@ struct patch {
|
||||
};
|
||||
|
||||
int patch_load(const char *patch_file);
|
||||
int patch_execute(int fd, int (*apply)(int fd, struct patch *patch));
|
||||
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch));
|
||||
|
||||
#endif
|
||||
|
||||
79
program.c
79
program.c
@@ -36,46 +36,11 @@
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "program.h"
|
||||
#include "qdl.h"
|
||||
|
||||
static struct program *programes;
|
||||
static struct program *programes_last;
|
||||
|
||||
static unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors)
|
||||
{
|
||||
xmlChar *value;
|
||||
|
||||
value = xmlGetProp(node, (xmlChar*)attr);
|
||||
if (!value)
|
||||
(*errors)++;
|
||||
|
||||
return strtoul((char*)value, NULL, 10);
|
||||
}
|
||||
|
||||
static const char *attr_as_string(xmlNode *node, const char *attr, int *errors)
|
||||
{
|
||||
xmlChar *value;
|
||||
|
||||
value = xmlGetProp(node, (xmlChar*)attr);
|
||||
if (!value)
|
||||
(*errors)++;
|
||||
|
||||
if (value && value[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
return strdup((char*)value);
|
||||
}
|
||||
|
||||
static bool attr_as_bool(xmlNode *node, const char *attr, int *errors)
|
||||
{
|
||||
xmlChar *value;
|
||||
|
||||
value = xmlGetProp(node, (xmlChar*)attr);
|
||||
if (!value)
|
||||
(*errors)++;
|
||||
|
||||
return xmlStrcmp(value, (xmlChar*)"true") == 0;
|
||||
}
|
||||
|
||||
int program_load(const char *program_file)
|
||||
{
|
||||
struct program *program;
|
||||
@@ -110,9 +75,6 @@ int program_load(const char *program_file)
|
||||
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->size = attr_as_unsigned(node, "size_in_KB", &errors);
|
||||
program->sparse = attr_as_bool(node, "sparse", &errors);
|
||||
program->start_bytes = attr_as_string(node, "start_byte_hex", &errors);
|
||||
program->start_sector = attr_as_string(node, "start_sector", &errors);
|
||||
|
||||
if (errors) {
|
||||
@@ -135,22 +97,34 @@ int program_load(const char *program_file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int program_execute(int usbfd, int (*apply)(int usbfd, struct program *program, int fd))
|
||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
||||
const char *incdir)
|
||||
{
|
||||
struct program *program;
|
||||
const char *filename;
|
||||
char tmp[PATH_MAX];
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
for (program = programes; program; program = program->next) {
|
||||
fd = -1;
|
||||
if (program->filename) {
|
||||
fd = open(program->filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
printf("Unable to open %s...ignoring\n", program->filename);
|
||||
continue;
|
||||
}
|
||||
if (!program->filename)
|
||||
continue;
|
||||
|
||||
filename = program->filename;
|
||||
if (incdir) {
|
||||
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
|
||||
if (access(tmp, F_OK) != -1)
|
||||
filename = tmp;
|
||||
}
|
||||
ret = apply(usbfd, program, fd);
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
printf("Unable to open %s...ignoring\n", program->filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = apply(qdl, program, fd);
|
||||
|
||||
close(fd);
|
||||
if (ret)
|
||||
@@ -165,9 +139,9 @@ int program_execute(int usbfd, int (*apply)(int usbfd, struct program *program,
|
||||
*
|
||||
* Returns partition number, or negative errno on failure.
|
||||
*
|
||||
* Scan program tags for a partition with the label "xbl" or "sbl1" 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)
|
||||
{
|
||||
@@ -178,7 +152,8 @@ int program_find_bootable_partition(void)
|
||||
for (program = programes; program; program = program->next) {
|
||||
label = program->label;
|
||||
|
||||
if (!strcmp(label, "xbl") || !strcmp(label, "sbl1")) {
|
||||
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
|
||||
!strcmp(label, "sbl1")) {
|
||||
if (part != -ENOENT)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __PROGRAM_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "qdl.h"
|
||||
|
||||
struct program {
|
||||
unsigned sector_size;
|
||||
@@ -10,16 +11,14 @@ struct program {
|
||||
const char *label;
|
||||
unsigned num_sectors;
|
||||
unsigned partition;
|
||||
unsigned size;
|
||||
bool sparse;
|
||||
const char *start_bytes;
|
||||
const char *start_sector;
|
||||
|
||||
struct program *next;
|
||||
};
|
||||
|
||||
int program_load(const char *program_file);
|
||||
int program_execute(int usbfd, int (*apply)(int usbfd, struct program *program, int fd));
|
||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
||||
const char *incdir);
|
||||
int program_find_bootable_partition(void);
|
||||
|
||||
#endif
|
||||
|
||||
280
qdl.c
280
qdl.c
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Linaro Ltd.
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,6 +29,9 @@
|
||||
* 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>
|
||||
@@ -35,6 +39,8 @@
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <libudev.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -48,11 +54,14 @@
|
||||
|
||||
#include "qdl.h"
|
||||
#include "patch.h"
|
||||
#include "ufs.h"
|
||||
#include "usb.h"
|
||||
|
||||
enum {
|
||||
QDL_FILE_UNKNOWN,
|
||||
QDL_FILE_PATCH,
|
||||
QDL_FILE_PROGRAM,
|
||||
QDL_FILE_UFS,
|
||||
QDL_FILE_CONTENTS,
|
||||
};
|
||||
|
||||
@@ -62,7 +71,8 @@ static int detect_type(const char *xml_file)
|
||||
{
|
||||
xmlNode *root;
|
||||
xmlDoc *doc;
|
||||
int type;
|
||||
xmlNode *node;
|
||||
int type = QDL_FILE_UNKNOWN;
|
||||
|
||||
doc = xmlReadFile(xml_file, NULL, 0);
|
||||
if (!doc) {
|
||||
@@ -71,196 +81,140 @@ static int detect_type(const char *xml_file)
|
||||
}
|
||||
|
||||
root = xmlDocGetRootElement(doc);
|
||||
if (!xmlStrcmp(root->name, (xmlChar*)"patches"))
|
||||
if (!xmlStrcmp(root->name, (xmlChar*)"patches")) {
|
||||
type = QDL_FILE_PATCH;
|
||||
else if (!xmlStrcmp(root->name, (xmlChar*)"data"))
|
||||
type = QDL_FILE_PROGRAM;
|
||||
else if (!xmlStrcmp(root->name, (xmlChar*)"contents"))
|
||||
} else if (!xmlStrcmp(root->name, (xmlChar*)"data")) {
|
||||
for (node = root->children; node ; node = node->next) {
|
||||
if (node->type != XML_ELEMENT_NODE)
|
||||
continue;
|
||||
if (!xmlStrcmp(node->name, (xmlChar*)"program")) {
|
||||
type = QDL_FILE_PROGRAM;
|
||||
break;
|
||||
}
|
||||
if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) {
|
||||
type = QDL_FILE_UFS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!xmlStrcmp(root->name, (xmlChar*)"contents")) {
|
||||
type = QDL_FILE_CONTENTS;
|
||||
else
|
||||
type = QDL_FILE_UNKNOWN;
|
||||
}
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static int readat(int dir, const char *name, char *buf, size_t len)
|
||||
static void print_usage(void)
|
||||
{
|
||||
ssize_t n;
|
||||
int fd;
|
||||
int ret = 0;
|
||||
|
||||
fd = openat(dir, name, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
n = read(fd, buf, len - 1);
|
||||
if (n < 0) {
|
||||
warn("failed to read %s", name);
|
||||
ret = -EINVAL;
|
||||
goto close_fd;
|
||||
}
|
||||
buf[n] = '\0';
|
||||
|
||||
buf[strcspn(buf, "\n")] = '\0';
|
||||
|
||||
close_fd:
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int find_qdl_tty(char *dev_name, size_t dev_name_len)
|
||||
{
|
||||
struct dirent *de;
|
||||
int found = -ENOENT;
|
||||
char vid[5];
|
||||
char pid[5];
|
||||
DIR *dir;
|
||||
int tty;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
tty = open("/sys/class/tty", O_DIRECTORY);
|
||||
if (tty < 0)
|
||||
err(1, "failed to open /sys/class/tty");
|
||||
|
||||
dir = fdopendir(tty);
|
||||
if (!dir)
|
||||
err(1, "failed to opendir /sys/class/tty");
|
||||
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
if (strncmp(de->d_name, "ttyUSB", 6) != 0)
|
||||
continue;
|
||||
|
||||
fd = openat(tty, de->d_name, O_DIRECTORY);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
ret = readat(fd, "../../../../idVendor", vid, sizeof(vid));
|
||||
if (ret < 0)
|
||||
goto close_fd;
|
||||
|
||||
ret = readat(fd, "../../../../idProduct", pid, sizeof(pid));
|
||||
if (ret < 0)
|
||||
goto close_fd;
|
||||
|
||||
if (strcmp(vid, "05c6") || strcmp(pid, "9008"))
|
||||
goto close_fd;
|
||||
|
||||
snprintf(dev_name, dev_name_len, "/dev/%s", de->d_name);
|
||||
found = 0;
|
||||
|
||||
close_fd:
|
||||
close(fd);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
close(tty);
|
||||
|
||||
return found;
|
||||
|
||||
}
|
||||
|
||||
static int tty_open(struct termios *old)
|
||||
{
|
||||
struct termios tios;
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
retry:
|
||||
ret = find_qdl_tty(path, sizeof(path));
|
||||
if (ret < 0) {
|
||||
printf("Waiting for QDL tty...\r");
|
||||
fflush(stdout);
|
||||
sleep(1);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDWR | O_NOCTTY | O_EXCL);
|
||||
if (fd < 0) {
|
||||
err(1, "unable to open \"%s\"", path);
|
||||
}
|
||||
|
||||
ret = tcgetattr(fd, old);
|
||||
if (ret < 0)
|
||||
err(1, "unable to retrieve \"%s\" tios", path);
|
||||
|
||||
memset(&tios, 0, sizeof(tios));
|
||||
tios.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD;
|
||||
tios.c_iflag = IGNPAR;
|
||||
tios.c_oflag = 0;
|
||||
|
||||
tcflush(fd, TCIFLUSH);
|
||||
|
||||
ret = tcsetattr(fd, TCSANOW, &tios);
|
||||
if (ret < 0)
|
||||
err(1, "unable to update \"%s\" tios", path);
|
||||
|
||||
return fd;
|
||||
extern const char *__progname;
|
||||
fprintf(stderr,
|
||||
"%s [--debug] [--storage <emmc|ufs>] [--finalize-provisioning] [--include <PATH>] <prog.mbn> [<program> <patch> ...]\n",
|
||||
__progname);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
extern const char *__progname;
|
||||
struct termios tios;
|
||||
char *prog_mbn;
|
||||
struct qdl_device *qdl;
|
||||
char *prog_mbn, *storage="ufs";
|
||||
char *incdir = NULL;
|
||||
int bootable;
|
||||
int type;
|
||||
int ret;
|
||||
int fd;
|
||||
int i;
|
||||
int opt;
|
||||
bool qdl_finalize_provisioning = false;
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "--debug") == 0) {
|
||||
qdl_debug = true;
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "%s <prog.mbn> [<program> <patch> ...]\n", __progname);
|
||||
return 1;
|
||||
}
|
||||
static struct option options[] = {
|
||||
{"debug", no_argument, 0, 'd'},
|
||||
{"include", required_argument, 0, 'i'},
|
||||
{"finalize-provisioning", no_argument, 0, 'l'},
|
||||
{"storage", required_argument, 0, 's'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
prog_mbn = argv[1];
|
||||
|
||||
for (i = 2; i < argc; i++) {
|
||||
type = detect_type(argv[i]);
|
||||
if (type < 0 || type == QDL_FILE_UNKNOWN)
|
||||
errx(1, "failed to detect file type of %s\n", argv[i]);
|
||||
|
||||
switch (type) {
|
||||
case QDL_FILE_PATCH:
|
||||
ret = patch_load(argv[i]);
|
||||
if (ret < 0)
|
||||
errx(1, "patch_load %s failed", argv[i]);
|
||||
while ((opt = getopt_long(argc, argv, "di:", options, NULL )) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
qdl_debug = true;
|
||||
break;
|
||||
case QDL_FILE_PROGRAM:
|
||||
ret = program_load(argv[i]);
|
||||
if (ret < 0)
|
||||
errx(1, "program_load %s failed", argv[i]);
|
||||
case 'i':
|
||||
incdir = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
qdl_finalize_provisioning = true;
|
||||
break;
|
||||
case 's':
|
||||
storage = optarg;
|
||||
break;
|
||||
default:
|
||||
errx(1, "%s type not yet supported", argv[i]);
|
||||
break;
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fd = tty_open(&tios);
|
||||
if (fd < 0)
|
||||
err(1, "failed to open QDL tty");
|
||||
/* at least 2 non optional args required */
|
||||
if ((optind + 2) > argc) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = sahara_run(fd, prog_mbn);
|
||||
prog_mbn = argv[optind++];
|
||||
|
||||
do {
|
||||
type = detect_type(argv[optind]);
|
||||
if (type < 0 || type == QDL_FILE_UNKNOWN)
|
||||
errx(1, "failed to detect file type of %s\n", argv[optind]);
|
||||
|
||||
switch (type) {
|
||||
case QDL_FILE_PATCH:
|
||||
ret = patch_load(argv[optind]);
|
||||
if (ret < 0)
|
||||
errx(1, "patch_load %s failed", argv[optind]);
|
||||
break;
|
||||
case QDL_FILE_PROGRAM:
|
||||
ret = program_load(argv[optind]);
|
||||
if (ret < 0)
|
||||
errx(1, "program_load %s failed", argv[optind]);
|
||||
break;
|
||||
case QDL_FILE_UFS:
|
||||
ret = ufs_load(argv[optind],qdl_finalize_provisioning);
|
||||
if (ret < 0)
|
||||
errx(1, "ufs_load %s failed", argv[optind]);
|
||||
break;
|
||||
default:
|
||||
errx(1, "%s type not yet supported", argv[optind]);
|
||||
break;
|
||||
}
|
||||
} while (++optind < argc);
|
||||
|
||||
qdl = usb_open();
|
||||
if (ret)
|
||||
return 1;
|
||||
|
||||
ret = sahara_run(qdl, prog_mbn);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return 1;
|
||||
|
||||
ret = firehose_run(fd);
|
||||
|
||||
out:
|
||||
ret = tcsetattr(fd, TCSANOW, &tios);
|
||||
ret = firehose_open(qdl, !strcmp(storage, "ufs"));
|
||||
if (ret < 0)
|
||||
warn("unable to restore tios of ttyUSB1");
|
||||
close(fd);
|
||||
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;
|
||||
}
|
||||
|
||||
23
qdl.h
23
qdl.h
@@ -5,10 +5,29 @@
|
||||
|
||||
#include "patch.h"
|
||||
#include "program.h"
|
||||
#include <libxml/tree.h>
|
||||
|
||||
int firehose_run(int fd);
|
||||
int sahara_run(int fd, char *prog_mbn);
|
||||
struct qdl_device;
|
||||
struct program;
|
||||
struct patch;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
59
sahara.c
59
sahara.c
@@ -35,6 +35,7 @@
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -44,6 +45,7 @@
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include "qdl.h"
|
||||
#include "usb.h"
|
||||
|
||||
struct sahara_pkt {
|
||||
uint32_t cmd;
|
||||
@@ -77,14 +79,14 @@ struct sahara_pkt {
|
||||
uint32_t status;
|
||||
} done_resp;
|
||||
struct {
|
||||
uint32_t image;
|
||||
uint64_t image;
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
} read64_req;
|
||||
};
|
||||
};
|
||||
|
||||
static void sahara_hello(int fd, struct sahara_pkt *pkt)
|
||||
static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
{
|
||||
struct sahara_pkt resp;
|
||||
|
||||
@@ -100,10 +102,10 @@ static void sahara_hello(int fd, struct sahara_pkt *pkt)
|
||||
resp.hello_resp.status = 0;
|
||||
resp.hello_resp.mode = pkt->hello_req.mode;
|
||||
|
||||
write(fd, &resp, resp.length);
|
||||
qdl_write(qdl, &resp, resp.length, true);
|
||||
}
|
||||
|
||||
static int sahara_read_common(int fd, const char *mbn, 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;
|
||||
@@ -125,7 +127,7 @@ static int sahara_read_common(int fd, const char *mbn, off_t offset, size_t len)
|
||||
goto out;
|
||||
}
|
||||
|
||||
n = write(fd, buf, n);
|
||||
n = qdl_write(qdl, buf, n, true);
|
||||
if (n != len)
|
||||
err(1, "failed to write %zu bytes to sahara", len);
|
||||
|
||||
@@ -136,7 +138,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sahara_read(int fd, struct sahara_pkt *pkt, const char *mbn)
|
||||
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -145,26 +147,26 @@ static void sahara_read(int fd, struct sahara_pkt *pkt, const char *mbn)
|
||||
printf("READ image: %d offset: 0x%x length: 0x%x\n",
|
||||
pkt->read_req.image, pkt->read_req.offset, pkt->read_req.length);
|
||||
|
||||
ret = sahara_read_common(fd, mbn, 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");
|
||||
}
|
||||
|
||||
static void sahara_read64(int fd, struct sahara_pkt *pkt, const char *mbn)
|
||||
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(pkt->length == 0x20);
|
||||
|
||||
printf("READ64 image: %d offset: 0x%lx length: 0x%lx\n",
|
||||
printf("READ64 image: %" PRId64 " offset: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
|
||||
pkt->read64_req.image, pkt->read64_req.offset, pkt->read64_req.length);
|
||||
|
||||
ret = sahara_read_common(fd, mbn, 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");
|
||||
}
|
||||
|
||||
static void sahara_eoi(int fd, struct sahara_pkt *pkt)
|
||||
static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
{
|
||||
struct sahara_pkt done;
|
||||
|
||||
@@ -179,10 +181,10 @@ static void sahara_eoi(int fd, struct sahara_pkt *pkt)
|
||||
|
||||
done.cmd = 5;
|
||||
done.length = 0x8;
|
||||
write(fd, &done, done.length);
|
||||
qdl_write(qdl, &done, done.length, true);
|
||||
}
|
||||
|
||||
static int sahara_done(int fd, struct sahara_pkt *pkt)
|
||||
static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
{
|
||||
assert(pkt->length == 0xc);
|
||||
|
||||
@@ -191,31 +193,18 @@ static int sahara_done(int fd, struct sahara_pkt *pkt)
|
||||
return pkt->done_resp.status;
|
||||
}
|
||||
|
||||
int sahara_run(int fd, char *prog_mbn)
|
||||
int sahara_run(struct qdl_device *qdl, const char *prog_mbn)
|
||||
{
|
||||
struct sahara_pkt *pkt;
|
||||
struct pollfd pfd;
|
||||
char buf[4096];
|
||||
char tmp[32];
|
||||
bool done = false;
|
||||
int n;
|
||||
|
||||
while (!done) {
|
||||
pfd.fd = fd;
|
||||
pfd.events = POLLIN;
|
||||
n = poll(&pfd, 1, -1);
|
||||
if (n < 0) {
|
||||
warn("failed to poll");
|
||||
n = qdl_read(qdl, buf, sizeof(buf), 1000);
|
||||
if (n < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
n = read(fd, buf, sizeof(buf));
|
||||
if (n == 0) {
|
||||
continue;
|
||||
} else if (n < 0) {
|
||||
warn("failed to read");
|
||||
break;
|
||||
}
|
||||
|
||||
pkt = (struct sahara_pkt*)buf;
|
||||
if (n != pkt->length) {
|
||||
@@ -225,20 +214,20 @@ int sahara_run(int fd, char *prog_mbn)
|
||||
|
||||
switch (pkt->cmd) {
|
||||
case 1:
|
||||
sahara_hello(fd, pkt);
|
||||
sahara_hello(qdl, pkt);
|
||||
break;
|
||||
case 3:
|
||||
sahara_read(fd, pkt, prog_mbn);
|
||||
sahara_read(qdl, pkt, prog_mbn);
|
||||
break;
|
||||
case 4:
|
||||
sahara_eoi(fd, pkt);
|
||||
sahara_eoi(qdl, pkt);
|
||||
break;
|
||||
case 6:
|
||||
sahara_done(fd, pkt);
|
||||
sahara_done(qdl, pkt);
|
||||
done = true;
|
||||
break;
|
||||
case 0x12:
|
||||
sahara_read64(fd, pkt, prog_mbn);
|
||||
sahara_read64(qdl, pkt, prog_mbn);
|
||||
break;
|
||||
default:
|
||||
sprintf(tmp, "CMD%x", pkt->cmd);
|
||||
@@ -247,5 +236,5 @@ int sahara_run(int fd, char *prog_mbn)
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return done ? 0 : -1;
|
||||
}
|
||||
|
||||
310
ufs.c
Normal file
310
ufs.c
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * 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.
|
||||
* * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/xpath.h>
|
||||
|
||||
#include "ufs.h"
|
||||
#include "qdl.h"
|
||||
#include "patch.h"
|
||||
|
||||
struct ufs_common *ufs_common_p;
|
||||
struct ufs_epilogue *ufs_epilogue_p;
|
||||
struct ufs_body *ufs_body_p;
|
||||
struct ufs_body *ufs_body_last;
|
||||
|
||||
static const char notice_bconfigdescrlock[] = "\n"
|
||||
"Please pay attention that UFS provisioning is irreversible (OTP) operation unless parameter bConfigDescrLock = 0.\n"
|
||||
"In order to prevent unintentional device locking the tool has the following safety:\n\n"
|
||||
" if you REALLY intend to perform OTP, please ensure that your XML includes property\n"
|
||||
" bConfigDescrLock = 1 AND provide command line parameter --finalize-provisioning.\n\n"
|
||||
" Unless you intend to lock your device, please set bConfigDescrLock = 0 in your XML\n"
|
||||
" and don't use command line parameter --finalize-provisioning.\n\n"
|
||||
"In case of mismatch between CL and XML provisioning is not performed.\n\n";
|
||||
|
||||
|
||||
|
||||
bool ufs_need_provisioning(void)
|
||||
{
|
||||
return !!ufs_epilogue_p;
|
||||
}
|
||||
|
||||
struct ufs_common *ufs_parse_common_params(xmlNode *node, bool finalize_provisioning)
|
||||
{
|
||||
struct ufs_common *result;
|
||||
int errors;
|
||||
|
||||
result = calloc(1, sizeof(struct ufs_common));
|
||||
errors = 0;
|
||||
|
||||
result->bNumberLU = attr_as_unsigned(node, "bNumberLU", &errors);
|
||||
result->bBootEnable = !!attr_as_unsigned(node, "bBootEnable", &errors);
|
||||
result->bDescrAccessEn = !!attr_as_unsigned(node, "bDescrAccessEn", &errors);
|
||||
result->bInitPowerMode = attr_as_unsigned(node, "bInitPowerMode", &errors);
|
||||
result->bHighPriorityLUN = attr_as_unsigned(node, "bHighPriorityLUN", &errors);
|
||||
result->bSecureRemovalType = attr_as_unsigned(node, "bSecureRemovalType", &errors);
|
||||
result->bInitActiveICCLevel = attr_as_unsigned(node, "bInitActiveICCLevel", &errors);
|
||||
result->wPeriodicRTCUpdate = attr_as_unsigned(node, "wPeriodicRTCUpdate", &errors);
|
||||
result->bConfigDescrLock = !!attr_as_unsigned(node, "bConfigDescrLock", &errors);
|
||||
|
||||
if (errors) {
|
||||
fprintf(stderr, "[UFS] errors while parsing common\n");
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ufs_body *ufs_parse_body(xmlNode *node)
|
||||
{
|
||||
struct ufs_body *result;
|
||||
int errors;
|
||||
|
||||
result = calloc(1, sizeof(struct ufs_body));
|
||||
errors = 0;
|
||||
|
||||
result->LUNum = attr_as_unsigned(node, "LUNum", &errors);
|
||||
result->bLUEnable = !!attr_as_unsigned(node, "bLUEnable", &errors);
|
||||
result->bBootLunID = attr_as_unsigned(node, "bBootLunID", &errors);
|
||||
result->size_in_kb = attr_as_unsigned(node, "size_in_kb", &errors);
|
||||
result->bDataReliability = attr_as_unsigned(node, "bDataReliability", &errors);
|
||||
result->bLUWriteProtect = attr_as_unsigned(node, "bLUWriteProtect", &errors);
|
||||
result->bMemoryType = attr_as_unsigned(node, "bMemoryType", &errors);
|
||||
result->bLogicalBlockSize = attr_as_unsigned(node, "bLogicalBlockSize", &errors);
|
||||
result->bProvisioningType = attr_as_unsigned(node, "bProvisioningType", &errors);
|
||||
result->wContextCapabilities = attr_as_unsigned(node, "wContextCapabilities", &errors);
|
||||
result->desc = attr_as_string(node, "desc", &errors);
|
||||
|
||||
if (errors) {
|
||||
fprintf(stderr, "[UFS] errors while parsing body\n");
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ufs_epilogue *ufs_parse_epilogue(xmlNode *node)
|
||||
{
|
||||
struct ufs_epilogue *result;
|
||||
int errors = 0;
|
||||
|
||||
result = calloc(1, sizeof(struct ufs_epilogue));
|
||||
|
||||
result->LUNtoGrow = attr_as_unsigned(node, "LUNtoGrow", &errors);
|
||||
|
||||
if (errors) {
|
||||
fprintf(stderr, "[UFS] errors while parsing epilogue\n");
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int ufs_load(const char *ufs_file, bool finalize_provisioning)
|
||||
{
|
||||
xmlNode *node;
|
||||
xmlNode *root;
|
||||
xmlDoc *doc;
|
||||
int retval = 0;
|
||||
struct ufs_body *ufs_body_tmp;
|
||||
|
||||
if (ufs_common_p) {
|
||||
fprintf(stderr,
|
||||
"Only one UFS provisioning XML allowed, %s ignored\n",
|
||||
ufs_file);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
doc = xmlReadFile(ufs_file, NULL, 0);
|
||||
if (!doc) {
|
||||
fprintf(stderr, "[UFS] failed to parse %s\n", ufs_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*)"ufs")) {
|
||||
fprintf(stderr, "[UFS] unrecognized tag \"%s\", ignoring\n",
|
||||
node->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (xmlGetProp(node, (xmlChar *)"bNumberLU")) {
|
||||
if (!ufs_common_p) {
|
||||
ufs_common_p = ufs_parse_common_params(node,
|
||||
finalize_provisioning);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[UFS] Only one common tag is allowed\n"
|
||||
"[UFS] provisioning aborted\n");
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ufs_common_p) {
|
||||
fprintf(stderr, "[UFS] Common tag corrupted\n"
|
||||
"[UFS] provisioning aborted\n");
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
} else if (xmlGetProp(node, (xmlChar *)"LUNum")) {
|
||||
ufs_body_tmp = ufs_parse_body(node);
|
||||
if(ufs_body_tmp) {
|
||||
if (ufs_body_p) {
|
||||
ufs_body_last->next = ufs_body_tmp;
|
||||
ufs_body_last = ufs_body_tmp;
|
||||
}
|
||||
else {
|
||||
ufs_body_p = ufs_body_tmp;
|
||||
ufs_body_last = ufs_body_tmp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[UFS] LU tag corrupted\n"
|
||||
"[UFS] provisioning aborted\n");
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
} else if (xmlGetProp(node, (xmlChar *)"commit")) {
|
||||
if (!ufs_epilogue_p) {
|
||||
ufs_epilogue_p = ufs_parse_epilogue(node);
|
||||
if (ufs_epilogue_p)
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[UFS] Only one finalizing tag is allowed\n"
|
||||
"[UFS] provisioning aborted\n");
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ufs_epilogue_p) {
|
||||
fprintf(stderr, "[UFS] Finalizing tag corrupted\n"
|
||||
"[UFS] provisioning aborted\n");
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "[UFS] Unknown tag or %s corrupted\n"
|
||||
"[UFS] provisioning aborted\n", ufs_file);
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
if (!retval && (!ufs_common_p || !ufs_body_p || !ufs_epilogue_p)) {
|
||||
fprintf(stderr, "[UFS] %s seems to be incomplete\n"
|
||||
"[UFS] provisioning aborted\n", ufs_file);
|
||||
retval = -EINVAL;
|
||||
}
|
||||
|
||||
if (retval){
|
||||
if (ufs_common_p) {
|
||||
free(ufs_common_p);
|
||||
}
|
||||
if (ufs_body_p) {
|
||||
free(ufs_body_p);
|
||||
}
|
||||
if (ufs_epilogue_p) {
|
||||
free(ufs_epilogue_p);
|
||||
}
|
||||
fprintf(stderr, "[UFS] %s seems to be corrupted, ignore\n", ufs_file);
|
||||
return retval;
|
||||
}
|
||||
if (!finalize_provisioning != !ufs_common_p->bConfigDescrLock) {
|
||||
fprintf(stderr,
|
||||
"[UFS] Value bConfigDescrLock %d in file %s don't match command line parameter --finalize-provisioning %d\n"
|
||||
"[UFS] provisioning aborted\n",
|
||||
ufs_common_p->bConfigDescrLock, ufs_file, finalize_provisioning);
|
||||
fprintf(stderr, notice_bconfigdescrlock);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ufs_provisioning_execute(struct qdl_device *qdl,
|
||||
int (*apply_ufs_common)(struct qdl_device *, struct ufs_common*),
|
||||
int (*apply_ufs_body)(struct qdl_device *, struct ufs_body*),
|
||||
int (*apply_ufs_epilogue)(struct qdl_device *, struct ufs_epilogue*, bool))
|
||||
{
|
||||
int ret;
|
||||
struct ufs_body *body;
|
||||
|
||||
if (ufs_common_p->bConfigDescrLock) {
|
||||
int i;
|
||||
printf("Attention!\nIrreversible provisioning will start in 5 s\n");
|
||||
for(i=5; i>0; i--) {
|
||||
printf(".\a");
|
||||
sleep(1);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Just ask a target to check the XML w/o real provisioning
|
||||
ret = apply_ufs_common(qdl, ufs_common_p);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (body = ufs_body_p; body; body = body->next) {
|
||||
ret = apply_ufs_body(qdl, body);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = apply_ufs_epilogue(qdl, ufs_epilogue_p, false);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"UFS provisioning impossible, provisioning XML may be corrupted\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Real provisioning -- target didn't refuse a given XML
|
||||
ret = apply_ufs_common(qdl, ufs_common_p);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (body = ufs_body_p; body; body = body->next) {
|
||||
ret = apply_ufs_body(qdl, body);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return apply_ufs_epilogue(qdl, ufs_epilogue_p, true);
|
||||
}
|
||||
75
ufs.h
Normal file
75
ufs.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * 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.
|
||||
* * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 __UFS_H__
|
||||
#define __UFS_H__
|
||||
#include <stdbool.h>
|
||||
|
||||
struct qdl_device;
|
||||
|
||||
struct ufs_common {
|
||||
unsigned bNumberLU;
|
||||
bool bBootEnable;
|
||||
bool bDescrAccessEn;
|
||||
unsigned bInitPowerMode;
|
||||
unsigned bHighPriorityLUN;
|
||||
unsigned bSecureRemovalType;
|
||||
unsigned bInitActiveICCLevel;
|
||||
unsigned wPeriodicRTCUpdate;
|
||||
bool bConfigDescrLock;
|
||||
};
|
||||
|
||||
struct ufs_body {
|
||||
unsigned LUNum;
|
||||
bool bLUEnable;
|
||||
unsigned bBootLunID;
|
||||
unsigned size_in_kb;
|
||||
unsigned bDataReliability;
|
||||
unsigned bLUWriteProtect;
|
||||
unsigned bMemoryType;
|
||||
unsigned bLogicalBlockSize;
|
||||
unsigned bProvisioningType;
|
||||
unsigned wContextCapabilities;
|
||||
const char *desc;
|
||||
|
||||
struct ufs_body *next;
|
||||
};
|
||||
|
||||
struct ufs_epilogue {
|
||||
unsigned LUNtoGrow;
|
||||
bool commit;
|
||||
};
|
||||
|
||||
int ufs_load(const char *ufs_file, bool finalize_provisioning);
|
||||
int ufs_provisioning_execute(struct qdl_device *qdl,
|
||||
int (*apply_ufs_common)(struct qdl_device *qdl, struct ufs_common *ufs),
|
||||
int (*apply_ufs_body)(struct qdl_device *qdl, struct ufs_body *ufs),
|
||||
int (*apply_ufs_epilogue)(struct qdl_device *qdl, struct ufs_epilogue *ufs, bool commit));
|
||||
bool ufs_need_provisioning(void);
|
||||
|
||||
#endif
|
||||
332
usb.c
Normal file
332
usb.c
Normal 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
10
usb.h
Normal 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
|
||||
32
util.c
32
util.c
@@ -31,6 +31,9 @@
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
@@ -77,3 +80,32 @@ void print_hex_dump(const char *prefix, const void *buf, size_t len)
|
||||
printf("%s %04x: %s\n", prefix, i, line);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors)
|
||||
{
|
||||
xmlChar *value;
|
||||
|
||||
value = xmlGetProp(node, (xmlChar*)attr);
|
||||
if (!value) {
|
||||
(*errors)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (unsigned int) strtoul((char*)value, NULL, 10);
|
||||
}
|
||||
|
||||
const char *attr_as_string(xmlNode *node, const char *attr, int *errors)
|
||||
{
|
||||
xmlChar *value;
|
||||
|
||||
value = xmlGetProp(node, (xmlChar*)attr);
|
||||
if (!value) {
|
||||
(*errors)++;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (value[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
return strdup((char*)value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user