bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 1 | /* |
| 2 | * QEMU PPC PREP hardware System Emulator |
| 3 | * |
| 4 | * Copyright (c) 2003-2004 Jocelyn Mayer |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 24 | #include "vl.h" |
| 25 | #include "m48t59.h" |
| 26 | |
| 27 | //#define HARD_DEBUG_PPC_IO |
| 28 | //#define DEBUG_PPC_IO |
| 29 | |
| 30 | extern int loglevel; |
| 31 | extern FILE *logfile; |
| 32 | |
| 33 | #if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO) |
| 34 | #define DEBUG_PPC_IO |
| 35 | #endif |
| 36 | |
| 37 | #if defined (HARD_DEBUG_PPC_IO) |
| 38 | #define PPC_IO_DPRINTF(fmt, args...) \ |
| 39 | do { \ |
| 40 | if (loglevel > 0) { \ |
| 41 | fprintf(logfile, "%s: " fmt, __func__ , ##args); \ |
| 42 | } else { \ |
| 43 | printf("%s : " fmt, __func__ , ##args); \ |
| 44 | } \ |
| 45 | } while (0) |
| 46 | #elif defined (DEBUG_PPC_IO) |
| 47 | #define PPC_IO_DPRINTF(fmt, args...) \ |
| 48 | do { \ |
| 49 | if (loglevel > 0) { \ |
| 50 | fprintf(logfile, "%s: " fmt, __func__ , ##args); \ |
| 51 | } \ |
| 52 | } while (0) |
| 53 | #else |
| 54 | #define PPC_IO_DPRINTF(fmt, args...) do { } while (0) |
| 55 | #endif |
| 56 | |
| 57 | #define BIOS_FILENAME "ppc_rom.bin" |
| 58 | #define LINUX_BOOT_FILENAME "linux_boot.bin" |
| 59 | |
| 60 | #define KERNEL_LOAD_ADDR 0x00000000 |
| 61 | #define KERNEL_STACK_ADDR 0x00400000 |
| 62 | #define INITRD_LOAD_ADDR 0x00800000 |
| 63 | |
| 64 | int load_kernel(const char *filename, uint8_t *addr, |
| 65 | uint8_t *real_addr) |
| 66 | { |
| 67 | int fd, size; |
| 68 | int setup_sects; |
| 69 | |
| 70 | fd = open(filename, O_RDONLY); |
| 71 | if (fd < 0) |
| 72 | return -1; |
| 73 | |
| 74 | /* load 16 bit code */ |
| 75 | if (read(fd, real_addr, 512) != 512) |
| 76 | goto fail; |
| 77 | setup_sects = real_addr[0x1F1]; |
| 78 | if (!setup_sects) |
| 79 | setup_sects = 4; |
| 80 | if (read(fd, real_addr + 512, setup_sects * 512) != |
| 81 | setup_sects * 512) |
| 82 | goto fail; |
| 83 | |
| 84 | /* load 32 bit code */ |
| 85 | size = read(fd, addr, 16 * 1024 * 1024); |
| 86 | if (size < 0) |
| 87 | goto fail; |
| 88 | close(fd); |
| 89 | return size; |
| 90 | fail: |
| 91 | close(fd); |
| 92 | return -1; |
| 93 | } |
| 94 | |
| 95 | static const int ide_iobase[2] = { 0x1f0, 0x170 }; |
| 96 | static const int ide_iobase2[2] = { 0x3f6, 0x376 }; |
| 97 | static const int ide_irq[2] = { 13, 13 }; |
| 98 | |
| 99 | #define NE2000_NB_MAX 6 |
| 100 | |
| 101 | static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 }; |
| 102 | static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; |
| 103 | |
| 104 | /* IO ports emulation */ |
| 105 | #define PPC_IO_BASE 0x80000000 |
| 106 | |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 107 | static void PPC_io_writeb (target_phys_addr_t addr, uint32_t value) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 108 | { |
| 109 | /* Don't polute serial port output */ |
| 110 | #if 0 |
| 111 | if ((addr < 0x800003F0 || addr > 0x80000400) && |
| 112 | (addr < 0x80000074 || addr > 0x80000077) && |
| 113 | (addr < 0x80000020 || addr > 0x80000021) && |
| 114 | (addr < 0x800000a0 || addr > 0x800000a1) && |
| 115 | (addr < 0x800001f0 || addr > 0x800001f7) && |
| 116 | (addr < 0x80000170 || addr > 0x80000177)) |
| 117 | #endif |
| 118 | { |
| 119 | PPC_IO_DPRINTF("0x%08x => 0x%02x\n", addr - PPC_IO_BASE, value); |
| 120 | } |
| 121 | cpu_outb(NULL, addr - PPC_IO_BASE, value); |
| 122 | } |
| 123 | |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 124 | static uint32_t PPC_io_readb (target_phys_addr_t addr) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 125 | { |
| 126 | uint32_t ret = cpu_inb(NULL, addr - PPC_IO_BASE); |
| 127 | |
| 128 | #if 0 |
| 129 | if ((addr < 0x800003F0 || addr > 0x80000400) && |
| 130 | (addr < 0x80000074 || addr > 0x80000077) && |
| 131 | (addr < 0x80000020 || addr > 0x80000021) && |
| 132 | (addr < 0x800000a0 || addr > 0x800000a1) && |
| 133 | (addr < 0x800001f0 || addr > 0x800001f7) && |
| 134 | (addr < 0x80000170 || addr > 0x80000177) && |
| 135 | (addr < 0x8000060 || addr > 0x8000064)) |
| 136 | #endif |
| 137 | { |
| 138 | PPC_IO_DPRINTF("0x%08x <= 0x%02x\n", addr - PPC_IO_BASE, ret); |
| 139 | } |
| 140 | |
| 141 | return ret; |
| 142 | } |
| 143 | |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 144 | static void PPC_io_writew (target_phys_addr_t addr, uint32_t value) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 145 | { |
| 146 | if ((addr < 0x800001f0 || addr > 0x800001f7) && |
| 147 | (addr < 0x80000170 || addr > 0x80000177)) { |
| 148 | PPC_IO_DPRINTF("0x%08x => 0x%04x\n", addr - PPC_IO_BASE, value); |
| 149 | } |
| 150 | cpu_outw(NULL, addr - PPC_IO_BASE, value); |
| 151 | } |
| 152 | |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 153 | static uint32_t PPC_io_readw (target_phys_addr_t addr) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 154 | { |
| 155 | uint32_t ret = cpu_inw(NULL, addr - PPC_IO_BASE); |
| 156 | |
| 157 | if ((addr < 0x800001f0 || addr > 0x800001f7) && |
| 158 | (addr < 0x80000170 || addr > 0x80000177)) { |
| 159 | PPC_IO_DPRINTF("0x%08x <= 0x%04x\n", addr - PPC_IO_BASE, ret); |
| 160 | } |
| 161 | |
| 162 | return ret; |
| 163 | } |
| 164 | |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 165 | static void PPC_io_writel (target_phys_addr_t addr, uint32_t value) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 166 | { |
| 167 | PPC_IO_DPRINTF("0x%08x => 0x%08x\n", addr - PPC_IO_BASE, value); |
| 168 | cpu_outl(NULL, addr - PPC_IO_BASE, value); |
| 169 | } |
| 170 | |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 171 | static uint32_t PPC_io_readl (target_phys_addr_t addr) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 172 | { |
| 173 | uint32_t ret = cpu_inl(NULL, addr - PPC_IO_BASE); |
| 174 | |
| 175 | PPC_IO_DPRINTF("0x%08x <= 0x%08x\n", addr - PPC_IO_BASE, ret); |
| 176 | |
| 177 | return ret; |
| 178 | } |
| 179 | |
| 180 | static CPUWriteMemoryFunc *PPC_io_write[] = { |
| 181 | &PPC_io_writeb, |
| 182 | &PPC_io_writew, |
| 183 | &PPC_io_writel, |
| 184 | }; |
| 185 | |
| 186 | static CPUReadMemoryFunc *PPC_io_read[] = { |
| 187 | &PPC_io_readb, |
| 188 | &PPC_io_readw, |
| 189 | &PPC_io_readl, |
| 190 | }; |
| 191 | |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 192 | /* Read-only register (?) */ |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 193 | static void _PPC_ioB_write (target_phys_addr_t addr, uint32_t value) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 194 | { |
| 195 | // printf("%s: 0x%08x => 0x%08x\n", __func__, addr, value); |
| 196 | } |
| 197 | |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 198 | static uint32_t _PPC_ioB_read (target_phys_addr_t addr) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 199 | { |
| 200 | uint32_t retval = 0; |
| 201 | |
| 202 | if (addr == 0xBFFFFFF0) |
| 203 | retval = pic_intack_read(NULL); |
| 204 | // printf("%s: 0x%08x <= %d\n", __func__, addr, retval); |
| 205 | |
| 206 | return retval; |
| 207 | } |
| 208 | |
| 209 | static CPUWriteMemoryFunc *PPC_ioB_write[] = { |
| 210 | &_PPC_ioB_write, |
| 211 | &_PPC_ioB_write, |
| 212 | &_PPC_ioB_write, |
| 213 | }; |
| 214 | |
| 215 | static CPUReadMemoryFunc *PPC_ioB_read[] = { |
| 216 | &_PPC_ioB_read, |
| 217 | &_PPC_ioB_read, |
| 218 | &_PPC_ioB_read, |
| 219 | }; |
| 220 | |
| 221 | #if 0 |
| 222 | static CPUWriteMemoryFunc *PPC_io3_write[] = { |
| 223 | &PPC_io3_writeb, |
| 224 | &PPC_io3_writew, |
| 225 | &PPC_io3_writel, |
| 226 | }; |
| 227 | |
| 228 | static CPUReadMemoryFunc *PPC_io3_read[] = { |
| 229 | &PPC_io3_readb, |
| 230 | &PPC_io3_readw, |
| 231 | &PPC_io3_readl, |
| 232 | }; |
| 233 | #endif |
| 234 | |
| 235 | /* Fake super-io ports for PREP platform (Intel 82378ZB) */ |
| 236 | static uint8_t PREP_fake_io[2]; |
| 237 | static uint8_t NVRAM_lock; |
| 238 | |
| 239 | static void PREP_io_write (void *opaque, uint32_t addr, uint32_t val) |
| 240 | { |
| 241 | PPC_IO_DPRINTF("0x%08x => 0x%08x\n", addr - PPC_IO_BASE, val); |
| 242 | PREP_fake_io[addr - 0x0398] = val; |
| 243 | } |
| 244 | |
| 245 | static uint32_t PREP_io_read (void *opaque, uint32_t addr) |
| 246 | { |
| 247 | PPC_IO_DPRINTF("0x%08x <= 0x%08x\n", addr - PPC_IO_BASE, PREP_fake_io[addr - 0x0398]); |
| 248 | return PREP_fake_io[addr - 0x0398]; |
| 249 | } |
| 250 | |
| 251 | static uint8_t syscontrol; |
| 252 | |
| 253 | static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) |
| 254 | { |
| 255 | PPC_IO_DPRINTF("0x%08x => 0x%08x\n", addr - PPC_IO_BASE, val); |
| 256 | switch (addr) { |
| 257 | case 0x0092: |
| 258 | /* Special port 92 */ |
| 259 | /* Check soft reset asked */ |
| 260 | if (val & 0x80) { |
| 261 | printf("Soft reset asked... Stop emulation\n"); |
| 262 | abort(); |
| 263 | } |
| 264 | /* Check LE mode */ |
| 265 | if (val & 0x40) { |
| 266 | printf("Little Endian mode isn't supported (yet ?)\n"); |
| 267 | abort(); |
| 268 | } |
| 269 | break; |
| 270 | case 0x0808: |
| 271 | /* Hardfile light register: don't care */ |
| 272 | break; |
| 273 | case 0x0810: |
| 274 | /* Password protect 1 register */ |
| 275 | NVRAM_lock ^= 0x01; |
| 276 | break; |
| 277 | case 0x0812: |
| 278 | /* Password protect 2 register */ |
| 279 | NVRAM_lock ^= 0x02; |
| 280 | break; |
| 281 | case 0x0814: |
| 282 | /* L2 invalidate register: don't care */ |
| 283 | break; |
| 284 | case 0x081C: |
| 285 | /* system control register */ |
| 286 | syscontrol = val; |
| 287 | break; |
| 288 | case 0x0850: |
| 289 | /* I/O map type register */ |
| 290 | if (val & 0x80) { |
| 291 | printf("No support for non-continuous I/O map mode\n"); |
| 292 | abort(); |
| 293 | } |
| 294 | break; |
| 295 | default: |
| 296 | break; |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) |
| 301 | { |
| 302 | uint32_t retval = 0xFF; |
| 303 | |
| 304 | switch (addr) { |
| 305 | case 0x0092: |
| 306 | /* Special port 92 */ |
| 307 | retval = 0x40; |
| 308 | break; |
| 309 | case 0x080C: |
| 310 | /* Equipment present register: |
| 311 | * no L2 cache |
| 312 | * no upgrade processor |
| 313 | * no cards in PCI slots |
| 314 | * SCSI fuse is bad |
| 315 | */ |
| 316 | retval = 0xFC; |
| 317 | break; |
| 318 | case 0x0818: |
| 319 | /* Keylock */ |
| 320 | retval = 0x00; |
| 321 | break; |
| 322 | case 0x081C: |
| 323 | /* system control register |
| 324 | * 7 - 6 / 1 - 0: L2 cache enable |
| 325 | */ |
| 326 | retval = syscontrol; |
| 327 | break; |
| 328 | case 0x0823: |
| 329 | /* */ |
| 330 | retval = 0x03; /* no L2 cache */ |
| 331 | break; |
| 332 | case 0x0850: |
| 333 | /* I/O map type register */ |
| 334 | retval = 0x00; |
| 335 | break; |
| 336 | default: |
| 337 | break; |
| 338 | } |
| 339 | PPC_IO_DPRINTF("0x%08x <= 0x%08x\n", addr - PPC_IO_BASE, retval); |
| 340 | |
| 341 | return retval; |
| 342 | } |
| 343 | |
| 344 | #define NVRAM_SIZE 0x2000 |
| 345 | #define NVRAM_END 0x1FF0 |
| 346 | #define NVRAM_OSAREA_SIZE 512 |
| 347 | #define NVRAM_CONFSIZE 1024 |
| 348 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 349 | static inline void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 350 | { |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 351 | m48t59_set_addr(nvram, addr); |
| 352 | m48t59_write(nvram, value); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 353 | } |
| 354 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 355 | static inline uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 356 | { |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 357 | m48t59_set_addr(nvram, addr); |
| 358 | return m48t59_read(nvram); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 359 | } |
| 360 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 361 | static inline void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 362 | { |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 363 | m48t59_set_addr(nvram, addr); |
| 364 | m48t59_write(nvram, value >> 8); |
| 365 | m48t59_set_addr(nvram, addr + 1); |
| 366 | m48t59_write(nvram, value & 0xFF); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 367 | } |
| 368 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 369 | static inline uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 370 | { |
| 371 | uint16_t tmp; |
| 372 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 373 | m48t59_set_addr(nvram, addr); |
| 374 | tmp = m48t59_read(nvram) << 8; |
| 375 | m48t59_set_addr(nvram, addr + 1); |
| 376 | tmp |= m48t59_read(nvram); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 377 | |
| 378 | return tmp; |
| 379 | } |
| 380 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 381 | static inline void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 382 | uint32_t value) |
| 383 | { |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 384 | m48t59_set_addr(nvram, addr); |
| 385 | m48t59_write(nvram, value >> 24); |
| 386 | m48t59_set_addr(nvram, addr + 1); |
| 387 | m48t59_write(nvram, (value >> 16) & 0xFF); |
| 388 | m48t59_set_addr(nvram, addr + 2); |
| 389 | m48t59_write(nvram, (value >> 8) & 0xFF); |
| 390 | m48t59_set_addr(nvram, addr + 3); |
| 391 | m48t59_write(nvram, value & 0xFF); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 392 | } |
| 393 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 394 | static inline uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr) |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 395 | { |
| 396 | uint32_t tmp; |
| 397 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 398 | m48t59_set_addr(nvram, addr); |
| 399 | tmp = m48t59_read(nvram) << 24; |
| 400 | m48t59_set_addr(nvram, addr + 1); |
| 401 | tmp |= m48t59_read(nvram) << 16; |
| 402 | m48t59_set_addr(nvram, addr + 2); |
| 403 | tmp |= m48t59_read(nvram) << 8; |
| 404 | m48t59_set_addr(nvram, addr + 3); |
| 405 | tmp |= m48t59_read(nvram); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 406 | |
| 407 | return tmp; |
| 408 | } |
| 409 | |
| 410 | static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) |
| 411 | { |
| 412 | uint16_t tmp; |
| 413 | uint16_t pd, pd1, pd2; |
| 414 | |
| 415 | tmp = prev >> 8; |
| 416 | pd = prev ^ value; |
| 417 | pd1 = pd & 0x000F; |
| 418 | pd2 = ((pd >> 4) & 0x000F) ^ pd1; |
| 419 | tmp ^= (pd1 << 3) | (pd1 << 8); |
| 420 | tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); |
| 421 | |
| 422 | return tmp; |
| 423 | } |
| 424 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 425 | static void NVRAM_set_crc (m48t59_t *nvram, uint32_t addr, |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 426 | uint32_t start, uint32_t count) |
| 427 | { |
| 428 | uint32_t i; |
| 429 | uint16_t crc = 0xFFFF; |
| 430 | int odd = 0; |
| 431 | |
| 432 | if (count & 1) |
| 433 | odd = 1; |
| 434 | count &= ~1; |
| 435 | for (i = 0; i != count; i++) { |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 436 | crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 437 | } |
| 438 | if (odd) { |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 439 | crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 440 | } |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 441 | NVRAM_set_word(nvram, addr, crc); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 442 | } |
| 443 | |
| 444 | static void prep_NVRAM_init (void) |
| 445 | { |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 446 | m48t59_t *nvram; |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 447 | |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 448 | nvram = m48t59_init(8, 0x0074, NVRAM_SIZE); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 449 | /* NVRAM header */ |
| 450 | /* 0x00: NVRAM size in kB */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 451 | NVRAM_set_word(nvram, 0x00, NVRAM_SIZE >> 10); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 452 | /* 0x02: NVRAM version */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 453 | NVRAM_set_byte(nvram, 0x02, 0x01); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 454 | /* 0x03: NVRAM revision */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 455 | NVRAM_set_byte(nvram, 0x03, 0x01); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 456 | /* 0x08: last OS */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 457 | NVRAM_set_byte(nvram, 0x08, 0x00); /* Unknown */ |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 458 | /* 0x09: endian */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 459 | NVRAM_set_byte(nvram, 0x09, 'B'); /* Big-endian */ |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 460 | /* 0x0A: OSArea usage */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 461 | NVRAM_set_byte(nvram, 0x0A, 0x00); /* Empty */ |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 462 | /* 0x0B: PM mode */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 463 | NVRAM_set_byte(nvram, 0x0B, 0x00); /* Normal */ |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 464 | /* Restart block description record */ |
| 465 | /* 0x0C: restart block version */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 466 | NVRAM_set_word(nvram, 0x0C, 0x01); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 467 | /* 0x0E: restart block revision */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 468 | NVRAM_set_word(nvram, 0x0E, 0x01); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 469 | /* 0x20: restart address */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 470 | NVRAM_set_lword(nvram, 0x20, 0x00); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 471 | /* 0x24: save area address */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 472 | NVRAM_set_lword(nvram, 0x24, 0x00); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 473 | /* 0x28: save area length */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 474 | NVRAM_set_lword(nvram, 0x28, 0x00); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 475 | /* 0x1C: checksum of restart block */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 476 | NVRAM_set_crc(nvram, 0x1C, 0x0C, 32); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 477 | |
| 478 | /* Security section */ |
| 479 | /* Set all to zero */ |
| 480 | /* 0xC4: pointer to global environment area */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 481 | NVRAM_set_lword(nvram, 0xC4, 0x0100); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 482 | /* 0xC8: size of global environment area */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 483 | NVRAM_set_lword(nvram, 0xC8, |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 484 | NVRAM_END - NVRAM_OSAREA_SIZE - NVRAM_CONFSIZE - 0x0100); |
| 485 | /* 0xD4: pointer to configuration area */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 486 | NVRAM_set_lword(nvram, 0xD4, NVRAM_END - NVRAM_CONFSIZE); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 487 | /* 0xD8: size of configuration area */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 488 | NVRAM_set_lword(nvram, 0xD8, NVRAM_CONFSIZE); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 489 | /* 0xE8: pointer to OS specific area */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 490 | NVRAM_set_lword(nvram, 0xE8, |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 491 | NVRAM_END - NVRAM_CONFSIZE - NVRAM_OSAREA_SIZE); |
| 492 | /* 0xD8: size of OS specific area */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 493 | NVRAM_set_lword(nvram, 0xEC, NVRAM_OSAREA_SIZE); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 494 | |
| 495 | /* Configuration area */ |
| 496 | /* RTC init */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 497 | // NVRAM_set_lword(nvram, 0x1FFC, 0x50); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 498 | |
| 499 | /* 0x04: checksum 0 => OS area */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 500 | NVRAM_set_crc(nvram, 0x04, 0x00, |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 501 | NVRAM_END - NVRAM_CONFSIZE - NVRAM_OSAREA_SIZE); |
| 502 | /* 0x06: checksum of config area */ |
bellard | c5df018 | 2004-04-12 20:54:52 +0000 | [diff] [blame] | 503 | NVRAM_set_crc(nvram, 0x06, NVRAM_END - NVRAM_CONFSIZE, NVRAM_CONFSIZE); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 504 | } |
| 505 | |
| 506 | int load_initrd (const char *filename, uint8_t *addr) |
| 507 | { |
| 508 | int fd, size; |
| 509 | |
| 510 | printf("Load initrd\n"); |
| 511 | fd = open(filename, O_RDONLY); |
| 512 | if (fd < 0) |
| 513 | return -1; |
| 514 | size = read(fd, addr, 16 * 1024 * 1024); |
| 515 | if (size < 0) |
| 516 | goto fail; |
| 517 | close(fd); |
| 518 | printf("Load initrd: %d\n", size); |
| 519 | return size; |
| 520 | fail: |
| 521 | close(fd); |
| 522 | printf("Load initrd failed\n"); |
| 523 | return -1; |
| 524 | } |
| 525 | |
| 526 | /* Quick hack for PPC memory infos... */ |
| 527 | static void put_long (void *addr, uint32_t l) |
| 528 | { |
| 529 | char *pos = addr; |
| 530 | pos[0] = (l >> 24) & 0xFF; |
| 531 | pos[1] = (l >> 16) & 0xFF; |
| 532 | pos[2] = (l >> 8) & 0xFF; |
| 533 | pos[3] = l & 0xFF; |
| 534 | } |
| 535 | |
| 536 | /* bootloader infos are in the form: |
| 537 | * uint32_t TAG |
| 538 | * uint32_t TAG_size (from TAG to next TAG). |
| 539 | * data |
| 540 | * .... |
| 541 | */ |
| 542 | #if !defined (USE_OPEN_FIRMWARE) |
| 543 | static void *set_bootinfo_tag (void *addr, uint32_t tag, uint32_t size, |
| 544 | void *data) |
| 545 | { |
| 546 | char *pos = addr; |
| 547 | |
| 548 | put_long(pos, tag); |
| 549 | pos += 4; |
| 550 | put_long(pos, size + 8); |
| 551 | pos += 4; |
| 552 | memcpy(pos, data, size); |
| 553 | pos += size; |
| 554 | |
| 555 | return pos; |
| 556 | } |
| 557 | #endif |
| 558 | |
| 559 | typedef struct boot_dev_t { |
| 560 | const unsigned char *name; |
| 561 | int major; |
| 562 | int minor; |
| 563 | } boot_dev_t; |
| 564 | |
| 565 | static boot_dev_t boot_devs[] = |
| 566 | { |
| 567 | { "/dev/fd0", 2, 0, }, |
| 568 | { "/dev/fd1", 2, 1, }, |
| 569 | { "/dev/hda", 3, 1, }, |
| 570 | // { "/dev/ide/host0/bus0/target0/lun0/part1", 3, 1, }, |
| 571 | // { "/dev/hdc", 22, 0, }, |
| 572 | { "/dev/hdc", 22, 1, }, |
| 573 | { "/dev/ram0 init=/linuxrc", 1, 0, }, |
| 574 | }; |
| 575 | |
| 576 | /* BATU: |
| 577 | * BEPI : bloc virtual address |
| 578 | * BL : area size bits (128 kB is 0, 256 1, 512 3, ... |
| 579 | * Vs/Vp |
| 580 | * BATL: |
| 581 | * BPRN : bloc real address align on 4MB boundary |
| 582 | * WIMG : cache access mode : not used |
| 583 | * PP : protection bits |
| 584 | */ |
| 585 | static void setup_BAT (CPUPPCState *env, int BAT, |
| 586 | uint32_t virtual, uint32_t physical, |
| 587 | uint32_t size, int Vs, int Vp, int PP) |
| 588 | { |
| 589 | uint32_t sz_bits, tmp_sz, align, tmp; |
| 590 | |
| 591 | sz_bits = 0; |
| 592 | align = 131072; |
| 593 | for (tmp_sz = size / 131072; tmp_sz != 1; tmp_sz = tmp_sz >> 1) { |
| 594 | sz_bits = (sz_bits << 1) + 1; |
| 595 | align = align << 1; |
| 596 | } |
| 597 | tmp = virtual & ~(align - 1); /* Align virtual area start */ |
| 598 | tmp |= sz_bits << 2; /* Fix BAT size */ |
| 599 | tmp |= Vs << 1; /* Supervisor access */ |
| 600 | tmp |= Vp; /* User access */ |
| 601 | env->DBAT[0][BAT] = tmp; |
| 602 | env->IBAT[0][BAT] = tmp; |
| 603 | tmp = physical & ~(align - 1); /* Align physical area start */ |
| 604 | tmp |= 0; /* Don't care about WIMG */ |
| 605 | tmp |= PP; /* Protection */ |
| 606 | env->DBAT[1][BAT] = tmp; |
| 607 | env->IBAT[1][BAT] = tmp; |
| 608 | printf("Set BATU0 to 0x%08x BATL0 to 0x%08x\n", |
| 609 | env->DBAT[0][BAT], env->DBAT[1][BAT]); |
| 610 | } |
| 611 | |
| 612 | static void VGA_printf (uint8_t *s) |
| 613 | { |
| 614 | uint16_t *arg_ptr; |
| 615 | unsigned int format_width, i; |
| 616 | int in_format; |
| 617 | uint16_t arg, digit, nibble; |
| 618 | uint8_t c; |
| 619 | |
| 620 | arg_ptr = (uint16_t *)((void *)&s); |
| 621 | in_format = 0; |
| 622 | format_width = 0; |
| 623 | while ((c = *s) != '\0') { |
| 624 | if (c == '%') { |
| 625 | in_format = 1; |
| 626 | format_width = 0; |
| 627 | } else if (in_format) { |
| 628 | if ((c >= '0') && (c <= '9')) { |
| 629 | format_width = (format_width * 10) + (c - '0'); |
| 630 | } else if (c == 'x') { |
| 631 | arg_ptr++; // increment to next arg |
| 632 | arg = *arg_ptr; |
| 633 | if (format_width == 0) |
| 634 | format_width = 4; |
| 635 | digit = format_width - 1; |
| 636 | for (i = 0; i < format_width; i++) { |
| 637 | nibble = (arg >> (4 * digit)) & 0x000f; |
| 638 | if (nibble <= 9) |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 639 | PPC_io_writeb(PPC_IO_BASE + 0x500, nibble + '0'); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 640 | else |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 641 | PPC_io_writeb(PPC_IO_BASE + 0x500, nibble + 'A'); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 642 | digit--; |
| 643 | } |
| 644 | in_format = 0; |
| 645 | } |
| 646 | //else if (c == 'd') { |
| 647 | // in_format = 0; |
| 648 | // } |
| 649 | } else { |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 650 | PPC_io_writeb(PPC_IO_BASE + 0x500, c); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 651 | } |
| 652 | s++; |
| 653 | } |
| 654 | } |
| 655 | |
| 656 | static void VGA_init (void) |
| 657 | { |
| 658 | /* Basic VGA init, inspired by plex86 VGAbios */ |
| 659 | printf("Init VGA...\n"); |
| 660 | #if 1 |
| 661 | /* switch to color mode and enable CPU access 480 lines */ |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 662 | PPC_io_writeb(PPC_IO_BASE + 0x3C2, 0xC3); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 663 | /* more than 64k 3C4/04 */ |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 664 | PPC_io_writeb(PPC_IO_BASE + 0x3C4, 0x04); |
| 665 | PPC_io_writeb(PPC_IO_BASE + 0x3C5, 0x02); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 666 | #endif |
| 667 | VGA_printf("PPC VGA BIOS...\n"); |
| 668 | } |
| 669 | |
| 670 | extern CPUPPCState *global_env; |
| 671 | |
| 672 | void PPC_init_hw (/*CPUPPCState *env,*/ uint32_t mem_size, |
| 673 | uint32_t kernel_addr, uint32_t kernel_size, |
| 674 | uint32_t stack_addr, int boot_device, |
| 675 | const unsigned char *initrd_file) |
| 676 | { |
| 677 | CPUPPCState *env = global_env; |
| 678 | char *p; |
| 679 | #if !defined (USE_OPEN_FIRMWARE) |
| 680 | char *tmp; |
| 681 | uint32_t tmpi[2]; |
| 682 | #endif |
| 683 | |
| 684 | printf("RAM size: %u 0x%08x (%u)\n", mem_size, mem_size, mem_size >> 20); |
| 685 | #if defined (USE_OPEN_FIRMWARE) |
| 686 | setup_memory(env, mem_size); |
| 687 | #endif |
| 688 | |
| 689 | /* Fake bootloader */ |
| 690 | { |
| 691 | #if 1 |
| 692 | uint32_t offset = |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 693 | *((uint32_t *)(phys_ram_base + kernel_addr)); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 694 | #else |
| 695 | uint32_t offset = 12; |
| 696 | #endif |
| 697 | env->nip = kernel_addr + offset; |
| 698 | printf("Start address: 0x%08x\n", env->nip); |
| 699 | } |
| 700 | /* Set up msr according to PREP specification */ |
| 701 | msr_ee = 0; |
| 702 | msr_fp = 1; |
| 703 | msr_pr = 0; /* Start in supervisor mode */ |
| 704 | msr_me = 1; |
| 705 | msr_fe0 = msr_fe1 = 0; |
| 706 | msr_ip = 0; |
| 707 | msr_ir = msr_dr = 1; |
| 708 | // msr_sf = 0; |
| 709 | msr_le = msr_ile = 0; |
| 710 | env->gpr[1] = stack_addr; /* Let's have a stack */ |
| 711 | env->gpr[2] = 0; |
| 712 | env->gpr[8] = kernel_addr; |
| 713 | /* There is a bug in 2.4 kernels: |
| 714 | * if a decrementer exception is pending when it enables msr_ee, |
| 715 | * it's not ready to handle it... |
| 716 | */ |
| 717 | env->decr = 0xFFFFFFFF; |
| 718 | p = (void *)(phys_ram_base + kernel_addr); |
| 719 | #if !defined (USE_OPEN_FIRMWARE) |
| 720 | /* Let's register the whole memory available only in supervisor mode */ |
| 721 | setup_BAT(env, 0, 0x00000000, 0x00000000, mem_size, 1, 0, 2); |
| 722 | /* Avoid open firmware init call (to get a console) |
| 723 | * This will make the kernel think we are a PREP machine... |
| 724 | */ |
| 725 | put_long(p, 0xdeadc0de); |
| 726 | /* Build a real stack room */ |
| 727 | p = (void *)(phys_ram_base + stack_addr); |
| 728 | put_long(p, stack_addr); |
| 729 | p -= 32; |
| 730 | env->gpr[1] -= 32; |
| 731 | /* Pretend there are no residual data */ |
| 732 | env->gpr[3] = 0; |
| 733 | if (initrd_file != NULL) { |
| 734 | int size; |
| 735 | env->gpr[4] = (kernel_addr + kernel_size + 4095) & ~4095; |
| 736 | size = load_initrd(initrd_file, |
| 737 | (void *)((uint32_t)phys_ram_base + env->gpr[4])); |
| 738 | if (size < 0) { |
| 739 | /* No initrd */ |
| 740 | env->gpr[4] = env->gpr[5] = 0; |
| 741 | } else { |
| 742 | env->gpr[5] = size; |
| 743 | boot_device = 'e'; |
| 744 | } |
| 745 | printf("Initrd loaded at 0x%08x (%d) (0x%08x 0x%08x)\n", |
| 746 | env->gpr[4], env->gpr[5], kernel_addr, kernel_size); |
| 747 | } else { |
| 748 | env->gpr[4] = env->gpr[5] = 0; |
| 749 | } |
| 750 | /* We have to put bootinfos after the BSS |
| 751 | * The BSS starts after the kernel end. |
| 752 | */ |
| 753 | #if 0 |
| 754 | p = (void *)(((uint32_t)phys_ram_base + kernel_addr + |
| 755 | kernel_size + (1 << 20) - 1) & ~((1 << 20) - 1)); |
| 756 | #else |
| 757 | p = (void *)((uint32_t)phys_ram_base + kernel_addr + 0x400000); |
| 758 | #endif |
| 759 | if (loglevel > 0) { |
| 760 | fprintf(logfile, "bootinfos: %p 0x%08x\n", |
| 761 | p, (uint32_t)p - (uint32_t)phys_ram_base); |
| 762 | } else { |
| 763 | printf("bootinfos: %p 0x%08x\n", |
| 764 | p, (uint32_t)p - (uint32_t)phys_ram_base); |
| 765 | } |
| 766 | /* Command line: let's put it after bootinfos */ |
| 767 | #if 0 |
| 768 | sprintf(p + 0x1000, "console=ttyS0,9600 root=%02x%02x mem=%dM", |
| 769 | boot_devs[boot_device - 'a'].major, |
| 770 | boot_devs[boot_device - 'a'].minor, |
| 771 | mem_size >> 20); |
| 772 | #else |
| 773 | sprintf(p + 0x1000, "console=ttyS0,9600 console=tty0 root=%s mem=%dM", |
| 774 | boot_devs[boot_device - 'a'].name, |
| 775 | mem_size >> 20); |
| 776 | #endif |
| 777 | env->gpr[6] = (uint32_t)p + 0x1000 - (uint32_t)phys_ram_base; |
| 778 | env->gpr[7] = env->gpr[6] + strlen(p + 0x1000); |
| 779 | if (loglevel > 0) { |
| 780 | fprintf(logfile, "cmdline: %p 0x%08x [%s]\n", |
| 781 | p + 0x1000, env->gpr[6], p + 0x1000); |
| 782 | } else { |
| 783 | printf("cmdline: %p 0x%08x [%s]\n", |
| 784 | p + 0x1000, env->gpr[6], p + 0x1000); |
| 785 | } |
| 786 | /* BI_FIRST */ |
| 787 | p = set_bootinfo_tag(p, 0x1010, 0, 0); |
| 788 | /* BI_CMD_LINE */ |
| 789 | p = set_bootinfo_tag(p, 0x1012, env->gpr[7] - env->gpr[6], |
| 790 | (void *)(env->gpr[6] + (uint32_t)phys_ram_base)); |
| 791 | /* BI_MEM_SIZE */ |
| 792 | tmp = (void *)tmpi; |
| 793 | tmp[0] = (mem_size >> 24) & 0xFF; |
| 794 | tmp[1] = (mem_size >> 16) & 0xFF; |
| 795 | tmp[2] = (mem_size >> 8) & 0xFF; |
| 796 | tmp[3] = mem_size & 0xFF; |
| 797 | p = set_bootinfo_tag(p, 0x1017, 4, tmpi); |
| 798 | /* BI_INITRD */ |
| 799 | tmp[0] = (env->gpr[4] >> 24) & 0xFF; |
| 800 | tmp[1] = (env->gpr[4] >> 16) & 0xFF; |
| 801 | tmp[2] = (env->gpr[4] >> 8) & 0xFF; |
| 802 | tmp[3] = env->gpr[4] & 0xFF; |
| 803 | tmp[4] = (env->gpr[5] >> 24) & 0xFF; |
| 804 | tmp[5] = (env->gpr[5] >> 16) & 0xFF; |
| 805 | tmp[6] = (env->gpr[5] >> 8) & 0xFF; |
| 806 | tmp[7] = env->gpr[5] & 0xFF; |
| 807 | p = set_bootinfo_tag(p, 0x1014, 8, tmpi); |
| 808 | env->gpr[4] = env->gpr[5] = 0; |
| 809 | /* BI_LAST */ |
| 810 | p = set_bootinfo_tag(p, 0x1011, 0, 0); |
| 811 | #else |
| 812 | /* Set up MMU: |
| 813 | * kernel is loaded at kernel_addr and wants to be seen at 0x01000000 |
| 814 | */ |
| 815 | setup_BAT(env, 0, 0x01000000, kernel_addr, 0x00400000, 1, 0, 2); |
| 816 | { |
| 817 | #if 0 |
| 818 | uint32_t offset = |
bellard | 2e12669 | 2004-04-25 21:28:44 +0000 | [diff] [blame^] | 819 | *((uint32_t *)(phys_ram_base + kernel_addr)); |
bellard | a541f29 | 2004-04-12 20:39:29 +0000 | [diff] [blame] | 820 | #else |
| 821 | uint32_t offset = 12; |
| 822 | #endif |
| 823 | env->nip = 0x01000000 | (kernel_addr + offset); |
| 824 | printf("Start address: 0x%08x\n", env->nip); |
| 825 | } |
| 826 | env->gpr[1] = env->nip + (1 << 22); |
| 827 | p = (void *)(phys_ram_base + stack_addr); |
| 828 | put_long(p - 32, stack_addr); |
| 829 | env->gpr[1] -= 32; |
| 830 | printf("Kernel starts at 0x%08x stack 0x%08x\n", env->nip, env->gpr[1]); |
| 831 | /* We want all lower address not to be translated */ |
| 832 | setup_BAT(env, 1, 0x00000000, 0x00000000, 0x010000000, 1, 1, 2); |
| 833 | /* We also need a BAT to access OF */ |
| 834 | setup_BAT(env, 2, 0xFFFE0000, mem_size - 131072, 131072, 1, 0, 1); |
| 835 | /* Setup OF entry point */ |
| 836 | { |
| 837 | char *p; |
| 838 | p = (char *)phys_ram_base + mem_size - 131072; |
| 839 | /* Special opcode to call OF */ |
| 840 | *p++ = 0x18; *p++ = 0x00; *p++ = 0x00; *p++ = 0x02; |
| 841 | /* blr */ |
| 842 | *p++ = 0x4E; *p++ = 0x80; *p++ = 0x00; *p++ = 0x20; |
| 843 | } |
| 844 | env->gpr[5] = 0xFFFE0000; |
| 845 | /* Register translations */ |
| 846 | { |
| 847 | OF_transl_t translations[3] = { |
| 848 | { 0x01000000, 0x00400000, kernel_addr, 0x00000002, }, |
| 849 | { 0x00000000, 0x01000000, 0x00000000, 0x00000002, }, |
| 850 | { 0xFFFE0000, 0x00020000, mem_size - (128 * 1024), |
| 851 | 0x00000001, }, |
| 852 | }; |
| 853 | OF_register_translations(3, translations); |
| 854 | } |
| 855 | /* Quite artificial, for now */ |
| 856 | OF_register_bus("isa", "isa"); |
| 857 | OF_register_serial("isa", "serial", 4, 0x3f8); |
| 858 | OF_register_stdio("serial", "serial"); |
| 859 | /* Set up RTAS service */ |
| 860 | RTAS_init(); |
| 861 | /* Command line: let's put it just over the stack */ |
| 862 | #if 0 |
| 863 | #if 0 |
| 864 | p = (void *)(((uint32_t)phys_ram_base + kernel_addr + |
| 865 | kernel_size + (1 << 20) - 1) & ~((1 << 20) - 1)); |
| 866 | #else |
| 867 | p = (void *)((uint32_t)phys_ram_base + kernel_addr + 0x400000); |
| 868 | #endif |
| 869 | #if 1 |
| 870 | sprintf(p, "console=ttyS0,9600 root=%02x%02x mem=%dM", |
| 871 | boot_devs[boot_device - 'a'].major, |
| 872 | boot_devs[boot_device - 'a'].minor, |
| 873 | mem_size >> 20); |
| 874 | #else |
| 875 | sprintf(p, "console=ttyS0,9600 root=%s mem=%dM ne2000=0x300,9", |
| 876 | boot_devs[boot_device - 'a'].name, |
| 877 | mem_size >> 20); |
| 878 | #endif |
| 879 | OF_register_bootargs(p); |
| 880 | #endif |
| 881 | #endif |
| 882 | } |
| 883 | |
| 884 | void PPC_end_init (void) |
| 885 | { |
| 886 | VGA_init(); |
| 887 | } |
| 888 | |
| 889 | /* PC hardware initialisation */ |
| 890 | void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, |
| 891 | DisplayState *ds, const char **fd_filename, int snapshot, |
| 892 | const char *kernel_filename, const char *kernel_cmdline, |
| 893 | const char *initrd_filename) |
| 894 | { |
| 895 | char buf[1024]; |
| 896 | int PPC_io_memory; |
| 897 | int ret, linux_boot, initrd_size, i, nb_nics1, fd; |
| 898 | |
| 899 | linux_boot = (kernel_filename != NULL); |
| 900 | |
| 901 | /* allocate RAM */ |
| 902 | cpu_register_physical_memory(0, ram_size, 0); |
| 903 | |
| 904 | if (linux_boot) { |
| 905 | /* now we can load the kernel */ |
| 906 | ret = load_image(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); |
| 907 | if (ret < 0) { |
| 908 | fprintf(stderr, "qemu: could not load kernel '%s'\n", |
| 909 | kernel_filename); |
| 910 | exit(1); |
| 911 | } |
| 912 | /* load initrd */ |
| 913 | initrd_size = 0; |
| 914 | #if 0 |
| 915 | if (initrd_filename) { |
| 916 | initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR); |
| 917 | if (initrd_size < 0) { |
| 918 | fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", |
| 919 | initrd_filename); |
| 920 | exit(1); |
| 921 | } |
| 922 | } |
| 923 | #endif |
| 924 | PPC_init_hw(/*env,*/ ram_size, KERNEL_LOAD_ADDR, ret, |
| 925 | KERNEL_STACK_ADDR, boot_device, initrd_filename); |
| 926 | } else { |
| 927 | /* allocate ROM */ |
| 928 | // snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); |
| 929 | snprintf(buf, sizeof(buf), "%s", BIOS_FILENAME); |
| 930 | printf("load BIOS at %p\n", phys_ram_base + 0x000f0000); |
| 931 | ret = load_image(buf, phys_ram_base + 0x000f0000); |
| 932 | if (ret != 0x10000) { |
| 933 | fprintf(stderr, "qemu: could not load PPC bios '%s' (%d)\n%m\n", |
| 934 | buf, ret); |
| 935 | exit(1); |
| 936 | } |
| 937 | } |
| 938 | |
| 939 | /* init basic PC hardware */ |
| 940 | vga_initialize(ds, phys_ram_base + ram_size, ram_size, |
| 941 | vga_ram_size); |
| 942 | rtc_init(0x70, 8); |
| 943 | pic_init(); |
| 944 | // pit_init(0x40, 0); |
| 945 | |
| 946 | fd = serial_open_device(); |
| 947 | serial_init(0x3f8, 4, fd); |
| 948 | #if 1 |
| 949 | nb_nics1 = nb_nics; |
| 950 | if (nb_nics1 > NE2000_NB_MAX) |
| 951 | nb_nics1 = NE2000_NB_MAX; |
| 952 | for(i = 0; i < nb_nics1; i++) { |
| 953 | ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]); |
| 954 | } |
| 955 | #endif |
| 956 | |
| 957 | for(i = 0; i < 2; i++) { |
| 958 | ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], |
| 959 | bs_table[2 * i], bs_table[2 * i + 1]); |
| 960 | } |
| 961 | kbd_init(); |
| 962 | AUD_init(); |
| 963 | DMA_init(); |
| 964 | // SB16_init(); |
| 965 | |
| 966 | fdctrl_init(6, 2, 0, 0x3f0, fd_table); |
| 967 | |
| 968 | /* Register 64 kB of IO space */ |
| 969 | PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write); |
| 970 | cpu_register_physical_memory(0x80000000, 0x10000, PPC_io_memory); |
| 971 | /* Register fake IO ports for PREP */ |
| 972 | register_ioport_read(0x398, 2, 1, &PREP_io_read, NULL); |
| 973 | register_ioport_write(0x398, 2, 1, &PREP_io_write, NULL); |
| 974 | /* System control ports */ |
| 975 | register_ioport_write(0x0092, 0x1, 1, &PREP_io_800_writeb, NULL); |
| 976 | register_ioport_read(0x0800, 0x52, 1, &PREP_io_800_readb, NULL); |
| 977 | register_ioport_write(0x0800, 0x52, 1, &PREP_io_800_writeb, NULL); |
| 978 | /* PCI intack location (0xfef00000 / 0xbffffff0) */ |
| 979 | PPC_io_memory = cpu_register_io_memory(0, PPC_ioB_read, PPC_ioB_write); |
| 980 | cpu_register_physical_memory(0xBFFFFFF0, 0x4, PPC_io_memory); |
| 981 | // cpu_register_physical_memory(0xFEF00000, 0x4, PPC_io_memory); |
| 982 | prep_NVRAM_init(); |
| 983 | |
| 984 | PPC_end_init(); |
| 985 | } |