Commit | Line | Data |
---|---|---|
00a0b122 JS |
1 | /* This file is part of the program psim. |
2 | ||
3 | Copyright (C) 1997,2008, Joel Sherrill <joel@OARcorp.com> | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
3fd725ef | 7 | the Free Software Foundation; either version 3 of the License, or |
00a0b122 JS |
8 | (at your option) any later version. |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
51b318de | 16 | along with this program; if not, see <http://www.gnu.org/licenses/>. |
00a0b122 JS |
17 | |
18 | */ | |
19 | ||
20 | ||
21 | #ifndef _HW_SEM_C_ | |
22 | #define _HW_SEM_C_ | |
23 | ||
24 | #include "device_table.h" | |
25 | ||
26 | #ifdef HAVE_STRING_H | |
27 | #include <string.h> | |
28 | #else | |
29 | #ifdef HAVE_STRINGS_H | |
30 | #include <strings.h> | |
31 | #endif | |
32 | #endif | |
33 | ||
34 | #include <sys/ipc.h> | |
35 | #include <sys/sem.h> | |
36 | ||
37 | #include <errno.h> | |
38 | ||
39 | /* DEVICE | |
40 | ||
41 | ||
42 | sem - provide access to a unix semaphore | |
43 | ||
44 | ||
45 | DESCRIPTION | |
46 | ||
47 | ||
48 | This device implements an interface to a unix semaphore. | |
49 | ||
50 | ||
51 | PROPERTIES | |
52 | ||
53 | ||
54 | reg = <address> <size> (required) | |
55 | ||
56 | Determine where the memory lives in the parents address space. | |
57 | ||
58 | key = <integer> (required) | |
59 | ||
60 | This is the key of the unix semaphore. | |
61 | ||
62 | EXAMPLES | |
63 | ||
64 | ||
65 | Enable tracing of the sem: | |
66 | ||
67 | | bash$ psim -t sem-device \ | |
68 | ||
69 | ||
70 | Configure a UNIX semaphore using key 0x12345678 mapped into psim | |
71 | address space at 0xfff00000: | |
72 | ||
73 | | -o '/sem@0xfff00000/reg 0xfff00000 0x80000' \ | |
74 | | -o '/sem@0xfff00000/key 0x12345678' \ | |
75 | ||
76 | sim/ppc/run -o '/#address-cells 1' \ | |
77 | -o '/sem@0xfff00000/reg 0xfff00000 12' \ | |
78 | -o '/sem@0xfff00000/key 0x12345678' ../psim-hello/hello | |
79 | ||
80 | REGISTERS | |
81 | ||
82 | offset 0 - lock count | |
83 | offset 4 - lock operation | |
84 | offset 8 - unlock operation | |
85 | ||
86 | All reads return the current or resulting count. | |
87 | ||
88 | BUGS | |
89 | ||
90 | None known. | |
91 | ||
92 | */ | |
93 | ||
94 | typedef struct _hw_sem_device { | |
95 | unsigned_word physical_address; | |
96 | key_t key; | |
97 | int id; | |
98 | int initial; | |
99 | int count; | |
100 | } hw_sem_device; | |
101 | ||
bf0275b1 | 102 | #ifndef HAVE_UNION_SEMUN |
00a0b122 JS |
103 | union semun { |
104 | int val; | |
105 | struct semid_ds *buf; | |
106 | unsigned short int *array; | |
107 | #if defined(__linux__) | |
108 | struct seminfo *__buf; | |
109 | #endif | |
110 | }; | |
111 | #endif | |
112 | ||
113 | static void | |
114 | hw_sem_init_data(device *me) | |
115 | { | |
116 | hw_sem_device *sem = (hw_sem_device*)device_data(me); | |
117 | const device_unit *d; | |
118 | int status; | |
119 | union semun help; | |
120 | ||
121 | /* initialize the properties of the sem */ | |
122 | ||
123 | if (device_find_property(me, "key") == NULL) | |
124 | error("sem_init_data() required key property is missing\n"); | |
125 | ||
126 | if (device_find_property(me, "value") == NULL) | |
127 | error("sem_init_data() required value property is missing\n"); | |
128 | ||
129 | sem->key = (key_t) device_find_integer_property(me, "key"); | |
130 | DTRACE(sem, ("semaphore key (%d)\n", sem->key) ); | |
131 | ||
132 | sem->initial = (int) device_find_integer_property(me, "value"); | |
133 | DTRACE(sem, ("semaphore initial value (%d)\n", sem->initial) ); | |
134 | ||
135 | d = device_unit_address(me); | |
136 | sem->physical_address = d->cells[ d->nr_cells-1 ]; | |
137 | DTRACE(sem, ("semaphore physical_address=0x%x\n", sem->physical_address)); | |
138 | ||
139 | /* Now to initialize the semaphore */ | |
140 | ||
141 | if ( sem->initial != -1 ) { | |
142 | ||
143 | sem->id = semget(sem->key, 1, IPC_CREAT | 0660); | |
144 | if (sem->id == -1) | |
145 | error("hw_sem_init_data() semget failed\n"); | |
146 | ||
147 | help.val = sem->initial; | |
148 | status = semctl( sem->id, 0, SETVAL, help ); | |
149 | if (status == -1) | |
150 | error("hw_sem_init_data() semctl -- set value failed\n"); | |
151 | ||
152 | } else { | |
153 | sem->id = semget(sem->key, 1, 0660); | |
154 | if (sem->id == -1) | |
155 | error("hw_sem_init_data() semget failed\n"); | |
156 | } | |
157 | ||
158 | sem->count = semctl( sem->id, 0, GETVAL, help ); | |
159 | if (sem->count == -1) | |
160 | error("hw_sem_init_data() semctl -- get value failed\n"); | |
161 | DTRACE(sem, ("semaphore OS value (%d)\n", sem->count) ); | |
162 | } | |
163 | ||
164 | static void | |
165 | hw_sem_attach_address_callback(device *me, | |
166 | attach_type attach, | |
167 | int space, | |
168 | unsigned_word addr, | |
169 | unsigned nr_bytes, | |
170 | access_type access, | |
171 | device *client) /*callback/default*/ | |
172 | { | |
173 | hw_sem_device *sem = (hw_sem_device*)device_data(me); | |
174 | ||
175 | if (space != 0) | |
176 | error("sem_attach_address_callback() invalid address space\n"); | |
177 | ||
178 | if (nr_bytes == 12) | |
179 | error("sem_attach_address_callback() invalid size\n"); | |
180 | ||
181 | sem->physical_address = addr; | |
182 | DTRACE(sem, ("semaphore physical_address=0x%x\n", addr)); | |
183 | } | |
184 | ||
185 | static unsigned | |
186 | hw_sem_io_read_buffer(device *me, | |
187 | void *dest, | |
188 | int space, | |
189 | unsigned_word addr, | |
190 | unsigned nr_bytes, | |
191 | cpu *processor, | |
192 | unsigned_word cia) | |
193 | { | |
194 | hw_sem_device *sem = (hw_sem_device*)device_data(me); | |
195 | struct sembuf sb; | |
196 | int status; | |
197 | unsigned32 u32; | |
198 | union semun help; | |
199 | ||
200 | /* do we need to worry about out of range addresses? */ | |
201 | ||
202 | DTRACE(sem, ("semaphore read addr=0x%x length=%d\n", addr, nr_bytes)); | |
203 | ||
204 | if (!(addr >= sem->physical_address && addr <= sem->physical_address + 11)) | |
205 | error("hw_sem_io_read_buffer() invalid address - out of range\n"); | |
206 | ||
207 | if ((addr % 4) != 0) | |
208 | error("hw_sem_io_read_buffer() invalid address - alignment\n"); | |
209 | ||
210 | if (nr_bytes != 4) | |
211 | error("hw_sem_io_read_buffer() invalid length\n"); | |
212 | ||
213 | switch ( (addr - sem->physical_address) / 4 ) { | |
214 | ||
215 | case 0: /* OBTAIN CURRENT VALUE */ | |
216 | break; | |
217 | ||
218 | case 1: /* LOCK */ | |
219 | sb.sem_num = 0; | |
220 | sb.sem_op = -1; | |
221 | sb.sem_flg = 0; | |
222 | ||
223 | status = semop(sem->id, &sb, 1); | |
224 | if (status == -1) { | |
225 | perror( "hw_sem.c: lock" ); | |
226 | error("hw_sem_io_read_buffer() sem lock\n"); | |
227 | } | |
228 | ||
229 | DTRACE(sem, ("semaphore lock %d\n", sem->count)); | |
230 | break; | |
231 | ||
232 | case 2: /* UNLOCK */ | |
233 | sb.sem_num = 0; | |
234 | sb.sem_op = 1; | |
235 | sb.sem_flg = 0; | |
236 | ||
237 | status = semop(sem->id, &sb, 1); | |
238 | if (status == -1) { | |
239 | perror( "hw_sem.c: unlock" ); | |
240 | error("hw_sem_io_read_buffer() sem unlock\n"); | |
241 | } | |
242 | DTRACE(sem, ("semaphore unlock %d\n", sem->count)); | |
243 | break; | |
244 | ||
245 | default: | |
246 | error("hw_sem_io_read_buffer() invalid address - unknown error\n"); | |
247 | break; | |
248 | } | |
249 | ||
250 | /* assume target is big endian */ | |
251 | u32 = H2T_4(semctl( sem->id, 0, GETVAL, help )); | |
252 | ||
253 | DTRACE(sem, ("semaphore OS value (%d)\n", u32) ); | |
254 | if (u32 == 0xffffffff) { | |
255 | perror( "hw_sem.c: getval" ); | |
256 | error("hw_sem_io_read_buffer() semctl -- get value failed\n"); | |
257 | } | |
258 | ||
259 | memcpy(dest, &u32, nr_bytes); | |
260 | return nr_bytes; | |
261 | ||
262 | } | |
263 | ||
264 | static device_callbacks const hw_sem_callbacks = { | |
265 | { generic_device_init_address, hw_sem_init_data }, | |
266 | { hw_sem_attach_address_callback, }, /* address */ | |
267 | { hw_sem_io_read_buffer, NULL }, /* IO */ | |
268 | { NULL, }, /* DMA */ | |
269 | { NULL, }, /* interrupt */ | |
270 | { NULL, }, /* unit */ | |
271 | NULL, | |
272 | }; | |
273 | ||
274 | static void * | |
275 | hw_sem_create(const char *name, | |
276 | const device_unit *unit_address, | |
277 | const char *args) | |
278 | { | |
279 | hw_sem_device *sem = ZALLOC(hw_sem_device); | |
280 | return sem; | |
281 | } | |
282 | ||
283 | const device_descriptor hw_sem_device_descriptor[] = { | |
284 | { "sem", hw_sem_create, &hw_sem_callbacks }, | |
285 | { NULL }, | |
286 | }; | |
287 | ||
288 | #endif /* _HW_SEM_C_ */ |