mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6200a2068 | ||
|
|
44d148a63b | ||
|
|
c37088f3fd | ||
|
|
e3d3811748 | ||
|
|
9aae1e9ff2 | ||
|
|
bff56ea21b | ||
|
|
5b4874667c | ||
|
|
e1a3670d91 | ||
|
|
bb1aa99068 | ||
|
|
342e99b9be | ||
|
|
066022e6d8 | ||
|
|
2d239db0ca |
32
.github/workflows/build.yml
vendored
32
.github/workflows/build.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: Buildtest
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, macos-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Dependencies (Ubuntu)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
|
||||
|
||||
- name: Dependencies (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install libxml2
|
||||
brew install libusb
|
||||
|
||||
- name: Build
|
||||
run: make
|
||||
36
.github/workflows/codeql.yml
vendored
36
.github/workflows/codeql.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: CodeQL
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
codeql:
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
|
||||
|
||||
- name: CodeQL init
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: c-cpp
|
||||
build-mode: autobuild
|
||||
|
||||
- name: CodeQL build
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: CodeQL analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,6 +1,2 @@
|
||||
*.o
|
||||
qdl
|
||||
qdl-ramdump
|
||||
ks
|
||||
compile_commands.json
|
||||
.cache
|
||||
|
||||
47
Makefile
47
Makefile
@@ -1,40 +1,33 @@
|
||||
QDL := qdl
|
||||
RAMDUMP := qdl-ramdump
|
||||
OUT := qdl
|
||||
NBDKIT := nbdkit-qdl-plugin.so
|
||||
|
||||
CFLAGS += -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0`
|
||||
LDFLAGS += `pkg-config --libs libxml-2.0 libusb-1.0`
|
||||
CFLAGS := -O2 -Wall -g `xml2-config --cflags` -fPIC
|
||||
LDFLAGS := `xml2-config --libs` -ludev
|
||||
prefix := /usr/local
|
||||
|
||||
QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c read.c ufs.c usb.c
|
||||
QDL_OBJS := $(QDL_SRCS:.c=.o)
|
||||
COMMON_SRCS := firehose.c json.c sahara.c util.c patch.c program.c ufs.c usb.c
|
||||
|
||||
RAMDUMP_SRCS := ramdump.c sahara.c usb.c util.c
|
||||
RAMDUMP_OBJS := $(RAMDUMP_SRCS:.c=.o)
|
||||
QDL_SRCS := qdl.c
|
||||
QDL_OBJS := $(COMMON_SRCS:.c=.o) $(QDL_SRCS:.c=.o)
|
||||
|
||||
KS_OUT := ks
|
||||
KS_SRCS := ks.c sahara.c util.c
|
||||
KS_OBJS := $(KS_SRCS:.c=.o)
|
||||
|
||||
default: $(QDL) $(RAMDUMP) $(KS_OUT)
|
||||
|
||||
$(QDL): $(QDL_OBJS)
|
||||
$(OUT): $(QDL_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(RAMDUMP): $(RAMDUMP_OBJS)
|
||||
NBDKIT_SRCS := nbdkit-qdl-plugin.c
|
||||
NBDKIT_OBJS := $(COMMON_SRCS:.c=.o) $(NBDKIT_SRCS:.c=.o)
|
||||
|
||||
$(NBDKIT): LDFLAGS += -shared
|
||||
$(NBDKIT): $(NBDKIT_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(KS_OUT): $(KS_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
.PHONY: lib
|
||||
lib: $(NBDKIT)
|
||||
|
||||
compile_commands.json: $(QDL_SRCS) $(KS_SRCS)
|
||||
@echo -n $^ | jq -snR "[inputs|split(\" \")[]|{directory:\"$(PWD)\", command: \"$(CC) $(CFLAGS) -c \(.)\", file:.}]" > $@
|
||||
.PHONY: all
|
||||
all: $(OUT) $(NBDKIT)
|
||||
|
||||
clean:
|
||||
rm -f $(QDL) $(QDL_OBJS)
|
||||
rm -f $(RAMDUMP) $(RAMDUMP_OBJS)
|
||||
rm -f $(KS_OUT) $(KS_OBJS)
|
||||
rm -f compile_commands.json
|
||||
rm -f $(OUT) $(QDL_OBJS) $(NBDKIT_OBJS)
|
||||
|
||||
install: $(QDL) $(RAMDUMP) $(KS_OUT)
|
||||
install -d $(DESTDIR)$(prefix)/bin
|
||||
install -m 755 $^ $(DESTDIR)$(prefix)/bin
|
||||
install: $(OUT)
|
||||
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<
|
||||
|
||||
33
README
33
README
@@ -7,10 +7,37 @@ loader and use this to flash images.
|
||||
Usage:
|
||||
qdl <prog.mbn> [<program> <patch> ...]
|
||||
|
||||
nbdkit plugin
|
||||
=============
|
||||
In addition to the qdl programmer a nbdkit plugin can be built. This provides
|
||||
block layer access to the flash on the device, allowing partition tables to be
|
||||
modified and partitions mounted on the host PC.
|
||||
|
||||
The nbdkit plugin has the following configuration parameters:
|
||||
programmer: firehose programmer
|
||||
lun: LUN number to be managed, defaults to 1
|
||||
storage: "ufs" or "emmc" to select firehose configuration
|
||||
|
||||
To launch a nbd server, launch nbdkit as follows:
|
||||
modprobe nbd
|
||||
nbdkit -fv ./nbdkit-qdl-plugin.so programmer=prog_firehose_ddr.elf lun=0
|
||||
|
||||
Then connect nbd to this, using:
|
||||
sudo nbd-client -b 4096 localhost /dev/nbd0
|
||||
|
||||
After this /dev/nbd0 and /dev/nbd0p* can be used to access the full storage
|
||||
device, as well as the individual partitions on the device.
|
||||
|
||||
To disconnect the nbd client, unmount any partitions and run:
|
||||
sudo nbd-client -d /dev/nbd0
|
||||
|
||||
Building
|
||||
========
|
||||
In order to build the project you need libxml2 and libusb-1.0 headers
|
||||
and libraries, found in e.g. the libxml2-dev and libusb-1.0.0-dev packages
|
||||
In order to build the project you need libxml2 headers and libraries, found in
|
||||
e.g. the libxml2-dev package.
|
||||
|
||||
With these installed run:
|
||||
With this installed run:
|
||||
make
|
||||
|
||||
or to build the nbdkit plugin run:
|
||||
make lib
|
||||
|
||||
536
firehose.c
536
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
|
||||
117
ks.c
117
ks.c
@@ -1,117 +0,0 @@
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "qdl.h"
|
||||
|
||||
static struct qdl_device qdl;
|
||||
|
||||
bool qdl_debug;
|
||||
|
||||
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
|
||||
{
|
||||
return read(qdl->fd, buf, len);
|
||||
}
|
||||
|
||||
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len)
|
||||
{
|
||||
|
||||
return write(qdl->fd, buf, len);
|
||||
}
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
extern const char *__progname;
|
||||
fprintf(stderr,
|
||||
"%s -p <sahara dev_node> -s <id:file path> ...\n",
|
||||
__progname);
|
||||
fprintf(stderr,
|
||||
" -p --port Sahara device node to use\n"
|
||||
" -s <id:file path> --sahara <id:file path> Sahara protocol file mapping\n"
|
||||
"\n"
|
||||
"One -p instance is required. One or more -s instances are required.\n"
|
||||
"\n"
|
||||
"Example: \n"
|
||||
"ks -p /dev/mhi0_QAIC_SAHARA -s 1:/opt/qti-aic/firmware/fw1.bin -s 2:/opt/qti-aic/firmware/fw2.bin\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool found_mapping = false;
|
||||
char *dev_node = NULL;
|
||||
long file_id;
|
||||
char *colon;
|
||||
int opt;
|
||||
int ret;
|
||||
|
||||
static struct option options[] = {
|
||||
{"port", required_argument, 0, 'p'},
|
||||
{"sahara", required_argument, 0, 's'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "p:s:", options, NULL )) != -1) {
|
||||
switch (opt) {
|
||||
case 'p':
|
||||
dev_node = optarg;
|
||||
printf("Using port - %s\n", dev_node);
|
||||
break;
|
||||
case 's':
|
||||
found_mapping = true;
|
||||
file_id = strtol(optarg, NULL, 10);
|
||||
if (file_id < 0) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
if (file_id >= MAPPING_SZ) {
|
||||
fprintf(stderr,
|
||||
"ID:%ld exceeds the max value of %d\n",
|
||||
file_id,
|
||||
MAPPING_SZ - 1);
|
||||
return 1;
|
||||
}
|
||||
colon = strchr(optarg, ':');
|
||||
if (!colon) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
qdl.mappings[file_id] = &optarg[colon - optarg + 1];
|
||||
printf("Created mapping ID:%ld File:%s\n", file_id, qdl.mappings[file_id]);
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// -p and -s is required
|
||||
if (!dev_node || !found_mapping) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
qdl.fd = open(dev_node, O_RDWR);
|
||||
if (qdl.fd < 0) {
|
||||
fprintf(stderr, "Unable to open %s\n", dev_node);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = sahara_run(&qdl, qdl.mappings, false, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
146
nbdkit-qdl-plugin.c
Normal file
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)
|
||||
1
patch.c
1
patch.c
@@ -30,7 +30,6 @@
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
|
||||
159
program.c
159
program.c
@@ -31,103 +31,23 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "program.h"
|
||||
#include "qdl.h"
|
||||
|
||||
|
||||
static struct program *programes;
|
||||
static struct program *programes_last;
|
||||
|
||||
static int load_erase_tag(xmlNode *node, bool is_nand)
|
||||
int program_load(const char *program_file)
|
||||
{
|
||||
struct program *program;
|
||||
int errors = 0;
|
||||
|
||||
if (!is_nand) {
|
||||
fprintf(stderr, "got \"erase\" tag for non-NAND storage\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
program = calloc(1, sizeof(struct program));
|
||||
|
||||
|
||||
program->is_nand = true;
|
||||
program->is_erase = true;
|
||||
|
||||
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
|
||||
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
|
||||
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
|
||||
program->start_sector = attr_as_string(node, "start_sector", &errors);
|
||||
|
||||
if (errors) {
|
||||
fprintf(stderr, "[PROGRAM] errors while parsing erase tag\n");
|
||||
free(program);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (programes) {
|
||||
programes_last->next = program;
|
||||
programes_last = program;
|
||||
} else {
|
||||
programes = program;
|
||||
programes_last = program;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_program_tag(xmlNode *node, bool is_nand)
|
||||
{
|
||||
struct program *program;
|
||||
int errors = 0;
|
||||
|
||||
program = calloc(1, sizeof(struct program));
|
||||
|
||||
program->is_nand = is_nand;
|
||||
|
||||
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
|
||||
program->filename = attr_as_string(node, "filename", &errors);
|
||||
program->label = attr_as_string(node, "label", &errors);
|
||||
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
|
||||
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
|
||||
program->start_sector = attr_as_string(node, "start_sector", &errors);
|
||||
|
||||
if (is_nand) {
|
||||
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
|
||||
if (NULL != xmlGetProp(node, (xmlChar *)"last_sector")) {
|
||||
program->last_sector = attr_as_unsigned(node, "last_sector", &errors);
|
||||
}
|
||||
} else {
|
||||
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
|
||||
}
|
||||
|
||||
if (errors) {
|
||||
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
|
||||
free(program);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (programes) {
|
||||
programes_last->next = program;
|
||||
programes_last = program;
|
||||
} else {
|
||||
programes = program;
|
||||
programes_last = program;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int program_load(const char *program_file, bool is_nand)
|
||||
{
|
||||
xmlNode *node;
|
||||
xmlNode *root;
|
||||
xmlDoc *doc;
|
||||
int errors = 0;
|
||||
int errors;
|
||||
|
||||
doc = xmlReadFile(program_file, NULL, 0);
|
||||
if (!doc) {
|
||||
@@ -140,27 +60,45 @@ int program_load(const char *program_file, bool is_nand)
|
||||
if (node->type != XML_ELEMENT_NODE)
|
||||
continue;
|
||||
|
||||
if (!xmlStrcmp(node->name, (xmlChar *)"erase"))
|
||||
errors = load_erase_tag(node, is_nand);
|
||||
else if (!xmlStrcmp(node->name, (xmlChar *)"program"))
|
||||
errors = load_program_tag(node, is_nand);
|
||||
else {
|
||||
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\"\n", node->name);
|
||||
errors = -EINVAL;
|
||||
if (xmlStrcmp(node->name, (xmlChar*)"program")) {
|
||||
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\", ignoring\n", node->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errors)
|
||||
goto out;
|
||||
errors = 0;
|
||||
|
||||
program = calloc(1, sizeof(struct program));
|
||||
|
||||
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
|
||||
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
|
||||
program->filename = attr_as_string(node, "filename", &errors);
|
||||
program->label = attr_as_string(node, "label", &errors);
|
||||
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
|
||||
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
|
||||
program->start_sector = attr_as_string(node, "start_sector", &errors);
|
||||
|
||||
if (errors) {
|
||||
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
|
||||
free(program);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (programes) {
|
||||
programes_last->next = program;
|
||||
programes_last = program;
|
||||
} else {
|
||||
programes = program;
|
||||
programes_last = program;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
return errors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
||||
const char *incdir, bool allow_missing)
|
||||
const char *incdir)
|
||||
{
|
||||
struct program *program;
|
||||
const char *filename;
|
||||
@@ -169,7 +107,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
|
||||
int fd;
|
||||
|
||||
for (program = programes; program; program = program->next) {
|
||||
if (program->is_erase || !program->filename)
|
||||
if (!program->filename)
|
||||
continue;
|
||||
|
||||
filename = program->filename;
|
||||
@@ -182,12 +120,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
|
||||
fd = open(filename, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
printf("Unable to open %s", program->filename);
|
||||
if (!allow_missing) {
|
||||
printf("...failing\n");
|
||||
return -1;
|
||||
}
|
||||
printf("...ignoring\n");
|
||||
printf("Unable to open %s...ignoring\n", program->filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -201,24 +134,6 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program))
|
||||
{
|
||||
struct program *program;
|
||||
int ret;
|
||||
|
||||
|
||||
for (program = programes; program; program = program->next) {
|
||||
if (!program->is_erase)
|
||||
continue;
|
||||
|
||||
ret = apply(qdl, program);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* program_find_bootable_partition() - find one bootable partition
|
||||
*
|
||||
@@ -236,8 +151,6 @@ int program_find_bootable_partition(void)
|
||||
|
||||
for (program = programes; program; program = program->next) {
|
||||
label = program->label;
|
||||
if (!label)
|
||||
continue;
|
||||
|
||||
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
|
||||
!strcmp(label, "sbl1")) {
|
||||
|
||||
10
program.h
10
program.h
@@ -5,7 +5,6 @@
|
||||
#include "qdl.h"
|
||||
|
||||
struct program {
|
||||
unsigned pages_per_block;
|
||||
unsigned sector_size;
|
||||
unsigned file_offset;
|
||||
const char *filename;
|
||||
@@ -13,18 +12,13 @@ struct program {
|
||||
unsigned num_sectors;
|
||||
unsigned partition;
|
||||
const char *start_sector;
|
||||
unsigned last_sector;
|
||||
|
||||
bool is_nand;
|
||||
bool is_erase;
|
||||
|
||||
struct program *next;
|
||||
};
|
||||
|
||||
int program_load(const char *program_file, bool is_nand);
|
||||
int program_load(const char *program_file);
|
||||
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
|
||||
const char *incdir, bool allow_missing);
|
||||
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
|
||||
const char *incdir);
|
||||
int program_find_bootable_partition(void);
|
||||
|
||||
#endif
|
||||
|
||||
79
qdl.c
79
qdl.c
@@ -29,33 +29,43 @@
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <libudev.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "qdl.h"
|
||||
#include "patch.h"
|
||||
#include "ufs.h"
|
||||
|
||||
#define MAX_USBFS_BULK_SIZE (16*1024)
|
||||
#include "usb.h"
|
||||
|
||||
enum {
|
||||
QDL_FILE_UNKNOWN,
|
||||
QDL_FILE_PATCH,
|
||||
QDL_FILE_PROGRAM,
|
||||
QDL_FILE_READ,
|
||||
QDL_FILE_UFS,
|
||||
QDL_FILE_CONTENTS,
|
||||
};
|
||||
|
||||
bool qdl_debug;
|
||||
static struct qdl_device qdl;
|
||||
|
||||
static int detect_type(const char *xml_file)
|
||||
{
|
||||
@@ -81,10 +91,6 @@ static int detect_type(const char *xml_file)
|
||||
type = QDL_FILE_PROGRAM;
|
||||
break;
|
||||
}
|
||||
if (!xmlStrcmp(node->name, (xmlChar*)"read")) {
|
||||
type = QDL_FILE_READ;
|
||||
break;
|
||||
}
|
||||
if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) {
|
||||
type = QDL_FILE_UFS;
|
||||
break;
|
||||
@@ -103,61 +109,44 @@ static void print_usage(void)
|
||||
{
|
||||
extern const char *__progname;
|
||||
fprintf(stderr,
|
||||
"%s [--debug] [--allow-missing] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] [--serial <NUM>] [--out-chunk-size <SIZE>] <prog.mbn> [<program> <patch> ...]\n",
|
||||
"%s [--debug] [--storage <emmc|ufs>] [--finalize-provisioning] [--include <PATH>] <prog.mbn> [<program> <patch> ...]\n",
|
||||
__progname);
|
||||
}
|
||||
|
||||
enum {
|
||||
OPT_OUT_CHUNK_SIZE = 1000,
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct qdl_device *qdl;
|
||||
char *prog_mbn, *storage="ufs";
|
||||
char *incdir = NULL;
|
||||
char *serial = NULL;
|
||||
int bootable;
|
||||
int type;
|
||||
int ret;
|
||||
int opt;
|
||||
bool qdl_finalize_provisioning = false;
|
||||
bool allow_missing = false;
|
||||
long out_chunk_size;
|
||||
|
||||
|
||||
static struct option options[] = {
|
||||
{"debug", no_argument, 0, 'd'},
|
||||
{"include", required_argument, 0, 'i'},
|
||||
{"finalize-provisioning", no_argument, 0, 'l'},
|
||||
{"out-chunk-size", required_argument, 0, OPT_OUT_CHUNK_SIZE },
|
||||
{"serial", required_argument, 0, 'S'},
|
||||
{"storage", required_argument, 0, 's'},
|
||||
{"allow-missing", no_argument, 0, 'f'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "dfi:S:", options, NULL )) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "di:", options, NULL )) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
qdl_debug = true;
|
||||
break;
|
||||
case 'f':
|
||||
allow_missing = true;
|
||||
break;
|
||||
case 'i':
|
||||
incdir = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
qdl_finalize_provisioning = true;
|
||||
break;
|
||||
case OPT_OUT_CHUNK_SIZE:
|
||||
out_chunk_size = strtol(optarg, NULL, 10);
|
||||
qdl_set_out_chunk_size(&qdl, out_chunk_size);
|
||||
break;
|
||||
case 's':
|
||||
storage = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
serial = optarg;
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
return 1;
|
||||
@@ -184,15 +173,10 @@ int main(int argc, char **argv)
|
||||
errx(1, "patch_load %s failed", argv[optind]);
|
||||
break;
|
||||
case QDL_FILE_PROGRAM:
|
||||
ret = program_load(argv[optind], !strcmp(storage, "nand"));
|
||||
ret = program_load(argv[optind]);
|
||||
if (ret < 0)
|
||||
errx(1, "program_load %s failed", argv[optind]);
|
||||
break;
|
||||
case QDL_FILE_READ:
|
||||
ret = read_op_load(argv[optind]);
|
||||
if (ret < 0)
|
||||
errx(1, "read_op_load %s failed", argv[optind]);
|
||||
break;
|
||||
case QDL_FILE_UFS:
|
||||
ret = ufs_load(argv[optind],qdl_finalize_provisioning);
|
||||
if (ret < 0)
|
||||
@@ -204,18 +188,33 @@ int main(int argc, char **argv)
|
||||
}
|
||||
} while (++optind < argc);
|
||||
|
||||
ret = qdl_open(&qdl, serial);
|
||||
qdl = usb_open();
|
||||
if (ret)
|
||||
return 1;
|
||||
|
||||
qdl.mappings[0] = prog_mbn;
|
||||
ret = sahara_run(&qdl, qdl.mappings, true, NULL, NULL);
|
||||
ret = sahara_run(qdl, prog_mbn);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
|
||||
ret = firehose_run(&qdl, incdir, storage, allow_missing);
|
||||
ret = firehose_open(qdl, !strcmp(storage, "ufs"));
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
|
||||
ret = program_execute(qdl, firehose_program, incdir);
|
||||
if (ret)
|
||||
return 1;
|
||||
|
||||
ret = patch_execute(qdl, firehose_apply_patch);
|
||||
if (ret)
|
||||
return 1;
|
||||
|
||||
bootable = program_find_bootable_partition();
|
||||
if (bootable < 0)
|
||||
fprintf(stderr, "no boot partition found\n");
|
||||
else
|
||||
firehose_set_bootable(qdl, bootable);
|
||||
|
||||
firehose_reset(qdl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
43
qdl.h
43
qdl.h
@@ -5,39 +5,30 @@
|
||||
|
||||
#include "patch.h"
|
||||
#include "program.h"
|
||||
#include "read.h"
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#define MAPPING_SZ 64
|
||||
struct qdl_device;
|
||||
struct program;
|
||||
struct patch;
|
||||
|
||||
struct libusb_device_handle;
|
||||
|
||||
struct qdl_device {
|
||||
struct libusb_device_handle *usb_handle;
|
||||
int fd;
|
||||
|
||||
int in_ep;
|
||||
int out_ep;
|
||||
|
||||
size_t in_maxpktsize;
|
||||
size_t out_maxpktsize;
|
||||
size_t out_chunk_size;
|
||||
|
||||
char *mappings[MAPPING_SZ]; // array index is the id from the device
|
||||
};
|
||||
|
||||
int qdl_open(struct qdl_device *qdl, const char *serial);
|
||||
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
|
||||
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len);
|
||||
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size);
|
||||
|
||||
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage, bool allow_missing);
|
||||
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
const char *ramdump_path, const char *ramdump_filter);
|
||||
int firehose_open(struct qdl_device *qdl, bool ufs);
|
||||
int sahara_run(struct qdl_device *qdl, const char *prog_mbn);
|
||||
void print_hex_dump(const char *prefix, const void *buf, size_t len);
|
||||
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
|
||||
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);
|
||||
|
||||
int firehose_getsize(struct qdl_device *qdl, int lun, size_t *sector_size,
|
||||
size_t *num_sectors);
|
||||
int firehose_reset(struct qdl_device *qdl);
|
||||
int firehose_set_bootable(struct qdl_device *qdl, int part);
|
||||
int firehose_program(struct qdl_device *qdl, struct program *program, int fd);
|
||||
int firehose_apply_patch(struct qdl_device *qdl, struct patch *patch);
|
||||
|
||||
ssize_t firehose_pread(struct qdl_device *qdl, int lun, size_t offset, void *buf,
|
||||
size_t sector_size, size_t num_sectors);
|
||||
ssize_t firehose_pwrite(struct qdl_device *qdl, int lun, size_t offset,
|
||||
const void *buf, size_t sector_size, size_t num_sectors);
|
||||
|
||||
extern bool qdl_debug;
|
||||
|
||||
#endif
|
||||
|
||||
66
ramdump.c
66
ramdump.c
@@ -1,66 +0,0 @@
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "qdl.h"
|
||||
|
||||
bool qdl_debug;
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
extern const char *__progname;
|
||||
fprintf(stderr,
|
||||
"%s [--debug] [-o <ramdump-path>] [segment-filter,...]\n",
|
||||
__progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct qdl_device qdl;
|
||||
char *ramdump_path = ".";
|
||||
char *filter = NULL;
|
||||
char *serial = NULL;
|
||||
int ret;
|
||||
int opt;
|
||||
|
||||
static struct option options[] = {
|
||||
{"debug", no_argument, 0, 'd'},
|
||||
{"output", required_argument, 0, 'o'},
|
||||
{"serial", required_argument, 0, 'S'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "do:S:", options, NULL )) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
qdl_debug = true;
|
||||
break;
|
||||
case 'o':
|
||||
ramdump_path = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
serial = optarg;
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
filter = argv[optind++];
|
||||
|
||||
if (optind != argc)
|
||||
print_usage();
|
||||
|
||||
ret = qdl_open(&qdl, serial);
|
||||
if (ret)
|
||||
return 1;
|
||||
|
||||
ret = sahara_run(&qdl, NULL, true, ramdump_path, filter);
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
131
read.c
131
read.c
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Linaro Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "read.h"
|
||||
#include "qdl.h"
|
||||
|
||||
static struct read_op *read_ops;
|
||||
static struct read_op *read_ops_last;
|
||||
|
||||
int read_op_load(const char *read_op_file)
|
||||
{
|
||||
struct read_op *read_op;
|
||||
xmlNode *node;
|
||||
xmlNode *root;
|
||||
xmlDoc *doc;
|
||||
int errors;
|
||||
|
||||
doc = xmlReadFile(read_op_file, NULL, 0);
|
||||
if (!doc) {
|
||||
fprintf(stderr, "[READ] failed to parse %s\n", read_op_file);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
root = xmlDocGetRootElement(doc);
|
||||
for (node = root->children; node ; node = node->next) {
|
||||
if (node->type != XML_ELEMENT_NODE)
|
||||
continue;
|
||||
|
||||
if (xmlStrcmp(node->name, (xmlChar*)"read")) {
|
||||
fprintf(stderr, "[READ] unrecognized tag \"%s\", ignoring\n", node->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
errors = 0;
|
||||
|
||||
read_op = calloc(1, sizeof(struct read_op));
|
||||
|
||||
read_op->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
|
||||
read_op->filename = attr_as_string(node, "filename", &errors);
|
||||
read_op->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
|
||||
read_op->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
|
||||
read_op->start_sector = attr_as_string(node, "start_sector", &errors);
|
||||
|
||||
if (errors) {
|
||||
fprintf(stderr, "[READ] errors while parsing read\n");
|
||||
free(read_op);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (read_ops) {
|
||||
read_ops_last->next = read_op;
|
||||
read_ops_last = read_op;
|
||||
} else {
|
||||
read_ops = read_op;
|
||||
read_ops_last = read_op;
|
||||
}
|
||||
}
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
|
||||
const char *incdir)
|
||||
{
|
||||
struct read_op *read_op;
|
||||
const char *filename;
|
||||
char tmp[PATH_MAX];
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
for (read_op = read_ops; read_op; read_op = read_op->next) {
|
||||
filename = read_op->filename;
|
||||
if (incdir) {
|
||||
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
|
||||
if (access(tmp, F_OK) != -1)
|
||||
filename = tmp;
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
|
||||
|
||||
if (fd < 0) {
|
||||
printf("Unable to open %s...\n", read_op->filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = apply(qdl, read_op, fd);
|
||||
|
||||
close(fd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
22
read.h
22
read.h
@@ -1,22 +0,0 @@
|
||||
#ifndef __READ_H__
|
||||
#define __READ_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct qdl_device;
|
||||
|
||||
struct read_op {
|
||||
unsigned sector_size;
|
||||
const char *filename;
|
||||
unsigned partition;
|
||||
unsigned num_sectors;
|
||||
const char *start_sector;
|
||||
struct read_op *next;
|
||||
};
|
||||
|
||||
int read_op_load(const char *read_op_file);
|
||||
int read_op_execute(struct qdl_device *qdl,
|
||||
int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
|
||||
const char *incdir);
|
||||
|
||||
#endif
|
||||
330
sahara.c
330
sahara.c
@@ -35,7 +35,6 @@
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <inttypes.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
@@ -46,49 +45,7 @@
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include "qdl.h"
|
||||
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
#define SAHARA_HELLO_CMD 0x1 /* Min protocol version 1.0 */
|
||||
#define SAHARA_HELLO_RESP_CMD 0x2 /* Min protocol version 1.0 */
|
||||
#define SAHARA_READ_DATA_CMD 0x3 /* Min protocol version 1.0 */
|
||||
#define SAHARA_END_OF_IMAGE_CMD 0x4 /* Min protocol version 1.0 */
|
||||
#define SAHARA_DONE_CMD 0x5 /* Min protocol version 1.0 */
|
||||
#define SAHARA_DONE_RESP_CMD 0x6 /* Min protocol version 1.0 */
|
||||
#define SAHARA_RESET_CMD 0x7 /* Min protocol version 1.0 */
|
||||
#define SAHARA_RESET_RESP_CMD 0x8 /* Min protocol version 1.0 */
|
||||
#define SAHARA_MEM_DEBUG_CMD 0x9 /* Min protocol version 2.0 */
|
||||
#define SAHARA_MEM_READ_CMD 0xa /* Min protocol version 2.0 */
|
||||
#define SAHARA_CMD_READY_CMD 0xb /* Min protocol version 2.1 */
|
||||
#define SAHARA_SWITCH_MODE_CMD 0xc /* Min protocol version 2.1 */
|
||||
#define SAHARA_EXECUTE_CMD 0xd /* Min protocol version 2.1 */
|
||||
#define SAHARA_EXECUTE_RESP_CMD 0xe /* Min protocol version 2.1 */
|
||||
#define SAHARA_EXECUTE_DATA_CMD 0xf /* Min protocol version 2.1 */
|
||||
#define SAHARA_MEM_DEBUG64_CMD 0x10 /* Min protocol version 2.5 */
|
||||
#define SAHARA_MEM_READ64_CMD 0x11 /* Min protocol version 2.5 */
|
||||
#define SAHARA_READ_DATA64_CMD 0x12 /* Min protocol version 2.8 */
|
||||
#define SAHARA_RESET_STATE_CMD 0x13 /* Min protocol version 2.9 */
|
||||
#define SAHARA_WRITE_DATA_CMD 0x14 /* Min protocol version 3.0 */
|
||||
|
||||
#define SAHARA_VERSION 2
|
||||
#define SAHARA_SUCCESS 0
|
||||
|
||||
#define SAHARA_MODE_IMAGE_TX_PENDING 0x0
|
||||
#define SAHARA_MODE_IMAGE_TX_COMPLETE 0x1
|
||||
#define SAHARA_MODE_MEMORY_DEBUG 0x2
|
||||
#define SAHARA_MODE_COMMAND 0x3
|
||||
|
||||
#define SAHARA_HELLO_LENGTH 0x30
|
||||
#define SAHARA_READ_DATA_LENGTH 0x14
|
||||
#define SAHARA_READ_DATA64_LENGTH 0x20
|
||||
#define SAHARA_END_OF_IMAGE_LENGTH 0x10
|
||||
#define SAHARA_MEM_READ64_LENGTH 0x18
|
||||
#define SAHARA_MEM_DEBUG64_LENGTH 0x18
|
||||
#define SAHARA_DONE_LENGTH 0x8
|
||||
#define SAHARA_DONE_RESP_LENGTH 0xc
|
||||
#define SAHARA_RESET_LENGTH 0x8
|
||||
|
||||
#define DEBUG_BLOCK_SIZE (512*1024)
|
||||
#include "usb.h"
|
||||
|
||||
struct sahara_pkt {
|
||||
uint32_t cmd;
|
||||
@@ -121,10 +78,6 @@ struct sahara_pkt {
|
||||
struct {
|
||||
uint32_t status;
|
||||
} done_resp;
|
||||
struct {
|
||||
uint64_t addr;
|
||||
uint64_t length;
|
||||
} debug64_req;
|
||||
struct {
|
||||
uint64_t image;
|
||||
uint64_t offset;
|
||||
@@ -133,49 +86,36 @@ struct sahara_pkt {
|
||||
};
|
||||
};
|
||||
|
||||
struct sahara_debug_region64 {
|
||||
uint64_t type;
|
||||
uint64_t addr;
|
||||
uint64_t length;
|
||||
char region[20];
|
||||
char filename[20];
|
||||
};
|
||||
|
||||
static void sahara_send_reset(struct qdl_device *qdl)
|
||||
{
|
||||
struct sahara_pkt resp;
|
||||
|
||||
resp.cmd = SAHARA_RESET_CMD;
|
||||
resp.length = SAHARA_RESET_LENGTH;
|
||||
|
||||
qdl_write(qdl, &resp, resp.length);
|
||||
}
|
||||
|
||||
static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
{
|
||||
struct sahara_pkt resp;
|
||||
|
||||
assert(pkt->length == SAHARA_HELLO_LENGTH);
|
||||
assert(pkt->length == 0x30);
|
||||
|
||||
printf("HELLO version: 0x%x compatible: 0x%x max_len: %d mode: %d\n",
|
||||
pkt->hello_req.version, pkt->hello_req.compatible, pkt->hello_req.max_len, pkt->hello_req.mode);
|
||||
|
||||
resp.cmd = SAHARA_HELLO_RESP_CMD;
|
||||
resp.length = SAHARA_HELLO_LENGTH;
|
||||
resp.hello_resp.version = SAHARA_VERSION;
|
||||
resp.cmd = 2;
|
||||
resp.length = 0x30;
|
||||
resp.hello_resp.version = 2;
|
||||
resp.hello_resp.compatible = 1;
|
||||
resp.hello_resp.status = SAHARA_SUCCESS;
|
||||
resp.hello_resp.status = 0;
|
||||
resp.hello_resp.mode = pkt->hello_req.mode;
|
||||
|
||||
qdl_write(qdl, &resp, resp.length);
|
||||
qdl_write(qdl, &resp, resp.length, true);
|
||||
}
|
||||
|
||||
static int sahara_read_common(struct qdl_device *qdl, int progfd, off_t offset, size_t len)
|
||||
static int sahara_read_common(struct qdl_device *qdl, const char *mbn, off_t offset, size_t len)
|
||||
{
|
||||
int progfd;
|
||||
ssize_t n;
|
||||
void *buf;
|
||||
int ret = 0;
|
||||
|
||||
progfd = open(mbn, O_RDONLY);
|
||||
if (progfd < 0)
|
||||
return -errno;
|
||||
|
||||
buf = malloc(len);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
@@ -187,96 +127,50 @@ static int sahara_read_common(struct qdl_device *qdl, int progfd, off_t offset,
|
||||
goto out;
|
||||
}
|
||||
|
||||
n = qdl_write(qdl, buf, n);
|
||||
n = qdl_write(qdl, buf, n, true);
|
||||
if (n != len)
|
||||
err(1, "failed to write %zu bytes to sahara", len);
|
||||
|
||||
free(buf);
|
||||
close(progfd);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
|
||||
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
|
||||
{
|
||||
unsigned int image;
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
assert(pkt->length == SAHARA_READ_DATA_LENGTH);
|
||||
assert(pkt->length == 0x14);
|
||||
|
||||
printf("READ image: %d offset: 0x%x length: 0x%x\n",
|
||||
pkt->read_req.image, pkt->read_req.offset, pkt->read_req.length);
|
||||
|
||||
if (single_image)
|
||||
image = 0;
|
||||
else
|
||||
image = pkt->read_req.image;
|
||||
|
||||
if (image >= MAPPING_SZ || !img_arr[image]) {
|
||||
fprintf(stderr, "Device specified invalid image: %u\n", image);
|
||||
sahara_send_reset(qdl);
|
||||
return;
|
||||
}
|
||||
|
||||
fd = open(img_arr[image], O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Can not open %s: %s\n", img_arr[image], strerror(errno));
|
||||
// Maybe this read was optional. Notify device of error and let
|
||||
// it decide how to proceed.
|
||||
sahara_send_reset(qdl);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = sahara_read_common(qdl, fd, pkt->read_req.offset, pkt->read_req.length);
|
||||
ret = sahara_read_common(qdl, mbn, pkt->read_req.offset, pkt->read_req.length);
|
||||
if (ret < 0)
|
||||
errx(1, "failed to read image chunk to sahara");
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
|
||||
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
|
||||
{
|
||||
unsigned int image;
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
assert(pkt->length == SAHARA_READ_DATA64_LENGTH);
|
||||
assert(pkt->length == 0x20);
|
||||
|
||||
printf("READ64 image: %" PRId64 " offset: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
|
||||
pkt->read64_req.image, pkt->read64_req.offset, pkt->read64_req.length);
|
||||
|
||||
if (single_image)
|
||||
image = 0;
|
||||
else
|
||||
image = pkt->read64_req.image;
|
||||
|
||||
if (image >= MAPPING_SZ || !img_arr[image]) {
|
||||
fprintf(stderr, "Device specified invalid image: %u\n", image);
|
||||
sahara_send_reset(qdl);
|
||||
return;
|
||||
}
|
||||
fd = open(img_arr[image], O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Can not open %s: %s\n", img_arr[image], strerror(errno));
|
||||
// Maybe this read was optional. Notify device of error and let
|
||||
// it decide how to proceed.
|
||||
sahara_send_reset(qdl);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = sahara_read_common(qdl, fd, pkt->read64_req.offset, pkt->read64_req.length);
|
||||
ret = sahara_read_common(qdl, mbn, pkt->read64_req.offset, pkt->read64_req.length);
|
||||
if (ret < 0)
|
||||
errx(1, "failed to read image chunk to sahara");
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
{
|
||||
struct sahara_pkt done;
|
||||
|
||||
assert(pkt->length == SAHARA_END_OF_IMAGE_LENGTH);
|
||||
assert(pkt->length == 0x10);
|
||||
|
||||
printf("END OF IMAGE image: %d status: %d\n", pkt->eoi.image, pkt->eoi.status);
|
||||
|
||||
@@ -285,165 +179,28 @@ static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
return;
|
||||
}
|
||||
|
||||
done.cmd = SAHARA_DONE_CMD;
|
||||
done.length = SAHARA_DONE_LENGTH;
|
||||
qdl_write(qdl, &done, done.length);
|
||||
done.cmd = 5;
|
||||
done.length = 0x8;
|
||||
qdl_write(qdl, &done, done.length, true);
|
||||
}
|
||||
|
||||
static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
|
||||
{
|
||||
assert(pkt->length == SAHARA_DONE_RESP_LENGTH);
|
||||
assert(pkt->length == 0xc);
|
||||
|
||||
printf("DONE status: %d\n", pkt->done_resp.status);
|
||||
|
||||
// 0 == PENDING, 1 == COMPLETE. Device expects more images if
|
||||
// PENDING is set in status.
|
||||
return pkt->done_resp.status;
|
||||
}
|
||||
|
||||
static ssize_t sahara_debug64_one(struct qdl_device *qdl,
|
||||
struct sahara_debug_region64 region,
|
||||
int ramdump_dir)
|
||||
{
|
||||
struct sahara_pkt read_req;
|
||||
uint64_t remain;
|
||||
size_t offset;
|
||||
size_t chunk;
|
||||
ssize_t n;
|
||||
void *buf;
|
||||
int fd;
|
||||
|
||||
buf = malloc(DEBUG_BLOCK_SIZE);
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
fd = openat(ramdump_dir, region.filename, O_WRONLY | O_CREAT, 0644);
|
||||
if (fd < 0) {
|
||||
warn("failed to open \"%s\"", region.filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
chunk = 0;
|
||||
while (chunk < region.length) {
|
||||
remain = MIN(region.length - chunk, DEBUG_BLOCK_SIZE);
|
||||
|
||||
read_req.cmd = SAHARA_MEM_READ64_CMD;
|
||||
read_req.length = SAHARA_MEM_READ64_LENGTH;
|
||||
read_req.debug64_req.addr = region.addr + chunk;
|
||||
read_req.debug64_req.length = remain;
|
||||
n = qdl_write(qdl, &read_req, read_req.length);
|
||||
if (n < 0)
|
||||
break;
|
||||
|
||||
offset = 0;
|
||||
while (offset < remain) {
|
||||
n = qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 30000);
|
||||
if (n < 0) {
|
||||
warn("failed to read ramdump chunk");
|
||||
goto out;
|
||||
}
|
||||
|
||||
write(fd, buf, n);
|
||||
offset += n;
|
||||
}
|
||||
|
||||
qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 10);
|
||||
|
||||
chunk += DEBUG_BLOCK_SIZE;
|
||||
}
|
||||
out:
|
||||
|
||||
close(fd);
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sahara_debug64_filter(const char *filename, const char *filter)
|
||||
{
|
||||
bool anymatch = false;
|
||||
char *ptr;
|
||||
char *tmp;
|
||||
char *s;
|
||||
|
||||
if (!filter)
|
||||
return false;
|
||||
|
||||
tmp = strdup(filter);
|
||||
for (s = strtok_r(tmp, ",", &ptr); s; s = strtok_r(NULL, ",", &ptr)) {
|
||||
if (fnmatch(s, filename, 0) == 0) {
|
||||
anymatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
return !anymatch;
|
||||
}
|
||||
|
||||
static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
|
||||
int ramdump_dir, const char *filter)
|
||||
{
|
||||
struct sahara_debug_region64 *table;
|
||||
struct sahara_pkt read_req;
|
||||
ssize_t n;
|
||||
int i;
|
||||
|
||||
assert(pkt->length == SAHARA_MEM_DEBUG64_LENGTH);
|
||||
|
||||
printf("DEBUG64 address: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
|
||||
pkt->debug64_req.addr, pkt->debug64_req.length);
|
||||
|
||||
read_req.cmd = SAHARA_MEM_READ64_CMD;
|
||||
read_req.length = SAHARA_MEM_READ64_LENGTH;
|
||||
read_req.debug64_req.addr = pkt->debug64_req.addr;
|
||||
read_req.debug64_req.length = pkt->debug64_req.length;
|
||||
|
||||
n = qdl_write(qdl, &read_req, read_req.length);
|
||||
if (n < 0)
|
||||
return;
|
||||
|
||||
table = malloc(read_req.debug64_req.length);
|
||||
|
||||
n = qdl_read(qdl, table, pkt->debug64_req.length, 1000);
|
||||
if (n < 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < pkt->debug64_req.length / sizeof(table[0]); i++) {
|
||||
if (sahara_debug64_filter(table[i].filename, filter))
|
||||
continue;
|
||||
|
||||
printf("%-2d: type 0x%" PRIx64 " address: 0x%" PRIx64 " length: 0x%" PRIx64 " region: %s filename: %s\n",
|
||||
i, table[i].type, table[i].addr, table[i].length, table[i].region, table[i].filename);
|
||||
|
||||
|
||||
n = sahara_debug64_one(qdl, table[i], ramdump_dir);
|
||||
if (n < 0)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
free(table);
|
||||
|
||||
sahara_send_reset(qdl);
|
||||
}
|
||||
|
||||
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
const char *ramdump_path, const char *ramdump_filter)
|
||||
int sahara_run(struct qdl_device *qdl, const char *prog_mbn)
|
||||
{
|
||||
struct sahara_pkt *pkt;
|
||||
int ramdump_dir = -1;
|
||||
char buf[4096];
|
||||
char tmp[32];
|
||||
bool done = false;
|
||||
int n;
|
||||
|
||||
if (ramdump_path) {
|
||||
ramdump_dir = open(ramdump_path, O_DIRECTORY);
|
||||
if (ramdump_dir < 0)
|
||||
err(1, "failed to open directory for ramdump output");
|
||||
}
|
||||
|
||||
while (!done) {
|
||||
n = qdl_read(qdl, buf, sizeof(buf), 1000);
|
||||
if (n < 0)
|
||||
@@ -451,37 +208,26 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
|
||||
pkt = (struct sahara_pkt*)buf;
|
||||
if (n != pkt->length) {
|
||||
fprintf(stderr, "length not matching\n");
|
||||
fprintf(stderr, "length not matching");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (pkt->cmd) {
|
||||
case SAHARA_HELLO_CMD:
|
||||
case 1:
|
||||
sahara_hello(qdl, pkt);
|
||||
break;
|
||||
case SAHARA_READ_DATA_CMD:
|
||||
sahara_read(qdl, pkt, img_arr, single_image);
|
||||
case 3:
|
||||
sahara_read(qdl, pkt, prog_mbn);
|
||||
break;
|
||||
case SAHARA_END_OF_IMAGE_CMD:
|
||||
case 4:
|
||||
sahara_eoi(qdl, pkt);
|
||||
break;
|
||||
case SAHARA_DONE_RESP_CMD:
|
||||
done = sahara_done(qdl, pkt);
|
||||
|
||||
/* E.g MSM8916 EDL reports done = 0 here */
|
||||
if (single_image)
|
||||
done = true;
|
||||
case 6:
|
||||
sahara_done(qdl, pkt);
|
||||
done = true;
|
||||
break;
|
||||
case SAHARA_MEM_DEBUG64_CMD:
|
||||
sahara_debug64(qdl, pkt, ramdump_dir, ramdump_filter);
|
||||
break;
|
||||
case SAHARA_READ_DATA64_CMD:
|
||||
sahara_read64(qdl, pkt, img_arr, single_image);
|
||||
break;
|
||||
case SAHARA_RESET_RESP_CMD:
|
||||
assert(pkt->length == SAHARA_RESET_LENGTH);
|
||||
if (ramdump_path)
|
||||
done = true;
|
||||
case 0x12:
|
||||
sahara_read64(qdl, pkt, prog_mbn);
|
||||
break;
|
||||
default:
|
||||
sprintf(tmp, "CMD%x", pkt->cmd);
|
||||
@@ -490,7 +236,5 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
|
||||
}
|
||||
}
|
||||
|
||||
close(ramdump_dir);
|
||||
|
||||
return done ? 0 : -1;
|
||||
}
|
||||
|
||||
7
ufs.c
7
ufs.c
@@ -86,13 +86,6 @@ struct ufs_common *ufs_parse_common_params(xmlNode *node, bool finalize_provisio
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* These parameters are optional */
|
||||
errors = 0;
|
||||
result->bWriteBoosterBufferPreserveUserSpaceEn = !!attr_as_unsigned(node, "bWriteBoosterBufferPreserveUserSpaceEn", &errors);
|
||||
result->bWriteBoosterBufferType = !!attr_as_unsigned(node, "bWriteBoosterBufferType", &errors);
|
||||
result->shared_wb_buffer_size_in_kb = attr_as_unsigned(node, "shared_wb_buffer_size_in_kb", &errors);
|
||||
result->wb = !errors;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user