Host API
Header: zbc_host.h
The host API provides functions for processing semihosting requests from guest code. Use this when implementing a semihosting device in an emulator or virtual machine.
Types
-
type zbc_host_mem_ops_t
Memory access callbacks for reading/writing guest memory.
You must implement all four callbacks. The ctx parameter is passed through from zbc_host_init().
-
type zbc_host_state_t
Host state structure.
Initialize with zbc_host_init() before use.
Functions
-
void zbc_host_init(zbc_host_state_t *state, const zbc_host_mem_ops_t *mem_ops, void *mem_ctx, const struct zbc_backend_s *backend, void *backend_ctx, uint8_t *work_buf, size_t work_buf_size)
Initialize host state.
- Parameters:
state – Host state structure to initialize
mem_ops – Memory operation callbacks
mem_ctx – Context passed to memory callbacks
backend – Backend vtable (from zbc_backend_*() factory)
backend_ctx – Backend-specific state
work_buf – Working buffer for RIFF parsing
work_buf_size – Size of working buffer (recommended: 1024 bytes)
Example:
static zbc_host_state_t host;
static uint8_t work_buf[1024];
static zbc_ansi_insecure_state_t backend_state;
zbc_host_mem_ops_t mem_ops = {
.read_u8 = my_read_u8,
.write_u8 = my_write_u8,
.read_block = my_read_block,
.write_block = my_write_block
};
zbc_ansi_insecure_init(&backend_state);
zbc_host_init(&host, &mem_ops, NULL,
zbc_backend_ansi_insecure(), &backend_state,
work_buf, sizeof(work_buf));
-
int zbc_host_process(zbc_host_state_t *state, uintptr_t riff_addr)
Process a semihosting request.
Call this when the guest writes to DOORBELL. The function: 1. Reads the RIFF request from guest memory 2. Parses the CNFG chunk (first request only) 3. Parses the CALL chunk and sub-chunks 4. Dispatches to the appropriate backend function 5. Writes the RETN (or ERRO) chunk back to guest memory
After this returns, set STATUS bit 0 (RESPONSE_READY) in your device register emulation.
- Parameters:
state – Initialized host state
riff_addr – Guest address of RIFF buffer
- Returns:
ZBC_OK on success, error code on failure
Example:
void on_doorbell_write(uintptr_t riff_ptr) {
int rc = zbc_host_process(&host, riff_ptr);
if (rc == ZBC_OK) {
set_status_response_ready();
}
}
Helper Functions
-
intptr_t zbc_host_read_guest_int(const zbc_host_state_t *state, const uint8_t *data, size_t size)
Read an integer from guest-endian data.
Converts from guest endianness to host integer.
- Parameters:
state – Host state (for guest endianness)
data – Pointer to integer data
size – Size of integer in bytes
- Returns:
Integer value
-
void zbc_host_write_guest_int(const zbc_host_state_t *state, uint8_t *data, uintptr_t value, size_t size)
Write an integer in guest-endian format.
Converts from host integer to guest endianness.
- Parameters:
state – Host state (for guest endianness)
data – Destination buffer
value – Integer value to write
size – Size of integer in bytes
Work Buffer Sizing
The work buffer should be large enough to hold the largest RIFF request plus space for the response. Recommended sizes:
Minimum: 256 bytes (most syscalls)
Typical: 1024 bytes (comfortable for file operations)
Large reads: Match the largest expected SYS_READ count plus overhead
For SYS_READ operations, the buffer is split: half for reading the request, half for the read data. So a 1024-byte buffer supports reads up to ~500 bytes.