217 lines
5.1 KiB
C
217 lines
5.1 KiB
C
/*
|
|
+ * 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;
|
|
}
|