mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
add support sparse attribute
Signed-off-by: Maxim Paymushkin <maxim.paymushkin.development@gmail.com>
This commit is contained in:
committed by
Maxim Paymushkin
parent
4288cebfdb
commit
02c008adfd
5
Makefile
5
Makefile
@@ -4,9 +4,12 @@ VERSION := $(or $(VERSION), $(shell git describe --dirty --always --tags 2>/dev/
|
||||
|
||||
CFLAGS += -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0`
|
||||
LDFLAGS += `pkg-config --libs libxml-2.0 libusb-1.0`
|
||||
ifeq ($(OS),Windows_NT)
|
||||
LDFLAGS += -lws2_32
|
||||
endif
|
||||
prefix := /usr/local
|
||||
|
||||
QDL_SRCS := firehose.c io.c qdl.c sahara.c util.c patch.c program.c read.c sha2.c sim.c ufs.c usb.c ux.c oscompat.c vip.c
|
||||
QDL_SRCS := firehose.c io.c qdl.c sahara.c util.c patch.c program.c read.c sha2.c sim.c ufs.c usb.c ux.c oscompat.c vip.c sparse.c
|
||||
QDL_OBJS := $(QDL_SRCS:.c=.o)
|
||||
|
||||
RAMDUMP_SRCS := ramdump.c sahara.c io.c sim.c usb.c util.c ux.c oscompat.c
|
||||
|
||||
53
firehose.c
53
firehose.c
@@ -26,6 +26,7 @@
|
||||
#include "ufs.h"
|
||||
#include "oscompat.h"
|
||||
#include "vip.h"
|
||||
#include "sparse.h"
|
||||
|
||||
enum {
|
||||
FIREHOSE_ACK = 0,
|
||||
@@ -366,6 +367,7 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
|
||||
int left;
|
||||
int ret;
|
||||
int n;
|
||||
uint32_t fill_value;
|
||||
|
||||
num_sectors = program->num_sectors;
|
||||
|
||||
@@ -373,14 +375,16 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
|
||||
if (ret < 0)
|
||||
err(1, "failed to stat \"%s\"\n", program->filename);
|
||||
|
||||
num_sectors = (sb.st_size + program->sector_size - 1) / program->sector_size;
|
||||
if (!program->sparse) {
|
||||
num_sectors = (sb.st_size + program->sector_size - 1) / program->sector_size;
|
||||
|
||||
if (program->num_sectors && num_sectors > program->num_sectors) {
|
||||
ux_err("%s to big for %s truncated to %d\n",
|
||||
program->filename,
|
||||
program->label,
|
||||
program->num_sectors * program->sector_size);
|
||||
num_sectors = program->num_sectors;
|
||||
if (program->num_sectors && num_sectors > program->num_sectors) {
|
||||
ux_err("%s to big for %s truncated to %d\n",
|
||||
program->filename,
|
||||
program->label,
|
||||
program->num_sectors * program->sector_size);
|
||||
num_sectors = program->num_sectors;
|
||||
}
|
||||
}
|
||||
|
||||
buf = malloc(qdl->max_payload_size);
|
||||
@@ -418,7 +422,24 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
|
||||
|
||||
t0 = time(NULL);
|
||||
|
||||
lseek(fd, (off_t)program->file_offset * program->sector_size, SEEK_SET);
|
||||
if (!program->sparse) {
|
||||
lseek(fd, (off_t)program->file_offset * program->sector_size, SEEK_SET);
|
||||
} else {
|
||||
switch (program->sparse_chunk_type) {
|
||||
case CHUNK_TYPE_RAW:
|
||||
lseek(fd, (off_t)program->sparse_chunk_data, SEEK_SET);
|
||||
break;
|
||||
case CHUNK_TYPE_FILL:
|
||||
fill_value = (uint32_t)program->sparse_chunk_data;
|
||||
for (n = 0; n < qdl->max_payload_size; n += sizeof(fill_value))
|
||||
memcpy(buf + n, &fill_value, sizeof(fill_value));
|
||||
break;
|
||||
default:
|
||||
ux_err("[SPARSE] invalid chunk type\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
left = num_sectors;
|
||||
|
||||
ux_debug("FIREHOSE RAW BINARY WRITE: %s, %d bytes\n",
|
||||
@@ -432,14 +453,16 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int
|
||||
vip_gen_chunk_init(qdl);
|
||||
chunk_size = MIN(qdl->max_payload_size / program->sector_size, left);
|
||||
|
||||
n = read(fd, buf, chunk_size * program->sector_size);
|
||||
if (n < 0) {
|
||||
ux_err("failed to read %s\n", program->filename);
|
||||
goto out;
|
||||
}
|
||||
if (!program->sparse || program->sparse_chunk_type != CHUNK_TYPE_FILL) {
|
||||
n = read(fd, buf, chunk_size * program->sector_size);
|
||||
if (n < 0) {
|
||||
ux_err("failed to read %s\n", program->filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (n < qdl->max_payload_size)
|
||||
memset(buf + n, 0, qdl->max_payload_size - n);
|
||||
if (n < qdl->max_payload_size)
|
||||
memset(buf + n, 0, qdl->max_payload_size - n);
|
||||
}
|
||||
|
||||
vip_gen_chunk_update(qdl, buf, chunk_size * program->sector_size);
|
||||
|
||||
|
||||
109
program.c
109
program.c
@@ -15,6 +15,7 @@
|
||||
#include "program.h"
|
||||
#include "qdl.h"
|
||||
#include "oscompat.h"
|
||||
#include "sparse.h"
|
||||
|
||||
static struct program *programes;
|
||||
static struct program *programes_last;
|
||||
@@ -54,6 +55,97 @@ static int load_erase_tag(xmlNode *node, bool is_nand)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct program *program_load_sparse(struct program *program, int fd)
|
||||
{
|
||||
struct program *program_sparse = NULL;
|
||||
struct program *programes_sparse = NULL;
|
||||
struct program *programes_sparse_last = NULL;
|
||||
char tmp[PATH_MAX];
|
||||
|
||||
sparse_header_t sparse_header;
|
||||
unsigned int start_sector, chunk_size, chunk_type, chunk_data;
|
||||
|
||||
if (sparse_header_parse(fd, &sparse_header)) {
|
||||
/*
|
||||
* If the XML tag "program" contains the attribute 'sparse="true"'
|
||||
* for a partition node but lacks a sparse header,
|
||||
* it will be validated against the defined partition size.
|
||||
* If the sizes match, it is likely that the 'sparse="true"' attribute
|
||||
* was set by mistake.
|
||||
*/
|
||||
if ((off_t)program->sector_size * program->num_sectors ==
|
||||
lseek(fd, 0, SEEK_END)) {
|
||||
program_sparse = calloc(1, sizeof(struct program));
|
||||
memcpy(program_sparse, program, sizeof(struct program));
|
||||
program_sparse->sparse = false;
|
||||
program_sparse->next = NULL;
|
||||
return program_sparse;
|
||||
}
|
||||
|
||||
ux_err("[PROGRAM] Unable to parse sparse header at %s...failed\n",
|
||||
program->filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < sparse_header.total_chunks; ++i) {
|
||||
chunk_type = sparse_chunk_header_parse(fd, &sparse_header,
|
||||
&chunk_size, &chunk_data);
|
||||
|
||||
switch (chunk_type) {
|
||||
case CHUNK_TYPE_RAW:
|
||||
case CHUNK_TYPE_FILL:
|
||||
case CHUNK_TYPE_DONT_CARE:
|
||||
break;
|
||||
default:
|
||||
ux_err("[PROGRAM] Unable to parse sparse chunk %i at %s...failed\n",
|
||||
i, program->filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (chunk_size == 0)
|
||||
continue;
|
||||
|
||||
if (chunk_size % program->sector_size != 0) {
|
||||
ux_err("[SPARSE] File chunk #%u size %u is not a sector-multiple\n",
|
||||
i, chunk_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (chunk_type) {
|
||||
case CHUNK_TYPE_RAW:
|
||||
case CHUNK_TYPE_FILL:
|
||||
|
||||
program_sparse = calloc(1, sizeof(struct program));
|
||||
memcpy(program_sparse, program, sizeof(struct program));
|
||||
|
||||
program_sparse->next = NULL;
|
||||
program_sparse->num_sectors = chunk_size / program->sector_size;
|
||||
|
||||
program_sparse->sparse_chunk_type = chunk_type;
|
||||
program_sparse->sparse_chunk_data = chunk_data;
|
||||
|
||||
if (programes_sparse) {
|
||||
programes_sparse_last->next = program_sparse;
|
||||
programes_sparse_last = program_sparse;
|
||||
} else {
|
||||
programes_sparse = program_sparse;
|
||||
programes_sparse_last = program_sparse;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
start_sector = (unsigned int)strtoul(program->start_sector, NULL, 0);
|
||||
start_sector += chunk_size / program->sector_size;
|
||||
sprintf(tmp, "%u", start_sector);
|
||||
program->start_sector = strdup(tmp);
|
||||
}
|
||||
|
||||
return programes_sparse;
|
||||
}
|
||||
|
||||
static int load_program_tag(xmlNode *node, bool is_nand)
|
||||
{
|
||||
struct program *program;
|
||||
@@ -138,6 +230,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
|
||||
const char *incdir, bool allow_missing)
|
||||
{
|
||||
struct program *program;
|
||||
struct program *program_sparse;
|
||||
const char *filename;
|
||||
char tmp[PATH_MAX];
|
||||
int ret;
|
||||
@@ -166,7 +259,21 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = apply(qdl, program, fd);
|
||||
if (!program->sparse) {
|
||||
ret = apply(qdl, program, fd);
|
||||
} else {
|
||||
program_sparse = program_load_sparse(program, fd);
|
||||
if (!program_sparse) {
|
||||
ux_err("[PROGRAM] load sparse failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (; program_sparse; program_sparse = program_sparse->next) {
|
||||
ret = apply(qdl, program_sparse, fd);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
if (ret)
|
||||
|
||||
@@ -20,6 +20,9 @@ struct program {
|
||||
bool is_nand;
|
||||
bool is_erase;
|
||||
|
||||
unsigned int sparse_chunk_type;
|
||||
unsigned int sparse_chunk_data;
|
||||
|
||||
struct program *next;
|
||||
};
|
||||
|
||||
|
||||
118
sparse.c
Normal file
118
sparse.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (c) 2025, Maksim Paimushkin <maxim.paymushkin.development@gmail.com>
|
||||
* All rights reserved.
|
||||
*/
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sparse.h"
|
||||
#include "qdl.h"
|
||||
|
||||
int sparse_header_parse(int fd, sparse_header_t *sparse_header)
|
||||
{
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
if (read(fd, sparse_header, sizeof(sparse_header_t)) != sizeof(sparse_header_t)) {
|
||||
ux_err("[SPARSE] Unable to read sparse header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ntohl(sparse_header->magic) != ntohl(SPARSE_HEADER_MAGIC)) {
|
||||
ux_err("[SPARSE] Invalid magic in sparse header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ntohs(sparse_header->major_version) != ntohs(SPARSE_HEADER_MAJOR_VER)) {
|
||||
ux_err("[SPARSE] Invalid major version in sparse header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ntohs(sparse_header->minor_version) != ntohs(SPARSE_HEADER_MINOR_VER)) {
|
||||
ux_err("[SPARSE] Invalid minor version in sparse header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sparse_header->file_hdr_sz > sizeof(sparse_header_t))
|
||||
lseek(fd, sparse_header->file_hdr_sz - sizeof(sparse_header_t), SEEK_CUR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sparse_chunk_header_parse(int fd, sparse_header_t *sparse_header,
|
||||
unsigned int *chunk_size, unsigned int *value)
|
||||
{
|
||||
chunk_header_t chunk_header;
|
||||
uint32_t fill_value = 0;
|
||||
|
||||
*chunk_size = 0;
|
||||
*value = 0;
|
||||
|
||||
if (read(fd, &chunk_header, sizeof(chunk_header_t)) != sizeof(chunk_header_t)) {
|
||||
ux_err("[SPARSE] Unable to read sparse chunk header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sparse_header->chunk_hdr_sz > sizeof(chunk_header_t))
|
||||
lseek(fd, sparse_header->chunk_hdr_sz - sizeof(chunk_header_t), SEEK_CUR);
|
||||
|
||||
if (ntohs(chunk_header.chunk_type) == ntohs(CHUNK_TYPE_RAW)) {
|
||||
*chunk_size = chunk_header.chunk_sz * sparse_header->blk_sz;
|
||||
|
||||
if (chunk_header.total_sz != (sparse_header->chunk_hdr_sz + *chunk_size)) {
|
||||
ux_err("[SPARSE] Bogus chunk size, type Raw\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Save the current file offset in the 'value' variable */
|
||||
*value = lseek(fd, 0, SEEK_CUR);
|
||||
|
||||
/* Move the file cursor forward by the size of the chunk */
|
||||
lseek(fd, *chunk_size, SEEK_CUR);
|
||||
|
||||
return CHUNK_TYPE_RAW;
|
||||
|
||||
} else if (ntohs(chunk_header.chunk_type) == ntohs(CHUNK_TYPE_DONT_CARE)) {
|
||||
*chunk_size = chunk_header.chunk_sz * sparse_header->blk_sz;
|
||||
|
||||
if (chunk_header.total_sz != sparse_header->chunk_hdr_sz) {
|
||||
ux_err("[SPARSE] Bogus chunk size, type Don't Care\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return CHUNK_TYPE_DONT_CARE;
|
||||
|
||||
} else if (ntohs(chunk_header.chunk_type) == ntohs(CHUNK_TYPE_FILL)) {
|
||||
*chunk_size = chunk_header.chunk_sz * sparse_header->blk_sz;
|
||||
|
||||
if (chunk_header.total_sz != (sparse_header->chunk_hdr_sz + sizeof(fill_value))) {
|
||||
ux_err("[SPARSE] Bogus chunk size, type Fill\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (read(fd, &fill_value, sizeof(fill_value)) != sizeof(fill_value)) {
|
||||
ux_err("[SPARSE] Unable to read fill value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Save the current fill value in the 'value' variable */
|
||||
*value = fill_value;
|
||||
|
||||
return CHUNK_TYPE_FILL;
|
||||
}
|
||||
|
||||
ux_err("[SPARSE] Unknown chunk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
64
sparse.h
Normal file
64
sparse.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*/
|
||||
#ifndef __SPARSE_H__
|
||||
#define __SPARSE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct __attribute__((__packed__)) sparse_header {
|
||||
/* 0xed26ff3a */
|
||||
uint32_t magic;
|
||||
/* (0x1) - reject images with higher major versions */
|
||||
uint16_t major_version;
|
||||
/* (0x0) - allow images with higer minor versions */
|
||||
uint16_t minor_version;
|
||||
/* 28 bytes for first revision of the file format */
|
||||
uint16_t file_hdr_sz;
|
||||
/* 12 bytes for first revision of the file format */
|
||||
uint16_t chunk_hdr_sz;
|
||||
/* block size in bytes, must be a multiple of 4 (4096) */
|
||||
uint32_t blk_sz;
|
||||
/* total blocks in the non-sparse output image */
|
||||
uint32_t total_blks;
|
||||
/* total chunks in the sparse input image */
|
||||
uint32_t total_chunks;
|
||||
/*
|
||||
* CRC32 checksum of the original data, counting "don't care"
|
||||
* as 0. Standard 802.3 polynomial, use a Public Domain
|
||||
* table implementation
|
||||
*/
|
||||
uint32_t image_checksum;
|
||||
} sparse_header_t;
|
||||
|
||||
#define SPARSE_HEADER_MAGIC 0xed26ff3a
|
||||
#define SPARSE_HEADER_MAJOR_VER 0x0001
|
||||
#define SPARSE_HEADER_MINOR_VER 0x0000
|
||||
|
||||
typedef struct __attribute__((__packed__)) chunk_header {
|
||||
uint16_t chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
|
||||
uint16_t reserved1;
|
||||
uint32_t chunk_sz; /* in blocks in output image */
|
||||
uint32_t total_sz; /* in bytes of chunk input file including chunk header and data */
|
||||
} chunk_header_t;
|
||||
|
||||
#define CHUNK_TYPE_RAW 0xCAC1
|
||||
#define CHUNK_TYPE_FILL 0xCAC2
|
||||
#define CHUNK_TYPE_DONT_CARE 0xCAC3
|
||||
|
||||
/*
|
||||
* Parses the sparse image header from the file descriptor.
|
||||
* Returns 0 on success, or an error code otherwise.
|
||||
*/
|
||||
int sparse_header_parse(int fd, sparse_header_t *sparse_header);
|
||||
|
||||
/*
|
||||
* Parses the sparse image chunk header from the file descriptor.
|
||||
* Sets the chunk size and value based on the parsed data.
|
||||
* Returns the chunk type on success, or an error code otherwise.
|
||||
*/
|
||||
int sparse_chunk_header_parse(int fd, sparse_header_t *sparse_header,
|
||||
unsigned int *chunk_size, unsigned int *value);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user