Platform: OpenLinuxBoot.efi

This commit is contained in:
MikeBeaton 2021-08-18 19:12:54 +01:00
parent d2ba13b6a2
commit 22cfebdf6f
55 changed files with 6618 additions and 362 deletions

View File

@ -5,7 +5,6 @@
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Uefi.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcMiscLib.h>

View File

@ -6,6 +6,11 @@ OpenCore Changelog
- Added pattern-based automatic variable initialisation for better security
- Updated underlying EDK II package to edk2-stable202108
- Updated Apple Secure Boot variables for `x86legacy`
- Update Linux variants in Flavours.md
- Implement Boot Entry Protocol, allowing plug-in boot entry drivers
- Add StringBuffer and FlexArray libraries
- Update Drivers to support arguments (requires config.plist update, see samples)
- Add OpenLinuxBoot driver: OC-native Linux autodetect and boot without chaining via GRUB
#### v0.7.2
- Fixed OSBundleLibraries/OSBundleLibaries64 handling

View File

@ -3682,6 +3682,7 @@ nvram 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:boot-log |
\item \texttt{GSTT} --- GoptStop
\item \texttt{HDA} --- AudioDxe
\item \texttt{KKT} --- KeyTester
\item \texttt{LNX} --- OpenLinuxBoot
\item \texttt{MMDD} --- MmapDump
\item \texttt{OCPAVP} --- PavpProvision
\item \texttt{OCRST} --- ResetSystem
@ -4106,8 +4107,13 @@ rm vault.pub
of EFI System Partition file system.
\item \texttt{0x00000800} (bit \texttt{11}) --- \texttt{OC\_SCAN\_ALLOW\_FS\_NTFS}, allows scanning
of NTFS (Msft Basic Data) file system.
\item \texttt{0x00001000} (bit \texttt{12}) --- \texttt{OC\_SCAN\_ALLOW\_FS\_EXT}, allows scanning
of EXT (Linux Root) file system.
\item \texttt{0x00001000} (bit \texttt{12}) --- \texttt{OC\_SCAN\_ALLOW\_FS\_LINUX\_ROOT}, allows
scanning of Linux Root file systems.
\item \texttt{0x00002000} (bit \texttt{13}) --- \texttt{OC\_SCAN\_ALLOW\_FS\_LINUX\_DATA}, allows
scanning of Linux Data file systems.
\item \texttt{0x00004000} (bit \texttt{14}) --- \texttt{OC\_SCAN\_ALLOW\_FS\_XBOOTLDR}, allows
scanning the Extended Boot Loader Partition as defined by the
\href{https://systemd.io/BOOT\_LOADER\_SPECIFICATION/}{Boot Loader Specification}.
\item \texttt{0x00010000} (bit \texttt{16}) --- \texttt{OC\_SCAN\_ALLOW\_DEVICE\_SATA}, allow
scanning SATA devices.
\item \texttt{0x00020000} (bit \texttt{17}) --- \texttt{OC\_SCAN\_ALLOW\_DEVICE\_SASEX}, allow
@ -5916,11 +5922,14 @@ Depending on the firmware, a different set of drivers may be required.
Loading an incompatible driver may lead the system to unbootable state or
even cause permanent firmware damage. Some of the known drivers are listed below:
\begin{tabular}{p{1.3in}p{5.55in}}
\begin{longtable}{p{1.3in}p{5.55in}}
\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{AudioDxe}}\textbf{*}
& HDA audio support driver in UEFI firmware for most Intel and some other analog audio controllers.
Staging driver, refer to \href{https://github.com/acidanthera/bugtracker/issues/740}{acidanthera/bugtracker\#740}
for known issues in AudioDxe. \\
\href{https://github.com/acidanthera/OcBinaryData}{\texttt{btrfs\_x64}}
& Open source BTRFS file system driver, required for booting with \hyperref[uefilinux]{OpenLinuxBoot}
from a file system which is now quite commonly used with Linux. \\
\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{CrScreenshotDxe}}\textbf{*}
& Screenshot making driver saving images to the root of OpenCore partition (ESP) or
any available writeable filesystem upon pressing \texttt{F10}.
@ -5930,6 +5939,9 @@ even cause permanent firmware damage. Some of the known drivers are listed below
& Proprietary ExFAT file system driver for Bootcamp support commonly found in Apple
firmware. For Sandy Bridge and earlier CPUs, the \texttt{ExFatDxeLegacy} driver should be
used due to the lack of \texttt{RDRAND} instruction support. \\
\href{https://github.com/acidanthera/OcBinaryData}{\texttt{ext4\_x64}}
& Open source EXT4 file system driver, required for booting with \hyperref[uefilinux]{OpenLinuxBoot}
from the file system most commonly used with Linux. \\
\href{https://github.com/acidanthera/OcBinaryData}{\texttt{HfsPlus}}
& Recommended. Proprietary HFS file system driver with bless support commonly found in Apple
firmware. For Sandy Bridge and earlier CPUs, the \texttt{HfsPlusLegacy} driver should be
@ -5952,6 +5964,10 @@ even cause permanent firmware damage. Some of the known drivers are listed below
& \hyperref[ueficanopy]{OpenCore plugin} implementing graphical interface. \\
\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenRuntime}}\textbf{*}
& \hyperref[uefiruntime]{OpenCore plugin} implementing \texttt{OC\_FIRMWARE\_RUNTIME} protocol. \\
\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenLinuxBoot}}\textbf{*}
& \hyperref[uefilinux]{OpenCore plugin} implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}
to allow direct detection and booting of Linux distributiuons from OpenCore, without
chainloading via GRUB. \\
\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenUsbKbDxe}}\textbf{*}
& USB keyboard driver adding support for \texttt{AppleKeyMapAggregator} protocols
on top of a custom USB keyboard driver implementation. This is an alternative to
@ -5982,7 +5998,7 @@ even cause permanent firmware damage. Some of the known drivers are listed below
& XHCI USB controller support driver from \texttt{MdeModulePkg}. This driver is
included in most types of firmware starting with the Sandy Bridge generation. For earlier firmware
or legacy systems, it may be used to support external USB 3.0 PCI cards.
\end{tabular}
\end{longtable}
Driver marked with \textbf{*} are bundled with OpenCore.
To compile the drivers from UDK (EDK II) the same command used for
@ -6158,6 +6174,178 @@ functioning. Feature highlights:
mapping (e.g. \texttt{EnableWriteUnprotector}).
\end{itemize}
\subsection{OpenLinuxBoot}\label{uefilinux}
\texttt{OpenLinuxBoot} is an OpenCore plugin implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}.
It detects and boots Linux distros which are installed according to the
\href{https://systemd.io/BOOT_LOADER_SPECIFICATION/}{Boot Loader Specification}
or to the closely related (but not identical, see next paragraph)
\href{https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault}{systemd BootLoaderSpecByDefault}.
In effect this means Linux distributions where the available boot options are found in
\texttt{\{ESP\}/loader/entries/*.conf} files (for instance \texttt{/boot/efi/loader/entries/*.conf})
or in \texttt{\{boot\}/loader/entries/*.conf} files (for instance \texttt{/boot/loader/entries/*.conf}).
The former layout -- pure Boot Loader Specification, using kernel files on the EFI System Partition or
Extended Boot Loader Partition -- is specific to systemd-boot, the latter
layout with kernel files typically on the partition which will be mounted as \texttt{/boot}
applies to most Fedora-related distros including Fedora itself, RHEL and variants.
BootLoaderSpecByDefault includes the possibility of expanding GRUB variables
in its \texttt{*.conf} files -- and this is used in practice in certain distros such as CentOS.
In order to correctly handle this, \texttt{OpenLinuxBoot} extracts all variables from
\texttt{\{boot\}/grub2/grubenv} and any unconditionally set variables from \texttt{\{boot\}/grub2/grub.cfg}.
This has proved sufficient in practice to extract the required variables seen so far in distros which use this
GRUB-specific feature.
For distributions which do not use either of the above schemes, \texttt{OpenLinuxBoot} will autodetect and
boot \texttt{\{boot\}/vmlinuz*} kernel files directly, after linking these automatically -- based on the
kernel version in the filename -- to their associated \texttt{\{boot\}/init*} ramdisk files, and after
searching in \texttt{/etc/default/grub} for kernel boot options and \texttt{/etc/os-release} for the
distro name.
This layout applies to most Debian-related distros, including Debian itself, Ubuntu and variants.
The method of starting the kernel relies on it being compiled with EFISTUB, however this applies
to almost all modern distros, particularly those which use systemd. Most modern distros
use systemd as their system manager (even though at the same time most do \emph{not} use systemd-boot as
their bootloader).
The latest kernel version of a given install is always shown in the boot menu. Additional versions,
recovery versions, etc. are added as auxiliary boot entries, so depending on OpenCore's
\texttt{HideAuxiliary} setting may not be shown until the space key is pressed.
\emph{Note 1}: \texttt{OpenLinuxBoot} requires filesystem drivers that may not be available in
firmware such as EXT4 and BTRFS drivers. These drivers can be obtained from external sources.
Drivers tested in basic scenarios can be downloaded from \href{https://github.com/acidanthera/OcBinaryData}{OcBinaryData}.
Be aware that these drivers are neither tested for reliability in all scenarious, nor underwent any
tamper-resistance testing, therefore have may carry potential security or data-loss risks.
Most Linux distributions keep their boot files on the EXT4 file system even when the distribution's
main filesystem is something else such as BTRFS, therefore a suitable UEFI EXT4 file system
driver such as \href{https://github.com/acidanthera/OcBinaryData}{\texttt{ext4\_x64}} is normally required.
A BTRFS driver such as \href{https://github.com/acidanthera/OcBinaryData}{\texttt{btrfs\_x64}}
will be required in a somewhat less standard setup where the boot files are on a BTRFS partition,
e.g. as by default in openSUSE.
Pure Boot Loader Spec (e.g. as implemented by systemd-boot) keeps all kernel and ramdisk images directly
on the EFI System Partition (or an Extended Boot Loader Partition), therefore it requires no additional
filesystem driver - but it is not widely used except in Arch Linux.
\emph{Note 2}: systemd-boot users (probably almost exclusively Arch Linux users) should be aware that \texttt{OpenLinuxBoot}
does not support the systemd-boot--specific \href{https://systemd.io/BOOT\_LOADER\_INTERFACE/}{Boot Loader Interface};
therefore use \texttt{efibootmgr} rather than \texttt{bootctl} for any low-level Linux command line interaction with
the boot menu.
The default parameter values should work well, but if you need to parameterise this driver the following
options may be specified in \texttt{UEFI/Drivers/Arguments}:
\begin{itemize}
\tightlist
\item \texttt{flags} - Default: all flags except \texttt{LINUX\_BOOT\_ADD\_DEBUG\_INFO} are set. \medskip
Available flags are: \medskip
\begin{itemize}
\tightlist
\item \texttt{0x00000001} (bit \texttt{0}) --- \texttt{LINUX\_BOOT\_SCAN\_ESP},
Allows scanning for entries on EFI System Partition.
\item \texttt{0x00000002} (bit \texttt{1}) --- \texttt{LINUX\_BOOT\_SCAN\_XBOOTLDR},
Allows scanning for entries on Extended Boot Loader Partition.
\item \texttt{0x00000004} (bit \texttt{2}) --- \texttt{LINUX\_BOOT\_SCAN\_LINUX\_ROOT},
Allows scanning for entries on Linux Root filesystems.
\item \texttt{0x00000008} (bit \texttt{3}) --- \texttt{LINUX\_BOOT\_SCAN\_LINUX\_DATA},
Allows scanning for entries on Linux Data filesystems.
\item \texttt{0x00000080} (bit \texttt{7}) --- \texttt{LINUX\_BOOT\_SCAN\_OTHER},
Allows scanning for entries on file systems not matched by any of the above. \medskip
The following notes apply to all of the above options: \medskip
\emph{Note 1}: Apple filesystems APFS and HFS are never scanned.
\medskip
\emph{Note 2}: Regardless of the above flags, a file system must first be
allowed by \texttt{Misc/Security/ScanPolicy} before it can be seen by
\texttt{OpenLinuxBoot} or any other \texttt{OC\_BOOT\_ENTRY\_PROTOCOL} driver.
\medskip
\emph{Note 3}: It is recommended to enable scanning \texttt{LINUX\_ROOT} and \texttt{LINUX\_DATA}
in both \texttt{OpenLinuxBoot} flags and \texttt{Misc/Security/ScanPolicy} in order to be sure
to detect all valid Linux installs.
\medskip
\item \texttt{0x00000100} (bit \texttt{8}) --- \texttt{LINUX\_BOOT\_ALLOW\_AUTODETECT},
If set allows autodetecting and linking \texttt{vmlinuz*} and \texttt{init*} ramdisk files
when \texttt{loader/entries} files are not found.
\item \texttt{0x00000200} (bit \texttt{9}) --- \texttt{LINUX\_BOOT\_USE\_LATEST},
When a Linux entry generated by \texttt{OpenLinuxBoot} is selected as the default boot entry
in OpenCore, automatically switch to the latest kernel when a new version is installed. \medskip
When this option is set, an internal menu entry id is shared between kernel versions from the same install
of Linux. Linux boot options are always sorted highest kernel version first, so this means that
the latest kernel version of the same install always shows as the default, with this option set. \medskip
\emph{Note}: This option is recommended on all systems. \medskip
\item \texttt{0x00000400} (bit \texttt{10}) --- \texttt{LINUX\_BOOT\_ADD\_RO},
This option applies to autodetected Linux only (i.e. to Debian-style distrubutions, not to BLSpec and
Fedora-style distributions with \texttt{/loader/entries/*.conf} files).
Some distrubtions run a filesystem check on loading which requires the root
filesystem to initially be mounted read-only via the \texttt{ro} kernel option. Set this bit to add this
option on autodetected distros; should be harmless but very slightly slow down boot time (due to requried
remount as read-write) on distros which do not require it. To specify this option for specific
distros only, use \texttt{partuuidopts:\{partuuid\}+=ro} instead of this flag.
\item \texttt{0x00008000} (bit \texttt{15}) --- \texttt{LINUX\_BOOT\_ADD\_DEBUG\_INFO},
Adds a human readable file system type, followed by the first eight characters of the
partition's unique partition uuid, to each generated entry name. Can help with debugging
the origin of entries generated by the driver when there are multiple Linux installs on
one system.
\end{itemize} \medskip
Flag values can be specified in hexadecimal beginning with \texttt{0x} or in decimal,
e.g. \texttt{flags=0x80} or \texttt{flags=128}. \medskip
\item \texttt{partuuidopts:\{partuuid\}[+]="\{options\}"} - Default: not set. \medskip
Allows specifying kernel options for a given partition only. If specified with \texttt{+=} then
these are used in addition to autodetected options, if specified with \texttt{=} they are used instead.
Used for autodetected Linux only. Values specified here are never used for entries created from
\texttt{/loader/entries/*.conf} files.
\medskip
\emph{Note}: The \texttt{partuuid} value to be specified here is typically the same as the \texttt{PARTUUID}
seen in \texttt{root=PARTUUID=...} in the Linux kernel boot options (view using
\texttt{cat /proc/cmdline}) for autodetected Debian-style distros, but is NOT the same for
Fedora-style distros booted from \texttt{/loader/entries/*.conf} files. \medskip
Typically you should not need this option in the latter case, but in case you do, to find out the unique
partition uuid to use, look for \texttt{LNX:} entries in the OpenCore debug log file. Alternatively, and
for more advanced scenarios, you may wish to examine how your drives are mounted using the
Linux \texttt{mount} command, and then find out the partuuid of relevant mounted drives by examining the
output of \texttt{ls -l /dev/disk/by-partuuid}. \medskip
\item \texttt{autoopts[+]="\{options\}"} - Default: None specified. The kernel options to use
for autodetected Linux only. The value here is never used for entries created from
\texttt{/loader/entries/*.conf} files. \texttt{partuuidopts} may be more suitable where there are multiple
distros, but \texttt{autoopts} with no PARTUUID required is more convenient for just one distro.
If specified with \texttt{+=} then these are used in addition to autodetected options, if specified
with \texttt{=} they are used instead. As example usage, it is possible to use \texttt{+=} format to add
a \texttt{vt.handoff} options, such as \texttt{autopts+="vt.handoff=7"} or \texttt{autopts+="vt.handoff=3"}
(check \texttt{cat /proc/cmdline} when booted via your existing bootloader) on Ubuntu and related distros,
in order to add the \texttt{vt.handoff} option to the auto-detected GRUB defaults, and avoid a flash of text
showing before the distro splash screen.
\medskip
Users may wish to compare their Linux boot options (shown with \texttt{cat /proc/cmdline}) seen when booting via
\texttt{OpenLinuxBoot} and via their distro's original bootloader, which is normally GRUB (but might also be e.g.
systemd-boot or EXTLINUX). Expect the options generated by \texttt{OpenLinuxBoot} not to
contain a \texttt{BOOT\_IMAGE=...} value where GRUB options do, and to contain an
\texttt{initrd=...} value where the GRUB options do not, since GRUB hands over ramdisks in a different way.
All remaining parameters should match, however -- perhaps excluding less important graphics handover options,
such as in the Ubuntu example given in \texttt{autoopts}.
\texttt{OpenLinuxBoot} will not start a distro unless it can find some configured options to use, therefore in
the hopefully unlikely case where no auto-detectable options are available, the user will need to specify the correct options
with \texttt{partuuidopts} or \texttt{autoopts} before the distro will boot. Examine the OpenCore debug log
for \texttt{LNX:} entries containing further information about what was found.
\end{itemize}
\subsection{Properties}\label{uefiprops}
\begin{enumerate}
@ -6228,12 +6416,11 @@ functioning. Feature highlights:
\item
\texttt{Drivers}\\
\textbf{Type}: \texttt{plist\ array}\\
\textbf{Type}: \texttt{plist\ dict}\\
\textbf{Failsafe}: None\\
\textbf{Description}: Load selected drivers from \texttt{OC/Drivers}
directory.
To be filled with string filenames meant to be loaded as UEFI drivers.
directory using the settings specified in the
\hyperref[uefidriversprops]{Drivers Properties} section below.
\item
\texttt{Input}\\
@ -6681,6 +6868,33 @@ functioning. Feature highlights:
\end{enumerate}
\subsection{Drivers Properties}\label{uefidriversprops}
\begin{enumerate}
\item
\texttt{Path}\\
\textbf{Type}: \texttt{plist\ string}\\
\textbf{Failsafe}: Empty\\
\textbf{Description}: Path of file to be loaded as a UEFI driver
from \texttt{OC/Drivers} directory.
\item
\texttt{Enabled}\\
\textbf{Type}: \texttt{plist\ boolean}\\
\textbf{Failsafe}: \texttt{false}\\
\textbf{Description}: If \texttt{false} this driver entry will be ignored.
\item
\texttt{Arguments}\\
\textbf{Type}: \texttt{plist\ string}\\
\textbf{Failsafe}: Empty\\
\textbf{Description}: Some OC plugins accept optional additional arguments
which may be specified as a string here.
\end{enumerate}
\subsection{Input Properties}\label{uefiinputprops}
\begin{enumerate}

View File

@ -78,6 +78,7 @@ Please open an Issue or Pull Request if an additional Linux flavour is required.
- **Linux** - Base icon for Linux (`Linux.icns`)
- **Arch:Linux** - Arch Linux (`Arch.icns`, etc.)
- **Astra:Linux** - Astra Linux
- **CentOS:Linux** - CentOS
- **Debian:Linux** - Debian
- **Deepin:Linux** - Deepin
- **elementaryOS:Linux** - elementary OS
@ -90,16 +91,18 @@ Please open an Issue or Pull Request if an additional Linux flavour is required.
- **Mageia:Linux** - Mageia (fork of former Mandriva)
- **Manjaro:Linux** - Manjaro
- **Mint:Linux** - Linux Mint
- **openSUSE:Linux** - openSUSE
- **Oracle:Linux** - Oracle Linux
- **PopOS:Linux** - Pop!_OS
- **RHEL:Linux** - Red Hat Enterprise Linux
- **Rocky:Linux** - Rocky Linux
- **Solus:Linux** - Solus
- **Ubuntu:Linux** - Ubuntu
- **Lubuntu:Ubuntu:Linux** - Lubuntu (`Lubuntu.icns`, etc.)
- **UbuntuMATE:Ubuntu:Linux** - Ubuntu MATE
- **Void:Linux** - Void Linux
- **Xubuntu:Ubuntu:Linux** - Xubuntu
- **Zorin:Linux** - Zorin OS
- **openSUSE:Linux** - openSUSE
## Other Operating Systems

View File

@ -1305,20 +1305,134 @@
<true/>
<key>Drivers</key>
<array>
<string>HfsPlus.efi</string>
<string>OpenRuntime.efi</string>
<string>#OpenCanopy.efi</string>
<string>#AudioDxe.efi</string>
<string>#OpenPartitionDxe.efi</string>
<string>#OpenUsbKbDxe.efi</string>
<string>#UsbMouseDxe.efi</string>
<string>#Ps2KeyboardDxe.efi</string>
<string>#Ps2MouseDxe.efi</string>
<string>#HiiDatabase.efi</string>
<string>#NvmExpressDxe.efi</string>
<string>#XhciDxe.efi</string>
<string>#ExFatDxe.efi</string>
<string>#CrScreenshotDxe.efi</string>
<dict>
<key>Path</key>
<string>HfsPlus.efi</string>
<key>Enabled</key>
<true/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenRuntime.efi</string>
<key>Enabled</key>
<true/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenCanopy.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>AudioDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenPartitionDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenUsbKbDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>UsbMouseDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>Ps2KeyboardDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>Ps2MouseDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>HiiDatabase.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>NvmExpressDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>XhciDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>ExFatDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>CrScreenshotDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>ext4_x64.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenLinuxBoot.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
</array>
<key>Input</key>
<dict>

View File

@ -1643,20 +1643,134 @@
<true/>
<key>Drivers</key>
<array>
<string>HfsPlus.efi</string>
<string>OpenRuntime.efi</string>
<string>#OpenCanopy.efi</string>
<string>#AudioDxe.efi</string>
<string>#OpenPartitionDxe.efi</string>
<string>#OpenUsbKbDxe.efi</string>
<string>#UsbMouseDxe.efi</string>
<string>#Ps2KeyboardDxe.efi</string>
<string>#Ps2MouseDxe.efi</string>
<string>#HiiDatabase.efi</string>
<string>#NvmExpressDxe.efi</string>
<string>#XhciDxe.efi</string>
<string>#ExFatDxe.efi</string>
<string>#CrScreenshotDxe.efi</string>
<dict>
<key>Path</key>
<string>HfsPlus.efi</string>
<key>Enabled</key>
<true/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenRuntime.efi</string>
<key>Enabled</key>
<true/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenCanopy.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>AudioDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenPartitionDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenUsbKbDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>UsbMouseDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>Ps2KeyboardDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>Ps2MouseDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>HiiDatabase.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>NvmExpressDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>XhciDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>ExFatDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>CrScreenshotDxe.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>ext4_x64.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
<dict>
<key>Path</key>
<string>OpenLinuxBoot.efi</string>
<key>Enabled</key>
<false/>
<key>Arguments</key>
<string></string>
</dict>
</array>
<key>Input</key>
<dict>

View File

@ -12,6 +12,7 @@
#include <IndustryStandard/AppleHid.h>
#include <Library/OcAppleBootPolicyLib.h>
#include <Library/OcAppleKeyMapLib.h>
#include <Library/OcFlexArrayLib.h>
#include <Library/OcStringLib.h>
#include <Library/OcStorageLib.h>
#include <Library/OcTypingLib.h>
@ -24,6 +25,10 @@
//#define BUILTIN_DEMONSTRATE_TYPING
#endif
#if !defined(OC_TRACE_PARSE_VARS)
#define OC_TRACE_PARSE_VARS DEBUG_VERBOSE
#endif
/**
Primary picker context.
**/
@ -180,7 +185,7 @@ EFI_STATUS
/**
Discovered boot entry.
Note, inner resources must be freed with OcResetBootEntry.
Note, inner resources must be freed with FreeBootEntry.
**/
typedef struct OC_BOOT_ENTRY_ {
//
@ -197,6 +202,10 @@ typedef struct OC_BOOT_ENTRY_ {
//
OC_BOOT_SYSTEM_ACTION SystemAction;
//
// Id under which to save entry as default.
//
CHAR16 *Id;
//
// Obtained human visible name.
//
CHAR16 *Name;
@ -235,6 +244,10 @@ typedef struct OC_BOOT_ENTRY_ {
//
BOOLEAN IsCustom;
//
// Set when entry was created by OC_BOOT_ENTRY_PROTOCOL.
//
BOOLEAN IsBootEntryProtocol;
//
// Should make this option default boot option.
//
BOOLEAN SetDefault;
@ -247,6 +260,11 @@ typedef struct OC_BOOT_ENTRY_ {
//
BOOLEAN ExposeDevicePath;
//
// Partition UUID of entry device.
// Set for boot entry protocol boot entries only.
//
EFI_GUID UniquePartitionGUID;
//
// Load option data (usually "boot args") size.
//
UINT32 LoadOptionsSize;
@ -256,6 +274,24 @@ typedef struct OC_BOOT_ENTRY_ {
VOID *LoadOptions;
} OC_BOOT_ENTRY;
/**
Parsed load option or shell variable.
**/
typedef struct OC_PARSED_VAR_ASCII_ {
CHAR8 *Name;
CHAR8 *Value;
} OC_PARSED_VAR_ASCII;
typedef struct OC_PARSED_VAR_UNICODE_ {
CHAR16 *Name;
CHAR16 *Value;
} OC_PARSED_VAR_UNICODE;
typedef union OC_PARSED_VAR_ {
OC_PARSED_VAR_ASCII Ascii;
OC_PARSED_VAR_UNICODE Unicode;
} OC_PARSED_VAR;
/**
Boot filesystem containing boot entries.
**/
@ -356,9 +392,21 @@ typedef struct OC_BOOT_CONTEXT_ {
#define OC_SCAN_ALLOW_FS_NTFS BIT11
/**
Allow scanning EXT filesystems (e.g. EXT4).
Allow scanning Linux Root filesystems.
https://systemd.io/DISCOVERABLE_PARTITIONS/
**/
#define OC_SCAN_ALLOW_FS_EXT BIT12
#define OC_SCAN_ALLOW_FS_LINUX_ROOT BIT12
/**
Allow scanning Linux Data filesystems.
https://systemd.io/DISCOVERABLE_PARTITIONS/
**/
#define OC_SCAN_ALLOW_FS_LINUX_DATA BIT13
/**
Allow scanning XBOOTLDR filesystems.
**/
#define OC_SCAN_ALLOW_FS_XBOOTLDR BIT14
/**
Allow scanning SATA devices.
@ -416,11 +464,12 @@ typedef struct OC_BOOT_CONTEXT_ {
OC_SCAN_ALLOW_DEVICE_PCI)
/**
All device bits used by OC_SCAN_DEVICE_LOCK.
All file system bits used by OC_SCAN_DEVICE_LOCK.
**/
#define OC_SCAN_FILE_SYSTEM_BITS ( \
OC_SCAN_ALLOW_FS_APFS | OC_SCAN_ALLOW_FS_HFS | OC_SCAN_ALLOW_FS_ESP | \
OC_SCAN_ALLOW_FS_NTFS | OC_SCAN_ALLOW_FS_EXT)
OC_SCAN_ALLOW_FS_APFS | OC_SCAN_ALLOW_FS_HFS | OC_SCAN_ALLOW_FS_ESP | \
OC_SCAN_ALLOW_FS_NTFS | OC_SCAN_ALLOW_FS_LINUX_ROOT | \
OC_SCAN_ALLOW_FS_LINUX_DATA | OC_SCAN_ALLOW_FS_XBOOTLDR )
/**
By default allow booting from APFS from internal drives.
@ -473,14 +522,23 @@ EFI_STATUS
/**
Custom picker entry.
Note that OpenLinuxBoot OC_BOOT_ENTRY_PROTOCOL_REVISION needs incrementing
when this structure is updated.
**/
typedef struct {
//
// Used by OC_BOOT_ENTRY_PROTOCOL to reidentify entry.
// Multiple entries may share an id - allows e.g. newest version
// of Linux install to automatically become selected default.
//
CONST CHAR8 *Id;
//
// Entry name.
//
CONST CHAR8 *Name;
//
// Entry path.
// Absolute device path to file for user custom entries,
// file path relative to device root for boot entry protocol.
//
CONST CHAR8 *Path;
//
@ -1378,6 +1436,18 @@ typedef struct OC_BOOT_ARGUMENTS_ {
EFI_SYSTEM_TABLE *SystemTable;
} OC_BOOT_ARGUMENTS;
///
/// Boot services does not zero LoadOptions and LoadOptionsSize of a
/// loaded image by default. Applying a limit to accepted LoadOptionsSize
/// is intended to spot this and make it less likely to cause a segmentation
/// fault if a newer driver (using LoadOptions) is called by an older version
/// of OC (or anything else) which leaves these uninitialised.
/// However the 'uninitialised' (?) value in LoadOptionsSize seems to be small
/// but not zero, therefore unfortunately this approach - while not harmful - is
/// not helping to detect this situation.
///
#define MAX_LOAD_OPTIONS_SIZE SIZE_4KB
/**
Parse macOS kernel into unified boot arguments structure.
@ -1747,4 +1817,189 @@ OcImageLoaderLoad (
OUT EFI_HANDLE *ImageHandle
);
/**
Parse loaded image protocol load options.
Assumes CHAR_NULL terminated Unicode string of space separated options,
each of form {name} or {name}={value}. Double quotes can be used round {value} to
include spaces, and '\' can be used within quoted or unquoted values to escape any
character (including space and '"').
NB Var names and values are left as pointers to within the original raw LoadOptions
string, which may be modified during processing.
@param[in] LoadedImage Loaded image handle.
@param[out] ParsedVars Parsed load options if successful, NULL otherwise.
Caller may free after use with OcFlexArrayFree
if required.
@retval EFI_SUCCESS Success.
@retval EFI_NOT_FOUND Missing or empty load options.
@retval EFI_OUT_OF_RESOURCES Out of memory.
@retval EFI_INVALID_PARAMETER Invalid load options detected.
**/
EFI_STATUS
OcParseLoadOptions (
IN CONST EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
OUT OC_FLEX_ARRAY **ParsedVars
);
/**
Parse Unix-style var file or string. Parses a couple of useful ASCII
GRUB config files (multi-line, name=var, with optinal comments) and
defines a standard format for Unicode UEFI LoadOptions.
Assumes CHAR_NULL terminated Unicode string of space separated options,
each of form {name} or {name}={value}. Double quotes can be used round {value} to
include spaces, and '\' can be used within quoted or unquoted values to escape any
character (including space and '"').
Comments (if any) run from '#' to end of same line.
NB Var names and values are left as pointers to within the raw string, which may
be modified during processing.
@param[in] StrVars Raw var string.
@param[out] ParsedVars Parsed variables if successful, NULL otherwise.
Caller may free after use with OcFlexArrayFree
if required.
@param[in] IsUnicode Are option names and values Unicode or ASCII?
@retval EFI_SUCCESS Success.
@retval EFI_NOT_FOUND Missing or empty load options.
@retval EFI_OUT_OF_RESOURCES Out of memory.
@retval EFI_INVALID_PARAMETER Invalid load options detected.
**/
EFI_STATUS
OcParseVars (
IN VOID *StrVars,
OUT OC_FLEX_ARRAY **ParsedVars,
IN CONST BOOLEAN IsUnicode
);
/**
Get string value of parsed var or load option.
Returned value is in same format as raw options.
Return value points directly into original raw option memory,
so may need to be copied if it is to be retained, and must not
be freed directly.
@param[in] ParsedVars Parsed variables.
@param[in] Name Option name.
@param[in] StrValue Option value if successful, not modified otherwise;
note that NULL is returned if option exists with no value.
Caller must not attempt to free this memory.
@param[in] IsUnicode Are option names and values Unicode or ASCII?
@retval TRUE Option exists.
@retval FALSE Option not found.
**/
BOOLEAN
OcParsedVarsGetStr (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST VOID *Name,
OUT VOID **StrValue,
IN CONST BOOLEAN IsUnicode
);
/**
Get string value of parsed var or load option.
Return value points directly into original raw option memory,
so may need to be copied if it is to be retained, and must not
be freed directly.
@param[in] ParsedVars Parsed variables.
@param[in] Name Option name.
@param[in] StrValue Option value if successful, not modified otherwise;
note that NULL is returned if option exists with no value.
Caller must not attempt to free this memory.
@retval TRUE Option exists.
@retval FALSE Option not found.
**/
BOOLEAN
OcParsedVarsGetUnicodeStr (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST CHAR16 *Name,
OUT CHAR16 **StrValue
);
/**
Get ASCII string value of parsed var or load option.
Return value points directly into original raw option memory,
so may need to be copied if it is to be retained, and must not
be freed directly.
@param[in] ParsedVars Parsed variables.
@param[in] Name Option name.
@param[in] StrValue Option value if successful, not modified otherwise;
note that NULL is returned if option exists with no value.
Caller must not attempt to free this memory.
@retval TRUE Option exists.
@retval FALSE Option not found.
**/
BOOLEAN
OcParsedVarsGetAsciiStr (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST CHAR8 *Name,
OUT CHAR8 **StrValue
);
/**
Get presence or absence of parsed shell var or load option.
@param[in] ParsedVars Parsed variables.
@param[in] Name Option name.
@param[in] IsUnicode Are option names and values Unicode or ASCII?
@retval TRUE Option exists (with or without a value).
@retval FALSE Option not found.
**/
BOOLEAN
OcHasParsedVar (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST VOID *Name,
IN CONST BOOLEAN IsUnicode
);
/**
Get integer value of parsed shell var or load option (parses hex and decimal representations).
@param[in] ParsedVars Parsed variables.
@param[in] Name Option name.
@param[in] Value Option value if successful, not modified otherwise.
@param[in] IsUnicode Are option names and values Unicode or ASCII?
@retval EFI_SUCCESS Success.
@retval EFI_NOT_FOUND Option not found, or has no value.
@retval other Error encountered when parsing option as int.
**/
EFI_STATUS
OcParsedVarsGetInt (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST VOID *Name,
OUT UINTN *Value,
IN CONST BOOLEAN IsUnicode
);
/**
Get guid value of parsed shell var or load option.
@param[in] ParsedVars Parsed variables.
@param[in] Name Option name.
@param[in] Value Option value if successful, not modified otherwise.
@param[in] IsUnicode Are option names and values Unicode or ASCII?
@retval EFI_SUCCESS Success.
@retval EFI_NOT_FOUND Option not found, or has no value.
@retval other Error encountered when parsing option as guid.
**/
EFI_STATUS
OcParsedVarsGetGuid (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST VOID *Name,
OUT EFI_GUID *Value,
IN CONST BOOLEAN IsUnicode
);
#endif // OC_BOOT_MANAGEMENT_LIB_H

View File

@ -569,10 +569,16 @@ typedef enum {
**/
///
/// Drivers is a sorted array of strings containing driver paths.
/// Drivers is an ordered array of drivers to load.
///
#define OC_UEFI_DRIVER_ENTRY_FIELDS(_, __) \
_(OC_STRING , Path , , OC_STRING_CONSTR ("", _, __) , OC_DESTR (OC_STRING) ) \
_(BOOLEAN , Enabled , , FALSE , ()) \
_(OC_STRING , Arguments , , OC_STRING_CONSTR ("", _, __) , OC_DESTR (OC_STRING) )
OC_DECLARE (OC_UEFI_DRIVER_ENTRY)
#define OC_UEFI_DRIVER_ARRAY_FIELDS(_, __) \
OC_ARRAY (OC_STRING, _, __)
OC_ARRAY (OC_UEFI_DRIVER_ENTRY, _, __)
OC_DECLARE (OC_UEFI_DRIVER_ARRAY)
///

View File

@ -116,11 +116,11 @@ OcGetVolumeLabel (
*/
EFI_STATUS
OcSafeFileOpen (
IN EFI_FILE_PROTOCOL *Protocol,
OUT EFI_FILE_PROTOCOL **NewHandle,
IN CONST CHAR16 *FileName,
IN UINT64 OpenMode,
IN UINT64 Attributes
IN CONST EFI_FILE_PROTOCOL *Protocol,
OUT EFI_FILE_PROTOCOL **NewHandle,
IN CONST CHAR16 *FileName,
IN CONST UINT64 OpenMode,
IN CONST UINT64 Attributes
);
/**
@ -137,10 +137,10 @@ OcSafeFileOpen (
**/
VOID *
OcReadFile (
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem,
IN CONST CHAR16 *FilePath,
OUT UINT32 *FileSize OPTIONAL,
IN UINT32 MaxFileSize OPTIONAL
IN CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem,
IN CONST CHAR16 *FilePath,
OUT UINT32 *FileSize OPTIONAL,
IN CONST UINT32 MaxFileSize OPTIONAL
);
/**
@ -148,19 +148,19 @@ OcReadFile (
Null termination does not affect the returned file size.
Depending on the implementation 0 byte files may return null.
@param[in] RootFile A pointer to the file protocol of the directory.
@param[in] FilePath The full path to the file on the device.
@param[out] FileSize The size of the file read (optional).
@param[in] MaxFileSize Upper file size bound (optional).
@param[in] RootDirectory A pointer to the file protocol of the directory.
@param[in] FilePath The full path to the file on the device.
@param[out] FileSize The size of the file read (optional).
@param[in] MaxFileSize Upper file size bound (optional).
@retval A pointer to a buffer containing file read or NULL.
**/
VOID *
OcReadFileFromFile (
IN EFI_FILE_PROTOCOL *RootFile,
IN CONST CHAR16 *FilePath,
OUT UINT32 *FileSize OPTIONAL,
IN UINT32 MaxFileSize OPTIONAL
OcReadFileFromDirectory (
IN CONST EFI_FILE_PROTOCOL *RootDirectory,
IN CONST CHAR16 *FilePath,
OUT UINT32 *FileSize OPTIONAL,
IN UINT32 MaxFileSize OPTIONAL
);
/**
@ -260,6 +260,68 @@ OcGetNewestFileFromDirectory (
OUT EFI_FILE_INFO **FileInfo
);
/**
Ensure specified file is directory or file as specified by IsDirectory.
@param[in] File The file to check.
@param[in] IsDirectory Require that file is directory.
@retval EFI_SUCCESS File is directory/file as specified.
@retval EFI_INVALID_PARAMETER File is not directory/file as specified.
**/
EFI_STATUS
OcEnsureDirectory (
IN EFI_FILE_PROTOCOL *File,
IN BOOLEAN IsDirectory
);
/**
Process directory item.
NB Successful processing must return EFI_SUCCESS or EFI_NOT_FOUND, or further
processing will be aborted.
Return EFI_NOT_FOUND to continue processing but act if no file found.
@param[in] Directory Parent directory file handle.
@param[in] FileInfo EFI_FILE_INFO allocated from pool memory,
will be freed after this call,
data to preserve must be copied.
@param[in] FileInfoSize FileInfoSize.
@param[in,out] Context Optional application-specific context.
@retval EFI_SUCCESS File found and successfully processed.
@retval EFI_NOT_FOUND (Act as if) no matching file was found.
@retval other Error processing file (aborts directory scan).
**/
typedef
EFI_STATUS
(*OC_PROCESS_DIRECTORY_ENTRY) (
EFI_FILE_HANDLE Directory,
EFI_FILE_INFO *FileInfo,
UINTN FileInfoSize,
VOID *Context OPTIONAL
);
/**
Scan directory, calling specified procedure for each directory entry.
@param[in] Directory The directory to scan.
@param[in] ProcessEntry Process entry, called for each directory entry matching filter.
@param[in,out] Context Optional application-specific context.
@retval EFI_NOT_FOUND Successful processing, no entries matching filter were found.
@retval EFI_SUCCESS Successful processing, at least one entry matching filter was found.
@retval EFI_OUT_OF_RESOURCES Out of memory.
@retval other Other error returned by file system or ProcessEntry during processing
**/
EFI_STATUS
OcScanDirectory (
IN EFI_FILE_HANDLE Directory,
IN OC_PROCESS_DIRECTORY_ENTRY ProcessEntry,
IN OUT VOID *Context OPTIONAL
);
/**
Get file information of specified type.

View File

@ -0,0 +1,263 @@
/** @file
Copyright (C) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#ifndef OC_FLEX_ARRAY_LIB_H
#define OC_FLEX_ARRAY_LIB_H
#include <Uefi.h>
#include <Library/OcStringLib.h>
#include <Protocol/ApplePlatformInfoDatabase.h>
/*
Forward declaration of OC_FLEX_ARRAY structure.
*/
typedef struct OC_FLEX_ARRAY_ OC_FLEX_ARRAY;
/**
Free any allocated memory pointed to by flex array item.
@param[in] Item Item to free.
**/
typedef
VOID
(* OC_FLEX_ARRAY_FREE_ITEM) (
IN VOID *Item
);
/**
Utility method to free memory pointed to by flex array
item when the item is a single pointer, or when the only
allocated memory is pointed to by a pointer which is the
first element.
@param[in] Item Flex array item to free.
**/
VOID
OcFlexArrayFreePointerItem (
IN VOID *Item
);
/**
Initialize flex array.
@param[in] ItemSize Size of each item in the array.
@param[in] FreeItem Method to free one item, called once per item by
OcFlexArrayFree; may be NULL if there is nothing
pointed to by pool items which needs freeing.
@retval Non-null Flex array was created.
@retval NULL Out of memory.
**/
OC_FLEX_ARRAY *
OcFlexArrayInit (
IN CONST UINTN ItemSize,
IN CONST OC_FLEX_ARRAY_FREE_ITEM FreeItem OPTIONAL
);
/**
Add new item to flex array, resizing if necessary. New item memory is zeroed.
@param[in,out] FlexArray Flex array to modify.
@retval Non-null Address of item created.
@retval NULL Out of memory, in which case FlexArray->Items will be set
to NULL, but FlexArray itself will still be allocated.
**/
VOID *
OcFlexArrayAddItem (
IN OUT OC_FLEX_ARRAY *FlexArray
);
/**
Insert new item at position in flex array, resizing array if necessary. New item memory is zeroed.
@param[in,out] FlexArray Flex array to modify.
@param[in] InsertIndex Index at which to insert; must be less than or equal to current item count.
@retval Non-null Address of item created.
@retval NULL Out of memory, in which case FlexArray->Items will be set
to NULL, but FlexArray itself will still be allocated.
**/
VOID *
OcFlexArrayInsertItem (
IN OUT OC_FLEX_ARRAY *FlexArray,
IN CONST UINTN InsertIndex
);
/**
Return item at index in array.
@param[in,out] FlexArray Flex array to access.
@param[in,out] Index Index of item to return.
@retval Non-null Item.
@retval NULL Out-of-range item requested. This also ASSERTs.
**/
VOID *
OcFlexArrayItemAt (
IN CONST OC_FLEX_ARRAY *FlexArray,
IN CONST UINTN Index
);
/**
Free flex array.
@param[in,out] FlexArray Flex array to free.
**/
VOID
OcFlexArrayFree (
IN OUT OC_FLEX_ARRAY **FlexArray
);
/**
Free dynamic container object, but do not free and
pass back out allocated items with item count.
@param[in,out] FlexArray Flex array to free.
@param[in,out] Items Pool allocated flex array item list.
@param[in,out] Count Item list count.
**/
VOID
OcFlexArrayFreeContainer (
IN OUT OC_FLEX_ARRAY **FlexArray,
IN OUT VOID **Items,
IN OUT UINTN *Count
);
/*
Flex array.
*/
struct OC_FLEX_ARRAY_ {
//
// Allocated array.
//
VOID *Items;
//
// Item size.
//
UINTN ItemSize;
//
// Current used count.
//
UINTN Count;
//
// Current allocated count.
//
UINTN AllocatedCount;
//
// Optional method to free memory pointed to from item.
//
OC_FLEX_ARRAY_FREE_ITEM FreeItem;
};
/*
Forward declaration of OC_STRING_BUFFER structure.
*/
typedef struct OC_STRING_BUFFER_ OC_STRING_BUFFER;
/**
Initialize string buffer.
@retval Non-null Buffer was created.
@retval NULL Out of memory.
**/
OC_STRING_BUFFER *
OcAsciiStringBufferInit (
VOID
);
/**
Append new string to buffer, resizing if necessary.
@param[in,out] StringBuffer Buffer to modify.
@param[in] AppendString String to append. If NULL, nothing will be modified.
@retval EFI_SUCCESS String was appended.
@retval EFI_OUT_OF_RESOURCES Out of memory.
@retval EFI_UNSUPPORTED Internal error.
**/
EFI_STATUS
OcAsciiStringBufferAppend (
IN OUT OC_STRING_BUFFER *Buffer,
IN CONST CHAR8 *AppendString OPTIONAL
);
/**
Append new substring to buffer, resizing if necessary.
@param[in,out] StringBuffer Buffer to modify.
@param[in] AppendString String to append. If NULL, nothing will be modified.
@param[in] Length Maxiumum length of substring to use. 0 means use 0 characters.
Use MAX_UINTN for all chars. Ignored if AppendString is NULL.
@retval EFI_SUCCESS String was appended.
@retval EFI_OUT_OF_RESOURCES Out of memory.
@retval EFI_UNSUPPORTED Internal error.
**/
EFI_STATUS
OcAsciiStringBufferAppendN (
IN OUT OC_STRING_BUFFER *Buffer,
IN CONST CHAR8 *AppendString, OPTIONAL
IN CONST UINTN Length
);
/**
Safely print to string buffer.
@param[in,out] StringBuffer Buffer to modify.
@param[in] FormatString A Null-terminated ASCII format string.
@param[in] ... Variable argument list whose contents are accessed based on the
format string specified by FormatString.
@retval EFI_SUCCESS When data was printed to string buffer.
@retval EFI_OUT_OF_RESOURCES Out of memory increasing string buffer sufficiently to print to.
**/
EFI_STATUS
EFIAPI
OcAsciiStringBufferSPrint (
IN OUT OC_STRING_BUFFER *Buffer,
IN CONST CHAR8 *FormatString,
...
);
/**
Free string buffer memory and return pointer to pool allocated resultant string.
Note that if no data was ever appended to the string then the return value
will be NULL, not a zero length string.
@param[in,out] StringBuffer StringBuffer to free.
@retval Non-NULL Pointer to pool allocated accumulated string.
@retval NULL No data was ever added to the string.
**/
CHAR8 *
OcAsciiStringBufferFreeContainer (
IN OUT OC_STRING_BUFFER **StringBuffer
);
/**
Free string buffer memory; free and discard any allocated resultant string.
@param[in,out] StringBuffer StringBuffer to free.
@retval Non-NULL Pointer to pool allocated accumulated string.
@retval NULL No data was ever added to the string.
**/
VOID
OcAsciiStringBufferFree (
IN OUT OC_STRING_BUFFER **StringBuffer
);
/*
String buffer.
*/
struct OC_STRING_BUFFER_ {
CHAR8 *String;
UINTN StringLength;
UINTN BufferSize;
};
#endif // OC_FLEX_ARRAY_LIB_H

View File

@ -29,6 +29,11 @@
**/
#define SECONDS_TO_MICROSECONDS(x) ((x)*1000000)
/**
Character length of EFI_GUID string representation.
**/
#define OC_EFI_GUID_STR_LEN (sizeof (EFI_GUID) * 2 + 4)
BOOLEAN
FindPattern (
IN CONST UINT8 *Pattern,

View File

@ -434,6 +434,19 @@ OcAsciiPrintBuffer (
...
);
/**
Convert a null-terminated ASCII string, in-place, to all lowercase.
Then return it.
@param Str The null-terminated string to be converted to all lowercase.
@return The null-terminated string converted into all lowercase.
**/
CHAR8 *
OcAsciiToLower (
CHAR8 *Str
);
/**
Returns the first occurrence of a Null-terminated Unicode sub-string
in a Null-terminated Unicode string through a case insensitive comparison.
@ -682,4 +695,33 @@ MixedStrCmp (
IN CONST CHAR8 *SecondString
);
/**
Function to reverse sort when comparing by Unicode strings using UefiSortLib PerformQuickSort.
@param[in] Buffer1 The pointer to String to compare (CHAR16**).
@param[in] Buffer2 The pointer to second String to compare (CHAR16**).
@retval 0 Buffer1 equal to Buffer2.
@return < 0 Buffer1 is less than Buffer2.
@return > 0 Buffer1 is greater than Buffer2.
**/
INTN
EFIAPI
OcReverseStringCompare (
IN CONST VOID *Buffer1,
IN CONST VOID *Buffer2
);
/**
Determine if a particular character is whitespace.
@param[in] Ch The character to check.
@return Returns TRUE if Ch is a whitespace character.
**/
BOOLEAN
OcIsSpace (
CHAR16 Ch
);
#endif // OC_STRING_LIB_H

View File

@ -0,0 +1,99 @@
/** @file
OpenCore Boot Entry Protocol.
Copyright (C) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#ifndef OC_BOOT_ENTRY_PROTOCOL_H
#define OC_BOOT_ENTRY_PROTOCOL_H
#include <Uefi.h>
#include <Library/OcBootManagementLib.h>
#include <Protocol/SimpleFileSystem.h>
/**
8604716E-ADD4-45B4-8495-08E36D497F4F
**/
#define OC_BOOT_ENTRY_PROTOCOL_GUID \
{ \
0x8604716E, 0xADD4, 0x45B4, { 0x84, 0x95, 0x08, 0xE3, 0x6D, 0x49, 0x7F, 0x4F } \
}
/**
Currently supported OC_BOOT_ENTRY_PROTOCOL protocol revision.
Needs to be changed every time the contract changes, including when
passed-in structures OC_PICKER_ENTRY and OC_PICKER_ENTRY change.
WARNING: This protocol is currently undergoing active design.
**/
#define OC_BOOT_ENTRY_PROTOCOL_REVISION 1
/**
Forward declaration of OC_BOOT_ENTRY_PROTOCOL structure.
**/
typedef struct OC_BOOT_ENTRY_PROTOCOL_ OC_BOOT_ENTRY_PROTOCOL;
/**
Return list of OpenCore boot entries associated with filesystem.
@param[in] PickerContext Picker context.
@param[in] Device The handle of the device to scan. NULL is passed in to
request custom entries. All implementations must support a
NULL input value, but may immediately return EFI_NOT_FOUND
if they do not provide any custom entries.
@param[out] BootEntries List of boot entries associated with the filesystem.
On EFI_SUCCESS BootEntries must be freed by the caller
with FreePool after use, and each individual boot entry
should eventually be freed by FreeBootEntry (as for boot
entries created within OC itself).
Does not point to allocated memory on return, if any status
other than EFI_SUCCESS was returned.
@param[out] NumEntries The number of entries returned in the BootEntries list.
If any status other than EFI_SUCCESS was returned, this
value may not have been initialised and should be ignored
by the caller.
@retval EFI_SUCCESS At least one matching entry was found, and the list and
count of boot entries has been returned.
@retval EFI_NOT_FOUND No matching boot entries were found.
@retval EFI_OUT_OF_RESOURCES Memory allocation failure.
@retval other An error returned by a sub-operation.
**/
typedef
EFI_STATUS
(EFIAPI *OC_GET_BOOT_ENTRIES) (
IN OC_PICKER_CONTEXT *PickerContext,
IN CONST EFI_HANDLE Device,
OUT OC_PICKER_ENTRY **Entries,
OUT UINTN *NumEntries
);
/**
Free list of OpenCore boot entries from previous call to OC_GET_BOOT_ENTRIES.
@param[in] Entries List of boot entries, as returned by previous call.
Correct implementation of interface should additionally
zero this pointer before returning.
@param[in] NumEntries The number of entries, as returned by previous call.
**/
typedef
VOID
(EFIAPI *OC_FREE_BOOT_ENTRIES) (
IN OC_PICKER_ENTRY **Entries,
IN UINTN NumEntries
);
/**
The structure exposed by the OC_BOOT_ENTRY_PROTOCOL.
**/
struct OC_BOOT_ENTRY_PROTOCOL_ {
UINTN Revision;
OC_GET_BOOT_ENTRIES GetBootEntries;
OC_FREE_BOOT_ENTRIES FreeBootEntries;
};
extern EFI_GUID gOcBootEntryProtocolGuid;
#endif // OC_BOOT_ENTRY_PROTOCOL_H

View File

@ -1,15 +1,6 @@
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Copyright (C) 2019-2021, vit9696, mikebeaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Guid/AppleVariable.h>
@ -18,13 +9,37 @@
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcBootManagementLib.h>
#include <Library/OcMiscLib.h>
#include <Library/OcFlexArrayLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include "BootManagementInternal.h"
/*
Shell var and load options processing states.
*/
typedef enum PARSE_VARS_STATE_ {
PARSE_VARS_WHITE_SPACE,
PARSE_VARS_COMMENT,
PARSE_VARS_NAME,
PARSE_VARS_VALUE_FIRST,
PARSE_VARS_VALUE,
PARSE_VARS_QUOTED_VALUE,
PARSE_VARS_SHELL_EXPANSION
} PARSE_VARS_STATE;
//
// Shift from token start to current position forwards by offset characters.
//
#define SHIFT_TOKEN(pos, token, offset) { \
CopyMem ((UINT8 *)(token) + (offset), (token), (UINT8 *)(pos) - (UINT8 *)(token)); \
(token) = (UINT8 *)(token) + (offset); \
(pos) = (UINT8 *)(token) + (offset); \
}
VOID
OcParseBootArgs (
OUT OC_BOOT_ARGUMENTS *Arguments,
@ -360,3 +375,348 @@ OcCheckArgumentFromEnv (
return HasArgument;
}
EFI_STATUS
OcParseLoadOptions (
IN CONST EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
OUT OC_FLEX_ARRAY **ParsedVars
)
{
EFI_STATUS Status;
ASSERT (LoadedImage != NULL);
ASSERT (ParsedVars != NULL);
*ParsedVars = NULL;
if (LoadedImage->LoadOptionsSize % sizeof (CHAR16) != 0 || LoadedImage->LoadOptionsSize > MAX_LOAD_OPTIONS_SIZE) {
DEBUG ((DEBUG_ERROR, "OCB: Invalid LoadOptions (%p:%u)\n", LoadedImage->LoadOptions, LoadedImage->LoadOptionsSize));
return EFI_INVALID_PARAMETER;
}
if (LoadedImage->LoadOptions == NULL ||
LoadedImage->LoadOptionsSize == 0 ||
((CHAR16 *)LoadedImage->LoadOptions)[0] == CHAR_NULL) {
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: No LoadOptions (%p:%u)\n", LoadedImage->LoadOptions, LoadedImage->LoadOptionsSize));
return EFI_NOT_FOUND;
}
Status = OcParseVars (LoadedImage->LoadOptions, ParsedVars, TRUE);
if (Status == EFI_INVALID_PARAMETER) {
DEBUG ((DEBUG_ERROR, "OCB: Invalid LoadOptions (%p:%u)\n", LoadedImage->LoadOptions, LoadedImage->LoadOptionsSize));
} else if (Status == EFI_NOT_FOUND) {
DEBUG ((DEBUG_WARN, "OCB: Empty LoadOptions\n"));
}
return Status;
}
EFI_STATUS
OcParseVars (
IN VOID *StrVars,
OUT OC_FLEX_ARRAY **ParsedVars,
IN CONST BOOLEAN IsUnicode
)
{
VOID *Pos;
PARSE_VARS_STATE State;
PARSE_VARS_STATE PushState;
BOOLEAN Retake;
CHAR16 Ch;
VOID *Name;
VOID *Value;
OC_PARSED_VAR *Option;
if (StrVars == NULL || (IsUnicode ? ((CHAR16 *) StrVars)[0] == CHAR_NULL : ((CHAR8 *) StrVars)[0] == '\0')) {
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: No vars (%p)\n", StrVars));
return EFI_NOT_FOUND;
}
*ParsedVars = OcFlexArrayInit (sizeof (OC_PARSED_VAR), NULL);
if (*ParsedVars == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Pos = StrVars;
State = PARSE_VARS_WHITE_SPACE;
PushState = PARSE_VARS_WHITE_SPACE;
Retake = FALSE;
do {
Ch = IsUnicode ? *((CHAR16 *) Pos) : *((CHAR8 *) Pos);
switch (State) {
case PARSE_VARS_WHITE_SPACE:
if (Ch == '#') {
State = PARSE_VARS_COMMENT;
} else if (!(OcIsSpace (Ch) || Ch == CHAR_NULL)) {
Name = Pos;
State = PARSE_VARS_NAME;
}
break;
case PARSE_VARS_COMMENT:
if (Ch == '\n') {
State = PARSE_VARS_WHITE_SPACE;
}
break;
case PARSE_VARS_NAME:
if (Ch == L'=' || OcIsSpace (Ch) || Ch == CHAR_NULL) {
if (IsUnicode) {
*((CHAR16 *) Pos) = CHAR_NULL;
} else {
*((CHAR8 *) Pos) = '\0';
}
if (Ch == L'=') {
State = PARSE_VARS_VALUE_FIRST;
} else {
State = PARSE_VARS_WHITE_SPACE;
}
Option = OcFlexArrayAddItem (*ParsedVars);
if (Option == NULL) {
OcFlexArrayFree (ParsedVars);
return EFI_OUT_OF_RESOURCES;
}
if (IsUnicode) {
Option->Unicode.Name = Name;
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: Name=\"%s\"\n", Name));
} else {
Option->Ascii.Name = Name;
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: Name=\"%a\"\n", Name));
}
if (State == PARSE_VARS_WHITE_SPACE) {
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: No value %u\n", 1));
}
Name = NULL;
}
break;
case PARSE_VARS_VALUE_FIRST:
if (Ch == L'"') {
State = PARSE_VARS_QUOTED_VALUE;
Value = (UINT8 *) Pos + (IsUnicode ? sizeof (CHAR16) : sizeof (CHAR8));
} else {
State = PARSE_VARS_VALUE;
Value = Pos;
Retake = TRUE;
}
break;
case PARSE_VARS_SHELL_EXPANSION:
if (Ch == '`') {
ASSERT (PushState != PARSE_VARS_WHITE_SPACE);
State = PushState;
}
break;
case PARSE_VARS_VALUE:
case PARSE_VARS_QUOTED_VALUE:
if (Ch == L'`') {
PushState = State;
State = PARSE_VARS_SHELL_EXPANSION;
} else if (Ch == L'\\') {
SHIFT_TOKEN (Pos, Value, IsUnicode ? sizeof (CHAR16) : sizeof (CHAR8));
} else if (
(State == PARSE_VARS_VALUE && (OcIsSpace (Ch) || Ch == CHAR_NULL)) ||
(State == PARSE_VARS_QUOTED_VALUE && Ch == '"')) {
//
// Explicitly quoted empty string needs to be stored detectably
// differently from missing value.
//
if (State != PARSE_VARS_QUOTED_VALUE && Pos == Value) {
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: No value %u\n", 2));
} else {
if (PushState != PARSE_VARS_WHITE_SPACE) {
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: Found shell expansion, cancelling value\n"));
PushState = PARSE_VARS_WHITE_SPACE;
} else {
if (IsUnicode) {
*((CHAR16 *) Pos) = CHAR_NULL;
Option->Unicode.Value = Value;
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: Value=\"%s\"\n", Value));
} else {
*((CHAR8 *) Pos) = '\0';
Option->Ascii.Value = Value;
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: Value=\"%a\"\n", Value));
}
}
}
Value = NULL;
Option = NULL;
State = PARSE_VARS_WHITE_SPACE;
}
break;
default:
ASSERT (FALSE);
break;
}
if (Retake) {
Retake = FALSE;
} else {
Pos = (UINT8 *) Pos + (IsUnicode ? sizeof (CHAR16) : sizeof (CHAR8));
}
} while (Ch != CHAR_NULL);
if (State != PARSE_VARS_WHITE_SPACE || PushState != PARSE_VARS_WHITE_SPACE) {
//
// E.g. for GRUB config files this may potentially be caused by a file
// neither we nor the user directly controls, so better warn than error.
//
DEBUG ((DEBUG_WARN, "OCB: Invalid vars (%u)\n", State));
OcFlexArrayFree (ParsedVars);
return EFI_INVALID_PARAMETER;
}
if ((*ParsedVars)->Items == NULL) {
OcFlexArrayFree (ParsedVars);
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
BOOLEAN
OcParsedVarsGetStr (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST VOID *Name,
OUT VOID **Value,
IN CONST BOOLEAN IsUnicode
)
{
UINTN Index;
OC_PARSED_VAR *Option;
ASSERT (Name != NULL);
ASSERT (Value != NULL);
if (ParsedVars == NULL) {
return FALSE;
}
ASSERT (ParsedVars->Items != NULL);
for (Index = 0; Index < ParsedVars->Count; ++Index) {
Option = OcFlexArrayItemAt (ParsedVars, Index);
if (IsUnicode) {
ASSERT (Option->Unicode.Name != NULL);
if (StrCmp (Option->Unicode.Name, Name) == 0) {
*Value = Option->Unicode.Value;
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: Using \"%s\"=\"%s\"\n", Name, *Value));
return TRUE;
}
} else {
ASSERT (Option->Ascii.Name != NULL);
if (AsciiStrCmp (Option->Ascii.Name, Name) == 0) {
*Value = Option->Ascii.Value;
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: Using \"%a\"=\"%a\"\n", Name, *Value));
return TRUE;
}
}
}
if (IsUnicode) {
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: No value for \"%s\"\n", Name));
} else {
DEBUG ((OC_TRACE_PARSE_VARS, "OCB: No value for \"%a\"\n", Name));
}
return FALSE;
}
BOOLEAN
OcParsedVarsGetUnicodeStr (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST CHAR16 *Name,
OUT CHAR16 **Value
)
{
return OcParsedVarsGetStr (ParsedVars, Name, (VOID**)Value, TRUE);
}
BOOLEAN
OcParsedVarsGetAsciiStr (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST CHAR8 *Name,
OUT CHAR8 **Value
)
{
return OcParsedVarsGetStr (ParsedVars, Name, (VOID**)Value, FALSE);
}
BOOLEAN
OcHasParsedVar (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST VOID *Name,
IN CONST BOOLEAN IsUnicode
)
{
VOID *Value;
return OcParsedVarsGetStr (ParsedVars, Name, &Value, IsUnicode);
}
EFI_STATUS
OcParsedVarsGetInt (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST VOID *Name,
OUT UINTN *Value,
IN CONST BOOLEAN IsUnicode
)
{
EFI_STATUS Status;
VOID *StrValue;
if (!OcParsedVarsGetStr (ParsedVars, Name, &StrValue, IsUnicode)) {
return EFI_NOT_FOUND;
}
if (StrValue == NULL) {
return EFI_NOT_FOUND;
}
if (IsUnicode){
if (OcUnicodeStartsWith (StrValue, L"0x", TRUE)) {
Status = StrHexToUintnS (StrValue, NULL, Value);
} else {
Status = StrDecimalToUintnS (StrValue, NULL, Value);
}
} else {
if (OcAsciiStartsWith (StrValue, "0x", TRUE)) {
Status = AsciiStrHexToUintnS (StrValue, NULL, Value);
} else {
Status = AsciiStrDecimalToUintnS (StrValue, NULL, Value);
}
}
return Status;
}
EFI_STATUS
OcParsedVarsGetGuid (
IN CONST OC_FLEX_ARRAY *ParsedVars,
IN CONST VOID *Name,
OUT EFI_GUID *Value,
IN CONST BOOLEAN IsUnicode
)
{
EFI_STATUS Status;
VOID *StrValue;
if (!OcParsedVarsGetStr (ParsedVars, Name, &StrValue, IsUnicode)) {
return EFI_NOT_FOUND;
}
if (StrValue == NULL) {
return EFI_NOT_FOUND;
}
if (IsUnicode) {
Status = StrToGuid (StrValue, Value);
} else {
Status = AsciiStrToGuid (StrValue, Value);
}
return Status;
}

View File

@ -1,18 +1,10 @@
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Copyright (C) 2019-2021, vit9696, mikebeaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include "BootManagementInternal.h"
#include "BootEntryProtocolInternal.h"
#include <Protocol/DevicePath.h>
#include <Protocol/SimpleFileSystem.h>
@ -22,6 +14,7 @@
#include <Guid/AppleVariable.h>
#include <Guid/FileInfo.h>
#include <Guid/GlobalVariable.h>
#include <Guid/Gpt.h>
#include <Guid/OcVariable.h>
#include <Library/BaseLib.h>
@ -136,7 +129,7 @@ ExpandShortFormBootPath (
//
// Check whether we are allowed to boot from this filesystem.
//
*FileSystem = InternalFileSystemForHandle (BootContext, FileSystemHandle, LazyScan);
*FileSystem = InternalFileSystemForHandle (BootContext, FileSystemHandle, LazyScan, NULL);
if (*FileSystem == NULL) {
continue;
}
@ -276,13 +269,14 @@ RegisterBootOption (
DEBUG ((
DEBUG_INFO,
"OCB: Registering entry %s [%a] (T:%d|F:%d|G:%d|E:%d) - %s\n",
"OCB: Registering entry %s [%a] (T:%d|F:%d|G:%d|E:%d|B:%d) - %s\n",
BootEntry->Name,
BootEntry->Flavour,
BootEntry->Type,
BootEntry->IsFolder,
BootEntry->IsGeneric,
BootEntry->IsExternal,
BootEntry->IsBootEntryProtocol,
OC_HUMAN_STRING (TextDevicePath)
));
@ -507,6 +501,51 @@ AddBootEntryOnFileSystem (
return EFI_SUCCESS;
}
/**
Release boot entry contents allocated from pool.
@param[in,out] BootEntry Located boot entry.
**/
STATIC
VOID
FreeBootEntry (
IN OC_BOOT_ENTRY *BootEntry
)
{
if (BootEntry->DevicePath != NULL) {
FreePool (BootEntry->DevicePath);
BootEntry->DevicePath = NULL;
}
if (BootEntry->Id != NULL) {
FreePool (BootEntry->Id);
BootEntry->Id = NULL;
}
if (BootEntry->Name != NULL) {
FreePool (BootEntry->Name);
BootEntry->Name = NULL;
}
if (BootEntry->PathName != NULL) {
FreePool (BootEntry->PathName);
BootEntry->PathName = NULL;
}
if (BootEntry->LoadOptions != NULL) {
FreePool (BootEntry->LoadOptions);
BootEntry->LoadOptions = NULL;
BootEntry->LoadOptionsSize = 0;
}
if (BootEntry->Flavour != NULL) {
FreePool (BootEntry->Flavour);
BootEntry->Flavour = NULL;
}
FreePool (BootEntry);
}
/**
Create bootable entry from custom entry.
@ -516,12 +555,12 @@ AddBootEntryOnFileSystem (
@retval EFI_SUCCESS on success.
**/
STATIC
EFI_STATUS
AddBootEntryFromCustomEntry (
InternalAddBootEntryFromCustomEntry (
IN OUT OC_BOOT_CONTEXT *BootContext,
IN OUT OC_BOOT_FILESYSTEM *FileSystem,
IN OC_PICKER_ENTRY *CustomEntry
IN OC_PICKER_ENTRY *CustomEntry,
IN BOOLEAN IsBootEntryProtocol
)
{
EFI_STATUS Status;
@ -532,8 +571,17 @@ AddBootEntryFromCustomEntry (
CHAR16 *BootDirectoryName;
EFI_HANDLE Device;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
CONST EFI_PARTITION_ENTRY *PartitionEntry;
if (CustomEntry->Auxiliary && BootContext->PickerContext->HideAuxiliary) {
DEBUG ((
DEBUG_INFO,
"OCB: Not adding hidden auxiliary entry %a (%a|B:%d) -> %a\n",
CustomEntry->Name,
CustomEntry->Tool ? "tool" : "os",
IsBootEntryProtocol,
CustomEntry->Path
));
return EFI_UNSUPPORTED;
}
@ -545,32 +593,38 @@ AddBootEntryFromCustomEntry (
return EFI_OUT_OF_RESOURCES;
}
if (CustomEntry->Id != NULL) {
BootEntry->Id = AsciiStrCopyToUnicode (CustomEntry->Id, 0);
if (BootEntry->Id == NULL) {
FreeBootEntry (BootEntry);
return EFI_OUT_OF_RESOURCES;
}
}
BootEntry->Name = AsciiStrCopyToUnicode (CustomEntry->Name, 0);
if (BootEntry->Name == NULL) {
FreePool (BootEntry);
FreeBootEntry (BootEntry);
return EFI_OUT_OF_RESOURCES;
}
PathName = AsciiStrCopyToUnicode (CustomEntry->Path, 0);
if (PathName == NULL) {
FreePool (BootEntry->Name);
FreePool (BootEntry);
FreeBootEntry (BootEntry);
return EFI_OUT_OF_RESOURCES;
}
BootEntry->Flavour = AllocateCopyPool (AsciiStrSize (CustomEntry->Flavour), CustomEntry->Flavour);
if (BootEntry->Flavour == NULL) {
FreePool (PathName);
FreePool (BootEntry->Name);
FreePool (BootEntry);
FreeBootEntry (BootEntry);
return EFI_OUT_OF_RESOURCES;
}
DEBUG ((
DEBUG_INFO,
"OCB: Adding custom entry %s (%a) -> %a\n",
"OCB: Adding custom entry %s (%a|B:%d) -> %a\n",
BootEntry->Name,
CustomEntry->Tool ? "tool" : "os",
IsBootEntryProtocol,
CustomEntry->Path
));
@ -581,12 +635,19 @@ AddBootEntryFromCustomEntry (
} else {
BootEntry->Type = OC_BOOT_EXTERNAL_OS;
BootEntry->DevicePath = ConvertTextToDevicePath (PathName);
//
// For boot entry protocol path is relative to device root,
// for user entry path is absolute device path.
//
if (IsBootEntryProtocol) {
UnicodeUefiSlashes (PathName);
BootEntry->DevicePath = FileDevicePath (FileSystem->Handle, PathName);
} else {
BootEntry->DevicePath = ConvertTextToDevicePath (PathName);
}
FreePool (PathName);
if (BootEntry->DevicePath == NULL) {
FreePool (BootEntry->Flavour);
FreePool (BootEntry->Name);
FreePool (BootEntry);
FreeBootEntry (BootEntry);
return EFI_OUT_OF_RESOURCES;
}
@ -598,10 +659,7 @@ AddBootEntryFromCustomEntry (
)
);
if (FilePath == NULL) {
FreePool (BootEntry->Flavour);
FreePool (BootEntry->Name);
FreePool (BootEntry->DevicePath);
FreePool (BootEntry);
FreeBootEntry (BootEntry);
return EFI_UNSUPPORTED;
}
@ -610,10 +668,7 @@ AddBootEntryFromCustomEntry (
FilePath->PathName
);
if (BootEntry->PathName == NULL) {
FreePool (BootEntry->Flavour);
FreePool (BootEntry->Name);
FreePool (BootEntry->DevicePath);
FreePool (BootEntry);
FreeBootEntry (BootEntry);
return EFI_OUT_OF_RESOURCES;
}
@ -663,6 +718,15 @@ AddBootEntryFromCustomEntry (
}
BootEntry->IsCustom = TRUE;
BootEntry->IsBootEntryProtocol = IsBootEntryProtocol;
if (IsBootEntryProtocol) {
PartitionEntry = OcGetGptPartitionEntry (FileSystem->Handle);
if (PartitionEntry == NULL) {
BootEntry->UniquePartitionGUID = gEfiPartTypeUnusedGuid;
} else {
BootEntry->UniquePartitionGUID = PartitionEntry->UniquePartitionGUID;
}
}
RegisterBootOption (
BootContext,
@ -953,7 +1017,7 @@ AddBootEntryFromBless (
// Obtain recovery file system and ensure scan policy if it was not done before.
//
if (FileSystem->RecoveryFs == NULL) {
FileSystem->RecoveryFs = InternalFileSystemForHandle (BootContext, RecoveryDeviceHandle, LazyScan);
FileSystem->RecoveryFs = InternalFileSystemForHandle (BootContext, RecoveryDeviceHandle, LazyScan, NULL);
}
//
@ -1047,9 +1111,18 @@ AddBootEntryFromSelfRecovery (
/**
Create bootable entries from boot options.
@param[in,out] BootContext Context of filesystems.
@param[in] BootOption Boot option number.
@param[in] LazyScan Lazy filesystem scanning.
@param[in,out] BootContext Context of filesystems.
@param[in] BootOption Boot option number.
@param[in] LazyScan Lazy filesystem scanning.
@param[in,out] CustomFileSystem File system on which to add user defined custom option.
If non-NULL still searching for first (normally only) OC
custom entry, either user defined or entry protocol.
@param[out] CustomIndex Index of custom user defined entry, if matched.
@param[in] EntryProtocolHandles Installed Boot Entry Protocol handles.
@param[in] EntryProtocolHandleCount Installed Boot Entry Protocol handle count.
@param[out] EntryProtocolPartuuid Unique partition UUID of parition with entry protocol
custom entry, if matched.
@param[out] EntryProtocolId Id of entry protocol custom entry, if matched.
@retval EFI_SUCCESS if at least one option was added.
**/
@ -1060,7 +1133,11 @@ AddBootEntryFromBootOption (
IN UINT16 BootOption,
IN BOOLEAN LazyScan,
IN OUT OC_BOOT_FILESYSTEM *CustomFileSystem,
IN OUT UINT32 *CustomIndex
OUT UINT32 *CustomIndex, OPTIONAL
IN EFI_HANDLE *EntryProtocolHandles,
IN UINTN EntryProtocolHandleCount,
OUT EFI_GUID *EntryProtocolPartuuid, OPTIONAL
OUT CHAR16 **EntryProtocolId OPTIONAL
)
{
EFI_STATUS Status;
@ -1075,10 +1152,13 @@ AddBootEntryFromBootOption (
BOOLEAN IsRoot;
EFI_LOAD_OPTION *LoadOption;
UINTN LoadOptionSize;
LIST_ENTRY *Link;
UINT32 Index;
INTN CmpResult;
CONST OC_CUSTOM_BOOT_DEVICE_PATH *CustomDevPath;
UINT32 Index;
INTN CmpResult;
CONST EFI_PARTITION_ENTRY *PartitionEntry;
CONST OC_CUSTOM_BOOT_DEVICE_PATH *CustomDevPath;
CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *EntryProtocolDevPath;
DEBUG ((DEBUG_INFO, "OCB: Building entry from Boot%04x\n", BootOption));
@ -1186,7 +1266,7 @@ AddBootEntryFromBootOption (
// Ensure that we are allowed to boot from this filesystem.
//
if (DevicePath != NULL) {
FileSystem = InternalFileSystemForHandle (BootContext, FileSystemHandle, LazyScan);
FileSystem = InternalFileSystemForHandle (BootContext, FileSystemHandle, LazyScan, NULL);
if (FileSystem == NULL) {
DevicePath = NULL;
}
@ -1247,28 +1327,87 @@ AddBootEntryFromBootOption (
NULL
);
} while (NumPatchedNodes > 0);
//
// If requested, pre-construct a custom entry found in BOOT#### so it can be
// set as default.
//
if (ExpandedDevicePath == NULL && CustomFileSystem != NULL) {
ASSERT (CustomIndex != NULL);
//
// If non-standard device path, attempt to pre-construct a user config
// custom entry found in BOOT#### so it can be set as default.
//
ASSERT (CustomIndex == NULL || *CustomIndex == MAX_UINT32);
CustomDevPath = InternalGetOcCustomDevPath (DevicePath);
for (Index = 0; Index < BootContext->PickerContext->AllCustomEntryCount; ++Index) {
CmpResult = MixedStrCmp (
CustomDevPath->EntryName.PathName,
BootContext->PickerContext->CustomEntries[Index].Name
);
if (CmpResult == 0) {
*CustomIndex = Index;
AddBootEntryFromCustomEntry (
BootContext,
CustomFileSystem,
&BootContext->PickerContext->CustomEntries[Index]
if (CustomDevPath != NULL) {
for (Index = 0; Index < BootContext->PickerContext->AllCustomEntryCount; ++Index) {
CmpResult = MixedStrCmp (
CustomDevPath->EntryName.PathName,
BootContext->PickerContext->CustomEntries[Index].Name
);
break;
if (CmpResult == 0) {
if (CustomIndex != NULL) {
*CustomIndex = Index;
}
InternalAddBootEntryFromCustomEntry (
BootContext,
CustomFileSystem,
&BootContext->PickerContext->CustomEntries[Index],
FALSE
);
break;
}
}
} else {
//
// If still unknown device path, attempt to pre-construct an entry protocol
// entry found in BOOT#### so it can be set as default.
//
ASSERT (EntryProtocolId == NULL || *EntryProtocolId == NULL);
ASSERT ((EntryProtocolPartuuid == NULL) == (EntryProtocolId == NULL));
EntryProtocolDevPath = InternalGetOcEntryProtocolDevPath (DevicePath);
if (EntryProtocolDevPath != NULL) {
for (
Link = GetFirstNode (&BootContext->FileSystems);
!IsNull (&BootContext->FileSystems, Link);
Link = GetNextNode (&BootContext->FileSystems, Link)) {
FileSystem = BASE_CR (Link, OC_BOOT_FILESYSTEM, Link);
//
// Search for ID on matching device only.
// Note that on, e.g., OVMF, every single device has partition UUID 00000000-0000-0000-0000000000000000,
// therefore the first matching entry protocol ID on *any* filesystem will match.
//
PartitionEntry = OcGetGptPartitionEntry (FileSystem->Handle);
if (PartitionEntry == NULL) {
continue;
}
if (CompareMem (
&PartitionEntry->UniquePartitionGUID,
&EntryProtocolDevPath->Partuuid,
sizeof (EFI_GUID)) == 0) {
Status = AddEntriesFromBootEntryProtocol (
BootContext,
FileSystem,
EntryProtocolHandles,
EntryProtocolHandleCount,
EntryProtocolDevPath->EntryName.PathName,
TRUE
);
if (!EFI_ERROR (Status)) {
if (EntryProtocolPartuuid != NULL) {
*EntryProtocolPartuuid = PartitionEntry->UniquePartitionGUID;
}
if (EntryProtocolId != NULL) {
*EntryProtocolId = AllocateCopyPool (StrSize (EntryProtocolDevPath->EntryName.PathName), EntryProtocolDevPath->EntryName.PathName);
//
// If NULL allocated, just continue as if we had not matched.
//
}
break;
}
}
}
}
}
}
@ -1366,46 +1505,6 @@ AddBootEntryFromBootOption (
return Status;
}
/**
Release boot entry contents allocated from pool.
@param[in,out] BootEntry Located boot entry.
**/
STATIC
VOID
FreeBootEntry (
IN OC_BOOT_ENTRY *BootEntry
)
{
if (BootEntry->DevicePath != NULL) {
FreePool (BootEntry->DevicePath);
BootEntry->DevicePath = NULL;
}
if (BootEntry->Name != NULL) {
FreePool (BootEntry->Name);
BootEntry->Name = NULL;
}
if (BootEntry->PathName != NULL) {
FreePool (BootEntry->PathName);
BootEntry->PathName = NULL;
}
if (BootEntry->LoadOptions != NULL) {
FreePool (BootEntry->LoadOptions);
BootEntry->LoadOptions = NULL;
BootEntry->LoadOptionsSize = 0;
}
if (BootEntry->Flavour != NULL) {
FreePool (BootEntry->Flavour);
BootEntry->Flavour = NULL;
}
FreePool (BootEntry);
}
/**
Allocate a new filesystem entry in boot entries
in case it can be used according to current ScanPolicy.
@ -1549,10 +1648,11 @@ AddFileSystemEntryForCustom (
continue;
}
Status = AddBootEntryFromCustomEntry (
Status = InternalAddBootEntryFromCustomEntry (
BootContext,
FileSystem,
&BootContext->PickerContext->CustomEntries[Index]
&BootContext->PickerContext->CustomEntries[Index],
FALSE
);
if (!EFI_ERROR (Status)) {
@ -1621,14 +1721,19 @@ FreeFileSystemEntry (
OC_BOOT_FILESYSTEM *
InternalFileSystemForHandle (
IN OC_BOOT_CONTEXT *BootContext,
IN EFI_HANDLE FileSystemHandle,
IN BOOLEAN LazyScan
IN OC_BOOT_CONTEXT *BootContext,
IN EFI_HANDLE FileSystemHandle,
IN BOOLEAN LazyScan,
OUT BOOLEAN *AlreadySeen OPTIONAL
)
{
EFI_STATUS Status;
LIST_ENTRY *Link;
OC_BOOT_FILESYSTEM *FileSystem;
OC_BOOT_FILESYSTEM *FileSystem;
if (AlreadySeen != NULL) {
*AlreadySeen = FALSE;
}
for (
Link = GetFirstNode (&BootContext->FileSystems);
@ -1638,6 +1743,9 @@ InternalFileSystemForHandle (
if (FileSystem->Handle == FileSystemHandle) {
DEBUG ((DEBUG_INFO, "OCB: Matched fs %p%a\n", FileSystemHandle, LazyScan ? " (lazy)" : ""));
if (AlreadySeen != NULL) {
*AlreadySeen = TRUE;
}
return FileSystem;
}
}
@ -1824,6 +1932,11 @@ OcScanForBootEntries (
OC_BOOT_FILESYSTEM *CustomFileSystem;
OC_BOOT_FILESYSTEM *CustomFileSystemDefault;
UINT32 DefaultCustomIndex;
EFI_GUID DefaultEntryProtocolPartuuid;
CHAR16 *DefaultEntryProtocolId;
EFI_HANDLE *EntryProtocolHandles;
UINTN EntryProtocolHandleCount;
CONST EFI_PARTITION_ENTRY *PartitionEntry;
//
// Obtain the list of filesystems filtered by scan policy.
@ -1838,6 +1951,11 @@ OcScanForBootEntries (
DEBUG ((DEBUG_INFO, "OCB: Found %u potentially bootable filesystems\n", (UINT32) BootContext->FileSystemCount));
//
// Locate loaded boot entry protocol drivers.
//
LocateBootEntryProtocolHandles (&EntryProtocolHandles, &EntryProtocolHandleCount);
//
// Create primary boot options from BootOrder.
//
@ -1855,7 +1973,8 @@ OcScanForBootEntries (
// Delay CustomFileSystem insertion to have custom entries at the end.
//
DefaultCustomIndex = MAX_UINT32;
DefaultCustomIndex = MAX_UINT32;
DefaultEntryProtocolId = NULL;
if (Context->BootOrder != NULL) {
CustomFileSystemDefault = CustomFileSystem;
@ -1866,14 +1985,18 @@ OcScanForBootEntries (
Context->BootOrder[Index],
FALSE,
CustomFileSystemDefault,
&DefaultCustomIndex
&DefaultCustomIndex,
EntryProtocolHandles,
EntryProtocolHandleCount,
&DefaultEntryProtocolPartuuid,
&DefaultEntryProtocolId
);
//
// Pre-create at most one custom entry. Under normal circumstances, no
// more than one entry should exist anyway.
//
if (DefaultCustomIndex != MAX_UINT32) {
if (DefaultCustomIndex != MAX_UINT32 || DefaultEntryProtocolId != NULL) {
CustomFileSystemDefault = NULL;
}
}
@ -1905,12 +2028,37 @@ OcScanForBootEntries (
);
}
//
// Try boot entry protocol.
// Entry protocol entries should almost certainly be added regardless
// of bless; e.g. user might well have /loader/entries in ESP, in addition
// to normal blessed files.
//
PartitionEntry = OcGetGptPartitionEntry (FileSystem->Handle);
if (PartitionEntry != NULL) {
AddEntriesFromBootEntryProtocol (
BootContext,
FileSystem,
EntryProtocolHandles,
EntryProtocolHandleCount,
CompareMem (&DefaultEntryProtocolPartuuid, &PartitionEntry->UniquePartitionGUID, sizeof (EFI_GUID)) == 0 ?
DefaultEntryProtocolId :
NULL,
FALSE
);
}
//
// Record predefined recoveries.
//
AddBootEntryFromSelfRecovery (BootContext, FileSystem);
}
if (DefaultEntryProtocolId != NULL) {
FreePool (DefaultEntryProtocolId);
DefaultEntryProtocolId = NULL;
}
if (CustomFileSystem != NULL) {
//
// Insert the custom file system last for entry order.
@ -1922,8 +2070,22 @@ OcScanForBootEntries (
// Build custom and system options.
//
AddFileSystemEntryForCustom (BootContext, CustomFileSystem, DefaultCustomIndex);
//
// Boot entry protocol also supports custom and system entries.
//
AddEntriesFromBootEntryProtocol (
BootContext,
CustomFileSystem,
EntryProtocolHandles,
EntryProtocolHandleCount,
NULL,
FALSE
);
}
FreeBootEntryProtocolHandles (&EntryProtocolHandles);
if (BootContext->BootEntryCount == 0) {
OcFreeBootContext (BootContext);
return NULL;
@ -1947,11 +2109,13 @@ OcScanForDefaultBootEntry (
OC_BOOT_CONTEXT *BootContext;
UINTN Index;
OC_BOOT_FILESYSTEM *FileSystem;
BOOLEAN AlreadySeen;
EFI_STATUS Status;
UINTN NoHandles;
EFI_HANDLE *Handles;
UINT32 DefaultCustomIndex;
OC_BOOT_FILESYSTEM *CustomFileSystem;
EFI_HANDLE *EntryProtocolHandles;
UINTN EntryProtocolHandleCount;
//
// Obtain empty list of filesystems.
@ -1963,6 +2127,11 @@ OcScanForDefaultBootEntry (
DEBUG ((DEBUG_INFO, "OCB: Looking up for default entry\n"));
//
// Locate loaded boot entry protocol drivers.
//
LocateBootEntryProtocolHandles (&EntryProtocolHandles, &EntryProtocolHandleCount);
//
// Create primary boot options from BootOrder.
//
@ -1986,20 +2155,26 @@ OcScanForDefaultBootEntry (
if (Context->BootOrder != NULL) {
for (Index = 0; Index < Context->BootOrderCount; ++Index) {
//
// DefaultCustomIndex is not used as the entry list will never be shown.
// Returned default entry values not required, as no other
// entries will be created after a match here.
//
AddBootEntryFromBootOption (
BootContext,
Context->BootOrder[Index],
TRUE,
CustomFileSystem,
&DefaultCustomIndex
NULL,
EntryProtocolHandles,
EntryProtocolHandleCount,
NULL,
NULL
);
//
// Return as long as we are good.
//
if (BootContext->DefaultEntry != NULL) {
FreeBootEntryProtocolHandles (&EntryProtocolHandles);
return BootContext;
}
}
@ -2022,36 +2197,50 @@ OcScanForDefaultBootEntry (
if (!EFI_ERROR (Status)) {
for (Index = 0; Index < NoHandles; ++Index) {
//
// Do not add filesystems twice.
// If file system has been seen during BOOT#### entry processing then
// bless has already been processed (and failed or we would not be here).
//
if (InternalFileSystemForHandle (BootContext, Handles[Index], FALSE) != NULL) {
FileSystem = InternalFileSystemForHandle (BootContext, Handles[Index], TRUE, &AlreadySeen);
if (FileSystem == NULL) {
continue;
}
Status = AddFileSystemEntry (
BootContext,
Handles[Index],
&FileSystem
);
if (EFI_ERROR (Status)) {
continue;
if (!AlreadySeen) {
AddBootEntryFromBless (
BootContext,
FileSystem,
gAppleBootPolicyPredefinedPaths,
gAppleBootPolicyNumPredefinedPaths,
FALSE,
FALSE
);
if (BootContext->DefaultEntry != NULL) {
FreeBootEntryProtocolHandles (&EntryProtocolHandles);
FreePool (Handles);
return BootContext;
}
}
AddBootEntryFromBless (
//
// Try boot entry protocol. No need to deduplicate as won't reach
// here if default entry from BOOT#### was successfully created.
//
AddEntriesFromBootEntryProtocol (
BootContext,
FileSystem,
gAppleBootPolicyPredefinedPaths,
gAppleBootPolicyNumPredefinedPaths,
FALSE,
EntryProtocolHandles,
EntryProtocolHandleCount,
NULL,
FALSE
);
if (BootContext->DefaultEntry != NULL) {
FreeBootEntryProtocolHandles (&EntryProtocolHandles);
FreePool (Handles);
return BootContext;
}
AddBootEntryFromSelfRecovery (BootContext, FileSystem);
if (BootContext->DefaultEntry != NULL) {
FreeBootEntryProtocolHandles (&EntryProtocolHandles);
FreePool (Handles);
return BootContext;
}
@ -2066,6 +2255,24 @@ OcScanForDefaultBootEntry (
// as the list is never shown.
//
AddFileSystemEntryForCustom (BootContext, CustomFileSystem, MAX_UINT32);
if (BootContext->DefaultEntry != NULL) {
FreeBootEntryProtocolHandles (&EntryProtocolHandles);
return BootContext;
}
//
// Boot entry protocol for custom and system entries.
//
AddEntriesFromBootEntryProtocol (
BootContext,
CustomFileSystem,
EntryProtocolHandles,
EntryProtocolHandleCount,
NULL,
FALSE
);
FreeBootEntryProtocolHandles (&EntryProtocolHandles);
}
if (BootContext->DefaultEntry == NULL) {

View File

@ -0,0 +1,199 @@
/** @file
Boot Entry Protocol.
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include "BootEntryProtocolInternal.h"
#include "BootManagementInternal.h"
#include <Protocol/OcBootEntry.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcFileLib.h>
#include <Library/OcDebugLogLib.h>
VOID
LocateBootEntryProtocolHandles (
IN OUT EFI_HANDLE **EntryProtocolHandles,
IN OUT UINTN *EntryProtocolHandleCount
)
{
EFI_STATUS Status;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gOcBootEntryProtocolGuid,
NULL,
EntryProtocolHandleCount,
EntryProtocolHandles
);
if (EFI_ERROR (Status)) {
//
// No loaded drivers is fine
//
if (Status != EFI_NOT_FOUND) {
DEBUG ((DEBUG_ERROR, "BEP: Error locating driver handles - %r\n", Status));
}
*EntryProtocolHandleCount = 0;
*EntryProtocolHandles = NULL;
}
}
VOID
FreeBootEntryProtocolHandles (
EFI_HANDLE **EntryProtocolHandles
)
{
if (*EntryProtocolHandles == NULL) {
return;
}
FreePool (*EntryProtocolHandles);
*EntryProtocolHandles = NULL;
}
EFI_STATUS
AddEntriesFromBootEntryProtocol (
IN OUT OC_BOOT_CONTEXT *BootContext,
IN OUT OC_BOOT_FILESYSTEM *FileSystem,
IN EFI_HANDLE *EntryProtocolHandles,
IN UINTN EntryProtocolHandleCount,
IN CONST CHAR16 *DefaultEntryId, OPTIONAL
IN CONST BOOLEAN CreateDefault
)
{
EFI_STATUS ReturnStatus;
EFI_STATUS Status;
UINTN Index;
UINTN EntryIndex;
OC_BOOT_ENTRY_PROTOCOL *BootEntryProtocol;
OC_PICKER_ENTRY *Entries;
UINTN NumEntries;
DEBUG_CODE_BEGIN ();
if (CreateDefault) {
ASSERT ((DefaultEntryId != NULL));
}
DEBUG_CODE_END ();
ReturnStatus = EFI_NOT_FOUND;
for (Index = 0; Index < EntryProtocolHandleCount; ++Index) {
//
// Previously marked as invalid protocol revision.
//
if (EntryProtocolHandles[Index] == NULL) {
continue;
}
Status = gBS->HandleProtocol (
EntryProtocolHandles[Index],
&gOcBootEntryProtocolGuid,
(VOID **) &BootEntryProtocol
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "BEP: HandleProtocol failed - %r\n", Status));
continue;
}
if (BootEntryProtocol->Revision != OC_BOOT_ENTRY_PROTOCOL_REVISION) {
DEBUG ((
DEBUG_ERROR,
"BEP: Invalid revision %u (!= %u) in loaded driver\n",
BootEntryProtocol->Revision,
OC_BOOT_ENTRY_PROTOCOL_REVISION
));
EntryProtocolHandles[Index] = NULL;
continue;
}
Status = BootEntryProtocol->GetBootEntries (
BootContext->PickerContext,
FileSystem->Handle == OC_CUSTOM_FS_HANDLE ? NULL : FileSystem->Handle,
&Entries,
&NumEntries
);
if (EFI_ERROR (Status)) {
//
// No entries for any given driver on any given filesystem is normal.
//
if (Status != EFI_NOT_FOUND) {
DEBUG ((DEBUG_WARN, "BEP: Unable to fetch boot entries - %r\n", Status));
}
continue;
}
for (EntryIndex = 0; EntryIndex < NumEntries; EntryIndex++) {
if (Entries[EntryIndex].Id == NULL) {
DEBUG ((DEBUG_WARN, "BEP: Entry->Id is required, ignoring entry.\n"));
}
if (DefaultEntryId == NULL ||
(MixedStrCmp (DefaultEntryId, Entries[EntryIndex].Id) == 0) == CreateDefault) {
Status = InternalAddBootEntryFromCustomEntry (
BootContext,
FileSystem,
&Entries[EntryIndex],
TRUE
);
if (EFI_ERROR (Status)) {
//
// EFI_UNSUPPORTED is auxiliary entry when HideAuxiliary=true.
//
if (Status != EFI_UNSUPPORTED) {
DEBUG ((DEBUG_WARN, "BEP: Error adding entries - %r\n", Status));
break;
}
} else {
ReturnStatus = EFI_SUCCESS;
//
// Stop searching after first match for default entry. Possible additional
// matches, e.g. older versions of Linux kernel, are normal.
//
if (CreateDefault) {
break;
}
}
} else {
//
// Create remaining matches after skipping first match for pre-created entry.
//
if (!CreateDefault) {
DefaultEntryId = NULL;
}
}
}
BootEntryProtocol->FreeBootEntries (
&Entries,
NumEntries
);
//
// If not found, keep hunting for default entry on other installed drivers.
//
if (CreateDefault) {
if (ReturnStatus == EFI_NOT_FOUND) {
continue;
}
break;
}
//
// On other error adding entry (should not fail), abort.
//
if (EFI_ERROR (Status) && Status != EFI_UNSUPPORTED) {
break;
}
}
return ReturnStatus;
}

View File

@ -0,0 +1,56 @@
/** @file
Copyright (C) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#ifndef BOOT_ENTRY_PROTOCOL_INTERNAL_H
#define BOOT_ENTRY_PROTOCOL_INTERNAL_H
#include <Uefi.h>
#include <Library/OcBootManagementLib.h>
/**
Locate boot entry protocol handles.
@param[in,out] EntryProtocolHandles Boot entry protocol handles, or NULL if none.
@param[in,out] EntryProtocolHandleCount Count of boot entry protocol handles.
**/
VOID
LocateBootEntryProtocolHandles (
IN OUT EFI_HANDLE **EntryProtocolHandles,
IN OUT UINTN *EntryProtocolHandleCount
);
/**
Free boot entry protocol handles.
@param[in,out] EntryProtocolHandles Boot entry protocol handles, or NULL if none.
**/
VOID
FreeBootEntryProtocolHandles (
EFI_HANDLE **EntryProtocolHandles
);
/**
Request bootable entries from installed boot entry protocol drivers.
@param[in,out] BootContext Context of filesystems.
@param[in,out] FileSystem Filesystem to scan for entries.
@param[in] EntryProtocolHandles Boot entry protocol handles, or NULL if none.
@param[in] EntryProtocolHandleCount Count of boot entry protocol handles.
@param[in] DefaultEntryId Id of saved default entry on this file system.
@param[in] CreateDefault Create default entry if TRUE, create all others otherwise.
@retval EFI_SUCCESS At least one entry was created.
**/
EFI_STATUS
AddEntriesFromBootEntryProtocol (
IN OUT OC_BOOT_CONTEXT *BootContext,
IN OUT OC_BOOT_FILESYSTEM *FileSystem,
IN EFI_HANDLE *EntryProtocolHandles,
IN UINTN EntryProtocolHandleCount,
IN CONST CHAR16 *DefaultEntryId, OPTIONAL
IN CONST BOOLEAN CreateDefault
);
#endif // BOOT_ENTRY_PROTOCOL_INTERNAL_H

View File

@ -33,6 +33,13 @@
{ 0xd6f263f9, 0x0b19, 0x4670, \
{ 0xb0, 0xa4, 0x9d, 0x95, 0x9f, 0x58, 0xdf, 0x65 } }
///
/// Identifies the DevicePath structure for Boot Entry Brotocol custom entries.
///
#define OC_ENTRY_PROTOCOL_DEVICE_PATH_GUID \
{ 0x669bf063, 0x78c1, 0x4c29, \
{ 0x93, 0x34, 0x2f, 0xf0, 0x15, 0xfe, 0xa2, 0xfe } }
#pragma pack(1)
///
@ -43,6 +50,16 @@ typedef PACKED struct {
FILEPATH_DEVICE_PATH EntryName;
} OC_CUSTOM_BOOT_DEVICE_PATH;
///
/// DevicePath to describe Boot Entry Protocol custom entries.
/// Include partuuid of boot drive in VenHw custom memory.
///
typedef PACKED struct {
VENDOR_DEVICE_PATH Hdr;
EFI_GUID Partuuid;
FILEPATH_DEVICE_PATH EntryName;
} OC_ENTRY_PROTOCOL_DEVICE_PATH;
//
// Ideally, a variant of FILEPATH_DEVICE_PATH will be used with PathName as a
// flexible array. Such cannot be used for declarations, so provide an
@ -53,6 +70,15 @@ typedef PACKED struct {
EFI_DEVICE_PATH_PROTOCOL EntryName;
} OC_CUSTOM_BOOT_DEVICE_PATH_DECL;
//
// Version not including first char of path name.
//
typedef PACKED struct {
VENDOR_DEVICE_PATH Header;
EFI_GUID Partuuid;
EFI_DEVICE_PATH_PROTOCOL EntryName;
} OC_ENTRY_PROTOCOL_DEVICE_PATH_DECL;
#pragma pack()
///
@ -61,6 +87,12 @@ typedef PACKED struct {
#define SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH \
(sizeof (VENDOR_DEVICE_PATH) + SIZE_OF_FILEPATH_DEVICE_PATH)
///
/// The size of a OC_ENTRY_PROTOCOL_DEVICE_PATH structure excluding the name.
///
#define SIZE_OF_OC_ENTRY_PROTOCOL_DEVICE_PATH \
(sizeof (VENDOR_DEVICE_PATH) + sizeof (EFI_GUID) + SIZE_OF_FILEPATH_DEVICE_PATH)
//
// Max. supported Apple version string size
//
@ -166,6 +198,24 @@ InternalGetBootOptionPath (
IN UINTN LoadOptionSize
);
/**
Create bootable entry from custom entry.
@param[in,out] BootContext Context of filesystems.
@param[in,out] FileSystem Filesystem to add custom entry.
@param[in] CustomEntry Custom entry.
@param[in] IsBootEntryProtocol Is entry from OC_BOOT_ENTRY_PROTOCOL.
@retval EFI_SUCCESS on success.
**/
EFI_STATUS
InternalAddBootEntryFromCustomEntry (
IN OUT OC_BOOT_CONTEXT *BootContext,
IN OUT OC_BOOT_FILESYSTEM *FileSystem,
IN OC_PICKER_ENTRY *CustomEntry,
IN BOOLEAN IsBootEntryProtocol
);
/**
Describe boot entry contents by setting fields other than DevicePath.
@ -190,18 +240,21 @@ InternalIsAppleLegacyLoadApp (
This solves the problem of checking scan policy multiple times
as well as the problem of finding the filesystem to add entries too.
@param[in] BootContext Context of filesystems.
@param[in] FileSystemHandle Partition handle.
@param[in] LazyScan Lazy filesystem scanning.
@param[in] BootContext Context of filesystems.
@param[in] FileSystemHandle Partition handle.
@param[in] LazyScan Lazy filesystem scanning.
@param[out] AlreadySeen Set to TRUE if file system was
already present in context.
@retval discovered filesystem (legit).
@retcal NULL when booting is not allowed from this filesystem.
**/
OC_BOOT_FILESYSTEM *
InternalFileSystemForHandle (
IN OC_BOOT_CONTEXT *BootContext,
IN EFI_HANDLE FileSystemHandle,
IN BOOLEAN LazyScan
IN OC_BOOT_CONTEXT *BootContext,
IN EFI_HANDLE FileSystemHandle,
IN BOOLEAN LazyScan,
OUT BOOLEAN *AlreadySeen OPTIONAL
);
/**
@ -230,6 +283,16 @@ InternalGetOcCustomDevPath (
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
);
/**
Determines whether DevicePath is a Boot Entry Protocol custom boot entry.
@returns The Boot Entry Protocol custom boot entry, or NULL.
**/
CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *
InternalGetOcEntryProtocolDevPath (
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
);
EFI_STATUS
InternalRunRequestPrivilege (
IN OC_PICKER_CONTEXT *PickerContext,

View File

@ -1,15 +1,6 @@
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Copyright (C) 2019-2021, vit9696, mikebeaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Uefi.h>
@ -19,6 +10,7 @@
#include <Guid/AppleFile.h>
#include <Guid/AppleVariable.h>
#include <Guid/GlobalVariable.h>
#include <Guid/Gpt.h>
#include <Guid/OcVariable.h>
#include <Protocol/DevicePath.h>
@ -40,7 +32,7 @@
#include <Library/UefiRuntimeServicesTableLib.h>
///
/// Template for an OpenCore custom boot entry DevicePath node.
/// Template for OpenCore custom boot entry DevicePath.
///
STATIC CONST OC_CUSTOM_BOOT_DEVICE_PATH_DECL mOcCustomBootDevPathTemplate = {
{
@ -58,6 +50,26 @@ STATIC CONST OC_CUSTOM_BOOT_DEVICE_PATH_DECL mOcCustomBootDevPathTemplate = {
}
};
///
/// Template for Boot Entry Protocol custom boot entry DevicePath.
///
STATIC CONST OC_ENTRY_PROTOCOL_DEVICE_PATH_DECL mOcEntryProtocolDevPathTemplate = {
{
{
HARDWARE_DEVICE_PATH,
HW_VENDOR_DP,
{ sizeof (VENDOR_DEVICE_PATH) + sizeof (EFI_GUID), 0 }
},
OC_ENTRY_PROTOCOL_DEVICE_PATH_GUID
},
EFI_PART_TYPE_UNUSED_GUID,
{
MEDIA_DEVICE_PATH,
MEDIA_FILEPATH_DP,
{ SIZE_OF_FILEPATH_DEVICE_PATH, 0 }
}
};
CONST OC_CUSTOM_BOOT_DEVICE_PATH *
InternalGetOcCustomDevPath (
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
@ -90,6 +102,38 @@ InternalGetOcCustomDevPath (
return CustomDevPath;
}
CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *
InternalGetOcEntryProtocolDevPath (
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
UINTN DevicePathSize;
INTN CmpResult;
CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *EntryProtocolDevPath;
DevicePathSize = GetDevicePathSize (DevicePath);
if (DevicePathSize < SIZE_OF_OC_ENTRY_PROTOCOL_DEVICE_PATH) {
return NULL;
}
CmpResult = CompareMem (
DevicePath,
&mOcEntryProtocolDevPathTemplate.Header,
sizeof (mOcEntryProtocolDevPathTemplate.Header)
);
if (CmpResult != 0) {
return NULL;
}
EntryProtocolDevPath = (CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *) DevicePath;
if (EntryProtocolDevPath->EntryName.Header.Type != MEDIA_DEVICE_PATH
|| EntryProtocolDevPath->EntryName.Header.SubType != MEDIA_FILEPATH_DP) {
return NULL;
}
return EntryProtocolDevPath;
}
EFI_LOAD_OPTION *
InternalGetBootOptionData (
OUT UINTN *OptionSize,
@ -358,7 +402,7 @@ InternalMatchCustomBootEntryByDevicePath (
{
INTN CmpResult;
if (!BootEntry->IsCustom) {
if (!BootEntry->IsCustom || BootEntry->IsBootEntryProtocol) {
return FALSE;
}
@ -370,6 +414,32 @@ InternalMatchCustomBootEntryByDevicePath (
return TRUE;
}
STATIC
BOOLEAN
InternalMatchEntryProtocolEntryByDevicePath (
IN OUT OC_BOOT_ENTRY *BootEntry,
IN CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *DevicePath
)
{
INTN CmpResult;
if (!BootEntry->IsCustom || !BootEntry->IsBootEntryProtocol || BootEntry->Id == NULL) {
return FALSE;
}
CmpResult = CompareMem (&BootEntry->UniquePartitionGUID, &DevicePath->Partuuid, sizeof (EFI_GUID));
if (CmpResult != 0) {
return FALSE;
}
CmpResult = StrCmp (BootEntry->Id, DevicePath->EntryName.PathName);
if (CmpResult != 0) {
return FALSE;
}
return TRUE;
}
STATIC
VOID
InternalClearNextVariables (
@ -735,23 +805,34 @@ OcSetDefaultBootEntry (
EFI_DEVICE_PATH *BootOptionRemainingDevicePath;
EFI_HANDLE DeviceHandle;
BOOLEAN MatchedEntry;
BOOLEAN IsOverflow;
BOOLEAN IsAsciiOptionName;
EFI_GUID *BootVariableGuid;
CHAR16 *BootOrderName;
CHAR16 *BootVariableName;
CHAR16 *LoadOptionId;
VOID *LoadOptionName;
CHAR8 *FirstFlavourEnd;
UINT16 *BootOrder;
UINT16 *NewBootOrder;
UINT16 BootTmp;
UINT16 EntryIdLength;
UINTN BootOrderCount;
UINTN BootChosenIndex;
UINTN Index;
UINTN DevicePathSize;
UINTN LoadOptionSize;
UINTN LoadOptionIdSize;
UINTN LoadOptionNameSize;
UINTN LoadOptionNameLen;
UINTN CopiedLength;
EFI_LOAD_OPTION *LoadOption;
CONST OC_CUSTOM_BOOT_DEVICE_PATH *CustomDevPath;
OC_CUSTOM_BOOT_DEVICE_PATH *DestCustomDevPath;
EFI_DEVICE_PATH_PROTOCOL *DestCustomEndNode;
CONST OC_CUSTOM_BOOT_DEVICE_PATH *CustomDevPath;
CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *EntryProtocolDevPath;
VENDOR_DEVICE_PATH *DestCustomDevPath;
FILEPATH_DEVICE_PATH *DestCustomEntryName;
EFI_DEVICE_PATH_PROTOCOL *DestCustomEndNode;
//
// Do not allow when prohibited.
@ -839,6 +920,14 @@ OcSetDefaultBootEntry (
Entry,
CustomDevPath
);
} else {
EntryProtocolDevPath = InternalGetOcEntryProtocolDevPath (BootOptionDevicePath);
if (EntryProtocolDevPath != NULL) {
MatchedEntry = InternalMatchEntryProtocolEntryByDevicePath (
Entry,
EntryProtocolDevPath
);
}
}
}
@ -849,12 +938,61 @@ OcSetDefaultBootEntry (
//
// Write to Boot0080
//
LoadOptionNameSize = StrSize (Entry->Name);
ASSERT (Entry->Name != NULL);
IsAsciiOptionName = FALSE;
if (Entry->Id == NULL) {
//
// Re-use user defined entry name as stored id.
//
LoadOptionName = Entry->Name;
LoadOptionNameSize = StrSize (Entry->Name);
LoadOptionId = LoadOptionName;
LoadOptionIdSize = LoadOptionNameSize;
} else {
//
// Re-use first part of flavour as option name if available, it is more human
// readable than entry id, but is not version specific, unlike entry name.
//
LoadOptionId = Entry->Id;
LoadOptionIdSize = StrSize (Entry->Id);
if (Entry->Flavour != NULL && Entry->Flavour[0] != '\0' && Entry->Flavour[0] != ':') {
FirstFlavourEnd = OcAsciiStrChr (Entry->Flavour, ':');
if (FirstFlavourEnd != NULL) {
LoadOptionNameLen = FirstFlavourEnd - Entry->Flavour;
} else {
LoadOptionNameLen = AsciiStrLen (Entry->Flavour);
}
IsAsciiOptionName = TRUE;
LoadOptionNameSize = (LoadOptionNameLen + 1) * sizeof (CHAR16) / sizeof (CHAR8);
LoadOptionName = Entry->Flavour;
} else {
LoadOptionName = LoadOptionId;
LoadOptionNameSize = LoadOptionIdSize;
}
}
if (!Entry->IsCustom) {
DevicePathSize = GetDevicePathSize (Entry->DevicePath);
} else {
DevicePathSize = SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH + LoadOptionNameSize + sizeof (EFI_DEVICE_PATH_PROTOCOL);
DevicePathSize = SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH
+ (Entry->IsBootEntryProtocol ? sizeof (EFI_GUID) : 0)
+ LoadOptionIdSize
+ sizeof (EFI_DEVICE_PATH_PROTOCOL);
if (LoadOptionIdSize > MAX_UINT16) {
IsOverflow = TRUE;
} else {
IsOverflow = OcOverflowAddU16 (SIZE_OF_FILEPATH_DEVICE_PATH, (UINT16)LoadOptionIdSize, &EntryIdLength);
}
if (IsOverflow) {
DEBUG ((DEBUG_ERROR, "OCB: Overflowing option id size (%u)\n", LoadOptionIdSize));
if (BootOrder != NULL) {
FreePool (BootOrder);
}
return EFI_INVALID_PARAMETER;
}
}
LoadOptionSize = sizeof (EFI_LOAD_OPTION) + LoadOptionNameSize + DevicePathSize;
@ -870,35 +1008,61 @@ OcSetDefaultBootEntry (
LoadOption->Attributes = LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT;
LoadOption->FilePathListLength = (UINT16) DevicePathSize;
CopyMem (LoadOption + 1, Entry->Name, LoadOptionNameSize);
if (IsAsciiOptionName) {
Status = AsciiStrnToUnicodeStrS (LoadOptionName, LoadOptionNameLen, (CHAR16 *)(LoadOption + 1), LoadOptionNameSize / sizeof (CHAR16), &CopiedLength);
ASSERT (!EFI_ERROR (Status));
ASSERT (CopiedLength == LoadOptionNameLen);
} else {
CopyMem (LoadOption + 1, LoadOptionName, LoadOptionNameSize);
}
if (!Entry->IsCustom) {
CopyMem ((UINT8 *) (LoadOption + 1) + LoadOptionNameSize, Entry->DevicePath, DevicePathSize);
} else {
DestCustomDevPath = (OC_CUSTOM_BOOT_DEVICE_PATH *) (
DestCustomDevPath = (VENDOR_DEVICE_PATH *) (
(UINT8 *) (LoadOption + 1) + LoadOptionNameSize
);
if (Entry->IsBootEntryProtocol) {
CopyMem (
DestCustomDevPath,
&mOcEntryProtocolDevPathTemplate,
sizeof (mOcEntryProtocolDevPathTemplate)
);
CopyMem (
DestCustomDevPath + 1,
&Entry->UniquePartitionGUID,
sizeof (EFI_GUID)
);
DestCustomEntryName = (FILEPATH_DEVICE_PATH *) (
(UINT8 *) (DestCustomDevPath + 1) +
sizeof (EFI_GUID)
);
} else {
CopyMem (
DestCustomDevPath,
&mOcCustomBootDevPathTemplate,
sizeof (mOcCustomBootDevPathTemplate)
);
DestCustomEntryName = (FILEPATH_DEVICE_PATH *) (
(UINT8 *) (DestCustomDevPath + 1)
);
}
CopyMem (
DestCustomDevPath,
&mOcCustomBootDevPathTemplate,
sizeof (mOcCustomBootDevPathTemplate)
DestCustomEntryName->PathName,
LoadOptionId,
LoadOptionIdSize
);
CopyMem (
DestCustomDevPath->EntryName.PathName,
Entry->Name,
LoadOptionNameSize
);
//
// FIXME: This may theoretically overflow.
//
DestCustomDevPath->EntryName.Header.Length[0] += (UINT8) LoadOptionNameSize;
DestCustomEntryName->Header.Length[0] = (UINT8) EntryIdLength;
DestCustomEntryName->Header.Length[1] = (UINT8) (EntryIdLength >> 8);
DestCustomEndNode = (EFI_DEVICE_PATH_PROTOCOL *) (
(UINT8 *) DestCustomDevPath + SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH + LoadOptionNameSize
(UINT8 *) DestCustomEntryName + EntryIdLength
);
SetDevicePathEndNode (DestCustomEndNode);
ASSERT (GetDevicePathSize (&DestCustomDevPath->Hdr.Header) == DevicePathSize);
ASSERT (GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *) DestCustomDevPath) == DevicePathSize);
}
Status = gRT->SetVariable (

View File

@ -39,6 +39,8 @@
BootEntryInfo.c
BootEntryManagement.c
BootManagementInternal.h
BootEntryProtocol.c
BootEntryProtocolInternal.h
BuiltinPicker.c
DefaultEntryChoice.c
DmgBootSupport.c
@ -77,6 +79,7 @@
gAppleSecureBootVariableGuid ## SOMETIMES_CONSUMES
gAppleTamperResistantBootSecureVariableGuid ## SOMETIMES_CONSUMES
gAppleTamperResistantBootEfiUserVariableGuid ## SOMETIMES_CONSUMES
gEfiPartTypeUnusedGuid ## SOMETIMES_CONSUMES
gOcVendorVariableGuid ## SOMETIMES_CONSUMES
gOcReadOnlyVariableGuid ## SOMETIMES_CONSUMES
gOcWriteOnlyVariableGuid ## SOMETIMES_CONSUMES
@ -92,6 +95,7 @@
gOcFirmwareRuntimeProtocolGuid ## SOMETIMES_CONSUMES
gOcAudioProtocolGuid ## SOMETIMES_CONSUMES
gAppleBeepGenProtocolGuid ## SOMETIMES_CONSUMES
gOcBootEntryProtocolGuid ## CONSUMES
[LibraryClasses]
BaseLib
@ -112,8 +116,9 @@
OcCryptoLib
OcDeviceMiscLib
OcDevicePathLib
OcGuardLib
OcFileLib
OcFlexArrayLib
OcGuardLib
OcMachoLib
OcMiscLib
OcPeCoffLib

View File

@ -192,6 +192,7 @@ EFI_GUID mMsftRecoveryPartitionTypeGuid = {
/**
Linux partitions.
https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
https://systemd.io/DISCOVERABLE_PARTITIONS/
**/
EFI_GUID mLinuxRootX86PartitionTypeGuid = {
0x44479540, 0xF297, 0x41B2, {0x9A, 0xF7, 0xD1, 0x31, 0xD5, 0xF0, 0x45, 0x8A}
@ -201,6 +202,18 @@ EFI_GUID mLinuxRootX8664PartitionTypeGuid = {
0x4F68BCE3, 0xE8CD, 0x4DB1, {0x96, 0xE7, 0xFB, 0xCA, 0xF9, 0x84, 0xB7, 0x09}
};
EFI_GUID mLinuxFileSystemPartitionTypeGuid = {
0x0FC63DAF, 0x8483, 0x4772, { 0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4 }
};
/**
Extended Boot Loader Partition (XBOOTLDR).
https://systemd.io/BOOT_LOADER_SPECIFICATION/
**/
EFI_GUID mXBootLdrPartitionTypeGuid = {
0xBC13C2FF, 0x59E6, 0x4262, { 0xA3, 0x52, 0xB2, 0x75, 0xFD, 0x6F, 0x71, 0x72 }
};
UINT32
OcGetFileSystemPolicyType (
IN EFI_HANDLE Handle
@ -223,6 +236,10 @@ OcGetFileSystemPolicyType (
return OC_SCAN_ALLOW_FS_ESP;
}
if (CompareGuid (&PartitionEntry->PartitionTypeGUID, &mXBootLdrPartitionTypeGuid)) {
return OC_SCAN_ALLOW_FS_XBOOTLDR;
}
//
// Unsure whether these two should be separate, likely not.
//
@ -237,7 +254,11 @@ OcGetFileSystemPolicyType (
if (CompareGuid (&PartitionEntry->PartitionTypeGUID, &mLinuxRootX86PartitionTypeGuid)
|| CompareGuid (&PartitionEntry->PartitionTypeGUID, &mLinuxRootX8664PartitionTypeGuid)) {
return OC_SCAN_ALLOW_FS_EXT;
return OC_SCAN_ALLOW_FS_LINUX_ROOT;
}
if (CompareGuid (&PartitionEntry->PartitionTypeGUID, &mLinuxFileSystemPartitionTypeGuid)) {
return OC_SCAN_ALLOW_FS_LINUX_DATA;
}
return 0;

View File

@ -71,6 +71,7 @@ OC_STRUCTORS (OC_PLATFORM_NVRAM_CONFIG, ())
OC_STRUCTORS (OC_PLATFORM_SMBIOS_CONFIG, ())
OC_STRUCTORS (OC_PLATFORM_CONFIG, ())
OC_STRUCTORS (OC_UEFI_DRIVER_ENTRY, ())
OC_ARRAY_STRUCTORS (OC_UEFI_DRIVER_ARRAY)
OC_STRUCTORS (OC_UEFI_APFS, ())
OC_STRUCTORS (OC_UEFI_APPLEINPUT, ())
@ -667,7 +668,15 @@ mPlatformConfigurationSchema[] = {
STATIC
OC_SCHEMA
mUefiDriversSchema = OC_SCHEMA_STRING (NULL);
mUefiDriversSchemaEntry[] = {
OC_SCHEMA_STRING_IN ("Arguments", OC_UEFI_DRIVER_ENTRY, Arguments),
OC_SCHEMA_BOOLEAN_IN ("Enabled", OC_UEFI_DRIVER_ENTRY, Enabled),
OC_SCHEMA_STRING_IN ("Path", OC_UEFI_DRIVER_ENTRY, Path),
};
STATIC
OC_SCHEMA
mUefiDriversSchema = OC_SCHEMA_DICT (NULL, mUefiDriversSchemaEntry);
STATIC
OC_SCHEMA

View File

@ -1,6 +1,6 @@
## @file
#
# Component description file for OcMisclibrary.
# Component description file for OcDeviceMiscLib.
#
# Copyright (C) 2016 - 2018, The HermitCrabs Lab. All rights reserved.<BR>
#

View File

@ -1,15 +1,6 @@
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Copyright (C) 2019-2021, vit9696, Goldfish64, mikebeaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Uefi.h>
@ -420,6 +411,30 @@ OcDirectorySeachContextInit (
ZeroMem (Context, sizeof (*Context));
}
EFI_STATUS
OcEnsureDirectory (
IN EFI_FILE_PROTOCOL *File,
IN BOOLEAN IsDirectory
)
{
EFI_FILE_INFO *FileInfo;
//
// Ensure this is a directory/file.
//
FileInfo = OcGetFileInfo (File, &gEfiFileInfoGuid, 0, NULL);
if (FileInfo == NULL) {
return EFI_INVALID_PARAMETER;
}
if (((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) != IsDirectory) {
FreePool (FileInfo);
return EFI_INVALID_PARAMETER;
}
FreePool (FileInfo);
return EFI_SUCCESS;
}
EFI_STATUS
OcGetNewestFileFromDirectory (
IN OUT DIRECTORY_SEARCH_CONTEXT *Context,
@ -445,18 +460,10 @@ OcGetNewestFileFromDirectory (
LatestIndex = 0;
LatestEpoch = 0;
//
// Ensure this is a directory.
//
FileInfoCurrent = OcGetFileInfo (Directory, &gEfiFileInfoGuid, 0, NULL);
if (FileInfoCurrent == NULL) {
return EFI_INVALID_PARAMETER;
Status = OcEnsureDirectory (Directory, TRUE);
if (EFI_ERROR (Status)) {
return Status;
}
if (!(FileInfoCurrent->Attribute & EFI_FILE_DIRECTORY)) {
FreePool (FileInfoCurrent);
return EFI_INVALID_PARAMETER;
}
FreePool (FileInfoCurrent);
//
// Allocate two FILE_INFO structures.
@ -564,3 +571,80 @@ OcGetNewestFileFromDirectory (
return EFI_SUCCESS;
}
//
// TODO: OcGetNewestFileFromDirectory above and ScanExtensions in CachelessContext.c could be redone using this.
// TODO: I am unclear exactly what the Apple 32-bit HFS is being described as doing (see also OcGetFileInfo), so
// have just copied the existing handling.
//
EFI_STATUS
OcScanDirectory (
IN EFI_FILE_HANDLE Directory,
IN OC_PROCESS_DIRECTORY_ENTRY ProcessEntry,
IN OUT VOID *Context OPTIONAL
)
{
EFI_STATUS Status;
EFI_STATUS TempStatus;
EFI_FILE_INFO *FileInfo;
UINTN FileInfoSize;
ASSERT (Directory != NULL);
ASSERT (ProcessEntry != NULL);
Status = OcEnsureDirectory (Directory, TRUE);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Allocate FILE_INFO structure.
//
FileInfo = AllocatePool (SIZE_1KB);
if (FileInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = EFI_NOT_FOUND;
Directory->SetPosition (Directory, 0);
do {
//
// Apple's HFS+ driver does not adhere to the spec and will return zero for
// EFI_BUFFER_TOO_SMALL. EFI_FILE_INFO structures larger than 1KB are
// unrealistic as the filename is the only variable.
//
FileInfoSize = SIZE_1KB - sizeof (CHAR16);
TempStatus = Directory->Read (Directory, &FileInfoSize, FileInfo);
if (EFI_ERROR (TempStatus)) {
Status = TempStatus;
break;
}
if (FileInfoSize > 0) {
TempStatus = ProcessEntry (Directory, FileInfo, FileInfoSize, Context);
//
// Act as if no matching file was found.
//
if (TempStatus == EFI_NOT_FOUND) {
continue;
}
if (EFI_ERROR (TempStatus)) {
Status = TempStatus;
break;
}
//
// At least one file found.
//
Status = EFI_SUCCESS;
}
} while (FileInfoSize > 0);
Directory->SetPosition (Directory, 0);
FreePool (FileInfo);
return Status;
}

View File

@ -27,11 +27,11 @@
EFI_STATUS
OcSafeFileOpen (
IN EFI_FILE_PROTOCOL *Protocol,
OUT EFI_FILE_PROTOCOL **NewHandle,
IN CONST CHAR16 *FileName,
IN UINT64 OpenMode,
IN UINT64 Attributes
IN CONST EFI_FILE_PROTOCOL *Protocol,
OUT EFI_FILE_PROTOCOL **NewHandle,
IN CONST CHAR16 *FileName,
IN CONST UINT64 OpenMode,
IN CONST UINT64 Attributes
)
{
EFI_STATUS Status;
@ -48,7 +48,7 @@ OcSafeFileOpen (
*NewHandle = NULL;
Status = Protocol->Open (
Protocol,
(EFI_FILE_PROTOCOL *) Protocol,
NewHandle,
(CHAR16 *) FileName,
OpenMode,

View File

@ -31,10 +31,10 @@
VOID *
OcReadFile (
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem,
IN CONST CHAR16 *FilePath,
OUT UINT32 *FileSize OPTIONAL,
IN UINT32 MaxFileSize OPTIONAL
IN CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem,
IN CONST CHAR16 *FilePath,
OUT UINT32 *FileSize OPTIONAL,
IN CONST UINT32 MaxFileSize OPTIONAL
)
{
EFI_STATUS Status;
@ -48,7 +48,7 @@ OcReadFile (
ASSERT (FilePath != NULL);
Status = FileSystem->OpenVolume (
FileSystem,
(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *) FileSystem,
&Volume
);
if (EFI_ERROR (Status)) {
@ -58,7 +58,7 @@ OcReadFile (
Status = OcSafeFileOpen (
Volume,
&FileHandle,
(CHAR16 *) FilePath,
FilePath,
EFI_FILE_MODE_READ,
0
);
@ -151,11 +151,11 @@ OcReadFileSize (
}
VOID *
OcReadFileFromFile (
IN EFI_FILE_PROTOCOL *RootFile,
IN CONST CHAR16 *FilePath,
OUT UINT32 *FileSize OPTIONAL,
IN UINT32 MaxFileSize OPTIONAL
OcReadFileFromDirectory (
IN CONST EFI_FILE_PROTOCOL *RootDirectory,
IN CONST CHAR16 *FilePath,
OUT UINT32 *FileSize OPTIONAL,
IN UINT32 MaxFileSize OPTIONAL
)
{
EFI_STATUS Status;
@ -163,13 +163,13 @@ OcReadFileFromFile (
UINT32 Size;
UINT8 *FileBuffer;
ASSERT (RootFile != NULL);
ASSERT (RootDirectory != NULL);
ASSERT (FilePath != NULL);
Status = OcSafeFileOpen (
RootFile,
RootDirectory,
&File,
(CHAR16 *) FilePath,
FilePath,
EFI_FILE_MODE_READ,
0
);

View File

@ -0,0 +1,196 @@
/** @file
String buffer.
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Base.h>
#include <Library/BaseLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcFlexArrayLib.h>
#include <Library/OcGuardLib.h>
#include <Library/PrintLib.h>
OC_STRING_BUFFER *
OcAsciiStringBufferInit (
VOID
)
{
OC_STRING_BUFFER *Buffer;
Buffer = AllocateZeroPool (sizeof (OC_STRING_BUFFER));
return Buffer;
}
EFI_STATUS
OcAsciiStringBufferAppend (
IN OUT OC_STRING_BUFFER *Buffer,
IN CONST CHAR8 *AppendString OPTIONAL
)
{
return OcAsciiStringBufferAppendN (Buffer, AppendString, MAX_UINTN);
}
STATIC
EFI_STATUS
InternalAsciiStringBufferExtendBy (
IN OUT OC_STRING_BUFFER *Buffer,
IN CONST UINTN AppendLength,
OUT UINTN *TargetLength
)
{
UINTN NewSize;
ASSERT (AppendLength != 0);
if (Buffer->String == NULL) {
ASSERT (Buffer->BufferSize == 0);
Buffer->String = AllocatePool (AppendLength + 1);
if (Buffer->String == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Buffer->BufferSize = AppendLength + 1;
*TargetLength = AppendLength;
} else {
if (Buffer->BufferSize == 0) {
ASSERT (FALSE);
return EFI_UNSUPPORTED;
}
NewSize = Buffer->BufferSize;
if (OcOverflowAddUN (Buffer->StringLength, AppendLength, TargetLength)) {
return EFI_OUT_OF_RESOURCES;
}
while (NewSize <= *TargetLength) {
if (OcOverflowMulUN (NewSize, 2, &NewSize)) {
return EFI_OUT_OF_RESOURCES;
}
}
if (NewSize > Buffer->BufferSize) {
Buffer->String = ReallocatePool (Buffer->BufferSize, NewSize, Buffer->String);
if (Buffer->String == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Buffer->BufferSize = NewSize;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
OcAsciiStringBufferAppendN (
IN OUT OC_STRING_BUFFER *Buffer,
IN CONST CHAR8 *AppendString, OPTIONAL
IN CONST UINTN Length
)
{
EFI_STATUS Status;
UINTN AppendLength;
UINTN TargetLength;
if (AppendString == NULL) {
return EFI_SUCCESS;
}
AppendLength = AsciiStrnLenS (AppendString, Length);
//
// Buffer stays NULL if zero appended.
//
if (AppendLength == 0) {
return EFI_SUCCESS;
}
Status = InternalAsciiStringBufferExtendBy (Buffer, AppendLength, &TargetLength);
if (EFI_ERROR (Status)) {
return Status;
}
Status = AsciiStrnCpyS (&Buffer->String[Buffer->StringLength], AppendLength + 1, AppendString, AppendLength);
if (EFI_ERROR (Status)) {
return Status;
}
Buffer->StringLength = TargetLength;
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
OcAsciiStringBufferSPrint (
IN OUT OC_STRING_BUFFER *Buffer,
IN CONST CHAR8 *FormatString,
...
)
{
EFI_STATUS Status;
VA_LIST Marker;
VA_LIST Marker2;
UINTN NumberOfPrinted;
UINTN TargetLength;
ASSERT (FormatString != NULL);
VA_START (Marker, FormatString);
VA_COPY (Marker2, Marker);
NumberOfPrinted = SPrintLengthAsciiFormat (FormatString, Marker2);
VA_END (Marker2);
//
// Buffer stays NULL if zero appended.
//
if (NumberOfPrinted == 0) {
Status = EFI_SUCCESS;
} else {
Status = InternalAsciiStringBufferExtendBy (Buffer, NumberOfPrinted, &TargetLength);
if (!EFI_ERROR (Status)) {
AsciiVSPrint (&Buffer->String[Buffer->StringLength], NumberOfPrinted + 1, FormatString, Marker);
Buffer->StringLength = TargetLength;
}
}
VA_END (Marker);
return Status;
}
CHAR8 *
OcAsciiStringBufferFreeContainer (
IN OUT OC_STRING_BUFFER **Buffer
)
{
CHAR8 *String;
if (Buffer == NULL || *Buffer == NULL) {
ASSERT (FALSE);
return NULL;
}
String = (*Buffer)->String;
FreePool (*Buffer);
*Buffer = NULL;
return String;
}
VOID
OcAsciiStringBufferFree (
IN OUT OC_STRING_BUFFER **StringBuffer
)
{
CHAR8 *Result;
Result = OcAsciiStringBufferFreeContainer (StringBuffer);
if (Result != NULL) {
FreePool (Result);
}
}

View File

@ -0,0 +1,195 @@
/** @file
Auto-resizing array.
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Uefi.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcFlexArrayLib.h>
#include <Library/OcGuardLib.h>
#define INITIAL_NUM_ITEMS (8)
OC_FLEX_ARRAY *
OcFlexArrayInit (
IN CONST UINTN ItemSize,
IN CONST OC_FLEX_ARRAY_FREE_ITEM FreeItem OPTIONAL
)
{
OC_FLEX_ARRAY *FlexArray;
ASSERT (ItemSize > 0);
FlexArray = AllocateZeroPool (sizeof (OC_FLEX_ARRAY));
if (FlexArray != NULL) {
FlexArray->ItemSize = ItemSize;
FlexArray->FreeItem = FreeItem;
}
return FlexArray;
}
STATIC
VOID *
InternalFlexArrayAddItem (
IN OUT OC_FLEX_ARRAY *FlexArray
)
{
VOID *TmpBuffer;
UINTN NewSize;
VOID *Item;
ASSERT (FlexArray != NULL);
if (FlexArray->Items == NULL) {
FlexArray->AllocatedCount = INITIAL_NUM_ITEMS;
if (OcOverflowMulUN (FlexArray->AllocatedCount, FlexArray->ItemSize, &NewSize)) {
return NULL;
}
FlexArray->Count = 1;
FlexArray->Items = AllocatePool (NewSize);
if (FlexArray->Items == NULL) {
return NULL;
}
} else {
ASSERT (FlexArray->Count > 0);
ASSERT (FlexArray->AllocatedCount > 0);
ASSERT (FlexArray->Count <= FlexArray->AllocatedCount);
++(FlexArray->Count);
if (FlexArray->Count > FlexArray->AllocatedCount) {
if (OcOverflowMulUN (FlexArray->AllocatedCount * FlexArray->ItemSize, 2, &NewSize)) {
return NULL;
}
TmpBuffer = ReallocatePool (FlexArray->AllocatedCount * FlexArray->ItemSize, NewSize, FlexArray->Items);
if (TmpBuffer == NULL) {
return NULL;
}
FlexArray->Items = TmpBuffer;
FlexArray->AllocatedCount = FlexArray->AllocatedCount * 2;
}
}
Item = OcFlexArrayItemAt (FlexArray, FlexArray->Count - 1);
return Item;
}
VOID *
OcFlexArrayAddItem (
IN OUT OC_FLEX_ARRAY *FlexArray
)
{
VOID *Item;
ASSERT (FlexArray != NULL);
Item = InternalFlexArrayAddItem (FlexArray);
if (Item != NULL) {
ZeroMem (Item, FlexArray->ItemSize);
}
return Item;
}
VOID *
OcFlexArrayInsertItem (
IN OUT OC_FLEX_ARRAY *FlexArray,
IN CONST UINTN InsertIndex
)
{
VOID *Item;
VOID *Dest;
ASSERT (FlexArray != NULL);
ASSERT (InsertIndex <= FlexArray->Count);
if (InsertIndex == FlexArray->Count) {
return OcFlexArrayAddItem (FlexArray);
}
Item = InternalFlexArrayAddItem (FlexArray);
if (Item == NULL) {
return Item;
}
Item = OcFlexArrayItemAt (FlexArray, InsertIndex);
Dest = OcFlexArrayItemAt (FlexArray, InsertIndex + 1);
CopyMem (Dest, Item, (FlexArray->Count - InsertIndex) * FlexArray->ItemSize);
ZeroMem (Item, FlexArray->ItemSize);
return Item;
}
VOID *
OcFlexArrayItemAt (
IN CONST OC_FLEX_ARRAY *FlexArray,
IN CONST UINTN Index
)
{
if (Index >= FlexArray->Count || FlexArray->Items == NULL) {
ASSERT (FALSE);
return NULL;
}
return ((UINT8 *) FlexArray->Items) + Index * FlexArray->ItemSize;
}
VOID
OcFlexArrayFree (
IN OC_FLEX_ARRAY **FlexArray
)
{
UINTN Index;
ASSERT (FlexArray != NULL);
if (*FlexArray != NULL) {
if ((*FlexArray)->Items != NULL) {
if ((*FlexArray)->FreeItem) {
for (Index = 0; Index < (*FlexArray)->Count; Index++) {
(*FlexArray)->FreeItem (OcFlexArrayItemAt (*FlexArray, Index));
}
}
FreePool ((*FlexArray)->Items);
}
FreePool (*FlexArray);
*FlexArray = NULL;
}
}
VOID
OcFlexArrayFreeContainer (
IN OC_FLEX_ARRAY **FlexArray,
IN VOID **Items,
IN UINTN *Count
)
{
if (FlexArray == NULL || *FlexArray == NULL) {
ASSERT (FALSE);
*Items = NULL;
*Count = 0;
} else {
*Items = (*FlexArray)->Items;
*Count = (*FlexArray)->Count;
FreePool (*FlexArray);
*FlexArray = NULL;
}
}
VOID
OcFlexArrayFreePointerItem (
IN VOID *Item
)
{
ASSERT (Item != NULL);
if (*(VOID **)Item != NULL) {
FreePool (*(VOID **)Item);
*(VOID **)Item = NULL;
}
}

View File

@ -0,0 +1,27 @@
## @file
# Component description file for OcFlexArray library.
#
# Copyright (C) 2021, Mike Beaton. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-3-Clause
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = OcFlexArrayLib
FILE_GUID = 38906C95-DCBA-491C-9FFA-E0AAFFF88EA0
MODULE_TYPE = BASE
VERSION_STRING = 1.0
LIBRARY_CLASS = OcFlexArrayLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_DRIVER UEFI_APPLICATION DXE_SMM_DRIVER
# VALID_ARCHITECTURES = IA32 X64
[Packages]
OpenCorePkg/OpenCorePkg.dec
MdePkg/MdePkg.dec
[LibraryClasses]
BaseLib
[Sources]
FlexArray.c
AsciiStringBuffer.c

View File

@ -331,7 +331,7 @@ OcKernelLoadAndReserveKext (
UnicodeUefiSlashes (FullPath);
if (IsForced) {
Kext->PlistData = OcReadFileFromFile (
Kext->PlistData = OcReadFileFromDirectory (
RootFile,
FullPath,
&Kext->PlistDataSize,
@ -388,7 +388,7 @@ OcKernelLoadAndReserveKext (
UnicodeUefiSlashes (FullPath);
if (IsForced) {
Kext->ImageData = OcReadFileFromFile (
Kext->ImageData = OcReadFileFromDirectory (
RootFile,
FullPath,
&Kext->ImageDataSize,

View File

@ -88,15 +88,18 @@ OcLoadDrivers (
OUT EFI_HANDLE **DriversToConnect OPTIONAL
)
{
EFI_STATUS Status;
VOID *Driver;
UINT32 DriverSize;
UINT32 Index;
CHAR16 DriverPath[OC_STORAGE_SAFE_PATH_MAX];
EFI_HANDLE ImageHandle;
EFI_HANDLE *DriversToConnectIterator;
VOID *DriverBinding;
BOOLEAN SkipDriver;
EFI_STATUS Status;
VOID *Driver;
UINT32 DriverSize;
UINT32 Index;
CHAR16 DriverPath[OC_STORAGE_SAFE_PATH_MAX];
EFI_HANDLE ImageHandle;
EFI_HANDLE *DriversToConnectIterator;
VOID *DriverBinding;
BOOLEAN SkipDriver;
OC_UEFI_DRIVER_ENTRY *DriverEntry;
CHAR8 *DriverFileName;
CONST CHAR8 *DriverArguments;
DriversToConnectIterator = NULL;
if (DriversToConnect != NULL) {
@ -106,18 +109,22 @@ OcLoadDrivers (
DEBUG ((DEBUG_INFO, "OC: Got %u drivers\n", Config->Uefi.Drivers.Count));
for (Index = 0; Index < Config->Uefi.Drivers.Count; ++Index) {
SkipDriver = OC_BLOB_GET (Config->Uefi.Drivers.Values[Index])[0] == '#';
DriverEntry = Config->Uefi.Drivers.Values[Index];
DriverFileName = OC_BLOB_GET (&DriverEntry->Path);
DriverArguments = OC_BLOB_GET (&DriverEntry->Arguments);
SkipDriver = !DriverEntry->Enabled || DriverFileName == NULL || DriverFileName[0] == '\0';
DEBUG ((
DEBUG_INFO,
"OC: Driver %a at %u is %a\n",
OC_BLOB_GET (Config->Uefi.Drivers.Values[Index]),
DriverFileName,
Index,
SkipDriver ? "skipped!" : "being loaded..."
));
//
// Skip drivers marked as comments.
// Skip disabled drivers.
//
if (SkipDriver) {
continue;
@ -127,14 +134,14 @@ OcLoadDrivers (
DriverPath,
sizeof (DriverPath),
OPEN_CORE_UEFI_DRIVER_PATH "%a",
OC_BLOB_GET (Config->Uefi.Drivers.Values[Index])
DriverFileName
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"OC: Driver %s%a does not fit path!\n",
OPEN_CORE_UEFI_DRIVER_PATH,
OC_BLOB_GET (Config->Uefi.Drivers.Values[Index])
DriverFileName
));
continue;
}
@ -144,7 +151,7 @@ OcLoadDrivers (
DEBUG ((
DEBUG_ERROR,
"OC: Driver %a at %u cannot be found!\n",
OC_BLOB_GET (Config->Uefi.Drivers.Values[Index]),
DriverFileName,
Index
));
//
@ -169,7 +176,7 @@ OcLoadDrivers (
DEBUG ((
DEBUG_ERROR,
"OC: Driver %a at %u cannot be loaded - %r!\n",
OC_BLOB_GET (Config->Uefi.Drivers.Values[Index]),
DriverFileName,
Index,
Status
));
@ -177,6 +184,20 @@ OcLoadDrivers (
continue;
}
if (DriverArguments != NULL && DriverArguments[0] != '\0') {
OcAppendArgumentsToLoadedImage (ImageHandle, &DriverArguments, 1, TRUE);
} else {
//
// These are not zeroed by boot services image loader, which means new drivers
// expecting load options loaded with old OC may crash horribly, instead
// of just seeing no options. Annoyingly the value in LoadOptions is non-randome
// and lowish, therefore setting an upper limit on option size, to attempt to
// reject unitialized values, does not help.
//
((EFI_LOADED_IMAGE_PROTOCOL *)ImageHandle)->LoadOptionsSize = 0;
((EFI_LOADED_IMAGE_PROTOCOL *)ImageHandle)->LoadOptions = NULL;
}
Status = gBS->StartImage (
ImageHandle,
NULL,
@ -187,7 +208,7 @@ OcLoadDrivers (
DEBUG ((
DEBUG_ERROR,
"OC: Driver %a at %u cannot be started - %r!\n",
OC_BLOB_GET (Config->Uefi.Drivers.Values[Index]),
DriverFileName,
Index,
Status
));
@ -198,7 +219,7 @@ OcLoadDrivers (
DEBUG ((
DEBUG_INFO,
"OC: Driver %a at %u is successfully loaded!\n",
OC_BLOB_GET (Config->Uefi.Drivers.Values[Index]),
DriverFileName,
Index
));
@ -233,7 +254,7 @@ OcLoadDrivers (
DEBUG ((
DEBUG_INFO,
"OC: Driver %a at %u needs connection.\n",
OC_BLOB_GET (Config->Uefi.Drivers.Values[Index]),
DriverFileName,
Index
));
}

View File

@ -1,6 +1,6 @@
## @file
#
# Component description file for OcMisclibrary.
# Component description file for OcMiscLib.
#
# Copyright (C) 2016 - 2018, The HermitCrabs Lab. All rights reserved.<BR>
#

View File

@ -1,6 +1,6 @@
## @file
#
# Component description file for OcMisclibrary.
# Component description file for OcMp3Lib.
#
# Copyright (C) 2016 - 2018, The HermitCrabs Lab. All rights reserved.<BR>
#

View File

@ -526,3 +526,20 @@ OcAsciiPrintBuffer (
AsciiStrCatS (*AsciiBuffer, *AsciiBufferSize, Tmp);
}
}
CHAR8 *
OcAsciiToLower (
CHAR8 *Str
)
{
UINTN Index;
ASSERT (Str != NULL);
for (Index = 0; Str[Index] != '\0'; ++Index) {
if (Str[Index] >= 'A' && Str[Index] <= 'Z') {
Str[Index] -= ('A' - 'a');
}
}
return Str;
}

View File

@ -37,9 +37,11 @@
[Packages]
OpenCorePkg/OpenCorePkg.dec
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
MemoryAllocationLib
PrintLib
SortLib

View File

@ -21,6 +21,7 @@
#include <Library/OcStringLib.h>
#include <Library/PrintLib.h>
#include <Library/PcdLib.h>
#include <Library/SortLib.h>
INTN
EFIAPI
@ -503,3 +504,21 @@ MixedStrCmp (
return *FirstString - *SecondString;
}
INTN
EFIAPI
OcReverseStringCompare (
IN CONST VOID *Buffer1,
IN CONST VOID *Buffer2
)
{
return -StringCompare (Buffer1, Buffer2);
}
BOOLEAN
OcIsSpace (
CHAR16 Ch
)
{
return (Ch == L' ') || (Ch == L'\t') || (Ch == L'\r') || (Ch == L'\n') || (Ch == L'\v') || (Ch == L'\f');
}

View File

@ -504,6 +504,9 @@
## Include/Acidanthera/Protocol/OcForceResolution.h
gOcForceResolutionProtocolGuid = { 0xBC7EC589, 0x2390, 0x4DA3, { 0x80, 0x25, 0x77, 0xDA, 0xD3, 0x4F, 0x36, 0x09 }}
## Include/Acidanthera/Protocol/OcBootEntry.h
gOcBootEntryProtocolGuid = { 0x8604716E, 0xADD4, 0x45B4, { 0x84, 0x95, 0x08, 0xE3, 0x6D, 0x49, 0x7F, 0x4F }}
## Include/AMI/Protocol/AmiPointer.h
gAmiEfiPointerProtocolGuid = { 0x15A10CE7, 0xEAB5, 0x43BF, { 0x90, 0x42, 0x74, 0x43, 0x2E, 0x69, 0x63, 0x77 }}
@ -849,6 +852,9 @@
## @libraryclass
OcFirmwareVolumeLib|Include/Acidanthera/Library/OcFirmwareVolumeLib.h
## @libraryclass
OcFlexArrayLib|Include/Acidanthera/Library/OcFlexArrayLib.h
## @libraryclass
OcGuardLib|Include/Acidanthera/Library/OcGuardLib.h

View File

@ -87,6 +87,7 @@
OcFileLib|OpenCorePkg/Library/OcFileLib/OcFileLib.inf
OcFirmwarePasswordLib|OpenCorePkg/Library/OcFirmwarePasswordLib/OcFirmwarePasswordLib.inf
OcFirmwareVolumeLib|OpenCorePkg/Library/OcFirmwareVolumeLib/OcFirmwareVolumeLib.inf
OcFlexArrayLib|OpenCorePkg/Library/OcFlexArrayLib/OcFlexArrayLib.inf
OcGuardLib|OpenCorePkg/Library/OcGuardLib/OcGuardLib.inf
OcHashServicesLib|OpenCorePkg/Library/OcHashServicesLib/OcHashServicesLib.inf
OcHdaDevicesLib|OpenCorePkg/Library/OcHdaDevicesLib/OcHdaDevicesLib.inf
@ -229,6 +230,7 @@
OpenCorePkg/Library/OcFileLib/OcFileLib.inf
OpenCorePkg/Library/OcFirmwarePasswordLib/OcFirmwarePasswordLib.inf
OpenCorePkg/Library/OcFirmwareVolumeLib/OcFirmwareVolumeLib.inf
OpenCorePkg/Library/OcFlexArrayLib/OcFlexArrayLib.inf
OpenCorePkg/Library/OcGuardLib/OcGuardLib.inf
OpenCorePkg/Library/OcHashServicesLib/OcHashServicesLib.inf
OpenCorePkg/Library/OcHdaDevicesLib/OcHdaDevicesLib.inf
@ -259,6 +261,7 @@
OpenCorePkg/Library/OcXmlLib/OcXmlLib.inf
OpenCorePkg/Platform/CrScreenshotDxe/CrScreenshotDxe.inf
OpenCorePkg/Platform/OpenCanopy/OpenCanopy.inf
OpenCorePkg/Platform/OpenLinuxBoot/OpenLinuxBoot.inf
OpenCorePkg/Platform/OpenPartitionDxe/PartitionDxe.inf
OpenCorePkg/Platform/OpenRuntime/OpenRuntime.inf
OpenCorePkg/Platform/OpenUsbKbDxe/UsbKbDxe.inf

View File

@ -0,0 +1,693 @@
/** @file
vmlinuz and initramfs/initrd autodetect.
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include "LinuxBootInternal.h"
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/DevicePathLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcBootManagementLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcFileLib.h>
#include <Library/OcFlexArrayLib.h>
#include <Library/OcStringLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/OcBootEntry.h>
#define GRUB_DEFAULT_FILE L"\\etc\\default\\grub"
#define OS_RELEASE_FILE L"\\etc\\os-release"
#define AUTODETECT_DIR L"\\boot"
#define ROOT_FS_FILE L"\\bin\\sh"
STATIC
OC_FLEX_ARRAY
*mVmlinuzFiles;
STATIC
OC_FLEX_ARRAY
*mInitrdFiles;
STATIC
OC_FLEX_ARRAY
*mEtcOsReleaseOptions;
STATIC
CHAR8
*mPrettyName;
STATIC
OC_FLEX_ARRAY
*mEtcDefaultGrubOptions;
STATIC
EFI_STATUS
ProcessVmlinuzFile (
EFI_FILE_HANDLE Directory,
EFI_FILE_INFO *FileInfo,
UINTN FileInfoSize,
VOID *Context OPTIONAL
)
{
CHAR16 *Dash;
VMLINUZ_FILE *VmlinuzFile;
if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
return EFI_NOT_FOUND;
}
//
// Do not use files without '-' in the name, i.e. we do not need and
// do not try to use `vmlinuz` or `initrd` symlinks even if present
// (and even though we can in fact specify them as filenames and boot
// fine from them).
//
Dash = OcStrChr (FileInfo->FileName, L'-');
if (Dash == NULL || Dash[1] == L'\0') {
return EFI_NOT_FOUND;
}
if (StrnCmp (L"vmlinuz", FileInfo->FileName, L_STR_LEN (L"vmlinuz")) == 0) {
VmlinuzFile = OcFlexArrayAddItem (mVmlinuzFiles);
} else if (StrnCmp (L"init", FileInfo->FileName, L_STR_LEN (L"init")) == 0) {
//
// initrd* or initramfs*
//
VmlinuzFile = OcFlexArrayAddItem (mInitrdFiles);
} else {
return EFI_NOT_FOUND;
}
if (VmlinuzFile == NULL) {
return EFI_OUT_OF_RESOURCES;
}
VmlinuzFile->FileName = AllocateCopyPool (StrSize (FileInfo->FileName), FileInfo->FileName);
if (VmlinuzFile->FileName == NULL) {
return EFI_OUT_OF_RESOURCES;
}
VmlinuzFile->Version = &VmlinuzFile->FileName[&Dash[1] - FileInfo->FileName];
VmlinuzFile->StrLen = StrLen (FileInfo->FileName);
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
CreateAsciiRelativePath (
CHAR8 **Dest,
CHAR16 *DirectoryPath,
UINTN DirectoryPathLength,
CHAR16 *FilePath,
UINTN FilePathLength
)
{
UINTN Size;
Size = DirectoryPathLength + FilePathLength + 2;
*Dest = AllocatePool (Size);
if (*Dest == NULL) {
return EFI_OUT_OF_RESOURCES;
}
AsciiSPrint (*Dest, Size, "%s\\%s", DirectoryPath, FilePath);
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
CreateRootPartuuid (
CHAR8 **Dest
)
{
UINTN Length;
UINTN NumPrinted;
Length = L_STR_LEN ("root=PARTUUID=") + OC_EFI_GUID_STR_LEN;
*Dest = AllocatePool (Length + 1);
if (*Dest == NULL) {
return EFI_OUT_OF_RESOURCES;
}
NumPrinted = AsciiSPrint (*Dest, Length + 1, "%a%g", "root=PARTUUID=", gPartuuid);
ASSERT (NumPrinted == Length);
OcAsciiToLower (&(*Dest)[L_STR_LEN ("root=PARTUUID=")]);
return EFI_SUCCESS;
}
STATIC
VOID
AutodetectTitle (
VOID
)
{
UINTN Index;
CHAR8 *AsciiStrValue;
BOOLEAN Found;
mPrettyName = NULL;
if (mEtcOsReleaseOptions != NULL) {
//
// If neither are present, default title gets set later to "Linux".
//
Found = FALSE;
for (Index = 0; Index < 2; Index++) {
if (OcParsedVarsGetAsciiStr (
mEtcOsReleaseOptions,
Index == 0 ? "PRETTY_NAME" : "NAME",
&AsciiStrValue
) &&
AsciiStrValue != NULL) {
mPrettyName = AsciiStrValue;
Found = TRUE;
break;
}
}
if (Found) {
DEBUG ((DEBUG_INFO, "LNX: Found distro %a\n", mPrettyName));
} else {
DEBUG ((DEBUG_WARN, "LNX: Neither %a nor %a found in %s\n", "PRETTY_NAME", "NAME", OS_RELEASE_FILE));
}
}
}
STATIC
EFI_STATUS
LoadEtcFiles (
IN CONST EFI_FILE_PROTOCOL *RootDirectory
)
{
EFI_STATUS Status;
CHAR8 *Contents;
Status = EFI_SUCCESS;
mEtcOsReleaseOptions = NULL;
//
// Load distro name from /etc/os-release.
//
Contents = OcReadFileFromDirectory (RootDirectory, OS_RELEASE_FILE, NULL, 0);
if (Contents == NULL) {
DEBUG ((DEBUG_WARN, "LNX: %s not found\n", OS_RELEASE_FILE));
} else {
DEBUG ((DEBUG_INFO, "LNX: Reading %s\n", OS_RELEASE_FILE));
Status = OcParseVars (Contents, &mEtcOsReleaseOptions, FALSE);
if (EFI_ERROR (Status)) {
FreePool (Contents);
DEBUG ((DEBUG_WARN, "LNX: Cannot parse %s - %r\n", OS_RELEASE_FILE, Status));
return Status;
}
//
// Do this early purely to give a nicer log entry order - distro is named
// before reports about it (esp. e.g. error below if it is not GRUB-based).
//
AutodetectTitle ();
}
//
// Load kernel options from /etc/default/grub.
//
Contents = OcReadFileFromDirectory (RootDirectory, GRUB_DEFAULT_FILE, NULL, 0);
if (Contents == NULL) {
DEBUG ((DEBUG_WARN, "LNX: %s not found (bootloader is not GRUB?)\n", GRUB_DEFAULT_FILE));
} else {
DEBUG ((DEBUG_INFO, "LNX: Reading %s\n", GRUB_DEFAULT_FILE));
Status = OcParseVars (Contents, &mEtcDefaultGrubOptions, FALSE);
if (EFI_ERROR (Status)) {
FreePool (Contents);
DEBUG ((DEBUG_WARN, "LNX: Cannot parse %s - %r\n", GRUB_DEFAULT_FILE, Status));
return Status;
}
}
return EFI_SUCCESS;
}
STATIC
VOID
FreeEtcFiles (
VOID
)
{
OcFlexArrayFree (&mEtcOsReleaseOptions);
OcFlexArrayFree (&mEtcDefaultGrubOptions);
}
STATIC
EFI_STATUS
InsertOption (
IN CONST UINTN InsertIndex,
IN OC_FLEX_ARRAY *Options,
IN CONST VOID *Value,
IN CONST BOOLEAN IsUnicode
)
{
EFI_STATUS Status;
UINTN OptionsLength;
UINTN CopiedLength;
CHAR8 **Option;
if (IsUnicode) {
OptionsLength = StrLen (Value);
} else {
OptionsLength = AsciiStrLen (Value);
}
if (OptionsLength > 0) {
Option = OcFlexArrayInsertItem (Options, InsertIndex);
if (Option == NULL) {
return EFI_OUT_OF_RESOURCES;
}
if (IsUnicode) {
*Option = AllocatePool ((OptionsLength + 1) * sizeof (CHAR16));
if (*Option == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = UnicodeStrnToAsciiStrS (Value, OptionsLength, *Option, OptionsLength + 1, &CopiedLength);
ASSERT (!EFI_ERROR (Status));
ASSERT (CopiedLength == OptionsLength);
} else {
*Option = AllocateCopyPool (OptionsLength + 1, Value);
if (*Option == NULL) {
return EFI_OUT_OF_RESOURCES;
}
}
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
AddOption (
IN OC_FLEX_ARRAY *Options,
IN CONST VOID *Value,
IN CONST BOOLEAN IsUnicode
)
{
return InsertOption (Options->Count, Options, Value, IsUnicode);
}
//
// TODO: Options for rescue versions. Would it be better e.g. just to add "ro" and nothing else?
// However on some installs (e.g. where modules to load are specified in the kernel opts) this
// would not boot at all.
// Maybe upgrade to partuuidopts:{partuuid}r="...": user options for rescue kernels on specified partuuid?
//
STATIC
EFI_STATUS
AutodetectBootOptions (
IN CONST BOOLEAN IsRescue,
IN OC_FLEX_ARRAY *Options
)
{
EFI_STATUS Status;
UINTN Index;
UINTN InsertIndex;
OC_PARSED_VAR *Option;
EFI_GUID Guid;
CHAR8 *AsciiStrValue;
BOOLEAN Found;
if ((gLinuxBootFlags & LINUX_BOOT_ADD_RO) != 0) {
DEBUG ((OC_TRACE_KERNEL_OPTS, "LNX: Adding \"ro\"\n"));
Status = AddOption (Options, "ro", FALSE);
}
Found = FALSE;
InsertIndex = Options->Count;
//
// Look for user-specified options for this partuuid.
// Remember that although args are ASCII in the OC config file, they are
// Unicode by the time they get passed as UEFI LoadOptions.
//
for (Index = 0; Index < gParsedLoadOptions->Count; Index++) {
Option = OcFlexArrayItemAt (gParsedLoadOptions, Index);
//
// partuuidopts:{partuuid}[+]="...": user options for specified partuuid.
//
if (OcUnicodeStartsWith (Option->Unicode.Name, L"partuuidopts:", TRUE)) {
if (Option->Unicode.Value == NULL) {
DEBUG ((DEBUG_WARN, "LNX: Missing value for %s\n", Option->Unicode.Name));
continue;
}
Status = StrToGuid (&Option->Unicode.Name[L_STR_LEN(L"partuuidopts:")], &Guid);
if (EFI_ERROR(Status)) {
DEBUG ((DEBUG_WARN, "LNX: Cannot parse partuuid from %s - %r\n", Option->Unicode.Name, Status));
continue;
}
if (CompareMem (&gPartuuid, &Guid, sizeof (EFI_GUID)) != 0) {
DEBUG ((OC_TRACE_KERNEL_OPTS, "LNX: No match %g != %g\n", &gPartuuid, &Guid));
} else {
DEBUG ((OC_TRACE_KERNEL_OPTS, "LNX: Using partuuidopts=\"%s\"\n", Option->Unicode.Value));
Status = AddOption (Options, Option->Unicode.Value, TRUE);
if (EFI_ERROR (Status)) {
return Status;
}
//
// partuuidopts:{partuuid}+="...": use user options in addition to detected options.
//
if (!OcUnicodeEndsWith (Option->Unicode.Name, L"+", FALSE)) {
return EFI_SUCCESS;
}
Found = TRUE;
}
}
}
//
// Use options from GRUB default location.
//
if (mEtcDefaultGrubOptions != NULL) {
//
// If both are present both should be added, standard grub scripts add them
// in this order.
// Rescue should only use GRUB_CMDLINE_LINUX so this is correct as
// far as it goes; however note that rescue options are unfortunately not
// normally stored here, but are generated in the depths of grub scripts.
//
for (Index = 0; Index < (IsRescue ? 1u : 2u); Index++) {
if (OcParsedVarsGetAsciiStr (
mEtcDefaultGrubOptions,
Index == 0 ? "GRUB_CMDLINE_LINUX" : "GRUB_CMDLINE_LINUX_DEFAULT",
&AsciiStrValue
) &&
AsciiStrValue != NULL) {
//
// Insert these after "ro" but before "partuuidopts+".
//
if (AsciiStrValue[0] != '\0') {
Status = InsertOption (InsertIndex, Options, AsciiStrValue, FALSE);
if (EFI_ERROR (Status)) {
return Status;
}
InsertIndex++;
}
//
// Empty string value is good enough for found: we are operating
// from GRUB cfg files rather than pure guesswork.
//
Found = TRUE;
}
}
}
//
// Use global defaults, if user has defined any.
//
for (Index = 0; Index < gParsedLoadOptions->Count; Index++) {
Option = OcFlexArrayItemAt (gParsedLoadOptions, Index);
if (!Found && StrCmp (Option->Unicode.Name, L"autoopts") == 0) {
if (Option->Unicode.Value == NULL) {
DEBUG ((DEBUG_WARN, "LNX: Missing value for %s\n", Option->Unicode.Name));
continue;
}
Status = AddOption (Options, Option->Unicode.Value, TRUE);
return Status;
} else if (StrCmp (Option->Unicode.Name, L"autoopts+") == 0) {
if (Option->Unicode.Value == NULL) {
DEBUG ((DEBUG_WARN, "LNX: Missing value for %s\n", Option->Unicode.Name));
continue;
}
Status = AddOption (Options, Option->Unicode.Value, TRUE);
if (EFI_ERROR (Status)) {
return Status;
}
Found = TRUE;
}
}
//
// It might be valid to have no options except "ro", but at least empty
// string "GRUB_CMDLINE_LINUX" needs to be present in that case or we stop.
//
if (!Found) {
DEBUG ((DEBUG_WARN, "LNX: No grub default or user defined options - aborting\n"));
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
GenerateEntriesForVmlinuzFiles (
IN CHAR16 *DirectoryPath
)
{
EFI_STATUS Status;
UINTN VmlinuzIndex;
UINTN InitrdIndex;
UINTN ShortestMatch;
UINTN DirectoryPathLength;
NAMED_LOADER_ENTRY *NamedEntry;
LOADER_ENTRY *Entry;
VMLINUZ_FILE *VmlinuzFile;
VMLINUZ_FILE *InitrdFile;
VMLINUZ_FILE *InitrdMatch;
CHAR8 **Option;
BOOLEAN IsRescue;
ASSERT (DirectoryPath != NULL);
DirectoryPathLength = StrLen (DirectoryPath);
for (VmlinuzIndex = 0; VmlinuzIndex < mVmlinuzFiles->Count; VmlinuzIndex++) {
VmlinuzFile = OcFlexArrayItemAt (mVmlinuzFiles, VmlinuzIndex);
IsRescue = FALSE;
if (OcUnicodeStartsWith (VmlinuzFile->Version, L"0", FALSE)
|| StrStr (VmlinuzFile->Version, L"rescue") != NULL
|| StrStr (VmlinuzFile->Version, L"recovery") != NULL) {
//
// We might have to scan /boot/grb/grub.cfg as grub os-prober does if
// we want to find rescue version options, or we need to find a way
// for user to pass these in, since they are generated in the depths
// of the grub scripts, and in typical distros are not present in
// /etc/default/grub, even though it looks as if they could be.
//
IsRescue = TRUE;
DEBUG ((DEBUG_INFO, "LNX: %s=rescue\n", VmlinuzFile->Version));
}
ShortestMatch = MAX_UINTN;
InitrdMatch = NULL;
//
// Find shortest init* filename containing the same version string.
//
for (InitrdIndex = 0; InitrdIndex < mInitrdFiles->Count; InitrdIndex++) {
InitrdFile = OcFlexArrayItemAt (mInitrdFiles, InitrdIndex);
if (InitrdFile->StrLen < ShortestMatch) {
if (StrStr (InitrdFile->Version, VmlinuzFile->Version) != NULL) {
InitrdMatch = InitrdFile;
ShortestMatch = InitrdFile->StrLen;
}
}
}
Entry = InternalAllocateLoaderEntry ();
if (Entry == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Linux.
//
Status = CreateAsciiRelativePath (&Entry->Linux, DirectoryPath, DirectoryPathLength, VmlinuzFile->FileName, VmlinuzFile->StrLen);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Version.
//
Entry->Version = AllocateCopyPool (
VmlinuzFile->StrLen - (VmlinuzFile->Version - VmlinuzFile->FileName) + 1,
&Entry->Linux[VmlinuzFile->Version - VmlinuzFile->FileName + DirectoryPathLength + 1]
);
if (Entry->Version == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// FileName & Id.
//
NamedEntry = InternalCreateNamedLoaderEntry (Entry, VmlinuzFile->FileName);
if (NamedEntry == NULL) {
InternalFreeLoaderEntry (&Entry);
return EFI_OUT_OF_RESOURCES;
} else {
//
// Named entry filename - do not free twice.
//
VmlinuzFile->FileName = NULL;
}
//
// Use title from os-release file.
//
Entry->Title = AllocateCopyPool (AsciiStrSize (mPrettyName), mPrettyName);
if (Entry->Title == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Initrd.
//
if (InitrdMatch == NULL) {
//
// No need for WARN, where initrd was required user will see clear (and safe) warning from Linux kernel.
//
DEBUG ((DEBUG_INFO, "LNX: No matching initrd/initramfs file found for %a\n", Entry->Linux));
} else {
Option = OcFlexArrayAddItem (Entry->Initrds);
if (Option == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = CreateAsciiRelativePath (Option, DirectoryPath, DirectoryPathLength, InitrdMatch->FileName, InitrdMatch->StrLen);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// root=PARTUUID=... option.
//
Option = OcFlexArrayAddItem (Entry->Options);
if (Option == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = CreateRootPartuuid (Option);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Remaining options.
//
Status = AutodetectBootOptions (IsRescue, Entry->Options);
if (EFI_ERROR (Status)) {
return Status;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
AutodetectLinux (
IN EFI_FILE_PROTOCOL *RootDirectory,
OUT OC_PICKER_ENTRY **Entries,
OUT UINTN *NumEntries
)
{
EFI_STATUS Status;
EFI_FILE_PROTOCOL *VmlinuzDirectory;
EFI_FILE_PROTOCOL *RootFsFile;
//
// For now we are only searching in /boot.
// vmlinuz files in / should not require autodetect, as
// they should be accompanied by /loader/entries (Fedora-style),
// and vmlinuz files in /boot not accompanied by /loader/entries
// is Debian-style, so it seems sensible to wait to see what
// else there is rather than speculatively adding directories.
//
Status = OcSafeFileOpen (RootDirectory, &VmlinuzDirectory, AUTODETECT_DIR, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR (Status)) {
return Status;
}
mVmlinuzFiles = NULL;
mInitrdFiles = NULL;
Status = OcSafeFileOpen (RootDirectory, &RootFsFile, ROOT_FS_FILE, EFI_FILE_MODE_READ, 0);
if (!EFI_ERROR (Status)) {
Status = OcEnsureDirectory (RootFsFile, FALSE);
RootFsFile->Close (RootFsFile);
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "LNX: Does not appear to be root filesystem - %r\n", Status));
}
if (!EFI_ERROR (Status)){
mVmlinuzFiles = OcFlexArrayInit (sizeof (VMLINUZ_FILE), OcFlexArrayFreePointerItem);
if (mVmlinuzFiles == NULL) {
Status = EFI_OUT_OF_RESOURCES;
}
}
if (!EFI_ERROR (Status)){
mInitrdFiles = OcFlexArrayInit (sizeof (VMLINUZ_FILE), OcFlexArrayFreePointerItem);
if (mInitrdFiles == NULL) {
Status = EFI_OUT_OF_RESOURCES;
}
}
//
// Place vmlinuz* and init* files into arrays.
//
if (!EFI_ERROR (Status)){
Status = OcScanDirectory (VmlinuzDirectory, ProcessVmlinuzFile, NULL);
}
if (!EFI_ERROR (Status)) {
gNamedLoaderEntries = OcFlexArrayInit (sizeof (NAMED_LOADER_ENTRY), (OC_FLEX_ARRAY_FREE_ITEM) InternalFreeNamedLoaderEntry);
if (gNamedLoaderEntries == NULL) {
Status = EFI_OUT_OF_RESOURCES;
} else {
Status = LoadEtcFiles (RootDirectory);
if (!EFI_ERROR (Status)) {
Status = GenerateEntriesForVmlinuzFiles (AUTODETECT_DIR);
}
FreeEtcFiles();
}
if (!EFI_ERROR (Status)) {
Status = InternalConvertNamedLoaderEntriesToBootEntries (
RootDirectory,
Entries,
NumEntries
);
}
OcFlexArrayFree (&gNamedLoaderEntries);
}
if (mVmlinuzFiles != NULL) {
OcFlexArrayFree (&mVmlinuzFiles);
}
if (mInitrdFiles != NULL) {
OcFlexArrayFree (&mInitrdFiles);
}
VmlinuzDirectory->Close (VmlinuzDirectory);
return Status;
}

View File

@ -0,0 +1,472 @@
/** @file
Naive GRUB config parser.
Attemps to respect GRUB escape and line continuation syntax, and
then to extract GRUB set commands for some basic processing.
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include "LinuxBootInternal.h"
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcMiscLib.h>
#include <Library/OcStringLib.h>
/*
grub.cfg processing states.
*/
typedef enum GRUB_PARSE_STATE_ {
GRUB_LEADING_SPACE,
GRUB_COMMENT,
GRUB_TOKEN,
GRUB_SINGLE_QUOTE,
GRUB_DOUBLE_QUOTE
} GRUB_PARSE_STATE;
/*
grub.cfg $var processing states.
*/
typedef enum GRUB_VAR_STATE_ {
GRUB_VAR_NONE,
GRUB_VAR_START,
GRUB_VAR_END,
GRUB_VAR_CHAR
} GRUB_VAR_STATE;
#define GRUB_LINE "in grub.cfg at line"
/*
grub.cfg $var processing flags.
*/
#define VAR_FLAGS_NONE (0)
#define VAR_FLAGS_BRACE BIT0
#define VAR_FLAGS_NUMERIC BIT1
#define SHIFT_TOKEN(offset) { \
CopyMem (*Token + (offset), *Token, &Content[*Pos] - *Token); \
*Token += (offset); \
}
STATIC
EFI_STATUS
GrubNextToken (
CHAR8 *Content,
UINTN *Pos,
UINTN *Line,
CHAR8 **Token,
BOOLEAN *IsIndented,
BOOLEAN *ContainsVars
)
{
GRUB_PARSE_STATE GrubState;
GRUB_VAR_STATE VarState;
UINTN GrubVarFlags;
BOOLEAN Escaped;
BOOLEAN TokenCompleted;
BOOLEAN Retake;
CHAR8 Ch;
CHAR8 Ch2;
UINTN Add;
*Token = NULL;
*IsIndented = FALSE;
*ContainsVars = FALSE;
GrubState = GRUB_LEADING_SPACE;
VarState = GRUB_VAR_NONE;
Escaped = FALSE;
TokenCompleted = FALSE;
Retake = FALSE;
do {
Ch = Content[*Pos];
if (Ch == '\n') {
*Line += 1;
} else if (!(Ch == '\0' || Ch == '\t' || (Ch >= 32 && Ch <= 127))) {
DEBUG ((DEBUG_WARN, "LNX: Invalid char 0x%x %a %u\n", Ch, GRUB_LINE, Line));
return EFI_INVALID_PARAMETER;
}
//
// Deal with escape char and line continuation.
//
if (Ch == '\\') {
Ch2 = Content[(*Pos) + 1];
if (Ch2 == '\0' || Ch2 == '\n') {
//
// Line continuation.
//
if (VarState != GRUB_VAR_NONE) {
//
// We could handle this fine (just remove this check), but GRUB doesn't:
// https://www.gnu.org/software/grub/manual/grub/html_node/grub_fot.html#FOOT7
//
DEBUG ((DEBUG_WARN, "LNX: Illegal line continuation within variable name %a %u\n", GRUB_LINE, Line));
return EFI_INVALID_PARAMETER;
}
//
// '\n' go to char afterwards, '\0' go to '\0'.
//
Add = (Ch2 == '\n' ? 2 : 1);
SHIFT_TOKEN (Add);
*Pos += Add;
Ch = Content[*Pos];
*Line += 1;
} else {
//
// Escapes.
//
switch (GrubState) {
//
// No escapes in single quote.
//
case GRUB_SINGLE_QUOTE:
break;
//
// Only these escapes in double quote.
//
case GRUB_DOUBLE_QUOTE:
if (Ch2 == '$' || Ch2 == '"') {
Escaped = TRUE;
}
break;
//
// Anything can be escaped.
//
default:
Escaped = TRUE;
break;
}
if (Escaped) {
SHIFT_TOKEN (1);
++(*Pos);
Ch = Ch2;
Escaped = FALSE;
}
}
}
//
// Grub var is a special state which can be entered within other states.
// Allowed: $?, $@, $#, $nnn, $alphanumeric
//
if (VarState != GRUB_VAR_NONE) {
ASSERT (GrubState == GRUB_TOKEN || GrubState == GRUB_SINGLE_QUOTE || GrubState == GRUB_DOUBLE_QUOTE);
switch (VarState) {
case GRUB_VAR_START:
//
// The fact that a token contains a var reference means we cannot use it;
// we are looking for tokens which define vars, not ones which use them.
//
*ContainsVars = TRUE;
if (Ch == '{') {
if ((GrubVarFlags & VAR_FLAGS_BRACE) != 0) {
DEBUG ((DEBUG_WARN, "LNX: Illegal character in variable name %a %u\n", GRUB_LINE, Line));
return EFI_INVALID_PARAMETER;
}
GrubVarFlags |= VAR_FLAGS_BRACE;
} else if (Ch == '}') {
//
// Empty var name is valid.
//
if ((GrubVarFlags & VAR_FLAGS_BRACE) == 0) {
DEBUG ((DEBUG_WARN, "LNX: Illegal character in variable name %a %u\n", GRUB_LINE, Line));
return EFI_INVALID_PARAMETER;
}
VarState = GRUB_VAR_NONE;
} else if (Ch == '@' || Ch == '?' || Ch == '#') {
VarState = GRUB_VAR_END;
} else if (IS_DIGIT (Ch)) {
GrubVarFlags |= VAR_FLAGS_NUMERIC;
VarState = GRUB_VAR_CHAR;
} else if (Ch == '_' || IS_ALPHA (Ch)) {
VarState = GRUB_VAR_CHAR;
} else {
DEBUG ((DEBUG_WARN, "LNX: Illegal character in variable name %a %u\n", GRUB_LINE, Line));
return EFI_INVALID_PARAMETER;
}
break;
case GRUB_VAR_END:
if ((GrubVarFlags & VAR_FLAGS_BRACE) != 0) {
if (Ch != '}') {
DEBUG ((DEBUG_WARN, "LNX: Illegal character in variable name %a %u\n", GRUB_LINE, Line));
return EFI_INVALID_PARAMETER;
}
} else {
Retake = TRUE;
}
VarState = GRUB_VAR_NONE;
break;
case GRUB_VAR_CHAR:
if (!(IS_DIGIT (Ch) ||
((GrubVarFlags & VAR_FLAGS_NUMERIC) == 0
&& (Ch == '_' || IS_ALPHA (Ch))))) {
VarState = GRUB_VAR_END;
Retake = TRUE;
}
break;
default:
ASSERT (FALSE);
break;
}
} else {
switch (GrubState) {
case GRUB_LEADING_SPACE:
if (Ch == '\0' || Ch == '\n' || (!Escaped && Ch == ';')) {
TokenCompleted = TRUE;
Retake = TRUE;
} else if (Ch == ' ' || Ch == '\t') {
*IsIndented = TRUE;
} else if (Ch == '#') {
GrubState = GRUB_COMMENT;
} else {
*Token = &Content[*Pos];
GrubState = GRUB_TOKEN;
Retake = TRUE;
}
break;
case GRUB_COMMENT:
if (Ch == '\n' || Ch == '\0') {
TokenCompleted = TRUE;
Retake = TRUE;
}
break;
case GRUB_TOKEN:
if (Ch == '\n' || Ch == '\0' || (!Escaped && (Ch == ';' || Ch == ' ' || Ch == '\t'))) {
TokenCompleted = TRUE;
Retake = TRUE;
} else if (!Escaped && Ch == '\'') {
SHIFT_TOKEN (1);
GrubState = GRUB_SINGLE_QUOTE;
} else if (!Escaped && Ch == '"') {
SHIFT_TOKEN (1);
GrubState = GRUB_DOUBLE_QUOTE;
} else if (!Escaped && Ch == '$') {
VarState = GRUB_VAR_START;
GrubVarFlags = VAR_FLAGS_NONE;
}
break;
case GRUB_SINGLE_QUOTE:
if (Ch == '\'') {
SHIFT_TOKEN (1);
GrubState = GRUB_TOKEN;
} else if (Ch == '$') {
VarState = GRUB_VAR_START;
GrubVarFlags = VAR_FLAGS_NONE;
}
break;
case GRUB_DOUBLE_QUOTE:
if (!Escaped && Ch == '"') {
SHIFT_TOKEN (1);
GrubState = GRUB_TOKEN;
} else if (!Escaped && Ch == '$') {
VarState = GRUB_VAR_START;
GrubVarFlags = VAR_FLAGS_NONE;
}
break;
}
}
if (Retake) {
Retake = FALSE;
} else if (Ch != '\0') {
++(*Pos);
}
}
while (Ch != '\0' && !TokenCompleted);
if (!TokenCompleted && GrubState != GRUB_LEADING_SPACE) {
DEBUG ((DEBUG_WARN, "LNX: Syntax error (state=%u) %a %u\n", GrubState, GRUB_LINE, Line));
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
STATIC
BOOLEAN
GrubNextLine (
CHAR8 Ch,
UINTN *Pos
)
{
if (Ch == '\0') {
return TRUE;
}
if (Ch == ';' || Ch == '\n') {
(*Pos)++;
return TRUE;
}
ASSERT (Ch == ' ' || Ch == '\t');
(*Pos)++;
return FALSE;
}
STATIC
EFI_STATUS
SetVar (
UINTN Line,
CHAR8 *Token,
BOOLEAN IsIndented,
BOOLEAN ContainsVars
)
{
EFI_STATUS Status;
CHAR8 *Equals;
CHAR8 *Dollar;
UINTN VarStatus;
//
// Note: It is correct grub2 parsing to treat these tokens with = in (whether after set or not) as one token.
//
Equals = OcAsciiStrChr (Token, '=');
if (Equals == NULL) {
DEBUG ((DEBUG_WARN, "LNX: Invalid set command %a %u\n", GRUB_LINE, Line));
return EFI_INVALID_PARAMETER;
}
*Equals = '\0';
Dollar = OcAsciiStrChr (Token, '$');
if (Dollar != NULL) {
//
// Non-typical but valid GRUB syntax to use variable replacements within
// variable name; we don't know what the name is (and are probably pretty
// unlikely to find the required values in valid, non-indented variables
// which we do know), so we ignore it.
//
DEBUG ((DEBUG_WARN, "LNX: Ignoring tokenised %a %a %a %u\n", "variable name", Token, GRUB_LINE, Line));
return EFI_SUCCESS;
}
VarStatus = 0;
if (IsIndented) {
VarStatus |= VAR_ERR_INDENTED;
}
if (ContainsVars) {
VarStatus |= VAR_ERR_HAS_VARS;
}
Status = InternalSetGrubVar (Token, Equals + 1, VarStatus);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
EFI_STATUS
InternalProcessGrubCfg (
IN OUT CHAR8 *Content
)
{
EFI_STATUS Status;
UINTN Pos;
UINTN Line;
UINTN NextLine;
UINTN TokenIndex;
CHAR8 *Dollar;
CHAR8 *Equals;
CHAR8 *Token;
CHAR8 LastChar;
BOOLEAN IsIndented;
BOOLEAN ContainsVars;
BOOLEAN SetCommand;
BOOLEAN SetIsIndented;
Pos = 0;
Line = 1;
do {
TokenIndex = 0;
SetCommand = FALSE;
do {
//
// Save new line number until we've finished any messages.
//
NextLine = Line;
Status = GrubNextToken (Content, &Pos, &NextLine, &Token, &IsIndented, &ContainsVars);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Terminate token and remember terminator char.
//
LastChar = Content[Pos];
if (Token != NULL) {
Content[Pos] = '\0';
if (TokenIndex == 0) {
//
// Warn on pretty obscure - though valid - syntax of building the command name from variables;
// do not warn for direct setting of grub internal values with no set command, i.e. just name=value,
// where the $ is only in the value.
//
Dollar = OcAsciiStrChr (Token, '$');
if (Dollar != NULL) {
Equals = OcAsciiStrChr (Token, '=');
if (Equals == NULL || Dollar < Equals) {
DEBUG ((DEBUG_WARN, "LNX: Ignoring tokenised %a %a %a %u\n", "command", Token, GRUB_LINE, Line));
}
} else {
//
// No non-indented variables after non-indented blscfg command can be used.
//
if (AsciiStrCmp("blscfg", Token) == 0) {
return EFI_SUCCESS;
}
//
// We could process grub unset command similarly to set, but we probably don't need it.
//
if (AsciiStrCmp("set", Token) == 0) {
SetCommand = TRUE;
SetIsIndented = IsIndented;
}
}
} else if (TokenIndex == 1 && SetCommand) {
Status = SetVar (Line, Token, SetIsIndented, ContainsVars);
if (EFI_ERROR (Status)) {
return Status;
}
}
++TokenIndex;
}
Line = NextLine;
} while (!GrubNextLine (LastChar, &Pos));
} while (LastChar != '\0');
//
// Possibly allow through on flag?
//
DEBUG ((DEBUG_WARN, "LNX: blscfg command not found in grub.cfg\n"));
return EFI_NOT_FOUND;
}

View File

@ -0,0 +1,85 @@
/** @file
GRUB environment block parser.
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Library/BaseLib.h>
#include <Library/OcDebugLogLib.h>
#include "LinuxBootInternal.h"
/*
grubenv processing states.
*/
typedef enum GRUBENV_STATE_ {
GRUBENV_NEXT_LINE,
GRUBENV_KEY,
GRUBENV_VAR,
GRUBENV_COMMENT
} GRUBENV_STATE;
EFI_STATUS
InternalProcessGrubEnv (
IN OUT CHAR8 *Content,
IN CONST UINTN Length
)
{
EFI_STATUS Status;
UINTN Pos;
UINTN KeyStart;
UINTN VarStart;
GRUBENV_STATE State;
State = GRUBENV_NEXT_LINE;
//
// In a valid grubenv block the last comment, if present, is not
// \n terminated, but all var lines must be.
//
for (Pos = 0; Pos < Length && Content[Pos] != '\0'; Pos++) {
switch (State) {
case GRUBENV_NEXT_LINE:
if (Content[Pos] == '#') {
State = GRUBENV_COMMENT;
} else {
KeyStart = Pos;
State = GRUBENV_KEY;
}
break;
case GRUBENV_COMMENT:
if (Content[Pos] == '\n') {
State = GRUBENV_NEXT_LINE;
}
break;
case GRUBENV_KEY:
if (Content[Pos] == '=') {
Content[Pos] = '\0';
VarStart = Pos + 1;
State = GRUBENV_VAR;
}
break;
case GRUBENV_VAR:
if (Content[Pos] == '\n') {
Content[Pos] = '\0';
Status = InternalSetGrubVar (&Content[KeyStart], &Content[VarStart], VAR_ERR_NONE);
if (EFI_ERROR (Status)) {
return Status;
}
State = GRUBENV_NEXT_LINE;
}
break;
default:
ASSERT (FALSE);
break;
}
}
ASSERT (State == GRUBENV_COMMENT || State == GRUBENV_NEXT_LINE);
return EFI_SUCCESS;
}

View File

@ -0,0 +1,273 @@
/** @file
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include "LinuxBootInternal.h"
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcFlexArrayLib.h>
#include <Library/OcStringLib.h>
#include <Library/OcStringLib.h>
#include <Protocol/OcBootEntry.h>
STATIC OC_FLEX_ARRAY *mGrubVars = NULL;
EFI_STATUS
InternalInitGrubVars (
VOID
)
{
mGrubVars = OcFlexArrayInit (sizeof (GRUB_VAR), NULL);
if (mGrubVars == NULL) {
return EFI_OUT_OF_RESOURCES;
}
return EFI_SUCCESS;
}
VOID
InternalFreeGrubVars (
VOID
)
{
if (mGrubVars != NULL) {
OcFlexArrayFree (&mGrubVars);
}
}
EFI_STATUS
InternalSetGrubVar (
CHAR8 *Key,
CHAR8 *Value,
UINTN Errors
)
{
GRUB_VAR *Var;
UINTN Index;
UINTN WereErrors;
ASSERT (mGrubVars != NULL);
ASSERT (Key[0] != '\0');
for (Index = 0; Index < mGrubVars->Count; ++Index) {
Var = OcFlexArrayItemAt (mGrubVars, Index);
if (AsciiStrCmp (Var->Key, Key) == 0) {
break;
}
}
if (Index < mGrubVars->Count) {
Var->Value = Value;
WereErrors = Var->Errors;
//
// Probably not worth the two lines of code (because unlikely to
// occur), but: allow later non-indented no-vars value to overwrite
// earlier non-indented has-vars value and thereby become usable.
//
if ((Errors & VAR_ERR_INDENTED) == 0) {
Var->Errors &= ~VAR_ERR_HAS_VARS;
}
//
// Indentation err stays set because, even if grub.cfg code layout is
// reasonable as we are assuming, we are not parsing enough to tell
// which order (or none) indented vars are set in.
//
Var->Errors |= Errors;
DEBUG ((OC_TRACE_GRUB_VARS,
"LNX: Repeated %a=%a (0x%x->0x%x)\n",
Key,
Value,
WereErrors,
Var->Errors
));
} else {
Var = OcFlexArrayAddItem (mGrubVars);
if (Var == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Var->Key = Key;
Var->Value = Value;
Var->Errors = Errors;
DEBUG ((OC_TRACE_GRUB_VARS,
"LNX: Added %a=%a (0x%x)\n",
Key,
Value,
Errors
));
}
return EFI_SUCCESS;
}
//
// Compatible with Grub2 blscfg's simple definition of what gets replaced,
// cf InternalExpandGrubVars. (If there was more complex logic, it would
// probably make most sense just not to have this pre-check.)
//
BOOLEAN
InternalHasGrubVars (
CHAR8 *Value
)
{
return OcAsciiStrChr (Value, '$') != NULL;
}
GRUB_VAR *
InternalGetGrubVar (
IN CONST CHAR8 *Key
)
{
UINTN Index;
GRUB_VAR *Var;
for (Index = 0; Index < mGrubVars->Count; Index++) {
Var = OcFlexArrayItemAt (mGrubVars, Index);
if (AsciiStrCmp (Var->Key, Key) == 0) {
return Var;
}
}
return NULL;
}
EFI_STATUS
InternalExpandGrubVarsForArray (
IN OUT OC_FLEX_ARRAY *Options
)
{
EFI_STATUS Status;
UINTN Index;
CHAR8 **Value;
CHAR8 *Result;
for (Index = 0; Index < Options->Count; Index++) {
Value = OcFlexArrayItemAt (Options, Index);
if (InternalHasGrubVars (*Value)) {
Status = InternalExpandGrubVars (*Value, &Result);
if (EFI_ERROR (Status)) {
return Status;
}
FreePool (*Value);
*Value = Result;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
InternalExpandGrubVars (
IN CONST CHAR8 *Value,
IN OUT CHAR8 **Result
)
{
EFI_STATUS Status;
UINTN Pos;
UINTN LastPos;
BOOLEAN InVar;
BOOLEAN Retake;
GRUB_VAR *Var;
CHAR8 Ch;
UINTN VarLength;
OC_STRING_BUFFER *StringBuffer;
ASSERT (Value != NULL);
ASSERT (Result != NULL);
*Result = NULL;
if (Value == NULL) {
return EFI_INVALID_PARAMETER;
}
StringBuffer = OcAsciiStringBufferInit ();
if (StringBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Pos = 0;
LastPos = 0;
InVar = FALSE;
Status = EFI_SUCCESS;
//
// These simple checks for what counts as a var to replace (including
// the fact that there is no escape for '$') match Grub2 blscfg module.
//
do {
Ch = Value[Pos];
if (!InVar) {
if (Ch == '$' || Ch == '\0') {
Status = OcAsciiStringBufferAppendN (StringBuffer, &Value[LastPos], Pos - LastPos);
InVar = TRUE;
LastPos = Pos + 1;
}
} else if (!(Ch == '_' || IS_DIGIT (Ch) || IS_ALPHA (Ch))) {
((CHAR8 *)Value)[Pos] = '\0';
Var = InternalGetGrubVar (&Value[LastPos]);
if (Var == NULL) {
DEBUG ((DEBUG_WARN, "LNX: Missing required grub var $%a\n", &Value[LastPos]));
Status = EFI_INVALID_PARAMETER;
} else if (Var->Errors != 0) {
DEBUG ((DEBUG_WARN, "LNX: Unusable grub var $%a - 0x%x\n", &Value[LastPos], Var->Errors));
Status = EFI_INVALID_PARAMETER;
}
((CHAR8 *)Value)[Pos] = Ch;
//
// Blscfg always appends a space to each expanded token (and the var values
// are often set up to end with a space, too), then later GRUB tokenization
// of the options as grub booter options (which we do not currently do - may
// be needed in some escaped cases?) cleans it up. Here, we try to combine
// obvious doubled up spaces right away.
//
if (!EFI_ERROR (Status)) {
if (Var->Value != NULL) {
VarLength = AsciiStrLen (Var->Value);
if (VarLength > 0 && Var->Value[VarLength - 1] == ' ') {
--VarLength;
}
}
Status = OcAsciiStringBufferAppendN (StringBuffer, Var->Value, VarLength);
}
if (!EFI_ERROR (Status) && !(Ch == ' ' || Ch == '\0')) {
Status = OcAsciiStringBufferAppend (StringBuffer, " ");
}
InVar = FALSE;
Retake = TRUE;
LastPos = Pos;
}
if (Retake) {
Retake = FALSE;
} else {
++Pos;
}
}
while (Ch != '\0' && !EFI_ERROR (Status));
*Result = OcAsciiStringBufferFreeContainer (&StringBuffer);
if (EFI_ERROR (Status)) {
if (*Result != NULL) {
FreePool (*Result);
*Result = NULL;
}
}
DEBUG ((OC_TRACE_GRUB_VARS, "LNX: Expanding '%a' => '%a' - %r\n", Value, *Result, Status));
return Status;
}

View File

@ -0,0 +1,360 @@
/** @file
Copyright (C) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#ifndef LINUX_BOOT_INTERNAL_H
#define LINUX_BOOT_INTERNAL_H
#if !defined(OC_TRACE_GRUB_VARS)
#define OC_TRACE_GRUB_VARS DEBUG_VERBOSE
#endif
#if !defined(OC_TRACE_KERNEL_OPTS)
#define OC_TRACE_KERNEL_OPTS DEBUG_VERBOSE
#endif
#include <Uefi.h>
#include <Library/OcBootManagementLib.h>
#include <Library/OcMiscLib.h>
#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
#define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
/*
Allow scan of ESP.
*/
#define LINUX_BOOT_SCAN_ESP BIT0
/*
Allow scan of XBOOTLDR.
*/
#define LINUX_BOOT_SCAN_XBOOTLDR BIT1
/*
Allow scan of Linux Root filesystems.
*/
#define LINUX_BOOT_SCAN_LINUX_ROOT BIT2
/*
Allow scan of Linux Data filesystems.
*/
#define LINUX_BOOT_SCAN_LINUX_DATA BIT3
/*
Some space for additional file systems.
*/
/*
Allow scan of any filesystem not explicitly mentioned
(including but not limited FAT other than ESP, and NTFS).
*/
#define LINUX_BOOT_SCAN_OTHER BIT7
/*
Allow autodetect of vmlinuz-{version} and matching init*-{version},
if scan for usable /loader/entries fails.
*/
#define LINUX_BOOT_ALLOW_AUTODETECT BIT8
/*
Define entry id by the first part (to dash) of the filename
from which it was created. Results in the first matching entry (after sorting)
always being the default entry, which results in updated Linux becoming the
new default automatically.
*/
#define LINUX_BOOT_USE_LATEST BIT9
/*
If set, add "ro" as initial option to all distros. Can be sepcified per
FS by using argument partuuidopts:{partuuid}+=ro instead.
*/
#define LINUX_BOOT_ADD_RO BIT10
/*
Prepend filesystem type and first 8 hex digits of PARTUUID to discovered
entry titles, to help in debugging where entries came from.
*/
#define LINUX_BOOT_ADD_DEBUG_INFO BIT15
#define LINUX_BOOT_ALL ( \
LINUX_BOOT_SCAN_ESP | \
LINUX_BOOT_SCAN_XBOOTLDR | \
LINUX_BOOT_SCAN_LINUX_ROOT | \
LINUX_BOOT_SCAN_LINUX_DATA | \
LINUX_BOOT_SCAN_OTHER | \
LINUX_BOOT_ALLOW_AUTODETECT | \
LINUX_BOOT_USE_LATEST | \
LINUX_BOOT_ADD_RO | \
LINUX_BOOT_ADD_DEBUG_INFO \
)
/*
GRUB var error codes.
*/
#define VAR_ERR_NONE (0)
#define VAR_ERR_INDENTED BIT0 // Naive detection of GRUB conditional logic
#define VAR_ERR_HAS_VARS BIT1 // We do not support nested vars (in name or value), even though GRUB does
/*
Global flags for this instance of OpenLinuxBoot.efi.
*/
extern UINTN gLinuxBootFlags;
/*
Boot picker context.
*/
extern OC_PICKER_CONTEXT *gPickerContext;
/*
Stored parsed load options.
Would be freed at driver unload, if that happened.
*/
extern OC_FLEX_ARRAY *gParsedLoadOptions;
/*
The array of loader entries, either really from *.conf files or generated by autodetect.
*/
extern OC_FLEX_ARRAY *gNamedLoaderEntries;
/*
The current partuuid.
*/
extern EFI_GUID gPartuuid;
/*
Human readable ascii name of current file system type.
*/
extern CHAR8 *gFileSystemType;
// TODO: Are all of the below types used outside a single file?
// TODO: Is this file sensibly ordered?
/*
Forward declaration of GRUB_VAR structure.
*/
typedef struct GRUB_VAR_ GRUB_VAR;
/*
Forward declaration of LOADER_ENTRY structure.
*/
typedef struct LOADER_ENTRY_ LOADER_ENTRY;
/*
Forward declaration of NAMED_LOADER_ENTRY structure.
*/
typedef struct NAMED_LOADER_ENTRY_ NAMED_LOADER_ENTRY;
/*
Forward declaration of VMLINUZ_FILE structure.
*/
typedef struct VMLINUZ_FILE_ VMLINUZ_FILE;
/*
GRUB vars.
*/
EFI_STATUS
InternalInitGrubVars (
VOID
);
VOID
InternalFreeGrubVars (
VOID
);
EFI_STATUS
InternalSetGrubVar (
CHAR8 *Key,
CHAR8 *Value,
UINTN Errors
);
BOOLEAN
InternalHasGrubVars (
CHAR8 *Options
);
GRUB_VAR *
InternalGetGrubVar (
IN CONST CHAR8 *Key
);
EFI_STATUS
InternalExpandGrubVarsForArray (
IN OUT OC_FLEX_ARRAY *Options
);
EFI_STATUS
InternalExpandGrubVars (
IN CONST CHAR8 *Options,
IN OUT CHAR8 **Result
);
/*
Process grubenv file.
*/
EFI_STATUS
InternalProcessGrubEnv (
IN OUT CHAR8 *Content,
IN CONST UINTN Length
);
/*
Process grub.cfg file.
*/
EFI_STATUS
InternalProcessGrubCfg (
IN OUT CHAR8 *Content
);
LOADER_ENTRY *
InternalAllocateLoaderEntry (
VOID
);
VOID
InternalFreeLoaderEntry (
LOADER_ENTRY **Entry
);
EFI_STATUS
InternalProcessLoaderEntryFile (
IN CONST CHAR16 *FileName,
IN OUT CHAR8 *Content,
OUT LOADER_ENTRY **Entry,
IN CONST BOOLEAN Grub2
);
/*
GRUB variable.
*/
struct GRUB_VAR_ {
//
// Points within loaded file memory.
//
CHAR8 *Key;
//
// Points within loaded file memory, may be empty string.
//
CHAR8 *Value;
//
// GRUB var error code flags.
//
UINTN Errors;
};
/*
Loader entries.
*/
NAMED_LOADER_ENTRY *
InternalCreateNamedLoaderEntry (
IN LOADER_ENTRY *Entry,
IN CHAR16 *FileName
);
VOID
InternalFreePickerEntry (
IN OC_PICKER_ENTRY *Entry
);
VOID
InternalFreeNamedLoaderEntry (
NAMED_LOADER_ENTRY *Entry
);
EFI_STATUS
InternalConvertNamedLoaderEntriesToBootEntries (
IN EFI_FILE_PROTOCOL *RootDirectory,
OUT OC_PICKER_ENTRY **Entries,
OUT UINTN *NumEntries
);
EFI_STATUS
ScanLoaderEntries (
IN EFI_FILE_PROTOCOL *RootDirectory,
OUT OC_PICKER_ENTRY **Entries,
OUT UINTN *NumEntries
);
/*
BLSpec / blscfg loader entry.
Some items within here probably don't need to be allocated and could stay
pointing within the source file as long as that is in memory (specifically
Version and Linux, which are probably never going to be modified), but to
keep things sane everything is (re)allocated.
*/
struct LOADER_ENTRY_ {
//
// First(blscfg)/last(sd-boot) title line encountered.
// Otherwise attempted autodetect "{Variant}" (e.g. "Ubuntu", "Fedora") from within kernel image.
// Otherwise "Linux".
//
CHAR8 *Title;
//
// First(blscfg)/last(sd-boot) version line encountered;
// Otherwise version-id from {machine-id}-{kernel-version}.conf or vmlinuz-{kernel-version} filename.
// Otherwise attempted autodetect from within kernel image.
//
CHAR8 *Version;
//
// First(blscfg)/last(sd-boot) linux line encountered.
// Required.
//
CHAR8 *Linux;
//
// Option lines encountered.
// In pure BLSpec (and in sd-boot) all are used; in blscfg only the first option line is used, so we match that.
// TODO: Test starting something (not really a Linux kernel?) with no options and no initrds.
//
OC_FLEX_ARRAY *Options;
//
// Initrd lines encountered.
// (All are used in both blscfg and sd-boot.)
//
OC_FLEX_ARRAY *Initrds;
//
// id line is not read from .conf file even if present.
// OcId is generated from .conf filename, to share machine-id between
// {machine-id}-{kernel-version}.conf files, in order to auto-boot the
// most recent kernel when a new one appears.
//
CHAR8 *OcId;
//
// Autodetect from within kernel image ("{Variant}:Linux").
// Otherwise just "Linux".
//
CHAR8 *OcFlavour;
//
// Is this an auxiliary entry for OpenCore?
//
BOOLEAN OcAuxiliary;
};
//
// Values for NAMED_LOADER_ENTRY DuplicateFlags.
// We previously implemented pure-Boot Loader Spec-style title disambiguation
// only when there is more than one entry with the same name, however
// within OC it works better to always disambiguate when
// HideAuxiliary is FALSE and never otherwise.
//
#define DUPLICATE_ID_SCANNED BIT0
/*
Loader entry associated with filename, for sorting.
*/
struct NAMED_LOADER_ENTRY_ {
CHAR16 *FileName;
LOADER_ENTRY *Entry;
UINTN DuplicateFlags;
};
struct VMLINUZ_FILE_ {
CHAR16 *FileName;
CHAR16 *Version;
UINTN StrLen;
};
/*
Autodetect.
*/
EFI_STATUS
AutodetectLinux (
IN EFI_FILE_PROTOCOL *RootDirectory,
OUT OC_PICKER_ENTRY **Entries,
OUT UINTN *NumEntries
);
#endif // LINUX_BOOT_INTERNAL_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,307 @@
/** @file
Linux boot driver, supporting Boot Loader Specification, GRUB2 blscfg, and autodetect.
Copyright (c) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include "LinuxBootInternal.h"
#include <Uefi.h>
#include <Guid/Gpt.h>
#include <Library/BaseLib.h>
#include <Library/DevicePathLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcBootManagementLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcFileLib.h>
#include <Library/OcFlexArrayLib.h>
#include <Library/OcStringLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/OcBootEntry.h>
UINTN gLinuxBootFlags = LINUX_BOOT_ALL & ~LINUX_BOOT_ADD_DEBUG_INFO;
OC_PICKER_CONTEXT *gPickerContext;
OC_FLEX_ARRAY *gParsedLoadOptions;
OC_FLEX_ARRAY *gNamedLoaderEntries;
EFI_GUID gPartuuid;
CHAR8 *gFileSystemType;
VOID
InternalFreePickerEntry (
IN OC_PICKER_ENTRY *Entry
)
{
ASSERT (Entry != NULL);
if (Entry == NULL) {
return;
}
//
// TODO: Is this un-CONST casting okay?
// (Are they CONST because they are not supposed to be freed when used as before?)
//
if (Entry->Id != NULL) {
FreePool ((CHAR8 *)Entry->Id);
}
if (Entry->Name != NULL) {
FreePool ((CHAR8 *)Entry->Name);
}
if (Entry->Path != NULL) {
FreePool ((CHAR8 *)Entry->Path);
}
if (Entry->Arguments != NULL) {
FreePool ((CHAR8 *)Entry->Arguments);
}
if (Entry->Flavour != NULL) {
FreePool ((CHAR8 *)Entry->Flavour);
}
}
STATIC
VOID
EFIAPI
OcFreeLinuxBootEntries (
IN OC_PICKER_ENTRY **Entries,
IN UINTN NumEntries
)
{
UINTN Index;
ASSERT (Entries != NULL);
ASSERT (*Entries != NULL);
if (Entries == NULL || *Entries == NULL) {
return;
}
for (Index = 0; Index < NumEntries; Index++) {
InternalFreePickerEntry (&(*Entries)[Index]);
}
FreePool (*Entries);
*Entries = NULL;
}
STATIC
EFI_STATUS
EFIAPI
OcGetLinuxBootEntries (
IN OC_PICKER_CONTEXT *PickerContext,
IN CONST EFI_HANDLE Device,
OUT OC_PICKER_ENTRY **Entries,
OUT UINTN *NumEntries
)
{
EFI_STATUS Status;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
EFI_FILE_PROTOCOL *RootDirectory;
UINT32 FileSystemPolicy;
CONST EFI_PARTITION_ENTRY *PartitionEntry;
ASSERT (PickerContext != NULL);
ASSERT (Entries != NULL);
ASSERT (NumEntries != NULL);
gPickerContext = PickerContext;
*Entries = NULL;
*NumEntries = 0;
//
// No custom entries.
//
if (Device == NULL) {
return EFI_NOT_FOUND;
}
//
// Open partition file system.
//
Status = gBS->HandleProtocol (
Device,
&gEfiSimpleFileSystemProtocolGuid,
(VOID **) &FileSystem
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "LNX: Missing filesystem - %r\n", Status));
return Status;
}
//
// Get handle to partiton root directory.
//
Status = FileSystem->OpenVolume (
FileSystem,
&RootDirectory
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "LNX: Invalid root volume - %r\n", Status));
return Status;
}
gFileSystemType = NULL;
FileSystemPolicy = OcGetFileSystemPolicyType (Device);
//
// Disallow Apple filesystems, mainly to avoid needlessly
// scanning multiple APFS partitions.
//
if ((FileSystemPolicy & OC_SCAN_ALLOW_FS_APFS) != 0) {
gFileSystemType = "APFS";
DEBUG ((DEBUG_INFO, "LNX: %a - not scanning\n", gFileSystemType));
Status = EFI_NOT_FOUND;
} else if ((FileSystemPolicy & OC_SCAN_ALLOW_FS_HFS) != 0) {
gFileSystemType = "HFS";
DEBUG ((DEBUG_INFO, "LNX: %a - not scanning\n", gFileSystemType));
Status = EFI_NOT_FOUND;
} else if ((FileSystemPolicy & OC_SCAN_ALLOW_FS_ESP) != 0) {
gFileSystemType = "ESP";
if ((gLinuxBootFlags & LINUX_BOOT_SCAN_ESP) == 0) {
DEBUG ((DEBUG_INFO, "LNX: %a - requested not to scan\n", gFileSystemType));
Status = EFI_NOT_FOUND;
}
} else if ((FileSystemPolicy & OC_SCAN_ALLOW_FS_XBOOTLDR) != 0) {
gFileSystemType = "XBOOTLDR";
if ((gLinuxBootFlags & LINUX_BOOT_SCAN_XBOOTLDR) == 0) {
DEBUG ((DEBUG_INFO, "LNX: %a - requested not to scan\n", gFileSystemType));
Status = EFI_NOT_FOUND;
}
} else if ((FileSystemPolicy & OC_SCAN_ALLOW_FS_LINUX_ROOT) != 0) {
gFileSystemType = "ROOT";
if ((gLinuxBootFlags & LINUX_BOOT_SCAN_LINUX_ROOT) == 0) {
DEBUG ((DEBUG_INFO, "LNX: %a - requested not to scan\n", gFileSystemType));
Status = EFI_NOT_FOUND;
}
} else if ((FileSystemPolicy & OC_SCAN_ALLOW_FS_LINUX_DATA) != 0) {
gFileSystemType = "DATA";
if ((gLinuxBootFlags & LINUX_BOOT_SCAN_LINUX_DATA) == 0) {
DEBUG ((DEBUG_INFO, "LNX: %a - requested not to scan\n", gFileSystemType));
Status = EFI_NOT_FOUND;
}
} else {
if ((FileSystemPolicy & OC_SCAN_ALLOW_FS_NTFS) != 0) {
//
// This is not just NTFS, Msft Basic Data part type GUID is used for non-ESP FAT too.
//
gFileSystemType = "NTFS/FAT";
} else {
gFileSystemType = "OTHER";
}
if ((gLinuxBootFlags & LINUX_BOOT_SCAN_OTHER) == 0) {
DEBUG ((DEBUG_INFO, "LNX: %a - requested not to scan\n", gFileSystemType));
Status = EFI_NOT_FOUND;
}
}
if (EFI_ERROR (Status)) {
RootDirectory->Close (RootDirectory);
return Status;
}
//
// Save PARTUUID for autodetect.
//
PartitionEntry = OcGetGptPartitionEntry (Device);
if (PartitionEntry == NULL) {
gPartuuid = gEfiPartTypeUnusedGuid;
} else {
gPartuuid = PartitionEntry->UniquePartitionGUID;
}
//
// Log TypeGUID and PARTUUID of the drive we're in.
//
DEBUG ((
DEBUG_INFO,
"LNX: TypeGUID: %g (%a) PARTUUID: %g\n",
PartitionEntry->PartitionTypeGUID,
gFileSystemType,
PartitionEntry->UniquePartitionGUID
));
//
// Scan for boot loader spec & blscfg entries (Fedora-like).
//
Status = ScanLoaderEntries (
RootDirectory,
Entries,
NumEntries
);
//
// Note: As currently structured, will fall through to autodetect
// if no /loader/entries/*.conf files are present, but also if there
// are only unusable files in there.
//
if (EFI_ERROR (Status)) {
if (Status != EFI_NOT_FOUND) {
DEBUG ((DEBUG_WARN, "LNX: ScanLoaderEntries - %r\n", Status));
}
//
// Auto-detect vmlinuz and initrd files on own root filesystem (Debian-like).
//
if ((gLinuxBootFlags & LINUX_BOOT_ALLOW_AUTODETECT) != 0) {
Status = AutodetectLinux (
RootDirectory,
Entries,
NumEntries
);
if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {
DEBUG ((DEBUG_WARN, "LNX: AutodetectLinux - %r\n", Status));
}
}
}
if (Status == EFI_NOT_FOUND) {
DEBUG ((DEBUG_INFO, "LNX: Nothing found\n"));
}
RootDirectory->Close (RootDirectory);
return Status;
}
STATIC
OC_BOOT_ENTRY_PROTOCOL
mLinuxBootEntryProtocol = {
OC_BOOT_ENTRY_PROTOCOL_REVISION,
OcGetLinuxBootEntries,
OcFreeLinuxBootEntries
};
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = OcParseLoadOptions (ImageHandle, &gParsedLoadOptions);
if (!EFI_ERROR (Status)) {
OcParsedVarsGetInt (gParsedLoadOptions, L"flags", &gLinuxBootFlags, TRUE);
} else if (Status != EFI_NOT_FOUND) {
return Status;
}
Status = gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gOcBootEntryProtocolGuid,
&mLinuxBootEntryProtocol,
NULL
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}

View File

@ -0,0 +1,44 @@
## @file
# Linux boot entry protocol implementation.
#
# Copyright (C) 2021, Mike Beaton. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-3-Clause
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = OpenLinuxBoot
ENTRY_POINT = UefiMain
FILE_GUID = B2EC4DF3-FC2C-4D24-B1D6-B2D2DE8D8172
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
[Packages]
OpenCorePkg/OpenCorePkg.dec
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[Guids]
gEfiPartTypeUnusedGuid ## SOMETIMES_CONSUMES
[LibraryClasses]
OcBootManagementLib
OcDebugLogLib
OcFileLib
OcFlexArrayLib
SortLib
UefiBootServicesTableLib
UefiDriverEntryPoint
UefiLib
[Protocols]
gOcBootEntryProtocolGuid # PRODUCES
[Sources]
Autodetect.c
GrubCfg.c
GrubEnv.c
GrubVars.c
LoaderEntry.c
OpenLinuxBoot.c

View File

@ -71,6 +71,7 @@ extern EFI_GUID gEfiLegacyRegionProtocolGuid;
extern EFI_GUID gEfiLegacyRegion2ProtocolGuid;
extern EFI_GUID gEfiPciRootBridgeIoProtocolGuid;
extern EFI_GUID gEfiSmbiosTableGuid;
extern EFI_GUID gEfiUnicodeCollation2ProtocolGuid;
extern EFI_GUID gOcBootstrapProtocolGuid;
extern EFI_GUID gOcVendorVariableGuid;

View File

@ -56,6 +56,7 @@ EFI_GUID gEfiLegacyRegionProtocolGuid = { 0x0fc9013a, 0x0568, 0x4ba9, { 0
EFI_GUID gEfiLegacyRegion2ProtocolGuid = { 0x70101eaf, 0x85, 0x440c, { 0xb3, 0x56, 0x8e, 0xe3, 0x6f, 0xef, 0x24, 0xf0 }};
EFI_GUID gEfiPciRootBridgeIoProtocolGuid = { 0x2F707EBB, 0x4A1A, 0x11D4, { 0x9A, 0x38, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }};
EFI_GUID gEfiSmbiosTableGuid = { 0xEB9D2D31, 0x2D88, 0x11D3, { 0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }};
EFI_GUID gEfiUnicodeCollation2ProtocolGuid = { 0xa4c751fc, 0x23ae, 0x4c3e, { 0x92, 0xe9, 0x49, 0x64, 0xcf, 0x63, 0xf3, 0x49 }};
EFI_GUID gOcBootstrapProtocolGuid = { 0xBA1EB455, 0xB182, 0x4F14, { 0x85, 0x21, 0xE4, 0x22, 0xC3, 0x25, 0xDE, 0xF6 }};
EFI_GUID gOcVendorVariableGuid = { 0x4D1FDA02, 0x38C7, 0x4A6A, { 0x9C, 0xC6, 0x4B, 0xCC, 0xA8, 0xB3, 0x01, 0x02 }};

View File

@ -136,7 +136,7 @@ ifneq ($(STANDALONE),1)
#
# UDK implementations.
#
OBJS += UefiLib.o UefiLibPrint.o CpuDeadLoop.o BaseDebugPrintErrorLevelLib.o DebugLib.o PrintLib.o PrintLibInternal.o String.o SafeString.o SwapBytes16.o SwapBytes32.o LinkedList.o HighBitSet32.o HighBitSet64.o MtrrLib.o GetPowerOfTwo32.o GetPowerOfTwo64.o Cpu.o BmpSupportLib.o SafeIntLib.o X86GetInterruptState.o PciLib.o PciExpressLib.o DevicePathUtilities.o UefiDevicePathLib.o DevicePathToText.o DevicePathFromText.o BitField.o CheckSum.o
OBJS += UefiLib.o UefiLibPrint.o CpuDeadLoop.o BaseDebugPrintErrorLevelLib.o DebugLib.o PrintLib.o PrintLibInternal.o String.o SafeString.o SwapBytes16.o SwapBytes32.o LinkedList.o HighBitSet32.o HighBitSet64.o MtrrLib.o GetPowerOfTwo32.o GetPowerOfTwo64.o Cpu.o BmpSupportLib.o SafeIntLib.o X86GetInterruptState.o PciLib.o PciExpressLib.o DevicePathUtilities.o UefiDevicePathLib.o DevicePathToText.o DevicePathFromText.o BitField.o CheckSum.o UefiSortLib.o
#
# Customised/Simplified implementations at userspace level.
#
@ -201,6 +201,7 @@ ifneq ($(STANDALONE),1)
$(UDK_PATH)/MdePkg/Library/BasePciExpressLib:$\
$(UDK_PATH)/MdePkg/Library/UefiDevicePathLib:$\
$(UDK_PATH)/MdeModulePkg/Library/BaseBmpSupportLib:$\
$(UDK_PATH)/MdeModulePkg/Library/UefiSortLib:$\
$(UDK_PATH)/UefiCpuPkg/Library/MtrrLib:$\
$(OC_USER)/Library/OcGuardLib:$\
$(OC_USER)/Library/OcSerializeLib:$\

View File

@ -203,21 +203,27 @@ AsciiPropertyIsLegal (
BOOLEAN
AsciiUefiDriverIsLegal (
IN CONST CHAR8 *Driver
IN CONST CHAR8 *Driver,
IN CONST UINTN DriverIndex
)
{
UINTN Index;
UINTN DriverLength;
//
// If an EFI driver does not contain .efi suffix,
// then it must be illegal.
//
if (!OcAsciiEndsWith (Driver, ".efi", TRUE)) {
DriverLength = AsciiStrLen (Driver);
if (DriverLength == 0) {
DEBUG ((DEBUG_WARN, "UEFI->Drivers[%u].Path value is missing!\n", DriverIndex));
return FALSE;
}
DriverLength = AsciiStrLen (Driver);
//
// If an EFI driver does not have .efi suffix,
// then it must be illegal.
//
if (!OcAsciiEndsWith (Driver, ".efi", TRUE)) {
DEBUG ((DEBUG_WARN, "UEFI->Drivers[%u].Path does not end with \"%a\"!\n", DriverIndex, ".efi"));
return FALSE;
}
for (Index = 0; Index < DriverLength; ++Index) {
//
@ -235,6 +241,7 @@ AsciiUefiDriverIsLegal (
//
// Disallowed characters matched.
//
DEBUG ((DEBUG_WARN, "UEFI->Drivers[%u].Path contains illegal character!\n", DriverIndex));
return FALSE;
}

View File

@ -99,13 +99,15 @@ AsciiPropertyIsLegal (
/**
Check if a UEFI Driver matches specific conventions.
@param[in] Driver Driver to be checked.
@param[in] Driver Driver path name to be checked.
@param[in] DriverIndex Index of driver being checked.
@retval TRUE If path of Driver contains .efi suffix, and only contains 0-9, A-Z, a-z, '_', '-', '.', and '/'.
**/
BOOLEAN
AsciiUefiDriverIsLegal (
IN CONST CHAR8 *Driver
IN CONST CHAR8 *Driver,
IN CONST UINTN DriverIndex
);
/**

View File

@ -140,18 +140,19 @@ CheckBooterQuirks (
IN OC_GLOBAL_CONFIG *Config
)
{
UINT32 ErrorCount;
UINT32 Index;
OC_BOOTER_CONFIG *UserBooter;
OC_UEFI_CONFIG *UserUefi;
CONST CHAR8 *Driver;
UINT8 MaxSlide;
BOOLEAN IsAllowRelocationBlockEnabled;
BOOLEAN IsProvideCustomSlideEnabled;
BOOLEAN IsEnableSafeModeSlideEnabled;
BOOLEAN IsDisableVariableWriteEnabled;
BOOLEAN IsEnableWriteUnprotectorEnabled;
BOOLEAN HasOpenRuntimeEfiDriver;
UINT32 ErrorCount;
UINT32 Index;
OC_BOOTER_CONFIG *UserBooter;
OC_UEFI_CONFIG *UserUefi;
OC_UEFI_DRIVER_ENTRY *DriverEntry;
CONST CHAR8 *Driver;
UINT8 MaxSlide;
BOOLEAN IsAllowRelocationBlockEnabled;
BOOLEAN IsProvideCustomSlideEnabled;
BOOLEAN IsEnableSafeModeSlideEnabled;
BOOLEAN IsDisableVariableWriteEnabled;
BOOLEAN IsEnableWriteUnprotectorEnabled;
BOOLEAN HasOpenRuntimeEfiDriver;
ErrorCount = 0;
UserBooter = &Config->Booter;
@ -165,13 +166,14 @@ CheckBooterQuirks (
MaxSlide = UserBooter->Quirks.ProvideMaxSlide;
for (Index = 0; Index < UserUefi->Drivers.Count; ++Index) {
Driver = OC_BLOB_GET (UserUefi->Drivers.Values[Index]);
DriverEntry = UserUefi->Drivers.Values[Index];
Driver = OC_BLOB_GET (&DriverEntry->Path);
//
// Skip sanitising UEFI->Drivers as it will be performed when checking UEFI section.
//
if (AsciiStrCmp (Driver, "OpenRuntime.efi") == 0) {
if (DriverEntry->Enabled && AsciiStrCmp (Driver, "OpenRuntime.efi") == 0) {
HasOpenRuntimeEfiDriver = TRUE;
}
}

View File

@ -184,21 +184,22 @@ CheckMiscBoot (
IN OC_GLOBAL_CONFIG *Config
)
{
UINT32 ErrorCount;
OC_MISC_CONFIG *UserMisc;
OC_UEFI_CONFIG *UserUefi;
UINT32 ConsoleAttributes;
CONST CHAR8 *HibernateMode;
UINT32 PickerAttributes;
UINT32 Index;
CONST CHAR8 *Driver;
BOOLEAN HasOpenCanopyEfiDriver;
CONST CHAR8 *PickerMode;
CONST CHAR8 *PickerVariant;
BOOLEAN IsPickerAudioAssistEnabled;
BOOLEAN IsAudioSupportEnabled;
CONST CHAR8 *LauncherOption;
CONST CHAR8 *LauncherPath;
UINT32 ErrorCount;
OC_MISC_CONFIG *UserMisc;
OC_UEFI_CONFIG *UserUefi;
UINT32 ConsoleAttributes;
CONST CHAR8 *HibernateMode;
UINT32 PickerAttributes;
UINT32 Index;
OC_UEFI_DRIVER_ENTRY *DriverEntry;
CONST CHAR8 *Driver;
BOOLEAN HasOpenCanopyEfiDriver;
CONST CHAR8 *PickerMode;
CONST CHAR8 *PickerVariant;
BOOLEAN IsPickerAudioAssistEnabled;
BOOLEAN IsAudioSupportEnabled;
CONST CHAR8 *LauncherOption;
CONST CHAR8 *LauncherPath;
ErrorCount = 0;
UserMisc = &Config->Misc;
@ -221,15 +222,16 @@ CheckMiscBoot (
PickerAttributes = UserMisc->Boot.PickerAttributes;
if ((PickerAttributes & ~OC_ATTR_ALL_BITS) != 0) {
DEBUG ((DEBUG_WARN, "Misc->Boot->PickerAttributes is has unknown bits set!\n"));
DEBUG ((DEBUG_WARN, "Misc->Boot->PickerAttributes has unknown bits set!\n"));
++ErrorCount;
}
HasOpenCanopyEfiDriver = FALSE;
for (Index = 0; Index < UserUefi->Drivers.Count; ++Index) {
Driver = OC_BLOB_GET (UserUefi->Drivers.Values[Index]);
DriverEntry = UserUefi->Drivers.Values[Index];
Driver = OC_BLOB_GET (&DriverEntry->Path);
if (AsciiStrCmp (Driver, "OpenCanopy.efi") == 0) {
if (DriverEntry->Enabled && AsciiStrCmp (Driver, "OpenCanopy.efi") == 0) {
HasOpenCanopyEfiDriver = TRUE;
}
}

View File

@ -231,6 +231,7 @@ CheckUEFIDrivers (
UINT32 ErrorCount;
OC_UEFI_CONFIG *UserUefi;
UINT32 Index;
OC_UEFI_DRIVER_ENTRY *DriverEntry;
CONST CHAR8 *Driver;
BOOLEAN HasOpenRuntimeEfiDriver;
BOOLEAN HasOpenUsbKbDxeEfiDriver;
@ -258,7 +259,8 @@ CheckUEFIDrivers (
HasAudioDxeEfiDriver = FALSE;
IndexAudioDxeEfiDriver = 0;
for (Index = 0; Index < UserUefi->Drivers.Count; ++Index) {
Driver = OC_BLOB_GET (UserUefi->Drivers.Values[Index]);
DriverEntry = UserUefi->Drivers.Values[Index];
Driver = OC_BLOB_GET (&DriverEntry->Path);
//
// Check the length of path relative to OC directory.
@ -268,19 +270,18 @@ CheckUEFIDrivers (
++ErrorCount;
}
if (Driver[0] == '#') {
continue;
}
//
// Sanitise strings.
//
if (!AsciiUefiDriverIsLegal (Driver)) {
DEBUG ((DEBUG_WARN, "UEFI->Drivers[%u] contains illegal character!\n", Index));
if (!AsciiUefiDriverIsLegal (Driver, Index)) {
++ErrorCount;
continue;
}
if (!DriverEntry->Enabled) {
continue;
}
if (AsciiStrCmp (Driver, "OpenRuntime.efi") == 0) {
HasOpenRuntimeEfiDriver = TRUE;
}

View File

@ -157,6 +157,7 @@ package() {
"AudioDxe.efi"
"CrScreenshotDxe.efi"
"OpenCanopy.efi"
"OpenLinuxBoot.efi"
"OpenPartitionDxe.efi"
"OpenRuntime.efi"
"OpenUsbKbDxe.efi"