1/*	$NetBSD: sread.c,v 1.2 2021/10/23 15:20:26 jmcneill Exp $	*/
2
3/*++
4
5Copyright (c) 1998  Intel Corporation
6
7Module Name:
8
9    sread.c
10
11Abstract:
12
13    Simple read file access
14
15
16
17Revision History
18
19--*/
20
21#include "lib.h"
22
23#define SIMPLE_READ_SIGNATURE       EFI_SIGNATURE_32('s','r','d','r')
24typedef struct _SIMPLE_READ_FILE {
25    UINTN               Signature;
26    BOOLEAN             FreeBuffer;
27    VOID                *Source;
28    UINTN               SourceSize;
29    EFI_FILE_HANDLE     FileHandle;
30} SIMPLE_READ_HANDLE;
31
32
33
34EFI_STATUS
35OpenSimpleReadFile (
36    IN BOOLEAN                  BootPolicy,
37    IN VOID                     *SourceBuffer   OPTIONAL,
38    IN UINTN                    SourceSize,
39    IN OUT EFI_DEVICE_PATH      **FilePath,
40    OUT EFI_HANDLE              *DeviceHandle,
41    OUT SIMPLE_READ_FILE        *SimpleReadHandle
42    )
43/*++
44
45Routine Description:
46
47    Opens a file for (simple) reading.  The simple read abstraction
48    will access the file either from a memory copy, from a file
49    system interface, or from the load file interface.
50
51Arguments:
52
53Returns:
54
55    A handle to access the file
56
57--*/
58{
59    SIMPLE_READ_HANDLE          *FHand;
60    EFI_DEVICE_PATH             *UserFilePath;
61    EFI_DEVICE_PATH             *TempFilePath;
62    EFI_DEVICE_PATH             *TempFilePathPtr;
63    FILEPATH_DEVICE_PATH        *FilePathNode;
64    EFI_DEVICE_PATH_PROTOCOL    *AlignedFilePath;
65    EFI_FILE_HANDLE             FileHandle, LastHandle;
66    EFI_STATUS                  Status;
67    EFI_LOAD_FILE_INTERFACE     *LoadFile;
68
69    FHand = NULL;
70    AlignedFilePath = NULL;
71    UserFilePath = *FilePath;
72
73    //
74    // Allocate a new simple read handle structure
75    //
76
77    FHand = AllocateZeroPool (sizeof(SIMPLE_READ_HANDLE));
78    if (!FHand) {
79        Status = EFI_OUT_OF_RESOURCES;
80        goto Done;
81    }
82
83    *SimpleReadHandle = (SIMPLE_READ_FILE) FHand;
84    FHand->Signature = SIMPLE_READ_SIGNATURE;
85
86    //
87    // If the caller passed a copy of the file, then just use it
88    //
89
90    if (SourceBuffer) {
91        FHand->Source = SourceBuffer;
92        FHand->SourceSize = SourceSize;
93        *DeviceHandle = NULL;
94        Status = EFI_SUCCESS;
95        goto Done;
96    }
97
98    //
99    // Attempt to access the file via a file system interface
100    //
101
102    FileHandle = NULL;
103    Status = uefi_call_wrapper(BS->LocateDevicePath, 3, &FileSystemProtocol, FilePath, DeviceHandle);
104    if (!EFI_ERROR(Status)) {
105        FileHandle = LibOpenRoot (*DeviceHandle);
106    }
107
108    Status = FileHandle ? EFI_SUCCESS : EFI_UNSUPPORTED;
109
110    //
111    // Duplicate FilePath to make sure it is aligned so that
112    // FilePathNode->PathName below is 16-bit aligned.
113    //
114    AlignedFilePath = DuplicateDevicePath(*FilePath);
115    if (AlignedFilePath == NULL) {
116        if (FileHandle != NULL) {
117            uefi_call_wrapper(FileHandle->Close, 1, FileHandle);
118        }
119        return EFI_OUT_OF_RESOURCES;
120    }
121
122    //
123    // To access as a filesystem, the filepath should only
124    // contain filepath components.  Follow the filepath nodes
125    // and find the target file
126    //
127
128    FilePathNode = (FILEPATH_DEVICE_PATH *)AlignedFilePath;
129    while (!IsDevicePathEnd(&FilePathNode->Header)) {
130
131        //
132        // For filesystem access each node should be a filepath component
133        //
134
135        if (DevicePathType(&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
136            DevicePathSubType(&FilePathNode->Header) != MEDIA_FILEPATH_DP) {
137            Status = EFI_UNSUPPORTED;
138        }
139
140        //
141        // If there's been an error, stop
142        //
143
144        if (EFI_ERROR(Status)) {
145            break;
146        }
147
148        //
149        // Open this file path node
150        //
151
152        LastHandle = FileHandle;
153        FileHandle = NULL;
154
155        Status = uefi_call_wrapper(
156			LastHandle->Open,
157			5,
158                        LastHandle,
159                        &FileHandle,
160                        FilePathNode->PathName,
161                        EFI_FILE_MODE_READ,
162                        0
163                        );
164
165        //
166        // Close the last node
167        //
168
169        uefi_call_wrapper(LastHandle->Close, 1, LastHandle);
170
171        //
172        // Get the next node
173        //
174
175        FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode(&FilePathNode->Header);
176    }
177
178    //
179    // If success, return the FHand
180    //
181
182    if (!EFI_ERROR(Status)) {
183        ASSERT(FileHandle);
184        FHand->FileHandle = FileHandle;
185        goto Done;
186    }
187
188    //
189    // Cleanup from filesystem access
190    //
191
192    if (FileHandle) {
193        uefi_call_wrapper(FileHandle->Close, 1, FileHandle);
194        FileHandle = NULL;
195        *FilePath = UserFilePath;
196    }
197
198    //
199    // If the error is something other then unsupported, return it
200    //
201
202    if (Status != EFI_UNSUPPORTED) {
203        goto Done;
204    }
205
206    //
207    // Attempt to access the file via the load file protocol
208    //
209
210    Status = LibDevicePathToInterface (&LoadFileProtocol, *FilePath, (VOID*)&LoadFile);
211    if (!EFI_ERROR(Status)) {
212
213        TempFilePath = DuplicateDevicePath (*FilePath);
214
215        TempFilePathPtr = TempFilePath;
216
217        Status = uefi_call_wrapper(BS->LocateDevicePath, 3, &LoadFileProtocol, &TempFilePath, DeviceHandle);
218
219        FreePool (TempFilePathPtr);
220
221        //
222        // Determine the size of buffer needed to hold the file
223        //
224
225        SourceSize = 0;
226        Status = uefi_call_wrapper(
227		    LoadFile->LoadFile,
228			5,
229                    LoadFile,
230                    *FilePath,
231                    BootPolicy,
232                    &SourceSize,
233                    NULL
234                    );
235
236        //
237        // We expect a buffer too small error to inform us
238        // of the buffer size needed
239        //
240
241        if (Status == EFI_BUFFER_TOO_SMALL) {
242            SourceBuffer = AllocatePool (SourceSize);
243
244            if (SourceBuffer) {
245                FHand->FreeBuffer = TRUE;
246                FHand->Source = SourceBuffer;
247                FHand->SourceSize = SourceSize;
248
249                Status = uefi_call_wrapper(
250			    LoadFile->LoadFile,
251				5,
252                            LoadFile,
253                            *FilePath,
254                            BootPolicy,
255                            &SourceSize,
256                            SourceBuffer
257                            );
258            }
259        }
260
261        //
262        // If success, return FHand
263        //
264
265        if (!EFI_ERROR(Status) || Status == EFI_ALREADY_STARTED) {
266            goto Done;
267        }
268    }
269
270    //
271    // Nothing else to try
272    //
273
274    DEBUG ((D_LOAD|D_WARN, "OpenSimpleReadFile: Device did not support a known load protocol\n"));
275    Status = EFI_UNSUPPORTED;
276
277Done:
278
279    if (AlignedFilePath) {
280        FreePool (AlignedFilePath);
281    }
282
283    //
284    // If the file was not accessed, clean up
285    //
286    if (EFI_ERROR(Status) && (Status != EFI_ALREADY_STARTED)) {
287        if (FHand) {
288            if (FHand->FreeBuffer) {
289                FreePool (FHand->Source);
290            }
291
292            FreePool (FHand);
293        }
294    }
295
296    return Status;
297}
298
299EFI_STATUS
300ReadSimpleReadFile (
301    IN SIMPLE_READ_FILE     UserHandle,
302    IN UINTN                Offset,
303    IN OUT UINTN            *ReadSize,
304    OUT VOID                *Buffer
305    )
306{
307    UINTN                   EndPos;
308    SIMPLE_READ_HANDLE      *FHand;
309    EFI_STATUS              Status;
310
311    FHand = UserHandle;
312    ASSERT (FHand->Signature == SIMPLE_READ_SIGNATURE);
313    if (FHand->Source) {
314
315        //
316        // Move data from our local copy of the file
317        //
318
319        EndPos = Offset + *ReadSize;
320        if (EndPos > FHand->SourceSize) {
321            *ReadSize = FHand->SourceSize - Offset;
322            if (Offset >= FHand->SourceSize) {
323                *ReadSize = 0;
324            }
325        }
326
327        CopyMem (Buffer, (CHAR8 *) FHand->Source + Offset, *ReadSize);
328        Status = EFI_SUCCESS;
329
330    } else {
331
332        //
333        // Read data from the file
334        //
335
336        Status = uefi_call_wrapper(FHand->FileHandle->SetPosition, 2, FHand->FileHandle, Offset);
337
338        if (!EFI_ERROR(Status)) {
339            Status = uefi_call_wrapper(FHand->FileHandle->Read, 3, FHand->FileHandle, ReadSize, Buffer);
340        }
341    }
342
343    return Status;
344}
345
346
347VOID
348CloseSimpleReadFile (
349    IN SIMPLE_READ_FILE     UserHandle
350    )
351{
352    SIMPLE_READ_HANDLE      *FHand;
353
354    FHand = UserHandle;
355    ASSERT (FHand->Signature == SIMPLE_READ_SIGNATURE);
356
357    //
358    // Free any file handle we opened
359    //
360
361    if (FHand->FileHandle) {
362        uefi_call_wrapper(FHand->FileHandle->Close, 1, FHand->FileHandle);
363    }
364
365    //
366    // If we allocated the Source buffer, free it
367    //
368
369    if (FHand->FreeBuffer) {
370        FreePool (FHand->Source);
371    }
372
373    //
374    // Done with this simple read file handle
375    //
376
377    FreePool (FHand);
378}
379