1/*
2 * linux/fs/binfmt_som.c
3 *
4 * These are the functions used to load SOM format executables as used
5 * by HP-UX.
6 *
7 * Copyright 1999 Matthew Wilcox <willy@bofh.ai>
8 * based on binfmt_elf which is
9 * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com).
10 */
11
12#include <linux/module.h>
13
14#include <linux/fs.h>
15#include <linux/stat.h>
16#include <linux/sched.h>
17#include <linux/mm.h>
18#include <linux/mman.h>
19#include <linux/errno.h>
20#include <linux/signal.h>
21#include <linux/binfmts.h>
22#include <linux/som.h>
23#include <linux/string.h>
24#include <linux/file.h>
25#include <linux/fcntl.h>
26#include <linux/ptrace.h>
27#include <linux/slab.h>
28#include <linux/shm.h>
29#include <linux/personality.h>
30#include <linux/init.h>
31
32#include <asm/a.out.h>
33#include <asm/uaccess.h>
34#include <asm/pgtable.h>
35
36
37#include <linux/elf.h>
38
39static int load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs);
40static int load_som_library(struct file *);
41
42/*
43 * If we don't support core dumping, then supply a NULL so we
44 * don't even try.
45 */
46#define som_core_dump	NULL
47
48#define SOM_PAGESTART(_v) ((_v) & ~(unsigned long)(SOM_PAGESIZE-1))
49#define SOM_PAGEOFFSET(_v) ((_v) & (SOM_PAGESIZE-1))
50#define SOM_PAGEALIGN(_v) (((_v) + SOM_PAGESIZE - 1) & ~(SOM_PAGESIZE - 1))
51
52static struct linux_binfmt som_format = {
53	.module		= THIS_MODULE,
54	.load_binary	= load_som_binary,
55	.load_shlib	= load_som_library,
56	.core_dump	= som_core_dump,
57	.min_coredump	= SOM_PAGESIZE
58};
59
60/*
61 * create_som_tables() parses the env- and arg-strings in new user
62 * memory and creates the pointer tables from them, and puts their
63 * addresses on the "stack", returning the new stack pointer value.
64 */
65static void create_som_tables(struct linux_binprm *bprm)
66{
67	char **argv, **envp;
68	int argc = bprm->argc;
69	int envc = bprm->envc;
70	unsigned long p;
71	unsigned long *sp;
72
73	/* Word-align the stack pointer */
74	sp = (unsigned long *)((bprm->p + 3) & ~3);
75
76	envp = (char **) sp;
77	sp += envc + 1;
78	argv = (char **) sp;
79	sp += argc + 1;
80
81	__put_user((unsigned long) envp,++sp);
82	__put_user((unsigned long) argv,++sp);
83
84	__put_user(argc, ++sp);
85
86	bprm->p = (unsigned long) sp;
87
88	p = current->mm->arg_start;
89	while (argc-- > 0) {
90		__put_user((char *)p,argv++);
91		p += strlen_user((char *)p);
92	}
93	__put_user(NULL, argv);
94	current->mm->arg_end = current->mm->env_start = p;
95	while (envc-- > 0) {
96		__put_user((char *)p,envp++);
97		p += strlen_user((char *)p);
98	}
99	__put_user(NULL, envp);
100	current->mm->env_end = p;
101}
102
103static int check_som_header(struct som_hdr *som_ex)
104{
105	int *buf = (int *)som_ex;
106	int i, ck;
107
108	if (som_ex->system_id != SOM_SID_PARISC_1_0 &&
109	    som_ex->system_id != SOM_SID_PARISC_1_1 &&
110	    som_ex->system_id != SOM_SID_PARISC_2_0)
111		return -ENOEXEC;
112
113	if (som_ex->a_magic != SOM_EXEC_NONSHARE &&
114	    som_ex->a_magic != SOM_EXEC_SHARE &&
115	    som_ex->a_magic != SOM_EXEC_DEMAND)
116		return -ENOEXEC;
117
118	if (som_ex->version_id != SOM_ID_OLD &&
119	    som_ex->version_id != SOM_ID_NEW)
120		return -ENOEXEC;
121
122	ck = 0;
123	for (i=0; i<32; i++)
124		ck ^= buf[i];
125	if (ck != 0)
126		return -ENOEXEC;
127
128	return 0;
129}
130
131static int map_som_binary(struct file *file,
132		const struct som_exec_auxhdr *hpuxhdr)
133{
134	unsigned long code_start, code_size, data_start, data_size;
135	unsigned long bss_start, som_brk;
136	int retval;
137	int prot = PROT_READ | PROT_EXEC;
138	int flags = MAP_FIXED|MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;
139
140	mm_segment_t old_fs = get_fs();
141	set_fs(get_ds());
142
143	code_start = SOM_PAGESTART(hpuxhdr->exec_tmem);
144	code_size = SOM_PAGEALIGN(hpuxhdr->exec_tsize);
145	current->mm->start_code = code_start;
146	current->mm->end_code = code_start + code_size;
147	down_write(&current->mm->mmap_sem);
148	retval = do_mmap(file, code_start, code_size, prot,
149			flags, SOM_PAGESTART(hpuxhdr->exec_tfile));
150	up_write(&current->mm->mmap_sem);
151	if (retval < 0 && retval > -1024)
152		goto out;
153
154	data_start = SOM_PAGESTART(hpuxhdr->exec_dmem);
155	data_size = SOM_PAGEALIGN(hpuxhdr->exec_dsize);
156	current->mm->start_data = data_start;
157	current->mm->end_data = bss_start = data_start + data_size;
158	down_write(&current->mm->mmap_sem);
159	retval = do_mmap(file, data_start, data_size,
160			prot | PROT_WRITE, flags,
161			SOM_PAGESTART(hpuxhdr->exec_dfile));
162	up_write(&current->mm->mmap_sem);
163	if (retval < 0 && retval > -1024)
164		goto out;
165
166	som_brk = bss_start + SOM_PAGEALIGN(hpuxhdr->exec_bsize);
167	current->mm->start_brk = current->mm->brk = som_brk;
168	down_write(&current->mm->mmap_sem);
169	retval = do_mmap(NULL, bss_start, som_brk - bss_start,
170			prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, 0);
171	up_write(&current->mm->mmap_sem);
172	if (retval > 0 || retval < -1024)
173		retval = 0;
174out:
175	set_fs(old_fs);
176	return retval;
177}
178
179
180/*
181 * These are the functions used to load SOM executables and shared
182 * libraries.  There is no binary dependent code anywhere else.
183 */
184
185static int
186load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
187{
188	int som_exec_fileno;
189	int retval;
190	unsigned int size;
191	unsigned long som_entry;
192	struct som_hdr *som_ex;
193	struct som_exec_auxhdr *hpuxhdr;
194	struct files_struct *files;
195
196	/* Get the exec-header */
197	som_ex = (struct som_hdr *) bprm->buf;
198
199	retval = check_som_header(som_ex);
200	if (retval != 0)
201		goto out;
202
203	/* Now read in the auxiliary header information */
204
205	retval = -ENOMEM;
206	size = som_ex->aux_header_size;
207	if (size > SOM_PAGESIZE)
208		goto out;
209	hpuxhdr = kmalloc(size, GFP_KERNEL);
210	if (!hpuxhdr)
211		goto out;
212
213	retval = kernel_read(bprm->file, som_ex->aux_header_location,
214			(char *) hpuxhdr, size);
215	if (retval != size) {
216		if (retval >= 0)
217			retval = -EIO;
218		goto out_free;
219	}
220
221	files = current->files; /* Refcounted so ok */
222	retval = unshare_files();
223	if (retval < 0)
224		goto out_free;
225	if (files == current->files) {
226		put_files_struct(files);
227		files = NULL;
228	}
229
230	retval = get_unused_fd();
231	if (retval < 0)
232		goto out_free;
233	get_file(bprm->file);
234	fd_install(som_exec_fileno = retval, bprm->file);
235
236	/* Flush all traces of the currently running executable */
237	retval = flush_old_exec(bprm);
238	if (retval)
239		goto out_free;
240
241	/* OK, This is the point of no return */
242	current->flags &= ~PF_FORKNOEXEC;
243	current->personality = PER_HPUX;
244
245	/* Set the task size for HP-UX processes such that
246	 * the gateway page is outside the address space.
247	 * This can be fixed later, but for now, this is much
248	 * easier.
249	 */
250
251	current->thread.task_size = 0xc0000000;
252
253	/* Set map base to allow enough room for hp-ux heap growth */
254
255	current->thread.map_base = 0x80000000;
256
257	retval = map_som_binary(bprm->file, hpuxhdr);
258	if (retval < 0)
259		goto out_free;
260
261	som_entry = hpuxhdr->exec_entry;
262	kfree(hpuxhdr);
263
264	set_binfmt(&som_format);
265	compute_creds(bprm);
266	setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT);
267
268	create_som_tables(bprm);
269
270	current->mm->start_stack = bprm->p;
271
272
273	map_hpux_gateway_page(current,current->mm);
274
275	start_thread_som(regs, som_entry, bprm->p);
276	if (current->ptrace & PT_PTRACED)
277		send_sig(SIGTRAP, current, 0);
278	return 0;
279
280	/* error cleanup */
281out_free:
282	kfree(hpuxhdr);
283out:
284	return retval;
285}
286
287static int load_som_library(struct file *f)
288{
289/* No lib support in SOM yet.  gizza chance.. */
290	return -ENOEXEC;
291}
292	/* Install the SOM loader.
293	 * N.B. We *rely* on the table being the right size with the
294	 * right number of free slots...
295	 */
296
297static int __init init_som_binfmt(void)
298{
299	return register_binfmt(&som_format);
300}
301
302static void __exit exit_som_binfmt(void)
303{
304	/* Remove the SOM loader. */
305	unregister_binfmt(&som_format);
306}
307
308core_initcall(init_som_binfmt);
309module_exit(exit_som_binfmt);
310