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