/* * Copyright (C) 2001-2003 Hewlett-Packard Co. * Contributed by Stephane Eranian * Copyright (C) 2006-2009 Intel Corporation * Contributed by Fenghua Yu * Contributed by Bibo Mao * Contributed by Chandramouli Narayanan * * This file is part of the ELILO, the EFI Linux boot loader. * * ELILO is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * ELILO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ELILO; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Please check out the elilo.txt for complete documentation on how * to use this program. */ #include #include #include "elilo.h" #include "fs/localfs.h" /* * LocalFS is just a shim layer on top of the * FileSystem Protocol which gives access to FAT32,16,12 */ #define FS_NAME L"vfat" typedef struct { EFI_HANDLE dev; /* device we're attached to */ EFI_FILE_HANDLE volume; /* root of volume */ } localfs_priv_state_t; #define LOCALFS_F2FD(f) ((UINTN)(f)) #define LOCALFS_FD2F(fd) ((EFI_FILE_HANDLE)(fd)) typedef union { localfs_interface_t pub_intf; struct { localfs_interface_t pub_intf; localfs_priv_state_t priv_data; } localfs_priv; } localfs_t; #define FS_PRIVATE(n) (&(((localfs_t *)n)->localfs_priv.priv_data)) static EFI_GUID LocalFsProtocol = LOCALFS_PROTOCOL; /* * let's be clean here */ typedef union { EFI_HANDLE dev; localfs_t *intf; } dev_tab_t; static dev_tab_t *dev_tab; /* holds all devices we found */ static UINTN ndev; /* how many entries in dev_tab */ static EFI_STATUS localfs_name(localfs_interface_t *this, CHAR16 *name, UINTN maxlen) { if (name == NULL || maxlen < 1) return EFI_INVALID_PARAMETER; StrnCpy(name, FS_NAME, maxlen-1); name[maxlen-1] = CHAR_NULL; return EFI_SUCCESS; } static EFI_STATUS localfs_open(localfs_interface_t *this, CHAR16 *name, UINTN *fd) { localfs_priv_state_t *lfs; EFI_STATUS status; EFI_FILE_HANDLE fh; if (this == NULL || name == NULL || fd == NULL) return EFI_INVALID_PARAMETER; lfs = FS_PRIVATE(this); DBG_PRT((L"localfs_open on %s\n", name)); status = uefi_call_wrapper(lfs->volume->Open, 5, lfs->volume, &fh, name, EFI_FILE_MODE_READ, (UINT64)0); if (status == EFI_SUCCESS) { *fd = LOCALFS_F2FD(fh); } return status; } static EFI_STATUS localfs_read(localfs_interface_t *this, UINTN fd, VOID *buf, UINTN *size) { localfs_priv_state_t *lfs; if (this == NULL || fd == 0 || buf == NULL || size == NULL) return EFI_INVALID_PARAMETER; lfs = FS_PRIVATE(this); return uefi_call_wrapper(lfs->volume->Read, 3, LOCALFS_FD2F(fd), size, buf); } static EFI_STATUS localfs_close(localfs_interface_t *this, UINTN fd) { localfs_priv_state_t *lfs; if (this == NULL || fd == 0) return EFI_INVALID_PARAMETER; lfs = FS_PRIVATE(this); return uefi_call_wrapper(lfs->volume->Close, 1, LOCALFS_FD2F(fd)); } static EFI_STATUS localfs_infosize(localfs_interface_t *this, UINTN fd, UINT64 *sz) { localfs_priv_state_t *lfs; EFI_FILE_INFO *info; if (this == NULL || fd == 0 || sz == NULL) return EFI_INVALID_PARAMETER; lfs = FS_PRIVATE(this); info = LibFileInfo(LOCALFS_FD2F(fd)); if (info == NULL) return EFI_UNSUPPORTED; *sz = info->FileSize; uefi_call_wrapper(BS->FreePool, 1, info); return EFI_SUCCESS; } static EFI_STATUS localfs_seek(localfs_interface_t *this, UINTN fd, UINT64 newpos) { localfs_priv_state_t *lfs; if (this == NULL || fd == 0) return EFI_INVALID_PARAMETER; lfs = FS_PRIVATE(this); return uefi_call_wrapper(lfs->volume->SetPosition, 2, LOCALFS_FD2F(fd), newpos); } static VOID localfs_init_state(localfs_t *localfs, EFI_HANDLE dev, EFI_FILE_HANDLE volume) { localfs_priv_state_t *lfs = FS_PRIVATE(localfs); /* need to do some init here on localfs_intf */ Memset(localfs, 0, sizeof(*localfs)); localfs->pub_intf.localfs_name = localfs_name; localfs->pub_intf.localfs_open = localfs_open; localfs->pub_intf.localfs_read = localfs_read; localfs->pub_intf.localfs_close = localfs_close; localfs->pub_intf.localfs_infosize = localfs_infosize; localfs->pub_intf.localfs_seek = localfs_seek; lfs->dev = dev; lfs->volume = volume; } static EFI_STATUS localfs_install_one(EFI_HANDLE dev, VOID **intf) { EFI_STATUS status; localfs_t *localfs; EFI_FILE_IO_INTERFACE *volume; EFI_FILE_HANDLE volume_fh; status = uefi_call_wrapper(BS->HandleProtocol, 3, dev, &LocalFsProtocol, (VOID **)&localfs); if (status == EFI_SUCCESS) { ERR_PRT((L"Warning: found existing %s protocol on device", FS_NAME)); goto found; } status = uefi_call_wrapper(BS->HandleProtocol, 3, dev, &FileSystemProtocol, (VOID **)&volume); if (EFI_ERROR(status)) return EFI_INVALID_PARAMETER; status = uefi_call_wrapper(volume->OpenVolume, 2, volume, &volume_fh); if (EFI_ERROR(status)) { ERR_PRT((L"cannot open volume")); return status; } localfs = (localfs_t *)alloc(sizeof(*localfs), EfiLoaderData); if (localfs == NULL) { ERR_PRT((L"failed to allocate %s", FS_NAME)); return EFI_OUT_OF_RESOURCES; } localfs_init_state(localfs, dev, volume_fh); status = LibInstallProtocolInterfaces(&dev, &LocalFsProtocol, localfs, NULL); if (EFI_ERROR(status)) { ERR_PRT((L"Cannot install %s protocol: %r", FS_NAME, status)); free(localfs); return status; } found: if (intf) *intf = (VOID *)localfs; VERB_PRT(3, { EFI_DEVICE_PATH *dp; CHAR16 *str; dp = DevicePathFromHandle(dev); str = DevicePathToStr(dp); Print(L"attached %s to %s\n", FS_NAME, str); uefi_call_wrapper(BS->FreePool, 1, str); }); return EFI_SUCCESS; } EFI_STATUS localfs_install(VOID) { UINTN size = 0; UINTN i; EFI_STATUS status; VOID *intf; uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &FileSystemProtocol, NULL, &size, NULL); if (size == 0) return EFI_UNSUPPORTED; /* no device found, oh well */ DBG_PRT((L"size=%d", size)); dev_tab = (dev_tab_t *)alloc(size, EfiLoaderData); if (dev_tab == NULL) { ERR_PRT((L"failed to allocate handle table")); return EFI_OUT_OF_RESOURCES; } status = uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &FileSystemProtocol, NULL, &size, (VOID **)dev_tab); if (status != EFI_SUCCESS) { ERR_PRT((L"failed to get handles: %r", status)); free(dev_tab); return status; } ndev = size / sizeof(EFI_HANDLE); for(i=0; i < ndev; i++) { intf = NULL; localfs_install_one(dev_tab[i].dev, &intf); /* override device handle with interface pointer */ dev_tab[i].intf = intf; } return EFI_SUCCESS; } EFI_STATUS localfs_uninstall(VOID) { localfs_priv_state_t *lfs; EFI_STATUS status; UINTN i; for(i=0; i < ndev; i++) { if (dev_tab[i].intf == NULL) continue; lfs = FS_PRIVATE(dev_tab[i].intf); status = uefi_call_wrapper(BS->UninstallProtocolInterface, 3, lfs->dev, &LocalFsProtocol, dev_tab[i].intf); if (EFI_ERROR(status)) { ERR_PRT((L"Uninstall %s error: %r", FS_NAME, status)); continue; } VERB_PRT(3, { EFI_DEVICE_PATH *dp; CHAR16 *str; dp = DevicePathFromHandle(lfs->dev); str = DevicePathToStr(dp); Print(L"uninstalled %s on %s\n", FS_NAME, str); uefi_call_wrapper(BS->FreePool, 1, str); }); free(dev_tab[i].intf); } if (dev_tab) free(dev_tab); return EFI_SUCCESS; }