@@ -17,9 +17,10 @@
use std::fmt::Write;
-use crate::spec::OutFileDef;
+use crate::spec::{OutFileDef, StructDef, Typ};
use convert_case::{Case, Casing};
+use log::{debug, trace};
/// An abstract indentation level. 0 is no indentation, 1 is [`INDENT_WIDTH`]
/// and so on.
@@ -29,6 +30,39 @@ struct Indentation(usize);
/// Default width of each level of indentation
const INDENT_WIDTH: usize = 4;
+/// Create a C-compatible struct field. Without the terminating semicolon.
+fn structfield(typ: &Typ, name: &str) -> String {
+ match typ {
+ Typ::Ptr(x) => {
+ let t: &Typ = x;
+ format!(
+ "XEN_GUEST_HANDLE_64({}) {name}",
+ match t {
+ Typ::U8 => "uint8",
+ Typ::U16 => "uint16",
+ Typ::U32 => "uint32",
+ Typ::U64 => "uint64_aligned_t",
+ Typ::I8 => "int8",
+ Typ::I16 => "int16",
+ Typ::I32 => "int32",
+ Typ::I64 => "int64_aligned_t",
+ _ => panic!("foo {t:?}"),
+ }
+ )
+ }
+ Typ::Struct(x) => format!("struct {x} {name}"),
+ Typ::Array(x, len) => format!("{}{name}[{len}]", structfield(x, "")),
+ Typ::U8 => format!("uint8_t {name}"),
+ Typ::U16 => format!("uint16_t {name}"),
+ Typ::U32 => format!("uint32_t {name}"),
+ Typ::U64 => format!("uint64_aligned_t {name}"),
+ Typ::I8 => format!("int8_t {name}"),
+ Typ::I16 => format!("int16_t {name}"),
+ Typ::I32 => format!("int32_t {name}"),
+ Typ::I64 => format!("int64_aligned_t {name}"),
+ }
+}
+
/// Add a comment to a struct or a field.
fn comment(out: &mut String, comment: &str, ind: Indentation) {
let spaces = " ".repeat(INDENT_WIDTH * ind.0);
@@ -48,6 +82,22 @@ fn comment(out: &mut String, comment: &str, ind: Indentation) {
}
}
+/// Write a C-compatible struct onto `out`
+fn structgen(out: &mut String, def: &StructDef) {
+ debug!("struct {}", def.name);
+
+ comment(out, &def.description, Indentation(0));
+ writeln!(out, "struct {} {{", def.name.to_case(Case::Snake)).unwrap();
+ for f in &def.fields {
+ trace!(" field {} type={:?}", f.name, f.typ);
+
+ comment(out, &f.description, Indentation(1));
+ writeln!(out, " {};", structfield(&f.typ, &f.name),).unwrap();
+ }
+ writeln!(out, "}};").unwrap();
+ writeln!(out).unwrap();
+}
+
/// Generates a single `.h` file.
///
/// `filedef` is a language-agnostic high level description of what the output
@@ -67,6 +117,10 @@ pub fn parse(filedef: &OutFileDef) -> String {
writeln!(out, "#ifndef __XEN_AUTOGEN_{name}_H").unwrap();
writeln!(out, "#define __XEN_AUTOGEN_{name}_H\n").unwrap();
+ for def in &filedef.structs {
+ structgen(&mut out, def);
+ }
+
writeln!(out, "#endif /* __XEN_AUTOGEN_{name}_H */\n").unwrap();
out
@@ -17,9 +17,61 @@ use std::{fs::read_to_string, path::Path};
use log::{debug, info};
+/// An IDL type. A type may be a primitive integer, a pointer to an IDL type,
+/// an array of IDL types or a struct composed of IDL types. Every integer must
+/// be aligned to its size.
+///
+/// FIXME: This enumerated type is recovered as-is from the `typ` field in the
+/// TOML files. Ideally, that representation should be more ergonomic and the
+/// parser instructed to deal with it.
+#[allow(clippy::missing_docs_in_private_items)]
+#[derive(Debug, serde::Deserialize, PartialEq)]
+#[serde(rename_all = "lowercase", tag = "tag", content = "args")]
+pub enum Typ {
+ Struct(String),
+ U8,
+ U16,
+ U32,
+ U64,
+ I8,
+ I16,
+ I32,
+ I64,
+ Ptr(Box<Typ>),
+ Array(Box<Typ>, usize),
+}
+
+/// Deserialized form of a hypercall struct
+#[derive(Debug, serde::Deserialize)]
+pub struct StructDef {
+ /// Name of the struct
+ pub name: String,
+ /// Description of what the struct is for. This string is added as a comment
+ /// on top of the autogenerated struct.
+ pub description: String,
+ /// Fields contained in the struct. May be none, in which case it's a zero
+ /// length struct.
+ pub fields: Vec<FieldDef>,
+}
+
+/// Deserialized form of a field within a hypercall struct (see [`StructDef`])
+#[derive(Debug, serde::Deserialize)]
+pub struct FieldDef {
+ /// Name of the field
+ pub name: String,
+ /// Description of what the field is for. This string is added as a comment
+ /// on top of the autogenerated field.
+ pub description: String,
+ /// Type of the field.
+ pub typ: Typ,
+}
+
/// A language-agnostic specification.
#[derive(Debug, serde::Deserialize)]
-struct InFileDef;
+struct InFileDef {
+ /// List of structs described in this input specification.
+ structs: Option<Vec<StructDef>>,
+}
/// Description of an abstract output (i.e: `.rs`, `.h`, etc).
///
@@ -28,6 +80,8 @@ struct InFileDef;
pub struct OutFileDef {
/// The name of the output file, without the final extension.
pub name: String,
+ /// List of structs described by all input spec files merged on this file.
+ pub structs: Vec<StructDef>,
}
impl OutFileDef {
@@ -40,13 +94,19 @@ impl OutFileDef {
pub fn new(name: String, dir: &Path) -> Result<Self, Error> {
info!("Reading {dir:?} to generate an output file");
- let ret = Self { name };
+ let mut ret = Self {
+ name,
+ structs: Vec::new(),
+ };
for entry in from_ioerr(dir.read_dir())? {
let path = from_ioerr(entry)?.path();
debug!("Reading {:?} to generate outfile={}", path, ret.name);
let toml_str = from_ioerr(read_to_string(path))?;
let filedef: InFileDef = toml::from_str(&toml_str).map_err(Error::Toml)?;
+ if let Some(structs) = filedef.structs {
+ ret.structs.extend(structs);
+ }
}
Ok(ret)
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com> --- tools/rust/xenbindgen/src/c_lang.rs | 56 ++++++++++++++++++++++++- tools/rust/xenbindgen/src/spec.rs | 64 ++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-)