581 lines
21 KiB
PowerShell
581 lines
21 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|||
|
|
|
||
|
|
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||
|
|
$UtsmDir = Join-Path $Root 'CODE\UTSM'
|
||
|
|
$DskDir = Join-Path $Root 'CODE\dsk'
|
||
|
|
$SystemDir = Join-Path $Root 'SYSTEM'
|
||
|
|
$IsoDir = Join-Path $Root 'ISO'
|
||
|
|
$Output = Join-Path $SystemDir 'boot\utsm.elf'
|
||
|
|
$DskOutput = Join-Path $SystemDir 'system\deshab64\deshab.elf'
|
||
|
|
$ImagePath = Join-Path $IsoDir 'deshab.img'
|
||
|
|
$BuildTmp = Join-Path $Root '.build_tmp'
|
||
|
|
|
||
|
|
function Find-Tool($Name, $ExtraNames = @()) {
|
||
|
|
$names = @($Name) + $ExtraNames
|
||
|
|
foreach ($n in $names) {
|
||
|
|
$cmd = Get-Command $n -ErrorAction SilentlyContinue
|
||
|
|
if ($cmd) { return $cmd.Source }
|
||
|
|
}
|
||
|
|
|
||
|
|
$dirs = @(
|
||
|
|
'C:\Program Files\LLVM\bin',
|
||
|
|
'C:\Program Files (x86)\LLVM\bin',
|
||
|
|
'C:\LLVM\bin',
|
||
|
|
'D:\LLVM\bin',
|
||
|
|
'D:\Program Files\LLVM\bin',
|
||
|
|
'D:\Code\LLVM\bin',
|
||
|
|
'C:\msys64\clang64\bin',
|
||
|
|
'C:\msys64\mingw64\bin',
|
||
|
|
'C:\mingw64\bin'
|
||
|
|
)
|
||
|
|
|
||
|
|
foreach ($dir in $dirs) {
|
||
|
|
foreach ($n in $names) {
|
||
|
|
$candidate = Join-Path $dir $n
|
||
|
|
if (Test-Path $candidate) { return $candidate }
|
||
|
|
if (-not $n.EndsWith('.exe')) {
|
||
|
|
$candidateExe = Join-Path $dir "$n.exe"
|
||
|
|
if (Test-Path $candidateExe) { return $candidateExe }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $null
|
||
|
|
}
|
||
|
|
|
||
|
|
function Build-DkmDriver([string]$SourcePath, [string]$OutputPath) {
|
||
|
|
$obj = Join-Path $BuildTmp (([System.IO.Path]::GetFileNameWithoutExtension($SourcePath)) + '.o')
|
||
|
|
$outDir = Split-Path -Parent $OutputPath
|
||
|
|
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Force -Path $outDir | Out-Null }
|
||
|
|
& $clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -O2 -c $SourcePath -o $obj
|
||
|
|
if ($LASTEXITCODE -ne 0) { throw "failed to compile DKM driver: $SourcePath" }
|
||
|
|
& $lld -r -o $OutputPath $obj
|
||
|
|
if ($LASTEXITCODE -ne 0) { throw "failed to link DKM driver: $OutputPath" }
|
||
|
|
Write-Host "[build] DKM driver Output: $OutputPath"
|
||
|
|
}
|
||
|
|
|
||
|
|
function Set-Le16([byte[]]$Buffer, [int]$Offset, [int]$Value) {
|
||
|
|
$Buffer[$Offset] = [byte]($Value -band 0xff)
|
||
|
|
$Buffer[$Offset + 1] = [byte](($Value -shr 8) -band 0xff)
|
||
|
|
}
|
||
|
|
|
||
|
|
function Set-Le32([byte[]]$Buffer, [int]$Offset, [UInt64]$Value) {
|
||
|
|
$Buffer[$Offset] = [byte]($Value -band 0xff)
|
||
|
|
$Buffer[$Offset + 1] = [byte](($Value -shr 8) -band 0xff)
|
||
|
|
$Buffer[$Offset + 2] = [byte](($Value -shr 16) -band 0xff)
|
||
|
|
$Buffer[$Offset + 3] = [byte](($Value -shr 24) -band 0xff)
|
||
|
|
}
|
||
|
|
|
||
|
|
function Set-Le64([byte[]]$Buffer, [int]$Offset, [UInt64]$Value) {
|
||
|
|
for ($i = 0; $i -lt 8; $i++) {
|
||
|
|
$Buffer[$Offset + $i] = [byte](($Value -shr ($i * 8)) -band 0xff)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function Write-At($Stream, [Int64]$Offset, [byte[]]$Data) {
|
||
|
|
[void]$Stream.Seek($Offset, [System.IO.SeekOrigin]::Begin)
|
||
|
|
$Stream.Write($Data, 0, $Data.Length)
|
||
|
|
}
|
||
|
|
|
||
|
|
function Get-Crc32([byte[]]$Data) {
|
||
|
|
$crc = [uint32]4294967295
|
||
|
|
foreach ($b in $Data) {
|
||
|
|
$crc = $crc -bxor [uint32]$b
|
||
|
|
for ($i = 0; $i -lt 8; $i++) {
|
||
|
|
if (($crc -band 1) -ne 0) {
|
||
|
|
$crc = [uint32](($crc -shr 1) -bxor 0xedb88320)
|
||
|
|
} else {
|
||
|
|
$crc = [uint32]($crc -shr 1)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return [uint32]($crc -bxor [uint32]4294967295)
|
||
|
|
}
|
||
|
|
|
||
|
|
function Test-ShortFatName([string]$Name) {
|
||
|
|
if ($Name -match '[a-z]') { return $false }
|
||
|
|
$parts = $Name.Split('.')
|
||
|
|
if ($parts.Count -gt 2) { return $false }
|
||
|
|
if ($parts[0].Length -lt 1 -or $parts[0].Length -gt 8) { return $false }
|
||
|
|
if ($parts.Count -eq 2 -and $parts[1].Length -gt 3) { return $false }
|
||
|
|
return $Name -match '^[A-Z0-9_\$%''\-@~`!\(\)\{\}\^#&]+(\.[A-Z0-9_\$%''\-@~`!\(\)\{\}\^#&]+)?$'
|
||
|
|
}
|
||
|
|
|
||
|
|
function New-ShortFatName([string]$LongName, $Used) {
|
||
|
|
$upper = $LongName.ToUpperInvariant()
|
||
|
|
if ((Test-ShortFatName $upper) -and -not $Used.ContainsKey($upper)) {
|
||
|
|
$Used[$upper] = $true
|
||
|
|
$parts = $upper.Split('.')
|
||
|
|
$base = $parts[0].PadRight(8, ' ')
|
||
|
|
$ext = ''
|
||
|
|
if ($parts.Count -eq 2) { $ext = $parts[1] }
|
||
|
|
return ($base + $ext.PadRight(3, ' '))
|
||
|
|
}
|
||
|
|
|
||
|
|
$dot = $upper.LastIndexOf('.')
|
||
|
|
if ($dot -ge 0) {
|
||
|
|
$baseRaw = $upper.Substring(0, $dot)
|
||
|
|
$extRaw = $upper.Substring($dot + 1)
|
||
|
|
} else {
|
||
|
|
$baseRaw = $upper
|
||
|
|
$extRaw = ''
|
||
|
|
}
|
||
|
|
|
||
|
|
$baseClean = ($baseRaw -replace '[^A-Z0-9]', '')
|
||
|
|
$extClean = ($extRaw -replace '[^A-Z0-9]', '')
|
||
|
|
if ($baseClean.Length -eq 0) { $baseClean = 'FILE' }
|
||
|
|
if ($extClean.Length -gt 3) { $extClean = $extClean.Substring(0, 3) }
|
||
|
|
|
||
|
|
for ($i = 1; $i -lt 1000; $i++) {
|
||
|
|
$suffix = "~$i"
|
||
|
|
$take = [Math]::Min(8 - $suffix.Length, $baseClean.Length)
|
||
|
|
$candidateBase = $baseClean.Substring(0, $take) + $suffix
|
||
|
|
$display = $candidateBase
|
||
|
|
if ($extClean.Length -gt 0) { $display = "$candidateBase.$extClean" }
|
||
|
|
if (-not $Used.ContainsKey($display)) {
|
||
|
|
$Used[$display] = $true
|
||
|
|
return ($candidateBase.PadRight(8, ' ') + $extClean.PadRight(3, ' '))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
throw "Unable to generate FAT short name for $LongName"
|
||
|
|
}
|
||
|
|
|
||
|
|
function Get-ShortNameBytes([string]$ShortName) {
|
||
|
|
return [System.Text.Encoding]::ASCII.GetBytes($ShortName)
|
||
|
|
}
|
||
|
|
|
||
|
|
function Get-LfnChecksum([byte[]]$ShortBytes) {
|
||
|
|
$sum = 0
|
||
|
|
foreach ($b in $ShortBytes) {
|
||
|
|
$sum = (((($sum -band 1) * 0x80) + ($sum -shr 1) + $b) -band 0xff)
|
||
|
|
}
|
||
|
|
return [byte]$sum
|
||
|
|
}
|
||
|
|
|
||
|
|
function New-LfnEntry([string]$Chunk, [int]$Order, [byte]$Checksum) {
|
||
|
|
$entry = [byte[]]::new(32)
|
||
|
|
for ($i = 0; $i -lt 32; $i++) { $entry[$i] = 0xff }
|
||
|
|
$entry[0] = [byte]$Order
|
||
|
|
$entry[11] = 0x0f
|
||
|
|
$entry[12] = 0x00
|
||
|
|
$entry[13] = $Checksum
|
||
|
|
$entry[26] = 0x00
|
||
|
|
$entry[27] = 0x00
|
||
|
|
|
||
|
|
$positions = @(1,3,5,7,9,14,16,18,20,22,24,28,30)
|
||
|
|
$chars = $Chunk.ToCharArray()
|
||
|
|
for ($i = 0; $i -lt $positions.Count; $i++) {
|
||
|
|
$pos = $positions[$i]
|
||
|
|
if ($i -lt $chars.Length) {
|
||
|
|
$code = [int][char]$chars[$i]
|
||
|
|
$entry[$pos] = [byte]($code -band 0xff)
|
||
|
|
$entry[$pos + 1] = [byte](($code -shr 8) -band 0xff)
|
||
|
|
} elseif ($i -eq $chars.Length) {
|
||
|
|
$entry[$pos] = 0x00
|
||
|
|
$entry[$pos + 1] = 0x00
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return $entry
|
||
|
|
}
|
||
|
|
|
||
|
|
function New-ShortEntry([string]$ShortName, [byte]$Attr, [int]$Cluster, [uint32]$Size) {
|
||
|
|
$entry = [byte[]]::new(32)
|
||
|
|
$nameBytes = Get-ShortNameBytes $ShortName
|
||
|
|
[Array]::Copy($nameBytes, 0, $entry, 0, 11)
|
||
|
|
$entry[11] = $Attr
|
||
|
|
Set-Le16 $entry 20 (($Cluster -shr 16) -band 0xffff)
|
||
|
|
Set-Le16 $entry 26 ($Cluster -band 0xffff)
|
||
|
|
Set-Le32 $entry 28 $Size
|
||
|
|
return $entry
|
||
|
|
}
|
||
|
|
|
||
|
|
function Add-DirectoryEntry($Entries, [string]$Name, [byte]$Attr, [int]$Cluster, [uint32]$Size, $UsedNames) {
|
||
|
|
$shortName = New-ShortFatName $Name $UsedNames
|
||
|
|
$shortBytes = Get-ShortNameBytes $shortName
|
||
|
|
$upper = $Name.ToUpperInvariant()
|
||
|
|
$needsLfn = -not (Test-ShortFatName $upper) -or (($shortName.Trim() -replace ' +', '') -ne ($upper -replace '\.', ''))
|
||
|
|
|
||
|
|
if ($needsLfn) {
|
||
|
|
$checksum = Get-LfnChecksum $shortBytes
|
||
|
|
$chunks = @()
|
||
|
|
for ($i = 0; $i -lt $Name.Length; $i += 13) {
|
||
|
|
$len = [Math]::Min(13, $Name.Length - $i)
|
||
|
|
$chunks += $Name.Substring($i, $len)
|
||
|
|
}
|
||
|
|
for ($i = $chunks.Count; $i -ge 1; $i--) {
|
||
|
|
$order = $i
|
||
|
|
if ($i -eq $chunks.Count) { $order = $order -bor 0x40 }
|
||
|
|
[void]$Entries.Add((New-LfnEntry $chunks[$i - 1] $order $checksum))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[void]$Entries.Add((New-ShortEntry $shortName $Attr $Cluster $Size))
|
||
|
|
}
|
||
|
|
|
||
|
|
function Get-GuidBytes([string]$GuidString) {
|
||
|
|
return ([Guid]$GuidString).ToByteArray()
|
||
|
|
}
|
||
|
|
|
||
|
|
function New-GptHeader([UInt64]$CurrentLba, [UInt64]$BackupLba, [UInt64]$FirstUsable, [UInt64]$LastUsable, [byte[]]$DiskGuid, [UInt64]$EntriesLba, [uint32]$EntryCount, [uint32]$EntrySize, [uint32]$EntriesCrc) {
|
||
|
|
$header = [byte[]]::new(512)
|
||
|
|
[Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('EFI PART'), 0, $header, 0, 8)
|
||
|
|
Set-Le32 $header 8 0x00010000
|
||
|
|
Set-Le32 $header 12 92
|
||
|
|
Set-Le64 $header 24 $CurrentLba
|
||
|
|
Set-Le64 $header 32 $BackupLba
|
||
|
|
Set-Le64 $header 40 $FirstUsable
|
||
|
|
Set-Le64 $header 48 $LastUsable
|
||
|
|
[Array]::Copy($DiskGuid, 0, $header, 56, 16)
|
||
|
|
Set-Le64 $header 72 $EntriesLba
|
||
|
|
Set-Le32 $header 80 $EntryCount
|
||
|
|
Set-Le32 $header 84 $EntrySize
|
||
|
|
Set-Le32 $header 88 $EntriesCrc
|
||
|
|
|
||
|
|
$crcData = [byte[]]::new(92)
|
||
|
|
[Array]::Copy($header, 0, $crcData, 0, 92)
|
||
|
|
Set-Le32 $crcData 16 0
|
||
|
|
$crc = Get-Crc32 $crcData
|
||
|
|
Set-Le32 $header 16 $crc
|
||
|
|
return $header
|
||
|
|
}
|
||
|
|
|
||
|
|
function New-GptFat32Image([string]$SourceDir, [string]$ImagePath, [int]$SizeMB = 128) {
|
||
|
|
$bytesPerSector = 512
|
||
|
|
$totalSectors = [UInt64](($SizeMB * 1024 * 1024) / $bytesPerSector)
|
||
|
|
$lastLba = $totalSectors - 1
|
||
|
|
$entryCount = [uint32]128
|
||
|
|
$entrySize = [uint32]128
|
||
|
|
$entrySectors = [UInt64](($entryCount * $entrySize) / $bytesPerSector)
|
||
|
|
$firstUsable = [UInt64](2 + $entrySectors)
|
||
|
|
$lastUsable = [UInt64]($lastLba - $entrySectors - 1)
|
||
|
|
$partStart = [UInt64]2048
|
||
|
|
$partEnd = $lastUsable
|
||
|
|
$partSectors = [UInt64]($partEnd - $partStart + 1)
|
||
|
|
$partitionOffset = [Int64]($partStart * $bytesPerSector)
|
||
|
|
|
||
|
|
$sectorsPerCluster = 1
|
||
|
|
$reservedSectors = 32
|
||
|
|
$numFats = 2
|
||
|
|
$fatSectors = 1
|
||
|
|
do {
|
||
|
|
$dataSectors = [Int64]$partSectors - $reservedSectors - ($numFats * $fatSectors)
|
||
|
|
$clusterCount = [int]($dataSectors / $sectorsPerCluster)
|
||
|
|
$neededFatSectors = [int]((($clusterCount + 2) * 4 + $bytesPerSector - 1) / $bytesPerSector)
|
||
|
|
if ($neededFatSectors -eq $fatSectors) { break }
|
||
|
|
$fatSectors = $neededFatSectors
|
||
|
|
} while ($true)
|
||
|
|
|
||
|
|
if ($clusterCount -lt 65525) {
|
||
|
|
throw "Invalid FAT32 cluster count: $clusterCount"
|
||
|
|
}
|
||
|
|
|
||
|
|
$fatOffset = [Int64]($partitionOffset + $reservedSectors * $bytesPerSector)
|
||
|
|
$dataOffset = [Int64]($partitionOffset + ($reservedSectors + $numFats * $fatSectors) * $bytesPerSector)
|
||
|
|
$clusterSize = $sectorsPerCluster * $bytesPerSector
|
||
|
|
$fat = [UInt32[]]::new($clusterCount + 2)
|
||
|
|
$fat[0] = 0x0ffffff8
|
||
|
|
$fat[1] = 0x0fffffff
|
||
|
|
$fat[2] = 0x0fffffff
|
||
|
|
$script:fatNextCluster = 3
|
||
|
|
|
||
|
|
function Allocate-Chain([Int64]$ByteCount) {
|
||
|
|
$needed = [Math]::Max(1, [int](($ByteCount + $clusterSize - 1) / $clusterSize))
|
||
|
|
$first = $script:fatNextCluster
|
||
|
|
for ($i = 0; $i -lt $needed; $i++) {
|
||
|
|
$cluster = $script:fatNextCluster
|
||
|
|
$script:fatNextCluster++
|
||
|
|
if ($script:fatNextCluster -ge $fat.Length) { throw 'FAT32 image is too small' }
|
||
|
|
if ($i -eq $needed - 1) {
|
||
|
|
$fat[$cluster] = 0x0fffffff
|
||
|
|
} else {
|
||
|
|
$fat[$cluster] = [UInt32]($cluster + 1)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return $first
|
||
|
|
}
|
||
|
|
|
||
|
|
function Get-ClusterOffset([int]$Cluster) {
|
||
|
|
return [Int64]($dataOffset + (($Cluster - 2) * $clusterSize))
|
||
|
|
}
|
||
|
|
|
||
|
|
function Write-FileContent($Stream, [string]$Path, [int]$FirstCluster) {
|
||
|
|
$bytes = [System.IO.File]::ReadAllBytes($Path)
|
||
|
|
$remaining = $bytes.Length
|
||
|
|
$srcOffset = 0
|
||
|
|
$cluster = $FirstCluster
|
||
|
|
while ($remaining -gt 0) {
|
||
|
|
$chunk = [Math]::Min($clusterSize, $remaining)
|
||
|
|
[void]$Stream.Seek((Get-ClusterOffset $cluster), [System.IO.SeekOrigin]::Begin)
|
||
|
|
$Stream.Write($bytes, $srcOffset, $chunk)
|
||
|
|
$srcOffset += $chunk
|
||
|
|
$remaining -= $chunk
|
||
|
|
if ($remaining -gt 0) { $cluster = $fat[$cluster] }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function Write-Directory($Stream, [string]$Path, [int]$SelfCluster, [int]$ParentCluster, [bool]$IsRoot) {
|
||
|
|
$entries = [System.Collections.ArrayList]::new()
|
||
|
|
$used = @{}
|
||
|
|
|
||
|
|
if (-not $IsRoot) {
|
||
|
|
[void]$entries.Add((New-ShortEntry '. ' 0x10 $SelfCluster 0))
|
||
|
|
[void]$entries.Add((New-ShortEntry '.. ' 0x10 $ParentCluster 0))
|
||
|
|
}
|
||
|
|
|
||
|
|
$items = Get-ChildItem -LiteralPath $Path -Force | Where-Object { $_.Name -notlike '*.tmp*' } | Sort-Object @{Expression={$_.PSIsContainer};Descending=$true}, Name
|
||
|
|
foreach ($item in $items) {
|
||
|
|
if ($item.PSIsContainer) {
|
||
|
|
$childCluster = Allocate-Chain $clusterSize
|
||
|
|
Add-DirectoryEntry $entries $item.Name 0x10 $childCluster 0 $used
|
||
|
|
Write-Directory $Stream $item.FullName $childCluster $SelfCluster $false
|
||
|
|
} else {
|
||
|
|
$fileSize = [uint32]$item.Length
|
||
|
|
$fileCluster = Allocate-Chain $item.Length
|
||
|
|
Add-DirectoryEntry $entries $item.Name 0x20 $fileCluster $fileSize $used
|
||
|
|
Write-FileContent $Stream $item.FullName $fileCluster
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$entryBytes = $entries.Count * 32
|
||
|
|
$neededBytes = [Math]::Max($clusterSize, [int](($entryBytes + $clusterSize - 1) / $clusterSize) * $clusterSize)
|
||
|
|
$dirBytes = [byte[]]::new($neededBytes)
|
||
|
|
$offset = 0
|
||
|
|
foreach ($entry in $entries) {
|
||
|
|
[Array]::Copy($entry, 0, $dirBytes, $offset, 32)
|
||
|
|
$offset += 32
|
||
|
|
}
|
||
|
|
Write-At $Stream (Get-ClusterOffset $SelfCluster) $dirBytes
|
||
|
|
}
|
||
|
|
|
||
|
|
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $ImagePath) | Out-Null
|
||
|
|
$fs = [System.IO.File]::Open($ImagePath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
|
||
|
|
try {
|
||
|
|
$fs.SetLength($SizeMB * 1024 * 1024)
|
||
|
|
|
||
|
|
$mbr = [byte[]]::new(512)
|
||
|
|
$mbr[446 + 4] = 0xee
|
||
|
|
Set-Le32 $mbr (446 + 8) 1
|
||
|
|
$protectiveSectors = [UInt64]($totalSectors - 1)
|
||
|
|
if ($protectiveSectors -gt [UInt64]4294967295) { $protectiveSectors = [UInt64]4294967295 }
|
||
|
|
Set-Le32 $mbr (446 + 12) $protectiveSectors
|
||
|
|
$mbr[510] = 0x55
|
||
|
|
$mbr[511] = 0xaa
|
||
|
|
Write-At $fs 0 $mbr
|
||
|
|
|
||
|
|
$partitionEntries = [byte[]]::new($entryCount * $entrySize)
|
||
|
|
$espType = Get-GuidBytes 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b'
|
||
|
|
$partGuid = ([Guid]::NewGuid()).ToByteArray()
|
||
|
|
[Array]::Copy($espType, 0, $partitionEntries, 0, 16)
|
||
|
|
[Array]::Copy($partGuid, 0, $partitionEntries, 16, 16)
|
||
|
|
Set-Le64 $partitionEntries 32 $partStart
|
||
|
|
Set-Le64 $partitionEntries 40 $partEnd
|
||
|
|
$nameBytes = [System.Text.Encoding]::Unicode.GetBytes('Deshab ESP')
|
||
|
|
[Array]::Copy($nameBytes, 0, $partitionEntries, 56, $nameBytes.Length)
|
||
|
|
$entriesCrc = Get-Crc32 $partitionEntries
|
||
|
|
$diskGuid = ([Guid]::NewGuid()).ToByteArray()
|
||
|
|
|
||
|
|
$primaryHeader = New-GptHeader 1 $lastLba $firstUsable $lastUsable $diskGuid 2 $entryCount $entrySize $entriesCrc
|
||
|
|
$backupEntriesLba = [UInt64]($lastLba - $entrySectors)
|
||
|
|
$backupHeader = New-GptHeader $lastLba 1 $firstUsable $lastUsable $diskGuid $backupEntriesLba $entryCount $entrySize $entriesCrc
|
||
|
|
|
||
|
|
Write-At $fs ($bytesPerSector * 1) $primaryHeader
|
||
|
|
Write-At $fs ($bytesPerSector * 2) $partitionEntries
|
||
|
|
Write-At $fs ([Int64]($backupEntriesLba * $bytesPerSector)) $partitionEntries
|
||
|
|
Write-At $fs ([Int64]($lastLba * $bytesPerSector)) $backupHeader
|
||
|
|
|
||
|
|
$boot = [byte[]]::new(512)
|
||
|
|
$boot[0] = 0xeb; $boot[1] = 0x58; $boot[2] = 0x90
|
||
|
|
[Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('DESHAB '), 0, $boot, 3, 8)
|
||
|
|
Set-Le16 $boot 11 $bytesPerSector
|
||
|
|
$boot[13] = [byte]$sectorsPerCluster
|
||
|
|
Set-Le16 $boot 14 $reservedSectors
|
||
|
|
$boot[16] = [byte]$numFats
|
||
|
|
Set-Le16 $boot 17 0
|
||
|
|
Set-Le16 $boot 19 0
|
||
|
|
$boot[21] = 0xf8
|
||
|
|
Set-Le16 $boot 22 0
|
||
|
|
Set-Le16 $boot 24 63
|
||
|
|
Set-Le16 $boot 26 255
|
||
|
|
Set-Le32 $boot 28 $partStart
|
||
|
|
Set-Le32 $boot 32 $partSectors
|
||
|
|
Set-Le32 $boot 36 $fatSectors
|
||
|
|
Set-Le16 $boot 40 0
|
||
|
|
Set-Le16 $boot 42 0
|
||
|
|
Set-Le32 $boot 44 2
|
||
|
|
Set-Le16 $boot 48 1
|
||
|
|
Set-Le16 $boot 50 6
|
||
|
|
$boot[64] = 0x80
|
||
|
|
$boot[66] = 0x29
|
||
|
|
Set-Le32 $boot 67 0x44534842
|
||
|
|
[Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('DESHAB '), 0, $boot, 71, 11)
|
||
|
|
[Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('FAT32 '), 0, $boot, 82, 8)
|
||
|
|
$boot[510] = 0x55; $boot[511] = 0xaa
|
||
|
|
Write-At $fs $partitionOffset $boot
|
||
|
|
Write-At $fs ($partitionOffset + 6 * $bytesPerSector) $boot
|
||
|
|
|
||
|
|
$fsinfo = [byte[]]::new(512)
|
||
|
|
Set-Le32 $fsinfo 0 0x41615252
|
||
|
|
Set-Le32 $fsinfo 484 0x61417272
|
||
|
|
Set-Le32 $fsinfo 488 4294967295
|
||
|
|
Set-Le32 $fsinfo 492 3
|
||
|
|
$fsinfo[508] = 0x00; $fsinfo[509] = 0x00; $fsinfo[510] = 0x55; $fsinfo[511] = 0xaa
|
||
|
|
Write-At $fs ($partitionOffset + $bytesPerSector) $fsinfo
|
||
|
|
Write-At $fs ($partitionOffset + 7 * $bytesPerSector) $fsinfo
|
||
|
|
|
||
|
|
Write-Directory $fs $SourceDir 2 2 $true
|
||
|
|
|
||
|
|
$fatBytes = [byte[]]::new($fatSectors * $bytesPerSector)
|
||
|
|
for ($i = 0; $i -lt $fat.Length; $i++) {
|
||
|
|
$off = $i * 4
|
||
|
|
if ($off + 3 -ge $fatBytes.Length) { break }
|
||
|
|
Set-Le32 $fatBytes $off $fat[$i]
|
||
|
|
}
|
||
|
|
Write-At $fs $fatOffset $fatBytes
|
||
|
|
Write-At $fs ($fatOffset + $fatSectors * $bytesPerSector) $fatBytes
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
$fs.Close()
|
||
|
|
}
|
||
|
|
|
||
|
|
Write-Host "[build] GPT + FAT32 ESP IMG: $ImagePath"
|
||
|
|
}
|
||
|
|
|
||
|
|
Write-Host '[build] Building UTSM kernel...'
|
||
|
|
|
||
|
|
$clang = Find-Tool 'clang'
|
||
|
|
$lld = Find-Tool 'ld.lld'
|
||
|
|
$make = Find-Tool 'make' @('mingw32-make')
|
||
|
|
|
||
|
|
if (-not $clang) {
|
||
|
|
throw 'clang was not found. Install LLVM/Clang or provide a freestanding x86_64 ELF toolchain.'
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-not $lld) {
|
||
|
|
throw 'ld.lld was not found. Install LLVM lld or provide a compatible linker.'
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-not $make) {
|
||
|
|
throw 'make was not found. Install make.'
|
||
|
|
}
|
||
|
|
|
||
|
|
Write-Host "[build] clang: $clang"
|
||
|
|
Write-Host "[build] ld.lld: $lld"
|
||
|
|
Write-Host "[build] make: $make"
|
||
|
|
|
||
|
|
New-Item -ItemType Directory -Force -Path $BuildTmp | Out-Null
|
||
|
|
$env:TMP = $BuildTmp
|
||
|
|
$env:TEMP = $BuildTmp
|
||
|
|
|
||
|
|
Push-Location $UtsmDir
|
||
|
|
try {
|
||
|
|
& $make -f MAKEFILE "CC=$clang" "LD=$lld"
|
||
|
|
if ($LASTEXITCODE -ne 0) {
|
||
|
|
throw "make UTSM failed with exit code $LASTEXITCODE"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
Pop-Location
|
||
|
|
}
|
||
|
|
|
||
|
|
Write-Host '[build] Building DSK main kernel...'
|
||
|
|
try {
|
||
|
|
& $make -C "$DskDir" -f MAKEFILE "CC=$clang" "LD=$lld"
|
||
|
|
if ($LASTEXITCODE -ne 0) {
|
||
|
|
throw "make DSK failed with exit code $LASTEXITCODE"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-not (Test-Path $Output)) {
|
||
|
|
throw "UTSM build did not produce $Output"
|
||
|
|
}
|
||
|
|
if (-not (Test-Path $DskOutput)) {
|
||
|
|
throw "DSK build did not produce $DskOutput"
|
||
|
|
}
|
||
|
|
|
||
|
|
$FirstInitDir = Join-Path $Root 'CODE\firstInit'
|
||
|
|
$FirstInitOutput = Join-Path $SystemDir 'system\user\use\FirstInit.elf'
|
||
|
|
|
||
|
|
Write-Host '[build] Building FirstInit.elf...'
|
||
|
|
try {
|
||
|
|
& $make -C "$FirstInitDir" -f MAKEFILE "CC=$clang" "LD=$lld"
|
||
|
|
if ($LASTEXITCODE -ne 0) {
|
||
|
|
throw "make FirstInit failed with exit code $LASTEXITCODE"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-not (Test-Path $FirstInitOutput)) {
|
||
|
|
throw "FirstInit build did not produce $FirstInitOutput"
|
||
|
|
}
|
||
|
|
Write-Host "[build] FirstInit Output: $FirstInitOutput"
|
||
|
|
|
||
|
|
$MouseDir = Join-Path $Root 'CODE\mouse'
|
||
|
|
$MouseOutput = Join-Path $SystemDir 'system\deshab64\mouse\mouseInit.elf'
|
||
|
|
|
||
|
|
Write-Host '[build] Building mouseInit.elf...'
|
||
|
|
try {
|
||
|
|
& $make -C "$MouseDir" -f MAKEFILE "CC=$clang" "LD=$lld"
|
||
|
|
if ($LASTEXITCODE -ne 0) {
|
||
|
|
throw "make mouseInit failed with exit code $LASTEXITCODE"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-not (Test-Path $MouseOutput)) {
|
||
|
|
throw "mouseInit build did not produce $MouseOutput"
|
||
|
|
}
|
||
|
|
Write-Host "[build] mouseInit Output: $MouseOutput"
|
||
|
|
|
||
|
|
$NetmanDir = Join-Path $Root 'CODE\netman'
|
||
|
|
$NetmanOutput = Join-Path $SystemDir 'system\deshab64\network\netman.elf'
|
||
|
|
|
||
|
|
Write-Host '[build] Building netman.elf...'
|
||
|
|
try {
|
||
|
|
& $make -C "$NetmanDir" -f MAKEFILE "CC=$clang" "LD=$lld"
|
||
|
|
if ($LASTEXITCODE -ne 0) {
|
||
|
|
throw "make netman failed with exit code $LASTEXITCODE"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
}
|
||
|
|
|
||
|
|
if (-not (Test-Path $NetmanOutput)) {
|
||
|
|
throw "netman build did not produce $NetmanOutput"
|
||
|
|
}
|
||
|
|
Write-Host "[build] netman Output: $NetmanOutput"
|
||
|
|
|
||
|
|
Write-Host '[build] Building DKM network drivers...'
|
||
|
|
Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv')
|
||
|
|
Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv')
|
||
|
|
|
||
|
|
Write-Host "[build] Output: $Output"
|
||
|
|
Write-Host "[build] DSK Output: $DskOutput"
|
||
|
|
Write-Host '[build] Packaging SYSTEM to GPT + FAT32 IMG...'
|
||
|
|
New-GptFat32Image $SystemDir $ImagePath 128
|
||
|
|
Write-Host "[build] Done: $ImagePath"
|
||
|
|
|
||
|
|
# Rebuild SATA FAT32 disk image (DSK reads deshab.elf/FirstInit.elf/mouseInit.elf from here)
|
||
|
|
Write-Host '[build] Rebuilding SATA FAT32 disk image...'
|
||
|
|
$BuildTmp = Join-Path $Root '.build_tmp'
|
||
|
|
$MkFat32 = Join-Path $BuildTmp 'mkfat32.exe'
|
||
|
|
if (Test-Path $MkFat32) {
|
||
|
|
Push-Location $BuildTmp
|
||
|
|
try {
|
||
|
|
& $MkFat32
|
||
|
|
if ($LASTEXITCODE -ne 0) {
|
||
|
|
Write-Host "[build] WARNING: mkfat32 exited with $LASTEXITCODE"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
Pop-Location
|
||
|
|
}
|
||
|
|
Write-Host "[build] SATA IMG: $(Join-Path $BuildTmp 'sata_fat32_dsk.img')"
|
||
|
|
} else {
|
||
|
|
Write-Host '[build] WARNING: mkfat32.exe not found, SATA image not rebuilt'
|
||
|
|
}
|