From patchwork Tue Jun 5 02:14:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ge Song X-Patchwork-Id: 925293 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 410G0m0nwKz9s2S for ; Tue, 5 Jun 2018 12:27:20 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Pn2fGJPG"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 410G0l6BxgzF158 for ; Tue, 5 Jun 2018 12:27:19 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Pn2fGJPG"; dkim-atps=neutral X-Original-To: petitboot@lists.ozlabs.org Delivered-To: petitboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:400e:c00::22a; helo=mail-pf0-x22a.google.com; envelope-from=songgebird@gmail.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Pn2fGJPG"; dkim-atps=neutral Received: from mail-pf0-x22a.google.com (mail-pf0-x22a.google.com [IPv6:2607:f8b0:400e:c00::22a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 410Fnr0KyCzF1SJ for ; Tue, 5 Jun 2018 12:17:51 +1000 (AEST) Received: by mail-pf0-x22a.google.com with SMTP id q1-v6so414480pff.13 for ; Mon, 04 Jun 2018 19:17:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=t/gkwnK9TsP7HHkLf8u9T4xU2D/Qw8ZVbxknkXOdO9E=; b=Pn2fGJPGNbfnL03rUAelrGwsVFa7vYaLpL1+nDAeK8a8dHzZtjnyOyT8QmCFPIjzbZ uXpy03bLKSTI/H/fDyOpE4Kfyf+qvor5l2rKYExl1l2NUodSiEPGBCB5I3jel0XC0+BL WbAAmqSUXxkILvRrOS0nzu22THyFKUC/IV60btlhwTRdzDqwFWVuWI4tH++4A6jKEVEV Ma5dz6M0R6lIq97SqW+TGmiPMdrVBbHO5qLXB58iYMQVAr5iIQ77th7OmMe7uStL+eLQ tk5ucXac+EqTbXD9tYZyDh/m9TB35szrNSSSbKgosdb+nZLPdpd4dIpuUkoRkLXIYG5x RO1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=t/gkwnK9TsP7HHkLf8u9T4xU2D/Qw8ZVbxknkXOdO9E=; b=jj9fxyP8xrdarpLeZu6qmRaqLBa13WHvTc7hMH0MBsaLwuSzZvFNlBWAf0Uj59kjVB 2mETSNIp6C5jsjIijwC6xOgFHrsScXnECRzs7Fj2cVR4OH+4v4g/GPjf6PPGgJyETQNE CDinHa5kgnA4RMpS5Z1+kayzrgATnYDCmBYbC1GT9UwyJhl4X/OH2As8/fDoMwEHYEZD u69ScxlPjJj+yI7G+sis10nJluOY1reuRY7MMZGZ9gHj91zMsUUw8GKk5VTStJtzUdDW Z2dXcLTR/y3wps/4mXRwcoFUm4GFM2RNncw8uzUhJ3Exn1ukPgdqxWx9yjYIwQUuhi5w pCPA== X-Gm-Message-State: ALKqPwfA1iway0m7A+cm4udPMd11O6GdvQlv8Lz+0NW0CoPsDTBTrDN0 ddQoRVfQDxFO8ROCLocmp6aLNaoc X-Google-Smtp-Source: ADUXVKJiZfIfBVqOdSC6BKvavoBpm8ysN3KET0MyJD8vDhYHGsVrHnDw9wukZu5i/EMbAu3KKWKhlA== X-Received: by 2002:a63:7707:: with SMTP id s7-v6mr14982873pgc.426.1528165069780; Mon, 04 Jun 2018 19:17:49 -0700 (PDT) Received: from vbox.hxtcorp.net ([180.173.110.177]) by smtp.gmail.com with ESMTPSA id f11-v6sm4195207pgq.18.2018.06.04.19.17.48 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 04 Jun 2018 19:17:49 -0700 (PDT) From: Ge Song X-Google-Original-From: Ge Song To: petitboot@lists.ozlabs.org Subject: [PATCH v5 1/2] lib: Support to deal with petitboot's configuration on efi-based platforms Date: Tue, 5 Jun 2018 10:14:47 +0800 Message-Id: <20180605021448.9064-2-ge.song@hxt-semitech.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180605021448.9064-1-ge.song@hxt-semitech.com> References: <20180605021448.9064-1-ge.song@hxt-semitech.com> X-BeenThere: petitboot@lists.ozlabs.org X-Mailman-Version: 2.1.26 Precedence: list List-Id: Petitboot bootloader development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Ge Song MIME-Version: 1.0 Errors-To: petitboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Petitboot" Provide methods to load/store petitboot's configuration on efi-based platforms. A test case is also provided. Signed-off-by: Ge Song --- Since V5: * Has no modification on V4. Since V4: * test-efivar.c: Use a test directory other than the system's efivarfs to * complete the test for efivar lib. * lib/efi: Add API to support caller to pass a custom efivarfs path. lib/Makefile.am | 2 + test/lib/Makefile.am | 3 +- lib/efi/efivar.h | 46 +++++ lib/efi/efivar.c | 204 ++++++++++++++++++++ test/lib/test-efivar.c | 127 ++++++++++++ 5 files changed, 381 insertions(+), 1 deletion(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 8f682021d45a..cfaa0d2cad47 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -59,6 +59,8 @@ lib_libpbcore_la_SOURCES = \ lib/util/util.h \ lib/flash/config.h \ lib/flash/flash.h \ + lib/efi/efivar.h \ + lib/efi/efivar.c \ $(gpg_int_SOURCES) if ENABLE_MTD diff --git a/test/lib/Makefile.am b/test/lib/Makefile.am index 9636b08d6a6b..22e3ac91499d 100644 --- a/test/lib/Makefile.am +++ b/test/lib/Makefile.am @@ -23,7 +23,8 @@ lib_TESTS = \ test/lib/test-process-parent-stdout \ test/lib/test-process-both \ test/lib/test-process-stdout-eintr \ - test/lib/test-fold + test/lib/test-fold \ + test/lib/test-efivar $(lib_TESTS): LIBS += $(core_lib) diff --git a/lib/efi/efivar.h b/lib/efi/efivar.h new file mode 100644 index 000000000000..058b196e5050 --- /dev/null +++ b/lib/efi/efivar.h @@ -0,0 +1,46 @@ +/* + * 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; version 2 of the License. + * + * 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 + * + * Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights + * reserved. + * Author: Ge Song + */ +#ifndef EFIVAR_H +#define EFIVAR_H + +#include +#include + +#define EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008 +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020 +#define EFI_VARIABLE_APPEND_WRITE 0x00000040 + +#ifndef EFIVARFS_MAGIC +#define EFIVARFS_MAGIC 0xde5e81e4 +#endif + +extern const char *efivarfs_path; + +void set_efivarfs_path(const char *path); +const char *get_efivarfs_path(void); +int efi_get_variable(void *ctx, const char *guidstr, const char *name, + uint8_t **data, size_t *data_size, uint32_t *attributes); +int efi_set_variable(void *ctx, const char *guidstr, const char *name, + uint8_t *data, size_t data_size, uint32_t attributes); +int efi_del_variable(void *ctx, const char *guidstr, const char *name); +#endif /* EFIVAR_H */ diff --git a/lib/efi/efivar.c b/lib/efi/efivar.c new file mode 100644 index 000000000000..8e6fce820bfb --- /dev/null +++ b/lib/efi/efivar.c @@ -0,0 +1,204 @@ +/* + * Methods to manipulation of EFI variables on UEFI-based platforms + * + * The library relies on pesudo file system named "efivarfs". This is the + * new interface to manipulate EFI varibles and was part of the linux + * kernel v3.10 release. + * + * There is also a legacy efivars interface exported via the path + * "firmware/efi/vars/" relative to the mount point of sysfs. Since it has + * some drawbacks and is deprecated, we don't support this interface. + * + * 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; version 2 of the License. + * + * 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 + * + * Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights + * reserved. + * Author: Ge Song + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "efivar.h" +#include "talloc/talloc.h" + +const char *efivarfs_path = NULL; + +inline void set_efivarfs_path(const char *path) +{ + efivarfs_path = path; +} + +inline const char *get_efivarfs_path(void) +{ + + return efivarfs_path; +} + +int efi_del_variable(void *ctx, const char *guidstr, + const char *name) +{ + int fd, flag, errno_value; + int rc = -1; + const char *dir; + char *path; + + dir = get_efivarfs_path(); + if (!dir) + return EFAULT; + + path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr); + if (!path) + return ENOMEM; + + fd = open(path, O_RDONLY|O_NONBLOCK); + if (fd == -1) + goto err; + + rc = ioctl(fd, FS_IOC_GETFLAGS, &flag); + if (rc == -1) + goto err; + + flag &= ~FS_IMMUTABLE_FL; + rc = ioctl(fd, FS_IOC_SETFLAGS, &flag); + if (rc == -1) + goto err; + + close(fd); + rc = unlink(path); + +err: + errno_value = errno; + if (fd > 0) + close(fd); + + errno = errno_value; + return rc; +} + +int efi_get_variable(void *ctx, const char *guidstr, const char *name, + uint8_t **data, size_t *data_size, uint32_t *attributes) +{ + int fd, errno_value; + int rc = -1; + void *p, *buf; + size_t bufsize = 4096; + size_t filesize = 0; + ssize_t sz; + const char *dir; + char *path; + + dir = get_efivarfs_path(); + if (!dir) + return EFAULT; + + path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr); + if (!path) + return ENOMEM; + + fd = open(path, O_RDONLY|O_NONBLOCK); + if (fd < 0) + goto err; + + buf = talloc_size(ctx, bufsize); + if (!buf) + goto err; + + do { + p = buf + filesize; + sz = read(fd, p, bufsize); + if (sz < 0 && errno == EAGAIN) { + continue; + } else if (sz == 0) { + break; + } + filesize += sz; + } while (1); + + *attributes = *(uint32_t *)buf; + *data = (uint8_t *)(buf + sizeof(uint32_t)); + *data_size = strlen(buf + sizeof(uint32_t)); + rc = 0; + +err: + errno_value = errno; + if (fd > 0) + close(fd); + + errno = errno_value; + return rc; +} + +int efi_set_variable(void *ctx, const char *guidstr, const char *name, + uint8_t *data, size_t data_size, uint32_t attributes) +{ + int rc = -1, errno_value; + int fd = -1; + ssize_t len; + const char *dir; + char *path; + void *buf; + size_t bufsize; + mode_t mask = 0644; + + dir = get_efivarfs_path(); + if (!dir) + return EFAULT; + + path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr); + if (!path) + return ENOMEM; + + if (!access(path, F_OK)) { + rc = efi_del_variable(ctx, guidstr, name); + if (rc < 0) { + goto err; + } + } + + fd = open(path, O_CREAT|O_WRONLY, mask); + if (fd < 0) + goto err; + + bufsize = sizeof(uint32_t) + data_size; + buf = talloc_size(ctx, bufsize); + if (!buf) + goto err; + + *(uint32_t *)buf = attributes; + memcpy(buf + sizeof(uint32_t), data, data_size); + + len = write(fd, buf, bufsize); + if ((size_t)len != bufsize) + goto err; + else + rc = 0; + +err: + errno_value = errno; + if (fd > 0) + close(fd); + + errno = errno_value; + return rc; +} diff --git a/test/lib/test-efivar.c b/test/lib/test-efivar.c new file mode 100644 index 000000000000..8ceb8f50cae9 --- /dev/null +++ b/test/lib/test-efivar.c @@ -0,0 +1,127 @@ +/* + * 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; version 2 of the License. + * + * 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 + * + * Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights + * reserved. + * Author: Ge Song + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "efi/efivar.h" +#include "talloc/talloc.h" + +#define DEF_ATTR (EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) + +static const char *test_efivar_guid = "c9c07add-256e-4452-b911-f8d0d35a1ac7"; +static const char *test_varname = "efivartest"; +static const char *test_data = "petitboot"; + +static char* find_efitest_path(void) +{ + static char dir[PATH_MAX] = {0}; + static bool run = false; + char *rest_path = "/efivarfs_data/"; + char *pos = NULL; + + if (run) + return dir; + + readlink("/proc/self/exe", dir, PATH_MAX); + + pos = strrchr(dir, '/'); + *pos = '\0'; + + strcat(dir, rest_path); + run = true; + + return dir; +} + +static bool probe(void) +{ + char *path; + int rc; + + path = find_efitest_path(); + + rc = access(path, F_OK); + if (rc) { + if (errno == ENOENT) { + rc = mkdir(path, 0755); + if(rc) + return false; + } else { + return false; + } + } + + set_efivarfs_path(path); + + return true; +} + +int main(void) +{ + void *ctx = NULL; + int rc, errno_value; + size_t size; + uint8_t *data = NULL; + uint32_t attr = DEF_ATTR; + char *path = NULL; + + if(!probe()) + return ENOENT; + + talloc_new(ctx); + size = strlen(test_data) + 1; + rc = efi_set_variable(ctx, test_efivar_guid, test_varname, + (uint8_t *)test_data, size, attr); + + rc = efi_get_variable(ctx, test_efivar_guid, test_varname, + &data, &size, &attr); + + assert(data != NULL); + rc = strcmp((char *)data, test_data); + if (rc) { + talloc_free(ctx); + assert(0); + } + + rc = efi_del_variable(ctx, test_efivar_guid, test_varname); + + rc = efi_get_variable(ctx, test_efivar_guid, test_varname, + &data, &size, &attr); + + errno_value = errno; + talloc_free(ctx); + + assert(errno_value == ENOENT); + + path = find_efitest_path(); + rmdir(path); + + return EXIT_SUCCESS; +}