blob: d2fb433ddc719547b0df0fc300307f1664c87ab2 [file] [log] [blame]
barfab@chromium.orgb6d29932012-04-11 09:46:43 +02001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Exposes the FAFTClient interface over XMLRPC.
6
7It launches a XMLRPC server and exposes the interface of FAFTClient object.
8The FAFTClient object aggreates some useful functions of exisintg SAFT
9libraries.
10"""
11
barfab@chromium.orgb6d29932012-04-11 09:46:43 +020012import functools, os, shutil, sys
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -070013from optparse import OptionParser
14from SimpleXMLRPCServer import SimpleXMLRPCServer
15
16# Import libraries from SAFT.
Tom Wai-Hong Tamc1576b72011-11-08 11:36:16 +080017sys.path.append('/usr/local/sbin/firmware/saft')
barfab@chromium.orgb6d29932012-04-11 09:46:43 +020018import cgpt_state, chromeos_interface, flashrom_handler, kernel_handler
19import saft_flashrom_util, tpm_handler
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -070020
21
Tom Wai-Hong Tam0e680af2011-10-26 14:32:55 +080022def allow_multiple_section_input(image_operator):
23 @functools.wraps(image_operator)
24 def wrapper(self, section):
25 if type(section) in (tuple, list):
26 for sec in section:
27 image_operator(self, sec)
28 else:
29 image_operator(self, section)
30 return wrapper
31
32
Vic Yang13d22ec2012-06-18 15:12:47 +080033class LazyFlashromHandlerProxy:
34 _loaded = False
35 _obj = None
36
37 def __init__(self, *args, **kargs):
38 self._args = args
39 self._kargs = kargs
40
41 def _load(self):
42 self._obj = flashrom_handler.FlashromHandler()
43 self._obj.init(*self._args, **self._kargs)
44 self._obj.new_image()
45 self._loaded = True
46
47 def __getattr__(self, name):
48 if not self._loaded:
49 self._load()
50 return getattr(self._obj, name)
51
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +080052 def reload(self):
53 self._loaded = False
54
Vic Yang13d22ec2012-06-18 15:12:47 +080055
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -070056class FAFTClient(object):
57 """A class of FAFT client which aggregates some useful functions of SAFT.
58
59 This class can be exposed via a XMLRPC server such that its functions can
60 be accessed remotely.
61
62 Attributes:
63 _chromeos_interface: An object to encapsulate OS services functions.
Vic Yang59cac9c2012-05-21 15:28:42 +080064 _bios_handler: An object to automate BIOS flashrom testing.
65 _ec_handler: An object to automate EC flashrom testing.
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -070066 _kernel_handler: An object to provide kernel related actions.
67 _tpm_handler: An object to control TPM device.
68 """
69
70 def __init__(self):
71 """Initialize the data attributes of this class."""
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -070072 # TODO(waihong): Move the explicit object.init() methods to the
73 # objects' constructors (ChromeOSInterface, FlashromHandler,
74 # KernelHandler, and TpmHandler).
75 self._chromeos_interface = chromeos_interface.ChromeOSInterface(False)
Tom Wai-Hong Tam48958832011-12-30 10:16:57 +080076 # We keep the state of FAFT test in a permanent directory over reboots.
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +080077 state_dir = '/var/tmp/faft'
78 self._chromeos_interface.init(state_dir, log_file='/tmp/faft_log.txt')
79 os.chdir(state_dir)
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -070080
Vic Yang13d22ec2012-06-18 15:12:47 +080081 self._bios_handler = LazyFlashromHandlerProxy(
82 saft_flashrom_util,
Vic Yang59cac9c2012-05-21 15:28:42 +080083 self._chromeos_interface,
84 None,
85 '/usr/share/vboot/devkeys',
86 'bios')
Vic Yang59cac9c2012-05-21 15:28:42 +080087
Todd Brochf2b1d012012-06-14 12:55:21 -070088 self._ec_handler = None
89 if not os.system("mosys ec info"):
Vic Yang13d22ec2012-06-18 15:12:47 +080090 self._ec_handler = LazyFlashromHandlerProxy(
91 saft_flashrom_util,
Todd Brochf2b1d012012-06-14 12:55:21 -070092 self._chromeos_interface,
Vic Yang13d22ec2012-06-18 15:12:47 +080093 'ec_root_key.vpubk',
Todd Brochf2b1d012012-06-14 12:55:21 -070094 '/usr/share/vboot/devkeys',
95 'ec')
Todd Brochf2b1d012012-06-14 12:55:21 -070096
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -070097
98 self._kernel_handler = kernel_handler.KernelHandler()
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +080099 # TODO(waihong): The dev_key_path is a new argument. We do that in
100 # order not to break the old image and still be able to run.
101 try:
102 self._kernel_handler.init(self._chromeos_interface,
103 dev_key_path='/usr/share/vboot/devkeys')
104 except:
105 # Copy the key to the current working directory.
106 shutil.copy('/usr/share/vboot/devkeys/kernel_data_key.vbprivk', '.')
107 self._kernel_handler.init(self._chromeos_interface)
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700108
109 self._tpm_handler = tpm_handler.TpmHandler()
110 self._tpm_handler.init(self._chromeos_interface)
111
Tom Wai-Hong Tam48958832011-12-30 10:16:57 +0800112 self._cgpt_state = cgpt_state.CgptState(
Tom Wai-Hong Tamed2231c2012-07-27 14:39:46 +0800113 'SHORT', self._chromeos_interface, self.get_root_dev())
Tom Wai-Hong Tam48958832011-12-30 10:16:57 +0800114
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700115
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +0800116 def _dispatch(self, method, params):
117 """This _dispatch method handles string conversion especially.
118
119 Since we turn off allow_dotted_names option. So any string conversion,
120 like str(FAFTClient.method), i.e. FAFTClient.method.__str__, failed
121 via XML RPC call.
122 """
123 is_str = method.endswith('.__str__')
124 if is_str:
125 method = method.rsplit('.', 1)[0]
126 try:
127 func = getattr(self, method)
128 except AttributeError:
129 raise Exception('method "%s" is not supported' % method)
130 else:
131 if is_str:
132 return str(func)
133 else:
134 return func(*params)
135
136
Tom Wai-Hong Tambea57b32011-09-02 18:27:47 +0800137 def is_available(self):
138 """Function for polling the RPC server availability.
139
140 Returns:
141 Always True.
142 """
143 return True
144
145
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700146 def run_shell_command(self, command):
147 """Run shell command.
148
149 Args:
150 command: A shell command to be run.
151 """
152 self._chromeos_interface.log('Requesting run shell command')
153 self._chromeos_interface.run_shell_command(command)
154
155
156 def run_shell_command_get_output(self, command):
157 """Run shell command and get its console output.
158
159 Args:
160 command: A shell command to be run.
161
162 Returns:
163 A list of strings stripped of the newline characters.
164 """
165 self._chromeos_interface.log(
166 'Requesting run shell command and get its console output')
167 return self._chromeos_interface.run_shell_command_get_output(command)
168
169
170 def software_reboot(self):
171 """Request software reboot."""
172 self._chromeos_interface.log('Requesting software reboot')
173 self._chromeos_interface.run_shell_command('reboot')
174
175
Tom Wai-Hong Tam678ab152011-12-14 15:27:24 +0800176 def get_platform_name(self):
177 """Get the platform name of the current system.
178
179 Returns:
180 A string of the platform name.
181 """
182 self._chromeos_interface.log('Requesting get platform name')
183 return self._chromeos_interface.run_shell_command_get_output(
184 'mosys platform name')[0]
185
186
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700187 def get_crossystem_value(self, key):
188 """Get crossystem value of the requested key.
189
190 Args:
191 key: A crossystem key.
192
193 Returns:
194 A string of the requested crossystem value.
195 """
196 self._chromeos_interface.log('Requesting get crossystem value')
197 return self._chromeos_interface.run_shell_command_get_output(
198 'crossystem %s' % key)[0]
199
200
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800201 def get_root_dev(self):
202 """Get the name of root device without partition number.
203
204 Returns:
205 A string of the root device without partition number.
206 """
207 self._chromeos_interface.log('Requesting get root device')
208 return self._chromeos_interface.get_root_dev()
209
210
211 def get_root_part(self):
212 """Get the name of root device with partition number.
213
214 Returns:
215 A string of the root device with partition number.
216 """
217 self._chromeos_interface.log('Requesting get root part')
218 return self._chromeos_interface.get_root_part()
219
220
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700221 def set_try_fw_b(self):
222 """Set 'Try Frimware B' flag in crossystem."""
223 self._chromeos_interface.log('Requesting restart with firmware B')
224 self._chromeos_interface.cs.fwb_tries = 1
225
226
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800227 def request_recovery_boot(self):
228 """Request running in recovery mode on the restart."""
229 self._chromeos_interface.log('Requesting restart in recovery mode')
230 self._chromeos_interface.cs.request_recovery()
231
232
Tom Wai-Hong Tam8c9eed62011-12-28 15:05:05 +0800233 def get_gbb_flags(self):
234 """Get the GBB flags.
235
236 Returns:
237 An integer of the GBB flags.
238 """
239 self._chromeos_interface.log('Getting GBB flags')
Vic Yang59cac9c2012-05-21 15:28:42 +0800240 return self._bios_handler.get_gbb_flags()
Tom Wai-Hong Tam8c9eed62011-12-28 15:05:05 +0800241
242
Tom Wai-Hong Tam81f70002011-12-13 12:29:46 +0800243 def get_firmware_flags(self, section):
244 """Get the preamble flags of a firmware section.
245
246 Args:
247 section: A firmware section, either 'a' or 'b'.
248
249 Returns:
250 An integer of the preamble flags.
251 """
252 self._chromeos_interface.log('Getting preamble flags of firmware %s' %
253 section)
Vic Yang59cac9c2012-05-21 15:28:42 +0800254 return self._bios_handler.get_section_flags(section)
255
256
Vic Yang91b73cf2012-07-31 17:18:11 +0800257 def set_firmware_flags(self, section, flags):
258 """Set the preamble flags of a firmware section.
259
260 Args:
261 section: A firmware section, either 'a' or 'b'.
262 flags: An integer of preamble flags.
263 """
264 self._chromeos_interface.log(
265 'Setting preamble flags of firmware %s to %s' % (section, flags))
266 version = self.get_firmware_version(section)
267 self._bios_handler.set_section_version(section, version, flags,
268 write_through=True)
269
270
271 def get_EC_firmware_sha(self):
272 """Get SHA1 hash of EC RW firmware section. """
273 return self._ec_handler.get_section_sha('rw')
274
275
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800276 def reload_firmware(self):
277 """Reload the firmware image that may be changed."""
278 self._bios_handler.reload()
279
280
Vic Yang59cac9c2012-05-21 15:28:42 +0800281 @allow_multiple_section_input
282 def corrupt_EC(self, section):
283 """Corrupt the requested EC section signature.
284
285 Args:
286 section: A EC section, either 'a' or 'b'.
287 """
288 self._chromeos_interface.log('Corrupting EC signature %s' %
289 section)
Vic Yang37a55462012-06-29 14:00:28 +0800290 self._ec_handler.corrupt_firmware(section, corrupt_all=True)
Vic Yang59cac9c2012-05-21 15:28:42 +0800291
292
293 @allow_multiple_section_input
294 def corrupt_EC_body(self, section):
295 """Corrupt the requested EC section body.
296
297 Args:
298 section: An EC section, either 'a' or 'b'.
299 """
300 self._chromeos_interface.log('Corrupting EC body %s' %
301 section)
Vic Yang37a55462012-06-29 14:00:28 +0800302 self._ec_handler.corrupt_firmware_body(section, corrupt_all=True)
Vic Yang59cac9c2012-05-21 15:28:42 +0800303
304
305 @allow_multiple_section_input
306 def restore_EC(self, section):
307 """Restore the previously corrupted EC section signature.
308
309 Args:
310 section: An EC section, either 'a' or 'b'.
311 """
312 self._chromeos_interface.log('Restoring EC signature %s' %
313 section)
Vic Yang37a55462012-06-29 14:00:28 +0800314 self._ec_handler.restore_firmware(section, restore_all=True)
Vic Yang59cac9c2012-05-21 15:28:42 +0800315
316
317 @allow_multiple_section_input
318 def restore_EC_body(self, section):
319 """Restore the previously corrupted EC section body.
320
321 Args:
322 section: An EC section, either 'a' or 'b'.
323 """
324 self._chromeos_interface.log('Restoring EC body %s' %
325 section)
Vic Yang37a55462012-06-29 14:00:28 +0800326 self._ec_handler.restore_firmware_body(section, restore_all=True)
Tom Wai-Hong Tam81f70002011-12-13 12:29:46 +0800327
328
Tom Wai-Hong Tam0e680af2011-10-26 14:32:55 +0800329 @allow_multiple_section_input
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700330 def corrupt_firmware(self, section):
Tom Wai-Hong Tam9aea8212011-12-12 15:08:45 +0800331 """Corrupt the requested firmware section signature.
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700332
333 Args:
334 section: A firmware section, either 'a' or 'b'.
335 """
Tom Wai-Hong Tam9aea8212011-12-12 15:08:45 +0800336 self._chromeos_interface.log('Corrupting firmware signature %s' %
337 section)
Vic Yang59cac9c2012-05-21 15:28:42 +0800338 self._bios_handler.corrupt_firmware(section)
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700339
340
Tom Wai-Hong Tam0e680af2011-10-26 14:32:55 +0800341 @allow_multiple_section_input
Tom Wai-Hong Tam81f70002011-12-13 12:29:46 +0800342 def corrupt_firmware_body(self, section):
343 """Corrupt the requested firmware section body.
344
345 Args:
346 section: A firmware section, either 'a' or 'b'.
347 """
348 self._chromeos_interface.log('Corrupting firmware body %s' %
349 section)
Vic Yang59cac9c2012-05-21 15:28:42 +0800350 self._bios_handler.corrupt_firmware_body(section)
Tom Wai-Hong Tam81f70002011-12-13 12:29:46 +0800351
352
353 @allow_multiple_section_input
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700354 def restore_firmware(self, section):
Tom Wai-Hong Tam9aea8212011-12-12 15:08:45 +0800355 """Restore the previously corrupted firmware section signature.
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700356
357 Args:
358 section: A firmware section, either 'a' or 'b'.
359 """
Tom Wai-Hong Tam9aea8212011-12-12 15:08:45 +0800360 self._chromeos_interface.log('Restoring firmware signature %s' %
361 section)
Vic Yang59cac9c2012-05-21 15:28:42 +0800362 self._bios_handler.restore_firmware(section)
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700363
364
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800365 @allow_multiple_section_input
Tom Wai-Hong Tam81f70002011-12-13 12:29:46 +0800366 def restore_firmware_body(self, section):
367 """Restore the previously corrupted firmware section body.
368
369 Args:
370 section: A firmware section, either 'a' or 'b'.
371 """
372 self._chromeos_interface.log('Restoring firmware body %s' %
373 section)
Vic Yang59cac9c2012-05-21 15:28:42 +0800374 self._bios_handler.restore_firmware_body(section)
Tom Wai-Hong Tam81f70002011-12-13 12:29:46 +0800375
376
Vic Yang91b73cf2012-07-31 17:18:11 +0800377 def get_firmware_version(self, section):
378 """Retrieve firmware version of a section."""
379 return self._bios_handler.get_section_version(section)
380
381
Tom Wai-Hong Tam46d03b12012-02-08 12:02:17 +0800382 def _modify_firmware_version(self, section, delta):
383 """Modify firmware version for the requested section, by adding delta.
384
385 The passed in delta, a positive or a negative number, is added to the
386 original firmware version.
387 """
Vic Yang91b73cf2012-07-31 17:18:11 +0800388 original_version = self.get_firmware_version(section)
Tom Wai-Hong Tam46d03b12012-02-08 12:02:17 +0800389 new_version = original_version + delta
Vic Yang59cac9c2012-05-21 15:28:42 +0800390 flags = self._bios_handler.get_section_flags(section)
Tom Wai-Hong Tam46d03b12012-02-08 12:02:17 +0800391 self._chromeos_interface.log(
392 'Setting firmware section %s version from %d to %d' % (
393 section, original_version, new_version))
Vic Yang59cac9c2012-05-21 15:28:42 +0800394 self._bios_handler.set_section_version(section, new_version, flags,
395 write_through=True)
Tom Wai-Hong Tam46d03b12012-02-08 12:02:17 +0800396
397 @allow_multiple_section_input
398 def move_firmware_backward(self, section):
399 """Decrement firmware version for the requested section."""
400 self._modify_firmware_version(section, -1)
401
402
403 @allow_multiple_section_input
404 def move_firmware_forward(self, section):
405 """Increase firmware version for the requested section."""
406 self._modify_firmware_version(section, 1)
407
408
Tom Wai-Hong Tam81f70002011-12-13 12:29:46 +0800409 @allow_multiple_section_input
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800410 def corrupt_kernel(self, section):
411 """Corrupt the requested kernel section.
412
413 Args:
414 section: A kernel section, either 'a' or 'b'.
415 """
416 self._chromeos_interface.log('Corrupting kernel %s' % section)
417 self._kernel_handler.corrupt_kernel(section)
418
419
420 @allow_multiple_section_input
421 def restore_kernel(self, section):
422 """Restore the requested kernel section (previously corrupted).
423
424 Args:
425 section: A kernel section, either 'a' or 'b'.
426 """
427 self._chromeos_interface.log('restoring kernel %s' % section)
428 self._kernel_handler.restore_kernel(section)
429
430
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800431 def _modify_kernel_version(self, section, delta):
432 """Modify kernel version for the requested section, by adding delta.
433
434 The passed in delta, a positive or a negative number, is added to the
435 original kernel version.
436 """
437 original_version = self._kernel_handler.get_version(section)
438 new_version = original_version + delta
439 self._chromeos_interface.log(
440 'Setting kernel section %s version from %d to %d' % (
441 section, original_version, new_version))
442 self._kernel_handler.set_version(section, new_version)
443
444
445 @allow_multiple_section_input
446 def move_kernel_backward(self, section):
447 """Decrement kernel version for the requested section."""
448 self._modify_kernel_version(section, -1)
449
450
451 @allow_multiple_section_input
452 def move_kernel_forward(self, section):
453 """Increase kernel version for the requested section."""
454 self._modify_kernel_version(section, 1)
455
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +0800456 def diff_kernel_a_b(self):
457 """Compare kernel A with B.
458
459 Returns:
460 True: if kernel A is different with B.
461 False: if kernel A is the same as B.
462 """
463 rootdev = self._chromeos_interface.get_root_dev()
464 kernel_a = self._chromeos_interface.join_part(rootdev, '3')
465 kernel_b = self._chromeos_interface.join_part(rootdev, '5')
466
467 # The signature (some kind of hash) for the kernel body is stored in
468 # the beginning. So compare the first 64KB (including header, preamble,
469 # and signature) should be enough to check them identical.
470 header_a = self._chromeos_interface.read_partition(kernel_a, 0x10000)
471 header_b = self._chromeos_interface.read_partition(kernel_b, 0x10000)
472
473 return header_a != header_b
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800474
Tom Wai-Hong Tam48958832011-12-30 10:16:57 +0800475 def run_cgpt_test_loop(self):
476 """Run the CgptState test loop. The tst logic is handled in the client.
477
478 Returns:
479 0: there are more cgpt tests to execute.
480 1: no more CgptState test, finished.
481 """
482 return self._cgpt_state.test_loop()
483
484
485 def set_cgpt_test_step(self, step):
486 """Set the CgptState test step.
487
488 Args:
489 step: A test step number.
490 """
491 self._cgpt_state.set_step(step)
492
493
494 def get_cgpt_test_step(self):
495 """Get the CgptState test step.
496
497 Returns:
498 A test step number.
499 """
500 return self._cgpt_state.get_step()
501
502
Tom Wai-Hong Tambea57b32011-09-02 18:27:47 +0800503 def cleanup(self):
504 """Cleanup for the RPC server. Currently nothing."""
505 pass
506
507
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700508def main():
509 parser = OptionParser(usage='Usage: %prog [options]')
510 parser.add_option('--port', type='int', dest='port', default=9990,
511 help='port number of XMLRPC server')
512 (options, args) = parser.parse_args()
513
514 faft_client = FAFTClient()
515
516 # Launch the XMLRPC server to provide FAFTClient commands.
Tom Wai-Hong Tam4a257e52011-11-12 08:36:22 +0800517 server = SimpleXMLRPCServer(('localhost', options.port), allow_none=True,
518 logRequests=False)
Tom Wai-Hong Tamb8a58ef2011-10-11 23:53:10 -0700519 server.register_introspection_functions()
520 server.register_instance(faft_client)
521 print 'XMLRPC Server: Serving FAFTClient on port %s' % options.port
522 server.serve_forever()
523
524
525if __name__ == '__main__':
526 main()