| 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_ */ |