blob: 46330304d8d593bf5e44638df657738fb4f94b48 [file] [log] [blame]
Yong Hong65bda312018-12-13 20:05:58 +08001# Copyright 2018 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 re
6import subprocess
7
8import factory_common # pylint: disable=unused-import
9from cros.factory.gooftool import common as gooftool_common
10
11
12# ChromeOS firmware VPD partition names.
13VPD_READONLY_PARTITION_NAME = 'RO_VPD'
14VPD_READWRITE_PARTITION_NAME = 'RW_VPD'
15
16
17class VPDTool(object):
18 """This class wraps the functions supplied by VPD cmdline tool into methods.
19 """
20 _KEY_PATTERN = re.compile(r'^[a-zA-Z0-9_.]+$')
21
22 def __init__(self, shell=None):
23 self._shell = shell or gooftool_common.Shell
24
25 def GetValue(self, key, default_value=None, partition=None, filename=None):
26 """Gets a VPD value with the specific key.
27
28 If the VPD doesn't contain the data with the given `key`, this function will
29 return `default_value`.
30
31 Args:
32 key: A string of the key of the data to get.
33 default_value: The value to return if the data doesn't exist.
34 filename: Filename of the bios image, see `vpd -h` for detail.
35 partition: Specify VPD partition name in fmap.
36
37 Returns:
38 A string of raw value data or `None`.
39 """
40 self._EnsureIfKeyValid(key)
41 try:
42 return self._InvokeCmd(
43 self._BuildBasicCmd(partition, filename) + ['-g', key])
44 except subprocess.CalledProcessError:
45 if filename is not None:
46 self._CheckFileExistence(filename)
47 return default_value
48
49 def GetAllData(self, partition=None, filename=None):
50 """Gets all VPD data in dictionary format.
51
52 Args:
53 filename: Filename of the bios image, see `vpd -h` for detail.
54 partition: Specify VPD partition name in fmap.
55
56 Returns:
57 A dictionary in which each key-value pair represents a VPD data entry.
58 """
59 raw_data = self._InvokeCmd(
60 self._BuildBasicCmd(partition, filename) + ['-l', '--null-terminated'])
61 result = dict(field.split('=', 1) for field in raw_data.split('\0')
62 if '=' in field)
63 if not result and filename is not None:
64 self._CheckFileExistence(filename)
65 return result
66
67 def UpdateData(self, items, partition=None, filename=None):
68 """Updates VPD data.
69
70 Args:
71 items: Items to set. A value of "None" deletes the item from the VPD.
72 filename: Filename of the bios, see `vpd -h` for detail.
73 partition: Specify VPD partition name in fmap.
74 """
75 cmd = self._BuildBasicCmd(partition, filename)
76 for k, v in items.items():
77 self._EnsureIfKeyValid(k)
78 cmd += ['-d', k] if v is None else ['-s', '%s=%s' % (k, v)]
79 self._InvokeCmd(cmd)
80
81 def _CheckFileExistence(self, filename):
82 # This could be CheckCall. However, to reduce API dependency, we are
83 # reusing CheckOutput.
84 self._InvokeCmd(['test', '-e', filename])
85
86 def _InvokeCmd(self, cmd):
87 proc_result = self._shell(cmd)
88 if not proc_result.success:
89 raise subprocess.CalledProcessError(proc_result.status, cmd)
90 return proc_result.stdout
91
92 @classmethod
93 def _BuildBasicCmd(cls, partition, filename):
94 cmd = ['vpd']
95 if partition:
96 cmd += ['-i', partition]
97 if filename:
98 cmd += ['-f', filename]
99 return cmd
100
101 @classmethod
102 def _EnsureIfKeyValid(cls, key):
103 if not cls._KEY_PATTERN.match(key):
104 raise ValueError('Invalid VPD key %r (does not match pattern %s)' %
105 (key, cls._KEY_PATTERN.pattern))