2 * Copyright (c) 2007-2009 Josef 'Jeff' Sipek
6 #include <binfmt_elf.h>
8 static unsigned char seek_data
[6];
9 static unsigned char search_data
[5];
10 static struct ccw ccw
[4];
19 static u8
*buf
= (u8
*) (16 * 1024);
20 static u32
*ptrbuf
= (u32
*) (20 * 1024);
25 * NOTE: we don't care about not clobbering registers as when this
26 * code executes, the CPU will be stopped.
28 static inline void die(void)
31 "SR %r1, %r1 # not used, but should be zero\n"
32 "SR %r3, %r3 # CPU Address\n"
33 "SIGP %r1, %r3, 0x05 # Signal, order 0x05\n"
37 * Just in case SIGP fails
42 static u64 pgm_new_psw
[2] = {
43 0x0000000180000000ULL
, (u64
) &PGMHANDLER
,
47 * determine amount of storage
49 static u64
sense_memsize(void)
54 #define SKIP_SIZE (1024*1024ULL)
57 memcpy((void*)0x1d0, pgm_new_psw
, 16);
59 for(size
= 0; size
< ((u64
)~SKIP_SIZE
)-1; size
+= SKIP_SIZE
) {
74 * we cheat here a little...if we try to tprot a location
75 * that isn't part of the configuration, a program exception
76 * fires off, but our handler sets the CC to 3, and resumes
83 /* invalidate new PGM psw */
84 memset((void*)0x1d0, 0, 16);
89 static void read_blk(void *ptr
, u32 lba
)
96 memset(ccw
, 0, sizeof(ccw
));
99 cc
= lba
/ RECORDS_PER_CYL
;
100 hh
= (lba
% RECORDS_PER_CYL
) / RECORDS_PER_TRACK
;
101 r
= (lba
% RECORDS_PER_CYL
) % RECORDS_PER_TRACK
;
104 ORB
.addr
= ADDR31(ccw
);
108 ccw
[0].flags
= CCW_FLAG_CC
| CCW_FLAG_SLI
;
110 ccw
[0].addr
= ADDR31(seek_data
);
112 seek_data
[0] = 0; /* zero */
113 seek_data
[1] = 0; /* zero */
114 seek_data
[2] = cc
>> 8; /* Cc */
115 seek_data
[3] = cc
& 0xff; /* cC */
116 seek_data
[4] = hh
>> 8; /* Hh */
117 seek_data
[5] = hh
& 0xff; /* hH */
121 ccw
[1].flags
= CCW_FLAG_CC
| CCW_FLAG_SLI
;
123 ccw
[1].addr
= ADDR31(search_data
);
125 search_data
[0] = cc
>> 8;
126 search_data
[1] = cc
& 0xff;
127 search_data
[2] = hh
>> 8;
128 search_data
[3] = hh
& 0xff;
135 ccw
[2].addr
= ADDR31(&ccw
[1]);
141 ccw
[3].addr
= ADDR31(ptr
);
150 * read the entire nucleus into TEMP_BASE
152 static inline void readnucleus(void)
154 struct ADT
*ADT
= (struct ADT
*) buf
;
155 struct FST
*FST
= (struct FST
*) buf
;
160 read_blk(buf
, EDF_LABEL_BLOCK_NO
);
162 if ((ADT
->IDENT
!= __ADTIDENT
) ||
163 (ADT
->DBSIZ
!= EDF_SUPPORTED_BLOCK_SIZE
) ||
165 (ADT
->FSTSZ
!= sizeof(struct FST
)))
170 read_blk(buf
, ADT
->DOP
);
175 for(i
=0,found
=0; i
<nfst
; i
++) {
176 if ((!memcmp(FST
[i
].FNAME
, CP_FN
, 8)) &&
177 (!memcmp(FST
[i
].FTYPE
, CP_FT
, 8))) {
178 memcpy(&fst
, &FST
[i
], sizeof(struct FST
));
187 if (fst
.PTRSZ
!= 4 ||
189 fst
.RECFM
!= FSTDFIX
)
192 /* Don't allow more than 3MB to be read */
193 if ((FST
->AIC
* FST
->LRECL
) > (3ULL * 1024 * 1024))
196 /* Since we're assuming that NLVL==1, there's only 1 pointer block */
197 read_blk(ptrbuf
, fst
.FOP
);
199 /* Read all the blocks pointed to by the ptr block */
200 for(i
=0; i
<fst
.AIC
; i
++)
201 read_blk(TEMP_BASE
+ (4096 * i
), ptrbuf
[i
]);
204 void load_nucleus(void)
207 * These are all stored in registers
211 register Elf64_Ehdr
*nucleus_elf
;
212 register Elf64_Shdr
*section
, *link_section
, *tmp_section
;
213 register Elf64_Phdr
*segment
;
214 register Elf64_Ehdr
*elfcpy
;
215 register unsigned char *symtab
;
216 register void (*start_sym
)(u64
, u32
, void*);
218 /* Save the IPL device subchannel id */
219 iplsch
= *((u32
*) 0xb8);
222 * Read entire ELF to temporary location
226 nucleus_elf
= (Elf64_Ehdr
*) TEMP_BASE
;
229 * Check that this looks like a valid ELF
231 if (nucleus_elf
->e_ident
[0] != '\x7f' ||
232 nucleus_elf
->e_ident
[1] != 'E' ||
233 nucleus_elf
->e_ident
[2] != 'L' ||
234 nucleus_elf
->e_ident
[3] != 'F' ||
235 nucleus_elf
->e_ident
[EI_CLASS
] != ELFCLASS64
||
236 nucleus_elf
->e_ident
[EI_DATA
] != ELFDATA2MSB
||
237 nucleus_elf
->e_ident
[EI_VERSION
] != EV_CURRENT
||
238 nucleus_elf
->e_type
!= ET_EXEC
||
239 nucleus_elf
->e_machine
!= 0x16 || // FIXME: find the name for the #define
240 nucleus_elf
->e_version
!= EV_CURRENT
)
243 /* Iterate through each program header, and copy all PT_LOAD
244 * segments to the final destinations.
247 for(i
=0; i
<nucleus_elf
->e_phnum
; i
++) {
248 segment
= (Elf64_Phdr
*) (TEMP_BASE
+
249 nucleus_elf
->e_phoff
+
250 nucleus_elf
->e_phentsize
* i
);
252 if (segment
->p_type
!= PT_LOAD
)
255 if (segment
->p_filesz
)
256 memcpy((void*) segment
->p_vaddr
,
257 TEMP_BASE
+ segment
->p_offset
,
260 if (segment
->p_filesz
!= segment
->p_memsz
)
261 memset((void*) (segment
->p_vaddr
+ segment
->p_filesz
),
262 0, segment
->p_memsz
- segment
->p_filesz
);
266 * Copy over the ELF header & tweak it.
268 symtab
= SYMTAB_BASE
;
269 elfcpy
= (Elf64_Ehdr
*) symtab
;
270 memcpy(elfcpy
, nucleus_elf
, nucleus_elf
->e_ehsize
);
271 symtab
+= nucleus_elf
->e_ehsize
;
273 /* the program headers start right after */
274 elfcpy
->e_phoff
= symtab
- SYMTAB_BASE
;
276 /* copy over all the program headers */
277 memcpy(symtab
, TEMP_BASE
+ nucleus_elf
->e_phoff
,
278 nucleus_elf
->e_phentsize
* nucleus_elf
->e_phnum
);
279 symtab
+= nucleus_elf
->e_phentsize
* nucleus_elf
->e_phnum
;
281 /* the section headers start right after */
282 elfcpy
->e_shoff
= symtab
- SYMTAB_BASE
;
285 * Iterate through each section to find the .symtab & .strtab (as
286 * well as the null section), and copy the the headers over
290 for(i
=0; i
<nucleus_elf
->e_shnum
; i
++) {
291 section
= (Elf64_Shdr
*) (TEMP_BASE
+
292 nucleus_elf
->e_shoff
+
293 nucleus_elf
->e_shentsize
* i
);
295 switch (section
->sh_type
) {
297 if (i
== nucleus_elf
->e_shstrndx
)
298 elfcpy
->e_shstrndx
= elfcpy
->e_shnum
;
302 memcpy(symtab
, section
, nucleus_elf
->e_shentsize
);
304 symtab
+= nucleus_elf
->e_shentsize
;
312 for(i
=0; i
<elfcpy
->e_shnum
; i
++) {
313 section
= (Elf64_Shdr
*) (SYMTAB_BASE
+
315 elfcpy
->e_shentsize
* i
);
317 if (!section
->sh_link
)
320 tmp_section
= (Elf64_Shdr
*) (TEMP_BASE
+
321 nucleus_elf
->e_shoff
+
322 nucleus_elf
->e_shentsize
*
325 for(j
=0; j
<elfcpy
->e_shnum
; j
++) {
326 link_section
= (Elf64_Shdr
*) (SYMTAB_BASE
+
328 elfcpy
->e_shentsize
* j
);
330 if (memcmp(tmp_section
, link_section
, elfcpy
->e_shentsize
))
333 section
->sh_link
= j
;
339 * Now that we know what sections and where, let's copy those over
341 for (i
=1; i
<elfcpy
->e_shnum
; i
++) {
342 section
= (Elf64_Shdr
*) (SYMTAB_BASE
+
344 elfcpy
->e_shentsize
* i
);
346 memcpy(symtab
, TEMP_BASE
+ section
->sh_offset
,
348 section
->sh_offset
= symtab
- SYMTAB_BASE
;
349 symtab
+= section
->sh_size
;
353 * Now, jump to the nucleus entry point
355 start_sym
= (void*) nucleus_elf
->e_entry
;
356 start_sym(sense_memsize(), iplsch
, SYMTAB_BASE
);