353 changes: 353 additions & 0 deletions Documentation/lib/fw_config.md
@@ -0,0 +1,353 @@
# Firmware Configuration Interface in coreboot

## Motivation

The firmware configuration interface in coreboot is designed to support a wide variety of
configuration options in that are dictated by the hardware at runtime. This allows a single
BIOS image to be used across a wide variety of devices which may have key differences but are
otherwise similar enough to use the same coreboot build target.

The initial implementation is designed to take advantage of a bitmask returned by the Embedded
Controller on Google Chrome OS devices which allows the manufacturer to use the same firmware
image across multiple devices by selecting various options at runtime. See the Chromium OS
[Firmware Config][1] documentation for more information.

This firmware configuration interface differs from the CMOS option interface in that this
bitmask value is not intended as a user-configurable setting as the configuration values must
match the actual hardware. In the case where a user was to swap their hardware this value
would need to be updated or overridden.

## Device Presence

One common example of why a firmware configuration interface is important is determining if a
device is present in the system. With some bus topologies and hardware mechanisms it is
possible to probe and enumerate this at runtime:

- PCI is a self-discoverable bus and is very easy to handle.
- I2C devices can often be probed with a combination of bus and address.
- The use of GPIOs with external strap to ground or different voltages can be used to detect
presence of a device.

However there are several cases where this is insufficient:

- I2C peripherals that require different drivers but have the same bus address cannot be
uniquely identified at runtime.
- A mainboard may be designed with multiple daughter board combinations which contain devices
and configurations that cannot be detected.
- While presence detect GPIOs are a convenient way for a single device presence, they are
unable to distinguish between different devices so it can require a large number of GPIOs to
support relatively few options.

This presence detection can impact different stages of boot:

### ACPI

Devices that are not present should not provide an ACPI device indicating that they are
present or the operating system may not be able to handle it correctly.

The ACPI devices are largely driven by chips defined in the mainboard `devicetree.cb` and
the variant overridetree.cb. This means it is important to be able to specify when a device
is present or not directly in `devicetree.cb` itself. Otherwise each mainboard needs custom
code to parse the tree and disable unused devices.

### GPIO

GPIOs with multiple functions may need to be configured correctly depending on the attached
device. Given the wide variety of GPIO configuration possibilities it is not feasible to
specify all combinations directly in `devicetree.cb` and it is best left to code provided by
the mainboard.

### FSP UPD

Enabling and disabling devices may require altering FSP UPD values that are provided to the
various stages of FSP. These options are also not easy to specify multiple times for
different configurations in `devicetree.cb` and can be provided by the mainboard as code.

## Firmware Configuration Interface

The firmware configuration interface can be enabled by selecting `CONFIG_FW_CONFIG` and also
providing a source for the value by defining an additional Kconfig option defined below.

If the firmware configuration interface is disabled via Kconfig then all probe attempts will
return true.

## Firmware Configuration Value

The 32bit value used as the firmware configuration bitmask is meant to be determined at runtime
but could also be defined at compile time if needed.

There are two supported sources for providing this information to coreboot.

### CBFS

The value can be provided with a 32bit raw value in CBFS that is read by coreboot. The value
can be set at build time but also adjusted in an existing image with `cbfstool`.

To enable this select the `CONFIG_FW_CONFIG_CBFS` option in the build configuration and add a
raw 32bit value to CBFS with the name of the current prefix at `CONFIG_FW_PREFIX/fw_config`.

When `fw_config_probe_device()` or `fw_config_probe()` is called it will look for the specified
file in CBFS use the value it contains when matching fields and options.

### Embedded Controller

Google Chrome OS devices support an Embedded Controller interface for reading and writing the
firmware configuration value, along with other board-specific information. It is possible for
coreboot to read this value at boot on systems that support this feature.

This option is selected by default for the mainboards that use it with
`CONFIG_FW_CONFIG_CHROME_EC_CBI` and it is not typically necessary to adjust the value. It is
possible by enabling the CBFS source and coreboot will look in CBFS first for a valid value
before asking the embedded controller.

It is also possible to adjust the value in the embedded controller *(after disabling write
protection)* with the `ectool` command in a Chrome OS environment.

For more information on the firmware configuration field on Chrome OS devices see the Chromium
documentation for [Firmware Config][1] and [Board Info][2].

[1]: http://chromium.googlesource.com/chromiumos/docs/+/master/design_docs/firmware_config.md
[2]: http://chromium.googlesource.com/chromiumos/docs/+/master/design_docs/cros_board_info.md

## Firmware Configuration Table

The firmware configuration table itself is defined in the mainboard `devicetree.cb` with
special tokens for defining fields and options.

The table itself is enclosed in a `fw_config` token and terminated with `end` and it contains
a mix of field and option definitions.

Each field is defined by providing the field name and the start and end bit marking the exact
location in the bitmask. Field names must be at least three characters long in order to
satisfy the sconfig parser requirements and they must be unique with non-overlapping masks.

field <name> <start-bit> <end-bit> [option...] end

For single-bit fields only one number is needed:

field <name> <bit> [option...] end

Each `field` definition starts a new block that can be composed of zero or more field options,
and it is terminated with `end`.

Inside the field block the options can be defined by providing the option name and the field
value that this option represents when the bit offsets are used to apply a mask and shift.
Option names must also be at least three characters for the sconfig parser.

option <name> <value>

It is possible for there to be multiple `fw_config` blocks and for subsequent `field` blocks
to add additional `option` definitions to the existing field. These subsequent definitions
should not provide the field bitmask as it has already been defined earlier in the file and
this is just matching an existing field by name.

field <name> [option...] end

This allows a baseboard to define the major fields and options in `devicetree.cb` and a board
variant to add specific options to fields in or define new fields in the unused bitmask in
`overridetree.cb`.

It is not possible to redefine a field mask or override the value of an existing option this
way, only to add new options to a field or new fields to the table.

### Firmware Configuration Table Example

In this example a baseboard defines a simple boolean feature that is enabled or disabled
depending on the value of bit 0, and a field at bits 1-2 that indicates which daughter board
is attached.

The baseboard itself defines one daughter board and the variant adds two more possibilities.
This way each variant can support multiple possible daughter boards in addition to the one
that was defined by the baseboard.

#### devicetree.cb

fw_config
field FEATURE 0
option DISABLED 0
option ENABLED 1
end
field DAUGHTER_BOARD 1 2
option NONE 0
option REFERENCE_DB 1
end
end

#### overridetree.cb

fw_config
field DAUGHTER_BOARD
option VARIANT_DB_ONE 2
option VARIANT_DB_TWO 3
end
end

The result of this table defined in `devicetree.cb` is a list of constants that can be used
to check if fields match the firmware configuration options determined at runtime with a
simple check of the field mask and the option value.

#### static.h

```c
/* field: FEATURE */
#define FW_CONFIG_FIELD_FEATURE_NAME "FEATURE"
#define FW_CONFIG_FIELD_FEATURE_MASK 0x00000001
#define FW_CONFIG_FIELD_FEATURE_OPTION_DISABLED_NAME "DISABLED"
#define FW_CONFIG_FIELD_FEATURE_OPTION_DISABLED_VALUE 0x00000000
#define FW_CONFIG_FIELD_FEATURE_OPTION_ENABLED_NAME "ENABLED"
#define FW_CONFIG_FIELD_FEATURE_OPTION_ENABLED_VALUE 0x00000001

/* field: DAUGHTER_BOARD */
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME "DAUGHTER_BOARD"
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK 0x00000006
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_NONE_NAME "NONE"
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_NONE_VALUE 0x00000000
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_REFERENCE_DB_NAME "REFERENCE_DB"
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_REFERENCE_DB_VALUE 0x00000002
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_NAME "VARIANT_DB_ONE"
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_VALUE 0x00000004
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_NAME "VARIANT_DB_TWO"
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_VALUE 0x00000006
```
## Device Probing
One use of the firmware configuration interface in devicetree is to allow device probing to be
specified directly with the devices themselves. A new `probe` token is introduced to allow a
device to be probed by field and option name. Multiple `probe` entries may be present for
each device and any successful probe will consider the device to be present.
### Probing Example
Continuing with the previous example this device would be considered present if the field
`DAUGHTER_BOARD` was set to either `VARIANT_DB_ONE` or `VARIANT_DB_TWO`:
#### overridetree.cb
chip drivers/generic/example
device generic 0 on
probe DAUGHTER_BOARD VARIANT_DB_ONE
probe DAUGHTER_BOARD VARIANT_DB_TWO
end
end
If the field were set to any other option, including `NONE` and `REFERENCE_DB` and any
undefined value then the device would be disabled.
### Probe Overrides
When a device is declared with a probe in the baseboard `devicetree.cb` and the same device
is also present in the `overridetree.cb` then the probing information from the baseboard
is discarded and the override device must provide all necessary probing information.
In this example a device is listed in the baseboard with `DAUGHTER_BOARD` field probing for
`REFERENCE_DB` as a field option, It is also defined as an override device with the field
probing for the `VARIANT_DB_ONE` option instead.
In this case only the probe listed in the override is checked and a field option of
`REFERENCE_DB` will not mark this device present. If both options are desired then the
override device must list both. This allows an override device to remove a probe entry that
was defined in the baseboard.
#### devicetree.cb
chip drivers/generic/example
device generic 0 on
probe DAUGHTER_BOARD REFERENCE_DB
end
end
#### overridetree.cb
chip drivers/generic/example
device generic 0 on
probe DAUGHTER_BOARD VARIANT_DB_ONE
end
end
### Automatic Device Probing
At boot time the firmware configuration interface will walk the device tree and apply any
probe entries that were defined in `devicetree.cb`. This probing takes effect before the
`BS_DEV_ENUMERATE` step during the boot state machine in ramstage.
Devices that have a probe list but do do not find a match are disabled by setting
`dev->enabled = 0` but the chip `enable_dev()` and device `enable()` handlers will still
be executed to allow any device disable code to execute.
The result of this probe definition is to provide an array of structures describing each
field and option to check.
#### fw_config.h
```c
/**
* struct fw_config - Firmware configuration field and option.
* @field_name: Name of the field that this option belongs to.
* @option_name: Name of the option within this field.
* @mask: Bitmask of the field.
* @value: Value of the option within the mask.
*/
struct fw_config {
const char *field_name;
const char *option_name;
uint32_t mask;
uint32_t value;
};
```

#### static.c

```c
STORAGE struct fw_config __devN_probe_list[] = {
{
.field_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME,
.option_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_NAME,
.mask = FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK,
.value = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_VALUE
},
{
.field_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME,
.option_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_NAME,
.mask = FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK,
.value = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_VALUE
},
{ }
};
```

### Runtime Probing

The device driver probing allows for seamless integration with the mainboard but it is only
effective in ramstage and for specific devices declared in devicetree.cb. There are other
situations where code may need to probe or check the value of a field in romstage or at other
points in ramstage. For this reason it is also possible to use the firmware configuration
interface directly.

```c
/**
* fw_config_probe() - Check if field and option matches.
* @match: Structure containing field and option to probe.
*
* Return %true if match is found, %false if match is not found.
*/
bool fw_config_probe(const struct fw_config *match);
```
The argument provided to this function can be created from a macro for easy use:
FW_CONFIG(field, option)
This example has a mainboard check if a feature is disabled and set an FSP UPD before memory
training. This example expects that the default value of this `register` is set to `true` in
`devicetree.cb` and this code is disabling that feature before FSP is executed.
```c
#include <fw_config.h>
void mainboard_memory_init_params(FSPM_UPD *mupd)
{
if (fw_config_probe_one(FW_CONFIG(FEATURE, DISABLED))
mupd->ExampleFeature = false;
}
```
2 changes: 1 addition & 1 deletion Documentation/lib/index.md
Expand Up @@ -3,7 +3,7 @@
This section contains documentation about coreboot internal technical
information and libraries.

## Structure and layout
- [Flashmap and Flashmap Descriptor](flashmap.md)
- [ABI data consumption](abi-data-consumption.md)
- [Timestamps](timestamp.md)
- [Firmware Configuration Interface](fw_config.md)
2 changes: 1 addition & 1 deletion Documentation/mainboard/gigabyte/ga-h61m-s2pv.md
Expand Up @@ -15,7 +15,7 @@ from [Gigabyte].
+---------------------+------------+
| Size | 4 MiB |
+---------------------+------------+
| In circuit flashing | Yes |
| In circuit flashing | No |
+---------------------+------------+
| Package | SOIC-8 |
+---------------------+------------+
Expand Down
8 changes: 8 additions & 0 deletions Documentation/mainboard/index.md
Expand Up @@ -116,6 +116,10 @@ The boards in this section are not real mainboards, but emulators.

- [MS-7707](msi/ms7707/ms7707.md)

## OCP

- [Tioga Pass](ocp/tiogapass.md)

## Open Cellular

- [Elgon](opencellular/elgon.md)
Expand All @@ -130,6 +134,10 @@ The boards in this section are not real mainboards, but emulators.

- [PQ7-M107](portwell/pq7-m107.md)

## Prodrive

- [Hermes](prodrive/hermes.md)

## Protectli

- [FW2B / FW4B](protectli/fw2b_fw4b.md)
Expand Down
100 changes: 100 additions & 0 deletions Documentation/mainboard/ocp/tiogapass.md
@@ -0,0 +1,100 @@
# OCP Tioga Pass

This page describes coreboot support status for the [OCP] (Open Compute Project)
Tioga Pass server platform.

## Introduction

OCP Tioga Pass server platform was contributed by Facebook, and was accepted
in 2019. The design collateral including datasheet can be found at [OCP Tioga Pass].

Since complete EE design collateral is open sourced, anyone can build server
as-is or a variant based on the original design. It can also be purchased from [OCP Market Place].
An off-the-shelf version is available, as well as rack ready version. With the
off-the-shelf version, the server can be plugged into wall power outlet.

With the off-the-shelf version of Tioga Pass, a complete software solution is
available. [Off-the-shelf Host Firmware] takes the approach of UEFI/Linuxboot.

coreboot as of release 4.13 is a proof-of-concept project between Facebook,
Intel, Wiwynn and Quanta. The context is described at [OCP Tioga Pass POC Blog].

## Required blobs

This board currently requires:
- FSP blob: The blob (Intel Skylake Scalable Processor Firmware Support Package)
is not yet available to the public. The binary is at POC status, hopefully
someday an IBV is able to obtain the privilege to maintain it.
- Microcode: `3rdparty/intel-microcode/intel-ucode/06-55-04`
- ME binary: The binary can be extracted from [Off-the-shelf Host Firmware].

## Payload
- Linuxboot: This is necessary only if you use Linuxboot as coreboot payload.
U-root as initramfs, is used in the POC activity. It can be extracted from
[Off-the-shelf Host Firmware], or it can be built following [All about u-root].

## Flashing coreboot

To do in-band FW image update, use [flashrom]:
flashrom -p internal:ich_spi_mode=hwseq -c "Opaque flash chip" --ifd \
-i bios --noverify-all -w <path to coreboot image>

From OpenBMC, to update FW image:
fw-util mb --force --update <path to coreboot image>

To power off/on the host:
power-util mb off
power-util mb on

To connect to console through SOL (Serial Over Lan):
sol-util mb

## Known issues / feature gaps
- C6 state is not supported. Workaround is to disable C6 support through
target OS and Linuxboot kernel paramter, such as "cpuidle.off=1".
- SMI handlers are not implemented.
- xSDT tables are not fully populated, such as processor/socket devices,
PCIe bridge devices.
- There is boot stability issue. Occasionally the boot hangs at ramstage
with following message "BIOS PCU Misc Config Read timed out."
- If [CB 40500 patchset] is not merged, when PCIe riser card is used,
boot fails.
- PCIe devices connected to socket 1 may not work, because FSP
does not support PCIe topology input for socket 1.k
- SMBIOS type 7 and type 17 are not populated.

## Working
The solution was developed using Linuxboot payload. The Linuxboot
kernel versions tried are 4.16.18 and 5.2.9. The initramfs image is
u-root.
- Most SMBIOS types
- BMC integration:
- BMC readiness check
- IPMI commands
- watchdog timer
- POST complete pin acknowledgement
- SEL record generation
- Early serial output
- port 80h direct to GPIO
- ACPI tables: APIC/DMAR/DSDT/FACP/FACS/HPET/MCFG/SPMI/SRAT/SLIT/SSDT

## Technology

```eval_rst
+------------------------+---------------------------------------------+
| Processor (2 sockets) | Intel Skylake Scalable Processor LGA3647 |
+------------------------+---------------------------------------------+
| BMC | Aspeed AST 2500 |
+------------------------+---------------------------------------------+
| PCH | Intel Lewisburg C621 |
+------------------------+---------------------------------------------+
```

[flashrom]: https://flashrom.org/Flashrom
[OCP]: https://www.opencompute.org/
[OCP Tioga Pass]: https://www.opencompute.org/contributions?query=Tioga%20Pass%20v1.0
[OCP Market Place]: https://www.opencompute.org/products/109/wiwynn-tioga-pass-advanced-2u-ocp-server-up-to-768gb-12-dimm-slots-4-ssds-for-io-performance
[Off-the-shelf Host Firmware]: https://github.com/linuxboot/book/blob/master/case_studies/TiogaPass/README.md
[OCP Tioga Pass POC Blog]: https://www.opencompute.org/blog/linux-firmware-boots-up-server-powered-by-intelr-xeonr-scalable-processor
[All about u-root]: https://github.com/linuxboot/book/tree/master/u-root
[CB 40500 patchset]: https://review.coreboot.org/c/coreboot/+/40500
54 changes: 54 additions & 0 deletions Documentation/mainboard/prodrive/hermes.md
@@ -0,0 +1,54 @@
# Hermes

Hermes is a regular ATX board designed for workstation PCs.

The board features:
* 5 PCIe 16x Gen3 slots
* 4 ECC capable DDR4 DIMMs
* 5 dedicated Ethernet ports
* 1 BMC Ethernet port
* VGA
* COM port
* 2 COM port headers
* 4 SATA ports,
* NVMe M2 slot
* CNVi M2 slot
* 3 optional DisplayPort outputs
* optional TPM2

## Required proprietary blobs

- [Intel FSP2.0]
- Intel SPS

## Flashing coreboot

* The BIOS flash can be updated over the BMC, but the update file has a proprietary format
* For development a dediprog compatible pinheader is present which allows to use an EM100

## Known issues

- MRC caching does not work on cold boot with Intel SPS (see [Intel FSP2.0])

## Technology

```eval_rst
+------------------+--------------------------------------------------+
| CPU | CoffeeLake + CoffeeLake R (Core + Xeon) |
+------------------+--------------------------------------------------+
| PCH | Intel C246 |
+------------------+--------------------------------------------------+
| Coprocessor | Intel SPS (server version of the ME) |
+------------------+--------------------------------------------------+
| Super I/O | none |
+------------------+--------------------------------------------------+
| BMC | Aspeed AST2500 |
+------------------+--------------------------------------------------+
```

## Extra links

[flashrom]: https://flashrom.org/Flashrom
[flashing tutorial]: ../../../../flash_tutorial/ext_power.md
[Intel FSP2.0]: ../../../../soc/intel/fsp/index.md
[AST2500]: https://www.aspeedtech.com/products.php?fPath=20&rId=440
26 changes: 26 additions & 0 deletions Documentation/releases/coreboot-4.13-relnotes.md
Expand Up @@ -13,4 +13,30 @@ Update this document with changes that should be in the release notes.
Significant changes
-------------------

### Hidden PCI devices

This new functionality takes advantage of the existing 'hidden' keyword in the
devicetree. Since no existing boards were using the keyword, its usage was
repurposed to make dealing with some unique PCI devices easier. The particular
case here is Intel's PMC (Power Management Controller). During the FSP-S run,
the PMC device is made hidden, meaning that its config space looks as if there
is no device there (Vendor ID reads as 0xFFFF_FFFF). However, the device does
have fixed resources, both MMIO and I/O. These were previously recorded in
different places (MMIO was typically an SA fixed resource, and I/O was treated
as an LPC resource). With this change, when a device in the tree is marked as
'hidden', it is not probed (`pci_probe_dev()`) but rather assumed to exist so
that its resources can be placed in a more natural location. This also adds the
ability for the device to participate in SSDT generation.

### Tools for generating SPDs for LP4x memory on TGL and JSL

A set of new tools `gen_spd.go` and `gen_part_id.go` are added to automate the
process of generating SPDs for LP4x memory and assigning hardware strap IDs for
memory parts used on TGL and JSL based boards. The SPD data obtained from memory
part vendors has to be massaged to format it correctly as per JEDEC and Intel MRC
expectations. These tools take a list of memory parts describing their physical
attributes as per their datasheet and convert those attributes into SPD files for
the platforms. More details about the tools are added in
[README.md](https://review.coreboot.org/plugins/gitiles/coreboot/+/refs/heads/master/util/spd_tools/intel/lp4x/README.md).

### Add significant changes here
4 changes: 4 additions & 0 deletions Documentation/security/index.md
Expand Up @@ -13,3 +13,7 @@ This section describes documentation about the security architecture of coreboot
- [Intel TXT in general](intel/txt.md)
- [Intel TXT Initial Boot Block](intel/txt_ibb.md)
- [Intel Authenticated Code Modules](intel/acm.md)

## SMM

- [System Management Mode](smm.md)
29 changes: 29 additions & 0 deletions Documentation/security/smm.md
@@ -0,0 +1,29 @@
# x86 System Managment Mode

## Introduction

The code running in System Management Mode (SMM) provides runtime services
to applications running in [ring0]. It has a higher privilege level than
[ring0] and resides in the SMRAM region which cannot be accessed from [ring0].

SMM can be entered by issuing System Managment Interrupts (SMIs).

## Secure data exchange

In order to not leak SMM internals or accidentally overwrite parts of SMM,
[ring0] provided data (pointers, offsets, sizes, ...) must be checked before
using them in SMM.

There exist two methods to verify data:

```C
/* Returns true if the region overlaps with the SMM */
bool smm_region_overlaps_handler(struct region *r);
```
```C
/* Returns true if the memory pointed to overlaps with SMM reserved memory. */
static inline bool smm_points_to_smram(const void *ptr, const size_t len);
```

[ring0]: https://en.wikipedia.org/wiki/Protection_ring
39 changes: 2 additions & 37 deletions Documentation/technotes/2020-03-unit-testing-coreboot.md
Expand Up @@ -279,41 +279,6 @@ tests/lib/string-test and tests/device/i2c-test:
├── i2c.o
```

### Adding new tests
For purpose of this description, let's assume that we want to add a new unit test
for src/device/i2c.c module. Since this module is rather simple, it will be enough
to have only one test module.

Firstly (assuming there is no tests/device/Makefile.inc file) we need to create
Makefile.inc in main unit test module directory. Inside this Makefile.inc, one
need to register new test and can specify multiple different attributes for it.

```bash
# Register new test, by adding its name to tests variable
tests-y += i2c-test

# All attributes are defined by <test_name>-<attribute> variables
# <test_name>-srcs is used to register all input files (test harness, unit under
# test and others) for this particular test. Remember to add relative paths.
i2c-test-srcs += tests/device/i2c-test.c
i2c-test-srcs += src/device/i2c.c

# We can define extra cflags for this particular test
i2c-test-cflags += -DSOME_DEFINE=1

# For mocking out external dependencies (functions which cannot be resolved by
# linker), it is possible to register a mock function. To register new mock, it
# is enough to add function-to-be-mocked name to <test_name>-mocks variable.
i2c-test-mocks += platform_i2c_transfer

# Similar to coreboot concept, unit tests also runs in the context of stages.
# By default all unit tests are compiled to be ramstage executables. If one want
# to overwrite this setting, there is <test_name>-stage variable available.
i2c-test-stage:= bootblock
```

### Writing new tests
Full description of how to write unit tests and Cmocka API description is out of
the scope of this document. There are other documents related to this
[Cmocka API](https://api.cmocka.org/) and
[Mocks](https://lwn.net/Articles/558106/).
Our tutorial series has [detailed guidelines](../tutorial/part3.md) for writing
unit tests.
1 change: 1 addition & 0 deletions Documentation/tutorial/index.md
Expand Up @@ -2,3 +2,4 @@

* [Part 1: Starting from scratch](part1.md)
* [Part 2: Submitting a patch to coreboot.org](part2.md)
* [Part 3: Writing unit tests](part3.md)
384 changes: 384 additions & 0 deletions Documentation/tutorial/part3.md
@@ -0,0 +1,384 @@
# Writing unit tests for coreboot

## Introduction
General thoughts about unit testing coreboot can be found in
[Unit testing coreboot](../technotes/2020-03-unit-testing-coreboot.md).

This document aims to guide developers through the process of adding and writing
unit tests for coreboot modules.

As an example of unit under test, `src/device/i2c.c` (referred hereafter as UUT
"Unit Under Test") will be used. This is simple module, thus it should be easy
for the reader to focus solely on the testing logic, without the need to spend
too much time on digging deeply into the source code details and flow of
operations. That being said, a good understanding of what the unit under test is
doing is crucial for writing unit tests.

This tutorial should also be helpful for developers who want to follow
[TDD](https://en.wikipedia.org/wiki/Test-driven_development). Even though TDD
has a different work flow of building tests first, followed by the code that
satisfies them, the process of writing tests and adding them to the tree is the
same.

## Analysis of unit under test
First of all, it is necessary to precisely establish what we want to test in a
particular module. Usually this will be an externally exposed API, which can be
used by other modules.

```eval_rst
.. admonition:: i2c-test example
In case of our UUT, API consist of two methods:
.. code-block:: c
int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t *data,
uint8_t mask, uint8_t shift)
int i2c_write_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t data,
uint8_t mask, uint8_t shift)
For sake of simplicity, let's focus on `i2c_read_field` in this document.
```

Once the API is defined, the next question is __what__ this API is doing (or
what it will be doing in case of TDD). In other words, what outputs we are
expecting from particular functions, when providing particular input parameters.

```eval_rst
.. admonition:: i2c-test example
.. code-block:: c
int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t *data,
uint8_t mask, uint8_t shift)
This is a method which means to read content of register `reg` from i2c device
on i2c `bus` and slave address `chip`, applying bit `mask` and offset `shift`
to it. Returned data should be placed in `data`.
```

The next step is to determine all external dependencies of UUT in order to mock
them out. Usually we want to isolate the UUT as much as possible, so that the
test result depends __only__ on the behavior of UUT and not on the other
modules. While some software dependencies may be hard to be mock (for example
due to complicated dependencies) and thus should be simply linked into the test
binaries, all hardware dependencies need to be mocked out, since in the
user-space host environment, targets hardware is not available.

```eval_rst
.. admonition:: i2c-test example
`i2c_read_field` is calling `i2c_readb`, which eventually invokes
`i2c_transfer`. This method simply calls `platform_i2c_transfer`. The last
function in the chain is a hardware-touching one, and defined separately for
different SOCs. It is responsible for issuing transactions on the i2c bus.
For the purpose of writing unit test, we should mock this function.
```

## Adding new tests
In order to keep the tree clean, the `tests/` directory should mimic the `src/`
directory, so that test harness code is placed in a location corresponding to
UUT. Furthermore, the naming convention is to add the suffix `-test` to the UUT
name when creating a new test harness file.

```eval_rst
.. admonition:: i2c-test example
Considering that UUT is `src/device/i2c.c`, test file should be named
`tests/device/i2c-test.c`. When adding a new test file, it needs to be
registered with the coreboot unit testing infrastructure.
```

Every directory under `tests/` should contain a Makefile.inc, similar to what
can be seen under the `src/`. Register a new test in Makefile.inc, by
__appending__ test name to the `tests-y` variable.

```eval_rst
.. admonition:: i2c-test example
.. code-block:: c
tests-y += i2c-test
```

Next step is to list all source files, which should be linked together in order
to create test binary. Usually a tests requires only two files - UUT and test
harness code, but sometimes more is needed to provide the test environment.
Source files are registered in `<test_name>-srcs` variable.

```eval_rst
.. admonition:: i2c-test example
.. code-block:: c
i2c-test-srcs += tests/device/i2c-test.c
i2c-test-srcs += src/device/i2c.c
```

Above minimal configuration is a basis for further work. One can try to build
and run test binary either by invoking `make tests/<test_dir>/<test_name>` or by
running all unit tests (whole suite) for coreboot `make unit-tests`.

```eval_rst
.. admonition:: i2c-test example
.. code-block:: c
make tests/device/i2c-test
or
.. code-block:: c
make unit-tests
```

When trying to build test binary, one can often see linker complains about
`undefined reference` to couple of symbols. This is one of solutions to
determine all external dependencies of UUT - iteratively build test and resolve
errors one by one. At this step, developer should decide either it's better to
add an extra module to provide necessary definitions or rather mock such
dependency. Quick guide through adding mocks is provided later in this doc.

## Writing new tests
In coreboot, [Cmocka](https://cmocka.org/) is used as unit test framework. The
project has exhaustive [API documentation](https://api.cmocka.org/). Let's see
how we may incorporate it when writing tests.

### Assertions
Testing the UUT consists of calling the functions in the UUT and comparing the
returned values to the expected values. Cmocka implements
[a set of assert macros](https://api.cmocka.org/group__cmocka__asserts.html) to
compare a value with an expected value. If the two values do not match, the test
fails with an error message.

```eval_rst
.. admonition:: i2c-test example
In our example, the simplest test is to call UUT for reading our fake devices
registers and do all calculation in the test harness itself. At the end, let's
compare integers with `assert_int_equal`.
.. code-block:: c
#define MASK 0x3
#define SHIFT 0x1
static void i2c_read_field_test(void **state)
{
int bus, slave, reg;
int i, j;
uint8_t buf;
mock_expect_params_platform_i2c_transfer();
/* Read particular bits in all registers in all devices, then compare
with expected value. */
for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++)
for (j = 0; j < ARRAY_SIZE(i2c_ex_devs[0].regs); j++) {
i2c_read_field(i2c_ex_devs[i].bus,
i2c_ex_devs[i].slave,
i2c_ex_devs[i].regs[j].reg,
&buf, MASK, SHIFT);
assert_int_equal((i2c_ex_devs[i].regs[j].data &
(MASK << SHIFT)) >> SHIFT, buf);
};
}
```

### Mocks

#### Overview
Many coreboot modules are low level software that touch hardware directly.
Because of this, one of the most important and challenging part of
writing tests is to design and implement mocks. A mock is a software component
which implements the API of another component so that the test can verify that
certain functions are called (or not called), verify the parameters passed to
those functions, and specify the return values from those functions. Mocks are
especially useful when the API to be implemented is one that accesses hardware
components.

When writing a mock, the developer implements the same API as the module being
mocked. Such a mock may, for example, register a set of driver methods. Behind
this API, there is usually a simulation of real hardware.

```eval_rst
.. admonition:: i2c-test example
For purpose of our i2c test, we may introduce two i2c devices with set of
registers, which simply are structs in memory.
.. code-block:: c
/* Simulate two i2c devices, both on bus 0, each with three uint8_t regs
implemented. */
typedef struct {
uint8_t reg;
uint8_t data;
} i2c_ex_regs_t;
typedef struct {
unsigned int bus;
uint8_t slave;
i2c_ex_regs_t regs[3];
} i2c_ex_devs_t;
i2c_ex_devs_t i2c_ex_devs[] = {
{.bus = 0, .slave = 0xA, .regs = {
{.reg = 0x0, .data = 0xB},
{.reg = 0x1, .data = 0x6},
{.reg = 0x2, .data = 0xF},
} },
{.bus = 0, .slave = 0x3, .regs = {
{.reg = 0x0, .data = 0xDE},
{.reg = 0x1, .data = 0xAD},
{.reg = 0x2, .data = 0xBE},
} },
};
These fake devices will be accessed instead of hardware ones:
.. code-block:: c
reg = tmp->buf[0];
/* Find object for requested device */
for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++, i2c_dev++)
if (i2c_ex_devs[i].slave == tmp->slave) {
i2c_dev = &i2c_ex_devs[i];
break;
}
if (i2c_dev == NULL)
return -1;
/* Write commands */
if (tmp->len > 1) {
i2c_dev->regs[reg].data = tmp->buf[1];
};
/* Read commands */
for (i = 0; i < count; i++, tmp++)
if (tmp->flags & I2C_M_RD) {
*(tmp->buf) = i2c_dev->regs[reg].data;
};
```

Cmocka uses a feature that gcc provides for breaking dependencies at the link
time. It is possible to override implementation of some function, with the
method from test harness. This allows test harness to take control of execution
from binary (during the execution of test), and stimulate UUT as required
without changing the source code.

coreboot unit test infrastructure supports overriding of functions at link time.
This is as simple as adding a `name_of_function` to be mocked into
<test_name>-mocks variable in Makefile.inc. The result is that every time the
function is called, `wrap_name_of_function` will be called instead.

```eval_rst
.. admonition:: i2c-test example
.. code-block:: c
i2c-test-mocks += platform_i2c_transfer
Now, dev can write own implementation of `platform_i2c_transfer` and define it
as `wrap_platform_i2c_transfer`. This implementation instead of accessing real
i2c bus, will write/read from fake structs.
.. code-block:: c
int __wrap_platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
int count)
{
}
```

#### Checking mock's arguments
A test can verify the parameters provided by the UUT to the mock function. The
developer may also verify that number of calls to mock is correct and the order
of calls to particular mocks is as expected (See
[this](https://api.cmocka.org/group__cmocka__call__order.html)). The Cmocka
macros for checking parameters are described
[here](https://api.cmocka.org/group__cmocka__param.html). In general, in mock
function, one makes a call to `check_expected(<param_name>)` and in the
corresponding test function, `expect*()` macro, with description which parameter
in which mock should have particular value, or be inside a described range.

```eval_rst
.. admonition:: i2c-test example
In our example, we may want to check that `platform_i2c_transfer` is fed with
number of segments bigger than 0, each segment has flags which are in
supported range and each segment has buf which is non-NULL. We are expecting
such values for _every_ call, thus the last parameter in `expect*` macros is
-1.
.. code-block:: c
static void mock_expect_params_platform_i2c_transfer(void)
{
unsigned long int expected_flags[] = {0, I2C_M_RD, I2C_M_TEN,
I2C_M_RECV_LEN, I2C_M_NOSTART};
/* Flags should always be only within supported range */
expect_in_set_count(__wrap_platform_i2c_transfer, segments->flags,
expected_flags, -1);
expect_not_value_count(__wrap_platform_i2c_transfer, segments->buf,
NULL, -1);
expect_in_range_count(__wrap_platform_i2c_transfer, count, 1, INT_MAX,
-1);
}
And the checks below should be added to our mock
.. code-block:: c
check_expected(count);
for (i = 0; i < count; i++, segments++) {
check_expected_ptr(segments->buf);
check_expected(segments->flags);
}
```

#### Instrument mocks
It is possible for the test function to instrument what the mock will return to
the UUT. This can be done by using the `will_return*()` and `mock()` macros.
These are described in
[the Mock Object section](https://api.cmocka.org/group__cmocka__mock.html) of
the Cmocka API documentation.

```eval_rst
.. admonition:: Example
There is an non-coreboot example for using Cmocka available
`here <https://lwn.net/Articles/558106/>`_.
```

### Test runner
Finally, the developer needs to implement the test `main()` function. All tests
should be registered there and cmocka test runner invoked. All methods for
invoking Cmocka test are described
[here](https://api.cmocka.org/group__cmocka__exec.html).

```eval_rst
.. admonition:: i2c-test example
We don't need any extra setup and teardown functions for i2c-test, so let's
simply register test for `i2c_read_field` and return from main value which is
output of Cmocka's runner (it returns number of tests that failed).
.. code-block:: c
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(i2c_read_field_test),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
```
564 changes: 334 additions & 230 deletions MAINTAINERS

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -103,7 +103,7 @@ ifeq ($(strip $(HAVE_DOTCONFIG)),)
NOCOMPILE:=1
endif
ifneq ($(MAKECMDGOALS),)
ifneq ($(filter %config %clean cross% clang iasl gnumake lint% help% what-jenkins-does,$(MAKECMDGOALS)),)
ifneq ($(filter %config %clean cross% clang iasl lint% help% what-jenkins-does,$(MAKECMDGOALS)),)
NOCOMPILE:=1
endif
ifneq ($(filter %clean lint% help% what-jenkins-does,$(MAKECMDGOALS)),)
Expand Down
24 changes: 19 additions & 5 deletions Makefile.inc
Expand Up @@ -13,6 +13,7 @@ CONFIG_CBFS_PREFIX:=$(call strip_quotes,$(CONFIG_CBFS_PREFIX))
CONFIG_FMDFILE:=$(call strip_quotes,$(CONFIG_FMDFILE))
CONFIG_DEVICETREE:=$(call strip_quotes, $(CONFIG_DEVICETREE))
CONFIG_OVERRIDE_DEVICETREE:=$(call strip_quotes, $(CONFIG_OVERRIDE_DEVICETREE))
CONFIG_MEMLAYOUT_LD_FILE:=$(call strip_quotes, $(CONFIG_MEMLAYOUT_LD_FILE))

#######################################################################
# misleadingly named, this is the coreboot version
Expand Down Expand Up @@ -98,6 +99,13 @@ classes-y := ramstage romstage bootblock decompressor postcar smm smmstub cpu_mi
$(call add-special-class,all)
all-handler = $(foreach class,bootblock verstage romstage postcar ramstage,$(eval $(class)-y += $(2)))

$(call add-special-class,verstage_x86)
ifeq ($(CONFIG_ARCH_VERSTAGE_X86_32)$(CONFIG_ARCH_VERSTAGE_X86_64),y)
verstage_x86-handler = $(eval verstage-y += $(2))
else
verstage_x86-handler =
endif

# Add dynamic classes for rmodules
$(foreach supported_arch,$(ARCH_SUPPORTED), \
$(eval $(call define_class,rmodules_$(supported_arch),$(supported_arch))))
Expand Down Expand Up @@ -609,6 +617,13 @@ smm-c-deps+=$(DEVICETREE_STATIC_C)
.PHONY: devicetree
devicetree: $(DEVICETREE_STATIC_C)

ramstage-y += $(CONFIG_MEMLAYOUT_LD_FILE)
romstage-y += $(CONFIG_MEMLAYOUT_LD_FILE)
bootblock-y += $(CONFIG_MEMLAYOUT_LD_FILE)
verstage-y += $(CONFIG_MEMLAYOUT_LD_FILE)
postcar-y += $(CONFIG_MEMLAYOUT_LD_FILE)
decompressor-y += $(CONFIG_MEMLAYOUT_LD_FILE)

#######################################################################
# Clean up rules
clean-abuild:
Expand Down Expand Up @@ -1172,11 +1187,10 @@ $(CONFIG_CBFS_PREFIX)/romstage-options := -S ".car.data"
ifneq ($(CONFIG_NO_XIP_EARLY_STAGES),y)
$(CONFIG_CBFS_PREFIX)/romstage-options += --xip

# If XIP_ROM_SIZE isn't being used don't overly constrain romstage by passing
# -P with a default value.
ifneq ($(CONFIG_NO_FIXED_XIP_ROM_SIZE),y)
$(CONFIG_CBFS_PREFIX)/romstage-options += -P $(CONFIG_XIP_ROM_SIZE)
endif # CONFIG_NO_FIXED_XIP_ROM_SIZE
# For efficient MTRR utilisation use natural alignment for romstage.
ifeq ($(CONFIG_SETUP_XIP_CACHE),y)
$(CONFIG_CBFS_PREFIX)/romstage-options += --pow2page
endif # CONFIG_SETUP_XIP_CACHE

endif # CONFIG_NO_XIP_EARLY_STAGES
endif # CONFIG_ARCH_ROMSTAGE_X86_32 / CONFIG_ARCH_ROMSTAGE_X86_64
Expand Down
17 changes: 17 additions & 0 deletions configs/builder/config.ocp.deltalake
@@ -0,0 +1,17 @@
# type this to get working .config:
# make defconfig KBUILD_DEFCONFIG=configs/builder/config.ocp.deltalake

CONFIG_VENDOR_OCP=y
CONFIG_BOARD_OCP_DELTALAKE=y
CONFIG_HAVE_IFD_BIN=y
CONFIG_HAVE_ME_BIN=y
CONFIG_DO_NOT_TOUCH_DESCRIPTOR_REGION=y
CONFIG_USE_CPU_MICROCODE_CBFS_BINS=y
CONFIG_CPU_MICROCODE_CBFS_EXTERNAL_BINS=y
CONFIG_CPU_UCODE_BINARIES="site-local/deltalake/mbf5065a.mcb"
CONFIG_ADD_FSP_BINARIES=y
CONFIG_FSP_T_FILE="site-local/deltalake/Server_T.fd"
CONFIG_FSP_M_FILE="site-local/deltalake/Server_M.fd"
CONFIG_FSP_S_FILE="site-local/deltalake/Server_S.fd"
CONFIG_ME_BIN_PATH="site-local/deltalake/flashregion_2_intel_me.bin"
CONFIG_IFD_BIN_PATH="site-local/deltalake/flashregion_0_flashdescriptor.bin"
2 changes: 1 addition & 1 deletion configs/config.pcengines_apu1
@@ -1,4 +1,4 @@
CONFIG_LOCALVERSION="v4.12.0.1"
CONFIG_LOCALVERSION="v4.12.0.2"
CONFIG_VENDOR_PCENGINES=y
CONFIG_PAYLOAD_CONFIGFILE="$(top)/src/mainboard/$(MAINBOARDDIR)/seabios_config"
CONFIG_NO_GFX_INIT=y
Expand Down
2 changes: 1 addition & 1 deletion configs/config.pcengines_apu2
@@ -1,4 +1,4 @@
CONFIG_LOCALVERSION="v4.12.0.1"
CONFIG_LOCALVERSION="v4.12.0.2"
CONFIG_VENDOR_PCENGINES=y
CONFIG_PAYLOAD_CONFIGFILE="$(top)/src/mainboard/$(MAINBOARDDIR)/seabios_config"
CONFIG_BOARD_PCENGINES_APU2=y
Expand Down
3 changes: 2 additions & 1 deletion configs/config.pcengines_apu3
@@ -1,9 +1,10 @@
CONFIG_LOCALVERSION="v4.12.0.1"
CONFIG_LOCALVERSION="v4.12.0.2"
CONFIG_VENDOR_PCENGINES=y
CONFIG_PAYLOAD_CONFIGFILE="$(top)/src/mainboard/$(MAINBOARDDIR)/seabios_config"
CONFIG_BOARD_PCENGINES_APU3=y
CONFIG_PXE_ROM_ID="8086,1539"
CONFIG_NO_GFX_INIT=y
CONFIG_USER_TPM2=y
CONFIG_DEFAULT_CONSOLE_LOGLEVEL_1=y
CONFIG_SEABIOS_REVISION=y
CONFIG_SEABIOS_REVISION_ID="rel-1.12.1.3"
Expand Down
3 changes: 2 additions & 1 deletion configs/config.pcengines_apu4
@@ -1,9 +1,10 @@
CONFIG_LOCALVERSION="v4.12.0.1"
CONFIG_LOCALVERSION="v4.12.0.2"
CONFIG_VENDOR_PCENGINES=y
CONFIG_PAYLOAD_CONFIGFILE="$(top)/src/mainboard/$(MAINBOARDDIR)/seabios_config"
CONFIG_BOARD_PCENGINES_APU4=y
CONFIG_PXE_ROM_ID="8086,1539"
CONFIG_NO_GFX_INIT=y
CONFIG_USER_TPM2=y
CONFIG_DEFAULT_CONSOLE_LOGLEVEL_1=y
CONFIG_SEABIOS_REVISION=y
CONFIG_SEABIOS_REVISION_ID="rel-1.12.1.3"
Expand Down
2 changes: 1 addition & 1 deletion configs/config.pcengines_apu5
@@ -1,4 +1,4 @@
CONFIG_LOCALVERSION="v4.12.0.1"
CONFIG_LOCALVERSION="v4.12.0.2"
CONFIG_VENDOR_PCENGINES=y
CONFIG_PAYLOAD_CONFIGFILE="$(top)/src/mainboard/$(MAINBOARDDIR)/seabios_config"
CONFIG_BOARD_PCENGINES_APU5=y
Expand Down
2 changes: 1 addition & 1 deletion payloads/external/Makefile.inc
Expand Up @@ -319,7 +319,7 @@ payloads/external/iPXE/ipxe/ipxe.rom ipxe: $(DOTCONFIG) $(PXE_CONFIG_SCRIPT)
CONFIG_PXE_CUSTOM_BUILD_ID=$(CONFIG_PXE_CUSTOM_BUILD_ID) \
CONFIG_SCRIPT=$(PXE_CONFIG_SCRIPT) \
CONFIG_HAS_SCRIPT=$(CONFIG_PXE_ADD_SCRIPT) \
CONFIG_PXE_NO_PROMT=$(CONFIG_PXE_NO_PROMT) \
CONFIG_PXE_NO_PROMPT=$(CONFIG_PXE_NO_PROMPT) \
CONFIG_PXE_HAS_HTTPS=$(CONFIG_PXE_HAS_HTTPS) \
MFLAGS= MAKEFLAGS=

Expand Down
2 changes: 1 addition & 1 deletion payloads/external/iPXE/Kconfig
Expand Up @@ -91,7 +91,7 @@ config PXE_CUSTOM_BUILD_ID
This option allows user to customize build_id for reproducible builds.
It is 32-bit hexadecimal number without "0x" prefix.

config PXE_NO_PROMT
config PXE_NO_PROMPT
bool "Do not show prompt to boot from PXE"
default n
depends on BUILD_IPXE
Expand Down
6 changes: 3 additions & 3 deletions payloads/external/iPXE/Makefile
Expand Up @@ -64,14 +64,14 @@ else
false
endif
endif
ifneq ($(filter y,$(CONFIG_HAS_SCRIPT) $(CONFIG_PXE_NO_PROMT)),)
ifneq ($(filter y,$(CONFIG_HAS_SCRIPT) $(CONFIG_PXE_NO_PROMPT)),)
cp "$(project_dir)/src/config/general.h" "$(project_dir)/src/config/general.h.cb"
endif
ifeq ($(CONFIG_HAS_SCRIPT),y)
sed 's|//#define\s*IMAGE_SCRIPT.*|#define IMAGE_SCRIPT|' "$(project_dir)/src/config/general.h" > "$(project_dir)/src/config/general.h.tmp"
mv "$(project_dir)/src/config/general.h.tmp" "$(project_dir)/src/config/general.h"
endif
ifeq ($(CONFIG_PXE_NO_PROMT),y)
ifeq ($(CONFIG_PXE_NO_PROMPT),y)
sed 's|#define\s*BANNER_TIMEOUT.*|#define BANNER_TIMEOUT 0|' "$(project_dir)/src/config/general.h" > "$(project_dir)/src/config/general.h.tmp"
mv "$(project_dir)/src/config/general.h.tmp" "$(project_dir)/src/config/general.h"
endif
Expand All @@ -93,7 +93,7 @@ ifeq ($(CONSOLE_SERIAL),yy)
cp "$(project_dir)/src/config/console.h.cb" "$(project_dir)/src/config/console.h"
cp "$(project_dir)/src/config/serial.h.cb" "$(project_dir)/src/config/serial.h"
endif
ifneq ($(filter y,$(CONFIG_HAS_SCRIPT) $(CONFIG_PXE_NO_PROMT)),)
ifneq ($(filter y,$(CONFIG_HAS_SCRIPT) $(CONFIG_PXE_NO_PROMPT)),)
cp "$(project_dir)/src/config/general.h.cb" "$(project_dir)/src/config/general.h"
endif

Expand Down
4 changes: 4 additions & 0 deletions payloads/libpayload/drivers/usb/usb.c
Expand Up @@ -86,6 +86,10 @@ usb_poll (void)
{
if (usb_hcs == 0)
return;

if (usb_poll_prepare)
usb_poll_prepare();

hci_t *controller = usb_hcs;
while (controller != NULL) {
int i;
Expand Down
7 changes: 6 additions & 1 deletion payloads/libpayload/drivers/usb/xhci.c
Expand Up @@ -129,7 +129,12 @@ xhci_switchback_ppt_ports(pcidev_t addr)
static long
xhci_handshake(volatile u32 *const reg, u32 mask, u32 wait_for, long timeout_us)
{
while ((*reg & mask) != wait_for && timeout_us--) udelay(1);
if (timeout_us <= 0)
return 0;
while ((*reg & mask) != wait_for && timeout_us != 0) {
--timeout_us;
udelay(1);
}
return timeout_us;
}

Expand Down
3 changes: 2 additions & 1 deletion payloads/libpayload/gdb/stub.c
Expand Up @@ -73,7 +73,8 @@ static void gdb_output_write(const void *buffer, size_t count)
if (!gdb_state.resumed) {
/* Must be a die_if() in GDB (or a bug), so bail out and die. */
gdb_exit(-1);
video_console_init();
if (CONFIG(LP_VIDEO_CONSOLE))
video_console_init();
puts("GDB died, redirecting its last words to the screen:\n");
console_write(buffer, count);
} else {
Expand Down
7 changes: 7 additions & 0 deletions payloads/libpayload/include/usb/usb.h
Expand Up @@ -344,6 +344,13 @@ static inline void usb_debug(const char *fmt, ...)
#endif
}

/**
* To be implemented by libpayload-client. It's called by the USB
* stack just before iterating over known devices to poll them for
* status change.
*/
void __attribute__((weak)) usb_poll_prepare (void);

/**
* To be implemented by libpayload-client. It's called by the USB stack
* when a new USB device is found which isn't claimed by a built in driver,
Expand Down
60 changes: 41 additions & 19 deletions src/Kconfig
Expand Up @@ -323,6 +323,33 @@ config BOOTSPLASH_FILE
The path and filename of the file to use as graphical bootsplash
screen. The file format has to be jpg.

config FW_CONFIG
bool "Firmware Configuration Probing"
default n
help
Enable support for probing devices with fw_config. This is a simple
bitmask broken into fields and options for probing.

config FW_CONFIG_SOURCE_CBFS
bool "Obtain Firmware Configuration value from CBFS"
depends on FW_CONFIG
default n
help
With this option enabled coreboot will look for the 32bit firmware
configuration value in CBFS at the selected prefix with the file name
"fw_config". This option will override other sources and allow the
local image to preempt the mainboard selected source.

config FW_CONFIG_SOURCE_CHROMEEC_CBI
bool "Obtain Firmware Configuration value from Google Chrome EC CBI"
depends on FW_CONFIG && EC_GOOGLE_CHROMEEC
default n
help
This option tells coreboot to read the firmware configuration value
from the Google Chrome Embedded Controller CBI interface. This source
is not tried if FW_CONFIG_SOURCE_CBFS is enabled and the value was
found in CBFS.

config HAVE_RAMPAYLOAD
bool

Expand Down Expand Up @@ -390,7 +417,7 @@ config OVERRIDE_DEVICETREE

config FMDFILE
string "fmap description file in fmd format"
default "src/mainboard/$(CONFIG_MAINBOARD_DIR)/chromeos.fmd" if CHROMEOS
default "src/mainboard/\$(CONFIG_MAINBOARD_DIR)/chromeos.fmd" if CHROMEOS
default ""
help
The build system creates a default FMAP from ROM_SIZE and CBFS_SIZE,
Expand Down Expand Up @@ -628,10 +655,6 @@ config HAVE_PIRQ_TABLE
Whether or not the PIRQ table is actually generated by coreboot
is configurable by the user via GENERATE_PIRQ_TABLE.

config COMMON_FADT
bool
default n

config ACPI_NHLT
bool
default n
Expand Down Expand Up @@ -707,20 +730,6 @@ config MAINBOARD_SMBIOS_PRODUCT_NAME
help
Override the default Product name stored in SMBIOS structures.

config SMBIOS_ENCLOSURE_TYPE
hex
depends on GENERATE_SMBIOS_TABLES
default 0x09 if SYSTEM_TYPE_LAPTOP
default 0x1e if SYSTEM_TYPE_TABLET
default 0x1f if SYSTEM_TYPE_CONVERTIBLE
default 0x20 if SYSTEM_TYPE_DETACHABLE
default 0x03
help
System Enclosure or Chassis Types as defined in SMBIOS specification.
The default value is SMBIOS_ENCLOSURE_DESKTOP (0x03) but laptop,
convertible, or tablet enclosure will be used if the appropriate
system type is selected.

endmenu

source "payloads/Kconfig"
Expand Down Expand Up @@ -820,6 +829,10 @@ config DEBUG_SMI

If unsure, say N.

config DEBUG_PERIODIC_SMI
bool "Trigger SMI periodically"
depends on DEBUG_SMI

# Only visible if debug level is DEBUG (7) or SPEW (8) as it does additional
# printk(BIOS_DEBUG, ...) calls.
config DEBUG_MALLOC
Expand Down Expand Up @@ -1173,6 +1186,15 @@ config BOOTBLOCK_CUSTOM
# src/lib/bootblock.c#main() C entry point.
bool

config MEMLAYOUT_LD_FILE
string
default "src/mainboard/\$(CONFIG_MAINBOARD_DIR)/memlayout.ld"
help
This variable allows SoC/mainboard to supply in a custom linker file
if required. This determines the linker file used for all the stages
(bootblock, romstage, verstage, ramstage, postcar) in
src/arch/${ARCH}/Makefile.inc.

###############################################################################
# Set default values for symbols created before mainboards. This allows the
# option to be displayed in the general menu, but the default to be loaded in
Expand Down
3 changes: 3 additions & 0 deletions src/acpi/Kconfig
Expand Up @@ -24,6 +24,9 @@ config ACPI_INTEL_HARDWARE_SLEEP_VALUES
Provide common definitions for Intel hardware PM1_CNT register sleep
values.

config ACPI_NO_SMI_GNVS
bool

config ACPI_NO_PCAT_8259
bool
help
Expand Down
7 changes: 3 additions & 4 deletions src/acpi/Makefile.inc
Expand Up @@ -8,6 +8,8 @@ ramstage-y += acpigen_dsm.c
ramstage-y += acpigen_ps2_keybd.c
ramstage-y += acpigen_usb.c
ramstage-y += device.c
ramstage-$(CONFIG_CHROMEOS) += chromeos-gnvs.c
ramstage-y += gnvs.c
ramstage-y += pld.c
ramstage-y += sata.c
ramstage-y += soundwire.c
Expand All @@ -16,8 +18,5 @@ ifneq ($(wildcard src/mainboard/$(MAINBOARDDIR)/acpi_tables.c),)
ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/acpi_tables.c
endif
$(eval $(call asl_template,dsdt))
ifneq ($(wildcard src/mainboard/$(MAINBOARDDIR)/fadt.c),)
ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/fadt.c
endif

endif # CONFIG_GENERATE_ACPI_TABLES
endif # CONFIG_HAVE_ACPI_TABLES
17 changes: 13 additions & 4 deletions src/acpi/acpi.c
Expand Up @@ -1219,7 +1219,9 @@ void acpi_write_bert(acpi_bert_t *bert, uintptr_t region, size_t length)
header->checksum = acpi_checksum((void *)bert, header->length);
}

#if CONFIG(COMMON_FADT)
__weak void soc_fill_fadt(acpi_fadt_t *fadt) { }
__weak void mainboard_fill_fadt(acpi_fadt_t *fadt) { }

void acpi_create_fadt(acpi_fadt_t *fadt, acpi_facs_t *facs, void *dsdt)
{
acpi_header_t *header = &(fadt->header);
Expand All @@ -1238,13 +1240,16 @@ void acpi_create_fadt(acpi_fadt_t *fadt, acpi_facs_t *facs, void *dsdt)
header->asl_compiler_revision = asl_revision;

fadt->firmware_ctrl = (unsigned long) facs;
fadt->dsdt = (unsigned long) dsdt;

fadt->x_firmware_ctl_l = (unsigned long)facs;
fadt->x_firmware_ctl_h = 0;

fadt->dsdt = (unsigned long) dsdt;
fadt->x_dsdt_l = (unsigned long)dsdt;
fadt->x_dsdt_h = 0;

/* should be 0 ACPI 3.0 */
fadt->reserved = 0;

if (CONFIG(SYSTEM_TYPE_CONVERTIBLE) ||
CONFIG(SYSTEM_TYPE_LAPTOP))
fadt->preferred_pm_profile = PM_MOBILE;
Expand All @@ -1256,10 +1261,12 @@ void acpi_create_fadt(acpi_fadt_t *fadt, acpi_facs_t *facs, void *dsdt)

acpi_fill_fadt(fadt);

soc_fill_fadt(fadt);
mainboard_fill_fadt(fadt);

header->checksum =
acpi_checksum((void *) fadt, header->length);
}
#endif

unsigned long __weak fw_cfg_acpi_tables(unsigned long start)
{
Expand Down Expand Up @@ -1607,6 +1614,8 @@ int get_acpi_table_revision(enum acpi_tables table)
return 1; /* TODO Should probably be upgraded to 2 */
case DMAR:
return 1;
case DRTM:
return 1;
case SLIT: /* ACPI 2.0 upto 6.3: 1 */
return 1;
case SPMI: /* IMPI 2.0 */
Expand Down
40 changes: 40 additions & 0 deletions src/acpi/acpigen.c
Expand Up @@ -339,6 +339,18 @@ void acpigen_write_scope(const char *name)
acpigen_emit_namestring(name);
}

void acpigen_get_package_op_element(uint8_t package_op, unsigned int element, uint8_t dest_op)
{
/* <dest_op> = DeRefOf (<package_op>[<element]) */
acpigen_write_store();
acpigen_emit_byte(DEREF_OP);
acpigen_emit_byte(INDEX_OP);
acpigen_emit_byte(package_op);
acpigen_write_integer(element);
acpigen_emit_byte(ZERO_OP); /* Ignore Index() Destination */
acpigen_emit_byte(dest_op);
}

void acpigen_write_processor(u8 cpuindex, u32 pblock_addr, u8 pblock_len)
{
/*
Expand Down Expand Up @@ -1268,6 +1280,20 @@ void acpigen_write_if_and(uint8_t arg1, uint8_t arg2)
acpigen_emit_byte(arg2);
}

/*
* Generates ACPI code for checking if operand1 and operand2 are equal.
* Both operand1 and operand2 are ACPI ops.
*
* If (Lequal (op,1 op2))
*/
void acpigen_write_if_lequal_op_op(uint8_t op1, uint8_t op2)
{
acpigen_write_if();
acpigen_emit_byte(LEQUAL_OP);
acpigen_emit_byte(op1);
acpigen_emit_byte(op2);
}

/*
* Generates ACPI code for checking if operand1 and operand2 are equal, where,
* operand1 is ACPI op and operand2 is an integer.
Expand Down Expand Up @@ -1341,6 +1367,12 @@ void acpigen_write_return_singleton_buffer(uint8_t arg)
acpigen_write_return_byte_buffer(&arg, 1);
}

void acpigen_write_return_op(uint8_t arg)
{
acpigen_emit_byte(RETURN_OP);
acpigen_emit_byte(arg);
}

void acpigen_write_return_byte(uint8_t arg)
{
acpigen_emit_byte(RETURN_OP);
Expand Down Expand Up @@ -1790,6 +1822,14 @@ void acpigen_get_rx_gpio(struct acpi_gpio *gpio)
acpigen_write_xor(LOCAL0_OP, 1, LOCAL0_OP);
}

void acpigen_get_tx_gpio(struct acpi_gpio *gpio)
{
acpigen_soc_get_tx_gpio(gpio->pins[0]);

if (gpio->polarity == ACPI_GPIO_ACTIVE_LOW)
acpigen_write_xor(LOCAL0_OP, 1, LOCAL0_OP);
}

/* refer to ACPI 6.4.3.5.3 Word Address Space Descriptor section for details */
void acpigen_resource_word(u16 res_type, u16 gen_flags, u16 type_flags, u16 gran,
u16 range_min, u16 range_max, u16 translation, u16 length)
Expand Down
18 changes: 18 additions & 0 deletions src/acpi/chromeos-gnvs.c
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#include <acpi/acpi_gnvs.h>
#include <types.h>
#include <ec/google/chromeec/ec.h>
#include <vendorcode/google/chromeos/gnvs.h>

void gnvs_assign_chromeos(void)
{
chromeos_acpi_t *gnvs_chromeos = gnvs_chromeos_ptr();
chromeos_init_chromeos_acpi(gnvs_chromeos);

/* EC can override to ECFW_RW. */
gnvs_chromeos->vbt2 = ACTIVE_ECFW_RO;

if (CONFIG(EC_GOOGLE_CHROMEEC) && !google_ec_running_ro())
gnvs_chromeos->vbt2 = ACTIVE_ECFW_RW;
}
175 changes: 145 additions & 30 deletions src/acpi/device.c
Expand Up @@ -53,7 +53,7 @@ const char *acpi_device_name(const struct device *dev)
return NULL;

/* Check for device specific handler */
if (dev->ops->acpi_name)
if (dev->ops && dev->ops->acpi_name)
return dev->ops->acpi_name(dev);

/* Walk up the tree to find if any parent can identify this device */
Expand Down Expand Up @@ -523,6 +523,80 @@ void acpi_device_write_spi(const struct acpi_spi *spi)
acpi_device_fill_len(desc_length);
}

/* UART Serial Bus - UARTSerialBusV2() */
void acpi_device_write_uart(const struct acpi_uart *uart)
{
void *desc_length, *type_length;
uint16_t flags;

/* Byte 0: Descriptor Type */
acpigen_emit_byte(ACPI_DESCRIPTOR_SERIAL_BUS);

/* Byte 1+2: Length (filled in later) */
desc_length = acpi_device_write_zero_len();

/* Byte 3: Revision ID */
acpigen_emit_byte(ACPI_UART_SERIAL_BUS_REVISION_ID);

/* Byte 4: Resource Source Index is Reserved */
acpigen_emit_byte(0);

/* Byte 5: Serial Bus Type is UART */
acpigen_emit_byte(ACPI_SERIAL_BUS_TYPE_UART);

/*
* Byte 6: Flags
* [7:2]: 0 => Reserved
* [1]: 1 => ResourceConsumer
* [0]: 0 => ControllerInitiated
*/
acpigen_emit_byte(BIT(1));

/*
* Byte 7-8: Type Specific Flags
* [15:8]: 0 => Reserved
* [7]: 0 => Little Endian, 1 => Big Endian
* [6:4]: Data bits
* [3:2]: Stop bits
* [1:0]: Flow control
*/
flags = uart->flow_control & 3;
flags |= (uart->stop_bits & 3) << 2;
flags |= (uart->data_bits & 7) << 4;
flags |= (uart->endian & 1) << 7;
acpigen_emit_word(flags);

/* Byte 9: Type Specific Revision ID */
acpigen_emit_byte(ACPI_UART_TYPE_SPECIFIC_REVISION_ID);

/* Byte 10-11: Type Data Length */
type_length = acpi_device_write_zero_len();

/* Byte 12-15: Initial Baud Rate */
acpigen_emit_dword(uart->initial_baud_rate);

/* Byte 16-17: RX FIFO size */
acpigen_emit_word(uart->rx_fifo_bytes);

/* Byte 18-19: TX FIFO size */
acpigen_emit_word(uart->tx_fifo_bytes);

/* Byte 20: Parity */
acpigen_emit_byte(uart->parity);

/* Byte 21: Lines Enabled */
acpigen_emit_byte(uart->lines_in_use);

/* Fill in Type Data Length */
acpi_device_fill_len(type_length);

/* Byte 22+: ResourceSource */
acpigen_emit_string(uart->resource);

/* Fill in Descriptor Length */
acpi_device_fill_len(desc_length);
}

/* PowerResource() with Enable and/or Reset control */
void acpi_device_add_power_res(const struct acpi_power_res_params *params)
{
Expand Down Expand Up @@ -658,10 +732,48 @@ static void acpi_dp_free(struct acpi_dp *dp)
}
}

static bool acpi_dp_write_properties(struct acpi_dp *prop, const char *uuid)
{
struct acpi_dp *dp;
char *prop_count = NULL;

/* Print base properties */
for (dp = prop; dp; dp = dp->next) {
if (dp->type == ACPI_DP_TYPE_TABLE ||
dp->type == ACPI_DP_TYPE_CHILD ||
dp->type == ACPI_DP_TYPE_PACKAGE)
continue;

/*
* The UUID and package is only added when
* we come across the first property. This
* is to avoid creating a zero-length package
* in situations where there are only children.
*/
if (!prop_count) {
/* ToUUID (dp->uuid) */
acpigen_write_uuid(uuid);
/*
* Package (PROP), element count determined as
* it is populated
*/
prop_count = acpigen_write_package(0);
}
(*prop_count)++;
acpi_dp_write_property(dp);
}
if (prop_count) {
/* Package (PROP) length, if a package was written */
acpigen_pop_len();
return true;
}
return false;
}

void acpi_dp_write(struct acpi_dp *table)
{
struct acpi_dp *dp, *prop;
char *dp_count, *prop_count = NULL;
char *dp_count;
int child_count = 0;

if (!table || table->type != ACPI_DP_TYPE_TABLE || !table->next)
Expand All @@ -677,37 +789,17 @@ void acpi_dp_write(struct acpi_dp *table)
dp_count = acpigen_write_package(0);

/* Print base properties */
for (dp = prop; dp; dp = dp->next) {
if (dp->type == ACPI_DP_TYPE_CHILD) {
if (acpi_dp_write_properties(prop, table->uuid))
*dp_count += 2;

/* Count child properties */
for (dp = prop; dp; dp = dp->next)
if (dp->type == ACPI_DP_TYPE_CHILD)
child_count++;
} else {
/*
* The UUID and package is only added when
* we come across the first property. This
* is to avoid creating a zero-length package
* in situations where there are only children.
*/
if (!prop_count) {
*dp_count += 2;
/* ToUUID (ACPI_DP_UUID) */
acpigen_write_uuid(ACPI_DP_UUID);
/*
* Package (PROP), element count determined as
* it is populated
*/
prop_count = acpigen_write_package(0);
}
(*prop_count)++;
acpi_dp_write_property(dp);
}
}
if (prop_count) {
/* Package (PROP) length, if a package was written */
acpigen_pop_len();
}

/* Add child properties to the base table */
if (child_count) {
/* Update DP package count to 2 or 4 */
/* Update DP package count */
*dp_count += 2;
/* ToUUID (ACPI_DP_CHILD_UUID) */
acpigen_write_uuid(ACPI_DP_CHILD_UUID);
Expand All @@ -722,6 +814,12 @@ void acpi_dp_write(struct acpi_dp *table)
acpigen_pop_len();
}

/* Write packages of properties with unique UUID */
for (dp = prop; dp; dp = dp->next)
if (dp->type == ACPI_DP_TYPE_PACKAGE)
if (acpi_dp_write_properties(dp->child, dp->uuid))
*dp_count += 2;

/* Package (DP) length */
acpigen_pop_len();

Expand All @@ -746,6 +844,7 @@ static struct acpi_dp *acpi_dp_new(struct acpi_dp *dp, enum acpi_dp_type type,
memset(new, 0, sizeof(*new));
new->type = type;
new->name = name;
new->uuid = ACPI_DP_UUID;

if (dp) {
/* Add to end of property list */
Expand Down Expand Up @@ -863,6 +962,22 @@ struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
return new;
}

struct acpi_dp *acpi_dp_add_package(struct acpi_dp *dp, struct acpi_dp *package)
{
struct acpi_dp *new;

if (!dp || !package || package->type != ACPI_DP_TYPE_TABLE)
return NULL;

new = acpi_dp_new(dp, ACPI_DP_TYPE_PACKAGE, NULL);
if (new) {
new->uuid = package->name;
new->child = package;
}

return new;
}

struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array)
{
struct acpi_dp *new;
Expand Down
69 changes: 69 additions & 0 deletions src/acpi/gnvs.c
@@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#include <acpi/acpi_gnvs.h>
#include <acpi/acpigen.h>
#include <cbmem.h>
#include <console/console.h>
#include <string.h>
#include <types.h>

static void *gnvs;

void *acpi_get_gnvs(void)
{
if (gnvs)
return gnvs;

gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS);
if (gnvs)
return gnvs;

printk(BIOS_ERR, "Unable to locate Global NVS\n");
return NULL;
}

static void gnvs_assign_cbmc(void)
{
uint32_t *gnvs_cbmc = gnvs_cbmc_ptr();
if (gnvs_cbmc)
*gnvs_cbmc = (uintptr_t)cbmem_find(CBMEM_ID_CONSOLE);
}

void *gnvs_get_or_create(void)
{
size_t gnvs_size;

if (gnvs)
return gnvs;

gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS);
if (gnvs)
return gnvs;

gnvs_size = gnvs_size_of_array();

gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, gnvs_size);
if (!gnvs)
return gnvs;

memset(gnvs, 0, gnvs_size);

if (CONFIG(CONSOLE_CBMEM))
gnvs_assign_cbmc();

if (CONFIG(CHROMEOS))
gnvs_assign_chromeos();

return gnvs;
}

void acpi_inject_nvsa(void)
{
uintptr_t gnvs_address = (uintptr_t)acpi_get_gnvs();
if (!gnvs_address)
return;

acpigen_write_scope("\\");
acpigen_write_name_dword("NVSA", gnvs_address);
acpigen_pop_len();
}
10 changes: 5 additions & 5 deletions src/arch/arm/Makefile.inc
Expand Up @@ -50,11 +50,11 @@ bootblock-y += stages.c

$(objcbfs)/bootblock.debug: $$(bootblock-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) -T $(call src-to-obj,bootblock,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(bootblock-objs)) --end-group
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) -T $(call src-to-obj,bootblock,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(bootblock-objs)) --end-group

$(objcbfs)/decompressor.debug: $$(decompressor-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) -T $(call src-to-obj,decompressor,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(decompressor-objs)) --end-group
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) -T $(call src-to-obj,decompressor,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(decompressor-objs)) --end-group

endif # CONFIG_ARCH_BOOTBLOCK_ARM

Expand All @@ -66,7 +66,7 @@ ifeq ($(CONFIG_ARCH_VERSTAGE_ARM),y)

$(objcbfs)/verstage.debug: $$(verstage-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_verstage) $(LDFLAGS_verstage) -o $@ -L$(obj) -T $(call src-to-obj,verstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(verstage-objs)) --end-group
$(LD_verstage) $(LDFLAGS_verstage) -o $@ -L$(obj) -T $(call src-to-obj,verstage,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(verstage-objs)) --end-group

verstage-y += boot.c
verstage-y += div0.c
Expand Down Expand Up @@ -99,7 +99,7 @@ rmodules_arm-y += eabi_compat.c

$(objcbfs)/romstage.debug: $$(romstage-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_romstage) $(LDFLAGS_romstage) -o $@ -L$(obj) -T $(call src-to-obj,romstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(romstage-objs)) --end-group
$(LD_romstage) $(LDFLAGS_romstage) -o $@ -L$(obj) -T $(call src-to-obj,romstage,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(romstage-objs)) --end-group

endif # CONFIG_ARCH_ROMSTAGE_ARM

Expand Down Expand Up @@ -128,6 +128,6 @@ ramstage-srcs += $(wildcard src/mainboard/$(MAINBOARDDIR)/mainboard.c)

$(objcbfs)/ramstage.debug: $$(ramstage-objs)
@printf " CC $(subst $(obj)/,,$(@))\n"
$(LD_ramstage) $(LDFLAGS_ramstage) -o $@ -L$(obj) -T $(call src-to-obj,ramstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) --end-group
$(LD_ramstage) $(LDFLAGS_ramstage) -o $@ -L$(obj) -T $(call src-to-obj,ramstage,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) --end-group

endif # CONFIG_ARCH_RAMSTAGE_ARM
1 change: 0 additions & 1 deletion src/arch/arm/armv7/exception.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: BSD-3-Clause */

#include <stdint.h>
#include <types.h>
#include <arch/cache.h>
#include <arch/exception.h>
Expand Down
10 changes: 5 additions & 5 deletions src/arch/arm64/Makefile.inc
Expand Up @@ -45,11 +45,11 @@ bootblock-y += memmove.S

$(objcbfs)/bootblock.debug: $$(bootblock-objs) $(obj)/config.h
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(bootblock-objs)) --end-group -T $(call src-to-obj,bootblock,src/mainboard/$(MAINBOARDDIR)/memlayout.ld)
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(bootblock-objs)) --end-group -T $(call src-to-obj,bootblock,$(CONFIG_MEMLAYOUT_LD_FILE))

$(objcbfs)/decompressor.debug: $$(decompressor-objs) $(obj)/config.h
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(decompressor-objs)) --end-group -T $(call src-to-obj,decompressor,src/mainboard/$(MAINBOARDDIR)/memlayout.ld)
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(decompressor-objs)) --end-group -T $(call src-to-obj,decompressor,$(CONFIG_MEMLAYOUT_LD_FILE))

endif # CONFIG_ARCH_BOOTBLOCK_ARM64

Expand All @@ -61,7 +61,7 @@ ifeq ($(CONFIG_ARCH_VERSTAGE_ARM64),y)

$(objcbfs)/verstage.debug: $$(verstage-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_verstage) $(LDFLAGS_verstage) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(verstage-objs)) --end-group -T $(call src-to-obj,verstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld)
$(LD_verstage) $(LDFLAGS_verstage) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(verstage-objs)) --end-group -T $(call src-to-obj,verstage,$(CONFIG_MEMLAYOUT_LD_FILE))

verstage-y += boot.c
verstage-y += div0.c
Expand Down Expand Up @@ -99,7 +99,7 @@ rmodules_arm64-y += eabi_compat.c

$(objcbfs)/romstage.debug: $$(romstage-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_romstage) $(LDFLAGS_romstage) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(romstage-objs)) --end-group -T $(call src-to-obj,romstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld)
$(LD_romstage) $(LDFLAGS_romstage) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(romstage-objs)) --end-group -T $(call src-to-obj,romstage,$(CONFIG_MEMLAYOUT_LD_FILE))

endif # CONFIG_ARCH_ROMSTAGE_ARM64

Expand Down Expand Up @@ -134,7 +134,7 @@ ramstage-srcs += $(wildcard src/mainboard/$(MAINBOARDDIR)/mainboard.c)

$(objcbfs)/ramstage.debug: $$(ramstage-objs)
@printf " CC $(subst $(obj)/,,$(@))\n"
$(LD_ramstage) $(LDFLAGS_ramstage) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) --end-group -T $(call src-to-obj,ramstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld)
$(LD_ramstage) $(LDFLAGS_ramstage) -o $@ -L$(obj) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) --end-group -T $(call src-to-obj,ramstage,$(CONFIG_MEMLAYOUT_LD_FILE))

# Build ARM Trusted Firmware (BL31)

Expand Down
1 change: 0 additions & 1 deletion src/arch/arm64/armv8/exception.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: BSD-3-Clause */

#include <stdint.h>
#include <types.h>
#include <arch/barrier.h>
#include <arch/exception.h>
Expand Down
6 changes: 3 additions & 3 deletions src/arch/ppc64/Makefile.inc
Expand Up @@ -24,7 +24,7 @@ bootblock-generic-ccopts += $(ppc64_flags)
$(objcbfs)/bootblock.debug: $$(bootblock-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) \
-T $(call src-to-obj,bootblock,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(bootblock-objs)) \
-T $(call src-to-obj,bootblock,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(bootblock-objs)) \
$(LIBGCC_FILE_NAME_bootblock) --end-group $(COMPILER_RT_bootblock)

endif
Expand All @@ -50,7 +50,7 @@ romstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c

$(objcbfs)/romstage.debug: $$(romstage-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_romstage) $(LDFLAGS_romstage) -o $@ -L$(obj) -T $(call src-to-obj,romstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(romstage-objs)) --end-group $(COMPILER_RT_romstage)
$(LD_romstage) $(LDFLAGS_romstage) -o $@ -L$(obj) -T $(call src-to-obj,romstage,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(romstage-objs)) --end-group $(COMPILER_RT_romstage)

romstage-c-ccopts += $(ppc64_flags)
romstage-S-ccopts += $(ppc64_asm_flags)
Expand Down Expand Up @@ -83,7 +83,7 @@ ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/mainboard.c

$(objcbfs)/ramstage.debug: $$(ramstage-objs)
@printf " CC $(subst $(obj)/,,$(@))\n"
$(LD_ramstage) $(LDFLAGS_ramstage) -o $@ -L$(obj) -T $(call src-to-obj,ramstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) --end-group $(COMPILER_RT_ramstage)
$(LD_ramstage) $(LDFLAGS_ramstage) -o $@ -L$(obj) -T $(call src-to-obj,ramstage,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) --end-group $(COMPILER_RT_ramstage)

ramstage-c-ccopts += $(ppc64_flags)
ramstage-S-ccopts += $(ppc64_asm_flags)
Expand Down
6 changes: 3 additions & 3 deletions src/arch/riscv/Makefile.inc
Expand Up @@ -64,7 +64,7 @@ bootblock-$(CONFIG_RISCV_USE_ARCH_TIMER) += arch_timer.c
$(objcbfs)/bootblock.debug: $$(bootblock-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_bootblock) $(LDFLAGS_bootblock) -o $@ -L$(obj) \
-T $(call src-to-obj,bootblock,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(bootblock-objs)) \
-T $(call src-to-obj,bootblock,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(bootblock-objs)) \
$(LIBGCC_FILE_NAME_bootblock) --end-group $(COMPILER_RT_bootblock)

bootblock-c-ccopts += $(riscv_flags)
Expand Down Expand Up @@ -99,7 +99,7 @@ romstage-$(CONFIG_RISCV_USE_ARCH_TIMER) += arch_timer.c

$(objcbfs)/romstage.debug: $$(romstage-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_romstage) $(LDFLAGS_romstage) -o $@ -L$(obj) -T $(call src-to-obj,romstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(romstage-objs)) --end-group $(COMPILER_RT_romstage)
$(LD_romstage) $(LDFLAGS_romstage) -o $@ -L$(obj) -T $(call src-to-obj,romstage,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(romstage-objs)) --end-group $(COMPILER_RT_romstage)

romstage-c-ccopts += $(riscv_flags)
romstage-S-ccopts += $(riscv_asm_flags)
Expand Down Expand Up @@ -148,7 +148,7 @@ ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/mainboard.c

$(objcbfs)/ramstage.debug: $$(ramstage-objs)
@printf " CC $(subst $(obj)/,,$(@))\n"
$(LD_ramstage) $(LDFLAGS_ramstage) -o $@ -L$(obj) -T $(call src-to-obj,ramstage,src/mainboard/$(MAINBOARDDIR)/memlayout.ld) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) --end-group $(COMPILER_RT_ramstage)
$(LD_ramstage) $(LDFLAGS_ramstage) -o $@ -L$(obj) -T $(call src-to-obj,ramstage,$(CONFIG_MEMLAYOUT_LD_FILE)) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) --end-group $(COMPILER_RT_ramstage)

ramstage-c-ccopts += $(riscv_flags)
ramstage-S-ccopts += $(riscv_asm_flags)
Expand Down
1 change: 0 additions & 1 deletion src/arch/riscv/fit_payload.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */

#include <cbfs.h>
#include <commonlib/bsd/compression.h>
#include <console/console.h>
#include <bootmem.h>
Expand Down
6 changes: 5 additions & 1 deletion src/arch/x86/Kconfig
Expand Up @@ -162,7 +162,7 @@ config HAVE_CMOS_DEFAULT

config CMOS_DEFAULT_FILE
string
default "src/mainboard/$(MAINBOARDDIR)/cmos.default"
default "src/mainboard/\$(MAINBOARDDIR)/cmos.default"
depends on HAVE_CMOS_DEFAULT

config IOAPIC_INTERRUPTS_ON_FSB
Expand Down Expand Up @@ -316,4 +316,8 @@ config MAX_ACPI_TABLE_SIZE_KB
help
Set the maximum size of all ACPI tables in KiB.

config MEMLAYOUT_LD_FILE
string
default "src/arch/x86/memlayout.ld"

endif
69 changes: 39 additions & 30 deletions src/arch/x86/Makefile.inc
Expand Up @@ -67,48 +67,39 @@ endef
define early_x86_stage
# $1 stage name
# $2 oformat
$(1)-y += memlayout.ld

# The '.' include path is needed for the generated assembly.inc file.
$(1)-S-ccopts += -I.

$$(objcbfs)/$(1).debug: $$$$($(1)-libs) $$$$($(1)-objs)
@printf " LINK $$(subst $$(obj)/,,$$(@))\n"
$$(LD_$(1)) $$(LDFLAGS_$(1)) -o $$@ -L$$(obj) $$(COMPILER_RT_FLAGS_$(1)) --whole-archive --start-group $$(filter-out %.ld,$$($(1)-objs)) $$($(1)-libs) --no-whole-archive $$(COMPILER_RT_$(1)) --end-group -T $(call src-to-obj,$(1),$(dir)/memlayout.ld) --oformat $(2)
$$(LD_$(1)) $$(LDFLAGS_$(1)) -o $$@ -L$$(obj) $$(COMPILER_RT_FLAGS_$(1)) --whole-archive --start-group $$(filter-out %.ld,$$($(1)-objs)) $$($(1)-libs) --no-whole-archive $$(COMPILER_RT_$(1)) --end-group -T $(call src-to-obj,$(1),$(CONFIG_MEMLAYOUT_LD_FILE)) --oformat $(2)
-LANG=C LC_ALL= $$(OBJCOPY_$(1)) --only-section .illegal_globals $$(@) $$(objcbfs)/$(1)_null.offenders >/dev/null 2>&1
if [ -z "$$$$($$(NM_$(1)) $$(objcbfs)/$(1)_null.offenders 2>&1 | grep 'no symbols')" ];then \
echo "Forbidden global variables in $(1):"; \
$$(NM_$(1)) $$(objcbfs)/$(1)_null.offenders; false; \
fi
endef

###############################################################################
# all (bootblock,verstage,romstage,postcar,ramstage)
###############################################################################

ifeq ($(CONFIG_ARCH_X86),y)

all-$(CONFIG_HAVE_CF9_RESET) += cf9_reset.c
all-y += boot.c
all-y += memcpy.c
all-y += memset.c
all-y += cpu_common.c
all-y += post.c

endif

###############################################################################
# bootblock
###############################################################################

ifeq ($(CONFIG_ARCH_BOOTBLOCK_X86_32)$(CONFIG_ARCH_BOOTBLOCK_X86_64),y)

bootblock-y += boot.c
bootblock-y += post.c
bootblock-y += cpu_common.c
bootblock-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c
bootblock-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S
bootblock-y += memcpy.c
bootblock-y += memset.c
bootblock-$(CONFIG_COLLECT_TIMESTAMPS_TSC) += timestamp.c
bootblock-$(CONFIG_X86_TOP4G_BOOTMEDIA_MAP) += mmap_boot.c
bootblock-$(CONFIG_BOOTBLOCK_NORMAL) += bootblock_normal.c
bootblock-y += id.S
bootblock-$(CONFIG_HAVE_CF9_RESET) += cf9_reset.c

$(call src-to-obj,bootblock,$(dir)/id.S): $(obj)/build.h

bootblock-y += bootblock_crt0.S
Expand All @@ -129,10 +120,16 @@ endif # CONFIG_ARCH_BOOTBLOCK_X86_32 / CONFIG_ARCH_BOOTBLOCK_X86_64

ifeq ($(CONFIG_ARCH_VERSTAGE_X86_32)$(CONFIG_ARCH_VERSTAGE_X86_64),y)

verstage-y += boot.c
verstage-y += post.c
verstage-$(CONFIG_VBOOT_SEPARATE_VERSTAGE) += gdt_init.S
verstage-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c
verstage-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S
verstage-$(CONFIG_HAVE_CF9_RESET) += cf9_reset.c

verstage-y += cpu_common.c
verstage-y += memset.c
verstage-y += memcpy.c
verstage-y += memmove.c
verstage-$(CONFIG_X86_TOP4G_BOOTMEDIA_MAP) += mmap_boot.c
# If verstage is a separate stage it means there's no need
Expand Down Expand Up @@ -161,16 +158,22 @@ endif # CONFIG_ARCH_VERSTAGE_X86_32 / CONFIG_ARCH_VERSTAGE_X86_64
ifeq ($(CONFIG_ARCH_ROMSTAGE_X86_32)$(CONFIG_ARCH_ROMSTAGE_X86_64),y)

romstage-$(CONFIG_HAVE_ACPI_RESUME) += acpi_s3.c
romstage-y += boot.c
romstage-y += post.c
# gdt_init.S is included by entry32.inc when romstage is the first C
# environment.
romstage-y += gdt_init.S
romstage-y += cbmem.c
romstage-y += cpu_common.c
romstage-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c
romstage-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S
romstage-y += memcpy.c
romstage-y += memmove.c
romstage-y += memset.c
romstage-$(CONFIG_X86_TOP4G_BOOTMEDIA_MAP) += mmap_boot.c
romstage-y += postcar_loader.c
romstage-$(CONFIG_COLLECT_TIMESTAMPS_TSC) += timestamp.c
romstage-$(CONFIG_HAVE_CF9_RESET) += cf9_reset.c

romstage-srcs += $(wildcard $(src)/mainboard/$(MAINBOARDDIR)/romstage.c)
romstage-libs ?=
Expand All @@ -196,21 +199,26 @@ $(eval $(call create_class_compiler,postcar,x86_32))
postcar-generic-ccopts += -D__POSTCAR__

postcar-$(CONFIG_HAVE_ACPI_RESUME) += acpi_s3.c
postcar-y += boot.c
postcar-y += post.c
postcar-y += gdt_init.S
postcar-y += cpu_common.c
postcar-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c
postcar-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S
postcar-y += exit_car.S
postcar-y += memcpy.c
postcar-y += memmove.c
postcar-y += memlayout.ld
postcar-y += memset.c
postcar-$(CONFIG_X86_TOP4G_BOOTMEDIA_MAP) += mmap_boot.c
postcar-y += postcar.c
postcar-$(CONFIG_COLLECT_TIMESTAMPS_TSC) += timestamp.c
postcar-$(CONFIG_HAVE_CF9_RESET) += cf9_reset.c

LDFLAGS_postcar += -Map $(objcbfs)/postcar.map

$(objcbfs)/postcar.debug: $$(postcar-objs)
@printf " LINK $(subst $(obj)/,,$(@))\n"
$(LD_postcar) $(LDFLAGS_postcar) -o $@ -L$(obj) $(COMPILER_RT_FLAGS_postcar) --whole-archive --start-group $(filter-out %.ld,$^) --no-whole-archive $(COMPILER_RT_postcar) --end-group -T $(call src-to-obj,postcar,src/arch/x86/memlayout.ld)
$(LD_postcar) $(LDFLAGS_postcar) -o $@ -L$(obj) $(COMPILER_RT_FLAGS_postcar) --whole-archive --start-group $(filter-out %.ld,$^) --no-whole-archive $(COMPILER_RT_postcar) --end-group -T $(call src-to-obj,postcar,$(CONFIG_MEMLAYOUT_LD_FILE))

$(objcbfs)/postcar.elf: $(objcbfs)/postcar.debug.rmod
cp $< $@
Expand All @@ -229,15 +237,19 @@ ifeq ($(CONFIG_ARCH_RAMSTAGE_X86_32)$(CONFIG_ARCH_RAMSTAGE_X86_64),y)

ramstage-$(CONFIG_HAVE_ACPI_RESUME) += acpi_s3.c
ramstage-$(CONFIG_ACPI_BERT) += acpi_bert_storage.c
ramstage-y += boot.c
ramstage-y += post.c
ramstage-y += c_start.S
ramstage-y += cpu.c
ramstage-y += cpu_common.c
ramstage-y += ebda.c
ramstage-y += exception.c
ramstage-y += idt.S
ramstage-y += gdt.c
ramstage-$(CONFIG_IOAPIC) += ioapic.c
ramstage-y += memlayout.ld
ramstage-y += memcpy.c
ramstage-y += memmove.c
ramstage-y += memset.c
ramstage-$(CONFIG_X86_TOP4G_BOOTMEDIA_MAP) += mmap_boot.c
ramstage-$(CONFIG_GENERATE_MP_TABLE) += mpspec.c
ramstage-$(CONFIG_GENERATE_PIRQ_TABLE) += pirq_routing.c
Expand All @@ -248,22 +260,23 @@ ramstage-$(CONFIG_COOP_MULTITASKING) += thread.c
ramstage-$(CONFIG_COOP_MULTITASKING) += thread_switch.S
ramstage-$(CONFIG_COLLECT_TIMESTAMPS_TSC) += timestamp.c
ramstage-$(CONFIG_HAVE_ACPI_RESUME) += wakeup.S
ramstage-$(CONFIG_HAVE_CF9_RESET) += cf9_reset.c

ifeq ($(CONFIG_ARCH_RAMSTAGE_X86_32),y)
rmodules_x86_32-y += memcpy.c
rmodules_x86_32-y += memmove.c
rmodules_x86_32-y += memset.c
else

rmodules_x86_64-y += memcpy.c
rmodules_x86_64-y += memmove.c
rmodules_x86_64-y += memset.c
endif

ifeq ($(CONFIG_ARCH_RAMSTAGE_X86_32),y)
target-objcopy=-O elf32-i386 -B i386
LD_MACHINE =-m elf_i386
endif
ifeq ($(CONFIG_ARCH_RAMSTAGE_X86_64),y)
target-objcopy=-O elf64-x86-64 -B i386:x86-64
LD_MACHINE =-m elf_x86_64
endif

ramstage-srcs += $(wildcard src/mainboard/$(MAINBOARDDIR)/mainboard.c)
Expand All @@ -289,17 +302,13 @@ $(objcbfs)/ramstage.elf: $(objcbfs)/ramstage.debug.rmod

endif

$(objcbfs)/ramstage.debug: $(objgenerated)/ramstage.o $(call src-to-obj,ramstage,src/arch/x86/memlayout.ld)
$(objcbfs)/ramstage.debug: $(objgenerated)/ramstage.o $(call src-to-obj,ramstage,$(CONFIG_MEMLAYOUT_LD_FILE))
@printf " CC $(subst $(obj)/,,$(@))\n"
$(LD_ramstage) $(CPPFLAGS) $(LDFLAGS_ramstage) -o $@ -L$(obj) $< -T $(call src-to-obj,ramstage,src/arch/x86/memlayout.ld)
$(LD_ramstage) $(CPPFLAGS) $(LDFLAGS_ramstage) -o $@ -L$(obj) $< -T $(call src-to-obj,ramstage,$(CONFIG_MEMLAYOUT_LD_FILE))

$(objgenerated)/ramstage.o: $$(ramstage-objs) $(COMPILER_RT_ramstage) $$(ramstage-libs)
@printf " CC $(subst $(obj)/,,$(@))\n"
ifeq ($(CONFIG_ARCH_RAMSTAGE_X86_32),y)
$(LD_ramstage) -m elf_i386 -r -o $@ $(COMPILER_RT_FLAGS_ramstage) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) $(ramstage-libs) --no-whole-archive $(COMPILER_RT_ramstage) --end-group
else
$(LD_ramstage) -m elf_x86_64 -r -o $@ $(COMPILER_RT_FLAGS_ramstage) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) $(ramstage-libs) --no-whole-archive $(COMPILER_RT_ramstage) --end-group
endif
$(LD_ramstage) $(LD_MACHINE) -r -o $@ $(COMPILER_RT_FLAGS_ramstage) --whole-archive --start-group $(filter-out %.ld,$(ramstage-objs)) $(ramstage-libs) --no-whole-archive $(COMPILER_RT_ramstage) --end-group

endif # CONFIG_ARCH_RAMSTAGE_X86_32 / CONFIG_ARCH_RAMSTAGE_X86_64

Expand Down
1 change: 0 additions & 1 deletion src/arch/x86/acpi_bert_storage.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#include <bootstate.h>
#include <cbmem.h>
#include <console/console.h>
#include <cpu/x86/name.h>
Expand Down
17 changes: 3 additions & 14 deletions src/arch/x86/acpi_s3.c
Expand Up @@ -4,14 +4,11 @@
#include <string.h>
#include <acpi/acpi.h>
#include <arch/cpu.h>
#include <cbmem.h>
#include <commonlib/helpers.h>
#include <cpu/x86/smm.h>
#include <fallback.h>
#include <timestamp.h>
#include <program_loading.h>
#include <romstage_handoff.h>
#include <symbols.h>
#include <cpu/x86/smm.h>

#if ENV_RAMSTAGE || ENV_POSTCAR

Expand Down Expand Up @@ -81,16 +78,8 @@ void __weak mainboard_suspend_resume(void)

void acpi_resume(void *wake_vec)
{
if (CONFIG(HAVE_SMI_HANDLER)) {
void *gnvs_address = cbmem_find(CBMEM_ID_ACPI_GNVS);

/* Restore GNVS pointer in SMM if found */
if (gnvs_address) {
printk(BIOS_DEBUG, "Restore GNVS pointer to %p\n",
gnvs_address);
smm_setup_structures(gnvs_address, NULL, NULL);
}
}
/* Restore GNVS pointer in SMM if found. */
apm_control(APM_CNT_GNVS_UPDATE);

/* Call mainboard resume handler first, if defined. */
mainboard_suspend_resume();
Expand Down
24 changes: 24 additions & 0 deletions src/arch/x86/car.ld
@@ -1,5 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-only */

/* CACHE_ROM_SIZE defined here. */
#include <cpu/x86/mtrr.h>

/* This file is included inside a SECTIONS block */
. = CONFIG_DCACHE_RAM_BASE;
.car.data . (NOLOAD) : {
Expand Down Expand Up @@ -73,6 +76,27 @@

_car_region_end = . + CONFIG_DCACHE_RAM_SIZE - (. - _car_region_start);
}
. = _car_region_end;
.car.mrc_var . (NOLOAD) : {
. += CONFIG_DCACHE_RAM_MRC_VAR_SIZE;
}

#if ENV_BOOTBLOCK
_car_mtrr_end = .;
_car_mtrr_start = _car_region_start;

_car_mtrr_size = _car_mtrr_end - _car_mtrr_start;
_car_mtrr_sz_log2 = 1 << LOG2CEIL(_car_mtrr_size);
_car_mtrr_mask = ~(MAX(4096, _car_mtrr_sz_log2) - 1);

#if !CONFIG(NO_XIP_EARLY_STAGES)
_xip_program_sz_log2 = 1 << LOG2CEIL(_ebootblock - _bootblock);
_xip_mtrr_mask = ~(MAX(4096, _xip_program_sz_log2) - 1);
#endif

_rom_mtrr_mask = ~(CACHE_ROM_SIZE - 1);
_rom_mtrr_base = _rom_mtrr_mask;
#endif

/* Global variables are not allowed in romstage
* This section is checked during stage creation to ensure
Expand Down
47 changes: 0 additions & 47 deletions src/arch/x86/early_ram.ld

This file was deleted.

5 changes: 2 additions & 3 deletions src/arch/x86/include/arch/romstage.h
Expand Up @@ -5,6 +5,7 @@

#include <stddef.h>
#include <stdint.h>
#include <cpu/x86/mtrr.h>

void mainboard_romstage_entry(void);

Expand All @@ -16,10 +17,8 @@ void mainboard_romstage_entry(void);

struct postcar_frame {
uintptr_t stack;
uint32_t upper_mask;
int max_var_mtrrs;
int num_var_mtrrs;
int skip_common_mtrr;
struct var_mtrr_context ctx;
};

/*
Expand Down
17 changes: 3 additions & 14 deletions src/arch/x86/memlayout.ld
Expand Up @@ -3,15 +3,6 @@
#include <memlayout.h>
#include <arch/header.ld>

/* Pull in the either CAR or early DRAM rules. */
#if ENV_ROMSTAGE_OR_BEFORE
#if ENV_CACHE_AS_RAM
#define EARLY_MEMLAYOUT "car.ld"
#else
#define EARLY_MEMLAYOUT "early_ram.ld"
#endif
#endif

SECTIONS
{
/*
Expand All @@ -30,18 +21,18 @@ SECTIONS
* Link at 32MiB address and rely on cbfstool to relocate to XIP. */
ROMSTAGE(CONFIG_ROMSTAGE_ADDR, 1M)

#include EARLY_MEMLAYOUT
#include "car.ld"
#elif ENV_SEPARATE_VERSTAGE
/* The 1M size is not allocated. It's just for basic size checking.
* Link at 32MiB address and rely on cbfstool to relocate to XIP. */
VERSTAGE(CONFIG_VERSTAGE_ADDR, 1M)

#include EARLY_MEMLAYOUT
#include "car.ld"
#elif ENV_BOOTBLOCK
BOOTBLOCK(CONFIG_X86_RESET_VECTOR - CONFIG_C_ENV_BOOTBLOCK_SIZE + 0x10,
CONFIG_C_ENV_BOOTBLOCK_SIZE)

#include EARLY_MEMLAYOUT
#include "car.ld"

#elif ENV_POSTCAR
POSTCAR(32M, 1M)
Expand All @@ -52,9 +43,7 @@ SECTIONS
/* Bootblock specific scripts which provide more SECTION directives. */
#include <cpu/x86/16bit/entry16.ld>
#include <cpu/x86/16bit/reset16.ld>
#if !CONFIG(RESET_VECTOR_IN_RAM)
#include <arch/x86/id.ld>
#endif
#if CONFIG(CPU_INTEL_FIRMWARE_INTERFACE_TABLE)
#include <cpu/intel/fit/fit.ld>
#endif
Expand Down
65 changes: 19 additions & 46 deletions src/arch/x86/postcar_loader.c
Expand Up @@ -26,12 +26,7 @@ static inline void stack_push(struct postcar_frame *pcf, uint32_t val)

static void postcar_frame_prepare(struct postcar_frame *pcf)
{
msr_t msr;
msr = rdmsr(MTRR_CAP_MSR);

pcf->upper_mask = (1 << (cpu_phys_address_size() - 32)) - 1;
pcf->max_var_mtrrs = msr.lo & MTRR_CAP_VCNT;
pcf->num_var_mtrrs = 0;
var_mtrr_context_init(&pcf->ctx, pcf);
}

int postcar_frame_init(struct postcar_frame *pcf, size_t stack_size)
Expand Down Expand Up @@ -60,47 +55,25 @@ int postcar_frame_init(struct postcar_frame *pcf, size_t stack_size)
return 0;
}

static void postcar_var_mtrr_set(const struct var_mtrr_context *ctx,
uintptr_t addr, size_t size,
msr_t base, msr_t mask)
{
struct postcar_frame *pcf = ctx->arg;

printk(BIOS_DEBUG, "MTRR Range: Start=%lx End=%lx (Size %zx)\n",
addr, addr + size, size);

stack_push(pcf, mask.hi);
stack_push(pcf, mask.lo);
stack_push(pcf, base.hi);
stack_push(pcf, base.lo);
}

void postcar_frame_add_mtrr(struct postcar_frame *pcf,
uintptr_t addr, size_t size, int type)
{
/*
* Utilize additional MTRRs if the specified size is greater than the
* base address alignment.
*/
while (size != 0) {
uint32_t addr_lsb;
uint32_t size_msb;
uint32_t mtrr_size;

if (pcf->num_var_mtrrs >= pcf->max_var_mtrrs) {
printk(BIOS_ERR, "No more variable MTRRs: %d\n",
pcf->max_var_mtrrs);
return;
}

addr_lsb = fls(addr);
size_msb = fms(size);

/* All MTRR entries need to have their base aligned to the mask
* size. The maximum size is calculated by a function of the
* min base bit set and maximum size bit set. */
if (addr_lsb > size_msb)
mtrr_size = 1 << size_msb;
else
mtrr_size = 1 << addr_lsb;

printk(BIOS_DEBUG, "MTRR Range: Start=%lx End=%lx (Size %x)\n",
addr, addr + mtrr_size, mtrr_size);

stack_push(pcf, pcf->upper_mask);
stack_push(pcf, ~(mtrr_size - 1) | MTRR_PHYS_MASK_VALID);
stack_push(pcf, 0);
stack_push(pcf, addr | type);
pcf->num_var_mtrrs++;

size -= mtrr_size;
addr += mtrr_size;
}
var_mtrr_set_with_cb(&pcf->ctx, addr, size, type, postcar_var_mtrr_set);
}

void postcar_frame_add_romcache(struct postcar_frame *pcf, int type)
Expand Down Expand Up @@ -140,8 +113,8 @@ static void postcar_commit_mtrrs(struct postcar_frame *pcf)
* Place the number of used variable MTRRs on stack then max number
* of variable MTRRs supported in the system.
*/
stack_push(pcf, pcf->num_var_mtrrs);
stack_push(pcf, pcf->max_var_mtrrs);
stack_push(pcf, pcf->ctx.used_var_mtrrs);
stack_push(pcf, pcf->ctx.max_var_mtrrs);
}

static void finalize_load(uintptr_t *stack_top_ptr, uintptr_t stack_top)
Expand Down
36 changes: 35 additions & 1 deletion src/arch/x86/smbios.c
Expand Up @@ -479,9 +479,24 @@ smbios_board_type __weak smbios_mainboard_board_type(void)
return SMBIOS_BOARD_TYPE_UNKNOWN;
}

/*
* System Enclosure or Chassis Types as defined in SMBIOS specification.
* The default value is SMBIOS_ENCLOSURE_DESKTOP (0x03) but laptop,
* convertible, or tablet enclosure will be used if the appropriate
* system type is selected.
*/
smbios_enclosure_type __weak smbios_mainboard_enclosure_type(void)
{
return CONFIG_SMBIOS_ENCLOSURE_TYPE;
if (CONFIG(SYSTEM_TYPE_LAPTOP))
return SMBIOS_ENCLOSURE_LAPTOP;
else if (CONFIG(SYSTEM_TYPE_TABLET))
return SMBIOS_ENCLOSURE_TABLET;
else if (CONFIG(SYSTEM_TYPE_CONVERTIBLE))
return SMBIOS_ENCLOSURE_CONVERTIBLE;
else if (CONFIG(SYSTEM_TYPE_DETACHABLE))
return SMBIOS_ENCLOSURE_DETACHABLE;
else
return SMBIOS_ENCLOSURE_DESKTOP;
}

const char *__weak smbios_system_serial_number(void)
Expand Down Expand Up @@ -524,6 +539,21 @@ const char *__weak smbios_system_sku(void)
return "";
}

const char * __weak smbios_chassis_version(void)
{
return "";
}

const char * __weak smbios_chassis_serial_number(void)
{
return "";
}

const char * __weak smbios_processor_serial_number(void)
{
return "";
}

static int get_socket_type(void)
{
if (CONFIG(CPU_INTEL_SLOT_1))
Expand Down Expand Up @@ -606,6 +636,9 @@ static int smbios_write_type3(unsigned long *current, int handle)
t->thermal_state = SMBIOS_STATE_SAFE;
t->_type = smbios_mainboard_enclosure_type();
t->security_status = SMBIOS_STATE_SAFE;
t->asset_tag_number = smbios_add_string(t->eos, smbios_mainboard_asset_tag());
t->version = smbios_add_string(t->eos, smbios_chassis_version());
t->serial_number = smbios_add_string(t->eos, smbios_chassis_serial_number());
len = t->length + smbios_string_table_len(t->eos);
*current += len;
return len;
Expand Down Expand Up @@ -659,6 +692,7 @@ static int smbios_write_type4(unsigned long *current, int handle)
t->l1_cache_handle = 0xffff;
t->l2_cache_handle = 0xffff;
t->l3_cache_handle = 0xffff;
t->serial_number = smbios_add_string(t->eos, smbios_processor_serial_number());
t->processor_upgrade = get_socket_type();
len = t->length + smbios_string_table_len(t->eos);
if (cpu_have_cpuid() && cpuid_get_max_func() >= 0x16) {
Expand Down
1 change: 0 additions & 1 deletion src/arch/x86/tables.c
Expand Up @@ -2,7 +2,6 @@

#include <console/console.h>
#include <bootmem.h>
#include <bootstate.h>
#include <boot/tables.h>
#include <boot/coreboot_tables.h>
#include <arch/pirq_routing.h>
Expand Down
1 change: 1 addition & 0 deletions src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
Expand Up @@ -21,6 +21,7 @@

#define CBFS_TYPE_DELETED 0x00000000
#define CBFS_TYPE_DELETED2 0xffffffff
#define CBFS_TYPE_BOOTBLOCK 0x01
#define CBFS_TYPE_STAGE 0x10
#define CBFS_TYPE_SELF 0x20
#define CBFS_TYPE_FIT 0x21
Expand Down
8 changes: 4 additions & 4 deletions src/commonlib/include/commonlib/cbmem_id.h
Expand Up @@ -12,8 +12,10 @@
#define CBMEM_ID_CAR_GLOBALS 0xcac4e6a3
#define CBMEM_ID_CBTABLE 0x43425442
#define CBMEM_ID_CBTABLE_FWD 0x43425443
#define CBMEM_ID_CB_EARLY_DRAM 0x4544524D
#define CBMEM_ID_CONSOLE 0x434f4e53
#define CBMEM_ID_COVERAGE 0x47434f56
#define CBMEM_ID_DRTM_LOG 0x444c4f47
#define CBMEM_ID_EHCI_DEBUG 0xe4c1deb9
#define CBMEM_ID_ELOG 0x454c4f47
#define CBMEM_ID_FREESPACE 0x46524545
Expand Down Expand Up @@ -45,8 +47,6 @@
#define CBMEM_ID_ROMSTAGE_RAM_STACK 0x90357ac4
#define CBMEM_ID_ROOT 0xff4007ff
#define CBMEM_ID_SMBIOS 0x534d4254
#define CBMEM_ID_BERT_RAW_DATA 0x42455254
#define CBMEM_ID_SMM_TSEG_SPACE 0x54534547
#define CBMEM_ID_SMM_SAVE_SPACE 0x07e9acee
#define CBMEM_ID_STAGEx_META 0x57a9e000
#define CBMEM_ID_STAGEx_CACHE 0x57a9e100
Expand Down Expand Up @@ -80,8 +80,10 @@
{ CBMEM_ID_CAR_GLOBALS, "CAR GLOBALS" }, \
{ CBMEM_ID_CBTABLE, "COREBOOT " }, \
{ CBMEM_ID_CBTABLE_FWD, "COREBOOTFWD" }, \
{ CBMEM_ID_CB_EARLY_DRAM, "EARLY DRAM USAGE" }, \
{ CBMEM_ID_CONSOLE, "CONSOLE " }, \
{ CBMEM_ID_COVERAGE, "COVERAGE " }, \
{ CBMEM_ID_DRTM_LOG, "DRTM TPMLOG" }, \
{ CBMEM_ID_EHCI_DEBUG, "USBDEBUG " }, \
{ CBMEM_ID_ELOG, "ELOG " }, \
{ CBMEM_ID_FREESPACE, "FREE SPACE " }, \
Expand Down Expand Up @@ -111,8 +113,6 @@
{ CBMEM_ID_ROMSTAGE_RAM_STACK, "ROMSTG STCK" }, \
{ CBMEM_ID_ROOT, "CBMEM ROOT " }, \
{ CBMEM_ID_SMBIOS, "SMBIOS " }, \
{ CBMEM_ID_BERT_RAW_DATA, "BERT DATA " }, \
{ CBMEM_ID_SMM_TSEG_SPACE, "TSEG " }, \
{ CBMEM_ID_SMM_SAVE_SPACE, "SMM BACKUP " }, \
{ CBMEM_ID_STORAGE_DATA, "SD/MMC/eMMC" }, \
{ CBMEM_ID_TCPA_LOG, "TCPA LOG " }, \
Expand Down
1 change: 1 addition & 0 deletions src/commonlib/include/commonlib/coreboot_tables.h
Expand Up @@ -79,6 +79,7 @@ enum {
LB_TAG_MMC_INFO = 0x0035,
LB_TAG_TCPA_LOG = 0x0036,
LB_TAG_FMAP = 0x0037,
LB_TAG_DRTM_LOG = 0x0038,
LB_TAG_CMOS_OPTION_TABLE = 0x00c8,
LB_TAG_OPTION = 0x00c9,
LB_TAG_OPTION_ENUM = 0x00ca,
Expand Down
4 changes: 2 additions & 2 deletions src/commonlib/include/commonlib/stdlib.h
Expand Up @@ -29,7 +29,7 @@ static inline void *xmalloc_work(size_t size, const char *file,
}
return ret;
}
#define xmalloc(size) xmalloc_work((size), __FILE__, __FUNCTION__, __LINE__)
#define xmalloc(size) xmalloc_work((size), __FILE__, __func__, __LINE__)

static inline void *xzalloc_work(size_t size, const char *file,
const char *func, int line)
Expand All @@ -38,7 +38,7 @@ static inline void *xzalloc_work(size_t size, const char *file,
memset(ret, 0, size);
return ret;
}
#define xzalloc(size) xzalloc_work((size), __FILE__, __FUNCTION__, __LINE__)
#define xzalloc(size) xzalloc_work((size), __FILE__, __func__, __LINE__)

void *dma_malloc(size_t size);
int dma_coherent(void *ptr);
Expand Down
29 changes: 0 additions & 29 deletions src/commonlib/include/commonlib/tcpa_log_serialized.h

This file was deleted.

2 changes: 2 additions & 0 deletions src/commonlib/include/commonlib/timestamp_serialized.h
Expand Up @@ -115,6 +115,8 @@ enum timestamp_id {
TS_FSP_AFTER_FINALIZE = 959,
TS_FSP_BEFORE_END_OF_FIRMWARE = 960,
TS_FSP_AFTER_END_OF_FIRMWARE = 961,
TS_FSP_MULTI_PHASE_SI_INIT_START = 962,
TS_FSP_MULTI_PHASE_SI_INIT_END = 963,

/* 1000+ reserved for payloads (1000-1200: ChromeOS depthcharge) */

Expand Down