1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-16 04:29:08 +00:00

feat(core/sdbackup): improve handling of mocked SD

By default, unit tests and Emulator starts without virtual SD card. A
card must be explicitly inserted in test setup.
This commit is contained in:
obrusvit 2024-01-20 13:04:24 +01:00
parent 3915171625
commit a429da5b0e
9 changed files with 69 additions and 51 deletions

View File

@ -17,6 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stdio.h>
#include <sys/mman.h>
#include "common.h"
#include "embed/extmod/trezorobj.h" #include "embed/extmod/trezorobj.h"
#include "py/mpconfig.h" #include "py/mpconfig.h"
#include "sdcard.h" #include "sdcard.h"
@ -70,7 +73,7 @@ STATIC mp_obj_t mod_trezorio_sdcard_switcher_insert(size_t n_args,
1024 * ONE_MEBIBYTE) // capacity between 1 MiB and 1 GiB 1024 * ONE_MEBIBYTE) // capacity between 1 MiB and 1 GiB
sdcard_mock.inserted = sectrue; sdcard_mock.inserted = sectrue;
set_sdcard_mock_filename(&sdcard_mock, (int)card_sn); set_sdcard_mock_filename((int)card_sn);
sdcard_mock.buffer = NULL; sdcard_mock.buffer = NULL;
sdcard_mock.serial_number = card_sn; sdcard_mock.serial_number = card_sn;
sdcard_mock.capacity_bytes = capacity_bytes; sdcard_mock.capacity_bytes = capacity_bytes;
@ -87,7 +90,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorio_sdcard_switcher_insert_obj, 1,
/// """ /// """
STATIC mp_obj_t mod_trezorio_sdcard_switcher_eject() { STATIC mp_obj_t mod_trezorio_sdcard_switcher_eject() {
sdcard_mock.inserted = secfalse; sdcard_mock.inserted = secfalse;
sdcard_mock.buffer = NULL;
if (sdcard_mock.buffer != NULL) {
// TODO repetion with unix/sdcard.c code
int r = munmap(sdcard_mock.buffer, sdcard_mock.capacity_bytes);
ensure(sectrue * (r == 0), "munmap failed");
sdcard_mock.buffer = NULL;
}
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_sdcard_switcher_eject_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_sdcard_switcher_eject_obj,

View File

@ -35,9 +35,10 @@
#define SDCARD_SIZE sdcard_mock.capacity_bytes #define SDCARD_SIZE sdcard_mock.capacity_bytes
#define SDCARD_BLOCKS (SDCARD_SIZE / SDCARD_BLOCK_SIZE) #define SDCARD_BLOCKS (SDCARD_SIZE / SDCARD_BLOCK_SIZE)
static secbool sdcard_powered = secfalse;
static void sdcard_exit(void) { static void sdcard_exit(void) {
if (SDCARD_BUFFER == NULL) {
return;
}
int r = munmap(SDCARD_BUFFER, SDCARD_SIZE); int r = munmap(SDCARD_BUFFER, SDCARD_SIZE);
ensure(sectrue * (r == 0), "munmap failed"); ensure(sectrue * (r == 0), "munmap failed");
SDCARD_BUFFER = NULL; SDCARD_BUFFER = NULL;
@ -78,7 +79,7 @@ void sdcard_init(void) {
for (int i = 0; i < SDCARD_SIZE; ++i) SDCARD_BUFFER[i] = 0xFF; for (int i = 0; i < SDCARD_SIZE; ++i) SDCARD_BUFFER[i] = 0xFF;
} }
sdcard_powered = secfalse; sdcard_mock.powered = secfalse;
atexit(sdcard_exit); atexit(sdcard_exit);
} }
@ -90,19 +91,19 @@ secbool sdcard_power_on(void) {
return secfalse; return secfalse;
} }
sdcard_init(); sdcard_init();
sdcard_powered = sectrue; sdcard_mock.powered = sectrue;
return sectrue; return sectrue;
} }
void sdcard_power_off(void) { sdcard_powered = secfalse; } void sdcard_power_off(void) { sdcard_mock.powered = secfalse; }
uint64_t sdcard_get_capacity_in_bytes(void) { uint64_t sdcard_get_capacity_in_bytes(void) {
return sdcard_powered == sectrue ? SDCARD_SIZE : 0; return sdcard_mock.powered == sectrue ? SDCARD_SIZE : 0;
} }
secbool sdcard_read_blocks(uint32_t *dest, uint32_t block_num, secbool sdcard_read_blocks(uint32_t *dest, uint32_t block_num,
uint32_t num_blocks) { uint32_t num_blocks) {
if (sectrue != sdcard_powered) { if (sectrue != sdcard_mock.powered) {
return secfalse; return secfalse;
} }
if (block_num >= SDCARD_BLOCKS) { if (block_num >= SDCARD_BLOCKS) {
@ -118,7 +119,7 @@ secbool sdcard_read_blocks(uint32_t *dest, uint32_t block_num,
secbool sdcard_write_blocks(const uint32_t *src, uint32_t block_num, secbool sdcard_write_blocks(const uint32_t *src, uint32_t block_num,
uint32_t num_blocks) { uint32_t num_blocks) {
if (sectrue != sdcard_powered) { if (sectrue != sdcard_mock.powered) {
return secfalse; return secfalse;
} }
if (block_num >= SDCARD_BLOCKS) { if (block_num >= SDCARD_BLOCKS) {

View File

@ -1,38 +1,26 @@
#include "sdcard_emu_mock.h" #include "sdcard_emu_mock.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "profile.h" #include "profile.h"
#include "sdcard.h" #include "sdcard.h"
// default SD Card filename serves for unit testing logic which requires SD card // By default, Emulator starts without mocked SD card, i.e. initially
// tests with emulator should call debuglink.insert_sdcard(...) // sdcard.is_present() == False
#define SDCARD_FILENAME_DEFAULT PROFILE_DIR_DEFAULT "/trezor.sdcard_def"
// default SD card data
SDCardMock sdcard_mock = { SDCardMock sdcard_mock = {
.inserted = sectrue, .inserted = secfalse,
.filename = SDCARD_FILENAME_DEFAULT, .powered = secfalse,
.filename = NULL,
.buffer = NULL, .buffer = NULL,
.serial_number = 1, .serial_number = 0,
.capacity_bytes = 64 * ONE_MEBIBYTE, .capacity_bytes = 0,
.blocks = (64 * ONE_MEBIBYTE) / SDCARD_BLOCK_SIZE, .blocks = 0 / SDCARD_BLOCK_SIZE,
.manuf_ID = 1, .manuf_ID = 0,
}; };
// "not inserted" SD card data void set_sdcard_mock_filename(int serial_number) {
/* SDCardMock sdcard_mock = { */ if (sdcard_mock.serial_number == serial_number) {
/* .inserted = secfalse, */ // serial_number determines the filename, so assuming the PROFILE_DIR
/* .filename = NULL, */ // doesn't change during a lifetime of the emulator, we can skip the rename
/* .buffer = NULL, */
/* .serial_number = 0, */
/* .capacity_bytes = 0, */
/* .blocks = 0 / SDCARD_BLOCK_SIZE, */
/* .manuf_ID = 0, */
/* }; */
void set_sdcard_mock_filename(SDCardMock *card, int serial_number) {
if (card == NULL) {
return; return;
} }
@ -58,11 +46,10 @@ void set_sdcard_mock_filename(SDCardMock *card, int serial_number) {
serial_number); serial_number);
// free the old filename // free the old filename
if (card->filename != NULL && if (sdcard_mock.filename != NULL) {
strcmp(card->filename, SDCARD_FILENAME_DEFAULT) != 0) { free(sdcard_mock.filename);
free(card->filename); sdcard_mock.filename = NULL;
card->filename = NULL;
} }
card->filename = new_filename; sdcard_mock.filename = new_filename;
} }

View File

@ -10,6 +10,7 @@
typedef struct { typedef struct {
secbool inserted; secbool inserted;
secbool powered;
char *filename; char *filename;
uint8_t *buffer; uint8_t *buffer;
uint32_t serial_number; uint32_t serial_number;
@ -20,6 +21,6 @@ typedef struct {
extern SDCardMock sdcard_mock; extern SDCardMock sdcard_mock;
void set_sdcard_mock_filename(SDCardMock *card, int serial_number); void set_sdcard_mock_filename(int serial_number);
#endif // __TREZOR_SDCARD_EMULATOR_MOCK_H__ #endif // __TREZOR_SDCARD_EMULATOR_MOCK_H__

View File

@ -118,11 +118,12 @@ def _from_env(name: str) -> bool:
@click.option("-r", "--record-dir", help="Directory where to record screen changes") @click.option("-r", "--record-dir", help="Directory where to record screen changes")
@click.option("-s", "--slip0014", is_flag=True, help="Initialize device with SLIP-14 seed (all all all...)") @click.option("-s", "--slip0014", is_flag=True, help="Initialize device with SLIP-14 seed (all all all...)")
@click.option("-S", "--script-gdb-file", type=click.Path(exists=True, dir_okay=False), help="Run gdb with an init file") @click.option("-S", "--script-gdb-file", type=click.Path(exists=True, dir_okay=False), help="Run gdb with an init file")
@click.option("--sdcard", "sdcard_present", is_flag=True, default=False, help="Run emulator with SD card inserted")
@click.option("-t", "--temporary-profile", is_flag=True, help="Create an empty temporary profile") @click.option("-t", "--temporary-profile", is_flag=True, help="Create an empty temporary profile")
@click.option("-w", "--watch", is_flag=True, help="Restart emulator if sources change") @click.option("-w", "--watch", is_flag=True, help="Restart emulator if sources change")
@click.option("-X", "--extra-arg", "extra_args", multiple=True, help="Extra argument to pass to micropython") @click.option("-X", "--extra-arg", "extra_args", multiple=True, help="Extra argument to pass to micropython")
# fmt: on
@click.argument("command", nargs=-1, type=click.UNPROCESSED) @click.argument("command", nargs=-1, type=click.UNPROCESSED)
# fmt: on
def cli( def cli(
disable_animation: bool, disable_animation: bool,
run_command: bool, run_command: bool,
@ -144,6 +145,7 @@ def cli(
record_dir: Optional[str], record_dir: Optional[str],
slip0014: bool, slip0014: bool,
script_gdb_file: str | Path | None, script_gdb_file: str | Path | None,
sdcard_present: bool,
temporary_profile: bool, temporary_profile: bool,
watch: bool, watch: bool,
extra_args: list[str], extra_args: list[str],
@ -240,6 +242,7 @@ def cli(
extra_args=extra_args, extra_args=extra_args,
main_args=main_args, main_args=main_args,
heap_size=heap_size, heap_size=heap_size,
sdcard_present=sdcard_present,
disable_animation=disable_animation, disable_animation=disable_animation,
workdir=SRC_DIR, workdir=SRC_DIR,
) )

View File

@ -9,10 +9,14 @@ class TestStorageSdSeedBackup(unittest.TestCase):
# TODO add more tests, also for repairing the backup card # TODO add more tests, also for repairing the backup card
def setUp(self): def setUp(self):
io.sdcard_switcher.insert(1)
self.mnemonic = ( self.mnemonic = (
b"crane mesh that gain predict open dice defy lottery toddler coin upgrade" b"crane mesh that gain predict open dice defy lottery toddler coin upgrade"
) )
def tearDown(self):
io.sdcard_switcher.eject()
def test_backup_and_restore(self): def test_backup_and_restore(self):
io.sdcard.power_on() io.sdcard.power_on()
io.fatfs.mkfs(True) io.fatfs.mkfs(True)

View File

@ -4,6 +4,11 @@ from trezor import io
class TestTrezorIoSdcard(unittest.TestCase): class TestTrezorIoSdcard(unittest.TestCase):
def setUp(self):
io.sdcard_switcher.insert(1)
def tearDown(self):
io.sdcard_switcher.eject()
def test_start(self): def test_start(self):
self.assertTrue(io.sdcard.is_present()) self.assertTrue(io.sdcard.is_present())
@ -31,8 +36,8 @@ class TestTrezorIoSdcard(unittest.TestCase):
def test_read_write(self): def test_read_write(self):
r = bytearray(8 * 512) r = bytearray(8 * 512)
w0 = bytearray(b'0' * (8 * 512)) w0 = bytearray(b"0" * (8 * 512))
w1 = bytearray(b'1' * (8 * 512)) w1 = bytearray(b"1" * (8 * 512))
io.sdcard.power_on() io.sdcard.power_on()
io.sdcard.write(0, w0) io.sdcard.write(0, w0)
io.sdcard.read(0, r) io.sdcard.read(0, r)
@ -43,5 +48,5 @@ class TestTrezorIoSdcard(unittest.TestCase):
io.sdcard.power_off() io.sdcard.power_off()
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -4,7 +4,14 @@ from trezor import io, sdcard
fatfs = io.fatfs fatfs = io.fatfs
class TestTrezorSdcard(unittest.TestCase): class TestTrezorSdcard(unittest.TestCase):
def setUp(self):
io.sdcard_switcher.insert(1)
def tearDown(self):
io.sdcard_switcher.eject()
def test_power(self): def test_power(self):
# sdcard.capacity() will return 0 if the card is not powered, # sdcard.capacity() will return 0 if the card is not powered,
# non-zero value otherwise # non-zero value otherwise

View File

@ -232,25 +232,21 @@ class CoreEmulator(Emulator):
port: Optional[int] = None, port: Optional[int] = None,
main_args: Sequence[str] = ("-m", "main"), main_args: Sequence[str] = ("-m", "main"),
workdir: Optional[Path] = None, workdir: Optional[Path] = None,
sdcard: Optional[bytes] = None,
disable_animation: bool = True, disable_animation: bool = True,
heap_size: str = "20M", heap_size: str = "20M",
sdcard_present: bool = False,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if workdir is not None: if workdir is not None:
self.workdir = Path(workdir).resolve() self.workdir = Path(workdir).resolve()
# FIXME does not work with switchable SD cards
# self.sdcard = self.profile_dir / "trezor.sdcard"
# if sdcard is not None:
# self.sdcard.write_bytes(sdcard)
if port: if port:
self.port = port self.port = port
self.disable_animation = disable_animation self.disable_animation = disable_animation
self.main_args = list(main_args) self.main_args = list(main_args)
self.heap_size = heap_size self.heap_size = heap_size
self.sdcard_present = sdcard_present
def make_env(self) -> Dict[str, str]: def make_env(self) -> Dict[str, str]:
env = super().make_env() env = super().make_env()
@ -275,6 +271,11 @@ class CoreEmulator(Emulator):
+ self.extra_args + self.extra_args
) )
def start(self) -> None:
super().start()
if self.sdcard_present:
self.client.debug.insert_sd_card()
def stop(self) -> None: def stop(self) -> None:
super().stop() super().stop()
for i in range(1, 17): for i in range(1, 17):