1/* a.out */
2struct 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
13struct 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
22static struct aout_state astate;
23
24static sector_t aout_download(unsigned char *data, unsigned int len, int eof);
25static 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
54static 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
132aout_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}
145