/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2004 AVM GmbH * * This program 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 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #ifdef CONFIG_SMP #define __SMP__ #endif /*--- #ifdef CONFIG_SMP ---*/ #include #include #include #include /*--- #include ---*/ #include #include #include #include #include #include #include #include #include #include #include #include #include /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ /*--- #define TFFS_DEBUG ---*/ #include #include #include "tffs_local.h" #include "tffs_efi.h" #define MAX_SEGMENT_SIZE (32 * 1024) #define MAX_ENV_ENTRY 256 /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static void send_notification(struct tffs_efi_ctx *ctx, enum _tffs_id Id, enum tffs3_notify_event evnt) { // pr_err("[%s-%d] Called.\n", __func__, __LINE__); if(ctx->notify_cb != NULL){ ctx->notify_cb(ctx->notify_priv, Id, evnt); } } static noinline int TFFS_get_name(struct tffs_efi_ctx *ctx, struct TFFS_EFI_State *state) { const struct _TFFS_Name_Table *nametable; const char *name; int result; result = -ENOENT; name = NULL; switch(state->id){ case FLASH_FS_NAME_TABLE: name = "tffs_nametable"; break; case 0: name = "dummy"; break; default: nametable = avm_urlader_get_nametable(); while(nametable != NULL && nametable->id != 0){ if(nametable->id == state->id && nametable->Name[0]){ name = nametable->Name; break; } ++nametable; } break; } if (name) { avm_efivar_resource_attach_var(state->res, name); result = 0; } else { pr_debug("[%s-%d] avm_urlader_env_get_variable() failed.\n", __func__, __LINE__); } return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static void *TFFS3_EFI_Open(struct tffs_module *this, struct tffs_core_handle *core_handle) { struct tffs_efi_ctx *ctx; struct TFFS_EFI_State *state; int result; ctx = (struct tffs_efi_ctx *) this->priv; core_handle->max_segment_size = EFIVARS_DATA_SIZE_MAX; // when opened in panic mode, use static state struct and if there is a special panic // mtd_info set up, use that one state = NULL; result = 0; if(core_handle->id != 0 && (core_handle->id <= FLASH_FS_ID_FIRMWARE_CONFIG_LAST || core_handle->id >= FLASH_FS_ID_LAST)) { result = -EINVAL; goto err_out; } state = kzalloc(sizeof(*state), GFP_KERNEL); if(state == NULL) { result = -ENOMEM; goto err_out; } state->offset = 0; state->id = core_handle->id; state->res = avm_efivar_resource_create(); if (IS_ERR(state->res)) { result = PTR_ERR(state->res); state->res = NULL; goto err_out; } result = TFFS_get_name(ctx, state); if(result != 0){ pr_debug("[%s] EFI name translation failed for ID 0x%x\n", __func__, state->id); goto err_out; } err_out: if(result != 0){ pr_debug("[%s] error: %d", __func__, result); if(!IS_ERR_OR_NULL(state)){ avm_efivar_resource_destroy(state->res); kfree(state); } state = ERR_PTR(result); } return state; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Close(struct tffs_module *this, void *handle) { struct tffs_efi_ctx *ctx; struct TFFS_EFI_State *state; // pr_err("[%s-%d] Called.\n", __func__, __LINE__); ctx = (struct tffs_efi_ctx *) this->priv; state = (struct TFFS_EFI_State *) handle; avm_efivar_resource_destroy(state->res); kfree(state); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Write(struct tffs_module *this, void *handle, uint8_t *write_buffer, size_t write_length, size_t *rlen, unsigned int final) { #if 1 struct tffs_efi_ctx *ctx; struct TFFS_EFI_State *state; int result; state = (struct TFFS_EFI_State *) handle; if(state->id >= FLASH_FS_ID_LAST){ pr_debug("[%s] invalid tffs_id: 0x%x\n", __func__, state->id); return -ENOENT; } // erasing an entry is always final if(write_length == 0 && write_buffer == NULL){ final = 1; } if(final == 0){ result = -ENOSPC; goto err_out; } ctx = (struct tffs_efi_ctx *)this->priv; if(ctx == NULL){ result = -EBADF; goto err_out; } if(write_buffer == NULL){ write_length = 0; } result = avm_efivar_set(state->res, NULL, write_buffer, write_length); /* * Entry was written. Send notification for ID */ if(result == 0){ send_notification(ctx, state->id, write_buffer ? tffs3_notify_update : tffs3_notify_clear); } #endif *rlen = write_length; err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Read(struct tffs_module *this, void *handle, uint8_t *read_buffer, size_t *read_length) { struct tffs_efi_ctx *ctx; struct TFFS_EFI_State *state; unsigned int result; size_t read_len; uint8_t *efidata; result = 0; ctx = (struct tffs_efi_ctx *)this->priv; if(ctx == NULL){ result = -EBADF; goto err_out; } state = (struct TFFS_EFI_State *) handle; /* set up handle state information and fill read buffer on first read */ if(state->offset == 0){ efidata = avm_efivar_get(state->res, NULL, &state->len); if (IS_ERR(efidata)) { result = PTR_ERR(efidata); goto err_out; } state->efidata = efidata; tffs_write_statistic(state->id, state->len, 0, 0); } if(state->len > state->offset){ read_len = min((state->len - state->offset), *read_length); memcpy(read_buffer, &state->efidata[state->offset], read_len); *read_length = read_len; state->offset += read_len; } else { *read_length = 0; } err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Reindex(struct tffs_module *this) { struct tffs_efi_ctx *ctx; int result; result = 0; ctx = (struct tffs_efi_ctx *)this->priv; if(ctx == NULL){ result = -EBADF; goto err_out; } err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Cleanup(struct tffs_module *this, void *handle) { struct tffs_efi_ctx *ctx; int result; result = 0; ctx = (struct tffs_efi_ctx *)this->priv; if(ctx == NULL){ pr_err("[%s] No context given.\n", __func__); result = -EBADF; goto err_out; } err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Info(struct tffs_module *this, unsigned int *Fill) { struct tffs_efi_ctx *ctx; ctx = (struct tffs_efi_ctx *) this->priv; tffs_write_statistic(FLASH_FS_ID_SKIP, sizeof(struct _TFFS_Entry), 0, 0); /*--- internal read ---*/ *Fill = 50; return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Register_Notify(struct tffs_module *this, void *notify_priv, tffs3_notify_fn notify_cb) { struct tffs_efi_ctx *ctx; int result; ctx = (struct tffs_efi_ctx *) this->priv; result = 0; if(ctx->notify_cb == NULL){ ctx->notify_priv = notify_priv; ctx->notify_cb = notify_cb; } else { result = -EEXIST; } return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Remove_Notify(struct tffs_module *this, void *notify_priv, tffs3_notify_fn notify_cb) { struct tffs_efi_ctx *ctx; int result; ctx = (struct tffs_efi_ctx *) this->priv; result = -EINVAL; if(ctx->notify_priv == notify_priv && ctx->notify_cb == notify_cb){ ctx->notify_cb = NULL; ctx->notify_priv = NULL; result = 0; } return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_EFI_Setup(struct tffs_module *this) { struct tffs_efi_ctx *ctx; int result; result = 0; if(this == NULL || this->priv == NULL){ pr_err("[%s] Module or context information missing!\n", __func__); result = -EINVAL; goto err_out; } ctx = (struct tffs_efi_ctx *) this->priv; err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_EFI_Configure(struct tffs_module *this) { struct tffs_efi_ctx *ctx; int result; result = -EINVAL; ctx = NULL; if(!efi_enabled(EFI_BOOT)){ pr_err("[%s] EFI services not available\n", __func__); result = -ENODEV; goto err_out; } ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if(ctx == NULL){ pr_err("[%s] Out of memory error\n", __func__); result = -ENOMEM; goto err_out; } this->name = "efi"; this->setup = TFFS3_EFI_Setup; this->open = TFFS3_EFI_Open; this->close = TFFS3_EFI_Close; this->read = TFFS3_EFI_Read; this->write = TFFS3_EFI_Write; this->cleanup = TFFS3_EFI_Cleanup; this->reindex = TFFS3_EFI_Reindex; this->info = TFFS3_EFI_Info; this->register_notify = TFFS3_EFI_Register_Notify; this->remove_notify = TFFS3_EFI_Remove_Notify; this->priv = ctx; smp_mb(); result = 0; err_out: if(result != 0 && !IS_ERR_OR_NULL(ctx)){ kfree(ctx); } return result; } EXPORT_SYMBOL(TFFS3_EFI_Configure);