Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * arch/ppc/boot/utils/mkbugboot.c |
| 3 | * |
| 4 | * Makes a Motorola PPCBUG ROM bootable image which can be flashed |
| 5 | * into one of the FLASH banks on a Motorola PowerPlus board. |
| 6 | * |
| 7 | * Author: Matt Porter <mporter@mvista.com> |
| 8 | * |
| 9 | * 2001 (c) MontaVista, Software, Inc. This file is licensed under |
| 10 | * the terms of the GNU General Public License version 2. This program |
| 11 | * is licensed "as is" without any warranty of any kind, whether express |
| 12 | * or implied. |
| 13 | */ |
| 14 | |
| 15 | #define ELF_HEADER_SIZE 65536 |
| 16 | |
| 17 | #include <unistd.h> |
| 18 | #include <sys/stat.h> |
| 19 | #include <string.h> |
| 20 | #include <stdio.h> |
| 21 | #include <stdlib.h> |
| 22 | #include <errno.h> |
| 23 | #include <fcntl.h> |
| 24 | #ifdef __sun__ |
| 25 | #include <inttypes.h> |
| 26 | #else |
| 27 | #include <stdint.h> |
| 28 | #endif |
| 29 | |
| 30 | #ifdef __i386__ |
| 31 | #define cpu_to_be32(x) le32_to_cpu(x) |
| 32 | #define cpu_to_be16(x) le16_to_cpu(x) |
| 33 | #else |
| 34 | #define cpu_to_be32(x) (x) |
| 35 | #define cpu_to_be16(x) (x) |
| 36 | #endif |
| 37 | |
| 38 | #define cpu_to_le32(x) le32_to_cpu((x)) |
| 39 | unsigned long le32_to_cpu(unsigned long x) |
| 40 | { |
| 41 | return (((x & 0x000000ffU) << 24) | |
| 42 | ((x & 0x0000ff00U) << 8) | |
| 43 | ((x & 0x00ff0000U) >> 8) | |
| 44 | ((x & 0xff000000U) >> 24)); |
| 45 | } |
| 46 | |
| 47 | #define cpu_to_le16(x) le16_to_cpu((x)) |
| 48 | unsigned short le16_to_cpu(unsigned short x) |
| 49 | { |
| 50 | return (((x & 0x00ff) << 8) | |
| 51 | ((x & 0xff00) >> 8)); |
| 52 | } |
| 53 | |
| 54 | /* size of read buffer */ |
| 55 | #define SIZE 0x1000 |
| 56 | |
| 57 | /* PPCBUG ROM boot header */ |
| 58 | typedef struct bug_boot_header { |
| 59 | uint8_t magic_word[4]; /* "BOOT" */ |
| 60 | uint32_t entry_offset; /* Offset from top of header to code */ |
| 61 | uint32_t routine_length; /* Length of code */ |
| 62 | uint8_t routine_name[8]; /* Name of the boot code */ |
| 63 | } bug_boot_header_t; |
| 64 | |
| 65 | #define HEADER_SIZE sizeof(bug_boot_header_t) |
| 66 | |
| 67 | uint32_t copy_image(int32_t in_fd, int32_t out_fd) |
| 68 | { |
| 69 | uint8_t buf[SIZE]; |
| 70 | int n; |
| 71 | uint32_t image_size = 0; |
| 72 | uint8_t zero = 0; |
| 73 | |
| 74 | lseek(in_fd, ELF_HEADER_SIZE, SEEK_SET); |
| 75 | |
| 76 | /* Copy an image while recording its size */ |
| 77 | while ( (n = read(in_fd, buf, SIZE)) > 0 ) |
| 78 | { |
| 79 | image_size = image_size + n; |
| 80 | write(out_fd, buf, n); |
| 81 | } |
| 82 | |
| 83 | /* BUG romboot requires that our size is divisible by 2 */ |
| 84 | /* align image to 2 byte boundary */ |
| 85 | if (image_size % 2) |
| 86 | { |
| 87 | image_size++; |
| 88 | write(out_fd, &zero, 1); |
| 89 | } |
| 90 | |
| 91 | return image_size; |
| 92 | } |
| 93 | |
| 94 | void write_bugboot_header(int32_t out_fd, uint32_t boot_size) |
| 95 | { |
| 96 | uint8_t header_block[HEADER_SIZE]; |
| 97 | bug_boot_header_t *bbh = (bug_boot_header_t *)&header_block[0]; |
| 98 | |
| 99 | memset(header_block, 0, HEADER_SIZE); |
| 100 | |
| 101 | /* Fill in the PPCBUG ROM boot header */ |
| 102 | strncpy(bbh->magic_word, "BOOT", 4); /* PPCBUG magic word */ |
| 103 | bbh->entry_offset = cpu_to_be32(HEADER_SIZE); /* Entry address */ |
| 104 | bbh->routine_length= cpu_to_be32(HEADER_SIZE+boot_size+2); /* Routine length */ |
| 105 | strncpy(bbh->routine_name, "LINUXROM", 8); /* Routine name */ |
| 106 | |
| 107 | /* Output the header and bootloader to the file */ |
| 108 | write(out_fd, header_block, HEADER_SIZE); |
| 109 | } |
| 110 | |
| 111 | uint16_t calc_checksum(int32_t bug_fd) |
| 112 | { |
| 113 | uint32_t checksum_var = 0; |
| 114 | uint8_t buf[2]; |
| 115 | int n; |
| 116 | |
| 117 | /* Checksum loop */ |
| 118 | while ( (n = read(bug_fd, buf, 2) ) ) |
| 119 | { |
| 120 | checksum_var = checksum_var + *(uint16_t *)buf; |
| 121 | |
| 122 | /* If we carry out, mask it and add one to the checksum */ |
| 123 | if (checksum_var >> 16) |
| 124 | checksum_var = (checksum_var & 0x0000ffff) + 1; |
| 125 | } |
| 126 | |
| 127 | return checksum_var; |
| 128 | } |
| 129 | |
| 130 | int main(int argc, char *argv[]) |
| 131 | { |
| 132 | int32_t image_fd, bugboot_fd; |
| 133 | int argptr = 1; |
| 134 | uint32_t kernel_size = 0; |
| 135 | uint16_t checksum = 0; |
| 136 | uint8_t bugbootname[256]; |
| 137 | |
| 138 | if ( (argc != 3) ) |
| 139 | { |
| 140 | fprintf(stderr, "usage: %s <kernel_image> <bugboot>\n",argv[0]); |
| 141 | exit(-1); |
| 142 | } |
| 143 | |
| 144 | /* Get file args */ |
| 145 | |
| 146 | /* kernel image file */ |
| 147 | if ((image_fd = open( argv[argptr] , 0)) < 0) |
| 148 | exit(-1); |
| 149 | argptr++; |
| 150 | |
| 151 | /* bugboot file */ |
| 152 | if ( !strcmp( argv[argptr], "-" ) ) |
| 153 | bugboot_fd = 1; /* stdout */ |
| 154 | else |
| 155 | if ((bugboot_fd = creat( argv[argptr] , 0755)) < 0) |
| 156 | exit(-1); |
| 157 | else |
| 158 | strcpy(bugbootname, argv[argptr]); |
| 159 | argptr++; |
| 160 | |
| 161 | /* Set file position after ROM header block where zImage will be written */ |
| 162 | lseek(bugboot_fd, HEADER_SIZE, SEEK_SET); |
| 163 | |
| 164 | /* Copy kernel image into bugboot image */ |
| 165 | kernel_size = copy_image(image_fd, bugboot_fd); |
| 166 | close(image_fd); |
| 167 | |
| 168 | /* Set file position to beginning where header/romboot will be written */ |
| 169 | lseek(bugboot_fd, 0, SEEK_SET); |
| 170 | |
| 171 | /* Write out BUG header/romboot */ |
| 172 | write_bugboot_header(bugboot_fd, kernel_size); |
| 173 | |
| 174 | /* Close bugboot file */ |
| 175 | close(bugboot_fd); |
| 176 | |
| 177 | /* Reopen it as read/write */ |
| 178 | bugboot_fd = open(bugbootname, O_RDWR); |
| 179 | |
| 180 | /* Calculate checksum */ |
| 181 | checksum = calc_checksum(bugboot_fd); |
| 182 | |
| 183 | /* Write out the calculated checksum */ |
| 184 | write(bugboot_fd, &checksum, 2); |
| 185 | |
| 186 | return 0; |
| 187 | } |