Add complete guide and all config variants
This commit is contained in:
BIN
hackintosh-guide/Utilities/ACPIe/ACPIe
Executable file
BIN
hackintosh-guide/Utilities/ACPIe/ACPIe
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/ACPIe/ACPIe.linux
Executable file
BIN
hackintosh-guide/Utilities/ACPIe/ACPIe.linux
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/BaseTools/EfiRom
Executable file
BIN
hackintosh-guide/Utilities/BaseTools/EfiRom
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/BaseTools/GenFfs
Executable file
BIN
hackintosh-guide/Utilities/BaseTools/GenFfs
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/CreateVault/RsaTool
Executable file
BIN
hackintosh-guide/Utilities/CreateVault/RsaTool
Executable file
Binary file not shown.
96
hackintosh-guide/Utilities/CreateVault/create_vault.sh
Executable file
96
hackintosh-guide/Utilities/CreateVault/create_vault.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
# create_vault.sh
|
||||
#
|
||||
#
|
||||
# Created by Rodion Shingarev on 13.04.19.
|
||||
#
|
||||
OCPath="$1"
|
||||
|
||||
if [ "${OCPath}" = "" ]; then
|
||||
echo "Usage ./create_vault.sh path/to/EFI/OC"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${OCPath}" ]; then
|
||||
echo "Path $OCPath is missing!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x /usr/bin/env ] || [ ! -x /usr/bin/find ] || [ ! -x /bin/rm ] || [ ! -x /usr/bin/sed ] || [ ! -x /usr/bin/openssl ] || [ ! -x /usr/bin/awk ] || [ ! -x /usr/bin/sort ] || [ ! -x /usr/bin/xxd ]; then
|
||||
echo "Unix environment is broken!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
abort() {
|
||||
/bin/rm -rf vault.plist vault.sig /tmp/vault_hash
|
||||
echo "Fatal error: ${1}!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# plist output functions so we don't need PlistBuddy
|
||||
write_header() {
|
||||
cat <<EOF > "$1"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Files</key>
|
||||
<dict>
|
||||
EOF
|
||||
}
|
||||
|
||||
write_file_name_and_hash() {
|
||||
{
|
||||
echo -e "\t\t<key>${2}</key>"
|
||||
echo -e "\t\t<data>"
|
||||
echo -e -n "\t\t"
|
||||
cat "$3"
|
||||
echo -e "\t\t</data>"
|
||||
} >> "$1"
|
||||
}
|
||||
|
||||
write_footer() {
|
||||
cat <<EOF >> "$1"
|
||||
</dict>
|
||||
<key>Version</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
}
|
||||
|
||||
echo "Chose ${OCPath} for hashing..."
|
||||
|
||||
cd "${OCPath}" || abort "Failed to reach ${OCPath}"
|
||||
/bin/rm -rf vault.plist vault.sig || abort "Failed to cleanup"
|
||||
|
||||
echo "Hashing files in ${OCPath}..."
|
||||
|
||||
write_header vault.plist
|
||||
|
||||
/usr/bin/find . -not -path '*/\.*' -type f \
|
||||
\( ! -iname ".*" \) \
|
||||
\( ! -iname "vault.*" \) \
|
||||
\( ! -iname "MemTest86.log" \) \
|
||||
\( ! -iname "MemTest86-Report-*.html" \) \
|
||||
\( ! -iname "OpenCore.efi" \) | env LC_COLLATE=POSIX /usr/bin/sort | while read -r fname; do
|
||||
fname="${fname#"./"}"
|
||||
wname="${fname//\//\\\\}"
|
||||
sha=$(/usr/bin/openssl sha256 "${fname}" | /usr/bin/awk '{print $2}') || abort "Failed to hash ${fname}"
|
||||
if [ "${#sha}" != 64 ] || [ "$(echo "$sha"| /usr/bin/sed 's/^[a-f0-9]*$//')" ]; then
|
||||
abort "Got invalid hash: ${sha}!"
|
||||
fi
|
||||
|
||||
echo "${wname}: ${sha}"
|
||||
|
||||
echo "${sha}" | /usr/bin/xxd -r -p | /usr/bin/openssl base64 > /tmp/vault_hash || abort "Hashing failure"
|
||||
write_file_name_and_hash vault.plist "${wname}" /tmp/vault_hash
|
||||
done
|
||||
|
||||
/bin/rm -rf /tmp/vault_hash
|
||||
|
||||
write_footer vault.plist
|
||||
|
||||
echo "All done!"
|
||||
exit 0
|
||||
72
hackintosh-guide/Utilities/CreateVault/sign.command
Executable file
72
hackintosh-guide/Utilities/CreateVault/sign.command
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/bin/sh
|
||||
|
||||
abort() {
|
||||
echo "Fatal error: ${1}!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2317,SC2329
|
||||
cleanup() {
|
||||
echo "Cleaning up key"
|
||||
rm -rf "${KeyPath}"
|
||||
}
|
||||
|
||||
if [ ! -x /usr/bin/dirname ] || [ ! -x /bin/chmod ] || [ ! -x /bin/mkdir ] || [ ! -x /bin/rm ] || [ ! -x /usr/bin/strings ] || [ ! -x /usr/bin/grep ] || [ ! -x /usr/bin/awk ] || [ ! -x /bin/dd ] || [ ! -x /usr/bin/uuidgen ] ; then
|
||||
abort "Unix environment is broken!"
|
||||
fi
|
||||
|
||||
cd "$(/usr/bin/dirname "$0")" || abort "Failed to enter working directory!"
|
||||
|
||||
OCPath="$1"
|
||||
|
||||
if [ "$OCPath" = "" ]; then
|
||||
OCPath=../../EFI/OC
|
||||
fi
|
||||
|
||||
KeyPath="/tmp/$(/usr/bin/uuidgen)"
|
||||
OCBin="${OCPath}/OpenCore.efi"
|
||||
PubKey="${KeyPath}/vault.pub"
|
||||
|
||||
if [ ! -d "${OCPath}" ]; then
|
||||
abort "Path ${OCPath} is missing!"
|
||||
fi
|
||||
|
||||
if [ ! -f "${OCBin}" ]; then
|
||||
abort "OpenCore.efi is missing!"
|
||||
fi
|
||||
|
||||
if [ ! -x ./RsaTool ] || [ ! -x ./create_vault.sh ]; then
|
||||
if [ -f ./RsaTool ]; then
|
||||
/bin/chmod a+x ./RsaTool || abort "Failed to set permission for RsaTool"
|
||||
else
|
||||
abort "Failed to find RsaTool!"
|
||||
fi
|
||||
|
||||
if [ -f ./create_vault.sh ]; then
|
||||
/bin/chmod a+x ./create_vault.sh || abort "Failed to set permission for create_vault.sh"
|
||||
else
|
||||
abort "Failed to find create_vault.sh!"
|
||||
fi
|
||||
fi
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
if [ ! -d "${KeyPath}" ]; then
|
||||
/bin/mkdir -p "${KeyPath}" || abort "Failed to create path ${KeyPath}"
|
||||
fi
|
||||
|
||||
./create_vault.sh "${OCPath}" || abort "create_vault.sh returns errors!"
|
||||
|
||||
echo "Signing ${OCBin}..."
|
||||
./RsaTool -sign "${OCPath}/vault.plist" "${OCPath}/vault.sig" "${PubKey}" || abort "Failed to patch ${PubKey}"
|
||||
|
||||
echo "Bin-patching ${OCBin}..."
|
||||
off=$((0x$(/usr/bin/hexdump -C "${OCBin}" | /usr/bin/grep "=BEGIN OC VAULT=" | /usr/bin/awk '{print $1}') + 16))
|
||||
if [ "${off}" -le 16 ]; then
|
||||
abort "${OCBin} is borked"
|
||||
fi
|
||||
|
||||
/bin/dd of="${OCBin}" if="${PubKey}" bs=1 seek="${off}" count=528 conv=notrunc || abort "Failed to bin-patch ${OCBin}"
|
||||
|
||||
echo "All done!"
|
||||
exit 0
|
||||
BIN
hackintosh-guide/Utilities/EnableGop/EnableGopDirect_1.4.efi
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/EnableGopDirect_1.4.efi
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/EnableGop/EnableGopDirect_1.4.ffs
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/EnableGopDirect_1.4.ffs
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/EnableGop/EnableGop_1.4.efi
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/EnableGop_1.4.efi
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/EnableGop/EnableGop_1.4.ffs
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/EnableGop_1.4.ffs
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/EnableGop/Pre-release/EnableGopDirect_1.5-dev.efi
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/Pre-release/EnableGopDirect_1.5-dev.efi
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/EnableGop/Pre-release/EnableGopDirect_1.5-dev.ffs
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/Pre-release/EnableGopDirect_1.5-dev.ffs
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/EnableGop/Pre-release/EnableGop_1.5-dev.efi
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/Pre-release/EnableGop_1.5-dev.efi
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/EnableGop/Pre-release/EnableGop_1.5-dev.ffs
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/Pre-release/EnableGop_1.5-dev.ffs
Executable file
Binary file not shown.
184
hackintosh-guide/Utilities/EnableGop/README.md
Executable file
184
hackintosh-guide/Utilities/EnableGop/README.md
Executable file
@@ -0,0 +1,184 @@
|
||||
# Provides standalone GOP driver for EFI era Mac Pro and iMac
|
||||
|
||||
## Releases
|
||||
|
||||
EnableGop version (OpenCore version)
|
||||
|
||||
### 1.4 (0.9.3)
|
||||
- Incorporates recent updates to OpenCore console control code, but no difference in behaviour compared
|
||||
to version 1.3 is expected on any supported systems.
|
||||
|
||||
### 1.3 (0.9.2)
|
||||
- Included fix to GopBurstMode for non-standard frame buffer information on AMD Radeon HD 7970 and similar
|
||||
- Applied GopBurstMode even on natively supported cards, as it can provide a noticable speed up
|
||||
|
||||
### 1.2 (0.9.1)
|
||||
- Added GopBurstMode support
|
||||
|
||||
*Note 1*: This should provide faster GOP rendering on all EnableGopDirect systems; and rendering at least at
|
||||
the same speed as before, and on some systems noticeably faster than before, on almost all EnableGop systems.
|
||||
|
||||
*Note 2*: The compressed driver for version 1.2 is 1KB larger than for version 1.1, so for AMD GPU firmware which is
|
||||
tight on space version 1.1 may be used instead to avoid the need for VGA stripping to make additional space.
|
||||
|
||||
### 1.1 (0.9.0)
|
||||
- Fixed early verbose boot lines appearing over picker
|
||||
- Added EnableGop version number to UI section
|
||||
|
||||
### 1.0 (0.8.9)
|
||||
- Initial public release
|
||||
|
||||
## Status
|
||||
**Current status: Beta release.**
|
||||
|
||||
This driver has been tested and is working on several iMac models
|
||||
with several different GPUs, and on several MacPro4,1/5,1 machines with several different GPUs. However, in the worst
|
||||
case (and still possible) scenario, an incompatible or incorrectly installed driver
|
||||
in firmware may brick your hardware.
|
||||
|
||||
*In all cases take a backup of the main firmware or GPU firmware which you are modifying, and confirm that
|
||||
you can successfully restore from this, before starting.*
|
||||
|
||||
## Recovery from bricked hardware
|
||||
- If attempting main firmware insertion on a MacPro4,1/5,1, for recovery from a bricked device you will either
|
||||
need a Matt card (which may breach intellectual property laws in some jurisdictions) or the ability to
|
||||
desolder and reprogram your own SPI flash chip.
|
||||
- If testing via main firmware insertion on an iMac, you will need the ability to disassemble your iMac and
|
||||
reprogram its SPI flash chip using a SOIC clip attached to a CH341A controller running on another computer.
|
||||
- If testing via GPU firmware insertion (iMac or Mac Pro), you will need the ability to disassemble your system,
|
||||
likely remove the heat sink from the graphics card, and then reprogram its SPI flash chip using a SOIC
|
||||
clip attached to a CH341A controller running on another computer.
|
||||
- If testing via GPU firmware insertion, in some cases it may also be possible
|
||||
to use physical electrical connection to your GPU in order to enable booting with no graphics even though the GPU
|
||||
is present, then connect to your machine with `ssh` (which must have been enabled beforehand) and reprogram the GPU
|
||||
firmware. Advice on this headless boot approach is not provided here, but may be found for instance on the iMac GPU
|
||||
related forum threads listed below.
|
||||
|
||||
*If you are not familiar with the above procedures, you are strongly recommended to wait for further testing by
|
||||
users who are. No further help can be provided here, and you proceed entirely at your own risk.*
|
||||
|
||||
## Summary
|
||||
Targetting EFI-era (~2009-2012) MacPro4,1/5,1 and iMac firmware, this driver gathers and injects the parts of
|
||||
OpenCore needed for pre-boot graphics support with non-natively supported GPUs.
|
||||
|
||||
The requirements for using this driver are:
|
||||
|
||||
- EFI-era (~2009-2012) MacPro4,1/5,1 or iMac with most recent main firmware.
|
||||
- A GPU which does not produce native pre-boot graphics (such as native picker when pressing ALT key during boot)
|
||||
before OpenCore starts (otherwise, you do not need it).
|
||||
- A GPU which produces graphics when using OpenCore (this must include successfully showing the native Apple boot
|
||||
picker when started via the latest version of OpenCore tool `BootKicker.efi`) (otherwise, the driver will not work).
|
||||
- *Note*: If your OpenCore installation includes a required GOP driver for your graphics card, then you would
|
||||
also need to burn that driver to the firmware of your graphics card in order to obtain pre-OpenCore graphics;
|
||||
instructions for this are outside the scope of this tutorial, although the procedures required for modifying
|
||||
GPU firmware are similar to what is covered here.
|
||||
Note that such a driver is added by the OCLP **Enable AMD GOP** option, which is enabled automatically on some
|
||||
systems by recent versions of OpenCore Legacy Patcher, as a way to enable the OpenCore menu in cards such as ex-mining GPUs.
|
||||
|
||||
When installed, the driver should enable:
|
||||
|
||||
- Native boot picker via ALT key
|
||||
- Firmware password UI
|
||||
- Target disk mode UI
|
||||
- macOS boot progress screen
|
||||
- etc.
|
||||
|
||||
Compiled versions of the driver files and these instructions may be found in the `Utilities/EnableGop`
|
||||
directory of the OpenCore release package.
|
||||
|
||||
For GPUs needing `DirectGopRendering` in OpenCore configuration, use `EnableGopDirect.efi`, otherwise use `EnableGop.efi`
|
||||
as it renders faster on most other systems.
|
||||
|
||||
The driver may be installed to GPU or main motherboard firmware. It is expected that most Mac Pro users will use main firmware insertion
|
||||
and most iMac users will chose GPU firmware insertion, however both techniques work on both systems (but it is harder to modify the
|
||||
iMac main firmware, since there is no simple way to enable writing to it).
|
||||
|
||||
Further discussion and community support for this driver is available at:
|
||||
|
||||
- https://forums.macrumors.com/threads/pre-opencore-gop-support-for-efi-era-imacs-and-mac-pros.2378942/
|
||||
|
||||
## Usage
|
||||
|
||||
## Install to main firmware
|
||||
|
||||
For reading and writing to main firmware on the Mac Pro, @Macschrauber's [Rom Dump](https://github.com/Macschrauber/Macschrauber-s-Rom-Dump) works
|
||||
well. Alternatively the kexts and executables which this uses can be sourced individually (or extracted from the Rom Dump app) and
|
||||
run from the command line.
|
||||
|
||||
The main firmware on the iMac cannot be updated without an initial hardware flash (SOIC clip plus CH341A controller), therefore
|
||||
the recommended approach on iMac systems is [GPU firmware injection](#install-to-gpu-firmware). However, the below instructions for firmware
|
||||
injection do work, if you are willing to do a hardware flash of the resulting firmware file, or if you have already
|
||||
[unprotected your iMac firmware](https://forums.macrumors.com/threads/imac-2011-see-more-uefi-firmware-mod.2257435/page-3?post=31087001#post-31087001) -
|
||||
which reduces security, and is only recommended for those actively developing firmware modifications.
|
||||
|
||||
The `.ffs` file provided in this directory can be manually added to the extracted firmware file using [`UEFITool`](https://github.com/LongSoft/UEFITool),
|
||||
or automatically added using @dosdude1's [`DXEInject`](https://dosdude1.com/apps/). Once more, if you are not familiar with these procedures,
|
||||
you are recommended to proceed with extreme caution.
|
||||
|
||||
### Using DXEInject
|
||||
|
||||
To install the driver via `DXEInject`, the command is:
|
||||
|
||||
- `DXEInject {original}.rom {modified}.rom EnableGop.ffs`
|
||||
|
||||
The file `{modifed}.rom` is ready for burning, although the result can be checked using UEFITool, if required.
|
||||
|
||||
> *Note*: If only reading a file with UEFITool, the latest version is recommended, as it provides the most information.
|
||||
For writing, the older version 0.25.1 must be used, as described below.
|
||||
|
||||
### Using UEFITool
|
||||
|
||||
The `.ffs` file may be inserted anywhere within the same firmware volume which contains `DuetBds`
|
||||
(file GUID `A6F691AC-31C8-4444-854C-E2C1A6950F92`). However, for simplicity, these instructions
|
||||
will insert it in the same place that `DXEInject` does:
|
||||
|
||||
- Use UEFITool 0.25.1 (it must be that old version, not the newer NE or new engine versions, which
|
||||
cannot yet edit)
|
||||
- Perform a header-only GUID search for `BAE7599F-3C6B-43B7-BDF0-9CE07AA91AA6`
|
||||
- Double-click on the search result
|
||||
- Right-click on the DXE driver with that GUID which should then appear
|
||||
- Choose "Insert after..." and select `EnableGop.ffs`
|
||||
- Save the modified firmware
|
||||
|
||||
The end result, after saving and re-loading, should look like this:
|
||||
|
||||
<img src="UEFITool_Inserted_Screenshot.png">
|
||||
|
||||
## Install to GPU firmware
|
||||
|
||||
Instructions and a script for inserting the driver into Nvidia or AMD GPU firmware (aka VBIOS) are provided.
|
||||
|
||||
Please note all the cautions already given above about the difficulty of recovering, unless you are familiar with
|
||||
the procedures necessary, if this process fails.
|
||||
|
||||
To use the provided `vBiosInsert.sh` script:
|
||||
|
||||
- Locate an appropriate version of the `nvflash` tool (Nvidia) or `amdvbflash` tool (AMD) (both are available for
|
||||
Linux and Windows), which can be used to read from and write to the GPU firmware.
|
||||
- Use that tool to read a copy of the GPU firmware.
|
||||
- Run `./vBiosInsert.sh [-a|-n] {original}.rom EnableGop.efi {modified}.rom`, with `-a` for AMD and `-n` for Nvidia.
|
||||
- If you have any problems with `vBiosInsert.sh` from a specific release
|
||||
of EnableGop, please try the version included with the latest release of OpenCore
|
||||
before reporting any issues.
|
||||
The script receives updates to support additional graphics cards independently
|
||||
of any bumps to the release version of EnableGop. If you need to, you can use
|
||||
the latest version of `vBiosInsert.sh` to inject older versions of EnableGop.
|
||||
- The new file `{modified}.rom` may be burnt to the GPU firmware.
|
||||
|
||||
In the case of AMD, considerably less space is normally available, due to a strict limit of 128k for legacy and EFI
|
||||
parts of the larger ROM image. If there is not enough space (i.e. script reports
|
||||
data would be truncated) then it is necessary to [strip some legacy VGA parts of the
|
||||
GPU firmware](https://github.com/Ausdauersportler/IMAC-EFI-BOOT-SCREEN/wiki/Deleting-the-VGA). This is beyond the scope
|
||||
of these instructions.
|
||||
|
||||
If required to manually detect the GOP offset (this should normally be autodetected):
|
||||
|
||||
> Using a hex editor, search in the GPU firmware dump for the byte sequence `F1 0E 00 00` with the byte sequence `55 AA` coming
|
||||
close before it; the start address of the `55 AA` is the GOP offset value needed.
|
||||
|
||||
For further information on GPU firmware modification, see:
|
||||
|
||||
- https://forums.macrumors.com/threads/2011-imac-graphics-card-upgrade.1596614/
|
||||
- https://forums.macrumors.com/threads/imac-2011-maxwell-and-pascal-gpu-upgrade.2300989/
|
||||
- https://github.com/Ausdauersportler/IMAC-EFI-BOOT-SCREEN/wiki
|
||||
- https://winraid.level1techs.com/t/amd-and-nvidia-gop-update-no-requests-diy/30917
|
||||
BIN
hackintosh-guide/Utilities/EnableGop/UEFITool_Inserted_Screenshot.png
Executable file
BIN
hackintosh-guide/Utilities/EnableGop/UEFITool_Inserted_Screenshot.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 694 KiB |
334
hackintosh-guide/Utilities/EnableGop/vBiosInsert.sh
Executable file
334
hackintosh-guide/Utilities/EnableGop/vBiosInsert.sh
Executable file
@@ -0,0 +1,334 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Copyright © 2023 Mike Beaton. All rights reserved.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
# Insert EFI into AMD or Nvidia VBIOS.
|
||||
# Tested back to Mac OS X 10.11 El Capitan.
|
||||
#
|
||||
|
||||
usage() {
|
||||
echo "Usage: ./${SELFNAME} [args] {rom-file} {efi-file} {out-file}"
|
||||
echo "Args:"
|
||||
echo " -a : AMD"
|
||||
echo " -n : Nvidia"
|
||||
echo " -o {GOP offset} : GOP offset (auto-detected if Homebrew grep is installed)"
|
||||
echo " Can specify 0x{hex} or {decimal}"
|
||||
echo " -t {temp dir} : Specify temporary directory, and keep temp files"
|
||||
echo " -m {max size} : Specify VBIOS max size (Nvidia only)"
|
||||
echo " (For AMD first 128KB is modified, any remainder is kept)"
|
||||
echo "Examples:"
|
||||
echo " ./${SELFNAME} -n -o 0xFC00 nv.rom EnableGop.efi nv_mod.rom"
|
||||
echo " ./${SELFNAME} -n nv.rom EnableGop.efi nv_mod.rom"
|
||||
echo " ./${SELFNAME} -a amd.rom EnableGop.efi amd_mod.rom"
|
||||
echo ""
|
||||
}
|
||||
|
||||
SELFNAME="$(/usr/bin/basename "${0}")"
|
||||
|
||||
commands=(
|
||||
"EfiRom"
|
||||
"UEFIRomExtract"
|
||||
"hexdump"
|
||||
"grep"
|
||||
)
|
||||
|
||||
FOUND=1
|
||||
for command in "${commands[@]}"; do
|
||||
if ! command -v "$command" 1>/dev/null ; then
|
||||
echo "${command} not available!"
|
||||
FOUND=0
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$FOUND" -eq 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
AMD=0
|
||||
AMD_SAFE_SIZE="0x20000"
|
||||
NVIDIA_SAFE_SIZE="0x40000"
|
||||
GOP_OFFSET="-"
|
||||
NVIDIA=0
|
||||
POS=0
|
||||
TRUNCATE=0
|
||||
|
||||
while true; do
|
||||
if [ "$1" = "-a" ] ; then
|
||||
AMD=1
|
||||
NVIDIA=0
|
||||
shift
|
||||
elif [ "$1" = "-n" ] ; then
|
||||
AMD=0
|
||||
NVIDIA=1
|
||||
shift
|
||||
elif [ "$1" = "-o" ] ; then
|
||||
shift
|
||||
if [ "$1" != "" ] && ! [ "${1:0:1}" = "-" ] ; then
|
||||
GOP_OFFSET=$1
|
||||
shift
|
||||
else
|
||||
echo "No GOP offset specified" && exit 1
|
||||
fi
|
||||
elif [ "$1" = "-s" ] ; then # semi-secret option to modify AMD safe size
|
||||
shift
|
||||
if [ "$1" != "" ] && ! [ "${1:0:1}" = "-" ] ; then
|
||||
AMD_SAFE_SIZE=$1
|
||||
shift
|
||||
else
|
||||
echo "No AMD safe size specified" && exit 1
|
||||
fi
|
||||
elif [ "$1" = "-m" ] ; then
|
||||
shift
|
||||
if [ "$1" != "" ] && ! [ "${1:0:1}" = "-" ] ; then
|
||||
TRUNCATE=1
|
||||
TRUNCATE_SIZE=$1
|
||||
NVIDIA_SAFE_SIZE=$TRUNCATE_SIZE
|
||||
shift
|
||||
else
|
||||
echo "No max size specified" && exit 1
|
||||
fi
|
||||
elif [ "$1" = "-t" ] ; then
|
||||
shift
|
||||
if [ "$1" != "" ] && ! [ "${1:0:1}" = "-" ] ; then
|
||||
TEMP_DIR=$1
|
||||
shift
|
||||
else
|
||||
echo "No temp dir specified" && exit 1
|
||||
fi
|
||||
elif [ "${1:0:1}" = "-" ] ; then
|
||||
echo "Unknown option: ${1}" && exit 1
|
||||
elif [ "$1" != "" ] ; then
|
||||
case "$POS" in
|
||||
0 )
|
||||
ROM_FILE="$1"
|
||||
;;
|
||||
1 )
|
||||
EFI_FILE="$1"
|
||||
;;
|
||||
2 )
|
||||
OUT_FILE="$1"
|
||||
;;
|
||||
* )
|
||||
echo "Too many filenames specified" && exit 1
|
||||
;;
|
||||
esac
|
||||
POS=$(($POS+1))
|
||||
shift
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$ROM_FILE" = "" ] ||
|
||||
[ "$EFI_FILE" = "" ] ||
|
||||
[ "$OUT_FILE" = "" ] ; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$AMD" -eq 0 ] && [ "$NVIDIA" -eq 0 ] ; then
|
||||
echo "Must specify -a or -n" && exit 1
|
||||
fi
|
||||
|
||||
if [ "$AMD" -eq 1 ] && [ "$TRUNCATE" -eq 1 ] ; then
|
||||
echo "-m is not valid with -a" && exit 1
|
||||
fi
|
||||
|
||||
if [ "$TEMP_DIR" != "" ] ; then
|
||||
mkdir -p "$TEMP_DIR" || exit 1
|
||||
tmpdir="$TEMP_DIR"
|
||||
else
|
||||
# https://unix.stackexchange.com/a/84980/340732
|
||||
tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'vbios') || exit 1
|
||||
fi
|
||||
|
||||
ORIGINAL_SIZE=$(stat -f%z "$ROM_FILE") || exit 1
|
||||
|
||||
if [ "$AMD" -eq 1 ] ; then
|
||||
# For AMD we can only modify the first 128KB, anything above 128KB
|
||||
# should not be moved, and is not mapped to memory visible by the CPU
|
||||
# for loading EFI drivers.
|
||||
TRUNCATE=1
|
||||
TRUNCATE_SIZE="$AMD_SAFE_SIZE"
|
||||
|
||||
# Also works, with empty keep_part.rom, in the atypical case where we
|
||||
# are provided with only the used part of the ROM below 128KB.
|
||||
dd bs=1 if="$ROM_FILE" of="$tmpdir/modify_part.rom" count="$AMD_SAFE_SIZE" 2>/dev/null || exit 1
|
||||
dd bs=1 if="$ROM_FILE" of="$tmpdir/keep_part.rom" skip="$AMD_SAFE_SIZE" 2>/dev/null || exit 1
|
||||
else
|
||||
if [ "$TRUNCATE" -eq 0 ] ; then
|
||||
# If original size is a plausible ROM size (exact power of two, 64KB or
|
||||
# larger; 64KB chosen partly for neat regexp) treat it as the full available
|
||||
# size of the VBIOS chip unless overridden with -m.
|
||||
printf '%x' "$ORIGINAL_SIZE" | grep -Eq "^(1|2|4|8)0000+$" && TRUNCATE=1
|
||||
if [ "$TRUNCATE" -eq 1 ] ; then
|
||||
echo "Detected standard ROM size."
|
||||
TRUNCATE_SIZE="$ORIGINAL_SIZE"
|
||||
else
|
||||
if [ "$ORIGINAL_SIZE" -gt "$((NVIDIA_SAFE_SIZE))" ] ; then
|
||||
echo " - File size of ${ORIGINAL_SIZE} bytes must be no more than $((NVIDIA_SAFE_SIZE)) bytes; use -m or check file" && exit 1
|
||||
fi
|
||||
TRUNCATE=1
|
||||
TRUNCATE_SIZE="$NVIDIA_SAFE_SIZE"
|
||||
fi
|
||||
fi
|
||||
|
||||
cp "$ROM_FILE" "$tmpdir/modify_part.rom" || exit 1
|
||||
fi
|
||||
|
||||
if [ "$GOP_OFFSET" = "-" ] ; then
|
||||
echo "Auto-detecting GOP offset..."
|
||||
|
||||
# nicer techniques which do not assume nice alignment of what is being searched for do not work on older Mac OS X
|
||||
OUTPUT=$(hexdump -C "$tmpdir/modify_part.rom" | grep '55 aa .. .. f1 0e 00 00' | head -1)
|
||||
# Make macOS bash to split as expected:
|
||||
# shellcheck disable=SC2206
|
||||
GOP_ARRAY=($OUTPUT)
|
||||
GOP_OFFSET=${GOP_ARRAY[0]}
|
||||
if [ "$GOP_OFFSET" != "" ] ; then
|
||||
GOP_OFFSET="0x${GOP_OFFSET}"
|
||||
GOP_OFFSET=$(($GOP_OFFSET))
|
||||
else
|
||||
GOP_OFFSET=-1
|
||||
fi
|
||||
|
||||
if [ "$GOP_OFFSET" -eq -1 ] ; then
|
||||
echo " - No GOP found in ROM!" && exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
dd bs=1 if="$tmpdir/modify_part.rom" of="$tmpdir/original_first_part.rom" count=$(($GOP_OFFSET)) 2>/dev/null || exit 1
|
||||
dd bs=1 if="$tmpdir/modify_part.rom" of="$tmpdir/original_last_part.rom" skip=$(($GOP_OFFSET)) 2>/dev/null || exit 1
|
||||
|
||||
echo "Compressing EFI using EfiRom..."
|
||||
if [ "$AMD" -eq 1 ] ; then
|
||||
EfiRom -o "$tmpdir/insert.rom" -ec "$EFI_FILE" -f 0xAAAA -i 0xBBBB -l 0x30000 -p || exit 1
|
||||
else
|
||||
EfiRom -o "$tmpdir/insert.rom" -ec "$EFI_FILE" -f 0xAAAA -i 0xBBBB -l 0x30000 || exit 1
|
||||
fi
|
||||
|
||||
if [ "$NVIDIA" -eq 1 ] ; then
|
||||
dd bs=1 if="$tmpdir/insert.rom" of="$tmpdir/insert_first_part" count=$((0x38)) 2>/dev/null || exit 1
|
||||
dd bs=1 if="$tmpdir/insert.rom" of="$tmpdir/insert_last_part" skip=$((0x38)) 2>/dev/null || exit 1
|
||||
|
||||
# TODO: truncation logic should be fixed for when there is not enough spare padding in output of EfiRom;
|
||||
# we currently assume without checking that there is enough space to fit in the NPDE header (if not,
|
||||
# script will report failure to verify with UEFIRomExtract below).
|
||||
INSERT_SIZE=$(stat -f%z "$tmpdir/insert.rom") || exit 1
|
||||
|
||||
# Calculate NPDE size from original GOP and add to new image
|
||||
EfiImageOffset=$(dd if="$tmpdir/original_last_part.rom" ibs=1 skip=$((0x16)) count=2 2>/dev/null | od -t u4 -An | xargs)
|
||||
if [ "$EfiImageOffset" -eq $((0x50)) ] ; then
|
||||
NpdeSize=$((0x18))
|
||||
elif [ "$EfiImageOffset" -eq $((0x4C)) ] ; then
|
||||
NpdeSize=$((0x14))
|
||||
elif [ "$EfiImageOffset" -eq $((0x38)) ] ; then
|
||||
NpdeSize=0
|
||||
else
|
||||
# Note: We need at least 0x14 NPDE size for the patch-ups we do below for size and end-marker to make sense
|
||||
printf "Unsupported EFI Image Offset 0x%x, cannot calculate NPDE Size!\n" "$EfiImageOffset"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$NVIDIA" -eq 1 ] && [ "$NpdeSize" -ne 0 ] ; then
|
||||
echo "Adding Nvidia header..."
|
||||
|
||||
dd bs=1 if="$tmpdir/original_last_part.rom" of="$tmpdir/insert_first_part" skip=$((0x38)) seek=$((0x38)) count="$NpdeSize" 2>/dev/null || exit 1
|
||||
cat "$tmpdir/insert_first_part" "$tmpdir/insert_last_part" > "$tmpdir/insert_oversize.rom" || exit 1
|
||||
# Note: `truncate` command is not present by default on macOS
|
||||
dd bs=1 if="$tmpdir/insert_oversize.rom" of="$tmpdir/insert_fixed.rom" count="$INSERT_SIZE" 2>/dev/null || exit 1
|
||||
|
||||
# Patch size in NPDE
|
||||
dd bs=1 if="$tmpdir/insert.rom" of="$tmpdir/insert_fixed.rom" skip=$((0x2)) seek=$((0x48)) count=1 conv=notrunc 2>/dev/null || exit 1
|
||||
else
|
||||
cp "$tmpdir/insert.rom" "$tmpdir/insert_fixed.rom" || exit 1
|
||||
fi
|
||||
|
||||
# patch with vendor and device id from original GOP
|
||||
dd bs=1 if="$tmpdir/original_last_part.rom" of="$tmpdir/insert_fixed.rom" skip=$((0x20)) seek=$((0x20)) count=4 conv=notrunc 2>/dev/null || exit 1
|
||||
|
||||
if [ "$NVIDIA" -eq 1 ] && [ "$NpdeSize" -ne 0 ] ; then
|
||||
# Patch EFI image offset in PCIR
|
||||
dd bs=1 if="$tmpdir/original_last_part.rom" of="$tmpdir/insert_fixed.rom" skip=$((0x16)) seek=$((0x16)) count=1 conv=notrunc 2>/dev/null || exit 1
|
||||
|
||||
# Patch end marker in NPDE in fixed ROM (leave PCIR correct and EFI extractable from fixed ROM)
|
||||
echo -n -e '\x00' | dd bs=1 of="$tmpdir/insert_fixed.rom" seek=$((0x4A)) conv=notrunc 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
echo "Combining..."
|
||||
cat "$tmpdir/original_first_part.rom" "$tmpdir/insert_fixed.rom" "$tmpdir/original_last_part.rom" > "$tmpdir/combined.rom" || exit 1
|
||||
|
||||
# If new ROM is larger than truncate size, determine overflow by seeing
|
||||
# whether truncated ROM still has padding.
|
||||
# For Nvidia we would need to parse and navigate NVGI and NPDE headers
|
||||
# to calculate true occupied VBIOS space. To calcuate AMD occupation below
|
||||
# 128KB limit, we could navigate normal PCI expansion ROM headers.
|
||||
COMBINED_SIZE=$(stat -f%z "$tmpdir/combined.rom") || exit 1
|
||||
if [ "$COMBINED_SIZE" -le "$(($TRUNCATE_SIZE))" ] ; then
|
||||
TRUNCATE=0
|
||||
fi
|
||||
if [ "$TRUNCATE" -eq 1 ] ; then
|
||||
echo "Truncating to original size..."
|
||||
|
||||
dd bs=1 if="$tmpdir/combined.rom" of="$tmpdir/truncated.rom" count="$TRUNCATE_SIZE" 2>/dev/null || exit 1
|
||||
|
||||
COUNT=$(hexdump -v -e '1/8 " %016X\n"' "$tmpdir/truncated.rom" | tail -n 8 | grep "FFFFFFFFFFFFFFFF" | wc -l)
|
||||
if [ "$COUNT" -ne 8 ] ; then
|
||||
# Some Nvidia ROMs, at least, incorrectly have 00000000 padding after active contents
|
||||
# (it is incorrect, since writing only active contents using nvflash resets the rest to ffffffff).
|
||||
# May also be relevant if we ever have any truly 00000000 default ROM images.
|
||||
COUNT=$(hexdump -v -e '1/8 " %016X\n"' "$tmpdir/truncated.rom" | tail -n 8 | grep "0000000000000000" | wc -l)
|
||||
fi
|
||||
|
||||
if [ "$COUNT" -ne 8 ] ; then
|
||||
echo " - Not enough space within $((TRUNCATE_SIZE / 1024))k limit - aborting!" && exit 1
|
||||
fi
|
||||
|
||||
if [ "$AMD" -eq 1 ] ; then
|
||||
cat "$tmpdir/truncated.rom" "$tmpdir/keep_part.rom" > "$OUT_FILE" || exit 1
|
||||
else
|
||||
cp "$tmpdir/truncated.rom" "$OUT_FILE" || exit 1
|
||||
fi
|
||||
else
|
||||
cp "$tmpdir/combined.rom" "$OUT_FILE" || exit 1
|
||||
fi
|
||||
|
||||
# patch end marker in PCIR in out file
|
||||
echo -n -e '\x00' | dd bs=1 of="$OUT_FILE" seek=$(($GOP_OFFSET + 0x31)) conv=notrunc 2>/dev/null || exit 1
|
||||
|
||||
printf "Verifying (starting at 0x%X)...\n" "$GOP_OFFSET"
|
||||
dd bs=1 if="$OUT_FILE" of="$tmpdir/out_efi_part.rom" skip=$(($GOP_OFFSET)) 2>/dev/null || exit 1
|
||||
# UEFIRomExtract error messages are on stdout, so we cannot suppress unwanted normal output here
|
||||
UEFIRomExtract "$tmpdir/out_efi_part.rom" "$tmpdir/extracted.efi" || exit 1
|
||||
ERROR=0
|
||||
diff "$tmpdir/extracted.efi" "$EFI_FILE" 1>/dev/null || ERROR=1
|
||||
|
||||
if [ "$ERROR" -ne 0 ] ; then
|
||||
echo " - Failure comparing extracted EFI to original!"
|
||||
fi
|
||||
|
||||
OLD_EFI_COUNT=$(EfiRom -d "$tmpdir/original_last_part.rom" | grep "0x0EF1" | wc -l) || exit 1
|
||||
OLD_EFI_COUNT=$(($OLD_EFI_COUNT)) || exit 1
|
||||
|
||||
NEW_EFI_COUNT=$(EfiRom -d "$tmpdir/out_efi_part.rom" | grep "0x0EF1" | wc -l) || exit 1
|
||||
NEW_EFI_COUNT=$(($NEW_EFI_COUNT)) || exit 1
|
||||
|
||||
if [ "$NEW_EFI_COUNT" -ne $(($OLD_EFI_COUNT + 1)) ] ; then
|
||||
echo " - ${OLD_EFI_COUNT} EFI parts in original ROM, and detected ${NEW_EFI_COUNT} EFI parts in modified ROM, expected $(($OLD_EFI_COUNT + 1))!"
|
||||
ERROR=1
|
||||
fi
|
||||
|
||||
if [ "$ERROR" -eq 0 ] ; then
|
||||
echo "SUCCESS."
|
||||
else
|
||||
echo "*** WARNING - FAIL ***"
|
||||
fi
|
||||
|
||||
if [ "$TEMP_DIR" = "" ] ; then
|
||||
rm -rf "$tmpdir" || exit 1
|
||||
fi
|
||||
|
||||
echo "Done."
|
||||
44
hackintosh-guide/Utilities/FindSerialPort/FindSerialPort.command
Executable file
44
hackintosh-guide/Utilities/FindSerialPort/FindSerialPort.command
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
|
||||
## @file
|
||||
# Copyright (c) 2022, joevt. All rights reserved.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
##
|
||||
|
||||
serial_dev=$(ioreg -p IODeviceTree -lw0 | perl -e '
|
||||
$ioregpath=""; $pcipath=""; while (<>) {
|
||||
if ( /^([ |]*)\+\-o (.+) <class (\w+)/ ) {
|
||||
$indent = (length $1) / 2; $name = $2; $class = $3;
|
||||
$ioregpath =~ s|^((/[^/]*){$indent}).*|$1/$name|;
|
||||
$pcipath =~ s|^((/[^/]*){$indent}).*|$1/|;
|
||||
$name = ""; $uid = 0; $classcode = "";
|
||||
}
|
||||
|
||||
if ( $class eq "IOACPIPlatformDevice" ) { if ( /^[ |]*"name" = <"([^\"]*)">/ ) { $name = $1; }; if ( /^[ |]*"_UID" = "(\d+)"/ ) { $uid = $1; } }
|
||||
elsif ( $class eq "IOPCIDevice" ) { if ( /^[ |]*"pcidebug" = "\d+:(\d+):(\d+).*"/ ) { $name = sprintf("Pci(0x%x,0x%x)", $1, $2); } if ( /^[ |]*"class-code" = <(\w+)>/ ) { $classcode = $1; } }
|
||||
if ( /^[ |]*}/ && $name ) {
|
||||
if ( $class eq "IOACPIPlatformDevice" ) {
|
||||
if ($name eq "PNP0A03") { $name = sprintf ("PciRoot(0x%x)", $uid); }
|
||||
elsif ($name eq "PNP0A08") { $name = sprintf ("PciRoot(0x%x)", $uid); }
|
||||
elsif ($name =~ /PNP..../) { $name = sprintf ("Acpi(%s,0x%x)", $name, $uid); }
|
||||
# not translating all ACPI types since we only care about PCI devices
|
||||
}
|
||||
$pcipath .= $name;
|
||||
if ( $classcode eq "02000700" ) {
|
||||
$serialdevicepath = $pcipath =~ s/.*PciRoot\(0x0\)//r =~ s|/Pci\(||gr =~ s|\)| 00 00 |gr =~ s|0x||gr =~ s|,| |gr =~ s|\b(\w)\b|0\1|gr =~ s|$|FF|r;
|
||||
print $ioregpath =~ s|/Root/||r . "\n";
|
||||
print $pcipath =~ s|/*||r . "\n";
|
||||
print "xxd -p -r <<< \"" . $serialdevicepath . "\" | base64\n";
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
')
|
||||
|
||||
if [ "$serial_dev" != "" ]; then
|
||||
echo "${serial_dev}"
|
||||
else
|
||||
echo "No serial device found!"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
4
hackintosh-guide/Utilities/FindSerialPort/README.md
Executable file
4
hackintosh-guide/Utilities/FindSerialPort/README.md
Executable file
@@ -0,0 +1,4 @@
|
||||
FindSerialPort
|
||||
================
|
||||
|
||||
This script finds PCIe serial ports and outputs their paths. Thanks [joevt](https://github.com/joevt) for writing it.
|
||||
131
hackintosh-guide/Utilities/LegacyBoot/BootInstallBase.sh
Executable file
131
hackintosh-guide/Utilities/LegacyBoot/BootInstallBase.sh
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Install booter on physical disk.
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
|
||||
if [ ! -f "boot${ARCHS}${DUET_SUFFIX}" ] || [ ! -f boot0 ] || [ ! -f boot1f32 ]; then
|
||||
echo "Boot files are missing from this package!"
|
||||
echo "You probably forgot to build DuetPkg first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(uname)" = "Linux" ]; then
|
||||
if [ "$EUID" -ne 0 ]
|
||||
then echo "Please run this script as root"
|
||||
exit
|
||||
fi
|
||||
if [ "$(which lsblk)" = "" ]; then
|
||||
echo "lsblk tool is missing! Try installing util-linux package"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(which fdisk)" = "" ]; then
|
||||
echo "fdisk tool is missing!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f newbs origbs
|
||||
|
||||
echo "Select the disk where you want to install boot files (${ARCHS}${DUET_SUFFIX}):"
|
||||
lsblk -d | tail -n+2 | cut -d" " -f1
|
||||
echo "Example: sda"
|
||||
read -r DRIVE
|
||||
|
||||
DRIVE="/dev/${DRIVE}"
|
||||
|
||||
if ! lsblk "$DRIVE"; then
|
||||
echo Disk "${DRIVE}" not found
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Choose EFI partition on selected disk:"
|
||||
lsblk -f "${DRIVE}"
|
||||
echo "Example: sda1"
|
||||
read -r EFI_PART
|
||||
|
||||
EFI_PART="/dev/${EFI_PART}"
|
||||
|
||||
if ! lsblk -f "$EFI_PART" | grep -q -e FAT32 -e vfat; then
|
||||
echo "No FAT32 partition to install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Write MBR
|
||||
dd if=boot0 of="$DRIVE" bs=1 count=446 conv=notrunc || exit 1
|
||||
|
||||
umount "${EFI_PART}"
|
||||
|
||||
dd if="${EFI_PART}" count=1 of=origbs
|
||||
cp -v boot1f32 newbs
|
||||
dd if=origbs of=newbs skip=3 seek=3 bs=1 count=87 conv=notrunc
|
||||
dd if=/dev/random of=newbs skip=496 seek=496 bs=1 count=14 conv=notrunc
|
||||
dd if=newbs of="${EFI_PART}"
|
||||
|
||||
p=/tmp/$(uuidgen)/EFI
|
||||
mkdir -p "${p}" || exit 1
|
||||
mount -t vfat "${EFI_PART}" "${p}" -o rw,noatime,uid="$(id -u)",gid="$(id -g)" || exit 1
|
||||
|
||||
cp -v "boot${ARCHS}${DUET_SUFFIX}" "${p}/boot" || exit 1
|
||||
|
||||
echo Check "${p}" boot drive EFI folder to install OpenCorePkg
|
||||
|
||||
DISK_SCHEME=$(fdisk -l "${DRIVE}" | sed -n 's/.*Disklabel type: *//p')
|
||||
if [ "$DISK_SCHEME" != "gpt" ]; then
|
||||
BOOT_FLAG=$(dd if="$DRIVE" bs=1 count=1 status=none skip=$((0x1BE)) | od -t x1 -A n | tr -d ' ')
|
||||
if [ "$BOOT_FLAG" != "80" ]; then
|
||||
fdisk "$DRIVE" <<END
|
||||
p
|
||||
a
|
||||
1
|
||||
w
|
||||
END
|
||||
fi
|
||||
fi
|
||||
else
|
||||
rm -f newbs origbs
|
||||
|
||||
diskutil list
|
||||
echo "Disable SIP in the case of any problems with installation!!!"
|
||||
echo "Enter disk number to install OpenDuet (${ARCHS}${DUET_SUFFIX}) to:"
|
||||
read -r N
|
||||
|
||||
if ! diskutil info disk"${N}" | grep -q "/dev/disk"; then
|
||||
echo Disk "$N" not found
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! diskutil info disk"${N}"s1 | grep -q -e FAT_32 -e EFI; then
|
||||
echo "No FAT32 partition to install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Write MBR
|
||||
sudo fdisk -uy -f boot0 /dev/rdisk"${N}" || exit 1
|
||||
|
||||
diskutil umount disk"${N}"s1
|
||||
sudo dd if=/dev/rdisk"${N}"s1 count=1 of=origbs
|
||||
cp -v boot1f32 newbs
|
||||
sudo dd if=origbs of=newbs skip=3 seek=3 bs=1 count=87 conv=notrunc
|
||||
dd if=/dev/random of=newbs skip=496 seek=496 bs=1 count=14 conv=notrunc
|
||||
sudo dd if=newbs of=/dev/rdisk"${N}"s1
|
||||
#if [[ "$(sudo diskutil mount disk"${N}"s1)" == *"mounted" ]]
|
||||
if sudo diskutil mount disk"${N}"s1 | grep -q mounted; then
|
||||
cp -v "boot${ARCHS}${DUET_SUFFIX}" "$(diskutil info disk"${N}"s1 | sed -n 's/.*Mount Point: *//p')/boot"
|
||||
else
|
||||
p=/tmp/$(uuidgen)/EFI
|
||||
mkdir -p "${p}" || exit 1
|
||||
sudo mount_msdos /dev/disk"${N}"s1 "${p}" || exit 1
|
||||
cp -v "boot${ARCHS}${DUET_SUFFIX}" "${p}/boot" || exit 1
|
||||
open "${p}"
|
||||
fi
|
||||
|
||||
if diskutil info disk"${N}" | grep -q FDisk_partition_scheme; then
|
||||
sudo fdisk -e /dev/rdisk"$N" <<-MAKEACTIVE
|
||||
p
|
||||
f 1
|
||||
w
|
||||
y
|
||||
q
|
||||
MAKEACTIVE
|
||||
fi
|
||||
fi
|
||||
6
hackintosh-guide/Utilities/LegacyBoot/BootInstall_IA32.tool
Executable file
6
hackintosh-guide/Utilities/LegacyBoot/BootInstall_IA32.tool
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
export ARCHS=IA32
|
||||
export DUET_SUFFIX=""
|
||||
source BootInstallBase.sh
|
||||
6
hackintosh-guide/Utilities/LegacyBoot/BootInstall_IA32_BlockIO.tool
Executable file
6
hackintosh-guide/Utilities/LegacyBoot/BootInstall_IA32_BlockIO.tool
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
export ARCHS=IA32
|
||||
export DUET_SUFFIX="-blockio"
|
||||
source BootInstallBase.sh
|
||||
6
hackintosh-guide/Utilities/LegacyBoot/BootInstall_X64.tool
Executable file
6
hackintosh-guide/Utilities/LegacyBoot/BootInstall_X64.tool
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
export ARCHS=X64
|
||||
export DUET_SUFFIX=""
|
||||
source BootInstallBase.sh
|
||||
6
hackintosh-guide/Utilities/LegacyBoot/BootInstall_X64_BlockIO.tool
Executable file
6
hackintosh-guide/Utilities/LegacyBoot/BootInstall_X64_BlockIO.tool
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
export ARCHS=X64
|
||||
export DUET_SUFFIX="-blockio"
|
||||
source BootInstallBase.sh
|
||||
186
hackintosh-guide/Utilities/LegacyBoot/QemuBuild.command
Executable file
186
hackintosh-guide/Utilities/LegacyBoot/QemuBuild.command
Executable file
@@ -0,0 +1,186 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build QEMU image, example:
|
||||
# qemu-system-x86_64 -drive file=$QEMU_IMAGE/OpenCore.RO.raw -serial stdio \
|
||||
# -usb -device usb-kbd -device usb-mouse -s -m 8192
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
|
||||
usage () {
|
||||
echo "Usage: $(basename "$0") [X64|IA32|EFI]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
missing_files () {
|
||||
echo "Boot files are missing from this package!"
|
||||
echo "You probably forgot to build DuetPkg first."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -gt 1 ]; then
|
||||
usage
|
||||
elif [ $# -eq 1 ]; then
|
||||
if [ "$1" != "X64" ] && [ "$1" != "IA32" ] && [ "$1" != "EFI" ]; then
|
||||
usage
|
||||
fi
|
||||
ARCHS=$1
|
||||
elif [ "${ARCHS}" = "" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ "$ARCHS" != "EFI" ]; then
|
||||
if [ ! -f "boot${ARCHS}" ] || [ ! -f boot0 ] || [ ! -f boot1f32 ]; then
|
||||
missing_files
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$(which qemu-img)" = "" ]; then
|
||||
echo "QEMU installation missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d ROOT ]; then
|
||||
echo "No ROOT directory with ESP partition contents"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(uname)" = "Linux" ]; then
|
||||
if [ "$EUID" -ne 0 ]
|
||||
then echo "Please run this script as root"
|
||||
exit
|
||||
fi
|
||||
if [ "$(which qemu-nbd)" = "" ]; then
|
||||
echo "Your QEMU installation doesn't contain qemu-nbd tool!"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(which mkfs.vfat)" = "" ]; then
|
||||
echo "mkfs.vfat not found, dosfstools package is missing?"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(which fdisk)" = "" ]; then
|
||||
echo "fdisk tool is missing!"
|
||||
exit 1
|
||||
fi
|
||||
IMAGE=OpenCore.raw
|
||||
DIR="$IMAGE.d"
|
||||
NBD=/dev/nbd0
|
||||
|
||||
rm -rf "$DIR"
|
||||
rm -f "$IMAGE" newbs origbs
|
||||
mkdir -p "$DIR"
|
||||
|
||||
# Create 200M MS-DOS bootable disk image with 1 FAT32 partition
|
||||
modprobe nbd
|
||||
qemu-img create -f raw "$IMAGE" 200M
|
||||
fdisk "$IMAGE" << END
|
||||
o
|
||||
n
|
||||
p
|
||||
1
|
||||
2048
|
||||
409599
|
||||
t
|
||||
b
|
||||
a
|
||||
w
|
||||
END
|
||||
qemu-nbd -f raw --connect="$NBD" "$IMAGE"
|
||||
# Wait a it after mounting
|
||||
sleep 2
|
||||
mkfs.vfat -F32 ${NBD}p1
|
||||
|
||||
if [ "$ARCHS" != "EFI" ]; then
|
||||
# Copy boot1f32 into FAT32 Boot Record
|
||||
dd if=${NBD}p1 of=origbs count=1
|
||||
cp -v boot1f32 newbs
|
||||
dd if=origbs of=newbs skip=3 seek=3 bs=1 count=87 conv=notrunc
|
||||
dd if=/dev/random of=newbs skip=496 seek=496 bs=1 count=14 conv=notrunc
|
||||
dd if=newbs of=${NBD}p1 conv=notrunc
|
||||
fi
|
||||
|
||||
mount -t vfat ${NBD}p1 "$DIR" -o rw,noatime,uid="$(id -u)",gid="$(id -g)"
|
||||
sleep 2
|
||||
|
||||
if [ "$ARCHS" != "EFI" ]; then
|
||||
# Copy boot file into FAT32 file system
|
||||
cp -v "boot${ARCHS}" "$DIR/boot"
|
||||
fi
|
||||
|
||||
# Copy ESP contents into FAT32 file system
|
||||
cp -rv ROOT/* "$DIR"
|
||||
|
||||
# Remove temporary files
|
||||
sleep 2
|
||||
umount -R "$DIR"
|
||||
qemu-nbd -d "$NBD"
|
||||
rm -r "$DIR"
|
||||
rm newbs origbs
|
||||
|
||||
if [ "$ARCHS" != "EFI" ]; then
|
||||
# Copy boot0 into MBR
|
||||
dd if=boot0 of="$IMAGE" bs=1 count=446 conv=notrunc
|
||||
fi
|
||||
|
||||
chown "$(whoami)" "$IMAGE"
|
||||
elif [ "$(uname)" = "Darwin" ]; then
|
||||
rm -f OpenCore.dmg.sparseimage OpenCore.RO.raw OpenCore.RO.dmg
|
||||
hdiutil create -size 200m -layout "UNIVERSAL HD" -type SPARSE -o OpenCore.dmg
|
||||
newDevice=$(hdiutil attach -nomount OpenCore.dmg.sparseimage |head -n 1 | awk '{print $1}')
|
||||
echo newdevice "$newDevice"
|
||||
|
||||
diskutil partitionDisk "${newDevice}" 1 MBR fat32 TEST R
|
||||
|
||||
# boot install script
|
||||
diskutil list
|
||||
N=$(echo "$newDevice" | tr -dc '0-9')
|
||||
echo "Will be installed to Disk ${N}"
|
||||
|
||||
|
||||
if [[ ! $(diskutil info disk"${N}" | sed -n 's/.*Device Node: *//p') ]]
|
||||
then
|
||||
echo Disk "$N" not found
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FS=$(diskutil info disk"${N}"s1 | sed -n 's/.*File System Personality: *//p')
|
||||
echo "$FS"
|
||||
|
||||
if [ "$FS" != "MS-DOS FAT32" ]
|
||||
then
|
||||
echo "No FAT32 partition to install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$ARCHS" != "EFI" ]; then
|
||||
# Write MBR
|
||||
sudo fdisk -f boot0 -u /dev/rdisk"${N}"
|
||||
|
||||
# Write boot1f32
|
||||
diskutil umount disk"${N}"s1
|
||||
sudo dd if=/dev/rdisk"${N}"s1 count=1 of=origbs
|
||||
cp -v boot1f32 newbs
|
||||
sudo dd if=origbs of=newbs skip=3 seek=3 bs=1 count=87 conv=notrunc
|
||||
dd if=/dev/random of=newbs skip=496 seek=496 bs=1 count=14 conv=notrunc
|
||||
sudo dd if=newbs of=/dev/rdisk"${N}"s1
|
||||
diskutil mount disk"${N}"s1
|
||||
|
||||
# Copy boot
|
||||
cp -v "boot${ARCHS}" "$(diskutil info disk"${N}"s1 | sed -n 's/.*Mount Point: *//p')/boot"
|
||||
fi
|
||||
|
||||
cp -rv ROOT/* "$(diskutil info disk"${N}"s1 | sed -n 's/.*Mount Point: *//p')"
|
||||
|
||||
if [ "$ARCHS" != "EFI" ] && [ "$(diskutil info disk"${N}" | sed -n 's/.*Content (IOContent): *//p')" == "FDisk_partition_scheme" ]; then
|
||||
sudo fdisk -e /dev/rdisk"$N" <<-MAKEACTIVE
|
||||
p
|
||||
f 1
|
||||
w
|
||||
y
|
||||
q
|
||||
MAKEACTIVE
|
||||
fi
|
||||
|
||||
hdiutil detach "$newDevice"
|
||||
hdiutil convert -format UDRO OpenCore.dmg.sparseimage -o OpenCore.RO.dmg
|
||||
qemu-img convert -f dmg -O raw OpenCore.RO.dmg OpenCore.RO.raw
|
||||
fi
|
||||
7
hackintosh-guide/Utilities/LegacyBoot/README.md
Executable file
7
hackintosh-guide/Utilities/LegacyBoot/README.md
Executable file
@@ -0,0 +1,7 @@
|
||||
BootInstall
|
||||
===========
|
||||
|
||||
This tool installs legacy DuetPkg environment on GPT-formatted disk
|
||||
to enable UEFI environment on BIOS-based systems.
|
||||
|
||||
Source code: https://github.com/acidanthera/DuetPkg
|
||||
BIN
hackintosh-guide/Utilities/LegacyBoot/boot0
Executable file
BIN
hackintosh-guide/Utilities/LegacyBoot/boot0
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/LegacyBoot/boot1f32
Executable file
BIN
hackintosh-guide/Utilities/LegacyBoot/boot1f32
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/LegacyBoot/bootIA32
Executable file
BIN
hackintosh-guide/Utilities/LegacyBoot/bootIA32
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/LegacyBoot/bootIA32-blockio
Executable file
BIN
hackintosh-guide/Utilities/LegacyBoot/bootIA32-blockio
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/LegacyBoot/bootX64
Executable file
BIN
hackintosh-guide/Utilities/LegacyBoot/bootX64
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/LegacyBoot/bootX64-blockio
Executable file
BIN
hackintosh-guide/Utilities/LegacyBoot/bootX64-blockio
Executable file
Binary file not shown.
767
hackintosh-guide/Utilities/LogoutHook/Launchd.command
Executable file
767
hackintosh-guide/Utilities/LogoutHook/Launchd.command
Executable file
@@ -0,0 +1,767 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Copyright © 2022 Mike Beaton. All rights reserved.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
# Examine log at /var/log/org.acidanthera.nvramhook.launchd/launchd.log.
|
||||
#
|
||||
# Script can run immediately or be installed as daemon or logout hook. Installs as
|
||||
# launch daemon on Yosemite and above, which is the only supported approach that on
|
||||
# shutdown reads NVRAM after macOS installer vars are set, in more recent macOS
|
||||
# installers (e.g. Monterey).
|
||||
#
|
||||
# Non-installed script can be run directly as 'daemon', 'agent' or 'logout' hook by
|
||||
# specifying one of those options on the command line. For 'logout' this saves NVRAM
|
||||
# immediately, for 'daemon' or 'agent' this waits to be terminated e.g. CTRL+C before
|
||||
# saving.
|
||||
#
|
||||
# Credits:
|
||||
# - Rodion Shingarev (with additional contributions by vit9696 and PMheart) for
|
||||
# original LogoutHook.command
|
||||
# - Rodion Shingarev for nvramdump command
|
||||
# - Mike Beaton for this script
|
||||
#
|
||||
|
||||
usage() {
|
||||
echo "Usage: ${SELFNAME} [install|uninstall|status] [logout|daemon|agent|both]"
|
||||
echo " - [install|uninstall] without type uses recommended type for macOS version"
|
||||
echo " - 'status' shows status of agent, daemon and logout hook"
|
||||
echo " - 'status daemon' or 'status agent' give additional detail"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Non-sudo log only if agent will use it, so logfile has been chmod-ed at install.
|
||||
# If only daemon or logout hook will use it, explicit sudo required at install
|
||||
# time, but harmless when running.
|
||||
doLog() {
|
||||
if [ ! "$AGENT" = "1" ] ; then
|
||||
# macOS recreates this for daemon at reboot, but preferable
|
||||
# to continue logging immediately after any log cleardown,
|
||||
# also never recreated except for this with logout hook
|
||||
sudo mkdir -p "${LOGDIR}"
|
||||
|
||||
# 'sudo tee' to write to log file as root (could also 'sh -c') for installation steps;
|
||||
# will be root anyway when running installed as daemon or logout hook
|
||||
if [ ! "${LOG_PREFIX}" = "" ] ; then
|
||||
sudo tee -a "${LOGFILE}" > /dev/null << EOF
|
||||
$(date +"${DATEFORMAT}") (${LOG_PREFIX}) ${1}
|
||||
EOF
|
||||
else
|
||||
sudo tee -a "${LOGFILE}" > /dev/null << EOF
|
||||
${1}
|
||||
EOF
|
||||
fi
|
||||
else
|
||||
# non-sudo copy, could simplify
|
||||
if [ ! "${LOG_PREFIX}" = "" ] ; then
|
||||
tee -a "${LOGFILE}" > /dev/null << EOF
|
||||
$(date +"${DATEFORMAT}") (${LOG_PREFIX}) ${1}
|
||||
EOF
|
||||
else
|
||||
tee -a "${LOGFILE}" > /dev/null << EOF
|
||||
${1}
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! "$INSTALLED" = "1" ] ; then
|
||||
echo "${1}"
|
||||
fi
|
||||
}
|
||||
|
||||
abort() {
|
||||
doLog "Fatal error: ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# abort without logging or requiring sudo
|
||||
earlyAbort() {
|
||||
echo "Fatal error: ${1}" > /dev/stderr
|
||||
exit 1
|
||||
}
|
||||
|
||||
getDarwinMajorVersion() {
|
||||
darwin_ver=$(uname -r)
|
||||
# Cannot add -r in earlier macOS
|
||||
# shellcheck disable=SC2162
|
||||
IFS="." read -a darwin_ver_array <<< "$darwin_ver"
|
||||
echo "${darwin_ver_array[0]}"
|
||||
}
|
||||
|
||||
installLog() {
|
||||
if [ ! -d "${LOGDIR}" ] ; then
|
||||
# We intentionally don't use -p as something is probably
|
||||
# weird if parent dirs don't exist here
|
||||
sudo mkdir "${LOGDIR}" || abort "Failed to mkdir for logfile!"
|
||||
fi
|
||||
|
||||
if [ ! -f "${LOGFILE}" ] ; then
|
||||
sudo touch "${LOGFILE}" || abort "Failed to touch logfile!"
|
||||
fi
|
||||
|
||||
if [ "$AGENT" = "1" ] ; then
|
||||
sudo chmod 666 "${LOGFILE}" || abort "Failed to chmod logfile!"
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
FAIL="Failed to install!"
|
||||
|
||||
if [ ! -d "${PRIVILEGED_HELPER_TOOLS}" ] ; then
|
||||
sudo mkdir "${PRIVILEGED_HELPER_TOOLS}" || abort "${FAIL}"
|
||||
fi
|
||||
|
||||
# Copy executable files into place.
|
||||
sudo cp "${SELFNAME}" "${HELPER}" || abort "${FAIL}"
|
||||
sudo cp nvramdump "${NVRAMDUMP}" || abort "${FAIL}"
|
||||
|
||||
# Install logout hook.
|
||||
if [ "$LOGOUT" = "1" ] ; then
|
||||
sudo defaults write com.apple.loginwindow LogoutHook "${HELPER}" || abort "${FAIL}"
|
||||
fi
|
||||
|
||||
if [ "$DAEMON" = "1" ] ; then
|
||||
# Make customised Launchd.command.plist for daemon.
|
||||
if [ "$BOTH" = "1" ] ; then
|
||||
LAUNCHFILE_DAEMON="${LAUNCHFILE_DAEMON} (1 of 2)"
|
||||
fi
|
||||
if [ -f "$LAUNCHFILE_DAEMON" ] ; then
|
||||
sudo rm "$LAUNCHFILE_DAEMON" || abort "${FAIL}"
|
||||
fi
|
||||
sudo ln -s "$(which sh)" "$LAUNCHFILE_DAEMON" || abort "${FAIL}"
|
||||
sed "s/\$LABEL/${ORG}.daemon/g;s/\$HELPER/$(sed 's/\//\\\//g' <<< "$HELPER")/g;s/\$PARAM/${DAEMON_PARAMS}/g;s/\$LOGFILE/$(sed 's/\//\\\//g' <<< "$LOGFILE")/g;s/\$LAUNCHFILE/$(sed 's/\//\\\//g' <<< "$LAUNCHFILE_DAEMON")/g" "Launchd.command.plist" > "/tmp/Launchd.command.plist" || abort "${FAIL}"
|
||||
sudo cp "/tmp/Launchd.command.plist" "${DAEMON_PLIST}" || abort "${FAIL}"
|
||||
rm -f /tmp/Launchd.command.plist
|
||||
|
||||
# Launch already installed daemon.
|
||||
sudo launchctl load "${DAEMON_PLIST}" || abort "${FAIL}"
|
||||
fi
|
||||
|
||||
if [ "$AGENT" = "1" ] ; then
|
||||
# Make customised Launchd.command.plist for agent.
|
||||
if [ "$BOTH" = "1" ] ; then
|
||||
LAUNCHFILE_AGENT="${LAUNCHFILE_AGENT} (2 of 2)"
|
||||
fi
|
||||
if [ -f "$LAUNCHFILE_AGENT" ] ; then
|
||||
sudo rm "$LAUNCHFILE_AGENT" || abort "${FAIL}"
|
||||
fi
|
||||
sudo ln -s "$(which sh)" "$LAUNCHFILE_AGENT" || abort "${FAIL}"
|
||||
sed "s/\$LABEL/${ORG}.agent/g;s/\$HELPER/$(sed 's/\//\\\//g' <<< "$HELPER")/g;s/\$PARAM/${AGENT_PARAMS}/g;s/\$LOGFILE/$(sed 's/\//\\\//g' <<< "$LOGFILE")/g;s/\$LAUNCHFILE/$(sed 's/\//\\\//g' <<< "$LAUNCHFILE_AGENT")/g" "Launchd.command.plist" > "/tmp/Launchd.command.plist" || abort "${FAIL}"
|
||||
sudo cp "/tmp/Launchd.command.plist" "${AGENT_PLIST}" || abort "${FAIL}"
|
||||
rm -f /tmp/Launchd.command.plist
|
||||
|
||||
# Launch already installed agent.
|
||||
sudo launchctl bootstrap gui/501 "${AGENT_PLIST}" || abort "${FAIL}"
|
||||
fi
|
||||
|
||||
echo "Installed."
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
UNINSTALLED=1
|
||||
|
||||
if [ "$DAEMON" = "1" ] || [ "$AGENT" = "1" ] ; then
|
||||
if [ "$DAEMON_PID" = "" ] ; then
|
||||
UNINSTALLED=2
|
||||
if [ ! "$INSTALL" = "1" ] && [ "$DAEMON" = "1" ] ; then
|
||||
echo "Daemon was not installed!" > /dev/stderr
|
||||
fi
|
||||
else
|
||||
# Place special value in saved device node so that nvram.plist is not updated at uninstall
|
||||
sudo /usr/sbin/nvram "${BOOT_NODE}=null" || abort "Failed to save null boot device!"
|
||||
sudo launchctl unload "${DAEMON_PLIST}" || UNINSTALLED=0
|
||||
DAEMON_PID=
|
||||
fi
|
||||
sudo rm -f "${DAEMON_PLIST}"
|
||||
sudo rm -f "${LAUNCHFILE_DAEMON}"*
|
||||
fi
|
||||
|
||||
if [ "$AGENT" = "1" ] || [ "$DAEMON" = "1" ] ; then
|
||||
if [ "$AGENT_PID" = "" ] ; then
|
||||
UNINSTALLED=2
|
||||
if [ ! "$INSTALL" = "1" ] && [ "$AGENT" = "1" ] ; then
|
||||
echo "Agent was not installed!" > /dev/stderr
|
||||
fi
|
||||
else
|
||||
# Place special value in saved device node so that nvram.plist is not updated at uninstall
|
||||
sudo /usr/sbin/nvram "${BOOT_NODE}=null" || abort "Failed to save null boot device!"
|
||||
sudo launchctl bootout "${AGENT_ID}" || UNINSTALLED=0
|
||||
AGENT_PID=
|
||||
fi
|
||||
sudo rm -f "${AGENT_PLIST}"
|
||||
sudo rm -f "${LAUNCHFILE_AGENT}"*
|
||||
fi
|
||||
|
||||
if [ "$LOGOUT" = "1" ] ; then
|
||||
if [ "$LOGOUT_HOOK" = "" ] ; then
|
||||
UNINSTALLED=2
|
||||
echo "Logout hook was not installed!" > /dev/stderr
|
||||
else
|
||||
sudo defaults delete com.apple.loginwindow LogoutHook || UNINSTALLED=0
|
||||
LOGOUT_HOOK=
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$DAEMON_PID" = "" ] &&
|
||||
[ "$AGENT_PID" = "" ] &&
|
||||
[ "$LOGOUT_HOOK" = "" ] ; then
|
||||
sudo rm -f "${HELPER}"
|
||||
sudo rm -f "${NVRAMDUMP}"
|
||||
fi
|
||||
|
||||
if [ ! "$INSTALL" = "1" ] ; then
|
||||
if [ "$UNINSTALLED" = "0" ] ; then
|
||||
echo "Could not uninstall!"
|
||||
elif [ "$UNINSTALLED" = "1" ] ; then
|
||||
echo "Uninstalled."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
status() {
|
||||
if [ "$DAEMON" = "1" ] ; then
|
||||
# Detailed info on daemon.
|
||||
sudo launchctl print "$DAEMON_ID"
|
||||
elif [ "$AGENT" = "1" ] ; then
|
||||
# Detailed info on agent.
|
||||
sudo launchctl print "$AGENT_ID"
|
||||
else
|
||||
echo "Daemon pid = ${DAEMON_PID}"
|
||||
echo "Agent pid = ${AGENT_PID}"
|
||||
if [ ! "$BOTH" = "1" ] ; then
|
||||
echo "LogoutHook = ${LOGOUT_HOOK}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# On earlier macOS (at least Mojave in VMWare) there is an intermittent
|
||||
# problem where msdos/FAT kext is occasionally not available when we try
|
||||
# to mount the drive on daemon exit; mounting and unmounting on daemon
|
||||
# start here fixes this.
|
||||
# Doing this introduces a different problem, since this early mount sometimes
|
||||
# fails initially, however (as with 'diskutil info' above) in that case the
|
||||
# script gets restarted by launchd after ~5s, and eventually either succeeds or
|
||||
# finds the drive already mounted (when applicable, i.e. not marked as ESP).
|
||||
#
|
||||
# On later macOS (Sonoma) this problem recurs, and the strategy of co-ordinated
|
||||
# daemon and user agent was introduced to handle this (see saveNvram()):
|
||||
#
|
||||
# At startup:
|
||||
# - daemon waits to see agent
|
||||
# - daemon mounts ESP
|
||||
# - daemon kills agent, then waits 1s
|
||||
# - agent:
|
||||
# o traps kill
|
||||
# o touches nvram.plist (which forces kext to be loaded)
|
||||
# o exits
|
||||
# - after 1s wait daemon unmounts ESP
|
||||
#
|
||||
# At shutdown:
|
||||
# - daemon mounts ESP and writes nvram.plist
|
||||
# o this only works if the above palaver at startup has happened recently
|
||||
#
|
||||
earlyMount() {
|
||||
mount_path=$(mount | sed -n "s:${node} on \(.*\) (.*$:\1:p")
|
||||
if [ ! "${mount_path}" = "" ] ; then
|
||||
doLog "Early mount not needed, already mounted at ${mount_path}"
|
||||
else
|
||||
sudo /usr/sbin/diskutil mount "${node}" 1>/dev/null || abort "Early mount failed!"
|
||||
sleep 1 & wait
|
||||
sudo /usr/sbin/diskutil unmount "${node}" 1>/dev/null || abort "Early unmount failed!"
|
||||
doLog "Early mount/unmount succeeded"
|
||||
fi
|
||||
}
|
||||
|
||||
# Save some diskutil info in emulated NVRAM for use at daemon shutdown:
|
||||
# - While we can access diskutil normally at agent startup and at logout hook;
|
||||
# - We cannot use diskutil at daemon shutdown, because:
|
||||
# "Unable to run because unable to use the DiskManagement framework.
|
||||
# Common reasons include, but are not limited to, the DiskArbitration
|
||||
# framework being unavailable due to being booted in single-user mode."
|
||||
# - At daemon startup, diskutil works but the device may not be ready
|
||||
# immediately, but macOS restarts us quickly (~5s) and then we can run.
|
||||
# Note that saving any info for use at process shutdown if not running as
|
||||
# daemon (sudo) would have to go into e.g. a file not nvram.
|
||||
#
|
||||
# $1 = 0 - Set node var, but do not save in emulated NVRAM
|
||||
# $1 = 1 - Set node var and save in emulated NVRAM
|
||||
#
|
||||
saveMount() {
|
||||
UUID="$(/usr/sbin/nvram "${BOOT_PATH}" | sed 's/.*GPT,\([^,]*\),.*/\1/')"
|
||||
if [ "$(printf '%s' "${UUID}" | /usr/bin/wc -c)" -eq 36 ] && [ -z "$(echo "${UUID}" | sed 's/[-0-9A-F]//g')" ] ; then
|
||||
node="$(/usr/sbin/diskutil info "${UUID}" | sed -n 's/.*Device Node: *//p')"
|
||||
|
||||
# This may randomly fail initially, if so the script gets restarted by
|
||||
# launchd and eventually succeeds.
|
||||
if [ "${node}" = "" ] ; then
|
||||
abort "Cannot access device node!"
|
||||
fi
|
||||
|
||||
doLog "Found boot device at ${node}"
|
||||
|
||||
if [ "${1}" = "1" ] ; then
|
||||
if [ "$BOTH" = "1" ] &&
|
||||
[ "$DAEMON" = "1" ]; then
|
||||
doLog "Using both agent and daemon, delaying early mount"
|
||||
else
|
||||
earlyMount
|
||||
fi
|
||||
|
||||
# Use hopefully emulated NVRAM as temporary storage for the boot
|
||||
# device node discovered with diskutil.
|
||||
# If we are in emulated NVRAM, should not appear at next boot as
|
||||
# nvramdump does not write values from OC GUID back to nvram.plist.
|
||||
sudo /usr/sbin/nvram "${BOOT_NODE}=${node}" || abort "Failed to store boot device!"
|
||||
fi
|
||||
else
|
||||
abort "Missing or invalid ${BOOT_PATH} value!"
|
||||
fi
|
||||
}
|
||||
|
||||
# $1 = 0 - Use existing node var, do not fetch from emulated NVRAM
|
||||
# $1 = 1 - Use node var saved in emulated NVRAM
|
||||
#
|
||||
# $2 = 0 - Just touch nvram.plist
|
||||
# $2 = 1 - Really save NVRAM state to nvram.plist
|
||||
# $2 = 2 - Just pre-mount
|
||||
#
|
||||
saveNvram() {
|
||||
if [ "${1}" = "1" ] ; then
|
||||
# . matches tab, note that \t for tab cannot be used in earlier macOS (e.g Mojave)
|
||||
node=$(/usr/sbin/nvram "$BOOT_NODE" | sed -n "s/${BOOT_NODE}.//p")
|
||||
if [ "$2" = "1" ]; then
|
||||
if [ ! "$INSTALLED" = "1" ] ; then
|
||||
# don't trash saved value if daemon or agent is live
|
||||
launchctl print "$DAEMON_ID" 2>/dev/null 1>/dev/null || \
|
||||
launchctl print "$AGENT_ID" 2>/dev/null 1>/dev/null || \
|
||||
sudo /usr/sbin/nvram -d "$BOOT_NODE"
|
||||
else
|
||||
sudo /usr/sbin/nvram -d "$BOOT_NODE"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${node}" = "" ] ; then
|
||||
abort "Cannot access saved device node!"
|
||||
elif [ "${node}" = "null" ] ; then
|
||||
if [ ! "$AGENT" = "1" ] ; then
|
||||
sudo /usr/sbin/nvram "${BOOT_NODE}=" || abort "Failed to remove boot node variable!"
|
||||
fi
|
||||
doLog "Uninstalling…"
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$INSTALLED" = "1" ] &&
|
||||
[ "$AGENT" = "1" ] ; then
|
||||
if /usr/sbin/nvram "${DAEMON_WAITING}" 1>/dev/null 2>/dev/null ; then
|
||||
echo -n
|
||||
else
|
||||
doLog "Daemon is not waiting, stopping..."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
mount_path=$(mount | sed -n "s:${node} on \(.*\) (.*$:\1:p")
|
||||
if [ ! "${mount_path}" = "" ] ; then
|
||||
doLog "Found mounted at ${mount_path}"
|
||||
if [ "$DAEMON" = "1" ] &&
|
||||
[ "$BOTH" = "1" ] &&
|
||||
[ ! "$2" = "2" ] &&
|
||||
[ "$(getDarwinMajorVersion)" -ge ${LAUNCHD_BOTH} ] ; then
|
||||
doLog "WARNING: User-mounted ESP may not save NVRAM successfully"
|
||||
fi
|
||||
else
|
||||
if [ "$INSTALLED" = "1" ] &&
|
||||
[ "$AGENT" = "1" ] ; then
|
||||
# This should have been pre-mounted for us by daemon
|
||||
abort "Agent cannot mount ESP!"
|
||||
fi
|
||||
# use reasonably assumed unique path
|
||||
mount_path="/Volumes/${UNIQUE_DIR}"
|
||||
sudo mkdir -p "${mount_path}" || abort "Failed to make directory!"
|
||||
sudo mount -t msdos "${node}" "${mount_path}" || abort "Failed to mount!"
|
||||
doLog "Mounted at ${mount_path}"
|
||||
MOUNTED=1
|
||||
fi
|
||||
|
||||
if [ "${NVRAM_DIR}" = "" ] ; then
|
||||
nvram_dir="${mount_path}"
|
||||
else
|
||||
nvram_dir="${mount_path}/${NVRAM_DIR}"
|
||||
if [ ! -d "${nvram_dir}" ] ; then
|
||||
mkdir "${nvram_dir}" || abort "Failed to make directory ${nvram_dir}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${2}" = "2" ] ; then
|
||||
doLog "Killing agent…"
|
||||
sudo /usr/sbin/nvram "${DAEMON_WAITING}=1" || abort "Failed to set daemon-waiting variable!"
|
||||
kill "$3" || abort "Cannot kill agent!"
|
||||
if [ "$BOTH" = "1" ] ; then
|
||||
sleep 1 & wait
|
||||
sudo /usr/sbin/nvram "${DAEMON_WAITING}=" || abort "Failed to remove daemon-waiting variable!"
|
||||
fi
|
||||
elif [ ! "${2}" = "1" ] ; then
|
||||
# Just touch nvram.plist
|
||||
touch "${nvram_dir}/nvram.plist" || abort "Failed to touch nvram.plist!"
|
||||
doLog "Touched nvram.plist"
|
||||
else
|
||||
# Really save NVRAM
|
||||
rm -f /tmp/nvram.plist
|
||||
${USE_NVRAMDUMP} || abort "failed to save nvram.plist!"
|
||||
|
||||
if [ -f "${nvram_dir}/nvram.plist" ] ; then
|
||||
cp "${nvram_dir}/nvram.plist" "${nvram_dir}/nvram.fallback" || abort "Failed to create nvram.fallback!"
|
||||
doLog "Copied nvram.fallback"
|
||||
fi
|
||||
|
||||
cp /tmp/nvram.plist "${nvram_dir}/nvram.plist" || abort "Failed to copy nvram.plist!"
|
||||
doLog "Saved nvram.plist"
|
||||
|
||||
rm -f /tmp/nvram.plist
|
||||
|
||||
if [ -f "${nvram_dir}/nvram.used" ] ; then
|
||||
rm "${nvram_dir}/nvram.used" || abort "Failed to delete nvram.used!"
|
||||
doLog "Deleted nvram.used"
|
||||
fi
|
||||
fi
|
||||
|
||||
# We would like to always unmount here if we made the mount point, but at exit of
|
||||
# installed daemon umount fails with "Resource busy" and diskutil is not available.
|
||||
# This should not cause any problem except that the boot drive will be left mounted
|
||||
# at the unique path if the daemon process gets killed (the process would then be
|
||||
# restarted by macOS and NVRAM should still be saved at exit).
|
||||
while [ "$MOUNTED" = "1" ] ;
|
||||
do
|
||||
# For logout hook or if running in immediate mode, we can clean up.
|
||||
if [ "$LOGOUT" = "1" ] ||
|
||||
[ ! "$INSTALLED" = "1" ] ||
|
||||
[ "$2" = "2" ] ; then
|
||||
# Sometimes needed after directory access...
|
||||
# # if [ ! "$2" = "2" ] ; then
|
||||
# # sleep 1 & wait
|
||||
# # fi
|
||||
|
||||
# Depending on how installed and macOS version, either unmount may be needed.
|
||||
# (On Lion failure of 'diskutil unmount' may be to say volume is already unmounted when it is not.)
|
||||
MOUNTED=0
|
||||
sudo diskutil unmount "${node}" 1>/dev/null 2>/dev/null || MOUNTED=1
|
||||
|
||||
if [ "$MOUNTED" = "0" ] ; then
|
||||
doLog "Unmounted with diskutil"
|
||||
else
|
||||
sudo umount "${node}" && MOUNTED=0
|
||||
if [ "$MOUNTED" = "0" ] ; then
|
||||
sudo rmdir "${mount_path}" || abort "Failed to remove directory!"
|
||||
doLog "Unmounted with umount"
|
||||
else
|
||||
if [ "$INSTALLED" = "1" ] ; then
|
||||
abort "Failed to unmount!"
|
||||
else
|
||||
doLog "Retrying…"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
onComplete() {
|
||||
doLog "Trap ${1}"
|
||||
|
||||
if [ "$DAEMON" = "1" ] ; then
|
||||
saveNvram 1 1
|
||||
elif [ "$AGENT" = "1" ] ; then
|
||||
saveNvram 1 0
|
||||
fi
|
||||
|
||||
doLog "Ended."
|
||||
|
||||
# Required if running directly (launchd kills any orphaned child processes by default).
|
||||
# ShellCheck ref: https://github.com/koalaman/shellcheck/issues/648#issuecomment-208306771
|
||||
# shellcheck disable=SC2046
|
||||
kill $(jobs -p)
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
if [ ! -x /usr/bin/dirname ] ||
|
||||
[ ! -x /usr/bin/basename ] ||
|
||||
[ ! -x /usr/bin/wc ] ||
|
||||
[ ! -x /usr/sbin/diskutil ] ||
|
||||
[ ! -x /usr/sbin/nvram ] ; then
|
||||
earlyAbort "Unix environment is broken!"
|
||||
fi
|
||||
|
||||
NVRAM_DIR="NVRAM"
|
||||
|
||||
OCNVRAMGUID="4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102"
|
||||
|
||||
# Darwin version from which we can wake a sleeping daemon, corresponds to Yosemite and upwards.
|
||||
LAUNCHD_DARWIN=14
|
||||
|
||||
# Darwin version for which we need both agent and daemon, corresponds to Sonoma and upwards.
|
||||
LAUNCHD_BOTH=23
|
||||
|
||||
# OC generated NVRAM var
|
||||
BOOT_PATH="${OCNVRAMGUID}:boot-path"
|
||||
|
||||
# temp storage vars for this script
|
||||
BOOT_NODE="${OCNVRAMGUID}:boot-node"
|
||||
DAEMON_WAITING="${OCNVRAMGUID}:daemon-waiting"
|
||||
|
||||
# re-use as unique directory name for mount point when needed
|
||||
UNIQUE_DIR="${BOOT_NODE}"
|
||||
|
||||
PRIVILEGED_HELPER_TOOLS="/Library/PrivilegedHelperTools"
|
||||
|
||||
ORG="org.acidanthera.nvramhook"
|
||||
NVRAMDUMP="${PRIVILEGED_HELPER_TOOLS}/${ORG}.nvramdump.helper"
|
||||
|
||||
DAEMON_ID="system/${ORG}.daemon"
|
||||
DAEMON_PLIST="/Library/LaunchDaemons/${ORG}.daemon.plist"
|
||||
DAEMON_PARAMS="daemon"
|
||||
|
||||
AGENT_ID="gui/501/${ORG}.agent"
|
||||
AGENT_PLIST="/Library/LaunchAgents/${ORG}.agent.plist"
|
||||
AGENT_PARAMS="agent"
|
||||
|
||||
HELPER="${PRIVILEGED_HELPER_TOOLS}/${ORG}.helper"
|
||||
LAUNCHFILE_DAEMON="${PRIVILEGED_HELPER_TOOLS}/${ORG} System Daemon"
|
||||
LAUNCHFILE_AGENT="${PRIVILEGED_HELPER_TOOLS}/${ORG} User Agent"
|
||||
|
||||
LOGDIR="/var/log/${ORG}.launchd"
|
||||
# launchd .plist configuration is set in install() to redirect stdout and stderr (useful) to LOGFILE anyway.
|
||||
LOGFILE="${LOGDIR}/launchd.log"
|
||||
|
||||
SELFDIR="$(/usr/bin/dirname "${0}")"
|
||||
SELFNAME="$(/usr/bin/basename "${0}")"
|
||||
|
||||
# -R sets this, on newer macOS only; date format is required because root from user sudo and root
|
||||
# when running daemons or logout hooks are picking up different date formats.
|
||||
DATEFORMAT="%a, %d %b %Y %T %z"
|
||||
|
||||
# Detect whether we're running renamed, i.e. installed copy, or else presumably from distribution directory.
|
||||
if [ ! "$SELFNAME" = "Launchd.command" ] ; then
|
||||
USE_NVRAMDUMP="${NVRAMDUMP}"
|
||||
INSTALLED=1
|
||||
else
|
||||
cd "${SELFDIR}" || earlyAbort "Failed to enter working directory!"
|
||||
|
||||
if [ ! -x ./nvramdump ] ; then
|
||||
earlyAbort "Compiled nvramdump is not found!"
|
||||
fi
|
||||
|
||||
USE_NVRAMDUMP="./nvramdump"
|
||||
fi
|
||||
|
||||
for arg;
|
||||
do
|
||||
case $arg in
|
||||
install )
|
||||
INSTALL=1
|
||||
;;
|
||||
uninstall )
|
||||
UNINSTALL=1
|
||||
;;
|
||||
daemon )
|
||||
DAEMON=1
|
||||
;;
|
||||
"daemon both" )
|
||||
DAEMON=1
|
||||
BOTH=1
|
||||
;;
|
||||
agent )
|
||||
AGENT=1
|
||||
;;
|
||||
"agent both" )
|
||||
AGENT=1
|
||||
BOTH=1
|
||||
;;
|
||||
both )
|
||||
BOTH=1
|
||||
;;
|
||||
logout )
|
||||
LOGOUT=1
|
||||
;;
|
||||
status )
|
||||
STATUS=1
|
||||
;;
|
||||
# Usage for invalid param.
|
||||
# Note script called as logout hook gets passed name of user logging out as a param.
|
||||
* )
|
||||
if [ ! "$INSTALLED" = "1" ] ; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Get root permissions early if immediate mode.
|
||||
if [ "$INSTALL" = "1" ] ||
|
||||
[ "$UNINSTALL" = "1" ] ||
|
||||
[ "$STATUS" = "1" ] ; then
|
||||
if [ "$(whoami)" = "root" ] ; then
|
||||
earlyAbort "Do not run this script as root!"
|
||||
fi
|
||||
|
||||
sudo echo -n || earlyAbort "Could not obtain sudo!"
|
||||
|
||||
if [ "$INSTALL" = "1" ] ; then
|
||||
UNINSTALL=1
|
||||
fi
|
||||
|
||||
if [ "$UNINSTALL" = "1" ] ||
|
||||
[ "$STATUS" = "1" ] ; then
|
||||
# For agent/daemon pid prefer 'launchctl list' as it works on older macOS where 'launchtl print' does not.
|
||||
DAEMON_PID="$(sudo launchctl list | sed -n "s/\([0-9\-]*\).*${ORG}.daemon/\1/p" | sed 's/-/Failed to start!/')"
|
||||
AGENT_PID="$(launchctl list | sed -n "s/\([0-9\-]*\).*${ORG}.agent/\1/p" | sed 's/-/Failed to start!/')"
|
||||
LOGOUT_HOOK="$(sudo defaults read com.apple.loginwindow LogoutHook 2>/dev/null)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If not told what to do, work it out.
|
||||
# When running as daemon, 'daemon' is specified as a param.
|
||||
if [ ! "$DAEMON" = "1" ] &&
|
||||
[ ! "$AGENT" = "1" ] &&
|
||||
[ ! "$BOTH" = "1" ] &&
|
||||
[ ! "$LOGOUT" = "1" ] ; then
|
||||
if [ "$INSTALL" = "1" ] ||
|
||||
[ "$UNINSTALL" = "1" ] ; then
|
||||
# When not specified, choose to (un)install daemon or logout hook depending on macOS version.
|
||||
if [ "$(getDarwinMajorVersion)" -ge ${LAUNCHD_BOTH} ] ; then
|
||||
echo "Darwin $(uname -r) >= ${LAUNCHD_BOTH}, using both agent and daemon"
|
||||
BOTH=1
|
||||
AGENT=1
|
||||
DAEMON=1
|
||||
elif [ "$(getDarwinMajorVersion)" -ge ${LAUNCHD_DARWIN} ] ; then
|
||||
echo "Darwin $(uname -r) >= ${LAUNCHD_DARWIN}, using daemon"
|
||||
DAEMON=1
|
||||
else
|
||||
echo "Darwin $(uname -r) < ${LAUNCHD_DARWIN}, using logout hook."
|
||||
LOGOUT=1
|
||||
fi
|
||||
else
|
||||
if [ "$INSTALLED" = "1" ] ; then
|
||||
# If installed with no type as a param, we are installed as logout hook.
|
||||
LOGOUT=1
|
||||
elif [ ! "$STATUS" = "1" ] ; then
|
||||
# Usage for no params.
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! "$INSTALLED" = "1" ] &&
|
||||
[ "$BOTH" = "1" ] ; then
|
||||
LOG_PREFIX="Both"
|
||||
AGENT=1
|
||||
DAEMON=1
|
||||
AGENT_PARAMS="${AGENT_PARAMS} both"
|
||||
DAEMON_PARAMS="${DAEMON_PARAMS} both"
|
||||
elif [ "$DAEMON" = "1" ] ; then
|
||||
LOG_PREFIX="Daemon"
|
||||
elif [ "$AGENT" = "1" ] ; then
|
||||
LOG_PREFIX="Agent"
|
||||
elif [ "$LOGOUT" = "1" ] ; then
|
||||
LOG_PREFIX="Logout"
|
||||
fi
|
||||
|
||||
if [ ! "$INSTALLED" = "1" ] ; then
|
||||
LOG_PREFIX="${LOG_PREFIX}-Immediate"
|
||||
fi
|
||||
|
||||
if [ "$INSTALL" = "1" ] &&
|
||||
[ "$AGENT" = "1" ] &&
|
||||
[ ! "$DAEMON" = "1" ] ; then
|
||||
earlyAbort "Standalone agent install is not supported!"
|
||||
fi
|
||||
|
||||
if [ "$UNINSTALL" = "1" ] ; then
|
||||
msg="$(uninstall)"
|
||||
if [ ! "${msg}" = "" ] ; then
|
||||
doLog "${msg}"
|
||||
fi
|
||||
if [ ! "$INSTALL" = "1" ] ; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$INSTALL" = "1" ] ; then
|
||||
installLog
|
||||
|
||||
# Save NVRAM immediately, this will become the fallback NVRAM after the first shutdown.
|
||||
# Do not install if this fails, since this indicates that required boot path from OC is
|
||||
# not available, or other fatal error.
|
||||
if [ "$DAEMON" = "1" ] ||
|
||||
[ "$LOGOUT" = "1" ] ; then
|
||||
doLog "Installing…"
|
||||
doLog "Saving initial nvram.plist…"
|
||||
saveMount 0
|
||||
if [ "$DAEMON" = "1" ] ; then
|
||||
# daemon
|
||||
saveNvram 0 1
|
||||
else
|
||||
# logout hook
|
||||
saveNvram 0 1
|
||||
fi
|
||||
fi
|
||||
|
||||
install
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$STATUS" = "1" ] ; then
|
||||
status
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${LOGOUT}" = "1" ] ; then
|
||||
saveMount 0
|
||||
saveNvram 0 1
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Useful for trapping all signals to see what we get.
|
||||
#for s in {1..31} ;do trap "onComplete $s" $s ;done
|
||||
|
||||
# Trap CTRL+C for testing for immediate mode, and trap normal or forced daemon
|
||||
# or agent termination. Separate trap commands so we can log which was caught.
|
||||
trap "onComplete SIGINT" SIGINT
|
||||
trap "onComplete SIGTERM" SIGTERM
|
||||
|
||||
doLog "Starting…"
|
||||
|
||||
if [ "$DAEMON" = "1" ] ; then
|
||||
saveMount 1
|
||||
fi
|
||||
|
||||
if [ "$DAEMON" = "1" ] &&
|
||||
[ "$BOTH" = "1" ] ; then
|
||||
agent_pid=""
|
||||
doLog "Waiting for agent…"
|
||||
while [ "$agent_pid" = "" ] ;
|
||||
do
|
||||
agent_pid="$(pgrep -f "$(echo "${HELPER} agent" | sed "s/\./\\\./g")")"
|
||||
if [ "$agent_pid" = "" ] ; then
|
||||
# https://apple.stackexchange.com/a/126066/113758
|
||||
# Only works from Yosemite upwards.
|
||||
sleep 5 & wait
|
||||
fi
|
||||
done
|
||||
doLog "Found agent…"
|
||||
saveNvram 1 2 "$agent_pid"
|
||||
fi
|
||||
|
||||
while true
|
||||
do
|
||||
doLog "Running…"
|
||||
|
||||
# https://apple.stackexchange.com/a/126066/113758
|
||||
# Only works from Yosemite upwards.
|
||||
sleep $RANDOM & wait
|
||||
done
|
||||
28
hackintosh-guide/Utilities/LogoutHook/Launchd.command.plist
Executable file
28
hackintosh-guide/Utilities/LogoutHook/Launchd.command.plist
Executable file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>$LABEL</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>$LAUNCHFILE</string>
|
||||
<string>$HELPER</string>
|
||||
<string>$PARAM</string>
|
||||
</array>
|
||||
<key>StandardOutPath</key>
|
||||
<string>$LOGFILE</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>$LOGFILE</string>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>EnableGlobbing</key>
|
||||
<false/>
|
||||
<key>EnableTransactions</key>
|
||||
<false/>
|
||||
<key>LowPriorityIO</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
17
hackintosh-guide/Utilities/LogoutHook/README.md
Executable file
17
hackintosh-guide/Utilities/LogoutHook/README.md
Executable file
@@ -0,0 +1,17 @@
|
||||
# LogoutHook
|
||||
|
||||
## Script
|
||||
|
||||
### Usage
|
||||
```./Launchd.command```
|
||||
|
||||
### Installation
|
||||
```./Launchd.command install```
|
||||
|
||||
### Status
|
||||
```./Launchd.command status```
|
||||
|
||||
Shows non-empty daemon pid only, if installed with default settings.
|
||||
|
||||
### Log
|
||||
```/var/log/org.acidanthera.nvramhook.launchd/launchd.log```
|
||||
BIN
hackintosh-guide/Utilities/LogoutHook/nvramdump
Executable file
BIN
hackintosh-guide/Utilities/LogoutHook/nvramdump
Executable file
Binary file not shown.
107
hackintosh-guide/Utilities/ShimUtils/README.md
Executable file
107
hackintosh-guide/Utilities/ShimUtils/README.md
Executable file
@@ -0,0 +1,107 @@
|
||||
## OpenCore + OpenLinuxBoot + Secure Boot
|
||||
|
||||
If you want to use OpenCore + OpenLinuxBoot + Secure Boot it is possible to sign everything
|
||||
manually yourself, including any new Linux kernels after updates. This is possible since most
|
||||
standard distros leave at least the previous kernel bootable (and OpenLinuxBoot exposes
|
||||
this, via the Auxiliary menu), so you can boot into the old kernel, then sign the new
|
||||
kernel yourself.
|
||||
|
||||
More convenient may be to trust the signing keys of the specific distros which you
|
||||
want to boot, which are bundled into the `shimx64.efi` file installed with each distro.
|
||||
You can extract these with `shim-to-cert.tool` distributed with OpenCore, then install
|
||||
them in your system Secure Boot `db` variable. Best practice would be to install the deny
|
||||
list (`vendor.dbx`) from `shimx64.efi`, if any, into your system `dbx` variable, as well.
|
||||
(Otherwise you are ignoring any revocations which the vendor has made.)
|
||||
|
||||
Recently, Shim has added SBAT support, as a more efficient way to revoke unsafe
|
||||
binaries. Unfortunately, the SBAT enforcement code is part of Shim, and is not
|
||||
something you can extract and add to your system Secure Boot database.
|
||||
|
||||
To work round this, the new recommended way to boot OpenCore + OpenLinuxBoot +
|
||||
Secure Boot is to make a user build of Shim. The vendor certificates
|
||||
and revocation lists extracted from the distro `shimx64.efi` files are combined
|
||||
and signed by you, into your own build of Shim; in this approach, these vendor
|
||||
certificates should NOT also be included in the system Secure Boot database,
|
||||
and should be removed if you added them previously. Including them in both places
|
||||
will still boot under Secure Boot, but will effectively disable SBAT revocation.
|
||||
|
||||
> If you are signing everything yourself, including Linux kernels after updates, that
|
||||
will still work as before and the below is not needed. Equally, if you are not
|
||||
using Secure Boot the below is not needed.
|
||||
|
||||
The advantages of using a user build of Shim are:
|
||||
- No need to sign every kernel after updates (same as previous method)
|
||||
- Linux SBAT integration (new)
|
||||
- Linux MOK integration (new)
|
||||
- No need to include the Windows intermediate CA - you are trusting whichever distro
|
||||
keys you choose to include in your own Shim, directly (new)
|
||||
|
||||
Disadvantages are:
|
||||
- Need to update when distro keys or distro revocation lists within Shim are updated
|
||||
(same as previous method)
|
||||
- Need to udpate when Shim SBAT level is updated (new)
|
||||
|
||||
### Method
|
||||
`Utilities/ShimUtils` includes a script `shim-make.tool` which will download the
|
||||
current Shim source and build it for you, on macOS (using Ubuntu multipass) or on
|
||||
Linux (Ubuntu and Fedora supported, others may work).
|
||||
|
||||
- Extract `vendor.db` and `vendor.dbx` files from the `shimx64.efi` file of each distro
|
||||
which you want to load (using `shim-to-cert.tool`)
|
||||
- For non-GRUB distros, the required public keys for this process cannot be extracted
|
||||
from `shimx64.efi` and so must be found by additional user research
|
||||
- Concatentate these (e.g. `cat fedora/vendor.db ubuntu/vendor.db > combined/vendor.db`
|
||||
and `cat fedora/vendor.dbx ubuntu/vendor.dbx > combined/vendor.dbx`)
|
||||
- Do not concatenate `.der` files directly, it will not work
|
||||
- If you have a single distro with a single `.der` file, you can use `VENDOR_CERT_FILE`
|
||||
instead of `VENDOR_DB_FILE` in the `make` options below; otherwise, you will need to use
|
||||
`cert-to-efi-sig-list` from `efitools` to convert the `.der` file to a sig list - this
|
||||
is done automatically by `shim-to-cert.tool` when `efitools` are available (in
|
||||
Linux; or from within Ubuntu multipass on macOS, e.g. `multipass shell oc-shim`)
|
||||
- Build a version of Shim which includes these concatenated signature lists (and
|
||||
launches OpenCore.efi directly):
|
||||
- `./shim-make.tool setup`
|
||||
- `./shim-make.tool clean` (only needed if remaking after the initial make)
|
||||
- `./shim-make.tool make VENDOR_DB_FILE={full-path-to}/vendor.db VENDOR_DBX_FILE={full-path-to}/vendor.dbx`
|
||||
- On macOS, the paths to these files must either be within the multipass VM, or
|
||||
within a subdirectory visible to macOS and the VM on the same path, such as
|
||||
`/Users/{username}/shim_root` when using `shim-make.tool` default settings
|
||||
- Copy the relevant files (`shimx64.efi` and `mmx64.efi` as well as `BOOTX64.CSV`) to your mounted ESP volume, e.g.:
|
||||
- `./shim-make.tool install /Volumes/EFI` (macOS)
|
||||
- `sudo ./shim-make.tool install /boot/efi` (Linux)
|
||||
- Sign the newly built `shimx64.efi` and `mmx64.efi` with your own ISK (see e.g.
|
||||
https://habr.com/en/articles/273497/ - Google translate is your friend)
|
||||
- If you do not copy and sign `mmx64.efi` as well as `shimx64.efi`, your system will hang if any MOK operations are attempted
|
||||
- `BOOTX64.CSV` is not required and is for information only
|
||||
|
||||
As before you need to sign `OpenCore.efi` and any drivers it loads with your ISK.
|
||||
You now also need to add an empty SBAT section to `OpenCore.efi` before signing it.
|
||||
|
||||
> An empty SBAT section means: 'I'm not part of the system which allocates SBAT names
|
||||
and signs them into boot files, and I don't want this boot file to be revoked by any
|
||||
future SBAT revocations'. Of course, you can still revoke boot files you signed yourself
|
||||
by rotating your own signing keys.
|
||||
|
||||
As noted [here](https://github.com/fwupd/fwupd/issues/2910) and
|
||||
[here](https://github.com/rhboot/shim/issues/376),
|
||||
the [documented](https://github.com/rhboot/shim/blob/main/SBAT.md) method for adding an
|
||||
SBAT section to an already-linked `.efi` file does not work correctly (GNU `objcopy`
|
||||
corrupts the executable). This
|
||||
[third party python script](https://github.com/rhboot/shim/issues/376#issuecomment-1628004034)
|
||||
does work. A suitable command is:
|
||||
|
||||
`pe-add-sections.py -s .sbat <(echo -n) -z .sbat -i OpenCore.efi -o OpenCore_empty_sbat.efi`
|
||||
|
||||
This file then needs to be signed and copied back into place, e.g.:
|
||||
|
||||
`sbsign --key {path-to}/ISK.key --cert {path-to}/ISK.pem OpenCore_empty_sbat.efi --output OpenCore.efi`
|
||||
|
||||
Finally, in order for OpenCore integration with Shim to work correctly
|
||||
`UEFI/Quirks/ShimRetainProtocol` must be enabled in `config.plist`, and
|
||||
`LauncherPath` should be set to `\EFI\OC\shimx64.efi`.
|
||||
|
||||
> Using Ubuntu multipass, it is now possible to operate entirely within macOS for signing,
|
||||
key generation, etc. Note that the `~/shim_root` directory is already shared between
|
||||
macOS and the `oc-shim` multipass VM (under its macOS path, e.g. `/Users/username/shim_root`),
|
||||
and other macOS folders and volumes can be mounted if you wish, e.g.
|
||||
`multipass mount /Volumes/EFI oc-shim:/Volumes/EFI`.
|
||||
132
hackintosh-guide/Utilities/ShimUtils/sbat-info.tool
Executable file
132
hackintosh-guide/Utilities/ShimUtils/sbat-info.tool
Executable file
@@ -0,0 +1,132 @@
|
||||
#!/bin/bash
|
||||
|
||||
# sbat-info.tool - Dump SBAT information from .efi executable, with additional
|
||||
# version and SBAT enforcement level information if the executable is Shim.
|
||||
#
|
||||
# Copyright (c) 2023, Michael Beaton. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
LIGHT_GREEN='\033[1;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Note: binutils can simply be placed last on the path in macOS, to provide objcopy but avoid hiding native objdump (this script parses LLVM or GNU objdump output).
|
||||
# Alternatively BINUTILS_PREFIX environment variable can be set to enabled prefixed co-existing GNU tools.
|
||||
command -v "${BINUTILS_PREFIX}"objcopy >/dev/null 2>&1 || { echo >&2 "objcopy not found - please install binutils package or set BINUTILS_PREFIX environment variable."; exit 1; }
|
||||
|
||||
usage () {
|
||||
echo "Usage: $(basename "$0") <efifile>"
|
||||
echo " Display SBAT information, if present, for any .efi executable."
|
||||
echo " If the file is a version of Shim, also dislay Shim version info and SBAT enforcement level."
|
||||
}
|
||||
|
||||
has_section () {
|
||||
"${BINUTILS_PREFIX}"objdump -h "$1" | sed -nr 's/^ *[0-9]+ ([^ ]+).*/\1/p' | grep "$2" 1>/dev/null
|
||||
}
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# https://unix.stackexchange.com/a/84980/340732
|
||||
op_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'shim_info') || exit 1
|
||||
|
||||
# objcopy and objdump do not like writing to stdout when part of a pipe, so just write to temp files
|
||||
# Note: these get created as empty files even if the section does not exist
|
||||
"${BINUTILS_PREFIX}"objcopy -O binary -j ".sbat" "$1" "${op_dir}/sbat" || { rm -rf "${op_dir}"; exit 1; }
|
||||
"${BINUTILS_PREFIX}"objcopy -O binary -j ".sbatlevel" "$1" "${op_dir}/sbatlevel" || { rm -rf "${op_dir}"; exit 1; }
|
||||
"${BINUTILS_PREFIX}"objcopy -O binary -j ".data.ident" "$1" "${op_dir}/data.ident" || { rm -rf "${op_dir}"; exit 1; }
|
||||
|
||||
if ! has_section "$1" ".data.ident" ; then
|
||||
IS_SHIM=0
|
||||
echo -e "${YELLOW}Shim version info not present (probably not Shim)${NC}"
|
||||
else
|
||||
# Treat this section's presence as enough for the warning colours below
|
||||
IS_SHIM=1
|
||||
FILE_HEADER="$(head -n 1 "${op_dir}/data.ident")"
|
||||
SHIM_HEADER="UEFI SHIM"
|
||||
if [ ! "$FILE_HEADER" = "$SHIM_HEADER" ] ; then
|
||||
echo -e "${RED}!!!Version header found '${FILE_HEADER}' expected '${SHIM_HEADER}'${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}Found ${SHIM_HEADER} version header${NC}"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
VERSION="$(grep -a 'Version' "${op_dir}/data.ident" | sed -n 's/^\$Version: *\([^[:space:]]*\) *\$$/\1/p')"
|
||||
if [ "$VERSION" = "" ] ; then
|
||||
echo -e "${RED}Expected version number not present${NC}"
|
||||
else
|
||||
echo -e "Version: ${LIGHT_GREEN}${VERSION}${NC} (${VERSION}+, if user build)"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
COMMIT="$(grep -a 'Commit' "${op_dir}/data.ident" | sed -n 's/^\$Commit: *\([^[:space:]]*\) *\$$/\1/p')"
|
||||
if [ "$COMMIT" = "" ] ; then
|
||||
echo -e "${RED}Expected commit id not present${NC}"
|
||||
else
|
||||
echo "Commit: ${COMMIT}"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "${YELLOW}SBAT (when tested by other files or by self):${NC}"
|
||||
|
||||
if ! has_section "$1" ".sbat" ; then
|
||||
echo
|
||||
echo -e "${RED}No .sbat section${NC}"
|
||||
else
|
||||
echo
|
||||
echo -n -e "$LIGHT_GREEN"
|
||||
cat "${op_dir}/sbat"
|
||||
echo -n -e "$NC"
|
||||
if ! cat "${op_dir}/sbat" | grep -E "[a-z]|[A-Z]" 1>/dev/null ; then
|
||||
# Red for shim; yellow for other file
|
||||
if [ "$IS_SHIM" -eq 1 ] ; then
|
||||
echo -n -e "$RED"
|
||||
else
|
||||
echo -n -e "$LIGHT_GREEN"
|
||||
fi
|
||||
echo -e "Present but empty .sbat section"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "${YELLOW}SBAT Level (when testing other files):${NC}"
|
||||
|
||||
if ! has_section "$1" ".sbatlevel" ; then
|
||||
echo
|
||||
# Yellow for shim (not present in earlier shims with .sbat, ~15.6); green for other file
|
||||
if [ "$IS_SHIM" -eq 1 ] ; then
|
||||
echo -n -e "$YELLOW"
|
||||
else
|
||||
echo -n -e "$LIGHT_GREEN"
|
||||
fi
|
||||
echo -n "No .sbatlevel section"
|
||||
echo -e "$NC"
|
||||
else
|
||||
sbat_level_version=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=0 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
|
||||
sbat_level_previous=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=4 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
|
||||
sbat_level_latest=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=8 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
|
||||
|
||||
if [ "$sbat_level_version" -ne 0 ] ; then
|
||||
echo -e "${RED}!!! Unexpected .sbatlevel version $sbat_level_version != 0 !!!${NC}"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "${YELLOW}Previous (more permissive boot, default):${NC}"
|
||||
echo -n -e "$LIGHT_GREEN"
|
||||
dd if="${op_dir}/sbatlevel" ibs=1 skip="$(($sbat_level_previous + 4))" count="$(($sbat_level_latest - $sbat_level_previous))" 2>/dev/null
|
||||
echo -e "$NC"
|
||||
|
||||
echo -e "${YELLOW}Latest (stricter boot, optional):${NC}"
|
||||
echo -n -e "$LIGHT_GREEN"
|
||||
dd if="${op_dir}/sbatlevel" ibs=1 skip="$(($sbat_level_latest + 4))" 2>/dev/null
|
||||
echo -e "$NC"
|
||||
fi
|
||||
|
||||
rm -rf "${op_dir}"
|
||||
|
||||
exit 0
|
||||
339
hackintosh-guide/Utilities/ShimUtils/shim-make.tool
Executable file
339
hackintosh-guide/Utilities/ShimUtils/shim-make.tool
Executable file
@@ -0,0 +1,339 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2023 Mike Beaton. All rights reserved.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
# Makes user build of shim which launches OpenCore.efi and includes specified vendor certificate lists.
|
||||
# Builds on macOS using Ubuntu multipass or on Linux directly (Ubuntu & Fedora tested).
|
||||
#
|
||||
# To debug shim (e.g. within OVMF) make with
|
||||
# ./shim-make.tool make OPTIMIZATIONS="-O0"
|
||||
# and set 605dab50-e046-4300-abb6-3dd810dd8b23:SHIM_DEBUG to (UINT8)1 (<data>AQ==</data>) in NVRAM
|
||||
#
|
||||
|
||||
ROOT=~/shim_root
|
||||
SHIM=~/shim_source
|
||||
OC_SHIM=oc-shim
|
||||
|
||||
unamer() {
|
||||
NAME="$(uname)"
|
||||
|
||||
if [ "$(echo "${NAME}" | grep MINGW)" != "" ] || [ "$(echo "${NAME}" | grep MSYS)" != "" ]; then
|
||||
echo "Windows"
|
||||
else
|
||||
echo "${NAME}"
|
||||
fi
|
||||
}
|
||||
|
||||
shim_command () {
|
||||
DIR=$1
|
||||
shift
|
||||
if [ $DARWIN -eq 1 ] ; then
|
||||
if [ $ECHO -eq 1 ] ; then
|
||||
# shellcheck disable=SC2068
|
||||
echo multipass exec ${OC_SHIM} --working-directory "'${DIR}'" -- $@ 1>/dev/stderr
|
||||
fi
|
||||
# shellcheck disable=SC2068,SC2294
|
||||
eval multipass exec ${OC_SHIM} --working-directory "'${DIR}'" -- $@
|
||||
else
|
||||
if [ $ECHO -eq 1 ] ; then
|
||||
# shellcheck disable=SC2068
|
||||
echo "[${DIR}]" $@ 1>/dev/stderr
|
||||
fi
|
||||
pushd "${DIR}" 1>/dev/null || exit 1
|
||||
# shellcheck disable=SC2068,SC2294
|
||||
eval $@
|
||||
retval=$?
|
||||
popd 1>/dev/null || exit 1
|
||||
return $retval
|
||||
fi
|
||||
}
|
||||
|
||||
shim_command_v () {
|
||||
FOUND="$(shim_command . command -v "'${1}'" | wc -l)"
|
||||
return $((1 - $FOUND))
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage:"
|
||||
echo -n " ./${SELFNAME} [args] [setup|clean|make [options]|install [esp-root-path]"
|
||||
if [ $DARWIN -eq 1 ] ; then
|
||||
echo -n "|mount [multipass-path]]"
|
||||
fi
|
||||
echo "]"
|
||||
echo
|
||||
echo "Args:"
|
||||
echo -n " -r : Specify shim output root, default '${ROOT}'"
|
||||
if [ $DARWIN -eq 1 ] ; then
|
||||
echo -n " (shared)"
|
||||
fi
|
||||
echo
|
||||
echo -n " -s : Specify shim source location, default '${SHIM}'"
|
||||
if [ $DARWIN -eq 1 ] ; then
|
||||
echo -n " (VM only, unless 'shim-make mount' used for development/debugging)"
|
||||
fi
|
||||
echo
|
||||
echo "If used, -r/-s:"
|
||||
echo " - Should be specified on every call, they are not remembered from 'setup'"
|
||||
echo " - Should come before the chosen command"
|
||||
echo
|
||||
echo "Examples:"
|
||||
echo " ./${SELFNAME} setup (sets up directories and installs rhboot/shim source)"
|
||||
echo " ./${SELFNAME} clean (cleans after previous make)"
|
||||
LOCATION="."
|
||||
if [ $DARWIN -eq 1 ] ; then
|
||||
LOCATION="${ROOT}"
|
||||
fi
|
||||
echo -n " ./${SELFNAME} make VENDOR_DB_FILE='${LOCATION}/combined/vendor.db' VENDOR_DBX_FILE='${LOCATION}/combined/vendor.dbx' (makes shim with db and dbx contents"
|
||||
if [ $DARWIN -eq 1 ] ; then
|
||||
echo -n "; note VENDOR_DB_FILE and VENDOR_DBX_FILE are inside a directory shared with VM"
|
||||
fi
|
||||
echo ")"
|
||||
echo " ./${SELFNAME} install '${EXAMPLE_ESP}' (installs made files to ESP mounted at '${EXAMPLE_ESP}')"
|
||||
echo
|
||||
echo "After installation shimx64.efi and mmx64.efi must be signed by user ISK; OpenCore.efi must have an .sbat section added and be signed by user ISK."
|
||||
}
|
||||
|
||||
mount_path () {
|
||||
SRC=$1
|
||||
if [ "${SRC}" = "" ] ; then
|
||||
echo "Incorrect call to mount_path!"
|
||||
exit 1
|
||||
fi
|
||||
DEST=$2
|
||||
if [ "${DEST}" = "" ] ; then
|
||||
DEST=${SRC}
|
||||
fi
|
||||
|
||||
if [ ! -d "${SRC}" ] ; then
|
||||
echo "Adding ${SRC}..."
|
||||
mkdir -p "${SRC}" || exit 1
|
||||
else
|
||||
echo "${SRC} already present..."
|
||||
fi
|
||||
|
||||
if [ $DARWIN -eq 1 ] ; then
|
||||
if ! multipass info ${OC_SHIM} | grep "${DEST}" 1>/dev/null ; then
|
||||
echo "Mounting ${SRC}..."
|
||||
multipass mount "${SRC}" "${OC_SHIM}:${DEST}" || exit 1
|
||||
else
|
||||
echo "${SRC} already mounted..."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
get_ready () {
|
||||
if [ $DARWIN -eq 1 ] ; then
|
||||
if ! command -v multipass 1>/dev/null ; then
|
||||
echo "Installing Ubuntu multipass..."
|
||||
brew install --cask multipass || exit 1
|
||||
else
|
||||
echo "Ubuntu multipass already installed..."
|
||||
fi
|
||||
|
||||
if ! multipass info ${OC_SHIM} &>/dev/null ; then
|
||||
echo "Launching ${OC_SHIM} multipass instance..."
|
||||
multipass launch -n ${OC_SHIM} || exit 1
|
||||
else
|
||||
echo "${OC_SHIM} multipass instance already launched..."
|
||||
fi
|
||||
fi
|
||||
|
||||
mount_path "${ROOT}"
|
||||
|
||||
if ! shim_command_v gcc || ! shim_command_v make || ! shim_command_v git || ! shim_command_v sbsign || ! shim_command_v sbsiglist ; then
|
||||
echo "Installing dependencies..."
|
||||
if shim_command_v dnf ; then
|
||||
shim_command . sudo dnf install -y gcc make git elfutils-libelf-devel sbsigntools efitools || exit 1
|
||||
else
|
||||
shim_command . sudo apt-get update || exit 1
|
||||
shim_command . sudo apt install -y gcc make git libelf-dev sbsigntool efitools || exit 1
|
||||
fi
|
||||
else
|
||||
echo "Dependencies already installed..."
|
||||
fi
|
||||
|
||||
#
|
||||
# For debug/develop purposes on Darwin it would be nicer to keep the source code in
|
||||
# macOS, just mounted and built in multipass, but the build is about 1/3 the speed of
|
||||
# building in a native multipass directory.
|
||||
# For the purposes of having a fast build but code which can be opened e.g.
|
||||
# within an IDE within macOS, sshfs can be used to mount out from multipass
|
||||
# to macOS using ./shim-make mount:
|
||||
# - https://github.com/canonical/multipass/issues/1070
|
||||
# - https://osxfuse.github.io/
|
||||
#
|
||||
if ! shim_command . test -d "'${SHIM}'" ; then
|
||||
# TODO: Once there is a stable version of Shim containing the commits we need
|
||||
# - 1f38cb30a5e1dcea97b8d48915515b866ec13c32
|
||||
# - a8b0b600ddcf02605da8582b4eac1932a3bb13fa
|
||||
# (i.e. 15.8. hopefully) we should probably check out at a fixed, stable tag, starting
|
||||
# at 15.8 and then manually updated in this script when a new stable version is released,
|
||||
# but which can be overridden with any commit or branch with a script option.
|
||||
echo "Cloning rhboot/shim..."
|
||||
shim_command . git clone https://github.com/rhboot/shim.git "'${SHIM}'" || exit 1
|
||||
shim_command "${SHIM}" git submodule update --init || exit 1
|
||||
else
|
||||
if ! shim_command "${SHIM}" git remote -v | grep "rhboot/shim" 1>/dev/null ; then
|
||||
echo "FATAL: VM subdirectory ${SHIM} is already present, but does not contain rhboot/shim!"
|
||||
exit 1
|
||||
fi
|
||||
echo "rhboot/shim already cloned..."
|
||||
fi
|
||||
|
||||
echo "Make.defaults:"
|
||||
|
||||
# These two modifications to Make.defaults only required for debugging
|
||||
FOUND=$(shim_command "${SHIM}" grep "gdwarf" Make.defaults | wc -l)
|
||||
if [ "$FOUND" -eq 0 ] ; then
|
||||
echo " Updating gdwarf flags..."
|
||||
shim_command "${SHIM}" sed -i "'s^-ggdb \\\\^-ggdb -gdwarf-4 -gstrict-dwarf \\\\^g'" Make.defaults
|
||||
else
|
||||
echo " gdwarf flags already updated..."
|
||||
fi
|
||||
|
||||
FOUND=$(shim_command "${SHIM}" grep "'${ROOT}'" Make.defaults | wc -l)
|
||||
if [ "$FOUND" -eq 0 ] ; then
|
||||
echo " Updating debug directory..."
|
||||
shim_command "${SHIM}" sed -i "\"s^-DDEBUGDIR='L\\\"/usr/lib/debug/usr/share/shim/\\\$(ARCH_SUFFIX)-\\\$(VERSION)\\\$(DASHRELEASE)/\\\"'^-DDEBUGDIR='L\\\"${ROOT}/usr/lib/debug/boot/efi/EFI/OC/\\\"'^g\"" Make.defaults
|
||||
else
|
||||
echo " Debug directory already updated..."
|
||||
fi
|
||||
|
||||
# Work-around for https://github.com/rhboot/shim/issues/596
|
||||
FOUND=$(shim_command "${SHIM}" grep "'export DEFINES'" Make.defaults | wc -l)
|
||||
if [ "$FOUND" -eq 0 ] ; then
|
||||
echo " Updating exports..."
|
||||
|
||||
# var assignment to make output piping work normally
|
||||
# shellcheck disable=SC2034
|
||||
temp=$(echo "export DEFINES" | shim_command "${SHIM}" tee -a Make.defaults) 1>/dev/null
|
||||
else
|
||||
echo " Exports already updated..."
|
||||
fi
|
||||
}
|
||||
|
||||
SELFNAME="$(/usr/bin/basename "${0}")"
|
||||
|
||||
ECHO=0
|
||||
|
||||
if [ "$(unamer)" = "Darwin" ] ; then
|
||||
DARWIN=1
|
||||
EXAMPLE_ESP='/Volumes/EFI'
|
||||
else
|
||||
DARWIN=0
|
||||
EXAMPLE_ESP='/boot/efi'
|
||||
fi
|
||||
|
||||
OPTS=0
|
||||
while [ "${1:0:1}" = "-" ] ; do
|
||||
OPTS=1
|
||||
if [ "$1" = "-r" ] ; then
|
||||
shift
|
||||
if [ "$1" != "" ] && ! [ "${1:0:1}" = "-" ] ; then
|
||||
ROOT=$1
|
||||
shift
|
||||
else
|
||||
echo "No root directory specified" && exit 1
|
||||
fi
|
||||
elif [ "$1" = "-s" ] ; then
|
||||
shift
|
||||
if [ "$1" != "" ] && ! [ "${1:0:1}" = "-" ] ; then
|
||||
SHIM=$1
|
||||
shift
|
||||
else
|
||||
echo "No shim directory specified" && exit 1
|
||||
fi
|
||||
elif [ "$1" = "--echo" ] ; then
|
||||
ECHO=1
|
||||
shift
|
||||
else
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$1" = "setup" ] ; then
|
||||
get_ready
|
||||
echo "Installation complete."
|
||||
exit 0
|
||||
elif [ "$1" = "clean" ] ; then
|
||||
echo "Cleaning..."
|
||||
shim_command "${SHIM}" make clean
|
||||
exit 0
|
||||
elif [ "$1" = "make" ] ; then
|
||||
echo "Making..."
|
||||
shift
|
||||
shim_command "${SHIM}" make DEFAULT_LOADER="\\\\\\\\OpenCore.efi" OVERRIDE_SECURITY_POLICY=1 "$@"
|
||||
exit 0
|
||||
elif [ "$1" = "install" ] ; then
|
||||
echo "Installing..."
|
||||
rm -rf "${ROOT:?}"/usr
|
||||
shim_command "${SHIM}" DESTDIR="'${ROOT}'" EFIDIR='OC' OSLABEL='OpenCore' make install
|
||||
if [ ! "$2" = "" ] ; then
|
||||
echo "Installing to ESP ${2}..."
|
||||
cp "${ROOT}"/boot/efi/EFI/OC/* "${2}"/EFI/OC || exit 1
|
||||
fi
|
||||
exit 0
|
||||
elif [ $DARWIN -eq 1 ] && [ "$1" = "mount" ] ; then
|
||||
MOUNT="$2"
|
||||
if [ "${MOUNT}" = "" ] ; then
|
||||
MOUNT=$SHIM
|
||||
fi
|
||||
|
||||
#
|
||||
# Useful for devel/debug only.
|
||||
# Note: We are only mounting in the reverse direction because we get much faster build speeds.
|
||||
#
|
||||
if ! command -v sshfs 1>/dev/null ; then
|
||||
echo "sshfs (https://osxfuse.github.io/) is required for mounting directories from multipass into macOS (https://github.com/canonical/multipass/issues/1070)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${MOUNT}" ] ; then
|
||||
echo "Making local directory ${MOUNT}..."
|
||||
mkdir -p "${MOUNT}" || exit 1
|
||||
fi
|
||||
|
||||
ls "${MOUNT}" 1>/dev/null
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "Directory may be mounted but not ready (no authorized key?)"
|
||||
echo "Try: umount ${MOUNT}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if mount | grep ":${MOUNT}" ; then
|
||||
echo "Already mounted at ${MOUNT}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2012
|
||||
if [ "$(ls -A -1 "${MOUNT}" | wc -l)" -ne 0 ] ; then
|
||||
echo "Directory ${MOUNT} is not empty!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IP=$(multipass info ${OC_SHIM} | grep IPv4 | cut -d ":" -f 2 | sed 's/ //g')
|
||||
if [ "${IP}" = "" ] ; then
|
||||
echo "Cannot obtain IPv4 for ${OC_SHIM}"
|
||||
exit 1
|
||||
fi
|
||||
if sshfs "ubuntu@${IP}:$(realpath "${MOUNT}")" "${MOUNT}" ; then
|
||||
echo "Mounted at ${MOUNT}"
|
||||
exit 0
|
||||
else
|
||||
umount "${MOUNT}"
|
||||
echo "Directory cannot be mounted, append your ssh public key to .ssh/authorized_keys in the VM and try again."
|
||||
exit 1
|
||||
fi
|
||||
elif [ "$1" = "" ] ; then
|
||||
if [ $OPTS -eq 0 ] ; then
|
||||
usage
|
||||
else
|
||||
echo "No command specified."
|
||||
fi
|
||||
exit 1
|
||||
else
|
||||
echo "Unkown command: $1"
|
||||
exit 1
|
||||
fi
|
||||
93
hackintosh-guide/Utilities/ShimUtils/shim-to-cert.tool
Executable file
93
hackintosh-guide/Utilities/ShimUtils/shim-to-cert.tool
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
|
||||
# shim-to-cert.tool - Extract OEM signing certificate public key (and full db, dbx if present) from GRUB shim file.
|
||||
#
|
||||
# Copyright (c) 2021-2023, Michael Beaton. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
LIGHT_GREEN='\033[1;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Note: binutils can be placed last on path in macOS, to provide objcopy but avoid overwriting native tools.
|
||||
command -v "${BINUTILS_PREFIX}"objcopy >/dev/null 2>&1 || { echo >&2 "objcopy not found - please install binutils package or set BINUTILS_PREFIX environment variable."; exit 1; }
|
||||
command -v openssl >/dev/null 2>&1 || { echo >&2 "openssl not found - please install openssl package."; exit 1; }
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $(basename "$0") <shimfile>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sectfile=$(mktemp) || exit 1
|
||||
|
||||
# make certain we have output file name, as objcopy will trash input file without it
|
||||
if [ "x$sectfile" = "x" ]; then
|
||||
echo >&2 "Error creating tempfile!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# extract .vendor_cert section
|
||||
"${BINUTILS_PREFIX}"objcopy -O binary -j .vendor_cert "$1" "$sectfile" || exit 1
|
||||
|
||||
if [ ! -s "$sectfile" ] ; then
|
||||
echo >&2 "No .vendor_cert section in $1."
|
||||
rm "$sectfile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# xargs trims white space
|
||||
vendor_authorized_size=$(dd if="$sectfile" ibs=1 skip=0 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
|
||||
vendor_deauthorized_size=$(dd if="$sectfile" ibs=1 skip=4 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
|
||||
vendor_authorized_offset=$(dd if="$sectfile" ibs=1 skip=8 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
|
||||
vendor_deauthorized_offset=$(dd if="$sectfile" ibs=1 skip=12 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
|
||||
|
||||
# extract cert or db
|
||||
certfile=$(mktemp) || { rm "$sectfile"; exit 1; }
|
||||
|
||||
# extract db
|
||||
if [ "$vendor_authorized_size" -ne "0" ]; then
|
||||
dd if="$sectfile" ibs=1 skip="$vendor_authorized_offset" count="$vendor_authorized_size" 2>/dev/null > "$certfile" || { rm "$sectfile"; rm "$certfile"; exit 1; }
|
||||
fi
|
||||
|
||||
# extract dbx
|
||||
if [ "$vendor_deauthorized_size" -ne "0" ]; then
|
||||
dd if="$sectfile" ibs=1 skip="$vendor_deauthorized_offset" count="$vendor_deauthorized_size" 2>/dev/null > "vendor.dbx" || { rm "$sectfile"; rm "$certfile"; exit 1; }
|
||||
echo -e " - Secure Boot revocation list saved as ${LIGHT_GREEN}vendor.dbx${NC}"
|
||||
else
|
||||
echo " - No secure Boot revocation list"
|
||||
fi
|
||||
|
||||
rm "$sectfile"
|
||||
|
||||
if [ "$vendor_authorized_size" -eq "0" ]; then
|
||||
echo "!!! Empty vendor_authorized section (no secure boot signing certificates present)."
|
||||
rm "$certfile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# valid as single cert?
|
||||
openssl x509 -noout -inform der -in "$certfile" 2>/dev/null
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
cp "$certfile" vendor.db
|
||||
echo -e " - Secure Boot signing list saved as ${LIGHT_GREEN}vendor.db${NC}"
|
||||
else
|
||||
# outfile name from cert CN
|
||||
certname=$(openssl x509 -noout -subject -inform der -in "$certfile" | sed 's/^subject=.*CN *=[ \"]*//' | sed 's/[,\/].*//' | sed 's/ *//g') || { rm "$certfile"; exit 1; }
|
||||
outfile="${certname}.der"
|
||||
|
||||
cp "$certfile" "$outfile" || { rm "$certfile"; exit 1; }
|
||||
|
||||
echo -e " - Secure Boot certificate saved as ${LIGHT_GREEN}${outfile}${NC}"
|
||||
|
||||
if ! command -v cert-to-efi-sig-list >/dev/null ; then
|
||||
echo -e " o To convert certificate to vendor.db use:"
|
||||
echo -e " ${YELLOW}" 'cert-to-efi-sig-list <(openssl x509 -in' "'${outfile}'" '-outform PEM) vendor.db' "${NC}"
|
||||
else
|
||||
cert-to-efi-sig-list <(openssl x509 -in "${outfile}" -outform PEM) vendor.db || { rm "$certfile"; exit 1; }
|
||||
echo -e " o Certificate has also been saved as single cert signing list ${LIGHT_GREEN}vendor.db${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm "$certfile"
|
||||
92
hackintosh-guide/Utilities/ShimUtils/unsign-efi-sig-list.tool
Executable file
92
hackintosh-guide/Utilities/ShimUtils/unsign-efi-sig-list.tool
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# unsign-efi-sig-list.tool
|
||||
# Removes signing key from signed certificate list,
|
||||
# also saves signing section and prints CNs found in,
|
||||
# thereby helping to indicate who signed it originally.
|
||||
#
|
||||
# Copyright (c) 2023, Michael Beaton. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
if [ $# -lt 1 ] || [ $# -gt 3 ] ; then
|
||||
echo "Usage:"
|
||||
echo " $(basename "$0") <signed-sig-list> [<outfile> [<sigfile>]]"
|
||||
echo ""
|
||||
echo " - Uses infile name with .unsigned appended if outfile name not specified"
|
||||
echo " - Uses infile name with .pkcs7 appended if sigfile name not specified"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -f "$1" ] ; then
|
||||
echo "File not found: $1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
infile="$1"
|
||||
shift
|
||||
|
||||
if [ "$1" != "" ] ; then
|
||||
outfile="$1"
|
||||
shift
|
||||
else
|
||||
outfile="${infile}.unsigned"
|
||||
fi
|
||||
|
||||
EfiTimeSize=16
|
||||
WinCertHdrSize=8
|
||||
GuidSize=16
|
||||
ExpectedRevision=$((0x0200))
|
||||
ExpectedCertificateType=$((0x0EF1))
|
||||
|
||||
WinCertificateSize=$(dd if="$infile" ibs=1 skip="$EfiTimeSize" count=4 2>/dev/null | od -t u4 -An | xargs)
|
||||
Revision=$(dd if="$infile" ibs=1 skip="$(($EfiTimeSize + 4))" count=2 2>/dev/null | od -t u4 -An | xargs)
|
||||
CertificateType=$(dd if="$infile" ibs=1 skip="$(($EfiTimeSize + 6))" count=2 2>/dev/null | od -t u4 -An | xargs)
|
||||
|
||||
if [ "$CertificateType" -ne "$ExpectedCertificateType" ] ; then
|
||||
printf "Not a signed certificate file (signature 0x%04X found, 0x%04X expected) in: %s\n" "$CertificateType" "$ExpectedCertificateType" "$infile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$Revision" -ne "$ExpectedRevision" ] ; then
|
||||
printf "Unexpected revision (0x%04X found, 0x%04X expected) in signed certificate file: %s\n" "$Revision" "$ExpectedRevision" "$infile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dd if="$infile" of="$outfile" ibs=1 skip=$(($EfiTimeSize + $WinCertificateSize)) 2>/dev/null
|
||||
|
||||
echo "Unsigned certificate list saved to '$outfile'"
|
||||
|
||||
# Total size is header+guid+sig
|
||||
if [ "$WinCertificateSize" -le "$(($WinCertHdrSize + $GuidSize))" ] ; then
|
||||
echo "!!! Certificate size smaller than expected header size"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# In little-endian byte order
|
||||
EFI_CERT_TYPE_PKCS7_GUID="9dd2af4adf68ee498aa9347d375665a7"
|
||||
|
||||
# Expect pkcs7 sig (diff piping requires bash not sh)
|
||||
if ! diff <(dd if="$infile" ibs=1 skip=$(($EfiTimeSize + $WinCertHdrSize)) count=$(($GuidSize)) 2>/dev/null | xxd -p) <(echo $EFI_CERT_TYPE_PKCS7_GUID) 1>/dev/null ; then
|
||||
echo "!!! EFI_CERT_TYPE_PKCS7_GUID not found"
|
||||
ext=".sig"
|
||||
else
|
||||
ext=".pkcs7"
|
||||
fi
|
||||
|
||||
if [ "$1" != "" ] ; then
|
||||
sigfile="$1"
|
||||
shift
|
||||
else
|
||||
sigfile="${infile}${ext}"
|
||||
fi
|
||||
|
||||
dd if="$infile" of="$sigfile" ibs=1 skip=$(($EfiTimeSize + $WinCertHdrSize + $GuidSize)) count=$(($WinCertificateSize - $WinCertHdrSize - $GuidSize)) 2>/dev/null
|
||||
|
||||
echo "Signing data saved to '$sigfile'"
|
||||
|
||||
# TODO: Figure out which openssl command parses this file at the correct level
|
||||
echo "Signing CNs:"
|
||||
openssl asn1parse -inform der -in "$sigfile" | grep -A1 commonName | grep STRING
|
||||
BIN
hackintosh-guide/Utilities/acdtinfo/acdtinfo
Executable file
BIN
hackintosh-guide/Utilities/acdtinfo/acdtinfo
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/acdtinfo/acdtinfo.linux
Executable file
BIN
hackintosh-guide/Utilities/acdtinfo/acdtinfo.linux
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/disklabel/disklabel
Executable file
BIN
hackintosh-guide/Utilities/disklabel/disklabel
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/disklabel/disklabel.exe
Executable file
BIN
hackintosh-guide/Utilities/disklabel/disklabel.exe
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/disklabel/disklabel.linux
Executable file
BIN
hackintosh-guide/Utilities/disklabel/disklabel.linux
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/icnspack/icnspack
Executable file
BIN
hackintosh-guide/Utilities/icnspack/icnspack
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/icnspack/icnspack.linux
Executable file
BIN
hackintosh-guide/Utilities/icnspack/icnspack.linux
Executable file
Binary file not shown.
219
hackintosh-guide/Utilities/kpdescribe/kpdescribe.sh
Executable file
219
hackintosh-guide/Utilities/kpdescribe/kpdescribe.sh
Executable file
@@ -0,0 +1,219 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# kpdescribe.sh (formerly kernel_diagreport2text.sh)
|
||||
#
|
||||
# Prints the stack trace from an OS X kernel panic diagnostic report, along
|
||||
# with as much symbol translation as your mach_kernel version provides.
|
||||
# By default, this is some, but with the Kernel Debug Kit, it should be a lot
|
||||
# more. This is not an official Apple tool.
|
||||
#
|
||||
# USAGE:
|
||||
# ./kpdescribe.sh [-f kernel_file] [-k kext_dir1;kext_dir2] Kernel_report.panic [...]
|
||||
#
|
||||
# Note: The Kernel Debug Kit currently requires an Apple ID to download. It
|
||||
# would be great if this was not necessary.
|
||||
#
|
||||
# This script calls atos(1) for symbol translation, and also some sed/awk
|
||||
# to decorate remaining untranslated symbols with kernel extension names,
|
||||
# if the ranges match.
|
||||
#
|
||||
# This uses your current kernel, /mach_kernel, to translate symbols. If you run
|
||||
# this on kernel diag reports from a different kernel version, it will print
|
||||
# a "kernel version mismatch" warning, as the translation may be incorrect. Find
|
||||
# a matching mach_kernel file and use the -f option to point to it.
|
||||
#
|
||||
# Updated in 2018 by vit9696 to support recent macOS versions, KEXT symbol solving,
|
||||
# register printing, and other stuff to work in bash.
|
||||
#
|
||||
# Copyright 2014 Brendan Gregg. All rights reserved.
|
||||
# Copyright 2018 vit9696. All rights reserved.
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License (the "License").
|
||||
# You may not use this file except in compliance with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at docs/cddl1.txt or
|
||||
# http://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at docs/cddl1.txt.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
|
||||
kernel=/mach_kernel
|
||||
kextdirs=$(echo "/System/Library/Extensions;/Library/Extensions" | tr ";" "\n")
|
||||
|
||||
if [ ! -f "$kernel" ]; then
|
||||
kernel=/System/Library/Kernels/kernel
|
||||
fi
|
||||
|
||||
function usage {
|
||||
echo "USAGE: $0 [-f kernel_file] [-k kext_dir1;kext_dir2] Kernel_diag_report.panic [...]"
|
||||
echo " eg, $0 /Library/Logs/DiagnosticReports/Kernel_2014-05-26-124827_bgregg.panic"
|
||||
exit
|
||||
}
|
||||
(( $# == 0 )) && usage
|
||||
[[ $1 == "-h" || $1 == "--help" ]] && usage
|
||||
|
||||
while true; do
|
||||
if [[ $1 == "-f" ]]; then
|
||||
kernel=$2
|
||||
if [[ ! -e $kernel ]]; then
|
||||
echo "ERROR: Kernel $kernel not found. Quitting."
|
||||
exit 2
|
||||
fi
|
||||
shift 2
|
||||
elif [[ $1 == "-k" ]]; then
|
||||
kextdirs=$(echo "$2" | tr ";" "\n")
|
||||
shift 2
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -x /usr/bin/atos ]]; then
|
||||
echo "ERROR: Couldn't find, and need, /usr/bin/atos. Is this part of Xcode? Quitting..."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
kexts=()
|
||||
# Expansion is intentional here
|
||||
# shellcheck disable=SC2068
|
||||
for kextdir in ${kextdirs[@]}; do
|
||||
if [ -d "$kextdir" ]; then
|
||||
while IFS='' read -r kext; do kexts+=("$kext"); done < <(find "$kextdir" -name Info.plist)
|
||||
fi
|
||||
done
|
||||
|
||||
while (( $# != 0 )); do
|
||||
if [[ "$file" != "" ]]; then print; fi
|
||||
file=$1
|
||||
shift
|
||||
echo "File ${file/$HOME/~}"
|
||||
|
||||
if [[ ! -e "$file" ]]; then
|
||||
print "ERROR: File ""$file"" not found. Skipping."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Find slide address
|
||||
slide=$(awk '/^Kernel slide:.*0x/ { print $3 }' "$file")
|
||||
if [[ "$slide" == "" ]]; then
|
||||
echo -n "ERROR: Missing \"Kernel slide:\" line, so can't process ""$file"". "
|
||||
echo "This is needed for atos -s. Is this really a Kernel diag panic file?"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Print panic line
|
||||
(grep -E -A 50 '^panic' | grep -E -B 50 '^Backtrace') < "$file" | grep -vE '^Backtrace'
|
||||
|
||||
# Check kernel version match (uname -v string)
|
||||
kernel_ver=$(strings -a "$kernel" | grep 'Darwin Kernel Version' | grep -v '@(#)')
|
||||
panic_ver=$(grep 'Darwin Kernel Version' "$file")
|
||||
warn=""
|
||||
if [[ "$kernel_ver" != "$panic_ver" ]]; then
|
||||
echo "WARNING: kernel version mismatch (use -f):"
|
||||
printf "%14s: %s\n" "$kernel" "$kernel_ver"
|
||||
printf "%14s: %s\n" "panic file" "$panic_ver"
|
||||
warn=" (may be incorrect due to mismatch)"
|
||||
fi
|
||||
|
||||
# Find kernel extension ranges
|
||||
ranges=$(awk 'ext == 1 && /0x.*->.*0x/ {
|
||||
gsub(/\(/, " "); gsub(/\)/, ""); gsub(/\[.*\]/, ""); gsub(/@/, " "); gsub(/->/, " ")
|
||||
print $0
|
||||
}
|
||||
/Kernel Extensions in backtrace/ { ext = 1 }
|
||||
/^$/ { ext = 0 }
|
||||
' < "$file" | while read -r n v s e; do
|
||||
# the awk gsub's convert this line:
|
||||
# com.apple.driver.AppleUSBHub(666.4)[CD9B71FF-2FDD-3BC4-9C39-5E066F66D158]@0xffffff7f84ed2000->0xffffff7f84ee9fff
|
||||
# into this:
|
||||
# com.apple.driver.AppleUSBHub 666.4 0xffffff7f84ed2000 0xffffff7f84ee9fff
|
||||
# which can then be read as three fields
|
||||
echo "$n" "$v" "$s" "$e"
|
||||
done)
|
||||
|
||||
i=0
|
||||
unset name version start end kfile
|
||||
while (( i < ${#ranges[@]} )); do
|
||||
read -r n v s e <<< "${ranges[$i]}"
|
||||
name[i]=$n
|
||||
start[i]=$s
|
||||
end[i]=$e
|
||||
|
||||
for kext in "${kexts[@]}"; do
|
||||
if [ ! -f "$kext" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
kname=$(/usr/libexec/PlistBuddy -c 'Print CFBundleIdentifier' "$kext" 2>&1)
|
||||
if [ "$kname" != "$n" ]; then
|
||||
continue
|
||||
fi
|
||||
kver=$(/usr/libexec/PlistBuddy -c 'Print CFBundleVersion' "$kext" 2>&1)
|
||||
if [[ "$kver" =~ $v ]] || [ "$(echo "$v" | grep "$kver")" != "" ]; then
|
||||
path="$(dirname "$kext")/MacOS/$(/usr/libexec/PlistBuddy -c 'Print CFBundleExecutable' "$kext" 2>&1)"
|
||||
if [ -f "$path" ]; then
|
||||
kfile[i]="$path"
|
||||
fi
|
||||
else
|
||||
echo "Version mismatch for $kname ($kver vs $v)"
|
||||
fi
|
||||
done
|
||||
(( i++ ))
|
||||
done
|
||||
|
||||
# Print and translate stack
|
||||
echo "Slide: $slide"
|
||||
echo "Backtrace [addr unslid symbol]$warn:"
|
||||
awk 'backtrace == 1 && /^[^ ]/ { print $3 }
|
||||
/Backtrace.*Return Address/ { backtrace = 1 }
|
||||
/^$/ { backtrace = 0 }
|
||||
' < "$file" | while read -r addr; do
|
||||
line=""
|
||||
# Check extensions
|
||||
if [[ $addr =~ 0x* ]]; then
|
||||
i=0
|
||||
while (( i <= ${#name[@]} )); do
|
||||
[[ "${start[i]}" == "" ]] && break
|
||||
# Assuming fixed width addresses, use string comparison:
|
||||
if [[ $addr > ${start[$i]} && $addr < ${end[$i]} ]]; then
|
||||
unslid=$((addr-${start[$i]}))
|
||||
if [ "${kfile[$i]}" != "" ]; then
|
||||
line=$(atos -o "${kfile[$i]}" -l "${start[$i]}" "$addr")
|
||||
else
|
||||
line="(in ${name[$i]} at ${start[$i]})"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
(( i++ ))
|
||||
done
|
||||
fi
|
||||
# Fallback to kernel
|
||||
if [ "$line" = "" ] ; then
|
||||
line=$(atos -o "$kernel" -s "$slide" "$addr")
|
||||
unslid=$((addr-slide))
|
||||
fi
|
||||
printf "0x%016llx 0x%016llx %s\n" "$addr" "$unslid" "$line"
|
||||
done
|
||||
|
||||
# Print other key details
|
||||
awk '/^BSD process name/ { gsub(/ corresponding to current thread/, ""); print $0 }
|
||||
ver == 1 { print "Mac OS version:", $0; ver = 0 }
|
||||
/^Mac OS version/ { ver = 1 }
|
||||
/^Boot args:/ { print $0 }
|
||||
' < "$file"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "ALWAYS include the original panic log as well!"
|
||||
echo ""
|
||||
8
hackintosh-guide/Utilities/macrecovery/README.md
Executable file
8
hackintosh-guide/Utilities/macrecovery/README.md
Executable file
@@ -0,0 +1,8 @@
|
||||
## macrecovery
|
||||
|
||||
macrecovery is a tool that helps to automate recovery interaction. It can be used to download diagnostics and recovery as well as analyse MLB.
|
||||
|
||||
Requires python3 to run. Run with `-h` argument to see all available arguments.
|
||||
|
||||
To create a disk image for a virtual machine installation use `build-image.sh`.
|
||||
|
||||
79
hackintosh-guide/Utilities/macrecovery/boards.json
Executable file
79
hackintosh-guide/Utilities/macrecovery/boards.json
Executable file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"Mac-EE2EBD4B90B839A8": "13.7.7",
|
||||
"Mac-BE0E8AC46FE800CC": "11.7.10",
|
||||
"Mac-9AE82516C7C6B903": "12.7.6",
|
||||
"Mac-942452F5819B1C1B": "10.13.6",
|
||||
"Mac-942C5DF58193131B": "10.13.6",
|
||||
"Mac-C08A6BB70A942AC2": "10.13.6",
|
||||
"Mac-742912EFDBEE19B3": "10.13.6",
|
||||
"Mac-66F35F19FE2A0D05": "10.15.7",
|
||||
"Mac-2E6FAB96566FE58C": "10.15.7",
|
||||
"Mac-35C1E88140C3E6CF": "11.7.10",
|
||||
"Mac-7DF21CB3ED6977E5": "11.7.10",
|
||||
"Mac-9F18E312C5C2BF0B": "12.7.6",
|
||||
"Mac-937CB26E2E02BB01": "12.7.6",
|
||||
"Mac-827FAC58A8FDFA22": "14.7.7",
|
||||
"Mac-226CB3C6A851A671": "14.7.7",
|
||||
"Mac-0CFF9C7C2B63DF8D": "15.6",
|
||||
"Mac-C3EC7CD22292981F": "10.15.7",
|
||||
"Mac-AFD8A9D944EA4843": "10.15.7",
|
||||
"Mac-189A3D4F975D5FFC": "11.7.10",
|
||||
"Mac-3CBD00234E554E41": "11.7.10",
|
||||
"Mac-2BD1B31983FE1663": "11.7.10",
|
||||
"Mac-06F11FD93F0323C5": "12.7.6",
|
||||
"Mac-06F11F11946D27C5": "12.7.6",
|
||||
"Mac-E43C1C25D4880AD6": "12.7.6",
|
||||
"Mac-473D31EABEB93F9B": "12.7.6",
|
||||
"Mac-66E35819EE2D0D05": "12.7.6",
|
||||
"Mac-A5C67F76ED83108C": "12.7.6",
|
||||
"Mac-B4831CEBD52A0C4C": "13.7.7",
|
||||
"Mac-CAD6701F7CEA0921": "13.7.7",
|
||||
"Mac-551B86E5744E2388": "13.7.7",
|
||||
"Mac-937A206F2EE63C01": "15.6",
|
||||
"Mac-827FB448E656EC26": "15.6",
|
||||
"Mac-1E7E29AD0135F9BC": "15.6",
|
||||
"Mac-53FDB3D8DB8CA971": "15.6",
|
||||
"Mac-E1008331FDC96864": "latest",
|
||||
"Mac-5F9802EFE386AA28": "latest",
|
||||
"Mac-E7203C0F68AA0004": "15.6",
|
||||
"Mac-A61BADE1FDAD7B05": "latest",
|
||||
"Mac-F22589C8": "10.13.6",
|
||||
"Mac-94245B3640C91C81": "10.13.6",
|
||||
"Mac-94245A3940C91C80": "10.13.6",
|
||||
"Mac-942459F5819B171B": "10.13.6",
|
||||
"Mac-4B7AC7E43945597E": "10.15.7",
|
||||
"Mac-6F01561E16C75D06": "10.15.7",
|
||||
"Mac-F60DEB81FF30ACF6": "12.7.6",
|
||||
"Mac-27AD2F918AE68F61": "latest",
|
||||
"Mac-F2208EC8": "10.13.6",
|
||||
"Mac-8ED6AF5B48C039E1": "10.13.6",
|
||||
"Mac-4BC72D62AD45599E": "10.13.6",
|
||||
"Mac-7BA5B2794B2CDB12": "10.13.6",
|
||||
"Mac-031AEE4D24BFF0B1": "10.15.7",
|
||||
"Mac-F65AE981FFA204ED": "10.15.7",
|
||||
"Mac-35C5E08120C7EEAF": "12.7.6",
|
||||
"Mac-7BA5B2DFE22DDD8C": "15.6",
|
||||
"Mac-942B5BF58194151B": "10.13.6",
|
||||
"Mac-942B59F58194171B": "10.13.6",
|
||||
"Mac-00BE6ED71E35EB86": "10.15.7",
|
||||
"Mac-FC02E91DDD3FA6A4": "10.15.7",
|
||||
"Mac-7DF2A3B5E5D671ED": "10.15.7",
|
||||
"Mac-031B6874CF7F642A": "10.15.7",
|
||||
"Mac-27ADBB7B4CEE8E61": "10.15.7",
|
||||
"Mac-77EB7D7DAF985301": "10.15.7",
|
||||
"Mac-81E3E92DD6088272": "11.7.10",
|
||||
"Mac-42FD25EABCABB274": "11.7.10",
|
||||
"Mac-A369DDC4E67F1C45": "12.7.6",
|
||||
"Mac-FFE5EF870D7BA81A": "12.7.6",
|
||||
"Mac-DB15BD556843C820": "12.7.6",
|
||||
"Mac-65CE76090165799A": "12.7.6",
|
||||
"Mac-B809C3757DA9BB8D": "12.7.6",
|
||||
"Mac-4B682C642B45593E": "13.7.7",
|
||||
"Mac-77F17D7DA9285301": "13.7.7",
|
||||
"Mac-BE088AF8C5EB4FA2": "13.7.7",
|
||||
"Mac-AA95B1DDAB278B95": "15.6",
|
||||
"Mac-63001698E7A34814": "15.6",
|
||||
"Mac-CFF7D910A743CAAF": "latest",
|
||||
"Mac-AF89B6D9451A490B": "latest",
|
||||
"Mac-7BA5B2D9E42DDD94": "15.6"
|
||||
}
|
||||
24
hackintosh-guide/Utilities/macrecovery/build-image.sh
Executable file
24
hackintosh-guide/Utilities/macrecovery/build-image.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
FORMAT="raw"
|
||||
else
|
||||
FORMAT="${1}"
|
||||
fi
|
||||
|
||||
rm -rf Recovery.RO.dmg Recovery.raw "Recovery.${FORMAT}" Recovery.dmg.sparseimage
|
||||
hdiutil create -size 900m -layout "UNIVERSAL HD" -type SPARSE -o Recovery.dmg
|
||||
newDevice=$(hdiutil attach -nomount Recovery.dmg.sparseimage | head -n 1 | awk '{print $1}')
|
||||
echo newdevice "$newDevice"
|
||||
diskutil partitionDisk "${newDevice}" 1 MBR fat32 RECOVERY R
|
||||
N=$(echo "$newDevice" | tr -dc '0-9')
|
||||
diskutil mount disk"${N}"s1
|
||||
MOUNT="$(diskutil info disk"${N}"s1 | sed -n 's/.*Mount Point: *//p')"
|
||||
mkdir -p "$MOUNT/com.apple.recovery.boot"
|
||||
cp ./*.dmg ./*.chunklist "$MOUNT/com.apple.recovery.boot/"
|
||||
diskutil umount disk"${N}"s1
|
||||
hdiutil detach "$newDevice"
|
||||
hdiutil convert -format UDZO Recovery.dmg.sparseimage -o Recovery.RO.dmg
|
||||
rm Recovery.dmg.sparseimage
|
||||
qemu-img convert -f dmg -O "${FORMAT}" Recovery.RO.dmg "Recovery.${FORMAT}"
|
||||
rm Recovery.RO.dmg
|
||||
519
hackintosh-guide/Utilities/macrecovery/macrecovery.py
Executable file
519
hackintosh-guide/Utilities/macrecovery/macrecovery.py
Executable file
@@ -0,0 +1,519 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Gather recovery information for Macs.
|
||||
|
||||
Copyright (c) 2019, vit9696
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import linecache
|
||||
import os
|
||||
import random
|
||||
import struct
|
||||
import string
|
||||
import sys
|
||||
|
||||
try:
|
||||
from urllib.request import Request, HTTPError, urlopen
|
||||
from urllib.parse import urlparse
|
||||
except ImportError:
|
||||
print('ERROR: Python 2 is not supported, please use Python 3')
|
||||
sys.exit(1)
|
||||
|
||||
SELF_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# MacPro7,1
|
||||
RECENT_MAC = 'Mac-27AD2F918AE68F61'
|
||||
MLB_ZERO = '00000000000000000'
|
||||
MLB_VALID = 'F5K105303J9K3F71M'
|
||||
MLB_PRODUCT = 'F5K00000000K3F700'
|
||||
|
||||
TYPE_SID = 16
|
||||
TYPE_K = 64
|
||||
TYPE_FG = 64
|
||||
|
||||
INFO_PRODUCT = 'AP'
|
||||
INFO_IMAGE_LINK = 'AU'
|
||||
INFO_IMAGE_HASH = 'AH'
|
||||
INFO_IMAGE_SESS = 'AT'
|
||||
INFO_SIGN_LINK = 'CU'
|
||||
INFO_SIGN_HASH = 'CH'
|
||||
INFO_SIGN_SESS = 'CT'
|
||||
INFO_REQURED = [INFO_PRODUCT, INFO_IMAGE_LINK, INFO_IMAGE_HASH, INFO_IMAGE_SESS, INFO_SIGN_LINK, INFO_SIGN_HASH, INFO_SIGN_SESS]
|
||||
|
||||
# Use -2 for better resize stability on Windows
|
||||
TERMINAL_MARGIN = 2
|
||||
|
||||
def run_query(url, headers, post=None, raw=False):
|
||||
if post is not None:
|
||||
data = '\n'.join(entry + '=' + post[entry] for entry in post).encode()
|
||||
else:
|
||||
data = None
|
||||
req = Request(url=url, headers=headers, data=data)
|
||||
try:
|
||||
response = urlopen(req)
|
||||
if raw:
|
||||
return response
|
||||
return dict(response.info()), response.read()
|
||||
except HTTPError as e:
|
||||
print(f'ERROR: "{e}" when connecting to {url}')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def generate_id(id_type, id_value=None):
|
||||
return id_value or ''.join(random.choices(string.hexdigits[:16].upper(), k=id_type))
|
||||
|
||||
|
||||
def product_mlb(mlb):
|
||||
return '00000000000' + mlb[11:15] + '00'
|
||||
|
||||
|
||||
def mlb_from_eeee(eeee):
|
||||
if len(eeee) != 4:
|
||||
print('ERROR: Invalid EEEE code length!')
|
||||
sys.exit(1)
|
||||
|
||||
return f'00000000000{eeee}00'
|
||||
|
||||
|
||||
# zhangyoufu https://gist.github.com/MCJack123/943eaca762730ca4b7ae460b731b68e7#gistcomment-3061078 2021-10-08
|
||||
Apple_EFI_ROM_public_key_1 = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF
|
||||
|
||||
ChunkListHeader = struct.Struct('<4sIBBBxQQQ')
|
||||
assert ChunkListHeader.size == 0x24
|
||||
|
||||
Chunk = struct.Struct('<I32s')
|
||||
assert Chunk.size == 0x24
|
||||
|
||||
|
||||
def verify_chunklist(cnkpath):
|
||||
with open(cnkpath, 'rb') as f:
|
||||
hash_ctx = hashlib.sha256()
|
||||
data = f.read(ChunkListHeader.size)
|
||||
hash_ctx.update(data)
|
||||
magic, header_size, file_version, chunk_method, signature_method, chunk_count, chunk_offset, signature_offset = ChunkListHeader.unpack(data)
|
||||
assert magic == b'CNKL'
|
||||
assert header_size == ChunkListHeader.size
|
||||
assert file_version == 1
|
||||
assert chunk_method == 1
|
||||
assert signature_method in [1, 2]
|
||||
assert chunk_count > 0
|
||||
assert chunk_offset == 0x24
|
||||
assert signature_offset == chunk_offset + Chunk.size * chunk_count
|
||||
for _ in range(chunk_count):
|
||||
data = f.read(Chunk.size)
|
||||
hash_ctx.update(data)
|
||||
chunk_size, chunk_sha256 = Chunk.unpack(data)
|
||||
yield chunk_size, chunk_sha256
|
||||
digest = hash_ctx.digest()
|
||||
if signature_method == 1:
|
||||
data = f.read(256)
|
||||
assert len(data) == 256
|
||||
signature = int.from_bytes(data, 'little')
|
||||
plaintext = int(f'0x1{"f"*404}003031300d060960864801650304020105000420{"0"*64}', 16) | int.from_bytes(digest, 'big')
|
||||
assert pow(signature, 0x10001, Apple_EFI_ROM_public_key_1) == plaintext
|
||||
elif signature_method == 2:
|
||||
data = f.read(32)
|
||||
assert data == digest
|
||||
raise RuntimeError('Chunklist missing digital signature')
|
||||
else:
|
||||
raise NotImplementedError
|
||||
assert f.read(1) == b''
|
||||
|
||||
|
||||
def get_session(args):
|
||||
headers = {
|
||||
'Host': 'osrecovery.apple.com',
|
||||
'Connection': 'close',
|
||||
'User-Agent': 'InternetRecovery/1.0',
|
||||
}
|
||||
|
||||
headers, _ = run_query('http://osrecovery.apple.com/', headers)
|
||||
|
||||
if args.verbose:
|
||||
print('Session headers:')
|
||||
for header in headers:
|
||||
print(f'{header}: {headers[header]}')
|
||||
|
||||
for header in headers:
|
||||
if header.lower() == 'set-cookie':
|
||||
cookies = headers[header].split('; ')
|
||||
for cookie in cookies:
|
||||
return cookie if cookie.startswith('session=') else ...
|
||||
|
||||
raise RuntimeError('No session in headers ' + str(headers))
|
||||
|
||||
|
||||
def get_image_info(session, bid, mlb=MLB_ZERO, diag=False, os_type='default', cid=None):
|
||||
headers = {
|
||||
'Host': 'osrecovery.apple.com',
|
||||
'Connection': 'close',
|
||||
'User-Agent': 'InternetRecovery/1.0',
|
||||
'Cookie': session,
|
||||
'Content-Type': 'text/plain',
|
||||
}
|
||||
|
||||
post = {
|
||||
'cid': generate_id(TYPE_SID, cid),
|
||||
'sn': mlb,
|
||||
'bid': bid,
|
||||
'k': generate_id(TYPE_K),
|
||||
'fg': generate_id(TYPE_FG)
|
||||
}
|
||||
|
||||
if diag:
|
||||
url = 'http://osrecovery.apple.com/InstallationPayload/Diagnostics'
|
||||
else:
|
||||
url = 'http://osrecovery.apple.com/InstallationPayload/RecoveryImage'
|
||||
post['os'] = os_type
|
||||
|
||||
headers, output = run_query(url, headers, post)
|
||||
|
||||
output = output.decode('utf-8')
|
||||
info = {}
|
||||
for line in output.split('\n'):
|
||||
try:
|
||||
key, value = line.split(': ')
|
||||
info[key] = value
|
||||
except KeyError:
|
||||
continue
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
for k in INFO_REQURED:
|
||||
if k not in info:
|
||||
raise RuntimeError(f'Missing key {k}')
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def save_image(url, sess, filename='', directory=''):
|
||||
purl = urlparse(url)
|
||||
headers = {
|
||||
'Host': purl.hostname,
|
||||
'Connection': 'close',
|
||||
'User-Agent': 'InternetRecovery/1.0',
|
||||
'Cookie': '='.join(['AssetToken', sess])
|
||||
}
|
||||
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
if filename == '':
|
||||
filename = os.path.basename(purl.path)
|
||||
if filename.find(os.sep) >= 0 or filename == '':
|
||||
raise RuntimeError('Invalid save path ' + filename)
|
||||
|
||||
print(f'Saving {url} to {directory}{os.sep}{filename}...')
|
||||
|
||||
with open(os.path.join(directory, filename), 'wb') as fh:
|
||||
response = run_query(url, headers, raw=True)
|
||||
headers = dict(response.headers)
|
||||
totalsize = -1
|
||||
for header in headers:
|
||||
if header.lower() == 'content-length':
|
||||
totalsize = int(headers[header])
|
||||
break
|
||||
size = 0
|
||||
oldterminalsize = 0
|
||||
while True:
|
||||
chunk = response.read(2**20)
|
||||
if not chunk:
|
||||
break
|
||||
fh.write(chunk)
|
||||
size += len(chunk)
|
||||
try:
|
||||
terminalsize = max(os.get_terminal_size().columns - TERMINAL_MARGIN, 0)
|
||||
except OSError:
|
||||
terminalsize = 80
|
||||
if oldterminalsize != terminalsize:
|
||||
print(f'\r{"":<{terminalsize}}', end='')
|
||||
oldterminalsize = terminalsize
|
||||
if totalsize > 0:
|
||||
progress = size / totalsize
|
||||
barwidth = terminalsize // 3
|
||||
print(f'\r{size / (2**20):.1f}/{totalsize / (2**20):.1f} MB ', end='')
|
||||
if terminalsize > 55:
|
||||
print(f'|{"=" * int(barwidth * progress):<{barwidth}}|', end='')
|
||||
print(f' {progress*100:.1f}% downloaded', end='')
|
||||
else:
|
||||
# Fallback if Content-Length isn't available
|
||||
print(f'\r{size / (2**20)} MB downloaded...', end='')
|
||||
sys.stdout.flush()
|
||||
print('\nDownload complete!')
|
||||
|
||||
return os.path.join(directory, os.path.basename(filename))
|
||||
|
||||
|
||||
def verify_image(dmgpath, cnkpath):
|
||||
print('Verifying image with chunklist...')
|
||||
|
||||
with open(dmgpath, 'rb') as dmgf:
|
||||
for cnkcount, (cnksize, cnkhash) in enumerate(verify_chunklist(cnkpath), 1):
|
||||
terminalsize = max(os.get_terminal_size().columns - TERMINAL_MARGIN, 0)
|
||||
print(f'\r{f"Chunk {cnkcount} ({cnksize} bytes)":<{terminalsize}}', end='')
|
||||
sys.stdout.flush()
|
||||
cnk = dmgf.read(cnksize)
|
||||
if len(cnk) != cnksize:
|
||||
raise RuntimeError(f'Invalid chunk {cnkcount} size: expected {cnksize}, read {len(cnk)}')
|
||||
if hashlib.sha256(cnk).digest() != cnkhash:
|
||||
raise RuntimeError(f'Invalid chunk {cnkcount}: hash mismatch')
|
||||
if dmgf.read(1) != b'':
|
||||
raise RuntimeError('Invalid image: larger than chunklist')
|
||||
print('\nImage verification complete!')
|
||||
|
||||
|
||||
def action_download(args):
|
||||
"""
|
||||
Reference information for queries:
|
||||
|
||||
Recovery latest:
|
||||
cid=3076CE439155BA14
|
||||
sn=...
|
||||
bid=Mac-E43C1C25D4880AD6
|
||||
k=4BE523BB136EB12B1758C70DB43BDD485EBCB6A457854245F9E9FF0587FB790C
|
||||
os=latest
|
||||
fg=B2E6AA07DB9088BE5BDB38DB2EA824FDDFB6C3AC5272203B32D89F9D8E3528DC
|
||||
|
||||
Recovery default:
|
||||
cid=4A35CB95FF396EE7
|
||||
sn=...
|
||||
bid=Mac-E43C1C25D4880AD6
|
||||
k=0A385E6FFC3DDD990A8A1F4EC8B98C92CA5E19C9FF1DD26508C54936D8523121
|
||||
os=default
|
||||
fg=B2E6AA07DB9088BE5BDB38DB2EA824FDDFB6C3AC5272203B32D89F9D8E3528DC
|
||||
|
||||
Diagnostics:
|
||||
cid=050C59B51497CEC8
|
||||
sn=...
|
||||
bid=Mac-E43C1C25D4880AD6
|
||||
k=37D42A8282FE04A12A7D946304F403E56A2155B9622B385F3EB959A2FBAB8C93
|
||||
fg=B2E6AA07DB9088BE5BDB38DB2EA824FDDFB6C3AC5272203B32D89F9D8E3528DC
|
||||
"""
|
||||
|
||||
session = get_session(args)
|
||||
info = get_image_info(session, bid=args.board_id, mlb=args.mlb, diag=args.diagnostics, os_type=args.os_type)
|
||||
if args.verbose:
|
||||
print(info)
|
||||
print(f'Downloading {info[INFO_PRODUCT]}...')
|
||||
cnkname = '' if args.basename == '' else args.basename + '.chunklist'
|
||||
cnkpath = save_image(info[INFO_SIGN_LINK], info[INFO_SIGN_SESS], cnkname, args.outdir)
|
||||
dmgname = '' if args.basename == '' else args.basename + '.dmg'
|
||||
dmgpath = save_image(info[INFO_IMAGE_LINK], info[INFO_IMAGE_SESS], dmgname, args.outdir)
|
||||
try:
|
||||
verify_image(dmgpath, cnkpath)
|
||||
return 0
|
||||
except Exception as err:
|
||||
if isinstance(err, AssertionError) and str(err) == '':
|
||||
try:
|
||||
tb = sys.exc_info()[2]
|
||||
while tb.tb_next:
|
||||
tb = tb.tb_next
|
||||
err = linecache.getline(tb.tb_frame.f_code.co_filename, tb.tb_lineno, tb.tb_frame.f_globals).strip()
|
||||
except Exception:
|
||||
err = "Invalid chunklist"
|
||||
print(f'\rImage verification failed. ({err})')
|
||||
return 1
|
||||
|
||||
|
||||
def action_selfcheck(args):
|
||||
"""
|
||||
Sanity check server logic for recovery:
|
||||
|
||||
if not valid(bid):
|
||||
return error()
|
||||
ppp = get_ppp(sn)
|
||||
if not valid(ppp):
|
||||
return latest_recovery(bid = bid) # Returns newest for bid.
|
||||
if valid(sn):
|
||||
if os == 'default':
|
||||
return default_recovery(sn = sn, ppp = ppp) # Returns oldest for sn.
|
||||
else:
|
||||
return latest_recovery(sn = sn, ppp = ppp) # Returns newest for sn.
|
||||
return default_recovery(ppp = ppp) # Returns oldest.
|
||||
"""
|
||||
|
||||
session = get_session(args)
|
||||
valid_default = get_image_info(session, bid=RECENT_MAC, mlb=MLB_VALID, diag=False, os_type='default')
|
||||
valid_latest = get_image_info(session, bid=RECENT_MAC, mlb=MLB_VALID, diag=False, os_type='latest')
|
||||
product_default = get_image_info(session, bid=RECENT_MAC, mlb=MLB_PRODUCT, diag=False, os_type='default')
|
||||
product_latest = get_image_info(session, bid=RECENT_MAC, mlb=MLB_PRODUCT, diag=False, os_type='latest')
|
||||
generic_default = get_image_info(session, bid=RECENT_MAC, mlb=MLB_ZERO, diag=False, os_type='default')
|
||||
generic_latest = get_image_info(session, bid=RECENT_MAC, mlb=MLB_ZERO, diag=False, os_type='latest')
|
||||
|
||||
if args.verbose:
|
||||
print(valid_default)
|
||||
print(valid_latest)
|
||||
print(product_default)
|
||||
print(product_latest)
|
||||
print(generic_default)
|
||||
print(generic_latest)
|
||||
|
||||
if valid_default[INFO_PRODUCT] == valid_latest[INFO_PRODUCT]:
|
||||
# Valid MLB must give different default and latest if this is not a too new product.
|
||||
print(f'ERROR: Cannot determine any previous product, got {valid_default[INFO_PRODUCT]}')
|
||||
return 1
|
||||
|
||||
if product_default[INFO_PRODUCT] != product_latest[INFO_PRODUCT]:
|
||||
# Product-only MLB must give the same value for default and latest.
|
||||
print(f'ERROR: Latest and default do not match for product MLB, got {product_default[INFO_PRODUCT]} and {product_latest[INFO_PRODUCT]}')
|
||||
return 1
|
||||
|
||||
if generic_default[INFO_PRODUCT] != generic_latest[INFO_PRODUCT]:
|
||||
# Zero MLB always give the same value for default and latest.
|
||||
print(f'ERROR: Generic MLB gives different product, got {generic_default[INFO_PRODUCT]} and {generic_latest[INFO_PRODUCT]}')
|
||||
return 1
|
||||
|
||||
if valid_latest[INFO_PRODUCT] != generic_latest[INFO_PRODUCT]:
|
||||
# Valid MLB must always equal generic MLB.
|
||||
print(f'ERROR: Cannot determine unified latest product, got {valid_latest[INFO_PRODUCT]} and {generic_latest[INFO_PRODUCT]}')
|
||||
return 1
|
||||
|
||||
if product_default[INFO_PRODUCT] != valid_default[INFO_PRODUCT]:
|
||||
# Product-only MLB can give the same value with valid default MLB.
|
||||
# This is not an error for all models, but for our chosen code it is.
|
||||
print(f'ERROR: Valid and product MLB give mismatch, got {product_default[INFO_PRODUCT]} and {valid_default[INFO_PRODUCT]}')
|
||||
return 1
|
||||
|
||||
print('SUCCESS: Found no discrepancies with MLB validation algorithm!')
|
||||
return 0
|
||||
|
||||
|
||||
def action_verify(args):
|
||||
"""
|
||||
Try to verify MLB serial number.
|
||||
"""
|
||||
session = get_session(args)
|
||||
generic_latest = get_image_info(session, bid=RECENT_MAC, mlb=MLB_ZERO, diag=False, os_type='latest')
|
||||
uvalid_default = get_image_info(session, bid=args.board_id, mlb=args.mlb, diag=False, os_type='default')
|
||||
uvalid_latest = get_image_info(session, bid=args.board_id, mlb=args.mlb, diag=False, os_type='latest')
|
||||
uproduct_default = get_image_info(session, bid=args.board_id, mlb=product_mlb(args.mlb), diag=False, os_type='default')
|
||||
|
||||
if args.verbose:
|
||||
print(generic_latest)
|
||||
print(uvalid_default)
|
||||
print(uvalid_latest)
|
||||
print(uproduct_default)
|
||||
|
||||
# Verify our MLB number.
|
||||
if uvalid_default[INFO_PRODUCT] != uvalid_latest[INFO_PRODUCT]:
|
||||
print(f'SUCCESS: {args.mlb} MLB looks valid and supported!' if uvalid_latest[INFO_PRODUCT] == generic_latest[INFO_PRODUCT] else f'SUCCESS: {args.mlb} MLB looks valid, but probably unsupported!')
|
||||
return 0
|
||||
|
||||
print('UNKNOWN: Run selfcheck, check your board-id, or try again later!')
|
||||
|
||||
# Here we have matching default and latest products. This can only be true for very
|
||||
# new models. These models get either latest or special builds.
|
||||
if uvalid_default[INFO_PRODUCT] == generic_latest[INFO_PRODUCT]:
|
||||
print(f'UNKNOWN: {args.mlb} MLB can be valid if very new!')
|
||||
return 0
|
||||
if uproduct_default[INFO_PRODUCT] != uvalid_default[INFO_PRODUCT]:
|
||||
print(f'UNKNOWN: {args.mlb} MLB looks invalid, other models use product {uproduct_default[INFO_PRODUCT]} instead of {uvalid_default[INFO_PRODUCT]}!')
|
||||
return 0
|
||||
print(f'UNKNOWN: {args.mlb} MLB can be valid if very new and using special builds!')
|
||||
return 0
|
||||
|
||||
|
||||
def action_guess(args):
|
||||
"""
|
||||
Attempt to guess which model does this MLB belong.
|
||||
"""
|
||||
|
||||
mlb = args.mlb
|
||||
anon = mlb.startswith('000')
|
||||
|
||||
with open(args.board_db, 'r', encoding='utf-8') as fh:
|
||||
db = json.load(fh)
|
||||
|
||||
supported = {}
|
||||
|
||||
session = get_session(args)
|
||||
|
||||
generic_latest = get_image_info(session, bid=RECENT_MAC, mlb=MLB_ZERO, diag=False, os_type='latest')
|
||||
|
||||
for model in db:
|
||||
try:
|
||||
if anon:
|
||||
# For anonymous lookup check when given model does not match latest.
|
||||
model_latest = get_image_info(session, bid=model, mlb=MLB_ZERO, diag=False, os_type='latest')
|
||||
|
||||
if model_latest[INFO_PRODUCT] != generic_latest[INFO_PRODUCT]:
|
||||
if db[model] == 'current':
|
||||
print(f'WARN: Skipped {model} due to using latest product {model_latest[INFO_PRODUCT]} instead of {generic_latest[INFO_PRODUCT]}')
|
||||
continue
|
||||
|
||||
user_default = get_image_info(session, bid=model, mlb=mlb, diag=False, os_type='default')
|
||||
|
||||
if user_default[INFO_PRODUCT] != generic_latest[INFO_PRODUCT]:
|
||||
supported[model] = [db[model], user_default[INFO_PRODUCT], generic_latest[INFO_PRODUCT]]
|
||||
else:
|
||||
# For normal lookup check when given model has mismatching normal and latest.
|
||||
user_latest = get_image_info(session, bid=model, mlb=mlb, diag=False, os_type='latest')
|
||||
|
||||
user_default = get_image_info(session, bid=model, mlb=mlb, diag=False, os_type='default')
|
||||
|
||||
if user_latest[INFO_PRODUCT] != user_default[INFO_PRODUCT]:
|
||||
supported[model] = [db[model], user_default[INFO_PRODUCT], user_latest[INFO_PRODUCT]]
|
||||
|
||||
except Exception as e:
|
||||
print(f'WARN: Failed to check {model}, exception: {e}')
|
||||
|
||||
if len(supported) > 0:
|
||||
print(f'SUCCESS: MLB {mlb} looks supported for:')
|
||||
for model in supported.items():
|
||||
print(f'- {model}, up to {supported[model][0]}, default: {supported[model][1]}, latest: {supported[model][2]}')
|
||||
return 0
|
||||
|
||||
print(f'UNKNOWN: Failed to determine supported models for MLB {mlb}!')
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Gather recovery information for Macs')
|
||||
parser.add_argument('action', choices=['download', 'selfcheck', 'verify', 'guess'],
|
||||
help='Action to perform: "download" - performs recovery downloading,'
|
||||
' "selfcheck" checks whether MLB serial validation is possible, "verify" performs'
|
||||
' MLB serial verification, "guess" tries to find suitable mac model for MLB.')
|
||||
parser.add_argument('-o', '--outdir', type=str, default='com.apple.recovery.boot',
|
||||
help='customise output directory for downloading, defaults to com.apple.recovery.boot')
|
||||
parser.add_argument('-n', '--basename', type=str, default='',
|
||||
help='customise base name for downloading, defaults to remote name')
|
||||
parser.add_argument('-b', '--board-id', type=str, default=RECENT_MAC,
|
||||
help=f'use specified board identifier for downloading, defaults to {RECENT_MAC}')
|
||||
parser.add_argument('-m', '--mlb', type=str, default=MLB_ZERO,
|
||||
help=f'use specified logic board serial for downloading, defaults to {MLB_ZERO}')
|
||||
parser.add_argument('-e', '--code', type=str, default='',
|
||||
help='generate product logic board serial with specified product EEEE code')
|
||||
parser.add_argument('-os', '--os-type', type=str, default='default', choices=['default', 'latest'],
|
||||
help=f'use specified os type, defaults to default {MLB_ZERO}')
|
||||
parser.add_argument('-diag', '--diagnostics', action='store_true', help='download diagnostics image')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='print debug information')
|
||||
parser.add_argument('-db', '--board-db', type=str, default=os.path.join(SELF_DIR, 'boards.json'),
|
||||
help='use custom board list for checking, defaults to boards.json')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.code != '':
|
||||
args.mlb = mlb_from_eeee(args.code)
|
||||
|
||||
if len(args.mlb) != 17:
|
||||
print('ERROR: Cannot use MLBs in non 17 character format!')
|
||||
sys.exit(1)
|
||||
|
||||
if args.action == 'download':
|
||||
return action_download(args)
|
||||
if args.action == 'selfcheck':
|
||||
return action_selfcheck(args)
|
||||
if args.action == 'verify':
|
||||
return action_verify(args)
|
||||
if args.action == 'guess':
|
||||
return action_guess(args)
|
||||
|
||||
assert False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
60
hackintosh-guide/Utilities/macrecovery/recovery_urls.txt
Executable file
60
hackintosh-guide/Utilities/macrecovery/recovery_urls.txt
Executable file
@@ -0,0 +1,60 @@
|
||||
Lion
|
||||
./macrecovery.py -b Mac-2E6FAB96566FE58C -m 00000000000F25Y00 download
|
||||
./macrecovery.py -b Mac-C3EC7CD22292981F -m 00000000000F0HM00 download
|
||||
|
||||
Mountain Lion:
|
||||
./macrecovery.py -b Mac-7DF2A3B5E5D671ED -m 00000000000F65100 download
|
||||
|
||||
Mavericks
|
||||
./macrecovery.py -b Mac-F60DEB81FF30ACF6 -m 00000000000FNN100 download
|
||||
|
||||
Yosemite:
|
||||
./macrecovery.py -b Mac-E43C1C25D4880AD6 -m 00000000000GDVW00 download
|
||||
|
||||
El Capitan
|
||||
./macrecovery.py -b Mac-FFE5EF870D7BA81A -m 00000000000GQRX00 download
|
||||
|
||||
Sierra
|
||||
./macrecovery.py -b Mac-77F17D7DA9285301 -m 00000000000J0DX00 download
|
||||
|
||||
High Sierra
|
||||
./macrecovery.py -b Mac-7BA5B2D9E42DDD94 -m 00000000000J80300 download
|
||||
./macrecovery.py -b Mac-BE088AF8C5EB4FA2 -m 00000000000J80300 download
|
||||
|
||||
Mojave
|
||||
./macrecovery.py -b Mac-7BA5B2DFE22DDD8C -m 00000000000KXPG00 download
|
||||
|
||||
Catalina
|
||||
./macrecovery.py -b Mac-CFF7D910A743CAAF -m 00000000000PHCD00 download
|
||||
./macrecovery.py -b Mac-00BE6ED71E35EB86 -m 00000000000000000 download
|
||||
|
||||
Big Sur
|
||||
./macrecovery.py -b Mac-2BD1B31983FE1663 -m 00000000000000000 download
|
||||
|
||||
Monterey
|
||||
./macrecovery.py -b Mac-E43C1C25D4880AD6 -m 00000000000000000 download
|
||||
|
||||
Ventura
|
||||
./macrecovery.py -b Mac-B4831CEBD52A0C4C -m 00000000000000000 download
|
||||
|
||||
Sonoma
|
||||
./macrecovery.py -b Mac-827FAC58A8FDFA22 -m 00000000000000000 download
|
||||
|
||||
Sequoia
|
||||
./macrecovery.py -b Mac-7BA5B2D9E42DDD94 -m 00000000000000000 download
|
||||
|
||||
Tahoe
|
||||
./macrecovery.py -b Mac-CFF7D910A743CAAF -m 00000000000000000 -os latest download
|
||||
|
||||
Diagnostics
|
||||
./macrecovery.py -b Mac-7BA5B2D9E42DDD94 -m 00000000000000000 -diag download
|
||||
./macrecovery.py -b Mac-7BA5B2D9E42DDD94 -m 00000000000JG3600 -diag download
|
||||
./macrecovery.py -b Mac-7BA5B2D9E42DDD94 <real MLB> -diag download
|
||||
|
||||
Default version
|
||||
./macrecovery.py -b Mac-7BA5B2D9E42DDD94 -m 00000000000JG3600 download (oldest)
|
||||
./macrecovery.py -b Mac-7BA5B2D9E42DDD94 -m <real MLB> -os default download (newer)
|
||||
|
||||
Latest version
|
||||
./macrecovery.py -b Mac-CFF7D910A743CAAF -m 00000000000000000 -os latest download
|
||||
./macrecovery.py -b Mac-CFF7D910A743CAAF -m <real MLB> -os latest
|
||||
224
hackintosh-guide/Utilities/macserial/FORMAT.md
Executable file
224
hackintosh-guide/Utilities/macserial/FORMAT.md
Executable file
@@ -0,0 +1,224 @@
|
||||
Apple Mac Serial Format
|
||||
=======================
|
||||
|
||||
It is reasonably important to get more information about the goods you buy, especially if they are not new, and you do not have absolute confidence in the seller. Serial numbers are the first thing to look at. For Apple products [Apple Check Coverage](https://checkcoverage.apple.com) is your best friend.
|
||||
|
||||
However, it does not show all the details encoded in the serial, and in some case it may be important. For example, certain shady dealers may change one valid serial by the other, and it will not be obvious at a glance that the serial does not belong to the actual model. This FAQ attempts to explain the reverse-engineered structure of the serials used in Apple hardware.
|
||||
|
||||
You could always receive information about the current serial number of your Mac by running `./macserial`. For the other serial use `./macserial -i SERIALNUMBER`, where `SERIALNUMBER` is the serial you check.
|
||||
|
||||
## Apple base 34
|
||||
|
||||
Select fields in the numbers are encoded values in base 34. So, certain alpha-numeric characters represent a slightly uncommon base 34 code excluding `O` and `I`.
|
||||
|
||||
| Char | Value | Char | Value |
|
||||
| ---- | ----- | ---- | ----- |
|
||||
| `0` | `0` | `H` | `17` |
|
||||
| `1` | `1` | `J` | `18` |
|
||||
| `2` | `2` | `K` | `19` |
|
||||
| `3` | `3` | `L` | `20` |
|
||||
| `4` | `4` | `M` | `21` |
|
||||
| `5` | `5` | `N` | `22` |
|
||||
| `6` | `6` | `P` | `23` |
|
||||
| `7` | `7` | `Q` | `24` |
|
||||
| `8` | `8` | `R` | `25` |
|
||||
| `9` | `9` | `S` | `26` |
|
||||
| `A` | `10` | `T` | `27` |
|
||||
| `B` | `11` | `U` | `28` |
|
||||
| `C` | `12` | `V` | `29` |
|
||||
| `D` | `13` | `W` | `30` |
|
||||
| `E` | `14` | `X` | `31` |
|
||||
| `F` | `15` | `Y` | `32` |
|
||||
| `G` | `16` | `Z` | `33` |
|
||||
|
||||
## Serial number (SN)
|
||||
|
||||
There generally are 2 similar formats of serial encoding: the old 11 character format, and the new 12 character format.
|
||||
|
||||
| Type | Location | Year | Week | Line | Product |
|
||||
| --------- | --------- | ---- | ---- | ----- | -------- |
|
||||
| Old (11) | `LL` | `Y` | `WW` | `SSS` | `PPP` |
|
||||
| New (12) | `LLL` | `Y` | `W` | `SSS` | `PPPP` |
|
||||
|
||||
Note: Models late 2021+ contain SN with 10 character format.
|
||||
|
||||
### Location
|
||||
|
||||
This value encodes the manufacturing location, which is often more descriptive than `Made in China`, since it may reveal the responsible company and the city. For example, `F5K` means `USA (Flextronics)` and `QT` means `Taiwan (Quanta Computer)`. The list is not standardised or published anywhere, but you can see several known locations by running `./macserial -l`.
|
||||
|
||||
One of the important locations for old-style serials (11 characters) is `RM`. It means that the model was refurbished. For new-style serials you have to call [Apple support](https://support.apple.com) to know this.
|
||||
|
||||
### Year
|
||||
|
||||
Year encodes the actual manufacturing year of each model. For refurbished models it is unknown whether it is replaced by the remanufacturing year.
|
||||
|
||||
For old-style serials it always is a digit that encodes the last digit of the year. For example, `8` means 2008 and `1` means 2011. Only `0` to `9` digitis are used for year encoding. Old-style serials are out of use starting with 2013, so `3` means 2003 not 2013.
|
||||
|
||||
| Char | Year |
|
||||
| ---- | ---- |
|
||||
| `3` | 2003 |
|
||||
| `4` | 2004 |
|
||||
| `5` | 2005 |
|
||||
| `6` | 2006 |
|
||||
| `7` | 2007 |
|
||||
| `8` | 2008 |
|
||||
| `9` | 2009 |
|
||||
| `0` | 2010 |
|
||||
| `1` | 2011 |
|
||||
| `2` | 2012 |
|
||||
|
||||
|
||||
For new-style serials it is an alphanumeric value, which not only encodes the year, but its half as well. Not all the values are allowed. The table below outlines the pairs of characters which are assumed to encode each supported year. First character in the pair is believed to encode the first half of the year, and the second character — the second half.
|
||||
|
||||
| Pair | Year |
|
||||
| -------- | ---- |
|
||||
| `C`, `D` | 2010 |
|
||||
| `F`, `G` | 2011 |
|
||||
| `H`, `J` | 2012 |
|
||||
| `K`, `L` | 2013 |
|
||||
| `M`, `N` | 2014 |
|
||||
| `P`, `Q` | 2015 |
|
||||
| `R`, `S` | 2016 |
|
||||
| `T`, `V` | 2017 |
|
||||
| `W`, `X` | 2018 |
|
||||
| `Y`, `Z` | 2019 |
|
||||
| `C`, `D` | 2020 |
|
||||
| `F`, `G` | 2021 |
|
||||
|
||||
### Week
|
||||
|
||||
Week encodes the actual manufacturing week of each model. This week has nothing in common with [ISO 8601](https://en.wikipedia.org/wiki/ISO_week_date), and appears to be encoded literally as 7-day sequences starting from January, 1st. Since each year has either 365 or 366 days, 53rd week is extremely rare, and you are lucky to have such a serial.
|
||||
|
||||
For old-style serials week is encoded in plain numeric digits with leading zeroes. `01`, `02`, ... `53`. For new-style serials an alpha-numeric code is used. Encoded year half also counts and means adds 26 weeks for the second one.
|
||||
|
||||
| Char | 1st half | 2nd half |
|
||||
| ---- | -------- | -------- |
|
||||
| `1` | `1` | `27` |
|
||||
| `2` | `2` | `28` |
|
||||
| `3` | `3` | `29` |
|
||||
| `4` | `4` | `30` |
|
||||
| `5` | `5` | `31` |
|
||||
| `6` | `6` | `32` |
|
||||
| `7` | `7` | `33` |
|
||||
| `8` | `8` | `34` |
|
||||
| `9` | `9` | `35` |
|
||||
| `C` | `10` | `36` |
|
||||
| `D` | `11` | `37` |
|
||||
| `F` | `12` | `38` |
|
||||
| `G` | `13` | `39` |
|
||||
| `H` | `14` | `40` |
|
||||
| `J` | `15` | `41` |
|
||||
| `K` | `16` | `42` |
|
||||
| `L` | `17` | `43` |
|
||||
| `M` | `18` | `44` |
|
||||
| `N` | `19` | `45` |
|
||||
| `P` | `20` | `46` |
|
||||
| `Q` | `21` | `47` |
|
||||
| `R` | `22` | `48` |
|
||||
| `T` | `23` | `49` |
|
||||
| `V` | `24` | `50` |
|
||||
| `W` | `25` | `51` |
|
||||
| `X` | `26` | `52` |
|
||||
| `Y` | `-` | `53` |
|
||||
|
||||
For old-style serials it is a pair of two digits, which encode the manufacturing week.
|
||||
|
||||
### Production line and copy
|
||||
|
||||
Production line is believed to be related to some identifier at assembly stage. It is encoded in base 34, but the actual derivation process is unknown and can only be guessed with relative success.
|
||||
|
||||
Current model, which apparently works well, represents it as a sum of three alpha-numeric characters with `1`, `34`, and `68` multipliers. The actual formula looks as follows:
|
||||
|
||||
```
|
||||
base34[S1] * 68 + base34[S2] * 34 + base34[S3] = production line
|
||||
```
|
||||
|
||||
This formula effectively defines a compression function, which allows to encode a total of `3400` production lines from `0` to `3399`. The compression produced by shortening `39304` space to `3400` allows multiple encodings of the same line. For example, for `939` line there can be `14` derivatives or "copies": `0TM`, `1RM`, `2PM`, `3MM`, `4KM`, ..., `D1M`.
|
||||
|
||||
While the formula does look strange, it was experimentally discovered that up to `N` first encoded derivatives are valid, and starting with the first invalid derivative there will be no valid ones. Thus for a complete serial list made up with all the derivatives from the above the following is assumed to be true: if `0TM` and `2PM` are valid and `3MM` is invalid, then `1RM` will also be valid, and `4KM` to `D1M` will be invalid. From this data it could be theorised that the encoded value is incremented for each model produced from the same line. So `0TM` is the first copy produced, and `D1M` is the last copy.
|
||||
|
||||
**Update**: At a later stage very few examples of valid derivatives after invalid were found. These exceptions disprove at least some parts of the model, but currently there exists no better theory.
|
||||
|
||||
### Product model
|
||||
|
||||
Last 3 (for legacy serials) or 4 (for new serials) symbols encode the actual product identifier of this exact piece of the hardware. This is probably the most useful part of the serial, since it allows you to get the detailed description of your hardware directly from the dedicated Apple Specs portal. To do so you need to modify the following URI to contain your real product code instead of `PPPP` and follow it in your browser:
|
||||
|
||||
```
|
||||
http://support-sp.apple.com/sp/index?page=cpuspec&cc=PPPP
|
||||
```
|
||||
|
||||
For example, for iMacPro1,1 it could be [HX87](http://support-sp.apple.com/sp/index?page=cpuspec&cc=HX87) and for MacBookPro14,3 it could be [HTD5](http://support-sp.apple.com/sp/index?page=cpuspec&cc=HTD5).
|
||||
|
||||
The list is not standardised or published anywhere, but you can see most products by running `./macserial -lp` and `./macserial -l` to match against mac models. The value seems to be a classic base 34 sequence: `P1 * 39304 + P2 * 1156 + P3 * 34 + P4`. The ranges seem to be allocated in chunks in non-decreasing manner. Normally each chunk is distanced from another chunk by up to 64 (90% matches).
|
||||
|
||||
## Logic board serial number (MLB)
|
||||
|
||||
There generally are 2 formats of logic board serial encoding: the old 13 character format, and the new 17 character format. Unlike serial number, these formats are quite different and in addition very little is known about MLB in general.
|
||||
|
||||
| Type | Location | Year | Week | Item | Infix | Product | Suffix |
|
||||
| --------- | --------- | ---- | ---- | ------ | ------ | -------- | ------ |
|
||||
| Old (13) | `LL` | `Y` | `WW` | `IIII` | | `EEE` | `C` |
|
||||
| New (17) | `LLL` | `Y` | `WW` | `III` | `AA` | `EEEE` | `CC` |
|
||||
|
||||
While it is unclear if this is intentional, for 17 character MLB it is possible to perform basic validation online through `osrecovery.apple.com`. The recovery server will return valid latest recovery image only when MLB is valid. Use `./macrecovery.py verify -m MLB -b BOARD-ID` to try verifying your MLB number.
|
||||
|
||||
It is not clear how strongly MLB is attached to serial number (SN). The following is known:
|
||||
|
||||
- Minimal supported macOS version is identified by `EEEE`
|
||||
- Maximum supported macOS version is identified by `EEEE` and `board-id`
|
||||
- Recovery server accepts a range of models with the same MLB (with only latest os different)
|
||||
|
||||
The following is suspected:
|
||||
- `EEEE` is unique number for all MLBs
|
||||
- `EEEE` are shared across different models and thus cannot identify the model
|
||||
|
||||
### Location
|
||||
|
||||
MLB location is equivalent to serial number location but does not necessarily match it, as logic boards can be manufactured at a different place.
|
||||
|
||||
### Year and week
|
||||
|
||||
MLB year and week in both 13-character and 17-character MLB are equivalent to legacy serial number year and week. The values are slightly lower as logic board is manufactured prior to the complete product.
|
||||
|
||||
### Item
|
||||
|
||||
MLB item is encoded differently for 13-character and 17-character MLB. It might serve as a production item per week and could be similar to 'Production line and copy' in the serial number.
|
||||
|
||||
- For old MLB, this is a variant of base 34 value. First item character is always `0`.
|
||||
- For new MLB, this value always is a number.
|
||||
|
||||
### Infix
|
||||
|
||||
Base 34 value present in new MLBs only. No information is known about it. Could actually be part of Item.
|
||||
|
||||
### Product board
|
||||
|
||||
Similarly to 'Product model' this field encodes logic board model number. This code is often referred to as `EEE code` in part catalogues and is useful for purchasing a compatible logic board for replacement.
|
||||
|
||||
For new 17 character MLBs this field is also used for identification at `osrecovery.apple.com` to provide a compatible internet recovery image and diagnostic tools upon request.
|
||||
|
||||
### Suffix
|
||||
|
||||
Base 34 value with unclear designation. Might be used for checksum validation. Checksum validation algorithm is reverse engineered from diagnostics tools and is valid for all 17 character MLBs. It is not clear whether 13 character MLBs have any checksum. 17 character MLB checksum follows.
|
||||
|
||||
```C
|
||||
static bool verify_mlb_checksum(const char *mlb, size_t len) {
|
||||
const char alphabet[] = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
||||
size_t checksum = 0;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
for (size_t j = 0; j <= sizeof (alphabet); ++j) {
|
||||
if (j == sizeof (alphabet))
|
||||
return false;
|
||||
if (mlb[i] == alphabet[j]) {
|
||||
checksum += (((i & 1) == (len & 1)) * 2 + 1) * j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return checksum % (sizeof(alphabet) - 1) == 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Appendix
|
||||
|
||||
This information was obtained experimentally and may not be accurate in certain details. Be warned that it is published at no warranty for educational and introductory purposes only.
|
||||
7
hackintosh-guide/Utilities/macserial/README.md
Executable file
7
hackintosh-guide/Utilities/macserial/README.md
Executable file
@@ -0,0 +1,7 @@
|
||||
## macserial
|
||||
|
||||
macserial is a tool that obtains and decodes Mac serial number and board identifier to provide more information about the production of your hardware. Works as a decent companion to [Apple Check Coverage](https://checkcoverage.apple.com) and [Apple Specs](http://support-sp.apple.com/sp/index?page=cpuspec&cc=HTD5) portal. Check the [format description](https://github.com/acidanthera/OpenCorePkg/blob/master/Utilities/macserial/FORMAT.md) for more details.
|
||||
|
||||
Should be built with a compiler supporting C99. Prebuilt binaries are available for macOS 10.4 and higher.
|
||||
|
||||
Run with `-h` argument to see all available arguments.
|
||||
BIN
hackintosh-guide/Utilities/macserial/macserial
Executable file
BIN
hackintosh-guide/Utilities/macserial/macserial
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/macserial/macserial.exe
Executable file
BIN
hackintosh-guide/Utilities/macserial/macserial.exe
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/macserial/macserial.linux
Executable file
BIN
hackintosh-guide/Utilities/macserial/macserial.linux
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/ocpasswordgen/ocpasswordgen
Executable file
BIN
hackintosh-guide/Utilities/ocpasswordgen/ocpasswordgen
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/ocpasswordgen/ocpasswordgen.exe
Executable file
BIN
hackintosh-guide/Utilities/ocpasswordgen/ocpasswordgen.exe
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/ocpasswordgen/ocpasswordgen.linux
Executable file
BIN
hackintosh-guide/Utilities/ocpasswordgen/ocpasswordgen.linux
Executable file
Binary file not shown.
140
hackintosh-guide/Utilities/ocvalidate/README.md
Executable file
140
hackintosh-guide/Utilities/ocvalidate/README.md
Executable file
@@ -0,0 +1,140 @@
|
||||
ocvalidate
|
||||
====================
|
||||
|
||||
Utility to validate whether a `config.plist` matches requirements and conventions imposed by OpenCore.
|
||||
|
||||
## Usage
|
||||
- Pass one single path to `config.plist` to verify it.
|
||||
- Pass `--version` for current supported OpenCore version.
|
||||
|
||||
## Technical background
|
||||
### At a glance
|
||||
- ocvalidate firstly calls `OcSerializeLib` which performs fundamental checks in terms of syntax and semantics. After that, the following will be checked.
|
||||
- The error message `<OCS: No schema for xxx>` complained by `OcSerializeLib` indicates unknown keys that can be deprecated in new versions of OpenCore. Such keys should be ***removed*** in order to avoid undefined behaviours.
|
||||
- Under active development, newer versions of OpenCore hardly have backward compatibility at this moment. As a result, please first run `ocvalidate --version` to check which version of OpenCore is supported, and thus please only use the specific version.
|
||||
|
||||
### Global Rules
|
||||
- All entries must be set once only. Duplication is strictly prohibited.
|
||||
- All strings (fields with plist `String` format) throughout the whole config only accept ASCII printable characters at most. Stricter rules may apply. For instance, some fields only accept specified values, as indicated in [Configuration.pdf](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf).
|
||||
- All the paths relative to OpenCore root must be less than or equal to 192 bytes (`OC_STORAGE_SAFE_PATH_MAX`) in total including '\0' terminator.
|
||||
- Most binary patches must have `Find`, `Replace`, `Mask` (if used), and `ReplaceMask` (if used) identical size set. Also, `Find` requires `Mask` (or `Replace` requires `ReplaceMask`) to be active (set to non-zero) for corresponding bits.
|
||||
- `MinKernel` and `MaxKernel` entries should follow conventions specified in [Configuration.pdf](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf). (TODO: Bring decent checks for this)
|
||||
- `MinKernel` cannot be a value that is below macOS 10.4 (Darwin version 8).
|
||||
- Entries taking file system path only accept `0-9, A-Z, a-z, '_', '-', '.', '/', and '\'`.
|
||||
- Device Paths (e.g. `PciRoot(0x0)/Pci(0x1b,0x0)`) only accept strings in canonic string format.
|
||||
- Paths of UEFI Drivers only accept `0-9, A-Z, a-z, '_', '-', '.', and '/'`.
|
||||
- Entries requiring bitwise operations (e.g. `ConsoleAttributes`, `PickerAttributes`, or `ScanPolicy`) only allow known bits stated in [Configuration.pdf](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf) to be set.
|
||||
- Entries involving GUID (mainly in Section `NVRAM`) must have correct format set.
|
||||
|
||||
### ACPI
|
||||
#### Add
|
||||
- Entry[N]->Path: Only `.aml` and `.bin` filename suffix are accepted.
|
||||
|
||||
### Booter
|
||||
#### MmioWhitelist
|
||||
- Entry[N]->Enabled: When at least one entry is enabled, `DevirtualiseMmio` in `Booter->Quirks` should be enabled.
|
||||
#### Patch
|
||||
- Entry[N]->Arch: Only `Any`, `i386`, or `x86_64` are accepted.
|
||||
- Entry[N]->Identifier: Only `Any`, `Apple`, or a specified bootloader with `.efi` sufffix, are accepted.
|
||||
#### Quirks
|
||||
- When `AllowRelocationBlock` is enabled, `ProvideCustomSlide` should be enabled altogether.
|
||||
- When `EnableSafeModeSlide` is enabled, `ProvideCustomSlide` should be enabled altogether.
|
||||
- If `ProvideMaxSlide` is set to a number greater than zero, `ProvideCustomSlide` should be enabled altogether.
|
||||
- `ResizeAppleGpuBars` must be set to `0` or `-1`.
|
||||
- When `DisableVariableWrite`, `EnableWriteUnprotector`, or `ProvideCustomSlide` is enabled, `OpenRuntime.efi` should be loaded in `UEFI->Drivers`.
|
||||
|
||||
### DeviceProperties
|
||||
- Requirements here all follow Global Rules.
|
||||
|
||||
### Kernel
|
||||
#### Add
|
||||
- Entry[N]->Arch: Only `Any`, `i386`, or `x86_64` are accepted.
|
||||
- Entry[N]->BundlePath: Filename should have `.kext` suffix.
|
||||
- Entry[N]->PlistPath: Filename should have `.plist` suffix.
|
||||
- Entry[N]: If `Lilu.kext` is used, `DisableLinkeditJettison` should be enabled in `Kernel->Quirks`.
|
||||
- `BrcmFirmwareRepo.kext` must not be injected by OpenCore.
|
||||
- For some known kexts, their `BundlePath`, `ExecutablePath`, and `PlistPath` must match against each other. Current list of rules can be found [here](https://github.com/acidanthera/OpenCorePkg/blob/master/Utilities/ocvalidate/KextInfo.c).
|
||||
- Plugin kext must be placed after parent kext. For example, [plugins of Lilu](https://github.com/acidanthera/Lilu/blob/master/KnownPlugins.md) must be placed after `Lilu.kext`.
|
||||
#### Delete
|
||||
- Entry[N]->Arch: Only `Any`, `i386`, or `x86_64` are accepted.
|
||||
- Entry[N]->Identifier: At least one dot (`.`) should exist, because any identifier looks like a domain sequence (`vendor.product`).
|
||||
#### Quirks
|
||||
- `CustomSMBIOSGuid` requires `PlatformInfo->UpdateSMBIOSMode` set to `Custom`.
|
||||
- `SetApfsTrimTimeout` cannot be a value that is greater than `MAX_UINT32`, or less than `-1`.
|
||||
#### Scheme
|
||||
- KernelArch: Only `Auto`, `i386`, `i386-user32`, or `x86_64` are accepted.
|
||||
- KernelCache: Only `Auto`, `Cacheless`, `Mkext`, or `Prelinked` are accepted.
|
||||
|
||||
### Misc
|
||||
#### BlessOverride
|
||||
- Entries cannot be `\EFI\Microsoft\Boot\bootmgfw.efi` or `\System\Library\CoreServices\boot.efi` since OpenCore knows these paths.
|
||||
#### Boot
|
||||
- HibernateMode: Only `None`, `Auto`, `RTC`, or `NVRAM` are accepted.
|
||||
- PickerMode: Only `Builtin`, `External`, or `Apple` are accepted.
|
||||
- `PickerAudioAssist` requires `AudioSupport` in `UEFI->Audio` to be enabled.
|
||||
- LauncherOption: Only `Disabled`, `Full`, `Short`, or `System` are accepted.
|
||||
- `LauncherPath` cannot be empty string.
|
||||
#### Security
|
||||
- AuthRestart: If enabled, `VirtualSMC.kext` should be present in `Kernel->Add`.
|
||||
- DmgLoading: Only `Disabled`, `Signed`, or `Any` are accepted.
|
||||
- Vault: Only `Optional`, `Basic`, or `Secure` are accepted.
|
||||
- SecureBootModel: Only `Default`, `Disabled`, `j137`, `j680`, `j132`, `j174`, `j140k`, `j780`, `j213`, `j140a`, `j152f`, `j160`, `j230k`, `j214k`, `j223`, `j215`, `j185`, `j185f`, or `x86legacy` are accepted.
|
||||
#### Serial
|
||||
- RegisterAccessWidth: Only `8` or `32` are accepted.
|
||||
- BaudRate: Only `921600`, `460800`, `230400`, `115200`, `57600`, `38400`, `19200`, `9600`, `7200`, `4800`, `3600`, `2400`, `2000`, `1800`, `1200`, `600`, `300`, `150`, `134`, `110`, `75`, or `50` are accepted.
|
||||
- PciDeviceInfo: The last byte must be `0xFF`.
|
||||
- PciDeviceInfo: Excluding the last byte `0xFF`, the rest must be divisible by 4.
|
||||
- PciDeviceInfo: Maximum allowed size is 41.
|
||||
|
||||
### NVRAM
|
||||
- Requirements here all follow Global Rules. In addition, the following keys and values are checked:
|
||||
#### gAppleBootVariableGuid (`7C436110-AB2A-4BBB-A880-FE41995C9F82`)
|
||||
- `nvda_drv` must have type `Plist Data` with the value of `0x30` or `0x31`.
|
||||
- `boot-args` must be an ASCII string (thus `Plist String`) without trailing `\0`.
|
||||
- `bootercfg` must be an ASCII string (thus `Plist String`) without trailing `\0`.
|
||||
- `csr-active-config` must have type `Plist Data` and have length of 4 bytes.
|
||||
- `StartupMute` must have type `Plist Data` and have length of 1 byte.
|
||||
- `SystemAudioVolume` must have type `Plist Data` and have length of 1 byte.
|
||||
#### gAppleVendorVariableGuid (`4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14`)
|
||||
- `UIScale` must have type `Plist Data` with the value of `0x01` or `0x02`.
|
||||
- `FirmwareFeatures` must have type `Plist Data` and have length of 4 bytes.
|
||||
- `ExtendedFirmwareFeatures` must have type `Plist Data` and have length of 8 bytes.
|
||||
- `FirmwareFeaturesMask` must have type `Plist Data` and have length of 4 bytes.
|
||||
- `ExtendedFirmwareFeatures` must have type `Plist Data` and have length of 8 bytes.
|
||||
- `DefaultBackgroundColor` must have type `Plist Data` and have length of 4 bytes. Also, its last byte must be `0x00`.
|
||||
|
||||
### PlatformInfo
|
||||
- UpdateSMBIOSMode: Only `TryOverwrite`, `Create`, `Overwrite`, or `Custom` are accepted.
|
||||
#### Generic
|
||||
- SystemProductName: Only real Mac models are accepted.
|
||||
- SystemMemoryStatus: Only `Auto`, `Upgradable`, or `Soldered` are accepted.
|
||||
- SystemUUID: Only empty string, `OEM` or valid UUID are accepted.
|
||||
- ProcessorType: Only known first byte can be set.
|
||||
|
||||
### UEFI
|
||||
#### APFS
|
||||
- When `EnableJumpstart` is enabled, `ScanPolicy` in `Misc->Security` should have `OC_SCAN_ALLOW_FS_APFS` (bit 8) set, together with `OC_SCAN_FILE_SYSTEM_LOCK` (bit 0) set. Or `ScanPolicy` should be `0` (failsafe value).
|
||||
#### Audio
|
||||
- When `AudioSupport` is enabled, `AudioDevice` must be either empty or a valid path.
|
||||
- When `AudioSupport` is enabled, `AudioOutMask` must be non-zero.
|
||||
#### Quirks
|
||||
- When `RequestBootVarRouting` is enabled, `OpenRuntime.efi` should be loaded in `UEFI->Drivers`.
|
||||
- `ResizeGpuBars` must be set to an integer value between `-1` and `19`.
|
||||
#### Drivers
|
||||
- When `OpenUsbKbDxe.efi` is in use, `KeySupport` in `UEFI->Input` should never be enabled altogether.
|
||||
- When `Ps2KeyboardDxe.efi` is in use, `KeySupport` in `UEFI->Input` should always be enabled altogether.
|
||||
- `OpenUsbKbDxe.efi` and `Ps2KeyboardDxe.efi` should never co-exist.
|
||||
- When HFS+ filesystem driver or `AudioDxe.efi` is in use, `ConnectDrivers` should be enabled altogether.
|
||||
- When `OpenCanopy.efi` is in use, `PickerMode` in `Misc->Boot` should be set to `External`.
|
||||
- When `OpenVariableRuntimeDxe.efi` is in use, its `LoadEarly` option must be set to `TRUE`.
|
||||
- `OpenRuntime.efi` must be placed after `OpenVariableRuntimeDxe.efi` when both are in use.
|
||||
- `LoadEarly` for any other driver but `OpenVariableRuntimeDxe.efi` and `OpenRuntime.efi` must be set to `FALSE`.
|
||||
#### Input
|
||||
- KeySupportMode: Only `Auto`, `V1`, `V2`, or `AMI` are accepted.
|
||||
- When `PointerSupport` is enabled, the value of `PointerSupportMode` should only be `ASUS`.
|
||||
#### Output
|
||||
- `ClearScreenOnModeSwitch`, `IgnoreTextInGraphics`, `ReplaceTabWithSpace`, and `SanitiseClearScreen` only apply to `System` TextRenderer
|
||||
- `Resolution` should match `NUMBERxNUMBER` or `NUMBERxNUMBER@NUMBER` sequences (unless it is an `Empty string` or is set to `Max`).
|
||||
- `UIScale` must be set to an integer value between `-1` and `2`.
|
||||
#### ReservedMemory
|
||||
- Type: Only `Reserved`, `LoaderCode`, `LoaderData`, `BootServiceCode`, `BootServiceData`, `RuntimeCode`, `RuntimeData`, `Available`, `Persistent`, `UnusableMemory`, `ACPIReclaimMemory`, `ACPIMemoryNVS`, `MemoryMappedIO`, `MemoryMappedIOPortSpace`, or `PalCode` are accepted.
|
||||
BIN
hackintosh-guide/Utilities/ocvalidate/ocvalidate
Executable file
BIN
hackintosh-guide/Utilities/ocvalidate/ocvalidate
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/ocvalidate/ocvalidate.exe
Executable file
BIN
hackintosh-guide/Utilities/ocvalidate/ocvalidate.exe
Executable file
Binary file not shown.
BIN
hackintosh-guide/Utilities/ocvalidate/ocvalidate.linux
Executable file
BIN
hackintosh-guide/Utilities/ocvalidate/ocvalidate.linux
Executable file
Binary file not shown.
Reference in New Issue
Block a user