1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2008 H. Peter Anvin - All Rights Reserved
4 *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
5 *
6 *   This program is free software; you can redistribute it and/or modify
7 *   it under the terms of the GNU General Public License as published by
8 *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
9 *   Boston MA 02110-1301, USA; either version 2 of the License, or
10 *   (at your option) any later version; incorporated herein by reference.
11 *
12 * ----------------------------------------------------------------------- */
13
14/*
15 * sdi.c
16 *
17 * Loader for the Microsoft System Deployment Image (SDI) format.
18 * Based on a historical patch by Remi Lefevre.
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <inttypes.h>
24#include <string.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <errno.h>
28#include <minmax.h>
29#include <sys/stat.h>
30#include <console.h>
31#include <dprintf.h>
32
33#include <syslinux/loadfile.h>
34#include <syslinux/movebits.h>
35#include <syslinux/bootrm.h>
36
37typedef uint8_t guid_t[16];
38
39struct SDIHeader {
40    uint32_t Signature;
41    char Version[4];
42    uint64_t MDBtype;
43    uint64_t BootCodeOffset;
44    uint64_t BootCodeSize;
45    uint64_t VendorID;
46    uint64_t DeviceID;
47    guid_t DeviceModel;
48    uint64_t DeviceRole;
49    uint64_t Reserved1;
50    guid_t RuntimeGUID;
51    uint64_t RuntimeOEMrev;
52    uint64_t Reserved2;
53    uint64_t PageAlignment;	/* BLOB alignment value in pages */
54    uint64_t Reserved3[48];
55    uint64_t Checksum;
56};
57
58#define SDI_LOAD_ADDR	(16 << 20)	/* 16 MB */
59#define SDI_SIGNATURE	('$' + ('S' << 8) + ('D' << 16) + ('I' << 24))
60
61static inline void error(const char *msg)
62{
63    fputs(msg, stderr);
64}
65
66static int boot_sdi(void *ptr, size_t len)
67{
68    const struct SDIHeader *hdr = ptr;
69    struct syslinux_memmap *mmap = NULL, *amap = NULL;
70    struct syslinux_rm_regs regs;
71    struct syslinux_movelist *ml = NULL;
72
73    /* **** Basic sanity checking **** */
74    if (hdr->Signature != SDI_SIGNATURE) {
75	fputs("No $SDI signature in file\n", stdout);
76	goto bail;
77    }
78    if (memcmp(hdr->Version, "0001", 4)) {
79	int i;
80	fputs("Warning: unknown SDI version: ", stdout);
81	for (i = 0; i < 4; i++)
82	    putchar(hdr->Version[i]);
83	putchar('\n');
84	/* Then try anyway... */
85    }
86
87    /* **** Setup **** */
88    mmap = syslinux_memory_map();
89    amap = syslinux_dup_memmap(mmap);
90    if (!mmap || !amap)
91	goto bail;
92
93    /* **** Map the BOOT BLOB to 0x7c00 **** */
94    if (!hdr->BootCodeOffset) {
95	fputs("No BOOT BLOB in image\n", stdout);
96	goto bail;
97    }
98    if (!hdr->BootCodeSize) {
99	fputs("BOOT BLOB is empty\n", stdout);
100	goto bail;
101    }
102    if (len < hdr->BootCodeOffset + hdr->BootCodeSize) {
103	fputs("BOOT BLOB extends beyond file\n", stdout);
104	goto bail;
105    }
106
107    if (syslinux_memmap_type(amap, 0x7c00, hdr->BootCodeSize) != SMT_FREE) {
108	fputs("BOOT BLOB too large for memory\n", stdout);
109	goto bail;
110    }
111    if (syslinux_add_memmap(&amap, 0x7c00, hdr->BootCodeSize, SMT_ALLOC))
112	goto bail;
113    if (syslinux_add_movelist(&ml, 0x7c00, (addr_t) ptr + hdr->BootCodeOffset,
114			      hdr->BootCodeSize))
115	goto bail;
116
117    /* **** Map the entire image to SDI_LOAD_ADDR **** */
118    if (syslinux_memmap_type(amap, SDI_LOAD_ADDR, len) != SMT_FREE) {
119	fputs("Image too large for memory\n", stdout);
120	goto bail;
121    }
122    if (syslinux_add_memmap(&amap, SDI_LOAD_ADDR, len, SMT_ALLOC))
123	goto bail;
124    if (syslinux_add_movelist(&ml, SDI_LOAD_ADDR, (addr_t) ptr, len))
125	goto bail;
126
127    /* **** Set up registers **** */
128    memset(&regs, 0, sizeof regs);
129    regs.ip = 0x7c00;
130    regs.esp.l = 0x7c00;
131    regs.edx.l = SDI_LOAD_ADDR | 0x41;
132
133    fputs("Booting...\n", stdout);
134    syslinux_shuffle_boot_rm(ml, mmap, 0, &regs);
135
136bail:
137    syslinux_free_memmap(amap);
138    syslinux_free_memmap(mmap);
139    syslinux_free_movelist(ml);
140    return -1;
141}
142
143/*
144 * Check that the sum of all bytes from first 512 bytes (SDI header)
145 * is 0 modulo 256.
146 */
147int has_valid_header(unsigned char *header)
148{
149    unsigned char checksum;
150    unsigned int i;
151
152    checksum = 0;
153    for (i = 0; i < sizeof(struct SDIHeader); i++)
154	checksum += header[i];
155    return (!checksum);
156}
157
158int main(int argc, char *argv[])
159{
160    void *data;
161    size_t data_len;
162
163    if (argc != 2) {
164	error("Usage: sdi.c32 sdi_file\n");
165	return 1;
166    }
167
168    fputs("Loading ", stdout);
169    fputs(argv[1], stdout);
170    fputs("... ", stdout);
171    if (zloadfile(argv[1], &data, &data_len)) {
172	error("failed!\n");
173	return 1;
174    }
175    fputs("ok\n", stdout);
176
177    if (!has_valid_header(data)) {
178	error("SDI header is corrupted\n");
179	return 1;
180    }
181
182    boot_sdi(data, data_len);
183    error("Invalid SDI file or insufficient memory\n");
184    return 1;
185}
186