bootentry.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package uefi
  15. import (
  16. "encoding/binary"
  17. "encoding/hex"
  18. "fmt"
  19. "strings"
  20. )
  21. type OvmfDevicePathType int
  22. const (
  23. DEVICE_TYPE_UNKNOWN OvmfDevicePathType = 0
  24. DEVICE_TYPE_CDROM OvmfDevicePathType = 1
  25. DEVICE_TYPE_IDE OvmfDevicePathType = 2
  26. DEVICE_TYPE_SCSI OvmfDevicePathType = 3
  27. DEVICE_TYPE_SCSI_CDROM OvmfDevicePathType = 4
  28. DEVICE_TYPE_PCI OvmfDevicePathType = 5
  29. DEVICE_TYPE_SATA OvmfDevicePathType = 6
  30. )
  31. type BootEntry struct {
  32. ID string // Boot0000, Boot0001, etc.
  33. Name string // Entry title
  34. DevPaths []*DevicePathElement // Device path elements
  35. RawData string // Raw hex data
  36. }
  37. func (b *BootEntry) GetType() OvmfDevicePathType {
  38. lenElements := len(b.DevPaths)
  39. if lenElements == 0 {
  40. return DEVICE_TYPE_UNKNOWN
  41. }
  42. devElement := b.DevPaths[lenElements-1]
  43. // fetch last device path element type
  44. switch devElement.devType {
  45. case DevicePathTypeHardware:
  46. if devElement.subType == 0x01 {
  47. return DEVICE_TYPE_PCI
  48. }
  49. case DevicePathTypeMessaging:
  50. switch devElement.subType {
  51. case 0x01:
  52. if strings.HasPrefix(b.Name, "UEFI QEMU DVD-ROM") {
  53. return DEVICE_TYPE_CDROM
  54. } else if strings.HasPrefix(b.Name, "UEFI QEMU HARDDISK") {
  55. return DEVICE_TYPE_IDE
  56. }
  57. case 0x02:
  58. if strings.HasPrefix(b.Name, "UEFI QEMU QEMU CD-ROM") {
  59. return DEVICE_TYPE_SCSI_CDROM
  60. } else if strings.HasPrefix(b.Name, "UEFI QEMU QEMU HARDDISK") {
  61. return DEVICE_TYPE_SCSI
  62. }
  63. case 0x12:
  64. return DEVICE_TYPE_SATA
  65. }
  66. }
  67. return DEVICE_TYPE_UNKNOWN
  68. }
  69. // ParseBootEntryData parses a boot entry from hex data
  70. func ParseBootEntryData(hexData string) (string, []*DevicePathElement, error) {
  71. // Decode hex data
  72. data, err := hex.DecodeString(hexData)
  73. if err != nil {
  74. return "", nil, fmt.Errorf("failed to decode hex data: %v", err)
  75. }
  76. // Check minimum length
  77. if len(data) < 8 {
  78. return "", nil, fmt.Errorf("data too short")
  79. }
  80. // Parse attributes and path list length
  81. // attributes := binary.LittleEndian.Uint32(data[0:4])
  82. pathListLen := binary.LittleEndian.Uint16(data[4:6])
  83. // Extract description string
  84. descData := data[6:]
  85. descBytes, strLen := ExtractUCS16String(descData)
  86. name := DecodeUTF16LE(descBytes)
  87. // Calculate path list start
  88. pathListStart := 6 + uint32(strLen)
  89. // Check if we have enough data for the path list
  90. if pathListLen == 0 {
  91. return name, []*DevicePathElement{}, nil
  92. }
  93. if uint32(len(data)) < pathListStart+uint32(pathListLen) {
  94. return name, nil, fmt.Errorf("invalid path list length")
  95. }
  96. // Extract path list
  97. pathListData := data[pathListStart : pathListStart+uint32(pathListLen)]
  98. // Parse device path elements
  99. devPaths, err := ParseDevicePathElements(pathListData)
  100. if err != nil {
  101. return name, nil, fmt.Errorf("failed to parse device path: %v", err)
  102. }
  103. return name, devPaths, nil
  104. }
  105. // ParseBootOrder parses a boot order from hex data
  106. func ParseBootOrder(hexData string) ([]uint16, error) {
  107. // Decode hex data
  108. data, err := hex.DecodeString(hexData)
  109. if err != nil {
  110. return nil, fmt.Errorf("failed to decode hex data: %v", err)
  111. }
  112. // Check data length
  113. if len(data) == 0 {
  114. return []uint16{}, nil
  115. }
  116. // Check if data length is valid (must be even)
  117. if len(data)%2 != 0 {
  118. return nil, fmt.Errorf("invalid boot order data length (must be even)")
  119. }
  120. // Parse boot order (2 bytes per entry)
  121. var bootOrder []uint16
  122. for i := 0; i < len(data); i += 2 {
  123. entryNum := binary.LittleEndian.Uint16(data[i : i+2])
  124. bootOrder = append(bootOrder, entryNum)
  125. }
  126. return bootOrder, nil
  127. }
  128. func ParseBootentryToBootorder(entry string) (uint16, error) {
  129. if !strings.HasPrefix(entry, "Boot") {
  130. return 0, fmt.Errorf("unknonw boot entry %s", entry)
  131. }
  132. hexData := entry[4:]
  133. // Decode hex data
  134. data, err := hex.DecodeString(hexData)
  135. if err != nil {
  136. return 0, fmt.Errorf("failed to decode hex data %s: %v", hexData, err)
  137. }
  138. return binary.BigEndian.Uint16(data), nil
  139. }
  140. // BuildBootOrderHex builds a hex string from boot order list
  141. func BuildBootOrderHex(bootOrder []uint16) string {
  142. // Allocate space for boot order (2 bytes per entry)
  143. data := make([]byte, len(bootOrder)*2)
  144. for i, entry := range bootOrder {
  145. // Write little-endian uint16
  146. binary.LittleEndian.PutUint16(data[i*2:], entry)
  147. }
  148. // Return hex string
  149. return hex.EncodeToString(data)
  150. }