| package gpt | 
 |  | 
 | import ( | 
 | 	"encoding/binary" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"math" | 
 | ) | 
 |  | 
 | // See UEFI Specification 2.9 Table 5-3 | 
 | type mbr struct { | 
 | 	BootCode         [440]byte | 
 | 	DiskSignature    [4]byte | 
 | 	_                [2]byte | 
 | 	PartitionRecords [4]mbrPartitionRecord | 
 | 	Signature        [2]byte | 
 | } | 
 |  | 
 | // See UEFI Specification 2.9 Table 5-4 | 
 | type mbrPartitionRecord struct { | 
 | 	BootIndicator byte | 
 | 	StartingCHS   [3]byte | 
 | 	Type          byte | 
 | 	EndingCHS     [3]byte | 
 | 	StartingBlock uint32 | 
 | 	SizeInBlocks  uint32 | 
 | } | 
 |  | 
 | var mbrSignature = [2]byte{0x55, 0xaa} | 
 |  | 
 | func makeProtectiveMBR(w io.Writer, blockCount int64, bootCode []byte) error { | 
 | 	var representedBlockCount = uint32(math.MaxUint32) | 
 | 	if blockCount < math.MaxUint32 { | 
 | 		representedBlockCount = uint32(blockCount) | 
 | 	} | 
 | 	m := mbr{ | 
 | 		DiskSignature: [4]byte{0, 0, 0, 0}, | 
 | 		PartitionRecords: [4]mbrPartitionRecord{ | 
 | 			{ | 
 | 				StartingCHS:   toCHS(1), | 
 | 				Type:          0xEE, // Table/Protective MBR | 
 | 				StartingBlock: 1, | 
 | 				SizeInBlocks:  representedBlockCount - 1, | 
 | 				EndingCHS:     toCHS(blockCount + 1), | 
 | 			}, | 
 | 			{}, | 
 | 			{}, | 
 | 			{}, | 
 | 		}, | 
 | 		Signature: mbrSignature, | 
 | 	} | 
 | 	if len(bootCode) > len(m.BootCode) { | 
 | 		return fmt.Errorf("BootCode is %d bytes, can only store %d", len(bootCode), len(m.BootCode)) | 
 | 	} | 
 | 	copy(m.BootCode[:], bootCode) | 
 | 	if err := binary.Write(w, binary.LittleEndian, &m); err != nil { | 
 | 		return fmt.Errorf("failed to write MBR: %w", err) | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // toCHS converts a LBA to a "logical" CHS, i.e. what a legacy BIOS 13h | 
 | // interface would use. This has nothing to do with the actual CHS geometry | 
 | // which depends on the disk and interface used. | 
 | func toCHS(lba int64) (chs [3]byte) { | 
 | 	const maxCylinders = (1 << 10) - 1 | 
 | 	const maxHeadsPerCylinder = (1 << 8) - 1 | 
 | 	const maxSectorsPerTrack = (1 << 6) - 2 // Sector is 1-based | 
 | 	cylinder := lba / (maxHeadsPerCylinder * maxSectorsPerTrack) | 
 | 	head := (lba / maxSectorsPerTrack) % maxHeadsPerCylinder | 
 | 	sector := (lba % maxSectorsPerTrack) + 1 | 
 | 	if cylinder > maxCylinders { | 
 | 		cylinder = maxCylinders | 
 | 		head = maxHeadsPerCylinder | 
 | 		sector = maxSectorsPerTrack + 1 | 
 | 	} | 
 | 	chs[0] = uint8(head) | 
 | 	chs[1] = uint8(sector) | 
 | 	chs[1] |= uint8(cylinder>>2) & 0xc0 | 
 | 	chs[2] = uint8(cylinder) | 
 | 	return | 
 | } |