You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
6.1 KiB
249 lines
6.1 KiB
/* |
|
* Copyright (c) 2022 Intel Corporation. |
|
* Copyright (c) 2025 Analog Devices, Inc. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/sys/byteorder.h> |
|
#include <zephyr/arch/common/semihost.h> |
|
#include "semihost_types.h" |
|
|
|
#define XTENSA_SEMIHOST_OPEN (-2) |
|
#define XTENSA_SEMIHOST_CLOSE (-3) |
|
#define XTENSA_SEMIHOST_READ (-4) |
|
#define XTENSA_SEMIHOST_WRITE (-5) |
|
#define XTENSA_SEMIHOST_LSEEK (-6) |
|
#define XTENSA_SEMIHOST_RENAME (-7) |
|
#define XTENSA_SEMIHOST_FSTAT (-10) |
|
|
|
enum semihost_open_flag { |
|
SEMIHOST_RDONLY = 0x0, |
|
SEMIHOST_WRONLY = 0x1, |
|
SEMIHOST_RDWR = 0x2, |
|
SEMIHOST_APPEND = 0x8, |
|
SEMIHOST_CREAT = 0x200, |
|
SEMIHOST_TRUNC = 0x400, |
|
SEMIHOST_EXCL = 0x800, |
|
}; |
|
|
|
uint32_t semihost_flags(enum semihost_open_mode mode) |
|
{ |
|
uint32_t flags = 0; |
|
|
|
switch (mode) { |
|
case SEMIHOST_OPEN_R: |
|
case SEMIHOST_OPEN_RB: |
|
flags = SEMIHOST_RDONLY; |
|
break; |
|
case SEMIHOST_OPEN_R_PLUS: |
|
case SEMIHOST_OPEN_RB_PLUS: |
|
flags = SEMIHOST_RDWR; |
|
break; |
|
case SEMIHOST_OPEN_W: |
|
case SEMIHOST_OPEN_WB: |
|
flags = SEMIHOST_WRONLY | SEMIHOST_CREAT | SEMIHOST_TRUNC; |
|
break; |
|
case SEMIHOST_OPEN_W_PLUS: |
|
case SEMIHOST_OPEN_WB_PLUS: |
|
flags = SEMIHOST_RDWR | SEMIHOST_CREAT | SEMIHOST_TRUNC; |
|
break; |
|
case SEMIHOST_OPEN_A: |
|
case SEMIHOST_OPEN_AB: |
|
flags = SEMIHOST_WRONLY | SEMIHOST_CREAT | SEMIHOST_APPEND; |
|
break; |
|
case SEMIHOST_OPEN_A_PLUS: |
|
case SEMIHOST_OPEN_AB_PLUS: |
|
flags = SEMIHOST_RDWR | SEMIHOST_CREAT | SEMIHOST_APPEND; |
|
break; |
|
default: |
|
return -1; |
|
} |
|
|
|
return flags; |
|
} |
|
|
|
uint32_t semihost_mode(enum semihost_open_mode mode) |
|
{ |
|
switch (mode) { |
|
case SEMIHOST_OPEN_W: |
|
case SEMIHOST_OPEN_WB: |
|
case SEMIHOST_OPEN_W_PLUS: |
|
case SEMIHOST_OPEN_WB_PLUS: |
|
case SEMIHOST_OPEN_A: |
|
case SEMIHOST_OPEN_AB: |
|
case SEMIHOST_OPEN_A_PLUS: |
|
case SEMIHOST_OPEN_AB_PLUS: |
|
/* Octal 0600, S_IRUSR | S_IWUSR */ |
|
return 0x180; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
static inline uintptr_t xtensa_semihost_call_4(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, |
|
uintptr_t arg4, uintptr_t call_id) |
|
{ |
|
register uintptr_t a2 __asm__("%a2") = call_id; |
|
register uintptr_t a6 __asm__("%a6") = arg1; |
|
register uintptr_t a3 __asm__("%a3") = arg2; |
|
register uintptr_t a4 __asm__("%a4") = arg3; |
|
register uintptr_t a5 __asm__("%a5") = arg4; |
|
|
|
__asm__ volatile("break 1, 14\n\t" |
|
: "=r"(a2) |
|
: "r"(a2), "r"(a6), "r"(a3), "r"(a4), "r"(a5) |
|
: "memory"); |
|
|
|
return a2; |
|
} |
|
|
|
static inline uintptr_t xtensa_semihost_call_3(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, |
|
uintptr_t call_id) |
|
{ |
|
register uintptr_t a2 __asm__("%a2") = call_id; |
|
register uintptr_t a6 __asm__("%a6") = arg1; |
|
register uintptr_t a3 __asm__("%a3") = arg2; |
|
register uintptr_t a4 __asm__("%a4") = arg3; |
|
|
|
__asm__ volatile("break 1, 14\n\t" |
|
: "=r"(a2) |
|
: "r"(a2), "r"(a6), "r"(a3), "r"(a4) |
|
: "memory"); |
|
|
|
return a2; |
|
} |
|
|
|
static inline uintptr_t xtensa_semihost_call_2(uintptr_t arg1, uintptr_t arg2, uintptr_t call_id) |
|
{ |
|
register uintptr_t a2 __asm__("%a2") = call_id; |
|
register uintptr_t a6 __asm__("%a6") = arg1; |
|
register uintptr_t a3 __asm__("%a3") = arg2; |
|
|
|
__asm__ volatile("break 1, 14\n\t" : "=r"(a2) : "r"(a2), "r"(a6), "r"(a3) : "memory"); |
|
|
|
return a2; |
|
} |
|
|
|
static inline uintptr_t xtensa_semihost_call_1(uintptr_t arg1, uintptr_t call_id) |
|
{ |
|
register uintptr_t a2 __asm__("%a2") = call_id; |
|
register uintptr_t a6 __asm__("%a6") = arg1; |
|
|
|
__asm__ volatile("break 1, 14\n\t" : "=r"(a2) : "r"(a2), "r"(a6) : "memory"); |
|
|
|
return a2; |
|
} |
|
|
|
long xtensa_semihost_open(struct semihost_open_args *args) |
|
{ |
|
return xtensa_semihost_call_4((uintptr_t)args->path, semihost_flags(args->mode), |
|
semihost_mode(args->mode), args->path_len, |
|
XTENSA_SEMIHOST_OPEN); |
|
} |
|
|
|
long xtensa_semihost_close(long fd) |
|
{ |
|
return xtensa_semihost_call_1(fd, XTENSA_SEMIHOST_CLOSE); |
|
} |
|
|
|
long xtensa_semihost_write(long fd, const char *buf, long len) |
|
{ |
|
long ret; |
|
|
|
ret = (long)xtensa_semihost_call_3(fd, (uintptr_t)buf, len, XTENSA_SEMIHOST_WRITE); |
|
|
|
/* semihost_write assumes that data was written successfully if ret == 0. */ |
|
if (ret == len) { |
|
return 0; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
long xtensa_semihost_read(long fd, void *buf, long len) |
|
{ |
|
long ret; |
|
|
|
ret = (long)xtensa_semihost_call_3(fd, (uintptr_t)buf, len, XTENSA_SEMIHOST_READ); |
|
|
|
/* semihost_read assumes that all bytes were read if ret == 0. |
|
* If ret == len, it means EOF was reached. |
|
*/ |
|
if (ret == len) { |
|
return 0; |
|
} else if (ret <= 0) { |
|
return len; |
|
} else { |
|
return ret; |
|
} |
|
} |
|
|
|
long xtensa_semihost_read_char(long fd) |
|
{ |
|
char c = 0; |
|
|
|
xtensa_semihost_call_3(fd, (uintptr_t)&c, 1, XTENSA_SEMIHOST_READ); |
|
|
|
return (long)c; |
|
} |
|
|
|
long xtensa_semihost_seek(struct semihost_seek_args *args) |
|
{ |
|
long ret; |
|
|
|
ret = (long)xtensa_semihost_call_3(args->fd, args->offset, 0, XTENSA_SEMIHOST_LSEEK); |
|
|
|
if (ret == args->offset) { |
|
return 0; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
long xtensa_semihost_flen(long fd) |
|
{ |
|
uint8_t buf[64] = {0}; |
|
long ret; |
|
|
|
ret = (long)xtensa_semihost_call_2(fd, (uintptr_t)buf, XTENSA_SEMIHOST_FSTAT); |
|
if (ret < 0) { |
|
return -1; |
|
} |
|
|
|
/* Struct stat is 64 bytes, bytes 28-35 correspond to st_size |
|
* field. 8-bytes cannot fit into long data type so return |
|
* only the lower 4 bytes. |
|
*/ |
|
ret = *((long *)&buf[32]); |
|
|
|
return sys_be32_to_cpu(ret); |
|
} |
|
|
|
long semihost_exec(enum semihost_instr instr, void *args) |
|
{ |
|
switch (instr) { |
|
case SEMIHOST_OPEN: |
|
return xtensa_semihost_open((struct semihost_open_args *)args); |
|
case SEMIHOST_CLOSE: |
|
return xtensa_semihost_close(((struct semihost_close_args *)args)->fd); |
|
case SEMIHOST_WRITEC: |
|
return xtensa_semihost_write(1, (char *)args, 1); |
|
case SEMIHOST_WRITE: |
|
return xtensa_semihost_write(((struct semihost_write_args *)args)->fd, |
|
((struct semihost_write_args *)args)->buf, |
|
((struct semihost_write_args *)args)->len); |
|
case SEMIHOST_READ: |
|
return xtensa_semihost_read(((struct semihost_read_args *)args)->fd, |
|
((struct semihost_read_args *)args)->buf, |
|
((struct semihost_read_args *)args)->len); |
|
case SEMIHOST_READC: |
|
return xtensa_semihost_read_char(((struct semihost_poll_in_args *)args)->zero); |
|
case SEMIHOST_SEEK: |
|
return xtensa_semihost_seek((struct semihost_seek_args *)args); |
|
case SEMIHOST_FLEN: |
|
return xtensa_semihost_flen(((struct semihost_flen_args *)args)->fd); |
|
default: |
|
return -1; |
|
} |
|
}
|
|
|