xref: /aosp_15_r20/external/autotest/server/cros/servo/firmware_programmer.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Li"""A utility to program ChromeOS devices' firmware using servo.
6*9c5db199SXin Li
7*9c5db199SXin LiThis utility expects the DUT to be connected to a servo device. This allows us
8*9c5db199SXin Lito put the DUT into the required state and to actually program the DUT's
9*9c5db199SXin Lifirmware using FTDI, USB and/or serial interfaces provided by servo.
10*9c5db199SXin Li
11*9c5db199SXin LiServo state is preserved across the programming process.
12*9c5db199SXin Li"""
13*9c5db199SXin Li
14*9c5db199SXin Liimport glob
15*9c5db199SXin Liimport logging
16*9c5db199SXin Liimport os
17*9c5db199SXin Liimport re
18*9c5db199SXin Liimport site
19*9c5db199SXin Liimport time
20*9c5db199SXin Liimport xml.etree.ElementTree
21*9c5db199SXin Li
22*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
23*9c5db199SXin Lifrom autotest_lib.server.cros.faft.utils.config import Config as FAFTConfig
24*9c5db199SXin Li
25*9c5db199SXin Li
26*9c5db199SXin Li# Number of seconds for program EC/BIOS to time out.
27*9c5db199SXin LiFIRMWARE_PROGRAM_TIMEOUT_SEC = 1800
28*9c5db199SXin Li
29*9c5db199SXin Liclass ProgrammerError(Exception):
30*9c5db199SXin Li    """Local exception class wrapper."""
31*9c5db199SXin Li    pass
32*9c5db199SXin Li
33*9c5db199SXin Li
34*9c5db199SXin Liclass _BaseProgrammer(object):
35*9c5db199SXin Li    """Class implementing base programmer services.
36*9c5db199SXin Li
37*9c5db199SXin Li    Private attributes:
38*9c5db199SXin Li      _servo: a servo object controlling the servo device
39*9c5db199SXin Li      _servo_host: a host object running commands like 'flashrom'
40*9c5db199SXin Li      _servo_prog_state: a tuple of strings of "<control>:<value>" pairs,
41*9c5db199SXin Li                         listing servo controls and their required values for
42*9c5db199SXin Li                         programming
43*9c5db199SXin Li      _servo_prog_state_delay: time in second to wait after changing servo
44*9c5db199SXin Li                               controls for programming.
45*9c5db199SXin Li      _servo_saved_state: a list of the same elements as _servo_prog_state,
46*9c5db199SXin Li                          those which need to be restored after programming
47*9c5db199SXin Li      _program_cmd: a string, the shell command to run on the servo host
48*9c5db199SXin Li                    to actually program the firmware. Dependent on
49*9c5db199SXin Li                    firmware/hardware type, set by subclasses.
50*9c5db199SXin Li    """
51*9c5db199SXin Li
52*9c5db199SXin Li    def __init__(self, servo, req_list, servo_host=None):
53*9c5db199SXin Li        """Base constructor.
54*9c5db199SXin Li        @param servo: a servo object controlling the servo device
55*9c5db199SXin Li        @param req_list: a list of strings, names of the utilities required
56*9c5db199SXin Li                         to be in the path for the programmer to succeed
57*9c5db199SXin Li        @param servo_host: a host object to execute commands. Default to None,
58*9c5db199SXin Li                           using the host object from the above servo object
59*9c5db199SXin Li        """
60*9c5db199SXin Li        self._servo = servo
61*9c5db199SXin Li        self._servo_prog_state = ()
62*9c5db199SXin Li        self._servo_prog_state_delay = 0
63*9c5db199SXin Li        self._servo_saved_state = []
64*9c5db199SXin Li        self._program_cmd = ''
65*9c5db199SXin Li        self._servo_host = servo_host
66*9c5db199SXin Li        if self._servo_host is None:
67*9c5db199SXin Li            self._servo_host = self._servo._servo_host
68*9c5db199SXin Li
69*9c5db199SXin Li        try:
70*9c5db199SXin Li            self._servo_host.run('which %s' % ' '.join(req_list))
71*9c5db199SXin Li        except error.AutoservRunError:
72*9c5db199SXin Li            # TODO: We turn this exception into a warn since the fw programmer
73*9c5db199SXin Li            # is not working right now, and some systems do not package the
74*9c5db199SXin Li            # required utilities its checking for.
75*9c5db199SXin Li            # We should reinstate this exception once the programmer is working
76*9c5db199SXin Li            # to indicate the missing utilities earlier in the test cycle.
77*9c5db199SXin Li            # Bug chromium:371011 filed to track this.
78*9c5db199SXin Li            logging.warning("Ignoring exception when verify required bins : %s",
79*9c5db199SXin Li                         ' '.join(req_list))
80*9c5db199SXin Li
81*9c5db199SXin Li
82*9c5db199SXin Li    def _set_servo_state(self):
83*9c5db199SXin Li        """Set servo for programming, while saving the current state."""
84*9c5db199SXin Li        logging.debug("Setting servo state for programming")
85*9c5db199SXin Li        for item in self._servo_prog_state:
86*9c5db199SXin Li            key, value = item.split(':')
87*9c5db199SXin Li            try:
88*9c5db199SXin Li                present = self._servo.get(key)
89*9c5db199SXin Li            except error.TestFail:
90*9c5db199SXin Li                logging.warning('Missing servo control: %s', key)
91*9c5db199SXin Li                continue
92*9c5db199SXin Li            if present == 'not_applicable':
93*9c5db199SXin Li                # control is has no bearing in this servo config so ignore it.
94*9c5db199SXin Li                logging.debug('Servo control %s is NA .. skipping', key)
95*9c5db199SXin Li                continue
96*9c5db199SXin Li            if present != value:
97*9c5db199SXin Li                self._servo_saved_state.append('%s:%s' % (key, present))
98*9c5db199SXin Li            self._servo.set(key, value)
99*9c5db199SXin Li        time.sleep(self._servo_prog_state_delay)
100*9c5db199SXin Li
101*9c5db199SXin Li
102*9c5db199SXin Li    def _restore_servo_state(self):
103*9c5db199SXin Li        """Restore previously saved servo state."""
104*9c5db199SXin Li        logging.debug("Restoring servo state after programming")
105*9c5db199SXin Li        self._servo_saved_state.reverse()  # Do it in the reverse order.
106*9c5db199SXin Li        for item in self._servo_saved_state:
107*9c5db199SXin Li            key, value = item.split(':')
108*9c5db199SXin Li            self._servo.set(key, value)
109*9c5db199SXin Li
110*9c5db199SXin Li
111*9c5db199SXin Li    def program(self):
112*9c5db199SXin Li        """Program the firmware as configured by a subclass."""
113*9c5db199SXin Li        self._set_servo_state()
114*9c5db199SXin Li        try:
115*9c5db199SXin Li            logging.debug("Programmer command: %s", self._program_cmd)
116*9c5db199SXin Li            self._servo_host.run(self._program_cmd,
117*9c5db199SXin Li                                 timeout=FIRMWARE_PROGRAM_TIMEOUT_SEC)
118*9c5db199SXin Li        finally:
119*9c5db199SXin Li            self._restore_servo_state()
120*9c5db199SXin Li
121*9c5db199SXin Li
122*9c5db199SXin Liclass FlashromProgrammer(_BaseProgrammer):
123*9c5db199SXin Li    """Class for programming AP flashrom."""
124*9c5db199SXin Li
125*9c5db199SXin Li    def __init__(self, servo, keep_ro=False):
126*9c5db199SXin Li        """Configure required servo state.
127*9c5db199SXin Li
128*9c5db199SXin Li        @param servo: a servo object controlling the servo device
129*9c5db199SXin Li        @param keep_ro: True to keep the RO portion unchanged
130*9c5db199SXin Li        """
131*9c5db199SXin Li        super(FlashromProgrammer, self).__init__(servo, ['flashrom',])
132*9c5db199SXin Li        self._keep_ro = keep_ro
133*9c5db199SXin Li        self._fw_path = None
134*9c5db199SXin Li        self.init_section_paths('/tmp')
135*9c5db199SXin Li        self._servo_version = self._servo.get_servo_version(active=True)
136*9c5db199SXin Li        self._servo_serials = self._servo._server.get_servo_serials()
137*9c5db199SXin Li
138*9c5db199SXin Li
139*9c5db199SXin Li    def init_section_paths(self, tmp_path):
140*9c5db199SXin Li        """Update section paths to use the tmp directory"""
141*9c5db199SXin Li        self._tmp_path = tmp_path
142*9c5db199SXin Li        self._fw_main = os.path.join(self._tmp_path, 'fw_main')
143*9c5db199SXin Li        self._wp_ro = os.path.join(self._tmp_path, 'wp_ro')
144*9c5db199SXin Li        self._ro_vpd = os.path.join(self._tmp_path, 'ro_vpd')
145*9c5db199SXin Li        self._rw_vpd = os.path.join(self._tmp_path, 'rw_vpd')
146*9c5db199SXin Li        self._gbb = os.path.join(self._tmp_path, 'gbb')
147*9c5db199SXin Li
148*9c5db199SXin Li
149*9c5db199SXin Li    def program(self):
150*9c5db199SXin Li        """Program the firmware but preserve VPD and HWID."""
151*9c5db199SXin Li        assert self._fw_path is not None
152*9c5db199SXin Li        self._set_servo_state()
153*9c5db199SXin Li        try:
154*9c5db199SXin Li            wp_ro_section = [('WP_RO', self._wp_ro)]
155*9c5db199SXin Li            rw_vpd_section = [('RW_VPD', self._rw_vpd)]
156*9c5db199SXin Li            ro_vpd_section = [('RO_VPD', self._ro_vpd)]
157*9c5db199SXin Li            gbb_section = [('GBB', self._gbb)]
158*9c5db199SXin Li            if self._keep_ro:
159*9c5db199SXin Li                # Keep the whole RO portion
160*9c5db199SXin Li                preserved_sections = wp_ro_section + rw_vpd_section
161*9c5db199SXin Li            else:
162*9c5db199SXin Li                preserved_sections = ro_vpd_section + rw_vpd_section
163*9c5db199SXin Li
164*9c5db199SXin Li            servo_v2_programmer = 'ft2232_spi:type=google-servo-v2'
165*9c5db199SXin Li            servo_v3_programmer = 'linux_spi'
166*9c5db199SXin Li            servo_v4_with_micro_programmer = 'raiden_debug_spi'
167*9c5db199SXin Li            servo_v4_with_ccd_programmer = 'raiden_debug_spi:target=AP'
168*9c5db199SXin Li
169*9c5db199SXin Li            if self._servo_version == 'servo_v2':
170*9c5db199SXin Li                programmer = servo_v2_programmer
171*9c5db199SXin Li                servo_serial = self._servo_serials.get('main')
172*9c5db199SXin Li                if servo_serial:
173*9c5db199SXin Li                    programmer += ',serial=%s' % servo_serial
174*9c5db199SXin Li            elif self._servo_version == 'servo_v3':
175*9c5db199SXin Li                programmer = servo_v3_programmer
176*9c5db199SXin Li            elif 'with_servo_micro' in self._servo_version:
177*9c5db199SXin Li                # When a uServo is connected to a DUT with CCD support, the
178*9c5db199SXin Li                # firmware programmer will always use the uServo to program.
179*9c5db199SXin Li                servo_micro_serial = self._servo_serials.get('servo_micro')
180*9c5db199SXin Li                programmer = servo_v4_with_micro_programmer
181*9c5db199SXin Li                programmer += ':serial=%s' % servo_micro_serial
182*9c5db199SXin Li            elif 'with_ccd' in self._servo_version:
183*9c5db199SXin Li                ccd_serial = self._servo_serials.get('ccd')
184*9c5db199SXin Li                programmer = servo_v4_with_ccd_programmer
185*9c5db199SXin Li                programmer += ',serial=%s' % ccd_serial
186*9c5db199SXin Li            else:
187*9c5db199SXin Li                raise Exception('Servo version %s is not supported.' %
188*9c5db199SXin Li                                self._servo_version)
189*9c5db199SXin Li            # Save needed sections from current firmware
190*9c5db199SXin Li            for section in preserved_sections + gbb_section:
191*9c5db199SXin Li                self._servo_host.run(' '.join([
192*9c5db199SXin Li                    'flashrom', '-V', '-p', programmer, '-r',
193*9c5db199SXin Li                    '-i', '%s:%s' % section]),
194*9c5db199SXin Li                    timeout=FIRMWARE_PROGRAM_TIMEOUT_SEC)
195*9c5db199SXin Li
196*9c5db199SXin Li            # Pack the saved VPD into new firmware
197*9c5db199SXin Li            self._servo_host.run('cp %s %s' % (self._fw_path, self._fw_main))
198*9c5db199SXin Li            img_size = self._servo_host.run_output(
199*9c5db199SXin Li                    "stat -c '%%s' %s" % self._fw_main)
200*9c5db199SXin Li            pack_cmd = ['flashrom',
201*9c5db199SXin Li                    '-p', 'dummy:image=%s,size=%s,emulate=VARIABLE_SIZE' % (
202*9c5db199SXin Li                        self._fw_main, img_size),
203*9c5db199SXin Li                    '-w', self._fw_main]
204*9c5db199SXin Li            for section in preserved_sections:
205*9c5db199SXin Li                pack_cmd.extend(['-i', '%s:%s' % section])
206*9c5db199SXin Li            self._servo_host.run(' '.join(pack_cmd),
207*9c5db199SXin Li                                 timeout=FIRMWARE_PROGRAM_TIMEOUT_SEC)
208*9c5db199SXin Li
209*9c5db199SXin Li            # HWID is inside the RO portion. Don't preserve HWID if we keep RO.
210*9c5db199SXin Li            if not self._keep_ro:
211*9c5db199SXin Li                # Read original HWID. The output format is:
212*9c5db199SXin Li                #    hardware_id: RAMBI TEST A_A 0128
213*9c5db199SXin Li                gbb_hwid_output = self._servo_host.run_output(
214*9c5db199SXin Li                        'futility gbb -g --hwid %s' % self._gbb)
215*9c5db199SXin Li                original_hwid = gbb_hwid_output.split(':', 1)[1].strip()
216*9c5db199SXin Li
217*9c5db199SXin Li                # Write HWID to new firmware
218*9c5db199SXin Li                self._servo_host.run("futility gbb -s --hwid='%s' %s" %
219*9c5db199SXin Li                        (original_hwid, self._fw_main))
220*9c5db199SXin Li
221*9c5db199SXin Li            # Flash the new firmware
222*9c5db199SXin Li            self._servo_host.run(' '.join([
223*9c5db199SXin Li                    'flashrom', '-V', '-p', programmer,
224*9c5db199SXin Li                    '-w', self._fw_main]), timeout=FIRMWARE_PROGRAM_TIMEOUT_SEC)
225*9c5db199SXin Li        finally:
226*9c5db199SXin Li            self._servo.get_power_state_controller().reset()
227*9c5db199SXin Li            self._restore_servo_state()
228*9c5db199SXin Li
229*9c5db199SXin Li
230*9c5db199SXin Li    def prepare_programmer(self, path):
231*9c5db199SXin Li        """Prepare programmer for programming.
232*9c5db199SXin Li
233*9c5db199SXin Li        @param path: a string, name of the file containing the firmware image.
234*9c5db199SXin Li        """
235*9c5db199SXin Li        self._fw_path = path
236*9c5db199SXin Li        self.init_section_paths(os.path.dirname(path))
237*9c5db199SXin Li
238*9c5db199SXin Li        # If servo is running with servo v4, there may be two programming
239*9c5db199SXin Li        # devices. Determine the programmer based on the active one.
240*9c5db199SXin Li        self._servo_version = self._servo.get_servo_version(active=True)
241*9c5db199SXin Li
242*9c5db199SXin Li        # CCD takes care holding AP/EC. Don't need the following steps.
243*9c5db199SXin Li        if 'with_ccd' not in self._servo_version:
244*9c5db199SXin Li            faft_config = FAFTConfig(self._servo.get_board())
245*9c5db199SXin Li            self._servo_prog_state_delay = faft_config.servo_prog_state_delay
246*9c5db199SXin Li            self._servo_prog_state = (
247*9c5db199SXin Li                'spi2_vref:%s' % faft_config.spi_voltage,
248*9c5db199SXin Li                'spi2_buf_en:on',
249*9c5db199SXin Li                'spi2_buf_on_flex_en:on',
250*9c5db199SXin Li                'spi_hold:off',
251*9c5db199SXin Li                'cold_reset:on',
252*9c5db199SXin Li                'usbpd_reset:on',
253*9c5db199SXin Li                )
254*9c5db199SXin Li
255*9c5db199SXin Li
256*9c5db199SXin Liclass FlashECProgrammer(_BaseProgrammer):
257*9c5db199SXin Li    """Class for programming AP flashrom."""
258*9c5db199SXin Li
259*9c5db199SXin Li    def __init__(self, servo, host=None, ec_chip=None):
260*9c5db199SXin Li        """Configure required servo state.
261*9c5db199SXin Li
262*9c5db199SXin Li        @param servo: a servo object controlling the servo device
263*9c5db199SXin Li        @param host: a host object to execute commands. Default to None,
264*9c5db199SXin Li                     using the host object from the above servo object, i.e.
265*9c5db199SXin Li                     a servo host. A CrOS host object can be passed here
266*9c5db199SXin Li                     such that it executes commands on the CrOS device.
267*9c5db199SXin Li        @param ec_chip: a string of EC chip. Default to None, using the
268*9c5db199SXin Li                        EC chip name reported by servo, the primary EC.
269*9c5db199SXin Li                        Can pass a different chip name, for the case of
270*9c5db199SXin Li                        the base EC.
271*9c5db199SXin Li
272*9c5db199SXin Li        """
273*9c5db199SXin Li        super(FlashECProgrammer, self).__init__(servo, ['flash_ec'], host)
274*9c5db199SXin Li        self._servo_version = self._servo.get_servo_version()
275*9c5db199SXin Li        self._ec_chip = ec_chip
276*9c5db199SXin Li
277*9c5db199SXin Li    def prepare_programmer(self, image):
278*9c5db199SXin Li        """Prepare programmer for programming.
279*9c5db199SXin Li
280*9c5db199SXin Li        @param image: string with the location of the image file
281*9c5db199SXin Li        """
282*9c5db199SXin Li        if self._ec_chip is None:
283*9c5db199SXin Li            self._ec_chip = self._servo.get('ec_chip')
284*9c5db199SXin Li
285*9c5db199SXin Li        # If servo is running with servo v4, there may be two programming
286*9c5db199SXin Li        # devices. Determine the programmer based on the active one.
287*9c5db199SXin Li        self._servo_version = self._servo.get_servo_version(active=True)
288*9c5db199SXin Li
289*9c5db199SXin Li        # Get the port of servod. flash_ec may use it to talk to servod.
290*9c5db199SXin Li        port = self._servo._servo_host.servo_port
291*9c5db199SXin Li        self._program_cmd = ('flash_ec --chip=%s --image=%s --port=%d' %
292*9c5db199SXin Li                             (self._ec_chip, image, port))
293*9c5db199SXin Li        if self._ec_chip == 'stm32':
294*9c5db199SXin Li            self._program_cmd += ' --bitbang_rate=57600'
295*9c5db199SXin Li        self._program_cmd += ' --verify'
296*9c5db199SXin Li        self._program_cmd += ' --verbose'
297*9c5db199SXin Li
298*9c5db199SXin Li
299*9c5db199SXin Liclass ProgrammerV2(object):
300*9c5db199SXin Li    """Main programmer class which provides programmer for BIOS and EC with
301*9c5db199SXin Li    servo V2."""
302*9c5db199SXin Li
303*9c5db199SXin Li    def __init__(self, servo):
304*9c5db199SXin Li        self._servo = servo
305*9c5db199SXin Li        self._valid_boards = self._get_valid_v2_boards()
306*9c5db199SXin Li        self._bios_programmer = self._factory_bios(self._servo)
307*9c5db199SXin Li        self._ec_programmer = self._factory_ec(self._servo)
308*9c5db199SXin Li
309*9c5db199SXin Li
310*9c5db199SXin Li    @staticmethod
311*9c5db199SXin Li    def _get_valid_v2_boards():
312*9c5db199SXin Li        """Greps servod config files to look for valid v2 boards.
313*9c5db199SXin Li
314*9c5db199SXin Li        @return A list of valid board names.
315*9c5db199SXin Li        """
316*9c5db199SXin Li        site_packages_paths = site.getsitepackages()
317*9c5db199SXin Li        SERVOD_CONFIG_DATA_DIR = None
318*9c5db199SXin Li        for p in site_packages_paths:
319*9c5db199SXin Li            servo_data_path = os.path.join(p, 'servo', 'data')
320*9c5db199SXin Li            if os.path.exists(servo_data_path):
321*9c5db199SXin Li                SERVOD_CONFIG_DATA_DIR = servo_data_path
322*9c5db199SXin Li                break
323*9c5db199SXin Li        if not SERVOD_CONFIG_DATA_DIR:
324*9c5db199SXin Li            raise ProgrammerError(
325*9c5db199SXin Li                    'Unable to locate data directory of Python servo module')
326*9c5db199SXin Li        SERVOFLEX_V2_R0_P50_CONFIG = 'servoflex_v2_r0_p50.xml'
327*9c5db199SXin Li        SERVO_CONFIG_GLOB = 'servo_*_overlay.xml'
328*9c5db199SXin Li        SERVO_CONFIG_REGEXP = 'servo_(?P<board>.+)_overlay.xml'
329*9c5db199SXin Li
330*9c5db199SXin Li        def is_v2_compatible_board(board_config_path):
331*9c5db199SXin Li            """Check if the given board config file is v2-compatible.
332*9c5db199SXin Li
333*9c5db199SXin Li            @param board_config_path: Path to a board config XML file.
334*9c5db199SXin Li
335*9c5db199SXin Li            @return True if the board is v2-compatible; False otherwise.
336*9c5db199SXin Li            """
337*9c5db199SXin Li            configs = []
338*9c5db199SXin Li            def get_all_includes(config_path):
339*9c5db199SXin Li                """Get all included XML config names in the given config file.
340*9c5db199SXin Li
341*9c5db199SXin Li                @param config_path: Path to a servo config file.
342*9c5db199SXin Li                """
343*9c5db199SXin Li                root = xml.etree.ElementTree.parse(config_path).getroot()
344*9c5db199SXin Li                for element in root.findall('include'):
345*9c5db199SXin Li                    include_name = element.find('name').text
346*9c5db199SXin Li                    configs.append(include_name)
347*9c5db199SXin Li                    get_all_includes(os.path.join(
348*9c5db199SXin Li                            SERVOD_CONFIG_DATA_DIR, include_name))
349*9c5db199SXin Li
350*9c5db199SXin Li            get_all_includes(board_config_path)
351*9c5db199SXin Li            return True if SERVOFLEX_V2_R0_P50_CONFIG in configs else False
352*9c5db199SXin Li
353*9c5db199SXin Li        result = []
354*9c5db199SXin Li        board_overlays = glob.glob(
355*9c5db199SXin Li                os.path.join(SERVOD_CONFIG_DATA_DIR, SERVO_CONFIG_GLOB))
356*9c5db199SXin Li        for overlay_path in board_overlays:
357*9c5db199SXin Li            if is_v2_compatible_board(overlay_path):
358*9c5db199SXin Li                result.append(re.search(SERVO_CONFIG_REGEXP,
359*9c5db199SXin Li                                        overlay_path).group('board'))
360*9c5db199SXin Li        return result
361*9c5db199SXin Li
362*9c5db199SXin Li
363*9c5db199SXin Li    def _get_flashrom_programmer(self, servo):
364*9c5db199SXin Li        """Gets a proper flashrom programmer.
365*9c5db199SXin Li
366*9c5db199SXin Li        @param servo: A servo object.
367*9c5db199SXin Li
368*9c5db199SXin Li        @return A programmer for flashrom.
369*9c5db199SXin Li        """
370*9c5db199SXin Li        return FlashromProgrammer(servo)
371*9c5db199SXin Li
372*9c5db199SXin Li
373*9c5db199SXin Li    def _factory_bios(self, servo):
374*9c5db199SXin Li        """Instantiates and returns (bios, ec) programmers for the board.
375*9c5db199SXin Li
376*9c5db199SXin Li        @param servo: A servo object.
377*9c5db199SXin Li
378*9c5db199SXin Li        @return A programmer for ec. If the programmer is not supported
379*9c5db199SXin Li            for the board, None will be returned.
380*9c5db199SXin Li        """
381*9c5db199SXin Li        _bios_prog = None
382*9c5db199SXin Li        _board = servo.get_board()
383*9c5db199SXin Li
384*9c5db199SXin Li        logging.debug('Setting up BIOS programmer for board: %s', _board)
385*9c5db199SXin Li        if _board in self._valid_boards:
386*9c5db199SXin Li            _bios_prog = self._get_flashrom_programmer(servo)
387*9c5db199SXin Li        else:
388*9c5db199SXin Li            logging.warning('No BIOS programmer found for board: %s', _board)
389*9c5db199SXin Li
390*9c5db199SXin Li        return _bios_prog
391*9c5db199SXin Li
392*9c5db199SXin Li
393*9c5db199SXin Li    def _factory_ec(self, servo):
394*9c5db199SXin Li        """Instantiates and returns ec programmer for the board.
395*9c5db199SXin Li
396*9c5db199SXin Li        @param servo: A servo object.
397*9c5db199SXin Li
398*9c5db199SXin Li        @return A programmer for ec. If the programmer is not supported
399*9c5db199SXin Li            for the board, None will be returned.
400*9c5db199SXin Li        """
401*9c5db199SXin Li        _ec_prog = None
402*9c5db199SXin Li        _board = servo.get_board()
403*9c5db199SXin Li
404*9c5db199SXin Li        logging.debug('Setting up EC programmer for board: %s', _board)
405*9c5db199SXin Li        if _board in self._valid_boards:
406*9c5db199SXin Li            _ec_prog = FlashECProgrammer(servo)
407*9c5db199SXin Li        else:
408*9c5db199SXin Li            logging.warning('No EC programmer found for board: %s', _board)
409*9c5db199SXin Li
410*9c5db199SXin Li        return _ec_prog
411*9c5db199SXin Li
412*9c5db199SXin Li
413*9c5db199SXin Li    def program_bios(self, image):
414*9c5db199SXin Li        """Programs the DUT with provide bios image.
415*9c5db199SXin Li
416*9c5db199SXin Li        @param image: (required) location of bios image file.
417*9c5db199SXin Li
418*9c5db199SXin Li        """
419*9c5db199SXin Li        self._bios_programmer.prepare_programmer(image)
420*9c5db199SXin Li        self._bios_programmer.program()
421*9c5db199SXin Li
422*9c5db199SXin Li
423*9c5db199SXin Li    def program_ec(self, image):
424*9c5db199SXin Li        """Programs the DUT with provide ec image.
425*9c5db199SXin Li
426*9c5db199SXin Li        @param image: (required) location of ec image file.
427*9c5db199SXin Li
428*9c5db199SXin Li        """
429*9c5db199SXin Li        self._ec_programmer.prepare_programmer(image)
430*9c5db199SXin Li        self._ec_programmer.program()
431*9c5db199SXin Li
432*9c5db199SXin Li
433*9c5db199SXin Liclass ProgrammerV2RwOnly(ProgrammerV2):
434*9c5db199SXin Li    """Main programmer class which provides programmer for only updating the RW
435*9c5db199SXin Li    portion of BIOS with servo V2.
436*9c5db199SXin Li
437*9c5db199SXin Li    It does nothing on EC, as EC software sync on the next boot will
438*9c5db199SXin Li    automatically overwrite the EC RW portion, using the EC RW image inside
439*9c5db199SXin Li    the BIOS RW image.
440*9c5db199SXin Li
441*9c5db199SXin Li    """
442*9c5db199SXin Li
443*9c5db199SXin Li    def _get_flashrom_programmer(self, servo):
444*9c5db199SXin Li        """Gets a proper flashrom programmer.
445*9c5db199SXin Li
446*9c5db199SXin Li        @param servo: A servo object.
447*9c5db199SXin Li
448*9c5db199SXin Li        @return A programmer for flashrom.
449*9c5db199SXin Li        """
450*9c5db199SXin Li        return FlashromProgrammer(servo, keep_ro=True)
451*9c5db199SXin Li
452*9c5db199SXin Li
453*9c5db199SXin Li    def program_ec(self, image):
454*9c5db199SXin Li        """Programs the DUT with provide ec image.
455*9c5db199SXin Li
456*9c5db199SXin Li        @param image: (required) location of ec image file.
457*9c5db199SXin Li
458*9c5db199SXin Li        """
459*9c5db199SXin Li        # Do nothing. EC software sync will update the EC RW.
460*9c5db199SXin Li        pass
461*9c5db199SXin Li
462*9c5db199SXin Li
463*9c5db199SXin Liclass ProgrammerV3(object):
464*9c5db199SXin Li    """Main programmer class which provides programmer for BIOS and EC with
465*9c5db199SXin Li    servo V3.
466*9c5db199SXin Li
467*9c5db199SXin Li    Different from programmer for servo v2, programmer for servo v3 does not
468*9c5db199SXin Li    try to validate if the board can use servo V3 to update firmware. As long as
469*9c5db199SXin Li    the servod process running in beagblebone with given board, the program will
470*9c5db199SXin Li    attempt to flash bios and ec.
471*9c5db199SXin Li
472*9c5db199SXin Li    """
473*9c5db199SXin Li
474*9c5db199SXin Li    def __init__(self, servo):
475*9c5db199SXin Li        self._servo = servo
476*9c5db199SXin Li        self._bios_programmer = FlashromProgrammer(servo)
477*9c5db199SXin Li        self._ec_programmer = FlashECProgrammer(servo)
478*9c5db199SXin Li
479*9c5db199SXin Li
480*9c5db199SXin Li    def program_bios(self, image):
481*9c5db199SXin Li        """Programs the DUT with provide bios image.
482*9c5db199SXin Li
483*9c5db199SXin Li        @param image: (required) location of bios image file.
484*9c5db199SXin Li
485*9c5db199SXin Li        """
486*9c5db199SXin Li        self._bios_programmer.prepare_programmer(image)
487*9c5db199SXin Li        self._bios_programmer.program()
488*9c5db199SXin Li
489*9c5db199SXin Li
490*9c5db199SXin Li    def program_ec(self, image):
491*9c5db199SXin Li        """Programs the DUT with provide ec image.
492*9c5db199SXin Li
493*9c5db199SXin Li        @param image: (required) location of ec image file.
494*9c5db199SXin Li
495*9c5db199SXin Li        """
496*9c5db199SXin Li        self._ec_programmer.prepare_programmer(image)
497*9c5db199SXin Li        self._ec_programmer.program()
498*9c5db199SXin Li
499*9c5db199SXin Li
500*9c5db199SXin Liclass ProgrammerV3RwOnly(ProgrammerV3):
501*9c5db199SXin Li    """Main programmer class which provides programmer for only updating the RW
502*9c5db199SXin Li    portion of BIOS with servo V3.
503*9c5db199SXin Li
504*9c5db199SXin Li    It does nothing on EC, as EC software sync on the next boot will
505*9c5db199SXin Li    automatically overwrite the EC RW portion, using the EC RW image inside
506*9c5db199SXin Li    the BIOS RW image.
507*9c5db199SXin Li
508*9c5db199SXin Li    """
509*9c5db199SXin Li
510*9c5db199SXin Li    def __init__(self, servo):
511*9c5db199SXin Li        self._servo = servo
512*9c5db199SXin Li        self._bios_programmer = FlashromProgrammer(servo, keep_ro=True)
513*9c5db199SXin Li
514*9c5db199SXin Li
515*9c5db199SXin Li    def program_ec(self, image):
516*9c5db199SXin Li        """Programs the DUT with provide ec image.
517*9c5db199SXin Li
518*9c5db199SXin Li        @param image: (required) location of ec image file.
519*9c5db199SXin Li
520*9c5db199SXin Li        """
521*9c5db199SXin Li        # Do nothing. EC software sync will update the EC RW.
522*9c5db199SXin Li        pass
523