VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c@ 93492

Last change on this file since 93492 was 93492, checked in by vboxsync, 2 years ago

EFI: Revert r139958 as it breaks booting from FreeBSD ISO images with EFI enabled, ticketref:19910 (will break ticketref:19862 but there is network boot support now making the iPXE ISO image approach superfluous)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.8 KB
Line 
1/* $Id: fsw_efi.c 93492 2022-01-31 10:31:30Z vboxsync $ */
2/** @file
3 * fsw_efi.c - EFI host environment code.
4 */
5
6/*
7 * Copyright (C) 2010-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 * ---------------------------------------------------------------------------
26 * This code is based on:
27 *
28 * Copyright (c) 2006 Christoph Pfisterer
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions are
32 * met:
33 *
34 * * Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 *
37 * * Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the
40 * distribution.
41 *
42 * * Neither the name of Christoph Pfisterer nor the names of the
43 * contributors may be used to endorse or promote products derived
44 * from this software without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
47 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
48 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
49 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
50 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
51 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
52 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
56 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57 */
58
59#include "fsw_efi.h"
60
61#define DEBUG_LEVEL 0
62
63#ifndef FSTYPE
64#ifdef VBOX
65#error FSTYPE must be defined!
66#else
67#define FSTYPE ext2
68#endif
69#endif
70
71/** Helper macro for stringification. */
72#define FSW_EFI_STRINGIFY(x) L#x
73/** Expands to the EFI driver name given the file system type name. */
74#define FSW_EFI_DRIVER_NAME(t) L"Fsw " FSW_EFI_STRINGIFY(t) L" File System Driver"
75
76
77// function prototypes
78
79EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This,
80 IN EFI_HANDLE ControllerHandle,
81 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath);
82EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This,
83 IN EFI_HANDLE ControllerHandle,
84 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath);
85EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This,
86 IN EFI_HANDLE ControllerHandle,
87 IN UINTN NumberOfChildren,
88 IN EFI_HANDLE *ChildHandleBuffer);
89
90EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
91 IN CHAR8 *Language,
92 OUT CHAR16 **DriverName);
93EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
94 IN EFI_HANDLE ControllerHandle,
95 IN EFI_HANDLE ChildHandle OPTIONAL,
96 IN CHAR8 *Language,
97 OUT CHAR16 **ControllerName);
98
99void fsw_efi_change_blocksize(struct fsw_volume *vol,
100 fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
101 fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize);
102fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer);
103
104EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume);
105
106EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
107 OUT EFI_FILE **Root);
108EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
109 OUT EFI_FILE **NewFileHandle);
110
111EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
112 IN OUT UINTN *BufferSize,
113 OUT VOID *Buffer);
114EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
115 OUT UINT64 *Position);
116EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
117 IN UINT64 Position);
118
119EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
120 OUT EFI_FILE **NewHandle,
121 IN CHAR16 *FileName,
122 IN UINT64 OpenMode,
123 IN UINT64 Attributes);
124EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
125 IN OUT UINTN *BufferSize,
126 OUT VOID *Buffer);
127EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
128 IN UINT64 Position);
129
130EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
131 IN EFI_GUID *InformationType,
132 IN OUT UINTN *BufferSize,
133 OUT VOID *Buffer);
134EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
135 IN struct fsw_dnode *dno,
136 IN OUT UINTN *BufferSize,
137 OUT VOID *Buffer);
138
139#if defined(VBOX) && (FSTYPE == hfs)
140extern fsw_status_t fsw_hfs_get_blessed_file(void *vol, struct fsw_string *path);
141#endif
142
143/**
144 * Interface structure for the EFI Driver Binding protocol.
145 */
146
147EFI_DRIVER_BINDING_PROTOCOL fsw_efi_DriverBinding_table = {
148 fsw_efi_DriverBinding_Supported,
149 fsw_efi_DriverBinding_Start,
150 fsw_efi_DriverBinding_Stop,
151 0x10,
152 NULL,
153 NULL
154};
155
156/**
157 * Interface structure for the EFI Component Name protocol.
158 */
159
160EFI_COMPONENT_NAME_PROTOCOL fsw_efi_ComponentName_table = {
161 fsw_efi_ComponentName_GetDriverName,
162 fsw_efi_ComponentName_GetControllerName,
163 "eng"
164};
165
166/**
167 * Dispatch table for our FSW host driver.
168 */
169
170struct fsw_host_table fsw_efi_host_table = {
171 FSW_STRING_TYPE_UTF16,
172
173 fsw_efi_change_blocksize,
174 fsw_efi_read_block
175};
176
177extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE);
178
179
180
181/**
182 * Image entry point. Installs the Driver Binding and Component Name protocols
183 * on the image's handle. Actually mounting a file system is initiated through
184 * the Driver Binding protocol at the firmware's request.
185 */
186EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE ImageHandle,
187 IN EFI_SYSTEM_TABLE *SystemTable)
188{
189 EFI_STATUS Status;
190
191#ifndef VBOX
192 InitializeLib(ImageHandle, SystemTable);
193#endif
194
195 // complete Driver Binding protocol instance
196 fsw_efi_DriverBinding_table.ImageHandle = ImageHandle;
197 fsw_efi_DriverBinding_table.DriverBindingHandle = ImageHandle;
198 // install Driver Binding protocol
199 Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
200 &PROTO_NAME(DriverBindingProtocol),
201 EFI_NATIVE_INTERFACE,
202 &fsw_efi_DriverBinding_table);
203 if (EFI_ERROR (Status)) {
204 return Status;
205 }
206
207 // install Component Name protocol
208 Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
209 &PROTO_NAME(ComponentNameProtocol),
210 EFI_NATIVE_INTERFACE,
211 &fsw_efi_ComponentName_table);
212 if (EFI_ERROR (Status)) {
213 return Status;
214 }
215
216 return EFI_SUCCESS;
217}
218
219/**
220 * Driver Binding EFI protocol, Supported function. This function is called by EFI
221 * to test if this driver can handle a certain device. Our implementation only checks
222 * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols)
223 * and implicitly checks if the disk is already in use by another driver.
224 */
225
226EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This,
227 IN EFI_HANDLE ControllerHandle,
228 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
229{
230 EFI_STATUS Status;
231 EFI_DISK_IO *DiskIo;
232
233 // we check for both DiskIO and BlockIO protocols
234
235 // first, open DiskIO
236 Status = BS->OpenProtocol(ControllerHandle,
237 &PROTO_NAME(DiskIoProtocol),
238 (VOID **) &DiskIo,
239 This->DriverBindingHandle,
240 ControllerHandle,
241 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
242 if (EFI_ERROR(Status))
243 {
244 return Status;
245 }
246
247 // we were just checking, close it again
248 BS->CloseProtocol(ControllerHandle,
249 &PROTO_NAME(DiskIoProtocol),
250 This->DriverBindingHandle,
251 ControllerHandle);
252
253 // next, check BlockIO without actually opening it
254 Status = BS->OpenProtocol(ControllerHandle,
255 &PROTO_NAME(BlockIoProtocol),
256 NULL,
257 This->DriverBindingHandle,
258 ControllerHandle,
259 EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
260 return Status;
261}
262
263static EFI_STATUS fsw_efi_ReMount(IN FSW_VOLUME_DATA *pVolume,
264 IN EFI_HANDLE ControllerHandle,
265 EFI_DISK_IO *pDiskIo,
266 EFI_BLOCK_IO *pBlockIo)
267{
268 EFI_STATUS Status;
269 pVolume->Signature = FSW_VOLUME_DATA_SIGNATURE;
270 pVolume->Handle = ControllerHandle;
271 pVolume->DiskIo = pDiskIo;
272 pVolume->MediaId = pBlockIo->Media->MediaId;
273 pVolume->LastIOStatus = EFI_SUCCESS;
274
275 // mount the filesystem
276 Status = fsw_efi_map_status(fsw_mount(pVolume, &fsw_efi_host_table,
277 &FSW_FSTYPE_TABLE_NAME(FSTYPE), &pVolume->vol),
278 pVolume);
279
280 if (!EFI_ERROR(Status)) {
281 // register the SimpleFileSystem protocol
282 pVolume->FileSystem.Revision = EFI_FILE_IO_INTERFACE_REVISION;
283 pVolume->FileSystem.OpenVolume = fsw_efi_FileSystem_OpenVolume;
284 Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle,
285 &PROTO_NAME(SimpleFileSystemProtocol), &pVolume->FileSystem,
286 NULL);
287#if DEBUG_LEVEL /* This error is always printed and destroys the boot logo. */
288 if (EFI_ERROR(Status))
289 Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status);
290#endif
291 }
292 return Status;
293}
294
295/**
296 * Driver Binding EFI protocol, Start function. This function is called by EFI
297 * to start driving the given device. It is still possible at this point to
298 * return EFI_UNSUPPORTED, and in fact we will do so if the file system driver
299 * cannot find the superblock signature (or equivalent) that it expects.
300 *
301 * This function allocates memory for a per-volume structure, opens the
302 * required protocols (just Disk I/O in our case, Block I/O is only looked
303 * at to get the MediaId field), and lets the FSW core mount the file system.
304 * If successful, an EFI Simple File System protocol is exported on the
305 * device handle.
306 */
307
308EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This,
309 IN EFI_HANDLE ControllerHandle,
310 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
311{
312 EFI_STATUS Status;
313 EFI_BLOCK_IO *BlockIo;
314 EFI_DISK_IO *DiskIo;
315 FSW_VOLUME_DATA *Volume;
316
317 // open consumed protocols
318 Status = BS->OpenProtocol(ControllerHandle,
319 &PROTO_NAME(BlockIoProtocol),
320 (VOID **) &BlockIo,
321 This->DriverBindingHandle,
322 ControllerHandle,
323 EFI_OPEN_PROTOCOL_GET_PROTOCOL); // NOTE: we only want to look at the MediaId
324 if (EFI_ERROR(Status)) {
325 return Status;
326 }
327
328 Status = BS->OpenProtocol(ControllerHandle,
329 &PROTO_NAME(DiskIoProtocol),
330 (VOID **) &DiskIo,
331 This->DriverBindingHandle,
332 ControllerHandle,
333 EFI_OPEN_PROTOCOL_BY_DRIVER);
334 if (EFI_ERROR(Status)) {
335 return Status;
336 }
337
338 // allocate volume structure
339 Volume = AllocateZeroPool(sizeof(FSW_VOLUME_DATA));
340 Status = fsw_efi_ReMount(Volume, ControllerHandle, DiskIo, BlockIo);
341
342 // on errors, close the opened protocols
343 if (EFI_ERROR(Status)) {
344 if (Volume->vol != NULL)
345 fsw_unmount(Volume->vol);
346 FreePool(Volume);
347
348#if 0
349 if (Status == EFI_MEDIA_CHANGED)
350 Status = fsw_efi_ReMount(Volume, ControllerHandle, DiskIo, BlockIo);
351 else
352#endif
353 BS->CloseProtocol(ControllerHandle,
354 &PROTO_NAME(DiskIoProtocol),
355 This->DriverBindingHandle,
356 ControllerHandle);
357 }
358
359 return Status;
360}
361
362/**
363 * Driver Binding EFI protocol, Stop function. This function is called by EFI
364 * to stop the driver on the given device. This translates to an unmount
365 * call for the FSW core.
366 *
367 * We assume that all file handles on the volume have been closed before
368 * the driver is stopped. At least with the EFI shell, that is actually the
369 * case; it closes all file handles between commands.
370 */
371
372EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This,
373 IN EFI_HANDLE ControllerHandle,
374 IN UINTN NumberOfChildren,
375 IN EFI_HANDLE *ChildHandleBuffer)
376{
377 EFI_STATUS Status;
378 EFI_FILE_IO_INTERFACE *FileSystem;
379 FSW_VOLUME_DATA *Volume;
380
381#if DEBUG_LEVEL
382 Print(L"fsw_efi_DriverBinding_Stop\n");
383#endif
384
385 // get the installed SimpleFileSystem interface
386 Status = BS->OpenProtocol(ControllerHandle,
387 &PROTO_NAME(SimpleFileSystemProtocol),
388 (VOID **) &FileSystem,
389 This->DriverBindingHandle,
390 ControllerHandle,
391 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
392 if (EFI_ERROR(Status))
393 return EFI_UNSUPPORTED;
394
395 // get private data structure
396 Volume = FSW_VOLUME_FROM_FILE_SYSTEM(FileSystem);
397
398 // uninstall Simple File System protocol
399 Status = BS->UninstallMultipleProtocolInterfaces(ControllerHandle,
400 &PROTO_NAME(SimpleFileSystemProtocol), &Volume->FileSystem,
401 NULL);
402 if (EFI_ERROR(Status)) {
403 Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status);
404 return Status;
405 }
406#if DEBUG_LEVEL
407 Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n");
408#endif
409
410 // release private data structure
411 if (Volume->vol != NULL)
412 fsw_unmount(Volume->vol);
413 FreePool(Volume);
414
415 // close the consumed protocols
416 Status = BS->CloseProtocol(ControllerHandle,
417 &PROTO_NAME(DiskIoProtocol),
418 This->DriverBindingHandle,
419 ControllerHandle);
420
421 return Status;
422}
423
424/**
425 * Component Name EFI protocol, GetDriverName function. Used by the EFI
426 * environment to inquire the name of this driver. The name returned is
427 * based on the file system type actually used in compilation.
428 */
429
430EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
431 IN CHAR8 *Language,
432 OUT CHAR16 **DriverName)
433{
434 if (Language == NULL || DriverName == NULL)
435 return EFI_INVALID_PARAMETER;
436#if 0
437
438 if (Language[0] == 'e' && Language[1] == 'n' && Language[2] == 'g' && Language[3] == 0) {
439 *DriverName = FSW_EFI_DRIVER_NAME(FSTYPE);
440 return EFI_SUCCESS;
441 }
442#endif
443 return EFI_UNSUPPORTED;
444}
445
446/**
447 * Component Name EFI protocol, GetControllerName function. Not implemented
448 * because this is not a "bus" driver in the sense of the EFI Driver Model.
449 */
450
451EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
452 IN EFI_HANDLE ControllerHandle,
453 IN EFI_HANDLE ChildHandle OPTIONAL,
454 IN CHAR8 *Language,
455 OUT CHAR16 **ControllerName)
456{
457 return EFI_UNSUPPORTED;
458}
459
460/**
461 * FSW interface function for block size changes. This function is called by the FSW core
462 * when the file system driver changes the block sizes for the volume.
463 */
464
465void fsw_efi_change_blocksize(struct fsw_volume *vol,
466 fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
467 fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize)
468{
469 // nothing to do
470}
471
472/**
473 * FSW interface function to read data blocks. This function is called by the FSW core
474 * to read a block of data from the device. The buffer is allocated by the core code.
475 */
476
477fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer)
478{
479 EFI_STATUS Status;
480 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)vol->host_data;
481
482 FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_efi_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize));
483
484 // read from disk
485 Status = Volume->DiskIo->ReadDisk(Volume->DiskIo, Volume->MediaId,
486 (UINT64)phys_bno * vol->phys_blocksize,
487 vol->phys_blocksize,
488 buffer);
489 Volume->LastIOStatus = Status;
490 if (EFI_ERROR(Status))
491 return FSW_IO_ERROR;
492 return FSW_SUCCESS;
493}
494
495/**
496 * Map FSW status codes to EFI status codes. The FSW_IO_ERROR code is only produced
497 * by fsw_efi_read_block, so we map it back to the EFI status code remembered from
498 * the last I/O operation.
499 */
500
501EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume)
502{
503 switch (fsw_status) {
504 case FSW_SUCCESS:
505 return EFI_SUCCESS;
506 case FSW_OUT_OF_MEMORY:
507 return EFI_VOLUME_CORRUPTED;
508 case FSW_IO_ERROR:
509 return Volume->LastIOStatus;
510 case FSW_UNSUPPORTED:
511 return EFI_UNSUPPORTED;
512 case FSW_NOT_FOUND:
513 return EFI_NOT_FOUND;
514 case FSW_VOLUME_CORRUPTED:
515 return EFI_VOLUME_CORRUPTED;
516 default:
517 return EFI_DEVICE_ERROR;
518 }
519}
520
521/**
522 * File System EFI protocol, OpenVolume function. Creates a file handle for
523 * the root directory and returns it. Note that this function may be called
524 * multiple times and returns a new file handle each time. Each returned
525 * handle is closed by the client using it.
526 */
527
528EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
529 OUT EFI_FILE **Root)
530{
531 EFI_STATUS Status;
532 FSW_VOLUME_DATA *Volume = FSW_VOLUME_FROM_FILE_SYSTEM(This);
533
534#if DEBUG_LEVEL
535 Print(L"fsw_efi_FileSystem_OpenVolume\n");
536#endif
537
538 Status = fsw_efi_dnode_to_FileHandle(Volume->vol->root, Root);
539
540 return Status;
541}
542
543/**
544 * File Handle EFI protocol, Open function. Dispatches the call
545 * based on the kind of file handle.
546 */
547
548EFI_STATUS EFIAPI fsw_efi_FileHandle_Open(IN EFI_FILE *This,
549 OUT EFI_FILE **NewHandle,
550 IN CHAR16 *FileName,
551 IN UINT64 OpenMode,
552 IN UINT64 Attributes)
553{
554 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
555
556 if (File->Type == FSW_EFI_FILE_TYPE_DIR)
557 return fsw_efi_dir_open(File, NewHandle, FileName, OpenMode, Attributes);
558 // not supported for regular files
559 return EFI_UNSUPPORTED;
560}
561
562/**
563 * File Handle EFI protocol, Close function. Closes the FSW shandle
564 * and frees the memory used for the structure.
565 */
566
567EFI_STATUS EFIAPI fsw_efi_FileHandle_Close(IN EFI_FILE *This)
568{
569 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
570
571#if DEBUG_LEVEL
572 Print(L"fsw_efi_FileHandle_Close\n");
573#endif
574
575 fsw_shandle_close(&File->shand);
576 FreePool(File);
577
578 return EFI_SUCCESS;
579}
580
581/**
582 * File Handle EFI protocol, Delete function. Calls through to Close
583 * and returns a warning because this driver is read-only.
584 */
585
586EFI_STATUS EFIAPI fsw_efi_FileHandle_Delete(IN EFI_FILE *This)
587{
588 EFI_STATUS Status;
589
590 Status = This->Close(This);
591 if (Status == EFI_SUCCESS) {
592 // this driver is read-only
593 Status = EFI_WARN_DELETE_FAILURE;
594 }
595
596 return Status;
597}
598
599/**
600 * File Handle EFI protocol, Read function. Dispatches the call
601 * based on the kind of file handle.
602 */
603
604EFI_STATUS EFIAPI fsw_efi_FileHandle_Read(IN EFI_FILE *This,
605 IN OUT UINTN *BufferSize,
606 OUT VOID *Buffer)
607{
608 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
609
610 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
611 return fsw_efi_file_read(File, BufferSize, Buffer);
612 else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
613 return fsw_efi_dir_read(File, BufferSize, Buffer);
614 return EFI_UNSUPPORTED;
615}
616
617/**
618 * File Handle EFI protocol, Write function. Returns unsupported status
619 * because this driver is read-only.
620 */
621
622EFI_STATUS EFIAPI fsw_efi_FileHandle_Write(IN EFI_FILE *This,
623 IN OUT UINTN *BufferSize,
624 IN VOID *Buffer)
625{
626 // this driver is read-only
627 return EFI_WRITE_PROTECTED;
628}
629
630/**
631 * File Handle EFI protocol, GetPosition function. Dispatches the call
632 * based on the kind of file handle.
633 */
634
635EFI_STATUS EFIAPI fsw_efi_FileHandle_GetPosition(IN EFI_FILE *This,
636 OUT UINT64 *Position)
637{
638 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
639
640 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
641 return fsw_efi_file_getpos(File, Position);
642 // not defined for directories
643 return EFI_UNSUPPORTED;
644}
645
646/**
647 * File Handle EFI protocol, SetPosition function. Dispatches the call
648 * based on the kind of file handle.
649 */
650
651EFI_STATUS EFIAPI fsw_efi_FileHandle_SetPosition(IN EFI_FILE *This,
652 IN UINT64 Position)
653{
654 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
655
656 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
657 return fsw_efi_file_setpos(File, Position);
658 else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
659 return fsw_efi_dir_setpos(File, Position);
660 return EFI_UNSUPPORTED;
661}
662
663/**
664 * File Handle EFI protocol, GetInfo function. Dispatches to the common
665 * function implementing this.
666 */
667
668EFI_STATUS EFIAPI fsw_efi_FileHandle_GetInfo(IN EFI_FILE *This,
669 IN EFI_GUID *InformationType,
670 IN OUT UINTN *BufferSize,
671 OUT VOID *Buffer)
672{
673 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
674
675 return fsw_efi_dnode_getinfo(File, InformationType, BufferSize, Buffer);
676}
677
678/**
679 * File Handle EFI protocol, SetInfo function. Returns unsupported status
680 * because this driver is read-only.
681 */
682
683EFI_STATUS EFIAPI fsw_efi_FileHandle_SetInfo(IN EFI_FILE *This,
684 IN EFI_GUID *InformationType,
685 IN UINTN BufferSize,
686 IN VOID *Buffer)
687{
688 // this driver is read-only
689 return EFI_WRITE_PROTECTED;
690}
691
692/**
693 * File Handle EFI protocol, Flush function. Returns unsupported status
694 * because this driver is read-only.
695 */
696
697EFI_STATUS EFIAPI fsw_efi_FileHandle_Flush(IN EFI_FILE *This)
698{
699 // this driver is read-only
700 return EFI_WRITE_PROTECTED;
701}
702
703/**
704 * Set up a file handle for a dnode. This function allocates a data structure
705 * for a file handle, opens a FSW shandle and populates the EFI_FILE structure
706 * with the interface functions.
707 */
708
709EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
710 OUT EFI_FILE **NewFileHandle)
711{
712 EFI_STATUS Status;
713 FSW_FILE_DATA *File;
714
715 // make sure the dnode has complete info
716 Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data);
717 if (EFI_ERROR(Status))
718 return Status;
719
720 // check type
721 if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR)
722 return EFI_UNSUPPORTED;
723
724 // allocate file structure
725 File = AllocateZeroPool(sizeof(FSW_FILE_DATA));
726 File->Signature = FSW_FILE_DATA_SIGNATURE;
727 if (dno->type == FSW_DNODE_TYPE_FILE)
728 File->Type = FSW_EFI_FILE_TYPE_FILE;
729 else if (dno->type == FSW_DNODE_TYPE_DIR)
730 File->Type = FSW_EFI_FILE_TYPE_DIR;
731
732 // open shandle
733 Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand),
734 (FSW_VOLUME_DATA *)dno->vol->host_data);
735 if (EFI_ERROR(Status)) {
736 FreePool(File);
737 return Status;
738 }
739
740 // populate the file handle
741 File->FileHandle.Revision = EFI_FILE_HANDLE_REVISION;
742 File->FileHandle.Open = fsw_efi_FileHandle_Open;
743 File->FileHandle.Close = fsw_efi_FileHandle_Close;
744 File->FileHandle.Delete = fsw_efi_FileHandle_Delete;
745 File->FileHandle.Read = fsw_efi_FileHandle_Read;
746 File->FileHandle.Write = fsw_efi_FileHandle_Write;
747 File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition;
748 File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition;
749 File->FileHandle.GetInfo = fsw_efi_FileHandle_GetInfo;
750 File->FileHandle.SetInfo = fsw_efi_FileHandle_SetInfo;
751 File->FileHandle.Flush = fsw_efi_FileHandle_Flush;
752
753 *NewFileHandle = &File->FileHandle;
754 return EFI_SUCCESS;
755}
756
757/**
758 * Data read function for regular files. Calls through to fsw_shandle_read.
759 */
760
761EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
762 IN OUT UINTN *BufferSize,
763 OUT VOID *Buffer)
764{
765 EFI_STATUS Status;
766 fsw_u32 buffer_size;
767
768#if DEBUG_LEVEL
769 Print(L"fsw_efi_file_read %d bytes\n", *BufferSize);
770#endif
771
772 buffer_size = (fsw_u32)*BufferSize;
773 if (buffer_size != *BufferSize)
774 buffer_size = ~(fsw_u32)0;
775 Status = fsw_efi_map_status(fsw_shandle_read(&File->shand, &buffer_size, Buffer),
776 (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data);
777 *BufferSize = buffer_size;
778
779 return Status;
780}
781
782/**
783 * Get file position for regular files.
784 */
785
786EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
787 OUT UINT64 *Position)
788{
789 *Position = File->shand.pos;
790 return EFI_SUCCESS;
791}
792
793/**
794 * Set file position for regular files. EFI specifies the all-ones value
795 * to be a special value for the end of the file.
796 */
797
798EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
799 IN UINT64 Position)
800{
801 if (Position == 0xFFFFFFFFFFFFFFFFULL)
802 File->shand.pos = File->shand.dnode->size;
803 else
804 File->shand.pos = Position;
805 return EFI_SUCCESS;
806}
807
808/**
809 * Open function used to open new file handles relative to a directory.
810 * In EFI, the "open file" function is implemented by directory file handles
811 * and is passed a relative or volume-absolute path to the file or directory
812 * to open. We use fsw_dnode_lookup_path to find the node plus an additional
813 * call to fsw_dnode_resolve because EFI has no concept of symbolic links.
814 */
815
816EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
817 OUT EFI_FILE **NewHandle,
818 IN CHAR16 *FileName,
819 IN UINT64 OpenMode,
820 IN UINT64 Attributes)
821{
822 EFI_STATUS Status;
823 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
824 struct fsw_dnode *dno;
825 struct fsw_dnode *target_dno;
826 struct fsw_string lookup_path;
827
828#if DEBUG_LEVEL
829 Print(L"fsw_efi_dir_open: '%s'\n", FileName);
830#endif
831
832 if (OpenMode != EFI_FILE_MODE_READ)
833 return EFI_WRITE_PROTECTED;
834
835 lookup_path.type = FSW_STRING_TYPE_UTF16;
836 lookup_path.len = (int)StrLen(FileName);
837 lookup_path.size = lookup_path.len * sizeof(fsw_u16);
838 lookup_path.data = FileName;
839
840 // resolve the path (symlinks along the way are automatically resolved)
841 Status = fsw_efi_map_status(fsw_dnode_lookup_path(File->shand.dnode, &lookup_path, '\\', &dno),
842 Volume);
843 if (EFI_ERROR(Status))
844 return Status;
845
846 // if the final node is a symlink, also resolve it
847 Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno),
848 Volume);
849 fsw_dnode_release(dno);
850 if (EFI_ERROR(Status))
851 return Status;
852 dno = target_dno;
853
854 // make a new EFI handle for the target dnode
855 Status = fsw_efi_dnode_to_FileHandle(dno, NewHandle);
856 fsw_dnode_release(dno);
857 return Status;
858}
859
860/**
861 * Read function for directories. A file handle read on a directory retrieves
862 * the next directory entry.
863 */
864
865EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
866 IN OUT UINTN *BufferSize,
867 OUT VOID *Buffer)
868{
869 EFI_STATUS Status;
870 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
871 struct fsw_dnode *dno;
872
873#if DEBUG_LEVEL
874 Print(L"fsw_efi_dir_read...\n");
875#endif
876
877 // read the next entry
878 Status = fsw_efi_map_status(fsw_dnode_dir_read(&File->shand, &dno),
879 Volume);
880 if (Status == EFI_NOT_FOUND) {
881 // end of directory
882 *BufferSize = 0;
883#if DEBUG_LEVEL
884 Print(L"...no more entries\n");
885#endif
886 return EFI_SUCCESS;
887 }
888 if (EFI_ERROR(Status))
889 return Status;
890
891 // get info into buffer
892 Status = fsw_efi_dnode_fill_FileInfo(Volume, dno, BufferSize, Buffer);
893 fsw_dnode_release(dno);
894 return Status;
895}
896
897/**
898 * Set file position for directories. The only allowed set position operation
899 * for directories is to rewind the directory completely by setting the
900 * position to zero.
901 */
902
903EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
904 IN UINT64 Position)
905{
906 if (Position == 0) {
907 File->shand.pos = 0;
908 return EFI_SUCCESS;
909 } else {
910 // directories can only rewind to the start
911 return EFI_UNSUPPORTED;
912 }
913}
914
915/**
916 * Get file or volume information. This function implements the GetInfo call
917 * for all file handles. Control is dispatched according to the type of information
918 * requested by the caller.
919 */
920
921EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
922 IN EFI_GUID *InformationType,
923 IN OUT UINTN *BufferSize,
924 OUT VOID *Buffer)
925{
926 EFI_STATUS Status;
927 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
928 EFI_FILE_SYSTEM_INFO *FSInfo;
929 UINTN RequiredSize;
930 struct fsw_volume_stat vsb;
931
932 if (CompareGuid(InformationType, &GUID_NAME(FileInfo))) {
933#if DEBUG_LEVEL
934 Print(L"fsw_efi_dnode_getinfo: FILE_INFO\n");
935#endif
936
937 Status = fsw_efi_dnode_fill_FileInfo(Volume, File->shand.dnode, BufferSize, Buffer);
938
939 } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemInfo))) {
940#if DEBUG_LEVEL
941 Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n");
942#endif
943
944 // check buffer size
945 RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + fsw_efi_strsize(&Volume->vol->label);
946 if (*BufferSize < RequiredSize) {
947 *BufferSize = RequiredSize;
948 return EFI_BUFFER_TOO_SMALL;
949 }
950
951 // fill structure
952 FSInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;
953 FSInfo->Size = RequiredSize;
954 FSInfo->ReadOnly = TRUE;
955 FSInfo->BlockSize = Volume->vol->log_blocksize;
956 fsw_efi_strcpy(FSInfo->VolumeLabel, &Volume->vol->label);
957
958 // get the missing info from the fs driver
959 ZeroMem(&vsb, sizeof(struct fsw_volume_stat));
960 Status = fsw_efi_map_status(fsw_volume_stat(Volume->vol, &vsb), Volume);
961 if (EFI_ERROR(Status))
962 return Status;
963 FSInfo->VolumeSize = vsb.total_bytes;
964 FSInfo->FreeSpace = vsb.free_bytes;
965
966 // prepare for return
967 *BufferSize = RequiredSize;
968 Status = EFI_SUCCESS;
969
970 } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemVolumeLabelInfoId))) {
971#if DEBUG_LEVEL
972 Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n");
973#endif
974
975 // check buffer size
976 RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + fsw_efi_strsize(&Volume->vol->label);
977 if (*BufferSize < RequiredSize) {
978 *BufferSize = RequiredSize;
979 return EFI_BUFFER_TOO_SMALL;
980 }
981
982 // copy volume label
983 fsw_efi_strcpy(((EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Buffer)->VolumeLabel, &Volume->vol->label);
984
985 // prepare for return
986 *BufferSize = RequiredSize;
987 Status = EFI_SUCCESS;
988
989#ifdef VBOX
990 } else if (CompareGuid(InformationType, &gVBoxFsBlessedFileInfoGuid)) {
991
992# if FSTYPE == hfs
993 struct fsw_string StrBlessedFile;
994
995 fsw_status_t rc = fsw_hfs_get_blessed_file(Volume->vol, &StrBlessedFile);
996 if (!rc)
997 {
998 // check buffer size
999 RequiredSize = SIZE_OF_VBOX_FS_BLESSED_FILE + fsw_efi_strsize(&StrBlessedFile);
1000 if (*BufferSize < RequiredSize) {
1001 *BufferSize = RequiredSize;
1002 return EFI_BUFFER_TOO_SMALL;
1003 }
1004
1005 // copy volume label
1006 fsw_efi_strcpy(((VBOX_FS_BLESSED_FILE *)Buffer)->BlessedFile, &StrBlessedFile);
1007
1008 // prepare for return
1009 *BufferSize = RequiredSize;
1010 Status = EFI_SUCCESS;
1011 }
1012 else
1013# endif
1014 Status = EFI_UNSUPPORTED;
1015#endif
1016
1017 } else {
1018 Status = EFI_UNSUPPORTED;
1019 }
1020
1021 return Status;
1022}
1023
1024/**
1025 * Time mapping callback for the fsw_dnode_stat call. This function converts
1026 * a Posix style timestamp into an EFI_TIME structure and writes it to the
1027 * appropriate member of the EFI_FILE_INFO structure that we're filling.
1028 */
1029
1030static void fsw_efi_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time)
1031{
1032 EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
1033
1034 if (which == FSW_DNODE_STAT_CTIME)
1035 fsw_efi_decode_time(&FileInfo->CreateTime, posix_time);
1036 else if (which == FSW_DNODE_STAT_MTIME)
1037 fsw_efi_decode_time(&FileInfo->ModificationTime, posix_time);
1038 else if (which == FSW_DNODE_STAT_ATIME)
1039 fsw_efi_decode_time(&FileInfo->LastAccessTime, posix_time);
1040}
1041
1042/**
1043 * Mode mapping callback for the fsw_dnode_stat call. This function looks at
1044 * the Posix mode passed by the file system driver and makes appropriate
1045 * adjustments to the EFI_FILE_INFO structure that we're filling.
1046 */
1047
1048static void fsw_efi_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode)
1049{
1050 EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
1051
1052 if ((posix_mode & S_IWUSR) == 0)
1053 FileInfo->Attribute |= EFI_FILE_READ_ONLY;
1054}
1055
1056/**
1057 * Common function to fill an EFI_FILE_INFO with information about a dnode.
1058 */
1059
1060EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
1061 IN struct fsw_dnode *dno,
1062 IN OUT UINTN *BufferSize,
1063 OUT VOID *Buffer)
1064{
1065 EFI_STATUS Status;
1066 EFI_FILE_INFO *FileInfo;
1067 UINTN RequiredSize;
1068 struct fsw_dnode *target_dno;
1069 struct fsw_dnode_stat sb;
1070
1071 // make sure the dnode has complete info
1072 Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume);
1073 if (EFI_ERROR(Status))
1074 return Status;
1075
1076 /// @todo check/assert that the dno's name is in UTF16
1077
1078 // check buffer size
1079 RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name);
1080 if (*BufferSize < RequiredSize) {
1081 /// @todo wind back the directory in this case
1082
1083#if DEBUG_LEVEL
1084 Print(L"...BUFFER TOO SMALL\n");
1085#endif
1086 *BufferSize = RequiredSize;
1087 return EFI_BUFFER_TOO_SMALL;
1088 }
1089
1090 // fill structure
1091 ZeroMem(Buffer, RequiredSize);
1092 FileInfo = (EFI_FILE_INFO *)Buffer;
1093
1094 // must preserve the original file name
1095 fsw_efi_strcpy(FileInfo->FileName, &dno->name);
1096
1097 // if the node is a symlink, also resolve it
1098 Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno), Volume);
1099 fsw_dnode_release(dno);
1100 if (EFI_ERROR(Status))
1101 return Status;
1102 dno = target_dno;
1103 // make sure the dnode has complete info again
1104 Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume);
1105 if (EFI_ERROR(Status))
1106 return Status;
1107
1108 FileInfo->Size = RequiredSize;
1109 FileInfo->FileSize = dno->size;
1110 FileInfo->Attribute = 0;
1111 if (dno->type == FSW_DNODE_TYPE_DIR)
1112 FileInfo->Attribute |= EFI_FILE_DIRECTORY;
1113
1114 // get the missing info from the fs driver
1115 ZeroMem(&sb, sizeof(struct fsw_dnode_stat));
1116 sb.store_time_posix = fsw_efi_store_time_posix;
1117 sb.store_attr_posix = fsw_efi_store_attr_posix;
1118 sb.host_data = FileInfo;
1119 Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume);
1120 if (EFI_ERROR(Status))
1121 return Status;
1122 FileInfo->PhysicalSize = sb.used_bytes;
1123
1124 // prepare for return
1125 *BufferSize = RequiredSize;
1126#if DEBUG_LEVEL
1127 Print(L"...returning '%s'\n", FileInfo->FileName);
1128#endif
1129 return EFI_SUCCESS;
1130}
1131
1132// EOF
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use