]> git.ozlabs.org Git - petitboot/blob - lib/efi/efivar.c
a0c21b5b5d345f3717981a9a8dba67b78382e8e8
[petitboot] / lib / efi / efivar.c
1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; version 2 of the License.
5  *
6  *  This program is distributed in the hope that it will be useful,
7  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *  GNU General Public License for more details.
10  *
11  *  You should have received a copy of the GNU General Public License
12  *  along with this program; if not, write to the Free Software
13  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
14  *
15  *  Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights
16  *  reserved.
17  *  Author: Ge Song <ge.song@hxt-semitech.com>
18  */
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <linux/fs.h>
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28
29 #include "efivar.h"
30 #include "log/log.h"
31 #include "talloc/talloc.h"
32
33 static const char *efivarfs_path;
34
35 inline void set_efivarfs_path(const char *path)
36 {
37         efivarfs_path = path;
38 }
39
40 inline const char *get_efivarfs_path(void)
41 {
42
43         return efivarfs_path;
44 }
45
46 static int efi_open(const char *name, const char *guidstr, int flags,
47         mode_t mode, char **path)
48 {
49         int fd;
50
51         *path = NULL;
52
53         if (!get_efivarfs_path())
54                 return -1;
55
56         *path = talloc_asprintf(NULL, "%s%s-%s", get_efivarfs_path(), name, guidstr);
57         if (!*path)
58                 return -1;
59
60         flags = flags ? flags : O_RDONLY | O_NONBLOCK;
61
62         fd = open(*path, flags, mode);
63
64         if (fd < 0) {
65                 pb_log("%s: open failed %s: %s\n", __func__, *path,
66                         strerror(errno));
67                 talloc_free(*path);
68                 *path = NULL;
69                 return -1;
70         }
71
72         return fd;
73 }
74
75 int efi_del_variable(const char *guidstr, const char *name)
76 {
77         int fd, flag;
78         int rc = -1;
79         char *path;
80
81         fd = efi_open(name, guidstr, 0, 0, &path);
82         if (fd < 0)
83                 return -1;
84
85         rc = ioctl(fd, FS_IOC_GETFLAGS, &flag);
86         if (rc == -1 && errno == ENOTTY) {
87                 pb_debug_fn("'%s' does not support ioctl_iflags.\n",
88                         efivarfs_path);
89                 goto delete;
90         } else if (rc == -1) {
91                 pb_log_fn("FS_IOC_GETFLAGS failed: (%d) %s\n", errno,
92                         strerror(errno));
93                 goto exit;
94         }
95
96         flag &= ~FS_IMMUTABLE_FL;
97         rc = ioctl(fd, FS_IOC_SETFLAGS, &flag);
98         if (rc == -1) {
99                 pb_log_fn("FS_IOC_SETFLAGS failed: (%d) %s\n", errno,
100                         strerror(errno));
101                 goto exit;
102         }
103
104 delete:
105         close(fd);
106         fd = 0;
107         rc = unlink(path);
108         if (rc == -1) {
109                 pb_log_fn("unlink failed: (%d) %s\n", errno, strerror(errno));
110                 goto exit;
111         }
112         pb_debug_fn("Deleted: '%s'\n", name);
113 exit:
114         talloc_free(path);
115         close(fd);
116         return rc;
117 }
118
119 int efi_get_variable(void *ctx, const char *guidstr, const char *name,
120                 struct efi_data **efi_data)
121 {
122         int fd;
123         int rc = -1;
124         char *p;
125         char buf[4096];
126         ssize_t total;
127         ssize_t count;
128         char *path;
129
130         *efi_data = NULL;
131
132         fd = efi_open(name, guidstr, 0, 0, &path);
133         if (fd < 0)
134                 return -1;
135
136         for (p = buf, total = 0; ; p = buf + count) {
137                 count = read(fd, p, sizeof(buf) - total);
138                 if (count < 0) {
139                         if (errno == EAGAIN || errno == EWOULDBLOCK)
140                                 continue;
141
142                         pb_log("%s: read failed %s: (%ld) %s\n", __func__, path,
143                                 count, strerror(errno));
144                         goto exit;
145                 }
146                 if (p >= (buf + sizeof(buf))) {
147                         pb_log("%s: buffer full %s: (%ld)\n", __func__, path,
148                                 sizeof(buf));
149                         goto exit;
150                 }
151                 if (count == 0)
152                         break;
153                 total += count;
154         };
155
156         *efi_data = (void*)talloc_zero_array(ctx, char,
157                 sizeof (struct efi_data) + total);
158
159         (*efi_data)->attributes = *(uint32_t *)buf;
160         (*efi_data)->data_size = total;
161         (*efi_data)->data = (*efi_data)->fill;
162         memcpy((*efi_data)->data, buf + sizeof (uint32_t), total);
163         pb_debug_fn("Found: '%s'='%s'\n", name, (const char *)(*efi_data)->data);
164
165         rc = 0;
166 exit:
167         talloc_free(path);
168         close(fd);
169         return rc;
170 }
171
172 int efi_set_variable(const char *guidstr, const char *name,
173                 const struct efi_data *efi_data)
174 {
175         int rc = -1;
176         int fd;
177         ssize_t count;
178         void *buf;
179         size_t bufsize;
180         char *path;
181
182         efi_del_variable(guidstr, name);
183
184         fd = efi_open(name, guidstr, O_CREAT | O_WRONLY,
185                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &path);
186         if (fd < 0)
187                 return -1;
188
189         bufsize = sizeof(uint32_t) + efi_data->data_size;
190         buf = talloc_size(path, bufsize);
191         if (!buf)
192                 goto exit;
193
194         *(uint32_t *)buf = efi_data->attributes;
195         memcpy(buf + sizeof(uint32_t), efi_data->data, efi_data->data_size);
196
197         count = write(fd, buf, bufsize);
198         if ((size_t)count != bufsize) {
199                 pb_log("%s: write failed %s: (%ld) %s\n", __func__, name,
200                         count, strerror(errno));
201                 goto exit;
202         }
203         rc = 0;
204         pb_debug_fn("Set: '%s'='%s'\n", name,  (const char *)efi_data->data);
205
206 exit:
207         talloc_free(path);
208         close(fd);
209         return rc;
210 }