# Nokia Harmattan flashing protocol

Everything below was reverse engineered from the stripped 32 bit ELF binary
`./flasher` (`flasher 3.12.1 (Oct 5 2011) Harmattan`, links against
`libusb-0.1`).
Symbol names in parentheses refer to that decompilation. The FIASCO container
format was cross checked against [0xFFFF](https://github.com/pali/0xFFFF).

The original source of the tool was never published; the
`harmattan-dev.nokia.com` mirror does not contain it.

---

## 1. USB modes

Three device tables are embedded in the binary (`nolo_devices` @ `0x08064480`,
`bootrom_devices` @ `0x080644a0` and an unnamed one @ `0x080644e0`). Each entry
is `{u32 vid, u32 pid, i32 interface, i32 alternate}`, terminated by a zero
entry:

| Interface class | VID:PID | iface/alt | Meaning |
|---|---|---|---|
| `bootloader` (`nolo_devices`) | `0421:0105` | 2 / 1 | NOLO bootloader, "flash mode" |
| `bootrom` (`bootrom_devices`) | `0451:d009` | - | OMAP3430 boot ROM (cold flash) |
| | `0451:d00e` | - | OMAP3630 boot ROM (cold flash) |
| | `0421:0106` | - | Nokia 2nd stage loader (cold flash) |
| `phonet` (third table) | `0421:01c8` | -1 | N900 (RX-51) update mode |
| | `0421:03d2` | -1 | N950 (RM-680) update mode |
| | `0421:051a` | -1 | N9 (RM-696) update / "sync and connect" mode |

Normal (non cold) flashing of a Harmattan device uses the **phonet** interface.
It is *not* selected by PID; the flasher scans all USB devices for a matching
descriptor (`FUN_0805b629`):

* an interface with `bInterfaceClass == 0x02` (CDC) and
  `bInterfaceSubClass == 0xFE` (Phonet);
* its class specific descriptors must contain
  * a vendor descriptor `0xAB` whose payload starts with `05 15`,
  * a CDC header functional descriptor (subtype `0x00`) with `bcdCDC >= 0x0110`,
  * a CDC **union** functional descriptor (subtype `0x06`); its slave interface
    is the *data* interface;
* on that data interface the alternate setting with more than one endpoint is
  selected and the first bulk IN / bulk OUT endpoints are used
  (`FUN_0805b800`).

In addition a **vendor specific** interface (`bInterfaceClass == 0xFF`,
subclass 0, protocol 0, 1-2 endpoints) provides a bulk OUT endpoint
(`FUN_0805b9f0`) which is used as the fast `usb:raw` auxiliary data pipe
("Raw data transfer EP found at EP%d").

### Getting a running phone into flashing mode (`flasher -i`)

A phone that is simply switched on ("Sync and connect" mode) also exposes a
Phonet interface, but there is no update server behind it: `SU_VERIFY_COMMS`
comes back as a `COMMON_MESSAGE` (`0xF0`) from the cellular modem. The flasher
detects this - either from the product id (`0421:03d2`, `0421:051a`,
`FUN_0805994f`) or from the unexpected reply - and reboots the phone into
flashing mode with two ADL messages on resource `0x6F` (`FUN_08059845`):

| step | message | payload | response |
|---|---|---|---|
| `ADL_SET_BOOT_FLAG` | `0x0D` | `FF FC 00 00 00 00` | `0x2D`, status byte |
| `ADL_REBOOT_PHONE` | `0x04` | `24 00` | `0x24`, status byte |

The boot flag is what makes the phone come back up in flashing mode instead of
booting normally. The phone often reboots before answering the second message,
so a missing reply is not treated as an error. Afterwards the tool waits for the
device to re-enumerate.

Note that the ADL replies do not mirror the device/object bytes of the request
(see §2), so they must be matched on the transaction id only.

---

## 2. ISI / Phonet framing

`FUN_0805ceed` builds every message; the header is 10 bytes:

```
offset  size  field
  0      1    media byte, always 0x1B (PN_MEDIA_USB)
  1      1    receiver device      0x00
  2      1    sender device        0x10
  3      1    resource             0x88 = software update server
                                   0x6F = ADL
  4      2    length, big endian = payload length + 4
  6      1    receiver object      0x00
  7      1    sender object        0x00
  8      1    transaction id, incremented for every request
  9      1    message id
 10    ...    payload
```

Total frame size is `ntohs(hdr[4..5]) + 6` (`FUN_0805d15c`).

Transmission rules (`FUN_0805ca06`, `FUN_0805d1f0`):

* a frame is written to the bulk OUT endpoint in one go; if its length is an
  exact multiple of the max packet size a **zero length packet** is appended;
* reads use a single bulk IN transfer of up to `0x10004` bytes; anything
  shorter than 10 bytes or shorter than the declared length is an error
  (`usb_isi_recv: need at least 10 bytes`, `reply size mismatch`);
* the original matches a reply to its request when `resp[1] == req[2]`,
  `resp[2] == req[1]`, `resp[6] == req[7]` and `resp[8] == req[8]`
  (`FUN_0805d07a`).

  **In practice only the transaction id (`[8]`) can be relied upon.** A real
  RM-696 answers `ADL_SET_BOOT_FLAG` on resource `0x6F` with device and object
  bytes that are *not* the mirror image of the request, so a strict comparison
  drops the reply and the request appears to time out. The web version therefore
  correlates on the transaction id and lets the next layer validate the message
  id.

---

## 3. Software update server (resource 0x88)

Response id = request id | `0x20`. Unless stated otherwise the **first byte of
the response payload is a status code** (`0` = success); a human readable error
string may follow it. All integers are big endian.

| id | name | request payload | response payload |
|----|------|-----------------|------------------|
| `0x00` | `SU_VERIFY_COMMS` | arbitrary data | the same data echoed (no status byte) |
| `0x01` | `SU_GET_PARAMETER` | parameter path | status + value |
| `0x02` | `SU_SET_PARAMETER` | `path \0 value` | status |
| `0x03` | `SU_BEGIN_UPDATE` | - | status |
| `0x04` | `SU_BEGIN_IMAGE_UPDATE` | image info subblocks (see §4) | `u32 pipe id`, `u32 block size`, status at offset 8 |
| `0x05` | `SU_SETUP_AUX_DATA_PIPE` | `u32 pipe id` + `"usb:raw"` | status |
| `0x06` | `SU_GET_UPDATE_STATUS` | `u32 pipe id` | `u32 state`, `u64 done`, `u64 total`, status at offset 20 |
| `0x08` | `SU_PREPARE_DATA_BLOCK` | `u32 pipe id`, `u32 length` | status |
| `0x09` | `SU_FEED_DATA_BLOCK` | `u32 pipe id` + data (≤ 32 KiB) | status |
| `0x0A` | `SU_FINISH_IMAGE_UPDATE` | `u32 pipe id` | status |
| `0x0B` | `SU_WAIT_FOR_BUFFER` | `u32 timeout in ms` | `u32 count`, `count × {u32 pipe id, u32 free bytes}`, status last |
| `0x0C` | `SU_DEVICE_STATE_CHANGE` | 16 bytes: `reboot`, `poweroff` or `reboot=<mode>` | status |
| `0x0D` | `SU_BANDWIDTH_TEST` | arbitrary data | status |
| `0x0F` | `SU_UPDATE_SW_REL` | SW release string | status |
| `0x10` | `SU_CANCEL_UPDATE` | - | status |
| `0x11` | `SU_READ_CONFIG` | `u32 index` | status + config blob |
| `0x12` | `SU_BEGIN_ERASE` | `userdata=secure`, `mmc=secure`, ... | status |
| `0x1F` | `SU_FINISH_UPDATE` | - | status |

Special message ids in replies: `0xFF` = server failure mode, `0xF0` =
`COMMON_MESSAGE` (usually from the cellular modem, not the update server).

### Status codes

| code | meaning |
|---|---|
| 0 | success |
| 1 | unknown error |
| 2 | invalid arguments |
| 3 | invalid state |
| 4 | out of memory |
| 5 | object exists |
| 6 | operation not supported |
| 7 | object not found |
| 8 | object too large |
| 9 | unknown request |
| 10 | security failure |
| 11 | battery low |

### Update states (low 16 bits of `SU_GET_UPDATE_STATUS.state`)

| value | name |
|---|---|
| 0 | poweroff |
| 1 | pending |
| 2 | init |
| 3 | erasing |
| 4 | writing |
| 0x10 | finishing / finished |

Bit `0x10000` set means the update failed.

### Parameters used by the flasher

```
/update/protocol_version       106..108 supported, host answers with "108"
/update/host_protocol_version
/update/application_info       must be >= 1.4.8
/update/supported_images       comma separated list
/update/trace_level
/update/no_battery_level_check
/update/mmc/no_preserve
/update/cmt/verify
/update/cmt/perform_rfs
/update/nand/part_table/version
/update/error_list             detailed error log after a failure
/device/product_code           e.g. RM-696
/device/hw_build               e.g. 1601
/device/battery_level          percent, >= 11 required
/device/rd_mode                R&D mode string, e.g. "+{no-omap-wd}"
/device/nand/part_table/version
/device/hw_build_override
```

R&D flags: `master`, `no-omap-wd`, `no-ext-wd`, `no-lifeguard-reset`,
`serial-console`, `no-usb-timeout`, `sti-console`, `no-charging`,
`force-power-key`.

---

## 4. Image info subblocks

`SU_BEGIN_IMAGE_UPDATE` takes the **raw subimage header of the FIASCO file**:
a flat concatenation of `{u8 id, u8 length, u8 data[length]}` records, with no
terminator (`FUN_0804d61e`, `FUN_0804d6e9`; total size must stay below 1024
bytes). When reading a FIASCO the flasher copies those bytes verbatim
(`FUN_0804f8b4`), so the web version does exactly the same.

Known ids:

| id | contents |
|----|----------|
| `0x2E` | file data, 25 bytes: `u8 asic index`, `u8 device type`, `u8 device index`, `u16 hash`, `char type[12]`, `u32 size`, `u32 load address` |
| `0x5C` | same but with 64 bit size and address (33 bytes) |
| `0x2F` | partition table description |
| `0x31` | `'1'` version string |
| `0x32` | `'2'` product code (16 bytes) followed by NUL separated 8 byte HW revisions |
| `0x33` | `'3'` layout |
| `0x34` | `'4'` data part: `u64 offset`, `u64 size`, name |

The 16 bit `hash` is the XOR of all little endian 16 bit words of the payload,
stored big endian (`FUN_0804ca06` / `FUN_0804ca45`).

### Choosing the right variant (`find_image`, `FUN_0804e5d8`)

A FIASCO holds one variant of each image type per device, so the flasher has to
pick one. Walking the subimages in file order, the first one whose type matches
is used when:

* it carries **no** `0x32` block at all (fits every device), or
* one of its `0x32` blocks names the device's `/device/product_code`
  (`strncmp` over 16 bytes) **and** either lists no revision (any revision of
  that product) or contains the device's `/device/hw_build` (`strncmp` over
  8 bytes).

A subimage may carry several `0x32` blocks; matching any of them is enough.
Further matching variants of the same type are skipped with
"Warning: ignoring subimage '%s' from file '%s'" (`FUN_08058883`), and when no
variant matches, the flasher prints
"Image %s not present for this HW (%s rev. %s)" and does not flash that type.

Image type names and their `--flash-only` bits (table @ `0x08063e00`):

| name | bit | media |
|---|---|---|
| `cert-sw` | 0x4000 | 3 |
| `cmt-2nd` | 0x0010 | 1 |
| `cmt-algo` | 0x0020 | 1 |
| `cmt-mcusw` | 0x0040 | 1 |
| `xloader` | 0x0001 | 0 |
| `secondary` | 0x0001 | 0 |
| `kernel` | 0x0002 | 0 |
| `moslo` | 0x0080 | 0 |
| `rootfs` | 0x0008 | 2 |
| `mmc` | 0x0100 | 2 |
| `tar` | 0x0200 | 2 |
| `config` | 0x2000 | 0 |

A FIASCO also carries image types that are **never written in update mode** and
therefore are not in that table. They are the loaders the flasher uploads over
the OMAP boot ROM when cold flashing, and the flashing algorithm:

| name | used for |
|---|---|
| `1st` | cold flash: first stage loader, sent to the OMAP boot ROM |
| `2nd` | cold flash: second stage loader |
| `ape-algo` | the APE flashing algorithm (`-a`), needed for cold flash, erase and R&D mode changes |
| `initfs`, `initrd` | legacy Maemo image types |

For reference, a stock `DFL61_HARMATTAN_40.2012.21-3_PR_LEGACY_009-OEM1-958_ARM.bin`
holds 110 subimages: 22 × `cert-sw`, 22 × `xloader`, 22 × `secondary`,
22 × `2nd`, 5 × `cmt-2nd`/`cmt-algo`/`cmt-mcusw`, 4 × `1st`, and one each of
`kernel`, `rootfs` and `ape-algo` — which boils down to 8 images for any single
phone.

---

## 5. FIASCO container

```
u8   0xB4
u32  length            (informational)
u32  count             number of global header blocks
count × { u8 id, u8 len, u8 data[len] }      0xE8 = name, 0x31 = SW release
repeat until EOF:
  u8  0x54                                  subimage marker
  u8  nblocks
  nblocks × { u8 id, u8 len, u8 data[len] } first one must be 0x2E / 25 bytes
  u8  checksum                              see below
  u8  data[size]                            payload, size taken from 0x2E
```

The checksum restarts after the `0x54` marker and covers **the block count
byte**, every subblock byte and the checksum byte itself; the 8 bit sum must be
`0xFF`. (Leaving the count byte out makes every subimage of a real firmware
look corrupt.) A checksum byte of `0x00` means "not checksummed".

---

## 6. Flashing sequence

```
ping                     SU_VERIFY_COMMS with growing timeouts (250 ms .. 5 s)
init                     GET /update/protocol_version, SET /update/host_protocol_version=108,
                         GET /update/application_info, /device/product_code,
                         /device/hw_build, /update/supported_images
battery                  GET /device/battery_level (>= 11 %), or
                         SET /update/no_battery_level_check=1
                         SU_BEGIN_UPDATE answers error 11 when the battery is flat
SU_BEGIN_UPDATE
  SET /update/cmt/verify=1                    (protocol >= 100)
  SET /update/mmc/no_preserve=0|1             (protocol >= 104)
  for every image:
     SU_BEGIN_IMAGE_UPDATE(subblocks)   -> pipe id, block size
     SU_SETUP_AUX_DATA_PIPE(pipe)       optional, only when a raw EP exists and
                                        the block size is not 512
     loop:
        n = SU_WAIT_FOR_BUFFER(250 ms)
        chunk = min(n, 1 MiB rounded down to block size, remaining)
        SU_PREPARE_DATA_BLOCK(pipe, chunk)
        write chunk               either SU_FEED_DATA_BLOCK in 32 KiB pieces,
                                  or straight to the usb:raw bulk endpoint
     poll SU_GET_UPDATE_STATUS(pipe) until state == 0x10
     SU_FINISH_IMAGE_UPDATE(pipe)
  SU_UPDATE_SW_REL(release)               optional
SU_FINISH_UPDATE
SU_DEVICE_STATE_CHANGE("reboot")          optional
```

Erasing user data / eMMC is `SU_BEGIN_ERASE` with `userdata=<method>` or
`mmc=<method>` (methods: `secure`, `quick`, ...) and may take up to 30 minutes.

`SU_SETUP_AUX_DATA_PIPE` is only a speed optimisation: it moves the image data
onto the vendor specific bulk endpoint instead of wrapping it in 32 KiB ISI
messages. Many devices answer it with error 6 ("operation not supported"), and
the original ignores the return value entirely (`FUN_08058b52` discards it), so
that is not a failure - the transfer simply continues over ISI.

---

## 7. Where the software update server comes from (`flasher -a`)

An N9 that has been rebooted into flashing mode enumerates as
**`0421:0105`, "N9 (Update mode)"** with this configuration:

```
iface 0 alt 0  class 02/08/00                       (CDC ACM, no endpoints)
iface 1 alt 0  class 02/fe/00                       (CDC Phonet control)
iface 2 alt 0  class 0a/00/00                       (CDC data, no endpoints)
iface 2 alt 1  class 0a/00/00  EP1 IN, EP2 OUT      (CDC data, bulk)
```

The Phonet interface is there, but **the software update server is not running
yet** - this is the NOLO bootloader (`0421:0105` is exactly the `nolo_devices`
entry, interface 2 / alternate 1). Sending ISI frames to it gets no answer and
eventually stalls the bulk endpoints.

Main (`0x0804b0xx`) does this:

```
mask = bootloader|phonet;  dev = find_device(mask)
if (found interface class == phonet)
     su_open(dev)                        -> softupd already running?
     on "no answer" (-4):
        "Unable to detect flashing interface: standing by for device reboot."
        close, mask = bootloader, dev = find_device(mask), nolo_init(dev)
else nolo_init(dev)

if (softupd not running && an ape-algo image is available) {
     upload the 'ape-algo' image        (FUN_08050fc4, image mask 0x1000)
     switch protocol                    (FUN_08051c16: nolo boot mode 3,
                                         close, re-find with mask = phonet,
                                         su_open again)
}
if (softupd still not running)
     "APE algorithm has to be provided to flash all the subimages"
```

So the APE flashing algorithm (the `ape-algo` subimage inside the firmware
FIASCO, ~7 MB) is uploaded over **NOLO** and booted; only then does the phone
run the softupd server that everything in §3 talks to. That is why every N9
flashing recipe passes the firmware twice:

```
flasher -a DFL61_HARMATTAN_..._ARM.bin -f -R          # -a = the ape-algo source
flasher -a DFL61_..._ARM.bin -F ..._EMMC_....bin -f -R
```

### The NOLO wire protocol

NOLO speaks **vendor control requests on the device recipient** (`bmRequestType`
`0x40` out / `0xC0` in, `FUN_08052115`) plus the bulk OUT endpoint of the same
Phonet data interface:

| request | direction | meaning |
|---|---|---|
| `0x05` | in | bootloader error strings, NUL separated (up to 2 KiB) |
| `0x42` | out | send an image; payload = the image's FIASCO subblock header (§4), the data follows on the bulk endpoint in 128 KiB chunks |
| `0x54` | out | same, but for `rootfs` (send *and* flash) |
| `0x52` | out | finish flashing, only after `0x54` (30 s timeout) |
| `0x82` | out | boot: `wValue=0` boots the loaded image (payload = optional kernel cmdline), `wValue=1` = "Booting device into flash mode" |
| `0x83` | out | reboot the device |

Note that the image header handed to `0x42` is byte for byte the same buffer
that `SU_BEGIN_IMAGE_UPDATE` takes, so both protocols share the FIASCO parser.

## 8. What is *not* implemented in the web version

* **Cold flashing** (`--cold-flash`): the OMAP boot ROM peripheral boot
  protocol (`first_get_asic_id`, ASIC ID, 1st/2nd image upload, `0x6301`
  message ids) used when the phone has no working bootloader.
* Certificate programming over NOLO (`common_write_cert`,
  `common_challenge_response`) and `NOLO_REQ_GET_STATUS`.
* CMT (modem) specific handling beyond passing `cmt-*` images through.
* Repartitioning (`--nand-repartition`) and CAL backup.
