]> rtime.felk.cvut.cz Git - jailhouse.git/blob - tools/jailhouse-cell-linux
tools: Rewrap jailhouse help output
[jailhouse.git] / tools / jailhouse-cell-linux
1 #!/usr/bin/env python
2
3 # Jailhouse, a Linux-based partitioning hypervisor
4 #
5 # Copyright (c) Siemens AG, 2015
6 #
7 # Authors:
8 #  Jan Kiszka <jan.kiszka@siemens.com>
9 #
10 # This work is licensed under the terms of the GNU GPL, version 2.  See
11 # the COPYING file in the top-level directory.
12
13 from __future__ import print_function
14 import argparse
15 import ctypes
16 import errno
17 import fcntl
18 import os
19 import struct
20 import sys
21
22 PARAMS_BASE = 0xf5000
23
24 libexecdir = None
25
26
27 class MemoryRegion:
28     JAILHOUSE_MEM_READ = 0x0001
29     JAILHOUSE_MEM_WRITE = 0x0002
30     JAILHOUSE_MEM_EXECUTE = 0x0004
31     JAILHOUSE_MEM_DMA = 0x0008
32     JAILHOUSE_MEM_IO = 0x0010
33     JAILHOUSE_MEM_COMM_REGION = 0x0020
34     JAILHOUSE_MEM_ROOTSHARED = 0x0080
35
36     E820_RAM = 1
37     E820_RESERVED = 2
38
39     _REGION_FORMAT = 'QQQQ'
40     SIZE = struct.calcsize(_REGION_FORMAT)
41
42     def __init__(self, region_struct):
43         (self.phys_start,
44          self.virt_start,
45          self.size,
46          self.flags) = \
47             struct.unpack_from(MemoryRegion._REGION_FORMAT, region_struct)
48
49     def is_ram(self):
50         return ((self.flags & (MemoryRegion.JAILHOUSE_MEM_READ |
51                                MemoryRegion.JAILHOUSE_MEM_WRITE |
52                                MemoryRegion.JAILHOUSE_MEM_EXECUTE |
53                                MemoryRegion.JAILHOUSE_MEM_DMA |
54                                MemoryRegion.JAILHOUSE_MEM_IO |
55                                MemoryRegion.JAILHOUSE_MEM_COMM_REGION |
56                                MemoryRegion.JAILHOUSE_MEM_ROOTSHARED)) ==
57                 (MemoryRegion.JAILHOUSE_MEM_READ |
58                  MemoryRegion.JAILHOUSE_MEM_WRITE |
59                  MemoryRegion.JAILHOUSE_MEM_EXECUTE |
60                  MemoryRegion.JAILHOUSE_MEM_DMA))
61
62     def is_comm_region(self):
63         return (self.flags & MemoryRegion.JAILHOUSE_MEM_COMM_REGION) != 0
64
65     def as_e820(self):
66         return struct.pack('QQI', self.virt_start, self.size,
67                            MemoryRegion.E820_RAM if self.is_ram() else
68                            MemoryRegion.E820_RESERVED)
69
70
71 class Config:
72     _HEADER_FORMAT = '8x32sIIIIIII'
73
74     def __init__(self, config_file):
75         self.data = config_file.read()
76
77         (name,
78          self.flags,
79          self.cpu_set_size,
80          self.num_memory_regions,
81          self.num_irqchips,
82          self.pio_bitmap_size,
83          self.num_pci_devices,
84          self.num_pci_caps) = \
85             struct.unpack_from(Config._HEADER_FORMAT, self.data)
86         self.name = str(name.decode())
87
88         memregion_offs = struct.calcsize(Config._HEADER_FORMAT) + \
89             self.cpu_set_size
90         self.memory_regions = []
91         for n in range(self.num_memory_regions):
92             self.memory_regions.append(
93                 MemoryRegion(self.data[memregion_offs:]))
94             memregion_offs += MemoryRegion.SIZE
95
96
97 class SetupHeader:
98     _HEADER_FORMAT = 'xB2xI8xH14xB7xII8xI4xI28xQ'
99
100     def __init__(self, kernel):
101         kernel.seek(0x1f0)
102         parse_size = struct.calcsize(SetupHeader._HEADER_FORMAT)
103         (self.setup_sects,
104          self.syssize,
105          self.jump,
106          self.type_of_loader,
107          self.ramdisk_image,
108          self.ramdisk_size,
109          self.cmd_line_ptr,
110          self.kernel_alignment,
111          self.setup_data) = \
112             struct.unpack(SetupHeader._HEADER_FORMAT, kernel.read(parse_size))
113
114         self.size = 0x202 + (self.jump >> 8) - 0x1f0
115         kernel.seek(0x1f0)
116         self.data = bytearray(kernel.read(self.size))
117
118     def get_data(self):
119         struct.pack_into(SetupHeader._HEADER_FORMAT, self.data, 0,
120                          self.setup_sects, self.syssize, self.jump,
121                          self.type_of_loader, self.ramdisk_image,
122                          self.ramdisk_size, self.cmd_line_ptr,
123                          self.kernel_alignment, self.setup_data)
124         return self.data
125
126
127 class ZeroPage:
128     def __init__(self, kernel, initrd, config):
129         self.setup_header = SetupHeader(kernel)
130
131         prot_image_offs = (self.setup_header.setup_sects + 1) * 512
132         prot_image_size = self.setup_header.syssize * 16
133
134         self.kernel_load_addr = self.setup_header.kernel_alignment - \
135             prot_image_offs
136
137         self.setup_header.type_of_loader = 0xff
138
139         if initrd:
140             kernel_size = os.fstat(kernel.fileno()).st_size
141             self.setup_header.ramdisk_size = os.fstat(initrd.fileno()).st_size
142             self.setup_header.ramdisk_image = \
143                 (self.kernel_load_addr - self.setup_header.ramdisk_size) & \
144                 ~0xfff
145         else:
146             self.setup_header.ramdisk_image = 0
147             self.setup_header.ramdisk_size = 0
148
149         self.e820_entries = []
150         for region in config.memory_regions:
151             if region.is_ram() or region.is_comm_region():
152                 if len(self.e820_entries) >= 128:
153                     print("Too many memory regions", file=sys.stderr)
154                     exit(1)
155                 self.e820_entries.append(region)
156
157     def get_data(self):
158         data = bytearray(0x1e8) + \
159             struct.pack('B', len(self.e820_entries)) + \
160             bytearray(0x1f0 - 0x1e9) + self.setup_header.get_data() + \
161             bytearray(0x2d0 - 0x1f0 - self.setup_header.size)
162         for region in self.e820_entries:
163             data += region.as_e820()
164         return data + bytearray(0x1000 - len(data))
165
166
167 class JailhouseCell:
168     JAILHOUSE_CELL_CREATE = 0x40100002
169     JAILHOUSE_CELL_LOAD = 0x40300003
170     JAILHOUSE_CELL_START = 0x40280004
171
172     JAILHOUSE_CELL_ID_UNUSED = -1
173
174     def __init__(self, config):
175         self.name = config.name.encode('utf-8')
176
177         self.dev = open('/dev/jailhouse')
178
179         cbuf = ctypes.c_buffer(config.data)
180         create = struct.pack('QI4x', ctypes.addressof(cbuf), len(config.data))
181         try:
182             fcntl.ioctl(self.dev, JailhouseCell.JAILHOUSE_CELL_CREATE, create)
183         except IOError as e:
184             if e.errno != errno.EEXIST:
185                 raise e
186
187     def load(self, image, address):
188         cbuf = ctypes.create_string_buffer(bytes(image))
189
190         load = struct.pack('i4x32sI4xQQQ8x',
191                            JailhouseCell.JAILHOUSE_CELL_ID_UNUSED, self.name,
192                            1, ctypes.addressof(cbuf), len(image), address)
193         fcntl.ioctl(self.dev, self.JAILHOUSE_CELL_LOAD, load)
194
195     def start(self):
196         start = struct.pack('i4x32s', JailhouseCell.JAILHOUSE_CELL_ID_UNUSED,
197                             self.name)
198         fcntl.ioctl(self.dev, JailhouseCell.JAILHOUSE_CELL_START, start)
199
200
201 def gen_setup_data():
202     MAX_CPUS = 255
203     return struct.pack('8x4sI4x', b'JLHS', 4 + MAX_CPUS) + bytearray(MAX_CPUS)
204
205
206 # pretend to be part of the jailhouse tool
207 sys.argv[0] = sys.argv[0].replace('-', ' ')
208
209 parser = argparse.ArgumentParser(description='Boot Linux in a non-root cell.')
210 parser.add_argument('config', metavar='CELLCONFIG',
211                     type=argparse.FileType('rb'),
212                     help='cell configuration file')
213 parser.add_argument('kernel', metavar='KERNEL', type=argparse.FileType('rb'),
214                     help='image of the kernel to be booted')
215 parser.add_argument('--initrd', '-i', metavar='FILE',
216                     type=argparse.FileType('rb'),
217                     help='initrd/initramfs for the kernel')
218 parser.add_argument('--cmdline', '-c', metavar='"STRING"',
219                     help='kernel command line')
220 parser.add_argument('--write-params', '-w', metavar='FILE',
221                     type=argparse.FileType('wb'),
222                     help='only parse cell configuration, write out '
223                          'parameters into the specified file and print '
224                          'required jailhouse cell commands to boot Linux '
225                          'to the console')
226
227 try:
228     args = parser.parse_args()
229 except IOError as e:
230     print(e.strerror, file=sys.stderr)
231     exit(1)
232
233 config = Config(args.config)
234
235 zero_page = ZeroPage(args.kernel, args.initrd, config)
236
237 setup_data = gen_setup_data()
238
239 zero_page.setup_header.setup_data = PARAMS_BASE + 0x1000
240 zero_page.setup_header.cmd_line_ptr = \
241     zero_page.setup_header.setup_data + len(setup_data)
242
243 params = zero_page.get_data() + setup_data + \
244     (args.cmdline.encode() if args.cmdline else b'') + b'\0'
245
246 if args.write_params:
247     args.write_params.write(params)
248     args.write_params.close()
249
250     print("\
251 Boot parameters written. Start Linux with the following commands (adjusting \
252 paths as needed):\n\
253 \n\
254 jailhouse cell create %s\n\
255 jailhouse cell load %s linux-loader.bin -a 0xf0000 %s -a 0x%x " %
256           (args.config.name, config.name, args.kernel.name,
257            zero_page.kernel_load_addr),
258           end="")
259     if args.initrd:
260         print("%s -a 0x%x " %
261               (args.initrd.name, zero_page.setup_header.ramdisk_image),
262               end="")
263     print("%s -a 0x%x" % (args.write_params.name, PARAMS_BASE))
264     print("jailhouse cell start %s" % config.name)
265 else:
266     arch_str = os.uname()[4]
267     if arch_str in ('i686', 'x86_64'):
268         srcarch = 'x86'
269     else:
270         print("Unsupported architecture", file=sys.stderr)
271         exit(1)
272
273     if libexecdir:
274         linux_loader = libexecdir + '/jailhouse/linux-loader.bin'
275     else:
276         linux_loader = os.path.abspath(os.path.dirname(sys.argv[0])) + \
277             '/../inmates/tools/' + srcarch + '/linux-loader.bin'
278
279     cell = JailhouseCell(config)
280     cell.load(open(linux_loader, mode='rb').read(), 0xf0000)
281     args.kernel.seek(0)
282     cell.load(args.kernel.read(), zero_page.kernel_load_addr)
283     if args.initrd:
284         cell.load(args.initrd.read(), zero_page.setup_header.ramdisk_image)
285     cell.load(params, PARAMS_BASE)
286     cell.start()