]> rtime.felk.cvut.cz Git - jailhouse.git/blob - tools/jailhouse-cell-linux
arm: Fold irqchip_root_cell_shrink into irqchip_cell_init
[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 = '8x32sIIIIIIII'
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_cache_regions,
82          self.num_irqchips,
83          self.pio_bitmap_size,
84          self.num_pci_devices,
85          self.num_pci_caps) = \
86             struct.unpack_from(Config._HEADER_FORMAT, self.data)
87         self.name = str(name.decode())
88
89         memregion_offs = struct.calcsize(Config._HEADER_FORMAT) + \
90             self.cpu_set_size
91         self.memory_regions = []
92         for n in range(self.num_memory_regions):
93             self.memory_regions.append(
94                 MemoryRegion(self.data[memregion_offs:]))
95             memregion_offs += MemoryRegion.SIZE
96
97
98 class SetupHeader:
99     _HEADER_FORMAT = 'xB2xI8xH14xB7xII8xI4xI28xQ'
100
101     def __init__(self, kernel):
102         kernel.seek(0x1f0)
103         parse_size = struct.calcsize(SetupHeader._HEADER_FORMAT)
104         (self.setup_sects,
105          self.syssize,
106          self.jump,
107          self.type_of_loader,
108          self.ramdisk_image,
109          self.ramdisk_size,
110          self.cmd_line_ptr,
111          self.kernel_alignment,
112          self.setup_data) = \
113             struct.unpack(SetupHeader._HEADER_FORMAT, kernel.read(parse_size))
114
115         self.size = 0x202 + (self.jump >> 8) - 0x1f0
116         kernel.seek(0x1f0)
117         self.data = bytearray(kernel.read(self.size))
118
119     def get_data(self):
120         struct.pack_into(SetupHeader._HEADER_FORMAT, self.data, 0,
121                          self.setup_sects, self.syssize, self.jump,
122                          self.type_of_loader, self.ramdisk_image,
123                          self.ramdisk_size, self.cmd_line_ptr,
124                          self.kernel_alignment, self.setup_data)
125         return self.data
126
127
128 class ZeroPage:
129     def __init__(self, kernel, initrd, config):
130         self.setup_header = SetupHeader(kernel)
131
132         prot_image_offs = (self.setup_header.setup_sects + 1) * 512
133         prot_image_size = self.setup_header.syssize * 16
134
135         self.kernel_load_addr = self.setup_header.kernel_alignment - \
136             prot_image_offs
137
138         self.setup_header.type_of_loader = 0xff
139
140         if initrd:
141             kernel_size = os.fstat(kernel.fileno()).st_size
142             self.setup_header.ramdisk_size = os.fstat(initrd.fileno()).st_size
143             self.setup_header.ramdisk_image = \
144                 (self.kernel_load_addr - self.setup_header.ramdisk_size) & \
145                 ~0xfff
146         else:
147             self.setup_header.ramdisk_image = 0
148             self.setup_header.ramdisk_size = 0
149
150         self.e820_entries = []
151         for region in config.memory_regions:
152             if region.is_ram() or region.is_comm_region():
153                 if len(self.e820_entries) >= 128:
154                     print("Too many memory regions", file=sys.stderr)
155                     exit(1)
156                 self.e820_entries.append(region)
157
158     def get_data(self):
159         data = bytearray(0x1e8) + \
160             struct.pack('B', len(self.e820_entries)) + \
161             bytearray(0x1f0 - 0x1e9) + self.setup_header.get_data() + \
162             bytearray(0x2d0 - 0x1f0 - self.setup_header.size)
163         for region in self.e820_entries:
164             data += region.as_e820()
165         return data + bytearray(0x1000 - len(data))
166
167
168 class JailhouseCell:
169     JAILHOUSE_CELL_CREATE = 0x40100002
170     JAILHOUSE_CELL_LOAD = 0x40300003
171     JAILHOUSE_CELL_START = 0x40280004
172
173     JAILHOUSE_CELL_ID_UNUSED = -1
174
175     def __init__(self, config):
176         self.name = config.name.encode('utf-8')
177
178         self.dev = open('/dev/jailhouse')
179
180         cbuf = ctypes.c_buffer(config.data)
181         create = struct.pack('QI4x', ctypes.addressof(cbuf), len(config.data))
182         try:
183             fcntl.ioctl(self.dev, JailhouseCell.JAILHOUSE_CELL_CREATE, create)
184         except IOError as e:
185             if e.errno != errno.EEXIST:
186                 raise e
187
188     def load(self, image, address):
189         cbuf = ctypes.create_string_buffer(bytes(image))
190
191         load = struct.pack('i4x32sI4xQQQ8x',
192                            JailhouseCell.JAILHOUSE_CELL_ID_UNUSED, self.name,
193                            1, ctypes.addressof(cbuf), len(image), address)
194         fcntl.ioctl(self.dev, self.JAILHOUSE_CELL_LOAD, load)
195
196     def start(self):
197         start = struct.pack('i4x32s', JailhouseCell.JAILHOUSE_CELL_ID_UNUSED,
198                             self.name)
199         fcntl.ioctl(self.dev, JailhouseCell.JAILHOUSE_CELL_START, start)
200
201
202 def gen_setup_data():
203     MAX_CPUS = 255
204     return struct.pack('8x4sI4x', b'JLHS', 4 + MAX_CPUS) + bytearray(MAX_CPUS)
205
206
207 # pretend to be part of the jailhouse tool
208 sys.argv[0] = sys.argv[0].replace('-', ' ')
209
210 parser = argparse.ArgumentParser(description='Boot Linux in a non-root cell.')
211 parser.add_argument('config', metavar='CELLCONFIG',
212                     type=argparse.FileType('rb'),
213                     help='cell configuration file')
214 parser.add_argument('kernel', metavar='KERNEL', type=argparse.FileType('rb'),
215                     help='image of the kernel to be booted')
216 parser.add_argument('--initrd', '-i', metavar='FILE',
217                     type=argparse.FileType('rb'),
218                     help='initrd/initramfs for the kernel')
219 parser.add_argument('--cmdline', '-c', metavar='"STRING"',
220                     help='kernel command line')
221 parser.add_argument('--write-params', '-w', metavar='FILE',
222                     type=argparse.FileType('wb'),
223                     help='only parse cell configuration, write out '
224                          'parameters into the specified file and print '
225                          'required jailhouse cell commands to boot Linux '
226                          'to the console')
227
228 try:
229     args = parser.parse_args()
230 except IOError as e:
231     print(e.strerror, file=sys.stderr)
232     exit(1)
233
234 config = Config(args.config)
235
236 zero_page = ZeroPage(args.kernel, args.initrd, config)
237
238 setup_data = gen_setup_data()
239
240 zero_page.setup_header.setup_data = PARAMS_BASE + 0x1000
241 zero_page.setup_header.cmd_line_ptr = \
242     zero_page.setup_header.setup_data + len(setup_data)
243
244 params = zero_page.get_data() + setup_data + \
245     (args.cmdline.encode() if args.cmdline else b'') + b'\0'
246
247 if args.write_params:
248     args.write_params.write(params)
249     args.write_params.close()
250
251     print("\
252 Boot parameters written. Start Linux with the following commands (adjusting \
253 paths as needed):\n\
254 \n\
255 jailhouse cell create %s\n\
256 jailhouse cell load %s linux-loader.bin -a 0xf0000 %s -a 0x%x " %
257           (args.config.name, config.name, args.kernel.name,
258            zero_page.kernel_load_addr),
259           end="")
260     if args.initrd:
261         print("%s -a 0x%x " %
262               (args.initrd.name, zero_page.setup_header.ramdisk_image),
263               end="")
264     print("%s -a 0x%x" % (args.write_params.name, PARAMS_BASE))
265     print("jailhouse cell start %s" % config.name)
266 else:
267     arch_str = os.uname()[4]
268     if arch_str in ('i686', 'x86_64'):
269         srcarch = 'x86'
270     else:
271         print("Unsupported architecture", file=sys.stderr)
272         exit(1)
273
274     if libexecdir:
275         linux_loader = libexecdir + '/jailhouse/linux-loader.bin'
276     else:
277         linux_loader = os.path.abspath(os.path.dirname(sys.argv[0])) + \
278             '/../inmates/tools/' + srcarch + '/linux-loader.bin'
279
280     cell = JailhouseCell(config)
281     cell.load(open(linux_loader, mode='rb').read(), 0xf0000)
282     args.kernel.seek(0)
283     cell.load(args.kernel.read(), zero_page.kernel_load_addr)
284     if args.initrd:
285         cell.load(args.initrd.read(), zero_page.setup_header.ramdisk_image)
286     cell.load(params, PARAMS_BASE)
287     cell.start()