1# Copyright 2015 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 5import logging 6import time 7import sys 8 9from multiprocessing import Process 10from autotest_lib.client.bin import utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.cros.faft.utils import shell_wrapper 13 14class ConnectionError(Exception): 15 """Raised on an error of connecting DUT.""" 16 pass 17 18 19class _BaseFwBypasser(object): 20 """Base class that controls bypass logic for firmware screens.""" 21 22 # Duration of holding Volume down button to quickly bypass the developer 23 # warning screen in tablets/detachables. 24 HOLD_VOL_DOWN_BUTTON_BYPASS = 3 25 26 def __init__(self, faft_framework): 27 self.servo = faft_framework.servo 28 self.faft_config = faft_framework.faft_config 29 self.client_host = faft_framework._client 30 31 32 def bypass_dev_mode(self): 33 """Bypass the dev mode firmware logic to boot internal image.""" 34 raise NotImplementedError 35 36 37 def bypass_dev_boot_usb(self): 38 """Bypass the dev mode firmware logic to boot USB.""" 39 raise NotImplementedError 40 41 42 def bypass_rec_mode(self): 43 """Bypass the rec mode firmware logic to boot USB.""" 44 raise NotImplementedError 45 46 47 def trigger_dev_to_rec(self): 48 """Trigger to the rec mode from the dev screen.""" 49 raise NotImplementedError 50 51 52 def trigger_rec_to_dev(self): 53 """Trigger to the dev mode from the rec screen.""" 54 raise NotImplementedError 55 56 57 def trigger_dev_to_normal(self): 58 """Trigger to the normal mode from the dev screen.""" 59 raise NotImplementedError 60 61 62class _CtrlDBypasser(_BaseFwBypasser): 63 """Controls bypass logic via Ctrl-D combo.""" 64 65 def bypass_dev_mode(self): 66 """Bypass the dev mode firmware logic to boot internal image. 67 68 Press Ctrl-D repeatedly. To obtain a low firmware boot time, pressing 69 Ctrl+D for every half second until firmware_screen delay has been 70 reached. 71 """ 72 logging.info("Pressing Ctrl-D.") 73 # At maximum, device waits for twice of firmware_screen delay to 74 # bypass the Dev screen. 75 timeout = time.time() + (self.faft_config.firmware_screen * 2) 76 while time.time() < timeout: 77 self.servo.ctrl_d() 78 time.sleep(0.5) 79 if self.client_host.ping_wait_up(timeout=0.1): 80 break 81 82 83 def bypass_dev_boot_usb(self): 84 """Bypass the dev mode firmware logic to boot USB.""" 85 time.sleep(self.faft_config.firmware_screen) 86 self.servo.ctrl_u() 87 88 89 def bypass_rec_mode(self): 90 """Bypass the rec mode firmware logic to boot USB.""" 91 self.servo.switch_usbkey('host') 92 time.sleep(self.faft_config.usb_plug) 93 self.servo.switch_usbkey('dut') 94 logging.info('Enabled dut_sees_usb') 95 if not self.client_host.ping_wait_up( 96 timeout=self.faft_config.delay_reboot_to_ping): 97 logging.info('ping timed out, try REC_ON') 98 psc = self.servo.get_power_state_controller() 99 psc.power_on(psc.REC_ON) 100 101 102 def trigger_dev_to_rec(self): 103 """Trigger to the rec mode from the dev screen.""" 104 time.sleep(self.faft_config.firmware_screen) 105 106 # Pressing Enter for too long triggers a second key press. 107 # Let's press it without delay 108 self.servo.enter_key(press_secs=0) 109 110 111 def trigger_rec_to_dev(self): 112 """Trigger to the dev mode from the rec screen.""" 113 time.sleep(self.faft_config.firmware_screen) 114 self.servo.ctrl_d() 115 time.sleep(self.faft_config.confirm_screen) 116 if self.faft_config.rec_button_dev_switch: 117 logging.info('RECOVERY button pressed to switch to dev mode') 118 self.servo.toggle_recovery_switch() 119 else: 120 logging.info('ENTER pressed to switch to dev mode') 121 self.servo.enter_key() 122 123 124 def trigger_dev_to_normal(self): 125 """Trigger to the normal mode from the dev screen.""" 126 time.sleep(self.faft_config.firmware_screen) 127 self.servo.enter_key() 128 time.sleep(self.faft_config.confirm_screen) 129 self.servo.enter_key() 130 131 132class _JetstreamBypasser(_BaseFwBypasser): 133 """Controls bypass logic of Jetstream devices.""" 134 135 def bypass_dev_mode(self): 136 """Bypass the dev mode firmware logic to boot internal image.""" 137 # Jetstream does nothing to bypass. 138 pass 139 140 141 def bypass_dev_boot_usb(self): 142 """Bypass the dev mode firmware logic to boot USB.""" 143 self.servo.switch_usbkey('dut') 144 time.sleep(self.faft_config.firmware_screen) 145 self.servo.toggle_development_switch() 146 147 148 def bypass_rec_mode(self): 149 """Bypass the rec mode firmware logic to boot USB.""" 150 self.servo.switch_usbkey('host') 151 time.sleep(self.faft_config.usb_plug) 152 self.servo.switch_usbkey('dut') 153 if not self.client_host.ping_wait_up( 154 timeout=self.faft_config.delay_reboot_to_ping): 155 psc = self.servo.get_power_state_controller() 156 psc.power_on(psc.REC_ON) 157 158 159 def trigger_dev_to_rec(self): 160 """Trigger to the rec mode from the dev screen.""" 161 # Jetstream does not have this triggering logic. 162 raise NotImplementedError 163 164 165 def trigger_rec_to_dev(self): 166 """Trigger to the dev mode from the rec screen.""" 167 self.servo.disable_development_mode() 168 time.sleep(self.faft_config.firmware_screen) 169 self.servo.toggle_development_switch() 170 171 172 def trigger_dev_to_normal(self): 173 """Trigger to the normal mode from the dev screen.""" 174 # Jetstream does not have this triggering logic. 175 raise NotImplementedError 176 177 178class _TabletDetachableBypasser(_BaseFwBypasser): 179 """Controls bypass logic of tablet/ detachable chromebook devices.""" 180 181 def set_button(self, button, duration, info): 182 """Helper method that sets the button hold time for UI selections""" 183 self.servo.set_nocheck(button, duration) 184 time.sleep(self.faft_config.confirm_screen) 185 logging.info(info) 186 187 188 def bypass_dev_boot_usb(self): 189 """Bypass the dev mode firmware logic to boot USB. 190 191 On tablets/ detachables, recovery entered by pressing pwr, vol up 192 & vol down buttons for 10s. 193 Menu options seen in DEVELOPER WARNING screen: 194 Developer Options 195 Show Debug Info 196 Enable Root Verification 197 Power Off* 198 Language 199 Menu options seen in DEV screen: 200 Boot legacy BIOS 201 Boot USB image 202 Boot developer image* 203 Cancel 204 Power off 205 Language 206 Vol up button selects previous item, vol down button selects 207 next item and pwr button selects current activated item. 208 """ 209 self.trigger_dev_screen() 210 time.sleep(self.faft_config.firmware_screen) 211 self.set_button('volume_up_hold', 100, ('Selecting power as' 212 ' enter key to select Boot USB Image')) 213 self.servo.power_short_press() 214 215 216 def bypass_rec_mode(self): 217 """Bypass the rec mode firmware logic to boot USB.""" 218 self.servo.switch_usbkey('host') 219 time.sleep(self.faft_config.usb_plug) 220 self.servo.switch_usbkey('dut') 221 logging.info('Enabled dut_sees_usb') 222 if not self.client_host.ping_wait_up( 223 timeout=self.faft_config.delay_reboot_to_ping): 224 logging.info('ping timed out, try REC_ON') 225 psc = self.servo.get_power_state_controller() 226 psc.power_on(psc.REC_ON) 227 228 229 def bypass_dev_mode(self): 230 """Bypass the developer warning screen immediately to boot into 231 internal disk. 232 233 On tablets/detachables, press & holding the Volume down button for 234 3-seconds will quickly bypass the developer warning screen. 235 """ 236 # Unit for the "volume_down_hold" console command is msec. 237 duration = (self.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1) * 1000 238 logging.info("Press and hold volume down button for %.1f seconds to " 239 "immediately bypass the Developer warning screen.", 240 self.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1) 241 # At maximum, device waits for twice of firmware_screen delay to 242 # bypass the Dev screen. 243 timeout = time.time() + (self.faft_config.firmware_screen * 2) 244 # To obtain a low firmware boot time, volume_down button pressed for 245 # every 3.1 seconds until firmware_screen delay has been reached. 246 while time.time() < timeout: 247 self.servo.set_nocheck('volume_down_hold', duration) 248 # After pressing 'volume_down_hold' button, wait for 0.2 seconds 249 # before start pressing the button for next iteration. 250 time.sleep(self.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.2) 251 if self.client_host.ping_wait_up(timeout=0.1): 252 break 253 254 255 def trigger_dev_screen(self): 256 """Helper method that transitions from DEVELOPER WARNING to DEV screen 257 258 Menu options seen in DEVELOPER WARNING screen: 259 Developer Options 260 Show Debug Info 261 Enable Root Verification 262 Power Off* 263 Language 264 Menu options seen in DEV screen: 265 Boot legacy BIOS 266 Boot USB image 267 Boot developer image* 268 Cancel 269 Power off 270 Language 271 Vol up button selects previous item, vol down button selects 272 next item and pwr button selects current activated item. 273 """ 274 time.sleep(self.faft_config.firmware_screen) 275 self.servo.set_nocheck('volume_up_hold', 100) 276 time.sleep(self.faft_config.confirm_screen) 277 self.servo.set_nocheck('volume_up_hold', 100) 278 time.sleep(self.faft_config.confirm_screen) 279 self.set_button('volume_up_hold', 100, ('Selecting power ' 280 'as enter key to select Developer Options')) 281 self.servo.power_short_press() 282 283 284 def trigger_rec_to_dev(self): 285 """Trigger to the dev mode from the rec screen using vol up button. 286 287 On tablets/ detachables, recovery entered by pressing pwr, vol up 288 & vol down buttons for 10s. TO_DEV screen is entered by pressing 289 vol up & vol down buttons together on the INSERT screen. 290 Menu options seen in TO_DEV screen: 291 Confirm enabling developer mode 292 Cancel* 293 Power off 294 Language 295 Vol up button selects previous item, vol down button selects 296 next item and pwr button selects current activated item. 297 """ 298 time.sleep(self.faft_config.firmware_screen) 299 self.set_button('volume_up_down_hold', 100, ('Enter Recovery Menu.')) 300 time.sleep(self.faft_config.confirm_screen) 301 self.set_button('volume_up_hold', 100, ('Selecting power as ' 302 'enter key to select Confirm Enabling Developer Mode')) 303 self.servo.power_short_press() 304 time.sleep(self.faft_config.firmware_screen) 305 306 307 def trigger_dev_to_normal(self): 308 """Trigger to the normal mode from the dev screen. 309 310 Menu options seen in DEVELOPER WARNING screen: 311 Developer Options 312 Show Debug Info 313 Enable Root Verification 314 Power Off* 315 Language 316 Menu options seen in TO_NORM screen: 317 Confirm Enabling Verified Boot* 318 Cancel 319 Power off 320 Language 321 Vol up button selects previous item, vol down button selects 322 next item and pwr button selects current activated item. 323 """ 324 time.sleep(self.faft_config.firmware_screen) 325 self.set_button('volume_up_hold', 100, ('Selecting ' 326 'Enable Root Verification using pwr ' 327 'button to enter TO_NORM screen')) 328 self.servo.power_short_press() 329 logging.info('Transitioning from DEV to TO_NORM screen.') 330 time.sleep(self.faft_config.firmware_screen) 331 logging.info('Selecting Confirm Enabling Verified ' 332 'Boot using pwr button in ' 333 'TO_NORM screen') 334 self.servo.power_short_press() 335 336 def trigger_dev_to_rec(self): 337 """Trigger to the TO_NORM screen from the dev screen. 338 Menu options seen in DEVELOPER WARNING screen: 339 Developer Options 340 Show Debug Info 341 Enable Root Verification 342 Power Off* 343 Language 344 Menu options seen in TO_NORM screen: 345 Confirm Enabling Verified Boot* 346 Cancel 347 Power off 348 Language 349 Vol up button selects previous item, vol down button selects 350 next item and pwr button selects current activated item. 351 """ 352 time.sleep(self.faft_config.firmware_screen) 353 self.set_button('volume_up_hold', 100, ('Selecting ' 354 'Enable Root Verification using pwr ' 355 'button to enter TO_NORM screen')) 356 self.servo.power_short_press() 357 logging.info('Transitioning from DEV to TO_NORM screen.') 358 time.sleep(self.faft_config.firmware_screen) 359 360 # In firmware_FwScreenPressPower, test will power off the DUT using 361 # Power button in second screen (TO_NORM screen) so scrolling to 362 # Power-off is necessary in this case. Hence scroll to Power-off as 363 # a generic action and wait for next action of either Lid close or 364 # power button press. 365 self.servo.set_nocheck('volume_down_hold', 100) 366 time.sleep(self.faft_config.confirm_screen) 367 self.servo.set_nocheck('volume_down_hold', 100) 368 time.sleep(self.faft_config.confirm_screen) 369 370def _create_fw_bypasser(faft_framework): 371 """Creates a proper firmware bypasser. 372 373 @param faft_framework: The main FAFT framework object. 374 """ 375 bypasser_type = faft_framework.faft_config.fw_bypasser_type 376 if bypasser_type == 'ctrl_d_bypasser': 377 logging.info('Create a CtrlDBypasser') 378 return _CtrlDBypasser(faft_framework) 379 elif bypasser_type == 'jetstream_bypasser': 380 logging.info('Create a JetstreamBypasser') 381 return _JetstreamBypasser(faft_framework) 382 elif bypasser_type == 'ryu_bypasser': 383 # FIXME Create an RyuBypasser 384 logging.info('Create a CtrlDBypasser') 385 return _CtrlDBypasser(faft_framework) 386 elif bypasser_type == 'tablet_detachable_bypasser': 387 logging.info('Create a TabletDetachableBypasser') 388 return _TabletDetachableBypasser(faft_framework) 389 else: 390 raise NotImplementedError('Not supported fw_bypasser_type: %s', 391 bypasser_type) 392 393 394class _BaseModeSwitcher(object): 395 """Base class that controls firmware mode switching.""" 396 397 HOLD_VOL_DOWN_BUTTON_BYPASS = _BaseFwBypasser.HOLD_VOL_DOWN_BUTTON_BYPASS 398 399 def __init__(self, faft_framework): 400 self.faft_framework = faft_framework 401 self.client_host = faft_framework._client 402 self.faft_client = faft_framework.faft_client 403 self.servo = faft_framework.servo 404 self.faft_config = faft_framework.faft_config 405 self.checkers = faft_framework.checkers 406 self.bypasser = _create_fw_bypasser(faft_framework) 407 self._backup_mode = None 408 409 410 def setup_mode(self, mode): 411 """Setup for the requested mode. 412 413 It makes sure the system in the requested mode. If not, it tries to 414 do so. 415 416 @param mode: A string of mode, one of 'normal', 'dev', or 'rec'. 417 @raise TestFail: If the system not switched to expected mode after 418 reboot_to_mode. 419 420 """ 421 if not self.checkers.mode_checker(mode): 422 logging.info('System not in expected %s mode. Reboot into it.', 423 mode) 424 if self._backup_mode is None: 425 # Only resume to normal/dev mode after test, not recovery. 426 self._backup_mode = 'dev' if mode == 'normal' else 'normal' 427 self.reboot_to_mode(mode) 428 if not self.checkers.mode_checker(mode): 429 raise error.TestFail('System not switched to expected %s' 430 ' mode after setup_mode.' % mode) 431 432 def restore_mode(self): 433 """Restores original dev mode status if it has changed. 434 435 @raise TestFail: If the system not restored to expected mode. 436 """ 437 if (self._backup_mode is not None and 438 not self.checkers.mode_checker(self._backup_mode)): 439 self.reboot_to_mode(self._backup_mode) 440 if not self.checkers.mode_checker(self._backup_mode): 441 raise error.TestFail('System not restored to expected %s' 442 ' mode in cleanup.' % self._backup_mode) 443 444 445 446 def reboot_to_mode(self, to_mode, from_mode=None, sync_before_boot=True, 447 wait_for_dut_up=True): 448 """Reboot and execute the mode switching sequence. 449 450 @param to_mode: The target mode, one of 'normal', 'dev', or 'rec'. 451 @param from_mode: The original mode, optional, one of 'normal, 'dev', 452 or 'rec'. 453 @param sync_before_boot: True to sync to disk before booting. 454 @param wait_for_dut_up: True to wait DUT online again. False to do the 455 reboot and mode switching sequence only and may 456 need more operations to pass the firmware 457 screen. 458 """ 459 logging.info('-[ModeSwitcher]-[ start reboot_to_mode(%r, %r, %r) ]-', 460 to_mode, from_mode, wait_for_dut_up) 461 if sync_before_boot: 462 self.faft_framework.blocking_sync() 463 if to_mode == 'rec': 464 self._enable_rec_mode_and_reboot(usb_state='dut') 465 if wait_for_dut_up: 466 self.wait_for_client() 467 468 elif to_mode == 'rec_force_mrc': 469 self._enable_rec_mode_force_mrc_and_reboot(usb_state='dut') 470 if wait_for_dut_up: 471 self.wait_for_client() 472 473 elif to_mode == 'dev': 474 self._enable_dev_mode_and_reboot() 475 if wait_for_dut_up: 476 self.bypass_dev_mode() 477 self.wait_for_client() 478 479 elif to_mode == 'normal': 480 self._enable_normal_mode_and_reboot() 481 if wait_for_dut_up: 482 self.wait_for_client() 483 484 else: 485 raise NotImplementedError( 486 'Not supported mode switching from %s to %s' % 487 (str(from_mode), to_mode)) 488 logging.info('-[ModeSwitcher]-[ end reboot_to_mode(%r, %r, %r) ]-', 489 to_mode, from_mode, wait_for_dut_up) 490 491 def simple_reboot(self, reboot_type='warm', sync_before_boot=True): 492 """Simple reboot method 493 494 Just reboot the DUT using either cold or warm reset. Does not wait for 495 DUT to come back online. Will wait for test to handle this. 496 497 @param reboot_type: A string of reboot type, 'warm' or 'cold'. 498 If reboot_type != warm/cold, raise exception. 499 @param sync_before_boot: True to sync to disk before booting. 500 If sync_before_boot=False, DUT offline before 501 calling mode_aware_reboot. 502 """ 503 if reboot_type == 'warm': 504 reboot_method = self.servo.get_power_state_controller().warm_reset 505 elif reboot_type == 'cold': 506 reboot_method = self.servo.get_power_state_controller().reset 507 else: 508 raise NotImplementedError('Not supported reboot_type: %s', 509 reboot_type) 510 if sync_before_boot: 511 boot_id = self.faft_framework.get_bootid() 512 self.faft_framework.blocking_sync() 513 logging.info("-[ModeSwitcher]-[ start simple_reboot(%r) ]-", 514 reboot_type) 515 reboot_method() 516 if sync_before_boot: 517 self.wait_for_client_offline(orig_boot_id=boot_id) 518 logging.info("-[ModeSwitcher]-[ end simple_reboot(%r) ]-", 519 reboot_type) 520 521 def mode_aware_reboot(self, reboot_type=None, reboot_method=None, 522 sync_before_boot=True, wait_for_dut_up=True): 523 """Uses a mode-aware way to reboot DUT. 524 525 For example, if DUT is in dev mode, it requires pressing Ctrl-D to 526 bypass the developer screen. 527 528 @param reboot_type: A string of reboot type, one of 'warm', 'cold', or 529 'custom'. Default is a warm reboot. 530 @param reboot_method: A custom method to do the reboot. Only use it if 531 reboot_type='custom'. 532 @param sync_before_boot: True to sync to disk before booting. 533 If sync_before_boot=False, DUT offline before 534 calling mode_aware_reboot. 535 @param wait_for_dut_up: True to wait DUT online again. False to do the 536 reboot only. 537 """ 538 if reboot_type is None or reboot_type == 'warm': 539 reboot_method = self.servo.get_power_state_controller().warm_reset 540 elif reboot_type == 'cold': 541 reboot_method = self.servo.get_power_state_controller().reset 542 elif reboot_type != 'custom': 543 raise NotImplementedError('Not supported reboot_type: %s', 544 reboot_type) 545 546 logging.info("-[ModeSwitcher]-[ start mode_aware_reboot(%r, %s, ..) ]-", 547 reboot_type, reboot_method.__name__) 548 is_dev = is_rec = is_devsw_boot = False 549 if sync_before_boot: 550 is_dev = self.checkers.mode_checker('dev') 551 is_rec = self.checkers.mode_checker('rec') 552 is_devsw_boot = self.checkers.crossystem_checker( 553 {'devsw_boot': '1'}, True) 554 boot_id = self.faft_framework.get_bootid() 555 self.faft_framework.blocking_sync() 556 if is_rec: 557 logging.info("-[mode_aware_reboot]-[ is_rec=%s is_dev_switch=%s ]-", 558 is_rec, is_devsw_boot) 559 else: 560 logging.info("-[mode_aware_reboot]-[ is_dev=%s ]-", is_dev) 561 reboot_method() 562 if sync_before_boot: 563 self.wait_for_client_offline(orig_boot_id=boot_id) 564 # Encapsulating the behavior of skipping dev firmware screen, 565 # hitting ctrl-D 566 # Note that if booting from recovery mode, we can predict the next 567 # boot based on the developer switch position at boot (devsw_boot). 568 # If devsw_boot is True, we will call bypass_dev_mode after reboot. 569 if is_dev or is_devsw_boot: 570 self.bypass_dev_mode() 571 if wait_for_dut_up: 572 self.wait_for_client() 573 logging.info("-[ModeSwitcher]-[ end mode_aware_reboot(%r, %s, ..) ]-", 574 reboot_type, reboot_method.__name__) 575 576 577 def _enable_rec_mode_and_reboot(self, usb_state=None): 578 """Switch to rec mode and reboot. 579 580 This method emulates the behavior of the old physical recovery switch, 581 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled 582 recovery mode, i.e. just press Power + Esc + Refresh. 583 584 @param usb_state: A string, one of 'dut', 'host', or 'off'. 585 """ 586 psc = self.servo.get_power_state_controller() 587 psc.power_off() 588 if usb_state: 589 self.servo.switch_usbkey(usb_state) 590 psc.power_on(psc.REC_ON) 591 592 593 def _enable_rec_mode_force_mrc_and_reboot(self, usb_state=None): 594 """Switch to rec mode, enable force mrc cache retraining, and reboot. 595 596 This method emulates the behavior of the old physical recovery switch, 597 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled 598 recovery mode, i.e. just press Power + Esc + Refresh. 599 600 @param usb_state: A string, one of 'dut', 'host', or 'off'. 601 """ 602 psc = self.servo.get_power_state_controller() 603 psc.power_off() 604 if usb_state: 605 self.servo.switch_usbkey(usb_state) 606 psc.power_on(psc.REC_ON_FORCE_MRC) 607 608 def _disable_rec_mode_and_reboot(self, usb_state=None): 609 """Disable the rec mode and reboot. 610 611 It is achieved by calling power state controller to do a normal 612 power on. 613 """ 614 psc = self.servo.get_power_state_controller() 615 psc.power_off() 616 time.sleep(self.faft_config.ec_boot_to_pwr_button) 617 psc.power_on(psc.REC_OFF) 618 619 620 def _enable_dev_mode_and_reboot(self): 621 """Switch to developer mode and reboot.""" 622 raise NotImplementedError 623 624 625 def _enable_normal_mode_and_reboot(self): 626 """Switch to normal mode and reboot.""" 627 raise NotImplementedError 628 629 630 # Redirects the following methods to FwBypasser 631 def bypass_dev_mode(self): 632 """Bypass the dev mode firmware logic to boot internal image.""" 633 logging.info("-[bypass_dev_mode]-") 634 self.bypasser.bypass_dev_mode() 635 636 637 def bypass_dev_boot_usb(self): 638 """Bypass the dev mode firmware logic to boot USB.""" 639 logging.info("-[bypass_dev_boot_usb]-") 640 self.bypasser.bypass_dev_boot_usb() 641 642 643 def bypass_rec_mode(self): 644 """Bypass the rec mode firmware logic to boot USB.""" 645 logging.info("-[bypass_rec_mode]-") 646 self.bypasser.bypass_rec_mode() 647 648 649 def trigger_dev_to_rec(self): 650 """Trigger to the rec mode from the dev screen.""" 651 self.bypasser.trigger_dev_to_rec() 652 653 654 def trigger_rec_to_dev(self): 655 """Trigger to the dev mode from the rec screen.""" 656 self.bypasser.trigger_rec_to_dev() 657 658 659 def trigger_dev_to_normal(self): 660 """Trigger to the normal mode from the dev screen.""" 661 self.bypasser.trigger_dev_to_normal() 662 663 664 def wait_for_client(self, timeout=180): 665 """Wait for the client to come back online. 666 667 New remote processes will be launched if their used flags are enabled. 668 669 @param timeout: Time in seconds to wait for the client SSH daemon to 670 come up. 671 @raise ConnectionError: Failed to connect DUT. 672 """ 673 logging.info("-[FAFT]-[ start wait_for_client ]---") 674 # Wait for the system to respond to ping before attempting ssh 675 if not self.client_host.ping_wait_up(timeout): 676 logging.warning("-[FAFT]-[ system did not respond to ping ]") 677 if self.client_host.wait_up(timeout): 678 # Check the FAFT client is avaiable. 679 self.faft_client.system.is_available() 680 # Stop update-engine as it may change firmware/kernel. 681 self.faft_framework.faft_client.updater.stop_daemon() 682 else: 683 logging.error('wait_for_client() timed out.') 684 raise ConnectionError('DUT is still down unexpectedly') 685 logging.info("-[FAFT]-[ end wait_for_client ]-----") 686 687 688 def wait_for_client_offline(self, timeout=60, orig_boot_id=None): 689 """Wait for the client to come offline. 690 691 @param timeout: Time in seconds to wait the client to come offline. 692 @param orig_boot_id: A string containing the original boot id. 693 @raise ConnectionError: Failed to wait DUT offline. 694 """ 695 # When running against panther, we see that sometimes 696 # ping_wait_down() does not work correctly. There needs to 697 # be some investigation to the root cause. 698 # If we sleep for 120s before running get_boot_id(), it 699 # does succeed. But if we change this to ping_wait_down() 700 # there are implications on the wait time when running 701 # commands at the fw screens. 702 if not self.client_host.ping_wait_down(timeout): 703 if orig_boot_id and self.client_host.get_boot_id() != orig_boot_id: 704 logging.warn('Reboot done very quickly.') 705 return 706 raise ConnectionError('DUT is still up unexpectedly') 707 708 709class _KeyboardDevSwitcher(_BaseModeSwitcher): 710 """Class that switches firmware mode via keyboard combo.""" 711 712 def _enable_dev_mode_and_reboot(self): 713 """Switch to developer mode and reboot.""" 714 logging.info("Enabling keyboard controlled developer mode") 715 # Rebooting EC with rec mode on. Should power on AP. 716 # Plug out USB disk for preventing recovery boot without warning 717 self._enable_rec_mode_and_reboot(usb_state='host') 718 self.wait_for_client_offline() 719 self.bypasser.trigger_rec_to_dev() 720 721 722 def _enable_normal_mode_and_reboot(self): 723 """Switch to normal mode and reboot.""" 724 logging.info("Disabling keyboard controlled developer mode") 725 self._disable_rec_mode_and_reboot() 726 self.wait_for_client_offline() 727 self.bypasser.trigger_dev_to_normal() 728 729 730class _JetstreamSwitcher(_BaseModeSwitcher): 731 """Class that switches firmware mode in Jetstream devices.""" 732 733 def _enable_dev_mode_and_reboot(self): 734 """Switch to developer mode and reboot.""" 735 logging.info("Enabling Jetstream developer mode") 736 self._enable_rec_mode_and_reboot(usb_state='host') 737 self.wait_for_client_offline() 738 self.bypasser.trigger_rec_to_dev() 739 740 741 def _enable_normal_mode_and_reboot(self): 742 """Switch to normal mode and reboot.""" 743 logging.info("Disabling Jetstream developer mode") 744 self.servo.disable_development_mode() 745 self._enable_rec_mode_and_reboot(usb_state='host') 746 time.sleep(self.faft_config.firmware_screen) 747 self._disable_rec_mode_and_reboot(usb_state='host') 748 749 750class _TabletDetachableSwitcher(_BaseModeSwitcher): 751 """Class that switches fw mode in tablets/detachables with fw menu UI.""" 752 753 def _enable_dev_mode_and_reboot(self): 754 """Switch to developer mode and reboot. 755 756 On tablets/ detachables, recovery entered by pressing pwr, vol up 757 & vol down buttons for 10s. 758 Menu options seen in RECOVERY screen: 759 Enable Developer Mode 760 Show Debug Info 761 Power off* 762 Language 763 """ 764 logging.info('Enabling tablets/detachable recovery mode') 765 self._enable_rec_mode_and_reboot(usb_state='host') 766 self.wait_for_client_offline() 767 self.bypasser.trigger_rec_to_dev() 768 769 770 def _enable_normal_mode_and_reboot(self): 771 """Switch to normal mode and reboot. 772 773 Menu options seen in DEVELOPER WARNING screen: 774 Developer Options 775 Show Debug Info 776 Enable Root Verification 777 Power Off* 778 Language 779 Menu options seen in TO_NORM screen: 780 Confirm Enabling Verified Boot 781 Cancel 782 Power off* 783 Language 784 Vol up button selects previous item, vol down button selects 785 next item and pwr button selects current activated item. 786 """ 787 self._disable_rec_mode_and_reboot() 788 self.wait_for_client_offline() 789 self.bypasser.trigger_dev_to_normal() 790 791 792class _RyuSwitcher(_BaseModeSwitcher): 793 """Class that switches firmware mode via physical button.""" 794 795 FASTBOOT_OEM_DELAY = 10 796 RECOVERY_TIMEOUT = 2400 797 RECOVERY_SETUP = 60 798 ANDROID_BOOTUP = 600 799 FWTOOL_STARTUP_DELAY = 30 800 801 def wait_for_client(self, timeout=180): 802 """Wait for the client to come back online. 803 804 New remote processes will be launched if their used flags are enabled. 805 806 @param timeout: Time in seconds to wait for the client SSH daemon to 807 come up. 808 @raise ConnectionError: Failed to connect DUT. 809 """ 810 if not self.faft_client.system.wait_for_client(timeout): 811 raise ConnectionError('DUT is still down unexpectedly') 812 813 # there's a conflict between fwtool and crossystem trying to access 814 # the nvram after the OS boots up. Temporarily put a hard wait of 815 # 30 seconds to try to wait for fwtool to finish up. 816 time.sleep(self.FWTOOL_STARTUP_DELAY) 817 818 819 def wait_for_client_offline(self, timeout=60, orig_boot_id=None): 820 """Wait for the client to come offline. 821 822 @param timeout: Time in seconds to wait the client to come offline. 823 @param orig_boot_id: A string containing the original boot id. 824 @raise ConnectionError: Failed to wait DUT offline. 825 """ 826 # TODO: Add a way to check orig_boot_id 827 if not self.faft_client.system.wait_for_client_offline(timeout): 828 raise ConnectionError('DUT is still up unexpectedly') 829 830 def print_recovery_warning(self): 831 """Print recovery warning""" 832 logging.info("***") 833 logging.info("*** Entering recovery mode. This may take awhile ***") 834 logging.info("***") 835 # wait a minute for DUT to get settled into wipe stage 836 time.sleep(self.RECOVERY_SETUP) 837 838 def is_fastboot_mode(self): 839 """Return True if DUT in fastboot mode, False otherwise""" 840 result = self.faft_client.host.run_shell_command_get_output( 841 'fastboot devices') 842 if not result: 843 return False 844 else: 845 return True 846 847 def wait_for_client_fastboot(self, timeout=30): 848 """Wait for the client to come online in fastboot mode 849 850 @param timeout: Time in seconds to wait the client 851 @raise ConnectionError: Failed to wait DUT offline. 852 """ 853 utils.wait_for_value(self.is_fastboot_mode, True, timeout_sec=timeout) 854 855 def _run_cmd(self, args): 856 """Wrapper for run_shell_command 857 858 For Process creation 859 """ 860 return self.faft_client.host.run_shell_command(args) 861 862 def _enable_dev_mode_and_reboot(self): 863 """Switch to developer mode and reboot.""" 864 logging.info("Entering RyuSwitcher: _enable_dev_mode_and_reboot") 865 try: 866 self.faft_client.system.run_shell_command('reboot bootloader') 867 self.wait_for_client_fastboot() 868 869 process = Process( 870 target=self._run_cmd, 871 args=('fastboot oem unlock',)) 872 process.start() 873 874 # need a slight delay to give the ap time to get into valid state 875 time.sleep(self.FASTBOOT_OEM_DELAY) 876 self.servo.power_key(self.faft_config.hold_pwr_button_poweron) 877 process.join() 878 879 self.print_recovery_warning() 880 self.wait_for_client_fastboot(self.RECOVERY_TIMEOUT) 881 self.faft_client.host.run_shell_command('fastboot continue') 882 self.wait_for_client(self.ANDROID_BOOTUP) 883 884 # need to reset DUT into clean state 885 except shell_wrapper.ShellError: 886 raise error.TestError('Error executing shell command') 887 except ConnectionError: 888 raise error.TestError('Timed out waiting for DUT to exit recovery') 889 except: 890 raise error.TestError('Unexpected Exception: %s' % sys.exc_info()[0]) 891 logging.info("Exiting RyuSwitcher: _enable_dev_mode_and_reboot") 892 893 def _enable_normal_mode_and_reboot(self): 894 """Switch to normal mode and reboot.""" 895 try: 896 self.faft_client.system.run_shell_command('reboot bootloader') 897 self.wait_for_client_fastboot() 898 899 process = Process( 900 target=self._run_cmd, 901 args=('fastboot oem lock',)) 902 process.start() 903 904 # need a slight delay to give the ap time to get into valid state 905 time.sleep(self.FASTBOOT_OEM_DELAY) 906 self.servo.power_key(self.faft_config.hold_pwr_button_poweron) 907 process.join() 908 909 self.print_recovery_warning() 910 self.wait_for_client_fastboot(self.RECOVERY_TIMEOUT) 911 self.faft_client.host.run_shell_command('fastboot continue') 912 self.wait_for_client(self.ANDROID_BOOTUP) 913 914 # need to reset DUT into clean state 915 except shell_wrapper.ShellError: 916 raise error.TestError('Error executing shell command') 917 except ConnectionError: 918 raise error.TestError('Timed out waiting for DUT to exit recovery') 919 except: 920 raise error.TestError('Unexpected Exception: %s' % sys.exc_info()[0]) 921 logging.info("Exiting RyuSwitcher: _enable_normal_mode_and_reboot") 922 923def create_mode_switcher(faft_framework): 924 """Creates a proper mode switcher. 925 926 @param faft_framework: The main FAFT framework object. 927 """ 928 switcher_type = faft_framework.faft_config.mode_switcher_type 929 if switcher_type == 'keyboard_dev_switcher': 930 logging.info('Create a KeyboardDevSwitcher') 931 return _KeyboardDevSwitcher(faft_framework) 932 elif switcher_type == 'jetstream_switcher': 933 logging.info('Create a JetstreamSwitcher') 934 return _JetstreamSwitcher(faft_framework) 935 elif switcher_type == 'ryu_switcher': 936 logging.info('Create a RyuSwitcher') 937 return _RyuSwitcher(faft_framework) 938 elif switcher_type == 'tablet_detachable_switcher': 939 logging.info('Create a TabletDetachableSwitcher') 940 return _TabletDetachableSwitcher(faft_framework) 941 else: 942 raise NotImplementedError('Not supported mode_switcher_type: %s', 943 switcher_type) 944