Commit 0a4cad9c authored by Enric Balletbo i Serra's avatar Enric Balletbo i Serra Committed by Tzung-Bi Shih

platform/chrome: Add ChromeOS ACPI device driver

The x86 Chromebooks have the ChromeOS ACPI device. This driver attaches
to the ChromeOS ACPI device and exports the values reported by ACPI in a
sysfs directory. This data isn't present in ACPI tables when read
through ACPI tools, hence a driver is needed to do it. The driver gets
data from firmware using the ACPI component of the kernel. The ACPI values
are presented in string form (numbers as decimal values) or binary
blobs, and can be accessed as the contents of the appropriate read only
files in the standard ACPI device's sysfs directory tree. This data is
consumed by the ChromeOS user space.
Reviewed-by: default avatarGuenter Roeck <groeck@chromium.org>
Reviewed-by: default avatarAndy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: default avatarEnric Balletbo i Serra <enric.balletbo@collabora.com>
Co-developed-by: default avatarMuhammad Usama Anjum <usama.anjum@collabora.com>
Signed-off-by: default avatarMuhammad Usama Anjum <usama.anjum@collabora.com>
Signed-off-by: default avatarTzung-Bi Shih <tzungbi@kernel.org>
Link: https://lore.kernel.org/r/Yn4OKYrtV35Dv+nd@debian-BULLSEYE-live-builder-AMD64
parent 7464ff8b
What: /sys/bus/platform/devices/GGL0001:*/BINF.2
Date: May 2022
KernelVersion: 5.19
Description:
This file shows information about the current boot of
the active EC firmware.
* 0 - Read only (recovery) firmware.
* 1 - Rewritable firmware.
What: /sys/bus/platform/devices/GGL0001:*/BINF.3
Date: May 2022
KernelVersion: 5.19
Description:
This file shows information about the current boot of
the active main firmware type.
* 0 - Recovery.
* 1 - Normal.
* 2 - Developer.
* 3 - Netboot (factory installation only).
What: /sys/bus/platform/devices/GGL0001:*/CHSW
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the switch position for the Chrome OS specific
hardware switches.
* 0 - No changes.
* 2 - Recovery button was pressed when firmware booted.
* 4 - Recovery button was pressed when EC firmware booted.
* 32 - Developer switch was enabled when firmware booted.
* 512 - Firmware write protection was disabled when firmware
booted.
What: /sys/bus/platform/devices/GGL0001:*/FMAP
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the physical memory address of the start of
the main processor firmware flashmap.
What: /sys/bus/platform/devices/GGL0001:*/FRID
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the firmware version for the read-only portion
of the main processor firmware.
What: /sys/bus/platform/devices/GGL0001:*/FWID
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the firmware version for the rewritable portion
of the main processor firmware.
What: /sys/bus/platform/devices/GGL0001:*/GPIO.X/GPIO.0
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the type of the GPIO signal for the Chrome OS
specific GPIO assignments.
* 1 - Recovery button.
* 2 - Developer mode switch.
* 3 - Firmware write protection switch.
* 256 to 511 - Debug header GPIO 0 to GPIO 255.
What: /sys/bus/platform/devices/GGL0001:*/GPIO.X/GPIO.1
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the signal attributes of the GPIO signal.
* 0 - Signal is active low.
* 1 - Signal is active high.
What: /sys/bus/platform/devices/GGL0001:*/GPIO.X/GPIO.2
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the GPIO number on the specified GPIO
controller.
What: /sys/bus/platform/devices/GGL0001:*/GPIO.X/GPIO.3
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the name of the GPIO controller.
What: /sys/bus/platform/devices/GGL0001:*/HWID
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the hardware ID for the Chromebook.
What: /sys/bus/platform/devices/GGL0001:*/MECK
Date: May 2022
KernelVersion: 5.19
Description:
This binary file returns the SHA-1 or SHA-256 hash that is
read out of the Management Engine extended registers during
boot. The hash is exported vi ACPI so the OS can verify that
the Management Engine firmware has not changed. If Management
Engine is not present, or if the firmware was unable to read the
extended registers, this buffer size can be zero.
What: /sys/bus/platform/devices/GGL0001:*/VBNV.0
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the offset in CMOS bank 0 of the verified boot
non-volatile storage block, counting from the first writable
CMOS byte (that is, 'offset = 0' is the byte following the 14
bytes of clock data).
What: /sys/bus/platform/devices/GGL0001:*/VBNV.1
Date: May 2022
KernelVersion: 5.19
Description:
This file shows the size in bytes of the verified boot
non-volatile storage block.
What: /sys/bus/platform/devices/GGL0001:*/VDAT
Date: May 2022
KernelVersion: 5.19
Description:
This binary file returns the verified boot data block shared
between the firmware verification step and the kernel
verification step.
.. SPDX-License-Identifier: GPL-2.0
=====================
Chrome OS ACPI Device
=====================
Hardware functionality specific to Chrome OS is exposed through a Chrome OS ACPI device.
The plug and play ID of a Chrome OS ACPI device is GGL0001. GGL is a valid PNP ID of Google.
PNP ID can be used with the ACPI devices according to the guidelines. The following ACPI
objects are supported:
.. flat-table:: Supported ACPI Objects
:widths: 1 2
:header-rows: 1
* - Object
- Description
* - CHSW
- Chrome OS switch positions
* - HWID
- Chrome OS hardware ID
* - FWID
- Chrome OS firmware version
* - FRID
- Chrome OS read-only firmware version
* - BINF
- Chrome OS boot information
* - GPIO
- Chrome OS GPIO assignments
* - VBNV
- Chrome OS NVRAM locations
* - VDTA
- Chrome OS verified boot data
* - FMAP
- Chrome OS flashmap base address
* - MLST
- Chrome OS method list
CHSW (Chrome OS switch positions)
=================================
This control method returns the switch positions for Chrome OS specific hardware switches.
Arguments:
----------
None
Result code:
------------
An integer containing the switch positions as bitfields:
.. flat-table::
:widths: 1 2
* - 0x00000002
- Recovery button was pressed when x86 firmware booted.
* - 0x00000004
- Recovery button was pressed when EC firmware booted. (required if EC EEPROM is
rewritable; otherwise optional)
* - 0x00000020
- Developer switch was enabled when x86 firmware booted.
* - 0x00000200
- Firmware write protection was disabled when x86 firmware booted. (required if
firmware write protection is controlled through x86 BIOS; otherwise optional)
All other bits are reserved and should be set to 0.
HWID (Chrome OS hardware ID)
============================
This control method returns the hardware ID for the Chromebook.
Arguments:
----------
None
Result code:
------------
A null-terminated ASCII string containing the hardware ID from the Model-Specific Data area of
EEPROM.
Note that the hardware ID can be up to 256 characters long, including the terminating null.
FWID (Chrome OS firmware version)
=================================
This control method returns the firmware version for the rewritable portion of the main
processor firmware.
Arguments:
----------
None
Result code:
------------
A null-terminated ASCII string containing the complete firmware version for the rewritable
portion of the main processor firmware.
FRID (Chrome OS read-only firmware version)
===========================================
This control method returns the firmware version for the read-only portion of the main
processor firmware.
Arguments:
----------
None
Result code:
------------
A null-terminated ASCII string containing the complete firmware version for the read-only
(bootstrap + recovery ) portion of the main processor firmware.
BINF (Chrome OS boot information)
=================================
This control method returns information about the current boot.
Arguments:
----------
None
Result code:
------------
.. code-block::
Package {
Reserved1
Reserved2
Active EC Firmware
Active Main Firmware Type
Reserved5
}
.. flat-table::
:widths: 1 1 2
:header-rows: 1
* - Field
- Format
- Description
* - Reserved1
- DWORD
- Set to 256 (0x100). This indicates this field is no longer used.
* - Reserved2
- DWORD
- Set to 256 (0x100). This indicates this field is no longer used.
* - Active EC firmware
- DWORD
- The EC firmware which was used during boot.
- 0 - Read-only (recovery) firmware
- 1 - Rewritable firmware.
Set to 0 if EC firmware is always read-only.
* - Active Main Firmware Type
- DWORD
- The main firmware type which was used during boot.
- 0 - Recovery
- 1 - Normal
- 2 - Developer
- 3 - netboot (factory installation only)
Other values are reserved.
* - Reserved5
- DWORD
- Set to 256 (0x100). This indicates this field is no longer used.
GPIO (Chrome OS GPIO assignments)
=================================
This control method returns information about Chrome OS specific GPIO assignments for
Chrome OS hardware, so the kernel can directly control that hardware.
Arguments:
----------
None
Result code:
------------
.. code-block::
Package {
Package {
// First GPIO assignment
Signal Type //DWORD
Attributes //DWORD
Controller Offset //DWORD
Controller Name //ASCIIZ
},
...
Package {
// Last GPIO assignment
Signal Type //DWORD
Attributes //DWORD
Controller Offset //DWORD
Controller Name //ASCIIZ
}
}
Where ASCIIZ means a null-terminated ASCII string.
.. flat-table::
:widths: 1 1 2
:header-rows: 1
* - Field
- Format
- Description
* - Signal Type
- DWORD
- Type of GPIO signal
- 0x00000001 - Recovery button
- 0x00000002 - Developer mode switch
- 0x00000003 - Firmware write protection switch
- 0x00000100 - Debug header GPIO 0
- ...
- 0x000001FF - Debug header GPIO 255
Other values are reserved.
* - Attributes
- DWORD
- Signal attributes as bitfields:
- 0x00000001 - Signal is active-high (for button, a GPIO value
of 1 means the button is pressed; for switches, a GPIO value
of 1 means the switch is enabled). If this bit is 0, the signal
is active low. Set to 0 for debug header GPIOs.
* - Controller Offset
- DWORD
- GPIO number on the specified controller.
* - Controller Name
- ASCIIZ
- Name of the controller for the GPIO.
Currently supported names:
"NM10" - Intel NM10 chip
VBNV (Chrome OS NVRAM locations)
================================
This control method returns information about the NVRAM (CMOS) locations used to
communicate with the BIOS.
Arguments:
----------
None
Result code:
------------
.. code-block::
Package {
NV Storage Block Offset //DWORD
NV Storage Block Size //DWORD
}
.. flat-table::
:widths: 1 1 2
:header-rows: 1
* - Field
- Format
- Description
* - NV Storage Block Offset
- DWORD
- Offset in CMOS bank 0 of the verified boot non-volatile storage block, counting from
the first writable CMOS byte (that is, offset=0 is the byte following the 14 bytes of
clock data).
* - NV Storage Block Size
- DWORD
- Size in bytes of the verified boot non-volatile storage block.
FMAP (Chrome OS flashmap address)
=================================
This control method returns the physical memory address of the start of the main processor
firmware flashmap.
Arguments:
----------
None
NoneResult code:
----------------
A DWORD containing the physical memory address of the start of the main processor firmware
flashmap.
VDTA (Chrome OS verified boot data)
===================================
This control method returns the verified boot data block shared between the firmware
verification step and the kernel verification step.
Arguments:
----------
None
Result code:
------------
A buffer containing the verified boot data block.
MECK (Management Engine Checksum)
=================================
This control method returns the SHA-1 or SHA-256 hash that is read out of the Management
Engine extended registers during boot. The hash is exported via ACPI so the OS can verify that
the ME firmware has not changed. If Management Engine is not present, or if the firmware was
unable to read the extended registers, this buffer can be zero.
Arguments:
----------
None
Result code:
------------
A buffer containing the ME hash.
MLST (Chrome OS method list)
============================
This control method returns a list of the other control methods supported by the Chrome OS
hardware device.
Arguments:
----------
None
Result code:
------------
A package containing a list of null-terminated ASCII strings, one for each control method
supported by the Chrome OS hardware device, not including the MLST method itself.
For this version of the specification, the result is:
.. code-block::
Package {
"CHSW",
"FWID",
"HWID",
"FRID",
"BINF",
"GPIO",
"VBNV",
"FMAP",
"VDTA",
"MECK"
}
......@@ -29,3 +29,4 @@ ACPI Support
non-d0-probe
extcon-intel-int3496
intel-pmc-mux
chromeos-acpi-device
......@@ -15,6 +15,17 @@ menuconfig CHROME_PLATFORMS
if CHROME_PLATFORMS
config CHROMEOS_ACPI
tristate "ChromeOS specific ACPI extensions"
depends on ACPI
help
This driver provides the firmware interface for the services
exported through the ChromeOS interfaces when using ChromeOS
ACPI firmware.
If you have an ACPI-compatible Chromebook, say Y or M here.
The module will be called chromeos_acpi.
config CHROMEOS_LAPTOP
tristate "Chrome OS Laptop"
depends on I2C && DMI && X86
......
......@@ -4,6 +4,7 @@
CFLAGS_cros_ec_trace.o:= -I$(src)
CFLAGS_cros_ec_sensorhub_ring.o:= -I$(src)
obj-$(CONFIG_CHROMEOS_ACPI) += chromeos_acpi.o
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
obj-$(CONFIG_CHROMEOS_PRIVACY_SCREEN) += chromeos_privacy_screen.o
obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* ChromeOS specific ACPI extensions
*
* Copyright 2022 Google LLC
*
* This driver attaches to the ChromeOS ACPI device and then exports the
* values reported by the ACPI in a sysfs directory. All values are
* presented in the string form (numbers as decimal values) and can be
* accessed as the contents of the appropriate read only files in the
* sysfs directory tree.
*/
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#define ACPI_ATTR_NAME_LEN 4
#define DEV_ATTR(_var, _name) \
static struct device_attribute dev_attr_##_var = \
__ATTR(_name, 0444, chromeos_first_level_attr_show, NULL);
#define GPIO_ATTR_GROUP(_group, _name, _num) \
static umode_t attr_is_visible_gpio_##_num(struct kobject *kobj, \
struct attribute *attr, int n) \
{ \
if (_num < chromeos_acpi_gpio_groups) \
return attr->mode; \
return 0; \
} \
static ssize_t chromeos_attr_show_gpio_##_num(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
char name[ACPI_ATTR_NAME_LEN + 1]; \
int ret, num; \
\
ret = parse_attr_name(attr->attr.name, name, &num); \
if (ret) \
return ret; \
return chromeos_acpi_evaluate_method(dev, _num, num, name, buf); \
} \
static struct device_attribute dev_attr_0_##_group = \
__ATTR(GPIO.0, 0444, chromeos_attr_show_gpio_##_num, NULL); \
static struct device_attribute dev_attr_1_##_group = \
__ATTR(GPIO.1, 0444, chromeos_attr_show_gpio_##_num, NULL); \
static struct device_attribute dev_attr_2_##_group = \
__ATTR(GPIO.2, 0444, chromeos_attr_show_gpio_##_num, NULL); \
static struct device_attribute dev_attr_3_##_group = \
__ATTR(GPIO.3, 0444, chromeos_attr_show_gpio_##_num, NULL); \
\
static struct attribute *attrs_##_group[] = { \
&dev_attr_0_##_group.attr, \
&dev_attr_1_##_group.attr, \
&dev_attr_2_##_group.attr, \
&dev_attr_3_##_group.attr, \
NULL \
}; \
static const struct attribute_group attr_group_##_group = { \
.name = _name, \
.is_visible = attr_is_visible_gpio_##_num, \
.attrs = attrs_##_group, \
};
static unsigned int chromeos_acpi_gpio_groups;
/* Parse the ACPI package and return the data related to that attribute */
static int chromeos_acpi_handle_package(struct device *dev, union acpi_object *obj,
int pkg_num, int sub_pkg_num, char *name, char *buf)
{
union acpi_object *element = obj->package.elements;
if (pkg_num >= obj->package.count)
return -EINVAL;
element += pkg_num;
if (element->type == ACPI_TYPE_PACKAGE) {
if (sub_pkg_num >= element->package.count)
return -EINVAL;
/* select sub element inside this package */
element = element->package.elements;
element += sub_pkg_num;
}
switch (element->type) {
case ACPI_TYPE_INTEGER:
return sysfs_emit(buf, "%d\n", (int)element->integer.value);
case ACPI_TYPE_STRING:
return sysfs_emit(buf, "%s\n", element->string.pointer);
case ACPI_TYPE_BUFFER:
return sysfs_emit(buf, "%s\n", element->buffer.pointer);
default:
dev_err(dev, "element type %d not supported\n", element->type);
return -EINVAL;
}
}
static int chromeos_acpi_evaluate_method(struct device *dev, int pkg_num, int sub_pkg_num,
char *name, char *buf)
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status;
int ret = -EINVAL;
status = acpi_evaluate_object(ACPI_HANDLE(dev), name, NULL, &output);
if (ACPI_FAILURE(status)) {
dev_err(dev, "failed to retrieve %s. %s\n", name, acpi_format_exception(status));
return ret;
}
if (((union acpi_object *)output.pointer)->type == ACPI_TYPE_PACKAGE)
ret = chromeos_acpi_handle_package(dev, output.pointer, pkg_num, sub_pkg_num,
name, buf);
kfree(output.pointer);
return ret;
}
static int parse_attr_name(const char *name, char *attr_name, int *attr_num)
{
int ret;
ret = strscpy(attr_name, name, ACPI_ATTR_NAME_LEN + 1);
if (ret == -E2BIG)
return kstrtoint(&name[ACPI_ATTR_NAME_LEN + 1], 0, attr_num);
return 0;
}
static ssize_t chromeos_first_level_attr_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
char attr_name[ACPI_ATTR_NAME_LEN + 1];
int ret, attr_num = 0;
ret = parse_attr_name(attr->attr.name, attr_name, &attr_num);
if (ret)
return ret;
return chromeos_acpi_evaluate_method(dev, attr_num, 0, attr_name, buf);
}
static unsigned int get_gpio_pkg_num(struct device *dev)
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
unsigned int count = 0;
char *name = "GPIO";
status = acpi_evaluate_object(ACPI_HANDLE(dev), name, NULL, &output);
if (ACPI_FAILURE(status)) {
dev_err(dev, "failed to retrieve %s. %s\n", name, acpi_format_exception(status));
return count;
}
obj = output.pointer;
if (obj->type == ACPI_TYPE_PACKAGE)
count = obj->package.count;
kfree(output.pointer);
return count;
}
DEV_ATTR(binf2, BINF.2)
DEV_ATTR(binf3, BINF.3)
DEV_ATTR(chsw, CHSW)
DEV_ATTR(fmap, FMAP)
DEV_ATTR(frid, FRID)
DEV_ATTR(fwid, FWID)
DEV_ATTR(hwid, HWID)
DEV_ATTR(meck, MECK)
DEV_ATTR(vbnv0, VBNV.0)
DEV_ATTR(vbnv1, VBNV.1)
DEV_ATTR(vdat, VDAT)
static struct attribute *first_level_attrs[] = {
&dev_attr_binf2.attr,
&dev_attr_binf3.attr,
&dev_attr_chsw.attr,
&dev_attr_fmap.attr,
&dev_attr_frid.attr,
&dev_attr_fwid.attr,
&dev_attr_hwid.attr,
&dev_attr_meck.attr,
&dev_attr_vbnv0.attr,
&dev_attr_vbnv1.attr,
&dev_attr_vdat.attr,
NULL
};
static const struct attribute_group first_level_attr_group = {
.attrs = first_level_attrs,
};
/*
* Every platform can have a different number of GPIO attribute groups.
* Define upper limit groups. At run time, the platform decides to show
* the present number of groups only, others are hidden.
*/
GPIO_ATTR_GROUP(gpio0, "GPIO.0", 0)
GPIO_ATTR_GROUP(gpio1, "GPIO.1", 1)
GPIO_ATTR_GROUP(gpio2, "GPIO.2", 2)
GPIO_ATTR_GROUP(gpio3, "GPIO.3", 3)
GPIO_ATTR_GROUP(gpio4, "GPIO.4", 4)
GPIO_ATTR_GROUP(gpio5, "GPIO.5", 5)
GPIO_ATTR_GROUP(gpio6, "GPIO.6", 6)
GPIO_ATTR_GROUP(gpio7, "GPIO.7", 7)
static const struct attribute_group *chromeos_acpi_all_groups[] = {
&first_level_attr_group,
&attr_group_gpio0,
&attr_group_gpio1,
&attr_group_gpio2,
&attr_group_gpio3,
&attr_group_gpio4,
&attr_group_gpio5,
&attr_group_gpio6,
&attr_group_gpio7,
NULL
};
static int chromeos_acpi_device_probe(struct platform_device *pdev)
{
chromeos_acpi_gpio_groups = get_gpio_pkg_num(&pdev->dev);
/*
* If the platform has more GPIO attribute groups than the number of
* groups this driver supports, give out a warning message.
*/
if (chromeos_acpi_gpio_groups > ARRAY_SIZE(chromeos_acpi_all_groups) - 2)
dev_warn(&pdev->dev, "Only %zu GPIO attr groups supported by the driver out of total %u.\n",
ARRAY_SIZE(chromeos_acpi_all_groups) - 2, chromeos_acpi_gpio_groups);
return 0;
}
/* GGL is valid PNP ID of Google. PNP ID can be used with the ACPI devices. */
static const struct acpi_device_id chromeos_device_ids[] = {
{ "GGL0001", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, chromeos_device_ids);
static struct platform_driver chromeos_acpi_device_driver = {
.probe = chromeos_acpi_device_probe,
.driver = {
.name = KBUILD_MODNAME,
.dev_groups = chromeos_acpi_all_groups,
.acpi_match_table = chromeos_device_ids,
}
};
module_platform_driver(chromeos_acpi_device_driver);
MODULE_AUTHOR("Muhammad Usama Anjum <usama.anjum@collabora.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ChromeOS specific ACPI extensions");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment