Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded' and distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Regards, SImon
On 28.09.20 18:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded' and distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
The Linux kernel has an EFI stub. This EFI stub consumes UEFI services and starts the main part of the kernel.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
Which app can be the Linux, Windows, BSD or if you do not want to start an operating system directly it can be a boot manager like GRUB, iPXE, rEFInd.
The strength of the UEFI spec is that it just provides an API and does not prescribe what you use it for. This gives much more choice than a firmware tied down to boot one specific OS only.
It is like with nuts and bolts. An M6 nut will fit onto any M6 bolt anywhere in the world. - But beware of 1/4" UNC parts. They only fit to their kin.
How about playing a game written for the EFI API: https://github.com/Openwide-Ingenierie/Pong-UEFI
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
In Linux you can use the command efibootmgr to set up the boot variables if the UEFI firmware offers the SetVariable() runtime service. (U-Boot does not offer it at runtime) With the variables you can only specify a binary that is started and "load options". Linux uses these load options as command line. A Linux command line parameter (initrd=<path>) can be used to specify the initial ramdisk.
The UEFI boot manager (in U-Boot command 'bootefi bootmgr') uses the BootNext and BootOrder variables to identify which BootXXXX variable contains the specification of the binary to start.
The entry point of an UEFI binary (e.g. the Linux EFI stub) receives two parameters: the system table pointer, and the handle of the loaded image. The UEFI system table points to a list of configuration tables. These may include ACPI, SMBIOS, device-tree, and more. The Linux command line can be retrieved via the LoadOptions field of the loaded image protocol installed on the handle passed to the entry point.
It is the task of the firmware to set up a configuration table with the device tree or the ACPI table.
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Secure Boot in UEFI terms involves setting up the PK, KEK, db, and dbx variables. Then every UEFI binary loaded can only be started via service StartImage() if it was signed with a certificate with a public key in db and not in dbx.
Verification of intrd and fdt is out of scope for the UEFI specification.
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Debian/Ubuntu, Fedora and Suse all use UEFI [-> shim] -> GRUB -> Linux if the firmware running the installer image supports UEFI.
FreeBSD and OpenBSD can be started as UEFI payloads but they typically do not use GRUB.
Here is a good starting point: https://wiki.ubuntu.com/UEFI/SecureBoot
Best regards
Heinrich
On Mon, 28 Sep 2020 at 20:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 28.09.20 18:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded' and distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
The Linux kernel has an EFI stub. This EFI stub consumes UEFI services and starts the main part of the kernel.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
Which app can be the Linux, Windows, BSD or if you do not want to start an operating system directly it can be a boot manager like GRUB, iPXE, rEFInd.
The strength of the UEFI spec is that it just provides an API and does not prescribe what you use it for. This gives much more choice than a firmware tied down to boot one specific OS only.
It is like with nuts and bolts. An M6 nut will fit onto any M6 bolt anywhere in the world. - But beware of 1/4" UNC parts. They only fit to their kin.
How about playing a game written for the EFI API: https://github.com/Openwide-Ingenierie/Pong-UEFI
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
This is indeed a priority and I was preparing the agenda for the next DTE
call and cover this exact topic. I am going to share my work in progress: https://docs.google.com/presentation/d/1JK00su6e7vt8lRfwSt2C9EuuzwcBHLyoLRRr...
It is definitively not fully baked but you just triggered the need to share it as it is.
Please not that this effort is meant so that:
- we share a common understanding and vocabulary about the boot flows (bootloader end up meaning not much) and what is part of the OS, the firmware, the Handover Function (is Grub to be considered here as a Linux artifact or a generic thing?) - we share a common understanding of "validation domains" (chain of <chains of trusts>) - There is a slide to be filled during or after the discussion about the reference boot flow(s) we need to consider (and probably a subset we will consider for the LEDGE Reference Platform).
In Linux you can use the command efibootmgr to set up the boot variables if the UEFI firmware offers the SetVariable() runtime service. (U-Boot does not offer it at runtime) With the variables you can only specify a binary that is started and "load options". Linux uses these load options as command line. A Linux command line parameter (initrd=<path>) can be used to specify the initial ramdisk.
The UEFI boot manager (in U-Boot command 'bootefi bootmgr') uses the BootNext and BootOrder variables to identify which BootXXXX variable contains the specification of the binary to start.
The entry point of an UEFI binary (e.g. the Linux EFI stub) receives two parameters: the system table pointer, and the handle of the loaded image. The UEFI system table points to a list of configuration tables. These may include ACPI, SMBIOS, device-tree, and more. The Linux command line can be retrieved via the LoadOptions field of the loaded image protocol installed on the handle passed to the entry point.
It is the task of the firmware to set up a configuration table with the device tree or the ACPI table.
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Secure Boot in UEFI terms involves setting up the PK, KEK, db, and dbx variables. Then every UEFI binary loaded can only be started via service StartImage() if it was signed with a certificate with a public key in db and not in dbx.
Verification of intrd and fdt is out of scope for the UEFI specification.
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Debian/Ubuntu, Fedora and Suse all use UEFI [-> shim] -> GRUB -> Linux if the firmware running the installer image supports UEFI.
FreeBSD and OpenBSD can be started as UEFI payloads but they typically do not use GRUB.
Here is a good starting point: https://wiki.ubuntu.com/UEFI/SecureBoot
Best regards
Heinrich _______________________________________________ boot-architecture mailing list boot-architecture@lists.linaro.org https://lists.linaro.org/mailman/listinfo/boot-architecture
Hi François,
On Mon, 28 Sep 2020 at 13:03, François Ozog francois.ozog@linaro.org wrote:
On Mon, 28 Sep 2020 at 20:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 28.09.20 18:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded' and distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
The Linux kernel has an EFI stub. This EFI stub consumes UEFI services and starts the main part of the kernel.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
Which app can be the Linux, Windows, BSD or if you do not want to start an operating system directly it can be a boot manager like GRUB, iPXE, rEFInd.
The strength of the UEFI spec is that it just provides an API and does not prescribe what you use it for. This gives much more choice than a firmware tied down to boot one specific OS only.
It is like with nuts and bolts. An M6 nut will fit onto any M6 bolt anywhere in the world. - But beware of 1/4" UNC parts. They only fit to their kin.
How about playing a game written for the EFI API: https://github.com/Openwide-Ingenierie/Pong-UEFI
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
This is indeed a priority and I was preparing the agenda for the next DTE
call and cover this exact topic. I am going to share my work in progress:
https://docs.google.com/presentation/d/1JK00su6e7vt8lRfwSt2C9EuuzwcBHLyoLRRr...
It is definitively not fully baked but you just triggered the need to share it as it is.
Please not that this effort is meant so that:
- we share a common understanding and vocabulary about the boot flows
(bootloader end up meaning not much) and what is part of the OS, the firmware, the Handover Function (is Grub to be considered here as a Linux artifact or a generic thing?)
- we share a common understanding of "validation domains" (chain of
<chains of trusts>)
- There is a slide to be filled during or after the discussion about
the reference boot flow(s) we need to consider (and probably a subset we will consider for the LEDGE Reference Platform).
Thanks for the slides. There are definitely a lot of different boot patches in use. I wonder if we can pull the verification earlier to avoid this complicating the verified boot?
- Simon
In Linux you can use the command efibootmgr to set up the boot variables if the UEFI firmware offers the SetVariable() runtime service. (U-Boot does not offer it at runtime) With the variables you can only specify a binary that is started and "load options". Linux uses these load options as command line. A Linux command line parameter (initrd=<path>) can be used to specify the initial ramdisk.
The UEFI boot manager (in U-Boot command 'bootefi bootmgr') uses the BootNext and BootOrder variables to identify which BootXXXX variable contains the specification of the binary to start.
The entry point of an UEFI binary (e.g. the Linux EFI stub) receives two parameters: the system table pointer, and the handle of the loaded image. The UEFI system table points to a list of configuration tables. These may include ACPI, SMBIOS, device-tree, and more. The Linux command line can be retrieved via the LoadOptions field of the loaded image protocol installed on the handle passed to the entry point.
It is the task of the firmware to set up a configuration table with the device tree or the ACPI table.
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Secure Boot in UEFI terms involves setting up the PK, KEK, db, and dbx variables. Then every UEFI binary loaded can only be started via service StartImage() if it was signed with a certificate with a public key in db and not in dbx.
Verification of intrd and fdt is out of scope for the UEFI specification.
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Debian/Ubuntu, Fedora and Suse all use UEFI [-> shim] -> GRUB -> Linux if the firmware running the installer image supports UEFI.
FreeBSD and OpenBSD can be started as UEFI payloads but they typically do not use GRUB.
Here is a good starting point: https://wiki.ubuntu.com/UEFI/SecureBoot
Best regards
Heinrich _______________________________________________ boot-architecture mailing list boot-architecture@lists.linaro.org https://lists.linaro.org/mailman/listinfo/boot-architecture
-- François-Frédéric Ozog | *Director Linaro Edge & Fog Computing Group* T: +33.67221.6485 francois.ozog@linaro.org | Skype: ffozog
Hi Heinrich,
On Mon, 28 Sep 2020 at 12:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 28.09.20 18:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded' and distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
The Linux kernel has an EFI stub. This EFI stub consumes UEFI services and starts the main part of the kernel.
What about the case where we don't want to use EFI?
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
Which app can be the Linux, Windows, BSD or if you do not want to start an operating system directly it can be a boot manager like GRUB, iPXE, rEFInd.
The strength of the UEFI spec is that it just provides an API and does not prescribe what you use it for. This gives much more choice than a firmware tied down to boot one specific OS only.
Exactly, but it is overly complex for many use cases.
It is like with nuts and bolts. An M6 nut will fit onto any M6 bolt anywhere in the world. - But beware of 1/4" UNC parts. They only fit to their kin.
How about playing a game written for the EFI API: https://github.com/Openwide-Ingenierie/Pong-UEFI
I think you've just proved my point.
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
In Linux you can use the command efibootmgr to set up the boot variables if the UEFI firmware offers the SetVariable() runtime service. (U-Boot does not offer it at runtime) With the variables you can only specify a binary that is started and "load options". Linux uses these load options as command line. A Linux command line parameter (initrd=<path>) can be used to specify the initial ramdisk.
The UEFI boot manager (in U-Boot command 'bootefi bootmgr') uses the BootNext and BootOrder variables to identify which BootXXXX variable contains the specification of the binary to start.
The entry point of an UEFI binary (e.g. the Linux EFI stub) receives two parameters: the system table pointer, and the handle of the loaded image. The UEFI system table points to a list of configuration tables. These may include ACPI, SMBIOS, device-tree, and more. The Linux command line can be retrieved via the LoadOptions field of the loaded image protocol installed on the handle passed to the entry point.
It is the task of the firmware to set up a configuration table with the device tree or the ACPI table.
Yes most of that matches what I understood, but I knew very little about the boot variables.
So I think we can have still have a reasonably simple boot if the boot variables and all required boot functionality are implemented in U-Boot (as apparently they are) and can be changed using efibootmgr under Linux.
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Secure Boot in UEFI terms involves setting up the PK, KEK, db, and dbx variables. Then every UEFI binary loaded can only be started via service StartImage() if it was signed with a certificate with a public key in db and not in dbx.
Verification of intrd and fdt is out of scope for the UEFI specification.
If these are in a FIT then the verification can be done in U-Boot.
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Debian/Ubuntu, Fedora and Suse all use UEFI [-> shim] -> GRUB -> Linux if the firmware running the installer image supports UEFI.
FreeBSD and OpenBSD can be started as UEFI payloads but they typically do not use GRUB.
Here is a good starting point: https://wiki.ubuntu.com/UEFI/SecureBoot
OK thank you. What are SHIM and MOK not incorporated into the boot loader?
Regards, Simon
Am 1. Oktober 2020 22:27:43 MESZ schrieb Simon Glass sjg@chromium.org:
Hi Heinrich,
On Mon, 28 Sep 2020 at 12:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 28.09.20 18:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded'
and
distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but
in
the general case the firmware provides them.
The Linux kernel has an EFI stub. This EFI stub consumes UEFI
services
and starts the main part of the kernel.
What about the case where we don't want to use EFI?
Nobody forbids you to forego UEFI. But the topic of EBBR is UEFI.
With UEFI you have a standardized way of booting which allows to boot alternative operating systems with alternative firmwares. This allows to replace OS and firmware independently.
There are use cases where you are tight on resources and will decide to skip this standard.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How
the
system actually boots is under control of the app.
Which app can be the Linux, Windows, BSD or if you do not want to
start
an operating system directly it can be a boot manager like GRUB,
iPXE,
rEFInd.
The strength of the UEFI spec is that it just provides an API and
does
not prescribe what you use it for. This gives much more choice than a firmware tied down to boot one specific OS only.
Exactly, but it is overly complex for many use cases.
It is like with nuts and bolts. An M6 nut will fit onto any M6 bolt anywhere in the world. - But beware of 1/4" UNC parts. They only fit
to
their kin.
How about playing a game written for the EFI API: https://github.com/Openwide-Ingenierie/Pong-UEFI
I think you've just proved my point.
I feel that a lot of the confusion about verified boot, DT
selections,
boot menus, etc. is coming from the introduction of an EFI app
which
has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof,
but
it is also arbitrarily complex, unpredictable and hard to secure.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different
way
(i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being
able
to change those variables from Linux userspace. Is that right?
In Linux you can use the command efibootmgr to set up the boot
variables
if the UEFI firmware offers the SetVariable() runtime service.
(U-Boot
does not offer it at runtime) With the variables you can only specify
a
binary that is started and "load options". Linux uses these load
options
as command line. A Linux command line parameter (initrd=<path>) can
be
used to specify the initial ramdisk.
The UEFI boot manager (in U-Boot command 'bootefi bootmgr') uses the BootNext and BootOrder variables to identify which BootXXXX variable contains the specification of the binary to start.
The entry point of an UEFI binary (e.g. the Linux EFI stub) receives
two
parameters: the system table pointer, and the handle of the loaded image. The UEFI system table points to a list of configuration
tables.
These may include ACPI, SMBIOS, device-tree, and more. The Linux
command
line can be retrieved via the LoadOptions field of the loaded image protocol installed on the handle passed to the entry point.
It is the task of the firmware to set up a configuration table with
the
device tree or the ACPI table.
Yes most of that matches what I understood, but I knew very little about the boot variables.
So I think we can have still have a reasonably simple boot if the boot variables and all required boot functionality are implemented in U-Boot (as apparently they are) and can be changed using efibootmgr under Linux.
efibootmgr can only be used if the firmware provides UEFI runtime services.
Currently you cannot use efibootmgr to set variables with U-Boot as U-Boot does not offer SetVariable() at runtime yet.
We are talking about having a 'secure' part of EBBR, which allows
for
secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI
variables?
Secure Boot in UEFI terms involves setting up the PK, KEK, db, and
dbx
variables. Then every UEFI binary loaded can only be started via
service
StartImage() if it was signed with a certificate with a public key in
db
and not in dbx.
Verification of intrd and fdt is out of scope for the UEFI
specification.
If these are in a FIT then the verification can be done in U-Boot.
If you ensure the integrity of your binaries without relying on the UEFI secure boot infrastructure, that is fine.
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Debian/Ubuntu, Fedora and Suse all use UEFI [-> shim] -> GRUB ->
Linux
if the firmware running the installer image supports UEFI.
FreeBSD and OpenBSD can be started as UEFI payloads but they
typically
do not use GRUB.
Here is a good starting point: https://wiki.ubuntu.com/UEFI/SecureBoot
OK thank you. What are SHIM and MOK not incorporated into the boot loader?
Shim was created to install Linux on devices that only have Microsoft certificates in the KEK.
If you control the PK and KEK UEFI variables, you don't need shim.
Best regards
Heinrich
Hi Heinrich,
On Thu, 1 Oct 2020 at 15:15, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
Am 1. Oktober 2020 22:27:43 MESZ schrieb Simon Glass sjg@chromium.org:
Hi Heinrich,
On Mon, 28 Sep 2020 at 12:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 28.09.20 18:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded'
and
distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but
in
the general case the firmware provides them.
The Linux kernel has an EFI stub. This EFI stub consumes UEFI
services
and starts the main part of the kernel.
What about the case where we don't want to use EFI?
Nobody forbids you to forego UEFI. But the topic of EBBR is UEFI.
With UEFI you have a standardized way of booting which allows to boot alternative operating systems with alternative firmwares. This allows to replace OS and firmware independently.
This is the bit that I just don't understand. When you talk about a standard way, I just don't see it. It seems that there are many ways to boot and none of it is standardised. It almost seems like UEFI is being used to avoid having to standardise things.
From the linux side, how do I determine what boots? From the firmware
side, how can I check that booting will succeed? From a testing point of view, how can I write a test of the end-to-end flow (well, firmware to Linux user space)?
There are use cases where you are tight on resources and will decide to skip this standard.
That seems like a huge missed opportunity. We just end up with n+1 ways of doing it.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How
the
system actually boots is under control of the app.
Which app can be the Linux, Windows, BSD or if you do not want to
start
an operating system directly it can be a boot manager like GRUB,
iPXE,
rEFInd.
The strength of the UEFI spec is that it just provides an API and
does
not prescribe what you use it for. This gives much more choice than a firmware tied down to boot one specific OS only.
Exactly, but it is overly complex for many use cases.
It is like with nuts and bolts. An M6 nut will fit onto any M6 bolt anywhere in the world. - But beware of 1/4" UNC parts. They only fit
to
their kin.
How about playing a game written for the EFI API: https://github.com/Openwide-Ingenierie/Pong-UEFI
I think you've just proved my point.
I feel that a lot of the confusion about verified boot, DT
selections,
boot menus, etc. is coming from the introduction of an EFI app
which
has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof,
but
it is also arbitrarily complex, unpredictable and hard to secure.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different
way
(i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being
able
to change those variables from Linux userspace. Is that right?
In Linux you can use the command efibootmgr to set up the boot
variables
if the UEFI firmware offers the SetVariable() runtime service.
(U-Boot
does not offer it at runtime) With the variables you can only specify
a
binary that is started and "load options". Linux uses these load
options
as command line. A Linux command line parameter (initrd=<path>) can
be
used to specify the initial ramdisk.
The UEFI boot manager (in U-Boot command 'bootefi bootmgr') uses the BootNext and BootOrder variables to identify which BootXXXX variable contains the specification of the binary to start.
The entry point of an UEFI binary (e.g. the Linux EFI stub) receives
two
parameters: the system table pointer, and the handle of the loaded image. The UEFI system table points to a list of configuration
tables.
These may include ACPI, SMBIOS, device-tree, and more. The Linux
command
line can be retrieved via the LoadOptions field of the loaded image protocol installed on the handle passed to the entry point.
It is the task of the firmware to set up a configuration table with
the
device tree or the ACPI table.
Yes most of that matches what I understood, but I knew very little about the boot variables.
So I think we can have still have a reasonably simple boot if the boot variables and all required boot functionality are implemented in U-Boot (as apparently they are) and can be changed using efibootmgr under Linux.
efibootmgr can only be used if the firmware provides UEFI runtime services.
Currently you cannot use efibootmgr to set variables with U-Boot as U-Boot does not offer SetVariable() at runtime yet.
I'm not even sure that is feasible/desirable. U-Boot may not be allowed to access the boot media if linux is also using it. How about we have a way for linux to write variable updates somewhere so that U-Boot picks them up on the next boot?
We are talking about having a 'secure' part of EBBR, which allows
for
secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI
variables?
Secure Boot in UEFI terms involves setting up the PK, KEK, db, and
dbx
variables. Then every UEFI binary loaded can only be started via
service
StartImage() if it was signed with a certificate with a public key in
db
and not in dbx.
Verification of intrd and fdt is out of scope for the UEFI
specification.
If these are in a FIT then the verification can be done in U-Boot.
If you ensure the integrity of your binaries without relying on the UEFI secure boot infrastructure, that is fine.
Perhaps we should avoid using the UEFI secure-boot stuff then? What benefit does it provide?
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Debian/Ubuntu, Fedora and Suse all use UEFI [-> shim] -> GRUB ->
Linux
if the firmware running the installer image supports UEFI.
FreeBSD and OpenBSD can be started as UEFI payloads but they
typically
do not use GRUB.
Here is a good starting point: https://wiki.ubuntu.com/UEFI/SecureBoot
OK thank you. What are SHIM and MOK not incorporated into the boot loader?
Shim was created to install Linux on devices that only have Microsoft certificates in the KEK.
If you control the PK and KEK UEFI variables, you don't need shim.
OK. So there is no way to add keys within the BIOS itself? Or is it just that people don't want the hassle?
Regards, Simon
On Wed, Oct 07, 2020 at 10:52:30AM -0600, Simon Glass wrote:
Hi Heinrich,
On Thu, 1 Oct 2020 at 15:15, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
Am 1. Oktober 2020 22:27:43 MESZ schrieb Simon Glass sjg@chromium.org:
Hi Heinrich,
On Mon, 28 Sep 2020 at 12:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 28.09.20 18:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded'
and
distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but
in
the general case the firmware provides them.
The Linux kernel has an EFI stub. This EFI stub consumes UEFI
services
and starts the main part of the kernel.
What about the case where we don't want to use EFI?
Nobody forbids you to forego UEFI. But the topic of EBBR is UEFI.
With UEFI you have a standardized way of booting which allows to boot alternative operating systems with alternative firmwares. This allows to replace OS and firmware independently.
This is the bit that I just don't understand. When you talk about a standard way, I just don't see it. It seems that there are many ways to boot and none of it is standardised. It almost seems like UEFI is being used to avoid having to standardise things.
I don't follow this (or perhaps I do follow it but don't agree with it).
UEFI is a standard. If is a flexible one although from the point of view of EBBR I think "scaleable" is a better term. It is the capacity to scale down to a very small feature set that allows it to be used in EBBR.
EBBR is mostly a set of constraints on a UEFI implement that reduces implementer flexibility (e.g. increasing standardization). We are willing to explicitly relax UEFI requirements but prefer to upstream that sort of thing to the UEFI spec (one of the recent changes to UEFI can, I think, be traced to that).
From the linux side, how do I determine what boots? From the firmware side, how can I check that booting will succeed?
That will depend to some extent on the use-case.
The introduction of secure boot will be a big improvement here. On systems where the OEM (or similar) controls a signing key then passing a signature check means that the payload has been tested and is known to be bootable.
From a testing point of view, how can I write a test of the end-to-end flow (well, firmware to Linux user space)?
The answer here is not very clear.
One of my team spent a little while looking at testing EBBR implementations using EFI applications and the scaleability was a problem since it becomes easy to have test suite that passes because almost all the tests get skipped due to the feature being disabled.
Like the above answer about verifying bootability I think we need to constrain what the system is for before we can see clearly how to test it.
There are use cases where you are tight on resources and will decide to skip this standard.
That seems like a huge missed opportunity. We just end up with n+1 ways of doing it.
Personally I'm more than a little sceptical that systems that tight on resources will actually exist in any market in which EBBR could ever be relevant.
Further I'm even more sceptical that if such systems will exist that they would have benefited from an alternative approach to standardization.
efibootmgr can only be used if the firmware provides UEFI runtime services.
Currently you cannot use efibootmgr to set variables with U-Boot as U-Boot does not offer SetVariable() at runtime yet.
I'm not even sure that is feasible/desirable. U-Boot may not be allowed to access the boot media if linux is also using it. How about we have a way for linux to write variable updates somewhere so that U-Boot picks them up on the next boot?
In principle this is can be achieved using UEFI by providing an app that uses EFI Boot Services to do update the variables.
However there is a chicken-and-egg problem for installers and similar (how to set the boot order to cause the app to run) and AFAIK there is no PoC implementation to play with at this point.
We are talking about having a 'secure' part of EBBR, which allows
for
secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI
variables?
Secure Boot in UEFI terms involves setting up the PK, KEK, db, and
dbx
variables. Then every UEFI binary loaded can only be started via
service
StartImage() if it was signed with a certificate with a public key in
db
and not in dbx.
Verification of intrd and fdt is out of scope for the UEFI
specification.
If these are in a FIT then the verification can be done in U-Boot.
If you ensure the integrity of your binaries without relying on the UEFI secure boot infrastructure, that is fine.
Perhaps we should avoid using the UEFI secure-boot stuff then? What benefit does it provide?
Primarily standardization...
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Debian/Ubuntu, Fedora and Suse all use UEFI [-> shim] -> GRUB ->
Linux
if the firmware running the installer image supports UEFI.
FreeBSD and OpenBSD can be started as UEFI payloads but they
typically
do not use GRUB.
Here is a good starting point: https://wiki.ubuntu.com/UEFI/SecureBoot
OK thank you. What are SHIM and MOK not incorporated into the boot loader?
Shim was created to install Linux on devices that only have Microsoft certificates in the KEK.
If you control the PK and KEK UEFI variables, you don't need shim.
OK. So there is no way to add keys within the BIOS itself? Or is it just that people don't want the hassle?
From an EBBR point of view I would expect it to be an implementation
decision. For some use cases key enrolment makes sense, for others not so much.
In the PC space, Microsoft required that vendors include a means for users to change the keys that are trusted as part of some certification programme. However AFAIK that rule does not apply to Arm even in the cases where that certification programme is relevant.
Daniel.
Hi Daniel,
On Thu, 8 Oct 2020 at 06:06, Daniel Thompson daniel.thompson@linaro.org wrote:
On Wed, Oct 07, 2020 at 10:52:30AM -0600, Simon Glass wrote:
Hi Heinrich,
On Thu, 1 Oct 2020 at 15:15, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
Am 1. Oktober 2020 22:27:43 MESZ schrieb Simon Glass sjg@chromium.org:
Hi Heinrich,
On Mon, 28 Sep 2020 at 12:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 28.09.20 18:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded'
and
distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but
in
the general case the firmware provides them.
The Linux kernel has an EFI stub. This EFI stub consumes UEFI
services
and starts the main part of the kernel.
What about the case where we don't want to use EFI?
Nobody forbids you to forego UEFI. But the topic of EBBR is UEFI.
With UEFI you have a standardized way of booting which allows to boot alternative operating systems with alternative firmwares. This allows to replace OS and firmware independently.
This is the bit that I just don't understand. When you talk about a standard way, I just don't see it. It seems that there are many ways to boot and none of it is standardised. It almost seems like UEFI is being used to avoid having to standardise things.
I don't follow this (or perhaps I do follow it but don't agree with it).
UEFI is a standard. If is a flexible one although from the point of view of EBBR I think "scaleable" is a better term. It is the capacity to scale down to a very small feature set that allows it to be used in EBBR.
EBBR is mostly a set of constraints on a UEFI implement that reduces implementer flexibility (e.g. increasing standardization). We are willing to explicitly relax UEFI requirements but prefer to upstream that sort of thing to the UEFI spec (one of the recent changes to UEFI can, I think, be traced to that).
From the linux side, how do I determine what boots? From the firmware side, how can I check that booting will succeed?
That will depend to some extent on the use-case.
The introduction of secure boot will be a big improvement here. On systems where the OEM (or similar) controls a signing key then passing a signature check means that the payload has been tested and is known to be bootable.
From a testing point of view, how can I write a test of the end-to-end flow (well, firmware to Linux user space)?
The answer here is not very clear.
One of my team spent a little while looking at testing EBBR implementations using EFI applications and the scaleability was a problem since it becomes easy to have test suite that passes because almost all the tests get skipped due to the feature being disabled.
Like the above answer about verifying bootability I think we need to constrain what the system is for before we can see clearly how to test it.
There are use cases where you are tight on resources and will decide to skip this standard.
That seems like a huge missed opportunity. We just end up with n+1 ways of doing it.
Personally I'm more than a little sceptical that systems that tight on resources will actually exist in any market in which EBBR could ever be relevant.
Further I'm even more sceptical that if such systems will exist that they would have benefited from an alternative approach to standardization.
efibootmgr can only be used if the firmware provides UEFI runtime services.
Currently you cannot use efibootmgr to set variables with U-Boot as U-Boot does not offer SetVariable() at runtime yet.
I'm not even sure that is feasible/desirable. U-Boot may not be allowed to access the boot media if linux is also using it. How about we have a way for linux to write variable updates somewhere so that U-Boot picks them up on the next boot?
In principle this is can be achieved using UEFI by providing an app that uses EFI Boot Services to do update the variables.
However there is a chicken-and-egg problem for installers and similar (how to set the boot order to cause the app to run) and AFAIK there is no PoC implementation to play with at this point.
We are talking about having a 'secure' part of EBBR, which allows
for
secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI
variables?
Secure Boot in UEFI terms involves setting up the PK, KEK, db, and
dbx
variables. Then every UEFI binary loaded can only be started via
service
StartImage() if it was signed with a certificate with a public key in
db
and not in dbx.
Verification of intrd and fdt is out of scope for the UEFI
specification.
If these are in a FIT then the verification can be done in U-Boot.
If you ensure the integrity of your binaries without relying on the UEFI secure boot infrastructure, that is fine.
Perhaps we should avoid using the UEFI secure-boot stuff then? What benefit does it provide?
Primarily standardization...
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
Debian/Ubuntu, Fedora and Suse all use UEFI [-> shim] -> GRUB ->
Linux
if the firmware running the installer image supports UEFI.
FreeBSD and OpenBSD can be started as UEFI payloads but they
typically
do not use GRUB.
Here is a good starting point: https://wiki.ubuntu.com/UEFI/SecureBoot
OK thank you. What are SHIM and MOK not incorporated into the boot loader?
Shim was created to install Linux on devices that only have Microsoft certificates in the KEK.
If you control the PK and KEK UEFI variables, you don't need shim.
OK. So there is no way to add keys within the BIOS itself? Or is it just that people don't want the hassle?
From an EBBR point of view I would expect it to be an implementation decision. For some use cases key enrolment makes sense, for others not so much.
In the PC space, Microsoft required that vendors include a means for users to change the keys that are trusted as part of some certification programme. However AFAIK that rule does not apply to Arm even in the cases where that certification programme is relevant.
So UEFI is a standard, in that it describes APIs. But so is the C library or ACPI, or C for that matter. I think there is another level missing here.
My point is not that systems are too constrained to use UEFI. It's more that there isn't a lot of point, for many systems. It seems to just add complexity, although it makes things simpler for Linux, I imagine. Some of the points made in this thread seem to be about removing arch-specific code from U-Boot and putting it in an EFI application, but to what end?
The valuable piece of a boot standard IMO would be to describe behaviour, how various features are configured (e.g. verified boot, adding kernels/devicetree/initrd, boot selection, key management, recovery) and how to test it.
Reading your responses, if I understand things correctly, EBBR is just a short doc that suggests using EFI protocols to boot Linux. It doesn't specify what any of the pieces should be, how they should interact, even less what their behaviour should be. I'd like to see a boot standard specify:
- what U-Boot (or some other bootloader) does and the environment it provides (EBBR seems to have some of this) - what any of the 'glue' pieces between U-Boot and linux are allowed to do, their inputs and outputs (this means grub, etc.) - how linux controls what boots, key management, etc.
Without this it is just 'please use EFI', so far as I can see. Is that the intent or is it just that it is in the early stages?
Regards, Simon
On Fri, Oct 09, 2020 at 11:12:35 -0600, Simon Glass wrote:
From an EBBR point of view I would expect it to be an implementation decision. For some use cases key enrolment makes sense, for others not so much.
In the PC space, Microsoft required that vendors include a means for users to change the keys that are trusted as part of some certification programme. However AFAIK that rule does not apply to Arm even in the cases where that certification programme is relevant.
So UEFI is a standard, in that it describes APIs. But so is the C library or ACPI, or C for that matter. I think there is another level missing here.
My point is not that systems are too constrained to use UEFI. It's more that there isn't a lot of point, for many systems. It seems to just add complexity, although it makes things simpler for Linux, I imagine. Some of the points made in this thread seem to be about removing arch-specific code from U-Boot and putting it in an EFI application, but to what end?
The valuable piece of a boot standard IMO would be to describe behaviour, how various features are configured (e.g. verified boot, adding kernels/devicetree/initrd, boot selection, key management, recovery) and how to test it.
Reading your responses, if I understand things correctly, EBBR is just a short doc that suggests using EFI protocols to boot Linux. It doesn't specify what any of the pieces should be, how they should interact, even less what their behaviour should be. I'd like to see a boot standard specify:
- what U-Boot (or some other bootloader) does and the environment it
provides (EBBR seems to have some of this)
- what any of the 'glue' pieces between U-Boot and linux are allowed
to do, their inputs and outputs (this means grub, etc.)
- how linux controls what boots, key management, etc.
Without this it is just 'please use EFI', so far as I can see. Is that the intent or is it just that it is in the early stages?
That is the intent. EBBR is actively operating system- and intended to be architecture agnostic, which adds some of the complexity you dislike.
If you are looking for a Linux-centric way of getting a fully-vertically-integrated OS up and running with a minimum of code, then that is arguably closer to the artist formerly known as LBBR: https://developer.arm.com/architectures/system-architectures/arm-systemready... although that probably carries other baggage you are also not interested in. And quite possibly does *not* add the bits you want.
I'm not saying what you're asking for wouldn't be useful, but that I don't think EBBR is the answer.
Regards,
Leif
Hi Leif,
On Mon, 12 Oct 2020 at 06:08, Leif Lindholm leif@nuviainc.com wrote:
On Fri, Oct 09, 2020 at 11:12:35 -0600, Simon Glass wrote:
From an EBBR point of view I would expect it to be an implementation decision. For some use cases key enrolment makes sense, for others not so much.
In the PC space, Microsoft required that vendors include a means for users to change the keys that are trusted as part of some certification programme. However AFAIK that rule does not apply to Arm even in the cases where that certification programme is relevant.
So UEFI is a standard, in that it describes APIs. But so is the C library or ACPI, or C for that matter. I think there is another level missing here.
My point is not that systems are too constrained to use UEFI. It's more that there isn't a lot of point, for many systems. It seems to just add complexity, although it makes things simpler for Linux, I imagine. Some of the points made in this thread seem to be about removing arch-specific code from U-Boot and putting it in an EFI application, but to what end?
The valuable piece of a boot standard IMO would be to describe behaviour, how various features are configured (e.g. verified boot, adding kernels/devicetree/initrd, boot selection, key management, recovery) and how to test it.
Reading your responses, if I understand things correctly, EBBR is just a short doc that suggests using EFI protocols to boot Linux. It doesn't specify what any of the pieces should be, how they should interact, even less what their behaviour should be. I'd like to see a boot standard specify:
- what U-Boot (or some other bootloader) does and the environment it
provides (EBBR seems to have some of this)
- what any of the 'glue' pieces between U-Boot and linux are allowed
to do, their inputs and outputs (this means grub, etc.)
- how linux controls what boots, key management, etc.
Without this it is just 'please use EFI', so far as I can see. Is that the intent or is it just that it is in the early stages?
That is the intent. EBBR is actively operating system- and intended to be architecture agnostic, which adds some of the complexity you dislike.
If you are looking for a Linux-centric way of getting a fully-vertically-integrated OS up and running with a minimum of code, then that is arguably closer to the artist formerly known as LBBR: https://developer.arm.com/architectures/system-architectures/arm-systemready... although that probably carries other baggage you are also not interested in. And quite possibly does *not* add the bits you want.
I'm not saying what you're asking for wouldn't be useful, but that I don't think EBBR is the answer.
But then it begs the question, what is EBBR for. I had assumed that we were trying to get devices to use it, if they don't want to use ACPI...but if it doesn't cover the needs, it's just an n+1 solution, as I said above.
It seems to me that we could standardise some of these things without too much trouble. By focussing on EFI (only) and not on how we can standardise all the boot flows, we might be missing the point.
Regards, Simon
Le jeu. 22 oct. 2020 à 22:16, Simon Glass sjg@chromium.org a écrit :
Hi Leif,
On Mon, 12 Oct 2020 at 06:08, Leif Lindholm leif@nuviainc.com wrote:
On Fri, Oct 09, 2020 at 11:12:35 -0600, Simon Glass wrote:
From an EBBR point of view I would expect it to be an implementation decision. For some use cases key enrolment makes sense, for others
not
so much.
In the PC space, Microsoft required that vendors include a means for users to change the keys that are trusted as part of some
certification
programme. However AFAIK that rule does not apply to Arm even in the cases where that certification programme is relevant.
So UEFI is a standard, in that it describes APIs. But so is the C library or ACPI, or C for that matter. I think there is another level missing here.
My point is not that systems are too constrained to use UEFI. It's more that there isn't a lot of point, for many systems. It seems to just add complexity, although it makes things simpler for Linux, I imagine. Some of the points made in this thread seem to be about removing arch-specific code from U-Boot and putting it in an EFI application, but to what end?
The valuable piece of a boot standard IMO would be to describe behaviour, how various features are configured (e.g. verified boot, adding kernels/devicetree/initrd, boot selection, key management, recovery) and how to test it.
Reading your responses, if I understand things correctly, EBBR is just a short doc that suggests using EFI protocols to boot Linux. It doesn't specify what any of the pieces should be, how they should interact, even less what their behaviour should be. I'd like to see a boot standard specify:
- what U-Boot (or some other bootloader) does and the environment it
provides (EBBR seems to have some of this)
- what any of the 'glue' pieces between U-Boot and linux are allowed
to do, their inputs and outputs (this means grub, etc.)
- how linux controls what boots, key management, etc.
Without this it is just 'please use EFI', so far as I can see. Is that the intent or is it just that it is in the early stages?
That is the intent. EBBR is actively operating system- and intended to be architecture agnostic, which adds some of the complexity you dislike.
If you are looking for a Linux-centric way of getting a fully-vertically-integrated OS up and running with a minimum of code, then that is arguably closer to the artist formerly known as LBBR:
https://developer.arm.com/architectures/system-architectures/arm-systemready...
although that probably carries other baggage you are also not interested in. And quite possibly does *not* add the bits you want.
I'm not saying what you're asking for wouldn't be useful, but that I don't think EBBR is the answer.
But then it begs the question, what is EBBR for. I had assumed that we were trying to get devices to use it, if they don't want to use ACPI...but if it doesn't cover the needs, it's just an n+1 solution, as I said above.
It seems to me that we could standardise some of these things without too much trouble. By focussing on EFI (only) and not on how we can standardise all the boot flows, we might be missing the point.
Let’s take concrete examples: chromeOS and Android are defining optimized and secured way of booting a single known OS. This is some form of walled garden and well defined. UEFI main goal is to provide an open way to boot any OSes, OSes that are unknown to UEFI. It can boot MacOS, windows, Linux, FreeBSD and even my hobby OS. EBBR is not about defining a unique boot flow that every one has to conform to. EBBR ambition is to allow any OS to be booted from an embedded board. Think of those vertical solutions: all at a sudden it increases the applicability of those solutions. It brings nothing to walled gardens environments but it does not prevent them to continue evolving their own path. In other words, it is not about standardizing everything into one boot flow, it is about opening the door to an ecosystem of OSes for boards, open source or not. Now, the walled gardens have found elegant solutions to some problems. I think one driving principle for EBBR is that we should leverage all those ideas so that we maximize code reuse.
Regards, Simon _______________________________________________ boot-architecture mailing list boot-architecture@lists.linaro.org https://lists.linaro.org/mailman/listinfo/boot-architecture
Francois already replied more elegantly, but I'll respond regardless.
On Thu, Oct 22, 2020 at 14:15:47 -0600, Simon Glass wrote:
If you are looking for a Linux-centric way of getting a fully-vertically-integrated OS up and running with a minimum of code, then that is arguably closer to the artist formerly known as LBBR: https://developer.arm.com/architectures/system-architectures/arm-systemready... although that probably carries other baggage you are also not interested in. And quite possibly does *not* add the bits you want.
I'm not saying what you're asking for wouldn't be useful, but that I don't think EBBR is the answer.
But then it begs the question, what is EBBR for.
It defines the interfaces to which any operating system can connect in order to boot, without having any prior knowledge of that platform. It's about general purpose computing and off-the shelf operating systems. It's not about standardising fully-vertically-integrated u-boot(or coreboot)+linux setups.
The de-facto reference implementation for U-Boot was initially written by Alex Graf to enable the standard UEFI SuSe installer (and post install, bootloader) to run unmodified on U-Boot platforms.
I had assumed that we were trying to get devices to use it, if they don't want to use ACPI...
I'm confused. ACPI can be used without UEFI. UEFI can be used without ACPI. EBBR is about defining a minimal subset of UEFI that is still useful to enable generic OS installers (or cloud images, or...).
Any codebase can implement that subset, and U-Boot now does. But a codebase that does not implement a subset of UEFI cannot be compliant with EBBR. Hence EBBR does not try to describe such things.
but if it doesn't cover the needs, it's just an n+1 solution, as I said above.
For the problem it is not trying to solve, absolutely. It is also not a solution for RCU or ending wars.
It seems to me that we could standardise some of these things without too much trouble. By focussing on EFI (only) and not on how we can standardise all the boot flows, we might be missing the point.
Which would nullify all the reasons for which the EBBR were actually created. There is no connection between what the EBBR is and what you are asking for.
There doesn't need to be any natural conflict between what that is and the EBBR, and it would certainly be useful if it could be made compatible with whatever specification you end up creating.
Best Regards,
Leif
Hi Leif and Francois,
On Thu, 22 Oct 2020 at 17:44, Leif Lindholm leif@nuviainc.com wrote:
Francois already replied more elegantly, but I'll respond regardless.
From Francois:
Let’s take concrete examples: chromeOS and Android are defining optimized and secured way of booting a single known OS. This is some form of walled garden and well defined. UEFI main goal is to provide an open way to boot any OSes, OSes that are unknown to UEFI. It can boot MacOS, windows, Linux, FreeBSD and even my hobby OS. EBBR is not about defining a unique boot flow that every one has to conform to. EBBR ambition is to allow any OS to be booted from an embedded board. Think of those vertical solutions: all at a sudden it increases the applicability of those solutions. It brings nothing to walled gardens environments but it does not prevent them to continue evolving their own path. In other words, it is not about standardizing everything into one boot flow, it is about opening the door to an ecosystem of OSes for boards, open source or not. Now, the walled gardens have found elegant solutions to some problems. I think one driving principle for EBBR is that we should leverage all those ideas so that we maximize code reuse.
Yes that is indeed a very concise and elegant explanation. My reading of this is we actually have the same goal to a large extent. But the key point here is 'not standardising...boot flow'. EBBR seems to be more about a sort-of plug-in approach with the EFI API as the common interface. So that seems to be the difference?
On Thu, Oct 22, 2020 at 14:15:47 -0600, Simon Glass wrote:
If you are looking for a Linux-centric way of getting a fully-vertically-integrated OS up and running with a minimum of code, then that is arguably closer to the artist formerly known as LBBR: https://developer.arm.com/architectures/system-architectures/arm-systemready... although that probably carries other baggage you are also not interested in. And quite possibly does *not* add the bits you want.
I'm not saying what you're asking for wouldn't be useful, but that I don't think EBBR is the answer.
But then it begs the question, what is EBBR for.
It defines the interfaces to which any operating system can connect in order to boot, without having any prior knowledge of that platform. It's about general purpose computing and off-the shelf operating systems. It's not about standardising fully-vertically-integrated u-boot(or coreboot)+linux setups.
The de-facto reference implementation for U-Boot was initially written by Alex Graf to enable the standard UEFI SuSe installer (and post install, bootloader) to run unmodified on U-Boot platforms.
I had assumed that we were trying to get devices to use it, if they don't want to use ACPI...
I'm confused. ACPI can be used without UEFI. UEFI can be used without ACPI. EBBR is about defining a minimal subset of UEFI that is still useful to enable generic OS installers (or cloud images, or...).
Any codebase can implement that subset, and U-Boot now does. But a codebase that does not implement a subset of UEFI cannot be compliant with EBBR. Hence EBBR does not try to describe such things.
but if it doesn't cover the needs, it's just an n+1 solution, as I said above.
For the problem it is not trying to solve, absolutely. It is also not a solution for RCU or ending wars.
It seems to me that we could standardise some of these things without too much trouble. By focussing on EFI (only) and not on how we can standardise all the boot flows, we might be missing the point.
Which would nullify all the reasons for which the EBBR were actually created. There is no connection between what the EBBR is and what you are asking for.
There doesn't need to be any natural conflict between what that is and the EBBR, and it would certainly be useful if it could be made compatible with whatever specification you end up creating.
(long pause while I contemplated the large disconnect here, finished off the Christmas cake and went back to work)
I suppose I am confused also. It doesn't help that I know very little about distributions generally and I can barely use the grub command line.
I expect from the name EBBR - Embedded Base Boot Requirements, that these are the base requirements for embedded booting. I had assumed that Chrome OS, for example, would eventually conform to this. In my mind, I should be able to boot different operating systems on my computer, including Chrome OS / Chromium OS.
But reading this thread again suggests that EBBR is a standard that requires using EFI protocol to couple firmware with the OS in a defined way. It does not seem very 'base' to me. Nor does it standardise things like verified boot, selecting A/B kernels, setting kernel parameters, the kernel upgrade mechanism, etc. These are presumably all left to distributions to deal with individually. I look at multiboot and actually see more definition of the boot mechanism. Perhaps EBBR would be better described as a distribution-boot convenience.
I am not intending to criticise EBBR since even what you describe sounds very valuable. Also while certain things about EFI are not to my taste (for example, in no particular order, the code style, GUIDs and its propensity to encourage closed-source firmware), I did in fact write the first EFI implementation in U-Boot in 2015 (booting U-Boot from UEFI), and spent much time reviewing and testing Alex's code a year later, as well as encouraging the inclusion of tests, which we now have thanks to Heinrich.
In any case I am not suggesting creating a new specification, but perhaps we could at least figure out what such a specification might solve. I would like to see:
- a dumb 'instruction file' of some sort for each OS on the system, a bit like the grub menu but not Turing-complete :-) - some non-volatile context to describe the user's preferences and system settings
and from those, be able to deterministically define the boot behaviour, with a set of unit tests to check it. It may be that this would be an anathema to distributions, since they would need to collaborate on a shared implementation of it. Perhaps it would also be pointless for distributions since they already have grub and UEFI on x86 and everything on ARM should just follow that path.
Or maybe not? I'm certainly interested in understanding all this better and it's the main reason I try to follow along with this.
Regards, Simon
Hi Simon,
Heinrich provided some great answers on the technical details. I'll try not to retrace his answers, but there are a few conceptual model bits that I think are worth talking about...
On 28/09/2020 17:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded' and distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
I'll assume for both of the cases you describe below that the UEFI boot path (bootefi) is used instead of the traditional bootm.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
As far as bootefi is concerned, case 1 & case 2 are identical. The image to be run is loaded into memory and bootefi executes the payload. In case 1 the payload in Linux's built-in UEFI stub. In case 2 the payload is grub.efi, shell.efi, shim.efi, etc.
bootefi differs from bootm in that instead of a bare vmlinuz image or a FIT image, the loaded image is a PECOFF executable. U=Boot parses the PECOFF headers, loads the sections into memory and jumps to the entry point. It also leaves a pointer to an in-memory information table with callbacks into firmware functions for accessing basic services (console, block device, network, graphics, filesystem, etc).
If the image is Linux, then the stub will do a small amount of setup before calling ExitBootServices(), which tells firmware to stop managing hardware because the kernel is taking over. (In UEFI terms, this is call an "OS loader" image)
If the image is a transient binary like Grub or the UEFI shell, it can use firmware facilities to load additional files (e.g., initrd, kernel, etc) before jumping into another PE/COFF binary. For example, Grub will typically load the kernel into memory, which itself is a PE/COFF image, and then jump to its entry point.
If an image doesn't call ExitBootServices(), and instead exits, control returns to firmware as one would expect.
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
Of the items you've listed above, DT selection does indeed need work, but the rest is quite well specified. The UEFI spec has very clear specifications on how the boot image is selected via the BOOTxxxx variables and the format of the binaries. Secure Boot is also standardized so that when Secure Boot is turned on, firmware will only execute images signed by a recognized key.
It is no more arbitrarily complex than booting an OS kernel. When compared against bootm, both boot flows load an image into memory, and both jump to the image starting point. In both cases the OS can do whatever it wants after the firmware jumps into it, and in both cases if security is enabled then the binaries must be signed.
Where UEFI differs is that it continues to offer {console,net,block,fs} services for as long as the image chooses to use them so that early boot code doesn't need to carry its own implementations.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
A great way to get familiar with it is to play around with the UEFI code already in U-Boot with the UEFI Self Certification test suite. Building the SCT is straight forward, and there are some instructions for doing so here:
https://github.com/glikely/edk2-test-manifest
If you enable UEFI in U-Boot and copy the SCT to a USB drive, u-boot should be able to find and run the UEFI shell from the USB drive.
There is also good documentation on the U-Boot implementation:
https://github.com/u-boot/u-boot/blob/master/doc/uefi/uefi.rst
Cheers, g.
Hi Grant,
[who is 'nd'?]
On Wed, 30 Sep 2020 at 09:26, Grant Likely grant.likely@arm.com wrote:
Hi Simon,
Heinrich provided some great answers on the technical details. I'll try not to retrace his answers, but there are a few conceptual model bits that I think are worth talking about...
On 28/09/2020 17:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded' and distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
I'll assume for both of the cases you describe below that the UEFI boot path (bootefi) is used instead of the traditional bootm.
Well in fact it is either bootm (ARM + x86) or zboot (x86) depending on what we are talking about.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
As far as bootefi is concerned, case 1 & case 2 are identical. The image to be run is loaded into memory and bootefi executes the payload. In case 1 the payload in Linux's built-in UEFI stub. In case 2 the payload is grub.efi, shell.efi, shim.efi, etc.
bootefi differs from bootm in that instead of a bare vmlinuz image or a FIT image, the loaded image is a PECOFF executable. U=Boot parses the PECOFF headers, loads the sections into memory and jumps to the entry point. It also leaves a pointer to an in-memory information table with callbacks into firmware functions for accessing basic services (console, block device, network, graphics, filesystem, etc).
If the image is Linux, then the stub will do a small amount of setup before calling ExitBootServices(), which tells firmware to stop managing hardware because the kernel is taking over. (In UEFI terms, this is call an "OS loader" image)
If the image is a transient binary like Grub or the UEFI shell, it can use firmware facilities to load additional files (e.g., initrd, kernel, etc) before jumping into another PE/COFF binary. For example, Grub will typically load the kernel into memory, which itself is a PE/COFF image, and then jump to its entry point.
Yes. I think this is the source of the pain, since there is really no limit on what these can do and there is no standard flow. Much of the loading happens outside the control of the bootloader, which means that verified boot and testing are harder.
If an image doesn't call ExitBootServices(), and instead exits, control returns to firmware as one would expect.
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
Of the items you've listed above, DT selection does indeed need work, but the rest is quite well specified. The UEFI spec has very clear specifications on how the boot image is selected via the BOOTxxxx variables and the format of the binaries. Secure Boot is also standardized so that when Secure Boot is turned on, firmware will only execute images signed by a recognized key.
It is no more arbitrarily complex than booting an OS kernel. When compared against bootm, both boot flows load an image into memory, and both jump to the image starting point. In both cases the OS can do whatever it wants after the firmware jumps into it, and in both cases if security is enabled then the binaries must be signed.
I think you are missing my point. With bootm etc. it jumps straight from U-Boot to linux. Of course linux may have an EFI shim, but there is no grub or any other loader 'in the way'.
Where UEFI differs is that it continues to offer {console,net,block,fs} services for as long as the image chooses to use them so that early boot code doesn't need to carry its own implementations.
Yes. Certainly having a standard way to output console text is useful early in linux for debugging. I'm not too sure about the others though, in the simple case.
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
A great way to get familiar with it is to play around with the UEFI code already in U-Boot with the UEFI Self Certification test suite. Building the SCT is straight forward, and there are some instructions for doing so here:
https://github.com/glikely/edk2-test-manifest
If you enable UEFI in U-Boot and copy the SCT to a USB drive, u-boot should be able to find and run the UEFI shell from the USB drive.
There is also good documentation on the U-Boot implementation:
https://github.com/u-boot/u-boot/blob/master/doc/uefi/uefi.rst
I have actually reviewed a lot of the code (and remember 5 years ago I worked on the original lib/efi in U-Boot - see doc/uefi/u-boot_on_efi.rst). But it is just hard to get my head around all the pieces. It is the complexity of the bit between U-Boot and Linux that I find challenging.
Thank you both for the pointers.
I think in general we are talking at cross purposes. I am looking for how we can specify a simple verified boot, with or without EFI, so that we actually create something that covers everything. Distributions and particular cases may go to endless depths of complexity, but if we want to incorporate the embedded case, then in my view we need a simple option, with no more pieces, code, boot time and complexity than is needed. It seems like the EFI protocol allows this, so I imagine:
Simple EFI boot:
1. U-Boot starts*, selects and verifies config using UEFI variables, loads image (linux, inittd, FPGA, DTs, etc.) verifying as it goes, boots linux, passing in the images and providing EFI services as specified by EBBR. OS updates use efibootmgr to select new kernels, images, etc., as (to be) specified by EBBR
2. (degenerate case, where EFI is not used) U-Boot starts, selects and verifies config using a proprietary mechanism, loads image (linux, inittd, FPGA, DTs, etc.) verifying as it goes, boots linux, passing in the images. Updates use a proprietary mechanism not specified by EBBR
Note that for platforms with separate firmware storage (e.g. SPI flash), if the disk is wiped, or fails to to verify before or after linux boots, then a recovery mode is needed to obtain a new disk image. The firmware needs to be able to load, verify and write an image to the disk so that the machine can continue to operate. Storing firmware on the disk is not really viable. For other platforms, presumably the disk is removable so the user can reflash it.
Complex EFI boot:
3. U-Boot (or anything else) starts, loads an EFI app (maybe grub or something else) and provides EFI services as specified by EBBR. What happens from there is distro-specific and not specified by EBBR. Updates are handled by any means necessary and not specified by EBBR.
Regards, Simon
* Of course there is ATF, SPL and other things which complicate this piece. I think EBBR should specify these too.
On Thu, 1 Oct 2020 at 22:30, Simon Glass sjg@chromium.org wrote:
Hi Grant,
[who is 'nd'?]
That would be our bot who kindly omits of the obnoxious email footer on outgoing email if cc'ed.
On Wed, 30 Sep 2020 at 09:26, Grant Likely grant.likely@arm.com wrote:
Hi Simon,
Heinrich provided some great answers on the technical details. I'll try not to retrace his answers, but there are a few conceptual model bits that I think are worth talking about...
On 28/09/2020 17:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded' and distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
I'll assume for both of the cases you describe below that the UEFI boot path (bootefi) is used instead of the traditional bootm.
Well in fact it is either bootm (ARM + x86) or zboot (x86) depending on what we are talking about.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
As far as bootefi is concerned, case 1 & case 2 are identical. The image to be run is loaded into memory and bootefi executes the payload. In case 1 the payload in Linux's built-in UEFI stub. In case 2 the payload is grub.efi, shell.efi, shim.efi, etc.
bootefi differs from bootm in that instead of a bare vmlinuz image or a FIT image, the loaded image is a PECOFF executable. U=Boot parses the PECOFF headers, loads the sections into memory and jumps to the entry point. It also leaves a pointer to an in-memory information table with callbacks into firmware functions for accessing basic services (console, block device, network, graphics, filesystem, etc).
If the image is Linux, then the stub will do a small amount of setup before calling ExitBootServices(), which tells firmware to stop managing hardware because the kernel is taking over. (In UEFI terms, this is call an "OS loader" image)
If the image is a transient binary like Grub or the UEFI shell, it can use firmware facilities to load additional files (e.g., initrd, kernel, etc) before jumping into another PE/COFF binary. For example, Grub will typically load the kernel into memory, which itself is a PE/COFF image, and then jump to its entry point.
Yes. I think this is the source of the pain, since there is really no limit on what these can do and there is no standard flow. Much of the loading happens outside the control of the bootloader, which means that verified boot and testing are harder.
If an image doesn't call ExitBootServices(), and instead exits, control returns to firmware as one would expect.
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
Of the items you've listed above, DT selection does indeed need work, but the rest is quite well specified. The UEFI spec has very clear specifications on how the boot image is selected via the BOOTxxxx variables and the format of the binaries. Secure Boot is also standardized so that when Secure Boot is turned on, firmware will only execute images signed by a recognized key.
It is no more arbitrarily complex than booting an OS kernel. When compared against bootm, both boot flows load an image into memory, and both jump to the image starting point. In both cases the OS can do whatever it wants after the firmware jumps into it, and in both cases if security is enabled then the binaries must be signed.
I think you are missing my point. With bootm etc. it jumps straight from U-Boot to linux. Of course linux may have an EFI shim, but there is no grub or any other loader 'in the way'.
Where UEFI differs is that it continues to offer {console,net,block,fs} services for as long as the image chooses to use them so that early boot code doesn't need to carry its own implementations.
Yes. Certainly having a standard way to output console text is useful early in linux for debugging. I'm not too sure about the others though, in the simple case.
Another advantage of EFI boot is that it allows us to avoid putting Linux specific knowledge into the bootloader, which is typically different for every architecture: - On x86, the location of the initrd and the commandline are passed via the boot_params struct, and where these may be loaded in memory is different between i686 and x86_64, differs between Linux versions, and is also dependent on whether the kernel is KASLR capable or not. Where the boot_params struct itself may be loaded in memory also varies. - on ARM and arm64 the location of the initrd and the contents of the command line are passed via DT - where the initrd may be loaded is arch specific, and the DT needs to be in lowmem (but the size of lowmem depends on your kernel config) - on ARM, the zImage must be placed within 128 MB of the start of DRAM - on arm64, the kernel Image must be aligned to 2 MB in memory, unless it is a KASLR kernel, in which case 64 KB is sufficient - KASLR seeds are passed in different ways - RISC-V yay! - etc etc
We have managed to move all this complex policy into the EFI stub for Linux, which only needs the EFI APIs, which are rigorously specified and documented, and vary very little between architectures. We have defined an arch-agnostic initrd EFI protocol on the Linux side, which the bootloader can expose (and which is already implemented by uboot), and provides a natural hook for attestation/measurement.
As a result, we have defined a generic EFI based platform, where secure boot and measured boot, and perhaps even firmware update can be implemented according to an industry spec, and the resulting code should deviate very little (if at all) between all these architectures.
Am 1. Oktober 2020 23:19:08 MESZ schrieb Ard Biesheuvel ardb@kernel.org:
On Thu, 1 Oct 2020 at 22:30, Simon Glass sjg@chromium.org wrote:
Hi Grant,
[who is 'nd'?]
That would be our bot who kindly omits of the obnoxious email footer on outgoing email if cc'ed.
On Wed, 30 Sep 2020 at 09:26, Grant Likely grant.likely@arm.com
wrote:
Hi Simon,
Heinrich provided some great answers on the technical details. I'll
try
not to retrace his answers, but there are a few conceptual model
bits
that I think are worth talking about...
On 28/09/2020 17:51, Simon Glass wrote:
Hi,
I thought perhaps it might be worth starting a thread on this, as despite Grant and Heinrich kinding spending a bit of time talking about this, I am still very much in the dark about how 'embedded'
and
distro/other boot flows are going to come together with EBBR. Of course this would be easier f2f.
I'll assume for both of the cases you describe below that the UEFI
boot
path (bootefi) is used instead of the traditional bootm.
Well in fact it is either bootm (ARM + x86) or zboot (x86) depending on what we are talking about.
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not,
but in
the general case the firmware provides them.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How
the
system actually boots is under control of the app.
As far as bootefi is concerned, case 1 & case 2 are identical. The
image
to be run is loaded into memory and bootefi executes the payload.
In
case 1 the payload in Linux's built-in UEFI stub. In case 2 the
payload
is grub.efi, shell.efi, shim.efi, etc.
bootefi differs from bootm in that instead of a bare vmlinuz image
or a
FIT image, the loaded image is a PECOFF executable. U=Boot parses
the
PECOFF headers, loads the sections into memory and jumps to the
entry
point. It also leaves a pointer to an in-memory information table
with
callbacks into firmware functions for accessing basic services
(console,
block device, network, graphics, filesystem, etc).
If the image is Linux, then the stub will do a small amount of
setup
before calling ExitBootServices(), which tells firmware to stop
managing
hardware because the kernel is taking over. (In UEFI terms, this is
call
an "OS loader" image)
If the image is a transient binary like Grub or the UEFI shell, it
can
use firmware facilities to load additional files (e.g., initrd,
kernel,
etc) before jumping into another PE/COFF binary. For example, Grub
will
typically load the kernel into memory, which itself is a PE/COFF
image,
and then jump to its entry point.
Yes. I think this is the source of the pain, since there is really no limit on what these can do and there is no standard flow. Much of the loading happens outside the control of the bootloader, which means that verified boot and testing are harder.
If an image doesn't call ExitBootServices(), and instead exits,
control
returns to firmware as one would expect.
I feel that a lot of the confusion about verified boot, DT
selections,
boot menus, etc. is coming from the introduction of an EFI app
which
has no specification (it can be grub, shim or something else, as
I
understand it). Certainly this is very flexible and future-proof,
but
it is also arbitrarily complex, unpredictable and hard to secure.
Of the items you've listed above, DT selection does indeed need
work,
but the rest is quite well specified. The UEFI spec has very clear specifications on how the boot image is selected via the BOOTxxxx variables and the format of the binaries. Secure Boot is also standardized so that when Secure Boot is turned on, firmware will
only
execute images signed by a recognized key.
It is no more arbitrarily complex than booting an OS kernel. When compared against bootm, both boot flows load an image into memory,
and
both jump to the image starting point. In both cases the OS can do whatever it wants after the firmware jumps into it, and in both
cases if
security is enabled then the binaries must be signed.
I think you are missing my point. With bootm etc. it jumps straight from U-Boot to linux. Of course linux may have an EFI shim, but there is no grub or any other loader 'in the way'.
Where UEFI differs is that it continues to offer
{console,net,block,fs}
services for as long as the image chooses to use them so that early
boot
code doesn't need to carry its own implementations.
Yes. Certainly having a standard way to output console text is useful early in linux for debugging. I'm not too sure about the others though, in the simple case.
Another advantage of EFI boot is that it allows us to avoid putting Linux specific knowledge into the bootloader, which is typically different for every architecture:
- On x86, the location of the initrd and the commandline are passed
via the boot_params struct, and where these may be loaded in memory is different between i686 and x86_64, differs between Linux versions, and is also dependent on whether the kernel is KASLR capable or not. Where the boot_params struct itself may be loaded in memory also varies.
- on ARM and arm64 the location of the initrd and the contents of the
command line are passed via DT - where the initrd may be loaded is arch specific, and the DT needs to be in lowmem (but the size of lowmem depends on your kernel config)
- on ARM, the zImage must be placed within 128 MB of the start of DRAM
- on arm64, the kernel Image must be aligned to 2 MB in memory, unless
it is a KASLR kernel, in which case 64 KB is sufficient
- KASLR seeds are passed in different ways
- RISC-V yay!
- etc etc
We have managed to move all this complex policy into the EFI stub for Linux, which only needs the EFI APIs, which are rigorously specified and documented, and vary very little between architectures. We have defined an arch-agnostic initrd EFI protocol on the Linux side, which the bootloader can expose (and which is already implemented by uboot), and provides a natural hook for attestation/measurement.
As a result, we have defined a generic EFI based platform, where secure boot and measured boot, and perhaps even firmware update can be implemented according to an industry spec, and the resulting code should deviate very little (if at all) between all these architectures.
The architecture specific quirks that I am aware of are:
* State of caches and MMU * Boot hart ID in device tree for RISC-V * Exception level (EL2, S-mode) * Restrictions on the memory map * Calling conventions * PE-COFF file format depending on 32/64 bit
Best regards
Heinrich
On 01/10/2020 21:29, Simon Glass wrote:
Hi Grant,
[who is 'nd'?]
As Ard says, magic bot that suppresses the disclaimer on outgoing Arm email. Others have figured out how to send email without it, but I've not got mine sorted yet.
On Wed, 30 Sep 2020 at 09:26, Grant Likely grant.likely@arm.com wrote:
On 28/09/2020 17:51, Simon Glass wrote:
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
As far as bootefi is concerned, case 1 & case 2 are identical. The image to be run is loaded into memory and bootefi executes the payload. In case 1 the payload in Linux's built-in UEFI stub. In case 2 the payload is grub.efi, shell.efi, shim.efi, etc.
bootefi differs from bootm in that instead of a bare vmlinuz image or a FIT image, the loaded image is a PECOFF executable. U=Boot parses the PECOFF headers, loads the sections into memory and jumps to the entry point. It also leaves a pointer to an in-memory information table with callbacks into firmware functions for accessing basic services (console, block device, network, graphics, filesystem, etc).
If the image is Linux, then the stub will do a small amount of setup before calling ExitBootServices(), which tells firmware to stop managing hardware because the kernel is taking over. (In UEFI terms, this is call an "OS loader" image)
If the image is a transient binary like Grub or the UEFI shell, it can use firmware facilities to load additional files (e.g., initrd, kernel, etc) before jumping into another PE/COFF binary. For example, Grub will typically load the kernel into memory, which itself is a PE/COFF image, and then jump to its entry point.
Yes. I think this is the source of the pain, since there is really no limit on what these can do and there is no standard flow. Much of the loading happens outside the control of the bootloader, which means that verified boot and testing are harder.
If an image doesn't call ExitBootServices(), and instead exits, control returns to firmware as one would expect.
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
Of the items you've listed above, DT selection does indeed need work, but the rest is quite well specified. The UEFI spec has very clear specifications on how the boot image is selected via the BOOTxxxx variables and the format of the binaries. Secure Boot is also standardized so that when Secure Boot is turned on, firmware will only execute images signed by a recognized key.
It is no more arbitrarily complex than booting an OS kernel. When compared against bootm, both boot flows load an image into memory, and both jump to the image starting point. In both cases the OS can do whatever it wants after the firmware jumps into it, and in both cases if security is enabled then the binaries must be signed.
I think you are missing my point. With bootm etc. it jumps straight from U-Boot to linux. Of course linux may have an EFI shim, but there is no grub or any other loader 'in the way'.
I fully understand your point, I just disagree with it. :-)
In both case U-Boot sets up an environment for "something-other" to run. In the case of a raw linux kernel image*, it sets up the environments as specified by Linux in - Documentation/arm/booting.rst - Documentation/arm64/booting.rst - etc.
In the case of UEFI PE/COFF, if sets up the environment as described in the UEFI spec, section 2; particularly 2.3 which describes the calling convention.
These are not fundamentally different, other than the Linux environment is specific to Linux, but the UEFI one is standard across several OSes and tools.
To look at the simplest scenario, UEFI also defines a standard way to choose what to boot next. It uses the BOOTxxxx and BOOTORDER variables to store what to boot, and which one to try next. If BOOTxxxx points at the kernel, then U-Boot goes straight to Linux.
[...]
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
A great way to get familiar with it is to play around with the UEFI code already in U-Boot with the UEFI Self Certification test suite. Building the SCT is straight forward, and there are some instructions for doing so here:
https://github.com/glikely/edk2-test-manifest
If you enable UEFI in U-Boot and copy the SCT to a USB drive, u-boot should be able to find and run the UEFI shell from the USB drive.
There is also good documentation on the U-Boot implementation:
https://github.com/u-boot/u-boot/blob/master/doc/uefi/uefi.rst
I have actually reviewed a lot of the code (and remember 5 years ago I worked on the original lib/efi in U-Boot - see doc/uefi/u-boot_on_efi.rst). But it is just hard to get my head around all the pieces. It is the complexity of the bit between U-Boot and Linux that I find challenging.
It doesn't help that u-boot-on-uefi is completely different from UEFI-in-U-Boot! The first is running U-Boot as a UEFI application on top of some other UEFI implementation (e.g. the stock BIOS on a PC)... which ironically is conceptually similar to running Grub!
The second is U-Boot exposing the UEFI ABI so that it can run UEFI applications itself....
The natural extension of this is of course to start nesting multiple copies of U-Boot, each both running on, and providing the ABI of, UEFI! :-p
Thank you both for the pointers.
I think in general we are talking at cross purposes. I am looking for how we can specify a simple verified boot, with or without EFI, so that we actually create something that covers everything. Distributions and particular cases may go to endless depths of complexity, but if we want to incorporate the embedded case, then in my view we need a simple option, with no more pieces, code, boot time and complexity than is needed. It seems like the EFI protocol allows this, so I imagine:
Simple EFI boot:
- U-Boot starts*, selects and verifies config using UEFI variables,
loads image (linux, inittd, FPGA, DTs, etc.) verifying as it goes, boots linux, passing in the images and providing EFI services as specified by EBBR. OS updates use efibootmgr to select new kernels, images, etc., as (to be) specified by EBBR
- (degenerate case, where EFI is not used) U-Boot starts, selects and
verifies config using a proprietary mechanism, loads image (linux, inittd, FPGA, DTs, etc.) verifying as it goes, boots linux, passing in the images. Updates use a proprietary mechanism not specified by EBBR
Note that for platforms with separate firmware storage (e.g. SPI flash), if the disk is wiped, or fails to to verify before or after linux boots, then a recovery mode is needed to obtain a new disk image. The firmware needs to be able to load, verify and write an image to the disk so that the machine can continue to operate. Storing firmware on the disk is not really viable. For other platforms, presumably the disk is removable so the user can reflash it.
Complex EFI boot:
- U-Boot (or anything else) starts, loads an EFI app (maybe grub or
something else) and provides EFI services as specified by EBBR. What happens from there is distro-specific and not specified by EBBR. Updates are handled by any means necessary and not specified by EBBR.
In the cases above, I think only cases 1 & 3 are relevant for EBBR. Case 2 is out of scope because EBBR is specifically about standardizing on the UEFI ABI, so it isn't going to cover any non-UEFI boot path. Alternate boot paths are certainly not forbidden (e.g. bootm and bootefi happily coexist), but without a strong cross-platform incentive, there isn't any reason to standarize this path.
Case 3 is already covered by the spec, but Case 1 requires a bit more discussion.
In case 1, UEFI already has all the functionality needed to directly load the OS loader (kernel) and provide a DT. The kernel can also itself load from disk both the initrd and dtb using command line options to the kernel. Loading FPGA bitstreams or peripheral firmware is not covered by the core UEFI ABI.
Loading of the initrd and dtb using kernel command line flags is not well liked among the kernel UEFI devs. It doesn't provide a way to validate the image, and it is preferred to load both prior to calling the kernel.
Assuming the FIT image model of shipping an OS image where the kernel, initrd & dtb are all packaged together, as well as ancillary firmware blobs and FPGA bitstreams, it would be valuable to define how that works in the UEFI boot flow. Need some discussion on what that might look like, and what the split of responsibility needs to be between the FIT contents and U-Boot proper.
- Of course there is ATF, SPL and other things which complicate this
piece. I think EBBR should specify these too.
Absolutely the layers of trusted firmware and TEEs are in scope.
Cheers, g.
On 02.10.20 14:19, Grant Likely wrote:
On 01/10/2020 21:29, Simon Glass wrote:
Hi Grant,
[who is 'nd'?]
As Ard says, magic bot that suppresses the disclaimer on outgoing Arm email. Others have figured out how to send email without it, but I've not got mine sorted yet.
On Wed, 30 Sep 2020 at 09:26, Grant Likely grant.likely@arm.com wrote:
On 28/09/2020 17:51, Simon Glass wrote:
Case 1: Firmware loads the kernel to a particular address, selects DT and boots it. The kernel may require EFI boot services, or may not, but in the general case the firmware provides them.
Case 2: Firmware loads EFI app and provides EFI boot services to it. How the system actually boots is under control of the app.
As far as bootefi is concerned, case 1 & case 2 are identical. The image to be run is loaded into memory and bootefi executes the payload. In case 1 the payload in Linux's built-in UEFI stub. In case 2 the payload is grub.efi, shell.efi, shim.efi, etc.
bootefi differs from bootm in that instead of a bare vmlinuz image or a FIT image, the loaded image is a PECOFF executable. U=Boot parses the PECOFF headers, loads the sections into memory and jumps to the entry point. It also leaves a pointer to an in-memory information table with callbacks into firmware functions for accessing basic services (console, block device, network, graphics, filesystem, etc).
If the image is Linux, then the stub will do a small amount of setup before calling ExitBootServices(), which tells firmware to stop managing hardware because the kernel is taking over. (In UEFI terms, this is call an "OS loader" image)
If the image is a transient binary like Grub or the UEFI shell, it can use firmware facilities to load additional files (e.g., initrd, kernel, etc) before jumping into another PE/COFF binary. For example, Grub will typically load the kernel into memory, which itself is a PE/COFF image, and then jump to its entry point.
Yes. I think this is the source of the pain, since there is really no limit on what these can do and there is no standard flow. Much of the loading happens outside the control of the bootloader, which means that verified boot and testing are harder.
If an image doesn't call ExitBootServices(), and instead exits, control returns to firmware as one would expect.
I feel that a lot of the confusion about verified boot, DT selections, boot menus, etc. is coming from the introduction of an EFI app which has no specification (it can be grub, shim or something else, as I understand it). Certainly this is very flexible and future-proof, but it is also arbitrarily complex, unpredictable and hard to secure.
Of the items you've listed above, DT selection does indeed need work, but the rest is quite well specified. The UEFI spec has very clear specifications on how the boot image is selected via the BOOTxxxx variables and the format of the binaries. Secure Boot is also standardized so that when Secure Boot is turned on, firmware will only execute images signed by a recognized key.
It is no more arbitrarily complex than booting an OS kernel. When compared against bootm, both boot flows load an image into memory, and both jump to the image starting point. In both cases the OS can do whatever it wants after the firmware jumps into it, and in both cases if security is enabled then the binaries must be signed.
I think you are missing my point. With bootm etc. it jumps straight from U-Boot to linux. Of course linux may have an EFI shim, but there is no grub or any other loader 'in the way'.
I fully understand your point, I just disagree with it. :-)
In both case U-Boot sets up an environment for "something-other" to run. In the case of a raw linux kernel image*, it sets up the environments as specified by Linux in
- Documentation/arm/booting.rst
- Documentation/arm64/booting.rst
- etc.
In the case of UEFI PE/COFF, if sets up the environment as described in the UEFI spec, section 2; particularly 2.3 which describes the calling convention.
These are not fundamentally different, other than the Linux environment is specific to Linux, but the UEFI one is standard across several OSes and tools.
To look at the simplest scenario, UEFI also defines a standard way to choose what to boot next. It uses the BOOTxxxx and BOOTORDER variables to store what to boot, and which one to try next. If BOOTxxxx points at the kernel, then U-Boot goes straight to Linux.
[...]
I am wondering if we can come up with a way to deterministically specify how a system will boot and how to make it boot a different way (i.e. with a different kernel, initrd, DT).
Heinrich mentioned EFI variables as a way of selecting kernel/initrd/DT. Then the problem becomes just a case of being able to change those variables from Linux userspace. Is that right?
We are talking about having a 'secure' part of EBBR, which allows for secure boot. Should we have a 'defined boot' part of EBBR, that defines how the kernel/DT/initrd are selected, based on EFI variables?
Unfortunately I just don't know enough about all the different boot flows used by the different distros. It seems like crazy town. Does anyone have some pointers so I can do some study?
A great way to get familiar with it is to play around with the UEFI code already in U-Boot with the UEFI Self Certification test suite. Building the SCT is straight forward, and there are some instructions for doing so here:
https://github.com/glikely/edk2-test-manifest
If you enable UEFI in U-Boot and copy the SCT to a USB drive, u-boot should be able to find and run the UEFI shell from the USB drive.
There is also good documentation on the U-Boot implementation:
https://github.com/u-boot/u-boot/blob/master/doc/uefi/uefi.rst
I have actually reviewed a lot of the code (and remember 5 years ago I worked on the original lib/efi in U-Boot - see doc/uefi/u-boot_on_efi.rst). But it is just hard to get my head around all the pieces. It is the complexity of the bit between U-Boot and Linux that I find challenging.
It doesn't help that u-boot-on-uefi is completely different from UEFI-in-U-Boot! The first is running U-Boot as a UEFI application on top of some other UEFI implementation (e.g. the stock BIOS on a PC)... which ironically is conceptually similar to running Grub!
The second is U-Boot exposing the UEFI ABI so that it can run UEFI applications itself....
The natural extension of this is of course to start nesting multiple copies of U-Boot, each both running on, and providing the ABI of, UEFI! :-p
Thank you both for the pointers.
I think in general we are talking at cross purposes. I am looking for how we can specify a simple verified boot, with or without EFI, so that we actually create something that covers everything. Distributions and particular cases may go to endless depths of complexity, but if we want to incorporate the embedded case, then in my view we need a simple option, with no more pieces, code, boot time and complexity than is needed. It seems like the EFI protocol allows this, so I imagine:
Simple EFI boot:
- U-Boot starts*, selects and verifies config using UEFI variables,
loads image (linux, inittd, FPGA, DTs, etc.) verifying as it goes, boots linux, passing in the images and providing EFI services as specified by EBBR. OS updates use efibootmgr to select new kernels, images, etc., as (to be) specified by EBBR
- (degenerate case, where EFI is not used) U-Boot starts, selects and
verifies config using a proprietary mechanism, loads image (linux, inittd, FPGA, DTs, etc.) verifying as it goes, boots linux, passing in the images. Updates use a proprietary mechanism not specified by EBBR
Note that for platforms with separate firmware storage (e.g. SPI flash), if the disk is wiped, or fails to to verify before or after linux boots, then a recovery mode is needed to obtain a new disk image. The firmware needs to be able to load, verify and write an image to the disk so that the machine can continue to operate. Storing firmware on the disk is not really viable. For other platforms, presumably the disk is removable so the user can reflash it.
Complex EFI boot:
- U-Boot (or anything else) starts, loads an EFI app (maybe grub or
something else) and provides EFI services as specified by EBBR. What happens from there is distro-specific and not specified by EBBR. Updates are handled by any means necessary and not specified by EBBR.
In the cases above, I think only cases 1 & 3 are relevant for EBBR. Case 2 is out of scope because EBBR is specifically about standardizing on the UEFI ABI, so it isn't going to cover any non-UEFI boot path. Alternate boot paths are certainly not forbidden (e.g. bootm and bootefi happily coexist), but without a strong cross-platform incentive, there isn't any reason to standarize this path.
Case 3 is already covered by the spec, but Case 1 requires a bit more discussion.
In case 1, UEFI already has all the functionality needed to directly load the OS loader (kernel) and provide a DT. The kernel can also itself load from disk both the initrd and dtb using command line options to the kernel. Loading FPGA bitstreams or peripheral firmware is not covered by the core UEFI ABI.
Loading of the initrd and dtb using kernel command line flags is not well liked among the kernel UEFI devs. It doesn't provide a way to validate the image, and it is preferred to load both prior to calling the kernel.
Assuming the FIT image model of shipping an OS image where the kernel, initrd & dtb are all packaged together, as well as ancillary firmware blobs and FPGA bitstreams, it would be valuable to define how that works in the UEFI boot flow. Need some discussion on what that might look like, and what the split of responsibility needs to be between the FIT contents and U-Boot proper.
Please read
https://u-boot.readthedocs.io/en/latest/uefi/uefi.html#launching-a-uefi-bina...
Up to now the signed fit image can provide the UEFI binary and the FDT.
We could easily and probably should extend U-Boot to provide the RAM disk loaded as part of a fit image via the EFI_LOAD_FILE2_PROTOCOL for consumption by Linux.
@Ard: Is there any documentation for the usage of the EFI_LOAD_FILE2_PROTOCOL by Linux? I could not find anything in https://www.kernel.org/doc/html/latest/search.html. We should document how the RAM disk is passed to the EFI stub and how it is communicated to the main Linux.
To my understanding this is how it works:
If the the kernel command line contains "noinitrd", no RAM disk is loaded by the EFI stub.
If the parameter is not passed the initial RAM disk is searched in the following sequence:
* EFI_LOAD_FILE2_PROTOCOL * file path indicated by initrd= command line argument.
After successfully loading a RAM disk the Linux EFI stub sets the device tree properties "linux,initrd-start" and "linux,initrd-end" in the "/chosen" node to indicated the location of the RAM disk.
If the EFI stub is called without a device-tree, an empty device tree is created for adding these properties. So on boards with ACPI the initrd address is passed via the device-tree too.
GRUB uses the same DT properties to pass a RAM disk (function linux_prepare_fdt()). So the RAM disk position passed by GRUB is only a fallback replaced by the kernel EFI stub when loading an image via the EFI_LOAD_FILE2_PROTOCOL or the initrd= parameter.
Best regards
Heinrich
- Of course there is ATF, SPL and other things which complicate this
piece. I think EBBR should specify these too.
Absolutely the layers of trusted firmware and TEEs are in scope.
Cheers, g.
On Fri, 2 Oct 2020 at 15:12, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
...
Please read
https://u-boot.readthedocs.io/en/latest/uefi/uefi.html#launching-a-uefi-bina...
Up to now the signed fit image can provide the UEFI binary and the FDT.
We could easily and probably should extend U-Boot to provide the RAM disk loaded as part of a fit image via the EFI_LOAD_FILE2_PROTOCOL for consumption by Linux.
@Ard: Is there any documentation for the usage of the EFI_LOAD_FILE2_PROTOCOL by Linux? I could not find anything in https://www.kernel.org/doc/html/latest/search.html. We should document how the RAM disk is passed to the EFI stub and how it is communicated to the main Linux.
To my understanding this is how it works:
If the the kernel command line contains "noinitrd", no RAM disk is loaded by the EFI stub.
If the parameter is not passed the initial RAM disk is searched in the following sequence:
- EFI_LOAD_FILE2_PROTOCOL
- file path indicated by initrd= command line argument.
After successfully loading a RAM disk the Linux EFI stub sets the device tree properties "linux,initrd-start" and "linux,initrd-end" in the "/chosen" node to indicated the location of the RAM disk.
If the EFI stub is called without a device-tree, an empty device tree is created for adding these properties. So on boards with ACPI the initrd address is passed via the device-tree too.
GRUB uses the same DT properties to pass a RAM disk (function linux_prepare_fdt()). So the RAM disk position passed by GRUB is only a fallback replaced by the kernel EFI stub when loading an image via the EFI_LOAD_FILE2_PROTOCOL or the initrd= parameter.
Indeed.
The need to modify the DT to pass parameters to the incoming kernel is problematic, because it interferes with the OS's ability to authenticate it (assuming we ever get to that point).
What we attempted with these changes is to have a cleaner separation between a) h/w description b) data provided by the boot environment.
in a way that enables authentication and attestation in a arch-agnostic manner.
Note that initrd= is problematic because it only supports loading the initrd from the same block device that the kernel image itself was loaded from, which may be overly restrictive in some cases.
boot-architecture@lists.linaro.org