diff mbox series

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

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

Commit Message

Ge Song April 9, 2018, 2:22 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>
---
 lib/Makefile.am        |   2 +
 test/lib/Makefile.am   |   3 +-
 lib/efi/efivar.h       |  43 +++++
 lib/efi/efivar.c       | 179 ++++++++++++++++++++
 test/lib/test-efivar.c |  68 ++++++++
 5 files changed, 294 insertions(+), 1 deletion(-)

Comments

Sam Mendoza-Jonas April 30, 2018, 4:29 a.m. UTC | #1
On Mon, 2018-04-09 at 10:22 +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>
> ---
>  lib/Makefile.am        |   2 +
>  test/lib/Makefile.am   |   3 +-
>  lib/efi/efivar.h       |  43 +++++
>  lib/efi/efivar.c       | 179 ++++++++++++++++++++
>  test/lib/test-efivar.c |  68 ++++++++
>  5 files changed, 294 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/Makefile.am b/lib/Makefile.am
> index bb7dfe489132..50fb9d14df49 100644
> --- a/lib/Makefile.am
> +++ b/lib/Makefile.am
> @@ -58,6 +58,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..da0e0d68b6c8
> --- /dev/null
> +++ b/lib/efi/efivar.h
> @@ -0,0 +1,43 @@
> +/*
> + *  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 <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
> +
> +#define EFIVARFS_MAGIC 0xde5e81e4
> +
> +static inline const char* get_efivarfs_path(void)
> +{
> +	return "/sys/firmware/efi/efivars/";
> +}
> +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..5ce3464b9e77
> --- /dev/null
> +++ b/lib/efi/efivar.c
> @@ -0,0 +1,179 @@
> +/*
> + *  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"
> +
> +int efi_del_variable(void *ctx, const char *guidstr,
> +		const char *name)
> +{
> +	int fd, flag, errno_value;
> +	int rc = -1;
> +	char *path;
> +
> +	path = talloc_asprintf(ctx, "%s%s-%s",
> +				get_efivarfs_path(), 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;
> +	char *path;
> +
> +	path = talloc_asprintf(ctx, "%s%s-%s",
> +				get_efivarfs_path(), name, guidstr);
> +	if (!path)
> +		return ENOMEM;
> +
> +	fd = open(path, O_RDONLY);
> +	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));

I'm a little unsure about this strlen() call. Why not just find the variable
size with stat()? Here it looks like we're trying to read the file as a string
but are we sure it will be null-terminated?

> +	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;
> +	char *path;
> +	void *buf;
> +	size_t bufsize;
> +	mode_t mask = umask(umask(0));
> +
> +	path = talloc_asprintf(ctx, "%s%s-%s",
> +				get_efivarfs_path(), 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..aec2549c4993
> --- /dev/null
> +++ b/test/lib/test-efivar.c
> @@ -0,0 +1,68 @@
> +/*
> + *  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 <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.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";
> +
> +int main(int argc, char *argv[])
> +{

This test case fails because argc and argv are unused:

../test/lib/test-efivar.c: In function ‘main’:
../test/lib/test-efivar.c:36:14: error: unused parameter ‘argc’ [-Werror=unused-parameter]
 int main(int argc, char *argv[])
              ^~~~
../test/lib/test-efivar.c:36:26: error: unused parameter ‘argv’ [-Werror=unused-parameter]
 int main(int argc, char *argv[])
                          ^~~~

However after fixing that the test actually segfaults below on the
strcmp():

> +	void *ctx = NULL;
> +	int rc, errno_value;
> +	size_t size;
> +	uint8_t *data = NULL;
> +	uint32_t attr = DEF_ATTR;
> +
> +	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);
> +
> +	rc = strcmp((char *)data, test_data);

gdb ./test/lib/test-efivar
...
(gdb) run
Starting program: /home/sam/dev/petitboot/patches/out/test/lib/test-efivar

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7aaf67a in __strcmp_sse2_unaligned () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff7aaf67a in __strcmp_sse2_unaligned () from /usr/lib/libc.so.6
#1  0x0000555555554e2e in main () at ../test/lib/test-efivar.c:54

(gdb) p data
$1 = (uint8_t *) 0x0

> +	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);
> +
> +	return EXIT_SUCCESS;
> +}
Ge Song May 1, 2018, 9:07 a.m. UTC | #2
On 04/30/2018 12:29 PM, Samuel Mendoza-Jonas wrote:

> On Mon, 2018-04-09 at 10:22 +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>
>> ---
>>   lib/Makefile.am        |   2 +
>>   test/lib/Makefile.am   |   3 +-
>>   lib/efi/efivar.h       |  43 +++++
>>   lib/efi/efivar.c       | 179 ++++++++++++++++++++
>>   test/lib/test-efivar.c |  68 ++++++++
>>   5 files changed, 294 insertions(+), 1 deletion(-)
>>
>> diff --git a/lib/Makefile.am b/lib/Makefile.am
>> index bb7dfe489132..50fb9d14df49 100644
>> --- a/lib/Makefile.am
>> +++ b/lib/Makefile.am
>> @@ -58,6 +58,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..da0e0d68b6c8
>> --- /dev/null
>> +++ b/lib/efi/efivar.h
>> @@ -0,0 +1,43 @@
>> +/*
>> + *  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 <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
>> +
>> +#define EFIVARFS_MAGIC 0xde5e81e4
>> +
>> +static inline const char* get_efivarfs_path(void)
>> +{
>> +	return "/sys/firmware/efi/efivars/";
>> +}
>> +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..5ce3464b9e77
>> --- /dev/null
>> +++ b/lib/efi/efivar.c
>> @@ -0,0 +1,179 @@
>> +/*
>> + *  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"
>> +
>> +int efi_del_variable(void *ctx, const char *guidstr,
>> +		const char *name)
>> +{
>> +	int fd, flag, errno_value;
>> +	int rc = -1;
>> +	char *path;
>> +
>> +	path = talloc_asprintf(ctx, "%s%s-%s",
>> +				get_efivarfs_path(), 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;
>> +	char *path;
>> +
>> +	path = talloc_asprintf(ctx, "%s%s-%s",
>> +				get_efivarfs_path(), name, guidstr);
>> +	if (!path)
>> +		return ENOMEM;
>> +
>> +	fd = open(path, O_RDONLY);
>> +	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));
> I'm a little unsure about this strlen() call. Why not just find the variable
> size with stat()? Here it looks like we're trying to read the file as a string
> but are we sure it will be null-terminated?
Yes, you are right. In platform-powerpc.c, value of each parameter is 
stored in the form of
string(even mac address, http proxy ...). To be consistent with it and 
reuse the routings,
the same manner is used.

Regard to efi variable, four elements are cared: attribute, guid(can 
think of this as namespace), data and
data size. Since value of each parameter is stored as a 
string(null-terminated), when we set it,
we set the data size as "strlen(string) + 1" to make '\0' is also stored 
in efi var. When we read it,
efivarfs will return the string(with '\0' in the end) in the buffer.

The size returned from stat() seems incorrect, since the 
inode->getattr() is not implemented in efivarfs.
>
>> +	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;
>> +	char *path;
>> +	void *buf;
>> +	size_t bufsize;
>> +	mode_t mask = umask(umask(0));
>> +
>> +	path = talloc_asprintf(ctx, "%s%s-%s",
>> +				get_efivarfs_path(), 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..aec2549c4993
>> --- /dev/null
>> +++ b/test/lib/test-efivar.c
>> @@ -0,0 +1,68 @@
>> +/*
>> + *  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 <errno.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.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";
>> +
>> +int main(int argc, char *argv[])
>> +{
> This test case fails because argc and argv are unused:
>
> ../test/lib/test-efivar.c: In function ‘main’:
> ../test/lib/test-efivar.c:36:14: error: unused parameter ‘argc’ [-Werror=unused-parameter]
>   int main(int argc, char *argv[])
>                ^~~~
> ../test/lib/test-efivar.c:36:26: error: unused parameter ‘argv’ [-Werror=unused-parameter]
>   int main(int argc, char *argv[])
>                            ^~~~
>
> However after fixing that the test actually segfaults below on the
> strcmp():
Sorry, the prototype of main() will be correct in next patch.
Initially the test case was designed to get the parameters from command 
line, after
some thought it is not a good solution since one has to spend time to 
find out
the right parameters to be passed.
>> +	void *ctx = NULL;
>> +	int rc, errno_value;
>> +	size_t size;
>> +	uint8_t *data = NULL;
>> +	uint32_t attr = DEF_ATTR;
>> +
>> +	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);
>> +
>> +	rc = strcmp((char *)data, test_data);
> gdb ./test/lib/test-efivar
> ...
> (gdb) run
> Starting program: /home/sam/dev/petitboot/patches/out/test/lib/test-efivar
>
> Program received signal SIGSEGV, Segmentation fault.
> 0x00007ffff7aaf67a in __strcmp_sse2_unaligned () from /usr/lib/libc.so.6
> (gdb) bt
> #0  0x00007ffff7aaf67a in __strcmp_sse2_unaligned () from /usr/lib/libc.so.6
> #1  0x0000555555554e2e in main () at ../test/lib/test-efivar.c:54
>
> (gdb) p data
> $1 = (uint8_t *) 0x0
Sorry for this, I made a mistake, a probe()(as probe() in 
platform-xxx.c) is needed to judge
if efivarfs is already mounted prior to all the operations. In fact I 
debugged it in gdb too,
because efivarfs is mounted in my system automatically, it failed to 
detect the issue.

BTW, I'm not familiar with the test rules. If probe() failed, which 
return code is reasonable? Or
EXIT_SUCCESS is enough?
>
>> +	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);
>> +
>> +	return EXIT_SUCCESS;
>> +}
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p>On 04/30/2018 12:29 PM, Samuel Mendoza-Jonas wrote:<br>
    </p>
    <blockquote type="cite"
cite="mid:e38046ef0ed3560d0ea856efcc7a71ba8ccc7021.camel@mendozajonas.com">
      <pre wrap="">On Mon, 2018-04-09 at 10:22 +0800, Ge Song wrote:
</pre>
      <blockquote type="cite">
        <pre wrap="">Provide methods to load/store petitboot's configuration on efi-based
platforms. A test case is also provided.

Signed-off-by: Ge Song <a class="moz-txt-link-rfc2396E" href="mailto:ge.song@hxt-semitech.com">&lt;ge.song@hxt-semitech.com&gt;</a>
---
 lib/Makefile.am        |   2 +
 test/lib/Makefile.am   |   3 +-
 lib/efi/efivar.h       |  43 +++++
 lib/efi/efivar.c       | 179 ++++++++++++++++++++
 test/lib/test-efivar.c |  68 ++++++++
 5 files changed, 294 insertions(+), 1 deletion(-)

diff --git a/lib/Makefile.am b/lib/Makefile.am
index bb7dfe489132..50fb9d14df49 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -58,6 +58,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..da0e0d68b6c8
--- /dev/null
+++ b/lib/efi/efivar.h
@@ -0,0 +1,43 @@
+/*
+ *  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 <a class="moz-txt-link-rfc2396E" href="mailto:ge.song@hxt-semitech.com">&lt;ge.song@hxt-semitech.com&gt;</a>
+ */
+#ifndef EFIVAR_H
+#define EFIVAR_H
+
+#include &lt;stdint.h&gt;
+
+#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
+
+#define EFIVARFS_MAGIC 0xde5e81e4
+
+static inline const char* get_efivarfs_path(void)
+{
+	return "/sys/firmware/efi/efivars/";
+}
+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..5ce3464b9e77
--- /dev/null
+++ b/lib/efi/efivar.c
@@ -0,0 +1,179 @@
+/*
+ *  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 <a class="moz-txt-link-rfc2396E" href="mailto:ge.song@hxt-semitech.com">&lt;ge.song@hxt-semitech.com&gt;</a>
+ */
+
+#include &lt;errno.h&gt;
+#include &lt;fcntl.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;stdbool.h&gt;
+#include &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+#include &lt;sys/ioctl.h&gt;
+#include &lt;sys/stat.h&gt;
+#include &lt;sys/types.h&gt;
+#include &lt;unistd.h&gt;
+
+#include "efivar.h"
+#include "talloc/talloc.h"
+
+int efi_del_variable(void *ctx, const char *guidstr,
+		const char *name)
+{
+	int fd, flag, errno_value;
+	int rc = -1;
+	char *path;
+
+	path = talloc_asprintf(ctx, "%s%s-%s",
+				get_efivarfs_path(), name, guidstr);
+	if (!path)
+		return ENOMEM;
+
+	fd = open(path, O_RDONLY|O_NONBLOCK);
+	if (fd == -1)
+		goto err;
+
+	rc = ioctl(fd, FS_IOC_GETFLAGS, &amp;flag);
+	if (rc == -1)
+		goto err;
+
+	flag &amp;= ~FS_IMMUTABLE_FL;
+	rc = ioctl(fd, FS_IOC_SETFLAGS, &amp;flag);
+	if (rc == -1)
+		goto err;
+
+	close(fd);
+	rc = unlink(path);
+
+err:
+	errno_value = errno;
+	if (fd &gt; 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;
+	char *path;
+
+	path = talloc_asprintf(ctx, "%s%s-%s",
+				get_efivarfs_path(), name, guidstr);
+	if (!path)
+		return ENOMEM;
+
+	fd = open(path, O_RDONLY);
+	if (fd &lt; 0)
+		goto err;
+
+	buf = talloc_size(ctx, bufsize);
+	if (!buf)
+		goto err;
+
+	do {
+		p = buf + filesize;
+		sz = read(fd, p, bufsize);
+		if (sz &lt; 0 &amp;&amp; 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));
</pre>
      </blockquote>
      <pre wrap="">
I'm a little unsure about this strlen() call. Why not just find the variable
size with stat()? Here it looks like we're trying to read the file as a string
but are we sure it will be null-terminated?</pre>
    </blockquote>
    <font face="Courier New, Courier, monospace"><font face="Courier 10
        Pitch"><font face="Courier 10 Pitch"><font face="Times New
            Roman, Times, serif">Yes, you are right. In
            platform-powerpc.c, value of each parameter is stored in the
            form of<br>
            string(even mac address, http proxy ...). To be consistent
            with it and reuse the routings,<br>
            the same manner is used.<br>
            <br>
            Regard to efi variable, four elements are cared: attribute,
            guid(can think of this as namespace), data and<br>
            data size. Since value of each parameter is stored as a
            string(null-terminated), when we set it,<br>
            we set the data size as "strlen(string) + 1" to make '\0' is
            also stored in efi var. When we read it,<br>
            efivarfs will return the string(with '\0' in the end) in the
            buffer.<br>
            <br>
            The size returned from stat() seems incorrect, since the
            inode-&gt;getattr() is not implemented in efivarfs.<br>
          </font></font></font></font>
    <blockquote type="cite"
cite="mid:e38046ef0ed3560d0ea856efcc7a71ba8ccc7021.camel@mendozajonas.com">
      <pre wrap="">

</pre>
      <blockquote type="cite">
        <pre wrap="">+	rc = 0;
+
+err:
+	errno_value = errno;
+	if (fd &gt; 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;
+	char *path;
+	void *buf;
+	size_t bufsize;
+	mode_t mask = umask(umask(0));
+
+	path = talloc_asprintf(ctx, "%s%s-%s",
+				get_efivarfs_path(), name, guidstr);
+	if (!path)
+		return ENOMEM;
+
+	if (!access(path, F_OK)) {
+		rc = efi_del_variable(ctx, guidstr, name);
+		if (rc &lt; 0) {
+			goto err;
+		}
+	}
+
+	fd = open(path, O_CREAT|O_WRONLY, mask);
+	if (fd &lt; 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 &gt; 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..aec2549c4993
--- /dev/null
+++ b/test/lib/test-efivar.c
@@ -0,0 +1,68 @@
+/*
+ *  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 <a class="moz-txt-link-rfc2396E" href="mailto:ge.song@hxt-semitech.com">&lt;ge.song@hxt-semitech.com&gt;</a>
+ */
+#include &lt;assert.h&gt;
+#include &lt;errno.h&gt;
+#include &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+
+#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";
+
+int main(int argc, char *argv[])
+{
</pre>
      </blockquote>
      <pre wrap="">
This test case fails because argc and argv are unused:

../test/lib/test-efivar.c: In function ‘main’:
../test/lib/test-efivar.c:36:14: error: unused parameter ‘argc’ [-Werror=unused-parameter]
 int main(int argc, char *argv[])
              ^~~~
../test/lib/test-efivar.c:36:26: error: unused parameter ‘argv’ [-Werror=unused-parameter]
 int main(int argc, char *argv[])
                          ^~~~

However after fixing that the test actually segfaults below on the
strcmp():
</pre>
    </blockquote>
    <font face="Times New Roman, Times, serif">Sorry, the prototype of
      main() will be correct in next patch.<br>
      Initially the test case was designed to get the parameters from
      command line, after<br>
      some thought it is not a good solution since one has to spend time
      to find out<br>
      the right parameters to be passed.   </font><br>
    <blockquote type="cite"
cite="mid:e38046ef0ed3560d0ea856efcc7a71ba8ccc7021.camel@mendozajonas.com">
      <pre wrap="">
</pre>
      <blockquote type="cite">
        <pre wrap="">+	void *ctx = NULL;
+	int rc, errno_value;
+	size_t size;
+	uint8_t *data = NULL;
+	uint32_t attr = DEF_ATTR;
+
+	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,
+				&amp;data, &amp;size, &amp;attr);
+
+	rc = strcmp((char *)data, test_data);
</pre>
      </blockquote>
      <pre wrap="">
gdb ./test/lib/test-efivar
...
(gdb) run
Starting program: /home/sam/dev/petitboot/patches/out/test/lib/test-efivar

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7aaf67a in __strcmp_sse2_unaligned () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff7aaf67a in __strcmp_sse2_unaligned () from /usr/lib/libc.so.6
#1  0x0000555555554e2e in main () at ../test/lib/test-efivar.c:54

(gdb) p data
$1 = (uint8_t *) 0x0</pre>
    </blockquote>
    <font face="Times New Roman, Times, serif">Sorry for this, I made a
      mistake, a probe()(as probe() in platform-xxx.c) is needed to
      judge<br>
      if efivarfs is already mounted prior to all the operations. In
      fact I debugged it in gdb too,<br>
      because efivarfs is mounted in my system automatically, it failed
      to detect the issue.<br>
      <br>
      BTW, I'm not familiar with the test rules. If probe() failed,
      which return code is reasonable? Or<br>
      EXIT_SUCCESS is enough?<br>
    </font>
    <blockquote type="cite"
cite="mid:e38046ef0ed3560d0ea856efcc7a71ba8ccc7021.camel@mendozajonas.com">
      <pre wrap="">

</pre>
      <blockquote type="cite">
        <pre wrap="">+	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,
+				&amp;data, &amp;size, &amp;attr);
+
+	errno_value = errno;
+	talloc_free(ctx);
+	assert(errno_value == ENOENT);
+
+	return EXIT_SUCCESS;
+}
</pre>
      </blockquote>
      <pre wrap="">
</pre>
    </blockquote>
    <br>
  </body>
</html>
diff mbox series

Patch

diff --git a/lib/Makefile.am b/lib/Makefile.am
index bb7dfe489132..50fb9d14df49 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -58,6 +58,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..da0e0d68b6c8
--- /dev/null
+++ b/lib/efi/efivar.h
@@ -0,0 +1,43 @@ 
+/*
+ *  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 <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
+
+#define EFIVARFS_MAGIC 0xde5e81e4
+
+static inline const char* get_efivarfs_path(void)
+{
+	return "/sys/firmware/efi/efivars/";
+}
+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..5ce3464b9e77
--- /dev/null
+++ b/lib/efi/efivar.c
@@ -0,0 +1,179 @@ 
+/*
+ *  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"
+
+int efi_del_variable(void *ctx, const char *guidstr,
+		const char *name)
+{
+	int fd, flag, errno_value;
+	int rc = -1;
+	char *path;
+
+	path = talloc_asprintf(ctx, "%s%s-%s",
+				get_efivarfs_path(), 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;
+	char *path;
+
+	path = talloc_asprintf(ctx, "%s%s-%s",
+				get_efivarfs_path(), name, guidstr);
+	if (!path)
+		return ENOMEM;
+
+	fd = open(path, O_RDONLY);
+	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;
+	char *path;
+	void *buf;
+	size_t bufsize;
+	mode_t mask = umask(umask(0));
+
+	path = talloc_asprintf(ctx, "%s%s-%s",
+				get_efivarfs_path(), 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..aec2549c4993
--- /dev/null
+++ b/test/lib/test-efivar.c
@@ -0,0 +1,68 @@ 
+/*
+ *  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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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";
+
+int main(int argc, char *argv[])
+{
+	void *ctx = NULL;
+	int rc, errno_value;
+	size_t size;
+	uint8_t *data = NULL;
+	uint32_t attr = DEF_ATTR;
+
+	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);
+
+	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);
+
+	return EXIT_SUCCESS;
+}