# Semihosting Support for Hexagon ## Overview Semihosting is a mechanism that allows embedded programs running on a simulator or debugger to access host system resources such as file I/O, console I/O, and system calls. Picolibc’s Hexagon port implements semihosting using the TRAP0 instruction to communicate with QEMU. ## Architecture ### Communication Mechanism Hexagon semihosting uses the **TRAP0** software interrupt: trap0 (#0) ; Trigger semihosting call Copy to clipboard **Register Convention**: - **r0**: System call number (input/output) - **r1**: Pointer to argument block (input) - **r0**: Return value or error code (output) ### System Call Interface int hexagon_semihost(int syscall, void *args); Copy to clipboard **Parameters**: - `syscall`: System call number (SYS\_OPEN, SYS\_READ, etc.) - `args`: Pointer to argument structure **Returns**: - Success: Non-negative value - Error: -1 (errno is set) ## System Calls ### File Operations #### SYS\_OPEN (0x01) Opens a file. **Arguments**: struct { const char *pathname; // File path int flags; // Open flags int mode; // File mode (unused) } args; Copy to clipboard **Flags**: #define O_RDONLY 0x0000 // Read-only #define O_WRONLY 0x0001 // Write-only #define O_RDWR 0x0002 // Read-write #define O_CREAT 0x0200 // Create if not exists #define O_TRUNC 0x0400 // Truncate to zero length #define O_APPEND 0x0008 // Append mode Copy to clipboard **Returns**: File descriptor (>= 0) or -1 on error **Example**: int fd = open("/tmp/test.txt", O_RDWR | O_CREAT, 0644); if (fd < 0) { perror("open"); } Copy to clipboard #### SYS\_CLOSE (0x02) Closes a file descriptor. **Arguments**: struct { int fd; // File descriptor } args; Copy to clipboard **Returns**: 0 on success, -1 on error **Example**: if (close(fd) < 0) { perror("close"); } Copy to clipboard #### SYS\_READ (0x06) Reads from a file descriptor. **Arguments**: struct { int fd; // File descriptor void *buf; // Buffer to read into size_t count; // Number of bytes to read } args; Copy to clipboard **Returns**: Number of bytes read, or -1 on error **Note**: Semihost returns bytes NOT read, so the implementation inverts it. **Example**: char buffer[100]; ssize_t n = read(fd, buffer, sizeof(buffer)); if (n < 0) { perror("read"); } Copy to clipboard #### SYS\_WRITE (0x05) Writes to a file descriptor. **Arguments**: struct { int fd; // File descriptor const void *buf; // Buffer to write from size_t count; // Number of bytes to write } args; Copy to clipboard **Returns**: Number of bytes written, or -1 on error **Note**: Semihost returns bytes NOT written, so the implementation inverts it. **Example**: const char *msg = "Hello, World!\n"; ssize_t n = write(STDOUT_FILENO, msg, strlen(msg)); if (n < 0) { perror("write"); } Copy to clipboard #### SYS\_SEEK (0x0A) Seeks to a position in a file. **Arguments**: struct { int fd; // File descriptor off_t offset; // Offset int whence; // SEEK_SET, SEEK_CUR, or SEEK_END } args; Copy to clipboard **Whence values**: #define SEEK_SET 0 // Absolute position #define SEEK_CUR 1 // Relative to current position #define SEEK_END 2 // Relative to end of file Copy to clipboard **Returns**: New file position, or -1 on error **Example**: off_t pos = lseek(fd, 0, SEEK_END); // Seek to end if (pos < 0) { perror("lseek"); } Copy to clipboard #### SYS\_FLEN (0x0C) Gets the length of a file. **Arguments**: struct { int fd; // File descriptor } args; Copy to clipboard **Returns**: File length in bytes, or -1 on error **Example**: off_t len = flen(fd); if (len < 0) { perror("flen"); } Copy to clipboard #### SYS\_FTELL (0x10) Gets the current file position. **Arguments**: struct { int fd; // File descriptor } args; Copy to clipboard **Returns**: Current position, or -1 on error **Example**: off_t pos = hexagon_ftell(fd); if (pos < 0) { perror("ftell"); } Copy to clipboard #### SYS\_REMOVE (0x0E) Deletes a file. **Arguments**: struct { const char *pathname; // File path } args; Copy to clipboard **Returns**: 0 on success, -1 on error **Example**: if (unlink("/tmp/test.txt") < 0) { perror("unlink"); } Copy to clipboard ### Terminal Operations #### SYS\_ISTTY (0x09) Checks if a file descriptor is a terminal. **Arguments**: struct { int fd; // File descriptor } args; Copy to clipboard **Returns**: 1 if terminal, 0 if not, -1 on error **Example**: if (isatty(STDOUT_FILENO)) { printf("stdout is a terminal\n"); } Copy to clipboard ### Program Control #### SYS\_EXIT (0x18) Terminates the program. **Arguments**: struct { int status; // Exit status } args; Copy to clipboard **Returns**: Does not return **Note**: Exit status is passed in r2 register. **Example**: _exit(0); // Normal termination _exit(1); // Error termination Copy to clipboard #### SYS\_GET\_CMDLINE (0x15) Retrieves command-line arguments. **Arguments**: struct { char *buf; // Buffer to store command line size_t size; // Buffer size } args; Copy to clipboard **Returns**: 0 on success, -1 on error **Example**: char cmdline[256]; if (get_cmdline(cmdline, sizeof(cmdline)) == 0) { printf("Command line: %s\n", cmdline); } Copy to clipboard ## Error Handling ### Error Codes Hexagon semihosting uses its own error codes that must be translated to standard errno values: | Hexagon Error | Value | Standard errno | Description | | --- | --- | --- | --- | | HEX\_EPERM | 1 | EPERM | Operation not permitted | | HEX\_ENOENT | 2 | ENOENT | No such file or directory | | HEX\_EINTR | 4 | EINTR | Interrupted system call | | HEX\_EIO | 5 | EIO | I/O error | | HEX\_EBADF | 9 | EBADF | Bad file descriptor | | HEX\_EACCES | 13 | EACCES | Permission denied | | HEX\_EFAULT | 14 | EFAULT | Bad address | | HEX\_EBUSY | 16 | EBUSY | Device or resource busy | | HEX\_EEXIST | 17 | EEXIST | File exists | | HEX\_ENODEV | 19 | ENODEV | No such device | | HEX\_ENOTDIR | 20 | ENOTDIR | Not a directory | | HEX\_EISDIR | 21 | EISDIR | Is a directory | | HEX\_EINVAL | 22 | EINVAL | Invalid argument | | HEX\_ENFILE | 23 | ENFILE | Too many open files in system | | HEX\_EMFILE | 24 | EMFILE | Too many open files | | HEX\_EFBIG | 27 | EFBIG | File too large | | HEX\_ENOSPC | 28 | ENOSPC | No space left on device | | HEX\_ESPIPE | 29 | ESPIPE | Illegal seek | | HEX\_EROFS | 30 | EROFS | Read-only file system | | HEX\_ENAMETOOLONG | 91 | ENAMETOOLONG | File name too long | ### Error Translation int hexagon_semihost_errno(int hex_errno); Copy to clipboard Converts Hexagon semihost error codes to standard errno values. **Example**: int result = hexagon_semihost(SYS_OPEN, &args); if (result < 0) { errno = hexagon_semihost_errno(-result); return -1; } Copy to clipboard ## Implementation Details ### Core Semihosting Function **File**: `libos/semihost/machine/hexagon/hexagon_semihost.c` int hexagon_semihost(int syscall, void *args) { register int r0 __asm__("r0") = syscall; register void *r1 __asm__("r1") = args; __asm__ volatile ( "trap0 (#0)" : "+r"(r0) : "r"(r1) : "memory" ); if (r0 < 0) { errno = hexagon_semihost_errno(-r0); return -1; } return r0; } Copy to clipboard ### File Open Implementation **File**: `libos/semihost/machine/hexagon/hexagon_open.c` int open(const char *pathname, int flags, ...) { struct { const char *pathname; int flags; int mode; } args; args.pathname = pathname; args.flags = translate_flags(flags); // POSIX to Hexagon args.mode = 0; return hexagon_semihost(SYS_OPEN, &args); } Copy to clipboard ### File Read Implementation **File**: `libos/semihost/machine/hexagon/hexagon_read.c` ssize_t read(int fd, void *buf, size_t count) { struct { int fd; void *buf; size_t count; } args; args.fd = fd; args.buf = buf; args.count = count; int result = hexagon_semihost(SYS_READ, &args); if (result < 0) { return -1; } // Semihost returns bytes NOT read, so invert return count - result; } Copy to clipboard ## Standard I/O ### File Descriptors Standard file descriptors are pre-opened: #define STDIN_FILENO 0 // Standard input #define STDOUT_FILENO 1 // Standard output #define STDERR_FILENO 2 // Standard error Copy to clipboard ### Console I/O #include int main(void) { printf("Hello, World!\n"); // Uses write(STDOUT_FILENO, ...) char buffer[100]; if (fgets(buffer, sizeof(buffer), stdin)) { // Uses read(STDIN_FILENO, ...) printf("You entered: %s", buffer); } return 0; } Copy to clipboard ### File I/O #include int main(void) { FILE *fp = fopen("/tmp/test.txt", "w"); if (fp == NULL) { perror("fopen"); return 1; } fprintf(fp, "Hello, File!\n"); fclose(fp); return 0; } Copy to clipboard ## QEMU Configuration ### Running with Semihosting qemu-system-hexagon \ -cpu v68 \ -machine virt \ -nographic \ -kernel program.elf Copy to clipboard **Options**: - `-cpu v68`: Hexagon v68 CPU - `-machine virt`: Virtual platform with semihosting - `-nographic`: No graphical output - `-kernel program.elf`: ELF binary to run ### Passing Arguments qemu-system-hexagon \ -cpu v68 \ -machine virt \ -nographic \ -kernel program.elf \ -append "arg1 arg2 arg3" Copy to clipboard Arguments are retrieved via `get_cmdline()` and parsed into `argc`/`argv`. ### Redirecting I/O # Redirect stdin qemu-system-hexagon ... < input.txt # Redirect stdout qemu-system-hexagon ... > output.txt # Redirect stderr qemu-system-hexagon ... 2> error.txt # Pipe echo "test" | qemu-system-hexagon ... Copy to clipboard ## Limitations ### Not Implemented The following functions return `ENOSYS` (not implemented): - `fstat()`: File status (use `flen()` for file size) - `lseek64()`: 64-bit seek (use `lseek()` instead) ### File System Access - Semihosting accesses the **host** file system - Paths are relative to QEMU’s working directory - Absolute paths access host file system directly - No sandboxing or security restrictions ### Performance - Semihosting is **slow** (each call traps to QEMU) - Not suitable for production use - Intended for development and testing only ## Debugging ### Enabling Semihosting Traces qemu-system-hexagon \ -d guest_errors,unimp \ -D qemu.log \ ... Copy to clipboard This logs semihosting calls to `qemu.log`. ### Common Issues #### Issue: File not found **Symptom**: `open()` returns -1 with `ENOENT` **Solution**: Check file path relative to QEMU working directory: # Run from correct directory cd /path/to/files qemu-system-hexagon -kernel program.elf Copy to clipboard #### Issue: Permission denied **Symptom**: `open()` returns -1 with `EACCES` **Solution**: Check file permissions on host: chmod 644 file.txt Copy to clipboard #### Issue: Semihosting not working **Symptom**: Program hangs or crashes on I/O **Solution**: Ensure QEMU supports semihosting: qemu-system-hexagon --version # Should show Hexagon support Copy to clipboard ## Testing ### Test Program #include #include #include #include int main(void) { // Test console I/O printf("Testing semihosting...\n"); // Test file write int fd = open("/tmp/test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { perror("open"); return 1; } const char *msg = "Hello from Hexagon!\n"; ssize_t n = write(fd, msg, strlen(msg)); if (n < 0) { perror("write"); close(fd); return 1; } close(fd); // Test file read fd = open("/tmp/test.txt", O_RDONLY); if (fd < 0) { perror("open"); return 1; } char buffer[100]; n = read(fd, buffer, sizeof(buffer) - 1); if (n < 0) { perror("read"); close(fd); return 1; } buffer[n] = '\0'; printf("Read from file: %s", buffer); close(fd); // Test unlink if (unlink("/tmp/test.txt") < 0) { perror("unlink"); return 1; } printf("All tests passed!\n"); return 0; } Copy to clipboard ### Expected Output Testing semihosting... Read from file: Hello from Hexagon! All tests passed! Copy to clipboard ## API Reference ### Functions All semihosting functions are declared in standard headers: #include // open() #include // read(), write(), close(), lseek(), unlink(), isatty() #include // exit(), _exit() Copy to clipboard ### Hexagon-Specific Functions // Get file length off_t flen(int fd); // Get current file position off_t hexagon_ftell(int fd); // Get command line int get_cmdline(char *buf, size_t size); Copy to clipboard ## Next Steps - [Getting Started](https://docs.qualcomm.com/doc/80-N3040-23/topic/getting-started.html) - Compiling your first picolibc program - [H2 + Picolibc](https://docs.qualcomm.com/doc/80-N3040-23/topic/picolibc-h2.html) - Using semihosting on H2 - [Bare-Metal + Picolibc](https://docs.qualcomm.com/doc/80-N3040-23/topic/picolibc-baremetal.html) - Semihosting on bare-metal targets > > > [!NOTE] > Toolchain-packaged sysroots (`target/picolibc/`) already include semihosting > enabled via `libsemihost.a`. When building picolibc from source, semihosting > is enabled via the Meson option `-Dsemihost=true`. Last Published: Oct 06, 2026 [Previous Topic Related documentation](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/picolibc-baremetal.md) [Next Topic Utilities User Guide](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/index_80-n2040-1588_utilities_user_guide.md)