344 lines
11 KiB
C
344 lines
11 KiB
C
// SPDX-FileCopyrightText: 2021 Florian Märkl <info@florianmaerkl.de>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_io.h>
|
|
#include "minunit.h"
|
|
|
|
static const char *ihex_sample =
|
|
":020000021000EC\r\n"
|
|
":10010000214601360121470136007EFE09D2190140\r\n"
|
|
":100110002146017EB7C20001FF5F16002148011988\r\n"
|
|
":020000022000DC\r\n"
|
|
":10010000194E79234623965778239EDA3F01B2CAC7\r\n"
|
|
":100110003F0156702B5E712B722B732146013421E7\r\n"
|
|
":00000001FF\r\n";
|
|
|
|
char *create_sample_file(const char *prefix) {
|
|
char *filename = rz_file_temp(prefix);
|
|
int fd = open(filename, O_RDWR | O_CREAT, 0644);
|
|
rz_xwrite(fd, ihex_sample, strlen(ihex_sample));
|
|
close(fd);
|
|
return filename;
|
|
}
|
|
|
|
bool test_rz_io_ihex_read(void) {
|
|
RzIO *io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
|
|
char *filename = create_sample_file("ihex0");
|
|
char *uri = rz_str_newf("ihex://%s", filename);
|
|
rz_io_open_nomap(io, uri, RZ_PERM_R, 0);
|
|
free(uri);
|
|
|
|
ut8 buf[8];
|
|
bool r = rz_io_read_at_mapped(io, 0, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xff\xff\xff\xff\xff\xff", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0x10100, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\x21\x46\x01\x36\x01\x21\x47\x01", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0x1010e, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\x19\x01\x21\x46\x01\x7e\xb7\xc2", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0x200fe, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\x19\x4e\x79\x23\x46\x23", sizeof(buf), "read");
|
|
|
|
rz_io_free(io);
|
|
rz_file_rm(filename);
|
|
free(filename);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_rz_io_ihex_write(void) {
|
|
char *filename = create_sample_file("ihex1");
|
|
char *uri = rz_str_newf("ihex://%s", filename);
|
|
|
|
RzIO *io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
RzIODesc *desc = rz_io_open_nomap(io, uri, RZ_PERM_RW, 0);
|
|
mu_assert_notnull(desc, "open");
|
|
|
|
// simple write contained entirely within one source chunk
|
|
bool r = rz_io_write_at(io, 0x10101, (const ut8 *)"Ulu", 3);
|
|
mu_assert_true(r, "write success");
|
|
ut8 buf[8];
|
|
r = rz_io_read_at_mapped(io, 0x10100, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\x21Ulu\x01\x21\x47\x01", sizeof(buf), "read");
|
|
|
|
// write crossing source chunk boundaries
|
|
r = rz_io_write_at(io, 0x1010e, (const ut8 *)"Mulu", 4);
|
|
mu_assert_true(r, "write success");
|
|
r = rz_io_read_at_mapped(io, 0x1010d, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xd2Mulu\x01\x7e\xb7", sizeof(buf), "read");
|
|
|
|
// write beyond source chunk boundaries
|
|
r = rz_io_write_at(io, 0x1011e, (const ut8 *)"UrShak", 6);
|
|
mu_assert_true(r, "write success");
|
|
r = rz_io_read_at_mapped(io, 0x1011d, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\x48UrShak\xff", sizeof(buf), "read");
|
|
|
|
// write before and into source chunk boundaries
|
|
r = rz_io_write_at(io, 0x200fe, (const ut8 *)"Tarrok", 6);
|
|
mu_assert_true(r, "write success");
|
|
r = rz_io_read_at_mapped(io, 0x200fd, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xffTarrok\x46", sizeof(buf), "read");
|
|
|
|
// write outside any chunks
|
|
r = rz_io_write_at(io, 0x10300, (const ut8 *)"Krushak", 7);
|
|
mu_assert_true(r, "write success");
|
|
r = rz_io_read_at_mapped(io, 0x102ff, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xffKrushak", sizeof(buf), "read");
|
|
|
|
// write that ends exactly at an extended addr boundary
|
|
r = rz_io_write_at(io, 0xfff8, (const ut8 *)"01234567", 8);
|
|
mu_assert_true(r, "write success");
|
|
r = rz_io_read_at_mapped(io, 0xfff8, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"01234567", sizeof(buf), "read");
|
|
|
|
// write far out in the open space
|
|
mu_assert_eq(rz_io_desc_size(desc), 0x20120, "desc size");
|
|
r = rz_io_write_at(io, 0x40000, (const ut8 *)"Arecibo", 7);
|
|
mu_assert_true(r, "write success");
|
|
// writing outside also resizes
|
|
mu_assert_eq(rz_io_desc_size(desc), 0x40007, "desc size");
|
|
r = rz_io_read_at_mapped(io, 0x3ffff, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xff"
|
|
"Arecibo",
|
|
sizeof(buf), "read");
|
|
|
|
rz_io_free(io);
|
|
|
|
const char *file_expect =
|
|
":08FFF800303132333435363765\n"
|
|
":020000040001F9\n"
|
|
":1001000021556c750121470136007efe09d24d75df\n"
|
|
":100110006c75017eb7c20001ff5f16002148557261\n"
|
|
":040120005368616B54\n"
|
|
":070300004B72757368616B1D\n"
|
|
":020000040002F8\n"
|
|
":1000fe00546172726f6b4623965778239eda3f01d6\n"
|
|
":02010E00B2CA73\n"
|
|
":100110003F0156702B5E712B722B732146013421E7\n"
|
|
":020000040004F6\n"
|
|
":070000004172656369626F44\n"
|
|
":00000001FF\n";
|
|
char *res = rz_file_slurp(filename, NULL);
|
|
rz_str_remove_char(res, '\r');
|
|
mu_assert_streq(res, file_expect, "written ihex file");
|
|
free(res);
|
|
|
|
// reopen and check reads again
|
|
io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
rz_io_open_nomap(io, uri, RZ_PERM_R, 0);
|
|
r = rz_io_read_at_mapped(io, 0x10100, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\x21Ulu\x01\x21\x47\x01", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0x1010d, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xd2Mulu\x01\x7e\xb7", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0x1011d, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\x48UrShak\xff", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0x200fd, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xffTarrok\x46", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0x102ff, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xffKrushak", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0xfff8, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"01234567", sizeof(buf), "read");
|
|
r = rz_io_read_at_mapped(io, 0x3ffff, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xff"
|
|
"Arecibo",
|
|
sizeof(buf), "read");
|
|
rz_io_free(io);
|
|
|
|
rz_file_rm(filename);
|
|
free(filename);
|
|
free(uri);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_rz_io_ihex_write_large(void) {
|
|
// write of a single chunk spanning more than 2 16bit pages
|
|
|
|
char *filename = create_sample_file("ihex1");
|
|
char *uri = rz_str_newf("ihex://%s", filename);
|
|
|
|
RzIO *io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
RzIODesc *desc = rz_io_open_nomap(io, uri, RZ_PERM_RW, 0);
|
|
mu_assert_notnull(desc, "open");
|
|
|
|
#define DATA_SIZE 0x26219 // some large size, more than 2 16bit pages
|
|
ut8 data[DATA_SIZE];
|
|
for (size_t i = 0; i < sizeof(data); i++) {
|
|
data[i] = rand();
|
|
}
|
|
|
|
bool r = rz_io_write_at(io, 0x1337, data, sizeof(data));
|
|
mu_assert_true(r, "write success");
|
|
|
|
ut8 tmp[DATA_SIZE];
|
|
#undef DATA_SIZE
|
|
r = rz_io_read_at_mapped(io, 0x1337, tmp, sizeof(tmp));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_true(!memcmp(tmp, data, sizeof(data)), "re-read in memory"); // faster for this large size than mu_assert_memeq
|
|
rz_io_free(io);
|
|
|
|
// reopen and check again
|
|
io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
rz_io_open_nomap(io, uri, RZ_PERM_R, 0);
|
|
r = rz_io_read_at_mapped(io, 0x1337, tmp, sizeof(tmp));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_true(!memcmp(tmp, data, sizeof(data)), "re-read from file"); // faster for this large size than mu_assert_memeq
|
|
rz_io_free(io);
|
|
|
|
rz_file_rm(filename);
|
|
free(filename);
|
|
free(uri);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_rz_io_ihex_resize_bigger(void) {
|
|
char *filename = create_sample_file("ihex2");
|
|
char *uri = rz_str_newf("ihex://%s", filename);
|
|
|
|
RzIO *io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
RzIODesc *desc = rz_io_open_nomap(io, uri, RZ_PERM_RW, 0);
|
|
mu_assert_notnull(desc, "open");
|
|
|
|
mu_assert_eq(rz_io_desc_size(desc), 0x20120, "desc size");
|
|
bool r = rz_io_desc_resize(desc, 0x30000);
|
|
mu_assert_true(r, "resized");
|
|
mu_assert_eq(rz_io_desc_size(desc), 0x30000, "desc size");
|
|
|
|
// Resizing to something bigger will write a single 0xff at the end since the size is
|
|
// defined as the address after the last populated byte.
|
|
// This behavior is debatable, but Intel Hex doesn't really have any notion of size so
|
|
// it can actually make sense.
|
|
|
|
// write in new space
|
|
r = rz_io_write_at(io, 0x21000, (const ut8 *)"HoshPak", 7);
|
|
mu_assert_true(r, "write success");
|
|
ut8 buf[8];
|
|
r = rz_io_read_at_mapped(io, 0x20fff, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xffHoshPak", sizeof(buf), "read");
|
|
|
|
rz_io_free(io);
|
|
|
|
const char *file_expect =
|
|
":020000040001F9\n"
|
|
":10010000214601360121470136007EFE09D2190140\n"
|
|
":100110002146017EB7C20001FF5F16002148011988\n"
|
|
":020000040002F8\n"
|
|
":10010000194E79234623965778239EDA3F01B2CAC7\n"
|
|
":100110003F0156702B5E712B722B732146013421E7\n"
|
|
":07100000486F736850616B3B\n"
|
|
":01FFFF00FF02\n"
|
|
":00000001FF\n";
|
|
char *res = rz_file_slurp(filename, NULL);
|
|
rz_str_remove_char(res, '\r');
|
|
mu_assert_streq(res, file_expect, "written ihex file");
|
|
free(res);
|
|
|
|
// reopen and check again
|
|
io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
desc = rz_io_open_nomap(io, uri, RZ_PERM_R, 0);
|
|
mu_assert_notnull(desc, "reopen");
|
|
mu_assert_eq(rz_io_desc_size(desc), 0x30000, "desc size");
|
|
r = rz_io_read_at_mapped(io, 0x20fff, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\xffHoshPak", sizeof(buf), "read");
|
|
rz_io_free(io);
|
|
|
|
rz_file_rm(filename);
|
|
free(filename);
|
|
free(uri);
|
|
mu_end;
|
|
}
|
|
|
|
bool test_rz_io_ihex_resize_smaller(void) {
|
|
char *filename = create_sample_file("ihex3");
|
|
char *uri = rz_str_newf("ihex://%s", filename);
|
|
|
|
RzIO *io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
RzIODesc *desc = rz_io_open_nomap(io, uri, RZ_PERM_RW, 0);
|
|
mu_assert_notnull(desc, "open");
|
|
|
|
mu_assert_eq(rz_io_desc_size(desc), 0x20120, "desc size");
|
|
bool r = rz_io_desc_resize(desc, 0x20108);
|
|
mu_assert_true(r, "resized");
|
|
mu_assert_eq(rz_io_desc_size(desc), 0x20108, "desc size");
|
|
|
|
ut8 buf[8];
|
|
r = rz_io_read_at_mapped(io, 0x20104, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\x46\x23\x96\x57\xff\xff\xff\xff", sizeof(buf), "read");
|
|
|
|
rz_io_free(io);
|
|
|
|
const char *file_expect =
|
|
":020000040001F9\n"
|
|
":10010000214601360121470136007EFE09D2190140\n"
|
|
":100110002146017EB7C20001FF5F16002148011988\n"
|
|
":020000040002F8\n"
|
|
":08010000194E7923462396579E\n"
|
|
":00000001FF\n";
|
|
char *res = rz_file_slurp(filename, NULL);
|
|
rz_str_remove_char(res, '\r');
|
|
mu_assert_streq(res, file_expect, "written ihex file");
|
|
free(res);
|
|
|
|
// reopen and check again
|
|
io = rz_io_new();
|
|
io->ff = true;
|
|
io->Oxff = 0xff;
|
|
desc = rz_io_open_nomap(io, uri, RZ_PERM_R, 0);
|
|
mu_assert_notnull(desc, "reopen");
|
|
mu_assert_eq(rz_io_desc_size(desc), 0x20108, "desc size");
|
|
r = rz_io_read_at_mapped(io, 0x20104, buf, sizeof(buf));
|
|
mu_assert_true(r, "read success");
|
|
mu_assert_memeq(buf, (const ut8 *)"\x46\x23\x96\x57\xff\xff\xff\xff", sizeof(buf), "read");
|
|
rz_io_free(io);
|
|
|
|
rz_file_rm(filename);
|
|
free(filename);
|
|
free(uri);
|
|
mu_end;
|
|
}
|
|
|
|
bool all_tests(void) {
|
|
mu_run_test(test_rz_io_ihex_read);
|
|
mu_run_test(test_rz_io_ihex_write);
|
|
mu_run_test(test_rz_io_ihex_write_large);
|
|
mu_run_test(test_rz_io_ihex_resize_bigger);
|
|
mu_run_test(test_rz_io_ihex_resize_smaller);
|
|
return tests_passed != tests_run;
|
|
}
|
|
|
|
mu_main(all_tests)
|