mirror of
https://github.com/Lekensteyn/lglaf.git
synced 2024-11-14 19:35:41 -05:00
400 lines
13 KiB
Markdown
400 lines
13 KiB
Markdown
# LG LAF Protocol
|
|
This document is a reverse-engineered protocol description for LG Advanced Flash
|
|
(LAF), the download mode offered by various LG models. It is based on analysis
|
|
on the `Send_Command.exe` utility and `LGD855_20140526_LGFLASHv160.dll` file and
|
|
a USB trace using Wireshark and usbmon on Linux. Some commands were found in the
|
|
`/sbin/lafd` binary.
|
|
|
|
This document uses the following conventions for types:
|
|
|
|
- `\xaa\xbb\xcc\xdd` denotes a byte pattern `aa bb cc dd`.
|
|
- `0xddccbbaa` denotes a 32-bit integer in hexadecimal format. It represents
|
|
the same byte pattern as `\xaa\xbb\xcc\xdd`.
|
|
|
|
## Overview
|
|
LAF is a simple request/response protocol operating over USB. The USB details
|
|
are described at the end of the document, the messages are described below.
|
|
|
|
Each message consists of a header, followed by an optional body. The header
|
|
contains 32-bit DWORDs, integers are encoded in little-endian form:
|
|
|
|
| Offset (hex) | Offset (dec) | Type | Description
|
|
| ----:| --:| ------- | ---
|
|
| 0x00 | 0 | char[4] | Command
|
|
| 0x04 | 4 | var | Argument 1
|
|
| 0x08 | 8 | var | Argument 2
|
|
| 0x0c | 12 | var | Argument 3
|
|
| 0x10 | 16 | var | Argument 4
|
|
| 0x14 | 20 | int | Body length
|
|
| 0x18 | 24 | int | CRC-16
|
|
| 0x1c | 28 | char[4] | Bit-wise invertion of command at offset 0
|
|
|
|
Arguments can be integers or character sequences depending on the command.
|
|
|
|
The CRC field is the CRC-16-CCITT calculation (LSB-first) over the header and
|
|
the body with zeroes in place of CRC.
|
|
|
|
Each request is followed by a response with a matching command field. If an
|
|
error occurs, the response contains command is `FAIL` with argument 1 being the
|
|
error code and the original request header as body.
|
|
|
|
## Commands
|
|
|
|
### OPEN - Open File
|
|
Opens a file path.
|
|
|
|
Arguments:
|
|
- arg1 (response): DWORD file descriptor.
|
|
|
|
Request body: NUL-terminated file path that should be opened for reading or an
|
|
empty string to open `/dev/block/mmcblk0` in read/write mode.
|
|
(at most 276 (0x114) bytes?)
|
|
|
|
Non-existing files result in FAIL with error code 0x80000001.
|
|
|
|
On newer versions, this requires authentication via `KILO` command.
|
|
|
|
### CLSE - Close File
|
|
Closes a file descriptor which was returned by the `OPEN` command.
|
|
|
|
Arguments:
|
|
- arg1: DWORD file descriptor (same in request and response).
|
|
|
|
Note: this allows you to close any file descriptor that are in use by the `lafd`
|
|
process, not just the one returned by `OPEN`. You can discover the current file
|
|
descriptors via `ls -l /proc/$pid/fd` where `$pid` is found by `ps | grep lafd`.
|
|
|
|
### HELO - Hello
|
|
Arguments:
|
|
- arg1: DWORD Protocol Version (`\1\0\0\1`) (resp must match req.)
|
|
- arg2 (response): Minimum Protocol Version (`\0\0\x80\0` was observed)
|
|
|
|
### CTRL - Control
|
|
Reboot or power off.
|
|
Arguments:
|
|
- arg1: sub-command:
|
|
- `POFF`: power off
|
|
- `RSON`: restart lafd
|
|
- `RSET`: reboot with param `oem-90466252` (normal reboot)
|
|
- `ONRS`: reboot with param `oem-02179092`
|
|
- `AATD`: reboot with param `aat_enter`
|
|
|
|
Note: `CTRL(RSET)` with no body is sent by the `Send_Command.exe` utility for
|
|
the `LEAVE` command.
|
|
|
|
LG Flash DLL waits 5000 milliseconds after this command.
|
|
|
|
Purpose of `ONRS` and `AATD` are unknown. Both seem to reboot normally. Probably
|
|
one is meant to enter fastboot?
|
|
|
|
### WRTE - Write File
|
|
Writes to a file descriptor.
|
|
|
|
Arguments:
|
|
- arg1: file descriptor (must be open for writing!)
|
|
- arg2 (request): offset in **blocks** (multiple of 512 bytes).
|
|
- arg2 (response): offset in **bytes**.
|
|
|
|
Request body: the data to be written. Can be of any size (including 1 or 513).
|
|
|
|
Note: writing to a file descriptor which was opened for reading results in FAIL
|
|
with code 0x82000002. This command is likely used for writing to partitions.
|
|
|
|
Integer overflow in the response offset is ignored. That is, the block offset
|
|
30736384 (0x1d50000) is 0x3aa000000 bytes, but will appear as 0xaa000000.
|
|
|
|
This can be used to write to already-opened files without authentication, but
|
|
lafd doesn't appear to have any files opened for writing by default.
|
|
|
|
### READ - Read File
|
|
Reads from a file descriptor.
|
|
|
|
Arguments:
|
|
- arg1: file descriptor.
|
|
- arg2: offset in **blocks** (multiple of 512 bytes).
|
|
- arg3: requested length in bytes (at most 8MiB).
|
|
- arg4: "whence" seek mode (see below).
|
|
|
|
Response body: data in file at given offset and requested length.
|
|
|
|
Note: be sure not to read past the end of the file (512 * offset + length), this
|
|
will hang the communication, requiring a reset (pull out battery)!
|
|
|
|
Arg4 affects the seek mode, values for request:
|
|
- 0 (`SEEK_SET`) - seek to `512 * offset`.
|
|
- 1 (`SEEK_CUR`) - read from current position (offset argument is ignored).
|
|
- 2 (`SEEK_END`) - kind of useless when all offsets are unsigned...
|
|
- 3 (`SEEK_DATA`) - FAILs with 0x80000001 when used on `/proc/kmsg` or
|
|
`/dev/block/mmcblk0p44`. Works on a regular file though.
|
|
|
|
The response matches the request (masked with 0x3).
|
|
|
|
If the length is larger than somewhere between 227 MiB and 228 MiB, an
|
|
0x80000001 error will be raised (observed with /dev/block/mmcblk0). Requesting
|
|
lengths larger than 8 MiB however already seem to hang the communication. Length
|
|
can be zero to test if a file is readable.
|
|
|
|
This can be used to read already-opened files without authentication, but lafd
|
|
seems to only have PNG files and `/dev/null` open by default. Attempting to read
|
|
`/dev/null` hangs communication.
|
|
|
|
### ERSE - Erase Block
|
|
TRIMs a block (`IOCTL_TRIM_CMD`).
|
|
|
|
Arguments:
|
|
- arg1: file descriptor (open `/dev/block/mmcblk0` for writing).
|
|
- arg2: start address (in sectors).
|
|
- arg3: count (in sectors).
|
|
- arg4: unknown, set to zero.
|
|
|
|
Request body: none.
|
|
|
|
Note: after sending TRIM, reading the block still returned old values. After a
|
|
reboot, everything was zeroed out though.
|
|
|
|
### EXEC - Execute Command
|
|
Arguments: none
|
|
|
|
Request body: NUL-terminated command, at most 255 bytes including terminator.
|
|
|
|
Response body: standard output of the command.
|
|
|
|
The command is split on spaces and then passed to `execvp`. In order to see
|
|
standard error, use variables and globbing, use a command such as:
|
|
|
|
sh -c eval\t"$*"</dev/null\t2>&1 -- echo $PATH
|
|
|
|
(replace `\t` by tabs)
|
|
|
|
If you need to read dmesg (or other blocking files), try to put busybox on the
|
|
device (e.g. by writing to an unused partition) and execute:
|
|
|
|
/data/busybox timeout -s 2 cat /proc/kmsg
|
|
|
|
The maximum output size appears to be 0x800001 (`LAF_MAX_DATA_PAYLOAD`). Larger
|
|
values result in an error. Output is read per byte, not very efficient for large
|
|
output...
|
|
|
|
On newer versions, this requires authentication via `KILO` command, and few
|
|
commands are allowed.
|
|
|
|
### INFO
|
|
Arguments:
|
|
- arg1: action (`GPRO` - Get Properties, `SPRO` - Set Properties)
|
|
|
|
Request body: a `laf_property` structure.
|
|
|
|
Response body: 2824 (0x00000b08) bytes of binary info.
|
|
|
|
See [scripts/parse-props.py](scripts/parse-props.py) for the structure of the
|
|
property body. This structure begins with a DWORD with a version that is
|
|
apparently the same as the expected length (2824 or `\x08\x0b\0\0`).
|
|
|
|
### UNLK - Unlink
|
|
Delete a file.
|
|
|
|
Arguments: none
|
|
|
|
Request body: NUL-terminated file name
|
|
|
|
Responds with FAIL code 0x80000001 if the file name is invalid (missing) or
|
|
file does not exist. Deleting directories is also not possible, giving the same
|
|
FAIL code 0x80000001.
|
|
|
|
### RSVD - Reserved
|
|
Miscellaneous commands.
|
|
|
|
Arguments:
|
|
- arg1: Subcommand:
|
|
- IDDD: create `/data/idt.cfg` ("indirect config file") and start IDT thread
|
|
- PDDD: unzip DF file
|
|
- QDDD: verify `/system/DFFileList.txt`
|
|
- RDDD: execute `/system/DFFileList.txt`
|
|
- SDDD: LCD test (turns screen grey)
|
|
- TDDD: set properties and disable USB:
|
|
- `ro.boot.laf = MID`
|
|
- `sys.usb.config = none`
|
|
|
|
### IOCT - ioctl
|
|
Perform flash IOCTLs. (XXX document this)
|
|
|
|
### MISC
|
|
Read/write misc parttition.
|
|
|
|
Arguments:
|
|
- arg1: Subcommand
|
|
- `READ`
|
|
- `WRTE`
|
|
|
|
XXX document this
|
|
|
|
### KILO
|
|
Challenge/response to authenticate for some commands.
|
|
|
|
Arguments:
|
|
- arg1: `CENT` or `METR`
|
|
- arg2: challenge/mode
|
|
|
|
1. Host sends `CENT` with arg2=0
|
|
2. Device responds `CENT` with a random value in arg2
|
|
3. Host responds `METR` with desired mode in arg2 and challenge response as body
|
|
4. Device responds `METR` with no body, or `FAIL`
|
|
|
|
The response must decrypt to the bytes
|
|
`00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F`. TODO: explain protocol
|
|
|
|
### DIFF
|
|
Execute a script of some sort, path specified in message body.
|
|
|
|
### SNIF
|
|
Unknown / looks related to TOT download (TODO: document this)
|
|
|
|
Arguments:
|
|
- arg1: Subcommand
|
|
- `REQS`
|
|
- `OPEN`
|
|
- `WRTE`
|
|
- `CLSE`
|
|
- `STUS`
|
|
- `IDDD`
|
|
|
|
### OPCM
|
|
Check/Write opcode
|
|
|
|
NOTE: Supported since Protocol version `0x1000004`
|
|
|
|
Arguments:
|
|
- arg1: action (`CHEK` - Check opcode, `WRTE` - Write opcode)
|
|
|
|
### FUSE
|
|
Get or set efuse(s) (TODO: document this)
|
|
|
|
Arguments:
|
|
- arg1: action (`GFUS` - Get efuse state, `SFRS` - Set/blow efuse)
|
|
|
|
### WRZR
|
|
Write zeroes
|
|
|
|
### CRCC
|
|
Calculcate CRC (TODO: document this)
|
|
|
|
### CHCK
|
|
Unknown (TODO: document this)
|
|
|
|
NOTE: Supported since Protocol version `0x1000003`
|
|
|
|
Arguments:
|
|
- arg1: Subcommand
|
|
- `TSUM`
|
|
- `CLER`
|
|
|
|
### SEBP
|
|
Set eMMC boot partition (TODO: document this)
|
|
|
|
### SBLU
|
|
Set UFS boot LUN# (TODO: document this)
|
|
|
|
### MBPT
|
|
Manipulate boot partition table (TODO: document this)
|
|
|
|
### FAIL
|
|
Dummy command.
|
|
|
|
### new versions
|
|
TODO: document these commands added in some version:
|
|
TOFF, COPY, SLEI, SIGN
|
|
|
|
|
|
## HDLC commands
|
|
These are sent through the same interface, but have a different structure:
|
|
|
|
* The packet must be at least 3 and at most 31 bytes long.
|
|
* The last byte must be 0x7E.
|
|
* Any 0x7D byte is skipped; the next byte is then XORed with 0x20. eg 0x7D 0x5E
|
|
becomes 0x7E. (Not sure why this is done.)
|
|
* The last two bytes of the body are a CRC16.
|
|
|
|
After decoding, the body is checked for an 0x7E byte (except the very last byte
|
|
of the packet); if one is found, all bytes up to and including it are discarded.
|
|
This is done before the checksum is validated. (for example, 0x7E marks the
|
|
beginning of the body, but can be omitted, in which case the body starts at the
|
|
first byte of the packet.)
|
|
|
|
The first byte of the body is a command:
|
|
* 0x06: unknown. Always responds with 0x02?
|
|
* 0x0A: Sets misc partition item 0x1C0 and reboots. (XXX investigate)
|
|
* 0xEF: webdload_proc
|
|
* 0xFA: testmode_proc
|
|
|
|
Additionally, sending a packet which decodes to an empty body (for example, a
|
|
packet of only `0x7E 0x7E 0x7E 0x7E`) restarts lafd. Unsure if this is
|
|
intentional.
|
|
|
|
### webdload_proc
|
|
First byte of body (after 0xEF command byte) is subcommand:
|
|
* 0x00: returns 0x05 and writes 0x04 at input[3]
|
|
* 0xA0: returns device OS version, target operator, model, etc.
|
|
* 0xA1, 0xA2, 0xB0, 0xB1, 0xB2, 0xB5: all return all-zero response.
|
|
|
|
These appear to also read/write some misc partition values.
|
|
|
|
### testmode_proc
|
|
First two bytes (after 0xFA command byte) must be 0x94 0x00. Next byte after
|
|
that is subcommand for `laf_testmode_bootloader_unlock_handler`:
|
|
* 0x00: Check if unlocked. (`/sys/devices/platform/lge-msm8226-qfprom/unlock`
|
|
contains 0x277F.)
|
|
* 0x01: Always returns 0.
|
|
* 0x02: Reads hex from `/sys/devices/platform/lge-msm8226-qfprom/unlock-extra`.
|
|
* 0x03: Read `/persist/rct`.
|
|
|
|
Commands 0x00 and 0x02 don't work on some devices, since they have `lge-qfprom`
|
|
instead of `lge-msm8226-qfprom` directory.
|
|
|
|
#### Return data
|
|
0xFA 0x94 0x00 followed by response body followed by CRC16 and 0x7E, encoded the
|
|
same way as the command packet.
|
|
|
|
First byte of response body is a status code:
|
|
* 0x00: OK
|
|
* 0x01: Failed
|
|
* 0x02: Return from command 0x06 (might be LAF_ERROR_INVALID_PARAMETER? but it
|
|
can only ever return this.)
|
|
* 0x05: Return from webdload_proc subcommand 0x00 (might be
|
|
LAF_ERROR_INTERNAL_ERROR?)
|
|
* 0xFF: Invalid command
|
|
|
|
Following response bytes depend on the command:
|
|
* testmode_proc: null-terminated string (eg "lock", "unlock", "fail") followed
|
|
by unknown bytes (0x19 0xEA 0xE8 0x7F 0x00 0x00 observed)
|
|
* webdload_proc: several strings of device info
|
|
|
|
|
|
## Encoded commands
|
|
For convenience, valid commands encoded with CRC:
|
|
* 0xEF 0x00 0x00 0x00 0xAD 0xFA 0x7E
|
|
* 0xEF 0xA0 0x00 0x00 0x7A 0xF5 0x7E
|
|
* 0xEF 0xA1 0x00 0x00 0xA6 0xAF 0x7E
|
|
* 0xEF 0xA2 0x00 0x00 0xC2 0x40 0x7E
|
|
* 0xEF 0xB0 0x00 0x00 0xEF 0x70 0x7E
|
|
* 0xEF 0xB1 0x00 0x00 0x33 0x2A 0x7E
|
|
* 0xEF 0xB2 0x00 0x00 0x57 0xC5 0x7E
|
|
* 0xEF 0xB5 0x00 0x00 0x52 0x49 0x7E
|
|
* 0xFA 0x94 0x00 0x00 0x43 0xBD 0x7E
|
|
* 0xFA 0x94 0x00 0x01 0xCA 0xAC 0x7E
|
|
* 0xFA 0x94 0x00 0x02 0x51 0x9E 0x7E
|
|
* 0xFA 0x94 0x00 0x03 0xD8 0x8F 0x7E
|
|
* 0xFA 0x94 0x00 0x04 0x67 0xFB 0x7E
|
|
|
|
|
|
## USB layer
|
|
The LG Windows driver (via `LGMobileDriver_WHQL_Ver_4.0.3.exe`) exposes two
|
|
serial ports, `LGANDNETMDM0` and `LGANDNETDIAG1`. The `LGANDNETDIAG1` port is
|
|
used for LAF.
|
|
|
|
The LG G3 (D855) has Vendor ID 0x1004 and Product ID 0x633e.
|
|
|
|
There is only one configuration descriptor and LAF uses bulk transfers over
|
|
endpoints 5 (for input from the device) and endpoint 3 (for output to the
|
|
device).
|
|
|
|
For other descriptors, see [info/lsusb.txt](info/lsusb.txt).
|