/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2020-2020. All rights reserved. * Description: emmc module driver * Author: Hisilicon * Create: 2020-10-15 */ #define LARGEFILE64_SOURCE #include "emmc_raw.h" #include "soc_log.h" #include #include #include #include "uapi_flash.h" #include "nand.h" #include "flash_ext.h" #include "cmdline_parts.h" #include "securec.h" static emmc_flash_s g_emmc_flash; #define EMMC_RAW_AREA_START 0 td_s32 emmc_find_part_from_devname(const td_char *media_name, td_char *bootargs, const td_char *devname, td_u64 *start, td_u64 *size) { td_u8 partnum; td_char *tmp = NULL; if ((media_name == NULL) || (*media_name == '\0')) { return TD_FAILURE; } if ((devname == NULL) || (*devname == '\0')) { return TD_FAILURE; } if ((bootargs == NULL) || (*bootargs == '\0')) { return TD_FAILURE; } tmp = strstr(bootargs, "blkdevparts="); if (tmp == NULL) { return TD_FAILURE; } tmp += strlen("blkdevparts="); if (!strstr(bootargs, media_name)) { return TD_FAILURE; } tmp = strstr(devname, "mmcblk0p"); if (tmp == NULL) { return TD_FAILURE; } tmp += strlen("mmcblk0p"); partnum = (td_u8)strtol(tmp, NULL, 10); /* 10 - number position */ if (get_part_info(partnum, start, size) != 0) { return TD_FAILURE; } return TD_SUCCESS; } static td_s32 emmc_raw_check_block(td_void) { td_s32 fd; td_u8 buf[BUF_LINE_SIZE]; const td_char *device_node = "/dev/block/mmcblk0"; const td_char *device_node_linux = "/dev/mmcblk0"; if (access(device_node, F_OK) == -1) { if (access(device_node_linux, F_OK) == -1) { return TD_FAILURE; } fd = open(device_node_linux, O_RDWR); } else { fd = open(device_node, O_RDWR); } if (fd == -1) { soc_print("open mmcblk0 failed, errno=%d\n", errno); return TD_FAILURE; } g_emmc_flash.erase_size = EMMC_SECTOR_SIZE; memset_s(buf, sizeof(buf), 0, sizeof(buf)); if (read(fd, buf, sizeof(buf) - 1) != ((ssize_t)sizeof(buf) - 1)) { soc_print("Failed to read dev, errno=%d\n", errno); close(fd); return TD_FAILURE; } close(fd); return TD_SUCCESS; } static td_s32 emmc_raw_check_size(td_void) { td_s32 fd; td_u64 part_size; td_u8 buf[BUF_LINE_SIZE]; /* Raw area start from 512, after MBR.. */ g_emmc_flash.raw_area_start = EMMC_RAW_AREA_START; fd = open("/sys/block/mmcblk0/size", O_RDONLY); if (fd < 0) { soc_print("Fail to open the size of mmcblk0\n"); return TD_FAILURE; } memset_s(buf, sizeof(buf), 0, sizeof(buf)); if (read(fd, buf, sizeof(buf) - 1) <= 0) { soc_print("Failed to read the size of mmcblk0\n"); close(fd); return TD_FAILURE; } close(fd); part_size = (td_u64)strtoull((td_char *)buf, NULL, STRTOULL_BASE_DEC); if (part_size * EMMC_SECTOR_SIZE > MAX_U64) { return TD_FAILURE; } g_emmc_flash.raw_area_size = part_size * EMMC_SECTOR_SIZE - EMMC_RAW_AREA_START; return TD_SUCCESS; } td_s32 emmc_raw_init(td_char *bootargs) { if (bootargs == NULL) { soc_print("Invalid parameter, bootargs NULL\n"); return TD_FAILURE; } if (cmdline_parts_init(bootargs) < 0) { return TD_FAILURE; } if (emmc_raw_check_block() == TD_FAILURE) { return TD_FAILURE; } if (emmc_raw_check_size() == TD_FAILURE) { return TD_FAILURE; } return TD_SUCCESS; } static td_s32 emmc_flash_probe(td_void) { td_s32 dev; const td_char *device_node = "/dev/block/mmcblk0"; const td_char *device_node_linux = "/dev/mmcblk0"; dev = open(device_node, O_RDWR | O_SYNC); if (dev == -1) { dev = open(device_node_linux, O_RDWR | O_SYNC); if (dev == -1) { soc_print("Failed to open device '/dev/mmcblk0'"); return TD_FAILURE; } } return (dev); } static td_s32 emmc_raw_open_check(td_u64 addr, td_u64 length) { /* Reject open, which are not block aligned */ if (((addr & (g_emmc_flash.erase_size - 1)) != 0) || ((length & (g_emmc_flash.erase_size - 1)) != 0)) { soc_print("Attempt to open non block aligned, " "eMMC blocksize: 0x%x, addr: 0x%08llx, length: 0x%08llx\n", g_emmc_flash.erase_size, addr, length); return TD_FAILURE; } if ((addr > g_emmc_flash.raw_area_start + g_emmc_flash.raw_area_size) || (length > g_emmc_flash.raw_area_start + g_emmc_flash.raw_area_size) || ((addr + length) > g_emmc_flash.raw_area_start + g_emmc_flash.raw_area_size)) { soc_print("Attempt to open outside the flash area, " "eMMC chipsize: 0x%08llx, addr: 0x%08llx, length: 0x%08llx\n", g_emmc_flash.raw_area_start + g_emmc_flash.raw_area_size, addr, length); return TD_FAILURE; } return TD_SUCCESS; } static emmc_cb_s *emmc_raw_malloc_cb(td_s32 fd, td_u64 addr, td_u64 length) { emmc_cb_s *emmc_cb = (emmc_cb_s *)malloc(sizeof(emmc_cb_s)); if (emmc_cb == NULL) { soc_print("no many memory"); return NULL; } memset_s(emmc_cb, sizeof(emmc_cb_s), 0, sizeof(emmc_cb_s)); emmc_cb->address = addr; emmc_cb->part_size = length; emmc_cb->erase_size = g_emmc_flash.erase_size; emmc_cb->fd = fd; emmc_cb->part_type = EMMC_PART_TYPE_RAW; return emmc_cb; } emmc_cb_s *emmc_raw_open(td_u64 addr, td_u64 length) { emmc_cb_s *emmc_cb = NULL; td_s32 fd; fd = emmc_flash_probe(); if (fd == -1) { soc_print("no devices available"); return NULL; } if (emmc_raw_open_check(addr, length) != TD_SUCCESS) { close(fd); return NULL; } emmc_cb = emmc_raw_malloc_cb(fd, addr, length); if (emmc_cb == NULL) { close(fd); return NULL; } return emmc_cb; } emmc_cb_s *emmc_node_open(const td_u8 *node) { emmc_cb_s *emmc_cb = NULL; td_s32 fd = -1; td_char tmp_node[PATH_MAX] = {0}; if (node == NULL) { return NULL; } if (realpath((const td_char*)node, tmp_node) == NULL) { soc_print("Failed to realpath %s.\n", node); return NULL; } fd = open((const td_char*)tmp_node, O_RDWR); if (fd == -1) { soc_print("no devices available"); return NULL; } emmc_cb = (emmc_cb_s *)malloc(sizeof(emmc_cb_s)); if (emmc_cb == NULL) { soc_print("No enough space"); close(fd); return NULL; } memset_s(emmc_cb, sizeof(emmc_cb_s), 0, sizeof(emmc_cb_s)); emmc_cb->address = 0; emmc_cb->part_size = 0; emmc_cb->erase_size = g_emmc_flash.erase_size; emmc_cb->fd = fd; emmc_cb->part_type = EMMC_PART_TYPE_LOGIC; return emmc_cb; } td_s32 emmc_block_read(td_s32 fd, td_u64 start, td_u32 len, td_u8 *buf) { td_s32 ret; if (lseek64(fd, (off64_t)start, SEEK_SET) == -1) { soc_print("Failed to lseek64"); return TD_FAILURE; } if (buf == NULL) { return TD_FAILURE; } ret = (td_s32)read(fd, buf, len); if (ret < 0) { return TD_FAILURE; } return ret; } td_s32 emmc_block_write(td_s32 fd, td_u64 start, td_u32 len, const td_u8 *buf) { td_s32 ret; if (lseek64(fd, (off64_t)start, SEEK_SET) == -1) { soc_print("Failed to lseek64.\n"); return TD_FAILURE; } if (buf == NULL) { return TD_FAILURE; } ret = (td_s32)write(fd, buf, len); if (ret < 0) { soc_print("Failed to write!\n"); return TD_FAILURE; } return ret; } static td_s32 emmc_raw_forbid(const emmc_cb_s *emmc_cb, td_u64 offset, td_u32 length) { return ((offset > emmc_cb->part_size) || (length > emmc_cb->part_size) || ((offset + length) > emmc_cb->part_size)); } static td_s32 emmc_raw_rw_check(const emmc_cb_s *emmc_cb, td_u64 offset, td_u32 length, const td_u8 *buf) { if ((emmc_cb == NULL) || (buf == NULL)) { soc_print("Pointer is null"); return TD_FAILURE; } /* Reject read, which are not block aligned */ if (emmc_cb->part_type == EMMC_PART_TYPE_RAW) { if (emmc_raw_forbid(emmc_cb, offset, length) != 0) { soc_print("Attempt to write outside the flash handle area, eMMC part size: 0x%08llx, offset: 0x%08llx, " "length: 0x%08x.\n", emmc_cb->part_size, offset, length); return TD_FAILURE; } } return TD_SUCCESS; } /* should be alignment with emmc block size */ td_s32 emmc_raw_read(const emmc_cb_s *emmc_cb, td_u64 offset, td_u32 length, td_u8 *buf) { td_u64 start; if (emmc_cb == NULL) { soc_print("emmc_cb is null"); return TD_FAILURE; } if (emmc_raw_rw_check(emmc_cb, offset, length, buf) != TD_SUCCESS) { return TD_FAILURE; } start = emmc_cb->address + offset; return emmc_block_read(emmc_cb->fd, start, length, buf); } /* should be alignment with emmc block size */ td_s32 emmc_raw_write(const emmc_cb_s *emmc_cb, td_u64 offset, td_u32 length, const td_u8 *buf) { td_u64 start; if (emmc_cb == NULL) { soc_print("emmc_cb is null"); return TD_FAILURE; } if (emmc_raw_rw_check(emmc_cb, offset, length, buf) != TD_SUCCESS) { return TD_FAILURE; } start = emmc_cb->address + offset; return emmc_block_write(emmc_cb->fd, start, length, buf); } td_s32 emmc_raw_close(emmc_cb_s *emmc_cb) { if (emmc_cb == NULL) { soc_print("Pointer is null"); return TD_FAILURE; } close(emmc_cb->fd); free(emmc_cb); return TD_SUCCESS; } #define MAX_HANDLE MAX_PARTS td_void emmc_raw_get_info(td_handle flash, uapi_flash_info *flash_info, emmc_cb_s *emmc_cb) { if ((emmc_cb == NULL) || (flash_info == NULL)) { soc_print("Pointer is null"); return; } if (flash >= MAX_HANDLE) { return; } /* 16 - A block contains the number of erase */ if ((g_emmc_flash.erase_size * 16) > MAX_U32) { return; } memset_s(flash_info, sizeof(*flash_info), 0, sizeof(*flash_info)); flash_info->total_size = g_emmc_flash.raw_area_size; flash_info->oob_size = 0; flash_info->block_size = (g_emmc_flash.erase_size * 16); /* 16 - A block contains the number of erase */ flash_info->page_size = 0; flash_info->flash_type = UAPI_FLASH_TYPE_EMMC_0; flash_info->open_addr = emmc_cb->address; flash_info->open_len = emmc_cb->part_size; }