/*------------------------------------------------------------------------------------------*\ * * 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 #include #include #include /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ /*--- #define TFFS_DEBUG ---*/ #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 ascii2utf(struct tffs_efi_ctx *ctx, char *ascii, efi_char16_t *utf, size_t len) { unsigned int i; int result; // pr_err("[%s-%d] Called. ctx: %p nls: %p ascii: %s len: %d\n", __func__, __LINE__, ctx, ctx->nls_tbl, ascii, len); result = 0; i = 0; while(ascii[i] != '\0' && len > 0){ #if 1 result = ctx->nls_tbl->char2uni(&(ascii[i]), 42, &(utf[i])); if(result < 0){ goto err_out; } #else utf[i] = ascii[i]; #endif ++i; --len; } utf[i] = '\0'; // pr_err("[%s-%d] Called. utf: %ls len: %d\n", __func__, __LINE__, utf, len); err_out: return result; } static noinline int TFFS_get_name(struct tffs_efi_ctx *ctx, struct TFFS_EFI_State *state) { char *name; int result, status; const struct _TFFS_Name_Table *nametable; result = -ENOENT; name = NULL; switch(state->id){ case FLASH_FS_NAME_TABLE: name = kstrdup("tffs_nametable", GFP_ATOMIC); break; case 0: name = kstrdup("dummy", GFP_ATOMIC); break; default: nametable = avm_urlader_get_nametable(); while(nametable != NULL && nametable->id != 0){ if(nametable->id == state->id && nametable->Name[0]){ name = kmalloc(strlen(nametable->Name) + 1, GFP_KERNEL); if(name == NULL ){ result = -ENOMEM; goto err_out; } strcpy(name, nametable->Name); result = 0; break; } ++nametable; } break; } if(!IS_ERR_OR_NULL(name)){ status = ascii2utf(ctx, name, state->entry.var.VariableName, ARRAY_SIZE(state->entry.var.VariableName)); result = (status < 0) ? status : 0; } else { pr_debug("[%s-%d] avm_urlader_env_get_variable() failed.\n", __func__, __LINE__); } err_out: if(!IS_ERR_OR_NULL(name)){ kfree(name); } 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) { pr_debug("[%s] malloc(%u) failed\n", __func__, sizeof(*state)); result = -ENOMEM; goto err_out; } state->offset = 0; state->id = core_handle->id; state->entry.var.VendorGuid = EFI_TFFS_TOKENSPACE; 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){ if(!IS_ERR_OR_NULL(state)){ 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; 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 = efivar_entry_set_safe(state->entry.var.VariableName, EFI_TFFS_TOKENSPACE, (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS), true, write_length, write_buffer); /* * 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; struct efivar_entry *entry; 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){ entry = &(state->entry); entry->var.DataSize = sizeof(entry->var.Data); result = efivar_entry_get(entry, &(entry->var.Attributes), &(entry->var.DataSize), &(entry->var.Data[0])); if(result != 0){ goto err_out; } state->len = entry->var.DataSize; tffs_write_statistic(state->id, entry->var.DataSize, 0, 0); } if(state->len > state->offset){ read_len = min((state->len - state->offset), *read_length); memcpy(read_buffer, &(state->entry.var.Data[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; } ctx->nls_tbl = load_nls("ascii"); if(ctx->nls_tbl == NULL){ pr_err("[%s] Unable to load NLS table\n", __func__); result = -ENODEV; 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);