from __future__ import division, print_function
from functools import reduce
import operator
import itertools
import xml.dom.minidom
from argparse import ArgumentParser
import sys
WORD_SIZE_BITS_ARCH = {
"aarch32": 32,
"ia32": 32,
"aarch64": 64,
"ia64": 64,
"x86_64": 64,
"arm_hyp": 32,
}
MESSAGE_REGISTERS_FOR_ARCH = {
"aarch32": 4,
"ia32": 2,
"x86_64": 4,
"arm_hyp": 4,
}
WORD_CONST_SUFFIX_BITS = {
32: "",
64: "",
}
MAX_MESSAGE_LENGTH = 32
TYPE_TRANS = {
"int": "isize",
"seL4_Uint8": "u8",
"seL4_Uint16": "u16",
"seL4_Uint32": "u32",
"seL4_Uint64": "u64",
"seL4_Bool": "u8",
"seL4_CapData_t": "seL4_CapData",
"seL4_PrioProps_t": "seL4_PrioProps",
"seL4_CapRights_t": "seL4_CapRights",
}
INCLUDES = [
'autoconf.h', 'sel4/types.h'
]
def translate_type(name):
if name in TYPE_TRANS:
return TYPE_TRANS[name]
else:
return name
def translate_expr(name):
if name == "type":
return "type_"
return name
def construction(expr, param):
if isinstance(param.type, StructType):
return "%s { words: [%s] }" % (param.type.name, expr)
else:
return "%s as %s" % (expr, translate_type(param.type.name))
TYPES = {
8: "u8",
16: "u16",
32: "u32",
64: "u64"
}
DEFAULT_CAP_DESC = "The capability to the %(interface_manual_name)s which is being operated on."
class Type(object):
def __init__(self, name, size_bits, wordsize, double_word=False, native_size_bits=None):
self.name = name
self.size_bits = size_bits
self.wordsize = wordsize
self.double_word = double_word
if native_size_bits:
self.native_size_bits = native_size_bits
else:
self.native_size_bits = size_bits
def pass_by_reference(self):
return self.size_bits > self.wordsize and not self.double_word
def render_parameter_name(self, name):
if name == "type":
name = "type_"
return "%s: %s" % (name, translate_type(self.name))
def pointer(self):
return PointerType(self, self.wordsize)
def c_expression(self, var_name, word_num=0):
assert word_num == 0
return "%s" % var_name
def double_word_expression(self, var_name, word_num, word_size):
assert word_num == 0 or word_num == 1
if word_num == 0:
return "{1} as {0}".format(TYPES[self.size_bits], var_name)
elif word_num == 1:
return "{1}.wrapping_shr({2}) as {0}".format(TYPES[self.size_bits], var_name,
word_size)
class PointerType(Type):
def __init__(self, base_type, wordsize):
Type.__init__(self, base_type.name, wordsize, wordsize)
self.base_type = base_type
def render_parameter_name(self, name):
if name == "type":
name = "type_"
return "%s: *mut %s" % (name, translate_type(self.name))
def c_expression(self, var_name, word_num=0):
assert word_num == 0
return "unsafe { *%s }" % var_name
def pointer(self):
raise NotImplementedError()
class CapType(Type):
def __init__(self, name, wordsize):
Type.__init__(self, name, wordsize, wordsize)
class StructType(Type):
def __init__(self, name, size_bits, wordsize):
Type.__init__(self, name, size_bits, wordsize)
def c_expression(self, var_name, word_num, member_name):
assert word_num < self.size_bits // self.wordsize
assert self.pass_by_reference()
return "(*%s).%s" % (var_name, member_name[word_num])
class BitFieldType(Type):
def __init__(self, name, size_bits, wordsize):
Type.__init__(self, name, size_bits, wordsize)
def c_expression(self, var_name, word_num=0):
return "%s.words[%d]" % (var_name, word_num)
class Parameter(object):
def __init__(self, name, type):
self.name = name
self.type = type
class Api(object):
def __init__(self, name):
self.name = name
def init_data_types(wordsize):
types = [
Type("int", 32, wordsize),
Type("long", wordsize, wordsize),
Type("seL4_Uint8", 8, wordsize),
Type("seL4_Uint16", 16, wordsize),
Type("seL4_Uint32", 32, wordsize),
Type("seL4_Uint64", 64, wordsize, double_word=(wordsize == 32)),
Type("seL4_Word", wordsize, wordsize),
Type("seL4_Bool", 1, wordsize, native_size_bits=8),
BitFieldType("seL4_CapData_t", wordsize, wordsize),
BitFieldType("seL4_PrioProps_t", wordsize, wordsize),
BitFieldType("seL4_CapRights_t", wordsize, wordsize),
CapType("seL4_CPtr", wordsize),
CapType("seL4_CNode", wordsize),
CapType("seL4_IRQHandler", wordsize),
CapType("seL4_IRQControl", wordsize),
CapType("seL4_TCB", wordsize),
CapType("seL4_Untyped", wordsize),
CapType("seL4_DomainSet", wordsize),
]
return types
def init_arch_types(wordsize):
arch_types = {
"aarch32" : [
Type("seL4_ARM_VMAttributes", wordsize, wordsize),
CapType("seL4_ARM_Page", wordsize),
CapType("seL4_ARM_PageTable", wordsize),
CapType("seL4_ARM_PageDirectory", wordsize),
CapType("seL4_ARM_ASIDControl", wordsize),
CapType("seL4_ARM_ASIDPool", wordsize),
CapType("seL4_ARM_VCPU", wordsize),
CapType("seL4_ARM_IOSpace", wordsize),
CapType("seL4_ARM_IOPageTable", wordsize),
StructType("seL4_UserContext", wordsize * 17, wordsize),
],
"arm_hyp" : [
Type("seL4_ARM_VMAttributes", wordsize, wordsize),
CapType("seL4_ARM_Page", wordsize),
CapType("seL4_ARM_PageTable", wordsize),
CapType("seL4_ARM_PageDirectory", wordsize),
CapType("seL4_ARM_ASIDControl", wordsize),
CapType("seL4_ARM_ASIDPool", wordsize),
CapType("seL4_ARM_VCPU", wordsize),
CapType("seL4_ARM_IOSpace", wordsize),
CapType("seL4_ARM_IOPageTable", wordsize),
StructType("seL4_UserContext", wordsize * 17, wordsize),
],
"ia32" : [
Type("seL4_X86_VMAttributes", wordsize, wordsize),
CapType("seL4_X86_IOPort", wordsize),
CapType("seL4_X86_ASIDControl", wordsize),
CapType("seL4_X86_ASIDPool", wordsize),
CapType("seL4_X86_IOSpace", wordsize),
CapType("seL4_X86_Page", wordsize),
CapType("seL4_X86_PageDirectory", wordsize),
CapType("seL4_X86_PageTable", wordsize),
CapType("seL4_X86_IOPageTable", wordsize),
CapType("seL4_X86_VCPU", wordsize),
CapType("seL4_X86_EPTPML4", wordsize),
CapType("seL4_X86_EPTPDPT", wordsize),
CapType("seL4_X86_EPTPD", wordsize),
CapType("seL4_X86_EPTPT", wordsize),
StructType("seL4_VCPUContext", wordsize * 7 ,wordsize),
StructType("seL4_UserContext", wordsize * 13, wordsize),
],
"x86_64" : [
Type("seL4_X86_VMAttributes", wordsize, wordsize),
CapType("seL4_X86_IOPort", wordsize),
CapType("seL4_X86_ASIDControl", wordsize),
CapType("seL4_X86_ASIDPool", wordsize),
CapType("seL4_X86_IOSpace", wordsize),
CapType("seL4_X86_Page", wordsize),
CapType("seL4_X64_PML4", wordsize),
CapType("seL4_X86_PDPT", wordsize),
CapType("seL4_X86_PageDirectory", wordsize),
CapType("seL4_X86_PageTable", wordsize),
CapType("seL4_X86_IOPageTable", wordsize),
CapType("seL4_X86_VCPU", wordsize),
CapType("seL4_X86_EPTPML4", wordsize),
CapType("seL4_X86_EPTPDPT", wordsize),
CapType("seL4_X86_EPTPD", wordsize),
CapType("seL4_X86_EPTPT", wordsize),
StructType("seL4_VCPUContext", wordsize * 7 ,wordsize),
StructType("seL4_UserContext", wordsize * 19, wordsize),
]
}
return arch_types
def struct_members(typ, structs):
members = [member for struct_name, member in structs if struct_name == typ.name]
assert len(members) == 1
return members[0]
def align_up(x, a):
if x % a == 0:
return x
return x + a - (x % a)
def get_parameter_positions(parameters, wordsize):
bits_used = 0
results = []
for param in parameters:
type_size = param.type.size_bits
assert ((type_size & (type_size - 1)) == 0) or (type_size % wordsize == 0)
bits_used = align_up(bits_used, min(type_size, wordsize))
results.append((param, bits_used, type_size))
bits_used += type_size
return results
def generate_param_list(input_params, output_params):
params = []
for param in input_params:
if not param.type.pass_by_reference():
params.append(param.type.render_parameter_name(param.name))
else:
params.append(param.type.pointer().render_parameter_name(param.name))
for param in output_params:
if param.type.pass_by_reference():
params.append(param.type.pointer().render_parameter_name(param.name))
return ", ".join(params)
def generate_marshal_expressions(params, num_mrs, structs, wordsize):
def generate_param_code(param, first_bit, num_bits, word_array, wordsize):
target_word = first_bit // wordsize
target_offset = first_bit % wordsize
if param.type.double_word:
word_array[target_word].append(param.type.double_word_expression(param.name, 0, wordsize))
word_array[target_word + 1].append(param.type.double_word_expression(param.name, 1, wordsize))
return
if num_bits == wordsize:
assert target_offset == 0
expr = param.type.c_expression(param.name)
word_array[target_word].append(expr)
return
if num_bits < wordsize:
expr = param.type.c_expression(param.name)
expr = "(%s & %#x%s)" % (expr, (1 << num_bits) - 1,
WORD_CONST_SUFFIX_BITS[wordsize])
if target_offset:
expr = "(%s as seL4_Word).wrapping_shl(%d)" % (expr, target_offset)
word_array[target_word].append(expr)
return
assert target_offset == 0
num_words = num_bits // wordsize
for i in range(num_words):
expr = param.type.c_expression(param.name, i, struct_members(param.type, structs))
word_array[target_word + i].append(expr)
positions = get_parameter_positions(params, wordsize)
words = [[] for _ in range(num_mrs, MAX_MESSAGE_LENGTH)]
for (param, first_bit, num_bits) in positions:
generate_param_code(param, first_bit, num_bits, words, wordsize)
return [" | ".join(map(lambda x: "(" + translate_expr(x) + " as seL4_Word)", x)) for x in words if len(x) > 0]
def generate_unmarshal_expressions(params, wordsize):
def unmarshal_single_param(first_bit, num_bits, wordsize):
first_word = first_bit // wordsize
bit_offset = first_bit % wordsize
if num_bits > wordsize:
result = []
for x in range(num_bits // wordsize):
result.append("%%(w%d)s" % (x + first_word))
return result
if num_bits == wordsize:
return ["%%(w%d)s" % first_word]
elif bit_offset == 0:
return ["(%%(w%d)s & %#x)" % (
first_word, (1 << num_bits) - 1)]
else:
return ["(%%(w%d)s.wrapping_shr(%d)) & %#x" % (
first_word, bit_offset, (1 << num_bits) - 1)]
positions = get_parameter_positions(params, wordsize)
results = []
for (param, first_bit, num_bits) in positions:
results.append((param, unmarshal_single_param(first_bit, num_bits, wordsize)))
return results
def generate_result_struct(interface_name, method_name, output_params):
if len([x for x in output_params if not x.type.pass_by_reference()]) == 0:
return None
result = []
result.append("#[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct %s_%s {" % (interface_name, method_name))
result.append("\tpub error: isize,")
for i in output_params:
if not i.type.pass_by_reference():
result.append("\tpub %s," % i.type.render_parameter_name(i.name))
result.append("}")
result.append("")
return "\n".join(result)
def generate_stub(arch, wordsize, interface_name, method_name, method_id, input_params, output_params, structs, use_only_ipc_buffer, comment):
result = []
if use_only_ipc_buffer:
num_mrs = 0
else:
num_mrs = MESSAGE_REGISTERS_FOR_ARCH[arch]
standard_params = []
cap_params = []
for x in input_params:
if isinstance(x.type, CapType):
cap_params.append(x)
else:
standard_params.append(x)
returning_struct = False
results_structure = generate_result_struct(interface_name, method_name, output_params)
if results_structure:
return_type = "%s_%s" % (interface_name, method_name)
returning_struct = True
else:
return_type = "isize"
result.append(comment)
result.append("#[inline(always)]")
result.append("pub unsafe fn %s_%s(%s) -> %s" % (interface_name, method_name,
generate_param_list(input_params, output_params), return_type))
result.append("{")
input_expressions = generate_marshal_expressions(standard_params, num_mrs,
structs, wordsize)
cap_expressions = [x.name for x in cap_params]
service_cap = cap_expressions[0]
cap_expressions = cap_expressions[1:]
input_param_words = len(input_expressions)
output_param_words = sum([p.type.size_bits for p in output_params]) // wordsize
result.append("\tlet mut result: %s = ::core::mem::zeroed();" % return_type)
result.append("\tlet tag = seL4_MessageInfo::new(InvocationLabel::%s as seL4_Word, 0, %d, %d);" % (method_id, len(cap_expressions), len(input_expressions)))
result.append("\tlet output_tag;")
for i in range(num_mrs):
result.append("\tlet mut mr%d: seL4_Word = 0;" % i)
result.append("")
if len(cap_expressions) > 0:
result.append("\t/* Setup input capabilities. */")
for i in range(len(cap_expressions)):
result.append("\tseL4_SetCap(%d, %s);" % (i, cap_expressions[i]))
result.append("")
if max(num_mrs, len(input_expressions)) > 0:
result.append("\t/* Marshal and initialize parameters. */")
for i in range(num_mrs):
if i < len(input_expressions):
result.append("\tmr%d = %s as seL4_Word;" % (i, input_expressions[i]))
else:
result.append("\tmr%d = 0;" % i)
for i in range(num_mrs, len(input_expressions)):
if input_expressions[i] == "type":
input_expressions[i] = "type_"
result.append("\tseL4_SetMR(%d, %s);" % (i, input_expressions[i]))
result.append("")
if use_only_ipc_buffer:
result.append("\t/* Perform the call. */")
result.append("\toutput_tag = seL4_Call(%s, tag);" % service_cap)
else:
result.append("\t/* Perform the call, passing in-register arguments directly. */")
result.append("\toutput_tag = seL4_CallWithMRs(%s, tag," % (service_cap))
result.append("\t\t%s);" % ', '.join(
("&mut mr%d" % i) for i in range(num_mrs)))
label = "result.error" if returning_struct else "result"
result.append("\t%s = output_tag.get_label() as _;" % label);
result.append("")
if not use_only_ipc_buffer:
result.append("\t/* Unmarshal registers into IPC buffer on error. */")
result.append("\tif (%s != seL4_Error::seL4_NoError as u32) {" % label)
for i in range(num_mrs):
result.append("\t\tseL4_SetMR(%d, mr%d);" % (i, i))
if returning_struct:
result.append("\t\treturn result;")
result.append("\t}")
result.append("")
if len(output_params) > 0:
result.append("\t/* Unmarshal result. */")
source_words = {}
for i in range(MAX_MESSAGE_LENGTH):
if i < num_mrs:
source_words["w%d" % i] = "mr%d" % i
else:
source_words["w%d" % i] = "seL4_GetMR(%d)" % i
unmashalled_params = generate_unmarshal_expressions(output_params, wordsize)
for (param, words) in unmashalled_params:
param.name = translate_expr(param.name)
if param.type.pass_by_reference():
members = struct_members(param.type, structs)
for i in range(len(words)):
result.append("\t(*%s).%s = %s;" %
(param.name, members[i], words[i] % source_words))
else:
if param.type.double_word:
result.append("\tresult.%s = ((%s)%s + ((%s)%s.wrapping_shr(32)));" %
(param.name, TYPES[64], words[0] % source_words,
TYPES[64], words[1] % source_words))
else:
for word in words:
result.append("\tresult.%s = %s;" % (param.name, construction(word % source_words, param)))
result.append("\treturn result;")
result.append("}")
return "\n".join(result) + "\n"
def get_xml_element_contents(element):
return "".join([c.toxml() for c in element.childNodes])
def get_xml_element_content_with_xmlonly(element):
result = []
prev_element = False
for node in element.childNodes:
if node.nodeType == xml.dom.Node.TEXT_NODE:
if prev_element:
result.append(" @endxmlonly ")
prev_element = False
else:
if not prev_element:
result.append(" @xmlonly ")
prev_element = True
result.append(node.toxml())
return "".join(result)
def normalise_text(text):
stripped = text.strip()
stripped_lines = [line.strip() for line in text.split("\n")]
stripped_head = list(itertools.dropwhile(lambda s: not s, stripped_lines))
stripped_tail = itertools.dropwhile(lambda s: not s, reversed(stripped_head))
return "\n".join(reversed(list(stripped_tail)))
def parse_xml_file(input_file, valid_types):
type_names = {}
for i in valid_types:
type_names[i.name] = i
methods = []
structs = []
doc = xml.dom.minidom.parse(input_file)
api = Api(doc.getElementsByTagName("api")[0].getAttribute("name"))
for struct in doc.getElementsByTagName("struct"):
_struct_members = []
struct_name = struct.getAttribute("name")
for members in struct.getElementsByTagName("member"):
member_name = members.getAttribute("name")
_struct_members.append(member_name)
structs.append((struct_name, _struct_members))
for interface in doc.getElementsByTagName("interface"):
interface_name = interface.getAttribute("name")
interface_manual_name = interface.getAttribute("manual_name") or interface_name
interface_cap_description = interface.getAttribute("cap_description") or DEFAULT_CAP_DESC % {"interface_manual_name": interface_manual_name}
for method in interface.getElementsByTagName("method"):
method_name = method.getAttribute("name")
method_id = method.getAttribute("id")
method_condition = method.getAttribute("condition")
method_manual_name = method.getAttribute("manual_name") or method_name
method_manual_label = method.getAttribute("manual_label") or \
"%s_%s" % (interface_manual_name.lower(), method_name.lower())
comment_lines = ["@xmlonly <manual name=\"%s - %s\" label=\"%s\"/> @endxmlonly" %
(interface_manual_name, method_manual_name, method_manual_label)]
method_brief = method.getElementsByTagName("brief")
if method_brief:
method_brief_text = get_xml_element_contents(method_brief[0])
normalised_method_brief_text = normalise_text(method_brief_text)
comment_lines.append("@brief @xmlonly %s @endxmlonly" % normalised_method_brief_text)
method_description = method.getElementsByTagName("description")
if method_description:
method_description_text = get_xml_element_contents(method_description[0])
normalised_method_description_text = normalise_text(method_description_text)
comment_lines.append("\n@xmlonly\n%s\n@endxmlonly\n" % normalised_method_description_text)
method_return_description = method.getElementsByTagName("return")
if method_return_description:
comment_lines.append("@return @xmlonly %s @endxmlonly" % get_xml_element_contents(method_return_description[0]))
input_params = [Parameter("_service", type_names[interface_name])]
cap_description = interface_cap_description
cap_param = method.getElementsByTagName("cap_param")
if cap_param:
append_description = cap_param[0].getAttribute("append_description")
if append_description:
cap_description += append_description
comment_lines.append("@param[in] _service %s" % cap_description)
output_params = []
for param in method.getElementsByTagName("param"):
param_name = param.getAttribute("name")
param_type = type_names.get(param.getAttribute("type"))
if not param_type:
raise Exception("Unknown type '%s'." % (param.getAttribute("type")))
param_dir = param.getAttribute("dir")
assert (param_dir == "in") or (param_dir == "out")
if param_dir == "in":
input_params.append(Parameter(param_name, param_type))
else:
output_params.append(Parameter(param_name, param_type))
if param_dir == "in" or param_type.pass_by_reference():
param_description = param.getAttribute("description")
if not param_description:
param_description_element = param.getElementsByTagName("description")
if param_description_element:
param_description_text = get_xml_element_content_with_xmlonly(param_description_element[0])
param_description = normalise_text(param_description_text)
comment_lines.append("@param[%s] %s %s " % (param_dir, param_name, param_description))
comment_lines = reduce(operator.add, [l.split("\n") for l in comment_lines], [])
comment = "\n".join(["/**"] + [" * %s" % l for l in comment_lines] + [" */"])
methods.append((interface_name, method_name, method_id, input_params, output_params, method_condition, comment))
return (methods, structs, api)
def generate_stub_file(arch, wordsize, input_files, output_file, use_only_ipc_buffer):
result = []
if arch not in WORD_SIZE_BITS_ARCH.keys():
raise Exception("Invalid architecture.")
data_types = init_data_types(wordsize)
arch_types = init_arch_types(wordsize)
methods = []
structs = []
for infile in input_files:
method, struct, _ = parse_xml_file(infile, data_types + arch_types[arch])
methods += method
structs += struct
result.append("""
/*
* Automatically generated system call stubs.
*/
""");
result.append("/*")
result.append(" * Return types for generated methods.")
result.append(" */")
for (interface_name, method_name, _, _, output_params, _, _) in methods:
results_structure = generate_result_struct(interface_name, method_name, output_params)
if results_structure:
result.append(results_structure)
result.append("/*")
result.append(" * Generated stubs.")
result.append(" */")
for (interface_name, method_name, method_id, inputs, outputs, condition, comment) in methods:
if condition != "":
condition = condition.replace('defined', '')
condition = condition.replace('(', '')
condition = condition.replace(')', '')
if 'CONFIG_' in condition:
condition = 'feature = "' + condition + '"'
if '>' not in condition:
result.append("#[cfg(%s)]" % condition)
result.append(generate_stub(arch, wordsize, interface_name, method_name,
method_id, inputs, outputs, structs, use_only_ipc_buffer, comment))
output = open(output_file, "w")
output.write("\n".join(result))
output.close()
def process_args():
usage_str = """
%(prog)s [OPTIONS] [FILES] """
epilog_str = """
"""
parser = ArgumentParser(description='seL4 System Call Stub Generator.',
usage=usage_str,
epilog=epilog_str)
parser.add_argument("-o", "--output", dest="output", default="/dev/stdout",
help="Output file to write stub to. (default: %(default)s).")
parser.add_argument("-b", "--buffer", dest="buffer", action="store_true", default=False,
help="Use IPC buffer exclusively, i.e. do not pass syscall arguments by registers. (default: %(default)s)")
parser.add_argument("-a", "--arch", dest="arch", required=True, choices=WORD_SIZE_BITS_ARCH,
help="Architecture to generate stubs for.")
wsizegroup = parser.add_mutually_exclusive_group()
wsizegroup.add_argument("-w", "--word-size", dest="wsize",
help="Word size(in bits), for the platform.")
wsizegroup.add_argument("-c", "--cfile", dest="cfile",
help="Config file for Kbuild, used to get Word size.")
parser.add_argument("files", metavar="FILES", nargs="+",
help="Input XML files.")
return parser
def main():
parser = process_args()
args = parser.parse_args()
if not (args.wsize or args.cfile):
parser.error("Require either -w/--word-size or -c/--cfile argument.")
sys.exit(2)
wordsize = -1
wordsize = int(args.wsize)
if wordsize is -1:
print("Invalid word size: %s" % wordsize)
sys.exit(2)
generate_stub_file(args.arch, wordsize, args.files, args.output, args.buffer)
if __name__ == "__main__":
sys.exit(main())