Initial code release
[external/syslinux.git] / gpxe / src / arch / i386 / core / aout_loader.c
1 /* a.out */
2 struct exec {
3         unsigned long      a_midmag;    /* flags<<26 | mid<<16 | magic */
4         unsigned long      a_text;      /* text segment size */
5         unsigned long      a_data;      /* initialized data size */
6         unsigned long      a_bss;       /* uninitialized data size */
7         unsigned long      a_syms;      /* symbol table size */
8         unsigned long      a_entry;     /* entry point */
9         unsigned long      a_trsize;    /* text relocation size */
10         unsigned long      a_drsize;    /* data relocation size */
11 };
12
13 struct aout_state {
14         struct exec head;
15         unsigned long curaddr;
16         int segment;                    /* current segment number, -1 for none */
17         unsigned long loc;              /* start offset of current block */
18         unsigned long skip;             /* padding to be skipped to current segment */
19         unsigned long toread;           /* remaining data to be read in the segment */
20 };
21
22 static struct aout_state astate;
23
24 static sector_t aout_download(unsigned char *data, unsigned int len, int eof);
25 static inline os_download_t aout_probe(unsigned char *data, unsigned int len)
26 {
27         unsigned long start, mid, end, istart, iend;
28         if (len < sizeof(astate.head)) {
29                 return 0;
30         }
31         memcpy(&astate.head, data, sizeof(astate.head));
32         if ((astate.head.a_midmag & 0xffff) != 0x010BL) {
33                 return 0;
34         }
35         
36         printf("(a.out");
37         aout_freebsd_probe();
38         printf(")... ");
39         /* Check the aout image */
40         start  = astate.head.a_entry;
41         mid    = (((start + astate.head.a_text) + 4095) & ~4095) + astate.head.a_data;
42         end    = ((mid + 4095) & ~4095) + astate.head.a_bss;
43         istart = 4096;
44         iend   = istart + (mid - start);
45         if (!prep_segment(start, mid, end, istart, iend))
46                 return dead_download;
47         astate.segment = -1;
48         astate.loc = 0;
49         astate.skip = 0;
50         astate.toread = 0;
51         return aout_download;
52 }
53
54 static sector_t aout_download(unsigned char *data, unsigned int len, int eof)
55 {
56         unsigned int offset;    /* working offset in the current data block */
57
58         offset = 0;
59
60 #ifdef AOUT_LYNX_KDI
61         astate.segment++;
62         if (astate.segment == 0) {
63                 astate.curaddr = 0x100000;
64                 astate.head.a_entry = astate.curaddr + 0x20;
65         }
66         memcpy(phys_to_virt(astate.curaddr), data, len);
67         astate.curaddr += len;
68         return 0;
69 #endif
70
71         do {
72                 if (astate.segment != -1) {
73                         if (astate.skip) {
74                                 if (astate.skip >= len - offset) {
75                                         astate.skip -= len - offset;
76                                         break;
77                                 }
78                                 offset += astate.skip;
79                                 astate.skip = 0;
80                         }
81
82                         if (astate.toread) {
83                                 if (astate.toread >= len - offset) {
84                                         memcpy(phys_to_virt(astate.curaddr), data+offset,
85                                                 len - offset);
86                                         astate.curaddr += len - offset;
87                                         astate.toread -= len - offset;
88                                         break;
89                                 }
90                                 memcpy(phys_to_virt(astate.curaddr), data+offset, astate.toread);
91                                 offset += astate.toread;
92                                 astate.toread = 0;
93                         }
94                 }
95
96                 /* Data left, but current segment finished - look for the next
97                  * segment.  This is quite simple for a.out files.  */
98                 astate.segment++;
99                 switch (astate.segment) {
100                 case 0:
101                         /* read text */
102                         astate.curaddr = astate.head.a_entry;
103                         astate.skip = 4096;
104                         astate.toread = astate.head.a_text;
105                         break;
106                 case 1:
107                         /* read data */
108                         /* skip and curaddr may be wrong, but I couldn't find
109                          * examples where this failed.  There is no reasonable
110                          * documentation for a.out available.  */
111                         astate.skip = ((astate.curaddr + 4095) & ~4095) - astate.curaddr;
112                         astate.curaddr = (astate.curaddr + 4095) & ~4095;
113                         astate.toread = astate.head.a_data;
114                         break;
115                 case 2:
116                         /* initialize bss and start kernel */
117                         astate.curaddr = (astate.curaddr + 4095) & ~4095;
118                         astate.skip = 0;
119                         astate.toread = 0;
120                         memset(phys_to_virt(astate.curaddr), '\0', astate.head.a_bss);
121                         goto aout_startkernel;
122                 default:
123                         break;
124                 }
125         } while (offset < len);
126
127         astate.loc += len;
128
129         if (eof) {
130                 unsigned long entry;
131
132 aout_startkernel:
133                 entry = astate.head.a_entry;
134                 done(1);
135
136                 aout_freebsd_boot();
137 #ifdef AOUT_LYNX_KDI
138                 xstart32(entry);
139 #endif
140                 printf("unexpected a.out variant\n");
141                 longjmp(restart_etherboot, -2);
142         }
143         return 0;
144 }