"Fossies" - the Fresh Open Source Software Archive

Member "syslinux-6.03/efi/adv.c" (6 Oct 2014, 8078 Bytes) of package /linux/misc/syslinux-6.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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 
   31 unsigned char syslinux_adv[2 * ADV_SIZE];
   32 
   33 static 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 
   51 void 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 
   58 static 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  */
   78 int 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  */
  111 static 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  */
  137 int 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  */
  190 void 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  */
  215 int 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 }