xref: /openbmc/qemu/tests/qemu-iotests/qed.py (revision 73d336510cf118fcc2ee7e98e774a193cf661614)
1*903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3
2e77964f7SStefan Hajnoczi#
3e77964f7SStefan Hajnoczi# Tool to manipulate QED image files
4e77964f7SStefan Hajnoczi#
5e77964f7SStefan Hajnoczi# Copyright (C) 2010 IBM, Corp.
6e77964f7SStefan Hajnoczi#
7e77964f7SStefan Hajnoczi# Authors:
8e77964f7SStefan Hajnoczi#  Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
9e77964f7SStefan Hajnoczi#
10e77964f7SStefan Hajnoczi# This work is licensed under the terms of the GNU GPL, version 2 or later.
11e77964f7SStefan Hajnoczi# See the COPYING file in the top-level directory.
12e77964f7SStefan Hajnoczi
13e77964f7SStefan Hajnocziimport sys
14e77964f7SStefan Hajnocziimport struct
15e77964f7SStefan Hajnocziimport random
16e77964f7SStefan Hajnocziimport optparse
17e77964f7SStefan Hajnoczi
18e77964f7SStefan Hajnoczi# This can be used as a module
19e77964f7SStefan Hajnoczi__all__ = ['QED_F_NEED_CHECK', 'QED']
20e77964f7SStefan Hajnoczi
21e77964f7SStefan HajnocziQED_F_NEED_CHECK = 0x02
22e77964f7SStefan Hajnoczi
23e77964f7SStefan Hajnocziheader_fmt = '<IIIIQQQQQII'
24e77964f7SStefan Hajnocziheader_size = struct.calcsize(header_fmt)
25e77964f7SStefan Hajnoczifield_names = ['magic', 'cluster_size', 'table_size',
26e77964f7SStefan Hajnoczi               'header_size', 'features', 'compat_features',
27e77964f7SStefan Hajnoczi               'autoclear_features', 'l1_table_offset', 'image_size',
28e77964f7SStefan Hajnoczi               'backing_filename_offset', 'backing_filename_size']
29e77964f7SStefan Hajnoczitable_elem_fmt = '<Q'
30e77964f7SStefan Hajnoczitable_elem_size = struct.calcsize(table_elem_fmt)
31e77964f7SStefan Hajnoczi
32e77964f7SStefan Hajnoczidef err(msg):
33e77964f7SStefan Hajnoczi    sys.stderr.write(msg + '\n')
34e77964f7SStefan Hajnoczi    sys.exit(1)
35e77964f7SStefan Hajnoczi
36e77964f7SStefan Hajnoczidef unpack_header(s):
37e77964f7SStefan Hajnoczi    fields = struct.unpack(header_fmt, s)
38e77964f7SStefan Hajnoczi    return dict((field_names[idx], val) for idx, val in enumerate(fields))
39e77964f7SStefan Hajnoczi
40e77964f7SStefan Hajnoczidef pack_header(header):
41e77964f7SStefan Hajnoczi    fields = tuple(header[x] for x in field_names)
42e77964f7SStefan Hajnoczi    return struct.pack(header_fmt, *fields)
43e77964f7SStefan Hajnoczi
44e77964f7SStefan Hajnoczidef unpack_table_elem(s):
45e77964f7SStefan Hajnoczi    return struct.unpack(table_elem_fmt, s)[0]
46e77964f7SStefan Hajnoczi
47e77964f7SStefan Hajnoczidef pack_table_elem(elem):
48e77964f7SStefan Hajnoczi    return struct.pack(table_elem_fmt, elem)
49e77964f7SStefan Hajnoczi
50e77964f7SStefan Hajnocziclass QED(object):
51e77964f7SStefan Hajnoczi    def __init__(self, f):
52e77964f7SStefan Hajnoczi        self.f = f
53e77964f7SStefan Hajnoczi
54e77964f7SStefan Hajnoczi        self.f.seek(0, 2)
55e77964f7SStefan Hajnoczi        self.filesize = f.tell()
56e77964f7SStefan Hajnoczi
57e77964f7SStefan Hajnoczi        self.load_header()
58e77964f7SStefan Hajnoczi        self.load_l1_table()
59e77964f7SStefan Hajnoczi
60e77964f7SStefan Hajnoczi    def raw_pread(self, offset, size):
61e77964f7SStefan Hajnoczi        self.f.seek(offset)
62e77964f7SStefan Hajnoczi        return self.f.read(size)
63e77964f7SStefan Hajnoczi
64e77964f7SStefan Hajnoczi    def raw_pwrite(self, offset, data):
65e77964f7SStefan Hajnoczi        self.f.seek(offset)
66e77964f7SStefan Hajnoczi        return self.f.write(data)
67e77964f7SStefan Hajnoczi
68e77964f7SStefan Hajnoczi    def load_header(self):
69e77964f7SStefan Hajnoczi        self.header = unpack_header(self.raw_pread(0, header_size))
70e77964f7SStefan Hajnoczi
71e77964f7SStefan Hajnoczi    def store_header(self):
72e77964f7SStefan Hajnoczi        self.raw_pwrite(0, pack_header(self.header))
73e77964f7SStefan Hajnoczi
74e77964f7SStefan Hajnoczi    def read_table(self, offset):
75e77964f7SStefan Hajnoczi        size = self.header['table_size'] * self.header['cluster_size']
76e77964f7SStefan Hajnoczi        s = self.raw_pread(offset, size)
77e77964f7SStefan Hajnoczi        table = [unpack_table_elem(s[i:i + table_elem_size]) for i in xrange(0, size, table_elem_size)]
78e77964f7SStefan Hajnoczi        return table
79e77964f7SStefan Hajnoczi
80e77964f7SStefan Hajnoczi    def load_l1_table(self):
81e77964f7SStefan Hajnoczi        self.l1_table = self.read_table(self.header['l1_table_offset'])
829a3a9a63SMax Reitz        self.table_nelems = self.header['table_size'] * self.header['cluster_size'] // table_elem_size
83e77964f7SStefan Hajnoczi
84e77964f7SStefan Hajnoczi    def write_table(self, offset, table):
85e77964f7SStefan Hajnoczi        s = ''.join(pack_table_elem(x) for x in table)
86e77964f7SStefan Hajnoczi        self.raw_pwrite(offset, s)
87e77964f7SStefan Hajnoczi
88e77964f7SStefan Hajnoczidef random_table_item(table):
89e77964f7SStefan Hajnoczi    vals = [(index, offset) for index, offset in enumerate(table) if offset != 0]
90e77964f7SStefan Hajnoczi    if not vals:
91e77964f7SStefan Hajnoczi        err('cannot pick random item because table is empty')
92e77964f7SStefan Hajnoczi    return random.choice(vals)
93e77964f7SStefan Hajnoczi
94e77964f7SStefan Hajnoczidef corrupt_table_duplicate(table):
95e77964f7SStefan Hajnoczi    '''Corrupt a table by introducing a duplicate offset'''
96e77964f7SStefan Hajnoczi    victim_idx, victim_val = random_table_item(table)
97e77964f7SStefan Hajnoczi    unique_vals = set(table)
98e77964f7SStefan Hajnoczi    if len(unique_vals) == 1:
99e77964f7SStefan Hajnoczi        err('no duplication corruption possible in table')
100e77964f7SStefan Hajnoczi    dup_val = random.choice(list(unique_vals.difference([victim_val])))
101e77964f7SStefan Hajnoczi    table[victim_idx] = dup_val
102e77964f7SStefan Hajnoczi
103e77964f7SStefan Hajnoczidef corrupt_table_invalidate(qed, table):
104e77964f7SStefan Hajnoczi    '''Corrupt a table by introducing an invalid offset'''
105e77964f7SStefan Hajnoczi    index, _ = random_table_item(table)
106e77964f7SStefan Hajnoczi    table[index] = qed.filesize + random.randint(0, 100 * 1024 * 1024 * 1024 * 1024)
107e77964f7SStefan Hajnoczi
108e77964f7SStefan Hajnoczidef cmd_show(qed, *args):
109e77964f7SStefan Hajnoczi    '''show [header|l1|l2 <offset>]- Show header or l1/l2 tables'''
110e77964f7SStefan Hajnoczi    if not args or args[0] == 'header':
111f03868bdSEduardo Habkost        print(qed.header)
112e77964f7SStefan Hajnoczi    elif args[0] == 'l1':
113f03868bdSEduardo Habkost        print(qed.l1_table)
114e77964f7SStefan Hajnoczi    elif len(args) == 2 and args[0] == 'l2':
115e77964f7SStefan Hajnoczi        offset = int(args[1])
116f03868bdSEduardo Habkost        print(qed.read_table(offset))
117e77964f7SStefan Hajnoczi    else:
118e77964f7SStefan Hajnoczi        err('unrecognized sub-command')
119e77964f7SStefan Hajnoczi
120e77964f7SStefan Hajnoczidef cmd_duplicate(qed, table_level):
121e77964f7SStefan Hajnoczi    '''duplicate l1|l2 - Duplicate a random table element'''
122e77964f7SStefan Hajnoczi    if table_level == 'l1':
123e77964f7SStefan Hajnoczi        offset = qed.header['l1_table_offset']
124e77964f7SStefan Hajnoczi        table = qed.l1_table
125e77964f7SStefan Hajnoczi    elif table_level == 'l2':
126e77964f7SStefan Hajnoczi        _, offset = random_table_item(qed.l1_table)
127e77964f7SStefan Hajnoczi        table = qed.read_table(offset)
128e77964f7SStefan Hajnoczi    else:
129e77964f7SStefan Hajnoczi        err('unrecognized sub-command')
130e77964f7SStefan Hajnoczi    corrupt_table_duplicate(table)
131e77964f7SStefan Hajnoczi    qed.write_table(offset, table)
132e77964f7SStefan Hajnoczi
133e77964f7SStefan Hajnoczidef cmd_invalidate(qed, table_level):
134e77964f7SStefan Hajnoczi    '''invalidate l1|l2 - Plant an invalid table element at random'''
135e77964f7SStefan Hajnoczi    if table_level == 'l1':
136e77964f7SStefan Hajnoczi        offset = qed.header['l1_table_offset']
137e77964f7SStefan Hajnoczi        table = qed.l1_table
138e77964f7SStefan Hajnoczi    elif table_level == 'l2':
139e77964f7SStefan Hajnoczi        _, offset = random_table_item(qed.l1_table)
140e77964f7SStefan Hajnoczi        table = qed.read_table(offset)
141e77964f7SStefan Hajnoczi    else:
142e77964f7SStefan Hajnoczi        err('unrecognized sub-command')
143e77964f7SStefan Hajnoczi    corrupt_table_invalidate(qed, table)
144e77964f7SStefan Hajnoczi    qed.write_table(offset, table)
145e77964f7SStefan Hajnoczi
146e77964f7SStefan Hajnoczidef cmd_need_check(qed, *args):
147e77964f7SStefan Hajnoczi    '''need-check [on|off] - Test, set, or clear the QED_F_NEED_CHECK header bit'''
148e77964f7SStefan Hajnoczi    if not args:
149f03868bdSEduardo Habkost        print(bool(qed.header['features'] & QED_F_NEED_CHECK))
150e77964f7SStefan Hajnoczi        return
151e77964f7SStefan Hajnoczi
152e77964f7SStefan Hajnoczi    if args[0] == 'on':
153e77964f7SStefan Hajnoczi        qed.header['features'] |= QED_F_NEED_CHECK
154e77964f7SStefan Hajnoczi    elif args[0] == 'off':
155e77964f7SStefan Hajnoczi        qed.header['features'] &= ~QED_F_NEED_CHECK
156e77964f7SStefan Hajnoczi    else:
157e77964f7SStefan Hajnoczi        err('unrecognized sub-command')
158e77964f7SStefan Hajnoczi    qed.store_header()
159e77964f7SStefan Hajnoczi
160e77964f7SStefan Hajnoczidef cmd_zero_cluster(qed, pos, *args):
161e77964f7SStefan Hajnoczi    '''zero-cluster <pos> [<n>] - Zero data clusters'''
162e77964f7SStefan Hajnoczi    pos, n = int(pos), 1
163e77964f7SStefan Hajnoczi    if args:
164e77964f7SStefan Hajnoczi        if len(args) != 1:
165e77964f7SStefan Hajnoczi            err('expected one argument')
166e77964f7SStefan Hajnoczi        n = int(args[0])
167e77964f7SStefan Hajnoczi
168e77964f7SStefan Hajnoczi    for i in xrange(n):
1699a3a9a63SMax Reitz        l1_index = pos // qed.header['cluster_size'] // len(qed.l1_table)
170e77964f7SStefan Hajnoczi        if qed.l1_table[l1_index] == 0:
171e77964f7SStefan Hajnoczi            err('no l2 table allocated')
172e77964f7SStefan Hajnoczi
173e77964f7SStefan Hajnoczi        l2_offset = qed.l1_table[l1_index]
174e77964f7SStefan Hajnoczi        l2_table = qed.read_table(l2_offset)
175e77964f7SStefan Hajnoczi
1769a3a9a63SMax Reitz        l2_index = (pos // qed.header['cluster_size']) % len(qed.l1_table)
177e77964f7SStefan Hajnoczi        l2_table[l2_index] = 1 # zero the data cluster
178e77964f7SStefan Hajnoczi        qed.write_table(l2_offset, l2_table)
179e77964f7SStefan Hajnoczi        pos += qed.header['cluster_size']
180e77964f7SStefan Hajnoczi
181e77964f7SStefan Hajnoczidef cmd_copy_metadata(qed, outfile):
182e77964f7SStefan Hajnoczi    '''copy-metadata <outfile> - Copy metadata only (for scrubbing corrupted images)'''
183e77964f7SStefan Hajnoczi    out = open(outfile, 'wb')
184e77964f7SStefan Hajnoczi
185e77964f7SStefan Hajnoczi    # Match file size
186e77964f7SStefan Hajnoczi    out.seek(qed.filesize - 1)
187e77964f7SStefan Hajnoczi    out.write('\0')
188e77964f7SStefan Hajnoczi
189e77964f7SStefan Hajnoczi    # Copy header clusters
190e77964f7SStefan Hajnoczi    out.seek(0)
191e77964f7SStefan Hajnoczi    header_size_bytes = qed.header['header_size'] * qed.header['cluster_size']
192e77964f7SStefan Hajnoczi    out.write(qed.raw_pread(0, header_size_bytes))
193e77964f7SStefan Hajnoczi
194e77964f7SStefan Hajnoczi    # Copy L1 table
195e77964f7SStefan Hajnoczi    out.seek(qed.header['l1_table_offset'])
196e77964f7SStefan Hajnoczi    s = ''.join(pack_table_elem(x) for x in qed.l1_table)
197e77964f7SStefan Hajnoczi    out.write(s)
198e77964f7SStefan Hajnoczi
199e77964f7SStefan Hajnoczi    # Copy L2 tables
200e77964f7SStefan Hajnoczi    for l2_offset in qed.l1_table:
201e77964f7SStefan Hajnoczi        if l2_offset == 0:
202e77964f7SStefan Hajnoczi            continue
203e77964f7SStefan Hajnoczi        l2_table = qed.read_table(l2_offset)
204e77964f7SStefan Hajnoczi        out.seek(l2_offset)
205e77964f7SStefan Hajnoczi        s = ''.join(pack_table_elem(x) for x in l2_table)
206e77964f7SStefan Hajnoczi        out.write(s)
207e77964f7SStefan Hajnoczi
208e77964f7SStefan Hajnoczi    out.close()
209e77964f7SStefan Hajnoczi
210e77964f7SStefan Hajnoczidef usage():
211f03868bdSEduardo Habkost    print('Usage: %s <file> <cmd> [<arg>, ...]' % sys.argv[0])
212f03868bdSEduardo Habkost    print()
213f03868bdSEduardo Habkost    print('Supported commands:')
214e77964f7SStefan Hajnoczi    for cmd in sorted(x for x in globals() if x.startswith('cmd_')):
215f03868bdSEduardo Habkost        print(globals()[cmd].__doc__)
216e77964f7SStefan Hajnoczi    sys.exit(1)
217e77964f7SStefan Hajnoczi
218e77964f7SStefan Hajnoczidef main():
219e77964f7SStefan Hajnoczi    if len(sys.argv) < 3:
220e77964f7SStefan Hajnoczi        usage()
221e77964f7SStefan Hajnoczi    filename, cmd = sys.argv[1:3]
222e77964f7SStefan Hajnoczi
223e77964f7SStefan Hajnoczi    cmd = 'cmd_' + cmd.replace('-', '_')
224e77964f7SStefan Hajnoczi    if cmd not in globals():
225e77964f7SStefan Hajnoczi        usage()
226e77964f7SStefan Hajnoczi
227e77964f7SStefan Hajnoczi    qed = QED(open(filename, 'r+b'))
228e77964f7SStefan Hajnoczi    try:
229e77964f7SStefan Hajnoczi        globals()[cmd](qed, *sys.argv[3:])
23003e18810SMarkus Armbruster    except TypeError as e:
231e77964f7SStefan Hajnoczi        sys.stderr.write(globals()[cmd].__doc__ + '\n')
232e77964f7SStefan Hajnoczi        sys.exit(1)
233e77964f7SStefan Hajnoczi
234e77964f7SStefan Hajnocziif __name__ == '__main__':
235e77964f7SStefan Hajnoczi    main()
236