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.