This section lists the interfaces that can be implemented by the processor model to enable certain Simics features. They are not required, but implementing them will allow user defined processor models to support the same generic feature set as Simics standard processor models. If you intend to plug your model into an existing Simics-provided platform, then many of these interfaces are actually required for such a platform to function.
The processor_info_v2
interface is implemented by
processors models. The interface has processor generic functions
that are architecture independent.
The disassemble function returns the disassemble string for an
instruction at address with opcode according to
instruction_data. The instruction_data is an
attr_value_t value of data type with the bytes of the
opcode. The bytes are in the same order as they are stored in memory. For
VLIW architectures, sub_operation is used to select which
sub-operation to disassemble. The sub-operations start at zero, and a
request for the entire unit including all sub-operations is encoded with
sub-operation -1. A request for a sub-operation that is not present (for
example when sub-operation is neither 0 nor -1 for non-VLIW
architectures) results in the integer part of the return tuple being set to
zero. If successful, the function should return a tuple with the size of the
instruction in bytes and the disassembly string. The disassembly string
should be allocated with MM_MALLOC or similar and is to be freed by the
caller. If more bytes are needed, then the function should indicate that by
returning a negative number in the tuple where the absolute value of the
number is the required number of bytes. The string should be NULL if more
bytes are needed. The implementor of processor_info_v2
is
allowed to request one additional byte at a time until enough bytes are
passed to determine what the instruction is. Illegal instructions should
still result in a valid returned tuple, where the integer part will be used
by the disassemble command to skip that many bytes before disassembling the
next instruction. The address can be used to display absolute
destinations of program counter relative branches.
The set_program_counter function sets the program counter in the processor. The get_program_counter function returns the current program counter.
The logical_to_physical function translates a logical
address to a physical address of the type defined by
access_type. The function returns a physical_block_t
struct with valid bit and the address. The
address is valid when the valid bit is not 0
. The
logical_to_physical function also returns
block_start and block_end. The start and end
of a block has the same logical to physical transformation as the translated
address. The range is inclusive, so block_end should be the
address of the last byte of the block.
This information can be used to figure out how often the
logical_to_physical function needs to be called. An implementation would
typically return the page start and end here, but it is free to return any
power of 2 sized block as long as it includes the translated address.
The current operating mode of the processor is returned with get_processor_mode.
The processor can be enabled or disabled with the
enable_processor or disable_processor
functions. The functions should return 0
if the processor
changed from enabled to disabled or from disabled to enabled, and
1
if the processor did not change state. The current state
is returned by the get_enabled function. Enabled or
disabled here refers to the state that the user of the model has
put the processor into. In particular, it is independent of the
power mode of the processor. A processor that has powered down does
not count as disabled in this sense, nor does the
enable_processor wake up a processor that is in
a power-saving sleep state.
The endianness of the processor is returned by the get_endian function.
The physical memory object is returned by the
get_physical_memory function. The object returned by
get_physical_memory is used to set breakpoints by the
global break command, and to read and write physical
memory through set, get,
load-binary, load-file, and the default
implementation of disassemble. The object returned
implements the memory_space
and
breakpoint
interfaces. The
memory_space
interface for the returned object is
only be used in inquiry mode corresponding to actions by the
simulator itself rather than by the simulated software. An
implementation may return NULL from this method, which will lead to
the command listed above not being supported when such a processor
is selected.
The get_logical_address_width function returns the number of logical/virtual address bits and the get_physical_address_width function returns the number of physical address bits.
The processor architecture is returned by calling the
architecture function. The architecture should be one of
arm
, mips32
,
mips64
, ppc32
, ppc64
, sparc-v8
,
sparc-v9
, x86
, x86-64
, or something else
if none of the listed is a good match.
All functions in the interface are optional. Each function can be set to NULL if it is not supported.
SIM_INTERFACE(processor_info_v2) { tuple_int_string_t (*disassemble)(conf_object_t *obj, generic_address_t address, attr_value_t instruction_data, int sub_operation); void (*set_program_counter)(conf_object_t *obj, logical_address_t pc); logical_address_t (*get_program_counter)(conf_object_t *obj); physical_block_t (*logical_to_physical)(conf_object_t *obj, logical_address_t address, access_t access_type); processor_mode_t (*get_processor_mode)(conf_object_t *obj); int (*enable_processor)(conf_object_t *obj); int (*disable_processor)(conf_object_t *obj); int (*get_enabled)(conf_object_t *obj); cpu_endian_t (*get_endian)(conf_object_t *obj); conf_object_t *(*get_physical_memory)(conf_object_t *obj); int (*get_logical_address_width)(conf_object_t *obj); int (*get_physical_address_width)(conf_object_t *obj); const char *(*architecture)(conf_object_t *obj); }; #define PROCESSOR_INFO_V2_INTERFACE "processor_info_v2"
Note that the original version of this interface
(processor_info
) must also be implemented. The only
difference between the two interfaces is that the original version lacks the
get_processor_mode function.
Some commands and features in the CLI use the
processor_cli
interface. Those commands will have
limited functionality if the interface is not fully implemented.
The first argument to each function is the object to act on. This object
should implement both the processor_info
interface and the
processor_cli
interface.
The get_disassembly function is used for the disassemble command as well as to disassemble the next instruction to be executed, when control is returned to the CLI prompt. For most architectures, get_disassembly can be set to NULL, in which case the command will use other interfaces to provide a generic disassembly. The get_disassembly function should return a tuple with the length of the instruction in bytes and the disassembly string. The addr_prefix parameter selects the address type of the address parameter, whether it is a physical address ("p"), a linear address ("l") or a virtual address ("v"), just as returned from get_address_prefix. The address parameter is the program counter for the instruction to disassemble. If print_cpu is non-zero, then the name of the processor should be included first in the disassembly line. If mnemonic is not NULL, then it should be output instead of the instruction disassemble. The mnemonic is used to print exception or interrupt information as returned by the get_pending_exception_string function.
get_pregs returns the string to output in the CLI for the print-processor-registers command. The all parameter is a boolean corresponding to the -all switch to the print-processor-registers command.
The diff_regs function is used by the stepi
command when the -r flag is used. The
diff_regs function returns a list of register names,
where each register in that list will be read through the
int_register
interface before and after an
instruction.
When returning to the CLI prompt, information about the next
instruction or step to execute is printed. Normally, that is the
disassemble of the instruction at the current program counter. The
get_pending_exception_string function is called before
the disassembly to find out if the next step will not be an
instruction, but rather a taken exception or interrupt. The
function should inspect the given cpu (an object
implementing processor_info
and
processor_cli
) and return NULL if the next step will
be the execution of the instruction at the current program
counter. If the next step will instead be the handling of an
exception or interrupt, then a string saying that should be
returned.
The get_address_prefix function returns a string with the default address prefix for memory related commands. Simics defines the generic prefixes "v" for virtual addresses, "l" for linear addresses, and "p" for physical addresses. The default if get_address_prefix is NULL is "v" for virtual addresses.
translate_to_physical translates an address to a
physical address. If translate_to_physical is NULL, then
the only allowed address prefixes are "v" (virtual) and "p"
(physical), and the logical_to_physical function in the
processor_info
interface will be used to translate
virtual addresses.
SIM_INTERFACE(processor_cli) { tuple_int_string_t (*get_disassembly)(conf_object_t *obj, const char *addr_prefix, generic_address_t address, bool print_cpu, const char *mnemonic); char *(*get_pregs)(conf_object_t *cpu, bool all); attr_value_t (*get_diff_regs)(conf_object_t *obj); char *(*get_pending_exception_string)(conf_object_t *obj); char *(*get_address_prefix)(conf_object_t *obj); physical_block_t (*translate_to_physical)(conf_object_t *obj, const char *prefix, generic_address_t address); }; #define PROCESSOR_CLI_INTERFACE "processor_cli"
The processor_gui
interface is implemented by
processors that support displays in the Simics native GUI. It is
only registered to indicate support for the displays, and does not
contain any actual functionality.
SIM_INTERFACE(processor_gui) { void (*dummy)(conf_object_t *obj); }; #define PROCESSOR_GUI_INTERFACE "processor_gui"
The step
interface is typically implemented by
processors, but can be implemented by other objects as well. Its
purpose is to handle step events using a queue.
The current number of steps for the queue is returned when calling get_step_count.
The post_step function will schedule an event that will
occur after steps (which must be nonnegative)
counted from local current step at
queue. An event previously posted can be removed by
calling cancel_step. The cancel_step function takes a
function pred as argument which is called when a matching
event is found. The event is only removed if pred returns
1
. The find_next_step takes the same arguments
as cancel_step but only returns the number of cycles before
the event will occur. The evclass is the event class,
obj is the object posting the event, and
user_data is pointer to data used as a parameter when
calling the callback function defined in the evclass.
If no matching event was found, find_next_step returns
−1.
The events method returns a list of all pending events in expiration order. Each element is a four-element list containing the event object, the event class name, the expiration time counted in steps as an integer and the event description as given by the event class describe method, or nil for events whose event class do not define that method.
The advance function will increment the number of steps for the queue, decrementing the number of steps to the first event to the value defined by steps. The number of steps remaining to the next event is returned. It is an error to advance beyond the next pending event, so the return value is never negative.
The implementor of the step
interface can use any
checkpoint representation. The name field in the
event class data structure is unique, and the attribute setter
function for checkpoint restore can use
VT_get_event_class to get the event class structure
corresponding to an event class name.
SIM_INTERFACE(step) { pc_step_t (*get_step_count)(conf_object_t *NOTNULL queue); void (*post_step)( conf_object_t *NOTNULL queue, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, pc_step_t steps, lang_void *user_data); void (*cancel_step)( conf_object_t *NOTNULL queue, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, int (*pred)(lang_void *data, lang_void *match_data), lang_void *match_data); pc_step_t (*find_next_step)( conf_object_t *NOTNULL queue, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, int (*pred)(lang_void *data, lang_void *match_data), lang_void *match_data); attr_value_t (*events)(conf_object_t *NOTNULL obj); pc_step_t (*advance)(conf_object_t *queue, pc_step_t steps); }; #define STEP_INTERFACE "step"
The step_cycle_ratio
interface is implemented by
processors that support a changeable ratio between steps and
cycles. The set-step-rate command uses this interface to
set the ratio between steps and cycles.
The set_ratio sets the ratio between steps and cycles. Note that the introduction of stall cycles can skew the ratio. The get_ratio simply returns the current ratio.
The cycles and step arguments must be in the range [1..128] and cycles must be a power of two. Implementers of this interface may choose to ignore other values of cycles and step and may log an error.
typedef struct { uint32 steps; uint32 cycles; } step_cycle_ratio_t; SIM_INTERFACE(step_cycle_ratio) { step_cycle_ratio_t (*get_ratio)(conf_object_t *obj); void (*set_ratio)(conf_object_t *obj, uint32 steps, uint32 cycles); }; #define STEP_CYCLE_RATIO_INTERFACE "step_cycle_ratio"
The stall
interface can be implemented by objects that also
implement the cycle
and step
interfaces. The
stall
interface controls the addition of extra cycles between
steps.
The get_stall_cycles function returns the remaining number of stall cycles. The object will advance that number of cycles before starting with the next step.
The set_stall_cycles function is used to change the number of stall cycles before the next step. It is legal to first call this function with a large value for cycles and then at a later point reduce the cycle count is resume execution earlier than indicated by the first call.
The get_total_stall_cycles returns the total accumulated number of stall cycles.
SIM_INTERFACE(stall) { cycles_t (*get_stall_cycles)(conf_object_t *obj); void (*set_stall_cycles)(conf_object_t *obj, cycles_t cycles); cycles_t (*get_total_stall_cycles)(conf_object_t *obj); }; #define STALL_INTERFACE "stall"
The int_register
interface is used for access to registers in a processor. It
can be used to access any kind of integer register, not only the
"normal" registers. This includes all kinds of control registers,
hidden registers and anything else that might be useful to access as
a register. The only limitation is that the register value should
be representable as a 64-bit unsigned integer.
This interface can be implemented by other classes than processors, but it is likely to be found mostly in processors.
Registers are identified by a number, and there are two functions to translate from register names to register numbers and back. The translation need not be one-to-one, which means that one register can have several names. A register name can, however, only translate to a single register number.
Often, registers are grouped in register banks, where registers in the bank are numbered from 0 up. Registers in a bank should have consecutive numbers (unless their numbering is very sparse). This allows a user to deduce register numbers by calling get_number for the first register only. The first register numbers should be used for the general-purpose integer registers, if possible (so that integer register rN has number N).
Using this interface to read or write registers does not cause any side effects, such as triggering interrupts or signalling haps.
get_number translates a register name to its number. Returns -1 if the register does not exist.
get_name translates a register number to its canonical name.
read reads a register value.
write writes a new register value.
all_registers returns a list of all register numbers that can be used for this object.
register_info returns information about a single register. The information return depends on the info parameter.
Core_Control_Register_Write
and
Core_Control_Register_Read
are triggered when this
register is written or read.typedef enum { Sim_RegInfo_Catchable } ireg_info_t;
SIM_INTERFACE(int_register) { int (*get_number)(conf_object_t *NOTNULL obj, const char *NOTNULL name); const char *(*get_name)(conf_object_t *NOTNULL obj, int reg); uint64 (*read)(conf_object_t *NOTNULL obj, int reg); void (*write)(conf_object_t *NOTNULL obj, int reg, uint64 val); attr_value_t (*all_registers)(conf_object_t *NOTNULL obj); int (*register_info)(conf_object_t *NOTNULL obj, int reg, ireg_info_t info); }; #define INT_REGISTER_INTERFACE "int_register"
SIM_INTERFACE(decoder) { void (*register_decoder)(conf_object_t *obj, decoder_t *NOTNULL decoder); void (*unregister_decoder)(conf_object_t *obj, decoder_t *NOTNULL decoder); };
The decoder
interface is implemented by processors
that allows connecting user decoders. This allows a user to
implement the semantics of instructions that are not available in
the standard Simics model or change the semantics of instructions
implemented by Simics. This interface replaces
SIM_register_arch_decoder and
SIM_unregister_arch_decoder functions.
The register_decoder function adds a decoder and unregister_decoder removes a decoder.
The decoder is installed/removed for every object of the same class as the obj argument which must be the same object from which the interface was fetched.
When Simics decodes an instruction, it will first see if any instruction decoders are registered for the current CPU class. For any decoders it finds, Simics will let it try to decode the instruction. The decoders are called in order, starting with the last registered decoder, and if one decoder accepts the instruction, the rest of the decoders will not be called.
The decoder is specified by the decoder_t
data structure that the
user supplies:
typedef struct { void *user_data; int (*NOTNULL decode)(uint8 *code, int valid_bytes, conf_object_t *cpu, instruction_info_t *ii, void *user_data); tuple_int_string_t (*NOTNULL disassemble)(uint8 *code, int valid_bytes, conf_object_t *cpu, void *user_data); int (*NOTNULL flush)(instruction_info_t *ii, void *user_data); } decoder_t;
The decode function is called to decode an instruction pointed to by code. The first byte corresponds to the lowest address of the instruction in the simulated memory. valid_bytes tells how many bytes can be read. The CPU is given in the cpu parameter. When the decoder has successfully decoded an instruction, it should set the ii_ServiceRoutine, the ii_Arg, and the ii_Type members of the ii structure (see below), and returns the number of bytes used in the decoding. If it does not apply to the given instruction, it should return zero. If the decoder needs more data than valid_bytes it should return a negative number corresponding to the total number of bytes it will need to continue the decoding. The underlying architecture limits the number of bytes that can be requested, e.g. no more than 4 bytes can be requested on most RISC architectures. Simics will call the decoder again when more bytes are available. This process is repeated until the decoder accepts or rejects the instruction. A decoder should never request more data than it needs. For example, if an instructions can be rejected by looking at the first byte, the decoder should never ask for more bytes.
The instruction_info_t
is defined as follows:
typedef struct instruction_info { service_routine_t ii_ServiceRoutine; uint64 ii_Arg; unsigned int ii_Type; lang_void *ii_UserData; logical_address_t ii_LogicalAddress; physical_address_t ii_PhysicalAddress; } instruction_info_t;
ii_ServiceRoutine is a pointer to a function that will be called by Simics every time the instruction is executed. It has the following prototype:
typedef exception_type_t (*service_routine_t)(conf_object_t *cpu, uint64 arg, lang_void *user_data);
The service routine function should return an exception when it is
finished to signal its status. If no exception occurs
Sim_PE_No_Exception
should be returned.
See exception_type_t
in
src/include/simics/base/memory.h
for the different
exceptions available.
A special return value, Sim_PE_Default_Semantics
, can be
returned; this signals Simics to run the default semantics for the
instruction. This is useful if the semantics of an instruction
should be changed but the user routine does not want to handle it all
the time.
Note that in a shared memory multiprocessor, the CPU used in decoding may differ from the CPU that executes the instruction, since the decoded instructions may be cached.
ii_Arg is the argument arg that will be passed on to the service routine function. Op code bit-fields for the instruction such as register numbers or intermediate values can be stored here. The ii_UserData field can also be used to pass information to the service routine if more data is needed.
ii_Type is either UD_IT_SEQUENTIAL
or
UD_IT_CONTROL_FLOW
. A sequential type means that the
instruction does not perform any branches and the update of the
program counter(s) is handled by Simics. In a control flow
instruction on the other hand it is up to the user to set the
program counter(s).
ii_LogicalAddress and ii_PhysicalAddress holds the logical and physical addresses of the instruction to be decoded.
The disassemble function is called to disassemble an
instruction. It uses the same code,
valid_bytes, and cpu parameters as
the decode function. If the disassembly is valid, then
the string part of the returned tuple_int_string_t
struct
should be a MALLOCed string with the disassembly and the integer
part should be its length in bytes. The caller is responsible for
freeing the disassembly string. The string member should be NULL
and the integer part should be zero if the disassembly is not
valid. If the disassemble function needs more data than
valid_bytes it should return a negative number in
the integer part in the same way as the decode function,
and set the string part to NULL.
The flush function is called to free any memory
allocated when decoding an instruction and any user data associated
with the instruction. It should return zero if it does not
recognize the instruction, and non-zero if it has accepted it.
Usually, the way to recognize if a decoded instruction is the right
one to flush is to compare ii->ii_ServiceRoutine
with the
function that was set in the decode function. Note
that the cpu parameter is the processor that caused
the flush. It is more or less an arbitrary processor and should be
ignored.
In addition to the function pointers, the
decoder_t
structure contains a
user_data pointer that is passed to all the
functions. This can be used for passing any data to the decoder
functions.
The exception
interface is used together with the
Core_Exception hap to enable inspection abilities for triggered
exceptions.
The exception
interface is used to translate
exception numbers, as received by the Core_Exception hap, to names,
and vice versa.
The get_number function returns the number associated with an exception name, or -1 if the no exception with the given name exist. The get_name returns the name associated with an exception number. The get_source function is only used on X86 targets and returns the source for an exception, as an exception number can be raised from different sources. The all_exceptions function returns a list of all exceptions numbers.
The exception numbers are architecturally defined, while their names are defined by the model.
SIM_INTERFACE(exception) { int (*get_number)(conf_object_t *NOTNULL obj, const char *NOTNULL name); const char *(*get_name)(conf_object_t *NOTNULL obj, int exc); int (*get_source)(conf_object_t *NOTNULL obj, int exc); attr_value_t (*all_exceptions)(conf_object_t *NOTNULL obj); }; #define EXCEPTION_INTERFACE "exception"
SIM_INTERFACE(context_handler) { conf_object_t *(*get_current_context)(conf_object_t *obj); int (*set_current_context)(conf_object_t *obj, conf_object_t *ctx); }; #define CONTEXT_HANDLER_INTERFACE "context_handler"
The exec_trace
interface is implemented by processor models
that support tracing. A trace listener registers itself with the
register_tracer call. The tracer callback will be
called by the processor model
when each instruction is just about to be executed, passing the
tracer_data as passed to the register_tracer function
in addition to information about the instruction that is executed.
Invoke unregister_tracer with the same two pointers to deregister
the listener.
typedef void (*instruction_trace_callback_t)(lang_void *tracer_data, conf_object_t *cpu, linear_address_t la, logical_address_t va, physical_address_t pa, byte_string_t opcode);
The pa parameter to the callback will always be valid, but some CPU architectures may not support la or va. The la argument is typically only valid for x86 CPUs. Lastly, the opcode of the instruction is passed in opcode. The opcode is passed without endian conversion, meaning that byte X in opcode corresponds to the byte at pa + X.
SIM_INTERFACE(exec_trace) { void (*register_tracer)(conf_object_t *NOTNULL cpu_obj, instruction_trace_callback_t tracer, lang_void *tracer_data); void (*unregister_tracer)(conf_object_t *NOTNULL cpu_obj, instruction_trace_callback_t tracer, lang_void *tracer_data); }; #define EXEC_TRACE_INTERFACE "exec_trace"
The opcode_info
interface is implemented by
processors that need to communicate information about the encoding
of instructions to the GUI.
The get_opcode_length function returns information about instruction encoding in the current operating mode of the processor. The min_alignment field indicates the smallest allowed alignment of instructions, typically 4 for regular RISC architectures. The max_length field specifies the maximum instruction length in bytes. The avg_length is an approximation of the average instruction size.
typedef struct { int min_alignment; int max_length; int avg_length; } opcode_length_info_t; SIM_INTERFACE(opcode_info) { opcode_length_info_t (*get_opcode_length_info)(conf_object_t *obj); }; #define OPCODE_INFO_INTERFACE "opcode_info"
Add and remove virtual-address (and, on x86, linear-address) read and write breakpoints. On every read access that intersects a read breakpoint's interval, the registered callback function is called with the object that initiated the read, and the address and size of the read. (The interval includes both endpoints; first must be less than or equal to last.) Write breakpoints work exactly the same, except that the callback is given the actual value being written, not just its size.
The callback is called before the read or write has taken place, but may not intervene. If one or more breakpoint callbacks stop the simulation, the current instruction is completed before the stop takes effect. If more than one breakpoint is triggered by the same read or write, the implementation may call their callbacks in any order.
On x86, the Virtual_Breakpoint_Flag_Linear
flag causes the
breakpoint to use linear rather than virtual addresses. (Adding a
breakpoint with unsupported flags is illegal.)
typedef enum { Virtual_Breakpoint_Flag_Linear = 1 } virtual_breakpoint_flags_t;
SIM_INTERFACE(virtual_data_breakpoint) { virtual_data_bp_handle_t *NOTNULL (*add_read)( conf_object_t *NOTNULL obj, generic_address_t first, generic_address_t last, void (*NOTNULL callback)( cbdata_call_t data, conf_object_t *NOTNULL initiator, generic_address_t address, unsigned size), cbdata_register_t data, uint32 flags); virtual_data_bp_handle_t *NOTNULL (*add_write)( conf_object_t *NOTNULL obj, generic_address_t first, generic_address_t last, void (*NOTNULL callback)( cbdata_call_t data, conf_object_t *NOTNULL initiator, generic_address_t address, bytes_t value), cbdata_register_t data, uint32 flags); void (*remove)(conf_object_t *NOTNULL obj, virtual_data_bp_handle_t *NOTNULL bp_handle); }; #define VIRTUAL_DATA_BREAKPOINT_INTERFACE "virtual_data_breakpoint"
Add and remove virtual-address (and, on x86, linear-address) instruction breakpoints. Every time the processor executes an instruction that intersects the breakpoint's interval, the callback function is called with the processor, and the address and size of the instruction. (The interval includes both endpoints; first must be less than or equal to last.)
The callback is called before the instruction is executed. If one or more breakpoint callbacks stop the simulation, the stop takes effect before the instruction is run. (This means that once the simulation starts again, the same breakpoints will trigger immediately again. The callback can use VT_step_stamp to detect re-triggering.) If more than one breakpoint is triggered by the same instruction, the implementation may call their callbacks in any order.
If the filter function is non-null and returns false, the callback is not called. The filter function is supplied with the instruction opcode (the raw bytes of the instruction) and a processor (which may not be the same processor that the breakpoint is set on, but is guaranteed to be of the same class). The filter may base its decision only on the opcode bytes and the string obtained by asking the processor to disassemble the instruction; this allows the implementation to cache the result and omit future calls to the filter function where the opcode and disassembly string would be the same.
On x86, the Virtual_Breakpoint_Flag_Linear
flag causes the
breakpoint to use linear rather than virtual addresses. Calling with
unsupported flags is illegal.
typedef enum { Virtual_Breakpoint_Flag_Linear = 1 } virtual_breakpoint_flags_t;
SIM_INTERFACE(virtual_instruction_breakpoint) { virtual_instr_bp_handle_t *NOTNULL (*add)( conf_object_t *NOTNULL obj, generic_address_t first, generic_address_t last, bool (*filter)(cbdata_call_t filter_data, conf_object_t *NOTNULL cpu, bytes_t opcode), cbdata_register_t filter_data, void (*NOTNULL callback)( cbdata_call_t callback_data, conf_object_t *NOTNULL cpu, generic_address_t address, unsigned size), cbdata_register_t callback_data, uint32 flags); void (*remove)(conf_object_t *NOTNULL obj, virtual_instr_bp_handle_t *NOTNULL bp_handle); }; #define VIRTUAL_INSTRUCTION_BREAKPOINT_INTERFACE \ "virtual_instruction_breakpoint"
This interface is used by the Simics debugger to get certain information from a processor.
The first_child function returns the first description in the sequence of child descriptions of parent or NULL if parent has no children. Groups can have both registers and groups as children, registers can only have fields as children and fields cannot have any children. If parent is NULL, return the first description in the sequence of top-level descriptions.
Use next_description to deallocate the previous description and return the next description in the sequence or NULL if there are no more descriptions in the current sequence.
The free_description function is used to free the description without returning the next one in the sequence.
The first_named_value function returns the first named value in the sequence of named values for parent or NULL if there are no named values for parent. Only fields and registers can have named values.
Use next_named_value to deallocate the previous named value and return the next named value or NULL if there are no more named values in this sequence.
Use free_named_value to free the named value without returning the next one in the sequence.
The get and set functions are used to get and set the value of the register. To set the value pass in a bytes_t for the value. The value passed in must be long enough to contain the full value of the register. If the bytes_t is too long it will be truncated. To get the value pass in a buffer_t which is long enough to contain the register's value. The value is encoded in little endian byte order.
typedef enum { Description_Type_Group, Description_Type_Int_Reg, Description_Type_Float_Reg, Description_Type_Fields_Reg, Description_Type_Int_Field, Description_Type_Float_Field, } description_type_t;
typedef enum { Reg_Role_None, /* No special role for the register. */ Reg_Role_Program_Counter /* The register is the program counter. */ } reg_role_t;
typedef enum { Reg_Bitorder_Little_Endian, Reg_Bitorder_Big_Endian } reg_bitorder_t;
typedef struct { const char *name; const char *description; const bytes_t value; /* Little endian byte order */ } named_value_t;
typedef struct { /* Common fields */ description_type_t type; const char *name; const char *description; /* Register and field fields */ int16 dwarf_id; /* id used by dwarf for this register or -1 if no such id is defined. This is ABI specific, but the CPU will give the ids for the most common ABI for that architecture. */ reg_bitorder_t bitorder; /* Bitorder convention used in the documentation for this register or field. */ reg_role_t role; /* Role of this register in the ABI/HW. */ bool memory_mapped; /* True if the register is memory mapped. */ uint64 offset; /* Offset into the bank for memory mapped registers. */ bool catchable; /* True if Core_Control_Register_Write and Core_Control_Register_Read are triggered when this register is written or read. */ int msb, lsb; /* Most and least significant bit of the register or field. Always given in le bitorder. For groups msb == -1 and lsb == 0. */ int regsize; /* Number of bits in the register, or the register this field is a part of. */ int reg_id; /* For registers and fields the id to pass to the get and set methods to access the register's value. Fields have the same reg_id as the register they are a part of. Not valid for groups.*/ } description_t;
SIM_INTERFACE(describe_registers) { const description_t *(*first_child)( conf_object_t *NOTNULL obj, const description_t *parent); const description_t *(*next_description)( conf_object_t *NOTNULL obj, const description_t *prev); void (*free_description)(conf_object_t *NOTNULL obj, const description_t *desc); const named_value_t *(*first_named_value)( conf_object_t *NOTNULL obj, const description_t *parent); const named_value_t *(*next_named_value)( conf_object_t *NOTNULL obj, const named_value_t *prev); void (*free_named_value)(conf_object_t *NOTNULL obj, const named_value_t *nv); void (*get)(conf_object_t *NOTNULL obj, int reg_id, buffer_t dest); void (*set)(conf_object_t *NOTNULL obj, int reg_id, bytes_t value); }; #define DESCRIBE_REGISTERS_INTERFACE "describe_registers"