blob: 46f561f0a992e6199023edac6913bb5c08e3e1eb [file] [log] [blame]
Lorenz Brunee17d832022-10-18 12:02:45 +00001package gpt
2
3import (
4 "encoding/binary"
5 "fmt"
6 "io"
7 "math"
8)
9
10// See UEFI Specification 2.9 Table 5-3
11type 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
20type mbrPartitionRecord struct {
21 BootIndicator byte
22 StartingCHS [3]byte
23 Type byte
24 EndingCHS [3]byte
25 StartingBlock uint32
26 SizeInBlocks uint32
27}
28
29var mbrSignature = [2]byte{0x55, 0xaa}
30
31func 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.
65func 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}