Skip to content

Conversation

@trdthg
Copy link
Collaborator

@trdthg trdthg commented Sep 14, 2025

link to #1250

Currently I have only implemented and tested a subset of the semihosting operations, based on the gem5 implementation, and there are still a lot of work to be done, including integrating the tests into CMake.
So for now, this is just a show of how to integrate semihosting into the sail-model to ensure we‘re heading in the right direction.

.gitignore Outdated
/build
# CMake build directories used by CLion.
/cmake-build-*
test/semihosting
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably shouldn't be here? If you want to add it locally you cna use .git/info/exclude

Copy link
Collaborator Author

@trdthg trdthg Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a few test files and integrated them with both CMake and CI. Although I meet some issues:

// Suffix = 0x40705013, // srai x0, x0, 7 NOP encoding semihosting
bool is_semihosting()
{
return (mem::read<uint32_t>(zPC.bits - 4) == 0x01f01013)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This here is slightly fragile, if someone builds the sail emulator on a big-endian platform they will not detect this. Either ensure you convert from little endian or just compare the individual bytes?
E.g. char magic[12]; mem::read(zPC.bits - 4, magic, 12); memcmp(magic, ...) == 0)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be addressed next. And I guess we should also push forward #751?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is unrelated since I am concerned about the host platform being big endian, not the simulation target. Instructions are always the same endianess so this pr should be independent of that

@@ -0,0 +1,368 @@
// See LICENSE for license details.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated but I think we should just add a SPDX-License-Identifier: line to all files?

Some of this looks copied from gem5, I think this is fine due to the license, but probably need to add some attribution?

@github-actions
Copy link

github-actions bot commented Sep 23, 2025

Test Results

2 109 tests  +8   2 109 ✅ +8   15m 59s ⏱️ -7s
    1 suites ±0       0 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit b98bdf2. ± Comparison against base commit 16fe433.

♻️ This comment has been updated with latest results.

@trdthg
Copy link
Collaborator Author

trdthg commented Sep 23, 2025

Features are basically complete now, also added a few tests and integrated them with both CMake and CI

Add two new command-line arguments :

  • --enable-semihosting: to enable or disable semihosting.
  • --elf-args: to pass arguments to the ELF for the GetCmdline interface.

Also added two new CMake parameters:

  • DOWNLOAD_PICOLIBC: whether to download picolibc source from github release and build it locally.
  • SEMIHOSTING_TESTS: whether to enable the semihosting tests. (macos is not able to run this yet)

The C++ template syntax here is quite complex, but it's also the way to keep the SYS_XXX implementations concise. I haven't found a better solution. Although I believe this is already much simpler than the gem5 implementation.

@trdthg trdthg force-pushed the semihosting branch 3 times, most recently from 7a6af4d to e0ec83f Compare September 28, 2025 10:24
Copy link
Collaborator

@arichardson arichardson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more comments, will try to review the rest later

uint64_t addr = (uint64_t)ptr;
for (size_t i = 0; i < len; i += 1) {
char c = read_mem((uint64_t)addr);
if (c) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we skipping nul bytes here? Wouldn't it make sense to also just stop and treat Len as a maximum length?


bool mem::target_is_little_endian = true;

void mem::read(uint64_t addr, void *dst, size_t len)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these functions should be doing the byteswapping, that should be done by wrapper functions that read an integer type

// start from addr, len = sizeof(T)
template <typename T> static T read(uint64_t addr)
{
uint8_t buffer[sizeof(T)] = {0};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint8_t buffer[sizeof(T)] = {0};
alignas(T) uint8_t buffer[sizeof(T)] = {0};

Otherwise the reinterpret_cast is unsafe. Or you could use a union.

std::string real_mode(mode);
if (real_mode[0] == 'w')
real_mode[0] = 'a';
file = fopen(name.c_str(), real_mode.c_str());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we support access to the host file system by default? Gem5 requires an extra flag to do that and I feel we should do here too.


RiscvSemihosting::File::~File()
{
if (file) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not be closing stdin/stdout/stderr here

{
}

int64_t RiscvSemihosting::FileFeatures::read(uint8_t *buffer, uint64_t size)
Copy link
Collaborator

@arichardson arichardson Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can replace this whole class with a call to fmemopen() in read-only mode. That should hopefully be equivalent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants