source: bootcd/isolinux/syslinux-6.03/efi/adv.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 7.9 KB
Line 
1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
4 *   Copyright 2012 Intel Corporation; author: H. Peter Anvin
5 *   Chandramouli Narayanan
6 *
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.
12 *
13 * ----------------------------------------------------------------------- */
14
15/*
16 * adv.c
17 *
18 * Core ADV I/O
19 * Code consolidated from libinstaller/adv*.c and core/adv.inc with the
20 * addition of EFI support
21 *
22 * Return 0 on success, -1 on error, and set errno.
23 *
24 */
25#define  _GNU_SOURCE
26
27#include <syslinux/config.h>
28#include <string.h>
29#include "adv.h"
30
31unsigned char syslinux_adv[2 * ADV_SIZE];
32
33static void cleanup_adv(unsigned char *advbuf)
34{
35    int i;
36    uint32_t csum;
37
38    /* Make sure both copies agree, and update the checksum */
39    *(uint32_t *)advbuf =  ADV_MAGIC1;
40
41    csum = ADV_MAGIC2;
42    for (i = 8; i < ADV_SIZE - 4; i += 4)
43        csum -= *(uint32_t *)(advbuf + i);
44
45    *(uint32_t *)(advbuf + 4) =  csum;
46    *(uint32_t *)(advbuf + ADV_SIZE - 4) =  ADV_MAGIC3;
47
48    memcpy(advbuf + ADV_SIZE, advbuf, ADV_SIZE);
49}
50
51void syslinux_reset_adv(unsigned char *advbuf)
52{
53    /* Create an all-zero ADV */
54    memset(advbuf + 2 * 4, 0, ADV_LEN);
55    cleanup_adv(advbuf);
56}
57
58static int adv_consistent(const unsigned char *p)
59{
60    int i;
61    uint32_t csum;
62
63    if (*(uint32_t *)p != ADV_MAGIC1 ||
64        *(uint32_t *)(p + ADV_SIZE - 4) != ADV_MAGIC3)
65        return 0;
66
67    csum = 0;
68    for (i = 4; i < ADV_SIZE - 4; i += 4)
69        csum += *(uint32_t *)(p + i);
70
71    return csum == ADV_MAGIC2;
72}
73
74/*
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().
77 */
78int syslinux_validate_adv(unsigned char *advbuf)
79{
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;
89    }
90}
91
92/*
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.
96 */
97
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
104 *
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
109 *
110 */
111static int make_filespec(CHAR16 **fspec, const char *path, const char *cfg)
112{
113        CHAR16 *p;
114        int size, append;
115
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;
120
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;
129
130        return 0;
131}
132
133
134/* TODO:
135 * set_attributes() and clear_attributes() are supported for VFAT only
136 */
137int read_adv(const char *path, const char *cfg)
138{
139    CHAR16 *file;
140    EFI_FILE_HANDLE fd;
141    EFI_FILE_INFO st;
142    int err = 0;
143    int rv;
144
145    rv = make_filespec(&file, path, cfg);
146    if (rv < 0 || !file) {
147        efi_perror(L"read_adv");
148        return -1;
149    }
150
151    /* TBD: Not sure if EFI accepts the attribute read only
152     * even if an existing file is opened for read access
153     */
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 */
161        }
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;
174    }
175
176    if (err < 0)
177        efi_perror(file);
178    if (fd)
179        efi_close(fd);
180    free(file);
181
182    return err;
183}
184
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.
189 */
190void efi_adv_init(void)
191{
192    union syslinux_derivative_info sdi;
193
194    get_derivative_info(&sdi);
195
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;
201
202        syslinux_validate_adv(syslinux_adv);
203    }
204}
205
206/* For EFI platform, write 2 * ADV_SIZE data to the file opened
207 * at ADV initialization. (i.e ldlinux.sys).
208 *
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?
213 *
214 */
215int efi_adv_write(void)
216{
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;
226
227    get_derivative_info(&sdi);
228    if (sdi.c.filesystem != SYSLINUX_FS_SYSLINUX)
229        return -1;
230
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;
237    }
238
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;
257
258        if (!err) {
259            /* Got a good one, write our own ADV here */
260            efi_clear_attributes(fd);
261
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;
271            }
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);
277            }
278            if (!err) {
279                efi_sync(fd);
280                efi_set_attributes(fd);
281            }
282        }
283    }
284
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:");
290
291    if (fd)
292        efi_close(fd);
293    if (file)
294        free(file);
295
296    return err;
297}
Note: See TracBrowser for help on using the repository browser.