290 lines
6.8 KiB
C
290 lines
6.8 KiB
C
/* 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 2 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, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
#ifndef _HW_SEM_C_
|
|
#define _HW_SEM_C_
|
|
|
|
#include "device_table.h"
|
|
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#else
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
#endif
|
|
|
|
#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;
|
|
|
|
#if !HAS_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_ */
|