Makefiles: export the firmware type as a cpp variable
[syslinux.git] / efi / adv.c
blob4056db1587f305da131175107919f1705798fb7b
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2012 Intel Corporation; author: H. Peter Anvin
5 * Chandramouli Narayanan
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
10 * Boston MA 02111-1307, USA; either version 2 of the License, or
11 * (at your option) any later version; incorporated herein by reference.
13 * ----------------------------------------------------------------------- */
16 * adv.c
18 * Core ADV I/O
19 * Code consolidated from libinstaller/adv*.c and core/adv.inc with the
20 * addition of EFI support
22 * Return 0 on success, -1 on error, and set errno.
25 #define _GNU_SOURCE
27 #include <syslinux/config.h>
28 #include <string.h>
29 #include "adv.h"
31 unsigned char syslinux_adv[2 * ADV_SIZE];
33 static void cleanup_adv(unsigned char *advbuf)
35 int i;
36 uint32_t csum;
38 /* Make sure both copies agree, and update the checksum */
39 *(uint32_t *)advbuf = ADV_MAGIC1;
41 csum = ADV_MAGIC2;
42 for (i = 8; i < ADV_SIZE - 4; i += 4)
43 csum -= *(uint32_t *)(advbuf + i);
45 *(uint32_t *)(advbuf + 4) = csum;
46 *(uint32_t *)(advbuf + ADV_SIZE - 4) = ADV_MAGIC3;
48 memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE);
51 void syslinux_reset_adv(unsigned char *advbuf)
53 /* Create an all-zero ADV */
54 memset(advbuf + 2 * 4, 0, ADV_LEN);
55 cleanup_adv(advbuf);
58 static int adv_consistent(const unsigned char *p)
60 int i;
61 uint32_t csum;
63 if (*(uint32_t *)p != ADV_MAGIC1 ||
64 *(uint32_t *)(p + ADV_SIZE - 4) != ADV_MAGIC3)
65 return 0;
67 csum = 0;
68 for (i = 4; i < ADV_SIZE - 4; i += 4)
69 csum += *(uint32_t *)(p + i);
71 return csum == ADV_MAGIC2;
75 * Verify that an in-memory ADV is consistent, making the copies consistent.
76 * If neither copy is OK, return -1 and call syslinux_reset_adv().
78 int syslinux_validate_adv(unsigned char *advbuf)
80 if (adv_consistent(advbuf + 0 * ADV_SIZE)) {
81 memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE);
82 return 0;
83 } else if (adv_consistent(advbuf + 1 * ADV_SIZE)) {
84 memcpy(advbuf, advbuf + ADV_SIZE, ADV_SIZE);
85 return 0;
86 } else {
87 syslinux_reset_adv(advbuf);
88 return -1;
93 * Read the ADV from an existing instance, or initialize if invalid.
94 * Returns -1 on fatal errors, 0 if ADV is okay, 1 if the ADV is
95 * invalid, and 2 if the file does not exist.
98 /* make_filespec
99 * Take the ASCII pathname and filename and concatenate them
100 * into an allocated memory space as unicode file specification string.
101 * The path and cfg ASCII strings are assumed to be null-terminated.
102 * For EFI, the separation character in the path name is '\'
103 * and therefore it is assumed that the file spec uses '\' as separation char
105 * The function returns
106 * 0 if successful and fspec is a valid allocated CHAR16 pointer
107 * Caller is responsible to free up the allocated filespec string
108 * -1 otherwise
111 static int make_filespec(CHAR16 **fspec, const char *path, const char *cfg)
113 CHAR16 *p;
114 int size, append;
116 /* allocate size for a CHAR16 string */
117 size = sizeof(CHAR16) * (strlena((CHAR8 *)path)+strlena((CHAR8 *)cfg)+2); /* including null */
118 *fspec = malloc(size);
119 if (!*fspec) return -1;
121 append = path[strlena((CHAR8 *)path) - 1] != '\\';
122 for (p = *fspec; *path; path++, p++)
123 *p = (CHAR16)*path;
124 /* append the separation character to the path if need be */
125 if (append) *p++ = (CHAR16)'\\';
126 for (; *cfg; cfg++, p++)
127 *p = (CHAR16)*cfg;
128 *p = (CHAR16)CHAR_NULL;
130 return 0;
134 /* TODO:
135 * set_attributes() and clear_attributes() are supported for VFAT only
137 int read_adv(const char *path, const char *cfg)
139 CHAR16 *file;
140 EFI_FILE_HANDLE fd;
141 EFI_FILE_INFO st;
142 int err = 0;
143 int rv;
145 rv = make_filespec(&file, path, cfg);
146 if (rv < 0 || !file) {
147 efi_perror(L"read_adv");
148 return -1;
151 /* TBD: Not sure if EFI accepts the attribute read only
152 * even if an existing file is opened for read access
154 fd = efi_open(file, EFI_FILE_MODE_READ);
155 if (!fd) {
156 if (efi_errno != EFI_NOT_FOUND) {
157 err = -1;
158 } else {
159 syslinux_reset_adv(syslinux_adv);
160 err = 2; /* Nonexistence is not a fatal error */
162 } else if (!efi_fstat(fd, &st)) {
163 err = -1;
164 } else if (st.FileSize < 2 * ADV_SIZE) {
165 /* Too small to be useful */
166 syslinux_reset_adv(syslinux_adv);
167 err = 0; /* Nothing to read... */
168 } else if (efi_xpread(fd, syslinux_adv, 2 * ADV_SIZE,
169 st.FileSize - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
170 err = -1;
171 } else {
172 /* We got it... maybe? */
173 err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
176 if (err < 0)
177 efi_perror(file);
178 if (fd)
179 efi_close(fd);
180 free(file);
182 return err;
185 /* For EFI platform, initialize ADV by opening ldlinux.sys
186 * as configured and return the primary (adv0) and alternate (adv1)
187 * data into caller's buffer. File remains open for subsequent
188 * operations. This routine is to be called from comboot vector.
190 void efi_adv_init(void)
192 union syslinux_derivative_info sdi;
194 get_derivative_info(&sdi);
196 if (sdi.c.filesystem == SYSLINUX_FS_SYSLINUX)
197 read_adv("", SYSLINUX_FILE);
198 else {
199 __syslinux_adv_ptr = &syslinux_adv[8]; /* skip head, csum */
200 __syslinux_adv_size = ADV_LEN;
202 syslinux_validate_adv(syslinux_adv);
206 /* For EFI platform, write 2 * ADV_SIZE data to the file opened
207 * at ADV initialization. (i.e ldlinux.sys).
209 * TODO:
210 * 1. Validate assumption: write back to file from __syslinux_adv_ptr
211 * 2. What if there errors?
212 * 3. Do we need to set the attributes of the sys file?
215 int efi_adv_write(void)
217 char *name;
218 unsigned char advtmp[2 * ADV_SIZE];
219 unsigned char *advbuf = syslinux_adv;
220 int rv;
221 int err = 0;
222 EFI_FILE_HANDLE fd; /* handle to ldlinux.sys */
223 CHAR16 *file;
224 EFI_FILE_INFO st, xst;
225 union syslinux_derivative_info sdi;
227 get_derivative_info(&sdi);
228 if (sdi.c.filesystem != SYSLINUX_FS_SYSLINUX)
229 return -1;
231 name = SYSLINUX_FILE;
232 rv = make_filespec(&file, "", name);
233 if (rv < 0 || !file) {
234 efi_errno = EFI_OUT_OF_RESOURCES;
235 efi_perror(L"efi_adv_write:");
236 return -1;
239 fd = efi_open(file, EFI_FILE_MODE_READ);
240 if (fd == (EFI_FILE_HANDLE)NULL) {
241 err = -1;
242 efi_printerr(L"efi_adv_write: Unable to open file %s\n", file);
243 } else if (efi_fstat(fd, &st)) {
244 err = -1;
245 efi_printerr(L"efi_adv_write: Unable to get info for file %s\n", file);
246 } else if (st.FileSize < 2 * ADV_SIZE) {
247 /* Too small to be useful */
248 err = -2;
249 efi_printerr(L"efi_adv_write: File size too small to be useful for file %s\n", file);
250 } else if (efi_xpread(fd, advtmp, 2 * ADV_SIZE,
251 st.FileSize - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
252 err = -1;
253 efi_printerr(L"efi_adv_write: Error reading ADV data from file %s\n", file);
254 } else {
255 cleanup_adv(advbuf);
256 err = syslinux_validate_adv(advbuf) ? -2 : 0;
258 if (!err) {
259 /* Got a good one, write our own ADV here */
260 efi_clear_attributes(fd);
262 /* Need to re-open read-write */
263 efi_close(fd);
264 /* There is no SYNC attribute with EFI open */
265 fd = efi_open(file, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE);
266 if (fd == (EFI_FILE_HANDLE)NULL) {
267 err = -1;
268 } else if (efi_fstat(fd, &xst) || xst.FileSize != st.FileSize) {
269 efi_perror(L"efi_adv_write: file status error/mismatch");
270 err = -2;
272 /* Write our own version ... */
273 if (efi_xpwrite(fd, advbuf, 2 * ADV_SIZE,
274 st.FileSize - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
275 err = -1;
276 efi_printerr(L"efi_adv_write: Error write ADV data to file %s\n", file);
278 if (!err) {
279 efi_sync(fd);
280 efi_set_attributes(fd);
285 if (err == -2)
286 efi_printerr(L"%s: cannot write auxilliary data (need --update)?\n",
287 file);
288 else if (err == -1)
289 efi_perror(L"efi_adv_write:");
291 if (fd)
292 efi_close(fd);
293 if (file)
294 free(file);
296 return err;