]> git.ozlabs.org Git - petitboot/blob - lib/efi/efivar.c
lib/efi: Add check for ioctl_iflags support
[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 exit:
113         talloc_free(path);
114         close(fd);
115         return rc;
116 }
117
118 int efi_get_variable(void *ctx, const char *guidstr, const char *name,
119                 struct efi_data **efi_data)
120 {
121         int fd;
122         int rc = -1;
123         char *p;
124         char buf[4096];
125         ssize_t total;
126         ssize_t count;
127         char *path;
128
129         *efi_data = NULL;
130
131         fd = efi_open(name, guidstr, 0, 0, &path);
132         if (fd < 0)
133                 return -1;
134
135         for (p = buf, total = 0; ; p = buf + count) {
136                 count = read(fd, p, sizeof(buf) - total);
137                 if (count < 0) {
138                         if (errno == EAGAIN || errno == EWOULDBLOCK)
139                                 continue;
140
141                         pb_log("%s: read failed %s: (%ld) %s\n", __func__, path,
142                                 count, strerror(errno));
143                         goto exit;
144                 }
145                 if (p >= (buf + sizeof(buf))) {
146                         pb_log("%s: buffer full %s: (%ld)\n", __func__, path,
147                                 sizeof(buf));
148                         goto exit;
149                 }
150                 if (count == 0)
151                         break;
152                 total += count;
153         };
154
155         *efi_data = (void*)talloc_zero_array(ctx, char,
156                 sizeof (struct efi_data) + total);
157
158         (*efi_data)->attributes = *(uint32_t *)buf;
159         (*efi_data)->data_size = total;
160         (*efi_data)->data = (*efi_data)->fill;
161         memcpy((*efi_data)->data, buf + sizeof (uint32_t), total);
162
163         rc = 0;
164 exit:
165         talloc_free(path);
166         close(fd);
167         return rc;
168 }
169
170 int efi_set_variable(const char *guidstr, const char *name,
171                 const struct efi_data *efi_data)
172 {
173         int rc = -1;
174         int fd;
175         ssize_t count;
176         void *buf;
177         size_t bufsize;
178         char *path;
179
180         efi_del_variable(guidstr, name);
181
182         fd = efi_open(name, guidstr, O_CREAT | O_WRONLY,
183                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &path);
184         if (fd < 0)
185                 return -1;
186
187         bufsize = sizeof(uint32_t) + efi_data->data_size;
188         buf = talloc_size(path, bufsize);
189         if (!buf)
190                 goto exit;
191
192         *(uint32_t *)buf = efi_data->attributes;
193         memcpy(buf + sizeof(uint32_t), efi_data->data, efi_data->data_size);
194
195         count = write(fd, buf, bufsize);
196         if ((size_t)count != bufsize) {
197                 pb_log("%s: write failed %s: (%ld) %s\n", __func__, name,
198                         count, strerror(errno));
199                 goto exit;
200         }
201         rc = 0;
202
203 exit:
204         talloc_free(path);
205         close(fd);
206         return rc;
207 }