~aleteoryx/muditaos

e5948375e070e3e933af3a55ce2852e26263ddee — Lucjan Bryndza 5 years ago e69576a
[EGD-6188] Add PurePhone flash script

Currently dd doesn't handle sparse files properly,
so the mudita pure flashing script should be used
on the linux platform instad of the dd command.

Tools will be created in the build dir
example usage is: pureflash <image_file> <block_device>
M doc/boot_and_update.md => doc/boot_and_update.md +23 -10
@@ 104,11 104,11 @@ Littlefs summary:

```

7. Once completed, flash the phone with the latest MuditaOS build. To do that run the `dd` command 
7. Once completed, flash the phone with the latest MuditaOS build. To do that run the `pureflash` command 
on the image:

```bash
sudo dd if=pure.img of=/dev/sdX status=progress  conv=sparse 
sudo pureflash pure.img /dev/sdX
```

**Note:** Replace `/dev/sdX` with the disk name you found for your phone after connecting it to your computer.


@@ 116,12 116,25 @@ sudo dd if=pure.img of=/dev/sdX status=progress  conv=sparse
Example output:

```bash
sudo dd if=pure.img of=/dev/sda status=progress  conv=sparse                    
[sudo] password for lucck: 
Sorry, try again.
[sudo] password for lucck: 
15484559360 bytes (15 GB, 14 GiB) copied, 19 s, 815 MB/s 
30621696+0 records in
30621696+0 records out
15678308352 bytes (16 GB, 15 GiB) copied, 19,3531 s, 810 MB/s
➜  build-linux-Debug git:(EGD-6188_flashing_script) ✗ ./pureflash ~/worksrc/MuditaOS/build-rt1051-RelWithDebInfo/PurePhone.img /dev/sda 
File /home/lucck/worksrc/MuditaOS/build-rt1051-RelWithDebInfo/PurePhone.img copy 7 extents:
#       Offset          Length           Status
0:      0000000000000000 0000000000001000 OK
1:      0000000000100000 0000000000001000 OK
2:      0000000000104000 0000000000a2d000 OK
3:      0000000040100000 0000000000001000 OK
4:      0000000040104000 0000000000200000 OK
5:      0000000080100000 0000000000010000 OK
6:      0000000320e20000 00000000001c0000 OK
File /home/lucck/worksrc/MuditaOS/build-rt1051-RelWithDebInfo/PurePhone.img verify 7 extents:
#       Offset          Length           Verify
0:      0000000000000000 0000000000001000 OK
1:      0000000000100000 0000000000001000 OK
2:      0000000000104000 0000000000a2d000 OK
3:      0000000040100000 0000000000001000 OK
4:      0000000040104000 0000000000200000 OK
5:      0000000080100000 0000000000010000 OK
6:      0000000320e20000 00000000001c0000 OK
Write image /home/lucck/worksrc/MuditaOS/build-rt1051-RelWithDebInfo/PurePhone.img to /dev/sda SUCCESS
➜  build-linux-Debug git:(EGD-6188_flashing_script) ✗ 
```

M doc/running_on_phone.md => doc/running_on_phone.md +2 -3
@@ 33,10 33,9 @@ Files required to boot from eMMC are:
- firmware image checksum files: `.boot.json` and `.boot.json.crc32`
- assets (images, fonts, sounds, databases)

### Generating image and uploading it to the eMMC
### Uploading it to the eMMC

-  Use convenience script `generate_purephone_image.sh <image_file.img> <build_folder>` to generate Mudita Pure burning image.  
-  Use the `dd` command to burn image into the eMMC card `sudo dd if=<image_file.img> of=/dev/sdX status=progress conv=sparse` **Note:** Replace `/dev/sdX` with the disk name you found for your phone after connecting it to your computer.
-  Use the `pureflash` command to burn image into the eMMC card `sudo pureflash <PurePhone.img> /dev/sdX` **Note:** Replace `/dev/sdX` with the disk name you found for your phone after connecting it to your computer.

## How to mount Mudita Pure as a USB MSC


M host-tools/CMakeLists.txt => host-tools/CMakeLists.txt +11 -0
@@ 19,9 19,20 @@ if (CMAKE_CROSSCOMPILING)
        -H"${CMAKE_SOURCE_DIR}/host-tools/genlittlefs"
        COMMAND ${CMAKE_COMMAND} --build genlittlefs --config Release
    )
    add_custom_target(
        pureflash ALL
        COMMAND ${CMAKE_COMMAND}
        -DCMAKE_BUILD_TYPE:STRING="Release"
        -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE:PATH="${CMAKE_BINARY_DIR}"
        -B"pureflash"
        -H"${CMAKE_SOURCE_DIR}/host-tools/pure-flash"
        COMMAND ${CMAKE_COMMAND} --build pureflash --config Release
    )

else()
    set(_genlittlefs "${CMAKE_BINARY_DIR}/genlittlefs${CMAKE_EXECUTABLE_SUFFIX}")
    add_subdirectory(genlittlefs)
    add_subdirectory(littlefs-fuse)
    add_subdirectory(pure-flash)
endif()


A host-tools/pure-flash/CMakeLists.txt => host-tools/pure-flash/CMakeLists.txt +12 -0
@@ 0,0 1,12 @@
cmake_minimum_required(VERSION 3.14)

project(pureflash LANGUAGES C)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})

set(PUREFLASH_SRCS
    pure-flash.c
)

add_executable(${PROJECT_NAME} ${PUREFLASH_SRCS})
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -pedantic -Werror -Wextra )
target_compile_definitions(${PROJECT_NAME} PRIVATE _GNU_SOURCE )

A host-tools/pure-flash/pure-flash.c => host-tools/pure-flash/pure-flash.c +263 -0
@@ 0,0 1,263 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <linux/fs.h>
#include <linux/fiemap.h>

static void syntax(char **argv)
{
    fprintf(stderr, "%s [filename] [blkdev]\n", argv[0]);
}

static struct fiemap *read_fiemap(int fd)
{
    struct fiemap *fiemap;
    int extents_size;

    if ((fiemap = (struct fiemap *)malloc(sizeof(struct fiemap))) == NULL) {
        fprintf(stderr, "Out of memory allocating fiemap\n");
        return NULL;
    }
    memset(fiemap, 0, sizeof(struct fiemap));

    fiemap->fm_start          = 0;
    fiemap->fm_length         = ~0; /* Lazy */
    fiemap->fm_flags          = 0;
    fiemap->fm_extent_count   = 0;
    fiemap->fm_mapped_extents = 0;

    /* Find out how many extents there are */
    if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) {
        fprintf(stderr, "fiemap ioctl() failed\n");
        return NULL;
    }

    /* Read in the extents */
    extents_size = sizeof(struct fiemap_extent) * (fiemap->fm_mapped_extents);

    /* Resize fiemap to allow us to read in the extents */
    if ((fiemap = (struct fiemap *)realloc(fiemap, sizeof(struct fiemap) + extents_size)) == NULL) {
        fprintf(stderr, "Out of memory allocating fiemap\n");
        return NULL;
    }

    memset(fiemap->fm_extents, 0, extents_size);
    fiemap->fm_extent_count   = fiemap->fm_mapped_extents;
    fiemap->fm_mapped_extents = 0;

    if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) {
        fprintf(stderr, "fiemap ioctl() failed\n");
        return NULL;
    }
    return fiemap;
}

static off_t compare_extent(int fd1, int fd2, off_t start_offs, size_t len)
{
    char buf1[32768], buf2[32768];
    off_t offs;
    ssize_t res1, res2;
    size_t segpos = 0;
    offs          = lseek(fd1, start_offs, SEEK_SET);
    if (offs < 0)
        return offs;
    offs = lseek(fd2, start_offs, SEEK_SET);
    if (offs < 0)
        return offs;
    while (len > 0) {
        const size_t rdsiz = (len > sizeof(buf1)) ? (sizeof buf1) : (len);
        res1               = read(fd1, buf1, rdsiz);
        res2               = read(fd2, buf2, rdsiz);
        if (res1 == res2 && res1 > 0) {
            if (memcmp(buf1, buf2, res1) == 0) {
                len -= res1;
                segpos += res1;
            }
            else {
                return start_offs + segpos + 1;
            }
        }
        else {
            return start_offs + segpos + 1;
        }
    }
    return 0;
}

static ssize_t write_all(int fd, const char *buf, ssize_t size)
{
    ssize_t res;
    while (size > 0 && (res = write(fd, buf, size)) != size) {
        if (res < 0 && errno == EINTR)
            continue;
        else if (res < 0) {
            return res;
        }
        size -= res;
        buf += res;
    }
    return 0;
}

static off_t copy_extent(int fd_dest, int fd_src, off_t start_offs, size_t len)
{
    char copy_buf[32768];
    off_t offs = lseek(fd_dest, start_offs, SEEK_SET);
    if (offs < 0)
        return offs;
    offs = lseek(fd_src, start_offs, SEEK_SET);
    if (offs < 0)
        return offs;
    while (len > 0) {
        const size_t nread = len > sizeof(copy_buf) ? sizeof(copy_buf) : len;
        ssize_t rlen       = read(fd_src, copy_buf, nread);
        if (rlen < 0 && errno == EINTR)
            continue;
        else if (rlen > 0) {
            ssize_t res = write_all(fd_dest, copy_buf, rlen);
            if (res == 0) {
                len -= rlen;
            }
            else {
                return -1;
            }
        }
        else {
            return -1;
        }
    }
    return 0;
}

static int verify_image(const char *image_file, const char *block_device)
{
    int fd_sparse, fd_block;
    if ((fd_sparse = open(image_file, O_RDONLY)) < 0) {
        fprintf(stderr, "Cannot open sparse file %s\n", image_file);
        return EXIT_FAILURE;
    }
    if ((fd_block = open(block_device, O_RDONLY)) < 0) {
        fprintf(stderr, "Cannot open block device %s\n", block_device);
        close(fd_sparse);
        return EXIT_FAILURE;
    }
    struct fiemap *fiemap;
    if (!(fiemap = read_fiemap(fd_sparse))) {
        fprintf(stderr, "Unable to read fiemap %s\n", image_file);
        close(fd_sparse);
        close(fd_block);
        return EXIT_FAILURE;
    }
    printf("File %s verify %d extents:\n", image_file, fiemap->fm_mapped_extents);
    printf("#\tOffset          Length           Verify\n");
    off_t result = -1;
    for (unsigned i = 0; i < fiemap->fm_mapped_extents; i++) {
        result = compare_extent(fd_sparse, fd_block, fiemap->fm_extents[i].fe_logical, fiemap->fm_extents[i].fe_length);
        printf("%d:\t%-16.16llx %-16.16llx ", i, fiemap->fm_extents[i].fe_logical, fiemap->fm_extents[i].fe_length);
        if (result) {
            printf("ERR (%lx)\n", result);
        }
        else {
            printf("OK\n");
        }
        if (result) {
            if (result >= 0) {
                fprintf(stderr, "Error: Data mismatch at offset %ld\n", result);
            }
            else {
                perror("System error:");
            }
            break;
        }
    }
    close(fd_sparse);
    close(fd_block);
    free(fiemap);
    return (result ? EXIT_FAILURE : EXIT_SUCCESS);
}

static int write_image(const char *image_file, const char *block_device)
{
    struct stat sbuf;
    if (stat(image_file, &sbuf)) {
        perror("stat image");
        return EXIT_FAILURE;
    }
    if (!S_ISREG(sbuf.st_mode)) {
        fprintf(stderr, "Error: %s is not a regular file\n", image_file);
        return EXIT_FAILURE;
    }
    if (stat(block_device, &sbuf)) {
        perror("stat blkdev");
        return EXIT_FAILURE;
    }
    if (!S_ISBLK(sbuf.st_mode)) {
        fprintf(stderr, "Error: %s is not a block device\n", block_device);
        return EXIT_FAILURE;
    }
    int fd_sparse, fd_block;
    if ((fd_sparse = open(image_file, O_RDONLY)) < 0) {
        fprintf(stderr, "Cannot open sparse file %s\n", image_file);
        return EXIT_FAILURE;
    }
    if ((fd_block = open(block_device, O_WRONLY)) < 0) {
        fprintf(stderr, "Cannot open block device %s\n", block_device);
        close(fd_sparse);
        return EXIT_FAILURE;
    }
    struct fiemap *fiemap;
    if (!(fiemap = read_fiemap(fd_sparse))) {
        fprintf(stderr, "Unable to read fiemap %s\n", image_file);
        close(fd_block);
        close(fd_sparse);
        return EXIT_FAILURE;
    }
    printf("File %s copy %d extents:\n", image_file, fiemap->fm_mapped_extents);
    printf("#\tOffset          Length           Status\n");
    off_t result = -1;
    for (unsigned i = 0; i < fiemap->fm_mapped_extents; i++) {
        result = copy_extent(fd_block, fd_sparse, fiemap->fm_extents[i].fe_logical, fiemap->fm_extents[i].fe_length);
        printf("%d:\t%-16.16llx %-16.16llx %s\n",
               i,
               fiemap->fm_extents[i].fe_logical,
               fiemap->fm_extents[i].fe_length,
               result ? "FAIL" : "OK");
        if (result) {
            perror("System error:");
            break;
        }
    }
    free(fiemap);
    // Sync block filesystem
    syncfs(fd_block);
    // Re-read partition table on the device
    ioctl(fd_block, BLKRRPART, NULL);
    close(fd_block);
    close(fd_sparse);
    return result ? EXIT_FAILURE : EXIT_SUCCESS;
}

int main(int argc, char **argv)
{
    if (argc < 3) {
        syntax(argv);
        exit(EXIT_FAILURE);
    }
    if (write_image(argv[1], argv[2])) {
        return EXIT_FAILURE;
    }
    int result = verify_image(argv[1], argv[2]);
    fprintf(stderr, "Write image %s to %s %s\n", argv[1], argv[2], result ? "FAILED" : "SUCCESS");
    return result;
}