The write functions operate on an unfragmented host system buffer. All C-code writer callback functions are allowed direct access to the unfragmented host buffer, while Python callback functions require the data to be copied from a Python owned data buffer.
The target system pipe buffer is not modified until all writer callbacks for its magic number have returned. Then the pipe buffer header is updated and the whole unfragmented host buffer is copied into the target pipe buffer fragments.
SIM_INTERFACE(magic_pipe_writer) { /* Query whether the simulated target system has a different byte-order than the simulator host system. */ bool (*is_byte_swap_needed)(conf_object_t *obj, uintptr_t buf); /* Query how much unused space is available in the pipe buffer. This value is decreased by each call to either write_data_add or write_data_copy. */ size_t (*write_buffer_left)(conf_object_t *obj, uintptr_t buf); /* Query the allocated pipe buffer size. This value includes both the pipe buffer header and payload data. */ size_t (*write_buffer_size)(conf_object_t *obj, uintptr_t buf); #ifndef PYWRAP /* Get direct write access to the outgoing pipe buffer data. This call returns a pointer to the write position in the pipe buffer and its remaining unused size. If the caller writes to the pipe buffer, then the write_data_add function must be called also to update the amount of used data in the pipe buffer and advance the write position. The write position is also advanced by calls to the write_data_copy function. In case neither write_data_add nor write_data_copy function is called, this function will return the exact same pointer address and size all the time. This function gives a direct pointer into internal memory and therefore cannot be used by Python code. */ buffer_t (*write_data_direct)(conf_object_t *obj, uintptr_t buf); /* Increase the amount of used data in the pipe buffer. When a caller to the write_data_direct function has written to the pipe buffer, the caller must also call this function to declare the amount of data written to the fragment. This will cause the write position to be moved to the next available space in the pipe buffer. If the length argument exceeds the available unused space. It is assumed that all the remaining space is used. */ void (*write_data_add)(conf_object_t *obj, uintptr_t buf, size_t len); #endif /* Append the data from the caller buffer to the outgoing pipe buffer. This function will copy as much of the data contents from the supplied buffer argument to the end of the pipe buffer as fits. The function will return the amount of data from the caller buffer that does not fit in the pipe buffer. If the return value is zero, then all data was copied. Otherwise the copied data was truncated and the remaining uncopied size is returned. Be sure to use the write_buffer_left function to determine the amount of remaining space, unless truncated data is desired and properly handled. This call will automatically advance the write position to the next unused space. */ size_t (*write_data_copy)(conf_object_t *obj, uintptr_t buf, bytes_t data); /* Change the magic number in the pipe buffer. This is used to assign a new magic number to the target magic pipe application. Typically this is done only on the first exchange with a new target application, to give it a unique identifier, which is then subscribed to by the simics extension, and used throughout all further communication. The magic number to pick is typically returned by the register_new_pipe function in the setup interface. However, the simics extension may choose to reserve a range of magic numbers and provide its own scheme for assigning these to new target applications. */ void (*write_buffer_magic)(conf_object_t *obj, uintptr_t buf, uint64 magic); }; #define MAGIC_PIPE_WRITER_INTERFACE "magic_pipe_writer"