1 /* 2 * ARM kernel loader. 3 * 4 * Copyright (c) 2006-2007 CodeSourcery. 5 * Written by Paul Brook 6 * 7 * This code is licenced under the GPL. 8 */ 9 10 #include "hw/hw.h" 11 #include "hw/arm/arm.h" 12 #include "sysemu/sysemu.h" 13 14 #define KERNEL_ARGS_ADDR 0x100 15 #define KERNEL_LOAD_ADDR 0x00010000 16 #define INITRD_LOAD_ADDR 0x00800000 17 18 /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ 19 static uint32_t bootloader[] = { 20 0xe3a00000, /* mov r0, #0 */ 21 0xe3a01000, /* mov r1, #0x?? */ 22 0xe3811c00, /* orr r1, r1, #0x??00 */ 23 0xe59f2000, /* ldr r2, [pc, #0] */ 24 0xe59ff000, /* ldr pc, [pc, #0] */ 25 0, /* Address of kernel args. Set by integratorcp_init. */ 26 0 /* Kernel entry point. Set by integratorcp_init. */ 27 }; 28 29 /* Entry point for secondary CPUs. Enable interrupt controller and 30 Issue WFI until start address is written to system controller. */ 31 static uint32_t smpboot[] = { 32 0xe3a00201, /* mov r0, #0x10000000 */ 33 0xe3800601, /* orr r0, r0, #0x001000000 */ 34 0xe3a01001, /* mov r1, #1 */ 35 0xe5801100, /* str r1, [r0, #0x100] */ 36 0xe3a00201, /* mov r0, #0x10000000 */ 37 0xe3800030, /* orr r0, #0x30 */ 38 0xe320f003, /* wfi */ 39 0xe5901000, /* ldr r1, [r0] */ 40 0xe3110003, /* tst r1, #3 */ 41 0x1afffffb, /* bne <wfi> */ 42 0xe12fff11 /* bx r1 */ 43 }; 44 45 static void main_cpu_reset(void *opaque) 46 { 47 CPUARMState *env = opaque; 48 49 cpu_reset(ENV_GET_CPU(env)); 50 if (env->boot_info) 51 arm_load_kernel(env, (struct arm_boot_info*)env->boot_info); 52 53 /* TODO: Reset secondary CPUs. */ 54 } 55 56 #define WRITE_WORD(p, value) do { \ 57 stl_phys_notdirty(p, value); \ 58 p += 4; \ 59 } while (0) 60 61 static void set_kernel_args(const struct arm_boot_info *info, 62 int initrd_size, hwaddr base) 63 { 64 hwaddr p; 65 66 p = base + KERNEL_ARGS_ADDR; 67 /* ATAG_CORE */ 68 WRITE_WORD(p, 5); 69 WRITE_WORD(p, 0x54410001); 70 WRITE_WORD(p, 1); 71 WRITE_WORD(p, 0x1000); 72 WRITE_WORD(p, 0); 73 /* ATAG_MEM */ 74 /* TODO: handle multiple chips on one ATAG list */ 75 WRITE_WORD(p, 4); 76 WRITE_WORD(p, 0x54410002); 77 WRITE_WORD(p, info->ram_size); 78 WRITE_WORD(p, info->loader_start); 79 if (initrd_size) { 80 /* ATAG_INITRD2 */ 81 WRITE_WORD(p, 4); 82 WRITE_WORD(p, 0x54420005); 83 WRITE_WORD(p, info->loader_start + INITRD_LOAD_ADDR); 84 WRITE_WORD(p, initrd_size); 85 } 86 if (info->kernel_cmdline && *info->kernel_cmdline) { 87 /* ATAG_CMDLINE */ 88 int cmdline_size; 89 90 cmdline_size = strlen(info->kernel_cmdline); 91 cpu_physical_memory_write(p + 8, (void *)info->kernel_cmdline, 92 cmdline_size + 1); 93 cmdline_size = (cmdline_size >> 2) + 1; 94 WRITE_WORD(p, cmdline_size + 2); 95 WRITE_WORD(p, 0x54410009); 96 p += cmdline_size * 4; 97 } 98 if (info->atag_board) { 99 /* ATAG_BOARD */ 100 int atag_board_len; 101 uint8_t atag_board_buf[0x1000]; 102 103 atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3; 104 WRITE_WORD(p, (atag_board_len + 8) >> 2); 105 WRITE_WORD(p, 0x414f4d50); 106 cpu_physical_memory_write(p, atag_board_buf, atag_board_len); 107 p += atag_board_len; 108 } 109 /* ATAG_END */ 110 WRITE_WORD(p, 0); 111 WRITE_WORD(p, 0); 112 } 113 114 static void set_kernel_args_old(const struct arm_boot_info *info, 115 int initrd_size, hwaddr base) 116 { 117 hwaddr p; 118 const char *s; 119 120 121 /* see linux/include/asm-arm/setup.h */ 122 p = base + KERNEL_ARGS_ADDR; 123 /* page_size */ 124 WRITE_WORD(p, 4096); 125 /* nr_pages */ 126 WRITE_WORD(p, info->ram_size / 4096); 127 /* ramdisk_size */ 128 WRITE_WORD(p, 0); 129 #define FLAG_READONLY 1 130 #define FLAG_RDLOAD 4 131 #define FLAG_RDPROMPT 8 132 /* flags */ 133 WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT); 134 /* rootdev */ 135 WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */ 136 /* video_num_cols */ 137 WRITE_WORD(p, 0); 138 /* video_num_rows */ 139 WRITE_WORD(p, 0); 140 /* video_x */ 141 WRITE_WORD(p, 0); 142 /* video_y */ 143 WRITE_WORD(p, 0); 144 /* memc_control_reg */ 145 WRITE_WORD(p, 0); 146 /* unsigned char sounddefault */ 147 /* unsigned char adfsdrives */ 148 /* unsigned char bytes_per_char_h */ 149 /* unsigned char bytes_per_char_v */ 150 WRITE_WORD(p, 0); 151 /* pages_in_bank[4] */ 152 WRITE_WORD(p, 0); 153 WRITE_WORD(p, 0); 154 WRITE_WORD(p, 0); 155 WRITE_WORD(p, 0); 156 /* pages_in_vram */ 157 WRITE_WORD(p, 0); 158 /* initrd_start */ 159 if (initrd_size) 160 WRITE_WORD(p, info->loader_start + INITRD_LOAD_ADDR); 161 else 162 WRITE_WORD(p, 0); 163 /* initrd_size */ 164 WRITE_WORD(p, initrd_size); 165 /* rd_start */ 166 WRITE_WORD(p, 0); 167 /* system_rev */ 168 WRITE_WORD(p, 0); 169 /* system_serial_low */ 170 WRITE_WORD(p, 0); 171 /* system_serial_high */ 172 WRITE_WORD(p, 0); 173 /* mem_fclk_21285 */ 174 WRITE_WORD(p, 0); 175 /* zero unused fields */ 176 while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) { 177 WRITE_WORD(p, 0); 178 } 179 s = info->kernel_cmdline; 180 if (s) { 181 cpu_physical_memory_write(p, (void *)s, strlen(s) + 1); 182 } else { 183 WRITE_WORD(p, 0); 184 } 185 } 186 187 void arm_load_kernel(CPUARMState *env, struct arm_boot_info *info) 188 { 189 int kernel_size; 190 int initrd_size; 191 int n; 192 int is_linux = 0; 193 int nb_cpus; 194 uint64_t elf_entry; 195 hwaddr entry; 196 197 /* Load the kernel. */ 198 if (!info->kernel_filename) { 199 fprintf(stderr, "Kernel image must be specified\n"); 200 exit(1); 201 } 202 203 nb_cpus = info->nb_cpus ? info->nb_cpus : 1; 204 env->boot_info = info; 205 206 /* Assume that raw images are linux kernels, and ELF images are not. */ 207 kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL); 208 entry = elf_entry; 209 if (kernel_size < 0) { 210 kernel_size = load_uimage(info->kernel_filename, &entry, NULL, 211 &is_linux); 212 } 213 if (kernel_size < 0) { 214 entry = info->loader_start + KERNEL_LOAD_ADDR; 215 kernel_size = load_image_targphys(info->kernel_filename, entry, 216 ram_size - KERNEL_LOAD_ADDR); 217 is_linux = 1; 218 } 219 if (kernel_size < 0) { 220 fprintf(stderr, "qemu: could not load kernel '%s'\n", 221 info->kernel_filename); 222 exit(1); 223 } 224 if (!is_linux) { 225 /* Jump to the entry point. */ 226 env->regs[15] = entry & 0xfffffffe; 227 env->thumb = entry & 1; 228 } else { 229 if (info->initrd_filename) { 230 initrd_size = load_image_targphys(info->initrd_filename, 231 info->loader_start 232 + INITRD_LOAD_ADDR, 233 ram_size - INITRD_LOAD_ADDR); 234 if (initrd_size < 0) { 235 fprintf(stderr, "qemu: could not load initrd '%s'\n", 236 info->initrd_filename); 237 exit(1); 238 } 239 } else { 240 initrd_size = 0; 241 } 242 bootloader[1] |= info->board_id & 0xff; 243 bootloader[2] |= (info->board_id >> 8) & 0xff; 244 bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR; 245 bootloader[6] = entry; 246 for (n = 0; n < sizeof(bootloader) / 4; n++) { 247 stl_phys_notdirty(info->loader_start + (n * 4), bootloader[n]); 248 } 249 if (nb_cpus > 1) { 250 for (n = 0; n < sizeof(smpboot) / 4; n++) { 251 stl_phys_notdirty(info->smp_loader_start + (n * 4), smpboot[n]); 252 } 253 } 254 if (old_param) 255 set_kernel_args_old(info, initrd_size, info->loader_start); 256 else 257 set_kernel_args(info, initrd_size, info->loader_start); 258 } 259 //info->is_linux = is_linux; 260 qemu_register_reset(main_cpu_reset, 0, env); 261 } 262