14f6ad66aSAchin Gupta /* 2e83b0cadSDan Handley * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. 34f6ad66aSAchin Gupta * 4*82cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause 54f6ad66aSAchin Gupta */ 64f6ad66aSAchin Gupta 74f6ad66aSAchin Gupta #include <assert.h> 84f6ad66aSAchin Gupta #include <errno.h> 94f6ad66aSAchin Gupta #include <semihosting.h> 1097043ac9SDan Handley #include <string.h> 114f6ad66aSAchin Gupta 124f6ad66aSAchin Gupta #ifndef SEMIHOSTING_SUPPORTED 134f6ad66aSAchin Gupta #define SEMIHOSTING_SUPPORTED 1 144f6ad66aSAchin Gupta #endif 154f6ad66aSAchin Gupta 16c6bc0710SDan Handley long semihosting_call(unsigned long operation, 174f6ad66aSAchin Gupta void *system_block_address); 184f6ad66aSAchin Gupta 194f6ad66aSAchin Gupta typedef struct { 204f6ad66aSAchin Gupta const char *file_name; 21cd529320SRyan Harkin unsigned long mode; 22cd529320SRyan Harkin size_t name_length; 23fb037bfbSDan Handley } smh_file_open_block_t; 244f6ad66aSAchin Gupta 254f6ad66aSAchin Gupta typedef struct { 26cd529320SRyan Harkin long handle; 27625de1d4SDan Handley uintptr_t buffer; 28cd529320SRyan Harkin size_t length; 29fb037bfbSDan Handley } smh_file_read_write_block_t; 304f6ad66aSAchin Gupta 314f6ad66aSAchin Gupta typedef struct { 32cd529320SRyan Harkin long handle; 33cd529320SRyan Harkin ssize_t location; 34fb037bfbSDan Handley } smh_file_seek_block_t; 354f6ad66aSAchin Gupta 364f6ad66aSAchin Gupta typedef struct { 374f6ad66aSAchin Gupta char *command_line; 38cd529320SRyan Harkin size_t command_length; 39fb037bfbSDan Handley } smh_system_block_t; 404f6ad66aSAchin Gupta 41cd529320SRyan Harkin long semihosting_connection_supported(void) 424f6ad66aSAchin Gupta { 434f6ad66aSAchin Gupta return SEMIHOSTING_SUPPORTED; 444f6ad66aSAchin Gupta } 454f6ad66aSAchin Gupta 46cd529320SRyan Harkin long semihosting_file_open(const char *file_name, size_t mode) 474f6ad66aSAchin Gupta { 48fb037bfbSDan Handley smh_file_open_block_t open_block; 494f6ad66aSAchin Gupta 504f6ad66aSAchin Gupta open_block.file_name = file_name; 514f6ad66aSAchin Gupta open_block.mode = mode; 524f6ad66aSAchin Gupta open_block.name_length = strlen(file_name); 534f6ad66aSAchin Gupta 544f6ad66aSAchin Gupta return semihosting_call(SEMIHOSTING_SYS_OPEN, 554f6ad66aSAchin Gupta (void *) &open_block); 564f6ad66aSAchin Gupta } 574f6ad66aSAchin Gupta 58cd529320SRyan Harkin long semihosting_file_seek(long file_handle, ssize_t offset) 594f6ad66aSAchin Gupta { 60fb037bfbSDan Handley smh_file_seek_block_t seek_block; 61cd529320SRyan Harkin long result; 624f6ad66aSAchin Gupta 634f6ad66aSAchin Gupta seek_block.handle = file_handle; 644f6ad66aSAchin Gupta seek_block.location = offset; 654f6ad66aSAchin Gupta 664f6ad66aSAchin Gupta result = semihosting_call(SEMIHOSTING_SYS_SEEK, 674f6ad66aSAchin Gupta (void *) &seek_block); 684f6ad66aSAchin Gupta 694f6ad66aSAchin Gupta if (result) 704f6ad66aSAchin Gupta result = semihosting_call(SEMIHOSTING_SYS_ERRNO, 0); 714f6ad66aSAchin Gupta 724f6ad66aSAchin Gupta return result; 734f6ad66aSAchin Gupta } 744f6ad66aSAchin Gupta 75625de1d4SDan Handley long semihosting_file_read(long file_handle, size_t *length, uintptr_t buffer) 764f6ad66aSAchin Gupta { 77fb037bfbSDan Handley smh_file_read_write_block_t read_block; 78cd529320SRyan Harkin long result = -EINVAL; 794f6ad66aSAchin Gupta 80625de1d4SDan Handley if ((length == NULL) || (buffer == (uintptr_t)NULL)) 814f6ad66aSAchin Gupta return result; 824f6ad66aSAchin Gupta 834f6ad66aSAchin Gupta read_block.handle = file_handle; 844f6ad66aSAchin Gupta read_block.buffer = buffer; 854f6ad66aSAchin Gupta read_block.length = *length; 864f6ad66aSAchin Gupta 874f6ad66aSAchin Gupta result = semihosting_call(SEMIHOSTING_SYS_READ, 884f6ad66aSAchin Gupta (void *) &read_block); 894f6ad66aSAchin Gupta 904f6ad66aSAchin Gupta if (result == *length) { 914f6ad66aSAchin Gupta return -EINVAL; 924f6ad66aSAchin Gupta } else if (result < *length) { 934f6ad66aSAchin Gupta *length -= result; 944f6ad66aSAchin Gupta return 0; 954f6ad66aSAchin Gupta } else 964f6ad66aSAchin Gupta return result; 974f6ad66aSAchin Gupta } 984f6ad66aSAchin Gupta 99cd529320SRyan Harkin long semihosting_file_write(long file_handle, 100cd529320SRyan Harkin size_t *length, 101625de1d4SDan Handley const uintptr_t buffer) 1024f6ad66aSAchin Gupta { 103fb037bfbSDan Handley smh_file_read_write_block_t write_block; 10431833affSJuan Castillo long result = -EINVAL; 1054f6ad66aSAchin Gupta 106625de1d4SDan Handley if ((length == NULL) || (buffer == (uintptr_t)NULL)) 1074f6ad66aSAchin Gupta return -EINVAL; 1084f6ad66aSAchin Gupta 1094f6ad66aSAchin Gupta write_block.handle = file_handle; 110625de1d4SDan Handley write_block.buffer = (uintptr_t)buffer; /* cast away const */ 1114f6ad66aSAchin Gupta write_block.length = *length; 1124f6ad66aSAchin Gupta 11331833affSJuan Castillo result = semihosting_call(SEMIHOSTING_SYS_WRITE, 1144f6ad66aSAchin Gupta (void *) &write_block); 1154f6ad66aSAchin Gupta 11631833affSJuan Castillo *length = result; 11731833affSJuan Castillo 11831833affSJuan Castillo return (result == 0) ? 0 : -EINVAL; 1194f6ad66aSAchin Gupta } 1204f6ad66aSAchin Gupta 121cd529320SRyan Harkin long semihosting_file_close(long file_handle) 1224f6ad66aSAchin Gupta { 1234f6ad66aSAchin Gupta return semihosting_call(SEMIHOSTING_SYS_CLOSE, 1244f6ad66aSAchin Gupta (void *) &file_handle); 1254f6ad66aSAchin Gupta } 1264f6ad66aSAchin Gupta 127cd529320SRyan Harkin long semihosting_file_length(long file_handle) 1284f6ad66aSAchin Gupta { 1294f6ad66aSAchin Gupta return semihosting_call(SEMIHOSTING_SYS_FLEN, 1304f6ad66aSAchin Gupta (void *) &file_handle); 1314f6ad66aSAchin Gupta } 1324f6ad66aSAchin Gupta 1334f6ad66aSAchin Gupta char semihosting_read_char(void) 1344f6ad66aSAchin Gupta { 1354f6ad66aSAchin Gupta return semihosting_call(SEMIHOSTING_SYS_READC, NULL); 1364f6ad66aSAchin Gupta } 1374f6ad66aSAchin Gupta 1384f6ad66aSAchin Gupta void semihosting_write_char(char character) 1394f6ad66aSAchin Gupta { 1404f6ad66aSAchin Gupta semihosting_call(SEMIHOSTING_SYS_WRITEC, (void *) &character); 1414f6ad66aSAchin Gupta } 1424f6ad66aSAchin Gupta 1434f6ad66aSAchin Gupta void semihosting_write_string(char *string) 1444f6ad66aSAchin Gupta { 1454f6ad66aSAchin Gupta semihosting_call(SEMIHOSTING_SYS_WRITE0, (void *) string); 1464f6ad66aSAchin Gupta } 1474f6ad66aSAchin Gupta 148cd529320SRyan Harkin long semihosting_system(char *command_line) 1494f6ad66aSAchin Gupta { 150fb037bfbSDan Handley smh_system_block_t system_block; 1514f6ad66aSAchin Gupta 1524f6ad66aSAchin Gupta system_block.command_line = command_line; 1534f6ad66aSAchin Gupta system_block.command_length = strlen(command_line); 1544f6ad66aSAchin Gupta 1554f6ad66aSAchin Gupta return semihosting_call(SEMIHOSTING_SYS_SYSTEM, 1564f6ad66aSAchin Gupta (void *) &system_block); 1574f6ad66aSAchin Gupta } 1584f6ad66aSAchin Gupta 159cd529320SRyan Harkin long semihosting_get_flen(const char *file_name) 1604f6ad66aSAchin Gupta { 161cd529320SRyan Harkin long file_handle; 162cd529320SRyan Harkin size_t length; 1634f6ad66aSAchin Gupta 1644f6ad66aSAchin Gupta assert(semihosting_connection_supported()); 1654f6ad66aSAchin Gupta 1664f6ad66aSAchin Gupta file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 1674f6ad66aSAchin Gupta if (file_handle == -1) 1684f6ad66aSAchin Gupta return file_handle; 1694f6ad66aSAchin Gupta 1704f6ad66aSAchin Gupta /* Find the length of the file */ 1714f6ad66aSAchin Gupta length = semihosting_file_length(file_handle); 1724f6ad66aSAchin Gupta 1734f6ad66aSAchin Gupta return semihosting_file_close(file_handle) ? -1 : length; 1744f6ad66aSAchin Gupta } 1754f6ad66aSAchin Gupta 176cd529320SRyan Harkin long semihosting_download_file(const char *file_name, 177cd529320SRyan Harkin size_t buf_size, 178625de1d4SDan Handley uintptr_t buf) 1794f6ad66aSAchin Gupta { 180cd529320SRyan Harkin long ret = -EINVAL; 181cd529320SRyan Harkin size_t length; 182cd529320SRyan Harkin long file_handle; 1834f6ad66aSAchin Gupta 1844f6ad66aSAchin Gupta /* Null pointer check */ 1854f6ad66aSAchin Gupta if (!buf) 1864f6ad66aSAchin Gupta return ret; 1874f6ad66aSAchin Gupta 1884f6ad66aSAchin Gupta assert(semihosting_connection_supported()); 1894f6ad66aSAchin Gupta 1904f6ad66aSAchin Gupta file_handle = semihosting_file_open(file_name, FOPEN_MODE_RB); 1914f6ad66aSAchin Gupta if (file_handle == -1) 1924f6ad66aSAchin Gupta return ret; 1934f6ad66aSAchin Gupta 1944f6ad66aSAchin Gupta /* Find the actual length of the file */ 1954f6ad66aSAchin Gupta length = semihosting_file_length(file_handle); 1964f6ad66aSAchin Gupta if (length == -1) 1974f6ad66aSAchin Gupta goto semihosting_fail; 1984f6ad66aSAchin Gupta 1994f6ad66aSAchin Gupta /* Signal error if we do not have enough space for the file */ 2004f6ad66aSAchin Gupta if (length > buf_size) 2014f6ad66aSAchin Gupta goto semihosting_fail; 2024f6ad66aSAchin Gupta 2034f6ad66aSAchin Gupta /* 2044f6ad66aSAchin Gupta * A successful read will return 0 in which case we pass back 2054f6ad66aSAchin Gupta * the actual number of bytes read. Else we pass a negative 2064f6ad66aSAchin Gupta * value indicating an error. 2074f6ad66aSAchin Gupta */ 2084f6ad66aSAchin Gupta ret = semihosting_file_read(file_handle, &length, buf); 2094f6ad66aSAchin Gupta if (ret) 2104f6ad66aSAchin Gupta goto semihosting_fail; 2114f6ad66aSAchin Gupta else 2124f6ad66aSAchin Gupta ret = length; 2134f6ad66aSAchin Gupta 2144f6ad66aSAchin Gupta semihosting_fail: 2154f6ad66aSAchin Gupta semihosting_file_close(file_handle); 2164f6ad66aSAchin Gupta return ret; 2174f6ad66aSAchin Gupta } 218