/* * Copyright 2014 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <errno.h> #include <stdio.h> #include <unistd.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <cutils/properties.h> #include <sys/mman.h> #include "fw_version_check.h" #include "edify/expr.h" #define FORCE_RW_OPT "0" #define BOOT_IFWI_SIZE 0x400000 #define BOOT_UMIP_SIZE 0x10000 #define BOOT_UMIP_SECTOR_SIZE 0x200 #define BOOT_UMIP_XOR_OFFSET 0x7 #define BOOT_UMIP_3GPP_OFFSET 0x76F #define BOOT_IFWI_XOR_OFFSET 0x0112d8 #define BOOT_DNX_TIMEOUT_OFFSET 0x400 #define IFWI_OFFSET 0 #define TOKEN_UMIP_AREA_OFFSET 0x4000 #define TOKEN_UMIP_AREA_SIZE 0x2C00 #define FILE_PATH_SIZE 64 #define IFWI_TYPE_LSH 12 static void dump_fw_versions(struct firmware_versions *v) { fprintf(stderr, "Image FW versions:\n"); fprintf(stderr, " ifwi: %04X.%04X\n", v->ifwi.major, v->ifwi.minor); fprintf(stderr, "---- components ----\n"); fprintf(stderr, " scu: %04X.%04X\n", v->scu.major, v->scu.minor); fprintf(stderr, " hooks/oem: %04X.%04X\n", v->valhooks.major, v->valhooks.minor); fprintf(stderr, " ia32: %04X.%04X\n", v->ia32.major, v->ia32.minor); fprintf(stderr, " chaabi: %04X.%04X\n", v->chaabi.major, v->chaabi.minor); fprintf(stderr, " mIA: %04X.%04X\n", v->mia.major, v->mia.minor); } static int force_rw(const char *name) { int ret, fd; fd = open(name, O_WRONLY); if (fd < 0) { fprintf(stderr, "force_ro(): failed to open %s\n", name); return fd; } ret = write(fd, FORCE_RW_OPT, sizeof(FORCE_RW_OPT)); if (ret <= 0) { fprintf(stderr, "force_ro(): failed to write %s\n", name); close(fd); return ret; } close(fd); return 0; } int check_ifwi_file_scu_emmc(void *data, size_t size) { struct firmware_versions dev_fw_rev, img_fw_rev; if (get_image_fw_rev(data, size, &img_fw_rev)) { fprintf(stderr, "Coudn't extract FW version data from image\n"); return -1; } dump_fw_versions(&img_fw_rev); if (get_current_fw_rev(&dev_fw_rev)) { fprintf(stderr, "Couldn't query existing IFWI version\n"); return -1; } fprintf(stderr, "Attempting to flash ifwi image version %04X.%04X over ifwi current version %04X.%04X\n", img_fw_rev.ifwi.major, img_fw_rev.ifwi.minor, dev_fw_rev.ifwi.major, dev_fw_rev.ifwi.minor); if (img_fw_rev.ifwi.major != dev_fw_rev.ifwi.major) { fprintf(stderr, "IFWI FW Major version numbers (file=%04X current=%04X) don't match, Update abort.\n", img_fw_rev.ifwi.major, dev_fw_rev.ifwi.major); return -1; } return 1; } static uint32_t xor_compute(char *ptr, uint32_t size) { uint32_t val = 0; uint32_t i; for (i = 0; i < size; i+=4) val = val ^ *(uint32_t *)(ptr + i); return val; } static uint8_t xor_factorize(uint32_t val) { return (uint8_t)((val & 0xff) ^ ((val >> 8) & 0xff) ^ ((val >> 16) & 0xff) ^ ((val >> 24) & 0xff)); } static void xor_update(char *ptr) { uint16_t i; uint32_t val; /* update UMIP xor of sector 2 to 127 */ for (i = 2; i < 128; i++) { val = xor_compute(ptr + i * BOOT_UMIP_SECTOR_SIZE, BOOT_UMIP_SECTOR_SIZE); *(uint32_t *)(ptr + 4 * i) = val; } /* update UMIP xor */ *(ptr + BOOT_UMIP_XOR_OFFSET) = 0; val= xor_compute(ptr, BOOT_UMIP_SIZE); *(ptr + BOOT_UMIP_XOR_OFFSET) = xor_factorize(val); /* update IFWI xor */ *(uint32_t *)(ptr + BOOT_IFWI_XOR_OFFSET) = 0x0; val= xor_compute(ptr, BOOT_IFWI_SIZE); *(uint32_t *)(ptr + BOOT_IFWI_XOR_OFFSET) = val; } static int write_umip_emmc(uint32_t addr_offset, void *data, size_t size) { int boot_fd = 0; int boot_index; char boot_partition[FILE_PATH_SIZE]; char boot_partition_force_ro[FILE_PATH_SIZE]; char *ptr; char *token_data; if (addr_offset == IFWI_OFFSET) { token_data = reinterpret_cast<char *>(malloc(TOKEN_UMIP_AREA_SIZE)); if (!token_data) { fprintf(stderr, "write_umip_emmc: Malloc error\n"); return -1; } if (size > BOOT_IFWI_SIZE) { fprintf(stderr, "write_umip_emmc: Truncating last %d bytes from the IFWI\n", (size - BOOT_IFWI_SIZE)); /* Since the last 144 bytes are the FUP header which are not required,*/ /* we truncate it to fit into the boot partition. */ size = BOOT_IFWI_SIZE; } } for (boot_index = 0; boot_index < 2; boot_index++) { snprintf(boot_partition, FILE_PATH_SIZE, "/dev/block/mmcblk0boot%d", boot_index); snprintf(boot_partition_force_ro, FILE_PATH_SIZE, "/sys/block/mmcblk0boot%d/force_ro", boot_index); if (force_rw(boot_partition_force_ro)) { fprintf(stderr, "write_umip_emmc: unable to force_ro %s\n", boot_partition); goto err_boot1; } boot_fd = open(boot_partition, O_RDWR); if (boot_fd < 0) { fprintf(stderr, "write_umip_emmc: failed to open %s\n", boot_partition); goto err_boot1; } ptr = (char *)mmap(NULL, BOOT_IFWI_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, boot_fd, 0); if (ptr == MAP_FAILED) { fprintf(stderr, "write_umip_emmc: mmap failed on boot%d with error : %s\n", boot_index, strerror(errno)); goto err_boot1; } if (addr_offset == IFWI_OFFSET) memcpy(token_data, ptr + TOKEN_UMIP_AREA_OFFSET, TOKEN_UMIP_AREA_SIZE); /* Write the data */ if (addr_offset + size <= BOOT_IFWI_SIZE) if (data == NULL) memset(ptr + addr_offset, 0, size); else memcpy(ptr + addr_offset, data, size); else { fprintf(stderr, "write_umip_emmc: write failed\n"); goto err_boot2; } if (addr_offset == IFWI_OFFSET) memcpy(ptr + TOKEN_UMIP_AREA_OFFSET, token_data, TOKEN_UMIP_AREA_SIZE); /* Compute and write xor */ xor_update(ptr); munmap(ptr, BOOT_IFWI_SIZE); close(boot_fd); } if (addr_offset == IFWI_OFFSET) free(token_data); return 0; err_boot2: munmap(ptr, BOOT_IFWI_SIZE); err_boot1: if (addr_offset == IFWI_OFFSET) free(token_data); close(boot_fd); return -1; } static int readbyte_umip_emmc(uint32_t addr_offset) { int boot_fd = 0; char *ptr; int value = 0; if (force_rw("/sys/block/mmcblk0boot0/force_ro")) { fprintf(stderr, "read_umip_emmc: unable to force_ro\n"); goto err_boot1; } boot_fd = open("/dev/block/mmcblk0boot0", O_RDWR); if (boot_fd < 0) { fprintf(stderr, "read_umip_emmc: failed to open /dev/block/mmcblk0boot0\n"); goto err_boot1; } ptr = (char *)mmap(NULL, BOOT_UMIP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, boot_fd, 0); if (ptr == MAP_FAILED) { fprintf(stderr, "read_umip_emmc: mmap failed on boot0 with error : %s\n", strerror(errno)); goto err_boot1; } /* Read the data */ if (addr_offset < BOOT_UMIP_SIZE) value = (int)*(ptr + addr_offset); else { fprintf(stderr, "read_umip_emmc: read failed\n"); goto err_boot2; } munmap(ptr, BOOT_UMIP_SIZE); close(boot_fd); return value; err_boot2: munmap(ptr, BOOT_UMIP_SIZE); err_boot1: close(boot_fd); return -1; } int update_ifwi_file_scu_emmc(void *data, size_t size) { return write_umip_emmc(IFWI_OFFSET, data, size); } int flash_ifwi_scu_emmc(void *data, unsigned size) { int ret; ret = check_ifwi_file_scu_emmc(data, size); if (ret > 0) return update_ifwi_file_scu_emmc(data, size); return ret; } Value* FlashIfwiFuguFn(const char *name, State * state, int argc, Expr * argv[]) { Value *ret = NULL; char *filename = NULL; unsigned char *buffer = NULL; int ifwi_size; FILE *f = NULL; if (argc != 1) { ErrorAbort(state, "%s() expected 1 arg, got %d", name, argc); return NULL; } if (ReadArgs(state, argv, 1, &filename) < 0) { ErrorAbort(state, "%s() invalid args ", name); return NULL; } if (filename == NULL || strlen(filename) == 0) { ErrorAbort(state, "filename argument to %s can't be empty", name); goto done; } if ((f = fopen(filename,"rb")) == NULL) { ErrorAbort(state, "Unable to open file %s: %s ", filename, strerror(errno)); goto done; } fseek(f, 0, SEEK_END); ifwi_size = ftell(f); if (ifwi_size < 0) { ErrorAbort(state, "Unable to get ifwi_size "); goto done; }; fseek(f, 0, SEEK_SET); if ((buffer = reinterpret_cast<unsigned char *>(malloc(ifwi_size))) == NULL) { ErrorAbort(state, "Unable to alloc ifwi flash buffer of size %d", ifwi_size); goto done; } fread(buffer, ifwi_size, 1, f); fclose(f); if(flash_ifwi_scu_emmc(buffer, ifwi_size) !=0) { ErrorAbort(state, "Unable to flash ifwi in emmc"); free(buffer); goto done; }; free(buffer); ret = StringValue(strdup("")); done: if (filename) free(filename); return ret; } void Register_librecovery_updater_fugu() { RegisterFunction("fugu.flash_ifwi", FlashIfwiFuguFn); }