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 #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 102 #ifndef HAVE_UNION_SEMUN 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_ */ 289