diff mbox series

[v4,2/2] lib: Support to deal with petitboot's configuration on efi-based platforms

Message ID 20180526035359.24024-3-ge.song@hxt-semitech.com
State Changes Requested
Headers show
Series Add support for arm64 efi-based platform | expand

Commit Message

Ge Song May 26, 2018, 3:53 a.m. UTC
Provide methods to load/store petitboot's configuration on efi-based
platforms. A test case is also provided.

Signed-off-by: Ge Song <ge.song@hxt-semitech.com>
---
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(-)

Comments

Sam Mendoza-Jonas May 30, 2018, 12:35 a.m. UTC | #1
On Sat, 2018-05-26 at 11:53 +0800, Ge Song wrote:
> Provide methods to load/store petitboot's configuration on efi-based
> platforms. A test case is also provided.
> 
> Signed-off-by: Ge Song <ge.song@hxt-semitech.com>
> ---
> 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.

Looks good!

> 
> 
>  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 <ge.song@hxt-semitech.com>
> + */
> +#ifndef EFIVAR_H
> +#define EFIVAR_H
> +
> +#include <linux/magic.h>
> +#include <stdint.h>
> +
> +#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 <ge.song@hxt-semitech.com>
> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/fs.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +
> +#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 <ge.song@hxt-semitech.com>
> + */
> +#include <assert.h>
> +#include <dirent.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/limits.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +#include <sys/statfs.h>
> +#include <unistd.h>
> +
> +#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;
> +}
Geoff Levand June 29, 2018, 12:13 a.m. UTC | #2
Hi Ge,

On 05/25/2018 08:53 PM, Ge Song wrote:
> +++ b/lib/efi/efivar.h> +#ifndef EFIVAR_H
> +#define EFIVAR_H
> 
> +#ifndef EFIVARFS_MAGIC
> +#define EFIVARFS_MAGIC 0xde5e81e4

Do we want to be supporting libc so old it does not have EFIVARFS_MAGIC?
Maybe emit an error here:

#error Upgrade libc to a newer version with EFIVARFS_MAGIC

> +#endif

> 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

The only use of ctx in the efivar.c functions is to attach temp variables
like path and buf.  We shouldn't be adding temp variables to contexts.
I think it better to not have ctx, then delete any temp variables on
function exit.

> +#include "talloc/talloc.h"
> +
> +const char *efivarfs_path = NULL;

efivarfs_path is a global, so this initialization is
unneeded.  It will be initialized to NULL.

> +
> +inline void set_efivarfs_path(const char *path)
> +int efi_get_variable(void *ctx, const char *guidstr, const char *name,
> +		uint8_t **data, size_t *data_size, uint32_t *attributes)

So use int efi_get_variable(const char *guidstr, ...)

> +{
> +	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);

Use talloc_asprintf(NULL, ...)

> +	if (!path)
> +		return ENOMEM;
> +
> +	fd = open(path, O_RDONLY|O_NONBLOCK);
> +	if (fd < 0)
> +		goto err;
> +
> +	buf = talloc_size(ctx, bufsize);

and talloc_size(path, ...)

> +	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:

then talloc_free(path)

> +	errno_value = errno;
> +	if (fd > 0)
> +		close(fd);
> +
> +	errno = errno_value;
> +	return rc;
> +}
diff mbox series

Patch

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 <ge.song@hxt-semitech.com>
+ */
+#ifndef EFIVAR_H
+#define EFIVAR_H
+
+#include <linux/magic.h>
+#include <stdint.h>
+
+#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 <ge.song@hxt-semitech.com>
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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 <ge.song@hxt-semitech.com>
+ */
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+
+#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;
+}