/* This file is part of the program psim. Copyright (C) 1997,2008, Joel Sherrill 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 . */ #ifndef _HW_SEM_C_ #define _HW_SEM_C_ #include "device_table.h" #include #include #include #include /* DEVICE sem - provide access to a unix semaphore DESCRIPTION This device implements an interface to a unix semaphore. PROPERTIES reg =
(required) Determine where the memory lives in the parents address space. key = (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; uint32_t 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_ */