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 * ----------------------------------------------------------------------- */
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.
27 #include <syslinux/config.h>
31 unsigned char syslinux_adv
[2 * ADV_SIZE
];
33 static void cleanup_adv(unsigned char *advbuf
)
38 /* Make sure both copies agree, and update the checksum */
39 *(uint32_t *)advbuf
= ADV_MAGIC1
;
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
);
58 static int adv_consistent(const unsigned char *p
)
63 if (*(uint32_t *)p
!= ADV_MAGIC1
||
64 *(uint32_t *)(p
+ ADV_SIZE
- 4) != ADV_MAGIC3
)
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
);
83 } else if (adv_consistent(advbuf
+ 1 * ADV_SIZE
)) {
84 memcpy(advbuf
, advbuf
+ ADV_SIZE
, ADV_SIZE
);
87 syslinux_reset_adv(advbuf
);
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.
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
111 static int make_filespec(CHAR16
**fspec
, const char *path
, const char *cfg
)
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
++)
124 /* append the separation character to the path if need be */
125 if (append
) *p
++ = (CHAR16
)'\\';
126 for (; *cfg
; cfg
++, p
++)
128 *p
= (CHAR16
)CHAR_NULL
;
135 * set_attributes() and clear_attributes() are supported for VFAT only
137 int read_adv(const char *path
, const char *cfg
)
145 rv
= make_filespec(&file
, path
, cfg
);
146 if (rv
< 0 || !file
) {
147 efi_perror(L
"read_adv");
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
);
156 if (efi_errno
!= EFI_NOT_FOUND
) {
159 syslinux_reset_adv(syslinux_adv
);
160 err
= 2; /* Nonexistence is not a fatal error */
162 } else if (!efi_fstat(fd
, &st
)) {
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
) {
172 /* We got it... maybe? */
173 err
= syslinux_validate_adv(syslinux_adv
) ? 1 : 0;
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
);
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).
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)
218 unsigned char advtmp
[2 * ADV_SIZE
];
219 unsigned char *advbuf
= syslinux_adv
;
222 EFI_FILE_HANDLE fd
; /* handle to ldlinux.sys */
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
)
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:");
239 fd
= efi_open(file
, EFI_FILE_MODE_READ
);
240 if (fd
== (EFI_FILE_HANDLE
)NULL
) {
242 efi_printerr(L
"efi_adv_write: Unable to open file %s\n", file
);
243 } else if (efi_fstat(fd
, &st
)) {
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 */
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
) {
253 efi_printerr(L
"efi_adv_write: Error reading ADV data from file %s\n", file
);
256 err
= syslinux_validate_adv(advbuf
) ? -2 : 0;
259 /* Got a good one, write our own ADV here */
260 efi_clear_attributes(fd
);
262 /* Need to re-open read-write */
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
) {
268 } else if (efi_fstat(fd
, &xst
) || xst
.FileSize
!= st
.FileSize
) {
269 efi_perror(L
"efi_adv_write: file status error/mismatch");
272 /* Write our own version ... */
273 if (efi_xpwrite(fd
, advbuf
, 2 * ADV_SIZE
,
274 st
.FileSize
- 2 * ADV_SIZE
) != 2 * ADV_SIZE
) {
276 efi_printerr(L
"efi_adv_write: Error write ADV data to file %s\n", file
);
280 efi_set_attributes(fd
);
286 efi_printerr(L
"%s: cannot write auxilliary data (need --update)?\n",
289 efi_perror(L
"efi_adv_write:");