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