param()
$ErrorActionPreference = 'Stop'
$taskRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
$buildRoot = Join-Path $taskRoot 'build/CSharpBoot/BootShimBuild'
[void][IO.Directory]::CreateDirectory($buildRoot)
$toolchain = Get-Content -Raw -Encoding UTF8 -LiteralPath (Join-Path $PSScriptRoot 'BflatToolchain.json') | ConvertFrom-Json
$toolRoot = Join-Path $taskRoot ('.tools/' + $toolchain.Directory)
$compiler = Join-Path $toolRoot 'bflat.exe'
if (!(Test-Path -LiteralPath $compiler)) {
& (Join-Path $PSScriptRoot 'GetBflat.ps1')
if (!(Test-Path -LiteralPath $compiler)) { throw 'The pinned C# UEFI compiler is unavailable.' }
}
foreach ($entry in $toolchain.ToolSha256.PSObject.Properties) {
$path = Join-Path $toolRoot $entry.Name
if (!(Test-Path -LiteralPath $path) -or (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() -cne $entry.Value) {
throw ('The pinned bflat toolchain differs: ' + $entry.Name)
}
}
$buildId = 'CSharp-' + [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() + '-' + [Guid]::NewGuid().ToString('N').Substring(0, 8)
$outputRoot = Join-Path $buildRoot $buildId
[void][IO.Directory]::CreateDirectory($outputRoot)
$artifact = Join-Path $outputRoot 'XfeBootX64.efi'
$arguments = @('build', 'Source/XFESystem.Boot/DisplayContracts.cs', 'Source/XFESystem.Boot/EfiEntry.cs',
'--os:uefi', '--arch:x64', '--stdlib:zero', '--deterministic', '--no-debug-info', '-Ot', '-x',
('-o:' + $artifact), '--ldflags:/entry:XfeEfiMain')
$previousLib = [Environment]::GetEnvironmentVariable('BFLAT_LIB', 'Process')
$previousLd = [Environment]::GetEnvironmentVariable('BFLAT_LD', 'Process')
try {
Remove-Item Env:BFLAT_LIB -ErrorAction SilentlyContinue
$env:BFLAT_LD = Join-Path $toolRoot 'bin/lld.exe'
Push-Location $taskRoot
try {
$savedErrorAction = $ErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
& $compiler @arguments 2>&1 | Tee-Object -FilePath (Join-Path $outputRoot 'Build.log')
$buildExitCode = $LASTEXITCODE
} finally { $ErrorActionPreference = $savedErrorAction }
if ($buildExitCode -ne 0) { throw ('The C# EFI display initializer build failed with exit code ' + $buildExitCode) }
} finally { Pop-Location }
} finally {
[Environment]::SetEnvironmentVariable('BFLAT_LIB', $previousLib, 'Process')
[Environment]::SetEnvironmentVariable('BFLAT_LD', $previousLd, 'Process')
}
$binary = [IO.File]::ReadAllBytes($artifact)
if ($binary.Length -lt 256 -or $binary[0] -ne 0x4D -or $binary[1] -ne 0x5A) { throw 'The C# output is not a PE image.' }
$pe = [BitConverter]::ToInt32($binary, 60)
if ($pe -lt 64 -or $pe + 264 -gt $binary.Length -or [BitConverter]::ToUInt32($binary, $pe) -ne 0x4550 -or
[BitConverter]::ToUInt16($binary, $pe + 4) -ne 0x8664 -or [BitConverter]::ToUInt16($binary, $pe + 24) -ne 0x20B -or
[BitConverter]::ToUInt16($binary, $pe + 24 + 68) -ne 10) { throw 'Expected a native AMD64 PE32+ EFI application.' }
if ([BitConverter]::ToUInt32($binary, $pe + 24 + 120) -ne 0 -or [BitConverter]::ToUInt32($binary, $pe + 24 + 124) -ne 0) {
throw 'The EFI application unexpectedly imports host libraries.'
}
if ([BitConverter]::ToUInt32($binary, $pe + 24 + 224) -ne 0) { throw 'The EFI output unexpectedly contains a managed CLR header.' }
$entryPoint = [BitConverter]::ToUInt32($binary, $pe + 24 + 16)
$sections = [BitConverter]::ToUInt16($binary, $pe + 6)
$sectionStart = $pe + 24 + [BitConverter]::ToUInt16($binary, $pe + 20)
$executableEntry = $false
for ($index = 0; $index -lt $sections; $index++) {
$section = $sectionStart + $index * 40
if ($section + 40 -gt $binary.Length) { throw 'Invalid EFI section table.' }
$virtualSize = [BitConverter]::ToUInt32($binary, $section + 8)
$virtualAddress = [BitConverter]::ToUInt32($binary, $section + 12)
$flags = [BitConverter]::ToUInt32($binary, $section + 36)
if ($entryPoint -ge $virtualAddress -and $entryPoint -lt [uint64]$virtualAddress + $virtualSize -and ($flags -band 0x20000000)) { $executableEntry = $true }
}
if (!$executableEntry) { throw 'The EFI entry point does not address executable native code.' }
$sources = [ordered]@{}
foreach ($relative in @('Source/XFESystem.Boot/XFESystem.Boot.csproj', 'Source/XFESystem.Boot/DisplayContracts.cs',
'Source/XFESystem.Boot/EfiEntry.cs', 'Tools/CSharpBoot/BuildBootShim.ps1', 'Tools/CSharpBoot/GetBflat.ps1',
'Tools/CSharpBoot/BflatToolchain.json', 'Tools/CSharpBoot/Licenses/Bflat-8.0.2.txt')) {
$sources[$relative] = (Get-FileHash -LiteralPath (Join-Path $taskRoot $relative) -Algorithm SHA256).Hash.ToLowerInvariant()
}
$receipt = [ordered]@{
Kind = 'XfeEfiDisplayInitializerBuild'; Language = 'C# UEFI'; KernelLanguage = 'C# NativeAOT'
Toolchain = 'bflat 8.0.2'; Target = 'x86_64-uefi'; StandardLibrary = 'Zero (official MIT binary distribution)'
ArtifactPath = $artifact; ArtifactSha256 = (Get-FileHash -LiteralPath $artifact -Algorithm SHA256).Hash.ToLowerInvariant()
CompilerArguments = $arguments; ToolSha256 = $toolchain.ToolSha256; CompilerArchiveSha256 = $toolchain.ArchiveSha256
SourceSha256 = $sources; ChainloadPath = 'EFI/XFE/Limine.efi'
GopPolicy = 'Maximum EDID and GOP intersection; firmware maximum when EDID is unavailable'
EntryPoint = 'XfeEfiMain'; NativePe32Plus = $true; EfiSubsystem = 10; HostImports = $false
DisplayPolicyDoesNotImplementVSync = $true
}
$receiptPath = Join-Path $outputRoot 'BootShimReceipt.json'
$receipt | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $receiptPath -Encoding UTF8
$activeReceipt = Join-Path $buildRoot 'BootShimReceipt.json'
if (Test-Path -LiteralPath $activeReceipt) {
Copy-Item -LiteralPath $activeReceipt -Destination (Join-Path $outputRoot 'PreviousActiveReceipt.json')
}
Copy-Item -LiteralPath $receiptPath -Destination $activeReceipt -Force
Write-Host ('Built C# EFI display initializer: ' + $receiptPath)
param()
$ErrorActionPreference = 'Stop'
$taskRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
$buildRoot = Join-Path $taskRoot 'build/CSharpBoot/BootShimBuild'
[void][IO.Directory]::CreateDirectory($buildRoot)
$toolchain = Get-Content -Raw -Encoding UTF8 -LiteralPath (Join-Path $PSScriptRoot 'BflatToolchain.json') | ConvertFrom-Json
$toolRoot = Join-Path $taskRoot ('.tools/' + $toolchain.Directory)
$compiler = Join-Path $toolRoot 'bflat.exe'
if (!(Test-Path -LiteralPath $compiler)) {
& (Join-Path $PSScriptRoot 'GetBflat.ps1')
if (!(Test-Path -LiteralPath $compiler)) { throw 'The pinned C# UEFI compiler is unavailable.' }
}
foreach ($entry in $toolchain.ToolSha256.PSObject.Properties) {
$path = Join-Path $toolRoot $entry.Name
if (!(Test-Path -LiteralPath $path) -or (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() -cne $entry.Value) {
throw ('The pinned bflat toolchain differs: ' + $entry.Name)
}
}
$buildId = 'CSharp-' + [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() + '-' + [Guid]::NewGuid().ToString('N').Substring(0, 8)
$outputRoot = Join-Path $buildRoot $buildId
[void][IO.Directory]::CreateDirectory($outputRoot)
$artifact = Join-Path $outputRoot 'XfeBootX64.efi'
$arguments = @('build', 'Source/XFESystem.Boot/DisplayContracts.cs', 'Source/XFESystem.Boot/EfiEntry.cs',
'--os:uefi', '--arch:x64', '--stdlib:zero', '--deterministic', '--no-debug-info', '-Ot', '-x',
('-o:' + $artifact), '--ldflags:/entry:XfeEfiMain')
$previousLib = [Environment]::GetEnvironmentVariable('BFLAT_LIB', 'Process')
$previousLd = [Environment]::GetEnvironmentVariable('BFLAT_LD', 'Process')
try {
Remove-Item Env:BFLAT_LIB -ErrorAction SilentlyContinue
$env:BFLAT_LD = Join-Path $toolRoot 'bin/lld.exe'
Push-Location $taskRoot
try {
$savedErrorAction = $ErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
& $compiler @arguments 2>&1 | Tee-Object -FilePath (Join-Path $outputRoot 'Build.log')
$buildExitCode = $LASTEXITCODE
} finally { $ErrorActionPreference = $savedErrorAction }
if ($buildExitCode -ne 0) { throw ('The C# EFI display initializer build failed with exit code ' + $buildExitCode) }
} finally { Pop-Location }
} finally {
[Environment]::SetEnvironmentVariable('BFLAT_LIB', $previousLib, 'Process')
[Environment]::SetEnvironmentVariable('BFLAT_LD', $previousLd, 'Process')
}
$binary = [IO.File]::ReadAllBytes($artifact)
if ($binary.Length -lt 256 -or $binary[0] -ne 0x4D -or $binary[1] -ne 0x5A) { throw 'The C# output is not a PE image.' }
$pe = [BitConverter]::ToInt32($binary, 60)
if ($pe -lt 64 -or $pe + 264 -gt $binary.Length -or [BitConverter]::ToUInt32($binary, $pe) -ne 0x4550 -or
[BitConverter]::ToUInt16($binary, $pe + 4) -ne 0x8664 -or [BitConverter]::ToUInt16($binary, $pe + 24) -ne 0x20B -or
[BitConverter]::ToUInt16($binary, $pe + 24 + 68) -ne 10) { throw 'Expected a native AMD64 PE32+ EFI application.' }
if ([BitConverter]::ToUInt32($binary, $pe + 24 + 120) -ne 0 -or [BitConverter]::ToUInt32($binary, $pe + 24 + 124) -ne 0) {
throw 'The EFI application unexpectedly imports host libraries.'
}
if ([BitConverter]::ToUInt32($binary, $pe + 24 + 224) -ne 0) { throw 'The EFI output unexpectedly contains a managed CLR header.' }
$entryPoint = [BitConverter]::ToUInt32($binary, $pe + 24 + 16)
$sections = [BitConverter]::ToUInt16($binary, $pe + 6)
$sectionStart = $pe + 24 + [BitConverter]::ToUInt16($binary, $pe + 20)
$executableEntry = $false
for ($index = 0; $index -lt $sections; $index++) {
$section = $sectionStart + $index * 40
if ($section + 40 -gt $binary.Length) { throw 'Invalid EFI section table.' }
$virtualSize = [BitConverter]::ToUInt32($binary, $section + 8)
$virtualAddress = [BitConverter]::ToUInt32($binary, $section + 12)
$flags = [BitConverter]::ToUInt32($binary, $section + 36)
if ($entryPoint -ge $virtualAddress -and $entryPoint -lt [uint64]$virtualAddress + $virtualSize -and ($flags -band 0x20000000)) { $executableEntry = $true }
}
if (!$executableEntry) { throw 'The EFI entry point does not address executable native code.' }
$sources = [ordered]@{}
foreach ($relative in @('Source/XFESystem.Boot/XFESystem.Boot.csproj', 'Source/XFESystem.Boot/DisplayContracts.cs',
'Source/XFESystem.Boot/EfiEntry.cs', 'Tools/CSharpBoot/BuildBootShim.ps1', 'Tools/CSharpBoot/GetBflat.ps1',
'Tools/CSharpBoot/BflatToolchain.json', 'Tools/CSharpBoot/Licenses/Bflat-8.0.2.txt')) {
$sources[$relative] = (Get-FileHash -LiteralPath (Join-Path $taskRoot $relative) -Algorithm SHA256).Hash.ToLowerInvariant()
}
$receipt = [ordered]@{
Kind = 'XfeEfiDisplayInitializerBuild'; Language = 'C# UEFI'; KernelLanguage = 'C# NativeAOT'
Toolchain = 'bflat 8.0.2'; Target = 'x86_64-uefi'; StandardLibrary = 'Zero (official MIT binary distribution)'
ArtifactPath = $artifact; ArtifactSha256 = (Get-FileHash -LiteralPath $artifact -Algorithm SHA256).Hash.ToLowerInvariant()
CompilerArguments = $arguments; ToolSha256 = $toolchain.ToolSha256; CompilerArchiveSha256 = $toolchain.ArchiveSha256
SourceSha256 = $sources; ChainloadPath = 'EFI/XFE/Limine.efi'
GopPolicy = 'Maximum EDID and GOP intersection; firmware maximum when EDID is unavailable'
EntryPoint = 'XfeEfiMain'; NativePe32Plus = $true; EfiSubsystem = 10; HostImports = $false
DisplayPolicyDoesNotImplementVSync = $true
}
$receiptPath = Join-Path $outputRoot 'BootShimReceipt.json'
$receipt | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $receiptPath -Encoding UTF8
$activeReceipt = Join-Path $buildRoot 'BootShimReceipt.json'
if (Test-Path -LiteralPath $activeReceipt) {
Copy-Item -LiteralPath $activeReceipt -Destination (Join-Path $outputRoot 'PreviousActiveReceipt.json')
}
Copy-Item -LiteralPath $receiptPath -Destination $activeReceipt -Force
Write-Host ('Built C# EFI display initializer: ' + $receiptPath)