1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

//! Derive/attribute macros for simics-api

#![deny(clippy::unwrap_used)]
#![deny(missing_docs)]
#![forbid(unsafe_code)]

use attr_value::{
    from_attr_value_dict_impl, from_attr_value_list_impl, into_attr_value_dict_impl,
    into_attr_value_list_impl,
};
use class::{class_derive_impl, class_impl};
use conf_object::{as_conf_object_impl, from_conf_object_impl};
use exception::simics_exception_impl;
use init::simics_init_impl;
use interface::interface_impl;
use proc_macro::TokenStream;

mod attr_value;
mod class;
mod conf_object;
mod exception;
mod init;
mod interface;

#[allow(non_snake_case)]
#[proc_macro_derive(IntoAttrValueList, attributes(attr_value))]
/// Derive Macro for implementing conversion into an `AttrValue` list, where each struct
/// field's value is an entry in the heterogeneous list.
///
/// # Arguments
///
/// At the field level (i.e. on each field of a struct deriving this attribute), the
/// following attributes are supported:
///
/// * `#[attr_value(skip)]` - Do not include this field in the conversion.
/// * `#[attr_value(fallible)]` - If the field type does not implement `Into<AttrValue>`,
///   use its implementation of `TryInto<AttrValue>` instead. Whether this flag is necessary
///   cannot be automatically determined by this macro, so it must be specified manually.
pub fn IntoAttrValueList(input: TokenStream) -> TokenStream {
    into_attr_value_list_impl(input)
}

#[allow(non_snake_case)]
#[proc_macro_derive(IntoAttrValueDict, attributes(attr_value))]
/// Derive Macro for implementing conversion into an `AttrValue` dictionary, where each
/// struct field's key, value pair is an entry in the dictionary.
///
/// # Arguments
///
/// At the field level (i.e. on each field of a struct deriving this attribute), the
/// following attributes are supported:
///
/// * `#[attr_value(skip)]` - Do not include this field in the conversion.
/// * `#[attr_value(fallible)]` - If the field type does not implement `Into<AttrValue>`,
///   use its implementation of `TryInto<AttrValue>` instead. Whether this flag is necessary
///   cannot be automatically determined by this macro, so it must be specified manually.
pub fn IntoAttrValueDict(input: TokenStream) -> TokenStream {
    into_attr_value_dict_impl(input)
}

#[allow(non_snake_case)]
#[proc_macro_derive(FromAttrValueList, attributes(attr_value))]
/// Derive Macro for implementing conversion from an `AttrValue` list into a struct, where
/// each entry in the list is a struct field's value.
///
/// # Arguments
///
/// At the field level (i.e. on each field of a struct deriving this attribute), the
/// following attributes are supported:
///
/// * `#[attr_value(fallible)]` - If the field type does not implement `From<AttrValue>`,
///   use its implementation of `TryFrom<AttrValue>` instead. Whether this flag is necessary
///   cannot be automatically determined by this macro, so it must be specified manually.
pub fn FromAttrValueList(input: TokenStream) -> TokenStream {
    from_attr_value_list_impl(input)
}

#[allow(non_snake_case)]
#[proc_macro_derive(FromAttrValueDict, attributes(attr_value))]
/// Derive Macro for implementing conversion from an `AttrValue` dict into a struct, where
/// each key, value pair in the dict is a struct field's name, value pair.
///
/// # Arguments
///
/// At the field level (i.e. on each field of a struct deriving this attribute), the
/// following attributes are supported:
///
/// * `#[attr_value(fallible)]` - If the field type does not implement `From<AttrValue>`,
///   use its implementation of `TryFrom<AttrValue>` instead. Whether this flag is necessary
///   cannot be automatically determined by this macro, so it must be specified manually.
pub fn FromAttrValueDict(input: TokenStream) -> TokenStream {
    from_attr_value_dict_impl(input)
}

#[allow(non_snake_case)]
#[proc_macro_derive(Class, attributes(class))]
/// Derive macro for implementing the `Class` trait for the annotated type
pub fn Class(input: TokenStream) -> TokenStream {
    class_derive_impl(input)
}

#[proc_macro_attribute]
/// Attribute macro for declaring a Simics class for a Rust struct type
pub fn class(args: TokenStream, input: TokenStream) -> TokenStream {
    class_impl(args, input)
}

#[allow(non_snake_case)]
#[proc_macro_derive(AsConfObject, attributes(conf_object))]
/// Derive macro for implementing conversion to raw `ConfObject` pointers.
/// This macro implements the `AsConfObject` trait for the annotated type.
pub fn AsConfObject(input: TokenStream) -> TokenStream {
    as_conf_object_impl(input)
}

#[allow(non_snake_case)]
#[proc_macro_derive(FromConfObject, attributes(conf_object))]
/// Derive macro for implementing conversion from raw `ConfObject` pointers.
/// This macro implements the `FromConfObject` trait for the annotated type.
///
/// # Arguments
///
/// At the item level (i.e. on the struct deriving this attribute), the following
/// attributes are supported:
///
/// * `#[conf_object(skip_from)]` - Skip also implementing `From<*_ ConfObject>` which is the
///   default behavior.
pub fn FromConfObject(input: TokenStream) -> TokenStream {
    from_conf_object_impl(input)
}

#[proc_macro_attribute]
/// Marks a function as being a SIMICS API that can throw exceptions in called FFI APIs.
///
/// A SIMICS exception can be generated by most APIs. This macro makes the function
/// private, wraps it, and adds the requisite code to check for and report exceptions.
/// `clear_exception` should *not* be called inside the wrapped function. `last_error`
/// may be called, however, as any exceptions will be cleared after the wrapped function
/// returns.
///
/// # Examples
///
/// Add the `#[simics_exception]` attribute to a function which calls a SIMICS API that can throw
/// exceptions. The function will be wrapped and the requisite code to check for and report
/// exceptions will be added.
///
/// ```rust,ignore
/// #[simics_exception]
/// pub fn write_byte(physical_memory: *mut ConfObject, physical_addr: u64, byte: u8) {
///     unsafe { SIM_write_byte(physical_memory, physical_addr, byte) };
/// }
/// ```
///
/// This expands to:
///
/// ```rust,ignore
/// fn _write_byte(physical_memory: *mut ConfObject, physical_addr: u64, byte: u8) {
///     unsafe { SIM_write_byte(physical_memory, physical_addr, byte) };
/// }
///
/// pub fn write_byte(physical_memory: *mut ConfObject, physical_addr: u64, byte: u8) -> Result<()> {
///     let res = _write_byte(physical_memory, physical_addr, byte);
///
///     match simics::get_pending_exception() {
///         SimException::SimExc_No_Exception => Ok(()),
///         exception => {
///             clear_exception();
///             Err(Error::from(exception))
///         }
///     }
/// }
/// ```
pub fn simics_exception(args: TokenStream, input: TokenStream) -> TokenStream {
    simics_exception_impl(args, input)
}

#[proc_macro_attribute]
/// Mark a function as being the initializer of a Simics module.
///
/// This function will be called on module load and should be used to initialize the
/// module. This macro will add the requisite
/// code to call the function on module load.
pub fn simics_init(args: TokenStream, input: TokenStream) -> TokenStream {
    simics_init_impl(args, input)
}

#[proc_macro_attribute]
/// Declare a struct implementation as a SIMICS API interface.
///
/// This macro will add the requisite code to implement the interface which allows
/// methods in the impl to be called
/// from Simics scripts in Python or the Simics language.
pub fn interface(args: TokenStream, input: TokenStream) -> TokenStream {
    interface_impl(args, input)
}