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