1 |
/* |
2 |
* Copyright (C) 2004-2007 Anders Gavare. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* 3. The name of the author may not be used to endorse or promote products |
13 |
* derived from this software without specific prior written permission. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* |
28 |
* $Id: useremul.c,v 1.6 2007/06/17 23:32:20 debug Exp $ |
29 |
* |
30 |
* COMMENT: Userland (syscall) emulation framework |
31 |
*/ |
32 |
|
33 |
#include <stdio.h> |
34 |
#include <stdlib.h> |
35 |
#include <string.h> |
36 |
|
37 |
#include "cpu.h" |
38 |
#include "machine.h" |
39 |
#include "misc.h" |
40 |
#include "useremul.h" |
41 |
|
42 |
|
43 |
struct syscall_emul { |
44 |
char *name; |
45 |
int arch; |
46 |
char *cpu_name; |
47 |
void (*f)(struct cpu *, uint32_t); |
48 |
void (*setup)(struct cpu *, int, char **); |
49 |
|
50 |
struct syscall_emul *next; |
51 |
}; |
52 |
|
53 |
static struct syscall_emul *first_syscall_emul; |
54 |
|
55 |
/* Max length of strings passed using syscall parameters: */ |
56 |
#define MAXLEN 8192 |
57 |
|
58 |
|
59 |
/* |
60 |
* useremul_setup(): |
61 |
* |
62 |
* Set up an emulated environment suitable for running userland code. The |
63 |
* program should already have been loaded into memory when this function |
64 |
* is called. |
65 |
*/ |
66 |
void useremul_setup(struct cpu *cpu, int argc, char **host_argv) |
67 |
{ |
68 |
struct syscall_emul *sep; |
69 |
|
70 |
sep = first_syscall_emul; |
71 |
|
72 |
while (sep != NULL) { |
73 |
if (strcasecmp(cpu->machine->userland_emul, sep->name) == 0) { |
74 |
sep->setup(cpu, argc, host_argv); |
75 |
return; |
76 |
} |
77 |
sep = sep->next; |
78 |
} |
79 |
|
80 |
fatal("useremul_setup(): internal error, unimplemented emulation?\n"); |
81 |
exit(1); |
82 |
} |
83 |
|
84 |
|
85 |
/* |
86 |
* useremul_syscall(): |
87 |
* |
88 |
* Handle userland syscalls. This function is called whenever a userland |
89 |
* process runs a 'syscall' instruction. The code argument is the code |
90 |
* embedded into the syscall instruction, if any. (This 'code' value is not |
91 |
* necessarily used by specific emulations.) |
92 |
*/ |
93 |
void useremul_syscall(struct cpu *cpu, uint32_t code) |
94 |
{ |
95 |
if (cpu->useremul_syscall == NULL) { |
96 |
fatal("useremul_syscall(): cpu->useremul_syscall == NULL\n"); |
97 |
} else |
98 |
cpu->useremul_syscall(cpu, code); |
99 |
} |
100 |
|
101 |
|
102 |
/* |
103 |
* useremul_name_to_useremul(): |
104 |
* |
105 |
* Example: |
106 |
* Input: name = "netbsd/pmax" |
107 |
* Output: sets *arch = ARCH_MIPS, *machine_name = "NetBSD/pmax", |
108 |
* and *cpu_name = "R3000". |
109 |
*/ |
110 |
void useremul_name_to_useremul(struct cpu *cpu, char *name, int *arch, |
111 |
char **machine_name, char **cpu_name) |
112 |
{ |
113 |
struct syscall_emul *sep; |
114 |
|
115 |
sep = first_syscall_emul; |
116 |
|
117 |
while (sep != NULL) { |
118 |
if (strcasecmp(name, sep->name) == 0) { |
119 |
if (cpu_family_ptr_by_number(sep->arch) == NULL) { |
120 |
printf("\nSupport for the CPU family needed" |
121 |
" for '%s' userland emulation was not" |
122 |
" enabled at configuration time.\n", |
123 |
sep->name); |
124 |
exit(1); |
125 |
} |
126 |
|
127 |
if (cpu != NULL) |
128 |
cpu->useremul_syscall = sep->f; |
129 |
|
130 |
if (arch != NULL) |
131 |
*arch = sep->arch; |
132 |
|
133 |
if (machine_name != NULL) |
134 |
CHECK_ALLOCATION((*machine_name) = |
135 |
strdup(sep->name)); |
136 |
|
137 |
if (cpu_name != NULL) |
138 |
CHECK_ALLOCATION((*cpu_name) = |
139 |
strdup(sep->cpu_name)); |
140 |
|
141 |
return; |
142 |
} |
143 |
|
144 |
sep = sep->next; |
145 |
} |
146 |
|
147 |
fatal("Unknown userland emulation '%s'\n", name); |
148 |
exit(1); |
149 |
} |
150 |
|
151 |
|
152 |
/* |
153 |
* add_useremul(): |
154 |
* |
155 |
* For internal use, from useremul_init() only. Adds an emulation mode. |
156 |
*/ |
157 |
static void add_useremul(char *name, int arch, char *cpu_name, |
158 |
void (*f)(struct cpu *, uint32_t), |
159 |
void (*setup)(struct cpu *, int, char **)) |
160 |
{ |
161 |
struct syscall_emul *sep; |
162 |
|
163 |
CHECK_ALLOCATION(sep = malloc(sizeof(struct syscall_emul))); |
164 |
memset(sep, 0, sizeof(sep)); |
165 |
|
166 |
sep->name = name; |
167 |
sep->arch = arch; |
168 |
sep->cpu_name = cpu_name; |
169 |
sep->f = f; |
170 |
sep->setup = setup; |
171 |
|
172 |
sep->next = first_syscall_emul; |
173 |
first_syscall_emul = sep; |
174 |
} |
175 |
|
176 |
|
177 |
/* |
178 |
* useremul_list_emuls(): |
179 |
* |
180 |
* List all available userland emulation modes. (Actually, only those which |
181 |
* have CPU support enabled.) |
182 |
*/ |
183 |
void useremul_list_emuls(void) |
184 |
{ |
185 |
struct syscall_emul *sep; |
186 |
int iadd = DEBUG_INDENTATION * 2; |
187 |
|
188 |
sep = first_syscall_emul; |
189 |
|
190 |
if (sep == NULL) |
191 |
return; |
192 |
|
193 |
debug("The following userland-only (syscall) emulation modes are" |
194 |
" available:\n\n"); |
195 |
debug_indentation(iadd); |
196 |
|
197 |
while (sep != NULL) { |
198 |
if (cpu_family_ptr_by_number(sep->arch) != NULL) { |
199 |
debug("%s (default CPU \"%s\")\n", |
200 |
sep->name, sep->cpu_name); |
201 |
} |
202 |
|
203 |
sep = sep->next; |
204 |
} |
205 |
|
206 |
debug_indentation(-iadd); |
207 |
debug("\n(Most of these modes are bogus.)\n\n"); |
208 |
} |
209 |
|
210 |
|
211 |
/* |
212 |
* useremul_init(): |
213 |
* |
214 |
* This function should be called before any other useremul_*() function |
215 |
* is used. |
216 |
*/ |
217 |
void useremul_init(void) |
218 |
{ |
219 |
/* Note: These are in reverse alphabetic order: */ |
220 |
|
221 |
add_useremul("NetBSD/pmax", ARCH_MIPS, "R3000", |
222 |
useremul_netbsd, useremul_netbsd_setup); |
223 |
|
224 |
add_useremul("FreeBSD/Alpha", ARCH_ALPHA, "21364", |
225 |
useremul_freebsd, useremul_freebsd_setup); |
226 |
} |
227 |
|