Lorenz Brun | ee17d83 | 2022-10-18 12:02:45 +0000 | [diff] [blame] | 1 | package gpt |
| 2 | |
| 3 | import ( |
| 4 | "encoding/binary" |
| 5 | "fmt" |
| 6 | "io" |
| 7 | "math" |
| 8 | ) |
| 9 | |
| 10 | // See UEFI Specification 2.9 Table 5-3 |
| 11 | type mbr struct { |
| 12 | BootCode [440]byte |
| 13 | DiskSignature [4]byte |
| 14 | _ [2]byte |
| 15 | PartitionRecords [4]mbrPartitionRecord |
| 16 | Signature [2]byte |
| 17 | } |
| 18 | |
| 19 | // See UEFI Specification 2.9 Table 5-4 |
| 20 | type mbrPartitionRecord struct { |
| 21 | BootIndicator byte |
| 22 | StartingCHS [3]byte |
| 23 | Type byte |
| 24 | EndingCHS [3]byte |
| 25 | StartingBlock uint32 |
| 26 | SizeInBlocks uint32 |
| 27 | } |
| 28 | |
| 29 | var mbrSignature = [2]byte{0x55, 0xaa} |
| 30 | |
| 31 | func makeProtectiveMBR(w io.Writer, blockCount int64, bootCode []byte) error { |
| 32 | var representedBlockCount = uint32(math.MaxUint32) |
| 33 | if blockCount < math.MaxUint32 { |
| 34 | representedBlockCount = uint32(blockCount) |
| 35 | } |
| 36 | m := mbr{ |
| 37 | DiskSignature: [4]byte{0, 0, 0, 0}, |
| 38 | PartitionRecords: [4]mbrPartitionRecord{ |
| 39 | { |
| 40 | StartingCHS: toCHS(1), |
| 41 | Type: 0xEE, // Table/Protective MBR |
| 42 | StartingBlock: 1, |
| 43 | SizeInBlocks: representedBlockCount, |
| 44 | EndingCHS: toCHS(blockCount + 1), |
| 45 | }, |
| 46 | {}, |
| 47 | {}, |
| 48 | {}, |
| 49 | }, |
| 50 | Signature: mbrSignature, |
| 51 | } |
| 52 | if len(bootCode) > len(m.BootCode) { |
| 53 | return fmt.Errorf("BootCode is %d bytes, can only store %d", len(bootCode), len(m.BootCode)) |
| 54 | } |
| 55 | copy(m.BootCode[:], bootCode) |
| 56 | if err := binary.Write(w, binary.LittleEndian, &m); err != nil { |
| 57 | return fmt.Errorf("failed to write MBR: %w", err) |
| 58 | } |
| 59 | return nil |
| 60 | } |
| 61 | |
| 62 | // toCHS converts a LBA to a "logical" CHS, i.e. what a legacy BIOS 13h |
| 63 | // interface would use. This has nothing to do with the actual CHS geometry |
| 64 | // which depends on the disk and interface used. |
| 65 | func toCHS(lba int64) (chs [3]byte) { |
| 66 | const maxCylinders = (1 << 10) - 1 |
| 67 | const maxHeadsPerCylinder = (1 << 8) - 1 |
| 68 | const maxSectorsPerTrack = (1 << 6) - 2 // Sector is 1-based |
| 69 | cylinder := lba / (maxHeadsPerCylinder * maxSectorsPerTrack) |
| 70 | head := (lba / maxSectorsPerTrack) % maxHeadsPerCylinder |
| 71 | sector := (lba % maxSectorsPerTrack) + 1 |
| 72 | if cylinder > maxCylinders { |
| 73 | cylinder = maxCylinders |
| 74 | head = maxHeadsPerCylinder |
| 75 | sector = maxSectorsPerTrack + 1 |
| 76 | } |
| 77 | chs[0] = uint8(head) |
| 78 | chs[1] = uint8(sector) |
| 79 | chs[1] |= uint8(cylinder>>2) & 0xc0 |
| 80 | chs[2] = uint8(cylinder) |
| 81 | return |
| 82 | } |