Merge remote-tracking branch 'origin/master' into system76

Change-Id: I4593b91276d447f8ac00daca7388fdfb22bca7f2
This commit is contained in:
Jeremy Soller
2020-06-01 14:11:34 -06:00
1742 changed files with 27631 additions and 31223 deletions

4
.gitmodules vendored
View File

@@ -42,3 +42,7 @@
url = https://review.coreboot.org/amd_blobs.git
update = none
ignore = dirty
[submodule "3rdparty/cmocka"]
path = 3rdparty/cmocka
url = ../cmocka.git
update = none

1
3rdparty/cmocka vendored Submodule

Submodule 3rdparty/cmocka added at 672c5cee79

2
3rdparty/vboot vendored

View File

@@ -23,7 +23,7 @@ write the ASL (ACPI Source Language) code yourself.
## Device drivers
Let's take a look at an example entry from
``src/mainboard/google/hatch/variant/hatch/overridetree.cb``:
``src/mainboard/google/hatch/variants/hatch/overridetree.cb``:
```
device pci 15.0 on

View File

@@ -6,3 +6,4 @@ they allow to easily reuse existing code accross platforms.
* [IPMI KCS](ipmi_kcs.md)
* [SMMSTORE](smmstore.md)
* [SoundWire](soundwire.md)

View File

@@ -0,0 +1,496 @@
# SoundWire Implementation in coreboot
## Introduction
SoundWire is an audio interface specification from the MIPI Alliance.
- Low complexity
- Low power
- Low latency
- Two pins (clock and data)
- Multi-drop capable
- Multiple audio streams
- Embedded control/command channel
The main *SoundWire Specification* is at version 1.2 and can be downloaded from
<https://mipi.org> but it is unfortunately only available to MIPI Alliance members.
There is a separate *SoundWire Discovery and Configuration (DisCo) Specification* which
is at version 1.0 and is available for non-members after providing name and email at
<https://resources.mipi.org/disco_soundwire>.
The coreboot implementation is based on the SoundWire DisCo Specification which defines
object hierarchy and properties for providing topology and configuration information to
OS kernel drivers via ACPI or DeviceTree.
SoundWire itself is architecture independent and the coreboot basic definition is also not
specific to any to any SoC. The examples in this document use ACPI to generate properties,
but the same structures and properties would be needed in a DeviceTree implementation.
## Bus
The SoundWire bus commonly consists of two pins:
* Clock: A common clock signal distributed from the master to all of the slaves.
* Data: A shared data signal that can be driven by any of the devices, and has a defined
value when no device is driving it.
While most designs have one data lane it is possible for a multi-lane device to have up
to 8 data lanes and thus would have more than two pins.
A SoundWire bus consists of one master device, up to 11 slave devices, and an optional
monitor interface for debug.
SoundWire is an enumerable bus, but not a discoverable one. That means it is required
for firmware to provide details about the connected devices to the OS.
### Controller
A SoundWire controller contains one or more master devices. The handling of multiple
masters is left up to the implementation, they may share a clock or be operated
independently or entirely in tandem. The master devices connected to a controller are
also referred to as links.
In coreboot the controller device is provided by the SoC or an add-in PCI card.
### Master
A SoundWire master (or link) device is responsible for clock and data handling, bus
management, and bit slot allocation.
In coreboot the definition of the master device is left up to the controller and the
mainboard should only need to know the controller's SoundWire topology (number of masters)
to configure `devicetree.cb`.
It may however be expected to provide some additional SoC-specific configuration data to
the controller, such as an input clock rate or a list of available masters that cannot
be determined at run time.
### Slave
SoundWire slave devices are connected to a master and respond to the two-wire control
information on the SoundWire bus. There can be up to 11 slave devices on a bus and they
are capable of interrupting and waking the host.
Slave devices may also have master links which can be connected to other slave devices.
It is also possible for a multi-lane slave device to have multiple data lanes connected
to different combinations of master and slave devices.
In coreboot the slave device is defined by a codec driver which should be found in the
source tree at `src/drivers/soundwire`.
The mainboard provides:
* Master link that this slave device is connected to.
* Unique ID that this codec responds to on the SoundWire bus.
* Multi-lane mapping. (optional)
The codec driver provides:
* Slave device properties.
* Audio Mode properties including bus frequencies and sampling rates.
* Data Port 1-14 properties such as word lengths, interrupt support, channels.
* Data Port 0 and Bulk Register Access properties. (optional)
### Monitor
A SoundWire monitor device is defined that allows for test equipment to snoop the bus and
take over and issue commands. The monitor interface is not defined for coreboot.
### Example SoundWire Bus
```
+---------------+ +---------------+
| | Clock Signal | |
| Master |-------+-------------------------------| Slave |
| Interface | | Data Signal | Interface 1 |
| |-------|-------+-----------------------| |
+---------------+ | | +---------------+
| |
| |
| |
+--+-------+--+
| |
| Slave |
| Interface 2 |
| |
+-------------+
```
## coreboot
The coreboot implementation of SoundWire integrates with the device model and takes
advantage of the hierarchical nature of `devicetree.cb` to populate the topology.
The architecture-independent SoundWire tables are defined at
src/include/device/soundwire.h
Support for new devices comes in three forms:
1. New controller and master drivers. The first implementation in coreboot is for an Intel
SoC but the SoundWire specification is in wide use on various ARM SoCs.
Controller drivers can be implemented in `src/soc` or `src/drivers` and should
strive to re-use code as much as possible between different SoC generations from the
same vendor.
2. New codec drivers. These should be implemented for each codec that is added which
supports SoundWire. The properties vary between codecs and careful study of the data sheet
is necessary to ensure proper operation.
Codec drivers should be implemented in `src/drivers/soundwire` as separate chip drivers.
As every codec is different there may not be opportunities of code re-use except between
similar codecs from the same vendor.
3. New mainboards with SoundWire support. The mainboard will combine controllers and codecs
to form a topology that is described in `devicetree.cb`. Some devices may need to provide
board-specific configuration information, and multi-lane devices will need to provide the
master/slave lane map.
## ACPI Implementation
The implementation for x86 devices relies on ACPI for providing device properties to the OS
kernel drivers.
The ACPI implementation can be found at
src/acpi/soundwire.c
And used by including
#include <acpi/acpi_soundwire.h>
### Controller
The controller driver should populate a `struct soundwire_controller`:
```c
/**
* struct soundwire_controller - SoundWire controller properties.
* @master_count: Number of masters present on this device.
* @master_list: One entry for each master device.
*/
struct soundwire_controller {
unsigned int master_list_count;
struct soundwire_link master_list[SOUNDWIRE_MAX_DEV];
};
```
Once the detail of the master links are specified in the `master_list` variable, the controller
properties for the ACPI object can be generated:
```c
struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
soundwire_gen_controller(dsd, &soc_controller, NULL);
acpi_dp_write(dsd);
```
If the controller needs to generate custom properties for links it can provide a callback
function to `soundwire_gen_controller()` instead of passing NULL:
```c
static void controller_link_prop_cb(struct acpi_dp *dsd, unsigned int id,
struct soundwire_controller *controller)
{
acpi_dp_add_integer(dsd, "custom-link-property", 1);
}
```
### Codec
The codec driver should populate a *struct soundwire_codec* with necessary properties:
```c
/**
* struct soundwire_codec - Contains all configuration for a SoundWire codec slave device.
* @slave: Properties for slave device.
* @audio_mode: Properties for Audio Mode for Data Ports 1-14.
* @dpn: Properties for Data Ports 1-14.
* @multilane: Properties for slave multilane device. (optional)
* @dp0_bra_mode: Properties for Bulk Register Access mode for Data Port 0. (optional)
* @dp0: Properties for Data Port 0 for Bulk Register Access. (optional)
*/
struct soundwire_codec {
struct soundwire_slave *slave;
struct soundwire_audio_mode *audio_mode[SOUNDWIRE_MAX_DEV];
struct soundwire_dpn_entry dpn[SOUNDWIRE_MAX_DPN - SOUNDWIRE_MIN_DPN];
struct soundwire_multilane *multilane;
struct soundwire_bra_mode *dp0_bra_mode[SOUNDWIRE_MAX_DEV];
struct soundwire_dp0 *dp0;
};
```
Many of these properties are optional, and depending on the codec will not be supported.
#### Slave Device Properties
These properties provide information about the codec device and what features it supports:
* Wake capability
* Clock stop behavior
* Clock and channel state machine behavior
* Features like register pages, broadcast read, bank delay, and high performance PHY
#### Multi-lane Slave Device Properties
Most slave devices have a single data pin and a single lane, but it is possible for up to
7 other lanes to be supported on a device. These lanes can be connected to other master
links or to other slave devices.
If a codec supports this feature it must indicate that by providing an entry for
`struct soundwire_multilane` in the chip configuration.
```c
/**
* struct drivers_soundwire_example_config - Example codec configuration.
* @multilane: Multi-lane slave configuration.
*/
struct drivers_soundwire_example_config {
struct soundwire_multilane multilane;
};
```
The mainboard is required to provide the lane map in `devicetree.cb` for any codec that has
multiple lanes connected. This includes the definition up to 7 entries that indicate which
lane number on the slave devices (array index starting at 1) maps to which other device:
```
chip drivers/soundwire/multilane_codec
register "multilane.lane_mapping" = "{
{
# Slave Lane 1 maps to Master Lane 2
.lane = 1,
.direction = MASTER_LANE,
.connection.master_lane = 2
},
{
# Slave Lane 3 maps to Slave Link B
.lane = 3,
.direction = SLAVE_LINK,
.connection.slave_link = 1
}
}"
device generic 0.0 on end
end
```
#### Data Port 0 Properties
SoundWire Data Port 0 (DP0) is a special port used for control and status operation relating
to the whole device interface, and as a special data port for bulk read/write operations.
The properties for data port 0 are different from that of data ports 1-14 and are about the
control channel behavior and the overall bulk register mode.
Data port 0 is not required to be supported by the slave device.
#### Bulk Register Access Mode Properties
Bulk Register Access (BRA) is an optional mechanism for transporting higher bandwidth of
register operations than the typical command mechanism. The BRA protocol is a particular
format of the data on the (optional) data port 0 connection between the master and slave.
The BRA protocol may have alignment or timing requirements that are directly related to the
bus frequencies. As a result there may be several configurations listed, for symmetry with
the audio modes paired with data ports 1-14.
#### Data Port 1-14 Properties
Data ports 1-14 are typically dedicated to streaming audio payloads, and each data port can
have from 1 to 8 channels. There are different levels of data ports, with some registers
being required and supported on all data ports and some optional registers only being used
on some data ports.
Data ports can have both a sink and a source component, and the codec may support one or
both of these on each port.
Similar to data port 0 the properties defined here describe the capabilities and supported
features of each data port, and they may be configured separately. For example the Maxim
MAX98373 codec supports a 32bit source data port for speaker output, and a 16bit sink data
port for speaker sense data.
#### Audio Mode Properties
Each data port may be tied to one or more audio modes. The audio mode describes the actual
audio capabilities of the codec, including supported frequencies and sample rates. These
modes can be shared by multiple data ports and do not need to be duplicated.
For example:
```
static struct soundwire_audio_mode audio_mode = {
.bus_frequency_max = 24 * MHz,
.bus_frequency_min = 24 * KHz,
.max_sampling_frequency = 192 * KHz,
.min_sampling_frequency = 8 * KHz,
};
static struct soundwire_dpn codec_dp1 = {
[...]
.port_audio_mode_count = 1,
.port_audio_mode_list = {0}
};
static struct soundwire_dpn codec_dp3 = {
[...]
.port_audio_mode_count = 1,
.port_audio_mode_list = {0}
};
```
### Generating Codec Properties
Once the properties are known it can generate the ACPI code with:
```c
struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
soundwire_gen_codec(dsd, &soundwire_codec, NULL);
acpi_dp_write(dsd);
```
If the codec needs to generate custom properties for links it can provide a callback
function to `soundwire_gen_codec()` instead of passing NULL:
```c
static void codec_dp_prop_cb(struct acpi_dp *dsd, unsigned int id,
struct soundwire_codec *codec)
{
acpi_dp_add_integer(dsd, "custom-dp-property", 1);
}
```
#### Codec Address
SoundWire slave devices use a SoundWire defined ACPI _ADR that requires a 64-bit integer
and uses the master link ID and slave device unique ID to form a unique address for the
device on this controller.
SoundWire addresses must be distinguishable from all other slave devices on the same master
link, so multiple instances of the same manufacturer and part on the same master link will
need different unique IDs. The value is typically determined by strapping pins on the codec
chip and can be decoded for this table with the codec datasheet and board schematics.
```c
/**
* struct soundwire_address - SoundWire ACPI Device Address Encoding.
* @version: SoundWire specification version from &enum soundwire_version.
* @link_id: Zero-based SoundWire Link Number.
* @unique_id: Unique ID for multiple devices.
* @manufacturer_id: Manufacturer ID from include/device/mipi_ids.h.
* @part_id: Vendor defined part ID.
* @class: MIPI class encoding in &enum mipi_class.
*/
struct soundwire_address {
enum soundwire_version version;
uint8_t link_id;
uint8_t unique_id;
uint16_t manufacturer_id;
uint16_t part_id;
enum mipi_class class;
};
```
This ACPI address can be generated by calling the provided acpigen function:
acpigen_write_ADR_soundwire_device(const struct soundwire_address *sdw);
### Mainboard
The mainboard needs to select appropriate drivers in `Kconfig` and define the topology in
`devicetree.cb` with the controllers and codecs that exist on the board.
The topology uses the **generic** device to describe SoundWire:
```c
struct generic_path {
unsigned int id; /* SoundWire Master Link ID */
unsigned int subid; /* SoundWire Slave Unique ID */
};
```
This allows devices to be specified in `devicetree.cb` with the necessary information to
generate ACPI address and device properties.
```
chip drivers/intel/soundwire
# SoundWire Controller 0
device generic 0 on
chip drivers/soundwire/codec1
# SoundWire Link 0 ID 0
device generic 0.0 on end
end
chip drivers/soundwire/codec2
# SoundWire Link 1 ID 2
device generic 1.2 on end
end
end
end
```
## Volteer Example
This is an example of an Intel Tiger Lake reference board using SoundWire Link 0 for the
headphone codec connection, and Link 1 for connecting two speaker amps for stereo speakers.
The mainboard can be found at
src/mainboard/google/volteer
```
+------------------+ +-------------------+
| | | Headphone Codec |
| Intel Tiger Lake | +--->| Realtek ALC5682 |
| SoundWire | | | ID 1 |
| Controller | | +-------------------+
| | |
| Link 0 +----+ +-------------------+
| | | Left Speaker Amp |
| Link 1 +----+--->| Maxim MAX98373 |
| | | | ID 3 |
| Link 2 | | +-------------------+
| | |
| Link 3 | | +-------------------+
| | | | Right Speaker Amp |
+------------------+ +--->| Maxim MAX98373 |
| ID 7 |
+-------------------+
```
This implementation requires a controller driver for the Intel Tigerlake SoC and a codec
driver for the Realtek and Maxim chips. If those drivers did not already exist they would
need to be added and reviewed separately before adding the support to the mainboard.
The volteer example requires some `Kconfig` options to be selected:
```
config BOARD_GOOGLE_BASEBOARD_VOLTEER
select DRIVERS_INTEL_SOUNDWIRE
select DRIVERS_SOUNDWIRE_ALC5682
select DRIVERS_SOUNDWIRE_MAX98373
```
And the following `devicetree.cb` entries to define this topology:
```
device pci 1f.3 on
chip drivers/intel/soundwire
# SoundWire Controller 0
device generic 0 on
chip drivers/soundwire/alc5682
# SoundWire Link 0 ID 1
register "desc" = ""Headphone Jack""
device generic 0.1 on end
end
chip drivers/soundwire/max98373
# SoundWire Link 0 ID 1
register "desc" = ""Left Speaker Amp""
device generic 1.3 on end
end
chip drivers/soundwire/max98373
# SoundWire Link 1 ID 7
register "desc" = ""Right Speaker Amp""
device generic 1.7 on end
end
end
end
end
```

View File

@@ -10,7 +10,7 @@ coreboot consists of multiple stages that are compiled as separate binaries and
are inserted into the CBFS with custom compression. The bootblock usually doesn't
have compression while the ramstage and payload are compressed with LZMA.
Each stage loads the next stage a given address (possibly decompressing it).
Each stage loads the next stage at given address (possibly decompressing it).
Some stages are relocatable and can be placed anywhere in DRAM. Those stages are
usually cached in CBMEM for faster loading times on ACPI S3 resume.

View File

@@ -254,6 +254,23 @@ commit message itself:
The script 'util/gitconfig/rebase.sh' can be used to help automate this.
Other tags such as 'Commit-Queue' can simply be removed.
* Check if there's documentation that needs to be updated to remain current
after your change. If there's no documentation for the part of coreboot
you're working on, consider adding some.
* When contributing a significant change to core parts of the code base (such
as the boot state machine or the resource allocator), or when introducing
a new way of doing something that you think is worthwhile to apply across
the tree (e.g. board variants), please bring up your design on the [mailing
list](../community/forums.md). When changing behavior substantially, an
explanation of what changes and why may be useful to have, either in the
commit message or, if the discussion of the subject matter needs way more
space, in the documentation. Since "what we did in the past and why it isn't
appropriate anymore" isn't the most useful reading several years down the road,
such a description could be put into the release notes for the next version
(that you can find in Documentation/releases/) where it will inform people
now without cluttering up the regular documentation, and also gives a nice
shout-out to your contribution by the next release.
Expectations contributors should have
-------------------------------------

View File

@@ -88,11 +88,28 @@ know through which interface the EDID can be queried:
select GFX_GMA_ANALOG_I2C_HDMI_C # or
select GFX_GMA_ANALOG_I2C_HDMI_D
Beside Kconfig options, *libgfxinit* needs to know which ports are
implemented on a board and should be probed for displays. The mapping
between the physical ports and these entries depends on the hardware
implementation and can be recovered by testing or studying the output
of `intelvbttool` or `intel_vbt_decode`.
*libgfxinit* needs to know which ports are implemented on a board
and should be probed for displays. There are two mechanisms to
constrain the list of ports to probe, 1. port presence straps on
the mainboard, and 2. a list of ports provided by *coreboot* (see
below).
Presence straps are configured via the state of certains pins of
the chipset at reset time. They are documented in the chipset's
datasheets. By default, *libgfxinit* honors these straps for
safety. However, some boards don't implement the straps correctly.
If ports are not strapped as implemented by error, one can select
an option to ignore the straps:
select GFX_GMA_IGNORE_PRESENCE_STRAPS
In the opposite case, that ports are strapped as implemented,
but are actually unconnected, one has to make sure that the
list of ports in *coreboot* omits them.
The mapping between the physical ports and these entries depends on
the hardware implementation and can be recovered by testing or
studying the output of `intelvbttool` or `intel_vbt_decode`.
Each board has to implement the package `GMA.Mainboard` with a list:
ports : HW.GFX.GMA.Display_Probing.Port_List;

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -0,0 +1,147 @@
# Dell OptiPlex 9010
This page describes how to run coreboot on Dell OptiPlex 9010 SFF.
![](optiplex_9010.jpg)
## Technology
```eval_rst
+------------+---------------------------------------------------------------+
| CPU | Intel Core 2nd Gen (Sandybridge) or 3rd Gen (Ivybridge) |
+------------+---------------------------------------------------------------+
| DRAM | Up to 4 DIMM slots, up to 32GB 1600MHz non-ECC DDR3 SDRAM |
+------------+---------------------------------------------------------------+
| Chipset | Intel Q77 Express |
+------------+---------------------------------------------------------------+
| Super I/O | SMSC SCH5545 (or SCH5544) with Environmental Controller |
+------------+---------------------------------------------------------------+
| TPM | ST Microelectronics ST33ZP24 |
+------------+---------------------------------------------------------------+
| Boot | From USB, SATA, NVMe (using PCIe x4 expansion card) |
+------------+---------------------------------------------------------------+
| Power | 200W-275W PSU |
+------------+---------------------------------------------------------------+
```
More specifications on [Dell OptiPlex 9010 specifications].
## Required proprietary blobs
```eval_rst
+------------------+---------------------------------+---------------------+
| Binary file | Apply | Required / Optional |
+==================+=================================+=====================+
| smsc_sch5545.bin | SMSC SCH5545 EC | Optional |
+------------------+---------------------------------+---------------------+
| microcode | CPU microcode | Required |
+------------------+---------------------------------+---------------------+
```
Microcode updates are automatically included into the coreboot image by build
system from the `3rdparty/intel-microcode` submodule.
SMSC SC5545 EC firmware is optional, however lack of the binary will result in
EC malfunction after power failure and fans running at full speed. The blob can
be extracted from original firmware. It should be located under a file with
GUID D386BEB8-4B54-4E69-94F5-06091F67E0D3, raw section. The file begins with a
signature `SMSCUBIM`. The easiest way to do this is to use [UEFITool] and
`Extract body` option on the raw section of the file.
## Flashing coreboot
```eval_rst
+---------------------+--------------------------+
| Type | Value |
+=====================+==========================+
| Socketed flash | no |
+---------------------+--------------------------+
| Model | MX25L6406E/MX25L3206E |
+---------------------+--------------------------+
| Size | 8 + 4 MiB |
+---------------------+--------------------------+
| Package | SOIC-16 + SOIC-8 |
+---------------------+--------------------------+
| Write protection | chipset PRR |
+---------------------+--------------------------+
| Dual BIOS feature | no |
+---------------------+--------------------------+
| Internal flashing | yes |
+---------------------+--------------------------+
```
### Internal programming
The SPI flash can be accessed using [flashrom].
flashrom -p internal -w coreboot.rom --ifd -i bios
Internal programming will not work when migrating from original UEFI firmware.
One will have to short the SERVICE_MODE jumper to enable HMRFPO and then boot
the machine to flash it.
### External programming
The external access to flash chip is available through standard SOP-8 clip
and/or SOP-16 clip on the right side of the CPU fan (marked on the board
image). The voltage of SPI flash is 3.3V.
There are no restrictions as to the programmer device. It is only recommended
to flash firmware without supplying power. There are no diodes connected to the
flash chips. External programming can be performed, for example using OrangePi
and Armbian. You can use linux_spi driver which provides communication with SPI
devices. Example command to program SPI flash with OrangePi using linux_spi:
flashrom -w coreboot.rom -p linux_spi:dev=/dev/spidev1.0,spispeed=16000
## Schematics
There are no schematics for SFF, but if one looks for MT/DT schematics, they
can be found publicly. Most of the schematics should match the SFF (although
MT/DT has additional PCIe and PCI slot).
## Known issues
- There seems to be a problem with DRAM clearing on reboot. The SSKPD register
still contains 0xCAFE which leads to reset loop.
## Untested
Not all mainboard's peripherals and functions were tested because of lack of
the cables or not being populated on the board case.
- Internal USB 2.0 header
- Wake from S3 using serial port
- Wake-on-Lan from ACPI S4/S5
## Working
- USB 3.0 and 2.0 rear and front ports (SeaBIOS and Linux 4.19)
- Gigabit Ethernet
- VGA and 2x DP port using libgfxinit
- flashrom
- PCIe x1 WiFi in PCIe x4 slot
- NVMe PCIe x4 using PCIe x4 expansion card
- PCIe x16 PEG port using Dell Radeon HD 7570
- SATA ports (SATA disks and DVD)
- Super I/O serial port 0 (RS232 DB9 connector on the rear side)
- SMBus (reading SPD from DIMMs)
- CPU initialization using Intel i7-3770
- Sandy Bridge/Ivy Bridge native RAM initialization
- SeaBIOS payload (version rel-1.13.0)
- PS/2 keyboard and mouse (including wake support)
- LPC debug header (requires soldering of the pin header and shorting RF24 for
LPC clock)
- USB debug dongle (the most bottom USB 2.0 port under RJ45 on the read side)
- SMSC SCH5545 Super I/O initialization
- SMSC SCH5545 EC initialization and firmware update
- SMSC SCH5545 EC automatic fan control
- TPM 1.2
- Booting Debian 10, Ubuntu 18.04, QubesOS R4.01
- Boot with cleaned ME
- Intruder detection
- Wake-on-Lan from ACPI S3
[flashrom]: https://flashrom.org/Flashrom
[Dell OptiPlex 9010 specifications]: https://www.dell.com/downloads/global/products/optix/en/dell_optiplex_9010_spec_sheet.pdf
[UEFITool]: https://github.com/LongSoft/UEFITool

View File

@@ -26,6 +26,10 @@ This section contains documentation about coreboot on specific mainboards.
- [CN81XX EVB SFF](cavium/cn8100_sff_evb.md)
## Dell
- [OptiPlex 9010 SFF](dell/optiplex_9010.md)
## Emulation
The boards in this section are not real mainboards, but emulators.

View File

@@ -67,3 +67,4 @@ the remaining space for the `bios` partition.
[me_cleaner]: ../../northbridge/intel/sandybridge/me_cleaner.md
[external programmer]: ../../flash_tutorial/index.md
[flashing tutorial]: ../../flash_tutorial/index.md

View File

@@ -67,6 +67,9 @@ be more frequent than was needed, so we scaled it back to twice a year.
ask for testing.
- [ ] Test the commit selected for release.
- [ ] Update release notes with actual commit id, push to repo.
- [ ] Create new release notes doc template for the next version.
- [ ] Fill in the release date, remove "Upcoming release" and other filler
from the current release notes.
- [ ] Run release script.
- [ ] Run vboot_list script.
- [ ] Test the release from the actual release tarballs.

View File

@@ -1,5 +1,5 @@
Upcoming release - coreboot 4.12
================================
coreboot 4.12
=============
coreboot 4.12 was released on May 12th, 2020.

View File

@@ -237,6 +237,51 @@ Picasso's FSP is compatible with rev. 2.0 of the External Architecture
Specification. Deviations, e.g., no FSP-T support, shall be published
in an Integration Guide.
## APCB setup
APCBs are used to provide the PSP with SPD information and optionally a set of
GPIOs to use for selecting which SPD to load.
### Prebuilt
The picasso `Makefile` expects APCBs to be located in
`3rdparty/blobs/mainboard/$(MAINBOARDDIR)`. If you have a pre-built binary just
add the following to your mainboard's Makefile.
```
# i.e., 3rdparty/blobs/mainboard/amd/mandolin/APCB_mandolin.bin
APCB_SOURCES = mandolin
```
### Generating APCBs
If you have a template APCB file, the `apcb_edit` tool can be used to inject the
SPD and GPIOs used to select the correct slot. Entries should match this
pattern `{NAME}_x{1,2}`. There should be a matching SPD hex file in
`SPD_SOURCES_DIR` matching the pattern `{NAME}.spd.hex`.
The `_x{1,2}` suffix denotes single or dual channel. Up to 16 slots can be used.
If a slot is empty, the special empty keyword can be used. This will generate
an APCB with an empty SPD.
```
APCB_SOURCES = hynix-HMA851S6CJR6N-VK_x1 # 0b0000
APCB_SOURCES += hynix-HMAA1GS6CMR6N-VK_x2 # 0b0001
APCB_SOURCES += empty # 0b0010
APCB_SOURCES += samsung-K4A8G165WC-BCWE_x1 # 0b0011
```
#### APCB Board ID GPIO configuration.
The GPIOs determine which memory SPD will be used during boot.
```
# APCB_BOARD_ID_GPIO[0-3] = GPIO_NUMBER GPIO_IO_MUX GPIO_BANK_CTL
# GPIO_NUMBER: FCH GPIO number
# GPIO_IO_MUX: Value write to IOMUX to configure this GPIO
# GPIO_BANK_CTL: Value write to GPIOBankCtl[23:16] to configure this GPIO
APCB_BOARD_ID_GPIO0 = 121 1 0
APCB_BOARD_ID_GPIO1 = 120 1 0
APCB_BOARD_ID_GPIO2 = 131 3 0
APCB_BOARD_ID_GPIO3 = 116 1 0
```
## Footnotes
1. *AMD Platform Security Processor BIOS Architecture Design Guide

View File

@@ -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.

View File

@@ -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)

View File

@@ -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);
}
```

View File

@@ -7,14 +7,17 @@ Copyright © 2012 Intel Corporation
Copyright 2012 Red Hat Inc.
Copyright 2013 Google Inc.
Copyright 2014 Google Inc.
Copyright 2014 The Chromium OS Authors. All rights reserved.
Copyright 2015 Google Inc.
Copyright 2015, Google Inc.
Copyright 2016 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Copyright 2016 The Chromium OS Authors. All rights reserved.
Copyright 2017-2019 Eltan B.V.
Copyright 2017 Google Inc.
Copyright 2018 Generated Code
Copyright 2018-present Facebook, Inc.
Copyright 2019 9Elements Agency GmbH <patrick.rudolph@9elements.com>
Copyright 2019 The Chromium OS Authors. All rights reserved.
Copyright (C) 2002 David S. Peterson. All rights reserved.
Copyright (c) 2003-2016 Cavium Inc. (support@cavium.com). All rights
Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights
@@ -25,17 +28,21 @@ Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
Copyright (C) 2008 Advanced Micro Devices, Inc.
Copyright (c) 2008, Google Inc.
Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
Copyright (C) 2008 Uwe Hermann <uwe@hermann-uwe.de>
Copyright (C) 2009-2010 coresystems GmbH
Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
Copyright (c) 2010-2017, The Regents of the University of California
Copyright (c) 2010, Code Aurora Forum. All rights reserved.
Copyright (C) 2010 coresystems GmbH
Copyright (c) 2010 Per Odlund <per.odlund@armagedon.se>
Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Copyright (c) 2011-2012 The Linux Foundation. All rights reserved.
Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Copyright (c) 2011 - 2014 The Linux Foundation. All rights reserved.
Copyright (c) 2011-2019 The Linux Foundation. All rights reserved.
Copyright (c) 2011, Google Inc.
Copyright (C) 2011 secunet Security Networks AG
Copyright (c) 2012 - 2013, 2015, 2019 The Linux Foundation.
Copyright (c) 2012 - 2013, 2015 The Linux Foundation. All rights reserved.
Copyright (c) 2012 - 2013 The Linux Foundation. All rights reserved.
@@ -76,7 +83,9 @@ Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor E
Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved.
Copyright (C) 2016 Google Inc.
Copyright (c) 2016, The Regents of the University of California (Regents).
Copyright (C) 2018-2019 Eltan B.V.
Copyright (C) 2018 - 2019 The Linux Foundation. All rights reserved.
Copyright (c) 2018 Eltan B.V.
Copyright (c) 2018, HardenedLinux.
Copyright (C) 2018, The Linux Foundation. All rights reserved.
Copyright Dave Airlie <airlied@redhat.com>

View File

@@ -1,33 +1,4 @@
##
##
## Copyright (C) 2008 Advanced Micro Devices, Inc.
## Copyright (C) 2008 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2009-2010 coresystems GmbH
## Copyright (C) 2011 secunet Security Networks AG
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions
## are met:
## 1. Redistributions of source code must retain the above copyright
## notice, this list of conditions and the following disclaimer.
## 2. Redistributions in binary form must reproduce the above copyright
## notice, this list of conditions and the following disclaimer in the
## documentation and/or other materials provided with the distribution.
## 3. The name of the author may not be used to endorse or promote products
## derived from this software without specific prior written permission.
##
## THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
## ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
## DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
## OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
## OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
## SUCH DAMAGE.
##
## SPDX-License-Identifier: BSD-3-Clause
ifneq ($(words $(CURDIR)),1)
$(error Error: Path to the main directory cannot contain spaces)

View File

@@ -562,6 +562,8 @@ $(AMDFWTOOL): $(top)/util/amdfwtool/amdfwtool.c
@printf " HOSTCC $(subst $(obj)/,,$(@))\n"
$(HOSTCC) $(HOSTCFLAGS) -DCONFIG_ROM_SIZE=$(CONFIG_ROM_SIZE) -o $@ $<
APCB_EDIT_TOOL:=$(top)/util/apcb/apcb_edit.py
CBOOTIMAGE:=$(objutil)/cbootimage/cbootimage
FUTILITY?=$(objutil)/futility/futility
@@ -596,6 +598,17 @@ bootblock-y+=$(DEVICETREE_STATIC_C)
postcar-y+=$(DEVICETREE_STATIC_C)
smm-y+=$(DEVICETREE_STATIC_C)
# Ensure static.c and static.h are created before any objects are compiled
ramstage-c-deps+=$(DEVICETREE_STATIC_C)
romstage-c-deps+=$(DEVICETREE_STATIC_C)
verstage-c-deps+=$(DEVICETREE_STATIC_C)
bootblock-c-deps+=$(DEVICETREE_STATIC_C)
postcar-c-deps+=$(DEVICETREE_STATIC_C)
smm-c-deps+=$(DEVICETREE_STATIC_C)
.PHONY: devicetree
devicetree: $(DEVICETREE_STATIC_C)
#######################################################################
# Clean up rules
clean-abuild:

View File

@@ -0,0 +1,9 @@
CONFIG_USE_OPTION_TABLE=y
CONFIG_USE_BLOBS=y
CONFIG_VENDOR_DELL=y
CONFIG_ONBOARD_VGA_IS_PRIMARY=y
# CONFIG_DRIVERS_UART_8250IO is not set
CONFIG_PCIEXP_CLK_PM=y
CONFIG_SEABIOS_PS2_TIMEOUT=3000
CONFIG_POST_DEVICE_LPC=y
CONFIG_HAVE_EM100_SUPPORT=y

View File

@@ -72,6 +72,35 @@ static int is_valid_fraction(const struct fraction *f)
return f->d != 0;
}
static int is_valid_scale(const struct scale *s)
{
return is_valid_fraction(&s->x) && is_valid_fraction(&s->y);
}
static void add_fractions(struct fraction *out,
const struct fraction *f1, const struct fraction *f2)
{
int64_t n, d;
int shift;
n = (int64_t)f1->n * f2->d + (int64_t)f2->n * f1->d;
d = (int64_t)f1->d * f2->d;
/* Simplest way to reduce the fraction until fitting in int32_t */
shift = log2(MAX(ABS(n), ABS(d)) >> 31);
if (shift > 0) {
n >>= shift;
d >>= shift;
}
out->n = n;
out->d = d;
}
static void add_scales(struct scale *out,
const struct scale *s1, const struct scale *s2)
{
add_fractions(&out->x, &s1->x, &s2->x);
add_fractions(&out->y, &s1->y, &s2->y);
}
/*
* Transform a vector:
* x' = x * a_x + offset_x
@@ -82,7 +111,7 @@ static int transform_vector(struct vector *out,
const struct scale *a,
const struct vector *offset)
{
if (!is_valid_fraction(&a->x) || !is_valid_fraction(&a->y))
if (!is_valid_scale(a))
return CBGFX_ERROR_INVALID_PARAMETER;
out->x = a->x.n * in->x / a->x.d + offset->x;
out->y = a->y.n * in->y / a->y.d + offset->y;
@@ -211,7 +240,6 @@ static int cbgfx_init(void)
int draw_box(const struct rect *box, const struct rgb_color *rgb)
{
struct vector top_left;
struct vector size;
struct vector p, t;
if (cbgfx_init())
@@ -222,14 +250,13 @@ int draw_box(const struct rect *box, const struct rgb_color *rgb)
.x = { .n = box->offset.x, .d = CANVAS_SCALE, },
.y = { .n = box->offset.y, .d = CANVAS_SCALE, }
};
const struct scale size_s = {
.x = { .n = box->size.x, .d = CANVAS_SCALE, },
.y = { .n = box->size.y, .d = CANVAS_SCALE, }
const struct scale bottom_right_s = {
.x = { .n = box->offset.x + box->size.x, .d = CANVAS_SCALE, },
.y = { .n = box->offset.y + box->size.y, .d = CANVAS_SCALE, }
};
transform_vector(&top_left, &canvas.size, &top_left_s, &canvas.offset);
transform_vector(&size, &canvas.size, &size_s, &vzero);
add_vectors(&t, &top_left, &size);
transform_vector(&t, &canvas.size, &bottom_right_s, &canvas.offset);
if (within_box(&t, &canvas) < 0) {
LOG("Box exceeds canvas boundary\n");
return CBGFX_ERROR_BOUNDARY;
@@ -247,8 +274,8 @@ int draw_rounded_box(const struct scale *pos_rel, const struct scale *dim_rel,
const struct fraction *thickness,
const struct fraction *radius)
{
struct scale pos_end_rel;
struct vector top_left;
struct vector size;
struct vector p, t;
if (cbgfx_init())
@@ -256,9 +283,12 @@ int draw_rounded_box(const struct scale *pos_rel, const struct scale *dim_rel,
const uint32_t color = calculate_color(rgb, 0);
if (!is_valid_scale(pos_rel) || !is_valid_scale(dim_rel))
return CBGFX_ERROR_INVALID_PARAMETER;
add_scales(&pos_end_rel, pos_rel, dim_rel);
transform_vector(&top_left, &canvas.size, pos_rel, &canvas.offset);
transform_vector(&size, &canvas.size, dim_rel, &vzero);
add_vectors(&t, &top_left, &size);
transform_vector(&t, &canvas.size, &pos_end_rel, &canvas.offset);
if (within_box(&t, &canvas) < 0) {
LOG("Box exceeds canvas boundary\n");
return CBGFX_ERROR_BOUNDARY;

View File

@@ -45,9 +45,9 @@ typedef long ptrdiff_t;
#define INT64_MAX (9223372036854775807LL)
#define INT8_MIN (-INT8_MAX - 1)
#define INT16_MIN (-INT16_MIN - 1)
#define INT32_MIN (-INT32_MIN - 1)
#define INT64_MIN (-INT64_MIN - 1)
#define INT16_MIN (-INT16_MAX - 1)
#define INT32_MIN (-INT32_MAX - 1)
#define INT64_MIN (-INT64_MAX - 1)
#define UINT8_MAX (255)
#define UINT16_MAX (65535)

View File

@@ -491,10 +491,10 @@ unsigned long long int strtoull(const char *ptr, char **endptr, int base)
else if (ptr[0] == '0') {
base = 8;
ptr++;
}
else
} else {
base = 10;
}
}
/* Base 16 allows the 0x on front - so skip over it */
@@ -516,7 +516,8 @@ unsigned long long int strtoull(const char *ptr, char **endptr, int base)
unsigned long int strtoul(const char *ptr, char **endptr, int base)
{
unsigned long long val = strtoull(ptr, endptr, base);
if (val > ULONG_MAX) return ULONG_MAX;
if (val > ULONG_MAX)
return ULONG_MAX;
return val;
}
@@ -598,8 +599,6 @@ char* strtok_r(char *str, const char *delim, char **ptr)
return start;
}
static char **strtok_global;
/**
* Extract first token in string str that is delimited by a character in tokens.
* Destroys str, eliminates the token delimiter and uses global state.
@@ -609,7 +608,9 @@ static char **strtok_global;
*/
char *strtok(char *str, const char *delim)
{
return strtok_r(str, delim, strtok_global);
static char *strtok_ptr;
return strtok_r(str, delim, &strtok_ptr);
}
/**

View File

@@ -1,6 +1,3 @@
##
##
##
## SPDX-License-Identifier: GPL-2.0-only
mainmenu "coreboot configuration"

View File

@@ -6,9 +6,11 @@ ramstage-y += acpi.c
ramstage-y += acpigen.c
ramstage-y += acpigen_dsm.c
ramstage-y += acpigen_ps2_keybd.c
ramstage-y += acpigen_usb.c
ramstage-y += device.c
ramstage-y += pld.c
ramstage-y += sata.c
ramstage-y += soundwire.c
ifneq ($(wildcard src/mainboard/$(MAINBOARDDIR)/acpi_tables.c),)
ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/acpi_tables.c

View File

@@ -18,6 +18,7 @@
#include <device/device.h>
#include <device/pci_def.h>
#include <device/pci_type.h>
#include <device/soundwire.h>
static char *gencurrent;
@@ -1885,3 +1886,30 @@ void acpigen_write_ADR_pci_device(const struct device *dev)
assert(dev->path.type == DEVICE_PATH_PCI);
acpigen_write_ADR_pci_devfn(dev->path.pci.devfn);
}
/**
* acpigen_write_ADR_soundwire_device() - SoundWire ACPI Device Address Encoding.
* @address: SoundWire device address properties.
*
* From SoundWire Discovery and Configuration Specification Version 1.0 Table 3.
*
* 63..52 - Reserved (0)
* 51..48 - Zero-based SoundWire Link ID, relative to the immediate parent.
* Used when a Controller has multiple master devices, each producing a
* separate SoundWire Link. Set to 0 for single-link controllers.
* 47..0 - SoundWire Device ID Encoding from specification version 1.2 table 88
* 47..44 - SoundWire specification version that this device supports
* 43..40 - Unique ID for multiple devices
* 39..24 - MIPI standard manufacturer code
* 23..08 - Vendor defined part ID
* 07..00 - MIPI class encoding
*/
void acpigen_write_ADR_soundwire_device(const struct soundwire_address *address)
{
acpigen_write_ADR((((uint64_t)address->link_id & 0xf) << 48) |
(((uint64_t)address->version & 0xf) << 44) |
(((uint64_t)address->unique_id & 0xf) << 40) |
(((uint64_t)address->manufacturer_id & 0xffff) << 24) |
(((uint64_t)address->part_id & 0xffff) << 8) |
(((uint64_t)address->class & 0xff)));
}

View File

@@ -168,6 +168,8 @@ static uint32_t rest_of_keymaps[] = {
KEYMAP(0xd0, KEY_DOWN),
KEYMAP(0xcd, KEY_RIGHT),
KEYMAP(0xc8, KEY_UP),
/* Power Key */
KEYMAP(0xde, KEY_POWER),
};
static void ssdt_generate_physmap(struct acpi_dp *dp, uint8_t num_top_row_keys,

136
src/acpi/acpigen_usb.c Normal file
View File

@@ -0,0 +1,136 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <acpi/acpi.h>
#include <acpi/acpi_device.h>
#include <acpi/acpigen.h>
#include <acpi/acpigen_usb.h>
static const char *power_role_to_str(enum usb_typec_power_role power_role)
{
switch (power_role) {
case TYPEC_POWER_ROLE_SOURCE:
return "source";
case TYPEC_POWER_ROLE_SINK:
return "sink";
case TYPEC_POWER_ROLE_DUAL:
return "dual";
default:
return "unknown";
}
}
static const char *try_power_role_to_str(enum usb_typec_try_power_role try_power_role)
{
switch (try_power_role) {
case TYPEC_TRY_POWER_ROLE_NONE:
/*
* This should never get returned; if there is no try-power role for a device,
* then the try-power-role field is not added to the DSD. Thus, this is just
* for completeness.
*/
return "none";
case TYPEC_TRY_POWER_ROLE_SINK:
return "sink";
case TYPEC_TRY_POWER_ROLE_SOURCE:
return "source";
default:
return "unknown";
}
}
static const char *data_role_to_str(enum usb_typec_data_role data_role)
{
switch (data_role) {
case TYPEC_DATA_ROLE_DFP:
return "host";
case TYPEC_DATA_ROLE_UFP:
return "device";
case TYPEC_DATA_ROLE_DUAL:
return "dual";
default:
return "unknown";
}
}
/* Add port capabilities as DP properties */
static void add_port_caps(struct acpi_dp *dsd,
const struct typec_connector_class_config *config)
{
acpi_dp_add_string(dsd, "power-role", power_role_to_str(config->power_role));
acpi_dp_add_string(dsd, "data-role", data_role_to_str(config->data_role));
if (config->try_power_role != TYPEC_TRY_POWER_ROLE_NONE)
acpi_dp_add_string(dsd, "try-power-role",
try_power_role_to_str(config->try_power_role));
}
static void add_device_ref(struct acpi_dp *dsd,
const char *prop_name,
const struct device *dev)
{
const char *path;
char *fresh;
if (!dev)
return;
/*
* Unfortunately, the acpi_dp_* API doesn't write out the data immediately, thus we need
* different storage areas for all of the strings, so strdup() is used for that. It is
* safe to use strdup() here, because the strings are generated at build-time and are
* guaranteed to be NUL-terminated (they come from the devicetree).
*/
path = acpi_device_path(dev);
if (path) {
fresh = strdup(path);
if (fresh)
acpi_dp_add_reference(dsd, prop_name, fresh);
}
}
static void add_device_references(struct acpi_dp *dsd,
const struct typec_connector_class_config *config)
{
/*
* Add references to the USB port objects so that the consumer of this information can
* know whether the port supports USB2, USB3, and/or USB4.
*/
add_device_ref(dsd, "usb2-port", config->usb2_port);
add_device_ref(dsd, "usb3-port", config->usb3_port);
add_device_ref(dsd, "usb4-port", config->usb4_port);
/*
* Add references to the ACPI device(s) which control the orientation, USB data role and
* data muxing.
*/
add_device_ref(dsd, "orientation-switch", config->orientation_switch);
add_device_ref(dsd, "usb-role-switch", config->usb_role_switch);
add_device_ref(dsd, "mode-switch", config->mode_switch);
}
void acpigen_write_typec_connector(const struct typec_connector_class_config *config,
int port_number,
add_custom_dsd_property_cb add_custom_dsd_property)
{
struct acpi_dp *dsd;
char name[5];
/* Create a CONx device */
snprintf(name, sizeof(name), "CON%1X", port_number);
acpigen_write_device(name);
acpigen_write_name_integer("_ADR", port_number);
dsd = acpi_dp_new_table("_DSD");
/* Write out the _DSD table */
acpi_dp_add_integer(dsd, "port-number", port_number);
add_port_caps(dsd, config);
add_device_references(dsd, config);
/* Allow client to add custom properties if desired */
if (add_custom_dsd_property)
add_custom_dsd_property(dsd, port_number);
acpi_dp_write(dsd);
acpigen_pop_len(); /* Device */
}

429
src/acpi/soundwire.c Normal file
View File

@@ -0,0 +1,429 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpigen.h>
#include <acpi/acpi_device.h>
#include <acpi/acpi_soundwire.h>
#include <commonlib/helpers.h>
#include <device/soundwire.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
/* Specification-defined prefix for SoundWire properties. */
#define SDW_PFX "mipi-sdw-"
/* Generate SoundWire property for integer. */
#define SDW_INT(__key, __val) \
acpi_dp_add_integer(dsd, SDW_PFX __key, __val)
/* Generate SoundWire property for integer array. */
#define SDW_INT_ARRAY(__key, __val) \
acpi_dp_add_integer_array(dsd, SDW_PFX __key, __val, __val##_count)
/**
* struct soundwire_name_map - Map ACPI name to SoundWire property name.
* @acpi_name: ACPI compatible name string.
* @sdw_name: MIPI SoundWire property name string.
*/
struct soundwire_name_map {
const char *acpi_name;
const char *sdw_name;
};
static const struct soundwire_name_map bra_mode_names[] = {
{ "BRA0", SDW_PFX "port-bra-mode-0" },
{ "BRA1", SDW_PFX "port-bra-mode-1" },
{ "BRA2", SDW_PFX "port-bra-mode-2" },
{ "BRA3", SDW_PFX "port-bra-mode-3" },
};
static const struct soundwire_name_map audio_mode_names[] = {
{ "MOD0", SDW_PFX "port-audio-mode-0" },
{ "MOD1", SDW_PFX "port-audio-mode-1" },
{ "MOD2", SDW_PFX "port-audio-mode-2" },
{ "MOD3", SDW_PFX "port-audio-mode-3" },
};
static const struct soundwire_name_map dpn_source_names[] = {
{ "DP0", SDW_PFX "dp-0-subproperties" },
{ "SRC1", SDW_PFX "dp-1-source-subproperties" },
{ "SRC2", SDW_PFX "dp-2-source-subproperties" },
{ "SRC3", SDW_PFX "dp-3-source-subproperties" },
{ "SRC4", SDW_PFX "dp-4-source-subproperties" },
{ "SRC5", SDW_PFX "dp-5-source-subproperties" },
{ "SRC6", SDW_PFX "dp-6-source-subproperties" },
{ "SRC7", SDW_PFX "dp-7-source-subproperties" },
{ "SRC8", SDW_PFX "dp-8-source-subproperties" },
{ "SRC9", SDW_PFX "dp-9-source-subproperties" },
{ "SRCA", SDW_PFX "dp-10-source-subproperties" },
{ "SRCB", SDW_PFX "dp-11-source-subproperties" },
{ "SRCC", SDW_PFX "dp-12-source-subproperties" },
{ "SRCD", SDW_PFX "dp-13-source-subproperties" }
};
static const struct soundwire_name_map dpn_sink_names[] = {
{ "DP0", SDW_PFX "dp-0-subproperties" },
{ "SNK1", SDW_PFX "dp-1-sink-subproperties" },
{ "SNK2", SDW_PFX "dp-2-sink-subproperties" },
{ "SNK3", SDW_PFX "dp-3-sink-subproperties" },
{ "SNK4", SDW_PFX "dp-4-sink-subproperties" },
{ "SNK5", SDW_PFX "dp-5-sink-subproperties" },
{ "SNK6", SDW_PFX "dp-6-sink-subproperties" },
{ "SNK7", SDW_PFX "dp-7-sink-subproperties" },
{ "SNK8", SDW_PFX "dp-8-sink-subproperties" },
{ "SNK9", SDW_PFX "dp-9-sink-subproperties" },
{ "SNKA", SDW_PFX "dp-10-sink-subproperties" },
{ "SNKB", SDW_PFX "dp-11-sink-subproperties" },
{ "SNKC", SDW_PFX "dp-12-sink-subproperties" },
{ "SNKD", SDW_PFX "dp-13-sink-subproperties" }
};
static const struct soundwire_name_map link_names[] = {
{ "LNK0", SDW_PFX "link-0-subproperties" },
{ "LNK1", SDW_PFX "link-1-subproperties" },
{ "LNK2", SDW_PFX "link-2-subproperties" },
{ "LNK3", SDW_PFX "link-3-subproperties" },
{ "LNK4", SDW_PFX "link-4-subproperties" },
{ "LNK5", SDW_PFX "link-5-subproperties" },
{ "LNK6", SDW_PFX "link-6-subproperties" },
{ "LNK7", SDW_PFX "link-7-subproperties" }
};
static const char * const multilane_names[] = {
SDW_PFX "lane-1-mapping",
SDW_PFX "lane-2-mapping",
SDW_PFX "lane-3-mapping",
SDW_PFX "lane-4-mapping",
SDW_PFX "lane-5-mapping",
SDW_PFX "lane-6-mapping",
SDW_PFX "lane-7-mapping",
SDW_PFX "lane-8-mapping"
};
static const char * const multilane_master_lane_names[] = {
SDW_PFX "master-lane-1",
SDW_PFX "master-lane-2",
SDW_PFX "master-lane-3",
SDW_PFX "master-lane-4",
SDW_PFX "master-lane-5",
SDW_PFX "master-lane-6",
SDW_PFX "master-lane-7",
SDW_PFX "master-lane-8"
};
static const char * const multilane_slave_link_names[] = {
SDW_PFX "slave-link-A",
SDW_PFX "slave-link-B",
SDW_PFX "slave-link-C",
SDW_PFX "slave-link-D",
SDW_PFX "slave-link-E",
SDW_PFX "slave-link-F",
SDW_PFX "slave-link-G",
SDW_PFX "slave-link-I"
};
static const char * const multilane_bus_holder_names[] = {
SDW_PFX "lane-1-bus-holder",
SDW_PFX "lane-2-bus-holder",
SDW_PFX "lane-3-bus-holder",
SDW_PFX "lane-4-bus-holder",
SDW_PFX "lane-5-bus-holder",
SDW_PFX "lane-6-bus-holder",
SDW_PFX "lane-7-bus-holder",
SDW_PFX "lane-8-bus-holder"
};
static void soundwire_gen_interface_revision(struct acpi_dp *dsd)
{
acpi_dp_add_integer(dsd, SDW_PFX "sw-interface-revision", SOUNDWIRE_SW_VERSION_1_0);
}
static void soundwire_gen_slave(struct acpi_dp *dsd, const struct soundwire_slave *prop)
{
soundwire_gen_interface_revision(dsd);
SDW_INT("wake-up-unavailable", prop->wake_up_unavailable);
SDW_INT("test-mode-supported", prop->test_mode_supported);
SDW_INT("clock-stop-mode1-supported", prop->clock_stop_mode1_supported);
/* Clock Stop Prepare Timeout only used without simplified Clock Stop Prepare. */
SDW_INT("simplified-clockstopprepare-sm-supported",
prop->simplified_clockstopprepare_sm_supported);
if (!prop->simplified_clockstopprepare_sm_supported)
SDW_INT("clockstopprepare-timeout", prop->clockstopprepare_timeout);
SDW_INT("clockstopprepare-hard-reset-behavior",
prop->clockstopprepare_hard_reset_behavior);
SDW_INT("slave-channelprepare-timeout", prop->slave_channelprepare_timeout);
SDW_INT("highPHY-capable", prop->highPHY_capable);
SDW_INT("paging-supported", prop->paging_supported);
SDW_INT("bank-delay-supported", prop->bank_delay_supported);
SDW_INT("port15-read-behavior", prop->port15_read_behavior);
SDW_INT("master-count", prop->master_count);
SDW_INT("source-port-list", prop->source_port_list);
SDW_INT("sink-port-list", prop->sink_port_list);
}
static void soundwire_gen_multilane(struct acpi_dp *dsd, const struct soundwire_multilane *prop)
{
size_t i;
soundwire_gen_interface_revision(dsd);
/* Fill out multilane map based on master/slave links. */
for (i = 0; i < prop->lane_mapping_count && i < SOUNDWIRE_MAX_LANE; i++) {
const struct soundwire_multilane_map *map = &prop->lane_mapping[i];
const char *name;
/* Get the name of this connection */
if (map->direction == MASTER_LANE)
name = multilane_master_lane_names[map->connection.master_lane];
else
name = multilane_slave_link_names[map->connection.slave_link];
acpi_dp_add_string(dsd, multilane_names[map->lane], name);
}
/* Add bus holder properties. */
for (i = 0; i < prop->lane_bus_holder_count; i++)
acpi_dp_add_integer(dsd, multilane_bus_holder_names[i],
prop->lane_bus_holder[i]);
}
static void soundwire_gen_link(struct acpi_dp *dsd, const struct soundwire_link *prop)
{
SDW_INT("clock-stop-mode0-supported", prop->clock_stop_mode0_supported);
SDW_INT("clock-stop-mode1-supported", prop->clock_stop_mode1_supported);
if (prop->clock_frequencies_supported_count > 0 &&
prop->clock_frequencies_supported_count < SOUNDWIRE_MAX) {
SDW_INT_ARRAY("clock-frequencies-supported",
prop->clock_frequencies_supported);
}
SDW_INT("default-frame-rate", prop->default_frame_rate);
SDW_INT("default-frame-row-size", prop->default_frame_row_size);
SDW_INT("default-frame-col-size", prop->default_frame_col_size);
SDW_INT("dynamic-frame-shape", prop->dynamic_frame_shape);
SDW_INT("command-error-threshold", prop->command_error_threshold);
}
static void soundwire_gen_bra_mode(struct acpi_dp *dsd, const struct soundwire_bra_mode *prop)
{
/* Bus frequency configs used if min/max not supported. */
if (prop->bus_frequency_configs_count > 0 &&
prop->bus_frequency_configs_count < SOUNDWIRE_MAX) {
SDW_INT_ARRAY("bra-mode-bus-frequency-configs", prop->bus_frequency_configs);
} else {
SDW_INT("bra-mode-min-bus-frequency", prop->min_bus_frequency);
SDW_INT("bra-mode-max-bus-frequency", prop->max_bus_frequency);
}
SDW_INT("bra-mode-max-data-per-frame", prop->max_data_per_frame);
SDW_INT("bra-mode-min-us-between-transactions", prop->min_us_between_transactions);
}
static void soundwire_gen_audio_mode(struct acpi_dp *dsd,
const struct soundwire_audio_mode *prop)
{
/* Bus frequency configs used if min/max not supported. */
if (prop->bus_frequency_configs_count > 0 &&
prop->bus_frequency_configs_count < SOUNDWIRE_MAX) {
SDW_INT_ARRAY("audio-mode-bus-frequency-configs", prop->bus_frequency_configs);
} else {
SDW_INT("audio-mode-min-bus-frequency", prop->min_bus_frequency);
SDW_INT("audio-mode-max-bus-frequency", prop->max_bus_frequency);
}
/* Sampling frequency configs used if min/max not supported. */
if (prop->sampling_frequency_configs_count > 0 &&
prop->sampling_frequency_configs_count < SOUNDWIRE_MAX) {
SDW_INT_ARRAY("audio-mode-sampling-frequency-configs",
prop->sampling_frequency_configs);
} else {
SDW_INT("audio-mode-max-sampling-frequency", prop->max_sampling_frequency);
SDW_INT("audio-mode-min-sampling-frequency", prop->min_sampling_frequency);
}
SDW_INT("audio-mode-prepare-channel-behavior", prop->prepare_channel_behavior);
SDW_INT("audio-mode-glitchless-transitions", prop->glitchless_transitions);
}
static void soundwire_gen_dp0(struct acpi_dp *dsd, const struct soundwire_dp0 *prop)
{
size_t i;
/* Max wordlength configs used if min/max not supported. */
if (prop->port_wordlength_configs_count > 0 &&
prop->port_wordlength_configs_count < SOUNDWIRE_MAX) {
SDW_INT_ARRAY("port-wordlength-configs", prop->port_wordlength_configs);
} else {
SDW_INT("port-max-wordlength", prop->port_max_wordlength);
SDW_INT("port-min-wordlength", prop->port_min_wordlength);
}
SDW_INT("bra-flow-controlled", prop->bra_flow_controlled);
SDW_INT("bra-imp-def-response-supported", prop->bra_imp_def_response_supported);
SDW_INT("bra-role-supported", prop->bra_role_supported);
SDW_INT("simplified-channel-prepare-sm", prop->simplified_channel_prepare_sm);
SDW_INT("imp-def-dp0-interrupts-supported", prop->imp_def_dp0_interrupts_supported);
SDW_INT("imp-def-bpt-supported", prop->imp_def_bpt_supported);
/* Add bulk register access mode property pointers. */
for (i = 0; i < prop->bra_mode_count && i < SOUNDWIRE_MAX_MODE; i++) {
struct acpi_dp *bra = acpi_dp_new_table(bra_mode_names[i].acpi_name);
acpi_dp_add_child(dsd, bra_mode_names[i].sdw_name, bra);
}
}
static void soundwire_gen_dpn(struct acpi_dp *dsd, const struct soundwire_dpn *prop)
{
size_t i;
SDW_INT("data-port-type", prop->data_port_type);
SDW_INT("max-grouping-supported", prop->max_grouping_supported);
SDW_INT("imp-def-dpn-interrupts-supported", prop->imp_def_dpn_interrupts_supported);
SDW_INT("modes-supported", prop->modes_supported);
SDW_INT("max-async-buffer", prop->max_async_buffer);
SDW_INT("block-packing-mode", prop->block_packing_mode);
SDW_INT("port-encoding-type", prop->port_encoding_type);
/* Max wordlength configs used if min/max not supported. */
if (prop->port_wordlength_configs_count > 0 &&
prop->port_wordlength_configs_count < SOUNDWIRE_MAX) {
SDW_INT_ARRAY("port-wordlength-configs", prop->port_wordlength_configs);
} else {
SDW_INT("port-max-wordlength", prop->port_max_wordlength);
SDW_INT("port-min-wordlength", prop->port_min_wordlength);
}
/* Channel Prepare Timeout only used without simplified Channel Prepare. */
SDW_INT("simplified-channelprepare-sm", prop->simplified_channelprepare_sm);
if (!prop->simplified_channelprepare_sm)
SDW_INT("port-channelprepare-timeout", prop->port_channelprepare_timeout);
/* Channel number list used if min/max not supported. */
if (prop->channel_number_list_count > 0 &&
prop->channel_number_list_count < SOUNDWIRE_MAX) {
SDW_INT_ARRAY("channel-number-list", prop->channel_number_list);
} else {
SDW_INT("min-channel-number", prop->min_channel_number);
SDW_INT("max-channel-number", prop->max_channel_number);
}
if (prop->channel_combination_list_count > 0 &&
prop->channel_combination_list_count < SOUNDWIRE_MAX) {
SDW_INT_ARRAY("channel-combination-list", prop->channel_combination_list);
}
/* Add reference to Audio Mode properties. */
for (i = 0; i < prop->port_audio_mode_count && i < SOUNDWIRE_MAX_MODE; i++) {
struct acpi_dp *am = acpi_dp_new_table(audio_mode_names[i].acpi_name);
acpi_dp_add_child(dsd, audio_mode_names[i].sdw_name, am);
}
}
void soundwire_gen_controller(struct acpi_dp *dsd, const struct soundwire_controller *prop,
soundwire_link_prop_cb link_prop_cb)
{
size_t i;
soundwire_gen_interface_revision(dsd);
SDW_INT("master-count", prop->master_list_count);
/* Generate properties for each master link on the controller. */
for (i = 0; i < prop->master_list_count && i < SOUNDWIRE_MAX_LINK; i++) {
struct acpi_dp *link = acpi_dp_new_table(link_names[i].acpi_name);
soundwire_gen_link(link, &prop->master_list[i]);
/* Callback for custom link properties from the controller. */
if (link_prop_cb)
link_prop_cb(link, i, prop);
acpi_dp_add_child(dsd, link_names[i].sdw_name, link);
}
}
void soundwire_gen_codec(struct acpi_dp *dsd, const struct soundwire_codec *codec,
soundwire_dp_prop_cb dp_prop_cb)
{
const struct soundwire_dpn_entry *entry;
const struct soundwire_name_map *name;
size_t i;
/* Generate slave properties for this codec. */
soundwire_gen_slave(dsd, codec->slave);
/* Generate properties for multilane config, if provided. */
if (codec->multilane)
soundwire_gen_multilane(dsd, codec->multilane);
/* Generate properties for data port 0, if provided. */
if (codec->dp0) {
struct acpi_dp *dp0;
/* First generate any Bulk Register Access mode properties. */
for (i = 0; i < SOUNDWIRE_MAX_MODE; i++) {
const struct soundwire_bra_mode *prop = codec->dp0_bra_mode[i];
struct acpi_dp *bra;
/* Stop processing at the first undefined BRA mode. */
if (!prop)
break;
name = &bra_mode_names[i];
bra = acpi_dp_new_table(name->acpi_name);
soundwire_gen_bra_mode(bra, prop);
acpi_dp_add_child(dsd, name->sdw_name, bra);
}
name = &dpn_source_names[0];
dp0 = acpi_dp_new_table(name->acpi_name);
soundwire_gen_dp0(dp0, codec->dp0);
/* Callback for custom properties from the codec. */
if (dp_prop_cb)
dp_prop_cb(dp0, 0, codec);
acpi_dp_add_child(dsd, name->sdw_name, dp0);
}
/*
* First generate audio modes for the data ports. This results in unnecessary
* (but harmless) references to the audio modes at the codec level, but it allows
* the data ports to use these objects without duplication.
*/
for (i = 0; i < SOUNDWIRE_MAX_MODE; i++) {
const struct soundwire_audio_mode *prop = codec->audio_mode[i];
struct acpi_dp *am;
/* Stop processing at the first undefined audio mode. */
if (!prop)
break;
name = &audio_mode_names[i];
am = acpi_dp_new_table(name->acpi_name);
soundwire_gen_audio_mode(am, prop);
acpi_dp_add_child(dsd, name->sdw_name, am);
}
/* Now generate properties for source/slave on each defined data port. */
for (entry = codec->dpn; entry; entry++) {
struct acpi_dp *dpn;
/* Stop processing at the first invalid data port. */
if (entry->port < SOUNDWIRE_MIN_DPN || entry->port > SOUNDWIRE_MAX_DPN)
break;
if (entry->source) {
name = &dpn_source_names[entry->port];
dpn = acpi_dp_new_table(name->acpi_name);
soundwire_gen_dpn(dpn, entry->source);
/* Callback for custom properties from the codec. */
if (dp_prop_cb)
dp_prop_cb(dpn, entry->port, codec);
acpi_dp_add_child(dsd, name->sdw_name, dpn);
}
if (entry->sink) {
name = &dpn_sink_names[entry->port];
dpn = acpi_dp_new_table(name->acpi_name);
soundwire_gen_dpn(dpn, entry->sink);
/* Callback for custom properties from the codec. */
if (dp_prop_cb)
dp_prop_cb(dpn, entry->port, codec);
acpi_dp_add_child(dsd, name->sdw_name, dpn);
}
}
}

View File

@@ -1,7 +1,4 @@
################################################################################
##
## SPDX-License-Identifier: GPL-2.0-only
##
###############################################################################
# ARM specific options

View File

@@ -1,8 +1,4 @@
################################################################################
##
## SPDX-License-Identifier: GPL-2.0-only
##
###############################################################################
armv4_flags = -marm -march=armv4t -I$(src)/arch/arm/include/armv4/ \
-D__COREBOOT_ARM_ARCH__=4

View File

@@ -5,7 +5,6 @@
* Reference: ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition
*/
#include <stdint.h>
#include <arch/cache.h>

View File

@@ -1,8 +1,4 @@
################################################################################
##
## SPDX-License-Identifier: GPL-2.0-only
##
###############################################################################
armv7_flags = -mthumb -I$(src)/arch/arm/include/armv7/ -D__COREBOOT_ARM_ARCH__=7
armv7-a_flags = -march=armv7-a $(armv7_flags) -D__COREBOOT_ARM_V7_A__

View File

@@ -3,7 +3,6 @@
* cache.c: Cache maintenance routines for ARMv7-M
*/
#include <stdint.h>
#include <arch/cache.h>

View File

@@ -1,8 +1,4 @@
################################################################################
##
## SPDX-License-Identifier: GPL-2.0-only
##
################################################################################
libgcc_files = ashldi3.S lib1funcs.S lshrdi3.S muldi3.S ucmpdi2.S uldivmod.S
libgcc_files += udivmoddi4.c umoddi3.c

View File

@@ -1,8 +1,4 @@
################################################################################
##
## SPDX-License-Identifier: GPL-2.0-only
##
################################################################################
################################################################################
# Take care of subdirectories

View File

@@ -1,8 +1,4 @@
################################################################################
##
## SPDX-License-Identifier: GPL-2.0-only
##
################################################################################
ifeq ($(CONFIG_ARCH_ARMV8_EXTENSION),0)
march = armv8-a

View File

@@ -1,8 +1,4 @@
################################################################################
##
## SPDX-License-Identifier: GPL-2.0-only
##
################################################################################
ppc64_flags = -I$(src)/arch/ppc64/ -mbig-endian -mcpu=power8 -mtune=power8

View File

@@ -1,8 +1,4 @@
################################################################################
##
## SPDX-License-Identifier: GPL-2.0-only
##
################################################################################
################################################################################
## RISC-V specific options

View File

@@ -5,7 +5,6 @@
#include <console/console.h>
#include <bootmem.h>
#include <program_loading.h>
#include <lib.h>
#include <fit.h>
#include <endian.h>

View File

@@ -5,7 +5,6 @@
#include <arch/cpu.h>
#include <arch/encoding.h>
#include <stdint.h>
#include <vm.h>
/* Delegate controls which traps are delegated to the payload. If you

View File

@@ -1,6 +1,4 @@
##
## SPDX-License-Identifier: GPL-2.0-only
##
config ARCH_X86
bool

View File

@@ -1,6 +1,4 @@
##
## SPDX-License-Identifier: GPL-2.0-only
##
ifeq ($(CONFIG_POSTCAR_STAGE),y)
$(eval $(call init_standard_toolchain,postcar))

View File

@@ -10,11 +10,12 @@
#include <cpu/x86/cr.h>
.section .text
/*
* Include the old code for reset vector and protected mode entry. That code has
* withstood the test of time.
*/
#include <arch/x86/prologue.inc>
#include <cpu/x86/16bit/entry16.inc>
#include <cpu/x86/16bit/reset16.inc>
#include <cpu/x86/32bit/entry32.inc>

View File

@@ -4,7 +4,6 @@
#include <fallback.h>
#include <program_loading.h>
#include <stddef.h>
#include <string.h>
static const char *get_fallback(const char *stagelist)
{

View File

@@ -62,13 +62,11 @@
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_bss = .;
#if ENV_STAGE_HAS_BSS_SECTION
/* Allow global uninitialized variables for stages without CAR teardown. */
*(.bss)
*(.bss.*)
*(.sbss)
*(.sbss.*)
#endif
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
_ebss = .;
_car_unallocated_start = .;

View File

@@ -23,7 +23,7 @@ _FMAP_SIZE = 0;
* The stack area is not shared between stages, but is defined here for
* convenience.
*/
. = CONFIG_X86_RESET_VECTOR - ARCH_STACK_ALIGN_SIZE - _STACK_SIZE - _CONSOLE_SIZE - _TIMESTAMPS_SIZE - _FMAP_SIZE;
. = CONFIG_X86_RESET_VECTOR - ARCH_STACK_ALIGN_SIZE - _STACK_SIZE - _CONSOLE_SIZE - _TIMESTAMPS_SIZE - _FMAP_SIZE - VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE;
_ = ASSERT(. > _eprogram, "Not enough room for .earlyram.data. Try increasing C_ENV_BOOTBLOCK_SIZE, or decreasing either EARLYRAM_BSP_STACK_SIZE or PRERAM_CBMEM_CONSOLE_SIZE.");
@@ -37,6 +37,11 @@ _ = ASSERT(. > _eprogram, "Not enough room for .earlyram.data. Try increasing C_
#if !CONFIG(NO_FMAP_CACHE)
FMAP_CACHE(., FMAP_SIZE)
#endif
#if CONFIG(VBOOT_STARTS_IN_BOOTBLOCK)
ALIGN_COUNTER(16);
VBOOT2_WORK(., VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE)
#endif
}
_ = ASSERT(. <= CONFIG_X86_RESET_VECTOR, "Earlyram data regions don't fit below the reset vector!");

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
SECTIONS {
. = (0xffffffff - CONFIG_ID_SECTION_OFFSET) - (__id_end - __id_start) + 1;
. = (CONFIG_X86_RESET_VECTOR - CONFIG_ID_SECTION_OFFSET) + 0x10 - (__id_end - __id_start);
.id (.): {
KEEP(*(.id))
}

View File

@@ -5,50 +5,42 @@
#include <stdint.h>
static __always_inline uint8_t read8(
const volatile void *addr)
static __always_inline uint8_t read8(const volatile void *addr)
{
return *((volatile uint8_t *)(addr));
}
static __always_inline uint16_t read16(
const volatile void *addr)
static __always_inline uint16_t read16(const volatile void *addr)
{
return *((volatile uint16_t *)(addr));
}
static __always_inline uint32_t read32(
const volatile void *addr)
static __always_inline uint32_t read32(const volatile void *addr)
{
return *((volatile uint32_t *)(addr));
}
static __always_inline uint64_t read64(
const volatile void *addr)
static __always_inline uint64_t read64(const volatile void *addr)
{
return *((volatile uint64_t *)(addr));
}
static __always_inline void write8(volatile void *addr,
uint8_t value)
static __always_inline void write8(volatile void *addr, uint8_t value)
{
*((volatile uint8_t *)(addr)) = value;
}
static __always_inline void write16(volatile void *addr,
uint16_t value)
static __always_inline void write16(volatile void *addr, uint16_t value)
{
*((volatile uint16_t *)(addr)) = value;
}
static __always_inline void write32(volatile void *addr,
uint32_t value)
static __always_inline void write32(volatile void *addr, uint32_t value)
{
*((volatile uint32_t *)(addr)) = value;
}
static __always_inline void write64(volatile void *addr,
uint64_t value)
static __always_inline void write64(volatile void *addr, uint64_t value)
{
*((volatile uint64_t *)(addr)) = value;
}

View File

@@ -1,6 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <cpu/x86/post_code.h>
.section ".rom.data", "a", @progbits
.section ".rom.text", "ax", @progbits

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
config COMMONLIB_STORAGE

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
ifeq ($(CONFIG_COMMONLIB_STORAGE),y)

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config CPU_AMD_AGESA

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
subdirs-$(CONFIG_CPU_AMD_AGESA_FAMILY14) += family14

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config CPU_AMD_AGESA_FAMILY14

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
romstage-y += fixme.c

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config CPU_AMD_AGESA_FAMILY15_TN

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
romstage-y += fixme.c

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config CPU_AMD_AGESA_FAMILY16_KB

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
romstage-y += fixme.c

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config CPU_AMD_PI_00630F01

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
romstage-y += fixme.c

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config CPU_AMD_PI_00660F01

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
romstage-y += fixme.c

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config CPU_AMD_PI_00730F01

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
romstage-y += fixme.c

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config CPU_AMD_PI

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
subdirs-$(CONFIG_CPU_AMD_PI_00630F01) += 00630F01

View File

@@ -12,7 +12,7 @@ void set_feature_ctrl_lock(void);
/*
* Init CPPC block with MSRs for Intel Enhanced Speed Step Technology.
* Version 2 is suggested--this function's implementation of version 3
* may have room for improvment.
* may have room for improvement.
*/
struct cppc_config;
void cpu_init_cppc_config(struct cppc_config *config, u32 version);

View File

@@ -8,7 +8,7 @@ fit_pointer:
.long 0
.previous
.section ".rom.data", "a", @progbits
.section .text
.align 16
.global fit_table
.global fit_table_end

View File

@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stdint.h>
#include <arch/cpu.h>
#include <cpu/x86/msr.h>
#include "haswell.h"

View File

@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stdint.h>
#include <console/console.h>
#include <cf9_reset.h>
#include <cpu/x86/bist.h>

View File

@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stdint.h>
#include <arch/cpu.h>
#include <cpu/x86/msr.h>
#include <cpu/intel/speedstep.h>

View File

@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stdint.h>
#include <arch/cpu.h>
#include <cpu/x86/msr.h>
#include <cpu/intel/speedstep.h>

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
config CPU_INTEL_SLOT_1

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
config CPU_QEMU_POWER8

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
config CPU_QEMU_X86

View File

@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <stddef.h>
#include <cbmem.h>
#include <symbols.h>

View File

@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <stddef.h>
#include <stdint.h>
#include <symbols.h>

View File

@@ -26,6 +26,7 @@
*/
#include <arch/rom_segs.h>
#include <cpu/x86/post_code.h>
/* Symbol _start16bit must be aligned to 4kB to start AP CPUs with
* Startup IPI message without RAM.

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
bootblock-y += name.c

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
ramstage-y += smm_module_loader.c

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
menu "Devices"
@@ -585,6 +583,22 @@ config PCIEXP_HOTPLUG_PREFETCH_MEM
child devices. This size should be page-aligned. The default is
256 MiB.
config PCIEXP_HOTPLUG_PREFETCH_MEM_ABOVE_4G
bool
depends on RESOURCE_ALLOCATOR_V4
default y if !PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G
default n
help
This enables prefetch memory allocation above 4G boundary for the
hotplug resources.
config PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G
bool "PCI Express Hotplug Prefetch Memory Allocation below 4G boundary"
default n
help
This enables prefetch memory allocation below 4G boundary for the
hotplug resources.
config PCIEXP_HOTPLUG_IO
hex "PCI Express Hotplug I/O Space"
default 0x2000
@@ -779,4 +793,22 @@ config SOFTWARE_I2C
I2C controller is not (yet) available. The platform code needs to
provide bindings to manually toggle I2C lines.
config RESOURCE_ALLOCATOR_V3
bool
default n
help
This config option enables resource allocator v3 which performs
top down allocation of resources in a single MMIO window. This is the
old resource allocator meant to be used only until the broken AMD
chipsets are fixed. DO NOT USE THIS FOR ANY NEW CHIPSETS!
config RESOURCE_ALLOCATOR_V4
bool
default n if RESOURCE_ALLOCATOR_V3
default y if !RESOURCE_ALLOCATOR_V3
help
This config option enables resource allocator v4 which uses multiple
ranges for allocating resources. This allows allocation of resources
above 4G boundary as well.
endmenu

View File

@@ -58,3 +58,7 @@ bootblock-y += mmio.c
verstage-y += mmio.c
romstage-y += mmio.c
ramstage-y += mmio.c
ramstage-y += resource_allocator_common.c
ramstage-$(CONFIG_RESOURCE_ALLOCATOR_V3) += resource_allocator_v3.c
ramstage-$(CONFIG_RESOURCE_ALLOCATOR_V4) += resource_allocator_v4.c

View File

@@ -145,33 +145,6 @@ struct device *alloc_find_dev(struct bus *parent, struct device_path *path)
return child;
}
/**
* Round a number up to an alignment.
*
* @param val The starting value.
* @param pow Alignment as a power of two.
* @return Rounded up number.
*/
static resource_t round(resource_t val, unsigned long pow)
{
resource_t mask;
mask = (1ULL << pow) - 1ULL;
val += mask;
val &= ~mask;
return val;
}
static const char *resource2str(struct resource *res)
{
if (res->flags & IORESOURCE_IO)
return "io";
if (res->flags & IORESOURCE_PREFETCH)
return "prefmem";
if (res->flags & IORESOURCE_MEM)
return "mem";
return "undefined";
}
/**
* Read the resources on all devices of a given bus.
*
@@ -209,523 +182,6 @@ static void read_resources(struct bus *bus)
dev_path(bus->dev), bus->secondary, bus->link_num);
}
struct pick_largest_state {
struct resource *last;
const struct device *result_dev;
struct resource *result;
int seen_last;
};
static void pick_largest_resource(void *gp, struct device *dev,
struct resource *resource)
{
struct pick_largest_state *state = gp;
struct resource *last;
last = state->last;
/* Be certain to pick the successor to last. */
if (resource == last) {
state->seen_last = 1;
return;
}
if (resource->flags & IORESOURCE_FIXED)
return; /* Skip it. */
if (last && ((last->align < resource->align) ||
((last->align == resource->align) &&
(last->size < resource->size)) ||
((last->align == resource->align) &&
(last->size == resource->size) && (!state->seen_last)))) {
return;
}
if (!state->result ||
(state->result->align < resource->align) ||
((state->result->align == resource->align) &&
(state->result->size < resource->size))) {
state->result_dev = dev;
state->result = resource;
}
}
static const struct device *largest_resource(struct bus *bus,
struct resource **result_res,
unsigned long type_mask,
unsigned long type)
{
struct pick_largest_state state;
state.last = *result_res;
state.result_dev = NULL;
state.result = NULL;
state.seen_last = 0;
search_bus_resources(bus, type_mask, type, pick_largest_resource,
&state);
*result_res = state.result;
return state.result_dev;
}
/**
* This function is the guts of the resource allocator.
*
* The problem.
* - Allocate resource locations for every device.
* - Don't overlap, and follow the rules of bridges.
* - Don't overlap with resources in fixed locations.
* - Be efficient so we don't have ugly strategies.
*
* The strategy.
* - Devices that have fixed addresses are the minority so don't
* worry about them too much. Instead only use part of the address
* space for devices with programmable addresses. This easily handles
* everything except bridges.
*
* - PCI devices are required to have their sizes and their alignments
* equal. In this case an optimal solution to the packing problem
* exists. Allocate all devices from highest alignment to least
* alignment or vice versa. Use this.
*
* - So we can handle more than PCI run two allocation passes on bridges. The
* first to see how large the resources are behind the bridge, and what
* their alignment requirements are. The second to assign a safe address to
* the devices behind the bridge. This allows us to treat a bridge as just
* a device with a couple of resources, and not need to special case it in
* the allocator. Also this allows handling of other types of bridges.
*
* @param bus The bus we are traversing.
* @param bridge The bridge resource which must contain the bus' resources.
* @param type_mask This value gets ANDed with the resource type.
* @param type This value must match the result of the AND.
* @return TODO
*/
static void compute_resources(struct bus *bus, struct resource *bridge,
unsigned long type_mask, unsigned long type)
{
const struct device *dev;
struct resource *resource;
resource_t base;
base = round(bridge->base, bridge->align);
if (!bus)
return;
printk(BIOS_SPEW, "%s %s: base: %llx size: %llx align: %d gran: %d"
" limit: %llx\n", dev_path(bus->dev), resource2str(bridge),
base, bridge->size, bridge->align,
bridge->gran, bridge->limit);
/* For each child which is a bridge, compute the resource needs. */
for (dev = bus->children; dev; dev = dev->sibling) {
struct resource *child_bridge;
if (!dev->link_list)
continue;
/* Find the resources with matching type flags. */
for (child_bridge = dev->resource_list; child_bridge;
child_bridge = child_bridge->next) {
struct bus* link;
if (!(child_bridge->flags & IORESOURCE_BRIDGE)
|| (child_bridge->flags & type_mask) != type)
continue;
/*
* Split prefetchable memory if combined. Many domains
* use the same address space for prefetchable memory
* and non-prefetchable memory. Bridges below them need
* it separated. Add the PREFETCH flag to the type_mask
* and type.
*/
link = dev->link_list;
while (link && link->link_num !=
IOINDEX_LINK(child_bridge->index))
link = link->next;
if (link == NULL) {
printk(BIOS_ERR, "link %ld not found on %s\n",
IOINDEX_LINK(child_bridge->index),
dev_path(dev));
}
compute_resources(link, child_bridge,
type_mask | IORESOURCE_PREFETCH,
type | (child_bridge->flags &
IORESOURCE_PREFETCH));
}
}
/* Remember we haven't found anything yet. */
resource = NULL;
/*
* Walk through all the resources on the current bus and compute the
* amount of address space taken by them. Take granularity and
* alignment into account.
*/
while ((dev = largest_resource(bus, &resource, type_mask, type))) {
/* Size 0 resources can be skipped. */
if (!resource->size)
continue;
/* Propagate the resource alignment to the bridge resource. */
if (resource->align > bridge->align)
bridge->align = resource->align;
/* Propagate the resource limit to the bridge register. */
if (bridge->limit > resource->limit)
bridge->limit = resource->limit;
/* Warn if it looks like APICs aren't declared. */
if ((resource->limit == 0xffffffff) &&
(resource->flags & IORESOURCE_ASSIGNED)) {
printk(BIOS_ERR,
"Resource limit looks wrong! (no APIC?)\n");
printk(BIOS_ERR, "%s %02lx limit %08llx\n",
dev_path(dev), resource->index, resource->limit);
}
if (resource->flags & IORESOURCE_IO) {
/*
* Don't allow potential aliases over the legacy PCI
* expansion card addresses. The legacy PCI decodes
* only 10 bits, uses 0x100 - 0x3ff. Therefore, only
* 0x00 - 0xff can be used out of each 0x400 block of
* I/O space.
*/
if ((base & 0x300) != 0) {
base = (base & ~0x3ff) + 0x400;
}
/*
* Don't allow allocations in the VGA I/O range.
* PCI has special cases for that.
*/
else if ((base >= 0x3b0) && (base <= 0x3df)) {
base = 0x3e0;
}
}
/* Base must be aligned. */
base = round(base, resource->align);
resource->base = base;
base += resource->size;
printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n",
dev_path(dev), resource->index, resource->base,
resource->base + resource->size - 1,
resource2str(resource));
}
/*
* A PCI bridge resource does not need to be a power of two size, but
* it does have a minimum granularity. Round the size up to that
* minimum granularity so we know not to place something else at an
* address positively decoded by the bridge.
*/
bridge->size = round(base, bridge->gran) -
round(bridge->base, bridge->align);
printk(BIOS_SPEW, "%s %s: base: %llx size: %llx align: %d gran: %d"
" limit: %llx done\n", dev_path(bus->dev),
resource2str(bridge),
base, bridge->size, bridge->align, bridge->gran, bridge->limit);
}
/**
* This function is the second part of the resource allocator.
*
* See the compute_resources function for a more detailed explanation.
*
* This function assigns the resources a value.
*
* @param bus The bus we are traversing.
* @param bridge The bridge resource which must contain the bus' resources.
* @param type_mask This value gets ANDed with the resource type.
* @param type This value must match the result of the AND.
*
* @see compute_resources
*/
static void allocate_resources(struct bus *bus, struct resource *bridge,
unsigned long type_mask, unsigned long type)
{
const struct device *dev;
struct resource *resource;
resource_t base;
base = bridge->base;
if (!bus)
return;
printk(BIOS_SPEW, "%s %s: base:%llx size:%llx align:%d gran:%d "
"limit:%llx\n", dev_path(bus->dev),
resource2str(bridge),
base, bridge->size, bridge->align, bridge->gran, bridge->limit);
/* Remember we haven't found anything yet. */
resource = NULL;
/*
* Walk through all the resources on the current bus and allocate them
* address space.
*/
while ((dev = largest_resource(bus, &resource, type_mask, type))) {
/* Propagate the bridge limit to the resource register. */
if (resource->limit > bridge->limit)
resource->limit = bridge->limit;
/* Size 0 resources can be skipped. */
if (!resource->size) {
/* Set the base to limit so it doesn't confuse tolm. */
resource->base = resource->limit;
resource->flags |= IORESOURCE_ASSIGNED;
continue;
}
if (resource->flags & IORESOURCE_IO) {
/*
* Don't allow potential aliases over the legacy PCI
* expansion card addresses. The legacy PCI decodes
* only 10 bits, uses 0x100 - 0x3ff. Therefore, only
* 0x00 - 0xff can be used out of each 0x400 block of
* I/O space.
*/
if ((base & 0x300) != 0) {
base = (base & ~0x3ff) + 0x400;
}
/*
* Don't allow allocations in the VGA I/O range.
* PCI has special cases for that.
*/
else if ((base >= 0x3b0) && (base <= 0x3df)) {
base = 0x3e0;
}
}
if ((round(base, resource->align) + resource->size - 1) <=
resource->limit) {
/* Base must be aligned. */
base = round(base, resource->align);
resource->base = base;
resource->limit = resource->base + resource->size - 1;
resource->flags |= IORESOURCE_ASSIGNED;
resource->flags &= ~IORESOURCE_STORED;
base += resource->size;
} else {
printk(BIOS_ERR, "!! Resource didn't fit !!\n");
printk(BIOS_ERR, " aligned base %llx size %llx "
"limit %llx\n", round(base, resource->align),
resource->size, resource->limit);
printk(BIOS_ERR, " %llx needs to be <= %llx "
"(limit)\n", (round(base, resource->align) +
resource->size) - 1, resource->limit);
printk(BIOS_ERR, " %s%s %02lx * [0x%llx - 0x%llx]"
" %s\n", (resource->flags & IORESOURCE_ASSIGNED)
? "Assigned: " : "", dev_path(dev),
resource->index, resource->base,
resource->base + resource->size - 1,
resource2str(resource));
}
printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n",
dev_path(dev), resource->index, resource->base,
resource->size ? resource->base + resource->size - 1 :
resource->base, resource2str(resource));
}
/*
* A PCI bridge resource does not need to be a power of two size, but
* it does have a minimum granularity. Round the size up to that
* minimum granularity so we know not to place something else at an
* address positively decoded by the bridge.
*/
bridge->flags |= IORESOURCE_ASSIGNED;
printk(BIOS_SPEW, "%s %s: next_base: %llx size: %llx align: %d "
"gran: %d done\n", dev_path(bus->dev),
resource2str(bridge), base, bridge->size, bridge->align,
bridge->gran);
/* For each child which is a bridge, allocate_resources. */
for (dev = bus->children; dev; dev = dev->sibling) {
struct resource *child_bridge;
if (!dev->link_list)
continue;
/* Find the resources with matching type flags. */
for (child_bridge = dev->resource_list; child_bridge;
child_bridge = child_bridge->next) {
struct bus* link;
if (!(child_bridge->flags & IORESOURCE_BRIDGE) ||
(child_bridge->flags & type_mask) != type)
continue;
/*
* Split prefetchable memory if combined. Many domains
* use the same address space for prefetchable memory
* and non-prefetchable memory. Bridges below them need
* it separated. Add the PREFETCH flag to the type_mask
* and type.
*/
link = dev->link_list;
while (link && link->link_num !=
IOINDEX_LINK(child_bridge->index))
link = link->next;
if (link == NULL)
printk(BIOS_ERR, "link %ld not found on %s\n",
IOINDEX_LINK(child_bridge->index),
dev_path(dev));
allocate_resources(link, child_bridge,
type_mask | IORESOURCE_PREFETCH,
type | (child_bridge->flags &
IORESOURCE_PREFETCH));
}
}
}
static int resource_is(struct resource *res, u32 type)
{
return (res->flags & IORESOURCE_TYPE_MASK) == type;
}
struct constraints {
struct resource io, mem;
};
static struct resource *resource_limit(struct constraints *limits,
struct resource *res)
{
struct resource *lim = NULL;
/* MEM, or I/O - skip any others. */
if (resource_is(res, IORESOURCE_MEM))
lim = &limits->mem;
else if (resource_is(res, IORESOURCE_IO))
lim = &limits->io;
return lim;
}
static void constrain_resources(const struct device *dev,
struct constraints* limits)
{
const struct device *child;
struct resource *res;
struct resource *lim;
struct bus *link;
/* Constrain limits based on the fixed resources of this device. */
for (res = dev->resource_list; res; res = res->next) {
if (!(res->flags & IORESOURCE_FIXED))
continue;
if (!res->size) {
/* It makes no sense to have 0-sized, fixed resources.*/
printk(BIOS_ERR, "skipping %s@%lx fixed resource, "
"size=0!\n", dev_path(dev), res->index);
continue;
}
lim = resource_limit(limits, res);
if (!lim)
continue;
/*
* Is it a fixed resource outside the current known region?
* If so, we don't have to consider it - it will be handled
* correctly and doesn't affect current region's limits.
*/
if (((res->base + res->size -1) < lim->base)
|| (res->base > lim->limit))
continue;
printk(BIOS_SPEW, "%s: %s %02lx base %08llx limit %08llx %s (fixed)\n",
__func__, dev_path(dev), res->index, res->base,
res->base + res->size - 1, resource2str(res));
/*
* Choose to be above or below fixed resources. This check is
* signed so that "negative" amounts of space are handled
* correctly.
*/
if ((signed long long)(lim->limit - (res->base + res->size -1))
> (signed long long)(res->base - lim->base))
lim->base = res->base + res->size;
else
lim->limit = res->base -1;
}
/* Descend into every enabled child and look for fixed resources. */
for (link = dev->link_list; link; link = link->next) {
for (child = link->children; child; child = child->sibling) {
if (child->enabled)
constrain_resources(child, limits);
}
}
}
static void avoid_fixed_resources(const struct device *dev)
{
struct constraints limits;
struct resource *res;
struct resource *lim;
printk(BIOS_SPEW, "%s: %s\n", __func__, dev_path(dev));
/* Initialize constraints to maximum size. */
limits.io.base = 0;
limits.io.limit = 0xffffffffffffffffULL;
limits.mem.base = 0;
limits.mem.limit = 0xffffffffffffffffULL;
/* Constrain the limits to dev's initial resources. */
for (res = dev->resource_list; res; res = res->next) {
if ((res->flags & IORESOURCE_FIXED))
continue;
printk(BIOS_SPEW, "%s:@%s %02lx limit %08llx\n", __func__,
dev_path(dev), res->index, res->limit);
lim = resource_limit(&limits, res);
if (!lim)
continue;
if (res->base > lim->base)
lim->base = res->base;
if (res->limit < lim->limit)
lim->limit = res->limit;
}
/* Look through the tree for fixed resources and update the limits. */
constrain_resources(dev, &limits);
/* Update dev's resources with new limits. */
for (res = dev->resource_list; res; res = res->next) {
if ((res->flags & IORESOURCE_FIXED))
continue;
lim = resource_limit(&limits, res);
if (!lim)
continue;
/* Is the resource outside the limits? */
if (lim->base > res->base)
res->base = lim->base;
if (res->limit > lim->limit)
res->limit = lim->limit;
/* MEM resources need to start at the highest address manageable. */
if (res->flags & IORESOURCE_MEM)
res->base = resource_max(res);
printk(BIOS_SPEW, "%s:@%s %02lx base %08llx limit %08llx\n",
__func__, dev_path(dev), res->index, res->base, res->limit);
}
}
struct device *vga_pri = NULL;
static void set_vga_bridge_bits(void)
{
@@ -996,9 +452,7 @@ void dev_enumerate(void)
*/
void dev_configure(void)
{
struct resource *res;
const struct device *root;
const struct device *child;
set_vga_bridge_bits();
@@ -1020,53 +474,8 @@ void dev_configure(void)
print_resource_tree(root, BIOS_SPEW, "After reading.");
/* Compute resources for all domains. */
for (child = root->link_list->children; child; child = child->sibling) {
if (!(child->path.type == DEVICE_PATH_DOMAIN))
continue;
post_log_path(child);
for (res = child->resource_list; res; res = res->next) {
if (res->flags & IORESOURCE_FIXED)
continue;
if (res->flags & IORESOURCE_MEM) {
compute_resources(child->link_list,
res, IORESOURCE_TYPE_MASK, IORESOURCE_MEM);
continue;
}
if (res->flags & IORESOURCE_IO) {
compute_resources(child->link_list,
res, IORESOURCE_TYPE_MASK, IORESOURCE_IO);
continue;
}
}
}
allocate_resources(root);
/* For all domains. */
for (child = root->link_list->children; child; child=child->sibling)
if (child->path.type == DEVICE_PATH_DOMAIN)
avoid_fixed_resources(child);
/* Store the computed resource allocations into device registers ... */
printk(BIOS_INFO, "Setting resources...\n");
for (child = root->link_list->children; child; child = child->sibling) {
if (!(child->path.type == DEVICE_PATH_DOMAIN))
continue;
post_log_path(child);
for (res = child->resource_list; res; res = res->next) {
if (res->flags & IORESOURCE_FIXED)
continue;
if (res->flags & IORESOURCE_MEM) {
allocate_resources(child->link_list,
res, IORESOURCE_TYPE_MASK, IORESOURCE_MEM);
continue;
}
if (res->flags & IORESOURCE_IO) {
allocate_resources(child->link_list,
res, IORESOURCE_TYPE_MASK, IORESOURCE_IO);
continue;
}
}
}
assign_resources(root->link_list);
printk(BIOS_INFO, "Done setting resources.\n");
print_resource_tree(root, BIOS_SPEW, "After assigning values.");

View File

@@ -156,6 +156,12 @@ static int path_eq(const struct device_path *path1,
case DEVICE_PATH_MMIO:
equal = (path1->mmio.addr == path2->mmio.addr);
break;
case DEVICE_PATH_ESPI:
equal = (path1->espi.addr == path2->espi.addr);
break;
case DEVICE_PATH_LPC:
equal = (path1->lpc.addr == path2->lpc.addr);
break;
default:
printk(BIOS_ERR, "Unknown device type: %d\n", path1->type);
break;
@@ -221,7 +227,7 @@ DEVTREE_CONST struct device *pcidev_path_on_bus(unsigned int bus, pci_devfn_t de
DEVTREE_CONST struct bus *pci_root_bus(void)
{
DEVTREE_CONST struct device *pci_domain;
MAYBE_STATIC_BSS DEVTREE_CONST struct bus *pci_root = NULL;
static DEVTREE_CONST struct bus *pci_root;
if (pci_root)
return pci_root;

View File

@@ -215,6 +215,14 @@ const char *dev_path(const struct device *dev)
snprintf(buffer, sizeof(buffer), "MMIO: %08lx",
dev->path.mmio.addr);
break;
case DEVICE_PATH_ESPI:
snprintf(buffer, sizeof(buffer), "ESPI: %08lx",
dev->path.espi.addr);
break;
case DEVICE_PATH_LPC:
snprintf(buffer, sizeof(buffer), "LPC: %08lx",
dev->path.lpc.addr);
break;
default:
printk(BIOS_ERR, "Unknown device path type: %d\n",
dev->path.type);
@@ -875,6 +883,13 @@ void tolm_test(void *gp, struct device *dev, struct resource *new)
best = *best_p;
/*
* If resource is not allocated any space i.e. size is zero,
* then do not consider this resource in tolm calculations.
*/
if (new->size == 0)
return;
if (!best || (best->base > new->base))
best = new;
@@ -885,9 +900,9 @@ u32 find_pci_tolm(struct bus *bus)
{
struct resource *min = NULL;
u32 tolm;
unsigned long mask_match = IORESOURCE_MEM | IORESOURCE_ASSIGNED;
search_bus_resources(bus, IORESOURCE_MEM, IORESOURCE_MEM,
tolm_test, &min);
search_bus_resources(bus, mask_match, mask_match, tolm_test, &min);
tolm = 0xffffffffUL;

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
subdirs-$(CONFIG_PCI_OPTION_ROM_RUN_YABEL) += x86emu

View File

@@ -1,5 +1,3 @@
##
##
## SPDX-License-Identifier: GPL-2.0-only
ramstage-$(CONFIG_PCI_OPTION_ROM_RUN_REALMODE) += x86.c

View File

@@ -9,6 +9,7 @@
#include <device/pci_ops.h>
#include <bootmode.h>
#include <console/console.h>
#include <cpu/cpu.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
@@ -433,22 +434,93 @@ void pci_domain_read_resources(struct device *dev)
/* Initialize the system-wide memory resources constraints. */
res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
res->limit = 0xffffffffULL;
res->limit = (1ULL << cpu_phys_address_size()) - 1;
res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE |
IORESOURCE_ASSIGNED;
}
static void pci_set_resource(struct device *dev, struct resource *resource)
void pci_domain_set_resources(struct device *dev)
{
assign_resources(dev->link_list);
}
static void pci_store_resource(const struct device *const dev,
const struct resource *const resource)
{
unsigned long base_lo, base_hi;
base_lo = resource->base & 0xffffffff;
base_hi = (resource->base >> 32) & 0xffffffff;
/*
* Some chipsets allow us to set/clear the I/O bit
* (e.g. VIA 82C686A). So set it to be safe.
*/
if (resource->flags & IORESOURCE_IO)
base_lo |= PCI_BASE_ADDRESS_SPACE_IO;
pci_write_config32(dev, resource->index, base_lo);
if (resource->flags & IORESOURCE_PCI64)
pci_write_config32(dev, resource->index + 4, base_hi);
}
static void pci_store_bridge_resource(const struct device *const dev,
struct resource *const resource)
{
resource_t base, end;
/*
* PCI bridges have no enable bit. They are disabled if the base of
* the range is greater than the limit. If the size is zero, disable
* by setting the base = limit and end = limit - 2^gran.
*/
if (resource->size == 0) {
base = resource->limit;
end = resource->limit - (1 << resource->gran);
resource->base = base;
} else {
base = resource->base;
end = resource_end(resource);
}
if (resource->index == PCI_IO_BASE) {
/* Set the I/O ranges. */
pci_write_config8(dev, PCI_IO_BASE, base >> 8);
pci_write_config16(dev, PCI_IO_BASE_UPPER16, base >> 16);
pci_write_config8(dev, PCI_IO_LIMIT, end >> 8);
pci_write_config16(dev, PCI_IO_LIMIT_UPPER16, end >> 16);
} else if (resource->index == PCI_MEMORY_BASE) {
/* Set the memory range. */
pci_write_config16(dev, PCI_MEMORY_BASE, base >> 16);
pci_write_config16(dev, PCI_MEMORY_LIMIT, end >> 16);
} else if (resource->index == PCI_PREF_MEMORY_BASE) {
/* Set the prefetchable memory range. */
pci_write_config16(dev, PCI_PREF_MEMORY_BASE, base >> 16);
pci_write_config32(dev, PCI_PREF_BASE_UPPER32, base >> 32);
pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT, end >> 16);
pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32, end >> 32);
} else {
/* Don't let me think I stored the resource. */
resource->flags &= ~IORESOURCE_STORED;
printk(BIOS_ERR, "ERROR: invalid resource->index %lx\n", resource->index);
}
}
static void pci_set_resource(struct device *dev, struct resource *resource)
{
/* Make certain the resource has actually been assigned a value. */
if (!(resource->flags & IORESOURCE_ASSIGNED)) {
if (resource->flags & IORESOURCE_BRIDGE) {
/* If a bridge resource has no value assigned,
we can treat it like an empty resource. */
resource->size = 0;
} else {
printk(BIOS_ERR, "ERROR: %s %02lx %s size: 0x%010llx not "
"assigned\n", dev_path(dev), resource->index,
resource_type(resource), resource->size);
return;
}
}
/* If this resource is fixed don't worry about it. */
if (resource->flags & IORESOURCE_FIXED)
@@ -476,62 +548,13 @@ static void pci_set_resource(struct device *dev, struct resource *resource)
dev->command |= PCI_COMMAND_MASTER;
}
/* Get the base address. */
base = resource->base;
/* Get the end. */
end = resource_end(resource);
/* Now store the resource. */
resource->flags |= IORESOURCE_STORED;
/*
* PCI bridges have no enable bit. They are disabled if the base of
* the range is greater than the limit. If the size is zero, disable
* by setting the base = limit and end = limit - 2^gran.
*/
if (resource->size == 0 && (resource->flags & IORESOURCE_PCI_BRIDGE)) {
base = resource->limit;
end = resource->limit - (1 << resource->gran);
resource->base = base;
}
if (!(resource->flags & IORESOURCE_PCI_BRIDGE)) {
unsigned long base_lo, base_hi;
/*
* Some chipsets allow us to set/clear the I/O bit
* (e.g. VIA 82C686A). So set it to be safe.
*/
base_lo = base & 0xffffffff;
base_hi = (base >> 32) & 0xffffffff;
if (resource->flags & IORESOURCE_IO)
base_lo |= PCI_BASE_ADDRESS_SPACE_IO;
pci_write_config32(dev, resource->index, base_lo);
if (resource->flags & IORESOURCE_PCI64)
pci_write_config32(dev, resource->index + 4, base_hi);
} else if (resource->index == PCI_IO_BASE) {
/* Set the I/O ranges. */
pci_write_config8(dev, PCI_IO_BASE, base >> 8);
pci_write_config16(dev, PCI_IO_BASE_UPPER16, base >> 16);
pci_write_config8(dev, PCI_IO_LIMIT, end >> 8);
pci_write_config16(dev, PCI_IO_LIMIT_UPPER16, end >> 16);
} else if (resource->index == PCI_MEMORY_BASE) {
/* Set the memory range. */
pci_write_config16(dev, PCI_MEMORY_BASE, base >> 16);
pci_write_config16(dev, PCI_MEMORY_LIMIT, end >> 16);
} else if (resource->index == PCI_PREF_MEMORY_BASE) {
/* Set the prefetchable memory range. */
pci_write_config16(dev, PCI_PREF_MEMORY_BASE, base >> 16);
pci_write_config32(dev, PCI_PREF_BASE_UPPER32, base >> 32);
pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT, end >> 16);
pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32, end >> 32);
} else {
/* Don't let me think I stored the resource. */
resource->flags &= ~IORESOURCE_STORED;
printk(BIOS_ERR, "ERROR: invalid resource->index %lx\n",
resource->index);
}
if (resource->flags & IORESOURCE_PCI_BRIDGE)
pci_store_bridge_resource(dev, resource);
else
pci_store_resource(dev, resource);
report_resource_stored(dev, resource, "");
}
@@ -764,17 +787,19 @@ struct device_operations default_pci_ops_dev = {
};
/** Default device operations for PCI bridges */
static struct pci_operations pci_bus_ops_pci = {
.set_subsystem = 0,
};
struct device_operations default_pci_ops_bus = {
.read_resources = pci_bus_read_resources,
.set_resources = pci_dev_set_resources,
.enable_resources = pci_bus_enable_resources,
.scan_bus = pci_scan_bridge,
.reset_bus = pci_bus_reset,
.ops_pci = &pci_bus_ops_pci,
};
/** Default device operations for PCI devices marked 'hidden' */
static struct device_operations default_hidden_pci_ops_dev = {
.read_resources = noop_read_resources,
.set_resources = noop_set_resources,
.scan_bus = scan_static_bus,
};
/**
@@ -1145,6 +1170,46 @@ unsigned int pci_match_simple_dev(struct device *dev, pci_devfn_t sdev)
dev->path.pci.devfn == PCI_DEV2DEVFN(sdev);
}
/**
* PCI devices that are marked as "hidden" do not get probed. However, the same
* initialization logic is still performed as if it were. This is useful when
* devices would like to be described in the devicetree.cb file, and/or present
* static PCI resources to the allocator, but the platform firmware hides the
* device (makes the device invisible to PCI enumeration) before PCI enumeration
* takes place.
*
* The expected semantics of PCI devices marked as 'hidden':
* 1) The device is actually present under the specified BDF
* 2) The device config space can still be accessed somehow, but the Vendor ID
* indicates there is no device there (it reads as 0xffffffff).
* 3) The device may still consume PCI resources. Typically, these would have
* been hardcoded elsewhere.
*
* @param dev Pointer to the device structure.
*/
static void pci_scan_hidden_device(struct device *dev)
{
if (dev->chip_ops && dev->chip_ops->enable_dev)
dev->chip_ops->enable_dev(dev);
/*
* If chip_ops->enable_dev did not set dev->ops, then set to a default
* .ops, because PCI enumeration is effectively being skipped, therefore
* no PCI driver will bind to this device. However, children may want to
* be enumerated, so this provides scan_static_bus for the .scan_bus
* callback.
*/
if (dev->ops == NULL)
dev->ops = &default_hidden_pci_ops_dev;
if (dev->ops->enable)
dev->ops->enable(dev);
/* Display the device almost as if it were probed normally */
printk(BIOS_DEBUG, "%s [0000/%04x] hidden%s\n", dev_path(dev),
dev->device, dev->ops ? "" : " No operations");
}
/**
* Scan a PCI bus.
*
@@ -1189,6 +1254,14 @@ void pci_scan_bus(struct bus *bus, unsigned int min_devfn,
/* First thing setup the device structure. */
dev = pci_scan_get_dev(bus, devfn);
/* Devices marked 'hidden' do not get probed */
if (dev && dev->hidden) {
pci_scan_hidden_device(dev);
/* Skip pci_probe_dev, go to next devfn */
continue;
}
/* See if a device is present and setup the device structure. */
dev = pci_probe_dev(dev, bus, devfn);
@@ -1212,8 +1285,12 @@ void pci_scan_bus(struct bus *bus, unsigned int min_devfn,
prev = &bus->children;
for (dev = bus->children; dev; dev = dev->sibling) {
/* If we read valid vendor id, it is not leftover device. */
if (dev->vendor != 0) {
/*
* The device is only considered leftover if it is not hidden
* and it has a Vendor ID of 0 (the default for a device that
* could not be probed).
*/
if (dev->vendor != 0 || dev->hidden) {
prev = &dev->sibling;
continue;
}

View File

@@ -512,7 +512,7 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev)
{
struct resource *resource;
// Add extra memory space
/* Add extra memory space */
resource = new_resource(dev, 0x10);
resource->size = CONFIG_PCIEXP_HOTPLUG_MEM;
resource->align = 12;
@@ -520,7 +520,7 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev)
resource->limit = 0xffffffff;
resource->flags |= IORESOURCE_MEM;
// Add extra prefetchable memory space
/* Add extra prefetchable memory space */
resource = new_resource(dev, 0x14);
resource->size = CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM;
resource->align = 12;
@@ -528,7 +528,11 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev)
resource->limit = 0xffffffffffffffff;
resource->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
// Add extra I/O space
/* Set resource flag requesting allocation above 4G boundary. */
if (CONFIG(PCIEXP_HOTPLUG_PREFETCH_MEM_ABOVE_4G))
resource->flags |= IORESOURCE_ABOVE_4G;
/* Add extra I/O space */
resource = new_resource(dev, 0x18);
resource->size = CONFIG_PCIEXP_HOTPLUG_IO;
resource->align = 12;

View File

@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <device/device.h>
struct pick_largest_state {
struct resource *last;
const struct device *result_dev;
struct resource *result;
int seen_last;
};
static void pick_largest_resource(void *gp, struct device *dev,
struct resource *resource)
{
struct pick_largest_state *state = gp;
struct resource *last;
last = state->last;
/* Be certain to pick the successor to last. */
if (resource == last) {
state->seen_last = 1;
return;
}
if (resource->flags & IORESOURCE_FIXED)
return; /* Skip it. */
if (last && ((last->align < resource->align) ||
((last->align == resource->align) &&
(last->size < resource->size)) ||
((last->align == resource->align) &&
(last->size == resource->size) && (!state->seen_last)))) {
return;
}
if (!state->result ||
(state->result->align < resource->align) ||
((state->result->align == resource->align) &&
(state->result->size < resource->size))) {
state->result_dev = dev;
state->result = resource;
}
}
const struct device *largest_resource(struct bus *bus,
struct resource **result_res,
unsigned long type_mask,
unsigned long type)
{
struct pick_largest_state state;
state.last = *result_res;
state.result_dev = NULL;
state.result = NULL;
state.seen_last = 0;
search_bus_resources(bus, type_mask, type, pick_largest_resource,
&state);
*result_res = state.result;
return state.result_dev;
}

View File

@@ -0,0 +1,542 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <device/device.h>
#include <post.h>
/**
* Round a number up to an alignment.
*
* @param val The starting value.
* @param pow Alignment as a power of two.
* @return Rounded up number.
*/
static resource_t round(resource_t val, unsigned long pow)
{
resource_t mask;
mask = (1ULL << pow) - 1ULL;
val += mask;
val &= ~mask;
return val;
}
static const char *resource2str(struct resource *res)
{
if (res->flags & IORESOURCE_IO)
return "io";
if (res->flags & IORESOURCE_PREFETCH)
return "prefmem";
if (res->flags & IORESOURCE_MEM)
return "mem";
return "undefined";
}
/**
* This function is the guts of the resource allocator.
*
* The problem.
* - Allocate resource locations for every device.
* - Don't overlap, and follow the rules of bridges.
* - Don't overlap with resources in fixed locations.
* - Be efficient so we don't have ugly strategies.
*
* The strategy.
* - Devices that have fixed addresses are the minority so don't
* worry about them too much. Instead only use part of the address
* space for devices with programmable addresses. This easily handles
* everything except bridges.
*
* - PCI devices are required to have their sizes and their alignments
* equal. In this case an optimal solution to the packing problem
* exists. Allocate all devices from highest alignment to least
* alignment or vice versa. Use this.
*
* - So we can handle more than PCI run two allocation passes on bridges. The
* first to see how large the resources are behind the bridge, and what
* their alignment requirements are. The second to assign a safe address to
* the devices behind the bridge. This allows us to treat a bridge as just
* a device with a couple of resources, and not need to special case it in
* the allocator. Also this allows handling of other types of bridges.
*
* @param bus The bus we are traversing.
* @param bridge The bridge resource which must contain the bus' resources.
* @param type_mask This value gets ANDed with the resource type.
* @param type This value must match the result of the AND.
* @return TODO
*/
static void compute_resources(struct bus *bus, struct resource *bridge,
unsigned long type_mask, unsigned long type)
{
const struct device *dev;
struct resource *resource;
resource_t base;
base = round(bridge->base, bridge->align);
if (!bus)
return;
printk(BIOS_SPEW, "%s %s: base: %llx size: %llx align: %d gran: %d"
" limit: %llx\n", dev_path(bus->dev), resource2str(bridge),
base, bridge->size, bridge->align,
bridge->gran, bridge->limit);
/* For each child which is a bridge, compute the resource needs. */
for (dev = bus->children; dev; dev = dev->sibling) {
struct resource *child_bridge;
if (!dev->link_list)
continue;
/* Find the resources with matching type flags. */
for (child_bridge = dev->resource_list; child_bridge;
child_bridge = child_bridge->next) {
struct bus* link;
if (!(child_bridge->flags & IORESOURCE_BRIDGE)
|| (child_bridge->flags & type_mask) != type)
continue;
/*
* Split prefetchable memory if combined. Many domains
* use the same address space for prefetchable memory
* and non-prefetchable memory. Bridges below them need
* it separated. Add the PREFETCH flag to the type_mask
* and type.
*/
link = dev->link_list;
while (link && link->link_num !=
IOINDEX_LINK(child_bridge->index))
link = link->next;
if (link == NULL) {
printk(BIOS_ERR, "link %ld not found on %s\n",
IOINDEX_LINK(child_bridge->index),
dev_path(dev));
}
compute_resources(link, child_bridge,
type_mask | IORESOURCE_PREFETCH,
type | (child_bridge->flags &
IORESOURCE_PREFETCH));
}
}
/* Remember we haven't found anything yet. */
resource = NULL;
/*
* Walk through all the resources on the current bus and compute the
* amount of address space taken by them. Take granularity and
* alignment into account.
*/
while ((dev = largest_resource(bus, &resource, type_mask, type))) {
/* Size 0 resources can be skipped. */
if (!resource->size)
continue;
/* Propagate the resource alignment to the bridge resource. */
if (resource->align > bridge->align)
bridge->align = resource->align;
/* Propagate the resource limit to the bridge register. */
if (bridge->limit > resource->limit)
bridge->limit = resource->limit;
/* Warn if it looks like APICs aren't declared. */
if ((resource->limit == 0xffffffff) &&
(resource->flags & IORESOURCE_ASSIGNED)) {
printk(BIOS_ERR,
"Resource limit looks wrong! (no APIC?)\n");
printk(BIOS_ERR, "%s %02lx limit %08llx\n",
dev_path(dev), resource->index, resource->limit);
}
if (resource->flags & IORESOURCE_IO) {
/*
* Don't allow potential aliases over the legacy PCI
* expansion card addresses. The legacy PCI decodes
* only 10 bits, uses 0x100 - 0x3ff. Therefore, only
* 0x00 - 0xff can be used out of each 0x400 block of
* I/O space.
*/
if ((base & 0x300) != 0) {
base = (base & ~0x3ff) + 0x400;
}
/*
* Don't allow allocations in the VGA I/O range.
* PCI has special cases for that.
*/
else if ((base >= 0x3b0) && (base <= 0x3df)) {
base = 0x3e0;
}
}
/* Base must be aligned. */
base = round(base, resource->align);
resource->base = base;
base += resource->size;
printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n",
dev_path(dev), resource->index, resource->base,
resource->base + resource->size - 1,
resource2str(resource));
}
/*
* A PCI bridge resource does not need to be a power of two size, but
* it does have a minimum granularity. Round the size up to that
* minimum granularity so we know not to place something else at an
* address positively decoded by the bridge.
*/
bridge->size = round(base, bridge->gran) -
round(bridge->base, bridge->align);
printk(BIOS_SPEW, "%s %s: base: %llx size: %llx align: %d gran: %d"
" limit: %llx done\n", dev_path(bus->dev),
resource2str(bridge),
base, bridge->size, bridge->align, bridge->gran, bridge->limit);
}
/**
* This function is the second part of the resource allocator.
*
* See the compute_resources function for a more detailed explanation.
*
* This function assigns the resources a value.
*
* @param bus The bus we are traversing.
* @param bridge The bridge resource which must contain the bus' resources.
* @param type_mask This value gets ANDed with the resource type.
* @param type This value must match the result of the AND.
*
* @see compute_resources
*/
static void __allocate_resources(struct bus *bus, struct resource *bridge,
unsigned long type_mask, unsigned long type)
{
const struct device *dev;
struct resource *resource;
resource_t base;
base = bridge->base;
if (!bus)
return;
printk(BIOS_SPEW, "%s %s: base:%llx size:%llx align:%d gran:%d "
"limit:%llx\n", dev_path(bus->dev),
resource2str(bridge),
base, bridge->size, bridge->align, bridge->gran, bridge->limit);
/* Remember we haven't found anything yet. */
resource = NULL;
/*
* Walk through all the resources on the current bus and allocate them
* address space.
*/
while ((dev = largest_resource(bus, &resource, type_mask, type))) {
/* Propagate the bridge limit to the resource register. */
if (resource->limit > bridge->limit)
resource->limit = bridge->limit;
/* Size 0 resources can be skipped. */
if (!resource->size)
continue;
if (resource->flags & IORESOURCE_IO) {
/*
* Don't allow potential aliases over the legacy PCI
* expansion card addresses. The legacy PCI decodes
* only 10 bits, uses 0x100 - 0x3ff. Therefore, only
* 0x00 - 0xff can be used out of each 0x400 block of
* I/O space.
*/
if ((base & 0x300) != 0) {
base = (base & ~0x3ff) + 0x400;
}
/*
* Don't allow allocations in the VGA I/O range.
* PCI has special cases for that.
*/
else if ((base >= 0x3b0) && (base <= 0x3df)) {
base = 0x3e0;
}
}
if ((round(base, resource->align) + resource->size - 1) <=
resource->limit) {
/* Base must be aligned. */
base = round(base, resource->align);
resource->base = base;
resource->limit = resource->base + resource->size - 1;
resource->flags |= IORESOURCE_ASSIGNED;
resource->flags &= ~IORESOURCE_STORED;
base += resource->size;
} else {
printk(BIOS_ERR, "!! Resource didn't fit !!\n");
printk(BIOS_ERR, " aligned base %llx size %llx "
"limit %llx\n", round(base, resource->align),
resource->size, resource->limit);
printk(BIOS_ERR, " %llx needs to be <= %llx "
"(limit)\n", (round(base, resource->align) +
resource->size) - 1, resource->limit);
printk(BIOS_ERR, " %s%s %02lx * [0x%llx - 0x%llx]"
" %s\n", (resource->flags & IORESOURCE_ASSIGNED)
? "Assigned: " : "", dev_path(dev),
resource->index, resource->base,
resource->base + resource->size - 1,
resource2str(resource));
}
printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n",
dev_path(dev), resource->index, resource->base,
resource->size ? resource->base + resource->size - 1 :
resource->base, resource2str(resource));
}
/*
* A PCI bridge resource does not need to be a power of two size, but
* it does have a minimum granularity. Round the size up to that
* minimum granularity so we know not to place something else at an
* address positively decoded by the bridge.
*/
bridge->flags |= IORESOURCE_ASSIGNED;
printk(BIOS_SPEW, "%s %s: next_base: %llx size: %llx align: %d "
"gran: %d done\n", dev_path(bus->dev),
resource2str(bridge), base, bridge->size, bridge->align,
bridge->gran);
/* For each child which is a bridge, __allocate_resources. */
for (dev = bus->children; dev; dev = dev->sibling) {
struct resource *child_bridge;
if (!dev->link_list)
continue;
/* Find the resources with matching type flags. */
for (child_bridge = dev->resource_list; child_bridge;
child_bridge = child_bridge->next) {
struct bus* link;
if (!(child_bridge->flags & IORESOURCE_BRIDGE) ||
(child_bridge->flags & type_mask) != type)
continue;
/*
* Split prefetchable memory if combined. Many domains
* use the same address space for prefetchable memory
* and non-prefetchable memory. Bridges below them need
* it separated. Add the PREFETCH flag to the type_mask
* and type.
*/
link = dev->link_list;
while (link && link->link_num !=
IOINDEX_LINK(child_bridge->index))
link = link->next;
if (link == NULL)
printk(BIOS_ERR, "link %ld not found on %s\n",
IOINDEX_LINK(child_bridge->index),
dev_path(dev));
__allocate_resources(link, child_bridge,
type_mask | IORESOURCE_PREFETCH,
type | (child_bridge->flags &
IORESOURCE_PREFETCH));
}
}
}
static int resource_is(struct resource *res, u32 type)
{
return (res->flags & IORESOURCE_TYPE_MASK) == type;
}
struct constraints {
struct resource io, mem;
};
static struct resource *resource_limit(struct constraints *limits,
struct resource *res)
{
struct resource *lim = NULL;
/* MEM, or I/O - skip any others. */
if (resource_is(res, IORESOURCE_MEM))
lim = &limits->mem;
else if (resource_is(res, IORESOURCE_IO))
lim = &limits->io;
return lim;
}
static void constrain_resources(const struct device *dev,
struct constraints* limits)
{
const struct device *child;
struct resource *res;
struct resource *lim;
struct bus *link;
/* Constrain limits based on the fixed resources of this device. */
for (res = dev->resource_list; res; res = res->next) {
if (!(res->flags & IORESOURCE_FIXED))
continue;
if (!res->size) {
/* It makes no sense to have 0-sized, fixed resources.*/
printk(BIOS_ERR, "skipping %s@%lx fixed resource, "
"size=0!\n", dev_path(dev), res->index);
continue;
}
lim = resource_limit(limits, res);
if (!lim)
continue;
/*
* Is it a fixed resource outside the current known region?
* If so, we don't have to consider it - it will be handled
* correctly and doesn't affect current region's limits.
*/
if (((res->base + res->size -1) < lim->base)
|| (res->base > lim->limit))
continue;
printk(BIOS_SPEW, "%s: %s %02lx base %08llx limit %08llx %s (fixed)\n",
__func__, dev_path(dev), res->index, res->base,
res->base + res->size - 1, resource2str(res));
/*
* Choose to be above or below fixed resources. This check is
* signed so that "negative" amounts of space are handled
* correctly.
*/
if ((signed long long)(lim->limit - (res->base + res->size -1))
> (signed long long)(res->base - lim->base))
lim->base = res->base + res->size;
else
lim->limit = res->base -1;
}
/* Descend into every enabled child and look for fixed resources. */
for (link = dev->link_list; link; link = link->next) {
for (child = link->children; child; child = child->sibling) {
if (child->enabled)
constrain_resources(child, limits);
}
}
}
static void avoid_fixed_resources(const struct device *dev)
{
struct constraints limits;
struct resource *res;
struct resource *lim;
printk(BIOS_SPEW, "%s: %s\n", __func__, dev_path(dev));
/* Initialize constraints to maximum size. */
limits.io.base = 0;
limits.io.limit = 0xffffffffffffffffULL;
limits.mem.base = 0;
limits.mem.limit = 0xffffffffffffffffULL;
/* Constrain the limits to dev's initial resources. */
for (res = dev->resource_list; res; res = res->next) {
if ((res->flags & IORESOURCE_FIXED))
continue;
printk(BIOS_SPEW, "%s:@%s %02lx limit %08llx\n", __func__,
dev_path(dev), res->index, res->limit);
lim = resource_limit(&limits, res);
if (!lim)
continue;
if (res->base > lim->base)
lim->base = res->base;
if (res->limit < lim->limit)
lim->limit = res->limit;
}
/* Look through the tree for fixed resources and update the limits. */
constrain_resources(dev, &limits);
/* Update dev's resources with new limits. */
for (res = dev->resource_list; res; res = res->next) {
if ((res->flags & IORESOURCE_FIXED))
continue;
lim = resource_limit(&limits, res);
if (!lim)
continue;
/* Is the resource outside the limits? */
if (lim->base > res->base)
res->base = lim->base;
if (res->limit > lim->limit)
res->limit = lim->limit;
/* MEM resources need to start at the highest address manageable. */
if (res->flags & IORESOURCE_MEM)
res->base = resource_max(res);
printk(BIOS_SPEW, "%s:@%s %02lx base %08llx limit %08llx\n",
__func__, dev_path(dev), res->index, res->base, res->limit);
}
}
void allocate_resources(const struct device *root)
{
struct resource *res;
const struct device *child;
/* Compute resources for all domains. */
for (child = root->link_list->children; child; child = child->sibling) {
if (!(child->path.type == DEVICE_PATH_DOMAIN))
continue;
post_log_path(child);
for (res = child->resource_list; res; res = res->next) {
if (res->flags & IORESOURCE_FIXED)
continue;
if (res->flags & IORESOURCE_MEM) {
compute_resources(child->link_list,
res, IORESOURCE_TYPE_MASK, IORESOURCE_MEM);
continue;
}
if (res->flags & IORESOURCE_IO) {
compute_resources(child->link_list,
res, IORESOURCE_TYPE_MASK, IORESOURCE_IO);
continue;
}
}
}
/* For all domains. */
for (child = root->link_list->children; child; child=child->sibling)
if (child->path.type == DEVICE_PATH_DOMAIN)
avoid_fixed_resources(child);
/* Store the computed resource allocations into device registers ... */
printk(BIOS_INFO, "Setting resources...\n");
for (child = root->link_list->children; child; child = child->sibling) {
if (!(child->path.type == DEVICE_PATH_DOMAIN))
continue;
post_log_path(child);
for (res = child->resource_list; res; res = res->next) {
if (res->flags & IORESOURCE_FIXED)
continue;
if (res->flags & IORESOURCE_MEM) {
__allocate_resources(child->link_list,
res, IORESOURCE_TYPE_MASK, IORESOURCE_MEM);
continue;
}
if (res->flags & IORESOURCE_IO) {
__allocate_resources(child->link_list,
res, IORESOURCE_TYPE_MASK, IORESOURCE_IO);
continue;
}
}
}
}

View File

@@ -0,0 +1,685 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <device/device.h>
#include <memrange.h>
#include <post.h>
/**
* Round a number up to an alignment.
*
* @param val The starting value.
* @param pow Alignment as a power of two.
* @return Rounded up number.
*/
static resource_t round(resource_t val, unsigned long pow)
{
return ALIGN_UP(val, POWER_OF_2(pow));
}
static const char *resource2str(const struct resource *res)
{
if (res->flags & IORESOURCE_IO)
return "io";
if (res->flags & IORESOURCE_PREFETCH)
return "prefmem";
if (res->flags & IORESOURCE_MEM)
return "mem";
return "undefined";
}
static bool dev_has_children(const struct device *dev)
{
const struct bus *bus = dev->link_list;
return bus && bus->children;
}
#define res_printk(depth, str, ...) printk(BIOS_DEBUG, "%*c"str, depth, ' ', __VA_ARGS__)
/*
* During pass 1, once all the requirements for downstream devices of a bridge are gathered,
* this function calculates the overall resource requirement for the bridge. It starts by
* picking the largest resource requirement downstream for the given resource type and works by
* adding requirements in descending order.
*
* Additionally, it takes alignment and limits of the downstream devices into consideration and
* ensures that they get propagated to the bridge resource. This is required to guarantee that
* the upstream bridge/domain honors the limit and alignment requirements for this bridge based
* on the tightest constraints downstream.
*/
static void update_bridge_resource(const struct device *bridge, struct resource *bridge_res,
unsigned long type_match, int print_depth)
{
const struct device *child;
struct resource *child_res;
resource_t base;
bool first_child_res = true;
const unsigned long type_mask = IORESOURCE_TYPE_MASK | IORESOURCE_PREFETCH;
struct bus *bus = bridge->link_list;
child_res = NULL;
/*
* `base` keeps track of where the next allocation for child resource can take place
* from within the bridge resource window. Since the bridge resource window allocation
* is not performed yet, it can start at 0. Base gets updated every time a resource
* requirement is accounted for in the loop below. After scanning all these resources,
* base will indicate the total size requirement for the current bridge resource
* window.
*/
base = 0;
res_printk(print_depth, "%s %s: size: %llx align: %d gran: %d limit: %llx\n",
dev_path(bridge), resource2str(bridge_res), bridge_res->size,
bridge_res->align, bridge_res->gran, bridge_res->limit);
while ((child = largest_resource(bus, &child_res, type_mask, type_match))) {
/* Size 0 resources can be skipped. */
if (!child_res->size)
continue;
/*
* Propagate the resource alignment to the bridge resource if this is the first
* child resource with non-zero size being considered. For all other children
* resources, alignment is taken care of by updating the base to round up as per
* the child resource alignment. It is guaranteed that pass 2 follows the exact
* same method of picking the resource for allocation using
* largest_resource(). Thus, as long as the alignment for first child resource
* is propagated up to the bridge resource, it can be guaranteed that the
* alignment for all resources is appropriately met.
*/
if (first_child_res && (child_res->align > bridge_res->align))
bridge_res->align = child_res->align;
first_child_res = false;
/*
* Propagate the resource limit to the bridge resource only if child resource
* limit is non-zero. If a downstream device has stricter requirements
* w.r.t. limits for any resource, that constraint needs to be propagated back
* up to the downstream bridges of the domain. This guarantees that the resource
* allocation which starts at the domain level takes into account all these
* constraints thus working on a global view.
*/
if (child_res->limit && (child_res->limit < bridge_res->limit))
bridge_res->limit = child_res->limit;
/*
* Propagate the downstream resource request to allocate above 4G boundary to
* upstream bridge resource. This ensures that during pass 2, the resource
* allocator at domain level has a global view of all the downstream device
* requirements and thus address space is allocated as per updated flags in the
* bridge resource.
*
* Since the bridge resource is a single window, all the downstream resources of
* this bridge resource will be allocated space above 4G boundary.
*/
if (child_res->flags & IORESOURCE_ABOVE_4G)
bridge_res->flags |= IORESOURCE_ABOVE_4G;
/*
* Alignment value of 0 means that the child resource has no alignment
* requirements and so the base value remains unchanged here.
*/
base = round(base, child_res->align);
res_printk(print_depth + 1, "%s %02lx * [0x%llx - 0x%llx] %s\n",
dev_path(child), child_res->index, base, base + child_res->size - 1,
resource2str(child_res));
base += child_res->size;
}
/*
* After all downstream device resources are scanned, `base` represents the total size
* requirement for the current bridge resource window. This size needs to be rounded up
* to the granularity requirement of the bridge to ensure that the upstream
* bridge/domain allocates big enough window.
*/
bridge_res->size = round(base, bridge_res->gran);
res_printk(print_depth, "%s %s: size: %llx align: %d gran: %d limit: %llx done\n",
dev_path(bridge), resource2str(bridge_res), bridge_res->size,
bridge_res->align, bridge_res->gran, bridge_res->limit);
}
/*
* During pass 1, resource allocator at bridge level gathers requirements from downstream
* devices and updates its own resource windows for the provided resource type.
*/
static void compute_bridge_resources(const struct device *bridge, unsigned long type_match,
int print_depth)
{
const struct device *child;
struct resource *res;
struct bus *bus = bridge->link_list;
const unsigned long type_mask = IORESOURCE_TYPE_MASK | IORESOURCE_PREFETCH;
for (res = bridge->resource_list; res; res = res->next) {
if (!(res->flags & IORESOURCE_BRIDGE))
continue;
if ((res->flags & type_mask) != type_match)
continue;
/*
* Ensure that the resource requirements for all downstream bridges are
* gathered before updating the window for current bridge resource.
*/
for (child = bus->children; child; child = child->sibling) {
if (!dev_has_children(child))
continue;
compute_bridge_resources(child, type_match, print_depth + 1);
}
/*
* Update the window for current bridge resource now that all downstream
* requirements are gathered.
*/
update_bridge_resource(bridge, res, type_match, print_depth);
}
}
/*
* During pass 1, resource allocator walks down the entire sub-tree of a domain. It gathers
* resource requirements for every downstream bridge by looking at the resource requests of its
* children. Thus, the requirement gathering begins at the leaf devices and is propagated back
* up to the downstream bridges of the domain.
*
* At domain level, it identifies every downstream bridge and walks down that bridge to gather
* requirements for each resource type i.e. i/o, mem and prefmem. Since bridges have separate
* windows for mem and prefmem, requirements for each need to be collected separately.
*
* Domain resource windows are fixed ranges and hence requirement gathering does not result in
* any changes to these fixed ranges.
*/
static void compute_domain_resources(const struct device *domain)
{
const struct device *child;
const int print_depth = 1;
if (domain->link_list == NULL)
return;
for (child = domain->link_list->children; child; child = child->sibling) {
/* Skip if this is not a bridge or has no children under it. */
if (!dev_has_children(child))
continue;
compute_bridge_resources(child, IORESOURCE_IO, print_depth);
compute_bridge_resources(child, IORESOURCE_MEM, print_depth);
compute_bridge_resources(child, IORESOURCE_MEM | IORESOURCE_PREFETCH,
print_depth);
}
}
static unsigned char get_alignment_by_resource_type(const struct resource *res)
{
if (res->flags & IORESOURCE_MEM)
return 12; /* Page-aligned --> log2(4KiB) */
else if (res->flags & IORESOURCE_IO)
return 0; /* No special alignment required --> log2(1) */
die("Unexpected resource type: flags(%d)!\n", res->flags);
}
/*
* If the resource is NULL or if the resource is not assigned, then it cannot be used for
* allocation for downstream devices.
*/
static bool is_resource_invalid(const struct resource *res)
{
return (res == NULL) || !(res->flags & IORESOURCE_ASSIGNED);
}
static void initialize_domain_io_resource_memranges(struct memranges *ranges,
const struct resource *res,
unsigned long memrange_type)
{
memranges_insert(ranges, res->base, res->limit - res->base + 1, memrange_type);
}
static void initialize_domain_mem_resource_memranges(struct memranges *ranges,
const struct resource *res,
unsigned long memrange_type)
{
resource_t res_base;
resource_t res_limit;
const resource_t limit_4g = 0xffffffff;
res_base = res->base;
res_limit = res->limit;
/*
* Split the resource into two separate ranges if it crosses the 4G boundary. Memrange
* type is set differently to ensure that memrange does not merge these two ranges. For
* the range above 4G boundary, given memrange type is ORed with IORESOURCE_ABOVE_4G.
*/
if (res_base <= limit_4g) {
resource_t range_limit;
/* Clip the resource limit at 4G boundary if necessary. */
range_limit = MIN(res_limit, limit_4g);
memranges_insert(ranges, res_base, range_limit - res_base + 1, memrange_type);
/*
* If the resource lies completely below the 4G boundary, nothing more needs to
* be done.
*/
if (res_limit <= limit_4g)
return;
/*
* If the resource window crosses the 4G boundary, then update res_base to add
* another entry for the range above the boundary.
*/
res_base = limit_4g + 1;
}
if (res_base > res_limit)
return;
/*
* If resource lies completely above the 4G boundary or if the resource was clipped to
* add two separate ranges, the range above 4G boundary has the resource flag
* IORESOURCE_ABOVE_4G set. This allows domain to handle any downstream requests for
* resource allocation above 4G differently.
*/
memranges_insert(ranges, res_base, res_limit - res_base + 1,
memrange_type | IORESOURCE_ABOVE_4G);
}
/*
* This function initializes memranges for domain device. If the resource crosses 4G boundary,
* then this function splits it into two ranges -- one for the window below 4G and the other for
* the window above 4G. The latter range has IORESOURCE_ABOVE_4G flag set to satisfy resource
* requests from downstream devices for allocations above 4G.
*/
static void initialize_domain_memranges(struct memranges *ranges, const struct resource *res,
unsigned long memrange_type)
{
unsigned char align = get_alignment_by_resource_type(res);
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
if (is_resource_invalid(res))
return;
if (res->flags & IORESOURCE_IO)
initialize_domain_io_resource_memranges(ranges, res, memrange_type);
else
initialize_domain_mem_resource_memranges(ranges, res, memrange_type);
}
/*
* This function initializes memranges for bridge device. Unlike domain, bridge does not need to
* care about resource window crossing 4G boundary. This is handled by the resource allocator at
* domain level to ensure that all downstream bridges are allocated space either above or below
* 4G boundary as per the state of IORESOURCE_ABOVE_4G for the respective bridge resource.
*
* So, this function creates a single range of the entire resource window available for the
* bridge resource. Thus all downstream resources of the bridge for the given resource type get
* allocated space from the same window. If there is any downstream resource of the bridge which
* requests allocation above 4G, then all other downstream resources of the same type under the
* bridge get allocated above 4G.
*/
static void initialize_bridge_memranges(struct memranges *ranges, const struct resource *res,
unsigned long memrange_type)
{
unsigned char align = get_alignment_by_resource_type(res);
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
if (is_resource_invalid(res))
return;
memranges_insert(ranges, res->base, res->limit - res->base + 1, memrange_type);
}
static void print_resource_ranges(const struct device *dev, const struct memranges *ranges)
{
const struct range_entry *r;
printk(BIOS_INFO, " %s: Resource ranges:\n", dev_path(dev));
if (memranges_is_empty(ranges))
printk(BIOS_INFO, " * EMPTY!!\n");
memranges_each_entry(r, ranges) {
printk(BIOS_INFO, " * Base: %llx, Size: %llx, Tag: %lx\n",
range_entry_base(r), range_entry_size(r), range_entry_tag(r));
}
}
/*
* This is where the actual allocation of resources happens during pass 2. Given the list of
* memory ranges corresponding to the resource of given type, it finds the biggest unallocated
* resource using the type mask on the downstream bus. This continues in a descending
* order until all resources of given type are allocated address space within the current
* resource window.
*/
static void allocate_child_resources(struct bus *bus, struct memranges *ranges,
unsigned long type_mask, unsigned long type_match)
{
struct resource *resource = NULL;
const struct device *dev;
while ((dev = largest_resource(bus, &resource, type_mask, type_match))) {
if (!resource->size)
continue;
if (memranges_steal(ranges, resource->limit, resource->size, resource->align,
type_match, &resource->base) == false) {
printk(BIOS_ERR, " ERROR: Resource didn't fit!!! ");
printk(BIOS_DEBUG, " %s %02lx * size: 0x%llx limit: %llx %s\n",
dev_path(dev), resource->index,
resource->size, resource->limit, resource2str(resource));
continue;
}
resource->limit = resource->base + resource->size - 1;
resource->flags |= IORESOURCE_ASSIGNED;
printk(BIOS_DEBUG, " %s %02lx * [0x%llx - 0x%llx] limit: %llx %s\n",
dev_path(dev), resource->index, resource->base,
resource->size ? resource->base + resource->size - 1 :
resource->base, resource->limit, resource2str(resource));
}
}
static void update_constraints(struct memranges *ranges, const struct device *dev,
const struct resource *res)
{
if (!res->size)
return;
printk(BIOS_DEBUG, " %s: %s %02lx base %08llx limit %08llx %s (fixed)\n",
__func__, dev_path(dev), res->index, res->base,
res->base + res->size - 1, resource2str(res));
memranges_create_hole(ranges, res->base, res->size);
}
/*
* Scan the entire tree to identify any fixed resources allocated by any device to
* ensure that the address map for domain resources are appropriately updated.
*
* Domains can typically provide memrange for entire address space. So, this function
* punches holes in the address space for all fixed resources that are already
* defined. Both IO and normal memory resources are added as fixed. Both need to be
* removed from address space where dynamic resource allocations are sourced.
*/
static void avoid_fixed_resources(struct memranges *ranges, const struct device *dev,
unsigned long mask_match)
{
const struct resource *res;
const struct device *child;
const struct bus *bus;
for (res = dev->resource_list; res != NULL; res = res->next) {
if ((res->flags & mask_match) != mask_match)
continue;
update_constraints(ranges, dev, res);
}
bus = dev->link_list;
if (bus == NULL)
return;
for (child = bus->children; child != NULL; child = child->sibling)
avoid_fixed_resources(ranges, child, mask_match);
}
static void constrain_domain_resources(const struct device *domain, struct memranges *ranges,
unsigned long type)
{
unsigned long mask_match = type | IORESOURCE_FIXED;
if (type == IORESOURCE_IO) {
/*
* Don't allow allocations in the VGA I/O range. PCI has special cases for
* that.
*/
memranges_create_hole(ranges, 0x3b0, 0x3df - 0x3b0 + 1);
/*
* Resource allocator no longer supports the legacy behavior where I/O resource
* allocation is guaranteed to avoid aliases over legacy PCI expansion card
* addresses.
*/
}
avoid_fixed_resources(ranges, domain, mask_match);
}
/*
* This function creates a list of memranges of given type using the resource that is
* provided. If the given resource is NULL or if the resource window size is 0, then it creates
* an empty list. This results in resource allocation for that resource type failing for all
* downstream devices since there is nothing to allocate from.
*
* In case of domain, it applies additional constraints to ensure that the memranges do not
* overlap any of the fixed resources under that domain. Domain typically seems to provide
* memrange for entire address space. Thus, it is up to the chipset to add DRAM and all other
* windows which cannot be used for resource allocation as fixed resources.
*/
static void setup_resource_ranges(const struct device *dev, const struct resource *res,
unsigned long type, struct memranges *ranges)
{
printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx\n",
dev_path(dev), resource2str(res), res->base, res->size, res->align,
res->gran, res->limit);
if (dev->path.type == DEVICE_PATH_DOMAIN) {
initialize_domain_memranges(ranges, res, type);
constrain_domain_resources(dev, ranges, type);
} else {
initialize_bridge_memranges(ranges, res, type);
}
print_resource_ranges(dev, ranges);
}
static void cleanup_resource_ranges(const struct device *dev, struct memranges *ranges,
const struct resource *res)
{
memranges_teardown(ranges);
printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx done\n",
dev_path(dev), resource2str(res), res->base, res->size, res->align,
res->gran, res->limit);
}
/*
* Pass 2 of resource allocator at the bridge level loops through all the resources for the
* bridge and generates a list of memory ranges similar to that at the domain level. However,
* there is no need to apply any additional constraints since the window allocated to the bridge
* is guaranteed to be non-overlapping by the allocator at domain level.
*
* Allocation at the bridge level works the same as at domain level (starts with the biggest
* resource requirement from downstream devices and continues in descending order). One major
* difference at the bridge level is that it considers prefmem resources separately from mem
* resources.
*
* Once allocation at the current bridge is complete, resource allocator continues walking down
* the downstream bridges until it hits the leaf devices.
*/
static void allocate_bridge_resources(const struct device *bridge)
{
struct memranges ranges;
const struct resource *res;
struct bus *bus = bridge->link_list;
unsigned long type_match;
struct device *child;
const unsigned long type_mask = IORESOURCE_TYPE_MASK | IORESOURCE_PREFETCH;
for (res = bridge->resource_list; res; res = res->next) {
if (!res->size)
continue;
if (!(res->flags & IORESOURCE_BRIDGE))
continue;
type_match = res->flags & type_mask;
setup_resource_ranges(bridge, res, type_match, &ranges);
allocate_child_resources(bus, &ranges, type_mask, type_match);
cleanup_resource_ranges(bridge, &ranges, res);
}
for (child = bus->children; child; child = child->sibling) {
if (!dev_has_children(child))
continue;
allocate_bridge_resources(child);
}
}
static const struct resource *find_domain_resource(const struct device *domain,
unsigned long type)
{
const struct resource *res;
for (res = domain->resource_list; res; res = res->next) {
if (res->flags & IORESOURCE_FIXED)
continue;
if ((res->flags & IORESOURCE_TYPE_MASK) == type)
return res;
}
return NULL;
}
/*
* Pass 2 of resource allocator begins at the domain level. Every domain has two types of
* resources - io and mem. For each of these resources, this function creates a list of memory
* ranges that can be used for downstream resource allocation. This list is constrained to
* remove any fixed resources in the domain sub-tree of the given resource type. It then uses
* the memory ranges to apply best fit on the resource requirements of the downstream devices.
*
* Once resources are allocated to all downstream devices of the domain, it walks down each
* downstream bridge to continue the same process until resources are allocated to all devices
* under the domain.
*/
static void allocate_domain_resources(const struct device *domain)
{
struct memranges ranges;
struct device *child;
const struct resource *res;
/* Resource type I/O */
res = find_domain_resource(domain, IORESOURCE_IO);
if (res) {
setup_resource_ranges(domain, res, IORESOURCE_IO, &ranges);
allocate_child_resources(domain->link_list, &ranges, IORESOURCE_TYPE_MASK,
IORESOURCE_IO);
cleanup_resource_ranges(domain, &ranges, res);
}
/*
* Resource type Mem:
* Domain does not distinguish between mem and prefmem resources. Thus, the resource
* allocation at domain level considers mem and prefmem together when finding the best
* fit based on the biggest resource requirement.
*
* However, resource requests for allocation above 4G boundary need to be handled
* separately if the domain resource window crosses this boundary. There is a single
* window for resource of type IORESOURCE_MEM. When creating memranges, this resource
* is split into two separate ranges -- one for the window below 4G boundary and other
* for the window above 4G boundary (with IORESOURCE_ABOVE_4G flag set). Thus, when
* allocating child resources, requests for below and above the 4G boundary are handled
* separately by setting the type_mask and type_match to allocate_child_resources()
* accordingly.
*/
res = find_domain_resource(domain, IORESOURCE_MEM);
if (res) {
setup_resource_ranges(domain, res, IORESOURCE_MEM, &ranges);
allocate_child_resources(domain->link_list, &ranges,
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
IORESOURCE_MEM);
allocate_child_resources(domain->link_list, &ranges,
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
IORESOURCE_MEM | IORESOURCE_ABOVE_4G);
cleanup_resource_ranges(domain, &ranges, res);
}
for (child = domain->link_list->children; child; child = child->sibling) {
if (!dev_has_children(child))
continue;
/* Continue allocation for all downstream bridges. */
allocate_bridge_resources(child);
}
}
/*
* This function forms the guts of the resource allocator. It walks through the entire device
* tree for each domain two times.
*
* Every domain has a fixed set of ranges. These ranges cannot be relaxed based on the
* requirements of the downstream devices. They represent the available windows from which
* resources can be allocated to the different devices under the domain.
*
* In order to identify the requirements of downstream devices, resource allocator walks in a
* DFS fashion. It gathers the requirements from leaf devices and propagates those back up
* to their upstream bridges until the requirements for all the downstream devices of the domain
* are gathered. This is referred to as pass 1 of resource allocator.
*
* Once the requirements for all the devices under the domain are gathered, resource allocator
* walks a second time to allocate resources to downstream devices as per the
* requirements. It always picks the biggest resource request as per the type (i/o and mem) to
* allocate space from its fixed window to the immediate downstream device of the domain. In
* order to accomplish best fit for the resources, a list of ranges is maintained by each
* resource type (i/o and mem). Domain does not differentiate between mem and prefmem. Since
* they are allocated space from the same window, the resource allocator at the domain level
* ensures that the biggest requirement is selected indepedent of the prefetch type. Once the
* resource allocation for all immediate downstream devices is complete at the domain level,
* resource allocator walks down the subtree for each downstream bridge to continue the
* allocation process at the bridge level. Since bridges have separate windows for i/o, mem and
* prefmem, best fit algorithm at bridge level looks for the biggest requirement considering
* prefmem resources separately from non-prefmem resources. This continues until resource
* allocation is performed for all downstream bridges in the domain sub-tree. This is referred
* to as pass 2 of resource allocator.
*
* Some rules that are followed by the resource allocator:
* - Allocate resource locations for every device as long as the requirements can be satisfied.
* - If a resource cannot be allocated any address space, then that resource needs to be
* properly updated to ensure that it does not incorrectly overlap some address space reserved
* for a different purpose.
* - Don't overlap with resources in fixed locations.
* - Don't overlap and follow the rules of bridges -- downstream devices of bridges should use
* parts of the address space allocated to the bridge.
*/
void allocate_resources(const struct device *root)
{
const struct device *child;
if ((root == NULL) || (root->link_list == NULL))
return;
for (child = root->link_list->children; child; child = child->sibling) {
if (child->path.type != DEVICE_PATH_DOMAIN)
continue;
post_log_path(child);
/* Pass 1 - Gather requirements. */
printk(BIOS_INFO, "==== Resource allocator: %s - Pass 1 (gathering requirements) ===\n",
dev_path(child));
compute_domain_resources(child);
/* Pass 2 - Allocate resources as per gathered requirements. */
printk(BIOS_INFO, "=== Resource allocator: %s - Pass 2 (allocating resources) ===\n",
dev_path(child));
allocate_domain_resources(child);
printk(BIOS_INFO, "=== Resource allocator: %s - resource allocation complete ===\n",
dev_path(child));
}
}

View File

@@ -1,5 +1,3 @@
#
#
# SPDX-License-Identifier: GPL-2.0-only
config DRIVERS_AMD_PI

Some files were not shown because too many files have changed in this diff Show More