/* * Copyright (C) 2015 The Android Open Source Project * * 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. */ /* Read/write/erase Embedded Controller integrated flash */ #define LOG_TAG "fwtool" #include <stdint.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/param.h> #include <sys/stat.h> #include <sys/types.h> #include "ec_commands.h" #include "flash_device.h" #include "update_log.h" #define CROS_EC_DEV_NAME "/dev/cros_ec" struct cros_ec_command { uint32_t version; uint32_t command; uint8_t *outdata; uint32_t outsize; uint8_t *indata; uint32_t insize; uint32_t result; }; #define CROS_EC_DEV_IOCXCMD _IOWR(':', 0, struct cros_ec_command) #define CROS_EC_DEV_IOCRDMEM _IOWR(':', 1, struct cros_ec_readmem) struct ec_data { int fd; struct ec_response_get_protocol_info proto; struct ec_response_flash_info_1 info; struct ec_response_flash_region_info ro_region; }; static int ec_command(void *hnd, int command, int version, const void *outdata, int outsize, void *indata, int insize) { struct ec_data *ec = hnd; struct cros_ec_command s_cmd; int r; if (ec->fd < 0) return -ENODEV; s_cmd.command = command; s_cmd.version = version; s_cmd.result = 0xff; s_cmd.outsize = outsize; s_cmd.outdata = (uint8_t *)outdata; s_cmd.insize = insize; s_cmd.indata = indata; r = ioctl(ec->fd, CROS_EC_DEV_IOCXCMD, &s_cmd); if (r < 0) { ALOGD("Cmd 0x%x failed %d\n", command, errno); return -errno; } else if (s_cmd.result != EC_RES_SUCCESS) { ALOGD("Cmd 0x%x error %d\n", command, s_cmd.result); return s_cmd.result; } return 0; } static void *ec_open(const void *params) { int res; struct ec_params_flash_region_info region; const char *path = params ? params : CROS_EC_DEV_NAME; struct ec_data *dev = calloc(1, sizeof(struct ec_data)); if (!dev) return NULL; dev->fd = open(path, O_RDWR); if (dev->fd == -1) { ALOGE("Cannot open EC device %s : %d\n", path, errno); goto out_free; } res = ec_command(dev, EC_CMD_GET_PROTOCOL_INFO, 0, NULL, 0, &dev->proto, sizeof(dev->proto)); if (res) { ALOGE("Cannot get EC protocol info for %s : %d\n", path, res); goto out_close; } res = ec_command(dev, EC_CMD_FLASH_INFO, 1, NULL, 0, &dev->info, sizeof(dev->info)); if (res) { ALOGE("Cannot get EC flash info for %s : %d\n", path, res); goto out_close; } region.region = EC_FLASH_REGION_RO; res = ec_command(dev, EC_CMD_FLASH_REGION_INFO, 1, ®ion, sizeof(region), &dev->ro_region, sizeof(dev->ro_region)); if (res) { ALOGE("Cannot get EC RO info for %s : %d\n", path, res); goto out_close; } ALOGD("EC %s: size %d erase_block_size %d write_ideal_size %d\n", path, dev->info.flash_size, dev->info.erase_block_size, dev->info.write_ideal_size); return dev; out_close: close(dev->fd); dev->fd = -1; out_free: free(dev); return NULL; } static void ec_close(void *hnd) { struct ec_data *dev = hnd; close(dev->fd); free(dev); } static int ec_read(void *hnd, off_t offset, void *buffer, size_t count) { struct ec_data *dev = hnd; ssize_t res; struct ec_params_flash_read p; uint8_t *ptr = buffer; uint32_t read_size = dev->proto.max_response_packet_size - sizeof(struct ec_host_response); while (count) { p.offset = offset; p.size = MIN(read_size, count); res = ec_command(dev, EC_CMD_FLASH_READ, 0, &p, sizeof(p), ptr, read_size); if (res) { ALOGW("Cannot read at %ld : %zd\n", offset, res); return res; } count -= p.size; ptr += p.size; offset += p.size; } return 0; } static int ec_write(void *hnd, off_t offset, void *buffer, size_t count) { struct ec_data *dev = hnd; ssize_t res; struct ec_params_flash_write *p; uint8_t *packet_data; uint8_t *ptr = buffer; uint32_t write_size = dev->info.write_ideal_size; uint32_t total_size = sizeof(*p) + write_size; p = malloc(total_size); if (!p) return -ENOMEM; packet_data = (uint8_t *)p + sizeof(*p); while (count) { p->offset = offset; p->size = write_size; memcpy(packet_data, ptr, write_size); res = ec_command(dev, EC_CMD_FLASH_WRITE, 1, p, total_size, NULL, 0); if (res) { ALOGW("Cannot write at %ld : %zd\n", offset, res); return res; } count -= write_size; ptr += write_size; offset += write_size; } return 0; } static int ec_erase(void *hnd, off_t offset, size_t count) { struct ec_data *dev = hnd; int res; struct ec_params_flash_erase erase; erase.offset = offset; erase.size = count; res = ec_command(dev, EC_CMD_FLASH_ERASE, 0, &erase, sizeof(erase), NULL, 0); if (res) { ALOGW("Cannot erase at %ld : %d\n", offset, res); return res; } return 0; } static size_t ec_get_size(void *hnd) { struct ec_data *dev = hnd; return dev && dev->fd > 0 ? dev->info.flash_size : 0; } static size_t ec_get_write_size(void *hnd) { struct ec_data *dev = hnd; return dev && dev->fd > 0 ? dev->info.write_ideal_size : 0; } static size_t ec_get_erase_size(void *hnd) { struct ec_data *dev = hnd; return dev && dev->fd > 0 ? dev->info.erase_block_size : 0; } static off_t ec_get_fmap_offset(void *hnd) { struct ec_data *dev = hnd; if (!hnd) return 0; /* * Try to find the FMAP signature at 64-byte boundaries * from the end of the RO region. */ return dev->ro_region.offset + dev->ro_region.size; } const struct flash_device_ops flash_ec_ops = { .name = "ec", .open = ec_open, .close = ec_close, .read = ec_read, .write = ec_write, .erase = ec_erase, .get_size = ec_get_size, .get_write_size = ec_get_write_size, .get_erase_size = ec_get_erase_size, .get_fmap_offset = ec_get_fmap_offset, .cmd = ec_command, };