adds initial semihosting host capabilities

This commit is contained in:
2024-01-08 17:17:59 +01:00
parent f4f90c5e65
commit 207f778ee6
12 changed files with 332 additions and 143 deletions

View File

@ -55,6 +55,8 @@
#include <util/ities.h>
#include <util/sparse_array.h>
#include <iss/semihosting/semihosting.h>
#if defined(__GNUC__)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
@ -290,6 +292,8 @@ public:
void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); }
void set_semihosting_callback(std::function<void(arch_if*, reg_t, reg_t)>& cb) { semihosting_cb = cb; };
protected:
struct riscv_instrumentation_if : public iss::instrumentation_if {
@ -344,9 +348,11 @@ protected:
reg_t fault_data;
uint64_t tohost = tohost_dflt;
uint64_t fromhost = fromhost_dflt;
unsigned to_host_wr_cnt = 0;
bool tohost_lower_written = false;
riscv_instrumentation_if instr_if;
std::function<void(arch_if*, reg_t, reg_t)> semihosting_cb;
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>;
using csr_page_type = typename csr_type::page_type;
@ -419,6 +425,7 @@ protected:
feature_config cfg;
unsigned mcause_max_irq{(FEAT & features_e::FEAT_CLIC) ? std::max(16U, static_cast<unsigned>(traits<BASE>::CLIC_NUM_IRQ)) : 16U};
inline bool debug_mode_active() { return this->reg.PRIV & 0x4; }
std::pair<std::function<mem_read_f>, std::function<mem_write_f>> replace_mem_access(std::function<mem_read_f> rd,
std::function<mem_write_f> wr) {
std::pair<std::function<mem_read_f>, std::function<mem_write_f>> ret{hart_mem_rd_delegate, hart_mem_wr_delegate};
@ -784,7 +791,7 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write(const address_type type, const acc
res = write_mem(phys_addr, length, data);
}
if(unlikely(res != iss::Ok && (access & access_type::DEBUG) == 0)) {
this->reg.trap_state = (1UL << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)
this->reg.trap_state = (1UL << 31) | (7UL << 16); // issue trap 7 (Store/AMO access fault)
fault_data = addr;
}
return res;
@ -1098,6 +1105,7 @@ iss::status riscv_hart_m_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned len
template <typename BASE, features_e FEAT>
iss::status riscv_hart_m_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) {
switch(paddr.val) {
// TODO remove UART, Peripherals should not be part of the ISS
case 0xFFFF0000: // UART0 base, TXFIFO reg
if(((char)data[0]) == '\n' || data[0] == 0) {
LOG(INFO) << "UART" << ((paddr.val >> 12) & 0x3) << " send '" << uart_buf.str() << "'";
@ -1115,7 +1123,8 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned le
auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
if(tohost_lower || tohost_upper) {
uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask));
if(tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
// in case of 32 bit system, two writes to tohost are needed, only evaluate on the second (high) write
if(tohost_upper && (tohost_lower || tohost_lower_written)) {
switch(hostvar >> 48) {
case 0:
if(hostvar != 0x1) {
@ -1138,13 +1147,13 @@ iss::status riscv_hart_m_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned le
uart_buf.str("");
} else
uart_buf << c;
to_host_wr_cnt = 0;
} break;
default:
break;
}
tohost_lower_written = false;
} else if(tohost_lower)
to_host_wr_cnt++;
tohost_lower_written = true;
} else if((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == fromhost)) {
uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (fromhost & mem.page_addr_mask));
*reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
@ -1253,6 +1262,31 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_m_p<BASE, FEAT>::e
} else {
csr[mtval] = addr;
}
if(semihosting_cb) {
// Check for semihosting call
phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4);
std::array<uint8_t, 8> data;
// check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07
this->read_mem(p_addr, 4, data.data());
p_addr.val += 8;
this->read_mem(p_addr, 4, data.data() + 4);
const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40};
if(data == ref_data) {
this->reg.NEXT_PC = addr + 8;
std::array<char, 32> buffer;
#if defined(_MSC_VER)
sprintf(buffer.data(), "0x%016llx", addr);
#else
sprintf(buffer.data(), "0x%016lx", addr);
#endif
CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred ";
semihosting_callback(this, this->reg.X10 /*a0*/, this->reg.X11 /*a1*/);
return this->reg.NEXT_PC;
}
}
break;
case 4:
case 6:

View File

@ -55,6 +55,8 @@
#include <util/ities.h>
#include <util/sparse_array.h>
#include <iss/semihosting/semihosting.h>
#if defined(__GNUC__)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
@ -341,6 +343,8 @@ public:
void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); }
void set_semihosting_callback(std::function<void(arch_if*, reg_t, reg_t)>& cb) { semihosting_cb = cb; };
protected:
struct riscv_instrumentation_if : public iss::instrumentation_if {
@ -395,9 +399,11 @@ protected:
std::array<vm_info, 2> vm;
uint64_t tohost = tohost_dflt;
uint64_t fromhost = fromhost_dflt;
unsigned to_host_wr_cnt = 0;
bool tohost_lower_written = false;
riscv_instrumentation_if instr_if;
std::function<void(arch_if*, reg_t, reg_t)> semihosting_cb;
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>;
using csr_page_type = typename csr_type::page_type;
@ -1092,7 +1098,8 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_add
auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
if(tohost_lower || tohost_upper) {
uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask));
if(tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
// in case of 32 bit system, two writes to tohost are needed, only evaluate on the second (high) write
if(tohost_upper && (tohost_lower || tohost_lower_written)) {
switch(hostvar >> 48) {
case 0:
if(hostvar != 0x1) {
@ -1104,8 +1111,10 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_add
}
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
this->interrupt_sim = hostvar;
#ifndef WITH_TCC
throw(iss::simulation_stopped(hostvar));
#endif
break;
// throw(iss::simulation_stopped(hostvar));
case 0x0101: {
char c = static_cast<char>(hostvar & 0xff);
if(c == '\n' || c == 0) {
@ -1113,13 +1122,13 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_add
uart_buf.str("");
} else
uart_buf << c;
to_host_wr_cnt = 0;
} break;
default:
break;
}
tohost_lower_written = false;
} else if(tohost_lower)
to_host_wr_cnt++;
tohost_lower_written = true;
} else if((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == fromhost)) {
uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (fromhost & mem.page_addr_mask));
*reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
@ -1304,6 +1313,31 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
// csr[dpc] = addr;
// csr[dcsr] = (csr[dcsr] & ~0x1c3) | (1<<6) | PRIV_M; //FIXME: cause should not be 4 (stepi)
csr[utval | (new_priv << 8)] = addr;
if(semihosting_cb) {
// Check for semihosting call
phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4);
std::array<uint8_t, 8> data;
// check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07
this->read_mem(p_addr, 4, data.data());
p_addr.val += 8;
this->read_mem(p_addr, 4, data.data() + 4);
const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40};
if(data == ref_data) {
this->reg.NEXT_PC = addr + 8;
std::array<char, 32> buffer;
#if defined(_MSC_VER)
sprintf(buffer.data(), "0x%016llx", addr);
#else
sprintf(buffer.data(), "0x%016lx", addr);
#endif
CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred ";
semihosting_callback(this, this->reg.X10 /*a0*/, this->reg.X11 /*a1*/);
return this->reg.NEXT_PC;
}
}
break;
case 4:
case 6:
@ -1321,7 +1355,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
this->reg.pending_trap = 0;
}
size_t adr = ucause | (new_priv << 8);
csr[adr] = (trap_id << 31) + cause;
csr[adr] = (trap_id << (traits<BASE>::XLEN - 1)) + cause;
// update mstatus
// xPP field of mstatus is written with the active privilege mode at the time
// of the trap; the x PIE field of mstatus

View File

@ -55,6 +55,8 @@
#include <util/ities.h>
#include <util/sparse_array.h>
#include <iss/semihosting/semihosting.h>
#if defined(__GNUC__)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
@ -317,6 +319,8 @@ public:
void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); }
void set_semihosting_callback(std::function<void(arch_if*, reg_t, reg_t)>& cb) { semihosting_cb = cb; };
protected:
struct riscv_instrumentation_if : public iss::instrumentation_if {
@ -371,9 +375,11 @@ protected:
reg_t fault_data;
uint64_t tohost = tohost_dflt;
uint64_t fromhost = fromhost_dflt;
unsigned to_host_wr_cnt = 0;
bool tohost_lower_written = false;
riscv_instrumentation_if instr_if;
std::function<void(arch_if*, reg_t, reg_t)> semihosting_cb;
using mem_type = util::sparse_array<uint8_t, 1ULL << 32>;
using csr_type = util::sparse_array<typename traits<BASE>::reg_t, 1ULL << 12, 12>;
using csr_page_type = typename csr_type::page_type;
@ -1317,6 +1323,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned le
template <typename BASE, features_e FEAT>
iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t* const data) {
switch(paddr.val) {
// TODO remove UART, Peripherals should not be part of the ISS
case 0xFFFF0000: // UART0 base, TXFIFO reg
if(((char)data[0]) == '\n' || data[0] == 0) {
LOG(INFO) << "UART" << ((paddr.val >> 12) & 0x3) << " send '" << uart_buf.str() << "'";
@ -1334,7 +1341,8 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l
auto tohost_lower = (traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
if(tohost_lower || tohost_upper) {
uint64_t hostvar = *reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask));
if(tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
// in case of 32 bit system, two writes to tohost are needed, only evaluate on the second (high) write
if(tohost_upper && (tohost_lower || tohost_lower_written)) {
switch(hostvar >> 48) {
case 0:
if(hostvar != 0x1) {
@ -1346,8 +1354,10 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l
}
this->reg.trap_state = std::numeric_limits<uint32_t>::max();
this->interrupt_sim = hostvar;
#ifndef WITH_TCC
throw(iss::simulation_stopped(hostvar));
#endif
break;
// throw(iss::simulation_stopped(hostvar));
case 0x0101: {
char c = static_cast<char>(hostvar & 0xff);
if(c == '\n' || c == 0) {
@ -1355,13 +1365,13 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l
uart_buf.str("");
} else
uart_buf << c;
to_host_wr_cnt = 0;
} break;
default:
break;
}
tohost_lower_written = false;
} else if(tohost_lower)
to_host_wr_cnt++;
tohost_lower_written = true;
} else if((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) || (traits<BASE>::XLEN == 64 && paddr.val == fromhost)) {
uint64_t fhostvar = *reinterpret_cast<uint64_t*>(p.data() + (fromhost & mem.page_addr_mask));
*reinterpret_cast<uint64_t*>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
@ -1474,6 +1484,31 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::
} else {
csr[utval | (new_priv << 8)] = addr;
}
if(semihosting_cb) {
// Check for semihosting call
phys_addr_t p_addr(access_type::DEBUG_READ, traits<BASE>::MEM, addr - 4);
std::array<uint8_t, 8> data;
// check for SLLI_X0_X0_0X1F and SRAI_X0_X0_0X07
this->read_mem(p_addr, 4, data.data());
p_addr.val += 8;
this->read_mem(p_addr, 4, data.data() + 4);
const std::array<uint8_t, 8> ref_data = {0x13, 0x10, 0xf0, 0x01, 0x13, 0x50, 0x70, 0x40};
if(data == ref_data) {
this->reg.NEXT_PC = addr + 8;
std::array<char, 32> buffer;
#if defined(_MSC_VER)
sprintf(buffer.data(), "0x%016llx", addr);
#else
sprintf(buffer.data(), "0x%016lx", addr);
#endif
CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred ";
semihosting_callback(this, this->reg.X10 /*a0*/, this->reg.X11 /*a1*/);
return this->reg.NEXT_PC;
}
}
break;
case 4:
case 6:

View File

@ -201,7 +201,6 @@ struct tgc5c: public arch_if {
inline uint32_t get_last_branch() { return reg.last_branch; }
#pragma pack(push, 1)
struct TGC5C_regs {
uint32_t X0 = 0;