• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Code to provide functions for FAFT tests.
6
7These can be exposed via a xmlrpci server running on the DUT.
8"""
9
10import functools, os, tempfile
11
12import common
13from autotest_lib.client.cros.faft.utils import (cgpt_handler,
14                                                 os_interface,
15                                                 firmware_check_keys,
16                                                 firmware_updater,
17                                                 flashrom_handler,
18                                                 kernel_handler,
19                                                 rootfs_handler,
20                                                 saft_flashrom_util,
21                                                 tpm_handler,
22                                                )
23
24
25def allow_multiple_section_input(image_operator):
26    """Decorate a method to support multiple sections.
27
28    @param image_operator: Method accepting one section as its argument.
29    """
30    @functools.wraps(image_operator)
31    def wrapper(self, section):
32        """Wrapper method to support multiple sections.
33
34        @param section: A list of sections of just a section.
35        """
36        if type(section) in (tuple, list):
37            for sec in section:
38                image_operator(self, sec)
39        else:
40            image_operator(self, section)
41    return wrapper
42
43
44class LazyInitHandlerProxy:
45    """Proxy of a given handler_class for lazy initialization."""
46    _loaded = False
47    _obj = None
48
49    def __init__(self, handler_class, *args, **kargs):
50        self._handler_class = handler_class
51        self._args = args
52        self._kargs = kargs
53
54    def _load(self):
55        self._obj = self._handler_class()
56        self._obj.init(*self._args, **self._kargs)
57        self._loaded = True
58
59    def __getattr__(self, name):
60        if not self._loaded:
61            self._load()
62        return getattr(self._obj, name)
63
64    def reload(self):
65        """Reload the FlashromHandler class."""
66        self._loaded = False
67
68
69class RPCFunctions(object):
70    """A class which aggregates some useful functions for firmware testing.
71
72    This class can be exposed via a XMLRPC server such that its functions can
73    be accessed remotely. Method naming should fit the naming rule
74    '_[categories]_[method_name]' where categories contains system, ec, bios,
75    kernel, cgpt, tpm, updater, etc. Methods should be called by
76    'FAFTClient.[categories].[method_name]', because _dispatch will rename
77    this name to '_[categories]_[method_name]'.
78
79    Attributes:
80        _os_if: An object to encapsulate OS services functions.
81        _bios_handler: An object to automate BIOS flashrom testing.
82        _ec_handler: An object to automate EC flashrom testing.
83        _kernel_handler: An object to provide kernel related actions.
84        _log_file: Path of the log file.
85        _tpm_handler: An object to control TPM device.
86        _updater: An object to update firmware.
87        _temp_path: Path of a temp directory.
88        _keys_path: Path of a directory, keys/, in temp directory.
89        _work_path: Path of a directory, work/, in temp directory.
90    """
91    def __init__(self):
92        """Initialize the data attributes of this class."""
93        # TODO(waihong): Move the explicit object.init() methods to the
94        # objects' constructors (OSInterface, FlashromHandler,
95        # KernelHandler, and TpmHandler).
96        self._os_if = os_interface.OSInterface()
97        # We keep the state of FAFT test in a permanent directory over reboots.
98        state_dir = '/var/tmp/faft'
99        self._log_file = os.path.join(state_dir, 'faft_client.log')
100        self._os_if.init(state_dir, log_file=self._log_file)
101        os.chdir(state_dir)
102
103        self._bios_handler = LazyInitHandlerProxy(
104                flashrom_handler.FlashromHandler,
105                saft_flashrom_util,
106                self._os_if,
107                None,
108                '/usr/share/vboot/devkeys',
109                'bios')
110
111        self._ec_handler = None
112        if self._os_if.run_shell_command_get_status('mosys ec info') == 0:
113            self._ec_handler = LazyInitHandlerProxy(
114                    flashrom_handler.FlashromHandler,
115                    saft_flashrom_util,
116                    self._os_if,
117                    'ec_root_key.vpubk',
118                    '/usr/share/vboot/devkeys',
119                    'ec')
120        else:
121            self._os_if.log('No EC is reported by mosys.')
122
123        self._kernel_handler = kernel_handler.KernelHandler()
124        self._kernel_handler.init(self._os_if,
125                                  dev_key_path='/usr/share/vboot/devkeys',
126                                  internal_disk=True)
127
128        self._tpm_handler = LazyInitHandlerProxy(
129                tpm_handler.TpmHandler,
130                self._os_if)
131
132        self._cgpt_handler = cgpt_handler.CgptHandler(self._os_if)
133
134        self._rootfs_handler = rootfs_handler.RootfsHandler()
135        self._rootfs_handler.init(self._os_if)
136
137        self._updater = firmware_updater.FirmwareUpdater(self._os_if)
138        self._check_keys = firmware_check_keys.firmwareCheckKeys()
139
140        # Initialize temporary directory path
141        self._temp_path = '/var/tmp/faft/autest'
142        self._keys_path = os.path.join(self._temp_path, 'keys')
143        self._work_path = os.path.join(self._temp_path, 'work')
144
145    def _dispatch(self, method, params):
146        """This _dispatch method handles string conversion especially.
147
148        Since we turn off allow_dotted_names option. So any string conversion,
149        like str(FAFTClient.method), i.e. FAFTClient.method.__str__, failed
150        via XML RPC call.
151        """
152        is_str = method.endswith('.__str__')
153        if is_str:
154            method = method.rsplit('.', 1)[0]
155
156        categories = ('system', 'host', 'bios', 'ec', 'kernel',
157                      'tpm', 'cgpt', 'updater', 'rootfs')
158        try:
159            if method.split('.', 1)[0] in categories:
160                func = getattr(self, '_%s_%s' % (method.split('.', 1)[0],
161                                                 method.split('.', 1)[1]))
162            else:
163                func = getattr(self, method)
164        except AttributeError:
165            raise Exception('method "%s" is not supported' % method)
166
167        if is_str:
168            return str(func)
169        else:
170            self._os_if.log('Dispatching method %s with args %r' %
171                    (func.__name__, params))
172            return func(*params)
173
174    def _system_is_available(self):
175        """Function for polling the RPC server availability.
176
177        @return: Always True.
178        """
179        return True
180
181    def _system_has_host(self):
182        """Return True if a host is connected to DUT."""
183        return self._os_if.has_host()
184
185    def _system_wait_for_client(self, timeout):
186        """Wait for the client to come back online.
187
188        @param timeout: Time in seconds to wait for the client SSH daemon to
189                        come up.
190        @return: True if succeed; otherwise False.
191        """
192        return self._os_if.wait_for_device(timeout)
193
194    def _system_wait_for_client_offline(self, timeout):
195        """Wait for the client to come offline.
196
197        @param timeout: Time in seconds to wait the client to come offline.
198        @return: True if succeed; otherwise False.
199        """
200        return self._os_if.wait_for_no_device(timeout)
201
202    def _system_dump_log(self, remove_log=False):
203        """Dump the log file.
204
205        @param remove_log: Remove the log file after dump.
206        @return: String of the log file content.
207        """
208        log = open(self._log_file).read()
209        if remove_log:
210            os.remove(self._log_file)
211        return log
212
213    def _system_run_shell_command(self, command):
214        """Run shell command.
215
216        @param command: A shell command to be run.
217        """
218        self._os_if.run_shell_command(command)
219
220    def _system_run_shell_command_get_output(self, command):
221        """Run shell command and get its console output.
222
223        @param command: A shell command to be run.
224        @return: A list of strings stripped of the newline characters.
225        """
226        return self._os_if.run_shell_command_get_output(command)
227
228    def _host_run_shell_command(self, command):
229        """Run shell command on the host.
230
231        @param command: A shell command to be run.
232        """
233        self._os_if.run_host_shell_command(command)
234
235    def _host_run_shell_command_get_output(self, command):
236        """Run shell command and get its console output on the host.
237
238        @param command: A shell command to be run.
239        @return: A list of strings stripped of the newline characters.
240        """
241        return self._os_if.run_host_shell_command_get_output(command)
242
243    def _host_run_nonblock_shell_command(self, command):
244        """Run non-blocking shell command
245
246        @param command: A shell command to be run.
247        @return: none
248        """
249        return self._os_if.run_host_shell_command(command, False)
250
251    def _system_software_reboot(self):
252        """Request software reboot."""
253        self._os_if.run_shell_command('reboot')
254
255    def _system_get_platform_name(self):
256        """Get the platform name of the current system.
257
258        @return: A string of the platform name.
259        """
260        # 'mosys platform name' sometimes fails. Let's get the verbose output.
261        lines = self._os_if.run_shell_command_get_output(
262                '(mosys -vvv platform name 2>&1) || echo Failed')
263        if lines[-1].strip() == 'Failed':
264            raise Exception('Failed getting platform name: ' + '\n'.join(lines))
265        return lines[-1]
266
267    def _system_get_crossystem_value(self, key):
268        """Get crossystem value of the requested key.
269
270        @param key: A crossystem key.
271        @return: A string of the requested crossystem value.
272        """
273        return self._os_if.run_shell_command_get_output(
274                'crossystem %s' % key)[0]
275
276    def _system_get_root_dev(self):
277        """Get the name of root device without partition number.
278
279        @return: A string of the root device without partition number.
280        """
281        return self._os_if.get_root_dev()
282
283    def _system_get_root_part(self):
284        """Get the name of root device with partition number.
285
286        @return: A string of the root device with partition number.
287        """
288        return self._os_if.get_root_part()
289
290    def _system_set_try_fw_b(self, count=1):
291        """Set 'Try Frimware B' flag in crossystem.
292
293        @param count: # times to try booting into FW B
294        """
295        self._os_if.cs.fwb_tries = count
296
297    def _system_set_fw_try_next(self, next, count=0):
298        """Set fw_try_next to A or B
299
300        @param next: Next FW to reboot to (A or B)
301        @param count: # of times to try booting into FW <next>
302        """
303        self._os_if.cs.fw_try_next = next
304        if count:
305            self._os_if.cs.fw_try_count = count
306
307    def _system_get_fw_vboot2(self):
308        """Get fw_vboot2"""
309        try:
310            return self._os_if.cs.fw_vboot2 == '1'
311        except os_interface.OSInterfaceError:
312            return False
313
314    def _system_request_recovery_boot(self):
315        """Request running in recovery mode on the restart."""
316        self._os_if.cs.request_recovery()
317
318    def _system_get_dev_boot_usb(self):
319        """Get dev_boot_usb value which controls developer mode boot from USB.
320
321        @return: True if enable, False if disable.
322        """
323        return self._os_if.cs.dev_boot_usb == '1'
324
325    def _system_set_dev_boot_usb(self, value):
326        """Set dev_boot_usb value which controls developer mode boot from USB.
327
328        @param value: True to enable, False to disable.
329        """
330        self._os_if.cs.dev_boot_usb = 1 if value else 0
331
332    def _system_is_removable_device_boot(self):
333        """Check the current boot device is removable.
334
335        @return: True: if a removable device boots.
336                 False: if a non-removable device boots.
337        """
338        root_part = self._os_if.get_root_part()
339        return self._os_if.is_removable_device(root_part)
340
341    def _system_create_temp_dir(self, prefix='backup_'):
342        """Create a temporary directory and return the path."""
343        return tempfile.mkdtemp(prefix=prefix)
344
345    def _bios_reload(self):
346        """Reload the firmware image that may be changed."""
347        self._bios_handler.reload()
348
349    def _bios_get_gbb_flags(self):
350        """Get the GBB flags.
351
352        @return: An integer of the GBB flags.
353        """
354        return self._bios_handler.get_gbb_flags()
355
356    def _bios_set_gbb_flags(self, flags):
357        """Set the GBB flags.
358
359        @param flags: An integer of the GBB flags.
360        """
361        self._bios_handler.set_gbb_flags(flags, write_through=True)
362
363    def _bios_get_preamble_flags(self, section):
364        """Get the preamble flags of a firmware section.
365
366        @param section: A firmware section, either 'a' or 'b'.
367        @return: An integer of the preamble flags.
368        """
369        return self._bios_handler.get_section_flags(section)
370
371    def _bios_set_preamble_flags(self, section, flags):
372        """Set the preamble flags of a firmware section.
373
374        @param section: A firmware section, either 'a' or 'b'.
375        @param flags: An integer of preamble flags.
376        """
377        version = self._bios_get_version(section)
378        self._bios_handler.set_section_version(section, version, flags,
379                                               write_through=True)
380
381    def _bios_get_body_sha(self, section):
382        """Get SHA1 hash of BIOS RW firmware section.
383
384        @param section: A firmware section, either 'a' or 'b'.
385        @param flags: An integer of preamble flags.
386        """
387        return self._bios_handler.get_section_sha(section)
388
389    def _bios_get_sig_sha(self, section):
390        """Get SHA1 hash of firmware vblock in section."""
391        return self._bios_handler.get_section_sig_sha(section)
392
393    @allow_multiple_section_input
394    def _bios_corrupt_sig(self, section):
395        """Corrupt the requested firmware section signature.
396
397        @param section: A firmware section, either 'a' or 'b'.
398        """
399        self._bios_handler.corrupt_firmware(section)
400
401    @allow_multiple_section_input
402    def _bios_restore_sig(self, section):
403        """Restore the previously corrupted firmware section signature.
404
405        @param section: A firmware section, either 'a' or 'b'.
406        """
407        self._bios_handler.restore_firmware(section)
408
409    @allow_multiple_section_input
410    def _bios_corrupt_body(self, section):
411        """Corrupt the requested firmware section body.
412
413        @param section: A firmware section, either 'a' or 'b'.
414        """
415        self._bios_handler.corrupt_firmware_body(section)
416
417    @allow_multiple_section_input
418    def _bios_restore_body(self, section):
419        """Restore the previously corrupted firmware section body.
420
421        @param section: A firmware section, either 'a' or 'b'.
422        """
423        self._bios_handler.restore_firmware_body(section)
424
425    def __bios_modify_version(self, section, delta):
426        """Modify firmware version for the requested section, by adding delta.
427
428        The passed in delta, a positive or a negative number, is added to the
429        original firmware version.
430        """
431        original_version = self._bios_get_version(section)
432        new_version = original_version + delta
433        flags = self._bios_handler.get_section_flags(section)
434        self._os_if.log(
435                'Setting firmware section %s version from %d to %d' % (
436                section, original_version, new_version))
437        self._bios_handler.set_section_version(section, new_version, flags,
438                                               write_through=True)
439
440    @allow_multiple_section_input
441    def _bios_move_version_backward(self, section):
442        """Decrement firmware version for the requested section."""
443        self.__bios_modify_version(section, -1)
444
445    @allow_multiple_section_input
446    def _bios_move_version_forward(self, section):
447        """Increase firmware version for the requested section."""
448        self.__bios_modify_version(section, 1)
449
450    def _bios_get_version(self, section):
451        """Retrieve firmware version of a section."""
452        return self._bios_handler.get_section_version(section)
453
454    def _bios_get_datakey_version(self, section):
455        """Return firmware data key version."""
456        return self._bios_handler.get_section_datakey_version(section)
457
458    def _bios_get_kernel_subkey_version(self, section):
459        """Return kernel subkey version."""
460        return self._bios_handler.get_section_kernel_subkey_version(section)
461
462    def _bios_dump_whole(self, bios_path):
463        """Dump the current BIOS firmware to a file, specified by bios_path.
464
465        @param bios_path: The path of the BIOS image to be written.
466        """
467        self._bios_handler.dump_whole(bios_path)
468
469    def _bios_write_whole(self, bios_path):
470        """Write the firmware from bios_path to the current system.
471
472        @param bios_path: The path of the source BIOS image.
473        """
474        self._bios_handler.new_image(bios_path)
475        self._bios_handler.write_whole()
476
477    def _ec_get_version(self):
478        """Get EC version via mosys.
479
480        @return: A string of the EC version.
481        """
482        return self._os_if.run_shell_command_get_output(
483                'mosys ec info | sed "s/.*| //"')[0]
484
485    def _ec_get_firmware_sha(self):
486        """Get SHA1 hash of EC RW firmware section."""
487        return self._ec_handler.get_section_sha('rw')
488
489    def _ec_dump_whole(self, ec_path):
490        """Dump the current EC firmware to a file, specified by ec_path.
491
492        @param ec_path: The path of the EC image to be written.
493        """
494        self._ec_handler.dump_whole(ec_path)
495
496    def _ec_write_whole(self, ec_path):
497        """Write the firmware from ec_path to the current system.
498
499        @param ec_path: The path of the source EC image.
500        """
501        self._ec_handler.new_image(ec_path)
502        self._ec_handler.write_whole()
503
504    @allow_multiple_section_input
505    def _ec_corrupt_sig(self, section):
506        """Corrupt the requested EC section signature.
507
508        @param section: A EC section, either 'a' or 'b'.
509        """
510        self._ec_handler.corrupt_firmware(section, corrupt_all=True)
511
512    @allow_multiple_section_input
513    def _ec_restore_sig(self, section):
514        """Restore the previously corrupted EC section signature.
515
516        @param section: An EC section, either 'a' or 'b'.
517        """
518        self._ec_handler.restore_firmware(section, restore_all=True)
519
520    @allow_multiple_section_input
521    def _ec_corrupt_body(self, section):
522        """Corrupt the requested EC section body.
523
524        @param section: An EC section, either 'a' or 'b'.
525        """
526        self._ec_handler.corrupt_firmware_body(section, corrupt_all=True)
527
528    @allow_multiple_section_input
529    def _ec_restore_body(self, section):
530        """Restore the previously corrupted EC section body.
531
532        @param section: An EC section, either 'a' or 'b'.
533        """
534        self._ec_handler.restore_firmware_body(section, restore_all=True)
535
536    def _ec_dump_firmware(self, ec_path):
537        """Dump the current EC firmware to a file, specified by ec_path.
538
539        @param ec_path: The path of the EC image to be written.
540        """
541        self._ec_handler.dump_whole(ec_path)
542
543    def _ec_set_write_protect(self, enable):
544        """Enable write protect of the EC flash chip.
545
546        @param enable: True if activating EC write protect. Otherwise, False.
547        """
548        if enable:
549            self._ec_handler.enable_write_protect()
550        else:
551            self._ec_handler.disable_write_protect()
552
553    @allow_multiple_section_input
554    def _kernel_corrupt_sig(self, section):
555        """Corrupt the requested kernel section.
556
557        @param section: A kernel section, either 'a' or 'b'.
558        """
559        self._kernel_handler.corrupt_kernel(section)
560
561    @allow_multiple_section_input
562    def _kernel_restore_sig(self, section):
563        """Restore the requested kernel section (previously corrupted).
564
565        @param section: A kernel section, either 'a' or 'b'.
566        """
567        self._kernel_handler.restore_kernel(section)
568
569    def __kernel_modify_version(self, section, delta):
570        """Modify kernel version for the requested section, by adding delta.
571
572        The passed in delta, a positive or a negative number, is added to the
573        original kernel version.
574        """
575        original_version = self._kernel_handler.get_version(section)
576        new_version = original_version + delta
577        self._os_if.log(
578                'Setting kernel section %s version from %d to %d' % (
579                section, original_version, new_version))
580        self._kernel_handler.set_version(section, new_version)
581
582    @allow_multiple_section_input
583    def _kernel_move_version_backward(self, section):
584        """Decrement kernel version for the requested section."""
585        self.__kernel_modify_version(section, -1)
586
587    @allow_multiple_section_input
588    def _kernel_move_version_forward(self, section):
589        """Increase kernel version for the requested section."""
590        self.__kernel_modify_version(section, 1)
591
592    def _kernel_get_version(self, section):
593        """Return kernel version."""
594        return self._kernel_handler.get_version(section)
595
596    def _kernel_get_datakey_version(self, section):
597        """Return kernel datakey version."""
598        return self._kernel_handler.get_datakey_version(section)
599
600    def _kernel_diff_a_b(self):
601        """Compare kernel A with B.
602
603        @return: True: if kernel A is different with B.
604                 False: if kernel A is the same as B.
605        """
606        rootdev = self._os_if.get_root_dev()
607        kernel_a = self._os_if.join_part(rootdev, '2')
608        kernel_b = self._os_if.join_part(rootdev, '4')
609
610        # The signature (some kind of hash) for the kernel body is stored in
611        # the beginning. So compare the first 64KB (including header, preamble,
612        # and signature) should be enough to check them identical.
613        header_a = self._os_if.read_partition(kernel_a, 0x10000)
614        header_b = self._os_if.read_partition(kernel_b, 0x10000)
615
616        return header_a != header_b
617
618    def _kernel_resign_with_keys(self, section, key_path=None):
619        """Resign kernel with temporary key."""
620        self._kernel_handler.resign_kernel(section, key_path)
621
622    def _kernel_dump(self, section, kernel_path):
623        """Dump the specified kernel to a file.
624
625        @param section: The kernel to dump. May be A or B.
626        @param kernel_path: The path to the kernel image to be written.
627        """
628        self._kernel_handler.dump_kernel(section, kernel_path)
629
630    def _kernel_write(self, section, kernel_path):
631        """Write a kernel image to the specified section.
632
633        @param section: The kernel to dump. May be A or B.
634        @param kernel_path: The path to the kernel image.
635        """
636        self._kernel_handler.write_kernel(section, kernel_path)
637
638    def _kernel_get_sha(self, section):
639        """Return the SHA1 hash of the specified kernel section."""
640        return self._kernel_handler.get_sha(section)
641
642    def _tpm_get_firmware_version(self):
643        """Retrieve tpm firmware body version."""
644        return self._tpm_handler.get_fw_version()
645
646    def _tpm_get_firmware_datakey_version(self):
647        """Retrieve tpm firmware data key version."""
648        return self._tpm_handler.get_fw_key_version()
649
650    def _tpm_get_kernel_version(self):
651        """Retrieve tpm kernel body version."""
652        return self._tpm_handler.get_kernel_version()
653
654    def _tpm_get_kernel_datakey_version(self):
655        """Retrieve tpm kernel data key version."""
656        return self._tpm_handler.get_kernel_key_version()
657
658    def _tpm_stop_daemon(self):
659        """Stop tpm related daemon."""
660        return self._tpm_handler.stop_daemon()
661
662    def _tpm_restart_daemon(self):
663        """Restart tpm related daemon which was stopped by stop_daemon()."""
664        return self._tpm_handler.restart_daemon()
665
666    def _cgpt_get_attributes(self):
667        """Get kernel attributes."""
668        rootdev = self._system_get_root_dev()
669        self._cgpt_handler.read_device_info(rootdev)
670        return {'A': self._cgpt_handler.get_partition(rootdev, 'KERN-A'),
671                'B': self._cgpt_handler.get_partition(rootdev, 'KERN-B')}
672
673    def _cgpt_set_attributes(self, attributes):
674        """Set kernel attributes."""
675        rootdev = self._system_get_root_dev()
676        allowed = ['priority', 'tries', 'successful']
677        for p in ('A', 'B'):
678            if p not in attributes:
679                continue
680            attr = dict()
681            for k in allowed:
682                if k in attributes[p]:
683                    attr[k] = attributes[p][k]
684            if attr:
685                self._cgpt_handler.set_partition(rootdev, 'KERN-%s' % p, attr)
686
687    def _updater_cleanup(self):
688        self._updater.cleanup_temp_dir()
689
690    def _updater_get_fwid(self):
691        """Retrieve shellball's fwid.
692
693        @return: Shellball's fwid.
694        """
695        return self._updater.retrieve_fwid()
696
697    def _updater_resign_firmware(self, version):
698        """Resign firmware with version.
699
700        @param version: new version number.
701        """
702        self._updater.resign_firmware(version)
703
704    def _updater_extract_shellball(self, append=None):
705        """Extract shellball with the given append suffix.
706
707        @param append: use for the shellball name.
708        """
709        self._updater.extract_shellball(append)
710
711    def _updater_repack_shellball(self, append=None):
712        """Repack shellball with new fwid.
713
714        @param append: use for new fwid naming.
715        """
716        self._updater.repack_shellball(append)
717
718    def _updater_run_autoupdate(self, append):
719        """Run chromeos-firmwareupdate with autoupdate mode."""
720        options = ['--noupdate_ec', '--nocheck_rw_compatible']
721        self._updater.run_firmwareupdate(mode='autoupdate',
722                                         updater_append=append,
723                                         options=options)
724
725    def _updater_run_factory_install(self):
726        """Run chromeos-firmwareupdate with factory_install mode."""
727        options = ['--noupdate_ec']
728        self._updater.run_firmwareupdate(mode='factory_install',
729                                         options=options)
730
731    def _updater_run_bootok(self, append):
732        """Run chromeos-firmwareupdate with bootok mode."""
733        self._updater.run_firmwareupdate(mode='bootok',
734                                         updater_append=append)
735
736    def _updater_run_recovery(self):
737        """Run chromeos-firmwareupdate with recovery mode."""
738        options = ['--noupdate_ec', '--nocheck_rw_compatible']
739        self._updater.run_firmwareupdate(mode='recovery',
740                                         options=options)
741
742    def _updater_get_temp_path(self):
743        """Get updater's temp directory path."""
744        return self._updater.get_temp_path()
745
746    def _updater_get_keys_path(self):
747        """Get updater's keys directory path."""
748        return self._updater.get_keys_path()
749
750    def _updater_get_work_path(self):
751        """Get updater's work directory path."""
752        return self._updater.get_work_path()
753
754    def _rootfs_verify_rootfs(self, section):
755        """Verifies the integrity of the root FS.
756
757        @param section: The rootfs to verify. May be A or B.
758        """
759        return self._rootfs_handler.verify_rootfs(section)
760
761    def _system_check_keys(self, expected_sequence):
762        """Check the keys sequence was as expected.
763
764        @param expected_sequence: A list of expected key sequences.
765        """
766        return self._check_keys.check_keys(expected_sequence)
767
768    def cleanup(self):
769        """Cleanup for the RPC server. Currently nothing."""
770        pass
771