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