Skip to content

Commit ddece09

Browse files
committed
Import two python scripts from muen.git
Summary: Import and rename the following scripts from muen.git: - policy/xml/mirageos/compose.py : solo5-policy-compose.py - components/linux/misc/linux-muen-gencspec.py : linux-gencspec.py The following command in the muen.git repository has been used for the import: $ git filter-repo \ --path policy/xml/mirageos/compose.py \ --path components/linux/misc/linux-muen-gencspec.py \ --path-rename policy/xml/mirageos/compose.py:scripts/solo5-policy-compose.py \ --path-rename components/linux/misc/linux-muen-gencspec.py:scripts/linux-gencspec.py Also rename solo5-muen-gencspec.py to solo5-gencspec.py, reformat linux-gencspec.py with black and remove _paths import from linux-gencspec.py (no longer needed). Reviewers: #muen, ken Reviewed By: #muen, ken Subscribers: ken Differential Revision: https://dev.codelabs.ch/D1272
2 parents 1722515 + 77a18a2 commit ddece09

File tree

3 files changed

+384
-0
lines changed

3 files changed

+384
-0
lines changed

scripts/linux-gencspec.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
from lxml import etree
5+
import os
6+
import sys
7+
8+
import muutils
9+
10+
DESCRIPTION = "Linux XML component spec generator"
11+
LINUX_VIRTUAL_ADDRESS = "16#0040_0000#"
12+
INITRAMFS_VIRTUAL_ADDRESS = 0x90000000
13+
14+
# See arch/x86/include/asm/boot.h
15+
BOOT_HEAP_SIZE = 0x10000
16+
BOOT_STACK_SIZE = 0x4000
17+
18+
19+
def add_provides_memory(
20+
xml_spec, name, region_type, address, filename, size, executable, writable
21+
):
22+
"""
23+
Add file-backed memory region with given name and size to <provides>
24+
section of XML spec. The size is rounded up to the next 4K.
25+
"""
26+
provides = xml_spec.xpath("/component/provides")[0]
27+
mem_size = size
28+
mem_size -= mem_size % -4096
29+
print(
30+
"* Adding '"
31+
+ name
32+
+ "' region with size "
33+
+ str(mem_size)
34+
+ " bytes, address "
35+
+ address
36+
+ ", type "
37+
+ region_type
38+
)
39+
mem = etree.Element(
40+
"memory",
41+
logical=name,
42+
virtualAddress=address,
43+
size=muutils.int_to_ada_hex(mem_size),
44+
executable=executable,
45+
writable=writable,
46+
type=region_type,
47+
)
48+
etree.SubElement(mem, "file", filename=filename, offset="none")
49+
provides.append(mem)
50+
51+
52+
def get_initramfs_attrs(xml_spec):
53+
"""
54+
Return virtual address of initramfs so its mapped directly adjacent to the
55+
existing initramfs. Also return whether the initramfs mapping is executable
56+
and writable.
57+
"""
58+
phys_regions = xml_spec.xpath("/system/memory/memory" + "[@type='subject_initrd']")
59+
if len(phys_regions) > 1:
60+
print("Warning: Muen system policy has multiple initramfs regions.")
61+
return None
62+
63+
if len(phys_regions) == 0:
64+
return muutils.int_to_ada_hex(INITRAMFS_VIRTUAL_ADDRESS), False, False
65+
66+
phys_name = phys_regions[0].attrib["name"].lower()
67+
mems = xml_spec.xpath(
68+
"/system/subjects/subject/memory/memory[@physical='" + phys_name + "']"
69+
)
70+
if len(mems) == 0:
71+
return muutils.int_to_ada_hex(INITRAMFS_VIRTUAL_ADDRESS), False, False
72+
73+
size = muutils.ada_hex_to_int(phys_regions[0].attrib["size"])
74+
virtual_address = mems[0].attrib["virtualAddress"]
75+
executable = mems[0].attrib["executable"]
76+
writable = mems[0].attrib["writable"]
77+
78+
"""
79+
Check that all existing initramfs mappings are located at the same virtual
80+
address. This is necessary to ensure consecutiveness for all Linux
81+
subjects, since the additional initramfs is added to the component XML spec
82+
and thus mapped at a specific virtual address in *all* Linux subjects.
83+
Also, only set executable/writable to true if all mappings allow it.
84+
"""
85+
for mapping in mems:
86+
if mapping.attrib["virtualAddress"] != virtual_address:
87+
print("Warning: Initramfs mappings not at same virtual address.")
88+
return None, False, False
89+
executable = executable and mapping.attrib["executable"]
90+
writable = writable and mapping.attrib["writable"]
91+
92+
new_address = muutils.ada_hex_to_int(virtual_address) + size
93+
return muutils.int_to_ada_hex(new_address), executable, writable
94+
95+
96+
def parse_args():
97+
"""
98+
Returned parsed command line arguments
99+
"""
100+
arg_parser = argparse.ArgumentParser(description=DESCRIPTION)
101+
arg_parser.add_argument("kernel_binary", type=str, help="Linux kernel binary")
102+
arg_parser.add_argument(
103+
"src_xml_spec", type=str, help="Muen component source XML specification"
104+
)
105+
arg_parser.add_argument(
106+
"--out_spec", type=str, help=("Filename of generated Muen component XML")
107+
)
108+
arg_parser.add_argument("--initramfs", type=str, help="Linux Modules Initramfs")
109+
arg_parser.add_argument("--src_policy", type=str, help=("Muen XML system policy"))
110+
111+
return arg_parser.parse_args()
112+
113+
114+
args = parse_args()
115+
src_bin_path = args.kernel_binary
116+
src_spec_path = args.src_xml_spec
117+
src_policy_path = args.src_policy
118+
src_initramfs_path = args.initramfs
119+
out_spec_path = args.out_spec
120+
121+
if not os.path.isfile(src_bin_path):
122+
sys.exit("Error: Linux kernel binary not found '" + src_bin_path + "'")
123+
124+
if not os.path.isfile(src_spec_path):
125+
sys.exit("Error: Source component XML specification not found")
126+
127+
if out_spec_path is None:
128+
sys.exit(("Error: Muen output component XML specification not specified"))
129+
130+
if src_initramfs_path is not None:
131+
if src_policy_path is None:
132+
sys.exit("Error: Muen source system policy XML not specified")
133+
134+
if not os.path.isfile(src_policy_path):
135+
sys.exit(
136+
"Error: Muen source system policy XML not found '" + src_policy_path + "'"
137+
)
138+
139+
out_spec_dir = os.path.dirname(out_spec_path)
140+
if len(out_spec_dir) > 0 and not os.path.isdir(out_spec_dir):
141+
sys.exit(
142+
(
143+
"Error: Output directory for component specification does not "
144+
+ "exist ('"
145+
+ os.path.dirname(out_spec_dir)
146+
+ "')"
147+
)
148+
)
149+
150+
print("Reading source component specification from '" + src_spec_path + "'")
151+
src_spec_name = os.path.basename(src_spec_path)
152+
xml_parser = etree.XMLParser(remove_blank_text=True)
153+
src_spec = etree.parse(src_spec_path, xml_parser).getroot()
154+
155+
print("Processing Linux binary '" + src_bin_path + "'")
156+
binary_name = os.path.basename(src_bin_path)
157+
binary_size = os.path.getsize(src_bin_path)
158+
binary_size += BOOT_HEAP_SIZE + BOOT_STACK_SIZE
159+
add_provides_memory(
160+
src_spec,
161+
"binary",
162+
"subject_binary",
163+
LINUX_VIRTUAL_ADDRESS,
164+
binary_name,
165+
binary_size,
166+
"true",
167+
"true",
168+
)
169+
170+
if src_initramfs_path is not None:
171+
print("Reading source system policy from '" + src_policy_path + "'")
172+
src_policy = etree.parse(src_policy_path, xml_parser).getroot()
173+
initramfs_addr, executable, writable = get_initramfs_attrs(src_policy)
174+
175+
if initramfs_addr is None:
176+
print("Warning: Manually add mappings for " + src_initramfs_path)
177+
else:
178+
print("Processing initramfs '" + src_initramfs_path + "'")
179+
initramfs_name = os.path.basename(src_initramfs_path)
180+
initramfs_size = os.path.getsize(src_initramfs_path)
181+
add_provides_memory(
182+
src_spec,
183+
"modules_initramfs",
184+
"subject_initrd",
185+
initramfs_addr,
186+
initramfs_name,
187+
initramfs_size,
188+
executable,
189+
writable,
190+
)
191+
192+
193+
with open(out_spec_path, "wb") as out_spec:
194+
print("Writing component specification to '" + out_spec_path + "'")
195+
out_spec.write(etree.tostring(src_spec, pretty_print=True))

scripts/solo5-policy-compose.py

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
5+
from copy import deepcopy
6+
from lxml import etree
7+
8+
import muutils
9+
10+
SUBJECT_NAME = "unikernel"
11+
LINUX_NAME = "nic_linux"
12+
LINUX_BASE_ADDR = 0xE03000000
13+
LINUX_BOOTPARAM = "unikernel_iface"
14+
RESET_VAR_NAME = "resettable"
15+
16+
17+
def add_physical_channels(doc, comp_channels, subject_name):
18+
"""
19+
Add physical channels for given component channels using specified subject
20+
name to construct the physical name.
21+
"""
22+
phys_channels = doc.xpath("/system/channels")[0]
23+
for comp_chan in comp_channels:
24+
name = comp_chan.get("logical")
25+
size = comp_chan.get("size")
26+
phys_name = subject_name + "_" + name.replace("|", "_")
27+
print("* Adding physical channel '" + phys_name + "' with size " + size)
28+
etree.SubElement(phys_channels, "channel", name=phys_name, size=size)
29+
30+
31+
def add_subject_channel_mappings(doc, comp_channels, subject_name):
32+
"""
33+
Add component channel mappings for given component channels to subject with
34+
specified name.
35+
"""
36+
subj_comp = doc.xpath(
37+
"/system/subjects/subject[@name='" + subject_name + "']/component"
38+
)[0]
39+
for comp_chan in comp_channels:
40+
log_name = comp_chan.get("logical")
41+
phys_name = subject_name + "_" + log_name.replace("|", "_")
42+
print("* Adding subject channel mapping for '" + log_name + "'")
43+
etree.SubElement(subj_comp, "map", logical=log_name, physical=phys_name)
44+
45+
46+
def add_linux_channels(doc, comp_channels, subject_name):
47+
"""
48+
Add logical channels for given component channels to Linux subject.
49+
"""
50+
subj_channels = doc.xpath(
51+
"/system/subjects/subject[@name='" + LINUX_NAME + "']/channels"
52+
)[0]
53+
address = LINUX_BASE_ADDR
54+
for comp_chan in comp_channels:
55+
# Flip channel role
56+
chan_type = "reader" if comp_chan.tag == "writer" else "writer"
57+
log_name = comp_chan.get("logical")
58+
size = muutils.ada_hex_to_int(comp_chan.get("size"))
59+
phys_name = subject_name + "_" + log_name.replace("|", "_")
60+
addr_str = muutils.int_to_ada_hex(address)
61+
print(
62+
"* Adding NIC Linux channel "
63+
+ chan_type
64+
+ " '"
65+
+ phys_name
66+
+ "' @ "
67+
+ addr_str
68+
)
69+
etree.SubElement(
70+
subj_channels,
71+
chan_type,
72+
logical=log_name,
73+
physical=phys_name,
74+
virtualAddress=addr_str,
75+
)
76+
address += size
77+
78+
79+
def copy_reset_variable(doc, subject_name):
80+
"""
81+
Copy component-local "resettable" variable to system config section.
82+
"""
83+
comp_rst = doc.xpath(
84+
"/system/components/component[@name='"
85+
+ subject_name
86+
+ "']/config/boolean[@name='"
87+
+ RESET_VAR_NAME
88+
+ "']"
89+
)[0]
90+
sys_config = doc.xpath("/system/config")[0]
91+
etree.SubElement(
92+
sys_config,
93+
"boolean",
94+
name=subject_name + "_" + RESET_VAR_NAME,
95+
value=comp_rst.get("value"),
96+
)
97+
98+
99+
def extend_subject_bootparams(doc, subject_name, bootparams):
100+
"""
101+
Append given boot parameters to bootparams of subject with specified name.
102+
"""
103+
print(
104+
"* Extending boot parameters of subject '"
105+
+ subject_name
106+
+ "' with '"
107+
+ bootparams
108+
+ "'"
109+
)
110+
subj_params = doc.xpath(
111+
"/system/subjects/subject[@name='" + subject_name + "']/bootparams"
112+
)[0]
113+
114+
if subj_params.text is not None and len(subj_params.text) > 0:
115+
params = subj_params.text + " " + bootparams
116+
else:
117+
params = bootparams
118+
119+
subj_params.text = params
120+
121+
122+
def set_subject_bootparams(doc, comp_spec, subject_name):
123+
"""
124+
Set subject boot parameters to those of the component config variable named
125+
'bootparams'. This can be dropped once component specifications can
126+
directly set bootparams.
127+
"""
128+
comp_params = comp_spec.xpath("/component/config/string" + "[@name='bootparams']")
129+
if len(comp_params) > 0:
130+
extend_subject_bootparams(doc, subject_name, comp_params[0].get("value"))
131+
132+
133+
def add_linux_bootparams(doc, comp_channels):
134+
"""
135+
Add 'unikernel_iface' bootparameter to Linux subject.
136+
"""
137+
ifaces = ""
138+
for comp_chan in comp_channels:
139+
if comp_chan.tag == "reader":
140+
log_name = comp_chan.get("logical").rstrip("|in")
141+
if len(ifaces) > 0:
142+
ifaces += ","
143+
ifaces += log_name
144+
145+
extend_subject_bootparams(doc, LINUX_NAME, LINUX_BOOTPARAM + "=" + ifaces)
146+
147+
148+
def add_component(doc, comp_doc):
149+
"""
150+
Add copy of given component specification to components section of
151+
specified system policy document.
152+
"""
153+
components = doc.xpath("/system/components")[0]
154+
print("* Adding component '" + comp_doc.get("name") + "'")
155+
components.append(deepcopy(comp_doc))
156+
157+
158+
def parse_args():
159+
"""
160+
Returned parsed command line arguments
161+
"""
162+
arg_parser = argparse.ArgumentParser(description="MirageOS/Solo5 system composer")
163+
arg_parser.add_argument(
164+
"policy_path", type=str, help="Path of to be created system policy"
165+
)
166+
arg_parser.add_argument(
167+
"--template", type=str, help=("System policy template path")
168+
)
169+
arg_parser.add_argument("--unikernel-spec", type=str, help=("Unikernel spec path"))
170+
return arg_parser.parse_args()
171+
172+
173+
args = parse_args()
174+
parser = etree.XMLParser(remove_blank_text=True)
175+
doc = etree.parse(args.template, parser).getroot()
176+
cspec_doc = etree.parse(args.unikernel_spec, parser).getroot()
177+
comp_channels = cspec_doc.xpath("/component/requires/channels/*")
178+
179+
add_component(doc, cspec_doc)
180+
add_physical_channels(doc, comp_channels, SUBJECT_NAME)
181+
add_subject_channel_mappings(doc, comp_channels, SUBJECT_NAME)
182+
add_linux_channels(doc, comp_channels, SUBJECT_NAME)
183+
add_linux_bootparams(doc, comp_channels)
184+
set_subject_bootparams(doc, cspec_doc, SUBJECT_NAME)
185+
copy_reset_variable(doc, SUBJECT_NAME)
186+
187+
with open(args.policy_path, "wb") as f:
188+
print("Writing system policy to '" + args.policy_path + "'")
189+
f.write(etree.tostring(doc, pretty_print=True))

0 commit comments

Comments
 (0)