blob: 5761f1a65d529645141e0790adbc236c0ca55630 [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Test if the memory size is correctly written in the firmware.
Description
-----------
Linux kernel trusts the available memory region specified from firmware, via
ACPI or Device Tree. However, it is possible for the firmware to send wrong
values, for example always only assigning 8GB for kernel while the system
has 16GB memory installed.
On traditional PC, the memory information is stored on SPD chipset on memory
module so firmware should read and claim free space for kernel according to SPD.
On modern Chromebooks, the SPD is replaced by a pre-defined mapping table and
decided by straps. When the mapping table is out-dated, for example if an old
firmware is installed, then the allocated memory for kernel would be wrong.
The Chrome OS command, ``mosys``, can read from physical or virtual SPD and
report expected memory size. So this test tries to compare the value from
``mosys`` and kernel ``meminfo`` to figure out if firmware has reported wrong
memory size for kernel.
Usually firmware has to reserve some memory, for example ACPI tables, DMA,
I/O port mappings, so the kernel is expected to get less memory. This is
specified by argument ``max_diff_gb``.
Meanwhile, for virtual SPD, it is possible that both firmware and ``mosys`` have
out-dated information of memory straps, so optionally we support a third source,
the shopfloor backend, to provide memory size.
If argument ``device_data_key`` is set, we will also check memory size by the
information from device data (usually retrieved from shopfloor backend if
factory supports it).
Test Procedure
--------------
This is an automated test without user interaction.
When started, the test collects memory size information from different source
and fail if the difference is too large.
Dependency
----------
- Command ``mosys``: ``mosys -k memory spd print geometry``.
- Kernel to support ``/proc/meminfo``, search for string ``MemTotal``.
- Optionally, shopfloor integration to save memory in device data.
Examples
--------
To compare and check only the memory size from ``mosys`` and kernel, add this
in test list::
{
"pytest_name": "memory_size"
}
To read device data from Shopfloor Service then compare and check the memory
size from ``mosys``, kernel, and device data ``component.memory_size``, with
difference up to 300MB::
{
"pytest_name": "shopfloor_service",
"args": {
"method": "GetDeviceInfo"
}
}
{
"pytest_name": "memory_size",
"args": {
"device_data_key": "component.memory_size",
"max_diff_gb": 0.3
}
}
"""
import re
import factory_common # pylint: disable=unused-import
from cros.factory.test import device_data
from cros.factory.test.i18n import _
from cros.factory.test import test_case
from cros.factory.utils.arg_utils import Arg
from cros.factory.utils import process_utils
class MemorySize(test_case.TestCase):
ARGS = [
Arg('device_data_key', str,
'Device data key for getting memory size in GB.',
default=None),
Arg('max_diff_gb', float,
('Maximum tolerance difference between memory size detected by '
'kernel and mosys in GB.'),
default=0.5),
]
def runTest(self):
self.ui.SetState(_('Checking memory info...'))
# Get memory info using mosys.
ret = process_utils.CheckOutput(
['mosys', '-k', 'memory', 'spd', 'print', 'geometry'])
mosys_mem_mb = sum([int(x) for x in re.findall('size_mb="([^"]*)"', ret)])
mosys_mem_gb = round(mosys_mem_mb / 1024.0, 1)
# Get kernel meminfo.
with open('/proc/meminfo', 'r') as f:
kernel_mem_kb = int(re.search(r'^MemTotal:\s*([0-9]+)\s*kB',
f.read()).group(1))
kernel_mem_gb = round(kernel_mem_kb / 1024.0 / 1024.0, 1)
diff = abs(kernel_mem_gb - mosys_mem_gb)
if diff > self.args.max_diff_gb:
self.fail('Memory size detected by kernel is different from mosys by '
'%.1f GB' % diff)
return
if not self.args.device_data_key:
return
sf_mem_gb = round(float(device_data.GetDeviceData(
self.args.device_data_key)), 1)
# The memory size info in mosys should be the same as that in device data.
if abs(mosys_mem_gb - sf_mem_gb) > 10e-6:
msg = ('Memory size detected in mosys (%.1f GB) is different from the '
'record in device data (%.1f GB)' % (mosys_mem_gb, sf_mem_gb))
self.fail(msg)