Skip to content

Commit 25470f2

Browse files
committed
add Block IO Protocol and Disk IO Protocol
1 parent 6d293a9 commit 25470f2

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
package uefi
2+
3+
import (
4+
"errors"
5+
"unsafe"
6+
)
7+
8+
// Errors Disk IO and Block IO can return
9+
var (
10+
ErrDiskNotReady = errors.New("disk not ready")
11+
ErrNoMediaPresent = errors.New("no media present")
12+
ErrNegativeOffset = errors.New("negative offset")
13+
ErrReadOnly = errors.New("media is read-only")
14+
)
15+
16+
//---------------------------------------------------------------------------
17+
// GUIDs
18+
//---------------------------------------------------------------------------
19+
20+
// EFI_DISK_IO_PROTOCOL_GUID = {CE345171-BA0B-11d2-8E4F-00A0C969723B}
21+
var EFI_DISK_IO_PROTOCOL_GUID = EFI_GUID{
22+
0xCE345171, 0xBA0B, 0x11D2,
23+
[8]uint8{0x8e, 0x4f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b},
24+
}
25+
26+
// EFI_BLOCK_IO_PROTOCOL_GUID = {964E5B21-6459-11d2-8E39-00A0C969723B}
27+
var EFI_BLOCK_IO_PROTOCOL_GUID = EFI_GUID{
28+
0x964E5B21, 0x6459, 0x11D2,
29+
[8]uint8{0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b},
30+
}
31+
32+
// Classic subset of MEDIA fields (UEFI 2.x adds more tail fields; we keep the common prefix)
33+
type EFI_BLOCK_IO_MEDIA struct {
34+
MediaId uint32
35+
RemovableMedia bool
36+
MediaPresent bool
37+
LogicalPartition bool
38+
ReadOnly bool
39+
WriteCaching bool
40+
BlockSize uint32
41+
IoAlign uint32
42+
LastBlock EFI_LBA
43+
// Newer fields exist after this point; we intentionally omit for compatibility.
44+
}
45+
46+
// §Block I/O function table order: Revision, Media, Reset, ReadBlocks, WriteBlocks, FlushBlocks
47+
type EFI_BLOCK_IO_PROTOCOL struct {
48+
Revision uint64
49+
Media *EFI_BLOCK_IO_MEDIA
50+
reset uintptr // (this, ExtendedVerification: bool)
51+
readBlocks uintptr // (this, MediaId, LBA, BufferSize, Buffer)
52+
writeBlocks uintptr // (this, MediaId, LBA, BufferSize, Buffer)
53+
flushBlocks uintptr // (this)
54+
}
55+
56+
// Reset the device (optionally extended verification)
57+
func (p *EFI_BLOCK_IO_PROTOCOL) Reset(extendedVerification bool) EFI_STATUS {
58+
return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), convertBool(extendedVerification))
59+
}
60+
61+
// ReadBlocks reads raw LBAs into Buffer (size in bytes).
62+
func (p *EFI_BLOCK_IO_PROTOCOL) ReadBlocks(mediaId uint32, lba EFI_LBA, bufSize UINTN, buffer unsafe.Pointer) EFI_STATUS {
63+
return UefiCall5(
64+
p.readBlocks,
65+
uintptr(unsafe.Pointer(p)),
66+
uintptr(mediaId),
67+
uintptr(lba),
68+
uintptr(bufSize),
69+
uintptr(buffer),
70+
)
71+
}
72+
73+
74+
// WriteBlocks writes raw LBAs from Buffer (size in bytes).
75+
func (p *EFI_BLOCK_IO_PROTOCOL) WriteBlocks(mediaId uint32, lba EFI_LBA, bufSize UINTN, buffer unsafe.Pointer) EFI_STATUS {
76+
return UefiCall5(
77+
p.writeBlocks,
78+
uintptr(unsafe.Pointer(p)),
79+
uintptr(mediaId),
80+
uintptr(lba),
81+
uintptr(bufSize),
82+
uintptr(buffer),
83+
)
84+
}
85+
86+
// FlushBlocks flushes any device caches.
87+
func (p *EFI_BLOCK_IO_PROTOCOL) FlushBlocks() EFI_STATUS {
88+
return UefiCall1(p.flushBlocks, uintptr(unsafe.Pointer(p)))
89+
}
90+
91+
//---------------------------------------------------------------------------
92+
// Disk I/O: byte-addressed wrapper over Block I/O
93+
//---------------------------------------------------------------------------
94+
95+
// §Disk I/O function table order: Revision, ReadDisk, WriteDisk
96+
type EFI_DISK_IO_PROTOCOL struct {
97+
Revision uint64
98+
readDisk uintptr // (this, MediaId, Offset, BufferSize, Buffer)
99+
writeDisk uintptr // (this, MediaId, Offset, BufferSize, Buffer)
100+
}
101+
102+
// ReadDisk reads BufferSize bytes at byte Offset into Buffer.
103+
func (p *EFI_DISK_IO_PROTOCOL) ReadDisk(mediaId uint32, offset uint64, bufSize UINTN, buffer unsafe.Pointer) EFI_STATUS {
104+
return UefiCall5(
105+
p.readDisk,
106+
uintptr(unsafe.Pointer(p)),
107+
uintptr(mediaId),
108+
uintptr(offset),
109+
uintptr(bufSize),
110+
uintptr(buffer),
111+
)
112+
}
113+
114+
// WriteDisk writes BufferSize bytes at byte Offset from Buffer.
115+
func (p *EFI_DISK_IO_PROTOCOL) WriteDisk(mediaId uint32, offset uint64, bufSize UINTN, buffer unsafe.Pointer) EFI_STATUS {
116+
return UefiCall5(
117+
p.writeDisk,
118+
uintptr(unsafe.Pointer(p)),
119+
uintptr(mediaId),
120+
uintptr(offset),
121+
uintptr(bufSize),
122+
uintptr(buffer),
123+
)
124+
}
125+
126+
//---------------------------------------------------------------------------
127+
// Helpers: locate Disk I/O + Block I/O, and a tiny io.ReaderAt/io.WriterAt adapter
128+
//---------------------------------------------------------------------------
129+
130+
// Disk bundles Disk I/O with its backing Block I/O (for MediaId, size, alignment).
131+
type Disk struct {
132+
DiskIO *EFI_DISK_IO_PROTOCOL
133+
BlockIO *EFI_BLOCK_IO_PROTOCOL
134+
}
135+
136+
// EnumerateDisks returns Disk adapters for every handle that exposes both Disk I/O and Block I/O.
137+
func EnumerateDisks() ([]*Disk, error) {
138+
var (
139+
handleCount UINTN
140+
handleBuffer *EFI_HANDLE
141+
)
142+
st := BS().LocateHandleBuffer(ByProtocol, &EFI_DISK_IO_PROTOCOL_GUID, nil, &handleCount, &handleBuffer)
143+
if st == EFI_NOT_FOUND {
144+
return nil, nil
145+
}
146+
if st != EFI_SUCCESS {
147+
return nil, StatusError(st)
148+
}
149+
handles := unsafe.Slice((*EFI_HANDLE)(unsafe.Pointer(handleBuffer)), int(handleCount))
150+
151+
disks := make([]*Disk, 0, len(handles))
152+
for _, h := range handles {
153+
var dio *EFI_DISK_IO_PROTOCOL
154+
if st = BS().HandleProtocol(h, &EFI_DISK_IO_PROTOCOL_GUID, unsafe.Pointer(&dio)); st != EFI_SUCCESS {
155+
continue
156+
}
157+
var bio *EFI_BLOCK_IO_PROTOCOL
158+
if st = BS().HandleProtocol(h, &EFI_BLOCK_IO_PROTOCOL_GUID, unsafe.Pointer(&bio)); st != EFI_SUCCESS {
159+
// Some platforms expose DiskIO without BlockIO on the same handle; try parent inference if you care.
160+
continue
161+
}
162+
disks = append(disks, &Disk{DiskIO: dio, BlockIO: bio})
163+
}
164+
return disks, nil
165+
}
166+
167+
// Size returns total bytes addressable on this disk (LastBlock is inclusive).
168+
func (d *Disk) Size() int64 {
169+
if d == nil || d.BlockIO == nil || d.BlockIO.Media == nil {
170+
return 0
171+
}
172+
blk := int64(d.BlockIO.Media.BlockSize)
173+
// LastBlock is the highest LBA (inclusive), so count = LastBlock+1
174+
return int64(d.BlockIO.Media.LastBlock+1) * blk
175+
}
176+
177+
// SectorSize returns the logical block size in bytes.
178+
func (d *Disk) SectorSize() int {
179+
if d == nil || d.BlockIO == nil || d.BlockIO.Media == nil {
180+
return 0
181+
}
182+
return int(d.BlockIO.Media.BlockSize)
183+
}
184+
185+
// ReadAt satisfies io.ReaderAt over Disk I/O (byte-addressable).
186+
func (d *Disk) ReadAt(p []byte, off int64) (int, error) {
187+
if d == nil || d.DiskIO == nil || d.BlockIO == nil || d.BlockIO.Media == nil {
188+
return 0, ErrDiskNotReady
189+
}
190+
if !d.BlockIO.Media.MediaPresent {
191+
return 0, ErrNoMediaPresent
192+
}
193+
if off < 0 {
194+
return 0, ErrNegativeOffset
195+
}
196+
if len(p) == 0 {
197+
return 0, nil
198+
}
199+
sz := UINTN(len(p))
200+
st := d.DiskIO.ReadDisk(d.BlockIO.Media.MediaId, uint64(off), sz, unsafe.Pointer(&p[0]))
201+
if st == EFI_SUCCESS {
202+
return int(sz), nil
203+
}
204+
return 0, StatusError(st)
205+
}
206+
207+
// WriteAt satisfies io.WriterAt over Disk I/O (byte-addressable).
208+
func (d *Disk) WriteAt(p []byte, off int64) (int, error) {
209+
if d == nil || d.DiskIO == nil || d.BlockIO == nil || d.BlockIO.Media == nil {
210+
return 0, ErrDiskNotReady
211+
}
212+
if d.BlockIO.Media.ReadOnly {
213+
return 0, ErrReadOnly
214+
}
215+
if off < 0 {
216+
return 0, ErrNegativeOffset
217+
}
218+
if len(p) == 0 {
219+
return 0, nil
220+
}
221+
sz := UINTN(len(p))
222+
st := d.DiskIO.WriteDisk(d.BlockIO.Media.MediaId, uint64(off), sz, unsafe.Pointer(&p[0]))
223+
if st == EFI_SUCCESS {
224+
return int(sz), nil
225+
}
226+
return 0, StatusError(st)
227+
}
228+
229+
// Flush flushes device caches via Block I/O (if supported).
230+
func (d *Disk) Flush() error {
231+
if d == nil || d.BlockIO == nil {
232+
return ErrDiskNotReady
233+
}
234+
st := d.BlockIO.FlushBlocks()
235+
if st == EFI_SUCCESS {
236+
return nil
237+
}
238+
return StatusError(st)
239+
}

src/machine/uefi/util.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,10 @@ func convertBoolean(b BOOLEAN) uintptr {
9898
}
9999
return 0
100100
}
101+
102+
func convertBool(b bool) uintptr {
103+
if b {
104+
return 1
105+
}
106+
return 0
107+
}

0 commit comments

Comments
 (0)