summaryrefslogtreecommitdiff
path: root/genisoimage/boot-mipsel.c
blob: 3d8ca65dd6f27ee061204f87106cb30c80ca7445 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
 * This file has been modified for the cdrkit suite.
 *
 * The behaviour and appearence of the program code below can differ to a major
 * extent from the version distributed by the original author(s).
 *
 * For details, see Changelog file distributed with the cdrkit package. If you
 * received this file from another source then ask the distributing person for
 * a log of modifications.
 *
 */

/*
 * Program boot-mipsel.c - Handle Mipsel boot extensions to iso9660.
 *
 *  Written by Steve McIntyre <steve@einval.com> (2004).
 *
 * Heavily inspired by / borrowed from delo:
 *
 * Copyright: (C) 2002 by Florian Lohoff <flo@rfc822.org>
 *            (C) 2004 by Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, Version 2, as published by the
 * Free Software Foundation.
 *
 * Format for volume header information
 *
 * The volume header is a block located at the beginning of all disk
 * media (sector 0).  It contains information pertaining to physical
 * device parameters and logical partition information.
 *
 * The volume header is manipulated by disk formatters/verifiers,
 * partition builders (e.g. fx, dvhtool, and mkfs), and disk drivers.
 *
 * Previous versions of IRIX wrote a copy of the volume header is
 * located at sector 0 of each track of cylinder 0.  These copies were
 * never used, and reduced the capacity of the volume header to hold large
 * files, so this practice was discontinued.
 * The volume header is constrained to be less than or equal to 512
 * bytes long.  A particular copy is assumed valid if no drive errors
 * are detected, the magic number is correct, and the 32 bit 2's complement
 * of the volume header is correct.  The checksum is calculated by initially
 * zeroing vh_csum, summing the entire structure and then storing the
 * 2's complement of the sum.  Thus a checksum to verify the volume header
 * should be 0.
 *
 * The error summary table, bad sector replacement table, and boot blocks are
 * located by searching the volume directory within the volume header.
 *
 * Tables are sized simply by the integral number of table records that
 * will fit in the space indicated by the directory entry.
 *
 * The amount of space allocated to the volume header, replacement blocks,
 * and other tables is user defined when the device is formatted.
 */

#include <mconfig.h>
#include "genisoimage.h"
#include <fctldefs.h>
#include <utypes.h>
#include <intcvt.h>
#include "match.h"
#include "diskmbr.h"
#include "bootinfo.h"
#include <schily.h>
#include "endianconv.h"
#include <errno.h>
#include <glibc_elf.h>

int             add_boot_mipsel_filename(char *filename);
static  int     boot_mipsel_write(FILE *outfile);

static  char   *boot_file_name = NULL;

#define MAX_MAPS        51
#define DEC_BOOT_MAGIC  0x02757a
#define HD_SECTOR_SIZE  512

/* Those were stolen from linux kernel headers. */

struct extent {
    uint32_t count;
    uint32_t start;
}
#ifdef __GNUC__
__attribute__((packed))
#endif
   ;

struct dec_bootblock {
    int8_t pad[8];
    int32_t magic;          /* We are a DEC BootBlock */
    int32_t mode;           /* 0: Single extent, 1: Multi extent boot */
    int32_t loadAddr;       /* Load below kernel */
    int32_t execAddr;       /* And exec there */
    struct extent bootmap[MAX_MAPS];
}
#ifdef __GNUC__
__attribute__((packed))
#endif
   ;

static void swap_in_elf32_ehdr(Elf32_Ehdr *ehdr)
{
    ehdr->e_type = read_le16((unsigned char *)&ehdr->e_type);
    ehdr->e_machine = read_le16((unsigned char *)&ehdr->e_machine);
    ehdr->e_version = read_le32((unsigned char *)&ehdr->e_version);
    ehdr->e_entry = read_le32((unsigned char *)&ehdr->e_entry);
    ehdr->e_phoff = read_le32((unsigned char *)&ehdr->e_phoff);
    ehdr->e_shoff = read_le32((unsigned char *)&ehdr->e_shoff);
    ehdr->e_flags = read_le32((unsigned char *)&ehdr->e_flags);
    ehdr->e_ehsize = read_le16((unsigned char *)&ehdr->e_ehsize);
    ehdr->e_phentsize = read_le16((unsigned char *)&ehdr->e_phentsize);
    ehdr->e_phnum = read_le16((unsigned char *)&ehdr->e_phnum);
    ehdr->e_shentsize = read_le16((unsigned char *)&ehdr->e_shentsize);
    ehdr->e_shnum = read_le16((unsigned char *)&ehdr->e_shnum);
    ehdr->e_shstrndx = read_le16((unsigned char *)&ehdr->e_shstrndx);
}

static void swap_in_elf32_phdr(Elf32_Phdr *phdr)
{
    phdr->p_type = read_le32((unsigned char *)&phdr->p_type);
    phdr->p_offset = read_le32((unsigned char *)&phdr->p_offset);
    phdr->p_vaddr = read_le32((unsigned char *)&phdr->p_vaddr);
    phdr->p_paddr = read_le32((unsigned char *)&phdr->p_paddr);
    phdr->p_filesz = read_le32((unsigned char *)&phdr->p_filesz);
    phdr->p_memsz = read_le32((unsigned char *)&phdr->p_memsz);
    phdr->p_flags = read_le32((unsigned char *)&phdr->p_flags);
    phdr->p_align = read_le32((unsigned char *)&phdr->p_align);
}

/* Simple function: store the filename to be used later when we need
   to find the boot file */
extern int add_boot_mipsel_filename(char *filename)
{
    boot_file_name = filename;
    return 0;
}

/* Parse the ELF header of the boot loaded to work out the load
   address and exec address */
static int parse_boot_file(char *filename, int32_t *loadaddr, int32_t *execaddr, int32_t *offset, int32_t *count)
{
    int error = 0;
    FILE *loader = NULL;
    Elf32_Ehdr ehdr;
    Elf32_Phdr phdr;
    
    loader = fopen(filename, "rb");
    if (!loader)
        return errno;
    
    error = fread(&ehdr, sizeof(ehdr), 1, loader);
    if (1 != error)
        return EIO;

    swap_in_elf32_ehdr(&ehdr);
    if (!(ehdr.e_ident[EI_MAG0] == ELFMAG0
          && ehdr.e_ident[EI_MAG1] == ELFMAG1
          && ehdr.e_ident[EI_MAG2] == ELFMAG2
          && ehdr.e_ident[EI_MAG3] == ELFMAG3
          && ehdr.e_ident[EI_CLASS] == ELFCLASS32
          && ehdr.e_ident[EI_DATA] == ELFDATA2LSB
          && ehdr.e_ident[EI_VERSION] == EV_CURRENT
          && ehdr.e_type == ET_EXEC
          && ehdr.e_machine == EM_MIPS
          && ehdr.e_version == EV_CURRENT))
    {
        fprintf(stderr, "Sorry, %s is not a MIPS ELF32 little endian file", filename);        
        return EINVAL;
    }
    if (ehdr.e_phnum != 1)
    {
        fprintf(stderr, "Sorry, %s has more than one ELF segment", filename);
        return EINVAL;
    }
    fseek(loader, ehdr.e_phoff, SEEK_SET);
    error = fread(&phdr, sizeof(phdr), 1, loader);
    if (1 != error)
        return EIO;

    *loadaddr = phdr.p_vaddr;
    *execaddr = ehdr.e_entry;
	*offset = (phdr.p_offset + HD_SECTOR_SIZE - 1) / HD_SECTOR_SIZE;
	*count = (phdr.p_filesz + HD_SECTOR_SIZE - 1) / HD_SECTOR_SIZE;

    fprintf(stderr, "Parsed mipsel boot image %s: using loadaddr 0x%X, execaddr 0x%X, offset 0x%X, count 0x%X\n",
            filename, *loadaddr, *execaddr, *offset, *count);

    fclose(loader);
    return 0;
}

static int boot_mipsel_write(FILE *outfile)
{
    char sector[2048];
    struct dec_bootblock *bb = (struct dec_bootblock *)sector;
    int error = 0;
    int offset = 0;
    int count = 0;
    struct directory_entry	*boot_file;	/* Boot file we need to search for in the image */
    unsigned long length = 0;
    unsigned long extent = 0;
    int loadaddr = 0;
    int execaddr = 0;

    memset(sector, 0, sizeof(sector));

    /* Fill in our values we care on */
    write_le32(DEC_BOOT_MAGIC, (unsigned char *)&bb->magic);
    write_le32(1, (unsigned char *)&bb->mode);

    /* Find the file entry in the CD image */
    boot_file = search_tree_file(root, boot_file_name);
    if (!boot_file)
    {
#ifdef	USE_LIBSCHILY
		comerrno(EX_BAD, "Uh oh, unable to find the mipsel boot file '%s'!\n",
                 boot_file_name);
#else
		fprintf(stderr, "Uh oh, unable to find the mipsel boot file '%s'!\n",
                boot_file_name);
		exit(1);
#endif
    }

    extent = get_733(boot_file->isorec.extent);
    length = get_733(boot_file->isorec.size);
    fprintf(stderr, "Found mipsel boot loader %s: using extent %lu, #blocks %lu\n",
            boot_file_name, extent, length);

    /* Parse the ELF headers on the boot file */
    error = parse_boot_file(boot_file->whole_name, &loadaddr, &execaddr, &offset, &count);
    if (error)
    {
#ifdef	USE_LIBSCHILY
		comerrno(EX_BAD, "Uh oh, unable to parse the mipsel boot file '%s'!\n",
                 boot_file->whole_name);
#else
		fprintf(stderr, "Uh oh, unable to parse the mipsel boot file '%s'!\n",
                boot_file->whole_name);
		exit(1);
#endif
    }

    write_le32(loadaddr, (unsigned char *)&bb->loadAddr);
    write_le32(execaddr, (unsigned char *)&bb->execAddr);
    write_le32((extent * 4) + offset, (unsigned char *)&bb->bootmap[0].start);
    write_le32(count, (unsigned char *)&bb->bootmap[0].count);
    
    jtwrite(sector, sizeof(sector), 1, 0, FALSE);
    xfwrite(sector, sizeof(sector), 1, outfile, 0, FALSE);
    last_extent_written++;

    return 0;
}

struct output_fragment mipselboot_desc = {NULL, oneblock_size, NULL, boot_mipsel_write, "mipsel boot block"};