Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / arch / ppc / mol / main.c
1 /*
2  *   Creation Date: <2002/10/02 22:24:24 samuel>
3  *   Time-stamp: <2004/03/27 01:57:55 samuel>
4  *
5  *      <main.c>
6  *
7  *
8  *
9  *   Copyright (C) 2002, 2003, 2004 Samuel Rydh (samuel@ibrium.se)
10  *
11  *   This program is free software; you can redistribute it and/or
12  *   modify it under the terms of the GNU General Public License
13  *   as published by the Free Software Foundation
14  *
15  */
16
17
18 #include "config.h"
19 #include "libopenbios/bindings.h"
20 #include "libopenbios/elfload.h"
21 #include "arch/common/nvram.h"
22 #include "libc/diskio.h"
23 #include "libc/vsprintf.h"
24 #include "mol/mol.h"
25 #include "libopenbios/ofmem.h"
26 #include "osi_calls.h"
27 #include "ablk_sh.h"
28 #include "boothelper_sh.h"
29
30
31 static void     patch_newworld_rom( char *start, size_t size );
32 static void     newworld_timer_hack( char *start, size_t size );
33
34 static void
35 transfer_control_to_elf( unsigned long entry )
36 {
37         extern void call_elf( unsigned long entry );
38         printk("Starting ELF boot loader\n");
39         call_elf( entry );
40
41         fatal_error("call_elf returned unexpectedly\n");
42 }
43
44 static int
45 load_elf_rom( unsigned long *entry, int fd )
46 {
47         int i, lszz_offs, elf_offs;
48         char buf[128], *addr;
49         Elf_ehdr ehdr;
50         Elf_phdr *phdr;
51         size_t s;
52
53         printk("Loading '%s' from '%s'\n", get_file_path(fd),
54                get_volume_name(fd) );
55
56         /* the ELF-image (usually) starts at offset 0x4000 */
57         if( (elf_offs=find_elf(fd)) < 0 ) {
58                 printk("----> %s is not an ELF image\n", buf );
59                 exit(1);
60         }
61         if( !(phdr=elf_readhdrs(fd, elf_offs, &ehdr)) )
62                 fatal_error("elf_readhdrs failed\n");
63
64         *entry = ehdr.e_entry;
65
66         /* load segments. Compressed ROM-image assumed to be located immediately
67          * after the last segment */
68         lszz_offs = elf_offs;
69         for( i=0; i<ehdr.e_phnum; i++ ) {
70                 /* p_memsz, p_flags */
71                 s = MIN( phdr[i].p_filesz, phdr[i].p_memsz );
72                 seek_io( fd, elf_offs + phdr[i].p_offset );
73
74                 /* printk("filesz: %08lX memsz: %08lX p_offset: %08lX p_vaddr %08lX\n",
75                    phdr[i].p_filesz, phdr[i].p_memsz, phdr[i].p_offset,
76                    phdr[i].p_vaddr ); */
77
78                 if( phdr[i].p_vaddr != phdr[i].p_paddr )
79                         printk("WARNING: ELF segment virtual addr != physical addr\n");
80                 lszz_offs = MAX( lszz_offs, elf_offs + phdr[i].p_offset + phdr[i].p_filesz );
81                 if( !s )
82                         continue;
83                 if( ofmem_claim( phdr[i].p_vaddr, phdr[i].p_memsz, 0 ) == -1 )
84                         fatal_error("Claim failed!\n");
85
86                 addr = (char*)phdr[i].p_vaddr;
87                 if( read_io(fd, addr, s) != s )
88                         fatal_error("read failed\n");
89
90                 /* patch CODE segment */
91                 if( *entry >= phdr[i].p_vaddr && *entry < phdr[i].p_vaddr + s ) {
92                         patch_newworld_rom( (char*)phdr[i].p_vaddr, s );
93                         newworld_timer_hack( (char*)phdr[i].p_vaddr, s );
94                 }
95                 flush_icache_range( addr, addr+s );
96
97                 /* printk("ELF ROM-section loaded at %08lX (size %08lX)\n",
98                    (unsigned long)phdr[i].p_vaddr, (unsigned long)phdr[i].p_memsz );*/
99         }
100         free( phdr );
101         return lszz_offs;
102 }
103
104
105 /************************************************************************/
106 /*      newworld ROM loading                                            */
107 /************************************************************************/
108
109 #define ROM_BASE        0x1100000               /* where we decide to put things */
110
111 /* fix bug present in the 2.4 and the 3.0 Apple ROM */
112 static void
113 patch_newworld_rom( char *start, size_t size )
114 {
115         int s;
116         unsigned long mark[] = { 0x7c7d1b78,            /* mr r29,r3 */
117                                  0x7c9c2378,            /* mr r28,r4 */
118                                  0x7cc33378,            /* mr r3,r6 */
119                                  0x7c864214,            /* add r4,r6,r8   <------ BUG -- */
120                                  0x80b10000,            /* lwz r5,0(r17) */
121                                  0x38a500e8 };          /* addi r5,r5,232 */
122
123         /* Correcting add r4,r6,r8  ---->  addi r4,r6,8 */
124         for( s=0; s<size-sizeof(mark); s+=4 )
125                 if( memcmp( start+s, mark, sizeof(mark)) == 0 ) {
126                         printk("FIXING ROM BUG @ %X!\n", s+12);
127                         ((unsigned long*)(start+s))[3] = 0x38860008;    /* addi r4,r6,8 */
128                 }
129 }
130
131 /* This hack is only needed on machines with a timebase slower than 12.5 MHz
132  * (50 MHz bus frequency). Typically only old, accelerated machines fall
133  * into this category. The cause of the problem is an overflow in Apple's
134  * calibration routine.
135  */
136 static void
137 newworld_timer_hack( char *start, size_t size )
138 {
139         int s;
140         unsigned long mark[] = { 0x7d0000a6, 0x5507045e, 0x7ce00124, 0x4c00012c,
141                                  0x38e00000, 0x3c80000f, 0x6084ffff, 0x98830c00,
142                                  0x7c0006ac, 0x98830a00, 0x7c0006ac, 0x7c9603a6,
143                                  0x4c00012c, 0x7cb602a6, 0x2c050000, 0x4181fff8,
144                                  0x7c0004ac, 0x88830a00, 0x7c0006ac, 0x88a30800,
145                                  0x7c0006ac, 0x88c30a00, 0x7c0006ac, 0x7c043040,
146                                  0x40a2ffe4, 0x5085442e, 0x7ca500d0, 0x54a5043e,
147                                  0x7c053840, 0x7ca72b78, 0x4082ff9c, 0x7ca32b78,
148                                  0x7d000124, 0x4c00012c, 0x4e800020
149         };
150
151         /* return #via ticks corresponding to 0xfffff DEC ticks (VIA frequency == 47/60 MHz) */
152         for( s=0; s < size-sizeof(mark); s+=4 ) {
153                 if( !memcmp( start+s, mark, sizeof(mark)) ) {
154                         extern char timer_calib_start[], timer_calib_end[];
155                         extern unsigned long nw_dec_calibration;
156                         int hz = OSI_UsecsToMticks(1000);
157                         nw_dec_calibration = OSI_MticksToUsecs(0xfffff*47)/60;
158                         memcpy( start + s, timer_calib_start, timer_calib_end - timer_calib_start );
159
160                         printk("Timer calibration fix: %d.%02d MHz [%ld]\n",
161                                hz/1000, (hz/10)%100, nw_dec_calibration );
162                         break;
163                 }
164         }
165 }
166
167 static unsigned long
168 load_newworld_rom( int fd )
169 {
170         int lszz_offs, lszz_size;
171         unsigned long entry, data[2];
172         phandle_t ph;
173
174         lszz_offs = load_elf_rom( &entry, fd );
175         seek_io( fd, -1 );
176         lszz_size = tell(fd) - lszz_offs;
177         seek_io( fd, lszz_offs );
178
179         /* printk("Compressed ROM image: offset %08X, size %08X loaded at %08x\n",
180            lszz_offs, lszz_size, ROM_BASE ); */
181
182         if( ofmem_claim(ROM_BASE, lszz_size, 0) == -1 )
183                 fatal_error("Claim failure (lszz)!\n");
184
185         read_io( fd, (char*)ROM_BASE, lszz_size );
186
187         /* Fix the /rom/macos/AAPL,toolbox-image,lzss property (phys, size) */
188 #if 0
189         if( (ph=prom_create_node("/rom/macos/")) == -1 )
190                 fatal_error("Failed creating /rom/macos/");
191 #else
192         ph = find_dev("/rom/macos");
193 #endif
194         data[0] = ROM_BASE;
195         data[1] = lszz_size;
196         set_property( ph, "AAPL,toolbox-image,lzss", (char*)data, sizeof(data) );
197
198         /* The 7.8 rom (MacOS 9.2) uses AAPL,toolbox-parcels instead of
199          * AAPL,toolbox-image,lzss. It probably doesn't hurt to have it
200          * always present (we don't have an easy way to determine ROM version...)
201          */
202         set_property( ph, "AAPL,toolbox-parcels", (char*)data, sizeof(data) );
203         return entry;
204 }
205
206 static int
207 search_nwrom( int fd, int fast )
208 {
209         char *s, buf[128];
210         int found = 0;
211
212         if( fast ) {
213                 int ind;
214                 found = !reopen( fd, "\\\\:tbxi" );
215                 for( ind=0; !found && (s=BootHGetStrResInd("macos_rompath", buf, sizeof(buf), ind++, 0)) ; )
216                         found = !reopen( fd, s );
217                 for( ind=0; !found && (s=BootHGetStrResInd("macos_rompath_", buf, sizeof(buf), ind++, 0)) ; )
218                         found = !reopen( fd, s );
219         } else {
220                 printk("Searching %s for a 'Mac OS ROM' file\n", get_volume_name(fd) );
221                 if( !(found=reopen_nwrom(fd)) ) {
222                         printk(" \n**** HINT ***************************************************\n");
223                         printk("*  The booting can be speeded up by adding the line\n");
224                         printk("*      macos_rompath: '%s'\n", get_file_path(fd) );
225                         printk("*  to the /etc/mol/molrc.macos (recommended).\n");
226                         printk("*************************************************************\n \n");
227                 }
228         }
229         return found;
230 }
231
232 static void
233 encode_bootpath( const char *spec, const char *args )
234 {
235         phandle_t chosen_ph = find_dev("/chosen");
236         set_property( chosen_ph, "bootpath", spec, strlen(spec)+1 );
237         set_property( chosen_ph, "bootargs", args, strlen(args)+1 );
238 }
239
240 static char *
241 newworld_load( const char *node_path, const char *spec, int do_search )
242 {
243         char *p, *entry, buf[80];
244         int fd, len;
245
246         if( (fd=open_io(spec)) == -1 )
247                 return NULL;
248
249         if( !search_nwrom(fd, do_search) ) {
250                 close_io(fd);
251                 return NULL;
252         }
253         printk("Boot Disk: %s [%s]\n", spec, get_fstype(fd) );
254
255         entry = (char*)load_newworld_rom( fd );
256
257 #if 1
258         PUSH_ih( get_ih_from_fd(fd) );
259         fword("get-instance-path");
260         len = POP();
261         p = (char*)POP();
262         buf[0] = 0;
263         if( len < sizeof(buf) ) {
264                 memcpy( buf, p, len );
265                 buf[len] =0;
266         }
267         strcat( buf, "/x@:" );
268         printk("boot_path: %s\n", buf );
269         encode_bootpath( buf, "" );
270 #endif
271         close_io( fd );
272         return entry;
273 }
274
275 static void
276 newworld_startup( void )
277 {
278         int i, j, bootunit, type, fd;
279         ablk_disk_info_t info;
280         char *entry = NULL;
281         char spec[80];
282         phandle_t ph;
283
284         char path[]="/pci/pci-bridge/mol-blk";
285         if( !(ph=find_dev(path)) )
286                 fatal_error("MOLBlockDriver node not found\n");
287
288         /* user-specified newworld ROMs take precedence */
289         if( (fd=open_io("pseudo:,nwrom")) >= 0 ) {
290                 entry = (char*)load_newworld_rom( fd );
291                 close_io( fd );
292         }
293
294         /* determine boot volume */
295         for( bootunit=-1, type=0; bootunit==-1 && type<3 ; type++ ) {
296                 for( i=0; !OSI_ABlkDiskInfo(0, i, &info) ; i++ ) {
297                         if( type<=1 && !(info.flags & ABLK_BOOT_HINT) )
298                                 continue;
299                         if( type>1 && (info.flags & ABLK_BOOT_HINT) )
300                                 continue;
301
302                         for( j=0; !entry && j<32; j++ ) {
303                                 snprintf( spec, sizeof(spec), "%s/disk@%x:%d",
304                                           path, i, j );
305                                 entry = newworld_load( path, spec, (!type || type==2) );
306                         }
307                         if( entry ) {
308                                 bootunit = i;
309                                 break;
310                         }
311                 }
312         }
313
314         if( entry ) {
315                 OSI_ABlkBlessDisk( 0 /*channel*/, bootunit );
316
317                 update_nvram();
318                 transfer_control_to_elf( (unsigned long)entry );
319                 /* won't come here */
320                 return;
321         }
322
323         printk("\n--- No bootable disk was found! -----------------------------\n");
324         printk("If this is an oldworld machine, try booting from the MacOS\n");
325         printk("install CD and install MacOS from within MOL.\n");
326         printk("-------------------------------------------------------------\n");
327         exit(1);
328 }
329
330
331 /************************************************************************/
332 /*      yaboot booting                                                  */
333 /************************************************************************/
334
335 static void
336 yaboot_startup( void )
337 {
338         const char *paths[] = { "pseudo:,ofclient", "pseudo:,yaboot", NULL };
339         unsigned long entry;
340         int i, fd;
341
342         for( i=0; paths[i]; i++ ) {
343                 if( (fd=open_io(paths[i])) == -1 )
344                         continue;
345                 (void) load_elf_rom( &entry, fd );
346                 close_io( fd );
347                 encode_bootpath( paths[i], "" );
348
349                 update_nvram();
350                 transfer_control_to_elf( entry );
351                 /* won't come here */
352         }
353         printk("*** Boot failure! No secondary bootloader specified ***\n");
354         exit(1);
355 }
356
357
358 /************************************************************************/
359 /*      entry                                                           */
360 /************************************************************************/
361
362 void
363 boot( void )
364 {
365         fword("update-chosen");
366         if( find_dev("/mol-platform") )
367                 yaboot_startup();
368         else
369                 newworld_startup();
370 }