| /* |
| + * Copyright (C) 2016 Freescale Semiconductor, Inc. |
| + * Copyright 2018 NXP |
| + * |
| + * SPDX-License-Identifier: GPL-2.0+ |
| + */ |
| #include <common.h> |
| #include <stdlib.h> |
| |
| #include "debug.h" |
| #include "utils.h" |
| |
| /* |
| * get margin_pos struct from offset [to the partition start/end] and |
| * num_bytes to read/write |
| */ |
| int get_margin_pos(uint64_t part_start, uint64_t part_end, unsigned long blksz, |
| margin_pos_t *margin, int64_t offset, size_t num_bytes, |
| bool allow_partial) { |
| long off; |
| if (margin == NULL) |
| return -1; |
| |
| if (blksz == 0 || part_start > part_end) |
| return -1; |
| |
| if (offset < 0) { |
| margin->blk_start = (offset + 1) / (uint64_t)blksz + part_end; |
| // offset == -1 means the last byte?, or start need -1 |
| margin->start = (off = offset % (uint64_t)blksz) == 0 ? |
| 0 : blksz + off; |
| if (offset + num_bytes - 1 >= 0) { |
| if (!allow_partial) |
| return -1; |
| margin->blk_end = part_end; |
| margin->end = blksz - 1; |
| } else { |
| // which blk the last byte is in |
| margin->blk_end = (num_bytes + offset) / |
| (uint64_t)blksz + part_end; |
| margin->end = (off = (num_bytes + offset - 1) % |
| (uint64_t)blksz) == 0 ? |
| 0 : blksz + off; // last byte |
| } |
| } else { |
| margin->blk_start = offset / (uint64_t)blksz + part_start; |
| margin->start = offset % (uint64_t)blksz; |
| margin->blk_end = ((offset + num_bytes - 1) / (uint64_t)blksz) + |
| part_start ; |
| margin->end = (offset + num_bytes - 1) % (uint64_t)blksz; |
| if (margin->blk_end > part_end) { |
| if (!allow_partial) |
| return -1; |
| margin->blk_end = part_end; |
| margin->end = blksz - 1; |
| } |
| } |
| VDEBUG("bs=%ld, be=%ld, s=%ld, e=%ld\n", |
| margin->blk_start, margin->blk_end, margin->start, margin->end); |
| |
| if (margin->blk_start > part_end || margin->blk_start < part_start) |
| return -1; |
| long multi = margin->blk_end - margin->blk_start - 1 + |
| (margin->start == 0) + (margin->end == blksz -1); |
| margin->multi = multi > 0 ? multi : 0; |
| VDEBUG("bm=%ld\n", margin->multi); |
| return 0; |
| } |
| |
| int read_from_partition_in_bytes(struct blk_desc *fs_dev_desc, |
| disk_partition_t *info, int64_t offset, |
| size_t num_bytes, void* buffer, |
| size_t* out_num_read) |
| { |
| unsigned char *bdata; |
| unsigned char *out_buf = (unsigned char *)buffer; |
| unsigned char *dst, *dst64 = NULL; |
| unsigned long blksz; |
| unsigned long s, cnt; |
| size_t num_read = 0; |
| lbaint_t part_start, part_end, bs, be, bm, blk_num; |
| margin_pos_t margin; |
| int ret; |
| |
| if(buffer == NULL || out_num_read == NULL) { |
| printf("NULL pointer error!\n"); |
| return -1; |
| } |
| |
| blksz = fs_dev_desc->blksz; |
| part_start = info->start; |
| part_end = info->start + info->size - 1; |
| |
| if (get_margin_pos((uint64_t)part_start, (uint64_t)part_end, blksz, |
| &margin, offset, num_bytes, true)) |
| return -1; |
| |
| bs = (lbaint_t)margin.blk_start; |
| be = (lbaint_t)margin.blk_end; |
| s = margin.start; |
| bm = margin.multi; |
| |
| /* alloc a blksz mem */ |
| bdata = (unsigned char *)memalign(ALIGN_BYTES, blksz); |
| if (bdata == NULL) { |
| printf("Failed to allocate memory!\n"); |
| return -1; |
| } |
| |
| /* support multi blk read */ |
| while (bs <= be) { |
| if (!s && bm > 1) { |
| dst = out_buf; |
| /* for mmc blk read alignment */ |
| dst64 = PTR_ALIGN(out_buf, 64); |
| if (dst64 != dst) { |
| dst = dst64; |
| bm--; |
| } |
| blk_num = bm; |
| cnt = bm * blksz; |
| bm = 0; /* no more multi blk */ |
| } else { |
| blk_num = 1; |
| cnt = blksz - s; |
| if (num_read + cnt > num_bytes) |
| cnt = num_bytes - num_read; |
| dst = bdata; |
| } |
| if (!blk_dread(fs_dev_desc, bs, blk_num, dst)) { |
| ret = -1; |
| goto fail; |
| } |
| |
| if (dst == bdata) |
| memcpy(out_buf, bdata + s, cnt); |
| else if (dst == dst64) |
| memcpy(out_buf, dst, cnt); /* internal copy */ |
| |
| s = 0; |
| bs += blk_num; |
| num_read += cnt; |
| out_buf += cnt; |
| } |
| *out_num_read = num_read; |
| ret = 0; |
| |
| fail: |
| free(bdata); |
| return ret; |
| } |
| |
| int write_to_partition_in_bytes(struct blk_desc *fs_dev_desc, |
| disk_partition_t *info, int64_t offset, |
| size_t num_bytes, |
| void* buffer, size_t *out_num_write) |
| { |
| unsigned char *bdata; |
| unsigned char *in_buf = (unsigned char *)buffer; |
| unsigned long blksz; |
| unsigned long s, cnt; |
| size_t num_write = 0; |
| lbaint_t part_start, part_end, bs; |
| margin_pos_t margin; |
| int ret; |
| |
| if(buffer == NULL || out_num_write == NULL) { |
| printf("NULL pointer error!\n"); |
| return -1; |
| } |
| |
| blksz = fs_dev_desc->blksz; |
| part_start = info->start; |
| part_end = info->start + info->size - 1; |
| |
| if(get_margin_pos((uint64_t)part_start, (uint64_t)part_end, blksz, |
| &margin, offset, num_bytes, false)) |
| return -1; |
| |
| bs = (lbaint_t)margin.blk_start; |
| s = margin.start; |
| |
| // alloc a blksz mem |
| bdata = (unsigned char *)memalign(ALIGN_BYTES, blksz); |
| if (bdata == NULL) |
| return -1; |
| |
| while (num_write < num_bytes) { |
| memset(bdata, 0, blksz); |
| cnt = blksz - s; |
| if (num_write + cnt > num_bytes) |
| cnt = num_bytes - num_write; |
| if (!s || cnt != blksz) { //read blk first |
| if (!blk_dread(fs_dev_desc, bs, 1, |
| bdata)) { |
| ret = -1; |
| goto fail; |
| } |
| } |
| memcpy(bdata + s, in_buf, cnt); //change data |
| if (!blk_dwrite(fs_dev_desc, bs, 1, bdata)) { |
| ret = -1; |
| goto fail; |
| } |
| bs++; |
| num_write += cnt; |
| in_buf += cnt; |
| s = 0; |
| } |
| *out_num_write = num_write; |
| ret = 0; |
| |
| fail: |
| free(bdata); |
| return ret; |
| } |