Compare commits
No commits in common. "main" and "HEAD" have entirely different histories.
5027 changed files with 50708 additions and 258322 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -35,7 +35,6 @@ tags
|
|||
.cache
|
||||
compile_commands.json
|
||||
.vscode/
|
||||
.clangd
|
||||
|
||||
# Cross-compile toolkits
|
||||
xgcc/
|
||||
|
|
|
|||
11
.gitmodules
vendored
11
.gitmodules
vendored
|
|
@ -54,7 +54,6 @@
|
|||
[submodule "3rdparty/intel-sec-tools"]
|
||||
path = 3rdparty/intel-sec-tools
|
||||
url = https://review.coreboot.org/9esec-security-tooling.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/stm"]
|
||||
path = 3rdparty/stm
|
||||
url = https://review.coreboot.org/STM
|
||||
|
|
@ -63,19 +62,9 @@
|
|||
path = util/goswid
|
||||
url = https://review.coreboot.org/goswid
|
||||
branch = trunk
|
||||
ignore = dirty
|
||||
[submodule "src/vendorcode/amd/opensil/genoa_poc/opensil"]
|
||||
path = src/vendorcode/amd/opensil/genoa_poc/opensil
|
||||
url = https://review.coreboot.org/opensil_genoa_poc.git
|
||||
[submodule "3rdparty/open-power-signing-utils"]
|
||||
path = 3rdparty/open-power-signing-utils
|
||||
url = https://review.coreboot.org/open-power-signing-utils.git
|
||||
[submodule "src/vendorcode/amd/opensil/phoenix_poc/opensil"]
|
||||
path = src/vendorcode/amd/opensil/phoenix_poc/opensil
|
||||
url = https://github.com/openSIL/openSIL.git
|
||||
branch = phoenix_poc
|
||||
[submodule "src/vendorcode/amd/opensil/turin_poc/opensil"]
|
||||
path = src/vendorcode/amd/opensil/turin_poc/opensil
|
||||
url = https://github.com/openSIL/openSIL.git
|
||||
branch = turin_poc
|
||||
|
||||
|
|
|
|||
2
3rdparty/amd_blobs
vendored
2
3rdparty/amd_blobs
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit aa9288a33c6d7a67e55b8757390029207593fa9f
|
||||
Subproject commit 26c572974bcf7255930b0e9a51da3144ed0104b5
|
||||
2
3rdparty/arm-trusted-firmware
vendored
2
3rdparty/arm-trusted-firmware
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 9109143417b24337d39a2a9583828a44996f8aac
|
||||
Subproject commit 15e5c6c91d483aa52908154cc80e48956e234232
|
||||
2
3rdparty/blobs
vendored
2
3rdparty/blobs
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 4a8de0324e7d389454ec33cdf66939b653bf6800
|
||||
Subproject commit 14f8fcc1b426cb0884a21a9b715da6e0c1c7f434
|
||||
2
3rdparty/fsp
vendored
2
3rdparty/fsp
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 81399b3b61479abc379c2d01362d4b6dc6f515c9
|
||||
Subproject commit 15c0f7b3f723bcd713e5ab11ebc502f30d9084e7
|
||||
2
3rdparty/intel-microcode
vendored
2
3rdparty/intel-microcode
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 250941fb670645d7d91f761cc63656ad3a1ec367
|
||||
Subproject commit 8ac9378a84879e81c503e09f344560b3dd7f72df
|
||||
2
3rdparty/libgfxinit
vendored
2
3rdparty/libgfxinit
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 3c3828add50024e90e57d6fbe0e660d1b66302d9
|
||||
Subproject commit 17cfc92f402493979783585b6581efbd98c0cf07
|
||||
2
3rdparty/qc_blobs
vendored
2
3rdparty/qc_blobs
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 6379308814bd00a8b6e36a4715bdb86ba019a3a7
|
||||
Subproject commit a252198ec6544e13904cfe831cec3e784aaa715d
|
||||
2
3rdparty/vboot
vendored
2
3rdparty/vboot
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 5c360ef458b0a013d8a6d47724bb0fffb5accbcf
|
||||
Subproject commit 3f94e2c7ed58c4e67d6e7dc6052ec615dbbb9bb4
|
||||
103
AUTHORS
103
AUTHORS
|
|
@ -15,14 +15,12 @@ Aaron Durbin
|
|||
Abe Levkoy
|
||||
Abel Briggs
|
||||
Abhinav Hardikar
|
||||
Abhishek Pandit-Subedi
|
||||
AdaCore
|
||||
Adam Liu
|
||||
Adam Mills
|
||||
Advanced Computing Lab, LANL
|
||||
Advanced Micro Devices, Inc.
|
||||
AG Electronics Ltd.
|
||||
Agogo
|
||||
Ahamed Husni
|
||||
Akshu Agrawal
|
||||
Al Hirani
|
||||
|
|
@ -43,30 +41,24 @@ Alexey Vazhnov
|
|||
Alice Sell
|
||||
Alicja Michalska
|
||||
Allen-KH Cheng
|
||||
Alok Agarwal
|
||||
Alper Nebi Yasak
|
||||
Amanda Hwang
|
||||
American Megatrends International, LLC
|
||||
Amersel
|
||||
Amit Caleechurn
|
||||
Ana Carolina Cabral
|
||||
Analog Devices Inc.
|
||||
Analogix Semiconductor
|
||||
Anand Mistry
|
||||
Anand Vaikar
|
||||
Anastasios Koutian
|
||||
Andre
|
||||
Andre Heider
|
||||
Andrew McRae
|
||||
Andrew SH Cheng
|
||||
Andrey Pronin
|
||||
Andriy Gapon
|
||||
Andy
|
||||
Andy Fleming
|
||||
Andy Pont
|
||||
Andy-ld Lu
|
||||
Angel Pons
|
||||
Angela Czubak
|
||||
Anil Kumar K
|
||||
Anna Karaś
|
||||
Annie Chen
|
||||
|
|
@ -76,7 +68,6 @@ Appukuttan V K
|
|||
Arashk Mahshidfar
|
||||
Arec Kao
|
||||
Ariel Fang
|
||||
Ariel Otilibili
|
||||
ARM Limited and Contributors
|
||||
Arthur Heymans
|
||||
Asami Doi
|
||||
|
|
@ -86,12 +77,9 @@ Ashqti
|
|||
ASPEED Technology Inc.
|
||||
Atheros Corporation
|
||||
Atmel Corporation
|
||||
Avi Uday
|
||||
Avinash Munduru
|
||||
Balaji Manigandan
|
||||
Balázs Vinarz
|
||||
BAP - Bruhnspace Advanced Projects
|
||||
Bartłomiej Grzesik
|
||||
Baruch Siach
|
||||
Ben Chuang
|
||||
Ben Kao
|
||||
|
|
@ -102,7 +90,6 @@ Bernardo Perez Priego
|
|||
Bhanu Prakash Maiya
|
||||
Bill Xie
|
||||
Bin Meng
|
||||
Bincai Liu
|
||||
Bitland Tech Inc.
|
||||
Bob Moragues
|
||||
Bora Guvendik
|
||||
|
|
@ -110,13 +97,10 @@ Boris Barbulovski
|
|||
Boris Mittelberg
|
||||
Brandon Breitenstein
|
||||
Brandon Weeks
|
||||
Brian Hsu
|
||||
Brian Norris
|
||||
Bryant Ou
|
||||
Carl-Daniel Hailfinger
|
||||
Carlos López
|
||||
Casper Chang
|
||||
Cathy Xu
|
||||
Caveh Jalali
|
||||
Cavium Inc.
|
||||
Chao Gui
|
||||
|
|
@ -141,7 +125,6 @@ Cong Yang
|
|||
CoolStar
|
||||
coresystems GmbH
|
||||
Corey Osgood
|
||||
Crystal Guo
|
||||
Curt Brune
|
||||
Curtis Chen
|
||||
Custom Ideas
|
||||
|
|
@ -161,7 +144,6 @@ Dave Airlie
|
|||
David Brownell
|
||||
David Greenman
|
||||
David Hendricks
|
||||
David Li
|
||||
David Lin
|
||||
David Milosevic
|
||||
David Mosberger-Tang
|
||||
|
|
@ -171,7 +153,6 @@ David Wu
|
|||
Dawei Chien
|
||||
Deepika Punyamurtula
|
||||
Deepti Deshatty
|
||||
Dehui Sun
|
||||
Denis 'GNUtoo' Carikli
|
||||
Denis Dowling
|
||||
DENX Software Engineering
|
||||
|
|
@ -185,7 +166,6 @@ Divya S Sasidharan
|
|||
Dmitry Ponamorev
|
||||
Dmitry Torokhov
|
||||
DMP Electronics Inc.
|
||||
Dolan Liu
|
||||
Dominik Behr
|
||||
Donghwa Lee
|
||||
Drew Eckhardt
|
||||
|
|
@ -206,8 +186,6 @@ ELSOFT AG
|
|||
Eltan B.V
|
||||
Eltan B.V.
|
||||
Elyes Haouas
|
||||
Emilie Roberts
|
||||
Enzo Potenza
|
||||
Eran Mitrani
|
||||
Eren Peng
|
||||
Eric Biederman
|
||||
|
|
@ -221,20 +199,17 @@ Ethan Tsao
|
|||
Eugene Myers
|
||||
Evan Green
|
||||
Evgeny Zinoviev
|
||||
Evie (Ivi) Ballou
|
||||
Fabian Groffen
|
||||
Fabian Kunkel
|
||||
Fabian Meyer
|
||||
Fabio Aiuto
|
||||
Fabrice Bellard
|
||||
Facebook, Inc.
|
||||
Federico Amedeo Izzo
|
||||
Fei Yan
|
||||
Felix Friedlander
|
||||
Felix Held
|
||||
Felix Singer
|
||||
Fengquan Chen
|
||||
Filip Brozovic
|
||||
Filip Lewiński
|
||||
Flora Fu
|
||||
Florian Laufenböck
|
||||
|
|
@ -250,12 +225,8 @@ Freescale Semiconductor, Inc.
|
|||
Furquan Shaikh
|
||||
Gaggery Tsai
|
||||
Gang C Chen
|
||||
Garen Wu
|
||||
Gareth Yu
|
||||
Garmin Chang
|
||||
Gary Jennejohn
|
||||
Gavin Liu
|
||||
George Burgess
|
||||
George Trudeau
|
||||
Gerald Van Baren
|
||||
Gerd Hoffmann
|
||||
|
|
@ -263,7 +234,6 @@ Gergely Kiss
|
|||
Google LLC
|
||||
Greg Watson
|
||||
Grzegorz Bernacki
|
||||
Guangjie Song
|
||||
Guennadi Liakhovetski
|
||||
Guodong Liu
|
||||
Gwendal Grignou
|
||||
|
|
@ -271,7 +241,6 @@ Hal Martin
|
|||
Hao Chou
|
||||
Hao Wang
|
||||
HardenedLinux
|
||||
Harrie Paijmans
|
||||
Harsha B R
|
||||
Harshit Sharma
|
||||
Henry C Chen
|
||||
|
|
@ -279,13 +248,11 @@ Herbert Wu
|
|||
Hewlett Packard Enterprise Development LP
|
||||
Hewlett-Packard Development Company, L.P.
|
||||
Himanshu Sahdev
|
||||
Hope Wang
|
||||
Housong Zhang
|
||||
Hsiao Chien Sung
|
||||
Hsin-hsiung wang
|
||||
Hsin-Te Yuan
|
||||
Hsuan Ting Chen
|
||||
Hualin Wei
|
||||
Huaqin Technology Co., Ltd
|
||||
Huaqin Telecom Inc.
|
||||
Hui Liu
|
||||
|
|
@ -300,7 +267,6 @@ Igor Pavlov
|
|||
Ikjoon Jang
|
||||
Imagination Technologies
|
||||
Infineon Technologies
|
||||
Ingo Reitz
|
||||
InKi Dae
|
||||
INSPUR Co., Ltd
|
||||
Intel Corporation
|
||||
|
|
@ -318,14 +284,11 @@ Jakub Czapiga
|
|||
James Chao
|
||||
James Lo
|
||||
James Ye
|
||||
Jameson Thies
|
||||
Jamie Chen
|
||||
Jamie Ryu
|
||||
Jan Dabros
|
||||
Jan Philipp Groß
|
||||
Jan Samek
|
||||
Jan Tatje
|
||||
Jarried Lin
|
||||
Jason Glenesk
|
||||
Jason Nein
|
||||
Jason V Le
|
||||
|
|
@ -334,9 +297,7 @@ Jason Zhao
|
|||
jason-ch chen
|
||||
Jason-jh Lin
|
||||
Jay Patel
|
||||
Jayvik Desai
|
||||
Jean Lucas
|
||||
Jędrzej Ciupis
|
||||
Jeff Chase
|
||||
Jeff Daly
|
||||
Jeff Li
|
||||
|
|
@ -357,12 +318,10 @@ Jingle Hsu
|
|||
Jitao Shi
|
||||
Joe Pillow
|
||||
Joe Tessler
|
||||
Joel Bueno
|
||||
Joel Kitching
|
||||
Joel Linn
|
||||
Joey Peng
|
||||
Johanna Schander
|
||||
Johannes Hahn
|
||||
John Su
|
||||
John Zhao
|
||||
Johnny Li
|
||||
|
|
@ -380,9 +339,7 @@ Jörg Mische
|
|||
Joseph Smith
|
||||
Josie Nordrum
|
||||
Juan José García-Castro Crespo
|
||||
Julia Kittlinger
|
||||
Julia Tsai
|
||||
Julian Intronati
|
||||
Julian Schroeder
|
||||
Julian Stecklina
|
||||
Julien Viard de Galbert
|
||||
|
|
@ -391,11 +348,9 @@ Kacper Stojek
|
|||
Kaiyen Chang
|
||||
Kane Chen
|
||||
Kangheui Won
|
||||
KangMin Wang
|
||||
Kapil Porwal
|
||||
Karol Zmyslowski
|
||||
Karthik Ramasubramanian
|
||||
Ke Zheng
|
||||
Kei Hiroyoshi
|
||||
Keith Hui
|
||||
Keith Packard
|
||||
|
|
@ -407,12 +362,10 @@ Kevin Chowski
|
|||
Kevin Cody-Little
|
||||
Kevin Keijzer
|
||||
Kevin O'Connor
|
||||
Kevin Yang
|
||||
Kevin3 Yang
|
||||
kewei xu
|
||||
Kilari Raasi
|
||||
Kirk Wang
|
||||
Kiwi Liu
|
||||
Konrad Adamczyk
|
||||
Kontron Europe GmbH
|
||||
Kornel Dulęba
|
||||
|
|
@ -422,7 +375,6 @@ Kshitij
|
|||
Kshitiz Godara
|
||||
Kulkarni. Srinivas
|
||||
Kun Liu
|
||||
KunYi Chen
|
||||
Kyle Lin
|
||||
Kyösti Mälkki
|
||||
Lance Zhao
|
||||
|
|
@ -445,15 +397,10 @@ linear
|
|||
Linus Torvalds
|
||||
Linux Networx, Inc.
|
||||
LiPPERT ADLINK Technology GmbH
|
||||
Liu Liu
|
||||
Liya Li
|
||||
Lu Tang
|
||||
Lu. Pen-ChunX
|
||||
Lubomir Rintel
|
||||
Luc Verhaegen
|
||||
Luca Lai
|
||||
Lucas Chen
|
||||
Lukas Wunner
|
||||
Mac Chiang
|
||||
Maciej Matuszczyk
|
||||
Maciej Pijanowski
|
||||
|
|
@ -473,7 +420,6 @@ Mario Scheithauer
|
|||
Marius Gröger
|
||||
Mariusz Szafranski
|
||||
Mariusz Szafrański
|
||||
Mark Chang
|
||||
Mark Hasemeyer
|
||||
Mark Hsieh
|
||||
Mars Chen
|
||||
|
|
@ -484,7 +430,6 @@ Martin Roth
|
|||
Marvell International Ltd.
|
||||
Marvell Semiconductor Inc.
|
||||
Marx Wang
|
||||
Masa Nakura
|
||||
Masanori Ogino
|
||||
Máté Kukri
|
||||
Matei Dibu
|
||||
|
|
@ -493,7 +438,6 @@ Matt Chen
|
|||
Matt Delco
|
||||
Matt DeVillier
|
||||
Matt Papageorge
|
||||
Matt Turner
|
||||
Matthew Blecker
|
||||
Matthew Ziegelbaum
|
||||
Mattias Nissler
|
||||
|
|
@ -506,7 +450,6 @@ Maximilian Brune
|
|||
Mediatek Inc.
|
||||
MediaTek Inc.
|
||||
Meera Ravindranath
|
||||
Melongmelong
|
||||
Meng-Huan Yu
|
||||
Meta Platforms, Inc
|
||||
mgabryelski1
|
||||
|
|
@ -519,20 +462,15 @@ Michael Strosche
|
|||
Michael Walle
|
||||
Michał Kopeć
|
||||
Michal Suchanek
|
||||
Michał Zieliński
|
||||
Michał Żygowski
|
||||
Micro-Star INT'L CO., LTD.
|
||||
Mika Westerberg
|
||||
Mike Banon
|
||||
Mike Lin
|
||||
Mike Shih
|
||||
Mingjin Ge
|
||||
Miriam Polzer
|
||||
mkurumel
|
||||
Moises Garcia
|
||||
Momoko Hattori
|
||||
Mondrian Nuessle
|
||||
Monika A
|
||||
Monikaanan
|
||||
MontaVista Software, Inc.
|
||||
Morgan Jang
|
||||
|
|
@ -542,7 +480,7 @@ mtk15698
|
|||
mturney mturney
|
||||
Musse Abdullahi
|
||||
Myles Watson
|
||||
Nancy Lin
|
||||
Nancy.Lin
|
||||
Naresh Solanki
|
||||
Nathan Lu
|
||||
Naveen R. Iyer
|
||||
|
|
@ -553,14 +491,12 @@ Nicholas Sielicki
|
|||
Nicholas Sudsgaard
|
||||
Nick Barker
|
||||
Nick Chen
|
||||
Nick Kochlowski
|
||||
Nick Vaccaro
|
||||
Nico Huber
|
||||
Nico Rikken
|
||||
Nicola Corna
|
||||
Nicolas Boichat
|
||||
Nicole Faerber
|
||||
Nigel Tao
|
||||
Nikolai Vyssotski
|
||||
Nils Jacobs
|
||||
Nina Wu
|
||||
|
|
@ -574,10 +510,6 @@ Omar Pakker
|
|||
Online SAS
|
||||
Opal Voravootivat
|
||||
Orion Technologies, LLC
|
||||
Ot_chhao.chang
|
||||
Ot_hao.han
|
||||
Ot_song Fan
|
||||
Pablo
|
||||
Pablo Ceballos
|
||||
Pablo Stebler
|
||||
Pan Gao
|
||||
|
|
@ -607,19 +539,16 @@ Philipp Bartsch
|
|||
Philipp Degler
|
||||
Philipp Deppenwiese
|
||||
Philipp Hug
|
||||
Pierce Chou
|
||||
Piotr Kleinschmidt
|
||||
Po Xu
|
||||
Poornima Tom
|
||||
Pranava Y N
|
||||
Prasad Malisetty
|
||||
Prashant Malani
|
||||
Pratik Vishwakarma
|
||||
Pratikkumar Prajapati
|
||||
Pratikkumar V Prajapati
|
||||
Protectli
|
||||
PugzAreCute
|
||||
Purdea Andrei
|
||||
Purism SPC
|
||||
Purism, SPC
|
||||
Qii Wang
|
||||
Qinghong Zeng
|
||||
|
|
@ -637,7 +566,6 @@ Ravindra
|
|||
Ravishankar Sarawadi
|
||||
Ray Han Lim Ng
|
||||
Raymond Chung
|
||||
Reagan
|
||||
Red Hat, Inc
|
||||
ReddestDream
|
||||
Rehan Ghori
|
||||
|
|
@ -655,7 +583,6 @@ Richard Woodruff
|
|||
Rick Lee
|
||||
Ricky Chang
|
||||
Riku Viitanen
|
||||
Rishika Raj
|
||||
Ritul Guru
|
||||
Rizwan Qureshi
|
||||
Rnhmjoj
|
||||
|
|
@ -675,7 +602,6 @@ Roman Zippel
|
|||
Ron Lee
|
||||
Ron Minnich
|
||||
Ronak Kanabar
|
||||
Ronald Claveau
|
||||
Ronald G. Minnich
|
||||
Rory Liu
|
||||
Rudolf Marek
|
||||
|
|
@ -697,7 +623,6 @@ Samuel Holland
|
|||
Sandeep Maheswaram
|
||||
Sathya Prakash M R
|
||||
Satya Priya Kakitapalli
|
||||
Satya SreenivasL
|
||||
Saurabh Mishra
|
||||
SciTech Software, Inc.
|
||||
Scott Chao
|
||||
|
|
@ -725,7 +650,6 @@ Shiyu Sun
|
|||
Shon Wang
|
||||
Shou-Chieh Hsu
|
||||
Shreesh Chhabbi
|
||||
Shunxi Zhang
|
||||
Shuo Liu
|
||||
Siemens AG
|
||||
SiFive, Inc
|
||||
|
|
@ -738,12 +662,10 @@ Simon Zhou
|
|||
Sindhoor Tilak
|
||||
Solomon Alan-Dei
|
||||
Song Fan
|
||||
Sowmya Aralguppe
|
||||
Sridhar Siricilla
|
||||
Srinidhi N Kaushik
|
||||
Srinivasa Rao Mandadapu
|
||||
ST Microelectronics
|
||||
Stanisław Kardach
|
||||
Stanley Wu
|
||||
Star Labs Online Ltd
|
||||
Stefan Binding
|
||||
|
|
@ -771,7 +693,6 @@ Tao Xia
|
|||
Tarun Tuli
|
||||
Teddy Shih
|
||||
Terry Chen
|
||||
Terry Cheong
|
||||
Texas Instruments
|
||||
The Android Open Source Project
|
||||
The ChromiumOS Authors
|
||||
|
|
@ -790,9 +711,7 @@ Timothy Pearson
|
|||
tinghan shen
|
||||
Tobias Diedrich
|
||||
Tom Hiller
|
||||
Tomasz Michalec
|
||||
Tommie Lin
|
||||
Tongtong Pan
|
||||
Tony Huang
|
||||
Tracy Wu
|
||||
Trevor Wu
|
||||
|
|
@ -809,13 +728,10 @@ Usha P
|
|||
Uwe Hermann
|
||||
Uwe Poeche
|
||||
V Sowmya
|
||||
Vladimir Epifantsev
|
||||
Václav Straka
|
||||
Vadim Bendebury
|
||||
Valentyn Sudomyr
|
||||
Van Chen
|
||||
Varshit B Pandya
|
||||
Varun Upadhyay
|
||||
Veerabhadrarao Badiganti
|
||||
Venkat Thogaru
|
||||
Venkata Krishna Nimmagadda
|
||||
|
|
@ -824,7 +740,6 @@ Victor Ding
|
|||
Vidya Gopalakrishnan
|
||||
Vikram Narayanan
|
||||
Vikrant L Jadeja
|
||||
Vince Liu
|
||||
Vinod Polimera
|
||||
Vipin Kumar
|
||||
Vitaly Rodionov
|
||||
|
|
@ -837,10 +752,8 @@ Ward Vandewege
|
|||
Wayne Wang
|
||||
Weimin Wu
|
||||
Weiyi Lu
|
||||
Wen Zhang
|
||||
Wenbin Mei
|
||||
Wentao Qin
|
||||
Wenzhen Yu
|
||||
Werner Zeh
|
||||
Wilbert Duijvenvoorde
|
||||
William Wei
|
||||
|
|
@ -859,16 +772,12 @@ Wonkyu Kim
|
|||
Wuxy
|
||||
Xiang W
|
||||
Xin Ji
|
||||
Xiwen Shao
|
||||
Xixi Chen
|
||||
Xue Yao
|
||||
Xueqi Zhang
|
||||
Xuxin Xiong
|
||||
YADRO
|
||||
Yan Liu
|
||||
Yang Wu
|
||||
Yann Collet
|
||||
Yanqiong Huang
|
||||
Yaroslav Kurlaev
|
||||
YH Lin
|
||||
Yidi Lin
|
||||
|
|
@ -883,19 +792,14 @@ Yu-Ping Wu
|
|||
Yuanliding
|
||||
Yuchen He
|
||||
Yuchen Huang
|
||||
Yuchiche
|
||||
Yunlong Jia
|
||||
Yuval Peress
|
||||
Zachary Yedidia
|
||||
Zanxi Chen
|
||||
Zebreus
|
||||
Zhanyong Wang
|
||||
Zhaoming Luo
|
||||
Zhaoqing Jiu
|
||||
Zheng Bao
|
||||
Zhenguo Li
|
||||
Zhi7 Li
|
||||
Zhigang Qin
|
||||
Zhiqiang Ma
|
||||
Zhixing Ma
|
||||
Zhiyong Tao
|
||||
|
|
@ -904,7 +808,6 @@ Zhuohao Lee
|
|||
Ziang Wang
|
||||
Zoey Wu
|
||||
Zoltan Baldaszti
|
||||
一颗小土豆
|
||||
小田喜陽彦
|
||||
忧郁沙茶
|
||||
陳建宏
|
||||
陳建宏
|
||||
|
|
@ -5,7 +5,7 @@ coreboot POST Codes
|
|||
This is an (incomplete) list of POST codes emitted by coreboot v4.
|
||||
|
||||
0x10 Entry into protected mode
|
||||
0x01 Entry into 'entry16.S' reset code jumps to here
|
||||
0x01 Entry into 'crt0.s' reset code jumps to here
|
||||
0x11 Start copying coreboot to RAM with decompression if compressed
|
||||
0x12 Copy/decompression finished jumping to RAM
|
||||
0x80 Entry into coreboot in RAM
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ Spec](https://uefi.org/specifications) for details, or run the tool
|
|||
* CRLF - Carriage Return, Line Feed - \\r\\n - The standard window EOL
|
||||
(End-of-Line) marker.
|
||||
* crt0 - [**C Run Time 0**](https://en.wikipedia.org/wiki/Crt0)
|
||||
* crt0s - crt0 Source code
|
||||
* CRT - [**Cathode Ray Tube**](https://en.wikipedia.org/wiki/Cathode-ray_tube)
|
||||
* CSE - Intel: Converged Security Engine
|
||||
* CSI - MIPI: [**Camera Serial
|
||||
|
|
|
|||
|
|
@ -6,5 +6,91 @@ This section contains documentation about coreboot on x86 architecture.
|
|||
:maxdepth: 1
|
||||
|
||||
x86 PAE support <pae.md>
|
||||
x86_64 support <x86_64.md>
|
||||
```
|
||||
|
||||
## State of x86_64 support
|
||||
Some SOCs now support 64bit mode. Search for HAVE_X86_64_SUPPORT in Kconfig.
|
||||
|
||||
In order to add support for x86_64 the following assumptions were made:
|
||||
* The CPU supports long mode
|
||||
* All memory returned by malloc must be below 4GiB in physical memory
|
||||
* All code that is to be run must be below 4GiB in physical memory
|
||||
* The high dword of pointers is always zero
|
||||
* The reference implementation is qemu
|
||||
* x86 payloads are loaded below 4GiB in physical memory and are jumped
|
||||
to in *protected mode*
|
||||
|
||||
## Assumptions for all stages using the reference implementation
|
||||
* 0-4GiB are identity mapped using 2MiB-pages as WB
|
||||
* Memory above 4GiB isn't accessible
|
||||
* page tables reside in memory mapped ROM
|
||||
* A stage can install new page tables in RAM
|
||||
|
||||
## Page tables
|
||||
A `pagetables` cbfs file is generated based on an assembly file.
|
||||
|
||||
To generate the static page tables it must know the physical address where to
|
||||
place the file.
|
||||
|
||||
The page tables contains the following structure:
|
||||
* PML4E pointing to PDPE
|
||||
* PDPE with *$n* entries each pointing to PDE
|
||||
* *$n* PDEs with 512 entries each
|
||||
|
||||
At the moment *$n* is 4, which results in identity mapping the lower 4 GiB.
|
||||
|
||||
## Basic x86_64 support
|
||||
Basic support for x86_64 has been implemented for QEMU mainboard target.
|
||||
|
||||
## Reference implementation
|
||||
The reference implementation is
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
QEMU i440fx <../../mainboard/emulation/qemu-i440fx.md>
|
||||
QEMU Q35 <../../mainboard/emulation/qemu-q35.md>
|
||||
```
|
||||
|
||||
## TODO
|
||||
* Identity map memory above 4GiB in ramstage
|
||||
|
||||
## Future work
|
||||
|
||||
1. Fine grained page tables for SMM:
|
||||
* Must not have execute and write permissions for the same page.
|
||||
* Must allow only that TSEG pages can be marked executable
|
||||
2. Support 64bit PCI BARs above 4GiB
|
||||
3. Place and run code above 4GiB
|
||||
|
||||
## Porting other boards
|
||||
* Fix compilation errors
|
||||
* Test how well CAR works with x86_64 and paging
|
||||
* Improve mode switches
|
||||
|
||||
## Known problems on real hardware
|
||||
|
||||
Running VGA rom directly fails. Yabel works fine though.
|
||||
|
||||
## Known bugs on KVM enabled qemu
|
||||
|
||||
The `x86_64` reference code runs fine in qemu soft-cpu, but has serious issues
|
||||
when using KVM mode on some machines. The workaround is to *not* place
|
||||
page-tables in ROM, as done in
|
||||
[CB:49228](https://review.coreboot.org/c/coreboot/+/49228).
|
||||
|
||||
Here's a list of known issues:
|
||||
|
||||
* After entering long mode, the FPU doesn't work anymore, including accessing
|
||||
MMX registers. It works fine before entering long mode. It works fine when
|
||||
switching back to protected mode. Other registers, like SSE registers, are
|
||||
working fine.
|
||||
* Reading from virtual memory, when the page tables are stored in ROM, causes
|
||||
the MMU to abort the "page table walking" mechanism when the lower address
|
||||
bits of the virtual address to be translated have a specific pattern.
|
||||
Instead of loading the correct physical page, the one containing the
|
||||
page tables in ROM will be loaded and used, which breaks code and data as
|
||||
the page table doesn't contain the expected data. This in turn leads to
|
||||
undefined behaviour whenever the 'wrong' address is being read.
|
||||
* Disabling paging in compatibility mode crashes the CPU.
|
||||
* Returning from long mode to compatibility mode crashes the CPU.
|
||||
* Entering long mode crashes on AMD host platforms.
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
# x86_64 architecture documentation
|
||||
|
||||
This section documents coreboot's x86_64 support. When enabled,
|
||||
every coreboot stage is built for x86_64, contrary to UEFI's implementation
|
||||
that only runs some stages in x86_64.
|
||||
On UEFI the PEI phase, which is x86_32, brings up DRAM and installs
|
||||
page tables for the x86_64 DXE and BDS phases.
|
||||
|
||||
## Toolchain requirements for x86_64 support
|
||||
* The compiler must support generating code for the *large memory model*
|
||||
(-mcmodel=large). It's supported since GCC 4.4.
|
||||
|
||||
Page tables can be used to provide security benefits, such as by marking
|
||||
memory as non-executable or removing it entirely. This could be useful
|
||||
for SMM to mark regular DRAM as NX.
|
||||
|
||||
The large memory model causes the compiler to emit 64bit addressing
|
||||
instructions, which increases code size. In theory, this is roughly
|
||||
made up for by the faster execution of the x86_64 code.
|
||||
|
||||
* All x86 coreboot stages and payloads must be loaded below 4GiB in
|
||||
physical memory. When jumping to the payload coreboot will drop from
|
||||
long mode back to protected mode to keep compatibility with these payloads.
|
||||
|
||||
## Comparison to UEFI
|
||||
On UEFI the SEC and PEI phases (similar to coreboot's bootblock and romstage)
|
||||
are run in x86_32 mode. The following (guessed) reasons are likely:
|
||||
* There's no need for x86_64 as memory hasn't been trained yet. The whole 4GiB
|
||||
address space, including CAR, memory mapped SPI flash and PCI BARs, are
|
||||
accessible in x86_32.
|
||||
* When the EFI specification was written compilers did not support
|
||||
*large memory model*, required in CAR when using a 1:1 page mapping
|
||||
* Code is 20% bigger in x86_64 due to *large memory model* where pointers and
|
||||
function calls always use 8 byte addressing. However flash size was very
|
||||
limited, compared to today's flash chips, when the EFI spec was written.
|
||||
|
||||
## Current software constraints for x86_64 support
|
||||
The following constraints are coreboot limitations as it was intended to run in
|
||||
protected mode only. The code still assumes 32bit pointers in some places and thus:
|
||||
* The high dword of pointers must always be zero.
|
||||
* All memory returned by malloc must be below 4GiB in physical memory.
|
||||
* All code that is to be run must be below 4GiB in physical memory.
|
||||
* CBMEM must reside below 4GiB in physical memory.
|
||||
|
||||
Any software within coreboot must not access memory resources above 4GiB until
|
||||
end of BS_DEV_RESOURCES in ramstage. Only at that point the full memory map is
|
||||
known and identity mapped.
|
||||
|
||||
## Supported boards
|
||||
On the supported boards you can enable x86_64 compilation by setting the
|
||||
Kconfig `USE_X86_64_SUPPORT`. This config option is enabled if the SOC/CPU
|
||||
selects `HAVE_X86_64_SUPPORT`.
|
||||
|
||||
## Protected mode wrappers
|
||||
On some platforms binary blobs are run to initialize parts of the hardware.
|
||||
When these binary blobs have been compiled for x86_32, then coreboot must
|
||||
switch to protected mode in order to call and run the blobs. Once the invoked
|
||||
blobs finish running, coreboot needs to switch back to long mode.
|
||||
|
||||
Since every BLOB is different a SoC must be enabled to support x86_64 mode
|
||||
by providing the correct wrapper around the x86_32 BLOBs.
|
||||
|
||||
## TODO
|
||||
* Support more platforms
|
||||
* Fix running VGA Option ROMs
|
||||
* Fix running MRC.bin (Sandy Bridge / Haswell boards)
|
||||
* Identity map memory above 4GiB in ramstage
|
||||
* Fine grained page tables for SMM:
|
||||
* Must not have execute and write permissions for the same page.
|
||||
* Must only allow TSEG pages to be marked as executable.
|
||||
* Must reside in SMRAM.
|
||||
* Must be placed together with SMM rmodule.
|
||||
* Support 64bit PCI BARs above 4GiB
|
||||
* Jump to compatible payloads in long mode
|
||||
|
||||
## Porting other boards
|
||||
* Fix compilation errors
|
||||
* Test how well CAR works with x86_64 and paging
|
||||
* Improve mode switches
|
||||
* Test libgfxinit / VGA Option ROMs / FSP
|
||||
|
||||
## Known bugs on real hardware
|
||||
|
||||
According to Intel x86_64 mode hasn't been validated in CAR environments.
|
||||
However, coreboot developers working on x86_64 support have tried this on
|
||||
various Intel platforms, and so far haven't found any issues with CAR when
|
||||
running in x86_64 mode.
|
||||
|
||||
## Known bugs on KVM enabled QEMU
|
||||
|
||||
The `x86_64` reference code runs fine in QEMU's soft-cpu, but has serious issues
|
||||
when using KVM mode on some machines. This is due to various mechanisms trying
|
||||
to accelerate the code execution.
|
||||
|
||||
Known issues in QEMU:
|
||||
* After entering long mode, the FPU doesn't work anymore, including accessing
|
||||
MMX registers. It works fine before entering long mode. It works fine when
|
||||
switching back to protected mode. Other registers, like SSE registers, are
|
||||
working fine.
|
||||
* Reading from virtual memory, when the page tables are stored in ROM, causes
|
||||
the MMU to abort the "page table walking" mechanism when the lower address
|
||||
bits of the virtual address to be translated have a specific pattern.
|
||||
Instead of loading the correct physical page, the one containing the
|
||||
page tables in ROM will be loaded and used, which breaks code and data as
|
||||
the page table doesn't contain the expected data. This in turn leads to
|
||||
undefined behaviour whenever the 'wrong' address is being read.
|
||||
* Disabling paging in compatibility mode crashes the CPU.
|
||||
* Returning from long mode to compatibility mode crashes the CPU.
|
||||
* Entering long mode crashes on AMD host platforms.
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
# Cross-Project Collaboration
|
||||
|
||||
Open source firmware has become popular and important over the last several
|
||||
decades and is currently anchored in multiple key applications like industry,
|
||||
automotive, infrastructure and commodity PC systems. coreboot has a
|
||||
long-reaching history in all these applications and has become a vital
|
||||
alternative to proprietary firmware solutions. Since coreboot itself is
|
||||
minimalistic, other open source projects like SeaBIOS and flashrom help complete
|
||||
the overall user experience by providing a well established way to boot into an
|
||||
OS and easily reprogram the firmware on demand.
|
||||
|
||||
Open source projects often lack funds and are heavily dependent on volunteer
|
||||
work by enthusiasts. coreboot has made its way over the many years it’s been
|
||||
running and is now able to operate its own paid infrastructure for various
|
||||
services like git, Gerrit and Jenkins, all of them are key factors for a
|
||||
worldwide collaboration and project success. Other small but still important
|
||||
projects do not have such resources and face infrastructure issues more often.
|
||||
|
||||
Furthermore, often the same people do work for different open source projects.
|
||||
Therefore, it is important to support such projects where feasible.
|
||||
For instance, sharing the IT infrastructure can leverage quite some synergies
|
||||
as the tasks for different projects are quite similar, e.g. push code to public,
|
||||
review it in Gerrit, let Jenkins do a build and report back to Gerrit or provide
|
||||
a bug tracker platform where users and developers can report bugs and issues.
|
||||
The coreboot project already has servers providing such services and these have
|
||||
a huge amount of headroom to execute such tasks for other projects.
|
||||
Additionally, the developers working on multiple projects are supported as they
|
||||
can do their work with the same mindset while interfacing with common services
|
||||
among different projects. This not only improves cross-project collaboration but
|
||||
also does improve code quality because the developers do not have to switch
|
||||
between different project setups.
|
||||
|
||||
Therefore, the coreboot project explicitly encourages collaboration with other
|
||||
open source projects in the firmware domain. Especially the already well
|
||||
established partnership with flashrom, SeaBIOS, and EM100 should continue to be
|
||||
maintained further on. This includes:
|
||||
|
||||
* Sharing of the IT infrastructure
|
||||
* Sharing the critical services like git, Gerrit, Jenkins and the bugtracker
|
||||
* Sharing of the established communication methods via mailing list, Slack, IRC
|
||||
or Discord
|
||||
* Sharing web services for web page or wikis and blog posts
|
||||
|
||||
If there is interest in a collaboration, please reach out to
|
||||
coreboot@coreboot.org.
|
||||
|
||||
The coreboot project is still responsible and in charge of its offered services
|
||||
to partner projects. The technical details of the collaboration will be
|
||||
discussed on a case-by-case basis with affected parties where coreboot project
|
||||
infrastructure maintainers have the lead.
|
||||
|
||||
Note that it is expected that everyone using the coreboot services is expected
|
||||
to follow coreboot code-of-conduct policies at all times. In cases where the
|
||||
coreboot CoC is broken by someone working only on other projects in the coreboot
|
||||
infrastructure, the coreboot leadership will work with the leadership of the
|
||||
other project on the issue.
|
||||
|
|
@ -7,5 +7,4 @@ Code of Conduct <code_of_conduct.md>
|
|||
Language style <language_style.md>
|
||||
Community forums <forums.md>
|
||||
coreboot at conferences <conferences.md>
|
||||
Cross-Project Collaboration <cross_project_collaboration.md>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -522,7 +522,7 @@ The preferred style for *long* (multi-line) comments is:
|
|||
|
||||
```c
|
||||
/*
|
||||
* This is the preferred style for long multi-line
|
||||
* This is the preferred style for multi-line
|
||||
* comments in the coreboot source code.
|
||||
* Please use it consistently.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -348,8 +348,8 @@ commit message itself:
|
|||
* Tested-by:
|
||||
* Reviewed-by:
|
||||
|
||||
The script `util/scripts/cross-repo-cherrypick` can be used to help
|
||||
automate this. Other tags such as 'Commit-Queue' can simply be removed.
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
# Git Commit messages
|
||||
|
||||
There are a number of different recommendations for git commit
|
||||
messages, so this is another one.
|
||||
|
||||
It's expected that coreboot contributors already generally understand
|
||||
the idea of writing good git commit messages, so this is not trying
|
||||
to go over everything again. There are other tutorials that cover that.
|
||||
|
||||
- [Linux Kernel tip tree Handbook](https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#patch-subject)
|
||||
- [How to write a Git Commit Message](https://cbea.ms/git-commit/)
|
||||
|
||||
## Line length
|
||||
|
||||
- The subject line should be <= 65 characters, with an absolute maximum
|
||||
of 72 characters
|
||||
- Prose in the commit message should be <= 72 characters
|
||||
- If reflowing prose to 72 characters can reduce the length of the
|
||||
commit message by 2 or more lines, please reflow it. Using the entire
|
||||
72 characters on a line when reasonable is recommended, but not
|
||||
required.
|
||||
- Non-prose text in the body in the commit message does not need to be
|
||||
wrapped at 72 characters. Examples: URLs, compiler output, or poetry.
|
||||
|
||||
## Both Subject & body
|
||||
|
||||
- Use the present tense. (The for loop exits too early, so ...", not
|
||||
"The for loop exited too early, so ...").
|
||||
- When using acronyms, make sure they're explained in the commit
|
||||
message itself or in the [acronyms list](https://doc.coreboot.org/acronyms.html).
|
||||
|
||||
## Commit message Subject Line
|
||||
|
||||
- Start the subject line with a prefix denoting the area of the change.
|
||||
Often part of the path can be used by that. Looking through existing
|
||||
commit messages summaries with `git log --oneline ...` gives a good
|
||||
idea. Because this prefix takes space used by the rest of the subject,
|
||||
it should be kept short while still uniquely describing the area.
|
||||
- Don't include `src/`
|
||||
- Use abbreviations where possible:
|
||||
- mb: mainboard
|
||||
- vc: vendorcode
|
||||
- Don't end the subject line with a period.
|
||||
- Use the imperative mood. ("Fix whitespace", not "whitespace fixes").
|
||||
|
||||
## Commit Message Body
|
||||
|
||||
- Make sure the problem being solved by the commit is described. While
|
||||
it may be obvious to the committer, it may not be obvious to others.
|
||||
- When referencing other commits use a 12 character hash and the subject
|
||||
(e.g. `commit XXXXXXXXXXXX ("some commit")`). However, use `CB:XXXXX`
|
||||
when referring to an open or abandoned change on Gerrit.
|
||||
- When using a URL in a commit message, use archive.org when possible.
|
||||
URLs often get changed or go stale, so this keeps them stable.
|
||||
- Make sure that all changes in a patch are addressed in the commit
|
||||
message.
|
||||
- A TEST= tag is highly recommended, but not required. This lets people
|
||||
know whether you tested it by booting the board or just by compiling.
|
||||
- All but the most trivial of patches should generally have a body.
|
||||
- A BUG= tag can be added when the author wants to indicate that the
|
||||
patch is or is not related to a bug. This can be either in coreboot's
|
||||
issue tracker, or a private issue tracker.
|
||||
- `BUG=b:####` is used by the Google internal issue tracker.
|
||||
- `BUG=chromium:####` indicates the Chromium public tracker at
|
||||
https://issues.chromium.org/
|
||||
- `BUG=CID ####` can be used to indicate coverity error fixes.
|
||||
- `BUG=https://...` can be used to link directly to a public
|
||||
tracker.
|
||||
- The BRANCH= tag is used in cases where a patch needs to be added to a
|
||||
specific downstream branch. This is never required by the coreboot
|
||||
project.
|
||||
|
||||
## Footers
|
||||
|
||||
- The Signed-off-by line is required (Jenkins forces this).
|
||||
- The Change ID is required (Gerrit forces this.)
|
||||
- When adding a patch that has already gone through another git or
|
||||
gerrit, the footers from those previous commits may be added, but
|
||||
keep the list reasonable.
|
||||
|
|
@ -5,25 +5,7 @@
|
|||
|
||||
Coding Style <coding_style.md>
|
||||
Gerrit Guidelines <gerrit_guidelines.md>
|
||||
Commit Message Guidelines <git_commit_messages.md>
|
||||
Project Ideas <project_ideas.md>
|
||||
Documentation Ideas <documentation_ideas.md>
|
||||
Google Summer of Code <gsoc.md>
|
||||
```
|
||||
|
||||
The coreboot project uses the Developer Certificate of Origin as its
|
||||
policy of accepting contributions. As such, a submitter bears the burden
|
||||
that they have all necessary rights to submit their contribution to the
|
||||
project under the project's licenses as appropriate. Violations of third
|
||||
party rights will lead to the code's removal or replacement as suitable
|
||||
to bring the project back into compliance.
|
||||
|
||||
This applies no matter under which circumstances a contribution has been
|
||||
created: legal frameworks, work contracts, Generative Artificial
|
||||
Intelligence ("AI") tooling, or other aspects that may affect the
|
||||
ownership and copyright status of a contribution are outside the
|
||||
project's control.
|
||||
|
||||
See the [Sign-off procedure] for more information.
|
||||
|
||||
[Sign-off procedure]: gerrit_guidelines.md#sign-off-procedure
|
||||
|
|
|
|||
|
|
@ -1,387 +0,0 @@
|
|||
# ACPI Active Cooling with Five-Level Fan Control
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes an ACPI-based thermal management pattern used across multiple coreboot mainboards. The implementation uses ACPI thermal zones with active cooling policies to control fan speed based on CPU temperature through a five-level power resource state machine.
|
||||
|
||||
This pattern is particularly prevalent on Intel-based mainboards using SuperIO environmental controllers for fan PWM control.
|
||||
|
||||
## Mainboards Using This Pattern
|
||||
|
||||
The following mainboards implement this five-level ACPI fan control pattern:
|
||||
|
||||
### Google Chromebooks
|
||||
- **google/beltino** - Haswell Chromebox
|
||||
- All variants (mccloud, monroe, panther, tricky, zako) use a single implementation
|
||||
- **google/jecht** - Broadwell Chromebox
|
||||
- Each variant (jecht, rikku, guado, tidus) has a unique implementation
|
||||
|
||||
### Samsung
|
||||
- **samsung/stumpy** - Sandy Bridge Chromebox
|
||||
|
||||
### Intel Reference Boards
|
||||
- **intel/wtm2** - Haswell ULT reference board
|
||||
- **intel/baskingridge** - Haswell desktop reference board
|
||||
- **intel/emeraldlake2** - Ivy Bridge reference board
|
||||
|
||||
## Architecture
|
||||
|
||||
### Hardware Components
|
||||
|
||||
Common hardware elements across implementations:
|
||||
|
||||
- **Temperature Sensor**: SuperIO TMPIN3 reads CPU temperature via PECI (Platform Environment Control Interface)
|
||||
- **Fan Controller**: SuperIO Environmental Controller (ENVC) with PWM output
|
||||
- Fan2 (F2PS) on Google Chromebooks
|
||||
- Fan3 (F3PS) on Intel/Samsung boards
|
||||
- **CPU**: Provides temperature data as an offset from Tj_max (maximum junction temperature)
|
||||
|
||||
### ACPI Components
|
||||
|
||||
- **Thermal Zone**: `\_TZ.THRM` - Main thermal management zone
|
||||
- **Fan Devices**: `FAN0` through `FAN4` - Five fan speed levels
|
||||
- **Power Resources**: `FNP0` through `FNP4` - Control fan state transitions
|
||||
- **Active Cooling Levels**: `_AC0` through `_AC4` - Temperature thresholds for each fan level
|
||||
|
||||
## Fan Speed Levels
|
||||
|
||||
The system implements **5 fan speed levels** (0-4), where:
|
||||
|
||||
- **Level 0**: Maximum fan speed (highest cooling, activated at highest temperature)
|
||||
- **Level 1**: High fan speed
|
||||
- **Level 2**: Medium fan speed
|
||||
- **Level 3**: Low fan speed
|
||||
- **Level 4**: Minimum fan speed (idle/baseline cooling, default state)
|
||||
|
||||
The system starts at **Level 4** (minimum speed) and increases to lower-numbered levels as temperature rises.
|
||||
|
||||
## Temperature Management
|
||||
|
||||
### Temperature Reading Process
|
||||
|
||||
1. **Raw PECI Reading**: Read from `\_SB.PCI0.LPCB.SIO.ENVC.TIN3`
|
||||
- Returns a value representing offset from Tj_max
|
||||
- Value 0x80 indicates "no reading available"
|
||||
- Values 0 or 255 are invalid
|
||||
|
||||
2. **Temperature Calculation**:
|
||||
```
|
||||
actual_temperature = Tj_max - (255 - raw_value)
|
||||
```
|
||||
|
||||
3. **Conversion to ACPI Format**: Convert from Celsius to deci-Kelvin:
|
||||
```
|
||||
deci_kelvin = (celsius * 10) + 2732
|
||||
```
|
||||
|
||||
### Critical Temperature Handling
|
||||
|
||||
Most implementations include safety logic in the `_TMP` method:
|
||||
- If temperature reaches `\TMAX` (Tj_max), logs a critical event
|
||||
- Waits 1 second for sensor re-poll
|
||||
- Re-reads temperature to confirm
|
||||
- Reports the current temperature to the OS
|
||||
|
||||
### Temperature Thresholds
|
||||
|
||||
Thresholds are defined in board-specific headers (typically `thermal.h`):
|
||||
|
||||
- `FAN0_THRESHOLD_ON/OFF` - Trigger points for maximum fan speed
|
||||
- `FAN1_THRESHOLD_ON/OFF` - Trigger points for high fan speed
|
||||
- `FAN2_THRESHOLD_ON/OFF` - Trigger points for medium fan speed
|
||||
- `FAN3_THRESHOLD_ON/OFF` - Trigger points for low fan speed
|
||||
- `FAN4_PWM` - PWM value for minimum fan speed
|
||||
|
||||
Each level has hysteresis (different ON/OFF thresholds) to prevent rapid cycling.
|
||||
|
||||
## Active Cooling Implementation
|
||||
|
||||
### Active Cooling Methods (`_ACx`)
|
||||
|
||||
Each `_ACx` method returns a temperature threshold:
|
||||
- When system temperature **exceeds** the threshold, the OS activates the corresponding fan level
|
||||
- When temperature **falls below** the threshold, the OS may deactivate that level
|
||||
|
||||
**Hysteresis Logic**:
|
||||
```
|
||||
Method (_AC0) {
|
||||
If (\FLVL <= 0) {
|
||||
Return (CTOK (FAN0_THRESHOLD_OFF)) // Higher temp to turn off
|
||||
} Else {
|
||||
Return (CTOK (FAN0_THRESHOLD_ON)) // Lower temp to turn on
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This prevents oscillation by requiring different temperatures to activate vs. deactivate a fan level.
|
||||
|
||||
### Active Cooling Lists (`_ALx`)
|
||||
|
||||
Each `_ALx` associates a cooling threshold with a fan device:
|
||||
```
|
||||
Name (_AL0, Package () { FAN0 }) // FAN0 activated at _AC0 threshold
|
||||
Name (_AL1, Package () { FAN1 }) // FAN1 activated at _AC1 threshold
|
||||
Name (_AL2, Package () { FAN2 }) // FAN2 activated at _AC2 threshold
|
||||
Name (_AL3, Package () { FAN3 }) // FAN3 activated at _AC3 threshold
|
||||
Name (_AL4, Package () { FAN4 }) // FAN4 activated at _AC4 threshold
|
||||
```
|
||||
|
||||
## Power Resource State Machine
|
||||
|
||||
Each fan level has an associated power resource (`FNP0` through `FNP4`) that manages state transitions.
|
||||
|
||||
### State Transitions
|
||||
|
||||
**FNP0-FNP3** (Levels 0-3):
|
||||
- `_STA`: Returns 1 (ON) if `\FLVL <= level`, else 0 (OFF)
|
||||
- `_ON`: Transitions **to** this level from a higher-numbered level
|
||||
- `_OFF`: Transitions **away** from this level to the next higher-numbered level
|
||||
|
||||
Example for FNP0 (maximum cooling):
|
||||
```
|
||||
PowerResource (FNP0, 0, 0) {
|
||||
Method (_STA) {
|
||||
If (\FLVL <= 0) { Return (1) } // Active if at max cooling
|
||||
Else { Return (0) }
|
||||
}
|
||||
Method (_ON) {
|
||||
If (!_STA ()) { // If not already active
|
||||
\FLVL = 0 // Set to max cooling
|
||||
\_SB.PCI0.LPCB.SIO.ENVC.F2PS = FAN0_PWM // Set fan PWM
|
||||
Notify (\_TZ.THRM, 0x81) // Notify thermal zone
|
||||
}
|
||||
}
|
||||
Method (_OFF) {
|
||||
If (_STA ()) { // If currently active
|
||||
\FLVL = 1 // Transition to level 1
|
||||
\_SB.PCI0.LPCB.SIO.ENVC.F2PS = FAN1_PWM // Set corresponding PWM
|
||||
Notify (\_TZ.THRM, 0x81) // Notify thermal zone
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**FNP4** (Minimum cooling - Level 4):
|
||||
- `_STA`: Returns 1 if `\FLVL <= 4` (always true in normal operation)
|
||||
- `_ON`: Transitions to minimum cooling state
|
||||
- `_OFF`: **No-op** - This is the minimum state, cannot transition lower
|
||||
|
||||
### Critical: FNP4._OFF Must Be a No-Op
|
||||
|
||||
This is **essential for Windows compatibility**:
|
||||
|
||||
**Problem**: Early implementations had `_OFF` setting `\FLVL = 4` and PWM values, identical to `_ON`. This violated ACPI power resource requirements:
|
||||
- After `_ON` is called, `_STA` must eventually return 1 (ON)
|
||||
- After `_OFF` is called, `_STA` must eventually return 0 (OFF)
|
||||
|
||||
Since both methods resulted in `\FLVL = 4`, and `_STA` returns 1 when `\FLVL <= 4`, the power resource could never properly transition to OFF state.
|
||||
|
||||
**Solution**: Make `_OFF` a no-op since FAN4 is the minimum cooling state:
|
||||
```
|
||||
PowerResource (FNP4, 0, 0) {
|
||||
Method (_STA) {
|
||||
If (\FLVL <= 4) { Return (1) }
|
||||
Else { Return (0) }
|
||||
}
|
||||
Method (_ON) {
|
||||
If (!_STA ()) {
|
||||
\FLVL = 4
|
||||
\_SB.PCI0.LPCB.SIO.ENVC.F2PS = FAN4_PWM
|
||||
Notify (\_TZ.THRM, 0x81)
|
||||
}
|
||||
}
|
||||
Method (_OFF) {
|
||||
// FAN4 is the minimum cooling state (idle/lowest fan speed)
|
||||
// There is no lower state to transition to, so _OFF is a no-op
|
||||
// to maintain proper ACPI power resource state machine semantics
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This maintains proper ACPI state machine semantics and ensures Windows compatibility while maintaining Linux compatibility.
|
||||
|
||||
## Passive Cooling
|
||||
|
||||
In addition to active (fan-based) cooling, most implementations support passive cooling:
|
||||
|
||||
- `_PSV`: Returns passive cooling threshold temperature
|
||||
- `_PSL`: Returns list of processors to throttle
|
||||
- `_TC1`, `_TC2`: Thermal constants for passive cooling algorithm
|
||||
- `_TSP`: Thermal sampling period (typically 20 deciseconds = 2 seconds)
|
||||
|
||||
When temperature exceeds `_PSV` threshold, the OS throttles CPUs listed in `_PSL` to reduce heat generation.
|
||||
|
||||
## Polling and Notification
|
||||
|
||||
- `_TZP`: Thermal zone polling period (typically 100 deciseconds = 10 seconds)
|
||||
- OS polls `_TMP` at this interval to check temperature
|
||||
|
||||
- `Notify (\_TZ.THRM, 0x81)`: Thermal zone change notification
|
||||
- Sent whenever fan level changes
|
||||
- Tells OS to re-evaluate thermal zone immediately
|
||||
|
||||
## Initialization
|
||||
|
||||
The `_INI` method runs when the thermal zone is initialized:
|
||||
|
||||
```
|
||||
Method (_INI) {
|
||||
\FLVL = 4 // Start at minimum cooling
|
||||
\_SB.PCI0.LPCB.SIO.ENVC.F2PS = FAN4_PWM // Set initial fan PWM
|
||||
Notify (\_TZ.THRM, 0x81) // Initial notification
|
||||
}
|
||||
```
|
||||
|
||||
## Operating System Interaction
|
||||
|
||||
### Thermal Policy Flow
|
||||
|
||||
1. **OS boots** → Executes `_INI` → Fan starts at Level 4
|
||||
2. **OS polls `_TMP`** periodically → Gets current temperature
|
||||
3. **Temperature rises** → Exceeds `_AC3` threshold
|
||||
4. **OS calls `FAN3._ON`** → Power resource FNP3 activated → `\FLVL = 3`
|
||||
5. **Temperature continues rising** → Exceeds `_AC2` threshold
|
||||
6. **OS calls `FAN2._ON`** → Power resource FNP2 activated → `\FLVL = 2`
|
||||
7. **Temperature drops** → Falls below `_AC2` threshold
|
||||
8. **OS calls `FAN2._OFF`** → `\FLVL = 3` → Returns to Level 3
|
||||
9. **Cycle continues** based on temperature changes
|
||||
|
||||
### Critical Temperature
|
||||
|
||||
If temperature reaches `_CRT` threshold:
|
||||
- OS initiates emergency shutdown
|
||||
- Prevents hardware damage from overheating
|
||||
|
||||
## Global Variables
|
||||
|
||||
Standard variables used across implementations:
|
||||
|
||||
- `\FLVL`: Current fan level (0-4)
|
||||
- `\TMAX`: Maximum junction temperature (Tj_max)
|
||||
- `\TCRT`: Critical shutdown temperature
|
||||
- `\TPSV`: Passive cooling threshold temperature
|
||||
|
||||
## Configuration
|
||||
|
||||
Fan thresholds and PWM values are defined in board-specific headers, typically:
|
||||
```
|
||||
src/mainboard/<vendor>/<board>/include/thermal.h
|
||||
```
|
||||
or
|
||||
```
|
||||
src/mainboard/<vendor>/<board>/variants/<variant>/include/variant/thermal.h
|
||||
```
|
||||
|
||||
Example configuration:
|
||||
```c
|
||||
#define FAN0_THRESHOLD_ON 75 // Temperature to activate max fan (°C)
|
||||
#define FAN0_THRESHOLD_OFF 65 // Temperature to deactivate max fan (°C)
|
||||
#define FAN0_PWM 0xFF // PWM duty cycle value (max)
|
||||
|
||||
#define FAN1_THRESHOLD_ON 65
|
||||
#define FAN1_THRESHOLD_OFF 55
|
||||
#define FAN1_PWM 0xC0
|
||||
|
||||
#define FAN2_THRESHOLD_ON 55
|
||||
#define FAN2_THRESHOLD_OFF 45
|
||||
#define FAN2_PWM 0x80
|
||||
|
||||
#define FAN3_THRESHOLD_ON 45
|
||||
#define FAN3_THRESHOLD_OFF 35
|
||||
#define FAN3_PWM 0x40
|
||||
|
||||
#define FAN4_PWM 0x20 // Idle fan speed
|
||||
```
|
||||
|
||||
## Implementation Variations
|
||||
|
||||
While the core pattern is consistent, there are some variations:
|
||||
|
||||
### PWM Output Selection
|
||||
- **Google boards**: Use Fan2 PWM (`F2PS`)
|
||||
- **Intel/Samsung boards**: Use Fan3 PWM (`F3PS`)
|
||||
|
||||
### Guard Checks
|
||||
Some implementations wrap state changes with `_STA()` checks:
|
||||
```
|
||||
Method (_ON) {
|
||||
If (!_STA ()) { // Only change state if not already active
|
||||
// ... state change
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Others omit the guard and always perform the state change.
|
||||
|
||||
### Temperature Reading
|
||||
- Most implementations read from SuperIO TMPIN3 via PECI
|
||||
- Some (like intel/wtm2) use simplified stub implementations for reference
|
||||
|
||||
### Dynamic Thermal Tables
|
||||
The google/jecht/tidus variant includes multiple thermal tables that can be switched based on system temperature sensors, allowing more sophisticated thermal management.
|
||||
|
||||
## Compatibility Notes
|
||||
|
||||
### Linux
|
||||
- More lenient ACPI parser
|
||||
- Tolerates minor state machine violations
|
||||
- Worked with buggy FNP4._OFF implementations
|
||||
|
||||
### Windows
|
||||
- Stricter ACPI compliance checking
|
||||
- Requires proper power resource state machine behavior
|
||||
- **Requires the FNP4._OFF no-op fix** to function correctly
|
||||
- May disable thermal zone entirely if ACPI violations detected
|
||||
|
||||
## Debugging
|
||||
|
||||
To debug fan control issues:
|
||||
|
||||
1. **Check ACPI errors**: Look for thermal zone errors in OS logs
|
||||
- Linux: `dmesg | grep -i acpi` or check `/sys/class/thermal/`
|
||||
- Windows: Event Viewer → System → ACPI errors
|
||||
|
||||
2. **Monitor temperature**: Use OS tools to check `_TMP` readings
|
||||
- Linux: `/sys/class/thermal/thermal_zone*/temp`
|
||||
- Windows: HWiNFO64, HWMonitor
|
||||
|
||||
3. **Check fan level**: Monitor `\FLVL` value (ACPI debugger or custom logging)
|
||||
|
||||
4. **Verify thresholds**: Ensure threshold values are appropriate for the hardware
|
||||
|
||||
5. **Test state transitions**: Verify each fan level activates at correct temperature
|
||||
|
||||
6. **ACPI table inspection**: Decompile DSDT/SSDT tables with `acpidump` and `iasl` to verify implementation
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
When implementing this pattern on a new board:
|
||||
|
||||
- [ ] Define all 5 fan threshold pairs (ON/OFF) with appropriate hysteresis
|
||||
- [ ] Define PWM values for all 5 fan levels
|
||||
- [ ] Implement temperature sensor reading (typically PECI via SuperIO)
|
||||
- [ ] Implement CTOK conversion method (°C to deci-Kelvin)
|
||||
- [ ] Create all 5 PowerResource objects (FNP0-FNP4)
|
||||
- [ ] **Critical**: Ensure FNP4._OFF is a no-op (not setting state)
|
||||
- [ ] Create all 5 Fan Device objects (FAN0-FAN4) with correct `_PR0` references
|
||||
- [ ] Implement _ACx methods with hysteresis logic
|
||||
- [ ] Define _ALx packages linking to fan devices
|
||||
- [ ] Implement _INI to set initial state
|
||||
- [ ] Implement _TMP with error handling
|
||||
- [ ] Define _CRT, _PSV, _PSL for critical/passive cooling
|
||||
- [ ] Set appropriate _TZP polling interval
|
||||
- [ ] Test on both Linux and Windows
|
||||
|
||||
## References
|
||||
|
||||
- [ACPI Specification 6.5 - Thermal Management](https://uefi.org/specs/ACPI/6.5/)
|
||||
- [ACPI Specification - ThermalZone Objects](https://uefi.org/specs/ACPI/6.5/11_Thermal_Management.html)
|
||||
- [ACPI Specification - PowerResource Objects](https://uefi.org/specs/ACPI/6.5/07_Power_and_Performance_Mgmt.html#power-resources)
|
||||
- Intel PECI Specification
|
||||
- SuperIO vendor datasheets (ITE, Nuvoton, Winbond, etc.)
|
||||
|
||||
## See Also
|
||||
|
||||
- [Intel DPTF](dptf.md) - More sophisticated Intel Dynamic Platform and Thermal Framework
|
||||
- [ACPI GPIO Documentation](../acpi/gpio.md)
|
||||
|
||||
|
|
@ -23,50 +23,6 @@ In both cases you have to add `C` structs in ramstage to describe the
|
|||
option and group them together into a form. CFR objects should reside
|
||||
on the heap as they can be modified to match the current boot flow.
|
||||
|
||||
### Overriding default values
|
||||
|
||||
Mainboards often want to reuse CFR objects defined in SoC or common code
|
||||
but with different default values. Rather than duplicating the entire object
|
||||
definition, mainboards can declare an override table that maps option names
|
||||
to new default values.
|
||||
|
||||
**Example:** Override defaults for several SoC-defined options:
|
||||
|
||||
```
|
||||
#include <drivers/option/cfr_frontend.h>
|
||||
#include <intelblocks/pcie_rp.h>
|
||||
|
||||
const struct cfr_default_override mb_cfr_overrides[] = {
|
||||
CFR_OVERRIDE_BOOL("s0ix_enable", false),
|
||||
CFR_OVERRIDE_ENUM("pciexp_aspm", ASPM_DISABLE),
|
||||
CFR_OVERRIDE_NUMBER("igd_dvmt", 64),
|
||||
CFR_OVERRIDE_END
|
||||
};
|
||||
|
||||
void mb_cfr_setup_menu(struct lb_cfr *cfr_root)
|
||||
{
|
||||
/* Register overrides before writing menu */
|
||||
cfr_register_overrides(mb_cfr_overrides);
|
||||
cfr_write_setup_menu(cfr_root, sm_root);
|
||||
}
|
||||
```
|
||||
|
||||
When the CFR system writes the setup menu, it will check the override table
|
||||
for each option and use the override value if one exists. All other object
|
||||
metadata (name, help text, enum values, flags) comes from the original object.
|
||||
|
||||
The following helper macros are available to populate the table:
|
||||
|
||||
- `CFR_OVERRIDE_BOOL(name, value)`
|
||||
- `CFR_OVERRIDE_ENUM(name, value)`
|
||||
- `CFR_OVERRIDE_NUMBER(name, value)`
|
||||
- `CFR_OVERRIDE_VARCHAR(name, value)`
|
||||
- `CFR_OVERRIDE_END`
|
||||
|
||||
Each macro encodes the override type, and the CFR backend validates that the
|
||||
override type matches the original object's type. If the types do not match,
|
||||
the override is ignored and a warning is printed.
|
||||
|
||||
### Updating CFR options
|
||||
|
||||
The CFR options should be updated before tables are written.
|
||||
|
|
@ -78,7 +34,7 @@ coreboot table.
|
|||
EMI eeprom.
|
||||
|
||||
```
|
||||
static void update_serial(struct sm_object *new)
|
||||
static void update_serial(const struct sm_object *obj, struct sm_object *new)
|
||||
{
|
||||
new->sm_varchar.default_value = get_emi_eeprom_vpd()->serial_number;
|
||||
}
|
||||
|
|
@ -94,9 +50,8 @@ static const struct sm_object serial_number = SM_DECLARE_VARCHAR({
|
|||
|
||||
The CFR options can have a dependency that must be evaluated at runtime by
|
||||
the OS/payload that parses the CFR record and displays the UI.
|
||||
By using the `WITH_DEP()` macro you can specify another numeric option that
|
||||
is checked to hide the current option. The `WITH_DEP_VALUES()` macro allows
|
||||
specifying one or more values that cause the dependent option to be displayed.
|
||||
By using the `WITH_DEP()` macro you can specify another numberic option that
|
||||
is checked to hide the current option.
|
||||
|
||||
**Example:** Declares a dependency from `sata_disable_port0` to `sata_enable`.
|
||||
The option `sata_disable_port0` will be hidden as long as "sata_enable" is 0.
|
||||
|
|
@ -121,37 +76,6 @@ static struct sm_object sata_disable_port0 = SM_DECLARE_BOOL({
|
|||
}, WITH_DEP(&sata_enable));
|
||||
```
|
||||
|
||||
**Example:** Declares a dependency from `com1_termination` to `com1_mode`.
|
||||
The option `com1_termination` will only be shown if `com1_mode` is set to RS-485.
|
||||
|
||||
```
|
||||
#define COM_MODE_DISABLED 3
|
||||
#define COM_MODE_RS232 0
|
||||
#define COM_MODE_RS485 1
|
||||
|
||||
static struct sm_object com1_mode = SM_DECLARE_ENUM({
|
||||
.flags = CFR_OPTFLAG_RUNTIME,
|
||||
.opt_name = "com1_mode",
|
||||
.ui_name = "COM1 Mode",
|
||||
.ui_helptext = NULL,
|
||||
.default_value = 1,
|
||||
.values = (const struct sm_enum_value[]) {
|
||||
{ "Disabled", COM_MODE_DISABLED },
|
||||
{ "RS-232", COM_MODE_RS232 },
|
||||
{ "RS-485", COM_MODE_RS485 },
|
||||
SM_ENUM_VALUE_END },
|
||||
});
|
||||
|
||||
static struct sm_object com1_termination = SM_DECLARE_BOOL({
|
||||
.flags = CFR_OPTFLAG_RUNTIME,
|
||||
.opt_name = "com1_termination",
|
||||
.ui_name = "Enable COM1 termination resistors",
|
||||
.ui_helptext = NULL,
|
||||
.default_value = false,
|
||||
}, WITH_DEP_VALUES(&com1_mode, COM_MODE_RS485));
|
||||
|
||||
```
|
||||
|
||||
### Providing mainboard custom options
|
||||
|
||||
A mainboard that uses CFR can provide a list of custom options
|
||||
|
|
@ -187,4 +111,4 @@ static const __cfr_form struct sm_obj_form southbridge = {
|
|||
NULL
|
||||
},
|
||||
};
|
||||
```
|
||||
```
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
# Generating signed UEFI capsules with EDK2
|
||||
|
||||
coreboot can cooperate with an EDK2 payload to support firmware updates via the UEFI
|
||||
ESRT/FMP capsule mechanism.
|
||||
|
||||
This document covers generating a *signed* capsule during the coreboot build.
|
||||
|
||||
At present, capsule generation requires a compatible EDK2 tree with the
|
||||
corresponding payload-side changes. Upstream support is being tracked in:
|
||||
|
||||
https://github.com/tianocore/edk2/pull/12053
|
||||
|
||||
Older EDK2 trees may be missing pieces required by this integration.
|
||||
|
||||
## Build-time capsule generation
|
||||
|
||||
Enable capsule support and use an EDK2 payload:
|
||||
|
||||
- `CONFIG_DRIVERS_EFI_UPDATE_CAPSULES`: enable coreboot capsule update support.
|
||||
- `CONFIG_DRIVERS_EFI_GENERATE_CAPSULE`: generate `build/coreboot.cap` after the ROM is finalised.
|
||||
- `CONFIG_PAYLOAD_EDK2`: build an EDK2 payload.
|
||||
|
||||
When enabled, the coreboot build generates `build/coreboot.cap` after the ROM image is
|
||||
finalised. The capsule can also be generated explicitly with `make capsule`.
|
||||
|
||||
Configure the FMAP allowlist embedded into the ROM as a manifest:
|
||||
|
||||
- `CONFIG_DRIVERS_EFI_CAPSULE_REGIONS`: whitespace-separated FMAP region allowlist embedded into
|
||||
the ROM as a manifest (e.g. `COREBOOT EC`).
|
||||
|
||||
Configure the ESRT/FMP firmware identity used by the capsule:
|
||||
|
||||
- `CONFIG_DRIVERS_EFI_MAIN_FW_GUID`: GUID of the firmware
|
||||
- `CONFIG_DRIVERS_EFI_MAIN_FW_VERSION`: firmware version encoded in the capsule header;
|
||||
if set to `0`, derive a value from the leading `<major>.<minor>` in
|
||||
`CONFIG_LOCALVERSION` when possible
|
||||
- `CONFIG_DRIVERS_EFI_MAIN_FW_LSV`: lowest supported firmware version; if set to `0`,
|
||||
use the resolved firmware version
|
||||
|
||||
Reset behavior during capsule application:
|
||||
|
||||
- `CONFIG_DRIVERS_EFI_CAPSULE_INITIATE_RESET`: add the capsule `InitiateReset` flag.
|
||||
This is disabled by default because Linux rejects capsules with `InitiateReset` when using
|
||||
`/dev/efi_capsule_loader`.
|
||||
|
||||
## Embedded drivers (FmpDxe in capsule)
|
||||
|
||||
Some EDK2 capsule update flows use an embedded `FmpDxe.efi` driver inside the capsule.
|
||||
|
||||
To generate capsules with an embedded `FmpDxe.efi`, enable:
|
||||
|
||||
- `CONFIG_DRIVERS_EFI_CAPSULE_EMBED_FMP_DXE`: embed `FmpDxe.efi` into generated capsules.
|
||||
- `CONFIG_DRIVERS_EFI_CAPSULE_ACCEPT_EMBEDDED_DRIVERS`: configure the EDK2 payload to accept
|
||||
capsules with embedded drivers (sets `PcdCapsuleEmbeddedDriverSupport=TRUE`).
|
||||
|
||||
Note: if Secure Boot is enabled, the embedded driver must be signed by a key trusted by the
|
||||
running firmware, otherwise capsule processing may fail when loading the embedded driver.
|
||||
|
||||
## Capsule signing certificates
|
||||
|
||||
`GenerateCapsule` can sign the FMP payload (PKCS#7). Many platforms require signed capsules.
|
||||
|
||||
coreboot exposes three Kconfig options for the certificate chain:
|
||||
|
||||
- `CONFIG_DRIVERS_EFI_CAPSULE_SIGNER_PRIVATE_CERT`: PEM containing the signing private key and
|
||||
leaf certificate
|
||||
- `CONFIG_DRIVERS_EFI_CAPSULE_OTHER_PUBLIC_CERT`: PEM intermediate certificate
|
||||
- `CONFIG_DRIVERS_EFI_CAPSULE_TRUSTED_PUBLIC_CERT`: PEM trusted root certificate
|
||||
|
||||
If a configured path is relative, it is interpreted relative to the configured EDK2 repository
|
||||
inside `payloads/external/edk2/workspace`.
|
||||
|
||||
The defaults use the EDK2 BaseTools test certificate chain. Do not use the test keys for
|
||||
production firmware updates.
|
||||
|
||||
To generate your own certificate chain and convert it into the required PEM files, see:
|
||||
`BaseTools/Source/Python/Pkcs7Sign/Readme.md` in the EDK2 tree.
|
||||
|
|
@ -18,16 +18,13 @@ Some of the drivers currently available include:
|
|||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
ACPI Five-Level Fan Control <acpi_fan_control.md>
|
||||
CFR <cfr.md>
|
||||
CFR use within coreboot <cfr_internal.md>
|
||||
Intel DPTF <dptf.md>
|
||||
IPMI BT (Block Transfer) <ipmi_bt.md>
|
||||
IPMI KCS <ipmi_kcs.md>
|
||||
SMMSTORE <smmstore.md>
|
||||
SMMSTOREv2 <smmstorev2.md>
|
||||
SoundWire <soundwire.md>
|
||||
USB4 Retimer <retimer.md>
|
||||
CBFS SMBIOS hooks <cbfs_smbios.md>
|
||||
EDK2 capsule generation <efi_capsule_generation.md>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
# IPMI Block Transfer (BT) driver
|
||||
|
||||
The driver can be found in `src/drivers/ipmi/` (same as KCS). It works with BMC
|
||||
that provides a BT I/O interface as specified in the [IPMI] standard. See
|
||||
"Intelligent Platform Management Interface Specification", v2.0, Rev. 1.1 for
|
||||
more details on the interface and IPMI in general.
|
||||
|
||||
The driver detects the IPMI version and reserves the I/O space in coreboot's
|
||||
resource allocator.
|
||||
|
||||
## For developers
|
||||
|
||||
To use the driver, select the `IPMI_BT` Kconfig and add the following PNP
|
||||
device (in example for the BT at 0xe4):
|
||||
|
||||
```
|
||||
chip drivers/ipmi
|
||||
device pnp e4.0 on end # IPMI BT
|
||||
end
|
||||
```
|
||||
|
||||
**Note:** The I/O base address must be aligned to 4.
|
||||
|
||||
The following settings can be set in a device tree:
|
||||
|
||||
```{eval-rst}
|
||||
+------------------+--------------+-------------------------------------------+
|
||||
| Setting | Type/Default | Description/Purpose |
|
||||
+==================+==============+===========================================+
|
||||
| wait_for_bmc | | Boolean | Wait for BMC to boot. This can be used if |
|
||||
| | | false | the BMC takes a long time to boot after |
|
||||
| | | PoR. |
|
||||
+------------------+--------------+-------------------------------------------+
|
||||
| bmc_boot_timeout | | Integer | The timeout in seconds to wait for the |
|
||||
| | | 0 | IPMI service to be loaded. Will be used |
|
||||
| | | if wait_for_bmc is true. |
|
||||
+------------------+--------------+-------------------------------------------+
|
||||
```
|
||||
|
||||
## Debugging/testing the driver
|
||||
|
||||
`ipmi_sim` from [OpenIPMI] project can be used by running `ipmi_sim -d` in one
|
||||
console to watch what's being sent/received and starting QEMU like this in
|
||||
another console:
|
||||
|
||||
```
|
||||
qemu-system-x86_64 \
|
||||
-M q35,smm=on \
|
||||
-bios build/coreboot.rom \
|
||||
-chardev socket,id=ipmichr0,host=localhost,port=9002,reconnect=10 \
|
||||
-device ipmi-bmc-extern,chardev=ipmichr0,id=bmc0 \
|
||||
-device isa-ipmi-bt,bmc=bmc0,irq=0 \
|
||||
-serial stdio
|
||||
```
|
||||
|
||||
A simpler alternative is to use QEMU's builtin BMC simulator:
|
||||
|
||||
```
|
||||
qemu-system-x86_64 \
|
||||
-M q35,smm=on \
|
||||
-bios build/coreboot.rom \
|
||||
-device ipmi-bmc-sim,id=bmc0 \
|
||||
-device isa-ipmi-bt,bmc=bmc0,irq=0 \
|
||||
-serial stdio
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
Useful links on the subject:
|
||||
* README of `ipmi_sim`:
|
||||
<https://github.com/wrouesnel/openipmi/blob/master/lanserv/README.ipmi_sim>
|
||||
* slides about OpenIPMI:
|
||||
<https://www.linux-kvm.org/images/7/76/03x08-Juniper-Corey_Minyard-UsingIPMIinQEMU.ods.pdf>
|
||||
* a usage example: <https://github.com/dhilst/qemu-ipmi>
|
||||
* old docs (the options are still there, but no longer have a dedicated page in
|
||||
modern documentation): <https://hskinnemoen.github.io/qemu/specs/ipmi.html>
|
||||
|
||||
[IPMI]: https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf
|
||||
[OpenIPMI]: https://github.com/wrouesnel/openipmi
|
||||
|
|
@ -74,30 +74,19 @@ has to read the coreboot table with tag `0x0039`, containing:
|
|||
struct lb_smmstorev2 {
|
||||
uint32_t tag;
|
||||
uint32_t size;
|
||||
uint32_t num_blocks; /* Number of writable blocks in SMM */
|
||||
uint32_t block_size; /* Size of a block in byte. Default: 64 KiB */
|
||||
uint32_t mmap_addr_deprecated; /* 32-bit MMIO address of the store for read only access.
|
||||
Prefer 'mmap_addr' for new software.
|
||||
Zero when the address won't fit into 32-bits. */
|
||||
uint32_t com_buffer; /* Physical address of the communication buffer */
|
||||
uint32_t com_buffer_size; /* Size of the communication buffer in bytes */
|
||||
uint8_t apm_cmd; /* The command byte to write to the APM I/O port */
|
||||
uint8_t unused[3]; /* Set to zero */
|
||||
uint64_t mmap_addr; /* 64-bit MMIO address of the store for read only access.
|
||||
Introduced after the initial implementation. Users of
|
||||
this table must check the 'size' field to detect if its
|
||||
written out by coreboot. */
|
||||
uint32_t num_blocks; /* Number of writeable blocks in SMM */
|
||||
uint32_t block_size; /* Size of a block in byte. Default: 64 KiB */
|
||||
uint32_t mmap_addr; /* MMIO address of the store for read only access */
|
||||
uint32_t com_buffer; /* Physical address of the communication buffer */
|
||||
uint32_t com_buffer_size; /* Size of the communication buffer in byte */
|
||||
uint8_t apm_cmd; /* The command byte to write to the APM I/O port */
|
||||
uint8_t unused[3]; /* Set to zero */
|
||||
};
|
||||
```
|
||||
|
||||
The absence of this coreboot table entry indicates that there's no
|
||||
SMMSTOREv2 support.
|
||||
|
||||
`mmap_addr` is an optional field added after the initial implementation.
|
||||
Users of this table must check the size field to know if it's written by coreboot.
|
||||
In case it's not present 'mmap_addr_deprecated' is to be used as the SPI ROM MMIO
|
||||
address and it must be below 4 GiB.
|
||||
|
||||
### Blocks
|
||||
|
||||
The SMMSTOREv2 splits the SMMSTORE FMAP partition into smaller chunks
|
||||
|
|
@ -208,45 +197,6 @@ coreboot tables, there's no risk that a malicious application capable
|
|||
of issuing SMIs could extract arbitrary data or modify the currently
|
||||
running kernel.
|
||||
|
||||
## Capsule update API
|
||||
|
||||
Availability of this command is tied to `CONFIG_DRIVERS_EFI_UPDATE_CAPSULES`.
|
||||
|
||||
To allow updating full flash content (except if locked at hardware
|
||||
level), few new calls were added. They reuse communication buffer, SMI
|
||||
command, return values and calling arguments of SMMSTORE commands listed
|
||||
above, with the exception of subcommand passed via `%ah`. If the
|
||||
subcommand is to operate on full flash size, it has the highest bit set,
|
||||
e.g. it is `0x85` for `SMMSTORE_CMD_RAW_READ` and `0x86` for
|
||||
`SMMSTORE_CMD_RAW_WRITE`. Every `block_id` describes block relative to
|
||||
the beginning of a flash, maximum value depends on its size.
|
||||
|
||||
Attempts to write the protected memory regions can lead to undesired
|
||||
consequences ranging from system instability to bricking and security
|
||||
vulnerabilities. When this feature is used, care must be taken to temporarily
|
||||
lift protections for the duration of an update when the whole flash is
|
||||
rewritten or the update must be constrained to affect only writable portions of
|
||||
the flash (e.g., "BIOS" region).
|
||||
|
||||
There is one new subcommand that must be called before any other subcommands
|
||||
with highest bit set can be used.
|
||||
|
||||
### - SMMSTORE_CMD_USE_FULL_FLASH = 0x80
|
||||
|
||||
This command can only be executed once and is done by the firmware.
|
||||
Calling this function at runtime has no effect. It takes one additional
|
||||
parameter that, contrary to other commands, isn't a pointer. Instead,
|
||||
`%ebx` indicates requested state of full flash access. If it equals 0,
|
||||
commands for accessing full flash are permanently disabled, otherwise
|
||||
they are permanently enabled until the next boot.
|
||||
|
||||
The assumption is that if capsule updates are enabled at build time and
|
||||
whole flash access is enabled at runtime, a UEFI payload (highly likely
|
||||
EDK2 or its derivative) won't allow a regular OS to boot if the handler is
|
||||
enabled without rebooting first. There could be a way of deactivating the
|
||||
handler, but coreboot, having no way of enforcing its usage, might as well
|
||||
permit access until a reboot and rely on the payload to do the right thing.
|
||||
|
||||
## External links
|
||||
|
||||
* [A Tour Beyond BIOS Implementing UEFI Authenticated Variables in SMM with EDK II](https://github.com/tianocore-docs/Docs/raw/master/White_Papers/A_Tour_Beyond_BIOS_Implementing_UEFI_Authenticated_Variables_in_SMM_with_EDKII_V2.pdf)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ The bootblock loads the romstage or the verstage if verified boot is enabled.
|
|||
|
||||
### Cache-As-Ram
|
||||
The *Cache-As-Ram*, also called Non-Eviction mode, or *CAR* allows to use the
|
||||
CPU cache like regular SRAM. This is particularly useful for high level
|
||||
CPU cache like regular SRAM. This is particullary useful for high level
|
||||
languages like `C`, which need RAM for heap and stack.
|
||||
|
||||
The CAR needs to be activated using vendor specific CPU instructions.
|
||||
|
|
|
|||
|
|
@ -75,7 +75,9 @@ $(call add_intermediate, add_mrc_data)
|
|||
|
||||
Note that the second line must start with a tab, not spaces.
|
||||
|
||||
See also <project:../tutorial/managing_local_additions.md>.
|
||||
```{eval-rst}
|
||||
See also :doc:`../tutorial/managing_local_additions`.
|
||||
```
|
||||
|
||||
#### FMAP region support
|
||||
With the addition of FMAP flash partitioning support to coreboot, there was a
|
||||
|
|
|
|||
|
|
@ -1,361 +0,0 @@
|
|||
# CBMEM high table memory manager
|
||||
|
||||
## Introduction
|
||||
|
||||
CBMEM (coreboot memory) is a dynamic memory infrastructure used in
|
||||
coreboot to store runtime data structures, logs, and other information
|
||||
that needs to persist across different boot stages, and even across warm
|
||||
boots. CBMEM is crucial for maintaining state and information through
|
||||
the coreboot boot process.
|
||||
|
||||
Its key responsibilities include:
|
||||
- Providing a stable storage area for critical boot data
|
||||
- Managing console logging
|
||||
- Storing configuration tables for hand off to payloads
|
||||
- Maintaining timestamps for performance analysis
|
||||
- Preserving boot state during S3 suspend/resume cycles
|
||||
- Storing data such as ACPI tables, SMBIOS tables and coreboot tables
|
||||
which are used at runtime
|
||||
|
||||
|
||||
## Creation and Placement
|
||||
|
||||
CBMEM is initialized by coreboot during romstage, but is used mainly in
|
||||
ramstage for storing any data that needs to be saved for more than a
|
||||
short period of time.
|
||||
|
||||
For 32-bit builds, CBMEM is typically located at the highest usable DRAM
|
||||
address below the 4GiB boundary. For 64-bit builds, while there is no
|
||||
strict upper limit, it is advisable to follow the same guidelines to
|
||||
prevent access or addressing issues. Regardless of the build type, the
|
||||
CBMEM address must remain consistent between romstage and ramstage.
|
||||
|
||||
Each platform may need to implement its own method for determining the
|
||||
`cbmem_top` address, as this can depend on specific hardware
|
||||
configurations and memory layouts.
|
||||
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Each CBMEM region is identified by a unique ID, allowing different
|
||||
components to store and retrieve their data during runtime.
|
||||
|
||||
Upon creating an entry, a block of memory of the requested size is
|
||||
allocated from the reserved CBMEM region. This region typically persists
|
||||
across warm reboots, making it useful for debugging and passing
|
||||
information to payloads.
|
||||
|
||||
CBMEM is implemented as a specialized in-memory database (IMD) system
|
||||
that grows downward from a defined upper memory limit. This means that
|
||||
only the latest added entry may be removed.
|
||||
|
||||
```text
|
||||
High Memory
|
||||
+----------------------+ <- cbmem_top
|
||||
| Root Pointer |
|
||||
|----------------------|
|
||||
| Root Structure |
|
||||
|----------------------|
|
||||
| Entry 1 |
|
||||
|----------------------|
|
||||
| Entry 2 |
|
||||
|----------------------|
|
||||
| ... |
|
||||
| (grows downward) |
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
|
||||
### Core Components
|
||||
|
||||
The CBMEM system consists of several key components:
|
||||
|
||||
1. **Root Pointer**: Located at the very top of CBMEM memory space,
|
||||
contains a magic number and offset to the Root Structure
|
||||
|
||||
2. **Root Structure**: Contains metadata about the CBMEM region,
|
||||
including:
|
||||
- Entry alignment requirements
|
||||
- Maximum number of entries
|
||||
- Current number of entries
|
||||
- Address of the lowest allocated memory
|
||||
|
||||
3. **Entries**: Individual allocations within CBMEM, identified by:
|
||||
- 32-bit ID (often encoding ASCII characters for readability)
|
||||
- Size information
|
||||
- Magic validation value
|
||||
|
||||
4. **IMD Implementation**: The underlying memory management system
|
||||
that handles allocation, deallocation, and entry management
|
||||
|
||||
|
||||
## Memory Layout and Initialization
|
||||
|
||||
CBMEM is positioned at the top of available system RAM, typically
|
||||
just below the 4GiB boundary for 32-bit builds. This placement
|
||||
ensures compatibility with legacy code while allowing CBMEM to
|
||||
retain its contents across warm resets.
|
||||
|
||||
|
||||
### CBMEM Location Determination
|
||||
|
||||
Each platform must implement a `cbmem_top_chipset()` function
|
||||
that returns the physical address where CBMEM should be located.
|
||||
This address must be consistent across coreboot stages and is
|
||||
determined based on:
|
||||
|
||||
- Available physical memory
|
||||
- Architecture-specific constraints
|
||||
- BIOS/chipset requirements
|
||||
|
||||
|
||||
### Initialization Process
|
||||
|
||||
CBMEM is initialized in a multi-step process:
|
||||
|
||||
1. **Early Initialization**: A small console buffer is created in during
|
||||
bootblock and romstage
|
||||
|
||||
2. **RAM Initialization**: The full CBMEM area is established in
|
||||
ramstage
|
||||
|
||||
3. **Normal Operation**:
|
||||
- In a normal boot, CBMEM is created fresh
|
||||
- Size and alignment values are specified
|
||||
- Initial entries are allocated
|
||||
|
||||
4. **Recovery Operation**:
|
||||
- During S3 resume, CBMEM structure is recovered from memory
|
||||
- Content is validated using magic numbers and checksums
|
||||
- All existing entries remain accessible
|
||||
|
||||
|
||||
## Entry Management
|
||||
|
||||
CBMEM entries are identified by a 32-bit ID, often encoding ASCII
|
||||
characters to indicate their purpose (for example, "CNSL" for console).
|
||||
|
||||
### Entry Creation
|
||||
|
||||
```c
|
||||
void *cbmem_add(u32 id, u64 size);
|
||||
```
|
||||
|
||||
This function:
|
||||
- Searches for an existing entry with the given ID
|
||||
- If found, returns the existing entry (size is ignored)
|
||||
- If not found, allocates a new entry of the requested size
|
||||
- Returns a pointer to the entry's data area
|
||||
|
||||
|
||||
### Entry Access
|
||||
|
||||
```c
|
||||
void *cbmem_find(u32 id);
|
||||
```
|
||||
|
||||
This function:
|
||||
- Searches for an entry with the specified ID
|
||||
- Returns a pointer to the entry's data area if found
|
||||
- Returns NULL if the entry does not exist
|
||||
|
||||
|
||||
### Entry Removal
|
||||
|
||||
```c
|
||||
int cbmem_entry_remove(const struct cbmem_entry *entry);
|
||||
```
|
||||
|
||||
This function:
|
||||
- Removes the specified entry if it was the last one added
|
||||
- Returns 0 on success, negative value on failure
|
||||
- Note: Due to the downward-growing design, only the most recently
|
||||
added entry can be removed
|
||||
|
||||
|
||||
## CBMEM Console Implementation
|
||||
|
||||
The CBMEM console is a circular buffer used for capturing log messages
|
||||
across all boot stages. It is one of the most widely used CBMEM
|
||||
features. The size of this buffer is determined by the
|
||||
`CONFIG_CBMEM_CONSOLE_SIZE` Kconfig option.
|
||||
|
||||
|
||||
### Console Structure
|
||||
|
||||
```c
|
||||
struct cbmem_console {
|
||||
u32 size; // Size of the buffer
|
||||
u32 cursor; // Current write position and flags
|
||||
u8 body[]; // Actual data buffer
|
||||
};
|
||||
```
|
||||
|
||||
Key features:
|
||||
- The high bit of `cursor` indicates overflow condition
|
||||
- Only the lower 28 bits of `cursor` are used as position
|
||||
- Supports ring-buffer operation when full
|
||||
|
||||
|
||||
### Console Operation
|
||||
|
||||
1. **Initialization**: A console entry is created in CBMEM with ID
|
||||
`CBMEM_ID_CONSOLE` (0x434f4e53 - 'CONS')
|
||||
|
||||
2. **Writing**: Log messages are written byte-by-byte using
|
||||
`cbmemc_tx_byte()` which:
|
||||
- Adds data to the current cursor position
|
||||
- Advances the cursor
|
||||
- Sets the overflow flag when wrapping around
|
||||
|
||||
3. **Stage Transition**: When transitioning between boot stages:
|
||||
- Pre-RAM console contents are copied to the main CBMEM console
|
||||
- Any overflow condition is preserved and noted
|
||||
- The process ensures no log messages are lost
|
||||
|
||||
|
||||
## Common CBMEM Entry Types
|
||||
|
||||
CBMEM contains various data structures used by different coreboot
|
||||
components:
|
||||
|
||||
- **Console**: Log messages from all boot stages (`CBMEM_ID_CONSOLE`)
|
||||
- **Timestamps**: Performance metrics (`CBMEM_ID_TIMESTAMP`)
|
||||
- **ACPI Tables**: ACPI data for OS handoff (`CBMEM_ID_ACPI`)
|
||||
- **coreboot Tables**: System information (`CBMEM_ID_CBTABLE`)
|
||||
- **Memory Information**: RAM configuration (`CBMEM_ID_MEMINFO`)
|
||||
- **Stage Cache**: Code/data for faster S3 resume
|
||||
- **ROM/CBFS Cache**: Cached ROM content
|
||||
- **Vendor-specific**: Platform-dependent structures
|
||||
|
||||
A complete list of IDs is defined in
|
||||
`src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h`.
|
||||
|
||||
|
||||
## Integration with Boot Stages
|
||||
|
||||
CBMEM interacts differently with each coreboot boot stage.
|
||||
|
||||
|
||||
### Bootblock/Romstage
|
||||
|
||||
- Uses cache-as-RAM for temporary console storage
|
||||
- Limited CBMEM functionality before RAM initialization
|
||||
- Sets up initial timestamp entries
|
||||
|
||||
|
||||
### Ramstage
|
||||
|
||||
- Full CBMEM initialization or recovery
|
||||
- All entries become accessible
|
||||
- Most coreboot subsystems interact with CBMEM
|
||||
- Console logging is fully operational
|
||||
|
||||
|
||||
### Payload Handoff
|
||||
|
||||
- CBMEM contents are preserved when transferring to the payload
|
||||
- Entries are made available via coreboot tables
|
||||
- Common payloads (SeaBIOS, GRUB, etc.) can access CBMEM data
|
||||
|
||||
|
||||
## Platform-Specific Considerations
|
||||
|
||||
Different platforms have unique requirements for CBMEM implementation.
|
||||
|
||||
|
||||
### x86 Platforms
|
||||
|
||||
- CBMEM typically located just below 4GiB
|
||||
- Often integrates with ACPI resume and SMM operations
|
||||
- May need to accommodate memory reserved by legacy components
|
||||
|
||||
|
||||
### ARM/RISC-V Platforms
|
||||
|
||||
- More flexibility in CBMEM placement
|
||||
- Must coordinate with platform-specific memory controllers
|
||||
- Memory topology can vary significantly between implementations
|
||||
|
||||
|
||||
## CBMEM Hooks
|
||||
|
||||
CBMEM provides a hook mechanism to allow subsystems to perform
|
||||
initialization or recovery operations when CBMEM becomes available:
|
||||
|
||||
```c
|
||||
CBMEM_CREATION_HOOK(hook_function); // First-time creation only
|
||||
CBMEM_READY_HOOK(hook_function); // Any CBMEM initialization
|
||||
CBMEM_READY_HOOK_EARLY(hook_function); // Early CBMEM initialization
|
||||
```
|
||||
|
||||
These macros register functions to be called when CBMEM is initialized,
|
||||
allowing components to set up their CBMEM entries at the appropriate time.
|
||||
|
||||
|
||||
## Debugging and Utilities
|
||||
|
||||
### CBMEM Utility
|
||||
|
||||
The `cbmem` utility provides direct access to CBMEM contents on a
|
||||
running system. It needs to be built from the coreboot source tree using
|
||||
`make -C util/cbmem`. Common uses include:
|
||||
|
||||
- Listing all CBMEM entries (`cbmem -l`)
|
||||
- Viewing console logs (`cbmem -c`)
|
||||
- Analyzing timestamps (`cbmem -t`)
|
||||
- Extracting specific entries by ID (`cbmem -e <ID>`)
|
||||
|
||||
|
||||
### Debugging Techniques
|
||||
|
||||
- CBMEM console contents can be dumped to UART for debugging if serial
|
||||
output is enabled.
|
||||
- The console overflow flag (`cbmem -c` output) helps identify if logs
|
||||
were truncated.
|
||||
- Size validation within CBMEM helps detect potential memory corruption.
|
||||
- Magic numbers provide integrity validation for CBMEM structures.
|
||||
- Enabling `CONFIG_CBMEM_CHECKS` in Kconfig adds extra sanity checks
|
||||
that can help catch issues during development.
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
While CBMEM is a powerful tool, it has some inherent limitations:
|
||||
|
||||
- **Downward Growth**: The stack-like allocation (growing downwards)
|
||||
means that only the most recently added entry can be removed. This
|
||||
prevents fragmentation but limits flexibility in freeing memory.
|
||||
- **Fixed Size**: Once CBMEM is initialized in ramstage, its total size
|
||||
and top address (`cbmem_top`) are fixed. Entries cannot be resized
|
||||
after allocation.
|
||||
- **Platform Complexity**: Determining the correct `cbmem_top` can be
|
||||
complex on some platforms due to varying memory maps and reserved
|
||||
regions.
|
||||
|
||||
|
||||
## Best Practices for Developers
|
||||
|
||||
When working with the CBMEM subsystem:
|
||||
|
||||
1. **Alignment**: Always respect the alignment requirements of the
|
||||
CBMEM tier (`IMD_ROOT` vs `IMD_SMALL`) you are using.
|
||||
|
||||
2. **ID Selection**: Use unique, meaningful IDs for new entries,
|
||||
preferably encoding ASCII characters for readability (see
|
||||
`cbmem_id.h`).
|
||||
|
||||
3. **Size Estimation**: Allocate sufficient space initially, as
|
||||
entries cannot be resized.
|
||||
|
||||
4. **Memory Conservation**: Be mindful of limited memory resources,
|
||||
especially on constrained platforms. Avoid storing excessively large
|
||||
data structures in CBMEM unless necessary.
|
||||
|
||||
5. **Persistence**: Remember that CBMEM contents persist across
|
||||
warm reboots (like S3 resume) but not across full system resets
|
||||
(cold boots).
|
||||
|
||||
6. **Entry Ordering**: Consider that only the most recently added
|
||||
entry can be removed, which might influence your allocation strategy
|
||||
if you anticipate needing to free space.
|
||||
|
|
@ -10,6 +10,5 @@ Kconfig <kconfig.md>
|
|||
Writing Documentation <writing_documentation.md>
|
||||
Setting up GPIOs <gpio.md>
|
||||
Adding devices to a device tree <devicetree.md>
|
||||
CBMEM <cbmem.md>
|
||||
Frequently Asked Questions <faq.md>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -14,13 +14,18 @@ coreboot uses [Sphinx] documentation tool. We prefer the markdown format
|
|||
over reStructuredText so only embedded ReST is supported. Checkout the
|
||||
[Markdown Guide] for more information.
|
||||
|
||||
### Option 1: Use the docker image
|
||||
### option 1: Use the docker image
|
||||
|
||||
The easiest way to build the documentation is using a docker image.
|
||||
To build the image run the following in the base directory:
|
||||
|
||||
make -C util/docker/ doc.coreboot.org
|
||||
|
||||
Before building the documentation make sure the output directory is given
|
||||
the correct permissions before running docker.
|
||||
|
||||
mkdir -p Documentation/_build
|
||||
|
||||
To build the documentation:
|
||||
|
||||
make -C util/docker docker-build-docs
|
||||
|
|
@ -31,29 +36,30 @@ To have the documentation build and served over a web server live run:
|
|||
|
||||
On the host machine, open a browser to the address <http://0.0.0.0:8000>.
|
||||
|
||||
### Option 2: Install Sphinx
|
||||
### option 2: Install Sphinx
|
||||
|
||||
Please follow this official [guide] to install sphinx. You will also need
|
||||
myst-parser for sphinx to be able to handle markdown documentation.
|
||||
Please follow this official [guide] to install sphinx.
|
||||
You will also need python-recommonmark for sphinx to be able to handle
|
||||
markdown documentation.
|
||||
|
||||
Since some Linux distributions don't package every needed sphinx extension,
|
||||
the installation via pip in a venv is recommended. You'll need these python3
|
||||
modules:
|
||||
|
||||
* sphinx
|
||||
* myst-parser
|
||||
* sphinx-rtd-theme
|
||||
* recommonmark
|
||||
* sphinx_rtd_theme
|
||||
|
||||
The following combination of versions has been tested: sphinx 8.1.3,
|
||||
myst-parser 4.0.0, and sphinx-rtd-theme 2.0.0.
|
||||
The following combination of versions has been tested: sphinx 2.3.1,
|
||||
recommonmark 0.6.0, and sphinx_rtd_theme 0.4.3.
|
||||
|
||||
Now change into the `Documentation` folder in the coreboot directory and run
|
||||
this command in there
|
||||
|
||||
make
|
||||
make sphinx
|
||||
|
||||
If no error occurs, you can find the generated HTML documentation in
|
||||
`Documentation/_build/html` now.
|
||||
`Documentation/_build` now.
|
||||
|
||||
### Optional
|
||||
|
||||
|
|
@ -82,17 +88,15 @@ Documentation:
|
|||
12. Shouldn't cover implementation details; for details, the code is the
|
||||
reference.
|
||||
|
||||
## Referencing markdown documents
|
||||
|
||||
Starting with Sphinx 1.6 recommonmark's *auto_doc_ref* feature is broken.
|
||||
To reference documents use the TOC tree or inline RST code.
|
||||
|
||||
## Markdown and Tables
|
||||
|
||||
Markdown tables are supported:
|
||||
|
||||
| Header 1 | Header 2 | Header 3 |
|
||||
|------------|-----------|-----------|
|
||||
| body row 1 | column 2 | column 3 |
|
||||
| body row 2 | column 2 | column 3 |
|
||||
|
||||
Tables can also be written using embedded reStructured Text, which provides
|
||||
additional features like the ability to merge cells:
|
||||
Under Sphinx markdown tables are not supported. Therefore you can use following
|
||||
code block to write tables in reStructuredText and embed them into the markdown:
|
||||
|
||||
```{eval-rst}
|
||||
+------------+------------+-----------+
|
||||
|
|
@ -113,20 +117,22 @@ additional features like the ability to merge cells:
|
|||
To make sure that all documents are included into the final documentation, you
|
||||
must reference each document from at least one *toctree*. The *toctree* must
|
||||
only reference files in the same folder or in subfolders !
|
||||
To create a toctree, you must use the following syntax to invoke the
|
||||
Sphinx toctree directive:
|
||||
To create a toctree, simply use a bullet list or numbered list with a single
|
||||
reference. References in regular text aren't considered as *toctree* .
|
||||
This feature is enabled by recommonmark's *enable_auto_toc_tree* .
|
||||
|
||||
**Example toctree:**
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
```
|
||||
* [Chapter 1](chapter1.md)
|
||||
* [Chapter 2](chapter2.md)
|
||||
* [Subchapter](sub/index.md)
|
||||
```
|
||||
|
||||
Chapter 1 <chapter1.md>
|
||||
Chapter 2 <chapter2.md>
|
||||
Subchapter <sub/index.md>
|
||||
```
|
||||
|
||||
References in regular text aren't considered as *toctree* .
|
||||
```
|
||||
1. [Chapter 1](chapter1.md)
|
||||
2. [Chapter 2](chapter2.md)
|
||||
```
|
||||
|
||||
If you do only reference the document, but do not include it in any toctree,
|
||||
you'll see the following warning:
|
||||
|
|
@ -146,9 +152,11 @@ readable tables, using the following reStructuredText snipped:
|
|||
Of course this can only be done from a markdown file that is included in the
|
||||
TOC tree.
|
||||
|
||||
[sphinx-autobuild]: https://github.com/sphinx-doc/sphinx-autobuild
|
||||
[guide]: https://www.sphinx-doc.org/en/master/usage/installation.html
|
||||
[Sphinx]: https://www.sphinx-doc.org/en/master/
|
||||
[coreboot]: https://coreboot.org
|
||||
[Documentation]: https://review.coreboot.org/cgit/coreboot.git/tree/Documentation
|
||||
[sphinx-autobuild]: https://github.com/GaretJax/sphinx-autobuild
|
||||
[guide]: http://www.sphinx-doc.org/en/stable/install.html
|
||||
[Sphinx]: http://www.sphinx-doc.org/en/master/
|
||||
[Markdown Guide]: https://www.markdownguide.org/
|
||||
[Gerrit Guidelines]: ../contributing/gerrit_guidelines.md
|
||||
[review.coreboot.org]: https://review.coreboot.org
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ GMA: Framebuffer Configuration
|
|||
*coreboot* supports two different framebuffer setups. The default
|
||||
enables the legacy VGA plane in textmode. Due to legacy hardware
|
||||
constraints, only the first found display is enabled in this mode.
|
||||
(cf. `src/drivers/intel/gma/text_fb/gma-gfx_init.adb`).
|
||||
(cf. `src/drivers/intel/gma/text_fb/gma.adb`).
|
||||
|
||||
The second option sets up a high-resolution framebuffer with the
|
||||
native resolution of the display if only one is detected, or the
|
||||
smallest of all resolutions (per dimension) if multiple displays
|
||||
are detected. This option is selected by
|
||||
`CONFIG_FRAMEBUFFER_KEEP_VESA_MODE`.
|
||||
(cf. `src/drivers/intel/gma/hires_fb/gma-gfx_init.adb`).
|
||||
(cf. `src/drivers/intel/gma/hires_fb/gma.adb`).
|
||||
|
||||
In any case, a smaller framebuffer is up-scaled to each display's
|
||||
native resolution while keeping aspect ratio.
|
||||
|
|
|
|||
|
|
@ -219,7 +219,6 @@ Community <community/index.md>
|
|||
Payloads <payloads.md>
|
||||
Distributions <distributions.md>
|
||||
Technotes <technotes/index.md>
|
||||
Internal APIs & Configuration <internals/index.md>
|
||||
ACPI <acpi/index.md>
|
||||
Native Graphics Initialization with libgfxinit <gfx/libgfxinit.md>
|
||||
Display panel <gfx/display-panel.md>
|
||||
|
|
|
|||
|
|
@ -1,537 +0,0 @@
|
|||
# The `chip_operations` Structure in coreboot
|
||||
|
||||
## Introduction
|
||||
|
||||
The `chip_operations` structure is a fundamental component of coreboot's
|
||||
chipset abstraction layer. It provides a standardized interface for chipset-
|
||||
specific code to interact with coreboot's device initialization framework.
|
||||
This structure enables coreboot to support a wide variety of chipsets
|
||||
while maintaining a consistent initialization flow across different hardware
|
||||
platforms.
|
||||
|
||||
In coreboot's architecture, a "chip" refers to a collection of hardware
|
||||
components that form a logical unit, such as a System-on-Chip (SoC),
|
||||
a CPU, or a distinct southbridge/northbridge. The `chip_operations`
|
||||
structure provides the hooks necessary for coreboot to discover, configure,
|
||||
and initialize these components during the boot process.
|
||||
|
||||
The `chip_operations` structure is particularly crucial for the ramstage
|
||||
portion of coreboot, where it connects the static device tree definitions
|
||||
with the actual hardware initialization code. It serves as the bridge
|
||||
between the declarative device descriptions and the imperative code that
|
||||
brings those devices to life.
|
||||
|
||||
|
||||
## Structure Definition
|
||||
|
||||
The `chip_operations` structure is defined in `src/include/device/device.h`
|
||||
as follows:
|
||||
|
||||
```c
|
||||
struct chip_operations {
|
||||
void (*enable_dev)(struct device *dev);
|
||||
void (*init)(void *chip_info);
|
||||
void (*final)(void *chip_info);
|
||||
unsigned int initialized : 1;
|
||||
unsigned int finalized : 1;
|
||||
const char *name;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Field Descriptions
|
||||
|
||||
- **enable_dev**: A function pointer that takes a `struct device*`
|
||||
parameter. This function is called for each device associated with the
|
||||
chip during the device enumeration phase (specifically, within the
|
||||
`scan_bus` operations triggered by `dev_enumerate`). Its primary
|
||||
purpose is to set up device operations (`dev->ops`) based on the
|
||||
device's role in the system.
|
||||
|
||||
- **init**: A function pointer that takes a `void*` parameter pointing to
|
||||
the chip's configuration data (typically cast to a chip-specific struct).
|
||||
This function is called during the chip initialization phase
|
||||
(`BS_DEV_INIT_CHIPS`), before device enumeration. It usually performs
|
||||
early hardware setup needed before individual devices can be configured.
|
||||
|
||||
- **final**: A function pointer that takes a `void*` parameter pointing to
|
||||
the chip's configuration data (typically cast to a chip-specific struct).
|
||||
This function is called during the final table writing phase of coreboot
|
||||
initialization (`BS_WRITE_TABLES`), after all devices have been
|
||||
initialized. It performs any necessary cleanup or late initialization
|
||||
operations.
|
||||
|
||||
- **initialized**: A bit flag indicating whether the chip's init function
|
||||
has been called.
|
||||
|
||||
- **finalized**: A bit flag indicating whether the chip's final function
|
||||
has been called.
|
||||
|
||||
- **name**: A string containing the human-readable name of the chip, used
|
||||
for debugging and logging purposes.
|
||||
|
||||
|
||||
## Initialization Sequence and `chip_operations`
|
||||
|
||||
The `chip_operations` structure integrates with coreboot's boot state
|
||||
machine, which is defined in `src/lib/hardwaremain.c`. The functions in
|
||||
this structure are called at specific points during the boot process:
|
||||
|
||||
1. **BS_DEV_INIT_CHIPS** state: The `init` function is called for each
|
||||
chip in the device tree. This is handled by `dev_initialize_chips()`
|
||||
which iterates through all devices, identifies unique chip instances,
|
||||
and invokes their `init` functions.
|
||||
|
||||
2. **BS_DEV_ENUMERATE** state: During the execution of this state,
|
||||
`dev_enumerate()` is called, which triggers bus scanning
|
||||
(e.g., `pci_scan_bus`). Within these scan routines, the `enable_dev`
|
||||
function is called for devices associated with a chip. This commonly
|
||||
assigns the appropriate `device_operations` structure to each device
|
||||
based on its type and purpose.
|
||||
|
||||
3. **BS_WRITE_TABLES** state: The `final` function is called for each
|
||||
chip by `dev_finalize_chips()` after all devices have been initialized
|
||||
and just before payloads are loaded.
|
||||
|
||||
This sequence ensures that chips can perform necessary setup before their
|
||||
individual devices are configured, and also perform cleanup or finalization
|
||||
after all devices have been initialized but before the final tables are
|
||||
written and the payload is executed.
|
||||
|
||||
|
||||
## Relationship Between `chip_operations` and `device_operations`
|
||||
|
||||
It's important to understand the distinction and relationship between
|
||||
`chip_operations` and `device_operations`:
|
||||
|
||||
- **chip_operations**: Operates at the chipset or SoC level, providing
|
||||
hooks for chip-wide initialization. It's responsible for the overall
|
||||
setup of a collection of devices that belong to the same logical chip.
|
||||
|
||||
- **device_operations**: Operates at the individual device level,
|
||||
providing functions to manage specific devices within a chip. These
|
||||
operations include resource allocation, device initialization, and device-
|
||||
specific functionality.
|
||||
|
||||
The key relationship is that `chip_operations.enable_dev` is typically
|
||||
responsible for assigning the appropriate `device_operations` structure
|
||||
to each device based on its type and function. This is where the bridge
|
||||
between the chip-level and device-level abstractions occurs.
|
||||
|
||||
For example, a typical implementation of the `enable_dev` function might
|
||||
look like this:
|
||||
|
||||
```c
|
||||
static void soc_enable(struct device *dev)
|
||||
{
|
||||
if (dev->path.type == DEVICE_PATH_DOMAIN)
|
||||
dev->ops = &pci_domain_ops;
|
||||
else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER)
|
||||
dev->ops = &cpu_bus_ops;
|
||||
else if (dev->path.type == DEVICE_PATH_GPIO)
|
||||
block_gpio_enable(dev);
|
||||
else if (dev->path.type == DEVICE_PATH_PCI &&
|
||||
dev->path.pci.devfn == PCH_DEVFN_PMC)
|
||||
dev->ops = &pmc_ops;
|
||||
}
|
||||
```
|
||||
|
||||
This function examines each device's path type and assigns the appropriate
|
||||
operations based on the device's role in the system.
|
||||
|
||||
|
||||
## Integration with the Devicetree
|
||||
|
||||
The `chip_operations` structure is tightly integrated with coreboot's
|
||||
devicetree mechanism. The devicetree is a hierarchical description of the
|
||||
hardware platform, defined in `.cb` files (typically `chipset.cb`,
|
||||
`devicetree.cb`, and optionally `overridetree.cb`).
|
||||
|
||||
In the devicetree, a `chip` directive starts a collection of devices
|
||||
associated with a particular chip driver. The path specified with the
|
||||
`chip` directive corresponds to a directory in the coreboot source tree
|
||||
that contains the chip driver code, including a `chip.c` file that defines
|
||||
the `chip_operations` structure for that chip.
|
||||
|
||||
For example, a devicetree might contain:
|
||||
|
||||
```
|
||||
chip soc/intel/cannonlake
|
||||
device domain 0 on
|
||||
device pci 00.0 on end # Host Bridge
|
||||
device pci 12.0 on end # Thermal Subsystem
|
||||
# ... more devices ...
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
This connects the devices under this chip directive with the
|
||||
`chip_operations` structure defined in
|
||||
`src/soc/intel/cannonlake/chip.c`:
|
||||
|
||||
```c
|
||||
struct chip_operations soc_intel_cannonlake_ops = {
|
||||
.name = "Intel Cannonlake",
|
||||
.enable_dev = &soc_enable,
|
||||
.init = &soc_init_pre_device,
|
||||
};
|
||||
```
|
||||
|
||||
During coreboot's build process, the `sconfig` utility processes the
|
||||
devicetree files and generates code that links the devices defined in the
|
||||
devicetree with their corresponding `chip_operations` structures.
|
||||
|
||||
|
||||
## Chip Configuration Data
|
||||
|
||||
Each chip typically defines a configuration structure in a `chip.h` file
|
||||
within its source directory. This structure contains configuration settings
|
||||
that can be specified in the devicetree using `register` directives.
|
||||
|
||||
For example, a chip might define a configuration structure like:
|
||||
|
||||
```c
|
||||
/* In src/soc/intel/cannonlake/chip.h */
|
||||
struct soc_intel_cannonlake_config {
|
||||
uint8_t pcie_rp_aspm[CONFIG_MAX_ROOT_PORTS];
|
||||
uint8_t usb2_ports[16];
|
||||
uint8_t usb3_ports[10];
|
||||
/* ... more configuration options ... */
|
||||
};
|
||||
```
|
||||
|
||||
In the devicetree, you would configure these options using register
|
||||
directives:
|
||||
|
||||
```
|
||||
chip soc/intel/cannonlake
|
||||
register "pcie_rp_aspm[0]" = "ASPM_AUTO"
|
||||
register "usb2_ports[5]" = "USB2_PORT_MID(OC_SKIP)"
|
||||
# ... more register settings ...
|
||||
|
||||
device domain 0 on
|
||||
# ... devices ...
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
These configuration values are made available to the chip's `init` and
|
||||
`final` functions through the `chip_info` parameter, which points to
|
||||
an instance of the chip's configuration structure (after appropriate
|
||||
casting from `void *`).
|
||||
|
||||
|
||||
## Implementation Examples
|
||||
|
||||
### Minimal Implementation
|
||||
|
||||
Some chips may not need extensive initialization and can provide a
|
||||
minimal implementation of the `chip_operations` structure:
|
||||
|
||||
```c
|
||||
struct chip_operations soc_ucb_riscv_ops = {
|
||||
.name = "UCB RISC-V",
|
||||
};
|
||||
```
|
||||
|
||||
This implementation only provides a name for debugging purposes but
|
||||
doesn't define any initialization functions.
|
||||
|
||||
|
||||
### Basic Implementation with Initialization
|
||||
|
||||
A more typical implementation includes at least initialization hooks:
|
||||
|
||||
```c
|
||||
struct chip_operations soc_amd_genoa_poc_ops = {
|
||||
.name = "AMD Genoa SoC Proof of Concept",
|
||||
.init = soc_init,
|
||||
.final = soc_final,
|
||||
};
|
||||
```
|
||||
|
||||
The `init` function might perform chip-wide initialization:
|
||||
|
||||
```c
|
||||
static void soc_init(void *chip_info)
|
||||
{
|
||||
default_dev_ops_root.write_acpi_tables = soc_acpi_write_tables;
|
||||
amd_opensil_silicon_init();
|
||||
data_fabric_print_mmio_conf();
|
||||
fch_init(chip_info);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Complete Implementation
|
||||
|
||||
A complete implementation includes all three function pointers:
|
||||
|
||||
```c
|
||||
struct chip_operations soc_intel_xeon_sp_cpx_ops = {
|
||||
.name = "Intel Cooper Lake-SP",
|
||||
.enable_dev = chip_enable_dev,
|
||||
.init = chip_init,
|
||||
.final = chip_final,
|
||||
};
|
||||
```
|
||||
|
||||
The `enable_dev` function would typically assign device operations
|
||||
based on device types:
|
||||
|
||||
```c
|
||||
static void chip_enable_dev(struct device *dev)
|
||||
{
|
||||
/* PCI root complex */
|
||||
if (dev->path.type == DEVICE_PATH_DOMAIN)
|
||||
dev->ops = &pci_domain_ops;
|
||||
/* CPU cluster */
|
||||
else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER)
|
||||
dev->ops = &cpu_cluster_ops;
|
||||
/* PCIe root ports */
|
||||
else if (dev->path.type == DEVICE_PATH_PCI &&
|
||||
PCI_SLOT(dev->path.pci.devfn) == PCIE_PORT1_SLOT)
|
||||
dev->ops = &pcie_rp_ops;
|
||||
/* ... other device types ... */
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Mainboard Implementation
|
||||
|
||||
It's also common for the mainboard-specific code (e.g.,
|
||||
`src/mainboard/vendor/board/mainboard.c`) to define its own
|
||||
`chip_operations`, often named `mainboard_ops`. The `mainboard_ops.init`
|
||||
can perform early board-level setup, and `mainboard_ops.enable_dev` can
|
||||
assign operations for devices specific to the mainboard or set default
|
||||
operations.
|
||||
|
||||
```c
|
||||
/* Example from src/mainboard/google/zork/mainboard.c */
|
||||
struct chip_operations mainboard_ops = {
|
||||
.enable_dev = mainboard_enable,
|
||||
.init = mainboard_init,
|
||||
.final = mainboard_final,
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Device Registration and Discovery
|
||||
|
||||
The `chip_operations` structure plays a key role in device registration
|
||||
and discovery within coreboot. Here's how it fits into this process:
|
||||
|
||||
1. **Static Device Definition**: Devices are statically defined in the
|
||||
devicetree files (`chipset.cb`, `devicetree.cb`, `overridetree.cb`).
|
||||
|
||||
2. **Code Generation**: The `sconfig` utility processes these files and
|
||||
generates code in `build/static.c` that creates the device structures
|
||||
and links them to their corresponding chip configuration data.
|
||||
|
||||
3. **Chip Initialization**: During the `BS_DEV_INIT_CHIPS` boot state,
|
||||
`dev_initialize_chips()` calls each chip's `init` function to perform
|
||||
chip-wide setup.
|
||||
|
||||
4. **Device Enumeration and Enabling**: During the `BS_DEV_ENUMERATE`
|
||||
boot state, `dev_enumerate()` initiates bus scanning. The scan
|
||||
functions call the associated chip's `enable_dev` function for each
|
||||
device, which assigns the appropriate device operations (`dev->ops`).
|
||||
|
||||
5. **Device Configuration and Initialization**: Subsequent boot states
|
||||
(`BS_DEV_RESOURCES`, `BS_DEV_ENABLE`, `BS_DEV_INIT`) configure and
|
||||
initialize the devices according to their assigned device operations.
|
||||
|
||||
6. **Chip Finalization**: After all devices have been initialized,
|
||||
`dev_finalize_chips()` calls each chip's `final` function during the
|
||||
`BS_WRITE_TABLES` boot state.
|
||||
|
||||
|
||||
## Build Process Integration
|
||||
|
||||
The `chip_operations` structures are integrated into the coreboot build
|
||||
process through several mechanisms:
|
||||
|
||||
1. **Devicetree Processing**: The `sconfig` utility processes the
|
||||
devicetree files and generates code that creates and links the device
|
||||
structures.
|
||||
|
||||
2. **Static Structure Declaration**: Each chip (and often the mainboard)
|
||||
defines its `chip_operations` structure in its respective `.c` file.
|
||||
These structures are collected during the build process.
|
||||
|
||||
3. **External References**: The generated code in `build/static.c`
|
||||
includes external references to these `chip_operations` structures.
|
||||
|
||||
4. **Linking**: The linker collects all the `chip_operations` structures
|
||||
and includes them in the final firmware image.
|
||||
|
||||
This process ensures that the appropriate chip operations are available
|
||||
during the boot process for each chip included in the devicetree.
|
||||
|
||||
|
||||
## Best Practices for Implementing `chip_operations`
|
||||
|
||||
When implementing the `chip_operations` structure for a new chip,
|
||||
follow these best practices:
|
||||
|
||||
1. **Provide a Meaningful Name**: The `name` field should be descriptive
|
||||
and identify the chip clearly for debugging purposes.
|
||||
|
||||
2. **Implement `enable_dev` Correctly**: The `enable_dev` function should
|
||||
assign the appropriate device operations based on device types and
|
||||
functions. It should handle all device types that might be part of the chip.
|
||||
Consider interactions with the mainboard `enable_dev`.
|
||||
|
||||
3. **Use Configuration Data**: The `init` and `final` functions should
|
||||
make use of the chip configuration data passed via the `chip_info`
|
||||
parameter (casting it to the correct type) to configure the chip
|
||||
according to the settings specified in the devicetree.
|
||||
|
||||
4. **Minimize Dependencies**: The `init` function should minimize
|
||||
dependencies on other chips being initialized, as the order of chip
|
||||
initialization is not guaranteed.
|
||||
|
||||
5. **Handle Resources Properly**: If the chip manages resources (memory
|
||||
regions, I/O ports, etc.), ensure that these are properly allocated and
|
||||
assigned to devices, usually within the associated `device_operations`.
|
||||
|
||||
6. **Implement Error Handling**: Include appropriate error handling in
|
||||
the initialization functions to handle hardware initialization failures
|
||||
gracefully.
|
||||
|
||||
7. **Document Special Requirements**: If the chip has special
|
||||
requirements or dependencies, document these clearly in comments or
|
||||
accompanying documentation.
|
||||
|
||||
|
||||
## Troubleshooting `chip_operations` Issues
|
||||
|
||||
When implementing or debugging `chip_operations`, you might encounter
|
||||
certain issues:
|
||||
|
||||
1. **Missing Device Operations**: If devices are not being initialized
|
||||
properly, check that the `enable_dev` function is correctly
|
||||
assigning device operations based on device types. Ensure it's being
|
||||
called during bus scanning.
|
||||
|
||||
2. **Initialization Order Problems**: If a chip's initialization depends
|
||||
on another chip being initialized first, you might need to adjust the
|
||||
initialization sequence or add explicit dependencies, possibly using
|
||||
boot state callbacks if necessary.
|
||||
|
||||
3. **Configuration Data Issues**: If chip configuration settings are not
|
||||
being applied correctly, check that the configuration structure is
|
||||
correctly defined in `chip.h`, that the register values in the
|
||||
devicetree match the expected format, and that the `chip_info` pointer
|
||||
is cast correctly in the `init`/`final` functions.
|
||||
|
||||
4. **Build Errors**: If you encounter build errors related to
|
||||
`chip_operations`, check that the structure is correctly defined and
|
||||
that all required symbols are properly exported and linked. Check for
|
||||
conflicts if multiple files define the same symbol.
|
||||
|
||||
5. **Runtime Failures**: If the chip initialization fails at runtime,
|
||||
add debug logging (using `printk`) to the `init`, `enable_dev`, and
|
||||
`final` functions to identify the specific point of failure.
|
||||
|
||||
|
||||
## Advanced `chip_operations` Patterns
|
||||
|
||||
### Hierarchical Chip Initialization
|
||||
|
||||
For complex chips with multiple components, you can implement a
|
||||
hierarchical initialization pattern within the `init` function:
|
||||
|
||||
```c
|
||||
static void soc_init(void *chip_info)
|
||||
{
|
||||
/* Initialize common components first */
|
||||
common_init(chip_info);
|
||||
|
||||
/* Initialize specific blocks */
|
||||
pcie_init(chip_info);
|
||||
usb_init(chip_info);
|
||||
sata_init(chip_info);
|
||||
|
||||
/* Final SoC-wide configuration */
|
||||
power_management_init(chip_info);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Variant Support
|
||||
|
||||
For chips with multiple variants, you can implement variant detection
|
||||
and specific initialization within the `init` function:
|
||||
|
||||
```c
|
||||
static void soc_init(void *chip_info)
|
||||
{
|
||||
uint32_t variant = read_chip_variant();
|
||||
|
||||
/* Common initialization */
|
||||
common_init(chip_info);
|
||||
|
||||
/* Variant-specific initialization */
|
||||
switch (variant) {
|
||||
case VARIANT_A:
|
||||
variant_a_init(chip_info);
|
||||
break;
|
||||
case VARIANT_B:
|
||||
variant_b_init(chip_info);
|
||||
break;
|
||||
default:
|
||||
printk(BIOS_WARNING, "Unknown variant %u\\n", variant);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Conditional Feature Initialization
|
||||
|
||||
You can conditionally initialize features based on configuration settings
|
||||
passed via `chip_info`:
|
||||
|
||||
```c
|
||||
static void soc_init(void *chip_info)
|
||||
{
|
||||
struct soc_config *config = chip_info;
|
||||
|
||||
/* Always initialize core components */
|
||||
core_init();
|
||||
|
||||
/* Conditionally initialize optional features */
|
||||
if (config->enable_xhci)
|
||||
xhci_init(config);
|
||||
|
||||
if (config->enable_sata)
|
||||
sata_init(config);
|
||||
|
||||
if (config->enable_pcie)
|
||||
pcie_init(config);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
The `chip_operations` structure is a fundamental component of coreboot's
|
||||
chipset abstraction layer. It provides a standardized interface for chipset-
|
||||
specific code to interact with coreboot's device initialization framework,
|
||||
enabling support for a wide variety of chipsets while maintaining a
|
||||
consistent initialization flow.
|
||||
|
||||
By implementing the `chip_operations` structure for a specific chipset
|
||||
(and often for the mainboard), developers can integrate their
|
||||
hardware-specific code with coreboot's device enumeration, configuration,
|
||||
and initialization process. This structure serves as the bridge between
|
||||
the declarative device descriptions in the devicetree and the imperative
|
||||
code that initializes the hardware.
|
||||
|
||||
Understanding the `chip_operations` structure and its role in the
|
||||
coreboot boot process is essential for anyone working on chipset or
|
||||
mainboard support in coreboot. By following the best practices and
|
||||
patterns outlined in this document, developers can create robust and
|
||||
maintainable hardware support code that integrates seamlessly with the
|
||||
coreboot firmware ecosystem.
|
||||
|
|
@ -1,696 +0,0 @@
|
|||
# Device Operations in coreboot Firmware
|
||||
|
||||
## Introduction
|
||||
|
||||
The `device_operations` structure is a cornerstone of coreboot's
|
||||
hardware abstraction layer. It represents a set of function pointers
|
||||
defining how the firmware interacts with a specific hardware device
|
||||
during various boot stages. This structure lets the coreboot
|
||||
architecture establish a consistent interface for device initialization,
|
||||
configuration, resource allocation, and ACPI table generation, while
|
||||
allowing device-specific implementations behind this common interface.
|
||||
|
||||
At its core, `device_operations` applies the strategy pattern in
|
||||
systems programming. It decouples algorithms (device operations) from
|
||||
the core boot sequence, allowing devices to define their own behavior
|
||||
while the boot process follows a predictable flow. This pattern enables
|
||||
coreboot to support a wide range of hardware platforms with minimal
|
||||
changes to the core boot sequence code.
|
||||
|
||||
|
||||
## Structure Definition
|
||||
|
||||
The `device_operations` structure, defined in
|
||||
`src/include/device/device.h`, consists of several function pointers,
|
||||
each representing a specific operation performed on a device during
|
||||
boot:
|
||||
|
||||
```c
|
||||
struct device_operations {
|
||||
void (*read_resources)(struct device *dev);
|
||||
void (*set_resources)(struct device *dev);
|
||||
void (*enable_resources)(struct device *dev);
|
||||
void (*init)(struct device *dev);
|
||||
void (*final)(struct device *dev);
|
||||
void (*scan_bus)(struct device *bus);
|
||||
void (*enable)(struct device *dev);
|
||||
void (*vga_disable)(struct device *dev);
|
||||
void (*reset_bus)(struct bus *bus);
|
||||
|
||||
int (*get_smbios_data)(struct device *dev, int *handle,
|
||||
unsigned long *current);
|
||||
void (*get_smbios_strings)(struct device *dev, struct smbios_type11 *t);
|
||||
|
||||
unsigned long (*write_acpi_tables)(const struct device *dev,
|
||||
unsigned long start, struct acpi_rsdp *rsdp);
|
||||
void (*acpi_fill_ssdt)(const struct device *dev);
|
||||
const char *(*acpi_name)(const struct device *dev);
|
||||
const char *(*acpi_hid)(const struct device *dev);
|
||||
|
||||
const struct pci_operations *ops_pci;
|
||||
const struct i2c_bus_operations *ops_i2c_bus;
|
||||
const struct spi_bus_operations *ops_spi_bus;
|
||||
const struct smbus_bus_operations *ops_smbus_bus;
|
||||
const struct pnp_mode_ops *ops_pnp_mode;
|
||||
const struct gpio_operations *ops_gpio;
|
||||
const struct mdio_bus_operations *ops_mdio;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Core Resource Management Functions
|
||||
|
||||
* `read_resources`: Discovers and collects resources required by the
|
||||
device (I/O ports, memory regions, IRQs, etc.). This typically
|
||||
involves reading configuration registers or static device tree
|
||||
information.
|
||||
* `set_resources`: Assigns finalized resources to the device after the
|
||||
resource allocation phase. This writes the assigned base addresses,
|
||||
lengths, and other parameters back to the device structure, but not
|
||||
necessarily to hardware registers yet.
|
||||
* `enable_resources`: Activates the assigned resources in the device's
|
||||
hardware registers (e.g., writing to PCI BARs, enabling memory
|
||||
decoding).
|
||||
|
||||
|
||||
### Device Lifecycle Functions
|
||||
|
||||
* `init`: Performs device-specific initialization, often requiring
|
||||
access to the device's assigned resources. This is called after
|
||||
resources have been enabled.
|
||||
* `final`: Executes final setup or cleanup operations before the
|
||||
payload is loaded. This is useful for tasks that depend on other
|
||||
devices being initialized.
|
||||
* `enable`: Enables the device in the hardware, often setting bits in
|
||||
configuration registers to make the device active. Called after
|
||||
`enable_resources`.
|
||||
|
||||
|
||||
### Bus Management Functions
|
||||
|
||||
* `scan_bus`: Enumerates child devices on a bus device (e.g., scanning
|
||||
a PCI bus for devices, probing I2C devices). Only applicable to
|
||||
devices that act as buses.
|
||||
* `reset_bus`: Resets all devices on a specific bus.
|
||||
* `vga_disable`: Disables VGA decoding on a PCI device when another VGA
|
||||
device is active. Used to manage legacy VGA resources.
|
||||
|
||||
|
||||
### System Table Generation Functions
|
||||
|
||||
* `get_smbios_data`: Provides SMBIOS data specific to the device for
|
||||
Type 9 (System Slots) or Type 41 (Onboard Devices Extended
|
||||
Information).
|
||||
* `get_smbios_strings`: Supplies string information for SMBIOS tables,
|
||||
often related to the data provided by `get_smbios_data`.
|
||||
* `write_acpi_tables`: Generates device-specific ACPI tables (like SSDTs)
|
||||
or contributes data to system-wide tables.
|
||||
* `acpi_fill_ssdt`: Adds device-specific objects (scopes, methods, data)
|
||||
to the Secondary System Description Table (SSDT).
|
||||
* `acpi_name`: Returns the ACPI name for the device (e.g.,
|
||||
`\_SB.PCI0.GFX0`). This defines the device's path in the ACPI
|
||||
namespace.
|
||||
* `acpi_hid`: Returns the ACPI Hardware ID (HID) for the device (e.g.,
|
||||
`PNP0A08`). Used by the OS to match drivers.
|
||||
|
||||
|
||||
### Bus-Specific Operation Pointers
|
||||
|
||||
These fields point to bus-specific operation structures when a device
|
||||
functions as a bus controller (or exposes bus-like functionality). See
|
||||
the "Bus-Specific Operations" section for details.
|
||||
|
||||
* `ops_pci`: Operations for PCI configuration space access.
|
||||
* `ops_i2c_bus`: Operations for I2C bus transactions (read, write,
|
||||
transfer).
|
||||
* `ops_spi_bus`: Operations for SPI bus transactions.
|
||||
* `ops_smbus_bus`: Operations for SMBus transactions.
|
||||
* `ops_pnp_mode`: Operations for Plug-and-Play device configuration.
|
||||
* `ops_gpio`: Operations for GPIO control (get, set, configure
|
||||
direction/pulls).
|
||||
* `ops_mdio`: Operations for MDIO (Management Data Input/Output) bus
|
||||
access, used for Ethernet PHYs.
|
||||
|
||||
|
||||
## Device Lifecycle in coreboot
|
||||
|
||||
The function pointers in `device_operations` are called at specific
|
||||
stages during the boot process, following a sequence defined in
|
||||
coreboot's boot state machine (`src/lib/hardwaremain.c`). Understanding
|
||||
this lifecycle helps developers implement appropriate behavior for each
|
||||
function pointer.
|
||||
|
||||
|
||||
### Boot Sequence and Device Operations
|
||||
|
||||
coreboot's main device initialization sequence involves these boot
|
||||
states:
|
||||
|
||||
1. **BS_DEV_INIT_CHIPS** (`dev_initialize_chips()`): Initializes chip
|
||||
drivers (`chip_operations`).
|
||||
2. **BS_DEV_ENUMERATE** (`dev_enumerate()`): Discovers and enumerates
|
||||
devices.
|
||||
* Calls `scan_bus()` for each bus to detect child devices.
|
||||
3. **BS_DEV_RESOURCES** (`dev_configure()`): Allocates resources across
|
||||
all enumerated devices.
|
||||
* Calls `read_resources()` for each device to discover required
|
||||
resources.
|
||||
* Calls `set_resources()` for each device to assign allocated
|
||||
resources back to the `struct device`.
|
||||
4. **BS_DEV_ENABLE** (`dev_enable()`): Enables devices and their
|
||||
resources.
|
||||
* Calls `enable_resources()` for each device to activate assigned
|
||||
resources in hardware.
|
||||
* Calls `enable()` for each device to perform general hardware
|
||||
enablement.
|
||||
5. **BS_DEV_INIT** (`dev_initialize()`): Initializes devices.
|
||||
* Calls `init()` for each device to perform device-specific setup.
|
||||
6. **BS_POST_DEVICE** (`dev_finalize()`): Finalizes devices before
|
||||
payload loading.
|
||||
* Calls `final()` for each device for any final cleanup or setup.
|
||||
|
||||
The sequence is primarily driven by the `boot_states` array in
|
||||
`src/lib/hardwaremain.c`:
|
||||
|
||||
```c
|
||||
static struct boot_state boot_states[] = {
|
||||
/* ... other states ... */
|
||||
BS_INIT_ENTRY(BS_PRE_DEVICE, bs_pre_device),
|
||||
BS_INIT_ENTRY(BS_DEV_INIT_CHIPS, bs_dev_init_chips),
|
||||
BS_INIT_ENTRY(BS_DEV_ENUMERATE, bs_dev_enumerate),
|
||||
BS_INIT_ENTRY(BS_DEV_RESOURCES, bs_dev_resources),
|
||||
BS_INIT_ENTRY(BS_DEV_ENABLE, bs_dev_enable),
|
||||
BS_INIT_ENTRY(BS_DEV_INIT, bs_dev_init),
|
||||
BS_INIT_ENTRY(BS_POST_DEVICE, bs_post_device),
|
||||
/* ... other states ... */
|
||||
};
|
||||
```
|
||||
|
||||
Later stages include ACPI and SMBIOS table generation, where functions
|
||||
like `write_acpi_tables()`, `acpi_fill_ssdt()`, `get_smbios_data()`, and
|
||||
`get_smbios_strings()` are invoked as part of the table construction
|
||||
process.
|
||||
|
||||
|
||||
## Inheritance and Code Reuse Patterns
|
||||
|
||||
The `device_operations` structure enables several patterns for code
|
||||
reuse:
|
||||
|
||||
|
||||
### 1. Default Implementations
|
||||
|
||||
coreboot provides default implementations for common device types (like
|
||||
root devices, PCI devices, PCI bridges), which can be used directly or
|
||||
extended. Chip or mainboard code often assigns these defaults if no
|
||||
specific driver is found.
|
||||
|
||||
```c
|
||||
/* From src/device/root_device.c */
|
||||
struct device_operations default_dev_ops_root = {
|
||||
.read_resources = noop_read_resources,
|
||||
.set_resources = noop_set_resources,
|
||||
.scan_bus = scan_static_bus,
|
||||
.reset_bus = root_dev_reset,
|
||||
#if CONFIG(HAVE_ACPI_TABLES)
|
||||
.acpi_name = root_dev_acpi_name,
|
||||
#endif
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### 2. No-op Functions
|
||||
|
||||
Simple shim functions (often static inline) are provided for cases where
|
||||
a device doesn't need to implement specific operations. Using these avoids
|
||||
leaving function pointers NULL.
|
||||
|
||||
```c
|
||||
/* From src/include/device/device.h */
|
||||
static inline void noop_read_resources(struct device *dev) {}
|
||||
static inline void noop_set_resources(struct device *dev) {}
|
||||
```
|
||||
|
||||
|
||||
### 3. Chain of Responsibility / Delegation
|
||||
|
||||
Some implementations delegate to parent devices or use helper functions
|
||||
when they can't handle an operation themselves or when common logic can
|
||||
be shared. For example, ACPI name generation often traverses up the
|
||||
device tree.
|
||||
|
||||
```c
|
||||
/* Simplified example logic */
|
||||
const char *acpi_device_name(const struct device *dev)
|
||||
{
|
||||
const char *name = NULL;
|
||||
const struct device *pdev = dev;
|
||||
|
||||
/* Check for device specific handler */
|
||||
if (dev->ops && dev->ops->acpi_name) {
|
||||
name = dev->ops->acpi_name(dev);
|
||||
if (name)
|
||||
return name; /* Device handled it */
|
||||
}
|
||||
|
||||
/* Walk up the tree to find if any parent can provide a name */
|
||||
while (pdev->upstream && pdev->upstream->dev) {
|
||||
pdev = pdev->upstream->dev;
|
||||
if (pdev->ops && pdev->ops->acpi_name) {
|
||||
/* Note: Parent's acpi_name might handle the original child 'dev' */
|
||||
name = pdev->ops->acpi_name(dev);
|
||||
if (name)
|
||||
return name; /* Parent handled it */
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback or default logic if needed */
|
||||
return NULL;
|
||||
}
|
||||
```
|
||||
This pattern allows parent devices (like buses) to provide default
|
||||
behavior or naming schemes if a child device doesn't specify its own.
|
||||
|
||||
|
||||
## Implementation Examples
|
||||
|
||||
These examples show typical `device_operations` assignments. Actual
|
||||
implementations might involve more conditional compilation based on
|
||||
Kconfig options.
|
||||
|
||||
|
||||
### PCI Device Operations (Default)
|
||||
|
||||
```c
|
||||
/* From src/device/pci_device.c */
|
||||
struct device_operations default_pci_ops_dev = {
|
||||
.read_resources = pci_dev_read_resources,
|
||||
.set_resources = pci_dev_set_resources,
|
||||
.enable_resources = pci_dev_enable_resources,
|
||||
#if CONFIG(HAVE_ACPI_TABLES)
|
||||
.write_acpi_tables = pci_rom_write_acpi_tables,
|
||||
.acpi_fill_ssdt = pci_rom_ssdt,
|
||||
#endif
|
||||
.init = pci_dev_init,
|
||||
/* Assigns PCI-specific operations */
|
||||
.ops_pci = &pci_dev_ops_pci,
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### CPU Cluster Operations
|
||||
|
||||
```c
|
||||
/* From src/soc/intel/alderlake/chip.c (representative example) */
|
||||
static struct device_operations cpu_bus_ops = {
|
||||
.read_resources = noop_read_resources,
|
||||
.set_resources = noop_set_resources,
|
||||
.enable_resources = cpu_set_north_irqs,
|
||||
#if CONFIG(HAVE_ACPI_TABLES)
|
||||
.acpi_fill_ssdt = cpu_fill_ssdt,
|
||||
#endif
|
||||
/* CPU clusters often don't need scan_bus, init, etc. */
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### GPIO Controller Operations
|
||||
|
||||
```c
|
||||
/* From src/soc/intel/common/block/gpio/gpio_dev.c */
|
||||
static struct gpio_operations gpio_ops = {
|
||||
.get = gpio_get,
|
||||
.set = gpio_set,
|
||||
/* ... other GPIO functions ... */
|
||||
};
|
||||
|
||||
struct device_operations block_gpio_ops = {
|
||||
.read_resources = noop_read_resources,
|
||||
.set_resources = noop_set_resources,
|
||||
/* Assigns GPIO-specific operations */
|
||||
.ops_gpio = &gpio_ops,
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Registration and Discovery
|
||||
|
||||
How are `device_operations` structures associated with `struct device`
|
||||
instances?
|
||||
|
||||
|
||||
### 1. Static Assignment (via `chip_operations`)
|
||||
|
||||
For devices known at build time (defined in devicetree.cb), the
|
||||
`device_operations` structure is often assigned in the SOC's or
|
||||
mainboard's `chip_operations->enable_dev()` function based on the
|
||||
device path type or other properties.
|
||||
|
||||
```c
|
||||
/* Example from src/soc/intel/alderlake/chip.c */
|
||||
static void soc_enable(struct device *dev)
|
||||
{
|
||||
/* Assign ops based on the device's role in the tree */
|
||||
if (dev->path.type == DEVICE_PATH_DOMAIN)
|
||||
dev->ops = &pci_domain_ops; /* Handles PCI domain resources */
|
||||
else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER)
|
||||
dev->ops = &cpu_bus_ops; /* Handles CPU cluster setup */
|
||||
else if (dev->path.type == DEVICE_PATH_GPIO)
|
||||
block_gpio_enable(dev); /* Assigns block_gpio_ops */
|
||||
/* ... other assignments for specific PCI devices, etc. ... */
|
||||
}
|
||||
```
|
||||
The `enable_dev` function is part of `struct chip_operations`, which
|
||||
handles broader chip-level initialization (see "Relationship with
|
||||
`chip_operations`" section).
|
||||
|
||||
|
||||
### 2. Dynamic Detection (PCI Drivers)
|
||||
|
||||
For PCI devices discovered during bus scanning (`scan_bus`), coreboot
|
||||
looks through a list of registered PCI drivers (`_pci_drivers` array)
|
||||
to find one matching the device's vendor and device IDs.
|
||||
|
||||
```c
|
||||
/* Logic from src/device/pci_device.c::set_pci_ops() */
|
||||
static void set_pci_ops(struct device *dev)
|
||||
{
|
||||
struct pci_driver *driver;
|
||||
|
||||
/* Check if ops already assigned (e.g., by chip_ops->enable_dev) */
|
||||
if (dev->ops)
|
||||
return;
|
||||
|
||||
/* Look through registered PCI drivers */
|
||||
for (driver = &_pci_drivers[0]; driver != &_epci_drivers[0]; driver++) {
|
||||
if ((driver->vendor == dev->vendor) &&
|
||||
device_id_match(driver, dev->device)) {
|
||||
/* Found a matching driver, assign its ops */
|
||||
dev->ops = (struct device_operations *)driver->ops;
|
||||
printk(BIOS_SPEW, "%s: Assigned ops from driver for %04x:%04x\n",
|
||||
dev_path(dev), driver->vendor, driver->device);
|
||||
return; /* Stop searching */
|
||||
}
|
||||
}
|
||||
|
||||
/* Fall back to default operations if no specific driver found */
|
||||
if (!dev->ops) {
|
||||
if ((dev->hdr_type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
dev->ops = get_pci_bridge_ops(dev); /* Special ops for bridges */
|
||||
} else {
|
||||
dev->ops = &default_pci_ops_dev; /* Default for normal devices */
|
||||
}
|
||||
printk(BIOS_SPEW, "%s: Assigned default PCI ops\n", dev_path(dev));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Build Process Integration
|
||||
|
||||
The `device_operations` structures are integrated into the coreboot
|
||||
build process:
|
||||
|
||||
1. **Static Device Tree**: The mainboard's `devicetree.cb` defines the
|
||||
initial device hierarchy. The build process converts this into C
|
||||
code (`static.c`) containing `struct device` instances.
|
||||
2. **PCI Driver Registration**: PCI drivers (containing their own
|
||||
`device_operations`) register themselves using the `__pci_driver`
|
||||
linker set macro.
|
||||
|
||||
```c
|
||||
/* Example pattern */
|
||||
struct pci_driver example_pci_driver __pci_driver = {
|
||||
.ops = &example_device_ops, /* Pointer to device_operations */
|
||||
.vendor = VENDOR_ID,
|
||||
.device = DEVICE_ID, /* Or .devices for a list */
|
||||
};
|
||||
```
|
||||
3. **Linking**: The build system collects all structures placed in the
|
||||
`__pci_driver` section and creates the `_pci_drivers` array used by
|
||||
`set_pci_ops()`. It ensures all necessary code (default ops, driver
|
||||
ops, core device functions) is linked into the final firmware image.
|
||||
|
||||
|
||||
## Relationship with `chip_operations`
|
||||
|
||||
It's important to distinguish `device_operations` from
|
||||
`chip_operations` (defined in `src/include/chip.h`).
|
||||
|
||||
* `chip_operations`: Defines operations related to the overall chipset
|
||||
or mainboard logic. It includes functions called earlier in the boot
|
||||
process, like `enable_dev`, `init`, and `final`.
|
||||
* `chip_operations->enable_dev()` is crucial as it often performs
|
||||
initial setup for static devices and is the primary place where
|
||||
`device_operations` pointers are *assigned* for non-PCI devices
|
||||
based on their path or type.
|
||||
* `chip_operations->init()` runs during `BS_DEV_INIT_CHIPS`, before
|
||||
most `device_operations` functions.
|
||||
* `device_operations`: Defines operations for *individual* devices
|
||||
within the device tree. These are called *after* the corresponding
|
||||
`chip_operations` stage and operate on a specific `struct device`.
|
||||
|
||||
Essentially, `chip_operations` sets the stage at the SoC/mainboard level,
|
||||
including assigning the correct `device_operations` to static devices,
|
||||
while `device_operations` handles the specific actions for each device
|
||||
later in the boot process.
|
||||
|
||||
|
||||
## Bus-Specific Operations
|
||||
|
||||
The `ops_*` pointers within `struct device_operations` (e.g., `ops_pci`,
|
||||
`ops_i2c_bus`, `ops_spi_bus`, `ops_gpio`) provide a way for devices that
|
||||
act as bus controllers or expose bus-like interfaces to offer
|
||||
standardized access methods.
|
||||
|
||||
* **Purpose:** They abstract the low-level details of interacting with
|
||||
that specific bus type. For example, a PCI host bridge device will
|
||||
implement `struct pci_operations` via its `ops_pci` pointer,
|
||||
allowing other code to perform PCI config reads/writes through it
|
||||
without knowing the exact hardware mechanism. Similarly, an I2C
|
||||
controller device implements `struct i2c_bus_operations` via
|
||||
`ops_i2c_bus` to provide standard `read`, `write`, and `transfer`
|
||||
functions for that bus segment.
|
||||
* **Usage:** Code needing to interact with a bus first finds the
|
||||
controller `struct device` in the tree, then accesses the relevant
|
||||
bus operations through the appropriate `ops_*` pointer, passing the
|
||||
target address or parameters. For instance, to talk to an I2C device
|
||||
at address `0x50` on the bus controlled by `i2c_controller_dev`, one
|
||||
might call:
|
||||
`i2c_controller_dev->ops->ops_i2c_bus->transfer(...)`. Helper
|
||||
functions often wrap this access pattern.
|
||||
* **Implementation:** The structures like `struct pci_operations`,
|
||||
`struct i2c_bus_operations`, etc., are defined in corresponding
|
||||
header files (e.g., `src/include/device/pci_ops.h`,
|
||||
`src/include/drivers/i2c/i2c_bus.h`). Devices acting as controllers
|
||||
provide concrete implementations of these functions, tailored to their
|
||||
hardware.
|
||||
|
||||
This mechanism allows coreboot to manage diverse bus types using a
|
||||
consistent device model, where the controller device itself exposes the
|
||||
necessary functions for interacting with devices on its bus.
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
When implementing `device_operations`:
|
||||
|
||||
1. **Leverage Defaults/No-ops**: Use default or no-op implementations
|
||||
whenever possible. Only override functions that require custom
|
||||
behavior for your specific device.
|
||||
2. **Error Handling**: Check return values from functions called within
|
||||
your ops implementations and handle errors gracefully (e.g., log an
|
||||
error, return an error code if applicable).
|
||||
3. **Resource Management**: In `read_resources`, accurately declare all
|
||||
resources (MMIO, I/O ports, IRQs) your device needs, specifying
|
||||
flags like fixed vs. alignment, or bridge vs. standard device.
|
||||
Incorrect resource declaration is a common source of issues.
|
||||
4. **Initialization Order**: Be mindful of dependencies in `init`. If
|
||||
your device relies on another device being fully initialized, consider
|
||||
deferring that part of the initialization to the `final` callback,
|
||||
which runs later.
|
||||
5. **Minimal Implementation**: Only implement the functions relevant to
|
||||
your device type. A simple MMIO device might only need
|
||||
`read_resources`, `set_resources`, `enable_resources`, and perhaps
|
||||
ACPI functions. A bus device additionally needs `scan_bus`.
|
||||
6. **Bus Operations**: If implementing a bus controller, correctly
|
||||
implement the corresponding bus operations structure (e.g.,
|
||||
`struct pci_operations`, `struct i2c_bus_operations`) and assign it
|
||||
to the appropriate `ops_*` field.
|
||||
7. **ACPI/SMBIOS**: If the device needs OS visibility via ACPI or
|
||||
SMBIOS, implement the relevant functions (`acpi_name`, `acpi_hid`,
|
||||
`acpi_fill_ssdt`, `get_smbios_data`, etc.). Ensure ACPI names and
|
||||
HIDs are correct according to specifications and platform needs.
|
||||
|
||||
|
||||
## Logging and Debugging
|
||||
|
||||
Use coreboot's logging facilities (`printk`) within your `device_operations`
|
||||
functions to provide visibility during development and debugging. Use
|
||||
appropriate log levels (e.g., `BIOS_DEBUG`, `BIOS_INFO`, `BIOS_ERR`).
|
||||
|
||||
```c
|
||||
static void example_device_init(struct device *dev)
|
||||
{
|
||||
printk(BIOS_DEBUG, "%s: Initializing device at %s\n", __func__,
|
||||
dev_path(dev));
|
||||
|
||||
/* ... Device initialization code ... */
|
||||
if (/* some condition */) {
|
||||
printk(BIOS_SPEW, "%s: Condition met, applying setting X\n",
|
||||
dev_path(dev));
|
||||
/* ... */
|
||||
}
|
||||
|
||||
if (/* error condition */) {
|
||||
printk(BIOS_ERR, "%s: Failed to initialize feature Y!\n",
|
||||
dev_path(dev));
|
||||
/* Handle error */
|
||||
}
|
||||
|
||||
printk(BIOS_DEBUG, "%s: Initialization complete for %s\n", __func__,
|
||||
dev_path(dev));
|
||||
}
|
||||
```
|
||||
Consistent logging helps trace the boot process and pinpoint where issues
|
||||
occur.
|
||||
|
||||
|
||||
## Common Troubleshooting
|
||||
|
||||
* **Missing Resource Declarations**:
|
||||
* *Problem*: Device fails to function, or conflicts arise because a
|
||||
required resource (MMIO range, I/O port, IRQ) was not declared
|
||||
in `read_resources`. The resource allocator is unaware of the
|
||||
need.
|
||||
* *Solution*: Verify that `read_resources` correctly calls functions
|
||||
like `pci_dev_read_resources` or manually adds all necessary
|
||||
resources using functions like `mmio_resource()`,
|
||||
`io_resource()`, etc. Check PCI BARs or device datasheets.
|
||||
* **Initialization Order Issues**:
|
||||
* *Problem*: `init()` fails because it depends on another device
|
||||
that hasn't been fully initialized yet (e.g., accessing a shared
|
||||
resource like SMBus before the SMBus controller is ready).
|
||||
* *Solution*: Move the dependent initialization code to the `final`
|
||||
callback if possible. Alternatively, ensure the dependency is met
|
||||
by careful ordering in the device tree or using boot state
|
||||
callbacks if necessary for complex scenarios.
|
||||
* **Resource Conflicts**:
|
||||
* *Problem*: Boot fails during resource allocation, or devices
|
||||
misbehave because multiple devices requested the same
|
||||
non-sharable resource (e.g., conflicting fixed MMIO regions).
|
||||
* *Solution*: Review resource declarations in `read_resources` across
|
||||
all relevant devices. Ensure fixed resources don't overlap. Check
|
||||
if bridge windows are correctly defined and large enough. Use
|
||||
coreboot's resource reporting logs to identify overlaps.
|
||||
* **ACPI Table Generation Errors**:
|
||||
* *Problem*: The operating system fails to recognize the device,
|
||||
assigns the wrong driver, or the device doesn't function correctly
|
||||
(e.g., power management issues).
|
||||
* *Solution*: Double-check the `acpi_name`, `acpi_hid`, `_CRS`
|
||||
(generated from assigned resources), and `acpi_fill_ssdt`
|
||||
implementations. Verify names match the ACPI hierarchy and HIDs
|
||||
match expected driver bindings. Ensure SSDT methods correctly
|
||||
access hardware. Use OS debugging tools (e.g., `acpidump`, Device
|
||||
Manager errors) to diagnose.
|
||||
* **Incorrect `ops` Pointer Assigned**:
|
||||
* *Problem*: Device behaves incorrectly because the wrong
|
||||
`device_operations` structure was assigned (e.g., default PCI ops
|
||||
assigned to a device needing a specific driver's ops).
|
||||
* *Solution*: Check the logic in `chip_operations->enable_dev` (for
|
||||
static devices) or the PCI driver registration (`__pci_driver`
|
||||
macro and `set_pci_ops` fallback logic) to ensure the correct
|
||||
`ops` structure is being selected and assigned based on device
|
||||
type, path, or PCI ID. Add debug prints to verify which `ops`
|
||||
structure is assigned.
|
||||
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Complex Device Hierarchies
|
||||
|
||||
For devices with non-standard interactions or complex initialization,
|
||||
custom `device_operations` can be created, often inheriting from defaults
|
||||
but overriding specific functions.
|
||||
|
||||
```c
|
||||
static void advanced_device_init(struct device *dev)
|
||||
{
|
||||
/* First, perform standard PCI init */
|
||||
pci_dev_init(dev);
|
||||
|
||||
/* Then, add custom initialization steps */
|
||||
printk(BIOS_DEBUG, "%s: Performing advanced init\n", dev_path(dev));
|
||||
/* ... custom register writes, configuration ... */
|
||||
}
|
||||
|
||||
static const char *advanced_device_acpi_name(const struct device *dev)
|
||||
{
|
||||
/* Provide a custom ACPI name based on some property */
|
||||
if (/* condition */)
|
||||
return "ADV0001";
|
||||
else
|
||||
return "ADV0002";
|
||||
}
|
||||
|
||||
/* Combine default and custom operations */
|
||||
static struct device_operations advanced_device_ops = {
|
||||
/* Inherit resource handling from default PCI ops */
|
||||
.read_resources = pci_dev_read_resources,
|
||||
.set_resources = pci_dev_set_resources,
|
||||
.enable_resources = pci_dev_enable_resources,
|
||||
|
||||
/* Override init */
|
||||
.init = advanced_device_init,
|
||||
|
||||
/* Override ACPI naming */
|
||||
.acpi_name = advanced_device_acpi_name,
|
||||
/* Other functions might use defaults or no-ops */
|
||||
};
|
||||
```
|
||||
|
||||
### Dynamic Configuration based on Probing
|
||||
|
||||
Some `init` or other op implementations might probe the device's
|
||||
capabilities or read configuration data (e.g., from SPD, VPD, or straps)
|
||||
and alter their behavior accordingly.
|
||||
|
||||
```c
|
||||
static void conditional_device_init(struct device *dev)
|
||||
{
|
||||
uint8_t feature_flags;
|
||||
|
||||
/* Read capability register from the device */
|
||||
feature_flags = pci_read_config8(dev, EXAMPLE_CAP_REG);
|
||||
|
||||
printk(BIOS_DEBUG, "%s: Feature flags: 0x%02x\n", dev_path(dev),
|
||||
feature_flags);
|
||||
|
||||
/* Conditional initialization based on detected features */
|
||||
if (feature_flags & FEATURE_X_ENABLED) {
|
||||
printk(BIOS_INFO, "%s: Initializing Feature X\n", dev_path(dev));
|
||||
init_feature_x(dev);
|
||||
}
|
||||
if (feature_flags & FEATURE_Y_ENABLED) {
|
||||
printk(BIOS_INFO, "%s: Initializing Feature Y\n", dev_path(dev));
|
||||
init_feature_y(dev);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
The `device_operations` structure is a powerful abstraction mechanism in
|
||||
coreboot. It enables consistent handling of diverse hardware while
|
||||
allowing for device-specific behavior. By providing a standard interface
|
||||
for device operations throughout the boot process, it simplifies the
|
||||
codebase, enhances maintainability, and provides the extensibility needed
|
||||
to support new hardware platforms.
|
||||
|
||||
Understanding this structure, its relationship with `chip_operations`,
|
||||
and its role in the boot process is essential for coreboot developers,
|
||||
particularly when adding support for new devices or debugging hardware
|
||||
initialization issues. By following the patterns and best practices
|
||||
outlined here, developers can create robust and reusable device driver
|
||||
implementations that integrate smoothly into the coreboot architecture.
|
||||
|
|
@ -1,684 +0,0 @@
|
|||
# Devicetree
|
||||
|
||||
## Introduction to the coreboot devicetree
|
||||
|
||||
The first thing that may come to mind when one hears "DeviceTree" is a
|
||||
different sort of description file that is generally passed to the Linux
|
||||
kernel to describe a system's components. Both that devicetree and
|
||||
coreboot's devicetree serve fundamentally the same purpose, but are
|
||||
otherwise unrelated and have completely different syntax. The term
|
||||
devicetree was used long before either version was created, and was
|
||||
initially used in coreboot as a generic term.
|
||||
|
||||
coreboot's devicetree's main use is to define and describe the runtime
|
||||
configuration and settings of the hardware on a board, chip or device
|
||||
level. It defines which of the functions of the chips on the board are
|
||||
enabled, and how they're configured.
|
||||
|
||||
The devicetree file is parsed during the build process by a utility
|
||||
named `sconfig`, which translates the devicetree into a tree of C
|
||||
structures containing the included devices. This code is placed in the
|
||||
file `static.c` and a couple of header files, all under the `build`
|
||||
directory. This file is then built into the binaries for the various
|
||||
coreboot stages and is referred to during the coreboot boot process.
|
||||
|
||||
For the early stages of the coreboot boot process, the data that is
|
||||
generated by `sconfig` is a useful resource, but this structure is the
|
||||
critical architectural glue of ramstage. This structure gets filled in
|
||||
with pointers to every chip's initialization code, allowing ramstage to
|
||||
find and initialize those devices through the `chip_operations`
|
||||
structures.
|
||||
|
||||
|
||||
### History of coreboot's devicetree
|
||||
|
||||
The initial devicetree in coreboot was introduced in 2003 by Ron Minnich
|
||||
as a part of the linuxbios config file, 'config.lb'. At this point both
|
||||
the devicetree and config options were in the same file. In 2009,
|
||||
when Kconfig was added into the coreboot build, devicetree was split
|
||||
out into its own file for each mainboard in a commit with this message:
|
||||
|
||||
```text
|
||||
devicetree.cb
|
||||
|
||||
The devicetree that formerly resided in src/mainboard/*/*/Config.lb.
|
||||
|
||||
Just without the build system crap
|
||||
```
|
||||
|
||||
The devicetree structure was initially mainly used only in ramstage for
|
||||
PCI device enumeration, configuration and resource allocation. It has
|
||||
since expanded for use in the pre-ram stages as a read-only structure.
|
||||
|
||||
The language used in the devicetree has been expanded greatly since it
|
||||
was first introduced as well, adding new features every year or so.
|
||||
|
||||
|
||||
### Devicetree Registers
|
||||
|
||||
In coreboot, the devicetree register setting is one of the two main
|
||||
methods used to configure a board's properties. In this way, devicetree
|
||||
is similar in function to Kconfig. It's more flexible in many ways as
|
||||
it can specify not only single values, but also arrays or structures.
|
||||
It's also even more static than Kconfig because there's no update
|
||||
mechanism for it other than editing the devicetree files.
|
||||
|
||||
Chip-specific configuration values are often set using `register`
|
||||
definitions within a `chip` block, corresponding to a `struct` defined
|
||||
in the chip's `chip.h` file.
|
||||
|
||||
For example, in a `chip drivers/gpio/acpi` block, you might set a GPE:
|
||||
|
||||
```text
|
||||
register "gpe0_sts" = "0x42"
|
||||
```
|
||||
|
||||
|
||||
### Adding new static configuration options: Devicetree or Kconfig
|
||||
|
||||
When adding options for a new board or chip, there is frequently a
|
||||
decision that needs to be made about how the option should be added.
|
||||
Using the devicetree or Kconfig are the two typical methods of
|
||||
build-time configuration. Below are some general guidelines on when to
|
||||
use each.
|
||||
|
||||
Kconfig should be used if the option configures the build in a Makefile,
|
||||
or if the option is something that should be user selectable. Kconfig
|
||||
is also preferred if the configuration is a global option and not limited
|
||||
to a single chip. Another thing Kconfig excels at is handling decisions
|
||||
based on other configuration options, which devicetree cannot do.
|
||||
|
||||
Devicetree should obviously be used to define the hardware hierarchy.
|
||||
It's also preferred if the option is only used in C code and is static
|
||||
for a mainboard, or if the option is chip-specific. As mentioned
|
||||
earlier, devicetree registers can also define structures or arrays,
|
||||
which Kconfig cannot.
|
||||
|
||||
Both Kconfig and devicetree can be used in C code for runtime
|
||||
configuration, but there's a significant difference in how they are
|
||||
handled. Because Kconfig generates a `#define` for the choice, the
|
||||
compiler can eliminate code paths not used by the option. Devicetree
|
||||
options, however, are actual runtime selections, and the code for all
|
||||
choices remains in the final build.
|
||||
|
||||
|
||||
## Basic Devicetree Syntax
|
||||
|
||||
The coreboot devicetree uses a custom language parsed by the `sconfig`
|
||||
utility. Here's a brief overview of the main keywords and concepts:
|
||||
|
||||
* **`chip <directory>`**: Defines a collection of devices associated
|
||||
with the code in the specified directory. `sconfig` may also parse a
|
||||
`chip.h` file within this directory for register definitions.
|
||||
* **`device <type> <id> [on|off] [alias <name>] ... end`**: Defines a
|
||||
specific hardware device.
|
||||
* `<type>`: Specifies the device type (e.g., `pci`, `cpu_cluster`,
|
||||
`i2c`).
|
||||
* `<id>`: A unique identifier for the device within its type/bus
|
||||
(e.g., PCI BDF `17.0`, I2C address `0x50`).
|
||||
* `on`/`off`: Enables or disables the device definition.
|
||||
* `alias <name>`: Assigns a human-readable alias for referencing
|
||||
this device elsewhere (often used in `chipset.cb`).
|
||||
* `end`: Marks the end of the device definition block. Registers
|
||||
and other properties are defined between `device` and `end`.
|
||||
* **`register "<name>" = <value>`**: Sets the value of a configuration
|
||||
register defined in the corresponding `chip.h` structure. The value
|
||||
can be a number, string, or complex structure initialization.
|
||||
* **`probe <field> <option>`**: Used for firmware configuration
|
||||
(`fw_config`), indicating a setting should be probed at runtime.
|
||||
* **`ops "<identifier>"`**: Associates a `chip_operations` structure
|
||||
with the device, used primarily in ramstage for device initialization.
|
||||
* **`fw_config field <name> size <bits> ... end`**: Defines a firmware
|
||||
configuration field, often used for passing board-specific data to
|
||||
payloads. Options within the field are defined using `option`.
|
||||
* **`ref <alias>`**: Used within `device` definitions in `devicetree.cb`
|
||||
or `overridetree.cb` to refer to a device defined (usually via an
|
||||
`alias`) in a lower-level file like `chipset.cb`.
|
||||
* **`# <comment text>`**: Single-line comments.
|
||||
|
||||
Device definitions can be nested within `chip` blocks. `end` keywords
|
||||
close the current block (`device` or `chip`).
|
||||
|
||||
|
||||
## Three levels of devicetree files
|
||||
|
||||
There are currently three different levels of devicetrees used to build
|
||||
up the structure of components and register values in coreboot. From
|
||||
the lowest, most general level to the highest and most specific, they
|
||||
are `chipset.cb`, `devicetree.cb`, and `overridetree.cb`.
|
||||
|
||||
Unless there's a specific reason to name them something other than these
|
||||
names, they should be used.
|
||||
|
||||
For newer SoCs and chipsets, there will generally be a `chipset.cb` file.
|
||||
Every mainboard requires a `devicetree.cb` file, although it can be empty
|
||||
if everything is inherited from the `chipset.cb`. An `overridetree.cb`
|
||||
file is only required if variants have differences from the primary
|
||||
mainboard's `devicetree.cb`.
|
||||
|
||||
|
||||
### SoC / chipset level, `chipset.cb`
|
||||
|
||||
The `chipset.cb` file was added in October 2020, allowing a single
|
||||
chipset or SoC to provide a "base level" devicetree, reducing
|
||||
duplication between mainboards.
|
||||
|
||||
The `chipset.cb` file also typically defines human-readable "aliases"
|
||||
for particular devices so that mainboards can use those instead of PCI
|
||||
device/function numbers or other hardware identifiers.
|
||||
|
||||
The use of the `chipset.cb` file is specified in Kconfig by the
|
||||
`CHIPSET_DEVICETREE` symbol, which provides the path to the file.
|
||||
|
||||
In a `chipset.cb` file, you might see lines like this:
|
||||
|
||||
```text
|
||||
# Chip definition for the SoC/chipset itself
|
||||
chip soc/intel/common/block
|
||||
|
||||
# Define PCI device 17.0, alias it to "sata", and default it off
|
||||
device pci 17.0 alias sata off end
|
||||
|
||||
# Define PCI device 1e.0, alias it to "uart0", and default it off
|
||||
device pci 1e.0 alias uart0 off end
|
||||
|
||||
end # chip soc/intel/common/block
|
||||
```
|
||||
|
||||
This defines the devices, assigns aliases, and sets their default state.
|
||||
|
||||
|
||||
### Primary mainboard level, `devicetree.cb`
|
||||
|
||||
Each mainboard must have a `devicetree.cb` file. The filename and path are
|
||||
typically set by the `DEVICETREE` Kconfig symbol, defaulting to
|
||||
`src/mainboard/<VENDOR>/<BOARD>/devicetree.cb`.
|
||||
|
||||
If a mainboard using the above `chipset.cb` wanted both devices enabled,
|
||||
its `devicetree.cb` might contain:
|
||||
|
||||
```text
|
||||
# Reference the SATA device by its alias and enable it
|
||||
device ref sata on end
|
||||
|
||||
# Reference the UART0 device by its alias and enable it
|
||||
device ref uart0 on end
|
||||
```
|
||||
|
||||
The `ref` keyword looks up the device (usually by alias) defined in a
|
||||
lower-level file (`chipset.cb` in this case) and modifies its properties.
|
||||
|
||||
|
||||
### Mainboard variant level, `overridetree.cb`
|
||||
|
||||
Introduced in 2018 to reduce duplication and maintenance for board
|
||||
variants, the `overridetree.cb` file is the most specific level.
|
||||
|
||||
This allows a base `devicetree.cb` at the top mainboard level shared by
|
||||
all variants. Each variant then only needs an `overridetree.cb` to
|
||||
specify its differences.
|
||||
|
||||
The override tree filename is set in Kconfig with the
|
||||
`OVERRIDE_DEVICETREE` symbol and is typically named `overridetree.cb`.
|
||||
|
||||
Finally, if one variant of the mainboard lacked a SATA connector, it
|
||||
could disable the SATA device again using the following in its specific
|
||||
`overridetree.cb`:
|
||||
|
||||
```text
|
||||
# Reference the SATA device by alias and disable it for this variant
|
||||
device ref sata off end
|
||||
```
|
||||
|
||||
|
||||
## Additional files
|
||||
|
||||
|
||||
### `chip.h` files
|
||||
|
||||
coreboot looks at a "chip" as a collection of devices. This collection
|
||||
can be a single logical device or multiple different ones. The `chip`
|
||||
keyword starts this collection. Following the `chip` keyword is a
|
||||
directory path (relative to `src/`) containing the code for that chip
|
||||
or logical block of hardware.
|
||||
|
||||
There may optionally be a `chip.h` file in that directory. If present,
|
||||
`sconfig` parses this file to define a C structure containing the
|
||||
"register definitions" for the chip. The values for this structure's
|
||||
members are set using the `register` keyword in one of the devicetree
|
||||
files (`chipset.cb`, `devicetree.cb`, `overridetree.cb`). If not
|
||||
explicitly set, members typically default to 0 or follow standard C
|
||||
initialization rules. The `chip.h` file frequently also contains C
|
||||
macros, enums, and sub-structures used for setting the members of the
|
||||
main register structure.
|
||||
|
||||
The C structure for the chip's register definition is named after the
|
||||
directory containing the `chip.h` file, with slashes (`/`) changed to
|
||||
underscores (`_`), and `_config` appended. The leading `src/` is omitted.
|
||||
|
||||
This means that a line in a devicetree file like:
|
||||
`chip drivers/i2c/hid`
|
||||
would cause `sconfig` to look for `src/drivers/i2c/hid/chip.h`. If found,
|
||||
the register definition structure it contains would be named
|
||||
`drivers_i2c_hid_config`.
|
||||
|
||||
Here is the content of `src/drivers/i2c/hid/chip.h`:
|
||||
|
||||
```c
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __DRIVERS_I2C_HID_CHIP_H__
|
||||
#define __DRIVERS_I2C_HID_CHIP_H__
|
||||
#include <drivers/i2c/generic/chip.h>
|
||||
#define I2C_HID_CID "PNP0C50"
|
||||
|
||||
struct drivers_i2c_hid_config {
|
||||
struct drivers_i2c_generic_config generic;
|
||||
uint8_t hid_desc_reg_offset;
|
||||
};
|
||||
|
||||
#endif /* __I2C_HID_CHIP_H__ */
|
||||
```
|
||||
|
||||
In a devicetree, you could set `hid_desc_reg_offset` like this:
|
||||
|
||||
```text
|
||||
chip drivers/i2c/hid
|
||||
device i2c 0x2c on
|
||||
# Set the HID descriptor register offset
|
||||
register "hid_desc_reg_offset" = "0x01"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
## The `sconfig` utility and generated files
|
||||
|
||||
|
||||
### `util/sconfig`
|
||||
|
||||
`sconfig` is the tool that parses the coreboot devicetrees and turns
|
||||
them into a collection of C structures. This is a coreboot-specific
|
||||
tool, built using flex & bison to define and parse the domain-specific
|
||||
language used by coreboot's devicetree.
|
||||
|
||||
`sconfig` is called by the makefiles during the build process and doesn't
|
||||
generally need to be run directly. If run manually (e.g.,
|
||||
`build/util/sconfig/sconfig --help`), it shows its command-line options.
|
||||
The exact options might vary slightly, but typically include:
|
||||
|
||||
```text
|
||||
usage: sconfig <options>
|
||||
|
||||
-c | --output_c : Path to output static.c file (required)
|
||||
-r | --output_h : Path to header static.h file (required)
|
||||
-d | --output_d : Path to header static_devices.h file (required)
|
||||
-f | --output_f : Path to header static_fw_config.h file (required)
|
||||
-m | --mainboard_devtree : Path to mainboard devicetree file (required)
|
||||
-o | --override_devtree : Path to override devicetree file (optional)
|
||||
-p | --chipset_devtree : Path to chipset/SOC devicetree file (optional)
|
||||
```
|
||||
|
||||
|
||||
### `sconfig` inputs
|
||||
|
||||
The `sconfig` input files `chip.h`, `chipset.cb`, `devicetree.cb`, and
|
||||
`overridetree.cb` were discussed previously. As the usage above shows,
|
||||
the only required input file is the mainboard devicetree (`-m`). The
|
||||
additional devicetree files, `chipset.cb` (`-p`) and `overridetree.cb`
|
||||
(`-o`), are optional. The `chip.h` files do not need to be specified on
|
||||
the command line; their locations are determined by the `chip` directory
|
||||
paths within the `.cb` files.
|
||||
|
||||
Constructing the devicetree input files will be discussed later.
|
||||
|
||||
|
||||
### `sconfig` outputs
|
||||
|
||||
#### `static.c`
|
||||
|
||||
This is the primary C file generated by `sconfig`. It contains the static
|
||||
definitions of the device tree structures, including device nodes, bus
|
||||
links, and register configuration data.
|
||||
|
||||
For historic reasons, `static.c` is generated in the
|
||||
`build/mainboard/<VENDOR>/<BOARD>` directory.
|
||||
|
||||
|
||||
#### `static.h`
|
||||
|
||||
The `static.h` file is the main header file included by most coreboot C
|
||||
files that need access to the devicetree data. It is included by
|
||||
`src/include/device/device.h`, which provides the primary API
|
||||
(definitions, structures, function prototypes) for interacting with the
|
||||
devicetree-generated output.
|
||||
|
||||
`static.h` used to contain all generated declarations directly. As of
|
||||
October 2020, it simply includes the other two generated header files
|
||||
(`static_devices.h` and `static_fw_config.h`). This separation allows
|
||||
the firmware config options (`fw_config`) to be used independently, for
|
||||
example, by a payload.
|
||||
|
||||
|
||||
#### `static_devices.h`
|
||||
|
||||
The file `static_devices.h` contains `extern` declarations for all the
|
||||
device structures (`struct device`) defined in `static.c`. This allows
|
||||
other C files to reference the generated device tree nodes.
|
||||
|
||||
|
||||
#### `static_fw_config.h`
|
||||
|
||||
`static_fw_config.h` contains only the `FW_CONFIG_FIELD_*` macro results,
|
||||
derived from `fw_config` entries in the devicetree. This makes it easily
|
||||
consumable by payloads or other components needing platform `FW_CONFIG`
|
||||
data without pulling in the full device tree structure.
|
||||
|
||||
|
||||
## Devicetree Example
|
||||
|
||||
|
||||
### A very simple devicetree
|
||||
|
||||
This is the `devicetree.cb` file from
|
||||
`src/mainboard/sifive/hifive-unleashed`, with line numbers added for
|
||||
reference. Non-x86 devicetree files are often simpler than their x86
|
||||
counterparts.
|
||||
|
||||
```text
|
||||
1 # SPDX-License-Identifier: GPL-2.0-only
|
||||
2 chip soc/sifive/fu540
|
||||
3 device cpu_cluster 0 on end
|
||||
4 end
|
||||
```
|
||||
|
||||
This can be broken down as follows:
|
||||
|
||||
Line 1: Comments start with `#`. This line is the SPDX license
|
||||
identifier for the file.
|
||||
|
||||
Line 2: `chip soc/sifive/fu540` starts a block for the SiFive FU540 SoC.
|
||||
`sconfig` will look for code and potentially a `chip.h` in
|
||||
`src/soc/sifive/fu540/`.
|
||||
|
||||
Line 3: `device cpu_cluster 0 on end` defines a device of type
|
||||
`cpu_cluster` with ID `0`. It's marked as enabled (`on`). Since there are
|
||||
no registers or other properties defined between `device` and `end`, this
|
||||
is a simple enablement entry.
|
||||
|
||||
Line 4: `end` closes the block started by the `chip` keyword on line 2.
|
||||
|
||||
|
||||
### Generated files
|
||||
|
||||
Continuing with the simple `sifive/hifive-unleashed` mainboard example,
|
||||
these are the files generated by `sconfig` from the devicetree above (as
|
||||
of mid-2022; exact output can change). Because the input devicetree is
|
||||
minimal, the generated files are also quite sparse.
|
||||
|
||||
|
||||
#### `build/static.h`
|
||||
|
||||
```c
|
||||
#ifndef __STATIC_DEVICE_TREE_H
|
||||
#define __STATIC_DEVICE_TREE_H
|
||||
|
||||
#include <static_fw_config.h>
|
||||
#include <static_devices.h>
|
||||
|
||||
#endif /* __STATIC_DEVICE_TREE_H */
|
||||
```
|
||||
(Includes the other generated headers.)
|
||||
|
||||
|
||||
#### `build/static_devices.h`
|
||||
|
||||
```c
|
||||
#ifndef __STATIC_DEVICES_H
|
||||
#define __STATIC_DEVICES_H
|
||||
#include <device/device.h>
|
||||
/* expose_device_names */
|
||||
#endif /* __STATIC_DEVICE_NAMES_H */
|
||||
```
|
||||
(Includes `device/device.h` but contains no actual device externs beyond
|
||||
the implicit root device, as the simple example didn't define complex
|
||||
devices requiring separate structs.)
|
||||
|
||||
|
||||
#### `build/static_fw_config.h`
|
||||
|
||||
```c
|
||||
#ifndef __STATIC_FW_CONFIG_H
|
||||
#define __STATIC_FW_CONFIG_H
|
||||
#endif /* __STATIC_FW_CONFIG_H */
|
||||
```
|
||||
(Empty because the example `devicetree.cb` did not use `fw_config`.)
|
||||
|
||||
|
||||
#### `build/mainboard/sifive/hifive-unleashed/static.c`
|
||||
|
||||
##### Includes
|
||||
|
||||
```text
|
||||
1 #include <boot/coreboot_tables.h>
|
||||
2 #include <device/device.h>
|
||||
3 #include <device/pci.h>
|
||||
4 #include <fw_config.h>
|
||||
5 #include <static.h>
|
||||
```
|
||||
|
||||
Lines 1-5: Includes header files required for the following structure
|
||||
definitions and macros.
|
||||
|
||||
|
||||
##### Declarations for chip-ops
|
||||
|
||||
```text
|
||||
6
|
||||
7 #if !DEVTREE_EARLY
|
||||
8 __attribute__((weak)) struct chip_operations mainboard_ops = {};
|
||||
9 extern struct chip_operations soc_sifive_fu540_ops;
|
||||
10 #endif
|
||||
```
|
||||
|
||||
Lines 7 & 10: The `ops` structures inside this `#if !DEVTREE_EARLY` block
|
||||
are only relevant and linked in ramstage.
|
||||
|
||||
Lines 8-9: Declarations for `chip_operations` structures. This section
|
||||
expands as more chips are added to the devicetree.
|
||||
* Line 8: `mainboard_ops` is always present. It's defined as `weak`
|
||||
because the mainboard C code may or may not provide this structure.
|
||||
* Line 9: This `extern` is generated by the `chip soc/sifive/fu540`
|
||||
declaration in the `devicetree.cb`. There will be a similar line for
|
||||
every `chip` declared.
|
||||
|
||||
##### `STORAGE` definition
|
||||
|
||||
```text
|
||||
11
|
||||
12 #define STORAGE static __unused DEVTREE_CONST
|
||||
```
|
||||
|
||||
Line 12: This macro conditionally adds `const` based on the build stage.
|
||||
It resolves to `static __unused const` in early stages (pre-RAM) and
|
||||
`static __unused` in ramstage, where the structures might be modified.
|
||||
|
||||
##### Structure definitions
|
||||
|
||||
```text
|
||||
13
|
||||
14
|
||||
15 /* pass 0 */
|
||||
16 STORAGE struct bus dev_root_links[];
|
||||
17 STORAGE struct device _dev_0;
|
||||
18 DEVTREE_CONST struct device * DEVTREE_CONST last_dev = &_dev_0;
|
||||
```
|
||||
|
||||
Lines 16-18: Forward declarations of the static structures generated by
|
||||
`sconfig` based on the devicetree input. `_dev_0` corresponds to the
|
||||
`cpu_cluster 0` device.
|
||||
|
||||
##### Register Structures
|
||||
|
||||
```text
|
||||
19
|
||||
20 /* chip configs */
|
||||
```
|
||||
|
||||
Line 20: This section is empty for this mainboard because the
|
||||
`soc/sifive/fu540/chip.h` file (if it exists) does not define a register
|
||||
structure, or the devicetree did not instantiate it using `register`.
|
||||
Otherwise, this section would contain the static initialization of chip
|
||||
configuration structures based on `register` entries.
|
||||
|
||||
##### `dev_root` structure
|
||||
|
||||
Lines 21-44: `dev_root`. This structure represents the root of the
|
||||
coreboot device tree. It is always generated, regardless of the content
|
||||
of the `devicetree.cb` file. It serves as the entry point for traversing
|
||||
the tree.
|
||||
|
||||
```text
|
||||
21
|
||||
22 /* pass 1 */
|
||||
23 DEVTREE_CONST struct device dev_root = {
|
||||
24 #if !DEVTREE_EARLY
|
||||
25 .ops = &default_dev_ops_root,
|
||||
26 #endif
|
||||
27 .bus = &dev_root_links[0],
|
||||
28 .path = { .type = DEVICE_PATH_ROOT },
|
||||
29 .enabled = 1,
|
||||
30 .hidden = 0,
|
||||
31 .mandatory = 0,
|
||||
32 .on_mainboard = 1,
|
||||
33 .link_list = &dev_root_links[0],
|
||||
34 .sibling = NULL,
|
||||
35 #if !DEVTREE_EARLY
|
||||
36 .chip_ops = &mainboard_ops,
|
||||
37 .name = mainboard_name,
|
||||
38 #endif
|
||||
39 .next=&_dev_0,
|
||||
40 #if !DEVTREE_EARLY
|
||||
41 #if CONFIG(GENERATE_SMBIOS_TABLES)
|
||||
42 #endif
|
||||
43 #endif
|
||||
44 };
|
||||
```
|
||||
|
||||
* Lines 24-26: Points to a default ramstage `device_operation`
|
||||
structure (`default_dev_ops_root`) found in
|
||||
`src/device/root_device.c`. This structure typically does little by
|
||||
default but can be overridden or utilized by mainboard code via the
|
||||
`chip_operations->enable_dev()` hook for tasks like ACPI table
|
||||
generation.
|
||||
* Line 27: `.bus`: Pointer to the bus structure associated with this
|
||||
device. For the root device, this points to its own bus structure.
|
||||
* Line 28: `.path`: The unique path identifier for this device. The type
|
||||
is `DEVICE_PATH_ROOT`.
|
||||
* Lines 29-32: Device status flags.
|
||||
* `enabled`: Set based on `on`/`off` in the devicetree (always on
|
||||
for `dev_root`). Can be modified later (e.g., during enumeration
|
||||
in ramstage).
|
||||
* `hidden`, `mandatory`: Set only by corresponding keywords in the
|
||||
devicetree (not used here).
|
||||
* `on_mainboard`: Indicates the device was defined in the static
|
||||
devicetree, as opposed to being discovered dynamically (e.g., via
|
||||
PCI enumeration). Always true for `dev_root`.
|
||||
* Line 33: `.link_list`: Pointer to the list of child buses attached to
|
||||
this device.
|
||||
* Line 34: `.sibling`: Pointer to the next device at the same level in
|
||||
the tree. Should always be `NULL` for `dev_root`.
|
||||
* Line 36: `.chip_ops`: Pointer to the mainboard's `chip_operations`
|
||||
structure (the `weak` `mainboard_ops`). Although not a physical
|
||||
chip, the mainboard gets this to hook into the boot process like
|
||||
other chips.
|
||||
* Line 37: `.name`: A string identifier, typically the mainboard name,
|
||||
set at build time (from `src/device/root_device.c`).
|
||||
* Line 39: `.next`: Pointer used internally by `sconfig` during tree
|
||||
construction. Points to the next device structure processed (`_dev_0`).
|
||||
|
||||
##### `dev_root_links`
|
||||
|
||||
Lines 45-52: The `dev_root` bus structure array.
|
||||
|
||||
This array (`struct bus`) holds pointers defining the bus topology. Each
|
||||
element represents a link on a bus. `dev_root` acts as the bridge for the
|
||||
top-level bus.
|
||||
|
||||
A new bus structure array is typically created for each distinct bus type
|
||||
or domain originating from a bridge device in the devicetree (e.g., PCI
|
||||
domain 0, LPC bus).
|
||||
|
||||
```text
|
||||
45 STORAGE struct bus dev_root_links[] = {
|
||||
46 [0] = {
|
||||
47 .link_num = 0,
|
||||
48 .dev = &dev_root,
|
||||
49 .children = &_dev_0,
|
||||
50 .next = NULL,
|
||||
51 },
|
||||
52 };
|
||||
```
|
||||
|
||||
* Line 47: `.link_num`: Index of this link within the bus array.
|
||||
* Line 48: `.dev`: Pointer back to the bridge device structure for this
|
||||
bus (`dev_root`).
|
||||
* Line 49: `.children`: Pointer to the first child device structure on
|
||||
this bus (`_dev_0`).
|
||||
* Line 50: `.next`: Pointer to the next bridge device on the *parent*
|
||||
bus. Since `dev_root` has no parent bus, this is `NULL`.
|
||||
|
||||
##### `_dev_0`
|
||||
|
||||
Lines 53-72: The `cpu_cluster` device structure (`_dev_0`).
|
||||
|
||||
This structure corresponds directly to the
|
||||
`device cpu_cluster 0 on end` line in the `devicetree.cb`.
|
||||
|
||||
```text
|
||||
53 STORAGE struct device _dev_0 = {
|
||||
54 #if !DEVTREE_EARLY
|
||||
55 .ops = NULL,
|
||||
56 #endif
|
||||
57 .bus = &dev_root_links[0],
|
||||
58 .path = {.type=DEVICE_PATH_CPU_CLUSTER,{.cpu_cluster={ .cluster = 0x0 }}},
|
||||
59 .enabled = 1,
|
||||
60 .hidden = 0,
|
||||
61 .mandatory = 0,
|
||||
62 .on_mainboard = 1,
|
||||
63 .link_list = NULL,
|
||||
64 .sibling = NULL,
|
||||
65 #if !DEVTREE_EARLY
|
||||
66 .chip_ops = &soc_sifive_fu540_ops,
|
||||
67 #endif
|
||||
68 #if !DEVTREE_EARLY
|
||||
69 #if CONFIG(GENERATE_SMBIOS_TABLES)
|
||||
70 #endif
|
||||
71 #endif
|
||||
72 };
|
||||
```
|
||||
|
||||
* Lines 54-56: `.ops`: Pointer to a `device_operations` structure. This
|
||||
is `NULL` because this entry represents the `chip` itself, not a
|
||||
specific functional sub-device requiring device-level operations. The
|
||||
chip-level operations are handled by `chip_ops`.
|
||||
* Line 57: `.bus`: Pointer to the bus structure this device resides on.
|
||||
Since it's directly under `dev_root`, it points to `dev_root_links[0]`.
|
||||
* Line 58: `.path`: The unique device path structure (defined in
|
||||
`src/include/device/path.h`). Type is `DEVICE_PATH_CPU_CLUSTER`,
|
||||
and the cluster ID is `0`, matching the devicetree entry. This path
|
||||
is used when searching the tree (e.g., with `dev_find_path()`).
|
||||
* Lines 59-62: Enumeration Status. Similar to `dev_root`. `enabled = 1`
|
||||
comes from the `on` keyword.
|
||||
* Line 63: `.link_list`: Pointer to child buses. `NULL` because this
|
||||
`cpu_cluster` device doesn't bridge to any further buses in this
|
||||
simple example.
|
||||
* Line 64: `.sibling`: Pointer to the next device at the same level
|
||||
(i.e., another device directly under `dev_root`). `NULL` as it's the
|
||||
only child.
|
||||
* Lines 65-67: `.chip_ops`: Pointer to the processor's `chip_operations`
|
||||
structure (`soc_sifive_fu540_ops`), used in ramstage for SoC/CPU
|
||||
initialization steps. This link comes from the `chip soc/sifive/fu540`
|
||||
declaration.
|
||||
* Lines 68-71: Placeholder for SMBIOS information, enabled by Kconfig.
|
||||
Not used in this example.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +0,0 @@
|
|||
# coreboot internals
|
||||
|
||||
This section contains documentation about the configuration and
|
||||
programming APIs internal to coreboot
|
||||
|
||||
## Configuration
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
coreboot devicetree <devicetree.md>
|
||||
coreboot devicetree language <devicetree_language.md>
|
||||
Chip Operations <chip_operations.md>
|
||||
Device Operations <device_operations>
|
||||
```
|
||||
|
|
@ -2,284 +2,225 @@
|
|||
|
||||
## Motivation
|
||||
|
||||
The firmware configuration interface in coreboot is designed to support
|
||||
a wide variety of configuration options in that are dictated by the
|
||||
hardware at runtime. This allows a single BIOS image to be used across a
|
||||
wide variety of devices which may have key differences but are otherwise
|
||||
similar enough to use the same coreboot build target.
|
||||
The firmware configuration interface in coreboot is designed to support a wide variety of
|
||||
configuration options in that are dictated by the hardware at runtime. This allows a single
|
||||
BIOS image to be used across a wide variety of devices which may have key differences but are
|
||||
otherwise similar enough to use the same coreboot build target.
|
||||
|
||||
The initial implementation is designed to take advantage of a bitmask
|
||||
returned by the Embedded Controller on Google ChromeOS devices which
|
||||
allows the manufacturer to use the same firmware image across multiple
|
||||
devices by selecting various options at runtime. See the ChromiumOS
|
||||
The initial implementation is designed to take advantage of a bitmask returned by the Embedded
|
||||
Controller on Google ChromeOS devices which allows the manufacturer to use the same firmware
|
||||
image across multiple devices by selecting various options at runtime. See the ChromiumOS
|
||||
[Firmware Config][1] documentation for more information.
|
||||
|
||||
This firmware configuration interface differs from the CMOS option
|
||||
interface in that this bitmask value is not intended as a
|
||||
user-configurable setting as the configuration values must match the
|
||||
actual hardware. In the case where a user was to swap their hardware
|
||||
this value would need to be updated or overridden.
|
||||
|
||||
This firmware configuration interface differs from the CMOS option interface in that this
|
||||
bitmask value is not intended as a user-configurable setting as the configuration values must
|
||||
match the actual hardware. In the case where a user was to swap their hardware this value
|
||||
would need to be updated or overridden.
|
||||
|
||||
## Device Presence
|
||||
|
||||
One common example of why a firmware configuration interface is
|
||||
important is determining if a device is present in the system. With some
|
||||
bus topologies and hardware mechanisms it is possible to probe and
|
||||
enumerate this at runtime:
|
||||
One common example of why a firmware configuration interface is important is determining if a
|
||||
device is present in the system. With some bus topologies and hardware mechanisms it is
|
||||
possible to probe and enumerate this at runtime:
|
||||
|
||||
- PCI is a self-discoverable bus and is very easy to handle.
|
||||
- I2C devices can often be probed with a combination of bus and address.
|
||||
- The use of GPIOs with external strap to ground or different voltages
|
||||
can be used to detect presence of a device.
|
||||
- The use of GPIOs with external strap to ground or different voltages can be used to detect
|
||||
presence of a device.
|
||||
|
||||
However there are several cases where this is insufficient:
|
||||
|
||||
- I2C peripherals that require different drivers but have the same bus
|
||||
address cannot be uniquely identified at runtime.
|
||||
- A mainboard may be designed with multiple daughter board combinations
|
||||
which contain devices and configurations that cannot be detected.
|
||||
- While presence detect GPIOs are a convenient way for a single device
|
||||
presence, they are unable to distinguish between different devices so
|
||||
it can require a large number of GPIOs to support relatively few
|
||||
options.
|
||||
- I2C peripherals that require different drivers but have the same bus address cannot be
|
||||
uniquely identified at runtime.
|
||||
- A mainboard may be designed with multiple daughter board combinations which contain devices
|
||||
and configurations that cannot be detected.
|
||||
- While presence detect GPIOs are a convenient way for a single device presence, they are
|
||||
unable to distinguish between different devices so it can require a large number of GPIOs to
|
||||
support relatively few options.
|
||||
|
||||
This presence detection can impact different stages of boot:
|
||||
|
||||
|
||||
### ACPI
|
||||
|
||||
Devices that are not present should not provide an ACPI device
|
||||
indicating that they are present or the operating system may not be able
|
||||
to handle it correctly.
|
||||
|
||||
The ACPI devices are largely driven by chips defined in the mainboard
|
||||
`devicetree.cb` and the variant overridetree.cb. This means it is
|
||||
important to be able to specify when a device is present or not directly
|
||||
in `devicetree.cb` itself. Otherwise each mainboard needs custom code to
|
||||
parse the tree and disable unused devices.
|
||||
Devices that are not present should not provide an ACPI device indicating that they are
|
||||
present or the operating system may not be able to handle it correctly.
|
||||
|
||||
The ACPI devices are largely driven by chips defined in the mainboard `devicetree.cb` and
|
||||
the variant overridetree.cb. This means it is important to be able to specify when a device
|
||||
is present or not directly in `devicetree.cb` itself. Otherwise each mainboard needs custom
|
||||
code to parse the tree and disable unused devices.
|
||||
|
||||
### GPIO
|
||||
|
||||
GPIOs with multiple functions may need to be configured correctly
|
||||
depending on the attached device. Given the wide variety of GPIO
|
||||
configuration possibilities it is not feasible to specify all
|
||||
combinations directly in `devicetree.cb` and it is best left to code
|
||||
provided by the mainboard.
|
||||
|
||||
GPIOs with multiple functions may need to be configured correctly depending on the attached
|
||||
device. Given the wide variety of GPIO configuration possibilities it is not feasible to
|
||||
specify all combinations directly in `devicetree.cb` and it is best left to code provided by
|
||||
the mainboard.
|
||||
|
||||
### FSP UPD
|
||||
|
||||
Enabling and disabling devices may require altering FSP UPD values that
|
||||
are provided to the various stages of FSP. These options are also not
|
||||
easy to specify multiple times for different configurations in
|
||||
`devicetree.cb` and can be provided by the mainboard as code.
|
||||
|
||||
Enabling and disabling devices may require altering FSP UPD values that are provided to the
|
||||
various stages of FSP. These options are also not easy to specify multiple times for
|
||||
different configurations in `devicetree.cb` and can be provided by the mainboard as code.
|
||||
|
||||
## Firmware Configuration Interface
|
||||
|
||||
The firmware configuration interface can be enabled by selecting
|
||||
`CONFIG_FW_CONFIG` and also providing a source for the value by defining
|
||||
an additional Kconfig option defined below.
|
||||
|
||||
If the firmware configuration interface is disabled via Kconfig then all
|
||||
probe attempts will return true.
|
||||
The firmware configuration interface can be enabled by selecting `CONFIG_FW_CONFIG` and also
|
||||
providing a source for the value by defining an additional Kconfig option defined below.
|
||||
|
||||
If the firmware configuration interface is disabled via Kconfig then all probe attempts will
|
||||
return true.
|
||||
|
||||
## Firmware Configuration Value
|
||||
|
||||
The 64-bit value used as the firmware configuration bitmask is meant to
|
||||
be determined at runtime but could also be defined at compile time if
|
||||
needed.
|
||||
|
||||
There are two supported sources for providing this information to
|
||||
coreboot.
|
||||
The 64-bit value used as the firmware configuration bitmask is meant to be determined at runtime
|
||||
but could also be defined at compile time if needed.
|
||||
|
||||
There are two supported sources for providing this information to coreboot.
|
||||
|
||||
### CBFS
|
||||
|
||||
The value can be provided with a 64-bit raw value in CBFS that is read
|
||||
by coreboot. The value can be set at build time but also adjusted in an
|
||||
existing image with `cbfstool`.
|
||||
The value can be provided with a 64-bit raw value in CBFS that is read by coreboot. The value
|
||||
can be set at build time but also adjusted in an existing image with `cbfstool`.
|
||||
|
||||
To enable this select the `CONFIG_FW_CONFIG_CBFS` option in the build
|
||||
configuration and add a raw 64-bit value to CBFS with the name of the
|
||||
current prefix at `CONFIG_FW_PREFIX/fw_config`.
|
||||
|
||||
When `fw_config_probe_device()` or `fw_config_probe()` is called it will
|
||||
look for the specified file in CBFS use the value it contains when
|
||||
matching fields and options.
|
||||
To enable this select the `CONFIG_FW_CONFIG_CBFS` option in the build configuration and add a
|
||||
raw 64-bit value to CBFS with the name of the current prefix at `CONFIG_FW_PREFIX/fw_config`.
|
||||
|
||||
When `fw_config_probe_device()` or `fw_config_probe()` is called it will look for the specified
|
||||
file in CBFS use the value it contains when matching fields and options.
|
||||
|
||||
### Embedded Controller
|
||||
|
||||
Google ChromeOS devices support an Embedded Controller interface for
|
||||
reading and writing the firmware configuration value, along with other
|
||||
board-specific information. It is possible for coreboot to read this
|
||||
value at boot on systems that support this feature.
|
||||
Google ChromeOS devices support an Embedded Controller interface for reading and writing the
|
||||
firmware configuration value, along with other board-specific information. It is possible for
|
||||
coreboot to read this value at boot on systems that support this feature.
|
||||
|
||||
This option is selected by default for the mainboards that use it with
|
||||
`CONFIG_FW_CONFIG_CHROME_EC_CBI` and it is not typically necessary to
|
||||
adjust the value. It is possible by enabling the CBFS source and
|
||||
coreboot will look in CBFS first for a valid value before asking the
|
||||
embedded controller.
|
||||
`CONFIG_FW_CONFIG_CHROME_EC_CBI` and it is not typically necessary to adjust the value. It is
|
||||
possible by enabling the CBFS source and coreboot will look in CBFS first for a valid value
|
||||
before asking the embedded controller.
|
||||
|
||||
It is also possible to adjust the value in the embedded controller
|
||||
*(after disabling write protection)* with the `ectool` command in a
|
||||
ChromeOS environment.
|
||||
It is also possible to adjust the value in the embedded controller *(after disabling write
|
||||
protection)* with the `ectool` command in a ChromeOS environment.
|
||||
|
||||
For more information on the firmware configuration field on ChromeOS
|
||||
devices see the Chromium documentation for [Firmware Config][1] and
|
||||
[Board Info][2].
|
||||
For more information on the firmware configuration field on ChromeOS devices see the Chromium
|
||||
documentation for [Firmware Config][1] and [Board Info][2].
|
||||
|
||||
[1]: http://chromium.googlesource.com/chromiumos/docs/+/HEAD/design_docs/firmware_config.md
|
||||
[2]: http://chromium.googlesource.com/chromiumos/docs/+/HEAD/design_docs/cros_board_info.md
|
||||
|
||||
|
||||
## Firmware Configuration Table
|
||||
|
||||
The firmware configuration table itself is defined in the mainboard
|
||||
`devicetree.cb` with special tokens for defining fields and options.
|
||||
The firmware configuration table itself is defined in the mainboard `devicetree.cb` with
|
||||
special tokens for defining fields and options.
|
||||
|
||||
The table itself is enclosed in a `fw_config` token and terminated with
|
||||
`end` and it contains a mix of field and option definitions.
|
||||
The table itself is enclosed in a `fw_config` token and terminated with `end` and it contains
|
||||
a mix of field and option definitions.
|
||||
|
||||
Each field is defined by providing the field name and the start and end
|
||||
bit marking the exact location in the bitmask. Field names must be at
|
||||
least three characters long in order to satisfy the sconfig parser
|
||||
requirements and they must be unique with non-overlapping masks.
|
||||
Each field is defined by providing the field name and the start and end bit marking the exact
|
||||
location in the bitmask. Field names must be at least three characters long in order to
|
||||
satisfy the sconfig parser requirements and they must be unique with non-overlapping masks.
|
||||
|
||||
```text
|
||||
field <name> <start-bit> <end-bit> [option...] end
|
||||
```
|
||||
field <name> <start-bit> <end-bit> [option...] end
|
||||
|
||||
For single-bit fields only one number is needed:
|
||||
|
||||
```text
|
||||
field <name> <bit> [option...] end
|
||||
```
|
||||
field <name> <bit> [option...] end
|
||||
|
||||
A field definition can also contain multiple sets of bit masks, which
|
||||
can be dis-contiguous. They are treated as if they are contiguous when
|
||||
defining option values. This allows for extending fields even after the
|
||||
bits after its current masks are occupied.
|
||||
A field definition can also contain multiple sets of bit masks, which can be dis-contiguous.
|
||||
They are treated as if they are contiguous when defining option values. This allows for
|
||||
extending fields even after the bits after its current masks are occupied.
|
||||
|
||||
```text
|
||||
field <name> <start-bit0> <end-bit0> | <start-bit1> <end-bit1> | ...
|
||||
```
|
||||
field <name> <start-bit0> <end-bit0> | <start-bit1> <end-bit1> | ...
|
||||
|
||||
For example, if more audio options need to be supported:
|
||||
|
||||
```text
|
||||
field AUDIO 3 3
|
||||
option AUDIO_0 0
|
||||
option AUDIO_1 1
|
||||
end
|
||||
field OTHER 4 4
|
||||
...
|
||||
end
|
||||
```
|
||||
field AUDIO 3 3
|
||||
option AUDIO_0 0
|
||||
option AUDIO_1 1
|
||||
end
|
||||
field OTHER 4 4
|
||||
...
|
||||
end
|
||||
|
||||
the following can be done:
|
||||
|
||||
```text
|
||||
field AUDIO 3 3 | 5 5
|
||||
option AUDIO_FOO 0
|
||||
option AUDIO_BLAH 1
|
||||
option AUDIO_BAR 2
|
||||
option AUDIO_BAZ 3
|
||||
end
|
||||
field OTHER 4 4
|
||||
...
|
||||
end
|
||||
```
|
||||
field AUDIO 3 3 | 5 5
|
||||
option AUDIO_FOO 0
|
||||
option AUDIO_BLAH 1
|
||||
option AUDIO_BAR 2
|
||||
option AUDIO_BAZ 3
|
||||
end
|
||||
field OTHER 4 4
|
||||
...
|
||||
end
|
||||
|
||||
In that case, the AUDIO masks are extended like so:
|
||||
|
||||
```c
|
||||
#define FW_CONFIG_FIELD_AUDIO_MASK 0x28
|
||||
#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_FOO_VALUE 0x0
|
||||
#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BLAH_VALUE 0x8
|
||||
#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BAR_VALUE 0x20
|
||||
#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BAz_VALUE 0x28
|
||||
```
|
||||
#define FW_CONFIG_FIELD_AUDIO_MASK 0x28
|
||||
#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_FOO_VALUE 0x0
|
||||
#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BLAH_VALUE 0x8
|
||||
#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BAR_VALUE 0x20
|
||||
#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BAz_VALUE 0x28
|
||||
|
||||
Each `field` definition starts a new block that can be composed of zero
|
||||
or more field options, and it is terminated with `end`.
|
||||
Each `field` definition starts a new block that can be composed of zero or more field options,
|
||||
and it is terminated with `end`.
|
||||
|
||||
Inside the field block the options can be defined by providing the
|
||||
option name and the field value that this option represents when the bit
|
||||
offsets are used to apply a mask and shift. Option names must also be at
|
||||
least three characters for the sconfig parser.
|
||||
Inside the field block the options can be defined by providing the option name and the field
|
||||
value that this option represents when the bit offsets are used to apply a mask and shift.
|
||||
Option names must also be at least three characters for the sconfig parser.
|
||||
|
||||
```text
|
||||
option <name> <value>
|
||||
```
|
||||
option <name> <value>
|
||||
|
||||
It is possible for there to be multiple `fw_config` blocks and for
|
||||
subsequent `field` blocks to add additional `option` definitions to the
|
||||
existing field. These subsequent definitions should not provide the
|
||||
field bitmask as it has already been defined earlier in the file and
|
||||
It is possible for there to be multiple `fw_config` blocks and for subsequent `field` blocks
|
||||
to add additional `option` definitions to the existing field. These subsequent definitions
|
||||
should not provide the field bitmask as it has already been defined earlier in the file and
|
||||
this is just matching an existing field by name.
|
||||
|
||||
```text
|
||||
field <name> [option...] end
|
||||
```
|
||||
field <name> [option...] end
|
||||
|
||||
This allows a baseboard to define the major fields and options in
|
||||
`devicetree.cb` and a board variant to add specific options to fields in
|
||||
or define new fields in the unused bitmask in `overridetree.cb`.
|
||||
|
||||
It is not possible to redefine a field mask or override the value of an
|
||||
existing option this way, only to add new options to a field or new
|
||||
fields to the table.
|
||||
This allows a baseboard to define the major fields and options in `devicetree.cb` and a board
|
||||
variant to add specific options to fields in or define new fields in the unused bitmask in
|
||||
`overridetree.cb`.
|
||||
|
||||
It is not possible to redefine a field mask or override the value of an existing option this
|
||||
way, only to add new options to a field or new fields to the table.
|
||||
|
||||
### Firmware Configuration Table Example
|
||||
|
||||
In this example a baseboard defines a simple boolean feature that is
|
||||
enabled or disabled depending on the value of bit 0, and a field at bits
|
||||
1-2 that indicates which daughter board is attached.
|
||||
|
||||
The baseboard itself defines one daughter board and the variant adds two
|
||||
more possibilities. This way each variant can support multiple possible
|
||||
daughter boards in addition to the one that was defined by the
|
||||
baseboard.
|
||||
In this example a baseboard defines a simple boolean feature that is enabled or disabled
|
||||
depending on the value of bit 0, and a field at bits 1-2 that indicates which daughter board
|
||||
is attached.
|
||||
|
||||
The baseboard itself defines one daughter board and the variant adds two more possibilities.
|
||||
This way each variant can support multiple possible daughter boards in addition to the one
|
||||
that was defined by the baseboard.
|
||||
|
||||
#### devicetree.cb
|
||||
|
||||
```text
|
||||
fw_config
|
||||
field FEATURE 0
|
||||
option DISABLED 0
|
||||
option ENABLED 1
|
||||
fw_config
|
||||
field FEATURE 0
|
||||
option DISABLED 0
|
||||
option ENABLED 1
|
||||
end
|
||||
field DAUGHTER_BOARD 1 2
|
||||
option NONE 0
|
||||
option REFERENCE_DB 1
|
||||
end
|
||||
end
|
||||
field DAUGHTER_BOARD 1 2
|
||||
option NONE 0
|
||||
option REFERENCE_DB 1
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
#### overridetree.cb
|
||||
|
||||
```text
|
||||
fw_config
|
||||
field DAUGHTER_BOARD
|
||||
option VARIANT_DB_ONE 2
|
||||
option VARIANT_DB_TWO 3
|
||||
fw_config
|
||||
field DAUGHTER_BOARD
|
||||
option VARIANT_DB_ONE 2
|
||||
option VARIANT_DB_TWO 3
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The result of this table defined in `devicetree.cb` is a list of
|
||||
constants that can be used to check if fields match the firmware
|
||||
configuration options determined at runtime with a simple check of the
|
||||
field mask and the option value.
|
||||
|
||||
The result of this table defined in `devicetree.cb` is a list of constants that can be used
|
||||
to check if fields match the firmware configuration options determined at runtime with a
|
||||
simple check of the field mask and the option value.
|
||||
|
||||
#### static.h
|
||||
|
||||
|
|
@ -305,96 +246,73 @@ field mask and the option value.
|
|||
#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_VALUE 0x00000006
|
||||
```
|
||||
|
||||
|
||||
## Device Probing
|
||||
|
||||
One use of the firmware configuration interface in devicetree is to
|
||||
allow device probing to be specified directly with the devices
|
||||
themselves. A new `probe` token is introduced to allow a device to be
|
||||
probed by field and option name. Multiple `probe` entries may be present
|
||||
for each device and any successful probe will consider the device to be
|
||||
present.
|
||||
|
||||
One use of the firmware configuration interface in devicetree is to allow device probing to be
|
||||
specified directly with the devices themselves. A new `probe` token is introduced to allow a
|
||||
device to be probed by field and option name. Multiple `probe` entries may be present for
|
||||
each device and any successful probe will consider the device to be present.
|
||||
|
||||
### Probing Example
|
||||
|
||||
Continuing with the previous example this device would be considered
|
||||
present if the field `DAUGHTER_BOARD` was set to either `VARIANT_DB_ONE`
|
||||
or `VARIANT_DB_TWO`:
|
||||
|
||||
Continuing with the previous example this device would be considered present if the field
|
||||
`DAUGHTER_BOARD` was set to either `VARIANT_DB_ONE` or `VARIANT_DB_TWO`:
|
||||
|
||||
#### overridetree.cb
|
||||
|
||||
```text
|
||||
chip drivers/generic/example
|
||||
device generic 0 on
|
||||
probe DAUGHTER_BOARD VARIANT_DB_ONE
|
||||
probe DAUGHTER_BOARD VARIANT_DB_TWO
|
||||
chip drivers/generic/example
|
||||
device generic 0 on
|
||||
probe DAUGHTER_BOARD VARIANT_DB_ONE
|
||||
probe DAUGHTER_BOARD VARIANT_DB_TWO
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
If the field were set to any other option, including `NONE` and
|
||||
`REFERENCE_DB` and any undefined value then the device would be
|
||||
disabled.
|
||||
|
||||
If the field were set to any other option, including `NONE` and `REFERENCE_DB` and any
|
||||
undefined value then the device would be disabled.
|
||||
|
||||
### Probe Overrides
|
||||
|
||||
When a device is declared with a probe in the baseboard `devicetree.cb`
|
||||
and the same device is also present in the `overridetree.cb` then the
|
||||
probing information from the baseboard is discarded and the override
|
||||
device must provide all necessary probing information.
|
||||
When a device is declared with a probe in the baseboard `devicetree.cb` and the same device
|
||||
is also present in the `overridetree.cb` then the probing information from the baseboard
|
||||
is discarded and the override device must provide all necessary probing information.
|
||||
|
||||
In this example a device is listed in the baseboard with
|
||||
`DAUGHTER_BOARD` field probing for `REFERENCE_DB` as a field option, It
|
||||
is also defined as an override device with the field probing for the
|
||||
`VARIANT_DB_ONE` option instead.
|
||||
|
||||
In this case only the probe listed in the override is checked and a
|
||||
field option of `REFERENCE_DB` will not mark this device present. If
|
||||
both options are desired then the override device must list both. This
|
||||
allows an override device to remove a probe entry that was defined in
|
||||
the baseboard.
|
||||
In this example a device is listed in the baseboard with `DAUGHTER_BOARD` field probing for
|
||||
`REFERENCE_DB` as a field option, It is also defined as an override device with the field
|
||||
probing for the `VARIANT_DB_ONE` option instead.
|
||||
|
||||
In this case only the probe listed in the override is checked and a field option of
|
||||
`REFERENCE_DB` will not mark this device present. If both options are desired then the
|
||||
override device must list both. This allows an override device to remove a probe entry that
|
||||
was defined in the baseboard.
|
||||
|
||||
#### devicetree.cb
|
||||
|
||||
```text
|
||||
chip drivers/generic/example
|
||||
device generic 0 on
|
||||
probe DAUGHTER_BOARD REFERENCE_DB
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
chip drivers/generic/example
|
||||
device generic 0 on
|
||||
probe DAUGHTER_BOARD REFERENCE_DB
|
||||
end
|
||||
end
|
||||
|
||||
#### overridetree.cb
|
||||
|
||||
```text
|
||||
chip drivers/generic/example
|
||||
device generic 0 on
|
||||
probe DAUGHTER_BOARD VARIANT_DB_ONE
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
chip drivers/generic/example
|
||||
device generic 0 on
|
||||
probe DAUGHTER_BOARD VARIANT_DB_ONE
|
||||
end
|
||||
end
|
||||
|
||||
### Automatic Device Probing
|
||||
|
||||
At boot time the firmware configuration interface will walk the device
|
||||
tree and apply any probe entries that were defined in `devicetree.cb`.
|
||||
This probing takes effect before the `BS_DEV_ENUMERATE` step during the
|
||||
boot state machine in ramstage.
|
||||
At boot time the firmware configuration interface will walk the device tree and apply any
|
||||
probe entries that were defined in `devicetree.cb`. This probing takes effect before the
|
||||
`BS_DEV_ENUMERATE` step during the boot state machine in ramstage.
|
||||
|
||||
Devices that have a probe list but do do not find a match are disabled
|
||||
by setting `dev->enabled = 0` but the chip `enable_dev()` and device
|
||||
`enable()` handlers will still be executed to allow any device disable
|
||||
code to execute.
|
||||
|
||||
The result of this probe definition is to provide an array of structures
|
||||
describing each field and option to check.
|
||||
Devices that have a probe list but do do not find a match are disabled by setting
|
||||
`dev->enabled = 0` but the chip `enable_dev()` and device `enable()` handlers will still
|
||||
be executed to allow any device disable code to execute.
|
||||
|
||||
The result of this probe definition is to provide an array of structures describing each
|
||||
field and option to check.
|
||||
|
||||
#### fw_config.h
|
||||
|
||||
|
|
@ -407,43 +325,40 @@ describing each field and option to check.
|
|||
* @value: Value of the option within the mask.
|
||||
*/
|
||||
struct fw_config {
|
||||
const char *field_name;
|
||||
const char *option_name;
|
||||
uint64_t mask;
|
||||
uint64_t value;
|
||||
const char *field_name;
|
||||
const char *option_name;
|
||||
uint64_t mask;
|
||||
uint64_t value;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
#### static.c
|
||||
|
||||
```c
|
||||
STORAGE struct fw_config __devN_probe_list[] = {
|
||||
{
|
||||
.field_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME,
|
||||
.option_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_NAME,
|
||||
.mask = FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK,
|
||||
.value = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_VALUE
|
||||
},
|
||||
{
|
||||
.field_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME,
|
||||
.option_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_NAME,
|
||||
.mask = FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK,
|
||||
.value = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_VALUE
|
||||
},
|
||||
{ }
|
||||
{
|
||||
.field_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME,
|
||||
.option_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_NAME,
|
||||
.mask = FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK,
|
||||
.value = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_VALUE
|
||||
},
|
||||
{
|
||||
.field_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME,
|
||||
.option_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_NAME,
|
||||
.mask = FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK,
|
||||
.value = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_VALUE
|
||||
},
|
||||
{ }
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Runtime Probing
|
||||
|
||||
The device driver probing allows for seamless integration with the
|
||||
mainboard but it is only effective in ramstage and for specific devices
|
||||
declared in devicetree.cb. There are other situations where code may
|
||||
need to probe or check the value of a field in romstage or at other
|
||||
points in ramstage. For this reason it is also possible to use the
|
||||
firmware configuration interface directly.
|
||||
The device driver probing allows for seamless integration with the mainboard but it is only
|
||||
effective in ramstage and for specific devices declared in devicetree.cb. There are other
|
||||
situations where code may need to probe or check the value of a field in romstage or at other
|
||||
points in ramstage. For this reason it is also possible to use the firmware configuration
|
||||
interface directly.
|
||||
|
||||
```c
|
||||
/**
|
||||
|
|
@ -457,21 +372,18 @@ bool fw_config_probe(const struct fw_config *match);
|
|||
|
||||
The argument provided to this function can be created from a macro for easy use:
|
||||
|
||||
```text
|
||||
FW_CONFIG(field, option)
|
||||
```
|
||||
FW_CONFIG(field, option)
|
||||
|
||||
This example has a mainboard check if a feature is disabled and set an
|
||||
FSP UPD before memory training. This example expects that the default
|
||||
value of this `register` is set to `true` in `devicetree.cb` and this
|
||||
code is disabling that feature before FSP is executed.
|
||||
This example has a mainboard check if a feature is disabled and set an FSP UPD before memory
|
||||
training. This example expects that the default value of this `register` is set to `true` in
|
||||
`devicetree.cb` and this code is disabling that feature before FSP is executed.
|
||||
|
||||
```c
|
||||
#include <fw_config.h>
|
||||
|
||||
void mainboard_memory_init_params(FSPM_UPD *mupd)
|
||||
{
|
||||
if (fw_config_probe(FW_CONFIG(FEATURE, DISABLED))
|
||||
mupd->ExampleFeature = false;
|
||||
if (fw_config_probe(FW_CONFIG(FEATURE, DISABLED))
|
||||
mupd->ExampleFeature = false;
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -11,7 +11,4 @@ ABI data consumption <abi-data-consumption.md>
|
|||
Timestamps <timestamp.md>
|
||||
Firmware Configuration Interface <fw_config.md>
|
||||
Relocatable Modules <rmodules.md>
|
||||
Timers, Stopwatch, and Delays <stopwatch.md>
|
||||
Threads <threads.md>
|
||||
Ramstage Bootstates & Bootstate Callbacks <ramstage_bootstates.md>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,514 +0,0 @@
|
|||
# coreboot Ramstage Bootstates & Bootstate Callbacks
|
||||
|
||||
## Introduction
|
||||
|
||||
The coreboot boot process is divided into several discrete phases, one
|
||||
of which is **ramstage**. Ramstage is the phase where the main hardware
|
||||
initialization and device setup occurs after memory initialization.
|
||||
Within ramstage, a state machine called the **bootstate machine**
|
||||
manages the sequence of operations needed to initialize the system,
|
||||
configure devices, and prepare to load and execute the payload (such as
|
||||
a bootloader, operating system, or firmware utility).
|
||||
|
||||
The bootstate machine provides a structured and extensible way to
|
||||
organize code execution during the boot process. It allows for clear
|
||||
separation of concerns between different initialization phases and
|
||||
provides hooks for component-specific code to run at well-defined
|
||||
points.
|
||||
|
||||
**Important Note:** The exact execution order of multiple callbacks
|
||||
registered for the same state and sequence (entry/exit) is not
|
||||
guaranteed. This means that you cannot depend on one call for the
|
||||
state/sequence in any other calls to the same state/sequence. If this
|
||||
ordering is required, join the calls to the two functions into a single
|
||||
function which specifies the order and create a callback to call the
|
||||
top-level function instead of the two individual callbacks.
|
||||
|
||||
|
||||
## Bootstate Machine Architecture
|
||||
|
||||
The bootstate machine's public API is defined in
|
||||
`src/include/bootstate.h`, and its core implementation resides in
|
||||
`src/lib/hardwaremain.c`. At its core, it consists of:
|
||||
|
||||
1. A series of sequential states that represent phases of the boot process
|
||||
2. A mechanism for callback registration to execute code during state transitions
|
||||
3. A framework for blocking and unblocking state transitions
|
||||
4. Timing and debugging facilities to measure and report performance during boot
|
||||
|
||||
|
||||
### Key Data Structures
|
||||
|
||||
The primary public data structure for interacting with the bootstate
|
||||
machine is `struct boot_state_callback`. The internal implementation
|
||||
also uses `struct boot_state` and `struct boot_phase`.
|
||||
|
||||
|
||||
#### Boot State Callback (Public API)
|
||||
|
||||
Callbacks that run during state transitions are defined by this
|
||||
structure in `src/include/bootstate.h`:
|
||||
|
||||
```c
|
||||
struct boot_state_callback {
|
||||
void *arg; // Argument to pass to the callback
|
||||
void (*callback)(void *arg); // Function pointer to the callback
|
||||
struct boot_state_callback *next; // Next callback in linked list (internal use)
|
||||
#if CONFIG(DEBUG_BOOT_STATE)
|
||||
const char *location; // Source location for debugging
|
||||
#endif
|
||||
};
|
||||
```
|
||||
|
||||
#### Boot State Sequence (Public API)
|
||||
|
||||
The boot state sequence type, defined in `src/include/bootstate.h`,
|
||||
specifies when a callback should run relative to the state's main
|
||||
action:
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
BS_ON_ENTRY, // Execute before state function
|
||||
BS_ON_EXIT // Execute after state function
|
||||
} boot_state_sequence_t;
|
||||
```
|
||||
|
||||
|
||||
#### Boot State (Internal Implementation)
|
||||
|
||||
The main internal data structure in `src/lib/hardwaremain.c` is
|
||||
`struct boot_state`, which defines a single state in the bootstate
|
||||
machine:
|
||||
|
||||
```c
|
||||
struct boot_state {
|
||||
const char *name; // Human-readable name of the state
|
||||
boot_state_t id; // Enumerated identifier for the state
|
||||
u8 post_code; // POST code to output during state execution
|
||||
struct boot_phase phases[2]; // Entry and exit phases (internal use)
|
||||
boot_state_t (*run_state)(void *arg); // Function to execute during the state
|
||||
void *arg; // Argument to pass to the run_state function
|
||||
int num_samples; // Counter for timing samples (internal use)
|
||||
bool complete; // Flag indicating if state has completed (internal use)
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
#### Boot Phase (Internal Implementation)
|
||||
|
||||
Each boot state has two internal phases ("entry" and "exit") represented
|
||||
by `struct boot_phase` in `src/lib/hardwaremain.c`:
|
||||
|
||||
```c
|
||||
struct boot_phase {
|
||||
struct boot_state_callback *callbacks; // Linked list of callbacks
|
||||
int blockers; // Counter for blocking state transition
|
||||
};
|
||||
```
|
||||
|
||||
## Bootstate Sequence
|
||||
|
||||
The bootstate machine defines the following sequence of states, executed
|
||||
in order by the `bs_walk_state_machine` function in
|
||||
`src/lib/hardwaremain.c`. The sequence is defined by the `boot_state_t`
|
||||
enum in `src/include/bootstate.h`:
|
||||
|
||||
1. **BS_PRE_DEVICE**: Initial state before any device operations begin
|
||||
2. **BS_DEV_INIT_CHIPS**: Early chip initialization for critical components
|
||||
3. **BS_DEV_ENUMERATE**: Device enumeration (discovering devices on buses)
|
||||
4. **BS_DEV_RESOURCES**: Resource allocation for devices
|
||||
5. **BS_DEV_ENABLE**: Enabling devices that were discovered
|
||||
6. **BS_DEV_INIT**: Device initialization
|
||||
7. **BS_POST_DEVICE**: All device operations have been completed
|
||||
8. **BS_OS_RESUME_CHECK**: Check if we're resuming from a sleep state
|
||||
9. **BS_OS_RESUME**: Handle OS resume process (if needed)
|
||||
10. **BS_WRITE_TABLES**: Write system tables (e.g., ACPI, SMBIOS)
|
||||
11. **BS_PAYLOAD_LOAD**: Load the payload into memory
|
||||
12. **BS_PAYLOAD_BOOT**: Boot the payload
|
||||
|
||||
This sequence forms the backbone of the ramstage execution flow. Each
|
||||
state performs a specific task, runs associated callbacks, and
|
||||
transitions to the next state upon completion, unless blocked.
|
||||
|
||||
|
||||
## Bootstate Details
|
||||
|
||||
### BS_PRE_DEVICE
|
||||
|
||||
**Purpose**: Serves as the initial state before any device tree
|
||||
operations begin.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_pre_device()`: Sets up initial environment and transitions to next
|
||||
state.
|
||||
|
||||
**Usage**: This state is used for initializing core components that need
|
||||
to be set up before any device operations. Examples include:
|
||||
- Setting up global NVRAM variables
|
||||
- Initializing debugging facilities
|
||||
- Preparing ACPI tables or other critical system structures
|
||||
|
||||
|
||||
### BS_DEV_INIT_CHIPS
|
||||
|
||||
**Purpose**: Initializes critical chips early in the boot process.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_dev_init_chips()`: Calls `dev_initialize_chips()` to initialize
|
||||
all chips in the device tree.
|
||||
|
||||
**Notes**: Chip initialization can disable unused devices, which is why
|
||||
it happens before device enumeration.
|
||||
|
||||
|
||||
### BS_DEV_ENUMERATE
|
||||
|
||||
**Purpose**: Discovers devices in the system.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_dev_enumerate()`: Calls `dev_enumerate()` to probe and identify
|
||||
devices.
|
||||
|
||||
**Notes**: During this phase, the system scans buses and detects
|
||||
connected devices.
|
||||
|
||||
|
||||
### BS_DEV_RESOURCES
|
||||
|
||||
**Purpose**: Allocates and assigns resources (I/O, memory, IRQs) to
|
||||
devices.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_dev_resources()`: Calls `dev_configure()` to compute and assign
|
||||
bus resources.
|
||||
|
||||
**Notes**: Resource allocation resolves conflicts and ensures each
|
||||
device has the resources it needs.
|
||||
|
||||
|
||||
### BS_DEV_ENABLE
|
||||
|
||||
**Purpose**: Enables devices in the system.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_dev_enable()`: Calls `dev_enable()` to enable devices on the bus.
|
||||
|
||||
**Notes**: Some devices may be selectively disabled based on hardware
|
||||
configuration or policy.
|
||||
|
||||
|
||||
### BS_DEV_INIT
|
||||
|
||||
**Purpose**: Initializes enabled devices.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_dev_init()`: Calls `dev_initialize()` to initialize devices on the
|
||||
bus.
|
||||
|
||||
**Notes**: This state performs device-specific initialization routines
|
||||
for all enabled devices.
|
||||
|
||||
|
||||
### BS_POST_DEVICE
|
||||
|
||||
**Purpose**: Final state after all device operations have completed.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_post_device()`: Calls `dev_finalize()` to complete any final
|
||||
device operations.
|
||||
|
||||
**Notes**: This state serves as a checkpoint that all device
|
||||
initialization is complete.
|
||||
|
||||
|
||||
### BS_OS_RESUME_CHECK
|
||||
|
||||
**Purpose**: Checks if the system should resume from a sleep state.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_os_resume_check()`: Looks for a wake vector to determine if resume
|
||||
is needed.
|
||||
|
||||
**Notes**: This state branches the boot flow based on whether the system
|
||||
is resuming from a sleep state.
|
||||
|
||||
### BS_OS_RESUME
|
||||
|
||||
**Purpose**: Handles the OS resume process.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_os_resume()`: Calls `acpi_resume()` with the wake vector to resume
|
||||
the OS.
|
||||
|
||||
**Notes**: After successful resume, control is transferred to the OS and
|
||||
does not return to coreboot.
|
||||
|
||||
|
||||
### BS_WRITE_TABLES
|
||||
|
||||
**Purpose**: Writes configuration tables for the payload or OS.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_write_tables()`: Calls `write_tables()` to generate system tables.
|
||||
|
||||
**Notes**: Tables include ACPI, SMBIOS, and other system configuration
|
||||
data.
|
||||
|
||||
|
||||
### BS_PAYLOAD_LOAD
|
||||
|
||||
**Purpose**: Loads the payload into memory.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_payload_load()`: Calls `payload_load()` to load the payload.
|
||||
|
||||
**Notes**: The payload could be a bootloader, an operating system kernel,
|
||||
or a firmware utility.
|
||||
|
||||
|
||||
### BS_PAYLOAD_BOOT
|
||||
|
||||
**Purpose**: Final state that boots the loaded payload.
|
||||
|
||||
**Key Functions**:
|
||||
- `bs_payload_boot()`: Calls `payload_run()` to execute the payload.
|
||||
|
||||
**Notes**: After successful execution, control is transferred to the
|
||||
payload and does not return to coreboot. If execution returns (which
|
||||
indicates an error), a boot failure message is printed.
|
||||
|
||||
|
||||
## Driving the State Machine
|
||||
|
||||
The state machine is driven by the `main()` function in
|
||||
`src/lib/hardwaremain.c`. After initial setup (like initializing the
|
||||
console and CBMEM), it calls `bs_walk_state_machine()`.
|
||||
|
||||
`bs_walk_state_machine()` loops through the defined boot states:
|
||||
1. It identifies the current state.
|
||||
2. Runs all `BS_ON_ENTRY` callbacks for that state.
|
||||
3. Executes the state's specific function (e.g., `bs_dev_enumerate()`).
|
||||
4. Runs all `BS_ON_EXIT` callbacks for that state.
|
||||
5. Transitions to the next state returned by the state function.
|
||||
|
||||
This loop continues until the final state (`BS_PAYLOAD_BOOT` or
|
||||
`BS_OS_RESUME`) transfers control away from coreboot.
|
||||
|
||||
|
||||
## External Functions (Public API)
|
||||
|
||||
The bootstate machine provides several functions in
|
||||
`src/include/bootstate.h` for interacting with states:
|
||||
|
||||
|
||||
### Callback Registration
|
||||
|
||||
```c
|
||||
int boot_state_sched_on_entry(struct boot_state_callback *bscb, boot_state_t state_id);
|
||||
```
|
||||
Schedules a callback to run when entering a state (`BS_ON_ENTRY`).
|
||||
|
||||
```c
|
||||
int boot_state_sched_on_exit(struct boot_state_callback *bscb, boot_state_t state_id);
|
||||
```
|
||||
Schedules a callback to run when exiting a state (`BS_ON_EXIT`).
|
||||
|
||||
|
||||
### State Transition Control
|
||||
|
||||
```c
|
||||
int boot_state_block(boot_state_t state, boot_state_sequence_t seq);
|
||||
```
|
||||
Blocks a state transition from occurring after the specified sequence
|
||||
(entry or exit callbacks). The transition will pause until the block is
|
||||
removed.
|
||||
|
||||
```c
|
||||
int boot_state_unblock(boot_state_t state, boot_state_sequence_t seq);
|
||||
```
|
||||
Removes a previously set block on a state transition.
|
||||
|
||||
|
||||
### Static Callback Registration
|
||||
|
||||
For registering callbacks at compile time, use the `BOOT_STATE_INIT_ENTRY`
|
||||
macro defined in `src/include/bootstate.h`:
|
||||
|
||||
```c
|
||||
BOOT_STATE_INIT_ENTRY(state, when, func, arg)
|
||||
```
|
||||
|
||||
This macro creates a static entry in a special section (`.bs_init`) of
|
||||
the binary. These entries are processed early in `main()` by
|
||||
`boot_state_schedule_static_entries()` to register the callbacks before
|
||||
the state machine starts running.
|
||||
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The bootstate machine behavior can be modified through Kconfig options:
|
||||
|
||||
|
||||
### DEBUG_BOOT_STATE
|
||||
|
||||
```
|
||||
config DEBUG_BOOT_STATE
|
||||
bool "Debug boot state machine"
|
||||
default n
|
||||
help
|
||||
Control debugging of the boot state machine. When selected displays
|
||||
the state boundaries in ramstage.
|
||||
```
|
||||
|
||||
When enabled, this option causes the bootstate machine to output
|
||||
debugging information via `printk`, including:
|
||||
- State transition notifications (`Entering/Exiting <state> state.`)
|
||||
- Callback execution details (address, source location, execution time)
|
||||
- Timing information for state execution phases (entry, run, exit)
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Adding a New Bootstate Callback
|
||||
|
||||
To register a function to be called when entering a specific state using
|
||||
the static registration method:
|
||||
|
||||
```c
|
||||
// Function to be called
|
||||
static void my_init_function(void *arg)
|
||||
{
|
||||
// Initialization code
|
||||
printk(BIOS_DEBUG, "My initialization running...\n");
|
||||
}
|
||||
|
||||
// Register the callback at compile time
|
||||
BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, my_init_function, NULL);
|
||||
```
|
||||
|
||||
|
||||
### Runtime Callback Registration
|
||||
|
||||
For dynamic callback registration during runtime (e.g., within another
|
||||
callback or state function):
|
||||
|
||||
```c
|
||||
static void runtime_init(void *arg)
|
||||
{
|
||||
// Do something
|
||||
}
|
||||
|
||||
void register_my_callbacks(void)
|
||||
{
|
||||
// Allocate or define a static callback structure
|
||||
static struct boot_state_callback bscb = {
|
||||
.callback = runtime_init,
|
||||
.arg = NULL,
|
||||
// .location is automatically handled if DEBUG_BOOT_STATE=y
|
||||
};
|
||||
|
||||
// Schedule it
|
||||
boot_state_sched_on_entry(&bscb, BS_DEV_ENABLE);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Blocking State Transition
|
||||
|
||||
To temporarily block a state from progressing until a condition is met,
|
||||
often used with timers:
|
||||
|
||||
```c
|
||||
#include <timer.h> // Required for timer functions
|
||||
|
||||
static void wait_for_device(void *arg)
|
||||
{
|
||||
if (!device_is_ready()) {
|
||||
// Block the transition *after* BS_DEV_INIT exits
|
||||
boot_state_block(BS_DEV_INIT, BS_ON_EXIT);
|
||||
|
||||
// Schedule a function to check again later (e.g., after 100us)
|
||||
// Assume schedule_timer exists and works appropriately
|
||||
schedule_timer(check_device_ready, NULL, 100);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_device_ready(void *arg)
|
||||
{
|
||||
if (device_is_ready()) {
|
||||
// Device is ready, unblock the transition
|
||||
boot_state_unblock(BS_DEV_INIT, BS_ON_EXIT);
|
||||
} else {
|
||||
// Still not ready, check again later
|
||||
schedule_timer(check_device_ready, NULL, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Register the initial check to run when entering BS_DEV_INIT
|
||||
BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, wait_for_device, NULL);
|
||||
```
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
### When Working with Bootstates
|
||||
|
||||
1. **Choose the appropriate state**: Register callbacks at the earliest
|
||||
state where all dependencies are guaranteed to be initialized, but no
|
||||
earlier. Check the state descriptions and the functions called by
|
||||
each state function (`bs_*`) in `hardwaremain.c`.
|
||||
|
||||
2. **Keep callbacks focused**: Each callback should perform a specific,
|
||||
related task and avoid complex operations that might significantly
|
||||
delay the boot process.
|
||||
|
||||
3. **Consider dependencies carefully**: Ensure any hardware, data
|
||||
structures, or other resources your callback needs are available and
|
||||
initialized at the chosen state and sequence (`BS_ON_ENTRY` vs.
|
||||
`BS_ON_EXIT`).
|
||||
|
||||
4. **Do not rely on callback order**: Remember that the execution order
|
||||
of callbacks within the same state and sequence is not guaranteed.
|
||||
Callbacks should be self-contained and not depend on side effects from
|
||||
other callbacks that might run before or after them in the same phase.
|
||||
|
||||
5. **Use blocking sparingly**: The blocking mechanism is powerful for
|
||||
synchronization but can complicate the boot flow and make debugging
|
||||
harder if overused. Always ensure a corresponding `boot_state_unblock`
|
||||
call will eventually run.
|
||||
|
||||
6. **Leverage compile-time registration**: Prefer using
|
||||
`BOOT_STATE_INIT_ENTRY` for callbacks whenever possible. It makes the
|
||||
registration explicit and easier to find. Runtime registration is
|
||||
necessary only when the need for the callback is determined dynamically.
|
||||
|
||||
7. **Debug with timestamps and `DEBUG_BOOT_STATE`**: Use the timestamp API
|
||||
(`timestamp_add_now()`) and enable `DEBUG_BOOT_STATE` to measure
|
||||
callback execution time, identify bottlenecks, and understand the
|
||||
flow during development.
|
||||
|
||||
8. **Document state-specific behavior**: When adding callbacks, add
|
||||
comments explaining why they are placed in a particular state and
|
||||
sequence.
|
||||
|
||||
9. **Be careful with late states**: Avoid registering non-essential
|
||||
callbacks in `BS_PAYLOAD_BOOT` or `BS_OS_RESUME`. Callbacks on
|
||||
`BS_ON_EXIT` for these states are disallowed by compile-time asserts,
|
||||
as coreboot is about to transfer control.
|
||||
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Boot Stages in coreboot](https://doc.coreboot.org/getting_started/architecture.html):
|
||||
Overview of all coreboot boot stages.
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- `src/include/bootstate.h`: Public API definitions (callbacks, enums,
|
||||
scheduling/blocking functions, static registration macro).
|
||||
- `src/lib/hardwaremain.c`: Internal implementation (state machine driver,
|
||||
state definitions, state functions).
|
||||
- `src/ec/google/wilco/chip.c`: Example of bootstate callback usage.
|
||||
- `src/mainboard/prodrive/hermes/mainboard.c`: Examples of mainboard-specific
|
||||
bootstate callbacks.
|
||||
|
|
@ -35,24 +35,8 @@ One can split the rmodules in two different kinds:
|
|||
1. coreboot stages (postcar, ramstage)
|
||||
2. simple binaries (smm, smmstub, sipi\_vector)
|
||||
|
||||
There is one important difference in how they are handled:
|
||||
The simple binaries are compiled into rmodules the same as coreboot
|
||||
stages are, but the simple binaries are always directly linked to a
|
||||
stage. Since rmodules are ELF files as well, we can easily link them
|
||||
to the stages in which we need them (usually postcar or ramstage).
|
||||
So they are not really separate modules anymore, but still retain
|
||||
the ability to accept rmodule\_parameters.
|
||||
Since the simple binaries are usually very small, linking them directly
|
||||
into the stage (e.g. ramstage or postcar) avoids having to fetch them
|
||||
from CBFS and running all that code to fetch a few hundred bytes of
|
||||
code. So the build system handles them as follows:
|
||||
1. create rmodule (which is an ELF file) from source files
|
||||
2. remove all the ELF headers and sections that are not loadable using
|
||||
`objcopy -O binary`
|
||||
3. from this, create an object file, which usually has the self invented
|
||||
.manual file extension, which can be linked to the appropriate stage
|
||||
4. add the generated .manual file as "source" file to the stage we want
|
||||
to link it to
|
||||
They are actually handled the same by the build system and only differ
|
||||
in the fact, that they are either coreboot stages or they are not.
|
||||
|
||||
In the end the ELF files will have three different ELF sections,
|
||||
which are all created by the rmodtool.
|
||||
|
|
|
|||
|
|
@ -1,910 +0,0 @@
|
|||
# coreboot Timers, Stopwatch, Delays, and Associated Callbacks
|
||||
|
||||
## Introduction
|
||||
|
||||
coreboot provides several mechanisms for handling time, including
|
||||
high-precision stopwatches for profiling, simple delay functions for
|
||||
hardware timing, and a monotonic timer system that forms the foundation
|
||||
for these features. It also supports scheduling timer callbacks for
|
||||
deferred execution. These tools are crucial for performance
|
||||
optimization, debugging, ensuring proper hardware timing, and managing
|
||||
asynchronous events during the boot process.
|
||||
|
||||
This document describes the core monotonic timer, the delay functions
|
||||
(`udelay`, `mdelay`, `delay`), the stopwatch API (`struct stopwatch`),
|
||||
and the timer callback mechanism.
|
||||
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The timing facilities are layered:
|
||||
|
||||
- **Platform Timer:** Hardware-specific implementations provide a raw
|
||||
time source (e.g., LAPIC timer, Time Base register). These are often
|
||||
abstracted by `timer_monotonic_get()`.
|
||||
- **Monotonic Timer:** (`src/include/timer.h`, `src/lib/timer.c`)
|
||||
Provides a consistent time source (`struct mono_time`) counting
|
||||
microseconds since timer initialization. Requires
|
||||
`CONFIG(HAVE_MONOTONIC_TIMER)`.
|
||||
- **Delay Functions:** (`src/include/delay.h`, `src/lib/delay.c`,
|
||||
`src/lib/timer.c`) Simple blocking delays (`udelay`, `mdelay`,
|
||||
`delay`). The generic `udelay` uses the stopwatch API if yielding via
|
||||
`thread_yield_microseconds()` is not performed.
|
||||
- **Stopwatch API:** (`src/include/timer.h`) A lightweight wrapper
|
||||
around the monotonic timer for measuring durations and handling
|
||||
timeouts (`struct stopwatch`,`wait_us`,`wait_ms`).
|
||||
- **Timer Callbacks:** (`src/include/timer.h`) Allows scheduling
|
||||
functions to be called after a specified delay
|
||||
(`struct timeout_callback`, `timer_sched_callback()`, `timers_run()`).
|
||||
|
||||
These APIs are generally designed to be thread-safe and usable across
|
||||
different boot stages.
|
||||
|
||||
|
||||
## Core Monotonic Timer
|
||||
|
||||
The foundation is the monotonic timer, enabled by
|
||||
`CONFIG(HAVE_MONOTONIC_TIMER)`. It provides a time source that
|
||||
continuously increases from an arbitrary starting point (usually near
|
||||
timer initialization).
|
||||
|
||||
### Data Structures
|
||||
|
||||
#### struct mono_time
|
||||
|
||||
```c
|
||||
struct mono_time {
|
||||
uint64_t microseconds; // Time in microseconds since timer init
|
||||
};
|
||||
```
|
||||
Represents a point in monotonic time. Direct field access outside core
|
||||
timer code is discouraged; use helper functions.
|
||||
|
||||
### API Functions
|
||||
|
||||
#### timer_monotonic_get
|
||||
|
||||
```c
|
||||
void timer_monotonic_get(struct mono_time *mt);
|
||||
```
|
||||
Retrieves the current monotonic time.
|
||||
|
||||
**Parameters:**
|
||||
- `mt`: Pointer to a `struct mono_time` to store the current time.
|
||||
|
||||
**Preconditions:**
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` must be enabled.
|
||||
- The underlying platform timer must be functional. Platform
|
||||
implementations may require an `init_timer()` call.
|
||||
|
||||
**Postconditions:**
|
||||
- `mt` contains the current monotonic time in microseconds.
|
||||
|
||||
**Note:** If `CONFIG(HAVE_MONOTONIC_TIMER)` is *not* enabled, functions
|
||||
relying on it (like stopwatch functions) will generally fall back to
|
||||
basic behavior (e.g., returning 0 durations, immediate expiration
|
||||
checks).
|
||||
|
||||
#### init_timer
|
||||
|
||||
```c
|
||||
void init_timer(void);
|
||||
```
|
||||
|
||||
Platform-specific timer initialization. The generic version in
|
||||
`src/lib/timer.c` is a weak empty function
|
||||
`__weak void init_timer(void) { /* do nothing */ }`. Platforms needing
|
||||
explicit timer setup (e.g., configuring frequency, enabling the
|
||||
counter) must provide their own strong implementation. Check platform
|
||||
code (`src/cpu/`, `src/soc/`) for details.
|
||||
|
||||
#### `mono_time` Helper Functions
|
||||
|
||||
`src/include/timer.h` also provides several inline helper functions for
|
||||
manipulating `struct mono_time` values:
|
||||
- `mono_time_set_usecs()`, `mono_time_set_msecs()`: Set time.
|
||||
- `mono_time_add_usecs()`, `mono_time_add_msecs()`: Add duration.
|
||||
- `mono_time_cmp()`: Compare two times.
|
||||
- `mono_time_after()`, `mono_time_before()`: Check time ordering.
|
||||
- `mono_time_diff_microseconds()`: Calculate difference between two
|
||||
times.
|
||||
|
||||
Refer to `src/include/timer.h` for their exact definitions.
|
||||
|
||||
|
||||
## Delay Functions
|
||||
|
||||
coreboot provides simple functions for introducing delays.
|
||||
|
||||
### udelay
|
||||
|
||||
```c
|
||||
void udelay(unsigned int usecs);
|
||||
```
|
||||
Delays execution for *at least* the specified number of microseconds.
|
||||
|
||||
**Parameters:**
|
||||
- `usecs`: Number of microseconds to delay.
|
||||
|
||||
**Implementation Notes:**
|
||||
- The generic implementation in `src/lib/timer.c` first attempts to
|
||||
yield using `thread_yield_microseconds()` if `CONFIG(HAS_THREADS)` is
|
||||
enabled. If yielding handles the required delay (function returns 0),
|
||||
`udelay` returns immediately, allowing other threads to run.
|
||||
- If `thread_yield_microseconds()` does not handle the delay (returns
|
||||
non-zero), or if threading is disabled, the generic `udelay` falls
|
||||
back to a busy-wait using the stopwatch API
|
||||
(`stopwatch_init_usecs_expire()` and
|
||||
`stopwatch_wait_until_expired()`).
|
||||
- Architecture-specific implementations (e.g., in `src/arch/`) may
|
||||
override the generic one for better precision or efficiency.
|
||||
- Adds 1 microsecond internally to ensure the delay is *at least* the
|
||||
requested duration.
|
||||
|
||||
**Preconditions:**
|
||||
- If relying on the stopwatch fallback,
|
||||
`CONFIG(HAVE_MONOTONIC_TIMER)` is needed.
|
||||
- Underlying timer (if used) must be initialized (potentially via a
|
||||
platform `init_timer()`).
|
||||
- The delay value should be reasonable (overly large values might cause
|
||||
issues or unexpected behavior depending on the implementation).
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
#include <delay.h>
|
||||
|
||||
// Wait for 100 microseconds
|
||||
udelay(100);
|
||||
```
|
||||
|
||||
### mdelay
|
||||
|
||||
```c
|
||||
void mdelay(unsigned int msecs);
|
||||
```
|
||||
Delays execution for *at least* the specified number of milliseconds.
|
||||
|
||||
**Parameters:**
|
||||
- `msecs`: Number of milliseconds to delay.
|
||||
|
||||
**Implementation Notes:**
|
||||
- The generic implementation in `src/lib/delay.c` simply calls
|
||||
`udelay(1000)` in a loop `msecs` times.
|
||||
- Therefore, it inherits the behavior of `udelay`, including the attempt
|
||||
to yield first if `udelay` supports it.
|
||||
|
||||
**Preconditions:**
|
||||
- Same as `udelay`.
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
#include <delay.h>
|
||||
|
||||
// Wait for 50 milliseconds
|
||||
mdelay(50);
|
||||
```
|
||||
|
||||
### delay
|
||||
|
||||
```c
|
||||
void delay(unsigned int secs);
|
||||
```
|
||||
Delays execution for *at least* the specified number of seconds.
|
||||
|
||||
**Parameters:**
|
||||
- `secs`: Number of seconds to delay.
|
||||
|
||||
**Implementation Notes:**
|
||||
- The generic implementation in `src/lib/delay.c` simply calls
|
||||
`mdelay(1000)` in a loop `secs` times.
|
||||
- Inherits the behavior of `mdelay` and `udelay`.
|
||||
|
||||
**Preconditions:**
|
||||
- Same as `udelay`.
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
#include <delay.h>
|
||||
|
||||
// Wait for 2 seconds
|
||||
delay(2);
|
||||
```
|
||||
|
||||
|
||||
## Stopwatch API
|
||||
|
||||
The stopwatch API provides a convenient way to measure time durations
|
||||
and implement timeouts based on the monotonic timer.
|
||||
|
||||
### Data Structures
|
||||
|
||||
#### struct stopwatch
|
||||
|
||||
```c
|
||||
#include <timer.h> // For struct stopwatch and struct mono_time
|
||||
|
||||
struct stopwatch {
|
||||
struct mono_time start; // Time when stopwatch was started or initialized
|
||||
struct mono_time current; // Time when stopwatch was last ticked
|
||||
struct mono_time expires; // Expiration time for timeout operations
|
||||
};
|
||||
```
|
||||
Holds the state for a stopwatch instance.
|
||||
|
||||
### API Functions
|
||||
|
||||
#### Initialization Functions
|
||||
|
||||
##### stopwatch_init
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
static inline void stopwatch_init(struct stopwatch *sw);
|
||||
```
|
||||
Initializes a stopwatch structure. `start`, `current`, and `expires` are
|
||||
all set to the current monotonic time. Use this when you only need to
|
||||
measure elapsed duration from initialization.
|
||||
|
||||
**Parameters:**
|
||||
- `sw`: Pointer to the stopwatch structure to initialize.
|
||||
|
||||
**Preconditions:**
|
||||
- `sw` must point to valid memory.
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled for meaningful
|
||||
timing.
|
||||
|
||||
**Postconditions:**
|
||||
- The stopwatch is initialized.
|
||||
|
||||
|
||||
##### stopwatch_init_usecs_expire
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
static inline void stopwatch_init_usecs_expire(struct stopwatch *sw, uint64_t us);
|
||||
```
|
||||
Initializes a stopwatch and sets an expiration time `us` microseconds
|
||||
from now.
|
||||
|
||||
**Parameters:**
|
||||
- `sw`: Pointer to the stopwatch structure.
|
||||
- `us`: Timeout duration in microseconds.
|
||||
|
||||
**Preconditions:**
|
||||
- `sw` must point to valid memory.
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled.
|
||||
|
||||
**Postconditions:**
|
||||
- The stopwatch is initialized, and `expires` is set `us` microseconds
|
||||
after `start`.
|
||||
|
||||
|
||||
##### stopwatch_init_msecs_expire
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
static inline void stopwatch_init_msecs_expire(struct stopwatch *sw, uint64_t ms);
|
||||
```
|
||||
Initializes a stopwatch and sets an expiration time `ms` milliseconds
|
||||
from now.
|
||||
|
||||
**Parameters:**
|
||||
- `sw`: Pointer to the stopwatch structure.
|
||||
- `ms`: Timeout duration in milliseconds.
|
||||
|
||||
**Preconditions:**
|
||||
- `sw` must point to valid memory.
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled.
|
||||
|
||||
**Postconditions:**
|
||||
- The stopwatch is initialized, and `expires` is set `ms` milliseconds
|
||||
after `start`.
|
||||
|
||||
|
||||
#### Time Measurement Functions
|
||||
|
||||
##### stopwatch_tick
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
static inline void stopwatch_tick(struct stopwatch *sw);
|
||||
```
|
||||
Updates the `current` time field in the stopwatch structure to the
|
||||
current monotonic time. This is often called implicitly by other
|
||||
stopwatch functions like `stopwatch_expired()` and
|
||||
`stopwatch_duration_usecs()`.
|
||||
|
||||
**Parameters:**
|
||||
- `sw`: Pointer to the stopwatch structure.
|
||||
|
||||
**Preconditions:**
|
||||
- The stopwatch must be initialized.
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled.
|
||||
|
||||
|
||||
##### stopwatch_expired
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
static inline int stopwatch_expired(struct stopwatch *sw);
|
||||
```
|
||||
Checks if the stopwatch's expiration time (`expires`) has passed. It
|
||||
implicitly calls `stopwatch_tick()` to get the current time for
|
||||
comparison.
|
||||
|
||||
**Parameters:**
|
||||
- `sw`: Pointer to the stopwatch structure.
|
||||
|
||||
**Returns:**
|
||||
- Non-zero (true) if `current` time >= `expires` time.
|
||||
- Zero (false) otherwise.
|
||||
|
||||
**Preconditions:**
|
||||
- The stopwatch must be initialized (preferably with an expiration
|
||||
time).
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled.
|
||||
|
||||
|
||||
##### stopwatch_wait_until_expired
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
static inline void stopwatch_wait_until_expired(struct stopwatch *sw);
|
||||
```
|
||||
Blocks (busy-waits) until the stopwatch expires by repeatedly calling
|
||||
`stopwatch_expired()`.
|
||||
|
||||
**Parameters:**
|
||||
- `sw`: Pointer to the stopwatch structure.
|
||||
|
||||
**Preconditions:**
|
||||
- The stopwatch must be initialized with an expiration time.
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled.
|
||||
|
||||
**Postconditions:**
|
||||
- The function returns only after the stopwatch has expired.
|
||||
|
||||
|
||||
#### Duration Measurement Functions
|
||||
|
||||
##### stopwatch_duration_usecs
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
static inline int64_t stopwatch_duration_usecs(struct stopwatch *sw);
|
||||
```
|
||||
Returns the elapsed time in microseconds between `start` and `current`.
|
||||
If `current` hasn't been updated since `stopwatch_init`, it implicitly
|
||||
calls `stopwatch_tick()` first.
|
||||
|
||||
**Parameters:**
|
||||
- `sw`: Pointer to the stopwatch structure.
|
||||
|
||||
**Returns:**
|
||||
- Elapsed time in microseconds (`current` - `start`).
|
||||
|
||||
**Preconditions:**
|
||||
- The stopwatch must be initialized.
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled for a meaningful
|
||||
duration.
|
||||
|
||||
|
||||
##### stopwatch_duration_msecs
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
static inline int64_t stopwatch_duration_msecs(struct stopwatch *sw);
|
||||
```
|
||||
Returns the elapsed time in milliseconds since the stopwatch was
|
||||
started. It calls `stopwatch_duration_usecs()` and divides by 1000.
|
||||
|
||||
**Parameters:**
|
||||
- `sw`: Pointer to the stopwatch structure.
|
||||
|
||||
**Returns:**
|
||||
- Elapsed time in milliseconds.
|
||||
|
||||
**Preconditions:**
|
||||
- The stopwatch must be initialized.
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled.
|
||||
|
||||
|
||||
### Utility Macros
|
||||
|
||||
These macros combine stopwatch initialization and expiration checking with
|
||||
a user-provided condition.
|
||||
|
||||
#### wait_us
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
#define wait_us(timeout_us, condition) ...
|
||||
```
|
||||
Waits until a condition becomes true or a timeout elapses, whichever
|
||||
comes first. Internally uses a `struct stopwatch`.
|
||||
|
||||
**Parameters:**
|
||||
- `timeout_us`: Timeout duration in microseconds.
|
||||
- `condition`: A C expression that evaluates to true or false. The loop
|
||||
continues as long as the condition is false and the timeout has not
|
||||
expired.
|
||||
|
||||
**Returns:**
|
||||
- 0: If the condition was still false when the timeout expired.
|
||||
- >0: If the condition became true before the timeout. The return value
|
||||
is the approximate number of microseconds waited (at least 1).
|
||||
|
||||
**Preconditions:**
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled.
|
||||
|
||||
|
||||
#### wait_ms
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
#define wait_ms(timeout_ms, condition) ...
|
||||
```
|
||||
Similar to `wait_us`, but the timeout is specified in milliseconds.
|
||||
|
||||
**Parameters:**
|
||||
- `timeout_ms`: Timeout duration in milliseconds.
|
||||
- `condition`: C expression to wait for.
|
||||
|
||||
**Returns:**
|
||||
- 0: If the condition was still false after the timeout.
|
||||
- >0: If the condition became true before the timeout. The return value
|
||||
is the approximate number of milliseconds waited (rounded up, at
|
||||
least 1).
|
||||
|
||||
**Preconditions:**
|
||||
- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled.
|
||||
|
||||
|
||||
## Key differences between `mdelay` and `wait_ms`
|
||||
|
||||
While both can be used to wait, they serve different purposes:
|
||||
|
||||
### Purpose and Use Case
|
||||
|
||||
- `mdelay`: Provides an *unconditional* delay for a fixed duration. The
|
||||
generic implementation attempts to yield to other threads first (if
|
||||
`CONFIG(HAS_THREADS)` is enabled via `udelay`), but it always waits
|
||||
for (at least) the specified time, potentially using a busy-wait if
|
||||
yielding doesn't occur. Primarily used for hardware timing
|
||||
requirements or pacing operations.
|
||||
|
||||
- `wait_ms`: Provides a *conditional* wait. It waits for a specific C
|
||||
expression (`condition`) to become true, but *only up to* a maximum
|
||||
timeout duration. Used when polling for a status change or event, with
|
||||
a safeguard against waiting indefinitely.
|
||||
|
||||
### When to Use Which
|
||||
|
||||
#### Use `mdelay` when:
|
||||
|
||||
- You need a guaranteed minimum delay (e.g., waiting for hardware to
|
||||
settle after a register write).
|
||||
- You are implementing a hardware initialization sequence with specific
|
||||
timing requirements between steps.
|
||||
- You want a simple pause, potentially allowing other threads to run if
|
||||
the underlying `udelay` yields.
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
#include <delay.h>
|
||||
#include <arch/io.h> // For write_reg hypothetical function
|
||||
|
||||
// Initialize hardware with specific timing requirements
|
||||
write_reg(REG_A, value);
|
||||
mdelay(10); // Must wait (at least) 10ms after writing REG_A
|
||||
write_reg(REG_B, another_value);
|
||||
```
|
||||
|
||||
#### Use `wait_ms` when:
|
||||
|
||||
- You are waiting for a hardware status bit to change or a condition to
|
||||
become true.
|
||||
- You need to implement a timeout for an operation that might fail or
|
||||
take too long.
|
||||
- You want to know approximately how long you waited for the condition
|
||||
to become true (via the return value).
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
#include <timer.h>
|
||||
#include <console/console.h>
|
||||
#include <arch/io.h> // For read_reg hypothetical function
|
||||
|
||||
// Wait for hardware to become ready, but not forever
|
||||
#define STATUS_REG 0x100
|
||||
#define READY_BIT (1 << 0)
|
||||
int64_t waited_ms = wait_ms(100, (read_reg(STATUS_REG) & READY_BIT));
|
||||
|
||||
if (waited_ms > 0) {
|
||||
printk(BIOS_INFO, "Hardware ready after ~%lld ms\n", waited_ms);
|
||||
} else {
|
||||
printk(BIOS_ERR, "Timeout: Hardware failed to become ready within 100 ms\n");
|
||||
}
|
||||
```
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
- `mdelay`: Generally simpler if the underlying `udelay` performs a
|
||||
busy-wait. If it yields (`thread_yield_microseconds`), overhead depends
|
||||
on the scheduler. Always waits for the full duration (approximately).
|
||||
- `wait_ms`: Involves repeated condition checking and stopwatch
|
||||
management (`stopwatch_expired`, which calls `timer_monotonic_get`),
|
||||
adding overhead within the loop. However, it can return early if the
|
||||
condition becomes true, potentially saving time compared to a fixed
|
||||
`mdelay`.
|
||||
|
||||
### Error Handling / Feedback
|
||||
|
||||
- `mdelay`: Provides no feedback. It simply waits.
|
||||
- `wait_ms`: Returns whether the condition was met within the timeout
|
||||
and provides the approximate wait time if successful. This allows for
|
||||
explicit timeout handling.
|
||||
|
||||
### Summary
|
||||
|
||||
Use `mdelay` for fixed, unconditional delays, potentially allowing
|
||||
thread yielding. Use `wait_ms` for conditional waits with a timeout and
|
||||
feedback on success or failure. Choose based on whether you need to poll
|
||||
for a condition or simply need to pause execution.
|
||||
|
||||
|
||||
## Timer Callbacks
|
||||
|
||||
coreboot provides a mechanism to schedule functions (callbacks) to be
|
||||
executed after a certain time has elapsed. This requires
|
||||
`CONFIG(TIMER_QUEUE)`.
|
||||
|
||||
### Data Structures
|
||||
|
||||
#### struct timeout_callback
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
struct timeout_callback {
|
||||
void *priv; // Private data for the callback
|
||||
void (*callback)(struct timeout_callback *tocb); // Function to call
|
||||
/* Internal use by timer library: */
|
||||
struct mono_time expiration; // Calculated expiration time
|
||||
};
|
||||
```
|
||||
|
||||
Represents a scheduled callback. The user initializes `priv` and
|
||||
`callback`.
|
||||
|
||||
### API Functions
|
||||
|
||||
#### timer_sched_callback
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
int timer_sched_callback(struct timeout_callback *tocb, uint64_t us);
|
||||
```
|
||||
Schedules a callback function to run after a specified delay.
|
||||
|
||||
**Parameters:**
|
||||
- `tocb`: Pointer to a `struct timeout_callback`. The `priv` and
|
||||
`callback` fields must be set by the caller. The structure must
|
||||
persist until the callback runs or is canceled (cancellation not
|
||||
directly supported by API).
|
||||
- `us`: Delay in microseconds from the time of this call until the
|
||||
callback should run.
|
||||
|
||||
**Returns:**
|
||||
- 0: Success.
|
||||
- <0: Error (e.g., timer queue full, invalid arguments).
|
||||
|
||||
**Preconditions:**
|
||||
- `CONFIG(TIMER_QUEUE)` and `CONFIG(HAVE_MONOTONIC_TIMER)` must be
|
||||
enabled.
|
||||
- `tocb` must point to a valid structure with `callback` assigned.
|
||||
|
||||
**Postconditions:**
|
||||
- The callback is added to a queue, to be executed when `timers_run()`
|
||||
is called after the expiration time.
|
||||
|
||||
|
||||
#### timers_run
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
int timers_run(void);
|
||||
```
|
||||
|
||||
Checks the timer callback queue and executes any callbacks whose
|
||||
expiration time has passed. This function needs to be called
|
||||
periodically from the main execution flow (e.g., in a boot state loop
|
||||
or idle task) for callbacks to be processed.
|
||||
|
||||
**Returns:**
|
||||
- 1: If callbacks were run or are still pending in the queue.
|
||||
- 0: If the queue is empty and no callbacks were run.
|
||||
|
||||
**Preconditions:**
|
||||
- `CONFIG(TIMER_QUEUE)` and `CONFIG(HAVE_MONOTONIC_TIMER)` must be
|
||||
enabled.
|
||||
|
||||
**Usage:**
|
||||
Typically called in loops where deferred work might need processing:
|
||||
```c
|
||||
#include <timer.h>
|
||||
|
||||
// Example main loop structure
|
||||
while (some_condition) {
|
||||
// ... do other work ...
|
||||
|
||||
// Process any expired timer callbacks
|
||||
timers_run();
|
||||
|
||||
// ... potentially yield or sleep ...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Timing Measurement Example
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
#include <console/console.h>
|
||||
|
||||
struct stopwatch sw;
|
||||
stopwatch_init(&sw);
|
||||
|
||||
// ---> Code section to measure start
|
||||
// ... perform some operations ...
|
||||
// ---> Code section to measure end
|
||||
|
||||
// stopwatch_duration_usecs implicitly ticks the stopwatch if needed
|
||||
int64_t duration_us = stopwatch_duration_usecs(&sw);
|
||||
printk(BIOS_INFO, "Operation took %lld microseconds\n", duration_us);
|
||||
|
||||
// Or in milliseconds
|
||||
int64_t duration_ms = stopwatch_duration_msecs(&sw);
|
||||
printk(BIOS_INFO, "Operation took %lld milliseconds\n", duration_ms);
|
||||
```
|
||||
|
||||
|
||||
### Timeout Operation (Polling) Example
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
#include <console/console.h>
|
||||
#include <stdint.h> // For uint8_t example
|
||||
|
||||
// Hypothetical hardware status check
|
||||
extern uint8_t check_hardware_status(void);
|
||||
#define HW_READY (1 << 0)
|
||||
|
||||
struct stopwatch sw;
|
||||
// Initialize stopwatch with a 100 millisecond timeout
|
||||
stopwatch_init_msecs_expire(&sw, 100);
|
||||
|
||||
uint8_t status = 0;
|
||||
while (!(status & HW_READY)) {
|
||||
status = check_hardware_status();
|
||||
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR, "Operation timed out waiting for HW_READY\n");
|
||||
// Handle timeout error...
|
||||
status = 0; // Indicate failure perhaps
|
||||
break;
|
||||
}
|
||||
// Optional: Add a small delay here to avoid busy-spinning the CPU excessively
|
||||
// udelay(10); // e.g., wait 10us between checks
|
||||
}
|
||||
|
||||
if (status & HW_READY) {
|
||||
printk(BIOS_DEBUG, "Hardware became ready.\n");
|
||||
// Continue with operation...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Waiting for a Condition (Using `wait_ms`) Example
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
#include <console/console.h>
|
||||
#include <stdint.h> // For uint8_t example
|
||||
|
||||
// Hypothetical hardware status check
|
||||
extern uint8_t check_hardware_status(void);
|
||||
#define HW_READY (1 << 0)
|
||||
|
||||
// Wait up to 100ms for the HW_READY bit
|
||||
int64_t waited_ms = wait_ms(100, (check_hardware_status() & HW_READY));
|
||||
|
||||
if (waited_ms > 0) {
|
||||
printk(BIOS_INFO, "Condition met: HW_READY asserted after ~%lld ms\n", waited_ms);
|
||||
// Continue...
|
||||
} else {
|
||||
printk(BIOS_ERR, "Timeout: HW_READY not asserted within 100 ms\n");
|
||||
// Handle timeout error...
|
||||
}
|
||||
```
|
||||
|
||||
### Scheduling a Timer Callback Example
|
||||
|
||||
```c
|
||||
#include <timer.h>
|
||||
#include <console/console.h>
|
||||
|
||||
// Data structure for our callback context
|
||||
struct my_callback_data {
|
||||
int counter;
|
||||
const char *message;
|
||||
};
|
||||
|
||||
// The callback function
|
||||
static void my_callback_handler(struct timeout_callback *tocb)
|
||||
{
|
||||
struct my_callback_data *data = tocb->priv;
|
||||
printk(BIOS_INFO, "Callback executed! Message: %s, Counter: %d\n",
|
||||
data->message, data->counter);
|
||||
// Note: 'tocb' can be reused here to reschedule if needed,
|
||||
// or the memory it points to can be freed if dynamically allocated.
|
||||
}
|
||||
|
||||
// Somewhere in initialization code:
|
||||
static struct timeout_callback my_timer;
|
||||
static struct my_callback_data my_data;
|
||||
|
||||
void schedule_my_task(void)
|
||||
{
|
||||
my_data.counter = 42;
|
||||
my_data.message = "Hello from timer";
|
||||
|
||||
my_timer.priv = &my_data;
|
||||
my_timer.callback = my_callback_handler;
|
||||
|
||||
// Schedule the callback to run after 500 milliseconds (500,000 us)
|
||||
int rc = timer_sched_callback(&my_timer, 500 * 1000);
|
||||
if (rc == 0) {
|
||||
printk(BIOS_DEBUG, "Scheduled my_callback_handler successfully.\n");
|
||||
} else {
|
||||
printk(BIOS_ERR, "Failed to schedule callback!\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Remember that timers_run() must be called periodically elsewhere
|
||||
// for the callback to actually execute after the delay.
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Enable Monotonic Timer:** For accurate timing and stopwatch
|
||||
functionality, ensure `CONFIG(HAVE_MONOTONIC_TIMER)` is enabled and a
|
||||
suitable platform timer is configured.
|
||||
2. **Minimize Stopwatch Overhead:** While lightweight, frequent calls,
|
||||
especially `timer_monotonic_get()` implicitly called by stopwatch
|
||||
functions, can add up. Measure larger, significant code blocks rather
|
||||
than tiny ones in tight loops unless absolutely necessary.
|
||||
3. **Use Appropriate Time Units:** Choose `usecs` or `msecs` variants
|
||||
based on the scale of time you are dealing with.
|
||||
4. **Handle Timeouts Gracefully:** When using expiration or
|
||||
`wait_ms`/`wait_us`, always check the result and handle the timeout
|
||||
case appropriately. Don't assume success.
|
||||
5. **Process Timer Callbacks:** If using `timer_sched_callback`, ensure
|
||||
`timers_run()` is called regularly in your main processing loops or
|
||||
idle states.
|
||||
6. **Avoid `mdelay` for Polling:** Prefer `wait_ms` or manual polling
|
||||
with `stopwatch_expired` when waiting for conditions, as `mdelay`
|
||||
offers no timeout handling or feedback.
|
||||
7. **Consider Platform Accuracy:** Timer resolution and accuracy vary
|
||||
between platforms. Don't assume microsecond precision unless verified
|
||||
for the target hardware.
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
1. **Monotonic Timer Dependence:** Stopwatch and timer callbacks rely
|
||||
heavily on `CONFIG(HAVE_MONOTONIC_TIMER)`. Without it, their
|
||||
behavior is limited.
|
||||
2. **Timer Resolution:** Accuracy is limited by the underlying platform
|
||||
timer's resolution and the frequency of `timer_monotonic_get()`
|
||||
updates internally.
|
||||
3. **64-bit Microsecond Counter:** While large, the
|
||||
`uint64_t microseconds` counter will eventually wrap around
|
||||
(after ~584,000 years), though this is not a practical concern for
|
||||
boot times. More relevant is the potential rollover of the
|
||||
*underlying hardware counter* between `timer_monotonic_get` calls,
|
||||
which the implementation must handle correctly (typically ok for
|
||||
intervals up to several seconds).
|
||||
4. **Busy Waiting:** `stopwatch_wait_until_expired` and the internal
|
||||
loops of `wait_us`/`wait_ms` (and potentially the fallback in
|
||||
`udelay`/`mdelay`) perform busy-waits, consuming CPU cycles. Consider
|
||||
alternatives like interrupt-driven timers or yielding
|
||||
(`thread_yield_microseconds`) if power consumption or concurrency is
|
||||
critical.
|
||||
5. **Callback Queue Size:** The timer callback queue has a fixed size
|
||||
defined by `CONFIG(TIMER_QUEUE_SIZE)`, limiting the number of pending
|
||||
callbacks.
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Common issues and solutions:
|
||||
|
||||
1. **Inaccurate Timing / Durations are Zero:**
|
||||
- Verify `CONFIG(HAVE_MONOTONIC_TIMER)` is enabled.
|
||||
- Check if a platform-specific `init_timer()` is required and being
|
||||
called.
|
||||
- Ensure the platform timer frequency is correctly configured.
|
||||
- Check for potential timer hardware issues or conflicts.
|
||||
|
||||
2. **Timeouts Not Working / `wait_ms` Never Returns > 0:**
|
||||
- Verify the timeout value is appropriate (not too short).
|
||||
- Double-check the `condition` logic in `wait_ms`/`wait_us`. Is it
|
||||
actually capable of becoming true?
|
||||
- Ensure the stopwatch (`sw` or the internal one in `wait_ms`) is
|
||||
properly initialized before the check loop.
|
||||
- Confirm `CONFIG(HAVE_MONOTONIC_TIMER)` is enabled.
|
||||
|
||||
3. **Timer Callbacks Not Running:**
|
||||
- Verify `CONFIG(TIMER_QUEUE)` is enabled.
|
||||
- Ensure `timer_sched_callback()` returned success (0).
|
||||
- **Crucially:** Check that `timers_run()` is being called
|
||||
periodically in a suitable loop after the callback was scheduled.
|
||||
- Make sure the `struct timeout_callback` structure persists in memory
|
||||
until the callback runs.
|
||||
|
||||
4. **Performance Impact:**
|
||||
- Reduce frequency of stopwatch checks or `wait_ms`/`wait_us` calls if
|
||||
possible.
|
||||
- Measure larger code blocks.
|
||||
- Investigate if the underlying `udelay` implementation is
|
||||
busy-waiting excessively; yielding (`thread_yield_microseconds`)
|
||||
might be preferable if available and appropriate.
|
||||
|
||||
|
||||
## Platform-Specific Considerations
|
||||
|
||||
The core APIs are generic, but the underlying implementation relies on
|
||||
platform code:
|
||||
- **Timer Source:** Platforms implement `timer_monotonic_get` (often
|
||||
weakly linked) or provide the necessary hooks for it, using hardware
|
||||
like the APIC timer (x86), Time Base (PPC), architectural timers
|
||||
(ARM), etc.
|
||||
- **`init_timer()`:** Platforms may need a custom `init_timer` to set up
|
||||
clocks, dividers, or enable the timer hardware.
|
||||
- **`udelay`:** Platforms might provide optimized `udelay`
|
||||
implementations.
|
||||
|
||||
Refer to the documentation and code within specific `src/soc/`,
|
||||
`src/cpu/`, or `src/arch/` directories for platform details.
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- `src/include/timer.h`: Monotonic timer, stopwatch, and callback
|
||||
declarations.
|
||||
- `src/include/delay.h`: `udelay`, `mdelay`, `delay` declarations.
|
||||
- `src/lib/timer.c`: Generic `udelay` (using stopwatch/yield), weak
|
||||
`init_timer`.
|
||||
- `src/lib/delay.c`: Generic `mdelay` and `delay` implementations
|
||||
(calling `udelay`).
|
||||
- `src/lib/hardwaremain.c`: Example usage of `timers_run()` in boot
|
||||
state machine.
|
||||
- Platform timer implementations: Search for `timer_monotonic_get` or
|
||||
`init_timer` in `src/cpu/`, `src/soc/`, `src/arch/` directories
|
||||
(e.g., `src/cpu/x86/lapic/apic_timer.c`,
|
||||
`src/arch/arm64/timer.c`).
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
# coreboot Threads
|
||||
|
||||
## Thread Management
|
||||
|
||||
coreboot provides a cooperative threading system that allows for
|
||||
concurrent execution of tasks during the boot process. The thread API is
|
||||
particularly useful for implementing asynchronous operations and
|
||||
managing hardware initialization sequences.
|
||||
|
||||
|
||||
### Thread Creation and Management
|
||||
|
||||
#### thread_run
|
||||
|
||||
```c
|
||||
int thread_run(struct thread_handle *handle, enum cb_err (*func)(void *), void *arg)
|
||||
```
|
||||
Creates and starts a new thread to execute the specified function.
|
||||
|
||||
**Parameters:**
|
||||
- `handle`: Pointer to a thread handle structure to track thread state
|
||||
(Note: `struct thread_handle` is an opaque structure used by the API
|
||||
to manage the thread's state.)
|
||||
- `func`: Function to execute in the new thread
|
||||
- `arg`: Argument to pass to the function
|
||||
|
||||
**Returns:**
|
||||
- 0 on success
|
||||
- < 0 on failure
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
struct thread_handle th;
|
||||
enum cb_err thread_func(void *arg) {
|
||||
// Thread work here
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
||||
if (thread_run(&th, thread_func, NULL) < 0) {
|
||||
printk(BIOS_ERR, "Failed to create thread\n");
|
||||
} else {
|
||||
// Wait for the thread to complete and check its status
|
||||
enum cb_err err = thread_join(&th);
|
||||
if (err != CB_SUCCESS) {
|
||||
printk(BIOS_ERR, "Thread failed with error %d\n", err);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### thread_run_until
|
||||
|
||||
```c
|
||||
int thread_run_until(struct thread_handle *handle, enum cb_err (*func)(void *), void *arg,
|
||||
boot_state_t state, boot_state_sequence_t seq)
|
||||
```
|
||||
Creates a thread that blocks boot state transitions until completion.
|
||||
|
||||
**Parameters:**
|
||||
- `handle`: Pointer to a thread handle structure
|
||||
- `func`: Function to execute
|
||||
- `arg`: Argument to pass to the function
|
||||
- `state`: Boot state to block
|
||||
- `seq`: Boot state sequence to block
|
||||
|
||||
**Returns:**
|
||||
- 0 on success
|
||||
- < 0 on failure
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
struct thread_handle th;
|
||||
enum cb_err init_func(void *arg) {
|
||||
// Hardware initialization
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
||||
// Block BS_DEV_ENABLE until initialization completes
|
||||
thread_run_until(&th, init_func, NULL, BS_DEV_ENABLE, 0);
|
||||
```
|
||||
|
||||
|
||||
### Thread Synchronization
|
||||
|
||||
#### thread_join
|
||||
|
||||
```c
|
||||
enum cb_err thread_join(struct thread_handle *handle)
|
||||
```
|
||||
Waits for a thread to complete and returns its error code.
|
||||
|
||||
**Parameters:**
|
||||
- `handle`: Thread handle to wait for
|
||||
|
||||
**Returns:**
|
||||
- Thread's error code (e.g., `CB_SUCCESS`, `CB_ERR`). See
|
||||
`src/include/cb_err.h` for details.
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
struct thread_handle th;
|
||||
// ... create thread ...
|
||||
|
||||
enum cb_err err = thread_join(&th);
|
||||
if (err != CB_SUCCESS) {
|
||||
printk(BIOS_ERR, "Thread failed with error %d\n", err);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Thread Yielding
|
||||
|
||||
Yielding is crucial in a cooperative multitasking system like
|
||||
coreboot's. Threads must explicitly yield control using `thread_yield`
|
||||
or `thread_yield_microseconds` to allow other threads to run. Failure to
|
||||
yield can lead to a single thread monopolizing the CPU, preventing other
|
||||
tasks from executing.
|
||||
|
||||
#### thread_yield
|
||||
|
||||
```c
|
||||
int thread_yield(void)
|
||||
```
|
||||
Yields the current thread's execution to allow other threads to run.
|
||||
|
||||
**Returns:**
|
||||
- 0 on success
|
||||
- < 0 if thread cannot yield
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
while (!condition) {
|
||||
if (thread_yield() < 0) {
|
||||
printk(BIOS_ERR, "Failed to yield thread\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### thread_yield_microseconds
|
||||
|
||||
```c
|
||||
int thread_yield_microseconds(unsigned int microsecs)
|
||||
```
|
||||
Yields the current thread for a specified number of microseconds.
|
||||
|
||||
**Parameters:**
|
||||
- `microsecs`: Number of microseconds to yield
|
||||
|
||||
**Returns:**
|
||||
- 0 on success
|
||||
- < 0 if thread cannot yield
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
// Wait for 100 microseconds
|
||||
if (thread_yield_microseconds(100) < 0) {
|
||||
printk(BIOS_ERR, "Failed to yield thread\n");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Thread Cooperation Control
|
||||
|
||||
#### thread_coop_enable
|
||||
|
||||
```c
|
||||
void thread_coop_enable(void)
|
||||
```
|
||||
Enables cooperative behavior for the current thread.
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
thread_coop_enable(); // Allow thread to yield
|
||||
```
|
||||
|
||||
#### thread_coop_disable
|
||||
|
||||
```c
|
||||
void thread_coop_disable(void)
|
||||
```
|
||||
Disables cooperative behavior for the current thread.
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
thread_coop_disable(); // Prevent thread from yielding
|
||||
```
|
||||
|
||||
|
||||
### Thread Mutexes
|
||||
|
||||
#### thread_mutex_lock
|
||||
|
||||
```c
|
||||
void thread_mutex_lock(struct thread_mutex *mutex)
|
||||
```
|
||||
Acquires a mutex lock, waiting if necessary.
|
||||
|
||||
**Parameters:**
|
||||
- `mutex`: Mutex to lock
|
||||
|
||||
**Example:**
|
||||
```c
|
||||
struct thread_mutex mtx = THREAD_MUTEX_INITIALIZER; // Or = { .locked = false };
|
||||
thread_mutex_lock(&mtx);
|
||||
// Critical section
|
||||
thread_mutex_unlock(&mtx);
|
||||
```
|
||||
|
||||
|
||||
#### thread_mutex_unlock
|
||||
|
||||
```c
|
||||
void thread_mutex_unlock(struct thread_mutex *mutex)
|
||||
```
|
||||
Releases a mutex lock.
|
||||
|
||||
**Parameters:**
|
||||
- `mutex`: Mutex to unlock
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Thread Safety**:
|
||||
- Use mutexes to protect shared resources
|
||||
- Be careful with global variables in threaded code
|
||||
- Consider thread cooperation when implementing critical sections
|
||||
|
||||
2. **Resource Management**:
|
||||
- Always join threads that you create using `thread_run` to check
|
||||
their completion status and clean up resources. Threads started
|
||||
with `thread_run_until` are implicitly managed by the boot state
|
||||
machine and typically do not require explicit joining.
|
||||
- Consistently check return values from thread creation and operation
|
||||
functions (like `thread_run`, `thread_yield`, `thread_join`) to
|
||||
detect errors early.
|
||||
- Clean up resources allocated or used within thread functions before
|
||||
they exit.
|
||||
|
||||
3. **Performance Considerations**:
|
||||
- Use thread_yield_microseconds for precise timing
|
||||
- Minimize time spent in critical sections
|
||||
- Consider using thread_run_until for hardware initialization
|
||||
|
||||
4. **Error Handling**:
|
||||
- Check thread creation and operation return values (as noted in
|
||||
Resource Management).
|
||||
- Implement proper error handling within thread functions, returning
|
||||
appropriate `cb_err` values.
|
||||
- Use `thread_join` (for `thread_run` threads) to check the final
|
||||
completion status.
|
||||
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Hardware Initialization
|
||||
|
||||
```c
|
||||
struct thread_handle init_th;
|
||||
enum cb_err init_hardware(void *arg) {
|
||||
// Initialize hardware
|
||||
if (hardware_init() != 0)
|
||||
return CB_ERR;
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
||||
// Run initialization in a thread
|
||||
thread_run_until(&init_th, init_hardware, NULL, BS_DEV_ENABLE, 0);
|
||||
```
|
||||
|
||||
### Asynchronous Operation
|
||||
|
||||
```c
|
||||
struct thread_handle async_th;
|
||||
enum cb_err async_operation(void *arg) {
|
||||
// Perform async operation
|
||||
while (!operation_complete()) {
|
||||
if (thread_yield() < 0)
|
||||
return CB_ERR;
|
||||
}
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
||||
// Start async operation
|
||||
thread_run(&async_th, async_operation, NULL);
|
||||
```
|
||||
|
||||
|
||||
### Critical Section Protection
|
||||
|
||||
```c
|
||||
struct thread_mutex resource_mtx = { .locked = false };
|
||||
|
||||
void access_shared_resource(void) {
|
||||
thread_mutex_lock(&resource_mtx);
|
||||
// Access shared resource
|
||||
thread_mutex_unlock(&resource_mtx);
|
||||
}
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
1. The thread system is cooperative, not preemptive.
|
||||
2. Threads must explicitly yield to allow other threads to run.
|
||||
3. Thread operations are typically only available after RAM
|
||||
initialization (in ramstage and later). Check specific environment
|
||||
constraints if unsure.
|
||||
4. Thread count is limited by the `CONFIG_NUM_THREADS` Kconfig option.
|
||||
5. Thread stack size is fixed by the `CONFIG_STACK_SIZE` Kconfig option.
|
||||
|
||||
|
|
@ -2,408 +2,175 @@
|
|||
|
||||
## Introduction
|
||||
|
||||
The aim of the timestamp library is to make it easier for different
|
||||
boards to save timestamps in cbmem / stash (until cbmem is brought up)
|
||||
by providing a simple API to initialize, add, and sync timestamps. In
|
||||
order to make the timestamps persistent and accessible from the kernel,
|
||||
we need to ensure that all the saved timestamps end up in cbmem under
|
||||
The aim of the timestamp library is to make it easier for different boards
|
||||
to save timestamps in cbmem / stash (until cbmem is brought up) by
|
||||
providing a simple API to initialize, add and sync timestamps. In order
|
||||
to make the timestamps persistent and accessible from the kernel, we
|
||||
need to ensure that all the saved timestamps end up in cbmem under
|
||||
the CBMEM_ID_TIMESTAMP tag. However, until the cbmem area is available,
|
||||
the timestamps can be saved to a SoC-defined `_timestamp` region if one
|
||||
is defined in the board's `memlayout.ld`. The work of identifying the
|
||||
right location for storing timestamps is done by the library and is not
|
||||
exposed to the user.
|
||||
|
||||
Timestamps in coreboot are a critical feature for performance analysis,
|
||||
debugging, and optimization of the boot process. They provide precise
|
||||
timing information about various stages and operations during system
|
||||
initialization, allowing developers to identify bottlenecks and optimize
|
||||
boot performance. The timestamp system is designed to be lightweight,
|
||||
accurate, and persistent across different boot stages.
|
||||
the timestamps can be saved to a SoC-defined \_timestamp region or in a
|
||||
local stage-specific stash. The work of identifying the right location for
|
||||
storing timestamps is done by the library and is not exposed to the user.
|
||||
|
||||
Working of timestamp library from a user perspective can be outlined in
|
||||
the following steps:
|
||||
1. Initialize the base time and reset cbmem timestamp area using
|
||||
`timestamp_init()`.
|
||||
2. Start adding timestamps using `timestamp_add()` or
|
||||
`timestamp_add_now()`.
|
||||
1. Initialize the base time and reset cbmem timestamp area
|
||||
2. Start adding timestamps
|
||||
|
||||
Behind the scenes, the timestamp library takes care of:
|
||||
1. Identifying the correct location for storing timestamps (`_timestamp`
|
||||
region before cbmem is ready, then cbmem region).
|
||||
2. Add a new cbmem timestamp area based on whether a reset of the cbmem
|
||||
1. Identifying the correct location for storing timestamps (cbmem or timestamp
|
||||
region or local stash).
|
||||
2. Once cbmem is up, ensure that all timestamps are synced from timestamp
|
||||
region or local stash into the cbmem area.
|
||||
3. Add a new cbmem timestamp area based on whether a reset of the cbmem
|
||||
timestamp region is required or not.
|
||||
3. Once cbmem is ready, ensuring that all timestamps are synced from the
|
||||
`_timestamp` region into the cbmem area.
|
||||
|
||||
Note that if `CONFIG_COLLECT_TIMESTAMPS` is disabled, all timestamp
|
||||
functions are implemented as no-ops, and no timestamps will be
|
||||
collected.
|
||||
### Transition from cache to cbmem
|
||||
|
||||
To move timestamps from the cache to cbmem (and initialize the cbmem area in
|
||||
the first place), we use the CBMEM_INIT_HOOK infrastructure of coreboot.
|
||||
|
||||
When cbmem is initialized, the hook is called, which creates the area,
|
||||
copies all timestamps to cbmem and disables the cache.
|
||||
|
||||
After such a transition, timestamp_init() must not be run again.
|
||||
|
||||
|
||||
## Background
|
||||
|
||||
The timestamp implementation in coreboot has evolved over time to meet
|
||||
the needs of the firmware development and performance analysis.
|
||||
Initially designed as a simple timing mechanism, it has grown into a
|
||||
sophisticated system that:
|
||||
|
||||
- Tracks boot stages and critical operations
|
||||
- Supports multiple hardware platforms (x86, ARM, RISC-V)
|
||||
- Provides persistent storage across boot stages
|
||||
- Enables post-boot analysis of boot performance
|
||||
- Integrates with vendor-specific firmware components
|
||||
|
||||
|
||||
## Timestamp Architecture
|
||||
|
||||
### Transition from cache (`_timestamp` region) to cbmem
|
||||
|
||||
To move timestamps from the early `_timestamp` region to cbmem (and
|
||||
initialize the cbmem area in the first place), we use the
|
||||
`CBMEM_READY_HOOK` infrastructure of coreboot.
|
||||
|
||||
When cbmem is initialized (`cbmem_initialize` or `cbmem_recovery`), the
|
||||
hook calls the `timestamp_reinit` function. This function allocates or
|
||||
finds the `CBMEM_ID_TIMESTAMP` area in cbmem, copies all timestamps from
|
||||
the `_timestamp` region (if used) using `timestamp_sync_cache_to_cbmem`,
|
||||
and updates an internal global pointer (`glob_ts_table`) to point to the
|
||||
cbmem table. Subsequent calls to `timestamp_add` will then write
|
||||
directly to cbmem.
|
||||
|
||||
After such a transition, `timestamp_init()` must not be run again (it is
|
||||
asserted to only run in `ENV_ROMSTAGE_OR_BEFORE`).
|
||||
|
||||
|
||||
### Data structures used
|
||||
|
||||
Timestamps are stored using instances of `struct timestamp_table`. A
|
||||
global pointer, `glob_ts_table`, points to the currently active table,
|
||||
which is either the `_timestamp` memory region (during early boot) or
|
||||
the CBMEM area.
|
||||
|
||||
The `_timestamp` region acts as an early cache before cbmem is ready.
|
||||
Its size is determined by `REGION_SIZE(timestamp)` defined in the linker
|
||||
script (`memlayout.ld`) and dictates the maximum number of entries that
|
||||
can be stored early on.
|
||||
|
||||
For timestamps stored in the cbmem area, a `timestamp_table` is
|
||||
allocated with space for a fixed number of entries (currently 192)
|
||||
defined by `MAX_TIMESTAMPS` in `src/lib/timestamp.c`.
|
||||
## Data structures used
|
||||
|
||||
The main structure that maintains information about the timestamp cache is:
|
||||
|
||||
```c
|
||||
struct timestamp_entry {
|
||||
uint32_t entry_id;
|
||||
int64_t entry_stamp;
|
||||
} __packed;
|
||||
struct __packed timestamp_cache {
|
||||
uint16_t cache_state;
|
||||
struct timestamp_table table;
|
||||
struct timestamp_entry entries[MAX_TIMESTAMP_CACHE];
|
||||
};
|
||||
```
|
||||
>>> _Source: `/src/commonlib/include/commonlib/timestamp_serialized.h`_
|
||||
|
||||
### cache_state
|
||||
|
||||
The state of the cache is maintained by `cache_state` attribute which can
|
||||
be any one of the following:
|
||||
|
||||
```c
|
||||
enum {
|
||||
TIMESTAMP_CACHE_UNINITIALIZED = 0,
|
||||
TIMESTAMP_CACHE_INITIALIZED,
|
||||
TIMESTAMP_CACHE_NOT_NEEDED,
|
||||
};
|
||||
```
|
||||
|
||||
By default, if the cache is stored in local stash (bss area), then
|
||||
it will be reset to uninitialized state. However, if the cache is
|
||||
stored in timestamp region, then it might have garbage in any of the
|
||||
attributes. Thus, if the timestamp region is being used by any board, it is
|
||||
initialized to default values by the library.
|
||||
|
||||
Once the cache is initialized, its state is set to
|
||||
`CACHE_INITIALIZED`. Henceforth, the calls to cache i.e. `timestamp_add`
|
||||
know that the state reflected is valid and timestamps can be directly
|
||||
saved in the cache.
|
||||
|
||||
Once the cbmem area is up (i.e. call to `timestamp_sync_cache_to_cbmem`),
|
||||
we do not need to store the timestamps in local stash / timestamp area
|
||||
anymore. Thus, the cache state is set to `CACHE_NOT_NEEDED`, which allows
|
||||
`timestamp_add` to store all timestamps directly into the cbmem area.
|
||||
|
||||
|
||||
### table
|
||||
|
||||
This field is represented by a structure which provides overall
|
||||
information about the entries in the timestamp area:
|
||||
|
||||
```c
|
||||
struct timestamp_table {
|
||||
uint64_t base_time;
|
||||
uint16_t max_entries;
|
||||
uint16_t tick_freq_mhz;
|
||||
uint32_t num_entries;
|
||||
struct timestamp_entry entries[]; /* Variable number of entries */
|
||||
uint64_t base_time;
|
||||
uint32_t max_entries;
|
||||
uint32_t num_entries;
|
||||
struct timestamp_entry entries[0]; /* Variable number of entries */
|
||||
} __packed;
|
||||
```
|
||||
>>> _Source: `/src/commonlib/include/commonlib/timestamp_serialized.h`_
|
||||
|
||||
It indicates the base time for all timestamp entries, maximum number
|
||||
of entries that can be stored, total number of entries that currently
|
||||
exist and an entry structure to hold variable number of entries.
|
||||
|
||||
|
||||
- `base_time`: Indicates the base time (offset) for all timestamp
|
||||
entries in this table.
|
||||
- `max_entries`: Maximum number of entries that can be stored in this
|
||||
table instance.
|
||||
- `tick_freq_mhz`: Timestamp tick frequency in MHz. Populated later in
|
||||
boot.
|
||||
- `num_entries`: Total number of entries that currently exist in this
|
||||
table instance.
|
||||
- `entries`: Array holding the actual timestamp entries.
|
||||
### entries
|
||||
|
||||
This field holds the details of each timestamp entry, up to a maximum
|
||||
of `MAX_TIMESTAMP_CACHE` which is defined as 16 entries. Each entry is
|
||||
defined by:
|
||||
|
||||
```c
|
||||
struct timestamp_entry {
|
||||
uint32_t entry_id;
|
||||
uint64_t entry_stamp;
|
||||
} __packed;
|
||||
```
|
||||
|
||||
`entry_id` holds the timestamp id corresponding to this entry and
|
||||
`entry_stamp` holds the actual timestamp.
|
||||
|
||||
|
||||
### Memory Layout
|
||||
|
||||
Timestamps are stored in two locations during the boot process:
|
||||
|
||||
1. **`_timestamp` Region**: Used during early boot stages (before CBMEM
|
||||
is available), *if* defined in `memlayout.ld`.
|
||||
- Located at the `_timestamp` symbol.
|
||||
- Persists across stage transitions within `ENV_ROMSTAGE_OR_BEFORE`.
|
||||
- Size determined by `REGION_SIZE(timestamp)` in the linker script,
|
||||
which limits the number of entries.
|
||||
|
||||
2. **CBMEM**: Used after CBMEM is initialized.
|
||||
- Identified by `CBMEM_ID_TIMESTAMP`.
|
||||
- Provides persistent storage across warm reboots.
|
||||
- Supports a fixed maximum number of entries.
|
||||
- Automatically synchronized from the `_timestamp` region when CBMEM
|
||||
becomes available via `timestamp_reinit`.
|
||||
For timestamps stored in the cbmem area, a `timestamp_table` is allocated
|
||||
with space for `MAX_TIMESTAMPS` equal to 30. Thus, the cbmem area holds
|
||||
`base_time`, `max_entries` (which is 30), current number of entries and the
|
||||
actual entries represented by `timestamp_entry`.
|
||||
|
||||
|
||||
## Function APIs
|
||||
|
||||
### Core Functions
|
||||
### timestamp_init
|
||||
|
||||
#### timestamp_init
|
||||
This function initializes the timestamp cache and should be run as early
|
||||
as possible. On platforms with SRAM, this might mean in bootblock, on
|
||||
x86 with its CAR backed memory in romstage, this means romstage before
|
||||
memory init.
|
||||
|
||||
```c
|
||||
void timestamp_init(uint64_t base);
|
||||
```
|
||||
>>> _Source: `/src/include/timestamp.h`_
|
||||
### timestamp_add
|
||||
|
||||
Initializes the timestamp system with a base time. This function sets up
|
||||
the timestamp cache (`_timestamp` region) and should be run in
|
||||
bootblock. It must be called once in *one* of the
|
||||
`ENV_ROMSTAGE_OR_BEFORE` stages. It will fail if no `timestamp` region
|
||||
is defined in `memlayout.ld`.
|
||||
|
||||
Note that platform setup code on x86 or the decompressor on Arm can
|
||||
measure some timestamps earlier and pass them in to
|
||||
bootblock_main_with_timestamp().
|
||||
This function accepts from user a timestamp id and time to record in the
|
||||
timestamp table. It stores the entry in the appropriate table in cbmem
|
||||
or `_timestamp` region or local stash.
|
||||
|
||||
|
||||
#### timestamp_add
|
||||
### timestamp_add_now
|
||||
|
||||
```c
|
||||
void timestamp_add(enum timestamp_id id, int64_t ts_time);
|
||||
```
|
||||
>>> _Source: `/src/include/timestamp.h`_
|
||||
|
||||
Adds a new timestamp with the specified ID and time value. It stores the
|
||||
timestamp in the currently active table (either `_timestamp` region or
|
||||
cbmem). The time value must be an absolute time value (typically
|
||||
obtained from `timestamp_get()`), as it will be adjusted by subtracting
|
||||
the table's `base_time` before being stored as an entry.
|
||||
|
||||
|
||||
#### timestamp_add_now
|
||||
|
||||
```c
|
||||
void timestamp_add_now(enum timestamp_id id);
|
||||
```
|
||||
>>> _Source: `/src/include/timestamp.h`_
|
||||
|
||||
Adds a new timestamp with the current time. This function calls
|
||||
`timestamp_add` with user-provided id and current time obtained from
|
||||
`timestamp_get()`.
|
||||
|
||||
|
||||
#### timestamp_rescale_table
|
||||
|
||||
```c
|
||||
void timestamp_rescale_table(uint16_t N, uint16_t M);
|
||||
```
|
||||
>>> _Source: `/src/include/timestamp.h`_
|
||||
|
||||
Applies a scaling factor N/M to all recorded timestamps (including the
|
||||
`base_time`).
|
||||
|
||||
|
||||
#### get_us_since_boot
|
||||
|
||||
```c
|
||||
uint32_t get_us_since_boot(void);
|
||||
```
|
||||
>>> _Source: `/src/include/timestamp.h`_
|
||||
|
||||
Returns the time since boot (relative to `base_time`) in microseconds.
|
||||
Requires `tick_freq_mhz` to be populated.
|
||||
|
||||
|
||||
#### timestamp_get
|
||||
|
||||
```c
|
||||
uint64_t timestamp_get(void);
|
||||
```
|
||||
>>> _Source: `/src/include/timestamp.h`_
|
||||
|
||||
Returns the current raw timestamp value from the underlying hardware
|
||||
timer (platform-specific weak implementation).
|
||||
|
||||
|
||||
#### timestamp_tick_freq_mhz
|
||||
|
||||
```c
|
||||
int timestamp_tick_freq_mhz(void);
|
||||
```
|
||||
>>> _Source: `/src/include/timestamp.h`_
|
||||
|
||||
Returns the timestamp tick frequency in MHz (platform-specific weak
|
||||
implementation).
|
||||
|
||||
|
||||
### Timestamp IDs
|
||||
|
||||
The system uses predefined timestamp IDs to mark various boot stages and
|
||||
operations. These are organized in ranges:
|
||||
|
||||
- 1-500: Miscellaneous coreboot operations (e.g., `TS_POSTCAR_START`,
|
||||
`TS_DELAY_START`, `TS_READ_UCODE_START`)
|
||||
- 500-600: Google/ChromeOS specific (e.g., `TS_VBOOT_START`,
|
||||
`TS_EC_SYNC_START`).
|
||||
|
||||
Note many of the existing timestamps here are no longer
|
||||
Google-specific since many features originally added for Google
|
||||
vendorcode have since been migrated into general coreboot code.
|
||||
|
||||
- 900-940: AMD specific (e.g., `TS_AGESA_INIT_EARLY_START`)
|
||||
- 940-950: Intel ME specific (e.g., `TS_ME_INFORM_DRAM_START`)
|
||||
- 950-989: Intel FSP specific (e.g., `TS_FSP_MEMORY_INIT_START`)
|
||||
- 990-999: Intel ME specific (continued) (e.g., `TS_ME_ROM_START`)
|
||||
- 1000+: Payload specific
|
||||
- Depthcharge: 1000-1199
|
||||
- ChromeOS Hypervisor: 1200-1299
|
||||
|
||||
Refer to `src/commonlib/include/commonlib/timestamp_serialized.h` for
|
||||
the complete list and descriptions.
|
||||
This function calls `timestamp_add` with user-provided id and current time.
|
||||
|
||||
|
||||
## Use / Test Cases
|
||||
|
||||
The following cases describe the behavior based on the presence of the
|
||||
`timestamp` region and when cbmem is initialized.
|
||||
The following cases have been considered while designing the timestamp
|
||||
library. It is important to ensure that any changes made to this library satisfy
|
||||
each of the following use cases:
|
||||
|
||||
### Case 1: Timestamp Region Exists (Fresh Boot / Resume)
|
||||
|
||||
### Case 1: Timestamp Region Exists
|
||||
In this case, the library needs to call `timestamp_init` as early as possible to
|
||||
enable the timestamp cache. Once cbmem is available, the values will be
|
||||
transferred automatically.
|
||||
|
||||
This is the standard configuration for collecting early timestamps.
|
||||
`timestamp_init` must be called in an `ENV_ROMSTAGE_OR_BEFORE` stage to
|
||||
initialize the `_timestamp` region. When the `CBMEM_READY_HOOK` runs
|
||||
`timestamp_reinit`, the contents of the `_timestamp` region are copied
|
||||
to the cbmem table, and subsequent timestamps go directly to cbmem. The
|
||||
cbmem table is reset on fresh boot or resume.
|
||||
All regions are automatically reset on initialization.
|
||||
|
||||
### Case 2: No timestamp region, fresh boot, cbmem_initialize called after timestamp_init
|
||||
|
||||
### Case 2: No Timestamp Region Defined
|
||||
`timestamp_init` will set up a local cache. cbmem must be initialized before that
|
||||
cache vanishes - as happens when jumping to the next stage.
|
||||
|
||||
If no `timestamp` region is defined in `memlayout.ld`, attempts to call
|
||||
`timestamp_init` will fail (specifically, `timestamp_cache_get()` will
|
||||
return NULL). No timestamps can be collected before cbmem is ready.
|
||||
Timestamps added after `timestamp_reinit` has run (via the
|
||||
`CBMEM_READY_HOOK`) will be added directly to the cbmem table, but there
|
||||
will be no `base_time` established from early boot.
|
||||
### Case 3: No timestamp region, fresh boot, cbmem_initialize called before timestamp_init
|
||||
|
||||
This case is not supported right now, just don't call `timestamp_init` after
|
||||
`cbmem_initialize`. (Patches to make this more robust are welcome.)
|
||||
|
||||
### Case 3: Resume
|
||||
### Case 4: No timestamp region, resume, cbmem_initialize called after timestamp_init
|
||||
|
||||
On resume (e.g., x86 S3), `timestamp_reinit` is typically called again.
|
||||
If `ENV_CREATES_CBMEM` is true for the resume path (as it is for x86
|
||||
S3), a new cbmem table is allocated by `timestamp_alloc_cbmem_table`,
|
||||
effectively clearing any pre-suspend timestamps. The `_timestamp` region
|
||||
content (if any) is copied over, but this usually contains stale data
|
||||
from the previous boot's early stages.
|
||||
We always reset the cbmem region before using it, so pre-suspend timestamps
|
||||
will be gone.
|
||||
|
||||
### Case 5: No timestamp region, resume, cbmem_initialize called before timestamp_init
|
||||
|
||||
## Configuration
|
||||
|
||||
### Kconfig Options
|
||||
|
||||
- `CONFIG_COLLECT_TIMESTAMPS`: Enable/disable timestamp collection
|
||||
globally. If disabled, timestamp functions become no-ops.
|
||||
- `CONFIG_TIMESTAMPS_ON_CONSOLE`: Print timestamps to console during
|
||||
boot as they are added.
|
||||
|
||||
|
||||
### Memory Layout
|
||||
|
||||
Collecting timestamps before cbmem is ready requires an `_timestamp`
|
||||
region in the memory layout, defined in the `memlayout.ld` linker
|
||||
script. Depending on the platform, the memory layout can be for the
|
||||
board, the SOC, or the Architecture. Any of them will typically follow
|
||||
the following pattern:
|
||||
|
||||
```text
|
||||
#include <memlayout.h>
|
||||
|
||||
...
|
||||
TIMESTAMP(., 0x200)
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
The size allocated to this region determines the maximum number of
|
||||
timestamps that can be stored before cbmem is available.
|
||||
|
||||
The cbmem timestamp table (`CBMEM_ID_TIMESTAMP`) has a fixed size,
|
||||
currently allowing up to 192 entries. This limit is defined by
|
||||
`MAX_TIMESTAMPS` in `src/lib/timestamp.c`.
|
||||
|
||||
|
||||
### Hardware Considerations
|
||||
|
||||
- x86: `timestamp_init` must be called before CAR (Cache-as-RAM) is torn
|
||||
down if called from bootblock or separate romstage. The library
|
||||
includes checks (`timestamp_should_run`) to ensure timestamps are only
|
||||
added by the primary processor during early boot on AP systems.
|
||||
- ARM: No special considerations noted in the code.
|
||||
- RISC-V: No special considerations noted in the code.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Initializing Timestamps (in bootblock)
|
||||
|
||||
```c
|
||||
/* In src/mainboard/$(MAINBOARDDIR)/bootblock.c */
|
||||
#include <timestamp.h>
|
||||
#include <timer.h> /* For timestamp_get() default implementation */
|
||||
|
||||
void bootblock_mainboard_init(void)
|
||||
{
|
||||
/* Initialize timestamp region with current time as base. */
|
||||
timestamp_init(timestamp_get());
|
||||
|
||||
/* Add first timestamp */
|
||||
timestamp_add_now(TS_BOOTBLOCK_START);
|
||||
|
||||
/* ... other bootblock code ... */
|
||||
}
|
||||
```
|
||||
|
||||
Note: `timestamp_get()` here provides the initial base time. Requires
|
||||
`CONFIG_COLLECT_TIMESTAMPS=y` and a `timestamp` region.
|
||||
|
||||
|
||||
### Adding Custom Timestamps
|
||||
|
||||
```c
|
||||
#include <timestamp.h>
|
||||
|
||||
void my_custom_function(void)
|
||||
{
|
||||
timestamp_add_now(TS_DEVICE_INITIALIZE); /* Use a relevant ID */
|
||||
// ... perform initialization ...
|
||||
timestamp_add_now(TS_DEVICE_DONE); /* Use a relevant ID */
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Initialization**:
|
||||
- Enable `CONFIG_COLLECT_TIMESTAMPS` if needed.
|
||||
- Define a `timestamp` region in `memlayout.ld` if early
|
||||
timestamps (before cbmem) are required. Ensure it's large enough
|
||||
for the expected number of early entries.
|
||||
- Call `timestamp_init()` exactly once in the earliest possible
|
||||
`ENV_ROMSTAGE_OR_BEFORE` stage (e.g., `bootblock`).
|
||||
- Use a consistent base time, typically `timestamp_get()`.
|
||||
|
||||
2. **Adding Timestamps**:
|
||||
- Use appropriate predefined timestamp IDs from
|
||||
`timestamp_serialized.h` whenever possible. Add custom IDs if
|
||||
necessary, avoiding conflicts.
|
||||
- Add timestamps for significant operations or stage transitions
|
||||
using `timestamp_add_now()`.
|
||||
- Be mindful of the entry limits: the size of the `_timestamp`
|
||||
region for early timestamps, and the fixed limit for the cbmem
|
||||
table. Check for "Timestamp table full" errors in the log.
|
||||
|
||||
3. **Analysis**:
|
||||
- Use the `cbmem -t` utility in the OS (if using LinuxBoot/NERF)
|
||||
to read and display timestamps stored in CBMEM.
|
||||
- Consider the `tick_freq_mhz` (also available in the `cbmem -t`
|
||||
output) when converting raw timestamp differences (`entry_stamp`)
|
||||
to time units. The raw values are offsets from `base_time`.
|
||||
We always reset the cbmem region before using it, so pre-suspend timestamps
|
||||
will be gone.
|
||||
|
|
|
|||
|
|
@ -122,8 +122,10 @@ $ sudo flashrom \
|
|||
-w coreboot.rom
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
In addition to the information here, please see the
|
||||
<project:../../tutorial/flashing_firmware/index.md>.
|
||||
:doc:`../../tutorial/flashing_firmware/index`.
|
||||
```
|
||||
|
||||
### External flashing
|
||||
|
||||
|
|
|
|||
|
|
@ -130,5 +130,5 @@ facing towards the bottom of the board.
|
|||
|
||||
[ASRock H110M-DVS]: https://www.asrock.com/mb/Intel/H110M-DVS%20R2.0/
|
||||
[MX25L6473E]: http://www.macronix.com/Lists/Datasheet/Attachments/7380/MX25L6473E,%203V,%2064Mb,%20v1.4.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[H110M-DVS manual]: https://web.archive.org/web/20191023230631/http://asrock.pc.cdn.bitgravity.com/Manual/H110M-DVS%20R2.0.pdf
|
||||
|
|
|
|||
|
|
@ -115,8 +115,10 @@ $ sudo flashrom --noverify-all --ifd -i bios -p internal -w coreboot.rom
|
|||
The use of `--noverify-all` is required since the Management Engine
|
||||
region is not readable even by the host.
|
||||
|
||||
```{eval-rst}
|
||||
In addition to the information here, please see the
|
||||
<project:../../tutorial/flashing_firmware/index.md>.
|
||||
:doc:`../../tutorial/flashing_firmware/index`.
|
||||
```
|
||||
|
||||
## Hardware monitoring and fan control
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ This page describes how to run coreboot on the [ASRock H81M-HDS].
|
|||
|
||||
## Required proprietary blobs
|
||||
|
||||
Please see <project:../../northbridge/intel/haswell/mrc.bin.md>.
|
||||
```{eval-rst}
|
||||
Please see :doc:`../../northbridge/intel/haswell/mrc.bin`.
|
||||
```
|
||||
|
||||
## Building coreboot
|
||||
|
||||
|
|
@ -73,8 +75,9 @@ facing towards the bottom of the board.
|
|||
in coreboot. The `coretemp` driver can still be used for accurate CPU
|
||||
temperature readings from an OS.
|
||||
|
||||
Please also see
|
||||
<project:../../northbridge/intel/haswell/known-issues.md>.
|
||||
```{eval-rst}
|
||||
Please also see :doc:`../../northbridge/intel/haswell/known-issues`.
|
||||
```
|
||||
|
||||
## Untested
|
||||
|
||||
|
|
@ -126,5 +129,5 @@ Please also see
|
|||
|
||||
[ASRock H81M-HDS]: https://www.asrock.com/mb/Intel/H81M-HDS/
|
||||
[W25Q32FV]: https://www.winbond.com/resource-files/w25q32fv%20revi%2010202015.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[Board manual]: https://web.archive.org/web/20191231093418/http://asrock.pc.cdn.bitgravity.com/Manual/H81M-HDS.pdf
|
||||
|
|
|
|||
|
|
@ -5,24 +5,24 @@ This page describes how to run coreboot on the [ASRock IMB-1222].
|
|||
## Technology
|
||||
|
||||
```{eval-rst}
|
||||
+---------+---------------------------------------------------------------+
|
||||
| CPU | | Intel 10th Gen (Comet lake-S) Core Processors (LGA-1200) |
|
||||
| | | CPUs over 80W will be limited due to power design |
|
||||
+---------+---------------------------------------------------------------+
|
||||
| DRAM | 2 SO-DIMM slots, DDR4 2933/2666/2400 MHz |
|
||||
+---------+---------------------------------------------------------------+
|
||||
| Chipset | Intel Q470E |
|
||||
+---------+---------------------------------------------------------------+
|
||||
| SuperIO | Fintek F81966 |
|
||||
+---------+---------------------------------------------------------------+
|
||||
| TPM | Infineon SLB 9670VQ2.0 |
|
||||
+---------+---------------------------------------------------------------+
|
||||
| Boot | USB, SATA, NVMe |
|
||||
+---------+---------------------------------------------------------------+
|
||||
| Power | | Laptop Power Supply: |
|
||||
| | | - 12V DC-in (IMB-1222) |
|
||||
| | | - 12V~28V DC-in (IMB-1222-WV) |
|
||||
+---------+---------------------------------------------------------------+
|
||||
+------------+---------------------------------------------------------------+
|
||||
| CPU | Intel 10th Gen (Comet lake-S) Core Processors (LGA-1200) |
|
||||
| | CPUs over 80W will be limited due to power design |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| DRAM | 2 SO-DIMM slots, DDR4 2933/2666/2400 MHz |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| Chipset | Intel Q470E |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| Super I/O | Fintek F81966 |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| TPM | Infineon SLB 9670VQ2.0 |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| Boot | USB, SATA, NVMe |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| Power | Laptop Power Supply: |
|
||||
| | - 12V DC-in (IMB-1222) |
|
||||
| | - 12V~28V DC-in (IMB-1222-WV) |
|
||||
+------------+---------------------------------------------------------------+
|
||||
```
|
||||
|
||||
```text
|
||||
|
|
@ -70,7 +70,7 @@ This page describes how to run coreboot on the [ASRock IMB-1222].
|
|||
| ALC122 | | LPC
|
||||
+--------------+ +--------------+
|
||||
+-----------------------+ +--------------+ | | +------------+
|
||||
| 2 x COM RS232/422/485 |---| ST3243E |---| Fintek |----| CPU FAN x1 |
|
||||
| 2 x COM RS232/422/482 |---| ST3243E |---| Fintek |----| CPU FAN x1 |
|
||||
+-----------------------+ +--------------+ | F81966 | +------------+
|
||||
+-----------------------+ +--------------+ | SuperIO | +------------+
|
||||
| 2 x COM RS232 |---| ST3243E |---| |----| NCT 3941SA |
|
||||
|
|
@ -85,48 +85,6 @@ This page describes how to run coreboot on the [ASRock IMB-1222].
|
|||
This port was created without a schematic/boardview, reverse engineering only.
|
||||
Feel free to make changes.
|
||||
|
||||
## Required proprietary blobs
|
||||
|
||||
To build full working image of coreboot, the following blobs are required:
|
||||
|
||||
```{eval-rst}
|
||||
+-----------------+-------------------------------------------+-------------------------+
|
||||
| Binary file | Apply | Required/Optional |
|
||||
+=================+===========================================+=========================+
|
||||
| FSP-M & FSP-S | | Intel Firmware Support Package 2.1 | Required |
|
||||
| | | 10th Generation Intel® Core™ processors | |
|
||||
| | | and chipsets (formerly Comet Lake) | |
|
||||
+-----------------+-------------------------------------------+-------------------------+
|
||||
| IFD | Intel Flash Descriptor | Required |
|
||||
+-----------------+-------------------------------------------+-------------------------+
|
||||
| ME | Intel Management Engine | Required |
|
||||
+-----------------+-------------------------------------------+-------------------------+
|
||||
| GBE | Gigabit Ethernet Configuration | | Optional |
|
||||
| | | | (if LAN2 is enabled) |
|
||||
+-----------------+-------------------------------------------+-------------------------+
|
||||
```
|
||||
|
||||
### FSP
|
||||
|
||||
Intel company provides [Firmware Support Package (2.1)](../../soc/intel/fsp/index.md)
|
||||
to initialize this generation silicon. Please see this
|
||||
[document](../../soc/intel/code_development_model/code_development_model.md).
|
||||
|
||||
### IFD, ME, GBE
|
||||
|
||||
Use the [vendor's firmware] version 1.80 to extract the IFD, ME, GBE blobs from it, according to
|
||||
the [Intel IFD Binary Extraction Tutorial](../../util/ifdtool/binary_extraction.md).
|
||||
|
||||
```bash
|
||||
wget --tries=5 "https://web.archive.org/web/20250413105432/https://download.asrock.com/IPC/BIOS/IMB-1222(1.80)ROM.zip"
|
||||
unzip "IMB-1222(1.80)ROM.zip"
|
||||
ifdtool --platform cnl -x IM12221.80
|
||||
File IM12221.80 is 33554432 bytes
|
||||
flashregion_0_flashdescriptor.bin Flash Region 0 (Flash Descriptor): 00000000 - 00000fff
|
||||
flashregion_2_intel_me.bin Flash Region 2 (Intel ME): 00003000 - 01002fff
|
||||
flashregion_3_gbe.bin Flash Region 3 (GbE): 00001000 - 00002fff
|
||||
```
|
||||
|
||||
## Building coreboot
|
||||
|
||||
The following commands will help quickly configure and build a project for this board:
|
||||
|
|
@ -152,8 +110,6 @@ make
|
|||
+---------------+------+---------+-----------+
|
||||
| Windows 10 | V | | |
|
||||
+---------------+------+---------+-----------+
|
||||
| Windows 11 | V | | |
|
||||
+---------------+------+---------+-----------+
|
||||
| Android 13 | | V | |
|
||||
+---------------+------+---------+-----------+
|
||||
```
|
||||
|
|
@ -162,12 +118,9 @@ make
|
|||
- SeaBIOS (1.16.3);
|
||||
- edk2 [MrChromebox fork] (uefipayload_2408).
|
||||
|
||||
### Additional information
|
||||
|
||||
- Ubuntu 22.04 (Linux 6.5.0-15-generic);
|
||||
- Ubuntu 24.04 (Linux 6.8.0-41-generic);
|
||||
- Microsoft Windows 10 Pro (10.0.19045.4780, 22H2 2022);
|
||||
- Microsoft Windows 11 Pro (10.0.26100.3194, 24H2 2024);
|
||||
- Andoid 13, [Bliss OS] x86_64 (16.9.7, Linux 6.1.112-gloria-xanmod1).
|
||||
|
||||
## Flashing coreboot
|
||||
|
|
@ -178,8 +131,8 @@ make
|
|||
+=====================+==========================+
|
||||
| Socketed flash | yes |
|
||||
+---------------------+--------------------------+
|
||||
| Model | | W25Q256JV |
|
||||
| | | MX25L25673G |
|
||||
| Model | W25Q256JV |
|
||||
| | MX25L25673G |
|
||||
+---------------------+--------------------------+
|
||||
| Size | 32 MiB |
|
||||
+---------------------+--------------------------+
|
||||
|
|
@ -218,7 +171,7 @@ information about this [here](../../tutorial/flashing_firmware/index.md).
|
|||
- USB 2.0 ports;
|
||||
- USB 3.2 ports;
|
||||
- M.2 Key-E 2230 slot for Wireless (PCIe x1, USB 2.0 and CNVi);
|
||||
- M.2 Key-B 3042/3052 WWAN slot for 4G/5G modem (PCIe x1, USB 3.0);
|
||||
- M.2 Key-B 3042/3052 slot for 4G/5G modem (PCIe x1);
|
||||
- M.2 Key-M 2242/2260/2280 for SSD/NVMe (PCIE x4, SATA3);
|
||||
- LAN1 Intel I225LM/I225V, 10/100/1000/2500 Mbps;
|
||||
- LAN2 Intel I219LM, 10/100/1000 Mbps;
|
||||
|
|
@ -232,14 +185,14 @@ information about this [here](../../tutorial/flashing_firmware/index.md).
|
|||
|
||||
## Unknown/untested
|
||||
|
||||
- USB 3.0 in M.2 Key-B 3042/3052 slot (currently disabled);
|
||||
- eDP/LVDS (currently disabled);
|
||||
- PCIe riser cards;
|
||||
- SPDIF;
|
||||
- SATA RAID.
|
||||
|
||||
[ASRock IMB-1222]: https://web.archive.org/web/20220924171403/https://www.asrockind.com/en-gb/IMB-1222
|
||||
[vendor's firmware]: https://web.archive.org/web/20250413105432/https://download.asrock.com/IPC/BIOS/IMB-1222(1.80)ROM.zip
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[MrChromebox fork]: https://github.com/MrChromebox/edk2
|
||||
[XutaxKamay fork]: https://github.com/XutaxKamay/me_cleaner
|
||||
[Bliss OS]: https://blissos.org/
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ Tested even with/without the Bolton and Hudson blobs.
|
|||
|
||||
[ASUS A88XM-E]: https://www.asus.com/Motherboards/A88XME/
|
||||
[Board manual]: https://dlcdnets.asus.com/pub/ASUS/mb/SocketFM2/A88XM-E/E9125_A88XM-E.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[GD25Q64]: http://www.elm-tech.com/ja/products/spi-flash-memory/gd25q64/gd25q64.pdf
|
||||
[Piledriver]: https://en.wikipedia.org/wiki/Piledriver_%28microarchitecture%29#APU_lines
|
||||
[Sea Islands]: https://en.wikipedia.org/wiki/Graphics_Core_Next#GCN_2nd_generation
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ This version is usable for all the GPUs.
|
|||
|
||||
[ASUS F2A85-M]: https://web.archive.org/web/20160320065008/http://www.asus.com/Motherboards/F2A85M/
|
||||
[Board manual]: https://web.archive.org/web/20211028063105/https://dlcdnets.asus.com/pub/ASUS/mb/SocketFM2/F2A85-M/E8005_F2A85-M.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[Piledriver]: https://en.wikipedia.org/wiki/Piledriver_%28microarchitecture%29#APU_lines
|
||||
[TeraScale 3]: https://en.wikipedia.org/wiki/TeraScale_%28microarchitecture%29#TeraScale_3
|
||||
[W25Q64FV]: https://web.archive.org/web/20220127184640/https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
# ASUS PRIME H610i-PLUS
|
||||
This is a Mini-ITX LGA1700 (Alder Lake/Raptor Lake) motherboard, using the H610 chipset and
|
||||
DDR4 RAM. It's a close relative of the H610M-K, and like it is also sold in DDR4 and DDR5
|
||||
variants.
|
||||
|
||||
## Variants
|
||||
- *ASUS PRIME H610i-PLUS **D4***: uses DDR4 RAM, supported
|
||||
- *ASUS PRIME H610i-PLUS* (no "D4"): uses DDR5 RAM, not currently supported by this port
|
||||
|
||||
## Flashing
|
||||
This mainboard uses a standard 3.3V SOIC-8 SPI flash chip. The vendor firmware enables write
|
||||
protection, thus for initial installation an external programmer is required. Thereafter,
|
||||
coreboot can be updated internally using `flashrom -p internal`.
|
||||
|
||||
An external programmer can be connected using an ordinary chip clip, but for development or
|
||||
testing, it can be more convenient to flash via the TPM header. A pinout can be found on Page
|
||||
1-4 of the board's User's Manual - to select the flash chip, connect your CS line to
|
||||
F_SPI_CS0#_R. An adapter cable can be made using a 2x7-pin 2.0mm female header, or a set of
|
||||
2.0mm jumper wires. Beware, despite its similar appearance, this TPM header pinout is NOT
|
||||
compatible with the pinout found on the MSI Z690A and Z790P boards (adapters for flashing those
|
||||
boards over the SPI TPM header will not work on ASUS boards).
|
||||
|
||||
## Feature Support
|
||||
### Working:
|
||||
- Console over onboard serial port
|
||||
- PS/2 keyboard
|
||||
- Port 80 POST codes over ASUS debug header
|
||||
- All USB ports, including USB3 working, except front USB2
|
||||
- All outputs (DP, HDMI, VGA) for iGPU
|
||||
- M.2 slot
|
||||
- PCIe WiFi card in WiFi slot
|
||||
- Onboard Ethernet
|
||||
- PCIe ASPM and clock power management for all devices
|
||||
- x16 PCIe slot
|
||||
- All SATA ports
|
||||
- Hard drive indicator LED
|
||||
- All audio including front panel
|
||||
- Fan control
|
||||
- ME disable with HAP bit in IFD
|
||||
- HSPHY-in-FMAP when ME is disabled
|
||||
|
||||
### Untested:
|
||||
- CNVi WiFi card in WiFi slot
|
||||
- SPI TPM
|
||||
- Front USB2 ports (did not have an adapter on hand to test)
|
||||
- Status LEDs in actual error states (they do show a normal status normally)
|
||||
|
||||
### Not working:
|
||||
- S3 sleep
|
||||
|
|
@ -105,4 +105,4 @@ for only CPU models that the board will actually be run with.
|
|||
|
||||
## Extra resources
|
||||
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -103,4 +103,4 @@ for only CPU models that the board will actually be run with.
|
|||
|
||||
## Extra resources
|
||||
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -91,4 +91,4 @@ flash externally.
|
|||
|
||||
[ASUS P8C WS]: https://www.asus.com/supportonly/p8c_ws/helpdesk_knowledge/
|
||||
[W25Q64FVA1Q]: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -107,5 +107,5 @@ region is not readable even by the host.
|
|||
|
||||
[ASUS P8H61-M LX]: https://www.asus.com/Motherboards/P8H61M_LX/
|
||||
[W25Q32BV]: https://web.archive.org/web/20211002141814/https://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[Board manual]: http://dlcdnet.asus.com/pub/ASUS/mb/LGA1155/P8H61_M_LX/E6803_P8H61-M_LX.zip
|
||||
|
|
|
|||
|
|
@ -100,4 +100,4 @@ region is not readable even by the host.
|
|||
|
||||
[ASUS P8H61-M Pro]: https://www.asus.com/Motherboards/P8H61M_Pro/
|
||||
[W25Q32BV]: https://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -78,4 +78,4 @@ work. The flash chip is socketed, so it's easy to remove and reflash.
|
|||
|
||||
[ASUS P8H77-V]: https://www.asus.com/supportonly/p8h77v/helpdesk_knowledge/
|
||||
[W25Q64FVA1Q]: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -169,4 +169,4 @@ The board has two onboard buttons, each with a related LED nearby.
|
|||
|
||||
[ASUS P8Z77-M]: https://www.asus.com/supportonly/p8z77-m/helpdesk_manual/
|
||||
[W25Q64FVA1Q]: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -165,4 +165,4 @@ easy to remove and reflash.
|
|||
|
||||
[ASUS P8Z77-M PRO]: https://www.asus.com/Motherboards/P8Z77M_PRO/
|
||||
[W25Q64FVA1Q]: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -108,5 +108,5 @@ See [Asus Wi-Fi Go! v1].
|
|||
|
||||
[ASUS P8Z77-V]: https://www.asus.com/supportonly/p8z77v/helpdesk_knowledge/
|
||||
[W25Q64FVA1Q]: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[Asus Wi-Fi Go! v1]: ./wifigo_v1.md
|
||||
|
|
|
|||
|
|
@ -1,258 +0,0 @@
|
|||
# ASUS P8Z77-V LE PLUS
|
||||
|
||||
This page describes how to run coreboot on the [ASUS P8Z77-V LE PLUS].
|
||||
|
||||
## Flashing coreboot
|
||||
|
||||
```{eval-rst}
|
||||
+---------------------+----------------+
|
||||
| Type | Value |
|
||||
+=====================+================+
|
||||
| Socketed flash | yes |
|
||||
+---------------------+----------------+
|
||||
| Model | W25Q64FVA1Q |
|
||||
+---------------------+----------------+
|
||||
| Size | 8 MiB |
|
||||
+---------------------+----------------+
|
||||
| Package | DIP-8 |
|
||||
+---------------------+----------------+
|
||||
| Write protection | yes |
|
||||
+---------------------+----------------+
|
||||
| Dual BIOS feature | no |
|
||||
+---------------------+----------------+
|
||||
| Internal flashing | yes |
|
||||
+---------------------+----------------+
|
||||
```
|
||||
|
||||
### How to flash
|
||||
|
||||
The main SPI flash cannot be written because the vendor firmware disables BIOSWE
|
||||
and enables BLE/SMM_BWP flags in BIOS_CNTL for their latest BIOSes. An external
|
||||
programmer is required. You must flash standalone, flashing in-circuit doesn't
|
||||
work. The flash chip is socketed, so it's easy to remove and reflash.
|
||||
|
||||
See page 2-2 of user's manual for flash chip location.
|
||||
|
||||
### Extra preparations for changing PCIe slot configuration
|
||||
|
||||
On vendor firmware, the black PCIEX16_3 slot can be configured as x2 or x4.
|
||||
If set for x4, PCIEX1_1 and PCIEX1_2 are disabled.
|
||||
|
||||
Before flashing coreboot for the first time, decide how you want to use the PCIe slots.
|
||||
If you want to be able to choose between using the two PCIEX1 slots and the PCIEX16_3 slot at
|
||||
x4 bandwidth, you need to do some preparation, namely make two backups of the whole flash
|
||||
chip, specifically the flash descriptor under both configurations.
|
||||
|
||||
Enter vendor UEFI setup and check the PCIEX16_3 (black) slot bandwidth setting. You'll back up
|
||||
under this setting first. Once one backup is made, come back and change the setting
|
||||
from x2 to x4 (or vice versa) and reboot once, then make the other backup.
|
||||
|
||||
With PCIEX16_3 (black) slot bandwidth at x2, run these commands:
|
||||
```bash
|
||||
flashrom -p internal -r pciex163_x2.bin
|
||||
dd if=pciex163_x2.bin of=ifd-pciex163_x2.bin bs=4096 count=1
|
||||
```
|
||||
|
||||
With PCIEX16_3 (black) slot bandwidth at x4, run these commands:
|
||||
```bash
|
||||
flashrom -p internal -r pciex163_x4.bin
|
||||
dd if=pciex163_x4.bin of=ifd-pciex163_x4.bin bs=4096 count=1
|
||||
```
|
||||
(`dd` needs not be run as root.)
|
||||
|
||||
Save the shortened `ifd-pciex163_*.bin` files for when you want to change the configuration.
|
||||
Keep one of the full backups as well.
|
||||
|
||||
See "PCIe config" section below for more details.
|
||||
|
||||
## Working
|
||||
|
||||
- Core i5-3570K and i7-3770K CPUs
|
||||
- Corsair CMZ16GX3M2A1600C10 2x8GB memory kit
|
||||
- SeaBIOS 1.16.3
|
||||
- edk2 mrchromebox fork uefipayload_2501
|
||||
- Kernel 6.12.7
|
||||
- All USB2 ports (mouse, keyboard)
|
||||
- All USB3 ports
|
||||
- Z77 SATA ports (WD Blue SA510, Liteon LH-20A1L)
|
||||
- nVidia 8800GT GPU in PCIEX16_1 slot running x16
|
||||
- PCI slots (Sound Blaster Live! Value)
|
||||
- RTL8111F LAN
|
||||
- CPU temperature sensors and hardware monitor
|
||||
(see [below](#hardware-monitoring-and-fan-speed-control))
|
||||
- Integrated graphics with libgfxinit and VBT
|
||||
(all ports tested and working)
|
||||
- Both PCIe x1 slots when properly configured
|
||||
(see [How to flash](#how-to-flash) above and [PCIe config](#pcie-config);
|
||||
Atheros 928x miniPCIe Wifi on adapter & MSI Herald-BE Wifi7 adapter)
|
||||
- PCIe x4 slot with Intel Octane H10 1TB NVMe at x2 mode
|
||||
- Serial port
|
||||
- PS/2 keyboard
|
||||
- Analog 7.1 audio out the 3.5mm jacks on rear panel
|
||||
- Front HDA audio panel
|
||||
- Digital audio out (Optical, internal SPDIF header, HDMI, DisplayPort)
|
||||
|
||||
Although a `spdif_dest` option is provided for feature parity with vendor firmware,
|
||||
it doesn't seem to matter and digital audio out is available through all ports.
|
||||
It does, however, change how the ports are presented to the OS.
|
||||
|
||||
- S3 suspend from Linux
|
||||
|
||||
## Known issues
|
||||
|
||||
- For 7.1 analog audio to work, at least the front channel (green jack) must be connected.
|
||||
|
||||
## Untested
|
||||
|
||||
- Hotplug of Z77 SATA ports
|
||||
- EHCI debugging
|
||||
|
||||
## Not working
|
||||
|
||||
- Wake-on-LAN
|
||||
- PS/2 mouse (requires a patch currently under review)
|
||||
- Asmedia USB 3.0 battery charging support (for USB 3 ports on the LAN stack)
|
||||
- USB Charger+ (When the bottom USB 3 port on the eSATA stack, also used for BIOS flashback,
|
||||
remains powered while the rest of the system is off. Both features are controlled by the same
|
||||
AI1314 controller.)
|
||||
- Marvell SATA ports are brought up in IDE mode, pata_marvell driver is loaded,
|
||||
but are effectively unusable.
|
||||
|
||||
## PCIe config
|
||||
See [Extra preparations](#extra-preparations-for-changing-pcie-slot-configuration) section above.
|
||||
|
||||
Changing the PCIe slot configuration requires manipulating a PCH GPIO line and a soft strap in
|
||||
the flash chip's descriptor section, which is read-only at runtime. coreboot programs the GPIO
|
||||
to match the soft strap, but how it can update the soft strap itself is to be determined. Until
|
||||
then, to make this change you have to re-flash the descriptor yourself, with one of the two
|
||||
copies you previously saved per above:
|
||||
```bash
|
||||
flashrom -p internal --ifd -i fd -w ifd-pciex163_x2.bin
|
||||
```
|
||||
|
||||
## Hardware monitoring and fan speed control
|
||||
|
||||
Although all fan ports are 4-pin for PWM fans, only CPU_FAN has actual PWM control;
|
||||
all other fan speed control is by voltage only.
|
||||
|
||||
Write 1 into `/sys/class/hwmon/hwmon1/pwm1_mode` to enable CHA_FAN1 control, otherwise it
|
||||
runs at full speed.
|
||||
|
||||
`fan5`/`pwm5` is not implemented and should be ignored.
|
||||
|
||||
These are the sensors.conf settings for this board:
|
||||
|
||||
```
|
||||
label fan1 "CHA_FAN1"
|
||||
label fan2 "CPU_FAN"
|
||||
label fan3 "CHA_FAN2"
|
||||
label fan4 "CHA_FAN3"
|
||||
ignore fan5
|
||||
label in1 "+12V"
|
||||
label in4 "+5V"
|
||||
compute in1 @*12, @/12
|
||||
compute in4 @*5, @/5
|
||||
set temp1_type 4
|
||||
set temp2_type 4
|
||||
```
|
||||
|
||||
## Extra onboard switches and LEDs
|
||||
|
||||
- `BIOS_FLBK`:
|
||||
Vendor firmware uses this button to facilitate a simple update mechanism
|
||||
via a USB drive plugged into the bottom USB port of the USB/ESATA6G stack.
|
||||
It connects to the proprietary AI1314 controller, along with `FLBK_LED`.
|
||||
|
||||
- `MemOK!`:
|
||||
OEM firmware uses this button for memory tuning related to overclocking.
|
||||
It connects to pin 74 of super I/O.
|
||||
|
||||
- `DRAM_LED` lights up when there is a memory problem or when vendor MemOK! feature is
|
||||
operating. Connects to GP07 line of super I/O. coreboot lights it up during memory init
|
||||
similar to vendor firmware.
|
||||
|
||||
- `EPU`: When enabled, lights up `EPU_LED` and takes PCH GPIO44 low.
|
||||
- `TPU`: When enabled, lights up `TPU_LED` and takes PCH GPIO45 low.
|
||||
|
||||
`EPU` and `TPU` are cues to vendor firmware to enable two embedded controllers for
|
||||
overclocking features. coreboot is not yet able to make use of these two signals.
|
||||
|
||||
- `SB_PWR` lights up whenever board is receiving power. It's all hardware
|
||||
and does not concern coreboot.
|
||||
|
||||
- `DRCT` is an undocumented 2-pin header next to the front panel connector block. It
|
||||
connects to both the power button circuit and Z77's intruder detection input. Shorting this
|
||||
header triggers both. With coreboot it currently works the same as the power button.
|
||||
|
||||
## Extra exposed GPIOs at `TB_HEADER`
|
||||
|
||||
A number of GPIO lines are broken out to `TB_HEADER` to support the ThunderboltEX adapter,
|
||||
which never took off. Now they're yours to play with. Additional programming may be required such
|
||||
as enabling GPIO by I/O for maximum effect.
|
||||
|
||||
This may be safely ignored for most normal uses.
|
||||
|
||||
**Be careful not to apply more than 3.3v to these pins!** And do not touch the two pins
|
||||
labeled "NOT A GPIO".
|
||||
|
||||
Pinout:
|
||||
```
|
||||
+---+---+---+---+---+
|
||||
| 2 | 4 | 5 | 7 | 9 |
|
||||
+---+---+---+---+---+
|
||||
| 1 | 3 | | 6 | 8 |
|
||||
+---+---+---+---+---+
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
+-----+-----------------------+----------+--------+
|
||||
| Pin | Name | Source | GPIO # |
|
||||
+=====+=======================+==========+========+
|
||||
| 1 | S_DP_DDC_CLK_TO_TB | **NOT A GPIO** |
|
||||
+-----+-----------------------+----------+--------+
|
||||
| 2 | TB_GPIO_6 | NCT6779D | 14 |
|
||||
+-----+-----------------------+----------+--------+
|
||||
| 3 | S_DP_DDC_DATA_TO_TB | **NOT A GPIO** |
|
||||
+-----+-----------------------+----------+--------+
|
||||
| 4 | TB_GPIO_7 | NCT6779D | 13 |
|
||||
+-----+-----------------------+----------+--------+
|
||||
| 5 | TB_FWUPDATE | NCT6779D | 11 |
|
||||
+-----+-----------------------+----------+--------+
|
||||
| 6 | TB_DEV_HPD | Z77 | 0 |
|
||||
+-----+-----------------------+----------+--------+
|
||||
| 7 | TB_GO2SX | NCT6779D | 17 |
|
||||
+-----+-----------------------+----------+--------+
|
||||
| 8 | TB_GO2SX#_ACK | NCT6779D | 16 |
|
||||
+-----+-----------------------+----------+--------+
|
||||
| 9 | Not connected |
|
||||
+-----+-------------------------------------------+
|
||||
```
|
||||
|
||||
Pins 2, 4, 6, 8 have 1M ohm pulldowns.
|
||||
|
||||
## Technology
|
||||
|
||||
```{eval-rst}
|
||||
+------------------+--------------------------------------------------+
|
||||
| Northbridge | :doc:`../../northbridge/intel/sandybridge/index` |
|
||||
+------------------+--------------------------------------------------+
|
||||
| Southbridge | bd82x6x |
|
||||
+------------------+--------------------------------------------------+
|
||||
| CPU | model_206ax |
|
||||
+------------------+--------------------------------------------------+
|
||||
| Super I/O | Nuvoton NCT6779D |
|
||||
+------------------+--------------------------------------------------+
|
||||
| EC | TPU (ENE KB3722), AI1314 |
|
||||
+------------------+--------------------------------------------------+
|
||||
| Coprocessor | Intel Management Engine |
|
||||
+------------------+--------------------------------------------------+
|
||||
```
|
||||
|
||||
## Extra resources
|
||||
|
||||
- [Flash chip datasheet][W25Q64FVA1Q]
|
||||
|
||||
[ASUS P8Z77-V LE PLUS]: https://www.asus.com/supportonly/p8z77-v%20le%20plus/helpdesk_manual/
|
||||
[W25Q64FVA1Q]: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[rtnicpg]: https://github.com/redchenjs/rtnicpg
|
||||
|
|
@ -142,6 +142,6 @@ the cables or not being populated on the board case.
|
|||
- Intruder detection
|
||||
- Wake-on-Lan from ACPI S3
|
||||
|
||||
[flashrom]: https://flashrom.org/
|
||||
[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
|
||||
|
|
|
|||
|
|
@ -20,23 +20,29 @@ To build full working image of coreboot, the following blobs are required:
|
|||
+-----------------+---------------------------------+----------------------+
|
||||
```
|
||||
|
||||
Microcode for D0 stepping cannot be generated from the tree.
|
||||
Therefore, including external microcode is **mandatory** for boards sold with
|
||||
D0 stepping (Engineering Sample).
|
||||
Microcode for those SoCs cannot be generated from the tree.
|
||||
While boards with D1 (production) stepping may work, microcode Intel had
|
||||
included in their tree is too old, which causes issues with APIC
|
||||
(Advanced Programmable Interrupt Controller), resulting in overall instability.
|
||||
|
||||
This is **required** for boards sold with D0 SoC revision (Engineering Sample).
|
||||
Maintainer of this port had included publicly-available [microcodes] in
|
||||
`3rdparty/blobs` coreboot repository, which are being pulled as submodule.
|
||||
|
||||
Microcode binary for D0 stepping is called
|
||||
`cpu806D0_platC2_ver00000054_2021-05-07_PRD_B0F9E245.bin`.
|
||||
To choose appropriate microcode for your system, you should choose:
|
||||
1. If your motherboard uses Engineering Sample (D0) stepping:
|
||||
- `cpu806D0_platC2_ver00000054_2021-05-07_PRD_B0F9E245.bin`
|
||||
2. If your motherboard uses retail (D1) stepping:
|
||||
- `cpu806D1_platC2_ver00000046_2023-02-27_PRD_08E6188A.bin`
|
||||
|
||||
It should be selected by going to:
|
||||
`Chipset -> Include CPU microcode in CBFS (Include external microcode binary)`.
|
||||
By going to `Chipset -> Include CPU microcode in CBFS
|
||||
(Include external microcode binary)`
|
||||
|
||||
Failure to choose an appropriate microcode will result in:
|
||||
- MCE (Machine Check Exception) errors.
|
||||
- Issues with APIC, resulting in random hangs.
|
||||
- Unstable system RAM, leading to bit flips and data corruption.
|
||||
Failure to choose an appropriate microcode may result in:
|
||||
- Bricked (unbootable) board
|
||||
- Issues with APIC, resulting in random freezes
|
||||
- MCE (Machine Check Exception) errors
|
||||
- Unstable system RAM, leading to bit flips and data corruption
|
||||
|
||||
There are no extra steps required for FSP.
|
||||
Both SKUs work perfectly with FSP Intel publishes in their public repository.
|
||||
|
|
@ -82,7 +88,7 @@ despite using mobile SoC and PCH.
|
|||
- RS232 serial output from IT8613E for debugging (cbmem, Linux)
|
||||
- Fan control from userspace (IT8613E Environment Controller)
|
||||
- USB2.0 and 3.0
|
||||
- HDMI, DisplayPort (iGPU, including audio)
|
||||
- HDMI (iGPU, including audio)
|
||||
- Realtek RTL8111 (GbE NIC)
|
||||
- Realtek ALC897 (integrated audio)
|
||||
- PCIe x16 4.0 (SoC)
|
||||
|
|
@ -100,14 +106,19 @@ despite using mobile SoC and PCH.
|
|||
- XMP Profiles (some people reported issues, despite successful tests).
|
||||
You can enable it by setting `SpdProfileSelected` in `romstage_fsp_params.c`.
|
||||
See [FSP XMP flags] for configuration options, proceed with caution.
|
||||
- Sleep states (which were broken on stock as well, RAM loses power in S3).
|
||||
- Automatic fan control (fans will always spin at 50% - see below).
|
||||
- PCIe ASPM (results in AER spam in dmesg).
|
||||
- GOP init on external GPUs (most EDK2 branches do not include module
|
||||
necessary to load external Option ROMs)
|
||||
- Sleep states (which were broken on stock as well)
|
||||
- USB3.2 might take few tries to get detected at full speed
|
||||
- iGPU DisplayPort (very simple fix, did not have time to fix GPIO)
|
||||
- Automatic fan control (fans will always spin at 50% - see below)
|
||||
- 2x USB2.0 FP and M.2 NGFF USB2.0 not mapped (yet)
|
||||
- PCIe ASPM (results in AER spam in dmesg)
|
||||
|
||||
Please ensure to:
|
||||
- Disable sleep state in your OS.
|
||||
- Disable sleep state in your OS to prevent data loss
|
||||
- Configure automatic fan control using pwmconfig
|
||||
(`modprobe it87 force_id=0x8603`).
|
||||
(`modprobe it87 force_id=0x8603`)
|
||||
- Append `pcie_aspm=off` to your kernel commandline to avoid dmesg spam.
|
||||
|
||||
## Notes
|
||||
|
|
@ -115,7 +126,7 @@ Please ensure to:
|
|||
1. Required blobs, if flashing the entire flash chip.
|
||||
They can be skipped safely if you are planning on flashing
|
||||
only the `SI_BIOS` region.
|
||||
- Intel Flash Descriptor (IFD): `fd.bin`
|
||||
- Intel Flash Descriptor (IFD): `descriptor.bin`
|
||||
- Intel Management Engine (ME): `me.bin`
|
||||
|
||||
Both blobs included in `3rdparty/blobs` repository were extracted
|
||||
|
|
@ -140,21 +151,29 @@ Please ensure to:
|
|||
- U-Boot
|
||||
- LinuxBoot (U-Root + Linux kernel)
|
||||
|
||||
If you would like to see output on your iGPU before that stage
|
||||
(for picking a different boot medium or toggling Secure Boot setting),
|
||||
you need to use [MrChromebox's EDK2] fork and include [GOP driver] for
|
||||
TigerLake iGPU in your build.
|
||||
|
||||
This will allow you to see output of EDK2 (payload, boot picker)
|
||||
on your monitor connected to iGPU.
|
||||
|
||||
If you're planning to primarly use an external card, disable iGPU by
|
||||
enabling `Chipset -> Disable Integrated GFX Controller (0:2:0)`
|
||||
and use [MrChromebox's EDK2] tree.
|
||||
and use [elly's EDK2] tree.
|
||||
|
||||
In order to enable loading Option ROMs from PCIe devices, enable:
|
||||
`Payload -> Load and Execute OpROMs on PCIe devices`
|
||||
In order to enable loading Option ROMs from PCIe devices, go to:
|
||||
`Payload -> edk2 additional custom build parameters`
|
||||
and add the string: `-D LOAD_OPTION_ROMS=TRUE`
|
||||
|
||||
This functionality has been tested with following graphics cards,
|
||||
with following results:
|
||||
- Nvidia GeForce RTX3080, RTX3090: Works perfectly.
|
||||
- AMD Radeon RX6600XT, RX7800XT: Works, but requires
|
||||
`Extend resource window for PCIe devices above 4G` if `Support PCIe
|
||||
Resizable BARs` is enabled.
|
||||
- Intel Arc A580: Works, but option
|
||||
`Extend resource window for PCIe devices above 4G` is **mandatory**.
|
||||
- Nvidia GeForce RTX3080, RTX3090: Works perfectly
|
||||
- AMD Radeon RX6600XT, RX7800XT: Works with ReBAR disabled,
|
||||
no output in EDK2 with ReBAR enabled
|
||||
- Intel Arc A580: Works with ReBAR disabled,
|
||||
corrupted framebuffer before modprobing with ReBAR enabled
|
||||
|
||||
## Specification
|
||||
|
||||
|
|
@ -178,5 +197,7 @@ Resizable BARs` is enabled.
|
|||
[microcodes]: https://github.com/platomav/CPUMicrocodes/tree/master/Intel
|
||||
[FSP XMP Flags]: https://github.com/intel/FSP/blob/master/TigerLakeFspBinPkg/Client/Include/FspmUpd.h#L586-L591
|
||||
[MrChromebox's EDK2]: https://github.com/MrChromebox/edk2
|
||||
[flashrom]: https://flashrom.org/
|
||||
[elly's EDK2]: https://github.com/ellyq/edk2
|
||||
[GOP driver]: https://github.com/MrChromebox/blobs/blob/master/soc/intel/tgl/IntelGopDriver.efi
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[flashprog]: https://flashprog.org/wiki/Flashprog
|
||||
|
|
|
|||
|
|
@ -77,4 +77,4 @@ Specifically, it's a Winbond W25Q64FV (3.3V), whose datasheet can be found
|
|||
|
||||
[W25Q64FW]: https://www.winbond.com/resource-files/w25q64fw%20revn%2005182017%20sfdp.pdf
|
||||
[W25Q64FV]: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -130,4 +130,4 @@ output.
|
|||
|
||||
[W25Q128JVSIQ]: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf
|
||||
[W25Q128JVSIM]: https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -72,4 +72,4 @@ To do this gently take the SPI flash out of its socket and flash with your progr
|
|||
|
||||
[FOXCONN D41S]: http://www.foxconnchannel.com/ProductDetail.aspx?T=motherboard&U=en-us0000481
|
||||
[FOXCONN]: http://www.foxconnchannel.com
|
||||
[Flashrom]: https://flashrom.org/
|
||||
[Flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ This motherboard [also works with Libreboot](https://libreboot.org/docs/install/
|
|||
|
||||
## Preparation
|
||||
|
||||
For more datails how to get sources and build the toolchain, see <project:../../tutorial/part1.md>.
|
||||
```{eval-rst}
|
||||
For more datails how to get sources and build the toolchain, see :doc:`../../tutorial/part1`.
|
||||
```
|
||||
|
||||
### Devuan 4 Chimaera
|
||||
|
||||
|
|
@ -138,8 +140,10 @@ Built gigabyte/ga-g41m-es2l (GA-G41M-ES2L)
|
|||
|
||||
## Flashing coreboot
|
||||
|
||||
```{eval-rst}
|
||||
In addition to the information here, please see the
|
||||
<project:../../tutorial/flashing_firmware/index.md>.
|
||||
:doc:`../../tutorial/flashing_firmware/index`.
|
||||
```
|
||||
|
||||
### Do backup
|
||||
|
||||
|
|
|
|||
|
|
@ -77,4 +77,4 @@ However, this makes DualBIOS unable to recover from a bad flash for some reason.
|
|||
|
||||
[Gigabyte GA-H61M-S2PV]: https://www.gigabyte.com/us/Motherboard/GA-H61M-S2PV-rev-10
|
||||
[Gigabyte]: https://www.gigabyte.com
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -146,4 +146,4 @@ as otherwise there's not enough space near the flash.
|
|||
[IFD Hack]: https://review.coreboot.org/plugins/gitiles/coreboot/+/refs/changes/70/38770/4/Documentation/flash_tutorial/int_macbook.md/
|
||||
[Compaq 8200 Elite SFF]: https://support.hp.com/us-en/document/c03414707
|
||||
[HP]: https://www.hp.com/
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ The following things are still missing from this coreboot port:
|
|||
|
||||
Internal flashing is possible. The SPI flash can be accessed using [flashrom],
|
||||
but you have to short the FDO pins located near the rear USB3 ports on the
|
||||
motherboard using a jumper to temporarily disable write protections while on the
|
||||
stock firmware. Remove the jumper once coreboot is installed.
|
||||
motherboard using a jumper, to temporarily disable write protections while on the
|
||||
stock firmware. Once the coreboot rom is flashed, one should remove the jumper.
|
||||
|
||||
### External programming
|
||||
|
||||
|
|
@ -70,4 +70,4 @@ as otherwise there's not enough space near the flash.
|
|||
|
||||
[Compaq 8300 Elite SFF]: https://support.hp.com/us-en/document/c03345460
|
||||
[HP]: https://www.hp.com/
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ Wake on LAN is active works great.
|
|||
### SuperIO
|
||||
|
||||
This board has a Nuvoton NPCD379 SuperIO chip. Fan speed and PS/2 keyboard work
|
||||
fine using coreboot's existing code for
|
||||
<project:../../superio/nuvoton/npcd378.md>.
|
||||
fine using coreboot's existing code for :doc:`../../superio/nuvoton/npcd378`.
|
||||
|
||||
[Compaq Elite 8300 USDT]: https://support.hp.com/gb-en/product/hp-compaq-elite-8300-ultra-slim-pc/5232866
|
||||
[HP]: https://www.hp.com/
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
# HP Compaq Pro 6300 Series
|
||||
|
||||
This page describes how to run coreboot on the HP [Compaq Pro 6300 Series] desktop.
|
||||
|
||||
These come in two versions: Microtower or Small Form Factor (SFF). They share the same
|
||||
mainboard (657239-001) and Maintenance and Service Guide (690362-003/c04034127). This port has
|
||||
been tested on a SFF unit.
|
||||
|
||||
## Working
|
||||
- i3-3220 / e3-1225v2 CPUs
|
||||
- SeaBIOS (version rel-1.16.3-0-ga6ed6b70)
|
||||
- EDK2 (MrChromebox/2502)
|
||||
- Fedora-mate with Linux kernels 6.11.4, 6.13.6; KDE neon with Linux kernel 6.8.0 / 6.11.0
|
||||
- Mixed memory configurations from single 4GB to 24GB total with 1.35v & 1.5v modules
|
||||
- Integrated Ethernet
|
||||
- Serial port
|
||||
- PS/2 keyboard and mouse
|
||||
- Integrated graphics over DisplayPort and VGA port
|
||||
- libgfxinit textmode (SeaBIOS) / framebuffer (EDK2)
|
||||
- discrete GPU's show boot screen using SeaBIOS / EDK2 (LOAD_OPTION_ROMS=TRUE)
|
||||
- All SATA ports
|
||||
- All USB2 & USB3 ports
|
||||
- PCI slot (Realtek RTL8169 GbE card)
|
||||
- PCIe 3.0 x16 using 2.0 8x 10Gb Intel X540-AT2 / 1.0 16x nVidia GeForce(6200 LE / GT640-2GD3)
|
||||
- PCIe 2.0 x1 using 2.0 1x 2.5Gb Realtek RTL8125
|
||||
- PCIe 2.0 x1 using 1.0 1x 1Gb Intel 82574L (SeaBIOS loads option rom)
|
||||
- Audio built-in speaker (plays music in OS compared to legacy bleep pc-speaker)
|
||||
- Front panel audio ports (front headphone port overrides built-in speaker; only microphone
|
||||
works with combo mic/headphone with TRRS plug)
|
||||
- Back panel audio ports
|
||||
- Sensors CPU and 4 DIMM jc42-i2c sensors
|
||||
- Booting USB / SATA(HDD/DVD)
|
||||
- LEDs HDD, Power(blinks on suspend)
|
||||
- Shutdown, Reboot, Suspend & Wake (USB keyboard & LAN)
|
||||
- Strip down Intel ME/TXE firmware and hide MEI device
|
||||
|
||||
## Untested
|
||||
- Parallel port
|
||||
|
||||
## Not working
|
||||
- Simultaneous use of discrete and integrated graphics
|
||||
|
||||
## TODO
|
||||
|
||||
The following things are still missing from this coreboot port:
|
||||
|
||||
- Extended HWM reporting
|
||||
- Advanced LED control
|
||||
- Advanced power configuration in S3
|
||||
|
||||
## Flashing coreboot
|
||||
|
||||
```{eval-rst}
|
||||
+---------------------+------------+
|
||||
| Type | Value |
|
||||
+=====================+============+
|
||||
| Socketed flash | no |
|
||||
+---------------------+------------+
|
||||
| Model | MT25Q128A |
|
||||
+---------------------+------------+
|
||||
| Size | 16 MiB |
|
||||
+---------------------+------------+
|
||||
| In circuit flashing | yes |
|
||||
+---------------------+------------+
|
||||
| Package | SOIC-16 |
|
||||
+---------------------+------------+
|
||||
| Write protection | Yes |
|
||||
+---------------------+------------+
|
||||
| Dual BIOS feature | No |
|
||||
+---------------------+------------+
|
||||
| Internal flashing | yes |
|
||||
+---------------------+------------+
|
||||
```
|
||||
|
||||
### Internal programming
|
||||
|
||||
Internal flashing is possible. The SPI flash can be accessed using [flashrom],
|
||||
but you have to short the FDO pins located near the rear USB3 ports on the
|
||||
motherboard using a jumper to temporarily disable write protections while on the
|
||||
stock firmware. Remove the jumper once coreboot is installed.
|
||||
|
||||
### External programming
|
||||
|
||||
External programming with an SPI adapter and [flashrom] does work, but it powers the
|
||||
whole southbridge complex. You need to supply enough current through the programming adapter.
|
||||
|
||||
If you want to use a SOIC Pomona test clip, you have to cut the 2nd DRAM DIMM holder,
|
||||
as otherwise there's not enough space near the flash.
|
||||
|
||||
## Technology
|
||||
|
||||
```{eval-rst}
|
||||
+------------------+--------------------------------------------------+
|
||||
| Northbridge | :doc:`../../northbridge/intel/sandybridge/index` |
|
||||
+------------------+--------------------------------------------------+
|
||||
| Southbridge | bd82x6x (Q75) |
|
||||
+------------------+--------------------------------------------------+
|
||||
| CPU | model_206ax |
|
||||
+------------------+--------------------------------------------------+
|
||||
| SuperIO | Nuvoton NPCD379 |
|
||||
+------------------+--------------------------------------------------+
|
||||
| EC | |
|
||||
+------------------+--------------------------------------------------+
|
||||
| Coprocessor | Intel ME |
|
||||
+------------------+--------------------------------------------------+
|
||||
```
|
||||
|
||||
[Compaq Pro 6300 Series]: https://support.hp.com/us-en/product/details/hp-compaq-pro-6300-small-form-factor-pc/model/5232884
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
@ -7,9 +7,6 @@ It is implemented in HP notebooks since 2013, and desktops since 2015.
|
|||
This document talks about some mechanism of HP Sure Start on some machines, and
|
||||
the method to bypass it.
|
||||
|
||||
The method may no longer be applicable to more recent boards. HP has a later
|
||||
[revision of the whitepaper] from 2019 and there may be others.
|
||||
|
||||
## Laptops with SMSC MEC1322 embedded controller
|
||||
|
||||
Haswell EliteBook, ZBook and ProBook 600 series use SMSC MEC1322 embedded controller.
|
||||
|
|
@ -60,5 +57,4 @@ located at the high address of the flash chip (and in the protected region),
|
|||
we can leave it untouched, and do not need to extract the EC firmware to put it in
|
||||
the coreboot image.
|
||||
|
||||
[HP Sure Start Technical Whitepaper]: https://h10032.www1.hp.com/ctg/Manual/c05163901.pdf
|
||||
[revision of the whitepaper]: https://h10032.www1.hp.com/ctg/Manual/c06216928.pdf
|
||||
[HP Sure Start Technical Whitepaper]: http://h10032.www1.hp.com/ctg/Manual/c05163901
|
||||
|
|
|
|||
|
|
@ -1,53 +1,43 @@
|
|||
# HP Pro 3x00 Series
|
||||
# HP Pro 3500 Series
|
||||
|
||||
This page describes how to run coreboot on the [Pro 3400 Series] and [Pro 3500 Series]
|
||||
desktops from [HP].
|
||||
This page describes how to run coreboot on the [Pro 3500 Series]
|
||||
desktop from [HP].
|
||||
|
||||
## State
|
||||
|
||||
All peripherals should work. Automatic fan control as well as S3 are
|
||||
working. The board was tested to boot Linux and Windows. EHCI debug
|
||||
is untested. With disabled ME, the SuperIO will not get CPU
|
||||
temperatures via PECI and therefore the automatic fan control
|
||||
will not increase the fan speed.
|
||||
is untested. When using MrChromebox edk2 with secure boot build in, the
|
||||
board will hang on each boot for about 20 seconds before continuing.
|
||||
With disabled ME, the SuperIO will not get CPU temperatures via PECI and
|
||||
therefore the automatic fan control will not increase the fan speed.
|
||||
|
||||
## Flashing coreboot
|
||||
|
||||
```{eval-rst}
|
||||
+---------------------+-----------------------------------------+
|
||||
| Type | Value |
|
||||
+=====================+=========================================+
|
||||
| Socketed flash | No |
|
||||
+---------------------+-----------------------------------------+
|
||||
| Model | W25Q32BVSIG (3400) / W25Q64FVSIG (3500) |
|
||||
+---------------------+-----------------------------------------+
|
||||
| Size | 4 MiB (3400) / 8 MiB (3500) |
|
||||
+---------------------+-----------------------------------------+
|
||||
| In circuit flashing | Yes |
|
||||
+---------------------+-----------------------------------------+
|
||||
| Package | SOIC-8 |
|
||||
+---------------------+-----------------------------------------+
|
||||
| Write protection | See below |
|
||||
+---------------------+-----------------------------------------+
|
||||
| Dual BIOS feature | No |
|
||||
+---------------------+-----------------------------------------+
|
||||
| Internal flashing | Yes |
|
||||
+---------------------+-----------------------------------------+
|
||||
+---------------------+-------------------------+
|
||||
| Type | Value |
|
||||
+=====================+=========================+
|
||||
| Socketed flash | No |
|
||||
+---------------------+-------------------------+
|
||||
| Model | W25Q64FVSIG |
|
||||
+---------------------+-------------------------+
|
||||
| Size | 8 MiB |
|
||||
+---------------------+-------------------------+
|
||||
| In circuit flashing | Yes |
|
||||
+---------------------+-------------------------+
|
||||
| Package | SOIC-8 |
|
||||
+---------------------+-------------------------+
|
||||
| Write protection | See below |
|
||||
+---------------------+-------------------------+
|
||||
| Dual BIOS feature | No |
|
||||
+---------------------+-------------------------+
|
||||
| Internal flashing | Yes |
|
||||
+---------------------+-------------------------+
|
||||
```
|
||||
|
||||
### Flash layout
|
||||
The original layout of the flash should look like this:
|
||||
|
||||
#### Pro 3400
|
||||
```
|
||||
00000000:00000fff fd
|
||||
00180000:003fffff bios
|
||||
00001000:0017ffff me
|
||||
00fff000:00000fff gbe
|
||||
00fff000:00000fff pd
|
||||
```
|
||||
|
||||
#### Pro 3500
|
||||
```
|
||||
00000000:00000fff fd
|
||||
00400000:007fffff bios
|
||||
|
|
@ -58,7 +48,8 @@ The original layout of the flash should look like this:
|
|||
|
||||
### Internal programming
|
||||
|
||||
The SPI flash can be accessed using [flashrom].
|
||||
The SPI flash can be accessed using [flashrom] (although it reports as
|
||||
"N25Q064..3E", it works fine).
|
||||
|
||||
With a missing FDO jumper, `fd` region is read-only, `bios` region is
|
||||
read-write and `me` region is locked. Vendor firmware will additionally
|
||||
|
|
@ -71,7 +62,9 @@ region will be modified on shutdown. Cut the AC power or do a restart
|
|||
from the OS.
|
||||
|
||||
**Position of FDO jumper (E2) close to the F_USB3**
|
||||

|
||||
![][pro_3500_jumper]
|
||||
|
||||
[pro_3500_jumper]: pro_3500_series_jumper.avif
|
||||
|
||||
### External programming
|
||||
|
||||
|
|
@ -83,7 +76,9 @@ The supply needs to quickly reach 3V3 or else the chip is also unstable
|
|||
until cleanly power cycled.
|
||||
|
||||
**Position of SOIC-8 flash and pin-header near ATX power connector**
|
||||

|
||||
![][pro_3500_flash]
|
||||
|
||||
[pro_3500_flash]: pro_3500_series_flash.avif
|
||||
|
||||
## Technology
|
||||
|
||||
|
|
@ -103,7 +98,6 @@ until cleanly power cycled.
|
|||
+------------------+--------------------------------------------------+
|
||||
```
|
||||
|
||||
[Pro 3400 Series]: https://support.hp.com/us-en/product/details/hp-pro-3400-microtower-pc/5160137
|
||||
[Pro 3500 Series]: https://support.hp.com/us-en/product/details/hp-pro-3500-microtower-pc/5270849
|
||||
[Pro 3500 Series]: https://support.hp.com/us-en/document/c03364089
|
||||
[HP]: https://www.hp.com/
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
|
@ -77,4 +77,4 @@ even interchangeable, so should do coreboot images built for them.
|
|||
[HP Z220 SFF Workstation]: https://support.hp.com/za-en/document/c03386950
|
||||
[HP Compaq Elite 8300 SFF]: https://support.hp.com/us-en/document/c03345460
|
||||
[HP]: https://www.hp.com/
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ IMB-1222 <asrock/imb-1222.md>
|
|||
|
||||
A88XM-E <asus/a88xm-e.md>
|
||||
F2A85-M <asus/f2a85-m.md>
|
||||
H610i-PLUS D4 <asus/h610i-plus-d4>
|
||||
P2B-LS <asus/p2b-ls.md>
|
||||
P3B-F <asus/p3b-f.md>
|
||||
P5Q <asus/p5q.md>
|
||||
|
|
@ -54,7 +53,6 @@ P8H77-V <asus/p8h77-v.md>
|
|||
P8Z77-M <asus/p8z77-m.md>
|
||||
P8Z77-M Pro <asus/p8z77-m_pro.md>
|
||||
P8Z77-V <asus/p8z77-v.md>
|
||||
P8Z77-V LE PLUS <asus/p8z77-v_le_plus.md>
|
||||
wifigo_v1 <asus/wifigo_v1.md>
|
||||
```
|
||||
|
||||
|
|
@ -141,8 +139,7 @@ GA-H61M-S2PV <gigabyte/ga-h61m-s2pv.md>
|
|||
Compaq 8200 Elite SFF <hp/compaq_8200_sff.md>
|
||||
Compaq 8300 Elite SFF <hp/compaq_8300_sff.md>
|
||||
Compaq Elite 8300 USDT <hp/compaq_8300_usdt.md>
|
||||
Compaq Pro 6300 Series Microtower/SFF <hp/compaq_pro_6300_series.md>
|
||||
Pro 3x00 Series <hp/pro_3x00_series.md>
|
||||
Pro 3500 Series <hp/pro_3500_series.md>
|
||||
Z220 Workstation SFF <hp/z220_sff.md>
|
||||
```
|
||||
|
||||
|
|
@ -187,8 +184,6 @@ mAL-10 <kontron/mal10.md>
|
|||
Mainboard codenames <lenovo/codenames.md>
|
||||
Hardware Maintenance Manual of ThinkPads <lenovo/thinkpad_hmm.md>
|
||||
R60 <lenovo/r60.md>
|
||||
ThinkCentre M710s <lenovo/thinkcentre_m710s.md>
|
||||
ThinkCentre M700 / M900 Tiny <lenovo/thinkcentre_m900_tiny.md>
|
||||
T4xx common <lenovo/t4xx_series.md>
|
||||
X2xx common <lenovo/x2xx_series.md>
|
||||
M920 Tiny <lenovo/m920q.md>
|
||||
|
|
@ -243,13 +238,6 @@ Internal flashing <lenovo/ivb_internal_flashing.md>
|
|||
T440p <lenovo/t440p.md>
|
||||
```
|
||||
|
||||
### Skylake/Kabylake series
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
T470s/T480/T480s/T580/X280 <lenovo/skylake.md>
|
||||
```
|
||||
## Libretrend
|
||||
|
||||
```{toctree}
|
||||
|
|
@ -358,10 +346,8 @@ StarBook Mk V <starlabs/starbook_tgl.md>
|
|||
StarBook Mk VI <starlabs/starbook_adl.md>
|
||||
StarBook Mk VII (N200) <starlabs/starbook_adl_n.md>
|
||||
StarBook Mk VII (165H) <starlabs/starbook_mtl.md>
|
||||
StarBook Horizon <starlabs/adl_horizon.md>
|
||||
Byte Mk II <starlabs/byte.md>
|
||||
Byte Mk II <starlabs/byte_adl.md>
|
||||
StarFighter Mk I <starlabs/starfighter_rpl.md>
|
||||
StarFighter Mk II <starlabs/starfighter_mtl.md>
|
||||
|
||||
Building coreboot <starlabs/common/building.md>
|
||||
Flashing devices <starlabs/common/flashing.md>
|
||||
|
|
|
|||
|
|
@ -96,4 +96,4 @@ The layout of the header is:
|
|||
```
|
||||
|
||||
[Intel DG43GT]: https://ark.intel.com/products/41036/Intel-Desktop-Board-DG43GT
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -104,9 +104,11 @@ the PCI configuration space of the LPC Interface Bridge, is set.
|
|||
It is possible to program the chip is to attach an external programmer
|
||||
with an SOIC-8 clip.
|
||||
|
||||
```{eval-rst}
|
||||
Another way is to boot the vendor firmware in UEFI mode and exploit the
|
||||
unpatched S3 Boot Script vulnerability. See this page for a similar procedure:
|
||||
<project:../lenovo/ivb_internal_flashing.md>.
|
||||
:doc:`../lenovo/ivb_internal_flashing`.
|
||||
```
|
||||
|
||||
On this specific board it is possible to prevent the BLE bit from being set
|
||||
when it resumes from S3. One entry in the S3 Boot Script must be modified,
|
||||
|
|
@ -124,10 +126,12 @@ The boot script contains an entry that writes 0x02 to memory at address
|
|||
Interface Bridge [0][1]. The value 0x02 sets the BLE bit, and the modification
|
||||
prevents this by making it write a 0 instead.
|
||||
|
||||
```{eval-rst}
|
||||
After suspending and resuming the board, the BIOS region can be flashed with
|
||||
a coreboot image, e.g. using flashrom. Note that the ME region is not readable,
|
||||
so the `--noverify-all` flag is necessary. Please refer to the
|
||||
<project:../../tutorial/flashing_firmware/index.md>.
|
||||
:doc:`../../tutorial/flashing_firmware/index`.
|
||||
```
|
||||
|
||||
## Hardware monitoring and fan control
|
||||
|
||||
|
|
|
|||
|
|
@ -76,4 +76,4 @@ $ flashrom -p internal --ifd -i bios -w coreboot.rom --noverify-all
|
|||
```
|
||||
|
||||
[W25Q128FV]: https://www.winbond.com/resource-files/w25q128fv%20rev.m%2005132016%20kms.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -95,6 +95,6 @@ close to the CPU.
|
|||
|
||||
[mAL10]: https://www.kontron.com/products/iot/iot-industry-4.0/iot-ready-boards-and-modules/com-express/com-express-mini/come-mal10-e2-.html
|
||||
[W25Q128FV]: https://www.winbond.com/resource-files/w25q128fv%20rev.m%2005132016%20kms.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[NCT7802Y]: https://www.nuvoton.com/products/cloud-computing/hardware-monitors/desktop-server-series/nct7802y/?__locale=en
|
||||
[crashes]: https://pastebin.com/cpCfrPCL
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ This information is valid for all supported models, except T430s, [T431s](t431s.
|
|||
exceed 4MiB in size, which means CONFIG_CBFS_SIZE must be smaller than 4MiB.
|
||||
* ROM chip size should be set to 12MiB.
|
||||
|
||||
Please also have a look at <project:../../tutorial/flashing_firmware/index.md>.
|
||||
```{eval-rst}
|
||||
Please also have a look at :doc:`../../tutorial/flashing_firmware/index`.
|
||||
```
|
||||
|
||||
## Splitting the coreboot.rom
|
||||
|
||||
|
|
|
|||
|
|
@ -98,5 +98,5 @@ Tested with edk2 payload (mrchromebox) and Ubuntu 22.04 (Linux 6.2.0):
|
|||
- another riser with PCIe x4 connector remains untested - please modify this
|
||||
page if you do test it!
|
||||
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[Lenovo M920 Tiny specifications]: https://psref.lenovo.com/syspool/Sys/PDF/ThinkCentre/ThinkCentre_M920_Tiny/ThinkCentre_M920_Tiny_Spec.PDF
|
||||
|
|
|
|||
|
|
@ -1,232 +0,0 @@
|
|||
# Lenovo ThinkPad T470s/T480/T480s/T580/X280
|
||||
|
||||
This page describes how to run coreboot on the Lenovo [Thinkpad T470s], [ThinkPad T480],
|
||||
[Thinkpad T480s], [Thinkpad T580], and [Thinkpad X280].
|
||||
|
||||
## Important Notes
|
||||
|
||||
### EC UART
|
||||
The T480 (but not T480s) supports the use of the EC UART for debugging. If you want to use this
|
||||
feature, you need to downgrade the vendor firmware to a version with a compatible EC firmware
|
||||
*before flashing* for the EC UART to work properly. The EC firmware version needs to be 1.22
|
||||
(N24HT37W), which means any BIOS from 1.39 (N24ET64W) to 1.54 (N24ET79W) is acceptable.
|
||||
|
||||
The mapping can be seen here [on Lenovo's site].
|
||||
|
||||
### Boot Guard
|
||||
Most Thinkpads newer than the xx30 models except machines with swappable CPUs (T440p, W541) and
|
||||
some models in the E series (E460, E470) have Intel Boot Guard enabled. This prevents these
|
||||
laptops from running custom firmware which is not cryptographically signed with the manufacturer's
|
||||
key.
|
||||
|
||||
The [deguard utility](../../soc/intel/deguard) will need to be used in order to bypass Boot Guard
|
||||
on these devices.
|
||||
|
||||
### Thunderbolt
|
||||
|
||||
Older versions of Thunderbolt 3 controller firmware are buggy on these laptops.
|
||||
|
||||
The issue is that the TB3 firmware writes its logging data to the TB3's own 4M flash chip, and
|
||||
when the flash chip fills up, the device fails to boot.
|
||||
|
||||
Lenovo has addressed this in a TB3 firmware update, which is recommended be be applied before
|
||||
installing coreboot to ensure long-term reliability of the device. That said, most laptops that
|
||||
still work likely have this fix already applied, and you can always externally flash the TB3
|
||||
firmware update at the same time as flashing coreboot.
|
||||
|
||||
If your device becomes bricked due to this issue, the only resolution is to externally flash the
|
||||
updated/fixed TB3 firmware flash (which is located on a separate flash IC from the main firmware).
|
||||
|
||||
The Lenovo TB3 FW update can be obtained [from Lenovo's site].
|
||||
|
||||
The Libreboot project has some more information about the issue and [how to externally flash the
|
||||
TB3 firmware].
|
||||
|
||||
## Initial Setup
|
||||
|
||||
Follow the coreboot [tutorial](../../tutorial/part1) to:
|
||||
* install dependencies/prerequisites
|
||||
* clone the coreboot repo
|
||||
* build the coreboot toolchain
|
||||
|
||||
You will also need to clone the [deguard](https://review.coreboot.org/deguard) repository in
|
||||
order to bypass Boot Guard.
|
||||
|
||||
## Required proprietary blobs
|
||||
|
||||
When flashing the laptop for the first time, the following blobs are required for a full
|
||||
flash image:
|
||||
* Flash Descriptor (IFD)
|
||||
* Management Engine (ME)
|
||||
* Gigabit Ethernet (GBE)
|
||||
|
||||
These will either be extracted from the vendor firmware on the device or downloaded as needed.
|
||||
|
||||
Additionally, all Skylake/Kabylake machines require Intel's Firmware Support Package (FSP) for
|
||||
hardware initialization. FSP is provided by default as part of the coreboot build, no user
|
||||
intervention is required.
|
||||
|
||||
## Reading the vendor firmware
|
||||
|
||||
The Skylake Thinkpads have a single BIOS flash chip (16 MiB serial flash in a SOIC-8 package).
|
||||
The flash chip uses the following layout:
|
||||
|
||||
00000000:00000fff flash descriptor (fd)
|
||||
00001000:00002fff gbe
|
||||
00003000:006fffff me
|
||||
00700000:00ffffff bios
|
||||
|
||||
To read the vendor firmware, disconnect external power and remove the back of the laptop
|
||||
as described in the [hardware maintenance manual](thinkpad_hmm).
|
||||
Disconnect/remove all batteries (and CMOS battery if equipped). Attach a chip clip to the flash.
|
||||
Use an external SPI programmer (ch341a, raspberry Pi, etc) to read the chip `bios.bin`.
|
||||
|
||||
## Preparing the binaries
|
||||
|
||||
The IFD and GBE need to be extracted from the vendor firmware backup read from the flash chip.
|
||||
See [Extract IFD binaries](../../util/ifdtool/binary_extraction).
|
||||
|
||||
ifdtool -x -p sklkbl bios.bin
|
||||
|
||||
Rename the extracted files to `ifd.bin` and `gbe.bin` and move them to the `binaries` folder
|
||||
you created per the instructions.
|
||||
|
||||
### Preparing the ME with deguard
|
||||
|
||||
Please review the documentation on the use of the [deguard utility](../../soc/intel/deguard).
|
||||
|
||||
All Skylake/Kabylake Thinkpads supported by coreboot are also supported in deguard. Hence, we
|
||||
simply need to download/extract the donor image, then generate the "deguarded" ME firmware
|
||||
image.
|
||||
|
||||
The donor ME binary required for use with the deguard utility can be downloaded as part of the
|
||||
[Dell firmware updater]. After downloading, use [Dell_PFS_Extract.py] to extract the required ME
|
||||
firmware image from the updater:
|
||||
|
||||
python Dell_PFS_Extract.py Inspiron_5468_1.3.0.exe
|
||||
|
||||
The resulting binary should be renamed `me_donor.bin` (file size 0x1f0000 bytes, sha256
|
||||
`912271bb3ff2cf0e2e27ccfb94337baaca027e6c90b4245f9807a592c8a652e1`) and moved to the `binaries`
|
||||
folder with the IFD and GBE binaries.
|
||||
|
||||
Then, generate the deguarded ME firmware image adjusting the `--delta` argument according to
|
||||
your laptop's model:
|
||||
|
||||
./finalimage.py --delta data/delta/thinkpad_t480 --version 11.6.0.1126 --pch LP --sku 2M --fake-fpfs data/fpfs/zero --input ../coreboot/binaries/me_donor.bin --output ../coreboot/binaries/me_deguarded.bin
|
||||
|
||||
The command above assumes you are running deguard from the root of the deguard repo, that the
|
||||
deguard and coreboot repos are checked out under the same parent directory, and that your ME
|
||||
donor firmware is in the `binaries` subdirectory of coreboot (along with the IFD/GBE binaries).
|
||||
Adjust the paths as necessary if that is not the case.
|
||||
|
||||
Please be careful with [me_cleaner](../../northbridge/intel/sandybridge/me_cleaner). While
|
||||
me_cleaner is generally expected to work, truncating the ME binary can have unintended side
|
||||
effects like disabling PCIe devices on Thinkpad T470s (NVMe, WLAN, WWAN, etc.).
|
||||
|
||||
### Preparing the IFD
|
||||
|
||||
In order to use the modified ME firmware binary generated by deguard above, we need to set the
|
||||
HAP bit in the IFD using `ifdtool`:
|
||||
|
||||
util/ifdtool/ifdtool -p sklkbl -M 1 binaries/ifd.bin
|
||||
|
||||
The modified IFD will be saved as `ifd.bin.new`. Rename the original file to `ifd.bin.orig` and
|
||||
the HAP-enabled one to `ifd.bin`
|
||||
|
||||
If you want to allocate the space that has become available from truncating the ME firmware to
|
||||
corebios, you can modify the IFD layout. First, save the layout below into a text file
|
||||
`layout.txt`:
|
||||
|
||||
00000000:00000fff fd
|
||||
001f4000:00ffffff bios
|
||||
00003000:001f3fff me
|
||||
00001000:00002fff gbe
|
||||
|
||||
Then run ifdtool again:
|
||||
|
||||
util/ifdtool/ifdtool -p sklkbl -n layout.txt binaries/ifd.bin
|
||||
|
||||
Once again, the modified IFD will be saved as `ifd.bin.new`. Rename `ifd.bin.new` to `ifd.bin`.
|
||||
|
||||
The layout above allows you to maximize the CBFS size in your coreboot `.config`:
|
||||
|
||||
CONFIG_CBFS_SIZE=0xE0C000
|
||||
|
||||
## Building coreboot
|
||||
|
||||
You can use `make menuconfig` or `make nconfig` to select the mainboard matching your machine,
|
||||
enable the inclusion of the IFD/ME/GBE binaries, and select your payload and options.
|
||||
For example, you can also just use the following defconfig for a Thinkpad T480 with EDK2/UEFI
|
||||
as a payload:
|
||||
|
||||
CONFIG_VENDOR_LENOVO=y
|
||||
CONFIG_BOARD_LENOVO_T480=y
|
||||
CONFIG_IFD_BIN_PATH="binaries/ifd.bin"
|
||||
CONFIG_ME_BIN_PATH="binaries/me_deguarded.bin"
|
||||
CONFIG_GBE_BIN_PATH="binaries/gbe.bin"
|
||||
CONFIG_HAVE_IFD_BIN=y
|
||||
CONFIG_HAVE_ME_BIN=y
|
||||
CONFIG_HAVE_GBE_BIN=y
|
||||
CONFIG_PAYLOAD_EDK2=y
|
||||
|
||||
Save the above to a file called `defconfig` in your coreboot directory, then run:
|
||||
|
||||
make defconfig
|
||||
make -j"$(nproc)"
|
||||
|
||||
Upon successful compilation, you will have a file `coreboot.rom` in the `build` directory ready
|
||||
to flash.
|
||||
|
||||
## Flashing instructions
|
||||
|
||||
The initial flash of coreboot with the modified IFD and deguarded ME must be done using an
|
||||
external programmer:
|
||||
|
||||
sudo flashrom -p <programmer> -w coreboot.rom
|
||||
|
||||
Subsequent flashes can be done internally and need only modify the `bios` region of the flash:
|
||||
|
||||
sudo flashrom -p internal -w coreboot.rom --ifd -i bios -N
|
||||
|
||||
The `coreboot.rom` flashed on subsequent flashes does not need to contain the IFD/ME/GBE
|
||||
binaries if only flashing the `bios` region.
|
||||
|
||||
## Known Issues
|
||||
|
||||
- Some Fn+F{1-12} keys aren't handled correctly
|
||||
- Nvidia dGPU is finicky
|
||||
- Needs option ROM
|
||||
- Power enable code is buggy
|
||||
- Proprietary driver does not work at all
|
||||
- Nouveau only works on linux 6.8-6.9
|
||||
- Headphone jack detection
|
||||
- Both headphone jack and speakers work when manually selected via pulseaudio
|
||||
|
||||
## Verified Working
|
||||
|
||||
- Alpine Ridge Thunderbolt 3 controller
|
||||
- Integrated graphics init with libgfxinit
|
||||
- video output: internal (eDP), miniDP
|
||||
- ACPI support
|
||||
- keyboard and trackpoint
|
||||
- SATA
|
||||
- M.2 SATA SSD
|
||||
- NVMe
|
||||
- USB
|
||||
- Ethernet
|
||||
- WLAN
|
||||
- WWAN
|
||||
- Bluetooth
|
||||
- Virtualization: VT-x and VT-d
|
||||
- Internal flashing (after initial flash with unlocked IFD)
|
||||
|
||||
[Thinkpad T470s]: https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkpad-t-series-laptops/thinkpad-t470s/
|
||||
[ThinkPad T480]: https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkpad-t-series-laptops/thinkpad-t480-type-20l5-20l6/
|
||||
[ThinkPad T480s]: https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkpad-t-series-laptops/thinkpad-t480s-type-20l7-20l8/
|
||||
[Thinkpad T580]: https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkpad-t-series-laptops/thinkpad-t580-type-20l9-20la/
|
||||
[Thinkpad X280]: https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkpad-x-series-laptops/thinkpad-x280-type-20kf-20ke/
|
||||
[on Lenovo's site]: https://support.lenovo.com/us/en/downloads/ds502355-bios-update-utility-bootable-cd-for-windows-10-64-bit-linux-thinkpad-t480
|
||||
[from Lenovo's site]: https://pcsupport.lenovo.com/gb/en/products/laptops-and-netbooks/thinkpad-t-series-laptops/thinkpad-t480s-type-20l7-20l8/solutions/ht508988-critical-intel-thunderbolt-software-and-firmware-updates-thinkpad
|
||||
[how to externally flash the TB3 firmware]: https://libreboot.org/docs/install/t480.html#thunderbolt-issue-read-this-before-flashing
|
||||
[Dell firmware updater]: https://web.archive.org/web/20241110222323/https://dl.dell.com/FOLDER04573471M/1/Inspiron_5468_1.3.0.exe
|
||||
[Dell_PFS_Extract.py]: https://github.com/vuquangtrong/Dell-PFS-BIOS-Assembler/blob/master/Dell_PFS_Extract.py
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
# Lenovo ThinkCentre M710s
|
||||
|
||||
This page provides technical documentation on [Lenovo ThinkCentre M710s].
|
||||
|
||||
## Flash chip
|
||||
|
||||
```{eval-rst}
|
||||
+----------+-------------+
|
||||
| Type | Value |
|
||||
+==========+=============+
|
||||
| Socketed | yes |
|
||||
+----------+-------------+
|
||||
| Model | W25Q64JV-.Q |
|
||||
+----------+-------------+
|
||||
| Size | 8MiB |
|
||||
+----------+-------------+
|
||||
| Package | SOIC-8 |
|
||||
+----------+-------------+
|
||||
```
|
||||
|
||||
The flash chip is divided into the following regions.
|
||||
|
||||
00000000:00000fff fd
|
||||
00200000:007fffff bios
|
||||
00003000:001fffff me
|
||||
00001000:00002fff gbe
|
||||
|
||||
## Flashing
|
||||
|
||||
The flash chip cannot be flashed internally when running vendor firmware, and must
|
||||
be flashed externally using a programmer of your choice.
|
||||
|
||||
Steps on how to open the chassis and get access to the mainboard are described
|
||||
in the [hardware maintenance manual]. Follow the steps shown from
|
||||
"[Removing the computer cover]" until step 1 of "[Replacing the storage drive]".
|
||||
|
||||
The SPI flash should be easy to identify and the location is shown in the image
|
||||
below. See the [datasheet] and [flashing firmware tutorial] for more information.
|
||||
|
||||

|
||||
|
||||
## Status
|
||||
|
||||
### Working
|
||||
* Ubuntu 22.04.1 (Linux 6.5.0) using payloads:
|
||||
* SeaBIOS
|
||||
* MrChromebox's EDK 2 fork
|
||||
* Tianocore's EDK 2
|
||||
* Internal flashing (from coreboot)
|
||||
* PEG (PCIe Graphics)
|
||||
* PCIe
|
||||
* SATA
|
||||
* M.2 SSD
|
||||
* M.2 WLAN (+ Bluetooth)
|
||||
* LAN
|
||||
* USB
|
||||
* Memory card reader
|
||||
* CPU fan
|
||||
* VGA
|
||||
* Display ports
|
||||
* Audio (output)
|
||||
* COM1
|
||||
* TPM
|
||||
|
||||
### Not working
|
||||
* Super I/O not well supported (there may be some minor issues)
|
||||
* Power button LED
|
||||
* ME cleaner
|
||||
|
||||
### Untested
|
||||
* Audio (input)
|
||||
* COM2 header
|
||||
* LPT header
|
||||
* PS/2 keyboard and mouse
|
||||
|
||||
[Lenovo ThinkCentre M710s]: https://www.lenovo.com/us/en/p/desktops/thinkcentre/m-series-sff/thinkcentre-m710s/11tc1md710s
|
||||
[hardware maintenance manual]: https://download.lenovo.com/pccbbs/thinkcentre_pdf/m710s_ug_hmm_en.pdf
|
||||
[Removing the computer cover]: https://download.lenovo.com/pccbbs/thinkcentre_pdf/m710s_ug_hmm_en.pdf#page=28
|
||||
[Replacing the storage drive]: https://download.lenovo.com/pccbbs/thinkcentre_pdf/m710s_ug_hmm_en.pdf#page=31
|
||||
[datasheet]: https://www.winbond.com/hq/product/code-storage-flash-memory/serial-nor-flash/?__locale=en&partNo=W25Q64JV
|
||||
[flashing firmware tutorial]: ../../tutorial/flashing_firmware/index.md
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
|
|
@ -1,88 +0,0 @@
|
|||
# Lenovo ThinkCentre M700 / M900 Tiny
|
||||
|
||||
This page provides technical documentation on [Lenovo ThinkCentre M700 / M900 Tiny].
|
||||
|
||||
M700 Tiny and M900 Tiny are twin designs using the same exact mainboard, with
|
||||
the following differences:
|
||||
|
||||
```{eval-rst}
|
||||
+------------------------+------+------+
|
||||
| Feature | M700 | M900 |
|
||||
+========================+======+======+
|
||||
| Chipset | B150 | Q170 |
|
||||
+------------------------+------+------+
|
||||
| Intel AMT | No | Yes |
|
||||
+------------------------+------+------+
|
||||
| Intel TXT | No | Yes |
|
||||
+------------------------+------+------+
|
||||
| PCIe lanes in M.2 slot | No | Yes |
|
||||
+------------------------+------+------+
|
||||
```
|
||||
|
||||
## Flash chip
|
||||
|
||||
```{eval-rst}
|
||||
+----------+--------------------------+
|
||||
| Type | Value |
|
||||
+==========+==========================+
|
||||
| Socketed | no |
|
||||
+----------+--------------------------+
|
||||
| Model | W25Q128.V or MX25L12873F |
|
||||
+----------+--------------------------+
|
||||
| Size | 16MiB |
|
||||
+----------+--------------------------+
|
||||
| Package | SOIC-8 |
|
||||
+----------+--------------------------+
|
||||
```
|
||||
|
||||
The flash chip is divided into the following regions.
|
||||
|
||||
00000000:00000fff fd
|
||||
00001000:00002fff gbe
|
||||
00003000:007fffff me
|
||||
00800000:00ffffff bios
|
||||
|
||||
## Flashing
|
||||
|
||||
The flash chip cannot be flashed internally when running vendor firmware, and must
|
||||
be flashed externally using a programmer of your choice.
|
||||
|
||||
Steps on how to open the chassis and get access to the mainboard are described
|
||||
in the [hardware maintenance manual]. Follow the steps shown from
|
||||
"[Removing the computer cover]" until "[Replacing the M.2 storage drive]".
|
||||
|
||||
The SPI flash should be easy to identify and the location is shown in the image
|
||||
below. See the [datasheet] and [flashing firmware tutorial] for more information.
|
||||
|
||||

|
||||
|
||||
## Status
|
||||
|
||||
### Working
|
||||
|
||||
* Debian 12 (Linux 6.1.0) using MrChromebox' EDK II fork (uefipayload_2502)
|
||||
* M.2 SATA + NVMe slot
|
||||
* M.2 Wi-Fi slot
|
||||
* Display ports
|
||||
* USB
|
||||
* Audio
|
||||
* LAN
|
||||
* CPU fan
|
||||
* Discrete TPM 1.2
|
||||
* Internal flashing (from coreboot)
|
||||
* COM1 header
|
||||
* DisplayPort header
|
||||
* Power LED
|
||||
* S3 Suspend
|
||||
|
||||
### Untested
|
||||
|
||||
* PS/2 header
|
||||
* PCIe + SATA "2L" expansion header
|
||||
|
||||
[Lenovo ThinkCentre M700 / M900 Tiny]: https://psref.lenovo.com/syspool/Sys/PDF/ThinkCentre/ThinkCentre_M900_Tiny/ThinkCentre_M900_Tiny_Spec.PDF
|
||||
[hardware maintenance manual]: https://download.lenovo.com/pccbbs/thinkcentre_pdf/m700_m900_m900x_tiny_hmm.pdf
|
||||
[Removing the computer cover]: https://download.lenovo.com/pccbbs/thinkcentre_pdf/m700_m900_m900x_tiny_hmm.pdf#page=119
|
||||
[Replacing the M.2 storage drive]: https://download.lenovo.com/pccbbs/thinkcentre_pdf/m700_m900_m900x_tiny_hmm.pdf#page=134
|
||||
[datasheet]: https://www.mouser.com/datasheet/2/949/w25q128jv_revf_03272018_plus-1489608.pdf
|
||||
[flashing firmware tutorial]: ../../tutorial/flashing_firmware/index.md
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 204 KiB |
|
|
@ -112,6 +112,6 @@ not publicly available.
|
|||
|
||||
[Libretrend LT1000]: https://libretrend.com/specs/librebox/
|
||||
[W25Q64FV]: https://www.winbond.com/resource-files/w25q64fv%20revs%2007182017.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[baseboard site]: http://www.minicase.net/product_LR-i7S65T1.html
|
||||
[custom module]: https://shop.3mdeb.com/product/tpm2-module-for-librebox/
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ and [u-root] as initramfs.
|
|||
[The Wiwynn's Yosemite-V3 product in OCP market place]: https://www.opencompute.org/products/423/wiwynn-yosemite-v3-server
|
||||
[osf-builder]: https://github.com/facebookincubator/osf-builder
|
||||
[OCP virtual summit 2020]: https://www.opencompute.org/summit/virtual-summit/schedule
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[All about u-root]: https://github.com/linuxboot/book/tree/master/u-root
|
||||
[u-root]: https://u-root.org/
|
||||
[ChromeOS VPD]: https://chromium.googlesource.com/chromiumos/platform/vpd/+/master/README.md
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ u-root.
|
|||
+------------------------+---------------------------------------------+
|
||||
```
|
||||
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[OCP]: https://www.opencompute.org/
|
||||
[OCP Tioga Pass]: http://files.opencompute.org/oc/public.php?service=files&t=6fc3033e64fb029b0f84be5a8faf47e8
|
||||
[OCP Market Place]: https://www.opencompute.org/products/109/wiwynn-tioga-pass-advanced-2u-ocp-server-up-to-768gb-12-dimm-slots-4-ssds-for-io-performance
|
||||
|
|
|
|||
|
|
@ -81,4 +81,4 @@ Dediprog compatible pinout.
|
|||
|
||||
[Elgon]: https://github.com/Telecominfraproject/OpenCellular
|
||||
[OpenCellular]: https://code.fb.com/connectivity/introducing-opencellular-an-open-source-wireless-access-platform/
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -94,4 +94,4 @@ site. Depending on the configuration:
|
|||
|
||||
[apu1c1_flash]: apu1c1.jpg
|
||||
[spi_header]: apu1_spi.jpg
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -113,4 +113,4 @@ are available for free on PC Engines official site. Both configurations
|
|||
|
||||
[apu2_flash]: apu2.jpg
|
||||
[spi_header]: apu2_spi.jpg
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
|
|
|
|||
|
|
@ -75,5 +75,5 @@ serial/video/pcie ports might be available.
|
|||
|
||||
[Portwell PQ7-M107]: http://portwell.com/products/detail.php?CUSTCHAR1=PQ7-M107
|
||||
[W25Q64FW]: https://www.winbond.com/resource-files/w25q64fw%20revn%2005182017%20sfdp.pdf
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[Board manual]: www.portwell.com/pdf/embedded/PQ7-M107.pdf
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ The board features:
|
|||
|
||||
## Extra links
|
||||
|
||||
[flashrom]: https://flashrom.org/
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[flashing tutorial]: ../../tutorial/flashing_firmware/ext_power.md
|
||||
[Intel FSP2.0]: ../../soc/intel/fsp/index.md
|
||||
[AST2500]: https://www.aspeedtech.com/products.php?fPath=20&rId=440
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue