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