|
| 1 | +package uefi |
| 2 | + |
| 3 | +import ( |
| 4 | + "unsafe" |
| 5 | +) |
| 6 | + |
| 7 | +//--------------------------------------------------------------------------- |
| 8 | +// GUIDs (§13.5 File Protocol and related info GUIDs) |
| 9 | +//--------------------------------------------------------------------------- |
| 10 | + |
| 11 | +// EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID = {964E5B22-6459-11D2-8E39-00A0C969723B} |
| 12 | +var EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID = EFI_GUID{ |
| 13 | + 0x964E5B22, 0x6459, 0x11D2, |
| 14 | + [8]uint8{0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}, |
| 15 | +} |
| 16 | + |
| 17 | +// gEfiFileInfoGuid = {09576E92-6D3F-11D2-8E39-00A0C969723B} |
| 18 | +var EFI_FILE_INFO_ID = EFI_GUID{ |
| 19 | + 0x09576e92, 0x6d3f, 0x11d2, |
| 20 | + [8]uint8{0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}, |
| 21 | +} |
| 22 | + |
| 23 | +// gEfiFileSystemInfoGuid = {09576E93-6D3F-11D2-8E39-00A0C969723B} |
| 24 | +var EFI_FILE_SYSTEM_INFO_ID = EFI_GUID{ |
| 25 | + 0x09576e93, 0x6d3f, 0x11d2, |
| 26 | + [8]uint8{0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}, |
| 27 | +} |
| 28 | + |
| 29 | +// gEfiFileSystemVolumeLabelInfoIdGuid = {DB47D7D3-FE81-11D3-9A35-0090273FC14D} |
| 30 | +var EFI_FILE_SYSTEM_VOLUME_LABEL_ID = EFI_GUID{ |
| 31 | + 0xdb47d7d3, 0xfe81, 0x11d3, |
| 32 | + [8]uint8{0x9a, 0x35, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}, |
| 33 | +} |
| 34 | + |
| 35 | +//--------------------------------------------------------------------------- |
| 36 | +// Constants (§13.5.1) |
| 37 | +//--------------------------------------------------------------------------- |
| 38 | + |
| 39 | +// Open modes are 64-bit flags |
| 40 | +const ( |
| 41 | + EFI_FILE_MODE_READ uint64 = 0x0000000000000001 |
| 42 | + EFI_FILE_MODE_WRITE uint64 = 0x0000000000000002 |
| 43 | + EFI_FILE_MODE_CREATE uint64 = 0x8000000000000000 |
| 44 | +) |
| 45 | + |
| 46 | +// Attribute flags (also 64-bit) |
| 47 | +const ( |
| 48 | + EFI_FILE_READ_ONLY uint64 = 0x0000000000000001 |
| 49 | + EFI_FILE_HIDDEN uint64 = 0x0000000000000002 |
| 50 | + EFI_FILE_SYSTEM uint64 = 0x0000000000000004 |
| 51 | + EFI_FILE_RESERVED uint64 = 0x0000000000000008 |
| 52 | + EFI_FILE_DIRECTORY uint64 = 0x0000000000000010 |
| 53 | + EFI_FILE_ARCHIVE uint64 = 0x0000000000000020 |
| 54 | +) |
| 55 | + |
| 56 | +// Optional: known protocol revision values (not strictly required to use) |
| 57 | +const ( |
| 58 | + EFI_FILE_PROTOCOL_REVISION = 0x00010000 |
| 59 | + EFI_FILE_PROTOCOL_REVISION2 = 0x00020000 |
| 60 | + EFI_FILE_PROTOCOL_LATEST = EFI_FILE_PROTOCOL_REVISION2 |
| 61 | +) |
| 62 | + |
| 63 | +//--------------------------------------------------------------------------- |
| 64 | +// Protocols (§13.4 Simple File System, §13.5 File Protocol) |
| 65 | +//--------------------------------------------------------------------------- |
| 66 | + |
| 67 | +// EFI_SIMPLE_FILE_SYSTEM_PROTOCOL (§13.4.2) |
| 68 | +// Function table: Revision, OpenVolume |
| 69 | +// OpenVolume returns a handle to the volume root directory (EFI_FILE_PROTOCOL*) |
| 70 | + |
| 71 | +type EFI_SIMPLE_FILE_SYSTEM_PROTOCOL struct { |
| 72 | + Revision uint64 |
| 73 | + openVolume uintptr // (this, **EFI_FILE_PROTOCOL) |
| 74 | +} |
| 75 | + |
| 76 | +func (p *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL) OpenVolume(root **EFI_FILE_PROTOCOL) EFI_STATUS { |
| 77 | + return UefiCall2( |
| 78 | + p.openVolume, |
| 79 | + uintptr(unsafe.Pointer(p)), |
| 80 | + uintptr(unsafe.Pointer(root)), |
| 81 | + ) |
| 82 | +} |
| 83 | + |
| 84 | +// EFI_FILE_PROTOCOL (§13.5.2) |
| 85 | +// Function table order: Revision, Open, Close, Delete, Read, Write, GetPosition, SetPosition, GetInfo, SetInfo, Flush |
| 86 | + |
| 87 | +type EFI_FILE_PROTOCOL struct { |
| 88 | + Revision uint64 |
| 89 | + open uintptr // (this, **newHandle, *FileName, OpenMode, Attributes) |
| 90 | + close uintptr // (this) |
| 91 | + delete uintptr // (this) |
| 92 | + read uintptr // (this, *BufferSize, Buffer) |
| 93 | + write uintptr // (this, *BufferSize, Buffer) |
| 94 | + getPosition uintptr // (this, *Position) |
| 95 | + setPosition uintptr // (this, Position) |
| 96 | + getInfo uintptr // (this, *InfoType, *BufferSize, Buffer) |
| 97 | + setInfo uintptr // (this, *InfoType, BufferSize, Buffer) |
| 98 | + flush uintptr // (this) |
| 99 | +} |
| 100 | + |
| 101 | +// Open opens a file or directory relative to this handle (which may be a root or directory handle). |
| 102 | +// FileName must be a NUL-terminated UTF-16 path using '\\' separators. (§13.5.3) |
| 103 | +func (p *EFI_FILE_PROTOCOL) Open(newHandle **EFI_FILE_PROTOCOL, fileName *CHAR16, openMode uint64, attributes uint64) EFI_STATUS { |
| 104 | + return UefiCall6( |
| 105 | + p.open, |
| 106 | + uintptr(unsafe.Pointer(p)), |
| 107 | + uintptr(unsafe.Pointer(newHandle)), |
| 108 | + uintptr(unsafe.Pointer(fileName)), |
| 109 | + uintptr(openMode), |
| 110 | + uintptr(attributes), |
| 111 | + 0, |
| 112 | + ) |
| 113 | +} |
| 114 | + |
| 115 | +// Close closes the file handle. (§13.5.4) |
| 116 | +func (p *EFI_FILE_PROTOCOL) Close() EFI_STATUS { |
| 117 | + return UefiCall1(p.close, uintptr(unsafe.Pointer(p))) |
| 118 | +} |
| 119 | + |
| 120 | +// Delete deletes the file opened by this handle. (§13.5.5) |
| 121 | +func (p *EFI_FILE_PROTOCOL) Delete() EFI_STATUS { |
| 122 | + return UefiCall1(p.delete, uintptr(unsafe.Pointer(p))) |
| 123 | +} |
| 124 | + |
| 125 | +// Read reads from the file into Buffer. BufferSize is in/out: on entry, size of Buffer; on return, bytes read. (§13.5.6) |
| 126 | +func (p *EFI_FILE_PROTOCOL) Read(bufferSize *UINTN, buffer unsafe.Pointer) EFI_STATUS { |
| 127 | + return UefiCall3( |
| 128 | + p.read, |
| 129 | + uintptr(unsafe.Pointer(p)), |
| 130 | + uintptr(unsafe.Pointer(bufferSize)), |
| 131 | + uintptr(buffer), |
| 132 | + ) |
| 133 | +} |
| 134 | + |
| 135 | +// Write writes to the file from Buffer. BufferSize is in/out: on entry, bytes to write; on return, bytes written. (§13.5.7) |
| 136 | +func (p *EFI_FILE_PROTOCOL) Write(bufferSize *UINTN, buffer unsafe.Pointer) EFI_STATUS { |
| 137 | + return UefiCall3( |
| 138 | + p.write, |
| 139 | + uintptr(unsafe.Pointer(p)), |
| 140 | + uintptr(unsafe.Pointer(bufferSize)), |
| 141 | + uintptr(buffer), |
| 142 | + ) |
| 143 | +} |
| 144 | + |
| 145 | +// GetPosition returns the current file position in bytes. (§13.5.8) |
| 146 | +func (p *EFI_FILE_PROTOCOL) GetPosition(position *uint64) EFI_STATUS { |
| 147 | + return UefiCall2( |
| 148 | + p.getPosition, |
| 149 | + uintptr(unsafe.Pointer(p)), |
| 150 | + uintptr(unsafe.Pointer(position)), |
| 151 | + ) |
| 152 | +} |
| 153 | + |
| 154 | +// SetPosition sets the current file position. Setting to 0xFFFFFFFFFFFFFFFF seeks to end-of-file. (§13.5.9) |
| 155 | +func (p *EFI_FILE_PROTOCOL) SetPosition(position uint64) EFI_STATUS { |
| 156 | + return UefiCall2( |
| 157 | + p.setPosition, |
| 158 | + uintptr(unsafe.Pointer(p)), |
| 159 | + uintptr(position), |
| 160 | + ) |
| 161 | +} |
| 162 | + |
| 163 | +// GetInfo retrieves metadata identified by InformationType. Common GUIDs: EFI_FILE_INFO_ID, EFI_FILE_SYSTEM_INFO_ID, EFI_FILE_SYSTEM_VOLUME_LABEL_ID. (§13.5.10) |
| 164 | +func (p *EFI_FILE_PROTOCOL) GetInfo(infoType *EFI_GUID, bufferSize *UINTN, buffer unsafe.Pointer) EFI_STATUS { |
| 165 | + return UefiCall4( |
| 166 | + p.getInfo, |
| 167 | + uintptr(unsafe.Pointer(p)), |
| 168 | + uintptr(unsafe.Pointer(infoType)), |
| 169 | + uintptr(unsafe.Pointer(bufferSize)), |
| 170 | + uintptr(buffer), |
| 171 | + ) |
| 172 | +} |
| 173 | + |
| 174 | +// SetInfo sets metadata identified by InformationType. (§13.5.11) |
| 175 | +func (p *EFI_FILE_PROTOCOL) SetInfo(infoType *EFI_GUID, bufferSize UINTN, buffer unsafe.Pointer) EFI_STATUS { |
| 176 | + return UefiCall4( |
| 177 | + p.setInfo, |
| 178 | + uintptr(unsafe.Pointer(p)), |
| 179 | + uintptr(unsafe.Pointer(infoType)), |
| 180 | + uintptr(bufferSize), |
| 181 | + uintptr(buffer), |
| 182 | + ) |
| 183 | +} |
| 184 | + |
| 185 | +// Flush flushes file data and metadata to the device. (§13.5.12) |
| 186 | +func (p *EFI_FILE_PROTOCOL) Flush() EFI_STATUS { |
| 187 | + return UefiCall1(p.flush, uintptr(unsafe.Pointer(p))) |
| 188 | +} |
| 189 | + |
| 190 | +//--------------------------------------------------------------------------- |
| 191 | +// Info structures (§13.5.13, §13.5.15) |
| 192 | +//--------------------------------------------------------------------------- |
| 193 | + |
| 194 | +// EFI_FILE_INFO head (FileName is a variable-length CHAR16[] immediately following this struct) |
| 195 | +// Layout must exactly match the spec; FileName is retrieved from the backing buffer used with GetInfo. |
| 196 | + |
| 197 | +type EFI_FILE_INFO struct { |
| 198 | + Size uint64 |
| 199 | + FileSize uint64 |
| 200 | + PhysicalSize uint64 |
| 201 | + CreateTime EFI_TIME |
| 202 | + LastAccessTime EFI_TIME |
| 203 | + ModificationTime EFI_TIME |
| 204 | + Attribute uint64 |
| 205 | + // CHAR16 FileName[] follows |
| 206 | +} |
| 207 | + |
| 208 | +// EFI_FILE_SYSTEM_INFO head (VolumeLabel is a variable-length CHAR16[] after the struct) |
| 209 | + |
| 210 | +type EFI_FILE_SYSTEM_INFO struct { |
| 211 | + Size uint64 |
| 212 | + ReadOnly bool |
| 213 | + _ [7]byte // pad to 8-byte alignment (bool is 1 byte) |
| 214 | + VolumeSize uint64 |
| 215 | + FreeSpace uint64 |
| 216 | + BlockSize uint32 |
| 217 | + _ uint32 // padding to keep next CHAR16[] aligned as in C |
| 218 | + // CHAR16 VolumeLabel[] follows |
| 219 | +} |
| 220 | + |
| 221 | +// EFI_FILE_SYSTEM_VOLUME_LABEL is just a CHAR16[] volume label; retrieved via GetInfo with EFI_FILE_SYSTEM_VOLUME_LABEL_ID. |
| 222 | +// Represented implicitly by reading a buffer of UTF-16 data. |
| 223 | + |
| 224 | +//--------------------------------------------------------------------------- |
| 225 | +// Helpers: enumerate volumes and open root |
| 226 | +//--------------------------------------------------------------------------- |
| 227 | + |
| 228 | +// EnumerateFileSystems locates all Simple File System handles and returns their protocol pointers. |
| 229 | +func EnumerateFileSystems() ([]*EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, error) { |
| 230 | + var ( |
| 231 | + handleCount UINTN |
| 232 | + handleBuffer *EFI_HANDLE |
| 233 | + ) |
| 234 | + st := BS().LocateHandleBuffer(ByProtocol, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, nil, &handleCount, &handleBuffer) |
| 235 | + if st == EFI_NOT_FOUND { |
| 236 | + return nil, nil |
| 237 | + } |
| 238 | + if st != EFI_SUCCESS { |
| 239 | + return nil, StatusError(st) |
| 240 | + } |
| 241 | + handles := unsafe.Slice((*EFI_HANDLE)(unsafe.Pointer(handleBuffer)), int(handleCount)) |
| 242 | + out := make([]*EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, 0, len(handles)) |
| 243 | + for _, h := range handles { |
| 244 | + var sfs *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL |
| 245 | + if st = BS().HandleProtocol(h, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, unsafe.Pointer(&sfs)); st == EFI_SUCCESS { |
| 246 | + out = append(out, sfs) |
| 247 | + } |
| 248 | + } |
| 249 | + return out, nil |
| 250 | +} |
| 251 | + |
| 252 | +// OpenRoot opens the volume root directory for a given SFS. |
| 253 | +func OpenRoot(sfs *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL) (*EFI_FILE_PROTOCOL, EFI_STATUS) { |
| 254 | + var root *EFI_FILE_PROTOCOL |
| 255 | + st := sfs.OpenVolume(&root) |
| 256 | + if st != EFI_SUCCESS { |
| 257 | + return nil, st |
| 258 | + } |
| 259 | + return root, EFI_SUCCESS |
| 260 | +} |
0 commit comments