| /* This file is part of the program psim. |
| |
| Copyright (C) 1997,2008, Joel Sherrill <joel@OARcorp.com> |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, see <http://www.gnu.org/licenses/>. |
| |
| */ |
| |
| |
| #ifndef _HW_SEM_C_ |
| #define _HW_SEM_C_ |
| |
| #include "device_table.h" |
| |
| #include <string.h> |
| #include <sys/ipc.h> |
| #include <sys/sem.h> |
| |
| #include <errno.h> |
| |
| /* DEVICE |
| |
| |
| sem - provide access to a unix semaphore |
| |
| |
| DESCRIPTION |
| |
| |
| This device implements an interface to a unix semaphore. |
| |
| |
| PROPERTIES |
| |
| |
| reg = <address> <size> (required) |
| |
| Determine where the memory lives in the parents address space. |
| |
| key = <integer> (required) |
| |
| This is the key of the unix semaphore. |
| |
| EXAMPLES |
| |
| |
| Enable tracing of the sem: |
| |
| | bash$ psim -t sem-device \ |
| |
| |
| Configure a UNIX semaphore using key 0x12345678 mapped into psim |
| address space at 0xfff00000: |
| |
| | -o '/sem@0xfff00000/reg 0xfff00000 0x80000' \ |
| | -o '/sem@0xfff00000/key 0x12345678' \ |
| |
| sim/ppc/run -o '/#address-cells 1' \ |
| -o '/sem@0xfff00000/reg 0xfff00000 12' \ |
| -o '/sem@0xfff00000/key 0x12345678' ../psim-hello/hello |
| |
| REGISTERS |
| |
| offset 0 - lock count |
| offset 4 - lock operation |
| offset 8 - unlock operation |
| |
| All reads return the current or resulting count. |
| |
| BUGS |
| |
| None known. |
| |
| */ |
| |
| typedef struct _hw_sem_device { |
| unsigned_word physical_address; |
| key_t key; |
| int id; |
| int initial; |
| int count; |
| } hw_sem_device; |
| |
| #ifndef HAVE_UNION_SEMUN |
| union semun { |
| int val; |
| struct semid_ds *buf; |
| unsigned short int *array; |
| #if defined(__linux__) |
| struct seminfo *__buf; |
| #endif |
| }; |
| #endif |
| |
| static void |
| hw_sem_init_data(device *me) |
| { |
| hw_sem_device *sem = (hw_sem_device*)device_data(me); |
| const device_unit *d; |
| int status; |
| union semun help; |
| |
| /* initialize the properties of the sem */ |
| |
| if (device_find_property(me, "key") == NULL) |
| error("sem_init_data() required key property is missing\n"); |
| |
| if (device_find_property(me, "value") == NULL) |
| error("sem_init_data() required value property is missing\n"); |
| |
| sem->key = (key_t) device_find_integer_property(me, "key"); |
| DTRACE(sem, ("semaphore key (%d)\n", sem->key) ); |
| |
| sem->initial = (int) device_find_integer_property(me, "value"); |
| DTRACE(sem, ("semaphore initial value (%d)\n", sem->initial) ); |
| |
| d = device_unit_address(me); |
| sem->physical_address = d->cells[ d->nr_cells-1 ]; |
| DTRACE(sem, ("semaphore physical_address=0x%x\n", sem->physical_address)); |
| |
| /* Now to initialize the semaphore */ |
| |
| if ( sem->initial != -1 ) { |
| |
| sem->id = semget(sem->key, 1, IPC_CREAT | 0660); |
| if (sem->id == -1) |
| error("hw_sem_init_data() semget failed\n"); |
| |
| help.val = sem->initial; |
| status = semctl( sem->id, 0, SETVAL, help ); |
| if (status == -1) |
| error("hw_sem_init_data() semctl -- set value failed\n"); |
| |
| } else { |
| sem->id = semget(sem->key, 1, 0660); |
| if (sem->id == -1) |
| error("hw_sem_init_data() semget failed\n"); |
| } |
| |
| sem->count = semctl( sem->id, 0, GETVAL, help ); |
| if (sem->count == -1) |
| error("hw_sem_init_data() semctl -- get value failed\n"); |
| DTRACE(sem, ("semaphore OS value (%d)\n", sem->count) ); |
| } |
| |
| static void |
| hw_sem_attach_address_callback(device *me, |
| attach_type attach, |
| int space, |
| unsigned_word addr, |
| unsigned nr_bytes, |
| access_type access, |
| device *client) /*callback/default*/ |
| { |
| hw_sem_device *sem = (hw_sem_device*)device_data(me); |
| |
| if (space != 0) |
| error("sem_attach_address_callback() invalid address space\n"); |
| |
| if (nr_bytes == 12) |
| error("sem_attach_address_callback() invalid size\n"); |
| |
| sem->physical_address = addr; |
| DTRACE(sem, ("semaphore physical_address=0x%x\n", addr)); |
| } |
| |
| static unsigned |
| hw_sem_io_read_buffer(device *me, |
| void *dest, |
| int space, |
| unsigned_word addr, |
| unsigned nr_bytes, |
| cpu *processor, |
| unsigned_word cia) |
| { |
| hw_sem_device *sem = (hw_sem_device*)device_data(me); |
| struct sembuf sb; |
| int status; |
| unsigned32 u32; |
| union semun help; |
| |
| /* do we need to worry about out of range addresses? */ |
| |
| DTRACE(sem, ("semaphore read addr=0x%x length=%d\n", addr, nr_bytes)); |
| |
| if (!(addr >= sem->physical_address && addr <= sem->physical_address + 11)) |
| error("hw_sem_io_read_buffer() invalid address - out of range\n"); |
| |
| if ((addr % 4) != 0) |
| error("hw_sem_io_read_buffer() invalid address - alignment\n"); |
| |
| if (nr_bytes != 4) |
| error("hw_sem_io_read_buffer() invalid length\n"); |
| |
| switch ( (addr - sem->physical_address) / 4 ) { |
| |
| case 0: /* OBTAIN CURRENT VALUE */ |
| break; |
| |
| case 1: /* LOCK */ |
| sb.sem_num = 0; |
| sb.sem_op = -1; |
| sb.sem_flg = 0; |
| |
| status = semop(sem->id, &sb, 1); |
| if (status == -1) { |
| perror( "hw_sem.c: lock" ); |
| error("hw_sem_io_read_buffer() sem lock\n"); |
| } |
| |
| DTRACE(sem, ("semaphore lock %d\n", sem->count)); |
| break; |
| |
| case 2: /* UNLOCK */ |
| sb.sem_num = 0; |
| sb.sem_op = 1; |
| sb.sem_flg = 0; |
| |
| status = semop(sem->id, &sb, 1); |
| if (status == -1) { |
| perror( "hw_sem.c: unlock" ); |
| error("hw_sem_io_read_buffer() sem unlock\n"); |
| } |
| DTRACE(sem, ("semaphore unlock %d\n", sem->count)); |
| break; |
| |
| default: |
| error("hw_sem_io_read_buffer() invalid address - unknown error\n"); |
| break; |
| } |
| |
| /* assume target is big endian */ |
| u32 = H2T_4(semctl( sem->id, 0, GETVAL, help )); |
| |
| DTRACE(sem, ("semaphore OS value (%d)\n", u32) ); |
| if (u32 == 0xffffffff) { |
| perror( "hw_sem.c: getval" ); |
| error("hw_sem_io_read_buffer() semctl -- get value failed\n"); |
| } |
| |
| memcpy(dest, &u32, nr_bytes); |
| return nr_bytes; |
| |
| } |
| |
| static device_callbacks const hw_sem_callbacks = { |
| { generic_device_init_address, hw_sem_init_data }, |
| { hw_sem_attach_address_callback, }, /* address */ |
| { hw_sem_io_read_buffer, NULL }, /* IO */ |
| { NULL, }, /* DMA */ |
| { NULL, }, /* interrupt */ |
| { NULL, }, /* unit */ |
| NULL, |
| }; |
| |
| static void * |
| hw_sem_create(const char *name, |
| const device_unit *unit_address, |
| const char *args) |
| { |
| hw_sem_device *sem = ZALLOC(hw_sem_device); |
| return sem; |
| } |
| |
| const device_descriptor hw_sem_device_descriptor[] = { |
| { "sem", hw_sem_create, &hw_sem_callbacks }, |
| { NULL }, |
| }; |
| |
| #endif /* _HW_SEM_C_ */ |